Что и зачем
Существует ряд ситуаций, когда это имеет смысл. От полного контроля памяти, до вынужденной необходимости. К примеру, одна из возможных ситуаций:
Мы используем alignment аллокаторы.
Мы перегружаем какой-то класс и данные от нашей 3rd зависимости.
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;
Более подробно об этом можно почитать тут.
Apoheliy
Вообще, чтобы проделывать такое в продакшене нужно очень любить выравнивание. Особенно, с учётом того, что если 3rd аллоцирует массив, то размер отдельных элементов будет (в общем случае) не выровнен и ... всё пропадает втуне.
Другое дело, что так можно приколотить собственный (не опирающийся на malloc) аллокатор/кучу. И вот тогда:
переопределяя malloc желательно и free / realloc направить туда же.
Хотя, в целом, идея достаточно странноватая. Полагаю, здесь можно было бы поставить тег ненормальное программирование.
ForserX Автор
О, точно. Я забыл о теге "ненормальное программирование "
Я думал, это подразумевается.
ForserX Автор
А вообще, данная статья больше будет полезна начинающим писателем читов. Тут и хуки функций и базовая работа с данными дизасма в студии.