Ну хватит разглагольствовать, смотри MsgProc и ECreateRemoteThread:
ECreateRemoteThreadNt proc USES ebx ecx esi hWind: HANDLE, dwStackSize: DWORD, lpStartAddress: DWORD, dwCreationFlags: DWORD
local ThreadID : DWORD
local HookH : HHOOK
local hEvent : HANDLE
local pLastBase : DWORD
local margs : CREATE_THREAD_ARGS
local FM : HANDLE
local MVF : DWORD
; Заполняем структуру параметров CreateThread'у
push dwStackSize
pop margs.dwStackSize
push lpStartAddress
pop margs.lpStartAddress
push dwCreationFlags
pop margs.dwCreationFlags
; Создаем файл, проецируемый в память...
invoke CreateFileMapping, INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, sizeof CREATE_THREAD_ARGS, SADD("FM_CRT_HOOK")
mov FM, eax
.if FM==0
mov eax, -3
ret
.endif
invoke MapViewOfFile, FM, FILE_MAP_WRITE, 0, 0, sizeof CREATE_THREAD_ARGS
mov MVF, eax
.if MVF==NULL
invoke CloseHandle, FM
mov eax, -4
ret
.endif
; Передаем параметры
invoke MemCopy, addr margs, MVF, sizeof CREATE_THREAD_ARGS
; Узнаем поток, в который будем внедряться
invoke GetWindowThreadProcessId, hWind, NULL
mov ThreadID, eax
.if ThreadID==NULL
invoke CloseHandle, FM
mov eax, -1
ret
.endif
invoke GetModuleHandle, NULL
mov pLastBase, eax
; Для синхронизации, чтобы не выгрузиться раньше выполнения LoadLibrary в ловушке...
invoke CreateEvent, NULL, FALSE, FALSE, SADD("EVENT_CRT_HOOK")
mov hEvent, eax
; Ставим локальный хук... Параметр, в котором должен быть базовый адрес библиотеки
invoke SetWindowsHookEx, WH_GETMESSAGE, addr MsgProc, pLastBase, ThreadID
mov HookH, eax
.if HookH==NULL
invoke CloseHandle, FM
mov eax, -2
ret
.endif
; Отсылаем сообщение, чтобы вызвалась MsgProc
invoke PostMessage, hWind, WM_USER+50, lpStartAddress, pLastBase
; Ждем пока не выполниться LoadLibrary
invoke WaitForSingleObject, hEvent, INFINITE
invoke UnhookWindowsHookEx, HookH
; Узнаем результат из того же MMF'a
mov eax,[MVF]
dodata <CRT_RES !<!>>
mov esi, ecx
invoke MemCopy, eax, esi, sizeof CRT_RES
mov eax, esi
push eax
; Закрываем за собой дверь
invoke UnmapViewOfFile, MVF
invoke CloseHandle, FM
invoke CloseHandle, hEvent
pop eax
ret
ECreateRemoteThreadNt endp
Теперь MsgProc:
MsgProc proc STDCALL USES ebx esi edi ecx code: DWORD, wparam: WPARAM, lparam: LPARAM
local LL : DWORD
local szModName: DWORD
local CT : DWORD
local FM : HANDLE
local lpThreadID : DWORD
local hEvent : HANDLE
local lpvar : DWORD
.if code==HC_ACTION
assume edi: PTR MSG
mov edi, lparam
.if [edi].message==WM_USER+50
; Увеличиваем счетчик блокировок нашего модуля, дабы не произошло AV
; Находим LoadLibrary...
dodata <db "LoadLibraryA",0>
push ecx
dodata <db "kernel32.dll",0>
push ecx
call EGetProcAddress
mov LL, eax
; Находим GetModuleFileName
dodata <db "GetModuleFileNameA",0>
push ecx
dodata <db "kernel32.dll",0>
push ecx
call EGetProcAddress
mov esi, eax
; Вызываем GetModuleFileName
push 126
dodata <db 126 dup(0)>
mov szModName, ecx
push ecx
call GetCurrModBase
push eax
call esi
; Вызываем LoadLibrary на наш модуль...
push szModName
call LL
; Для синхронизации
; Находим OpenEvent
dodata <db "OpenEventA",0>
push ecx
dodata <db "kernel32.dll",0>
push ecx
call EGetProcAddress
; Вызываем OpenEvent
dodata <db "EVENT_CRT_HOOK",0>
push ecx
push TRUE
push EVENT_ALL_ACCESS
call eax
mov hEvent, eax
; Create'им thread 😉
; Находим CreateThread
dodata <db "CreateThread",0>
push ecx
dodata <db "kernel32.dll",0>
push ecx
call EGetProcAddress
mov CT, eax
; Получаем параметры
; Находим OpenFileMapping
dodata <db "OpenFileMappingA",0>
push ecx
dodata <db "kernel32.dll",0>
push ecx
call EGetProcAddress
; Вызываем OpenFileMapping
dodata <db "FM_CRT_HOOK",0>
push ecx
push FALSE
push FILE_MAP_WRITE
call eax
cmp eax, 0
jz exit
mov ebx, eax
; Находим MapViewOfFile
dodata <db "MapViewOfFile",0>
push ecx
dodata <db "kernel32.dll",0>
push ecx
call EGetProcAddress
; Вызываем MapViewOfFile
push sizeof CREATE_THREAD_ARGS
push 0
push 0
push FILE_MAP_WRITE
push ebx
call eax
cmp eax, 0
jz exit
mov esi, eax
assume esi: PTR CREATE_THREAD_ARGS
; Вызываем CreateThread
lea eax, lpThreadID
push eax
push [esi].dwCreationFlags
push NULL
; Узнаем смещение ThreadProc'a
; Вычитаем из старого адреса старую базу
mov ebx, [edi].wParam
sub ebx, [edi].lParam
; Теперь вычисляем новый адрес
call GetCurrModBase
add ebx, eax
push ebx
push [esi].dwStackSize
push NULL
call CT
; Возвращаем результат
assume esi: PTR CRT_RES
push lpThreadID
pop [esi].ThreadID
push eax
pop [esi].hThread
call GetCurrModBase
push eax
pop [esi].hModule
; Находим SetEvent
dodata <db "SetEvent",0>
push ecx
dodata <db "kernel32.dll",0>
push ecx
call EGetProcAddress
; Вызываем SetEvent, чтобы продолжить основную прогу...
push hEvent
call eax
.endif
.endif
exit:
ret
MsgProc endp
Для удобства я объявил пару структур:
CREATE_THREAD_ARGS struct ; Параметры; смысл и название идентичны параметрам CT
dwStackSize dd 0
lpStartAddress dd 0
dwCreationFlags dd 0
CREATE_THREAD_ARGS ends
CRT_RES struct ; Результат, возвращаемый ECreateRemoteThread
hModule dd 0 ; база нашего модуля в чужом адресном пространстве
ThreadID dd 0 ; Индификатор нового потока
hThread dd 0 ; Хэндл нового потока
CRT_RES ends
Практика.
Что я сделал в качестве примера использования EcreateRemoteThread()??? Банальный
MessageBoxA. Но так как сообщение заставляет прогу «застыть», то мы можем протестировать – не происходит ли досрочная выгрузка нашего модуля. Если происходит, то возврат из MBA будет указывать
в никуда, появиться Access Violation. Это ты сможешь пронаблюдать, если уберешь из MsgProc’a LoadLibraryA. Рассмотрим текст главного модуля примера, который я написал. Тут все просто:
;-- Start of MakeIt.bat --
; @echo off
;
; if exist 1.obj del 1.obj
; if exist 1.exe del 1.exe
;
; c:\masm32\bin\ml /c /coff /nologo 1.asm
; c:\masm32\bin\Link /SUBSYSTEM:WINDOWS /FIXED:NO /MERGE:.rdata=.text /SECTION:.text,ERW
; 1.obj
;
; dir 1.*
;
; pause
;-- End of MakeIt.bat --
.386
.model flat, stdcall
option casemap :none
include c:\masm32\include\windows.inc ; Куча структур и еще много разной полезной хрени
include c:\masm32\include\user32.inc ; Разные MessageBox()'ов и SetWindowsHookEx()'ов
include c:\masm32\include\kernel32.inc ; ExitProcess, GetModuleHandle и т.д.
include c:\masm32\macros\macros.asm ; Макросы
include ..\include\API_Emul.asm ; Эмуляция апи функций+пару макросов by me 😉
include ..\include\CRT_API.asm ; CreateRemoteThread() by me 😉
includelib c:\masm32\lib\user32.lib
includelib c:\masm32\lib\kernel32.lib
.code
ThreadProc proc
; Находим MessageBox
dodata <db "MessageBoxA",0>
push ecx
dodata <db "user32.dll",0>
push ecx
call EGetProcAddress
; Показываем MB
push MB_OK
dodata <db "GOTCHA!!!",0>
push ecx
push ecx
push 0
call eax
; Завершаем поток
dodata <db "ExitThread",0>
push ecx
dodata <db "kernel32.dll",0>
push ecx
call EGetProcAddress
push 0
call eax
ret
ThreadProc endp
start:
main proc
invoke FindWindow, NULL, SADD("Калькулятор")
; Идентификатор окна в первом параметре
; Второй параметр - размер стэка нового потока , делаем его дефолтным
(1Мб)
; Третий – Точка входа потока в ГЛАВНОМ модуле.
; Для примера возьмем его suspend’нутым
(4ый параметр)
invoke ECreateRemoteThreadNt, eax, 0,ThreadProc,CREATE_SUSPENDED
; Результат в виде структуры CRT_RES
assume eax: PTR CRT_RES
.if (eax>0) && ([eax].hThread!=NULL)
; В CRT_RES уже есть hThread, но он не имеет прав на приостановку/восстановление
invoke OpenThread, THREAD_SUSPEND_RESUME, FALSE, [eax].ThreadID
; Запускаем поток
invoke ResumeThread, eax
.endif
invoke ExitProcess, 0
ret
main endp
end start
Ну вот, тут все прокомментировано и должно быть понятно.
Если ты думаешь, что это все, что может этот подход к хукам, ты ошибаешься… Можно делать поистине удивительные вещи с этой фишкой! Мы с ZeroIce’ом пораскинули мозгами и пришли к выводу, что довольно легко можно сделать свой код ВООБЩЕ невидимым… То бишь ни в списке процессов, ни в списке потоков, ни в списке модулей, он светиться не будет! Как это сделать? Просто! Все по старой схеме, только вместо создания потока и увеличения счетчика модуля делаем VirtualAlloc и копируем в зарезервированную область нужный код! А потом передаем
туда управление. А если сделать его еще и полиморфиком ;). Только тут, конечно, есть недостаток. Поток, в который мы внедрились, приостановиться на время выполнения нашего внедренного кода, но ведь можно и сделать
так, чтобы эта фишка происходила по определенному событию… Например, реализовать перехват АПИ через изменение таблицы импорта, а наш код будет покоиться, пока не произойдет вызов перехватываемой АПИ ;). Реализацию этого фишкаря оставлю тебе… В то же время в папке 9х ты найдешь пример резервирования памяти в чужом процессе и копирования
туда кода, установление атрибута исполнения и т.д.
Но передача туда управления не произойдет! У нас другая тема, реализация перехвата АПИ – тоже избитая тема, ссылка в конце статьи+читай у Рихтера…
Инструменты
Для тестирования кода на платформе 9х использовался MS Windows ME+OllyDbg v1.09d
Для тестирования кода на платформе NT использовался MS Windows XP Professional(build 2600 без sp)+SoftIce
В качестве компилятора везде выступал masm32v8.0 by Hutch
Ссылки
1) Где купить Рихтера? Например http://www.books.ru/shop/books/8283
или http://www.books-shop.com/book89.html
или http://www.mistral.ru/content/38168.shtml
(А ты че думал??? Электронный вариант? Черта с
два… Не для этого Рихтер работал, чтобы ты
на халяву его талмуд скатал)
2) masm32 by Hutch http://www.movsd.com/masmdl.htm
3) Описание таблицы экспорта http://www.wasm.ru/article.php?article=1002007
4) Эмуляция GetProcAddress’a http://www.wasm.ru/article.php?article=searchapi
5) Нахождение базы kernel’a http://sbvc.host.sk/articles/9.html
Greetzzzzz
Спасибо Four-F’у, который додумался, что все
это работает только из-за релоков. Если бы
не он, я бы, наверное, до сих пор делал бы /DEBUG
:).
Спасибо ZeroIce’у, который часто указывал
мне на баги и ошибки в моих примерах.