GDB is a great debugger that comes ready with many linux OS's. For those of you who dont know, a debugger is a computer program used to test and debug other computer programs. This is just a simple walkthrough to show you how this program can be used. I stress the fact that the example i'm giving here is extreemely simple, and you're likely to encounter much more complex problems in real life situations. This is only meant as a beginners tutorial for the debugger, not for assembly.

Also, you should be aware that this article assumes that you have a basic understanding of assembly, and a bit of experience with C programming may also be helpful.

So, let's say we have a password program written in C that we need to crack and we don't have the source code. Well, we can always use gdb to find the assembly and trace through it. So, let's get started.

It's usually safe to assume that there will be a 'main()' function in a program, so let's start by finding out what it looks like:

(gdb)> disasseble main
Dump of assembler code for function main:

0x0804840c <main+0>:    lea    0x4(%esp),%ecx
0x08048410 <main+4>:    and    $0xfffffff0,%esp
0x08048413 <main+7>:    pushl  0xfffffffc(%ecx)
0x08048416 <main+10>:   push   %ebp
0x08048417 <main+11>:   mov    %esp,%ebp
0x08048419 <main+13>:   push   %ecx
0x0804841a <main+14>:   sub    $0x34,%esp
0x0804841d <main+17>:   lea    0xffffffde(%ebp),%eax
0x08048420 <main+20>:   mov    %eax,0x4(%esp)
0x08048424 <main+24>:   movl   $0x804854e,(%esp)
0x0804842b <main+31>:   call   0x80482d8 <scanf@plt>
0x08048430 <main+36>:   lea    0xffffffde(%ebp),%eax
0x08048433 <main+39>:   mov    %eax,(%esp)
0x08048436 <main+42>:   call   0x80483c8 <checkPass>
0x0804843b <main+47>:   mov    $0x0,%eax
0x08048440 <main+52>:   add    $0x34,%esp
0x08048443 <main+55>:   pop    %ecx
0x08048444 <main+56>:   pop    %ebp
0x08048445 <main+57>:   lea    0xfffffffc(%ecx),%esp
0x08048448 <main+60>:   ret    
0x08048449 <main+61>:   nop

Briefly skim over, and you should notice that it makes a few function calls. The first is a call to the built-in function scanf which makes sense since the program asks you to input a password. The second method call is to a method called checkPass which sounds like what we're looking for, so our next step should be to see what that looks like:

(gdb)> disassemble checkPass
Dump of assembler code for function checkPass:

0x080483c8 <checkPass+0>:       push   %ebp
0x080483c9 <checkPass+1>:       mov    %esp,%ebp
0x080483cb <checkPass+3>:       sub    $0x18,%esp
0x080483ce <checkPass+6>:       mov    0x8048548,%eax
0x080483d3 <checkPass+11>:      mov    %eax,0xfffffffa(%ebp)
0x080483d6 <checkPass+14>:      movzwl 0x804854c,%eax
0x080483dd <checkPass+21>:      mov    %ax,0xfffffffe(%ebp)
0x080483e1 <checkPass+25>:      lea    0xfffffffa(%ebp),%eax
0x080483e4 <checkPass+28>:      mov    %eax,0x4(%esp)
0x080483e8 <checkPass+32>:      mov    0x8(%ebp),%eax
0x080483eb <checkPass+35>:      mov    %eax,(%esp)
0x080483ee <checkPass+38>:      call   0x80482f8 <strcmp@plt>
0x080483f3 <checkPass+43>:      test   %eax,%eax
0x080483f5 <checkPass+45>:      jne    0x8048405 <checkPass+61>
0x080483f7 <checkPass+47>:      movl   $0x804853e,(%esp)
0x080483fe <checkPass+54>:      call   0x80482e8 <puts@plt>
0x08048403 <checkPass+59>:      jmp    0x804840a <checkPass+66>
0x08048405 <checkPass+61>:      call   0x80483b4 <notifyAuthorities>
0x0804840a <checkPass+66>:      leave  
0x0804840b <checkPass+67>:      ret

Again, briefly skim over the code and again, you should notice two method calls. The first method call is to the built-in function strcmp (string compare). You should be excited now because that probably means that we're just comparing our string to the correct password, meaning this will be very easy. The second function call is to a method called notifyAuthorities which doesnt sound like very much fun to me, so we should make sure we tell the debugger to stop the program before it gets to that:

(gdb)> break notifyAuthorites

Now, if we accidentally input the wrong password, the program will stop at that method call and we can type 'kill' into the prompt to end the program before the authorities are notified.

So, now that we're all set, lets find out what the password is. I suppose you could try to sit there and translate all that assembly, but i really prefer to just run through it a few times first and see what's happening, so that's what we're going to do. The important function is checkPass, so let's set a breakpoint there (we dont need to trace through the main function in this example).

(gdb)> break checkPass

Now, before we begin it'd probably be helpful if i gave you a list of some useful gdb commands, so here it is:

help - pull up the help pages (takes a command as an arguement)

break - sets a breakpoint (takes a function name or address as an arguement)

continue(also cont or c) - continue a program run from breakpoint

run(also r) - runs the program (takes command line parameters as arguements)

nexti(also ni) - next instruction, skipping functions

stepi(also si) - next instruction, stepping into functions

finish(also fi) - complete current function then break

del - deletes breakpoint. takes a breakpoint name as an arguement

disassemble(also disas) - breaks down a program into assembly (takes a function name as an arguement)

kill(also k) - stops program execution

print - prints out a value

x - examine (dereference arguement and print) can have the following type specifiers added:
/d = decimal
/x = hex
/a = address
/s = string
/c = character

info registers - prints out values in all registers as well as the value of the flags

Also, you probably want to note that in assembly code, registers have a percent sign(%) in front of them, but in gdb you put a dollar sign($) in front of them instead when referencing a register.

So, let's start (i'm going to enter the first word that pops into my head as the password this time around):

(gdb)> run
> pizza
Breakpoint 2, 0x080483ce in checkPass ()

Okay, it should now be stopped at our first breakpoint at checkPass. Info registers is usually a good place to start, so let's enter that:

(gdb)> info registers
eax            0xbff4ff46       -1074462906
ecx            0x48b8e420       1220076576
edx            0x0      0
ebx            0x48b8dff4       1220075508
esp            0xbff4ff10       0xbff4ff10
ebp            0xbff4ff28       0xbff4ff28
esi            0x4809fca0       1208614048
edi            0x0      0
eip            0x80483ce        0x80483ce <checkPass+6>
eflags         0x200282 [ SF IF ID ]
cs             0x73     115
ss             0x7b     123
ds             0x7b     123
es             0x7b     123
fs             0x0      0
gs             0x33     51

Well, nothing really stands out to me there, but let's take a look into the eax register since that is generally the all-purpose register. Since we're probably looking for a string, let's try that:

(gdb)> x/s $eax
0xbff4ff46: "pizza"

Hey, look at that. It's our input. Okay, we're currently on the line labled <checkPass+6>, and you can see that it's putting something else into the eax register. Why dont we step once and see what it puts in there.

(gdb)> si
0x080483d3 in checkPass ()
(gdb)> x/s $eax
0x6863616e: <address 0x6863616e out of bounds>

Well, that was a bust. Oh well, lets take a look at the code again:

0x080483d3 <checkPass+11>:      mov    %eax,0xfffffffa(%ebp)
0x080483d6 <checkPass+14>:      movzwl 0x804854c,%eax
0x080483dd <checkPass+21>:      mov    %ax,0xfffffffe(%ebp)
0x080483e1 <checkPass+25>:      lea    0xfffffffa(%ebp),%eax
0x080483e4 <checkPass+28>:      mov    %eax,0x4(%esp)
0x080483e8 <checkPass+32>:      mov    0x8(%ebp),%eax
0x080483eb <checkPass+35>:      mov    %eax,(%esp)
0x080483ee <checkPass+38>:      call   0x80482f8 <strcmp@plt>

Well, the three lines before the call to strcmp are setting up the arguements for the function call, and we can probably assume that one of the arguements is our input and the other is probably the correct password, so lets step ahead a few more time to checkPass+28. At that point, the program has just moved a new value into eax, so lets see what it is:

(gdb)> si
0x080483d6 in checkPass ()
(gdb)> si
0x080483dd in checkPass ()
(gdb)> si
0x080483e1 in checkPass ()
(gdb)> si
0x080483e4 in checkPass ()
(gdb)> x/s $eax
0xbff4ff22: "nacho"

Hey, it's a nice string. It looks like a likely candidate for a password. We set our breakpoint at notifyAuthorities, so we've got nothing to lose if we try it, so let's kill this thing and see if it's right (remember, we still have our breakpoint set at checkPass, so we'll have to tell it to keep going once it stops there):

(gdb)> kill
Kill the program being debugged? (y or n) y
(gdb)> run
> nacho
Breakpoint 2, 0x080483ce in checkPass ()
(gdb)> continue
good job!

Program exited normally.

Well, looks like we did it. Just in case you're curious, here's what the C code looks like for the program we were running:

#include <stdio.h>
#include <string.h>
void notifyAuthorities()
    //doesnt really do anything, just an example
    printf("authorites have been notified\n");
void checkPass(char input[30])
    char pass[]="nacho";
    if(!strcmp(input, pass))
        printf("good job!\n");
        notifyAuthorities(); }
int main()
    char input[30];
    scanf("%s", input);

If you compile your code with gcc using the -g option (e.g. gcc -g filename.c -o exeName) then you can use the debugger to trace through your source code. So, say you did create the executable file that way, but there was a problem and you can't figure out why the program isnt working. You could open up gdb in the same way:

> gdb ./Password

And you could use most of the same commands such as break, print, continue, etc. You could then trace through the program line-by-line and keep track of the values in each variable to find where things go wrong.

A useful command to use for this purpose is the display command. You can type: display variableName and it will show the value of that variable as you step through the program. It's very useful when tracing through loops.