Skip to content

Commit

Permalink
WIP: Implement a Binary Ninja plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
ergrelet committed Apr 24, 2024
1 parent 600b81e commit c36200a
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 10 deletions.
64 changes: 64 additions & 0 deletions binja_plugin/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
from binaryninja import *

from miasm.analysis.binary import Container
from miasm.analysis.machine import Machine
from miasm.core.locationdb import LocationDB

from themida_unmutate.symbolic_execution import disassemble_and_simplify_functions
from themida_unmutate.miasm_utils import MiasmContext

SUPPORTED_ARCHS = ["x86_64"]


def deobfuscate_at_address(bv: BinaryView, address: int) -> None:
arch = str(bv.platform.arch)
if arch not in SUPPORTED_ARCHS:
return

binary_data = get_binary_data(bv)
miasm_ctx = create_miasm_context(arch, binary_data, bv.original_base)

print("Resolving mutated's functions' addresses...")
# mutated_func_addrs = unwrap_functions(args.protected_binary, [address])
mutated_func_addrs = [address]

# Disassemble mutated functions and simplify them
print("Deobfuscating mutated functions...")
simplified_func_asmcfgs = disassemble_and_simplify_functions(miasm_ctx, mutated_func_addrs)

print(simplified_func_asmcfgs)

# bv.update_analysis()


def get_binary_data(bv: BinaryView) -> bytearray:
# Sort sections by start address
sections = list(bv.sections.values())
sorted_section = sorted(sections, key=lambda s: s.start)

br = BinaryReader(bv)
last_section_address = bv.original_base
exe_data = bytearray()
for section in sorted_section:
# Pad with zeroes
padding_size = section.start - last_section_address
exe_data += b"\x00" * padding_size
exe_data += br.read(section.length, section.start)
last_section_address = section.start + section.length

return exe_data


def create_miasm_context(arch: str, binary_data: bytearray, binary_base_address: int) -> MiasmContext:
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)
mdis = machine.dis_engine(container.bin_stream, loc_db=loc_db)
lifter = machine.lifter(loc_db)

return MiasmContext(loc_db, container, machine, mdis, lifter)


PluginCommand.register_for_address("themida-unmutate", "Deobfuscate mutated code from this address",
deobfuscate_at_address)
24 changes: 24 additions & 0 deletions plugin.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"pluginmetadataversion": 2,
"name": "themida-unmutate-bn",
"type": ["core"],
"api": ["python3"],
"description": "Static deobfuscator for Themida's mutation-based obfuscation, powered by Miasm.",
"longdescription": "",
"license": {
"name": "GPL-3.0-or-later",
"text": ""
},
"platforms": ["Darwin", "Linux", "Windows"],
"installinstructions": {
"Darwin": "",
"Linux": "",
"Windows": ""
},
"dependencies": {
"pip": ["themida-unmutate"]
},
"version": "0.1.0",
"author": "Erwan Grelet",
"minimumbinaryninjaversion": 3164
}
2 changes: 1 addition & 1 deletion themida_unmutate/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def entry_point() -> None:
setup_logger(args.verbose)

# Setup disassembler and lifter
miasm_ctx = MiasmContext(args.protected_binary)
miasm_ctx = MiasmContext.from_binary_file(args.protected_binary)

# Resolve mutated functions' addresses if needed
protected_func_addrs = list(map(lambda addr: int(addr, 0), args.addresses))
Expand Down
20 changes: 12 additions & 8 deletions themida_unmutate/miasm_utils.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from dataclasses import dataclass
from typing import Self

import miasm.expression.expression as m2_expr

Expand All @@ -20,18 +21,21 @@ class MiasmContext:
mdis: disasmEngine
lifter: Lifter

def __init__(self, target_binary_path: str) -> None:
@classmethod
def from_binary_file(cls, target_binary_path: str) -> Self:
"""
Initialize our Miasm context, targeted at x86_64 binaries.
Initialize our Miasm context from a binary file.
"""
self.loc_db = LocationDB()
loc_db = LocationDB()
with open(target_binary_path, 'rb') as target_binary:
self.container = Container.from_stream(target_binary, self.loc_db)
self.machine = Machine(self.container.arch)
assert self.machine.dis_engine is not None
container = Container.from_stream(target_binary, loc_db)
machine = Machine(container.arch)
assert machine.dis_engine is not None

self.mdis = self.machine.dis_engine(self.container.bin_stream, loc_db=self.loc_db)
self.lifter = self.machine.lifter(self.loc_db)
mdis = machine.dis_engine(container.bin_stream, loc_db=loc_db)
lifter = machine.lifter(loc_db)

return cls(loc_db, container, machine, mdis, lifter)

@property
def arch(self) -> str:
Expand Down
2 changes: 1 addition & 1 deletion themida_unmutate/unwrapping.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

def unwrap_function(target_bin_path: str, target_addr: int) -> int:
# Setup disassembler and lifter
miasm_ctx = MiasmContext(target_bin_path)
miasm_ctx = MiasmContext.from_binary_file(target_bin_path)

# Disassemble trampoline
miasm_ctx.mdis.follow_call = True
Expand Down

0 comments on commit c36200a

Please sign in to comment.