@@ -321,7 +321,8 @@ def __init__(self, minidump_file, *, trace=False, quiet=False, thread_id=None, d
321321 self .handles = HandleManager ()
322322 self ._setup_handles ()
323323 self ._setup_registry ()
324- self .kill_me = None
324+ self .stopped = False
325+ self .kill_exception = None
325326 self .exit_code = None
326327 self .exports = self ._all_exports ()
327328 self ._exception = UnicornExceptionInfo ()
@@ -457,28 +458,26 @@ def _setup_pebteb(self, thread):
457458 self .teb = thread .Teb & 0xFFFFFFFFFFFFF000
458459
459460 # Handle WoW64 support
460- def patch_wow64 (patch_addr ):
461- # See: https://opcode0x90.wordpress.com/2007/05/18/kifastsystemcall-hook/
462- # mov edx, esp; sysenter; ret
463- KiFastSystemCall = b"\x8B \xD4 \x0F \x34 \x90 \x90 \xC3 "
464- self .write (patch_addr , KiFastSystemCall )
465- self .wow64 = True
466-
467461 ntdll = self .modules ["ntdll.dll" ]
468462 Wow64Transition = ntdll .find_export ("Wow64Transition" )
469463 ZwWow64ReadVirtualMemory64 = ntdll .find_export ("ZwWow64ReadVirtualMemory64" )
470464 if Wow64Transition :
471465 # This exists from Windows 10 1607 (Build: 14393)
472466 patch_addr = self .read_ptr (Wow64Transition .address )
473467 self .info (f"Patching Wow64Transition: [{ hex (Wow64Transition .address )} ] -> { hex (patch_addr )} " )
474- patch_wow64 (patch_addr )
468+ # See: https://opcode0x90.wordpress.com/2007/05/18/kifastsystemcall-hook/
469+ # sysenter; nop; nop; ret
470+ self .write (patch_addr , b"\x0F \x34 \x90 \x90 \xC3 " )
471+ self .wow64 = True
475472 elif ZwWow64ReadVirtualMemory64 :
476473 # This function exists since Windows XP
477474 # TODO: Implement by finding EA ???????? 3300 in wow64cpu.dll instead
478475 # Reference: https://github.com/x64dbg/ScyllaHide/blob/a727ac39/InjectorCLI/RemoteHook.cpp#L354-L434
479476 patch_addr = self .read_ptr (self .teb + 0xC0 )
480477 self .error (f"Unsupported WoW64 OS version detected, trampoline: { hex (patch_addr )} " )
481- patch_wow64 (patch_addr )
478+ # sysenter; nop; nop; jmp [esp]
479+ self .write (patch_addr , b"\x0F \x34 \x90 \x90 \xFF \x24 \x24 " )
480+ self .wow64 = True
482481 else :
483482 self .wow64 = False
484483
@@ -945,6 +944,8 @@ def handle_exception(self):
945944
946945 if self ._exception_hook is not None :
947946 hook_result = self ._exception_hook (self ._exception )
947+ if self .stopped :
948+ return None
948949 if hook_result is not None :
949950 # Clear the pending exception
950951 self ._last_exception = self ._exception
@@ -1075,12 +1076,16 @@ def write_stack(cur_ptr: int, data: bytes):
10751076 return self .KiUserExceptionDispatcher
10761077
10771078 def start (self , begin , end = 0xffffffffffffffff , count = 0 ) -> None :
1079+ # Clear stop state
1080+ self .stopped = False
1081+ self .kill_exception = None
1082+ self .exit_code = None
10781083 # Clear exceptions before starting
10791084 self ._exception = UnicornExceptionInfo ()
10801085 emu_begin = begin
10811086 emu_until = end
10821087 emu_count = count
1083- while True :
1088+ while not self . stopped :
10841089 try :
10851090 if self ._exception .type != ExceptionType .NoException :
10861091 if self ._exception .final :
@@ -1095,6 +1100,8 @@ def start(self, begin, end=0xffffffffffffffff, count=0) -> None:
10951100
10961101 try :
10971102 emu_begin = self .handle_exception ()
1103+ if self .stopped :
1104+ break
10981105 except Exception :
10991106 traceback .print_exc ()
11001107 self .error (f"exception during exception handling (stack overflow?)" )
@@ -1119,15 +1126,14 @@ def start(self, begin, end=0xffffffffffffffff, count=0) -> None:
11191126 emu_count = self ._exception .tb_icount + 1
11201127
11211128 self .info (f"emu_start({ hex (emu_begin )} , { hex (emu_until )} , { emu_count } )" )
1122- self .kill_me = None
11231129 self ._uc .emu_start (emu_begin , until = emu_until , count = emu_count )
11241130 self .info (f'emulation finished, cip = { hex (self .regs .cip )} ' )
11251131 if self .exit_code is not None :
11261132 self .info (f"exit code: { hex (self .exit_code )} " )
11271133 break
11281134 except UcError as err :
1129- if self .kill_me is not None and type (self .kill_me ) is not UcError :
1130- raise self .kill_me
1135+ if self .kill_exception is not None and type (self .kill_exception ) is not UcError :
1136+ raise self .kill_exception from None
11311137 if self ._exception .type != ExceptionType .NoException :
11321138 # Handle the exception outside of the except handler
11331139 continue
@@ -1144,17 +1150,17 @@ def stop(self, exit_code=None) -> None:
11441150 except Exception :
11451151 traceback .print_exc ()
11461152 self .error ("Invalid type passed to exit_code!" )
1153+ self .stopped = True
11471154 self ._uc .emu_stop ()
11481155
11491156 def raise_kill (self , exc = None ):
11501157 # HACK: You need to use this to exit from hooks (although it might not always work)
11511158 self .regs .cip = FORCE_KILL_ADDR
1152- self .kill_me = exc
1153- if exc is not None :
1154- return exc
1155- else :
1156- self .kill_me = True
1157- self ._uc .emu_stop ()
1159+ self .stop ()
1160+ if exc is None :
1161+ exc = Exception ()
1162+ self .kill_exception = exc
1163+ return exc
11581164
11591165 def NtCurrentProcess (self ):
11601166 return 0xFFFFFFFFFFFFFFFF if self ._x64 else 0xFFFFFFFF
@@ -1294,12 +1300,12 @@ def _hook_code_exception(uc: Uc, address, size, dp: Dumpulator):
12941300
12951301def _hook_mem (uc : Uc , access , address , size , value , dp : Dumpulator ):
12961302 if dp ._pages .handle_lazy_page (address , min (size , PAGE_SIZE )):
1297- dp .debug (f"committed lazy page { hex (address )} [{ hex (size )} ]" )
1303+ dp .debug (f"committed lazy page { hex (address )} [{ hex (size )} ] (cip: { hex ( dp . regs . cip ) } ) " )
12981304 return True
12991305
13001306 fetch_accesses = [UC_MEM_FETCH , UC_MEM_FETCH_PROT , UC_MEM_FETCH_UNMAPPED ]
1301- if access == UC_MEM_FETCH_UNMAPPED and FORCE_KILL_ADDR - 0x10 <= address <= FORCE_KILL_ADDR + 0x10 and dp . kill_me is not None :
1302- dp .error (f"forced exit memory operation { access } of { hex (address )} [{ hex (size )} ] = { hex ( value ) } " )
1307+ if dp . stopped and access == UC_MEM_FETCH_UNMAPPED and FORCE_KILL_ADDR - 0x10 <= address <= FORCE_KILL_ADDR + 0x10 :
1308+ dp .error (f"force exit fetch of { hex (address )} [{ hex (size )} ]" )
13031309 return False
13041310 if dp ._exception .final and access in fetch_accesses :
13051311 dp .info (f"fetch from { hex (address )} [{ size } ] already reported" )
@@ -1370,7 +1376,7 @@ def _hook_mem(uc: Uc, access, address, size, value, dp: Dumpulator):
13701376 dp ._exception .final = True
13711377
13721378 # Stop emulation (we resume it on KiUserExceptionDispatcher later)
1373- dp .stop ()
1379+ dp ._uc . emu_stop ()
13741380 return False
13751381
13761382 # There should not be an exception active
@@ -1389,7 +1395,7 @@ def _hook_mem(uc: Uc, access, address, size, value, dp: Dumpulator):
13891395 dp ._exception = exception
13901396
13911397 # Stop emulation (we resume execution later)
1392- dp .stop ()
1398+ dp ._uc . emu_stop ()
13931399 return False
13941400 except AssertionError as err :
13951401 traceback .print_exc ()
@@ -1423,50 +1429,53 @@ def _get_regs(instr, include_write=False):
14231429 return regs
14241430
14251431def _hook_code (uc : Uc , address , size , dp : Dumpulator ):
1426- code = b""
14271432 try :
1428- code = dp .read (address , min (size , 15 ))
1429- instr = next (dp .cs .disasm (code , address , 1 ))
1430- except StopIteration :
1431- instr = None # Unsupported instruction
1432- except IndexError :
1433- instr = None # Likely invalid memory
1434-
1435- address_name = dp .exports .get (address , "" )
1433+ code = b""
1434+ try :
1435+ code = dp .read (address , min (size , 15 ))
1436+ instr = next (dp .cs .disasm (code , address , 1 ))
1437+ except StopIteration :
1438+ instr = None # Unsupported instruction
1439+ except IndexError :
1440+ instr = None # Likely invalid memory
1441+ address_name = dp .exports .get (address , "" )
14361442
1437- module = ""
1438- if dp .last_module and address in dp .last_module :
1439- # same module again
1440- pass
1441- else :
1442- # new module
1443- dp .last_module = dp .modules .find (address )
1444- if dp .last_module :
1445- module = dp .last_module .name
1446-
1447- if address_name :
1448- address_name = " " + address_name
1449- elif module :
1450- address_name = " " + module
1451-
1452- line = f"{ hex (address )} { address_name } |"
1453- if instr is not None :
1454- line += instr .mnemonic
1455- if instr .op_str :
1456- line += " "
1457- line += instr .op_str
1458- for reg in _get_regs (instr ):
1459- line += f"|{ reg } ={ hex (dp .regs .__getattr__ (reg ))} "
1460- if instr .mnemonic == "call" :
1461- # print return address
1462- ret_address = address + instr .size
1463- line += f"|return_address={ hex (ret_address )} "
1464- if instr .mnemonic in {"syscall" , "sysenter" }:
1465- line += f"|sequence_id=[{ dp .sequence_id } ]"
1466- else :
1467- line += f"??? (code: { code .hex ()} , size: { hex (size )} )"
1468- line += "\n "
1469- dp .trace .write (line )
1443+ module = ""
1444+ if dp .last_module and address in dp .last_module :
1445+ # same module again
1446+ pass
1447+ else :
1448+ # new module
1449+ dp .last_module = dp .modules .find (address )
1450+ if dp .last_module :
1451+ module = dp .last_module .name
1452+
1453+ if address_name :
1454+ address_name = " " + address_name
1455+ elif module :
1456+ address_name = " " + module
1457+
1458+ line = f"{ hex (address )} { address_name } |"
1459+ if instr is not None :
1460+ line += instr .mnemonic
1461+ if instr .op_str :
1462+ line += " "
1463+ line += instr .op_str
1464+ for reg in _get_regs (instr ):
1465+ line += f"|{ reg } ={ hex (dp .regs .__getattr__ (reg ))} "
1466+ if instr .mnemonic == "call" :
1467+ # print return address
1468+ ret_address = address + instr .size
1469+ line += f"|return_address={ hex (ret_address )} "
1470+ elif instr .mnemonic in {"syscall" , "sysenter" }:
1471+ line += f"|sequence_id=[{ dp .sequence_id } ]"
1472+ else :
1473+ line += f"??? (code: { code .hex ()} , size: { hex (size )} )"
1474+ line += "\n "
1475+ dp .trace .write (line )
1476+ except (KeyboardInterrupt , SystemExit ) as e :
1477+ dp .stop ()
1478+ raise e
14701479
14711480def _unicode_string_to_string (dp : Dumpulator , arg : P [UNICODE_STRING ]):
14721481 try :
@@ -1629,7 +1638,7 @@ def syscall_arg(index):
16291638 if isinstance (status , ExceptionInfo ):
16301639 print ("context switch, stopping emulation" )
16311640 dp ._exception = status
1632- raise dp . raise_kill ( UcError (UC_ERR_EXCEPTION )) from None
1641+ raise UcError (UC_ERR_EXCEPTION )
16331642 else :
16341643 dp .info (f"status = { hex (status )} " )
16351644 dp .regs .cax = status
@@ -1671,7 +1680,7 @@ def _hook_invalid(uc: Uc, dp: Dumpulator):
16711680 if dp .trace :
16721681 dp .trace .flush ()
16731682 # HACK: unicorn cannot gracefully exit in all contexts
1674- if dp .kill_me :
1683+ if dp .stopped :
16751684 dp .error (f"terminating emulation..." )
16761685 return False
16771686 dp .error (f"invalid instruction at { hex (address )} " )
0 commit comments