Как я могу найти структуру данных, которая представляет мое расположение Minesweeper в памяти?
Я пытаюсь изучить реверс-инжиниринг, используя Minesweeper в качестве примера приложения. Я нашел эту статью MSDN о простой команде WinDbg, которая раскрывает все мины, но она старая, не объясняется в деталях и на самом деле не то, что я ищу.
У меня есть дизассемблер IDA Pro и отладчик WinDbg, и я загрузил winmine.exe в оба из них. Может ли кто-нибудь дать несколько практических советов для любой из этих программ с точки зрения определения местоположения структуры данных, представляющей минное поле?
В WinDbg я могу установить точки останова, но мне сложно представить, в какой точке установить точку останова и в какой ячейке памяти. Точно так же, когда я просматриваю статический код в IDA Pro, я не уверен, с чего начать, чтобы найти функцию или структуру данных, представляющую минное поле.
Есть ли какие-нибудь обратные инженеры в Stackru, которые могут указать мне правильное направление?
10 ответов
Часть 1 из 3
Если вы серьезно относитесь к реверс-инжинирингу - забудьте о тренажерах и чит-движках.
Хороший реверс-инженер должен сначала познакомиться с ОС, основными функциями API, общей структурой программы (что такое цикл выполнения, структуры окон, процедуры обработки событий), форматом файла (PE). Может помочь классика Петцольда "Программирование Windows" (www.amazon.com/exec/obidos/ISBN=157231995X), а также онлайновый MSDN.
Сначала вы должны подумать о том, где можно вызвать процедуру инициализации минного поля. Я думал о следующем:
- Когда вы запускаете игру
- Когда вы нажимаете счастливое лицо
- Когда вы нажимаете Game->New или нажимаете F2
- Когда вы меняете уровень сложности
Я решил проверить команду акселератора F2.
Чтобы найти код обработки акселератора, вы должны найти процедуру обработки сообщений окна (WndProc). Это можно отследить с помощью вызовов CreateWindowEx и RegisterClass.
Читать:
- CreateWindowEx http://msdn.microsoft.com/en-us/library/ms632680%28VS.85%29.aspx
- RegisterClass http://msdn.microsoft.com/en-us/library/ms633586%28VS.85%29.aspx
- Глава 3 Петцольда "Окна и сообщения"
Откройте окно IDA, окно Imports, найдите "CreateWindow*", перейдите к нему и используйте команду "Jump xref to operand (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. Вы должны найти, где это по сравнению с 111ч. Это можно сделать либо путем отслеживания 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
Это простой способ узнать значения идентификатора сообщения.
Чтобы понять, как работать с акселератором, проверьте:
- Использование ускорителей клавиатуры
- Хакер ресурсов ( http://angusj.com/resourcehacker/)
Это довольно много текста для одного ответа. Если вам интересно я могу написать еще пару постов. Короче говоря, минное поле хранится в виде массива байтов [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"; на разрыве "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 ................................
Хм, похоже, мне нужен еще один пост, чтобы закрыть тему
Кажется, что вы пытаетесь разобрать исходный код, но вам нужно посмотреть на пространство памяти запущенной программы. Шестнадцатеричный редактор HxD имеет функцию, которая позволяет вам именно это.
http://www.freeimagehosting.net/uploads/fcc1991162.png
Как только вы попадаете в область памяти, вам нужно делать снимки памяти, пока вы бездельничаете с платой. Выделите то, что меняется, а что нет. Когда вы думаете, что у вас есть ручка, где структура данных находится в шестнадцатеричной памяти, попробуйте отредактировать ее, пока она находится в памяти, и посмотрите, не изменится ли в результате плата.
Процесс, который вы хотите, мало чем отличается от создания "тренера" для видеоигры. Они обычно основаны на поиске, где ценности, такие как здоровье и боеприпасы, живут в памяти и изменяют их на лету Возможно, вам удастся найти несколько хороших руководств о том, как создавать игровые тренажеры.
Ознакомьтесь с этой статьей проекта кода, она немного глубже, чем упомянутое вами сообщение в блоге.
http://www.codeproject.com/KB/trace/minememoryreader.aspx
редактировать
И эта статья, хотя и не посвящена непосредственно тральщику, дает вам хорошее пошаговое руководство по поиску памяти с помощью WinDbg:
http://www.codingthewheel.com/archives/extracting-hidden-text-with-windbg
Редактировать 2
Опять же, речь идет не о тральщике, но он определенно дал мне пищу для размышлений для отладки моей памяти, здесь есть множество учебных пособий:
http://memoryhacking.com/forums/index.php
Кроме того, загрузите CheatEngine (упомянутый Ником Д.) и проработайте учебник, который входит в комплект.
"В WinDbg я могу установить точки останова, но мне сложно представить, в какой точке установить точку останова и в каком месте памяти. Точно так же, когда я смотрю статический код в IDA Pro, я не уверен, с чего начать. чтобы найти функцию или структуру данных, которая представляет минное поле."
Именно так!
Что ж, вы можете искать такие подпрограммы, как random(), которые будут вызываться при создании таблицы mines. Эта книга мне очень помогла, когда я экспериментировал с реверс-инжинирингом.:)
В целом, хорошими местами для установки точек останова являются вызовы в окнах сообщений, вызовы для воспроизведения звука, таймеры и другие процедуры Win32 API.
Кстати, я сканирую тральщик прямо сейчас с OllyDbg.
Обновление: Немо напомнил мне отличный инструмент, Cheat Engine от Эрика "Dark Byte" Хейнена.
Cheat Engine (CE) - отличный инструмент для наблюдения и изменения пространства памяти других процессов. Помимо этого базового средства, CE имеет больше специальных функций, таких как просмотр дизассемблированной памяти процесса и внедрение кода в другие процессы.
(реальная ценность этого проекта в том, что вы можете скачать исходный код -Delphi- и посмотреть, как эти механизмы были реализованы - я сделал это много лет назад:o)
Очень хорошая статья на эту тему может быть найдена в Uninformed. Он довольно подробно описывает реверсивный Minesweeper (как введение в реверс-инжиниринг приложений Win32) и представляет собой довольно большой ресурс.
Этот сайт может быть более полезным:
http://www.subversity.net/reversing/hacking-minesweeper
Общий способ сделать это:
- Каким-то образом получить исходный код.
- Разберите и надеюсь, что оставшиеся символы могут помочь вам.
- Угадайте тип данных и попробуйте манипулировать им и использовать сканер памяти, чтобы ограничить возможности.
В ответ на Баунти
Ну, во втором чтении кажется, что вы хотели руководство о том, как использовать отладчик, такой как WinDBG, а не обычный вопрос о том, как выполнить обратный инжиниринг. Я уже показал вам веб-сайт, который сообщает вам значения, которые нужно искать, поэтому вопрос в том, как вы его ищете?
Я использую Блокнот в этом примере, потому что у меня не установлен Minesweeper. Но идея та же самая.
Вы печатаете
s <options> <memory start> <memory end> <pattern>
Нажмите "? ", А затем "s", чтобы увидеть помощь.
Как только вы нашли нужный вам шаблон памяти, вы можете нажать alt+5, чтобы вызвать просмотрщик памяти для хорошего отображения.
WinDBG требует некоторого привыкания, но он так же хорош, как и любой другой отладчик.
Мины, вероятно, будут храниться в каком-то двумерном массиве. Это означает, что это либо массив указателей, либо отдельный массив логических значений в стиле C.
Всякий раз, когда форма получает событие при наведении курсора, на эту структуру данных ссылаются. Индекс будет рассчитываться с использованием координат мыши, возможно, с использованием целочисленного деления. Это означает, что вы, вероятно, должны искать cmp
или аналогичная инструкция, где один из операндов вычисляется с использованием смещения и x
, где x
является результатом вычисления, включающего целочисленное деление. Тогда смещение будет указателем на начало структуры данных.
Вполне разумно предположить, что информация о минах размещается в памяти непрерывно, по крайней мере, для строк (т. Е. Это двумерный массив или массив массивов). Таким образом, я попытался бы открыть несколько смежных ячеек в одной строке, создавая дамп памяти процесса по мере того, как я иду, а затем анализировать их и искать любые повторяющиеся изменения в той же области памяти (т. Е. 1 байт изменен на первом шаге, на следующем на следующем шаге байт изменился до точно такого же значения и т. д.).
Также существует вероятность, что это упакованный битовый массив (3 бита на каждую шахту должно быть достаточно для записи всех возможных состояний - закрыт / открыт, моя / нет моя, помечены / не помечены), так что я бы тоже позаботился об этом (шаблоны также будут повторяемыми, хотя их будет сложнее обнаружить). Но это не удобная структура для работы, и я не думаю, что использование памяти было узким местом для Minesweeper, поэтому маловероятно, что такого рода вещи будут использоваться.
Хорошая точка для начала трассировки в отладчике - при наведении мыши. Так что найдите процедуру главного окна (я думаю, что инструменты, такие как spyxx, могут проверять свойства окон, и адрес обработчика событий является одним из них). Взломайте его и найдите, где он обрабатывает события мыши - там будет переключатель, если вы сможете распознать его в ассемблере (посмотрите на значение WM_XXX для мыши в windows.h).
Установите точку останова и начните входить. Где-то между моментом, когда вы отпустили кнопку мыши и обновление экрана, victum получит доступ к искомой структуре данных.
Будьте терпеливы, попытайтесь определить, что делается в любой момент времени, но не пытайтесь заглянуть слишком глубоко в код, который вы подозреваете в неинтересности для вашей текущей цели. Для отладки может потребоваться несколько запусков в отладчике.
Знание нормального рабочего процесса Win32 приложений тоже помогает.
Хотя это и не совсем "инструмент для реверс-инженера", а скорее игрушка, которую может использовать даже такой идиот, как я, посмотрите Cheat Engine. Это позволяет легко отслеживать, какие части памяти изменились, когда и даже имеет средства для отслеживания измененных частей памяти с помощью указателей (хотя, вероятно, вам это не нужно). Хороший интерактивный учебник включен.