找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 2486|回复: 0
打印 上一主题 下一主题
收起左侧

HOOK SSDT表的理解(1)

[复制链接]
跳转到指定楼层
楼主
ID:71922 发表于 2015-1-10 23:20 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
**********************************************************************
刚开始时挺难理解SSDT 郁金香的视频教程说的还是不够详细,后来自己实验了,
多比较,多看就明白怎么回事了,现在写下来方便以后自己查看。
**********************************************************************
SSDT 是一张表,里面存储了所有Nt开头的内核API内存地址。
每个内核API都有一个索引号。系统通过这个索引号得到相应的内核NtAPI的内存地址然后调用
可以把 SSDT 想象成一个LONG *SSDT 指针:
LONG *SSDT;
SSDT[0] 存储着 NtAcceptConnectPort 所在的内存地址   0x805A565C
SSDT[1] 存储着 NtAccessCheck 所在的内存地址         0x805F243E
...
SSDT[122] 存储着 NtOpenProcess 所在的内存地址       0x805CC470  
其中数组元素
0 就是 NtAcceptConnectPort 索引号
1 就是 NtAccessCheck 索引号
122 就是 NtOpenProcess 索引号
如果要得到NtOpenProcess所在的内存地址 首先必须要知道 SSDT首地址 和 NtOpenProcess的索引号
【注意】不同的系统版本索引号也会不一样 此处仅仅是为了解释SSDT表。
现在根据实例来配合,现在要得到NtOpenProcess的内存地址
用 winDebug 得到 SSDT这个表的首地址是 0x80505480




进入这个地址看看里面的内容是什么



0: kd> dd poi[KeServiceDescriptorTable]
  地址       0        1        2        3  
80505480  805a565c 805f243e 805f5c74 805f2470
             4        5        6        7
80505490  805f5cae 805f24a6 805f5cf2 805f5d36
             8        9        10       11
805054a0  80616d1e 80617a60 805ed83c 805ed494
805054b0  805d5bae 805d5b5e 80617344 805b6fe2
805054c0  80616960 805a9ae6 805b15f6 805d7672
805054d0  8050289c 80617a52 80577b0a 80539c34
805054e0  8060ff2e 805bd55c 805f61ae 80624cf0
805054f0  805fa6c2 805a5d4a 80624f44 805a55fc

看到 0x80505480 这个地址存的第0个地址是 0x805A565C
Kerne Detective 这个工具看看这个是什么API




0个是NtAcceptConnectPort 这个API
可以看到 WinDebug 得到的数据 和 KD 一样
用工具可以很容易找到SSDT的基地址 和 索引号以及NtAPI的内存地址
用编程实现  (精华):
要检查 SSDT 指定的NtAPI有没有被HOOK 就要先 获取到现在的NtAPI地址、获取原来的NtAPI的内存地址,然后将两个地址相比较即可。
获取现在NtAPI的内存地址的具体流程
首先应该得到SSDT的首地址 即 基址
然后将ServiceTableBase 的内存首地址+索引号*4 得到存储着相应索引号内核NtAPI现在的内存地址的地址(ServiceTableBase的偏移地址)
读取该地址得到现在的NtAPI内存地址
获取原来的NtAPI的内存地址的具体流程
通过 MmGetSystemRoutineAddress 可以得到原来的NtAPI 地址
主要是 获取现在的NtAPI比较费心思去理解,只要理解了这个流程,也就理解了SSDT表的结构了。
看看下面的代码以及注释就能理解SSDT表的结构了
左边是用KD工具得到 NOtOpenProcess 内存地址 右边是用WinDebug得到




下图是通过编程得到 NtOpenProcess 内存地址:




代码:
ypedef struct _ServiceDescriptorTable {
     PVOID ServiceTableBase; //System Service Dispatch Table 的基地址
     PVOID ServiceCounterTable;
     //包含着SSDT 中每个服务被调用次数的计数器。这个计数器一般由sysenter 更新。
     unsigned int NumberOfServices;//由ServiceTableBase 描述的服务的数目。
     PVOID ParamTableBase; //包含每个系统服务参数字节数表的基地址-系统服务参数表
}*PServiceDescriptorTable;  
extern PServiceDescriptorTable KeServiceDescriptorTable;
ULONG GetNt_CurAddr() //获取当前SSDT_NtOpenProcess的现在地址
{
LONG *SSDT_Adr,SSDT_NtOpenProcess_Cur_Addr,t_addr;      
KdPrint(("驱动成功被加载中.............................\n\n"));
KdPrint(("********************** 计算现在的地址**********************\n\n"));
//读取SSDT表中索引值为x7A的函数
//poi(poi(KeServiceDescriptorTable)+0x7a*4)
t_addr=(LONG)KeServiceDescriptorTable->ServiceTableBase;     // 得到ServiceTableBase 的地址
KdPrint(("[得到ServiceTableBase 的基址] \n当前ServiceTableBase地址为0x%X \n\n",t_addr));
// 将该地址里面的内容+ 索引号* 4  就能得到相应索引号内核NtAPI现在的内存地址0x7A为NtOpenProcess在SSDT的索引
SSDT_Adr=(PLONG)(t_addr+0x7A*4);
KdPrint(("[将ServiceTableBase 的内存首地址+索引号*4 \n得到存储着相应索引号内核NtAPI现在的内存地址的地址(ServiceTableBase的偏移地址)]\n"));
KdPrint(("SSDT首地址0x%X + 0x7A * 4= 0x%X  这个地址0x%X 存储着索引号为0x7A 的NtOpenProcess内存地址\n\n", t_addr, SSDT_Adr, SSDT_Adr));
SSDT_NtOpenProcess_Cur_Addr=*SSDT_Adr;                       
KdPrint(("[读取0x%X 得到NtOpenProcess 函数现在的内存地址] 现在的NtOpenProcess 内存地址为0x%X \n\n",SSDT_Adr, SSDT_NtOpenProcess_Cur_Addr));
KdPrint(("********************** 计算完毕***************************\n\n"));
// 汇编
/*
__asm
{    int 3
push ebx
push eax
  mov ebx,KeServiceDescriptorTable
  mov ebx,[ebx] //表的基地址 取 KeServiceDescriptorTable 的地址  t_addr=(LONG)KeServiceDescriptorTable->ServiceTableBase;
  mov eax,0x7a
  shl eax,2//0x7A*4 //imul eax,eax,4//shl eax,2
  add ebx,eax//[KeServiceDescriptorTable]+0x7A*4
  mov ebx,[ebx] // SSDT_NtOpenProcess_Cur_Addr=*SSDT_Adr;    // 取出该地址中存储的NtOpenProcess 函数的地址      
        mov SSDT_NtOpenProcess_Cur_Addr,ebx
pop  eax
pop  ebx
}
*/
return SSDT_NtOpenProcess_Cur_Addr;                              // 将获得的地址返回
}
ULONG GetNt_OldAddr()
{
     UNICODE_STRING Old_NtOpenProcess;
    ULONG Old_Addr;
     KdPrint(("********************** 计算原来的地址**********************\n\n"));
     RtlInitUnicodeString(&Old_NtOpenProcess,L"NtOpenProcess");
     Old_Addr=(ULONG)MmGetSystemRoutineAddress(&Old_NtOpenProcess);//取得NtOpenProcess的地址
     KdPrint(("用MmGetSystemRoutineAddress 取得原来NtOpenProcess 的地址为0x%X\n\n",Old_Addr));
     KdPrint(("********************** 计算完毕****************************\n\n"));
     return Old_Addr;
}
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject,PUNICODE_STRING B) //TYPEDEF LONG NTSTATUS
{  
     ULONG cur,old;
     cur=GetNt_CurAddr();// 得到现在的NtAPI 地址
     old=GetNt_OldAddr();// 得到原来的NtAPI 地址
     if (cur!=old)
         KdPrint(("NtOpenProcess被HOOK了"));
     else
         KdPrint(("NtOpenProcess 没有被HOOK"));


分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享淘帖 顶 踩
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表