from V2.print_log import printlog import os from V2 import file_utils, apk_tool, path_utils, contants, xml_utils from xml.etree import ElementTree as ET def pack(apk_decompile_tmp_dir, channel_path, sdk_name, package_name, platform): 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, channel_path) 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, platform) if ret: return ret printlog('复制渠道SDK资源') ret = copy_res(apk_decompile_tmp_dir, channel_path, platform) if ret: return ret # 合并主文件 printlog('合并Androidmanifest.xml 文件') ret = merge_manifest_res(apk_decompile_tmp_dir, channel_path, sdk_name, platform) if ret: return ret # 打包lib依赖 printlog('将SDK中jar包打进临时打包目录') ret = pack_jar(apk_decompile_tmp_dir, channel_path, platform) 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, channel_path): """ 该步骤未完善,考虑如果覆盖res资源,即可不用执行 :param apk_decompile_tmp_dir: :return: """ res_path = os.path.join(apk_decompile_tmp_dir, 'res') temp_public_xml = os.path.join(res_path, 'values', 'public.xml') if os.path.exists(temp_public_xml): printlog('remove sdk node in public.xml ') temp_public_xml_tree = ET.parse(temp_public_xml) root = temp_public_xml_tree.getroot() for node in list(root): name = node.attrib.get('name') if name.startswith('hnyy_') or name.startswith('xy_') or name.startswith('xy_') or name.startswith('qj_') \ or name.startswith('xinrui_') or name.startswith('yyxx_comm'): root.remove(node) temp_public_xml_tree.write(temp_public_xml, 'UTF-8') 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_comm'): 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, platform): platform_res_path = os.path.join(platform, 'res') temp_res_path = os.path.join(apk_decompile_tmp_dir, 'res') res_list = [] for value_dir in os.listdir(platform_res_path): if not value_dir.startswith('values'): continue value_path = os.path.join(platform_res_path, value_dir) for res_file in os.listdir(value_path): if res_file.endswith('.DS_Store'): continue if res_file.startswith('hnyy_') or res_file.startswith('xy_') or res_file.startswith( 'xy_') or res_file.startswith('qj_') \ or res_file.startswith('xinrui_') or res_file.startswith('yyxx_comm_'): 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, platform): sdk_res_path = os.path.join(channel_path, 'res') printlog('[sdk_res_path]: %s ' % sdk_res_path) temp_res_path = os.path.join(apk_decompile_tmp_dir, 'res') printlog('[temp_res_path]: %s ' % temp_res_path) platform_res_path = os.path.join(platform, 'res') printlog('begin copy sdk res.') for d in os.listdir(sdk_res_path): copy_res_with_type(sdk_res_path, temp_res_path, d) printlog('begin copy platform res') for r in os.listdir(platform_res_path): copy_res_with_type(platform_res_path, temp_res_path, r) # 拷贝assets printlog('copy assets...') sdk_assets_path = os.path.join(channel_path, 'assets') temp_assets_path = os.path.join(apk_decompile_tmp_dir, 'assets') platform_assets_path = os.path.join(platform, 'assets') if os.path.exists(sdk_assets_path): ret = file_utils.copy_file_all_dir(sdk_assets_path, temp_assets_path) if ret: return ret if os.path.exists(platform_assets_path): ret = file_utils.copy_file_all_dir(sdk_assets_path, platform_assets_path) if ret: return ret # 拷贝jniLib printlog('copy lib...') temp_jni_path = os.path.join(apk_decompile_tmp_dir, 'lib') sdk_jni_path = os.path.join(channel_path, 'jniLibs') platform_jni_path = os.path.join(platform, 'jniLibs') if not os.path.exists(temp_jni_path): printlog('There is no need to merge lib .[temp_jni_path:]:%s' % temp_jni_path) return 0 abiFilters = [] if os.path.exists(temp_jni_path): for abi in os.listdir(temp_jni_path): printlog('append : ' + abi) abiFilters.append(abi) else: abiFilters = ['armeabi-v7a'] if os.path.exists(sdk_jni_path): ret = file_utils.copy_file_all_dir( sdk_jni_path, temp_jni_path, False, abiFilters) if ret: return ret if os.path.exists(platform_jni_path): ret = file_utils.copy_file_all_dir( platform_jni_path, temp_jni_path, False, abiFilters) if ret: return ret return 0 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, platform): """ 合并AndroidManifest.xml文件 """ sdk_manifest_path = os.path.join(channel_path, sdk_name + '_sdk_config.xml') temp_manifest_path = os.path.join(apk_decompile_tmp_dir, 'AndroidManifest.xml') platform_manifest_path = os.path.join(platform, 'platform_sdk_config.xml') ret = file_utils.mergeManifest(sdk_manifest_path, temp_manifest_path) if ret: return ret ret = file_utils.mergeManifest(platform_manifest_path, temp_manifest_path) if ret: return ret def pack_jar(apk_decompile_tmp_dir, channel_path, platform): """ 打包所有的jar :param platform: :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') platform_lib_path = os.path.join(platform, 'lib') lib_list = '' for sdk_lib in file_utils.iterate_dir_path(sdk_lib_path): if sdk_lib.endswith('.DS_Store'): continue lib_list += ' ' + sdk_lib for platform_lib in file_utils.iterate_dir_path(platform_lib_path): if platform_lib.endswith('.DS_Store'): continue lib_list += ' ' + platform_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 def remove_public_tag_node(public_path): if os.path.exists(public_path): printlog('remove sdk node in public.xml ') temp_public_xml_tree = ET.parse(public_path) root = temp_public_xml_tree.getroot() for node in list(root): name = node.attrib.get('name') if name.startswith('hnyy_') or name.startswith('xy_') or name.startswith('xy_') or name.startswith('qj_') \ or name.startswith('xinrui_') or name.startswith('yyxx_comm'): root.remove(node) temp_public_xml_tree.write(public_path, 'UTF-8') if __name__ == "__main__": remove_public_tag_node("/Users/kaiweicai/Desktop/apk/public.xml")