package_utils.py 16 KB

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