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 bit shifting, let’s start with the code below:
/*
* This file focuses on bit shifting
left and right
* The objective is to get a better
understanding of how these are disassembled in C
* Author Nik Alleyne
* Blog: securitynik.blogspot.com
* File: shl_shr.c
*
*/
#include <stdio.h>
int main()
{
unsigned int a, b, c;
a = 0x40;
b = a * 8;
c = b / 16;
return c;
}
Here is the disassembled code:
int main()
{
00401000 push
ebp
00401001 mov
ebp,esp
00401003 sub
esp,0Ch
unsigned int a, b, c;
a = 0x40;
00401006 mov
dword ptr [ebp-4],40h
b = a * 8;
0040100D mov
eax,dword ptr [ebp-4]
00401010 shl
eax,3
00401013 mov
dword ptr [ebp-8],eax
c = b / 16;
00401016 mov
ecx,dword ptr [ebp-8]
00401019 shr
ecx,4
0040101C mov
dword ptr [ebp-0Ch],ecx
return c;
0040101F mov
eax,dword ptr [ebp-0Ch]
}
00401022 mov
esp,ebp
00401024 pop
ebp
00401025 ret
Since this example builds on the other posts in this series, I will not being going through all the instructions with details. Instead I will give the attention to the newer instructions.
Since this example builds on the other posts in this series, I will not being going through all the instructions with details. Instead I will give the attention to the newer instructions.
Looking at the disassembly, we see
the typical stuff associated with the prologue, that is “push ebp” and “mov ebp,esp”. See
this post for more on prologue and epilogue.
Next up we see “sub esp,0Ch”. This
reserves 12 bytes for the variables “int a, b, c”. Let’s peak at the registers
before the sub …
EAX = 66CD1944 EBX = 00251000
ECX = 00000001 EDX = 00404388 ESI = 004013B0
EDI = 004013B0 EIP = 00401003 ESP = 0019FF04 EBP = 0019FF04
EFL = 00000202
… and the memory
0x0019FF04 0019ff18 .ÿ.. - EBP
0x0019FF08 0040139e ž.@. – Return Pointer
0x0019FF0C 00000001 ....
0x0019FF10 0051bd18 ..Q.
0x0019FF14 00518338 8ƒQ.
0x0019FF18 0019ff70 pÿ..
0x0019FF1C 00401280 €.@.
0x0019FF1C 00401280 €.@.
Let’s now execute the instruction
“sub esp, 0Ch” and revisit the register …
EAX = 66CD1944 EBX = 00251000
ECX = 00000001 EDX = 00404388 ESI = 004013B0
EDI = 004013B0 EIP = 00401006 ESP = 0019FEF8 EBP = 0019FF04
EFL = 00000212
… and now the stack with the 12
bytes allocated
0x0019FEF8 00000000 .... – [ebp-c] – location of int c
0x0019FEFC 0019ff10 .ÿ.. – [ebp-8] – location of int b
0x0019FF00 66c256e5 åVÂf – [ebp-4] – location of int a
0x0019FF04 0019ff18 .ÿ.. - EBP
0x0019FF08 0040139e ž.@.
0x0019FF0C 00000001 ....
0x0019FF10 0051bd18 ..Q.
0x0019FF10 0051bd18 ..Q.
From above we see the 12 bytes have
been allocated for the 3 integer (int) variables. Remember each int is 4 bytes
or 32 bits. Therefore since we have 3 integers, 4x3 bytes have been allocated
for local variable storage.
Moving along …
Next command “mov dword ptr
[ebp-4],40h” states to move hex 40 or
decimal 64 into the memory location occupied by “[ebp-4]”. After executing the
instruction and looking at the stack we see …
0x0019FEF8 00000000 ....
0x0019FEFC 0019ff10 .ÿ..
0x0019FF00 00000040 @... – [ebp-4]
0x0019FF04 0019ff18 .ÿ.. - EBP
0x0019FF08 0040139e ž.@.
0x0019FF0C 00000001 ....
0x0019FF10 0051bd18 ..Q.
0x0019FF10 0051bd18 ..Q.
The next instruction “mov eax,dword
ptr [ebp-4]” states to take our value “0x40” at “[ebp-4]” and pace it in the
“EAX” register. Upon executing this instruction our register looks like …
EAX = 00000040 EBX = 00251000 ECX = 00000001
EDX = 00404388 ESI = 004013B0 EDI = 004013B0
EIP = 00401010 ESP = 0019FEF8 EBP = 0019FF04
EFL = 00000212
Next instruction “shl eax,3” is one
of our first new instruction. Let’s look at this a bit more. “shl” is shift to
the left and is similar to multiplying by 2n where “n” is the number
of bits to be shifted.
Let’s look at this through an example. If we have the number “0000 0010” or 2 in decimal and decide to shift this 1 bit, we get the following “0000 0100”. Notice the “1” has been shifted to the left. This value “0000 0100” is now 4. Remember our original value 2 and we shift by 1. Therefore 21 is 2 hence we multiply 2*2 and got 4.
Let’s look at this through an example. If we have the number “0000 0010” or 2 in decimal and decide to shift this 1 bit, we get the following “0000 0100”. Notice the “1” has been shifted to the left. This value “0000 0100” is now 4. Remember our original value 2 and we shift by 1. Therefore 21 is 2 hence we multiply 2*2 and got 4.
Let’s look at it through another
example using the same number “0000 0010” or 2 in decimal. If we shift this left
by 2 bits (22 =4), we get “0000 1000”. Hence we now have 4 * 2 =
8.
Ok! One more to reinforce the point. If we have the number “0000 0010” or 2 in decimal and decide to shift this left 3 bit (23=8), we get the number 0001 0000 which is 16. As we know from our calculations 8 * 2=16
Ok! One more to reinforce the point. If we have the number “0000 0010” or 2 in decimal and decide to shift this left 3 bit (23=8), we get the number 0001 0000 which is 16. As we know from our calculations 8 * 2=16
Now that we have those examples clearly explained, let’s pay attention to our specific instruction “shl eax,3”.
As we know from above EAX has a
value of 0x40 (dec 64) which is “0100 0000”. When this value is shifted left by
3 bits we get “10 0000 0000” (512 decimal) and (x200). Once we execute the
instruction “shl eax,3” and revisit the registers, we see …
EAX = 00000200 EBX = 00251000 ECX = 00000001
EDX = 00404388 ESI = 004013B0 EDI = 004013B0
EIP = 00401013 ESP = 0019FEF8 EBP = 0019FF04
EFL = 00000206
Now that EAX has “x200” this value
is now placed on the stack at the position occupied by “[ebp-8]” which is for
the variable “b”. Executing the next instruction “dword ptr [ebp-8],eax” and
looking at the stack we see …
0x0019FEF8 00000000 ....
0x0019FEFC 00000200 .... – [ebp-8]
0x0019FF00 00000040 @... – [ebp-4]
0x0019FF04 0019ff18 .ÿ.. - EBP
0x0019FF08 0040139e ž.@.
0x0019FF0C 00000001 ....
0x0019FF10 0051bd18 ..Q.
0x0019FF10 0051bd18 ..Q.
Our stack now has the values of “a”
and “b”.
The next instruction “mov ecx,dword
ptr [ebp-8]” takes our “x200” which is at “[ebp-8]” and place it into the ECX
register. From the previous printout of the registers we see “ECX = 00000001”. After executing the instruction
and looking at the registers again we see …
EAX = 00000200 EBX = 00251000 ECX = 00000200
EDX = 00404388 ESI = 004013B0 EDI = 004013B0
EIP = 00401019 ESP = 0019FEF8 EBP = 0019FF04
EFL = 00000206
Our next instruction is another new one “shr” or “shr ecx,4”. Whereas our “shl” shifted to the left, the “shr” shifts to the right and is used for division of multiple of 2.
Looking at it through the lens of an example, if we have the number “0000 0010” or 2 in decimal and decide to shift this 1 bit (21=2) to the right, we get the following “0000 0001” which (1). Basically 2/2 = 1.
Our next instruction is another new one “shr” or “shr ecx,4”. Whereas our “shl” shifted to the left, the “shr” shifts to the right and is used for division of multiple of 2.
Looking at it through the lens of an example, if we have the number “0000 0010” or 2 in decimal and decide to shift this 1 bit (21=2) to the right, we get the following “0000 0001” which (1). Basically 2/2 = 1.
Another example, using the number if
we have the number “0000 1000” or 8 in decimal and decide to shift this 2 bit
(22=4) to the right, we get the following “0000 0010” which (2).
Basically 8/4 = 2.
One final example, if we have the
number “0100 0000” or 64 in decimal and decide to shift this 3 bit (23=8)
to the right, we get the following “0000 1000” which (8). Basically 64/8 = 8.
Now that we have that out of the way
let’s look at our specific example “shr ecx,4”. The value in ECX is 0x200 which
is “10 0000 0000” (512 decimal) and (x200). Shifting the binary value “10 0000
0000” to the right by 4, means we are dividing 512*16. Remember we are shifting
y 4 bytes, this means we are shifting by 24 which is 16.
Therefore when we right shift by 4,
the value of variable “c” will be “32” (0x20) . Let’s execute our instruction
“shr ecx,4” after which we see the registers showing ...
EAX = 00000200 EBX = 00251000 ECX = 00000020 EDX = 00404388 ESI = 004013B0 EDI = 004013B0 EIP = 0040101C ESP = 0019FEF8 EBP = 0019FF04 EFL = 00000202
Our next instruction “mov dword ptr [ebp-0Ch],ecx” now takes our ECX value 0x20 and places it in the position held by the variable “c” thus completing the values of all our variables. Once this instruction is executed, our stack looks like …
EAX = 00000200 EBX = 00251000 ECX = 00000020 EDX = 00404388 ESI = 004013B0 EDI = 004013B0 EIP = 0040101C ESP = 0019FEF8 EBP = 0019FF04 EFL = 00000202
Our next instruction “mov dword ptr [ebp-0Ch],ecx” now takes our ECX value 0x20 and places it in the position held by the variable “c” thus completing the values of all our variables. Once this instruction is executed, our stack looks like …
0x0019FEF8 00000020 ... – [ebp-0c]
0x0019FEFC 00000200 .... – [ebp-8]
0x0019FF00 00000040 @... – [ebp-4]
0x0019FF04 0019ff18 .ÿ.. - EBP
0x0019FF08 0040139e ž.@.
0x0019FF0C 00000001 ....
0x0019FF10 0051bd18 ..Q.
0x0019FF10 0051bd18 ..Q.
Our final instruction says “mov
eax,dword ptr [ebp-0Ch]” says to take the value we have at “[ebp-0c]” and place
it in EAX register. Do remember, the EAX register holds our return value, thus
the value of “c” (0x20) is returned to the function.
After this the next set of code is
our epilogue. See
this post for more on prologue and epilogues.
References:
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