今天我们接上第二课继续加新功能:读取SSDT。 什么是ssdt? 我不作过多解释,因为我解释不清楚,GOOGLE一下有大把的资料,向大家介绍一篇好文章:http://blog.titilima.com/ssdt.html。我的理解就是建立ring0与ring3的映射关系。 操作SSDT表,我们会用到ntdll.dll中导出的KeServiceDescriptorTable这个结构体。
首先声明KeServiceDescriptorTable结构
typedef struct _ServiceDescriptorTable {
PVOID ServiceTableBase; //System Service Dispatch Table 的基地址
PVOID ServiceCounterTable;
//包含着 SSDT 中每个服务被调用次数的计数器。这个计数器一般由sysenter 更新。
unsigned int NumberOfServices;//由 ServiceTableBase 描述的服务的数目。
PVOID ParamTableBase; //包含每个系统服务参数字节数表的基地址-系统服务参数表
}*PServiceDescriptorTable;
extern PServiceDescriptorTable KeServiceDescriptorTable;
有了这个结构,我们就可以在我们的驱动中读取ring0级函数的执行地址,代码如下:
ULONG cReadSSDT()
{
LONG *SSDT_Adr,SSDT_NtOpenProcess_Cur_Addr,t_addr;
//读取SSDT表中索引值为0x7A的函数
//poi(poi(KeServiceDescriptorTable)+0x7a*4)
t_addr=(LONG)KeServiceDescriptorTable->ServiceTableBase;
KdPrint(("当前ServiceTableBase地址为%x \n",t_addr));
SSDT_Adr=(PLONG)(t_addr+0x7A*4);
KdPrint(("当前t_addr+0x7A*4=%x \n",SSDT_Adr));
SSDT_NtOpenProcess_Cur_Addr=*SSDT_Adr;
KdPrint(("当前SSDT_NtOpenProcess_Cur_Addr地址为%x \n",SSDT_NtOpenProcess_Cur_Addr));
return SSDT_NtOpenProcess_Cur_Addr;
}
还有一种方法是用内联汇编的方式读取
ULONG asmReadSSDT()
{
ULONG SSDT_NtOpenProcess_Cur_Addr;
__asm
{
push eax
push ebx
mov eax,KeServiceDescriptorTable
mov eax,[eax] //表的基地址
//[[KeServiceDescriptorTable]+0x7A*4]
mov ebx, 0x7A
shl ebx, 2
add eax, ebx
mov eax, [eax]
mov SSDT_NtOpenProcess_Cur_Addr, eax
pop ebx
pop eax
}
KdPrint(("NtOpenProcess Current address is:%x", SSDT_NtOpenProcess_Cur_Addr));
return SSDT_NtOpenProcess_Cur_Addr;
}
上面两个方法读取出的是NtOpenProcess函数的当前执行地址,下面我们读取它的原始地址。
ULONG getSystemCallAddr()
{
UNICODE_STRING Old_NtOpenProcess;
ULONG Old_Addr;
RtlInitUnicodeString(&Old_NtOpenProcess,L"NtOpenProcess");
Old_Addr=(ULONG)MmGetSystemRoutineAddress(&Old_NtOpenProcess);//取得NtOpenProcess的地址
KdPrint(("取得原函数NtOpenProcess的值为 %x",Old_Addr));
return Old_Addr;
}
当前地址与原始地址在干净的SSDT中是相等的,但是函数被inline hook后就不相等。
具体参看完整源码。
本文介绍了通过两种方法读取SSDT表中的NtOpenProcess函数地址:使用C语言结构体指针和内联汇编。同时展示了如何获取该函数的原始地址。
2248

被折叠的 条评论
为什么被折叠?



