Начинающий
- Статус
- Оффлайн
- Регистрация
- 13 Фев 2026
- Сообщения
- 180
- Реакции
- 5
Народ, кто сейчас ковыряет обходы для Ring 0, подкинул вам годноту по части эмуляции Secure Boot. Слил базу, потому что сам в свое время намучался с аттестацией TPM и кривыми рег-ключами.
Суть в том, что обычный спуфер или патч может не помочь, если античит делает полноценную проверку через TPM-аттестацию. Этот код позволяет пропатчить значения в ядре и подправить реестр (WCBL), чтобы система «думала», что Secure Boot включен, даже если вы его отключили для тестов или работы с драйверами.
Технические моменты:
Важно для понимания:
Код — это база. Как правильно заметили в источнике, если АС серьезный (типа Вангарда или жесткого Ricochet), то аттестация TPM может вас слить в момент. Лучший вариант — врубать Secure Boot в BIOS и юзать мануальный маппинг с хорошим драйвером, который умеет обходить Driver Signature Enforcement без отключения SB. Но для специфичных нужд или тестов на виртуалках/вторичках — самое то.
Братва, кто уже пробовал прокидывать подобные хуки через ZwQueryValueKey на актуальных билдах? Не отлетаете в пермач по Kernel Callback? Делитесь опытом, кто допиливал под себя, кидайте свои правки или методы оптимизации аллокаций, чтобы АС не палил ExAllocatePool.
Суть в том, что обычный спуфер или патч может не помочь, если античит делает полноценную проверку через TPM-аттестацию. Этот код позволяет пропатчить значения в ядре и подправить реестр (WCBL), чтобы система «думала», что Secure Boot включен, даже если вы его отключили для тестов или работы с драйверами.
Технические моменты:
- Работа с NtosKrnl: Используется паттерн для поиска HalEfiRuntimeServicesTable. Работает как на десятке, так и на одиннадцатой винде.
- WCBL Fix: Функция SetWCBLFile и SetWCBLRegistry лезут в логи платформы и правят байты, отвечающие за статус Secure Boot.
- KUSER_SHARED_DATA: Классика — форсим DbgSecureBootEnabled в единицу прямо в структуре данных ядра.
Код:
#include "Driver.h"
//48 8B 05 ? ? ? ? 48 8B ? 0F 11 45 ? 48 85 C0 = HalEfiRuntimeServicesTable -> works on win11 and win10!
VOID SetSecureBootNtosValue()
{
static CHAR szSig[] = "C1 E8 03 24 01 88 42 01";
pDriver->ulSecureBootValueAddress = FindPattern( pDriver->ulNtosKrnlBase, pDriver->ulNtosKrnlSize, szSig, -0x6, TRUE, 0x2 );
memset( szSig, 0, sizeof(szSig) );
//DBGPRINT( "SetSecureBootNtosValue::ulAddress: %p", ulAddress );
if(!pDriver->ulSecureBootValueAddress)
return;
ULONG ulValue = *(ULONG*)(pDriver->ulSecureBootValueAddress);
pDriver->ulSecureBootOrigValue = ulValue;
//DBGPRINT( "SetSecureBootNtosValue::Value 1: %x", ulValue );
ulValue |= 0x1;
ulValue |= 0x8;
*(ULONG*)(pDriver->ulSecureBootValueAddress) = ulValue;
//DBGPRINT( "SetSecureBootNtosValue::Value 2: %x", ulValue );
}
VOID SetSecureBootRegKey()
{
CHAR szSecureBootState[128] = {0};
GetDecryptedString( STRING_REG_KEYPATH_SECUREBOOTSTATE, szSecureBootState );
CHAR szUEFISecureBootEnabled[128] = {0};
GetDecryptedString( STRING_REG_KEYNAME_UEFISECUREBOOTENABLED, szUEFISecureBootEnabled );
WCHAR wzSecureBootState[128] = {0};
mbstowcs( wzSecureBootState, szSecureBootState, 128 );
WCHAR wzUEFISecureBootEnabled[128] = {0};
mbstowcs( wzUEFISecureBootEnabled, szUEFISecureBootEnabled, 128 );
UNICODE_STRING usKeyPath, usValueName;
RtlInitUnicodeString( &usKeyPath, wzSecureBootState );
RtlInitUnicodeString( &usValueName, wzUEFISecureBootEnabled );
OBJECT_ATTRIBUTES obj;
InitializeObjectAttributes( &obj, &usKeyPath, OBJ_CASE_INSENSITIVE, NULL, NULL );
HANDLE hKey = 0;
NTSTATUS ntRet = ZwOpenKey( &hKey, KEY_ALL_ACCESS, &obj );
if(ntRet == STATUS_SUCCESS)
{
DWORD dwValue = 0x1;
ZwSetValueKey( hKey, &usValueName, 0, REG_DWORD, (PVOID)&dwValue, sizeof(DWORD) );
ZwClose( hKey );
}
}
VOID SetWCBLFile( PWCHAR pPath )
{
UNICODE_STRING usPath;
RtlInitUnicodeString( &usPath, pPath );
OBJECT_ATTRIBUTES Obj;
InitializeObjectAttributes( &Obj, &usPath, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL );
CHAR szStatus[256] = {0};
HANDLE hFile = 0;
NTSTATUS ntRet = ZwCreateFile( &hFile, FILE_ALL_ACCESS, &Obj, (PIO_STATUS_BLOCK)&szStatus[0], NULL,
FILE_ATTRIBUTE_SYSTEM, 0, FILE_OPEN, FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0 );
//DBGPRINT( "SetWCBLFile::ntRet: %x", ntRet );
if(ntRet == STATUS_SUCCESS)
{
//DBGPRINT( "SetWCBLFile::Querying...." );
FILE_STANDARD_INFORMATION FileInfo = {0};
if(ZwQueryInformationFile( hFile, (PIO_STATUS_BLOCK)&szStatus, (PVOID)&FileInfo, sizeof(FILE_STANDARD_INFORMATION), FileStandardInformation ) == STATUS_SUCCESS)
{
ULONG ulFileLen = FileInfo.EndOfFile.LowPart;
//DBGPRINT( "SetWCBLFile::dwFileLen: %x", ulFileLen );
if(ulFileLen > 0x10)
{
PBYTE pAlloc = (PBYTE)ExAllocatePool( NonPagedPool, ulFileLen + 1 );
//DBGPRINT( "SetWCBLFile::pAlloc: %p", pAlloc );
if(pAlloc)
{
CHAR szStatus[256] = {0};
LARGE_INTEGER liOffset = {0};
ntRet = ZwReadFile( hFile, NULL, NULL, NULL, (PIO_STATUS_BLOCK)&szStatus[0], pAlloc, ulFileLen, &liOffset, NULL );
if(ntRet == STATUS_SUCCESS)
{
//DBGPRINT( "SetWCBLFile::Read Succeeded!" );
memset( szStatus, 0, 256 );
INT iSecureBootIndex = 0, iSizeIndex = 0, iSecureBootIndex2 = 0;
//DBGPRINT( "SetWCBLFile::FileSize: %x", ulFileLen );
for(int i = 0; i < (int)(ulFileLen - 0x10); i++)
{
if( *(ULONG_PTR*)(&pAlloc[i]) == 0x0075006300650053 ) //S.e.c.u
{
iSizeIndex = i - 0x24;
iSecureBootIndex = i + 0x14;
iSecureBootIndex2 = i - 0x8;
break;
}
}
//DBGPRINT( "SetWCBLFile::iSizeIndex: %x", iSizeIndex );
//DBGPRINT( "SetWCBLFile::iSecureBootIndex: %x", iSecureBootIndex );
if(pAlloc[iSizeIndex] == 0x34)
{
//DBGPRINT( "SetWCBLFile::Fixing Size!" );
memcpy( &pAlloc[iSecureBootIndex + 1], &pAlloc[iSecureBootIndex], ulFileLen - iSecureBootIndex );
pAlloc[iSizeIndex] += 0x1;
pAlloc[iSecureBootIndex] = 0x1;
pAlloc[iSecureBootIndex2] = 0x1;
ulFileLen += 1;
}
else if(pAlloc[iSizeIndex] == 0x35)
{
pAlloc[iSecureBootIndex] = 0x1;
pAlloc[iSecureBootIndex2] = 0x1;
//DBGPRINT( "SetWCBLFile::Set SB Byte!" );
}
liOffset.QuadPart = 0;
ntRet = ZwWriteFile( hFile, 0, 0, 0, (PIO_STATUS_BLOCK)&szStatus[0], (PVOID)pAlloc, ulFileLen, &liOffset, 0 );
//DBGPRINT( "SetWCBLFile::ZwWriteFile: %x", ntRet );
}
else
{
//DBGPRINT( "SetWCBLFile::READ FAILED ON BOOTFILE: %x", ntRet );
}
ExFreePool( pAlloc );
}
}
}
else
{
//DBGPRINT( "SetWCBLFile::QUERY FAILED ON BOOTFILE!" );
}
ZwClose( hFile );
}
else
{
//DBGPRINT( "SetWCBLFile::ZwCreateFile: %x", ntRet );
}
}
VOID SetRegistryKey( PWCHAR wPath, PWCHAR wKeyName, INT Type, PVOID pData, INT Len )
{
HANDLE hKey = 0;
UNICODE_STRING usKeyPath, usValueName;
RtlInitUnicodeString( &usKeyPath, wPath );
RtlInitUnicodeString( &usValueName, wKeyName );
OBJECT_ATTRIBUTES obj;
InitializeObjectAttributes( &obj, &usKeyPath, OBJ_CASE_INSENSITIVE, NULL, NULL );
NTSTATUS ntRet = ZwOpenKey( &hKey, KEY_ALL_ACCESS, &obj );
if(ntRet == STATUS_SUCCESS)
{
ZwSetValueKey( hKey, &usValueName, 0, Type, (PVOID)pData, Len );
ZwClose( hKey );
}
}
VOID SetWCBLRegistry()
{
CHAR szIntegrityServices[128] = {0};
GetDecryptedString( STRING_REG_KEYPATH_INTEGRITYSERVICES, szIntegrityServices );
CHAR szWBCL[128] = {0};
GetDecryptedString( STRING_REG_KEYNAME_WBCL, szWBCL );
WCHAR wzIntegrityServices[128] = {0};
mbstowcs( wzIntegrityServices, szIntegrityServices, 128 );
WCHAR wzWBCL[128] = {0};
mbstowcs( wzWBCL, szWBCL, 128 );
UNICODE_STRING usKeyPath, usValueName;
RtlInitUnicodeString( &usKeyPath, wzIntegrityServices );
RtlInitUnicodeString( &usValueName, wzWBCL );
OBJECT_ATTRIBUTES obj;
InitializeObjectAttributes( &obj, &usKeyPath, OBJ_CASE_INSENSITIVE, NULL, NULL );
HANDLE hKey = 0;
if(ZwOpenKey( &hKey, KEY_ALL_ACCESS, &obj ) == STATUS_SUCCESS)
{
PKEY_VALUE_PARTIAL_INFORMATION pKeyInfo = (PKEY_VALUE_PARTIAL_INFORMATION)ExAllocatePool( NonPagedPool, WBCL_SIZE * 2 ); //Make this alloc static inside driver so A/C can't see the allocs???
if(pKeyInfo)
{
ULONG ulOutSize = 0;
NTSTATUS ntRet = ZwQueryValueKey( hKey, &usValueName, KeyValuePartialInformation, (PVOID)pKeyInfo, WBCL_SIZE * 2, &ulOutSize );
if(ntRet == STATUS_SUCCESS )
{
INT iSecureBootIndex = 0, iSizeIndex = 0, iSecureBootIndex2 = 0;
for(int i = 0; i < (int)(pKeyInfo->DataLength - 0x10); i++)
{
if( *(ULONG_PTR*)(&pKeyInfo->Data[i]) == 0x0075006300650053 ) //S.e.c.u
{
iSizeIndex = i - 0x24;
iSecureBootIndex = i + 0x14;
iSecureBootIndex2 = i - 0x8;
break;
}
}
//DBGPRINT( "SetWCBLRegistry::DataLength: %x", pKeyInfo->DataLength );
//DBGPRINT( "SetWCBLRegistry::iSizeIndex: %x", iSizeIndex );
//DBGPRINT( "SetWCBLRegistry::iSecureBootIndex: %x", iSecureBootIndex );
if(pKeyInfo->Data[iSizeIndex] == 0x34)
{
//DBGPRINT( "SetWCBLRegistry::Fixing Size!" );
memcpy( &pKeyInfo->Data[iSecureBootIndex + 1], &pKeyInfo->Data[iSecureBootIndex], pKeyInfo->DataLength - iSecureBootIndex );
pKeyInfo->Data[iSizeIndex] += 0x1;
pKeyInfo->Data[iSecureBootIndex] = 0x1;
pKeyInfo->Data[iSecureBootIndex2] = 0x1;
pKeyInfo->DataLength += 1;
}
else if(pKeyInfo->Data[iSizeIndex] == 0x35)
{
*(BYTE*)(&pKeyInfo->Data[iSecureBootIndex]) = 0x1;
//DBGPRINT( "SetWCBLRegistry::Set SB Byte!" );
}
if(iSecureBootIndex && iSizeIndex)
{
SetRegistryKey( wzIntegrityServices, wzWBCL, REG_BINARY, (PVOID)pKeyInfo->Data, pKeyInfo->DataLength );
}
//DBGPRINT( "SetWCBLRegistry::WBCL REGISTRY SET!" );
}
ExFreePool( pKeyInfo );
}
ZwClose( hKey );
}
}
BOOL GetPlatformLogFilePath( PWCHAR pOut )
{
CHAR szIntegrityServices[128] = {0};
GetDecryptedString( STRING_REG_KEYPATH_INTEGRITYSERVICES, szIntegrityServices );
CHAR szPlatformLogFile[128] = {0};
GetDecryptedString( STRING_REG_KEYNAME_PLATFORMLOGFILE, szPlatformLogFile );
WCHAR wzIntegrityServices[128] = {0};
mbstowcs( wzIntegrityServices, szIntegrityServices, 128 );
WCHAR wzPlatformLogFile[128] = {0};
mbstowcs( wzPlatformLogFile, szPlatformLogFile, 128 );
UNICODE_STRING usKeyPath, usValueName;
RtlInitUnicodeString( &usKeyPath, wzIntegrityServices );
RtlInitUnicodeString( &usValueName, wzPlatformLogFile );
OBJECT_ATTRIBUTES obj;
InitializeObjectAttributes( &obj, &usKeyPath, OBJ_CASE_INSENSITIVE, NULL, NULL );
BOOL bRet = FALSE;
HANDLE hKey = 0;
if(ZwOpenKey( &hKey, KEY_ALL_ACCESS, &obj ) == STATUS_SUCCESS)
{
PKEY_VALUE_PARTIAL_INFORMATION pKeyInfo = (PKEY_VALUE_PARTIAL_INFORMATION)ExAllocatePool( NonPagedPool, 256 * sizeof(WCHAR) ); //Make this alloc static inside driver so A/C can't see the allocs???
if(pKeyInfo)
{
ULONG ulOutSize = 0;
if(ZwQueryValueKey( hKey, &usValueName, KeyValuePartialInformation, (PVOID)pKeyInfo, 256 * sizeof(WCHAR), &ulOutSize ) == STATUS_SUCCESS )
{
swprintf( pOut, L"%s", (PWCHAR)pKeyInfo->Data );
bRet = TRUE;
}
ExFreePool( pKeyInfo );
}
ZwClose( hKey );
}
return bRet;
}
VOID SetTPMRegister()
{
WCHAR wzPlatformFilePath[256] = {0};
if(GetPlatformLogFilePath( wzPlatformFilePath ) )
{
SetWCBLFile( wzPlatformFilePath );
SetWCBLRegistry();
}
}
VOID SetUserSharedDataSecureBoot()
{
_KUSER_SHARED_DATA2* pSharedData = (_KUSER_SHARED_DATA2*)KI_USER_SHARED_DATA;
//DBGPRINT( "pSharedData->DbgSecureBootEnabled 1: %d", pSharedData->DbgSecureBootEnabled );
_KUSER_SHARED_DATA2* pAlloc = (_KUSER_SHARED_DATA2*)ExAllocatePool( NonPagedPool, sizeof(_KUSER_SHARED_DATA2) );
if(pAlloc)
{
memcpy( pAlloc, pSharedData, sizeof(_KUSER_SHARED_DATA2) );
pAlloc->DbgSecureBootEnabled = 1;
SafeWrite( (PVOID)&pSharedData->SharedDataFlags, (PVOID)&pAlloc->SharedDataFlags, sizeof(ULONG) );
ExFreePool( pAlloc );
}
//DBGPRINT( "pSharedData->DbgSecureBootEnabled 2: %d", pSharedData->DbgSecureBootEnabled );
}
Важно для понимания:
Код — это база. Как правильно заметили в источнике, если АС серьезный (типа Вангарда или жесткого Ricochet), то аттестация TPM может вас слить в момент. Лучший вариант — врубать Secure Boot в BIOS и юзать мануальный маппинг с хорошим драйвером, который умеет обходить Driver Signature Enforcement без отключения SB. Но для специфичных нужд или тестов на виртуалках/вторичках — самое то.
Братва, кто уже пробовал прокидывать подобные хуки через ZwQueryValueKey на актуальных билдах? Не отлетаете в пермач по Kernel Callback? Делитесь опытом, кто допиливал под себя, кидайте свои правки или методы оптимизации аллокаций, чтобы АС не палил ExAllocatePool.