apk_tool.py 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. # -*- coding:utf-8 -*-
  2. import file_utils, path_utils, contants
  3. import os, subprocess, platform, re
  4. from print_log import printlog
  5. # decompile apk
  6. def decompile(origin_apk_full_path, apk_decompile_out_dir):
  7. printlog("------------------------ start to decompiling -------------------------")
  8. global SUFFIX_BAT
  9. if not os.path.exists(origin_apk_full_path):
  10. printlog("[no such apk]:%s" % origin_apk_full_path)
  11. return 1
  12. apktool_path = path_utils.get_apktool_path()
  13. decompileCmd = " d -f -o %s/ %s" % (apk_decompile_out_dir, origin_apk_full_path)
  14. ret = exec_jar_cmd(apktool_path, decompileCmd)
  15. printlog("------------------------ finished decompiling -------------------------")
  16. return ret
  17. # recompile fileset to apk
  18. def recompile(apk_decompile_out_dir, out_put_unsigned_apk_path):
  19. printlog("------------------------------ start to recompile ------------------------------ ")
  20. apktool_path = path_utils.get_apktool_path()
  21. useAppt2 = ''
  22. if contants.is_use_aapt2:
  23. useAppt2 = ' --use-aapt2'
  24. recompileCmd = 'b -f "%s" -o "%s"' % (apk_decompile_out_dir, out_put_unsigned_apk_path)
  25. ret = exec_jar_cmd(apktool_path, recompileCmd)
  26. printlog("------------------------------ finished recompile ------------------------------ ")
  27. return ret
  28. def V1signer(out_put_unsigned_apk_path, out_put_signed_apk_path, keystore_path, storepass, alias, keypass):
  29. getKeystoreAlgorithmCmd = "%s -list -v -keystore %s -alias %s -storepass %s" % (
  30. contants.key_tool_path, keystore_path, alias, storepass)
  31. ret, result = exec_format_cmd_with_result(getKeystoreAlgorithmCmd)
  32. regex = re.compile('签名算法名称: (\S+)')
  33. algorithm_method = regex.findall(result)
  34. if algorithm_method:
  35. algorithm_method = algorithm_method[0]
  36. else:
  37. regex = re.compile('Signature algorithm name: (\S+)')
  38. algorithm_method = regex.findall(result)
  39. if algorithm_method:
  40. algorithm_method = algorithm_method[0]
  41. if not algorithm_method:
  42. printlog("获取签名算法失败,签名失败。")
  43. return 1
  44. printlog("签名文件算法为:%s" % algorithm_method)
  45. resignCmd = "%s -sigalg %s -digestalg SHA1 -storepass %s -keypass %s -keystore %s -signedjar %s %s %s" % (
  46. contants.jar_signer_tool_path, algorithm_method, storepass, keypass, keystore_path, out_put_signed_apk_path,
  47. out_put_unsigned_apk_path, alias)
  48. return exec_format_cmd(resignCmd)
  49. def signer(out_put_unsigned_apk_path, out_put_signed_apk_path, keystore_path, storepass, alias, keypass):
  50. """
  51. 签名apk
  52. """
  53. # if game in signConfig:
  54. # if sdk in signConfig[game]:
  55. # keystore = signConfig[game][sdk]
  56. # else:
  57. # keystore = signConfig['default']
  58. # else:
  59. # keystore = signConfig['default']
  60. apksigner = path_utils.get_apksigner_path()
  61. # java -jar apksigner.jar sign --ks key.jks --ks-key-alias releasekey --ks-pass pass:pp123456 --key-pass pass:pp123456 --out output.apk input.apk
  62. useV2sign = ''
  63. if not contants.is_use_apk_v2_sign:
  64. useV2sign = ' --v2-signing-enabled=false'
  65. return exec_jar_cmd(apksigner,
  66. 'sign%s --ks "%s" --ks-key-alias %s --ks-pass pass:%s --key-pass pass:%s --out "%s" "%s"' % (
  67. useV2sign, keystore_path, alias, storepass,
  68. keypass, out_put_signed_apk_path, out_put_unsigned_apk_path))
  69. def zipalign(out_put_signed_apk_path, out_put_zipalign_apk_path):
  70. align_apk_tool = path_utils.get_zipalign_path()
  71. get_exec_permission(align_apk_tool)
  72. return exec_format_cmd('%s -f -p 4 %s %s' % (align_apk_tool, out_put_signed_apk_path, out_put_zipalign_apk_path))
  73. def exec_jar_cmd(jar, params):
  74. cmd = 'java -jar "%s" %s' % (jar, params)
  75. printlog("[exec_jar_cmd]:%s" % cmd)
  76. ret, result = exec_common_cmd(cmd)
  77. return ret
  78. def exec_format_cmd(cmd):
  79. ret, result = exec_common_cmd(cmd)
  80. return ret
  81. def exec_format_cmd_with_result(cmd):
  82. ret, result = exec_common_cmd(cmd)
  83. return ret, result
  84. def exec_common_cmd(cmd, cd=None):
  85. """
  86. 执行cmd命令
  87. 返回值:None —— 子进程尚未结束;
  88. ==0 —— 子进程正常退出;
  89. > 0—— 子进程异常退出,return code对应于出错码;
  90. < 0—— 子进程被信号杀掉了。
  91. """
  92. '''print(cmd)
  93. p = os.popen(cmd)
  94. print(p.read())''
  95. '''
  96. try:
  97. s = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, cwd=cd, encoding='utf-8')
  98. std_output, err_output = s.communicate()
  99. if platform.system() == 'Windows':
  100. std_output = std_output.decode('gbk')
  101. err_output = err_output.decode('gbk')
  102. '''
  103. None —— 子进程尚未结束;
  104. ==0 —— 子进程正常退出;
  105. > 0—— 子进程异常退出,return code对应于出错码;
  106. < 0—— 子进程被信号杀掉了。
  107. '''
  108. ret = s.returncode
  109. if ret:
  110. print('*******ERROR*******')
  111. print(std_output)
  112. print(err_output)
  113. print('*******************')
  114. cmd = 'error::' + cmd + ' !!!exec Fail!!! '
  115. else:
  116. print(std_output)
  117. print(err_output)
  118. cmd = cmd + ' !!!exec success!!! '
  119. print(cmd)
  120. except Exception as e:
  121. print(e)
  122. return 1, e
  123. return ret, std_output
  124. def create_R_file(apk_decompile_tmp_dir, package_name):
  125. decomplie_res_path = os.path.join(apk_decompile_tmp_dir, 'res')
  126. temp_gen_path = os.path.join(apk_decompile_tmp_dir, 'gen')
  127. if not os.path.exists(temp_gen_path):
  128. os.mkdir(temp_gen_path)
  129. temp_manifest_path = os.path.join(apk_decompile_tmp_dir, 'AndroidManifest.xml')
  130. android_platforms = path_utils.get_android_compile_tool_path()
  131. if not contants.is_use_aapt2:
  132. aapt = path_utils.get_aapt_path()
  133. ret = get_exec_permission(aapt)
  134. if ret:
  135. return ret
  136. createRCmd = '"%s" p -f -m -J "%s" -S "%s" -I "%s" -M "%s"' % (
  137. aapt, temp_gen_path, decomplie_res_path, android_platforms, temp_manifest_path)
  138. ret = exec_format_cmd(createRCmd)
  139. if ret:
  140. return ret
  141. else:
  142. aapt2 = path_utils.get_aapt2_path()
  143. ret = get_exec_permission(aapt2)
  144. if ret:
  145. return ret
  146. complie_res_zip = os.path.join(temp_gen_path, 'resource.zip')
  147. if os.path.exists(complie_res_zip):
  148. printlog('resource.zip is exists , No need to compile again ')
  149. else:
  150. complieResCmd = '%s compile --dir %s -o %s ' % (
  151. aapt2, decomplie_res_path, complie_res_zip)
  152. ret = exec_format_cmd(complieResCmd)
  153. if ret:
  154. return ret
  155. # link
  156. print('link res ...')
  157. outApk = os.path.join(temp_gen_path, 'sources.apk')
  158. linkResCmd = '%s link -o %s -I %s --manifest %s --java %s/ %s --custom-package %s' % (
  159. aapt2, outApk, android_platforms, temp_manifest_path, temp_gen_path, complie_res_zip,package_name)
  160. print('link cmd is %s' % linkResCmd)
  161. ret = exec_format_cmd(linkResCmd)
  162. if ret:
  163. return ret
  164. # 编译R文件
  165. r_source_pkg_name = package_name.replace(".", "/")
  166. r_pkg_path = os.path.join(temp_gen_path, r_source_pkg_name)
  167. r_source_path = os.path.join(r_pkg_path, 'R.java')
  168. createRClassCmd = '%s -source 1.8 -target 1.8 -encoding UTF-8 "%s"' % (contants.javac_path, r_source_path)
  169. ret = exec_format_cmd(createRClassCmd)
  170. if ret:
  171. return ret
  172. # 生成dex
  173. print('dex R.class ...')
  174. outDex = os.path.join(temp_gen_path, 'classes.dex')
  175. if not contants.is_use_aapt2:
  176. dx = path_utils.get_dx_path()
  177. dexCmd = '--dex --no-warning --output="%s" "%s"' % (
  178. outDex, temp_gen_path)
  179. else:
  180. dx = path_utils.get_d8_path()
  181. clazz = os.path.join(r_pkg_path, '*.class')
  182. dexCmd = '--lib "%s" --output "%s" %s' % (
  183. android_platforms, temp_gen_path, clazz)
  184. ret = exec_jar_cmd(dx, dexCmd)
  185. if ret:
  186. return ret
  187. # 反向dex生成smali
  188. # 存放在out目录
  189. print('baksmali classes.dex ...')
  190. bak_smali_path = path_utils.get_baksmali_path()
  191. out_smali_path = os.path.join(temp_gen_path, 'out')
  192. ret = exec_jar_cmd(
  193. bak_smali_path, 'd "%s" -o "%s"' % (outDex, out_smali_path))
  194. if ret:
  195. return ret
  196. # 将生成的文件拷贝到目标目录
  197. print('copy R.smali ...')
  198. smaliPath = os.path.join(apk_decompile_tmp_dir, 'smali')
  199. file_utils.copy_file_all_dir(out_smali_path, smaliPath)
  200. return 0
  201. def get_exec_permission(file):
  202. """
  203. linux下获取执行权限
  204. """
  205. if platform.system() == 'Windows':
  206. return 0
  207. return exec_format_cmd('chmod +x "%s"' % file)
  208. if __name__ == "__main__":
  209. V1signer('/Users/kaiweicai/Documents/Project/PackKit/YYXXPackKit/output/g1_huawei_com.yyxx.qyj2.huawei_Y010402.apk',
  210. '/Users/kaiweicai/Documents/Project/PackKit/YYXXPackKit/output/g1_huawei_com.yyxx.qyj2.huawei_Y010402_____.apk',
  211. '/Users/kaiweicai/Documents/Project/PackKit/YYXXPackKit/keystore/ziyun.jks'
  212. , 'yh1234', 'key0', 'yh1234')