基于ObRegisterCallbacks实现的线程和进程监控及其保护

要实现监控系统线程和进程,并实现对指定线程和进程的保护,在 32 位系统上可以使用 HOOK 技术,HOOK 相关的函数来实现。但是,到了 64 位平台上,就不能继续按常规的 HOOK 方法去实现了。

好在 Windows 给我们提供了 ObRegisterCallbacks 内核函数来注册系统回调,可以用来注册系统线程回调,监控系统的线程创建、退出等情况,而且还能进行控制;也可以用来注册系统进程回调,可以监控系统的进程创建、退出等情况,而且也能进行控制。这使得我们实现保护指定线程、进程不被结束,提供了可能。

现在,我就对程序的实现过程和原理进行整理,形成文档,分享给大家。

函数介绍

ObRegisterCallbacks 函数

注册线程、进程和桌面句柄操作的回调函数。

函数声明

 
  1. NTSTATUS ObRegisterCallbacks(
  2. _In_ POB_CALLBACK_REGISTRATION CallBackRegistration,
  3. _Out_ PVOID *RegistrationHandle
  4. );

参数

  • CallBackRegistration [in]
    指向指定回调例程列表和其他注册信息的OB_CALLBACK_REGISTRATION结构的指针。
  • RegistrationHandle [out]
    指向变量的指针,该变量接收一个标识已注册的回调例程集合的值。 调用者将此值传递给ObUnRegisterCallbacks例程以注销该回调集。

返回值

  • 成功,则返回 STATUS_SUCCESS,否则,返回其它 NTSTATUS 错误码。

备注

  • 驱动程序必须在卸载之前注销所有回调例程。 您可以通过调用ObUnRegisterCallbacks例程来注销回调例程。

OB_CALLBACK_REGISTRATION 结构体

 
  1. typedef struct _OB_CALLBACK_REGISTRATION {
  2. USHORT Version;
  3. USHORT OperationRegistrationCount;
  4. UNICODE_STRING Altitude;
  5. PVOID RegistrationContext;
  6. OB_OPERATION_REGISTRATION *OperationRegistration;
  7. } OB_CALLBACK_REGISTRATION, *POB_CALLBACK_REGISTRATION;

成员

  • Version
    请求的对象回调注册版本。 驱动程序应指定OB_FLT_REGISTRATION_VERSION。
  • OperationRegistrationCount
    OperationRegistration数组中的条目数。
  • Altitude
    指定驱动程序Altitude的Unicode字符串。一定要存在,不能置为空 NULL,可以任意指定。
  • RegistrationContext
    当回调例程运行时,系统将RegistrationContext值传递给回调例程。 该值的含义是由驱动程序自定义的。
  • OperationRegistration
    指向OB_OPERATION_REGISTRATION结构数组的指针。 每个结构指定ObjectPreCallback和ObjectPostCallback回调例程以及调用例程的操作类型。

OB_OPERATION_REGISTRATION 结构体

 
  1. typedef struct _OB_OPERATION_REGISTRATION {
  2. POBJECT_TYPE *ObjectType;
  3. OB_OPERATION Operations;
  4. POB_PRE_OPERATION_CALLBACK PreOperation;
  5. POB_POST_OPERATION_CALLBACK PostOperation;
  6. } OB_OPERATION_REGISTRATION, *POB_OPERATION_REGISTRATION;

成员

  • ObjectType
    指向触发回调例程的对象类型的指针。 指定以下值之一:
    用于进程句柄操作的PsProcessType
    线程句柄操作的PsThreadType
    用于桌面句柄操作的ExDesktopObjectType。 此值在Windows 10中受支持,而不在早期版本的操作系统中。
  • Operations
    指定以下一个或多个标志:
    OB_OPERATION_HANDLE_CREATE
    一个新的进程,线程或桌面句柄已被打开或将被打开。
    OB_OPERATION_HANDLE_DUPLICATE
    进程,线程或桌面句柄已被或将被复制。
  • PreOperation
    指向ObjectPreCallback例程的指针。 在请求的操作发生之前,系统调用此例程。
  • PostOperation
    指向ObjectPostCallback例程的指针。 在请求的操作发生后,系统调用此例程。

IoThreadToProcess 函数

返回指向指定线程的进程的指针。

函数声明

 
  1. PEPROCESS IoThreadToProcess(
  2. _In_ PETHREAD Thread
  3. );

参数

  • Thread[in]
  • 要返回进程的指定线程对象。

返回值

  • 返回线程对象对应的进程对象的指针。

PsGetProcessId 函数

返回与指定进程关联的进程标识符(进程ID)。

函数声明

 
  1. HANDLE PsGetProcessId(
  2. _In_ PEPROCESS Process
  3. );

参数

  • Process[in]

    指向进程对象结构的指针。

返回值

  • 返回进程ID。

实现原理

破解 ObRegisterCallbacks 函数的使用限制

第一种方法

在讲解怎么使用 ObRegisterCallbacks 函数来注册系统线程、进程回调的之前,先来讲解下 Windows 对这个函数做的限制:驱动程序必须有数字签名才能使用此函数。不过国外的黑客对此限制很不满,通过逆向 ObRegisterCallbacks,找到了
破解这个限制的方法。经研究,内核通过 MmVerifyCallbackFunction 验证此回调
是否合法, 但此函数只是简单的验证了一下 DriverObject->DriverSection->Flags 的值是不是为 0x20:

 
  1. nt!MmVerifyCallbackFunction+0x75:
  2. fffff800`01a66865 f6406820 test byte ptr [rax+68h],20h
  3. fffff800`01a66869 0f45fd cmovne edi,ebp

所以破解方法非常简单,只要把 DriverObject->DriverSection->Flags 的值按位或 0x20 即可。其中,DriverSection 是指向 LDR_DATA_TABLE_ENTRY 结构的值,要注意该结构在 32 位和 64 位系统下的定义。

 

// 注意32位与64位的对齐大小

// 注意32位与64位的对齐大小
#ifndef _WIN64
    #pragma pack(1)                               
#endif
typedef struct _LDR_DATA_TABLE_ENTRY
{
    LIST_ENTRY InLoadOrderLinks;
    LIST_ENTRY InMemoryOrderLinks;
    LIST_ENTRY InInitializationOrderLinks;
    PVOID DllBase;
    PVOID EntryPoint;
    ULONG SizeOfImage;
    UNICODE_STRING FullDllName;
    UNICODE_STRING BaseDllName;
    ULONG Flags;
    USHORT LoadCount;
    USHORT TlsIndex;
    union
    {
        LIST_ENTRY HashLinks;
        struct
        {
            PVOID SectionPointer;
            ULONG CheckSum;
        };
    };
    union
    {
        ULONG TimeDateStamp;
        PVOID LoadedImports;
    };
    PVOID EntryPointActivationContext;
    PVOID PatchInformation;
    LIST_ENTRY ForwarderLinks;
    LIST_ENTRY ServiceTagLinks;
    LIST_ENTRY StaticLinks;
} LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;
#ifndef _WIN64
    #pragma pack()
#endif

第二种方法

使用此函数, 一定要设置 IMAGE_OPTIONAL_HEADER 中的 DllCharacterisitics 字段设置为:IMAGE_DLLCHARACTERISITICS_FORCE_INTEGRITY 属性,该属性是一个驱动强制签名属性。
使用 VS2013 开发环境设置方式是:

  1. 右击项目,选择属性;
  2. 选中配置属性中的链接器,点击命令行;
  3. 在其它选项中输入: /INTEGRITYCHECK 表示设置; /INTEGRITYCHECK:NO 表示不设置。
  4. 对于使用sources文件编译的方式,增加LINKER_FLAGS=/INTEGRITYCHECK

这样,设置之后,驱动程序必须要进行驱动签名才可正常运行!

调用 ObRegisterCallbacks 注册线程回调以及进程回调

我们从上面的函数介绍中,可以知道大概的实现流程:

  1. 首先,在调用 ObRegisterCallbacks 函数注册系统回调之前,我们要先对结构体 OB_CALLBACK_REGISTRATION 进行初始化。设置回调的版本 Version;设置回调的 Altitude,任意指定;设置回调函数的数量 OperationRegistrationCount;设置回调函数 OperationRegistration。其中,OperationRegistration 是一个 OB_OPERATION_REGISTRATION 结构体数组,里面存储着回调对象的类型、操作类型以及回调函数,它的数量要和 OperationRegistrationCount 对应。
  2. 然后,再调用 ObRegisterCallbacks 进行注册,并保留系统回调对象句柄。
  3. 最后,在不使用回调的时候,调用 ObUnRegisterCallbacks 函数传入系统回调对象句柄,删除回调。

其中,线程回调和进程回调的注册,只有 OB_OPERATION_REGISTRATION 结构体的成员 ObjectType 不同。对于线程,ObjectType 为 PsThreadType;对于进程,ObjectType 为 PsProcessType。其它的操作及其含义,均相同。

线程、进程回调函数中实现线程、进程保护

由于在注册系统回调的时候,我们设置监控线程以及进程的操作类型为:OB_OPERATION_HANDLE_CREATE 和 OB_OPERATION_HANDLE_DUPLICATE。要想实现,拒绝结束线程或者进程的操作,我们只需从操作类型句柄信息中去掉相应的结束线程或者进程的权限即可:

 
  1. // OB_OPERATION_HANDLE_CREATE 操作类型
  2. pObPreOperationInfo->Parameters->CreateHandleInformation.DesiredAccess = 0;
 
  1. // OB_OPERATION_HANDLE_DUPLICATE 操作类型
  2. pObPreOperationInfo->Parameters->DuplicateHandleInformation.DesiredAccess = 0;

那么,我们怎么从线程对象或者进程对象 pObPreOperationInfo->Object 中判断是否是保护线程或者进程呢。
对于进程,我们可以调用 PsGetProcessImageFileName 函数,从进程结构对象获取进程名称进行判断。
对于线程,我们可以通过 IoThreadToProcess 函数,根据线程结构对象获取相应的进程结构对象,再根据 PsGetProcessImageFileName 函数,从进程结构对象获取进程名称进行判断。

编码实现

破解 ObRegisterCallbacks 的使用限制

// 编程方式绕过签名检查
BOOLEAN BypassCheckSign(PDRIVER_OBJECT pDriverObject)
{
#ifdef _WIN64
    typedef struct _KLDR_DATA_TABLE_ENTRY
    {
        LIST_ENTRY listEntry;
        ULONG64 __Undefined1;
        ULONG64 __Undefined2;
        ULONG64 __Undefined3;
        ULONG64 NonPagedDebugInfo;
        ULONG64 DllBase;
        ULONG64 EntryPoint;
        ULONG SizeOfImage;
        UNICODE_STRING path;
        UNICODE_STRING name;
        ULONG   Flags;
        USHORT  LoadCount;
        USHORT  __Undefined5;
        ULONG64 __Undefined6;
        ULONG   CheckSum;
        ULONG   __padding1;
        ULONG   TimeDateStamp;
        ULONG   __padding2;
    } KLDR_DATA_TABLE_ENTRY, *PKLDR_DATA_TABLE_ENTRY;
#else
    typedef struct _KLDR_DATA_TABLE_ENTRY
    {
        LIST_ENTRY listEntry;
        ULONG unknown1;
        ULONG unknown2;
        ULONG unknown3;
        ULONG unknown4;
        ULONG unknown5;
        ULONG unknown6;
        ULONG unknown7;
        UNICODE_STRING path;
        UNICODE_STRING name;
        ULONG   Flags;
    } KLDR_DATA_TABLE_ENTRY, *PKLDR_DATA_TABLE_ENTRY;
#endif
    PKLDR_DATA_TABLE_ENTRY pLdrData = (PKLDR_DATA_TABLE_ENTRY)pDriverObject->DriverSection;
    pLdrData->Flags = pLdrData->Flags | 0x20;
    return TRUE;
}

注册线程回调

// 设置线程回调函数
NTSTATUS SetThreadCallbacks()
{
    NTSTATUS status = STATUS_SUCCESS;
    OB_CALLBACK_REGISTRATION obCallbackReg = { 0 };
    OB_OPERATION_REGISTRATION obOperationReg = { 0 };
    RtlZeroMemory(&obCallbackReg, sizeof(OB_CALLBACK_REGISTRATION));
    RtlZeroMemory(&obOperationReg, sizeof(OB_OPERATION_REGISTRATION));
    // 设置 OB_CALLBACK_REGISTRATION
    obCallbackReg.Version = ObGetFilterVersion();
    obCallbackReg.OperationRegistrationCount = 1;
    obCallbackReg.RegistrationContext = NULL;
    RtlInitUnicodeString(&obCallbackReg.Altitude, L"321001");
    obCallbackReg.OperationRegistration = &obOperationReg;
    // 设置 OB_OPERATION_REGISTRATION
    // Thread 和 Process 的区别所在
    obOperationReg.ObjectType = PsThreadType;                                   
    obOperationReg.Operations = OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE;
    // Thread 和 Process 的区别所在
    obOperationReg.PreOperation = (POB_PRE_OPERATION_CALLBACK)(&ThreadPreCall); 
    // 注册回调函数
    status = ObRegisterCallbacks(&obCallbackReg, &g_obThreadHandle);
    if (!NT_SUCCESS(status))
    {
        DbgPrint("ObRegisterCallbacks Error[0x%X]\n", status);
        return status;
    }
    return status;
}

注册进程回调

// 设置进程回调函数
NTSTATUS SetProcessCallbacks()
{
    NTSTATUS status = STATUS_SUCCESS;
    OB_CALLBACK_REGISTRATION obCallbackReg = { 0 };
    OB_OPERATION_REGISTRATION obOperationReg = { 0 };
    RtlZeroMemory(&obCallbackReg, sizeof(OB_CALLBACK_REGISTRATION));
    RtlZeroMemory(&obOperationReg, sizeof(OB_OPERATION_REGISTRATION));
    // 设置 OB_CALLBACK_REGISTRATION
    obCallbackReg.Version = ObGetFilterVersion();
    obCallbackReg.OperationRegistrationCount = 1;
    obCallbackReg.RegistrationContext = NULL;
    RtlInitUnicodeString(&obCallbackReg.Altitude, L"321000");
    obCallbackReg.OperationRegistration = &obOperationReg;
    // 设置 OB_OPERATION_REGISTRATION
    // Thread 和 Process 的区别所在
    obOperationReg.ObjectType = PsProcessType;                                   
    obOperationReg.Operations = OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE;
    // Thread 和 Process 的区别所在
    obOperationReg.PreOperation = (POB_PRE_OPERATION_CALLBACK)(&ProcessPreCall); 
    // 注册回调函数
    status = ObRegisterCallbacks(&obCallbackReg, &g_obProcessHandle);
    if (!NT_SUCCESS(status))
    {
        DbgPrint("ObRegisterCallbacks Error[0x%X]\n", status);
        return status;
    }
    return status;
}

线程回调函数

// 线程回调函数
OB_PREOP_CALLBACK_STATUS ThreadPreCall(PVOID RegistrationContext, POB_PRE_OPERATION_INFORMATION pObPreOperationInfo)
{
    PEPROCESS pEProcess = NULL;
    // 判断对象类型
    if (*PsThreadType != pObPreOperationInfo->ObjectType)
    {
        return OB_PREOP_SUCCESS;
    }
    // 获取线程对应的进程 PEPROCESS
    pEProcess = IoThreadToProcess((PETHREAD)pObPreOperationInfo->Object);
    // 判断是否市保护PID, 若是, 则拒绝结束线程
    if (IsProtectProcess(pEProcess))
    {
        // 操作类型: 创建句柄
        if (OB_OPERATION_HANDLE_CREATE == pObPreOperationInfo->Operation)
        {
            if (1 == (1 & pObPreOperationInfo->Parameters->CreateHandleInformation.OriginalDesiredAccess))
            {
                pObPreOperationInfo->Parameters->CreateHandleInformation.DesiredAccess = 0;
            }
        }
        // 操作类型: 复制句柄
        else if (OB_OPERATION_HANDLE_DUPLICATE == pObPreOperationInfo->Operation)
        {
            if (1 == (1 & pObPreOperationInfo->Parameters->DuplicateHandleInformation.OriginalDesiredAccess))
            {
                pObPreOperationInfo->Parameters->DuplicateHandleInformation.DesiredAccess = 0;
            }
        }
    }
    return OB_PREOP_SUCCESS;
}

进程回调函数

// 进程回调函数
OB_PREOP_CALLBACK_STATUS ProcessPreCall(PVOID RegistrationContext, POB_PRE_OPERATION_INFORMATION pObPreOperationInfo)
{
    PEPROCESS pEProcess = NULL;
    // 判断对象类型
    if (*PsProcessType != pObPreOperationInfo->ObjectType)
    {
        return OB_PREOP_SUCCESS;
    }
    // 获取进程结构对象
    pEProcess = (PEPROCESS)pObPreOperationInfo->Object;
    // 判断是否市保护PID, 若是, 则拒绝结束进程
    if (IsProtectProcess(pEProcess))
    {
        // 操作类型: 创建句柄
        if (OB_OPERATION_HANDLE_CREATE == pObPreOperationInfo->Operation)
        {
            if (1 == (1 & pObPreOperationInfo->Parameters->CreateHandleInformation.OriginalDesiredAccess))
            {
                pObPreOperationInfo->Parameters->CreateHandleInformation.DesiredAccess = 0;
            }
        }
        // 操作类型: 复制句柄
        else if (OB_OPERATION_HANDLE_DUPLICATE == pObPreOperationInfo->Operation)
        {
            if (1 == (1 & pObPreOperationInfo->Parameters->DuplicateHandleInformation.OriginalDesiredAccess))
            {
                pObPreOperationInfo->Parameters->DuplicateHandleInformation.DesiredAccess = 0;
            }
        }
    }
    return OB_PREOP_SUCCESS;
}

 

总结

其中要注意两个问题:

一是,破解 ObRegisterCallbacks 函数的使用限制有两种方式,一种是通过编程来解决;一种是通过 VS 开发环境和数字签名来解决。在使用编程方式解决限制的时候,一定要注意 32 位与 64 位下,LDR_DATA_TABLE_ENTRY 结构体的对齐差别。

二是,OB_CALLBACK_REGISTRATION 中的 Altitude 成员一定要存在,不能置为空 NULL,可以任意指定。(下面的关于Altitude值的具体函义定义)

About load order groups and altitudes

Windows uses a dedicated set of load order groups for file system filter drivers and legacy filter drivers that are loaded at system startup.

Each load order group has a defined range of altitudes. Every filter driver must have a unique altitude identifier. The filter's altitude defines its position relative to other filter drivers in the I/O stack when it is loaded. The altitude is an infinite-precision string interpreted as a decimal number. A filter driver that has a low numerical altitude is loaded into the I/O stack below a filter driver that has a higher numerical value.

Altitude allocation is managed by Microsoft. To request an altitude for your filter driver, see Filter Altitude Request.

Altitude values for a filter driver are specified in the Instance definitions of the Strings Section in the filter driver's INF file. Instance definitions can also be specified in calls to the InstanceSetupCallback routine in the FLT_REGISTRATION structure. Multiple instances and altitudes can be defined for a filter driver. These instance definitions apply across all volumes.

Table of load order groups and altitude ranges

The following table lists the system-defined load order groups and altitude ranges for filter drivers. For each load order group, the Load order group column contains the value that should be specified for that group in the LoadOrderGroup entry in the ServiceInstall Section of a filter's INF file. The Altitude range column contains the range of altitudes for a particular load order group.

Note that the load order groups and altitude ranges are listed as they appear on the stack, which is the reverse of the order in which they are loaded.

TABLE OF LOAD ORDER GROUPS AND ALTITUDE RANGES
Load order groupAltitude rangeDescription
Filter420000-429999This group is the same as the Filter load order group that was available on Windows 2000 and earlier. This group loads last and thus attaches furthest from the file system.
FSFilter Top400000-409999This group is provided for filter drivers that must attach above all other FSFilter types.
FSFilter Activity Monitor360000-389999This group includes filter drivers that observe and report on file I/O.
FSFilter Undelete340000-349999This group includes filters that recover deleted files.
FSFilter Anti-Virus320000-329999This group includes filter drivers that detect and disinfect viruses during file I/O.
FSFilter Replication300000-309999This group includes filter drivers that replicate file data to remote servers.
FSFilter Continuous Backup280000-289999This group includes filter drivers that replicate file data to backup media.
FSFilter Content Screener260000-269999This group includes filter drivers that prevent the creation of specific files or file content.
FSFilter Quota Management240000-249999This group includes filter drivers that provide enhanced file system quotas.
FSFilter System Recovery220000-229999This group includes filter drivers that perform operations to maintain operating system integrity, such as the System Restore (SR) filter.
FSFilter Cluster File System200000-209999This group includes filter drivers that are used in products that provide file server metadata across a network.
FSFilter HSM180000-189999This group includes filter drivers that perform hierarchical storage management.
FSFilter Imaging170000-175000This group includes ZIP-like filter drivers that provide a virtual namespace.
FSFilter Compression160000-169999This group includes filter drivers that perform file data compression.
FSFilter Encryption140000-149999This group includes filter drivers that encrypt and decrypt data during file I/O.
FSFilter Virtualization130000- 139999This group includes filter drivers that virtualize the file path, such as the Least Authorized User (LUA) filter driver added in Windows Vista.
FSFilter Physical Quota Management120000-129999This group includes filter drivers that manage quotas by using physical block counts.
FSFilter Open File100000-109999This group includes filter drivers that provide snapshots of already open files.
FSFilter Security Enhancer80000-89999This group includes filter drivers that apply lockdown and enhanced access control lists (ACLs).
FSFilter Copy Protection60000-69999This group includes filter drivers that check for out-of-band data on media.
FSFilter Bottom40000-49999This group is provided for filter drivers that must attach below all other FSFilter types.
FSFilter System20000-29999Reserved for internal use.
FSFilter InfrastructureReserved for internal use. This group loads first and thus attaches closest to the file system.
  • 8
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值