HOOK钩子技术4 SSDT HOOK
来源:互联网 发布:java web中启动线程池 编辑:程序博客网 时间:2024/05/22 03:30
API 调用过程
以一个demo测试为例,本测试查看OpenProcess
在R3-R0
下的调用。
// test.cpp : Defines the entry point for the console application.//#include "stdafx.h"#include <windows.h>#include <stdio.h>int main(int argc, char* argv[]){// __asm int 3 HANDLE handle = OpenProcess(PROCESS_ALL_ACCESS,FALSE,2548); printf("handle=%08x",handle); return 0;}
RING3
载入OD具体分析调用。
ring3 application space00401028 |. 8BF4 mov esi, esp0040102A |. 68 F4090000 push 0x9F4 ; /ProcessId = 9F40040102F |. 6A 00 push 0x0 ; |Inheritable = FALSE00401031 |. 68 FF0F1F00 push 0x1F0FFF ; |Access = PROCESS_ALL_ACCESS00401036 |. FF15 4CA14200 call dword ptr ds:[<&KERNEL32.OpenProcess>] ; \OpenProcess
此时由测试进程模块 进入到kernel32
模块
--kernel32.dll (OpenProcess)7C830A0B 8975 E8 mov dword ptr ss:[ebp-0x18], esi7C830A0E 8975 F0 mov dword ptr ss:[ebp-0x10], esi7C830A11 8975 F4 mov dword ptr ss:[ebp-0xC], esi7C830A14 FF15 1C11807C call dword ptr ds:[<&ntdll.NtOpenProcess>]; ntdll.ZwOpenProcess----ntdll.dll(NtOpenProcess (or ZWOpenProcess))7C92D5E0 > B8 7A000000 mov eax, 0x7A7C92D5E5 BA 0003FE7F mov edx, 0x7FFE03007C92D5EA FF12 call dword ptr ds:[edx] ; ntdll.KiFastSystemCall7C92E4F0 > 8BD4 mov edx, esp7C92E4F2 0F34 sysenter ; ring3->ring07C92E4F4 > C3 retn
在R3下的函数调用过程:本地模块->kernel32(openprocess)->ntdll(zwOpenProcess)->ntdll(sysenter)
。
ring3级别的函数调用栈回朔地址 堆栈 函数过程 / 参数 调用来自 结构........ ........ sysenter ntdll.KiFastSystemCall+0xC(7C92E4F2) ...0012FEE0 7C92D5EC 包含ntdll.KiFastSystemCall ntdll.7C92D5EA 0012FF1C0012FEE4 7C830A1A ntdll.ZwOpenProcess kernel32.7C830A14 0012FF1C0012FF20 0040103C kernel32.OpenProcess test.00401036 0012FF1C0012FF24 001F0FFF Access = PROCESS_ALL_ACCESS0012FF28 00000000 Inheritable = FALSE0012FF2C 000009F4 ProcessId = 9F40012FF84 00401239 test.00401005 test.<模块入口点>+0E4 0012FF80
也可以用windbg
去查看,更简单,但是调试就不能是local kernel
模式了,往windbg
中拖拽一个可执行文件后,查看结构
0:000> u kernel32!openprocess l30 //l30表示取30条指令,只求多,不求少kernel32!OpenProcess:7c8309d1 8bff mov edi,edi7c8309d3 55 push ebp7c8309d4 8bec mov ebp,esp7c8309d6 83ec20 sub esp,20h7c8309d9 8b4510 mov eax,dword ptr [ebp+10h]7c8309dc 8945f8 mov dword ptr [ebp-8],eax7c8309df 8b450c mov eax,dword ptr [ebp+0Ch]7c8309e2 56 push esi7c8309e3 33f6 xor esi,esi7c8309e5 f7d8 neg eax7c8309e7 1bc0 sbb eax,eax7c8309e9 83e002 and eax,27c8309ec 8945ec mov dword ptr [ebp-14h],eax7c8309ef 8d45f8 lea eax,[ebp-8]7c8309f2 50 push eax7c8309f3 8d45e0 lea eax,[ebp-20h]7c8309f6 50 push eax7c8309f7 ff7508 push dword ptr [ebp+8]7c8309fa 8d4510 lea eax,[ebp+10h]7c8309fd 50 push eax7c8309fe 8975fc mov dword ptr [ebp-4],esi7c830a01 c745e018000000 mov dword ptr [ebp-20h],18h7c830a08 8975e4 mov dword ptr [ebp-1Ch],esi7c830a0b 8975e8 mov dword ptr [ebp-18h],esi7c830a0e 8975f0 mov dword ptr [ebp-10h],esi7c830a11 8975f4 mov dword ptr [ebp-0Ch],esi7c830a14 ff151c11807c call dword ptr [kernel32!_imp__NtOpenProcess (7c80111c)]7c830a1a 3bc6 cmp eax,esi7c830a1c 5e pop esi7c830a1d 0f8cdf710000 jl kernel32!OpenProcess+0x53 (7c837c02)7c830a23 8b4510 mov eax,dword ptr [ebp+10h]7c830a26 c9 leave7c830a27 c20c00 ret 0Ch
可以看到,从导入表中调用了NtOpenProcess
。
7c830a14 ff151c11807c call dword ptr [kernel32!_imp__NtOpenProcess (7c80111c)]
很遗憾,这里没有告诉NtOpenProcess
属于哪个模块(其实是ntdll
模块),没关系,如果符号唯一的话,可以windbg可以识别出来
再次跟进NtOpenProcess
:
0:000> u ntopenprocess l4ntdll!ZwOpenProcess:7c92d5e0 b87a000000 mov eax,7Ah7c92d5e5 ba0003fe7f mov edx,offset SharedUserData!SystemCallStub (7ffe0300)7c92d5ea ff12 call dword ptr [edx]7c92d5ec c21000 ret 10h
这里告诉我们是ntdll
模块。同时可以注意到ntdll下NtOpenProcess 和ZwOpenProcess
是同一个函数。当然,不确定的话可以尝试一下:
0:000> u zwopenprocess l4ntdll!ZwOpenProcess:7c92d5e0 b87a000000 mov eax,7Ah7c92d5e5 ba0003fe7f mov edx,offset SharedUserData!SystemCallStub (7ffe0300)7c92d5ea ff12 call dword ptr [edx]7c92d5ec c21000 ret 10h
可以看到,它们两个是同一个函数。
跟进 [edx]=[7ffe0300]=?
0:000> dd 7ffe03007ffe0300 7c92e4f0 7c92e4f4 00000000 000000007ffe0310 00000000 00000000 00000000 000000007ffe0320 00000000 00000000 00000000 000000007ffe0330 32c5a0a6 00000000 00000000 000000007ffe0340 00000000 00000000 00000000 000000007ffe0350 00000000 00000000 00000000 000000007ffe0360 00000000 00000000 00000000 000000007ffe0370 00000000 00000000 00000000 000000000:000> u 7c92e4f0 l3*****ntdll!KiFastSystemCall:*****7c92e4f0 8bd4 mov edx,esp7c92e4f2 0f34 sysenterntdll!KiFastSystemCallRet:7c92e4f4 c3 ret
可以看到7c92e4f2 0f34 sysenter
,实现r3到r0的转化。个人感觉用windbg
更快速一点,更适合查看数据结构,而不适合ring3
下的动态调试(可能也是因为我不熟吧)。
RING0
执行sysenter
之后,就与ring3
没有关系了,此时进入ring0
,调试器只能选择windbg
,用local kernel
调试分析。
有一点是默认的,进入内核之后,会自动调用对应的nt!ZwOpenProcess
.(保留意见-update:5.14)
ring0 kernel system spacent!ZwOpenProcess:804ff720 b87a000000 mov eax,7Ah804ff725 8d542404 lea edx,[esp+4]804ff729 9c pushfd804ff72a 6a08 push 8804ff72c e850ed0300 call nt!KiSystemService (8053e481)804ff731 c21000 ret 10hnt!ZwOpenProcessToken:804ff734 b87b000000 mov eax,7Bh804ff739 8d542404 lea edx,[esp+4]804ff73d 9c pushfd804ff73e 6a08 push 8804ff740 e83ced0300 call nt!KiSystemService (8053e481)804ff745 c20c00 ret 0Chnt!ZwOpenProcessTokenEx:804ff748 b87c000000 mov eax,7Ch804ff74d 8d542404 lea edx,[esp+4]804ff751 9c pushfd804ff752 6a08 push 8804ff754 e828ed0300 call nt!KiSystemService (8053e481)804ff759 c21000 ret 10h call nt!KiSystemService (8053e481)做了什么:从eax中获得ssdt数组索引,访问ssdt找到nt!NtOpenProcess地址,执行最终的内核函数nt!NtOpenProcessnt!NtOpenProcess:805c2296 68c4000000 push 0C4h805c229b 68a8aa4d80 push offset nt!ObWatchHandles+0x25c (804daaa8)805c22a0 e86b6cf7ff call nt!_SEH_prolog (80538f10).......805c250d eb05 jmp nt!NtOpenProcess+0x27e (805c2514)805c250f b8300000c0 mov eax,0C0000030h805c2514 e8326af7ff call nt!_SEH_epilog (80538f4b)805c2519 c21000 ret 10h
因此可以得到以下流程顺序:
OpenProcessring3: our module ->kernel32.dll(OpenProcess) ->ntdll(zw(nt)OpenProcess) (ssdt 索引号在eax中) ->system call ring0: ->ntoskrnl.exe(nt!ZWOpenProcess) (同样存放ssdt索引号在eax中) -> ntoskrnl.exe(nt!KiSystemService) ->ntoskrnl.exe(nt!NtOpenProccess)
update2:5.14
这幅图是我查找资料时得到的,大致说的还行。
但是还有些问题,这个图的画法就不说了。记得我前面说了:
有一点是默认的,进入内核之后,会自动调用对应的
nt!ZwOpenProcess
.(保留意见-update:5.14)
这幅图作者的意思是说,切换时触发中断后,一定是先执行nt!KiSystemService
,也就是一定不是先执行nt!ZwOpenProcess(内部其实也是在调用KiSystemService)
,这和我之前的说法有些出入 。还有说nt!zw*
不触发中断,什么意思。下个断点去研究了一下。
kd> bc *kd> bp nt!zwOpenProcesskd> bp nt!ntOpenProcesskd> bl 0 e 804ff720 0001 (0001) nt!ZwOpenProcess 1 e 805c2296 0001 (0001) nt!NtOpenProcesskd> gBreakpoint 1 hitnt!NtOpenProcess:805c2296 68c4000000 push 0C4h
***********************
可以看到,打开一个程序后,断点在Nt*
上,没有在ZW*
上,所以我之前的说法是错误的。在本例中使用ntdll.KiFastSystemCall
,并没有进入到nt!ZWOpenProcess
中。
其实最好的方式是在KiSystemService
中下断点。但是这个符号基本上随时都在调用,根本调试不了。我想到的方法是添加条件断点,可惜现在还不会啊。。。
***********************
或者说,要研究的问题是:
既然r3下已经有了
nt!nt*
函数在ssdt
中的索引号,通过nt!KiSystemservice
检索就可以直接执行目标内核函数。那么nt!ZW*
再包装一次索引号的价值是什么。优势在哪里。
ntoskrnl.exe(nt!KiSystemService)
我没有研究过,汇编有好多行,不过现在知道主要功能如下。
通过eax(ssdt索引号)找到对应的nt!nt*函数地址,执行此函数,此函数为最终的内核函数。
SSDT技术原理
上面概括性的讲了可以通过ssdt索引号
找到原始API函数地址,现在具体分析下SSDT
的数据结构和查找过程。
首先需要了解如何找到SSDT:
typedef struct _SERVICE_DESCRIPTOR_TABLE{ PULONG ServiceTableBase; //ssdt的指针 PULONG ServiceCounterTableBase; ULONG NumberOfService; //SSDT中存放的NATIVE API 地址数据,即可索引数目。 PUCHAR ParamTableBase;}SERVICE_DESCRIPTOR_TABLE,*PSERVICE_DESCRIPTOR_TABLE;
ntoskrnl.exe
中导出了PSERVICE_DESCRPITOR_TABLE
类型指针,变量为KeServiceDescriptorTable
,在编程中导出即可。
来看下这个结构,使用windbg
选择local kernel
。
lkd> dd KeServiceDescriptorTable l480553fa0 80502b8c 00000000 0000011c 80503000
从上面的数据结构可以看出,ssdt的地址为80502b8c
,可索引的函数有0x11c
个。
SSDT是个数组结构,里面存放了所有的nt!nt*
函数地址,我们来查看下SSDT数组。
lkd> dd 80502b8c l0x11c80502b8c 8059a948 805e7db6 805eb5fc 805e7de880502b9c 805eb636 805e7e1e 805eb67a 805eb6be80502bac 8060cdfe 8060db50 805e31b4 805e2e0c80502bbc 805cbde6 805cbd96 8060d424 805ac5ae...
或者直接输入一下命令。
lkd> dd kiservicetable80502b8c 8059a948 805e7db6 805eb5fc 805e7de880502b9c 805eb636 805e7e1e 805eb67a 805eb6be80502bac 8060cdfe 8060db50 805e31b4 805e2e0c80502bbc 805cbde6 805cbd96 8060d424 805ac5ae...
可以看到,第一种方法是通过KeServiceDescriptorTable
得到ssdt地址,再查看。第二种是直接用符号查看。遗憾的是,编程中只能用第一种方法来获取,因为ntoskrnl.exe
没有导出这个符号变量。来验证一下。
C:\WINDOWS\system32>dumpbin /exports ntoskrnl.exe |findstr "KeService" 609 252 00083220 KeServiceDescriptorTableC:\WINDOWS\system32>dumpbin /exports ntoskrnl.exe |findstr "KiService"C:\WINDOWS\system32>
不管上面的小插曲,来验证下关于SSDT数组存放最终API地址的说法是否正确。
nt!ZwOpenProcess:804ff720 b87a000000 mov eax,7Ah804ff725 8d542404 lea edx,[esp+4]804ff729 9c pushfd804ff72a 6a08 push 8804ff72c e850ed0300 call nt!KiSystemService (8053e481)
从上方可以得到nt!NTOpenProcess
地址在SSDT
表中的索引为7Ah。接下来查找一下其地址。
lkd> dd kiservicetable +0x7A*4 l180502d74 805c2296lkd> u 805c2296nt!NtOpenProcess:805c2296 68c4000000 push 0C4h805c229b 68a8aa4d80 push offset nt!ObWatchHandles+0x25c (804daaa8)805c22a0 e86b6cf7ff call nt!_SEH_prolog (80538f10)805c22a5 33f6 xor esi,esi805c22a7 8975d4 mov dword ptr [ebp-2Ch],esi805c22aa 33c0 xor eax,eax805c22ac 8d7dd8 lea edi,[ebp-28h]805c22af ab stos dword ptr es:[edi]
可见,猜想是正确的。
这时候再谈论SSDT HOOK就很简单了。SSDT表中存放的是目标函数地址,改写这个函数地址,就可以为所欲为了。从流程上说,我们的挂钩点是在调用ring0下的nt!KiSystemService
时触发的。
DEMO
#include "stdafx.h"#ifdef __cplusplusextern "C" NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath);#endifvoid SSDTHookUnload(PDRIVER_OBJECT);//这个结构不是SSDT,是服务描述表,不过能从成员中能找到SSDT而已。typedef struct _SERVICE_DESCRIPTOR_TABLE{ PULONG ServiceTableBase; PULONG ServiceCounterTableBase; ULONG NumberOfService; PUCHAR ParamTableBase;}SERVICE_DESCRIPTOR_TABLE,*PSERVICE_DESCRIPTOR_TALBE;//必须extern "C" ,因为文件为CPP//extern "C" __declspec(dllimport) PSERVICE_DESCRIPTOR_TALBE KeServiceDescriptorTable; --此句错误,会出现无法挂钩,找了很长时间的bug。。可能是声明__declspec(dllimport)的姿势不对吧。看来是资料查错了,好好去补基础。extern "C" PSERVICE_DESCRIPTOR_TALBE KeServiceDescriptorTable;//搬运工typedef NTSTATUS (*NtCreateProcessEx)( OUT PHANDLE ProcessHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, IN HANDLE ParentProcess, IN BOOLEAN InheritObjectTable, IN HANDLE SectionHandle OPTIONAL, IN HANDLE DebugPort OPTIONAL, IN HANDLE ExceptionPort OPTIONAL ); //函数指针,本程序中存放的是原始函数地址NtCreateProcessEx ulNtCreateProcessEx = NULL;//SSDT中此函数地址的指针ULONG ulNtCreateProcessExAddr = 0;/*xp及其以后,SSDT内核内存页为只读属性,为了改写需要修改CR0 WP位cr0的第16位是WP位,只要将这一位置0就可写,置1则只读。*/void REMOVE_ONLY_READ(){ __asm { push eax mov eax,CR0 and eax,~10000h //16th bit is 0 mov CR0,eax pop eax }}void RESET_ONLY_READ(){ __asm { push eax mov eax,CR0 or eax,10000h //16th bit is 1 mov CR0,eax pop eax }}//fake function ,声明要和NtCreateProcessEx一致。NTSTATUS MyNtCreateProcessEx( OUT PHANDLE ProcessHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, IN HANDLE ParentProcess, IN BOOLEAN InheritObjectTable, IN HANDLE SectionHandle OPTIONAL, IN HANDLE DebugPort OPTIONAL, IN HANDLE ExceptionPort OPTIONAL ){ NTSTATUS Status = STATUS_SUCCESS; DbgPrint("have hook CreateProcess!\r\n"); Status = ulNtCreateProcessEx( ProcessHandle, DesiredAccess, ObjectAttributes, ParentProcess, InheritObjectTable, SectionHandle , DebugPort , ExceptionPort ); return Status;}VOID HOOKCreateProcess(){ ULONG ulSsdt = 0; ulSsdt = (ULONG)KeServiceDescriptorTable->ServiceTableBase; ulNtCreateProcessExAddr = ulSsdt + 0x30*4 ; //index is 0x30 ulNtCreateProcessEx = (NtCreateProcessEx)*(PULONG)ulNtCreateProcessExAddr; //real addr REMOVE_ONLY_READ(); *(PULONG)ulNtCreateProcessExAddr = (ULONG)MyNtCreateProcessEx; RESET_ONLY_READ();}NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath){// #ifdef DBG// __asm int 3// #endif DbgPrint("Hello from SSDTHook!\n"); DriverObject->DriverUnload = SSDTHookUnload; HOOKCreateProcess(); return STATUS_SUCCESS;}void SSDTHookUnload(IN PDRIVER_OBJECT DriverObject){ REMOVE_ONLY_READ(); //当时两边都写成ulNtCreateProcessExAddr 了。。。。 *(PULONG)ulNtCreateProcessExAddr = (ULONG)ulNtCreateProcessEx; RESET_ONLY_READ(); DbgPrint("Goodbye from SSDTHook!\n");}
show time
DeBugView输出结果
用Kernel Detective查看,发现已被修改
- HOOK钩子技术4 SSDT HOOK
- HOOK钩子技术5 SSDT Inline Hook
- Hook钩子技术
- hook(钩子)技术
- HOOK技术(钩子)
- Hook SSDT
- SSDT hook
- HOOK SSDT
- SSDT HOOK
- SSDT HOOK
- hook ssdt
- SSDT HOOK
- HOOK SSDT
- HooK SSDT
- SSDT HOOK
- SSDT Hook
- 钩子技术、HOOK技术应用
- Hook技术1:线程钩子
- java开发环境搭建
- [刷题]Minimum Path Sum
- leetcode 104 Maximum Depth of Binary Tree二叉树求深度
- Intent的详细介绍
- 编程之美---求数组的子数组之和的最大值
- HOOK钩子技术4 SSDT HOOK
- 322A - Ciel and Dancing 322B - Ciel and Flowers
- java swing基础和事件处理
- html5之新增标签学习
- 杭电ACM1000A + B Problem
- 三角蛇形填数
- #260 (div.1) A.Boredom
- 第九周 【项目5-方程也是类】
- 手机硬件开发之CPU