• API Hooking 的原理


    昨天提到了如何用Hook Ole32 提供的两个API来实现Hook Drag & Drop的过程。算是给EasyHook库做一个广告吧。今天给大家讲讲EasyHook的实现原理。

    API Hooking有两种,内核态级别的和用户态级别的。内核态级别的API Hooking也是很多病毒的实现原理,而且在微软不断强化其PatchGuard技术之后,已经越来越困难。做为建设四化的四有青年,我们还是把精力集中在用户态级别的API Hooking上好了。用户态的API Hooking其实就是为了在应用程序和Windows之间插一脚,从而达到用户调用的不再直接是Windows提供的函数,而是调用我们提供的函数。我们可以选择把调用转交给真正的Windows提供的函数,也可以选择自己处理掉,如果我们知道怎么处理的话。API Hooking非常强大可以应用在很多地方,比如监测(非安全领域,因为很容易绕过),虚拟化,软件破解等。要实现API Hooking有很多选择,基本上可以分为:

    1、木马DLL

    2、修改导出表

    3、修改导入表

    4、代码重写

    实现的难易程度也基本上是上述的顺序。木马DLL是最好实现的,前年我因为需要解码nellymoser编码的音频,但是只有Flash播放器才有它的解码器。所以我就把winmm.dll这个系统提供多媒体功能的dll做了一个木马,实现了拦截wav输出的目的。木马DLL只需要你写一个同名的dll,导出相同名字和签名的函数即可。不过它的限制也最大,最大的原因是windows核心的DLL是不会从当前目录加载的(比如user32.dll),同时它还要求木马DLL必须预先放置在exe的同级目录下。

    修改导出和导入表限制也很大。真正的王道是代码重写,不过难度陡增。在这个方面比较出名的有Detours(微软研究院的,不过已经没更新了),和madshi(DELPHI领域的,用户众多)。不过最近新出了一种非常强大的库,叫EasyHook。它具有非常独特的特性,支持.NET。也就是说,你可以用C#来写API钩子。这在以前是无法想象的。它的项目地址是:http://www.codeplex.com/easyhook。EasyHook其实不光是一个API Hook的库,它还提供了Remote Injection(远程注入)的功能。也就是说,你不但能够在Hook当前进程的API,还能去Hook其他进程的API。

    所谓代码重写,是指修改DLL映射到当前进程的内存中的代码(二进制的机器指令)。每个API都会在运行时解析出一个地址,然后程序会用call指令去调用这个地址所包含的机器指令。代码重写就是把这个地址所在位置的机器指令重写了,从而达到从中插一脚的目的。简化后的流程为:

    1、调用API地址所在处的指令

    2、被改写的代码被执行(一般就是一个jmp指令,跳转到一个更开阔的地方去做更多的事情)

    3、执行Hook

    4、执行被覆盖的指令(一般是5个字节)

    5、跳转回(API地址所在处+5个字节)的位置继续执行原代码

    要完成这样一个流程已经很不容易。它需要你懂得80x86的汇编语言,而且要有很强的debug能力。但是这只是一个简化的流程,真正复杂的地方在于:

    1、不可覆盖的代码的监测(比如jne就无法被覆盖)

    2、Hook的可靠卸载

    3、被覆盖指令长度的确定(一般是5个字节,但是那只是一般)

    4、不同CPU指令集的支持(64位?)

    5、Hook中调用了被Hook的API怎么办?

    对于上述问题考虑的周到与否是衡量一个API Hook库的标准。很高兴的告诉你,EasyHook把上述问题都考虑进去了。而且依靠CLR Hosting API,EasyHook还实现了.NET的API钩子。原理就是EasyHook自己先把API调用拦截下来,然后用CLR Hosting API启动一个CLR的runtime,然后用runtime去加载一个GAC中的Assembly,然后用反射去调用你的代码,并且给你提供了足够的RuntimeInfo,使得你可以知道当前的环境,以及回调原始的API。不过仍然在Hook系统的API的时候,仍然要非常小心死锁的问题。

    EasyHook也不是完美的。据我个人测试,它远程Hook .NET产生出来的exe有问题。不过这是它的Remote Injection部分的问题(很可能是CreateRemoteThread与CLR冲突导致的)。而且EasyHook确定指令长度那块内嵌的反汇编器来路不明(作者自述是从一个德文论坛上下载的程序反汇编来的),其实可以考虑用distorm代替。

    其内部实现有一段关键的汇编代码叫trampoline(蹦床)。这段代码就是那被覆盖的5个字节要jmp到的一段代码,它负责调用你的Hook和善后。所以说如果你用的是.NET写的钩子,这是一个三级跳的过程:

    1、应用程序调用API

    2、API被调用(其实是我们的jmp指令被调用,因为头5个字节被覆盖了)

    3、那个5个字节的jmp,跳转到了trampoline,这蹦床被jmp了,哈哈,所以弹了回去

    4、trampoline跳转到了EasyHook的Thread Deadlock Barrier

    5、EasyHook再用CLR Hosting API跳转到了我们的.NET代码

    非常Hard Core。有机会给大家注释这段代码(我没告诉你EasyHook是开源的吗?),不过今天就写到这里了。

  • 相关阅读:
    用户在第二层,而你在第五层,深度玩转华为预测服务
    Docker启动PostgreSQL时创建多个数据库
    CORS跨源资源共享概念及配置(Kubernetes Ingress和Spring Cloud Gateway)
    HTTP Cookies知识-查看、发送、获取、返回
    Kubernetes ConfigMap详解,多种方式创建、多种方式使用
    示例讲解PostgreSQL表分区的三种方式
    Springboot WebFlux集成Spring Security实现JWT认证
    Bean初始化操作initMethod、@PostConstruct和InitializingBean
    Springboot集成Spring Security实现JWT认证
    Spring Data JPA的Audit功能,审计数据库的变更
  • 原文地址:https://www.cnblogs.com/taowen/p/1324016.html
Copyright © 2020-2023  润新知