The slide deck closes with a worked example, password_manager_v2, that exercises every tool in the binary-analysis toolbox in one walkthrough. It is worth reading as a single story rather than as a tool list, because the static and dynamic halves feed each other: each static finding suggests a breakpoint, and each dynamic observation sends you back to the disassembly.
Static reconnaissance
Start without ever executing the binary. The four static tools answer the four opening questions in order.
file reveals the binary’s width and linking model — 64-bit, dynamically linked — which fixes the register set and tells us to expect ldd-visible dependencies. ldd lists the shared libraries the dynamic linker will load. readelf’s symbol table gives the address of main, landing us in the right corner of the disassembly. And strings surfaces the candidate messages the program will print — enough to guess that it asks for a password and tests whether it is correct.
Finally, objdump -M intel -ds produces the disassembly. Scrolling through main, the calls jump out: <strcmp@plt> (the password check) and <puts@plt> (the message printer). Four jump instructions — jne, jnz, and two jmp — implement the branching. We have a picture of the program’s skeleton: read input, compare, branch on the result, print. We still don’t have the password.
Dynamic confirmation, and the first surprise
Running the binary under strace confirms the syscall surface but, more usefully, reveals that the password is expected as a command-line argument, not via stdin — a fact the static pass did not surface. ltrace ./password_manager asdf then traces the library calls; the strcmp against an argument reveals the first candidate password.
For password_manager_v2, however, the string seen by ltrace is not what the program compares against — a fresh reason to load gdb + pwndbg and watch state evolve.
GDB: breakpoints and the disassembly loop
Loading gdb /exercises/password_manager_v2 and disassembling main, two stack slots matter. Our input is read by scanf and stored at [rbp-0x40]; a constant "wizkccbgybn\0" is stored at [rbp-0x23]. Later, [rbp-0x40] is compared to [rbp-0x23] with strcmp — so on the face of it the password is "wizkccbgybn".
Set a breakpoint just after scanf, run the program, and supply a guess; set another at strcmp. Stepping with nexti past scanf lets us read our input at [rbp-0x40] and confirm the comparison target at [rbp-0x23].
The loop that mutates the input
Stepping further, three instructions hint at a for loop. call strlen on [rbp-0x40] — our input — measures it. The result is compared with [rbp-0x18], currently 0, and a jb jumps back to main+78 while [rbp-0x18] < strlen([rbp-0x40]). So [rbp-0x18] is the loop iterator i, bounded by the input length.
Inside the loop body, the character input[i] is added to a local [rbp-0x14] (call it current) inside a local variable, then compared with 'z' (0x7a). If current <= 'z' the program writes the new current back to input[i]; if current > 'z' it subtracts 0x1a first. Walking through the three branches, the body is the single operation rotate the character right by 10 — and the loop walks every input character through that rotation before the comparison.
Reversing the rotation
The loop transforms the input before comparing it against "wizkccbgybn". So "wizkccbgybn" is not the password; it is the rotated image of the password. To recover the password, apply the inverse — rotate "wizkccbgybn" left by 10 — and you obtain "mypassword". Supplying that to the program makes strcmp return 0, the equality branch fires, and the program prints its success message. Reverse-engineering wins without brute force.
The short-circuit bypass
There is a second way in, which illustrates what dynamic analysis can do that static analysis cannot. strcmp returns 0 on equality, and the subsequent jne/jnz reads RAX to decide. Stopping just after call strcmp and running set $rax = 0 in gdb makes the branch behave as if the strings had compared equal, regardless of what was typed. The check is bypassed — but the password itself was never recovered. The contrast is the point: defeating a check and understanding the program are different goals, and dynamic analysis serves both, one by tampering and one by observation.
The landing
The walkthrough shows the rhythm: static recon to find structure, dynamic steps to confirm hypotheses and watch addresses, then back to the disassembly once the runtime reveals which slots carry what. The password_manager_v2 case is small enough to read in one pass, but it scales — Ghidra, pwntools, checksec, and the CyberChef family all extend the same loop on larger binaries. The cluster that follows, the memory-corruption attacks, reuses exactly this skill: find the unsafe call, find the control data it can reach, and decide whether the overflow walks into the saved return address or only into a neighbouring variable.