HookAPI
Взять можно здесь: http://sources.codenet.ru/download/1205/HookApi_h.html
HookApi - базовый класс для перехвата вызовов функций API из текущего процесса.
Класс позволяет как мониторить вызовы (перехватывать и отдавать управление в реальную функцию API), так и самостоятельно обрабатывать вызовы (возвращать управление вызывающему коду, не передавая управление в реальную функцию API).
ВНИМАНИЕ! Код перехвата изменяет значения регистра ECX.
Для моих целей это было нестрашно, но для ваших может быть серьезной помехой. Так что применяйте обдуманно.
Использовать класс очень просто. Наследуете от него класс для перехвата определенной функции API, переопределеяте в наследнике виртуальный метод handler. Для удобства установки хука можно определить ещё специальный метод hook.
Подробнее о виртуальном методе handler:
Аргументы:
stack - указатель на вершину стека при вызове API, т.е. по большому счету указатель на адрес возврата, за которым следуют параметры вызова.
result - указатель на возвращаемое вместо API-функции значение. Если установленный хук сам обрабатывает вызов не передавая управление в реальную функцию API, то в DWORD, на который указывает result, должно быть помещено якобы возвращаемое из API-функции значение.
Возвращаемое значение:
Если меньше нуля, то это значит, что управление из перехватчика будет передано в реальную функцию;
Если больше, либо равно нулю, то это размер аргументов на стеке передаваемых в API-функцию. Т.к. API-функции имеют спецификацию cdecl, т.е. обязаны сами очищать стек, от переданных им параметров, а количество переданных параметров зависит от конкретной функции, то при эмуляции (перехвате с обработкой без вызова реальной API-функции) перехватывающий код должен сам очистить стек от этих параметров, а для этого надо указать сколько они занимали места на стеке.
Пример использования:
#include <iostream>
using namespace std;
#include "HookApi.h"
//
// Перехватчик API-функции NtOpenFile
//
class HookNtOpenFile :public HookApi
{
public:
bool hook() {
return HookApi::hook("ntdll", "NtOpenFile");
}
private:
virtual DWORD handler(void** stack, PDWORD result) {
wcout << "HookNtOpenFile" << endl;
return -1; // Передаем управление в реальную API-функцию
}
};
//
// Перехватчик API-функции RtlDosSearchPath_U
//
class HookRtlDosSearchPath_U :public HookApi
{
public:
bool hook() {
return HookApi::hook("ntdll", "RtlDosSearchPath_U");
}
private:
virtual DWORD handler(void** stack, PDWORD result) {
// читаем параметры вызова из стека
PWSTR lpPath = (PWSTR)stack[1];
PWSTR lpFileName = (PWSTR)stack[2];
PWSTR lpExtension = (PWSTR)stack[3];
ULONG nBufferLength = (ULONG)stack[4];
PWSTR lpBuffer = (PWSTR)stack[5];
PWSTR* lpFilePart = (PWSTR*)stack[6];
wcout << "HookRtlDosSearchPath_U: " << endl;
wcout << lpPath << endl;
wcout << lpFileName << endl;
// Здесь производим собственную обработку
...................................
*result = 0; // Выставляем возвращаемое в вызывающий код значение
return 6 * sizeof(DWORD); // Возвращаем управление в вызывающий
// код без передачи в реальную API-функцию.
// Шесть параметров занимают на стеке 6 * sizeof(DWORD)
}
};
int main()
{
// Устанавливаем хуки
HookNtOpenFile hookNtOpenFile;
hookNtOpenFile.hook();
HookRtlDosSearchPath_U hookRtlDosSearchPath_U;
hookRtlDosSearchPath_U.hook();
// Код вызывающий прохученные функции
................................
return 0;
а так то нормально, цивильно
ну надо же придумать через объект устанаваливать перехват на АПИ
P.S. Прохученные - какое слово-то нашел... ладно хоть не хуканутые:D
Вторая версия базового класса для перехвата вызовов функций API из текущего процесса.
Взять можно здесь: http://sources.codenet.ru/download/1225/HookApi_v2_0.html
Класс позволяет как мониторить вызовы (перехватывать и отдавать управление в реальную функцию API), так и самостоятельно обрабатывать вызовы (возвращать управление вызывающему коду, не передавая управление в реальную функцию API).
Основное отличие от первой версии в том, что теперь параметры обрабатывающей функции такие же, как у функции, на которую этот хук поставлен. Т.о. можно просто скопировать объявление API-функции и заменить её имя на handler и описание функции перехвата готово.
Добавлена возможность снятия хука (метод unhook), в т.ч. через деструктор объекта (в деструкторе базового класса вызывается метод unhook).
Теперь код перехвата не изменяет значения регистра ECX.
Единственный регистр, значение которого изменяется, - это EAX.
Использовать класс очень просто. Наследуете от него класс для перехвата определенной функции API, определеяте в наследнике метод handler (теперь не виртуальный). Параметры этого метода (аргументы и возвращаемое значение) должны быть такие же, как у функции, которую перехватываем.
Внимание: базовый класс является шаблонным, наследоваться нужно от него, инстациированого дочерним классом (см. пример использования).
Для того, чтобы после обработки перехватчиком управление не было передано в реальную функцию, необходимо вызвать метод setHandled.
Для удобства установки хука можно определить ещё специальный метод hook (см. пример использования).
Пример использования:
#include <windows.h>
#include <winternl.h>
#include <iostream>
using namespace std;
#include "HookApi.h"
class HookNtOpenFile :public HookApi<HookNtOpenFile>
{
public:
bool hook() {
return HookApi::hook("ntdll", "NtOpenFile");
}
NTSTATUS handler(OUT PHANDLE FileHandle, IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes, OUT PIO_STATUS_BLOCK IoStatusBlock,
IN ULONG ShareAccess, IN ULONG OpenOptions)
{
wcout << "HookNtOpenFile: " << ObjectAttributes->ObjectName->Buffer << endl;
return 0;
}
};
#define STATUS_OBJECT_NAME_NOT_FOUND ((NTSTATUS)0xC0000034L)
class HookNtOpenSection :public HookApi<HookNtOpenSection>
{
public:
bool hook() {
return HookApi::hook("ntdll", "NtOpenSection");
}
NTSTATUS handler(OUT PHANDLE SectionHandle, IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes)
{
wcout << "NtOpenSection: " << ObjectAttributes->ObjectName->Buffer << endl;
if( !wcsicmp(ObjectAttributes->ObjectName->Buffer, L"USER32.DLL") ) {
cout << "BINGO!" << endl;
*SectionHandle = NULL;
setHandled(); // Возвращаем управление в вызывающий
return STATUS_OBJECT_NAME_NOT_FOUND; //код без передачи в реальную API-функцию.
}
return 0;
}
};
int main()
{
HookNtOpenFile hookNtOpenFile;
hookNtOpenFile.hook();
HookNtOpenSection hookNtOpenSection;
hookNtOpenSection.hook();
HMODULE nMod = LoadLibrary("user32.dll");
return 0;
}
P.S. Обратите внимание, что теперь не надо указывать сколько аргументов функция принимает, это делается автоматически (для функций, у которых до 16 аргументов). Как это делается можно узнать, взглянув в файл ArgumentsCount.h.
Может кто-то предложит способ сделать это изящнее?
Ещё обратите внимание на шаблон super_cast. :D
P.S. Спасибо за пример:)
Да нет, возможно. Только для EAX это не требуется, т.к. в нем, все равно, будет возвращаемое значение.
На этот раз пример использования вполне рабочий. Можно скопировать, построить и запустить.
Если интересно, могу рассказать в чем его суть (что он делает). Это хитрый пример. ;)
На этот раз пример использования вполне рабочий. Можно скопировать, построить и запустить.
Если интересно, могу рассказать в чем его суть (что он делает). Это хитрый пример. ;)
Интересно, конечно...только вот я асм плохо знаю:) так что не знаю как понимать буду.
Вот про это интересно. В мсдене про это как то невнятно написано. Я так понимаю, при любом формате вызова, 4 регистра сохраняются, ESI , EDI , EBX , EBP. А как быть с остальными?
Вот, например, EFLASS. В нем что то остается неизменно? или нет?
Сохранять надо самому, обычно на стеке. Т.е. в начале функции сохраняешь все регистры, которые будешь использовать, а при выходе восстанавливаешь:
push ebp
push ebx
..........
pop ebx
pop ebx
Что касается специальных регистров (epi, efl), то их значения, на сколько мне известно, напрямую не сохранить, да и зачем?
2 Green:ну ты,зелёный,сказал-epi
Про флаги.
Использование оправдано только в тандеме с CLI/STI. Да и то, если после восстановления флагового регистра, не произойдет NMI. Сие действо усложняется в PM и при мультизадачности, ибо CPL :). Имхо, нет смысла.
Данная возможность полезна для принудительной работы с регистром EFLAGS флагов:
pop ax
...
push ax
popf
Хотя это и не принципиально, но флаги VM и RF сохраняются нулевыми.
Про PushAD/PopAD.
Эта команда только для 8-ми регистров общего назначения. Ни MMX (это понятно, они в сопроцессоре), ни XMM регистры она не сохраняет. Как, кстати, и EIP. Возвращаемое значение, обычно, хранится в (E)AX, и соответственно, после применения POPAD... :)
Конечно, можно сохранить возвращаемое значение в памяти, а после восстановления регистров поместить его в EAX, при этом получив 'пенальти' в несколько сотен тактов. Так что, для оперативной работы эти команды не подходят (к сожалению).
2)так будет короче(чем сохранять все используемые регистры)
На этот раз пример использования вполне рабочий. Можно скопировать, построить и запустить.
Если интересно, могу рассказать в чем его суть (что он делает). Это хитрый пример. ;)
Да, давненько это было, но Green очень интересный пример. В принципе понятно что осуществляется перехват, но расскажи пожалуста поподробней в чем суть и какая хитрость?
Хитрость не в перехвате, а в том, что перехватывается.
В примере производиться попытка загрузки user32.dll. Эта DLL относиться к так называемым KnownDLL и поэтому загружается она в процесс не с диска, как другие DLL, а делается её копия в память процесса с помощью NtOpenSection.
Т.о. если поставить хук на NtOpenFile, то можно увидеть, как открываются файлы обычных DLL при их загрузке, но открытия файла user32.dll не последует.
Однако, если перехватить NtOpenSection и вернуть STATUS_OBJECT_NAME_NOT_FOUND, то система попытается загрузить эту DLL с диска, как обычную.
Собственно, таким "обманом" и занимается пример.
Она обычно входит в стандартную поставку VC++ и находиться в папке PlatformSDK\Include.
У меня при компиляции выдает такие ошибки:
error C2146: syntax error : missing ';' before identifier 'handler'
error C2501: 'HookNtOpenFile::NTSTATUS' : missing storage-class or type specifiers
Первый пример у меня компилится нормально, а второй не хочет.
Перехват MessageBoxA тоже не выходит :confused:
На вызове setHandled() происходит Access Violation
Из-за чего не работает?
{
public:
bool hook() {
return HookApi<HookMessageBoxA>::hook("user32", "MessageBoxA");
}
int handler(HWND hWnd,const char *lpText,const char *lpCaption,unsigned int uType)
{
setHandled();
return 1;
}
};
int main()
{
HookMessageBoxA hookMessageBoxA;
bool flag=hookMessageBoxA.hook();
int i=MessageBoxA(NULL,"text","text",0);
return 0;
}
Кроме того, не компилируется, если не заменить в обьявлении базового класса protected на public
Сейчас, к сожалению, проверить не могу.
Вернусь в Питер, посмотрю подробнее.
Windows XP SP2
-------------
C++ Builder 6 выдает кучу ошибок в winntrnl.h
Твой пример там нормально компилируктся и работает.
winntrnl.h для него не нужен, нужно лишь:
#include <stddef.h>
#include "HookApi.h"
обрати книмание на stddef.h
это надо для offsetof
Третья версия базового класса для перехвата вызовов функций API из текущего процесса.
Взять можно здесь: http://sources.codenet.ru/download/2070/HookApi_v3_0.html
Класс позволяет перехватывать функции API и по необходимости вызывать в обработчике перехваченную функцию, либо возвращать управление без вызова перехваченной фцнкции.
Основное отличие от второй версии в способе передачи управления перехваченной функции API. Теперь если надо вызывать перехваченную функцию это делается с помощью вызова callOrgFunc с соотв. функции API параметрами.
Ещё одно изменение в том, что теперь экземпляру перехватчика указывается адрес перехватываемой ф-ции (в методе init), а не её имя.
Использовать класс очень просто. Наследуете от него класс для перехвата определенной функции API, определеяте в наследнике метод handler. Параметры этого метода (аргументы и возвращаемое значение) должны быть такие же, как у функции, которую перехватываем.
Внимание: базовый класс является шаблонным, наследоваться нужно от него, инстациированого дочерним классом (см. пример использования).
#include <windows.h>
#include <winternl.h>
#include <iostream>
using namespace std;
#include "HookApi.h"
class HookNtOpenFile :public HookApi<HookNtOpenFile>
{
public:
NTSTATUS handler(OUT PHANDLE FileHandle, IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes, OUT PIO_STATUS_BLOCK IoStatusBlock,
IN ULONG ShareAccess, IN ULONG OpenOptions)
{
wcout << "HookNtOpenFile: " << ObjectAttributes->ObjectName->Buffer << endl;
// Вызов перехваченной ф-ции API
return callOrgFunc(FileHandle, DesiredAccess, ObjectAttributes, IoStatusBlock,
ShareAccess, OpenOptions);
}
};
#define STATUS_OBJECT_NAME_NOT_FOUND ((NTSTATUS)0xC0000034L)
class HookNtOpenSection :public HookApi<HookNtOpenSection>
{
public:
NTSTATUS handler(OUT PHANDLE SectionHandle, IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes)
{
wcout << "NtOpenSection: " << ObjectAttributes->ObjectName->Buffer << endl;
if( !wcsicmp(ObjectAttributes->ObjectName->Buffer, L"USER32.DLL") ) {
cout << "BINGO!" << endl;
*SectionHandle = NULL;
// Возвращаем управление в вызывающий код без вызова в реальной API-функции.
return STATUS_OBJECT_NAME_NOT_FOUND;
}
// Вызов перехваченной ф-ции API
return callOrgFunc(SectionHandle, DesiredAccess, ObjectAttributes);
}
};
int main()
{
// Создание перехватчиков
HookNtOpenFile hookNtOpenFile;
HookNtOpenSection hookNtOpenSection;
// Инициализация перехватчиков адресами ф-ций API
HMODULE hDll = LoadLibrary("ntdll");
hookNtOpenFile.init( GetProcAddress(hDll, "NtOpenFile") );
hookNtOpenSection.init( GetProcAddress(hDll, "NtOpenSection") );
// Установка перехватчиков
hookNtOpenFile.hook();
hookNtOpenSection.hook();
// Проверяем, как работает :)
HMODULE nMod = LoadLibrary("user32.dll");
return 0;
}
Почему падает?
Дело может быть не в HookAPI, а в том, что ты неправильно написал обработчик.
вот мой класс:
class HookFindFirstFile :public HookApi<HookFindFirstFile>
{
public:
HANDLE handler(IN LPCTSTR lpFileName, OUT LPWIN32_FIND_DATA lpFindFileData)
{
//wcout << "HookFindFirstFile: " << lpFileName << endl;
// Вызов перехваченной ф-ции API
return callOrgFunc(lpFileName, lpFindFileData);
}
};
Под отладчиком вижу, что в handler еще не попадаю.
#include <iostream>
using namespace std;
#include "HookApi.h"
class HookFindFirstFile :public HookApi<HookFindFirstFile>
{
public:
HANDLE handler(IN LPCTSTR lpFileName, OUT LPWIN32_FIND_DATA lpFindFileData)
{
wcout << "HookFindFirstFile: " << lpFileName << endl;
// Вызов перехваченной ф-ции API
return callOrgFunc(lpFileName, lpFindFileData);
}
};
int main()
{
HookFindFirstFile hookFindFirstFile;
hookFindFirstFile.init(&FindFirstFile);
hookFindFirstFile.hook();
WIN32_FIND_DATA FindFileData;
HANDLE hFind = FindFirstFile("asd", &FindFileData);
return 0;
}
Если ты получаешь адрес ф-ции с помощью GetProcAddress, то учти, что FindFirstFile - это не ф-ция, а макрос, который раскрывается в FindFirstFileA или FindFirstFileW.
Полезно проверять полученный адрес прежде, чем передавать его в инициализатор хука:
if(addr == NULL) {
// ERROR
}
0013FF56 push ebp
0013FF57 mov ebp,esp
-> 0013FF59 mov ecx,13FF4Ch
0013FF5E jmp @ILT+790(HookFindFirstFile::handler) (0040131b)
А падает с каким cообщением?
Ты собираешь тот пример, что я привел постом выше?
Покажи трассировку от вызова FindFirstFile, до падения.
У меня это выглядит так:
004116D5 8B F4 mov esi,esp
004116D7 8D 85 7C FE FF FF lea eax,[ebp-184h]
004116DD 50 push eax
004116DE 68 FC 86 41 00 push offset string "asd" (4186FCh)
004116E3 FF 15 20 C3 41 00 call dword ptr [__imp__FindFirstFileA@8 (41C320h)]
_FindFirstFileA@8:
7C8137D9 E9 5B C7 91 83 jmp 0012FF39
0012FF39 B9 2C FF 12 00 mov ecx,12FF2Ch
0012FF3E E9 05 14 2E 00 jmp HookFindFirstFile::handler (411348h)
У меня дизассемблер выглядит так:
25: int main()
26: {
00411760 push ebp
00411761 mov ebp,esp
00411763 push 0FFFFFFFFh
00411765 push offset __ehhandler$_main (416F58h)
0041176A mov eax,dword ptr fs:[00000000h]
00411770 push eax
00411771 sub esp,254h
00411777 push ebx
00411778 push esi
00411779 push edi
0041177A lea edi,[ebp-260h]
00411780 mov ecx,95h
00411785 mov eax,0CCCCCCCCh
0041178A rep stos dword ptr es:[edi]
0041178C mov eax,dword ptr [___security_cookie (41C048h)]
00411791 xor eax,ebp
00411793 mov dword ptr [ebp-10h],eax
00411796 push eax
00411797 lea eax,[ebp-0Ch]
0041179A mov dword ptr fs:[00000000h],eax
27: HookFindFirstFile hookFindFirstFile;
004117A0 lea ecx,[ebp-3Ch]
004117A3 call HookFindFirstFile::HookFindFirstFile (411014h)
004117A8 mov dword ptr [ebp-4],0
28: hookFindFirstFile.init(&FindFirstFile);
004117AF mov eax,dword ptr [__imp__FindFirstFileA@8 (41D2D4h)]
004117B4 push eax
004117B5 lea ecx,[ebp-3Ch]
004117B8 call HookApiBase::init (411032h)
29: hookFindFirstFile.hook();
004117BD lea ecx,[ebp-3Ch]
004117C0 call HookApi<HookFindFirstFile>::hook (4112ADh)
30:
31: WIN32_FIND_DATA FindFileData;
32: HANDLE hFind = FindFirstFile("asd", &FindFileData);
004117C5 mov esi,esp
004117C7 lea eax,[ebp-184h]
004117CD push eax
004117CE push offset string "asd" (419800h)
004117D3 call dword ptr [__imp__FindFirstFileA@8 (41D2D4h)]
004117D9 cmp esi,esp
004117DB call @ILT+675(__RTC_CheckEsp) (4112A8h)
004117E0 mov dword ptr [ebp-190h],eax
33:
34: return 0;
004117E6 mov dword ptr [ebp-25Ch],0
004117F0 mov dword ptr [ebp-4],0FFFFFFFFh
004117F7 lea ecx,[ebp-3Ch]
004117FA call HookFindFirstFile::~HookFindFirstFile (4112C6h)
004117FF mov eax,dword ptr [ebp-25Ch]
35: }
_FindFirstFileA@8:
7C813559 jmp 0013FF39
0013FF39 mov ecx,13FF2Ch
0013FF3E jmp HookFindFirstFile::handler (4112CBh)
004117D3 call dword ptr [__imp__FindFirstFileA@8 (41D2D4h)]
или выложи exe + pdb
Это какая-то наведенная ошибка.
С каким сообщением падает?
Выложи exe + pdb
И смотри ещё странность: в одном посте ты приводишь код
-> 0013FF59 mov ecx,13FF4Ch
0013FF5E jmp @ILT+790(HookFindFirstFile::handler) (0040131b)
а в другом:
0013FF39 mov ecx,13FF2Ch
0013FF3E jmp HookFindFirstFile::handler (4112CBh)
что изменилось между сборками?
Пересобери с /MTd
Построй релиз с /MTd и скажи, с каким сообщением падает?
---------------------------
HookApiTest.exe - Ошибка приложения
---------------------------
Инструкция по адресу "0x0012fcdd" обратилась к памяти по адресу "0x0012fcdd". Память не может быть "written".
Это не /MTd
Exe с таким ключом должен весить ~500k
Посмотри Project -> Properties -> C/C++ -> Code Generation -> Runtime Lbrary -> Multi-threaded Debug (/MTd)