Monday, July 24, 2017

Beginning x86 disassembly – Understanding function arguments and variables with Visual Studio 2017

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!
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
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:
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   .@.
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ÿ..
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”.
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ÿ..
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
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
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.
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

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


0x0019FF00  00000002  ....
0x0019FF04  0019ff18  .ÿ..
0x0019FF08  004013be  ..@.
0x0019FF0C  00000002  ....
0x0019FF10  005de738  8ç].
0x0019FF14  005dc948  HÉ].
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ÿ..

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ÿ..

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.

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

… 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.

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  ..@.

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.

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.



Reference:
Open Security Training


Other posts in this series:
8. Beginning x86 disassembly – Understanding the basics of “memcpy” with Visual Studio 2017

No comments:

Post a Comment