标签:code sed 检查 服务 oid 原理 写入 disable write
通过08内核编程HOOK_KiFastCall.md可以知道,用户层的函数调用都会进入到0环, 0环将服务函数的地址实现保存在SSDT表中. KiFastCallEntry
函数会使用调用号找到函数的参数个数表和函数地址表, 并将用户栈的参数拷贝到内核栈,最后调用了系统服务表中的函数.
SSDT HOOK的原理很简单: 找到SSDT,将对应的函数地址进行替换,就完成HOOK了.
在进行HOOK的过程中,唯一需要注意的是: SSDT表是不可写的, 强行写入会产生内存访问异常, 但是也有方法使其变成可写:
关闭CR0
寄存器中的WP
位 这个位是用于控制是否开启页保护的, 当其被置1, CPU就会做写入检查, 当其置0,就不做检查.(02_寄存器.md)
使用MDL重新映射SSDT表的元素,这样也可以进行写入(04内核编程内核新概念.md)
#include <ntddk.h>
?
typedef struct _KSYSTEM_SERVICE_TABLE
{
PULONG ServiceTableBase; //函数地址表的首地址
PULONG ServiceCounterTableBase;// 函数表中每个函数被调用的次数
ULONG NumberOfService;// 服务函数的个数, NumberOfService * 4 就是整个地址表的大小
UCHAR* ParamTableBase; // 参数个数表首地址
} KSYSTEM_SERVICE_TABLE, *PKSYSTEM_SERVICE_TABLE;
?
typedef struct _KSERVICE_TABLE_DESCRIPTOR
{
KSYSTEM_SERVICE_TABLE ntoskrnl;// ntoskrnl.exe的服务函数,即SSDT
KSYSTEM_SERVICE_TABLE win32k; // win32k.sys的服务函数(GDI32.dll/User32.dll 的内核支持),即ShadowSSDT
KSYSTEM_SERVICE_TABLE notUsed1; // 不使用
KSYSTEM_SERVICE_TABLE notUsed2; // 不使用
}KSERVICE_TABLE_DESCRIPTOR, *PKSERVICE_TABLE_DESCRIPTOR;
typedef NTSTATUS(NTAPI*FnZwOpenProcess)(PHANDLE ProcessHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, PCLIENT_ID ClientId);
?
?
?
void OnUnLoad(DRIVER_OBJECT* driver);
?
void installHookSSDT();
void uninstallHook();
void disablePageWriteProtect();
void enablePageWriteProtect();
NTSTATUS NTAPI MyZwOpenProcess(PHANDLE ProcessHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, PCLIENT_ID ClientId);
?
?
FnZwOpenProcess g_oldZwOpenProcess; // 原始ZwOpenProcess函数
ULONG g_uPid; // 需要保护的进程ID, 这个PID可以通过内核通讯来修改.
KSERVICE_TABLE_DESCRIPTOR* g_pServiceTable = NULL;
?
NTSTATUS DriverEntry(DRIVER_OBJECT* driver, UNICODE_STRING* path)
{
?
DbgBreakPoint();
driver->DriverUnload = OnUnLoad;
?
// 安装HOOK
installHookSSDT();
?
return STATUS_SUCCESS;
}
?
void OnUnLoad(DRIVER_OBJECT* driver)
{
// 卸载HOOK
uninstallHook();
}
?
void installHookSSDT()
{
// 1. 找到SSDT表的首地址
// 1.1 在分析KiFastCallEntry时, 可以看到系统从KPCR中取出了当前线程对象
// 然后又从当前线程对象(KTHREAD)中取出了ServiceTable.因此,可以仿照
// 这个做法.
// 1.2 ServiceTable在KTHREAD的以下偏移:
// +0x0bc ServiceTable : Ptr32 Void
PETHREAD* pCurThread = PsGetCurrentThread();
?
g_pServiceTable = (KSERVICE_TABLE_DESCRIPTOR*)
(*(ULONG*)((ULONG_PTR)pCurThread + 0xBC));
?
// 2. 找到函数在表中的位置(其位置就是调用号.)
// 2.1 保存旧的函数地址(0xBE是ZwOpenProcess函数)
g_oldZwOpenProcess =
(FnZwOpenProcess)g_pServiceTable->ntoskrnl.ServiceTableBase[0xBE];
?
// 3. 将内存分页设置为可写
disablePageWriteProtect();
// 4. 写入新函数地址到SSDT表中
g_pServiceTable->ntoskrnl.ServiceTableBase[0xBE] = (PULONG)MyZwOpenProcess;
// 5. 将内存分页属性恢复不可写
enablePageWriteProtect();
}
?
void uninstallHook()
{
if (g_oldZwOpenProcess)
{
// 1. 将内存分页设置为可写
disablePageWriteProtect();
// 2. 写入新函数地址到SSDT表中
g_pServiceTable->ntoskrnl.ServiceTableBase[0xBE] = (PULONG)g_oldZwOpenProcess;
// 3. 将内存分页属性恢复不可写
enablePageWriteProtect();
}
}
?
NTSTATUS NTAPI MyZwOpenProcess(PHANDLE ProcessHandle,
ACCESS_MASK DesiredAccess,
POBJECT_ATTRIBUTES ObjectAttributes,
PCLIENT_ID ClientId)
{
if (ClientId->UniqueProcess == g_uPid)
{
DesiredAccess = 0; // 将访问权限置零
}
// 调用原始函数
return g_oldZwOpenProcess(ProcessHandle,
DesiredAccess,
ObjectAttributes,
ClientId);
}
?
// 关闭内存页写入保护
void _declspec(naked) disablePageWriteProtect()
{
_asm
{
push eax;
mov eax, cr0;
and eax, ~0x10000;
mov cr0, eax;
pop eax;
ret;
}
}
?
// 开启内存页写入保护
void _declspec(naked) enablePageWriteProtect()
{
_asm
{
push eax;
mov eax, cr0;
or eax, 0x10000;
mov cr0, eax;
pop eax;
ret;
}
}
标签:code sed 检查 服务 oid 原理 写入 disable write
原文地址:https://www.cnblogs.com/ltyandy/p/11439441.html