- Статус
- Оффлайн
- Регистрация
- 13 Фев 2026
- Сообщения
- 507
- Реакции
- 13
GenTool — это база для любого, кто играет в Generals или Zero Hour на современных системах, но для разработчика софта это лишний геморрой. Если вы пытались патчить .text секцию или вешать хуки напрямую, то наверняка ловили либо блокировку изменения прав доступа, либо характерный черный экран через пару минут игры.
Разберем, как GenTool мешает нам жить и как его приземлить через кастомный VirtualProtect и отстрел проверочных потоков.
Часть 1: Обходим хук VirtualProtect
GenTool хукает VirtualProtect и ZwProtectVirtualMemory, предотвращая любые попытки изменить атрибуты страниц памяти. Если прогнать вызов через отладчик, станет ясно, что исполнение улетает в подпрограмму d3d8.dll.
Технически VirtualProtect — это обертка над ZwProtectVirtualMemory. Античит вешает хуки на обе функции. Решение: пишем свою реализацию, которая находит оригинальный адрес системного вызова внутри самого хука GenTool и прыгает сразу туда, игнорируя проверку.
Чтобы не переписывать весь проект, просто юзаем макрос в общем хедере:
Часть 2: Чексумма движка и черный экран
Даже если вы успешно пропатчили память, GenTool рано или поздно выкинет черный экран. Это срабатывает фоновая проверка контрольных сумм. Она запускается в отдельных потоках через случайные интервалы времени.
Самое забавное, что эти потоки никак не защищены. Мы можем просто найти их по сигнатуре и завершить до того, как они успеют что-то проверить.
Алгоритм действий:
После этого можно спокойно вешать любые хуки и патчить байты в .text секции, никаких санкций от GenTool не последует. Главное — проводить зачистку сразу при инжекте dll.
Метод грубый, но для этой игры работает безотказно.
Разберем, как GenTool мешает нам жить и как его приземлить через кастомный VirtualProtect и отстрел проверочных потоков.
Часть 1: Обходим хук VirtualProtect
GenTool хукает VirtualProtect и ZwProtectVirtualMemory, предотвращая любые попытки изменить атрибуты страниц памяти. Если прогнать вызов через отладчик, станет ясно, что исполнение улетает в подпрограмму d3d8.dll.
Технически VirtualProtect — это обертка над ZwProtectVirtualMemory. Античит вешает хуки на обе функции. Решение: пишем свою реализацию, которая находит оригинальный адрес системного вызова внутри самого хука GenTool и прыгает сразу туда, игнорируя проверку.
Код:
using ZwProtectVirtualMemory_t = NTSTATUS(__stdcall*)(HANDLE, PVOID*, PULONG, ULONG, PULONG);
BOOL TrueVirtualProtect(LPVOID base, SIZE_T amount, DWORD newProtect, PDWORD oldProtect)
{
static auto ZwProtectVirtualMemory =
reinterpret_cast<ZwProtectVirtualMemory_t>(GetProcAddress(LoadLibraryA("ntdll.dll"), "ZwProtectVirtualMemory"));
if (ZwProtectVirtualMemory == NULL) return FALSE;
static ZwProtectVirtualMemory_t OriginalZwProtectVirtualMemory = NULL;
if (OriginalZwProtectVirtualMemory == NULL)
{
// Проверяем, наложил ли GenTool свой прыжок (0xE9)
if (*reinterpret_cast<uint8_t*>(ZwProtectVirtualMemory) == 0xE9)
{
uintptr_t ZwProtectVirtualMemoryAddress = reinterpret_cast<uintptr_t>(ZwProtectVirtualMemory);
int relativeJumpOffset = *reinterpret_cast<int*>(ZwProtectVirtualMemoryAddress + 1);
int absoluteJumpOffset = relativeJumpOffset + ZwProtectVirtualMemoryAddress + sizeof(uint8_t) + sizeof(uintptr_t);
// Вытаскиваем адрес, куда GenTool сам прыгает в конце своего хука
OriginalZwProtectVirtualMemory = **reinterpret_cast<ZwProtectVirtualMemory_t**>(absoluteJumpOffset + 130);
}
else
{
OriginalZwProtectVirtualMemory = ZwProtectVirtualMemory;
}
}
return OriginalZwProtectVirtualMemory(GetCurrentProcess(), &base, &amount, newProtect, oldProtect) == STATUS_SUCCESS;
}
Чтобы не переписывать весь проект, просто юзаем макрос в общем хедере:
Код:
#define VirtualProtect TrueVirtualProtect
Часть 2: Чексумма движка и черный экран
Даже если вы успешно пропатчили память, GenTool рано или поздно выкинет черный экран. Это срабатывает фоновая проверка контрольных сумм. Она запускается в отдельных потоках через случайные интервалы времени.
Самое забавное, что эти потоки никак не защищены. Мы можем просто найти их по сигнатуре и завершить до того, как они успеют что-то проверить.
Код:
Pattern patterns::gentool::ChecksumThread = Pattern(
"\xA1\x00\x00\x00\x00\x33\xC4\x50\x8D\x44\x24\x18\x64\xA3\x00\x00\x00\x00\x00\x00\x00\x00\x8B",
"x????xxxxxxxxx????????x"
);
Алгоритм действий:
- Снимаем снапшот всех потоков процесса.
- Ищем адрес функции проверки в d3d8.dll по паттерну.
- Перебираем потоки и через NtQueryInformationThread запрашиваем ThreadQuerySetWin32StartAddress.
- Если адрес начала потока совпадает с нашей функцией — вызываем TerminateThread.
После этого можно спокойно вешать любые хуки и патчить байты в .text секции, никаких санкций от GenTool не последует. Главное — проводить зачистку сразу при инжекте dll.
Метод грубый, но для этой игры работает безотказно.