Lesson 6 of 9 · 24m
Reading Decompiler Output Skeptically
The decompiler produces C-shaped output that reads fluently and lies fluently — every type is a guess. Audit it against the listing and inject understanding through retyping.
The decompiler is the best thing that ever happened to reverse engineering and the most dangerous tool in the kit. It emits C-shaped text that reads fluently and lies fluently — every type is a guess, every name a placeholder. Treat it as a fast first draft you audit against the listing, never as the truth.
What it gets wrong
undefined8 FUN_00001139(long param_1)
{
int local_c;
local_c = 0;
while (*(char *)(param_1 + local_c) != 0) {
*(char *)(param_1 + local_c) = *(char *)(param_1 + local_c) ^ 0x5a;
local_c = local_c + 1;
}
return 0;
}This is a strlen-style walk plus an XOR decode loop — the decompiler just doesn't know param_1 is a char *. Retype it (Ctrl+L → char *) and watch the casts evaporate, the indexing become param_1[local_c], and the function suddenly read like the source. Retyping isn't cosmetic; it's how you inject your understanding back into the tool so it can compound.
Specific lies to expect
- Wrong signedness.
if (local_c < 0x20)may have been an unsigned compare in source — the listing'sjb/jaeversusjl/jgetells you which. The decompiler guessed. - Everything is an integer. A
doublelocal shows up asundefined8wrapped in bizarre integer ops until you retype it; aboolbecomes anint. - Invented control flow.
gotos vanish intowhileloops it constructed; short-circuit&&/||become nestedifs, or vice versa. - Inlining erased the boundaries. A 400-line pseudocode function is often six functions the optimizer merged into one.
When pseudocode and listing disagree
The listing wins — it is the program. If pseudocode shows a comparison you can't square with the cmp/jcc pair, or variables materialize from nowhere (extraout_EAX, in_RDI, CONCAT44), drop to assembly for that block. The usual root cause is a wrong type at a leaf propagating nonsense upward through an expression tree — fix the type at the leaf, not the symptom at the top.
Rebuild the structures
When [param_1 + 0x18] is written in three functions and read in seven more, +0x18 is a field. Right-click the variable → auto-create structure, fill in fields as you identify them, retype the parameter — and every function taking that type gets clearer simultaneously. Struct recovery is where decompiler work pays compound interest: one correct type explains more code than an hour of reading.
Field notes
- Names are claims you're staking.
FUN_...renamed toxor_decodeis a hypothesis; rename freely and rename honestly when it dies. CONCAT44,SUB164,extraout_,in_prefixes are decompiler bookkeeping artifacts, not real variables. Don't decode them — trace where the underlying value comes from.- Higher optimization means worse decompilation.
-O2inlining plus vectorized loops can produce pseudocode uglier than the assembly; sometimes the right call is reading the asm directly.