diff options
Diffstat (limited to 'EfiGuardDxe/PatchNtoskrnl.c')
-rw-r--r-- | EfiGuardDxe/PatchNtoskrnl.c | 85 |
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 |