前言:
使用vs自带的开发人员工具,可以使用dumpbin命令,查看pe文件的导入表等信息 :
命令的意思:
使用dumpbin把 ntoskrnl.exe中的导出表 复制到1.txt中
SSDT:system service descriptor table 系统服务描述符表
1.什么是SSDT
网上也有一篇博客讲的不错:
SSDT详解
a.Windows系统中的内核执行层是ntoskrnl.exe
i.不同的版本,ntoskrnl.exe可能是不一样的
ii.这个文档说明都是在win7 sp1 32位下的ntoskrnl.exe
b.windows中,有4张系统服务表,机构体如下:
typedef _KSERVICE_TABLE_DESXRIPTOR{
KSYSTEM_SERVICE_TABLE ntoskrnl;//指向SSDT
KSYSTEM_SERVICE_TABLE win32k; //shadowSSDT
KSYSTEM_SERVICE_TABLE notUsed1;
KSYSTEM_SERVICE_TABLE notUsd2;
}KSERVICRE_TABLE_DESCRIPTOR;
//在KiFastEntry的时候,会判断是ssdt 还是shadowSSD
c.在内核执行层中,有2张服务表,分别叫做:
i.KeServiceDescriptor Table :这就是SSDT的表
ii.KeServiceDescriptorTableShadow:这是SSDTShadow表
iii.kernel32.dll中的函数,都是由SSDT这张表来处理
iv.SSDTShadow这张表主要是用来处理user32.dll和gdi32.dll的函数
d.不管是KeServiceDescriptor Table 还是KeServiceDescriptorTableShadow,它们所指向的结构体都是相同的;
typedef struct _KESERVICE_DESCRIPTOR_Table
{
PULONG ServiceTableBase;//SSDT或者shadowSSDT的基址
PULONG ServiceCounterTableBase;//用户check builds,包含ssdt中每个服务被调用的次数
ULONG NumberOfServices;//服务函数的个数,NumberOfServices*4就是整个地址表的大小
PUCHAR ParamTableBase;//SSPT(system service Parameter table),参数表的地址
}KESERVICE_DESCRIPTOR_TABLE, *PKESERVICE_DESCRIPTOR_TABLE;
2.怎么找SSDT
a.以OpenProcess为例:
i.进入OD随便打开一个程序
ii.ctrl+g 找到OpenProcess这个函数的位置
iii.进入OpenProcess 可以发现,会调用ntdll中的ZwOpenProcess,点击enter进去
iv.以发现,mov eax,0xBE ,0xBE这就是SSDT的索引号 0xbe = 190d
通过PCHUNTER可以看一下 :
V.确实可以看出,SSDT这表中,下标为0xBE处的确是 NtOpenProcess
然后这个call 应该就是进入sysenter 了,od已经跟不进去了
b.首先要知道,这个调用号本身的结构
并未使用 | 服务表号 | 索引号 |
---|---|---|
23位 | 1位 | 12位 |
i.服务表号就是SSDT表还是shadowssdt表
0 | SSDT |
---|---|
1 | shadowSSDT |
去看一下ssdt,因为shadow ssdt 之后再有GUI的线程中才可以看到
windbg kernel debug 挂上本机就行:
lkd> dd nt!KeServiceDescriptorTable
81d869c0 81c8d6f0 00000000 00000191 81c8dd38
81d869d0 00000000 00000000 00000000 00000000
81d869e0 81cf9493 00000000 02224c74 000000bb
81d869f0 00000011 00000100 5385d2ba d717548f
81d86a00 81c8d6f0 00000000 00000191 81c8dd38
81d86a10 97715000 00000000 00000339 9771602c
81d86a20 00000000 00000000 81d86a24 00000240
81d86a30 00000240 835ad7e0 00000003 00000000
通过Windbg可以看出,第一行81c8d6f0 00000000 00000191 81c8dd38
这一行对应的就是KESERVICE_DESCRIPTOR_TABLE这个结构体 ;
0xbe 就是ZwOpenProcess在SSDT表中的索引 :
lkd> dd 81c8d6f0 +0xbe*4
81c8d9e8 81e9f531 81e5a8f1 81e5a03e 81d9bdc0
81c8d9f8 86ba4804 81e023ca 81e21cc8 81e9c48a
81c8da08 81e9de88 81e5a155 81e59c29 81f26edb
81c8da18 81f0f879 81f10b11 81e06231 81e577fc
81c8da28 81f0f42a 81f0f14a 81f0f4e2 81f0f202
81c8da38 81e1905c 81de19ba 81dfc03f 81f1126c
81c8da48 81f11332 86ba3670 81eb4c3c 81e80b48
81c8da58 81f21afa 81f21f3d 81ce0c6a 81ea2473
然后查看这个地址 :
lkd> u 81e9f531
nt!NtOpenProcess:
81e9f531 8bff mov edi,edi
81e9f533 55 push ebp
81e9f534 8bec mov ebp,esp
81e9f536 51 push ecx
81e9f537 51 push ecx
81e9f538 64a124010000 mov eax,dword ptr fs:[00000124h]
81e9f53e 8a803a010000 mov al,byte ptr [eax+13Ah]
81e9f544 8b4d14 mov ecx,dword ptr [ebp+14h]
这个就是OpenProcess在内核中的函数 ;
3.代码
#include <ntifs.h>
//////////////////////////////////////////////////////////////////////////
// hook 目标
// ZwOpenProcess
//定义给一个 字节定义的
typedef NTSTATUS(NTAPI *ZWOPENPROCESS)(
_Out_ PHANDLE ProcessHandle,
_In_ ACCESS_MASK DesiredAccess,
_In_ POBJECT_ATTRIBUTES ObjectAttributes,
_In_opt_ PCLIENT_ID ClientId
);
ZWOPENPROCESS g_OldOpenProcess = 0;//老的openProcess的函数地址
ULONG g_NeedToProtectProcessID = 0;
// 定义一个我们自己的OpenProcess
NTSTATUS NTAPI MyOpenProcess(
_Out_ PHANDLE ProcessHandle,
_In_ ACCESS_MASK DesiredAccess,
_In_ POBJECT_ATTRIBUTES ObjectAttributes,
_In_opt_ PCLIENT_ID ClientId
)
{
DbgPrint("Hook Sucess\n");
if (ClientId->UniqueProcess == g_NeedToProtectProcessID)
{
//如果判断打开的是我们保护的进程
//直接把权限改成0
DesiredAccess = 0;
}
//让旧的函数去处理
return g_OldOpenProcess(ProcessHandle, DesiredAccess, ObjectAttributes, ClientId);
}
#pragma pack(1) //设置对齐力度,这里是以1个字节的力度对齐
typedef struct _ServiceDesriptorEntry
{
ULONG * ServiceTableBase; //服务表基地址
ULONG * ServiceCounterTableBase;//计数表基址
ULONG NumberOfServices; //表中项的个数
UCHAR* ParamTableBase; //参数表基址
}SSDTENTRY,*PSSDTENTRY;
#pragma pack()
//导入SSDT
// 32位下,SSDT直接导入就可以用
// 64位就不行,没有导出,而且服务表也改变了,里面存的不是地址
// 而是偏移
NTSYSAPI SSDTENTRY KeServiceDescriptorTable;
//恢复页保护
void OnProtect(){
_asm{
push eax;
mov eax, CR0; //CR0 寄存器主要是跟页的属性的有关
or eax,0x10000;
mov CR0, eax;
pop eax;
}
}
//关掉页保护
void OffProtect(){
_asm{
push eax;
mov eax, CR0;
and eax, ~0x10000;
mov CR0, eax;
pop eax;
}
}
//开始hook函数
void OnHook(){
g_OldOpenProcess = (ZWOPENPROCESS)KeServiceDescriptorTable.ServiceTableBase[190];//获取OpenProcess的函数地址
OffProtect();//关闭保护
KeServiceDescriptorTable.ServiceTableBase[190] = (ULONG)MyOpenProcess;//强制更改一波
OnProtect();
}
void offHook(){
OffProtect();
KeServiceDescriptorTable.ServiceTableBase[190] = g_OldOpenProcess;
OnProtect();
}
//驱动卸载函数
void DriverUnload(PDRIVER_OBJECT pDriver){
offHook();
UNREFERENCED_PARAMETER(pDriver);
}
NTSTATUS DriverEntry(PDRIVER_OBJECT pDirver, PUNICODE_STRING path){
UNREFERENCED_PARAMETER(path);
_asm int 3;
DbgPrint("entry driver\n");
g_NeedToProtectProcessID = 1456;//这里手动设置了需要保护的ID,最好遍历出来
OnHook();
pDirver->DriverUnload = DriverUnload;
return STATUS_SUCCESS;
}