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.
In this post I’m looking at function
arguments and variables
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 arguments and variables
* The ojective is to get a better
understanding of these are disassembled in C
* Author Nik Alleyne
* Blog: securitynik.blogspot.com
* File: args_and_variables.c
* The value for argv[1] is 7
*
*/
#include <stdlib.h>
int SecurityNik(int x, int y)
{
return 2*x+y;
}
int main(int argc, char **argv)
{
int a;
a = atoi(argv[1]);
return SecurityNik(argc, a);
}
… and the disassembled code for
main()
int main(int argc, char **argv)
{
00401010 push
ebp
00401011 mov
ebp,esp
00401013 push
ecx
int a;
a = atoi(argv[1]);
00401014 mov
eax,4
00401019 shl
eax,0
0040101C mov
ecx,dword ptr [ebp+0Ch]
0040101F mov
edx,dword ptr [ecx+eax]
00401022 push
edx
00401023 call
dword ptr ds:[004030B0h]
00401029 add
esp,4
0040102C mov
dword ptr [ebp-4],eax
return
SecurityNik(argc, a);
0040102F mov
eax,dword ptr [ebp-4]
00401032 push
eax
00401033 mov
ecx,dword ptr [ebp+8]
00401036 push
ecx
00401037 call
00401000
0040103C add
esp,8
}
0040103F mov
esp,ebp
00401041 pop
ebp
00401042 ret
… and the disassembled code for
SecurityNik()
int SecurityNik(int x, int y)
{
00401000 push
ebp
00401001 mov
ebp,esp
return 2*x+y;
00401003 mov
eax,dword ptr [ebp+8]
return 2*x+y;
00401006 mov
ecx,dword ptr [ebp+0Ch]
00401009 lea
eax,[ecx+eax*2]
}
0040100C pop
ebp
0040100D ret
As we move forward and look at the
stack we see two values of importance at the top
0x0019FF08 004013be ..@. – Return pointer
0x0019FF0C 00000002 .... – value or argc from main
function. There are 2 arguments
0x0019FF10 0061e928 (éa.
0x0019FF14 0061c948 HÉa.
0x0019FF18 0019ff70 pÿ..
0x0019FF1C 004012a0 .@.
0x0019FF20 1200d550 PÕ..
Starting
off by looking at main(), we see the stack is built using the prologue
push ebp
mov ebp,esp
push ebp
mov ebp,esp
The
previous two instructions were discussed in this post on prologues
and epiologues
Next instruction we see is “push ecx”. My understanding is this is allocating 4 bytes on the stack for the variable “int a”. However, before we “push ecx” on the stack, let’s see what the registers look like:
Next instruction we see is “push ecx”. My understanding is this is allocating 4 bytes on the stack for the variable “int a”. However, before we “push ecx” on the stack, let’s see what the registers look like:
EAX = 0FF11944
EBX = 0028C000 ECX = 00000002
EDX = 00404388 ESI = 004013D0 EDI = 004013D0 EIP = 00401013
ESP = 0019FF04
EBP = 0019FF04 EFL = 00000206
Looking at the top of the stack:
0x0019FF04 0019ff18 .ÿ..
0x0019FF08 004013be ..@.
0x0019FF0C 00000002 ....
0x0019FF10 0063e738 8çc.
0x0019FF14 0063c948 HÉc.
0x0019FF18 0019ff70 pÿ..
0x0019FF1C 004012a0 .@.
Looking at the top of the stack:
0x0019FF04 0019ff18 .ÿ..
0x0019FF08 004013be ..@.
0x0019FF0C 00000002 ....
0x0019FF10 0063e738 8çc.
0x0019FF14 0063c948 HÉc.
0x0019FF18 0019ff70 pÿ..
0x0019FF1C 004012a0 .@.
As
we look at the first few bytes on the stack, we see that ESP points to
“0x0019ff04”.
Let’s now execute the “push ecx” instruction. For this we expect the value at the top of the stack to be equal to “00000002”. This also means the top of the stack “0x0019ff04” will now be decremented by 4. So “0x0019ff04 - 4” will equal to “0x0019FF00”. Let’s verify this.
0x0019FF00 00000002 ....
0x0019FF04 0019ff18 .ÿ..
0x0019FF08 004013be ..@.
0x0019FF0C 00000002 ....
0x0019FF10 0063e738 8çc.
0x0019FF14 0063c948 HÉc.
0x0019FF18 0019ff70 pÿ..
Let’s now execute the “push ecx” instruction. For this we expect the value at the top of the stack to be equal to “00000002”. This also means the top of the stack “0x0019ff04” will now be decremented by 4. So “0x0019ff04 - 4” will equal to “0x0019FF00”. Let’s verify this.
0x0019FF00 00000002 ....
0x0019FF04 0019ff18 .ÿ..
0x0019FF08 004013be ..@.
0x0019FF0C 00000002 ....
0x0019FF10 0063e738 8çc.
0x0019FF14 0063c948 HÉc.
0x0019FF18 0019ff70 pÿ..
Good stuff!! We see the top of the
stack has now changed along with the value from ECX has been pushed to the
stack.
Moving along … executing the next
instruction “mov eax,4”. Basically pushes the value of 4 into the EAX register.
Next we “shl eax,0”. This means to shift left the value in eax by 0. This basically keeps the value in EAX the same. Let move along to something that seems more interesting.
Next instruction is “mov ecx,dword ptr [ebp+0Ch]”. Basically we are moving the value at “ebp + 12” into the “ecx” register.
Let’s take a look at the register before we move forward.
EAX = 00000004 EBX = 0028C000 ECX = 00000002 EDX = 00404388 ESI = 004013D0 EDI = 004013D0 EIP = 00401019 ESP = 0019FF00 EBP = 0019FF04 EFL = 00000206
As we can see “EBP = 0019FF04”. Therefore EBP + 12 means we are more than likely preparing to read in an argument from the memory location “ebp + 12” which is “0019ff10”.
Next we “shl eax,0”. This means to shift left the value in eax by 0. This basically keeps the value in EAX the same. Let move along to something that seems more interesting.
Next instruction is “mov ecx,dword ptr [ebp+0Ch]”. Basically we are moving the value at “ebp + 12” into the “ecx” register.
Let’s take a look at the register before we move forward.
EAX = 00000004 EBX = 0028C000 ECX = 00000002 EDX = 00404388 ESI = 004013D0 EDI = 004013D0 EIP = 00401019 ESP = 0019FF00 EBP = 0019FF04 EFL = 00000206
As we can see “EBP = 0019FF04”. Therefore EBP + 12 means we are more than likely preparing to read in an argument from the memory location “ebp + 12” which is “0019ff10”.
Now let’s look at the stack (ESP)
0x0019FF00 00000002 ....
0x0019FF04 0019ff18 .ÿ.. – EBP
0x0019FF08 004013be ..@. – EBP + 4
0x0019FF0C 00000002 .... – EBP + 8
0x0019FF10 0063e738 8çc. – EBP + 12
0x0019FF14 0063c948 HÉc.
0x0019FF18 0019ff70 pÿ..
0x0019FF00 00000002 ....
0x0019FF04 0019ff18 .ÿ.. – EBP
0x0019FF08 004013be ..@. – EBP + 4
0x0019FF0C 00000002 .... – EBP + 8
0x0019FF10 0063e738 8çc. – EBP + 12
0x0019FF14 0063c948 HÉc.
0x0019FF18 0019ff70 pÿ..
From above we confirm that EBP + 12
is at “0x0019FF10”
and has a value of “0063e738”.
This value will be copied into the ecx register. Let’s step into the program
and revisit the register:
EAX = 00000004 EBX = 0028C000 ECX = 0063E738 EDX = 00404388 ESI = 004013D0 EDI = 004013D0 EIP = 0040101F ESP = 0019FF00 EBP = 0019FF04 EFL = 00000206
EAX = 00000004 EBX = 0028C000 ECX = 0063E738 EDX = 00404388 ESI = 004013D0 EDI = 004013D0 EIP = 0040101F ESP = 0019FF00 EBP = 0019FF04 EFL = 00000206
Next instruction states “mov
edx,dword ptr [ecx+eax]”, which is move the value at memory address “ecx+eax”
into “edx”. Looking at the registers above we see ecx is found at “ECX = 0063E738” and EAX has a value “EAX = 00000004”. Therefore 0063E738 + 4 = 0063E73C. Therefore we
need the value which is found at “0063E73C” to be in edx. Let’s execute the instruction and look
at the register
EAX = 00000004 EBX = 0028C000
ECX = 0063E738 EDX = 0063E7A0
ESI = 004013D0 EDI = 004013D0 EIP = 00401022
ESP = 0019FF00 EBP = 0019FF04 EFL = 00000206
Above we see EDX that has a value of “EDX = 0063E7A0”
which corresponds to the value at offset “0063E73C”.
0x0063E73C
0063e7a0 çc.
0x0063E740 00000000 ....
0x0063E744 4c5c3a44 D:\L
0x0063E748 6e726165 earn
0x0063E74C 20676e69 ing
0x0063E750 6574614d Mate
0x0063E754 6c616972 rial
0x0063E740 00000000 ....
0x0063E744 4c5c3a44 D:\L
0x0063E748 6e726165 earn
0x0063E74C 20676e69 ing
0x0063E750 6574614d Mate
0x0063E754 6c616972 rial
The next instruction “push edx”
pushes the value of the EDX register unto the stack. Looking at the stack, we
see this value is now at the top of the stack:
0x0019FEFC 004fe0e8 èàO.
0x0019FF00 00000002 ....
0x0019FF04 0019ff18 .ÿ..
0x0019FF08 004013be ..@.
0x0019FF0C 00000002 ....
0x0019FF10 004fe080 ۈO.
0x0019FF14 004fc948 HÉO.
0x0019FF14 004fc948 HÉO.
Next there is a “call dword ptr
ds:[004030B0h]”. We will “step over” this as this is the call for “atoi”. In stepping over this function, we also did
not get to see the action taken to move “7” the value in “argv[1]” into EAX.
However, if we look at the registers we will see the “7” above moved into EAX.
EAX = 00000007 EBX = 003EA000 ECX = 0019FEC8 EDX = 00000000 ESI = 004013D0 EDI = 004013D0 EIP = 00401029 ESP = 0019FEFC EBP = 0019FF04 EFL = 00000202
EAX = 00000007 EBX = 003EA000 ECX = 0019FEC8 EDX = 00000000 ESI = 004013D0 EDI = 004013D0 EIP = 00401029 ESP = 0019FEFC EBP = 0019FF04 EFL = 00000202
Next instruction says “add esp,4”.
This means we are adding 4 bytes to the ESP. ESP currently points to “ESP = 0019FEFC” so adding 4 to ESP moves the ESP
value to “0019FEFC + 4 = 0019FF00”. If we look at the stack after
this instruction is completed we see the top of the stack now shows.
EAX = 00000007 EBX = 003EA000 ECX = 0019FEC8 EDX = 00000000 ESI = 004013D0 EDI = 004013D0 EIP = 0040102C ESP = 0019FF00 EBP = 0019FF04 EFL = 00000216
EAX = 00000007 EBX = 003EA000 ECX = 0019FEC8 EDX = 00000000 ESI = 004013D0 EDI = 004013D0 EIP = 0040102C ESP = 0019FF00 EBP = 0019FF04 EFL = 00000216
0x0019FF00 00000002 ....
0x0019FF04 0019ff18 .ÿ..
0x0019FF08 004013be ..@.
0x0019FF0C 00000002 ....
0x0019FF10 005de738 8ç].
0x0019FF14 005dc948 HÉ].
0x0019FF18 0019ff70 pÿ..
0x0019FF18 0019ff70 pÿ..
Next up we need to “mov dword ptr
[ebp-4],eax”. Which is moving the value of 7 which is in EAX above to the
memory location occupied by EBP-4. Currently EBP points to “0019FF04”. So EBP – 4 = “0019FF04
– 4 = 0019FF00”.
Currently and as can be seen above,
the current value at ebp-4 is “0x0000002”. However, after the next instruction
is executed, we should expect to see 7 there as shown below:
0x0019FF00 00000007 .... – EBP - 4
0x0019FF04 0019ff18 .ÿ.. - EBP
0x0019FF08 004013be ..@.
0x0019FF0C 00000002 ....
0x0019FF10 005de738 8ç].
0x0019FF14 005dc948 HÉ].
0x0019FF18 0019ff70 pÿ..
0x0019FF18 0019ff70 pÿ..
At this point, we picked up an argument that has a value of 7 (ebp+12). Also we know that there is a value a local variable (ebp-4). At this point, the value which was picked up as an argument is now placed into the local variable “int a”.
Next instruction “mov eax,dword ptr [ebp-4]” is to
take our local variable “int a”, the value or argv[1] and move it into the
“eax” register. Let’s take a look at our register before we make this move.
EAX = 00000007 EBX = 00297000
ECX = 0019FEC8 EDX = 00000000 ESI = 004013D0
EDI = 004013D0 EIP = 0040102F ESP = 0019FF00 EBP = 0019FF04
EFL = 00000216
Now let’s look at our stack.
0x0019FF00 00000007 .... – Top of the
stack – Current value of ESP – also EBP - 4
0x0019FF04 0019ff18 .ÿ.. - EBP
0x0019FF08 004013be ..@.
0x0019FF0C 00000002 ....
0x0019FF10 0055e380 ۋU.
0x0019FF14 0055c948 HÉU.
0x0019FF18 0019ff70 pÿ..
0x0019FF18 0019ff70 pÿ..
Looking from above we see that the value currently
at the top of the stack is currently the value of EBP – 4. Therefore the value
7 which sits at the top of the stack “7” should be moved into “EAX”. However,
“EAX” already has “7” therefore the change is not noticeable.
The next instruction “push eax” pushes the value of
“7” unto the top of the stack.
Looking at the registers …
EAX = 00000007 EBX = 00395000
ECX = 0019FEC8 EDX = 00000000 ESI = 004013D0
EDI = 004013D0 EIP = 00401033 ESP = 0019FEFC EBP = 0019FF04
EFL = 00000216
… and the stack
0x0019FEFC 00000007 ....
0x0019FF00 00000007 ....
0x0019FF04 0019ff18 .ÿ..
0x0019FF08 004013be ..@.
0x0019FF0C 00000002 ....
0x0019FF10 0055e380 ۋU.
0x0019FF14 0055c948 HÉU.
0x0019FF14 0055c948 HÉU.
At this point we have pushed our “argv[1]” value
unto the stack. Next step is to get the value for “argc” unto the stack. This
comes from “mov ecx,dword ptr [ebp+8]”. Taking a copy of the previous output of
the registers and the stack we see the following:
EAX = 00000007 EBX = 00395000 ECX = 0019FEC8 EDX = 00000000 ESI = 004013D0 EDI = 004013D0 EIP = 00401033 ESP = 0019FEFC EBP = 0019FF04 EFL = 00000216
EAX = 00000007 EBX = 00395000 ECX = 0019FEC8 EDX = 00000000 ESI = 004013D0 EDI = 004013D0 EIP = 00401033 ESP = 0019FEFC EBP = 0019FF04 EFL = 00000216
… and the stack
0x0019FEFC 00000007 .... – EBP - 8
0x0019FF00 00000007 .... – EBP - 4
0x0019FF04 0019ff18 .ÿ.. – EBP
0x0019FF08 004013be ..@. – EBP + 4
0x0019FF0C 00000002 .... – EBP + 8
0x0019FF10 0055e380 ۋU.
0x0019FF14 0055c948 HÉU.
As we can see from above, the value of “EBP+8” is “2” and is found at offset “0x0019FF0C”. This value will now be moved into ECX and then “push ecx” will push the value in the ECX register unto the stack. Let’s look at the registers …
EAX = 00000007 EBX = 00395000 ECX = 00000002
EDX = 00000000 ESI = 004013D0 EDI = 004013D0
EIP = 00401037 ESP = 0019FEF8
EBP = 0019FF04 EFL = 00000216
… now the stack
0x0019FEF8 00000002 ....
0x0019FEFC 00000007 ....
0x0019FF00 00000007 ....
0x0019FF04 0019ff18 .ÿ..
0x0019FF08 004013be ..@.
0x0019FF0C 00000002 ....
0x0019FF10 0055e380 ۋU.
0x0019FF10 0055e380 ۋU.
Now if we look at the stack, we see
our two arguments, the value for “a” “7”
and the value of “argc” “2”. Important point to note is that the function
arguments are passed unto the stack in reverse order. That is from right to
left. In this example, the SecurityNik(argc, a), “a” (7) is first passed unto
the stack, then “argc” (2) is next passed unto the stack. Now that we have our
arguments, we see the next instruction is “call
00401000”. This basically means we are calling the function at memory
address “00401000”. If we remember from above, the instruction at “00401000” is
basically the first instruction in “int SecurityNik(int x, int y)” function.
Let’s now step into this function.
Once we executed the instruction, we
then hit the prologue “pusb ebp” and “mov ebp, esp”. See my post on prologue
and epilogue for how to understand prologues and epiolgues.
Next instruction is “mov eax,dword ptr [ebp+8]” which is move the
value from ebp+8 into EAX and this is followed by “mov ecx,dword ptr [ebp+0Ch]”
which is move the value of “ebp+12” into ECX.
Important to note that when we see “EBP+” we are talking about function
arguments.
Let’s look into our registers to see
where EBP lies before we look at ebp+8 and ebp+12.
EAX = 00000007 EBX = 00395000 ECX = 00000002 EDX = 00000000 ESI = 004013D0 EDI = 004013D0 EIP = 00401003 ESP = 0019FEF0 EBP = 0019FEF0 EFL = 00000216
Now if we look at our stack to locate “ebp+8” and “ebp+12”, we see:
0x0019FEF0 0019ff04 .ÿ.. EBP
0x0019FEF4 0040103c <.@.
EBP + 4
0x0019FEF8 00000002 .... EBP + 8
0x0019FEFC 00000007 .... EBP + 12
0x0019FF00 00000007 ....
0x0019FF04 0019ff18 .ÿ..
0x0019FF08 004013be ..@.
0x0019FF08 004013be ..@.
So from above, we see that the
values 2 and 7 are found in EBP+8 and EBP+12 respectively.
When we execute “mov eax,dword ptr
[ebp+8]” the value of “2” gets moved into EAX, then for “mov ecx,dword ptr
[ebp+0Ch]” the value of “7” gets placed into ECX. This basically means for the
function “SecurityNik(int x, int y)” the arguments of “x” and “y” will have
values of “2” and “7” respectively.
Looking at the registers after these
instructions have been executed we see:
EAX = 00000002 EBX = 00395000 ECX = 00000007
EDX = 00000000 ESI = 004013D0 EDI = 004013D0
EIP = 00401009 ESP = 0019FEF0 EBP = 0019FEF0
EFL = 00000216
Next instruction states “lea eax,[ecx+eax*2]”. This first takes the value in the eax register which is 2, then multiplies it by 2 which equals 4 then add this to the value in ecx (7). This means this instruction will move a value of “11” into the eax register.
Next instruction states “lea eax,[ecx+eax*2]”. This first takes the value in the eax register which is 2, then multiplies it by 2 which equals 4 then add this to the value in ecx (7). This means this instruction will move a value of “11” into the eax register.
Looking at the registers after the
instruction is executed, we see
EAX = 0000000B EBX = 00395000 ECX = 00000007
EDX = 00000000 ESI = 004013D0 EDI = 004013D0
EIP = 0040100C ESP = 0019FEF0 EBP = 0019FEF0
EFL = 00000216
As we can see “EAX = 0000000B” which from hex to decimal, “B” equals to “11”.
Once the epilogue is completed, see this post for information on epilogue … the code then “ret” to main where the next instruction is ‘add esp, 8’. Basically, here we are beginning the process of cleaning up the stack, then we move into the epilogue.
Once the epilogue is completed, see this post for information on epilogue … the code then “ret” to main where the next instruction is ‘add esp, 8’. Basically, here we are beginning the process of cleaning up the stack, then we move into the epilogue.
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