win32api之进程的创建与使用(二)
创始人
2025-05-30 21:21:00
0

什么是进程

在计算机操作系统中,进程是正在运行中的程序的实例。进程是操作系统进行资源分配和管理的基本单位,包括内存、文件句柄、系统状态等。每个进程都有自己的独立内存空间和运行状态,因此它们不会互相干扰,也不会互相影响。多个进程可以在操作系统上同时运行,每个进程都在自己的空间里执行自己的代码


进程内存空间的划分

在Windows X86环境下, 进程的内存空间通常被划分为以下三个区域

分区描述地址范围
用户模式区用户模式区占据了进程地址空间的大部分,通常是4GB大小。它包含了程序代码、堆、栈、数据等,是进程中大部分数据的存放位置0x00000000 ~ 0x7FFFFFFF (2GB)
系统保留区由于内核区和用户模式区之间存在一定的限制和隔离,这个区域可以用来共享一些必要的数据,以便内核和用户模式的进程之间能够进行通信和数据共享0x7FFE0000 - 0x7FFFFFFF (128KB)
内核区内核模式区只占据了进程地址空间的一小部分,它通常包含了内核数据结构、操作系统代码和其他内核相关的资源。内核模式区对于应用程序来说是不可访问的,只能通过系统调用等方式来访问其中的数据和功能0x80000000 ~ 0xFFFFFFFF (2GB)

进程的创建过程

首先有一点要清楚, 任何进程都是别的进程创建的, 而第一个进程是系统启动时由操作系统内核创建的, 它的进程ID为0, 称为系统空闲进程或系统进程

在Windows中,进程的创建可以使用CreateProcess函数,该函数会返回一个指向新进程的句柄

1.为进程分配内存空间,加载EXE文件, 并将其映射到内存

image-20230216194610827


2.创建进程内核对象EPROCESS

EPROCESS是Windows操作系统内核中的一种数据结构,它代表了一个进程对象。EPROCESS结构体包含了进程的许多信息,例如进程的PID、进程的线程列表、虚拟内存映射、访问权限、I/O访问权限等等

image-20230216194949260


3.映射系统dll(ntdll.dll)至内存

image-20230216195410482


4.创建线程内核对象ETHREAD

创建进程的时候系统会默认创建一个线程, 也就是说每个进程都至少有一个线程

image-20230216195620381


5.系统启动线程

线程启动之前, 还需要映射运行可执行文件所需的其他dll文件, 随后线程才开始执行

image-20230216200411778


进程涉及API

CreateProcess

CreateProcess函数用于创建一个新的进程并返回进程句柄, 其原型如下

BOOL CreateProcess(LPCTSTR lpApplicationName,  //需要运行的可执行文件名LPTSTR lpCommandLine,  //命令行参数字符串LPSECURITY_ATTRIBUTES lpProcessAttributes,  //进程的安全属性LPSECURITY_ATTRIBUTES lpThreadAttributes,  //线程的安全属性BOOL bInheritHandles,  //是否继承父进程的句柄DWORD dwCreationFlags,  //进程标志 LPVOID lpEnvironment,  //新进程的环境变量,如果为NULL,则将使用当前进程的环境变量LPCTSTR lpCurrentDirectory,  //新进程的当前目录LPSTARTUPINFO lpStartupInfo,  //指向STARTUPINFO结构的指针,包含了启动进程时的窗口状态和标志等信息LPPROCESS_INFORMATION lpProcessInformation  //指向PROCESS_INFORMATION结构的指针,用于返回新进程的进程句柄和主线程句柄
);

如下是PROCESS_INFORMATION结构的成员

typedef struct _PROCESS_INFORMATION {HANDLE hProcess;  //进程句柄	HANDLE hThread;  //线程句柄DWORD dwProcessId;  //进程IDDWORD dwThreadId;  //线程ID
}

如下是STARTUPINFO结构的成员

typedef struct _STARTUPINFOW {DWORD   cb;   //结构体大小LPWSTR  lpReserved;  //保留,置为NULLLPWSTR  lpDesktop;  //指定进程的窗口站和桌面名称,或者是一个空字符LPWSTR  lpTitle; //指定进程的窗口标题DWORD   dwX;  //指定主窗口左上角的初始位置,以屏幕坐标表示DWORD   dwY;  DWORD   dwXSize;  //指定主窗口的宽度和高度,以像素表示DWORD   dwYSize;  DWORD   dwXCountChars;   //指定主窗口的字符宽度和字符高度DWORD   dwYCountChars;DWORD   dwFillAttribute;  //指定用于填充主窗口的初始颜色和属性DWORD   dwFlags;  //指定控制进程如何创建的一组标志WORD    wShowWindow;  //指定主窗口最初如何显示WORD    cbReserved2;  //保留成员LPBYTE  lpReserved2;  //保留成员HANDLE  hStdInput;  //标准输入设备的句柄HANDLE  hStdOutput;  //标准输出设备的句柄HANDLE  hStdError;  //标准错误设备的句柄
}

如下代码是一个创建进程的简单实例, 用于启动计算器(calc.exe)

include 
include //创建子进程函数,传递两个参数,分别是应用程序名字和程序命令行参数,若子进程创建成功则返回子进程句柄
DWORD CreateChildProcess(TCHAR ApplicationName[],TCHAR CommandLine[]=NULL) {STARTUPINFO si;  //进程启动信息PROCESS_INFORMATION pi;  //进程信息ZeroMemory(&si, sizeof(si));  //将结构体si的所有成员都初始化为0ZeroMemory(&pi, sizeof(pi));  //将结构体pi的所有成员都初始化为0si.cb = sizeof(si);  //结构体大小if (!CreateProcess(ApplicationName,  //要执行的应用程序名称(包含路径)NULL,  //命令行参数NULL,  //进程句柄不可被继承NULL,  //线程句柄不可被继承FALSE,  //不继承句柄0,  //标志位为0NULL,  //使用父进程的环境变量NULL,  //使用父进程的工作目录&si,  //传递启动信息&pi)  //传递进程信息) {printf("CreateProcess failed (%d).\n", GetLastError());  //打印错误信息return 0;}return (DWORD)pi.hProcess;  //返回进程句柄//return (DWORD)pi.dwProcessId;  //返回进程ID//释放进程句柄和线程句柄CloseHandle(pi.hProcess);CloseHandle(pi.hThread);
}int main()
{TCHAR ApplicationName[] = TEXT("E:\\calc.exe");DWORD ProcessHandle = CreateChildProcess(ApplicationName);return 0;
}

若需要以挂起的形式创建进程, 可将CreateProcess函数的第六个参数设置为CREARE_SUSPEND, 这样创建的进程一开始并不会自动启动线程, 而是需要自己手动执行ResumeThread函数恢复线程后才会启动

include 
include int main()
{STARTUPINFO si;  //进程启动信息PROCESS_INFORMATION pi;  //进程信息ZeroMemory(&si, sizeof(si));  //将结构体si的所有成员都初始化为0ZeroMemory(&pi, sizeof(pi));  //将结构体pi的所有成员都初始化为0si.cb = sizeof(si);  //结构体大小TCHAR ApplicationName[] = TEXT("E://test.exe");  //test.exe是一个只输出"进程执行"的文件if (!CreateProcess(ApplicationName,  //要执行的应用程序名称(包含路径)NULL,  //命令行参数NULL,  //进程句柄不可被继承NULL,  //线程句柄不可被继承FALSE,  //不继承句柄CREATE_SUSPENDED,  //以挂起的形式创建进程NULL,  //使用父进程的环境变量NULL,  //使用父进程的工作目录&si,  //传递启动信息&pi)  //传递进程信息) {printf("CreateProcess failed (%d).\n", GetLastError());  //打印错误信息return 0;}for (int i = 0; i < 5; i++){printf("#######\n");Sleep(1000);}ResumeThread(pi.hThread);//释放进程句柄和线程句柄CloseHandle(pi.hProcess);CloseHandle(pi.hThread);
}

如上代码所示, 线程会在for循环打印代码执行结束后才会启动线程, 执行结果如下

image-20230221085734005

OpeoProcess

OpenProcess函数用于打开一个已存在的进程对象,以便对该进程执行操作,例如向该进程发送信号或从该进程读取内存。此函数的调用者必须具有足够的权限来打开目标进程

如果函数执行成功,返回打开进程的句柄,否则返回NULL,并可通过调用GetLastError函数获取错误码

要注意的是, 使用CloseHandle函数释放句柄后, 就不能再使用OpenProcess函数来打开这个进程了, 因为CloseHandle函数会将句柄从进程的句柄表中移除,并且在所有引用计数都归零之后释放内存资源

OpenProcess函数的语法如下:

HANDLE OpenProcess(DWORD dwDesiredAccess,  // 指定进程的访问权限,可取值为PROCESS_ALL_ACCESS或其他指定的进程访问权限常量BOOL bInheritHandle,    // 指定句柄是否可被子进程继承,TRUE表示可继承,FALSE表示不可继承DWORD dwProcessId       // 指定要打开的进程的进程ID
);

以下是dwDesireAccess参数的可取值:

  • PROCESS_ALL_ACCESS:具有完全访问权限的进程访问权限。
  • PROCESS_CREATE_PROCESS:允许创建新进程。
  • PROCESS_CREATE_THREAD:允许在进程中创建新线程。
  • PROCESS_DUP_HANDLE:允许进程使用 DuplicateHandle 函数复制句柄。
  • PROCESS_QUERY_INFORMATION:允许查询进程信息,如进程ID、进程优先级等。
  • PROCESS_QUERY_LIMITED_INFORMATION:允许查询受限信息,如进程ID、进程优先级、进程占用内存等。
  • PROCESS_SET_INFORMATION:允许设置进程信息,如进程优先级、进程AffinityMask等。
  • PROCESS_SET_QUOTA:允许设置进程的工作集大小和默认的硬错误模式。
  • PROCESS_SUSPEND_RESUME:允许挂起和恢复进程。
  • PROCESS_TERMINATE:允许终止进程。
  • PROCESS_VM_OPERATION:允许进行虚拟内存操作,如 VirtualAlloc、VirtualProtect 等。
  • PROCESS_VM_READ:允许读取进程的虚拟内存。
  • PROCESS_VM_WRITE:允许写入进程的虚拟内存

TeminateProcess

TerminateProcess函数是Windows操作系统提供的函数之一,用于终止指定进程, 当调用TerminateProcess函数时,会向指定进程发送一个中断信号,强制其终止。

这个过程是非常暴力的,会直接终止进程的所有线程,不会给进程和线程任何清理资源的机会,因此使用该函数需要非常慎重

函数定义如下:

BOOL TerminateProcess(HANDLE hProcess,  //进程句柄,用于标识被终止的进程UINT   uExitCode  //进程的退出代码,表示进程退出的原因,可以随意填写
);

GetModuleFileName

GetModuleFileName函数用于获取指定模块的完整路径名。通常情况下,可以通过指定NULL作为参数hModule,来获取当前应用程序的完整路径名,该函数的声明如下:

DWORD GetModuleFileName(HMODULE hModule,  // 模块句柄,指定NULL表示获取当前应用程序的路径名LPTSTR lpFilename,  // 接收完整路径名的缓冲区DWORD nSize  // 缓冲区大小
);

GetCurrentDirectory

GetCurrentDirectory函数用于获取当前进程的工作目录。其函数原型为

DWORD GetCurrentDirectory(DWORD  nBufferLength,  // 缓冲区大小,单位为字节LPTSTR lpBuffer        // 存储路径的缓冲区
);

这个函数的路径是指当前进程的工作目录,而不是当前模块的目录。如果需要获取当前模块的目录,需要使用GetModuleFileName函数来获取模块文件的路径

include 
include int main()
{	//获取当前模块的完整路径char str1[256];GetModuleFileName(NULL, str1, 256);printf("当前应用程序路径名:%s\n", str1);//获取当前应用程序的工作目录char str2[256];GetCurrentDirectory(256, str2);printf("当前程序工作目录:%s", str2);return 0;
}

image-20230220162026966


GetStartupInfo

GetStartupInfo函数用于检索当前进程的启动信息,它的主要功能是获取STARTUPINFO结构体,其中包含了进程的启动信息,如命令行参数、标准输入输出句柄、窗口显示方式等

调用GetStartupInfo函数需传递一个指向STARTUPINFO类型的指针

int main()
{
// 定义一个 STARTUPINFO 结构体变量 si
STARTUPINFO si;// 使用 ZeroMemory 函数将 si 清零
ZeroMemory(&si, sizeof(si));// 设置 si 的 cb 字段
si.cb = sizeof(si);// 使用 GetStartupInfo 函数获取启动信息
GetStartupInfo(&si);// 输出当前进程窗口状态
printf("Show window command: %d\n", si.dwFlags);// 返回 0,表示程序执行成功
return 0;
}

GetCurrentProcessID

GetCurrentProcessID函数是Windows API中的一部分,它返回当前进程的进程ID(Process ID, 其返回值是一个无符号长整型

其语法格式如下:

GetProcessId(_In_ HANDLE Process
);

GetCurrentProcess

GetCurrentProcess函数是Windows API提供的一个函数,用于获取当前进程的句柄。该函数没有任何参数,调用后将返回一个类型为HANDLE的句柄,该句柄指向当前进程

GetCurrentProcess 获取当前进程的一个伪句柄GetCurrentProcess 总是返回-1(即0xFFFFFFFF),代表当前进程。这个句柄不在句柄表中,不是真正的句柄,所以叫伪句柄


EnumProcesses

EnumProcesses函数是Windows API中的一个函数,用于列举当前正在运行的进程的ID号,通常用于获取系统中所有进程的ID号列表。若函数执行成功则返回TRUE, 否则返回FLASE, 它的声明如下:

BOOL EnumProcesses(DWORD  *pProcessIds, // 接收进程ID的缓冲区DWORD  cb,           // 缓冲区大小(以字节为单位)DWORD  *pBytesReturned  // 实际写入缓冲区的字节数
);

如果包含了Windows.h头文件, 还提示"EnumProcesses"未定义标识符, 请检查是否正确链接了psapi.lib库文件。

可以通过在Visual Studio中转到“项目”菜单,然后选择“属性”来查看和配置链接器选项。在属性页面的左侧选择“链接器”,然后选择“输入”。在“附加依赖项”字段中添加“Psapi.lib”,然后包含头文件:include "psapi.h"

如下实例枚举当前系统运行的所有进程ID, 并打印至控制台

include 
include 
include "psapi.h"define ARRAY_SIZE 1024int main()
{DWORD aProcesses[ARRAY_SIZE], cbNeeded, cProcesses;if (!EnumProcesses(aProcesses, sizeof(aProcesses), &cbNeeded)){printf("EnumProcesses failed: %d\n", GetLastError());return 1;}// 计算枚举到的进程数cProcesses = cbNeeded / sizeof(DWORD);// 打印进程IDfor (DWORD i = 0; i < cProcesses; i++){if (aProcesses[i] != 0){printf("Process ID: %u\n", aProcesses[i]);}}return 0;
}

image-20230223151412794


CreateToolhelp32Snapshot

CreateToolhelp32Snapshot函数是Windows系统提供的一个快照函数,可以获取系统中当前正在运行的进程和线程的快照。该函数可以通过枚举系统中所有进程和线程来帮助实现进程和线程的监控和管理。在调用该函数时,需要指定快照类型,如进程快照、线程快照等。函数会返回一个句柄,该句柄可以作为参数传递给其他Tool Help函数,以获取有关系统中进程和线程的详细信息。在使用完成后,需要调用CloseHandle函数关闭句柄

如下实例使用CreateToolhelp32Snapshot函数枚举系统所有进程:

include 
include 
include 
include int main()
{HANDLE hProcessSnap;PROCESSENTRY32 pe32;// 获取系统进程快照hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);if (hProcessSnap == INVALID_HANDLE_VALUE) {std::cout << "CreateToolhelp32Snapshot failed: " << GetLastError() << std::endl;return 1;}// 设置pe32结构体的大小,否则Process32First/Next函数会失败pe32.dwSize = sizeof(PROCESSENTRY32);// 获取第一个进程的信息if (!Process32First(hProcessSnap, &pe32)) {std::cout << "Process32First failed: " << GetLastError() << std::endl;CloseHandle(hProcessSnap);return 1;}// 遍历进程列表,输出每个进程的PID和名称do {_tprintf(TEXT("PID=%d, Name=%s\n"), pe32.th32ProcessID, pe32.szExeFile);} while (Process32Next(hProcessSnap, &pe32));// 关闭进程快照句柄CloseHandle(hProcessSnap);return 0;
}

相关内容

热门资讯

安卓系统和oppo系统哪个流畅... 你有没有想过,手机系统哪个更流畅呢?安卓系统和OPPO系统,这两个名字听起来就让人心动。今天,咱们就...
安卓怎么用微软系统,利用微软系... 你是不是也和我一样,对安卓手机上的微软系统充满了好奇?想象那熟悉的Windows界面在你的安卓手机上...
安卓系统如何安装nfc,安卓系... 你有没有想过,用手机刷公交卡、支付账单,是不是比掏出钱包来得酷炫多了?这就得归功于NFC技术啦!今天...
ios系统可以转安卓,跨平台应... 你有没有想过,你的iPhone手机里的那些宝贝应用,能不能搬到安卓手机上继续使用呢?没错,今天就要来...
iOSapp移植到安卓系统,i... 你有没有想过,那些在iOS上让你爱不释手的app,是不是也能在安卓系统上大放异彩呢?今天,就让我带你...
现在安卓随便换系统,探索个性化... 你知道吗?现在安卓手机换系统简直就像换衣服一样简单!没错,就是那种随时随地、随心所欲的感觉。今天,就...
安卓系统安装按钮灰色,探究原因... 最近发现了一个让人头疼的小问题,那就是安卓手机的安装按钮突然变成了灰色,这可真是让人摸不着头脑。你知...
安卓7.1.1操作系统,系统特... 你知道吗?最近我在手机上发现了一个超级酷的新玩意儿——安卓7.1.1操作系统!这可不是什么小打小闹的...
安卓os系统怎么设置,并使用`... 你有没有发现,你的安卓手机有时候就像一个不听话的小孩子,有时候设置起来真是让人头疼呢?别急,今天就来...
安卓降低系统版本5.1,探索安... 你知道吗?最近安卓系统又来了一次大动作,竟然把系统版本给降到了5.1!这可真是让人有点摸不着头脑,不...
解放安卓系统被保护,解放安卓系... 你有没有想过,你的安卓手机其实可以更加自由地呼吸呢?是的,你没听错,我说的就是解放安卓系统被保护的束...
校务帮安卓系统下载,便捷校园生... 你有没有想过,你的手机里装了一个神奇的助手——校务帮安卓系统下载?没错,就是那个能让你轻松管理学校事...
安卓系统没有拼多多,拼多多崛起... 你知道吗?最近我在手机上发现了一个小小的秘密,那就是安卓系统里竟然没有拼多多这个应用!这可真是让我大...
甜城麻将安卓系统,解锁全新麻将... 你有没有听说过那个超级火的甜城麻将安卓系统?没错,就是那个让无数麻将爱好者为之疯狂的软件!今天,就让...
安卓系统卸载的软件,深度揭秘卸... 手机里的软件越来越多,是不是感觉内存不够用了?别急,今天就来教你怎么在安卓系统里卸载那些不再需要的软...
安卓系统推荐好游戏,畅享指尖乐... 手机里的游戏可是咱们休闲娱乐的好伙伴,尤其是安卓系统的用户,选择面那可是相当广呢!今天,就让我来给你...
王者安卓系统怎么卖,揭秘如何轻... 你有没有听说最近王者安卓系统的火爆程度?没错,就是那个让无数玩家沉迷其中的王者荣耀!今天,我就来给你...
安卓开发系统内置证书,基于安卓... 你有没有想过,你的安卓手机里那些神秘的内置证书,它们到底是个啥玩意儿?别急,今天就来给你揭秘这些隐藏...
荣耀安装安卓原生系统,深度体验... 你知道吗?最近荣耀手机界可是掀起了一股热潮,那就是——荣耀安装安卓原生系统!这可不是什么小打小闹,而...
安卓13小米系统,创新功能与流... 你知道吗?最近安卓13系统可谓是风头无两,各大手机厂商纷纷推出自家的新版系统,其中小米的安卓13系统...