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;
}

相关内容

热门资讯

安卓系统双清详图解,恢复出厂设... 你有没有遇到过手机卡顿、运行缓慢的问题?别急,今天就来给你详细解析一下安卓系统的“双清”操作,让你的...
召唤抽奖系统安卓直装,轻松体验... 你知道吗?现在市面上有一种特别火的玩意儿,那就是召唤抽奖系统安卓直装。是不是听起来就让人心动不已?没...
系统工具箱安卓2.3,深度解析... 你有没有发现,手机里的那些小工具,有时候就像是个神奇的百宝箱呢?今天,就让我带你一探究竟,看看安卓2...
华硕平板安卓刷机系统,解锁性能... 亲爱的数码爱好者们,你是否曾为你的华硕平板安卓系统感到厌倦,想要给它来一次焕然一新的体验呢?那就跟着...
鸿蒙系统与安卓怎么区别,差异解... 你有没有发现,最近手机圈子里有个大热门,那就是鸿蒙系统和安卓系统的区别。这两位“系统大侠”各有各的绝...
红帽系统怎么刷回安卓,红帽系统... 你是不是也和我一样,对红帽系统刷回安卓充满了好奇?别急,今天就来给你详细揭秘这个过程,让你轻松上手,...
ios安卓联想三系统,全面解析... 你有没有发现,现在的手机市场真是热闹非凡呢!各种操作系统轮番登场,让人眼花缭乱。今天,就让我带你来聊...
安卓调用系统相机并存盘,And... 你有没有想过,手机里的照片和视频,是怎么被我们随手拍下,又神奇地存到手机里的呢?今天,就让我带你一探...
安卓4.0原生系统下,引领智能... 你有没有发现,安卓4.0原生系统下,手机的使用体验简直就像打开了新世界的大门?今天,就让我带你一起探...
安卓c13系统,创新功能与性能... 你知道吗?最近安卓系统又来了一次大更新,那就是安卓C13系统。这可不是一个小打小闹的更新,而是带来了...
鸿蒙3.0脱离安卓系统,开启全... 你知道吗?最近科技圈可是炸开了锅,因为华为的新操作系统鸿蒙3.0横空出世,竟然宣布要脱离安卓系统,这...
安卓怎么应对苹果系统,安卓系统... 你知道吗?在智能手机的世界里,安卓和苹果就像是一对相爱相杀的恋人。安卓系统,这位多才多艺的“大众情人...
安卓系统如何开橱窗教程,安卓系... 你有没有想过,你的安卓手机里也能开个橱窗,展示那些你心爱的宝贝?没错,就是那种可以随时翻看、随时分享...
安卓系统软件APK,深入探究安... 你有没有发现,手机里的那些好玩的应用,其实都是靠一个小小的文件来“住”进去的?没错,就是安卓系统里的...
css安卓系统line-hei... 你有没有发现,在使用安卓手机的时候,有时候文字看起来会有些挤,不够舒展呢?这可真是让人头疼的小问题。...
如何换桌面安卓系统,轻松切换个... 你是不是也和我一样,对安卓系统的桌面换换口味,想要来点新鲜感呢?那就跟我一起探索如何轻松给安卓桌面来...
安卓移动到系统目录lib,An... 你有没有想过,你的安卓手机里那些神秘的系统目录里藏着什么秘密?今天,就让我带你一探究竟,揭开安卓移动...
安卓系统神器软件下载,畅享智能... 你有没有发现,手机里装满了各种应用,但总有一些神器级的软件,让你的生活和工作变得轻松愉快呢?今天,就...
安卓系统怎么创小号教程,安卓系... 你是不是也和我一样,对安卓系统的小号功能充满了好奇?想要在同一个设备上玩转多个账号,但又不知道怎么操...
华为安卓9.0系统图库,探索创... 你知道吗?最近华为出了一款搭载安卓9.0系统的手机,那叫一个火啊!这款手机不仅外观时尚,性能强大,而...