HOOK技术的一些简单总结

好久没写博客了, 一个月一篇还是要尽量保证,今天谈下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权限,并且也可以和当前桌面进程交互了。

很多人可能都不知道这个API, 但是这个API其实挺重要的, 看名字就知道它是Hook事件(Event)的, 具体哪些事件可以看 这里.

为什么说这个API重要, 因为这个API大部分时候没有SetWindowsHookEx的权限问题, 也就是说这个API可以让你Hook到高权限程序的事件, 它同时支持进程内( 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。

API Hook
常见的API Hook包括2种, 一种是基于PE文件的导入表(IAT), 还有 一种是修改前5个字节直接JMP的inline Hook.

对于基于IAT的方式, 原理是PE文件里有个导入表, 代表该模块调用了哪些外部API,模块被加载到内存后, PE加载器会修改该表,地址改成外部API重定位后的真实地址, 我们只要直接把里面的地址改成我们新函数的地址, 就可以完成对相应API的Hook。《Windows核心编程》里第22章 有个封装挺好的CAPIHook类,我们可以直接拿来用。
我曾经用API Hook来实现自动测试,见这里 API Hook在TA中的应用

对于基于Jmp方式的inline hook, 原理是修改目标函数的前5个字节, 直接Jmp到我们的新函数。虽然原理挺简单, 但是因为用到了平台相关的汇编代码, 一般人很难写稳定。真正在项目中用还是要求稳定, 所以我们一般用微软封装好的Detours, 对于Detours的原理,这里有篇不错的文章  微软研究院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。
关于C++ 对象模型和虚表可以看我这篇  探索C++对象模型

对于COMHook,考虑下面2种case:

一种是我们Hook程序先运行,然后启动某个游戏程序(DirectX 9), 我们想Hook游戏的绘画内容。

这种方式下, 我们可以先Hook API  Direct3DCreate9, 然后我们继承于IDirect3D9, 自己实现一个COM对象返回回去, 这样我们就可以拦截到所有对该对象的操作, 为所欲为了, 当然我们自己现实的COM对象内部会调用真正的 Direct3DCreate9,封装真正的 IDirect3D9。

当然有时我们可能不用替代整个COM组件,我们只需要修改其中一个或几个COM函数, 这种情况下我们可以创建真正的 IDirect3D9对象后直接修改它的虚表, 把其中某些函数改成我们自己的函数地址就可以了。

其实ATL就是用接口替代的方式来调试和记录COM接口引用计数的次数, 具体可以看我这篇  理解ATL中的一些汇编代码

还有一种case是游戏程序已经在运行了, 然后才启动我们的Hook进程, 我们怎么样才能Hook到里面的内容?

这种情况下我们首先要对程序内存有比较详细的认识, 才能思考创建出来的D3D对象的虚表位置, 从而进行Hook, 关于程序内存布局,可见我这篇  理解程序内存

理论上说COM对象如果是以C++接口的方式实现, 虚表会位于PE文件的只读数据节(.rdata), 并且所有该类型的对象都共享该虚表, 所以我们只要创建一个该类型对象,我们就可以获得其他人创建的该类型对象的虚表位置,我们就可以改写该虚表实现Hook(实际操作时需要通过VirtualProtect修改页面的只读属性才能写入)。

但是实际上COM的虚表只是一块内存, 它并不一定是以C++实现, 所以它可以存在于任何内存的任何地方。另外对象的虚表也不一定是所有同类型的对象共享同一虚表, 我们完全可以每个对象都有自己的一份虚表。比如我发现 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

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
本人使用易语言 也有10几年时间,至今留下来的也只是一些怀念和情节,还记得上一次在本论坛发帖是在10年前,期间也只是零星来论坛看看。平常使用也纯属兴趣爱好,并非科班出身,一些见解如若有误,忘大神包含。我们知道目前易语言是支持32位编译,后期估计也不会有所改善了,这似乎已经成为一门被放弃的失败品。既然如此面对如今64位系统的普及,易语言爱好者面对64位程序的操作层面上就显得有些无奈和悲哀。好在有着一些执着的爱好者不想放弃,依然在鼓励解决这些问题,如本论坛的一个开源模块WOW64Ext,就为易语言操作64位模块进程提供了一些基本的封装,本人也借此基础上封装了几个功能,作为进一步扩展,有兴许的朋友可以继续完善。 一:浅谈64位进程远程hook技术 关于HOOK这个话题,网络上铺天盖地并无新鲜,故在此我就不讲述什么是HOOK这些无聊话题了,本文主要阐述一些64位下远程HOOK与32位的主要区别。首先我们来看看要实现一个远程HOOK的构成顺序:1:在目标进程申请内存空间,存放我们截断后的穿插代码与HOOK原代码2:修改HOOK目标位置的指令为跳转至1申请的内存空间中3:穿插代码中把我们需要的寄存器或其他通过通讯手段传达到我们程序的回调接口中去,在这个过程中如果只需要取值,穿插代码不需要等待,如果需要修改生效,穿插代码需要等待回调接口的返回,并把修改内容写回。4:穿插代码最后跳回到HOOK位置长度的下一跳指令,或指定的位置。5:完成整个HOOK过程了解了整个过程看上去似乎很简单,确实要做到这个过程是不难的,只是要做到相对完美要考虑的情况有很多。比如对跳转使用的选择情况:HOOK跳转代码肯定是越短越好,像32位JMP跳转只需要5字节即可,但是在64位进程中情况确截然不同。32位进程寻址能力为4字节,而64位进程寻址能力变成了8字节,然而64位汇编中所有的跳转直接寻址只支持4字节,这在32位中当然不是什么问题,因为32位最大寻址本来就不会超越4字节,不存在超限的说法:但64位中想要达到长转移,必须借用寄存器或地址偏移,那么一般在64位中HOOK的跳转代码在不影响寄存器的情况下一般使用如下办法FFFFFFFFFFFFFFFF作为跳转目标地址:为了不影响寄存器必须提前压入一个寄存器 --------------------------Push raxMov rax, FFFFFFFFFFFFFFFF JMP rax 或 call rax 在内部要取回rax ,这里注意JMP和call的区别,最后平栈 -------------------------- Push rax Mov rax,FFFFFFFFFFFFFFFF Push rax Ret 在内部要取回rax ,最后平栈 有的朋友看到这要问了 我不能直接 JMP FFFFFFFFFFFFFFFF或者 push FFFFFFFFFFFFFFFF 啊,您要这么问,我实在不知如何回答你,表示无语,您还是直接下个源码玩玩算了。 其他类似的列子我就不一一举例了,总结也是差不多形态,以上列子共占用13字节长度,这还是堆栈放在了内部平,否则还要+1个字节长度,如果放弃其中一个寄存器可以-1个字节长度,所以一般网上现有的64位hook一般都在12字节以上,但是一个好用的hook要占用13字节的长度,对我而言无疑无法忍受,难道真的没有其他办法了吗,要保护寄存器且支持长转移,是不是还有其他办法,那么其实是有办法的,就是通过JMP [rip] 机器码形态为 FF 25 00 00 00 00 这句代码占用6字节,那么这是什么意思呢 FF 25 = jmp ,00 00 00 00为偏移长度 对一个支持2G的字节转移长度,JMP [rip]在调试器中可以解释为 jmp qword ptr ds:[0x地址],对了,也就是读取这个偏移位置中的8字节数值作为跳转地址转移过去,如果偏移为00 00 00 00 那么就代表 JMP [rip]的下一条指令处8字节数据。想到这你也许会问  那么这个意思不就是JMP [rip]6字节+8字节长度吗,对如果是连起来确实如此,但是我们可以给他个偏移啊,不就可以分开了吗,我们只需要搜索同模块的其他位置中00或CC等连续8字节无用代码位置,把跳转的地址写入其中,那么JMP [rip]就可以通过偏移读取到跳转地址了。我们也就能实现6字节的HOOK,这个方式的亮点是改写长度小,且不影响寄存器和rsp堆栈指针,也算是达到曲线救国的目的。 比如对穿插代码中数据传递的问题:我们要获得16个通用寄存器RAX—R15的每个值,这些值我们又如何传递过去。一般远程HOOK数据传递使用消息或者远线程,因为这两种方式汇编改写量小一点,相对容易实现,在这我们不讨论远线程,我们来看看消息传递,一般是两个函数的选
[Trial version] 加密解密技术内幕.htm [Trial version] 目录.htm [Trial version] 第1章 PE文件格式深入研究 [Trial version] 1.1 PE文件格式格式纵览 [Trial version] PE文件格式一览.html [Trial version] 1.2 PE文件结构 [Trial version] 1. 检验PE文件的有效性.html [Trial version] 2. File Header (文件头).html [Trial version] 3. Optional Header.html [Trial version] 4. Section Table(节表).html [Trial version] 5. Import Table(引入表).html [Trial version] 6. Export Table(引出表).html [Trial version] 7.Debug信息节.htm [Trial version] DocNet 学习笔记之Anti ILDASM 研究.htm [Trial version] PE 档的基底重定位(Base Relocations).htm [Trial version] PE 格式学习总结(一)-- PE文件概述.htm [Trial version] PE 格式学习总结(二)-- PE文件中的输出函数.htm [Trial version] PE 格式学习总结(三)-- PE文件中的输入函数.htm [Trial version] PE 格式学习总结(四)-- PE文件中的资源.htm [Trial version] TLS.htm [Trial version] 基址从定位.htm [Trial version] 异常处理.htm [Trial version] 第2章 PE分析工具编写 [Trial version] 关于PE可执行文件的修改.htm [Trial version] 手工构造一个超微型的 PE 文件.htm [Trial version] 第3章 Win32 调试API [Trial version] 病毒是如何抗动态测试.htm [Trial version] 3.1 Win32调试API原理 [Trial version] Win32调试API第一部分.htm [Trial version] Win32调试API第二部分.htm [Trial version] Win32调试API第三部分.htm [Trial version] Win32调试API学习心得(一).htm [Trial version] Win32调试API学习心得(二).htm [Trial version] win32调试API学习心得(三).htm [Trial version] 用调试函数跟踪API.htm [Trial version] 3.2 利用调试API编写脱壳机 [Trial version] DEF分析与打造其脱壳软件----我的一篇析文.htm [Trial version] 用调试函数跟踪API.htm [Trial version] 3.3 利用调试API制作内存补丁 [Trial version] 利用DebugAPI做一些原先手工完成的动作,我用这种方法做过内存补丁,内存注册机等,完全VC编译.htm [Trial version] 第4章 Windows下的异常处理 [Trial version] 4.1 基本概念 [Trial version] SEH基本概念.htm [Trial version] 什么是异常.htm [Trial version] 4.2 结构化异常处理(SEH) [Trial version] 初步实战演习.htm [Trial version] 4.3 异常处理程序设计 [Trial version] 传递给异常处理例程的参数.htm [Trial version] 异常处理的堆栈展开.htm [Trial version] 4.4 SEH的简单应用 [Trial version] Win9x下利用SEH进ring0.htm [Trial version] 利用SEH实现对自身的单步自跟踪.htm [Trial version] 异常死亡进程的自动复活.htm [Trial version] 用 SEH 技术实现 API Hook.htm [Trial version] 4.6 VC是如何封装系统提供的SEH机制的 [Trial version] VC++编译器怎么实现异常处理.htm [Trial version] 4.7 Windows XP下的向量化异常处理(VEH [Trial version] windows XP下的向量化异常处理.htm [Trial version] 第5章 软件加密技术 [Trial version] 5.1 反调试技术(Anti-Debug) [Trial version] 5.1.1 句柄检测.htm [Trial version] 5.1.4 ICECream子类型.htm [Trial version] 5.1.5 判断NTICE服务是否运行.htm [Trial version] anti-debug技术探讨.htm [Trial version] AntiSoftICE.htm [Trial version] VB中实现检测ICE.htm [Trial version] 测试ICE是否在运行.htm [Trial version] 检测debugger的方法补遗.htm [Trial version] 另外一种检测SOFTICE的方法.htm [Trial version] 通过PEB实现AntiDebug.htm [Trial version] 5.2 反跟踪技术(Anti-Trace) [Trial version] tELock中的SEH反跟踪代码.htm [Trial version] 利用SEH改变程序流程以达到反跟踪的目的.htm [Trial version] 5.2.3 SMC技术实现 [Trial version] 浅析SMC技术.htm [Trial version] 5.3 反加载技术(Anti-Loader) [Trial version] VB anti-loader 2种常用方法介绍.htm [Trial version] 5.3.1 利用TEB检测 [Trial version] 浅谈利用 TEB 实现的反跟踪.htm [Trial version] 5.4 反DUMP技术(Anti-Dump) [Trial version] 理论知识.htm [Trial version] 5.5 文件完整性检验 [Trial version] PE 头部校验和(checksum)的计算.htm [Trial version] 5.5.1 CRC校验实现 [Trial version] 矛与盾的较量(1)——CRC原理篇.htm [Trial version] 矛与盾的较量(2)——CRC实践篇.htm [Trial version] 5.7 反静态分析技术 [Trial version] Fooling Disassemblers.htm [Trial version] 5.7.2 花指令 [Trial version] 矛与盾的较量(1)——花指令.htm [Trial version] 5.8 代码与数据结合技术 [Trial version] 与破解过招,保护你的共享软件.htm [Trial version] 5.9 软件保护的若干忠告 [Trial version] WIN下动态注册码实现方法.htm [Trial version] 安全程序设计.htm [Trial version] 如何用简单方法防止破解.htm [Trial version] 第6章 加壳软件编写 [Trial version] 6.1 外壳编写基础 [Trial version] 加壳软件实现.htm [Trial version] 6.2 加壳程序综合运用的实例 [Trial version] 浅谈壳的加载步骤及手动脱壳。.htm [Trial version] 第7章 如何让壳与程序融为一体 [Trial version] 7.2 欺骗检查壳的工具 [Trial version] 让侦测工具把壳识别为VC++.htm [Trial version] 7.4 使用sdk把程序和壳溶为一体 [Trial version] 实现调用加壳的外壳中的子程序的一点见解.htm [Trial version] 7.5 后记关于壳和程序的思考 [Trial version] 加壳技术探讨-加壳时处理IAT.htm [Trial version] 第8章 Visual Basic 6 逆向工程 [Trial version] VB的Native Code的传奇.htm [Trial version] VB网址大全.htm [Trial version] Visual Basic 6 逆向工程与反逆向工程 (1).htm [Trial version] Visual Basic 6 逆向工程与反逆向工程 (2).htm [Trial version] 《真是想不到系列》.htm [Trial version] 关于VB P-CODE的一些总结.htm [Trial version] 关于在VB中进行COM组件的开发(一、了解COM).htm [Trial version] 未公开API函数揭秘--通用对话框.htm [Trial version] VB与COM [Trial version] 如何用VB6写COM组件(一).htm [Trial version] 如何用VB6写COM组件(二).htm [Trial version] 附录A 在Visual C++中使用内联汇编 [Trial version] Visual C++ 内嵌汇编.htm [Trial version] Visual C++ 内嵌汇编二.htm [Trial version] 附录B 在Visual Basic中使用汇编 [Trial version] 完全用VB进行ASM编程的示例.htm [Trial version] 附录C COM技术 [Trial version] 《COM 原理与应用》学习笔记 - 第一部分 COM原理.htm [Trial version] 附录D 通过崩溃地址找出出错的代码行 [Trial version] 如何通过崩溃地址找到出错的代码行.htm [Trial version] 附录E 堆栈修正 [Trial version] 堆栈修正.htm

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值