├── LICENSE ├── README.md ├── badgirl.inf └── driver.c /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Hanson 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Prevent_File_Deletion 2 | Record & prevent file deletion in kernel mode 3 | 4 | Huge thanks to https://0x00sec.org/t/kernel-mode-rootkits-file-deletion-protection/7616 5 | 6 | This guy has some awesome things! Thanks to the tutorials! https://github.com/NtRaiseHardError 7 | 8 | # Study Notes 9 | 10 | ## What to do in `DriverEntry` 11 | 1. Use `FltRegisterFilter` to register a minifilter. 12 | 2. We can use `0x%08x` format specifier in `DbgPrint` to print error codes. 13 | 3. After `FltRegisterFilter`, use `FltStartFiltering` to start the minifilter. 14 | 4. If `FltStartFiltering` fails, we should unregister the minifilter by calling `FltUnregisterFilter`. 15 | 16 | Now here comes the tough things... 17 | 18 | ## `FltRegisterFilter` function 19 | For `FltRegisterFilter` function, we need three parameters: 20 | 21 | (Reference: https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/fltkernel/nf-fltkernel-fltregisterfilter) 22 | 23 | The code should be like this: 24 | 25 | ```c 26 | PFLT_FILTER Filter; 27 | status = FltRegisterFilter(DriverObject, &FilterRegistration, &Filter); 28 | ``` 29 | 30 | The `Registration` parameter can be a little complicated. Other parameters are straightforward. The type of `Registration` is [FLT_REGISTRATION](https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/fltkernel/ns-fltkernel-_flt_registration), which will be something like this: 31 | 32 | (Reference: https://github.com/NtRaiseHardError/Anti-Delete/blob/9fbe5ac0e46ba7a70b9e9983977d1bd38a3999a0/Anti%20Delete/Anti%20Delete/Driver.c#L24) 33 | 34 | ```c 35 | const FLT_REGISTRATION FilterRegistration = { 36 | sizeof(FLT_REGISTRATION), // Size 37 | FLT_REGISTRATION_VERSION, // Version 38 | 0, // Flags 39 | NULL, // ContextRegistration 40 | Callbacks, // OperationRegistration 41 | badgirlFilterUnload, // FilterUnloadCallback 42 | NULL, // InstanceSetupCallback 43 | NULL, // InstanceQueryTeardownCallback 44 | NULL, // InstanceTeardownStartCallback 45 | NULL, // InstanceTeardownCompleteCallback 46 | NULL, // GenerateFileNameCallback 47 | NULL, // NormalizeNameComponentCallback 48 | NULL, // NormalizeContextCleanupCallback 49 | NULL, // TransactionNotificationCallback 50 | NULL, // NormalizeNameComponentExCallback 51 | NULL // SectionNotificationCallback 52 | }; 53 | ``` 54 | Note that we need to specify `OperationRegistration` and `FilterUnloadCallback` in this demo. I don't know what do other callbacks do so far. 55 | 56 | Pay attention to `OperationRegistration`. Note that `Callbacks` should be `FLT_OPERATION_REGISTRATION`, which is an array. It binds major function and callbacks. It should be like this: 57 | 58 | (Reference: https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/fltkernel/ns-fltkernel-_flt_operation_registration) 59 | 60 | ```c 61 | const FLT_OPERATION_REGISTRATION Callbacks[] = { 62 | { IRP_MJ_CREATE, 0, badgirlFilterAntiDelete, NULL, NULL }, 63 | { IRP_MJ_SET_INFORMATION, 0, badgirlFilterAntiDelete, NULL, NULL }, 64 | 65 | ... 66 | 67 | { IRP_MJ_OPERATION_END } 68 | }; 69 | ``` 70 | 71 | - The 1st element specifies MajorFunction of the operation. 72 | - The 2nd element is Flags, I don't know what's that, even after reading the documentation. Leave that with 0. 73 | - The 3rd element is PreOperation, which is the callback function that will be called before an operation. 74 | - The 4th element is PostOperation, which is the callback function that will be called after an operation. In this demo, we don't need PostOperation callbacks, so we set them to NULLs. 75 | - The 5th element is reserved. Set that to NULL. 76 | - The last element in the array must be `{ IRP_MJ_OPERATION_END }`. 77 | 78 | So... That's what you need to do to call `FltRegisterFilter`. 79 | 80 | ## Registry 81 | When `FltRegisterFilter` returns STATUS_OBJECT_NAME_NOT_FOUND (0xc0000034), it usually indicates there's something wrong with the registry. (Reference: https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/fltkernel/nf-fltkernel-fltregisterfilter#return-value) After searching for this problem, I found the reason. It is because I am too lazy and I didn't create an INF file for this driver. So the minifilter is not recognized by the system. To solve this, we need to write an appropriate INF file. (Reference: https://stackoverflow.com/questions/42389211/fltregisterfilter-not-working) The INF file for a minifilter is different from INF file for other drivers. (https://docs.microsoft.com/en-us/windows-hardware/drivers/ifs/creating-an-inf-file-for-a-minifilter-driver) There are several things to notice. 82 | 83 | ### Service name 84 | The service name that the driver loader passes to `CreateService` should be the same as the `ServiceName` specified in the INF file. Otherwise, the system won't recognize the minifilter. 85 | 86 | ### INF file 87 | 1. You need to specify `CatalogFile` to `your_driver_name.cat`, even though the documentation (https://docs.microsoft.com/en-us/windows-hardware/drivers/ifs/creating-an-inf-file-for-a-minifilter-driver?redirectedfrom=MSDN#version-section-required) says minifilter drivers except antivirus minifilter drivers should leave this entry blank (This demo is not an antivirus minifilter (320000 < Altitude < 329999), but an activity monitor minifilter (Altitude = 365000)). I don't know why, but my solution refuses to compile unless I specify the value of `CatalogFile`. 88 | 2. Change `Class` and `ClassGuid` according to your purpose. (https://docs.microsoft.com/en-us/windows-hardware/drivers/ifs/file-system-filter-driver-classes-and-class-guids) 89 | 3. Change `Instance1.Altitude` to an appropriate value according to your purpose. (https://docs.microsoft.com/en-us/windows-hardware/drivers/ifs/load-order-groups-and-altitudes-for-minifilter-drivers) 90 | 4. An example INF file: https://github.com/Microsoft/Windows-driver-samples/blob/master/filesys/miniFilter/NameChanger/NameChanger.inf 91 | 5. If the solution fails to build and says `Section xxx should have an architecture decoration`, go to project properties - Driver Settings - General - Set "Target Platform" to "Desktop". (https://github.com/microsoft/Windows-driver-samples/issues/366) This may be a better solution, but I havn't tried that: https://community.osr.com/discussion/291286/wdk10-stampinf-returns-error-1420-defaultinstall-based-inf-cannot-be-processed-as-primitive 92 | 93 | ## Linking 94 | If the compiler says "unresolved external symbol _Flt*", you need to link fltMgr.lib. Go to project properties - Linker - Input, edit "Additional Dependencies" and add `$(DDK_LIB_PATH)fltMgr.lib`. 95 | 96 | ## IRQL 97 | I don't have much concept about IRQL. But I noticed that every minifilter callback function (like PreOperation, FilterUnloadCallback) has `PAGED_CODE();` in its body. This is related to IRQL, but I still need to understand the concept. 98 | 99 | ## Misc 100 | 1. `FalgOn(a, b)` macro can be used to determine if a includes b. (i.e. a & b != 0 indicates a includes b) 101 | 2. We can use `FltGetFileNameInformation` and `FltParseFileNameInformation` to get file name information from `FltObjects`. 102 | 3. `Data->Thread` can be used to identify the process that's requesting the operation. 103 | 4. To prevent an operation: 104 | ```c 105 | Data->IoStatus.Status = STATUS_ACCESS_DENIED; 106 | Data->IoStatus.Information = 0; 107 | ret = FLT_PREOP_COMPLETE; 108 | ``` 109 | 110 | # UPDATE: Prevent file modification 111 | To prevent file modification, we can check flags in `Data->Iopb->Parameters.Create.SecurityContext->DesiredAccess`. The following code will prevent rename, delete, modify and property change (Not strictly tested though). If we want to prevent the file from being read, we can deny the access when related `DesiredAccess` flag is detected. 112 | 113 | ```c 114 | if (FlagOn(Data->Iopb->Parameters.Create.SecurityContext->DesiredAccess, FILE_WRITE_DATA) || 115 | FlagOn(Data->Iopb->Parameters.Create.SecurityContext->DesiredAccess, FILE_WRITE_ATTRIBUTES) || 116 | FlagOn(Data->Iopb->Parameters.Create.SecurityContext->DesiredAccess, FILE_WRITE_EA) || 117 | FlagOn(Data->Iopb->Parameters.Create.SecurityContext->DesiredAccess, FILE_APPEND_DATA) || 118 | FlagOn(Data->Iopb->Parameters.Create.SecurityContext->DesiredAccess, DELETE) || 119 | FlagOn(Data->Iopb->Parameters.Create.SecurityContext->DesiredAccess, WRITE_DAC) || 120 | FlagOn(Data->Iopb->Parameters.Create.SecurityContext->DesiredAccess, WRITE_OWNER) || 121 | FlagOn(Data->Iopb->Parameters.Create.SecurityContext->DesiredAccess, GENERIC_WRITE)) { 122 | 123 | Data->IoStatus.Status = STATUS_ACCESS_DENIED; 124 | Data->IoStatus.Information = 0; 125 | ret = FLT_PREOP_COMPLETE; 126 | } 127 | ``` 128 | 129 | # UPDATE: Find out which process requested the operation 130 | To find out which process requested the operation, we can use `Data->Thread`. 131 | ```c 132 | PEPROCESS TargetProcess = IoThreadToProcess(Data->Thread); 133 | HANDLE Pid = PsGetProcessId(TargetProcess); 134 | PUNICODE_STRING ProcessName = NULL; 135 | SeLocateProcessImageName(TargetProcess, &ProcessName); 136 | ``` 137 | 138 | # What to do next 139 | 1. Currently, loading the driver will return an error code that indicates `An instance of the service is already running`. I don't know why, because there is no an intance of the service is already running. 140 | 2. Loading the driver for a second time will fail to register the minifilter, I don't know why. (Update: I found that reinstall the service will solve the problem, but still don't know why). 141 | 3. I will need to figure out how to prevent file creation and modification. (Solved) 142 | 4. I found that the minifilter will be loaded even if I whatever path I specified in the driver loader. This is weird! Maybe it's because the system puts the newly installed minifilter driver into a "pending to load" list, and it will be loaded as soon as some program loads other drivers? I don't know. 143 | 5. HRSword can delete/rename protected files without the specific `DesiredAccess`. Maybe it is because it simply passes the file handle to the kernel-mode driver and let the driver to finish the job? I am not sure. 144 | -------------------------------------------------------------------------------- /badgirl.inf: -------------------------------------------------------------------------------- 1 | ;;; 2 | ;;; badgirl 3 | ;;; 4 | 5 | [Version] 6 | Signature = "$Windows NT$" 7 | ; TODO - 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 | Class = "ActivityMonitor" 11 | ClassGuid = {b86dff51-a31e-4bac-b3cf-e8cfe75c9fc2} 12 | Provider = %ManufacturerName% 13 | DriverVer = 1/1/2000,1.0.0.0 14 | CatalogFile = badgirl.cat 15 | 16 | [DestinationDirs] 17 | DefaultDestDir = 12 18 | MiniFilter.DriverFiles = 12 ;%windir%\system32\drivers 19 | 20 | ;; 21 | ;; Default install sections 22 | ;; 23 | 24 | [DefaultInstall] 25 | OptionDesc = %ServiceDescription% 26 | CopyFiles = MiniFilter.DriverFiles 27 | 28 | [DefaultInstall.Services] 29 | AddService = %ServiceName%,,MiniFilter.Service 30 | 31 | ;; 32 | ;; Default uninstall sections 33 | ;; 34 | 35 | [DefaultUninstall] 36 | DelFiles = MiniFilter.DriverFiles 37 | 38 | [DefaultUninstall.Services] 39 | DelService = %ServiceName%,0x200 ;Ensure service is stopped before deleting 40 | 41 | ; 42 | ; Services Section 43 | ; 44 | 45 | [MiniFilter.Service] 46 | DisplayName = %ServiceName% 47 | Description = %ServiceDescription% 48 | ServiceBinary = %12%\%DriverName%.sys ;%windir%\system32\drivers\ 49 | Dependencies = "FltMgr" 50 | ServiceType = 2 ;SERVICE_FILE_SYSTEM_DRIVER 51 | StartType = 3 ;SERVICE_DEMAND_START 52 | ErrorControl = 1 ;SERVICE_ERROR_NORMAL 53 | ; TODO - Change the Load Order Group value 54 | ; LoadOrderGroup = "FSFilter Activity Monitor" 55 | LoadOrderGroup = "Badgirl Monitor" 56 | AddReg = MiniFilter.AddRegistry 57 | 58 | ; 59 | ; Registry Modifications 60 | ; 61 | 62 | [MiniFilter.AddRegistry] 63 | HKR,,"DebugFlags",0x00010001 ,0x0 64 | HKR,,"SupportedFeatures",0x00010001,0x3 65 | HKR,"Instances","DefaultInstance",0x00000000,%DefaultInstance% 66 | HKR,"Instances\"%Instance1.Name%,"Altitude",0x00000000,%Instance1.Altitude% 67 | HKR,"Instances\"%Instance1.Name%,"Flags",0x00010001,%Instance1.Flags% 68 | 69 | ; 70 | ; Copy Files 71 | ; 72 | 73 | [MiniFilter.DriverFiles] 74 | %DriverName%.sys 75 | 76 | [SourceDisksFiles] 77 | badgirl.sys = 1,, 78 | 79 | [SourceDisksNames] 80 | 1 = %DiskId1%,,, 81 | 82 | ;; 83 | ;; String Section 84 | ;; 85 | 86 | [Strings] 87 | ; TODO - Add your manufacturer 88 | ManufacturerName = "Microsuck Corporation" 89 | ServiceDescription = "badgirl Mini-Filter Driver" 90 | ServiceName = "badgirl" 91 | DriverName = "badgirl" 92 | DiskId1 = "badgirl Device Installation Disk" 93 | 94 | ;Instances specific information. 95 | DefaultInstance = "badgirl Instance" 96 | Instance1.Name = "badgirl Instance" 97 | ; TODO - Change the altitude value, see https://msdn.microsoft.com/en-us/windows/hardware/drivers/ifs/load-order-groups-and-altitudes-for-minifilter-drivers 98 | Instance1.Altitude = "365000" 99 | Instance1.Flags = 0x0 ; Allow all attachments 100 | -------------------------------------------------------------------------------- /driver.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | /* 5 | ============================================================== 6 | Function prototypes 7 | ============================================================== 8 | */ 9 | 10 | // Driver-related 11 | NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath); 12 | void badgirlDriverUnload(PDRIVER_OBJECT DriverObject); 13 | 14 | // Filter-related 15 | FLT_PREOP_CALLBACK_STATUS badgirlFilterAntiDelete(_Inout_ PFLT_CALLBACK_DATA Data, _In_ PCFLT_RELATED_OBJECTS FltObjects, _Flt_CompletionContext_Outptr_ PVOID* CompletionContext); 16 | NTSTATUS badgirlFilterUnload(FLT_FILTER_UNLOAD_FLAGS Flags); 17 | 18 | /* 19 | ============================================================== 20 | Global variables 21 | ============================================================== 22 | */ 23 | 24 | // https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/fltkernel/ns-fltkernel-_flt_operation_registration 25 | const FLT_OPERATION_REGISTRATION Callbacks[] = { 26 | { IRP_MJ_CREATE, 0, badgirlFilterAntiDelete, NULL, NULL }, 27 | { IRP_MJ_SET_INFORMATION, 0, badgirlFilterAntiDelete, NULL, NULL }, 28 | { IRP_MJ_OPERATION_END } 29 | }; 30 | 31 | // https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/fltkernel/ns-fltkernel-_flt_registration 32 | const FLT_REGISTRATION FilterRegistration = { 33 | sizeof(FLT_REGISTRATION), // Size 34 | FLT_REGISTRATION_VERSION, // Version 35 | 0, // Flags 36 | NULL, // ContextRegistration 37 | Callbacks, // OperationRegistration 38 | badgirlFilterUnload, // FilterUnloadCallback 39 | NULL, // InstanceSetupCallback 40 | NULL, // InstanceQueryTeardownCallback 41 | NULL, // InstanceTeardownStartCallback 42 | NULL, // InstanceTeardownCompleteCallback 43 | NULL, // GenerateFileNameCallback 44 | NULL, // NormalizeNameComponentCallback 45 | NULL, // NormalizeContextCleanupCallback 46 | NULL, // TransactionNotificationCallback 47 | NULL, // NormalizeNameComponentExCallback 48 | NULL // SectionNotificationCallback 49 | }; 50 | 51 | PFLT_FILTER Filter; 52 | 53 | /* 54 | ============================================================== 55 | Function implementations 56 | ============================================================== 57 | */ 58 | 59 | NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath) { 60 | UNREFERENCED_PARAMETER(RegistryPath); 61 | 62 | NTSTATUS status = STATUS_SUCCESS; 63 | 64 | DriverObject->DriverUnload = badgirlDriverUnload; 65 | 66 | DbgPrint("I am a bad bad girl! I am going to do bad bad things!\n"); 67 | 68 | // Regerence: https://0x00sec.org/t/kernel-mode-rootkits-file-deletion-protection/7616 69 | // Register the minifilter with the filter manager 70 | status = FltRegisterFilter(DriverObject, &FilterRegistration, &Filter); 71 | if (!NT_SUCCESS(status)) { 72 | DbgPrint("Failed to register filter! 0x%08x\n", status); 73 | status = STATUS_SUCCESS; // Don't do this in real development! Return with status instead 74 | return status; 75 | } 76 | else { 77 | DbgPrint("Filter registered!\n"); 78 | } 79 | 80 | status = FltStartFiltering(Filter); 81 | if (!NT_SUCCESS(status)) { 82 | // If we fail, we need to unregister the minifilter 83 | FltUnregisterFilter(Filter); 84 | 85 | DbgPrint("Failed to start filter! 0x%08x\n", status); 86 | status = STATUS_SUCCESS; // Don't do this in real development! Return with status instead 87 | return status; 88 | } 89 | else { 90 | DbgPrint("Filter started!\n"); 91 | status = STATUS_SUCCESS; 92 | } 93 | 94 | return status; 95 | } 96 | 97 | FLT_PREOP_CALLBACK_STATUS badgirlFilterAntiDelete(_Inout_ PFLT_CALLBACK_DATA Data, _In_ PCFLT_RELATED_OBJECTS FltObjects, _Flt_CompletionContext_Outptr_ PVOID* CompletionContext) { 98 | UNREFERENCED_PARAMETER(CompletionContext); 99 | 100 | PAGED_CODE(); 101 | 102 | FLT_PREOP_CALLBACK_STATUS ret = FLT_PREOP_SUCCESS_NO_CALLBACK; 103 | 104 | // Ignore directories 105 | BOOLEAN IsDir; 106 | NTSTATUS status = FltIsDirectory(FltObjects->FileObject, FltObjects->Instance, &IsDir); 107 | if (NT_SUCCESS(status)) { 108 | if (IsDir) { 109 | return ret; 110 | } 111 | } 112 | 113 | // https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/irp-mj-create 114 | // When the system tries to open a handle to a file object, 115 | // detect requests that have DELETE_ON_CLOSE in DesiredAccess 116 | if (Data->Iopb->MajorFunction == IRP_MJ_CREATE) { 117 | if (!FlagOn(Data->Iopb->Parameters.Create.Options, FILE_DELETE_ON_CLOSE)) { 118 | return ret; 119 | } 120 | } 121 | 122 | // Process requests with FileDispositionInformation, FileDispositionInformationEx or file renames 123 | if (Data->Iopb->MajorFunction == IRP_MJ_SET_INFORMATION) { 124 | switch (Data->Iopb->Parameters.SetFileInformation.FileInformationClass) { 125 | case FileRenameInformation: 126 | case FileRenameInformationEx: 127 | case FileDispositionInformation: 128 | case FileDispositionInformationEx: 129 | case FileRenameInformationBypassAccessCheck: 130 | case FileRenameInformationExBypassAccessCheck: 131 | break; 132 | 133 | default: 134 | return ret; 135 | } 136 | } 137 | 138 | PFLT_FILE_NAME_INFORMATION FileNameInfo = NULL; 139 | if (FltObjects->FileObject) { 140 | status = FltGetFileNameInformation(Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &FileNameInfo); 141 | if (NT_SUCCESS(status)) { 142 | FltParseFileNameInformation(FileNameInfo); 143 | 144 | Data->IoStatus.Status = STATUS_ACCESS_DENIED; 145 | Data->IoStatus.Information = 0; 146 | 147 | ret = FLT_PREOP_COMPLETE; 148 | 149 | DbgPrint("[DENIED] %wZ\n", FileNameInfo->Name); 150 | } 151 | else { 152 | DbgPrint("[ERROR] Failed to get file name information!\n"); 153 | } 154 | } 155 | else { 156 | DbgPrint("[ERROR] FltObjects->FileObject is NULL!\n"); 157 | } 158 | 159 | return ret; 160 | } 161 | 162 | NTSTATUS badgirlFilterUnload(FLT_FILTER_UNLOAD_FLAGS Flags) { 163 | UNREFERENCED_PARAMETER(Flags); 164 | 165 | PAGED_CODE(); 166 | 167 | DbgPrint("badgirlFilterUnloadCallback called\n"); 168 | 169 | // Unregister the minifilter 170 | FltUnregisterFilter(Filter); 171 | 172 | return STATUS_SUCCESS; 173 | } 174 | 175 | void badgirlDriverUnload(PDRIVER_OBJECT DriverObject) { 176 | UNREFERENCED_PARAMETER(DriverObject); 177 | 178 | DbgPrint("Bad bad girl is now leaving!\n"); 179 | } 180 | --------------------------------------------------------------------------------