作者在总结出结论之前,最初也是因为对某些厂商在Windows系统服务层面的HIPS技术产生了浓厚的兴趣才去研究的。多半的应用层代码也是逆向自某产品的一个小模块。作者并没有其他企图,只是作为学习和成长的一个途径去研究这个课题而已。对这个模块研究完之后得到这样的结论:
1. 对服务的创建/修改的HIPS并不是内核级别的注册表拦截,而是注入到services.exe进行的暗地操作;
2. 这个应用层模块劫持了RPCRT4.dll的一些函数,即RPC Server的注册流程,同步式与内核模块实现阻断规则;
由于RPCRT4.dll在不同版本中提供了不同的服务注册接口,存在一些兼容问题,那么没办法,只有把可能途经访问到的函数都做一下劫持。
劫持点如下标出,顺序为低版本到高版本:
RPC_STATUS RPC_ENTRY RpcEpRegisterW(
RPC_IF_HANDLE IfSpec,
RPC_BINDING_VECTOR *BindingVector,
UUID_VECTOR *UuidVector,
RPC_WSTR Annotation
)
RPC_STATUS RPC_ENTRY RpcServerRegisterIfEx(
RPC_IF_HANDLE IfSpec,
UUID *MgrTypeUuid,
RPC_MGR_EPV *MgrEpv,
unsigned int Flags,
unsigned int MaxCalls,
RPC_IF_CALLBACK_FN *IfCallback
)
RPC_STATUS RPC_ENTRY RpcServerRegisterIf3(
RPC_IF_HANDLE IfSpec,
UUID *MgrTypeUuid,
RPC_MGR_EPV *MgrEpv,
unsigned int Flags,
unsigned int MaxCalls,
unsigned int MaxRpcSize,
RPC_IF_CALLBACK_FN *IfCallback,
void *SecurityDescriptor
)
在注册过程中有一个最为明显的特征,可以标识出是我们想要的RPC组件服务进行了注册,就是IfSpec->InterfaceId.SyntaxGUID,本文讨论的这个GUID,目前为止还都是同一个:0x367ABB81, 0x9844, 0x35F1, 0xAD, 0x32, 0x98, 0xF0, 0x38, 0x00, 0x10, 0x03
即 {367ABB81-9844-35F1-AD32-98F038001003}。
劫持了一些我们感兴趣的RPC函数之后,却还有一个大的问题,那就是怎么拿到客户端的进程/线程信息~
幸运的是,RPCRT4.dll提供了一些函数,可以很方便的查到客户端是谁,I_RpcOpenClientThread,
I_RpcOpenClientProcess。
而且services.exe的TOKEN所在组(应该是属于Admin),没有拒绝TOKEN的问题,但是换到一些低权限组那没办法,只能用一些别的方法了~
使用
I_RpcOpenClientThread
/
I_RpcOpenClientProcess时注意DesiredAccess不要太高就行,要啥权限就写啥权限,一般而言QUERY_INFORMATION就行。
详细的实现部分就不继续说了,见附件内容。
从操作系统的角度来说,自身集成了很多组件服务然后提供出一些调用标准供开发者使用。那么在Windows中,最常见的莫过于com组件和rpc调用了。本文主要分享的主题也就是rpc的相关劫持,相信独立写过IPC的同学都不会对rpc陌生,进程间通信方法中rpc的效率相对于其他方式有着明显的优势,而且也是Windows操作系统内部比较常用的通信方式。然而对于如何使用rpc微软官方也没有提供多少文档,尤其是在其通信标准和通信方式上,比如在通信方式上的管道、Socket以及AlpcPort/Port的协议选择,又或者是同步方式与异步方式的调用选择上都存在着一些门槛。然而本文并不是来跟大家讨论如何用rpc去做进程间通信的,而是想带大家了解下Windows操作系统内部使用rpc的部分组件并且实现对之劫持。
0x01 起源
作者在总结出结论之前,最初也是因为对某些厂商在Windows系统服务层面的HIPS技术产生了浓厚的兴趣才去研究的。多半的应用层代码也是逆向自某产品的一个小模块。作者并没有其他企图,只是作为学习和成长的一个途径去研究这个课题而已。对这个模块研究完之后得到这样的结论:
1. 对服务的创建/修改的HIPS并不是内核级别的注册表拦截,而是注入到services.exe进行的暗地操作;
2. 这个应用层模块劫持了RPCRT4.dll的一些函数,即RPC Server的注册流程,同步式与内核模块实现阻断规则;
3. services.exe具有相对较高的TOKEN令牌,很方便就可以获得到Client端的属性信息。
那么接下来就按照这个思路,自己进行一些尝试。
0x02 相关结构体
构建一个自己的rpc,首先需要一个idl接口描述,然后使用MIDL工具生成对应的客户端以及服务端的Stub文件。
服务端:
RPCRTAPI
RPC_STATUS
RPC_ENTRY
RpcServerRegisterIf (
_In_ RPC_IF_HANDLE IfSpec,
_In_opt_ UUID __RPC_FAR * MgrTypeUuid,
_In_opt_ RPC_MGR_EPV __RPC_FAR * MgrEpv
);
typedef void __RPC_FAR * RPC_IF_HANDLE;
服务端需要进行注册,最为主要的结构为
RPC_IF_HANDLE,在服务端,这个指针结构为:
typedef struct _RPC_SERVER_INTERFACE
{
unsigned int Length;
RPC_SYNTAX_IDENTIFIER InterfaceId;
RPC_SYNTAX_IDENTIFIER TransferSyntax;
PRPC_DISPATCH_TABLE DispatchTable;
unsigned int RpcProtseqEndpointCount;
PRPC_PROTSEQ_ENDPOINT RpcProtseqEndpoint;
RPC_MGR_EPV __RPC_FAR *DefaultManagerEpv;
void const __RPC_FAR *InterpreterInfo;
unsigned int Flags ;
} RPC_SERVER_INTERFACE, __RPC_FAR * PRPC_SERVER_INTERFACE;
然后需要关注的并不是
DispatchTable
,而是
InterpreterInfo
成员,它其实是:
typedef struct _MIDL_SERVER_INFO_
{
PMIDL_STUB_DESC pStubDesc;
const SERVER_ROUTINE * DispatchTable;
PFORMAT_STRING ProcString;
const unsigned short * FmtStringOffset;
const STUB_THUNK * ThunkTable;
PRPC_SYNTAX_IDENTIFIER pTransferSyntax;
ULONG_PTR nCount;
PMIDL_SYNTAX_INFO pSyntaxInfo;
} MIDL_SERVER_INFO, *PMIDL_SERVER_INFO;
这里的
DispatchTable
才是我们所关心的位置。那么对于Windows的关键进程services.exe,可以看到他内部的一些逻辑:
140072320实际上就是 RPC_SERVER_INTERFACE
结构,我们可以发现
InterpreterInfo
是在140073D10的位置
140073D10 结构为 MIDL_SERVER_INFO ,第二个成员即为服务端的DispatchTable
于是看到了这么多跟服务相关的函数表,那么劫持这张表里的关键点就可以达到预期
0x03 逆推实现
由于RPCRT4.dll在不同版本中提供了不同的服务注册接口,存在一些兼容问题,那么没办法,只有把可能途经访问到的函数都做一下劫持。
劫持点如下标出,顺序为低版本到高版本:
RPC_STATUS RPC_ENTRY RpcEpRegisterW(
RPC_IF_HANDLE IfSpec,
RPC_BINDING_VECTOR *BindingVector,
UUID_VECTOR *UuidVector,
RPC_WSTR Annotation
)
RPC_STATUS RPC_ENTRY RpcServerRegisterIfEx(
RPC_IF_HANDLE IfSpec,
UUID *MgrTypeUuid,
RPC_MGR_EPV *MgrEpv,
unsigned int Flags,
unsigned int MaxCalls,
RPC_IF_CALLBACK_FN *IfCallback
)
RPC_STATUS RPC_ENTRY RpcServerRegisterIf3(
RPC_IF_HANDLE IfSpec,
UUID *MgrTypeUuid,
RPC_MGR_EPV *MgrEpv,
unsigned int Flags,
unsigned int MaxCalls,
unsigned int MaxRpcSize,
RPC_IF_CALLBACK_FN *IfCallback,
void *SecurityDescriptor
)
在注册过程中有一个最为明显的特征,可以标识出是我们想要的RPC组件服务进行了注册,就是IfSpec->InterfaceId.SyntaxGUID,本文讨论的这个GUID,目前为止还都是同一个:0x367ABB81, 0x9844, 0x35F1, 0xAD, 0x32, 0x98, 0xF0, 0x38, 0x00, 0x10, 0x03
即 {367ABB81-9844-35F1-AD32-98F038001003}。
劫持了一些我们感兴趣的RPC函数之后,却还有一个大的问题,那就是怎么拿到客户端的进程/线程信息~
幸运的是,RPCRT4.dll提供了一些函数,可以很方便的查到客户端是谁,I_RpcOpenClientThread,
I_RpcOpenClientProcess。
而且services.exe的TOKEN所在组(应该是属于Admin),没有拒绝TOKEN的问题,但是换到一些低权限组那没办法,只能用一些别的方法了~
使用
I_RpcOpenClientThread
/
I_RpcOpenClientProcess时注意DesiredAccess不要太高就行,要啥权限就写啥权限,一般而言QUERY_INFORMATION就行。
详细的实现部分就不继续说了,见附件内容。
作者在总结出结论之前,最初也是因为对某些厂商在Windows系统服务层面的HIPS技术产生了浓厚的兴趣才去研究的。多半的应用层代码也是逆向自某产品的一个小模块。作者并没有其他企图,只是作为学习和成长的一个途径去研究这个课题而已。对这个模块研究完之后得到这样的结论:
1. 对服务的创建/修改的HIPS并不是内核级别的注册表拦截,而是注入到services.exe进行的暗地操作;
2. 这个应用层模块劫持了RPCRT4.dll的一些函数,即RPC Server的注册流程,同步式与内核模块实现阻断规则;
3. services.exe具有相对较高的TOKEN令牌,很方便就可以获得到Client端的属性信息。
那么接下来就按照这个思路,自己进行一些尝试。
0x02 相关结构体
构建一个自己的rpc,首先需要一个idl接口描述,然后使用MIDL工具生成对应的客户端以及服务端的Stub文件。
服务端:
RPCRTAPI
RPC_STATUS
RPC_ENTRY
RpcServerRegisterIf (
_In_ RPC_IF_HANDLE IfSpec,
_In_opt_ UUID __RPC_FAR * MgrTypeUuid,
_In_opt_ RPC_MGR_EPV __RPC_FAR * MgrEpv
);
typedef void __RPC_FAR * RPC_IF_HANDLE;
服务端需要进行注册,最为主要的结构为
RPC_IF_HANDLE,在服务端,这个指针结构为:
typedef struct _RPC_SERVER_INTERFACE
{
unsigned int Length;
RPC_SYNTAX_IDENTIFIER InterfaceId;
RPC_SYNTAX_IDENTIFIER TransferSyntax;
PRPC_DISPATCH_TABLE DispatchTable;
unsigned int RpcProtseqEndpointCount;
PRPC_PROTSEQ_ENDPOINT RpcProtseqEndpoint;
RPC_MGR_EPV __RPC_FAR *DefaultManagerEpv;
void const __RPC_FAR *InterpreterInfo;
unsigned int Flags ;
} RPC_SERVER_INTERFACE, __RPC_FAR * PRPC_SERVER_INTERFACE;
然后需要关注的并不是
DispatchTable
,而是
InterpreterInfo
成员,它其实是:
typedef struct _MIDL_SERVER_INFO_
{
PMIDL_STUB_DESC pStubDesc;
const SERVER_ROUTINE * DispatchTable;
PFORMAT_STRING ProcString;
const unsigned short * FmtStringOffset;
const STUB_THUNK * ThunkTable;
PRPC_SYNTAX_IDENTIFIER pTransferSyntax;
ULONG_PTR nCount;
PMIDL_SYNTAX_INFO pSyntaxInfo;
} MIDL_SERVER_INFO, *PMIDL_SERVER_INFO;
这里的
DispatchTable
才是我们所关心的位置。那么对于Windows的关键进程services.exe,可以看到他内部的一些逻辑:
140072320实际上就是 RPC_SERVER_INTERFACE
结构,我们可以发现
InterpreterInfo
是在140073D10的位置
140073D10 结构为 MIDL_SERVER_INFO ,第二个成员即为服务端的DispatchTable
于是看到了这么多跟服务相关的函数表,那么劫持这张表里的关键点就可以达到预期
0x03 逆推实现
由于RPCRT4.dll在不同版本中提供了不同的服务注册接口,存在一些兼容问题,那么没办法,只有把可能途经访问到的函数都做一下劫持。
劫持点如下标出,顺序为低版本到高版本:
RPC_STATUS RPC_ENTRY RpcEpRegisterW(
RPC_IF_HANDLE IfSpec,
RPC_BINDING_VECTOR *BindingVector,
UUID_VECTOR *UuidVector,
RPC_WSTR Annotation
)
RPC_STATUS RPC_ENTRY RpcServerRegisterIfEx(
RPC_IF_HANDLE IfSpec,
UUID *MgrTypeUuid,
RPC_MGR_EPV *MgrEpv,
unsigned int Flags,
unsigned int MaxCalls,
RPC_IF_CALLBACK_FN *IfCallback
)
RPC_STATUS RPC_ENTRY RpcServerRegisterIf3(
RPC_IF_HANDLE IfSpec,
UUID *MgrTypeUuid,
RPC_MGR_EPV *MgrEpv,
unsigned int Flags,
unsigned int MaxCalls,
unsigned int MaxRpcSize,
RPC_IF_CALLBACK_FN *IfCallback,
void *SecurityDescriptor
)
在注册过程中有一个最为明显的特征,可以标识出是我们想要的RPC组件服务进行了注册,就是IfSpec->InterfaceId.SyntaxGUID,本文讨论的这个GUID,目前为止还都是同一个:0x367ABB81, 0x9844, 0x35F1, 0xAD, 0x32, 0x98, 0xF0, 0x38, 0x00, 0x10, 0x03
即 {367ABB81-9844-35F1-AD32-98F038001003}。
劫持了一些我们感兴趣的RPC函数之后,却还有一个大的问题,那就是怎么拿到客户端的进程/线程信息~
幸运的是,RPCRT4.dll提供了一些函数,可以很方便的查到客户端是谁,I_RpcOpenClientThread,
I_RpcOpenClientProcess。
而且services.exe的TOKEN所在组(应该是属于Admin),没有拒绝TOKEN的问题,但是换到一些低权限组那没办法,只能用一些别的方法了~
使用
I_RpcOpenClientThread
/
I_RpcOpenClientProcess时注意DesiredAccess不要太高就行,要啥权限就写啥权限,一般而言QUERY_INFORMATION就行。
详细的实现部分就不继续说了,见附件内容。
2. 这个应用层模块劫持了RPCRT4.dll的一些函数,即RPC Server的注册流程,同步式与内核模块实现阻断规则;
3. services.exe具有相对较高的TOKEN令牌,很方便就可以获得到Client端的属性信息。
那么接下来就按照这个思路,自己进行一些尝试。
0x02 相关结构体
构建一个自己的rpc,首先需要一个idl接口描述,然后使用MIDL工具生成对应的客户端以及服务端的Stub文件。
服务端:
RPCRTAPI
RPC_STATUS
RPC_ENTRY
RpcServerRegisterIf (
_In_ RPC_IF_HANDLE IfSpec,
_In_opt_ UUID __RPC_FAR * MgrTypeUuid,
_In_opt_ RPC_MGR_EPV __RPC_FAR * MgrEpv
);
typedef void __RPC_FAR * RPC_IF_HANDLE;
服务端需要进行注册,最为主要的结构为
RPC_IF_HANDLE,在服务端,这个指针结构为:
typedef struct _RPC_SERVER_INTERFACE
{
unsigned int Length;
RPC_SYNTAX_IDENTIFIER InterfaceId;
RPC_SYNTAX_IDENTIFIER TransferSyntax;
PRPC_DISPATCH_TABLE DispatchTable;
unsigned int RpcProtseqEndpointCount;
PRPC_PROTSEQ_ENDPOINT RpcProtseqEndpoint;
RPC_MGR_EPV __RPC_FAR *DefaultManagerEpv;
void const __RPC_FAR *InterpreterInfo;
unsigned int Flags ;
} RPC_SERVER_INTERFACE, __RPC_FAR * PRPC_SERVER_INTERFACE;
然后需要关注的并不是
DispatchTable
,而是
InterpreterInfo
成员,它其实是:
3. services.exe具有相对较高的TOKEN令牌,很方便就可以获得到Client端的属性信息。
那么接下来就按照这个思路,自己进行一些尝试。
0x02 相关结构体
构建一个自己的rpc,首先需要一个idl接口描述,然后使用MIDL工具生成对应的客户端以及服务端的Stub文件。
服务端:
RPCRTAPI
RPC_STATUS
RPC_ENTRY
RpcServerRegisterIf (
_In_ RPC_IF_HANDLE IfSpec,
_In_opt_ UUID __RPC_FAR * MgrTypeUuid,
_In_opt_ RPC_MGR_EPV __RPC_FAR * MgrEpv
);
typedef void __RPC_FAR * RPC_IF_HANDLE;
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
最后于 2019-5-5 13:33
被FaEry编辑
,原因: