进程的创建
引言
进程是资源申请、调度和独立运行的单位,它使用系统中的运行
资源;而程序不能申请系统资源,不能被系统调度,也不能作为独立
运行的单位,它不占用系统的运行资源。
如何通过代码接口取得进程信息。 | ||
---|---|---|
函数名 | 存在原因 | 优点 |
Winexec | 历史遗留,为了兼容老板系统 | 参数少,常用 |
ShellExecute | 历史遗留,为了兼容老板系统 | 参数少,不常用 |
CreateProcess | 日常开发用的多 | 功能全,参数多 |
注意:
上面两个16位系统使用的,使用简单,仍然在重要使用;
第三个CreateProcess要穿10个参数,CreateProcess功能最为齐全;
进程创建的相关函数
Winexec
功能:主要运行EXE文件。如:WinExec(‘Notepad.exe Readme.txt’, SW_SHOW); | ||
---|---|---|
![]() |
||
参数一:命令行参数 | 指向一个0结尾的字符串,要执行的exe,没有带文件夹路径的,或命令行。 | 会自动搜索文件,跟dll的搜索顺序一样;【搜索顺序如下】 |
参数二:窗口显示状态 | SW_SHOW,和ShowWindow的第二个参数的取值一样 | SW_HIDE |
优点: | 好用的地方:如果没填路径,只是有exe的名字,会自动按照以下路劲去搜索。 | |
PS: 启动cmd的时候,需要两个才能填命令行 |
![]() |
|
示例: | ||
1.拷贝, |
![]() |
|
![]() |
使用WinExec
使用MFC —— 在静态库中使用MFC 是为了当程序发生断言或错误,能定位到代码错误的位置,更方便的调试代码。
给按钮id,双击,修改字符集
1、一般使用
启动一个exe,要先有一个exe,搞一个过来;
权限:看你调用的程序是什么权限;
2、cmd使用技巧:
cmd:包含系统路径,写名字就可以执行
1、带命令行参数
要加/c 再接命令、或者/k;
注意/c /k的区别:
/c是执行完之后立马推掉;
/k是执行完命令后之后进程一直保留着
小结:
WinExec可以执行命令——可以隐秘的执行某个命令;
2、一个好玩的东西:(隐秘的执行某个命令举例)
测试一下上图第三个:
WinExec("cmd /k %windir%\\System32\\reg.exe ADD HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System /v EnableLUA /t REG_DWORD /d 0 /f", SW_SHOW);
关闭UAC:
想要隐藏好自己,把自己加入启动项这有点傻,应该加入服务启动与内存常驻。
3、生成一个带小盾牌的exe:
项目-属性-链接器-所有选项
图标会带小盾牌,启动会提示需要管理员权限
ShellExexcute
其实就是加强版的WinExec
功能:ShellExecute的功能是运行一个外部程序(或者是打开一个已注册的文件、打开一个目录、打印一个文件等等)并对外部程序有一定的控制。有几个API函数都可以实现这些功能,但是在大多数情况下ShellExecute是更多的被使用的,同时它并不是太复杂。
HINSTANCE ShellExecute(
HWND hwnd, //父窗口句柄
LPCTSTR lpOperation, //操作, 打开方式"edit","explore","open","find","print","NULL"
LPCTSTR lpFile, //文件名,前面可加路径
LPCTSTR lpParameters, //参数
LPCTSTR lpDirectory, //默认文件夹
INT nShowCmd //显示方式
);
```| | |
| **参数一:指定父窗口句柄** | **如果没有则为**NULL | <br /> |
| **参数二:指定动作** | **取值是一个表。**<br />**操作对象都是**<br />**(lpfile指定的文件夹)** | **NULL, 默认操作"open"**<br />**Edit, 打开文件类型的默认程序**<br />**explore, 打开文件夹**<br />**find, 打开查找窗口**<br />**open, 打开文件或文件夹**<br />**print, 打印机界面(lpfile参数指定的文件)**<br />**runes, 管理员权限,** |
| **参数三:** | **可以是网址,可执行 ** | |
| **参数四:** | **用于指定默认目录。**<br />**若lpFile参数是一个可执行文件,则此参数指定命令行参数** | |
| **参数五:** | **用于指定程序窗口初始显示方式。** | |
| **参数六:** | **用跟WinExec第二个参数一样。** | |
| **PS:** | **一般参数1,4,5都填NULL(可以做封装)** | |
| **示例:** | **打开文件,打开网址,打开资源管理器,打开打印界面** | |
|  | | |
> **_参数无初始显示方式:从下列选择_**
```cpp
#define SW_HIDE 0 /*隐藏窗体,并激活另一个窗体*/
#define SW_SHOWNORMAL 1 /*与SW_RESTORE相同*/
#define SW_SHOWMINIMIZED 2 /*激活并以最小化的形式显示窗体*/
#define SW_SHOWMAXIMIZED 3 /*激活并以最大化的形式显示窗体*/
#define SW_MAXIMIZE 3 /*最大化指定的窗体*/
#define SW_SHOWNOACTIVATE 4 /*以上次的状态显示指定的窗体,但不激活它*/
#define SW_SHOW 5 /*激活窗体,并将其显示在当前的大小和位置上*/
#define SW_MINIMIZE 6 /*最小化指定的窗体,并激活另一个窗体*/
#define SW_SHOWMINNOACTIVE 7 /*以最小化形式显示指定的窗体,但不激活它*/
#define SW_SHOWNA 8 /*以当前的状态显示指定的窗体,但不激活它*/
#define SW_RESTORE 9 /*以原本的大小和位置,激活并显示指定的窗体*/
#define SW_SHOWDEFAULT 10 /*设置显示的状态由STARTUPINFO结构体指定*/
示例:
使用示例:
ShellExecute(NULL, "open", "E:\\CR41\\第2阶段\\Windows\\04-进程和进程遍历\\Test\\Test.txt", NULL, NULL, SW_SHOWNORMAL);
ShellExecute(NULL, "open", "www.baidu.com/s?wd=\"拼音\"", NULL, NULL, SW_SHOW);
ShellExecute(NULL, "explore", "E:\\CR41\\第2阶段\\Windows\\04-进程和进程遍历\\Test", NULL, NULL, SW_SHOWNORMAL);
ShellExecute(NULL, "print", "E:\\CR41\\第2阶段\\Windows\\04-进程和进程遍历\\Test\\Test.txt", NULL, NULL,SW_SHOW);
ShellExecute(NULL, "open", "calc.exe", NULL, NULL, SW_SHOW);
CreateProcess
参数简要说明
功能:用来创建一个新的进程和它的主线程,这个新进程运行指定的可执行文件。 返回值:非零表示成功。零表示失败。要获取扩展的错误信息,请调用 GetLastError。 |
||
---|---|---|
![]() |
||
lpApplicationName | **一个指向NULL终止的字符串,用来指定可执行程序的名称。该名 称可以是该程序的完整路径和文件名,也可以是部分名称。如果是后 者,CreateProcess函数就在当前路径下搜索可执行文件名,但不会使 用搜索路径进行搜索。注意:一定要加上扩展名,系统不会自动假设 文件名有一个“.exe”扩展名。 ** |
|
lpCommandLine | 如果需要搜索,参数1填NULL,支持命令行参数。 | |
lpProcessAttributes | 填NULL,暂时不考虑 | |
lpThreadAttributes | 填NULL,暂时不考虑 | |
bInheritHandles | 填TRUE和FALSE都可以 | |
dwCreationFlags | 创建标志,从表中选,默认给0 | |
**lpEnvironment, ** | 指向新的环境块的指针 ,默认NULL | |
lpCurrentDirectory | 指向新的环境块的指针 ,默认NULL | |
lpStartupInfo | 指向一个用于决定新进程的主窗体如何显示的STARTUPINFO结构体。 | |
lpProcessInformation | 指向一个用来接收新进程的识别信息的PROCESS_INFORMATION结构体 |
dwCreationFlags标志
dwCreationFlags | 含义 | |
---|---|---|
CREATE_DEFAULT_ERROR_MODE | 新的进程不继承调用进程的错误模式。 | |
CREATE_NEW_CONSOLE | 新的进程将使用一个新的控制台,而不是继承父进程的控制台。 | |
CREATE_NEW_PROCESS_GROUP | ||
CREATE_SEPARATE_WOW_VDM | ||
CREATE_SHARED_WOW_VDM | ||
CREATE_SUSPENDED | 挂起进程 | |
CREATE_UNICODE_ENVIRONMENT | ||
DEBUG_PROCESS | ||
DEBUG_ONLY_THIS_PROCESS | ||
DETACHED_PROCESS | ||
CREATE_NO_WINDOW |
lpStartupInfo
创建新进程主窗口结构体
typedef struct _STARTUPINFO
{
DWORD cb; //包含STARTUPINFO结构中的字节数.如果Microsoft将来扩展该结构,它可用作版本控制手段.应用程序必须将cb初始化为sizeof(STARTUPINFO)
PSTR lpReserved; //保留。必须初始化为NULL
PSTR lpDesktop; //用于标识启动应用程序所在的桌面的名字。如果该桌面存在,新进程便与指定的桌面相关联。如果桌面不存在,便创建一个带有默认属性的桌面,并使用为新进程指定的名字。如果lpDesktop是NULL(这是最常见的情况 ),那么该进程将与当前桌面相关联
PSTR lpTitle; //用于设定控制台窗口的名称。如果lpTitle是NULL,则可执行文件的名字将用作窗口名.This parameter must be NULL for GUI or console processes that do not create a new console window.
DWORD dwX; //用于设定应用程序窗口相对屏幕左上角位置的x 坐标(以像素为单位)。
DWORD dwY; //对于GUI processes用CW_USEDEFAULT作为CreateWindow的x、y参数,创建它的第一个重叠窗口。若是创建控制台窗口的应用程序,这些成员用于指明相对控制台窗口的左上角的位置
DWORD dwXSize; //用于设定应用程序窗口的宽度(以像素为单位)
DWORD dwYSize; //子进程将CW_USEDEFAULT 用作CreateWindow 的nWidth、nHeight参数来创建它的第一个重叠窗口。若是创建控制台窗口的应用程序,这些成员将用于指明控制台窗口的宽度
DWORD dwXCountChars; //用于设定子应用程序的控制台窗口的宽度(屏幕显示的字节列)和高度(字节行)(以字符为单位)
DWORD dwYCountChars;
DWORD dwFillAttribute; //用于设定子应用程序的控制台窗口使用的文本和背景颜色
DWORD dwFlags; //请参见下一段和表4-7 的说明
WORD wShowWindow; //用于设定如果子应用程序初次调用的ShowWindow 将SW_*作为nCmdShow 参数传递时,该应用程序的第一个重叠窗口应该如何出现。本成员可以是通常用于ShowWindow 函数的任何一个SW_*标识符,除了SW_SHOWDEFAULT.
WORD cbReserved2; //保留。必须被初始化为0
PBYTE lpReserved2; //保留。必须被初始化为NULL
HANDLE hStdInput; //用于设定供控制台输入和输出用的缓存的句柄。按照默认设置,hStdInput 用于标识键盘缓存,hStdOutput 和hStdError用于标识控制台窗口的缓存
HANDLE hStdOutput;
HANDLE hStdError;
} STARTUPINFO, *LPSTARTUPINFO;
============================================ 使 用 =====================================
当Windows 创建新进程时,它将使用该结构的有关成员。大多数应用程序将要求生成的应用程序仅仅使用默认值。至少应将该结构中的所有成员初始化为零,然后将cb成员设置为该结构的大小:
STARTUPINFO StartupInfo;
char szCMDPath[255];
//配置隐藏窗口结构体
StartupInfo.cb=sizeof(STARTUPINFO);
StartupInfo.wShowWindow=SW_HIDE;
StartupInfo.dwFlags=STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;
StartupInfo.hStdInput=(HANDLE)listenFd;
StartupInfo.hStdOutput=(HANDLE)listenFd;
StartupInfo.hStdError=(HANDLE)listenFd;
//创建匿名管道
PROCESS_INFORMATION ProcessInfo;
//other operation
CreateProcess(NULL,szCMDPath,NULL,NULL,TRUE,0,NULL,NULL,&StartupInfo,&ProcessInfo);
==================================== dwFlags 使用标志及含义 ==========================================================
STARTF_USESIZE //使用dwXSize 和dwYSize 成员
STARTF_USESHOWWINDOW //使用wShowWindow 成员
STARTF_USEPOSITION //使用dwX 和dwY 成员
STARTF_USECOUNTCHARS //使用dwXCountChars 和dwYCount Chars 成员
STARTF_USEFILLATTRIBUTE //使用dwFillAttribute 成员
STARTF_USESTDHANDLES //使用hStdInput 、hStdOutput 和hStdError 成员
STARTF_RUN_FULLSCREEN //强制在x86 计算机上运行的控制台应用程序以全屏幕方式启动运行
指向一个用于决定新进程的主窗体如何显示的STARTUPINFO结构体。
大多数应用程序都希望生成的应用程序只是使用默认值,最起码要全部初始化为0,再把cb成员设为此结构体的大小,如果没有清0,则新进程可能创建失败.
lpProcessInformation
进程信息结构体
typedef struct _PROCESS_INFORMATION {
HANDLE hProcess;
HANDLE hThread;
DWORD dwProcessId;
DWORD dwThreadId;
} PROCESS_INFORMATION;
代码示例解释:
void CCreateProcesslastparameterDlg::OnBnClickedButton1()
{
// TODO: 在此添加控件通知处理程序代码
STARTUPINFO si = {
NULL,//cb,指定结构的大小(以字节为单位)。
NULL,//保留。必须初始化为N U L L
NULL,//lpDesktop
/*用于标识启动应用程序所在的桌面的名字。如果该桌面存在,新进程便与指定的桌面相关联。
如果桌面不存在,便创建一个带有默认属性的桌面,并使用为新进程指定的名字。
如果l p D e s k t o p 是N U L L (这是最常见的情况),那么该进程将与当前桌面相关联*/
"HELLO!",//lpTitle
/* 对于控制台进程,如果创建了一个新的控制台窗口,这是在标题栏中显示的标题。
如果为空,则可执行文件的名称被用作窗口标题。对于不创建新控制台窗口的GUI或控制台进程,此参数必须为NULL*/
300,300,//dwX,dwY
/*用于设定应用程序窗口在屏幕上应该放置的位置的x 和y 坐标(以像素为单位)。
若是创建控制台窗口的应用程序,这些成员用于指明控制台窗口的左上角*/
100,100,//dwXSize,dwYSize
/*用于设定应用程序窗口的宽度和长度(以像素为单位)若是创建控制台窗口的应用程序,
这些成员将用于指明控制台窗口的宽度*/
100,500,//dwXCountChars,dwYCountChars
//用于设定子应用程序的控制台窗口的宽度和高度(以字符为单位)注意:二者一样并不是矩形;
FOREGROUND_BLUE | BACKGROUND_GREEN,//dwFillAttribute
/*FOREGROUND_BLUE | FOREGROUND_GREEN
|FOREGROUND_RED | FOREGROUND_INTENSITY
|BACKGROUND_BLUE|BACKGROUND_GREEN
|BACKGROUND_RED|BACKGROUND_INTENSITY,*/
//用于设定子应用程序的控制台窗口使用的文本和背景颜色
STARTF_USEFILLATTRIBUTE
|STARTF_USESIZE
//|STARTF_USESHOWWINDOW//窗口显示的方式,最大化最小化,
//| STARTF_USEPOSITION//dwX,dwY
|STARTF_USECOUNTCHARS ,//使用d w F i l l A t t r i b u t e 成员
//这是一个位字段,决定进程创建窗口时是否使用某些STARTUPINFO成员。可以指定以下值的任意组合:
//STARTF_USESIZE使用dwXSize和dwYSize 成员
//STARTF_USESHOWWINDOW使用wShowWindow 成员
//STARTF_USEPOSITION使用dwX和dwY 成员
//STARTF_USECOUNTCHARS使用dwXCountChars和dwYCount Chars 成员
//STARTF_USEFILLATTRIBUTE使用dwFillAttribute成员
//STARTF_USESTDHANDLES使用hStdInput、hStdOutput和hStdError成员
//STARTF_RUN_FULLSCREEN强制在x86计算机上运行的控制台应用程序以全屏幕方式启动运行
SW_SHOW,//见表
0,//保留; 必须为零
NULL,保留; 必须为NULL
//最后还有三个参数,忽略
//HANDLE hStdInput;
//HANDLE hStdOutput;
//HANDLE hStdError;
};
si.cb = sizeof(si);
PROCESS_INFORMATION pi = {};
//研究CreateProcess中的STARTUPINFO 中的成员的用法,并提供示例代码
::CreateProcess(NULL,//exe路径
"cmd.exe",//命令行参数
//"calc",
NULL, NULL, FALSE,//暂时不用
0,//默认
NULL,//环境遍历
NULL,//当前目录
&si,
&pi
);
}
杂谈书籍推荐
进程有pid 线程 有tid,不会重复,不可以手动指定,由系统来维护
书籍推荐:《深入解析windows操作系统》第六章
示例:
void CProcessDlg::OnBnClickedButton3()
{
STARTUPINFO si = {};
si.cb = sizeof(si);
PROCESS_INFORMATION pi = {};
BOOL bRet = ::CreateProcess(
"E:\\CR41\\第2阶段\\Windows\\04-进程和进程遍历\\Process\\Debug\\Process - 副本.exe",//exe路径
NULL,//命令行参数
NULL,
NULL,
FALSE,
0,//创建标志,默认给0
NULL,//环境块,环境变量
NULL,//当前目录,工作目录
&si,
&pi
);
if (!bRet)
{
AfxMessageBox("创建进程失败");
}
}
但是CreateProcess 这样使用的话,不具备搜索路径的功能,也就是说lpApplicationName 不为空的话,不会自动搜索路径
如果在lpCommandLine参数中传递了一个可执行的文件名,并且没有包含路
径,lpApplicationName为空就会自动搜索路径。那么这时CreateProcess函数将按照以下顺序搜索可执行文件:
(1)应用程序被装载的目录。
(2)父进程的目录。
(3)32位Windows系统目录。可以使用GetSystemDirectory函数
来得到该目录的路径。
(4)16位 Windows 系统目录。没有函数可以获取该目录的路
径,但该目录会被搜索到,这个目录的名称是System。
(5)Windows目录。可以使用GetWindowsDirectory函数来得到该
目录的路径。
(6)PATH环境变量中列出的目录。
总结:
lpCommandLine参数也可以为空,这时,CreateProcess函数将使用lpApplicationName参数作为命令行。这两个参数的区别在于,如果在 lpApplicationName 参数中指定可执行文件名,那么系统将只在当前目录下查找该可执行文件,如果没有找到,就失败返回。 通常在调用CreateProcess函数时,我们将可执行文件名和命令行参数都传递给lpCommandLine参数。
进程的退出
进程终止 | ||
---|---|---|
![]() |
||
1. 主线程的进入点函数返回(最好使用这个方法),给句柄发送close消息自动撤销 | ||
2. 进程中的一个线程调用ExitProcess函数(尽量避免) | ||
3. 另外一个进程中的线程调用TerminateProcess函数(尽量避免) | ||
4. 进程中的所有线程自行终止运行(这种情况几乎从未发生) | ||
PS:ExitPorcess 在执行后程序就直接退出终止,不会进行内存的释放等。 |
进程退出的相关函数
1、从main函数返回
推荐使用,寿终正寝;
2、ExitProcess
- 酌情使用,只能结束自己;
- 调用时即结束,注意调用前清理资源
- dll会被调入dllmain,并传入DLL_PROCESS_DETTACH标志
API介绍
参数:传入一个退出码
直接退出进程 不会管后面资源释放等问题
例如:
void CProcessDlg::OnBnClickedButton4()
{
ExitProcess(0);
int n =100;
}
3、TerminateProcess
功能: | 终止指定的进程及其所有线程。通常由别的进程调用。 | |
---|---|---|
参数: | 句柄、参数码 | |
弊端: | 不知道进程运行到哪里,有没有申请内存,等,不会释放内存。 | |
进程在退出的时候通常是会释放的,系统申请的东西都会释放,自己代码申请的就不会释放。调用该函数则直接关闭进程 dll不会被调入dllmain,dll无法收到DLL_PROCESS_DETTACH标志 |
||
总结: | 最好的退出办法给进程发送close消息,让他自己撤销资源等,这样才能长时间的运行。否则其他方法难免有遗漏的地方。 |
使用API
g_pi 是CreateProcess的最后一个参数;
GetCurrentProcess
获取自己进程句柄的函数
GetExitCodeProcess
BOOL GetExitCodeProcess(
HANDLE hProcess, // 进程句柄
LPDWORD lpExitCode // 终止状态,结束返回码
);
应用场景: | 守护进程,判断进程是否正常退出,如果不是就继续给他重启软件。 | |
---|---|---|
宏:STILL_ACTIVE | 当进程未终止,返回的是这个宏0x103,终止返回退出码 | |
![]() |
4、三种退出的区别
1、两个API的区别:
ExitProcess只能结束自己
TerminateProcess 结束指定结束自己
2、推荐从main函数退出
如果上面两个API后面还有代码,上面两种方式是不会执行后面代码的
举例:
class CFoo
{
public:
CFoo() {
AfxMessageBox("构造函数");
}
~CFoo() {
AfxMessageBox("析构函数");
}
};
void CProcessDlg::OnBnClickedButton4()
{
CFoo cfoo;
ExitProcess(0);
}
正常情况下是:构造析构都可以,但是结束进程了,没有析构;局部变量没有释放资源的地方;影响不是很大。
所以我们在调用退出的API要注意释放资源,清理资源,先做完去退出;
TerminateProcess更不推荐,它结束其他进程,其他进程更没有机会清理资源;压根不知道进程是什么时候被结束的;
5、进程终止运行时出现的情况
终止进程会导致以下后果:
进程打开的所有对象句柄都被关闭。
进程中的所有线程都会终止它们的执行。
进程对象的状态变为有信号状态,满足所有等待进程终止的线程。
进程中所有线程的状态变为有信号状态,满足所有等待线程终止的线程。
进程的终止状态从STILL_ACTIVE变为进程的退出值。
6 、进程退出编程的意义
当我们创建一个进程,进程会得到一个4g的虚拟内存,exe的代码、dll都会映射到进程;当进程退出的时候,映射关系都会取消,4g内存也会被销毁(里面的东西)——4g内存申请的堆,栈,就也都没了;进程打开的句柄也全部被关闭;
问:既然进程退掉 4g的虚拟内存映射取消,那我申请的堆是不是就可以不管,让系统释放,可不可以这样?
答:千万不能这么做,进程不是一下就映射4g虚拟内存(all in),而是进程打开是用多少内存,映射多少内存,这个时候,凡是你申请的内存都会占用物理内存(不够交换到磁盘),此时如果不自己释放,物理内存会不断减少,不够就会交换到磁盘,频繁的操作磁盘会影响内存的读写速度,交换速度越来越慢,卡!所以在内存管理的时候一定要释放!不要依赖进程退出的时候给你释放!
进程的遍历
1、快照遍历
什么是快照:
虚拟中的快照: 我们在分析病毒,分析木马的时候,不能在真机分析,在虚拟机中分析,把病毒丢到虚拟机中,分析之前把环境配好,拍照保存;然后进行分析,分析过后,想从头再分析,就把快照一恢复,就可以恢复成刚配好环境的虚拟机;
快照遍历进程的思路:
我们遍历进程也是通过快照的方式,把当前的系统的信息创建一份快照——(创建快照的)那一刻所有进程的信息,到快照里面浏览这些信息;
2、相关API介绍
1、CreateToolhelp32Snapshot
参数:
1、标志;你创建这个快照要包含什么信息;
2、进程id – 当你需要看创建指定进程模块,和线程快照才用到,对于进程来说没啥用;
拿到快照之后,获取快照里面的信息需要其他API:
2、Process32First
参数:
句柄,快照的句柄;
传入传出参数, 点击上图蓝色文字,有一个结构体,只有第一个成员(dwSize——本结构体的大小)是需要我们填的;其他的都是返回给我们的;
3、Process32Next
参数和first的一样;
4、示例
Process32Next往下翻下图链接有使用例子;
MSDN中的示例代码
#include <windows.h>
#include <tlhelp32.h>
#include <stdio.h>
BOOL GetProcessList ()
{
HANDLE hProcessSnap = NULL;
BOOL bRet = FALSE;
PROCESSENTRY32 pe32 = {0};
// Take a snapshot of all processes in the system.
hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hProcessSnap == INVALID_HANDLE_VALUE)
return (FALSE);
// Fill in the size of the structure before using it.
pe32.dwSize = sizeof(PROCESSENTRY32);
// Walk the snapshot of the processes, and for each process,
// display information.
if (Process32First(hProcessSnap, &pe32))
{
DWORD dwPriorityClass;
BOOL bGotModule = FALSE;
MODULEENTRY32 me32 = {0};
do
{
bGotModule = GetProcessModule(pe32.th32ProcessID,
pe32.th32ModuleID, &me32, sizeof(MODULEENTRY32));
if (bGotModule)
{
HANDLE hProcess;
// Get the actual priority class.
hProcess = OpenProcess (PROCESS_ALL_ACCESS,
FALSE, pe32.th32ProcessID);
dwPriorityClass = GetPriorityClass (hProcess);
CloseHandle (hProcess);
// Print the process's information.
printf( "\nPriority Class Base\t%d\n",
pe32.pcPriClassBase);
printf( "PID\t\t\t%d\n", pe32.th32ProcessID);
printf( "Thread Count\t\t%d\n", pe32.cntThreads);
printf( "Module Name\t\t%s\n", me32.szModule);
printf( "Full Path\t\t%s\n\n", me32.szExePath);
}
}
while (Process32Next(hProcessSnap, &pe32));
bRet = TRUE;
}
else
bRet = FALSE; // could not walk the list of processes
// Do not forget to clean up the snapshot object.
CloseHandle (hProcessSnap);
return (bRet);
}
示例:
#include <tlhelp32.h>
void CProcessDlg::OnBnClickedButton6()
{
BOOL bRet = FALSE;
PROCESSENTRY32 pe32 = {0};
//创建快照
HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
//从快照中获取进程信息
pe32.dwSize = sizeof(PROCESSENTRY32);
if (Process32First(hProcessSnap, &pe32))
{
do
{
CString strFmt;
strFmt.Format("[proc] PID: %d [path:%s", pe32.th32ProcessID, pe32.szExeFile);
OutputDebugString(strFmt);
} while (Process32Next(hProcessSnap, &pe32));
}
CloseHandle(hProcessSnap);
}
使用DebugView 查看
新建工程
对MSDN的代码进行修改
// Travel.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//遍历当前进程信息
#include <iostream>
#include <windows.h>
#include <tlhelp32.h>
#include <stdio.h>
int main()
{
HANDLE hProcessSnap = NULL;
//创建,获取进程的信息
hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hProcessSnap == INVALID_HANDLE_VALUE)
return (FALSE);
//结构体
PROCESSENTRY32 pe32 = { 0 };
//填写结构体中的第一个成员
pe32.dwSize = sizeof(PROCESSENTRY32);
if (Process32First(hProcessSnap, &pe32))
{
//First成功之后,进入循环
//循环的时候调一下next,通过xxx拿一下进程的信息
do
{
printf("pid:%d\t name:%s\r\n", pe32.th32ProcessID, pe32.szExeFile);
} while (Process32Next(hProcessSnap, &pe32));
}
CloseHandle(hProcessSnap);
}
效果:
注意:
和资源管理器中进程对比是一样的;
和Process Hacker 中进程对比少,我们的进程是三环下的,Process Hacker是可以拿到内核进程的;
3、无序遍历
窗口遍历
EnumChildWindows
GetNextWIndow
Spy++桌面上所有窗口思路
所有的窗口的是桌面窗口的子窗口,拿子窗口,然后GetNextWIndow遍历,同时遍历所有的子窗口,递归
GetDesktopwindow – 拿到左面窗口
GetChildWindow – 拿到桌面的子窗口
GetNextWIndow – 遍历同层的子窗口
补充:
病毒的种类以及运行技巧。(让电脑放松警惕)
名词UAC,关闭用户控制设置。提权用【后期病毒可用】
病毒种类:母体(依附,释放子体),子体(改数据,传输等)。
附录:
WinExec:Cmd命令行参数
::WinExec("E:\\CR40\\windows\\03\\MySpyPlusPlus.exe", SW_SHOW );
/*
/k - 执行命令,保持窗口显示
/c - 执行命令,然后关闭窗口
*/
::WinExec("cmd /k dir", SW_SHOW);
::WinExec("cmd /k %windir%\\System32\\reg.exe ADD HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System /v EnableLUA /t REG_DWORD /d 0 /f", SW_SHOW);
ShellExecute:参数2模式的示例
ShellExecute(NULL, "open", "cmd", "/k dir", NULL, SW_SHOW);
ShellExecute(NULL, "open", "E:\\CR40\\windows\\03\\进程(1).ppt", NULL, NULL,SW_SHOWNORMAL);
ShellExecute(NULL, "open", "www.baidu.com/s?wd=\"拼音\"", NULL, NULL, SW_SHOW);
ShellExecute(NULL, "explore", "E:\\CR40", NULL, NULL, SW_SHOWNORMAL);
ShellExecute(NULL, "print", "E:\\CR40\\windows\\03\\进程(1).ppt", NULL, NULL, SW_HIDE);
CreateProcess示例:
TARTUPINFO si = {}; //环境块结构体
si.cb = sizeof(si);
PROCESS_INFORMATION pi = {}; //进程信息结构体
BOOL bRet = ::CreateProcess(
NULL,//exe路径,
"cmd /k dir c:\\",//命令行参数,可以是文件,会自动搜索
NULL, NULL, TRUE, //暂时不考虑
0,//默认给0
NULL, NULL,
&si,
&pi
);
if (!bRet)
{
AfxMessageBox("创建进程失败!!!");
}
GetExitCodeProcess示例:
DWORD dwExitCode = 0;
::GetExitCodeProcess(g_hProc, &dwExitCode);
CString str;
str.Format("%08X", dwExitCode);
AfxMessageBox(str);
它的错误码来源主要有以下三点:
1. ExitProcess(错误码);
2. TerminateProcess(进程句柄,错误码)
3. 异常终止或则正常进程退出错误码
ExitProcess:终止进程
ExitProcess(错误码); //之后的代码不会执行,同样也不会执行资源的撤销。
窗口遍历
#include <iostream>
using namespace std;
#include <windows.h>
#include <tlhelp32.h>
#include <stdio.h>
BOOL GetProcessModule(DWORD dwPID, char* szExeName,
LPMODULEENTRY32 lpMe32, DWORD cbMe32)
{
BOOL bRet = FALSE;
BOOL bFound = FALSE;
HANDLE hModuleSnap = NULL;
MODULEENTRY32 me32 = { 0 };
// Take a snapshot of all modules in the specified process.
hModuleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwPID);
if (hModuleSnap == INVALID_HANDLE_VALUE)
return (FALSE);
// Fill the size of the structure before using it.
me32.dwSize = sizeof(MODULEENTRY32);
// Walk the module list of the process, and find the module of
// interest. Then copy the information to the buffer pointed
// to by lpMe32 so that it can be returned to the caller.
if (Module32First(hModuleSnap, &me32))
{
do
{
if (strcmp(szExeName, me32.szModule) == 0)
{
CopyMemory(lpMe32, &me32, cbMe32);
bFound = TRUE;
}
} while (!bFound && Module32Next(hModuleSnap, &me32));
bRet = bFound; // if this sets bRet to FALSE, dwModuleID
// no longer exists in specified process
}
else
bRet = FALSE; // could not walk module list
CloseHandle(hModuleSnap);
return (bRet);
}
BOOL GetProcessList()
{
HANDLE hProcessSnap = NULL;
BOOL bRet = FALSE;
PROCESSENTRY32 pe32 = { 0 };
// Take a snapshot of all processes in the system.
hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hProcessSnap == INVALID_HANDLE_VALUE)
return (FALSE);
// Fill in the size of the structure before using it.
pe32.dwSize = sizeof(PROCESSENTRY32);
// Walk the snapshot of the processes, and for each process,
// display information.
if (Process32First(hProcessSnap, &pe32))
{
DWORD dwPriorityClass;
BOOL bGotModule = FALSE;
MODULEENTRY32 me32 = { 0 };
do
{
bGotModule = GetProcessModule(pe32.th32ProcessID,
pe32.szExeFile, &me32, sizeof(MODULEENTRY32));
if (bGotModule)
{
HANDLE hProcess;
// Get the actual priority class.
hProcess = OpenProcess(PROCESS_ALL_ACCESS,
FALSE, pe32.th32ProcessID);
dwPriorityClass = GetPriorityClass(hProcess);
CloseHandle(hProcess);
// Print the process's information.
printf("\nPriority Class Base\t%d\n",
pe32.pcPriClassBase);
printf("PID\t\t\t%d\n", pe32.th32ProcessID);
printf("Thread Count\t\t%d\n", pe32.cntThreads);
printf("Module Name\t\t%s\n", me32.szModule);
printf("Full Path\t\t%s\n\n", me32.szExePath);
}
} while (Process32Next(hProcessSnap, &pe32));
bRet = TRUE;
}
else
bRet = FALSE; // could not walk the list of processes
// Do not forget to clean up the snapshot object.
CloseHandle(hProcessSnap);
return (bRet);
}
BOOL CALLBACK EnumWindowsProc(HWND hwnd, // handle to parent window
LPARAM lParam // application-defined value
)
{
char szBuff[MAXBYTE] = {};
GetWindowText(hwnd, szBuff, MAXBYTE);
printf("%s \r\n", szBuff);
return TRUE;//一直遍历,直到所有窗口都被遍历完
}
int main()
{
//GetProcessList();
EnumWindows(EnumWindowsProc, NULL);
std::cout << "Hello World!\n";
}
作业
1、(周)进程遍历工具(进程,线程,模块,堆),可以结束指定进程,打开文件位置
**2、**程序自删除
3、研究CreatePorcess的STARTUPINFO 中的成员的用法,并提供示例代码-不看23和最后5个
- 进程查看工具(周作业)ProcessHaker
功能:- 进程
- 查看线程
- 查看模块
- 查看窗口
- 定位可执行文件位置
- 结束进程