首页
社区
课程
招聘
[原创]搞明白64位下常用于进程保护的函数ObRegisterCallbacks如何使用
发表于: 2014-5-19 17:33 19574

[原创]搞明白64位下常用于进程保护的函数ObRegisterCallbacks如何使用

2014-5-19 17:33
19574
各种搜索引擎查了很多"64位进程保护"的东西
发现基本都一样..最后发现原来都来自于我大看雪的:
"教你在64位Win7系统下使用ObRegisterCallbacks内核函数来实现进程保护"
http://bbs.pediy.com/showthread.php?t=168023
的这篇文章..

各个站都是各种转帖..里面的很多东西都没搞明白
当然这里不是说什么原理之类的..因为偶也还不清楚原理
其实我也就是说下这个系统函数: ObRegisterCallbacks
该API的用法之一就是用于进程保护

注意:该函数是在Windows Vista Service Pack 1(SP1)加入的.也就是说XP不可用

对于该函数的使用,微软提供了一个例子

该函数原型为:

NTSTATUS ObRegisterCallbacks(
  _In_   POB_CALLBACK_REGISTRATION CallBackRegistration,
  _Out_  PVOID *RegistrationHandle
);
这个函数是用于注册回调的(从名字就能看出来)

其第二个参数RegistrationHandle实质上指向了一个内部结构.我们用的时候可以当一个句柄看
该通过该指针我们可以使用ObUnRegisterCallbacks来解除我们注册的回调

第一个参数CallBackRegistration是POB_CALLBACK_REGISTRATION类型,该结构如下:

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

第一个成员Version我们需要设置为OB_FLT_REGISTRATION_VERSION
第二个成员OperationRegistrationCount是最后一个成员(数组)的数量
第三个成员Altitude需要单独向微软申请.微软提供了一个表来记录这些值,网络上都用的321000这个值.他是Norman的nvcmflt.sys驱动的值,不知道为何网络上都是这个.这里咱们谨慎起见也先用这个吧
第四个成员RegistrationContext在MSDN的描述是:
The system passes the RegistrationContext value to the callback routine when the callback routine is run. The meaning of this value is driver-defined.

我没看明白. 不过在微软提供的例程中是这么写的:
TD_CALLBACK_REGISTRATION CBCallbackRegistration = {0};
CBObRegistration.RegistrationContext        = &CBCallbackRegistration;
不过网络上流传的都是填写为NULL
我们也填NULL就好了

第五个成员比较重要.是OB_OPERATION_REGISTRATION类型的数组,该数组指定了要挂的回调

其类型定义如下:

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;

第一个成员为POBJECT_TYPE,MSDN上要求填写为PsProcessType或PsThreadType从名字可以看出来是进程操作或线程操作

第二个成员为Operations,也就是操作类型.微软要求填写为:OB_OPERATION_HANDLE_CREATE或OB_OPERATION_HANDLE_DUPLICATE

        指定了OB_OPERATION_HANDLE_CREATE的话看起来是指线程/进程被创建时调用回调,但是实际上所有的open操作都被包含在内,发生Open操作时就会调用回调

        指定了OB_OPERATION_HANDLE_DUPLICATE则是当进程/线程句柄被dup(复制)的操作通知回调

        微软说Specify one or more of the following flags那么意思就是说可以同时指定..那么..我们可以用|来同时指定

        PS:用户态(ring3)打开一个handle,在内核实际上是创建一个关联.也就是create一个句柄出来.所以这里是create

第三个成员PreOperation是操作前回调函数的指针.该回调会在操作发生前被调用
该成员要求类型为ObjectPreCallback,原型如下:

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

其中第一个参数是我们之前指定的POB_CALLBACK_REGISTRATION类型中的RegistrationContext 我们可以用他来做点什么

第二个参数则是操作信息是POB_PRE_OPERATION_INFORMATION类型

        定义如下:

        typedef struct _OB_PRE_OPERATION_INFORMATION {
          OB_OPERATION                 Operation;
          union {
                ULONG  Flags;
                struct {
                  ULONG KernelHandle  :1;
                  ULONG Reserved  :31;
                };
          };
          PVOID                        Object;
          POBJECT_TYPE                 ObjectType;
          PVOID                        CallContext;
          POB_PRE_OPERATION_PARAMETERS Parameters;
        } OB_PRE_OPERATION_INFORMATION, *POB_PRE_OPERATION_INFORMATION;
        我们只看我们关注的:

        Operation表示操作类型,就是之前的

        OB_OPERATION_HANDLE_CREATE或OB_OPERATION_HANDLE_DUPLICATE

        objectType则说明了是PsProcessType还是PsThreadType

        Object则是被open进程的PEPROCESS

        KernelHandle 则说明了这个操作是不是来自内核(实际上是告知我们是否用的内核句柄.但是一般来说如果这里是TRUE则直接放行吧)

OB_OPERATION_REGISTRATION的第四个成员PostOperation是操作后回调函数的指针,该回调会在操作完成后被调用

        该成员要求ObjectPostCallback类型,定义如下:

        VOID ObjectPostCallback(
          _In_  PVOID RegistrationContext,
          _In_  POB_POST_OPERATION_INFORMATION OperationInformation
        );
        和PRE不同的除了返回值就只是第二个参数了

        该类型为POB_POST_OPERATION_INFORMATION,定义如下:

        typedef struct _OB_POST_OPERATION_INFORMATION {
          OB_OPERATION                  Operation;
          union {
                ULONG  Flags;
                struct {
                  ULONG KernelHandle  :1;
                  ULONG Reserved  :31;
                };
          };
          PVOID                         Object;
          POBJECT_TYPE                  ObjectType;
          PVOID                         CallContext;
          NTSTATUS                      ReturnStatus;
          POB_POST_OPERATION_PARAMETERS Parameters;
        } OB_POST_OPERATION_INFORMATION, *POB_POST_OPERATION_INFORMATION;
        他与之前的OB_PRE_OPERATION_INFORMATION完全一致.就不再说了

        不过这里也单独说一下POB_POST_OPERATION_PARAMETERS这个类型吧,定义如下:

        typedef union _OB_POST_OPERATION_PARAMETERS {
          OB_POST_CREATE_HANDLE_INFORMATION    CreateHandleInformation;
          OB_POST_DUPLICATE_HANDLE_INFORMATION DuplicateHandleInformation;
        } OB_POST_OPERATION_PARAMETERS, *POB_POST_OPERATION_PARAMETERS;
        根据上面的Operation来确定是访问哪个

        如果Operation是OB_OPERATION_HANDLE_CREATE那么我们就关注CreateHandleInformation
        如果Operation是OB_OPERATION_HANDLE_DUPLICATE那么我们就关注DuplicateHandleInformation

        而这两个类型基本上完全一致,其中有一个叫GrantedAccess的ACCESS_MASK类型的成员

而PRE的有所区别:

CreateHandleInformation有两个成员(均是ACCESS_MASK类型)

1是DesiredAccess
2是OriginalDesiredAccess

其中OriginalDesiredAccess是要的权限,而DesiredAccess则是真正给的权限
在PRE中可以修改DesiredAccess来取消某些权限如THREAD_TERMINATE

DuplicateHandleInformation有四个成员,定义如下:

typedef struct _OB_PRE_DUPLICATE_HANDLE_INFORMATION {
  ACCESS_MASK DesiredAccess;
  ACCESS_MASK OriginalDesiredAccess;
  PVOID       SourceProcess;
  PVOID       TargetProcess;
} OB_PRE_DUPLICATE_HANDLE_INFORMATION, *POB_PRE_DUPLICATE_HANDLE_INFORMATION;
其中前两个与CreateHandleInformation相同

SourceProcess是复制句柄的进程的结构
TargetProcess则是将要得到复制后句柄的进程的结构

我们可以通过判断TargetProcess来确定得到句柄的进程(因为此时我们的代码运行在SourceProcess的上下文中.所以没办法知道目标进程.需要从这个参数里面得到)
需要注意的是.在dup(复制句柄)的时候,我们不能向DesiredAccess中添加SourceProcess没有的权限...当然还是可以删除权限的

PS:想知道SourceProcess有哪些权限.可以看看OB_POST_CREATE_HANDLE_INFORMATION里面写了哪些

那么现在我们就明白该如何用这个函数了
首先我们使用ObRegisterCallbacks来注册一个回调,注册时候需要构建一下结构体
首先构建OB_OPERATION_REGISTRATION结构

OB_OPERATION_REGISTRATION CBOperationRegistrations[0];
CBOperationRegistrations[0].ObjectType = PsProcessType;
CBOperationRegistrations[0].Operations = OB_OPERATION_HANDLE_CREATE|OB_OPERATION_HANDLE_DUPLICATE;
CBOperationRegistrations[0].PreOperation = CBTdPreOperationCallback;
CBOperationRegistrations[0].PostOperation = CBTdPostOperationCallback;
因为我们只挂一个回调.所以数组只有一个成员就可以了

CBTdPreOperationCallback和CBTdPostOperationCallback是我们自己的回调函数

OB_CALLBACK_REGISTRATION  CBObRegistration;
CBObRegistration.Version                    = OB_FLT_REGISTRATION_VERSION;
CBObRegistration.OperationRegistrationCount = 1;
RtlInitUnicodeString (&CBObRegistration.Altitude , L"321000");
CBObRegistration.RegistrationContext        = NULL;
CBObRegistration.OperationRegistration      = CBOperationRegistrations;
构建好OB_CALLBACK_REGISTRATION结构

然后调用ObRegisterCallbacks

NTSTATUS Status = STATUS_SUCCES;
PVOID pCBRegistrationHandle = NULL;
Status = ObRegisterCallbacks(&CBObRegistration,&pCBRegistrationHandle);
注意这里pCBRegistrationHandle请定义为全局变量.因为后续unreg的时候还要用到的

我们在在需保护进程的上下文中执行:

PEPROCESS MYProcess;
MYProcess = PsGetCurrentProcess();
注意MYProcess是个全局变量..

然后在CBTdPreOperationCallback回调中:

OB_PREOP_CALLBACK_STATUS CBTdPreOperationCallback (IN PVOID RegistrationContext,INOUT POB_PRE_OPERATION_INFORMATION PreInfo)
{
    if(PreInfo->KernelHandle == TRUE)
    {
        //如果是KernelHandle则直接返回不做操作
        return(OB_PREOP_SUCCESS);
    }
    if (PreInfo->ObjectType == *PsProcessType)
    {
        //这里做PsProcessType相关判断
        if(MYProcess != PreInfo->Object)
        {
            //不是我们需要保护的进程则直接返回不操作
            return(OB_PREOP_SUCCESS);
        }
        if(PreInfo->Operation == OB_OPERATION_HANDLE_CREATE)
        {
            //判断是否有PROCESS_TERMINATE
            if(PreInfo->Parameters->CreateHandleInformation->OriginalDesiredAccess & PROCESS_TERMINATE)
                {
                    //有则去掉PROCESS_TERMINATE标记防止进程被结束
                    PreInfo->Parameters->CreateHandleInformation->DesiredAccess =
                    PreInfo->Parameters->CreateHandleInformation->OriginalDesiredAccess & ~PROCESS_TERMINATE;
                    //这里我用的是OriginalDesiredAccess作为基准,实际上从兼容性考虑应该直接使用DesiredAccess
                    return(OB_PREOP_SUCCESS);
                }
        }
    }
    if (PreInfo->ObjectType == *PsThreadType)
    {
        //这里做PsThreadType相关判断
    }
}
这样就可以非常简单的做下进程的防护..当然漏不少地方.都可以在回调中添加.比如这里只是简单的防止带PROCESS_TERMINATE来Open进程

看雪没找到怎么发代码的格式..所以看着有点乱.有愿意的可以去我博客看下
http://blog.shajincheng.com/post/67.html

[课程]Android-CTF解题方法汇总!

收藏
免费 4
支持
分享
最新回复 (9)
雪    币: 220
活跃值: (117)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
2
因为我没有x64适合的环境..所以只是在Notepad++中敲的代码...
所以难免会有问题...

并且因为没有经过调试和验证.逻辑上也许也有问题.希望大家指出...
2014-5-19 17:35
0
雪    币: 11
活跃值: (40)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3

这里已经有了完整编译的代码http://bbs.pediy.com/showthread.php?p=1280620#post1280620
2014-5-19 17:57
0
雪    币: 11
活跃值: (25)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
4
……楼主一个虚拟机都装不起吗。。。
2014-5-19 17:57
0
雪    币: 220
活跃值: (117)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
5
没有任何关键字..根本搜索不到...
我找了半天就是那一个帖子而已...

唉.希望发这些教程啥的可以贴点关键词方便搜索啊..
2014-5-19 18:12
0
雪    币: 11
活跃值: (40)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
要求太高
2014-5-19 18:16
0
雪    币: 220
活跃值: (117)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
7
可能看起来是要求很高..但是实际上如果不知道有这么个东西.就根本找不到他..

比如每张的开头一小点内容也行= =~!
2014-5-19 18:20
0
雪    币: 77
活跃值: (48)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
mark
2014-5-20 09:06
0
雪    币: 142
活跃值: (55)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
楼主你这个在win8下面好像不灵吧
2014-8-5 11:08
0
雪    币: 40
活跃值: (274)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
解释的很详细,楼主挺用心的嘛
2014-11-13 13:23
0
游客
登录 | 注册 方可回帖
返回
//