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

в 10:42, , рубрики: Программирование, системное программирование, хуки, метки:

image

Некоторое время назад я писал вводную статью о хуках (что это, зачем нужно, Hello world). Статья задумывалась простой, минималистичной и, вроде бы, такой и получилась. Единственный упрёк, который я услышал в комментариях — «Зачем же брать библиотеку Microsoft Detours, которая для коммерческого использования стоит 10 000$ ?». Замечание справедливое. В этой статье я приведу тот же пример с использованием другой библиотеки ценой примерно в 20 раз меньше (что уже вполне себе в рамках разумного) — madCodeHook.

Для лучшего понимания данной статьи рекомендуется сначала прочитать первую часть.

madCodeHook

Библиотека с богатым прошлым. Первая её версия вышла в далёком 2000-ом году, предназначалась для использования под Delphi и мало что умела. Тем ни менее за последующие годы автор весьма неплохо её развивал: сделал SDK для С++, внедрил поддержку 64-битных систем, всех версий Windows от 9х до Win 8.1, реализовал драйвер для внедрения хуков во все новосозданные процессы, ну и вообще достаточно активно работал над проектом (апдейты и сейчас выходят регулярно). По ходу дела из-из нежелания быть подмогой вирусописателям библиотека потеряла бесплатную версию, однако цены, начинающиеся от 349 евро делают её реальной альтернативой и нереально дорогой Microsoft Detours, и малоудобной mhook, и нестабильной (по моему опыту) EasyHook.

Ограничения evaluation-версии

  • Нет исходников
  • Нет возможности статической линковки
  • Должно быть вручную запущено входящее в комплект приложение mchEvaluation.exe
  • Библиотека madCHook.dll должна быть предварительно скопирована в System32

В общем, для изучения — ничего критически мешающего. Для коммерческого продукта в любом случае нужно покупать лицензию.

Вспоминаем нашу задачу

В первой части статьи мы использовали хуки для того, чтобы заставить браузер Mozilla Firefox при заходе на Хабр писать в своём заголовке «Привет!». Во-первых, мне лень придумывать новую задачу, а во-вторых даже правильнее будет реализовать снова тоже самое на базе другой библиотеки — можно сравнить скорость разработки, объём и сложность кода. Кроме того, в первой части мы уже разобрались куда и какие хуки нужно вешать, так что сэкономим на этом немного времени.

Практика

1. Качаем последнюю версию madCodeHook, устанавливаем.
2. Создаём в Visual Studio (я использую VS 2010, но вы можете взять и другую) solution c двумя проектами. Первый — библиотека с кодом хука, которую мы будем инжектить в процесс браузера. Второй — приложение инжектора, его задача — забросить библиотеку в адресное пространство браузера.

  • Для создания первого проекта: File->New->Project. Тип Visual C++ -> Win32 -> Win32 Project. В диалоге создания проекта указываем тип «Dll»
  • Для создания второго проекта: File->Add->New Project. Тип Visual C++ -> Win32 -> Win32 Console Application.

4. Подкидываем в наши проекты заголовочный файл и lib-файл из SDK madCodeHook. При установке библиотеки по-умолчанию они находятся по адресу C:Program Files (x86)madCollectionmadCodeHookDll. В evaluation-версии нам доступна только динамическая линковка, так что забираем файлы madCHook — dynamic.h и madCHook — dynamic — microsoft.lib, можно их для краткости переименовать в madCHook.h и madCHook.lib.

5. Пишем код. Ключевые моменты:

Код инжектора

#include "stdafx.h"
#include <conio.h>
#include "windows.h"
#include "madCHook.h"
#include <tlhelp32.h>

HANDLE GetProcessByName(PCWSTR name)
{
	DWORD pid = 0;

	HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
	PROCESSENTRY32 process;
	ZeroMemory(&process, sizeof(process));
	process.dwSize = sizeof(process);

	if (Process32First(snapshot, &process))
	{
		do
		{
			if (_wcsicmp(process.szExeFile, name) == 0)
			{
				pid = process.th32ProcessID;
				break;
			}
		} while(Process32Next(snapshot, &process));
	}

	CloseHandle(snapshot);

	if (pid != 0)
		return OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);

	return NULL;
}

int _tmain(int argc, _TCHAR* argv[])
{
	InjectLibraryW((DWORD)GetProcessByName(L"firefox.exe"), L"HookLib.dll");
	_getch();
	UninjectLibraryW((DWORD)GetProcessByName(L"firefox.exe"), L"HookLib.dll");
}

Код библиотеки с хуком

#include "stdafx.h"
#include "madCHook.h"

LRESULT (WINAPI * TrueSendMessageW)(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) = SendMessageW;

__declspec(dllexport) LRESULT WINAPI MySendMessageW(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
	if (Msg == WM_SETTEXT && wcsstr((LPCTSTR)lParam, L"Хабрахабр"NULL)
		return TrueSendMessageW(hWnd, Msg, wParam, (LPARAM)L"Привет!");

	return TrueSendMessageW(hWnd, Msg, wParam, lParam);
}

BOOL APIENTRY DllMain(HMODULE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved)
{
	switch (ul_reason_for_call)
	{
		case DLL_PROCESS_ATTACH:
			HookAPI("User32.dll", "SendMessageW", MySendMessageW, (PVOID*) &TrueSendMessageW);
	}
	return TRUE;
}

Готовый проект на Гитхабе

6. Компилируем, запускаем Firefox, запускаем mchEvaluation.exe, запускаем инжектор, в браузере переходим на Хабр. Результат:

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

Выводы

В плане удобства работы с evaluation-версией madCodeHook немного проигрывает Microsoft Detours, полнофункциональные версии в этом плане примерно равны. Кода писать madCodeHook требует даже меньше. В составе madCodeHook есть драйвер для внедрения библиотек на общесистемном уровне (во все существующие и новые процессы), в Detours эту задачу нужно решать собственным сервисом или драйвером. По скорости и стабильности библиотеки показались мне аналогичными. madCodeHook не вызывает ощущения «энтерпрайзности», как продукт Microsoft, что одновременно и хорошо и плохо: автора легко можно поймать на форуме (что хорошо), но там же написано «я могу уйти в отпуск на 6 недель в любое время года» (что плохо). Сообщество madCodeHook сосредоточено на их форуме, сообщество Microsoft Detours как-то раскидано по Stackoverflow, wasm.ru, форумам MSDN и ощущения целостности не создаёт.

В общем, библиотека madCodeHook оставляет хорошее впечатление, можно пользоваться.

Автор: tangro

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js