Static vs Dynamic Analysis in Reverse Engineering

Article · RE

Handed a stripped binary with no source, no comments, and a prompt that just says "find the flag," there are really only two ways in: read the binary without running it, or run it and watch what it does. Most solvers end up doing both, but knowing which one to reach for first — and when to switch — is what separates a fast solve from an hour lost in a disassembler.

Static analysis: reading without running

Static analysis means examining the binary's code and structure without executing it — disassembling it, decompiling it, and reading the resulting logic like source code. Tools like Ghidra (free) or IDA turn machine code back into readable pseudocode, letting you trace a function's logic, spot string comparisons, and follow control flow without ever running the program:

strings challenge | grep -i flag
file challenge
objdump -d challenge | less

It's the right first move whenever the binary might be hostile to run directly (unknown malware samples, anything you don't trust on your own machine) or when the logic is simple enough to read straight through — a basic serial-number check or an XOR loop is often faster to solve on the page than by stepping through it live.

Dynamic analysis: watching it run

Dynamic analysis means running the binary — usually inside a debugger like gdb (often with the pwndbg or GEF extensions) or under a sandbox — and observing its actual behavior: register values, memory contents, and the real execution path it takes with real input. This is where static analysis starts to lose to complexity: heavy obfuscation, anti-debugging tricks, or logic that only makes sense once you see the actual values flowing through it.

gdb ./challenge
(gdb) break *0x401234
(gdb) run
(gdb) info registers

Set a breakpoint at the comparison that decides pass or fail, run the binary, and inspect what it's actually comparing your input against — often faster than reverse-engineering the same check by reading disassembly cold.

Where they meet

In practice the two approaches feed each other. Static analysis gives you a map — which functions exist, roughly what they do, where the interesting comparison probably lives — so you're not debugging blind. Dynamic analysis then confirms or corrects that map against what the binary actually does at runtime, which matters because decompilers get it wrong often enough that pseudocode alone can mislead you. A common workflow: skim statically to find the function that checks your input, then set a breakpoint there and inspect it dynamically to read out the exact expected value instead of reverse-engineering the check by hand.

Wrapping up

Reach for static analysis first when the binary is small, readable, or risky to execute; switch to dynamic analysis once obfuscation, anti-debugging, or sheer complexity make reading the code alone too slow. Most reverse engineering challenges reward moving between the two rather than committing to one. If you want to see this play out on a real challenge, our writeups section has full walkthroughs, and the Getting Started with CTF guide covers the essential tools for both approaches.

Back to Blog