123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487 |
- from print_log import printlog
- import os
- import file_utils, apk_tool, path_utils, contants, xml_utils, merge_share_sdk
- from xml.etree import ElementTree as ET
- androidNS = 'http://schemas.android.com/apk/res/android'
- def pack(apk_decompile_tmp_dir, channel_path, package_name, config):
- 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以下的资源目录')
- change_un_support_config(apk_decompile_tmp_dir)
- 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文件')
- 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 文件')
- sdk_base_xml = os.path.join(channel_path, "SDKManifest.xml")
- apk_decom_dir_manifest_xml = os.path.join(apk_decompile_tmp_dir, 'AndroidManifest.xml')
- ret = mergeManifest(apk_decom_dir_manifest_xml, sdk_base_xml)
- if ret:
- return ret
- if 'WEIBO_SHARE' in config or 'QQ_SHARE' in config or 'WECHAT_SHARE' in config:
- merge_share_sdk.start_merge(apk_decompile_tmp_dir, config)
- # 修改${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')
- 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('yyxx_ui_') 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('yyxx_ui_') 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, channel_path):
- channel_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(channel_res_path):
- if not value_dir.startswith('values'):
- continue
- value_path = os.path.join(channel_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):
- 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)
- printlog('begin copy sdk res.')
- for d in os.listdir(sdk_res_path):
- copy_res_with_type(sdk_res_path, temp_res_path, d)
- # 拷贝assets
- printlog('copy assets...')
- sdk_assets_path = os.path.join(channel_path, 'assets')
- temp_assets_path = os.path.join(apk_decompile_tmp_dir, 'assets')
- # 拷贝smali_classes2
- printlog('copy smali_classes2...')
- sdk_smali_path = os.path.join(channel_path, 'smali_classes2')
- temp_smali_path = os.path.join(apk_decompile_tmp_dir, 'smali_classes2')
- if os.path.exists(sdk_smali_path):
- ret = file_utils.copy_file_all_dir(sdk_smali_path, temp_smali_path)
- if ret:
- return ret
- if os.path.exists(sdk_assets_path):
- ret = file_utils.copy_file_all_dir(sdk_assets_path, temp_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')
- 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
- 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_aar_to_sdk(manifest_from, manifest_to):
- """
- Merge aars AndroidManifest.xml to the sdk SdkManifest.xml
- """
- if not os.path.exists(manifest_to) or not os.path.exists(manifest_from):
- print("the manifest file is not exists.manifestTo:%s;manifestFrom:%s", manifest_to, manifest_from)
- return False
- ET.register_namespace('android', androidNS)
- targetTree = ET.parse(manifest_to)
- # 获取xml根节点
- targetRoot = targetTree.getroot()
- ET.register_namespace('android', androidNS)
- aarTree = ET.parse(manifest_from)
- aarRoot = aarTree.getroot()
- f = open(manifest_to)
- targetContent = f.read()
- f.close()
- permissionConfigNode = targetRoot.find('permissionConfig')
- for child in list(aarRoot):
- # 子节点对应的values,uses-feature,uses-permission,permission
- key = '{' + androidNS + '}name'
- val = child.get(key)
- if val != None and len(val) > 0:
- attrIndex = targetContent.find(val)
- # values不存在添加
- if -1 == attrIndex:
- permissionConfigNode.append(child)
- aarAppNode = aarRoot.find('application')
- sdkAppConfigNode = targetRoot.find('applicationConfig')
- if aarAppNode is not None:
- for aarChild in list(aarAppNode):
- key = '{' + androidNS + '}name'
- val = aarChild.get(key)
- if val != None and len(val) > 0:
- attrIndex = targetContent.find(val)
- if -1 == attrIndex:
- sdkAppConfigNode.append(aarChild)
- targetTree.write(manifest_to, 'UTF-8')
- return True
- 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 mergeManifest(targetManifest, sdkManifest):
- """
- Merge sdk SdkManifest.xml to the apk AndroidManifest.xml
- """
- if not os.path.exists(targetManifest) or not os.path.exists(sdkManifest):
- printlog("the manifest file is not exists.targetManifest:%s;sdkManifest:%s" % (targetManifest, sdkManifest))
- return 1
- ET.register_namespace('android', androidNS)
- targetTree = ET.parse(targetManifest)
- targetRoot = targetTree.getroot()
- ET.register_namespace('android', androidNS)
- sdkTree = ET.parse(sdkManifest)
- sdkRoot = sdkTree.getroot()
- f = open(targetManifest)
- targetContent = f.read()
- f.close()
- permissionConfigNode = sdkRoot.find('permissionConfig')
- if permissionConfigNode is not None and len(permissionConfigNode) > 0:
- for child in list(permissionConfigNode):
- key = '{' + androidNS + '}name'
- val = child.get(key)
- if val != None and len(val) > 0:
- attrIndex = targetContent.find(val)
- if -1 == attrIndex:
- targetRoot.append(child)
- appConfigNode = sdkRoot.find('applicationConfig')
- appNode = targetRoot.find('application')
- if appConfigNode is not None:
- for child in list(appConfigNode):
- appNode.append(child)
- targetTree.write(targetManifest, 'UTF-8')
- return 0
- 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('yyxx_ui_') 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")
|