SSDT-HOOK

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

内容简介: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

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


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

查看所有标签

猜你喜欢:

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

编程语言实现模式

编程语言实现模式

Terence Parr / 李袁奎、尧飘海 / 华中科技大学出版社 / 2012-3-20 / 72.00元

《编程语言实现模式》旨在传授开发语言应用(工具)的经验和理念,帮助读者构建自己的语言应用。这里的语言应用并非特指用编译器或解释器实现编程语言,而是泛指任何处理、分析、翻译输入文件的程序,比如配置文件读取器、数据读取器、模型驱动的代码生成器、源码到源码的翻译器、源码分析工具、解释器,以及诸如此类的工具。为此,作者举例讲解已有语言应用的工作机制,拆解、归纳出31种易于理解且常用的设计模式(每种都包括通......一起来看看 《编程语言实现模式》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具