Как я могу найти структуру данных, которая представляет мое расположение 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.

Читать:

Откройте окно 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

Это простой способ узнать значения идентификатора сообщения.

Чтобы понять, как работать с акселератором, проверьте:

Это довольно много текста для одного ответа. Если вам интересно я могу написать еще пару постов. Короче говоря, минное поле хранится в виде массива байтов [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

Общий способ сделать это:

  1. Каким-то образом получить исходный код.
  2. Разберите и надеюсь, что оставшиеся символы могут помочь вам.
  3. Угадайте тип данных и попробуйте манипулировать им и использовать сканер памяти, чтобы ограничить возможности.

В ответ на Баунти

Ну, во втором чтении кажется, что вы хотели руководство о том, как использовать отладчик, такой как 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. Это позволяет легко отслеживать, какие части памяти изменились, когда и даже имеет средства для отслеживания измененных частей памяти с помощью указателей (хотя, вероятно, вам это не нужно). Хороший интерактивный учебник включен.

Другие вопросы по тегам