Skip to content

Commit 52cf201

Browse files
committed
Make logging a bit more structured and add quiet=True
Closes #9
1 parent f16acea commit 52cf201

File tree

1 file changed

+34
-26
lines changed

1 file changed

+34
-26
lines changed

src/dumpulator/dumpulator.py

+34-26
Original file line numberDiff line numberDiff line change
@@ -404,7 +404,8 @@ def __setitem__(self, index, value):
404404

405405

406406
class Dumpulator(Architecture):
407-
def __init__(self, minidump_file, trace=False):
407+
def __init__(self, minidump_file, *, trace=False, quiet=False):
408+
self._quiet = quiet
408409
self._minidump = MinidumpFile.parse(minidump_file)
409410
super().__init__(type(self._minidump.threads.threads[0].ContextObject) is not WOW64_CONTEXT)
410411
self.addr_mask = 0xFFFFFFFFFFFFFFFF if self._x64 else 0xFFFFFFFF
@@ -434,6 +435,13 @@ def __init__(self, minidump_file, trace=False):
434435
self._setup_syscalls()
435436
self.exports = self._setup_exports()
436437

438+
def info(self, message: str):
439+
if not self._quiet:
440+
print(message)
441+
442+
def error(self, message: str):
443+
print(message)
444+
437445
# Source: https://github.com/mandiant/speakeasy/blob/767edd2272510a5badbab89c5f35d43a94041378/speakeasy/windows/winemu.py#L533
438446
def _setup_gdt(self, teb_addr):
439447
"""
@@ -538,7 +546,7 @@ def _setup_emulator(self):
538546
for info in self._minidump.memory_info.infos:
539547
emu_addr = info.BaseAddress & self.addr_mask
540548
if info.State == MemoryState.MEM_COMMIT:
541-
print(f"mapped base: 0x{emu_addr:x}, size: 0x{info.RegionSize:x}, protect: {info.Protect}")
549+
self.info(f"mapped base: 0x{emu_addr:x}, size: 0x{info.RegionSize:x}, protect: {info.Protect}")
542550
self._uc.mem_map(emu_addr, info.RegionSize, map_unicorn_perms(info.Protect))
543551
elif info.State == MemoryState.MEM_FREE and emu_addr > 0x10000 and info.RegionSize >= self._allocate_size:
544552
self._allocate_base = emu_addr
@@ -547,7 +555,7 @@ def _setup_emulator(self):
547555
seg: MinidumpMemorySegment
548556
for seg in self._minidump.memory_segments_64.memory_segments:
549557
emu_addr = seg.start_virtual_address & self.addr_mask
550-
print(f"initialize base: 0x{emu_addr:x}, size: 0x{seg.size:x}")
558+
self.info(f"initialize base: 0x{emu_addr:x}, size: 0x{seg.size:x}")
551559
memory.move(seg.start_virtual_address)
552560
assert memory.current_position == seg.start_virtual_address
553561
data = memory.read(seg.size)
@@ -631,17 +639,17 @@ def _setup_emulator(self):
631639
self.stdin_handle = self.read_ptr(process_parameters + 0x18)
632640
self.stdout_handle = self.read_ptr(process_parameters + 0x1c)
633641
self.stderr_handle = self.read_ptr(process_parameters + 0x20)
634-
print(f"TEB: 0x{self.teb:x}, PEB: 0x{self.peb:x}")
635-
print(f" ConsoleHandle: 0x{self.console_handle:x}")
636-
print(f" StandardInput: 0x{self.stdin_handle:x}")
637-
print(f" StandardOutput: 0x{self.stdout_handle:x}")
638-
print(f" StandardError: 0x{self.stderr_handle:x}")
642+
self.info(f"TEB: 0x{self.teb:x}, PEB: 0x{self.peb:x}")
643+
self.info(f" ConsoleHandle: 0x{self.console_handle:x}")
644+
self.info(f" StandardInput: 0x{self.stdin_handle:x}")
645+
self.info(f" StandardOutput: 0x{self.stdout_handle:x}")
646+
self.info(f" StandardError: 0x{self.stderr_handle:x}")
639647

640648
def _setup_exports(self):
641649
exports = {}
642650
for module in self._minidump.modules.modules:
643651
module_name = module.name.split('\\')[-1].lower()
644-
print(f"{module_name} 0x{module.baseaddress:x}[0x{module.size:x}]")
652+
self.info(f"{module_name} 0x{module.baseaddress:x}[0x{module.size:x}]")
645653
for export in self._parse_module_exports(module):
646654
if export.name:
647655
name = export.name.decode("utf-8")
@@ -669,7 +677,7 @@ def _parse_module_exports(self, module):
669677
try:
670678
module_data = self.read(module.baseaddress, module.size)
671679
except UcError:
672-
print(f"Failed to read module data")
680+
self.error(f"Failed to read module data")
673681
return []
674682
pe = PE(data=module_data, fast_load=True)
675683
# Hack to adjust pefile to accept in-memory modules
@@ -691,7 +699,7 @@ def _setup_syscalls(self):
691699
elif export.name == b"Wow64Transition":
692700
addr = ntdll.baseaddress + export.address
693701
patch_addr = self.read_ptr(addr)
694-
print(f"Patching Wow64Transition: {addr:0x} -> {patch_addr:0x}")
702+
self.info(f"Patching Wow64Transition: {addr:0x} -> {patch_addr:0x}")
695703
# See: https://opcode0x90.wordpress.com/2007/05/18/kifastsystemcall-hook/
696704
# mov edx, esp; sysenter; ret
697705
KiFastSystemCall = b"\x8B\xD4\x0F\x34\xC3"
@@ -753,11 +761,11 @@ def allocate(self, size):
753761
def start(self, begin, end=0xffffffffffffffff, count=0):
754762
try:
755763
self._uc.emu_start(begin, until=end, count=count)
756-
print(f'emulation finished, cip = {self.regs.cip:0x}')
764+
self.info(f'emulation finished, cip = {self.regs.cip:0x}')
757765
if self.exit_code is not None:
758-
print(f"exit code: {self.exit_code}")
766+
self.info(f"exit code: {self.exit_code}")
759767
except UcError as err:
760-
print(f'error: {err}, cip = {self.regs.cip:0x}')
768+
self.error(f'error: {err}, cip = {self.regs.cip:0x}')
761769

762770
def stop(self, exit_code=None):
763771
self.exit_code = int(exit_code)
@@ -772,12 +780,12 @@ def NtCurrentThread(self):
772780

773781
def _hook_mem(uc: Uc, access, address, size, value, dp: Dumpulator):
774782
if access == UC_MEM_READ_UNMAPPED:
775-
print(f"unmapped read from {address:0x}[{size:0x}], cip = {dp.regs.cip:0x}")
783+
dp.error(f"unmapped read from {address:0x}[{size:0x}], cip = {dp.regs.cip:0x}")
776784
elif access == UC_MEM_WRITE_UNMAPPED:
777-
print(f"unmapped write to {address:0x}[{size:0x}] = {value:0x}, cip = {dp.regs.cip:0x}")
785+
dp.error(f"unmapped write to {address:0x}[{size:0x}] = {value:0x}, cip = {dp.regs.cip:0x}")
778786

779787
elif access == UC_MEM_FETCH_UNMAPPED:
780-
print(f"unmapped fetch of {address:0x}[{size:0x}] = {value:0x}, cip = {dp.regs.cip:0x}")
788+
dp.error(f"unmapped fetch of {address:0x}[{size:0x}] = {value:0x}, cip = {dp.regs.cip:0x}")
781789
return False
782790

783791
def _get_regs(instr):
@@ -844,7 +852,7 @@ def _arg_type_string(arg):
844852

845853

846854
def _hook_interrupt(uc: Uc, number, dp: Dumpulator):
847-
print(f"interrupt {number}, cip = {dp.regs.cip:0x}")
855+
dp.error(f"interrupt {number}, cip = {dp.regs.cip:0x}")
848856
uc.emu_stop()
849857

850858

@@ -861,7 +869,7 @@ def syscall_arg(index):
861869
return dp.regs.r10
862870
return dp.args[index]
863871

864-
print(f"syscall: {name}(")
872+
dp.info(f"syscall: {name}(")
865873
for i in range(0, argcount):
866874
argname = argspec.args[1 + i]
867875
argtype = argspec.annotations[argname]
@@ -881,25 +889,25 @@ def syscall_arg(index):
881889
if i + 1 == argcount:
882890
comma = ""
883891

884-
print(f" {_arg_type_string(argvalue)} {argname} = {_arg_to_string(argvalue)}{comma}")
885-
print(")")
892+
dp.info(f" {_arg_type_string(argvalue)} {argname} = {_arg_to_string(argvalue)}{comma}")
893+
dp.info(")")
886894
try:
887895
status = cb(dp, *args)
888-
print(f"status = {status:x}")
896+
dp.info(f"status = {status:x}")
889897
dp.regs.cax = status
890898
dp.regs.ccx = dp.regs.cip + 2
891899
except Exception as exc:
892900
sys.stderr = sys.stdout
893901
traceback.print_exception(type(exc), exc, exc.__traceback__)
894-
print(f"Exception thrown during syscall implementation, stopping emulation!")
902+
dp.error(f"Exception thrown during syscall implementation, stopping emulation!")
895903
uc.emu_stop()
896904
else:
897-
print(f"syscall index: {index:0x} -> {name} not implemented!")
905+
dp.error(f"syscall index: {index:0x} -> {name} not implemented!")
898906
uc.emu_stop()
899907
else:
900-
print(f"syscall index {index:0x} out of range")
908+
dp.error(f"syscall index {index:0x} out of range")
901909
uc.emu_stop()
902910

903911
def _hook_invalid(uc: Uc, address, dp: Dumpulator):
904-
print(f"invalid instruction at {address:0x}")
912+
dp.error(f"invalid instruction at {address:0x}")
905913
return False

0 commit comments

Comments
 (0)