通过注册回调函数保护特定进程

在 win10 以上操作系统,出于系统安全考虑无法进行各种挂钩,因此官方推出了通过注册回调函数来达到类似系统挂钩的效果。

那么,我们同样可以通过注册回调函数来保护我们的进程,当一个其他的进程想要打开我们的进程的时候,必然会调用 OpenProcess 函数,就会先经过我们注册的回调函数,那么我们就可以通过对该操作进行降权处理,就可以达到保护特定进程的目的。

由于回调函数所拥有的权限较高,因此注册回调函数需要驱动签名才可以,否则在加载驱动的时候会提示 拒绝访问,但是我们可以在代码中加一条语句来禁用驱动签名:

*(PULONG)((PCHAR)DriverObject->DriverSection + 13 * sizeof(void*)) |= 0x20;

接下来定义两个全局变量:

PVOID g_callback_handle = NULL;  // 回调函数句柄
HANDLE g_pid = (HANDLE)532;  // 需要保护的进程

然后我们需要填充两个结构体,第一个是 OB_CALLBACK_REGISTRATION,第二个是 OB_OPERATION_REGISTRATION。前一个结构体用来指定注册回调函数的一些参数,后一个结构体用来指定该回调函数和操作相关的参数。

首先我们来注册 OB_CALLBACK_REGISTRATION 和 OB_OPERATION_REGISTRATION 结构体:

OB_CALLBACK_REGISTRATION callBackReg{ 0 };
OB_OPERATION_REGISTRATION callBackOper{ 0 };

callBackReg.Version = OB_FLT_REGISTRATION_VERSION;  // 请求的回调对象注册的版本
callBackReg.OperationRegistrationCount = 1;  // OperationRegistration 数组中的条目数
callBackReg.Altitude = RTL_CONSTANT_STRING(L"371055.1351");  // 海拔高度
callBackReg.RegistrationContext = NULL;  //传递给回调例程的上下文
callBackReg.OperationRegistration = &callBackOper;  // 指向注册回调操作类型的结构体

在注册 OB_CALLBACK_REGISTRATION 结构体的时候我们需要注意,Altitude 参数是一个 UNICODE 字符串,且数值越大越后面执行。比如,系统中若有海拔高度为 1000 和 2000 的两个回调函数,那么则会先执行海拔高度为 1000 的回调函数,后执行海拔高度为 2000 的回调函数。

接下来我们继续填充第二个结构体:

callBackOper.ObjectType = PsProcessType;  // 指定该回调用于进程句柄
callBackOper.Operations = OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE;  // 回调拦截的操作
callBackOper.PreOperation = ObProcessCallBack;  // 进程或线程创建或复制前执行的回调函数
callBackOper.PostOperation = NULL;  // 进程或线程创建或复制后执行的回调函数

第一个参数为回调函数的拦截类型,可以是进程句柄类型(PsProcessType)、线程句柄类型(PsThreadType)和桌面句柄类型(ExDesktopObjectType),桌面句柄类型只有 win10 及以上系统支持,平时开发用的很少。其中最关键的是要指定我们需要注册的回调函数,也就是 PreOperation 参数,该参数指定的回调函数会在拦截的特定操作之前执行,我们编写并指定回调函数 ObProcessCallBack:

OB_PREOP_CALLBACK_STATUS ObProcessCallBack(PVOID RegistrationContext,
	POB_PRE_OPERATION_INFORMATION OperationInformation)
{
	UNREFERENCED_PARAMETER(RegistrationContext);

	PEPROCESS eprocess{ 0 };
	PsLookupProcessByProcessId(g_pid, &eprocess);
	if (OperationInformation->Operation == OB_OPERATION_HANDLE_CREATE
		|| OperationInformation->Operation == OB_OPERATION_HANDLE_DUPLICATE)
	{
		// 如果是需要保护的进程,则对创建和复制操作进行降权
		if (OperationInformation->Object == eprocess)
		{
			OperationInformation->Parameters->CreateHandleInformation.DesiredAccess = 0;
		}
	}

	return OB_PREOP_SUCCESS;
}

上面的回调函数会检查每一个想要打开我们进程的操作,不管该操作是来自系统还是其他用户,我们都会将该操作的权限降为 0,来拒绝对我们进程的访问,达到保护进程的目的。

然后我们只需要注册该回调函数即可:

ObRegisterCallbacks(&callBackReg, &g_callback_handle);  // 注册回调函数

当然我们必须要在驱动卸载函数中卸载我们注册的所有回调函数,否则会蓝屏:

// 卸载回调函数
if (g_callback_handle)
{
    ObUnRegisterCallbacks(g_callback_handle);
}

最后我们就可以来对该驱动进行测试了,在虚拟机中打开 notepad.exe,然后将 pid 填入代码,加载驱动,用 CE 来打开进程列表:

我们可以看到,notepad 软件的图标已经没有了,那么我们就算附加了进程也无法看到任何的数据,因为在 CE 打开进程的时候已经被我们进行了降权处理,比如我们可以点击 查看内存

至此我们就实现了对特定进程的保护。

posted @ 2024-08-11 10:28  lostin9772  阅读(7)  评论(0)    收藏  举报