bsdanm
bsdanm

Reputation: 53

What is the decompiled (C) code construct of this assembly x86 code?

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.

assembly

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

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

Answers (2)

Arkku
Arkku

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

tkausl
tkausl

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 jnes. 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

Related Questions