Хуки — это просто (часть 3)

image

Как-то так получилось, что я написал на Хабре уже несколько статей о библиотеках для хуков. Первая была об общих принципах и реализации на базе 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)

Удачи с библиотекой!

© Habrahabr.ru