From 53f2d25502c016a2405d9aa322bd92619ee13742 Mon Sep 17 00:00:00 2001 From: anvbis <88613997+anvbis@users.noreply.github.com> Date: Sun, 26 Jan 2025 14:35:37 +1100 Subject: [PATCH 1/5] add support for full linux memory dumps (fix #219) --- linux_mode/qemu_snapshot/gdb_fuzzbkpt.py | 48 +++++++++++++++++++++- linux_mode/qemu_snapshot/target_vm/init.sh | 2 +- 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/linux_mode/qemu_snapshot/gdb_fuzzbkpt.py b/linux_mode/qemu_snapshot/gdb_fuzzbkpt.py index a02c0ca..871689e 100755 --- a/linux_mode/qemu_snapshot/gdb_fuzzbkpt.py +++ b/linux_mode/qemu_snapshot/gdb_fuzzbkpt.py @@ -11,6 +11,7 @@ import struct import subprocess import pathlib +import lief currentdir = pathlib.Path(inspect.getfile(inspect.currentframe())) currentdir = currentdir.absolute().parent @@ -26,6 +27,7 @@ pwn.context.arch = "amd64" SYMSTORE_FILENAME = pathlib.Path("symbol-store.json") +ELF_FILENAME = pathlib.Path("mem.elf") RAW_FILENAME = pathlib.Path("raw") DMP_FILENAME = pathlib.Path("mem.dmp") REGS_JSON_FILENAME = pathlib.Path("regs.json") @@ -43,6 +45,36 @@ class dump_file: BMP_EXPECTED_SIGNATURE = b"SDMP" BMP_EXPECTED_VALID_DUMP = b"DUMP" + def convert_elf_to_raw(): + print(f"Converting elf file '{ELF_FILENAME}' to raw file '{RAW_FILENAME}'") + + core = lief.parse(ELF_FILENAME) + + def is_load_segment(s): + return s.type == lief.ELF.Segment.TYPE.LOAD + segments = list(filter(is_load_segment, core.segments)) + + elf_file = ELF_FILENAME.open('rb') + raw_file = RAW_FILENAME.open('wb') + + pos = 0 + for s in segments: + pad_len = s.physical_address - pos + for _ in range(pad_len // dump_file.PAGE_SIZE): + raw_file.write(b'\0' * dump_file.PAGE_SIZE) + pos += dump_file.PAGE_SIZE + + elf_file.seek(s.file_offset) + for _ in range(s.physical_size // dump_file.PAGE_SIZE): + raw_file.write(elf_file.read(dump_file.PAGE_SIZE)) + pos += dump_file.PAGE_SIZE + + elf_file.close() + raw_file.close() + + ELF_FILENAME.unlink() + print("Done") + def convert_raw_to_dmp(out_filename: pathlib.Path): dump_size = RAW_FILENAME.stat().st_size pages_count = int(dump_size / dump_file.PAGE_SIZE) @@ -147,6 +179,19 @@ def write_phys_mem_file_to_disk(): qemu_monitor.wait_ready(s) print("Done") + def dump_guest_memory(): + print( + f"Connecting to Qemu monitor at {qemu_monitor.HOSTNAME}:{qemu_monitor.PORT}" + ) + s = qemu_monitor.setup_sock() + print("Connected") + qemu_monitor.wait_ready(s) + + print(f"Instructing Qemu to dump physical memory into '{ELF_FILENAME}'") + s.send(f"dump-guest-memory {ELF_FILENAME}\n".encode()) + qemu_monitor.wait_ready(s) + print("Done") + class kernel: @staticmethod @@ -325,7 +370,8 @@ def wait_for_cpu_regs_dump(): wait_for_cpu_regs_dump() - qemu_monitor.write_phys_mem_file_to_disk() + qemu_monitor.dump_guest_memory() + dump_file.convert_elf_to_raw() out_filename = self.target_dir / "state" / DMP_FILENAME dump_file.convert_raw_to_dmp(out_filename) diff --git a/linux_mode/qemu_snapshot/target_vm/init.sh b/linux_mode/qemu_snapshot/target_vm/init.sh index 6570a86..5684cd5 100755 --- a/linux_mode/qemu_snapshot/target_vm/init.sh +++ b/linux_mode/qemu_snapshot/target_vm/init.sh @@ -63,7 +63,7 @@ download_prereqs() { sudo apt-get install -y libglib2.0-dev libpixman-1-dev python3-pip cmake - pip3 install pwntools + pip3 install pwntools lief # If there isn't a bookworm script for debootstrap (like in Ubuntu 18.04), copy # over the bullseye script as it is the same From 9167b5163bee5bf3a4194856fe7e81b14a6d28a6 Mon Sep 17 00:00:00 2001 From: 0vercl0k <1476421+0vercl0k@users.noreply.github.com> Date: Sat, 1 Feb 2025 08:59:28 -0800 Subject: [PATCH 2/5] nit: black + let iterator be lazy --- linux_mode/qemu_snapshot/gdb_fuzzbkpt.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/linux_mode/qemu_snapshot/gdb_fuzzbkpt.py b/linux_mode/qemu_snapshot/gdb_fuzzbkpt.py index 871689e..fb66128 100755 --- a/linux_mode/qemu_snapshot/gdb_fuzzbkpt.py +++ b/linux_mode/qemu_snapshot/gdb_fuzzbkpt.py @@ -52,16 +52,17 @@ def convert_elf_to_raw(): def is_load_segment(s): return s.type == lief.ELF.Segment.TYPE.LOAD - segments = list(filter(is_load_segment, core.segments)) - elf_file = ELF_FILENAME.open('rb') - raw_file = RAW_FILENAME.open('wb') + segments = filter(is_load_segment, core.segments) + + elf_file = ELF_FILENAME.open("rb") + raw_file = RAW_FILENAME.open("wb") pos = 0 for s in segments: pad_len = s.physical_address - pos for _ in range(pad_len // dump_file.PAGE_SIZE): - raw_file.write(b'\0' * dump_file.PAGE_SIZE) + raw_file.write(b"\0" * dump_file.PAGE_SIZE) pos += dump_file.PAGE_SIZE elf_file.seek(s.file_offset) From a3ff8bf68b6f2e320c402445de02b2112b3d8779 Mon Sep 17 00:00:00 2001 From: 0vercl0k <1476421+0vercl0k@users.noreply.github.com> Date: Sat, 1 Feb 2025 09:08:02 -0800 Subject: [PATCH 3/5] update docs w/ new output --- linux_mode/README.md | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/linux_mode/README.md b/linux_mode/README.md index 7b59d55..5a14dcd 100644 --- a/linux_mode/README.md +++ b/linux_mode/README.md @@ -140,19 +140,23 @@ The second tab will detect once the first tab has finished executing the `cpu` c Once the second tab indicates that snapshotting is complete, the target VM can be terminated. ```console -In the Qemu tab, press Ctrl+C, run the `cpu` command -Detected cpu registers dumped to regs.json +In the QEMU tab, press Ctrl+C, run the `cpu` command +Detected cpu registers dumped to 'regs.json' Connecting to Qemu monitor at localhost:55555 Connected -Instructing Qemu to dump physical memory to file raw +Instructing Qemu to dump physical memory into 'mem.elf' +Done +Converting elf file 'mem.elf' to raw file 'raw' +The current binary doesn't have a section header +The current binary doesn't have a section header Done -Converting raw file raw to dump file /wtf/targets/linux_crash_test/state/mem.dmp +Converting raw file 'raw' to dump file '/home/over/wtf/targets/linux_crash_test/state/mem.dmp' Done -mv regs.json /wtf/targets/linux_crash_test/state/regs.json -mv symbol-store.json /wtf/targets/linux_crash_test/state/symbol-store.json +mv 'regs.json' '/home/over/wtf/targets/linux_crash_test/state/regs.json' +mv 'symbol-store.json' '/home/over/wtf/targets/linux_crash_test/state/symbol-store.json' Snapshotting complete -Breakpoint 1, 0x0000555555555189 in do_crash_test () +Breakpoint 1, 0x00005555555551e9 in do_crash_test () (gdb) ``` From d3c0a36242c350594d545d32de7c8b6890443c54 Mon Sep 17 00:00:00 2001 From: 0vercl0k <1476421+0vercl0k@users.noreply.github.com> Date: Sat, 1 Feb 2025 09:08:50 -0800 Subject: [PATCH 4/5] oops --- linux_mode/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/linux_mode/README.md b/linux_mode/README.md index 5a14dcd..03a2614 100644 --- a/linux_mode/README.md +++ b/linux_mode/README.md @@ -150,10 +150,10 @@ Converting elf file 'mem.elf' to raw file 'raw' The current binary doesn't have a section header The current binary doesn't have a section header Done -Converting raw file 'raw' to dump file '/home/over/wtf/targets/linux_crash_test/state/mem.dmp' +Converting raw file 'raw' to dump file '/wtf/targets/linux_crash_test/state/mem.dmp' Done -mv 'regs.json' '/home/over/wtf/targets/linux_crash_test/state/regs.json' -mv 'symbol-store.json' '/home/over/wtf/targets/linux_crash_test/state/symbol-store.json' +mv 'regs.json' '/wtf/targets/linux_crash_test/state/regs.json' +mv 'symbol-store.json' '/wtf/targets/linux_crash_test/state/symbol-store.json' Snapshotting complete Breakpoint 1, 0x00005555555551e9 in do_crash_test () From d25b61471651e11ea78a0d349d1753c57e716a3a Mon Sep 17 00:00:00 2001 From: 0vercl0k <1476421+0vercl0k@users.noreply.github.com> Date: Sat, 1 Feb 2025 09:24:01 -0800 Subject: [PATCH 5/5] nit --- linux_mode/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linux_mode/README.md b/linux_mode/README.md index 03a2614..51062be 100644 --- a/linux_mode/README.md +++ b/linux_mode/README.md @@ -181,7 +181,7 @@ user@pc:/wtf/targets/linux_crash_test$ ../../src/build/wtf master --name linux_c Run the fuzzee and note that crashes are found quickly. ```console -user@pc:/wtf/targets/linux_crash_test$ ../../src/build/wtf fuzz --backend=bochscpu --name linux_crash_test +user@pc:/wtf/targets/linux_crash_test$ ../../src/build/wtf fuzz --name linux_crash_test Setting @fptw to 0xff'ff. The debugger instance is loaded with 16 items Setting debug register status to zero.