Skip to content

Commit

Permalink
Patch nt!KiMcaDeferredRecoveryService on Windows >= 8.1
Browse files Browse the repository at this point in the history
  • Loading branch information
Mattiwatti committed May 7, 2020
1 parent e2ca738 commit b5d0353
Showing 1 changed file with 97 additions and 4 deletions.
101 changes: 97 additions & 4 deletions EfiGuardDxe/PatchNtoskrnl.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,17 @@ STATIC CONST UINT8 SigKiVerifyScopesExecute[] = {
0x48, 0xB8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE // mov rax, 0FEFFFFFFFFFFFFFFh
};

// Signature for nt!KiMcaDeferredRecoveryService
// This function is present since Windows 8.1 and bugchecks the system with bugcode 0x109 after zeroing registers.
// It is called by KiScanQueues and KiSchedulerDpc, two PatchGuard DPCs which may be queued from various unrelated kernel functions.
STATIC CONST UINT8 SigKiMcaDeferredRecoveryService[] = {
0x33, 0xC0, // xor eax, eax
0x8B, 0xD8, // mov ebx, eax
0x8B, 0xF8, // mov edi, eax
0x8B, 0xE8, // mov ebp, eax
0x4C, 0x8B, 0xD0 // mov r10, rax
};

// Signature for nt!KiSwInterrupt
// This function is present since Windows 10 and is the interrupt handler for int 20h.
// This interrupt is a spurious interrupt on older versions of Windows, and does nothing useful on Windows 10.
Expand Down Expand Up @@ -136,7 +147,7 @@ DisablePatchGuard(
return EFI_LOAD_ERROR;
}

CONST UINTN Length = SizeOfRawData;
UINTN Length = SizeOfRawData;
UINTN Offset = 0;
ZyanU64 InstructionAddress;
ZydisDecodedInstruction Instruction;
Expand Down Expand Up @@ -277,14 +288,85 @@ DisablePatchGuard(
}
}

// Search for KiSwInterrupt (only exists on Windows >= 10)
UINT8* KiSwInterruptPatternAddress = NULL;
if (BuildNumber >= 10240)
// Search for callers of KiMcaDeferredRecoveryService (only exists on Windows >= 8.1)
UINT8* KiMcaDeferredRecoveryServiceCallers[2];
ZeroMem(KiMcaDeferredRecoveryServiceCallers, sizeof(KiMcaDeferredRecoveryServiceCallers));
if (BuildNumber >= 9600)
{
StartRva = TextSection->VirtualAddress;
SizeOfRawData = TextSection->SizeOfRawData;
StartVa = ImageBase + StartRva;

// Search for KiMcaDeferredRecoveryService
PRINT_KERNEL_PATCH_MSG(L"== Searching for nt!KiMcaDeferredRecoveryService pattern in .text ==\r\n");
UINT8* KiMcaDeferredRecoveryService = NULL;
for (UINT8* Address = (UINT8*)StartVa; Address < StartVa + SizeOfRawData - sizeof(SigKiMcaDeferredRecoveryService); ++Address)
{
if (CompareMem(Address, SigKiMcaDeferredRecoveryService, sizeof(SigKiMcaDeferredRecoveryService)) == 0)
{
KiMcaDeferredRecoveryService = Address;
PRINT_KERNEL_PATCH_MSG(L" Found KiMcaDeferredRecoveryService pattern at 0x%llX.\r\n", (UINTN)KiMcaDeferredRecoveryService);
break;
}
}

if (KiMcaDeferredRecoveryService == NULL)
{
PRINT_KERNEL_PATCH_MSG(L" Failed to find KiMcaDeferredRecoveryService.\r\n");
return EFI_NOT_FOUND;
}

// Start decode loop
Length = SizeOfRawData;
Offset = 0;
while ((InstructionAddress = (ZyanU64)(StartVa + Offset),
Status = ZydisDecoderDecodeBuffer(&Decoder,
(VOID*)InstructionAddress,
Length - Offset,
&Instruction)) != ZYDIS_STATUS_NO_MORE_DATA)
{
if (!ZYAN_SUCCESS(Status))
{
Offset++;
continue;
}

// Check if this is 'call KiMcaDeferredRecoveryService'
ZyanU64 OperandAddress = 0;
if (Instruction.mnemonic == ZYDIS_MNEMONIC_CALL &&
ZYAN_SUCCESS(ZydisCalcAbsoluteAddress(&Instruction, &Instruction.operands[0], InstructionAddress, &OperandAddress)) &&
OperandAddress == (UINTN)KiMcaDeferredRecoveryService)
{
if (KiMcaDeferredRecoveryServiceCallers[0] == NULL)
{
KiMcaDeferredRecoveryServiceCallers[0] = (UINT8*)InstructionAddress;
}
else if (KiMcaDeferredRecoveryServiceCallers[1] == NULL)
{
KiMcaDeferredRecoveryServiceCallers[1] = (UINT8*)InstructionAddress;
break;
}
}

Offset += Instruction.length;
}

// Backtrack to function start
KiMcaDeferredRecoveryServiceCallers[0] = BacktrackToFunctionStart(KiMcaDeferredRecoveryServiceCallers[0],
(UINT8*)(KiMcaDeferredRecoveryServiceCallers[0] - StartVa));
KiMcaDeferredRecoveryServiceCallers[1] = BacktrackToFunctionStart(KiMcaDeferredRecoveryServiceCallers[1],
(UINT8*)(KiMcaDeferredRecoveryServiceCallers[1] - StartVa));
if (KiMcaDeferredRecoveryServiceCallers[0] == NULL || KiMcaDeferredRecoveryServiceCallers[1] == NULL)
{
PRINT_KERNEL_PATCH_MSG(L" Failed to find KiMcaDeferredRecoveryService callers.\r\n");
return EFI_NOT_FOUND;
}
}

// Search for KiSwInterrupt (only exists on Windows >= 10)
UINT8* KiSwInterruptPatternAddress = NULL;
if (BuildNumber >= 10240)
{
PRINT_KERNEL_PATCH_MSG(L"== Searching for nt!KiSwInterrupt pattern in .text ==\r\n");
CONST EFI_STATUS FindKiSwInterruptStatus = FindPattern(SigKiSwInterrupt,
0xCC,
Expand Down Expand Up @@ -313,6 +395,11 @@ DisablePatchGuard(
*((UINT32*)ExpLicenseWatchInitWorker) = No;
if (KiVerifyScopesExecute != NULL)
*(UINT32*)KiVerifyScopesExecute = No;
if (KiMcaDeferredRecoveryServiceCallers[0] != NULL && KiMcaDeferredRecoveryServiceCallers[1] != NULL)
{
*(UINT32*)KiMcaDeferredRecoveryServiceCallers[0] = No;
*(UINT32*)KiMcaDeferredRecoveryServiceCallers[1] = No;
}
if (KiSwInterruptPatternAddress != NULL)
SetMem(KiSwInterruptPatternAddress, sizeof(SigKiSwInterrupt), 0x90); // 11 x nop

Expand All @@ -331,6 +418,12 @@ DisablePatchGuard(
PRINT_KERNEL_PATCH_MSG(L" Patched KiVerifyScopesExecute [RVA: 0x%X].\r\n",
(UINT32)(KiVerifyScopesExecute - ImageBase));
}
if (KiMcaDeferredRecoveryServiceCallers[0] != NULL && KiMcaDeferredRecoveryServiceCallers[1] != NULL)
{
PRINT_KERNEL_PATCH_MSG(L" Patched KiMcaDeferredRecoveryService [RVAs: 0x%X, 0x%X].\r\n",
(UINT32)(KiMcaDeferredRecoveryServiceCallers[0] - ImageBase),
(UINT32)(KiMcaDeferredRecoveryServiceCallers[1] - ImageBase));
}
if (KiSwInterruptPatternAddress != NULL)
{
PRINT_KERNEL_PATCH_MSG(L" Patched KiSwInterrupt [RVA: 0x%X].\r\n",
Expand Down

0 comments on commit b5d0353

Please sign in to comment.