• 2871阅读
  • 1回复

搞明白64位下常用于进程保护的函数ObRegisterCallbacks如何使用 [复制链接]

上一主题 下一主题
离线啊冲
 

只看楼主 倒序阅读 使用道具 楼主  发表于: 2016-02-03
这里不是说什么原理之类的..因为偶也还不清楚原理
其实就是说一个系统函数(): ObRegisterCallbacks
该API的用法之一就是用于进程保护
该函数是在Windows Vista Service Pack 1(SP1)加入的.也就是说XP不可用
对于该函数的使用,微软提供了一个例子


该函数原型为:
1
2
3
4


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


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


第一个参数CallBackRegistration是POB_CALLBACK_REGISTRATION类型,该结构如下:
1
2
3
4
5
6
7


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的描述是:
RegistrationContext
The system passes the RegistrationContext value to the callback routine when the callback routine is run. The meaning of this value is driver-defined.
我没看明白. 不过在微软提供的例程中是
1
2


TD_CALLBACK_REGISTRATION CBCallbackRegistration = {0};
CBObRegistration.RegistrationContext        = &CBCallbackRegistration;
不过网络上流传的都是填写为NULL
我们也填NULL就好了


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

1
2
3
4
5
6


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
原型如下:
1
2
3
4


OB_PREOP_CALLBACK_STATUS ObjectPreCallback(
  _In_  PVOID RegistrationContext,
  _In_  POB_PRE_OPERATION_INFORMATION OperationInformation
);
其中第一个参数是我们之前指定的POB_CALLBACK_REGISTRATION类型中的RegistrationContext 我们可以用他来做点什么
第二个参数则是操作信息是POB_PRE_OPERATION_INFORMATION类型
定义如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14


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类型,定义如下:
1
2
3
4


VOID ObjectPostCallback(
  _In_  PVOID RegistrationContext,
  _In_  POB_POST_OPERATION_INFORMATION OperationInformation
);
和PRE不同的除了返回值就只是第二个参数了
该类型为POB_POST_OPERATION_INFORMATION,定义如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15


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这个类型吧,定义如下:
1
2
3
4


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有四个成员,定义如下:
1
2
3
4
5
6


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结构
1
2
3
4
5


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是我们自己的回调函数


1
2
3
4
5
6


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结构
-----------------------------------------------
注意下这里的代码.其他地方的代码是构建一个OB_OPERATION_REGISTRATION之后在OperationRegistration时候加取指针&
而这里是用的数组.所以无需加& -->这是跟微软的例子学的
-----------------------------------------------
然后调用ObRegisterCallbacks
1
2
3


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


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


PEPROCESS MYProcess;
MYProcess = PsGetCurrentProcess();
注意MYProcess是个全局变量..
然后在CBTdPreOperationCallback回调中:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33


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进程
其他的权限同理啦..


当然也可以通过
1
2


ProcessID = PsGetProcessId((PEPROCESS)PreInfo->Object);
GetProcessNameByProcessId(ProcessID)
的方法获取PID然后
来获取进程名进行比较...这里没用这种方法只是因为网上都是这样的..所以就标新立异一下..但是有没有效果.还是大家来试试比较好

善者 慈悲心常在 无怨无恨 以苦为乐
默认压缩密码www.hifyl.com
文件分享密码问题:http://www.hifyl.com/read-htm-tid-4444.html
离线v2680267313

只看该作者 沙发  发表于: 2016-04-30
用户被禁言,该主题自动屏蔽!
快速回复
限100 字节
批量上传需要先选择文件,再选择上传
 
上一个 下一个