@@ -52,17 +52,18 @@ STATIC CONST UINT8 SigKiMcaDeferredRecoveryService[] = {
52
52
// If int 20h is issued from kernel mode, the PatchGuard verification routine KiSwInterruptDispatch is called.
53
53
STATIC CONST UINT8 SigKiSwInterrupt [] = {
54
54
0xFB , // sti
55
- 0x48 , 0x8D , 0xCC , 0xCC , // lea rcx, XX
55
+ 0x48 , 0x8D , 0xCC , 0xCC , // lea REG, [REG-XX]
56
56
0xE8 , 0xCC , 0xCC , 0xCC , 0xCC , // call KiSwInterruptDispatch
57
57
0xFA // cli
58
58
};
59
+ STATIC CONST UINTN SigKiSwInterruptCallOffset = 5 , SigKiSwInterruptCliOffset = 10 ;
59
60
60
61
// Signature for nt!SeCodeIntegrityQueryInformation, called through NtQuerySystemInformation(SystemCodeIntegrityInformation).
61
62
// This function has actually existed since Vista in various forms, sometimes (8/8.1/early 10) inlined in ExpQuerySystemInformation.
62
63
// This signature is only for the Windows 10 RS3+ version. I could add more signatures but this is a pretty superficial patch anyway.
63
64
STATIC CONST UINT8 SigSeCodeIntegrityQueryInformation [] = {
64
65
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
66
67
0x4D , 0x8B , 0xC8 , // mov r9, r8
67
68
0x4C , 0x8B , 0xD1 , // mov r10, rcx
68
69
0x74 , 0xCC // jz XX
@@ -88,6 +89,7 @@ DisablePatchGuard(
88
89
IN UINT8 * ImageBase ,
89
90
IN PEFI_IMAGE_NT_HEADERS NtHeaders ,
90
91
IN PEFI_IMAGE_SECTION_HEADER InitSection ,
92
+ IN PEFI_IMAGE_SECTION_HEADER InitDataSection ,
91
93
IN PEFI_IMAGE_SECTION_HEADER TextSection ,
92
94
IN UINT16 BuildNumber
93
95
)
@@ -358,11 +360,21 @@ DisablePatchGuard(
358
360
}
359
361
}
360
362
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 ;
363
370
if (BuildNumber >= 10240 )
364
371
{
372
+ StartRva = TextSection -> VirtualAddress ;
373
+ SizeOfRawData = TextSection -> SizeOfRawData ;
374
+ StartVa = ImageBase + StartRva ;
375
+
365
376
PRINT_KERNEL_PATCH_MSG (L"== Searching for nt!KiSwInterrupt pattern in .text ==\r\n" );
377
+ UINT8 * KiSwInterruptDispatchAddress = NULL ;
366
378
CONST EFI_STATUS FindKiSwInterruptStatus = FindPattern (SigKiSwInterrupt ,
367
379
0xCC ,
368
380
sizeof (SigKiSwInterrupt ),
@@ -371,14 +383,55 @@ DisablePatchGuard(
371
383
(VOID * * )& KiSwInterruptPatternAddress );
372
384
if (EFI_ERROR (FindKiSwInterruptStatus ))
373
385
{
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.
375
387
// However note that in this case, any attempt to issue int 20h from kernel mode later will result in a bugcheck.
376
388
PRINT_KERNEL_PATCH_MSG (L" Failed to find KiSwInterrupt. Skipping patch.\r\n" );
377
389
}
378
390
else
379
391
{
392
+ ASSERT (SigKiSwInterrupt [SigKiSwInterruptCallOffset ] == 0xE8 && SigKiSwInterrupt [SigKiSwInterruptCliOffset ] == 0xFA );
393
+ CONST INT32 Relative = * (INT32 * )(KiSwInterruptPatternAddress + SigKiSwInterruptCallOffset + 1 );
394
+ KiSwInterruptDispatchAddress = KiSwInterruptPatternAddress + SigKiSwInterruptCliOffset + Relative ;
395
+
380
396
PRINT_KERNEL_PATCH_MSG (L" Found KiSwInterrupt pattern at 0x%llX.\r\n" , (UINTN )KiSwInterruptPatternAddress );
381
397
}
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
+ }
382
435
}
383
436
384
437
// We have all the addresses we need; now do the actual patching.
@@ -395,8 +448,15 @@ DisablePatchGuard(
395
448
CopyWpMem (KiMcaDeferredRecoveryServiceCallers [0 ], & No , sizeof (No ));
396
449
CopyWpMem (KiMcaDeferredRecoveryServiceCallers [1 ], & No , sizeof (No ));
397
450
}
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
+ {
399
458
SetWpMem (KiSwInterruptPatternAddress , sizeof (SigKiSwInterrupt ), 0x90 ); // 11 x nop
459
+ }
400
460
401
461
// Print info
402
462
PRINT_KERNEL_PATCH_MSG (L"\r\n Patched KeInitAmd64SpecificState [RVA: 0x%X].\r\n" ,
@@ -419,7 +479,12 @@ DisablePatchGuard(
419
479
(UINT32 )(KiMcaDeferredRecoveryServiceCallers [0 ] - ImageBase ),
420
480
(UINT32 )(KiMcaDeferredRecoveryServiceCallers [1 ] - ImageBase ));
421
481
}
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 )
423
488
{
424
489
PRINT_KERNEL_PATCH_MSG (L" Patched KiSwInterrupt [RVA: 0x%X].\r\n" ,
425
490
(UINT32 )(KiSwInterruptPatternAddress - ImageBase ));
@@ -788,7 +853,7 @@ PatchNtoskrnl(
788
853
}
789
854
790
855
// 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 ;
792
857
PEFI_IMAGE_SECTION_HEADER Section = IMAGE_FIRST_SECTION (NtHeaders );
793
858
for (UINT16 i = 0 ; i < NtHeaders -> FileHeader .NumberOfSections ; ++ i )
794
859
{
@@ -798,6 +863,8 @@ PatchNtoskrnl(
798
863
799
864
if (AsciiStrCmp (SectionName , "INIT" ) == 0 )
800
865
InitSection = Section ;
866
+ else if (AsciiStrCmp (SectionName , "INITDATA" ) == 0 )
867
+ InitDataSection = Section ;
801
868
else if (AsciiStrCmp (SectionName , ".text" ) == 0 )
802
869
TextSection = Section ;
803
870
else if (AsciiStrCmp (SectionName , "PAGE" ) == 0 )
@@ -806,16 +873,15 @@ PatchNtoskrnl(
806
873
Section ++ ;
807
874
}
808
875
809
- ASSERT (InitSection != NULL );
810
- ASSERT (TextSection != NULL );
811
- ASSERT (PageSection != NULL );
876
+ ASSERT (InitSection != NULL && InitDataSection != NULL && TextSection != NULL && PageSection != NULL );
812
877
813
878
// Patch INIT and .text sections to disable PatchGuard
814
879
PRINT_KERNEL_PATCH_MSG (L"[PatchNtoskrnl] Disabling PatchGuard... [INIT RVA: 0x%X - 0x%X]\r\n" ,
815
880
InitSection -> VirtualAddress , InitSection -> VirtualAddress + InitSection -> SizeOfRawData );
816
881
Status = DisablePatchGuard (ImageBase ,
817
882
NtHeaders ,
818
883
InitSection ,
884
+ InitDataSection ,
819
885
TextSection ,
820
886
BuildNumber );
821
887
if (EFI_ERROR (Status ))
0 commit comments