Что происходит в этом ассемблерном коде?

Предыстория: я выполняю задание «бинарная бомба», в котором я должен пройти 6 различных фаз, чтобы «обезвредить» бомбу. Я могу использовать отладчик, чтобы отслеживать значения и проходить каждую строку сборки. Я застрял на втором этапе.

Цель: найти «пароль» для этого этапа, который удовлетворяет следующей программе. Правильный пароль переведет меня на следующий этап

Думаю, что знаю: похоже, сразу пытается прочитать 6 цифр. Когда я разобрал функцию main () всей бомбы, я заметил строку a с "% d% d% d% d% d% d", поэтому на этой фазе должно быть 6 чисел. Кроме того, похоже, что есть цикл, начинающийся с 1 и заканчивающийся после того, как счетчик больше 5. Кажется, он отслеживается в части памяти -0x4 (% rbp). Похоже на строку, она сравнивает то, что отслеживается в% eax, с тем, что вводит пользователь (ранее сохраненное в% edx в строке, чтобы увидеть, равны ли они. Если они не равны, то бомба взорвется. Если они есть, это будет продолжаться через цикл.

0x400ec9 <phase_2>:     push   %rbp
0x400eca <phase_2+1>:   mov    %rsp,%rbp
0x400ecd <phase_2+4>:   sub    $0x30,%rsp
0x400ed1 <phase_2+8>:   mov    %rdi,-0x28(%rbp)
0x400ed5 <phase_2+12>:  lea    -0x20(%rbp),%rsi
0x400ed9 <phase_2+16>:  mov    -0x28(%rbp),%rdi
0x400edd <phase_2+20>:  callq  0x4013e9 <read_six_numbers>
0x400ee2 <phase_2+25>:  mov    -0x20(%rbp),%eax
0x400ee5 <phase_2+28>:  test   %eax,%eax
0x400ee7 <phase_2+30>:  jns    0x400eee <phase_2+37>
0x400ee9 <phase_2+32>:  callq  0x401983 <explode_bomb>
0x400eee <phase_2+37>:  movl   $0x1,-0x4(%rbp)
0x400ef5 <phase_2+44>:  jmp    0x400f1f <phase_2+86>
0x400ef7 <phase_2+46>:  mov    -0x4(%rbp),%eax
0x400efa <phase_2+49>:  cltq
0x400efc <phase_2+51>:  mov    -0x20(%rbp,%rax,4),%edx
0x400f00 <phase_2+55>:  mov    -0x4(%rbp),%eax
0x400f03 <phase_2+58>:  sub    $0x1,%eax
0x400f06 <phase_2+61>:  cltq
0x400f08 <phase_2+63>:  mov    -0x20(%rbp,%rax,4),%eax
0x400f0c <phase_2+67>:  add    -0x4(%rbp),%eax
0x400f0f <phase_2+70>:  add    $0x1,%eax
0x400f12 <phase_2+73>:  cmp    %eax,%edx
0x400f14 <phase_2+75>:  je     0x400f1b <phase_2+82>
0x400f16 <phase_2+77>:  callq  0x401983 <explode_bomb>
0x400f1b <phase_2+82>:  addl   $0x1,-0x4(%rbp)
0x400f1f <phase_2+86>:  cmpl   $0x5,-0x4(%rbp)
0x400f23 <phase_2+90>:  jle    0x400ef7 <phase_2+46>
0x400f25 <phase_2+92>:  leaveq
0x400f26 <phase_2+93>:  retq

Чего я не знаю: здесь есть некоторые новые инструкции, с которыми я не знаком. 'cltq', по-видимому, выполняет «знак-расширение% eax до% rax», но я не совсем уверен, что это означает в данном случае. Кроме того, я не понимаю строку "mov -0x20 (% rbp,% rax, 4),% edx" и что именно она делает. Я понимаю, что он перемещает то, что хранится в 0x20, в регистр% rdx, но я не знаю, что должно быть в этом месте в памяти в то время. Я знаю, что при отладке% rbp at -0x20 содержит одно из 6 введенных мной значений. Но я не совсем уверен, что именно делают% rax и 4.

Если кто-то может увидеть, какие 6 цифр требуются, ОТЛИЧНО! Если нет, я надеюсь, что кто-то сможет хотя бы пролить свет на этот код и проблемы, которые у меня возникают, чтобы помочь мне двигаться в правильном направлении! Спасибо

Некоторые неверные решения, которые я тестировал: 1 2 3 4 5 6; 10 20 30 40 50 60

(Я не хотел продолжать попытки, потому что каждый раз, когда я взрываю бомбу, я снимаю оценку за задание)


person Jay    schedule 16.04.2015    source источник
comment
См. эту ссылку для объяснения синтаксиса -0x20(%rbp, %rax, 4). В основном это эквивалентно доступу к массиву, где каждый член имеет 4 байта (отсюда 4), а %rax - это индекс массива, а %rbp - адрес. Я не уверен, что делает -0x20, но предполагаю, что это какое-то смещение, которое просто добавляется к окончательному адресу ...   -  person nonsensickle    schedule 16.04.2015
comment
@nonsensickle Я просто вошел в эту строку с отладчиком и отобразил информацию о регистрах. % rax говорит, что он содержит 0x1 или только 1. Так что это, вероятно, глупый вопрос, но означает ли это, что это значение 1 * 4, которое будет = 4, или оно все еще равно 1, но хранится в этом массиве?   -  person Jay    schedule 16.04.2015
comment
Да вы правы. Но помните, что в C это делается автоматически. Целые числа, как правило, хранятся как 32-битные числа (то есть 4 байта), поэтому, если у вас есть их массив и у вас есть адрес первого целого числа, вам нужно добавить 4, чтобы получить следующее, и добавить 4, чтобы получить одно после что...   -  person nonsensickle    schedule 16.04.2015
comment
Это обозначение позволяет вам сказать, насколько велик каждый элемент в вашем массиве. Итак, вы говорите, что это мой начальный адрес, мне нужен 3-й элемент, и каждый элемент имеет длину 4 байта. Итак, вы идете вычислить, какой адрес вам нужен, чтобы получить его для меня (это будет адрес + 3 * 4 = адрес + 12) ...   -  person nonsensickle    schedule 16.04.2015


Ответы (1)


Дай мне посмотреть, смогу ли я взломать этот код ...

Я попытался превратить это в код C, так что вот:

void read_six_numbers(int *array);
void explode();

int phase_2()
{
    int array[6];

    read_six_numbers(array);

    if (array[0] < 0)
    {
        explode();
    }

    for (int i = 1; i <= 5; ++i)
    {
        int cur  = array[i];   // %edx
        int prev = array[i-1]; // %eax

        prev += i;
        prev++;

        if (prev != cur)
        {
            explode();
        }
    }
}

Итак, для прохождения теста вам понадобится следующая последовательность чисел:

n, n + 2, (n + 2) + 3, ((n + 2) + 3) + 4, (((n + 2) + 3) + 4) + 5, ((((n + 2) + 3) + 4) + 5) + 6

or

n, n + 2, n + 5, n + 9, n + 14, n + 20

Где n - положительное число или ноль.

Вот мои аннотации вашего ассемблерного кода:

0x400ec9 <phase_2>:     push   %rbp
0x400eca <phase_2+1>:   mov    %rsp,%rbp
0x400ecd <phase_2+4>:   sub    $0x30,%rsp          // Reserve some space on the stack...
0x400ed1 <phase_2+8>:   mov    %rdi,-0x28(%rbp)
0x400ed5 <phase_2+12>:  lea    -0x20(%rbp),%rsi   // Compute the address of -0x20(%rbp) and store it in %rsi (I'm assuming this is an argument for read_six_numbers below)
0x400ed9 <phase_2+16>:  mov    -0x28(%rbp),%rdi
0x400edd <phase_2+20>:  callq  0x4013e9 <read_six_numbers>
0x400ee2 <phase_2+25>:  mov    -0x20(%rbp),%eax       // From the code this looks like an array. Load its first element.
0x400ee5 <phase_2+28>:  test   %eax,%eax              // bitwise and that sets the flags.
0x400ee7 <phase_2+30>:  jns    0x400eee <phase_2+37>  // Jump not signed (jump if positive), so we can deduce Rule 1: First number is positive!
0x400ee9 <phase_2+32>:  callq  0x401983 <explode_bomb> // Explode!
0x400eee <phase_2+37>:  movl   $0x1,-0x4(%rbp)         // -0x4(%rbp) looks like a counter that starts at one so I'll call it `int i = 0;`
0x400ef5 <phase_2+44>:  jmp    0x400f1f <phase_2+86>   // Goto loop condition!
0x400ef7 <phase_2+46>:  mov    -0x4(%rbp),%eax         // Loop body:
0x400efa <phase_2+49>:  cltq   // Extend sign for %eax (converts %eax [int32] to %rax [int64])
0x400efc <phase_2+51>:  mov    -0x20(%rbp,%rax,4),%edx // Our read values are in an array located at -0x20(%rbp) I'll call it array from now on. So this line ends up being %edx = array[i]
0x400f00 <phase_2+55>:  mov    -0x4(%rbp),%eax         // reset i back to it's last value...
0x400f03 <phase_2+58>:  sub    $0x1,%eax               // Subtract 1 from i (are we checking our values backwards?)
0x400f06 <phase_2+61>:  cltq
0x400f08 <phase_2+63>:  mov    -0x20(%rbp,%rax,4),%eax // Load array[i] into %eax
0x400f0c <phase_2+67>:  add    -0x4(%rbp),%eax         // Add i to this value? Nice trick...
0x400f0f <phase_2+70>:  add    $0x1,%eax
0x400f12 <phase_2+73>:  cmp    %eax,%edx
0x400f14 <phase_2+75>:  je     0x400f1b <phase_2+82>
0x400f16 <phase_2+77>:  callq  0x401983 <explode_bomb>
0x400f1b <phase_2+82>:  addl   $0x1,-0x4(%rbp)
0x400f1f <phase_2+86>:  cmpl   $0x5,-0x4(%rbp)         // Loop condition: Check i against 5.
0x400f23 <phase_2+90>:  jle    0x400ef7 <phase_2+46>   // If i <= 5 goto Loop body
0x400f25 <phase_2+92>:  leaveq
0x400f26 <phase_2+93>:  retq
person nonsensickle    schedule 16.04.2015
comment
Должно быть i <= 5 из-за jle, поэтому последний номер также проверяется. Комментарий в вашей сборке правильный, но код C и решение неверны. - person Jester; 16.04.2015
comment
Итак, я мог бы попробовать такое решение, как: 1 3 8 17 31 50? Я знаю, что @Jester выше говорит, что это неправильный код C, поэтому я боюсь пробовать это из-за возможных штрафов. - person Jay; 16.04.2015
comment
Это задача реверс-инжиниринга, первым шагом должно было быть отключение бомбы как таковой ... не такая уж сложная вещь, это. Просто установите точку останова на explode_bomb и не позволяйте этому продолжаться. Затем вы можете попробовать столько входов, сколько захотите. Конечно, вы также можете отключить сеть. Вы также можете просто собрать эту единственную функцию и закомментировать explode_bomb вызовы или использовать заполнитель. - person Jester; 16.04.2015
comment
Итак, если я установил точку останова на explode_bomb, если она сломается после попытки ввода, это означает, что ввод не удалось? А потом просто не продолжать, а выйти (q) и повторить попытку? - person Jay; 16.04.2015
comment
@Jester спасибо за поправку, ты прав! Я его обновил. - person nonsensickle; 17.04.2015
comment
@nonsensickle Вы правильно ответили! Я набрал «0 2 5 9 14 20» и смог пройти этот этап. Сначала я просмотрел ваше решение, в котором значение n сохраняется после каждого цикла, поэтому я попробовал ответить, например, «0 2 7 16 30 50», который не сработал. Большое спасибо за то, что помогли мне понять, как перевести Assembly обратно в код C. Должен помочь мне в третьей фазе. - person Jay; 17.04.2015