Windows系统调用学习笔记(四)—— 系统服务表&SSDT

  • 要点回顾
  • 系统服务表
    • 实验:分析 KiSystemService 与 KiFastCallEntry 共同代码
  • SSDT
    • 实验:在SSDT中查找内核函数信息
      • 第一步:查看函数地址
      • 第二步:查看参数个数
      • 第三步:查看内核函数反汇编
  • 练习

要点回顾

API进入0环后调用的函数:

  1. 中断门 – KiSystemService
  2. 快速调用 – KiFastCallEntry

上一篇留了几个练习:

  1. 进0环后,原来的寄存器存在哪里?
  2. 如何根据系统服务号(eax中存储)找到要执行的内核函数?
  3. 调用时参数是存储到3环的堆栈,如何传递给内核函数?
  4. 两种调用方式是如何返回到3环的?

本篇将对第二个和第三个练习进行说明

系统服务表

描述

  1. 系统服务表:System Service Table
  2. 系统服务表共有两张,第一张表后紧接第二张表
  3. 系统服务表里的函数都是来自内核文件导出的函数
  4. 它并不包含内核文件导出的所有函数,而是3环最常用的内核函数
  5. 系统服务表位于 _KTHREAD +00xE0

结构体

typedef struct _SERVICE_DESCRIPTOR_TABLE
{PULONG ServiceTableBase;           // 指针,指向函数地址,每个成员占4字节PULONG ServiceCounterTableBase;      // 当前系统服务表被调用的次数ULONG  NumberOfService;             // 服务函数的总数PUCHAR ParamTableBase;                // 服务函数的参数总长度,以字节为单位,每个成员占一个字节// 如:服务函数有两个参数,每个参数占四字节,那么对应参数总长度为8// 函数地址成员 与 参数总长度成员 一一对应
} SSDTEntry, *PSSDTEntry;

结构图

实验:分析 KiSystemService 与 KiFastCallEntry 共同代码

IDA反汇编

.text:00406932 loc_406932:                             ; CODE XREF: _KiBBTUnexpectedRange+18↑j
.text:00406932                                         ; _KiSystemService+71↑j
.text:00406932                 mov     edi, eax                     ; 从 eax 寄存器中取出3环传进来的系统调用号
.text:00406934                 shr     edi, 8                       ; 系统调用号右移8位
.text:00406937                 and     edi, 30h                     ; 右移后的值和0x30进行与运算; 目的是检测第12位是否为1; WindowsNT基本的(Native)系统调用有两百多个,编号都小于0x1000; 编号大于0x1000的系统调用号是微软扩展出来的; 这些扩展出的系统调用位于动态安装的模块中,即win32k.sys; 若第12位为0x00,则将低12位作为下标在 ntoskrl.exe 中寻找对应的系统调用; 若第12位为0x10,则将低12位作为下标在 win32k.sys 中寻找对应的系统调用
.text:0040693A                 mov     ecx, edi                     ; 将运算结果赋值给 ecx
.text:0040693C                 add     edi, [esi+0E0h]             ; 将系统服务表的指针赋值给 edi; nt!_KTHREAD;    +0x0e0 ServiceTable     : Ptr32 Void; 这里将系统服务表所在地址直接加上edi的运算结果; 巧妙地得到要查哪张表(两张表是连续的),每张表占16字节
.text:00406942                 mov     ebx, eax                     ; 将系统调用号赋值给 ebx
.text:00406944                 and     eax, 0FFFh                   ; 将系统调用号与 0xFFF进行与运算; 目的是保留低12位,作为函数地址的下标
.text:00406949                 cmp     eax, [edi+8]                 ; typedef struct _SYSTEM_SERVICE_TABLE; {;    PVOID ServiceTableBase;     //这个指向系统服务函数地址表;    PULONG ServiceCounterTableBase;;    ULONG NumberOfService;      //服务函数总数;    ULONG ParamTableBase;       //参数总长度; }SYSTEM_SERVICE_TABLE,*PSYSTEM_SERVICE_TABLE;;; +8 是服务函数的个数
.text:0040694C                 jnb     _KiBBTUnexpectedRange        ; 若大于系统调用号的个数则跳转,即系统调用号越界
.text:00406952                 cmp     ecx, 10h                     ; 将 ecx 与0x10进行比较; ecx 保存的是 edi 与0x30与运算后的结果,只能是0x00货0x10
.text:00406955                 jnz     short loc_406972             ; 若系统调用号小于0x1000,则跳转
.text:00406957                 mov     ecx, large fs:18h            ; 只有当ecx == 0x10才会向下执行; 作用是动态加载GUI等图形相关函数
.text:0040695E                 xor     ebx, ebx
.text:00406960
.text:00406960 loc_406960:
.text:00406960                 or      ebx, [ecx+0F70h]
.text:00406966                 jz      short loc_406972
.text:00406968                 push    edx
.text:00406969                 push    eax
.text:0040696A                 call    ds:_KeGdiFlushUserBatch
.text:00406970                 pop     eax
.text:00406971                 pop     edx
.text:00406972
.text:00406972 loc_406972:                             ; CODE XREF: _KiFastCallEntry+B6↑j
.text:00406972                                         ; KiSystemServiceAccessTeb()+6↑j
.text:00406972                 inc     large dword ptr fs:638h      ; _KPRCB -> +0x518 KeSystemCalls 增加1
.text:00406979                 mov     esi, edx                     ; edx保存的是3环参数的指针
.text:0040697B                 mov     ebx, [edi+0Ch]              ; edi指向要使用的系统服务表; +0Ch是ParamTableBase(参数表指针)
.text:0040697E                 xor     ecx, ecx                     ;ecx清零
.text:00406980                 mov     cl, [eax+ebx]               ; eax保存的是3环传入的系统调用号; ebx保存的是是参数表指针; 这条指令的目的是得到内核函数的参数总长度,存入cl
.text:00406983                 mov     edi, [edi]                   ; 取出系统调用表的第一个成员(函数地址指针)
.text:00406985                 mov     ebx, [edi+eax*4]                ; 函数地址指针 + 系统调用号*4(乘4是因为每个成员占4字节); 目的是找到函数地址,存入ebx
.text:00406988                 sub     esp, ecx                     ; 提升堆栈,大小为参数总长度
.text:0040698A                 shr     ecx, 2                       ; 参数总长度/4 = 参数个数
.text:0040698D                 mov     edi, esp                     ; 设置要COPY的目的地
.text:0040698F                 test    byte ptr [ebp+72h], 2
.text:00406993                 jnz     short loc_40699B
.text:00406995                 test    byte ptr [ebp+6Ch], 1
.text:00406999                 jz      loc_4069A7
.text:0040699B
.text:0040699B loc_40699B:                             ; CODE XREF: KiSystemServiceAccessTeb()+33↑j
.text:0040699B                 cmp     esi, ds:_MmUserProbeAddress  ; 全局变量存储用户能访问的最大范围; 这条指令的作用是判断3环参数是否越界
.text:004069A1                 jnb     loc_406B4F                   ; 越界则跳转至异常处理
.text:004069A7
.text:004069A7 loc_4069A7:
.text:004069A7                 rep movsd                            ; 将3环参数复制到0环堆栈
.text:004069A9                 call    ebx                          ; 调用函数

SSDT

描述

  1. 全称:System Services Descriptor Table(系统服务描述符表)
  2. SSDT的每个成员叫做系统服务表
  3. SSDT的第一个成员是导出的,声明一下即可使用
  4. SSDT的第二个成员是未导出的,需要通过其它方式查找
  5. 在Windows中,SSDT的第三个成员和第四个成员未被使用

在WinDbg中查看已导出成员:

kd>dd KeServiceDescriptorTable


在WinDbg中查看未导出成员:

实验:在SSDT中查找内核函数信息

实验说明

  1. 在之前的实验中,我们通过分析三环的ReadProcessMemory函数,一步步了解三环函数是如何进入内核的
  2. ReadProcessMemory即将进入内核时,传递了一个系统服务号,为0BAh
  3. 本次实验查找编号 0BAh 在 SSDT 表中的相关信息

第一步:查看函数地址

函数地址表

[函数地址表 + 系统服务号*4] = 内核函数地址

第二步:查看参数个数

参数表

[参数表 + 系统服务号] = 内核函数参数个数(单位:字节)

第三步:查看内核函数反汇编

练习

要求:在SSDT表中追加一个函数地址(NtReadVirtualMemory),自己编写API的3环部分调用这个新增的函数(注意:使用2-9-9-12分页)。

答案:略(待补充)

Windows系统调用学习笔记(四)—— 系统服务表SSDT相关推荐

  1. Windows系统调用学习笔记(三)—— 保存现场

    Windows系统调用学习笔记(三)-- 保存现场 要点回顾 基本概念 Trap Frame 结构 线程相关的结构体 ETHREAD KTHREAD CPU相关的结构体 KPCR _NT_TIB KP ...

  2. Windows系统调用学习笔记(二)—— 3环进0环

    Windows系统调用学习笔记(二)-- 3环进0环 要点回顾 基本概念 _KUSER_SHARED_DATA 0x7FFE0300 实验:判断CPU是否支持快速调用 第一步:修改EAX=1 第二步: ...

  3. Windows系统调用学习笔记(一)—— API函数调用过程

    Windows系统调用学习笔记(一)-- API函数调用过程 Windows API 实验1:分析ReadProcessMemory 第一步:定位函数 第二步:开始分析 总结 实验2:分析NtRead ...

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

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

  5. Windows异常学习笔记(四)—— 编译器扩展SEH

    Windows异常学习笔记(四)-- 编译器扩展SEH 要点回顾 编译器支持的SEH 过滤表达式 实验一:理解_try_except 实验二:_try_except 嵌套 拓展SEH结构体 scope ...

  6. Windows异常学习笔记(一)—— CPU异常记录模拟异常记录

    Windows异常学习笔记(一)-- CPU异常记录 基础知识 异常的分类 CPU异常 分析中断处理函数 _KiTrap00 分析 CommonDispatchException 总结 软件模拟异常 ...

  7. Windows APC学习笔记(二)—— 挂入过程执行过程

    Windows APC学习笔记(二)-- 挂入过程&执行过程 基础知识 挂入过程 KeInitializeApc ApcStateIndex KiInsertQueueApc Alertabl ...

  8. Windows异常学习笔记(五)—— 未处理异常

    Windows异常学习笔记(五)-- 未处理异常 要点回顾 最后一道防线 实验一:理解最后一道防线 实验二:新线程的最后一道防线 总结 UnhandledExceptionFilter 实验三:理解U ...

  9. Windows异常学习笔记(二)—— 内核异常处理流程用户异常的分发

    Windows异常学习笔记(二)-- 内核异常处理流程&用户异常分发 用户层与内核层异常 内核异常 分析 KiDispatchException 分析 RtlDispatchException ...

最新文章

  1. Wiener Filter
  2. gtk移植到嵌入式_入行嵌入式研发10多年,一位工程师悟出了这些道理
  3. python专科就业难吗-听说Python就业难,是真的吗?
  4. hutool 读取扩展名文件_用卓语言实现中文编程显示隐藏文件扩展名
  5. bash删除文件中含指定内容的行
  6. SpringCloud Feign实战(二)
  7. 【小白冲冲冲!!!】37. ORBSLAM初始化时为什么要同时初始化H矩阵和F矩阵?
  8. paip.提升开发效率---事件化V0829
  9. 域名和IP地址的区别
  10. html横菜单中菜单均匀分布,html – 如何在flexbox中的行间均匀分布元素?
  11. c语言中整形的最大最小值,c语言整数和浮点数的最大最小值
  12. PHP在线网课问答题库搜索,推荐一个大学mooc网课答案题库在线查询公众号
  13. VS2010:X64和X86冲突问题
  14. 【原生】JS 获取今天日期
  15. 简单明了理解交叉验证
  16. 软件实施工作个人看法
  17. excel中联系人转换为csv导入手机出现乱码的解决方法
  18. 以下哪些不是Linux操作系统特点,Linux系统都有哪些特点?很多人不知道!
  19. 【BZOJ2002】【HNOI2010】弹飞绵羊(LCT)
  20. 上古卷轴3晨风职业_上古卷轴3晨风

热门文章

  1. AI:2020年6月21日北京智源大会演讲分享之14:50-15:15穗志方教授《从语言到知识——构建语言智能的基石》
  2. DL之AE:自编码器AutoEncoder的简介、应用、经典案例之详细攻略
  3. 成功解决Python3版UnicodeDecodeError: ‘ascii‘ codec can‘t decode byte 0x90 in position 614: ordinal not in
  4. Android的Intent你知道多少?
  5. 201312-1- 出现次数最多的数
  6. 第九章 字符串,字符和字节
  7. 小程序 常用快捷键
  8. Android IllegalStateException: The specified child already has a parent问题解决办法
  9. 【自译】八步成为数据科学家
  10. 常用代码整理(重要)