Metadata-Version: 2.4
Name: gravedigger
Version: 0.1.0
Classifier: Environment :: Console
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Rust
Classifier: Topic :: Software Development :: Debuggers
Classifier: Topic :: Security
License-File: LICENSE
Summary: A from-scratch x86-64 disassembler and emulator-backed debugger - no capstone, no objdump, no ptrace. Ships the Rust binary as a console script, ruff-style.
Keywords: disassembler,debugger,x86-64,emulator,reverse-engineering,cli
Author: Makeph
License: MIT
Requires-Python: >=3.8
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
Project-URL: Homepage, https://github.com/Makeph/gravedigger
Project-URL: Issues, https://github.com/Makeph/gravedigger/issues
Project-URL: Repository, https://github.com/Makeph/gravedigger

# gravedigger

**A from-scratch x86-64 disassembler and emulator-backed debugger. No capstone, no objdump, no ptrace — it decodes machine code by hand and *runs* it in its own software CPU.**

Most debuggers borrow a corpse: they attach to a live process through the OS
(`ptrace`, the Windows debug API) and lean on a library like capstone to read
the bytes. gravedigger digs its own grave. It decodes x86-64 from raw opcodes,
parses ELF and PE containers itself, and executes instructions in a from-scratch
emulator — so you can single-step, breakpoint and inspect **any** x86-64 binary,
offline, on any OS, with **zero dependencies**.

```console
$ gravedigger info ./a.out
format : Pe
arch   : X86_64
entry  : 0x140073550

sections:
  name                     addr             size  flags
  .text          0000000140001000        476160  x
  .rdata         0000000140076000        130048
  .data          0000000140096000           512  w
```

## What it does

- **Disassembles** x86-64 the hard way — legacy prefixes, REX, ModRM/SIB,
  RIP-relative addressing, one- and two-byte (`0f`) opcode maps — and prints
  clean Intel syntax.
- **Follows control flow.** Recursive descent walks from the entry point and
  every function symbol through calls and jumps, so you get a real listing
  instead of a linear sweep that trips over data and padding.
- **Loads ELF64 and PE32+** natively (and raw shellcode blobs at a chosen base).
- **Runs the code.** A hand-written CPU — 16 registers, RFLAGS with correct
  arithmetic/overflow/carry semantics, a segmented address space with a mapped
  stack — executes instructions one at a time. Calls push, rets pop, syscalls
  surface as stop events. No host process is ever touched.
- **Debugs interactively.** Breakpoints, step, continue, register/memory/stack
  inspection, live disassembly — all driven by the emulator.

## Disassemble

```console
$ gravedigger disasm ./a.out --entry main
main:
  0000000140012110:  48 83 ec 28               sub     rsp, 0x28
  0000000140012114:  49 89 d0                  mov     r8, rdx
  0000000140012117:  48 63 d1                  movsxd  rdx, ecx
  000000014001211a:  48 8d 0d 8f ed ff ff      lea     rcx, [rip - 0x1271]
  0000000140012121:  45 31 c9                  xor     r9d, r9d
  0000000140012124:  e8 97 17 00 00            call    0x1400138c0
```

Raw machine code works too:

```console
$ gravedigger disasm shellcode.bin --raw --base 0x400000
```

## Debug

```console
$ gravedigger debug ./a.out --entry main
gravedigger debugger -- 'help' for commands, 'q' to quit
=> 0000000140073550:  48 83 ec 28   sub     rsp, 0x28
(grave) s
=> 0000000140073554:  e8 77 02 00 00   call    0x1400737d0
(grave) s
=> 00000001400737d6:  48 8b ec         mov     rbp, rsp     ; stepped INTO the call
(grave) stack 3
stack (rsp = 00007fffffffeec0):
  00007fffffffeec0: 0000000000000000
  00007fffffffeec8: 0000000140073559          <- return address, one past the call
  00007fffffffeed0: 0000000000000000
```

That return address on the stack isn't printed by an OS — gravedigger's CPU
pushed it when it executed the `call`.

| command      | does                                            |
|--------------|-------------------------------------------------|
| `s [n]`      | step n instructions (Enter repeats)             |
| `c`          | continue to breakpoint / halt / top-level `ret` |
| `b <t>`      | breakpoint at `0xADDR`, decimal, or a symbol    |
| `d <t>`      | delete breakpoint                               |
| `r`          | dump registers and flags                        |
| `x <t> [n]`  | examine memory                                  |
| `stack [n]`  | dump the stack                                  |
| `dis [n]`    | disassemble from `rip`                          |

## Build

```console
$ cargo build --release
$ cargo test          # 12 tests: golden decode vectors + end-to-end emulation
```

No dependencies. The whole thing is `std` and hand-rolled tables.

## How it fits together

```
   bytes ──▶ x86::decode ──▶ Insn ──┬──▶ x86::fmt  ──▶ Intel text
                                     └──▶ emu::exec ──▶ CPU state / stops
   file  ──▶ loader (elf/pe/raw) ──▶ Program ──▶ analysis (recursive descent)
                                              ──▶ emu (mapped memory + stack)
                                              ──▶ dbg (interactive REPL)
```

See [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) for the full tour.

## Limitations

v0.1 covers the integer/scalar subset a modern compiler actually emits: mov,
lea, the full arithmetic/logic set, mul/div, shifts, cmov/setcc, push/pop, call,
ret, leave, conditional and indirect branches, movzx/movsx/movsxd, syscall.
Not yet: SSE/AVX/x87, and `cdqe`/`cqo` assume 64-bit width. The decoder emits
`(bad)` for anything it doesn't know rather than guessing.

## License

MIT. Dig responsibly.

