文件系统通知工具包 (FSNK) 简介





0/5 (0投票)
文件系统通知工具包 (FSNK) 产品简介,用于实时监控文件系统活动。
引言
文件系统通知工具包 (FSNK) 是一款用于实时监控文件系统活动的产品。
如果您想了解文件系统发生了什么,您无需花费大量时间编写驱动程序。只需下载并试用 FSNK。
FSNK 使用一个内核模式驱动程序,它是一个文件系统过滤器。当过滤器收到文件系统请求通知时,它会将相关信息通过应用程序连接到 FSNK 引擎时指定的**回调函数**发送给应用程序。
该引擎能够查看所有基本的文件操作,例如在文件中写入和读取数据、修改文件或文件夹属性、创建文件或文件夹、删除或移动文件等。当您收到通知时,您可以获取有关该事件的更详细信息。
除了标准文件操作外,该工具包还可以跟踪卷的挂载和卸载操作、安全描述符修改、对象 ID 更改、缓存刷新操作、硬链接和符号链接的创建以及其他文件系统事件。
FSNK 内容
FSNK 安装包中的文件说明
- Fsnk.sys 是一个内核模式驱动程序,它会截获文件系统操作。它可以动态加载和卸载,无需重启系统。
- Fsnklib.dll 是一个动态链接库,它允许应用程序与驱动程序进行交互并管理其状态。它为您的应用程序提供文件系统活动信息。
- Fsnkd.exe 是一个控制台实用程序,演示了产品的功能。
- Install.exe 是一个简单的应用程序,用于安装和卸载产品。
- Fsnklib.lib 是一个静态库,用于在编译时链接到动态库。
- *.h 文件是头文件,包含 C/C++ 项目所需的 API 函数和数据类型的定义。
要求
目前,该产品支持从 XP SP2 到 Server 2012 R2 的所有 Windows 版本,包括 x86-64 架构的 64 位系统(也可根据要求提供基于 Itanium 的系统版本)。由于 FSNK 驱动程序 fsnk.sys 需要 Filter Manager 组件,而该组件不能单独安装,因此需要 SP2 更新。服务器系统需要 Windows Server 2003 SP1 或更高版本。
特点
文件系统通知工具包为您提供了一个绝佳的机会,可以了解给定时间点文件系统中发生的一切。它提供了所有文件操作的通知:创建文件、删除、读取、写入、读取和修改文件元数据、安全描述符、属性等。
FSNK 支持熟悉的 DOS 风格路径 C:\Windows 和操作系统原生的 NT 风格路径 \Device\HarddiskVolume1\Windows。
使用 FSNK,您将收到有关每次文件操作的详细信息。这些信息包括操作码、操作状态、唯一线程、进程和会话 ID、登录会话 ID、用户安全标识符 (SID)、文件对象卷 ID、完整规范化的 NT 风格文件系统对象名称以及字符偏移量到相对文件路径。
FSNK 收集有关文件系统性能的统计信息,包括读取和写入的字节数、读取和写入操作的次数等。它有一个内置的通知过滤器,用于只接收您感兴趣的事件的通知。
对于每个文件系统事件,您可以调用 FsnkGetName
(以及适用的 FsnkGetTargetName
)函数来获取与该操作相关的文件系统对象的 DOS 风格名称。您还可以调用 FsnkGetAccountName
例程来获取请求该操作的用户的名称。您还可以调用 FsnkGetProcessImagePath
例程来获取启动该操作的进程的可执行文件的路径。
它提供了一套 API 函数,用于低于 Win32 子系统和 Native 层的文件 I/O。这使您能够绕过这些级别的潜在拦截器。FsnkIoCreate
函数可以使用 NT 风格的文件名打开和创建文件对象。该组中的其他函数使您能够使用接收到的句柄处理文件。
FSNK 对操作系统支持的任何文件系统都能很好地工作,包括磁盘文件系统、CD 甚至网络文件系统。
请注意,FSNK 的当前版本无法阻止操作或更改其参数。目前,它只能提供已发生事件的通知。
API
本节进一步描述了产品基本功能的 API 函数。
首先,您需要通过 FsnkInitialize API 函数初始化引擎;在此之前,您无法调用 FSNK 的其他 API 函数。您应该注意,一次只有一个进程可以连接到引擎。这意味着如果您的一个应用程序连接到引擎以获取通知,另一个应用程序将无法做到。要取消初始化引擎并释放库使用的所有资源,您应该调用 FsnkUninitialize
函数。
安装
FsnkInstall
函数安装一个内核模式过滤器驱动程序 (fsnk.sys)。输入参数包括将在系统服务列表中显示的*服务名称*、驱动程序文件的*完整路径*、负责在系统启动时自动启动驱动程序的*标志*以及*Altitude ID*。此 API 函数成功安装引擎时返回“true”,否则返回“false”。
// Installing the engine. if (! FsnkInstall ( L"My FSNK engine", L"C:\\Program Files\\My product\\fsnk.sys", TRUE, // For automatic startup. L"265000", // Specify your own altitude here. 0)) // No flags defined at this moment. { // Error handling goes here. uError = FsnkGetErrorCode (); ... }
调用该函数的线程必须具有管理员权限才能成功安装引擎。如果您希望在您的产品中大规模分发 FSNK,请不要忘记从 Microsoft 获取 Altitude ID。Altitude ID 应从 FSFilter Activity Monitor 组请求,因为监控过滤器应该在此级别工作。
FsnkUninstall API 函数卸载引擎,但不会将其从内存中卸载。您应该提前通过 FsnkUnload
例程停止引擎,或者重启系统以完全删除引擎。引擎可以根据需要启动和停止多次,而无需重启系统。操作成功时返回“true”,否则返回“false”。
// Remove the engine from the system. if (! FsnkUninstall ()) { // Error handling goes here. uError = FsnkGetErrorCode (); ... }
与安装类似,此函数必须从以管理员用户上下文运行的线程调用。
管理过滤器状态
FsnkLoad API 函数启动引擎。启动无需任何参数。函数成功启动时返回“true”,否则返回“false”。之后,应用程序可以通过 FsnkInitialize
连接到引擎。要将引擎从内存中卸载,请调用 FsnkUnload
函数。
// Load the engine, this will actually load the FSNK filter driver. if (! FsnkLoad ()) { // Error handling goes here. uError = FsnkGetErrorCode (); ... } // Activate filter to start receiving notifications about file system events. if (! FsnkActivate ()) { // Error handling goes here. uError = FsnkGetErrorCode (); ... // Perform any cleanup needed. FsnkUnload (); ... }
FsnkActivate API 函数打开从过滤器到应用程序的文件系统事件通知的发送,通过在调用 FsnkInitialize
时指定的*回调例程*。之后,就可以接收来自过滤器的事件了。请注意,在激活过滤器之前,您的回调函数应准备好接收通知。要暂时禁用过滤器,请调用 FsnkDeactivate
函数。
// Stop receiving notifications. if (! FsnkDeactivate ()) { // Error handling goes here. uError = FsnkGetErrorCode (); ... } // Unload FSNK filter from memory. if (! FsnkUnload ()) { // Error handling goes here. uError = FsnkGetErrorCode (); ... }
管理通知过滤器规则
可以通过 FsnkAddRule 和 FsnkDeleteRule API 函数设置和删除您想接收通知的事件的过滤器。这些函数将规则插入和删除用于过滤文件系统事件的规则列表中。过滤器使用函数输入参数中描述的各种参数进行设置。最初,没有设置任何过滤器,应用程序将收到所有支持的文件操作的通知。
每个规则至少必须包含一个非零的操作位掩码,而文件名模式和进程 ID 是可选的(要指定“任何”进程 ID,请在相应参数中指定 FsnkPidInvalid
值)。如果指定的对象名称模式已在规则列表中,则指定的位掩码和进程 ID 将替换该规则,不会创建新规则。
uFlags
参数设置各种标志来控制函数行为。例如,如果您希望规则保存在注册表中的规则数据库中,可以指定 FsnkRuleFlagSave
标志;此数据库中的规则在引擎启动时会自动加载。默认情况下,规则仅存储在内存中,在系统或引擎重启后不会保存。
// Include all executable files. if (! FsnkAddRule ( L"*.exe", 0, // 0 = all operations. FsnkPidInvalid, // Means all processes. FsnkRuleFlagSave)) // Store rule permanently. { // Error handling goes here. uError = FsnkGetErrorCode (); ... } ... // Exclude temporary files. if (! FsnkAddRule ( L"*.tmp", FsnkOpRead | FsnkOpWrite, FsnkPidInvalid, FsnkRuleFlagExclude)) // Should not match to include. { // Error handling goes here. uError = FsnkGetErrorCode (); ... }
FsnkDeleteRule
函数从数据库中删除规则。输入时,该函数接收文件名模式和用于从持久数据库中删除规则的参数。FsnkClearRules
函数从规则列表或持久数据库中删除所有现有规则。
// Delete rule for temporary files. if (! FsnkDeleteRule ( L"*.tmp", FsnkRuleDeleteActive)) { // Error handling goes here. uError = FsnkGetErrorCode (); ... } // Delete all permanent rules. if (! FsnkClearRules ( FsnkRuleClearSaved)) { // Error handling goes here. uError = FsnkGetErrorCode (); ... }
文件对象名称
FsnkGetName
函数返回用于执行操作的文件系统对象的完整 DOS 风格名称,例如创建的文件或文件夹的名称,或者删除的文件夹的名称。输入参数包括存储操作数据的结构地址,或者提供具有网络文件系统上对象名称的网络路径的标志。输出时,如果操作成功,该函数会用对象名称信息填充 FSNK_NAME_INFORMATION
。函数成功时返回非零值,否则返回零。
FsnkGetTargetName
函数返回对象的完整名称,但与 FsnkGetName
不同,它返回目标对象的名称。例如,移动操作的目标文件名,或者正在创建的硬链接或符号链接的名称。
void __stdcall MyFsCallbackRoutine ( IN PVOID pContext, // Application-defined value. IN PFSNK_OP_DATA pData, // General information of the request. IN PFSNK_OP_PARAMS pParams) // Operation-specific information. { ULONG uError = 0; FSNK_NAME_INFORMATION NameInfo = {0}; FSNK_NAME_INFORMATION TargetNameInfo = {0}; ... // Preallocate string buffers. You should do that before you call FsnkInitialize() routine so these strings can be passed here as context for performance purposes. FsnkCreateString ( &NameInfo.Name, // String structure to initialise. 65554, // Maximum possible size of a string. NULL, 0); // Let the library to allocate new buffers. ... // Query DOS-style name for the indicated file system object. if (! FsnkGetName ( pData, FsnkGetNameNetworkInfo, // Ask for the network path as well. &NameInfo)) { // Error handling goes here. uError = FsnkGetErrorCode (); ... } // Check if a name string is present in the returned name information. if (NameInformation.IsName) { // Deal with name as required. MyLogWriteFileName (&NameInfo.Name); ... } // Check if a share string is present in the returned name information. if (NameInformation.IsShare) { // The specified object is in the network file system and we have the name of the share. MyLogWriteFileName (&NameInfo.Share); ... } // Query DOS-style name for the target object. // Only applicable to some file operations. if (! FsnkGetTargetName ( pData, pParams, // Required as source. FsnkGetNameNetworkInfo, // Ask for network path as well. &TargetNameInfo)) { // Error handling goes here. uError = FsnkGetErrorCode (); ... } ... // Perform cleanup tasks. FsnkFreeName (&NameInfo); FsnkFreeName (&TargetNameInfo); ... }
管理字符串
库中的字符串管理通过 FSNK_STRING
结构进行。FsnkCreateString
初始化字符串结构,FsnkDeleteString
函数删除字符串结构,其中删除意味着必要时清理使用的资源。FsnkCreateString
函数既可以用预分配的缓冲区初始化结构,也可以自己创建新缓冲区。
FsnkCreateString
函数中的字符串缓冲区大小在 uMaximumSize
输入参数中指定,以字节为单位。此参数是必需的,不能为 NULL。第一个参数指示正在初始化的字符串结构。pwInitialData
参数是预先准备好的缓冲区;如果它不是 NULL,则字符串将初始化为使用此缓冲区,否则函数将分配一个新缓冲区。最后一个参数指定缓冲区的大小(以字节为单位)。
FSNK_STRING FileName = {0}; PWSTR pwFileName = L"C:\\Windows\\notepad.exe"; USHORT uBufferSize = (lstrlen (pwFileName) + 1) * sizeof (WCHAR); // Initialise for the existing string buffer. if (! FsnkCreateString ( &FileName, uBufferSize, // Total size of the buffer. pwFileName, // No buffers will be allocated. 0, // Actual size of the string, to be calculated. { // Error handling goes here. You can't call FsnkGetErrorCode because string routines don't use error codes (for performance). ... }
用于删除字符串的 FsnkDeleteString
函数接收需要清理的结构地址;而字符串缓冲区仅在由库分配时才会被释放。例如,通过 FsnkInitString
初始化后,字符串中的缓冲区将不会被释放,应用程序负责释放。
FSNK_STRING String = {0}; ... // Free resources used for the string. FsnkDeleteString ( &String);
用户帐户名
FsnkGetAccountName API 函数允许您使用安全标识符来获取关联用户帐户的名称。这可以是 FSNK_OP_DATA
结构中的 SID 或任何其他有效的 SID。输出时,您将获得一个包含帐户当前名称的 Unicode 字符串的地址。在完成使用后,此字符串应通过 FsnkFreeString
删除。
由于用户帐户名称随时可能更改,因此您不应依赖它来识别帐户。请使用 SID 进行识别,仅在必要时请求帐户名称。例如,如果您需要向用户显示或写入事件日志。
void __stdcall MyFsCallbackRoutine ( IN PVOID pContext, // Application-defined value. IN PFSNK_OP_DATA pData, // General information of the request. IN PFSNK_OP_PARAMS pParams) // Operation-specific information. { ULONG uError = 0; SID_NAME_USE uNameUse = 0; PWSTR pwAccountName = NULL; ... // Query user account name. if (! FsnkGetAccountName ( pData -> Sid, // User account SID. &pwAccountName, // Name string will be returned here. &uNameUse)) // Account name usage type. { // Error handling goes here. uError = FsnkGetErrorCode (); ... } // Use account name string. MyLogWriteString (pwAccountName); ... // Perform cleanup tasks. FsnkFreeString (&pwAccountName); }
获取进程映像文件名
FsnkGetProcessImagePath
函数返回进程的可执行映像文件的完整 DOS 风格路径。输入时,您应指定从 FSNK_OP_DATA
结构中的 ProcessId
字段获得的进程 ID。输出时,您将获得一个包含进程映像文件路径的 Unicode 字符串的地址。返回的字符串最终必须通过 FsnkFreeString
释放。有关更多信息,请参阅产品文档。
void __stdcall MyFsCallbackRoutine ( IN PVOID pContext, // Application-defined value. IN PFSNK_OP_DATA pData, // General information of the request. IN PFSNK_OP_PARAMS pParams) // Operation-specific information. { ULONG uError = 0; PWSTR pwImageFilePath = NULL; ... // Query image file path for the requestor process. if (! FsnkGetProcessImagePath ( pData -> ProcessId // Unique requestor ID. &pwImageFilePath)) // Image path string will be returned here. { // Error handling goes here. uError = FsnkGetErrorCode (); ... } // Use process image path string. MyLogWriteString (pwImageFilePath); ... // Perform cleanup tasks. FsnkFreeString (&pwImageFilePath); }
文件系统性能统计
FsnkGetStatistics
函数提供文件系统性能的统计数据,例如读取、写入和打开/创建文件的总次数(包括成功和失败的操作),成功读取和写入的总字节数,统计信息收集的开始时间,文件系统中最后一次读取和写入操作的时间。要查看此功能的演示,请在控制台演示中使用 fsnkd stat 命令。
ULONG uError = 0; FSNK_STATISTICS_DATA FsStatistics = {0}; // Query current statistics. if (! FsnkGetStatistics ( &FsStatistics)) { // Error handling goes here. uError = FsnkGetErrorCode (); ... } // Write statistics to the log. MyLogPrintString ("Bytes read: %I64u", FsStatistics.BytesRead); MyLogPrintString ("Bytes written: %I64u", FsStatistics.BytesWritten); ...
请注意,如果在库初始化期间将标志设置为 FsnkInitFullStats
,则统计信息将包括所有支持的 I/O 操作。否则,统计信息将根据当前过滤器规则(如果添加到规则列表中)进行收集。
低级文件输入/输出
FsnkIoCreate API 函数创建一个或打开一个具有指定名称的文件系统对象。此对象可以是文件、文件夹或逻辑磁盘(卷)。如果使用了 FsnkIoBypassFilters
标志,则通过此函数执行的打开或创建文件操作不会在库中生成任何通知。此外,此标志允许您绕过顶层文件过滤器(即位于 FSNK 引擎之上的过滤器)。此函数有很多参数,建议在文档中阅读。要关闭文件,请使用 FsnkIoClose
例程。
void __stdcall MyFsCallbackRoutine ( IN PVOID pContext, // Application-defined value. IN PFSNK_OP_DATA pData, // General information of the request. IN PFSNK_OP_PARAMS pParams) // Operation-specific information. { ULONG uError = 0; HANDLE hFileHandle = NULL; FSNK_CREATE_RESULT uCreateResult = 0; FSNK_CREATE_OPTION uCreateOptions = 0; // Create options bitmask. uCreateOptions = FsnkOptionNonDirectory | // Open if not a directory only. FsnkOptionSynchronousIoNonAlert | // Open for synchronous access. FsnkOptionRandomAccess; // Don't read ahead and cache. // Open the indicated file object. if (! FsnkIoCreate ( &hFileHandle, // File handle will be returned here. &uCreateResult, // What was done will be returned here. pData -> FileName, // Full name of the object to open. FILE_GENERIC_READ, // Request read access only. FsnkDispositionOpen, // Open existing file only. FsnkShareRead, // Don't allow write access while reading. uCreateOptions, // Options for create operation. FsnkIoBypassFilters)) // Don't generate recursive calls to us. { // Error handling goes here. uError = FsnkGetErrorCode (); ... } ... // Not needed anymore, close file. FsnkIoClose (&hFileHandle); }
打开文件后,您可以使用此组中的其他函数来处理它。例如,FsnkIoRead
函数用于从已打开的文件中读取数据。与此组中的其他函数类似,读取请求将绕过 Win32 和 Native 层,并且顶层过滤器也不会看到该请求。该函数的输入参数包括对象句柄、从文件中读取的数据的缓冲区地址、缓冲区大小(以字节为单位)以及一些用于操作控制的标志。输出时,如果成功,实际读取的字节数将在 *puBytesRead
中返回。
ULONG uError = 0; ULONG uBytesRead = 0; HANDLE hFileHandle = NULL; BYTE auDataBuffer [1024] = {0}; ... // Read file header from the beginning. if (! FsnkIoRead ( hFileHandle, // File handle with read access. auDataBuffer, // Buffer for the data. sizeof (auDataBuffer), // Size of the specified buffer, in bytes. 0, // Offset value to start reading from, in bytes. FsnkIoNoBuffering, // Don't use caching for the particular operation. &uBytesRead)) // Number of bytes that was actually read. { // Error handling goes here. uError = FsnkGetErrorCode (); ... }
此组包含大量函数,所有这些函数都在文档中有详细描述。我们建议您仔细阅读文档。
回调函数和结构
本节描述了回调函数,该函数为文件系统中的每个事件调用,以及用于告知引擎中发生的事件的回调函数。此外,还有存储有关拦截的文件系统事件信息的结构的描述。
文件系统事件回调
此回调例程向应用程序报告文件系统事件。事件信息通过两个结构在回调中表示:FSNK_OP_DATA
和 FSNK_OP_PARAMS
。可以通过库 API 函数获取一些额外信息。
pData
参数是存储每种操作通用信息的结构的地址:操作代码(创建、删除、写入等)、操作状态、请求者的进程 ID、用作操作安全上下文的用户帐户的 SID,以及其他一些信息。pParams
输入包含保存特定于某个操作的信息的结构的地址。例如,对于读取或写入操作,它是从文件中读取或写入的字节数,对于创建链接,它是链接文件名。
某些值仅在其用于系统时才具有唯一性。例如卷 ID:在卷未卸载之前它是唯一的。如果您想在应用程序中使用这些标识符,您应该拥有一个最新的列表,而回调将帮助您做到这一点。
此例程在库线程之一的上下文中调用。您应该尽快将控制权返回给库,因为处理文件系统事件通知的线程数量是有限的。库仅对已完成的操作调用此例程,因此没有可能阻止操作或更改其参数。
引擎事件回调
此回调是可选的,但它可以方便地告知产品引擎中的各种事件。回调参数接收以下数据:应用程序定义的上下文(例如,结构的地址)、库事件代码以及适用的 Win32 错误代码。最后一个参数表示事件的特定详细信息。例如,它可能指示发生故障的引擎组件,这对于调试很有用。
此例程可以在任意线程的上下文中调用。建议始终设置此回调,因为在发生故障时,有机会重新初始化库并通过从回调返回“true”来继续工作。有关更多信息,请参阅文档。
文件系统事件一般信息
FSNK_OP_DATA
结构存储有关文件系统事件的一般信息。通过使用它,您可以获取有关操作类型、进程、会话以及请求操作的用户的信息。
该结构存储以下数据:操作标志位掩码、操作代码(读取、写入等)以及操作状态。接下来是相关标识符:线程、进程和会话的唯一标识符、登录会话 ID、用于执行操作的帐户 SID、文件对象所在的卷的 ID。最后,文件系统对象的 NT 风格名称以及到文件相对路径的字符偏移量。
请记住,指示的线程、进程和会话 ID 并非在所有时间都唯一。例如,线程标识符仅在线程终止之前有效。当前的 FSNK 版本无法跟踪线程和进程的创建和终止,但计划在未来的版本中添加此功能。
文件系统事件特定信息
使用 FSNK_OP_PARAMS
结构,您可以获取许多操作的特定于操作的信息,例如创建、关闭、读取和写入、挂载、更改卷标、创建链接、移动或重命名、更改安全描述符、卷扩展和收缩等。
例如,以下数据可用于打开/创建操作:请求类型、对对象的请求访问、文件属性、文件已存在时应采取的操作、文件对其他应用程序的可用性、定义创建操作执行各种方面的参数的位掩码、新文件的初始大小以及操作的最终结果(已创建、已替换、已打开等)。
版本历史
2014.04.10 v1.0
- 文件系统通知工具包的初始版本。 非常欢迎您的反馈。