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!
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,esp” starts
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
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 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
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”.
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
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
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
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.
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.
References:
Open Security Training
x86 Instruction Set Reference
VRegisters Window (Intel x86 Processor Flags)
Tutorial Point – Assembly Conditions
Open Security Training
x86 Instruction Set Reference
VRegisters Window (Intel x86 Processor Flags)
Tutorial Point – Assembly Conditions
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