package_utils.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403
  1. from print_log import printlog
  2. import os
  3. import file_utils, xml_utils, path_utils, apk_tool, contants
  4. from xml.etree import ElementTree as ET
  5. def pack(apk_decompile_tmp_dir, channel_path, sdk_name, package_name, platform):
  6. printlog('删剔除母包融合SDK代码(cn/yyxx)')
  7. ret = remove_yyxx_old_code(apk_decompile_tmp_dir)
  8. if ret:
  9. return ret
  10. printlog('删除一些不支持的属性')
  11. ret = remove_no_support_attr(apk_decompile_tmp_dir)
  12. if ret:
  13. return ret
  14. printlog('修改minSdkVersion为21,删除res里仅支持21以下的资源目录')
  15. ret = change_un_support_config(apk_decompile_tmp_dir)
  16. if ret:
  17. return ret
  18. printlog('修改关于反编译后不符合要求的资源目录 eg:drawable-hdpi-v4')
  19. ret = merge_darwable_res_end_with_v4(apk_decompile_tmp_dir)
  20. if ret:
  21. return ret
  22. printlog('移除母包SDK的res资源')
  23. ret = remove_old_res(apk_decompile_tmp_dir, channel_path)
  24. if ret:
  25. return ret
  26. printlog('移除母包SDK的.so文件')
  27. ret = remove_old_abi(apk_decompile_tmp_dir)
  28. if ret:
  29. return ret
  30. printlog('移除打包目录中values的重复资源')
  31. ret = remove_same_values_res(apk_decompile_tmp_dir, platform)
  32. if ret:
  33. return ret
  34. printlog('复制渠道SDK资源')
  35. ret = copy_res(apk_decompile_tmp_dir, channel_path, platform)
  36. if ret:
  37. return ret
  38. # 合并主文件
  39. printlog('合并Androidmanifest.xml 文件')
  40. ret = merge_manifest_res(apk_decompile_tmp_dir, channel_path, sdk_name, platform)
  41. if ret:
  42. return ret
  43. # 打包lib依赖
  44. printlog('将SDK中jar包打进临时打包目录')
  45. ret = pack_jar(apk_decompile_tmp_dir, channel_path, platform)
  46. if ret:
  47. return ret
  48. # 修改${applicationId}
  49. printlog('修改替换AndroidManifest.xml中${applicationId}关键字')
  50. temp_manifest_path = os.path.join(apk_decompile_tmp_dir, 'AndroidManifest.xml')
  51. file_utils.replace_content(temp_manifest_path, '${applicationId}', package_name)
  52. # 生成R文件
  53. printlog('生成R文件')
  54. ret = apk_tool.create_R_file(apk_decompile_tmp_dir, package_name)
  55. if ret:
  56. return ret
  57. def remove_yyxx_old_code(apk_decompile_tmp_dir):
  58. yyxx_code_path = os.path.join(apk_decompile_tmp_dir, 'smali', 'cn', 'yyxx')
  59. file_utils.safeFileDelete(yyxx_code_path)
  60. return 0
  61. def remove_no_support_attr(apk_decompile_tmp_dir):
  62. """
  63. 删除一些不支持的属性
  64. """
  65. manifest = os.path.join(apk_decompile_tmp_dir, 'AndroidManifest.xml')
  66. xml_utils.remove_root_attr(manifest, 'compileSdkVersion')
  67. xml_utils.remove_root_attr(manifest, 'compileSdkVersionCodename')
  68. return 0
  69. def change_un_support_config(apk_decompile_tmp_dir):
  70. """
  71. 默认最低版本为21
  72. 1.修改 minSdkVersion 为21
  73. 2.删除res里仅支持21以下的资源目录
  74. :param apk_decompile_tmp_dir:
  75. :return:
  76. """
  77. min_sdk_version = 21
  78. yml = os.path.join(apk_decompile_tmp_dir, 'apktool.yml')
  79. file_utils.change_min_sdk_version(yml, min_sdk_version)
  80. res_path = os.path.join(apk_decompile_tmp_dir, 'res')
  81. tag = '-v'
  82. for res in os.listdir(res_path):
  83. # print('res = ' + res)
  84. if res.startswith('values') and tag in res:
  85. start = res.index(tag)
  86. version = res[start + len(tag):]
  87. if not version.isdigit():
  88. continue
  89. version = int(version)
  90. printlog('version = %d' % version)
  91. if version < min_sdk_version:
  92. un_support_path = os.path.join(res_path, res)
  93. file_utils.delete_folder(un_support_path)
  94. printlog('[un_support_path] : %s \n has been deleted ' % un_support_path)
  95. def merge_darwable_res_end_with_v4(apk_decompile_tmp_dir):
  96. """
  97. 修改关于反编译后不符合要求的资源目录
  98. eg:drawable-hdpi-v4
  99. :param apk_decompile_tmp_dir:
  100. :return:
  101. """
  102. res_path = os.path.join(apk_decompile_tmp_dir, 'res')
  103. for path in os.listdir(res_path):
  104. # print('res path %s' % path)
  105. if (path.startswith('drawable') or path.startswith('mipmap')) and path.endswith('-v4'):
  106. # print('merge path %s' % path)
  107. v4DrawablePath = os.path.join(res_path, path)
  108. drawablePath = os.path.join(res_path, path[:-3])
  109. if os.path.exists(drawablePath):
  110. ret = file_utils.copy_file_all_dir(
  111. v4DrawablePath, drawablePath, True)
  112. if ret:
  113. return ret
  114. else:
  115. os.rename(v4DrawablePath, drawablePath)
  116. return 0
  117. def remove_old_res(apk_decompile_tmp_dir, channel_path):
  118. """
  119. 该步骤未完善,考虑如果覆盖res资源,即可不用执行
  120. :param apk_decompile_tmp_dir:
  121. :return:
  122. """
  123. res_path = os.path.join(apk_decompile_tmp_dir, 'res')
  124. temp_public_xml = os.path.join(res_path, 'values', 'public.xml')
  125. if os.path.exists(temp_public_xml):
  126. printlog('remove sdk node in public.xml ')
  127. temp_public_xml_tree = ET.parse(temp_public_xml)
  128. root = temp_public_xml_tree.getroot()
  129. for node in list(root):
  130. name = node.attrib.get('name')
  131. if name.startswith('hnyy_') or name.startswith('xy_') or name.startswith('xy_') or name.startswith('qj_') \
  132. or name.startswith('xinrui_') or name.startswith('yyxx_comm'):
  133. root.remove(node)
  134. temp_public_xml_tree.write(temp_public_xml, 'UTF-8')
  135. sub_folders = os.listdir(res_path)
  136. for folder in sub_folders:
  137. sub_folder_path = os.path.join(res_path, folder)
  138. res_xml_docs = os.listdir(sub_folder_path)
  139. for xml in res_xml_docs:
  140. if xml.startswith('hnyy_') or xml.startswith('xy_') or xml.startswith('xy_') or xml.startswith('qj_') \
  141. or xml.startswith('xinrui_') or xml.startswith('yyxx_comm'):
  142. xml_path = os.path.join(sub_folder_path, xml)
  143. print(xml_path)
  144. os.remove(xml_path)
  145. return 0
  146. def remove_old_abi(apk_decompile_tmp_dir):
  147. libs_name = ['libdolin-zap.so', 'libmmkv.so', 'libsecsdk.so', 'libyyxxgame.so']
  148. lib_path = os.path.join(apk_decompile_tmp_dir, 'lib')
  149. if not os.path.exists(lib_path):
  150. return
  151. abi_folders = os.listdir(lib_path)
  152. for abi_folder in abi_folders:
  153. abi_folder_path = os.path.join(lib_path, abi_folder)
  154. abi_files = os.listdir(abi_folder_path)
  155. for abi in abi_files:
  156. if abi in libs_name:
  157. abi_path = os.path.join(abi_folder_path, abi)
  158. os.remove(abi_path)
  159. printlog("remove [abi_path]:%s" % abi_path)
  160. def remove_same_values_res(apk_decompile_tmp_dir, platform):
  161. platform_res_path = os.path.join(platform, 'res')
  162. temp_res_path = os.path.join(apk_decompile_tmp_dir, 'res')
  163. res_list = []
  164. for value_dir in os.listdir(platform_res_path):
  165. if not value_dir.startswith('values'):
  166. continue
  167. value_path = os.path.join(platform_res_path, value_dir)
  168. for res_file in os.listdir(value_path):
  169. if res_file.endswith('.DS_Store'):
  170. continue
  171. if res_file.startswith('hnyy_') or res_file.startswith('xy_') or res_file.startswith(
  172. 'xy_') or res_file.startswith('qj_') \
  173. or res_file.startswith('xinrui_') or res_file.startswith('yyxx_comm_'):
  174. res_list = xml_utils.read_all_res(os.path.join(value_path, res_file), res_list)
  175. if len(res_list) == 0:
  176. printlog('no same res found')
  177. return 0
  178. # 移除相同的资源
  179. for path in os.listdir(temp_res_path):
  180. if not path.startswith('values'):
  181. continue
  182. abs_path = os.path.join(temp_res_path, path)
  183. for res_file in os.listdir(abs_path):
  184. xml_utils.remove_same_values_res(os.path.join(abs_path, res_file), res_list)
  185. return 0
  186. def copy_res(apk_decompile_tmp_dir, channel_path, platform):
  187. sdk_res_path = os.path.join(channel_path, 'res')
  188. printlog('[sdk_res_path]: %s ' % sdk_res_path)
  189. temp_res_path = os.path.join(apk_decompile_tmp_dir, 'res')
  190. printlog('[temp_res_path]: %s ' % temp_res_path)
  191. platform_res_path = os.path.join(platform, 'res')
  192. printlog('begin copy sdk res.')
  193. for d in os.listdir(sdk_res_path):
  194. copy_res_with_type(sdk_res_path, temp_res_path, d)
  195. printlog('begin copy platform res')
  196. for r in os.listdir(platform_res_path):
  197. copy_res_with_type(platform_res_path, temp_res_path, r)
  198. # 拷贝assets
  199. printlog('copy assets...')
  200. sdk_assets_path = os.path.join(channel_path, 'assets')
  201. temp_assets_path = os.path.join(apk_decompile_tmp_dir, 'assets')
  202. platform_assets_path = os.path.join(platform, 'assets')
  203. if os.path.exists(sdk_assets_path):
  204. ret = file_utils.copy_file_all_dir(sdk_assets_path, temp_assets_path)
  205. if ret:
  206. return ret
  207. if os.path.exists(platform_assets_path):
  208. ret = file_utils.copy_file_all_dir(sdk_assets_path, platform_assets_path)
  209. if ret:
  210. return ret
  211. # 拷贝jniLib
  212. printlog('copy lib...')
  213. temp_jni_path = os.path.join(apk_decompile_tmp_dir, 'lib')
  214. sdk_jni_path = os.path.join(channel_path, 'jniLibs')
  215. platform_jni_path = os.path.join(platform, 'jniLibs')
  216. if not os.path.exists(temp_jni_path):
  217. printlog('There is no need to merge lib .[temp_jni_path:]:%s' % temp_jni_path)
  218. return 0
  219. abiFilters = []
  220. if os.path.exists(temp_jni_path):
  221. for abi in os.listdir(temp_jni_path):
  222. printlog('append : ' + abi)
  223. abiFilters.append(abi)
  224. else:
  225. abiFilters = ['armeabi-v7a']
  226. if os.path.exists(sdk_jni_path):
  227. ret = file_utils.copy_file_all_dir(
  228. sdk_jni_path, temp_jni_path, False, abiFilters)
  229. if ret:
  230. return ret
  231. if os.path.exists(platform_jni_path):
  232. ret = file_utils.copy_file_all_dir(
  233. platform_jni_path, temp_jni_path, False, abiFilters)
  234. if ret:
  235. return ret
  236. return 0
  237. def copy_res_with_type(res_path, decompile_res_path, type_name):
  238. # aapt的打包目录会带-v4后缀
  239. resDir = os.path.join(res_path, type_name)
  240. target = os.path.join(decompile_res_path, type_name)
  241. targetV4 = os.path.join(decompile_res_path, type_name + '-v4')
  242. if not os.path.exists(target) and not os.path.exists(targetV4):
  243. os.makedirs(target)
  244. return file_utils.copyAllFile(resDir, target)
  245. elif not os.path.exists(target):
  246. return file_utils.copyAllFile(resDir, targetV4)
  247. else:
  248. return file_utils.copyAllFile(resDir, target)
  249. def merge_manifest_res(apk_decompile_tmp_dir, channel_path, sdk_name, platform):
  250. """
  251. 合并AndroidManifest.xml文件
  252. """
  253. sdk_manifest_path = os.path.join(channel_path, sdk_name + '_sdk_config.xml')
  254. temp_manifest_path = os.path.join(apk_decompile_tmp_dir, 'AndroidManifest.xml')
  255. platform_manifest_path = os.path.join(platform, 'platform_sdk_config.xml')
  256. ret = file_utils.mergeManifest(sdk_manifest_path, temp_manifest_path)
  257. if ret:
  258. return ret
  259. ret = file_utils.mergeManifest(platform_manifest_path, temp_manifest_path)
  260. if ret:
  261. return ret
  262. def pack_jar(apk_decompile_tmp_dir, channel_path, platform):
  263. """
  264. 打包所有的jar
  265. :param platform:
  266. :param apk_decompile_tmp_dir:
  267. :param channel_path:
  268. :param sdk_name:
  269. :return:
  270. """
  271. temp_gen_path = os.path.join(apk_decompile_tmp_dir, 'gen')
  272. print('[temp_gen_path]: %s ' % temp_gen_path)
  273. if not os.path.exists(temp_gen_path):
  274. os.makedirs(temp_gen_path)
  275. if contants.IS_USE_AAPT2:
  276. dx = path_utils.get_dx_path()
  277. dex_cmd = '--dex --no-warning --output="%s"' % temp_gen_path
  278. else:
  279. dx = path_utils.get_d8_path()
  280. android_jar = path_utils.get_android_compile_tool_path()
  281. dex_cmd = '--lib "%s" --output "%s"' % (android_jar, temp_gen_path)
  282. # 找到所有lib依赖,编译成class.dex
  283. sdk_lib_path = os.path.join(channel_path, 'lib')
  284. platform_lib_path = os.path.join(platform, 'lib')
  285. lib_list = ''
  286. for sdk_lib in file_utils.iterate_dir_path(sdk_lib_path):
  287. if sdk_lib.endswith('.DS_Store'):
  288. continue
  289. lib_list += ' ' + sdk_lib
  290. for platform_lib in file_utils.iterate_dir_path(platform_lib_path):
  291. if platform_lib.endswith('.DS_Store'):
  292. continue
  293. lib_list += ' ' + platform_lib
  294. dex_cmd = dex_cmd + lib_list
  295. printlog('packaging all jar ...')
  296. ret = apk_tool.exec_jar_cmd(dx, dex_cmd)
  297. if ret:
  298. return ret
  299. printlog('baksmali classes.dex ...')
  300. out_dex = os.path.join(temp_gen_path, 'classes.dex')
  301. bak_smali_path = path_utils.get_baksmali_path()
  302. out_smali_path = os.path.join(temp_gen_path, 'out')
  303. ret = apk_tool.exec_jar_cmd(bak_smali_path, 'd "%s" -o "%s"' % (out_dex, out_smali_path))
  304. if ret:
  305. return ret
  306. # 将生成的文件拷贝到目标目录
  307. print('copy all smali ...')
  308. temp_smali_path = os.path.join(apk_decompile_tmp_dir, 'smali')
  309. ret = file_utils.copy_file_all_dir(out_smali_path, temp_smali_path, True)
  310. if ret:
  311. return ret
  312. def remove_public_tag_node(public_path):
  313. if os.path.exists(public_path):
  314. printlog('remove sdk node in public.xml ')
  315. temp_public_xml_tree = ET.parse(public_path)
  316. root = temp_public_xml_tree.getroot()
  317. for node in list(root):
  318. name = node.attrib.get('name')
  319. if name.startswith('hnyy_') or name.startswith('xy_') or name.startswith('xy_') or name.startswith('qj_') \
  320. or name.startswith('xinrui_') or name.startswith('yyxx_comm'):
  321. root.remove(node)
  322. temp_public_xml_tree.write(public_path, 'UTF-8')
  323. if __name__ == "__main__":
  324. remove_public_tag_node("/Users/kaiweicai/Desktop/apk/public.xml")