Stop a program and look inside
gdb is the GNU debugger: it runs a program under your control, stops it where you ask and lets you read its registers and memory.
That matters because a program has to hold both words in memory at the moment it compares them. Stopping it at that moment shows you what it compares, with no need to read every instruction. You will use it on a practice program only.
By the end of this lesson
Note
I can stop a program and read what it compares.
Key words
| Word | What it means | Example |
|---|---|---|
| Debugger | a tool that runs a program and lets you stop it | gdb |
| Breakpoint | a place where the debugger stops the program | |
| Argument | a value given to a function when it is called | |
| Address | the number that says where something is in memory | |
| Pointer | a register or variable that holds an address | |
| Calling convention | the agreed place for each argument of a call |
The cells students fill in are left blank in this preview.
A breakpoint stops the program where you choose
A breakpoint tells the debugger: stop when you reach this place. Here the place is the function strcmp, which compares two strings.
The program stops just as the function is about to run. Its arguments, the two strings, are already in place. You only have to know where to look.
Worked example: strcmp(typed, stored)
| Register | Holds | In vault |
|---|---|---|
| rdi | The first argument, an address | Where the typed word is |
| rsi | The second argument, an address | Where the stored word is |
| rax or eax | The answer, after the call | Zero when the words are equal |
The cells students fill in are left blank in this preview.
x/s shows the text at an address
A register such as rdi holds only an address, a number. x/s $rdi means: examine the memory at that address, shown as a string. The dollar sign names the register.
Typing gdb by hand is a conversation. For these Runs each command is given with -ex and gdb exits at the end. The last two lines it prints are the two strings, which is why tail -n 2 keeps only those.
Write the practice program
printf '#include <stdio.h>\n#include <string.h>\nint main(int argc, char **argv) {\n if (argc != 2) { puts("usage: vault WORD"); return 2; }\n if (strcmp(argv[1], "blue-harbour") == 0) { puts("Door open."); return 0; }\n puts("Door shut.");\n return 1;\n}\n' > vault.cShown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Compile it
gcc -O0 -no-pie -fno-stack-protector -o vault vault.cShown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Stop at strcmp and read both strings
gdb -batch -ex 'set debuginfod enabled off' -ex 'set environment LD_BIND_NOW=1' -ex 'break main' -ex 'run' -ex 'break strcmp' -ex 'continue' -ex 'x/s $rdi' -ex 'x/s $rsi' --args ./vault guess 2>&1 | tail -n 2Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
The two registers themselves
gdb -batch -ex 'set debuginfod enabled off' -ex 'set environment LD_BIND_NOW=1' -ex 'break main' -ex 'run' -ex 'break strcmp' -ex 'continue' -ex 'info registers rdi rsi' --args ./vault guess 2>&1 | tail -n 2Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
The same stop with the right word
gdb -batch -ex 'set debuginfod enabled off' -ex 'set environment LD_BIND_NOW=1' -ex 'break main' -ex 'run' -ex 'break strcmp' -ex 'continue' -ex 'x/s $rdi' -ex 'x/s $rsi' --args ./vault blue-harbour 2>&1 | tail -n 2Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Doing it by hand in gdb
To work in gdb yourself, start it with gdb ./vault and type each command at its prompt: break main, run guess, break strcmp, continue, x/s $rsi, then quit. It never ends until you quit, so it is not a Run.
If gdb offers to use debuginfod, answer n. After each stop, continue carries on to the next breakpoint.
A common mistake
Warning
Many people type break strcmp first and expect to stop in their own program. Other code runs before main, and it can stop there instead, so you read the wrong strings. Stop at main first, then set the breakpoint, then continue.