通过注册回调函数保护特定进程
在 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 打开进程的时候已经被我们进行了降权处理,比如我们可以点击 查看内存
:
至此我们就实现了对特定进程的保护。