Source-Level vs. Assembly-Level Debuggers

Both source-level and assembly-level debuggers are used for debugging programs, but they differ significantly in what they show and how they are used. Here’s a side-by-side comparison:

FeatureSource- LevelAssembly-Level
ViewDisplays the original source code (e.g., C/C++, Python).Displays machine instructions (assembly code).
SymbolsUses variable/function names, line numbers, and data types.Often lacks symbols; uses addresses and offsets.
Ease of UseUser-friendly, intuitive; ideal for high-level debugging.Requires understanding of assembly and processor architecture.
BreakpointsSet breakpoints on source lines or function names.Set breakpoints at instruction addresses.
VariablesShows variable names, values, and types directly.Shows registers, memory, and stack contents, not high-level variables.
Use CaseBest for developers with access to source code and debugging high-level logic.Ideal for reverse engineering, malware analysis, kernel debugging, or when source is unavailable.
ToolsExamples: GDB (with symbols), Visual Studio Debugger, LLDB, WinDbg (with PDBs).Examples: OllyDbg, x64dbg, IDA Pro (debugger), Ghidra (runtime), WinDbg (assembly view).
Strengths & LimitationsCannot debug stripped binaries or deeply obfuscated code.Full control; reveals exact program behavior at runtime.
Common inApplication development, software testing.Low-level debugging, exploit development, binary analysis.

When to Use Each

  • Use Source-Level Debugger when:
    • You are developing or debugging your own program.
    • You have access to source and debug symbols.
    • You need to debug high-level logic or variable state.
  • Use Assembly-Level Debugger when:
    • You’re analyzing compiled code without source.
    • You’re doing reverse engineering or malware analysis.
    • You need precise control and visibility over low-level behavior.

Kernel-Mode vs. User-Mode Debugging

When debugging software on Windows or other operating systems, understanding the difference between kernel-mode and user-mode debugging especially in reverse engineering, driver development, or low-level systems work.

Basic Definitions

Debugging ModeDescription
User-ModeDebugging regular applications that run in user space, isolated from the OS kernel.
Kernel-ModeDebugging code that runs in the operating system core, such as device drivers or the kernel itself.

Key Differences

FeatureUser-Mode DebuggingKernel-Mode Debugging
Code Being DebuggedApplications (.exe, .dll)OS components, device drivers, interrupts, kernel data structures
Memory AccessCan access only user space memoryCan access entire system memory, including user and kernel memory
Crash ImpactCrashes only the target applicationCan crash or hang the entire system (Blue Screen of Death - BSOD)
Debugger ToolsGDB, Visual Studio Debugger, x64dbg, OllyDbgWinDbg (kernel mode), GDB with qemu/vm, VMware GDB Stub
SymbolsUses PDBs or DWARF for app symbolsUses Microsoft symbol server, custom PDBs for drivers
Setup ComplexitySimple. Can debug locallyComplex. Often requires remote debugging, VMs, or serial pipes
Use CasesMalware analysis, app bugs, performance tuningDriver development, rootkit detection, OS-level reverse engineering
Privileges RequiredStandard user/admin (depends on debugger)Requires admin privileges and kernel debugger configuration
IsolationRuns in a safe, isolated modeRuns in privileged mode; debugging errors can cause serious issues

When to Use

  • User-Mode Debugging:
    • Debugging app crashes, logic errors, or malware with no kernel-level component.
    • You have the executable or source code of the application.
  • Kernel-Mode Debugging:
    • Debugging device drivers, kernel exploits, or reverse engineering rootkits.
    • You need to inspect kernel memory, system calls, or privileged instructions.

Degugging

Single-Stepping

Single-stepping is a core debugging feature that allows you to execute one instruction or line of code at a time, so you can closely observe what your program is doing at each step.

It’s used to:

  • Track how variables change.
  • See which paths the program takes (especially in conditions or loops).
  • Catch the exact instruction where an error or crash happens.
  • Understand complex or unknown code, like in reverse engineering.

Example

(C Code)

int a = 5;
int b = 10;
int c = a + b;  // ← break here and start stepping
printf("%d\n", c);

If you single-step here:

  1. You’ll see a = 5 assigned.
  2. Then b = 10.
  3. Then c = 15 gets computed.
  4. Then the output happens.
    Assembly:
    In tools like x64dbg or WinDbg, single-stepping is even finer-grained:
mov eax, 5
add eax, 3
call printf

You can step through each instruction, watching how EAX changes in the CPU register window.


**Stepping-Over and Stepping-Into

When Single-Stepping through code, especially in reverse engineering or software debugging, it’s important to understand how you step through functions:

Step-Into

  • What it does: Enters the function being called.
  • When to use:
    • You want to analyze the inner workings of a function.
    • You suspect that the function contains important or malicious behavior.
  • What you’ll see: The very first instruction of the called function.

Example:

doWork(); // ← Step Into

You’ll land at the first instruction inside doWork().

Step-Over

  • What it does: Executes the function without entering it.
  • When to use:
    • You trust the function or know it’s not critical (e.g., printf, LoadLibrary).
    • You want to save time by skipping irrelevant internal operations.
  • What you’ll see: The instruction right after the function call.

Example:

doWork(); // ← Step Over

You’ll skip over the function internals and land after the call.

Risks of Stepping Over

  • Missed logic: You might miss malware or custom behavior hidden inside a function.
  • Functions that never return: If a function like ExitProcess() or a malicious loop is stepped over, your debugger may lose control, and you’ll be stuck.

Pausing Execution with Breakpoints

Breakpoints are critical tools in any debugger that allow you to pause program execution and examine the current state—registers, memory, stack, etc.—at a precise location.

Why Use Breakpoints?

  • While a program runs, registers and memory are constantly changing.
  • A breakpoint halts execution at a specific instruction so you can analyze what’s happening at that moment.
  • Especially useful when you want to inspect:
    • What values are in registers (like EAX)
    • What arguments are being passed to a function (like CreateFileW)
    • What data is about to be encrypted or sent over the network

Types of Breakpoints

1. Software Execution Breakpoints

  • How it works: Replaces the first byte of an instruction with 0xCC (INT 3).
  • Purpose: Stops execution when a specific instruction is executed.
  • Example:
    • In disassembly: 00401130 push ebp becomes 00401130 CC in memory.
  • Risks:
    • Can be detected or altered by anti-debugging techniques.
    • If the code modifies itself or verifies its code section, the 0xCC will cause issues.

2. Hardware Execution Breakpoints

  • How it works: Uses special debug registers (DR0–DR3) in the CPU to monitor for a specific address.
  • Benefits:
    • Doesn’t modify the program code.
    • Can watch for read/write or execution.
    • Perfect for self-modifying or anti-debugging code.
  • Limitations:
    • Only 4 hardware breakpoints available.
    • Can be manipulated by malware using debug register access.
      🛡 Protection: Set the General Detect flag in DR7 to trap unauthorized debug register modifications.

3. Conditional Breakpoints

  • How it works: Triggers only if a specified condition is true.
  • Example:
    • Break only if GetProcAddress("RegSetValue") is called.
    • Condition: check the value at the top of the stack or in a register.
  • Downside:
    • Slows down execution significantly if overused.