在前面的章节《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. websocket群聊实战

    演示及源码地址: 演示地址:https://www.wchime.xyz/#/ 后端代码:https://gitee.com/mom925/wchime-web-api 前端代码:https://gi ...

  2. Printer Queue,UVa 12100 (自定义标记法 + 优先队列)

    题目描述: 我们需要用打印机打印任务.每个任务都有1~9间的优先级,优先级越高,任务越急. 打印机的运作方式:从打印队列里取出一个任务j,如果队列里有比j更急的任务,则直接把j放到打印队列尾部,否则打 ...

  3. AtCoder ABC 165 D - Floor Function (Good, 手动模拟推出公式)

    题目链接:Here 题意: 给出正整数 \(A,B,N (1\le A\le 1e6,1\le B,N\le1e12)\) ,对于 \(x\in [0,N]\) 求出 \(\left\lfloor\f ...

  4. Codeforce :466C. Number of Ways (数学)

    https://codeforces.com/problemset/problem/466/C 解题说明:此题是一道数学题,若平分分成若干种情况,应当整体(sum)考虑,对sum/3进行分析.它是区分 ...

  5. S3C2440移植uboot之支持DM9000

      上一节S3C2440移植uboot之支持NANDFLASH操作移植了uboot 支持了NANDFLASH的操作,这一节修改uboot支持DM9000. 目录 通过Makefile把dm9000x编 ...

  6. 深入理解java线程池 一

    本文为博主原创,未经允许不得转载: 在多线程和高并发场景中,需要创建大量的线程来进行业务处理,我们通常创建线程有两种方法,一种是通过继承Thread类,另一种是实现Runnable的接口,但是我们创建 ...

  7. Pgsql之查询一段时间内的所有年月yyyy-mm

    前几天干活儿的时候,报表中有这么个需求,需要用pgsql查询两个日期间的所有年月,下面贴代码: 1 with recursive t(n) as ( 2 select date('2020-01-01 ...

  8. Kubeadm 安装支持IPV6 K8S1.28.x的简单过程

    Kubeadm 安装支持IPV6 K8S的简单过程 背景 手贱 找了一个晚上想尝试安装一个K8S集群 并且可以支持IPV6 协议栈的 然后就开始各种百度. 各种处理 找到了一堆歪门邪道. 但是还不知道 ...

  9. [转帖]Linux cache参数调优

    https://zhuanlan.zhihu.com/p/136237953 缓存机制(cache)是保证Linux环境下对硬盘/flash操作效率的有效方式.cache建立在内存中,它缓存了硬盘/f ...

  10. [转帖]Linux内核映像vmlinux、Image、zImage、uImage区别

    https://zhuanlan.zhihu.com/p/466226177 本文介绍几种常用的Linux内核映像的区别. 一.vmlinux vmlinux:Linux内核编译出来的原始的内核文件, ...