Assembly can be very difficult to understand. GDB can make it much easier to follow the flow
of a program at the lowest level so you have at least some idea of what's going on. The first
thing that you will want to do is to get the right layout. layout next
will cycle through
GDB's layouts until you get to the one with the ASM, REG, and CMD panes all showing.
In order to interact with the assembly, the following commands are very useful.
-
si
andni
: Move from assembly instruction to assembly instruction -
x/x 0xDEADFEED
: This lets you examine the contents of a particular memory location. -
fin
: Pop a stack frame and return to the "caller" function.
With all of that being said, understanding how the registers work with the memory is
perhaps the most important part of processing assembly. In the case of a 32-bit bit machine,
there are many registers that have different meanings. The most important of these are
esp
, ebp
, eip
, eax
.
-
eip
: This register is the instruction pointer. The value it holds is the next instruction that will be executed. Instructions likejmp
andret
operate directly on this register.jmp
is pretty easy to understand, the value specified is simply loaded directly into theeip
.ret
, on the other hand, works by loading the value pointed to the by the current stack pointer intoeip
and incrementingesp
(the stack grows by subtraction). You can see the return value beforeret
is actually executed by typingx/x $VALUE_OF_ESP
. -
ebp
andesp
: These two registers work in tandem to define the top and bottom of a stack frame. Everytime a function is called, it immediately pushesebp
onto the stack, and then doesmove ebp esp
, setting the old top of the stack to the new bottom of the stack. Similarly, prior to returning, it will callpop ebp
to restore the base pointer of the previous call. -
eax
: This register is used to communicate return values. The caller function can expect to find the reponse of the callee in this register.
As an example of how these interact in a typical function call, we'll use the following snippet of assembly code
8048f00: 55 push %ebp
8048f01: 89 e5 mov %esp,%ebp
8048f03: 83 ec 18 sub $0x18,%esp
8048f06: 8b 45 08 mov 0x8(%ebp),%eax
8048f09: 89 45 fc mov %eax,-0x4(%ebp)
8048f0c: 8d 45 0c lea 0xc(%ebp),%eax
8048f0f: 89 45 f8 mov %eax,-0x8(%ebp)
8048f12: 8b 4d fc mov -0x4(%ebp),%ecx
8048f15: 89 e2 mov %esp,%edx
8048f17: 89 42 04 mov %eax,0x4(%edx)
8048f1a: 89 0a mov %ecx,(%edx)
8048f1c: e8 ff fa ff ff call 8048a20 <vprintf>
8048f21: 89 45 f4 mov %eax,-0xc(%ebp)
8048f24: 83 c4 18 add $0x18,%esp
8048f27: 5d pop %ebp
8048f28: c3 ret
This function is a snippet from a simplified printf function. The caller of this
function would do something like call 0x8048f00
. This would do two things. First
it would push the current eip
onto the stack, decrementing the stack pointer. Second
it would load the desired address into eip
, meaning the control flow will hop to this
location upon the next cycle.
Upon being called, the first thing the function does is save the base pointer of the
caller function. This push ebp
both save the value in ebp
at esp
and decrements
the esp
. The next command, move esp ebp
, marks the current esp
value as the
bottom of our stack frame. Anytime the stack increases from this point forward, it will
be be relative to this value.
The final thing that happens in setting up this function call is sub 0x18 esp
. This
command decrements the stack pointer, creating space for any local variables assocaited
with this function. At this point, all of the "intitalization" assocaited with this
function is completed and it can begin to execute.
The deinitialization occurs at 0x8048f21
. Here, a value is stored into the eax
register. This register is used to store return values. The next instruction,
add 0x18 esp
increments the stack pointer by the same amount it was decremented by on
initialization. This effectively wipes out all of the local variables assocated with
this function. pop ebp
restores the previous functions base pointer. Finally, ret
puts the value pointed to by esp
into eip
and increments esp
. Upon the next cycle,
the caller function will be restored with everything where it left it.