Последовательность
08048a38 mov eax, dword [ebp-0x24]
08048a3b mov dword [eax], 0x2391
08048a41 mov eax, dword [ebp-0x24]
08048a44 mov dword [eax+4], 0x239d
(и т. Д.) Записывает 0x2391, 0x239d, ... в последовательных расположениях в массиве 32-битных целых чисел (см. Смежные смещения с интервалом в 4 байта), указатель которого был передан в качестве параметра (значение указателя загружено - необъяснимо каждый раз - от ebp минус что-то). В конечном итоге его содержание будет:
0x2391
0x239d
0x239d
0x2399
0x239c
0x2363
0x2358
0x2358
0x2390
0x2398
0x2398
0x2357
0x2390
0x2395
0x2358
0x2377
0x235e
0x2380
0x237a
0x2381
0x23a3
Теперь, это не ASCII, но повторение второго и третьего элемента (плюс другие повторения позже) и тот факт, что все они в значительной степени находятся в одном диапазоне, заставили меня думать, что все они должны быть закодированы в тот же самый простой способ, предположительно суммирование или xor с некоторым фиксированным значением (всегда идентичный верхний байт 0x23 был пустой раздачей); поэтому, поскольку остальная часть кода (где, по-видимому, этот массив декодируется) отсутствует в вашем вопросе, я просто угадал и попытался вычесть им одинаковое значение, чтобы они вернулись в диапазон ASCII.
Мое первое предположение состояло в том, что повторное число выше (0x239d) должно было быть l
(которые часто встречаются парами на английском языке). Следовательно, мне нужно было такое число, чтобы 0x239d стал l
(ASCII 108); 0x239d - 108 = 9009, и это то, что я вычитал каждому персонажу. Результатом был весь ASCII (обнадеживающий - если бы схема была чем-то более сложным, я бы получил нечитаемый случайный мусор, кроме двух l
), но бессмысленный.
Я выбрал метод «грубой силы» (наиболее разумные случаи - всего 62 - прописные, строчные и цифры - и исчерпывающий поиск с использованием этого метода - не более 256 случаев, все еще управляемый при визуальном осмотре) и пробовал некоторые аналогичные числа - все, что приводило к ASCII-символам, но, надеюсь, более разумно.
И действительно, однажды я достиг 9001:
In [18]: [chr(int(x,16)-9001) for x in s.split()]
Out[18]:
['h',
't',
't',
'p',
's',
':',
'/',
'/',
'g',
'o',
'o',
'.',
'g',
'l',
'/',
'N',
'5',
'W',
'Q',
'X',
'z']
(s
здесь строка, содержащая содержимое второго кодового блока этого поста, со всеми значениями массива)
Получайте удовольствие от следующего уровня вашей игры. : -)
Кстати, эта сборка действительно ужасна. Некоторые из них, такие как продолжение перезагрузки того же значения из стека в регистры
mov eax, dword [ebp-0x24]
заставит меня задуматься о неоптимизированной, дружественной к отладчику сборке; OTOH, есть вещи, которые на самом деле не приходят из компилятора:
mov eax, 0x10
sub eax, 0x1
Здесь даже компиляция на скромном уровне распространения константы оптимизации привела бы к mov eax, 0xf
, или, при -O0
, это было бы выполнено в стеке, а не в регистрах, чтобы помочь одноступенчатому переходу на уровне источника.
mov ebx, 0x10
mov edx, 0x0
div ebx
imul eax, eax, 0x10
Это мозговая смерть на разных уровнях; вы никогда не увидите компилятор, выдающий mov edx, 0x0
в целом - даже при -O0
обнуление регистра почти всегда xor edx, edx
. Кроме того, существует некоторая путаница с типами данных: сначала без знака div
, затем со знаком imul
(он сопоставляется с чем-то вроде ((int)((unsigned)(foo)/16))*16
, что я считаю маловероятным).
Но самое главное, что gcc никогда не выдаст div
или mul
для деления / умножения на 16; преобразует их в сдвиг даже при -O0
; clang - это то же самое (хотя при -O0
он все равно будет выдавать idiv
для деления со знаком вместо сдвига + знаковый бит, используемый на более высоких уровнях оптимизации).
Наконец, если входное значение не имеет знака (согласно div
), все это сводится к маскировке младших 4 битов, так что весь этот беспорядок может быть просто
and eax, 0xfffffff0
.
Итак, мне кажется, что это рукописный код человека, не очень опытного в сборке; Подобные вещи даже не кажутся «преднамеренными трудностями», усложняющими разборку - код довольно прост, он просто очень наивен.