Kali 中文网

 找回密码
 立即注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

搜索
查看: 200|回复: 1

见招拆招:详谈COM接口函数Hook技术

[复制链接]
发表于 2017-9-12 17:56:52 | 显示全部楼层 |阅读模式

前言


在日常的工作中,使用hook操作有时可以大大提高我们的工作效率。其中,典型的例子有:为了便于对程序进行调试,我们可以对Windows API函数执行hook操作或者为了进行恶意软件检测,我们也可以对Windows API函数执行hook操作。在这些场景中,往往是一些DLL被注入到目标进程中,然后对相关的功能函数执行hook操作,有几种方法可以做到这一点,但这并不是本文要讨论的重点,感兴趣的读者可以在网上搜索与DLL相关的技术文章。

在组件对象模型(COM)的世界中,想要对COM组件接口函数执行hook操作并不是一件容易的事情。原因是因为COM组件是基于对象的,因此通常情况下我们是不可能获取COM组件接口函数地址的。并且,由于COM组件接口函数不直接导出,因此通过调用GetProcAddress或类似的方法也是无法找到这些函数地址的。此外,即使COM组件中的某些函数的地址被找到,执行hook操作也需要将一些代码注入到目标进程中,这种操作在某些情况下(例如受保护的进程)几乎是不可能实现的。

但经过我们的研究发现,COM组件提供了另一种“hooking”操作或者相当于重定向的机制,此机制能够将一个CLSID重定向到另一个CLSID,MSDN文档中把这种机制称之为“仿真”功能,就像一个类可以模拟另一个类的功能似的。这种机制在一定程度上打开了将一个类重定向到另一个类的可能性,而不需要注入代码到目标进程中或者对某些函数执行hook操作。


Hooking COM接口函数实例


下面让我们一起来看一个具体的例子。Windows中的后台智能传输服务(BITS)提供了异步下载/上传服务,该服务具有进度通知,网络自动恢复等功能。恶意软件可以通过BITS服务来下载其有效载荷,而不需要其直接下载有效载荷,直接下载会使恶意软件更容易暴露在反恶意软件检测工具中。通过BITS下载使得恶意软件与下载的有效载荷没有什么直接的关联,进一步加大了对恶意软件的检测难度。

由于BITS是基于COM组件来实现的,因此可以使用上述所说的“仿真”机制对任何需要获得BITS操作的函数执行hook操作,以获取BITS模块的执行结果。实现这个想法的关键是COM组件中的CoTreatAsClass 这个API函数,该函数将一个TreatAs键添加到原始键中,并将其值指向备用的CLSID。尽管如此,可能我们还是没有办法来对某些BITS操作执行hook操作,原因是由于任何COM激活请求(例如通过CoCreateInstance)都将重定向到TreatAs CLSID,因此我们没有办法只重定向某些特定的请求。

因此,对哪些CLSID应该执行hook操作是我们应该要好好考虑和研究的问题。在BITS的情况下,我们需要操作的对象是BackgroundCopyManager,它是使用BITS操作时肯定会创建的对象。或许你们会认为一个更好的CLSID目标对象是由BITS创建的实际Job对象,但是这样做将起不到任何的作用,因为没有这样的CLSID对象。像许多基于COM组件的API一样,只有几个对象实际上可以使用COM公共激活API创建并具有CLSIDs。通过函数间接创建的一些对象,是不需要通过COM激活机制的。

以下是创建“替代类”以拦截BITS请求所需的代码:

1
2
3
[size=1em]HRESULT hr = ::CoTreatAsClass(
[size=1em]__uuidof(BackgroundCopyManager),
[size=1em]   __uuidof(FakeBitsManager));

上述代码中的FakeBitsManager是我创建的一个COM类,该类需要实现与原始对象(IBackgroundCopyManager)相同的接口;否则,机器上的所有BITS操作都将失败!由于访问权限的原因,上述CoTreatAsClass函数将调用失败,即使从高权限的进程中调用也是无用的。原因是HKCR\CLSID {4991d34b-80a1-4291-83b6-3328366b9097}下的这个注册表键值由TrustedInstaller拥有,它不允许被篡改,甚至不允许系统帐户去修改这个键值! 然而,超级管理员用户可以通过执行Take Ownership特权,成为该注册表键值的新拥有者,因此我们还是有权更改该键值权限的,如下图所示,我们现在已经拥有对该键值的修改权限:

那么,我们在哪里调用CoTreatAsClass函数比较合适呢?经过一些实验我们发现该调用可以在一些安装程序中完成,安装程序可以是一个简单的批处理或PowerShell脚本,并且可以直接使用注册表函数进行正确的设置,具体如下图所示:

下面是一个使用ATL实现的COM类,通过实现IBackgroundCopyManager以及IUnknown以用来对BITS管理器类执行拦截操作,代码如下所示:

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
[size=1em]class ATL_NO_VTABLE CFakeBitsManager :
[size=1em]public CComObjectRootEx<CComSingleThreadModel>,
[size=1em]public CComCoClass<CFakeBitsManager, &CLSID_FakeBitsManager>,
[size=1em]public IBackgroundCopyManager
[size=1em]{
[size=1em]//...
[size=1em]    BEGIN_COM_MAP(CFakeBitsManager)
[size=1em]        COM_INTERFACE_ENTRY(IBackgroundCopyManager)
[size=1em]    END_COM_MAP()
[size=1em]    HRESULT FinalConstruct();
[size=1em]    CComPtr<IBackgroundCopyManager> m_spRealBits;
[size=1em]public:
[size=1em]    STDMETHOD(CreateJob)(
[size=1em]        LPCWSTR DisplayName,
[size=1em]        BG_JOB_TYPE Type,
[size=1em]        __RPC__out GUID *pJobId,
[size=1em]        IBackgroundCopyJob **ppJob);
[size=1em]    STDMETHOD(GetJob)(
[size=1em]        REFGUID jobID,
[size=1em]        IBackgroundCopyJob **ppJob);
[size=1em]    STDMETHOD(EnumJobs)(
[size=1em]        DWORD dwFlags,
[size=1em]        IEnumBackgroundCopyJobs **ppEnum);
[size=1em]    STDMETHOD(GetErrorDescription)(
[size=1em]        HRESULT hResult,
[size=1em]        DWORD LanguageId,
[size=1em]        LPWSTR *pErrorDescription);
[size=1em]};
[size=1em]OBJECT_ENTRY_AUTO(__uuidof(FakeBitsManager), CFakeBitsManager)

上述代码只是IBackgroundCopyManager类实现中的部分代码,需要我们重点关注的是IBackgroundCopyManager接口映射函数的声明以及从<bits.h>头文件复制的四个方法。另一个需要我们重点关注的是IBackgroundCopyManager(m_spRealBits)智能指针的另一个实现,这个成员应该会被初始化为真正的BITS管理器,由于我们不想让所有的BITS操作都失败,因此我们可以将请求转发给它。但是如何创建真正的BITS管理器呢?其实答案很简单,我们只需要关闭TreatAs,以便快速调用创建真正的BITS,然后重新启动,代码如下所示:

1
2
3
4
5
6
7
8
9
[size=1em]HRESULT CFakeBitsManager::FinalConstruct() {
[size=1em]    auto hr = ::CoTreatAsClass(__uuidof(BackgroundCopyManager), CLSID_NULL);
[size=1em]    ATLASSERT(hr);
[size=1em]    hr = m_spRealBits.CoCreateInstance(__uuidof(BackgroundCopyManager));
[size=1em]    ATLASSERT(SUCCEEDED(hr));
[size=1em]    hr = ::CoTreatAsClass(__uuidof(BackgroundCopyManager), __uuidof(FakeBitsManager));
[size=1em]    ATLASSERT(SUCCEEDED(hr));
[size=1em]    return hr;
[size=1em]}

使用CLSID_NULL的第二个参数调用CoTreatAsClass会关闭“仿真”,机器上的其他客户端程序会有很小的机会可以获得“真正的”BITS。但是这个时间间隔很小,并没有一种简单的方法可以获取该BITS(基于注册表的回调和ETW事件会捕获它)。但即使这样,用户模式下的注册表回调可能还不够快,因此它可能在恢复之前注意不到该更改。当然,高权限的进程可以手动还原它,但这不是一个典型的客户端程序应该要考虑的事情,因为它可能没有足够的权限去这样做。

现在我们可以继续实现这些函数方法。任何“不友善”的函数方法都可以被简单地重定向到真正的BITS管理器。示例代码如下所示:

1
2
3
4
5
6
7
8
9
10
[size=1em]STDMETHODIMP CFakeBitsManager::GetJob(
[size=1em]    REFGUID jobID,
[size=1em]    IBackgroundCopyJob **ppJob) {
[size=1em]    return m_spRealBits->GetJob(jobID, ppJob);
[size=1em]}
[size=1em]STDMETHODIMP CFakeBitsManager::EnumJobs(
[size=1em]    DWORD dwFlags,
[size=1em]    IEnumBackgroundCopyJobs **ppEnum) {
[size=1em]    return m_spRealBits->EnumJobs(dwFlags, ppEnum);
[size=1em]}

其中,比较有趣的函数是CreateJob函数,因为我们可以使用该函数收集信息,注册事件通知或其他信息。示例代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
[size=1em]STDMETHODIMP CFakeBitsManager::CreateJob(
[size=1em]    LPCWSTR DisplayName, BG_JOB_TYPE Type,
[size=1em]    GUID *pJobId, IBackgroundCopyJob **ppJob) {
[size=1em]    if(Type != BG_JOB_TYPE_DOWNLOAD)
[size=1em]        return m_spRealBits->CreateJob(DisplayName, Type, pJobId, ppJob);
[size=1em]    auto hr = m_spRealBits->CreateJob(DisplayName, Type, pJobId, ppJob);
[size=1em]    if (FAILED(hr))
[size=1em]        return hr;
[size=1em]    // handle the successful job creation
[size=1em]    // register for notifications, log the download, ...
[size=1em]    return hr;
[size=1em]}

该示例显示,对于非下载作业任务,我们只需将请求传递给真正的BITS管理器处理即可。在下载作业任务创建时,我们可以对该下载请求执行任何的监视操作,这样我们就完成了对COM接口函数的Hooking操作。


总结


理论上讲,对COM接口函数执行hook操作要比对Win32 API执行hook操作可能会简单一些。因为hook Win32 API还需要我们去懂得一些汇编语言相关的知识,以及机器码等底层知识。它们的共同之处都是修改目标程序的执行流程,以达到对数据流进行监控的目的。对COM接口执行hook操作是一个有趣的技术,因此还需要进一步研究和分析。


参考来源:安全客


+1
201°C
1
  • Ghost丶Soul
过: 他们
发表于 2017-9-14 11:05:33 | 显示全部楼层
++++++++++++++++++++++++1111111
回复 支持 反对

使用道具 举报

*滑动验证:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关闭

站长推荐上一条 /1 下一条

Kali linux ( 蜀ICP备10027298号-4 ) Topics |


免责声明:
Kali linux中文网所发布的一切教程文章仅限用于学习和研究目的;不得将上述内容用于商业或者非法用途,否则,一切后果请用户自负。本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑中彻底删除上述内容。

GMT+8, 2017-11-24 00:43 , Processed in 0.197190 second(s), 28 queries , Gzip On.

Powered by Discuz!

© 2009-2016 CNNS

快速回复 返回顶部 返回列表