Skip to content
YugVarshneyPublic

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

MiniDbg — A Lightweight Linux x86-64 Debugger

MiniDbg is a minimalist Linux x86-64 debugger written in modern C++17 using the ptrace system call interface. It provides instruction-level stepping, software breakpoints via INT 3 (0xCC), general-purpose register inspection and modification, arbitrary process memory reading and writing, zero-dependency ELF64 symbol table parsing, and frame-pointer-based stack backtracing.


1. What MiniDbg Is

MiniDbg is an educational, systems-focused debugger designed to illustrate the fundamentals of low-level process control, x86-64 CPU architecture, Linux system programming, and ELF binary structures.

It avoids external framework abstractions, implementing the core mechanics of debugging directly against Linux kernel APIs and CPU architecture specifications.


2. Why I Built It

Debuggers like GDB and LLDB are large codebases comprising hundreds of thousands of lines of code. Consequently, the actual primitives of debugging—how breakpoints alter machine instructions in memory, why the instruction pointer register must be rewound after INT 3, and how stack frames are traversed—are often obscured behind layers of abstraction.

I built MiniDbg to:

  • Deeply understand how a parent process traces and controls a child process at the operating system boundary.
  • Implement software breakpoints directly by manipulating raw instruction bytes.
  • Understand the low-level lifecycle of registers, process virtual memory, and the x86-64 ABI.
  • Parse ELF64 headers and symbol tables directly without external libraries.
  • Prepare for System Software, Compiler, and Debugger engineering interviews by building a project where every line of code can be explained and defended from first principles.

3. Features

  • Process Tracing: Spawns and attaches to target binaries using fork(), ptrace(PTRACE_TRACEME), and execvp().
  • Execution Control: Full support for continue (PTRACE_CONT) and single-step machine instruction execution (PTRACE_SINGLESTEP).
  • Software Breakpoints: Breakpoints via 0xCC (INT 3) insertion, automatic RIP rewinding, and temporary single-step restoration (step-over logic).
  • Register Inspection & Modification: Reads and writes all x86-64 general-purpose registers (rax, rbx, rcx, rdx, rsi, rdi, rbp, rsp, rip, r8–r15, eflags) via PTRACE_GETREGS and PTRACE_SETREGS.
  • Memory Inspection & Modification: Reads and writes 64-bit words in the target process virtual address space via PTRACE_PEEKDATA and PTRACE_POKEDATA.
  • ELF64 Symbol Resolution: Direct parser for .symtab, .strtab, and section headers, enabling symbolic breakpoints (e.g. break main) and symbol backtrace resolution.
  • Frame-Pointer Backtracing: Call stack unwinding along the RBP frame-pointer chain for binaries compiled with -fno-omit-frame-pointer.
  • Interactive REPL: Lightweight, intuitive command-line interface.

4. Architecture

Debugger Process
      |
    ptrace
      |
      v
Target Process
      |
      +--> Registers (PTRACE_GETREGS / PTRACE_SETREGS)
      +--> Memory (PTRACE_PEEKDATA / PTRACE_POKEDATA)
      +--> Execution Control (PTRACE_CONT / PTRACE_SINGLESTEP)
      +--> SIGTRAP / Breakpoints (INT 3 0xCC)

Directory Structure

MiniDbg/
├── CMakeLists.txt              # CMake build configuration
├── Makefile                    # Standard Unix Makefile
├── README.md                   # Technical documentation
├── docs/
│   ├── architecture.md         # In-depth architectural & hardware explanations
│   ├── ptrace_notes.md         # Linux ptrace syscall reference & states
│   ├── elf_notes.md            # ELF64 binary format, symbols, ASLR & PIE
│   └── interview_questions.md  # 20 technical interview Q&A pairs
├── include/
│   ├── Platform.h              # Linux x86-64 ABI definitions & fallbacks
│   ├── RegisterUtils.h         # Register mappings and table formatting
│   ├── MemoryUtils.h           # Process memory peek/poke utilities
│   ├── Breakpoint.h            # Software breakpoint byte management
│   ├── ElfParser.h             # ELF64 header, section, and symbol parser
│   └── Debugger.h              # Debugger engine and REPL controller
├── src/
│   ├── RegisterUtils.cpp
│   ├── MemoryUtils.cpp
│   ├── Breakpoint.cpp
│   ├── ElfParser.cpp
│   ├── Debugger.cpp
│   └── main.cpp                # CLI entry point
└── tests/
    ├── sample_programs/        # Sample C++ test targets
    │   ├── 01_simple.cpp
    │   ├── 02_loop.cpp
    │   └── 03_backtrace.cpp
    ├── test_registers.cpp
    ├── test_memory.cpp
    ├── test_breakpoints.cpp
    ├── test_elf.cpp
    ├── test_debugger_unit.cpp
    └── run_all_tests.py        # Automated test runner

5. How Linux Debugging Works

  1. Spawning the Target: The debugger calls fork(). In the child process:

    ptrace(PTRACE_TRACEME, 0, nullptr, nullptr);
    execvp(program, args);

    The kernel flags the process for tracing. When execve replaces the image, the kernel halts the child before its first user instruction and raises SIGTRAP.

  2. Parent Synchronization: The parent debugger calls waitpid(pid, &status, 0). Once the child halts on SIGTRAP, the debugger can set breakpoints or inspect registers.


6. Building

Requirements

  • Linux x86-64
  • GCC or Clang with C++17 support
  • Make or CMake >= 3.14

Using Make

cd MiniDbg
make

Using CMake

cd MiniDbg
mkdir build && cd build
cmake ..
make

7. Usage

Launch MiniDbg with the target executable:

./minidbg ./tests/sample_programs/01_simple

Available Commands

Command Shorthand Description
run / continue c Continue execution of the target process
step s Single-step one machine instruction
break <addr|sym> b Set a breakpoint at address or symbol name
delete <id|addr> Delete a breakpoint by ID or address
info breakpoints List all active breakpoints
registers Display all general-purpose registers
reg <name> Display value of a specific register (e.g. reg rip)
setreg <name> <val> Modify a register value
x <addr> [count] Examine memory words at address
setmem <addr> <val> Write 64-bit word to memory
symbols Dump parsed ELF symbol table
backtrace bt Print frame-pointer stack backtrace
help Show command reference
quit q Exit the debugger

8. Breakpoint Implementation

Software breakpoints work by modifying executable code in memory:

Normal instruction
       ↓
Replace first byte with INT3 (0xCC)
       ↓
Target executes INT3
       ↓
SIGTRAP
       ↓
Debugger restores original byte
       ↓
RIP = RIP - 1
       ↓
Single-step original instruction
       ↓
Reinsert INT3

Step-Over Mechanics

  1. On x86-64, executing 0xCC advances RIP by 1 byte before raising SIGTRAP.
  2. When the debugger halts, it inspects RIP - 1. If an active breakpoint exists at that address, the debugger decrements RIP by 1 using PTRACE_SETREGS.
  3. To resume past the breakpoint:
    • Restore the original byte via PTRACE_POKEDATA.
    • Single-step the original instruction via PTRACE_SINGLESTEP.
    • Reinsert 0xCC via PTRACE_POKEDATA.
    • Continue execution via PTRACE_CONT.

9. ELF Symbol Lookup

MiniDbg reads the target ELF binary directly from disk:

  1. Validates the ELF magic number (\x7fELF) in Elf64_Ehdr.
  2. Locates the Section Header Table at e_shoff.
  3. Finds .symtab (symbol entries) and .strtab (string table).
  4. Iterates through all Elf64_Sym structs, extracting function addresses (st_value), sizes (st_size), and names from the string table offset (st_name).
  5. Supports resolving symbol names to addresses for setting breakpoints (break main), and reverse-resolving addresses to symbol names for backtraces.

10. Register & Memory Inspection

  • Registers: Extracted via PTRACE_GETREGS into user_regs_struct. Registers can be read or modified individually.
  • Memory: Read using PTRACE_PEEKDATA and written using PTRACE_POKEDATA. Even though the target .text segment is mapped read-only in user space, the Linux kernel overrides write protections for ptrace calls, enabling breakpoint injection.

11. Stack Backtrace

Backtracing walks the linked list of stack frames along %rbp:

  1. Current frame: Instruction pointer in %rip.
  2. Stack walk:
    • *(uint64_t*)rbp yields the caller's saved %rbp.
    • *(uint64_t*)(rbp + 8) yields the caller's return address.
  3. Each return address is resolved to its enclosing function symbol via ElfParser.

Binaries must be compiled with -fno-omit-frame-pointer for this frame chain to be maintained.


12. Testing

Run the automated test suite:

python3 tests/run_all_tests.py

This executes all unit tests:

  • test_registers: Verifies register name mapping, accessors, and formatting.
  • test_memory: Verifies word-aligned memory reading, writing, and byte extraction.
  • test_breakpoints: Verifies byte preservation, substitution with 0xCC, and restoration.
  • test_elf: Verifies synthetic ELF64 parsing, symbol lookups, and reverse address resolution.
  • test_debugger_unit: Verifies breakpoint table deduplication, ID management, and lifecycle.

13. Limitations

  1. Non-PIE binaries: Test binaries should be compiled with -no-pie. For Position Independent Executables with ASLR enabled, runtime base addresses must be extracted from /proc/<pid>/maps.
  2. Frame pointers required: Backtracing relies on %rbp frame chains (-fno-omit-frame-pointer). It does not parse DWARF CFI (.eh_frame).
  3. No DWARF line tables: Stepping operates at the instruction level, not source-line level.
  4. Single-threaded only: Does not handle multithreaded clone/thread tracing events.

14. Future Work

  • ASLR & PIE Base Address Resolution: Parse /proc/<pid>/maps dynamically on attach to calculate relocations.
  • DWARF Line Number Parsing: Parse .debug_line to support source-level stepping (next / step).
  • Hardware Breakpoints: Utilize x86-64 debug registers (DR0–DR3, DR7) for data watchpoints without modifying memory bytes.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages