New Concepts Covered
- Conditionals
In this article, we’ll understand how conditions look like in disassembly by looking at a simple program that tells us whether our user input is more than 10, less than 10, or equal to 10.
Source Code
#include <stdio.h>
int main()
{
int x = 0;
scanf("%d", &x);
if (x > 10)
puts("Input is more than 10");
else if (x < 10)
puts("Input is less than 10");
else
puts("Input is equal to 10");
return 0;
}
Disassembly
Dump of assembler code for function main:
0x0804845b <+0>: push ebp
0x0804845c <+1>: mov ebp,esp
0x0804845e <+3>: sub esp,0x4
0x08048461 <+6>: mov DWORD PTR [ebp-0x4],0x0
0x08048468 <+13>: lea eax,[ebp-0x4]
0x0804846b <+16>: push eax
0x0804846c <+17>: push 0x8048540
0x08048471 <+22>: call 0x8048340 <__isoc99_scanf@plt>
0x08048476 <+27>: add esp,0x8
0x08048479 <+30>: mov eax,DWORD PTR [ebp-0x4]
0x0804847c <+33>: cmp eax,0xa
0x0804847f <+36>: jle 0x8048490 <main+53>
0x08048481 <+38>: push 0x8048543
0x08048486 <+43>: call 0x8048320 <puts@plt>
0x0804848b <+48>: add esp,0x4
0x0804848e <+51>: jmp 0x80484b4 <main+89>
0x08048490 <+53>: mov eax,DWORD PTR [ebp-0x4]
0x08048493 <+56>: cmp eax,0x9
0x08048496 <+59>: jg 0x80484a7 <main+76>
0x08048498 <+61>: push 0x8048559
0x0804849d <+66>: call 0x8048320 <puts@plt>
0x080484a2 <+71>: add esp,0x4
0x080484a5 <+74>: jmp 0x80484b4 <main+89>
0x080484a7 <+76>: push 0x804856f
0x080484ac <+81>: call 0x8048320 <puts@plt>
0x080484b1 <+86>: add esp,0x4
0x080484b4 <+89>: mov eax,0x0
0x080484b9 <+94>: leave
0x080484ba <+95>: ret
End of assembler dump.
Let’s start off by reversing the disassembly above using the techniques we learnt in the last two articles.
0x0804845b <+0>: push ebp
0x0804845c <+1>: mov ebp,esp
0x0804845e <+3>: sub esp,0x4
0x08048461 <+6>: mov DWORD PTR [ebp-0x4],0x0
int main()
{
int i = 0;
}
This is the function prologue, followed by the initialization of a local variable to 0.
0x08048468 <+13>: lea eax,[ebp-0x4]
0x0804846b <+16>: push eax
0x0804846c <+17>: push 0x8048540
0x08048471 <+22>: call 0x8048340 <__isoc99_scanf@plt>
0x08048476 <+27>: add esp,0x8
int main()
{
int x = 0;
scanf("%d", &x);
}
We load the address of the value at ebp-0x4
into eax
and push it on the stack. We then push 0x8048540
(address of %d
) on the stack, and call scanf()
.
Conditionals
0x08048479 <+30>: mov eax,DWORD PTR [ebp-0x4]
0x0804847c <+33>: cmp eax,0xa
0x0804847f <+36>: jle 0x8048490 <main+53>
At +30
, we move the value at ebp-0x4
into eax
. Recall that this is the variable that was written to in the invocation of scanf
earlier. As such, ebp-0x4
contains the integer that was entered by the user. After which, eax
is compared (cmp
) to 0xa
which is 10
in decimal. This is followed by a jle
instruction, which is an initialism for jump less than/equal.
At this point, it is a good idea to refer to the intel manual to get a better understanding of the
cmp
/jle
instructions. Thecmp
instuction actually sets some status registers which jump-type instructions such asjle
will check before deciding on whether or not to perform the jump.
As such, jle 0x8048480
translates to: jump to address 0x8048480
if the comparison (based on status registers) is true, true being less than or equal to in this case. Knowing this, we can continue to reverse the code.
int main()
{
int x = 0;
scanf("%d", &x);
if (x <= 10) {
goto 0x8048490;
}
0x8048490:
}
Let’s take a look at the new few lines of disassembly,
0x08048481 <+38>: push 0x8048543
0x08048486 <+43>: call 0x8048320 <puts@plt>
0x0804848b <+48>: add esp,0x4
0x0804848e <+51>: jmp 0x80484b4 <main+89>
Seems like 0x8048543
(Input is greater than 10
) is passed into puts
, followed by an unconditional jump (jmp
) to 0x80484b4
.
int main()
{
int x = 0;
scanf("%d", &x);
if (x <= 10) {
goto 0x8048490;
} else {
puts("Input is greater than 10");
goto 0x80484b4;
}
0x8048490:
0x80484b4:
}
Let’s continue with the next few lines. Take note that we’re now looking at 0x08048490
which where the if-block that we reversed earlier jumps to.
0x08048490 <+53>: mov eax,DWORD PTR [ebp-0x4]
0x08048493 <+56>: cmp eax,0x9
0x08048496 <+59>: jg 0x80484a7 <main+76>
The value at ebp-0x4
is moved into eax
and if it is greater than (jg
) 9, we will jump to 0x80484a7
.
int main()
{
int x = 0;
scanf("%d", &x);
if (x <= 10) {
goto 0x8048490;
} else {
puts("Input is greater than 10");
goto 0x80484b4;
}
0x8048490:
if (x > 9) {
goto 0x80484a7;
}
0x80484a7:
0x80484b4:
}
The next few lines of disassembly continues in wthin the same block.
0x08048498 <+61>: push 0x8048559
0x0804849d <+66>: call 0x8048320 <puts@plt>
0x080484a2 <+71>: add esp,0x4
0x080484a5 <+74>: jmp 0x80484b4 <main+89>
The above translates to,
int main()
{
int x = 0;
scanf("%d", &x);
if (x <= 10) {
goto 0x8048490;
} else {
puts("Input is greater than 10");
goto 0x80484b4;
}
0x8048490:
if (x > 9) {
goto 0x80484a7;
}
puts("Input is less than to 10");
goto 0x80484b4;
0x80484a7:
0x80484b4:
}
We’ll now move on to the next few lines of disassembly, taking note that 0x080484a7
is within the if (x > 9)
block.
0x080484a7 <+76>: push 0x804856f
0x080484ac <+81>: call 0x8048320 <puts@plt>
0x080484b1 <+86>: add esp,0x4
This translates to,
int main()
{
int x = 0;
scanf("%d", &x);
if (x <= 10) {
goto 0x8048490;
} else {
puts("Input is greater than 10");
goto 0x80484b4;
}
0x8048490:
if (x > 9) {
goto 0x80484a7;
}
puts("Input is less than to 10");
goto 0x80484b4;
0x80484a7:
puts("Input is equal to 10");
0x80484b4:
}
The next block of disassembly starts at 0x080484b4
, which also happens to be our function epilogue.
0x080484b4 <+89>: mov eax,0x0
0x080484b9 <+94>: leave
0x080484ba <+95>: ret
This translates to,
int main()
{
int x = 0;
scanf("%d", &x);
if (x <= 10) {
goto 0x8048490;
} else {
puts("Input is greater than 10");
goto 0x80484b4;
}
0x8048490:
if (x > 9) {
goto 0x80484a7;
}
puts("Input is less than to 10");
goto 0x80484b4;
0x80484a7:
puts("Input is equal to 10");
0x80484b4:
return 0;
}
If we understand the flow of the code, we can actually remove the goto
commands to clean things up.
int main()
{
int x = 0;
scanf("%d", &x);
if (x < 10) {
puts("Input is less than to 10");
} else if (x == 10) {
puts("Input is equal to 10");
} else {
puts("Input is greater than 10");
}
return 0;
}
Note that code that is reversed is not always structrually the same as the original source code. However, it should be equivalent in terms of what it achieves.