@@ -321,7 +321,8 @@ def __init__(self, minidump_file, *, trace=False, quiet=False, thread_id=None, d
321
321
self .handles = HandleManager ()
322
322
self ._setup_handles ()
323
323
self ._setup_registry ()
324
- self .kill_me = None
324
+ self .stopped = False
325
+ self .kill_exception = None
325
326
self .exit_code = None
326
327
self .exports = self ._all_exports ()
327
328
self ._exception = UnicornExceptionInfo ()
@@ -457,28 +458,26 @@ def _setup_pebteb(self, thread):
457
458
self .teb = thread .Teb & 0xFFFFFFFFFFFFF000
458
459
459
460
# 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
-
467
461
ntdll = self .modules ["ntdll.dll" ]
468
462
Wow64Transition = ntdll .find_export ("Wow64Transition" )
469
463
ZwWow64ReadVirtualMemory64 = ntdll .find_export ("ZwWow64ReadVirtualMemory64" )
470
464
if Wow64Transition :
471
465
# This exists from Windows 10 1607 (Build: 14393)
472
466
patch_addr = self .read_ptr (Wow64Transition .address )
473
467
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
475
472
elif ZwWow64ReadVirtualMemory64 :
476
473
# This function exists since Windows XP
477
474
# TODO: Implement by finding EA ???????? 3300 in wow64cpu.dll instead
478
475
# Reference: https://github.com/x64dbg/ScyllaHide/blob/a727ac39/InjectorCLI/RemoteHook.cpp#L354-L434
479
476
patch_addr = self .read_ptr (self .teb + 0xC0 )
480
477
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
482
481
else :
483
482
self .wow64 = False
484
483
@@ -945,6 +944,8 @@ def handle_exception(self):
945
944
946
945
if self ._exception_hook is not None :
947
946
hook_result = self ._exception_hook (self ._exception )
947
+ if self .stopped :
948
+ return None
948
949
if hook_result is not None :
949
950
# Clear the pending exception
950
951
self ._last_exception = self ._exception
@@ -1075,12 +1076,16 @@ def write_stack(cur_ptr: int, data: bytes):
1075
1076
return self .KiUserExceptionDispatcher
1076
1077
1077
1078
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
1078
1083
# Clear exceptions before starting
1079
1084
self ._exception = UnicornExceptionInfo ()
1080
1085
emu_begin = begin
1081
1086
emu_until = end
1082
1087
emu_count = count
1083
- while True :
1088
+ while not self . stopped :
1084
1089
try :
1085
1090
if self ._exception .type != ExceptionType .NoException :
1086
1091
if self ._exception .final :
@@ -1095,6 +1100,8 @@ def start(self, begin, end=0xffffffffffffffff, count=0) -> None:
1095
1100
1096
1101
try :
1097
1102
emu_begin = self .handle_exception ()
1103
+ if self .stopped :
1104
+ break
1098
1105
except Exception :
1099
1106
traceback .print_exc ()
1100
1107
self .error (f"exception during exception handling (stack overflow?)" )
@@ -1119,15 +1126,14 @@ def start(self, begin, end=0xffffffffffffffff, count=0) -> None:
1119
1126
emu_count = self ._exception .tb_icount + 1
1120
1127
1121
1128
self .info (f"emu_start({ hex (emu_begin )} , { hex (emu_until )} , { emu_count } )" )
1122
- self .kill_me = None
1123
1129
self ._uc .emu_start (emu_begin , until = emu_until , count = emu_count )
1124
1130
self .info (f'emulation finished, cip = { hex (self .regs .cip )} ' )
1125
1131
if self .exit_code is not None :
1126
1132
self .info (f"exit code: { hex (self .exit_code )} " )
1127
1133
break
1128
1134
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
1131
1137
if self ._exception .type != ExceptionType .NoException :
1132
1138
# Handle the exception outside of the except handler
1133
1139
continue
@@ -1144,17 +1150,17 @@ def stop(self, exit_code=None) -> None:
1144
1150
except Exception :
1145
1151
traceback .print_exc ()
1146
1152
self .error ("Invalid type passed to exit_code!" )
1153
+ self .stopped = True
1147
1154
self ._uc .emu_stop ()
1148
1155
1149
1156
def raise_kill (self , exc = None ):
1150
1157
# HACK: You need to use this to exit from hooks (although it might not always work)
1151
1158
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
1158
1164
1159
1165
def NtCurrentProcess (self ):
1160
1166
return 0xFFFFFFFFFFFFFFFF if self ._x64 else 0xFFFFFFFF
@@ -1294,12 +1300,12 @@ def _hook_code_exception(uc: Uc, address, size, dp: Dumpulator):
1294
1300
1295
1301
def _hook_mem (uc : Uc , access , address , size , value , dp : Dumpulator ):
1296
1302
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 ) } ) " )
1298
1304
return True
1299
1305
1300
1306
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 )} ]" )
1303
1309
return False
1304
1310
if dp ._exception .final and access in fetch_accesses :
1305
1311
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):
1370
1376
dp ._exception .final = True
1371
1377
1372
1378
# Stop emulation (we resume it on KiUserExceptionDispatcher later)
1373
- dp .stop ()
1379
+ dp ._uc . emu_stop ()
1374
1380
return False
1375
1381
1376
1382
# There should not be an exception active
@@ -1389,7 +1395,7 @@ def _hook_mem(uc: Uc, access, address, size, value, dp: Dumpulator):
1389
1395
dp ._exception = exception
1390
1396
1391
1397
# Stop emulation (we resume execution later)
1392
- dp .stop ()
1398
+ dp ._uc . emu_stop ()
1393
1399
return False
1394
1400
except AssertionError as err :
1395
1401
traceback .print_exc ()
@@ -1423,50 +1429,53 @@ def _get_regs(instr, include_write=False):
1423
1429
return regs
1424
1430
1425
1431
def _hook_code (uc : Uc , address , size , dp : Dumpulator ):
1426
- code = b""
1427
1432
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 , "" )
1436
1442
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
1470
1479
1471
1480
def _unicode_string_to_string (dp : Dumpulator , arg : P [UNICODE_STRING ]):
1472
1481
try :
@@ -1629,7 +1638,7 @@ def syscall_arg(index):
1629
1638
if isinstance (status , ExceptionInfo ):
1630
1639
print ("context switch, stopping emulation" )
1631
1640
dp ._exception = status
1632
- raise dp . raise_kill ( UcError (UC_ERR_EXCEPTION )) from None
1641
+ raise UcError (UC_ERR_EXCEPTION )
1633
1642
else :
1634
1643
dp .info (f"status = { hex (status )} " )
1635
1644
dp .regs .cax = status
@@ -1671,7 +1680,7 @@ def _hook_invalid(uc: Uc, dp: Dumpulator):
1671
1680
if dp .trace :
1672
1681
dp .trace .flush ()
1673
1682
# HACK: unicorn cannot gracefully exit in all contexts
1674
- if dp .kill_me :
1683
+ if dp .stopped :
1675
1684
dp .error (f"terminating emulation..." )
1676
1685
return False
1677
1686
dp .error (f"invalid instruction at { hex (address )} " )
0 commit comments