看了梦无极的ssdt_hook教程,虽然大牛讲得很细,但是很多细节还是要自己去体会,才会更加深入。在这里我总结一下我的分析过程,若有不对的地方,希望大家指出来。
首先我们应该认识 ssdt是什么?从梦无极的讲解过程中,我联想到了这样一个场景:
古时候有一户人家,姓李,住在皇宫外面,他们家有一个女儿进皇宫当宫女。有一天,李家的老大爷要送东西给他女儿,我们假定他可以进皇宫,于是就开始了这样一个过程。进皇宫只有一条路,那就是走正门,于是李大爷带着东西从家走到了城外,进皇宫前必须从这门口进去,通过一条路后才能到达皇宫,守门的侍卫挺友善的,把老大爷的东西让一个侍卫拿到一个地方去放着,然后叫了一个手下带着老大爷往里走,当走到皇宫门口时,守城门的侍卫就离开了,让另一个人带着老大爷继续走,然后老大爷就进了皇宫,进了皇宫后,有一个侍卫来问李大爷的女儿是在哪边,这里皇宫只有东边和西边。于是李大爷跟他说在东边,于是侍卫把李大爷带到了东边,然后见到一个总管,李大爷给出女儿是几号房的,总管根据一个皇室总表,找到了李大爷的女儿住址,于是李大爷就找到了他女儿,总管还告诉他,你带的东西可以在哪 里去取。
这里拿OpenProcess函数比作李大爷,NtOpenProcess比作李大爷的女儿,OpenProcess通过普通的调用规则进行调用来到城外,再通过ZwOpenProcess进行接见,带着它往皇宫走,然后遇到KiFastSystemCall,带他进入了皇宫。进了ring0后,需要知道是ssdt还是shawssdt,于是分东边的西边,我们假定ssdt就是东西。于是从东边 的路走过去,遇到总管KiFastCallEntry,给出函数偏移号,总管手里的皇室总表就是ssdt这个数据结构,于是找到了NtOPenProcess,而OpenProcess的参数即李大爷带的东西在ParamTableBase这里去找。
好吧,故事讲完了,我们继续分析。
ssdt竟然是一个数组,那么我们就需要先得到这个数组的首地址,于是这个结构就出来了。
代码:
typedef struct ServiceDescriptorEntry {
unsigned int *ServiceTableBase;
unsigned int *ServiceCounterTableBase;
unsigned int NumberOfServices;
unsigned char *ParamTableBase;
} ServiceDescriptorTableEntry_t, *PServiceDescriptorTableEntry_t;
这个结构只需要知道两个参数的作用,一个是ServiceTableBase,这个参数是ssdt数组的基址,有了它,我们再给出具体函数的偏移,就能找到正确的函数地址了。
另一个是NumberOfService,这个是ssdt这个数组的最大值,也就是ssdt中函数的个数。
有了这个结构过后,按照偏移就可以找到想要Hook的函数的地址了。但是hook之前,需要修改内核保护,否则会无情蓝屏。
代码:
void PageProtectOff()
{
__asm{
cli
mov eax,cr0
and eax,not 10000h
mov cr0,eax
}
}
Cli表示让其他人都休息,看我一个人表演。
Cro这个寄存器就保存了内核保护的标志位,用 10000h取反再和他进行与运算,就使标志位从1变成0了,就可以修改内核了。当然,完成后得修改回来,否则会出现这样的情况,你在上面跳舞,别人看你表演,你跳完了,别人还一直盯着舞台,以为你还在表演。
代码:
void PageProtectOn()
{
__asm{
mov eax,cr0
or eax,10000h
mov cr0,eax
sti
}
}
修改了内核保护后ÿ