将进程提升至PPL权限

PPL介绍

Windows 的 PPL(Protected Process Light,受保护的进程 Light)是一种安全机制,用于保护关键系统进程和受信任的程序免受恶意软件的篡改和攻击。它通过限制对受保护进程的访问来实现这一点,即使是管理员用户也无法随意修改或终止这些进程。

为什么要提升权限

为什么要研究将进程提升至到PPL权限呢??

因为我准备尝试使用ETW-TI,也就是Microsoft-Windows-Threat-Intelligence这个provider,之前我一直不知道什么是ETW-TI😅,直到我发现原来是这个玩意。。。。

Microsoft-Windows-Threat-Intelligence这个provider,它主要用于记录与安全和威胁情报相关的信息。

我们可以看看这玩意的关键字,几乎都是敏感的操作

据说杀软也是依赖这个的

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
提供程序                                 GUID
-------------------------------------------------------------------------------
Microsoft-Windows-Threat-Intelligence {F4E1897C-BB5D-5668-F1D8-040F4D8DD344}

值 关键字 描述
-------------------------------------------------------------------------------
0x0000000000000001 KERNEL_THREATINT_KEYWORD_ALLOCVM_LOCAL
0x0000000000000002 KERNEL_THREATINT_KEYWORD_ALLOCVM_LOCAL_KERNEL_CALLER
0x0000000000000004 KERNEL_THREATINT_KEYWORD_ALLOCVM_REMOTE
0x0000000000000008 KERNEL_THREATINT_KEYWORD_ALLOCVM_REMOTE_KERNEL_CALLER
0x0000000000000010 KERNEL_THREATINT_KEYWORD_PROTECTVM_LOCAL
0x0000000000000020 KERNEL_THREATINT_KEYWORD_PROTECTVM_LOCAL_KERNEL_CALLER
0x0000000000000040 KERNEL_THREATINT_KEYWORD_PROTECTVM_REMOTE
0x0000000000000080 KERNEL_THREATINT_KEYWORD_PROTECTVM_REMOTE_KERNEL_CALLER
0x0000000000000100 KERNEL_THREATINT_KEYWORD_MAPVIEW_LOCAL
0x0000000000000200 KERNEL_THREATINT_KEYWORD_MAPVIEW_LOCAL_KERNEL_CALLER
0x0000000000000400 KERNEL_THREATINT_KEYWORD_MAPVIEW_REMOTE
0x0000000000000800 KERNEL_THREATINT_KEYWORD_MAPVIEW_REMOTE_KERNEL_CALLER
0x0000000000001000 KERNEL_THREATINT_KEYWORD_QUEUEUSERAPC_REMOTE
0x0000000000002000 KERNEL_THREATINT_KEYWORD_QUEUEUSERAPC_REMOTE_KERNEL_CALLER
0x0000000000004000 KERNEL_THREATINT_KEYWORD_SETTHREADCONTEXT_REMOTE
0x0000000000008000 KERNEL_THREATINT_KEYWORD_SETTHREADCONTEXT_REMOTE_KERNEL_CALLER
0x0000000000010000 KERNEL_THREATINT_KEYWORD_READVM_LOCAL
0x0000000000020000 KERNEL_THREATINT_KEYWORD_READVM_REMOTE
0x0000000000040000 KERNEL_THREATINT_KEYWORD_WRITEVM_LOCAL
0x0000000000080000 KERNEL_THREATINT_KEYWORD_WRITEVM_REMOTE
0x0000000000100000 KERNEL_THREATINT_KEYWORD_SUSPEND_THREAD
0x0000000000200000 KERNEL_THREATINT_KEYWORD_RESUME_THREAD
0x0000000000400000 KERNEL_THREATINT_KEYWORD_SUSPEND_PROCESS
0x0000000000800000 KERNEL_THREATINT_KEYWORD_RESUME_PROCESS
0x0000000001000000 KERNEL_THREATINT_KEYWORD_FREEZE_PROCESS
0x0000000002000000 KERNEL_THREATINT_KEYWORD_THAW_PROCESS
0x0000000004000000 KERNEL_THREATINT_KEYWORD_CONTEXT_PARSE
0x0000000008000000 KERNEL_THREATINT_KEYWORD_EXECUTION_ADDRESS_VAD_PROBE
0x0000000010000000 KERNEL_THREATINT_KEYWORD_EXECUTION_ADDRESS_MMF_NAME_PROBE
0x0000000020000000 KERNEL_THREATINT_KEYWORD_READWRITEVM_NO_SIGNATURE_RESTRICTION
0x0000000040000000 KERNEL_THREATINT_KEYWORD_DRIVER_EVENTS
0x0000000080000000 KERNEL_THREATINT_KEYWORD_DEVICE_EVENTS
0x0000000100000000 KERNEL_THREATINT_KEYWORD_READVM_REMOTE_FILL_VAD
0x0000000200000000 KERNEL_THREATINT_KEYWORD_WRITEVM_REMOTE_FILL_VAD
0x0000000400000000 KERNEL_THREATINT_KEYWORD_PROTECTVM_LOCAL_FILL_VAD
0x0000000800000000 KERNEL_THREATINT_KEYWORD_PROTECTVM_LOCAL_KERNEL_CALLER_FILL_VAD
0x0000001000000000 KERNEL_THREATINT_KEYWORD_PROTECTVM_REMOTE_FILL_VAD
0x0000002000000000 KERNEL_THREATINT_KEYWORD_PROTECTVM_REMOTE_KERNEL_CALLER_FILL_VAD
0x0000004000000000 KERNEL_THREATINT_KEYWORD_PROCESS_IMPERSONATION_UP
0x0000008000000000 KERNEL_THREATINT_KEYWORD_PROCESS_IMPERSONATION_REVERT
0x0000010000000000 KERNEL_THREATINT_KEYWORD_PROCESS_SYSCALL_USAGE
0x0000020000000000 KERNEL_THREATINT_KEYWORD_QUEUEUSERAPC_AT_DPC
0x0000040000000000 KERNEL_THREATINT_KEYWORD_PROCESS_IMPERSONATION_DOWN
0x8000000000000000 Microsoft-Windows-Threat-Intelligence/Analytic

可以看到,使用这个ETW TI其实是有门槛的

1738741904396

签名咱肯定是弄不到了,但是,看资料说提升至PPL也是可以的

1738742239811

原理弄懂了,开始启动

关于PPL,可以看我之前写的一篇文章

[PPL Attack | Cc12138’s blog](https://0xcc12138.github.io/PPL Attack/)

实现代码

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
#pragma once
extern "C"
{
#include <ntifs.h>
#include <ntddk.h>
#define DebugPrint(...) DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL,__VA_ARGS__)
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath);
VOID DriverUnloadRoutine(IN PDRIVER_OBJECT DriverObject);
typedef struct _PS_PROTECTION {
UCHAR Type : 3; // 保护类型
UCHAR Audit : 1; // 审计标记
UCHAR Signer : 4; // 签名者类型
} PS_PROTECTION, * PPS_PROTECTION;

typedef struct _PROCESS_SIGNATURE {
UCHAR SignatureLevel; // 签名级别
UCHAR SectionSignatureLevel; // 段签名级别(暂时不使用)
PS_PROTECTION Protection; // 保护类型
} PROCESS_SIGNATURE, * PPROCESS_SIGNATURE;

struct ProcessSignature {
ULONG Pid; // 进程PID
UCHAR SignerType; // 签名类型
UCHAR SignatureSigner; // 签名者类型
};
ULONG SignatureLevelOffset = 0; // EPROCESS中PROCESS_SIGNATURE结构的偏移量
}




#include "header.h"


// 获取SignatureLevel的偏移量
ULONG GetSignatureLevelOffset() {
//在我的电脑上是 SignatureLevel offset=0x878
return 0x878;
}

// 设置进程签名
NTSTATUS SetProcessSignature(ProcessSignature* ProcessSignature) {
if (!SignatureLevelOffset) {
SignatureLevelOffset = GetSignatureLevelOffset(); // 获取偏移量
}
if (SignatureLevelOffset) {
PEPROCESS process;
NTSTATUS status = STATUS_SUCCESS;

// 查找进程
status = PsLookupProcessByProcessId(ULongToHandle(ProcessSignature->Pid), &process);

if (!NT_SUCCESS(status)) {
return status;
}

// 计算新的SignatureLevel(算法就是这样,懒得深究了)
UCHAR newSignatureLevel = (ProcessSignature->SignerType << 4) | ProcessSignature->SignatureSigner;

// 定位到EPROCESS中的PROCESS_SIGNATURE结构
PPROCESS_SIGNATURE processSignature = (PPROCESS_SIGNATURE)((UINT64)process + SignatureLevelOffset);

// 设置新的SignatureLevel和Protection信息
processSignature->SignatureLevel = newSignatureLevel;
processSignature->Protection.Type = ProcessSignature->SignerType;
processSignature->Protection.Signer = ProcessSignature->SignatureSigner;

// 释放对进程的引用
ObDereferenceObject(process);

return status;
}

return STATUS_INVALID_PARAMETER;
}

// 使用示例
NTSTATUS SetProtectedProcess(ULONG Pid) {
ProcessSignature ps;
ps.Pid = Pid;
ps.SignerType = 2; // PsProtectedTypeProtectedLight
ps.SignatureSigner = 5; // PsProtectedSignerWindows

NTSTATUS status = SetProcessSignature(&ps);
if (NT_SUCCESS(status))
{
DebugPrint("Successfully set the process to PPL.\n");
return status;
}
else
{
DebugPrint("Failed to set the process to PPL. Status: 0x%x\n", status);
return status;
}
}


// 定义一个处理函数,始终返回 STATUS_SUCCESS
NTSTATUS IrpDispatchRoutine(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
DeviceObject;
// 在此例程中,你可以添加日志记录或者其他处理,但我们保证它始终返回 STATUS_SUCCESS
Irp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest(Irp, IO_NO_INCREMENT); // 完成请求
return STATUS_SUCCESS;
}



// 定义控制码(示例)
#define IOCTL_EXAMPLE CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)

// 设备控制请求的处理函数
NTSTATUS IrpDeviceControlRoutine(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
DeviceObject;
PIO_STACK_LOCATION ioStackLocation = IoGetCurrentIrpStackLocation(Irp);
ULONG inputBufferLength = ioStackLocation->Parameters.DeviceIoControl.InputBufferLength;
PVOID inputBuffer = Irp->AssociatedIrp.SystemBuffer;

NTSTATUS status = STATUS_SUCCESS;
ULONG inputValue = 0;

switch (ioStackLocation->Parameters.DeviceIoControl.IoControlCode) {
case IOCTL_EXAMPLE:
// 检查输入缓冲区是否包含足够的空间来存放一个ULONG
if (inputBufferLength >= sizeof(ULONG)) {
// 假设传入的是多字节字符(ASCII字符串)
ANSI_STRING ansiString;
RtlInitAnsiString(&ansiString, (PCSZ)inputBuffer);

// 将多字节字符串转换为ULONG(10表示十进制)
status = RtlCharToInteger(ansiString.Buffer, 10, &inputValue);

if (NT_SUCCESS(status)) {
// 继续处理转换后的inputValue
status = SetProtectedProcess(inputValue);
}
else {
// 转换失败时输出错误信息
DbgPrint("Error: Failed to convert string to ULONG. Status: 0x%08X\n", status);
status = STATUS_INVALID_PARAMETER;
}
}
else {
// 输入缓冲区太小
DbgPrint("Error: Input buffer too small. Expected size: %d, actual size: %d\n", sizeof(ULONG), inputBufferLength);
status = STATUS_BUFFER_TOO_SMALL;
}
break;

// 其他case处理...

default:
// 无效的设备控制请求
DbgPrint("Error: Invalid IOCTL control code: 0x%08X\n", ioStackLocation->Parameters.DeviceIoControl.IoControlCode);
status = STATUS_INVALID_DEVICE_REQUEST;
break;
}

// 完成 IRP 请求
Irp->IoStatus.Status = status;
Irp->IoStatus.Information = 0; // 无输出信息
IoCompleteRequest(Irp, IO_NO_INCREMENT);

return status;
}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
{
UNREFERENCED_PARAMETER(RegistryPath);
DebugPrint(("Create!"));


KdBreakPoint();
// 驱动程序卸载例程&注册例程
DriverObject->DriverUnload = DriverUnloadRoutine;
//创建设备
NTSTATUS status;
PDEVICE_OBJECT DeviceObject = NULL;
UNICODE_STRING DeviceName;
RtlInitUnicodeString(&DeviceName, L"\\Device\\MyDevice");
status = IoCreateDevice(
DriverObject, // 驱动程序对象
0, // 设备扩展大小
&DeviceName, // 设备名称
FILE_DEVICE_UNKNOWN, // 设备类型
0, // 设备特征
FALSE, // 非独占设备
&DeviceObject // 返回的设备对象指针
);

// 设置设备特性标志,这里设置为DO_BUFFERED_IO
DeviceObject->Characteristics |= DO_BUFFERED_IO;

if (!NT_SUCCESS(status))
{
DebugPrint("Failed to create device: %X\n", status);
return status;
}
DebugPrint(("Device created successfully\n"));

UNICODE_STRING symbolicLink = RTL_CONSTANT_STRING(L"\\??\\MyDevice_Link");
status = IoCreateSymbolicLink(&symbolicLink, &DeviceName);
if (!NT_SUCCESS(status))
{
DebugPrint("Failed to create device: %X\n", status);
return status;
}
DebugPrint(("Device created successfully\n"));


for (ULONG i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++)
{
DriverObject->MajorFunction[i] = IrpDispatchRoutine;
}

DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = IrpDeviceControlRoutine;


return STATUS_SUCCESS;
}



VOID DriverUnloadRoutine(IN PDRIVER_OBJECT DriverObject)
{
if (DriverObject->DeviceObject != NULL)
{
UNICODE_STRING symbolicLink = RTL_CONSTANT_STRING(L"\\??\\MyDevice_Link");
IoDeleteSymbolicLink(&symbolicLink);
IoDeleteDevice(DriverObject->DeviceObject);
}

DebugPrint("Driver unloaded\n");
}

应用层代码:

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
#include <windows.h>
#include <iostream>
using namespace std;

#define IOCTL_EXAMPLE_DEVICE_CONTROL CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)

int main() {
system("pause");
HANDLE hDevice;
DWORD bytesReturned;
char inputData[10] = {0}; // 要发送的数据
char outputData[100]; // 设备返回的数据


cout << "请输入要提升至PPL的进程的PID(十进制)" << endl;
cin >> inputData;

// 打开设备对象
hDevice = CreateFile(
L"\\\\.\\MyDevice_Link", // 设备名称
GENERIC_READ | GENERIC_WRITE, // 读写权限
0, // 不共享
NULL, // 安全属性
OPEN_EXISTING, // 如果设备存在则打开
0, // 无重叠I/O
NULL // 无模板文件
);

if (hDevice == INVALID_HANDLE_VALUE) {
printf("Failed to open device. Error: %d\n", GetLastError());
return 1;
}

// 向设备发送控制请求
BOOL result = DeviceIoControl(
hDevice, // 设备句柄
IOCTL_EXAMPLE_DEVICE_CONTROL, // 控制代码
inputData, // 输入缓冲区(发送给设备)
sizeof(inputData), // 输入缓冲区的大小
NULL, // 输出缓冲区(设备返回的数据)
NULL, // 输出缓冲区的大小
&bytesReturned, // 返回实际的字节数
NULL // 重叠I/O结构(如果需要)
);

if (!result) {
printf("DeviceIoControl failed. Error: %d\n", GetLastError());
}
else {
printf("Device response: %s\n", outputData);
}

// 关闭设备句柄
CloseHandle(hDevice);

return 0;
}

用法展示:

例如我们要将Notepad.exe提升到PPL权限

1738744737646

可以看到此时notepad.exe是没有任何保护的

1738744817700

然后安装好驱动,打开我们的三环程序

1738744934336

1738744913117

效果如下,可以看到已经是加了保护了,而且是禁止访问的

1738744968513