Как мне найти структуру данных, которая представляет мое расположение Minesweeper в памяти? - PullRequest
91 голосов
/ 31 мая 2009

Я пытаюсь изучить реверс-инжиниринг, используя Minesweeper в качестве примера приложения. Я нашел эту статью MSDN о простой команде WinDbg, которая показывает все мины, но она старая, не объясняется ни в каких деталях и на самом деле не то, что я ищу.

У меня есть IDA Pro дизассемблер и WinDbg , и я загрузил winmine.exe в оба из них. Может ли кто-нибудь дать несколько практических советов для любой из этих программ с точки зрения определения местоположения структуры данных, представляющей минное поле?

В WinDbg я могу установить точки останова, но мне сложно представить, в какой точке установить точку останова и в какой ячейке памяти. Точно так же, когда я просматриваю статический код в IDA Pro, я не уверен, с чего бы начать поиск функции или структуры данных, представляющей минное поле.

Есть ли какие-нибудь обратные инженеры в Stackoverflow, которые могут указать мне правильное направление?

Ответы [ 10 ]

121 голосов
/ 24 июля 2009

Часть 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  ................................

Хм, похоже мне нужен еще один пост, чтобы закрыть тему

14 голосов
/ 21 июля 2009

Кажется, что вы пытаетесь разобрать источник, но вам нужно посмотреть на объем памяти работающей программы. Шестнадцатеричный редактор HxD имеет функцию, которая позволяет вам сделать это.

http://www.freeimagehosting.net/uploads/fcc1991162.png

Как только вы попадаете в область памяти, вам нужно делать снимки памяти, пока вы бездельничаете с платой. Выделите то, что меняется, а что нет. Если вы считаете, что у вас есть указатель на то, где находится структура данных в шестнадцатеричной памяти, попробуйте отредактировать ее, пока она находится в памяти, и посмотрите, не изменится ли в результате плата.

Процесс, который вы хотите, мало чем отличается от создания «трейнера» для видеоигры. Они обычно основаны на поиске, где такие ценности, как здоровье и боеприпасы, живут в памяти и меняют их на лету. Возможно, вам удастся найти несколько хороших руководств по созданию игровых тренажеров.

11 голосов
/ 07 июля 2009

Ознакомьтесь с этой статьей проекта кода, она немного глубже, чем упомянутое вами сообщение в блоге.

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 (упомянутый Ником Д.) и проработайте учебное руководство, которое входит в комплект.

9 голосов
/ 31 мая 2009

"В WinDbg я могу установить точки останова, но мне трудно представить в какой точке установить точку останова и какая ячейка памяти. Точно так же, когда Я смотрю статический код в IDA Pro, я не уверен, где вообще начать искать функция или структура данных, которая представляет минное поле. "

Точно!

Ну, вы можете искать подпрограммы наподобие random (), которые будут вызываться во время построения таблицы mines. Эта книга очень помогла мне, когда я экспериментировал с реверс-инжинирингом. :)

В общем, хорошими местами для установки точек останова являются вызовы в окнах сообщений, вызовы для воспроизведения звука, таймеры и другие процедуры win32 API.

Кстати, я сейчас сканирую тральщик с OllyDbg .

Обновление: nemo напомнило мне отличный инструмент Cheat Engine от Eric "Dark Byte" Heijnen.

Cheat Engine (CE) - отличный инструмент для наблюдения и изменения пространства памяти других процессов. Помимо этого basic , CE обладает более специальными функциями, такими как просмотр разобранной памяти процесса и внедрение кода в другие процессы.

( реальное значение этого проекта в том, что вы можете загрузить исходный код -Delphi- и посмотреть, как эти механизмы были реализованы - я делал это много лет назад: o)

5 голосов
/ 22 июля 2009

Очень хорошая статья на эту тему может быть найдена в Uninformed . Он довольно подробно описывает реверсивный Minesweeper (как введение в реверс-инжиниринг приложений Win32) и представляет собой довольно большой ресурс.

4 голосов
/ 31 мая 2009

Этот сайт может быть более полезным:

http://www.subversity.net/reversing/hacking-minesweeper

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

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

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

Ну, во втором чтении кажется, что вы хотели получить руководство о том, как использовать отладчик, такой как WinDBG, а не обычный вопрос о том, как выполнить обратный инжиниринг. Я уже показал вам веб-сайт, который сообщает вам значения, которые нужно искать, поэтому вопрос в том, как вы его ищете?

Я использую Блокнот в этом примере, потому что у меня не установлен Minesweeper. Но идея та же.

alt text

Вы вводите

s <options> <memory start> <memory end> <pattern>

Нажмите «?», А затем «s», чтобы увидеть справку.

Как только вы нашли нужный вам шаблон памяти, вы можете нажать alt + 5, чтобы вызвать просмотрщик памяти для хорошего отображения.

alt text

WinDBG требует некоторого привыкания, но он так же хорош, как и любой другой отладчик.

0 голосов
/ 23 июля 2009

Хотя это не совсем «инструмент для реверс-инженера», и даже больше игрушек, которые мог бы использовать даже такой идиот, как я, посмотрите Cheat Engine . Это несколько облегчает отслеживание того, какие части памяти изменились, когда и даже имеет средства для отслеживания измененных частей памяти с помощью указателей (хотя, вероятно, вам это не нужно). Хороший интерактивный учебник включен.

0 голосов
/ 22 июля 2009

Разумно предположить, что информация о минах размещается в памяти непрерывно, по крайней мере, для строк (то есть это 2D-массив или массив массивов). Таким образом, я попытался бы открыть несколько смежных ячеек в одной строке, создавая дамп памяти процесса по мере того, как я иду, а затем анализировать их и искать любые повторяющиеся изменения в той же области памяти (т. Е. 1 байт изменен на первом шаге, на следующем на следующем шаге байт изменился до точно такого же значения и т. д.)

Существует также вероятность того, что это упакованный битовый массив (3 бита на каждую шахту должно быть достаточно для записи всех возможных состояний - закрыт / открыт, моя / нет моя, помечен / не помечен), так что я бы обратил внимание на это тоже (паттерны также будут повторяемыми, хотя их будет сложнее обнаружить). Но иметь дело с не очень удобной структурой, и я не думаю, что использование памяти было узким местом для Minesweeper, поэтому маловероятно, что такого рода вещи будут использоваться.

0 голосов
/ 22 июля 2009

Мины, вероятно, будут храниться в каком-то двумерном массиве. Это означает, что это либо массив указателей, либо один массив логических значений в стиле C.

Всякий раз, когда форма получает событие мыши, на эту структуру данных ссылаются. Индекс будет рассчитываться с использованием координат мыши, возможно, с использованием целочисленного деления. Это означает, что вам, вероятно, следует искать cmp или аналогичную инструкцию, где один из операндов вычисляется с использованием смещения, а x, где x - результат вычисления с целочисленным делением. Тогда смещение будет указателем на начало структуры данных.

0 голосов
/ 21 июля 2009

Хорошая точка для начала трассировки в отладчике - при наведении мыши. Так что найдите процедуру главного окна (я думаю, что инструменты, такие как spyxx, могут проверять свойства окон, и адрес обработчика событий является одним из них). Взломайте его и найдите, где он обрабатывает события мыши - будет переключатель, если вы сможете распознать его в ассемблере (посмотрите на значение WM_XXX для мыши в windows.h).

Установите точку останова и начните входить. Где-то между моментом, когда вы отпустили кнопку мыши и обновление экрана, victum получит доступ к искомой структуре данных.

Будьте терпеливы, постарайтесь определить, что делается в любой момент времени, но не пытайтесь заглядывать слишком глубоко в код, который вы подозреваете в неинтересности для вашей текущей цели. Для отладки может потребоваться несколько прогонов в отладчике.

Знание нормального рабочего процесса приложений win32 также помогает.

...