Skip to content

Instantly share code, notes, and snippets.

@jamie-34254

jamie-34254/hlil_decoder.py Secret

Created Dec 1, 2020
Embed
What would you like to do?
#!/usr/bin/env python
import os.path
import logging
from termcolor import colored
from io import BytesIO
from binaryninja import *
log_to_stderr(LogLevel.ErrorLog)
print(core_version())
h = "5c9b30d502e2f103f089607ce699520f88154e3d7988a9db801f2a2a4378bf41"
if os.path.exists(f"./testcases/{h}.bndb"):
print("Loading existing DB")
fm = binaryninja.FileMetadata()
db = fm.open_existing_database(f"./testcases/{h}.bndb")
if db is None:
raise Exception("Database doesn't exist")
bv = db.get_view_of_type('PE') or db.get_view_of_type('ELF')
elif os.path.exists(f"./testcases/{h}"):
print("Creating DB")
bv = binaryninja.BinaryViewType.get_view_of_file_with_options(f"./testcases/{h}")
else:
raise Exception("File doesn't exist...")
morestack_noctxt_sym = bv.get_symbols_by_name("runtime.morestack_noctxt") or bv.get_symbols_by_name("_runtime.morestack_noctxt") or bv.get_symbols_by_name("runtime_morestack_noctxt") or bv.get_symbols_by_name("_runtime_morestack_noctxt")
slicebytetostring_sym = bv.get_symbols_by_name("runtime.slicebytetostring") or bv.get_symbols_by_name("_runtime.slicebytetostring") or bv.get_symbols_by_name("runtime_slicebytetostring") or bv.get_symbols_by_name("_runtime_slicebytetostring")
morestack_noctxt = bv.get_function_at(morestack_noctxt_sym[0].address)
slicebytetostring = bv.get_function_at(slicebytetostring_sym[0].address)
deobfuscate_candidates = set()
print("{} functions".format(len(bv.functions)))
for func in bv.functions:
func_callees = func.callees
if (len(func_callees)) == 2:
if morestack_noctxt in func_callees and slicebytetostring in func_callees:
print(func.name)
for ins in func.instructions:
ins_type = ins[0][0]
if ins_type.text != "xor":
continue
src = ins[0][2]
dst = ins[0][4]
if src == "eax" and dst == "eax":
continue
deobfuscate_candidates.add(func)
break
logging.getLogger().setLevel(logging.INFO)
def handle_deref(bv, deref_size, deref_addr):
br = binaryninja.BinaryReader(bv)
br.seek(deref_addr)
data = br.read(deref_size)
return data
for candidate in deobfuscate_candidates:
logging.debug(candidate.name)
instrn = candidate.hlil.instructions
# string based state machine goes brrrrrrrr
state = "start"
assigning_to = None
data = BytesIO()
things = []
for ins in instrn:
logging.debug("{} {} | {}".format(hex(ins.address), colored(ins, "red"), colored(ins.operation, "green")))
if state == "loading" and (ins.operation != binaryninja.HighLevelILOperation.HLIL_DEREF and ins.operation != binaryninja.HighLevelILOperation.HLIL_ASSIGN):
logging.debug("Finished")
state = "start"
data.seek(0)
things.append(data.getvalue())
data = BytesIO()
if state == "start" and ins.operation == binaryninja.HighLevelILOperation.HLIL_VAR_INIT:
if ins.operands[1].value == 0:
continue
state = "loading"
logging.debug("Init")
logging.debug(ins.dest.storage)
if ins.src.operation == binaryninja.HighLevelILOperation.HLIL_CONST:
const_len = ins.src.expr_type.width
logging.debug("Init Const len:{}".format(const_len))
#print(ins.src.value)
#print(ins.src.value.value)
#print(ins.src.expr_type)
#print(const_len)
#print(ins.src.value.value)
#print(type(ins.src.value.value))
#print(ins.src.expr_type.signed)
data.write(ins.src.value.value.to_bytes(const_len, byteorder='little', signed=ins.src.value.value < 0))
elif ins.src.operation == HighLevelILOperation.HLIL_DEREF:
logging.debug("Init Deref")
#print(ins.dest)
deref_size = ins.src.expr_type.width
deref_addr = ins.src.src.value.value
logging.debug("Dereference {} bytes from {}".format(deref_size, hex(deref_addr)))
data.write(handle_deref(bv, deref_size, deref_addr))
else:
raise Exception("unhandled VAR_INIT src load")
elif state == "loading" and ins.operation == binaryninja.HighLevelILOperation.HLIL_DEREF:
deref_size = ins.src.expr_type.return_value.width
deref_addr = ins.src.value.value
logging.debug("HLIL_DEREF Dereference {} bytes from {}".format(deref_size, hex(deref_addr)))
#print(ins.)
data.write(handle_deref(bv, deref_size, deref_addr))
elif state == "loading" and ins.operation == binaryninja.HighLevelILOperation.HLIL_ASSIGN:
# TODO: Catch an assign after a deref, remove this hack and do it properly
if ins.operands[1].value == 0:
continue
logging.debug("Assign")
if ins.dest.operation == binaryninja.HighLevelILOperation.HLIL_STRUCT_FIELD:
offset = ins.dest.offset
logging.debug("OFFSET YO")
data.seek(offset)
if ins.src.operation == binaryninja.HighLevelILOperation.HLIL_CONST:
data.write(ins.src.value.value.to_bytes(const_len, byteorder='little', signed=ins.src.value.value < 0))
elif ins.src.operation == HighLevelILOperation.HLIL_DEREF:
deref_size = ins.src.expr_type.width
deref_addr = ins.src.src.value.value
logging.debug("HLIL_ASSIGN Dereference {} bytes from {}".format(deref_size, hex(deref_addr)))
data.write(handle_deref(bv, deref_size, deref_addr))
else:
raise Exception("unhandled ASSIGN load")
# We might have been writing to an offset so let's jump to the end to counter-act that
data.seek(data.getbuffer().nbytes)
if len(things) != 2:
logging.info("{} = {}".format(colored(candidate.name, "yellow"), colored("failed", "red")))
else:
thing1 = things[0]
thing2 = things[1]
logging.info("{} = {}".format(colored(candidate.name, "yellow"), colored(bytes(a ^ b for (a, b) in zip(thing1, thing2)), "white")))
logging.info("----------------------------")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment