def build_teb(jitter, teb_address): """ Build TEB information using following structure: @jitter: jitter instance @teb_address: the TEB address """ # Only allocate space for ExceptionList/ProcessEnvironmentBlock/Self jitter.vm.add_memory_page( teb_address, PAGE_READ | PAGE_WRITE, b"\x00" * NT_TIB.get_offset("StackBase"), "TEB.NtTib.ExceptionList" ) jitter.vm.add_memory_page( teb_address + NT_TIB.get_offset("Self"), PAGE_READ | PAGE_WRITE, b"\x00" * (NT_TIB.sizeof() - NT_TIB.get_offset("Self")), "TEB.NtTib.Self" ) jitter.vm.add_memory_page( teb_address + TEB.get_offset("ProcessEnvironmentBlock"), PAGE_READ | PAGE_WRITE, b"\x00" * ( TEB.get_offset("LastErrorValue") - TEB.get_offset("ProcessEnvironmentBlock") ), "TEB.ProcessEnvironmentBlock" ) Teb = TEB(jitter.vm, teb_address) Teb.NtTib.ExceptionList = DEFAULT_SEH Teb.NtTib.Self = teb_address Teb.ProcessEnvironmentBlock = peb_address
def return_from_seh(jitter): """Handle the return from an exception handler @jitter: jitter instance""" # Get object addresses seh_address = jitter.vm.get_u32(jitter.cpu.ESP + 0x4) context_address = jitter.vm.get_u32(jitter.cpu.ESP + 0x8) # 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 dump_seh(jitter): """ Walk and dump the SEH entries @jitter: jitter instance """ log.info('Dump_seh. Tib_address: %x', tib_address) cur_seh_ptr = NT_TIB(jitter.vm, tib_address).ExceptionList loop = 0 while cur_seh_ptr and jitter.vm.is_mapped(cur_seh_ptr.val, len(cur_seh_ptr)): if loop > MAX_SEH: log.warn("Too many seh, quit") return err = cur_seh_ptr.deref log.info('\t' * (loop + 1) + 'seh_ptr: %x { prev_seh: %r eh %r }', err.get_addr(), err.Next, err.Handler) cur_seh_ptr = err.Next loop += 1
def fake_seh_handler(jitter, except_code, previous_seh=None): """ Create an exception context @jitter: jitter instance @except_code: x86 exception code @previous_seh: (optional) last SEH address when multiple SEH are used """ global seh_count log.warning('Exception at %x %r', jitter.cpu.EIP, seh_count) seh_count += 1 # Get space on stack for exception handling new_ESP = jitter.cpu.ESP - 0x3c8 exception_base_address = new_ESP exception_record_address = exception_base_address + 0xe8 context_address = exception_base_address + 0xfc fake_seh_address = exception_base_address + 0x14 # Save a CONTEXT regs2ctxt(jitter, context_address) jitter.cpu.ESP = new_ESP # Get current seh (fs:[0]) tib = NT_TIB(jitter.vm, tib_address) seh = tib.ExceptionList.deref if previous_seh: # Recursive SEH while seh.get_addr() != previous_seh: seh = seh.Next.deref seh = seh.Next.deref log.info( 'seh_ptr %x { old_seh %r eh %r} ctx_addr %x', seh.get_addr(), seh.Next, seh.Handler, context_address ) # Write exception_record except_record = EXCEPTION_RECORD(jitter.vm, exception_record_address) except_record.memset(b"\x00") except_record.ExceptionCode = except_code except_record.ExceptionAddress = jitter.cpu.EIP # Prepare the stack jitter.push_uint32_t(context_address) # Context jitter.push_uint32_t(seh.get_addr()) # SEH jitter.push_uint32_t(except_record.get_addr()) # 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) fake_seh = EXCEPTION_REGISTRATION_RECORD(jitter.vm, fake_seh_address) fake_seh.Next.val = tib.ExceptionList.val fake_seh.Handler = 0xaaaaaaaa tib.ExceptionList.val = fake_seh.get_addr() dump_seh(jitter) # Remove exceptions jitter.vm.set_exception(0) jitter.cpu.set_exception(0) # XXX set ebx to nul? jitter.cpu.EBX = 0 log.info('Jumping at %r', seh.Handler) return seh.Handler.val