letKiwi = Java.use("com.kiwi.sdk.Kiwi"); Kiwi["init_appname"].implementation = function(str){ let ret = this.init_appname(str); console.log('init_appname param value is ' + str); console.log('init_appname ret value is ' + ret); return ret; };
Kiwi["server_to_local"].implementation = function(str){ let ret = this.server_to_local(str); console.log('server_to_local ret value is ' + ret); return ret; };
letSuperNetworkKitPlugin = Java.use("com.example.super_network_kit.SuperNetworkKitPlugin"); SuperNetworkKitPlugin["onMethodCall"].implementation = function(methodCall, result){ let ret = this.onMethodCall(methodCall, result); let mc = Java.cast(methodCall, MethodCall); let mn = mc.method.value; if (mn == 'sendString') { let connID = mc.argument("connID"); let data = mc.argument("data").toString(); let dj = JSON.parse(data); console.log('send -> random: ' + dj['random'] + '; connID: ' + connID + '; data: ' + JSON.stringify(dj)); }elseif (mn == 'initConnection'){ let url = mc.argument("url"); let salt = mc.argument("salt"); let enableZip = mc.argument("enableZip"); console.log('initConnection: ' + `${url}, ${salt}, ${enableZip}`); }else{ let connID = mc.argument("connID"); console.log('connID: ' + connID + '; method: ' + mn + '; Now: ' + newDate().getTime()); } return ret; }; }); }
import os import zlib import shutil import hashlib import logging
from io import BufferedReader, BufferedRandom from zipfile import ZipFile
import click from cryptography.hazmat.primitives import padding from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
classQC: def__init__(self, apk, work_dir, key, out=None) -> None: """ param: apk: apk path to modify dir: work dir to save files in the process key: the aes ecb key to de/en-crypt dex """ # files paths self.work_path = self.clean_path(work_dir + '/') self.unzip_apk_path = self.clean_path(os.path.join(work_dir, os.path.basename(apk)[:-4])) # The encrypted dex splited from apk self.original_path = self.clean_path(os.path.join(work_dir, 'original/')) # The decrypted dex from original encrypted dex self.decrypted_path = self.clean_path(os.path.join(work_dir, 'decrypted/')) # The smali files baksmali from decrypted dex self.baksmali_path = os.path.join(work_dir, 'baksmali/') # The dex from modified smali files self.updated_path = os.path.join(work_dir, 'updated/') # The encrypted dex from updated dex self.encrypted_path = self.clean_path(os.path.join(work_dir, 'encrypted/'))
@staticmethod defsignature(dexf: BufferedRandom): """ Update signature of dex file param: dexf: the file-object of dex file """ dexf.seek(12) sourceData = dexf.read(20) dexf.seek(32) sigdata = dexf.read() sha1 = hashlib.sha1() sha1.update(sigdata) sha2 = sha1.digest() logging.info("signature: " + sourceData.hex() + " -> " + sha2.hex()) if dexf.writable and sourceData != sha2: dexf.seek(12) dexf.write(sha2)
defsplitdex(self, dexf: BufferedReader): """ Split original classes.dex file param: dexf: the file-object of dex file """ dexf.seek(0x20) length = int.from_bytes(dexf.read(4), byteorder='little') dexf.seek(length - 4) index_length = int.from_bytes(dexf.read(4), byteorder='big') dexf.seek(length - 4 - index_length) _tmp = dexf.read(index_length).decode('utf8') info = "".join(_tmp[i] for i inrange(1, len(_tmp), 2)) for di in info.split('-'): self.dex_dict[di.split('=')[0]] = int(di.split('=')[1]) # dex_start is the start of inject data dex_start = length - 4 - index_length for dex_name in self.dex_dict: dex_length = self.dex_dict[dex_name] dex_start = dex_start - dex_length # get the shell dex withopen(self.original_path + 'shell.dex', 'wb') as sf: dexf.seek(0) sf.write(dexf.read(dex_start)) # get resume dex for dex_name in self.dex_dict: dex_length = self.dex_dict[dex_name] withopen(self.original_path + dex_name, 'wb') as cf: dexf.seek(dex_start) cf.write(dexf.read(dex_length)) dex_start = dex_start + dex_length
defdecdex(self, dex_name: str): """ Decrypt dex param: the dex name to be dec """ withopen(self.original_path + dex_name, 'rb') as e: bencdata = e.read() decryptor = Cipher(algorithms.AES(self.key), modes.ECB()).decryptor() dexraw = decryptor.update(bencdata) + decryptor.finalize() unpadder = padding.PKCS7(128).unpadder() bdecdex = unpadder.update(dexraw) + unpadder.finalize() withopen(self.decrypted_path + dex_name, 'wb') as d: d.write(bdecdex)
defencdex(self, dex_name: str): """ Encrypt dex param: the dex name to be enc """ withopen(self.updated_path + dex_name, 'rb') as e: bdecdata = e.read() padder = padding.PKCS7(128).padder() bpaddata = padder.update(bdecdata) + padder.finalize() encryptor = Cipher(algorithms.AES(self.key), modes.ECB()).encryptor() bencdex = encryptor.update(bpaddata) + encryptor.finalize() withopen(self.encrypted_path + dex_name, 'wb') as d: return d.write(bencdex)
defrepdex(self, dex_path, dex_name): """ Repair signature and checksum of dex param: dex_path: the dex path dex_name: the dex name """ logging.info(f'Start Repair {dex_path + dex_name}') withopen(dex_path + dex_name, 'r+b') as dexf: self.signature(dexf) self.checksum(dexf)
defbaksmali(self): """ apply baksmali.jar, from decrypted_path to baksmali_path """ dec_name = self.work_path + 'decrypted' shutil.make_archive(dec_name, 'zip', self.decrypted_path) os.system(f'java -jar bin/apktool.jar d {dec_name}.zip -o {self.baksmali_path}')
defsmali(self): """ apply smali.jar, from baksmali_path to updated_path """ bak_path = self.work_path + 'baksmali.zip' os.system(f'java -jar bin/apktool.jar b {self.baksmali_path} -o {bak_path}') shutil.unpack_archive(bak_path, self.updated_path, 'zip')
defwrite_out(self): """ merge all classes to one classes use the original encrypt type """ withopen(self.work_path + 'classes.dex', 'w+b') as dexf: withopen(self.original_path + 'shell.dex', 'rb') as sf: shell_length = dexf.write(sf.read()) for dex_name in self.dex_dict: dex_length = self.encdex(dex_name) self.dex_dict[dex_name] = dex_length withopen(self.encrypted_path + dex_name, 'rb') as df: dexf.write(df.read()) dex_list = map(lambda x: x + '=' + str(self.dex_dict[x]), self.dex_dict) dex_index = ("." + ".".join(i for i in'-'.join(list(dex_list)))).encode('utf8') dexf.write(dex_index) index_length = len(dex_index).to_bytes(4, byteorder='big') dexf.write(index_length) dexf.seek(0x20) length = shell_length + sum(self.dex_dict.values()) + len(dex_index) + 4 dexf.write(length.to_bytes(4, byteorder='little')) self.repdex(self.work_path, 'classes.dex')
defpack_sign(self): """ Zip and Sign :return: """ shutil.move(self.work_path + 'classes.dex', 'classes.dex') logging.info(f'Start pack to {self.outapk}') os.system(f"bin\\7za d {self.outzip} classes.dex > nul") os.system(f"bin\\7za a {self.outzip} classes.dex > nul") os.rename(self.outzip, self.outapk) logging.info(f'Start sign {self.outapk}') os.system(f'apksigner.bat sign --ks bin/release.jks --ks-pass pass:123456 ' f'--min-sdk-version 22 {self.outapk}') shutil.move('classes.dex', self.work_path + 'classes.dex')
defbreak_dex(self): """ command d: splitdex + decrypt + baksmali """ withopen(self.classes_path, 'rb') as cf: logging.info('Start Split original classes.dex') self.splitdex(cf) for dex_name in self.dex_dict: logging.info(f'Start Decrypt {dex_name}') self.decdex(dex_name) logging.info('Start Baksmali dex to smali') self.baksmali()
defreplace(self): """ command r: replace ByteString.smali """ for dex_name in self.dex_dict: BS_path = f'{self.baksmali_path}smali_{dex_name[:-4]}/okio/ByteString.smali' if os.path.exists(BS_path): logging.info(f'Start Replace ByteString.smali of {dex_name}') os.remove(BS_path) shutil.copy('ByteString.smali', BS_path)
defbuild(self): """ command b: smali + encrypt + write + pack + sign """ logging.info('Start smali.jar to convert smali to dex') self.smali() for dex_name in self.dex_dict: logging.info(f'Start Decrypt {dex_name}') self.encdex(dex_name) logging.info('Start write out to classes.dex') self.write_out() logging.info('Start pack and sign the zip file') self.pack_sign()