所使用的Windows API 原型

CreateToolhelp32Snapshot 函数 (tlhelp32.h)

CreateToolhelp32Snapshot API功能是获取指定进程以及这些进程使用的堆,模块和线程的快照

HANDLE CreateToolhelp32Snapshot(
[in] DWORD dwFlags,//要包含在快照中的系统部分
[in] DWORD th32ProcessID//系统进程标识符
);

OpenProcess函数(processthreadsapi.h)

HANDLE OpenProcess(//打开现有的本地进程对象。
[in] DWORD dwDesiredAccess,//对进程对象的访问,参数是一个进程的访问权限。具体的参数可以查看处理安全性和访问权限 - Win32 apps | Microsoft Learn
[in] BOOL bInheritHandle,//一个BOOL值 使用TRUE,进程创建的子进程会继承这个句柄(对系统进程的枚举使用FALSE)。
[in] DWORD dwProcessId//进程的PID
);

Process32First 函数 (tlhelp32.h)

BOOL Process32First API功能检索有关系统快照的第一个进程的信息(

[in] HANDLE hSnapshot,//快照的句柄,该句柄是从CreateToolhelp32Snapshot函数返回的
[in, out] LPPROCESSENTRY32 //指向PROCESSSNTRY32的结构的指针 它包含进程信息 比如可执行文件的名称 进程标识符 和父进程的标识符
);

Process32Next 函数 (tlhelp32.h)

BOOL Process32Next 检索快照中记录的下一个进程的信息(

[in] HANDLE hSnapshot,//从CreateToolhelp32Snapshot函数的上一次调用返回的快照句柄
[out] LPPROCESSENTRY32W lppe//指向PROCESSENTRY32W结构的指针
);

Module32First 函数 (tlhelp32.h)

BOOL Module32First(//检索与进程相关联的第一个模块的相关信息
[in] HANDLE hSnapshot,//从CreateToolhelp32Snapshot函数的上一次调用返回的快照句柄
[in, out] LPMODULEENTRY32 lpme//指向MODULEENTRY32结构的指针
);

Module32Next 函数 (tlhelp32.h)

Module32Next//检索与进程或线程关联的下一个模块的相关信息

BOOL Module32Next(
[in] HANDLE hSnapshot,//CreateToolhelp32Snapshot函数返回的句柄
[out] LPMODULEENTRY32 lpme//指向MODULEENTRY32结构的指针
);

closeHandle 函数 (handleapi.h)

BOOL CloseHandle(//关闭对象打开的进程
[in] HANDLE hObject//打开的进程句柄
);

代码

代码功能要求

代码功能为对系统运行的进程进行检测,并输出当前进程所使用的模块和调用模块的PID,调用该进程的父进程。(代码未写出主函数,需要使用自行写入 代码参考MSCN文档[枚举进程的所有模块 - Win32 apps | Microsoft Learn](https://learn.microsoft.com/zh-cn/windows/win32/psapi/enumerating-all-modules-for-a-process))

代码功能实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
BOOL GetProcessList()

{

HANDLEhProcessSnap;

HANDLEhProcess;

PROCESSENTRY32pe32;

DWORDdwPriorityClass;

hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPALL,0);

if (hProcessSnap==INVALID_HANDLE_VALUE)

{

return FALSE;

}

FILE*outputFile = fopen("output.txt","w");//将结果输出到一个文件

if (outputFile==NULL)

{

CloseHandle(hProcessSnap);

returnFALSE;

}

pe32.dwSize = sizeof(PROCESSENTRY32);

if (!Process32First(hProcessSnap,&pe32))//检索第一个进程

{

CloseHandle(hProcessSnap);

return FALSE;

}

do

{

fprintf(outputFile,"ProcessName: %s\n",pe32.szExeFile);

dwPriorityClass = 0;

hProcess = OpenProcess(PROCESS_ALL_ACCESS,FALSE,pe32.th32ProcessID);//返回检索进程对象的句柄

if (hProcess==NULL)

CloseHandle(hProcess);

else

{

dwPriorityClass = GetPriorityClass(hProcess);

if (!dwPriorityClass)

CloseHandle(hProcess);

else

{

// 调用函数来枚举模块

ListProcessModule(pe32.th32ProcessID,outputFile);

CloseHandle(hProcess);

}

}

fprintf(outputFile,"ProcessId=0x%08X\n",pe32.th32ProcessID);

fprintf(outputFile,"ParentProcessId= 0x%08X\n",pe32.th32ParentProcessID);

} while (Process32Next(hProcessSnap,&pe32));

CloseHandle(hProcessSnap);

return TRUE;

}

BOOL ListProcessModule(DWORDdwPID,FILE*outputFile)

{

HANDLEhModuleSnap = INVALID_HANDLE_VALUE;

MODULEENTRY32me32;

hModuleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE,dwPID);

if (hModuleSnap==INVALID_HANDLE_VALUE)//**INVALID_HANDLE_VALUE是CreateToolhelp32Snapshot函数的返回值,创建快照失败所返回。**

{

return FALSE;

}

me32.dwSize = sizeof(MODULEENTRY32);

if (!Module32First(hModuleSnap,&me32))//**检索对应进程下的模块**

{

CloseHandle(hModuleSnap);

return FALSE;

}

do

{

fprintf(outputFile,"MODULE NAME: %s\n\n",me32.szModule);

fprintf(outputFile,"Process ID = 0x%08X\n",me32.th32ProcessID);

} while (Module32Next(hModuleSnap,&me32));

CloseHandle(hModuleSnap);

return TRUE;

}

代码功能测试

测试程序在高内存使用时候是否可以正常进行使用,所以运行slay the spire.exe 进行测试。但是在运行时并没有找到slay the spire.exe这个进程,有一个javaw.exe在运行当时发现这个进程但是没有注意(javaw.exe虚拟机中运行了这个java架构的游戏,java代码的运行是实际上是在java虚拟机中运行的)。大佬建议我使用进程命令行去查找,是否程序是进程代理启动,java代码对游戏进行了启动。

jps(JVM命令)

1
jps -v

命令可以显示java虚拟机启动传递的JVM参数

1
jps-l

命令可以枚举所有正在运行的PID和进程名,查找对应的进程号可以确定端口号

netstat -ano |findstr <PID>

命令可以枚举所有端口 |findstr 可以查看指定的PID 显示哪一个进程占用了端口。

1
2

vmic process where caption="javaw.exe" get processid,caption,commandline /value

命令可以检查对应的进程的详细信息

代码在两个状态下运行,左图是未运行状态,右图是运行状态。运行状态下多了三个使用的模块。

img

img

img

事实上代码并没有在高内存占用的情况下崩溃。