- Статус
- Оффлайн
- Регистрация
- 13 Фев 2026
- Сообщения
- 635
- Реакции
- 17
Задолбали проверки Secure Boot в современных античитах?
Когда античит требует включить Secure Boot (привет, Vanguard и свежие апдейты Faceit), большинство идет крутить настройки BIOS. Но если вы пишете свой драйвер или маппер, вешать хуки на системные вызовы — это слишком громко и чревато быстрым детектом по сигнатурам.
Нашел интересный метод прямой манипуляции переменными в ядре. Суть в том, чтобы пропатчить флаги в CI.dll и подменить кэшированные данные в hal.dll без использования стандартных техник перехвата.
Что внутри реализации:
Технические нюансы:
Паттерн 83 0D для CI — это классика, но учитывайте, что в разных билдах Windows инструкции могут меняться. Обязательно проверяйте смещения перед тем, как кастить указатель к ULONG. Прямая запись в ciOpt и секции HAL может триггернуть PatchGuard (BSOD CRITICAL_STRUCTURE_CORRUPTION), если делать это в неподходящий момент или на системах с активным HVCI.
Для полноценного обхода сейчас одного этого мало (нужно еще возиться с TPM и имитировать состояние реестра для конкретного процесса античита), но как кусок тех-базы для своего проекта метод отличный.
Кто как сейчас борется с проверками на Windows 11, делитесь идеями по обходу HVCI и TPM в комментариях.
Когда античит требует включить Secure Boot (привет, Vanguard и свежие апдейты Faceit), большинство идет крутить настройки BIOS. Но если вы пишете свой драйвер или маппер, вешать хуки на системные вызовы — это слишком громко и чревато быстрым детектом по сигнатурам.
Нашел интересный метод прямой манипуляции переменными в ядре. Суть в том, чтобы пропатчить флаги в CI.dll и подменить кэшированные данные в hal.dll без использования стандартных техник перехвата.
Что внутри реализации:
- Скан CI.dll: ищем глобальную переменную g_CiOptions. Код находит инструкцию через паттерн, резолвит смещение относительно RIP и ручками выставляет бит включенной проверки целостности (CI), одновременно отключая Test Signing.
- Реестр: В коде прописан проброс стандартных ключей в SecureBoot\State и CI\Protected. Это база, которую чекают простые АС через юзермод.
- Скан секций hal.dll: Самое интересное. Код проходит по секциям инициализированных данных HAL, ищет Unicode-строку "SecureBoot" и патчит связанные с ней значения EFI-переменных прямо в памяти.
Код:
// CI.dll kernel variable + registry
inline void SpoofSecureBoot()
{
ULONG ciSize = 0;
PVOID ciBase = GetKernelModule("CI.dll", &ciSize);
if (ciBase && ciSize)
{
// CiInitialize references g_CiOptions via:
// "or dword ptr [rip+disp32], imm8" → 83 0D XX XX XX XX YY
// We find this pattern and resolve the global variable.
UCHAR pat[] = { 0x83, 0x0D };
char msk[] = "xx";
PUCHAR found = PatternScan(ciBase, ciSize, pat, msk, 2);
if (found && MmIsAddressValid(found))
{
// Instruction: 83 0D [disp32] [imm8] = 7 bytes total
if (MmIsAddressValid(found + 2) && MmIsAddressValid(found + 5))
{
LONG disp = *(LONG*)(found + 2);
PUCHAR target = found + 7 + disp; // RIP after 7-byte instruction
if (MmIsAddressValid(target) && MmIsAddressValid(target + 3))
{
ULONG* ciOpt = (ULONG*)target;
// Bit 0 = CI enabled, bit 3 = test signing
// Set CI enabled, clear test signing
*ciOpt = (*ciOpt | 0x1) & ~0x8;
}
}
}
}
// ── Registry Layer ───────────────────────────────────────────────
RegSetDw(L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet"
L"\\Control\\SecureBoot\\State", L"UEFISecureBootEnabled", 1);
RegSetDw(L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet"
L"\\Control\\CI\\Protected", L"Licensed", 1);
RegSetDw(L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet"
L"\\Control\\CI", L"UMCIAuditMode", 0);
}
// HAL module data section scan
inline void SpoofEfiVars()
{
ULONG halSize = 0;
PVOID halBase = GetKernelModule("hal.dll", &halSize);
if (!halBase || !halSize) return;
// Scan hal.dll data sections for cached "SecureBoot" variable
PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)halBase;
if (!MmIsAddressValid(dos) || dos->e_magic != IMAGE_DOS_SIGNATURE) return;
PIMAGE_NT_HEADERS64 nt = (PIMAGE_NT_HEADERS64)((PBYTE)halBase + dos->e_lfanew);
if (!MmIsAddressValid(nt) || nt->Signature != IMAGE_NT_SIGNATURE) return;
PIMAGE_SECTION_HEADER sec = IMAGE_FIRST_SECTION(nt);
for (USHORT si = 0; si < nt->FileHeader.NumberOfSections; si++, sec++)
{
if (!MmIsAddressValid(sec)) break;
if (!(sec->Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA)) continue;
PBYTE secBase = (PBYTE)halBase + sec->VirtualAddress;
ULONG secLen = sec->Misc.VirtualSize;
const WCHAR target[] = L"SecureBoot";
ULONG tgtBytes = sizeof(target) - sizeof(WCHAR); // 20 bytes
for (ULONG off = 0; off + tgtBytes + 8 < secLen; off += 2)
{
if (!MmIsAddressValid(secBase + off)) { off = (off | 0xFFF) + 1; continue; }
BOOLEAN match = TRUE;
for (ULONG c = 0; c < tgtBytes; c++) {
if (!MmIsAddressValid(secBase+off+c)) { match = FALSE; break; }
if (secBase[off+c] != ((PUCHAR)target)[c]) { match = FALSE; break; }
}
if (!match) continue;
// Found "SecureBoot" string in HAL.
// The variable data (0 or 1) is typically stored in a structure
// near/after the variable name. Scan forward for a single-byte
// value field (0x00 or 0x01).
for (ULONG v = tgtBytes; v < tgtBytes + 128; v += 4) {
if (!MmIsAddressValid(secBase+off+v)) break;
// Look for a DWORD-aligned value of 0 or 1
ULONG val = *(ULONG*)(secBase+off+v);
if (val == 0 || val == 1) {
*(ULONG*)(secBase+off+v) = 1; // Set SecureBoot = enabled
}
}
}
}
}
Технические нюансы:
Паттерн 83 0D для CI — это классика, но учитывайте, что в разных билдах Windows инструкции могут меняться. Обязательно проверяйте смещения перед тем, как кастить указатель к ULONG. Прямая запись в ciOpt и секции HAL может триггернуть PatchGuard (BSOD CRITICAL_STRUCTURE_CORRUPTION), если делать это в неподходящий момент или на системах с активным HVCI.
\Registry\Machine\SYSTEM\CurrentControlSet\Control\SecureBoot\State -> UEFISecureBootEnabled = 1
\Registry\Machine\SYSTEM\CurrentControlSet\Control\CI\Protected -> Licensed = 1
\Registry\Machine\SYSTEM\CurrentControlSet\Control\CI -> UMCIAuditMode = 0
\Registry\Machine\SYSTEM\CurrentControlSet\Control\CI\Protected -> Licensed = 1
\Registry\Machine\SYSTEM\CurrentControlSet\Control\CI -> UMCIAuditMode = 0
Для полноценного обхода сейчас одного этого мало (нужно еще возиться с TPM и имитировать состояние реестра для конкретного процесса античита), но как кусок тех-базы для своего проекта метод отличный.
Кто как сейчас борется с проверками на Windows 11, делитесь идеями по обходу HVCI и TPM в комментариях.