简单记录下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