Here’s an example of an if-elseif-else conditional statement.
sub_checker:
push ebp
mov ebp, esp
test [ebp+0x8], [ebp+0x8]
jnz loc_block2
push off_you_win
call foo
jmp end
loc_block2:
cmp -1, [ebp+0x8]
jne loc_block3
push off_you_lose
call foo
jmp end
loc_block3:
push off_error_check_machine
call foo
end:
mov -1, [ebp+0x8]
mov esp, ebp
pop ebp
ret
// Pseudocode
void checker(int *arg) {
if (*arg == 0){
foo("You win!")
} else if (*arg == -1) {
foo("You lose...");
} else {
foo("Error. Check machine");
}
// End
*arg = -1;
}
The main thing to note about simple conditional statements (not multiple conditions like AND, OR, NOT, etc.) is that the comparison is inverted. We’ll take a look at complex conditionals later on in this article.
A great example of a loop is below
lea edx, [eax+1]
loc_12345:
mov cl, [eax]
add eax, 1
test cl, cl
jnz loc_12345
sub eax, edx
// Pseudocode
int strlen(const char *eax) {
const char* edx = eax+1;
char c = eax[0];
while (c != NULL) {
eax += 1;
c = *eax;
}
return edx - eax;
}
An example of a for loop
mov eax, 0
loc_12345:
cmp eax, 10
jnl end
call foo
add eax, 1
jmp loc_12345
end:
// Whatever...
// Pseudocode
for (i = 0; i < 10; i++) {
foo();
}
// whatever...
Another great live example
xor esi, esi # control variable
loc_123456: | <- Start of loop body
push off_494949[esi] |
lea eax, [ebp-2c0h] |
push eax | function call:
call sub_323232 | - push 2 arguments to stack
pop ecx | - call
pop ecx | - pop two local variables (cdecl style)
test eax, eax | <- test condition (is eax null)
jz loc_end
add esi, 4 | increment and compare the control variable
cmp esi, 38h |
jb short loc_123456 # end of loop body
end:
Usually, you should always rely on IDA Pro or your disassembler of choice since they have good heuristics to identify a loop body, but this is an ASM primer so we’ll do stuff manually.
Assembly is just like any programming language: when one looks at it long enough, your eye starts to cluster the letters in patterns that it can identify later. A good example of this is:
lea eax, [ebp-2c0h]
There’s a lot going on here. I got taken back when I first saw this operation. I had to slow down and break it down. Now, my eye can catch what’s happening and has clustered it into patterns:
2c0h
is IDA-speak for 0x2c0
[ebp-2c0h]
means that IDA is accessing a local variable, most probably in x86 since its using ebp
.
lea eax, [ebp-2c0h]
means move memory address of a local variable to eaxloc_xxxxx
in IDA)We’ll take the same example of simple conditional statements from above and modify it to include multiple cases
sub_checker:
push ebp
mov ebp, esp
cmp 1, [ebp+0x8]
jmp loc_block1
cmp 0, [ebp+0xc]
jge loc_block2
loc_block1:
push off_machine_broken
call foo
mov esp, ebp
pop ebp
ret
loc_block2:
cmp 0, [ebp+0x8]
jl loc_block3
cmp 10, [ebp+0x8]
jge loc_block3
push off_you_lose
call foo
loc_block3:
cmp 10, [ebp+0x8]
jl loc_block4
cmp 100, [ebp+0x8]
jge loc_block4
push off_you_win_big
call foo
loc_block4:
mov -1, [ebp+0x8]
mov esp, ebp
pop ebp
ret
// Pseudocode
void slot_machine(int isBroken, int *score) {
if (isBroken || *score < 0) {
foo("Machine is broken...")
return
}
if (*score >= 0 && *score < 10){
foo("You lose...")
} else if (*score >= 10 && *score < 100) {
foo("You win BIG!!!!");
}
// End
*score = -1;
}