Skip to content

Commit b94b82d

Browse files
committed
KiSwInterrupt patch: change the PG context address instead if possible
Reference: #101
1 parent 0e62d5a commit b94b82d

File tree

1 file changed

+77
-11
lines changed

1 file changed

+77
-11
lines changed

EfiGuardDxe/PatchNtoskrnl.c

+77-11
Original file line numberDiff line numberDiff line change
@@ -52,17 +52,18 @@ STATIC CONST UINT8 SigKiMcaDeferredRecoveryService[] = {
5252
// If int 20h is issued from kernel mode, the PatchGuard verification routine KiSwInterruptDispatch is called.
5353
STATIC CONST UINT8 SigKiSwInterrupt[] = {
5454
0xFB, // sti
55-
0x48, 0x8D, 0xCC, 0xCC, // lea rcx, XX
55+
0x48, 0x8D, 0xCC, 0xCC, // lea REG, [REG-XX]
5656
0xE8, 0xCC, 0xCC, 0xCC, 0xCC, // call KiSwInterruptDispatch
5757
0xFA // cli
5858
};
59+
STATIC CONST UINTN SigKiSwInterruptCallOffset = 5, SigKiSwInterruptCliOffset = 10;
5960

6061
// Signature for nt!SeCodeIntegrityQueryInformation, called through NtQuerySystemInformation(SystemCodeIntegrityInformation).
6162
// This function has actually existed since Vista in various forms, sometimes (8/8.1/early 10) inlined in ExpQuerySystemInformation.
6263
// This signature is only for the Windows 10 RS3+ version. I could add more signatures but this is a pretty superficial patch anyway.
6364
STATIC CONST UINT8 SigSeCodeIntegrityQueryInformation[] = {
6465
0x48, 0x83, 0xEC, // sub rsp, XX
65-
0xCC, 0x48, 0x83, 0x3D, 0xCC, 0xCC, 0xCC, 0xCC, 0x00, // cmp cs:qword_14035E638, 0
66+
0xCC, 0x48, 0x83, 0x3D, 0xCC, 0xCC, 0xCC, 0xCC, 0x00, // cmp ds:qword_xxxx, 0
6667
0x4D, 0x8B, 0xC8, // mov r9, r8
6768
0x4C, 0x8B, 0xD1, // mov r10, rcx
6869
0x74, 0xCC // jz XX
@@ -88,6 +89,7 @@ DisablePatchGuard(
8889
IN UINT8* ImageBase,
8990
IN PEFI_IMAGE_NT_HEADERS NtHeaders,
9091
IN PEFI_IMAGE_SECTION_HEADER InitSection,
92+
IN PEFI_IMAGE_SECTION_HEADER InitDataSection,
9193
IN PEFI_IMAGE_SECTION_HEADER TextSection,
9294
IN UINT16 BuildNumber
9395
)
@@ -358,11 +360,21 @@ DisablePatchGuard(
358360
}
359361
}
360362

361-
// Search for KiSwInterrupt (only exists on Windows >= 10)
362-
UINT8* KiSwInterruptPatternAddress = NULL;
363+
// We need KiSwInterruptDispatch to call ExAllocatePool2 for our preferred method to work, because we rely on it to
364+
// return null for zero pool tags. Windows 10 20H1 does export ExAllocatePool2, but without using it where we need it.
365+
CONST BOOLEAN FindGlobalPgContext = BuildNumber >= 20348 && InitDataSection != NULL &&
366+
GetProcedureAddress((UINTN)ImageBase, NtHeaders, "ExAllocatePool2") != NULL;
367+
368+
// Search for KiSwInterrupt[Dispatch] and optionally its global PatchGuard context (named g_PgContext here). Both of these only exist on Windows >= 10
369+
UINT8* KiSwInterruptPatternAddress = NULL, *gPgContext = NULL;
363370
if (BuildNumber >= 10240)
364371
{
372+
StartRva = TextSection->VirtualAddress;
373+
SizeOfRawData = TextSection->SizeOfRawData;
374+
StartVa = ImageBase + StartRva;
375+
365376
PRINT_KERNEL_PATCH_MSG(L"== Searching for nt!KiSwInterrupt pattern in .text ==\r\n");
377+
UINT8* KiSwInterruptDispatchAddress = NULL;
366378
CONST EFI_STATUS FindKiSwInterruptStatus = FindPattern(SigKiSwInterrupt,
367379
0xCC,
368380
sizeof(SigKiSwInterrupt),
@@ -371,14 +383,55 @@ DisablePatchGuard(
371383
(VOID**)&KiSwInterruptPatternAddress);
372384
if (EFI_ERROR(FindKiSwInterruptStatus))
373385
{
374-
// This is not a fatal error as the system can still boot without patching KiSwInterrupt.
386+
// This is not a fatal error as the system can still boot without patching g_PgContext or KiSwInterrupt.
375387
// However note that in this case, any attempt to issue int 20h from kernel mode later will result in a bugcheck.
376388
PRINT_KERNEL_PATCH_MSG(L" Failed to find KiSwInterrupt. Skipping patch.\r\n");
377389
}
378390
else
379391
{
392+
ASSERT(SigKiSwInterrupt[SigKiSwInterruptCallOffset] == 0xE8 && SigKiSwInterrupt[SigKiSwInterruptCliOffset] == 0xFA);
393+
CONST INT32 Relative = *(INT32*)(KiSwInterruptPatternAddress + SigKiSwInterruptCallOffset + 1);
394+
KiSwInterruptDispatchAddress = KiSwInterruptPatternAddress + SigKiSwInterruptCliOffset + Relative;
395+
380396
PRINT_KERNEL_PATCH_MSG(L" Found KiSwInterrupt pattern at 0x%llX.\r\n", (UINTN)KiSwInterruptPatternAddress);
381397
}
398+
399+
if (KiSwInterruptDispatchAddress != NULL && FindGlobalPgContext)
400+
{
401+
// Start decode loop
402+
Context.Length = 128;
403+
Context.Offset = 0;
404+
while ((Context.InstructionAddress = (ZyanU64)(KiSwInterruptDispatchAddress + Context.Offset),
405+
Status = ZydisDecoderDecodeFull(&Context.Decoder,
406+
(VOID*)Context.InstructionAddress,
407+
Context.Length - Context.Offset,
408+
&Context.Instruction,
409+
Context.Operands)) != ZYDIS_STATUS_NO_MORE_DATA)
410+
{
411+
if (!ZYAN_SUCCESS(Status))
412+
{
413+
Context.Offset++;
414+
continue;
415+
}
416+
417+
// Check if this is 'mov REG, ds:g_PgContext'
418+
if (Context.Instruction.operand_count == 2 &&
419+
Context.Instruction.mnemonic == ZYDIS_MNEMONIC_MOV &&
420+
(Context.Instruction.attributes & ZYDIS_ATTRIB_ACCEPTS_SEGMENT) != 0 &&
421+
Context.Operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER &&
422+
Context.Operands[1].type == ZYDIS_OPERAND_TYPE_MEMORY && Context.Operands[1].mem.base == ZYDIS_REGISTER_RIP &&
423+
(Context.Operands[1].mem.segment == ZYDIS_REGISTER_CS || Context.Operands[1].mem.segment == ZYDIS_REGISTER_DS))
424+
{
425+
if (ZYAN_SUCCESS(ZydisCalcAbsoluteAddress(&Context.Instruction, &Context.Operands[1], Context.InstructionAddress, (ZyanU64*)&gPgContext)))
426+
{
427+
PRINT_KERNEL_PATCH_MSG(L" Found g_PgContext at 0x%llX.\r\n", (UINTN)gPgContext);
428+
break;
429+
}
430+
}
431+
432+
Context.Offset += Context.Instruction.length;
433+
}
434+
}
382435
}
383436

384437
// We have all the addresses we need; now do the actual patching.
@@ -395,8 +448,15 @@ DisablePatchGuard(
395448
CopyWpMem(KiMcaDeferredRecoveryServiceCallers[0], &No, sizeof(No));
396449
CopyWpMem(KiMcaDeferredRecoveryServiceCallers[1], &No, sizeof(No));
397450
}
398-
if (KiSwInterruptPatternAddress != NULL)
451+
if (gPgContext != NULL)
452+
{
453+
CONST UINT64 NewPgContextAddress = (UINT64)ImageBase + InitDataSection->VirtualAddress; // Address in discardable section
454+
CopyWpMem(gPgContext, &NewPgContextAddress, sizeof(NewPgContextAddress));
455+
}
456+
else if (KiSwInterruptPatternAddress != NULL)
457+
{
399458
SetWpMem(KiSwInterruptPatternAddress, sizeof(SigKiSwInterrupt), 0x90); // 11 x nop
459+
}
400460

401461
// Print info
402462
PRINT_KERNEL_PATCH_MSG(L"\r\n Patched KeInitAmd64SpecificState [RVA: 0x%X].\r\n",
@@ -419,7 +479,12 @@ DisablePatchGuard(
419479
(UINT32)(KiMcaDeferredRecoveryServiceCallers[0] - ImageBase),
420480
(UINT32)(KiMcaDeferredRecoveryServiceCallers[1] - ImageBase));
421481
}
422-
if (KiSwInterruptPatternAddress != NULL)
482+
if (gPgContext != NULL)
483+
{
484+
PRINT_KERNEL_PATCH_MSG(L" Patched g_PgContext [RVA: 0x%X].\r\n",
485+
(UINT32)(gPgContext - ImageBase));
486+
}
487+
else if (KiSwInterruptPatternAddress != NULL)
423488
{
424489
PRINT_KERNEL_PATCH_MSG(L" Patched KiSwInterrupt [RVA: 0x%X].\r\n",
425490
(UINT32)(KiSwInterruptPatternAddress - ImageBase));
@@ -788,7 +853,7 @@ PatchNtoskrnl(
788853
}
789854

790855
// Find the INIT and PAGE sections
791-
PEFI_IMAGE_SECTION_HEADER InitSection = NULL, TextSection = NULL, PageSection = NULL;
856+
PEFI_IMAGE_SECTION_HEADER InitSection = NULL, InitDataSection = NULL, TextSection = NULL, PageSection = NULL;
792857
PEFI_IMAGE_SECTION_HEADER Section = IMAGE_FIRST_SECTION(NtHeaders);
793858
for (UINT16 i = 0; i < NtHeaders->FileHeader.NumberOfSections; ++i)
794859
{
@@ -798,6 +863,8 @@ PatchNtoskrnl(
798863

799864
if (AsciiStrCmp(SectionName, "INIT") == 0)
800865
InitSection = Section;
866+
else if (AsciiStrCmp(SectionName, "INITDATA") == 0)
867+
InitDataSection = Section;
801868
else if (AsciiStrCmp(SectionName, ".text") == 0)
802869
TextSection = Section;
803870
else if (AsciiStrCmp(SectionName, "PAGE") == 0)
@@ -806,16 +873,15 @@ PatchNtoskrnl(
806873
Section++;
807874
}
808875

809-
ASSERT(InitSection != NULL);
810-
ASSERT(TextSection != NULL);
811-
ASSERT(PageSection != NULL);
876+
ASSERT(InitSection != NULL && InitDataSection != NULL && TextSection != NULL && PageSection != NULL);
812877

813878
// Patch INIT and .text sections to disable PatchGuard
814879
PRINT_KERNEL_PATCH_MSG(L"[PatchNtoskrnl] Disabling PatchGuard... [INIT RVA: 0x%X - 0x%X]\r\n",
815880
InitSection->VirtualAddress, InitSection->VirtualAddress + InitSection->SizeOfRawData);
816881
Status = DisablePatchGuard(ImageBase,
817882
NtHeaders,
818883
InitSection,
884+
InitDataSection,
819885
TextSection,
820886
BuildNumber);
821887
if (EFI_ERROR(Status))

0 commit comments

Comments
 (0)