diff --git a/.claude/settings.local.json b/.claude/settings.local.json
index 2ad811c..672c19a 100644
--- a/.claude/settings.local.json
+++ b/.claude/settings.local.json
@@ -9,7 +9,8 @@
"Bash(python -c:*)",
"WebSearch",
"Bash(python:*)",
- "Bash(du:*)"
+ "Bash(du:*)",
+ "Bash(git -C \"D:\\\\Users\\\\21405\\\\source\\\\repos\\\\Filerestore_CLI\" log --oneline -3 master)"
]
}
}
diff --git a/.gitignore b/.gitignore
index 707fb5e..9b92e92 100644
--- a/.gitignore
+++ b/.gitignore
@@ -33,10 +33,8 @@ x86/
*.VC.db-wal
*.VC.opendb
-# Build directories
-# (已统一输出到解决方案根目录的 x64/)
-x64/
-
+#development notes
+dev_notes/
# NuGet packages (can be restored via nuget restore)
packages/
*.nupkg
@@ -111,5 +109,6 @@ Filerestore_CLI/deps/ftxui/build/
Filerestore_CLI/deps/ftxui/.cache/
Filerestore_CLI/deps/ftxui/cmake-build-*/
-# Developer notes (private, not tracked)
-dev_notes/
\ No newline at end of file
+# Kernel driver build outputs
+*.sys
+*.cer
\ No newline at end of file
diff --git a/Filerestore_CLI/src/fileRestore/MonitorDaemon.cpp b/Filerestore_CLI/src/fileRestore/MonitorDaemon.cpp
index 8b12a28..dcf1674 100644
--- a/Filerestore_CLI/src/fileRestore/MonitorDaemon.cpp
+++ b/Filerestore_CLI/src/fileRestore/MonitorDaemon.cpp
@@ -233,6 +233,7 @@ void MonitorDaemon::DetachSharedMemory() {
bool MonitorDaemon::ReadState(MonitorSharedState& out) {
if (!sharedPtr_) return false;
if (sharedPtr_->magic != MONITOR_SHARED_MAGIC) return false;
+ if (sharedPtr_->version != MONITOR_SHARED_VERSION) return false;
// seqlock 读端:重试直到拿到一致快照
for (int retry = 0; retry < 100; retry++) {
@@ -258,6 +259,9 @@ bool MonitorDaemon::ReadState(MonitorSharedState& out) {
}
// 超过重试上限(不应发生),仍返回最后一次拷贝
memcpy(&out, (const void*)sharedPtr_, sizeof(MonitorSharedState));
+ SpinLockRelease(&sharedPtr_->spinLock);
+
+ // 拷贝完成后释放锁,out 是本地副本,后续操作无需锁
return true;
}
diff --git a/Filerestore_sys/Filerestore_sys/Filerestore_sys.inf b/Filerestore_sys/Filerestore_sys/Filerestore_sys.inf
new file mode 100644
index 0000000..996f5d9
--- /dev/null
+++ b/Filerestore_sys/Filerestore_sys/Filerestore_sys.inf
@@ -0,0 +1,84 @@
+;
+; Filerestore_sys.inf - Minifilter driver installation
+;
+
+[Version]
+Signature = "$WINDOWS NT$"
+Class = "ActivityMonitor"
+ClassGuid = {b86dff51-a31e-4bac-b3cf-e8cfe75c9fc2}
+Provider = %ManufacturerName%
+CatalogFile = Filerestore_sys.cat
+DriverVer = 02/19/2026,1.0.0.0
+PnpLockdown = 1
+
+[DestinationDirs]
+DefaultDestDir = 12 ; %windir%\system32\drivers
+FileRestoreMon.CopyFiles = 12
+
+[SourceDisksNames]
+1 = %DiskName%,,,""
+
+[SourceDisksFiles]
+Filerestore_sys.sys = 1,,
+
+;*****************************************
+; DefaultInstall Section (non-PnP)
+;*****************************************
+
+[DefaultInstall.NT$ARCH$]
+OptionDesc = %ServiceDescription%
+CopyFiles = FileRestoreMon.CopyFiles
+
+[DefaultInstall.NT$ARCH$.Services]
+AddService = FileRestoreMon,,FileRestoreMon.Service
+
+[DefaultUninstall.NT$ARCH$]
+LegacyUninstall = 1
+DelFiles = FileRestoreMon.CopyFiles
+
+[DefaultUninstall.NT$ARCH$.Services]
+DelService = FileRestoreMon,0x200 ; SPSVCINST_STOPSERVICE
+
+;*****************************************
+; File copy
+;*****************************************
+
+[FileRestoreMon.CopyFiles]
+Filerestore_sys.sys
+
+;*****************************************
+; Service installation
+;*****************************************
+
+[FileRestoreMon.Service]
+DisplayName = %ServiceName%
+Description = %ServiceDescription%
+ServiceType = 2 ; SERVICE_FILE_SYSTEM_DRIVER
+StartType = 3 ; SERVICE_DEMAND_START
+ErrorControl = 1 ; SERVICE_ERROR_NORMAL
+ServiceBinary = %12%\Filerestore_sys.sys
+LoadOrderGroup = "FSFilter Activity Monitor"
+AddReg = FileRestoreMon.AddRegistry
+
+;*****************************************
+; Minifilter instance registration
+;*****************************************
+
+[FileRestoreMon.AddRegistry]
+HKR,"Instances","DefaultInstance",0x00000000,%DefaultInstance%
+HKR,"Instances\"%Instance1.Name%,"Altitude",0x00000000,%Instance1.Altitude%
+HKR,"Instances\"%Instance1.Name%,"Flags",0x00010001,%Instance1.Flags%
+
+;*****************************************
+; Strings
+;*****************************************
+
+[Strings]
+ManufacturerName = "FileRestore"
+ServiceName = "FileRestoreMon"
+ServiceDescription = "FileRestore Monitor Minifilter Driver"
+DiskName = "FileRestoreMon Installation Disk"
+DefaultInstance = "FileRestoreMon Instance"
+Instance1.Name = "FileRestoreMon Instance"
+Instance1.Altitude = "370100"
+Instance1.Flags = 0x0 ; automatic attachment
diff --git a/Filerestore_sys/Filerestore_sys/Filerestore_sys.vcxproj b/Filerestore_sys/Filerestore_sys/Filerestore_sys.vcxproj
new file mode 100644
index 0000000..f48cde3
--- /dev/null
+++ b/Filerestore_sys/Filerestore_sys/Filerestore_sys.vcxproj
@@ -0,0 +1,168 @@
+
+
+
+
+
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+ Debug
+ ARM64
+
+
+ Release
+ ARM64
+
+
+
+ {4E97BA7A-9203-3699-D57F-5596CAD720A0}
+ {1bc93793-694f-48fe-9372-81e2b05556fd}
+ v4.5
+ 12.0
+ Debug
+ x64
+ Filerestore_sys
+
+
+
+ Windows10
+ true
+ WindowsKernelModeDriver10.0
+ Driver
+ WDM
+ Universal
+
+
+ Windows10
+ false
+ WindowsKernelModeDriver10.0
+ Driver
+ WDM
+ Universal
+
+
+ Windows10
+ true
+ WindowsKernelModeDriver10.0
+ Driver
+ WDM
+ Universal
+
+
+ Windows10
+ false
+ WindowsKernelModeDriver10.0
+ Driver
+ WDM
+ Universal
+
+
+
+
+
+
+
+
+
+ false
+ false
+
+
+ DbgengKernelDebugger
+
+
+ DbgengKernelDebugger
+
+
+ DbgengKernelDebugger
+
+
+ DbgengKernelDebugger
+
+
+
+ sha256
+
+
+ %(PreprocessorDefinitions)
+ %(AdditionalIncludeDirectories)
+
+
+ %(AdditionalDependencies);fltMgr.lib
+
+
+
+
+ sha256
+
+
+ %(PreprocessorDefinitions)
+ %(AdditionalIncludeDirectories)
+
+
+ %(AdditionalDependencies);fltMgr.lib
+
+
+
+
+ sha256
+
+
+ %(PreprocessorDefinitions)
+ %(AdditionalIncludeDirectories)
+
+
+ %(AdditionalDependencies);fltMgr.lib
+
+
+
+
+ sha256
+
+
+ %(PreprocessorDefinitions)
+ %(AdditionalIncludeDirectories)
+
+
+ %(AdditionalDependencies);fltMgr.lib
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 这台计算机上缺少此项目引用的 NuGet 程序包。使用“NuGet 程序包还原”可下载这些程序包。有关更多信息,请参见 http://go.microsoft.com/fwlink/?LinkID=322105。缺少的文件是 {0}。
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Filerestore_sys/Filerestore_sys/Filerestore_sys.vcxproj.filters b/Filerestore_sys/Filerestore_sys/Filerestore_sys.vcxproj.filters
new file mode 100644
index 0000000..ee59303
--- /dev/null
+++ b/Filerestore_sys/Filerestore_sys/Filerestore_sys.vcxproj.filters
@@ -0,0 +1,48 @@
+
+
+
+
+ {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
+ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx
+
+
+ {93995380-89BD-4b04-88EB-625FBE52EBFB}
+ h;hpp;hxx;hm;inl;inc;xsd
+
+
+ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
+ rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
+
+
+ {8E41214B-6785-4CFE-B992-037D68949A14}
+ inf;inv;inx;mof;mc;
+
+
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+
+
+ Header Files
+
+
+ Header Files
+
+
+
+
+ Driver Files
+
+
+
+
+
+
diff --git a/Filerestore_sys/Filerestore_sys/common.h b/Filerestore_sys/Filerestore_sys/common.h
new file mode 100644
index 0000000..fc0cbe9
--- /dev/null
+++ b/Filerestore_sys/Filerestore_sys/common.h
@@ -0,0 +1,99 @@
+/*
+ * common.h - Shared definitions between kernel driver and user-mode client
+ *
+ * This header is included by both the minifilter driver (kernel) and the
+ * user-mode application (Filerestore_CLI). Keep it pure C compatible.
+ */
+
+#ifndef _FILERESTORE_COMMON_H_
+#define _FILERESTORE_COMMON_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Communication port name */
+#define FILERESTORE_PORT_NAME L"\\FileRestoreMonPort"
+
+/* Command codes (user -> kernel) */
+#define CMD_START_MONITOR 1
+#define CMD_STOP_MONITOR 2
+#define CMD_GET_EVENTS 3
+#define CMD_GET_STATS 4
+#define CMD_GET_VERSION 5
+
+/* Delete types */
+#define DELETE_TYPE_PERMANENT 0
+#define DELETE_TYPE_RECYCLE 1
+
+/* Limits */
+#define MAX_LCN_ENTRIES 64
+#define MAX_FILE_PATH_CHARS 520
+#define DRIVER_VERSION_STRING "1.0.0"
+
+/* Connection authentication */
+#define FILERESTORE_CONNECTION_MAGIC 0x46524D43 /* 'FRMC' */
+#define FILERESTORE_PROTOCOL_VERSION 1
+#define FILERESTORE_CLIENT_IMAGE_NAME L"\\Filerestore_CLI.exe"
+
+/* Connection context (user -> kernel, passed via FilterConnectCommunicationPort) */
+typedef struct _CONNECTION_CONTEXT {
+ ULONG Magic; /* must be FILERESTORE_CONNECTION_MAGIC */
+ ULONG Version; /* must be FILERESTORE_PROTOCOL_VERSION */
+ ULONG ProcessId; /* caller PID (kernel can cross-validate) */
+ ULONG Reserved; /* alignment padding */
+} CONNECTION_CONTEXT, *PCONNECTION_CONTEXT;
+
+/* ===== Structures ===== */
+
+/* LCN (Logical Cluster Number) entry for file extent mapping */
+typedef struct _LCN_ENTRY {
+ ULONGLONG StartLCN;
+ ULONG ClusterCount;
+ ULONG Reserved;
+} LCN_ENTRY, *PLCN_ENTRY;
+
+/* Single delete event notification */
+typedef struct _DELETE_NOTIFICATION {
+ LARGE_INTEGER Timestamp;
+ ULONG ProcessId;
+ ULONG DeleteType;
+ LARGE_INTEGER FileSize;
+ LARGE_INTEGER AllocationSize;
+ LARGE_INTEGER CreationTime;
+ LARGE_INTEGER LastWriteTime;
+ ULONG LCNCount;
+ LCN_ENTRY LCNEntries[MAX_LCN_ENTRIES];
+ USHORT FileNameLength; /* in bytes */
+ WCHAR FileName[MAX_FILE_PATH_CHARS];
+} DELETE_NOTIFICATION, *PDELETE_NOTIFICATION;
+
+/* Command message (user -> kernel) */
+typedef struct _COMMAND_MESSAGE {
+ ULONG Command;
+} COMMAND_MESSAGE, *PCOMMAND_MESSAGE;
+
+/* Events reply header (kernel -> user, for CMD_GET_EVENTS) */
+typedef struct _EVENTS_REPLY {
+ ULONG EventCount;
+ DELETE_NOTIFICATION Events[1]; /* variable-length array */
+} EVENTS_REPLY, *PEVENTS_REPLY;
+
+/* Statistics reply (kernel -> user, for CMD_GET_STATS) */
+typedef struct _STATS_REPLY {
+ ULONG TotalEvents;
+ ULONG PendingEvents;
+ ULONG DroppedEvents;
+ ULONG MonitorActive;
+} STATS_REPLY, *PSTATS_REPLY;
+
+/* Version reply (kernel -> user, for CMD_GET_VERSION) */
+typedef struct _VERSION_REPLY {
+ CHAR Version[64];
+} VERSION_REPLY, *PVERSION_REPLY;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _FILERESTORE_COMMON_H_ */
diff --git a/Filerestore_sys/Filerestore_sys/communication.c b/Filerestore_sys/Filerestore_sys/communication.c
new file mode 100644
index 0000000..24b97f4
--- /dev/null
+++ b/Filerestore_sys/Filerestore_sys/communication.c
@@ -0,0 +1,348 @@
+/*
+ * communication.c - FltMgr communication port management and event buffering
+ */
+
+#include "driver.h"
+
+/* Forward declarations for port callbacks */
+static NTSTATUS
+ConnectNotifyCallback(
+ _In_ PFLT_PORT ClientPort,
+ _In_opt_ PVOID ServerPortCookie,
+ _In_reads_bytes_opt_(SizeOfContext) PVOID ConnectionContext,
+ _In_ ULONG SizeOfContext,
+ _Outptr_result_maybenull_ PVOID *ConnectionCookie
+ );
+
+static VOID
+DisconnectNotifyCallback(
+ _In_opt_ PVOID ConnectionCookie
+ );
+
+static NTSTATUS
+MessageNotifyCallback(
+ _In_opt_ PVOID PortCookie,
+ _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer,
+ _In_ ULONG InputBufferLength,
+ _Out_writes_bytes_to_opt_(OutputBufferLength, *ReturnOutputBufferLength) PVOID OutputBuffer,
+ _In_ ULONG OutputBufferLength,
+ _Out_ PULONG ReturnOutputBufferLength
+ );
+
+/* ===== SetupCommunicationPort ===== */
+
+NTSTATUS
+SetupCommunicationPort(VOID)
+{
+ NTSTATUS status;
+ UNICODE_STRING portName;
+ PSECURITY_DESCRIPTOR sd = NULL;
+ OBJECT_ATTRIBUTES oa;
+
+ RtlInitUnicodeString(&portName, FILERESTORE_PORT_NAME);
+
+ /* Build a security descriptor that only allows admin access */
+ status = FltBuildDefaultSecurityDescriptor(&sd, FLT_PORT_ALL_ACCESS);
+ if (!NT_SUCCESS(status)) {
+ return status;
+ }
+
+ InitializeObjectAttributes(
+ &oa,
+ &portName,
+ OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
+ NULL,
+ sd
+ );
+
+ status = FltCreateCommunicationPort(
+ g_Context.FilterHandle,
+ &g_Context.ServerPort,
+ &oa,
+ NULL, /* ServerPortCookie */
+ ConnectNotifyCallback,
+ DisconnectNotifyCallback,
+ MessageNotifyCallback,
+ 1 /* MaxConnections: single client */
+ );
+
+ FltFreeSecurityDescriptor(sd);
+ return status;
+}
+
+/* ===== VerifyCallerImage ===== */
+
+/* ZwQueryInformationProcess is not declared in standard WDK headers */
+NTSYSAPI NTSTATUS NTAPI ZwQueryInformationProcess(
+ _In_ HANDLE ProcessHandle,
+ _In_ PROCESSINFOCLASS ProcessInformationClass,
+ _Out_writes_bytes_(ProcessInformationLength) PVOID ProcessInformation,
+ _In_ ULONG ProcessInformationLength,
+ _Out_opt_ PULONG ReturnLength
+ );
+
+/*
+ * VerifyCallerImage - Verify the calling process image name matches the
+ * expected client executable. Uses ZwQueryInformationProcess(ProcessImageFileName)
+ * to get the NT path, then checks if the path suffix matches
+ * FILERESTORE_CLIENT_IMAGE_NAME.
+ */
+static NTSTATUS VerifyCallerImage(VOID)
+{
+ NTSTATUS status;
+ UCHAR buffer[512];
+ ULONG returnedLength;
+ PUNICODE_STRING imagePath;
+ UNICODE_STRING expectedSuffix;
+ UNICODE_STRING actualSuffix;
+ USHORT suffixBytes;
+
+ status = ZwQueryInformationProcess(
+ NtCurrentProcess(),
+ ProcessImageFileName, /* = 27 */
+ buffer,
+ sizeof(buffer),
+ &returnedLength
+ );
+ if (!NT_SUCCESS(status)) {
+ return status;
+ }
+
+ imagePath = (PUNICODE_STRING)buffer;
+ RtlInitUnicodeString(&expectedSuffix, FILERESTORE_CLIENT_IMAGE_NAME);
+
+ suffixBytes = expectedSuffix.Length;
+ if (imagePath->Length < suffixBytes) {
+ return STATUS_ACCESS_DENIED;
+ }
+
+ /* Extract the suffix of the image path matching the expected length */
+ actualSuffix.Buffer = (PWCH)((PUCHAR)imagePath->Buffer +
+ imagePath->Length - suffixBytes);
+ actualSuffix.Length = suffixBytes;
+ actualSuffix.MaximumLength = suffixBytes;
+
+ /* Case-insensitive comparison */
+ if (RtlCompareUnicodeString(&actualSuffix, &expectedSuffix, TRUE) != 0) {
+ return STATUS_ACCESS_DENIED;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+/* ===== ConnectNotifyCallback ===== */
+
+static NTSTATUS
+ConnectNotifyCallback(
+ _In_ PFLT_PORT ClientPort,
+ _In_opt_ PVOID ServerPortCookie,
+ _In_reads_bytes_opt_(SizeOfContext) PVOID ConnectionContext,
+ _In_ ULONG SizeOfContext,
+ _Outptr_result_maybenull_ PVOID *ConnectionCookie
+ )
+{
+ PCONNECTION_CONTEXT ctx;
+
+ UNREFERENCED_PARAMETER(ServerPortCookie);
+
+ /* 1. Validate connection context (handshake) */
+ if (ConnectionContext == NULL ||
+ SizeOfContext < sizeof(CONNECTION_CONTEXT)) {
+ return STATUS_ACCESS_DENIED;
+ }
+
+ ctx = (PCONNECTION_CONTEXT)ConnectionContext;
+ if (ctx->Magic != FILERESTORE_CONNECTION_MAGIC ||
+ ctx->Version != FILERESTORE_PROTOCOL_VERSION) {
+ return STATUS_ACCESS_DENIED;
+ }
+
+ /* 2. Verify caller process image name */
+ if (!NT_SUCCESS(VerifyCallerImage())) {
+ return STATUS_ACCESS_DENIED;
+ }
+
+ g_Context.ClientPort = ClientPort;
+ *ConnectionCookie = NULL;
+ return STATUS_SUCCESS;
+}
+
+/* ===== DisconnectNotifyCallback ===== */
+
+static VOID
+DisconnectNotifyCallback(
+ _In_opt_ PVOID ConnectionCookie
+ )
+{
+ UNREFERENCED_PARAMETER(ConnectionCookie);
+
+ if (g_Context.ClientPort != NULL) {
+ FltCloseClientPort(g_Context.FilterHandle, &g_Context.ClientPort);
+ g_Context.ClientPort = NULL;
+ }
+
+ /* Stop monitoring when client disconnects */
+ InterlockedExchange(&g_Context.MonitorActive, 0);
+}
+
+/* ===== MessageNotifyCallback ===== */
+
+static NTSTATUS
+MessageNotifyCallback(
+ _In_opt_ PVOID PortCookie,
+ _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer,
+ _In_ ULONG InputBufferLength,
+ _Out_writes_bytes_to_opt_(OutputBufferLength, *ReturnOutputBufferLength) PVOID OutputBuffer,
+ _In_ ULONG OutputBufferLength,
+ _Out_ PULONG ReturnOutputBufferLength
+ )
+{
+ PCOMMAND_MESSAGE cmdMsg;
+ KIRQL oldIrql;
+ LONG count;
+ LONG tail;
+ LONG i;
+
+ UNREFERENCED_PARAMETER(PortCookie);
+
+ *ReturnOutputBufferLength = 0;
+
+ if (InputBuffer == NULL || InputBufferLength < sizeof(COMMAND_MESSAGE)) {
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ cmdMsg = (PCOMMAND_MESSAGE)InputBuffer;
+
+ switch (cmdMsg->Command) {
+
+ case CMD_START_MONITOR:
+ InterlockedExchange(&g_Context.MonitorActive, 1);
+ return STATUS_SUCCESS;
+
+ case CMD_STOP_MONITOR:
+ InterlockedExchange(&g_Context.MonitorActive, 0);
+ return STATUS_SUCCESS;
+
+ case CMD_GET_EVENTS:
+ {
+ PEVENTS_REPLY reply;
+ ULONG replySize;
+
+ if (OutputBuffer == NULL || OutputBufferLength < sizeof(EVENTS_REPLY)) {
+ return STATUS_BUFFER_TOO_SMALL;
+ }
+
+ reply = (PEVENTS_REPLY)OutputBuffer;
+
+ KeAcquireSpinLock(&g_Context.BufferLock, &oldIrql);
+
+ count = g_Context.EventCount;
+ if (count == 0) {
+ KeReleaseSpinLock(&g_Context.BufferLock, oldIrql);
+ reply->EventCount = 0;
+ *ReturnOutputBufferLength = FIELD_OFFSET(EVENTS_REPLY, Events);
+ return STATUS_SUCCESS;
+ }
+
+ /* Calculate how many events fit in the output buffer */
+ replySize = FIELD_OFFSET(EVENTS_REPLY, Events);
+ {
+ LONG maxEvents = (LONG)((OutputBufferLength - replySize) / sizeof(DELETE_NOTIFICATION)) + 1;
+ if (count > maxEvents) {
+ count = maxEvents;
+ }
+ }
+
+ /* Copy events from ring buffer */
+ tail = g_Context.EventTail;
+ for (i = 0; i < count; i++) {
+ RtlCopyMemory(
+ &reply->Events[i],
+ &g_Context.Events[tail],
+ sizeof(DELETE_NOTIFICATION)
+ );
+ tail = (tail + 1) % MAX_PENDING_EVENTS;
+ }
+
+ g_Context.EventTail = tail;
+ g_Context.EventCount -= count;
+
+ KeReleaseSpinLock(&g_Context.BufferLock, oldIrql);
+
+ reply->EventCount = (ULONG)count;
+ *ReturnOutputBufferLength = FIELD_OFFSET(EVENTS_REPLY, Events) +
+ (ULONG)count * sizeof(DELETE_NOTIFICATION);
+ return STATUS_SUCCESS;
+ }
+
+ case CMD_GET_STATS:
+ {
+ PSTATS_REPLY statsReply;
+
+ if (OutputBuffer == NULL || OutputBufferLength < sizeof(STATS_REPLY)) {
+ return STATUS_BUFFER_TOO_SMALL;
+ }
+
+ statsReply = (PSTATS_REPLY)OutputBuffer;
+ statsReply->TotalEvents = (ULONG)g_Context.TotalEvents;
+ statsReply->DroppedEvents = (ULONG)g_Context.DroppedEvents;
+ statsReply->MonitorActive = (ULONG)g_Context.MonitorActive;
+
+ KeAcquireSpinLock(&g_Context.BufferLock, &oldIrql);
+ statsReply->PendingEvents = (ULONG)g_Context.EventCount;
+ KeReleaseSpinLock(&g_Context.BufferLock, oldIrql);
+
+ *ReturnOutputBufferLength = sizeof(STATS_REPLY);
+ return STATUS_SUCCESS;
+ }
+
+ case CMD_GET_VERSION:
+ {
+ PVERSION_REPLY verReply;
+
+ if (OutputBuffer == NULL || OutputBufferLength < sizeof(VERSION_REPLY)) {
+ return STATUS_BUFFER_TOO_SMALL;
+ }
+
+ verReply = (PVERSION_REPLY)OutputBuffer;
+ RtlZeroMemory(verReply, sizeof(VERSION_REPLY));
+ RtlStringCbCopyA(verReply->Version, sizeof(verReply->Version), DRIVER_VERSION_STRING);
+
+ *ReturnOutputBufferLength = sizeof(VERSION_REPLY);
+ return STATUS_SUCCESS;
+ }
+
+ default:
+ return STATUS_INVALID_PARAMETER;
+ }
+}
+
+/* ===== BufferPushEvent ===== */
+
+VOID
+BufferPushEvent(
+ _In_ const DELETE_NOTIFICATION* Event
+ )
+{
+ KIRQL oldIrql;
+
+ KeAcquireSpinLock(&g_Context.BufferLock, &oldIrql);
+
+ if (g_Context.EventCount >= MAX_PENDING_EVENTS) {
+ /* Buffer full - drop the oldest event by advancing tail */
+ g_Context.EventTail = (g_Context.EventTail + 1) % MAX_PENDING_EVENTS;
+ g_Context.EventCount--;
+ InterlockedIncrement(&g_Context.DroppedEvents);
+ }
+
+ RtlCopyMemory(
+ &g_Context.Events[g_Context.EventHead],
+ Event,
+ sizeof(DELETE_NOTIFICATION)
+ );
+
+ g_Context.EventHead = (g_Context.EventHead + 1) % MAX_PENDING_EVENTS;
+ g_Context.EventCount++;
+
+ KeReleaseSpinLock(&g_Context.BufferLock, oldIrql);
+}
diff --git a/Filerestore_sys/Filerestore_sys/driver.c b/Filerestore_sys/Filerestore_sys/driver.c
new file mode 100644
index 0000000..f91b65a
--- /dev/null
+++ b/Filerestore_sys/Filerestore_sys/driver.c
@@ -0,0 +1,118 @@
+/*
+ * driver.c - DriverEntry, filter registration, unload
+ */
+
+#include "driver.h"
+
+/* Global context - zero-initialized */
+GLOBAL_CONTEXT g_Context = { 0 };
+
+/* Forward declarations */
+DRIVER_INITIALIZE DriverEntry;
+
+static NTSTATUS
+FilterUnloadCallback(
+ _In_ FLT_FILTER_UNLOAD_FLAGS Flags
+ );
+
+/* ===== Filter registration ===== */
+
+static const FLT_OPERATION_REGISTRATION FilterCallbacks[] = {
+ {
+ IRP_MJ_SET_INFORMATION,
+ 0,
+ PreSetInformation, /* PreOperation */
+ NULL /* PostOperation - not needed */
+ },
+ { IRP_MJ_OPERATION_END }
+};
+
+static const FLT_REGISTRATION FilterRegistration = {
+ sizeof(FLT_REGISTRATION), /* Size */
+ FLT_REGISTRATION_VERSION, /* Version */
+ 0, /* Flags */
+ NULL, /* ContextRegistration */
+ FilterCallbacks, /* OperationRegistration */
+ FilterUnloadCallback, /* FilterUnloadCallback */
+ NULL, /* InstanceSetupCallback */
+ NULL, /* InstanceQueryTeardownCallback */
+ NULL, /* InstanceTeardownStartCallback */
+ NULL, /* InstanceTeardownCompleteCallback */
+ NULL, /* GenerateFileNameCallback */
+ NULL, /* NormalizeNameComponentCallback */
+ NULL /* NormalizeContextCleanupCallback */
+};
+
+/* ===== DriverEntry ===== */
+
+NTSTATUS
+DriverEntry(
+ _In_ PDRIVER_OBJECT DriverObject,
+ _In_ PUNICODE_STRING RegistryPath
+ )
+{
+ NTSTATUS status;
+
+ UNREFERENCED_PARAMETER(RegistryPath);
+
+ /* 1. Initialize the event ring buffer lock */
+ KeInitializeSpinLock(&g_Context.BufferLock);
+ g_Context.EventCount = 0;
+ g_Context.EventHead = 0;
+ g_Context.EventTail = 0;
+ g_Context.TotalEvents = 0;
+ g_Context.DroppedEvents = 0;
+ g_Context.MonitorActive = 0;
+
+ /* 2. Register the minifilter */
+ status = FltRegisterFilter(
+ DriverObject,
+ &FilterRegistration,
+ &g_Context.FilterHandle
+ );
+
+ if (!NT_SUCCESS(status)) {
+ return status;
+ }
+
+ /* 3. Create the communication port */
+ status = SetupCommunicationPort();
+ if (!NT_SUCCESS(status)) {
+ FltUnregisterFilter(g_Context.FilterHandle);
+ return status;
+ }
+
+ /* 4. Start filtering */
+ status = FltStartFiltering(g_Context.FilterHandle);
+ if (!NT_SUCCESS(status)) {
+ FltCloseCommunicationPort(g_Context.ServerPort);
+ FltUnregisterFilter(g_Context.FilterHandle);
+ return status;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+/* ===== FilterUnloadCallback ===== */
+
+static NTSTATUS
+FilterUnloadCallback(
+ _In_ FLT_FILTER_UNLOAD_FLAGS Flags
+ )
+{
+ UNREFERENCED_PARAMETER(Flags);
+
+ /* Close the server port first - prevents new connections */
+ if (g_Context.ServerPort != NULL) {
+ FltCloseCommunicationPort(g_Context.ServerPort);
+ g_Context.ServerPort = NULL;
+ }
+
+ /* Unregister the filter */
+ if (g_Context.FilterHandle != NULL) {
+ FltUnregisterFilter(g_Context.FilterHandle);
+ g_Context.FilterHandle = NULL;
+ }
+
+ return STATUS_SUCCESS;
+}
diff --git a/Filerestore_sys/Filerestore_sys/driver.h b/Filerestore_sys/Filerestore_sys/driver.h
new file mode 100644
index 0000000..af4611e
--- /dev/null
+++ b/Filerestore_sys/Filerestore_sys/driver.h
@@ -0,0 +1,59 @@
+/*
+ * driver.h - Internal driver declarations
+ */
+
+#ifndef _FILERESTORE_DRIVER_H_
+#define _FILERESTORE_DRIVER_H_
+
+#include
+#include
+#include "common.h"
+#include
+/* Pool tag: 'MsFR' (stored little-endian as 'RFsM') */
+#define POOL_TAG 'RFsM'
+
+/* Maximum pending events in the kernel ring buffer */
+#define MAX_PENDING_EVENTS 256
+
+/* ===== Global context ===== */
+
+typedef struct _GLOBAL_CONTEXT {
+ PFLT_FILTER FilterHandle;
+ PFLT_PORT ServerPort; /* server-side communication port */
+ PFLT_PORT ClientPort; /* connected client port (single client) */
+
+ /* Kernel event ring buffer */
+ KSPIN_LOCK BufferLock;
+ LONG EventCount; /* number of events currently buffered */
+ LONG EventHead; /* next write position */
+ LONG EventTail; /* next read position */
+ DELETE_NOTIFICATION Events[MAX_PENDING_EVENTS];
+
+ /* Statistics */
+ volatile LONG TotalEvents;
+ volatile LONG DroppedEvents;
+ volatile LONG MonitorActive; /* 1 = monitoring active */
+} GLOBAL_CONTEXT, *PGLOBAL_CONTEXT;
+
+extern GLOBAL_CONTEXT g_Context;
+
+/* ===== Function prototypes ===== */
+
+/* communication.c */
+NTSTATUS
+SetupCommunicationPort(VOID);
+
+VOID
+BufferPushEvent(
+ _In_ const DELETE_NOTIFICATION* Event
+ );
+
+/* filter.c */
+FLT_PREOP_CALLBACK_STATUS
+PreSetInformation(
+ _Inout_ PFLT_CALLBACK_DATA Data,
+ _In_ PCFLT_RELATED_OBJECTS FltObjects,
+ _Flt_CompletionContext_Outptr_ PVOID *CompletionContext
+ );
+
+#endif /* _FILERESTORE_DRIVER_H_ */
diff --git a/Filerestore_sys/Filerestore_sys/filter.c b/Filerestore_sys/Filerestore_sys/filter.c
new file mode 100644
index 0000000..a4456e1
--- /dev/null
+++ b/Filerestore_sys/Filerestore_sys/filter.c
@@ -0,0 +1,275 @@
+/*
+ * filter.c - PreSetInformation callback and file metadata capture
+ */
+
+#include "driver.h"
+
+/* Forward declarations */
+static VOID
+CaptureDeleteEvent(
+ _Inout_ PFLT_CALLBACK_DATA Data,
+ _In_ PCFLT_RELATED_OBJECTS FltObjects,
+ _In_ FILE_INFORMATION_CLASS InfoClass
+ );
+
+static NTSTATUS
+QueryFileLCNs(
+ _In_ PCFLT_RELATED_OBJECTS FltObjects,
+ _Inout_ PDELETE_NOTIFICATION Notification
+ );
+
+/* ===== PreSetInformation callback ===== */
+
+FLT_PREOP_CALLBACK_STATUS
+PreSetInformation(
+ _Inout_ PFLT_CALLBACK_DATA Data,
+ _In_ PCFLT_RELATED_OBJECTS FltObjects,
+ _Flt_CompletionContext_Outptr_ PVOID *CompletionContext
+ )
+{
+ FILE_INFORMATION_CLASS infoClass;
+
+ *CompletionContext = NULL;
+
+ /* Check if monitoring is active */
+ if (!g_Context.MonitorActive) {
+ return FLT_PREOP_SUCCESS_NO_CALLBACK;
+ }
+
+ infoClass = Data->Iopb->Parameters.SetFileInformation.FileInformationClass;
+
+ switch (infoClass) {
+
+ case FileDispositionInformation:
+ {
+ PFILE_DISPOSITION_INFORMATION dispInfo;
+ dispInfo = (PFILE_DISPOSITION_INFORMATION)
+ Data->Iopb->Parameters.SetFileInformation.InfoBuffer;
+ if (dispInfo == NULL || !dispInfo->DeleteFile) {
+ return FLT_PREOP_SUCCESS_NO_CALLBACK;
+ }
+ break;
+ }
+
+ case FileDispositionInformationEx:
+ {
+ PFILE_DISPOSITION_INFORMATION_EX dispInfoEx;
+ dispInfoEx = (PFILE_DISPOSITION_INFORMATION_EX)
+ Data->Iopb->Parameters.SetFileInformation.InfoBuffer;
+ if (dispInfoEx == NULL ||
+ !(dispInfoEx->Flags & FILE_DISPOSITION_DELETE)) {
+ return FLT_PREOP_SUCCESS_NO_CALLBACK;
+ }
+ break;
+ }
+
+ default:
+ /* Not a delete operation */
+ return FLT_PREOP_SUCCESS_NO_CALLBACK;
+ }
+
+ /* Capture metadata before the delete reaches NTFS */
+ CaptureDeleteEvent(Data, FltObjects, infoClass);
+
+ /* Never block the original operation */
+ return FLT_PREOP_SUCCESS_NO_CALLBACK;
+}
+
+/* ===== CaptureDeleteEvent ===== */
+
+static VOID
+CaptureDeleteEvent(
+ _Inout_ PFLT_CALLBACK_DATA Data,
+ _In_ PCFLT_RELATED_OBJECTS FltObjects,
+ _In_ FILE_INFORMATION_CLASS InfoClass
+ )
+{
+ DELETE_NOTIFICATION notif;
+ PFLT_FILE_NAME_INFORMATION nameInfo = NULL;
+ FILE_STANDARD_INFORMATION stdInfo;
+ FILE_BASIC_INFORMATION basicInfo;
+ NTSTATUS status;
+ USHORT copyLen;
+
+ UNREFERENCED_PARAMETER(InfoClass);
+
+ RtlZeroMemory(¬if, sizeof(notif));
+
+ __try {
+
+ /* 1. Get file name */
+ status = FltGetFileNameInformation(
+ Data,
+ FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT,
+ &nameInfo
+ );
+
+ if (NT_SUCCESS(status)) {
+ status = FltParseFileNameInformation(nameInfo);
+ if (NT_SUCCESS(status)) {
+ copyLen = nameInfo->Name.Length;
+ if (copyLen > (MAX_FILE_PATH_CHARS - 1) * sizeof(WCHAR)) {
+ copyLen = (MAX_FILE_PATH_CHARS - 1) * sizeof(WCHAR);
+ }
+ RtlCopyMemory(notif.FileName, nameInfo->Name.Buffer, copyLen);
+ notif.FileNameLength = copyLen;
+ notif.FileName[copyLen / sizeof(WCHAR)] = L'\0';
+ }
+ FltReleaseFileNameInformation(nameInfo);
+ nameInfo = NULL;
+ }
+
+ /* 2. Get file size */
+ status = FltQueryInformationFile(
+ FltObjects->Instance,
+ FltObjects->FileObject,
+ &stdInfo,
+ sizeof(stdInfo),
+ FileStandardInformation,
+ NULL
+ );
+
+ if (NT_SUCCESS(status)) {
+ notif.FileSize = stdInfo.EndOfFile;
+ notif.AllocationSize = stdInfo.AllocationSize;
+ }
+
+ /* 3. Get timestamps */
+ status = FltQueryInformationFile(
+ FltObjects->Instance,
+ FltObjects->FileObject,
+ &basicInfo,
+ sizeof(basicInfo),
+ FileBasicInformation,
+ NULL
+ );
+
+ if (NT_SUCCESS(status)) {
+ notif.CreationTime = basicInfo.CreationTime;
+ notif.LastWriteTime = basicInfo.LastWriteTime;
+ }
+
+ /* 4. Get LCN mapping (may fail for resident files - that's OK) */
+ QueryFileLCNs(FltObjects, ¬if);
+
+ /* 5. Fill remaining fields */
+ notif.ProcessId = FltGetRequestorProcessId(Data);
+ notif.DeleteType = DELETE_TYPE_PERMANENT;
+ KeQuerySystemTimePrecise(¬if.Timestamp);
+
+ /* 6. Push to ring buffer */
+ BufferPushEvent(¬if);
+ InterlockedIncrement(&g_Context.TotalEvents);
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER) {
+ /* Silent failure - never interfere with the original delete */
+ if (nameInfo != NULL) {
+ FltReleaseFileNameInformation(nameInfo);
+ }
+ }
+}
+
+/* ===== QueryFileLCNs ===== */
+
+static NTSTATUS
+QueryFileLCNs(
+ _In_ PCFLT_RELATED_OBJECTS FltObjects,
+ _Inout_ PDELETE_NOTIFICATION Notification
+ )
+{
+ NTSTATUS status;
+ STARTING_VCN_INPUT_BUFFER vcnInput;
+ PRETRIEVAL_POINTERS_BUFFER rpBuf = NULL;
+ ULONG rpBufSize = 4096;
+ ULONG returned;
+ ULONG i;
+ ULONG count;
+ LARGE_INTEGER prevVcn;
+
+ vcnInput.StartingVcn.QuadPart = 0;
+
+ /* Allocate initial buffer */
+ rpBuf = (PRETRIEVAL_POINTERS_BUFFER)ExAllocatePool2(
+ POOL_FLAG_NON_PAGED,
+ rpBufSize,
+ POOL_TAG
+ );
+
+ if (rpBuf == NULL) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ /* Query retrieval pointers */
+ status = FltFsControlFile(
+ FltObjects->Instance,
+ FltObjects->FileObject,
+ FSCTL_GET_RETRIEVAL_POINTERS,
+ &vcnInput,
+ sizeof(vcnInput),
+ rpBuf,
+ rpBufSize,
+ &returned
+ );
+
+ if (status == STATUS_BUFFER_OVERFLOW) {
+ /* Retry with larger buffer */
+ ExFreePoolWithTag(rpBuf, POOL_TAG);
+ rpBufSize = 16384;
+ rpBuf = (PRETRIEVAL_POINTERS_BUFFER)ExAllocatePool2(
+ POOL_FLAG_NON_PAGED,
+ rpBufSize,
+ POOL_TAG
+ );
+ if (rpBuf == NULL) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ status = FltFsControlFile(
+ FltObjects->Instance,
+ FltObjects->FileObject,
+ FSCTL_GET_RETRIEVAL_POINTERS,
+ &vcnInput,
+ sizeof(vcnInput),
+ rpBuf,
+ rpBufSize,
+ &returned
+ );
+ }
+
+ if (!NT_SUCCESS(status)) {
+ /* Resident files or other failures - LCNCount stays 0 */
+ ExFreePoolWithTag(rpBuf, POOL_TAG);
+ return status;
+ }
+
+ /* Parse extents */
+ count = rpBuf->ExtentCount;
+ if (count > MAX_LCN_ENTRIES) {
+ count = MAX_LCN_ENTRIES;
+ }
+
+ prevVcn = rpBuf->StartingVcn;
+ for (i = 0; i < count; i++) {
+ LARGE_INTEGER nextVcn = rpBuf->Extents[i].NextVcn;
+ LARGE_INTEGER lcn = rpBuf->Extents[i].Lcn;
+
+ /* LCN == -1 means sparse/unallocated extent, skip */
+ if (lcn.QuadPart != -1) {
+ Notification->LCNEntries[Notification->LCNCount].StartLCN =
+ (ULONGLONG)lcn.QuadPart;
+ Notification->LCNEntries[Notification->LCNCount].ClusterCount =
+ (ULONG)(nextVcn.QuadPart - prevVcn.QuadPart);
+ Notification->LCNEntries[Notification->LCNCount].Reserved = 0;
+ Notification->LCNCount++;
+
+ if (Notification->LCNCount >= MAX_LCN_ENTRIES) {
+ break;
+ }
+ }
+
+ prevVcn = nextVcn;
+ }
+
+ ExFreePoolWithTag(rpBuf, POOL_TAG);
+ return STATUS_SUCCESS;
+}
diff --git a/Filerestore_sys/Filerestore_sys/packages.config b/Filerestore_sys/Filerestore_sys/packages.config
new file mode 100644
index 0000000..bb6933b
--- /dev/null
+++ b/Filerestore_sys/Filerestore_sys/packages.config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/README.md b/README.md
index 45103ca..39d0a18 100644
--- a/README.md
+++ b/README.md
@@ -176,6 +176,13 @@ usnrecover C 3 D:\recovered\ # 按索引恢复
recover C important.docx D:\recovered\ # 智能恢复向导
```
+### 2. USN 精准恢复 (v1.0.0+)
+```bash
+usnlist C # 列出最近删除文件
+usnrecover C 3 D:\recovered\ # 按索引恢复
+recover C important.docx D:\recovered\ # 智能恢复向导
+```
+
### 3. 实时删除监控 (v1.0.0+)
- 后台守护进程监听 USN 删除事件
- 文件删除瞬间自动捕获 MFT 快照
@@ -818,6 +825,12 @@ msbuild Filerestore_CLI.vcxproj /p:Configuration=Release /p:Platform=x64
| `usnlist ` | List recently deleted files (with confidence scoring) |
| `usnrecover