Windows XP 系统调用(二)

ʕ •ᴥ•ʔ ɔ:

1 系统服务调用

上一篇分析了2种方式从3环进0环,进入0环后分别调用2个0环的函数:KiSystemService(int 0x2e)、KiFastCallEntry(sysenter),这两个函数经过一波初始化任务之后都转到相同的地址去就执行代码。以下4个问题中的1已经在上一篇解决了(保留到_Trap_Frame结构体中),接下来将会继续分析问题2和3,问题4要等到学习APC之后才能解决。

  1. 进0环后,原来的寄存器存在哪里?
  2. 如何根据系统调用号(eax中存储)找到要执行的内核函数?
  3. 调用时参数是存储到3环的堆栈,如何传递给内核函数?
  4. 2种调用方式是如何返回到3环的?

1.1 SystemServiceTable(SST)

注意:SystemServiceTable 系统服务表不是SSDT。

SystemServiceTable:SST,系统服务表。
System Services Descriptor Table:SSDT,系统服务描述符表,导出结构为KeServiceDescriptorTable(在内核ntoskrnl.exe导出表中可看到该全局变量)。
SystemServiceParameterTable:SSPT,系统服务参数表。

System Services Descriptor Table Shadow:SSDT Shadow,系统服务描述符表Shadow,导出结构为KeServiceDescriptorTableShadow,该结构没有导出。

19.png

通过上图,我们可以得知以下信息:

  • 通过_KTHREAD偏移0xE0可以找到系统服务表SSDT。
  • 系统服务表又指向了函数地址表和函数参数表(SSPT)
  • 有两张系统服务表,第一张是用来找内核函数的,第二张是找Win32k.sys驱动函数的。
  • 两张系统服务表是线性地址连续的,每张16字节
  • SST表成员:
    • ServiceTable,是一个指针,指向函数地址表(每个成员4Byte)。
    • Count没有用。
    • ServiceLimit,当前系统服务表函数的个数。
    • ArgmentTable,指向函数参数表,参数表每个成员大小为1字节,值为参数的大小,函数参数个数(存储值 / 4 = 参数个数),每个成员和函数地址表中的函数一一对应。

3环API进0环之前,无论是中断门还是快速调用,都会在 eax 里存一个值,我们称之为系统调用号或者服务号。EAX系统服务号:

  • 1~12位:函数地址表/函数参数表索引(下标)。
  • 第13位(下标12)
    • 0,表示找第一张系统服务表(绿色的表);
    • 1,那么找第二张表(黄色的表)。

20.png

SST结构如下:

1
2
3
4
5
6
7
typedef struct _KSYSTEM_SERVICE_TABLE
{
PULONG ServiceTableBase; // 函数地址表基址
PULONG ServiceCounterTableBase;// 函数被调用的次数
ULONG NumberOfService; // 函数个数
PULONG ParamTableBase; // 函数参数表基址
} KSYSTEM_SERVICE_TABLE, *PKSYSTEM_SERVICE_TABLE,SST;

1.2 继续分析函数

前篇博客,逆向分析了 KiSystemService 和 KiFastCallEntry 填充_KTRAP_FRAME 结构体的代码,二者大同小异,主要的区别是 sysenter 只改了eip,cs,ss,虽然esp也改了,但是windows不使用,而是从TSS里取esp0;另外sysenter并没有像中断门那样压栈,所以3环的 ss, esp, eflags, cs,eip都要在函数里依次保存到 _KTRAP_FRAME 。

他们两个函数开头的初始化工作不一样,有两个入口,初始化的工作有区别,但是往后就共用一个函数体。以下继续分析:

  • 如何根据系统服务号(eax中存储)找到要执行的内核函数?
  • 调用时参数是存储到3环的堆栈,如何传递给内核函数?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
.text:0046A5AF
.text:0046A5AF loc_46A5AF: ; CODE XREF: _KiBBTUnexpectedRange+18↑j
.text:0046A5AF ; _KiSystemService+72↑j
.text:0046A5AF mov edi, eax //edi = eax = 系统调用号
.text:0046A5B1 shr edi, 8 //系统服务号右移8位
.text:0046A5B4 and edi, 30h //edi = 取系统服务号第13、14位来与运算
//000X xxxx & 0011 0000 = 000X 0000 = 0xX0 = 0x10/0x00
.text:0046A5B7 mov ecx, edi //ecx = edi = 0x10/0x00
.text:0046A5B9 add edi, [esi+0E0h] //esi = _KTHREAD,esi+0E0h指向SSDT,edi = SST
//系统服务表有ServiceTable,Count,ServiceLimit和ArgmentTable四个成员,
//4项共0x10字节,所以通过这里的代码也可以推断,内核和win32k.sys的系统服务表是
//连续的。第一张是ntoskrnl.exe导出,第二张是win32k.sys导出的
.text:0046A5BF mov ebx, eax //ebx = eax = 系统调用号
.text:0046A5C1 and eax, 0FFFh //取系统调用号低12位做偏移
.text:0046A5C6 cmp eax, [edi+8] //系统调用号 - SST.ServiceLimit,判断函数是否在表的范围内
.text:0046A5C9 jnb _KiBBTUnexpectedRange//系统调用号 >= SST.ServiceLimit,不在表里,跳转异常处理
.text:0046A5CF cmp ecx, 10h //判断系统服务号的函数是ntoskrnl.exe/win32k.sys导出
.text:0046A5D2 jnz short loc_46A5EF //判断系统服务号的函数是ntoskrnl.exe
.text:0046A5D4 mov ecx, large fs:18h//ecx指向_NT_TIB
.text:0046A5DB xor ebx, ebx //ebx = 0
.text:0046A5DD
.text:0046A5DD loc_46A5DD: ; DATA XREF: _KiTrap0E+117↓o
.text:0046A5DD or ebx, [ecx+0F70h]
.text:0046A5E3 jz short loc_46A5EF
.text:0046A5E5 push edx
.text:0046A5E6 push eax
.text:0046A5E7 call ds:_KeGdiFlushUserBatch
.text:0046A5ED pop eax
.text:0046A5EE pop edx
.text:0046A5EF
.text:0046A5EF loc_46A5EF: ; CODE XREF: _KiFastCallEntry+B2↑j
.text:0046A5EF ; _KiFastCallEntry+C3↑j
.text:0046A5EF inc large dword ptr fs:638h //0FFDFF638h = fs[0]+_KPRCB+518 = KeSystemCalls
//_KPCR.KPRCB.KeSystemCalls += 1, 系统调用计数加1
.text:0046A5F6 mov esi, edx //esi指向3环API第一个参数
.text:0046A5F8 mov ebx, [edi+0Ch] //ebx = SST.ArgmentTable
.text:0046A5FB xor ecx, ecx //ecx = 0
.text:0046A5FD mov cl, [eax+ebx] //ecx = SST.ArgmentTable + 系统调用号低12位*1,即ecx = 函数参数大小
.text:0046A600 mov edi, [edi] //edi = SST.ServiceTable = KeServiceDescriptorTable
.text:0046A602 mov ebx, [edi+eax*4] //ebx = SST.ServiceTable + 系统调用号低12位*4 = 函数地址
.text:0046A605 sub esp, ecx //0环抬高栈顶,马上将3环参数复制到0环
.text:0046A607 shr ecx, 2 //ecx = 参数个数 = 参数大小/4
.text:0046A60A mov edi, esp //edi指向0环栈顶
.text:0046A60C cmp esi, ds:_MmUserProbeAddress//越界检查,如果 esi(3环参数指针)大于等于
.text:0046A612 jnb loc_46A7C0 //用户空间0x7fff0000,则返回 c0000005 异常
.text:0046A618
.text:0046A618 loc_46A618: ; CODE XREF: _KiFastCallEntry+2A4↓j
.text:0046A618 ; DATA XREF: _KiTrap0E+10D↓o
.text:0046A618 rep movsd //复制参数:复制 esi 到 edi,每次复制4字节,次数由 ecx 决定
//方向由DF决定,DF=0,故每次复制后,edi 和 esi 都加4
.text:0046A61A call ebx //调用内核函数,ebx = 函数地址,返回值存储在eax中
.text:0046A61C
.text:0046A61C loc_46A61C: ; CODE XREF: _KiFastCallEntry+2AF↓j
.text:0046A61C ; DATA XREF: _KiTrap0E+12D↓o ...
.text:0046A61C mov esp, ebp
.text:0046A61E
.text:0046A61E loc_46A61E: ; CODE XREF: _KiBBTUnexpectedRange+38↑j
.text:0046A61E ; _KiBBTUnexpectedRange+43↑j
.text:0046A61E mov ecx, large fs:124h
.text:0046A625 mov edx, [ebp+3Ch]
.text:0046A628 mov [ecx+134h], edx
.text:0046A628 _KiFastCallEntry endp
.text:0046A628
.text:0046A62E
.text:0046A62E ; =============== S U B R O U T I N E =======================================

2 SSDT

SSDT(System Services Descriptor Table)系统服务描述符表,内核模块ntoskrnl.exe导出的一个全局变量KeServiceDescriptorTable,用来指向SSDT。

SSDT结构:

1
2
3
4
5
6
7
typedef struct _KSERVICE_TABLE_DESCRIPTOR
{
KSYSTEM_SERVICE_TABLE ntoskrnl; // 内核函数
KSYSTEM_SERVICE_TABLE win32k; // win32k.sys 函数
KSYSTEM_SERVICE_TABLE unUsed1; // 未使用
KSYSTEM_SERVICE_TABLE unUsed2; // 未使用
} KSERVICE_TABLE_DESCRIPTOR, *PKSERVICE_TABLE_DESCRIPTOR, SSDT;

2.1 SST和SSDT

SST和SSDT的关系如下:

1
2
3
4
5
6
7
SSDT
{
SST;
SST;
SST;
SST;
}

SST结构如下:

1
2
3
4
5
6
7
typedef struct _KSYSTEM_SERVICE_TABLE
{
PULONG ServiceTableBase; // 函数地址表基址
PULONG ServiceCounterTableBase;// 函数被调用的次数
ULONG NumberOfService; // 函数个数
PULONG ParamTableBase; // 函数参数表基址
} KSYSTEM_SERVICE_TABLE, *PKSYSTEM_SERVICE_TABLE,SST;

通过前面1.2的分析,要找到SST表可以通过_KTHREAD + 0xE0指向的地址找到,Windows系统在内核模块中导出了一个全局变量KeServiceDescriptorTable(SSDT),也可以得到SST表。

15.png

通过_KTHREAD + 0xE0查找SST:

  1. fs[0] = _KPCR = 0xffdff000

    1
    2
    3
    4
    5
    6
    7
    8
    kd> u _KPCR ffdff000
    Couldn't resolve error at '_KPCR ffdff000'
    kd> dt _KPCR ffdff000
    nt!_KPCR
    +0x000 NtTib : _NT_TIB
    +0x01c SelfPcr : 0xffdff000 _KPCR
    +0x020 Prcb : 0xffdff120 _KPRCB
    ...
  2. 可以看到_KPRCB = 0xffdff120

    1
    2
    3
    4
    5
    6
    +0x000 MinorVersion     : 1
    +0x002 MajorVersion : 1
    +0x004 CurrentThread : 0x8055ce60 _KTHREAD
    +0x008 NextThread : (null)
    +0x00c IdleThread : 0x8055ce60 _KTHREAD
    ...
  3. 可以看到_KTHREAD = 0x8055ce60

    1
    2
    3
    4
    5
    6
    7
    8
    9
    kd> dt _KTHREAD 0x8055ce60
    ntdll!_KTHREAD
    +0x000 Header : _DISPATCHER_HEADER
    +0x010 MutantListHead : _LIST_ENTRY [ 0x8055ce70 - 0x8055ce70 ]
    ...
    +0x0e0 ServiceTable : 0x8055d700 Void
    +0x0e4 Queue : (null)
    +0x0e8 ApcQueueLock : 0
    ...
  4. 以看到0x8055d700指向SSDT。

    1
    2
    3
    4
    5
    6
    kd> dd 0x8055d700
    8055d700 80505450 00000000 0000011c 805058c4
    8055d710 00000000 00000000 00000000 00000000
    8055d720 00000000 00000000 00000000 00000000
    8055d730 00000000 00000000 00000000 00000000
    8055d740 00000002 00002710 bf80c0b6 00000000
  5. 查看内核模块导出的结构KeServiceDescriptorTable

    1
    2
    3
    4
    5
    6
    kd> dd KeServiceDescriptorTable
    8055d700 80505450 00000000 0000011c 805058c4
    8055d710 00000000 00000000 00000000 00000000
    8055d720 00000000 00000000 00000000 00000000
    8055d730 00000000 00000000 00000000 00000000
    8055d740 00000002 00002710 bf80c0b6 00000000

可以得到结论:

  1. 通过_KTHREAD + 0xE0查找得到表是SSDT,通过ntkrnlpa.exe(2-9-9-12)导出结构KeServiceDescriptorTable得到的也是SSDT。
  2. SSDT中过包涵4个SST,但是仅可以看到第一个(由ntkrnlpa.exe导出的全局变量),想要看到win32k.sys导出的函数还需要分析系统未导出的结构SSDT Shadow。

关于SSDT更多细节可参读:

2.2 SSDT和SSDT Shadow

  • SSDT:KeServiceDescriptorTable,是由ntkrnlpa.exe导出的全局变量,可以在代码中(仅0环)使用。
  • SSDT Shadow:KeServiceDescriptorTableShadow,不是导出结构,不可以在0环代码中使用。

查看SSDT和SSDT Shadow:

1
2
3
4
5
6
7
8
9
10
11
12
13
kd> dd KeServiceDescriptorTable
8055d700 80505450 00000000 0000011c 805058c4
8055d710 00000000 00000000 00000000 00000000
8055d720 00000000 00000000 00000000 00000000
8055d730 00000000 00000000 00000000 00000000
8055d740 00000002 00002710 bf80c0b6 00000000

kd> dd KeServiceDescriptorTableShadow
8055d6c0 80505450 00000000 0000011c 805058c4
8055d6d0 bf999b80 00000000 0000029b bf99a890
8055d6e0 00000000 00000000 00000000 00000000
8055d6f0 00000000 00000000 00000000 00000000
8055d700 80505450 00000000 0000011c 805058c4
  1. SSDT、SSDT Shadow都包含4个SST,但从SSDT中仅可看到由ntkrnlpa.exe导出函数的地址表。
  2. SSDT Shadow可以看到2个地址表,由ntkrnlpa.exe和win32k.sys导出。

在驱动程序中使用SSDT:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//extern "C" __declspec(dllimport)  SSDT  KeServiceDescriptorTable;
extern SSDT KeServiceDescriptorTable;

typedef struct _KSYSTEM_SERVICE_TABLE
{
PULONG ServiceTableBase; // 函数地址表基址
PULONG ServiceCounterTableBase;// 函数被调用的次数
ULONG NumberOfService; // 函数个数
PULONG ParamTableBase; // 函数参数表基址
} KSYSTEM_SERVICE_TABLE, *PKSYSTEM_SERVICE_TABLE,SST;
typedef struct _KSERVICE_TABLE_DESCRIPTOR
{
KSYSTEM_SERVICE_TABLE ntoskrnl; // 内核函数
KSYSTEM_SERVICE_TABLE win32k; // win32k.sys 函数
KSYSTEM_SERVICE_TABLE unUsed1; // 未使用
KSYSTEM_SERVICE_TABLE unUsed2; // 未使用
} KSERVICE_TABLE_DESCRIPTOR, *PKSERVICE_TABLE_DESCRIPTOR, SSDT;

内核中有两个系统服务描述符表,一个是KeServiceDescriptorTable(由ntoskrnl.exe导出),一个是KeServieDescriptorTableShadow(没有导出)。
两者的区别是:KeServiceDescriptorTable仅有ntoskrnel一项,KeServieDescriptorTableShadow包含了ntoskrnel以及win32k。一般的Native API的服务地址由KeServiceDescriptorTable分派,gdi.dll/user.dll的内核API调用服务地址由KeServieDescriptorTableShadow分派。还有要清楚一点的是win32k.sys只有在GUI线程中才加载,一般情况下是不加载的,所以要Hook KeServieDescriptorTableShadow的话,一般是用一个GUI程序通过IoControlCode来触发(或者在程序中先调用一个gdi.dll/user.dll的API,否则会蓝屏)。

32位系统:

  • XP:KeServiceDescriptorTableShadow = KeServiceDescriptorTable - 0×40
  • Win7:KeServiceDescriptorTableShadow = KeServiceDescriptorTable + 0×40

64位系统:SSDT表是加密的。

64位系统的SSDT表可参考:HOOK技术之SSDT hook(x86/x64)

3 SSDT HOOK

3.1 SSDT HOOK 原理

SSDT HOOK原理:用自己写好的一个函数替换SSDT中的一个函数SSDT.ntoskrnl.ServiceTableBase[0xIndex]即可。

注意事项:

  1. 替换函数地址时必须保证SSDT表是可写入的,但自XP系统之后SSDT内存块属性仅可读,需要自己进行修改,有至少三种方法可使用:

    • 更改注册表
      恢复页面保护:HKLM\SYSTEM\CurrentControlset\Control\Session Manger\Memory Management\EnforceWriteProtection=0
      去掉页面保护:HKLM\SYSTEM\CurrentControlset\Control\Session Manger\Memory Management\DisablePagingExecutive=1

    • 改变CR0寄存器下标16的位(第17位 == 0,可写)

      CR0寄存器的第17位叫做保护属性位,控制着页的读或写属性。该方法在多核情况下不稳定,核切换时CR0也就切换了。

      51.png

    • 页表基址修改页属性

      通过修改要替换地址的PDE_R/W & PTE_R/W属性。该方法最稳定最好。

32位系统上SSDT是导出的(KeServiceDescriptorTable),64位是不会导出的,但是可以通过PCHunter、Kernel Detective等工具查看到内核函数对应的函数下标索引。

如下是通过PCHunter查看到的SSDT表(内核钩子-SSDT-右键取消仅显示挂钩函数):

21.png

每一个版本的Windows操作系统的系统服务函数的编号都是固定的,例如所有32位的windows 7的系统服务函数的编号都是固定的,无论系统版本如何变化。这主要是因为一旦更新操作系统后,如果系统服务函数的编号发生变化会导致系统不稳定。基于以上事实,我们只需要针对win7和win10定义四份函数表即可(32位、64位)。SSDT(系统服务描述符表 system services descriptor table)

SSDT HOOK推荐几篇文章:

3.2 SSDT HOOK模版

下面的驱动代码ssdt hook了NtOpenProcess函数,可以监视打开进程的操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
#include <ntddk.h>
#include <ntstatus.h>

/************************************************************************
类型声明
************************************************************************/

// 系统服务表SST
typedef struct _KSYSTEM_SERVICE_TABLE
{
PULONG ServiceTableBase; // 函数地址表
PULONG ServiceCounterTableBase; //函数被调用的次数
ULONG NumberOfService; // 函数个数
PULONG ParamTableBase; // 函数参数表(SSPT)
} KSYSTEM_SERVICE_TABLE, *PKSYSTEM_SERVICE_TABLE;

typedef struct _KSERVICE_TABLE_DESCRIPTOR
{
KSYSTEM_SERVICE_TABLE ntoskrnl; // 内核ntkrnlpa.exe函数
KSYSTEM_SERVICE_TABLE win32k; // win32k.sys函数
KSYSTEM_SERVICE_TABLE unUsed1;
KSYSTEM_SERVICE_TABLE unUsed2;
} KSERVICE_TABLE_DESCRIPTOR, *PKSERVICE_TABLE_DESCRIPTOR;

// NTOPENPROCESS,用来指向旧的NtOpenProcess函数
typedef NTSTATUS (*NTOPENPROCESS) (PHANDLE ProcessHandle,
ACCESS_MASK DesiredAccess,
POBJECT_ATTRIBUTES ObjectAttributes,
PCLIENT_ID ClientId);


/************************************************************************
函数声明
************************************************************************/

VOID DriverUnload(PDRIVER_OBJECT pDriver);
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING reg_path);
VOID EnableWrite();
VOID DisableWrite();
VOID HookNtOpenProcess();
VOID UnHookNtOpenProcess();
NTSTATUS MyNtOpenProcess(PHANDLE ProcessHandle,
ACCESS_MASK DesiredAccess,
POBJECT_ATTRIBUTES ObjectAttributes,
PCLIENT_ID ClientId);


/************************************************************************/
/* 全局变量 */
/************************************************************************/

extern PKSERVICE_TABLE_DESCRIPTOR KeServiceDescriptorTable; // ntoskrnl.exe 导出的全局变量
ULONG uOldNtOpenProcess; // 旧的函数地址


/************************************************************************/
/* 函数定义 */
/************************************************************************/

// 驱动入口
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING reg_path)
{
// HOOK
HookNtOpenProcess();

pDriver->DriverUnload = DriverUnload;

return STATUS_SUCCESS;
}

// 卸载驱动
VOID DriverUnload(PDRIVER_OBJECT pDriver)
{
UnHookNtOpenProcess();
DbgPrint("Driver unloaded.\n");
}

/*
if(RCR4 & 0x00000020) //说明是2-9-9-12分页
{
//修改PTE_R/W
*(DWORD64*)(0xC0000000 + ((HookFunAddr >> 9) & 0x007FFFF8)) |= 0x02;
//修改PDE_R/W
*(DWORD64*)(0xc0600000 + ((HookFunAddr >> 18) & 0x3ff8))) |= 0x02;
}
else //说明是10-10-12分页
{
//修改PTE_R/W
*(DWORD*)(0xC0000000 + ((HookFunAddr >> 10) & 0x003FFFFC)) |= 0x02;
//修改PDE_R/W
*(DWORD*)(0xC0000000 + (((HookFunAddr >> 22) & 0x3FF) < 2)) |= 0x02;
}
*/

// 设置为可写,WP = 0
VOID EnableWrite()
{
__asm
{
cli; // 关闭中断
mov eax, cr0;
and eax, not 0x10000; // and eax,0FFFEFFFFh,WP位置0
mov cr0, eax;
}
}

// 设置为不可写,WP = 1
VOID DisableWrite()
{
__asm
{
mov eax, cr0;
or eax, 0x10000; // WP位置1,
mov cr0, eax;
sti; // 恢复中断
}
}

// HOOK NtOpenProcess
VOID HookNtOpenProcess()
{
EnableWrite();
uOldNtOpenProcess = KeServiceDescriptorTable->ntoskrnl.ServiceTableBase[0x7A];
KeServiceDescriptorTable->ntoskrnl.ServiceTableBase[0x7A] = (ULONG)MyNtOpenProcess;
PageProtectOn();
}

// UnHOOK NtOpenProcess
VOID UnHookNtOpenProcess()
{
PageProtectOff();
KeServiceDescriptorTable->ntoskrnl.ServiceTableBase[0x7A] = uOldNtOpenProcess;
DisableWrite();
}

// 被修改的 NtOpenProcess 函数,简单打印参数
NTSTATUS MyNtOpenProcess(PHANDLE ProcessHandle,
ACCESS_MASK DesiredAccess,
POBJECT_ATTRIBUTES ObjectAttributes,
PCLIENT_ID ClientId)
{
DbgPrint("%x %x %x %x\n", ProcessHandle, DesiredAccess, ObjectAttributes, ClientId);
return ((NTOPENPROCESS)uOldNtOpenProcess)(ProcessHandle, DesiredAccess, ObjectAttributes, ClientId);
}

3.3 SSDT HOOK 实现保护记事本进程

题目要求:
将系统服务表中某个函数改成自己的函数,使任务管理器右键无法关闭自己,只有点击自己的关闭按钮才可以正常关闭。

补充内容:
在3环的程序要想终止某个进程会调用函数TerminateProcess(HANDLE hProcess, UINT uExitCode),通过追码有以下调用流程:

1
2
3环:Kernel32.dll.TerminateProcess(x,x) --> NtDll.dll.NtTerminateProcess(x,x)
0环:--> ntkrnlpa.exe.NtTerminateProcess(x,x)

前面在驱动开发部分已经通过特征码搜索未导出函数PspTerminateProcess来结束一个进程,他和NtTerminateProcess函数的区别:

  • NtTerminateProcess:ntkrnlpa.exe中未导出该函数存在于SST中,操作系统提供给3环API调用。
  • PspTerminateProcess:ntkrnlpa.exe中未导出该函数也不存在于SST中,但是操作系统自己结束一个进程时却是调用该函数。

所以该题方法是SSDT HOOK NtTerminateProcess 函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
#include <ntddk.h>
#include <ntstatus.h>
// NtTerminateProcess 系统调用号
#define NTTERMINATEPROCESS_EAX 0x101

/************************************************************************
类型声明
************************************************************************/

// 系统服务表SST
typedef struct _KSYSTEM_SERVICE_TABLE
{
PULONG ServiceTableBase; // 函数地址表
PULONG ServiceCounterTableBase; //函数被调用的次数
ULONG NumberOfService; // 函数个数
PULONG ParamTableBase; // 函数参数表(SSPT)
} KSYSTEM_SERVICE_TABLE, *PKSYSTEM_SERVICE_TABLE;

typedef struct _KSERVICE_TABLE_DESCRIPTOR
{
KSYSTEM_SERVICE_TABLE ntoskrnl; // 内核ntkrnlpa.exe函数
KSYSTEM_SERVICE_TABLE win32k; // win32k.sys函数
KSYSTEM_SERVICE_TABLE unUsed1;
KSYSTEM_SERVICE_TABLE unUsed2;
} KSERVICE_TABLE_DESCRIPTOR, *PKSERVICE_TABLE_DESCRIPTOR;

// NTTERMINATEPRO,用来指向旧的NtTerminateProcess函数
typedef NTSTATUS (*NTTERMINATEPRO) (HANDLE hProcess, NTSTATUS uExitCode);


/************************************************************************
函数声明
************************************************************************/

VOID DriverUnload(PDRIVER_OBJECT pDriver);
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING reg_path);
VOID EnableWrite();
VOID DisableWrite();
VOID HookNtTerminateProcess();
VOID UnHookNtTerminateProcess();
NTSTATUS MyNtTerminateProcess(HANDLE hProcess, NTSTATUS uExitCode);


/************************************************************************
全局变量
************************************************************************/

extern PKSERVICE_TABLE_DESCRIPTOR KeServiceDescriptorTable; // ntoskrnl.exe 导出的全局变量
ULONG uOldNtTerminateProcess = 0; // 旧的函数地址


/************************************************************************
函数定义
************************************************************************/

// 驱动入口
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING reg_path)
{
// HOOK
HookNtTerminateProcess();

pDriver->DriverUnload = DriverUnload;

return STATUS_SUCCESS;
}

// 卸载驱动
VOID DriverUnload(PDRIVER_OBJECT pDriver)
{
UnHookNtTerminateProcess();
DbgPrint("Driver unloaded.\n");
}

// 设置为可写,WP = 0
VOID EnableWrite()
{
/* __asm
{
cli; // 关闭中断
mov eax, cr0;
and eax, not 0x10000; // and eax,0FFFEFFFFh,WP位置0
mov cr0, eax;
}
*/
ULONG RCR4 = 0;
ULONG HookFunAddr = KeServiceDescriptorTable->ntoskrnl.ServiceTableBase[NTTERMINATEPROCESS_EAX];
__asm
{
cli;
//mov eax,cr4,VS2010无法识别该行代码,故以硬编码形式写入
_emit 0x0F
_emit 0x20
_emit 0xE0
mov RCR4,eax;
}
if(RCR4 & 0x00000020) //说明是2-9-9-12分页
{
//修改PTE_R/W
*(DWORD64*)(0xC0000000 + ((HookFunAddr >> 9) & 0x007FFFF8)) |= 0x02;
//修改PDE_R/W
*(DWORD64*)(0xc0600000 + ((HookFunAddr >> 18) & 0x3ff8)) |= 0x02;
}
else //说明是10-10-12分页
{
//修改PTE_R/W
*(ULONG*)(0xC0000000 + ((HookFunAddr >> 10) & 0x003FFFFC)) |= 0x02;
//修改PDE_R/W
*(ULONG*)(0xC0300000 + ((HookFunAddr >> 20) & 0xFFC)) |= 0x02;
}
}

// 设置为不可写,WP = 1
VOID DisableWrite()
{
/* __asm
{
mov eax, cr0;
or eax, 0x10000; // WP位置1,
mov cr0, eax;
sti; // 恢复中断
}
*/
ULONG RCR4 = 0;
ULONG HookFunAddr = KeServiceDescriptorTable->ntoskrnl.ServiceTableBase[NTTERMINATEPROCESS_EAX];
__asm
{
//mov eax,cr4,VS2010无法识别该行代码,故以硬编码形式写入
_emit 0x0F
_emit 0x20
_emit 0xE0
mov RCR4,eax;
}
if(RCR4 & 0x00000020) //说明是2-9-9-12分页
{
//修改PTE_R/W
*(DWORD64*)(0xC0000000 + ((HookFunAddr >> 9) & 0x007FFFF8)) &= 0xFFFFFFFDF;
//修改PDE_R/W
*(DWORD64*)(0xc0600000 + ((HookFunAddr >> 18) & 0x3ff8)) &= 0xFFFFFFFDF;
}
else //说明是10-10-12分页
{
//修改PTE_R/W
*(ULONG*)(0xC0000000 + ((HookFunAddr >> 10) & 0x003FFFFC)) &= 0xFFFFFFFDF;
//修改PDE_R/W
*(ULONG*)(0xC0300000 + ((HookFunAddr >> 20) & 0xFFC)) &= 0xFFFFFFFDF;
}
__asm sti;
}

// HOOK NtTerminateProcess
VOID HookNtTerminateProcess()
{
EnableWrite();
uOldNtTerminateProcess = KeServiceDescriptorTable->ntoskrnl.ServiceTableBase[NTTERMINATEPROCESS_EAX];
KeServiceDescriptorTable->ntoskrnl.ServiceTableBase[NTTERMINATEPROCESS_EAX] = (ULONG)MyNtTerminateProcess;
DisableWrite();
}

// UnHOOK NtTerminateProcess
VOID UnHookNtTerminateProcess()
{
EnableWrite();
KeServiceDescriptorTable->ntoskrnl.ServiceTableBase[NTTERMINATEPROCESS_EAX] = uOldNtTerminateProcess;
DisableWrite();
}

// 被修改的 NtOpenProcess 函数,简单打印参数
NTSTATUS MyNtTerminateProcess(HANDLE hProcess, NTSTATUS uExitCode)
{
PEPROCESS pEprocess = NULL;
NTSTATUS status = 0;
PCHAR ImageFileName = NULL;

//通过进程句柄来获得该进程所对应的 FileObject 对象,由于这里是进程对象,自然获得的是 EPROCESS 对象
//判断要关闭的进程是否是记事本
status = ObReferenceObjectByHandle(hProcess,FILE_ANY_ACCESS,*PsProcessType,KernelMode,(PVOID*)&pEprocess,NULL);
if (!NT_SUCCESS(status))
{
return status;
}
ImageFileName = (PCHAR)((ULONG)pEprocess + 0x174);
//判断要关闭的进程是否是记事本
if (strcmp(ImageFileName, "notepad.exe") == 0)
{
//如果是当前进程
if (hProcess == (HANDLE)0xFFFFFFFF)
{
// 通过关闭按钮关闭
return ((NTTERMINATEPRO)uOldNtTerminateProcess)(hProcess, uExitCode);
}
else
{
// 通过任务管理器关闭
DbgPrint("Terminate denied. %s: NtTerminateProcess(%x, %x)\n", ImageFileName, hProcess, uExitCode);
return STATUS_ACCESS_DENIED;
}
}

DbgPrint("hProcess:%x,uExitCode:%x\n", hProcess, uExitCode);
return ((NTTERMINATEPRO)uOldNtTerminateProcess)(hProcess, uExitCode);
}

通过任务管理器关闭记事本:

23.png

通过关闭记事本的按钮:

22.png

通过PCHunter查看:

24.png

3.4 SSDT Shadow HOOK 的 FindWindowA 监视器

先追一下FindWindowA调用路径:

FindWindowA –> user32.dll.FindWindowA –> user32.dll.NtUserFindWindowEx(x, x, x, x, x) –> eax = 117Ah

0环驱动代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
#include <ntddk.h>
#include <ntstatus.h>

#define DEVICE_NAME L"\\Device\\HOOKFindWindowADev"
#define DRIVER_LINK L"\\??\\HOOKFindWindowALnk"
#define OPHOOK CTL_CODE(FILE_DEVICE_UNKNOWN,0x900,METHOD_BUFFERED,FILE_ANY_ACCESS)
#define OPUNHOOK CTL_CODE(FILE_DEVICE_UNKNOWN,0x901,METHOD_BUFFERED,FILE_ANY_ACCESS)
// 系统调用号
#define NTUSERFINDWINDOWEX_SERVICE (0x117A & 0x0FFF)

// 系统服务表SST
typedef struct _KSYSTEM_SERVICE_TABLE
{
PULONG ServiceTableBase; // 函数地址表
PULONG ServiceCounterTableBase; //函数被调用的次数
ULONG NumberOfService; // 函数个数
PULONG ParamTableBase; // 函数参数表(SSPT)
} KSYSTEM_SERVICE_TABLE, *PKSYSTEM_SERVICE_TABLE;

typedef struct _KSERVICE_TABLE_DESCRIPTOR
{
KSYSTEM_SERVICE_TABLE ntoskrnl; // 内核ntkrnlpa.exe函数
KSYSTEM_SERVICE_TABLE win32k; // win32k.sys函数
KSYSTEM_SERVICE_TABLE unUsed1;
KSYSTEM_SERVICE_TABLE unUsed2;
} KSERVICE_TABLE_DESCRIPTOR, *PKSERVICE_TABLE_DESCRIPTOR;

// ntoskrnl.exe 导出的全局变量
extern PKSERVICE_TABLE_DESCRIPTOR KeServiceDescriptorTable;
// Only XP 32bit can be used.
PKSERVICE_TABLE_DESCRIPTOR KeServiceDescriptorTableShadow;
// 旧的函数地址
ULONG uOldNtUserFindWindowEx;
//系统服务号,从3环传进来
ULONG g_dwFun_R0_API = 0;

// 函数声明
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING RegPath);
VOID DriverUnload(PDRIVER_OBJECT pDriver);
NTSTATUS IrpCreateProc(PDEVICE_OBJECT pDevObj, PIRP pIrp);
NTSTATUS IrpCloseProc(PDEVICE_OBJECT pDevObj, PIRP pIrp);
NTSTATUS IrpDeviceControlProc(PDEVICE_OBJECT pDevObj, PIRP pIrp);
VOID EnableWrite();
VOID DisableWrite();
BOOLEAN HookNtUserFindWindowEx();
BOOLEAN UnHookNtUserFindWindowEx();
// NTUSERFINDWINDOWSEX,用来指向旧的NtUserFindWindowEx函数
typedef NTSTATUS (*NTUSERFINDWINDOWSEX) (
IN ULONG hwndParent,
IN ULONG hwndChild,
IN ULONG pstrClassName,
IN ULONG pstrWindowName,
IN ULONG dwTyp);
NTSTATUS MyNtUserFindWindowEx(
IN ULONG hwndParent,
IN ULONG hwndChild,
IN ULONG pstrClassName,
IN ULONG pstrWindowName,
IN ULONG dwTyp);
VOID PrintCurrentProcessInfo();

// 设置为可写,WP = 0
VOID EnableWrite()
{
/* __asm
{
cli; // 关闭中断
mov eax, cr0;
and eax, not 0x10000; // and eax,0FFFEFFFFh,WP位置0
mov cr0, eax;
}
*/
ULONG RCR4 = 0;
ULONG HookFunAddr = KeServiceDescriptorTableShadow->win32k.ServiceTableBase[NTUSERFINDWINDOWEX_SERVICE];
__asm
{
cli;
//mov eax,cr4,VS2010无法识别该行代码,故以硬编码形式写入
_emit 0x0F
_emit 0x20
_emit 0xE0
mov RCR4,eax;
}
if(RCR4 & 0x00000020) //说明是2-9-9-12分页
{
//修改PTE_R/W
*(DWORD64*)(0xC0000000 + ((HookFunAddr >> 9) & 0x007FFFF8)) |= 0x02;
//修改PDE_R/W
*(DWORD64*)(0xc0600000 + ((HookFunAddr >> 18) & 0x3ff8)) |= 0x02;
}
else //说明是10-10-12分页
{
//修改PTE_R/W
*(ULONG*)(0xC0000000 + ((HookFunAddr >> 10) & 0x003FFFFC)) |= 0x02;
//修改PDE_R/W
*(ULONG*)(0xC0300000 + ((HookFunAddr >> 20) & 0xFFC)) |= 0x02;
}
}
// 设置为不可写,WP = 1
VOID DisableWrite()
{
/* __asm
{
mov eax, cr0;
or eax, 0x10000; // WP位置1,
mov cr0, eax;
sti; // 恢复中断
}
*/
ULONG RCR4 = 0;
ULONG HookFunAddr = KeServiceDescriptorTableShadow->win32k.ServiceTableBase[NTUSERFINDWINDOWEX_SERVICE];
__asm
{
//mov eax,cr4,VS2010无法识别该行代码,故以硬编码形式写入
_emit 0x0F
_emit 0x20
_emit 0xE0
mov RCR4,eax;
}
if(RCR4 & 0x00000020) //说明是2-9-9-12分页
{
//修改PTE_R/W
*(DWORD64*)(0xC0000000 + ((HookFunAddr >> 9) & 0x007FFFF8)) &= 0xFFFFFFFDF;
//修改PDE_R/W
*(DWORD64*)(0xc0600000 + ((HookFunAddr >> 18) & 0x3ff8)) &= 0xFFFFFFFDF;
}
else //说明是10-10-12分页
{
//修改PTE_R/W
*(ULONG*)(0xC0000000 + ((HookFunAddr >> 10) & 0x003FFFFC)) &= 0xFFFFFFFDF;
//修改PDE_R/W
*(ULONG*)(0xC0300000 + ((HookFunAddr >> 20) & 0xFFC)) &= 0xFFFFFFFDF;
}
__asm sti;
}

// 入口函数
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING RegPath)
{
ULONG Status;
ULONG uIndex = 0;
PDEVICE_OBJECT pDeviceObj = NULL; // 设备对象指针
UNICODE_STRING DeviceName; // 设备名,0环用
UNICODE_STRING SymbolicLinkName; // 符号链接名,3环用
KeServiceDescriptorTableShadow = (PKSERVICE_TABLE_DESCRIPTOR)((ULONG)KeServiceDescriptorTable - 0x40);

PrintCurrentProcessInfo();
// 创建设备名称
RtlInitUnicodeString(&DeviceName,DEVICE_NAME);
// 创建设备
Status = IoCreateDevice(pDriver,0,&DeviceName,FILE_DEVICE_UNKNOWN,FILE_DEVICE_SECURE_OPEN,FALSE,&pDeviceObj);
if (Status != STATUS_SUCCESS)
{
IoDeleteDevice(pDeviceObj);
DbgPrint("创建设备失败.\n");
return Status;
}
DbgPrint("创建设备成功.\n");
// 设置交互数据的方式
pDeviceObj->Flags |= DO_BUFFERED_IO;
// 创建符号链接
RtlInitUnicodeString(&SymbolicLinkName, DRIVER_LINK);
IoCreateSymbolicLink(&SymbolicLinkName, &DeviceName);
// 设置分发函数
pDriver->MajorFunction[IRP_MJ_CREATE] = IrpCreateProc;
pDriver->MajorFunction[IRP_MJ_CLOSE] = IrpCloseProc;
pDriver->MajorFunction[IRP_MJ_DEVICE_CONTROL] = IrpDeviceControlProc;
// 设置卸载函数
pDriver->DriverUnload = DriverUnload;
return STATUS_SUCCESS;
}

// 卸载驱动
VOID DriverUnload(PDRIVER_OBJECT pDriver)
{
UNICODE_STRING SymbolicLinkName;
// 删除符号链接,删除设备
RtlInitUnicodeString(&SymbolicLinkName, DRIVER_LINK);
IoDeleteSymbolicLink(&SymbolicLinkName);
IoDeleteDevice(pDriver->DeviceObject);
DbgPrint("驱动卸载成功\n");
}

// 不设置这个函数,则Ring3调用CreateFile会返回1
// IRP_MJ_CREATE 处理函数
NTSTATUS IrpCreateProc(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{
DbgPrint("应用层连接设备.\n");
// 返回状态如果不设置,Ring3返回值是失败
pIrp->IoStatus.Status = STATUS_SUCCESS;
pIrp->IoStatus.Information = 0;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}

// IRP_MJ_CLOSE 处理函数
NTSTATUS IrpCloseProc(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{
DbgPrint("应用层断开连接设备.\n");
// 返回状态如果不设置,Ring3返回值是失败
pIrp->IoStatus.Status = STATUS_SUCCESS;
pIrp->IoStatus.Information = 0;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}

// IRP_MJ_DEVICE_CONTROL 处理函数
NTSTATUS IrpDeviceControlProc(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{
// DbgPrint("IrpDeviceControlProc.\n");
NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST;
PIO_STACK_LOCATION pIrpStack;
ULONG uIoControlCode;
PVOID pIoBuffer;
ULONG uInLength;
ULONG uOutLength;
ULONG uRead;
ULONG uWrite;
BOOLEAN bRet = FALSE;

// 设置临时变量的值
uRead = 0;
uWrite = 0;
// 获取IRP数据
pIrpStack = IoGetCurrentIrpStackLocation(pIrp);
// 获取控制码
uIoControlCode = pIrpStack->Parameters.DeviceIoControl.IoControlCode;
// 获取缓冲区地址(输入输出是同一个)
pIoBuffer = pIrp->AssociatedIrp.SystemBuffer;
// Ring3 发送数据的长度
uInLength = pIrpStack->Parameters.DeviceIoControl.InputBufferLength;
// Ring0 发送数据的长度
uOutLength = pIrpStack->Parameters.DeviceIoControl.OutputBufferLength;

switch (uIoControlCode)
{
case OPHOOK:
{
// 打印进程镜像名和PID
PrintCurrentProcessInfo();
// 读取缓冲区
memcpy(&uRead,pIoBuffer,4);
//DbgPrint("Get Service Num is %x\n",uRead);
//执行结束进程
bRet = HookNtUserFindWindowEx();
if(bRet == TRUE)
{
// 写入缓冲区
uWrite = 1;
memcpy(pIoBuffer, &uWrite, 4);
}else
{
// 写入缓冲区
uWrite = 0;
memcpy(pIoBuffer, &uWrite, 4);
}

// 设置状态
pIrp->IoStatus.Information = 4;
status = STATUS_SUCCESS;
break;
}
case OPUNHOOK:
{
bRet = UnHookNtUserFindWindowEx();
// 设置状态
pIrp->IoStatus.Information = 0;
status = STATUS_SUCCESS;
break;
}
}

// 返回状态如果不设置,Ring3返回值是失败
pIrp->IoStatus.Status = status;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}

// HOOK NtTerminateProcess
BOOLEAN HookNtUserFindWindowEx()
{
EnableWrite();
uOldNtUserFindWindowEx = KeServiceDescriptorTableShadow->win32k.ServiceTableBase[NTUSERFINDWINDOWEX_SERVICE];
KeServiceDescriptorTableShadow->win32k.ServiceTableBase[NTUSERFINDWINDOWEX_SERVICE] = (ULONG)MyNtUserFindWindowEx;
DisableWrite();
DbgPrint("Hooked: %p -> %p\n", uOldNtUserFindWindowEx, KeServiceDescriptorTableShadow->win32k.ServiceTableBase[NTUSERFINDWINDOWEX_SERVICE]);
return TRUE;
}

// UnHOOK NtTerminateProcess
BOOLEAN UnHookNtUserFindWindowEx()
{
EnableWrite();
KeServiceDescriptorTableShadow->win32k.ServiceTableBase[NTUSERFINDWINDOWEX_SERVICE] = uOldNtUserFindWindowEx;
DisableWrite();
DbgPrint("UnHooked: %p\n", KeServiceDescriptorTableShadow->win32k.ServiceTableBase[NTUSERFINDWINDOWEX_SERVICE]);
return TRUE;
}

// 被修改的 NtUserFindWindowEx 函数,简单打印参数
NTSTATUS MyNtUserFindWindowEx(
IN ULONG hwndParent,
IN ULONG hwndChild,
IN ULONG pstrClassName,
IN ULONG pstrWindowName,
IN ULONG dwTyp)
{
DbgPrint("NtUserFindWindowEx(%x, %x, %x, %x, %x)\n", hwndParent,hwndChild,pstrClassName,pstrWindowName,dwTyp);
return ((NTUSERFINDWINDOWSEX)uOldNtUserFindWindowEx)(hwndParent,hwndChild,pstrClassName,pstrWindowName,dwTyp);
}

// 打印进程镜像名和PID
VOID PrintCurrentProcessInfo()
{
PEPROCESS pEprocess = NULL;
PCHAR ImageFileName;
ULONG pid;
__asm
{
mov eax, fs:[0x124];
mov eax, [eax + 0x220];
mov pEprocess, eax;
mov eax, [eax + 0x84];
mov pid, eax;
}
ImageFileName = (PCHAR)pEprocess + 0x174;
DbgPrint("pid: %x, ImageFileName:%s\n",pid, ImageFileName);
}

3环代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
// HOOK0316.cpp : Defines the entry point for the console application.

#include "stdafx.h"
#include <Windows.h>
#include <winioctl.h>
#include <tchar.h>
#include <stdlib.h>

// 编写一个简单的驱动,在驱动入口和卸载函数打印一些提示信息
#define DRIVER_NAME L"HOOKFindWindowA"
#define DRIVER_PATH L"HOOKFindWindowA.sys"
#define DRIVER_LINK L"\\\\.\\HOOKFindWindowALnk"
#define OPHOOK CTL_CODE(FILE_DEVICE_UNKNOWN,0x900,METHOD_BUFFERED,FILE_ANY_ACCESS)
#define OPUNHOOK CTL_CODE(FILE_DEVICE_UNKNOWN,0x901,METHOD_BUFFERED,FILE_ANY_ACCESS)


BOOL LoadDriver(PCWSTR lpszDriverName, PCWSTR lpszDriverPath)
{
// 获取驱动完整路径
WCHAR szDriverFullPath[MAX_PATH] = { 0 };
GetFullPathNameW(lpszDriverPath,MAX_PATH,szDriverFullPath,NULL);
//printf("%ws\n", szDriverFullPath);
// 打开服务控制管理器
SC_HANDLE hServiceMgr = NULL; // SCM管理器句柄
hServiceMgr = OpenSCManagerW(NULL,NULL,SC_MANAGER_ALL_ACCESS);
if (NULL == hServiceMgr)
{
printf("OpenSCManagerW 失败, %d\n", GetLastError());
return FALSE;
}
printf("打开服务控制管理器成功.\n");
// 创建驱动服务
SC_HANDLE hServiceDDK = NULL; // NT驱动程序服务句柄
hServiceDDK = CreateServiceW(
hServiceMgr,
lpszDriverName,
lpszDriverName,
SERVICE_ALL_ACCESS,
SERVICE_KERNEL_DRIVER,
SERVICE_DEMAND_START,
SERVICE_ERROR_IGNORE,
szDriverFullPath,
NULL,
NULL,
NULL,
NULL,
NULL);
if (NULL == hServiceDDK)
{
DWORD dwErr = GetLastError();
if (dwErr != ERROR_IO_PENDING && dwErr != ERROR_SERVICE_EXISTS)
{
printf("创建驱动服务失败, %d\n", dwErr);
return FALSE;
}
}
printf("创建驱动服务成功.\n");
DWORD dwErr = 0;
// 驱动服务已经创建,打开服务
hServiceDDK = OpenServiceW(hServiceMgr,lpszDriverName,SERVICE_ALL_ACCESS);
dwErr = GetLastError();
//printf("%d\n", dwErr);
if (!StartService(hServiceDDK, NULL, NULL))
{
dwErr = GetLastError();
if (dwErr != ERROR_SERVICE_ALREADY_RUNNING)
{
printf("运行驱动服务失败, %d\n", dwErr);
return FALSE;
}
}
printf("运行驱动服务成功.\n");
if (hServiceDDK)
{
CloseServiceHandle(hServiceDDK);
}
if (hServiceMgr)
{
CloseServiceHandle(hServiceMgr);
}
return TRUE;
}

void RunHOOKDriver()
{
HANDLE hDevice = CreateFileW(DRIVER_LINK, GENERIC_READ|GENERIC_WRITE,0,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);
if (hDevice == INVALID_HANDLE_VALUE)
{
printf("创建设备失败. %d\n", GetLastError());
return;
}
// 要HOOK的系统服务号
BYTE InBuffer[8] = {0};
DWORD OutBuffer;
DWORD dwOut = 0;
DeviceIoControl(hDevice,OPHOOK,InBuffer,4,&OutBuffer,4,&dwOut,NULL);
printf("从0环的返回码:%d\n",OutBuffer);
if(OutBuffer == 1)
{
printf("HOOK成功.\n");
}else
{
printf("HOOK失败.\n");
}
// 关闭设备
CloseHandle(hDevice);
}

void UnLoadDriver(PCWSTR lpszDriverName)
{
SC_HANDLE hServiceMgr = OpenSCManagerW(0,0,SC_MANAGER_ALL_ACCESS);
SC_HANDLE hServiceDDK = OpenServiceW(hServiceMgr,lpszDriverName,SERVICE_ALL_ACCESS);
SERVICE_STATUS SvrStatus;
ControlService(hServiceDDK,SERVICE_CONTROL_STOP,&SvrStatus);
DeleteService(hServiceDDK);
if (hServiceDDK)
{
CloseServiceHandle(hServiceDDK);
}
if (hServiceMgr)
{
CloseServiceHandle(hServiceMgr);
}
}

BOOL UnHook()
{
HANDLE hDevice = CreateFileW(DRIVER_LINK, GENERIC_READ|GENERIC_WRITE,0,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);
DWORD bRet;
if (hDevice == INVALID_HANDLE_VALUE)
{
printf("UnHook失败.\n");
return FALSE;
}
DeviceIoControl(hDevice,OPUNHOOK,NULL,0,NULL,0,&bRet,NULL);
// 关闭设备
CloseHandle(hDevice);
return TRUE;
}

int _tmain(int argc, _TCHAR* argv[])
{
// win32k.sys系统服务表挂物理页,没有这行在0环的代码可能会使电脑蓝屏
HWND hwnd = FindWindowA(NULL,"notepad.exe");
if (!LoadDriver(DRIVER_NAME, DRIVER_PATH))
{
printf("加载驱动失败.\n");
return 1;
}

printf("本程序仅用于XP 32bit环境,Sys驱动请放到和本程序相同的目录下,且不要改驱动文件名!\n按回车开始HOOK...\n");
RunHOOKDriver();

printf("按回车开始卸载UNHOOK...\n");
getchar();
UnHook();

printf("按回车开始卸载HOOK...\n");
getchar();
UnLoadDriver(DRIVER_NAME);

return 0;
}

25.png

⚠️注意事项:

  1. 在3环程序中,驱动名称DRIVER_NAME和驱动路径DRIVER_PATH名称都要是.sys的名称。

  2. 3环的加载程序需要和.sys驱动程序在同一目录下。

  3. 3环程序在多次调试后可能会失败卸载驱动,导致StartService()失败,GetLastError()得到错误码是2,此时需要用KmdManager.exe对驱动进行卸载。

    26.png

  4. 在VC直接F7、F5运行3环的加载程序可能会一闪而过,这个时候需要在该程序的目录下双击运行该程序(需要先执行步骤3进行卸载再双击)。

注意驱动的调用进程:

27.png

上述程序说明,驱动加载后,执行驱动入口代码时,所属进程是系统进程。这和 DeviceIoControl 时情况又有所不同,DeviceIoControl 通信时,所属进程是发起通信的3环程序。