Что такое декомпилированная (C) конструкция кода этого ассемблерного кода x86? - PullRequest
2 голосов
/ 19 апреля 2019

Этот код сравнивает каждый символ строки (расположенный в ebp+arg_0) с различными константами (символами ASCII), такими как 'I', 'o' и 'S'.Я предполагаю, что, основываясь на других частях кода, этот код изначально написан на C.

assembly

Эта часть кода сравнения выглядит очень неэффективно. Мой вопрос , как вы думаете, этот код будет выглядеть на C?Какая конструкция кода использовалась изначально?мои мысли до сих пор

  • Это не для цикла .Потому что я не вижу никакого прыжка вверх и состояния остановки.

  • Это не какое-то время / случай / конструкция кода переключателя

  • Мой лучшийПредполагается, что это много последовательных операторов if / else.Вы можете помочь?

Да, это часть испытания, у меня уже есть флаг / решение, не беспокойтесь об этом.Просто пытаюсь лучше понять код.

Ответы [ 2 ]

4 голосов
/ 19 апреля 2019

Это не цикл.Потому что я не вижу никакого прыжка вверх и состояния остановки.

Правильно.

Это не какое-то время / дело / код переключателя construct

Этого не может быть, он сравнивает различные признаки массива.

Мое лучшее предположение, что это много последовательных if / elses.Можете ли вы помочь?

Похоже, что это может быть этот код:

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
}

Вот как GCC компилирует его без оптимизации :

.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

Очень похоже на ваш дизассемблированный код.

Эта часть кода сравнения выглядит очень неэффективно

Похоже, она была скомпилирована без оптимизации.При включенной оптимизации gcc скомпилировал код в :

.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

Не уверен, почему gcc решил перепрыгнуть вниз и обратно вверх вместо прямой линии jne с.Кроме того, ret больше нет, ваш printf оптимизирован для хвостовых вызовов, то есть jmp printf вместо call printf, за которым следует ret.

2 голосов
/ 19 апреля 2019

Первый аргумент (arg_0) - указатель на заданную строку пароля, например, const char *arg_0. Этот указатель (адрес первого символа) загружается в регистр eax (mov eax, [ebp+arg_0]), а затем добавляется индекс текущего символа, чтобы перейти к этому индексу (add eax, 4 и т. Д.). Один байт по этому адресу затем загружается в eax (movzx eax, byte ptr [eax]).

Затем этот байт / символ сравнивается с правильным (cmp eax, 'I' и т. Д.). Если результат не равен нулю (т. Е. Если они не равны), программа переходит на ветку «Неправильный пароль» (jnz - переход, если не ноль), в противном случае она переходит к следующему сравнению (и в конечном итоге к успеху) .

Следовательно, ближайший прямой C-эквивалент будет выглядеть примерно так:

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");
}

(Конечно, фактический код C вряд ли выглядел бы так, поскольку расположение goto fail не типично.)

...