Bypassing PatchGuard at runtime

February 1th, 2023. The PatchGuard research lab was performed on Windows 10 Pro 22H2 (Build 19045.2364). The article is provided for educational and information purposes.

Along with WinDbg Preview + KDNET. HEX DEREF software played a key role in finding and verifying the PatchGuard context.

PatchGuard

PatchGuard is a mechanism that protects kernel critical structures from being modified. If a modification is detected. The protection will shutdown the system. The PatchGuard routine executes periodically, if you manage to revert the change before the next check, there should not be a bug check. The DSE bypass implemented in the discussion thread backups that claim. In the later parts of the article. The PatchGuard is referred to as kernel patch protection (KPP). You can comment on the article in Patchguard bypass discussion.

The bypass is based on the author's C/C++ kernel driver code, windows kernel internal research and reference articles found on the internet. For obvious reasons the source code of the driver is not publicly disclosed. If you're interested to purchase the source code or compiled version of the driver. Leave a comment in the discussion thread above.

Any user-mode exploit that can elevate privileges to administrator with this payload is enough. Or if the user runs an executable with admin privileges. A malicious user could use the bypass for malware purposes.

It's worth mentioning that Windows Defender did not detected when the PatchGuard integrity checks were disabled on the fly. The author was even able to apply a DSE bypass on the fly that allows a malicious user to load an unsigned kernel driver without the need to enable test signing. The latter one can be applied on the fly without the need to disable PatchGuard. A common attack scenario is to use a vulnerable kernel driver with a valid EV certificate to gain a low-level execution privileges.

PatchGuard bypass at runtime without an EFI bootkit

It's possible to bypass kernel patch protection (KPP) on the fly. The author of the article was able to develop a kernel driver that disables PatchGuard integrity checks at runtime.

Proof of uptime without PatchGuard CRITICAL_STRUCTURE_CORRUPTION (0x109) after applying DKOM in the kernel that is subject to BSDO without disabling. If you apply DKOM in the kernel improperly. A PatchGuard KERNEL_SECURITY_CHECK_FAILURE (0x139) BSDO may occur.


PatchGuard analysis tips

HEX DEREF software can, among many other, perform the kernel functions mentioned below:

  • Enumerate and stackwalk system threads
  • Any system thread executing code outside the loaded modules list (e.g not backed by a valid module) is a possible PatchGuard or malware thread
  • Enumerate the kernel timer list and find a DPC with characteristics of being a PG timer

There is nothing preventing a malware that already gained Ring 0 code execution privileges from disabling PatchGuard. The functionality in HEX DEREF kernel driver is designed to find a sign of an advanced malware in kernel memory using the kernel API's and other methods.

Patchguard context does not exist in the kernel memory if you use an EFI based bootkit such as EfiGuard to disable KPP at boot time. This can be easily detected by invoking an INT 20h from the kernel driver that triggers the PatchGuard verification routine.

UCHAR myFunc[3] = { 0xCD, 0x20, 0xC3 }; // INT 20h + RETN

typedef PVOID(*pInvokePatchGuardRoutine)(PVOID);

static const pInvokePatchGuardRoutine InvokePatchGuardVerificationRoutine = (pInvokePatchGuardRoutine)&myFunc;

The another way is to check that does the pointer exist to the global context of PatchGuard (in other words, to check was the kernel patch protection initialized at boot time) as shown in the article. The bypass defeats also PatchGuard integrity check routine that can be manually invoked. The author verified that by byte patching the KeBugCheckEx routine with RETN. If PatchGuard integrity checks are running. The result is usually a BSDO in about a minute.

As shown in below screenshot. The gray arrow points to PatchGuard's global context. This pointer is not present if kernel patch protection is disabled at boot time. The signature below was generated with HEX DEREF. The software includes both a signature scanner and a generator for user mode processes and the kernel.


PatchGuard verification routine

After an initialization of PatchGuard, a pointer to the global context of PatchGuard will appear in the kernel memory, which looks like this in the memory viewer of HEX DEREF

You cannot know the base address of the pointer beforehand because of kernel address space layout randomization (KASLR). This protection can however be easily bypassed using a signature.

PatchGuard global context

It is worth mentioning that after PatchGuard has been initialized. It's context is most of the time in an encrypted form making it difficult to find it in kernel memory. Depending on the initialized method, the PatchGuard context mutates with randomly generated constants every time when the timer gets fired, defeating any prior decrypting method.

PatchGuard encrypted context

Disabling PatchGuard with a kernel mechanism

To disable PatchGuard in the simplest way, all you have to do is to enable kernel debugging as per the instructions in the article: KDNET Kernel Debugging. If the kernel debugger is attached at boot, PatchGuard is not initialized by design.

The trick is to shutdown the debuggee after you've enabled kernel debugging and leave a KDNET to a "Waiting to reconnect..." on the other machine. Once you start the debuggee again, a KD will be attached at startup and PatchGuard will not initialize.

KDNET

The other benefit is that you can load an unsigned driver as well while the KD is attached without the need to enable test signing. Once you detach the kernel debugger (KD). Code Integrity (CI) will restore itself to the original state.

The only downside is that you need two computers (at least one physical computer + VM) if you want to use KDNET to disable PatchGuard. Most of anti-cheats refuses to start a game if they detect that the kernel debugging is enabled.

CREDITS: Satoshi Tanda