04-进程和进程遍历

进程的创建

引言

进程是资源申请、调度和独立运行的单位,它使用系统中的运行
资源;而程序不能申请系统资源,不能被系统调度,也不能作为独立
运行的单位,它不占用系统的运行资源。

如何通过代码接口取得进程信息。
函数名 存在原因 优点
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.拷贝,
2.打开文件。
3.修改注册表
(病毒里面提取出来的)

使用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(可以做封装)** |  |
| **示例:** | **打开文件,打开网址,打开资源管理器,打开打印界面** |  |
| ![image.png](https://img-blog.csdnimg.cn/img_convert/ea8369f7bb44927c4aafba31e7606fc2.png#averageHue=#d0ccc6&clientId=u0f63d6be-ee16-4&from=paste&height=401&id=u904c465f&name=image.png&originHeight=541&originWidth=976&originalType=binary&ratio=1.5&rotation=0&showTitle=false&size=286263&status=done&style=none&taskId=udda95297-1392-4364-90b2-e1e26a9e725&title=&width=722.6666870117188) |  |  |

> **_参数无初始显示方式:从下列选择_**

```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

  1. 酌情使用,只能结束自己;
  2. 调用时即结束,注意调用前清理资源
  3. 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_piCreateProcess的最后一个参数;

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个

  1. 进程查看工具(周作业)ProcessHaker
    功能:
    1. 进程
    2. 查看线程
    3. 查看模块
    4. 查看窗口
    5. 定位可执行文件位置
    6. 结束进程