Hacking Windows Kernel: How to Dynamically Locate g_CiEnabled and Bypass DSE Like a Pro + Video

Listen to this Post

Featured Image

Introduction:

Driver Signature Enforcement (DSE) is a critical Windows security mechanism that blocks unsigned or tampered kernel-mode drivers from loading, preventing many rootkits and advanced persistence techniques. The global variable `g_CiEnabled` inside `ci.dll` (Code Integrity) controls whether DSE is active; if an attacker can locate and modify this variable in memory, they can disable DSE entirely and load arbitrary malicious drivers. This article dissects a real‑world offensive security project – `g_CiEnabled-Hunter` – that dynamically resolves the address of g_CiEnabled, and provides a step‑by‑step guide to using it, along with defensive countermeasures.

Learning Objectives:

  • Understand the role of `g_CiEnabled` in Windows Driver Signature Enforcement and how kernel‑mode exploitation targets it.
  • Learn to dynamically locate kernel variables using pattern scanning and Windows internal structures without hardcoded offsets.
  • Implement both offensive (disabling DSE for Red Teaming) and defensive (detecting such modifications) techniques using PowerShell, WinDbg, and custom scripts.

You Should Know:

  1. What Is `g_CiEnabled` and Why Attackers Target It

    `g_CiEnabled` is a Boolean (or integer) variable exported by `ci.dll` – the Code Integrity module – that determines if Driver Signature Enforcement is active. When set to 1, DSE is enforced; writing `0` to its address disables DSE until next reboot. Attackers with kernel‑mode code execution (e.g., via a vulnerable driver or a privilege escalation exploit) can overwrite this variable, effectively bypassing one of Windows’ strongest integrity protections. The challenge is that `g_CiEnabled` resides at different addresses across Windows versions and patches. The `g_CiEnabled-Hunter` script uses signature scanning to find it dynamically, making the attack version‑agnostic.

Step‑by‑step guide to understand the concept:

  • On a test VM, load `ci.dll` into a debugger (WinDbg) with `lm m ci` to get its base address.
  • Use `x ci!g_CiEnabled` to display the variable’s offset (if symbols are available). Without symbols, pattern scan for the unique byte sequence that references it.
  • Attackers read the current value: dq <address> L1. A value of `1` means DSE is on.
  • Writing `0` to that address (via `eq` in WinDbg or `MmMapIoSpace` from a driver) disables DSE. After modification, loading an unsigned driver with `sc.exe create` and `net start` will succeed.

2. Using the g_CiEnabled-Hunter Script (GitHub Project)

The project “g_CiEnabled-Hunter” (https://github.com/ring0shady/g_CiEnabled-Hunter) provides a script that automatically resolves the address of `g_CiEnabled` by scanning the `ci.dll` image for a known instruction pattern. It is written for use in kernel‑mode payloads or post‑exploitation tools. The script does not require hardcoded offsets – it works across Windows 10, 11, and Server editions (with minor pattern adjustments).

Step‑by‑step guide to run and integrate the script:

  1. Clone the repository on a Windows development machine (or on your Red Team C2):
    `git clone https://github.com/ring0shady/g_CiEnabled-Hunter.git`
  2. Examine the main file (e.g., `CiHunter.c` or .asm). It typically performs the following:

– Obtains the base address of `ci.dll` in kernel memory using `ZwQuerySystemInformation` or EnumDeviceDrivers.
– Parses the PE headers of the loaded `ci.dll` to locate the `.text` section.
– Searches for a unique byte signature (e.g., `48 8B 0D …` or 80 3D ...) that references `g_CiEnabled` relative to RIP.
– Extracts the relative offset and computes the absolute address.
3. Compile the script as a kernel driver (or shellcode). For testing, use a test‑signed driver with bcdedit /set testsigning on.
4. Load the compiled driver (e.g., `sc.exe create CiHunter binPath= C:\path\to\driver.sys type= kernel` then sc start CiHunter).
5. The driver prints the address of `g_CiEnabled` via `DbgPrint` – capture it with DebugView or kernel debugging.
6. Once the address is known, a separate kernel write primitive can zero it out:

(PUCHAR)g_CiEnabledAddress = 0;

  1. Manual Method: Finding g_CiEnabled with WinDbg (No Script)

If you have kernel debugging access (e.g., via a vulnerable driver or a physical machine), you can locate `g_CiEnabled` manually. This is useful for live memory forensics or when the script cannot be deployed.

Step‑by‑step guide:

  1. Attach WinDbg to the target (local kernel debugging: `bcdedit /debug on` + reboot + WinDbg -kl).

2. Get the base address of `ci.dll`:

`lm m ci` → output example: `fffff801` 3e6a0000 `fffff801` 3e8e3000 `ci` (base = `fffff801` 3e6a0000).
3. Disassemble around known functions that access g_CiEnabled. For example, `CiCheckSignedFile` is a good candidate:

`u ci!CiCheckSignedFile L20`

  1. Look for a `cmp` or `test` instruction against a global variable. On recent Windows 10, you might see:
    `fffff801` 3e6c1a4b 803d `XXXXXXXX 00` cmp byte ptr [ci!g_CiEnabled],0
  2. Extract the address from the `RIP + offset` operand. For manual calculation:
    `? fffff801` 3e6c1a4b + (offset) + 6 (instruction length).
  3. Verify the variable: `db L1` – if it shows `01` or `00` and changing it with `eb` affects DSE, you have located g_CiEnabled.

  4. Bypassing DSE via g_CiEnabled Modification (Offensive Lab Setup)

Once you have the address, disabling DSE is straightforward. However, on a fully patched Windows 10/11, you need kernel write privileges – typically obtained via a Bring Your Own Vulnerable Driver (BYOVD) attack or an exploit like CVE‑2024‑21345 (Windows Kernel Elevation of Privilege). This section demonstrates a controlled lab environment using a self‑signed driver with DSE disabled initially (for testing the concept).

Step‑by‑step guide:

  • Prepare a Windows 10 test VM with Secure Boot off and test signing on:

`bcdedit /set testsigning on` → reboot.

  • Load a vulnerable driver that exposes arbitrary kernel read/write (e.g., Capcom.sys, gdrv.sys). Use tools like `EoPLoadDriver` to communicate with it.
  • Deploy the g_CiEnabled-Hunter to retrieve the address. (Or use manual WinDbg results.)
  • Write a userspace program that uses `DeviceIoControl` to send a kernel write request to the vulnerable driver, targeting the `g_CiEnabled` address:
    // Pseudo-code
    DWORD bytesRet;
    BYTE zero = 0;
    DeviceIoControl(hDriver, IOCTL_WRITE_KERNEL, &g_CiEnabledAddr, sizeof(PVOID), &zero, 1, &bytesRet, NULL);
    
  • Verify DSE is disabled by loading an unsigned driver:
    sc.exe create UnsignedDriver binPath= C:\unsigned.sys type= kernel
    sc.exe start UnsignedDriver
    

    If the start succeeds without error 577 (Windows cannot verify the digital signature), DSE is off.

5. Defensive Hardening: Detecting and Blocking g_CiEnabled Tampering

Defenders can mitigate this attack by monitoring kernel memory writes to ci.dll’s `.data` section and by enforcing Hypervisor‑Protected Code Integrity (HVCI) / Memory Integrity. HVCI runs the kernel in a virtualized environment, making direct writes to `g_CiEnabled` impossible from a compromised kernel.

Step‑by‑step guide for blue teams:

  • Enable HVCI via Group Policy or Windows Security → Device Security → Core isolation → Memory integrity (requires compatible hardware).
  • Monitor for driver load events (Event ID 6: Driver loaded) and check for unsigned drivers even if DSE appears disabled.
  • Use Kernel Patch Protection (KPP) / PatchGuard – on 64‑bit Windows, modifying `g_CiEnabled` can trigger a bug check (CRITICAL_STRUCTURE_CORRUPTION). Attackers must bypass PatchGuard first, which is non‑trivial.
  • Deploy a kernel callback using `CmRegisterCallback` or `PsSetLoadImageNotifyRoutine` to check for modifications to ci.dll. You can compute a hash of the `.data` section periodically and alert on changes.
  • Create a PowerShell detection script that scans kernel memory (using `ReadProcessMemory` on \Device\PhysicalMemory) for the pattern of `g_CiEnabled` and verifies it remains 1. This is invasive but possible in forensics.

6. Linux & Cross‑Platform Context: Equivalent Protections

While this article focuses on Windows, similar concepts exist on Linux (e.g., `lock_kernel` variables, module_signing). Attackers on Linux target `CONFIG_MODULE_SIG_FORCE` or directly modify the `modprobe` path. Understanding dynamic variable resolution is transferable.

Step‑by‑step Linux example:

  • On Linux, kernel module signing enforcement can be bypassed by finding the `module_sig_ok` variable inside `kernel/module.c` (if not hardened).
  • Use `/proc/kallsyms` to locate symbols (if kptr_restrict=0):

`grep module_sig_ok /proc/kallsyms`

  • Write to the address via `/dev/mem` or a kernel exploit.

For Red Teams, cross‑compile the same scanning technique: parse `System.map` or use `kprobes` to resolve addresses dynamically.

7. Legal and Ethical Considerations

Modifying `g_CiEnabled` on a production system without authorization violates laws (CFAA, Computer Misuse Act) and corporate policies. This technique should only be exercised in isolated lab environments, authorized penetration tests, or capture‑the‑flag competitions. Always obtain written permission before attempting kernel‑level bypasses.

What Undercode Say:

  • Key Takeaway 1: Dynamically resolving kernel variables like `g_CiEnabled` makes exploits version‑resistant and far more dangerous than hardcoded offset attacks – defenders must adopt HVCI and PatchGuard to raise the cost.
  • Key Takeaway 2: Scripts like `g_CiEnabled-Hunter` democratize kernel hacking, lowering the skill barrier for Red Teams, but also highlight the need for continuous memory integrity monitoring.

Analysis: The project demonstrates a classic offensive security pattern – locate a security‑critical flag in memory and flip it. While DSE bypass via `g_CiEnabled` has been known since Windows 8, the dynamic approach ensures reliability across thousands of Windows builds. From a defender’s perspective, relying solely on DSE is insufficient; defense‑in‑depth with HVCI, exploit mitigation (CFG, SMEP, SMAP), and EDR kernel callbacks is mandatory. Additionally, PatchGuard’s role is often misunderstood – it does not protect `g_CiEnabled` directly, only certain structures. Thus, a sophisticated attacker can still disable DSE if they avoid PatchGuard‑monitored regions. The future lies in virtualization‑based security, where even kernel memory writes can be trapped.

Prediction:

Within the next two years, Microsoft will deprecate the ability for non‑hypervisor kernel code to modify `g_CiEnabled` entirely, likely by moving DSE status into a Secure Kernel (Isolated User Mode) or by requiring attestation via TPM before kernel modules load. Meanwhile, offensive tooling will shift to abusing legitimate signed drivers (BYOVD) combined with return‑oriented programming (ROP) to flip the variable without triggering PatchGuard. Blue teams will need to adopt runtime memory attestation and integrity monitoring for `ci.dll` as a standard detection use case. The cat‑and‑mouse game between DSE bypass and HVCI will escalate, but ultimately hardware‑rooted security (AMD SEV‑SNP, Intel TDX) will become the baseline for critical infrastructure.

▶️ Related Video (80% Match):

🎯Let’s Practice For Free:

IT/Security Reporter URL:

Reported By: Hossam Shady – Hackers Feeds
Extra Hub: Undercode MoN
Basic Verification: Pass ✅

🔐JOIN OUR CYBER WORLD [ CVE News • HackMonitor • UndercodeNews ]

💬 Whatsapp | 💬 Telegram

📢 Follow UndercodeTesting & Stay Tuned:

𝕏 formerly Twitter 🐦 | @ Threads | 🔗 Linkedin | 🦋BlueSky