def parse(self, line: str) -> t.Optional[Error]: self.raw_line: str = line.rstrip() matches = re.match(r'^.*(invoke-.* ?) {(.*?)}, (.*)$', self.raw_line) if not matches: return Error("Couldn't process line: {}".format(self.raw_line)) if len(matches.groups()) != 3: return Error( "Incorrect number of groups while parsing SmaliInvocation: {}: {}" .format(self.raw_line, matches.groups())) # e.g: invoke-interface type_str = matches.group(1).strip() # e.g: p1, v1, self.params_str = matches.group(2).strip() # e.g: Ljava/lang/System;->currentTimeMillis()J self.method_sig = matches.group(3).strip() if type_str == 'invoke-static': self.type = INVOKE_TYPE['static'] elif type_str == 'invoke-direct': self.type = INVOKE_TYPE['direct'] elif type_str == 'invoke-virtual': self.type = INVOKE_TYPE['virtual'] elif type_str == 'invoke-interface': self.type = INVOKE_TYPE['interface'] elif type_str == 'invoke-super': self.type = INVOKE_TYPE['super'] return None
def is_apk(apk_path: str) -> t.Optional[Error]: try: with open(apk_path, 'rb') as fd: if fd.read()[0:4] != b'PK\x03\x04': return Error("%s is not an APK file", apk_path) except FileNotFoundError: return Error("%s is not a proper path", apk_path) return None
def parse_apk( self, apk_path: str ) -> t.Tuple[t.Optional[str], t.Optional[str], t.Optional[Error]]: disassembled_classes_path = tempfile.mkdtemp() logging.info('baksmaling apk [%s] to [%s]', apk_path, disassembled_classes_path) if subprocess.run( 'apktool d {} --no-res --output {} -f 1>/dev/null'.format( apk_path, disassembled_classes_path), shell=True).returncode != 0: return None, None, Error( 'baksmali failed on apk [{}]'.format(apk_path)) smali_filepaths = [ os.path.join(root, name) for root, _, files in os.walk(disassembled_classes_path) for name in files if name.endswith('.smali') and self.focus_pkg in os.path.join(root, name) ] logging.debug('Disassembled APK to %d smali files', len(smali_filepaths)) for path in smali_filepaths: # Read the first line of the smali file, which contains it's class name clazz_name = None with open(path, 'r') as fd: lines = fd.readlines() if not lines or len(lines) < 1: return None, None, Error('Bad smali file {}'.format(path)) # Change this: # .class Lcom/aaa/bbb; # to this: # com.aaa.bbb clazz_name = lines[0].split()[-1] clazz_name = clazz_name[1:-1].replace('/', '.') if not clazz_name: return None, None, Error( 'Couldnt parse class name {}'.format(path)) clazz = SmaliClass(clazz_name) err = clazz.parse(path) self.classes[clazz_name] = clazz if err: return None, None, err pkg_name, err = util.get_pkg_name_from_apk(apk_path) if err: return None, None, err return disassembled_classes_path, pkg_name, None
def apply_changes_to_disk( self, disassembled_classes_path: str, out_smali_dir: str = None) -> t.Tuple[str, t.Optional[Error]]: """ Takes a decompiled Smali directory in 'disassembled_classes_path' and replaces all classes in 'self.focus_pkg' with their equivalent in 'self.classes' if 'out_smali_dir' is not None, it would write the changes to the specified directory. If None, it would overwrite the directory """ workspace: str = '' if not out_smali_dir: workspace = disassembled_classes_path else: workspace = out_smali_dir shutil.rmtree(workspace) shutil.copytree(disassembled_classes_path, workspace) logging.info( 'Rewriting classes:\n\tPath [%s]\n\tfocus_pkg [%s]\n\tout directory [%s]', disassembled_classes_path, self.focus_pkg, workspace) focus_dir_arr = glob.glob('{}/**/{}'.format(workspace, self.focus_pkg)) if not focus_dir_arr: return '', Error('focus_pkg {} is bad') if len(focus_dir_arr) != 1: return '', Error('focus_pkg {} is ambiguous. Be more granular') focus_dir = focus_dir_arr[0] if not os.path.isdir(focus_dir): return '', Error( '{} not found in workspace [{}]. Focus pkg is invalid'.format( focus_dir, workspace)) # Remove focus_dir on disk and replace it with the changes in SmaliParser shutil.rmtree(focus_dir, ignore_errors=True) os.makedirs(focus_dir) for _, clazz in self.classes.items(): out_path = os.path.join(focus_dir, '{}.smali'.format(clazz.get_simple_name())) with open(out_path, 'w') as fd: clazz.write(fd) return workspace, None
def __cross_match_recorded_inv_with_smali_tree( self, smali_parser: SmaliParser, recorded_inv: RecordedInvocation ) -> t.Tuple[t.Optional[SmaliInvocation], t.Optional[Error]]: caller_clazz: str = recorded_inv.caller_info.clazz caller_method: str = recorded_inv.caller_info.method caller_linenum: int = recorded_inv.caller_info.linenum if caller_clazz not in smali_parser.classes: return (None, Error('class [{}] not found. Possibly packed classloader'. format(caller_clazz))) arr = [ inv for smali_method in smali_parser.classes[caller_clazz].methods if smali_method.name == caller_method for linenum, smali_line in smali_method.lines.items() if linenum == caller_linenum for inv in smali_line.block if isinstance(inv, SmaliInvocation) and inv.method_sig == recorded_inv.method_sig ] if not arr: return None, None return arr[0], None
def __init__(self, **kwargs): super(Router, self).__init__(**kwargs) # must load .kv file, unless put these to build method of PrinterApp Builder.load_file('src/home.kv') self.add_widget(Home(name='home')) Builder.load_file('src/bookingcode.kv') self.add_widget(BookingCode(name='bookingcode')) Builder.load_file('src/complete.kv') self.add_widget(Complete(name='complete')) Builder.load_file('src/detail.kv') self.add_widget(Detail(name='detail')) Builder.load_file('src/pay.kv') self.add_widget(Pay(name='pay')) Builder.load_file('src/error.kv') self.add_widget(Error(name='error')) Builder.load_file('src/usb.kv') self.add_widget(Usb(name='usb')) Builder.load_file('src/usbguide.kv') self.add_widget(UsbGuide(name='usbguide')) Builder.load_file('src/qrguide.kv') self.add_widget(QrGuide(name='qrguide')) Builder.load_file('src/payselector.kv') self.add_widget(PaySelector(name='payselector'))
def get_pkg_name_from_apk(apk_path: str) -> t.Tuple[str, t.Optional[Error]]: cmd = subprocess.run('aapt dump badging {}'.format(apk_path), shell=True, capture_output=True) if cmd.returncode != 0: return '', Error('aapt failed') pkg_line = [ l for l in cmd.stdout.decode().split('\n') if re.search(r'package', l) ] if not pkg_line: return '', Error('Not package name found in APK') pkg_name = re.match(r"package: name='(.*?)'", pkg_line[0]).group(1) return pkg_name, None
def __init__(self, class_name): if LoggerFactory.log_dir is None: cur_path = path.dirname(__file__) parent_path = os.path.dirname(cur_path) # 获得d所在的目录,即d的父级目录 LoggerFactory.log_dir = parent_path + "/logs" self.debug_path = LoggerFactory.log_dir + "/debug.log" self.info_path = LoggerFactory.log_dir + "/info.log" self.warn_path = LoggerFactory.log_dir + "/warn.log" self.error_path = LoggerFactory.log_dir + "/error.log" self.critical_path = LoggerFactory.log_dir + "/critical.log" self.__debug = Debug(class_name, self.debug_path) self.__info = Info(class_name, self.info_path) self.__warn = Warn(class_name, self.warn_path) self.__error = Error(class_name, self.error_path) self.__critical = Critical(class_name, self.critical_path)
def rebuild_apk(self, classes_path) -> t.Tuple[str, t.Optional[Error]]: """ Rebuild and resign a disassembled smali directory in [classes_path]. Return a signed APK """ logging.info('rebuilding APK in %s...', classes_path) if not classes_path or not os.path.isdir(classes_path): return '', Error('{} is not a directory'.format(classes_path)) cmd = subprocess.run('apktool b', cwd=classes_path, shell=True, capture_output=True) if cmd.returncode != 0: return '', Error('apktool b failed: {}\n{}'.format( cmd.stdout.decode(), cmd.stderr.decode())) apk_path = glob.glob('{}/dist/*.apk'.format(classes_path)) if not apk_path: return '', Error( 'Built APK in [{}] is not a file'.format(apk_path)) apk_path = apk_path[0] signed_apk_path = '{}-signed.apk'.format(apk_path.strip('.apk')) logging.info("Signing rebuilt APK...") if subprocess.run( 'jarsigner -keystore {} -storepass {} {} {} >/dev/null'.format( self.DUMMY_KEYSTORE_PATH, self.DUMMY_KEYSTORE_PASS, apk_path, self.DUMMY_KEYSTORE_ALIAS), shell=True).returncode != 0: return '', Error('jarsigner failed') if subprocess.run('zipalign -v 4 {} {} >/dev/null'.format( apk_path, signed_apk_path), shell=True).returncode != 0: return '', Error('zipalign failed') return signed_apk_path, None
def hook(self, hook_file: str): target_methods, err = self.__parse_hooks(hook_file) if err: return err if not target_methods: return Error('Empty target_methods') logging.info("Hooking...") for method_sig in target_methods: self.exports.describe(method_sig) self.exports.hook_into(method_sig) return None
def __parse_hooks( self, hook_file: str ) -> t.Tuple[t.Optional[t.List[str]], t.Optional[Error]]: lines = [] try: with open(hook_file, 'r', encoding="utf-8") as fd: for line in fd: line = line.strip() if not line: continue lines.append(line) except EnvironmentError: return None, Error("Couldn't parse hooks file properly") return lines, None
def __parse_class_header( self, idx: int, file_lines: t.List[str] ) -> t.Tuple[int, t.List[str], str, t.Optional[Error]]: try: sig = file_lines[0].split()[-1][1:-1].strip() except IndexError: return 0, [], '', Error('Could not parse class header: {}'.format( self.name)) i = 1 # Skipping the first line header_block: t.List[str] = [] for i in range(idx, len(file_lines)): line = file_lines[i] if '.method' in line: break header_block.append(line) return i, header_block, sig, None
def compared_to_keras(batch_size: int, input_size: int, output_size: int, activation: Activation, loss_function: Error, bias: bool, eps_decimal: int = 4, seed: int = 1): if isinstance(activation, SigmoidActivation): activation_name = "sigmoid" elif isinstance(activation, LinearActivation): activation_name = "linear" elif isinstance(activation, ReLUActivation): activation_name = "relu" elif isinstance(activation, SoftmaxActivation): activation_name = "softmax" else: raise RuntimeError("Unknown activation function!") if isinstance(loss_function, MeanAbsoluteError): loss_function_name = "mean_absolute_error" elif isinstance(loss_function, LogError): loss_function_name = "binary_crossentropy" elif isinstance(loss_function, MeanSquaredError): loss_function_name = "mean_squared_error" else: raise RuntimeError("Unknown loss function!") model = Sequential() model.add( Dense(output_size, use_bias=bias, input_shape=(input_size, ), activation=activation_name)) model.compile(optimizer="sgd", loss=loss_function_name) np.random.seed(seed) mlp = MLP(size_in=input_size, size_out=output_size, activation=activation, bias=bias, seed=seed) if bias: model.layers[0].set_weights([mlp.w, mlp.b.flatten()]) else: model.layers[0].set_weights([mlp.w]) x = np.random.rand(batch_size, input_size) y = np.random.rand(batch_size, output_size) loss = model.evaluate(x, y, verbose=2) output = model.predict(x) output2 = mlp.forward(x) loss2 = loss_function.get_error(output2, y) # equal outputs np.testing.assert_almost_equal(output, output2, decimal=eps_decimal) # equal loss np.testing.assert_almost_equal(loss, loss2, decimal=eps_decimal) # equal weights and biases derivatives mlp.backward(loss_function.get_derivative()) if bias: [dw, db] = get_weight_grad(model, x, y) np.testing.assert_almost_equal(db, mlp.db, decimal=eps_decimal) np.testing.assert_almost_equal(dw, mlp.dw, decimal=eps_decimal) else: [dw] = get_weight_grad(model, x, y) np.testing.assert_almost_equal(dw, mlp.dw, decimal=eps_decimal)