aboutsummaryrefslogtreecommitdiff
path: root/EfiGuardDxe/PatchNtoskrnl.c
diff options
context:
space:
mode:
Diffstat (limited to 'EfiGuardDxe/PatchNtoskrnl.c')
-rw-r--r--EfiGuardDxe/PatchNtoskrnl.c85
1 files changed, 73 insertions, 12 deletions
diff --git a/EfiGuardDxe/PatchNtoskrnl.c b/EfiGuardDxe/PatchNtoskrnl.c
index a852dcb..2e2d20e 100644
--- a/EfiGuardDxe/PatchNtoskrnl.c
+++ b/EfiGuardDxe/PatchNtoskrnl.c
@@ -35,7 +35,7 @@ STATIC CONST UINT8 SigKeInitAmd64SpecificState[] = {
// This function is present since Windows 8.1 and is responsible for executing all functions in the KiVerifyXcptRoutines array.
// One of these functions, KiVerifyXcpt15, will indirectly initialize a PatchGuard context from its exception handler.
STATIC CONST UINT8 SigKiVerifyScopesExecute[] = {
- 0x48, 0x83, 0xCC, 0xCC, 0x00, // and [REG+XX], 0
+ 0x83, 0xCC, 0xCC, 0x00, // and d/qword ptr [REG+XX], 0
0x48, 0xB8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE // mov rax, 0FEFFFFFFFFFFFFFFh
};
@@ -57,10 +57,11 @@ STATIC CONST UINT8 SigKiMcaDeferredRecoveryService[] = {
// If int 20h is issued from kernel mode, the PatchGuard verification routine KiSwInterruptDispatch is called.
STATIC CONST UINT8 SigKiSwInterrupt[] = {
0xFB, // sti
- 0x48, 0x8D, 0xCC, 0xCC, // lea rcx, XX
+ 0x48, 0x8D, 0xCC, 0xCC, // lea REG, [REG-XX]
0xE8, 0xCC, 0xCC, 0xCC, 0xCC, // call KiSwInterruptDispatch
0xFA // cli
};
+STATIC CONST UINTN SigKiSwInterruptCallOffset = 5, SigKiSwInterruptCliOffset = 10;
#endif
#endif
@@ -69,7 +70,7 @@ STATIC CONST UINT8 SigKiSwInterrupt[] = {
// This signature is only for the Windows 10 RS3+ version. I could add more signatures but this is a pretty superficial patch anyway.
STATIC CONST UINT8 SigSeCodeIntegrityQueryInformation[] = {
0x48, 0x83, 0xEC, // sub rsp, XX
- 0xCC, 0x48, 0x83, 0x3D, 0xCC, 0xCC, 0xCC, 0xCC, 0x00, // cmp cs:qword_14035E638, 0
+ 0xCC, 0x48, 0x83, 0x3D, 0xCC, 0xCC, 0xCC, 0xCC, 0x00, // cmp ds:qword_xxxx, 0
0x4D, 0x8B, 0xC8, // mov r9, r8
0x4C, 0x8B, 0xD1, // mov r10, rcx
0x74, 0xCC // jz XX
@@ -295,7 +296,7 @@ DisablePatchGuard(
#ifndef EAC_COMPAT_MODE
// Search for callers of KiMcaDeferredRecoveryService (only exists on Windows >= 8.1)
UINT8* KiMcaDeferredRecoveryServiceCallers[2];
- ZeroMem(KiMcaDeferredRecoveryServiceCallers, sizeof(KiMcaDeferredRecoveryServiceCallers));
+ ZeroMem((VOID*)KiMcaDeferredRecoveryServiceCallers, sizeof(KiMcaDeferredRecoveryServiceCallers));
if (BuildNumber >= 9600)
{
StartRva = TextSection->VirtualAddress;
@@ -367,11 +368,20 @@ DisablePatchGuard(
}
}
- // Search for KiSwInterrupt (only exists on Windows >= 10)
- UINT8* KiSwInterruptPatternAddress = NULL;
+ // We need KiSwInterruptDispatch to call ExAllocatePool2 for our preferred method to work, because we rely on it to
+ // return null for zero pool tags. Windows 10 20H1 does export ExAllocatePool2, but without using it where we need it.
+ CONST BOOLEAN FindGlobalPgContext = BuildNumber >= 20348 && GetProcedureAddress((UINTN)ImageBase, NtHeaders, "ExAllocatePool2") != NULL;
+
+ // Search for KiSwInterrupt[Dispatch] and optionally its global PatchGuard context (named g_PgContext here). Both of these only exist on Windows >= 10
+ UINT8* KiSwInterruptPatternAddress = NULL, *gPgContext = NULL;
if (BuildNumber >= 10240)
{
+ StartRva = TextSection->VirtualAddress;
+ SizeOfRawData = TextSection->SizeOfRawData;
+ StartVa = ImageBase + StartRva;
+
PRINT_KERNEL_PATCH_MSG(L"== Searching for nt!KiSwInterrupt pattern in .text ==\r\n");
+ UINT8* KiSwInterruptDispatchAddress = NULL;
CONST EFI_STATUS FindKiSwInterruptStatus = FindPattern(SigKiSwInterrupt,
0xCC,
sizeof(SigKiSwInterrupt),
@@ -380,14 +390,55 @@ DisablePatchGuard(
(VOID**)&KiSwInterruptPatternAddress);
if (EFI_ERROR(FindKiSwInterruptStatus))
{
- // This is not a fatal error as the system can still boot without patching KiSwInterrupt.
+ // This is not a fatal error as the system can still boot without patching g_PgContext or KiSwInterrupt.
// However note that in this case, any attempt to issue int 20h from kernel mode later will result in a bugcheck.
PRINT_KERNEL_PATCH_MSG(L" Failed to find KiSwInterrupt. Skipping patch.\r\n");
}
else
{
+ ASSERT(SigKiSwInterrupt[SigKiSwInterruptCallOffset] == 0xE8 && SigKiSwInterrupt[SigKiSwInterruptCliOffset] == 0xFA);
+ CONST INT32 Relative = *(INT32*)(KiSwInterruptPatternAddress + SigKiSwInterruptCallOffset + 1);
+ KiSwInterruptDispatchAddress = KiSwInterruptPatternAddress + SigKiSwInterruptCliOffset + Relative;
+
PRINT_KERNEL_PATCH_MSG(L" Found KiSwInterrupt pattern at 0x%llX.\r\n", (UINTN)KiSwInterruptPatternAddress);
}
+
+ if (KiSwInterruptDispatchAddress != NULL && FindGlobalPgContext)
+ {
+ // Start decode loop
+ Context.Length = 128;
+ Context.Offset = 0;
+ while ((Context.InstructionAddress = (ZyanU64)(KiSwInterruptDispatchAddress + Context.Offset),
+ Status = ZydisDecoderDecodeFull(&Context.Decoder,
+ (VOID*)Context.InstructionAddress,
+ Context.Length - Context.Offset,
+ &Context.Instruction,
+ Context.Operands)) != ZYDIS_STATUS_NO_MORE_DATA)
+ {
+ if (!ZYAN_SUCCESS(Status))
+ {
+ Context.Offset++;
+ continue;
+ }
+
+ // Check if this is 'mov REG, ds:g_PgContext'
+ if (Context.Instruction.operand_count == 2 &&
+ Context.Instruction.mnemonic == ZYDIS_MNEMONIC_MOV &&
+ (Context.Instruction.attributes & ZYDIS_ATTRIB_ACCEPTS_SEGMENT) != 0 &&
+ Context.Operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER &&
+ Context.Operands[1].type == ZYDIS_OPERAND_TYPE_MEMORY && Context.Operands[1].mem.base == ZYDIS_REGISTER_RIP &&
+ (Context.Operands[1].mem.segment == ZYDIS_REGISTER_CS || Context.Operands[1].mem.segment == ZYDIS_REGISTER_DS))
+ {
+ if (ZYAN_SUCCESS(ZydisCalcAbsoluteAddress(&Context.Instruction, &Context.Operands[1], Context.InstructionAddress, (ZyanU64*)&gPgContext)))
+ {
+ PRINT_KERNEL_PATCH_MSG(L" Found g_PgContext at 0x%llX.\r\n", (UINTN)gPgContext);
+ break;
+ }
+ }
+
+ Context.Offset += Context.Instruction.length;
+ }
+ }
}
#endif
@@ -406,8 +457,15 @@ DisablePatchGuard(
CopyWpMem(KiMcaDeferredRecoveryServiceCallers[0], &No, sizeof(No));
CopyWpMem(KiMcaDeferredRecoveryServiceCallers[1], &No, sizeof(No));
}
- if (KiSwInterruptPatternAddress != NULL)
+ if (gPgContext != NULL)
+ {
+ CONST UINT64 NewPgContextAddress = (UINT64)ImageBase + InitSection->VirtualAddress; // Address in discardable section
+ CopyWpMem(gPgContext, &NewPgContextAddress, sizeof(NewPgContextAddress));
+ }
+ else if (KiSwInterruptPatternAddress != NULL)
+ {
SetWpMem(KiSwInterruptPatternAddress, sizeof(SigKiSwInterrupt), 0x90); // 11 x nop
+ }
#endif
// Print info
@@ -432,7 +490,12 @@ DisablePatchGuard(
(UINT32)(KiMcaDeferredRecoveryServiceCallers[0] - ImageBase),
(UINT32)(KiMcaDeferredRecoveryServiceCallers[1] - ImageBase));
}
- if (KiSwInterruptPatternAddress != NULL)
+ if (gPgContext != NULL)
+ {
+ PRINT_KERNEL_PATCH_MSG(L" Patched g_PgContext [RVA: 0x%X].\r\n",
+ (UINT32)(gPgContext - ImageBase));
+ }
+ else if (KiSwInterruptPatternAddress != NULL)
{
PRINT_KERNEL_PATCH_MSG(L" Patched KiSwInterrupt [RVA: 0x%X].\r\n",
(UINT32)(KiSwInterruptPatternAddress - ImageBase));
@@ -821,9 +884,7 @@ PatchNtoskrnl(
Section++;
}
- ASSERT(InitSection != NULL);
- ASSERT(TextSection != NULL);
- ASSERT(PageSection != NULL);
+ ASSERT(InitSection != NULL && TextSection != NULL && PageSection != NULL);
#ifndef DO_NOT_DISABLE_PATCHGUARD
// Patch INIT and .text sections to disable PatchGuard