也谈SSDT Hook(一)
一、原理篇
1. 关于系统服务。
系统服务是由操作系统提供一组函数,使得开发者能够通过APIs直接或间接的调用。一个API可以对应一个系统服务,也可以一个API依赖多个系统服务。比如,WriteFile API对应的系统服务是ntoskrnl.exe中的NtWriteFile。系统服务分发属于陷阱分发的范畴,更详细的资料可参考’Windows Internal(4th edition)’相关章节。从APIs到系统服务的分发过程可简化为图1:
图1
图1只表现了ntdll.dll分发系统服务陷阱的过程,对于GDI/USER过程,它是负责管理图形界面的,暂不作考虑。要钩住系统服务当然要修改服务分发表了(要搞系统服务当然不只值一个方法,但是本文只考虑怎样通过SSDT来做),所以,关键是要找到服务分发列表的索引号(0,1,2,…,n),就可以找到相应的系统服务内存入口地址。系统服务分发表的结构可以直观的简化为图2:
图2
Windows系统服务是Nt*系列的Native APIs,他们在内存中的入口地址保存在SSDT中。另外,还应该注意Zw*系列的Native APIs,这是以Nt开头的系统服务入口点的镜像,它把原先的访问模式设置为内核模式,从而消除了参数的有效性检查过程,因为Nt系统服务只有当原来的访问模式为ring 3时才进行参数检查。多说几句,除了在ring 0的ntoskrnl.exe有导出中,在ring 3的ntdll.dll中也有这个两系列的函数。这四者的关系怎样呢?以NtQuerySystemInformation系统服务为例:
Ring 3
lkd> u ntdll!ZwQuerySystemInformation L4
ntdll!ZwQuerySystemInformation:
7c92e1aa b8ad000000 mov eax,0ADh
7c92e1af ba0003fe7f mov edx,offset SharedUserData!SystemCallStub (7ffe0300)
7c92e1b4 ff12 call dword ptr [edx]
7c92e1b6 c21000 ret 10h
lkd> u ntdll!NtQuerySystemInformation L4
ntdll!ZwQuerySystemInformation:
7c92e1aa b8ad000000 mov eax,0ADh
7c92e1af ba0003fe7f mov edx,offset SharedUserData!SystemCallStub (7ffe0300)
7c92e1b4 ff12 call dword ptr [edx]
7c92e1b6 c21000 ret 10h
由此可见,在Ring 3下ntdll.dll中,这两个函数是完全一样的。
Ring 0
lkd> u nt!ZwQuerySystemInformation L6
nt!ZwQuerySystemInformation:
804de440 b8ad000000 mov eax,0ADh
804de445 8d542404 lea edx,[esp+4]
804de449 9c pushfd
804de44a6a08 push 8
804de44ce8e0110000 call nt!KiSystemService (804df631)
804de451 c21000 ret 10h
lkd> u nt!NtQuerySystemInformation
nt!NtQuerySystemInformation:
8057e786 6810020000 push 210h
8057e78b 6830ab4e80 push offset nt!ExTraceAllTables+0x1eb (804eab30)
8057e790 e8a64cf6ff call nt!_SEH_prolog (804e343b)
8057e795 33c0 xor eax,eax
8057e797 8945e4 mov dword ptr [ebp-1Ch],eax
8057e79a8945dc mov dword ptr [ebp-24h],eax
8057e79d 8945fc mov dword ptr [ebp-4],eax
8057e7a0 64a124010000 mov eax,dword ptr fs:[00000124h]
在Ring 0下,ZwQuerySystemInformation实现了对KiSystemService(系统服务分发器)的调用,并在阿函数开始的时候将索引号放入eax寄存器(mov eax,0ADh),这是我们需要的,通过0ADh可以找到系统服务NtQuerySystemInformation,下节详细讨论。
在’ Undocumented Windows 2000 Secrets’中有所阐述,这里让大家看到事实了。找几个其他的APIs尝试一下,自己去悟吧,没悟性成不了佛的。
1. 找到Hook入口
系统服务分发表是一个C的数据结构,ntolkrnl.exe导出了该结构的指针(符号为KeServiceDescriptorTable)。其实,内核还维护了一个替代的SDT,其名称为:KeServiceDescriptorTableShadow,但这个SDT并没有被ntolkrnl.exe导出。KeServiceDescriptorTable定义如下:
struct _KeServiceDescriptorTableEntry
{
unsigned int *ServiceTableBase;
unsigned int *ServiceCounterTableBase; //Used only in checked build
nsigned int NumberOfServices;
unsigned char *ParamTableBase;
} KeServiceDescriptorTableEntry, *PKeServiceDescriptorTableEntry
其第一个成员ServiceTableBase就是系统服务列表数组的其实地址。
首先,就是要获取KeServiceDescriptorTableEntry的内存地址。由于KeServiceDescriptorTable已经被导出,所以导入KeServiceDescriptorTable即可:
extern PServiceDescriptorTableEntry KeServiceDescriptorTable;
创建访问参考:
PServiceDescriptorTableEntry pSDT = KeServiceDescriptorTable;
不过,hoglund是这样做的
__declspec(dllimport) ServiceDescriptorTableEntry_t KeServiceDescriptorTable;
异曲同工,都是导入KeServiceDescriptorTable。
现在找到了SDT,有了一个好的开头,接下来就是要找到关注的系统服务了,才能做一些想做的事情。回到ZwQuerySystemInformation的那段反汇编的代码:
lkd> u nt!ZwQuerySystemInformation L6
nt!ZwQuerySystemInformation:
804de440 b8ad000000 mov eax,0ADh
804de445 8d542404 lea edx,[esp+4]
804de449 9c pushfd
804de44a6a08 push 8
804de44ce8e0110000 call nt!KiSystemService (804df631)
804de451 c21000 ret 10h
它索引号0ADh放到了eax寄存器,dd一下:
lkd> dd nt!ZwQuerySystemInformation
804de440 0000adb8 24548d00 086a9c04 0011e0e8
804de450 0010c200 0000aeb8 24548d00 086a9c04
804de440是它的入口地址,AD就存放在那段机器码里。这样就可以了:
DWORD dwIndex= *(*ULONG)((UCHAR*)ZwQuerySystemInformation+1);
好多小星星(*),慢慢理解吧。转换成汇编就容易理解了:
mov ecx, DWORD PTR [ZwQuerySystemInformation];
mov edx, [ecx+1];
最后,有了KeServiceDescriptorTable.ServiceTableBase的地址,又找到了索引号,这样所关注系统服务就找到了。
KeServiceDescriptorTable.ServiceTableBase+ dwIndex*4
手工试一下,还是以ZwQuerySystemInformation为例子。
通过KeServiceDescriptorTable.ServiceTableBase获取系统服务数组的起始地址
lkd> dd KeServiceDescriptorTable
8055a680 804e36a8 00000000 0000011c80513eb8
是这里804e36a8,可以先都为快:
lkd> dd 804e36a8
804e36a8 80580302 80579b8c8058b7ae 805907e4
804e36b8 805905fe 806377a0 80639931 8063997a
804e36c8 8057560b 806481cf 80636f5f8058fb85
804e36d8 8062f0a4 8057be31 8058cc26 806261bd
804e36e8 805dcf20 80568f9d 805d9ac1 805a2bb0
804e36f8 804e3cb4 806481bb 805ca22c804f0e28
804e3708 80569649 80567d49 8058fff3 8064e1c1
804e3718 8058f8f5 80581225 8064e42ff584dc90
试一下第一个系统服务80580302是谁呢?
lkd> u 80580302
nt!NtAcceptConnectPort:
80580302 689c000000 push 9Ch
80580307 68d8224f80 push offset nt!_real+0x128 (804f22d8)
8058030c e82a31f6ff call nt!_SEH_prolog (804e343b)
80580311 64a124010000 mov eax,dword ptr fs:[00000124h]
80580317 8a8040010000 mov al,byte ptr [eax+140h]
8058031d 884590 mov byte ptr [ebp-70h],al
80580320 84c0 test al,al
80580322 0f84e9080300 je nt!NtAcceptConnectPort+0x1df (805b0c11)
果然是NtAcceptConnectPort!套用算法公式找一下NtQuerySystemInformation:
804e36a8+0xAD*4 = 804E395C
lkd> dd 804E395C
804e395c 8057e786 80590ad0 80591857 805871f3
804e396c f7377b46 8056d338 80570e3b 8059068f
804e397c 804e303a806477af 805710d8 805dae6c
804e398c 8058f6a6 8057b545 8057dbee 80566809
804e399c 8058b492 80567272 8065a3d6 8064e029
804e39ac f58647c0 8057f307 8056ae96 8056a9ae
804e39bc 80622b92 8062b803 8058aa2cf584d960
804e39cc 8062b5fc 8059d753 8053c14af5864a50
那就是8057e786的位置了,反汇编:
lkd> u 8057e786
nt!NtQuerySystemInformation:
8057e786 6810020000 push 210h
8057e78b 6830ab4e80 push offset nt!ExTraceAllTables+0x1eb (804eab30)
8057e790 e8a64cf6ff call nt!_SEH_prolog (804e343b)
8057e795 33c0 xor eax,eax
8057e797 8945e4 mov dword ptr [ebp-1Ch],eax
8057e79a8945dc mov dword ptr [ebp-24h],eax
8057e79d 8945fc mov dword ptr [ebp-4],eax
8057e7a0 64a124010000 mov eax,dword ptr fs:[00000124h]
真的是NtQuerySystemInformation。搞定了!
有些网上流传的代码将新的系统服务函数命名为NewZwQuerySystemInformation在语法角度是没有什么错误,但是实际上它并不是替换了ZwQuerySystemInformation而是NtQuerySystemInformation,这种命名让读者产生误解,应该是NewNtQuerySystemInformation更为妥当。我们只是通过ZwQuerySystemInformation来找到NtQuerySystemInformation,最终都是在Nt*系列的函数上做文章的。对于那些“钩住Zw*”文章的提法,也不敢苟同,坏事都是新的Nt*干的,Zw*只是提供了线索,有点受冤了。
2. 系统服务替换及还原
万事俱备,是不是可以“动手”了?不妨试一下,Windows 2000及以上必定是BSOD,伤心的蓝色海洋。Why?该内存区域写保护。点解?去掉写保护,修改标识寄存器CR0。
31 |
30 |
... |
18 |
17 |
16 |
... |
5 |
4 |
3 |
2 |
0 |
1 |
P/G |
C/D |
... |
A/M |
W/P |
... |
N/E |
E/T |
T/S |
E/M |
M/P |
P/E |
我们主要注意这个WP这位,其他的请参考IA-32 Volume 3A;
WP——Write Protect,当设置为1时只提供读页权限;
PE——Paging,当设置为1时提供分页;
MP——Protection Enable,当设置为1时进入保护模式;
因此,只要把WP这一位设置为0时,就可以修改SSDT了。
去除写保护标示:
unsigned long _cr0;
_asm
{
cli;
mov eax,cr0
mov _cr0,eax
and eax,0fffeffffh
mov cr0,eax
}
恢复写保护:
_asm
{
mov eax, _cr0
mov cr0,eax
sti
}
还有更绅士的做法,将整个SSDT的存储数组映射到一个非分页MDL(Memory Description List)的内存空间,然后就方便对这块内存区域修改属性、改写内容... Greg Hoglund那个例子的做法。
lkd> dt _mdl
nt!_MDL
+0x000 Next : Ptr32 _MDL
+0x004 Size : Int2B
+0x006 MdlFlags : Int2B
+0x008 Process : Ptr32 _EPROCESS
+0x00cMappedSystemVa : Ptr32 Void
+0x010 StartVa : Ptr32 Void
+0x014 ByteCount : Uint4B
+0x018 ByteOffset : Uint4B
最后,服务卸载时当然不能忘了把SSDT修改过来,就是上述操作的逆过程,大同小异。
(待续)
也谈SSDT Hook(一)相关推荐
- 也谈SSDT Hook(二)
一.实战篇 本不想摘代码,既然实战,就不多讲废话了,还是贴上吧,谁都有违背原则的时候:). 代码一:经典案例,替换NtQuerySystemInformation,列取所有查询到的进程名,我使用修改C ...
- 进程隐藏与进程保护(SSDT Hook 实现)(二)
文章目录: 1. 引子 – Demo 实现效果: 2. 进程隐藏与进程保护概念: 3. SSDT Hook 框架搭建: 4. Ring0 实现进程隐藏: 5. Ring0 实现进程保护: 6. 隐藏进 ...
- 进程隐藏与进程保护(SSDT Hook 实现)(二) 转载自 Zachary.XiaoZhen - 梦想的天空
文章目录: 1. 引子 – Demo 实现效果: 2. 进程隐藏与进程保护概念: 3. SSDT Hook 框架搭建: 4. Ring0 实现进程隐藏: 5. Ri ...
- FSD HOOK与SSDT HOOK恢复简单思路
FSD 解释: File System Driver文件系统驱动程序,分为本地FSD和远程FSD. (1) 本地FSD:允许用户访问本地计算机上的数据 --本地FSD负责向I/O管理器注册自己,当开始 ...
- Win64 驱动内核编程-22.SHADOW SSDT HOOK(宋孖健)
SHADOW SSDT HOOK HOOK 和 UNHOOK SHADOW SSDT 跟之前的 HOOK/UNHOOK SSDT 类似,区别是查找SSSDT的特征码,以及根据索引计算函数地址的公式,还 ...
- (52)系统调用阶段测试——基于 SSDT HOOK 的 FindWindowA 监视器
一.项目说明 SSDT HOOK 内核函数我们已经会了,请看这两篇博客: SSDT HOOK 实现进程保护 补充内容:SSDT HOOK 模板 此次考试和 hook NtOpenProcess 或 N ...
- (51)SSDT HOOK 实现进程保护
一.回顾 在前面的课程里,我们逆向分析了 KiSystemService / KiFastCallEntry 的部分代码,我们发现这两个函数找系统服务表 SystemServiceTable 的方法是 ...
- (50)补充内容:SSDT HOOK 模板
代码 下面的驱动代码ssdt hook了NtOpenProcess函数,可以监视打开进程的操作. #include <ntddk.h> #include <ntstatus.h> ...
- Windows驱动开发学习笔记(五)—— SSDT HOOK
Windows驱动开发学习笔记(五)-- SSDT HOOK 系统服务表 系统服务描述符表 实验一:通过代码获取SSDT表地址 通过页表基址修改页属性 方法1:修改页属性 方法2:修改CR0寄存器 实 ...
最新文章
- LabVIEW保存、读取配置文件
- 中国电子学会图形化四级编程题:绘制雪花
- 二十九、基本分页存储管理的基本概念
- python爬虫:Multipart/form-data POST文件上传详解
- MAT之GA:利用GA对一元函数进行优化过程,求x∈(0,10)中y的最大值
- C++描述的位运算总结
- linux 安装反病毒软件
- Linux 使用 yum 查看安装的软件包
- python打印菱形星号代码_Python打印“菱形”星号代码
- 前端学习(2645):懂代码之header表头页之未读消息
- java 日志设计_Java日志设计实践(3) - 开发篇
- 区块链开发公司浅析区块链服务商提供哪些解决方案
- 《Python预测之美》送书活动,中奖名单公示
- PHP写评论模块,uchome2.0 日志评论模块分析(php代码及js代码分析)
- 以太坊漫游指南:读懂以太坊发展路线图
- doris core安装报错Makefile:158: recipe for target 'processor.o' failed make: *** [processor.o] Error 1
- 北京大学数学科学学院2006\9\20声明:坚持真理、追求卓越zz
- 学习C语言的心路历程
- 生猪出售 matlab,数学建模论文-肥猪最佳销售时机问题.doc
- 网站发布一般步骤以及解决方法
热门文章
- HDU 1561 The more ,The Better
- windows简易使用composer 安装国内镜像
- /var/lib/mlocate/mlocate.db
- NEU 	1497 Kid and Ants 思路 难度:0
- 小赋诗歌一首,以感学生时代结束
- scatter() 散点图样式
- [云炬python3玩转机器学习] 6-1什么是梯度下降法
- Coursera吴恩达《神经网络与深度学习》课程笔记(2)-- 神经网络基础之逻辑回归
- 台湾大学林轩田机器学习基石课程学习笔记12 -- Nonlinear Transformation
- 利用ajax赋值,jquery利用async在ajax中给全局变量赋值