驱动开发:对象回调监控文件访问

2022-12-20 14:00:39 浏览数 (2)

无论在用户层还是内核层,操作文件的流程基本一致,除了在API函数上的区别(用户层调用用户层API,内核层调用内核API)以外其他基本一致,先讲解一下文件系统执行的流程。实现文件的监控呢,比如当文件被访问时自动触发回调,看如下代码实现方式。

以NTFS文件系统为例:

  • 假设我们要读取文件,他们最终都会被转换为IRP(I/O Request Package)请求,该请求会被分发到 NTFS.sys 驱动中的 IRP_MJ_READ 分发函数里。
  • NTFS.sys 驱动经过处理后,继续将IRP请求传递给CLASSPNP.sys磁盘类驱动的 IRP_MJ_READ 分发函数。
  • 磁盘类驱动处理完毕后,又把 IRP 传给磁盘ATAPI.SYS小端口驱动的 IRP_MJ_SCSI 分发函数中。
  • 依靠 HAL.DLL 发送相关的硬件中断请求,而硬件中断则负责完成实际的磁盘寻址,此时数据就真的从硬盘里读取了出来,然后再按照相反的方向把数据返回到调用者。

那么如何实现文件的监控呢,比如当文件被访问时自动触发回调,看如下代码实现方式。

代码语言:c复制
#include <ntddk.h>
#include <string.h>

PVOID obHandle;

typedef struct _OBJECT_TYPE_INITIALIZER
{
	/*0x000*/     UINT16       Length;
	union
	{
		/*0x002*/         UINT8        ObjectTypeFlags;
		struct
		{
			/*0x002*/             UINT8        CaseInsensitive : 1;
			/*0x002*/             UINT8        UnnamedObjectsOnly : 1;
			/*0x002*/             UINT8        UseDefaultObject : 1;
			/*0x002*/             UINT8        SecurityRequired : 1;
			/*0x002*/             UINT8        MaintainHandleCount : 1;
			/*0x002*/             UINT8        MaintainTypeList : 1;
			/*0x002*/             UINT8        SupportsObjectCallbacks : 1;
		};
	};
	/*0x004*/     ULONG32      ObjectTypeCode;
	/*0x008*/     ULONG32      InvalidAttributes;
	/*0x00C*/     struct _GENERIC_MAPPING GenericMapping;
	/*0x01C*/     ULONG32      ValidAccessMask;
	/*0x020*/     ULONG32      RetainAccess;
	/*0x024*/     enum _POOL_TYPE PoolType;
	/*0x028*/     ULONG32      DefaultPagedPoolCharge;
	/*0x02C*/     ULONG32      DefaultNonPagedPoolCharge;
	/*0x030*/     PVOID DumpProcedure;
	/*0x038*/     PVOID OpenProcedure;
	/*0x040*/     PVOID CloseProcedure;
	/*0x048*/     PVOID DeleteProcedure;
	/*0x050*/     PVOID ParseProcedure;
	/*0x058*/     PVOID SecurityProcedure;
	/*0x060*/     PVOID QueryNameProcedure;
	/*0x068*/     PVOID OkayToCloseProcedure;
}OBJECT_TYPE_INITIALIZER, *POBJECT_TYPE_INITIALIZER;

typedef struct _EX_PUSH_LOCK                 // 7 elements, 0x8 bytes (sizeof)
{
	union                                    // 3 elements, 0x8 bytes (sizeof)
	{
		struct                               // 5 elements, 0x8 bytes (sizeof)
		{
			/*0x000*/             UINT64       Locked : 1;         // 0 BitPosition
			/*0x000*/             UINT64       Waiting : 1;        // 1 BitPosition
			/*0x000*/             UINT64       Waking : 1;         // 2 BitPosition
			/*0x000*/             UINT64       MultipleShared : 1; // 3 BitPosition
			/*0x000*/             UINT64       Shared : 60;        // 4 BitPosition
		};
		/*0x000*/         UINT64       Value;
		/*0x000*/         VOID*        Ptr;
	};
}EX_PUSH_LOCK, *PEX_PUSH_LOCK;

typedef struct _MY_OBJECT_TYPE
{
	/*0x000*/     struct  _LIST_ENTRY TypeList;
	/*0x010*/     struct  _UNICODE_STRING Name;
	/*0x020*/     VOID*   DefaultObject;
	/*0x028*/     UINT8   Index;
	/*0x029*/     UINT8   _PADDING0_[0x3];
	/*0x02C*/     ULONG32 TotalNumberOfObjects;
	/*0x030*/     ULONG32 TotalNumberOfHandles;
	/*0x034*/     ULONG32 HighWaterNumberOfObjects;
	/*0x038*/     ULONG32 HighWaterNumberOfHandles;
	/*0x03C*/     UINT8   _PADDING1_[0x4];
	/*0x040*/     struct _OBJECT_TYPE_INITIALIZER TypeInfo;
	/*0x0B0*/     struct _EX_PUSH_LOCK TypeLock;
	/*0x0B8*/     ULONG32      Key;
	/*0x0BC*/     UINT8        _PADDING2_[0x4];
	/*0x0C0*/     struct _LIST_ENTRY CallbackList;
}MY_OBJECT_TYPE, *PMY_OBJECT_TYPE;


// 要监控文件,首先要文件对象支持对象回调
VOID EnableObType(POBJECT_TYPE ObjectType)
{
	PMY_OBJECT_TYPE myobtype = (PMY_OBJECT_TYPE)ObjectType;
	myobtype->TypeInfo.SupportsObjectCallbacks = 1;
}

OB_PREOP_CALLBACK_STATUS preFileCallBack(PVOID RegistrationContext, POB_PRE_OPERATION_INFORMATION OperationInformation)
{
	UNICODE_STRING DosName;
	PFILE_OBJECT fileo = OperationInformation->Object;
	HANDLE CurrentProcessId = PsGetCurrentProcessId();
	UNREFERENCED_PARAMETER(RegistrationContext);

	if (OperationInformation->ObjectType != *IoFileObjectType) { return OB_PREOP_SUCCESS; }
	//过滤无效指针
	if (fileo->FileName.Buffer == NULL ||
		!MmIsAddressValid(fileo->FileName.Buffer) ||
		fileo->DeviceObject == NULL ||
		!MmIsAddressValid(fileo->DeviceObject))
		return OB_PREOP_SUCCESS;

	// 此处可添加过滤规则,过滤掉无效文件名
	if (!_wcsicmp(fileo->FileName.Buffer, L"\Endpoint") ||
		!_wcsicmp(fileo->FileName.Buffer, L"?") ||
		!_wcsicmp(fileo->FileName.Buffer, L"\.\.") ||
		!_wcsicmp(fileo->FileName.Buffer, L"\"))
		return OB_PREOP_SUCCESS;

	if (wcsstr(_wcslwr(fileo->FileName.Buffer), L".exe")) // 如果包含有exe文件,则触发
	{
		DbgPrint("当前ID= %ld ---> 路径= %wZ", (ULONG64)CurrentProcessId, &fileo->FileName);
	}
	return OB_PREOP_SUCCESS;
}

VOID UnDriver(PDRIVER_OBJECT driver)
{
	UNREFERENCED_PARAMETER(driver);
	ObUnRegisterCallbacks(obHandle);
}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
	NTSTATUS status = STATUS_SUCCESS;
	OB_CALLBACK_REGISTRATION obReg;
	OB_OPERATION_REGISTRATION opReg;

	EnableObType(*IoFileObjectType);
	memset(&obReg, 0, sizeof(obReg));
	obReg.Version = ObGetFilterVersion();
	obReg.OperationRegistrationCount = 1;
	obReg.RegistrationContext = NULL;
	RtlInitUnicodeString(&obReg.Altitude, L"321000");
	obReg.OperationRegistration = &opReg;
	memset(&opReg, 0, sizeof(opReg));
	opReg.ObjectType = IoFileObjectType;
	opReg.Operations = OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE;
	opReg.PreOperation = (POB_PRE_OPERATION_CALLBACK)&preFileCallBack;
	status = ObRegisterCallbacks(&obReg, &obHandle);
	
	if (!NT_SUCCESS(status))
		status = STATUS_UNSUCCESSFUL;
	Driver->DriverUnload = UnDriver;
	return status;
}

0 人点赞