一、原理篇

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(一)相关推荐

  1. 也谈SSDT Hook(二)

    一.实战篇 本不想摘代码,既然实战,就不多讲废话了,还是贴上吧,谁都有违背原则的时候:). 代码一:经典案例,替换NtQuerySystemInformation,列取所有查询到的进程名,我使用修改C ...

  2. 进程隐藏与进程保护(SSDT Hook 实现)(二)

    文章目录: 1. 引子 – Demo 实现效果: 2. 进程隐藏与进程保护概念: 3. SSDT Hook 框架搭建: 4. Ring0 实现进程隐藏: 5. Ring0 实现进程保护: 6. 隐藏进 ...

  3. 进程隐藏与进程保护(SSDT Hook 实现)(二) 转载自 Zachary.XiaoZhen - 梦想的天空

    文章目录:                   1. 引子 – Demo 实现效果: 2. 进程隐藏与进程保护概念: 3. SSDT Hook 框架搭建: 4. Ring0 实现进程隐藏: 5. Ri ...

  4. FSD HOOK与SSDT HOOK恢复简单思路

    FSD 解释: File System Driver文件系统驱动程序,分为本地FSD和远程FSD. (1) 本地FSD:允许用户访问本地计算机上的数据 --本地FSD负责向I/O管理器注册自己,当开始 ...

  5. Win64 驱动内核编程-22.SHADOW SSDT HOOK(宋孖健)

    SHADOW SSDT HOOK HOOK 和 UNHOOK SHADOW SSDT 跟之前的 HOOK/UNHOOK SSDT 类似,区别是查找SSSDT的特征码,以及根据索引计算函数地址的公式,还 ...

  6. (52)系统调用阶段测试——基于 SSDT HOOK 的 FindWindowA 监视器

    一.项目说明 SSDT HOOK 内核函数我们已经会了,请看这两篇博客: SSDT HOOK 实现进程保护 补充内容:SSDT HOOK 模板 此次考试和 hook NtOpenProcess 或 N ...

  7. (51)SSDT HOOK 实现进程保护

    一.回顾 在前面的课程里,我们逆向分析了 KiSystemService / KiFastCallEntry 的部分代码,我们发现这两个函数找系统服务表 SystemServiceTable 的方法是 ...

  8. (50)补充内容:SSDT HOOK 模板

    代码 下面的驱动代码ssdt hook了NtOpenProcess函数,可以监视打开进程的操作. #include <ntddk.h> #include <ntstatus.h> ...

  9. Windows驱动开发学习笔记(五)—— SSDT HOOK

    Windows驱动开发学习笔记(五)-- SSDT HOOK 系统服务表 系统服务描述符表 实验一:通过代码获取SSDT表地址 通过页表基址修改页属性 方法1:修改页属性 方法2:修改CR0寄存器 实 ...

最新文章

  1. LabVIEW保存、读取配置文件
  2. 中国电子学会图形化四级编程题:绘制雪花
  3. 二十九、基本分页存储管理的基本概念
  4. python爬虫:Multipart/form-data POST文件上传详解
  5. MAT之GA:利用GA对一元函数进行优化过程,求x∈(0,10)中y的最大值
  6. C++描述的位运算总结
  7. linux 安装反病毒软件
  8. Linux 使用 yum 查看安装的软件包
  9. python打印菱形星号代码_Python打印“菱形”星号代码
  10. 前端学习(2645):懂代码之header表头页之未读消息
  11. java 日志设计_Java日志设计实践(3) - 开发篇
  12. 区块链开发公司浅析区块链服务商提供哪些解决方案
  13. 《Python预测之美》送书活动,中奖名单公示
  14. PHP写评论模块,uchome2.0 日志评论模块分析(php代码及js代码分析)
  15. 以太坊漫游指南:读懂以太坊发展路线图
  16. doris core安装报错Makefile:158: recipe for target 'processor.o' failed make: *** [processor.o] Error 1
  17. 北京大学数学科学学院2006\9\20声明:坚持真理、追求卓越zz
  18. 学习C语言的心路历程
  19. 生猪出售 matlab,数学建模论文-肥猪最佳销售时机问题.doc
  20. 网站发布一般步骤以及解决方法

热门文章

  1. HDU 1561 The more ,The Better
  2. windows简易使用composer 安装国内镜像
  3. /var/lib/mlocate/mlocate.db
  4. NEU 1497 Kid and Ants 思路 难度:0
  5. 小赋诗歌一首,以感学生时代结束
  6. scatter() 散点图样式
  7. [云炬python3玩转机器学习] 6-1什么是梯度下降法
  8. Coursera吴恩达《神经网络与深度学习》课程笔记(2)-- 神经网络基础之逻辑回归
  9. 台湾大学林轩田机器学习基石课程学习笔记12 -- Nonlinear Transformation
  10. 利用ajax赋值,jquery利用async在ajax中给全局变量赋值