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__)
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)))
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
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
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__)
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__)
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')
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")