其实 SSDT Hook 的原理是很简单的,我们可以知道在 SSDT 这个数组中呢,保存了系统服务的地址,比如对于 Ring0 下的 NtQuerySystemInformation 这个系统服务的地址,就保存在 KeServiceDescriptorTable[105h] 中(计算公式 address = SSDT首地址+服务号*4)

lkd> u 84647e3e

nt!NtQuerySystemInformation:

84647e3e 8bff            mov     edi,edi

84647e40 55              push    ebp

84647e41 8bec          mov     ebp,esp

84647e43 8b5508      mov     edx,dword ptr [ebp+8]

既然是 Hook 的话,我们就可以将这个 KeServiceDescriptorTable[105h] 下保存的服务地址替换掉,将我们自己的 Hook 处理函数的地址来替换掉原来的地址,

Mov eax,84496170h

Mov [eax],MyHookFunction

这样当每次调用 KeServiceDescriptorTable[105h]时就会调用我们自己的这个 Hook 处理函数了,就执行了我们的代码,达到hook原理。

我给大家画个图便以理解:

所有我们只要修改 KeServiceDescriptorTable[105h] 即可达到目的。

所以这就是我们在平时所讲的SSDT Hook原理。

大家都知道在SSDT Hook都时候都看到如下代码:

修改内存属性

__asm

{

cli

mov eax, cr0

and eax, not 10000h

mov cr0, eax

}

恢复内存属性

__asm

{

mov eax, cr0

or eax, 10000h

mov cr0, eax

sti

}

这是为什么呢?原因在于Windows系统对部分内存起用了写保护,IAT 内存属性,来防止内存页被修改,SSDT表的内存属性只是只读。如果你试图对一个只读属性的表进行写入操作,那么等待你的就是无情的蓝屏。因此,要修改ssdt表的话,首先要让表变为可写属性。

注:

修改系统分页内存属性的方法也可以参考:

http://blog.csdn.net/qq1084283172/article/details/40987347这里提供三种修改分页内存的方法。

有两种方法可以绕过写保护,一种是修改控制寄存器的CR0中的写保护位(WP)来绕过,另一种是利用MDL(Memory Descriptor List)来绕过写保护.

第一种方法比较简单,也就是把CR0重的WP(写保护)位设置为0,就可以禁止内存保护了。

比如大家看到看雪上的大部分代码,都是通过修改cr0的wp来禁止内存保护:

http://bbs.pediy.com/showthread.php?t=168061

http://bbs.pediy.com/showthread.php?t=148831

第二种是根据MDL。我们可以根据Mdl分配一段虚拟地址映射到SSDT所在的物理地址,同时因为我们映射的MDL内存属性却是可写,所以就可以修改SSDT,这样就替代了cr0方式。

MDL有很多标志:

1 //MDL Flags

2 #define MDL_MAPPED_TO_SYSTEM_VA     0x0001

3 #define MDL_PAGES_LOCKED                0x0002

4 #define MDL_SOURCE_IS_NONPAGED_POOL 0x0004

5 #define MDL_ALLOCATED_FIXED_SIZE        0x0008

6 #define MDL_PARTIAL                     0x0010

7 #define MDL_PARTIAL_HAS_BEEN_MAPPED 0x0020

8 #define MDL_IO_PAGE_READ                0x0040

9 #define MDL_WRITE_OPERATION             0x0080

10 #define MDL_PARENT_MAPPED_SYSTEM_VA 0x0100

11 #define MDL_LOCK_HELD                   0x0200

12 #define MDL_PHYSICAL_VIEW               0x0400

13 #define MDL_IO_SPACE                        0x0800

14 #define MDL_NETWORK_HEADER          0x1000

15 #define MDL_MAPPING_CAN_FAIL            0x2000

16 #define MDL_ALLOCATED_MUST_SUCCEED  0x4000

我们在SSDTHook过程中,只关心MDL_MAPPED_TO_SYSTEM_VA这个标志位,因为他表示我们申请的MDL内存页池是可读可写的。

下面是采用Cr0和Mdl的方式进行SSDT表的函数Hook的示例:

SSDT.h头文件

//SSDT.h
#ifndef _SSDT_H_
#define _SSDT_H_#include <ntifs.h>//SSDT表的结构体
#pragma pack(1)
typedef struct _SERVICE_DESCRIPTOR_TABLE
{//System Service Dispatch Table的基地址  PULONG   ServiceTable;//SSDT中每个服务被调用次数的计数器。PULONG  CounterTable;//由 ServiceTableBase 描述的服务数目。ULONG   TableSize;//每个系统服务参数字节数表的基地址-系统服务参数表SSPTPUCHAR  ArgumentTable;
} SERVICE_DESCRIPTOR_TABLE, *PSERVICE_DESCRIPTOR_TABLE;
#pragma pack()  //进行ssdt表KeServiceDescriptorTable的导出声明
extern PSERVICE_DESCRIPTOR_TABLE KeServiceDescriptorTable;/*
_Hook为自定义用于Hook SSDT中函数的函数地址
_Function为SSDT表中被Hook函数原始地址
pdword_mapped_table申请的非分页内存的地址
*///内存描述符列表
PMDL  pmdl_system_call;
PVOID *pdword_mapped_table;//获取SSDT表中函数的调用序号Index
#define SYSCALL_INDEX(_Function) *(PULONG)((PUCHAR)_Function+1)//Mdl方式的SSDT表函数Hook
#define HOOK_SYSCALL(_Function, _Hook, _Orig )  \_Orig = (PVOID) InterlockedExchange( (PLONG) &pdword_mapped_table[SYSCALL_INDEX(_Function)], (LONG) _Hook)//移除Mdl方式的SSDT表函数Hook
#define UNHOOK_SYSCALL(_Function, _Hook, _Orig )  \InterlockedExchange( (PLONG) &pdword_mapped_table[SYSCALL_INDEX(_Function)], (LONG) _Hook)//开启内存写保护
VOID WPON();//关闭内存写保护
VOID WPOFF();//采用Cr0方式SSDT表函数Hook
VOID Cr0SSDTHook(int *Index, ULONG_PTR *ul_save_real_addrsss, ULONG_PTR ul_hook_address);//移除采用Cr0方式SSDT表函数Hook
VOID Cr0RemoveSSDTHook(int *Index, ULONG_PTR ul_save_real_addrsss);//采用Mdl方式SSDT表函数Hook
NTSTATUS MdlSSDTHook(ULONG_PTR ul_real_function, ULONG_PTR hook_function_addr, ULONG_PTR *ul_save_real_function_addr);//移除采用Mdl方式SSDT表函数Hook
NTSTATUS MdlRemoveSSDTHook(ULONG_PTR ul_real_function, ULONG_PTR hook_function_addr, ULONG_PTR *ul_save_real_function_addr);/*
驱动中输出提示信息推荐使用 KdPrint函数 :
驱动编程学习中,往往需要通过DbgPrint或者KdPrint来输出调试信息,
对于Check版本,KdPrint只是DbgPrint的一个宏定义,而对于Free版本,KdPrint将被优化掉。
*//*
LONG InterlockedExchange(  IN OUT PLONG  Target,  IN LONG  Value);  InterlockedExchange(a,b)能以原子操作的方式交换俩个参数a, b,并返回a以前的值;
因为InterlockedExchange 是原子函数,不会要求中止中断,所以交换指针的方式是安全的。
*//*
#define SYSTEMSERVICE(_Function) KeServiceDescriptorTable.ServiceTableBase[ *(PULONG)((PUCHAR)_Function+1)]#define SYSCALL_INDEX(_Function) *(PULONG)((PUCHAR)_Function+1)#define HOOK_SYSCALL(_Function, _Hook, _Orig ) _Orig = (PVOID) InterlockedExchange( (PLONG) &MappedSystemCallTable[SYSCALL_INDEX(_Function)], (LONG) _Hook)#define UNHOOK_SYSCALL(_Function, _Hook, _Orig)  InterlockedExchange((PLONG)&MappedSystemCallTable[SYSCALL_INDEX(_Function)], (LONG) _Hook)而SYSTEMSERVICE宏是采用由ntoskrnl.exe导出的Zw*函数地址,并返回对应的Nt*函数在SSDT中的地址,SYSCALL_INDEX采用Zw*函数地址并返回它在SSDT中相应的索引号。
由于ZwCreateThread没有被ntoskrnl.exe导出,所以这时我们就无法直接使用上述的宏.
*//*
MDL的全称是Memory Descriptor List,即内存描述符表。我们可以通过MDL描述一块内存区域,
在MDL中包含了该内存区域的起始地址、拥有者进程、字节数量、标记等信息,如下所示:
typedef struct _MDL
{struct _MDL  *Next;CSHORT       Size;CSHORT       MdlFlags;struct _EPROCESS *Process;PVOID          MappedSystemVa;PVOID          StartVa;ULONG         ByteCount;ULONG   ByteOffset;
} MDL, *PMDL;
*//*
#define SYSTEMSERVICE(_function)  KeServiceDescriptorTable.ServiceTableBase[ *(PULONG)((PUCHAR)_function+1)]
*/#endif

SSDTHook.h头文件

//SSDTHook.h
#ifndef _SSDT_HOOK_H_
#define _SSDT_HOOK_H_#include "SSDT.h"//自定义的ZwOpenProcess函数
NTSTATUS NewZwOpenProcess(OUT PHANDLE ProcessHandle,IN ACCESS_MASK DesiredAccess,IN POBJECT_ATTRIBUTES ObjectAttributes,IN PCLIENT_ID ClientId);//声明ZwOpenProcess函数指针类型
typedef NTSTATUS (__stdcall *ZWOPENPROCESS)(OUT PHANDLE ProcessHandle,IN ACCESS_MASK DesiredAccess,IN POBJECT_ATTRIBUTES ObjectAttributes,IN PCLIENT_ID ClientId);
ZWOPENPROCESS RealZwOpenProcess;//决定我们使用哪种hook
BOOLEAN bool_hook_type = TRUE;//设置SSDT表Hook函数的Index
int int_hook_index = 190;//保存ZwOpenProcess函数的真实值
ULONG_PTR ul_ZwOpenProcess;
UNICODE_STRING unicode_string;#endif
//SSDT.c
#include "SSDT.h"//关闭内存写保护
VOID WPOFF()
{__asm{cli                  //屏蔽中断mov eax, cr0and eax, not 10000hmov cr0, eax}
}//开启内存写保护
VOID WPON()
{__asm{mov eax, cr0or eax, 10000hmov cr0, eaxsti                   //回复中断}
}/*
第一个参数Index就是服务号,
第二个参数ul_save_real_addrsss就是保存函数原始地址。因为我们要在过滤函数中调用原始地址
第三个参数ul_hook_address就是我们的hook代码的函数
*///Cr0方式SSDT表的函数Hook
VOID Cr0SSDTHook(int *Index, ULONG_PTR *ul_save_real_addrsss, ULONG_PTR ul_hook_address)
{ULONG_PTR ul_real_service_address;ULONG_PTR num = *Index;//设置断点//__asm int 3//根据计算公式//ul_real_service_address = 0x84495d5c + 105*4;//ul_real_service_address = 0x84496170;//获取被Hook函数的原始地址ul_real_service_address = (ULONG)KeServiceDescriptorTable->ServiceTable + num*4;if (ul_real_service_address){//0x84647e3e/*nt!NtQuerySystemInformation:84647e3e 8bff            mov     edi,edi84647e40 55              push    ebp84647e41 8bec            mov     ebp,esp84647e43 8b5508          mov     edx,dword ptr [ebp+8]84647e46 83fa53          cmp     edx,53h*///保存SSDT表中原始函数地址*ul_save_real_addrsss = *((ULONG*)ul_real_service_address);//关闭wp写保护WPOFF();//修改SSDT表中的Hook函数的地址为自定函数的地址*((ULONG*)ul_real_service_address) = ul_hook_address;//开启wp写保护WPON();}
}//移除Cr0方式SSDT表的函数Hook即恢复SSDT中被Hook的函数
VOID Cr0RemoveSSDTHook(int *Index, ULONG_PTR ul_save_real_addrsss)
{ULONG_PTR ul_real_service_address;ULONG_PTR num = *Index;//关闭wp写保护WPOFF();//根据计算公式ul_real_service_address = (ULONG)KeServiceDescriptorTable->ServiceTable + num*4;if (ul_real_service_address){//恢复SSDT表中被Hook函数的地址*((ULONG*)ul_real_service_address) = ul_save_real_addrsss;}//开启wp写保护WPON();
}//******************************************************************************************
//采用比较安全的方法修改ssdt表
//因为SSDT的虚拟地址分页属性是只读的,我们不能够直接修改它,否则会产生蓝屏
//我们借助Mdl分配一段虚拟地址映射到SSDT所在的物理地址,
//同时因为我们映射的MDL内存属性却可以是可写,所以就可以修改ssdt,这样就替代了cr0方式。
//******************************************************************************************//采用Mdl方式的SSDT表函数Hook
NTSTATUS MdlSSDTHook(ULONG_PTR ul_real_function, ULONG_PTR hook_function_addr, ULONG_PTR *ul_save_real_function_addr)
{//设置断点//__asm int 3//创建内存描述符列表Mdl,映射到SSDT表所在的内存物理地址pmdl_system_call = MmCreateMdl(NULL, KeServiceDescriptorTable->ServiceTable, KeServiceDescriptorTable->TableSize*sizeof(ULONG_PTR));//判断内存描述符列表Mdl if(!pmdl_system_call){return STATUS_UNSUCCESSFUL;}//构建非分页内存,建立虚拟地址与物理地址的映射关系MmBuildMdlForNonPagedPool(pmdl_system_call);//改变MDL的标志,设置为MDL_MAPPED_TO_SYSTEM_VA标志,让这块内存变可写pmdl_system_call->MdlFlags = pmdl_system_call->MdlFlags | MDL_MAPPED_TO_SYSTEM_VA;//锁定非分页内存pdword_mapped_table = MmMapLockedPages(pmdl_system_call, KernelMode);if (pdword_mapped_table){//开始Mdl方式的SSDT表函数HookHOOK_SYSCALL(ul_real_function, hook_function_addr, *ul_save_real_function_addr);}return STATUS_SUCCESS;
}//移除采用Mdl方式的SSDT表函数Hook
NTSTATUS MdlRemoveSSDTHook(ULONG_PTR ul_real_function, ULONG_PTR hook_function_addr, ULONG_PTR *ul_save_real_function_addr)
{//恢复SSDT表中被Hook的函数UNHOOK_SYSCALL(ul_real_function, *ul_save_real_function_addr, hook_function_addr);if(pmdl_system_call){//解锁非分页内存MmUnmapLockedPages(pdword_mapped_table, pmdl_system_call);//释放申请的非分页内存IoFreeMdl(pmdl_system_call);return STATUS_SUCCESS;}return STATUS_UNSUCCESSFUL;
}

SSDTHook.c文件

//SSDTHook.c
#include "SSDTHook.h"//卸载函数
VOID DriverUnload(IN PDRIVER_OBJECT DriverObject)
{if (bool_hook_type){//移除采用Cr0方式的SSDT表函数HookCr0RemoveSSDTHook(&int_hook_index, RealZwOpenProcess);DbgPrint("ZwOpenProcess Remove Cr0RemoveSSDTHook success\r\n");}else{//移除采用Mdl方式的SSDT表函数Hookookif (MdlRemoveSSDTHook((ULONG_PTR)ul_ZwOpenProcess, NewZwOpenProcess, &RealZwOpenProcess) == STATUS_SUCCESS){DbgPrint("ZwOpenProcess Remove MdlRemoveSSDTHook success\r\n");}}DbgPrint("卸载完成!\n");
}//因为这节只是阐述SSDTHOOK原理,所以只是简单的弄一个过滤函数
/*
lkd> u 8a114070 l 30
8a114070 8bff            mov     edi,edi
8a114072 55              push    ebp
8a114073 8bec            mov     ebp,esp
8a114075 8b4514          mov     eax,dword ptr [ebp+14h]
8a114078 50              push    eax
8a114079 8b4d10          mov     ecx,dword ptr [ebp+10h]
8a11407c 51              push    ecx
8a11407d 8b550c          mov     edx,dword ptr [ebp+0Ch]
8a114080 52              push    edx
8a114081 8b4508          mov     eax,dword ptr [ebp+8]
8a114084 50              push    eax
8a114085 ff151c60118a    call    dword ptr ds:[8A11601Ch]
8a11408b 5d              pop     ebp
8a11408c c21000          ret     10h
*/
/*
HOOK,SSDT表的内容:
lkd> dd 84495d5c+be*4
84496054  8a114070我们unhook:
lkd> dd 84495d5c+be*4
84496054  84629ad4
*///自定义的ZwOpenProcess函数NewZwOpenProcess
NTSTATUS NewZwOpenProcess(OUT PHANDLE ProcessHandle,IN ACCESS_MASK DesiredAccess,IN POBJECT_ATTRIBUTES ObjectAttributes,IN PCLIENT_ID ClientId)
{//Your OEM codeDbgPrint("You Have Hooked Me\r\n");//在过滤函数当中,要调用,所以我们在hook的时候要保存好return RealZwOpenProcess(ProcessHandle,DesiredAccess,ObjectAttributes,ClientId);
}//****************************************************************************************************************//驱动入口函数DriverEntry
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{//设置卸载例程函数DriverObject->DriverUnload = DriverUnload;//定义一个BOOLEANif (bool_hook_type){//Cr0方式SSDT表函数HookCr0SSDTHook(&int_hook_index, &RealZwOpenProcess, (ULONG_PTR)NewZwOpenProcess);DbgPrint("ZwOpenProcess start Cr0RemoveSSDTHook success\r\n");}else{DbgPrint("ZwOpenProcess start MdlRemoveSSDTHook success\r\n");RtlInitUnicodeString(&unicode_string, L"ZwOpenProcess");//获取函数ZwOpenProcess的原始调用地址ul_ZwOpenProcess = (ULONG_PTR)MmGetSystemRoutineAddress(&unicode_string);if (ul_ZwOpenProcess){//Mdl方式SSDT表函数Hookif (MdlSSDTHook(ul_ZwOpenProcess, (ULONG_PTR)NewZwOpenProcess, &RealZwOpenProcess) == STATUS_SUCCESS){DbgPrint("ZwOpenProcess MdlSSDTHook success\r\n");}}}return STATUS_SUCCESS;
}

驱动编译需要的配置文件的编写

makefile文件

#
# DO NOT EDIT THIS FILE!!!  Edit .\sources. if you want to add a new source
# file to this component.  This file merely indirects to the real make file
# that is shared by all the driver components of the Windows NT DDK
#!INCLUDE $(NTMAKEENV)\makefile.def

sources文件

TARGETNAME=ssdtHook
TARGETTYPE=DRIVER
TARGETPATH=objINCLUDE=.\SOURCES = SSDTHook.c\SSDT.c \

文档和代码的下载地址:http://download.csdn.net/detail/qq1084283172/8838535

AGP讲课资料的整理和修改。

SSDT表函数Hook原理相关推荐

  1. ssdt 表结构及hook的一般理解

    1       Ssdt表的基本结构 KeServiceDescriptorTable 首地址:8055D700 0: kd> dd KeServiceDescriptorTable 8055d ...

  2. Android的so注入( inject)和函数Hook(基于got表) - 支持arm和x86

    本文博客地址:http://blog.csdn.net/qq1084283172/article/details/53942648 前面深入学习了古河的Libinject注入Android进程,下面来 ...

  3. 读取SSDT表和原函数地址

    读取当前地址代码(NtOpenProcess):  LONG *SSDT_Adr,t_addr,adr;  t_addr=(LONG)KeServiceDescriptorTable->Serv ...

  4. android基于plt/got的hook原理

    目录 概述 简单示例 ELF文件格式初探 装载.动态链接与重定位 PLT与GOT 如何定位基址? 如何修改呢? 解析基址和偏移 思考和小结 概述 我们日常开发中编写的C/C++代码经过NDK进行编译和 ...

  5. ELF PLT Hook 原理简述

    [无线平台]ELF PLT Hook 原理简述 简述 Android 是基于Linux的操作系统,因此在Android开发平台上,ELF是原生支持的可执行文件格式:ELF文件格式除了作为可执行文件,还 ...

  6. 驱动开发:内核读取SSDT表基址

    在前面的章节<X86驱动:挂接SSDT内核钩子>我们通过代码的方式直接读取 KeServiceDescriptorTable 这个被导出的表结构从而可以直接读取到SSDT表的基址,而在Wi ...

  7. X64驱动:读取SSDT表基址

    前面的驱动编程相关内容都是在32位环境下进行的,驱动程序与应用程序不同,32位的驱动只能运行在32位系统中,64位驱动只能在64位系统中运行,在WIN32环境下,我们可以各种Hook挂钩各种系统函数, ...

  8. Win7 64位的SSDTHOOK(1)---SSDT表的寻找

    最近在学习64位驱动,涉及到了SSDT的知识,结果发现64位下的SSDT和32位下的SSDT有所不同. 开始发现64位下的KeServiceDescriptorTable是未导出的函数.首先要找到Ke ...

  9. windbg查看SSDT表

    SSDT,System Services Descriptor Table,系统服务描述符表. 见此     https://blog.csdn.net/bcbobo21cn/article/deta ...

  10. 【转】c++虚函数实现原理

    [转]c++虚函数实现原理 原文链接:https://blog.csdn.net/neiloid/article/details/6934135 C++中的虚函数的作用主要是实现了多态的机制.关于多态 ...

最新文章

  1. PowerDesigner生成数据库
  2. 从oracle中读取图片,Pb从oracle中读取和保存图片
  3. diou diou_nms代码分享
  4. 关于对话框DoModal()函数调用失败的原因分析
  5. JZOJ 5878. 【NOIP2018提高组模拟9.22】电路图 A
  6. async / await对异步的处理
  7. java comparator内部类_java - Java Comparator使用.reverseOrder()但内部类 - 堆栈内存溢出...
  8. 《Java并发性和多线程介绍》-Java TheadLocal
  9. linux emacs 快捷键,emacs常用快捷键
  10. 凯撒密码加密,解密的实现,可以在项目上使用
  11. PMI-ACP敏捷项目认证练习题(四)
  12. 关于Windows 2003 安装Inter G33/G31 显卡问题
  13. 应用在夜灯领域的环境光测距感器芯片
  14. 长沙市民吴先生乘坐滴滴D1后,取消了买特斯拉的计划
  15. 牛牛试题排版插件V3更新发布
  16. MyBatis 02
  17. 云原生关乎文化,而不是容器
  18. 梯度消失/爆炸与RNN家族的介绍(LSTM GRU B-RNN Multi-RNNs)-基于cs224n的最全总结
  19. c语言书面作业,巢湖学院2011级网络工程1,2班C语言书面作业(江家宝)版
  20. 1362: [蓝桥杯2018初赛]第几个幸运数(简单题)

热门文章

  1. 2022年免费企业邮箱大全,用免费邮箱发邮件安全吗?
  2. 一山一世界,雅居乐陈卓林“乐活”美好生活,在此进阶
  3. 一只100万年也不死的猫
  4. 【Unscented Kalman Filter】C#无迹卡尔曼滤波Demo--两个示例附代码
  5. 粗糙集(Rough Sets)
  6. 1034-乘积小于 K 的子数组
  7. Java实验1:个人银行账户管理系统总结
  8. linux开发员用游戏本吗,为什么很多程序员使用thinkpad而不是同等价位的游戏本呢?...
  9. CSS(Cascading Style Sheets)
  10. 强化学习——Modle-free DRL算法