ObRegisterCallbacks注册句柄操作回调与进程保护

简单记录下ObRegisterCallbacks的基本用法,加深一下自己的理解,也希望帮助看到的人少走这方面的弯路。如有错误还望不吝指正。

NTSTATUS ObRegisterCallbacks(
POB_CALLBACK_REGISTRATION CallbackRegistration,
PVOID *RegistrationHandle
);

第二个参数指向一个PVOID类型的数组,用于接收此次操作所注册的回调的句柄。一般来说你要注册多少个回调,数组就多长。
重点在于第一个参数,它是一个指向_OB_CALLBACK_REGISTRATION 结构的指针。

typedef struct _OB_CALLBACK_REGISTRATION {
  USHORT                    Version;
  USHORT                    OperationRegistrationCount;
  UNICODE_STRING            Altitude;
  PVOID                     RegistrationContext;
  OB_OPERATION_REGISTRATION *OperationRegistration;
} OB_CALLBACK_REGISTRATION, *POB_CALLBACK_REGISTRATION;

1.第一个成员Version,msdn说"Drivers should specify OB_FLT_REGISTRATION_VERSION.",那就听他的。
2.第二个成员OperationRegistrationCount,要注册的回调的数量。不必多说。一般与前面那个数组的长度一样。
3.第三个成员Altitude,根据msdn上的解释是驱动被加载的优先级。不同类型的驱动有不同的值范围。在后面的例子中我使用了“321000”这个值。(在一个环境下同一个值不能被重复使用,否则调用失败。注册多个回调时要注意这一点)
4.第四个成员RegistrationContext,当你的回调函数被调用时,系统会把这个参数传递给那个回调函数,如果不需要就填NULL。
5.第五个成员OperationRegistration,它指向一个_OB_OPERATION_REGISTRATION类型的结构。

typedef struct _OB_OPERATION_REGISTRATION {
  POBJECT_TYPE                *ObjectType;
  OB_OPERATION                Operations;
  POB_PRE_OPERATION_CALLBACK  PreOperation;
  POB_POST_OPERATION_CALLBACK PostOperation;
} OB_OPERATION_REGISTRATION, *POB_OPERATION_REGISTRATION;

1.第一个成员ObjectType,指明了这个回调要处理的对象类型,按照msdn的说法可以是PsProcessType,PsThreadType,ExDesktopObjectType 的其中之一。但其他的类似于SeTokenObjectType,IoFileObjectType也可以用,但因为系统默认是不开启这些回调的,需要先用EnableObType函数启用。
例如:EnableObType(*SeTokenObjectType);
2.第二个成员Operations,指定要处理的句柄操作类型,可以是OB_OPERATION_HANDLE_CREATE或者OB_OPERATION_HANDLE_DUPLICATE。顾名思义前者代表一个句柄被创建,后者代表句柄被复制。可以用位或操作运算符|进行叠加。
3.第三个和第四个成员分别是两个回调函数的指针,前者会在操作发生前被调用,后者会在操作发生后被调用。可以为NULL。

再来说一下回调函数的定义。

首先是操作前被调用的。

POB_PRE_OPERATION_CALLBACK PobPreOperationCallback;

OB_PREOP_CALLBACK_STATUS PobPreOperationCallback(
  [in] PVOID RegistrationContext,
  [in] POB_PRE_OPERATION_INFORMATION OperationInformation
)
{...}

1.第一个参数是你在_OB_CALLBACK_REGISTRATION中指定的。
2.第二个参数指向一个_OB_PRE_OPERATION_INFORMATION类型的结构体

typedef struct _OB_PRE_OPERATION_INFORMATION {
  OB_OPERATION                 Operation;
  union {
    ULONG Flags;
    struct {
      ULONG KernelHandle : 1;
      ULONG Reserved : 31;
    };
  };
  PVOID                        Object;
  POBJECT_TYPE                 ObjectType;
  PVOID                        CallContext;
  POB_PRE_OPERATION_PARAMETERS Parameters;
} OB_PRE_OPERATION_INFORMATION, *POB_PRE_OPERATION_INFORMATION;

1.第一个成员Operation,发生的操作类型,OB_OPERATION_HANDLE_CREATE或者OB_OPERATION_HANDLE_DUPLICATE。
2.下面那个联合体中,Flags和Reserved都是系统保留的空间,KernelHandle表示这个句柄是不是内核句柄。
3.Object是将要操作的句柄所对应的对象。比如PEPROCESS,PETHREAD。具体靠下一个成员ObjectType判断。
4.ObjectType表示这个句柄的类型,同_OB_OPERATION_REGISTRATION的第一个成员。
5.CallContext默认为NULL,在PreCallback中可以将其设置为其他值,这个值会被传递到PostCallback。
6.Parameters指向一个_OB_POST_OPERATION_PARAMETERS类型的联合体

typedef union _OB_POST_OPERATION_PARAMETERS {
  OB_POST_CREATE_HANDLE_INFORMATION    CreateHandleInformation;
  OB_POST_DUPLICATE_HANDLE_INFORMATION DuplicateHandleInformation;
} OB_POST_OPERATION_PARAMETERS, *POB_POST_OPERATION_PARAMETERS;

根据Operation的值来确定访问哪一个,OB_OPERATION_HANDLE_CREATE对应前者。

typedef struct _OB_PRE_CREATE_HANDLE_INFORMATION {
  ACCESS_MASK DesiredAccess;
  ACCESS_MASK OriginalDesiredAccess;
} OB_PRE_CREATE_HANDLE_INFORMATION, *POB_PRE_CREATE_HANDLE_INFORMATION;

_OB_PRE_CREATE_HANDLE_INFORMATION中定义了被创建的句柄所具有的原始权限和最终申请的权限。我们可以通过修改DesiredAccess的值来修改句柄的权限,比如我不想让我的进程被其他进程Terminate,就可以从句柄中拿掉相关的权限,示例如下。

switch (OperationInformation->Operation)
{
	case OB_OPERATION_HANDLE_DUPLICATE:
		break;

	case OB_OPERATION_HANDLE_CREATE:
	{
		if (OperationInformation->Parameters->CreateHandleInformation.OriginalDesiredAccess & 1)
		{
			OperationInformation->Parameters->CreateHandleInformation.DesiredAccess = 0;
		}
		break;
	}
}

那些被按位置零的值是ACCESS_MASK中的Specific Right,也就是前16位。对象类型是Process时代表PROCESS_TERMINATE等等。当然我们最好不只把PROCESS_TERMINATE权限拿掉,为了保险也把内存操作,挂起之类的权限拿掉了。
DuplicateHandleInformation与前面的大致相同,不同点是可以获取复制句柄的源进程和操作进程。
其他需要注意的是,你不能给复制的句柄添加原来没有的权限,但可以把已有的权限拿掉。

PostOperationCallback不常用并且用法与PreOperationCallback大致相同就不多说了。
只需要注意你不能在PostOperationCallback回调函数中修改这个句柄的权限。只能通过GrantedAccess获取。

附完整Demo代码:

#include "driverdef.h"

VOID DriverUnload(PDRIVER_OBJECT pDriver);
PVOID ProcHandle = NULL;

extern "C" NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
{
	OB_CALLBACK_REGISTRATION cr;  
	_OB_OPERATION_REGISTRATION or;
	
	UNREFERENCED_PARAMETER(RegistryPath);

	

	DriverObject->DriverUnload = DriverUnload;
	((PLDR_DATA)DriverObject->DriverSection)->Flags |= 0x20;    //绕过签名检测

	RtlZeroMemory(&or, sizeof(OB_OPERATION_REGISTRATION));
	RtlZeroMemory(&cr, sizeof(OB_CALLBACK_REGISTRATION));
	RtlInitUnicodeString(&cr.Altitude, L"321000");

	cr.Version = OB_FLT_REGISTRATION_VERSION;

	cr.RegistrationContext = NULL;
	cr.OperationRegistrationCount = 1;
	
	or.ObjectType = PsProcessType;
	or.Operations = OB_OPERATION_HANDLE_CREATE;
	or.PreOperation = ObjectPreCallback;

	cr.OperationRegistration = ∨
	
	return ObRegisterCallbacks(&cr, &ProcHandle);
}

VOID DriverUnload(PDRIVER_OBJECT pDriver)
{
	UNREFERENCED_PARAMETER(pDriver);

	if (ProcHandle != NULL)ObUnRegisterCallbacks(ProcHandle);
}

OB_PREOP_CALLBACK_STATUS ObjectPreCallback(
	_In_  PVOID RegistrationContext,
	_In_  POB_PRE_OPERATION_INFORMATION OperationInformation
)
{
	UNREFERENCED_PARAMETER(RegistrationContext);
	UNREFERENCED_PARAMETER(OperationInformation);
	
	PEPROCESS pEProcess = NULL;

	// 判断对象类型 
	if (*PsProcessType != OperationInformation->ObjectType)
	{
		return OB_PREOP_SUCCESS;
	}


	//获取该进程结构对象的名称
	pEProcess = (PEPROCESS)OperationInformation->Object;
	HANDLE pid = PsGetProcessId(pEProcess);
	char szProcName[256] = { 0 };
	strcpy(szProcName, GetProcessImageNameByProcessID((ULONG)pid));

	if (_stricmp(szProcName, "Demo.exe"))return OB_PREOP_SUCCESS;

	switch (OperationInformation->Operation)
	{
	case OB_OPERATION_HANDLE_DUPLICATE:
		break;

	case OB_OPERATION_HANDLE_CREATE:
	{
		if (OperationInformation->Parameters->CreateHandleInformation.OriginalDesiredAccess & 1)
		{
			OperationInformation->Parameters->CreateHandleInformation.DesiredAccess = 0;
		}
		break;
	}
	}
	
	return OB_PREOP_SUCCESS;
}

char* GetProcessImageNameByProcessID(
	_In_ ULONG ulProcessID
)
{
	NTSTATUS  Status;
	PEPROCESS  EProcess = NULL;

	Status = PsLookupProcessByProcessId(
		(HANDLE)ulProcessID,
		&EProcess);    //EPROCESS

	//通过句柄获取EProcess
	if (!NT_SUCCESS(Status))
		return FALSE;
	ObDereferenceObject(EProcess);
	//通过EProcess获得进程名称
	return (char*)PsGetProcessImageFileName(EProcess);
}

Git库:https://github.com/rootacite/ProcessProtect_Level3

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值