关于进程保护,在 64 位版的 Windows7 操作系统中不能通过 HOOK SSDT 等方式来实现,因为会触发 PatchGuard 保护造成蓝屏。在本文中通过内核函数 ObRegisterCallbacks 来实现对一般进程的保护。

函数 ObRegisterCallbacks 的定义如下:

NTSTATUS ObRegisterCallbacks (
  _In_  POB_CALLBACK_REGISTRATION CallBackRegistration,
  _Out_ PVOID                    *RegistrationHandle
);

首先需要看的是在微软 MSDN 中的 WDK 文档对 ObRegisterCallbacks 函数的解释。

The ObRegisterCallbacks routine registers a list of callback routines for thread, process, and desktop handle operations.

“ObRegisterCallbacks 例程为线程、进程和桌面句柄操作注册一个回调例程列表。”通过这个函数注册一个回调函数,在回调处理函数中执行我们需要执行的保护操作。

再来看看 ObRegisterCallbacks 的两个参数,第二个参数是指向注册句柄的指针,通过这个句柄对注册回调进行卸载,目前先不讨论。第一个参数 CallBackRegistration 是个指向 OB_CALLBACK_REGISTRATION 类型的结构体的指针。当 ObRegisterCallbacks 例程注册 ObjectPreCallback 或 ObjectPostCallback 回调例程时这个结构体指定回调例程和其他注册信息的的列表。其结构如下:

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

其中第五个成员 OperationRegistration 是关键部分。这是个指向 OB_OPERATION_REGISTRATION 类型结构体数组的指针。每个 OB_OPERATION_REGISTRATION 结构体指定 ObjectPreCallback 和 ObjectPostCallback 回调例程以及那些例程被调用的操作类型。其结构如下:

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;

在这里需要注意的是第三个成员 PreOperation。这是个指向 ObjectPreCallback 例程的指针,系统会在请求的操作发生之前调用这个例程,通过这个 ObjectPreCallback 例程来达到我们的目的。现在来看看 ObjectPreCallback 的具体定义:

OB_PREOP_CALLBACK_STATUS ObjectPreCallback (
  _In_ PVOID                         RegistrationContext,
  _In_ POB_PRE_OPERATION_INFORMATION OperationInformation
);

我们在后面实现的回调处理函数也必须以这样的格式来定义。接下来就需要实现注册这个例程的代码实现部分。

PVOID obCallbackHandle = NULL; // void* variable, the second perameter of ObRegisterCallbacks

NTSTATUS PreventKillingProcess (void)
{
    OB_CALLBACK_REGISTRATION obReg;
    OB_OPERATION_REGISTRATION opReg;

    memset(&obReg, 0, sizeof(obReg));
    obReg.Version = ObGetFilterVersion();
    obReg.OperationRegistrationCount = 1;
    obReg.RegistrationContext = NULL;
    RtlInitUnicodeString(&obReg.Altitude, L"321000");

    memset(&opReg, 0, sizeof(opReg)); // init structure viriable

    opReg.ObjectType = PsProcessType;
    opReg.Operations = OB_OPERATION_HANDLE_CREATE|OB_OPERATION_HANDLE_DUPLICATE; 

    opReg.PreOperation = (POB_PRE_OPERATION_CALLBACK)&fnPreCallbackRoutine; //set the callback routine pointer
    obReg.OperationRegistration = &opReg; // bind

    return ObRegisterCallbacks(&obReg, &obCallbackHandle); // register the callback routine
}

以上是注册回调的函数代码。这时候会发现最关键的回调处理函数 fnPreCallbackRoutine 并没有实现,那么接下来就需要实现这部分的代码了。

fnPreCallbackRoutine 是个返回值为 OB_PRE_OPERATION_CALLBACK 的函数,也就是我们要实现的回调处理函数。在这个函数中,通过判断进程名确定是否为指定的目标进程,并将访问权限成员 pOperationInformation->Parameters->CreateHandleInformation.DesiredAccess 中的进程关闭、操作、读写等权限标志位全部去掉。DesiredAccess 是一个 ACCESS_MASK 类型的结构体成员,它的值指定授予给句柄的访问权限,这个成员默认情况下和 OriginalDesiredAccess 相同,但是 ObjectPreCallback 例程可以修改这个值来限制授权的访问。其实现代码如下:

OB_PREOP_CALLBACK_STATUS 
fnPreCallbackRoutine (
    PVOID RegistrationContext,
    POB_PRE_OPERATION_INFORMATION pOperationInformation
)
{
    HANDLE pid = PsGetProcessId((PEPROCESS)pOperationInformation->Object);
    char szProcName[16] = {0};
    UNREFERENCED_PARAMETER(RegistrationContext);
    strcpy(szProcName,GetProcessNameByProcessId(pid));
    if ( !_stricmp(szProcName,"notepad.exe") )
    {
        if (pOperationInformation->Operation == OB_OPERATION_HANDLE_CREATE)
        {
            if ((pOperationInformation->Parameters->CreateHandleInformation.OriginalDesiredAccess & PROCESS_TERMINATE) == PROCESS_TERMINATE)
                pOperationInformation->Parameters->CreateHandleInformation.DesiredAccess &= ~PROCESS_TERMINATE;
            if ((pOperationInformation->Parameters->CreateHandleInformation.OriginalDesiredAccess & PROCESS_VM_OPERATION) == PROCESS_VM_OPERATION)
                pOperationInformation->Parameters->CreateHandleInformation.DesiredAccess &= ~PROCESS_VM_OPERATION;
            if ((pOperationInformation->Parameters->CreateHandleInformation.OriginalDesiredAccess & PROCESS_VM_READ) == PROCESS_VM_READ)
                pOperationInformation->Parameters->CreateHandleInformation.DesiredAccess &= ~PROCESS_VM_READ;
            if ((pOperationInformation->Parameters->CreateHandleInformation.OriginalDesiredAccess & PROCESS_VM_WRITE) == PROCESS_VM_WRITE)
                pOperationInformation->Parameters->CreateHandleInformation.DesiredAccess &= ~PROCESS_VM_WRITE;
        }
    }
    return OB_PREOP_SUCCESS;
}

为了保证程序的稳定,需要在驱动程序卸载例程中对注册回调进行卸载。

ObUnRegisterCallbacks(obCallbackHandle);

另外需要注意的是,在使用 ObCallBackxx 函数和 PsxxEx 函数的时候,在这些函数的内部实现中都会去调用 MmVerifyCallbackFunction() 来进行校验,所以需要对其进行绕过。绕过的代码如下:

PLDR_DATA ldr;
ldr = (PLDR_DATA)DriverObject->DriverSection;
ldr->Flags |= 0x20;

微软在 MmVerifyCallbackFunction 函数中是通过检测驱动对象的 ((PLDR_DATA_TABLE_ENTRY)pDriverObj->DriverSection)->Flags 的 0x20 标志位来进行校验的,所以只需要修改 Flags 的这个标志位即可。

小结:

可能这种方法并不是一个十分稳固和可靠的方法,仍旧可以被一些更加高深和先进的方式破解和绕过。当然,更加牛逼的技术等以后掌握了再去讨论,哈哈。

MSDN 中的相关文档:http://msdn.microsoft.com/en-us/library/ff558692(v=vs.85).aspx

- THE END -

文章链接: https://xiaodaozhi.com/kernel/4.html