Хуки — это просто (часть 3)
Как-то так получилось, что я написал на Хабре уже несколько статей о библиотеках для хуков. Первая была об общих принципах и реализации на базе Detours, вторая — о более дешевой (но не менее функциональной) библиотеке madCodeHook. Сегодня я расскажу об ещё одном варианте — библиотеке Deviare от компании Nektra. «Ещё одна точно такая же библиотека для хуков?» — спросите вы. «Такая же, да не такая» — отвечу я. У Deviare есть несколько особенностей, отличающих её и от Detours и от madCodeHook и делающей её в некоторых случаях намного более полезной.
Библиотека распространяется по двойной лицензии — GPL\коммерческая. Для опенсорсных проектов она живёт на GitHub, а для тех, кому нужна поддержка и право внедрять код библиотеки в закрытые продукты — добро пожаловать на официальный сайт.То есть да, вы можете прямо взять и почитать код библиотеки, забрать её с GitHub и внедрить в своём опенсорс-проекте прямо сейчас не заплатив никому ни копейки. Чудо, не доступное для Detours и madCodeHook. Нужно сказать, что проект долгое время был закрытым, но потом Nektra решили, что надо быть как-то ближе к людям.На базе библиотеки сделан мощный продукт SpyStudio Перед тем, как писать какой-то код для хуков, можно просто запустить данную утилиту и посмотреть, на что она способна. На базе Detours и madCodeHook тоже, конечно, много всякого сделано, но вот аналогичного ПО от авторов самих библиотек как-то нет.Авторы накидали в своём блоге кучу практических примеров использования хуков Тут вам и хуки на SQL Server для предотвращения SQL-инъекций, и запись видео из игр, рисуемых через DirectX, и читы, и изменения скорости работы плеера в браузере. Читаешь — и сразу становится ясно, для чего можно использовать хуки.Кроме классического С++ предлагается COM-компонент, который позволяет ставить хуки откуда угодно (C#, Python, VB, Delphi и т.д.) Да, наконец-то можно начать использовать хуки, не разбираясь в том, что такое указатель на указатель на массив указателей.Хуки можно вешать не только на нативные функции или методы COM-объектов, но и на .NET-методы Данная фича кажется мне не такой уж убойной, поскольку в .NET и так вроде бы неплохо с метаинформацией и рефлексией, а значит кому надо было — умел это делать и сам. Но так, в качестве вишенки на пироге — почему бы и нет.Библиотека давно пережила все «детские» болезни Сегодня её используют компании из Fortune 500, а партнёрство с VmWare позволяет делать, например, портабельные версии установленных приложений.
Я не буду тут расписывать километры кода (глупо это делать для опенсорсной библиотеки с примерами на GitHub). Ну чисто так, обзорно.
Инъекция своей библиотеки в чужой процесс:
#include "NktHookLib.h"
NktHookLibHelpers::InjectDllByPidW(dwPid, L"myDll.dll");
Установка хука на функцию:
#include "NktHookLib.h"
typedef int (WINAPI *lpfnMessageBoxW)(__in_opt HWND hWnd, __in_opt LPCWSTR lpText, __in_opt LPCWSTR lpCaption, __in UINT uType);
static int WINAPI Hooked_MessageBoxW(__in_opt HWND hWnd, __in_opt LPCWSTR lpText, __in_opt LPCWSTR lpCaption, __in UINT uType);
static struct {
SIZE_T nHookId;
lpfnMessageBoxW fnMessageBoxW;
} sMessageBoxW_Hook = { 0, NULL };
int WinMainCRTStartup()
{
CNktHookLib cHookMgr;
HINSTANCE hUser32Dll;
LPVOID fnOrigMessageBoxW;
DWORD dwOsErr;
hUser32Dll = NktHookLibHelpers::GetModuleBaseAddress(L"user32.dll");
if (hUser32Dll == NULL) {
::MessageBoxW(0, L"Error: Cannot get handle of user32.dll", L"HookTest", MB_OK|MB_ICONERROR);
return 0;
}
fnOrigMessageBoxW = NktHookLibHelpers::GetProcedureAddress(hUser32Dll, "MessageBoxW");
if (fnOrigMessageBoxW == NULL) {
::MessageBoxW(0, L"Error: Cannot get address of MessageBoxW", L"HookTest", MB_OK|MB_ICONERROR);
return 0;
}
dwOsErr = cHookMgr.Hook(&(sMessageBoxW_Hook.nHookId), (LPVOID*)&(sMessageBoxW_Hook.fnMessageBoxW),
fnOrigMessageBoxW, Hooked_MessageBoxW,
NKTHOOKLIB_DisallowReentrancy);
::MessageBoxW(0, L"This should be hooked", L"HookTest", MB_OK);
dwOsErr = cHookMgr.Unhook(sMessageBoxW_Hook.nHookId);
::MessageBoxW(0, L"This should NOT be hooked", L"HookTest", MB_OK);
return 0;
}
static int WINAPI Hooked_MessageBoxW(__in_opt HWND hWnd, __in_opt LPCWSTR lpText, __in_opt LPCWSTR lpCaption, __in UINT uType)
{
return ::MessageBoxW(hWnd, lpText, L"HOOKED!!!", uType);
}
А вот как-то так из Python запускается Notepad, в котором вешается хук на функцию CreateFileW:
import win32com.client
import ctypes, sys
from EventHandlers import NktSpyMgrEvents
from AuxFunctions import *
if sys.version_info.major < 3:
warnings.warn("Need Python 3.0 for this program to run", RuntimeWarning)
sys.exit(0)
win32com.client.pythoncom.CoInitialize()
spyManager = win32com.client.DispatchWithEvents("DeviareCOM.NktSpyMgr", NktSpyMgrEvents)
result = spyManager.Initialize()
if not result == 0:
print ("ERROR: Could not initialize the SpyManager. Error code: %d" % (result))
sys.exit(0)
notepad = StartNotepadAndHook(spyManager)
MessageBox = ctypes.windll.user32.MessageBoxW
MessageBox(None, "Press OK to end the demo.", "Deviare Python Demo", 0)
notepad.Terminate(0)
Удачи с библиотекой!