apktool.py 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  1. # -*- coding:utf-8 -*-
  2. import os
  3. import re
  4. import shutil
  5. import subprocess
  6. import sys
  7. import time
  8. import win32api
  9. import win32con
  10. import yaml
  11. import xml.etree.ElementTree as ET
  12. channel_rose = '朱雀'
  13. channel_coolpad='FAKE'
  14. channel_xmy = '小绵羊'
  15. channel_9hgame = '九狐'
  16. channel_icefoxgame = '冰狐'
  17. # 测试组游戏地址,需要根据游戏修改
  18. # 测试组游戏地址,需要根据游戏修改
  19. # 测试组游戏地址,需要根据游戏修改
  20. game_dir = r'\\10.8.230.114\public\测试组\青雀\新倚天屠龙记Y'
  21. # 其他情况使用BPM上面的任务名称
  22. game_channel = '新倚天屠龙记Y-朱雀'
  23. def copy_mommy_for_9hgame(decompile_dir_path):
  24. htprotect_dir_path = os.path.join(decompile_dir_path, 'smali', 'com', 'ydzs', 'framework')
  25. if not os.path.exists(htprotect_dir_path):
  26. os.makedirs(htprotect_dir_path)
  27. shutil.copy(os.path.join(r'D:\special_problem\九狐', 'MommyUtils.smali'), htprotect_dir_path)
  28. if ' ' in game_channel:
  29. game_channel = game_channel.replace(' ', '-')
  30. print(sys.argv)
  31. if len(sys.argv) == 3:
  32. game_dir = sys.argv[1]
  33. game_channel = sys.argv[2]
  34. print(game_dir)
  35. print(game_channel)
  36. def fix_install_fail_bug(decompile_dir_path):
  37. namespace = '{http://schemas.android.com/apk/res/android}'
  38. ET.register_namespace('android', 'http://schemas.android.com/apk/res/android')
  39. manifest_path = os.path.join(decompile_dir_path, 'AndroidManifest.xml')
  40. xml_tree = ET.parse(manifest_path)
  41. xml_root = xml_tree.getroot()
  42. application_node = xml_root.find('./application')
  43. etract_value = application_node.get(f'{namespace}extractNativeLibs')
  44. if etract_value is not None and etract_value == 'false':
  45. # 修改extractNativeLib
  46. application_node.set(f'{namespace}extractNativeLibs', 'true')
  47. xml_tree.write(manifest_path, encoding='utf-8', xml_declaration=True)
  48. yaml_path = os.path.join(decompile_dir_path, 'apktool.yml')
  49. file = open(yaml_path, 'r', encoding='utf-8')
  50. yaml_result = yaml.load(file, Loader=yaml.BaseLoader)
  51. sdk_info = yaml_result['sdkInfo']
  52. yaml_result['packageInfo']['renameManifestPackage'] = None
  53. print(sdk_info)
  54. if int(sdk_info['targetSdkVersion']) > 29:
  55. sdk_info['targetSdkVersion'] = '29'
  56. print(sdk_info)
  57. file.close()
  58. with open(yaml_path, 'w', encoding='utf-8') as f:
  59. yaml.dump(yaml_result, f)
  60. def rindex(lst, value):
  61. for i, v in enumerate(reversed(lst)):
  62. if v == value:
  63. return len(lst) - i - 1 # return the index in the original list`
  64. return None
  65. def move_game_apk_to_work():
  66. cmd_move_str = f'copy {game_path} {work_path}'
  67. print(cmd_move_str)
  68. result = subprocess.getoutput(cmd_move_str)
  69. print(result)
  70. def get_decompile_dir_name(apk_path):
  71. s_index = apk_path.index('.')
  72. print('s_index', s_index)
  73. p_index = rindex(apk_path, '\\')
  74. print('p_index:', p_index)
  75. dir_name = apk_path[:rindex(apk_path, '\\') + 1] + apk_path[p_index + 1:s_index]
  76. print('dir_name:', dir_name)
  77. return dir_name
  78. def find_r_smali_and_replace(smali_file_path, regex_str):
  79. with open(smali_file_path, 'r') as f:
  80. smali_str = f.read()
  81. pattern = re.compile(regex_str)
  82. resource_arr = pattern.findall(smali_str)
  83. if len(resource_arr) > 0:
  84. print(smali_file_path)
  85. for r_str in resource_arr:
  86. virtual_name = r_str[r_str.index(' ') + 1:r_str.index(',')]
  87. print(virtual_name)
  88. resource_type = r_str[r_str.index('R$') + 2:r_str.index(';')].capitalize()
  89. print(resource_type)
  90. resource_name = r_str[r_str.index('->') + 2:r_str.index(':')]
  91. print(resource_name)
  92. new_r_str = "const-string %s, \"%s\"\n\n\tinvoke-static {%s}, Lcom/ydzs/framework/MommyUtils;->get%sId(Ljava/lang/String;)I\n\n\tmove-result %s" % (
  93. virtual_name, resource_name, virtual_name, resource_type, virtual_name)
  94. print(new_r_str)
  95. smali_str = smali_str.replace(r_str, new_r_str)
  96. with open(smali_file_path, 'w') as f:
  97. f.write(smali_str)
  98. def replace_rose_resource(d_dir):
  99. r_regex_str = r'sget[^\n]*R\$layout[^\n]*I|sget[^\n]*R\$id[^\n]*I|sget[^\n]*R\$drawable[^\n]*I|sget[^\n]*R\$string[^\n]*I|sget[^\n]*R\$array[^\n]*I|sget[^\n]*R\$bool[^\n]*I|sget[^\n]*R\$integer[^\n]*I'
  100. for dirpath, dirnames, filenames in os.walk(d_dir):
  101. if r'com\wanwu' in dirpath or r'com\ydzs' in dirpath:
  102. for smali_file in filenames:
  103. smali_file_path = os.path.join(dirpath, smali_file)
  104. # print(smali_file_path)
  105. find_r_smali_and_replace(smali_file_path, r_regex_str)
  106. def fix_coolpad_version_3_method_name_change_bug(decompile_dir):
  107. is_version_3_sdk = False
  108. is_new_sdk_and_version_lt_version_3 = False
  109. for dirpath, dirnames, filenames in os.walk(decompile_dir):
  110. if r'com\yulong\sdk\promoter' in dirpath and 'OnGameAuthListener.smali' in filenames:
  111. auth_listener_path = os.path.join(dirpath, 'OnGameAuthListener.smali')
  112. with open(auth_listener_path, 'r', encoding='utf-8') as f:
  113. smali_str_arr = f.read()
  114. if 'onSuccess' in smali_str_arr:
  115. is_version_3_sdk = True
  116. if 'onResult' in smali_str_arr:
  117. is_new_sdk_and_version_lt_version_3 = True
  118. break
  119. for dirpath, dirnames, filenames in os.walk(decompile_dir):
  120. if r'com\yulong\sdk\promoter' in dirpath and 'PromoterGameAuthApi$1.smali' in filenames:
  121. smali_path = os.path.join(dirpath, 'PromoterGameAuthApi$1.smali')
  122. print(smali_path)
  123. with open(smali_path, 'r', encoding='utf-8') as f:
  124. smali_str_arr = f.readlines()
  125. index_str = r'onSuccess'
  126. if is_new_sdk_and_version_lt_version_3:
  127. index_str = r'onSuccess'
  128. elif is_version_3_sdk:
  129. index_str = r'onResult'
  130. for line in smali_str_arr:
  131. if index_str in line:
  132. print(line)
  133. index = smali_str_arr.index(line)
  134. if is_new_sdk_and_version_lt_version_3:
  135. smali_str_arr[index] = line.replace(index_str, 'onResult')
  136. elif is_version_3_sdk:
  137. smali_str_arr[index] = line.replace(index_str, 'onSuccess')
  138. print(smali_str_arr[index])
  139. break
  140. with open(smali_path, 'w', encoding='utf-8') as f:
  141. f.write(''.join(smali_str_arr))
  142. break
  143. return is_version_3_sdk
  144. def fix_coolpad_version_3_login_fail_bug(decompile_dir_path):
  145. insert_str_list = [
  146. '\n\t.locals 0\n\n',
  147. '\treturn-void\n\n']
  148. for dirpath, dirnames, filenames in os.walk(decompile_dir_path):
  149. if r'com\yulong\account\auth' in dirpath and 'AuthCodeApiImpl.smali' in filenames:
  150. smali_path = os.path.join(dirpath, 'AuthCodeApiImpl.smali')
  151. with open(smali_path, 'r', encoding='utf-8') as f:
  152. smali_str_arr = f.readlines()
  153. index_str = '.method private returnAuthError(Lcom/yulong/account/common/info/ErrorInfo;)V'
  154. end_method_str = '.end method'
  155. insert_str = ''.join(insert_str_list)
  156. on_create_index = 0
  157. insert_index = 0
  158. for line in smali_str_arr:
  159. if index_str in line:
  160. on_create_index = smali_str_arr.index(line)
  161. print('on_create_index', on_create_index, line)
  162. break
  163. for index, value in enumerate(smali_str_arr):
  164. if index > on_create_index and end_method_str in value:
  165. insert_index = index
  166. print('insert_index', index)
  167. break
  168. print(on_create_index, insert_index)
  169. del smali_str_arr[on_create_index + 1:insert_index]
  170. smali_str_arr.insert(on_create_index + 1, insert_str)
  171. with open(smali_path, 'w', encoding='utf-8') as f:
  172. f.write(''.join(smali_str_arr))
  173. break
  174. start_time = int(time.time())
  175. # 判断游戏是否需要修复并且是否是朱雀游戏,不需要修复就只需要拷贝arm64
  176. script_dir_list = os.listdir(r'F:\python_learn\day_1\script')
  177. is_need_fix = False
  178. for py_name in script_dir_list:
  179. if game_channel in py_name:
  180. is_need_fix = True
  181. break
  182. print("aaaaaaaaaaaaaaa", is_need_fix)
  183. game_path = 'game_path is empty'
  184. for game_name in os.listdir(game_dir):
  185. if ((channel_rose in game_channel or '青雀' in game_channel) and channel_rose in game_name) \
  186. or ((channel_coolpad in game_channel or 'fake' in game_channel) and channel_coolpad in game_name)\
  187. or ((channel_icefoxgame in game_channel or '冰狐' in game_channel) and channel_icefoxgame in game_name)\
  188. or (channel_9hgame in game_channel and channel_9hgame in game_name)\
  189. or (channel_xmy in game_channel and channel_xmy in game_name)\
  190. or ('遥望' in game_channel and '遥望' in game_name):
  191. game_path = os.path.join(game_dir, game_name)
  192. break
  193. print('game_path:', game_path)
  194. work_path = r"D:\work"
  195. apk_file_path = f'{work_path}' + game_path[rindex(game_path, "\\"):]
  196. apk_file_path_copy = os.path.join(work_path, 'copy_'+game_path[rindex(game_path, "\\")+1:])
  197. print(apk_file_path_copy)
  198. if '全民泡泡' in game_channel:
  199. if channel_rose in game_channel:
  200. decompile_dir_name = os.path.join(work_path, 'qmpp_rose')
  201. elif channel_coolpad in game_channel or 'fake' in game_channel:
  202. decompile_dir_name = os.path.join(work_path, 'qmpp_coolpad')
  203. elif channel_9hgame in game_channel:
  204. decompile_dir_name = os.path.join(work_path, 'qmpp_9hgame')
  205. else:
  206. decompile_dir_name = os.path.join(work_path, 'qmpp_xmy')
  207. elif '我的安吉拉2' in game_channel:
  208. if channel_rose in game_channel:
  209. decompile_dir_name = os.path.join(work_path, 'wdajl_rose')
  210. elif channel_coolpad in game_channel or 'fake' in game_channel:
  211. decompile_dir_name = os.path.join(work_path, 'wdajl_coolpad')
  212. elif channel_9hgame in game_channel:
  213. decompile_dir_name = os.path.join(work_path, 'wdajl_9hgame')
  214. else:
  215. decompile_dir_name = os.path.join(work_path, 'wdajl_xmy')
  216. else:
  217. decompile_dir_name = get_decompile_dir_name(apk_file_path)
  218. kfzs_sign = "kfzs_sign"
  219. kfzs_sign_youliang = "kfzs_sign_youliang"
  220. ydzs_sign = "ydzs_sign"
  221. # 签名文件,需要根据游戏修改
  222. # 签名文件,需要根据游戏修改
  223. # 签名文件,需要根据游戏修改
  224. current_sign = kfzs_sign
  225. if channel_rose in game_path or channel_coolpad in game_path or channel_9hgame in game_path:
  226. current_sign = ydzs_sign
  227. elif channel_xmy in game_path:
  228. current_sign = kfzs_sign
  229. # 修复脚本文件名,需要根据游戏修改
  230. # 修复脚本文件名,需要根据游戏修改
  231. # 修复脚本文件名,需要根据游戏修改
  232. if is_need_fix:
  233. fix_python_path = os.path.join(r'F:\python_learn\day_1\script', f'{game_channel}.py')
  234. print('fix_python_path', fix_python_path)
  235. # # 拷贝游戏包
  236. move_game_apk_to_work()
  237. # 复制一个原始包,只第一次复制
  238. if apk_file_path_copy[rindex(apk_file_path, '\\')+1:] not in os.listdir(work_path):
  239. print("执行拷贝原始游戏包")
  240. copy_origin_game_apk = f'copy {apk_file_path} {apk_file_path_copy}'
  241. subprocess.getoutput(copy_origin_game_apk)
  242. else:
  243. print("不执行拷贝原始游戏包")
  244. # # 解包
  245. cmd_decompile_str = rf'apktool d {apk_file_path} -f -o {decompile_dir_name} --only-main-classes'
  246. print(cmd_decompile_str)
  247. os.system(cmd_decompile_str)
  248. if channel_9hgame in game_path or channel_icefoxgame in game_path:
  249. copy_mommy_for_9hgame(decompile_dir_name)
  250. #朱雀包拷贝arm64
  251. if (channel_rose in game_path or channel_coolpad in game_path or channel_9hgame in game_path or channel_icefoxgame in game_path) and 'arm64-v8a' in os.listdir(os.path.join(decompile_dir_name, 'lib')):
  252. print('朱雀拷贝arm64')
  253. arm64_path = os.path.join(decompile_dir_name, 'lib', 'arm64-v8a')
  254. cmd_copy_arm64 = rf'copy D:\special_problem\朱雀arm64\libydzs.so {arm64_path}'
  255. os.system(cmd_copy_arm64)
  256. cmd_copy_arm64 = rf'copy D:\special_problem\朱雀arm64\libwanwusdk.so {arm64_path}'
  257. os.system(cmd_copy_arm64)
  258. #朱雀包拷贝x86
  259. if (channel_rose in game_path or channel_coolpad in game_path or channel_9hgame in game_path or channel_icefoxgame in game_path) and 'x86' in os.listdir(os.path.join(decompile_dir_name, 'lib')):
  260. print('朱雀拷贝x86')
  261. x86_path = os.path.join(decompile_dir_name, 'lib', 'x86')
  262. cmd_copy_x86 = rf'copy D:\special_problem\朱雀x86\libydzs.so {x86_path}'
  263. os.system(cmd_copy_x86)
  264. #朱雀包修改资源获取方式
  265. if channel_rose in game_path or channel_coolpad in game_path or channel_9hgame in game_path or channel_icefoxgame in game_path:
  266. print('执行朱雀包修改资源脚本')
  267. replace_rose_resource(decompile_dir_name)
  268. for dirpath, dirnames, filenames in os.walk(decompile_dir_name):
  269. if r'com\ydzs\framework\java' in dirpath and 'YDZSSDKUser.smali' in filenames:
  270. smali_path = os.path.join(dirpath, 'YDZSSDKUser.smali')
  271. print(smali_path)
  272. with open(smali_path, 'r', encoding='utf-8') as f:
  273. smali_str_arr = f.readlines()
  274. index_str = 'invoke-static {}, Lcom/ydzs/framework/SDKNativeWrapper;->nativeGetRealnameStatus()Ljava/lang/String;'
  275. index_str2 = 'invoke-static {p1}, Lcom/ydzs/framework/SDKNativeWrapper;->nativeEnterGame(Ljava/util/HashMap;)Ljava/lang/String;'
  276. replace_str = '\tinvoke-static {}, Lcom/ydzs/framework/SDKNativeWrapper;->nativeGetRealnameStatus()V\n'
  277. replace_str2 = '\tinvoke-static {p1}, Lcom/ydzs/framework/SDKNativeWrapper;->nativeEnterGame(Ljava/util/HashMap;)V\n'
  278. for line in smali_str_arr:
  279. if index_str in line:
  280. smali_str_arr[smali_str_arr.index(line)] = replace_str
  281. if index_str2 in line:
  282. smali_str_arr[smali_str_arr.index(line)] = replace_str2
  283. with open(smali_path, 'w', encoding='utf-8') as f:
  284. f.write(''.join(smali_str_arr))
  285. if r'com\ydzs\framework' in dirpath and 'SDKNativeWrapper.smali' in filenames:
  286. smali_path = os.path.join(dirpath, 'SDKNativeWrapper.smali')
  287. print(smali_path)
  288. with open(smali_path, 'r', encoding='utf-8') as f:
  289. smali_str_arr = f.readlines()
  290. index_str = '.method public static native nativeGetRealnameStatus()Ljava/lang/String;'
  291. index_str2 = '.method public static native nativeEnterGame(Ljava/util/HashMap;)Ljava/lang/String;'
  292. replace_str = '.method public static native nativeGetRealnameStatus()V\n'
  293. replace_str2 = '.method public static native nativeEnterGame(Ljava/util/HashMap;)V\n'
  294. for line in smali_str_arr:
  295. if index_str in line:
  296. smali_str_arr[smali_str_arr.index(line)] = replace_str
  297. if index_str2 in line:
  298. smali_str_arr[smali_str_arr.index(line)] = replace_str2
  299. with open(smali_path, 'w', encoding='utf-8') as f:
  300. f.write(''.join(smali_str_arr))
  301. # # 修复脚本
  302. if is_need_fix:
  303. os.system(f'python {fix_python_path} {decompile_dir_name} {game_dir}')
  304. #修复酷派新sdk 3.0接口方法名改变登录后闪退bug
  305. is_version_3_sdk = fix_coolpad_version_3_method_name_change_bug(decompile_dir_name)
  306. print('is_version_3_sdk: ', is_version_3_sdk)
  307. if is_version_3_sdk:
  308. fix_coolpad_version_3_login_fail_bug(decompile_dir_name)
  309. fix_install_fail_bug(decompile_dir_name)
  310. # # 合包
  311. if '全民泡泡' in game_channel or '我的安吉拉2' in game_channel:
  312. cmd_build_str = f'apktool b {decompile_dir_name} -f -o {apk_file_path} --use-aapt2'
  313. elif '乱世终结' in game_channel:
  314. cmd_build_str = f'apktool b {decompile_dir_name} -f -o {apk_file_path} -api 29'
  315. else:
  316. cmd_build_str = f'apktool b {decompile_dir_name} -f -o {apk_file_path}'
  317. print('cmd_build_str:', cmd_build_str)
  318. os.system(cmd_build_str)
  319. # 签名
  320. if '极无双' in game_channel or '终末阵线' in game_channel\
  321. or '灌篮高手' in game_channel or '炼仙传说' in game_channel\
  322. or '生死狙击' in game_channel or '全明星激斗' in game_channel\
  323. or '弹弹堂大冒险' in game_channel or '勇者秘境' in game_channel\
  324. or '狩猎吧原始人' in game_channel:
  325. current_sign = kfzs_sign_youliang
  326. if '航海王启航D' in game_channel:
  327. current_sign = ydzs_sign
  328. if '我叫MT:经典再现Y' in game_channel:
  329. current_sign = kfzs_sign
  330. cmd_sign_str = rf'{current_sign} {apk_file_path}'
  331. print(cmd_sign_str)
  332. cmd_result = subprocess.getoutput(cmd_sign_str)
  333. print(cmd_result)
  334. # 拷贝回测试组
  335. cmd_move_back_to_test_public = f'copy {apk_file_path} {game_dir}'
  336. print(cmd_move_back_to_test_public)
  337. cmd_result = subprocess.getoutput(cmd_move_back_to_test_public)
  338. print(cmd_result)
  339. end_time = int(time.time())
  340. print('执行时间s:', end_time-start_time)
  341. print('执行时间m:', (end_time-start_time)/60)
  342. win32api.MessageBox(0, "游戏修复完毕", "提醒", win32con.MB_OK)