Частина 1 з 3
Якщо ви серйозно ставитесь до зворотного проектування - забудьте про тренерів і чіт-двигунів.
Хороший інженер з реверсу повинен спочатку ознайомитися з ОС, основними функціями API, загальною структурою програми (що таке цикл запуску, структурами Windows, процедурами обробки подій), форматом файлу (PE). Класика Petzold "Програмування Windows" може допомогти (www.amazon.com/exec/obidos/ISBN=157231995X), а також онлайн-MSDN.
Спочатку слід подумати, де можна викликати процедуру ініціалізації мінного поля. Я думав про наступне:
- Коли ви запускаєте гру
- Коли ви натискаєте щасливе обличчя
- Коли ви натискаєте Гра-> Створити або натисніть F2
- При зміні рівня складності
Я вирішив перевірити F2 прискорювач команди.
Щоб знайти код обробки прискорювача, ви повинні знайти процедуру обробки віконних повідомлень (WndProc). Це можна простежити за допомогою викликів CreateWindowEx та RegisterClass.
Читати:
Відкрийте вікно ІДА, імпорт, знайдіть «Створити вікно *», перейдіть до нього та скористайтеся командою «Перейти xref до операнду (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
Це призводить до фрагмента коду, який викликає деякий proc і виходить з 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
Я був повністю впевнений, що знайшов масив мінного поля. Причина циклу, який запускає масив довжиною 360h байт (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 ................................
Хм, схоже, мені знадобиться ще одна публікація, щоб закрити тему