Monday, July 24, 2017

Beginning x86 disassembly – Understanding Conditional Jumps 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.

Let’s get started!

To understand conditional jumps, let’s start with the code below:

/*
* This file focuses on flow control via unconditional jumps using the "if" statement
* The objective is to get a better understanding of how these are disassembled in C
* Author Nik Alleyne
* Blog: securitynik.blogspot.com
* File: flow_control_if.c
*
*/
#include <stdio.h>

int main()
{
  int a = 1, b = 2;

  if (a == b)
    {
      return "A equals B";
    }

  if (a > b)
    {
      return "A greater than B";
    }

  if (a < b)
    {
      return "A less than B";
    }
  return "Something went wrong!";

}


And here is our disassembly code for above:

int main()
{
00401000  push        ebp 
00401001  mov         ebp,esp 
00401003  sub         esp,8 
  int a = 1, b = 2;
00401006  mov         dword ptr [ebp-4],1 
0040100D  mov         dword ptr [ebp-8],2 

  if (a == b)
00401014  mov         eax,dword ptr [ebp-4] 
00401017  cmp         eax,dword ptr [ebp-8] 
0040101A  jne         00401023 
    {
      return "A equals B";
0040101C  mov         eax,404000h 
00401021  jmp         00401046 
    }

  if (a > b)
00401023  mov         ecx,dword ptr [ebp-4] 
00401026  cmp         ecx,dword ptr [ebp-8] 
00401029  jle         00401032 
    {
      return "A greater than B";
0040102B  mov         eax,40400Ch 
00401030  jmp         00401046 
    }

  if (a < b)
00401032  mov         edx,dword ptr [ebp-4] 
00401035  cmp         edx,dword ptr [ebp-8] 
00401038  jge         00401041 
    {
      return "A less than B";
0040103A  mov         eax,404020h 
0040103F  jmp         00401046 
    }
  return "Something went wrong!";
00401041  mov         eax,404030h 

}
00401046  mov         esp,ebp 
00401048  pop         ebp 
00401049  ret 

Let’s now get down to understanding conditional jumps.

Our first two commands “00401000  push ebp” and “00401001  mov ebp,espstarts up our prologue. See this post for information about prologue and epilogue.

Next instruction “00401003  sub  esp,8” allocates 8 bytes on the stack for our two variables “int a” and “int b”. Because an int is 4 bytes, we allocate 8 bytes, 4 for each of the integers (a and b).

Once the space is allocated on the stack the next two instructions move the values of our initialized variables unto the stack.

Before we move forward, let’s look at our registers and our stack.

First up the registers:
EAX = 0FD91944 EBX = 0029D000 ECX = 00000001 EDX = 004043D0 ESI = 004013D0 EDI = 004013D0 EIP = 00401006 ESP = 0019FEFC EBP = 0019FF04 EFL = 00000216

… and now for our stack

0x0019FEFC  0019ff10  .ÿ..  – EBP-8
0x0019FF00  0fce56e5  åVÎ.  – EBP-4
0x0019FF04  0019ff18  .ÿ..  - EBP
0x0019FF08  004013be  ..@.
0x0019FF0C  00000001  ....
0x0019FF10  00534440  @DS.
0x0019FF14  0051c940  @ÉQ.

From what we can see above, EBP is found at “0019FF04”. Hence EBP-4 is found at “0x0019FF00” and EBP-8 is found at “0x0019FEFC”.

When the next two instructions “00401006  mov  dword ptr [ebp-4],1” and “0040100D  mov  dword ptr [ebp-8],2” are executed, the value “1” which is the value of “int a” is first pushed on the stack, then the value “2” which is value of “int b” is pushed unto the stack.

Let’s look at our registers after the two instruction are executed  …

EAX = 0FD91944 EBX = 0029D000 ECX = 00000001 EDX = 004043D0 ESI = 004013D0 EDI = 004013D0 EIP = 00401014 ESP = 0019FEFC EBP = 0019FF04 EFL = 00000216

… and now our stack after the push.
0x0019FEFC  00000002  .... – EBP-8
0x0019FF00  00000001  .... – EBP-4
0x0019FF04  0019ff18  .ÿ.. - EBP
0x0019FF08  004013be  ..@.
0x0019FF0C  00000001  ....
0x0019FF10  00534440  @DS.
0x0019FF14  0051c940  @ÉQ.


An important take away from above is when we see “ebp - N” we are talking about local variables. When talking about “ebp + N” we are talking about function arguments. In this case we have 2 “ebp-n” therefore it would be safe to say we have two local variables.

Something to note is that there are lots of “cmp” in this disassembly. The “cmp” instruction actually does a subtract of the second operand from the first operand then setting the “EFLAGS” register. More importantly, the “cmp” instruction is typically used with conditional (cc) instruction such as “jcc”. Consider the “cmp” as typical to a “sub” instruction.

Let’s now analyze the “if” statement:

The first part is checking to see if “a = b”, which is basically if “1=2”.

The first instruction in this sequence says “mov  eax,dword ptr [ebp-4]”. If we remember from above, “ebp-4” is “1”. So this instruction results in us moving “1” into the “eax” register. From the previous print of the register, we see “
EAX = 0FD91944”. Once the instruction is executed, we see the following in the registers:

EAX = 00000001 EBX = 003DA000 ECX = 00000001 EDX = 004043D0 ESI = 004013D0 EDI = 004013D0 EIP = 00401017 ESP = 0019FEFC EBP = 0019FF04 EFL = 00000216

From above we see “eax” now has a value of “1”. The next instruction says “cmp eax,dword ptr [ebp-8]”. As we also know from the above memory dump “ebp-8” has a value of “2”. If I understand this correctly from the StackOverflow article on cmp, this instruction is basically doing a “sub eax – [ebp-8]” after which the “EFlAGS” are set. Here is what those flags look like before execution:

OV = 0 UP = 0 EI = 1 PL = 0 ZR = 0 AC = 1 PE = 1 CY = 0

Once the instruction is run, the “EFLAGS” now look like:

OV = 0 UP = 0 EI = 1 PL = 1 ZR = 0 AC = 1 PE = 1 CY = 1

The next instruction “jne 00401023”, says to jump to the address “00401023” if the equation does not equal to “0. This is determined by checking the “ZR” flag to see if it is set (“1”). From above we see it is not set as “
ZR = 0”, therefore we can expect the code to jump to “00401023” which basically begins evaluating the next instruction to see if “a > b”.

Now that we are at “00401023”, the instruction here states “mov ecx,dword ptr [ebp-4]”. This means move the value at “ebp-4” which is “1” into “ecx”. If we look at the previous dump of the registers, we see that “ecx” already has a value of “1”.

The next instruction “cmp ecx,dword ptr [ebp-8]”, compares the value in “ecx” (“1”)with the value at memory address ebp-8 (“2”). This looks like “1-2”, which means that the answer would be -1 thus the value in “ecx” is not greater than the value at “ebp-8”.

Let’s look at the registers to see what they look like before the “cmp”

OV = 0 UP = 0 EI = 1 PL = 1 ZR = 0 AC = 1 PE = 1 CY = 1

… and after the instruction is executed …
OV = 0 UP = 0 EI = 1 PL = 1 ZR = 0 AC = 1 PE = 1 CY = 1

Next instruction “jle 00401032” says to jump to the address if the previous compare action is less than or equal to.

My understanding is that “jle” is determined by the “ZF = 1 or SF <> OF”. Looking at the flags above, we see that “
ZR = 0” while “OV = 0”  and “PL = 1”. Therefore, since the zero flag is not set and the overflow flag not equal to the sign flag, the next instruction to be executed will be found at memory address “00401032”. This instruction is “mov edx,dword ptr [ebp-4]” followed by “cmp edx,dword ptr [ebp-8]”. Basically we are first taking the value of “ebp-4” and place it into the “edx” register then compare this value to the value at memory location “ebp-8”.

This comparison is basically checking to see if “a<b”.  Leveraging the same concept from earlier in that “cmp” does a “sub edx – [ebp-8]” we get a value of “1-2”. This shows that 1 is less than 2. However the next instruction is “jge 00401041”. Meaning if 1 greater than 2, then go to memory address “00401041”. As we know, 1 is not greater than 2, so we should not be hitting the code ‘return “A less than B”’.

Let’s verify our “EFLAGS” before moving forward

OV = 0 UP = 0 EI = 1 PL = 1 ZR = 0 AC = 1 PE = 1 CY = 1

… and after execution
OV = 0 UP = 0 EI = 1 PL = 1 ZR = 0 AC = 1 PE = 1 CY = 1

Now that we are at “jge 00401041”, we know that “jge” leverages the “SF = OF”. From above we see that “
PL = 1” and “OV = 0”. Hence the “SF” does not equal to “OF”, therefore there should not be any jumps and the next instruction in this sequence should continue. That instruction being “mov eax,404020h”.

Looking at the memory address at “404020h” we see the string:

0x00404020  656c2041  A le
0x00404024  74207373  ss t
0x00404028  206e6168  han
0x0040402C  00000042  B...

This means the once the next instruction is executed, the “eax” register will contain the value of “404020h” which is a pointer to the string “A less than B”.

Taking a quick glance at the register after execution.
EAX = 00404020 EBX = 002B4000 ECX = 00000001 EDX = 00000001 ESI = 004013D0 EDI = 004013D0 EIP = 0040103F ESP = 0019FEFC EBP = 0019FF04 EFL = 00000297

this is then followed by an unconditional jump “jmp 00401046” which moves the next instruction down to the epilogue. See this post for more on prologue and epilogue.




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

No comments:

Post a Comment