背景

SSDT 全称为 System Services Descriptor Table,即系统服务描述符表。SSDT 表的作用就是把 ring3 的 WIN32 API 函数和 ring0 的内核 API 函数联系起来。对于ring3下的一些API,最终会对应于 ntdll.dll 里一个 Ntxxx 函数,例如 CreateFile,最终调用到 ntdll.dll 里的 NtCreateFile 这个函数。NtCreateFile最终将系统服务号放入EAX,然后 CALL 系统的服务分发函数 KiSystemService,进入到内核当中。从 ring3 到 ring0,最终在 ring0 当中通过传入的 EAX 得到对应的同名系统服务的内核地址,这样就完成了一次系统服务的调用。SSDT 并不仅仅只包含一个庞大的地址索引表,它还包含着一些其它有用的信息,诸如地址索引的基地址、服务函数个数等。

SSDT 通过修改此表的函数地址可以对常用 Windows 函数进行 HOOK,从而实现对一些核心的系统动作进行过滤、监控的目的。一些HIPS、防毒软件、系统监控、注册表监控软件往往会采用此接口来实现自己的监控模块。

本质上,其实 SSDT 就是一个用来保存 Windows 系统服务地址的数组而已 。

32 位系统和 64 位上,获取 SSDT 表的方式并不相同,获取 SSDT 表中的函数地址也不相同。现在,我就分别对其进行极讲解介绍,并形成文档。本文主要讲解的是 32 位系统下,编程实现获取 SSDT 表的地址,以及获取 SSDT 表函数对应的内核地址。

实现原理

获取 SSDT 表的地址

在 32 位系统中,SSDT 表是内核 Ntoskrnl.exe 导出的一张表,导出符号为 KeServiceDescriptorTable,该表含有一个指针指向SSDT中包含 Ntoskrnl.exe 实现的核心服务。所以,我们要想在 32 位系统上获取 SSDT 表地址,直接获取 Ntoskrnl.exe 导出符号 KeServiceDescriptorTable 即可。

SSDT 表结构为:

#pragmapack(1)

typedefstruct_SERVICE_DESCIPTOR_TABLE

{

PULONGServiceTableBase;// SSDT基址

PULONGServiceCounterTableBase;// SSDT中服务被调用次数计数器

ULONGNumberOfService;// SSDT服务个数

PUCHARParamTableBase;// 系统服务参数表基址

}SSDTEntry,*PSSDTEntry;

#pragmapack()

所以,从 Ntoskrnl.exe 获取导出符号 KeServiceDescriptorTable 的代码如下:

externSSDTEntry__declspec(dllimport)KeServiceDescriptorTable;

获取 SSDT 表函数地址

在 32 位系统中,SSDT 包含了所有内核导出函数的地址。每个地址长度为 4 字节。所以要获得 SSDT 中某个函数的地址,如下代码所示:

KeServiceDescriptorTable.ServiceTableBase+SSDT函数索引号*4

// 或者

KeServiceDescriptorTable.ServiceTableBase[SSDT函数索引号]

SSDT 函数索引号可以从 ntdll.dll 文件中获取,当 ring3 级 API 函数最终进入 ring0 级的时候,它会先将 SSDT函数索引号 mov 给 eax 寄存器。所以,我们获取 ntdll.dll 导出函数的地址,从中获取 SSDT 函数索引号。具体的实现过程分析过程,可以参考我写的《内核内存映射文件之获取SSDT函数索引号》这篇文章。

编码实现

获取SSDT函数索引号// 从 ntdll.dll 中获取 SSDT 函数索引号

ULONGGetSSDTFunctionIndex(UNICODE_STRING ustrDllFileName,PCHAR pszFunctionName)

{

ULONG ulFunctionIndex=0;

NTSTATUS status=STATUS_SUCCESS;

HANDLE hFile=NULL;

HANDLE hSection=NULL;

PVOID pBaseAddress=NULL;

// 内存映射文件

status=DllFileMap(ustrDllFileName,&hFile,&hSection,&pBaseAddress);

if(!NT_SUCCESS(status))

{

KdPrint(("DllFileMap Error!\n"));

returnulFunctionIndex;

}

// 根据导出表获取导出函数地址, 从而获取 SSDT 函数索引号

ulFunctionIndex=GetIndexFromExportTable(pBaseAddress,pszFunctionName);

// 释放

ZwUnmapViewOfSection(NtCurrentProcess(),pBaseAddress);

ZwClose(hSection);

ZwClose(hFile);

returnulFunctionIndex;

}

// 内存映射文件

NTSTATUSDllFileMap(UNICODE_STRING ustrDllFileName,HANDLE*phFile,HANDLE*phSection,PVOID*ppBaseAddress)

{

NTSTATUS status=STATUS_SUCCESS;

HANDLE hFile=NULL;

HANDLE hSection=NULL;

OBJECT_ATTRIBUTES objectAttributes={0};

IO_STATUS_BLOCK iosb={0};

PVOID pBaseAddress=NULL;

SIZE_T viewSize=0;

// 打开 DLL 文件, 并获取文件句柄

InitializeObjectAttributes(&objectAttributes,&ustrDllFileName,OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE,NULL,NULL);

status=ZwOpenFile(&hFile,GENERIC_READ,&objectAttributes,&iosb,

FILE_SHARE_READ,FILE_SYNCHRONOUS_IO_NONALERT);

if(!NT_SUCCESS(status))

{

KdPrint(("ZwOpenFile Error! [error code: 0x%X]",status));

returnstatus;

}

// 创建一个节对象, 以 PE 结构中的 SectionALignment 大小对齐映射文件

status=ZwCreateSection(&hSection,SECTION_MAP_READ|SECTION_MAP_WRITE,NULL,0,PAGE_READWRITE,0x1000000,hFile);

if(!NT_SUCCESS(status))

{

ZwClose(hFile);

KdPrint(("ZwCreateSection Error! [error code: 0x%X]",status));

returnstatus;

}

// 映射到内存

status=ZwMapViewOfSection(hSection,NtCurrentProcess(),&pBaseAddress,0,1024,0,&viewSize,ViewShare,MEM_TOP_DOWN,PAGE_READWRITE);

if(!NT_SUCCESS(status))

{

ZwClose(hSection);

ZwClose(hFile);

KdPrint(("ZwMapViewOfSection Error! [error code: 0x%X]",status));

returnstatus;

}

// 返回数据

*phFile=hFile;

*phSection=hSection;

*ppBaseAddress=pBaseAddress;

returnstatus;

}

// 根据导出表获取导出函数地址, 从而获取 SSDT 函数索引号

ULONGGetIndexFromExportTable(PVOID pBaseAddress,PCHAR pszFunctionName)

{

ULONG ulFunctionIndex=0;

// Dos Header

PIMAGE_DOS_HEADER pDosHeader=(PIMAGE_DOS_HEADER)pBaseAddress;

// NT Header

PIMAGE_NT_HEADERS pNtHeaders=(PIMAGE_NT_HEADERS)((PUCHAR)pDosHeader+pDosHeader->e_lfanew);

// Export Table

PIMAGE_EXPORT_DIRECTORY pExportTable=(PIMAGE_EXPORT_DIRECTORY)((PUCHAR)pDosHeader+pNtHeaders->OptionalHeader.DataDirectory[0].VirtualAddress);

// 有名称的导出函数个数

ULONG ulNumberOfNames=pExportTable->NumberOfNames;

// 导出函数名称地址表

PULONG lpNameArray=(PULONG)((PUCHAR)pDosHeader+pExportTable->AddressOfNames);

PCHAR lpName=NULL;

// 开始遍历导出表

for(ULONG i=0;i

{

lpName=(PCHAR)((PUCHAR)pDosHeader+lpNameArray[i]);

// 判断是否查找的函数

if(0==_strnicmp(pszFunctionName,lpName,strlen(pszFunctionName)))

{

// 获取导出函数地址

USHORT uHint=*(USHORT*)((PUCHAR)pDosHeader+pExportTable->AddressOfNameOrdinals+2*i);

ULONG ulFuncAddr=*(PULONG)((PUCHAR)pDosHeader+pExportTable->AddressOfFunctions+4*uHint);

PVOID lpFuncAddr=(PVOID)((PUCHAR)pDosHeader+ulFuncAddr);

// 获取 SSDT 函数 Index

#ifdef_WIN64

ulFunctionIndex=*(ULONG*)((PUCHAR)lpFuncAddr+4);

#else

ulFunctionIndex=*(ULONG*)((PUCHAR)lpFuncAddr+1);

#endif

break;

}

}

returnulFunctionIndex;

}

获取SSDT表地址#pragmapack(1)

typedefstruct_SERVICE_DESCIPTOR_TABLE

{

PULONGServiceTableBase;// SSDT基址

PULONGServiceCounterTableBase;// SSDT中服务被调用次数计数器

ULONGNumberOfService;// SSDT服务个数

PUCHARParamTableBase;// 系统服务参数表基址

}SSDTEntry,*PSSDTEntry;

#pragmapack()

// 直接获取 SSDT

externSSDTEntry__declspec(dllimport)KeServiceDescriptorTable;

获取SSDT函数地址// 获取 SSDT 函数地址

PVOIDGetSSDTFunction(PCHAR pszFunctionName)

{

UNICODE_STRING ustrDllFileName;

ULONG ulSSDTFunctionIndex=0;

PVOID pFunctionAddress=NULL;

RtlInitUnicodeString(&ustrDllFileName,L"\\??\\C:\\Windows\\System32\\ntdll.dll");

// 从 ntdll.dll 中获取 SSDT 函数索引号

ulSSDTFunctionIndex=GetSSDTFunctionIndex(ustrDllFileName,pszFunctionName);

// 根据索引号, 从SSDT表中获取对应函数地址

pFunctionAddress=(PVOID)KeServiceDescriptorTable.ServiceTableBase[ulSSDTFunctionIndex];

// 显示

DbgPrint("[%s][Index:%d][Address:0x%p]\n",pszFunctionName,ulSSDTFunctionIndex,pFunctionAddress);

returnpFunctionAddress;

}

程序测试

在 Win7 32 位系统下,驱动程序正常执行:

在 Win10 32 位系统下,驱动程序正常执行:

总结

32 位下的 SSDT 表已经由 Ntoskrnl.exe 导出,我们直接获取导出符号 KeServiceDescriptorTable 就能获取 SSDT 表。其中,要注意 SSDT 结构体是按 1 字节大小对齐的。

对于 SSDT 函数索引号的获取,可以从 ntdll.dll 的导出函数中获取,因为 ntdll.dll 导出函数的开头,总是将 SSDT 函数索引号 mov 到 eax 寄存器,所以,我们可以直接根据 ntdll.dll 的导出函数地址,获取 SSDT 函数索引号。

参考

ssdt函数索引号_技术分享 - 32位系统上获取SSDT表地址以及从中获取指定SSDT函数的地址...相关推荐

  1. sql2005性能优化(在32位系统上突破2G内存使用量的方法)

    服务器磁盘为(SAS)IBM组成RAID0+1,SQL2K5只识别4G内存,实际只占用2G内存.而使用 AWE的话,应用程序可以直接将操作系统允许的最大物理内存量保留为未分页的内存.使用 AWE 使  ...

  2. ssdt函数索引号_【NT】一行代码获取SSDT服务索引号

    注:本文是以32位的windows7为实例. 今天在研究SSDT的过程中看到了一个大神写的教程,其中还附了一些代码,代码主要讲解的是SSDT hook过程,我在他的代码中没有看到任何有关服务函数的索引 ...

  3. mysql 创建索引 终止_技术分享 | 常见索引问题处理

    作者:EneTakane 数据库技术爱好者,爱可生 DBA 团队成员,负责 MySQL 日常问题处理以及数据库运维平台的问题排查,擅长 MySQL 主从复制及优化,喜欢钻研技术问题,还有不得不提的 w ...

  4. oracle 查看索引大小_技术分享|简述Oracle统计信息

    于树文 云技术管理处 在Oracle的11g版本中,统计信息为自动收集功能.在部署安装11g Oracle软件过程中,其中有一个步骤便是提示是否启动这个功能(默认是启用这个功能).有时候在生产环境中, ...

  5. mysql capi函数详解_技术分享|MySQLCAPI参数MYSQL_OPT_READ_TIMEOUT的一些行为分析

    作者:戴岳兵 MYSQL_OPT_READ_TIMEOUT 是 MySQL c api 客户端中用来设置读取超时时间的参数.在 MySQL 的官方文档中,该参数的描述是这样的: MYSQL_OPT_R ...

  6. oracle以32位运行,Oracle在 32位系统上运行突破sga1.7g的方法

    在32bit或者64bit的平台上跑32bit的Oracle的时候,SGA总是受到限制,导致大内存的机器不能完全发挥作用,最近就专门找了一些关于调整最大SGA区的文档,看了看,感觉收获蛮大的,总结了一 ...

  7. 32位系统上开发的Access为数据库的程序在64位机器上运行出错的解决办法

    64位平台下解决方法为在VS2010开发环境下进行以下操作:生成->配置管理器->平台->点击Any Cpu选项卡->新建->新建平台->X86

  8. linux 生成2g文件吗,linux 32位系统 c++写大于2G文件

    问题:在centos5.5 32位系统上,开发的c++程序,用vfprintf 输出日志文件,发现当日志大于2G时会报错"File size limit exceeded".开始以 ...

  9. 32位Windows7上8G内存使用感受+xp 32位下使用8G内存 (转)

    32位Windows7上8G内存使用感受+xp 32位下使用8G内存 博客分类: Windows XPWindowsIE企业应用软件测试  我推荐做开发的朋友:赶快加入8G的行列吧....呵呵..超爽 ...

  10. 32位Windows7上8G内存使用感受+xp 32位下使用8G内存

    我推荐做开发的朋友:赶快加入8G的行列吧....呵呵..超爽...速度超快...基本没有等待的概念...深有体会... 为什么要使用8G内存?在国内外各大论坛上,这都是一个有争议的问题.问题的反方论据 ...

最新文章

  1. python注入_python的常见命令注入威胁
  2. 分享阿里云SLB-负载均衡的实现基本原理架构
  3. MATLAB - 为什么imshow(g,[])可以正常显示,而imshow(g)却显示空白图像?
  4. ubuntu16安装中文版和windows关联
  5. python和嵌入式哪个容易_嵌入式与python选哪个
  6. Java通过Class的对象来获取泛型的class示例
  7. python pytest mark.parametrize
  8. silverlight 自定义资源整理(待后续补充)
  9. delphi开发回忆录——面向对象的基础,继承
  10. 火狐中jq的attr出现的bug问题用prop代替
  11. Navicat Premium 注册码与破解
  12. 手机html送礼,送给家中“老顽童”的好礼物!这些手机的远程功能你会用嘛?...
  13. stm32摄像头调试 | 串口传输照片数据 | 用python来设计上位机通信软件
  14. 中标麒麟linux系统安装打印机_中标麒麟Linux操作系统和理光打印机完成互相兼容认证...
  15. vue:不同环境配置不同打包命令
  16. 计算机网络 有效数据率,在计算机网络中,表征数据传输有效性的指标是( ) A.误码率 B.频带利用率 C.信道容量 D.传输速率...
  17. pic12f508 c 语言教程,pic12f508中文资料
  18. 抖音企业号seo排名优化账号矩阵系统
  19. discuz 数据字典大全
  20. PECL轻松安装PHP扩展

热门文章

  1. word中首行缩进、悬挂缩进、左缩进有什么区别?如何操作?
  2. caniuse-lite is outdated. Please run: npx browserslist@latest --update-db
  3. 世界地图各大洲鼠标移入切换显示
  4. Method annotated with @Bean is called directly. Use dependency injection instead
  5. POJ - 3404 Bridge over a rough river (DP)
  6. springboot打jar包部署在linux(阿里云)服务器上项目启动成功但页面访问时提示无法访问此网站
  7. csirs参考信号_发送和接收点(TRP)及信道状态信息参考信号(CSI-RS)传输的方法与流程...
  8. MAL II,怀槐凝集素II(MAL II)
  9. 2020牛客暑期多校训练营(第九场) The Escape Plan of Groundhog
  10. 深度学习技术在股票交易上的应用研究调查