Описание метода взлома игры FlashPoint
Автор: FreeExec
Проанализировав содержимое файла OperationFlashpoint.exe, я обнаружил, что названия сегментов не содержат не одного символа и экспортируется минимальный набор функций из модулей KERNEL32.DLL, USER32.dll: LoadLibraryA, GetProcAddress, VirtualProtect, ExitProcess, MessageBoxA. Так обычно поступают, чтобы скрыть содержимое файла от всяких дисассемблеров, т.е. когда файл запакован.
Далее запускаю SoftICE и ставлю брекпоинт на функцию определения типа устройства (bpx GetDriveTypeA). Вставляем CD и запускаем игру. Убеждаемся, что функцию вызвала игра, а не explorer.exe. Дальше жму F5 пока эту функцию не вызовут с параметром указывающий путь к CD.
00011E2: lea eax,[ebp][-000C]
00011E5: push eax
00011E6: call d,[000407018] ; GetDriveTypeA
00011EC: mov dl,al ; al=05 тут мы оказались после выхода из функ.
00011EE: lea edi,[ebp][0FFFFF254] ; ссылка на буфер с меткой тома диска
00011F4: or ecx,-001
00011F7: xor eax,eax
00011F9: repne scasb
00011FB: not ecx
00011FD: dec ecx ; ecx равен длине метки
00011FE: add ecx,esi ; esi соответствует адресу на 4 байта меньше чем адрес буфера, т..е обработке подлежат последние 4 символа
0001200: mov bl,[eax][ecx]
0001203: add bl,dl
0001205: mov [eax][ecx],bl ;увеличивают символы на 5(вернула функ.) позиций дальше
0001208: inc eax
0001209: cmp eax,004 ; всего 4 символа
000120C: jl 000001200 -------- (2)
000120E: mov ecx,[ebp][-0010]
0001211: lea edx,[ebp][0FFFFFD1F]
0001217: push ecx
0001218: lea eax,[ebp][0FFFFF254]
000121E: push edx
000121F: push eax
0001220: call 0000014C0 -------- (3) ; эта функция сравнивает два буфера переданные ей в параметрах
0001225: add esp,00C
0001228: test eax,eax
000122A: jne 0000012D5 -------- (4)
Функция (3) возвращает 0, если буфера одинаковые, и значит (4)-ый переход не осуществляется.
:00401230 lea ecx, dword ptr [ebp-6C]
:00401233 push ecx
:00401234 call dword ptr [00407014] ; GetStartupInfoA
:0040123A xor eax, eax
:0040123C mov cl, byte ptr [ebp+eax-00000179] ; mrc32.dll
:00401243 mov byte ptr [ebp+eax-00000DAC], cl
:0040124A inc eax
:0040124B test cl, cl
:0040124D jne 0040123C
:0040124F lea edi, dword ptr [ebp+FFFFF254]
:00401255 or ecx, FFFFFFFF
:00401258 xor eax, eax
:0040125A mov dx, word ptr [004080C0]
:00401261 repnz
:00401262 scasb
:00401263 or ecx, FFFFFFFF
:00401266 mov word ptr [edi-01], dx
:0040126A mov edi, dword ptr [ebp+10]
:0040126D repnz
:0040126E scasb
:0040126F not ecx
:00401271 sub edi, ecx
:00401273 lea edx, dword ptr [ebp+FFFFF254]
:00401279 mov esi, edi
:0040127B mov ebx, ecx
:0040127D mov edi, edx
:0040127F or ecx, FFFFFFFF
:00401282 repnz
:00401283 scasb
:00401284 mov ecx, ebx
:00401286 dec edi
:00401287 shr ecx, 02
:0040128A repz
:0040128B movsd
:0040128C mov ecx, ebx
:0040128E lea eax, dword ptr [ebp-28]
:00401291 and ecx, 00000003
:00401294 push eax ; struct _PROCESS_INFORMATION
:00401295 repz
:00401296 movsb
:00401297 lea ecx, dword ptr [ebp-6C]
:0040129A lea edx, dword ptr [ebp+FFFFF254]
:004012A0 push ecx ; struct _STARTUPINFO
:004012A1 push 00000000
:004012A3 push 00000000
:004012A5 push 00000004
:004012A7 push 00000000
:004012A9 push 00000000
:004012AB push 00000000
:004012AD push edx ; mrc32.dll
:004012AE push 00000000
:004012B0 call dword ptr [00407010] ; CreateProcess
:004012B6 test eax, eax
:004012B8 jne 00401307
...
:00401307 mov ecx, dword ptr [ebp+FFFFFCF8]
:0040130D mov edx, dword ptr [ebp-28]
:00401310 lea eax, dword ptr [ebp-08]
:00401313 add ecx, 00400000
:00401319 push eax ; буфер, сюда вернут старые права
:0040131A push 00000040 ; новый код доступа, 40h=PAGE_EXECUTE_READWRITE
:0040131C push 00000100 ; размер
:00401321 push ecx ; адрес региона для изменения доступа к странице памяти
:00401322 push edx ; HANDLE hProcess из struct _PROCESS_INFORMATION
:00401323 call dword ptr [0040700C] ; VirtualProtectEx
:00401329 test eax, eax
:0040132B jne 00401348
…
:00401348 mov edx, dword ptr [ebp+FFFFFCF8]
:0040134E lea eax, dword ptr [ebp-08]
:00401351 push eax ; буфер, сюда вернут количество записанных байт
:00401352 mov eax, dword ptr [ebp-28]
:00401355 lea ecx, dword ptr [ebp+FFFFFD70]
:0040135B push 00000100 ; размер буфера
:00401360 add edx, 00400000
:00401366 push ecx ; адрес – откуда скопировать (12FCA4)
:00401367 push edx ; адрес – куда писать (44CCBF)
:00401368 push eax ; HANDLE hProcess
:00401369 call dword ptr [00407008] ; WriteProccessMemory
:0040136F test eax, eax
:00401371 je 0040137C
:00401373 cmp dword ptr [ebp-08], 00000100
:0040137A je 00401398 ; осуществляется прыжок
…
:00401398 mov ecx, dword ptr [ebp-24]
:0040139B push ecx ; HANDLE hThread
:0040139C call dword ptr [00407004] ; ResumeThread
:004013A2 mov edx, dword ptr [ebp-28]
:004013A5 push FFFFFFFF ; количество миллисикунд
:004013A7 push edx ; HANDLE hHandle
:004013A8 call dword ptr [00407000] ; WaitForSingleObject
|
Теперь разберемся во всех вызывающихся функциях. GetStartupInfo нужна для получения одноименной структуры, требующейся для функции создания процесса - CreateProcess. Создаем процесс используя файл mrc32.dll, он то и есть ядро игры. Затем функцией VirtualProtectEx назначаем новые права коду загруженного модуля, а именно разрешаем выполнять этот код. Потом туда копируем код из адреса 12FCA4, и передаем управление созданному процессу, функцией WaitForSingleObject. Единственная трудность это получить код который мы копируем для исполнения. Можно было конечно переписать эти 256 байт и руками, но я сделал по другому.
Вставляю CD, ставлю брекпоинт на функцию GetDriveTypeA (bpx GetDriveTypeA), запускаю игру. После вылета в функцию, ставлю еще один брекпоинт (bpx 401355),жму F5. Теперь мне нужны адреса трех функций CreateFileA, WriteFile, ExitProcess. Получаю их поможью команды EXP (exp CreateFileA), в ответ должно быть что-то такое:
exp CreateFileA
KERNEL32
001B:77E7A837 CreateFileA
exp WriteFile
KERNEL32
001B:77E79D8C WriteFile
exp ExitProcess
KERNEL32
001B:77E75CB5 ExitProcess
|
Затем задаю параметры первой функции, прямо в стек.
d esp ## показать стек
e ## начать редактировать его
|
Должно это выглядеть как-то так. Функция на языке Си выгледит так: CreateFileA(&filename, 0x40000000, 0, 0, 2, 0x80, 0); 8C 9D E7 77 – это адрес возврата (на функцию WriteFile = 77E79D8C), туда попадаем после завершения функции CreateFileA. По адресу 0012E198 (98 E1 12 00) находится имя файла завершающееся нулевым символом. Этот файл должен сущуствовать. Теперь ставим указатель на начало функции CreateFileA.
r eip=77E7A837 # изменяем значение регистра EIP
bpx WriteFile # что отловить момент когда завершится первая функция
|
Жму F5. Если все нормально, то должен остановится на первой команде функции WriteFile, а EAX не равняться FFFFFFFF, потому что это код ошибки. У меня EAX=000007CC, это хэндл на только что созданный файл.
Задаю параметры второй функции, прямо в стек.
d esp ## показать стек
e ## начать редактировать его
|
Должно это выглядеть как-то так. Функция на языке Си выгледит так: WriteFile(hHandle, 0x0012FCA4, 100, 0x0012E1C8, 0); где hHandle=0x7CC, 0x0012FCA4 – адрес буфера ради которого все и затеял, 100 его размер, 0x0012E1C8 – сюда напишут сколько реально записали байт. 5B 5C E7 77 – это адрес возврата (на функцию ExitProcess = 77E75CB5), туда попадаем после завершения функции CreateFileA. Теперь у меня есть все чтобы с имитировать запуск игры, т.е. написать загрузчик самому без всяких выкрутасов.
Приведен пример загрузчика:
File calldll.asm
.386
.model flat, stdcall
option casemap :none ; case sensitive
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
.data?
buff db 100h dup (?)
prin db 100h dup (?)
.code
start:
jmp @F
libName db "mrc32.dll",0
include patch.inc
@@:
push offset buff
call GetStartupInfo
push offset prin
push offset buff
push 0
push 0
push 4
push 0
push 0
push 0
push offset libName
push 0
call CreateProcess
mov eax, offset [prin+20h]
push eax
push 000040h
push 000100h
mov ecx, 44CCBFh
push ecx
mov eax, dword ptr [prin]
push eax
call VirtualProtectEx
mov eax, offset [prin+20h]
push eax
push 000100h
push offset patch
push 44CCBFh
mov eax, dword ptr [prin]
push eax
call WriteProcessMemory
mov eax, dword ptr [prin+4]
push eax
call ResumeThread
push 0000FFh
mov eax, dword ptr [prin]
push eax
call WaitForSingleObject
invoke ExitProcess,eax
end start
|
file patch.ini
patch:
db 055h,08Bh,0ECh,06Ah,0FFh,068h,018h,024h,06Eh,000h,068h,0ECh,020h,06Ch,000h,064h
db 0A1h,000h,000h,000h,000h,050h,064h,089h,025h,000h,000h,000h,000h,083h,0ECh,058h
db 053h,056h,057h,089h,065h,0E8h,0FFh,015h,00Ch,052h,06Ch,000h,033h,0D2h,08Ah,0D4h
db 089h,015h,0C8h,070h,077h,000h,08Bh,0C8h,081h,0E1h,0FFh,000h,000h,000h,089h,00Dh
db 0C4h,070h,077h,000h,0C1h,0E1h,008h,003h,0CAh,089h,00Dh,0C0h,070h,077h,000h,0C1h
db 0E8h,010h,0A3h,0BCh,070h,077h,000h,033h,0F6h,056h,0E8h,0CAh,052h,0FFh,0FFh,059h
db 085h,0C0h,075h,008h,06Ah,01Ch,0E8h,06Fh,04Eh,027h,000h,059h,089h,075h,0FCh,0E8h
db 0B9h,062h,0FFh,0FFh,0FFh,015h,0D8h,051h,06Ch,000h,0A3h,064h,08Bh,077h,000h,0E8h
db 08Fh,02Ah,0FFh,0FFh,0A3h,0FCh,070h,077h,000h,0E8h,0A3h,0A0h,0FFh,0FFh,0E8h,037h
db 0A1h,0FFh,0FFh,0E8h,0F4h,036h,0FFh,0FFh,089h,075h,0D0h,08Dh,045h,0A4h,050h,0FFh
db 015h,010h,052h,06Ch,000h,0E8h,0FEh,0FEh,0FFh,0FFh,089h,045h,09Ch,0F6h,045h,0D0h
db 001h,074h,006h,00Fh,0B7h,045h,0D4h,0EBh,003h,06Ah,00Ah,058h,050h,0FFh,075h,09Ch
db 056h,056h,0FFh,015h,0E8h,050h,06Ch,000h,050h,0E8h,099h,087h,0FDh,0FFh,089h,045h
db 0A0h,050h,0E8h,0F3h,036h,0FFh,0FFh,08Bh,045h,0ECh,08Bh,008h,08Bh,009h,089h,04Dh
db 098h,050h,051h,0E8h,060h,001h,000h,000h,059h,059h,0C3h,08Bh,065h,0E8h,0FFh,075h
db 098h,0E8h,0C3h,036h,0FFh,0FFh,083h,03Dh,004h,071h,077h,000h,001h,075h,005h,0E8h
|
file compl.bat
@echo off
e:\masm32\bin\ml /Zf /Zi /c /coff /w calldll.asm
if errorlevel 1 goto exit
echo _
echo MASM32 OK *************************************************
echo _
e:\masm32\bin\Link /DEBUG /SUBSYSTEM:WINDOWS calldll.obj
:exit
pause
|
Весь архив можно скачать (и он вроде даже работает :)
|