In this series of
posts, I’m going through the Open Security Training for beginning Assembly Language and thus am putting my
own spin on things to enhance my knowledge of x86 disassembly. However, to make
the most of these tutorials you may be better of reviewing the material from Open Security Training directly.
Let’s get started!
Let’s get started!
Here is some basic C Code, which was
built with Visual Studio 2017.
/*
* This file focuses on the basics of function
creation and tear down in assembly
* The ojective is to get a better
understanding of prologue and epilogue
* Author Nik Alleyne
* Blog: securitynik.blogspot.com
* File: stackbasics.c
*
*/
int SecurityNik()
int SecurityNik()
{
return
"SecurityNik!";
}
int main()
{
SecurityNik();
return
"Returning!!";
}
The code is then disassembled in Visual Studio using its disassembler.
The code is then disassembled in Visual Studio using its disassembler.
Here is the disassembled code for
function “SecurityNik()”
int SecurityNik()
{
00401330
push ebp
00401331
mov ebp,esp
return
"SecurityNik!";
00401333
mov eax,407000h
}
00401338
pop ebp
00401339
ret
…. and here
is the disassembled code for function “main()”
int main()
int main()
{
00401340 push
ebp
00401341 mov
ebp,esp
SecurityNik();
00401343 call
_SecurityNik (040119Fh)
return "Returning!!";
00401348 mov
eax,407010h
}
0040134D pop
ebp
0040134E ret
Let’s now try to understand what is going on by starting with function “main()”
Beginning with the registers:
EAX = 0F471944 EBX = 002BA000 ECX = 00000001 EDX = 00407578 ESI = 00401023 EDI = 00401023 EIP = 00401340 ESP = 0019FF08 EBP = 0019FF18 EFL = 00000202
EAX = 0F471944 EBX = 002BA000 ECX = 00000001 EDX = 00407578 ESI = 00401023 EDI = 00401023 EIP = 00401340 ESP = 0019FF08 EBP = 0019FF18 EFL = 00000202
The first action in our main
function is to “push ebp”. Looking at the registers we see EBP register has a
value of “0019FF18”. This means this value will be
pushed unto the stack: Let’s verify this by “stepping” through the code to the
next instruction and then looking at the memory at the location occupied by
“ESP”.
First peaking at the registers
again.
EAX = 0FD61944 EBX = 0033C000 ECX = 00000001 EDX = 00407578 ESI = 00401023 EDI = 00401023 EIP = 00401341 ESP = 0019FF04 EBP = 0019FF18 EFL = 00000202
Looking at the memory address occupied by ESP, we see the value of the previous EBP register is now there. The first highlighted portion is the EBP being placed on the stack as main function builds its stack. The second highlighted 4 bytes “0x0040178e” must then be the return pointer for the function which called main().
EAX = 0FD61944 EBX = 0033C000 ECX = 00000001 EDX = 00407578 ESI = 00401023 EDI = 00401023 EIP = 00401341 ESP = 0019FF04 EBP = 0019FF18 EFL = 00000202
Looking at the memory address occupied by ESP, we see the value of the previous EBP register is now there. The first highlighted portion is the EBP being placed on the stack as main function builds its stack. The second highlighted 4 bytes “0x0040178e” must then be the return pointer for the function which called main().
0x0019FF04 0019ff18 .ÿ.. – ESP (top of the stack) – has most recent pushed value
0x0019FF08 0040178e Ž.@.
0x0019FF0C 00000001 ....
0x0019FF10 0075cfe8 èÏu.
0x0019FF14 00758558 X.u.
0x0019FF18 0019ff70 pÿ..
0x0019FF1C 004015f0 ð.@.
0x0019FF20 9b9af0d4 Ôðš.
0x0019FF24 00401023 #.@.
0x0019FF24 00401023 #.@.
Our next instruction is to move the
value of ESP into EBP (“mov ebp, esp”) as we continue to build the stack frame
for main.
The current value of ESP is ESP = 0019FF04 as shown in our register above. So pushing ESP into EBP should now change the value of EBP to reflect “0019FF04”. Let’s verify this by stepping and looking at our register.
EAX = 0FD61944 EBX = 0033C000 ECX = 00000001 EDX = 00407578 ESI = 00401023 EDI = 00401023 EIP = 00401343 ESP = 0019FF04 EBP = 0019FF04 EFL = 00000202
The current value of ESP is ESP = 0019FF04 as shown in our register above. So pushing ESP into EBP should now change the value of EBP to reflect “0019FF04”. Let’s verify this by stepping and looking at our register.
EAX = 0FD61944 EBX = 0033C000 ECX = 00000001 EDX = 00407578 ESI = 00401023 EDI = 00401023 EIP = 00401343 ESP = 0019FF04 EBP = 0019FF04 EFL = 00000202
Ok, as seen above the EBP register
was changed to reflect the current value of ESP.
Next step is to call function
SecurityNik() at address “040119Fh”. This now moves execution to the
SecurityNik function. So one again we start building our stack frame. However,
an important thing for us to take away is that once execution is switched to
function “SecurityNik()”, then before EBP is pushed unto the stack, the return
pointer of “return
"Returning!!";”
must be set just before EBP. Therefore, considering the next instruction in
“main()” after function “SecurityNik()” is returned is “mov eax,407010h” which is at address “00401348”, we then expect to see this on the
stack before the next “EBP” value.
Looking first at the registers now that we have stepped into function “SecurityNik()”.
EAX = 0FF21944 EBX = 00359000 ECX = 00000001 EDX = 00407578 ESI = 00401023 EDI = 00401023 EIP = 00401330 ESP = 0019FF00 EBP = 0019FF04 EFL = 00000202
Looking first at the registers now that we have stepped into function “SecurityNik()”.
EAX = 0FF21944 EBX = 00359000 ECX = 00000001 EDX = 00407578 ESI = 00401023 EDI = 00401023 EIP = 00401330 ESP = 0019FF00 EBP = 0019FF04 EFL = 00000202
Now that we have the value of EBP
highlighted, let’s step to the next instruction which is “push ebp”
Looking at the registers again:
EAX = 0FF21944 EBX = 00359000 ECX = 00000001 EDX = 00407578 ESI = 00401023 EDI = 00401023 EIP = 00401331 ESP = 0019FEFC EBP = 0019FF04 EFL = 00000202
Looking at the registers again:
EAX = 0FF21944 EBX = 00359000 ECX = 00000001 EDX = 00407578 ESI = 00401023 EDI = 00401023 EIP = 00401331 ESP = 0019FEFC EBP = 0019FF04 EFL = 00000202
Let’s look at the memory occupied by
ESP to verify the EBP “EBP = 0019FF04” value has been pushed unto the stack. While there, let’s
verify that the return pointer “00401348”,
has also been pushed unto the stack.
0x0019FEFC 0019ff04 .ÿ.. – ESP Points to top of stack – EBP value at ESP
0x0019FF00 00401348 H.@. – Return pointer for main()
0x0019FF04 0019ff18 .ÿ..
0x0019FF08 0040178e Ž.@.
0x0019FF0C 00000001 ....
0x0019FF10 0053e3e0 àãS.
0x0019FF14 00538558 X.S.
0x0019FF18 0019ff70 pÿ..
0x0019FF1C 004015f0 ð.@.
Good stuff! From above we have the
value of EBP pushed unto the stack but most importantly we see the address of “00401348” which is for “mov eax, 407010h”. “407010h” is the pointer to the string "Returning!!", which is our return value.
What comes next? Well we are still
building our stack frame, so the next instruction is to move the value in ESP
to EBP (mov ebp,esp)
ESP currently has a value of ESP = 0019FEFC. After execution we expect ESP and
EBP to be the same. Let’s verify.
EAX = 0F1D1944 EBX = 002FC000 ECX = 00000001 EDX = 004043A8 ESI = 004013A0 EDI = 004013A0 EIP = 00401003 ESP = 0019FEFC EBP = 0019FEFC EFL = 00000202
EAX = 0F1D1944 EBX = 002FC000 ECX = 00000001 EDX = 004043A8 ESI = 004013A0 EDI = 004013A0 EIP = 00401003 ESP = 0019FEFC EBP = 0019FEFC EFL = 00000202
Looks good! Next instruction says “mov eax,407000h”. This will
move the value “407000h” into the EAX register. Point to
note is that the EAX register is used for storing return values and in this
case, the address “407000h” points to our return string
“SecurityNik” as shown below.
0x00407000 75636553 Secu
0x00407004 79746972 rity
0x00407008 216b694e Nik!
0x0040700C 00000000 ....
0x00407010 75746552 Retu
0x00407014 6e696e72 rnin
0x00407018 00212167 g!!.
0x0040701C 00000000 ....
0x00407020 00000000 ....
From the register print out above,
we see “EAX = 0F1D1944”. Let’s now step into the code and
revisit our registers.
EAX = 00407000 EBX = 00359000 ECX = 00000001
EDX = 00407578 ESI = 00401023 EDI = 00401023
EIP = 00401338 ESP = 0019FEFC EBP = 0019FEFC
EFL = 00000202
As we can see EAX now has the value for our memory location. Most importantly, the EIP points to code back in the main function.
However
before, we go back to main, let’s clean up the stack and pop the value of EBP
off of the stack. Currently EBP = 0019FEFC and
ESP = 0019FEFC. This would be safe to conclude that the value of EBP
is at the top of the stack. So popping this off increases the stack size by 4
bytes and puts the value “0019ff04” into EBP.
So we now have our registers as follow after
execution of “pop ebp”:
EAX = 00407000 EBX = 00359000 ECX = 00000001 EDX = 00407578 ESI = 00401023 EDI = 00401023 EIP = 00401339 ESP = 0019FF00 EBP = 0019FF04 EFL = 00000202
EAX = 00407000 EBX = 00359000 ECX = 00000001 EDX = 00407578 ESI = 00401023 EDI = 00401023 EIP = 00401339 ESP = 0019FF00 EBP = 0019FF04 EFL = 00000202
Note also that EBP points to our EBP which was
setup for main as we have shown above.
Once we have completed “pop”ing ebp, the next instruction was “ret” which returned execution to main().
Once we have completed “pop”ing ebp, the next instruction was “ret” which returned execution to main().
Now that we are back in main(), the next
instruction is to return "Returning!!"; which is performed via “mov
eax,407010h”. Similarly to above a memory address is being placed into “eax’
register. Once again, EAX holds our return value. Looking at the memory address
“407010h” we see:
0x00407010 75746552 Retu
0x00407014 6e696e72 rnin
0x00407018 00212167 g!!.
0x0040701C 00000000 ....
0x00407020 00000000 ....
0x00407024 ffffffff ÿÿÿÿ
0x00407028 00000001 ....
0x0040702C 359603eb ë.–5
0x00407030 ca69fc14 .üiÊ
Once we have executed the
instruction, our registers now look like:
EAX = 00407010 EBX = 00359000 ECX = 00000001 EDX = 00407578 ESI = 00401023 EDI = 00401023 EIP = 0040134D ESP = 0019FF04 EBP = 0019FF04 EFL = 00000202
EAX = 00407010 EBX = 00359000 ECX = 00000001 EDX = 00407578 ESI = 00401023 EDI = 00401023 EIP = 0040134D ESP = 0019FF04 EBP = 0019FF04 EFL = 00000202
Once again, EAX holds our return value.
Next we “pop” the ebp value off of the stack and
then return execution to the code that called main.
Ok. That’s it for this entry. I now have a better
understand of the function stacks, prologues and epilogues. Do you?
References:
Open Security Training
Open Security Training
Other posts in this series:
2. Beginning
x86 disassembly – Understanding function arguments and variables with Visual
Studio 2017
8. Beginning
x86 disassembly – Understanding the basics of “memcpy” with Visual Studio 2017
No comments:
Post a Comment