def return_from_seh(myjit): "Handle return after a call to fake seh handler" # Get current context context_address = upck32(myjit.vm.get_mem(myjit.cpu.ESP + 0x8, 4)) log.info('Context address: %x', context_address) myjit.cpu.ESP = upck32(myjit.vm.get_mem(context_address + 0xc4, 4)) log.info('New esp: %x', myjit.cpu.ESP) # Rebuild SEH old_seh = upck32(myjit.vm.get_mem(tib_address, 4)) new_seh = upck32(myjit.vm.get_mem(old_seh, 4)) log.info('Old seh: %x New seh: %x', old_seh, new_seh) myjit.vm.set_mem(tib_address, pck32(new_seh)) dump_seh(myjit) if myjit.cpu.EAX == 0x0: # ExceptionContinueExecution ctxt_ptr = context_address log.info('Seh continues Context: %x', ctxt_ptr) # Get registers changes ctxt_str = myjit.vm.get_mem(ctxt_ptr, 0x2cc) ctxt2regs(ctxt_str, myjit) myjit.pc = myjit.cpu.EIP log.info('Context::Eip: %x', myjit.pc) elif myjit.cpu.EAX == -1: raise NotImplementedError("-> seh try to go to the next handler") elif myjit.cpu.EAX == 1: # ExceptionContinueSearch raise NotImplementedError("-> seh, gameover")
def return_from_seh(jitter): """Handle the return from an exception handler @jitter: jitter instance""" # Get current context context_address = upck32(jitter.vm.get_mem(jitter.cpu.ESP + 0x8, 4)) log.info('Context address: %x', context_address) jitter.cpu.ESP = upck32(jitter.vm.get_mem(context_address + 0xc4, 4)) log.info('New esp: %x', jitter.cpu.ESP) # Rebuild SEH old_seh = upck32(jitter.vm.get_mem(tib_address, 4)) new_seh = upck32(jitter.vm.get_mem(old_seh, 4)) log.info('Old seh: %x New seh: %x', old_seh, new_seh) jitter.vm.set_mem(tib_address, pck32(new_seh)) dump_seh(jitter) if jitter.cpu.EAX == 0x0: # ExceptionContinueExecution ctxt_ptr = context_address log.info('Seh continues Context: %x', ctxt_ptr) # Get registers changes ctxt_str = jitter.vm.get_mem(ctxt_ptr, 0x2cc) ctxt2regs(ctxt_str, jitter) jitter.pc = jitter.cpu.EIP log.info('Context::Eip: %x', jitter.pc) elif jitter.cpu.EAX == -1: raise NotImplementedError("-> seh try to go to the next handler") elif jitter.cpu.EAX == 1: # ExceptionContinueSearch raise NotImplementedError("-> seh, gameover")
def xxx___printf_chk(jitter): """Tiny implementation of printf_chk""" global nb_tests ret_ad, args = jitter.func_args_cdecl(["out", "format"]) if args.out != 1: raise RuntimeError("Not implemented") fmt = jitter.get_str_ansi(args.format) # Manage llx fmt = fmt.replace("llx", "lx") fmt = fmt.replace("%016lx", "%016z") fmt_a = parse_fmt(fmt) esp = jitter.cpu.ESP args = [] i = 0 for x in fmt_a: a = upck32(jitter.vm.get_mem(esp + 8 + 4*i, 4)) if x == "s": a = jitter.get_str_ansi(a) elif x.lower() in ("x", 'd'): pass elif x.lower() in ("f", "l"): a2 = upck32(jitter.vm.get_mem(esp + 8 + 4*(i+1), 4)) a = struct.unpack("d", struct.pack("Q", a2 << 32 | a))[0] i += 1 elif x.lower() == 'z': a2 = upck32(jitter.vm.get_mem(esp + 8 + 4*(i+1), 4)) a = a2 << 32 | a i += 1 else: raise RuntimeError("Not implemented format") args.append(a) i += 1 fmt = fmt.replace("%016z", "%016lx") output = fmt%(tuple(args)) # NaN bad repr in Python output = output.replace("nan", "-nan") if "\n" not in output: raise RuntimeError("Format must end with a \\n") # Check with expected result line = expected.next() if output != line: print "Expected:", line print "Obtained:", output raise RuntimeError("Bad semantic") sys.stdout.write("[%d] %s" % (nb_tests, output)) nb_tests += 1 jitter.func_ret_cdecl(ret_ad, 0)
def return_from_seh(jitter): """Handle the return from an exception handler @jitter: jitter instance""" # Get object addresses seh_address = upck32(jitter.vm.get_mem(jitter.cpu.ESP + 0x4, 4)) context_address = upck32(jitter.vm.get_mem(jitter.cpu.ESP + 0x8, 4)) # Get registers changes log.info('Context address: %x', context_address) status = jitter.cpu.EAX ctxt2regs(jitter, context_address) # Rebuild SEH (remove fake SEH) tib = NT_TIB(jitter.vm, tib_address) seh = tib.ExceptionList.deref log.info('Old seh: %x New seh: %x', seh.get_addr(), seh.Next.val) tib.ExceptionList.val = seh.Next.val dump_seh(jitter) # Handle returned values if status == 0x0: # ExceptionContinueExecution log.info('SEH continue') jitter.pc = jitter.cpu.EIP log.info('Context::Eip: %x', jitter.pc) elif status == 1: # ExceptionContinueSearch log.info("Delegate to the next SEH handler") # exception_base_address: context_address - 0xfc # -> exception_record_address: exception_base_address + 0xe8 exception_record = EXCEPTION_RECORD(jitter.vm, context_address - 0xfc + 0xe8) pc = fake_seh_handler(jitter, exception_record.ExceptionCode, seh_address) jitter.pc = pc else: # https://msdn.microsoft.com/en-us/library/aa260344%28v=vs.60%29.aspx # But the type _EXCEPTION_DISPOSITION may take 2 others values: # - ExceptionNestedException = 2 # - ExceptionCollidedUnwind = 3 raise ValueError("Valid values are ExceptionContinueExecution and " "ExceptionContinueSearch") # Jitter's breakpoint compliant return True
def arm_guess_jump_table( mnemo, attrib, pool_bin, cur_bloc, offsets_to_dis, symbol_pool): ira = get_ira(mnemo, attrib) jra = ExprId('jra') jrb = ExprId('jrb') sp = AsmSymbolPool() ir_arch = ira(sp) ir_arch.add_bloc(cur_bloc) ir_blocks = ir_arch.blocks.values() for irblock in ir_blocks: # print 'X'*40 # print irblock pc_val = None # lr_val = None for exprs in irblock.irs: for e in exprs: if e.dst == ir_arch.pc: pc_val = e.src # if e.dst == mnemo.regs.LR: # lr_val = e.src if pc_val is None: continue if not isinstance(pc_val, ExprMem): continue assert(pc_val.size == 32) print pc_val ad = pc_val.arg ad = expr_simp(ad) print ad res = match_expr(ad, jra + jrb, set([jra, jrb])) if res is False: raise NotImplementedError('not fully functional') print res if not isinstance(res[jrb], ExprInt): raise NotImplementedError('not fully functional') base_ad = int(res[jrb]) print base_ad addrs = set() i = -1 max_table_entry = 10000 max_diff_addr = 0x100000 # heuristic while i < max_table_entry: i += 1 try: ad = upck32(pool_bin.getbytes(base_ad + 4 * i, 4)) except: break if abs(ad - base_ad) > max_diff_addr: break addrs.add(ad) print [hex(x) for x in addrs]
def return_from_seh(myjit): "Handle return after a call to fake seh handler" # Get current context myjit.cpu.ESP = upck32(myjit.vm.get_mem(context_address + 0xc4, 4)) logging.info('-> new esp: %x', myjit.cpu.ESP) # Rebuild SEH old_seh = upck32(myjit.vm.get_mem(tib_address, 4)) new_seh = upck32(myjit.vm.get_mem(old_seh, 4)) logging.info('-> old seh: %x', old_seh) logging.info('-> new seh: %x', new_seh) myjit.vm.set_mem(tib_address, pck32(new_seh)) dump_seh(myjit) # Release SEH free_seh_place(old_seh) if myjit.cpu.EAX == 0x0: # ExceptionContinueExecution print '-> seh continues' ctxt_ptr = context_address print '-> context:', hex(ctxt_ptr) # Get registers changes ctxt_str = myjit.vm.get_mem(ctxt_ptr, 0x2cc) regs = ctxt2regs(ctxt_str) myjit.pc = regs["EIP"] for reg_name, reg_value in regs.items(): setattr(myjit.cpu, reg_name, reg_value) logging.info('-> context::Eip: %x', myjit.pc) elif myjit.cpu.EAX == -1: raise NotImplementedError("-> seh try to go to the next handler") elif myjit.cpu.EAX == 1: # ExceptionContinueSearch raise NotImplementedError("-> seh, gameover")
def dump_seh(myjit): log.info('Dump_seh. Tib_address: %x', tib_address) cur_seh_ptr = upck32(myjit.vm.get_mem(tib_address, 4)) indent = 1 loop = 0 while True: if loop > MAX_SEH: log.warn("Too many seh, quit") return prev_seh, eh = struct.unpack('II', myjit.vm.get_mem(cur_seh_ptr, 8)) log.info('\t' * indent + 'seh_ptr: %x { prev_seh: %x eh %x }', cur_seh_ptr, prev_seh, eh) if prev_seh in [0xFFFFFFFF, 0]: break cur_seh_ptr = prev_seh indent += 1 loop += 1
def dump_seh(myjit): print 'dump_seh:' print '-> tib_address:', hex(tib_address) cur_seh_ptr = upck32(myjit.vm.get_mem(tib_address, 4)) indent = 1 loop = 0 while True: if loop > 5: print "too many seh, quit" return prev_seh, eh = struct.unpack('II', myjit.vm.get_mem(cur_seh_ptr, 8)) print '\t' * indent + 'seh_ptr:', hex(cur_seh_ptr), print ' -> { prev_seh:', hex(prev_seh), 'eh:', hex(eh), '}' if prev_seh in [0xFFFFFFFF, 0]: break cur_seh_ptr = prev_seh indent += 1 loop += 1
def dump_seh(jitter): """ Walk and dump the SEH entries @jitter: jitter instance """ log.info('Dump_seh. Tib_address: %x', tib_address) cur_seh_ptr = upck32(jitter.vm.get_mem(tib_address, 4)) indent = 1 loop = 0 while True: if loop > MAX_SEH: log.warn("Too many seh, quit") return prev_seh, eh = struct.unpack('II', jitter.vm.get_mem(cur_seh_ptr, 8)) log.info('\t' * indent + 'seh_ptr: %x { prev_seh: %x eh %x }', cur_seh_ptr, prev_seh, eh) if prev_seh in [0xFFFFFFFF, 0]: break cur_seh_ptr = prev_seh indent += 1 loop += 1
def ctxt2regs(ctxt): ctxt = ctxt[:] regs = {} # regs['ctxtsflags'] = upck32(ctxt[:4]) ctxt = ctxt[4:] for i in xrange(8): if i in [4, 5]: continue # regs['dr%d'%i] = upck32(ctxt[:4]) ctxt = ctxt[4:] ctxt = ctxt[112:] # skip float # regs['seg_gs'] = upck32(ctxt[:4]) ctxt = ctxt[4:] # regs['seg_fs'] = upck32(ctxt[:4]) ctxt = ctxt[4:] # regs['seg_es'] = upck32(ctxt[:4]) ctxt = ctxt[4:] # regs['seg_ds'] = upck32(ctxt[:4]) ctxt = ctxt[4:] regs['EDI'], regs['ESI'], regs['EBX'], regs['EDX'], regs['ECX'], regs[ 'EAX'], regs['EBP'], regs['EIP'] = struct.unpack('I' * 8, ctxt[:4 * 8]) ctxt = ctxt[4 * 8:] # regs['seg_cs'] = upck32(ctxt[:4]) ctxt = ctxt[4:] # regs['eflag'] = upck32(ctxt[:4]) ctxt = ctxt[4:] regs['ESP'] = upck32(ctxt[:4]) ctxt = ctxt[4:] for a, b in regs.items(): print a, hex(b) # skip extended return regs
def fake_seh_handler(jitter, except_code): """ Create an exception context @jitter: jitter instance @except_code: x86 exception code """ global seh_count, context_address regs = jitter.cpu.get_gpreg() log.warning('Exception at %x %r', jitter.cpu.EIP, seh_count) seh_count += 1 # Help lambda p = lambda s: struct.pack('I', s) # Forge a CONTEXT ctxt = regs2ctxt(jitter) # Get current seh (fs:[0]) seh_ptr = upck32(jitter.vm.get_mem(tib_address, 4)) # Retrieve seh fields old_seh, eh, safe_place = struct.unpack( 'III', jitter.vm.get_mem(seh_ptr, 0xc)) # Get space on stack for exception handling jitter.cpu.ESP -= 0x3c8 exception_base_address = jitter.cpu.ESP exception_record_address = exception_base_address + 0xe8 context_address = exception_base_address + 0xfc fake_seh_address = exception_base_address + 0x14 log.info('seh_ptr %x { old_seh %x eh %x safe_place %x} ctx_addr %x', seh_ptr, old_seh, eh, safe_place, context_address) # Write context jitter.vm.set_mem(context_address, ctxt) # Write exception_record """ #http://msdn.microsoft.com/en-us/library/aa363082(v=vs.85).aspx typedef struct _EXCEPTION_RECORD { DWORD ExceptionCode; DWORD ExceptionFlags; struct _EXCEPTION_RECORD *ExceptionRecord; PVOID ExceptionAddress; DWORD NumberParameters; ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS]; } EXCEPTION_RECORD, *PEXCEPTION_RECORD; """ jitter.vm.set_mem(exception_record_address, pck32(except_code) + pck32(0) + pck32(0) + pck32(jitter.cpu.EIP) + pck32(0)) # Prepare the stack jitter.push_uint32_t(context_address) # Context jitter.push_uint32_t(seh_ptr) # SEH jitter.push_uint32_t(exception_record_address) # ExceptRecords jitter.push_uint32_t(return_from_exception) # Ret address # Set fake new current seh for exception log.info("Fake seh ad %x", fake_seh_address) jitter.vm.set_mem(fake_seh_address, pck32(seh_ptr) + pck32( 0xaaaaaaaa) + pck32(0xaaaaaabb) + pck32(0xaaaaaacc)) jitter.vm.set_mem(tib_address, pck32(fake_seh_address)) dump_seh(jitter) log.info('Jumping at %x', eh) jitter.vm.set_exception(0) jitter.cpu.set_exception(0) # XXX set ebx to nul? jitter.cpu.EBX = 0 return eh
def ctxt2regs(ctxt, jitter): """ Restore x86_32 registers from an exception context @ctxt: the serialized context @jitter: jitload instance """ ctxt = ctxt[:] # ContextFlags ctxt = ctxt[4:] # DRX XXX TODO ctxt = ctxt[4 * 6:] # Float context XXX TODO ctxt = ctxt[112:] # gs jitter.cpu.GS = upck32(ctxt[:4]) ctxt = ctxt[4:] # fs jitter.cpu.FS = upck32(ctxt[:4]) ctxt = ctxt[4:] # es jitter.cpu.ES = upck32(ctxt[:4]) ctxt = ctxt[4:] # ds jitter.cpu.DS = upck32(ctxt[:4]) ctxt = ctxt[4:] # Gpregs jitter.cpu.EDI = upck32(ctxt[:4]) ctxt = ctxt[4:] jitter.cpu.ESI = upck32(ctxt[:4]) ctxt = ctxt[4:] jitter.cpu.EBX = upck32(ctxt[:4]) ctxt = ctxt[4:] jitter.cpu.EDX = upck32(ctxt[:4]) ctxt = ctxt[4:] jitter.cpu.ECX = upck32(ctxt[:4]) ctxt = ctxt[4:] jitter.cpu.EAX = upck32(ctxt[:4]) ctxt = ctxt[4:] jitter.cpu.EBP = upck32(ctxt[:4]) ctxt = ctxt[4:] jitter.cpu.EIP = upck32(ctxt[:4]) ctxt = ctxt[4:] # CS jitter.cpu.CS = upck32(ctxt[:4]) ctxt = ctxt[4:] # Eflag XXX TODO ctxt = ctxt[4:] # ESP jitter.cpu.ESP = upck32(ctxt[:4]) ctxt = ctxt[4:]
def pop_uint32_t(self): value = upck32(self.vm.get_mem(self.cpu.ESP, self.ir_arch.sp.size / 8)) self.cpu.ESP += self.ir_arch.sp.size / 8 return value
def pop_uint32_t(self): value = upck32(self.vm.get_mem(self.cpu.SP, 4)) self.cpu.SP += 4 return value
def get_stack_arg(self, index): return upck32(self.vm.get_mem(self.cpu.SP + 4 * index, 4))
def fake_seh_handler(myjit, except_code): global seh_count regs = myjit.cpu.get_gpreg() print '-> exception at', hex(myjit.cpu.EIP), seh_count seh_count += 1 # Help lambda p = lambda s: struct.pack('I', s) # dump_gpregs_py() # jitarch.dump_gpregs() # Forge a CONTEXT ctxt = '\x00\x00\x00\x00' + '\x00\x00\x00\x00' * 6 + '\x00' * 112 ctxt += '\x00\x00\x00\x00' + '\x3b\x00\x00\x00' + '\x23\x00\x00\x00' ctxt += '\x23\x00\x00\x00' ctxt += pck32(myjit.cpu.EDI) + pck32(myjit.cpu.ESI) + \ pck32(myjit.cpu.EBX) + pck32(myjit.cpu.EDX) + \ pck32(myjit.cpu.ECX) + pck32(myjit.cpu.EAX) + \ pck32(myjit.cpu.EBP) + pck32(myjit.cpu.EIP) ctxt += '\x23\x00\x00\x00' + '\x00\x00\x00\x00' + pck32(myjit.cpu.ESP) ctxt += '\x23\x00\x00\x00' # ctxt = regs2ctxt(regs) # Find a room for seh # seh = (get_memory_page_max_address_py()+0x1000)&0xfffff000 # Get current seh (fs:[0]) seh_ptr = upck32(myjit.vm.get_mem(tib_address, 4)) # Retrieve seh fields old_seh, eh, safe_place = struct.unpack( 'III', myjit.vm.get_mem(seh_ptr, 0xc)) print '-> seh_ptr', hex(seh_ptr), '-> { old_seh', print hex(old_seh), 'eh', hex(eh), 'safe_place', hex(safe_place), '}' # print '-> write SEH at', hex(seh&0xffffffff) # Write current seh # myjit.vm.add_memory_page(seh, PAGE_READ | PAGE_WRITE, p(old_seh) + # p(eh) + p(safe_place) + p(0x99999999)) # Write context myjit.vm.set_mem(context_address, ctxt) # Write exception_record """ #http://msdn.microsoft.com/en-us/library/aa363082(v=vs.85).aspx typedef struct _EXCEPTION_RECORD { DWORD ExceptionCode; DWORD ExceptionFlags; struct _EXCEPTION_RECORD *ExceptionRecord; PVOID ExceptionAddress; DWORD NumberParameters; ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS]; } EXCEPTION_RECORD, *PEXCEPTION_RECORD; """ myjit.vm.set_mem(exception_record_address, pck32(except_code) + pck32(0) + pck32(0) + pck32(myjit.cpu.EIP) + pck32(0) + pck32(0)) # Prepare the stack myjit.push_uint32_t(context_address) # Context myjit.push_uint32_t(seh_ptr) # SEH myjit.push_uint32_t(exception_record_address) # ExceptRecords myjit.push_uint32_t(return_from_exception) # Ret address # Set fake new current seh for exception fake_seh_ad = get_free_seh_place() print hex(fake_seh_ad) myjit.vm.set_mem(fake_seh_ad, pck32(seh_ptr) + pck32( 0xaaaaaaaa) + pck32(0xaaaaaabb) + pck32(0xaaaaaacc)) myjit.vm.set_mem(tib_address, pck32(fake_seh_ad)) dump_seh(myjit) print '-> jumping at', hex(eh) myjit.vm.set_exception(0) myjit.cpu.set_exception(0) # XXX set ebx to nul? myjit.cpu.EBX = 0 return eh
def fake_seh_handler(myjit, except_code): global seh_count regs = myjit.cpu.get_gpreg() print '-> exception at', hex(myjit.cpu.EIP), seh_count seh_count += 1 # Help lambda p = lambda s: struct.pack('I', s) # dump_gpregs_py() # jitarch.dump_gpregs() # Forge a CONTEXT ctxt = '\x00\x00\x00\x00' + '\x00\x00\x00\x00' * 6 + '\x00' * 112 ctxt += '\x00\x00\x00\x00' + '\x3b\x00\x00\x00' + '\x23\x00\x00\x00' ctxt += '\x23\x00\x00\x00' ctxt += pck32(myjit.cpu.EDI) + pck32(myjit.cpu.ESI) + \ pck32(myjit.cpu.EBX) + pck32(myjit.cpu.EDX) + \ pck32(myjit.cpu.ECX) + pck32(myjit.cpu.EAX) + \ pck32(myjit.cpu.EBP) + pck32(myjit.cpu.EIP) ctxt += '\x23\x00\x00\x00' + '\x00\x00\x00\x00' + pck32(myjit.cpu.ESP) ctxt += '\x23\x00\x00\x00' # ctxt = regs2ctxt(regs) # Find a room for seh # seh = (get_memory_page_max_address_py()+0x1000)&0xfffff000 # Get current seh (fs:[0]) seh_ptr = upck32(myjit.vm.get_mem(tib_address, 4)) # Retrieve seh fields old_seh, eh, safe_place = struct.unpack('III', myjit.vm.get_mem(seh_ptr, 0xc)) print '-> seh_ptr', hex(seh_ptr), '-> { old_seh', print hex(old_seh), 'eh', hex(eh), 'safe_place', hex(safe_place), '}' # print '-> write SEH at', hex(seh&0xffffffff) # Write current seh # myjit.vm.add_memory_page(seh, PAGE_READ | PAGE_WRITE, p(old_seh) + # p(eh) + p(safe_place) + p(0x99999999)) # Write context myjit.vm.set_mem(context_address, ctxt) # Write exception_record """ #http://msdn.microsoft.com/en-us/library/aa363082(v=vs.85).aspx typedef struct _EXCEPTION_RECORD { DWORD ExceptionCode; DWORD ExceptionFlags; struct _EXCEPTION_RECORD *ExceptionRecord; PVOID ExceptionAddress; DWORD NumberParameters; ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS]; } EXCEPTION_RECORD, *PEXCEPTION_RECORD; """ myjit.vm.set_mem( exception_record_address, pck32(except_code) + pck32(0) + pck32(0) + pck32(myjit.cpu.EIP) + pck32(0) + pck32(0)) # Prepare the stack myjit.push_uint32_t(context_address) # Context myjit.push_uint32_t(seh_ptr) # SEH myjit.push_uint32_t(exception_record_address) # ExceptRecords myjit.push_uint32_t(return_from_exception) # Ret address # Set fake new current seh for exception fake_seh_ad = get_free_seh_place() print hex(fake_seh_ad) myjit.vm.set_mem( fake_seh_ad, pck32(seh_ptr) + pck32(0xaaaaaaaa) + pck32(0xaaaaaabb) + pck32(0xaaaaaacc)) myjit.vm.set_mem(tib_address, pck32(fake_seh_ad)) dump_seh(myjit) print '-> jumping at', hex(eh) myjit.vm.set_exception(0) myjit.cpu.set_exception(0) # XXX set ebx to nul? myjit.cpu.EBX = 0 return eh