Пример #1
0
    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
Пример #2
0
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
Пример #3
0
    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
Пример #4
0
    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
Пример #5
0
    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
Пример #6
0
    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'))
Пример #7
0
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
Пример #8
0
    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)
Пример #9
0
    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
Пример #10
0
    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
Пример #11
0
    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
Пример #12
0
    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
Пример #13
0
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)