├── .cargo └── config.toml ├── .github └── workflows │ └── rust.yml ├── .gitignore ├── CHANGELOG.md ├── Cargo.toml ├── DEBUG.md ├── EWDKbuild.md ├── LICENSE.md ├── MINIFILTER.md ├── README.md ├── minifilter ├── .gitignore ├── RWatch.sln ├── SharedDefs │ ├── Common.h │ ├── SharedDefs.h │ └── Unique_ptr.h └── snFilter │ ├── Communication.cpp │ ├── Communication.h │ ├── DriverData.cpp │ ├── DriverData.h │ ├── HashTable.h │ ├── KernelCommon.cpp │ ├── KernelCommon.h │ ├── KernelString.cpp │ ├── KernelString.h │ ├── ShanonEntropy.cpp │ ├── ShanonEntropy.h │ ├── install.cmd │ ├── snFilter.cpp │ ├── snFilter.h │ ├── snFilter.inf │ ├── snFilter.rc │ ├── snFilter.vcxproj │ └── snFilter.vcxproj.filters ├── readme_resources ├── boot.png ├── disable_memory_integrity.png ├── example.gif ├── final_look.png ├── registry.png ├── shared_def.png ├── symbol_search_path.png ├── virtualbox_download.png ├── vm_setup.png └── windows_additions.png └── src ├── bin └── minifilter.rs ├── driver_comm.rs ├── lib.rs └── shared_def.rs /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | rustdocflags = ["--document-private-items"] -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: windows-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v3 19 | - name: Build 20 | run: cargo build --verbose 21 | - name: Run tests 22 | run: cargo test --verbose 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /Cargo.lock 3 | /.idea 4 | /.vs 5 | 6 | ## Ignore Visual Studio temporary files, build results, and 7 | ## files generated by popular Visual Studio add-ons. 8 | 9 | # User-specific files 10 | *.suo 11 | *.user 12 | *.sln.docstates 13 | 14 | # User-specific files (MonoDevelop/Xamarin Studio) 15 | *.userprefs 16 | 17 | # Build results 18 | [Dd]ebug/ 19 | [Dd]ebugPublic/ 20 | [Rr]elease/ 21 | [Rr]eleases/ 22 | x64/ 23 | x86/ 24 | bld/ 25 | [Bb]in/ 26 | [Oo]bj/ 27 | [Ll]og/ 28 | 29 | # Visual Studio 2015 cache/options directory 30 | .vs/ 31 | # Uncomment if you have tasks that create the project's static files in wwwroot 32 | #wwwroot/ 33 | 34 | # MSTest test Results 35 | [Tt]est[Rr]esult*/ 36 | [Bb]uild[Ll]og.* 37 | 38 | # NUNIT 39 | *.VisualState.xml 40 | TestResult.xml 41 | 42 | # Build Results of an ATL Project 43 | [Dd]ebugPS/ 44 | [Rr]eleasePS/ 45 | dlldata.c 46 | 47 | # DNX 48 | project.lock.json 49 | project.fragment.lock.json 50 | artifacts/ 51 | 52 | *_i.c 53 | *_p.c 54 | *_i.h 55 | *.ilk 56 | *.meta 57 | *.obj 58 | *.pch 59 | *.pdb 60 | *.pgc 61 | *.pgd 62 | *.rsp 63 | *.sbr 64 | *.tlb 65 | *.tli 66 | *.tlh 67 | *.tmp 68 | *.tmp_proj 69 | *.log 70 | *.vspscc 71 | *.vssscc 72 | .builds 73 | *.pidb 74 | *.svclog 75 | *.scc 76 | 77 | # Chutzpah Test files 78 | _Chutzpah* 79 | 80 | # Visual C++ cache files 81 | ipch/ 82 | *.aps 83 | *.ncb 84 | *.opendb 85 | *.opensdf 86 | *.sdf 87 | *.cachefile 88 | *.VC.db 89 | *.VC.VC.opendb 90 | 91 | # Visual Studio profiler 92 | *.psess 93 | *.vsp 94 | *.vspx 95 | *.sap 96 | 97 | # TFS 2012 Local Workspace 98 | $tf/ 99 | 100 | # Guidance Automation Toolkit 101 | *.gpState 102 | 103 | # ReSharper is a .NET coding add-in 104 | _ReSharper*/ 105 | *.[Rr]e[Ss]harper 106 | *.DotSettings.user 107 | 108 | # JustCode is a .NET coding add-in 109 | .JustCode 110 | 111 | # TeamCity is a build add-in 112 | _TeamCity* 113 | 114 | # DotCover is a Code Coverage Tool 115 | *.dotCover 116 | 117 | # NCrunch 118 | _NCrunch_* 119 | .*crunch*.local.xml 120 | nCrunchTemp_* 121 | 122 | # MightyMoose 123 | *.mm.* 124 | AutoTest.Net/ 125 | 126 | # Web workbench (sass) 127 | .sass-cache/ 128 | 129 | # Installshield output folder 130 | [Ee]xpress/ 131 | 132 | # DocProject is a documentation generator add-in 133 | DocProject/buildhelp/ 134 | DocProject/Help/*.HxT 135 | DocProject/Help/*.HxC 136 | DocProject/Help/*.hhc 137 | DocProject/Help/*.hhk 138 | DocProject/Help/*.hhp 139 | DocProject/Help/Html2 140 | DocProject/Help/html 141 | 142 | # Click-Once directory 143 | publish/ 144 | 145 | # Publish Web Output 146 | *.[Pp]ublish.xml 147 | *.azurePubxml 148 | # TODO: Comment the next line if you want to checkin your web deploy settings 149 | # but database connection strings (with potential passwords) will be unencrypted 150 | #*.pubxml 151 | *.publishproj 152 | 153 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 154 | # checkin your Azure Web App publish settings, but sensitive information contained 155 | # in these scripts will be unencrypted 156 | PublishScripts/ 157 | 158 | # NuGet Packages 159 | *.nupkg 160 | # The packages folder can be ignored because of Package Restore 161 | **/packages/* 162 | # except build/, which is used as an MSBuild target. 163 | !**/packages/build/ 164 | # Uncomment if necessary however generally it will be regenerated when needed 165 | #!**/packages/repositories.config 166 | # NuGet v3's project.json files produces more ignoreable files 167 | *.nuget.props 168 | *.nuget.targets 169 | 170 | # Microsoft Azure Build Output 171 | csx/ 172 | *.build.csdef 173 | 174 | # Microsoft Azure Emulator 175 | ecf/ 176 | rcf/ 177 | 178 | # Windows Store app package directories and files 179 | AppPackages/ 180 | BundleArtifacts/ 181 | Package.StoreAssociation.xml 182 | _pkginfo.txt 183 | 184 | # Visual Studio cache files 185 | # files ending in .cache can be ignored 186 | *.[Cc]ache 187 | # but keep track of directories ending in .cache 188 | !*.[Cc]ache/ 189 | 190 | # Others 191 | ClientBin/ 192 | ~$* 193 | *~ 194 | *.dbmdl 195 | *.dbproj.schemaview 196 | *.jfm 197 | *.pfx 198 | *.publishsettings 199 | node_modules/ 200 | orleans.codegen.cs 201 | 202 | # Since there are multiple workflows, uncomment next line to ignore bower_components 203 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 204 | #bower_components/ 205 | 206 | # RIA/Silverlight projects 207 | Generated_Code/ 208 | 209 | # Backup & report files from converting an old project file 210 | # to a newer Visual Studio version. Backup files are not needed, 211 | # because we have git ;-) 212 | _UpgradeReport_Files/ 213 | Backup*/ 214 | UpgradeLog*.XML 215 | UpgradeLog*.htm 216 | 217 | # SQL Server files 218 | *.mdf 219 | *.ldf 220 | 221 | # Business Intelligence projects 222 | *.rdl.data 223 | *.bim.layout 224 | *.bim_*.settings 225 | 226 | # Microsoft Fakes 227 | FakesAssemblies/ 228 | 229 | # GhostDoc plugin setting file 230 | *.GhostDoc.xml 231 | 232 | # Node.js Tools for Visual Studio 233 | .ntvs_analysis.dat 234 | 235 | # Visual Studio 6 build log 236 | *.plg 237 | 238 | # Visual Studio 6 workspace options file 239 | *.opt 240 | 241 | # Visual Studio LightSwitch build output 242 | **/*.HTMLClient/GeneratedArtifacts 243 | **/*.DesktopClient/GeneratedArtifacts 244 | **/*.DesktopClient/ModelManifest.xml 245 | **/*.Server/GeneratedArtifacts 246 | **/*.Server/ModelManifest.xml 247 | _Pvt_Extensions 248 | 249 | # Paket dependency manager 250 | .paket/paket.exe 251 | paket-files/ 252 | 253 | # FAKE - F# Make 254 | .fake/ 255 | 256 | # JetBrains Rider 257 | .idea/ 258 | *.sln.iml 259 | 260 | # CodeRush 261 | .cr/ 262 | 263 | # Python Tools for Visual Studio (PTVS) 264 | __pycache__/ 265 | *.pyc 266 | /Application/Python37/Lib/encodings 267 | /Application/Python37/Lib/importlib 268 | /Application/Python37/Lib/collections 269 | /Application/Python37/Lib 270 | 271 | # Custom 272 | *.exe 273 | /src/temp.rs 274 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # v0.8.0 2 | 3 | - Add [DEBUG](DEBUG.md) docs 4 | - Upgrade `sysinfo` from 0.27.1 -> 0.28.0 5 | - Increase capacity of certain objects in minifilter 6 | 7 | # v0.7.0 8 | 9 | - Refactor minifilter 10 | - Improve printing speed, for [example](src/bin/minifilter.rs) 11 | - General stability improvements 12 | - Upgrade `windows-rs` 0.43.0 -> 0.44.0 13 | - Add rough [`SystemTime`](https://doc.rust-lang.org/std/time/struct.SystemTime.html) to `IPR` messages and compare them 14 | using the same 15 | - Better handling of `exepath` for `IOMessage` IRP 16 | 17 | # v0.6.0 18 | 19 | - Fix an issue of floating point operations in the kernel driver 20 | - Performance improvements 21 | 22 | # v0.5.5 23 | 24 | - Upgrade `C` standard to `C11` 25 | - General stability improvements around IRQL, DriverEntry, etc 26 | - Update `sysinfo` to `0.27.1` 27 | 28 | # v0.5.0 29 | 30 | - Replace `ZwClose` with `FltClose` in minifilter to solve potential memory leak 31 | - Remove unused dependencies and add categories to `Cargo.toml` 32 | - Vastly improve documentation 33 | - Refactor code to be more readable and conscience 34 | 35 | # v0.4.0 36 | 37 | - Improve performance even further 38 | - Add `#[inline]` calls to all functions 39 | - Remove `x86`, `arm` and `arm64` targets from minifilter 40 | - Upgrade to `c++20` standard for minifilter 41 | - Reduce waiting time, for [example](src/bin/minifilter.rs) 42 | 43 | # v0.3.0 44 | 45 | - Improve performance of minifilter by using `-O2` and `-LTO` alongside release build 46 | - Improve performance of [example](src/bin/minifilter.rs) by not locking and releasing IO 47 | - Refactor and reformat minifilter 48 | - Stop using debug libraries for minifilter 49 | - Update readme and add changelog 50 | - Add LICENSE -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "fsfilter-rs" 3 | version = "0.8.0" 4 | edition = "2021" 5 | authors = ["sn99 "] 6 | description = "A rust library to monitor filesystem and more in windows" 7 | repository = "https://github.com/SubconsciousCompute/fsfilter-rs" 8 | homepage = "https://github.com/SubconsciousCompute/fsfilter-rs" 9 | license = "MIT" 10 | readme = "README.md" 11 | keywords = ["filesystem", "driver", "windows", "minifilter", "syscalls"] 12 | categories = ["development-tools", "os::windows-apis", "filesystem", "api-bindings"] 13 | documentation = "https://docs.rs/fsfilter-rs" 14 | 15 | [dependencies] 16 | sysinfo = "0.28.0" 17 | widestring = "1.0.1" 18 | serde = { version = "1.0.130", features = ["derive"] } 19 | num-derive = "0.3" 20 | num-traits = "0.2.14" 21 | wchar = "0.11.0" 22 | 23 | [dependencies.windows] 24 | version = "0.44.0" 25 | features = [ 26 | "Win32_Storage_InstallableFileSystems", 27 | "Win32_Foundation", 28 | "Win32_Security", 29 | "Win32_Storage_FileSystem", 30 | "Win32_System_Threading", 31 | "Win32_System_ProcessStatus", 32 | "Win32_System_Diagnostics_Debug", 33 | ] 34 | 35 | [profile.release] 36 | lto = true 37 | codegen-units = 1 38 | opt-level = 3 -------------------------------------------------------------------------------- /DEBUG.md: -------------------------------------------------------------------------------- 1 | # Setting up kernel mode debugging 2 | 3 | ### Tools Used: 4 | 5 | - [WinDbg](https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/debugging-using-windbg-preview) 6 | - [VMware](https://www.vmware.com/in/products/workstation-player.html) 7 | - [VirtualKD-Redux](https://github.com/4d61726b/VirtualKD-Redux) 8 | 9 | ## Steps 10 | 11 | *All steps are to be executed in Administrative CMD* 12 | 13 | 🟢 - Host 14 | 15 | 🔵 - VM 16 | 17 | - 🟢 Disable Memory integrity ![img.png](readme_resources/disable_memory_integrity.png) 18 | - 🟢 Run `bcdedit /set hypervisorlaunchtype off` 19 | - 🟢 Install [VMware](https://www.vmware.com/in/products/workstation-player.html) 20 | - [Download Windows](https://www.microsoft.com/software-download/windows11) and follow any tutorial to set up a windows 21 | VM (You might want to configure space to be around 50GB+, ram around 4GB, enable hardware acceleration and so on) 22 | - 🔵 You can skip Microsoft sign in by using banned email id, eg: use `no@thankyou.com` and type in any password, you 23 | should now be able to skip the sign-in process 24 | - 🔵 Install `VMware tools` 25 | - 🔵 Right click on `Start` and click on `Run` 26 | - 🔵 Type in `regedit` 27 | - 🔵 Now goto `HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager` 28 | - 🔵 Right click on `Session Manager -> New -> Key` 29 | - 🔵 Rename the new key to `Debug Print Filter` and select it 30 | - 🔵 Right-click the left column blank space and choose `New -> DWORD (32-bit) Value` 31 | - 🔵 Rename it to `DEFAULT` 32 | - 🔵 Double click and change its value to `ffffffff` ![img.png](readme_resources/registry.png) 33 | - 🟢 Now download [driver loader](https://www.osronline.com/article.cfm%5Earticle=157.htm) 34 | - 🟢 Extract it 35 | - 🔵 Drag and drop `"..\osrloaderv30\Projects\OsrLoader\kit\WLH\AMD64\FRE\OSRLOADER.exe"` to VM 36 | - Next we move on to [VirtualKD-Redux](https://github.com/4d61726b/VirtualKD-Redux) 37 | - You can follow 38 | the [tutorial here](https://github.com/4d61726b/VirtualKD-Redux/blob/master/VirtualKD-Redux/Docs/Tutorial.md) 39 | - 🔵 `F8` bcdedit/debug and Select `Disable Driver Sig...` ![img.png](readme_resources/boot.png) 40 | - 🟢 Now open `vmmon64.exe` as administrator 41 | - 🔵 Now open the windows VM machine, if you have followed correctly then you WinDbg should launch automatically and 42 | windows boot-up should halt till you `Debug -> Go` in WinDbg 43 | - 🟢 Now goto `Debug -> Break`, followed by `File -> Symbol File Path ...` and 44 | put `SRV*c:\symbols* http://msdl.microsoft.com/download/symbols`, click on `Reload` and 45 | then `Ok` ![img.png](readme_resources/final_look.png), finally `Debug -> Go` 46 | - 🔵 If this does not work, then in an elevated Command Prompt window, enter: `bcdedit /debug on` 47 | and `bcdedit /dbgsettings serial debugport:2 baudrate:115200`, you might have to change debug port to `1` 48 | - 🟢 You can also space this workspace in WinDbg by `File -> Save Workspace As...` 49 | - 🟢 Goto `Debug -> Break`, followed by `File -> Symbol File Path ...` and 50 | put `C:\Users\sn99\CLionProjects\fsfilter-rs\minifilter\x64\Debug` or wherever the `.pdb` file is (this should be in 51 | the same build folder as `.sys` driver file), click on `Reload` and then `Ok`, 52 | finally `Debug -> Go`, and then `File -> Save Workspace` ![img.png](readme_resources/symbol_search_path.png) 53 | 54 | ## References 55 | 56 | - [Windows Kernel Programming Tutorial 1 - Setting up Environment - Part 1](https://youtu.be/XUlbYRFFYf0) 57 | - [Windows Kernel Programming Tutorial 2 - Setting up Environment - Part 2](https://youtu.be/nF3aYhmfL-0) 58 | - [Debugging Tools for Windows (WinDbg, KD, CDB, NTSD)](https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/) 59 | - [Microsoft public symbol server](https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/microsoft-public-symbols) 60 | - [Get started with WinDbg (kernel-mode)](https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/getting-started-with-windbg--kernel-mode-) 61 | - [Windbg always show "Waiting to reconnect..."](https://social.msdn.microsoft.com/Forums/Windows/en-US/2332bc93-c618-41f2-8be1-1e72d8089d23/windbg-always-show-quotwaiting-to-reconnectquot?forum=wdk) 62 | - [VirtualKD-Redux Tutorial](https://github.com/4d61726b/VirtualKD-Redux/blob/master/VirtualKD-Redux/Docs/Tutorial.md) 63 | - [Driver Loader](https://www.osronline.com/article.cfm%5Earticle=157.htm) 64 | -------------------------------------------------------------------------------- /EWDKbuild.md: -------------------------------------------------------------------------------- 1 | # Building using EWDK (Enterprise WDK) 2 | 3 | The EWDK is a standalone, self-contained command-line environment for building drivers. It includes Visual Studio Build 4 | Tools, the SDK, and the WDK. 5 | 6 | 1. Download the [EWDK](https://docs.microsoft.com/en-us/windows-hardware/drivers/download-the-wdk) and mount it 7 | - It should be mounted as a drive something like `D:` and contain `LaunchBuildEnv.cmd` 8 | 2. Now from inside the `fsfilter-rs\minifilter` directory, run the following commands: 9 | 1. From an Administrator command prompt, run `call "D:\LaunchBuildEnv.cmd"` 10 | 2. Followed by `msbuild RWatch.sln /m /p:configuration=Release /p:platform=x64 /p:RunCodeAnalysis=false` 11 | 12 | The next steps are the same as [MINIFILTER.md](MINIFILTER.md). 13 | 14 | ## References 15 | 16 | - [download-the-wdk](https://learn.microsoft.com/en-us/windows-hardware/drivers/download-the-wdk) 17 | - [using-the-enterprise-wdk](https://learn.microsoft.com/en-us/windows-hardware/drivers/develop/using-the-enterprise-wdk) -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Siddharth Naithani 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MINIFILTER.md: -------------------------------------------------------------------------------- 1 | # minifilter-rs 2 | 3 | **Use `cargo doc --no-deps --document-private-items --open` to read Documentation** 4 | 5 | ## Table of Contents 6 | 7 |
8 | Table of Contents 9 | 10 | - [Minifilter Driver](https://github.com/SubconsciousCompute/fsfilter-rs#minifilter-driver) 11 | - [Building Driver](https://github.com/SubconsciousCompute/fsfilter-rs#building-driver) 12 | - [Installing Driver](https://github.com/SubconsciousCompute/fsfilter-rs#building-driver) 13 | - [Loading/Removing Driver](https://github.com/SubconsciousCompute/fsfilter-rs#loadingremoving-driver) 14 | - [Rust Application](https://github.com/SubconsciousCompute/fsfilter-rs#rust-application) 15 | - [Building Rust App](https://github.com/SubconsciousCompute/fsfilter-rs#building-rust-app) 16 | - [Running Rust App](https://github.com/SubconsciousCompute/fsfilter-rs#running-rust-app) 17 | - [What and the How](https://github.com/SubconsciousCompute/fsfilter-rs#what-and-the-how) 18 | 19 |
20 | 21 | ## Minifilter Driver 22 | 23 | ### Building Driver 24 | 25 | 1. Open `VS 2022` 26 | 2. Goto `minifilter-rs -> minifilter -> RWatch.sln` 27 | 3. Build the solution in `Release` mode with `x64` 28 | 29 | **NOTE: Enable Loading of Test Signed Drivers by executing `Bcdedit.exe -set TESTSIGNING ON` in administrative cmd** 30 | 31 | ### Installing Driver 32 | 33 | 1. Open Powershell or command prompt as Administrator 34 | 2. `RUNDLL32.EXE SETUPAPI.DLL,InstallHinfSection DefaultInstall 132 \minifilter-rs\minifilter\x64\Debug\snFilter.inf` 35 | 36 | You should be able to see the driver at `"C:\Windows\System32\drivers\snFilter.sys"` 37 | 38 | ### Loading/Removing Driver 39 | 40 | 1. Open Powershell or command prompt as Administrator 41 | 2. Start the driver using `sc start snFilter`, expected output: 42 | ``` 43 | SERVICE_NAME: snFilter 44 | TYPE : 2 FILE_SYSTEM_DRIVER 45 | STATE : 4 RUNNING 46 | (STOPPABLE, NOT_PAUSABLE, IGNORES_SHUTDOWN) 47 | WIN32_EXIT_CODE : 0 (0x0) 48 | SERVICE_EXIT_CODE : 0 (0x0) 49 | CHECKPOINT : 0x0 50 | WAIT_HINT : 0x0 51 | PID : 0 52 | FLAGS : 53 | ``` 54 | 3. Stop the driver using `sc stop snFilter`, should give the following output: 55 | ``` 56 | SERVICE_NAME: snFilter 57 | TYPE : 2 FILE_SYSTEM_DRIVER 58 | STATE : 1 STOPPED 59 | WIN32_EXIT_CODE : 0 (0x0) 60 | SERVICE_EXIT_CODE : 0 (0x0) 61 | CHECKPOINT : 0x0 62 | WAIT_HINT : 0x0 63 | ``` 64 | 4. Remove it by `sc delete snFilter`, should give the following output: 65 | ``` 66 | [SC] DeleteService SUCCESS 67 | ``` 68 | 69 | You can also run `Fltmc.exe` to see the currently loaded drivers: 70 | 71 | ``` 72 | 73 | Filter Name Num Instances Altitude Frame 74 | ------------------------------ ------------- ------------ ----- 75 | bindflt 1 409800 0 76 | snFilter 4 378781 0 // our minifilter driver 77 | WdFilter 5 328010 0 78 | storqosflt 0 244000 0 79 | wcifs 0 189900 0 80 | CldFlt 0 180451 0 81 | FileCrypt 0 141100 0 82 | luafv 1 135000 0 83 | npsvctrig 1 46000 0 84 | Wof 3 40700 0 85 | FileInfo 5 40500 0 86 | ``` 87 | 88 | ## Rust Application 89 | 90 | ### Building Rust App 91 | 92 | Simply use `cargo build --release` to build the application 93 | 94 | ### Running Rust App 95 | 96 | Use `cargo run --bin minifilter --release` to run the application 97 | 98 | The program starts to print the `IOMessage` which is defined like: 99 | 100 | ```rust 101 | #[repr(C)] 102 | pub struct IOMessage { 103 | /// The file extension 104 | pub extension: [wchar_t; 12], 105 | /// Hard Disk Volume Serial Number where the file is saved (from [`FILE_ID_INFO`]) 106 | pub file_id_vsn: c_ulonglong, 107 | /// File ID on the disk ([`FILE_ID_INFO`]) 108 | pub file_id_id: [u8; 16], 109 | /// Number of bytes transferred (`IO_STATUS_BLOCK.Information`) 110 | pub mem_sized_used: c_ulonglong, 111 | /// (Optional) File Entropy calculated by the driver 112 | pub entropy: f64, 113 | /// Pid responsible for this io activity 114 | pub pid: c_ulong, 115 | /// Windows IRP Type caught by the minifilter: 116 | /// - NONE (0) 117 | /// - READ (1) 118 | /// - WRITE (2) 119 | /// - SETINFO (3) 120 | /// - CREATE (4) 121 | /// - CLEANUP (5) 122 | pub irp_op: c_uchar, 123 | /// Is the entropy calculated? 124 | pub is_entropy_calc: u8, 125 | /// Type of i/o operation: 126 | /// - FILE_CHANGE_NOT_SET (0) 127 | /// - FILE_OPEN_DIRECTORY (1) 128 | /// - FILE_CHANGE_WRITE (2) 129 | /// - FILE_CHANGE_NEW_FILE (3) 130 | /// - FILE_CHANGE_RENAME_FILE (4) 131 | /// - FILE_CHANGE_EXTENSION_CHANGED (5) 132 | /// - FILE_CHANGE_DELETE_FILE (6) 133 | /// - FILE_CHANGE_DELETE_NEW_FILE (7) 134 | /// - FILE_CHANGE_OVERWRITE_FILE (8) 135 | pub file_change: c_uchar, 136 | /// The driver has the ability to monitor specific directories only (feature currently not used): 137 | /// - FILE_NOT_PROTECTED (0): Monitored dirs do not contained this file 138 | /// - FILE_PROTECTED (1) 139 | /// - FILE_MOVED_IN (2) 140 | /// - FILE_MOVED_OUT (3) 141 | pub file_location_info: c_uchar, 142 | /// File path on the disk 143 | pub filepathstr: String, 144 | /// Group Identifier (maintained by the minifilter) of the operation 145 | pub gid: c_ulonglong, 146 | /// see class [`RuntimeFeatures`] 147 | pub runtime_features: RuntimeFeatures, 148 | /// Size of the file. Can be equal to -1 if the file path is not found. 149 | pub file_size: i64, 150 | /// Rough time at which the IRP was created 151 | pub time: SystemTime, 152 | } 153 | ``` 154 | 155 | We end the process using `ctrl + c` in the example video: 156 | ![video](readme_resources/example.gif) 157 | 158 | #### NOTE: 159 | 160 | - Might fail if not run with administrative privileges 161 | - You need to [load and start the driver]((https://github.com/SubconsciousCompute/fsfilter-rs#loadingremoving-driver)) before running 162 | the program or else it will error out 163 | 164 | ## What and the How 165 | 166 | We basically share definition between the mini-filter and Rust using `#[repr(C)]` 167 | 168 | ![shared_def](readme_resources/shared_def.png) 169 | 170 | We use [channels](https://doc.rust-lang.org/std/sync/mpsc/fn.channel.html) to process 171 | all [IRPs](https://docs.microsoft.com/en-us/windows-hardware/drivers/ifs/irps-are-different-from-fast-i-o). 172 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # fsfilter-rs 2 | 3 | [![Rust](https://github.com/SubconsciousCompute/fsfilter-rs/actions/workflows/rust.yml/badge.svg)](https://github.com/SubconsciousCompute/fsfilter-rs/actions/workflows/rust.yml) 4 | [![Crates.io](https://img.shields.io/crates/v/fsfilter-rs?style=flat-square)](https://crates.io/crates/fsfilter-rs) 5 | [![docs.rs](https://img.shields.io/docsrs/fsfilter-rs?style=flat-square)](https://docs.rs/fsfilter-rs/latest/fsfilter_rs/) 6 | 7 | A rust library to monitor filesystem and more in windows. 8 | 9 | ![shared_def](readme_resources/shared_def.png) 10 | 11 | **Also see [poc-windows-rust-filter](https://github.com/SubconsciousCompute/poc-windows-rust-filter) for a pure Rust minifilter.** 12 | 13 | ## MINIFILTER 14 | 15 | See [MINIFILTER.md](MINIFILTER.md) for building the minifilter or just [right click install using the `.inf` file 16 | provided in releases](https://github.com/SubconsciousCompute/fsfilter-rs/releases/latest/download/snFilter.zip). 17 | 18 | You can also build using [EWDK](EWDKbuild.md) if you don't want to install Visual Studio, SDK and WDK. 19 | 20 | **NOTE: By default, it is built for Windows 10 and above.** 21 | 22 | **NOTE: Enable Loading of Test Signed Drivers by executing `Bcdedit.exe -set TESTSIGNING ON` in administrative cmd.** 23 | 24 | ## RUNNING EXAMPLE 25 | 26 | Use `cargo run --bin minifilter --release` to run the example application or just [run the `.exe` provided in 27 | releases](https://github.com/SubconsciousCompute/fsfilter-rs/releases/latest/download/minifilter.exe) as administrator( 28 | for 29 | some reason the new default terminal (not the one that opens when you run it as administrator) on 2H22 is very, very 30 | slow). 31 | 32 | The program starts to print the `IOMessage` which is defined like: 33 | 34 | ```rust 35 | #[repr(C)] 36 | pub struct IOMessage { 37 | /// The file extension 38 | pub extension: [wchar_t; 12], 39 | /// Hard Disk Volume Serial Number where the file is saved (from [`FILE_ID_INFO`]) 40 | pub file_id_vsn: c_ulonglong, 41 | /// File ID on the disk ([`FILE_ID_INFO`]) 42 | pub file_id_id: [u8; 16], 43 | /// Number of bytes transferred (`IO_STATUS_BLOCK.Information`) 44 | pub mem_sized_used: c_ulonglong, 45 | /// (Optional) File Entropy calculated by the driver 46 | pub entropy: f64, 47 | /// Pid responsible for this io activity 48 | pub pid: c_ulong, 49 | /// Windows IRP Type caught by the minifilter: 50 | /// - NONE (0) 51 | /// - READ (1) 52 | /// - WRITE (2) 53 | /// - SETINFO (3) 54 | /// - CREATE (4) 55 | /// - CLEANUP (5) 56 | pub irp_op: c_uchar, 57 | /// Is the entropy calculated? 58 | pub is_entropy_calc: u8, 59 | /// Type of i/o operation: 60 | /// - FILE_CHANGE_NOT_SET (0) 61 | /// - FILE_OPEN_DIRECTORY (1) 62 | /// - FILE_CHANGE_WRITE (2) 63 | /// - FILE_CHANGE_NEW_FILE (3) 64 | /// - FILE_CHANGE_RENAME_FILE (4) 65 | /// - FILE_CHANGE_EXTENSION_CHANGED (5) 66 | /// - FILE_CHANGE_DELETE_FILE (6) 67 | /// - FILE_CHANGE_DELETE_NEW_FILE (7) 68 | /// - FILE_CHANGE_OVERWRITE_FILE (8) 69 | pub file_change: c_uchar, 70 | /// The driver has the ability to monitor specific directories only (feature currently not used): 71 | /// - FILE_NOT_PROTECTED (0): Monitored dirs do not contained this file 72 | /// - FILE_PROTECTED (1) 73 | /// - FILE_MOVED_IN (2) 74 | /// - FILE_MOVED_OUT (3) 75 | pub file_location_info: c_uchar, 76 | /// File path on the disk 77 | pub filepathstr: String, 78 | /// Group Identifier (maintained by the minifilter) of the operation 79 | pub gid: c_ulonglong, 80 | /// see class [`RuntimeFeatures`] 81 | pub runtime_features: RuntimeFeatures, 82 | /// Size of the file. Can be equal to -1 if the file path is not found. 83 | pub file_size: i64, 84 | /// Rough time at which the IRP was created 85 | pub time: SystemTime, 86 | } 87 | ``` 88 | 89 | We end the process using `ctrl + c` in the example video: 90 | ![video](readme_resources/example.gif) 91 | 92 | ## PERFORMANCE 93 | 94 | The performance of the minifilter doesn't really exceed `<1%` of the CPU usage (I never saw it tickle even to 1% while 95 | running scripts to make multiple temporary files). Although depending on you console if you try running 96 | `cargo run --bin minifilter --release` you might see spikes reaching `1-3%` but that is because of the console itself( 97 | comment out the `writeln!` in the bin example) or try changing consoles (maybe run `minifilter.exe` directly). 98 | 99 | ## DEBUGGING 100 | 101 | See [DEBUG](DEBUG.md) for setting up VMware windows machine debugging 102 | with [VirtualKD-Redux](https://github.com/4d61726b/VirtualKD-Redux) 103 | and [WinDbg](https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/debugging-using-windbg-preview). 104 | 105 | ## MAINTAINERS 106 | 107 | Feel free to open issues to ask any questions/queries. You're free to ping the current maintainers. 108 | 109 | Currently maintained by the following: 110 | 111 | - [sn99](https://github.com/sn99) (original author) 112 | 113 | Previous maintainers: 114 | 115 | - N/A 116 | 117 | ## LICENSE 118 | 119 | This project is licensed under the terms of the [MIT license](LICENSE.md). 120 | 121 | ## ACKNOWLEDGEMENTS 122 | 123 | - [RansomWatch](https://github.com/RafWu/RansomWatch) 124 | - [SitinCloud](https://github.com/SitinCloud) 125 | - [SubconsciousCompute](https://github.com/SubconsciousCompute) 126 | -------------------------------------------------------------------------------- /minifilter/.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | project.fragment.lock.json 46 | artifacts/ 47 | 48 | *_i.c 49 | *_p.c 50 | *_i.h 51 | *.ilk 52 | *.meta 53 | *.obj 54 | *.pch 55 | *.pdb 56 | *.pgc 57 | *.pgd 58 | *.rsp 59 | *.sbr 60 | *.tlb 61 | *.tli 62 | *.tlh 63 | *.tmp 64 | *.tmp_proj 65 | *.log 66 | *.vspscc 67 | *.vssscc 68 | .builds 69 | *.pidb 70 | *.svclog 71 | *.scc 72 | 73 | # Chutzpah Test files 74 | _Chutzpah* 75 | 76 | # Visual C++ cache files 77 | ipch/ 78 | *.aps 79 | *.ncb 80 | *.opendb 81 | *.opensdf 82 | *.sdf 83 | *.cachefile 84 | *.VC.db 85 | *.VC.VC.opendb 86 | 87 | # Visual Studio profiler 88 | *.psess 89 | *.vsp 90 | *.vspx 91 | *.sap 92 | 93 | # TFS 2012 Local Workspace 94 | $tf/ 95 | 96 | # Guidance Automation Toolkit 97 | *.gpState 98 | 99 | # ReSharper is a .NET coding add-in 100 | _ReSharper*/ 101 | *.[Rr]e[Ss]harper 102 | *.DotSettings.user 103 | 104 | # JustCode is a .NET coding add-in 105 | .JustCode 106 | 107 | # TeamCity is a build add-in 108 | _TeamCity* 109 | 110 | # DotCover is a Code Coverage Tool 111 | *.dotCover 112 | 113 | # NCrunch 114 | _NCrunch_* 115 | .*crunch*.local.xml 116 | nCrunchTemp_* 117 | 118 | # MightyMoose 119 | *.mm.* 120 | AutoTest.Net/ 121 | 122 | # Web workbench (sass) 123 | .sass-cache/ 124 | 125 | # Installshield output folder 126 | [Ee]xpress/ 127 | 128 | # DocProject is a documentation generator add-in 129 | DocProject/buildhelp/ 130 | DocProject/Help/*.HxT 131 | DocProject/Help/*.HxC 132 | DocProject/Help/*.hhc 133 | DocProject/Help/*.hhk 134 | DocProject/Help/*.hhp 135 | DocProject/Help/Html2 136 | DocProject/Help/html 137 | 138 | # Click-Once directory 139 | publish/ 140 | 141 | # Publish Web Output 142 | *.[Pp]ublish.xml 143 | *.azurePubxml 144 | # TODO: Comment the next line if you want to checkin your web deploy settings 145 | # but database connection strings (with potential passwords) will be unencrypted 146 | #*.pubxml 147 | *.publishproj 148 | 149 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 150 | # checkin your Azure Web App publish settings, but sensitive information contained 151 | # in these scripts will be unencrypted 152 | PublishScripts/ 153 | 154 | # NuGet Packages 155 | *.nupkg 156 | # The packages folder can be ignored because of Package Restore 157 | **/packages/* 158 | # except build/, which is used as an MSBuild target. 159 | !**/packages/build/ 160 | # Uncomment if necessary however generally it will be regenerated when needed 161 | #!**/packages/repositories.config 162 | # NuGet v3's project.json files produces more ignoreable files 163 | *.nuget.props 164 | *.nuget.targets 165 | 166 | # Microsoft Azure Build Output 167 | csx/ 168 | *.build.csdef 169 | 170 | # Microsoft Azure Emulator 171 | ecf/ 172 | rcf/ 173 | 174 | # Windows Store app package directories and files 175 | AppPackages/ 176 | BundleArtifacts/ 177 | Package.StoreAssociation.xml 178 | _pkginfo.txt 179 | 180 | # Visual Studio cache files 181 | # files ending in .cache can be ignored 182 | *.[Cc]ache 183 | # but keep track of directories ending in .cache 184 | !*.[Cc]ache/ 185 | 186 | # Others 187 | ClientBin/ 188 | ~$* 189 | *~ 190 | *.dbmdl 191 | *.dbproj.schemaview 192 | *.jfm 193 | *.pfx 194 | *.publishsettings 195 | node_modules/ 196 | orleans.codegen.cs 197 | 198 | # Since there are multiple workflows, uncomment next line to ignore bower_components 199 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 200 | #bower_components/ 201 | 202 | # RIA/Silverlight projects 203 | Generated_Code/ 204 | 205 | # Backup & report files from converting an old project file 206 | # to a newer Visual Studio version. Backup files are not needed, 207 | # because we have git ;-) 208 | _UpgradeReport_Files/ 209 | Backup*/ 210 | UpgradeLog*.XML 211 | UpgradeLog*.htm 212 | 213 | # SQL Server files 214 | *.mdf 215 | *.ldf 216 | 217 | # Business Intelligence projects 218 | *.rdl.data 219 | *.bim.layout 220 | *.bim_*.settings 221 | 222 | # Microsoft Fakes 223 | FakesAssemblies/ 224 | 225 | # GhostDoc plugin setting file 226 | *.GhostDoc.xml 227 | 228 | # Node.js Tools for Visual Studio 229 | .ntvs_analysis.dat 230 | 231 | # Visual Studio 6 build log 232 | *.plg 233 | 234 | # Visual Studio 6 workspace options file 235 | *.opt 236 | 237 | # Visual Studio LightSwitch build output 238 | **/*.HTMLClient/GeneratedArtifacts 239 | **/*.DesktopClient/GeneratedArtifacts 240 | **/*.DesktopClient/ModelManifest.xml 241 | **/*.Server/GeneratedArtifacts 242 | **/*.Server/ModelManifest.xml 243 | _Pvt_Extensions 244 | 245 | # Paket dependency manager 246 | .paket/paket.exe 247 | paket-files/ 248 | 249 | # FAKE - F# Make 250 | .fake/ 251 | 252 | # JetBrains Rider 253 | .idea/ 254 | *.sln.iml 255 | 256 | # CodeRush 257 | .cr/ 258 | 259 | # Python Tools for Visual Studio (PTVS) 260 | __pycache__/ 261 | *.pyc 262 | /Application/Python37/Lib/encodings 263 | /Application/Python37/Lib/importlib 264 | /Application/Python37/Lib/collections 265 | /Application/Python37/Lib 266 | 267 | # Custom 268 | *.exe 269 | 270 | # CLion files 271 | /.idea 272 | -------------------------------------------------------------------------------- /minifilter/RWatch.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.3.32922.545 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "snFilter", "snFilter\snFilter.vcxproj", "{DF8682E7-17C1-4450-A68C-D7CEF8780D8F}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{D289F655-3416-4C39-97D1-9EC964BA43FF}" 9 | ProjectSection(SolutionItems) = preProject 10 | README.md = README.md 11 | EndProjectSection 12 | EndProject 13 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "sharedDefs", "sharedDefs", "{FBDB615D-884C-4735-9C31-372EE539AE85}" 14 | ProjectSection(SolutionItems) = preProject 15 | SharedDefs\SharedDefs.h = SharedDefs\SharedDefs.h 16 | EndProjectSection 17 | EndProject 18 | Global 19 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 20 | Debug|x64 = Debug|x64 21 | Release|x64 = Release|x64 22 | EndGlobalSection 23 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 24 | {DF8682E7-17C1-4450-A68C-D7CEF8780D8F}.Debug|x64.ActiveCfg = Debug|x64 25 | {DF8682E7-17C1-4450-A68C-D7CEF8780D8F}.Debug|x64.Build.0 = Debug|x64 26 | {DF8682E7-17C1-4450-A68C-D7CEF8780D8F}.Debug|x64.Deploy.0 = Debug|x64 27 | {DF8682E7-17C1-4450-A68C-D7CEF8780D8F}.Release|x64.ActiveCfg = Release|x64 28 | {DF8682E7-17C1-4450-A68C-D7CEF8780D8F}.Release|x64.Build.0 = Release|x64 29 | {DF8682E7-17C1-4450-A68C-D7CEF8780D8F}.Release|x64.Deploy.0 = Release|x64 30 | EndGlobalSection 31 | GlobalSection(SolutionProperties) = preSolution 32 | HideSolutionNode = FALSE 33 | EndGlobalSection 34 | GlobalSection(ExtensibilityGlobals) = postSolution 35 | SolutionGuid = {BA4B40B9-6827-4A96-8EE1-2BB52F53967A} 36 | EndGlobalSection 37 | EndGlobal 38 | -------------------------------------------------------------------------------- /minifilter/SharedDefs/Common.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // https://learn.microsoft.com/en-us/cpp/build/reference/kernel-create-kernel-mode-binary?view=msvc-170 4 | #ifdef _KERNEL_MODE 5 | 6 | #else 7 | 8 | #endif -------------------------------------------------------------------------------- /minifilter/SharedDefs/SharedDefs.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /*++ 4 | 5 | Abstract : 6 | 7 | Header file which contains the structures, type definitions, 8 | constants, global variables and function prototypes that are 9 | shared between kernel and user mode. 10 | 11 | Environment : 12 | 13 | Kernel & user mode 14 | 15 | --*/ 16 | 17 | // 18 | // Name of port used to communicate 19 | // 20 | 21 | const PCWSTR ComPortName = L"\\snFilter"; 22 | 23 | #define MAX_FILE_NAME_LENGTH 1024 24 | #define MAX_FILE_NAME_SIZE (MAX_FILE_NAME_LENGTH * sizeof(WCHAR)) // max length in bytes of files sizes and dir paths 25 | #define FILE_OBJECT_ID_SIZE 128 26 | #define FILE_OBJEC_MAX_EXTENSION_SIZE 20 27 | 28 | #define MAX_COMM_BUFFER_SIZE 0x10000 // the size of the buffer we allocate to receive irp ops from the driver 29 | #define MAX_OPS_SAVE \ 30 | 0x10000 // max ops to save, we limit this to prevent the driver from filling the non-paged memory and crashing the os 31 | 32 | // msgs types that the application may send to the driver 33 | enum COM_MESSAGE_TYPE 34 | { 35 | MESSAGE_ADD_SCAN_DIRECTORY, 36 | MESSAGE_REM_SCAN_DIRECTORY, 37 | MESSAGE_GET_OPS, 38 | MESSAGE_SET_PID, 39 | MESSAGE_KILL_GID 40 | }; 41 | 42 | // msgs struct that the application sends when sending msg to the driver, type member should be one of the 43 | // COM_MESSAGE_TYPE 44 | typedef struct _COM_MESSAGE 45 | { 46 | ULONG type; 47 | ULONG pid; 48 | ULONGLONG gid; 49 | WCHAR path[MAX_FILE_NAME_LENGTH]; 50 | 51 | } COM_MESSAGE, *PCOM_MESSAGE; 52 | 53 | enum FILE_CHANGE_INFO 54 | { 55 | FILE_CHANGE_NOT_SET, 56 | FILE_OPEN_DIRECTORY, 57 | FILE_CHANGE_WRITE, 58 | FILE_CHANGE_NEW_FILE, 59 | FILE_CHANGE_RENAME_FILE, 60 | FILE_CHANGE_EXTENSION_CHANGED, 61 | FILE_CHANGE_DELETE_FILE, 62 | FILE_CHANGE_DELETE_NEW_FILE, 63 | FILE_CHANGE_OVERWRITE_FILE 64 | }; 65 | 66 | enum FILE_LOCATION_INFO 67 | { 68 | FILE_NOT_PROTECTED, // nothing to set, not protected 69 | FILE_PROTECTED, // if not read remember change in file 70 | FILE_MOVED_IN, // new file to remove from protected 71 | FILE_MOVED_OUT // keep filename if not already exist 72 | }; 73 | 74 | enum IRP_MAJOR_OP 75 | { 76 | IRP_NONE, 77 | IRP_READ, 78 | IRP_WRITE, 79 | IRP_SETINFO, 80 | IRP_CREATE, 81 | IRP_CLEANUP, 82 | }; 83 | 84 | // -64- bytes structure, fixed to -96- bytes, fixed to 104 bytes 85 | typedef struct _DRIVER_MESSAGE 86 | { 87 | WCHAR Extension[FILE_OBJEC_MAX_EXTENSION_SIZE + 1]; // null terminated 24 bytes 88 | 89 | #ifdef _KERNEL_MODE 90 | FILE_ID_INFORMATION 91 | FileID; // 24 bytes - file id 128 bits and its volume serial number 92 | #else 93 | FILE_ID_INFO 94 | FileID; // 24 bytes - file id 128 bits and its volume serial number 95 | #endif 96 | 97 | ULONGLONG 98 | MemSizeUsed; // for read and write, we follow buffer sizes 8 bytes 99 | DOUBLE Entropy; // 8 bytes 100 | ULONG PID; // 4 bytes 101 | UCHAR IRP_OP; // 1 byte 102 | BOOLEAN isEntropyCalc; // 1 byte 103 | UCHAR FileChange; // 1 byte 104 | UCHAR FileLocationInfo; // 1 byte align 105 | UNICODE_STRING 106 | filePath; // 16 bytes unicode string - filename, also contains size and max size, buffer is outside the struct 107 | ULONGLONG Gid; // 8 bytes process gid 108 | PVOID 109 | next; // 8 bytes - next PDRIVER_MESSAGE, we use it to allow adding the fileName to the same buffer, This pointer 110 | // should point to the next PDRIVER_MESSAGE in buffer (kernel handled) 111 | 112 | } DRIVER_MESSAGE, *PDRIVER_MESSAGE; 113 | 114 | // header for return buffer from the driver on irp ops, 115 | // has a pointer to the first driver message, num ops in the buffer and 116 | // readable data size in the buffer 117 | typedef struct _RWD_REPLY_IRPS 118 | { 119 | size_t dataSize; // 8 bytes 120 | PDRIVER_MESSAGE 121 | data; // 8 bytes points to the first IRP driver message, The next DRIVER_MESSAGE is a pointer inside DRIVER_MESSAGE 122 | ULONGLONG num_ops; // 8 bytes 123 | 124 | size_t size() 125 | { 126 | return dataSize + sizeof(_RWD_REPLY_IRPS); 127 | } 128 | 129 | size_t addSize(size_t size) 130 | { 131 | dataSize += size; 132 | return dataSize; 133 | } 134 | 135 | ULONGLONG addOp() 136 | { 137 | num_ops++; 138 | return num_ops; 139 | } 140 | 141 | ULONGLONG numOps() 142 | { 143 | return num_ops; 144 | } 145 | 146 | _RWD_REPLY_IRPS() : dataSize(sizeof(_RWD_REPLY_IRPS)), data(nullptr), num_ops(0) 147 | { 148 | } 149 | } RWD_REPLY_IRPS, *PRWD_REPLY_IRPS; -------------------------------------------------------------------------------- /minifilter/SharedDefs/Unique_ptr.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include -------------------------------------------------------------------------------- /minifilter/snFilter/Communication.cpp: -------------------------------------------------------------------------------- 1 | #include "Communication.h" 2 | 3 | #define POOL_FLAG_NON_PAGED 0x0000000000000040UI64 // Non paged pool NX 4 | 5 | NTSTATUS InitCommData() { 6 | HRESULT status; 7 | OBJECT_ATTRIBUTES oa; 8 | UNICODE_STRING uniString; 9 | PSECURITY_DESCRIPTOR sd; 10 | // 11 | // Create a communication port. 12 | // 13 | RtlInitUnicodeString(&uniString, ComPortName); 14 | 15 | status = FltBuildDefaultSecurityDescriptor( 16 | &sd, 17 | FLT_PORT_ALL_ACCESS); // We secure the port so only ADMIN(s) & SYSTEM can 18 | // access it. 19 | status = RtlSetDaclSecurityDescriptor( 20 | sd, TRUE, NULL, 21 | FALSE); // allow user application without admin to enter 22 | 23 | if (NT_SUCCESS(status)) { 24 | InitializeObjectAttributes( 25 | &oa, &uniString, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, sd); 26 | 27 | status = FltCreateCommunicationPort( 28 | commHandle->Filter, &commHandle->ServerPort, &oa, NULL, RWFConnect, 29 | RWFDissconnect, RWFNewMessage, 1); 30 | // 31 | // Free the security descriptor in all cases. It is not needed once 32 | // the call to FltCreateCommunicationPort() is made. 33 | // 34 | 35 | FltFreeSecurityDescriptor(sd); 36 | } 37 | 38 | return status; 39 | } 40 | 41 | BOOLEAN IsCommClosed() { return commHandle->CommClosed; } 42 | 43 | void CommClose() { 44 | // FLT_ASSERT(IsCommClosed()); 45 | 46 | if (commHandle->ClientPort) { 47 | FltCloseClientPort(commHandle->Filter, &commHandle->ClientPort); 48 | commHandle->ClientPort = NULL; 49 | } 50 | 51 | if (commHandle->ServerPort) { 52 | FltCloseCommunicationPort(commHandle->ServerPort); 53 | commHandle->ServerPort = NULL; 54 | } 55 | commHandle->UserProcess = NULL; 56 | commHandle->CommClosed = TRUE; 57 | } 58 | 59 | NTSTATUS 60 | RWFConnect(_In_ PFLT_PORT ClientPort, _In_opt_ PVOID ServerPortCookie, 61 | _In_reads_bytes_opt_(SizeOfContext) PVOID ConnectionContext, 62 | _In_ ULONG SizeOfContext, 63 | _Outptr_result_maybenull_ PVOID *ConnectionCookie) { 64 | UNREFERENCED_PARAMETER(ServerPortCookie); 65 | UNREFERENCED_PARAMETER(ConnectionContext); 66 | UNREFERENCED_PARAMETER(SizeOfContext); 67 | UNREFERENCED_PARAMETER(ConnectionCookie = NULL); 68 | 69 | FLT_ASSERT(commHandle->ClientPort == NULL); 70 | 71 | // 72 | // Set the user process and port. In a production filter it may 73 | // be necessary to synchronize access to such fields with port 74 | // lifetime. For instance, while filter manager will synchronize 75 | // FltCloseClientPort with FltSendMessage's reading of the port 76 | // handle, synchronizing access to the UserProcess would be up to 77 | // the filter. 78 | // 79 | 80 | commHandle->ClientPort = ClientPort; 81 | DbgPrint("!!! user connected, port=0x%p\n", ClientPort); 82 | 83 | return STATUS_SUCCESS; 84 | } 85 | 86 | VOID RWFDissconnect(_In_opt_ PVOID ConnectionCookie) { 87 | UNREFERENCED_PARAMETER(ConnectionCookie); 88 | 89 | DbgPrint("!!! user disconnected, port=0x%p\n", commHandle->ClientPort); 90 | 91 | // 92 | // Close our handle to the connection: note, since we limited max connections 93 | // to 1, another connecting will not be allowed until we return from the 94 | // disconnect routine. 95 | // 96 | 97 | FltCloseClientPort(commHandle->Filter, &commHandle->ClientPort); 98 | 99 | // 100 | // Reset the user-process field. 101 | // 102 | DbgPrint("Disconnect\n"); 103 | commHandle->CommClosed = TRUE; 104 | } 105 | 106 | NTSTATUS 107 | RWFNewMessage(IN PVOID PortCookie, IN PVOID InputBuffer, 108 | IN ULONG InputBufferLength, OUT PVOID OutputBuffer, 109 | IN ULONG OutputBufferLength, 110 | OUT PULONG ReturnOutputBufferLength) { 111 | UNREFERENCED_PARAMETER(PortCookie); 112 | UNREFERENCED_PARAMETER(InputBufferLength); 113 | 114 | *ReturnOutputBufferLength = 0; 115 | 116 | COM_MESSAGE *message = static_cast(InputBuffer); 117 | if (message == NULL) 118 | return STATUS_INTERNAL_ERROR; // failed message type 119 | 120 | if (message->type == MESSAGE_ADD_SCAN_DIRECTORY) { 121 | DbgPrint("Received add directory message\n"); 122 | PDIRECTORY_ENTRY newEntry = new DIRECTORY_ENTRY(); 123 | if (newEntry == NULL) { 124 | return STATUS_INSUFFICIENT_RESOURCES; 125 | } 126 | NTSTATUS hr = 127 | CopyWString(newEntry->path, message->path, MAX_FILE_NAME_LENGTH); 128 | if (!NT_SUCCESS(hr)) { 129 | delete newEntry; 130 | return STATUS_INTERNAL_ERROR; 131 | } 132 | *ReturnOutputBufferLength = 1; 133 | if (driverData->AddDirectoryEntry(newEntry)) { 134 | *((PBOOLEAN)OutputBuffer) = TRUE; 135 | DbgPrint("Added scan directory successfully\n"); 136 | return STATUS_SUCCESS; 137 | } else { 138 | delete newEntry; 139 | *((PBOOLEAN)OutputBuffer) = FALSE; 140 | DbgPrint("Failed to add scan directory\n"); 141 | return STATUS_SUCCESS; 142 | } 143 | } else if (message->type == MESSAGE_REM_SCAN_DIRECTORY) { 144 | PDIRECTORY_ENTRY ptr = driverData->RemDirectoryEntry(message->path); 145 | *ReturnOutputBufferLength = 1; 146 | if (ptr == NULL) { 147 | *((PBOOLEAN)OutputBuffer) = FALSE; 148 | DbgPrint("Failed to remove directory\n"); 149 | return STATUS_SUCCESS; 150 | } else { 151 | delete ptr; 152 | } 153 | *((PBOOLEAN)OutputBuffer) = TRUE; 154 | DbgPrint("Removed scan directory successfully\n"); 155 | return STATUS_SUCCESS; 156 | } else if (message->type == MESSAGE_GET_OPS) { 157 | if (OutputBuffer == NULL || OutputBufferLength != MAX_COMM_BUFFER_SIZE) { 158 | return STATUS_INVALID_PARAMETER; 159 | } 160 | driverData->DriverGetIrps(OutputBuffer, OutputBufferLength, 161 | ReturnOutputBufferLength); 162 | return STATUS_SUCCESS; 163 | } else if (message->type == MESSAGE_SET_PID) { 164 | if (message->pid != 0) { 165 | driverData->setPID(message->pid); 166 | driverData->setSystemRootPath(message->path); 167 | commHandle->CommClosed = FALSE; 168 | 169 | return STATUS_SUCCESS; 170 | } 171 | return STATUS_INVALID_PARAMETER; 172 | } 173 | // TODO: the kill code to gid 174 | else if (message->type == MESSAGE_KILL_GID) { 175 | if (OutputBuffer == NULL || OutputBufferLength != sizeof(LONG)) { 176 | return STATUS_INVALID_PARAMETER; 177 | } 178 | *ReturnOutputBufferLength = sizeof(LONG); 179 | NTSTATUS status = STATUS_SUCCESS; 180 | HANDLE processHandle; 181 | ULONGLONG GID = message->gid; 182 | BOOLEAN isGidExist = FALSE; 183 | ULONGLONG gidSize = driverData->GetGidSize(GID, &isGidExist); 184 | if (gidSize == 0 || isGidExist == FALSE) { 185 | DbgPrint("!!! FS : Gid already ended or no such gid %d\n", (int)GID); 186 | *((PLONG)OutputBuffer) = STATUS_NO_SUCH_GROUP; // fail to kill the process 187 | return STATUS_SUCCESS; 188 | } 189 | // there is gid with processes 190 | PULONG 191 | Buffer = (PULONG)ExAllocatePool2(POOL_FLAG_NON_PAGED, 192 | sizeof(ULONG) * gidSize, 'RW'); 193 | if (Buffer == nullptr) { 194 | DbgPrint("!!! FS : memory allocation error on non paged pool\n"); 195 | *((PLONG)OutputBuffer) = 196 | STATUS_MEMORY_NOT_ALLOCATED; // fail to kill the process 197 | return STATUS_SUCCESS; 198 | } 199 | ULONGLONG pidsReturned = 0; 200 | isGidExist = driverData->GetGidPids(GID, Buffer, gidSize, &pidsReturned); 201 | if (isGidExist) { // got all irps and correct size 202 | for (int i = 0; i < gidSize; i++) { // kill each process 203 | CLIENT_ID clientId; 204 | clientId.UniqueProcess = (HANDLE)Buffer[i]; 205 | clientId.UniqueThread = 0; 206 | 207 | OBJECT_ATTRIBUTES objAttribs; 208 | NTSTATUS exitStatus = STATUS_FAIL_CHECK; 209 | 210 | DbgPrint("!!! FS : Attempt to terminate pid: %d from gid: %d\n", 211 | Buffer[i], (int)GID); 212 | 213 | InitializeObjectAttributes(&objAttribs, NULL, OBJ_KERNEL_HANDLE, NULL, 214 | NULL); 215 | 216 | status = ZwOpenProcess(&processHandle, PROCESS_ALL_ACCESS, &objAttribs, 217 | &clientId); 218 | 219 | if (!NT_SUCCESS(status)) { 220 | *((PLONG)OutputBuffer) = STATUS_FAIL_CHECK; // fail 221 | DbgPrint("!!! FS : Failed to open process %d, reason: %d\n", 222 | Buffer[i], status); 223 | continue; // try to kill others 224 | } 225 | status = ZwTerminateProcess(processHandle, exitStatus); 226 | if (!NT_SUCCESS(status)) { 227 | *((PLONG)OutputBuffer) = STATUS_FAIL_CHECK; // fail 228 | DbgPrint("!!! FS : Failed to kill process %d, reason: %d\n", 229 | Buffer[i], status); 230 | status = NtClose(processHandle); 231 | continue; // try to kill others 232 | } 233 | NtClose(processHandle); 234 | 235 | DbgPrint("!!! FS : Termination of pid: %d from gid: %d succeeded\n", 236 | Buffer[i], (int)GID); 237 | } 238 | } 239 | ExFreePoolWithTag(Buffer, 'RW'); 240 | return STATUS_SUCCESS; 241 | } 242 | 243 | return STATUS_INTERNAL_ERROR; 244 | } 245 | 246 | CommHandler *commHandle; -------------------------------------------------------------------------------- /minifilter/snFilter/Communication.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "../SharedDefs/SharedDefs.h" 7 | #include "DriverData.h" 8 | 9 | struct CommHandler { 10 | // Server-side communicate ports. 11 | PFLT_PORT ServerPort; 12 | 13 | // port for a connection to user-mode 14 | PFLT_PORT ClientPort; 15 | 16 | // The filter handle that results from a call to 17 | PFLT_FILTER Filter; 18 | 19 | // A flag that indicating that the filter is connected 20 | BOOLEAN CommClosed; 21 | 22 | // User process that connected to the port 23 | 24 | ULONG UserProcess; 25 | 26 | CommHandler(PFLT_FILTER Filter) 27 | : ServerPort(NULL), ClientPort(NULL), Filter(Filter), CommClosed(TRUE), 28 | UserProcess(0) {} 29 | }; 30 | 31 | extern CommHandler *commHandle; 32 | 33 | NTSTATUS InitCommData(); 34 | 35 | // close the comm handler, close both ports 36 | void CommClose(); 37 | 38 | BOOLEAN IsCommClosed(); 39 | 40 | // AMFConnect: Handles user mode application which connects to the driver 41 | 42 | NTSTATUS 43 | RWFConnect(_In_ PFLT_PORT ClientPort, _In_opt_ PVOID ServerPortCookie, 44 | _In_reads_bytes_opt_(SizeOfContext) PVOID ConnectionContext, 45 | _In_ ULONG SizeOfContext, 46 | _Outptr_result_maybenull_ PVOID 47 | 48 | *ConnectionCookie); 49 | 50 | // AMFConnect: handle messages received from user mode 51 | 52 | NTSTATUS RWFNewMessage(IN PVOID PortCookie, IN PVOID InputBuffer, 53 | IN ULONG InputBufferLength, OUT PVOID OutputBuffer, 54 | IN ULONG OutputBufferLength, 55 | OUT PULONG ReturnOutputBufferLength); 56 | 57 | // AMFDisconnect: Handles user mode application which disconnects from the 58 | // driver 59 | 60 | VOID RWFDissconnect(_In_opt_ PVOID ConnectionCookie); -------------------------------------------------------------------------------- /minifilter/snFilter/DriverData.cpp: -------------------------------------------------------------------------------- 1 | #include "DriverData.h" 2 | 3 | DriverData::DriverData(PDRIVER_OBJECT DriverObject) 4 | : FilterRun(FALSE), Filter(nullptr), DriverObject(DriverObject), pid(0), 5 | irpOpsSize(0), directoryRootsSize(0), GidToPids(), PidToGids() { 6 | systemRootPath[0] = L'\0'; 7 | InitializeListHead(&irpOps); 8 | InitializeListHead(&rootDirectories); 9 | KeInitializeSpinLock(&irpOpsLock); // init spin lock 10 | KeInitializeSpinLock(&directoriesSpinLock); // init spin lock 11 | 12 | GidCounter = 0; 13 | KeInitializeSpinLock(&GIDSystemLock); // init spin lock 14 | gidsSize = 0; 15 | InitializeListHead(&GidsList); 16 | } 17 | 18 | DriverData::~DriverData() { Clear(); } 19 | 20 | DriverData *driverData; 21 | 22 | // ####################################################################################### 23 | // # Gid system handling 24 | // ####################################################################################### 25 | 26 | /****************** Private ******************/ 27 | 28 | // call assumes protected code high irql 29 | BOOLEAN DriverData::RemoveProcessRecordAux(ULONG ProcessId, ULONGLONG gid) { 30 | BOOLEAN ret = FALSE; 31 | PGID_ENTRY gidRecord = (PGID_ENTRY)GidToPids.get(gid); 32 | if (gidRecord == nullptr) { // shouldn't happen 33 | return FALSE; 34 | } 35 | PLIST_ENTRY header = &(gidRecord->HeadListPids); 36 | PLIST_ENTRY iterator = header->Flink; 37 | while (iterator != header) { 38 | PPID_ENTRY pStrct = 39 | (PPID_ENTRY)CONTAINING_RECORD(iterator, PID_ENTRY, entry); 40 | if (pStrct->Pid == ProcessId) { 41 | RemoveEntryList(iterator); 42 | delete pStrct->Path; 43 | delete pStrct; 44 | gidRecord->pidsSize--; 45 | ret = TRUE; 46 | break; 47 | } 48 | iterator = iterator->Flink; 49 | } 50 | if (ret) { 51 | if (IsListEmpty(header)) { 52 | GidToPids.deleteNode(gid); // remove the gidRecord from GidToPids 53 | RemoveEntryList( 54 | &(gidRecord->GidListEntry)); // unlink from the list of gids 55 | gidsSize--; 56 | delete gidRecord; 57 | } 58 | PidToGids.deleteNode(ProcessId); 59 | } 60 | return ret; 61 | } 62 | 63 | // call assumes protected code high irql 64 | BOOLEAN DriverData::RemoveGidRecordAux(PGID_ENTRY gidRecord) { 65 | BOOLEAN ret = FALSE; 66 | ASSERT(gidRecord != nullptr); 67 | PLIST_ENTRY headerPids = &(gidRecord->HeadListPids); 68 | PULONGLONG pidsSize = &(gidRecord->pidsSize); 69 | PLIST_ENTRY iterator = headerPids->Flink; 70 | while (iterator != headerPids) { // clear list 71 | PPID_ENTRY pStrct = 72 | (PPID_ENTRY)CONTAINING_RECORD(iterator, PID_ENTRY, entry); 73 | PLIST_ENTRY next = iterator->Flink; 74 | RemoveEntryList(iterator); 75 | PidToGids.deleteNode(pStrct->Pid); 76 | pidsSize--; 77 | delete pStrct->Path; // release PUNICODE_STRING 78 | delete pStrct; // release PID_ENTRY 79 | ret = TRUE; 80 | iterator = next; 81 | } 82 | ASSERT(IsListEmpty(headerPids)); 83 | return ret; 84 | } 85 | 86 | /****************** Public ******************/ 87 | 88 | BOOLEAN DriverData::RemoveProcess(ULONG ProcessId) { 89 | BOOLEAN ret = FALSE; 90 | KIRQL irql = KeGetCurrentIrql(); 91 | KeAcquireSpinLock(&GIDSystemLock, &irql); 92 | ULONGLONG gid = (ULONGLONG)PidToGids.get(ProcessId); 93 | if (gid) { // there is Gid 94 | ret = RemoveProcessRecordAux(ProcessId, gid); 95 | } 96 | 97 | KeReleaseSpinLock(&GIDSystemLock, irql); 98 | return ret; 99 | } 100 | 101 | _IRQL_raises_(DISPATCH_LEVEL) BOOLEAN DriverData::RecordNewProcess( 102 | PUNICODE_STRING ProcessName, ULONG ProcessId, ULONG ParentPid) { 103 | BOOLEAN ret = FALSE; 104 | KIRQL irql = KeGetCurrentIrql(); 105 | KeAcquireSpinLock(&GIDSystemLock, &irql); 106 | ULONGLONG gid = (ULONGLONG)PidToGids.get(ParentPid); 107 | PPID_ENTRY pStrct = new PID_ENTRY; 108 | pStrct->Pid = ProcessId; 109 | pStrct->Path = ProcessName; 110 | if (gid) { // there is Gid 111 | ULONGLONG retInsert; 112 | if ((retInsert = (ULONGLONG)PidToGids.insertNode(ProcessId, (HANDLE)gid)) != 113 | gid) { // shouldn't happen 114 | RemoveProcessRecordAux(ProcessId, retInsert); 115 | } 116 | PGID_ENTRY gidRecord = (PGID_ENTRY)GidToPids.get(gid); 117 | InsertHeadList(&(gidRecord->HeadListPids), &(pStrct->entry)); 118 | gidRecord->pidsSize++; 119 | PidToGids.insertNode(ProcessId, (HANDLE)gid); 120 | } else { 121 | PGID_ENTRY newGidRecord = new GID_ENTRY(++GidCounter); 122 | InsertHeadList(&(newGidRecord->HeadListPids), &(pStrct->entry)); 123 | InsertTailList(&GidsList, &(newGidRecord->GidListEntry)); 124 | GidToPids.insertNode(GidCounter, newGidRecord); 125 | PidToGids.insertNode(ProcessId, (HANDLE)GidCounter); 126 | newGidRecord->pidsSize++; 127 | gidsSize++; 128 | } 129 | KeReleaseSpinLock(&GIDSystemLock, irql); 130 | return ret; 131 | } 132 | 133 | BOOLEAN DriverData::RemoveGid(ULONGLONG gid) { 134 | BOOLEAN ret = FALSE; 135 | KIRQL irql = KeGetCurrentIrql(); 136 | KeAcquireSpinLock(&GIDSystemLock, &irql); 137 | PGID_ENTRY gidRecord = (PGID_ENTRY)GidToPids.get(gid); 138 | if (gidRecord) { // there is a Gid list 139 | RemoveGidRecordAux(gidRecord); // clear process list 140 | GidToPids.deleteNode(gid); // remove the gidRecord from GidToPids 141 | RemoveEntryList(&(gidRecord->GidListEntry)); // unlink from the list of gids 142 | gidsSize--; 143 | delete gidRecord; 144 | ret = TRUE; 145 | } 146 | 147 | KeReleaseSpinLock(&GIDSystemLock, irql); 148 | return ret; 149 | } 150 | 151 | ULONGLONG DriverData::GetGidSize(ULONGLONG gid, PBOOLEAN found) { 152 | ASSERT(found != nullptr); 153 | *found = FALSE; 154 | ULONGLONG ret = 0; 155 | KIRQL irql = KeGetCurrentIrql(); 156 | KeAcquireSpinLock(&GIDSystemLock, &irql); 157 | PGID_ENTRY GidRecord = (PGID_ENTRY)GidToPids.get(gid); 158 | if (GidRecord != nullptr) { // there is such a Gid 159 | *found = TRUE; 160 | ret = GidRecord->pidsSize; 161 | } 162 | KeReleaseSpinLock(&GIDSystemLock, irql); 163 | return ret; 164 | } 165 | 166 | BOOLEAN DriverData::GetGidPids(ULONGLONG gid, PULONG buffer, 167 | ULONGLONG bufferSize, 168 | PULONGLONG returnedLength) { 169 | ASSERT(buffer != nullptr); 170 | ASSERT(returnedLength != nullptr); 171 | *returnedLength = 0; 172 | if (bufferSize == 0) 173 | return FALSE; 174 | ULONGLONG pidsSize = 0; 175 | ULONGLONG pidsIter = 0; 176 | KIRQL irql = KeGetCurrentIrql(); 177 | KeAcquireSpinLock(&GIDSystemLock, &irql); 178 | PGID_ENTRY GidRecord = (PGID_ENTRY)GidToPids.get(gid); 179 | if (GidRecord != nullptr) { // there is such a Gid 180 | pidsSize = GidRecord->pidsSize; 181 | PLIST_ENTRY PidsListHeader = &(GidRecord->HeadListPids); 182 | PLIST_ENTRY iterator = PidsListHeader->Flink; 183 | while (iterator != PidsListHeader && pidsIter < bufferSize) { 184 | PPID_ENTRY pStrct = 185 | (PPID_ENTRY)CONTAINING_RECORD(iterator, PID_ENTRY, entry); 186 | ASSERT(pStrct != nullptr); 187 | if (pStrct != nullptr) { 188 | buffer[pidsIter++] = pStrct->Pid; 189 | *returnedLength += 1; 190 | } 191 | iterator = iterator->Flink; 192 | } 193 | } 194 | KeReleaseSpinLock(&GIDSystemLock, irql); 195 | if (GidRecord == nullptr) { 196 | return FALSE; 197 | } 198 | if (pidsSize == pidsIter) { 199 | return TRUE; 200 | } 201 | return FALSE; 202 | } 203 | 204 | // if found return true on found else return false 205 | ULONGLONG DriverData::GetProcessGid(ULONG ProcessId, PBOOLEAN found) { 206 | ASSERT(found != nullptr); 207 | *found = FALSE; 208 | ULONGLONG ret = 0; 209 | KIRQL irql = KeGetCurrentIrql(); 210 | KeAcquireSpinLock(&GIDSystemLock, &irql); 211 | ret = (ULONGLONG)PidToGids.get(ProcessId); 212 | if (ret) 213 | *found = TRUE; 214 | KeReleaseSpinLock(&GIDSystemLock, irql); 215 | // DbgPrint("Gid: %d %d\n", ret, *found); 216 | return ret; 217 | } 218 | 219 | // clear all data related to the Gid system 220 | VOID DriverData::ClearGidsPids() { 221 | KIRQL irql = KeGetCurrentIrql(); 222 | KeAcquireSpinLock(&GIDSystemLock, &irql); 223 | PLIST_ENTRY headGids = &GidsList; 224 | PLIST_ENTRY iterator = headGids->Flink; 225 | while (iterator != headGids) { // clear list 226 | PGID_ENTRY pStrct = 227 | (PGID_ENTRY)CONTAINING_RECORD(iterator, GID_ENTRY, GidListEntry); 228 | PLIST_ENTRY next = iterator->Flink; 229 | RemoveGidRecordAux( 230 | pStrct); // clear process list and processes from PidToGids 231 | GidToPids.deleteNode(pStrct->gid); // remove gid from GidToPids 232 | gidsSize--; 233 | delete pStrct; // release GID_ENTRY 234 | iterator = next; 235 | } 236 | // ASSERT(headGids->Flink == headGids); 237 | GidCounter = 0; 238 | KeReleaseSpinLock(&GIDSystemLock, irql); 239 | } 240 | 241 | ULONGLONG DriverData::GidsSize() { 242 | ULONGLONG ret = 0; 243 | KIRQL irql = KeGetCurrentIrql(); 244 | KeAcquireSpinLock(&GIDSystemLock, &irql); 245 | ret = gidsSize; 246 | KeReleaseSpinLock(&GIDSystemLock, irql); 247 | return ret; 248 | } 249 | 250 | // ####################################################################################### 251 | // # Irp handling 252 | // ####################################################################################### 253 | 254 | VOID DriverData::ClearIrps() { 255 | KIRQL irql = KeGetCurrentIrql(); 256 | KeAcquireSpinLock(&irpOpsLock, &irql); 257 | PLIST_ENTRY pEntryIrps = irpOps.Flink; 258 | while (pEntryIrps != &irpOps) { 259 | LIST_ENTRY temp = *pEntryIrps; 260 | PIRP_ENTRY pStrct = 261 | (PIRP_ENTRY)CONTAINING_RECORD(pEntryIrps, IRP_ENTRY, entry); 262 | delete pStrct; 263 | // next 264 | pEntryIrps = temp.Flink; 265 | } 266 | irpOpsSize = 0; 267 | InitializeListHead(&irpOps); 268 | KeReleaseSpinLock(&irpOpsLock, irql); 269 | } 270 | 271 | ULONG DriverData::IrpSize() { 272 | ULONG ret = 0; 273 | KIRQL irql = KeGetCurrentIrql(); 274 | KeAcquireSpinLock(&irpOpsLock, &irql); 275 | ret = irpOpsSize; 276 | KeReleaseSpinLock(&irpOpsLock, irql); 277 | return ret; 278 | } 279 | 280 | BOOLEAN DriverData::AddIrpMessage(PIRP_ENTRY newEntry) { 281 | KIRQL irql = KeGetCurrentIrql(); 282 | KeAcquireSpinLock(&irpOpsLock, &irql); 283 | if (irpOpsSize < MAX_OPS_SAVE) { 284 | irpOpsSize++; 285 | InsertTailList(&irpOps, &newEntry->entry); 286 | } else { 287 | KeReleaseSpinLock(&irpOpsLock, irql); 288 | return FALSE; 289 | } 290 | KeReleaseSpinLock(&irpOpsLock, irql); 291 | return TRUE; 292 | } 293 | 294 | BOOLEAN DriverData::RemIrpMessage(PIRP_ENTRY newEntry) { 295 | KIRQL irql = KeGetCurrentIrql(); 296 | KeAcquireSpinLock(&irpOpsLock, &irql); 297 | RemoveEntryList(&newEntry->entry); 298 | irpOpsSize--; 299 | 300 | KeReleaseSpinLock(&irpOpsLock, irql); 301 | return TRUE; 302 | } 303 | 304 | PIRP_ENTRY DriverData::GetFirstIrpMessage() { 305 | PLIST_ENTRY ret; 306 | KIRQL irql = KeGetCurrentIrql(); 307 | KeAcquireSpinLock(&irpOpsLock, &irql); 308 | ret = RemoveHeadList(&irpOps); 309 | irpOpsSize--; 310 | KeReleaseSpinLock(&irpOpsLock, irql); 311 | if (ret == &irpOps) { 312 | return NULL; 313 | } 314 | return (PIRP_ENTRY)CONTAINING_RECORD(ret, IRP_ENTRY, entry); 315 | } 316 | 317 | VOID DriverData::DriverGetIrps(PVOID Buffer, ULONG BufferSize, 318 | PULONG ReturnOutputBufferLength) { 319 | *ReturnOutputBufferLength = sizeof(RWD_REPLY_IRPS); 320 | 321 | PCHAR OutputBuffer = (PCHAR)Buffer; 322 | ASSERT(OutputBuffer != nullptr); 323 | OutputBuffer += sizeof(RWD_REPLY_IRPS); 324 | 325 | ULONG BufferSizeRemain = BufferSize - sizeof(RWD_REPLY_IRPS); 326 | 327 | RWD_REPLY_IRPS outHeader; 328 | PLIST_ENTRY irpEntryList; 329 | 330 | PIRP_ENTRY PrevEntry = nullptr; 331 | PDRIVER_MESSAGE Prev = nullptr; 332 | USHORT prevBufferSize = 0; 333 | 334 | KIRQL irql = KeGetCurrentIrql(); 335 | KeAcquireSpinLock(&irpOpsLock, &irql); 336 | 337 | while (irpOpsSize) { 338 | irpEntryList = RemoveHeadList(&irpOps); 339 | irpOpsSize--; 340 | PIRP_ENTRY irp = 341 | (PIRP_ENTRY)CONTAINING_RECORD(irpEntryList, IRP_ENTRY, entry); 342 | UNICODE_STRING FilePath = irp->filePath; 343 | PDRIVER_MESSAGE irpMsg = &(irp->data); 344 | USHORT nameBufferSize = FilePath.Length; 345 | irpMsg->next = nullptr; 346 | irpMsg->filePath.Buffer = nullptr; 347 | if (FilePath.Length) { 348 | irpMsg->filePath.Length = nameBufferSize; 349 | irpMsg->filePath.MaximumLength = nameBufferSize; 350 | } else { 351 | irpMsg->filePath.Length = 0; 352 | irpMsg->filePath.MaximumLength = 0; 353 | } 354 | 355 | if (sizeof(DRIVER_MESSAGE) + nameBufferSize >= 356 | BufferSizeRemain) { // return to an irps list, not enough space 357 | InsertHeadList(&irpOps, irpEntryList); 358 | irpOpsSize++; 359 | break; 360 | } else { 361 | if (Prev != nullptr) { 362 | Prev->next = 363 | PDRIVER_MESSAGE(OutputBuffer + sizeof(DRIVER_MESSAGE) + 364 | prevBufferSize); // PrevFilePath might be 0 size 365 | if (prevBufferSize) { 366 | Prev->filePath.Buffer = 367 | PWCH(OutputBuffer + 368 | sizeof(DRIVER_MESSAGE)); // filePath buffer is after irp 369 | } 370 | RtlCopyMemory(OutputBuffer, Prev, 371 | sizeof(DRIVER_MESSAGE)); // copy previous irp 372 | OutputBuffer += sizeof(DRIVER_MESSAGE); 373 | outHeader.addSize(sizeof(DRIVER_MESSAGE)); 374 | *ReturnOutputBufferLength += sizeof(DRIVER_MESSAGE); 375 | if (prevBufferSize) { 376 | RtlCopyMemory(OutputBuffer, PrevEntry->Buffer, 377 | prevBufferSize); // copy previous filePath 378 | OutputBuffer += prevBufferSize; 379 | outHeader.addSize(prevBufferSize); 380 | *ReturnOutputBufferLength += prevBufferSize; 381 | } 382 | delete PrevEntry; 383 | } 384 | } 385 | 386 | PrevEntry = irp; 387 | Prev = irpMsg; 388 | prevBufferSize = nameBufferSize; 389 | if (prevBufferSize > MAX_FILE_NAME_SIZE) 390 | prevBufferSize = MAX_FILE_NAME_SIZE; 391 | BufferSizeRemain -= (sizeof(DRIVER_MESSAGE) + prevBufferSize); 392 | outHeader.addOp(); 393 | } 394 | KeReleaseSpinLock(&irpOpsLock, irql); 395 | if (prevBufferSize > MAX_FILE_NAME_SIZE) 396 | prevBufferSize = MAX_FILE_NAME_SIZE; 397 | if (Prev != nullptr && PrevEntry != nullptr) { 398 | Prev->next = nullptr; 399 | if (prevBufferSize) { 400 | Prev->filePath.Buffer = 401 | PWCH(OutputBuffer + 402 | sizeof(DRIVER_MESSAGE)); // filePath buffer is after irp 403 | } 404 | RtlCopyMemory(OutputBuffer, Prev, 405 | sizeof(DRIVER_MESSAGE)); // copy previous irp 406 | OutputBuffer += sizeof(DRIVER_MESSAGE); 407 | outHeader.addSize(sizeof(DRIVER_MESSAGE)); 408 | *ReturnOutputBufferLength += sizeof(DRIVER_MESSAGE); 409 | if (prevBufferSize) { 410 | RtlCopyMemory(OutputBuffer, PrevEntry->Buffer, 411 | prevBufferSize); // copy previous filePath 412 | OutputBuffer += prevBufferSize; 413 | outHeader.addSize(prevBufferSize); 414 | *ReturnOutputBufferLength += prevBufferSize; 415 | } 416 | delete PrevEntry; 417 | } 418 | 419 | if (outHeader.numOps()) { 420 | outHeader.data = PDRIVER_MESSAGE((PCHAR)Buffer + sizeof(RWD_REPLY_IRPS)); 421 | } 422 | 423 | RtlCopyMemory((PCHAR)Buffer, &(outHeader), sizeof(RWD_REPLY_IRPS)); 424 | } 425 | 426 | LIST_ENTRY DriverData::GetAllEntries() { 427 | LIST_ENTRY newList; 428 | KIRQL irql = KeGetCurrentIrql(); 429 | KeAcquireSpinLock(&irpOpsLock, &irql); 430 | irpOpsSize = 0; 431 | newList = irpOps; 432 | InitializeListHead(&irpOps); 433 | 434 | KeReleaseSpinLock(&irpOpsLock, irql); 435 | return newList; 436 | } 437 | 438 | // ####################################################################################### 439 | // # Directory handling 440 | // ####################################################################################### 441 | 442 | BOOLEAN DriverData::AddDirectoryEntry(PDIRECTORY_ENTRY newEntry) { 443 | BOOLEAN ret = FALSE; 444 | BOOLEAN foundMatch = FALSE; 445 | KIRQL irql = KeGetCurrentIrql(); 446 | KeAcquireSpinLock(&directoriesSpinLock, &irql); 447 | 448 | PLIST_ENTRY pEntry = rootDirectories.Flink; 449 | while (pEntry != &rootDirectories) { 450 | PDIRECTORY_ENTRY pStrct; 451 | // 452 | // Do some processing. 453 | // 454 | pStrct = 455 | (PDIRECTORY_ENTRY)CONTAINING_RECORD(pEntry, DIRECTORY_ENTRY, entry); 456 | 457 | if (!wcsncmp(newEntry->path, pStrct->path, 458 | wcsnlen_s(newEntry->path, MAX_FILE_NAME_LENGTH))) { 459 | foundMatch = TRUE; 460 | break; 461 | } 462 | // 463 | // Move to the next Entry in the list. 464 | // 465 | pEntry = pEntry->Flink; 466 | } 467 | if (foundMatch == FALSE) { 468 | InsertHeadList(&rootDirectories, &newEntry->entry); 469 | directoryRootsSize++; 470 | ret = TRUE; 471 | } 472 | KeReleaseSpinLock(&directoriesSpinLock, irql); 473 | return ret; 474 | } 475 | 476 | PDIRECTORY_ENTRY DriverData::RemDirectoryEntry(LPCWSTR directory) { 477 | PDIRECTORY_ENTRY ret = NULL; 478 | KIRQL irql = KeGetCurrentIrql(); 479 | KeAcquireSpinLock(&directoriesSpinLock, &irql); 480 | 481 | PLIST_ENTRY pEntry = rootDirectories.Flink; 482 | 483 | while (pEntry != &rootDirectories) { 484 | PDIRECTORY_ENTRY pStrct; 485 | // 486 | // Do some processing. 487 | // 488 | pStrct = 489 | (PDIRECTORY_ENTRY)CONTAINING_RECORD(pEntry, DIRECTORY_ENTRY, entry); 490 | 491 | if (!wcsncmp(directory, pStrct->path, 492 | wcsnlen_s(directory, MAX_FILE_NAME_LENGTH))) { 493 | if (RemoveEntryList(pEntry)) { 494 | ret = pStrct; 495 | directoryRootsSize--; 496 | break; 497 | } 498 | } 499 | // 500 | // Move to the next Entry in the list. 501 | // 502 | pEntry = pEntry->Flink; 503 | } 504 | KeReleaseSpinLock(&directoriesSpinLock, irql); 505 | return ret; 506 | } 507 | 508 | /** 509 | IsContainingDirectory returns true if one of the directory entries in our 510 | LIST_ENTRY of PDIRECTORY_ENTRY is in the path passed as param 511 | */ 512 | BOOLEAN DriverData::IsContainingDirectory(CONST PUNICODE_STRING path) { 513 | if (path == NULL || path->Buffer == NULL) 514 | return FALSE; 515 | BOOLEAN ret = FALSE; 516 | KIRQL irql = KeGetCurrentIrql(); 517 | // DbgPrint("Looking for path: %ls in lookup dirs", path); 518 | KeAcquireSpinLock(&directoriesSpinLock, &irql); 519 | if (directoryRootsSize != 0) { 520 | PLIST_ENTRY pEntry = rootDirectories.Flink; 521 | while (pEntry != &rootDirectories) { 522 | PDIRECTORY_ENTRY pStrct = 523 | (PDIRECTORY_ENTRY)CONTAINING_RECORD(pEntry, DIRECTORY_ENTRY, entry); 524 | for (ULONG i = 0; i < path->Length; i++) { 525 | if (pStrct->path[i] == L'\0') { 526 | ret = TRUE; 527 | break; 528 | } else if (pStrct->path[i] == path->Buffer[i]) { 529 | continue; 530 | } else { 531 | break; // for loop 532 | } 533 | } 534 | 535 | // ret = (wcsstr(path, pStrct->path) != NULL); 536 | if (ret) 537 | break; 538 | // Move to the next Entry in the list. 539 | pEntry = pEntry->Flink; 540 | } 541 | } 542 | KeReleaseSpinLock(&directoriesSpinLock, irql); 543 | return ret; 544 | } 545 | 546 | VOID DriverData::ClearDirectories() { 547 | KIRQL irql = KeGetCurrentIrql(); 548 | KeAcquireSpinLock(&directoriesSpinLock, &irql); 549 | PLIST_ENTRY pEntryDirs = rootDirectories.Flink; 550 | while (pEntryDirs != &rootDirectories) { 551 | LIST_ENTRY temp = *pEntryDirs; 552 | PDIRECTORY_ENTRY pStrct = 553 | (PDIRECTORY_ENTRY)CONTAINING_RECORD(pEntryDirs, DIRECTORY_ENTRY, entry); 554 | delete pStrct; 555 | // next 556 | pEntryDirs = temp.Flink; 557 | } 558 | directoryRootsSize = 0; 559 | InitializeListHead(&rootDirectories); 560 | KeReleaseSpinLock(&directoriesSpinLock, irql); 561 | } -------------------------------------------------------------------------------- /minifilter/snFilter/DriverData.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "HashTable.h" 6 | #include "KernelCommon.h" 7 | #include "KernelString.h" 8 | 9 | /* DriverData: shared class across the driver, hold driver D.S. */ 10 | class DriverData { 11 | BOOLEAN FilterRun; // true if filter currently runs 12 | PFLT_FILTER Filter; 13 | PDRIVER_OBJECT DriverObject; // internal 14 | WCHAR systemRootPath[MAX_FILE_NAME_LENGTH]; // system root path, help analyze 15 | // image files loaded 16 | ULONG 17 | pid; // pid of the current connected user mode application, set by 18 | // communication 19 | 20 | ULONG irpOpsSize; // number of irp ops waiting in entry_list 21 | LIST_ENTRY irpOps; // list entry bidirectional list of irp ops 22 | KSPIN_LOCK irpOpsLock; // lock for irp list ops 23 | 24 | ULONG directoryRootsSize; // number of protected dirs in the list 25 | LIST_ENTRY rootDirectories; // list entry bdirectional of protected dirs 26 | KSPIN_LOCK directoriesSpinLock; // lock for the directory list 27 | 28 | /* GID system data members */ 29 | ULONGLONG 30 | GidCounter; // internal counter for gid, every new application receives a new 31 | // gid 32 | HashMap GidToPids; // mapping from gid to pids 33 | HashMap PidToGids; // mapping from pid to gid 34 | ULONGLONG gidsSize; // number of gids currently active 35 | LIST_ENTRY GidsList; // list entry of gids, used to clear memory 36 | KSPIN_LOCK GIDSystemLock; 37 | 38 | private: 39 | // call assumes protected code - high IRQL 40 | BOOLEAN RemoveProcessRecordAux(ULONG ProcessId, ULONGLONG gid); 41 | 42 | // call assumes protected code - high IRQL 43 | BOOLEAN RemoveGidRecordAux(PGID_ENTRY gidRecord); 44 | 45 | public: 46 | // c'tor init D.S. 47 | explicit DriverData(PDRIVER_OBJECT DriverObject); 48 | 49 | ~DriverData(); 50 | 51 | PWCHAR GetSystemRootPath() { return systemRootPath; } 52 | 53 | // sets the system root path, received from user mode application; we copy the 54 | // systemRootPath sent on the message 55 | VOID setSystemRootPath(PWCHAR setsystemRootPath) { 56 | RtlZeroBytes(systemRootPath, MAX_FILE_NAME_SIZE); 57 | RtlCopyBytes(systemRootPath, setsystemRootPath, MAX_FILE_NAME_LENGTH); 58 | RtlCopyBytes(systemRootPath + 59 | wcsnlen(systemRootPath, MAX_FILE_NAME_LENGTH / 2), 60 | L"\\Windows", wcsnlen(L"\\Windows", MAX_FILE_NAME_LENGTH / 2)); 61 | DbgPrint("Set system root path %ls\n", systemRootPath); 62 | } 63 | 64 | // remove a process which ended from the GID system, function raises IRQL 65 | BOOLEAN RemoveProcess(ULONG ProcessId); 66 | 67 | // record a process which was created to the GID system, function raises IRQL 68 | _IRQL_raises_(DISPATCH_LEVEL) BOOLEAN 69 | RecordNewProcess(PUNICODE_STRING ProcessName, ULONG ProcessId, 70 | ULONG ParentPid); 71 | 72 | // removed a gid from the system, function raises IRQL 73 | BOOLEAN RemoveGid(ULONGLONG gid); 74 | 75 | // gets the number of processes in a gid, function raise IRQL 76 | ULONGLONG GetGidSize(ULONGLONG gid, PBOOLEAN found); 77 | 78 | // help function, receives a buffer and returns an array of pids, returns true 79 | // only if all pids are restored 80 | BOOLEAN GetGidPids(ULONGLONG gid, PULONG buffer, ULONGLONG bufferSize, 81 | PULONGLONG returnedLength); 82 | 83 | // if found return true on found else return false 84 | ULONGLONG GetProcessGid(ULONG ProcessId, PBOOLEAN found); 85 | 86 | // clear all data related to the Gid system 87 | VOID ClearGidsPids(); 88 | 89 | ULONGLONG GidsSize(); 90 | 91 | BOOLEAN setFilterStart() { return (FilterRun = TRUE); } 92 | 93 | BOOLEAN setFilterStop() { return (FilterRun = FALSE); } 94 | 95 | BOOLEAN isFilterClosed() { return !FilterRun; } 96 | 97 | PFLT_FILTER *getFilterAdd() { return &Filter; } 98 | 99 | PFLT_FILTER getFilter() { return Filter; } 100 | 101 | ULONG getPID() { return pid; } 102 | 103 | ULONG setPID(ULONG Pid) { 104 | pid = Pid; 105 | return Pid; 106 | } 107 | 108 | // clears all irps waiting to report, function raises IRQL 109 | VOID ClearIrps(); 110 | 111 | ULONG IrpSize(); 112 | 113 | BOOLEAN AddIrpMessage(PIRP_ENTRY newEntry); 114 | 115 | BOOLEAN RemIrpMessage(PIRP_ENTRY newEntry); 116 | 117 | PIRP_ENTRY GetFirstIrpMessage(); 118 | 119 | // Takes Irps from the driverData and copy them to a buffer, also copies the 120 | // file names on which the irp occured, function raises IRQL 121 | VOID DriverGetIrps(PVOID Buffer, ULONG BufferSize, 122 | PULONG ReturnOutputBufferLength); 123 | 124 | LIST_ENTRY GetAllEntries(); 125 | 126 | BOOLEAN AddDirectoryEntry(PDIRECTORY_ENTRY newEntry); 127 | 128 | PDIRECTORY_ENTRY RemDirectoryEntry(LPCWSTR directory); 129 | 130 | /** 131 | IsContainingDirectory returns true if one of the directory entries in our 132 | LIST_ENTRY of PDIRECTORY_ENTRY is in the path passed as param 133 | */ 134 | BOOLEAN IsContainingDirectory(CONST PUNICODE_STRING path); 135 | 136 | VOID ClearDirectories(); 137 | 138 | VOID Clear() { 139 | // clear directories 140 | ClearDirectories(); 141 | 142 | // clear irps 143 | ClearIrps(); 144 | 145 | // clear gid system 146 | ClearGidsPids(); 147 | } 148 | }; 149 | 150 | extern DriverData *driverData; -------------------------------------------------------------------------------- /minifilter/snFilter/HashTable.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #define POOL_FLAG_NON_PAGED 0x0000000000000040UI64 // Non paged pool NX 3 | 4 | // Hashnode class 5 | struct HashNode { 6 | LIST_ENTRY entry; 7 | HANDLE value; 8 | ULONGLONG key; 9 | 10 | // Constructor of hashnode 11 | HashNode(ULONGLONG skey, HANDLE svalue) { 12 | InitializeListHead(&entry); 13 | value = svalue; 14 | key = skey; 15 | } 16 | 17 | void *operator new(size_t size) { 18 | void *ptr = ExAllocatePool2(POOL_FLAG_NON_PAGED, size, 'RW'); 19 | if (ptr != 0) { 20 | memset(ptr, 0, size); 21 | } 22 | return ptr; 23 | } 24 | 25 | void operator delete(void *ptr) { ExFreePoolWithTag(ptr, 'RW'); } 26 | // fixme needs new and delete operator 27 | }; 28 | 29 | // Our own Hashmap class - implemented as an array of list entries 30 | class HashMap { 31 | // hash element array 32 | PLIST_ENTRY arr[100]; 33 | 34 | ULONGLONG capacity; 35 | // current size 36 | ULONGLONG size; 37 | // dummy node 38 | 39 | public: 40 | HashMap() { 41 | // Initial capacity of a hash array 42 | capacity = 100; 43 | size = 0; 44 | 45 | // Initialise all elements of the array as NULL 46 | for (ULONGLONG i = 0; i < capacity; i++) { 47 | arr[i] = new LIST_ENTRY; 48 | InitializeListHead(arr[i]); 49 | } 50 | } 51 | 52 | ~HashMap() { 53 | for (ULONGLONG i = 0; i < capacity; i++) { 54 | delete (arr[i]); 55 | } 56 | } 57 | 58 | // This implements hash function to find index for a key 59 | ULONGLONG hashCode(ULONGLONG key) { return key % capacity; } 60 | 61 | // Function to add a key value pair 62 | HANDLE insertNode(ULONGLONG key, HANDLE value) { 63 | ULONGLONG hashIndex = hashCode(key); 64 | 65 | PLIST_ENTRY head = arr[hashIndex]; 66 | PLIST_ENTRY iterator = head->Flink; 67 | while (iterator != head) { // update 68 | HashNode *pClass; 69 | // 70 | // Do some processing. 71 | // 72 | pClass = (HashNode *)CONTAINING_RECORD(iterator, HashNode, entry); 73 | if (pClass->key == key) { 74 | HANDLE val = pClass->value; 75 | pClass->value = value; 76 | return val; 77 | } 78 | iterator = iterator->Flink; 79 | } 80 | // insert, no key found 81 | HashNode *temp = new HashNode(key, value); 82 | InsertHeadList(head, &(temp->entry)); 83 | size++; 84 | return value; 85 | } 86 | 87 | // Function to delete a key value pair 88 | HANDLE deleteNode(ULONGLONG key) { 89 | ULONGLONG hashIndex = hashCode(key); 90 | 91 | PLIST_ENTRY head = arr[hashIndex]; 92 | PLIST_ENTRY iterator = head->Flink; 93 | while (iterator != head) { 94 | HashNode *pClass; 95 | // 96 | // Do some processing. 97 | // 98 | pClass = (HashNode *)CONTAINING_RECORD(iterator, HashNode, entry); 99 | if (pClass->key == key) { 100 | RemoveEntryList(iterator); 101 | HANDLE value = pClass->value; 102 | size--; 103 | delete pClass; 104 | return value; 105 | } 106 | iterator = iterator->Flink; 107 | } 108 | 109 | // If not found return null 110 | return NULL; 111 | } 112 | 113 | // Function to search the value for a given key 114 | HANDLE get(ULONGLONG key) { 115 | ULONGLONG hashIndex = hashCode(key); 116 | PLIST_ENTRY head = arr[hashIndex]; 117 | PLIST_ENTRY iterator = head->Flink; 118 | while (iterator != head) { 119 | HashNode *pClass; 120 | // 121 | // Do some processing. 122 | // 123 | pClass = (HashNode *)CONTAINING_RECORD(iterator, HashNode, entry); 124 | if (pClass->key == key) { 125 | return pClass->value; 126 | } 127 | iterator = iterator->Flink; 128 | } 129 | 130 | // If not found return null 131 | return NULL; 132 | } 133 | 134 | // Return current size 135 | ULONGLONG sizeofMap() { return size; } 136 | 137 | // Return true if size is 0 138 | bool isEmpty() { return size == 0; } 139 | }; -------------------------------------------------------------------------------- /minifilter/snFilter/KernelCommon.cpp: -------------------------------------------------------------------------------- 1 | #include "KernelCommon.h" 2 | 3 | #define POOL_FLAG_NON_PAGED 0x0000000000000040UI64 // Non paged pool NX 4 | 5 | void *__cdecl operator new(size_t size) { 6 | return ExAllocatePool2(POOL_FLAG_NON_PAGED, size, 'RW'); 7 | } 8 | 9 | void __cdecl operator delete(void *data, size_t size) { 10 | UNREFERENCED_PARAMETER(size); 11 | if (data != NULL) 12 | ExFreePoolWithTag(data, 'RW'); 13 | } 14 | 15 | void __cdecl operator delete(void *data) { 16 | if (data != NULL) 17 | ExFreePoolWithTag(data, 'RW'); 18 | } 19 | 20 | // FIXME: add count param for copy length, MAX_FILE_NAME_LENGTH - 1 is default 21 | // value 22 | NTSTATUS CopyWString(LPWSTR dest, LPCWSTR source, size_t size) { 23 | INT err = wcsncpy_s(dest, size, source, MAX_FILE_NAME_LENGTH - 1); 24 | if (err == 0) { 25 | dest[size - 1] = L'\0'; 26 | return STATUS_SUCCESS; 27 | } else { 28 | return STATUS_INTERNAL_ERROR; 29 | } 30 | } 31 | 32 | WCHAR *stristr(const WCHAR *String, const WCHAR *Pattern) { 33 | WCHAR *pptr, *sptr, *start; 34 | 35 | for (start = (WCHAR *)String; *start != L'\0'; ++start) { 36 | while (((*start != L'\0') && 37 | (RtlUpcaseUnicodeChar(*start) != RtlUpcaseUnicodeChar(*Pattern)))) { 38 | ++start; 39 | } 40 | 41 | if (L'\0' == *start) 42 | return NULL; 43 | 44 | pptr = (WCHAR *)Pattern; 45 | sptr = (WCHAR *)start; 46 | 47 | while (RtlUpcaseUnicodeChar(*sptr) == RtlUpcaseUnicodeChar(*pptr)) { 48 | sptr++; 49 | pptr++; 50 | 51 | if (L'\0' == *pptr) 52 | return (start); 53 | } 54 | } 55 | 56 | return NULL; 57 | } 58 | 59 | BOOLEAN startsWith(PUNICODE_STRING String, PWCHAR Pattern) { 60 | if (String == NULL || Pattern == NULL) 61 | return FALSE; 62 | PWCHAR buffer = String->Buffer; 63 | for (ULONG i = 0; i < wcslen(Pattern); i++) { 64 | if (String->Length <= 2 * i) { 65 | // DbgPrint("String ended before pattern, %d\n", i); 66 | return FALSE; 67 | } 68 | if (RtlDowncaseUnicodeChar(Pattern[i]) != 69 | RtlDowncaseUnicodeChar(buffer[i])) { 70 | // DbgPrint("Chars not eq: %d, %d\n", RtlDowncaseUnicodeChar(Pattern[i]), 71 | // RtlDowncaseUnicodeChar(buffer[i])); 72 | return FALSE; 73 | } 74 | // DbgPrint("Chars are eq: %d, %d\n", RtlDowncaseUnicodeChar(Pattern[i]), 75 | // RtlDowncaseUnicodeChar(buffer[i])); 76 | } 77 | return TRUE; 78 | } -------------------------------------------------------------------------------- /minifilter/snFilter/KernelCommon.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "../SharedDefs/SharedDefs.h" 6 | 7 | // #define DEBUG_IRP 8 | #ifdef DEBUG_IRP 9 | #define IS_DEBUG_IRP 1 10 | #else 11 | #define IS_DEBUG_IRP 0 12 | #endif // DEBUG_IRP 13 | 14 | #define POOL_FLAG_NON_PAGED 0x0000000000000040UI64 // Non paged pool NX 15 | 16 | // PID_ENTRY - for each process in the system we record, we get its pid and 17 | // image file, Those are stored in thi struct the struct is meant to be used in 18 | // blist (LIST_ENTRY) 19 | typedef struct _PID_ENTRY { 20 | LIST_ENTRY entry; 21 | PUNICODE_STRING Path; 22 | ULONG Pid; 23 | 24 | _PID_ENTRY() { 25 | Pid = 0; 26 | Path = nullptr; 27 | entry.Flink = nullptr; 28 | entry.Blink = nullptr; 29 | } 30 | 31 | void *operator new(size_t size) { 32 | void *ptr = ExAllocatePool2(POOL_FLAG_NON_PAGED, size, 'RW'); 33 | if (size && ptr != nullptr) 34 | memset(ptr, 0, size); 35 | return ptr; 36 | } 37 | 38 | void operator delete(void *ptr) { ExFreePoolWithTag(ptr, 'RW'); } 39 | 40 | // fixme needs new and delete operator, dtor 41 | } PID_ENTRY, *PPID_ENTRY; 42 | 43 | typedef struct _DIRECTORY_ENTRY { 44 | LIST_ENTRY entry; 45 | WCHAR path[MAX_FILE_NAME_LENGTH]; 46 | 47 | _DIRECTORY_ENTRY() { 48 | InitializeListHead(&entry); 49 | path[0] = L'\0'; 50 | } 51 | 52 | } DIRECTORY_ENTRY, *PDIRECTORY_ENTRY; 53 | 54 | typedef struct _IRP_ENTRY { 55 | LIST_ENTRY entry; 56 | DRIVER_MESSAGE data; 57 | UNICODE_STRING 58 | filePath; // keep the path to unicode string related to the object, we copy it 59 | // later to user 60 | WCHAR Buffer[MAX_FILE_NAME_LENGTH]; // unicode string buffer for file name 61 | 62 | _IRP_ENTRY() { 63 | filePath.Length = 0; 64 | filePath.MaximumLength = MAX_FILE_NAME_SIZE; 65 | filePath.Buffer = Buffer; 66 | RtlZeroBytes(Buffer, MAX_FILE_NAME_SIZE); 67 | data.next = nullptr; 68 | data.IRP_OP = IRP_NONE; 69 | data.MemSizeUsed = 0; 70 | data.isEntropyCalc = FALSE; 71 | data.FileChange = FILE_CHANGE_NOT_SET; 72 | data.FileLocationInfo = FILE_NOT_PROTECTED; 73 | } 74 | 75 | void *operator new(size_t size) { 76 | void *ptr = ExAllocatePool2(POOL_FLAG_NON_PAGED, size, 'RW'); 77 | if (size && ptr != nullptr) 78 | memset(ptr, 0, size); 79 | return ptr; 80 | } 81 | 82 | void operator delete(void *ptr) { ExFreePoolWithTag(ptr, 'RW'); } 83 | 84 | } IRP_ENTRY, *PIRP_ENTRY; 85 | 86 | void *__cdecl operator new(size_t size); 87 | 88 | void __cdecl operator delete(void *data, size_t size); 89 | 90 | void __cdecl operator delete(void *data); 91 | 92 | NTSTATUS CopyWString(LPWSTR dest, LPCWSTR source, size_t size); 93 | 94 | WCHAR *stristr(const WCHAR *String, const WCHAR *Pattern); 95 | 96 | BOOLEAN startsWith(PUNICODE_STRING String, PWCHAR Pattern); 97 | 98 | // GID_ENTRY - for each gid in the system we record, holds pids entries 99 | // (PID_ENTRY) the struct is meant to be used in blist (LIST_ENTRY) 100 | struct GID_ENTRY { 101 | LIST_ENTRY GidListEntry; 102 | ULONGLONG gid; 103 | ULONGLONG pidsSize; 104 | LIST_ENTRY HeadListPids; 105 | 106 | // gid as input 107 | GID_ENTRY(ULONGLONG Gid) { 108 | gid = Gid; 109 | InitializeListHead(&HeadListPids); 110 | InitializeListHead(&GidListEntry); 111 | pidsSize = 0; 112 | } 113 | 114 | // copy 115 | GID_ENTRY(const GID_ENTRY &a) { 116 | HeadListPids.Flink = a.HeadListPids.Flink; 117 | HeadListPids.Blink = a.HeadListPids.Blink; 118 | GidListEntry.Flink = a.GidListEntry.Flink; 119 | GidListEntry.Blink = a.GidListEntry.Blink; 120 | gid = a.gid; 121 | pidsSize = a.pidsSize; 122 | } 123 | 124 | const GID_ENTRY &operator=(const GID_ENTRY &a) { 125 | HeadListPids.Flink = a.HeadListPids.Flink; 126 | HeadListPids.Blink = a.HeadListPids.Blink; 127 | GidListEntry.Flink = a.GidListEntry.Flink; 128 | GidListEntry.Blink = a.GidListEntry.Blink; 129 | gid = a.gid; 130 | pidsSize = a.pidsSize; 131 | this; 132 | } 133 | }; 134 | 135 | typedef GID_ENTRY *PGID_ENTRY; -------------------------------------------------------------------------------- /minifilter/snFilter/KernelString.cpp: -------------------------------------------------------------------------------- 1 | #include "KernelString.h" 2 | 3 | #define POOL_FLAG_NON_PAGED 0x0000000000000040UI64 // Non paged pool NX 4 | 5 | NTSTATUS 6 | FSAllocateUnicodeString(_Inout_ PUNICODE_STRING String) 7 | /*++ 8 | 9 | Routine Description: 10 | 11 | This routine allocates unicode string 12 | 13 | Arguments: 14 | 15 | String - supplies the size of the string to be allocated in the 16 | MaximumLength field return the unicode string 17 | 18 | Return Value: 19 | 20 | STATUS_SUCCESS - success 21 | STATUS_INSUFFICIENT_RESOURCES - failure 22 | 23 | --*/ 24 | { 25 | String->Buffer = 26 | (PWCH)ExAllocatePool2(POOL_FLAG_NON_PAGED, String->MaximumLength, 'RW'); 27 | 28 | if (String->Buffer == NULL) { 29 | return STATUS_INSUFFICIENT_RESOURCES; 30 | } 31 | 32 | String->Length = 0; 33 | 34 | return STATUS_SUCCESS; 35 | } 36 | 37 | VOID FSFreeUnicodeString(_Inout_ PUNICODE_STRING String) 38 | /*++ 39 | 40 | Routine Description: 41 | 42 | This routine frees unicode string 43 | 44 | Arguments: 45 | 46 | String - supplies the string to be freed 47 | 48 | Return Value: 49 | 50 | None 51 | 52 | --*/ 53 | { 54 | if (String->Buffer) { 55 | ExFreePoolWithTag(String->Buffer, 'RW'); 56 | String->Buffer = NULL; 57 | } 58 | 59 | String->Length = String->MaximumLength = 0; 60 | String->Buffer = NULL; 61 | } -------------------------------------------------------------------------------- /minifilter/snFilter/KernelString.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | NTSTATUS 6 | FSAllocateUnicodeString(_Inout_ PUNICODE_STRING String); 7 | 8 | VOID FSFreeUnicodeString(_Inout_ PUNICODE_STRING String); -------------------------------------------------------------------------------- /minifilter/snFilter/ShanonEntropy.cpp: -------------------------------------------------------------------------------- 1 | #pragma warning(disable : 28110) 2 | 3 | #include "ShanonEntropy.h" 4 | 5 | constexpr DOUBLE M_LOG2E = 1.4426950408889634; 6 | 7 | constexpr ULONG MAX_BYTE_SIZE = 256; 8 | 9 | _Kernel_float_used_ DOUBLE shannonEntropy(PUCHAR buffer, size_t size) { 10 | if (IS_DEBUG_IRP) 11 | DbgPrint("!!! snFilter: Calc entropy started\n"); 12 | DOUBLE entropy = 0.0; 13 | ULONG bucketByteVals[MAX_BYTE_SIZE] = {}; 14 | for (ULONG i = 0; i < size; i++) { 15 | bucketByteVals[buffer[i]]++; 16 | } 17 | 18 | __try { 19 | for (ULONG i = 0; i < MAX_BYTE_SIZE; i++) { 20 | if (bucketByteVals[i] != 0) { 21 | DOUBLE 22 | val = (DOUBLE)bucketByteVals[i] / (DOUBLE)size; 23 | entropy += (-1) * val * log(val) * M_LOG2E; 24 | } 25 | } 26 | } __finally { 27 | } 28 | 29 | return entropy; 30 | } -------------------------------------------------------------------------------- /minifilter/snFilter/ShanonEntropy.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "KernelCommon.h" 7 | 8 | // entropy between 0.0 to 8.0 9 | _Kernel_float_used_ DOUBLE shannonEntropy(PUCHAR buffer, size_t size); -------------------------------------------------------------------------------- /minifilter/snFilter/install.cmd: -------------------------------------------------------------------------------- 1 | echo Removing old version if exist > "%~dp0\install.log" 2 | start /wait /b sc stop snFilter >> "%~dp0\install.log" 2>&1 3 | start /wait /b sc delete snFilter >> "%~dp0\install.log" 2>&1 4 | start /wait /b pnputil -d %~dp0\snFilter.inf >> "%~dp0\install.log" 2>&1 5 | 6 | echo Installing new version >> "%~dp0\install.log" 2>&1 7 | start /wait /b pnputil -i -a %~dp0\snFilter.inf >> "%~dp0\install.log" 2>&1 8 | start /wait /b sc start snFilter >> "%~dp0\install.log" 2>&1 9 | -------------------------------------------------------------------------------- /minifilter/snFilter/snFilter.cpp: -------------------------------------------------------------------------------- 1 | #pragma clang diagnostic push 2 | #pragma ide diagnostic ignored "UnreachableCode" 3 | 4 | /*++ 5 | 6 | Module Name: 7 | 8 | snFilter.c 9 | 10 | Abstract: 11 | 12 | This is the main module of the snFilter miniFilter driver. 13 | 14 | Environment: 15 | 16 | Kernel mode 17 | 18 | --*/ 19 | 20 | #include "snFilter.h" 21 | 22 | #pragma prefast(disable \ 23 | : __WARNING_ENCODE_MEMBER_FUNCTION_POINTER, \ 24 | "Not valid for kernel mode drivers") 25 | 26 | #define POOL_FLAG_NON_PAGED 0x0000000000000040UI64 // Non paged pool NX 27 | 28 | // Structure that contains all the global data structures used throughout the 29 | // driver. 30 | 31 | EXTERN_C_START 32 | 33 | DRIVER_INITIALIZE DriverEntry; 34 | 35 | NTSTATUS 36 | DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath); 37 | 38 | EXTERN_C_END 39 | 40 | // 41 | // Constant FLT_REGISTRATION structure for our filter. This 42 | // initializes the callback routines our filter wants to register 43 | // for. This is only used to register with the filter manager 44 | // 45 | 46 | CONST 47 | FLT_OPERATION_REGISTRATION Callbacks[] = 48 | { //{IRP_MJ_CREATE, 0, FSPreOperation, FSPostOperation}, 49 | {IRP_MJ_CLOSE, 0, FSPreOperation, FSPostOperation}, 50 | {IRP_MJ_READ, 0, FSPreOperation, FSPostOperation}, 51 | //{IRP_MJ_CLEANUP, 0, FSPreOperation, NULL}, 52 | {IRP_MJ_WRITE, 0, FSPreOperation, NULL}, 53 | //{IRP_MJ_SET_INFORMATION, 0, FSPreOperation, NULL}, 54 | {IRP_MJ_OPERATION_END}}; 55 | 56 | /*++ 57 | 58 | FilterRegistration Defines what we want to filter with the driver 59 | 60 | --*/ 61 | CONST FLT_REGISTRATION FilterRegistration = { 62 | sizeof(FLT_REGISTRATION), // Size 63 | FLT_REGISTRATION_VERSION, // Version 64 | 0, // Flags 65 | NULL, // Context Registration. 66 | Callbacks, // Operation callbacks 67 | FSUnloadDriver, // FilterUnload 68 | FSInstanceSetup, // InstanceSetup 69 | FSInstanceQueryTeardown, // InstanceQueryTeardown 70 | FSInstanceTeardownStart, // InstanceTeardownStart 71 | FSInstanceTeardownComplete, // InstanceTeardownComplete 72 | NULL, // GenerateFileName 73 | NULL, // GenerateDestinationFileName 74 | NULL // NormalizeNameComponent 75 | }; 76 | 77 | //////////////////////////////////////////////////////////////////////////// 78 | // 79 | // Filter initialization and unload routines. 80 | // 81 | //////////////////////////////////////////////////////////////////////////// 82 | 83 | DRIVER_INITIALIZE DriverEntry; 84 | 85 | NTSTATUS 86 | DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) 87 | /*++ 88 | 89 | Routine Description: 90 | 91 | This is the initialization routine for the Filter driver. This 92 | registers the Filter with the filter manager and initializes all 93 | its global data structures. 94 | 95 | Arguments: 96 | 97 | DriverObject - Pointer to a driver object created by the system to 98 | represent this driver. 99 | 100 | RegistryPath - Unicode string identifying where the parameters for this 101 | driver are located in the registry. 102 | 103 | Return Value: 104 | 105 | Returns STATUS_SUCCESS. 106 | --*/ 107 | { 108 | UNREFERENCED_PARAMETER(RegistryPath); 109 | NTSTATUS status; 110 | 111 | // 112 | // Default to NonPagedPoolNx for non paged pool allocations where supported. 113 | // 114 | 115 | ExInitializeDriverRuntime(DrvRtPoolNxOptIn); 116 | 117 | // 118 | // Register with filter manager. 119 | // 120 | 121 | driverData = new DriverData(DriverObject); 122 | if (driverData == NULL) { 123 | return STATUS_MEMORY_NOT_ALLOCATED; 124 | } 125 | 126 | PFLT_FILTER *FilterAdd = driverData->getFilterAdd(); 127 | 128 | status = FltRegisterFilter(DriverObject, &FilterRegistration, FilterAdd); 129 | 130 | if (!NT_SUCCESS(status)) { 131 | delete driverData; 132 | return status; 133 | } 134 | 135 | commHandle = new CommHandler(driverData->getFilter()); 136 | if (commHandle == NULL) { 137 | delete driverData; 138 | return STATUS_MEMORY_NOT_ALLOCATED; 139 | } 140 | 141 | status = InitCommData(); 142 | 143 | if (!NT_SUCCESS(status)) { 144 | FltUnregisterFilter(driverData-> 145 | 146 | getFilter() 147 | 148 | ); 149 | delete driverData; 150 | delete commHandle; 151 | return status; 152 | } 153 | // 154 | // Start filtering I/O. 155 | // 156 | status = FltStartFiltering(driverData->getFilter()); 157 | 158 | if (!NT_SUCCESS(status)) { 159 | CommClose(); 160 | 161 | FltUnregisterFilter(driverData-> 162 | 163 | getFilter() 164 | 165 | ); 166 | delete driverData; 167 | delete commHandle; 168 | return status; 169 | } 170 | driverData-> 171 | 172 | setFilterStart(); 173 | 174 | DbgPrint("loaded scanner successfully"); 175 | // new code 176 | // TODO: check status and release in unload 177 | PsSetCreateProcessNotifyRoutine(AddRemProcessRoutine, FALSE); 178 | return STATUS_SUCCESS; 179 | } 180 | 181 | NTSTATUS 182 | FSUnloadDriver(_In_ FLT_FILTER_UNLOAD_FLAGS Flags) 183 | /*++ 184 | 185 | Routine Description: 186 | 187 | This is to unload routine for the Filter driver.This is unregistering the 188 | Filter with the filter manager and frees any allocated global data 189 | structures. 190 | 191 | Arguments: 192 | 193 | None. 194 | 195 | Return Value: 196 | 197 | Returns the final status of the de-allocation routines. 198 | 199 | --*/ 200 | { 201 | UNREFERENCED_PARAMETER(Flags); 202 | 203 | // 204 | // Close the server port. 205 | // 206 | driverData->setFilterStop(); 207 | CommClose(); 208 | 209 | // 210 | // Unregister the filter 211 | // 212 | 213 | FltUnregisterFilter(driverData->getFilter()); 214 | delete driverData; 215 | delete commHandle; 216 | PsSetCreateProcessNotifyRoutine(AddRemProcessRoutine, TRUE); 217 | return STATUS_SUCCESS; 218 | } 219 | 220 | NTSTATUS 221 | FSInstanceSetup(_In_ PCFLT_RELATED_OBJECTS FltObjects, 222 | _In_ FLT_INSTANCE_SETUP_FLAGS Flags, 223 | _In_ DEVICE_TYPE VolumeDeviceType, 224 | _In_ FLT_FILESYSTEM_TYPE VolumeFilesystemType) 225 | /*++ 226 | 227 | Routine Description: 228 | 229 | This routine is called whenever a new instance is created on a volume. This 230 | gives us a chance to decide if we need to attach to this volume or not. 231 | 232 | If this routine is not defined in the registration structure, automatic 233 | instances are always created. 234 | 235 | Arguments: 236 | 237 | FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing 238 | opaque handles to this filter, instance and its associated volume. 239 | 240 | Flags - Flags describing the reason for this attach request. 241 | 242 | Return Value: 243 | 244 | STATUS_SUCCESS - attach 245 | STATUS_FLT_DO_NOT_ATTACH - do not attach 246 | 247 | --*/ 248 | { 249 | UNREFERENCED_PARAMETER(FltObjects); 250 | UNREFERENCED_PARAMETER(Flags); 251 | UNREFERENCED_PARAMETER(VolumeDeviceType); 252 | UNREFERENCED_PARAMETER(VolumeFilesystemType); 253 | 254 | DbgPrint("snFilter: Entered FSInstanceSetup\n"); 255 | 256 | WCHAR newTemp[40]; 257 | 258 | GvolumeData.MaximumLength = 80; 259 | GvolumeData.Buffer = newTemp; 260 | GvolumeData.Length = 0; 261 | 262 | NTSTATUS hr = STATUS_SUCCESS; 263 | PDEVICE_OBJECT devObject; 264 | hr = FltGetDiskDeviceObject(FltObjects->Volume, &devObject); 265 | if (!NT_SUCCESS(hr)) { 266 | return STATUS_SUCCESS; 267 | // return hr; 268 | } 269 | hr = IoVolumeDeviceToDosName(devObject, &GvolumeData); 270 | if (!NT_SUCCESS(hr)) { 271 | // return STATUS_SUCCESS; 272 | 273 | return hr; 274 | } 275 | return STATUS_SUCCESS; 276 | } 277 | 278 | NTSTATUS 279 | FSInstanceQueryTeardown(_In_ PCFLT_RELATED_OBJECTS FltObjects, 280 | _In_ FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags) 281 | /*++ 282 | 283 | Routine Description: 284 | 285 | This is called when an instance is being manually deleted by a 286 | call to FltDetachVolume or FilterDetach thereby giving us a 287 | chance to fail that detach request. 288 | 289 | If this routine is not defined in the registration structure, explicit 290 | detach requests via FltDetachVolume or FilterDetach will always be 291 | failed. 292 | 293 | Arguments: 294 | 295 | FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing 296 | opaque handles to this filter, instance and its associated volume. 297 | 298 | Flags - Indicating where this detached request came from. 299 | 300 | Return Value: 301 | 302 | Returns the status of this operation. 303 | 304 | --*/ 305 | { 306 | UNREFERENCED_PARAMETER(FltObjects); 307 | UNREFERENCED_PARAMETER(Flags); 308 | 309 | DbgPrint("snFilter: Entered FSInstanceQueryTeardown\n"); 310 | 311 | return STATUS_SUCCESS; 312 | } 313 | 314 | VOID FSInstanceTeardownStart(_In_ PCFLT_RELATED_OBJECTS FltObjects, 315 | _In_ FLT_INSTANCE_TEARDOWN_FLAGS Flags) 316 | /*++ 317 | 318 | Routine Description: 319 | 320 | This routine is called at the start of instance teardown. 321 | 322 | Arguments: 323 | 324 | FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing 325 | opaque handles to this filter, instance and its associated volume. 326 | 327 | Flags - Reason why this instance is being deleted. 328 | 329 | Return Value: 330 | 331 | None. 332 | 333 | --*/ 334 | { 335 | UNREFERENCED_PARAMETER(FltObjects); 336 | UNREFERENCED_PARAMETER(Flags); 337 | 338 | DbgPrint("snFilter: Entered FSInstanceTeardownStart\n"); 339 | } 340 | 341 | VOID FSInstanceTeardownComplete(_In_ PCFLT_RELATED_OBJECTS FltObjects, 342 | _In_ FLT_INSTANCE_TEARDOWN_FLAGS Flags) 343 | /*++ 344 | 345 | Routine Description: 346 | 347 | This routine is called at the end of instance teardown. 348 | 349 | Arguments: 350 | 351 | FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing 352 | opaque handles to this filter, instance and its associated volume. 353 | 354 | Flags - Reason why this instance is being deleted. 355 | 356 | Return Value: 357 | 358 | None. 359 | 360 | --*/ 361 | { 362 | UNREFERENCED_PARAMETER(FltObjects); 363 | UNREFERENCED_PARAMETER(Flags); 364 | DbgPrint("snFilter: Entered FSInstanceTeardownComplete\n"); 365 | } 366 | 367 | FLT_PREOP_CALLBACK_STATUS 368 | FSPreOperation(_Inout_ PFLT_CALLBACK_DATA Data, 369 | _In_ PCFLT_RELATED_OBJECTS FltObjects, 370 | _Flt_CompletionContext_Outptr_ PVOID 371 | 372 | *CompletionContext) 373 | /*++ 374 | 375 | Routine Description: 376 | 377 | Pre operations callback 378 | 379 | Arguments: 380 | 381 | Data - The structure which describes the operation parameters. 382 | 383 | FltObject - The structure which describes the objects affected by this 384 | operation. 385 | 386 | CompletionContext - Output parameter which can be used to pass a context 387 | from this pre-create callback to the post-create callback. 388 | 389 | Return Value: 390 | 391 | FLT_PREOP_SUCCESS_WITH_CALLBACK - If this is not our user-mode process. 392 | FLT_PREOP_SUCCESS_NO_CALLBACK - All other threads. 393 | 394 | --*/ 395 | { 396 | NTSTATUS hr = STATUS_SUCCESS; 397 | // See if this "create" is being done by our user process. 398 | if (FltGetRequestorProcessId(Data) == 4) 399 | return FLT_PREOP_SUCCESS_NO_CALLBACK; // system process-skip 400 | if (FltGetRequestorProcessId(Data) == driverData->getPID()) { 401 | if (IS_DEBUG_IRP) 402 | DbgPrint( 403 | "!!! snFilter: Allowing pre op for trusted process, no post op\n"); 404 | 405 | return FLT_PREOP_SUCCESS_NO_CALLBACK; 406 | } 407 | if (FltObjects->FileObject == NULL) { // no file object 408 | return FLT_PREOP_SUCCESS_NO_CALLBACK; 409 | } 410 | // create tested only on post-op, can't check here 411 | if (Data->Iopb->MajorFunction == IRP_MJ_CREATE) { 412 | return FLT_PREOP_SUCCESS_WITH_CALLBACK; 413 | } 414 | hr = FSProcessPreOperartion(Data, FltObjects, CompletionContext); 415 | if (hr == FLT_PREOP_SUCCESS_WITH_CALLBACK) 416 | return FLT_PREOP_SUCCESS_WITH_CALLBACK; 417 | 418 | return FLT_PREOP_SUCCESS_NO_CALLBACK; 419 | } 420 | 421 | NTSTATUS 422 | FSProcessPreOperartion(_Inout_ PFLT_CALLBACK_DATA Data, 423 | _In_ PCFLT_RELATED_OBJECTS FltObjects, 424 | _Flt_CompletionContext_Outptr_ PVOID 425 | 426 | *CompletionContext) { 427 | // no communication 428 | if (driverData-> 429 | 430 | isFilterClosed() 431 | 432 | || 433 | 434 | IsCommClosed() 435 | 436 | ) { 437 | // DbgPrint("!!! snFilter: Filter is closed, or Port is closed, skipping 438 | // data\n"); 439 | return FLT_PREOP_SUCCESS_NO_CALLBACK; 440 | } 441 | NTSTATUS hr = FLT_PREOP_SUCCESS_NO_CALLBACK; 442 | 443 | PFLT_FILE_NAME_INFORMATION nameInfo; 444 | hr = FltGetFileNameInformation( 445 | Data, 446 | FLT_FILE_NAME_OPENED | FLT_FILE_NAME_QUERY_ALWAYS_ALLOW_CACHE_LOOKUP, 447 | &nameInfo); 448 | if (!NT_SUCCESS(hr)) 449 | return hr; 450 | 451 | BOOLEAN isDir; 452 | hr = FltIsDirectory(Data->Iopb->TargetFileObject, Data->Iopb->TargetInstance, 453 | &isDir); 454 | if (!NT_SUCCESS(hr)) 455 | return hr; 456 | if (isDir) 457 | return FLT_PREOP_SUCCESS_NO_CALLBACK; 458 | PIRP_ENTRY newEntry = new IRP_ENTRY(); 459 | if (newEntry == NULL) { 460 | FltReferenceFileNameInformation(nameInfo); 461 | return hr; 462 | } 463 | // reset 464 | PDRIVER_MESSAGE newItem = &newEntry->data; 465 | PUNICODE_STRING FilePath = &(newEntry->filePath); 466 | 467 | hr = GetFileNameInfo(FltObjects, FilePath, nameInfo); 468 | if (!NT_SUCCESS(hr)) { 469 | FltReferenceFileNameInformation(nameInfo); 470 | delete newEntry; 471 | return hr; 472 | } 473 | 474 | // get pid 475 | newItem->PID = FltGetRequestorProcessId(Data); 476 | 477 | BOOLEAN isGidFound; 478 | ULONGLONG gid = driverData->GetProcessGid(newItem->PID, &isGidFound); 479 | if (gid == 0 || !isGidFound) { 480 | if (IS_DEBUG_IRP) 481 | DbgPrint("!!! snFilter: Item does not have a gid, skipping\n"); 482 | FltReferenceFileNameInformation(nameInfo); 483 | delete newEntry; 484 | return FLT_PREOP_SUCCESS_NO_CALLBACK; 485 | } 486 | newItem->Gid = gid; 487 | 488 | if (IS_DEBUG_IRP) 489 | DbgPrint("!!! snFilter: Registering new irp for Gid: %d with pid: %d\n", 490 | (int)gid, newItem->PID); 491 | 492 | // get file id 493 | hr = CopyFileIdInfo(Data, newItem); 494 | if (!NT_SUCCESS(hr)) { 495 | FltReferenceFileNameInformation(nameInfo); 496 | delete newEntry; 497 | return hr; 498 | } 499 | 500 | if (FSIsFileNameInScanDirs(FilePath)) { 501 | if (IS_DEBUG_IRP) 502 | DbgPrint("!!! snFilter: File in scan area \n"); 503 | newItem->FileLocationInfo = FILE_PROTECTED; 504 | } 505 | 506 | if (Data->Iopb->MajorFunction == IRP_MJ_READ || 507 | Data->Iopb->MajorFunction == IRP_MJ_WRITE) { 508 | CopyExtension(newItem->Extension, nameInfo); 509 | } 510 | 511 | if (IS_DEBUG_IRP) 512 | DbgPrint("!!! snFilter: Logging IRP op: %s \n", 513 | FltGetIrpName(Data->Iopb->MajorFunction)); 514 | 515 | if (Data->Iopb->MajorFunction != IRP_MJ_SET_INFORMATION) 516 | FltReleaseFileNameInformation(nameInfo); 517 | 518 | switch (Data->Iopb->MajorFunction) { 519 | // create is handled on post-operation, read is created here but calculated on 520 | // post(data avilable 521 | case IRP_MJ_READ: { 522 | newItem->IRP_OP = IRP_READ; 523 | if (Data->Iopb->Parameters.Read.Length == 0) // no data to read 524 | { 525 | delete newEntry; 526 | DbgPrint("snFilter: IRP READ NOCALLBACK LENGTH IS ZERO! \n"); 527 | return FLT_PREOP_SUCCESS_NO_CALLBACK; 528 | } 529 | if (IS_DEBUG_IRP) 530 | DbgPrint("!!! snFilter: Preop IRP_MJ_READ, return with postop \n"); 531 | // save context for post, we calculate the entropy of read, we pass the irp 532 | // to application on post-op 533 | *CompletionContext = newEntry; 534 | DbgPrint("snFilter: IRP READ WITH CALLBACK! ****************** \n"); 535 | return FLT_PREOP_SUCCESS_WITH_CALLBACK; 536 | } 537 | case IRP_MJ_CLEANUP: 538 | newItem->IRP_OP = IRP_CLEANUP; 539 | break; 540 | case IRP_MJ_WRITE: { 541 | newItem->IRP_OP = IRP_WRITE; 542 | // if (newItem->FileLocationInfo == FILE_NOT_PROTECTED) { 543 | // delete newEntry; 544 | // return FLT_PREOP_SUCCESS_NO_CALLBACK; 545 | // } 546 | newItem->FileChange = FILE_CHANGE_WRITE; 547 | PVOID writeBuffer = NULL; 548 | if (Data->Iopb->Parameters.Write.Length == 0) // no data to write 549 | { 550 | break; 551 | } 552 | 553 | // prepare buffer for entropy calc 554 | if (Data->Iopb->Parameters.Write.MdlAddress == 555 | NULL) { // there's mdl buffer, we use it 556 | writeBuffer = Data->Iopb->Parameters.Write.WriteBuffer; 557 | } else { 558 | writeBuffer = MmGetSystemAddressForMdlSafe( 559 | Data->Iopb->Parameters.Write.MdlAddress, 560 | NormalPagePriority | MdlMappingNoExecute); 561 | } 562 | if (writeBuffer == NULL) { // alloc failed 563 | delete newEntry; 564 | // fail the irp request 565 | Data->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; 566 | Data->IoStatus.Information = 0; 567 | return FLT_PREOP_COMPLETE; 568 | } 569 | newItem->MemSizeUsed = Data->Iopb->Parameters.Write.Length; 570 | 571 | // we catch EXCEPTION_EXECUTE_HANDLER so to prevent crash when calculating 572 | KFLOATING_SAVE SaveState; 573 | NTSTATUS Status = KeSaveFloatingPointState(&SaveState); 574 | if (NT_SUCCESS(Status)) { 575 | __try { 576 | newItem->Entropy = 577 | shannonEntropy((PUCHAR)writeBuffer, newItem->MemSizeUsed); 578 | newItem->isEntropyCalc = TRUE; 579 | } __except (EXCEPTION_EXECUTE_HANDLER) { 580 | if (IS_DEBUG_IRP) 581 | DbgPrint("!!! snFilter: Failed to calc entropy\n"); 582 | delete newEntry; 583 | // fail the irp request 584 | Data->IoStatus.Status = STATUS_INTERNAL_ERROR; 585 | Data->IoStatus.Information = 0; 586 | return FLT_PREOP_COMPLETE; 587 | } 588 | } else { 589 | if (IS_DEBUG_IRP) 590 | DbgPrint("!!! snFilter: Failed to calc entropy\n"); 591 | delete newEntry; 592 | // fail the irp request 593 | Data->IoStatus.Status = STATUS_INTERNAL_ERROR; 594 | Data->IoStatus.Information = 0; 595 | return FLT_PREOP_COMPLETE; 596 | } 597 | KeRestoreFloatingPointState(&SaveState); 598 | } break; 599 | case IRP_MJ_SET_INFORMATION: { 600 | newItem->IRP_OP = IRP_SETINFO; 601 | // we check for delete later and renaming 602 | FILE_INFORMATION_CLASS fileInfo = 603 | Data->Iopb->Parameters.SetFileInformation.FileInformationClass; 604 | 605 | if (fileInfo == FileDispositionInformation && // handle delete later 606 | (((PFILE_DISPOSITION_INFORMATION)(Data->Iopb->Parameters 607 | .SetFileInformation.InfoBuffer)) 608 | ->DeleteFile)) { 609 | newItem->FileChange = FILE_CHANGE_DELETE_FILE; 610 | } // end delete 1 611 | 612 | else if (fileInfo == FileDispositionInformationEx && 613 | FlagOn(((PFILE_DISPOSITION_INFORMATION_EX)(Data->Iopb->Parameters 614 | .SetFileInformation 615 | .InfoBuffer)) 616 | ->Flags, 617 | FILE_DISPOSITION_DELETE)) { 618 | newItem->FileChange = FILE_CHANGE_DELETE_FILE; 619 | } // end delete 2 620 | 621 | else if (fileInfo == FileRenameInformation || 622 | fileInfo == FileRenameInformationEx) { 623 | // OPTIONAL: get new name? 624 | 625 | newItem->FileChange = FILE_CHANGE_RENAME_FILE; 626 | PFILE_RENAME_INFORMATION renameInfo = 627 | (PFILE_RENAME_INFORMATION) 628 | Data->Iopb->Parameters.SetFileInformation.InfoBuffer; 629 | PFLT_FILE_NAME_INFORMATION newNameInfo; 630 | WCHAR Buffer[MAX_FILE_NAME_LENGTH]; 631 | UNICODE_STRING NewFilePath; 632 | NewFilePath.Buffer = Buffer; 633 | NewFilePath.Length = 0; 634 | NewFilePath.MaximumLength = MAX_FILE_NAME_SIZE; 635 | 636 | hr = FltGetDestinationFileNameInformation( 637 | FltObjects->Instance, FltObjects->FileObject, 638 | renameInfo->RootDirectory, renameInfo->FileName, 639 | renameInfo->FileNameLength, 640 | FLT_FILE_NAME_QUERY_DEFAULT | 641 | FLT_FILE_NAME_REQUEST_FROM_CURRENT_PROVIDER | 642 | FLT_FILE_NAME_OPENED, 643 | &newNameInfo); 644 | if (!NT_SUCCESS(hr)) { 645 | delete newEntry; 646 | FltReleaseFileNameInformation(nameInfo); 647 | return hr; 648 | } 649 | 650 | NTSTATUS status = GetFileNameInfo(FltObjects, &NewFilePath, newNameInfo); 651 | if (!NT_SUCCESS(status)) { 652 | delete newEntry; 653 | FltReleaseFileNameInformation(nameInfo); 654 | FltReleaseFileNameInformation(newNameInfo); 655 | return FLT_PREOP_SUCCESS_NO_CALLBACK; 656 | } 657 | 658 | RtlCopyBytes(newEntry->Buffer, Buffer, 659 | MAX_FILE_NAME_SIZE); // replace buffer data with new file 660 | newItem->FileLocationInfo = FILE_MOVED_OUT; 661 | /* 662 | if (FSIsFileNameInScanDirs(&NewFilePath)) { 663 | if (newItem->FileLocationInfo == FILE_NOT_PROTECTED) { // moved in - 664 | report new file name newItem->FileLocationInfo = FILE_MOVED_IN; 665 | //newEntry->filePath = NewFilePath; // remember file moved in 666 | RtlCopyBytes(newEntry->Buffer, Buffer, MAX_FILE_NAME_SIZE); // replace 667 | buffer data with new file } // else we still report old file name, so we 668 | know it was changed 669 | } 670 | else { // new file name not protected 671 | if (newItem->FileLocationInfo == FILE_PROTECTED) { // moved out - report 672 | old file name newItem->FileLocationInfo = FILE_MOVED_OUT; 673 | } 674 | /*else { // we dont care - rename of file in unprotected area to 675 | unprotected area delete newEntry; FltReleaseFileNameInformation(nameInfo); 676 | FltReleaseFileNameInformation(newNameInfo); 677 | return FLT_PREOP_SUCCESS_NO_CALLBACK; 678 | } 679 | } 680 | */ 681 | 682 | CopyExtension(newItem->Extension, newNameInfo); 683 | FltReleaseFileNameInformation(newNameInfo); 684 | for (LONG i = 0; i < FILE_OBJEC_MAX_EXTENSION_SIZE; i++) { 685 | if (i == (nameInfo->Extension.Length / 2)) 686 | break; 687 | if (newItem->Extension[i] != nameInfo->Extension.Buffer[i]) { 688 | newItem->FileChange = FILE_CHANGE_EXTENSION_CHANGED; 689 | break; 690 | } 691 | } 692 | FltReleaseFileNameInformation(nameInfo); 693 | } // end rename 694 | else // not rename or delete (set info) 695 | { 696 | delete newEntry; 697 | FltReleaseFileNameInformation(nameInfo); 698 | return FLT_PREOP_SUCCESS_NO_CALLBACK; 699 | } 700 | break; 701 | } 702 | default: 703 | delete newEntry; 704 | return FLT_PREOP_SUCCESS_NO_CALLBACK; 705 | } 706 | if (IS_DEBUG_IRP) 707 | DbgPrint("!!! snFilter: Adding entry to irps %s\n", 708 | FltGetIrpName(Data->Iopb->MajorFunction)); 709 | if (!driverData->AddIrpMessage(newEntry)) { 710 | delete newEntry; 711 | } 712 | return FLT_PREOP_SUCCESS_NO_CALLBACK; 713 | } 714 | 715 | FLT_POSTOP_CALLBACK_STATUS 716 | FSPostOperation(_Inout_ PFLT_CALLBACK_DATA Data, 717 | _In_ PCFLT_RELATED_OBJECTS FltObjects, 718 | _In_opt_ PVOID CompletionContext, 719 | _In_ FLT_POST_OPERATION_FLAGS Flags) 720 | /*++ 721 | 722 | Routine Description: 723 | 724 | Post-operation callback. we reach here in case of IRP_MJ_CREATE or 725 | IRP_MJ_READ 726 | 727 | Arguments: 728 | 729 | Data - The structure which describes the operation parameters. 730 | 731 | FltObject - The structure which describes the objects affected by this 732 | operation. 733 | 734 | CompletionContext - The operation context passed fron the pre-create 735 | callback. 736 | 737 | Flags - Flags to say why we are getting this post-operation callback. 738 | 739 | Return Value: 740 | 741 | FLT_POSTOP_FINISHED_PROCESSING - ok to open the file, or we wish to deny 742 | access to this file, hence undo the open 743 | 744 | --*/ 745 | { 746 | // DbgPrint("!!! snFilter: Enter post op for irp: %s, pid of process: %u\n", 747 | // FltGetIrpName(Data->Iopb->MajorFunction), FltGetRequestorProcessId(Data)); 748 | if (!NT_SUCCESS(Data->IoStatus.Status) || 749 | (STATUS_REPARSE == Data->IoStatus.Status)) { 750 | // DbgPrint("!!! snFilter: finished post operation, already failed \n"); 751 | if (CompletionContext != nullptr && 752 | Data->Iopb->MajorFunction == IRP_MJ_READ) { 753 | delete (PIRP_ENTRY)CompletionContext; 754 | } 755 | return FLT_POSTOP_FINISHED_PROCESSING; 756 | } 757 | 758 | if (Data->Iopb->MajorFunction == IRP_MJ_CREATE) { 759 | return FSProcessCreateIrp(Data, FltObjects); 760 | } else if (Data->Iopb->MajorFunction == IRP_MJ_READ) { 761 | // return FLT_POSTOP_FINISHED_PROCESSING; 762 | return FSProcessPostReadIrp(Data, FltObjects, CompletionContext, Flags); 763 | } 764 | return FLT_POSTOP_FINISHED_PROCESSING; 765 | } 766 | 767 | FLT_POSTOP_CALLBACK_STATUS 768 | FSProcessCreateIrp(_Inout_ PFLT_CALLBACK_DATA Data, 769 | _In_ PCFLT_RELATED_OBJECTS FltObjects) { 770 | NTSTATUS hr; 771 | if (FlagOn(Data->Iopb->OperationFlags, SL_OPEN_TARGET_DIRECTORY) || 772 | FlagOn(Data->Iopb->OperationFlags, SL_OPEN_PAGING_FILE)) { 773 | return FLT_POSTOP_FINISHED_PROCESSING; 774 | } 775 | 776 | if (driverData->isFilterClosed() || IsCommClosed()) { 777 | // DbgPrint("!!! snFilter: filter closed or comm closed, skip irp\n"); 778 | return FLT_POSTOP_FINISHED_PROCESSING; 779 | } 780 | 781 | BOOLEAN isDir; 782 | hr = FltIsDirectory(Data->Iopb->TargetFileObject, Data->Iopb->TargetInstance, 783 | &isDir); 784 | if (!NT_SUCCESS(hr)) { 785 | return FLT_POSTOP_FINISHED_PROCESSING; 786 | } 787 | 788 | PFLT_FILE_NAME_INFORMATION nameInfo; 789 | hr = FltGetFileNameInformation( 790 | Data, 791 | FLT_FILE_NAME_OPENED | FLT_FILE_NAME_QUERY_ALWAYS_ALLOW_CACHE_LOOKUP, 792 | &nameInfo); 793 | if (!NT_SUCCESS(hr)) { 794 | return FLT_POSTOP_FINISHED_PROCESSING; 795 | } 796 | 797 | PIRP_ENTRY newEntry = new IRP_ENTRY(); 798 | if (newEntry == NULL) { 799 | FltReleaseFileNameInformation(nameInfo); 800 | return FLT_POSTOP_FINISHED_PROCESSING; 801 | } 802 | PDRIVER_MESSAGE newItem = &newEntry->data; 803 | 804 | newItem->PID = FltGetRequestorProcessId(Data); 805 | newItem->IRP_OP = IRP_CREATE; 806 | newItem->FileLocationInfo = FILE_PROTECTED; 807 | PUNICODE_STRING FilePath = &(newEntry->filePath); 808 | 809 | BOOLEAN isGidFound; 810 | ULONGLONG gid = driverData->GetProcessGid(newItem->PID, &isGidFound); 811 | if (gid == 0 || !isGidFound) { 812 | // DbgPrint("!!! snFilter: Item does not have a gid, skipping\n"); // TODO: 813 | // incase it doesnt exist we can add it with our method that checks for 814 | // system process 815 | FltReferenceFileNameInformation(nameInfo); 816 | delete newEntry; 817 | return FLT_POSTOP_FINISHED_PROCESSING; 818 | } 819 | newItem->Gid = gid; 820 | DbgPrint("!!! snFilter: Registering new irp for Gid: %d with pid: %d\n", 821 | (int)gid, 822 | newItem->PID); // TODO: incase it doesnt exist we can add it with our 823 | // method that checks for system process 824 | 825 | // get file id 826 | hr = CopyFileIdInfo(Data, newItem); 827 | if (!NT_SUCCESS(hr)) { 828 | delete newEntry; 829 | return FLT_POSTOP_FINISHED_PROCESSING; 830 | } 831 | 832 | hr = GetFileNameInfo(FltObjects, FilePath, nameInfo); 833 | if (!NT_SUCCESS(hr)) { 834 | delete newEntry; 835 | return FLT_POSTOP_FINISHED_PROCESSING; 836 | } 837 | 838 | CopyExtension(newItem->Extension, nameInfo); 839 | 840 | FltReleaseFileNameInformation(nameInfo); 841 | 842 | /* 843 | if (!FSIsFileNameInScanDirs(FilePath)) { 844 | if (IS_DEBUG_IRP) DbgPrint("!!! snFilter: Skipping uninterented file, not 845 | in scan area \n"); delete newEntry; return FLT_POSTOP_FINISHED_PROCESSING; 846 | } 847 | */ 848 | 849 | if (isDir && (Data->IoStatus.Information) == FILE_OPENED) { 850 | if (IS_DEBUG_IRP) 851 | DbgPrint("!!! snFilter: Dir listing opened on existing directory\n"); 852 | newItem->FileChange = FILE_OPEN_DIRECTORY; 853 | } else if (isDir) { 854 | if (IS_DEBUG_IRP) 855 | DbgPrint("!!! snFilter: Dir but not listing, not important \n"); 856 | delete newEntry; 857 | return FLT_POSTOP_FINISHED_PROCESSING; 858 | } else if ((Data->IoStatus.Information) == FILE_OVERWRITTEN || 859 | (Data->IoStatus.Information) == FILE_SUPERSEDED) { 860 | newItem->FileChange = FILE_CHANGE_OVERWRITE_FILE; 861 | } else if (FlagOn(Data->Iopb->Parameters.Create.Options, 862 | FILE_DELETE_ON_CLOSE)) { 863 | newItem->FileChange = FILE_CHANGE_DELETE_FILE; 864 | if ((Data->IoStatus.Information) == FILE_CREATED) { 865 | newItem->FileChange = FILE_CHANGE_DELETE_NEW_FILE; 866 | } 867 | } else if ((Data->IoStatus.Information) == FILE_CREATED) { 868 | newItem->FileChange = FILE_CHANGE_NEW_FILE; 869 | } 870 | if (IS_DEBUG_IRP) 871 | DbgPrint("!!! snFilter: Adding entry to irps\n"); 872 | if (!driverData->AddIrpMessage(newEntry)) { 873 | delete newEntry; 874 | } 875 | return FLT_POSTOP_FINISHED_PROCESSING; 876 | } 877 | 878 | FLT_POSTOP_CALLBACK_STATUS 879 | FSProcessPostReadIrp(_Inout_ PFLT_CALLBACK_DATA Data, 880 | _In_ PCFLT_RELATED_OBJECTS FltObjects, 881 | _In_opt_ PVOID CompletionContext, 882 | _In_ FLT_POST_OPERATION_FLAGS Flags) { 883 | if (CompletionContext == NULL) { 884 | return FLT_POSTOP_FINISHED_PROCESSING; 885 | } 886 | 887 | PIRP_ENTRY entry = (PIRP_ENTRY)CompletionContext; 888 | 889 | if (driverData->isFilterClosed() || IsCommClosed()) { 890 | if (IS_DEBUG_IRP) 891 | DbgPrint("!!! snFilter: Post op read, comm or filter closed\n"); 892 | delete entry; 893 | return FLT_POSTOP_FINISHED_PROCESSING; 894 | } 895 | 896 | FLT_POSTOP_CALLBACK_STATUS status = FLT_POSTOP_FINISHED_PROCESSING; 897 | 898 | PVOID ReadBuffer = NULL; 899 | 900 | // prepare buffer for entropy calc 901 | if (Data->Iopb->Parameters.Read.MdlAddress != 902 | NULL) { // there's mdl buffer, we use it 903 | ReadBuffer = 904 | MmGetSystemAddressForMdlSafe(Data->Iopb->Parameters.Read.MdlAddress, 905 | NormalPagePriority | MdlMappingNoExecute); 906 | } else if (FlagOn(Data->Flags, FLTFL_CALLBACK_DATA_SYSTEM_BUFFER)) // safe 907 | { 908 | ReadBuffer = Data->Iopb->Parameters.Read.ReadBuffer; 909 | } else { 910 | if (FltDoCompletionProcessingWhenSafe( 911 | Data, FltObjects, CompletionContext, Flags, FSProcessPostReadSafe, 912 | &status)) { // post to worker thread or run if irql is ok 913 | return FLT_POSTOP_FINISHED_PROCESSING; 914 | } else { 915 | Data->IoStatus.Status = STATUS_INTERNAL_ERROR; 916 | Data->IoStatus.Information = 0; 917 | delete entry; 918 | return status; 919 | } 920 | } 921 | if (!ReadBuffer) { 922 | delete entry; 923 | Data->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; 924 | Data->IoStatus.Information = 0; 925 | return FLT_POSTOP_FINISHED_PROCESSING; 926 | } 927 | entry->data.MemSizeUsed = 928 | (ULONG)Data->IoStatus.Information; // successful read data 929 | 930 | // we catch EXCEPTION_EXECUTE_HANDLER so to prevent crash when calculating 931 | KFLOATING_SAVE SaveState; 932 | NTSTATUS Status = KeSaveFloatingPointState(&SaveState); 933 | if (NT_SUCCESS(Status)) { 934 | __try { 935 | entry->data.Entropy = 936 | shannonEntropy((PUCHAR)ReadBuffer, Data->IoStatus.Information); 937 | entry->data.isEntropyCalc = TRUE; 938 | } __except (EXCEPTION_EXECUTE_HANDLER) { 939 | delete entry; 940 | // fail the irp request 941 | Data->IoStatus.Status = STATUS_INTERNAL_ERROR; 942 | Data->IoStatus.Information = 0; 943 | return FLT_POSTOP_FINISHED_PROCESSING; 944 | } 945 | } else { 946 | delete entry; 947 | // fail the irp request 948 | Data->IoStatus.Status = STATUS_INTERNAL_ERROR; 949 | Data->IoStatus.Information = 0; 950 | return FLT_POSTOP_FINISHED_PROCESSING; 951 | } 952 | KeRestoreFloatingPointState(&SaveState); 953 | 954 | if (IS_DEBUG_IRP) 955 | DbgPrint("!!! snFilter: Adding entry to irps IRP_MJ_READ\n"); 956 | if (!driverData->AddIrpMessage(entry)) { 957 | delete entry; 958 | } 959 | return FLT_POSTOP_FINISHED_PROCESSING; 960 | } 961 | 962 | FLT_POSTOP_CALLBACK_STATUS FSProcessPostReadSafe( 963 | _Inout_ PFLT_CALLBACK_DATA Data, _In_ PCFLT_RELATED_OBJECTS FltObjects, 964 | _In_opt_ PVOID CompletionContext, _In_ FLT_POST_OPERATION_FLAGS Flags) { 965 | UNREFERENCED_PARAMETER(Flags); 966 | UNREFERENCED_PARAMETER(FltObjects); 967 | 968 | NTSTATUS status = STATUS_SUCCESS; 969 | PIRP_ENTRY entry = (PIRP_ENTRY)CompletionContext; 970 | ASSERT(entry != nullptr); 971 | status = FltLockUserBuffer(Data); 972 | if (NT_SUCCESS(status)) { 973 | PVOID ReadBuffer = 974 | MmGetSystemAddressForMdlSafe(Data->Iopb->Parameters.Read.MdlAddress, 975 | NormalPagePriority | MdlMappingNoExecute); 976 | if (ReadBuffer != NULL) { 977 | KFLOATING_SAVE SaveState; 978 | NTSTATUS Status = KeSaveFloatingPointState(&SaveState); 979 | if (NT_SUCCESS(Status)) { 980 | __try { 981 | if (entry != nullptr) { 982 | entry->data.Entropy = 983 | shannonEntropy((PUCHAR)ReadBuffer, Data->IoStatus.Information); 984 | entry->data.MemSizeUsed = Data->IoStatus.Information; 985 | entry->data.isEntropyCalc = TRUE; 986 | } 987 | if (IS_DEBUG_IRP) 988 | DbgPrint("!!! snFilter: Adding entry to irps IRP_MJ_READ\n"); 989 | if (driverData->AddIrpMessage(entry)) { 990 | return FLT_POSTOP_FINISHED_PROCESSING; 991 | } 992 | } __except (EXCEPTION_EXECUTE_HANDLER) { 993 | status = STATUS_INTERNAL_ERROR; 994 | } 995 | } else { 996 | status = STATUS_INTERNAL_ERROR; 997 | } 998 | KeRestoreFloatingPointState(&SaveState); 999 | } 1000 | status = STATUS_INSUFFICIENT_RESOURCES; 1001 | } 1002 | delete entry; 1003 | return FLT_POSTOP_FINISHED_PROCESSING; 1004 | } 1005 | 1006 | BOOLEAN 1007 | FSIsFileNameInScanDirs(CONST PUNICODE_STRING path) { 1008 | // ASSERT(driverData != NULL); 1009 | return driverData->IsContainingDirectory(path); 1010 | } 1011 | 1012 | NTSTATUS 1013 | FSEntrySetFileName(CONST PFLT_VOLUME Volume, 1014 | PFLT_FILE_NAME_INFORMATION nameInfo, 1015 | PUNICODE_STRING uString) { 1016 | NTSTATUS hr = STATUS_SUCCESS; 1017 | PDEVICE_OBJECT devObject; 1018 | USHORT volumeDosNameSize; 1019 | USHORT finalNameSize; 1020 | USHORT volumeNameSize = nameInfo->Volume.Length; // in bytes 1021 | USHORT origNameSize = nameInfo->Name.Length; // in bytes 1022 | 1023 | WCHAR newTemp[40]; 1024 | 1025 | UNICODE_STRING volumeData; 1026 | volumeData.MaximumLength = 80; 1027 | volumeData.Buffer = newTemp; 1028 | volumeData.Length = 0; 1029 | 1030 | hr = FltGetDiskDeviceObject(Volume, &devObject); 1031 | if (!NT_SUCCESS(hr)) { 1032 | return hr; 1033 | } 1034 | /*if (KeAreAllApcsDisabled()) { 1035 | return hr; 1036 | }*/ 1037 | 1038 | if (! 1039 | 1040 | KeAreAllApcsDisabled() 1041 | 1042 | ) { 1043 | hr = IoVolumeDeviceToDosName(devObject, &GvolumeData); 1044 | } 1045 | volumeDosNameSize = GvolumeData.Length; 1046 | finalNameSize = origNameSize - volumeNameSize + 1047 | volumeDosNameSize; // not null terminated, in bytes 1048 | 1049 | // DbgPrint("Volume name: %wZ, Size: %d, finalNameSize: %d, volumeNameSize: 1050 | // %d\n", volumeData, volumeDosNameSize, finalNameSize, volumeNameSize); 1051 | // DbgPrint("Name buffer: %wZ\n", nameInfo->Name); 1052 | 1053 | if (uString == NULL) { 1054 | ObDereferenceObject(devObject); 1055 | return STATUS_INVALID_ADDRESS; 1056 | } 1057 | if (volumeNameSize == 1058 | origNameSize) { // file is the volume, don't need to do anything 1059 | ObDereferenceObject(devObject); 1060 | return RtlUnicodeStringCopy(uString, &nameInfo->Name); 1061 | } 1062 | 1063 | if (NT_SUCCESS(hr = RtlUnicodeStringCopy( 1064 | uString, &GvolumeData))) { // prefix of volume e.g. C: 1065 | 1066 | // DbgPrint("File name: %wZ\n", uString); 1067 | RtlCopyMemory(uString->Buffer + (volumeDosNameSize / 2), 1068 | nameInfo->Name.Buffer + (volumeNameSize / 2), 1069 | ((finalNameSize - volumeDosNameSize > 1070 | MAX_FILE_NAME_SIZE - volumeDosNameSize) 1071 | ? (MAX_FILE_NAME_SIZE - volumeDosNameSize) 1072 | : (finalNameSize - volumeDosNameSize))); 1073 | uString->Length = (finalNameSize > MAX_FILE_NAME_SIZE) ? MAX_FILE_NAME_SIZE 1074 | : finalNameSize; 1075 | // DbgPrint("File name: %wZ\n", uString); 1076 | } 1077 | ObDereferenceObject(devObject); 1078 | return hr; 1079 | } 1080 | 1081 | NTSTATUS 1082 | CopyFileIdInfo(_Inout_ PFLT_CALLBACK_DATA Data, PDRIVER_MESSAGE newItem) { 1083 | FILE_ID_INFORMATION fileInformation; 1084 | NTSTATUS hr = FltQueryInformationFile( 1085 | Data->Iopb->TargetInstance, Data->Iopb->TargetFileObject, 1086 | &fileInformation, sizeof(FILE_ID_INFORMATION), FileIdInformation, NULL); 1087 | RtlCopyMemory(&(newItem->FileID), &fileInformation, 1088 | sizeof(FILE_ID_INFORMATION)); 1089 | return hr; 1090 | } 1091 | 1092 | NTSTATUS GetFileNameInfo(_In_ PCFLT_RELATED_OBJECTS FltObjects, 1093 | PUNICODE_STRING FilePath, 1094 | PFLT_FILE_NAME_INFORMATION nameInfo) { 1095 | NTSTATUS hr; 1096 | hr = FltParseFileNameInformation(nameInfo); 1097 | if (!NT_SUCCESS(hr)) { 1098 | FltReleaseFileNameInformation(nameInfo); 1099 | return hr; 1100 | } 1101 | hr = FSEntrySetFileName(FltObjects->Volume, nameInfo, FilePath); 1102 | // DbgPrint("!!!snFilter DEBUG EntryFileName %d \n", NT_SUCCESS(hr)); 1103 | if (!NT_SUCCESS(hr)) { 1104 | FltReleaseFileNameInformation(nameInfo); 1105 | } 1106 | return hr; 1107 | } 1108 | 1109 | VOID CopyExtension(PWCHAR dest, PFLT_FILE_NAME_INFORMATION nameInfo) { 1110 | if (IS_DEBUG_IRP) 1111 | DbgPrint("!!! snFilter: copying the file type extension, extension length: " 1112 | "%d, name: %wZ\n", 1113 | nameInfo->Extension.Length, nameInfo->Extension); 1114 | RtlZeroBytes(dest, (FILE_OBJEC_MAX_EXTENSION_SIZE + 1) * sizeof(WCHAR)); 1115 | for (LONG i = 0; i < FILE_OBJEC_MAX_EXTENSION_SIZE; i++) { 1116 | if (i == (nameInfo->Extension.Length / 2)) 1117 | break; 1118 | dest[i] = nameInfo->Extension.Buffer[i]; 1119 | } 1120 | } 1121 | 1122 | static NTSTATUS GetProcessNameByHandle(_In_ HANDLE ProcessHandle, 1123 | _Out_ PUNICODE_STRING 1124 | 1125 | *Name) { 1126 | ULONG retLength = 0; 1127 | ULONG pniSize = 512; 1128 | PUNICODE_STRING pni = NULL; 1129 | NTSTATUS status = STATUS_UNSUCCESSFUL; 1130 | 1131 | do { 1132 | pni = (PUNICODE_STRING)ExAllocatePool2(POOL_FLAG_NON_PAGED, pniSize, 'RW'); 1133 | if (pni != NULL) { 1134 | status = ZwQueryInformationProcess(ProcessHandle, ProcessImageFileName, 1135 | pni, pniSize, &retLength); 1136 | if (!NT_SUCCESS(status)) { 1137 | ExFreePoolWithTag(pni, 'RW'); 1138 | pniSize *= 2; 1139 | } 1140 | } else 1141 | status = STATUS_INSUFFICIENT_RESOURCES; 1142 | } while (status == STATUS_INFO_LENGTH_MISMATCH); 1143 | 1144 | if (NT_SUCCESS(status)) 1145 | *Name = pni; 1146 | 1147 | return status; 1148 | } 1149 | 1150 | // new code process recording 1151 | _IRQL_raises_(DISPATCH_LEVEL) VOID 1152 | AddRemProcessRoutine(HANDLE ParentId, HANDLE ProcessId, BOOLEAN Create) { 1153 | if (commHandle->CommClosed) 1154 | return; 1155 | if (Create) { 1156 | NTSTATUS hr; 1157 | if (ZwQueryInformationProcess == NULL) { 1158 | UNICODE_STRING routineName = 1159 | RTL_CONSTANT_STRING(L"ZwQueryInformationProcess"); 1160 | 1161 | ZwQueryInformationProcess = 1162 | (QUERY_INFO_PROCESS)MmGetSystemRoutineAddress(&routineName); 1163 | 1164 | if (ZwQueryInformationProcess == NULL) { 1165 | DbgPrint("Cannot resolve ZwQueryInformationProcess\n"); 1166 | hr = STATUS_UNSUCCESSFUL; 1167 | return; 1168 | } 1169 | } 1170 | HANDLE procHandleParent; 1171 | HANDLE procHandleProcess; 1172 | 1173 | CLIENT_ID clientIdParent; 1174 | clientIdParent.UniqueProcess = ParentId; 1175 | clientIdParent.UniqueThread = 0; 1176 | 1177 | CLIENT_ID clientIdProcess; 1178 | clientIdProcess.UniqueProcess = ProcessId; 1179 | clientIdProcess.UniqueThread = 0; 1180 | 1181 | OBJECT_ATTRIBUTES objAttribs; 1182 | 1183 | InitializeObjectAttributes(&objAttribs, NULL, OBJ_KERNEL_HANDLE, NULL, 1184 | NULL); 1185 | 1186 | hr = ZwOpenProcess(&procHandleParent, PROCESS_ALL_ACCESS, &objAttribs, 1187 | &clientIdParent); 1188 | if (!NT_SUCCESS(hr)) { 1189 | DbgPrint("!!! snFilter: Failed to open process: %#010x.\n", hr); 1190 | return; 1191 | } 1192 | hr = ZwOpenProcess(&procHandleProcess, PROCESS_ALL_ACCESS, &objAttribs, 1193 | &clientIdProcess); 1194 | if (!NT_SUCCESS(hr)) { 1195 | DbgPrint("!!! snFilter: Failed to open process: %#010x.\n", hr); 1196 | hr = FltClose(procHandleParent); 1197 | if (!NT_SUCCESS(hr)) { 1198 | DbgPrint("!!! snFilter: Failed to close process: %#010x.\n", hr); 1199 | return; 1200 | } 1201 | return; 1202 | } 1203 | 1204 | PUNICODE_STRING procName; 1205 | PUNICODE_STRING parentName; 1206 | hr = GetProcessNameByHandle(procHandleParent, &parentName); 1207 | if (!NT_SUCCESS(hr)) { 1208 | DbgPrint("!!! snFilter: Failed to get parent name: %#010x\n", hr); 1209 | return; 1210 | } 1211 | hr = GetProcessNameByHandle(procHandleProcess, &procName); 1212 | if (!NT_SUCCESS(hr)) { 1213 | DbgPrint("!!! snFilter: Failed to get process name: %#010x\n", hr); 1214 | return; 1215 | } 1216 | 1217 | DbgPrint("!!! snFilter: New Process, parent: %wZ. Pid: %d\n", parentName, 1218 | (ULONG)(ULONG_PTR)ParentId); 1219 | 1220 | hr = FltClose(procHandleParent); 1221 | if (!NT_SUCCESS(hr)) { 1222 | DbgPrint("!!! snFilter: Failed to close process: %#010x.\n", hr); 1223 | return; 1224 | } 1225 | hr = FltClose(procHandleProcess); 1226 | if (!NT_SUCCESS(hr)) { 1227 | DbgPrint("!!! snFilter: Failed to close process: %#010x.\n", hr); 1228 | return; 1229 | } 1230 | DbgPrint("!!! snFilter: New Process, process: %wZ , pid: %d.\n", procName, 1231 | (ULONG)(ULONG_PTR)ProcessId); 1232 | 1233 | BOOLEAN found = FALSE; 1234 | if (startsWith(procName, 1235 | driverData->GetSystemRootPath()) && // process in safe area 1236 | startsWith(parentName, 1237 | driverData->GetSystemRootPath()) && // parent in safe area 1238 | (driverData->GetProcessGid((ULONG)(ULONG_PTR)ParentId, &found) == 0) && 1239 | !found) // parent is not documented, if it was, there was a recursive 1240 | // call from a not safe process which 1241 | // resulted in safe are in windows dir 1242 | { 1243 | DbgPrint("!!! snFilter: Open Process not recorded, both parent and " 1244 | "process are safe\n"); 1245 | delete parentName; 1246 | delete procName; 1247 | return; 1248 | } 1249 | // options to reach: a process is not safe (parent safe or not), 1250 | // process safe parent is not, both safe, but before 1251 | // parent there was an unsafe process 1252 | DbgPrint("!!! snFilter: Open Process recording, is parent safe: %d, is " 1253 | "process safe: %d\n", 1254 | startsWith(procName, driverData->GetSystemRootPath()), 1255 | startsWith(parentName, driverData->GetSystemRootPath())); 1256 | driverData->RecordNewProcess(procName, (ULONG)(ULONG_PTR)ProcessId, 1257 | (ULONG)(ULONG_PTR)ParentId); 1258 | delete parentName; 1259 | } else { 1260 | DbgPrint("!!! snFilter: Terminate Process, Process: %d pid\n", 1261 | (ULONG)(ULONG_PTR)ProcessId); 1262 | driverData->RemoveProcess((ULONG)(ULONG_PTR)ProcessId); 1263 | } 1264 | } 1265 | 1266 | #pragma clang diagnostic pop -------------------------------------------------------------------------------- /minifilter/snFilter/snFilter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /*++ 4 | 5 | Module Name: 6 | 7 | snFilter.h 8 | 9 | Abstract: 10 | 11 | Header file for the kernel FS driver 12 | 13 | Environment: 14 | 15 | Kernel mode 16 | 17 | --*/ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include "../SharedDefs/SharedDefs.h" 27 | #include "Communication.h" 28 | #include "DriverData.h" 29 | #include "KernelString.h" 30 | #include "ShanonEntropy.h" 31 | 32 | NTSTATUS 33 | FSUnloadDriver(_In_ FLT_FILTER_UNLOAD_FLAGS Flags); 34 | 35 | FLT_POSTOP_CALLBACK_STATUS 36 | FSPostOperation(_Inout_ PFLT_CALLBACK_DATA Data, 37 | _In_ PCFLT_RELATED_OBJECTS FltObjects, 38 | _In_opt_ PVOID CompletionContext, 39 | _In_ FLT_POST_OPERATION_FLAGS Flags); 40 | 41 | FLT_PREOP_CALLBACK_STATUS 42 | FSPreOperation(_Inout_ PFLT_CALLBACK_DATA Data, 43 | _In_ PCFLT_RELATED_OBJECTS FltObjects, 44 | _Flt_CompletionContext_Outptr_ PVOID 45 | 46 | *CompletionContext); 47 | 48 | NTSTATUS 49 | FSInstanceSetup(_In_ PCFLT_RELATED_OBJECTS FltObjects, 50 | _In_ FLT_INSTANCE_SETUP_FLAGS Flags, 51 | _In_ DEVICE_TYPE VolumeDeviceType, 52 | _In_ FLT_FILESYSTEM_TYPE VolumeFilesystemType); 53 | 54 | NTSTATUS 55 | FSInstanceQueryTeardown(_In_ PCFLT_RELATED_OBJECTS FltObjects, 56 | _In_ FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags); 57 | 58 | VOID FSInstanceTeardownStart(_In_ PCFLT_RELATED_OBJECTS FltObjects, 59 | _In_ FLT_INSTANCE_TEARDOWN_FLAGS Flags); 60 | 61 | VOID FSInstanceTeardownComplete(_In_ PCFLT_RELATED_OBJECTS FltObjects, 62 | _In_ FLT_INSTANCE_TEARDOWN_FLAGS Flags); 63 | 64 | // handles pre operation for read, write, set info and close files 65 | NTSTATUS 66 | FSProcessPreOperartion(_Inout_ PFLT_CALLBACK_DATA Data, 67 | _In_ PCFLT_RELATED_OBJECTS FltObjects, 68 | _Flt_CompletionContext_Outptr_ PVOID 69 | 70 | *CompletionContext); 71 | 72 | NTSTATUS 73 | FSEntrySetFileName(const PFLT_VOLUME volume, 74 | PFLT_FILE_NAME_INFORMATION nameInfo, 75 | PUNICODE_STRING uString); 76 | 77 | FLT_POSTOP_CALLBACK_STATUS 78 | FSProcessPostReadIrp(_Inout_ PFLT_CALLBACK_DATA Data, 79 | _In_ PCFLT_RELATED_OBJECTS FltObjects, 80 | _In_opt_ PVOID CompletionContext, 81 | _In_ FLT_POST_OPERATION_FLAGS Flags); 82 | 83 | FLT_POSTOP_CALLBACK_STATUS 84 | FSProcessPostReadSafe(_Inout_ PFLT_CALLBACK_DATA Data, 85 | _In_ PCFLT_RELATED_OBJECTS FltObjects, 86 | _In_opt_ PVOID CompletionContext, 87 | _In_ FLT_POST_OPERATION_FLAGS Flags); 88 | 89 | // handles IRP_MJ_CREATE irps on post-op 90 | FLT_POSTOP_CALLBACK_STATUS 91 | FSProcessCreateIrp(_Inout_ PFLT_CALLBACK_DATA Data, 92 | _In_ PCFLT_RELATED_OBJECTS FltObjects); 93 | 94 | // compares unicode string file name to the directories in protected areas in 95 | // driverData object return true if the file is in one of the dirs 96 | BOOLEAN 97 | FSIsFileNameInScanDirs(CONST PUNICODE_STRING path); 98 | 99 | // ZwQueryInformationProcess - dynamic loaded function which query info data 100 | // about already opened processes 101 | typedef NTSTATUS (*QUERY_INFO_PROCESS)( 102 | __in HANDLE ProcessHandle, __in PROCESSINFOCLASS ProcessInformationClass, 103 | __out_bcount(ProcessInformationLength) PVOID ProcessInformation, 104 | __in ULONG ProcessInformationLength, __out_opt PULONG ReturnLength); 105 | 106 | QUERY_INFO_PROCESS ZwQueryInformationProcess; 107 | 108 | // copy the file id info from the data argument (FLT_CALLBACK_DATA) to 109 | // DRIVER_MESSAGE class allocated 110 | NTSTATUS 111 | CopyFileIdInfo(_Inout_ PFLT_CALLBACK_DATA Data, PDRIVER_MESSAGE newItem); 112 | 113 | // receives a pointer to allocated unicode string, FLT_RELATED_OBJECTS and 114 | // FILE_NAME_INFORMATION class. function gets the file name from the name info 115 | // and flt objects and fill the unicode string with it 116 | NTSTATUS GetFileNameInfo(_In_ PCFLT_RELATED_OBJECTS FltObjects, 117 | PUNICODE_STRING FilePath, 118 | PFLT_FILE_NAME_INFORMATION nameInfo); 119 | 120 | // copy extension info from FILE_NAME_INFORMATION class to null terminated wchar 121 | // string 122 | VOID CopyExtension(PWCHAR dest, PFLT_FILE_NAME_INFORMATION nameInfo); 123 | 124 | // AddRemProcessRoutine is the function hooked to the processes creation and 125 | // exit. When a new process enters, we add it to parent gid if there is any. if 126 | // parent doesn't have a gid and both are system process, a new process isn't 127 | // recorded else we create a new gid for the process 128 | 129 | _IRQL_raises_(DISPATCH_LEVEL) VOID 130 | AddRemProcessRoutine(HANDLE ParentId, HANDLE ProcessId, BOOLEAN Create); 131 | 132 | UNICODE_STRING GvolumeData; -------------------------------------------------------------------------------- /minifilter/snFilter/snFilter.inf: -------------------------------------------------------------------------------- 1 | ; ----------------------------------------------------------------------- 2 | ; snFilter 3 | ; ----------------------------------------------------------------------- 4 | 5 | [Version] 6 | Signature = "$Windows NT$" 7 | ; Change the Class and ClassGuid to match the Load Order Group value, see https://msdn.microsoft.com/en-us/windows/hardware/gg462963 8 | Class = "ActivityMonitor" ;This is determined by the work this filter driver does 9 | ClassGuid = {b86dff51-a31e-4bac-b3cf-e8cfe75c9fc2} ;This value is determined by the Load Order Group value 10 | Provider = %ManufacturerName% 11 | DriverVer = 08/07/2022,1.0.0.0 12 | CatalogFile = snFilter.cat 13 | PnpLockDown = 1 14 | 15 | ; ----------------------------------------------------------------------- 16 | 17 | [DestinationDirs] 18 | DefaultDestDir = 12 19 | ; MiniFilter.DriverFiles = 12 ;%windir%\system32\drivers 20 | MiniFilter.CopyDriverFiles = 12 ;%windir%\system32\drivers 21 | MiniFilter.DeleteDriverFiles = 12 ;%windir%\system32\drivers 22 | MiniFilter.UserFiles = 10,FltMgr 23 | 24 | ; Default install sections ---------------------------------------------- 25 | 26 | [DefaultInstall.NTamd64] 27 | OptionDesc = %ServiceDescription% 28 | CopyFiles = MiniFilter.CopyDriverFiles 29 | 30 | [DefaultInstall.NTamd64.Services] 31 | AddService = %ServiceName%,,MiniFilter.Service 32 | 33 | [DefaultInstall.NTx86] 34 | OptionDesc = %ServiceDescription% 35 | CopyFiles = MiniFilter.CopyDriverFiles 36 | 37 | [DefaultInstall.NTx86.Services] 38 | AddService = %ServiceName%,,MiniFilter.Service 39 | 40 | ; Default uninstall sections -------------------------------------------- 41 | 42 | [DefaultUninstall.NTamd64] 43 | DelFiles = MiniFilter.DeleteDriverFiles 44 | LegacyUninstall = 1 45 | 46 | [DefaultUninstall.NTamd64.Services] 47 | DelService = %ServiceName%,0x200 ;Ensure service is stopped before deleting 48 | 49 | [DefaultUninstall.NTx86] 50 | DelFiles = MiniFilter.DeleteDriverFiles 51 | LegacyUninstall = 1 52 | 53 | [DefaultUninstall.NTx86.Services] 54 | DelService = %ServiceName%,0x200 ;Ensure service is stopped before deleting 55 | 56 | ; Services Section ------------------------------------------------------ 57 | 58 | [MiniFilter.Service] 59 | DisplayName = %ServiceName% 60 | Description = %ServiceDescription% 61 | ServiceBinary = %12%\%DriverName%.sys ;%windir%\system32\drivers\ 62 | Dependencies = FltMgr 63 | ServiceType = 2 ;SERVICE_FILE_SYSTEM_DRIVER 64 | StartType = 3 ;SERVICE_DEMAND_START 65 | ErrorControl = 1 ;SERVICE_ERROR_NORMAL 66 | ; Change the Load Order Group value 67 | LoadOrderGroup = "snFilter Activity Monitor" 68 | AddReg = MiniFilter.AddRegistry 69 | 70 | ; Registry Modifications ------------------------------------------------ 71 | 72 | [MiniFilter.AddRegistry] 73 | HKR,,"DebugFlags",0x00010001 ,0x0 74 | HKR,,"SupportedFeatures",0x00010001,0x3 75 | HKR,"Instances","DefaultInstance",0x00000000,%DefaultInstance% 76 | HKR,"Instances\"%Instance1.Name%,"Altitude",0x00000000,%Instance1.Altitude% 77 | HKR,"Instances\"%Instance1.Name%,"Flags",0x00010001,%Instance1.Flags% 78 | 79 | ; Copy Delete Files ----------------------------------------------------- 80 | 81 | [MiniFilter.DeleteDriverFiles] 82 | %DriverName%.sys,,,0x00010001 ;(DELFLG_IN_USE | DELFLG_IN_USE1) 83 | 84 | [MiniFilter.CopyDriverFiles] 85 | %DriverName%.sys,,,0x00002000 ;COPYFLG_NOPRUNE 86 | 87 | [SourceDisksFiles] 88 | snFilter.sys = 1,, 89 | 90 | [SourceDisksNames] 91 | 1 = %DiskId1%,,, 92 | 93 | ; String Section -------------------------------------------------------- 94 | 95 | [Strings] 96 | ManufacturerName = "sn99" 97 | ServiceDescription = "snFilter Mini-Filter Driver" 98 | ServiceName = "snFilter" 99 | DriverName = "snFilter" 100 | DiskId1 = "snFilter Device Installation Disk" 101 | 102 | ; Instances specific information ---------------------------------------- 103 | 104 | DefaultInstance = "snFilter Instance" 105 | Instance1.Name = "snFilter Instance" 106 | ; Change the altitude value, see https://msdn.microsoft.com/en-us/windows/hardware/drivers/ifs/load-order-groups-and-altitudes-for-minifilter-drivers 107 | Instance1.Altitude = "378781" 108 | Instance1.Flags = 0x0 ; Allow all attachments 109 | -------------------------------------------------------------------------------- /minifilter/snFilter/snFilter.rc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define VER_FILETYPE VFT_DRV 5 | #define VER_FILESUBTYPE VFT2_DRV_SYSTEM 6 | #define VER_FILEDESCRIPTION_STR "snFilter Filter Driver" 7 | #define VER_INTERNALNAME_STR "snFilter.sys" 8 | 9 | #include "common.ver" -------------------------------------------------------------------------------- /minifilter/snFilter/snFilter.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | x64 7 | 8 | 9 | Release 10 | x64 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | {DF8682E7-17C1-4450-A68C-D7CEF8780D8F} 25 | {f2f62967-0815-4fd7-9b86-6eedcac766eb} 26 | v4.5 27 | 12.0 28 | Debug 29 | Win32 30 | snFilter 31 | snFilter 32 | 10.0.22621.0 33 | False 34 | CustomCommand 35 | %SystemDrive%\DriverTest\Drivers\install.cmd 36 | 37 | 38 | 39 | Windows10 40 | true 41 | WindowsKernelModeDriver10.0 42 | Driver 43 | WDM 44 | x64 45 | false 46 | true 47 | 48 | 49 | Windows10 50 | false 51 | WindowsKernelModeDriver10.0 52 | Driver 53 | WDM 54 | true 55 | false 56 | x64 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | DbgengKernelDebugger 68 | true 69 | true 70 | 71 | 72 | DbgengKernelDebugger 73 | true 74 | true 75 | true 76 | 77 | 78 | 79 | fltmgr.lib;$(DDK_LIB_PATH)\libcntpr.lib;%(AdditionalDependencies) 80 | true 81 | UseLinkTimeCodeGeneration 82 | 83 | 84 | Level4 85 | MultiThreaded 86 | false 87 | stdcpp17 88 | MaxSpeed 89 | true 90 | Speed 91 | true 92 | 93 | 94 | SHA256 95 | 96 | 97 | 98 | 99 | fltmgr.lib;$(DDK_LIB_PATH)\libcntpr.lib;%(AdditionalDependencies) 100 | UseLinkTimeCodeGeneration 101 | 102 | 103 | stdcpp20 104 | MaxSpeed 105 | false 106 | Level4 107 | true 108 | Speed 109 | MultiThreaded 110 | AnySuitable 111 | Precise 112 | stdc11 113 | 114 | 115 | SHA1 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | -------------------------------------------------------------------------------- /minifilter/snFilter/snFilter.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | {8E41214B-6785-4CFE-B992-037D68949A14} 18 | inf;inv;inx;mof;mc; 19 | 20 | 21 | 22 | 23 | Source Files 24 | 25 | 26 | Source Files 27 | 28 | 29 | Source Files 30 | 31 | 32 | Source Files 33 | 34 | 35 | Source Files 36 | 37 | 38 | Source Files 39 | 40 | 41 | 42 | 43 | Resource Files 44 | 45 | 46 | 47 | 48 | Header Files 49 | 50 | 51 | Header Files 52 | 53 | 54 | Header Files 55 | 56 | 57 | Header Files 58 | 59 | 60 | Header Files 61 | 62 | 63 | Header Files 64 | 65 | 66 | Header Files 67 | 68 | 69 | 70 | 71 | Driver Files 72 | 73 | 74 | -------------------------------------------------------------------------------- /readme_resources/boot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SubconsciousCompute/fsfilter-rs/c7bc03168e2eba6d378c9bf73533a4147921d75b/readme_resources/boot.png -------------------------------------------------------------------------------- /readme_resources/disable_memory_integrity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SubconsciousCompute/fsfilter-rs/c7bc03168e2eba6d378c9bf73533a4147921d75b/readme_resources/disable_memory_integrity.png -------------------------------------------------------------------------------- /readme_resources/example.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SubconsciousCompute/fsfilter-rs/c7bc03168e2eba6d378c9bf73533a4147921d75b/readme_resources/example.gif -------------------------------------------------------------------------------- /readme_resources/final_look.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SubconsciousCompute/fsfilter-rs/c7bc03168e2eba6d378c9bf73533a4147921d75b/readme_resources/final_look.png -------------------------------------------------------------------------------- /readme_resources/registry.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SubconsciousCompute/fsfilter-rs/c7bc03168e2eba6d378c9bf73533a4147921d75b/readme_resources/registry.png -------------------------------------------------------------------------------- /readme_resources/shared_def.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SubconsciousCompute/fsfilter-rs/c7bc03168e2eba6d378c9bf73533a4147921d75b/readme_resources/shared_def.png -------------------------------------------------------------------------------- /readme_resources/symbol_search_path.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SubconsciousCompute/fsfilter-rs/c7bc03168e2eba6d378c9bf73533a4147921d75b/readme_resources/symbol_search_path.png -------------------------------------------------------------------------------- /readme_resources/virtualbox_download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SubconsciousCompute/fsfilter-rs/c7bc03168e2eba6d378c9bf73533a4147921d75b/readme_resources/virtualbox_download.png -------------------------------------------------------------------------------- /readme_resources/vm_setup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SubconsciousCompute/fsfilter-rs/c7bc03168e2eba6d378c9bf73533a4147921d75b/readme_resources/vm_setup.png -------------------------------------------------------------------------------- /readme_resources/windows_additions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SubconsciousCompute/fsfilter-rs/c7bc03168e2eba6d378c9bf73533a4147921d75b/readme_resources/windows_additions.png -------------------------------------------------------------------------------- /src/bin/minifilter.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused_must_use)] 2 | use fsfilter_rs::driver_comm; 3 | use fsfilter_rs::shared_def::{CDriverMsgs, IOMessage}; 4 | use std::io::Write; 5 | use std::sync::mpsc::channel; 6 | use std::time::Duration; 7 | use std::{io, thread}; 8 | 9 | fn main() { 10 | let driver = driver_comm::Driver::open_kernel_driver_com() 11 | .expect("Cannot open driver communication (is the mini-filter started?)"); 12 | driver 13 | .driver_set_app_pid() 14 | .expect("Cannot set driver app pid"); 15 | let mut vecnew: Vec = Vec::with_capacity(65536); 16 | 17 | let (tx_iomsgs, rx_iomsgs) = channel::(); 18 | 19 | thread::spawn(move || loop { 20 | if let Some(reply_irp) = driver.get_irp(&mut vecnew) { 21 | if reply_irp.num_ops > 0 { 22 | let drivermsgs = CDriverMsgs::new(&reply_irp); 23 | for drivermsg in drivermsgs { 24 | let iomsg = IOMessage::from(&drivermsg); 25 | if tx_iomsgs.send(iomsg).is_ok() { 26 | } else { 27 | panic!("Cannot send iomsg"); 28 | } 29 | } 30 | } else { 31 | // Don't use "continue" as jump statements are expensive "if reply_irp.num_ops > 0" 32 | // continue; 33 | thread::sleep(Duration::from_millis(2)); 34 | } 35 | } else { 36 | panic!("Can't receive Driver Message?"); 37 | } 38 | }); 39 | 40 | { 41 | let mut lock = io::stdout().lock(); 42 | loop { 43 | if let Ok(mut io_message) = rx_iomsgs.recv() { 44 | io_message.exepath(); 45 | write!(lock, "{io_message:?}"); 46 | lock.flush(); 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/driver_comm.rs: -------------------------------------------------------------------------------- 1 | //! Low-level communication with the minifilter. 2 | 3 | use std::mem; 4 | use std::os::raw::{c_ulong, c_ulonglong}; 5 | use std::ptr; 6 | 7 | use sysinfo::{get_current_pid, Pid, PidExt}; 8 | use wchar::wchar_t; 9 | use widestring::U16CString; 10 | use windows::core::{HRESULT, PCSTR, PCWSTR}; 11 | use windows::Win32::Foundation::{CloseHandle, HANDLE}; 12 | use windows::Win32::Storage::FileSystem::GetDriveTypeA; 13 | use windows::Win32::Storage::InstallableFileSystems::{ 14 | FilterConnectCommunicationPort, FilterSendMessage, 15 | }; 16 | 17 | use crate::driver_comm::DriveType::{ 18 | DriveCDRom, DriveFixed, DriveNoRootDir, DriveRamDisk, DriveRemote, DriveRemovable, DriveUnknown, 19 | }; 20 | use crate::driver_comm::IrpMajorOp::{IrpCreate, IrpNone, IrpRead, IrpSetInfo, IrpWrite}; 21 | use crate::shared_def; 22 | use crate::shared_def::ReplyIrp; 23 | 24 | type BufPath = [wchar_t; 520]; 25 | 26 | /// The user-mode app (this app) can send several messages types to the driver. See [`DriverComMessageType`] 27 | /// for details. 28 | /// Depending on the message type, the *pid*, *gid* and *path* fields can be optional. 29 | #[derive(Debug)] 30 | #[repr(C)] 31 | struct DriverComMessage { 32 | /// The type message to send. See [`DriverComMessageType`]. 33 | r#type: c_ulong, 34 | /// The pid of the process which triggered an i/o activity; 35 | pid: c_ulong, 36 | /// The gid is maintained by the driver 37 | gid: c_ulonglong, 38 | path: BufPath, 39 | } 40 | 41 | /// Messages types to send directives to the minifilter, by using te [`DriverComMessage`] struct. 42 | #[repr(C)] 43 | #[allow(dead_code)] 44 | enum DriverComMessageType { 45 | /// Not used yet. The minifilter has the ability to monitor a specific part of the fs. 46 | AddScanDirectory, 47 | /// Not used yet. The minifilter has the ability to monitor a specific part of the fs. 48 | RemScanDirectory, 49 | /// Ask for a [`ReplyIrp`], if any available. 50 | GetOps, 51 | /// Set this app pid to the minifilter (related IRPs will be ignored); 52 | SetPid, 53 | /// Instruct the minifilter to kill all pids in the family designated by a given gid. 54 | KillGid, 55 | } 56 | 57 | /// A minifilter is identified by a port (know in advance), like a named pipe used for communication, 58 | /// and a handle, retrieved by [`open_kernel_driver_com`](Self::open_kernel_driver_com). 59 | #[derive(Debug)] 60 | #[repr(C)] 61 | pub struct Driver { 62 | handle: HANDLE, 63 | } 64 | 65 | impl Driver { 66 | /// Can be used to properly close the communication (and unregister) with the minifilter. 67 | /// If this fn is not used and the program has stopped, the handle is automatically closed, 68 | /// seemingly without any side-effects. 69 | #[inline] 70 | pub fn close_kernel_communication(&self) -> bool { 71 | unsafe { CloseHandle(self.handle).as_bool() } 72 | } 73 | 74 | /// The user-mode running app (this one) has to register itself to the driver. 75 | /// 76 | /// # Panics 77 | /// This fn panics if it is unable to get the current pid. 78 | /// 79 | /// # Errors 80 | /// This fn returns an error if it is unable to register itself to the minifilter. 81 | #[inline] 82 | pub fn driver_set_app_pid(&self) -> Result<(), windows::core::Error> { 83 | let buf = Self::string_to_commessage_buffer(r"\Device\harddiskVolume"); 84 | 85 | let mut get_irp_msg: DriverComMessage = DriverComMessage { 86 | r#type: DriverComMessageType::SetPid as c_ulong, 87 | pid: get_current_pid().unwrap().as_u32() as c_ulong, 88 | gid: 140_713_315_094_899, 89 | path: buf, //wch!("\0"), 90 | }; 91 | let mut tmp: u32 = 0; 92 | 93 | unsafe { 94 | FilterSendMessage( 95 | self.handle, 96 | ptr::addr_of_mut!(get_irp_msg).cast::(), 97 | mem::size_of::() as c_ulong, 98 | None, 99 | 0, 100 | std::ptr::addr_of_mut!(tmp), 101 | ) 102 | } 103 | } 104 | 105 | /// Try to open a com canal with the minifilter before this app is registered. 106 | /// 107 | /// # Panics 108 | /// This function will panic if the minifilter port has any nul value (except the last one) 109 | /// in it's name. 110 | /// 111 | /// # Errors 112 | /// This fn can fail is the minifilter is unreachable: 113 | /// 114 | /// * if it is not started (try `sc start snFilter` first 115 | /// * if a connection is already established: it can accepts only one at a time. 116 | /// 117 | /// In that case the Error is raised by the OS (`windows::Error`) and is generally readable. 118 | #[inline] 119 | pub fn open_kernel_driver_com() -> Result { 120 | let com_port_name = U16CString::from_str("\\snFilter").unwrap().into_raw(); 121 | let handle; 122 | unsafe { 123 | handle = FilterConnectCommunicationPort(PCWSTR(com_port_name), 0, None, 0, None)?; 124 | } 125 | let res = Self { handle }; 126 | Ok(res) 127 | } 128 | 129 | /// Ask the driver for a [`ReplyIrp`], if any. This is a low-level function and the returned object 130 | /// uses C pointers. Managing C pointers requires a special care, because of the Rust timelines. 131 | /// [`ReplyIrp`] is optional since the minifilter returns null if there is no new activity. 132 | /// 133 | /// # Panics 134 | /// This fn panics if it is unable to get the current pid or cannot get driver message from the 135 | /// minifilter. 136 | #[inline] 137 | pub fn get_irp(&self, vecnew: &mut Vec) -> Option { 138 | let mut get_irp_msg = Self::build_irp_msg( 139 | DriverComMessageType::GetOps, 140 | get_current_pid().unwrap(), 141 | 0, 142 | "", 143 | ); 144 | let mut tmp: u32 = 0; 145 | 146 | unsafe { 147 | FilterSendMessage( 148 | self.handle, 149 | ptr::addr_of_mut!(get_irp_msg).cast::(), 150 | mem::size_of::() as c_ulong, 151 | Option::from(vecnew.as_mut_ptr().cast::()), 152 | 65536_u32, 153 | ptr::addr_of_mut!(tmp).cast::(), 154 | ) 155 | .expect("Cannot get driver message from driver"); 156 | } 157 | 158 | if tmp != 0 { 159 | let reply_irp: ReplyIrp; 160 | unsafe { 161 | reply_irp = 162 | std::ptr::read_unaligned(vecnew.as_ptr().cast::()); 163 | } 164 | return Some(reply_irp); 165 | } 166 | None 167 | } 168 | 169 | /// Ask the minifilter to kill all pids related to the given *gid*. Pids are killed in driver-mode 170 | /// by calls to `NtClose`. 171 | #[inline] 172 | pub fn try_kill(&self, gid: c_ulonglong) -> Result { 173 | let mut killmsg = DriverComMessage { 174 | r#type: DriverComMessageType::KillGid as c_ulong, 175 | pid: 0, //get_current_pid().unwrap() as u32, 176 | gid, 177 | path: [0; 520], 178 | }; 179 | let mut res: i32 = 0; 180 | let mut res_size: u32 = 0; 181 | 182 | unsafe { 183 | FilterSendMessage( 184 | self.handle, 185 | ptr::addr_of_mut!(killmsg).cast::(), 186 | mem::size_of::() as c_ulong, 187 | Option::from(ptr::addr_of_mut!(res).cast::()), 188 | 4_u32, 189 | ptr::addr_of_mut!(res_size).cast::(), 190 | )?; 191 | } 192 | 193 | Ok(HRESULT(res)) 194 | } 195 | 196 | #[inline] 197 | fn string_to_commessage_buffer(bufstr: &str) -> BufPath { 198 | let temp = U16CString::from_str(bufstr).unwrap(); 199 | let mut buf: BufPath = [0; 520]; 200 | for (i, c) in temp.as_slice_with_nul().iter().enumerate() { 201 | buf[i] = *c as wchar_t; 202 | } 203 | buf 204 | } 205 | 206 | // TODO: move to ComMessage? 207 | #[inline] 208 | fn build_irp_msg( 209 | commsgtype: DriverComMessageType, 210 | pid: Pid, 211 | gid: u64, 212 | path: &str, 213 | ) -> DriverComMessage { 214 | DriverComMessage { 215 | r#type: commsgtype as c_ulong, // SetPid 216 | pid: pid.as_u32() as c_ulong, 217 | gid, 218 | path: Self::string_to_commessage_buffer(path), 219 | } 220 | } 221 | } 222 | 223 | /// See [`IOMessage`](crate::shared_def::IOMessage) struct and 224 | /// [this doc](https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/irp-major-function-codes). 225 | #[repr(C)] 226 | pub enum IrpMajorOp { 227 | /// Nothing happened 228 | IrpNone, 229 | /// On read, any time following the successful completion of a create request. 230 | IrpRead, 231 | /// On write, any time following the successful completion of a create request. 232 | IrpWrite, 233 | /// Set Metadata about a file or file handle. In that case, [`FileChangeInfo`](crate::shared_def::FileChangeInfo) indicates 234 | /// the nature of the modification. 235 | IrpSetInfo, 236 | /// Open a handle to a file object or device object. 237 | IrpCreate, 238 | /// File object handle has been closed 239 | IrpCleanUp, 240 | } 241 | 242 | impl IrpMajorOp { 243 | #[inline] 244 | #[must_use] 245 | pub fn from_byte(b: u8) -> Self { 246 | match b { 247 | // 0 => IrpNone, 248 | 1 => IrpRead, 249 | 2 => IrpWrite, 250 | 3 => IrpSetInfo, 251 | 4 | 5 => IrpCreate, 252 | _ => IrpNone, 253 | } 254 | } 255 | } 256 | 257 | /// See [`IOMessage`](crate::shared_def::IOMessage) struct and 258 | /// [this doc](https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getdrivetypea). 259 | #[repr(C)] 260 | pub enum DriveType { 261 | /// The drive type cannot be determined. 262 | DriveUnknown, 263 | /// The root path is invalid; for example, there is no volume mounted at the specified path. 264 | DriveNoRootDir, 265 | /// The drive has removable media; for example, a floppy drive, thumb drive, or flash card reader. 266 | DriveRemovable, 267 | /// The drive has fixed media; for example, a hard disk drive or flash drive. 268 | DriveFixed, 269 | /// The drive is a remote (network) drive. 270 | DriveRemote, 271 | /// The drive is a CD-ROM drive. 272 | DriveCDRom, 273 | /// The drive is a RAM disk. 274 | DriveRamDisk, 275 | } 276 | 277 | impl DriveType { 278 | /// Determines whether a disk drive is a removable, fixed, CD-ROM, RAM disk, or network drive. 279 | /// 280 | /// # Panics 281 | /// Will panic if drive path is invalid. 282 | #[inline] 283 | #[must_use] 284 | pub fn from_filepath(filepath: &str) -> Self { 285 | let mut drive_type = 1u32; 286 | if !filepath.is_empty() { 287 | let drive_path = &filepath[..=filepath.find('\\').unwrap()]; 288 | unsafe { 289 | drive_type = GetDriveTypeA(PCSTR(String::from(drive_path).as_ptr())); 290 | } 291 | } 292 | match drive_type { 293 | 0 => DriveUnknown, 294 | // 1 => DriveNoRootDir, 295 | 2 => DriveRemovable, 296 | 3 => DriveFixed, 297 | 4 => DriveRemote, 298 | 5 => DriveCDRom, 299 | 6 => DriveRamDisk, 300 | _ => DriveNoRootDir, 301 | } 302 | } 303 | } 304 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # minifilter-rs 2 | //! 3 | //! Checkout the [README](https://github.com/SubconsciousCompute/fsfilter-rs/blob/master/README.md) too at github. 4 | //! 5 | //! **Use `cargo doc --no-deps --document-private-items --open` to read Documentation** 6 | //! 7 | //! ## Table of Contents 8 | //! 9 | //!
10 | //! Table of Contents 11 | //! 12 | //! - [Minifilter Driver](https://!github.com/SubconsciousCompute/fsfilter-rs#minifilter-driver) 13 | //! - [Building Driver](https://!github.com/SubconsciousCompute/fsfilter-rs#building-driver) 14 | //! - [Installing Driver](https://!github.com/SubconsciousCompute/fsfilter-rs#building-driver) 15 | //! - [Loading/Removing Driver](https://!github.com/SubconsciousCompute/fsfilter-rs#loadingremoving-driver) 16 | //! - [Rust Application](https://!github.com/SubconsciousCompute/fsfilter-rs#rust-application) 17 | //! - [Building Rust App](https://!github.com/SubconsciousCompute/fsfilter-rs#building-rust-app) 18 | //! - [Running Rust App](https://!github.com/SubconsciousCompute/fsfilter-rs#running-rust-app) 19 | //! - [What and the How](https://!github.com/SubconsciousCompute/fsfilter-rs#what-and-the-how) 20 | //! 21 | //!
22 | //! 23 | //! ## Minifilter Driver 24 | //! 25 | //! ### Building Driver 26 | //! 27 | //! 1. Open `VS 2022` 28 | //! 2. Goto `minifilter-rs -> minifilter -> RWatch.sln` 29 | //! 3. Build solution in `Release` mode with `x64` 30 | //! 31 | //! **NOTE: Enable Loading of Test Signed Drivers by executing `Bcdedit.exe -set TESTSIGNING ON` in administrative cmd** 32 | //! 33 | //! ### Installing Driver 34 | //! 35 | //! 1. Open Powershell or command prompt as Administrator 36 | //! 2. `RUNDLL32.EXE SETUPAPI.DLL,InstallHinfSection DefaultInstall 132 \minifilter-rs\minifilter\x64\Debug\snFilter.inf` 37 | //! 38 | //! You should be able to see the driver at `"C:\Windows\System32\drivers\snFilter.sys"` 39 | //! 40 | //! ### Loading/Removing Driver 41 | //! 42 | //! 1. Open Powershell or command prompt as Administrator 43 | //! 2. Start the driver using `sc start snFilter`, expected output: 44 | //! ```ignore 45 | //! SERVICE_NAME: snFilter 46 | //! TYPE : 2 FILE_SYSTEM_DRIVER 47 | //! STATE : 4 RUNNING 48 | //! (STOPPABLE, NOT_PAUSABLE, IGNORES_SHUTDOWN) 49 | //! WIN32_EXIT_CODE : 0 (0x0) 50 | //! SERVICE_EXIT_CODE : 0 (0x0) 51 | //! CHECKPOINT : 0x0 52 | //! WAIT_HINT : 0x0 53 | //! PID : 0 54 | //! FLAGS : 55 | //! ``` 56 | //! 3. Stop the driver using `sc stop snFilter`, should give the following output: 57 | //! ```ignore 58 | //! SERVICE_NAME: snFilter 59 | //! TYPE : 2 FILE_SYSTEM_DRIVER 60 | //! STATE : 1 STOPPED 61 | //! WIN32_EXIT_CODE : 0 (0x0) 62 | //! SERVICE_EXIT_CODE : 0 (0x0) 63 | //! CHECKPOINT : 0x0 64 | //! WAIT_HINT : 0x0 65 | //! ``` 66 | //! 4. Remove it by `sc delete snFilter`, should give the following output: 67 | //! ```ignore 68 | //! [SC] DeleteService SUCCESS 69 | //! ``` 70 | //! 71 | //! You can also run `Fltmc.exe` to see the currently loaded drivers: 72 | //! 73 | //! ```ignore 74 | //! Filter Name Num Instances Altitude Frame 75 | //! ------------------------------ ------------- ------------ ----- 76 | //! bindflt 1 409800 0 77 | //! snFilter 4 378781 0 //our minifilter driver 78 | //! WdFilter 5 328010 0 79 | //! storqosflt 0 244000 0 80 | //! wcifs 0 189900 0 81 | //! CldFlt 0 180451 0 82 | //! FileCrypt 0 141100 0 83 | //! luafv 1 135000 0 84 | //! npsvctrig 1 46000 0 85 | //! Wof 3 40700 0 86 | //! FileInfo 5 40500 0 87 | //! ``` 88 | //! 89 | //! ## Rust Application 90 | //! 91 | //! ### Building Rust App 92 | //! 93 | //! Simply use `cargo build --release` to build the application 94 | //! 95 | //! ### Running Rust App 96 | //! 97 | //! Use `cargo run --bin minifilter --release` to run the application 98 | //! 99 | //! The program starts to print the `IOMessage` which is defined like: 100 | //! 101 | //! ```ignore 102 | //! #[repr(C)] 103 | //! pub struct IOMessage { 104 | //! pub extension: [wchar_t; 12], 105 | //! pub file_id_vsn: c_ulonglong, 106 | //! pub file_id_id: [u8; 16], 107 | //! pub mem_sized_used: c_ulonglong, 108 | //! pub entropy: f64, 109 | //! pub pid: c_ulong, 110 | //! pub irp_op: c_uchar, 111 | //! pub is_entropy_calc: u8, 112 | //! pub file_change: c_uchar, 113 | //! pub file_location_info: c_uchar, 114 | //! pub filepathstr: String, 115 | //! pub gid: c_ulonglong, 116 | //! pub runtime_features: RuntimeFeatures, 117 | //! pub file_size: i64, 118 | //! } 119 | //! ``` 120 | //! 121 | //! We end the process using `ctrl + c` in the example video: 122 | //! ![video](https://!github.com/SubconsciousCompute/fsfilter-rs/readme_resources/example.gif) 123 | //! 124 | //! #### NOTE: 125 | //! 126 | //! - Might fail if not ran with administrative privileges 127 | //! - You need to [load and start the driver]((https://!github.com/SubconsciousCompute/fsfilter-rs#loadingremoving-driver)) before running 128 | //! the program or else it will error out 129 | //! 130 | //! ## What and the How 131 | //! 132 | //! We basically share definition between the mini-filter and Rust using `#[repr(C)]` 133 | //! 134 | //! ![`shared_def`](https://!github.com/SubconsciousCompute/fsfilter-rs/readme_resources/shared_def.png) 135 | //! 136 | //! We use [channels](https://!doc.rust-lang.org/std/sync/mpsc/fn.channel.html) to process 137 | //! all [IRPs](https://!docs.microsoft.com/en-us/windows-hardware/drivers/ifs/irps-are-different-from-fast-i-o). 138 | 139 | pub mod driver_comm; 140 | pub mod shared_def; 141 | -------------------------------------------------------------------------------- /src/shared_def.rs: -------------------------------------------------------------------------------- 1 | //! Contains all definitions shared between this user-mode app and the minifilter in order to 2 | //! communicate properly. Those are C-representation of structures sent or received from the minifilter. 3 | 4 | use std::cmp::Ordering; 5 | use std::fmt; 6 | use std::os::raw::{c_uchar, c_ulong, c_ulonglong, c_ushort}; 7 | use std::path::PathBuf; 8 | use std::time::SystemTime; 9 | 10 | use num_derive::FromPrimitive; 11 | use serde::{Deserialize, Serialize}; 12 | use wchar::wchar_t; 13 | use windows::Win32::Foundation::{CloseHandle, GetLastError}; 14 | use windows::Win32::Storage::FileSystem::FILE_ID_INFO; 15 | use windows::Win32::System::ProcessStatus::K32GetProcessImageFileNameA; 16 | use windows::Win32::System::Threading::{OpenProcess, PROCESS_QUERY_INFORMATION, PROCESS_VM_READ}; 17 | 18 | /// See [`IOMessage`] struct. Used with [`IrpSetInfo`](crate::driver_comm::IrpMajorOp::IrpSetInfo) 19 | #[derive(FromPrimitive)] 20 | #[repr(C)] 21 | pub enum FileChangeInfo { 22 | FileChangeNotSet, 23 | FileOpenDirectory, 24 | FileChangeWrite, 25 | FileChangeNewFile, 26 | FileChangeRenameFile, 27 | FileChangeExtensionChanged, 28 | FileChangeDeleteFile, 29 | /// Temp file: created and deleted on close 30 | FileChangeDeleteNewFile, 31 | FileChangeOverwriteFile, 32 | } 33 | 34 | /// See [`IOMessage`] struct. 35 | #[derive(FromPrimitive)] 36 | #[repr(C)] 37 | pub enum FileLocationInfo { 38 | FileNotProtected, 39 | FileProtected, 40 | FileMovedIn, 41 | FileMovedOut, 42 | } 43 | 44 | /// Low-level C-like object to communicate with the minifilter. 45 | /// The minifilter yields `ReplyIrp` objects (retrieved by [`get_irp`](crate::driver_comm::Driver::get_irp) to 46 | /// manage the fixed size of the *data buffer. 47 | /// In other words, a `ReplyIrp` is a collection of [`CDriverMsg`] with a capped size. 48 | #[derive(Debug, Copy, Clone)] 49 | #[repr(C)] 50 | pub struct ReplyIrp { 51 | /// The size od the collection. 52 | pub data_size: c_ulonglong, 53 | /// The C pointer to the buffer containing the [`CDriverMsg`] events. 54 | pub data: *const CDriverMsg, 55 | /// The number of different operations in this collection. 56 | pub num_ops: u64, 57 | } 58 | 59 | impl ReplyIrp { 60 | /// Iterate through `self.data` and returns the collection of [`CDriverMsg`] 61 | #[inline] 62 | fn unpack_drivermsg(&self) -> Vec<&CDriverMsg> { 63 | let mut res = vec![]; 64 | unsafe { 65 | let mut msg = &*self.data; 66 | res.push(msg); 67 | for _ in 0..(self.num_ops) { 68 | if msg.next.is_null() { 69 | break; 70 | } 71 | msg = &*msg.next; 72 | res.push(msg); 73 | } 74 | } 75 | res 76 | } 77 | } 78 | 79 | /// This class is the straight Rust translation of the Win32 API 80 | /// [`UNICODE_STRING`](https://docs.microsoft.com/en-us/windows/win32/api/ntdef/ns-ntdef-_unicode_string), 81 | /// returned by the driver. 82 | #[derive(Debug, Copy, Clone)] 83 | #[repr(C)] 84 | pub struct UnicodeString { 85 | pub length: c_ushort, 86 | pub maximum_length: c_ushort, 87 | pub buffer: *const wchar_t, 88 | } 89 | 90 | impl UnicodeString { 91 | /* 92 | pub fn to_string(&self) -> String { 93 | unsafe { 94 | let str_slice = std::slice::from_raw_parts(self.buffer, self.length as usize); 95 | let mut first_zero_index = 0; 96 | for (i, c) in str_slice.iter().enumerate() { 97 | if *c == 0 { 98 | first_zero_index = i; 99 | break; 100 | } 101 | } 102 | String::from_utf16_lossy(&str_slice[..first_zero_index]) 103 | } 104 | } 105 | */ 106 | 107 | /// Get the file path from the `UnicodeString` path and the extension returned by the driver. 108 | #[inline] 109 | #[must_use] 110 | pub fn to_string_ext(&self, extension: [wchar_t; 12]) -> String { 111 | unsafe { 112 | let str_slice = std::slice::from_raw_parts(self.buffer, self.length as usize); 113 | let mut first_zero_index = 0; 114 | let mut last_dot_index = 0; 115 | let mut first_zero_index_ext = 0; 116 | 117 | // Filepath 118 | for (i, c) in str_slice.iter().enumerate() { 119 | if *c == 46 { 120 | last_dot_index = i + 1; 121 | } 122 | if *c == 0 { 123 | first_zero_index = i; 124 | break; 125 | } 126 | } 127 | 128 | if first_zero_index_ext > 0 && last_dot_index > 0 { 129 | // Extension 130 | for (i, c) in extension.iter().enumerate() { 131 | if *c == 0 { 132 | first_zero_index_ext = i; 133 | break; 134 | } else if *c != str_slice[last_dot_index + i] { 135 | first_zero_index_ext = 0; 136 | break; 137 | } 138 | } 139 | String::from_utf16_lossy( 140 | &[ 141 | &str_slice[..last_dot_index], 142 | &extension[..first_zero_index_ext], 143 | ] 144 | .concat(), 145 | ) 146 | } else { 147 | String::from_utf16_lossy(&str_slice[..first_zero_index]) 148 | } 149 | } 150 | } 151 | } 152 | 153 | impl fmt::Display for UnicodeString { 154 | #[inline] 155 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 156 | unsafe { 157 | let str_slice = std::slice::from_raw_parts(self.buffer, self.length as usize); 158 | let mut first_zero_index = 0; 159 | for (i, c) in str_slice.iter().enumerate() { 160 | if *c == 0 { 161 | first_zero_index = i; 162 | break; 163 | } 164 | } 165 | write!( 166 | f, 167 | "{}", 168 | String::from_utf16_lossy(&str_slice[..first_zero_index]) 169 | ) 170 | } 171 | } 172 | } 173 | 174 | /// Represents a driver message. 175 | #[derive(Debug, Clone, Serialize, Deserialize)] 176 | #[repr(C)] 177 | pub struct IOMessage { 178 | /// The file extension 179 | pub extension: [wchar_t; 12], 180 | /// Hard Disk Volume Serial Number where the file is saved (from [`FILE_ID_INFO`]) 181 | pub file_id_vsn: c_ulonglong, 182 | /// File ID on the disk ([`FILE_ID_INFO`]) 183 | pub file_id_id: [u8; 16], 184 | /// Number of bytes transferred (`IO_STATUS_BLOCK.Information`) 185 | pub mem_sized_used: c_ulonglong, 186 | /// (Optional) File Entropy calculated by the driver 187 | pub entropy: f64, 188 | /// Pid responsible for this io activity 189 | pub pid: c_ulong, 190 | /// Windows IRP Type caught by the minifilter: 191 | /// - NONE (0) 192 | /// - READ (1) 193 | /// - WRITE (2) 194 | /// - SETINFO (3) 195 | /// - CREATE (4) 196 | /// - CLEANUP (5) 197 | pub irp_op: c_uchar, 198 | /// Is the entropy calculated? 199 | pub is_entropy_calc: u8, 200 | /// Type of i/o operation: 201 | /// - FILE_CHANGE_NOT_SET (0) 202 | /// - FILE_OPEN_DIRECTORY (1) 203 | /// - FILE_CHANGE_WRITE (2) 204 | /// - FILE_CHANGE_NEW_FILE (3) 205 | /// - FILE_CHANGE_RENAME_FILE (4) 206 | /// - FILE_CHANGE_EXTENSION_CHANGED (5) 207 | /// - FILE_CHANGE_DELETE_FILE (6) 208 | /// - FILE_CHANGE_DELETE_NEW_FILE (7) 209 | /// - FILE_CHANGE_OVERWRITE_FILE (8) 210 | pub file_change: c_uchar, 211 | /// The driver has the ability to monitor specific directories only (feature currently not used): 212 | /// - FILE_NOT_PROTECTED (0): Monitored dirs do not contained this file 213 | /// - FILE_PROTECTED (1) 214 | /// - FILE_MOVED_IN (2) 215 | /// - FILE_MOVED_OUT (3) 216 | pub file_location_info: c_uchar, 217 | /// File path on the disk 218 | pub filepathstr: String, 219 | /// Group Identifier (maintained by the minifilter) of the operation 220 | pub gid: c_ulonglong, 221 | /// see class [`RuntimeFeatures`] 222 | pub runtime_features: RuntimeFeatures, 223 | /// Size of the file. Can be equal to -1 if the file path is not found. 224 | pub file_size: i64, 225 | /// Rough time at which the IRP was created 226 | pub time: SystemTime, 227 | } 228 | 229 | impl IOMessage { 230 | /// Make a new [`IOMessage`] from a received [`CDriverMsg`] 231 | #[inline] 232 | #[must_use] 233 | pub fn from(c_drivermsg: &CDriverMsg) -> Self { 234 | Self { 235 | extension: c_drivermsg.extension, 236 | file_id_vsn: c_drivermsg.file_id.VolumeSerialNumber, 237 | file_id_id: c_drivermsg.file_id.FileId.Identifier, 238 | mem_sized_used: c_drivermsg.mem_sized_used, 239 | entropy: c_drivermsg.entropy, 240 | pid: c_drivermsg.pid, 241 | irp_op: c_drivermsg.irp_op, 242 | is_entropy_calc: c_drivermsg.is_entropy_calc, 243 | file_change: c_drivermsg.file_change, 244 | file_location_info: c_drivermsg.file_location_info, 245 | filepathstr: c_drivermsg.filepath.to_string_ext(c_drivermsg.extension), 246 | gid: c_drivermsg.gid, 247 | runtime_features: RuntimeFeatures::new(), 248 | file_size: match PathBuf::from( 249 | &c_drivermsg.filepath.to_string_ext(c_drivermsg.extension), 250 | ) 251 | .metadata() 252 | { 253 | Ok(f) => f.len() as i64, 254 | Err(_e) => -1, 255 | }, 256 | time: SystemTime::now(), 257 | } 258 | } 259 | 260 | /// Opens an existing local process object to retrieve the name of the executable file for the 261 | /// specified process. 262 | #[inline] 263 | pub fn exepath(&mut self) { 264 | let pid = self.pid; 265 | unsafe { 266 | let r_handle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, pid); 267 | if let Ok(handle) = r_handle { 268 | if !(handle.is_invalid() || handle.0 == 0) { 269 | let mut buffer: Vec = Vec::new(); 270 | buffer.resize(1024, 0); 271 | let res = K32GetProcessImageFileNameA(handle, buffer.as_mut_slice()); 272 | 273 | CloseHandle(handle); 274 | if res == 0 { 275 | let _errorcode = GetLastError().0; 276 | } else { 277 | let pathbuf = PathBuf::from( 278 | String::from_utf8_unchecked(buffer).trim_matches(char::from(0)), 279 | ); 280 | self.runtime_features.exe_still_exists = true; 281 | self.runtime_features.exepath = pathbuf.file_name().map_or_else( 282 | || PathBuf::from(r"DEFAULT"), 283 | |filename| PathBuf::from(filename.to_string_lossy().to_string()), 284 | ); 285 | } 286 | // dbg!(is_closed_handle); 287 | } 288 | } 289 | } 290 | } 291 | } 292 | 293 | impl Eq for IOMessage {} 294 | 295 | impl Ord for IOMessage { 296 | fn cmp(&self, other: &Self) -> Ordering { 297 | self.time.cmp(&other.time) 298 | } 299 | } 300 | 301 | impl PartialOrd for IOMessage { 302 | fn partial_cmp(&self, other: &Self) -> Option { 303 | Some(self.time.cmp(&other.time)) 304 | } 305 | } 306 | 307 | impl PartialEq for IOMessage { 308 | fn eq(&self, other: &Self) -> bool { 309 | self.time == other.time 310 | } 311 | } 312 | 313 | /// Stores runtime features that come from our application (and not the minifilter). 314 | #[derive(Debug, Clone, Serialize, Deserialize)] 315 | #[repr(C)] 316 | pub struct RuntimeFeatures { 317 | /// The path of the gid root process 318 | pub exepath: PathBuf, 319 | /// Did the root exe file still existed (at the moment of this specific *DriverMessage* operation)? 320 | pub exe_still_exists: bool, 321 | } 322 | 323 | impl RuntimeFeatures { 324 | /// Make a new [`RuntimeFeatures`] 325 | #[inline] 326 | #[must_use] 327 | pub fn new() -> Self { 328 | Self { 329 | exepath: PathBuf::new(), 330 | exe_still_exists: true, 331 | } 332 | } 333 | } 334 | 335 | impl Default for RuntimeFeatures { 336 | #[inline] 337 | fn default() -> Self { 338 | Self::new() 339 | } 340 | } 341 | 342 | /// The C object returned by the minifilter, available through [`ReplyIrp`]. 343 | /// It is low level and use C pointers logic which is not always compatible with RUST (in particular 344 | /// the lifetime of `*next`). That's why we convert it asap to a plain Rust [`IOMessage`] object. 345 | /// 346 | /// `next` is null `(0x0)` when there is no [`IOMessage`] remaining. 347 | #[derive(Debug, Copy, Clone)] 348 | #[repr(C)] 349 | pub struct CDriverMsg { 350 | pub extension: [wchar_t; 12], 351 | pub file_id: FILE_ID_INFO, 352 | pub mem_sized_used: c_ulonglong, 353 | pub entropy: f64, 354 | pub pid: c_ulong, 355 | pub irp_op: c_uchar, 356 | pub is_entropy_calc: u8, 357 | pub file_change: c_uchar, 358 | pub file_location_info: c_uchar, 359 | pub filepath: UnicodeString, 360 | pub gid: c_ulonglong, 361 | /// null (0x0) when there is no [`IOMessage`] remaining 362 | pub next: *const CDriverMsg, 363 | } 364 | 365 | /// To iterate easily over a collection of [`IOMessage`] received from the minifilter, before they are 366 | /// converted to [`IOMessage`]. 367 | #[repr(C)] 368 | pub struct CDriverMsgs<'a> { 369 | drivermsgs: Vec<&'a CDriverMsg>, 370 | index: usize, 371 | } 372 | 373 | impl CDriverMsgs<'_> { 374 | /// Make a new [`CDriverMsgs`] from a received [`ReplyIrp`] 375 | #[inline] 376 | #[must_use] 377 | pub fn new(irp: &ReplyIrp) -> CDriverMsgs { 378 | CDriverMsgs { 379 | drivermsgs: irp.unpack_drivermsg(), 380 | index: 0, 381 | } 382 | } 383 | } 384 | 385 | impl Iterator for CDriverMsgs<'_> { 386 | type Item = CDriverMsg; 387 | 388 | #[inline] 389 | fn next(&mut self) -> Option { 390 | if self.index == self.drivermsgs.len() { 391 | None 392 | } else { 393 | let res = *self.drivermsgs[self.index]; 394 | self.index += 1; 395 | Some(res) 396 | } 397 | } 398 | } 399 | --------------------------------------------------------------------------------