Athena Wiki

Dynamic Analysis for Reverse Engineering

reversingintermediate

Run and debug binaries to understand their behavior - GDB, strace, ltrace, and angr.

reversingdynamic-analysisgdbgefstraceltraceangrdebugging

Dynamic Analysis for Reverse Engineering

Dynamic analysis runs the binary (in a safe environment) and observes its behavior. It complements static analysis — especially when code is obfuscated, packed, or when you need to see actual runtime values. The two most powerful dynamic techniques are ltrace (see library calls with arguments) and GDB breakpoints at comparison functions.

Always run unknown CTF binaries inside a VM. Never execute them on your host machine — even "safe" CTF binaries can be malicious.


Analysis Flow

Mermaid diagram

Before You Run Anything

Set Up a Safe Environment

Always run in a VM — never on your host. Snapshot the VM before running anything.

Check Architecture & Dependencies

file ./challenge       # architecture, 32/64-bit, linked?
ldd ./challenge        # which libraries are needed?

Optionally Disable ASLR

echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
# Restore: echo 2 | sudo tee /proc/sys/kernel/randomize_va_space

Disabling ASLR makes addresses predictable, so breakpoints at fixed addresses work every run.

Start with Lightweight Tools

Run strace and ltrace first — they often reveal the answer in seconds with no setup.


strace — System Call Tracer

strace intercepts all Linux syscalls. Great for understanding what files a binary reads and how it compares input.

strace ./challenge                        # basic trace
strace -e trace=file ./challenge          # only file-related calls
strace -e trace=read,write ./challenge    # read/write only
echo "test_input" | strace ./challenge    # trace with input

Common things to look for:

open("/flag.txt", ...)         → binary reads a flag file
read(3, "flag{...}", ...)      → flag is read into memory
write(1, "Wrong!\n", ...)      → output indicates wrong input

ltrace — Library Call Tracer

ltrace intercepts calls to shared library functions (libc). Excellent for crackmes.

ltrace ./challenge
# Common pattern: see strcmp with the expected password
# strcmp("your_input", "s3cr3t_p4ss") = -1

ltrace is often the fastest solve for beginner crackmes. The second argument to strcmp or memcmp is usually the expected answer — it appears directly in the output.


GDB with GEF/PEDA/pwndbg

gdb ./challenge

# Breakpoints
(gdb) break main                # breakpoint at main
(gdb) break *0x401234           # breakpoint at address
(gdb) break strcmp              # breakpoint at libc strcmp

# Execution control
(gdb) run                       # run the binary
(gdb) run < input.txt           # run with file as stdin
(gdb) next                      # step over
(gdb) step                      # step into
(gdb) continue                  # continue to next breakpoint

# Inspection
(gdb) info registers            # show all registers
(gdb) x/20gx $rsp               # examine stack (20 qwords)
(gdb) x/s $rdi                  # first arg as string
(gdb) x/s $rsi                  # second arg as string

# Modification
(gdb) set $rip = 0x401234       # change instruction pointer
(gdb) set $rax = 0              # force return value

Patching Binaries

gdb ./challenge
(gdb) break *0x401234
(gdb) run
(gdb) set $eax = 0       # force a comparison result
(gdb) jump *0x401256     # jump to a different address

Runtime patches don't modify the file on disk.


angr — Symbolic Execution

angr finds inputs that reach a desired state automatically. Great for crackmes with complex input validation.

import angr

proj = angr.Project('./crackme', auto_load_libs=False)
initial_state = proj.factory.entry_state()
simgr = proj.factory.simulation_manager(initial_state)

simgr.explore(find=0x4012a0, avoid=0x4012b5)

if simgr.found:
    sol = simgr.found[0]
    print(sol.posix.dumps(0))  # print stdin that reaches 'find'

angr is most useful when input validation is complex (multiple loops, math, mixed comparisons). Find the "Correct!" address with strings -t x ./crackme | grep -i correct, then use that as find=.


Checklist

  • Run in a VM (never on host)
  • ltrace — library calls, strcmp arguments often reveal password directly
  • strace — check if binary opens a flag file, reads from a specific path
  • GDB: break at strcmp/memcmp → x/s $rsi shows expected value
  • GDB: break at comparison instructions → inspect registers
  • Patch JNE→JMP or conditional to NOP if needed
  • Use angr if input space is large and manual analysis is slow

Last updated on

On this page