Ejemplo n.º 1
0
def repack_apk(apk_path_or_list,
               provider_name,
               merge_dex_path,
               activity_list=None,
               res_file_list=None,
               debuggable=None,
               vm_safe_mode=None,
               max_heap_size=0,
               force_append=False):
    '''
    '''
    if not isinstance(apk_path_or_list, list):
        apk_path_or_list = [apk_path_or_list]
    elif len(apk_path_or_list) == 0:
        raise ValueError('apk path not specified')

    apk_file_list = []
    signature_dict = {}
    for it in apk_path_or_list:
        apk_file = APKFile(it)
        apk_file_list.append(apk_file)
        manifest = AndroidManifest(apk_file)
        if debuggable != None:
            manifest.debuggable = debuggable  # 修改debuggable属性
        if vm_safe_mode != None:
            manifest.vm_safe_mode = vm_safe_mode  # 修改安全模式
        process_list = manifest.get_process_list()
        authorities = manifest.package_name + '.authorities'
        print('Add provider %s' % provider_name)
        manifest.add_provider(provider_name, authorities)  # 主进程
        for i, process in enumerate(process_list):
            sub_provider_name = provider_name + '$InnerClass' + str(i + 1)
            print('Add provider %s in process %s' %
                  (sub_provider_name, process))
            manifest.add_provider(sub_provider_name, authorities + str(i),
                                  process)

        if activity_list:
            for activity in activity_list:
                print('Add activity %s' % activity['name'])
                manifest.add_activity(activity['name'], activity['exported'],
                                      activity['process'])

        # 合并dex文件
        print('Merge dex %s' % merge_dex_path)
        classes_dex_path = tempfile.mktemp('.dex')

        dex_index = 1
        low_memory = False
        while True:
            dex_file = 'classes%s.dex' % (dex_index if dex_index > 1 else '')
            if not apk_file.get_file(dex_file):
                # 作为最后一个classes.dex
                with open(merge_dex_path, 'rb') as f:
                    apk_file.add_file(dex_file, f.read())
                print('Save dex %s to %s' % (merge_dex_path, dex_file))
                break

            if force_append or low_memory:
                # 低内存下直接跳过已有dex,进行加速
                dex_index += 1
                continue

            if os.path.exists(classes_dex_path):
                os.remove(classes_dex_path)
            apk_file.extract_file(dex_file, classes_dex_path)
            try:
                merge_dex(classes_dex_path, [classes_dex_path, merge_dex_path],
                          max_heap_size)
            except TooManyMethodsError:
                print('Merge dex into %s failed due to methods number' %
                      dex_file)
                dex_index += 1
            except OutOfMemoryError:
                print('Merge dex into %s failed due to out of memory error' %
                      dex_file)
                low_memory = True
                dex_index += 1
            else:
                print('Merge dex into %s success' % dex_file)
                with open(classes_dex_path, 'rb') as f:
                    apk_file.add_file(dex_file, f.read())
                break

        if dex_index > 1:
            # 合并进非主dex只支持5.0以上系统
            print('WARNING: APK can only be installed in android above 5.0')
            manifest.min_sdk_version = 21

        manifest.save()

        if res_file_list:
            for src_path, dst_path in res_file_list:
                with open(src_path, 'rb') as f:
                    print('Copy file %s => %s' % (src_path, dst_path))
                    data = f.read()
                    apk_file.add_file(dst_path, data)

        for it in apk_file.list_dir('META-INF'):
            if it.lower().endswith('.rsa'):
                print('Signature file is %s' % it)
                tmp_rsa_path = tempfile.mktemp('.rsa')
                apk_file.extract_file('META-INF/%s' % it, tmp_rsa_path)
                orig_signature = get_apk_signature(tmp_rsa_path).strip()
                os.remove(tmp_rsa_path)
                logging.info('%s signature is %s' %
                             (manifest.package_name, orig_signature))
                signature_dict[manifest.package_name] = orig_signature
                break
        else:
            raise RuntimeError('Can not find .sf file in META-INF dir')

        for it in apk_file.list_dir('META-INF'):
            apk_file.delete_file('META-INF/%s' % it)

    out_apk_list = []

    # 写入原始签名信息
    print('Write original signatures: %s' % json.dumps(signature_dict))
    temp_dir = tempfile.mkdtemp('-repack')
    for i, apk_file in enumerate(apk_file_list):
        apk_file.add_file('assets/qt4a_package_signatures.txt',
                          json.dumps(signature_dict))
        file_name = os.path.split(apk_path_or_list[i])[-1][:-4] + '-repack.apk'
        tmp_apk_path = os.path.join(temp_dir, file_name)
        apk_file.save(tmp_apk_path)
        new_path = resign_apk(tmp_apk_path)
        os.remove(tmp_apk_path)
        out_apk_list.append(new_path)
    if len(out_apk_list) == 1:
        return out_apk_list[0]
    else:
        return out_apk_list
Ejemplo n.º 2
0
def repack_apk(apk_path_or_list, debuggable=True):
    '''重打包apk
    
    :param apk_path_or_list: apk路径或apk路径列表
    :type  apk_path_or_list: string/list
    :param debuggable: 重打包后的apk是否是调试版本:
                           True - 是
                           False - 否
                           None - 与原apk保持一致
    :type debuggable:  bool/None
    '''
    cur_path = os.path.dirname(os.path.abspath(__file__))
    if not isinstance(apk_path_or_list, list):
        apk_path_or_list = [apk_path_or_list]
    elif len(apk_path_or_list) == 0:
        raise ValueError('apk path not specified')

    apk_file_list = []
    signature_dict = {}
    for it in apk_path_or_list:
        apk_file = APKFile(it)
        apk_file_list.append(apk_file)
        manifest = AndroidManifest(apk_file)
        if debuggable != None:
            manifest.debuggable = debuggable  # 修改debuggable属性
        process_list = manifest.get_process_list()
        provider_name = 'com.test.androidspy.inject.DexLoaderContentProvider'
        authorities = manifest.package_name + '.authorities'
        manifest.add_provider(provider_name, authorities)  # 主进程
        manifest.add_activity('com.test.androidspy.inject.CmdExecuteActivity',
                              "true", ':qt4a_cmd')
        for i, process in enumerate(process_list):
            manifest.add_provider(provider_name + '$InnerClass' + str(i + 1),
                                  authorities + str(i), process)
        manifest.save()

        # 合并dex文件
        dexloader_path = os.path.join(cur_path, 'tools', 'dexloader.dex')
        classes_dex_path = tempfile.mktemp('.dex')
        apk_file.extract_file('classes.dex', classes_dex_path)
        merge_dex(classes_dex_path, [classes_dex_path, dexloader_path])
        with open(classes_dex_path, 'rb') as f:
            apk_file.add_file('classes.dex', f.read())

        # 添加QT4A测试桩文件
        file_list = [
            'AndroidSpy.jar', 'arm64-v8a/libdexloader.so',
            'arm64-v8a/libdexloader64.so', 'arm64-v8a/libandroidhook.so',
            'armeabi/libdexloader.so', 'armeabi/libandroidhook.so',
            'armeabi-v7a/libdexloader.so', 'armeabi-v7a/libandroidhook.so',
            'x86/libdexloader.so', 'x86/libandroidhook.so'
        ]
        tools_path = os.path.join(os.path.dirname(cur_path), 'androiddriver',
                                  'tools')
        for it in file_list:
            file_path = os.path.join(tools_path, it)
            with open(file_path, 'rb') as f:
                data = f.read()
                apk_file.add_file('assets/qt4a/%s' % it, data)

        for it in apk_file.list_dir('META-INF'):
            if it.lower().endswith('.rsa'):
                print('Signature file is %s' % it)
                tmp_rsa_path = tempfile.mktemp('.rsa')
                apk_file.extract_file('META-INF/%s' % it, tmp_rsa_path)
                orig_signature = get_apk_signature(tmp_rsa_path).strip()
                os.remove(tmp_rsa_path)
                logging.info('%s signature is %s' %
                             (manifest.package_name, orig_signature))
                signature_dict[manifest.package_name] = orig_signature
                break
        else:
            raise RuntimeError('Can not find .sf file in META-INF dir')

        for it in apk_file.list_dir('META-INF'):
            apk_file.delete_file('META-INF/%s' % it)

    out_apk_list = []

    # 写入原始签名信息
    temp_dir = tempfile.mkdtemp('-repack')
    for i, apk_file in enumerate(apk_file_list):
        apk_file.add_file('assets/qt4a_package_signatures.txt',
                          json.dumps(signature_dict))
        file_name = os.path.split(apk_path_or_list[i])[-1][:-4] + '-repack.apk'
        tmp_apk_path = os.path.join(temp_dir, file_name)
        apk_file.save(tmp_apk_path)
        new_path = resign_apk(tmp_apk_path)
        os.remove(tmp_apk_path)
        out_apk_list.append(new_path)
    if len(out_apk_list) == 1: return out_apk_list[0]
    else: return out_apk_list