SSDT-HOOK

栏目: 数据库 · 发布时间: 6年前

内容简介:SSDT-HOOK保护进程

SSDT-HOOK保护进程

实验环境: win7虚拟机


示例是对内核层进行SSDT-HOOK实现保护进程的功能。

一会儿会用到的API是 OpenProcess ,在3环也就是用户层调用此API它保存一些信息传入到0环内核层后实际调用的是 ZwOpenProcess 函数,所以先使用OD随意打开一个.exe可执行程序,然后在kernel32模块里面查找 OpenProcess 函数,经过2个jmp后进入下一层,找到一个call进入其中便是 ZwOpenProcess 的调用。

SSDT-HOOK

SSDT-HOOK

下面调用API代码才是我们需要注意的,每个API函数调用时在进入关键函数之前有一句汇编代码 mov eax,0xXX ,这是用eax保存一个调用号。

SSDT-HOOK

在进入内核层后,而每个调用号对应一个内核函数,为此Windows设计了一张表通过调用号作为序号,就能找到函数的地址。


Windows内核把这张函数地址表称为 : 系统服务描述表 (System Service Descriptor Table ) 简称SSDT。

SSDT-HOOK 例如根据之前截图知在调用 ZwOpenProcess时调 用号就是0xbe,那么我们如果通过调用号找到了函数地址,把这个地址替换成我们自己的函数,这样就能完成HOOK,类似于3环的 IAT-HOOK。

Windows 内核中设计了两张系统服务描述符表,一张表是上述所说的SSDT,它只保存非用户界面相关的系统服务(例如创建文件、创建进程等); 另一张表称为 ShadowSSDT ,它专门用于保存和用户界面相关的服务(例如创建窗口等),这两张表在内核中都使用了同一个结构体的表示:

//系统服务表
typedef struct _KSYSTEM_SERVICE_TABLE
{

PULONG ServiceTableBase; //函数地址表的首地址
PULONG ServiceCounterTableBase;//函数表中每个函数被调用的次数
ULONG NumberOfService; //服务函数的个数
ULONG ParamTableBase; //参数个数表首地址
}KSYSTEM_SERVICE_TABLE;

但实际上系统共有4个系统服务描述符,其中2个就是上述的2张表,另外2个没有被使用,可能是留着将来备用的。 他们用如下结构体表示:

//服务描述符
typedef struct _KSERVICE_TABLE_DESCRIPTOR
{

KSYSTEM_SERVICE_TABLE ntoskrnl;//ntoskrnl.exe的服务函数,即SSDT
KSYSTEM_SERVICE_TABLE win32k; //win32k.sys的服务函数,即ShadowSSDT
KSYSTEM_SERVICE_TABLE notUsed1;//暂时没用1
KSYSTEM_SERVICE_TABLE notUsed2;//暂时没用2
}KSERVICE_TABLE_DESCRIPTOR;

调用号

进入0环时调用号是eax传递的,但这个调用号并不只是一个普通的数字作为索引序号,系统会把他用32位数据表示,拆分成19: 1: 12的格式,如下:

分析一下0-11这低12位组成一个真正的索引号,第12位表示服务表号,13-31位没有使用。

而进入内核后调用哪一张表,就由调用号中的第12位决定,为0则调用SSDT表,为1则调用ShadowSSDT表。

想要对SSDT表进行HOOK,首先要找到SSDT表,可以通过服务表找到它,服务表存在于KTHREAD结构体的偏移为0xbc的一个字段中,可双机调试使用windbg查看,输入dt _kthread,然后再找到偏移为0xbc的字段

SSDT-HOOK

SSDT-HOOK

还是要说明一点KTHREAD这个结构体是未文档化的,所以里面的数据随着系统版本的不同各个字段偏移就有可能不同,所以使用前最好用windbg确认下所需要的字段偏移。

使用PsGetCurrentThread()函数可获取当前KTHREAD的首地址。

但是需要注意的是SSDT表所在的内存页属性是只读,没有写入的权限,所以需要把该地址设置为可写入,这样才能写入自己的函数。

我使用的是CR0寄存器关闭只读属性, 简单介绍下CR0寄存器:

SSDT-HOOK

可以看到这里使用32位寄存器,而在CR0寄存器中,我们重点关注的是3个标志位:

PE ­ 是否启用保护模式,置1则启用。

PG ­ 是否使用分页模式,置1则开启分页模式,此标志置1时,PE 标志也必须置1,否则CPU报异常。

WP ­ WP为1 时,不能修改只读的内存页;WP为0 时,可以修改只读的内存页。

所以在进行HOOK时,只要把CR0寄存器中的WP位置为0,就能对内存进行写入操作。

操作代码如下:

//关闭页只读保护
__asm
{
push eax;
mov eax, cr0;
and eax, ~0x10000;
mov cr0, eax;
pop eax;
ret;
}

当然使用完成后要把只读保护属性还回去,不然会引发不可预料的严重后果。

//开启页只读保护
__asm
{
push eax;
mov eax, cr0;
or eax, 0x10000;
mov cr0, eax;
pop eax;
ret;
}

有了以上分析的基础那么现在就可以写安装内核钩子的代码了:

//安装钩子
void InstallHook()
{
//1.获取KTHREAD
PETHREAD pNowThread = PsGetCurrentThread();
//2.获取ServiceTable表,该字段偏移为0xbc
g_pServiceTable = (KSERVICE_TABLE_DESCRIPTOR*)
(*(ULONG*)((ULONG)pNowThread + 0xbc));
//3.保存hook的旧的函数的地址,0xbe为ZwOpenProcess的调用号
g_OldZwOpenProcess = (FuZwOpenProcess)
g_pServiceTable->ntoskrnl.ServiceTableBase[0xbe];
//4.关闭页只读保护
ShutPageProtect();
//5.写入自己的函数到SSDT表内
g_pServiceTable->ntoskrnl.ServiceTableBase[0xbe]
= (ULONG)MyZwOpenProcess;
//6.开启页只读保护
OpenPageProtect();
}

看一下自己写的 MyZwOpenProcess 函数,通过对比PID找到要保护的进程,并且是以结束进程权限PROCESS_TERMINATE(0x1)访问时,则修改权限为0使其无法访问, 就达到了保护进程的目的。

//自写的函数
NTSTATUS NTAPI MyZwOpenProcess(
_Out_ PHANDLE ProcessHandle,
_In_ ACCESS_MASK DesiredAccess,
_In_ POBJECT_ATTRIBUTES ObjectAttributes,
_In_opt_ PCLIENT_ID ClientId
)
{
//当此进程为要保护的进程时,并且是以结束进程权限访问时
if (ClientId->UniqueProcess == (HANDLE)g_Pid &&
DesiredAccess == PROCESS_TERMINATE)
{
//设为拒绝访问
DesiredAccess = 0;
}
//调用原函数
return g_OldZwOpenProcess(
ProcessHandle,
DesiredAccess,
ObjectAttributes,
ClientId);
}

生成.sys文件后使用 工具 安装驱动服务,然后打开任务管理器,关闭被保护的进程,就可以看到拒绝访问,到此保护进程就成功了。 在这主要描述了 SSDT-HOOK,HOOK 的功能就可以去任意发挥了。

SSDT-HOOK

完整源码如下:

#include <ntifs.h>

//内核之SSDT-HOOK
//系统服务表
typedef struct _KSYSTEM_SERVICE_TABLE
{

PULONG ServiceTableBase; //函数地址表的首地址
PULONG ServiceCounterTableBase;//函数表中每个函数被调用的次数
ULONG NumberOfService; //服务函数的个数
ULONG ParamTableBase; //参数个数表首地址
}KSYSTEM_SERVICE_TABLE;

//服务描述符
typedef struct _KSERVICE_TABLE_DESCRIPTOR
{

KSYSTEM_SERVICE_TABLE ntoskrnl;//ntoskrnl.exe的服务函数,SSDT
KSYSTEM_SERVICE_TABLE win32k; //win32k.sys的服务函数,ShadowSSDT
KSYSTEM_SERVICE_TABLE notUsed1;//暂时没用1
KSYSTEM_SERVICE_TABLE notUsed2;//暂时没用2
}KSERVICE_TABLE_DESCRIPTOR;

//定义HOOK的函数的类型
typedef NTSTATUS (NTAPI*FuZwOpenProcess)(
_Out_ PHANDLE ProcessHandle,
_In_ ACCESS_MASK DesiredAccess,
_In_ POBJECT_ATTRIBUTES ObjectAttributes,
_In_opt_ PCLIENT_ID ClientId
)
;

//自写的函数声明
NTSTATUS NTAPI MyZwOpenProcess(
_Out_ PHANDLE ProcessHandle,
_In_ ACCESS_MASK DesiredAccess,
_In_ POBJECT_ATTRIBUTES ObjectAttributes,
_In_opt_ PCLIENT_ID ClientId
)
;

//记录系统的该函数
FuZwOpenProcess g_OldZwOpenProcess;
//服务描述符表指针
KSERVICE_TABLE_DESCRIPTOR* g_pServiceTable = NULL;
//要保护进程的ID
ULONG g_Pid = 9527;

//安装钩子
void InstallHook();
//卸载钩子
void UninstallHook();
//关闭页写入保护
void ShutPageProtect();
//开启页写入保护
void OpenPageProtect();

//卸载驱动
void OutLoad(DRIVER_OBJECT* obj);



////***驱动入口主函数***/
NTSTATUS DriverEntry(DRIVER_OBJECT* driver, UNICODE_STRING* path)
{
path;
KdPrint(("驱动启动成功!\n"));
//DbgBreakPoint();

//安装钩子
InstallHook();

driver->DriverUnload = OutLoad;
return STATUS_SUCCESS;
}

//卸载驱动
void OutLoad(DRIVER_OBJECT* obj)
{
obj;
//卸载钩子
UninstallHook();
}

//安装钩子
void InstallHook()
{
//1.获取KTHREAD
PETHREAD pNowThread = PsGetCurrentThread();
//2.获取ServiceTable表
g_pServiceTable = (KSERVICE_TABLE_DESCRIPTOR*)
(*(ULONG*)((ULONG)pNowThread + 0xbc));
//3.保存旧的函数
g_OldZwOpenProcess = (FuZwOpenProcess)
g_pServiceTable->ntoskrnl.ServiceTableBase[0xbe];
//4.关闭页只读保护
ShutPageProtect();
//5.写入自己的函数到SSDT表内
g_pServiceTable->ntoskrnl.ServiceTableBase[0xbe]
= (ULONG)MyZwOpenProcess;
//6.开启页只读保护
OpenPageProtect();
}

//卸载钩子
void UninstallHook()
{
//1.关闭页只读保护
ShutPageProtect();
//2.写入原来的函数到SSDT表内
g_pServiceTable->ntoskrnl.ServiceTableBase[0xbe]
= (ULONG)g_OldZwOpenProcess;
//3.开启页只读保护
OpenPageProtect();
}

//关闭页只读保护
void _declspec(naked) ShutPageProtect()
{
__asm
{
push eax;
mov eax, cr0;
and eax, ~0x10000;
mov cr0, eax;
pop eax;
ret;
}
}

//开启页只读保护
void _declspec(naked) OpenPageProtect()
{
__asm
{
push eax;
mov eax, cr0;
or eax, 0x10000;
mov cr0, eax;
pop eax;
ret;
}
}

//自写的函数
NTSTATUS NTAPI MyZwOpenProcess(
_Out_ PHANDLE ProcessHandle,
_In_ ACCESS_MASK DesiredAccess,
_In_ POBJECT_ATTRIBUTES ObjectAttributes,
_In_opt_ PCLIENT_ID ClientId
)

{
//当此进程为要保护的进程时
if (ClientId->UniqueProcess == (HANDLE)g_Pid &&
DesiredAccess == PROCESS_TERMINATE)
{
//设为拒绝访问
DesiredAccess = 0;
}
//调用原函数
return g_OldZwOpenProcess(
ProcessHandle,
DesiredAccess,
ObjectAttributes,
ClientId);
}

- End -

SSDT-HOOK

看雪ID: 九阳道人             

https://bbs.pediy.com/user-847228.htm

本文由看雪论坛  九阳道人   原创

转载请注明来自看雪社区

热门图书推荐

SSDT-HOOK   立即购买!

SSDT-HOOK

公众号ID:ikanxue

官方微博:看雪安全

商务合作:wsc@kanxue.com

点击下方“阅读原文”,查看更多干货


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Text Processing in Python

Text Processing in Python

David Mertz / Addison-Wesley Professional / 2003-6-12 / USD 54.99

Text Processing in Python describes techniques for manipulation of text using the Python programming language. At the broadest level, text processing is simply taking textual information and doing som......一起来看看 《Text Processing in Python》 这本书的介绍吧!

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具