5.3 KiB
Disassembler
Sucessful reverse engineers do not evaluate each instruction individually unless they must. The process is too tedious.
Global vs Local Variables
Global variables can be accessed and used by any function in the program.
Local variables can be accessed only by the function in which they are defined.
Both types of variables are declared similarly in c but completely differently in assembly.
Global variables are referenced by memory addresses
Local variables are referenced by stack addresses
Recognising if Statements
In IDA, branches will be represented as such:
int x = 1;
int y = 2;
if (x == y){
printf("x equals y\n");
} else {
printf("x does not equals y\n");
}
00401006 mov [ebp+var_8], 1
0040100D mov [ebp+var_4], 2
00401014 mov eax, [ebp+var_8]
00401017 cmp eax, [ebp+var_4]
0040101A jnz short loc_40102B
0040101C push offset aXEqualsY_ ; "x equals y.\n"
00401021 call printf
00401026 add esp, 4
00401029 jmp short loc_401038
0040102B loc_40102B:
0040102B push offset aXIsNotEqualToY ; "x is not equal to y.\n"
00401030 call printf
Here the instruction jnz on line 5 causes the branch. A cmp is done between ebp+var_8 (y) and ebp+var_4(x). If the values are not equal, then we jump to “x is not equal to y”
This is what that looks like in IDA
Recognising Loops
Finding for loops
For loops have 4 basic components:
- initialisation
- comparison
- execution instructions
- increment/decrement
int i;
for (i=0; i<100; i++)
{
printf("i equals %d\n", i);
}
mov [ebp+var_4], 0 ; INITIALISATION
jmp short loc_401016
loc_40100D:
mov eax, [ebp+var_4] ; INCREMENT START
add eax, 1
mov [ebp+var_4], eax ; INCREMENT STOP
loc_401016:
cmp [ebp+var_4], 64h ; COMPARISON
jge short loc_40102F ; COMPARISON
mov ecx, [ebp+var_4]
push ecx
push offset aID ; "i equals %d\n"
call printf
add esp, 8
jmp short loc_40100D ; UNCONDITIONAL JUMP
Note: the box on the bottom right is the function’s epilogue
Finding While Loops
While loops look similar to for loops in assembly, but are easier to understand.
int status = 0;
int result = 0;
while (status == 0)
{
result = performAction();
status = checkResult(result);
}
The assembly for this code will look similar to before; however, it lacks the increment section.
mov [ebp+var_4], 0
mov [ebp+var_8], 0
loc_401044:
cmp [ebp+var_4], 0
jnz short loc_401063 ; CONDITIONAL JUMP
call performAction
mov [ebp+var_8], eax
mov eax, [ebp+var_8]
push eax
call checkResult
add esp, 4
mov [ebp+var_4], eax
jmp short loc_401044 ; UNCONDITIONAL JUMP
A conditional jump occurs on line 5 and an unconditional jump at line 13, but the only way for this code to stop executing repeatedly is for that conditional jump to occur.
Understanding Function Call Conventions
Function call conventions govern:
- The order in which parameters are placed on the stack or in registers
- Whether the caller or callee is responsible for cleaning up the stack
Calling convention depends on the compiler used
int adder(int a, int b)
{
return a+b;
}
void main()
{
int x=1;
int y=2;
printf("adder(1,2): %d", adder(x,y));
}
Switch Statements
Switch statements are compiled in two different ways: if style or using jump tables
switch(i)
{
case 1:
printf("i = %d", i+1);
break;
case 2:
printf("i = %d", i+2);
break;
case 3:
printf("i = %d", i+3);
break;
default:
break;
}
If Style
Jump Table
This example is usually found with large contiguous switch statements. The compiler optimises the code to avoid needing to make so many comparisons
This assembly uses a jump table which defines offsets to additional memory locations. The switch variable (stored in ecx) is used as an index into the jump table.
edx is multiplied by 4 and added to the base of the jump table to determine which case code block to jump to.
It is multiplied by 4 because each entry in the jump table is an address that is 4 bytes in size.
Disassembling Arrays
int b[5] = {123, 87, 487, 7, 978};
void main()
{
int i;
int a[5];
for(i = 0; i<5; i++)
{
a[i] = i;
b[i] = i;
}
}
In assembly, arrays are accessed using a base address as a starting point. The size of each element is not always obvious, but can be determined by seeing how the array is being indexed.
00401006 mov [ebp+var_18], 0
0040100D jmp short loc_401018
0040100F loc_40100F:
0040100F mov eax, [ebp+var_18]
00401012 add eax, 1
00401015 mov [ebp+var_18], eax
00401018 loc_401018:
00401018 cmp [ebp+var_18], 5
0040101C jge short loc_401037
0040101E mov ecx, [ebp+var_18]
00401021 mov edx, [ebp+var_18]
00401024 mov [ebp+ecx*4+var_14], edx ; LOCAL
00401028 mov eax, [ebp+var_18]
0040102B mov ecx, [ebp+var_18]
0040102E mov dword_40A000[ecx*4], eax ; GLOBAL
00401035 jmp short loc_40100F
In both cases ecx is used as the index, which is multiplied by 4 to account for the size of the elements. This is added onto the base address of the array to access the proper array element.




