首页
社区
课程
招聘
[翻译]内核模式Rootkits:文件删除保护
发表于: 2018-8-20 15:16 8787

[翻译]内核模式Rootkits:文件删除保护

2018-8-20 15:16
8787

https://0x00sec.org/t/kernel-mode-rootkits-file-deletion-protection/7616

// The callbacks array defines what IRPs we want to process.
// callbacks数组定义我们想处理的IRP类型
CONST FLT_OPERATION_REGISTRATION Callbacks[] = {
    { IRP_MJ_CREATE, 0, PreAntiDelete, NULL },                // DELETE_ON_CLOSE creation flag.
    { IRP_MJ_SET_INFORMATION, 0, PreAntiDelete, NULL },        // FileInformationClass == FileDispositionInformation(Ex).
    { IRP_MJ_OPERATION_END }
};

CONST FLT_REGISTRATION FilterRegistration = {
    sizeof(FLT_REGISTRATION),                // Size
    FLT_REGISTRATION_VERSION,                // Version
    0,                                        // Flags
    NULL,                                    // ContextRegistration
    Callbacks,                                // OperationRegistration
    Unload,                                    // FilterUnloadCallback
    NULL,                                    // InstanceSetupCallback
    NULL,                                    // InstanceQueryTeardownCallback
    NULL,                                    // InstanceTeardownStartCallback
    NULL,                                    // InstanceTeardownCompleteCallback
    NULL,                                    // GenerateFileNameCallback
    NULL,                                    // NormalizeNameComponentCallback
    NULL                                    // NormalizeContextCleanupCallback
};

PFLT_FILTER Filter;
static UNICODE_STRING ProtectedExtention = RTL_CONSTANT_STRING(L"PROTECTED");

NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath) {
    // We can use this to load some configuration settings.
    //我们可以使用这个宏加载一些配置
    UNREFERENCED_PARAMETER(RegistryPath);

    DBG_PRINT("DriverEntry called.\n");

    // Register the minifilter with the filter manager.
    //通过过滤管理器注册微过滤驱动
    NTSTATUS status = FltRegisterFilter(DriverObject, &FilterRegistration, &Filter);
    if (!NT_SUCCESS(status)) {
        DBG_PRINT("Failed to register filter: <0x%08x>.\n", status);
        return status;
    }
    
    // Start filtering I/O.
    //开始过滤IO
    status = FltStartFiltering(Filter);
    if (!NT_SUCCESS(status)) {
        DBG_PRINT("Failed to start filter: <0x%08x>.\n", status);
        // If we fail, we need to unregister the minifilter.
        //如果失败,解除注册的过滤器
        FltUnregisterFilter(Filter);
    }

    return status;
}
First of all, the IRPs that should be processed by the driver are IRP_MJ_CREATE 1 and IRP_MJ_SET_INFORMATION 1 which are requests made when a file (or directory) is created and when metadata is being set, respectively. Both of these IRPs have the ability to delete a file which will be detailed later. The Callbacks array is defined with the respective IRP to be processed and the pre-operation and post-operation callback functions. The pre-operation defines the function that is called when the IRP goes down the stack while the post-operation is the function that is called when the IRP goes back up after it has been completed. Note that the post-operation is NULL as this scenario does not require one; the interception of file deletion is only handled in the pre-operation.
首先,驱动需要处理的IRP是IRP_MJ_CREATE[1]和IRP_MJ_SET_INFORMATION[1],这两个请求分别在文件或目录被创建和元数据(所创建文件或目录的元数据)被设置时发生。后面会说明,这些IRP都有能力删除一个文件。每个要处理的IRP都定义了Callbacks数组针、预处理、后处理函数。预处理在IRP顺着驱动栈向下请求时被调用,后处理在IRP被处理完成后向上返回时被调用。注意,这里不需要后处理,设置为NULL,介入文件删除只在预处理中进行。

DriverEntry is the driver’s main function where the registration with the filter manager is performed using FltRegisterFilter. Once that is successful, to start filtering IRPs, it must call the FltStartFiltering function with the filter handle. Also note that we have defined the extension to protect as .PROTECTED as aforementioned.
DriverEntry是驱动的主函数,在这里使用FltRegisterFilter向过滤管理器进行注册。一旦注册成功,要开始过滤,要使用得到的过滤器句柄调用FltStartFiltering函数。注意,之前提到过,我们要保护的文件扩展名为.PROTECTED。

It is also good practice to define an unload function so that if the driver has been requested to stop, it can perform an necessary cleanups. Its reference in this article is purely for completeness and does not serve any purpose for the main content.
定义一个卸载函数是一个很好的实践,如果驱动被请求停止,它可以执行需要的清除操作。本文中,它只是单纯地完成操作对于主要任务没有帮助。

/*
 * This is the driver unload routine used by the filter manager.
 * When the driver is requested to unload, it will call this function
 * and perform the necessary cleanups.
 */
NTSTATUS Unload(_In_ FLT_FILTER_UNLOAD_FLAGS Flags) {
	UNREFERENCED_PARAMETER(Flags);

	DBG_PRINT("Unload called.\n");

	// Unregister the minifilter.
	FltUnregisterFilter(Filter);

	return STATUS_SUCCESS;
}

The final function in this code is the PreAntiDelete pre-operation callback which handles the IRP_MJ_CREATE and IRP_MJ_SET_INFORMATION IRPs. IRP_MJ_CREATE includes functions that request a “file handle or file object or device object”[1] to be opened such as ZwCreateFile. IRP_MJ_SET_INFORMATION includes functions that request to set “metadata about a file or a file handle”[2] such as ZwSetInformationFile.
代码中最后的函数是预处理回调PreAntiDelete,它处理IRP_MJ_CREATE和IRP_MJ_SET_INFORMATION这两个IRP。IRP_MJ_CREATE的发起包括:请求打开文件句柄、文件对象或设备对象的函数,如ZwCreateFile。IRP_MJ_SET_INFORMATION的发起包括:请求设置文件、文件句柄的元数据的函数,如ZwSetInformationFile。

/*
 * This routine is called every time I/O is requested for:
 * - file creates (IRP_MJ_CREATE) such as ZwCreateFile and 
 * - file metadata sets on files or file handles 
 *   (IRP_MJ_SET_INFORMATION) such as ZwSetInformation.
 *
 * This is a pre-operation callback routine which means that the
 * IRP passes through this function on the way down the driver stack
 * to the respective device or driver to be handled.
 */
FLT_PREOP_CALLBACK_STATUS PreAntiDelete(_Inout_ PFLT_CALLBACK_DATA Data, _In_ PCFLT_RELATED_OBJECTS FltObjects, _Flt_CompletionContext_Outptr_ PVOID *CompletionContext) {
	UNREFERENCED_PARAMETER(CompletionContext);

	/* 
	 * This pre-operation callback code should be running at 
	 * IRQL <= APC_LEVEL as stated in the docs:
	这个预处理回调运行的IRQL<= APC_LEVEL,详细见下文
	 * https://docs.microsoft.com/en-us/windows-hardware/drivers/ifs/writing-preoperation-callback-routines
	 * and both ZwCreateFile and ZwSetInformaitonFile are also run at 
	 * IRQL == PASSIVE_LEVEL:
	 * - ZwCreateFile: https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/ntifs/nf-ntifs-ntcreatefile#requirements
	 * - ZwSetInformationFile: https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/ntifs/nf-ntifs-ntsetinformationfile#requirements
	 */
	PAGED_CODE();

	/*
	 * By default, we don't want to call the post-operation routine
	 * because there's no need to further process it and also
	 * because there is none.
	 */
	/*
	默认,我们不需要后处理回调函数,因为不需要更多的处理。
*/
	FLT_PREOP_CALLBACK_STATUS ret = FLT_PREOP_SUCCESS_NO_CALLBACK;

	// We don't care about directories.
	//我们不关心目录
	BOOLEAN IsDirectory;
	NTSTATUS status = FltIsDirectory(FltObjects->FileObject, FltObjects->Instance, &IsDirectory);
	if (NT_SUCCESS(status)) {
		if (IsDirectory == TRUE) {
			return ret;
		}
	}

	/*
	 * We don't want anything that doesn't have the DELETE_ON_CLOSE 
	 * flag.
	 */
	/*
	只关心有DELETE_ON_CLOSE标志的情况
*/
	if (Data->Iopb->MajorFunction == IRP_MJ_CREATE) {
		if (!FlagOn(Data->Iopb->Parameters.Create.Options, FILE_DELETE_ON_CLOSE)) {
			return ret;
		}
	}

	/*
	 * We don't want anything that doesn't have either 
	 * FileDispositionInformation or FileDispositionInformationEx or 
	 * file renames (which can just simply rename the extension).
	 */
	/*
	 我们不关心FileDispositionInformation、FileDispositionInformationEx或FileRename以外的情况 (可以重命名扩展名).
	 */

	if (Data->Iopb->MajorFunction == IRP_MJ_SET_INFORMATION) {
		switch (Data->Iopb->Parameters.SetFileInformation.FileInformationClass) {
			case FileRenameInformation:
			case FileRenameInformationEx:
			case FileDispositionInformation:
			case FileDispositionInformationEx:
			case FileRenameInformationBypassAccessCheck:
			case FileRenameInformationExBypassAccessCheck:
			case FileShortNameInformation:
				break;
			default:
				return ret;
		}
	}

	/*
	 * Here we can check if we want to allow a specific process to fall 
	 * through the checks, e.g. our own application.
	 * Since this is a PASSIVE_LEVEL operation, we can assume(?) that 
	 * the thread context is the thread that requested the I/O. We can  
	 * check the current thread and compare the EPROCESS of the 
	 * authenticated application like so:
	 *
	 * if (IoThreadToProcess(Data->Thread) == UserProcess) {
	 *     return FLT_PREOP_SUCCESS_NO_CALLBACK;
	 * }
	 *
	 * Of course, we would need to find and save the EPROCESS of the 
	 * application somewhere first. Something like a communication port 
	 * could work.
	 */
	/*
这里可以检查是否准许特定的进程绕过检查(如我们自己的应用)。当该操作的优先级是PASSIVE_LEVEL时,我们可以假设当前的线程上下文是发起IO请求的线程。我们可以检查当前的线程,比较EPROCESS有否是特权的应用:
if (IoThreadToProcess(Data->Thread) == UserProcess) {
	      return FLT_PREOP_SUCCESS_NO_CALLBACK;
	  }
当然,我们在之前要保存特权应用的EPROCESS。可以通过类似通信端口来实现。
*/

	PFLT_FILE_NAME_INFORMATION FileNameInfo = NULL;
	// Make sure the file object exists.
	//确保文件对象存在
	if (FltObjects->FileObject != NULL) {
		// Get the file name information with the normalized name.
		//以标准化名称形式获得文件名信息
		status = FltGetFileNameInformation(Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &FileNameInfo);
		if (NT_SUCCESS(status)) {
			// Now we want to parse the file name information to get the extension.
			//解析文件名信息获取扩展名
			FltParseFileNameInformation(FileNameInfo);

			// Compare the file extension (case-insensitive) and check if it is protected.
			//比较文件扩展名判断是否保护
			if (RtlCompareUnicodeString(&FileNameInfo->Extension, &ProtectedExtention, TRUE) == 0) {
				DBG_PRINT("Protecting file deletion/rename!");
				// Strings match, deny access!
				Data->IoStatus.Status = STATUS_ACCESS_DENIED;
				Data->IoStatus.Information = 0;
				// Complete the I/O request and send it back up.
				ret = FLT_PREOP_COMPLETE;
			}

			// Clean up file name information.
			//清理文件名信息
			FltReleaseFileNameInformation(FileNameInfo);
		}
	}

	return ret;
}

For IRP_MJ_CREATE, we want to check for the FILE_DELETE_ON_CLOSE create option which is described as "Delete the file when the last handle to it is passed to NtClose. If this flag is set, the DELETE flag must be set in the DesiredAccess parameter."[3] If this flag is not present, we do not care about it so it is passed down the stack for further processing as represented by the FLT_PREOP_SUCCESS_NO_CALLBACK return value. Note that the NO_CALLBACK means that the post-operation routine should not be called when the IRP is completed and passed back up the stack which is what should always be returned by this function as there is no post-operation.
对于IRP_MJ_CREATE,我们检查是否有FILE_DELETE_ON_CLOSE 创建选项,它的描述是:通过最后交给NtClose处理来删除文件。如果设置了该标志,在高层函数的DesiredAccess参数中一定设置了DELETE标志,如果该标志不存在,我们就不关心该IRP请求,通过返回FLT_PREOP_SUCCESS_NO_CALLBACK交给驱动栈下面驱动的进行处理。NO_CALLBACK表明当IRP完成请求向上回传时不需要调用后处理函数,因为没有后处理,该函数应该总是返回该值。

For IRP_MJ_SET_INFORMATION, the FileInformationClass parameter should be checked. The FileDispositionInformation value is described as "Usually, sets the DeleteFile member of a FILE_DISPOSITION_INFORMATION to TRUE, so the file can be deleted when NtClose is called to release the last open handle to the file object. The caller must have opened the file with the DELETE flag set in the DesiredAccess parameter."[4]. To prevent the file from simply being renamed such that the protected extension no longer exists, the FileRenameInformation and FileShortNameInformation values must also be checked.
对于IRP_MJ_SET_INFORMATION,需要检查FileInformationClass参数。该参数的可能值FileDispositionInformation的描述是:设置FILE_DISPOSITION_INFORMATION的DeleteFile成员为真,当通过NtClose关闭打开文件的最后一个句柄时文件会被删除。调用者在打开文件时的DesiredAccess参数必须有DELETE标志[4]。为了防止文件被重命名导致保护标志不再存在,也要检查参数值是否为FileRenameInformation和FileShortNameInformation。

If the driver receives an IRP request that is selected for file deletion, it must parse the file name information to extract the extension by using the FltGetFileNameInformation and FltParseFileNameInformation functions. Then it is a simple string comparison between the requested file for deletion’s extension and the protected extension to determine whether the delete operation should be allowed or disallowed. In the case of an unauthorised file deletion, the status of the operation is set to STATUS_ACCESS_DENIED and the pre-operation function completes the IRP.
如果驱动收到删除文件的IRP请求,必须使用FltGetFileNameInformation和FltParseFileNameInformation函数解析文件名信息并提取出扩展名。然后通过简单的字符串比较判断要删除的文件的扩展名是否是被保护的扩展名,决定是否准许该删除操作。当检测到非法的文件删除,设置操作的状态为STATUS_ACCESS_DENIED并在预处理回调函数中完成该IRP。

Demonstration

演示


Attempt to delete the file:
尝试删除文件:
Attempt to rename the file:
尝试重命名文件:
Hope that was educational and somewhat interesting or motivational. As usual, you can find the code on my GitHub. Thanks for reading!
希望本文既能带给你知识也能激发你的兴趣。通常代码都可以在我的GitHub上找到。
https://github.com/NtRaiseHardError/Anti-Delete


/*
 * This is the driver unload routine used by the filter manager.
 * When the driver is requested to unload, it will call this function
 * and perform the necessary cleanups.
 */
NTSTATUS Unload(_In_ FLT_FILTER_UNLOAD_FLAGS Flags) {
	UNREFERENCED_PARAMETER(Flags);

	DBG_PRINT("Unload called.\n");

	// Unregister the minifilter.
	FltUnregisterFilter(Filter);

	return STATUS_SUCCESS;
}

The final function in this code is the PreAntiDelete pre-operation callback which handles the IRP_MJ_CREATE and IRP_MJ_SET_INFORMATION IRPs. IRP_MJ_CREATE includes functions that request a “file handle or file object or device object”[1] to be opened such as ZwCreateFile. IRP_MJ_SET_INFORMATION includes functions that request to set “metadata about a file or a file handle”[2] such as ZwSetInformationFile.
代码中最后的函数是预处理回调PreAntiDelete,它处理IRP_MJ_CREATE和IRP_MJ_SET_INFORMATION这两个IRP。IRP_MJ_CREATE的发起包括:请求打开文件句柄、文件对象或设备对象的函数,如ZwCreateFile。IRP_MJ_SET_INFORMATION的发起包括:请求设置文件、文件句柄的元数据的函数,如ZwSetInformationFile。

/*
 * This routine is called every time I/O is requested for:
 * - file creates (IRP_MJ_CREATE) such as ZwCreateFile and 
 * - file metadata sets on files or file handles 
 *   (IRP_MJ_SET_INFORMATION) such as ZwSetInformation.
 *
 * This is a pre-operation callback routine which means that the
 * IRP passes through this function on the way down the driver stack
 * to the respective device or driver to be handled.
 */
FLT_PREOP_CALLBACK_STATUS PreAntiDelete(_Inout_ PFLT_CALLBACK_DATA Data, _In_ PCFLT_RELATED_OBJECTS FltObjects, _Flt_CompletionContext_Outptr_ PVOID *CompletionContext) {
	UNREFERENCED_PARAMETER(CompletionContext);

	/* 
	 * This pre-operation callback code should be running at 
	 * IRQL <= APC_LEVEL as stated in the docs:
	这个预处理回调运行的IRQL<= APC_LEVEL,详细见下文
	 * https://docs.microsoft.com/en-us/windows-hardware/drivers/ifs/writing-preoperation-callback-routines
	 * and both ZwCreateFile and ZwSetInformaitonFile are also run at 
	 * IRQL == PASSIVE_LEVEL:
	 * - ZwCreateFile: https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/ntifs/nf-ntifs-ntcreatefile#requirements
	 * - ZwSetInformationFile: https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/ntifs/nf-ntifs-ntsetinformationfile#requirements
	 */
	PAGED_CODE();

	/*
	 * By default, we don't want to call the post-operation routine
	 * because there's no need to further process it and also
	 * because there is none.
	 */
	/*
	默认,我们不需要后处理回调函数,因为不需要更多的处理。
*/
	FLT_PREOP_CALLBACK_STATUS ret = FLT_PREOP_SUCCESS_NO_CALLBACK;

	// We don't care about directories.
	//我们不关心目录
	BOOLEAN IsDirectory;
	NTSTATUS status = FltIsDirectory(FltObjects->FileObject, FltObjects->Instance, &IsDirectory);
	if (NT_SUCCESS(status)) {
		if (IsDirectory == TRUE) {
			return ret;
		}
	}

	/*
	 * We don't want anything that doesn't have the DELETE_ON_CLOSE 
	 * flag.
	 */
	/*
	只关心有DELETE_ON_CLOSE标志的情况
*/
	if (Data->Iopb->MajorFunction == IRP_MJ_CREATE) {
		if (!FlagOn(Data->Iopb->Parameters.Create.Options, FILE_DELETE_ON_CLOSE)) {
			return ret;
		}
	}

	/*
	 * We don't want anything that doesn't have either 
	 * FileDispositionInformation or FileDispositionInformationEx or 
	 * file renames (which can just simply rename the extension).
	 */
	/*
	 我们不关心FileDispositionInformation、FileDispositionInformationEx或FileRename以外的情况 (可以重命名扩展名).
	 */

	if (Data->Iopb->MajorFunction == IRP_MJ_SET_INFORMATION) {
		switch (Data->Iopb->Parameters.SetFileInformation.FileInformationClass) {
			case FileRenameInformation:
			case FileRenameInformationEx:
			case FileDispositionInformation:
			case FileDispositionInformationEx:
			case FileRenameInformationBypassAccessCheck:
			case FileRenameInformationExBypassAccessCheck:
			case FileShortNameInformation:
				break;
			default:
				return ret;
		}
	}

	/*
	 * Here we can check if we want to allow a specific process to fall 
	 * through the checks, e.g. our own application.
	 * Since this is a PASSIVE_LEVEL operation, we can assume(?) that 
	 * the thread context is the thread that requested the I/O. We can  
	 * check the current thread and compare the EPROCESS of the 
	 * authenticated application like so:
	 *
	 * if (IoThreadToProcess(Data->Thread) == UserProcess) {
	 *     return FLT_PREOP_SUCCESS_NO_CALLBACK;
	 * }
	 *
	 * Of course, we would need to find and save the EPROCESS of the 
	 * application somewhere first. Something like a communication port 
	 * could work.
	 */
	/*
这里可以检查是否准许特定的进程绕过检查(如我们自己的应用)。当该操作的优先级是PASSIVE_LEVEL时,我们可以假设当前的线程上下文是发起IO请求的线程。我们可以检查当前的线程,比较EPROCESS有否是特权的应用:
if (IoThreadToProcess(Data->Thread) == UserProcess) {
	      return FLT_PREOP_SUCCESS_NO_CALLBACK;
	  }
当然,我们在之前要保存特权应用的EPROCESS。可以通过类似通信端口来实现。
*/

	PFLT_FILE_NAME_INFORMATION FileNameInfo = NULL;
	// Make sure the file object exists.
	//确保文件对象存在
	if (FltObjects->FileObject != NULL) {
		// Get the file name information with the normalized name.
		//以标准化名称形式获得文件名信息
		status = FltGetFileNameInformation(Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &FileNameInfo);
		if (NT_SUCCESS(status)) {
			// Now we want to parse the file name information to get the extension.
			//解析文件名信息获取扩展名
			FltParseFileNameInformation(FileNameInfo);

			// Compare the file extension (case-insensitive) and check if it is protected.
			//比较文件扩展名判断是否保护
			if (RtlCompareUnicodeString(&FileNameInfo->Extension, &ProtectedExtention, TRUE) == 0) {
				DBG_PRINT("Protecting file deletion/rename!");
				// Strings match, deny access!
				Data->IoStatus.Status = STATUS_ACCESS_DENIED;
				Data->IoStatus.Information = 0;
				// Complete the I/O request and send it back up.
				ret = FLT_PREOP_COMPLETE;
			}

			// Clean up file name information.
			//清理文件名信息
			FltReleaseFileNameInformation(FileNameInfo);
		}
	}

	return ret;
}

For IRP_MJ_CREATE, we want to check for the FILE_DELETE_ON_CLOSE create option which is described as "Delete the file when the last handle to it is passed to NtClose. If this flag is set, the DELETE flag must be set in the DesiredAccess parameter."[3] If this flag is not present, we do not care about it so it is passed down the stack for further processing as represented by the FLT_PREOP_SUCCESS_NO_CALLBACK return value. Note that the NO_CALLBACK means that the post-operation routine should not be called when the IRP is completed and passed back up the stack which is what should always be returned by this function as there is no post-operation.
对于IRP_MJ_CREATE,我们检查是否有FILE_DELETE_ON_CLOSE 创建选项,它的描述是:通过最后交给NtClose处理来删除文件。如果设置了该标志,在高层函数的DesiredAccess参数中一定设置了DELETE标志,如果该标志不存在,我们就不关心该IRP请求,通过返回FLT_PREOP_SUCCESS_NO_CALLBACK交给驱动栈下面驱动的进行处理。NO_CALLBACK表明当IRP完成请求向上回传时不需要调用后处理函数,因为没有后处理,该函数应该总是返回该值。

For IRP_MJ_SET_INFORMATION, the FileInformationClass parameter should be checked. The FileDispositionInformation value is described as "Usually, sets the DeleteFile member of a FILE_DISPOSITION_INFORMATION to TRUE, so the file can be deleted when NtClose is called to release the last open handle to the file object. The caller must have opened the file with the DELETE flag set in the DesiredAccess parameter."[4]. To prevent the file from simply being renamed such that the protected extension no longer exists, the FileRenameInformation and FileShortNameInformation values must also be checked.
对于IRP_MJ_SET_INFORMATION,需要检查FileInformationClass参数。该参数的可能值FileDispositionInformation的描述是:设置FILE_DISPOSITION_INFORMATION的DeleteFile成员为真,当通过NtClose关闭打开文件的最后一个句柄时文件会被删除。调用者在打开文件时的DesiredAccess参数必须有DELETE标志[4]。为了防止文件被重命名导致保护标志不再存在,也要检查参数值是否为FileRenameInformation和FileShortNameInformation。

If the driver receives an IRP request that is selected for file deletion, it must parse the file name information to extract the extension by using the FltGetFileNameInformation and FltParseFileNameInformation functions. Then it is a simple string comparison between the requested file for deletion’s extension and the protected extension to determine whether the delete operation should be allowed or disallowed. In the case of an unauthorised file deletion, the status of the operation is set to STATUS_ACCESS_DENIED and the pre-operation function completes the IRP.
如果驱动收到删除文件的IRP请求,必须使用FltGetFileNameInformation和FltParseFileNameInformation函数解析文件名信息并提取出扩展名。然后通过简单的字符串比较判断要删除的文件的扩展名是否是被保护的扩展名,决定是否准许该删除操作。当检测到非法的文件删除,设置操作的状态为STATUS_ACCESS_DENIED并在预处理回调函数中完成该IRP。

Demonstration

演示


Attempt to delete the file:
尝试删除文件:
Attempt to rename the file:
尝试重命名文件:
Hope that was educational and somewhat interesting or motivational. As usual, you can find the code on my GitHub. Thanks for reading!
希望本文既能带给你知识也能激发你的兴趣。通常代码都可以在我的GitHub上找到。
https://github.com/NtRaiseHardError/Anti-Delete


/*
 * This routine is called every time I/O is requested for:
 * - file creates (IRP_MJ_CREATE) such as ZwCreateFile and 
 * - file metadata sets on files or file handles 
 *   (IRP_MJ_SET_INFORMATION) such as ZwSetInformation.
 *
 * This is a pre-operation callback routine which means that the
 * IRP passes through this function on the way down the driver stack
 * to the respective device or driver to be handled.
 */
FLT_PREOP_CALLBACK_STATUS PreAntiDelete(_Inout_ PFLT_CALLBACK_DATA Data, _In_ PCFLT_RELATED_OBJECTS FltObjects, _Flt_CompletionContext_Outptr_ PVOID *CompletionContext) {
	UNREFERENCED_PARAMETER(CompletionContext);

	/* 
	 * This pre-operation callback code should be running at 
	 * IRQL <= APC_LEVEL as stated in the docs:
	这个预处理回调运行的IRQL<= APC_LEVEL,详细见下文
	 * https://docs.microsoft.com/en-us/windows-hardware/drivers/ifs/writing-preoperation-callback-routines
	 * and both ZwCreateFile and ZwSetInformaitonFile are also run at 
	 * IRQL == PASSIVE_LEVEL:
	 * - ZwCreateFile: https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/ntifs/nf-ntifs-ntcreatefile#requirements
	 * - ZwSetInformationFile: https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/ntifs/nf-ntifs-ntsetinformationfile#requirements
	 */
	PAGED_CODE();

	/*
	 * By default, we don't want to call the post-operation routine
	 * because there's no need to further process it and also
	 * because there is none.
	 */
	/*
	默认,我们不需要后处理回调函数,因为不需要更多的处理。
*/
	FLT_PREOP_CALLBACK_STATUS ret = FLT_PREOP_SUCCESS_NO_CALLBACK;

	// We don't care about directories.
	//我们不关心目录
	BOOLEAN IsDirectory;
	NTSTATUS status = FltIsDirectory(FltObjects->FileObject, FltObjects->Instance, &IsDirectory);
	if (NT_SUCCESS(status)) {
		if (IsDirectory == TRUE) {
			return ret;
		}
	}

	/*
	 * We don't want anything that doesn't have the DELETE_ON_CLOSE 
	 * flag.
	 */
	/*
	只关心有DELETE_ON_CLOSE标志的情况
*/
	if (Data->Iopb->MajorFunction == IRP_MJ_CREATE) {
		if (!FlagOn(Data->Iopb->Parameters.Create.Options, FILE_DELETE_ON_CLOSE)) {
			return ret;
		}
	}

	/*
	 * We don't want anything that doesn't have either 
	 * FileDispositionInformation or FileDispositionInformationEx or 
	 * file renames (which can just simply rename the extension).
	 */
	/*
	 我们不关心FileDispositionInformation、FileDispositionInformationEx或FileRename以外的情况 (可以重命名扩展名).
	 */

	if (Data->Iopb->MajorFunction == IRP_MJ_SET_INFORMATION) {
		switch (Data->Iopb->Parameters.SetFileInformation.FileInformationClass) {
			case FileRenameInformation:
			case FileRenameInformationEx:
			case FileDispositionInformation:
			case FileDispositionInformationEx:
			case FileRenameInformationBypassAccessCheck:
			case FileRenameInformationExBypassAccessCheck:
			case FileShortNameInformation:
				break;
			default:
				return ret;
		}
	}

	/*
	 * Here we can check if we want to allow a specific process to fall 
	 * through the checks, e.g. our own application.
	 * Since this is a PASSIVE_LEVEL operation, we can assume(?) that 
	 * the thread context is the thread that requested the I/O. We can  
	 * check the current thread and compare the EPROCESS of the 
	 * authenticated application like so:
	 *
	 * if (IoThreadToProcess(Data->Thread) == UserProcess) {
	 *     return FLT_PREOP_SUCCESS_NO_CALLBACK;
	 * }
	 *
	 * Of course, we would need to find and save the EPROCESS of the 
	 * application somewhere first. Something like a communication port 
	 * could work.
	 */
	/*
这里可以检查是否准许特定的进程绕过检查(如我们自己的应用)。当该操作的优先级是PASSIVE_LEVEL时,我们可以假设当前的线程上下文是发起IO请求的线程。我们可以检查当前的线程,比较EPROCESS有否是特权的应用:
if (IoThreadToProcess(Data->Thread) == UserProcess) {
	      return FLT_PREOP_SUCCESS_NO_CALLBACK;
	  }
当然,我们在之前要保存特权应用的EPROCESS。可以通过类似通信端口来实现。
*/

	PFLT_FILE_NAME_INFORMATION FileNameInfo = NULL;
	// Make sure the file object exists.
	//确保文件对象存在
	if (FltObjects->FileObject != NULL) {
		// Get the file name information with the normalized name.
		//以标准化名称形式获得文件名信息
		status = FltGetFileNameInformation(Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &FileNameInfo);
		if (NT_SUCCESS(status)) {
			// Now we want to parse the file name information to get the extension.
			//解析文件名信息获取扩展名
			FltParseFileNameInformation(FileNameInfo);

			// Compare the file extension (case-insensitive) and check if it is protected.
			//比较文件扩展名判断是否保护
			if (RtlCompareUnicodeString(&FileNameInfo->Extension, &ProtectedExtention, TRUE) == 0) {
				DBG_PRINT("Protecting file deletion/rename!");
				// Strings match, deny access!
				Data->IoStatus.Status = STATUS_ACCESS_DENIED;
				Data->IoStatus.Information = 0;
				// Complete the I/O request and send it back up.
				ret = FLT_PREOP_COMPLETE;
			}

			// Clean up file name information.
			//清理文件名信息
			FltReleaseFileNameInformation(FileNameInfo);
		}
	}

	return ret;
}

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 2
支持
分享
最新回复 (5)
雪    币: 775
活跃值: (2292)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
2
感谢分享~
2018-8-20 16:08
0
雪    币: 144
活跃值: (335)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
谢谢分享
2018-8-20 17:09
0
雪    币: 1535
活跃值: (695)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
谢谢分享!
2018-8-27 15:30
0
雪    币: 3736
活跃值: (3867)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
5
感谢分享
2018-8-28 17:08
0
雪    币: 234
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
6
感谢分享
2019-4-23 19:09
0
游客
登录 | 注册 方可回帖
返回
//