Skip to content

Commit

Permalink
Implement an action that can deobfuscate code marked by Themida Spotter
Browse files Browse the repository at this point in the history
  • Loading branch information
ergrelet committed Jul 12, 2024
1 parent 294f47f commit 73e84a1
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 33 deletions.
13 changes: 11 additions & 2 deletions binja_plugin/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
79 changes: 56 additions & 23 deletions binja_plugin/actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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):
Expand All @@ -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!")
57 changes: 49 additions & 8 deletions binja_plugin/common.py
Original file line number Diff line number Diff line change
@@ -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:
Expand All @@ -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:
"""
Expand Down Expand Up @@ -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)

0 comments on commit 73e84a1

Please sign in to comment.