Часть 1 из 3
Если вы серьезно относитесь к реверс-инжинирингу - забудьте о трейнерах и чит-движках.
Хороший реверс-инженер должен сначала узнать ОС, основные функции API, общую структуру программы (что такое цикл выполнения, структуры окон, процедуры обработки событий), формат файла (PE). Классика Петцольда «Программирование Windows» может помочь (www.amazon.com/exec/obidos/ISBN=157231995X), а также онлайновый MSDN.
Сначала вы должны подумать о том, где можно вызвать процедуру инициализации минного поля. Я подумал о следующем:
- Когда вы запускаете игру
- Когда вы нажимаете счастливое лицо
- Когда вы нажимаете Game-> New или нажимаете F2
- Когда вы меняете уровень сложности
Решил проверить команду акселератора F2.
Чтобы найти код обработки ускорителя, вам нужно найти процедуру обработки оконных сообщений (WndProc). Это можно отследить по вызовам CreateWindowEx и RegisterClass.
Читать:
Откройте IDA, окно импорта, найдите «CreateWindow *», перейдите к нему и используйте команду «Перейти по внешней ссылке к операнду (X)», чтобы увидеть, где он вызывается. Звонок должен быть только один.
Теперь найдите функцию RegisterClass и ее параметр WndClass.lpfnWndProc выше. В моем случае я уже назвал функцию mainWndProc.
.text:0100225D mov [ebp+WndClass.lpfnWndProc], offset mainWndProc
.text:01002264 mov [ebp+WndClass.cbClsExtra], edi
.text:01002267 mov [ebp+WndClass.cbWndExtra], edi
.text:0100226A mov [ebp+WndClass.hInstance], ecx
.text:0100226D mov [ebp+WndClass.hIcon], eax
.text:01002292 call ds:RegisterClassW
Нажмите Enter на имени функции (используйте 'N', чтобы переименовать его во что-нибудь получше)
Теперь посмотрим на
.text:01001BCF mov edx, [ebp+Msg]
Это идентификатор сообщения, который при нажатии кнопки F2 должен содержать значение WM_COMMAND. Вы должны найти, где он сравнивается с 111h. Это можно сделать либо путем отслеживания edx в IDA, либо путем установки условной точки останова. в WinDbg и нажатия F2 в игре.
В любом случае приводит к чему-то вроде
.text:01001D5B sub eax, 111h
.text:01001D60 jz short loc_1001DBC
Щелкните правой кнопкой мыши 111h и выберите «Символьная константа» -> «Использовать стандартную символьную константу», введите WM_ и Enter. Теперь у вас должно быть
.text:01001D5B sub eax, WM_COMMAND
.text:01001D60 jz short loc_1001DBC
Это простой способ узнать значения идентификаторов сообщений.
Чтобы понять работу с ускорителем, ознакомьтесь с:
Достаточно много текста для одного ответа. Если интересно, могу написать еще пару постов. Короче говоря, минное поле хранится в виде массива байтов [24x36], 0x0F показывает, что байт не используется (играет меньшее поле), 0x10 - пустое поле, 0x80 - мое.
Часть 2 из 3
Хорошо, давайте продолжим с кнопкой F2.
В соответствии с использованием ускорителей клавиатуры при нажатии кнопки F2 функция wndProc
... получает сообщение WM_COMMAND или WM_SYSCOMMAND. Младшее слово параметра wParam содержит идентификатор ускорителя.
Хорошо, мы уже нашли, где обрабатывается WM_COMMAND, но как определить соответствующее значение параметра wParam? Вот где в игру вступает ресурсный хакер . Накормите его двоичным кодом, и он вам все покажет. Нравится таблица ускорителей для меня.
альтернативный текст http://files.getdropbox.com/u/1478671/2009-07-29_161532.jpg
Вы можете видеть здесь, что кнопка F2 соответствует 510 в wParam.
Теперь вернемся к коду, который обрабатывает WM_COMMAND. Он сравнивает wParam с разными константами.
.text:01001DBC HandleWM_COMMAND: ; CODE XREF: mainWndProc+197j
.text:01001DBC movzx eax, word ptr [ebp+wParam]
.text:01001DC0 mov ecx, 210h
.text:01001DC5 cmp eax, ecx
.text:01001DC7 jg loc_1001EDC
.text:01001DC7
.text:01001DCD jz loc_1001ED2
.text:01001DCD
.text:01001DD3 cmp eax, 1FEh
.text:01001DD8 jz loc_1001EC8
Используйте контекстное меню или сочетание клавиш 'H' для отображения десятичных значений, и вы можете увидеть наш прыжок
.text:01001DBC HandleWM_COMMAND: ; CODE XREF: mainWndProc+197j
.text:01001DBC movzx eax, word ptr [ebp+wParam]
.text:01001DC0 mov ecx, 528
.text:01001DC5 cmp eax, ecx
.text:01001DC7 jg loc_1001EDC
.text:01001DC7
.text:01001DCD jz loc_1001ED2
.text:01001DCD
.text:01001DD3 cmp eax, 510
.text:01001DD8 jz loc_1001EC8 ; here is our jump
Это приводит к фрагменту кода, который вызывает некоторую процедуру и завершает работу wndProc.
.text:01001EC8 loc_1001EC8: ; CODE XREF: mainWndProc+20Fj
.text:01001EC8 call sub_100367A ; startNewGame ?
.text:01001EC8
.text:01001ECD jmp callDefAndExit ; default
Это функция, которая запускает новую игру? Узнай это в последней части! Следите за обновлениями.
Часть 3 из 3
Давайте посмотрим на первую часть этой функции.
.text:0100367A sub_100367A proc near ; CODE XREF: sub_100140C+CAp
.text:0100367A ; sub_1001B49+33j ...
.text:0100367A mov eax, dword_10056AC
.text:0100367F mov ecx, uValue
.text:01003685 push ebx
.text:01003686 push esi
.text:01003687 push edi
.text:01003688 xor edi, edi
.text:0100368A cmp eax, dword_1005334
.text:01003690 mov dword_1005164, edi
.text:01003696 jnz short loc_10036A4
.text:01003696
.text:01003698 cmp ecx, dword_1005338
.text:0100369E jnz short loc_10036A4
Два значения (dword_10056AC, uValue) считываются в регистры eax и ecx и сравниваются с двумя другими значениями (dword_1005164, dword_1005338).
Взгляните на фактические значения с помощью WinDBG ('bp 01003696'; на break 'p eax; p ecx') - мне они показались размерами минного поля. Игра с нестандартным размером минного поля показала, что первая пара - это новые измерения, а вторая - текущие. Зададим новые имена.
.text:0100367A startNewGame proc near ; CODE XREF: handleButtonPress+CAp
.text:0100367A ; sub_1001B49+33j ...
.text:0100367A mov eax, newMineFieldWidth
.text:0100367F mov ecx, newMineFieldHeight
.text:01003685 push ebx
.text:01003686 push esi
.text:01003687 push edi
.text:01003688 xor edi, edi
.text:0100368A cmp eax, currentMineFieldWidth
.text:01003690 mov dword_1005164, edi
.text:01003696 jnz short loc_10036A4
.text:01003696
.text:01003698 cmp ecx, currentMineFieldHeight
.text:0100369E jnz short loc_10036A4
Чуть позже новые значения перезаписывают текущие и вызывается подпрограмма.
.text:010036A7 mov currentMineFieldWidth, eax
.text:010036AC mov currentMineFieldHeight, ecx
.text:010036B2 call sub_1002ED5
И когда я это увидел
.text:01002ED5 sub_1002ED5 proc near ; CODE XREF: sub_1002B14:loc_1002B1Ep
.text:01002ED5 ; sub_100367A+38p
.text:01002ED5 mov eax, 360h
.text:01002ED5
.text:01002EDA
.text:01002EDA loc_1002EDA: ; CODE XREF: sub_1002ED5+Dj
.text:01002EDA dec eax
.text:01002EDB mov byte ptr dword_1005340[eax], 0Fh
.text:01002EE2 jnz short loc_1002EDA
Я был полностью уверен, что нашел массив минных полей. Причина цикла, который начинается с массива длиной 360 байт (dword_1005340) с 0xF.
Почему 360h = 864? Ниже приведены некоторые подсказки, что строка занимает 32 байта, а 864 можно разделить на 32, поэтому массив может содержать 27 * 32 ячеек (хотя пользовательский интерфейс допускает максимальное поле 24 * 30, есть одно байтовое заполнение вокруг массива для границ).
Следующий код генерирует верхнюю и нижнюю границы минного поля (0x10 байт). Надеюсь, вы видите повторение цикла в этом беспорядке;) Мне пришлось использовать бумагу и ручку
.text:01002EE4 mov ecx, currentMineFieldWidth
.text:01002EEA mov edx, currentMineFieldHeight
.text:01002EF0 lea eax, [ecx+2]
.text:01002EF3 test eax, eax
.text:01002EF5 push esi
.text:01002EF6 jz short loc_1002F11 ;
.text:01002EF6
.text:01002EF8 mov esi, edx
.text:01002EFA shl esi, 5
.text:01002EFD lea esi, dword_1005360[esi]
.text:01002EFD
.text:01002F03 draws top and bottom borders
.text:01002F03
.text:01002F03 loc_1002F03: ; CODE XREF: sub_1002ED5+3Aj
.text:01002F03 dec eax
.text:01002F04 mov byte ptr MineField?[eax], 10h ; top border
.text:01002F0B mov byte ptr [esi+eax], 10h ; bottom border
.text:01002F0F jnz short loc_1002F03
.text:01002F0F
.text:01002F11
.text:01002F11 loc_1002F11: ; CODE XREF: sub_1002ED5+21j
.text:01002F11 lea esi, [edx+2]
.text:01002F14 test esi, esi
.text:01002F16 jz short loc_1002F39
А остальная часть подпрограммы рисует левую и правую границы.
.text:01002F18 mov eax, esi
.text:01002F1A shl eax, 5
.text:01002F1D lea edx, MineField?[eax]
.text:01002F23 lea eax, (MineField?+1)[eax+ecx]
.text:01002F23
.text:01002F2A
.text:01002F2A loc_1002F2A: ; CODE XREF: sub_1002ED5+62j
.text:01002F2A sub edx, 20h
.text:01002F2D sub eax, 20h
.text:01002F30 dec esi
.text:01002F31 mov byte ptr [edx], 10h
.text:01002F34 mov byte ptr [eax], 10h
.text:01002F37 jnz short loc_1002F2A
.text:01002F37
.text:01002F39
.text:01002F39 loc_1002F39: ; CODE XREF: sub_1002ED5+41j
.text:01002F39 pop esi
.text:01002F3A retn
Разумное использование команд WinDBG может предоставить вам классный дамп минного поля (нестандартный размер 9x9). Проверьте границы!
0:000> db /c 20 01005340 L360
01005340 10 10 10 10 10 10 10 10-10 10 10 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f ................................
01005360 10 0f 0f 0f 0f 0f 0f 0f-0f 0f 10 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f ................................
01005380 10 0f 0f 0f 0f 0f 0f 0f-0f 0f 10 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f ................................
010053a0 10 0f 0f 0f 0f 0f 0f 0f-0f 0f 10 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f ................................
010053c0 10 0f 0f 0f 0f 0f 0f 0f-0f 0f 10 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f ................................
010053e0 10 0f 0f 0f 0f 0f 0f 0f-0f 0f 10 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f ................................
01005400 10 0f 0f 0f 0f 0f 0f 0f-0f 0f 10 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f ................................
01005420 10 0f 0f 0f 0f 0f 0f 0f-0f 0f 10 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f ................................
01005440 10 0f 0f 0f 0f 0f 0f 0f-0f 0f 10 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f ................................
01005460 10 0f 0f 0f 0f 0f 0f 0f-0f 0f 10 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f ................................
01005480 10 10 10 10 10 10 10 10-10 10 10 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f ................................
010054a0 0f 0f 0f 0f 0f 0f 0f 0f-0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f ................................
010054c0 0f 0f 0f 0f 0f 0f 0f 0f-0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f ................................
010054e0 0f 0f 0f 0f 0f 0f 0f 0f-0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f ................................
Хм, похоже мне понадобится еще один пост, чтобы закрыть тему