Подписывайтесь на наш Telegram и не пропускайте важные новости! Перейти

Гайд [Сурс] C&C Generals: Zero Hour — Обход GenTool (VirtualProtect & Checksum)

Sloppy
Начинающий
Начинающий
Статус
Оффлайн
Регистрация
13 Фев 2026
Сообщения
507
Реакции
13
GenTool — это база для любого, кто играет в Generals или Zero Hour на современных системах, но для разработчика софта это лишний геморрой. Если вы пытались патчить .text секцию или вешать хуки напрямую, то наверняка ловили либо блокировку изменения прав доступа, либо характерный черный экран через пару минут игры.

Разберем, как GenTool мешает нам жить и как его приземлить через кастомный VirtualProtect и отстрел проверочных потоков.

Часть 1: Обходим хук VirtualProtect

GenTool хукает VirtualProtect и ZwProtectVirtualMemory, предотвращая любые попытки изменить атрибуты страниц памяти. Если прогнать вызов через отладчик, станет ясно, что исполнение улетает в подпрограмму d3d8.dll.

Технически VirtualProtect — это обертка над ZwProtectVirtualMemory. Античит вешает хуки на обе функции. Решение: пишем свою реализацию, которая находит оригинальный адрес системного вызова внутри самого хука GenTool и прыгает сразу туда, игнорируя проверку.

Код:
Expand Collapse Copy
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;
}

Чтобы не переписывать весь проект, просто юзаем макрос в общем хедере:
Код:
Expand Collapse Copy
#define VirtualProtect TrueVirtualProtect

Часть 2: Чексумма движка и черный экран

Даже если вы успешно пропатчили память, GenTool рано или поздно выкинет черный экран. Это срабатывает фоновая проверка контрольных сумм. Она запускается в отдельных потоках через случайные интервалы времени.

Самое забавное, что эти потоки никак не защищены. Мы можем просто найти их по сигнатуре и завершить до того, как они успеют что-то проверить.

Код:
Expand Collapse Copy
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"
);

Алгоритм действий:

  1. Снимаем снапшот всех потоков процесса.
  2. Ищем адрес функции проверки в d3d8.dll по паттерну.
  3. Перебираем потоки и через NtQueryInformationThread запрашиваем ThreadQuerySetWin32StartAddress.
  4. Если адрес начала потока совпадает с нашей функцией — вызываем TerminateThread.

После этого можно спокойно вешать любые хуки и патчить байты в .text секции, никаких санкций от GenTool не последует. Главное — проводить зачистку сразу при инжекте dll.

Метод грубый, но для этой игры работает безотказно.
 
Назад
Сверху Снизу