Beispiel #1
0
    def obfuscate(self, obfuscation_info: Obfuscation):
        self.logger.info('Running "{0}" obfuscator'.format(self.__class__.__name__))

        try:
            for smali_file in util.show_list_progress(obfuscation_info.get_smali_files(),
                                                      interactive=obfuscation_info.interactive,
                                                      description='Inserting arithmetic computations in smali files'):
                self.logger.debug('Inserting arithmetic computations in file "{0}"'.format(smali_file))
                with util.inplace_edit_file(smali_file) as current_file:
                    editing_method = False
                    start_label = None
                    end_label = None
                    for line in current_file:
                        if line.startswith('.method ') and ' abstract ' not in line and \
                                ' native ' not in line and not editing_method:
                            # Entering method.
                            print(line, end='')
                            editing_method = True

                        elif line.startswith('.end method') and editing_method:
                            # Exiting method.
                            if start_label and end_label:
                                print('\t:{0}'.format(end_label))
                                print('\tgoto/32 :{0}'.format(start_label))
                                start_label = None
                                end_label = None
                            print(line, end='')
                            editing_method = False

                        elif editing_method:
                            # Inside method.
                            print(line, end='')
                            match = util.locals_pattern.match(line)
                            if match and int(match.group('local_count')) >= 2:
                                # If there are at least 2 registers available, add a fake branch at the beginning of
                                # the method: one branch will continue from here, the other branch will go to the end
                                # of the method and then will return here through a "goto" instruction.
                                v0, v1 = util.get_random_int(1, 32), util.get_random_int(1, 32)
                                start_label = util.get_random_string(16)
                                end_label = util.get_random_string(16)
                                tmp_label = util.get_random_string(16)
                                print('\n\tconst v0, {0}'.format(v0))
                                print('\tconst v1, {0}'.format(v1))
                                print('\tadd-int v0, v0, v1')
                                print('\trem-int v0, v0, v1')
                                print('\tif-gtz v0, :{0}'.format(tmp_label))
                                print('\tgoto/32 :{0}'.format(end_label))
                                print('\t:{0}'.format(tmp_label))
                                print('\t:{0}'.format(start_label))

                        else:
                            print(line, end='')

        except Exception as e:
            self.logger.error('Error during execution of "{0}" obfuscator: {1}'.format(self.__class__.__name__, e))
            raise

        finally:
            obfuscation_info.used_obfuscators.append(self.__class__.__name__)
Beispiel #2
0
    def transform_package_name(self, manifest_xml_root: Element):
        self.encrypted_package_name = '.'.join([self.encrypt_identifier(token)
                                                for token in self.package_name.split('.')])

        # Rename package name in manifest file.
        manifest_xml_root.set('package', self.encrypted_package_name)
        manifest_xml_root.set('{http://schemas.android.com/apk/res/android}sharedUserId',
                              '{0}.uid.shared'.format(util.get_random_string(16)))
Beispiel #3
0
 def add_random_fields(self, original_field_declaration: str):
     if self.added_fields < self.max_fields_to_add:
         for _ in range(util.get_random_int(1, 4)):
             print('\n', end='')
             print(original_field_declaration.replace(
                 ':', '{0}:'.format(util.get_random_string(8))),
                   end='')
             self.added_fields += 1
Beispiel #4
0
    def rename_field_declarations(self,
                                  smali_files: List[str],
                                  interactive: bool = False) -> Set[str]:
        renamed_fields: Set[str] = set()

        # Search for field definitions that can be renamed.
        for smali_file in util.show_list_progress(
                smali_files,
                interactive=interactive,
                description="Renaming field declarations",
        ):
            with util.inplace_edit_file(smali_file) as (in_file, out_file):
                for line in in_file:
                    # Field declared in class.
                    field_match = util.field_pattern.match(line)

                    if field_match:
                        field_name = field_match.group("field_name")
                        # Avoid sub-fields.
                        if "$" not in field_name:
                            # Rename field declaration (usages of this field will be
                            # renamed later) and add some random fields.
                            line = line.replace(
                                "{0}:".format(field_name),
                                "{0}:".format(self.rename_field(field_name)),
                            )
                            out_file.write(line)

                            # Add random fields.
                            if self.added_fields < self.max_fields_to_add:
                                for _ in range(util.get_random_int(1, 4)):
                                    out_file.write("\n")
                                    out_file.write(
                                        line.replace(
                                            ":",
                                            "{0}:".format(
                                                util.get_random_string(8)),
                                        ))
                                    self.added_fields += 1

                            field = "{field_name}:{field_type}".format(
                                field_name=field_match.group("field_name"),
                                field_type=field_match.group("field_type"),
                            )
                            renamed_fields.add(field)
                        else:
                            out_file.write(line)
                    else:
                        out_file.write(line)

        return renamed_fields
Beispiel #5
0
    def obfuscate(self, obfuscation_info: Obfuscation):
        self.logger.info('Running "{0}" obfuscator'.format(
            self.__class__.__name__))

        try:
            op_codes = util.get_code_block_valid_op_codes()
            op_code_pattern = re.compile(r"\s+(?P<op_code>\S+)")
            if_pattern = re.compile(
                r"\s+(?P<if_op_code>\S+)"
                r"\s(?P<register>[vp0-9,\s]+?),\s:(?P<goto_label>\S+)")

            for smali_file in util.show_list_progress(
                    obfuscation_info.get_smali_files(),
                    interactive=obfuscation_info.interactive,
                    description="Code reordering",
            ):
                self.logger.debug(
                    'Reordering code in file "{0}"'.format(smali_file))
                with util.inplace_edit_file(smali_file) as (in_file, out_file):
                    editing_method = False
                    inside_try_catch = False
                    jump_count = 0
                    for line in in_file:
                        if (line.startswith(".method ")
                                and " abstract " not in line
                                and " native " not in line
                                and not editing_method):
                            # If at the beginning of a non abstract/native method
                            out_file.write(line)
                            editing_method = True
                            inside_try_catch = False
                            jump_count = 0

                        elif line.startswith(".end method") and editing_method:
                            # If a the end of the method.
                            out_file.write(line)
                            editing_method = False
                            inside_try_catch = False

                        elif editing_method:
                            # Inside method. Check if this line contains an op code at
                            # the beginning of the string.
                            match = op_code_pattern.match(line)
                            if match:
                                op_code = match.group("op_code")

                                # Check if we are entering or leaving a try-catch
                                # block of code.
                                if op_code.startswith(":try_start_"):
                                    out_file.write(line)
                                    inside_try_catch = True
                                elif op_code.startswith(":try_end_"):
                                    out_file.write(line)
                                    inside_try_catch = False

                                # If this is a valid op code, and we are not inside a
                                # try-catch block, mark this section with a special
                                # label that will be used later and invert the if
                                # conditions (if any).
                                elif op_code in op_codes and not inside_try_catch:
                                    jump_name = util.get_random_string(16)
                                    out_file.write(
                                        "\tgoto/32 :l_{label}_{count}\n\n".
                                        format(label=jump_name,
                                               count=jump_count))
                                    out_file.write("\tnop\n\n")
                                    out_file.write("#!code_block!#\n")
                                    out_file.write(
                                        "\t:l_{label}_{count}\n".format(
                                            label=jump_name, count=jump_count))
                                    jump_count += 1

                                    new_if = self.if_mapping.get(op_code, None)
                                    if new_if:
                                        if_match = if_pattern.match(line)
                                        random_label_name = util.get_random_string(
                                            16)
                                        out_file.write(
                                            "\t{if_cond} {register}, "
                                            ":gl_{new_label}\n\n".format(
                                                if_cond=new_if,
                                                register=if_match.group(
                                                    "register"),
                                                new_label=random_label_name,
                                            ))
                                        out_file.write(
                                            "\tgoto/32 :{0}\n\n".format(
                                                if_match.group("goto_label")))
                                        out_file.write("\t:gl_{0}".format(
                                            random_label_name))
                                    else:
                                        out_file.write(line)
                                else:
                                    out_file.write(line)
                            else:
                                out_file.write(line)

                        else:
                            out_file.write(line)

                # Reorder code blocks randomly.
                with util.inplace_edit_file(smali_file) as (in_file, out_file):
                    editing_method = False
                    block_count = 0
                    code_blocks: List[CodeBlock] = []
                    current_code_block = None
                    for line in in_file:
                        if (line.startswith(".method ")
                                and " abstract " not in line
                                and " native " not in line
                                and not editing_method):
                            # If at the beginning of a non abstract/native method
                            out_file.write(line)
                            editing_method = True
                            block_count = 0
                            code_blocks = []
                            current_code_block = None

                        elif line.startswith(".end method") and editing_method:
                            # If a the end of the method.
                            editing_method = False
                            random.shuffle(code_blocks)
                            for code_block in code_blocks:
                                out_file.write(code_block.smali_code)
                            out_file.write(line)

                        elif editing_method:
                            # Inside method. Check if this line is marked with
                            # a special label.
                            if line.startswith("#!code_block!#"):
                                block_count += 1
                                current_code_block = CodeBlock(block_count, "")
                                code_blocks.append(current_code_block)
                            else:
                                if block_count > 0 and current_code_block:
                                    current_code_block.add_smali_code_to_block(
                                        line)
                                else:
                                    out_file.write(line)

                        else:
                            out_file.write(line)

        except Exception as e:
            self.logger.error(
                'Error during execution of "{0}" obfuscator: {1}'.format(
                    self.__class__.__name__, e))
            raise

        finally:
            obfuscation_info.used_obfuscators.append(self.__class__.__name__)
Beispiel #6
0
    def obfuscate(self, obfuscation_info: Obfuscation):
        self.logger.info('Running "{0}" obfuscator'.format(
            self.__class__.__name__))

        try:
            op_codes = util.get_code_block_valid_op_codes()
            op_code_pattern = re.compile(r'\s+(?P<op_code>\S+)')
            if_pattern = re.compile(
                r'\s+(?P<if_op_code>\S+)\s(?P<register>[vp0-9,\s]+?),\s:(?P<goto_label>\S+)'
            )

            for smali_file in util.show_list_progress(
                    obfuscation_info.get_smali_files(),
                    interactive=obfuscation_info.interactive,
                    description='Code reordering'):
                self.logger.debug(
                    'Reordering code in file "{0}"'.format(smali_file))
                with util.inplace_edit_file(smali_file) as current_file:
                    editing_method = False
                    inside_try_catch = False
                    jump_count = 0
                    for line in current_file:
                        if line.startswith('.method ') and ' abstract ' not in line and \
                                ' native ' not in line and not editing_method:
                            # If at the beginning of a non abstract/native method
                            print(line, end='')
                            editing_method = True
                            inside_try_catch = False
                            jump_count = 0

                        elif line.startswith('.end method') and editing_method:
                            # If a the end of the method.
                            print(line, end='')
                            editing_method = False
                            inside_try_catch = False

                        elif editing_method:
                            # Inside method. Check if this line contains an op code at the beginning of the string.
                            match = op_code_pattern.match(line)
                            if match:
                                op_code = match.group('op_code')

                                # Check if we are entering or leaving a try-catch block of code.
                                if op_code.startswith(':try_start_'):
                                    print(line, end='')
                                    inside_try_catch = True
                                elif op_code.startswith(':try_end_'):
                                    print(line, end='')
                                    inside_try_catch = False

                                # If this is a valid op code, and we are not inside a try-catch block, mark this
                                # section with a special label that will be used later and invert the if conditions
                                # (if any).
                                elif op_code in op_codes and not inside_try_catch:
                                    jump_name = util.get_random_string(16)
                                    print('\tgoto/32 :l_{label}_{count}\n'.
                                          format(label=jump_name,
                                                 count=jump_count))
                                    print('\tnop\n')
                                    print('#!code_block!#')
                                    print('\t:l_{label}_{count}'.format(
                                        label=jump_name, count=jump_count))
                                    jump_count += 1

                                    new_if = self.if_mapping.get(op_code, None)
                                    if new_if:
                                        if_match = if_pattern.match(line)
                                        random_label_name = util.get_random_string(
                                            16)
                                        print(
                                            '\t{if_cond} {register}, :gl_{new_label}\n'
                                            .format(
                                                if_cond=new_if,
                                                register=if_match.group(
                                                    'register'),
                                                new_label=random_label_name))
                                        print('\tgoto/32 :{0}\n'.format(
                                            if_match.group('goto_label')))
                                        print('\t:gl_{0}'.format(
                                            random_label_name),
                                              end='')
                                    else:
                                        print(line, end='')
                                else:
                                    print(line, end='')
                            else:
                                print(line, end='')

                        else:
                            print(line, end='')

                # Reorder code blocks randomly.
                with util.inplace_edit_file(smali_file) as current_file:
                    editing_method = False
                    block_count = 0
                    code_blocks: List[CodeBlock] = []
                    current_code_block = None
                    for line in current_file:
                        if line.startswith('.method ') and ' abstract ' not in line and \
                                ' native ' not in line and not editing_method:
                            # If at the beginning of a non abstract/native method
                            print(line, end='')
                            editing_method = True
                            block_count = 0
                            code_blocks = []
                            current_code_block = None

                        elif line.startswith('.end method') and editing_method:
                            # If a the end of the method.
                            editing_method = False
                            random.shuffle(code_blocks)
                            for code_block in code_blocks:
                                print(code_block.smali_code, end='')
                            print(line, end='')

                        elif editing_method:
                            # Inside method. Check if this line is marked with a special label.
                            if line.startswith('#!code_block!#'):
                                block_count += 1
                                current_code_block = CodeBlock(block_count, '')
                                code_blocks.append(current_code_block)
                            else:
                                if block_count > 0 and current_code_block:
                                    current_code_block.add_smali_code_to_block(
                                        line)
                                else:
                                    print(line, end='')

                        else:
                            print(line, end='')

        except Exception as e:
            self.logger.error(
                'Error during execution of "{0}" obfuscator: {1}'.format(
                    self.__class__.__name__, e))
            raise

        finally:
            obfuscation_info.used_obfuscators.append(self.__class__.__name__)
Beispiel #7
0
    def change_method_call(self, invoke_type: str, invoke_pass: str,
                           invoke_object: str, invoke_method: str,
                           invoke_param: str, invoke_return: str,
                           class_name: str, new_method: StringIO):

        new_method_name = util.get_random_string(16)

        is_range_invocation = self.is_range(invoke_type)
        is_static_invocation = self.is_static(invoke_type)

        register_list = self.get_registers(invoke_pass)
        if is_range_invocation:
            register_count = self.get_register_range_count(register_list)
        else:
            register_count = len(register_list)

        is_void_value = self.is_void(invoke_return)
        is_wide_value = self.is_wide(invoke_return)
        is_object_value = self.is_object(invoke_return)

        local_register_count = 1
        if is_void_value:
            local_register_count = 0
        if is_wide_value:
            local_register_count = 2

        move_result_str = 'move-result v0'
        if is_void_value:
            move_result_str = ''
        if is_wide_value:
            move_result_str = 'move-result-wide v0'
        if is_object_value:
            move_result_str = 'move-result-object v0'

        return_str = 'return v0'
        if is_void_value:
            return_str = 'return-void'
        if is_wide_value:
            return_str = 'return-wide v0'
        if is_object_value:
            return_str = 'return-object v0'

        add_param = '' if is_static_invocation else invoke_object
        new_invoke = 'invoke-static/range' if is_range_invocation else 'invoke-static'

        # Insert the new method invocation in the smali file.
        print(
            '\t{invoke_type} {{{invoke_pass}}}, {class_name}->{method_name}({add_param}{invoke_param}){invoke_return}'
            .format(invoke_type=new_invoke,
                    invoke_pass=invoke_pass,
                    class_name=class_name,
                    method_name=new_method_name,
                    add_param=add_param,
                    invoke_param=invoke_param,
                    invoke_return=invoke_return))

        # Prepare the new method(s) declaration (will be inserted later into code).
        new_method.write(
            '.method public static {method_name}({add_param}{invoke_param}){invoke_return}\n'
            .format(method_name=new_method_name,
                    add_param=add_param,
                    invoke_param=invoke_param,
                    invoke_return=invoke_return))
        new_method.write('    .locals {local_count}\n\n'.format(
            local_count=local_register_count))
        new_method.write(
            '    {invoke_type} {{'.format(invoke_type=invoke_type))
        if is_range_invocation:
            new_method.write('p0 .. p{count}'.format(count=(register_count -
                                                            1)))
        else:
            for index in range(0, register_count):
                new_method.write('p{count}'.format(count=index))
                if index + 1 < register_count:
                    new_method.write(', ')
        new_method.write(
            '}}, {invoke_object}->{invoke_method}({invoke_param}){invoke_return}\n\n'
            .format(invoke_object=invoke_object,
                    invoke_method=invoke_method,
                    invoke_param=invoke_param,
                    invoke_return=invoke_return))
        if move_result_str:
            new_method.write(
                '    {move_result}\n\n'.format(move_result=move_result_str))
        new_method.write(
            '    {return_result}\n'.format(return_result=return_str))
        new_method.write('.end method\n\n')
Beispiel #8
0
    def change_method_call(
        self,
        invoke_type: str,
        invoke_pass: str,
        invoke_object: str,
        invoke_method: str,
        invoke_param: str,
        invoke_return: str,
        class_name: str,
        new_method: StringIO,
        out_file,
    ):

        new_method_name = util.get_random_string(16)

        is_range_invocation = self.is_range(invoke_type)
        is_static_invocation = self.is_static(invoke_type)

        register_list = self.get_registers(invoke_pass)
        if is_range_invocation:
            register_count = self.get_register_range_count(register_list)
        else:
            register_count = len(register_list)

        is_void_value = self.is_void(invoke_return)
        is_wide_value = self.is_wide(invoke_return)
        is_object_value = self.is_object(invoke_return)

        local_register_count = 1
        if is_void_value:
            local_register_count = 0
        if is_wide_value:
            local_register_count = 2

        move_result_str = "move-result v0"
        if is_void_value:
            move_result_str = ""
        if is_wide_value:
            move_result_str = "move-result-wide v0"
        if is_object_value:
            move_result_str = "move-result-object v0"

        return_str = "return v0"
        if is_void_value:
            return_str = "return-void"
        if is_wide_value:
            return_str = "return-wide v0"
        if is_object_value:
            return_str = "return-object v0"

        add_param = "" if is_static_invocation else invoke_object
        new_invoke = "invoke-static/range" if is_range_invocation else "invoke-static"

        # Insert the new method invocation in the smali file.
        out_file.write(
            "\t{invoke_type} {{{invoke_pass}}}, {class_name}->"
            "{method_name}({add_param}{invoke_param}){invoke_return}\n".format(
                invoke_type=new_invoke,
                invoke_pass=invoke_pass,
                class_name=class_name,
                method_name=new_method_name,
                add_param=add_param,
                invoke_param=invoke_param,
                invoke_return=invoke_return,
            ))

        # Prepare the new method(s) declaration (will be inserted later into code).
        new_method.write(
            ".method public static "
            "{method_name}({add_param}{invoke_param}){invoke_return}\n".format(
                method_name=new_method_name,
                add_param=add_param,
                invoke_param=invoke_param,
                invoke_return=invoke_return,
            ))
        new_method.write("    .locals {local_count}\n\n".format(
            local_count=local_register_count))
        new_method.write(
            "    {invoke_type} {{".format(invoke_type=invoke_type))
        if is_range_invocation:
            new_method.write("p0 .. p{count}".format(count=(register_count -
                                                            1)))
        else:
            for index in range(0, register_count):
                new_method.write("p{count}".format(count=index))
                if index + 1 < register_count:
                    new_method.write(", ")
        new_method.write(
            "}}, {invoke_object}->"
            "{invoke_method}({invoke_param}){invoke_return}\n\n".format(
                invoke_object=invoke_object,
                invoke_method=invoke_method,
                invoke_param=invoke_param,
                invoke_return=invoke_return,
            ))
        if move_result_str:
            new_method.write(
                "    {move_result}\n\n".format(move_result=move_result_str))
        new_method.write(
            "    {return_result}\n".format(return_result=return_str))
        new_method.write(".end method\n\n")