***********关于hook**********************************
首先我们说下hook,什么是hook?hook的英文已经说明了,hook在英文中是钩的意思,计算机取其意叫钩子,而我的理解叫截!
大家应该写过r3下程序,估计也写过一些r3的hook,例如有一个API是OpenProcess,功能是打开进程得到进程句柄,比如我们要结束一个进程,如果调用TerminateProcess这个API来结束进程,就必须知道进程句柄。
那么结束一个进程的伪代码就是这样
push PID
push 0
push 2035711 //权限
call OpenProcess
push 0
push eax
call TerminateProcess
这段代码执行,如果正常的话,这个PID的进程应该就会被结束了,这里我们来设想一下,假设我们这里call openprocess做一下修改,变成这样,
push PID
push 0
push 2035711 //权限
call myopen
push 0
push eax
call TerminateProcess
myopen这里是我自己写的一个函数,这个函数跟OpenProcess一样,也有三个参数,也有相同的返回值类型。在这个函数里,我把三个参数,传递给真正的OpenProcess,而把OpenProcess的返回值,做为myopen的返回值,那么,这套流程下来,肯定也是可以执行的,myopen函数类似这样:
有童鞋要问了,这样做执行是可以执行,但是何必费力做一个包装函数,再调用原函数,这样的意义是什么?
现在这套流程下来非常正常,我们做的这个包装函数没有意义,现在我们假设,如果有一个恶意进程PID=9527,你写这个结束程序来结束这个PID=9527的进程,这个恶意进程却给你安装了这个包装函数。但是,他稍微的修改了下,变成这样:
嘿嘿,这样下来,会怎么样?你还能结束恶意进程吗?
当然不能
因为你如果打开PID为9527的进程时候,得不到正确的句柄返回值,返回值为0。
这个方法,叫hook,我们也可以在OpenProcess的函数头进行hook。
而我把这种方法,叫截,截获后,可阻止,可改变,可放行!hook,无非是截获流程,根据自己的需要替换,阻止,放行而已!
r3层的r0层的hook原理一样,大道至简,现在很多驱动hook一上来就说内核,ssdt,弄的大家一头雾水,其实假传万卷书,真传一句话,驱动hook就是截获api在内核的执行流程,然后根据自己的需要替换,阻止,放行。 理论上你会r3的hook,你一定会r0的hook!
*********关于SSDT**********************************
明白了HOOK原理后,那么内核hook就很简单了,只要我们在API函数执行流程走到内核后在内核流程中进行截断,处理,就是内核hook了
我们在内核流程的位置选择了SSDT表,那么,SSDT表是什么?
SSDT(System Services Descriptor Table),系统服务描述符表。这个表就是一个把ring3的Win32 API和ring0的内核API联系起来。SSDT并不仅仅只包含一个庞大的地址索引表,它还包含着一些其它有用的信息,诸如地址索引的基地址、服务函数个数等。
现在我们以OpenProcess来说明,看下SSDT表在函数执行流程中起的作用!
函数的执行流程以OpenProcess是这样.先说明,下面再解释
*r3 OpenProcess进入ntdll.dll的NtOpenProcess或ZwOpenProcess函数(反正是同一个地址)
*进入内核
*进入Ntoskrnl.exe的ZwOpenProcess
*根据ZwOpenProcess的索引,找到SSDT表对应的地址,再根据SSDT表对应地址的数据(Ntoskrnl.exe的NtOpenProcess),执行函数。
*也就是Ntoskrnl.exe的NtOpenProcess才是真正的执行主体
根据上面OpenProcess的流程说明,进入内核后,OpenProcess是进入了Ntoskrnl.exe的ZwOpenProcess这个函数,根据这个函数的索引,在SSDT表查找到真正的内核执行体Ntoskrnl.exe的NtOpenProcess函数,然后执行。
首先,我们先找到SSDT表,下面我们来看一个被内核导出的结构KeServiceDescriptorTable.
struct
KeServiceDescriptorTable
SSDT表的指针
//这个指向系统服务函数地址表
ServiceCounterTableBase;
NumberOfService;
//服务函数的个数
TableBase
end KeServiceDescriptorTable
这里第一个字段就是SSDT表的指针,我们用windbg看一下,先dd KeServiceDescriptorTable
第一个字段是80502b8c,这个就是SSDT表的地址了
我们dd 80502b8c看看SSDT表
里面存放的都是一些NT函数地址。
之前说了Ntoskrnl.exe的ZwOpenProcess函数有个索引,根据索引在SSDT表里查找真正的内核执行体函数Ntoskrnl.exe的NtOpenProcess函数
我们看看ZwOpenProcess,用windbg查看,u nt!ZwOpenProcess
索引号是7Ah
那么Ntoskrnl.exe的NtOpenProcess函数应该存放的位置就是=80502b8ch(SSDT表的开始地址)+7Ah(索引)*4(每个函数的间隔),得到80502D74h
80502D74h处存放的地址是805C2296,这个应该是Ntoskrnl.exe的NtOpenProcess函数的地址,我们看下是不是
果然是的.
所以函数在由R3进R0后,要通过SSDT表的来选择最终执行的内核函数,假设我们想在内核中HOOKOpenProcess这个函数,只需要修改80502d74处存放的地址,放入我们自己写的newNtOpenProcess函数地址,在我们newNtOpenProcess函数中,进行了修改或替换或阻止或放行后执行真正的NtOpenProcess,这就是所谓的SSDThook
那么,我简单说下ssdt表中 hook OpenProcess这个函数步骤
1.先得到Ntoskrnl.exe的NtOpenProcess函数的地址(用MmGetSystemRoutineAddress函数得到内核导出函数地址)
2.再得到SSDT表中Ntoskrnl.exe的NtOpenProcess函数应该存放的位置
(SSDT表的开始地址)+(索引)*4(每个函数的间隔)
SSDT表的开始地址=[KeServiceDescriptorTable->SSDT表的指针]
索引=[Ntoskrnl.exe的zwOpenProcess地址+1]
3.自己建立一个新函数,参数和返回值要和Ntoskrnl.exe的NtOpenProcess函数一模一样
4.把自己的新函数地址写到SSDT表中我们算出来的存放位置处,(mov [(SSDT表的开始地址)+(索引)*4(每个函数的间隔)],新函数地址)
下面是部分重要代码:
执行效果如图: