Reputation: 53
This code compares every char of a string (located at ebp+arg_0
) with different constants (ASCII chars) like 'I', 'o' and 'S'. I guess, based on other code parts, this code is originally written in C.
This compare-code-part looks very inefficient to. My question, how do you think this code will look in C? What code construct was used originally? my thoughts so far
It's not a for loop. Because i don't see any upward jump and stop-condition.
It's not a while/case/switch code construct
My best guess is that this are a lot of consecutive if/else statements. Can you help?
Yes it's part of a challenge, i already have the flag/solution, no worries about that. Just trying to better understand the code.
Upvotes: 2
Views: 625
Reputation: 42109
The first argument (arg_0
) is a pointer to the given password string, e.g., const char *arg_0
. This pointer (the address of the first character) is loaded into the eax
register (mov eax, [ebp+arg_0]
), and then the index of the current character is added to advance it to that index (add eax, 4
etc.). The single byte at that address is then loaded into eax
(movzx eax, byte ptr [eax]
).
Then that byte/character is compared against the correct one (cmp eax, 'I'
, etc). If the result is not zero (i.e., if they are not equal), the program jumps to the "Wrong password" branch (jnz
- jump if not zero), otherwise it continues on to the next comparison (and ultimately success).
The nearest direct C equivalent would therefore be something like:
void check(const char *arg_0) {
// presumably comparisons 0-3 are omitted
if (arg_0[4] != 'I') goto fail;
if (arg_0[5] != 'o') goto fail;
if (arg_0[6] != 'S') goto fail;
printf("Gratz man :)");
exit(0);
fail:
puts("Wrong password");
}
(Of course the actual C code is unlikely to have looked like this, since the goto fail
arrangement is not typical.)
Upvotes: 3
Reputation: 14269
It's not a for loop. Because i don't see any upward jump and stop-condition.
Correct.
It's not a while/case/switch code constuct
It can't be, it compares different indicies of the array.
My best guess is that this are a lot of consecutive if/elses. Can you help?
Looks like it could be this code:
void f(const char* arg_0) {
if(arg_0[4] == 'I' && arg_0[5] == 'o' && arg_0[6] == 'S') {
printf("Gratz man :)");
exit(0); //noreturn, hence your control flow ends here in the assembly
}
puts("Wrong password"); // Or `printf("Wrong password\n");` which gets optimized to `puts`
// leave, retn
}
This is how gcc compiles it without optimizations:
.LC0:
.string "Gratz man :)"
.LC1:
.string "Wrong password"
f(char const*):
push ebp
mov ebp, esp
sub esp, 8
mov eax, DWORD PTR [ebp+8]
add eax, 4
movzx eax, BYTE PTR [eax]
cmp al, 73
jne .L2
mov eax, DWORD PTR [ebp+8]
add eax, 5
movzx eax, BYTE PTR [eax]
cmp al, 111
jne .L2
mov eax, DWORD PTR [ebp+8]
add eax, 6
movzx eax, BYTE PTR [eax]
cmp al, 83
jne .L2
sub esp, 12
push OFFSET FLAT:.LC0
call printf
add esp, 16
sub esp, 12
push 0
call exit
.L2:
sub esp, 12
push OFFSET FLAT:.LC1
call puts
add esp, 16
nop
leave
ret
Looks very similar to your disassembled code.
This compare-code-part looks very inefficient
Looks like it was compiled without optimizations. With optimizations enabled, gcc compiled the code to:
.LC0:
.string "Gratz man :)"
.LC1:
.string "Wrong password"
f(char const*):
sub esp, 12
mov eax, DWORD PTR [esp+16]
cmp BYTE PTR [eax+4], 73
jne .L2
cmp BYTE PTR [eax+5], 111
je .L5
.L2:
mov DWORD PTR [esp+16], OFFSET FLAT:.LC1
add esp, 12
jmp puts
.L5:
cmp BYTE PTR [eax+6], 83
jne .L2
sub esp, 12
push OFFSET FLAT:.LC0
call printf
mov DWORD PTR [esp], 0
call exit
Not sure why gcc decided to jump down and back up again instead of a straight line of jne
s. Also, the ret
is gone, your printf
got tail-call-optimized, i.e. a jmp printf
istead of a call printf
followed by a ret
.
Upvotes: 5