DEBUGGING

Debugging is more of a method used during dynamic analysis and deobfuscation, rather than a separate or sequential step

The debugging process generally follows four main phases. First, you set breakpoints at strategic locations, such as function entries or critical instructions, to pause execution where closer inspection is needed. Next, you examine and analyze the program state by inspecting registers, memory, variables, and the stack to understand what is happening at that point in execution. From there, you step through the program—either instruction by instruction or line by line—to observe control flow, function calls, and how data changes in real time. Finally, you may modify the program’s state directly within the debugger, altering register values, memory contents, or execution flow to test hypotheses, apply quick patches, or bypass errors. Together, these phases provide a structured cycle for isolating bugs, understanding behavior, and experimenting with potential fixes.

STEP 1: VIEW FUNCTION / VARIABLE ADDRESS

STEP 2: SET BREAKPOINTS

This method can be done in two ways and depends whether the binary is stripped or not.

STEP 3: RUN THROUGH AND ANALYZE

gef>  b _start
 Breakpoint 1 at 0x401000
gef>  r
 Starting program: ./helloWorld 

 Breakpoint 1, 0x0000000000401000 in _start ()
 [ Legend: Modified register | Code | Heap | Stack | String ]
 ───────────────────────────────────────────────────────────────────────────────────── registers ────
 $rax   : 0x0               
 $rbx   : 0x0               
 $rcx   : 0x0               
 $rdx   : 0x0               
 $rsp   : 0x00007fffffffe310  →  0x0000000000000001
 $rbp   : 0x0               
 $rsi   : 0x0               
 $rdi   : 0x0               
 $rip   : 0x0000000000401000  →  <_start+0> mov eax, 0x1
 ...SNIP...
 ───────────────────────────────────────────────────────────────────────────────────────── stack ────
 0x00007fffffffe310│+0x0000: 0x0000000000000001	 ← $rsp
 0x00007fffffffe318│+0x0008: 0x00007fffffffe5a0  →  "./helloWorld"
 ...SNIP...
 ─────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
      0x400ffa                  add    BYTE PTR [rax], al
      0x400ffc                  add    BYTE PTR [rax], al
      0x400ffe                  add    BYTE PTR [rax], al
  →   0x401000 <_start+0>       mov    eax, 0x1
      0x401005 <_start+5>       mov    edi, 0x1
      0x40100a <_start+10>      movabs rsi, 0x402000
      0x401014 <_start+20>      mov    edx, 0x12
      0x401019 <_start+25>      syscall 
      0x40101b <_start+27>      mov    eax, 0x3c
 ─────────────────────────────────────────────────────────────────────────────────────── threads ────
 [#0] Id 1, Name: "helloWorld", stopped 0x401000 in _start (), reason: BREAKPOINT
 ───────────────────────────────────────────────────────────────────────────────────────── trace ────
 [#0] 0x401000 → _start()
 ────────────────────────────────────────────────────────────────────────────────────────────────────

 * from here another breakpoint can be set
    - e.g., if another breakpoint is required at "_start+10", use either
      b *_start+10 or b *0x40100a 
       - use the "continue" or "c" command to continue to the next breakpoint
       
gef> b *0x40100a
 ...

gef> continue
 ...
 
gef>  x/4ig $rip
 => 0x401000 <_start>:	mov    eax,0x1
    0x401005 <_start+5>:	mov    edi,0x1
    0x40100a <_start+10>:	movabs rsi,0x402000
    0x401014 <_start+20>:	mov    edx,0x12
    
 *  4 refers to the count, i for the format, and g for the size
 
 * use the following to perform manual analysis of values in registers and addresses
    - format: x/FMT ADDRESS
       - ADDRESS is the address or register to be examined
       - FMT refers to the examine format
          - FMT parts
             - count: is the number of times to repeat the examination 2, 3, 10
             - format representation: x(hex), s(string), i(instruction)
             - size of memory to examine: b(byte), h(halfword), w(word), 
               g(giant, 8 bytes)

STEP 4: STEPPING

This steps through the program one instruction or line of code at a time. The stepi or si cmd will step through the assembly instructions one by one. It is the smallest level of steps possible while debugging

gef>  si
 0x0000000000401005 in _start ()
    0x400fff                  add    BYTE PTR [rax+0x1], bh
  →   0x401005 <_start+5>       mov    edi, 0x1
      0x40100a <_start+10>      movabs rsi, 0x402000
      0x401014 <_start+20>      mov    edx, 0x12
      0x401019 <_start+25>      syscall 
 ─────────────────────────────────────────────────────────────────────────────────────── threads ────
      [#0] Id 1, Name: "helloWorld", stopped 0x401005 in _start (), reason: SINGLE STEP

 * the instruction shown with the -> symbol is where , and it has not yet 
   been processed

STEP 5: MODIFICATION

This step is used to modify values in registers and addresses at a certain point of execution. It assists in seeing how this would affect the execution of the program

ADDRESS

gef>  b _start
 Breakpoint 1 at 0x401000
 
gef>  r
 Starting program: ./helloWorld 

 Breakpoint 1, 0x0000000000401000 in _start ()
 [ Legend: Modified register | Code | Heap | Stack | String ]
 ───────────────────────────────────────────────────────────────────────────────────── registers ────
 $rax   : 0x0               
 $rbx   : 0x0               
 $rcx   : 0x0               
 $rdx   : 0x0               
 $rsp   : 0x00007fffffffe310  →  0x0000000000000001
 $rbp   : 0x0               
 $rsi   : 0x0               
 $rdi   : 0x0               
 $rip   : 0x0000000000401000  →  <_start+0> mov eax, 0x1
 ...SNIP...
 ───────────────────────────────────────────────────────────────────────────────────────── stack ────
 0x00007fffffffe310│+0x0000: 0x0000000000000001	 ← $rsp
 0x00007fffffffe318│+0x0008: 0x00007fffffffe5a0  →  "./helloWorld"
 ...SNIP...
 ─────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
      0x400ffa                  add    BYTE PTR [rax], al
      0x400ffc                  add    BYTE PTR [rax], al
      0x400ffe                  add    BYTE PTR [rax], al
  →   0x401000 <_start+0>       mov    eax, 0x1
      0x401005 <_start+5>       mov    edi, 0x1
      0x40100a <_start+10>      movabs rsi, 0x402000
      0x401014 <_start+20>      mov    edx, 0x12
      0x401019 <_start+25>      syscall 
      0x40101b <_start+27>      mov    eax, 0x3c
      0x401020 <_start+32>      mov    edi, 0x0
      0x401025 <_start+37>      syscall 
 ─────────────────────────────────────────────────────────────────────────────────────── threads ────
 [#0] Id 1, Name: "helloWorld", stopped 0x401000 in _start (), reason: BREAKPOINT
 ───────────────────────────────────────────────────────────────────────────────────────── trace ────
 [#0] 0x401000 → _start()
 ────────────────────────────────────────────────────────────────────────────────────────────────────
 
gef>  break *0x401019

Breakpoint 1 at 0x401019
gef>  r
gef>  patch string 0x402000 "Patched!\\x0a"
gef>  c

Continuing.
Patched!

* in GDB, the "set" command is used to modify values. however, the "patch" command
  in GEF makes this step much easier.
  
* the \x0a adds a new line to the string 

REGISTER

Last updated