Handling Filenames in CBFS Filter
CBFilter is a component of CBFS Filter, an SDK and library designed for monitoring and controlling various system activities, including filesystem operations. This component — available as a class, struct, or trait depending on your programming language — enables you to control all types of filesystem operations within your Windows applications. The article reviews the ways CBFilter deals with file names and paths in different operations.
In Windows, as in most operating systems, file-related APIs are built around filenames, directories, and paths. CBFilter handles filesystem operations based on rules, with paths and masks serving as the foundation for each rule. Therefore, it is crucial to understand how paths and filenames are represented and addressed in the system, as well as how CBFilter manages some complex scenarios.
When you add a rule — whether it's a standard filter rule, a reparse rule, a default rule, or a pass-through rule — the first parameter is the mask. This string can include a path (though it is not required) and should specify either an exact filename or a wildcard mask. Regular Win32 wildcard characters, specifically "*" (asterisk) and "?" (question mark), are supported and utilized to match the actual file name.
This is where simplicity gives way to complexity.
When an application monitors or controls the filesystem for security purposes, attackers may attempt to deceive the application using tricks to bypass the established rules.
Therefore, an application must carefully handle paths and filenames, both when setting the rules and when processing filesystem requests.
Path Formats
We are all familiar with Windows paths that begin with drive letters. However, internally, Windows does not use drive letters to address files. Instead, it employs the NT-native format, where a path starts with "\Device\HarddiskVolumeN\". A drive letter is essentially a "DOS device name," which the Win32 subsystem of Windows translates into a volume name.
When you add a mask that includes a path, you have several options for naming a volume, including a drive letter, a volume GUID, or an NT-native name. If you specify a drive letter or a volume GUID, the component will convert them into the corresponding NT-native name before adding a rule to the driver’s rule list.
When your application handles events, the name of the file on which the operation is performed is passed to the event handler. Depending on your configuration, you will receive either an NT-native name or a drive letter in the path. The setting that controls this mode is called ResolveNtDeviceToDriveLetter:
filter.Config("ResolveNtDeviceToDriveLetter=False");
This option is enabled by default, providing an application with pathes that include a drive letter if one is available. However, some devices and volumes may not have a drive letter, in which case translation will not be possible.
Short File Names
Although rarely used today, short file names (also known as 8.3 or 8dot3 names) are still supported in some environments. If short names are enabled for a particular filesystem, some processes may specify the shortened version of a long name in the path. This functionality applies only when the original name is long and a corresponding short name exists. For example, "C:\Program Files" can be shortened to "C:\Progra~1," while "C:\Temp" cannot be shortened.
If short names are relevant, the application should instruct CBFilter to "unwrap" or resolve short names. This can be achieved by setting the NormalizeShortFileNames configuration option to a non-zero value:
filter.Config("NormalizeShortFileNames=2");
Setting the value to 1 enables short name resolution only on local drives, while a value of 2 instructs the driver to resolve short names on all filesystems.
Opening by ID
One of the lesser-known features of Windows is the ability to open a file by its numeric ID, provided the filesystem supports file IDs (which NTFS does for compatibility with POSIX and NFS). Fortunately, CBFS Filter 2024 forwards these IDs to event handlers when a file is opened by ID. It is currently the application's responsibility to convert an ID to a file path if necessary. IDs are passed in the filename parameter in a special format, specifically as "|XXXXXXXX|" — a hexadecimal number enclosed in pipe characters.
Concurrent Names of Files
One of the powerful yet sometimes risky features of the NTFS filesystem is its support for hard links. Despite their name, hard links essentially serve as alternative file names. When a file is a "named sequence of bytes," that sequence can have multiple names, each of which may be listed in the same or different directories. Each name is independent and holds equal rights to the others—it can be modified (renamed) or removed without affecting the file or its other names. The file data is only deleted from the disk when the last name associated with it is removed.
For CBFilter, hard links have a significant impact: when a path-based rule is added, a hard link created in a different directory can allow a user to bypass that rule. To counteract this, applications can handle the BeforeCreateHardLink or AfterCreateHardLink events or collect and check all names associated with a file.
If an application tracks the creation of links, it can either disable their creation or simply log the event, potentially adding a different rule to remember the details of the new hard link. The operating system also provides functions for enumerating file names, specifically FindFirstFileNameW and FindNextFileNameW, which can be useful in certain scenarios.
To collect all names of a file for proper rule application, an application should enable the RetrieveLinkNamesOnOpen configuration setting:
filter.Config("RetrieveLinkNamesOnOpen=True");
When enabled, this setting instructs the driver to enumerate all names using the OS functions and store them within the file object created when a file is opened or created. Subsequently, all known names are matched against the defined rules.
Renaming of Files
Files, or rather their named entries in the directories, can be renamed and moved. Renaming and moving are essentially the same operation, as both can involve changing the file's name and the directory in which that name is listed.
Renaming or moving a file outside the monitored scope can be detected because the operating system first opens the file for renaming. This file opening is tracked by the driver, which can then respond appropriately.
When a file is renamed or moved into the monitored scope (e.g., into a specific directory), it is not opened in its new location. If an application sets rules for any of the BeforeRenameOrMoveFile, AfterRenameOrMoveFile, or NotifyRenameOrMoveFile events, and its mask matches the new name, the application will receive the corresponding event. However, it will not receive a creation or opening event for the file with this new name (this does not apply if another file existed there that needed to be overwritten, in which case, that previous file would be opened before the operation).
Moving a file into a directory or another monitored scope can pose a security risk; therefore, security and protection applications should closely monitor file renaming and moving.
A rename operation can be simulated through a pair of actions: first, a hard link to the file is created at the new destination, then the original name is deleted, leaving the file data accessible under the new name. Thus, if an application needs to track file renaming, it should also consider the implications of hard links.
Zero, One, Or Many?
How many names does your event handler need? The answer depends on the use case, and CBFilter accommodates every situation.
The simplest scenario occurs when filenames are not necessary—if the rule is set with the appropriate mask, names do not need further analysis. For example, to protect all files in a directory from deletion, you simply set a rule to "c:\path\to\directory\*.*" and that’s all there is to it.
To skip names and speed up operations, you can enable the OmitEventFileNames configuration setting:
filter.Config("OmitEventFileNames=True");
In this case, names are still retrievable; you just need to call the GetEventFileName method.
By default, the component passes one filename—the name used in the request. This typically applies to file creation or opening, after which the name is associated with the opened handle and remains unchanged unless the file is renamed.
Having only one filename allows some of the aforementioned tricks to be possible or more likely, especially when an event handler analyzes the name.
CBFilter can also collect and maintain all names of the files, including both current and previous names (those that existed before the file was renamed or moved). To utilize this functionality, enable the PassAllFileNames configuration setting and, optionally, the RetrieveLinkNamesOnOpen setting:
filter.Config("PassAllFileNames=True");
filter.Config("RetrieveLinkNamesOnOpen=True");
The first setting instructs the driver to track all names and pass them to event handlers. The second setting enables the driver to request hard link names when a file is opened in order to have a more complete list.
When a file is opened by its ID, that ID is added to the list of current filenames. The names are separated by a newline character (numeric code 10). Current and previous names are combined in one list, separated by an empty line.
When a file or directory is opened and the driver knows multiple names associated with it, each known name is matched against the rules to determine the appropriate action (whether to block or redirect access, fire an event, or skip the operation).
Getting Started with CBFilter
You can find an evaluation version of the SDK for your platform and programming language in the Download Center. After downloading and installing the SDK, you will receive a library, sample code, and comprehensive documentation on your system. The documentation includes a "Getting Started" section with programming instructions. Additionally, free technical support is available during the evaluation phase.
We appreciate your feedback. If you have any questions, comments, or suggestions about this article please contact our support team at support@callback.com.