Пример #1
0
    def process_invoke_static_statement(line):
        """处理invoke static语句,获取解密函数相关信息

        Args:
            line (str): Description of parameter `line`.

        Returns:
            tupple: None 表示跳过,非None(类名、方法名、参数类型、返回值类型、参数寄存器) 表示不跳过。

        """
        # 获取函数相关信息
        cname, mname, ptypes, rtype, rnames = SmaliLine.parse(line)

        # 调用类
        if cname in {'Ljava/lang/reflect/Method;', 'Ljava/lang/String;'}:
            return

        # 返回值如果为对象,那么不能为非字符串类型
        if rtype[0] == 'L' and rtype != 'Ljava/lang/String;':
            return

        if rtype in ['V', 'Z']:
            return

        # 解密参数
        # TODO 也许可以指定任意参数,目前暂时不支持。
        itypes = set(ptypes) & {
            'Landroid/content/Context;', 'Landroid/app/Activity;',
            'Ljava/lang/Throwable;'
        }
        if itypes:
            return

        return cname, mname, ptypes, rtype, rnames
Пример #2
0
    def process_xget_statement(self, line):
        """处理opcode形如*get相关的语句,如sget、iget等。
        这类语句通常都是从类的字段中获取值。
        iget-object v3, p0, La/b/c/d;->q:Ljava/lang/String;

        Args:
            line (str): smali语句

        Returns:
            None:

        """
        desc = line.split()[-1]  # La/b/c/d;->q:Ljava/lang/String;
        if desc in self.fields:
            return
        cname, fname, rtype, rname = SmaliLine.parse(line)

        types = {
            'B', 'S', 'C', 'I', 'J', 'F', 'D', 'Ljava/lang/String;', '[B',
            '[S', '[C', '[I', '[J', '[F', '[D', '[Ljava/lang/String;'
        }
        if rtype not in types:
            return

        self.json_list = {'type': 'field', 'data': []}
        json_item = {'className': cname, 'fieldName': [fname]}
        self.json_list['data'].append(json_item)
        result = self.get_field_value()
        # if DEBUG_MODE:
        #     print('FeildValue:', result)
        if not result:
            return
        value = result[fname]

        if rtype in {'B', 'S', 'C', 'I', 'J', 'F', 'D'}:
            value = int(value)
        elif rtype.startswith('['):
            value = ast.literal_eval(value)
        self.fields[desc] = value
Пример #3
0
    def process_body(self, body):
        '''
        返回(结果、新的方法体)
        '''
        array_snippet = self.get_array_snippet(body)

        lines = re.split(r'\n', body)

        flag = False
        new_body = []
        snippet = []
        args = {}
        for line in lines:
            if not line:
                continue

            if 'move-result-object' in line and 'const-string' in new_body[-1]:
                # 这种情况会导致反编译工具反编译失败
                # const-string v9, "bytes="
                # move-result-object v9

                v0 = SmaliLine.parse(line)
                vx, string_id = SmaliLine.parse(new_body[-1])

                if v0 != vx:
                    new_line = 'const-string {}, "{}"'.format(v0, string_id)
                    new_body[-1] = new_line
                continue

            snippet.append(line)

            for _, prog in self.progs.items():
                result = prog.search(line)
                if result:
                    break
            else:
                # 如果smali代码存在非字符串调用,则清理所有代码
                if 'invoke-' in line and ', Ljava/lang/String' not in line:
                    snippet.clear()
                new_body.append(line)
                continue

            rtname = result.groups()[0]

            snippet.append('return-object {}'.format(rtname))
            snippet.extend(array_snippet)
            args.update(self.pre_process(snippet))

            self.emu.call(snippet, args=args, thrown=False)
            args = self.emu.vm.variables
            result = self.emu.vm.result

            if result:
                flag = True
                if not isinstance(result, str):
                    result = str(result)
                new_line = 'const-string {}, "{}"'.format(rtname, result)
                if 'array' in new_body[-2]:
                    del new_body[-1]
                    del new_body[-1]
                new_body.append(new_line)
            else:
                new_body.append(line)

            snippet.clear()

        return (flag, new_body)
Пример #4
0
    def _process_mtd(self, mtd):
        # if DEBUG_MODE:
        #     print('\n', '+' * 100)
        #     print('Starting to decode ...')
        #     print(Color.green(mtd))

        # 如果存在数组
        array_data_content = []
        arr_res = self.arr_data_ptn.search(mtd.get_body())
        if arr_res:
            array_data_content = re.split(r'\n\s', arr_res.group())

        lines = re.split(r'\n\s*', mtd.get_body())

        old_body = lines.copy()  # 存放原始方法体
        new_body = []  # 存放解密后的方法体

        snippet = []  # 存放smali代码,用于模拟执行
        args = {}  # 存放方法参数,用于smaliemu执行

        index = -1  # 用于计数

        xget_opcodes = {'iget', 'iget-object', 'sget', 'sget-object'}

        block_args = {'first': {}}  # 保存所有分支的执行结果
        last_block_key = 'first'  # 上一个分支-关键字
        this_block_key = 'first'  # 当前分支,默认第一分支
        keys = ['first']  # 默认执行第一分支

        for line in lines:
            index += 1
            if not line:
                continue
            new_body.append(line)  # 解密结果,直接放后面即可

            # if DEBUG_MODE:
            #     print(Color.blue(line))

            parts = line.split()
            opcode = parts[0]

            # smali 代码分块执行
            # 命中下述关键字,则表示当前分支结束
            # 并根据上一个分支的情况,判断之前的分支是否可用
            if 'if-' in opcode:
                # if DEBUG_MODE:
                #     print('>' * 10, opcode)
                #     print('this_block_key', this_block_key)
                #     print('last_block_key', last_block_key)
                #     print('block_args', block_args)

                # 存在两种情况
                # 1. 当前代码片段(if语句之前的代码),还没执行;全部执行一次
                # 2. 当前代码片段,已经执行了一部分,因为解密;从执行后的地方开始执行

                pre_args = {}
                if this_block_key in last_block_key:
                    for key in reversed(keys):
                        if this_block_key not in key:
                            pre_args = block_args[key].copy()
                            break
                else:
                    pre_args = block_args[last_block_key].copy()

                if this_block_key in block_args:
                    pre_args.update(block_args[this_block_key])

                snippet.extend(array_data_content)
                self.emu.call(snippet, args=pre_args, cv=True, thrown=False)
                block_args[this_block_key] = self.emu.vm.variables
                snippet.clear()

                last_block_key = this_block_key
                this_block_key = 'if' + parts[-1]  # 表示接下来跑的代码块是这个语句的
                keys.append(this_block_key)

                # if DEBUG_MODE:
                #     print('block_args - 运行后', block_args)
                continue
            elif 'goto' in opcode:
                # 跳转语句,直接跳过
                continue
            elif opcode.startswith(':cond_')\
                or opcode.startswith(':try_start')\
                    or opcode.startswith('.catch_'):
                # if DEBUG_MODE:
                #     print('>' * 10, opcode)
                #     print('this_block_key', this_block_key)
                #     print('last_block_key', last_block_key)
                #     print('block_args', block_args)
                # 存在两种情况
                # 1. 当前代码片段,还没执行;全部执行一次
                # 2. 当前代码片段,已经执行了一部分,因为解密;从执行后的地方开始执行
                pre_args = block_args[last_block_key].copy()
                if this_block_key in block_args:
                    pre_args.update(block_args[this_block_key])

                snippet.extend(array_data_content)
                self.emu.call(snippet, args=pre_args, cv=True, thrown=False)
                block_args[this_block_key] = self.emu.vm.variables

                snippet.clear()

                last_block_key = this_block_key
                this_block_key = opcode  # 表示接下来跑的代码块是这个语句的
                keys.append(this_block_key)

                # if DEBUG_MODE:
                #     print('block_args - 运行后', block_args)
                continue
            elif opcode.startswith(':try_start'):
                pass
            elif '.catch_' in opcode:
                # 前面代码当成一块处理
                continue

            snippet.append(line)

            is_static = True
            if opcode == 'invoke-static':
                result = self.process_invoke_static_statement(line)
                if result:
                    cname, mname, ptypes, rtype, rnames = result
                else:
                    continue
            # elif opcode == 'invoke-virtual':
            # TODO 实例方法,目前只考虑无参实例化。
            #     result = self.process_invoke_static_statement(line)
            #     if result:
            #         cname, mname, ptypes, rtype, rnames = result
            #         print(result)
            #         # 判断类的构造函数是否为<init>()V
            #         clz = self.smalidir.get_method(
            #             java2smali(cname), '<init>()V')
            #         if not clz:
            #             continue
            #         is_static = False
            #     else:
            #         continue
            elif opcode in xget_opcodes:
                self.process_xget_statement(line)
                continue
            elif 'Ljava/lang/String;-><init>([B)V' in line:
                if 'move-result-object' in snippet[0]:
                    snippet = snippet[1:]
                self.emu.call(snippet, args=args, cv=True, thrown=False)
                if not self.emu.vm.result:
                    continue

                # 如果有结果,则替换
                vx, _ = SmaliLine.parse(line)
                new_line = 'const-string {}, "{}"'.format(
                    vx, self.emu.vm.result)
                del new_body[-1]
                new_body.append(new_line)
                self.make_changes = True
                mtd.set_modified(True)

                snippet.clear()
                continue
            else:
                continue

            # 模拟执行,获取解密参数
            del snippet[-1]
            snippet.extend(array_data_content)
            try:
                snippet = self.process_if_statement(snippet)

                # if DEBUG_MODE:
                #     print(Color.red('开始处理解密参数 {}'.format(line)))
                #     for l in snippet:
                #         print(Color.red(l))

                #     print('args', args)
                #     print(block_args)
                #     print(keys)
                #     print(this_block_key)
                #     print('-' * 80)

                pre_args = block_args[last_block_key].copy()
                args.update(pre_args)
                if this_block_key in block_args:
                    args.update(block_args[this_block_key])
                args.update(self.fields)

                self.emu.call(snippet, args=args, cv=True, thrown=False)
                registers = self.emu.vm.variables
                block_args[this_block_key] = registers

                # if DEBUG_MODE:
                #     print(snippet)
                #     print('args:', args)
                #     print('smali执行后,寄存器内容', registers)

                if registers:
                    for k, v in registers.items():
                        if v is None:
                            continue
                        args[k] = v

                registers = args
            except TIMEOUT_EXCEPTION:
                snippet.clear()
                continue

            print(Color.red('->'))

            obj_flag = False
            if len(ptypes) == 1 and ptypes[0][0] == 'L' and ptypes != [
                    'Ljava/lang/String;'
            ]:
                # 单独处理参数为对象的情况
                obj_flag = True

            snippet.clear()  # 已经执行过的代码,不再执行
            if not registers and not obj_flag:
                continue

            print(Color.red('->>'))

            # 从寄存器中获取对应的参数
            # 参数获取 "arguments": ["I:198", "I:115", "I:26"]}
            arguments = []
            # args = {}  # the parameter of smali method
            if not obj_flag:
                ridx = -1
                for item in ptypes:
                    ridx += 1
                    rname = rnames[ridx]
                    if rname not in registers:
                        break
                    value = registers[rnames[ridx]]
                    argument = self.convert_args(item, value)
                    if argument is None:
                        break
                    arguments.append(argument)
            else:
                arguments.append('Object:' + smali2java(ptypes[0]))

            # if DEBUG_MODE:
            #     print(Color.red('->>'))
            #     print('参数类型', ptypes)
            #     print('参数值', arguments)
            if len(arguments) != len(ptypes):
                print(Color.red('->> 参数对不上'))
                continue

            json_item = self.get_json_item(cname, mname, arguments)
            print(json_item)
            # print('生成json item')

            # {id}_{rtn_name} 让这个唯一化,便于替换
            old_content = '# %s' % json_item['id']
            # 如果 move_result_obj 操作存在的话,解密后一起替换
            find = self.move_result_obj_ptn.search(lines[index + 1])

            print(Color.red('->> not fount'))

            # 必须要找到返回值操作,用于更新寄存器
            if not find:
                print('找不到返回寄存器')
                continue

            print(Color.red('->>>'))

            rtn_name = find.groups()[0]
            # 为了避免 '# abc_v10' 替换成 '# abc_v1'
            old_content = old_content + '_' + rtn_name + 'X'
            self.append_json_item(json_item, mtd, old_content, rtn_name)

            result = self.get_result(rtype)
            # if DEBUG_MODE:
            #     print("解密结果", result)

            self.json_list.clear()

            if result:
                new_body = new_body[:-1]
            else:
                continue

            if not args:
                args = {}
            if rtype == 'Ljava/lang/String;':
                result = list(result.values())[0][0]
                # 更新寄存器
                args[rtn_name] = result
                # 更新代码
                new_line = 'const-string {}, "{}"'.format(rtn_name, result)
                new_body.append(new_line)
                self.make_changes = True
                mtd.set_modified(True)
            elif rtype.startswith('['):
                args[rtn_name] = result
                # 把结果保存到当前分支

            else:
                print("返回值并非字符串,也不是B/C数组")

            if args:
                block_args[this_block_key].update(args)

            # 把结果保存到当前分支
            # if DEBUG_MODE:
            #     print(block_args)
            #     print('*' * 100)
            #     print('last_block_key', last_block_key)
            #     print('this_block_key', this_block_key)
            #
            # pre_args = block_args[last_block_key].copy()
            # if this_block_key in block_args:
            #     pre_args.update(block_args[this_block_key])
            # block_args[this_block_key] =

        mtd.set_body('\n'.join(new_body))
Пример #5
0
    def _process_mtd(self, mtd):
        # 如果存在数组
        array_data_content = []
        arr_res = self.arr_data_ptn.search(mtd.get_body())
        if arr_res:
            array_data_content = re.split(r'\n\s', arr_res.group())

        lines = re.split(r'\n\s*', mtd.get_body())

        old_body = lines.copy()  # 存放原始方法体
        new_body = []   # 存放解密后的方法体

        snippet = []  # 存放smali代码,用于模拟执行
        args = self.fields   # 存放方法参数,用于smaliemu执行

        index = -1  # 用于计数

        for line in lines:
            snippet.append(line)
            index += 1

            if 'sget' in line:
                print('->', line)
                from smafile import SmaliLine
                result = SmaliLine.parse(line)
                if not result:
                    continue
                cname, fname, rtype, rname = result

                if rtype not in {'I', 'S', 'C', 'F', 'Ljava/lang/String;', '[B', '[C', '[I', '[Ljava/lang/String;'}:
                    continue

                abs_fname = self.java2smali(cname) + '->' + fname + ':' + rtype
                
                if abs_fname in self.fields.keys():
                    continue

                if abs_fname in args.keys():
                    print(args[abs_fname])
                    continue

                print(cname, fname, rtype, rname)
                self.feild_datas = {
                    'type': 'field',
                    'data': []
                }
                json_item = {
                    'className': cname,
                    'fieldName': [fname],
                    'fieldType': rtype
                }
                print(json_item)
                self.feild_datas['data'].append(json_item)
                
                value = self.get_field_value(json_item)
                if not value:
                    continue
                
                self.fields[abs_fname] = value
                continue

            if 'invoke-static' not in line or not line.endswith(')Ljava/lang/String;'):
                new_body.append(line)
                continue

            # 排除Android自身的类
            flag = False
            for clz in android_strs:
                if clz in line:
                    flag = True
                    break
            if flag:
                new_body.append(line)
                continue
            
            # result = self.invoke_static_ptn.match(line)
            # if not result:
            #     new_body.append(line)
            #     continue
            
            # print(result)

            from smafile import SmaliLine
            cname, mname, protos, rtype, rnames = SmaliLine.parse_invoke_static(line)
            
            # 参数名(寄存器的名),类名,方法名,proto(简称)
            # register_name, class_name, mtd_name, protos
            # ('v1, v2, v3', 'Lcom/game/pay/sdk/y', 'a', 'ISB')
            # 解密参数的寄存器名
            # rnames = result.groups()[0].split(', ')
            # cname = result.groups()[1][1:].replace('/', '.')
            # mname = result.groups()[2]
            # protos = self.proto_ptn.findall(result.groups()[3])

            print(rnames, cname, mname, protos)

            # 初始化所有寄存器
            del snippet[-1]
            # snippet.extend(array_data_content)
            # try:
            #     args.update(self.pre_process(snippet))
            # except TIMEOUT_EXCEPTION:
            #     pass
            
            try:
                # registers = self.get_vm_variables(
                #     snippet, args, rnames)
                # args = registers if registers else args
                if DEBUG:
                    print('smali代码:')
                    print(snippet)
                self.emu.call(snippet, args=args, cv=True, thrown=False)
                registers = self.emu.vm.variables
                if registers:
                    for k, v in registers.items():
                        if v is None:
                            continue
                        args[k] = v
                
                registers = args

            except TIMEOUT_EXCEPTION:
                snippet.clear()
                # new_body.append(line)
                continue

            snippet.clear()
            if not registers:
                continue
            # snippet.clear()

            # if not registers:
            #     new_body.append(line)
            #     continue

            # 从寄存器中获取对应的参数
            # 参数获取 "arguments": ["I:198", "I:115", "I:26"]}
            arguments = []
            # args = {}  # the parameter of smali method
            ridx = -1
            for item in protos:
                ridx += 1
                rname = rnames[ridx]
                if rname not in registers:
                    break
                value = registers[rnames[ridx]]
                argument = self.convert_args(item, value)
                if argument is None:
                    break
                arguments.append(argument)
            
            if DEBUG:
                print('解密参数:')
                print(arguments)

            if len(arguments) != len(protos):
                new_body.append(line)
                continue

            json_item = self.get_json_item(cname, mname,
                                           arguments)
            # {id}_{rtn_name} 让这个唯一化,便于替换
            old_content = '# %s' % json_item['id']

            # 如果 move_result_obj 操作存在的话,解密后一起替换
            find = self.move_result_obj_ptn.search(lines[index + 1])

            if find:
                rtn_name = find.groups()[0]
                # 为了避免 '# abc_v10' 替换成 '# abc_v1'
                old_content = old_content + '_' + rtn_name + 'X'
                self.append_json_item(json_item, mtd, old_content,
                                      rtn_name)
            else:
                old_content = old_content + '_X'
                self.append_json_item(
                    json_item, mtd, old_content, None)
            old_body[index] = old_content

        mtd.set_body('\n'.join(old_body))

        if DEBUG:
            print("解密方法内容:")
            print(self.json_list)