| title | How to Reproduce This Experiment |
|---|---|
| short_title | Experiment Guide |
Follow these steps to reproduce the decompilation analysis on your own system.
::::{grid} 2
:::{card} Required Tools
- GCC compiler (any recent version)
- Text editor (VSCode, vim, etc.)
- Web browser
- Internet connection for Dogbolt :::
:::{card} Optional Tools
- Git (for version control)
objdump(for assembly inspection)- Ghidra (for local decompilation) :::
::::
:::{tip} Download or copy the complete source code from the source code page. :::
🛠 Save the source code as experiment.c:
#include <stdio.h>
#include <stdlib.h>
// Function for Fibonacci sequence
int calculate_fibonacci(int n) {
if (n <= 1) {
return n;
}
return calculate_fibonacci(n - 1) + calculate_fibonacci(n - 2);
}
// Function with loop and local variables to compute sum of a given array
int sum_array(int *arr, int size) {
int sum = 0;
int i;
for (i = 0; i < size; i++) {
sum += arr[i];
}
return sum;
}
// ... (rest of code - see source.md)🛠 Open your terminal and navigate to the directory containing experiment.c:
cd /path/to/your/experiment🛠 Compile with three different optimization levels:
# No optimization - easiest to decompile
gcc -O0 experiment.c -o experiment_O0
# Moderate optimization
gcc -O2 experiment.c -o experiment_O2
# Aggressive optimization - hardest to decompile
gcc -O3 experiment.c -o experiment_O3:::{note}
The -O0, -O2, and -O3 flags control how aggressively GCC optimizes your code. -O0 means no optimization, while -O3 applies the most aggressive optimizations.
:::
🛠 Generate human-readable assembly for comparison:
gcc -O0 -S experiment.c -o experiment_O0.s
gcc -O2 -S experiment.c -o experiment_O2.s
gcc -O3 -S experiment.c -o experiment_O3.s🛠 Compare assembly line counts:
wc -l experiment_O*.sYou should see output similar to:
234 experiment_O0.s
392 experiment_O2.s
454 experiment_O3.s
1080 total
:::{important} Dogbolt is an online decompiler aggregator that lets you compare results from multiple decompilers including Ghidra, IDA, Binary Ninja, and more. :::
🛠 Open your web browser and navigate to:
https://dogbolt.org
🛠 Click "Upload Binary" on the Dogbolt homepage
🛠 Select experiment_O0 from your file system
🛠 Wait for decompilation (typically 30-60 seconds)
🛠 Select "Ghidra" from the decompiler options on the left sidebar
:::{tip} You can also select other decompilers like "Hex-Rays" or "Binary Ninja" to compare results! :::
🛠 Find the main function in the decompiled code
🛠 Compare with your original source:
- Are variable names the same?
- Is the control flow recognizable?
- Are functions still separate?
🛠 Look for calculate_fibonacci and sum_array:
- Do they still exist as separate functions?
- Can you identify the algorithm?
🛠 Upload experiment_O2 and experiment_O3 separately
🛠 Compare how optimizations transformed the code:
- Which functions disappeared (inlined)?
- How did loops change?
- Is the Fibonacci algorithm still recognizable?
Create a comparison table like this:
| Aspect | -O0 | -O2 | -O3 |
|---|---|---|---|
| Variable names preserved? | ❌ No | ❌ No | ❌ No |
| Functions separate? | ✅ Yes | ❌ No (inlined) | |
| Fibonacci recognizable? | ✅ Clear | ❌ Unrecognizable | |
| Array sum loop clear? | ✅ Yes | ❌ Constant folded |
🛠 View the assembly of a specific function:
objdump -d experiment_O0 | grep -A 30 "calculate_fibonacci"If you have Ghidra installed:
- Create a new project in Ghidra
- Import your binary: File → Import File
- Analyze with default options
- Navigate to the Functions list in the Symbol Tree
- Compare the Decompile view with your original source
🛠 View specific sections of assembly:
# See how Fibonacci changes across optimization levels
grep -A 50 "calculate_fibonacci:" experiment_O0.s > fib_O0.txt
grep -A 50 "calculate_fibonacci:" experiment_O3.s > fib_O3.txt
diff fib_O0.txt fib_O3.txt::::{grid} 2
:::{card} Easy to Recover ✅
- Control flow (if/else/loops)
- String literals
- Numeric constants
- Function boundaries (-O0)
- Basic algorithms :::
:::{card} Lost Forever ❌
- Variable names
- Comments
- Type definitions (struct names)
- Developer reasoning
- Code organization :::
::::
// Original
int total = sum_array(numbers, array_size);
// Decompiled - very similar!
uint uVar2;
uVar2 = sum_array(numbers, 10);// Original
int total = sum_array(numbers, array_size);
// Decompiled - computed at compile time!
__printf_chk(1, "Sum of array: %d\n", 0x37);:::{warning}
The entire function call was eliminated and replaced with the hardcoded result 0x37 (55 in hex).
:::
If your binary is too large, try:
- Compiling with
-sto strip symbols:gcc -O0 -s experiment.c -o experiment_O0_stripped - Using a local decompiler instead (Ghidra, IDA)
- Make sure you compiled without stripping: don't use
-sflag - Look for function addresses in the assembly output
- In Ghidra, functions might be auto-named like
FUN_00401234
- Remember: decompilers are approximations, not perfect
- Try different decompilers on Dogbolt
- Compare with the assembly to verify logic
- Add more complex algorithms: Try quicksort, binary search, or linked lists
- Use different compilers: Compare GCC vs Clang vs MSVC
- Try obfuscation: Use tools like
llvm-obfuscator - Strip symbols: Compile with
-sflag to see how it affects analysis - Add static analysis: Use
cppcheckorclang-tidyon decompiled code
- Dogbolt: https://dogbolt.org
- Compiler Explorer: https://godbolt.org (see assembly + source side-by-side)
- Ghidra: https://ghidra-sre.org (full-featured reverse engineering)
- GCC Optimization Docs: https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html
:::{tip} Once you've completed the experiment, compare your findings with the main analysis report! :::