其实 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; //每个系统服务参数字节数表的基地址-系统服务参数表SSPT
PUCHAR 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文件

//SSDT.c
#include "SSDT.h" //关闭内存写保护
VOID WPOFF()
{
__asm
{
cli //屏蔽中断
mov eax, cr0
and eax, not 10000h
mov cr0, eax
}
} //开启内存写保护
VOID WPON()
{
__asm
{
mov eax, cr0
or eax, 10000h
mov cr0, eax
sti //回复中断
}
} /*
第一个参数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,edi
84647e40 55 push ebp
84647e41 8bec mov ebp,esp
84647e43 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表函数Hook
HOOK_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表函数Hook
Cr0RemoveSSDTHook(&int_hook_index, RealZwOpenProcess); DbgPrint("ZwOpenProcess Remove Cr0RemoveSSDTHook success\r\n");
}
else
{
//移除采用Mdl方式的SSDT表函数Hookook
if (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 code
DbgPrint("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; //定义一个BOOLEAN
if (bool_hook_type)
{
//Cr0方式SSDT表函数Hook
Cr0SSDTHook(&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表函数Hook
if (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=obj INCLUDE=.\ SOURCES = SSDTHook.c\
SSDT.c \

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

参考资料:

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

SSDT表函数Hook原理的更多相关文章

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

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

  2. SSDT表详解

    SSDT(system service dispatch table) 系统服务分派表 SSPT(system service parameter table) 系统服务参数表 #pragma pac ...

  3. SSDT表概念具体解释

    SSDT 的全称是 System Services Descriptor Table,系统服务描写叙述符表. 这个表就是一个把 Ring3 的 Win32 API 和 Ring0 的内核 API 联系 ...

  4. Hook原理--逆向开发

    今天我们将继续讲解逆向开发工程另一个重要内容--Hook原理讲解.Hook,可以中文译为“挂钩”或者“钩子”,逆向开发中改变程序运行的一种技术.按照如下过程进行讲解 Hook概述 Hook技术方式 f ...

  5. SSDT表概念详解

    SSDT 的全称是 System Services Descriptor Table,系统服务描述符表. 这个表就是一个把 Ring3 的 Win32 API 和 Ring0 的内核 API 联系起来 ...

  6. SSDT表结构的深入学习

    SSDT表的知识目录: A.了解SSDT结构 B.由SSDT索引号获取当前函数地址        C.如何获取索引号 D.获取起源地址-判断SSDT是否被HOOK E.如何向内核地址写入自己代码 A. ...

  7. Android C/C++层hook和java层hook原理以及比较

    作者:Denny Qiao(乔喜铭),云智慧/架构师. 云智慧集团成立于2009年,是全栈智能业务运维解决方案服务商.经过多年自主研发,公司形成了从IT运维.电力运维到IoT运维的产业布局,覆盖ITO ...

  8. 节点地址的函数list_entry()原理详解

    本节中,我们继续讲解,在linux2.4内核下,如果通过一些列函数从路径名找到目标节点. 3.3.1)接下来查看chached_lookup()的代码(namei.c) [path_walk()> ...

  9. 内核知识第12讲,SSDT表.以用户模式到系统模式的两种方式.

    内核知识第12讲,SSDT表.以用户模式到系统模式的两种方式. 一丶IDT解析. 我们知道.IDT表中存放着各种中断信息.比如当我们调用int 3的时候,则会调用IDT表中的第三项来进行调用. 而函数 ...

随机推荐

  1. 《C++ Primer》笔记 第11章 关联容器

    关联容器类型 解释 按关键字有序保存元素 -- map 关联数组:保存关键字-值对 set 关键字即值,即只保存关键字的容器 multimap 关键字可重复出现的map multiset 关键字可重复 ...

  2. 干货!!!测试如何确定是前端bug还是后端bug

    目前的项目大多数都是前后端分离的,当我们发现bug后不知道指派给哪位开发,指派错了不仅影响解决bug 的效率,还容易被开发怼.最主要的是人家会认为你不专业,不专,不专呀.废话少说,上干货(踩过的坑)! ...

  3. css标题文字和下划线重叠

    <view class="text"> <text class="textCon">标题</text> <text c ...

  4. 八. SpringCloud消息总线

    1. 消息总线概述 1.1 分布式配置的动态刷新问题 Linux运维修改Github上的配置文件内容做调整 刷新3344,发现ConfigServer配置中心立刻响应 刷新3355,发现ConfigC ...

  5. Ext.Net一般处理程序上传文件

    引言 最近公司项目全部转向前端化,故所有aspx页面业务逻辑尽可能的转到用户控件前台页面完成.以方便每次发布项目时只是替换前端页面不会影响客户体验. 既然转到前台逻辑,那么必须走后台的业务也就单独封装 ...

  6. windows跳转端口

    //将客户机端口内网33306转发到外网,在通过本地连接ssh -L 3306:10.0.0.208:3306 ttx@180.180.180.182--通过git bash执行命令--10.0.0. ...

  7. Java线程安全问题

    线程安全问题是一个老生常谈的问题,那么多线程环境下究竟有那些问题呢?这么说吧,问题的形式多种多样的,归根结底的说是共享资源问题,无非可见性与有序性问题. 1. 可见性 可见性是对于内存中的共享资源来说 ...

  8. Java 运行时数据区和内存模型

    运行时数据区是指对 JVM 运行过程中涉及到的内存根据功能.目的进行的划分,而内存模型可以理解为对内存进行存取操作的过程定义.总是有人望文生义的将前者描述为 "Java 内存模型" ...

  9. Python基础(1)——变量和数据类型[xiaoshun]

    目录 一.变量 1.概述 Variables are used to store information to be referenced(引用)and manipulated(操作) in a co ...

  10. 在用free()函数释放指针内存时为何要将其指针置空

    在通过free()函数释放指针内存之后讲其指针置空,这样可以避免后面的程序对与该指针非法性的判断所造成的程序崩溃问题.释放空间,指针的值并没有改变,无法直接通过指针自身来进行判断空间是否已经被释放,将 ...