Beispiel #1
0
def run_data_file(data_file):
    emu = Emulator()
    ret = emu.run(data_file)
    outx = emu.vm.variables.copy()
    outx.update({'ret': ret})
    return str(OrderedDict(sorted(outx.items()))).replace(
        'OrderedDict([(',
        '{').replace(')])', '}').replace("',", "':").replace("), (", ', ')
Beispiel #2
0
    def __init__(self, driver, smalidir):
        self.make_changes = False
        self.driver = driver
        self.smalidir = smalidir
        # self.smali_files = smali_files

        self.emu = Emulator()
        self.emu2 = Emulator()
Beispiel #3
0
    def __init__(self, driver, smalidir, mfilters=None):
        self.make_changes = False
        self.driver = driver
        self.smalidir = smalidir
        # method filters
        self.mfilters = mfilters
        # self.smali_files = smali_files

        self.emu = Emulator()
        self.emu2 = Emulator()
Beispiel #4
0
    def get_arguments_from_clinit(self, field):
        reg = '([\w\W]+?)sput-object (v\d+), %s' % re.escape(field)
        sput_obj_ptn = re.compile(reg)

        from smaliemu.emulator import Emulator
        emu = Emulator()

        array_data_ptn = re.compile(r':array_[\w\d]+\s*.array-data[\w\W\s]+.end array-data')


        class_name = field.split('->')[0]
        for sf in self.smali_files:
            if sf.class_name == class_name:
                for mtd in sf.methods:
                    arr = []
                    if mtd.name == '<clinit>':
                        matchs = sput_obj_ptn.search(mtd.body).groups()
                        snippet = matchs[0]
                        code_content = matchs[0]
                        array_data_context = re.split(r'\n+', array_data_ptn.search(mtd.body).group())
                        # print(array_data_context)

                        return_register_name = matchs[1]
                        arr = re.split(r'\n+', snippet)[:-1]
                        arr.append('return-object %s' % return_register_name)
                        arr.extend(array_data_context)
                        # print(arr)
                        # raise Exception




                        try:
                            # TODO 默认异常停止,这种情况可以考虑,全部跑一遍。
                            # 因为有可能参数声明的时候,位置错位,还有可能是寄存器复用。
                            arr_data = emu.call(arr, thrown=True)
                            if len(emu.vm.exceptions) > 0:
                                break

                            arguments = []
                            byte_arr = []
                            for item in arr_data:
                                if item == '':
                                    item = 0
                                byte_arr.append(item)
                            arguments.append('[B:' + str(byte_arr))

                            return arguments
                        except Exception as e:
                            print(e)
                            pass
                        break
Beispiel #5
0
    def __init__(self, driver, smalidir):
        Plugin.__init__(self, driver, smalidir)
        self.emu2 = Emulator()

        # 匹配参数为内置类型的静态调用函数
        INVOKE_STATIC_RE = (
            r'invoke-static.*?{([(v|p)\.\d,\s]*)}, (.*?);->(.*?)'
            r'\(((?:B|S|C|I|J|F|D|Ljava/lang/String;|'
            r'\[B|\[S|\[C|\[I|\[J|\[F|\[D|\[Ljava/lang/String;'
            r')*?)\)Ljava/lang/String;')

        # 匹配proto
        PROTO_RE = (r'(B|S|C|I|J|F|D|Ljava/lang/String;|'
                    r'\[B|\[S|\[C|\[I|\[J|\[F|\[D|\[Ljava/lang/String;)')

        self.invoke_static_ptn = re.compile(INVOKE_STATIC_RE)
        self.proto_ptn = re.compile(PROTO_RE)
        self.arr_data_ptn = re.compile(self.ARRAY_DATA_PATTERN)
        self.move_result_obj_ptn = re.compile(self.MOVE_RESULT_OBJECT)
Beispiel #6
0
 def __init__(self, driver, smalidir):
     Plugin.__init__(self, driver, smalidir)
     self.emu2 = Emulator()
     self.templets = []
     if not self.templets:
         self._init_templets()
Beispiel #7
0
class Plugin(object):
    """
    解密插件基类
    """
    name = 'Plugin'
    description = ''
    version = ''
    enabled = True
    index = 0  # 插件执行顺序;最小值为0,数值越大,执行越靠后。

    # TODO 这个得放到库中

    # const/16 v2, 0x1a
    CONST_NUMBER = r'const(?:\/\d+) [vp]\d+, (-?0x[a-f\d]+)\s+'
    # ESCAPE_STRING = '''"(.*?)(?<!\\\\)"'''
    ESCAPE_STRING = '"(.*?)"'
    # const-string v3, "encode string"
    CONST_STRING = r'const-string [vp]\d+, ' + ESCAPE_STRING + '.*'
    # move-result-object v0
    MOVE_RESULT_OBJECT = r'move-result-object ([vp]\d+)'
    # new-array v1, v1, [B
    NEW_BYTE_ARRAY = r'new-array [vp]\d+, [vp]\d+, \[B\s+'
    # new-array v1, v1, [B
    NEW_INT_ARRAY = r'new-array [vp]\d+, [vp]\d+, \[I\s+'
    # new-array v1, v1, [B
    NEW_CHAR_ARRAY = r'new-array [vp]\d+, [vp]\d+, \[C\s+'
    # fill-array-data v1, :array_4e
    FILL_ARRAY_DATA = r'fill-array-data [vp]\d+, :array_[\w\d]+\s+'

    ARRAY_DATA_PATTERN = r':array_[\w\d]+\s*.array-data[\w\W\s]+.end array-data'

    # [{'className':'', 'methodName':'', 'arguments':'', 'id':''}, ..., ]
    json_list = []
    # [(mtd, old_content, new_content), ..., ]
    target_contexts = {}
    #
    data_arraies = {}
    # smali methods witch have been update
    smali_mtd_updated_set = set()

    # 存放field的内容,各个插件通用。
    fields = {}

    def __init__(self, driver, smalidir, mfilters=None):
        self.make_changes = False
        self.driver = driver
        self.smalidir = smalidir
        # method filters
        self.mfilters = mfilters
        # self.smali_files = smali_files

        self.emu = Emulator()
        self.emu2 = Emulator()

    # def get_return_variable_name(self, line):
    #     mro_statement = re.search(self.MOVE_RESULT_OBJECT, line).group()
    #     return mro_statement[mro_statement.rindex(' ') + 1:]

    @timeout(1)
    def pre_process(self, snippet):
        """
        smaliemu 处理sget等获取类的变量时,是直接从变量池中取等。
        所以,对于这些指令,可以预先初始化。

        先执行Feild Value插件,对类的成员变量进行解密,再进行处理。

        TODO 其他指令,其他参数
        FIXME 注意,这种方法不一定能获取到参数,改用其他方式吧。
        """
        args = {}
        # sget-object v0, clz_name;->field_name:Ljava/util/List;
        # 能够作为参数的值,数字、字符串、数组等;而不是其他
        for line in snippet:
            if 'sget' not in line:
                continue

            field_desc = line.split()[-1]
            try:
                field = self.smalidir.get_field(field_desc)
                if not field:
                    continue
            except TypeError as ex:
                logger.warning(ex)
                logger(field_desc)
                continue

            value = field.get_value()
            if not value:
                continue

            args.update({field_desc: value})

        return args

    @staticmethod
    def convert_args(typ8, value):
        """
        根据参数类型,把参数转换为适合Json保存的格式。
        """
        if value is None:
            return None

        if typ8 == 'I':
            if not isinstance(value, int):
                return None
            return 'I:' + str(value)

        if typ8 == 'B':
            if not isinstance(value, int):
                return None
            return 'B:' + str(value)

        if typ8 == 'S':
            if not isinstance(value, int):
                return None
            return 'S:' + str(value)

        if typ8 == 'C':
            # don't convert to char, avoid some unreadable chars.
            return 'C:' + str(value)

        if typ8 == 'Ljava/lang/String;':
            if not isinstance(value, str):
                return None
            # smali 会把非ascii字符串转换为unicode字符串
            # java 可以直接处理unicode字符串
            return "java.lang.String:" + value
        if typ8 == '[B':
            if not isinstance(value, list):
                return None
            byte_arr = []
            for item in value:
                if item == '':
                    item = 0
                byte_arr.append(item)
            return '[B:' + str(byte_arr)

        if typ8 == '[C':
            if not isinstance(value, list):
                return None
            byte_arr = []
            for item in value:
                if item == '':
                    item = 0
                byte_arr.append(item)
            return '[C:' + str(byte_arr)

        logger.warning('不支持该类型 %s %s', typ8, value)

    @timeout(3)
    def get_vm_variables(self, snippet, args, rnames):
        """
        snippet : smali 代码
        args    :方法参数
        rnames  :寄存器

        获取当前vm的变量

        """
        # 原本想法是,行数太多,执行过慢;而参数一般在前几行
        # 可能执行5句得倒的结果,跟全部执行的不一样
        # TODO 有一定的几率,得到奇怪的参数,导致解密结果异常
        self.emu2.call(snippet[-5:], args=args, thrown=False)
        result = self.varify_argments(self.emu2.vm.variables, rnames)
        if result:
            return self.emu2.vm.variables

        self.emu2.call(snippet, args=args, thrown=False)
        result = self.varify_argments(self.emu2.vm.variables, rnames)
        if result:
            return self.emu2.vm.variables

    @staticmethod
    def varify_argments(variables, arguments):
        """
        variables :vm存放的变量
        arguments :smali方法的参数
        验证smali方法的参数
        """
        for k in arguments:
            value = variables.get(k, None)
            if value is None:
                return False
        return True

    @staticmethod
    def get_json_item(cls_name, mtd_name, args):
        """
        json item 为一个json格式的解密对象。
        包含id、className、methodName、arguments。
        模拟器/手机会通过解析这个对象进行解密。
        """
        item = {
            'className': cls_name,
            'methodName': mtd_name,
            'arguments': args
        }
        item['id'] = hashlib.sha256(
            JSONEncoder().encode(item).encode('utf-8')).hexdigest()
        return item

    def append_json_item(self, json_item, mtd, old_content, rtn_name):
        """
        往json list添加json解密对象
        json list 存放了所有的json格式解密对象。
        """
        mid = json_item['id']
        if rtn_name:
            new_content = 'const-string ' + rtn_name + ', "{}"\n'
        else:
            new_content = ('const-string v0, "Dexsim"\n'
                           'const-string v1, "{}"\n'
                           'invoke-static {{v0, v1}}, Landroid/util/Log;->d'
                           '(Ljava/lang/String;Ljava/lang/String;)I\n')

        if mid not in self.target_contexts:
            self.target_contexts[mid] = [(mtd, old_content, new_content)]
        else:
            self.target_contexts[mid].append((mtd, old_content, new_content))

        if json_item not in self.json_list:
            self.json_list.append(json_item)

    @abstractmethod
    def run(self):
        """
        插件执行逻辑
        插件必须实现该方法
        """
        pass

    def optimize(self):
        """
        smali 通用优化代码
        一般情况下,可以使用这个,插件也可以实现自己的优化方式。
        """
        if not self.json_list or not self.target_contexts:
            return

        jsons = JSONEncoder().encode(self.json_list)
        if DEBUG:
            print("\nJSON内容(解密类、方法、参数):")
            print(jsons)

        outputs = {}
        with tempfile.NamedTemporaryFile(mode='w+', delete=False) as tfile:
            tfile.write(jsons)
        outputs = self.driver.decode(tfile.name)
        os.unlink(tfile.name)

        if DEBUG:
            print("解密结果:")
            print(outputs)

        if not outputs:
            return

        if isinstance(outputs, str):
            return

        for key, value in outputs.items():
            if key not in self.target_contexts:
                logger.warning('not found %s', key)
                continue

            if not value[0] or value[0] == 'null':
                continue

            if not value[0].isprintable():
                print("解密结果不可读:", key, value)
                continue

            # json_item, mtd, old_content, rtn_name
            for item in self.target_contexts[key]:
                old_body = item[0].get_body()
                old_content = item[1]
                if DEBUG:
                    print(item[2], value[0])
                new_content = item[2].format(value[0])

                item[0].set_body(old_body.replace(old_content, new_content))
                item[0].set_modified(True)
                self.make_changes = True

        self.smali_files_update()
        self.clear()

    def clear(self):
        """
        每次解密完毕后,都需要清理。
        """
        self.json_list.clear()
        self.target_contexts.clear()

    def smali_files_update(self):
        '''
            write changes to smali files
        '''
        if self.make_changes:
            for sf in self.smalidir:
                sf.update()
Beispiel #8
0
# Software distributed under the License is distributed
# on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either
# express or implied. See the GPL for the specific language
# governing rights and limitations.
#
# You should have received a copy of the GPL along with this
# program. If not, go to http://www.gnu.org/licenses/gpl.html
# or write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
import sys
import os
sys.path.append(os.path.join(os.path.dirname(__file__), '..'))

from smaliemu.emulator import Emulator

emu = Emulator()

filename = os.path.join(os.path.dirname(__file__), 'decryptor.smali')

# Arguments for the method.
# args = {
#     'p0': (-62, -99, -106, -125, -123, -105, -98, -37, -105, -97, -103, -41, -118, -97, -113, -103, -109, -104, -115, 111, 98, 103, 35, 52),
#     'p1': 19
# }

# ret = emu.run(filename, args)
# print(emu.stats)
# print("RESULT: %s" % ret)
# print('-' * 100)

emu2 = Emulator()
Beispiel #9
0
 def __init__(self, driver, smalidir):
     Plugin.__init__(self, driver, smalidir)
     self.emu2 = Emulator()
Beispiel #10
0
 def __init__(self, driver, methods, smali_files):
     self.emu = Emulator()
     Plugin.__init__(self, driver, methods, smali_files)
Beispiel #11
0
class NEW_STRING(Plugin):

    name = "NEW_STRING"
    version = '0.0.3'
    enabled = True

    def __init__(self, driver, methods, smali_files):
        self.emu = Emulator()
        Plugin.__init__(self, driver, methods, smali_files)

    def run(self):
        print('run Plugin: %s' % self.name, end=' -> ')
        self.__process_new_str()
        self.__process_to_string()

    def __process_new_str(self):
        '''
            这里有2种情况:
            1. 只有1个数值常量
            2. 有1个以上的数值常量,会使用fill-array-data

            这个都无所谓,直接执行代码片段即可
        '''
        for mtd in self.methods:
            if 'Ljava/lang/String;-><init>([B)V' not in mtd.body:
                continue

            # TODO 初始化 array-data 所有的数组
            fill_array_datas = {}
            array_re = r'(array_[\w\d]+)\s*\.array-data[\w\s]+.end array-data$'
            ptn1 = re.compile(array_re)

            flag = False
            new_body = []
            array_key = None

            new_string_re = r'invoke-direct {(v\d+), v\d+}, Ljava/lang/String;-><init>\([\[BCI]+\)V'
            ptn2 = re.compile(new_string_re)
            for line in re.split(r'\n+', mtd.body):
                new_line = None
                if 'Ljava/lang/String;-><init>' in line:
                    result = ptn2.search(line)
                    if not result:
                        new_body.append(line)
                        continue
                    return_register_name = result.groups()[0]

                    tmp = new_body.copy()
                    tmp.append(line)
                    try:
                        tmp.append('return-object %s' % return_register_name)
                        decoded_string = (self.emu.call(tmp))
                        if decoded_string:
                            new_line = 'const-string %s, "%s"' % (return_register_name, decoded_string)
                    except Exception as e:
                        # TODO log2file
                        pass

                if new_line:
                    flag = True
                    new_body.append(new_line)
                else:
                    new_body.append(line)

            if flag:
                mtd.body = '\n'.join(new_body)
                mtd.modified = True
                self.make_changes = True

        self.smali_files_update()


    def __process_to_string(self):
        to_string_re = (r'new-instance v\d+, Ljava/lang/StringBuilder;[\w\W\s]+?{(v\d+)[.\sv\d]*}, Ljava/lang/StringBuilder;->toString\(\)Ljava/lang/String;')
        ptn2 = re.compile(to_string_re)
        for mtd in self.methods:

            if 'const-string' not in mtd.body:
                continue
            if 'Ljava/lang/StringBuilder;-><init>' not in mtd.body:
                continue
            if 'Ljava/lang/StringBuilder;->toString()Ljava/lang/String;' not in mtd.body:
                continue

            flag = False
            new_content = None

            result = ptn2.finditer(mtd.body)

            for item in result:
                return_register_name = item.groups()[0]
                old_content = item.group()
                arr = re.split(r'\n+', old_content)
                arr.append('return-object %s' % return_register_name)
                try:
                    decoded_string = self.emu.call(arr)

                    if len(self.emu.vm.exceptions) > 0:
                        continue

                    if decoded_string:
                        new_content = 'const-string %s, "%s"' % (return_register_name, decoded_string)
                except Exception as e:
                    # print(e)
                    continue

                if new_content:
                    flag = True
                    mtd.body = mtd.body.replace(old_content, new_content)

            if flag:
                mtd.modified = True
                self.make_changes = True

        self.smali_files_update()
Beispiel #12
0
class Plugin(object):
    """
    解密插件基类
    """
    name = 'Plugin'
    description = ''
    version = ''
    enabled = True

    # const/16 v2, 0x1a
    CONST_NUMBER = r'const(?:\/\d+) [vp]\d+, (-?0x[a-f\d]+)\s+'
    # ESCAPE_STRING = '''"(.*?)(?<!\\\\)"'''
    ESCAPE_STRING = '"(.*?)"'
    # const-string v3, "encode string"
    CONST_STRING = r'const-string [vp]\d+, ' + ESCAPE_STRING + '.*'
    # move-result-object v0
    MOVE_RESULT_OBJECT = r'move-result-object ([vp]\d+)'
    # new-array v1, v1, [B
    NEW_BYTE_ARRAY = r'new-array [vp]\d+, [vp]\d+, \[B\s+'
    # new-array v1, v1, [B
    NEW_INT_ARRAY = r'new-array [vp]\d+, [vp]\d+, \[I\s+'
    # new-array v1, v1, [B
    NEW_CHAR_ARRAY = r'new-array [vp]\d+, [vp]\d+, \[C\s+'
    # fill-array-data v1, :array_4e
    FILL_ARRAY_DATA = r'fill-array-data [vp]\d+, :array_[\w\d]+\s+'

    ARRAY_DATA_PATTERN = r':array_[\w\d]+\s*.array-data[\w\W\s]+.end array-data'

    # [{'className':'', 'methodName':'', 'arguments':'', 'id':''}, ..., ]
    json_list = []
    # [(mtd, old_content, new_content), ..., ]
    target_contexts = {}
    #
    data_arraies = {}
    # smali methods witch have been update
    smali_mtd_updated_set = set()

    def __init__(self, driver, smalidir):
        self.make_changes = False
        self.driver = driver
        self.smalidir = smalidir
        # self.smali_files = smali_files

        self.emu = Emulator()
        self.emu2 = Emulator()

    # def get_return_variable_name(self, line):
    #     mro_statement = re.search(self.MOVE_RESULT_OBJECT, line).group()
    #     return mro_statement[mro_statement.rindex(' ') + 1:]

    @timeout(1)
    def pre_process(self, snippet):
        """
        预处理 sget指令
        """
        # emu2 = Emulator()
        args = {}

        clz_sigs = set()
        field_desc_prog = re.compile(r'^.*, (.*?->.*)$')
        for line in snippet:
            if 'sget' not in line:
                continue

            field_desc = field_desc_prog.match(line).groups()[0]

            try:
                field = self.smalidir.get_field(field_desc)
            except TypeError as ex:
                logger.warning(ex)
                logger(field_desc)
                continue

            if field:
                value = field.get_value()
                if value:
                    args.update({field_desc: value})
                    continue
            clz_sigs.add(field_desc.split('->')[0])

        for clz_sig in clz_sigs:
            mtd = self.smalidir.get_method(clz_sig, '<clinit>()V')
            if mtd:
                body = mtd.get_body()
                self.emu2.call(re.split(r'\n\s*', body), thrown=False)
                self.emu2.call(re.split(r'\n\s*', body), thrown=False)
                args.update(self.emu2.vm.variables)

                for (key, value) in self.emu2.vm.variables.items():
                    if clz_sig in key:
                        field = self.smalidir.get_field(key)
                        field.set_value(value)
        # print(__name__, 'pre_process, emu2', sys.getsizeof(self.emu2))
        return args

    @staticmethod
    def convert_args(typ8, value):
        """
        根据参数类型,把参数转换为适合Json保存的格式。
        """
        if value is None:
            return None

        if typ8 == 'I':
            if not isinstance(value, int):
                return None
            return 'I:' + str(value)

        if typ8 == 'B':
            if not isinstance(value, int):
                return None
            return 'B:' + str(value)

        if typ8 == 'S':
            if not isinstance(value, int):
                return None
            return 'S:' + str(value)

        if typ8 == 'C':
            # don't convert to char, avoid some unreadable chars.
            return 'C:' + str(value)

        if typ8 == 'Ljava/lang/String;':
            if not isinstance(value, str):
                return None

            import codecs
            item = codecs.getdecoder('unicode_escape')(value)[0]
            args = []
            for i in item.encode("UTF-8"):
                args.append(i)
            return "java.lang.String:" + str(args)

        if typ8 == '[B':
            if not isinstance(value, list):
                return None
            byte_arr = []
            for item in value:
                if item == '':
                    item = 0
                byte_arr.append(item)
            return '[B:' + str(byte_arr)

        if typ8 == '[C':
            if not isinstance(value, list):
                return None
            byte_arr = []
            for item in value:
                if item == '':
                    item = 0
                byte_arr.append(item)
            return '[C:' + str(byte_arr)

        logger.warning('不支持该类型 %s %s', typ8, value)

    @timeout(3)
    def get_vm_variables(self, snippet, args, rnames):
        """
        snippet : smali 代码
        args    :方法藏书
        rnames  :寄存器

        获取当前vm的变量
        """
        self.emu2.call(snippet[-5:], args=args, thrown=False)

        # 注意: 寄存器的值,如果是跨方法的话,可能存在问题 —— 导致解密乱码
        # A方法的寄存器v1,与B方法的寄存器v1,保存的内容不一定一样
        # TODO 下一个方法,则进行清理
        # 方法成员变量,可以考虑初始化到smalifile中
        # 其他临时变量,则用smali执行
        result = self.varify_argments(self.emu2.vm.variables, rnames)
        if result:
            return self.emu2.vm.variables

        self.emu2.call(snippet, args=args, thrown=False)
        result = self.varify_argments(self.emu2.vm.variables, rnames)
        if result:
            return self.emu2.vm.variables

    @staticmethod
    def varify_argments(variables, arguments):
        """
        variables :vm存放的变量
        arguments :smali方法的参数
        验证smali方法的参数
        """
        for k in arguments:
            value = variables.get(k, None)
            if value is None:
                return False
        return True

    @staticmethod
    def get_json_item(cls_name, mtd_name, args):
        """
        json item 为一个json格式的解密对象。
        包含id、className、methodName、arguments。
        模拟器/手机会通过解析这个对象进行解密。
        """
        item = {
            'className': cls_name,
            'methodName': mtd_name,
            'arguments': args
        }
        item['id'] = hashlib.sha256(
            JSONEncoder().encode(item).encode('utf-8')).hexdigest()
        return item

    def append_json_item(self, json_item, mtd, old_content, rtn_name):
        """
        往json list添加json解密对象
        json list 存放了所有的json格式解密对象。
        """
        mid = json_item['id']
        if rtn_name:
            new_content = 'const-string %s, ' % rtn_name + '%s'
        else:
            # TODO XX 也许有更好的方式
            # const-string v0, "Dexsim"
            # const-string v1, "Decode String"
            # invoke-static {v0, v1}, Landroid/util/Log;->d(
            # Ljava/lang/String;Ljava/lang/String;)I
            new_content = ('const-string v0, "Dexsim"\n'
                           'const-string v1, %s\n'
                           'invoke-static {v0, v1}, Landroid/util/Log;->d'
                           '(Ljava/lang/String;Ljava/lang/String;)I\n')

        if mid not in self.target_contexts:
            self.target_contexts[mid] = [(mtd, old_content, new_content)]
        else:
            self.target_contexts[mid].append((mtd, old_content, new_content))

        if json_item not in self.json_list:
            self.json_list.append(json_item)

    @abstractmethod
    def run(self):
        """
        插件执行逻辑
        插件必须实现该方法
        """
        pass

    def optimize(self):
        """
        smali 通用优化代码
        一般情况下,可以使用这个,插件也可以实现自己的优化方式。
        """
        if not self.json_list or not self.target_contexts:
            return

        jsons = JSONEncoder().encode(self.json_list)

        outputs = {}
        with tempfile.NamedTemporaryFile(mode='w+', delete=False) as tfile:
            tfile.write(jsons)
        outputs = self.driver.decode(tfile.name)
        os.unlink(tfile.name)

        if not outputs:
            return

        if isinstance(outputs, str):
            return

        for key, value in outputs.items():
            if 'success' not in value:
                continue
            if key not in self.target_contexts:
                logger.warning('not found %s', key)
                continue

            if value[1] == 'null':
                continue

            # json_item, mtd, old_content, rtn_name
            for item in self.target_contexts[key]:
                old_body = item[0].get_body()
                old_content = item[1]
                new_content = item[2] % value[1]

                # It's not a string.
                if outputs[key][1] == 'null':
                    continue

                item[0].set_body(old_body.replace(old_content, new_content))
                item[0].set_modified(True)
                self.make_changes = True

        self.smali_files_update()

    def clear(self):
        """
        每次解密完毕后,都需要清理。
        """
        self.json_list.clear()
        self.target_contexts.clear()

    def smali_files_update(self):
        '''
            write changes to smali files
        '''
        if self.make_changes:
            for sf in self.smalidir:
                sf.update()