diff --git a/binja_plugin/__init__.py b/binja_plugin/__init__.py index c9bae5f..b2b1d95 100644 --- a/binja_plugin/__init__.py +++ b/binja_plugin/__init__.py @@ -9,9 +9,18 @@ PluginCommand.register_for_address, actions.deobfuscate_at_address, ), + ( + f"{plugin.NAME}\\Deobfuscate mutated code from Themida Spotter tags", + "Deobfuscate mutated code located marked by Themida Spotter tags", + PluginCommand.register, + actions.deobfuscate_themida_spotter_tags, + ), ] def plugin_init(): - for (command_name, command_description, command_registrator, command_action) in plugin_commands: - command_registrator(name=command_name, description=command_description, action=command_action) + for (command_name, command_description, command_registrator, + command_action) in plugin_commands: + command_registrator(name=command_name, + description=command_description, + action=command_action) diff --git a/binja_plugin/actions.py b/binja_plugin/actions.py index 7b999d8..c995650 100644 --- a/binja_plugin/actions.py +++ b/binja_plugin/actions.py @@ -3,8 +3,6 @@ from binaryninja import BinaryView # type:ignore from binaryninja.log import Logger # type:ignore from binaryninja.plugin import BackgroundTaskThread # type:ignore -from themida_unmutate.unwrapping import unwrap_functions -from themida_unmutate.symbolic_execution import disassemble_and_simplify_functions from . import common, plugin @@ -17,6 +15,10 @@ def deobfuscate_at_address(bv: BinaryView, address: int) -> None: DeobfuscateCodeAtAddressTask(bv=bv, address=address).start() +def deobfuscate_themida_spotter_tags(bv: BinaryView) -> None: + DeobfuscateCodeAtThemidaSpotterTagsTask(bv=bv).start() + + class DeobfuscateCodeAtAddressTask(BackgroundTaskThread): def __init__(self, bv: BinaryView, address: int): @@ -29,36 +31,67 @@ def __init__(self, bv: BinaryView, address: int): def run(self: Self) -> None: if self.bv.arch is None: - logger.log_error("Could not get architecture of current binary view") + logger.log_error( + "Could not get architecture of current binary view") return arch = str(self.bv.platform.arch) if arch not in SUPPORTED_ARCHS: - logger.log_error("Current binary view's architecture isn't supported") + logger.log_error( + "Current binary view's architecture isn't supported by the plugin" + ) return - logger.log_info(f"Deobfuscating code at 0x{self.address:x}") + progress_msg = f"Deobfuscating code at 0x{self.address:x}" + logger.log_info(progress_msg) + self.progress = f"({plugin.NAME}) {progress_msg}" + + # Deobfuscate the address pointed to by the user protected_func_addrs = [self.address] - binary_data = common.get_binary_data(self.bv) - miasm_ctx = common.create_miasm_context(arch, self.bv.original_base, binary_data) + common.deobfuscate_addresses(self.bv, arch, protected_func_addrs) + logger.log_info(f"Successfully simplified code at 0x{self.address:x}!") - logger.log_info("Resolving mutated's function' address...") - mutated_func_addrs = unwrap_functions(miasm_ctx, protected_func_addrs) - # Disassemble mutated functions and simplify them - logger.log_info("Deobfuscating mutated function...") - simplified_func_asmcfgs = disassemble_and_simplify_functions(miasm_ctx, mutated_func_addrs) +class DeobfuscateCodeAtThemidaSpotterTagsTask(BackgroundTaskThread): - # Map protected functions' addresses to their corresponding simplified `AsmCFG` - func_addr_to_simplified_cfg = { - protected_func_addrs[i]: asm_cfg - for i, asm_cfg in enumerate(simplified_func_asmcfgs) - } + THEMIDA_SPOTTER_TAG_TYPE = "Themida's obfuscated code entries" - # Rewrite the protected binary with the simplified function - logger.log_info("Patching binary file...") - common.rebuild_simplified_binary(miasm_ctx, func_addr_to_simplified_cfg, self.bv) + def __init__(self, bv: BinaryView): + super().__init__( + initial_progress_text= + "Deobfuscating code marked by Themida Spotter", + can_cancel=False, + ) + self.bv = bv - # Relaunch analysis to take our changes into account - self.bv.update_analysis() - logger.log_info(f"Successfully simplified code at 0x{self.address:x}!") + def run(self: Self) -> None: + if self.bv.arch is None: + logger.log_error( + "Could not get architecture of current binary view") + return + + arch = str(self.bv.platform.arch) + if arch not in SUPPORTED_ARCHS: + logger.log_error( + "Current binary view's architecture isn't supported by the plugin" + ) + return + + progress_msg = "Looking for Themida Spotter tags..." + logger.log_info(progress_msg) + self.progress = f"({plugin.NAME}) {progress_msg}" + + mutated_code_locations: list[int] = [] + # List Themida Spotter tags + for addr, tag in self.bv.get_tags(): + # Look for Themida Spotter's mutated code entry tags + if tag.type.name == self.THEMIDA_SPOTTER_TAG_TYPE and \ + "Mutated" in tag.data: + mutated_code_locations.append(addr) + + # Deobfuscate all addresses marked by Themida Spotter + logger.log_info( + f"Found {len(mutated_code_locations)} mutated code entries") + self.progress = f"({plugin.NAME}) Deobfuscating mutated functions" + common.deobfuscate_addresses(self.bv, arch, mutated_code_locations) + logger.log_info("Successfully simplified code!") diff --git a/binja_plugin/common.py b/binja_plugin/common.py index 95423cd..7ab3a53 100644 --- a/binja_plugin/common.py +++ b/binja_plugin/common.py @@ -1,10 +1,17 @@ from binaryninja import BinaryView, BinaryReader, BinaryWriter # type:ignore +from binaryninja.log import Logger # type:ignore from miasm.analysis.binary import Container from miasm.analysis.machine import Machine from miasm.core.asmblock import AsmCFG, asm_resolve_final from miasm.core.locationdb import LocationDB from themida_unmutate.miasm_utils import MiasmContext, MiasmFunctionInterval, generate_code_redirect_patch +from themida_unmutate.unwrapping import unwrap_functions +from themida_unmutate.symbolic_execution import disassemble_and_simplify_functions + +from . import plugin + +logger = Logger(session_id=0, logger_name=plugin.NAME) def get_binary_data(bv: BinaryView) -> bytearray: @@ -29,23 +36,54 @@ def get_binary_data(bv: BinaryView) -> bytearray: return exe_data -def create_miasm_context(arch: str, binary_base_address: int, binary_data: bytearray) -> MiasmContext: +def create_miasm_context(arch: str, binary_base_address: int, + binary_data: bytearray) -> MiasmContext: """ Create `MiasmContext` from a `bytearray`, given the architecture and base address. """ loc_db = LocationDB() machine = Machine(arch) assert machine.dis_engine is not None - container = Container.from_string(binary_data, loc_db, addr=binary_base_address) + container = Container.from_string(binary_data, + loc_db, + addr=binary_base_address) mdis = machine.dis_engine(container.bin_stream, loc_db=loc_db) lifter = machine.lifter(loc_db) return MiasmContext(loc_db, container, machine, mdis, lifter) +def deobfuscate_addresses(bv: BinaryView, arch: str, + mutated_code_addresses: list[int]) -> None: + binary_data = get_binary_data(bv) + miasm_ctx = create_miasm_context(arch, bv.original_base, binary_data) + + logger.log_info("Resolving mutated function(s)' address(es)...") + mutated_func_addrs = unwrap_functions(miasm_ctx, mutated_code_addresses) + + # Disassemble mutated functions and simplify them + logger.log_info("Deobfuscating mutated function(s)...") + simplified_func_asmcfgs = disassemble_and_simplify_functions( + miasm_ctx, mutated_func_addrs) + + # Map protected functions' addresses to their corresponding simplified `AsmCFG` + func_addr_to_simplified_cfg = { + mutated_code_addresses[i]: asm_cfg + for i, asm_cfg in enumerate(simplified_func_asmcfgs) + } + + # Rewrite the protected binary with the simplified function + logger.log_info("Patching binary file...") + rebuild_simplified_binary(miasm_ctx, func_addr_to_simplified_cfg, bv) + + # Relaunch analysis to take our changes into account + bv.update_analysis() + + def rebuild_simplified_binary( miasm_ctx: MiasmContext, - func_addr_to_simplified_cfg: dict[int, tuple[AsmCFG, MiasmFunctionInterval]], + func_addr_to_simplified_cfg: dict[int, tuple[AsmCFG, + MiasmFunctionInterval]], bv: BinaryView, ) -> None: """ @@ -75,19 +113,22 @@ def rebuild_simplified_binary( miasm_ctx.loc_db.set_location_offset(head, target_addr) # Generate the simplified machine code - new_section_patches = asm_resolve_final(miasm_ctx.mdis.arch, - simplified_asmcfg, - dst_interval=orignal_asmcfg_interval) + new_section_patches = asm_resolve_final( + miasm_ctx.mdis.arch, + simplified_asmcfg, + dst_interval=orignal_asmcfg_interval) # Apply patches for address, data in new_section_patches.items(): bw.write(bytes(data), address) # Associate original addr to simplified addr - original_to_simplified[protected_func_addr] = min(new_section_patches.keys()) + original_to_simplified[protected_func_addr] = min( + new_section_patches.keys()) # Redirect functions to their simplified versions for target_addr in func_addr_to_simplified_cfg.keys(): simplified_func_addr = original_to_simplified[target_addr] - address, data = generate_code_redirect_patch(miasm_ctx, target_addr, simplified_func_addr) + address, data = generate_code_redirect_patch(miasm_ctx, target_addr, + simplified_func_addr) bw.write(data, address)