在前面的章节《X86驱动:挂接SSDT内核钩子》我们通过代码的方式直接读取 KeServiceDescriptorTable 这个被导出的表结构从而可以直接读取到SSDT表的基址,而在Win64系统中 KeServiceDescriptorTable 这个表并没有被导出,所以我们必须手动搜索到它的地址。

为了确保系统的安全性与稳定性,微软从 Windows Vista X64 开始对系统内核增加了一定的限制,其主要增加了两种保护措施,一是KPP (内核补丁保护),KPP是机制其利用了PG(PatchGuard)技术,PG技术在x64系统下加入了内核哨兵,用于检测系统内核是否被恶意篡改(打补丁),如果发现被打了补丁,则会导致关键结构损毁直接蓝屏,二是DSE (驱动强制签名),DSE技术则是拒绝加载不包含正确签名的驱动。

1.这里我们可以通过MSR(特别模块寄存器),读取C0000082寄存器,从而得到KiSystemCall64的地址,在内核调试模式下直接输入 rdmsr c0000082 即可读取到该地址,反汇编可看到 nt!KiSystemCall64 函数。

kd> rdmsr c0000082
msr[c0000082] = fffff800`03c72ec0 kd> u fffff800`03c72ec0
nt!KiSystemCall64:
fffff800`03c72ec0 0f01f8 swapgs
fffff800`03c72ec3 654889242510000000 mov qword ptr gs:[10h],rsp
fffff800`03c72ecc 65488b2425a8010000 mov rsp,qword ptr gs:[1A8h]
fffff800`03c72ed5 6a2b push 2Bh
fffff800`03c72ed7 65ff342510000000 push qword ptr gs:[10h]
fffff800`03c72edf 4153 push r11
fffff800`03c72ee1 6a33 push 33h
fffff800`03c72ee3 51 push rcx

2.接着我们从 KiSystemCall64 函数地址开始向下反汇编,可以看到最后 nt!KiSystemServiceRepeat 这个函数里面包含了 nt!KeServiceDescriptorTable (fffff80003eaa840) ,通过 03c72ff2 减去03c72ec0 即可得到SDT表结构与KiSystemCall64函数之间的偏移值 132 (306)

kd> uf KiSystemCall64
Flow analysis was incomplete, some code may be missing
nt!KiSystemCall64:
fffff800`03c72ec0 0f01f8 swapgs
fffff800`03c72ec3 654889242510000000 mov qword ptr gs:[10h],rsp
fffff800`03c72ecc 65488b2425a8010000 mov rsp,qword ptr gs:[1A8h]
fffff800`03c72ed5 6a2b push 2Bh
fffff800`03c72ed7 65ff342510000000 push qword ptr gs:[10h]
fffff800`03c72edf 4153 push r11
fffff800`03c72ee1 6a33 push 33h
fffff800`03c72ee3 51 push rcx
fffff800`03c72ee4 498bca mov rcx,r10 nt!KiSystemServiceRepeat:
fffff800`03c72ff2 4c8d1547782300 lea r10,[nt!KeServiceDescriptorTable (fffff800`03eaa840)]
fffff800`03c72ff9 4c8d1d80782300 lea r11,[nt!KeServiceDescriptorTableShadow (fffff800`03eaa880)]
fffff800`03c73000 f7830001000080000000 test dword ptr [rbx+100h],80h
fffff800`03c7300a 4d0f45d3 cmovne r10,r11
fffff800`03c7300e 423b441710 cmp eax,dword ptr [rdi+r10+10h]
fffff800`03c73013 0f83e9020000 jae nt!KiSystemServiceExit+0x1a7 (fffff800`03c73302) Branch

总结一下:我们通过读取C0000082寄存器,能够得到KiSystemCall64的地址,然后从KiSystemCall64的地址开始,往下搜索0x150字节左右(特征码4c8d15),就能得到KeServiceDescriptorTable的地址。

#include <ntddk.h>
#include <windef.h>
#include <intrin.h> #pragma intrinsic(__readmsr) VOID UnDriver(PDRIVER_OBJECT driver)
{
DbgPrint(("驱动程序卸载成功! \n"));
} ULONGLONG Get_SSTD_Base()
{
PUCHAR Base = (PUCHAR)__readmsr(0xC0000082); // 读取C0000082寄存器
PUCHAR Address = Base + 0x150; // 相加偏移
PUCHAR i = NULL;
UCHAR b1 = 0, b2 = 0, b3 = 0; // 保存特征码
ULONG templong = 0;
ULONGLONG addr = 0; // 最后获取到的地址
for (i = Base; i<Address; i++)
{
if (MmIsAddressValid(i) && MmIsAddressValid(i + 1) && MmIsAddressValid(i + 2))
{
b1 = *i; b2 = *(i + 1); b3 = *(i + 2);
if (b1 == 0x4c && b2 == 0x8d && b3 == 0x15) // 判断是否=4c8d15
{
memcpy(&templong, i + 3, 4); // 在i+3位置拷贝,拷贝4字节
addr = (ULONGLONG)templong + (ULONGLONG)i + 7;
return addr;
}
}
}
return 0;
} NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{
DbgPrint("SSTD Base= %11x", Get_SSTD_Base());
DriverObject->DriverUnload = UnDriver;
return STATUS_SUCCESS;
}

3.接着我们则需要获取到SSDT中某个函数的序号,这里以OpenProcess为例:

0:000> u ntdll!NtOpenProcess
ntdll!NtOpenProcess:
77820700 b826000000 mov eax,23h
77820705 bac04f8377 mov edx,offset ntdll!Wow64SystemServiceCall (77834fc0)
7782070a ffd2 call edx
7782070c c21000 ret 10h
7782070f 90 nop

4.读取代码如下.

#include <ntddk.h>
#include <windef.h>
#include <intrin.h> #pragma intrinsic(__readmsr) VOID UnDriver(PDRIVER_OBJECT driver)
{
DbgPrint(("驱动程序卸载成功! \n"));
} ULONGLONG Get_SSDT_Base()
{
PUCHAR Base = (PUCHAR)__readmsr(0xC0000082); // 读取C0000082寄存器
PUCHAR Address = Base + 0x150; // 相加偏移
PUCHAR i = NULL;
UCHAR b1 = 0, b2 = 0, b3 = 0; // 保存特征码
ULONG templong = 0;
ULONGLONG addr = 0; // 最后获取到的地址
for (i = Base; i<Address; i++)
{
if (MmIsAddressValid(i) && MmIsAddressValid(i + 1) && MmIsAddressValid(i + 2))
{
b1 = *i; b2 = *(i + 1); b3 = *(i + 2);
if (b1 == 0x4c && b2 == 0x8d && b3 == 0x15) // 判断是否=4c8d15
{
memcpy(&templong, i + 3, 4); // 在i+3位置拷贝,拷贝4字节
addr = (ULONGLONG)templong + (ULONGLONG)i + 7;
return addr;
}
}
}
return 0;
} typedef struct _SYSTEM_SERVICE_TABLE{
PVOID ServiceTableBase;
PVOID ServiceCounterTableBase;
ULONGLONG NumberOfServices;
PVOID ParamTableBase;
} SYSTEM_SERVICE_TABLE, *PSYSTEM_SERVICE_TABLE; ULONGLONG GetSSDTFunction(ULONGLONG Index)
{
LONG dwTemp = 0;
ULONGLONG qwTemp = 0, stb = 0, ret = 0;
PSYSTEM_SERVICE_TABLE ssdt = (PSYSTEM_SERVICE_TABLE)Get_SSDT_Base();
stb = (ULONGLONG)(ssdt->ServiceTableBase);
qwTemp = stb + 4 * Index;
dwTemp = *(PLONG)qwTemp;
dwTemp = dwTemp >> 4;
ret = stb + (LONG64)dwTemp;
return ret;
} NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{
DbgPrint("OpenProcess=%llx", GetSSDTFunction(0x23));
DriverObject->DriverUnload = UnDriver;
return STATUS_SUCCESS;
}

在64位环境下想要任意Hook系统函数是不可能的,因为64位中每个驱动程序都不在同一个4GB空间里,而4字节的整数只能表示4GB的范围,所以无论你怎么改,都不可能跨越这个内存空间,而微软也不希望你挂钩内核的一些函数,如果非要使用的话,微软提供了一些回调函数可以实现相应的挂钩效果。

驱动开发:内核读取SSDT表基址的更多相关文章

  1. 驱动开发:Win10内核枚举SSDT表基址

    三年前面朝黄土背朝天的我,写了一篇如何在Windows 7系统下枚举内核SSDT表的文章<驱动开发:内核读取SSDT表基址>三年过去了我还是个单身狗,开个玩笑,微软的Windows 10系 ...

  2. Windows驱动开发-内核常用内存函数

    搞内存常用函数 C语言 内核 malloc ExAllocatePool memset RtlFillMemory memcpy RtlMoveMemory free ExFreePool

  3. 驱动开发:内核枚举ShadowSSDT基址

    在笔者上一篇文章<驱动开发:Win10枚举完整SSDT地址表>实现了针对SSDT表的枚举功能,本章继续实现对SSSDT表的枚举,ShadowSSDT中文名影子系统服务描述表,SSSDT其主 ...

  4. 驱动开发:内核枚举PspCidTable句柄表

    在上一篇文章<驱动开发:内核枚举DpcTimer定时器>中我们通过枚举特征码的方式找到了DPC定时器基址并输出了内核中存在的定时器列表,本章将学习如何通过特征码定位的方式寻找Windows ...

  5. 驱动开发:内核枚举Registry注册表回调

    在笔者上一篇文章<驱动开发:内核枚举LoadImage映像回调>中LyShark教大家实现了枚举系统回调中的LoadImage通知消息,本章将实现对Registry注册表通知消息的枚举,与 ...

  6. 驱动开发:内核监控Register注册表回调

    在笔者前一篇文章<驱动开发:内核枚举Registry注册表回调>中实现了对注册表的枚举,本章将实现对注册表的监控,不同于32位系统在64位系统中,微软为我们提供了两个针对注册表的专用内核监 ...

  7. 64位内核开发第四讲,查看SSDT表与showSSDT表

    目录 SSDt表与ShadowSSDT表的查看. 一丶SSDT表 1.什么是SSDT表 2.查看步骤 二丶ShadowSSDT表 1.什么是ShadowSSDT表 2.如何查看. 三丶工具介绍 SSD ...

  8. 《天书夜读:从汇编语言到windows内核编程》五 WDM驱动开发环境搭建

    (原书)所有内核空间共享,DriverEntery是内核程序入口,在内核程序被加载时,这个函数被调用,加载入的进程为system进程,xp下它的pid是4.内核程序的编写有一定的规则: 不能调用win ...

  9. X86驱动:挂接SSDT内核钩子

    SSDT 中文名称为系统服务描述符表,该表的作用是将Ring3应用层与Ring0内核层,两者的API函数连接起来,起到承上启下的作用,SSDT并不仅仅只包含一个庞大的地址索引表,它还包含着一些其它有用 ...

  10. Linux驱动开发必看详解神秘内核(完全转载)

    Linux驱动开发必看详解神秘内核 完全转载-链接:http://blog.chinaunix.net/uid-21356596-id-1827434.html   IT168 技术文档]在开始步入L ...

随机推荐

  1. vue mutations与actions的区别

    关于 mutations与actions的区别,网上有很多文章,大多是照着vue.js的教程再来一波!!因为最近接手vue项目,自己之前vue的知识点掌握也不深,就此机会把这个知识点再深挖一下. 使用 ...

  2. IDEA整合SSM框架:简易图书操作模块

    这个SSM整合文章,以图书的增删改查为例 本篇文章源码已上传: Github:https://github.com/RivTian/University-coursework/tree/master/ ...

  3. UVA - 1594 :Ducci Sequence (set应用)

    给定n元组(a1,a2,...,an),ai均为整数,得到下一个序列为(|a1-a2|,|a2-a3|,...,|an-a1|),如此循环下去,必定会出现全零序列或重复序列. 现要求判断给定序列是全零 ...

  4. 领域驱动设计(DDD)实践之路(三):如何设计聚合

    本文首发于 vivo互联网技术 微信公众号 链接:https://mp.weixin.qq.com/s/oAD25H0UKH4zujxFDRXu9Q作者:wenbo zhang [领域驱动设计实践之路 ...

  5. 2.5D 组态案例合集 | 智慧园区、数据中心、SMT 生产线、汽车制造

    在阅读文章之前,大家可以思考下 2.5D 设计属于哪种界定? 2.5D 是通过二维的元素来呈现出三维的效果.其实在国外并没有 2.5D 这样的称呼,标准说法是 Isometric 风格,翻译过来就是等 ...

  6. vue 状态管理 四、Action用法

    系列导航 vue 状态管理 一.状态管理概念和基本结构 vue 状态管理 二.状态管理的基本使用 vue 状态管理 三.Mutations和Getters用法 vue 状态管理 四.Action用法 ...

  7. <vue 路由 4、嵌套路由>

    一.效果 点击about后,新闻和体育属于about的子路由调用的页面 知识点说明 路由里使用children属性可以实现路由的嵌套 三.代码结构 注:主要是标红的几个文件 四.代码 重新编写这几个文 ...

  8. Servlet系列:(HttpServletRequest、HttpServletResponse、ServletContext、ServletConfig)详解

    HttpServletRequest HttpServletRequest 对象:主要作用是用来接收客户端发送过来的请求信息,例如:请求的参数,发送的头信息等都属于客户端发来的信息,service() ...

  9. 线段树 hdu 4027

    ***又是超时的问题,当一个区间全是1时,再去开方和不开方是一样的,所以在这一步不需要再往底层递归了*** #include <iostream> #include <cstdio& ...

  10. Vue第五篇 Vue的生命周期

    Vue生命周期简介 生命周期的钩子函数 <!DOCTYPE html> <html lang="en"> <head> <meta cha ...