A、Shadow SSDT表基址定位
B、Shadow SSDT表结构
C、Shadow SSDT HOOK
课时:45
KeServiceDescriptorTable //系统描述符表 extern
KeServiceDescriptorTableShadow //影子系统描述符表 win32k.sys
bp win32k!NtUserPostMessage
bp win32k!NtUserShowWindow
bp win32k!NtUserFindWindow
bp win32k!NtUserDestroyWindow
bp win32k!NtUserPostMessage
Shadow SSDT表基址定位
系统只提供了KeServiceDescriptorTable导出
KeServiceDescriptorTableShadow是个未导出结构
dd poi(KeServiceDescriptorTable-0x40+0x10) //XP
RtlGetVersion
PsGetVersion
Windows 2000: dwMajorVersion = 5 dwMinorVersion = 0
Windows XP: dwMajorVersion = 5 dwMinorVersion = 1
Windows 2003: dwMajorVersion = 5 dwMinorVersion = 1
Windows Vista: dwMajorVersion = 6
Operating system Version number dwMajorVersion dwMinorVersion Other
Windows 7 6.1 6 1 OSVERSIONINFOEX.wProductType == VER_NT_WORKSTATION
Windows Server 2008 R2 6.1 6 1 OSVERSIONINFOEX.wProductType != VER_NT_WORKSTATION
Windows Server 2008 6.0 6 0 OSVERSIONINFOEX.wProductType != VER_NT_WORKSTATION
Windows Vista 6.0 6 0 OSVERSIONINFOEX.wProductType == VER_NT_WORKSTATION
Windows Server 2003 R2 5.2 5 2 GetSystemMetrics(SM_SERVERR2) != 0
Windows Home Server 5.2 5 2 OSVERSIONINFOEX.wSuiteMask & VER_SUITE_WH_SERVER
Windows Server 2003 5.2 5 2 GetSystemMetrics(SM_SERVERR2) == 0
Windows XP Professional x64 Edition 5.2 5 2 (OSVERSIONINFOEX.wProductType == VER_NT_WORKSTATION) && (SYSTEM_INFO.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_AMD64)
Windows XP 5.1 5 1 Not applicable
Windows 2000 5.0 5 0 Not applicable
DWORD Get_KeServiceDescriptorTableShadow_Addr()
{ DWORD KeServiceDescriptorTableShadow=0;
DWORD Version=GetVersion();
switch (Version )
{
case VERSION_2K:
KeServiceDescriptorTableShadow=(DWORD)KeServiceDescriptorTable+0xE0;
break;
case VERSION_2K3:
break;
case VERSION_XP:
KeServiceDescriptorTableShadow=(DWORD)KeServiceDescriptorTable-0x40;
break;
default:
break;
}
return KeServiceDescriptorTableShadow;
}
dd poi(KeServiceDescriptorTableShadow+10) ///SSDT Shadow Base
NtUserDestroyWindow //ZwTerminateProcess
NtUserFindWindowEx
NtUserSetWindowLong
NtUserPostMessage
NtUserGetForegroundWindow
#define VERSION_2K 50
#define VERSION_XP 51
#define VERSION_2K3 52
#define VERSION_XP64 52
#define VERSION_2K3_R2 52
#define VERSION_VISTA 60
#define VERSION_SERVER2008 60
#define VERSION_WIN7 61
#define VERSION_SERVER2008_R2 61
#pragma PAGECODE
DWORD GetVersion()
{ ULONG rtn=0;
ULONG MajorVersion,MinorVersion,BuildNumber;
PsGetVersion(&MajorVersion,&MinorVersion,&BuildNumber,NULL);//系统版本.参数1主版本,参数2副版本,参数3时间序号,参数4字串
rtn=MajorVersion;
rtn=rtn *10;
rtn+=MinorVersion; //主版本+副版本
return rtn;
}
#pragma PAGECODE
DWORD Get_KeServiceDescriptorTableShadow_Addr()
{ DWORD KeServiceDescriptorTableShadow=0;
DWORD Version=GetVersion();
switch (Version )
{
case VERSION_2K:
KeServiceDescriptorTableShadow=(DWORD)KeServiceDescriptorTable+0xE0;
break;
case VERSION_2K3:
break;
case VERSION_XP:
KeServiceDescriptorTableShadow=(DWORD)KeServiceDescriptorTable-0x40;//XP系统的影子描述表是-0X40
break;
default:
break;
}
return KeServiceDescriptorTableShadow;
}
#pragma PAGECODE
VOID Show_SSDTShadowList()
{
KdPrint(("Entry Show_SSDTShadowList \n"));
DWORD TableBase=Get_KeServiceDescriptorTableShadow_Addr();
TableBase=TableBase+0x10; //取表基址
DWORD TableCount=TableBase+8; //偏移8是数量
DWORD count=*((PDWORD)TableCount); //读出数量
KdPrint(("SSDT_Shadow Base=%x Count=%x\n",TableBase,count));
//__asm int 3
PDWORD CFun_Addr=PDWORD(TableBase);
CFun_Addr=PDWORD(*CFun_Addr);
for (DWORD i=0;i<count;i++) //以数量遍历出列表
{
KdPrint(("\n %d=%x\n",i,*CFun_Addr ));
CFun_Addr++;
//__asm int 3
}
}
HWND myh;
typedef BOOL (__stdcall *PNtUserDestroyWindow)(HWND hwnd);
//定义原有和当前地址
PNtUserDestroyWindow Old_NtUserDestroyWindow,Cur_NtUserDestroyWindow;
#pragma PAGECODE
BOOL __stdcall My_NtUserDestroyWindow(HWND h) //造假函数
{
KdPrint(("h=%x \n",h));
if (h==myh)
{
KdPrint(("被保护窗口h=%x \n",h)); //准备HOOK
return FALSE;
}else return Old_NtUserDestroyWindow(h); //放过保护
}
#pragma PAGECODE
VOID SSDT_HOOK_NtUserDestroyWindow() //355
{ KdPrint(("Entry Show_SSDTShadowList \n"));
DWORD TableBase=Get_KeServiceDescriptorTableShadow_Addr();
TableBase=TableBase+0x10; //列表基址
DWORD TableCount=TableBase+8; //函数数量
DWORD count=*((PDWORD)TableCount);
KdPrint(("SSDT_Shadow Base=%x Count=%x\n",TableBase,count));
//__asm int 3
PDWORD CFun_Addr=PDWORD(TableBase);
CFun_Addr=PDWORD(*CFun_Addr); //列表首
CFun_Addr+=355; //要HOOK地址
Old_NtUserDestroyWindow=(PNtUserDestroyWindow)(*CFun_Addr);//保存
KdPrint(("\n NtUserDestroyWindow当前地址=%x,%x \n",CFun_Addr,*CFun_Addr));
__asm //去掉页面保护
{
cli
mov eax,cr0
and eax,not 10000h //and eax,0FFFEFFFFh
mov cr0,eax
}
*CFun_Addr=(DWORD)(&My_NtUserDestroyWindow); //指向HOOK地址
KdPrint(("\n NtUserDestroyWindow HOOK后地址=%x,%x \n",CFun_Addr,*CFun_Addr));
__asm
{
mov eax, cr0
or eax, 10000h
mov cr0, eax
sti
}
}
#pragma PAGECODE
VOID SSDT_UNHOOK_NtUserDestroyWindow() //还原HOOK
{
KdPrint(("Entry Show_SSDTShadowList \n"));
DWORD TableBase=Get_KeServiceDescriptorTableShadow_Addr();
TableBase=TableBase+0x10;
DWORD TableCount=TableBase+8;
DWORD count=*((PDWORD)TableCount);
KdPrint(("SSDT_Shadow Base=%x Count=%x\n",TableBase,count));
//__asm int 3
PDWORD CFun_Addr=PDWORD(TableBase);
CFun_Addr=PDWORD(*CFun_Addr);
CFun_Addr+=355; //NtUserDestroyWindow偏移,DWORD每加为4
*CFun_Addr=(DWORD)(Old_NtUserDestroyWindow);
}
===========================================
笔记:
本节课又是基础理论课.先打开虚拟机,在WINDBG里看一下系统描述符表和影子描述表(教案),这两个表的前面一行都是相同的,第二行不同,我们通过第一行当作特征码来查找第二行;我们还有另外一个简单的办法,一般来讲在同一个操作系统中这两个表之间的偏移大小是相同的,所以直接计算即可,比如这里就是0X40大小.那么我们怎么查找里面数据呢?在WINDBG下输入命令
Dd poi(KeServiceDescriptorTable-0x40+0x10)
但是全是???? 我们必须通过下断bp win32k!NtUserPostMessage
,再重新载入符号,输入命令.reload ,断下来之后再用命令显示一下列表就出来了,这样就取出了影子表.
以28课代码为例,加入已经写好代码函数来遍历整个表,因为必须要在GUI里查找,所以必须要在EXE中进行传递参数.打开Kernel Detective查看一下列表.
将已写好的代码解释之后,讲到影子表是不能直接访问的,是属于system进程,而我们之所以能查出是在不同的EXE进程中访问的.写好代码后在虚拟机里进行测试,然后就遍历出了所有函数列表,对比一下667个函数全部正确.读取地址已经正确了,那么我们对其进行替换,这就是SSDT HOOK了,比如我们HOOK一下NtUserDestroyWindow函数,也就是索引号355.
老师又将代码进行详解.在SYS中与EXE进行连接,通过查找窗口传递进程ID在SYS中的myh进行保护,禁止关闭窗口.而当我们关闭EXE的时候则将保护卸载掉.在虚拟机中测试一下,记事本果然关不掉了,而当我们关掉自己的EXE时则记事本可以正确的关闭掉了,当然如果强行关闭也是可以的,因为那是另外一个函数.
这两个系统描述表一个是可以导出的,另一个是不可以导出的. |