好久没写博客了, 一个月一篇还是要尽量保证,今天谈下Hook技术。
在Window平台上开发任何稍微底层一点的东西,基本上都是Hook满天飞, 普通应用程序如此,安全软件更是如此, 这里简单记录一些常用的Hook技术。
基本上做Windows开发都知道这个API, 它给我们提供了一个拦截系统事件和消息的机会, 并且它可以将我们的DLL注入到其他进程。
但是随着64位时代的到来和Vista之后的UAC机制开启,这个API很多时候不能正常工作了:
首先,32位DLL没法直接注入到64位的应用程序里面, 因为他们的地址空间完全不一样的。当然尽管没法直接注入,但是在权限范围内,系统会尽量以消息的方式让你能收到64位程序的消息事件。
其次,UAC打开的情况下低权限程序没法Hook高权限程序, 实际上低权限程序以高权限程序窗口为Owner创建窗口也会失败, 低权限程序在高权限程序窗口上模拟鼠标键盘也会失败。
有人说我们可以关闭UAC, Win7下你确实可以,但是Win8下微软已经不支持真正关闭UAC, 从这里我们也可以看到微软技术过渡的方式, 中间会提供一个选项来让你慢慢适应,最后再把这个选项关掉, UAC和Aero模式都是如此。
那么我们如何解决这些问题?
对于64位问题 , 解决方法是提供2个DLL,分别可以Hook32和64位程序。
对于权限问题, 解决方法是提升权限, 通过注册系统服务, 由服务程序创建我们的工作进程。这里为什么要创建一个其他进程而不直接在服务进程里干活? 因为Vista后我们有了Session隔离机制,服务程序运行在Session 0,我们的其他程序运行在Session 1, Session 2等, 如果我们直接在服务程序里干活,我们就只能在Session 0里工作。通过创建进程,我们可以在
DuplicateTokenEx后将Token的SessionID设置成目标Session,并且在
CreateProcessAsUser时指定目标WinStation和Desktop, 这样我们就既获得了System权限,并且也可以和当前桌面进程交互了。
DuplicateTokenEx后将Token的SessionID设置成目标Session,并且在
CreateProcessAsUser时指定目标WinStation和Desktop, 这样我们就既获得了System权限,并且也可以和当前桌面进程交互了。
很多人可能都不知道这个API, 但是这个API其实挺重要的, 看名字就知道它是Hook事件(Event)的, 具体哪些事件可以看
这里.
这里.
为什么说这个API重要, 因为这个API大部分时候没有SetWindowsHookEx的权限问题, 也就是说这个API可以让你Hook到高权限程序的事件, 它同时支持进程内(
WINEVENT_INCONTEXT)和进程外(
WINEVENT_OUTOFCONTEXT)2种Hook方式, 你可以以进程外的方式Hook到64位程序的事件。
WINEVENT_INCONTEXT)和进程外(
WINEVENT_OUTOFCONTEXT)2种Hook方式, 你可以以进程外的方式Hook到64位程序的事件。
为什么这个API没有权限问题, 因为它是给Accessibility用的, 也就是它是给自动测试和残障工具用的, 所以它要保证有效。
我曾经看到这样一个程序,当任何程序(无论权限高低)有窗口拖动(拖标题栏改变位置或是拖边框改变大小), 程序都能捕获到, 当时很好奇它是怎么做到的?
Spy了下窗口消息, 知道有这样2个消息:
WM_ENTERSIZEMOVE和
WM_EXITSIZEMOVE表示进入和退出这个事件, 但是那也只能获得自己的消息,其他程序的消息它是如何捕获到的?当时怀疑用的是Hook, 却发现没有DLL注入。查遍了Windows API 也没有发现有API可以查询一个窗口是否在这个拖动状态。最后发现用的是SetWinEventHook
的EVENT_SYSTEM_MOVESIZESTART和EVENT_SYSTEM_MOVESIZEEND。
WM_ENTERSIZEMOVE和
WM_EXITSIZEMOVE表示进入和退出这个事件, 但是那也只能获得自己的消息,其他程序的消息它是如何捕获到的?当时怀疑用的是Hook, 却发现没有DLL注入。查遍了Windows API 也没有发现有API可以查询一个窗口是否在这个拖动状态。最后发现用的是SetWinEventHook
的EVENT_SYSTEM_MOVESIZESTART和EVENT_SYSTEM_MOVESIZEEND。
API Hook
常见的API Hook包括2种, 一种是基于PE文件的导入表(IAT), 还有
一种是修改前5个字节直接JMP的inline Hook.
一种是修改前5个字节直接JMP的inline Hook.
对于基于IAT的方式, 原理是PE文件里有个导入表, 代表该模块调用了哪些外部API,模块被加载到内存后, PE加载器会修改该表,地址改成外部API重定位后的真实地址, 我们只要直接把里面的地址改成我们新函数的地址, 就可以完成对相应API的Hook。《Windows核心编程》里第22章
有个封装挺好的CAPIHook类,我们可以直接拿来用。
有个封装挺好的CAPIHook类,我们可以直接拿来用。
我曾经用API Hook来实现自动测试,见这里 API Hook在TA中的应用
对于基于Jmp方式的inline hook, 原理是修改目标函数的前5个字节, 直接Jmp到我们的新函数。虽然原理挺简单, 但是因为用到了平台相关的汇编代码, 一般人很难写稳定。真正在项目中用还是要求稳定, 所以我们一般用微软封装好的Detours, 对于Detours的原理,这里有篇不错的文章
微软研究院Detour开发包之API拦截技术。
微软研究院Detour开发包之API拦截技术。
比较一下2种方式:
IAT的方式比较安全简单, 但是只适用于Hook导入函数方式的API。
Inline Hook相对来说复杂点, 但是它能Hook到任何函数(API和内部函数),但是它要求目标函数大于5字节, 同时把握好修改时机或是Freeze其他线程, 因为多线程中改写可能会引起冲突。
COM Hook
Window上因为有很多开发包是以COM方式提供的(比如DirectX), 所以我们就有了拦截COM调用的COM Hook。
因为COM里面很关键的是它的接口是C++里虚表的形式提供的, 所以COM的Hook很多是时候其实就是虚表
(vtable)的Hook。
(vtable)的Hook。
对于COMHook,考虑下面2种case:
一种是我们Hook程序先运行,然后启动某个游戏程序(DirectX 9), 我们想Hook游戏的绘画内容。
这种方式下, 我们可以先Hook API
Direct3DCreate9, 然后我们继承于IDirect3D9, 自己实现一个COM对象返回回去, 这样我们就可以拦截到所有对该对象的操作,
为所欲为了, 当然我们自己现实的COM对象内部会调用真正的
Direct3DCreate9,封装真正的
IDirect3D9。
Direct3DCreate9, 然后我们继承于IDirect3D9, 自己实现一个COM对象返回回去, 这样我们就可以拦截到所有对该对象的操作,
为所欲为了, 当然我们自己现实的COM对象内部会调用真正的
Direct3DCreate9,封装真正的
IDirect3D9。
当然有时我们可能不用替代整个COM组件,我们只需要修改其中一个或几个COM函数, 这种情况下我们可以创建真正的
IDirect3D9对象后直接修改它的虚表, 把其中某些函数改成我们自己的函数地址就可以了。
IDirect3D9对象后直接修改它的虚表, 把其中某些函数改成我们自己的函数地址就可以了。
还有一种case是游戏程序已经在运行了, 然后才启动我们的Hook进程, 我们怎么样才能Hook到里面的内容?
理论上说COM对象如果是以C++接口的方式实现, 虚表会位于PE文件的只读数据节(.rdata), 并且所有该类型的对象都共享该虚表, 所以我们只要创建一个该类型对象,我们就可以获得其他人创建的该类型对象的虚表位置,我们就可以改写该虚表实现Hook(实际操作时需要通过VirtualProtect修改页面的只读属性才能写入)。
但是实际上COM的虚表只是一块内存, 它并不一定是以C++实现, 所以它可以存在于任何内存的任何地方。另外对象的虚表也不一定是所有同类型的对象共享同一虚表, 我们完全可以每个对象都有自己的一份虚表。比如我发现
IDirect3D9是大家共享同一虚表的(存在D3D9.dll的), 但是IDirect3DDevice9就是每个对象都有自己的虚表了(存在于堆heap)。所以如果你要Hook
IDirect3DDevice9接口,通过修改虚表实际上没法实现。
IDirect3D9是大家共享同一虚表的(存在D3D9.dll的), 但是IDirect3DDevice9就是每个对象都有自己的虚表了(存在于堆heap)。所以如果你要Hook
IDirect3DDevice9接口,通过修改虚表实际上没法实现。
但是尽管有时每个对象的虚表不一样,同类型对象虚表里的函数地址却都是一样的, 所以这种情况下我们可以通过inline Hook直接修改函数代码。当然有些情况下如果是静态链接库,即使函数代码也是每个模块都有自己的一份, 这种情况下就只能反汇编获取虚表和函数的地址了。
最后,总结一下, 上面主要探讨了Windows上的各种Hook技术,通过将这些Hook技术组起来, 可以实现很多意想不到的功能, 比如我们完全可以通过Hook D3D实现Win7任务栏那种Thumbnail预览的效果(当然该效果可以直接由DWM API实现, 但是如果我们可以通过HOOK已动画的方式实现是不是更有趣 )。
转载于:https://www.cnblogs.com/weiym/p/3396274.html