apk_tool.py 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. # -*- coding:utf-8 -*-
  2. import path_utils, contants, file_utils
  3. import os, subprocess, platform, zipfile, 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"%s' % (apk_decompile_out_dir, out_put_unsigned_apk_path, useAppt2)
  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. return exec_format_cmd(
  72. '"%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('Exception ' + 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. complieResCmd = '%s compile --dir %s -o %s ' % (
  148. aapt2, decomplie_res_path, complie_res_zip)
  149. ret = exec_format_cmd(complieResCmd)
  150. if ret:
  151. return ret
  152. # link
  153. print('link res ...')
  154. outApk = os.path.join(temp_gen_path, 'sources.apk')
  155. linkResCmd = '%s link -o %s -I %s --manifest %s --java %s/ %s' % (
  156. aapt2, outApk, android_platforms, temp_manifest_path, temp_gen_path, complie_res_zip)
  157. print('link cmd is %s' % linkResCmd)
  158. ret = exec_format_cmd(linkResCmd)
  159. if ret:
  160. return ret
  161. # 编译R文件
  162. r_source_pkg_name = package_name.replace(".", "/")
  163. r_pkg_path = os.path.join(temp_gen_path, r_source_pkg_name)
  164. r_source_path = os.path.join(r_pkg_path, 'R.java')
  165. createRClassCmd = 'javac -source 1.8 -target 1.8 -encoding UTF-8 "%s"' % r_source_path
  166. ret = exec_format_cmd(createRClassCmd)
  167. if ret:
  168. return ret
  169. # 生成dex
  170. print('dex R.class ...')
  171. outDex = os.path.join(temp_gen_path, 'classes.dex')
  172. if not contants.IS_USE_AAPT2:
  173. dx = path_utils.get_dx_path()
  174. dexCmd = '--dex --no-warning --output="%s" "%s"' % (
  175. outDex, temp_gen_path)
  176. else:
  177. dx = path_utils.get_d8_path()
  178. clazz = os.path.join(r_pkg_path, '*.class')
  179. dexCmd = '--lib "%s" --output "%s" %s' % (
  180. android_platforms, temp_gen_path, clazz)
  181. ret = exec_jar_cmd(dx, dexCmd)
  182. if ret:
  183. return ret
  184. # 反向dex生成smali
  185. # 存放在out目录
  186. print('baksmali classes.dex ...')
  187. bak_smali_path = path_utils.get_baksmali_path()
  188. out_smali_path = os.path.join(temp_gen_path, 'out')
  189. ret = exec_jar_cmd(
  190. bak_smali_path, 'd "%s" -o "%s"' % (outDex, out_smali_path))
  191. if ret:
  192. return ret
  193. # 将生成的文件拷贝到目标目录
  194. print('copy R.smali ...')
  195. smaliPath = os.path.join(apk_decompile_tmp_dir, 'smali')
  196. file_utils.copy_file_all_dir(out_smali_path, smaliPath)
  197. return 0
  198. def get_exec_permission(file):
  199. """
  200. linux下获取执行权限
  201. """
  202. if platform.system() == 'Windows':
  203. return 0
  204. return exec_format_cmd('chmod +x "%s"' % file)
  205. if __name__ == "__main__":
  206. V1signer('/Users/kaiweicai/Documents/Project/PackKit/YYXXPackKit/output/g1_huawei_com.yyxx.qyj2.huawei_Y010402.apk',
  207. '/Users/kaiweicai/Documents/Project/PackKit/YYXXPackKit/output/g1_huawei_com.yyxx.qyj2.huawei_Y010402_____.apk',
  208. '/Users/kaiweicai/Documents/Project/PackKit/YYXXPackKit/keystore/ziyun.jks'
  209. , 'yh1234', 'key0', 'yh1234')