原文地址,Ring3层的hook又分为两种类型

2020-01-05 19:23 来源:未知

API Hook的几种实现

0x00 Hook技术

hook技术分为两块:

  • Ring3层的Hook,俗称应用层hook技术
  • Ring0层的Hook,俗称内核层Hook技术

Ring3层的hook又分为两种类型

  • Windows消息的hook
  • WindowsAPI的hook

如下图所示:

永利平台娱乐 1

hook1

永利平台娱乐 2

hook2

永利平台娱乐 3

hook1

关于Windows消息的Hook,相信很多朋友都有接触过的,因为一个SetWindowsHookEx即可以完成消息 Hook,在这里简要介绍一下消息 Hook,消息 Hook 是通过SetWindowsHookEx可以实现将自己的钩子插入到钩子链的最前端,而对于发送给被 Hook 的窗口(也有可能是所有的窗口,即全局 Hook)的消息都会被我们的钩子处理函数所捕获到,也就是我们可以优先于窗体先捕获到这些消息,Windows 消息 Hook 可以实现为进程内消息 Hook 和全局消息 Hook,对于进程内消息 Hook,则可以简单的将 Hook 处理函数直接写在这个进程内,即是自己 Hook 自己,而对于用途更为广泛的全局消息 Hook,则需要将 Hook 处理函数写在一个 DLL 中,这样才可以让你的处理函数被所有的进程所加载(进程自动加载包含 Hook 消息处理函数的 DLL)。

对于 Windows 消息 Hook 呢,可以有个简单的邪恶应用,就是记录键盘按键消息,从而达到监视用户输入的键值信息的目的,这样,对于一些简单的用户通过键盘输入的密码就可以被 Hook 获取到,因为没当用户按下一个键时,Windows 都会产生一个按键消息(当然有按下,弹起等消息的区分),然后我们可以 Hook 到这个按键消息,这样就可以在 Hook 的消息处理函数中获取到用户按下的是什么键了。

不过消息的hook不是本文的重点。

本文要讲的SSDT hook呢其实是属于内核Hook,常见于病毒以及杀软中。

下图展示了内核hook的几个基本类型。

永利平台娱乐 4

Kernel hook

  • 文章来源:
  • 原文作者: ATField
  • 整理日期: 2008-07-16
  • 发表评论

  • 字体大小:

所谓的API Hook,就是利用某种技术将API的调用转为我们自己定义的函数的调用。这种技术在实际项目里面的应用也是很广泛的。最近,我在做关于我们项目的自动化 测试的时候,就遇到了这种情况。在写测试代码之前,我们对测试代码有一些要求。1. 不能因为测试代码而修改原代码。2. 原有的模块是以dll格式输出的,在做测试的时候,要测的类和函数也只能使用dll的导出类或者函数,而不能将源文件重新编译。由于这些限制,导致测试用 例往往不能在普通的机器上运行。比如这样一个函数:

0x01 SSDT简介

SSDT全称System Service Descriptor Table(系统描述符表),这个表用于将Ring3的Win32API和内核的API联系起来。

SSDT并不仅仅只包含一个庞大的地址索引表,它还包含一些有用的信息,如地址索引的基地址,服务函数的个数等。通过修改此表可以达到对一些关心的系统动作进行过滤以及监控的目的。

在NT4.0的windows操作系统中,默认存在两个系统服务描述符表,这两个描述符表对应了两类不同的系统服务,这两个表为:KeServiceDescriptorTable(SSDT)和KeServiceDescriptorTable(SSDT Shadow)。其中SSDT负责处理来自Ring3层的Kernel32.dll的系统调用。而SSDT Shadow则主要处理来自User32.dll和GDI32.dll的系统调用。同时SSDT在ntoskrnl.exe中是导出的,而SSDT Shadow如其名是未被Windows所导出的,而关于SSDT的全部内容都是通KeServiceDescriptorTable来完成的。

以下截图说明,KeServiceDescriptorTable是在ntoskrnl.exe中被导出的:

永利平台娱乐 5

Export1

随后我们看看看看Windows操作系统的源码中如何定义KeServiceDescriptorTable的,通过观察WRK可知,

永利平台娱乐 6

KeServiceDescriptorTable in WRK

这么看还是有点蛋疼啊。改写以下变量的名称吧。

    typedef struct _KSYSTEM_SERVICE_TABLE{
        PULONG ServiceTableBase;            //SSDT的基地址指针
        PULONG ServiceCounterTableBase; //SSDT中每个服务被调用次数表的基地址指针
        ULONG NumberOfService;              //服务函数个数,NumberOfService*4就是整个地址表的大小
        ULONG ParamTableBase;               //SSPT的基地址
    }KSYSTEM_SERVICE_TABLE,*PKSYSTEM_SERVICE_TABLE;
    typedef struct _KSERVICE_TABLE_DESCRIPTOR{
        KSYSTEM_SERVICE_TABLE ntoskrnl;     //ntoskrnl.exe的服务函数
        KSYSTEM_SERVICE_TABLE win32k;       //win32k.sys的服务函数(GDI32/User32)
        KSYSTEM_SERVICE_TABLE notUsed1;
        KSYSTEM_SERVICE_TABLE notUsed2;
    }KSERVICE_TABLE_DESCRIPTOR,*PKSERVICE_TABLE_DESCRIPTOR;
    //导出由ntoskrnl.exe所导出的SSDT
    extern PKSERVICE_TABLE_DESCRIPTOR KeServiceDescriptorTable;

从上述介绍可知,KeServiceDescriptorTable可以看做是一个数组,是4个KSYSTEM_SERVICE_TABLE结构形成的数组,而每个KSYSTEM_SERVICE_TABLE对应一个PE文件导出的服务描述符表,根据这些服务描述符表我们可以获得更加详细的服务信息。在应用层ntdll.dll中的API在这个系统服务描述表中都存在一个与之对应的服务,当我们的应用程序调用ntdll.dll里的API时最终可以调用对应的系统服务函数,通知给内核一个索引,内核通过该索引在SSDT中查找对应的服务,内核调用服务完成应用的API调用请求。

 

 

<!--

应用层调用Win32API流程

有了以上的SSDT基础后,我们再来看看在应用层调用Win32API(主要指ntdll.dll中的API)的流程,我们主要针对ntdll.dll中的NtQuerySystemInformation这个API的调用流程来进行阐述。

这里存在四个类似的API。

永利平台娱乐 7

4APIs

再给出这些API的调用流程。

永利平台娱乐 8

calling way

这里我们可以看到ntdll.dll中的nt和zw都会进入内核层去调用ntoskrnl的zw函数,而zw最终会调用nt函数,这个函数作为内核API最终去请求系统服务的执行。
用exescope工具可以打开ntdll.dll,看到NtQuerySystemInformation以及ZwQuerySystemInformation。

永利平台娱乐 9

ntdll1

永利平台娱乐 10

ntdll2

而实质上zw和nt都是同一函数,指向同一区域,入口地址相同。

因此Ntdll.dll中的API都是对内核API的封装,当Kernel32.dll中的API通过Ntdll.dll去调用系统API时,会进行参数检查,并调用中断(int 2Eh或SysEnter),从而从Ring3进入Ring0,并将所要调用的服务号,即SSDT数组的索引值,存放进寄存器EAX中,并且将参数地址放到指定的寄存器EDX中,再复制参数到内核地址空间,根据存放在EAX中的索引值来在SSDT数组中调用指定的服务。

经过上面步骤我们来到Ring0层。使用exescope看看ntoskrnl.exe中的ZwQuerySystemInformation以及NtQuerySystemInformation。

永利平台娱乐 11

ntoskrnl1

永利平台娱乐 12

ntoskrnl2

再反汇编ntoskrnl这个文件可以看到Zw函数中调用KiSystemService系统服务分发函数时往EAX中存放了索引号ADh。如图:

永利平台娱乐 13

ZwQSystemInfor

随后根据该索引值检索SSDT项,最后根据该SSDT项中所存放的系统服务地址来调用这个系统服务。在这里就是调用KeServiceDescriptorTable[ADh]处保存的地址对应的系统服务。那就是Ring0下的NtQuerySystemInformation。

原文地址:

 

Code highlighting produced by Actipro CodeHighlighter (freeware)

0x02 详解SSDT

这节内我们用WinDbg来调试XP系统,借此说明SSDT是个什么鬼。
据我们上文的结构定义可知,KeServiceDescriptorTable是一个指向4个KSYSTEM_SERVICE_TABLE结构首地址的指针,以下图为例吧。

永利平台娱乐 14

dd KeServiceDescriptorTable

这里的0x80563520这一行就是ntoskrnl对应的服务描述符表结构KSYSTEM_SERVICE_TABLE。那么第一个32位的0x804e58a0则是对应ntoskrnl对应的KSYSTEM_SERVICE_TABLE中的SSDT Base,即服务描述符表的首地址。通过对该首地址的dump,我们可以看到许多以128位为一组排列的服务描述符,每个描述符中的第一个32位(0x80591bfb)对应着系统服务的入口地址。通过对该入口地址的反汇编,可以看到这是SSDT第一个系统服务NtAcceptConnectPort函数。如图:

永利平台娱乐 15

NtAcceptConnectPort

那么知道了SSDT首地址,同时知道了索引,那么我们就可以通过索引来找到对应的系统服务入口地址了。通过计算“SSDT中系统服务地址所在的地址 = SSDT首地址 + 4 * 索引值”,可以推算出NtQuerySystemInformation的起始地址位0x80586ff1,对该地址进行反汇编,可得下图:

永利平台娱乐 16

NtQuerySystemInformation

由此可知,SSDT就是个保存Windows系统服务地址的数组。

原文对我的帮助极大,正是因为看了原文,我才学会了HOOK,鉴于原文的排版不是很好,

注:本文是根据我两年前写的一个系统行为监测程序写成(参考了一些书籍和文章)。最近在论坛上看到有不少人在问关于API Hook的问题,便写成此文,希望能对朋友们在写API Hook代码的时候能够有所帮助。

-->int func()
{
    //Some initializing codes
    int hardware_code = get_hardware_code();
    if (is_valid_code(hardware_code))
    {
        //永利平台娱乐 17
    }
    //永利平台娱乐 18
永利平台娱乐,    return ret;
}

0x03 SSDT hook原理

从上面的分析中我们可以看到SSDT数组中保存了系统服务的地址,如Ring0下的NtQuerySystemInformation系统服务地址,就保存在KeServiceDescriptorTable[ADh]中,既然hook就是取出这个地址后替换上我们的hook函数,在hook函数中执行原函数即可。

参考文献:

http://www.cnblogs.com/boyxiao/archive/2011/09/03/2164574.html

又没有原工程例子源码下载,因此我决定对其重新整理,文章后面附有我测试时的工程源码下载地址。

1 基本原理

API Hook是什么我就不多说了,直接进入正题。API Hook技术主要有下面的技术难点:

  1. 如何将自己的的代码Inject到其他进程
  2. 如何Hook到API

此处,函数get_hardware_code()是与特定平台相关的,在普通PC上运行肯定无法获得正确的结果。如果拿不到正确的结果,也就不能 对函数func()进行测试了。于是,我们就可以利用API Hook技术,在测试代码里面,把所有对get_hardware_code()的调用换成我们自定义的函数 mock_get_hardware_code()的调用,这样,在我们自己定义的函数里面,可以返回一个有效的代码以保证原代码能够正确的往下执行。

注:我测试的环境为Win7+VS2008+MFC

1.1 代码的Injection

常用的方法有:

  1. 使用注册表HKLM/Software/Microsoft/Windows NT/CurrentVersion/Windows/AppInit_DLLs

 

这种方法可以指定多个DLL,用空格隔开。这些DLL会被任何用到User32.dll的所有程序自动加载。当User32.dll加载的时候,User32.dll的DllMain会收到一个DLL_PROCESS_ATTACH通知,User32在这个时候读取注册表项中的值,调用LoadLibrary加载各个DLL。

显然使用这种方法要求设置注册表之后立刻重起系统,不过一般情况下这不是大问题。这种方法的主要问题在于,只有用到User32.dll的应用程序才会被Inject。所有的GUI和少部分CUI程序会用到User32.dll,所以如果你的API Hook程序不打算监视CUI程序的话,那么可能问题并不太大。但是如果你的API Hook程序需要监视系统中所有进程的话,这种方法的限制将是非常致命的。

 

  1. 调用SetWindowsHookEx(WH_GETMESSAGE, …, 0)

 

可以使用SetWindowsHookEx(WH_GETMESSAGE, …, 0) 设置全局的消息钩子,虽然可能你的程序并不用到消息钩子,但是钩子的一个副作用是会将对应的DLL加载到所有的GUI线程之中。类似的,只有用到GUI的进程才会被挂接。虽然有这种限制,这种方法仍然是最常用的挂接进程的方法。

 

  1. 使用CreateRemoteThread函数在目标进程中创建远程线程

 

这种方法可以在任意的目标进程中创建一个远程线程,远程线程中可以执行任意代码,这样便可以做到把我们的代码Inject到目标进程中。这种方法具有最大的灵活性,但是难度也最高:

a) 远程线程代码必须可以自重定位

b) 要能够监视进程的启动和结束,这样才可以挂接到所有进程

这两个问题都是可以解决的,在本文中我将重点讲述如何创建远程线程和解决这两个问题。

 

  1. 如果你只是要挂接某个特定进程的并且情况允许你自己来创建此进程,你可以调用CreateProcess(…, CREATE_SUSPENDED)创建子进程并暂停运行,然后修改入口代码使之调用LoadLibrary加载自己的DLL。该方法在不同CPU之间显然是无法移植的。

经过研究,API Hook有这么几种方法。

原文出处,好像是这篇:      //后来才看到的

1.2 Hook API

常用的方法有:

  1. 找到API函数在内存中的地址,改写函数头几个字节为JMP指令跳转到自己的代码,执行完毕再执行API开头几个字节的内容再跳回原地址。这种方法对CPU有较大的依赖性,而且在多线程环境下可能出问题,当改写函数代码的时候有可能此函数正在被执行,这样做可能导致程序出错。
  2. 修改PE文件的IAT (Import Address Table),使之指向自己的代码,这样EXE/DLL在调用系统API的时候便会调用你自己的函数

 

TAG标签:
版权声明:本文由永利平台娱乐发布于新闻动态,转载请注明出处:原文地址,Ring3层的hook又分为两种类型