Что и зачем

Существует ряд ситуаций, когда это имеет смысл. От полного контроля памяти, до вынужденной необходимости. К примеру, одна из возможных ситуаций:

  1. Мы используем alignment аллокаторы.

  2. Мы перегружаем какой-то класс и данные от нашей 3rd зависимости.

  3. 3rd зависимость не использует alignment аллокаторы.

Ищем new/delete/malloc/free

Если нам повезёт, то 3rd может иметь свои new/delete операторы, которые были экспортированы. Хотя везением это сложно назвать, т.к. придётся ковыряться в дизассемблированном коде. Если вы решили не заморачиваться с поиском адресов, то этот шаг можно пропустить.

Немного помучавшись, можно прийти к следующему:

Затем, нам нужен RVA адрес функции.

Для начала получаем адрес dll в памяти:

После чего вычисляем адрес нашего символа:

00007FFC9BB7D760 - Symbol
00007FFC9BAF0000 - Base adress

// Без адреса входа
B7D760 
AF0000

D760 // Раз адрес кончается на 0000, то D760 просто запоминаем
0000 // Те самые ноли 

// Получаем первые два символа адреса 
b7 - af = 08

// Конечный итог
08D760

Осталось только достать Ordinal. Для этого подойдёт тот же CFF Explorer:

Видим заветные 5CD. Они нам и нужны.

Перегружаем символ

Теперь переходим к более интересной части. Итак, для начала нам потребуется библиотека для создание хуков в рантайме. К примеру, minhook.

Далее пишем базовый код:

using OperatorNewFunc = void* (*)(size_t size);


struct InitialHook
{
	InitialHook()
	{
		MH_STATUS ResultCode = MH_Initialize();

        // Загружаем наш символ из модуля 
		HMODULE hMFC = GetModuleHandle("mfc140.dll");

        // Загружаем ordinal 
		OperatorNewFunc hOperatorNew = (OperatorNewFunc)GetProcAddress(hMFC, MAKEINTRESOURCE(0x5CD));
		
		ResultCode = MH_CreateHook(hOperatorNew, /* Вставьте свой каллбек */, nullptr);
		MH_EnableHook(hOperatorNew);
	}
};

Однако, если же вы решили не искать адрес, то просто достаём malloc из ucrtbase.dll


	HMODULE hCRT = GetModuleHandle("ucrtbase.dll");
	OperatorNewFunc hFreeFunc = (OperatorNewFunc)GetProcAddress(hCRT, "malloc");

Вторым аргументом для MH_CreateHook передаём нашу новую функцию аллокации памяти.

А теперь самое важно. Нужно заставить компилятор выполнять инициализацию нашей структуры раньше остальных глобальных функций.

// Регистрируем секцию
#pragma section(".Hook",read)

using OperatorNewFunc = void* (*)(size_t size);

struct InitialHook
{
	InitialHook()
	{
		MH_STATUS ResultCode = MH_Initialize();

        // Загружаем наш символ из модуля 
		HMODULE hMFC = GetModuleHandle("mfc140.dll");
		OperatorNewFunc hOperatorNew = (OperatorNewFunc)GetProcAddress(hMFC, MAKEINTRESOURCE(0x5CD));
		
		ResultCode = MH_CreateHook(hOperatorNew, /* Вставьте свой каллбек */, nullptr);
		MH_EnableHook(hOperatorNew);
	}
};

// Определям символ нашей структуры 
#pragma init_seg(lib)
__declspec(allocate(".Hook"))
static const InitialHook HookInit;

Более подробно об этом можно почитать тут.

Комментарии (3)


  1. Apoheliy
    07.12.2022 19:23
    +1

    Вообще, чтобы проделывать такое в продакшене нужно очень любить выравнивание. Особенно, с учётом того, что если 3rd аллоцирует массив, то размер отдельных элементов будет (в общем случае) не выровнен и ... всё пропадает втуне.

    Другое дело, что так можно приколотить собственный (не опирающийся на malloc) аллокатор/кучу. И вот тогда:

    А теперь самое важно

    переопределяя malloc желательно и free / realloc направить туда же.

    Хотя, в целом, идея достаточно странноватая. Полагаю, здесь можно было бы поставить тег ненормальное программирование.


    1. ForserX Автор
      07.12.2022 20:26

      Хотя, в целом, идея достаточно странноватая. Полагаю, здесь можно было бы поставить тег ненормальное программирование.

      О, точно. Я забыл о теге "ненормальное программирование "

      переопределяя malloc желательно и free / realloc направить туда же.

      Я думал, это подразумевается.


    1. ForserX Автор
      07.12.2022 20:42

      А вообще, данная статья больше будет полезна начинающим писателем читов. Тут и хуки функций и базовая работа с данными дизасма в студии.