源代码下载地址:挂钩SSDT源代码

据微软所言,服务描述符表是一个由四个结构组成的数组,其中的每一个结构都是由四个双字项组成。因此,我们可以将服务描述符表表示为:

typedef struct ServiceDescriptorTable

{

SDE ServiceDescriptor[4];

}SDT;

其中,其中的每一个服务描述符呈现出四个双字的形式,它的结构为:

#pragma pack(1)

typedef struct ServiceDescriptorEntry

{

unsigned int *ServiceTableBase;

unsigned int *ServiceCounterTableBase; //Used only in checked build

unsigned int NumberOfServices;

unsigned char *ParamTableBase;

} SDE,*PSDE;

#pragma pack()

我们寻找的数据结构SSDT是由第一个域所引用的调用表(可以在调试器中用命令dps nt!KiServiceTable查看)

一、      禁用WP

如果我们能够简单的将值换人并换出SSDT该有多好,然而障碍在于SSDT存在于只读内存中。因此,为了挂钩由SSDT引用的例程,我们一般的策略看起来应该类似于以下形式(以伪代码表示):

DisableReadProtection();

ModifySSDT();

EnableReadProtection();

英特尔的文档写明:“如果CR0.WP=1,访问类型由页目录和页表项的R/W标志位决定。如果CR0.WP=0,超级用户权限允许读写访问。”因此,为破坏SSDT上的写保护,我们需要临时清除写保护(Write Protect,WP)标志。

通过分配自己的MDL来描述SSDT,我们可以禁用读保护。MDL与存储SSDT内容的物理内存页相关联,MDL元素结构在WDK附带的wdm.h头文件中的定义为:

typedef struct _MDL

{

struct _MDL *Next;

CSHORT Size;

CSHORT MdlFlags;

struct _EPROCESS *Process;

PVOID MappedSystemVa;

PVOID StartVa;

ULONG ByteCount;

ULONG ByteOffset;

}MDL,*PMDL;

对于物理内存的这一区域,一旦我们添加了自己的私有描述,就可以使用按位OR以及MDL_MAPPED_TO_SYSTEM_VA宏调整MDL的权限。再一次,我们可以成功实现,因为我们拥有自己的MDL对象。最后,我们使用SSDT在物理内存中的位置与MDL之间的映射正式化。

然后,锁定我们在线性空间创建的MDL缓冲区。作为回报,我们得到一个新的线性地址,它也指向SSDT,而且能够对其进行修改。

简而言之,使用MDL,我们在系统的线性地址空间中创建了新的可写缓冲区,它恰好解析成存储SSDT的物理内存。只要两个区域解析成同样的物理内存区域,它就没什么不同。它只是一个数字游戏,纯粹而简单。如果你不能写入线性内存的给定区域,那么就创建自己的区域并且向其写入。

WP_GLOBALS disableWP_MDL(unsigned int *ssdt,unsigned int nServices)

{

WP_GLOBALS wpGbs;

DbgPrint("[disableWP_MDL] SSDT=%\n",ssdt);

DbgPrint("[disableWP_MDL] nServices=%\n",nServices);

wpGbs.pMDL = MmCreateMdl(NULL, (PVOID)ssdt, (SIZE_T)nServices*4);

if(wpGbs.pMDL==NULL)

{

DbgPrint("[disableWP_MDL] %\n","call to MmCreateMdl failed");

return (wpGbs);

}

MmBuildMdlForNonPagedPool(wpGbs.pMDL);

wpGbs.pMDL->MdlFlags = wpGbs.pMDL->MdlFlags | MDL_MAPPED_TO_SYSTEM_VA;

wpGbs.callTable = (unsigned char*)MmMapLockedPages(wpGbs.pMDL, KernelMode);

if(wpGbs.callTable==NULL)

{

DbgPrint("[disableWP_MDL] %\n","call to MmMapLockedPages failed");

return (wpGbs);

}

return (wpGbs);

}

这个例程返回一个结构,它只是一个指向我们MDL和SSDT指针的包装器。

typedef struct _WP_GLOBALS

{

unsigned char *callTable;

PMDL pMDL;

}WP_GLOBALS;

我们通过前面的函数返回该结构,以便访问可写版本的SSDT,而且当不再需要MDL缓冲区时,我们可以复原事务的原始状态。为复原系统状态,我们使用以下函数:

void enableWP_MDL(PMDL mdlPtr,unsigned char *callTable)

{

if(mdlPtr != NULL)

{

MmUnmapLockedPages((PVOID)callTable, mdlPtr);

IoFreeMdl(mdlPtr);

}

}

二、      挂钩SSDT

一旦禁用了写保护,我们可以使用以下例程将新的函数地址换人SSDT

unsigned char* hookSSDT(unsigned char *apiCall,unsigned char *newAddr,unsigned int *callTable)

{

PLONG target;

unsigned int indexValue;

indexValue = getSSDTIndex(apiCall);

target = (PLONG)&(callTable[indexValue]);

return ((unsigned char*)InterlockedExchange(target,(LONG)newAddr));

}

该例程接收挂钩例程的地址、现有例程的地址以及指向SSDT的指针,返回现有例程的地址(以便完成任务后恢复SSDT)。

给定某个Nt*()函数,他的地址在SSDT中的什么位置?我们在调试器中使用u nt!Zw*命令查看汇编代码,发现这种函数开始处的汇编代码都是这种格式:mov eax,xxxH,这个xxx就是系统调用的索引号。

unsigned int getSSDTIndex(unsigned char *address)

{

unsigned char *addressOfIndex;

unsigned int  indexValue;

addressOfIndex = address + 1;

indexValue = *((PULONG)addressOfIndex);

return (indexValue);

}

一旦有了索引值,定位表项的地址并且将其换出就是一件简单的事情。但是请注意,我们必须使用InterlockedExchange()锁定对此项的访问,以便我们暂时拥有独占的访问权。与IDT或GDT这样基于处理器的结构不同,不论多少个处理器正在运行,只有一个单独的SSDT。

对SSDT中的系统调用脱钩使用同样的基本机制。唯一的不同之处在于我们不向调用例程返回值。

void unHookSSDT(unsigned char *apiCall,unsigned char *oldAddr,unsigned int *callTable)

{

PLONG target;

unsigned int indexValue;

indexValue = getSSDTIndex(apiCall);

target = (PLONG)&(callTable[indexValue]);

InterlockedExchange(target,(LONG)oldAddr);

}

三、      SSDT示例

既然已经分析了组成这首乐曲的各种和弦,让我们将其演奏起来听听看。挂钩ZwTerminateProcess系统调用。

NTSTATUS  DriverEntry (

IN PDRIVER_OBJECT    DriverObject,

IN PUNICODE_STRING  RegistryPath

)

{

DbgPrint("Load SSDT Driver \n");

DriverObject ->DriverUnload = UnLoad;

wpGlobals = disableWP_MDL(KeServiceDescriptorTable.ServiceTableBase, KeServiceDescriptorTable.NumberOfServices);

if(wpGlobals.pMDL==NULL || wpGlobals.callTable==NULL)

{

return (STATUS_UNSUCCESSFUL);

}

pMDL = wpGlobals.pMDL;

systemCallTable = (PVOID *)wpGlobals.callTable;

Old_ZwTerminateProcess =(ZwTerminateProcessPtr)hookSSDT((unsigned char*)ZwTerminateProcess, (unsigned char*)NewZwTerminateProcess, (unsigned int*)systemCallTable);

return STATUS_SUCCESS;

}

KeServiceDescriptorTable是由ntoskrnl.exe导出的符号。要想访问它,我们必须为声明加上__declspec(dllimport)前缀,以便编译器知道我们在做什么。导出的内核符号为我们提供了内存中一个位置的地址。我们提供的数据类型定义(即typedef struct ServiceDescriptorEntry)把某个复合结构强加于这个地址的内存。通过这种通用方法,你能够修改由操作系统导出的任意变量。

我们将返回值保存到三个全局变量中(pMDL,systemCallTable,Old_ZwTerminateProcess),以便能够脱钩系统调用,并且重新启用写保护。

VOID

UnLoad (

IN PDRIVER_OBJECT      DriverObject

)

{

DbgPrint("Unload SSDT Driver \n");

unHookSSDT((unsigned char*)ZwTerminateProcess, (unsigned char*)Old_ZwTerminateProcess, (unsigned int*)systemCallTable);

enableWP_MDL(pMDL,(unsigned char*)systemCallTable);

}

每当ZwTerminateProcess被调用,我们挂钩的函数都会被调用。为了存储实现此接口的现有系统调用的地址,定义以下的函数指针数据类型。

typedef NTSTATUS(*ZwTerminateProcessPtr)(

IN HAndLE ProcessHAndle OPTIONAL,

IN NTSTATUS ExitStatus );

要做的事情只剩下实现挂钩例程。我们通过打印输出字符串跟踪调用,然后调用原始的系统调用。

NTSTATUS NewZwTerminateProcess(

IN HAndLE ProcessHAndle OPTIONAL,

IN NTSTATUS ExitStatus  )

{

NTSTATUS ntStatus;

DbgPrint("NewZwTerminateProcess Called \n");

ntStatus = ((ZwTerminateProcessPtr)(Old_ZwTerminateProcess))(ProcessHAndle,ExitStatus);

if(!NT_SUCCESS(ntStatus))

{

DbgPrint("[NewZwTerminateProcess] %\n","Call to Old_ZwTerminateProcess failed");

}

return STATUS_SUCCESS;

}

我们在这个示例中建立的是挂钩SSDT的标准操作程序。不论我们正在拦截哪个例程,挂钩与脱钩的技术细节保持相同。从现在起,无论我们何时想要跟踪或过滤系统调用,必须做的所有工作只有以下几点

  • 声明原始系统调用原型(例如ZwTerminateProcess()
  • 声明相应的函数指针数据类型(例如ZwTerminateProcessPtr
  • 定义函数指针(例如Old_ZwTerminateProcess
  • 实现挂钩例程(例如NewZwTerminateProcess()

四、加载驱动

    我们用INSTDRV加载编译好的驱动程序DCSSSDT.sys

我们用PCHunter32查看挂钩是否成功

 

我们用Dbgview查看调试信息

没有问题都成功了。

转自  http://www.dcscms.com/article/content.php?seq=13

转载于:https://www.cnblogs.com/mule/p/3812665.html

挂钩SSDT详解附源代码相关推荐

  1. 蓝牙:CRC原理详解(附crc16校验代码)

    CRC原理详解(附crc16校验代码) 参考链接: https://www.cnblogs.com/esestt/archive/2007/08/09/848856.html Cyclic Redun ...

  2. 《前端》权限链接--vue前端权限控制方案详解附demo_feiyu_may的博客-CSDN博客_vue 前端权限

    前端权限控制 - 潘正 - 博客园  https://www.cnblogs.com/guchengnan/p/11800947.html vue前端权限控制方案详解附demo_feiyu_may的博 ...

  3. Win+TexLive2020+TexStudio安装过程详解附ElsevierLatex模板下载并使用

    Win+TexLive2020+TexStudio安装过程详解附ElsevierLatex模板下载并使用 一.下载并安装Texlive2020 1.下载TexLive2020 2.安装过程 解压之后运 ...

  4. 计算机排名的985大学排名,2019年985大学名单排名,985大学详解(附全榜单)

    中国最有名的就是211大学和985大学了.2019年211大学名单排名已经为大家公布了,相比之下985大学更加少,全国只有39所985大学,可见985是比211更加有含金量的学校了.下面排行榜123网 ...

  5. .user.ini上传详解附CTF例题

    .user.ini上传详解附CTF例题 题目 解法 https://buuoj.cn/challenges#[SUCTF%202019]CheckIn [SUCTF 2019]CheckIn 题目 解 ...

  6. 数学规划详解(附例题及部分Python实现)

    数学规划详解(附例题及Python实现) 例题来自于清风老师的数学建模课,个人认为讲的非常好,欢迎大家购买 一.概述 1.1 定义 数学规划是运筹学的一个分支,在约束条件下,按照目标函数来寻求计划管理 ...

  7. python直线拟合_RANSAC算法详解(附Python拟合直线模型代码)

    之前只是简单了解RANSAC模型,知道它是干什么的.然后今天有个课程设计的报告,上去讲了一下RANSAC,感觉这个东西也没那么复杂,所以今天就总结一些RASAC并用Python实现一下直线拟合. RA ...

  8. java远程_java实现电脑远程控制详解,附完整源代码

    Java JDK1.4 的Robot对象,该对象可以完成屏幕图像截取操作,控制鼠标,键盘,如此便可以轻而易举地实现远程服务器的控制.本文向大家介绍如何用Java Robot对象实现远程服务器的控制,并 ...

  9. Windows7旗舰版磁盘分区详解—附分区步骤截图

    最近工作中配置使用联想的Thinkpad TL系列本本.当然原装的系统时刚发布的Windows RTM旗舰版.在考虑买之前也参考了戴尔 苹果的等等, 但个人私下也是一直在用Tinkpad系列, 相比其 ...

  10. Windows WMIC命令使用详解(附实例)

    第一次执行WMIC命令时,Windows首先要安装WMIC,然后显示出WMIC的命令行提示符.在WMIC命令行提示符上,命令以交互的方式执行执行"wmic"命令启动WMIC命令行环 ...

最新文章

  1. redis集群搭建及设置账户(转)
  2. Applese 涂颜色(欧拉定理降幂+快速幂)
  3. 2013\National _C_C++_A\4.约数倍数选卡片
  4. Android笔记(六十七) 自定义控件
  5. zblog php 标题优化,Zblog分类页标题重复的优化 - 张力博客
  6. ubuntu php.ini 配置,ubuntu下配置PHP+JSON模块(apache) | 学步园
  7. 带领国产数据库走向世界,POLARDB底层逻辑是什么?
  8. 关于c/s vs web 程序的并发问题
  9. 对我帮助很大的ESXCLI命令
  10. Struts2 之 对xwork的理解
  11. 垂直居中之父元素高度确定的文本
  12. 日常开发中的几个常用跨域处理方式
  13. .NET 2.0泛型集合类与.NET 1.1集合类的区别(一)
  14. 自动跳动滑动门html,jQuery 滑动门自动滑动实现代码
  15. 16元日薪,从阿里云雇佣一个专家阿里云中小企业AI产品码栈解析
  16. Socket长连接和短连接的区别
  17. java类定义初成员变量赋值_Java中成员变量初始化
  18. 机器视觉怎么和plc通讯
  19. 海南信用社计算机试题,2021年海南农村信用社计算机笔试内容17
  20. 微信小程序:页面有内容却不显示原因

热门文章

  1. linux一个数据页多少,复习——Linux
  2. bcb quickrep保存为 图片_干货|SCI论文中图片与组图技巧
  3. python读取properties文件_读取properties文件
  4. html 给一个无限宽,html – CSS div与其内容一样宽
  5. 十大排序算法——快速排序法【挖坑法、左右指针法、前后指针法和优化方式三路快排】(C语言)
  6. python 列表的行 列长度_Python连载|Pandas手册(上)
  7. OpenCV : 投影变换
  8. C/C++[1928, ]日期处理
  9. 阿里云云计算 32 PolarDB的概念
  10. 易筋SpringBoot 2.1 | 第六篇:JdbcTemplate访问MySQL