Skip to content

Commit 6f045df

Browse files
committed
Fix Ctrl+C while tracing
1 parent 8df2d31 commit 6f045df

File tree

1 file changed

+71
-60
lines changed

1 file changed

+71
-60
lines changed

src/dumpulator/dumpulator.py

Lines changed: 71 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -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()
@@ -943,6 +944,8 @@ def handle_exception(self):
943944

944945
if self._exception_hook is not None:
945946
hook_result = self._exception_hook(self._exception)
947+
if self.stopped:
948+
return None
946949
if hook_result is not None:
947950
# Clear the pending exception
948951
self._last_exception = self._exception
@@ -1073,12 +1076,16 @@ def write_stack(cur_ptr: int, data: bytes):
10731076
return self.KiUserExceptionDispatcher
10741077

10751078
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
10761083
# Clear exceptions before starting
10771084
self._exception = UnicornExceptionInfo()
10781085
emu_begin = begin
10791086
emu_until = end
10801087
emu_count = count
1081-
while True:
1088+
while not self.stopped:
10821089
try:
10831090
if self._exception.type != ExceptionType.NoException:
10841091
if self._exception.final:
@@ -1093,6 +1100,8 @@ def start(self, begin, end=0xffffffffffffffff, count=0) -> None:
10931100

10941101
try:
10951102
emu_begin = self.handle_exception()
1103+
if self.stopped:
1104+
break
10961105
except Exception:
10971106
traceback.print_exc()
10981107
self.error(f"exception during exception handling (stack overflow?)")
@@ -1117,15 +1126,14 @@ def start(self, begin, end=0xffffffffffffffff, count=0) -> None:
11171126
emu_count = self._exception.tb_icount + 1
11181127

11191128
self.info(f"emu_start({hex(emu_begin)}, {hex(emu_until)}, {emu_count})")
1120-
self.kill_me = None
11211129
self._uc.emu_start(emu_begin, until=emu_until, count=emu_count)
11221130
self.info(f'emulation finished, cip = {hex(self.regs.cip)}')
11231131
if self.exit_code is not None:
11241132
self.info(f"exit code: {hex(self.exit_code)}")
11251133
break
11261134
except UcError as err:
1127-
if self.kill_me is not None and type(self.kill_me) is not UcError:
1128-
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
11291137
if self._exception.type != ExceptionType.NoException:
11301138
# Handle the exception outside of the except handler
11311139
continue
@@ -1142,17 +1150,17 @@ def stop(self, exit_code=None) -> None:
11421150
except Exception:
11431151
traceback.print_exc()
11441152
self.error("Invalid type passed to exit_code!")
1153+
self.stopped = True
11451154
self._uc.emu_stop()
11461155

11471156
def raise_kill(self, exc=None):
11481157
# HACK: You need to use this to exit from hooks (although it might not always work)
11491158
self.regs.cip = FORCE_KILL_ADDR
1150-
self.kill_me = exc
1151-
if exc is not None:
1152-
return exc
1153-
else:
1154-
self.kill_me = True
1155-
self._uc.emu_stop()
1159+
self.stop()
1160+
if exc is None:
1161+
exc = Exception()
1162+
self.kill_exception = exc
1163+
return exc
11561164

11571165
def NtCurrentProcess(self):
11581166
return 0xFFFFFFFFFFFFFFFF if self._x64 else 0xFFFFFFFF
@@ -1292,12 +1300,12 @@ def _hook_code_exception(uc: Uc, address, size, dp: Dumpulator):
12921300

12931301
def _hook_mem(uc: Uc, access, address, size, value, dp: Dumpulator):
12941302
if dp._pages.handle_lazy_page(address, min(size, PAGE_SIZE)):
1295-
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)})")
12961304
return True
12971305

12981306
fetch_accesses = [UC_MEM_FETCH, UC_MEM_FETCH_PROT, UC_MEM_FETCH_UNMAPPED]
1299-
if access == UC_MEM_FETCH_UNMAPPED and FORCE_KILL_ADDR - 0x10 <= address <= FORCE_KILL_ADDR + 0x10 and dp.kill_me is not None:
1300-
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)}]")
13011309
return False
13021310
if dp._exception.final and access in fetch_accesses:
13031311
dp.info(f"fetch from {hex(address)}[{size}] already reported")
@@ -1368,7 +1376,7 @@ def _hook_mem(uc: Uc, access, address, size, value, dp: Dumpulator):
13681376
dp._exception.final = True
13691377

13701378
# Stop emulation (we resume it on KiUserExceptionDispatcher later)
1371-
dp.stop()
1379+
dp._uc.emu_stop()
13721380
return False
13731381

13741382
# There should not be an exception active
@@ -1387,7 +1395,7 @@ def _hook_mem(uc: Uc, access, address, size, value, dp: Dumpulator):
13871395
dp._exception = exception
13881396

13891397
# Stop emulation (we resume execution later)
1390-
dp.stop()
1398+
dp._uc.emu_stop()
13911399
return False
13921400
except AssertionError as err:
13931401
traceback.print_exc()
@@ -1421,50 +1429,53 @@ def _get_regs(instr, include_write=False):
14211429
return regs
14221430

14231431
def _hook_code(uc: Uc, address, size, dp: Dumpulator):
1424-
code = b""
14251432
try:
1426-
code = dp.read(address, min(size, 15))
1427-
instr = next(dp.cs.disasm(code, address, 1))
1428-
except StopIteration:
1429-
instr = None # Unsupported instruction
1430-
except IndexError:
1431-
instr = None # Likely invalid memory
1432-
1433-
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, "")
14341442

1435-
module = ""
1436-
if dp.last_module and address in dp.last_module:
1437-
# same module again
1438-
pass
1439-
else:
1440-
# new module
1441-
dp.last_module = dp.modules.find(address)
1442-
if dp.last_module:
1443-
module = dp.last_module.name
1444-
1445-
if address_name:
1446-
address_name = " " + address_name
1447-
elif module:
1448-
address_name = " " + module
1449-
1450-
line = f"{hex(address)}{address_name}|"
1451-
if instr is not None:
1452-
line += instr.mnemonic
1453-
if instr.op_str:
1454-
line += " "
1455-
line += instr.op_str
1456-
for reg in _get_regs(instr):
1457-
line += f"|{reg}={hex(dp.regs.__getattr__(reg))}"
1458-
if instr.mnemonic == "call":
1459-
# print return address
1460-
ret_address = address + instr.size
1461-
line += f"|return_address={hex(ret_address)}"
1462-
if instr.mnemonic in {"syscall", "sysenter"}:
1463-
line += f"|sequence_id=[{dp.sequence_id}]"
1464-
else:
1465-
line += f"??? (code: {code.hex()}, size: {hex(size)})"
1466-
line += "\n"
1467-
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
14681479

14691480
def _unicode_string_to_string(dp: Dumpulator, arg: P[UNICODE_STRING]):
14701481
try:
@@ -1627,7 +1638,7 @@ def syscall_arg(index):
16271638
if isinstance(status, ExceptionInfo):
16281639
print("context switch, stopping emulation")
16291640
dp._exception = status
1630-
raise dp.raise_kill(UcError(UC_ERR_EXCEPTION)) from None
1641+
raise UcError(UC_ERR_EXCEPTION)
16311642
else:
16321643
dp.info(f"status = {hex(status)}")
16331644
dp.regs.cax = status
@@ -1669,7 +1680,7 @@ def _hook_invalid(uc: Uc, dp: Dumpulator):
16691680
if dp.trace:
16701681
dp.trace.flush()
16711682
# HACK: unicorn cannot gracefully exit in all contexts
1672-
if dp.kill_me:
1683+
if dp.stopped:
16731684
dp.error(f"terminating emulation...")
16741685
return False
16751686
dp.error(f"invalid instruction at {hex(address)}")

0 commit comments

Comments
 (0)