Доброго времени суток ув. форумчане.Покопавшись на форуме я понял что не хватает статей по перехвату АПИ,да и я давно ничего толком не писал.Так что сегодня пойдет речь,я думаю,об одном из самых перспективынх методов перехвата АПИ в ring 3- сплайсинге. И так не много теории. Сплайсинг-метод перехвата API функций в режиме юзера (ring 3). Обычно изменяются первые 5 байт функции на длинный прыжок по адресу обработчика перехвата.В зависимости от ситуации может понадобится дизассемблер длин инструкций,однако M$ изменила пролог начиная с ХР SP2, позволяя при этом не анализировать длины.Длины в 5 байт будет достаточно(5байт- E9h- опкод прыжка jmp).Сплайсинг так же позволяет осуществлять глобальный перехват функций, тем самым охватывая все процессы в системе. Применение: Данный вид перехвата используется: 1)Десктопное ПО производящее операции с ОС (например мониторы) 2)Хуки 3)Малварь (руткиты и т.п.) Инструменты: 1)ЯП (в данном случае Delphi) 2)музычка (kmfdm или rammstein сойдет) 3)руки.голова И так начнем. Как вы поняли наш код будет размещаться в dll-ке которая будет загружаться во все процессы и творить там свои темные дела.Чтобы не парится я буду извращаться с мэссэджбоксом. код: Создаем две записи-структуры Code: type OC=packed record frst:dword; sec:word; end; fj= packed record pusho:byte; pushar:pointer; reto:byte; end; 1-я будет содержать запись на оригинал кода.2-я длинный прыжок объявим переменные структур Code: var jmpmw,jmpma:fj; ocmw,ocma:oc; mwadr,maadr:pointer; я возьму функцию messageboxex- это считайте тот же messagebox только в нем присутствует идентификатор языка. Теперь нам нужно описать функции на которые будет происходить прыжок. Их будет две.Почему так?Мурка заключается в том что существуют две категории АПИ ansi и Unicode которые имеют соответствующее окончание MessageBoxA или например MessageBoxW. Соль в том что анси является 8 битной кодировкой и может предоставлять только 256 уникальных символов, в то время как юникод обеспечивает 65535 уникальных символов,это было сделано с целью работы со всеми языками мира. начнем описание функций: Code: 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 мы не работаем,а работаем с памятью. Получаем длину текста и длину заголовка.Выделяем кол-во динамической памяти= длине, получая данные в нужные переменные.Затем конвертируем строки и получаем готовые флаги ф-ии.вызываем ф-ю с нужными параметрами и освобождаем память. Следующей определяем оригинал Code: 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 Code: 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. Но в самом начале кода нам потребуется две вспомогательные функции. вот они: Code: 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 Code: 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; Дальше мы получаем адреса функций ,заполняем записи и устанавливаем прыжок. Code: 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.И переписываются оригинальные начала на наши. когда надо подставить оригинал,возвращаем байты Code: 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-ку глобально Code: Function Msg(code : integer; wParam : word; lParam : longint) : longint; stdcall; begin CallNextHookEx(0, Code, wParam, lparam); Result := 0; end; собсно сама процедура Code: Procedure SetGlobalHookO; begin SetWindowsHookEx(WH_GETMESSAGE, @Msg, HInstance, 0); Sleep(INFINITE); end; и оставляем в памяти. Code: Procedure SetGlobalHook; var hMutex: dword; TrId: dword; begin hMutex := CreateMutex(nil, false, 'Hook'); if GetLastError = 0 then CreateThread(nil, 0, @SetGlobalHookO, nil, 0, TrId) else CloseHandle(hMutex); end; устанавливаем глобальный хук. проверяем запущен ли файл.Только поток владеющий мьютексом имеет домтуп к области памяти. Code: 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. и смысл последней процедуры: при аттаче длл устанавливается хук. при детаче-хук соответственно снимается. Остается лишь написать лоадер. Code: .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-функции данными из структур Таким образом в начало оригинальных функций будут вставлены переходы на их функции-заменители. Рез-ат работы перехвата: ССЫЛАЛСЯ: На код перехвата createprocess ms-rema. Сильно не бейте!спасибо за внимание!
То, что на ачате нету статей по перехвату API, это нехорошо, конечно. Но и здесь не описано ничего нового. Все это можно найти в инете. Тот же MS-REM описал все намного лучше. Это также есть на самом деле нехорошо: Так как запросто может произойти ошибка в многопоточных приложениях, которые в свою очередь сразу же аварийно завершат работу. Код оформлен плохо. Эти INVALID_HANDLE_VALUE немного сбивают с толку. Также нужно сказать, что здесь перезаписывается не 5, а 6 байт.
Нет. Здесь нужен дизассемблер длин. Если использовать способ с перезаписыванием первых 5 байт, то алгоритм примерно такой: 0. С помощью дизассемблера длин выделяем столько целых первых команд перехватываемой функции, длина которых >= 5. Запоминаем эту длину в переменную len. 1. Запоминаем адрес следующей команды в переменную next_opcode. 2. Выделяем len + 5 байт памяти, адрес этой памяти записываем в переменную old_func. 3. В выделенный участок памяти копируем первые len байт из перехватываемой функции и дописываем команду jmp next_opcode (для нее мы выделели 5 дополнительных байт). 4. Перезаписываем первые 5 байт перехватываемой функции командой jmp <адрес нашей функции>. 5. Когда нужно вызвать старую функцию, делаем call old_func.
В многопоточных приложениях можно увидеть проблему. Например, перехватываешь recv (Winsock) в браузере и открываешь несколько вкладок.
а,я думал ты конкретно за эту фу-ю.возможно для других функций дизасм понадобится.надо бы юнит попробовать накатать.
Потоко опасный метод.Не нада ничего восстанавливать в теле оригинальной ф-ции.Кури детуры. ЗЫ: да и вообще ты люто бешанно боянишь http://forum.antichat.ru/thread32176.html
Chrome~ уже написал, что нужно использовать гейт. Суть хуков не раскрыта, вот если бы написал о VMT, IAT, HWBP и прочих популярных методах, да ещё и на C/C++, то было бы шикарно.
Суть раскрыта тут http://habrahabr.ru/blogs/cpp/90377/ .Только в детурах что там выложенны, в _AnalyzeFunctionCode нада впаять норм дизассемблер длин.И все будет зергуд работать)