当前位置: 首页 > article >正文

SSDT Hook

SSDT表

系统服务描述符表

这里以Windows XP SP3为例

kd> dd KeServiceDescriptorTable
80553fa0  80502b8c 00000000 0000011c 80503000
80553fb0  00000000 00000000 00000000 00000000
80553fc0  00000000 00000000 00000000 00000000
80553fd0  00000000 00000000 00000000 00000000
80553fe0  00002710 bf80c0b6 00000000 00000000
80553ff0  f8b7ba80 f82ffb60 820808f8 806e2f40
80554000  00000000 00000000 00000000 00000000
80554010  28728ec0 01db120e 00000000 00000000

第一个参数指向的是内核函数存储的首地址

kd> dd 80502b8c
ReadVirtual: 80502b8c not properly sign extended
80502b8c  8059a948 805e7db6 805eb5fc 805e7de8
80502b9c  805eb636 805e7e1e 805eb67a 805eb6be
80502bac  8060cdfe 8060db50 805e31b4 805e2e0c
80502bbc  805cbde6 805cbd96 8060d424 805ac5ae
80502bcc  8060ca3c 8059edbe 805a6a00 805cd8c4
80502bdc  80500828 8060db42 8056ccd6 8053600e
80502bec  806060d4 805b2c3a 805ebb36 8061ae56
80502bfc  805f0028 8059b036 8061b0aa 8059a8e8

第三个参数11c(284)代表的是SSDT表有多少个内核函数

第四个参数指向的是一个地址,这个地址表示的内核函数相对应的参数个数

kd> dd 80503000
ReadVirtual: 80503000 not properly sign extended
80503000  2c2c2018 44402c40 1818080c 0c040408
80503010  08081810 0808040c 080c0404 2004040c
80503020  140c1008 0c102c0c 10201c0c 20141038
80503030  141c2424 34102010 080c0814 04040404
80503040  0428080c 1808181c 1808180c 040c080c
80503050  100c0010 10080828 0c08041c 00081004
80503060  0c080408 10040828 0c0c0404 28240428
80503070  0c0c0c30 0c0c0c18 0c10300c 0c0c0c10

比如0x2c = 44,44/4 = 11个参数

分析

我们来看一个函数OpenProcess是具体怎么调用的

tips:最好是什么编译环境,就在什么环境分析,xp上编的在xp上分析,win10上编的在win10上分析

可以看见,OpenProcess也是调用了kernel32.dll

在这里插入图片描述

kernel32.dll里面看见了syscall的调用,调用号是0x7A
在这里插入图片描述

这时候找到SSDT表

kd> dd KeServiceDescriptorTable
80553fa0  80502b8c 00000000 0000011c 80503000
80553fb0  00000000 00000000 00000000 00000000
80553fc0  00000000 00000000 00000000 00000000
80553fd0  00000000 00000000 00000000 00000000
80553fe0  00002710 bf80c0b6 00000000 00000000
80553ff0  f8b67a80 f82ffb60 81be2928 806e2f40
80554000  00000000 00000000 00000000 00000000
80554010  08909a40 01db1211 00000000 00000000
kd> dd 80502b8c+7a*4
ReadVirtual: 80502d74 not properly sign extended
80502d74  805c2296 805e49fc 805e4660 805a0722
80502d84  8060c254 805ba77a 805c2522 805e4a1a
80502d94  805e47d0 8060e1b0 8063cc78 805c0346
80502da4  805eedce 805eaa16 805eac02 805aea08
80502db4  806062dc 8056d0ce 8060db50 8060db50
80502dc4  8053d02e 80607e68 80608ac8 80570074
80502dd4  805b4de0 805703ca 806063a4 8056d222
80502de4  8060d2dc 80570c46 805ccee0 8059b6fc

在这里0x805c2296就是我们要找的函数调用表的首地址,进去看个伪代码

kd> u 805c2296 L50
805c2296 68c4000000      push    0C4h
805c229b 68a8aa4d80      push    offset nt!ObWatchHandles+0x25c (804daaa8)
805c22a0 e86b6cf7ff      call    nt!_SEH_prolog (80538f10)
805c22a5 33f6            xor     esi,esi
805c22a7 8975d4          mov     dword ptr [ebp-2Ch],esi
805c22aa 33c0            xor     eax,eax
805c22ac 8d7dd8          lea     edi,[ebp-28h]
805c22af ab              stos    dword ptr es:[edi]
805c22b0 64a124010000    mov     eax,dword ptr fs:[00000124h]
805c22b6 8a8040010000    mov     al,byte ptr [eax+140h]
805c22bc 8845cc          mov     byte ptr [ebp-34h],al
805c22bf 84c0            test    al,al
805c22c1 0f848f000000    je      nt!NtOpenProcess+0xc0 (805c2356)
805c22c7 8975fc          mov     dword ptr [ebp-4],esi
805c22ca a1d4995580      mov     eax,dword ptr [nt!MmUserProbeAddress (805599d4)]
805c22cf 8b4d08          mov     ecx,dword ptr [ebp+8]
805c22d2 3bc8            cmp     ecx,eax
805c22d4 7202            jb      nt!NtOpenProcess+0x42 (805c22d8)
805c22d6 8930            mov     dword ptr [eax],esi
805c22d8 8b01            mov     eax,dword ptr [ecx]
805c22da 8901            mov     dword ptr [ecx],eax
805c22dc 8b5d10          mov     ebx,dword ptr [ebp+10h]
805c22df f6c303          test    bl,3
805c22e2 7405            je      nt!NtOpenProcess+0x53 (805c22e9)
805c22e4 e8d5970400      call    nt!ExRaiseDatatypeMisalignment (8060babe)
805c22e9 a1d4995580      mov     eax,dword ptr [nt!MmUserProbeAddress (805599d4)]
805c22ee 3bd8            cmp     ebx,eax
805c22f0 7207            jb      nt!NtOpenProcess+0x63 (805c22f9)
805c22f2 8930            mov     dword ptr [eax],esi
805c22f4 a1d4995580      mov     eax,dword ptr [nt!MmUserProbeAddress (805599d4)]
805c22f9 397308          cmp     dword ptr [ebx+8],esi
805c22fc 0f9545e6        setne   byte ptr [ebp-1Ah]
805c2300 8b4b0c          mov     ecx,dword ptr [ebx+0Ch]
805c2303 894dc8          mov     dword ptr [ebp-38h],ecx
805c2306 8b4d14          mov     ecx,dword ptr [ebp+14h]

KeServiceDescriptorTable和KeServiceDescriptorTableShadow

在 NT 4.0 以上的 Windows 操作系统中,默认就存在两个系统服务描述表,这两个调度表对应了两类不 同的系统服务,这两个调度表为: KeServiceDescriptorTableKeServiceDescriptorTableShadow ,其中 KeServiceDescriptorTable 主要是处理来自 ring3 层 kernel32.dll 中的系统调用,而 KeServiceDescriptorTableShadow 则主要处理来自 User32.dll 和 GDI32.dll 中的系统调用,并且 KeServiceDescriptorTable 在 ntoskrnl.exe 是导出的,而 KeServiceDescriptorTableShadow 则是没有被 Windows 操作系统所导出

SSDT表的结构

typedef struct _KSERVICE_TABLE_DESCRIPTOR
{
KSYSTEM_SERVICE_TABLE ntoskrnl; // ntoskrnl.exe 的服务函数
KSYSTEM_SERVICE_TABLE win32k; // win32k.sys 的服务函数(GDI32.dll/User32.dll 的
内核支持)
KSYSTEM_SERVICE_TABLE notUsed1;
KSYSTEM_SERVICE_TABLE notUsed2;
} KSERVICE_TABLE_DESCRIPTOR, * PKSERVICE_TABLE_DESCRIPTOR;

其中每一项又是一个结构体KSYSTEM_SERVICE_TABLE,通过结构体表示为如下,也是我们用指令dd KeServiceDescriptorTable看到的内容

typedef struct _KSYSTEM_SERVICE_TABLE
{
PULONG ServiceTableBase; // SSDT (System Service Dispatch Table)的基地址
PULONG ServiceCounterTableBase; // 用于 checked builds, 包含 SSDT 中每个服务被调
用的次数
ULONG NumberOfService; // 服务函数的个数, NumberOfService * 4 就是整个地址表的
大小
ULONG ParamTableBase; // SSPT(System Service Parameter Table)的基地址
} KSYSTEM_SERVICE_TABLE, * PKSYSTEM_SERVICE_TABLE;

系统调用

由于三环和零环有数据需要交互,所以在Windows中,在User层和Kernel层分别定义了一个_KUSER_SHARED_DATA结构区域,用于User层和Kernel层共享某些数据,它们使用固定的地址值映射,

在32位系统中

User 层地址为:0x7ffe0000
Kernel 层地址为:0xffdf0000

在内核中,首先在0x300偏移处看见了例如SystemCall的调用地址

kd>  dt _KUSER_SHARED_DATA 0xffdf0000
nt!_KUSER_SHARED_DATA
   +0x000 TickCountLow     : 0x974
   +0x004 TickCountMultiplier : 0xfa00000
   +0x008 InterruptTime    : _KSYSTEM_TIME
   +0x014 SystemTime       : _KSYSTEM_TIME
   +0x020 TimeZoneBias     : _KSYSTEM_TIME
   +0x02c ImageNumberLow   : 0x14c
   +0x02e ImageNumberHigh  : 0x14c
   +0x030 NtSystemRoot     : [260] 0x43
   +0x238 MaxStackTraceDepth : 0
   +0x23c CryptoExponent   : 0
............................................
   +0x300 SystemCall       : 0x7c92e4f0
   +0x304 SystemCallReturn : 0x7c92e4f4
   +0x308 SystemCallPad    : [3] 0
   +0x320 TickCount        : _KSYSTEM_TIME
   +0x320 TickCountQuad    : 0
   +0x330 Cookie           : 0x820b9fac

在用户层中,我们也可以展开来看

kd> dt _KUSER_SHARED_DATA 0x7ffe0000
nt!_KUSER_SHARED_DATA
   +0x000 TickCountLow     : 0x974
   +0x004 TickCountMultiplier : 0xfa00000
   +0x008 InterruptTime    : _KSYSTEM_TIME
   +0x014 SystemTime       : _KSYSTEM_TIME
   +0x020 TimeZoneBias     : _KSYSTEM_TIME
   +0x02c ImageNumberLow   : 0x14c
   +0x02e ImageNumberHigh  : 0x14c
   +0x030 NtSystemRoot     : [260] 0x43
   +0x238 MaxStackTraceDepth : 0
   +0x23c CryptoExponent   : 0
   +0x240 TimeZoneId       : 0
   +0x244 Reserved2        : [8] 0
   +0x264 NtProductType    : 1 ( NtProductWinNt )
   +0x268 ProductTypeIsValid : 0x1 ''
   +0x26c NtMajorVersion   : 5
   +0x270 NtMinorVersion   : 1
   +0x274 ProcessorFeatures : [64]  ""
   +0x2b4 Reserved1        : 0x7ffeffff
   +0x2b8 Reserved3        : 0x80000000
   +0x2bc TimeSlip         : 0
   +0x2c0 AlternativeArchitecture : 0 ( StandardDesign )
   +0x2c8 SystemExpirationDate : _LARGE_INTEGER 0x0
   +0x2d0 SuiteMask        : 0x110
   +0x2d4 KdDebuggerEnabled : 0x3 ''
   +0x2d5 NXSupportPolicy  : 0x2 ''
   +0x2d8 ActiveConsoleId  : 0
   +0x2dc DismountCount    : 0
   +0x2e0 ComPlusPackage   : 0xffffffff
   +0x2e4 LastSystemRITEventTickCount : 0
   +0x2e8 NumberOfPhysicalPages : 0x1ff6c
   +0x2ec SafeBootMode     : 0 ''
   +0x2f0 TraceLogging     : 0
   +0x2f8 TestRetInstruction : 0xc3
   +0x300 SystemCall       : 0x7c92e4f0
   +0x304 SystemCallReturn : 0x7c92e4f4
   +0x308 SystemCallPad    : [3] 0
   +0x320 TickCount        : _KSYSTEM_TIME
   +0x320 TickCountQuad    : 0
   +0x330 Cookie           : 0x820b9fac

可以看见几个相似偏移的地址是相同的,证明这些映射的物理页也是相同的

也可以用别的指令看看,结论是相同的

kd> dd 0x7ffe0000
7ffe0000  0001e3a9 0fa00000 8122bb6a 00000004
7ffe0010  00000004 1af24bac 01db1269 01db1269
7ffe0020  f1dcc000 ffffffbc ffffffbc 014c014c
7ffe0030  003a0043 0057005c 004e0049 004f0044
7ffe0040  00530057 00000000 00000000 00000000
7ffe0050  00000000 00000000 00000000 00000000
7ffe0060  00000000 00000000 00000000 00000000
7ffe0070  00000000 00000000 00000000 00000000

kd> dd 0xffdf0000
ReadVirtual: ffdf0000 not properly sign extended
ffdf0000  0001e3a9 0fa00000 8122bb6a 00000004
ffdf0010  00000004 1af24bac 01db1269 01db1269
ffdf0020  f1dcc000 ffffffbc ffffffbc 014c014c
ffdf0030  003a0043 0057005c 004e0049 004f0044
ffdf0040  00530057 00000000 00000000 00000000
ffdf0050  00000000 00000000 00000000 00000000
ffdf0060  00000000 00000000 00000000 00000000
ffdf0070  00000000 00000000 00000000 00000000

虽然指向的是同一个物理页,但是在User层是只读的,在Kernel层是可写的

Cr3寄存器

CR3是一个控制寄存器,该寄存器内保存有页目录表物理地址,其实CR3内部存放的就是页目录表的内
存基地址,运用CR3切换可实现对特定进程内存地址的强制读写操作

Cr3寄存器不同于其他寄存器,在所有的寄存器中,只有Cr3寄存器存储的地址是物理地址,其他寄存器 存储的都是线性地址

Cr3寄存器所存储的物理地址指向了一个页目录表(Page-Directory Table,PDT),在Windows中,一个页的大小通常位4KB,即一个页(页目录表)可以存储1024个页目录表项(PDE)

同样,第二级为页表(PTT),每个页表的大小为4kb,即一个页表可以存储1024个页表项(PTE)

在这里插入图片描述

PDE&PTE

在这里插入图片描述

通过上面这张图我们可以看见,在PDE或者PTE中,只有0位置为1,那么整个表才有效,1位,2位分别是读写位和特权位,分别表示属性可读可写和是否是特权用户

查看PDE和PTE

kd> !process 0 0
**** NT ACTIVE PROCESS DUMP ****
Failed to get VadRoot
PROCESS 821b9830  SessionId: none  Cid: 0004    Peb: 00000000  ParentCid: 0000
    DirBase: 00aff000  ObjectTable: e1000cf8  HandleCount: 243.
    Image: System

Failed to get VadRoot
PROCESS 82063360  SessionId: none  Cid: 0170    Peb: 7ffd6000  ParentCid: 0004
    DirBase: 08fc0020  ObjectTable: e13e1510  HandleCount:  19.
    Image: smss.exe

这里我们看见了DirBase是09580020,开个用户层看看,这里PTE属性最后是5,也就是二进制的0101,R/W位为0,则属性为可读

kd> !vtop 08fc0020 0x7ffe0000
X86VtoP: Virt 000000007ffe0000, pagedir 0000000008fc0020
X86VtoP: PAE PDPE 0000000008fc0028 - 0000000008d2f001
X86VtoP: PAE PDE 0000000008d2fff8 - 0000000008cfa067
X86VtoP: PAE PTE 0000000008cfaf00 - 8000000000041025
X86VtoP: PAE Mapped phys 0000000000041000
Virtual address 7ffe0000 translates to physical address 41000.

然后再开一个内核层的看看,PTE的最后的属性是3,即为0011,R/W位为1,可读可写

kd> !vtop 08fc0020 0xffdf0000
X86VtoP: Virt 00000000ffdf0000, pagedir 0000000008fc0020
X86VtoP: PAE PDPE 0000000008fc0038 - 0000000008d2d001
X86VtoP: PAE PDE 0000000008d2dff0 - 0000000000b10163
X86VtoP: PAE PTE 0000000000b10f80 - 0000000000041163
X86VtoP: PAE Mapped phys 0000000000041000
Virtual address ffdf0000 translates to physical address 41000.

调用原理

在我们了解了KUSER_SHARED_DATA结构体后,就可以知道call的实际上是 Systemcall 的地址,通过反 汇编查看,通过 sysenter 指令(快速调用)进入0环。操作系统会在系统启动的时候在 KUSER_SHARED_DATA 结构体的+300的位置,写入一个函数,这个函数就是 KiFastSystemCall 或者 KiIntSystemCall,我们可以看见SystemCall

kd> dt _KUSER_SHARED_DATA 0xffdf0000
nt!_KUSER_SHARED_DATA
   +0x000 TickCountLow     : 0x10859
   +0x004 TickCountMultiplier : 0xfa00000
..............................................
   +0x300 SystemCall       : 0x7c92e4f0
   +0x304 SystemCallReturn : 0x7c92e4f4
   +0x308 SystemCallPad    : [3] 0
   +0x320 TickCount        : _KSYSTEM_TIME
   +0x320 TickCountQuad    : 0
   +0x330 Cookie           : 0xbd8441e5

我们可以看见这个SystemCall的地址是0x7c92e4f0

Windows中三环进零环需要我们有换栈,换寄存器这些操作

在进入0环之前,需要我们更改CS,SS,ESP,EIP四个寄存器

CS的权限由3变为0 意味着需要新的CS
SS与CS的权限永远一致 需要新的SS
权限发生切换的时候,堆栈也一定会切换,需要新的ESP
进0环后代码的位置,需要EIP

如果通过中断门(int 2e)进0环,需要的CS、EIP在IDT表中,需要查内存(SS与 ESP由TSS提供)

而CPU如果支持sysenter指令时,操作系统会提前将CS/SS/ESP/EIP的值存储在MSR寄存器中,sysenter 指令执行时,**CPU会将MSR寄存器中的值直接写入相关寄存器,没有读内存的过程,**所以叫快速调用

API通过中断门进0环: 固定中断号为0x2E CS/EIP由门描述符提供 ESP/SS由TSS提供 进入0环后执行的内核函数:NT!KiSystemService

API通过sysenter指令进0环: CS/ESP/EIP由MSR寄存器提供(SS是算出来的) 进入0环后执行的内核函数:NT!KiFastCallEntry

SSDT hook

上面我们提到过

typedef struct _KSERVICE_TABLE_DESCRIPTOR
{
KSYSTEM_SERVICE_TABLE ntoskrnl; // ntoskrnl.exe 的服务函数
KSYSTEM_SERVICE_TABLE win32k; // win32k.sys 的服务函数(GDI32.dll/User32.dll 的
内核支持)
KSYSTEM_SERVICE_TABLE notUsed1;
KSYSTEM_SERVICE_TABLE notUsed2;
} KSERVICE_TABLE_DESCRIPTOR, * PKSERVICE_TABLE_DESCRIPTOR;

对于其中的每一项,又是一个结构体,代码如下

typedef struct _KSYSTEM_SERVICE_TABLE
{
PULONG ServiceTableBase; // SSDT (System Service Dispatch Table)的基地址
PULONG ServiceCounterTableBase; // 用于 checked builds, 包含 SSDT 中每个
服务被调用的次数
ULONG NumberOfService; // 服务函数的个数, NumberOfService * 4 就是整个地址表的
大小
ULONG ParamTableBase; // SSPT(System Service Parameter Table)的基地址
} KSYSTEM_SERVICE_TABLE, * PKSYSTEM_SERVICE_TABLE;

Cr4寄存器

在我们的函数准备好之后,需要我们将该函数的指针覆盖原来的NtOpenProcess指针

我们知道物理页的内存的R/W位的属性是由PDE和PTE相与来的,那么我们就可以,通过改变SSDT对应的PDE和PTE的属性,将其设置成为可读可写的

这里首先我们用Cr4寄存器判断是2-9-9-12分页还是10-10-12分页

if(RCR4 & 0x00000020)
{//说明是2-9-9-12分页
KdPrint(("2-9-9-12分页 %p\n",RCR4));
KdPrint(("PTE1 %p\n",*(DWORD*)(0xC0000000 + ((HookFunAddr >> 9)
&0x007FFFF8))));
*(DWORD64*)(0xC0000000 + ((HookFunAddr >> 9) & 0x007FFFF8)) |= 0x02;
KdPrint(("PTE1 %p\n",*(DWORD*)(0xC0000000 + ((HookFunAddr >> 9)
&0x007FFFF8))));
}
else
{//说明是10-10-12分页
KdPrint(("10-10-12分页\n"));
KdPrint(("PTE1 %p\n",*(DWORD*)(0xC0000000 + ((HookFunAddr >> 10) &
0x003FFFFC))));
*(DWORD*)(0xC0000000 + ((HookFunAddr >> 10) & 0x003FFFFC)) |= 0x02;
KdPrint(("PTE2 %p\n",*(DWORD*)(0xC0000000 + ((HookFunAddr >> 10)
&0x003FFFFC))));
}

Cr0寄存器

我们可以使用PsGetCurrentThread函数来获取当前KTHREAD的首地址,但是SSDT表所在的内存页的属性是只读,没有写入的权限,所以需要吧地址设置为可写入,这里使用Cr0来关闭寄存器的只读属性

在这里插入图片描述

在这个寄存器中,重点关注三个标志位

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; // 与0x10000相与后取反
mov cr0, eax;
pop eax;
ret;
}
//开启页只读保护
__asm
{
push eax;
mov eax, cr0;
or eax, 0x10000;
mov cr0, eax;
pop eax;
ret;
}

demo(x86未验证)

#include<ntddk.h>
#include<ntstatus.h>

//记录函数的地址
ULONG uOldNtOpenProcess;

//内核之SSDT-HOOK
//系统服务表
typedef struct _KSYSTEM_SERVICE_TABLE {
	PULONG ServiceTableBase;  //函数地址表中的首地址
	PULONG ServiceCounterTableBase; //函数表中每个函数被调用的次数
	ULONG NumberOfService;    //服务函数的个数
	ULONG ParamTableBsse;     //参数个数表的首地址
} KSYSTEM_SERVICE_TABLE,* PSSYSTEM_SERVICE_TABLE;

//服务描述符
typedef struct _KSYSTEM_TABLE_DESCRIPTOR {
	KSYSTEM_SERVICE_TABLE ntoskrnl;//ntoskrnl.exe的服务函数,SSDT
	KSYSTEM_SERVICE_TABLE win32k;//win32k.sys的服务函数,ShadowSSDT
	KSYSTEM_SERVICE_TABLE notUsed1; //
	KSYSTEM_SERVICE_TABLE notUsed2;
}KSERCIVCE_TABLE_DESCRIPTOR, * PKSERCIVCE_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
);

//这里KeSerciveDescriptorTable 为ntoskrnl.exe所导出的全局变量
extern PKSERCIVCE_TABLE_DESCRIPTOR KeServiceDescriptorTable;

//记录系统的该函数
FuZwOpenProcess g_OldZwOpenProcess;
//服务描述符表指针
KSERCIVCE_TABLE_DESCRIPTOR* g_pSercivceTable = NULL;

//要保护的进程ID
ULONG g_Pid = 1624;

//安装钩子
NTSTATUS HookNtOpenProcess();

//卸载钩子
NTSTATUS UnHookOpenProcess();

//关闭页写入保护
void ShutPageProtect();

//开启页写入保护
void OpenPageProtect();

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

//驱动入口主函数


NTSTATUS DriverEntry(DRIVER_OBJECT* driver, UNICODE_STRING* path) {

	KdPrint(("驱动启动成功!\n"));
		//安装钩子
	HookNtOpenProcess();
	driver->DriverUnload = DriverUnload;
	return STATUS_SUCCESS;

}

//卸载驱动
void DriverUnload(DRIVER_OBJECT* obj) {
	//卸载钩子
	UnHookOpenProcess();
	KdPrint(("驱动卸载成功!\n"));

}

NTSTATUS HookNtOpenProcess() {
	NTSTATUS Status;

	Status = STATUS_SUCCESS;

	//1.关闭页只读保护
	ShutPageProtect();

	//2.写入原来的函数到SSDT表内
	uOldNtOpenProcess = KeServiceDescriptorTable -> ntoskrnl.ServiceTableBase[0x7a];
	KeServiceDescriptorTable->ntoskrnl.ServiceTableBase[0x7a] =(ULONG)MyZwOpenProcess;

	//3.开启页只读保护
	OpenPageProtect();
	return Status;
}

//卸载钩子
NTSTATUS UnHookNtOpenProcess() {
	NTSTATUS status;
	status = STATUS_SUCCESS;
	//1.关闭页保护
	ShutPageProtect();
	//2.写入原来的函数进入SSDT表内
	KeServiceDescriptorTable->ntoskrnl.ServiceTableBase[0x7a] = uOldNtOpenProcess;
	//3.开启页只读保护
	OpenPageProtect();

	return status;

}

//关闭页只读保护
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;
	}
}

//Hook 函数
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 = 0;
	}
	return NtOpenProcess(ProcessHandle, DesiredAccess, ObjectAttributes, ClientId);
}

http://www.kler.cn/a/398909.html

相关文章:

  • Win10/11 安装使用 Neo4j Community Edition
  • 2024年09月CCF-GESP编程能力等级认证Python编程三级真题解析
  • 嵌入式课程day13-C语言指针
  • PostgreSQL物化视图详解
  • v-html 富文本中图片使用element-ui image-viewer组件实现预览,并且阻止滚动条
  • 【HAProxy09】企业级反向代理HAProxy高级功能之压缩功能与后端服务器健康性监测
  • 小程序-基于java+SpringBoot+Vue的校园失物招领系统设计与实现
  • outlook邮箱关闭垃圾邮件——PowerAutomate自动化任务
  • TCP实现网络通信(多进程与多线程版本)
  • 正则表达式语法详解(python)
  • Area-Composition模型部署指南
  • vue 中使用rem布局
  • Pytest-Bdd-Playwright 系列教程(10):配置功能文件路径 优化场景定义
  • AB矩阵秩1乘法,列乘以行
  • AWD脚本编写_1
  • JQuery 基础知识学习(详尽版)2024.11.17
  • 内联函数与普通函数有什么区别?如何定义和使用内联函数?
  • Thinkphp6视图介绍
  • oracle19c开机自启动
  • 洛谷刷题日记||基础篇8
  • HarmonyOs DevEco Studio小技巧31--卡片的生命周期与卡片的开发
  • uni-app快速入门(八)--常用内置组件(上)
  • 人机界面中的数据、信息、知识、算法分层
  • UE5遇到问题记录—在sequence制作时如何让角色隐藏/显示?
  • 数据结构_图的遍历
  • springboot整合elasticsearch,并使用docker desktop运行elasticsearch镜像容器遇到的问题。