Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- from binaryninja.architecture import Architecture, ArchitectureHook
- from dataclasses import dataclass
- import struct
- @dataclass
- class ExceptionStruct:
- rva: int
- case: int
- loc1: int
- loc2: int
- # Build breakpoint EIP adjustment table
- #######################################
- def get_struct_data(addr) -> ExceptionStruct:
- rva, case, loc1, loc2 = struct.unpack('<LBll', bv.read(addr, 0xd))
- return ExceptionStruct(rva, case, loc1, loc2)
- def create_breakpoint_lut():
- breakpoint_lut = {}
- STRUCT_RVA = 0x1280a0
- BASE_ADDRESS = 0x60000
- offset = 0
- while True:
- x = get_struct_data(BASE_ADDRESS + STRUCT_RVA + offset)
- if x.rva == 0: # Continue scanning for structs until we encounter null data
- break
- breakpoint_lut[BASE_ADDRESS + x.rva] = x
- offset += 0xd
- return breakpoint_lut
- # Helper functions for our case functions.
- ##########################################
- def get_label(il, address):
- label = il.get_label_for_address(Architecture['x86'], address)
- if label == None:
- il.add_label_for_address(Architecture['x86'], address)
- label = il.get_label_for_address(Architecture['x86'], address)
- assert label != None
- return label
- def create_branch(il, condition, t_addr, f_addr):
- t = get_label(il, t_addr)
- f = get_label(il, f_addr)
- il.append(il.if_expr(condition, t, f))
- # Code responsible for creating the appropriate branch type for each int3
- #########################################################################
- def case0(il, t_addr, f_addr):
- flag = il.flag_group('ne')
- create_branch(il, flag, t_addr, f_addr)
- def case1(il, t_addr, f_addr):
- flag = il.flag_group('e')
- create_branch(il, flag, t_addr, f_addr)
- def case2(il, t_addr, f_addr):
- flag = il.flag_group('ge')
- create_branch(il, flag, t_addr, f_addr)
- def case3(il, t_addr, f_addr):
- flag = il.flag_group('lt')
- create_branch(il, flag, t_addr, f_addr)
- def case4(il, t_addr, f_addr):
- flag = il.flag_group('gt')
- create_branch(il, flag, t_addr, f_addr)
- def case5(il, t_addr, f_addr):
- flag = il.flag_group('le')
- create_branch(il, flag, t_addr, f_addr)
- def case6(il, t_addr, f_addr):
- flag = il.flag_condition(LowLevelILFlagCondition.LLFC_SGE)
- create_branch(il, flag, t_addr, f_addr)
- def case7(il, t_addr, f_addr):
- flag = il.flag_condition(LowLevelILFlagCondition.LLFC_SLE)
- create_branch(il, flag, f_addr, t_addr) # Reserved order of branch addresses
- def case8(il, t_addr, f_addr):
- flag = il.flag_condition(LowLevelILFlagCondition.LLFC_SGT)
- create_branch(il, flag, t_addr, f_addr)
- def case9(il, t_addr, f_addr):
- flag = il.flag_condition(LowLevelILFlagCondition.LLFC_SLE)
- create_branch(il, flag, t_addr, f_addr)
- def casea(il, t_addr, f_addr):
- flag = il.flag_group('pe')
- create_branch(il, flag, t_addr, f_addr)
- def caseb(il, t_addr, f_addr):
- flag = il.flag_group('po')
- create_branch(il, flag, t_addr, f_addr)
- def cased(il, t_addr, f_addr):
- flag = il.flag_condition(LowLevelILFlagCondition.LLFC_POS)
- create_branch(il, flag, t_addr, f_addr)
- def casee(il, t_addr, f_addr):
- flag = il.flag_condition(LowLevelILFlagCondition.LLFC_NEG)
- create_branch(il, flag, t_addr, f_addr)
- def case11(il, t_addr, f_addr):
- target = get_label(il, f_addr)
- il.append(il.goto(target))
- exception_handler_lut = {
- 0x0: case0,
- 0x1: case1,
- 0x2: case2,
- 0x3: case3,
- 0x4: case4,
- 0x5: case5,
- 0x6: case6,
- 0x7: case7,
- 0x8: case8,
- 0x9: case9,
- 0xa: casea,
- 0xb: caseb,
- 0xd: cased,
- 0xe: casee,
- 0x11: case11
- }
- # Create and insert our IL hook
- ###############################
- class ILLifterHook(ArchitectureHook):
- lut: dict[int, ExceptionStruct]
- def __init__(self, base_arch, lut):
- super(ILLifterHook, self).__init__(base_arch)
- self.lut = lut
- def get_instruction_info(self, data, addr):
- # This callback is responsible for informing BN on what branches an instruction has.
- # We need to hook this so that we can inform BN that some int3s have a true/false branch.
- # If we don't hook this, binary ninja will not include the branch locations within the functions' address space.
- # If the address isn't within the function, we wont be able to acquire address labels for if_expr IL's.
- if addr in self.lut and bv.read(addr, 1) == b'\xcc':
- cf_data = self.lut[addr]
- info = InstructionInfo()
- info.length = 1
- if cf_data.case == 0x11:
- info.add_branch(enums.BranchType.UnconditionalBranch, addr + cf_data.loc2)
- else:
- info.add_branch(enums.BranchType.TrueBranch, addr + cf_data.loc1)
- info.add_branch(enums.BranchType.FalseBranch, addr + cf_data.loc2)
- return info
- else:
- return super(ILLifterHook, self).get_instruction_info(data, addr)
- def get_instruction_low_level_il(self, data, addr, il):
- # This callback is responsible for inserting LLIL into the IL tree/graph.
- # We'll process the int3s as if_expr's.
- if data[0] == 0xCC and addr in self.lut:
- control_flow = self.lut[addr]
- if control_flow.case in exception_handler_lut:
- exception_handler_lut[control_flow.case](il, addr + control_flow.loc1, addr + control_flow.loc2)
- else:
- print(f'Unhandled @ {hex(addr)} for case: {control_flow.case}')
- il.append(il.unimplemented())
- return 1
- else:
- return super(ILLifterHook, self).get_instruction_low_level_il(data, addr, il)
- hook = ILLifterHook(Architecture['x86'], create_breakpoint_lut())
- hook.register()
Advertisement
Add Comment
Please, Sign In to add comment