сегодня пойдет речь,я думаю,об одном из самых перспективынх методов перехвата АПИ в ring 3- сплайсинге.
И так не много теории.
Сплайсинг-метод перехвата API функций в режиме юзера (ring 3). Обычно изменяются первые 5 байт функции на длинный прыжок по адресу обработчика перехвата.В зависимости от ситуации может понадобится дизассемблер длин инструкций,однако M$ изменила пролог начиная с ХР SP2, позволяя при этом не анализировать длины.Длины в 5 байт будет достаточно(5байт- E9h- опкод прыжка jmp).Сплайсинг так же позволяет осуществлять глобальный перехват функций, тем самым охватывая все процессы в системе.
Применение:
Данный вид перехвата используется:
1)Десктопное ПО производящее операции с ОС (например мониторы)
2)Хуки
3)Малварь (руткиты и т.п.)
Инструменты:
1)ЯП (в данном случае Delphi)
2)музычка (kmfdm или rammstein сойдет)
3)руки.голова
И так начнем.
Как вы поняли наш код будет размещаться в dll-ке которая будет загружаться во все процессы и творить там свои темные дела.Чтобы не парится я буду извращаться с мэссэджбоксом.
код:
Создаем две записи-структуры
type
OC=packed record
frst:dword;
sec:word;
end;
fj= packed record
pusho:byte;
pushar:pointer;
reto:byte;
end;
1-я будет содержать запись на оригинал кода.2-я длинный прыжок
объявим переменные структур
var
jmpmw,jmpma:fj;
ocmw,ocma:oc;
mwadr,maadr:pointer;
я возьму функцию messageboxex- это считайте тот же messagebox только в нем присутствует идентификатор языка.
Теперь нам нужно описать функции на которые будет происходить прыжок.
Их будет две.Почему так?Мурка заключается в том что существуют две категории АПИ ansi и Unicode которые имеют соотв. окончание MessageBoxA или например MessageBoxW. Соль в том что анси является 8 битной кодировкой и может предоставлять только 256 уникальных символов, в то время как юникод обеспечивает 65535 уникальных символов,это было сделано с целью работы со всеми языками мира.
начнем описание функций:
function NMessageBoxExA(hWnd: HWND; lpText, lpCaption: PAnsiChar;
uType: UINT; wLanguageId: Word): Integer; stdcall;
var txt,cap:pwidechar;
txtl,capl:dword;
begin
txtl:=lstrlen(lptext)*sizeof(widechar)+2;
capl:=lstrlen(lpcaption)*sizeof(widechar)+2;
getmem(txt,txtl);
getmem(cap,capl);
stringtowidechar(lptext,txt,txtl);
stringtowidechar(lpcaption,cap,capl);
result:=MessageBoxExW(hWnd, txt, Cap, uType, wLanguageId);
freemem(txt);
freemem(cap);
end;
что к чему. новая функция имеет те же флаги что и обычная:
дескриптор окна, текст сообщения,заголовок окна,стиль окна и язык.
работаем с widechar выше разобрали почему(т.к. он поддерживает интернациональные символы).
напрямую со string мы не работаем,а работаем с памятью.
Получаем длину текста и длину заголовка.Выделяем кол-во динамической памяти= длине, получая данные в нужные переменные.Затем конвертируем строки и получаем готовые флаги ф-ии.вызываем ф-ю с нужными параметрами и освобождаем память.
Следующей определяем оригинал
function TrueMessageBoxExW(hWnd: HWND; lpText, lpCaption: PWideChar;
uType: UINT; wLanguageId: Word): Integer; stdcall;
var
Written: dword;
begin
writeProcessMemory(INVALID_HANDLE_VALUE, mwadr,@ocmw, SizeOf(OC), Written);
Result := MessageBoxExW(hWnd, lpText, lpCaption, uType, wLanguageId);
WriteProcessMemory(INVALID_HANDLE_VALUE, mwadr,@Jmpmw, SizeOf(fj), Written);
end;
За ней идет юникодный msgbox
function NMessageBoxExW(hWnd: HWND; lpText, lpCaption: PWideChar;
uType: UINT; wLanguageId: Word): Integer; stdcall;
var
Atxt: PWideChar;
ntxtt: PWideChar;
nlen: dword;
a:integer;
begin
a:=length('Happy new year asshole!');
Atxt := stringtopwide('Happy new year asshole!',a);
nlen := (lstrlenw(Atxt) + lstrlenw(lpText)) * SizeOf(WideChar) + 20;
GetMem(ntxtt, nlen);
lstrcpyw(ntxtt, lpText);
lstrcatw(ntxtt, #10#13#10#13);
lstrcatw(ntxtt, Atxt);
FreeMem(Atxt);
Result := TrueMessageBoxExW(hWnd, ntxtt, lpCaption, uType, wLanguageId);
FreeMem(ntxtt);
end;
по анологии с ansichar заполняем Unicode. Вы можете наблюдать строку.Эта строка будет выдаваться теперь при вызовах msgbox. Но в самом начале кода нам потребуется две вспомогательные функции.
вот они:
Function AllocMem(Size: Integer): Pointer;
asm
or EAX, EAX
JZ @@exit
PUSH EAX
CALL System.@GetMem
POP EDX
PUSH EAX
MOV CL, 0
CALL System.@FillChar
POP EAX
@@exit:
end;
*написано с целью не подключать модуль sysutils
эта функция выд-т область памяти,после чего мы применяем функцию конвертирования string строки в widechar
function stringtopwide(str:string;var a:integer):pwidechar;
var
pwc:pwidechar;
d:integer;
begin
d:=length(str)+1;
a:=d*2;
pwc:=allocmem(a);
multibytetowidechar(cp_acp,0,pchar(str),d,pwc,a);
result:=pwc;
end;
Дальге мы получаем адреса функций ,заполняем записи и устанавливаем прыжок.
procedure SetHook;
var
hwnd: dword;
Byte: dword;
begin
hwnd := GetModuleHandle('user32.dll');
mwadr := GetProcAddress(hwnd, 'MessageBoxExW');
maadr := GetProcAddress(hwnd, 'MessageBoxExA');
ReadProcessMemory(INVALID_HANDLE_VALUE, mwadr, @ocmw, SizeOf(OC), Byte);
ReadProcessMemory(INVALID_HANDLE_VALUE, maadr, @ocma, SizeOf(OC), Byte);
jmpmw.pusho := $68;
jmpmw.PushAr := @NMessageBoxExW;
jmpmw.RetO := $C3;
jmpma.pusho := $68;
jmpma.PushAr := @NMessageBoxExA;
jmpma.RetO := $C3;
WriteProcessMemory(INVALID_HANDLE_VALUE, mwadr, @jmpmw, SizeOf(fj), Byte);
WriteProcessMemory(INVALID_HANDLE_VALUE, maadr, @jmpma, SizeOf(fj), Byte);
end;
разбираем:
получаем дескриптор системной dll-ки в которой хранятся наши функции.
записываем их адркса в записи.
сохраняем оригинальные начала. Формирование новых начал функций в структурах jmpmw и jmpma идет так: опкод 68h -push, затем ее аргумент – адрес нашей функции, которая заменит оригинальную, в конце идет опкод c3h-ret.И переписываются оригинальные начала на наши.
когда надо подставить оригинал,возвращаем байты
Procedure Unhook;
var
Byte: dword;
begin
WriteProcessMemory(INVALID_HANDLE_VALUE, maadr, @ocma, SizeOf(OC), Byte);
WriteProcessMemory(INVALID_HANDLE_VALUE, mwadr, @ocmw, SizeOf(OC), Byte);
end;
Далее следует процедура,которая поможет загрузить нашу dll-ку глобально
Function Msg(code : integer; wParam : word;
lParam : longint) : longint; stdcall;
begin
CallNextHookEx(0, Code, wParam, lparam);
Result := 0;
end;
собсно сама процедура
Procedure SetGlobalHookO;
begin
SetWindowsHookEx(WH_GETMESSAGE, @Msg, HInstance, 0);
Sleep(INFINITE);
end;
и оставляем в памяти.
Procedure SetGlobalHook;
var
hMutex: dword;
TrId: dword;
begin
hMutex := CreateMutex(nil, false, 'AdvareHook');
if GetLastError = 0 then
CreateThread(nil, 0, @SetGlobalHookO, nil, 0, TrId) else
CloseHandle(hMutex);
end;
устанавливаем глобальный хук. проверяем запущен ли файл.Только поток владеющий мьютексом имеет домтуп к области памяти.
procedure DLL(dwReason: DWord);
begin
case dwReason of
DLL_PROCESS_ATTACH:
begin
SetGlobalHook;
SetHook;
end;
DLL_PROCESS_DETACH: UnHook;
end;
end;
begin
DllProc := @DLL;
DLL(DLL_PROCESS_ATTACH);
end.
и смысл последней процедуры: при аттаче длл устанавливается хук. при детаче-хук соответственно снимается.
Остается лишь написать лоадер.
.386
.model flat,stdcall
option casemap:none
include include\windows.inc
include include\kernel32.inc
includelib lib\kernel32.lib
.data
libname db 'MSGSPLICE.dll',0
start:
invoke LoadLibrary,addr libname
invoke Sleep,INFINITE
end start
грузим dll-ку и висим в памяти.
и так алгоритм работы в кратце (итог типо)
1) получаем дескриптор библиотеки , в которой содержатся функции которые необходимо перехватить.
2) в соответствующие переменные записываем оригинальные адреса функций
3) сохраняются оригинальные начала функций
4) Формирование новых начал функций в структурах jmpmw и jmpma. В начале идет опкод команды push, затем ее аргумент – адрес нашей функции, которая заменит оригинальную, в конце идет оператор ret.
5) переписываются начала оригинальных API-функции данными из структур
Таким образом в начало оригинальных функций
будут вставлены переходы на их функции-заменители.
Комментариев нет:
Отправить комментарий