首页
社区
课程
招聘
[原创] ObRegisterCallbacks 保护进程对象以及反ObRegisterCallbacks的分析与实现
发表于: 2018-12-28 18:34 20642

[原创] ObRegisterCallbacks 保护进程对象以及反ObRegisterCallbacks的分析与实现

2018-12-28 18:34
20642

一、ObRegisterCallbacks的实现

ObRegisterCallbacks可以在调用在NtOpenProcess调用时进行权限的过滤, 比如清除(终止进程)PROCESS_TERMINATE, (对进程内存读写)PROCESS_VM_OPERATION 等操作。

实现代码就直接给出非常简单的几行代码。

#include <ntddk.h>

PVOID pRegistrationHandle;
VOID DriverUnload(
    _In_ struct _DRIVER_OBJECT *DriverObject
)
{
    KdPrint(("DriverUnload\n"));

    // 卸载驱动
    if (NULL != pRegistrationHandle)
    {
        KdPrint(("ObUnRegisterCallbacks\n"));
        ObUnRegisterCallbacks(pRegistrationHandle);
    }

    PDEVICE_OBJECT pDev;

    pDev = DriverObject->DeviceObject;

    if (NULL != pDev)
    {
        IoDeleteDevice(pDev); // 删除设备
    }
}


#define PROCESS_CREATE_PROCESS (0x80)

extern "C" UCHAR *
PsGetProcessImageFileName(
    __in PEPROCESS Process
);


//extern int* ObTypeIndexTable;
OB_PREOP_CALLBACK_STATUS PreProcessHandle(
    _In_ PVOID RegistrationContext,
    _Inout_ POB_PRE_OPERATION_INFORMATION OperationInformation
)
{
    PVOID hProcess = OperationInformation->Object; // 操作句柄

    UCHAR *szImageName = PsGetProcessImageFileName((PEPROCESS)hProcess);

    if (0 != strcmp((const char *)szImageName, "calc.exe"))
    {
        return OB_PREOP_SUCCESS;
    }

    // 区分操作类型
    switch (OperationInformation->Operation)
    {
    case OB_OPERATION_HANDLE_DUPLICATE:
        KdPrint(("OB_OPERATION_HANDLE_DUPLICATE\n"));
        break;

    case OB_OPERATION_HANDLE_CREATE: 
        KdPrint(("OB_OPERATION_HANDLE_CREATE\n"));

        OperationInformation->Parameters->CreateHandleInformation.DesiredAccess = 0;

        break;
    }

    return OB_PREOP_SUCCESS;
}

VOID PostProcessHandle(
    _In_ PVOID RegistrationContext,
    _In_ POB_POST_OPERATION_INFORMATION OperationInformation
)
{

}



#pragma code_seg("INIT")
extern "C" NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject, IN PUNICODE_STRING pRegisterPath)
{
    KdPrint(("DriverEntry\n"));
    // 指定驱动卸载函数
    pDriverObject->DriverUnload = DriverUnload;

    UNICODE_STRING usDeviceName; // 设备对象名称
    RtlInitUnicodeString(&usDeviceName, L"\\Device\\ObTestDevice");

    PDEVICE_OBJECT pDev;
    NTSTATUS status;
    status = IoCreateDevice(pDriverObject, 0, &usDeviceName, FILE_DEVICE_UNKNOWN, 0, true, &pDev);

    if (!NT_SUCCESS(status))
    {
        return status;
    }

    pDev->Flags |= DO_BUFFERED_IO;

    OB_OPERATION_REGISTRATION obr;
    obr.ObjectType = PsProcessType;
    obr.Operations = OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE; // 创建和复制
    obr.PreOperation = PreProcessHandle;
    obr.PostOperation = PostProcessHandle;

    OB_CALLBACK_REGISTRATION ocr;
    ocr.Version = OB_FLT_REGISTRATION_VERSION; // 版本
    ocr.RegistrationContext = NULL; // 自定义数据 
    ocr.OperationRegistrationCount = 1; // 回调函数个数
    ocr.OperationRegistration = &obr;
    RtlInitUnicodeString(&ocr.Altitude, L"321000"); // 加载顺序

    pRegistrationHandle = NULL;

    if (NT_SUCCESS(ObRegisterCallbacks(&ocr, &pRegistrationHandle)))
    {
        // 注册保护成功
        KdPrint(("Protect Success!!!"));
    }
    else
    {
        // 注册保护失败
        KdPrint(("Protect Failure!!!"));
    }

    return STATUS_SUCCESS;
}

但是ObRegisterCallbacks这个函数会验证驱动, 这里我们直接PATCH掉就好了。

 

用IDA加载内核查看ObRegisterCallbacks这个函数
图片描述
在调试的时候用WINDBG 把 JZ 指令替换为NOP就可以了。
我们这里保护的是计算器(calc.exe)。
驱动启动成功之后任务管理器就不管结束calc.exe进程,当然OD也不可以附加调试。
图片描述

二、如何反ObRegisterCallBacks

相对于实现,估计大家都是比较关心如何处理ObRegisterCallBacks保护的进程,对于ObRegisterCallbacks保护的进程如何使用OD进行附加调试。

首先我们打开 XT 查看一下钩子:
图片描述
发现在Object钩子下有PreOperation和PostOperation的钩子。这个就是我们驱动挂的钩子。那么XT是何如检测这个钩子的?

 

WINDBG 首先设置好符号路劲
图片描述
然后我们在我们的回调函数 PreProcessHandle 下一个断点。

bp KMDFDriver2!PreProcessHandle

运行起来就会立马断下来.

 

查看一下堆栈,确认是从NtOpenProcess调用过来的,否则继续运行。
图片描述

 

可以看到堆栈的第一层
01 ae4fb7a0 83eeb1f3 nt!ObpCallPreOperationCallbacks+0x163
是从ObpCallPreOperationCallbacks函数调用过来的。
我们切换到第一层堆栈, 然后查看一下上文的代码

.frame 1
ub ObpCallPreOperationCallbacks+0x163 L10

图片描述

 

发现他是通过CALL eax 去调用我们的回调函数的。
eax 是通过 [edi + 0x18] 获取到的。
图片描述

 

那 edi 的值是怎么来的呢。 通过查看windbg去看上面的汇编代码不好找。我们用IDA 查看一下 ObpCallPreOperationCallbacks这个函数的代码。

 

图片描述

 

我们选中edi 让edi变成高亮的状态,然后查找给edi赋值的地方。
图片描述

 

从这幅图我们可以看出
edi -> eax
eax -> edi+80h
[ebp+var_c] -> eax
eax -> [ebp+var_c]
edi -> [eax]

 

而edi + 80h中的edi 来源于上层的 nt!ObpPreInterceptHandleCreate+0x6f 传入的 eax
我们继续用IDA查看一下nt!ObpPreInterceptHandleCreate+0x6f这个函数看看eax的值是怎么来的。

 

图片描述
mov edx, _ObTypeIndexTable[eax*4]
eax的值最终来源于ObTypeIndexTable这个对象数组。但是具体的下标值我们还不知道。

 

我们在ObpPreInterceptHandleCreate下断点看一下NtOpenProcess调用时的下标的值。

bp ObpPreInterceptHandleCreate

图片描述
发现eax = 7。

 

现在我们来总结一下得到的数据
[[_ObTypeIndexTable[eax * 4] + 0x80] + 0x18] 就是我们回调函数的地址。

 

数据是找到了。但是这只是其中一个回调函数,但是ObRegisterCallbacks是可以注册多个回调函数的。其他的回调函数呢?

 

我们注意在IDA中查看这个函数
ObpCallPreOperationCallbacks

 

图片描述

 

他会去 比较 edi 和 eax的值 当他们相等的时候,回调函数就不会执行。
eax = [_ObTypeIndexTable[eax * 4] + 0x80
mov edi, [eax]
cmp edi, eax

 

他会去比较 [_ObTypeIndexTable[eax 4] + 0x80 地址的值和 [[_ObTypeIndexTable[eax 4] + 0x80]的值。
我们用windbg查看一下 [_ObTypeIndexTable[eax * 4] + 0x80的值,我们把程序运行到比较的地方

 

图片描述

dd eax
dd edi

图片描述
我们发现 [eax] 中保存了 edi 的地址.
但是 [edi] 中也保存了 eax 的地址.
看到这里大家应该知道了 其实[_ObTypeIndexTable[eax * 4] + 0x80其实是一个循环单链表的结构,如果头结点和尾节点相同代表就是一个空链表。
大家可以多注册一些回调函数 这样可以看的更明白一些。

 

好了 现在我们开始写代码,来解决 ObRegisterCallbacks 的保护。
首先我们要获取到_ObTypeIndexTable的地址。但是很不幸的是 _ObTypeIndexTable 这个全局变量不是一个导出的符号我们不能直接使用。
我们用IDA 查看一个 _ObTypeIndexTable 的交叉应用。看看能不能找到合适的函数。

 

图片描述
我找到半天发现这个函数是可以通过传入一个结构体,结构体里面设置一个索引。来获取到_ObTypeIndexTable的值。

 

完整代码如下:

#include <ntddk.h>

PVOID pRegistrationHandle;
VOID DriverUnload(
    _In_ struct _DRIVER_OBJECT *DriverObject
)
{
    KdPrint(("DriverUnload\n"));


    PDEVICE_OBJECT pDev;

    pDev = DriverObject->DeviceObject;

    if (NULL != pDev)
    {
        IoDeleteDevice(pDev); // 删除设备
    }
}

struct ObIdxInfo
{
    int idx;
    int _4;
    int _8;
    int _c;
};


void AntiObRegister()
{
    typedef int *(*ObGetObjectType)(int *);
    UNICODE_STRING name;
    RtlInitUnicodeString(&name, L"ObGetObjectType");
    ObGetObjectType GetObjectType = (ObGetObjectType)MmGetSystemRoutineAddress(&name);

    if (NULL == GetObjectType)
    {
        KdPrint(("ObGetObjectType Get Failure"));
        return ;
    }


    const int OBLink = 7; // ObRegisterCallbacks 链表 [ObTypeIndexTable[7 * 4] + 0x80]

    ObIdxInfo info;
    info.idx = OBLink;

    int *LinkContext = GetObjectType(&info._c); // 取出 OBLink的值
    int *LinkHead = (int *)((char *)LinkContext + 0x80); // 取出 + 80 
    int *NodeHead = (int *)(*LinkHead);

    if (NodeHead == LinkHead)
    {
        KdPrint(("没有检测到ObRegisterCallbacks HOOK!"));
        return ;
    }

    do
    {
        DbgPrint("PRE HOOK FUNC ADDR: %0X\n", *(int *)((char *)NodeHead + 0x18));
        NodeHead = (int *)(*NodeHead); // 指向下一个节点
    } while (LinkHead != NodeHead);

    __asm {//去掉内存保护
        cli
        mov  eax, cr0
        and  eax, not 10000h
        mov  cr0, eax
    }

    KdPrint(("清除所有HOOK\n"));
    *LinkHead = (int)LinkHead; // 将下一个节点指向自己 变成一个空链表

    __asm {//恢复内存保护  
        mov  eax, cr0
        or eax, 10000h
        mov  cr0, eax
        sti
    }


}


#pragma code_seg("INIT")
extern "C" NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject, IN PUNICODE_STRING pRegisterPath)
{
    KdPrint(("DriverEntry\n"));
    // 指定驱动卸载函数
    pDriverObject->DriverUnload = DriverUnload;

    UNICODE_STRING usDeviceName; // 设备对象名称
    RtlInitUnicodeString(&usDeviceName, L"\\Device\\ObAntiTest");

    PDEVICE_OBJECT pDev;
    NTSTATUS status;
    status = IoCreateDevice(pDriverObject, 0, &usDeviceName, FILE_DEVICE_UNKNOWN, 0, true, &pDev);

    if (!NT_SUCCESS(status))
    {
        return status;
    }

    pDev->Flags |= DO_BUFFERED_IO;

    AntiObRegister();

    return STATUS_SUCCESS;
}

编译完成我们运行一下,在DebugView中会输出信息
图片描述
会打印出所有的回调函数并且清除回调函数。这个时候我们就可以用OD附加调试了。


[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 2
支持
分享
最新回复 (16)
雪    币: 2391
活跃值: (309)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
2
沙发
2018-12-28 18:41
0
雪    币: 10704
活跃值: (809)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
给力,新思路搞定callback,
2018-12-28 19:11
0
雪    币: 5734
活跃值: (1737)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
4
没什么卵用 
2018-12-28 20:26
0
雪    币: 6588
活跃值: (4032)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
有个屁用 全是硬编码、。
2018-12-28 21:27
0
雪    币: 18
活跃值: (1059)
能力值: ( LV7,RANK:110 )
在线值:
发帖
回帖
粉丝
6
zhatian 有个屁用 全是硬编码、。
喊楼主给一份商业级别的代码出来,这样你这样的伸手党就可以直接使用了,所以现在的人不愿意分享技术了,就是因为你这种伸手党存在。
2018-12-29 04:00
2
雪    币: 1485
活跃值: (1135)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
技术贴必须支持。好像楼主分享精神,兼容性可以直接处理吧。请问一下有没有一种通杀系统所有hook的办法?包括irp fsd ssdt ssdt idt等hook
2018-12-29 07:51
0
雪    币: 712
活跃值: (121)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
直接hook ObRegisterCallbacks 可以不?
2018-12-29 09:08
0
雪    币: 2391
活跃值: (309)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
9
whathhh 直接hook ObRegisterCallbacks 可以不?
也是可以的,那样是在别人的驱动加载之前你去HOOK ObRegisterCallbacks。本文主要了解一下 CALLBACKS的调用流程
2018-12-29 09:33
0
雪    币: 6588
活跃值: (4032)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
你不扣代码?  你牛叉。。。       傻子才不扣代码
2018-12-29 17:05
0
雪    币: 6588
活跃值: (4032)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
猪会被杀掉 喊楼主给一份商业级别的代码出来,这样你这样的伸手党就可以直接使用了,所以现在的人不愿意分享技术了,就是因为你这种伸手党存在。
你不扣代码?  你牛叉。。。       傻子才不扣代码
2018-12-29 17:05
0
雪    币: 712
活跃值: (121)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
你不扣代码?  你牛叉。。。       傻子才不扣代码
2019-1-3 16:01
0
雪    币: 144
活跃值: (335)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
也可以枚举句柄,然后全部ObUnRegisterCallbacks的吧
2019-1-23 17:27
0
雪    币: 200
活跃值: (25)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
人在塔在 也可以枚举句柄,然后全部ObUnRegisterCallbacks的吧
这样的话对于某P驱动是没有用的吧,它会定时检查OB回调没有的话会重新创建,所以我觉得还是在驱动加载前修改某P的线程代码
2019-3-31 12:14
0
雪    币: 79
活跃值: (59)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
比较完整的代码,谢谢分享!
2022-11-13 16:43
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
16
我有个简单的思路:可以再注册一个执行顺序在它之后的precallback把权限拉满
2023-9-7 10:41
0
雪    币: 237
活跃值: (656)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
江寒夜 这样的话对于某P驱动是没有用的吧,它会定时检查OB回调没有的话会重新创建,所以我觉得还是在驱动加载前修改某P的线程代码
为啥不直接把callback ret 掉呢
6天前
0
游客
登录 | 注册 方可回帖
返回
//