from PrintLog import PrintLog import os import file_utils, xml_utils, path_utils, apk_tool, contants def pack(apk_decompile_tmp_dir, channel_path, sdk_name,package_name): PrintLog('删剔除母包融合SDK代码(cn/yyxx)') ret = remove_yyxx_old_code(apk_decompile_tmp_dir) if ret: return ret PrintLog('删除一些不支持的属性') ret = remove_no_support_attr(apk_decompile_tmp_dir) if ret: return ret PrintLog('修改minSdkVersion为21,删除res里仅支持21以下的资源目录') ret = change_un_support_config(apk_decompile_tmp_dir) if ret: return ret PrintLog('修改关于反编译后不符合要求的资源目录 eg:drawable-hdpi-v4') ret = merge_darwable_res_end_with_v4(apk_decompile_tmp_dir) if ret: return ret PrintLog('移除母包SDK的res资源') ret = remove_old_res(apk_decompile_tmp_dir) if ret: return ret PrintLog('移除母包SDK的.so文件') ret = remove_old_abi(apk_decompile_tmp_dir) if ret: return ret PrintLog('移除打包目录中values的重复资源') ret = remove_same_values_res(apk_decompile_tmp_dir, channel_path) if ret: return ret PrintLog('复制渠道SDK资源') ret = copy_res(apk_decompile_tmp_dir, channel_path) if ret: return ret # 合并主文件 PrintLog('合并Androidmanifest.xml 文件') ret = merge_manifest_res(apk_decompile_tmp_dir, channel_path, sdk_name) if ret: return ret # 打包lib依赖 PrintLog('将SDK中jar包打进临时打包目录') ret = pack_jar(apk_decompile_tmp_dir, channel_path) if ret: return ret # 修改${applicationId} PrintLog('修改替换AndroidManifest.xml中${applicationId}关键字') temp_manifest_path = os.path.join(apk_decompile_tmp_dir,'AndroidManifest.xml') file_utils.replace_content(temp_manifest_path, '${applicationId}', package_name) # 生成R文件 PrintLog('生成R文件') ret = apk_tool.create_R_file(apk_decompile_tmp_dir,package_name) if ret: return ret def remove_yyxx_old_code(apk_decompile_tmp_dir): yyxx_code_path = os.path.join(apk_decompile_tmp_dir, 'smali', 'cn', 'yyxx') file_utils.safeFileDelete(yyxx_code_path) return 0 def remove_no_support_attr(apk_decompile_tmp_dir): """ 删除一些不支持的属性 """ manifest = os.path.join(apk_decompile_tmp_dir, 'AndroidManifest.xml') xml_utils.remove_root_attr(manifest, 'compileSdkVersion') xml_utils.remove_root_attr(manifest, 'compileSdkVersionCodename') return 0 def change_un_support_config(apk_decompile_tmp_dir): """ 默认最低版本为21 1.修改 minSdkVersion 为21 2.删除res里仅支持21以下的资源目录 :param apk_decompile_tmp_dir: :return: """ min_sdk_version = 21 yml = os.path.join(apk_decompile_tmp_dir, 'apktool.yml') file_utils.change_min_sdk_version(yml, min_sdk_version) res_path = os.path.join(apk_decompile_tmp_dir, 'res') tag = '-v' for res in os.listdir(res_path): # print('res = ' + res) if res.startswith('values') and tag in res: start = res.index(tag) version = res[start + len(tag):] if not version.isdigit(): continue version = int(version) PrintLog('version = %d' % version) if version < min_sdk_version: un_support_path = os.path.join(res_path, res) file_utils.delete_folder(un_support_path) PrintLog('[un_support_path] : %s \n has been deleted ' % un_support_path) def merge_darwable_res_end_with_v4(apk_decompile_tmp_dir): """ 修改关于反编译后不符合要求的资源目录 eg:drawable-hdpi-v4 :param apk_decompile_tmp_dir: :return: """ res_path = os.path.join(apk_decompile_tmp_dir, 'res') for path in os.listdir(res_path): # print('res path %s' % path) if (path.startswith('drawable') or path.startswith('mipmap')) and path.endswith('-v4'): # print('merge path %s' % path) v4DrawablePath = os.path.join(res_path, path) drawablePath = os.path.join(res_path, path[:-3]) if os.path.exists(drawablePath): ret = file_utils.copy_file_all_dir( v4DrawablePath, drawablePath, True) if ret: return ret else: os.rename(v4DrawablePath, drawablePath) return 0 def remove_old_res(apk_decompile_tmp_dir): """ 该步骤未完善,考虑如果覆盖res资源,即可不用执行 :param apk_decompile_tmp_dir: :return: """ res_path = os.path.join(apk_decompile_tmp_dir, 'res') public_xml = os.path.join(res_path, 'values', 'public.xml') if os.path.exists(public_xml): file_utils.safeFileDelete(public_xml) sub_folders = os.listdir(res_path) for folder in sub_folders: sub_folder_path = os.path.join(res_path, folder) res_xml_docs = os.listdir(sub_folder_path) for xml in res_xml_docs: if xml.startswith('hnyy_') or xml.startswith('xy_') or xml.startswith('xy_') or xml.startswith('qj_') \ or xml.startswith('xinrui_') or xml.startswith('yyxx_'): xml_path = os.path.join(sub_folder_path, xml) print(xml_path) os.remove(xml_path) return 0 def remove_old_abi(apk_decompile_tmp_dir): libs_name = ['libdolin-zap.so', 'libmmkv.so', 'libsecsdk.so', 'libyyxxgame.so'] lib_path = os.path.join(apk_decompile_tmp_dir, 'lib') if not os.path.exists(lib_path): return abi_folders = os.listdir(lib_path) for abi_folder in abi_folders: abi_folder_path = os.path.join(lib_path, abi_folder) abi_files = os.listdir(abi_folder_path) for abi in abi_files: if abi in libs_name: abi_path = os.path.join(abi_folder_path, abi) os.remove(abi_path) PrintLog("remove [abi_path]:%s" % abi_path) def remove_same_values_res(apk_decompile_tmp_dir, channel_path): sdk_res_path = os.path.join(channel_path, 'res') temp_res_path = os.path.join(apk_decompile_tmp_dir, 'res') res_list = [] for value_dir in os.listdir(sdk_res_path): if not value_dir.startswith('values'): continue value_path = os.path.join(sdk_res_path, value_dir) for res_file in os.listdir(value_path): if res_file.endswith('.DS_Store'): continue res_list = xml_utils.read_all_res(os.path.join(value_path, res_file), res_list) if len(res_list) == 0: PrintLog('no same res found') return 0 # 移除相同的资源 for path in os.listdir(temp_res_path): if not path.startswith('values'): continue abs_path = os.path.join(temp_res_path, path) for res_file in os.listdir(abs_path): xml_utils.remove_same_values_res(os.path.join(abs_path, res_file), res_list) return 0 def copy_res(apk_decompile_tmp_dir, channel_path): sdk_res_path = os.path.join(channel_path, 'res') print('[sdk_res_path]: %s ' % sdk_res_path) temp_res_path = os.path.join(apk_decompile_tmp_dir, 'res') print('[temp_res_path]: %s ' % temp_res_path) for d in os.listdir(sdk_res_path): copy_res_with_type(sdk_res_path, temp_res_path, d) # 拷贝assets print('copy assets...') sdk_assets_path = os.path.join(channel_path, 'res') temp_assets_path = os.path.join(apk_decompile_tmp_dir, 'res') if os.path.exists(sdk_assets_path): ret = file_utils.copy_file_all_dir(sdk_assets_path, temp_assets_path) if ret: return ret def copy_res_with_type(res_path, decompile_res_path, type_name): # aapt的打包目录会带-v4后缀 resDir = os.path.join(res_path, type_name) target = os.path.join(decompile_res_path, type_name) targetV4 = os.path.join(decompile_res_path, type_name + '-v4') if not os.path.exists(target) and not os.path.exists(targetV4): os.makedirs(target) return file_utils.copyAllFile(resDir, target) elif not os.path.exists(target): return file_utils.copyAllFile(resDir, targetV4) else: return file_utils.copyAllFile(resDir, target) def merge_manifest_res(apk_decompile_tmp_dir, channel_path, sdk_name): """ 合并AndroidManifest.xml文件 """ sdkConfigPath = os.path.join(channel_path, sdk_name + '_sdk_config.xml') apkManifestPath = os.path.join(apk_decompile_tmp_dir, 'AndroidManifest.xml') return file_utils.mergeManifest(sdkConfigPath, apkManifestPath) def pack_jar(apk_decompile_tmp_dir, channel_path): """ 打包所有的jar :param apk_decompile_tmp_dir: :param channel_path: :param sdk_name: :return: """ temp_gen_path = os.path.join(apk_decompile_tmp_dir, 'gen') print('[temp_gen_path]: %s ' % temp_gen_path) if not os.path.exists(temp_gen_path): os.makedirs(temp_gen_path) if contants.IS_USE_AAPT2: dx = path_utils.get_dx_path() dex_cmd = '--dex --no-warning --output="%s"' % temp_gen_path else: dx = path_utils.get_d8_path() android_jar = path_utils.get_android_compile_tool_path() dex_cmd = '--lib "%s" --output "%s"' % (android_jar, temp_gen_path) # 找到所有lib依赖,编译成class.dex sdk_lib_path = os.path.join(channel_path, 'lib') path_list = file_utils.iterate_dir_path(sdk_lib_path) lib_list = '' for sdk_lib in path_list: if sdk_lib.endswith('.DS_Store'): continue lib_list += ' ' + sdk_lib dex_cmd = dex_cmd + lib_list PrintLog('packaging all jar ...') ret = apk_tool.exec_jar_cmd(dx, dex_cmd) if ret: return ret PrintLog('baksmali classes.dex ...') out_dex = os.path.join(temp_gen_path, 'classes.dex') bak_smali_path = path_utils.get_baksmali_path() out_smali_path = os.path.join(temp_gen_path, 'out') ret = apk_tool.exec_jar_cmd(bak_smali_path, 'd "%s" -o "%s"' % (out_dex, out_smali_path)) if ret: return ret # 将生成的文件拷贝到目标目录 print('copy all smali ...') temp_smali_path = os.path.join(apk_decompile_tmp_dir, 'smali') ret = file_utils.copy_file_all_dir(out_smali_path, temp_smali_path, True) if ret: return ret if __name__ == "__main__": pack_jar('/Users/kaiweicai/Documents/Project/PackKit/YYXXPackKit/game/817050/dcm_tmphuawei', '/Users/kaiweicai/Documents/Project/PackKit/YYXXPackKit/channel/huawei')