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

  • Tutorial
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)\madCollection\madCodeHook\Dll. В 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, запускаем инжектор, в браузере переходим на Хабр. Результат:



Выводы


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

В общем, библиотека madCodeHook оставляет хорошее впечатление, можно пользоваться.
Инфопульс Украина
96.91
Creating Value, Delivering Excellence
Share post

Comments 12

    +2
    Минималистичный код, да. Но так и не понял, почему не использовать WinAPI?
      0
      В каком моменте и как именно?
      0
      Хотел спросить «А что насчет совместимости с Win8», но зашел на сайт проэкта и прочитал:
      madCodeHook offers everything you need to hook code (mostly APIs) in all 32 and 64 bit Windows operating systems from Windows 95 to Windows 8.1
        0
        Во втором абзаце написано об этом.
        0
        OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
        


        Неужто для того, чтобы заинжектить код в удалённый процесс, необходимо открыть этот самый процесс со всеми возможными привилегиями?
          0
          Ну смотрите, нам точно нужны PROCESS_VM_WRITE и PROCESS_VM_READ (чтобы забросить туда свой код и иметь доступ к нему). SYNCHRONIZE тоже нужен, поскольку неплохо бы знать завершен процесс или нет. PROCESS_VM_OPERATION нужен чтобы иметь возможность сделать VirtualProtectEx (разрешить выполнение кода в определённом блоке памяти). PROCESS_CREATE_THREAD тоже нужен, ведь создание в удалённом процессе своего потока — нужная в 90% случаев функциональность.

          В общем, из указанных тут 14 прав штук 10 видятся мне необходимыми. Какой при этом толк отключать оставшиеся 4?
            0
            Какой при этом толк отключать оставшиеся 4?

            Толк в том, что однажды обязательно наступит момент, когда Ваш код перестанет работать из-за того, что система не выдала ему привилегии, которые вообще не нужны для выполнения поставленной задачи. Это плохой тон программирования.
              0
              В целом, Вы, конечно, правы — надо просить только то, что нужно. Но вот в чём беда — кода самой библиотеки madCodeHook я не видел, поэтому точно знать, какие права ей нужны, я не могу. А в официальном примере из документации — процесс открывается именно так.
                0
                Тогда, перед использованием библиотеки, было бы логично задать вопрос о необходимых привилегиях автору. Я бы так и сделал, по крайней мере. Не юзать же кота в мешке?
          0
          Кстати, для поиграться/поизучать можно потрогать MinHook(опенсорс, лицензия BSD, поддержка x84/x64 кода), немного пользовался ею — остался доволен.
            0
            Я правильно понимаю, что это хукинг функций в текущем процессе, или инжект в другие там тоже есть?
              0
              В текущем процессе, но никто не мешает запулить свою дллку в процесс чужой, дело 30 строчек кода, про это ещё ms-rem писал.

          Only users with full accounts can post comments. Log in, please.