刚开始看到通过 SSDT 来 HOOK ZwXXX 内核函数的方法时不是很了解, 等把 ZwXXX 反汇编后才发现技术细节.
原来也没什么新鲜的,就是找到目标函数在 SSDT 中的位置( 偏移量=位置*4 ), 然后保存并替换偏移量处的值为自己新的函数地址就行了。
这种技术现在已是老掉牙了,不过在实际的软件开发中也比较常用, 可以实现各种监控功能.
上次安了个诺顿, 它把所有的 ZwXXX 都给 Hook, 而且在 service.exe 注入了监视线程,作用是防止主进程被关闭.
不说别的了,免得思路被打断了.
下面以 Hook ZwDeviceIoControlFile 为例说明一下:
例如下面的 ring0 级代码:
VOID HookStart( void )
{
if( !IsHooked )
{
RealZwDeviceIoControlFile = SYSCALL( ZwDeviceIoControlFile );
SYSCALL( ZwDeviceIoControlFile ) = (PVOID) HookZwDeviceIoControlFile;
IsHooked = TRUE;
}
}
你能想象这样的代码能取得 ZwDeviceIoControlFile 在 SSDT 中的位置吗?
现在再让我们看看反汇编代码:
.text:00010381 mov eax, ds:ZwDeviceIoControlFile ; 保存 ZwDeviceIoControlFile 地址到 eax
.text:00010386 mov ecx, ssdt ; 取得 ssdt 的地址, 保存到 ecx
.text:0001038C push esi
.text:0001038D mov edx, [eax+1] ; 取得 ZwDeviceIoControlFile 在 SSDT 中的位置, 参看
; 该函数反汇编后的代码 B838000000 MOV EAX, 00000038, eax+1 的效果是跳过了B8, 指向了38000000。[eax+1] 的值自然就是 00000038 (NOTE: Little-Endian).现在 edx 值为 00000038.
.text:00010390 mov esi, [ecx] ; 取得 SSDT 的地址 ==> esi
.text:00010392 mov edx, [esi+edx*4] ; 保存 ZwDeviceIoControlFile 真正的函数地址
.text:00010395 pop esi
.text:00010396 mov dword_10814, edx
.text:0001039C mov eax, [eax+1] ; ZwDeviceIoControlFile 函数在 SSDT 的位置 ===> eax
.text:0001039F mov ecx, [ecx] ; SSDT 地址
.text:000103A1 mov dword ptr [ecx+eax*4], offset sub_1030E ; 用我们自己新的函数替换
; ZwDeviceIoControlFile 在SSDT位置中的数值
OK!! ,现在已经在 dword_10814 保存了正确的 ZwDeviceIoControlFile 地址, 并把我们自己的函数 dword_10814 替换了ZwDeviceIoControlFile , 到此完成!
下面是 ZwDeviceIoControlFile的反汇编代码, 可以简单的在windbg 中用 "u ZwDeviceIoControlFile" 得到反汇编代码
00400BC6: B838000000 MOV EAX, 00000038
00400BCB: 8D542404 LEA EDX, [ESP+04]
00400BCF: CD2E INT 2E
00400BD1: C22800 RETN 0028
入口点加1,便是系统调用号.