文章总结: 文档详解Windows创建进程的三种API:ShellExecuteA、ShellExecuteEx及CreateProcess。涵盖各函数参数、关键结构体与标志位,并附带C语言示例。理解这些底层机制对分析恶意软件行为、实施红队操作及进行安全开发至关重要。 综合评分: 78 文章分类: 二进制安全,红队,安全开发
Windows安全攻防-进程创建的几种方式
原创
R0x7e
剑外思归客
2026年1月11日 00:00 江苏
相关windows API
ShellExecuteA
位于 Shellapi.h 中对指定文件执行操作 返回值是一个大于 32 的值表示成功;小于 32 的值表示错误,并且每个值代表特定的错误类型。例如,值为 2 表示“文件未找到”,值为 11 表示“无效的可执行文件”等。
HINSTANCE ShellExecuteA(
[in, optional] HWND hwnd, //父窗口的句柄。 如果操作未与窗口关联,则可以NULL。
[in, optional] LPCSTR lpOperation,
[in] LPCSTR lpFile,
[in, optional] LPCSTR lpParameters,
[in, optional] LPCSTR lpDirectory,
[in] INT nShowCmd
);
hwnd父窗口的句柄。 如果操作未与窗口关联,则可以NULL。lpOperation指向一个以 null 结尾的字符串,指定要进行的操作,例如"open"、"edit"或"print"。如果此参数为NULL,则默认执行"open"操作。lpFile指向要执行或打开的文件名或程序名的字符串。该文件可以是可执行文件或其他类型的文件(如文档、URL等)。lpParameters: 如果lpFile指定了一个可执行文件,此参数指向命令行参数的字符串。如果lpFile不是可执行文件,则此参数应为NULL。lpDirectory: 指向一个以 null 结尾的字符串,指定默认目录。nShowCmd: 指定应用程序窗口如何显示。常见的值包括SW_HIDE、SW_MAXIMIZE、SW_MINIMIZE、SW_SHOW等。
ShellExecuteEx
位于 Shellapi.h中,对指定文件执行操作 如果成功,则返回 TRUE;否则,FALSE。 调用 GetLastError 以获取扩展的错误信息。
BOOL ShellExecuteExW(
[in, out] SHELLEXECUTEINFOW *pExecInfo
);
函数都需要传入一个 SHELLEXECUTEINFO 结构体指针,这个结构体包含执行操作所需的所有信息
typedef struct _SHELLEXECUTEINFOW {
DWORD cbSize; // 结构体大小
ULONG fMask; // 操作标志位
HWND hwnd; // 父窗口句柄
LPCWSTR lpVerb; // 动作:open, print, runas 等
LPCWSTR lpFile; // 文件或程序路径
LPCWSTR lpParameters; // 命令行参数(仅用于可执行文件)
LPCWSTR lpDirectory; // 工作目录
INT nShow; // 窗口显示方式(如 SW_SHOW, SW_HIDE)
HINSTANCE hInstApp; // 返回值(不使用)
void *lpIDList; // 保留,通常为 NULL
LPCWSTR lpClass; // 文件类型(可选)
HKEY hkeyClass; // 注册表项(可选)
DWORD dwHotKey; // 热键(可选)
HANDLE hIcon; // 图标句柄(可选)
HANDLE hProcess; // 输出:被启动进程的句柄(如果指定了 SEE_MASK_NOCLOSEPROCESS)
} SHELLEXECUTEINFOW, *LPSHELLEXECUTEINFOW;
fMask 常用值
| 标志 | 说明 |
| — | — |
| SEE_MASK_NOCLOSEPROCESS | 获取被启动进程的句柄(可通过 hProcess 成员访问) |
| SEE_MASK_NOASYNC | 强制同步执行(等待操作完成) |
| SEE_MASK_FLAG_DDEWAIT | 等待 DDE 操作完成 |
| SEE_MASK_INVOKEIDLIST | 使用 IDList 而不是 lpFile |
| SEE_MASK_UNICODE | 表示传入的字符串是 Unicode 格式(用于 ShellExecuteExA 时) |
| | |
示例
#include <windows.h>
#include <shellapi.h>
#include <stdio.h>
int main() {
SHELLEXECUTEINFOW sei = { 0 };
sei.cbSize = sizeof(SHELLEXECUTEINFOW);
sei.fMask = SEE_MASK_NOCLOSEPROCESS;
sei.hwnd = NULL;
sei.lpVerb = L"open";
sei.lpFile = L"notepad.exe";
sei.lpParameters = NULL;
sei.lpDirectory = NULL;
sei.nShow = SW_SHOW;
if (ShellExecuteExW(&sei)) {
WaitForSingleObject(sei.hProcess, INFINITE);
CloseHandle(sei.hProcess);
printf("记事本已打开。\n");
}
else {
printf("执行失败,错误代码:%d\n", GetLastError());
}
return0;
}
CreateProcess
位于kernel32.dll中,用于创建一个新的进程(process)及其主线程。它允许你启动一个可执行文件(EXE),并控制其运行方式,包括指定命令行参数、环境变量、安全属性、工作目录等
BOOL CreateProcess(
LPCWSTR lpApplicationName, // 可执行文件路径
LPWSTR lpCommandLine, // 命令行参数(可包含程序名)
LPSECURITY_ATTRIBUTES lpProcessAttributes, // 进程安全属性
LPSECURITY_ATTRIBUTES lpThreadAttributes, // 线程安全属性
BOOL bInheritHandles, // 是否继承句柄
DWORD dwCreationFlags, // 创建标志
LPVOID lpEnvironment, // 环境变量
LPCWSTR lpCurrentDirectory, // 当前工作目录
LPSTARTUPINFOW lpStartupInfo, // 启动信息
LPPROCESS_INFORMATION lpProcessInformation // 输出:进程和线程信息
);
1. lpApplicationName 要执行的模块(EXE 文件)的完整路径, 如果为 NULL,则从 lpCommandLine 中提取可执行文件名。 2. lpCommandLine 命令行字符串,通常包括可执行文件名和参数。 如果 lpApplicationName 为 NULL,则第一个空格前的部分会被当作程序名。示例:L"notepad.exe C:\\test.txt"3. lpProcessAttributes 指向一个结构体,决定进程句柄是否可被继承。通常设为 NULL 表示使用默认安全属性。 4. lpThreadAttributes 决定主线程句柄是否可被继承。通常设为 NULL。 5. bInheritHandles 是否允许子进程继承句柄。如果为 TRUE,子进程可以继承父进程中具有可继承属性的句柄。 6. dwCreationFlags 控制进程创建行为的标志位。 常用标志:
| 标志 | 说明 |
| — | — |
| CREATE_NEW_CONSOLE | 为新进程创建新的控制台 |
| CREATE_SUSPENDED | 创建后挂起主线程,直到调用 ResumeThread |
| DETACHED_PROCESS | 不关联控制台 |
| CREATE_NO_WINDOW | 不创建窗口(适用于 GUI 程序) |
| HIGH_PRIORITY_CLASS | 高优先级 |
| IDLE_PRIORITY_CLASS | 低优先级 |
| CREATE_UNICODE_ENVIRONMENT | 指定环境变量为 Unicode 格式 |
| | |
7. lpEnvironment 指向新进程使用的环境块。通常为 NULL,表示使用当前进程的环境变量。 8. lpCurrentDirectory 新进程的工作目录。 为 NULL 表示使用调用进程的当前目录。 9. lpStartupInfo 类型:LPSTARTUPINFOW,指定新进程的主窗口特性,如控制台、标准输入输出等。 结构体定义
typedef struct _STARTUPINFOW {
DWORD cb; // 结构体大小
LPWSTR lpReserved; // 保留
LPWSTR lpDesktop; // 桌面名称
LPWSTR lpTitle; // 窗口标题(用于控制台)
DWORD dwX; // 窗口位置
DWORD dwY;
DWORD dwXSize; // 窗口大小
DWORD dwYSize;
DWORD dwXCountChars;
DWORD dwYCountChars;
DWORD dwFillAttribute;
DWORD dwFlags; // 标志(如 STARTF_USESTDHANDLES)
WORD wShowWindow; // 窗口显示方式
WORD cbReserved2; // 保留
BYTE *lpReserved2; // 保留
HANDLE hStdInput; // 标准输入句柄
HANDLE hStdOutput; // 标准输出句柄
HANDLE hStdError; // 标准错误句柄
} STARTUPINFOW, *LPSTARTUPINFOW;
10. lpProcessInformation 类型:LPPROCESS_INFORMATION,输出参数,返回新进程和主线程的句柄与 ID。 结构体定义
typedef struct _PROCESS_INFORMATION {
HANDLE hProcess; // 进程句柄
HANDLE hThread; // 主线程句柄
DWORD dwProcessId; // 进程 ID
DWORD dwThreadId; // 线程 ID
} PROCESS_INFORMATION, *PPROCESS_INFORMATION;
示例
#include "TestCreateProcessAPI.h"
#include <windows.h>
#include <stdio.h>
int TestCreateProcess() {
STARTUPINFOW si = { 0 };
PROCESS_INFORMATION pi = { 0 };
si.cb = sizeof(STARTUPINFO);
WCHAR cmdLine[] = L"notepad.exe";
// 创建进程
if (CreateProcessW(
NULL, // 应用程序名(从命令行中提取)
cmdLine, // 命令行
NULL, // 进程安全属性
NULL, // 线程安全属性
FALSE, // 不继承句柄
0, // 默认标志
NULL, // 使用当前环境
NULL, // 使用当前目录
&si, // 启动信息
&pi // 进程信息
)) {
printf("记事本已启动,等待关闭...\n");
// 等待进程结束
WaitForSingleObject(pi.hProcess, INFINITE);
// 关闭句柄
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
printf("记事本已关闭。\n");
return1;
}
else {
printf("创建进程失败,错误代码:%d\n", GetLastError());
}
return0;
}
相关参考
https://learn.microsoft.com/zh-cn/windows/win32/api/shellapi/nf-shellapi-shellexecutea
By:《剑外思归客》公众号,R0x7e
免责声明 / Ethical & Legal Disclaimer
- 本文内容仅面向网络安全教育、网络安全研究与防御加固目的,所有演示均应在明确书面授权或自有资产范围内进行。
- 任何将本文内容用于未授权测试、攻击、渗透、持久化、权限提升、数据访问或破坏行为的,均与作者无关,后果由行为人自行承担。
- 本文不构成针对任何系统/组织的攻击指引或操作保证;读者应评估适用性与风险,确保测试过程不影响生产环境与第三方权益。
- 如本文引用或展示的工具、样本、截图或配置涉及第三方内容,其版权与责任归原权利人所有。
- 继续阅读/使用本文内容,视为你已理解并同意上述条款。
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:剑外思归客 R0x7e《Windows安全攻防-进程创建的几种方式》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。










评论