《windows核心编程》第4章 进程

news2024/12/28 17:41:24

一、进程基本概念

1、进程:一个进程就是一个正在运行的程序,一个程序可以产生多个进程。进程包含下面两个东西

进程内核对象一个内核对象被系统用来管理某个进程,内核对象就是代表这个进程。这个内核对象中,还包含了进程的一些策略,策略信息是系统怎么给进程分配时间、怎么调内核的优先级

● 地址空间:地址空间中包含了可执行代码、动态链接库模块代码、数据、程序动态内存分配获取的内存。

2、进程的特性

进程是静态的,进程中真正执行代码的是线程,所以进程必须有一个主线程primarythread。两种方法退出进程,第一如果进程内所有线程退出,那么进程自动销毁;第二使用函数ExitProcess函数强制退出当前进程

把进程比喻成工厂,把工厂中的人比作线程,工厂给人提供资源(场地、加工设备、原材料),主线程是随进程产生(工厂产生的时候同时产生它的厂长)。厂长工作第一件事就是招人,线程也是一样,产生后,就生成其他线程开始干活了。

二、WINDOW程序执行流程

Windows程序分两种,一个是控制台程序CUI、一个是图形界面程序GUI。如果我们选错了,可以在下图相应的位置修改项目属性。

我们所编写的windows程序中,真正第一个被执行的函数是:

WinMainCRTStartup // GUI_ASCII字符类型C运行时库的入口函数

wWinMainCRTStartup // GUI_UNICODE字符类型C运行时库的入口函数

mainCRTStartup // CUI_ASCII字符类型C运行时库的入口函数

WmainCRTStartup // CUI_UNICODE字符类型C运行时库的入口函数

C运行时库函数,主要完成以下任务

1. 获取进程命令行指针;

2. 获取进程环境变量指针;

3. 初始化C/C++运行时库的全局变量,如果你包含了头stdlib.h,那么你就可以访问这些变量;

4. 初始化malloc函数的内存堆;

5. 为C++全局类,调用构造函数。

C语言库函数,例如malloc函数最终会调用windowsAPI函数,我们直接调用virtualAlloc windowsAPl函数,效率会更高

程序执行流程

-> Kernerl调用XXXCRTStartup函数

-> XXXCRTStartup函数调用main函数

-> main函数退出

-> exit函数被调用(真正调用的是crt0dat.c中的static void __cdecl doexit(int code int quick, int retcaller);

exit函数主要做以下事情

1. 调用_onexit函数注册的所有函数;

2. 调用C++销毁函数销毁所有的全局和静态类对象;

3. 如果_CRTDBG_LEAK_CHECK_DF标志被设置,那么调用CrtDumpMemoryLeaks函数,列出泄露内存;

4. 调用ExitProcess函数,系統杀死当前进程

如果我们直接调用ExitProcess,那么前面的内存释放工作都不会做,所以可以会存在一些问题

三、程序运行系统和版本

#include <stdio.h>

#include <windows.h>

//操作系统版本

#define WINXP 51

#define WINXP2600 512600

#define WIN7 61

#define WIN77600 617600

#define WIN77601 617601

#define WIN8 62

#define WIN89200 629200

#define WIN81 63

#define WIN819600 639600

#define WIN10 100

#define WIN1010240 10010240

#define WIN1010586 10010586

#define WIN1014393 10014393

int main(void)

{

//定义变量

typedef LONG(__stdcall *fnRtlGetVersion)(PRTL_OSVERSIONINFOW lpVersionInformation);

fnRtlGetVersion pRtlGetVersion;

HMODULE hNtdll;

LONG ntStatus;

ULONG    dwMajorVersion = 0;

ULONG    dwMinorVersion = 0;

ULONG    dwBuildNumber = 0;

RTL_OSVERSIONINFOW VersionInformation = { 0 };

DWORD OsVersion;

do

{

hNtdll = GetModuleHandle(L"ntdll.dll");

if (hNtdll == NULL)break;

pRtlGetVersion = (fnRtlGetVersion)GetProcAddress(hNtdll, "RtlGetVersion");

if (pRtlGetVersion == NULL)break;

VersionInformation.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOW);

ntStatus = pRtlGetVersion(&VersionInformation);

if (ntStatus != 0)break;

dwMajorVersion = VersionInformation.dwMajorVersion;

dwMinorVersion = VersionInformation.dwMinorVersion;

dwBuildNumber = VersionInformation.dwBuildNumber;

if (dwMajorVersion == 5 && dwMinorVersion == 1 && dwBuildNumber == 2600)

OsVersion = WINXP2600;

else if (dwMajorVersion == 5 && dwMinorVersion == 1)

OsVersion = WINXP;

else if (dwMajorVersion == 6 && dwMinorVersion == 1 && dwBuildNumber == 7601)

OsVersion = WIN77601;

else if (dwMajorVersion == 6 && dwMinorVersion == 1 && dwBuildNumber == 7600)

OsVersion = WIN77600;

else if (dwMajorVersion == 6 && dwMinorVersion == 1)

OsVersion = WIN7;

else if (dwMajorVersion == 6 && dwMinorVersion == 2 && dwBuildNumber == 9200)

OsVersion = WIN89200;

else if (dwMajorVersion == 6 && dwMinorVersion == 2)

OsVersion = WIN8;

else if (dwMajorVersion == 6 && dwMinorVersion == 3 && dwBuildNumber == 9600)

OsVersion = WIN819600;

else if (dwMajorVersion == 6 && dwMinorVersion == 3)

OsVersion = WIN81;

else if (dwMajorVersion == 10 && dwMinorVersion == 0 && dwBuildNumber == 10240)

OsVersion = WIN1010240;

else if (dwMajorVersion == 10 && dwMinorVersion == 0 && dwBuildNumber == 10586)

OsVersion = WIN1010586;

else if (dwMajorVersion == 10 && dwMinorVersion == 0 && dwBuildNumber == 14393)

OsVersion = WIN1014393;

else if (dwMajorVersion == 10 && dwMinorVersion == 0)

OsVersion = WIN10;

else

{

return FALSE;

}

} while (FALSE);

printf("%d\n", OsVersion);

getchar();

getchar();

return 0;

}

四、进程句柄

1、进程句柄,要和进程内核对象的句柄区分开来,进程句柄也可以叫做模块句柄。每个可执行文件或者DLL文件被装入到某个进程的地址空间,都会有一个唯一的实例句柄,来表示装入后的可执行文件或者DLL,此时把这个可执行文件或者DLL叫做进程地址空间中的一个模块。

进程句柄(HINS)在程序很多地方都被使用,尤其是在装入某一个资源的时候,下面代码是装入图标资源的时候使用进程句柄hInstance:

LoadIcon(

HINSTANCE hInstance;

PCTSTR pszIcon);

2、获得进程句柄的两种方法

方法1:WinMain函数的参数

WinMain函数的第一个参数就是当前可执行程序的进程句柄

方法2:使用GetModuleHandle函数

在DLL中调用GetModuleHandle返回的不是DLL模块的地址,而是当前进程的模块地址

这个函数只检查本进程地址空间,不检查别的进程的地址空间

3、有进程句柄获取进程模块的路径和名字

DWORD GetModuleFileName (

HMODULE hInstance,

PTSTR pszPath,

DWORD cchPath);

五、进程命令行参数

方法1:入口函数传入参数

方法2:GetCommandLine();

六、环境变量操作

6.1 获取当前环境变量

方法1:使用全局变量

方法2:放在函数里

环境变量被保存放在注册表中

这个注册表键下放的是系统环境变量:HKEY_LOCAL_MACHINE \ SYSTEM \ CurrentControlSer \ Control \ Session Manager \ Environment

这个注册表键下放的是当前登陆用户自己的环境变量:HKEY_CURRENT_USER \ Environment

在你的程序中,如果你改变了环境变量,那么最好你发送一个消息:WM_SETTINGCHANGE。发这个消息时,不要使用某一个窗口的句柄,应该使用:HWND_BROADCAST作为窗口句柄。使用SendMessage或者SendMessageTimeout函数来发送这个消息,IParam参数应该设置成"Environment"。

#include <windows.h>

#include <stdio.h>

#include <fcntl.h>

#include <io.h>

int main()

{

wchar_t buf[1024];

DWORD i = GetEnvironmentVariableW(TEXT("Path"), buf, 1024);

_setmode(_fileno(stdout), _O_U16TEXT);

wprintf(TEXT("str: %s\n"), buf);

//SetEnvironmentVariable(L"asd", L"qwe");

getchar();

return 0;

}

6.2 改变本进程的环境变量

修改系统环境变量要改注册表

6.3 解析系统变量中的%

七、进程目录操作

7.1 GetCurentDirectory获取进程的当前目录

DWORD WINAPI GetCurrentDirectory(

___in DWORD nBufferLength,

___out LPTSTR IpBuffer

);

nBufferLength: lpBuffer指针指向内存块的大小 (单位TCHAR) ;

lpBuffer: 接收当前路径的内存块。

7.2 SetCurrentDirectory设置进程当前目录

_chdir: 设置当前目录

7.3 代码测试

#include <windows.h>

#include <stdio.h>

#include <fcntl.h>

#include <io.h>

int main()

{

_setmode(_fileno(stdout), _O_U16TEXT);

wchar_t* szPath[MAX_PATH];

GetCurrentDirectoryW(MAX_PATH, (LPWSTR)szPath);

wprintf(TEXT("%s\n"), szPath);

wchar_t* str = TEXT("D:\\ico");

SetCurrentDirectoryW(str);

GetCurrentDirectoryW(MAX_PATH, (LPWSTR)szPath);

wprintf(TEXT("%s\n"), szPath);

getchar();

return 0;

}

7.4 GetFullPathName补齐目录

DWORD WINAPI GetFullPathName(

__in LPCTSTR lpFileName,

__in DMORD nBufferLength,

__out LPTSTR lpBuffer,

__out LPTSTR *lpFilePart

);

lpFileName: 文件名

NBufferlength: 获取全路径的内存大小( TCHAR )

lpBuffer: 内存指针

lpFilePart: 文件名最后一个元素,在lpBuffer中的位置。

上面生成的路径,实际上我磁盘中有D:\\ico,但是没有D:\\ico\\myIco


八、创建进程

8.1 用到的函数

BOOL WINAPI CreateProcess (

__in_opt LPCTSTR lpApplicationName,

__inout_opt LPTSTR lpCommandLine ,

__in_opt LPSECURITY_ATTRIBUTES lpProcessAttributes,

__in_opt LPSECURITY_ATTRIBUTES lpThreadAttributes,

__in BOOL bInheritHandles,

__in DWORD dwCreationFlags,

_in_opt LPVOID lpEnvironment,

__in_opt LPCTSTR lpCurrentDirectory,

__in LPSTARTUPINFO IpStartupInfo,

__out LPPROCESS_INFORMATION lpProcessInformation

);

简单使用如下,我们关闭控制台,计算机没有被关闭

8.2 参数1:lpApplicationName

被执行的模块的名称,这个模块可以是一个windows应用程序,也可以是其他类型的模块(例如MS-DOS或者OS/2)。如果要运行这些类型的模块,操作系统必须有支持这些模块运行的子系统

可以指定文件的全路径名和文件名,或者相对当前程序运行路径(前面讲了修改程序当前运行路径的函数)的文件名。参数要包含文件的拓展名

8.3 参数2:lpCommandLine

被执行的命令行参数。这个字符串的最大长度可以达到32768个字符,包括null结尾符。如果lpApplicationName是NULL,那么lpCommandLine参数中的可执行文件的名字被限定在MAT_PATH个字符之内。有命令行参数启动进程的方法如下

如果路径有空格,需要用引号括起来

8.4 参数3:lpProcessAttributes

进程的安全描述符

8.5 参数4:lpThreadAttributes

线程的安全描述符,参数3和参数4有专门的语言来编写

8.6 参数5:dwCreationFlags

这里面存放创建时的标志。可以设置进程的优先级,并且规定进程是怎么创建的,详情阅读进程创建标志:

WinBase.h) (进程创建标志 - Win32 apps | Microsoft Learn

我们测试几个

首先建一个这样的项目:

测试CREATE_NEW_CONSOLE标志,如果不加这个标志,不会打开新的控制台

如果加了这个标志,就会重新打开一个控制台打印新进程的输出

进程优先级的标志

8.7 参数6:lpEnvironment

一个指向环境变量内存块的指针。如果这个参数是NULL,那么新进程使用父进程的环境变量。环境变量块保存的是一个NULL结尾的字符串,每个环境变量在改字符串中的形式为:name=value\0

8.8 参数7:lpCurrentDirectory

进程的当前目录

8.9 参数8:lpStartupInfo

启动信息。里面是一个指向STARTUPINFO或者STARTUPINEX结构的指针

8.10 参数9:lpProcessInformation

一个指向PROCESS_INFORMATION结构的指针。

typedef struct PROCESS_INFORMATION {

HANDLE hProcess; // 进程句柄

HANDLE hThread; // 主线程句柄

DWORD dwProcessld; // 进程 ID

DWORD dwThreadld; // 主线程 ID

} PROCESS_INFORMATION, *LPPROCESS_INFORMATION;

8.11 进程和进程内核对象的关系

进程内核对象的生命周期一定比进程生命周期长,进程没有退出代表进程的进程内核对象一定不会被销毁。当进程退出以后,进程内核对象才能被销毁。进程内核对象的引用计数为零时,销毁进程内核对象。当进程退出的时候,进程内核对象的状态发生变化。我们把进程推出后,代表该进程的内核对象的状态叫激发态

、结束进程

9.1 进程结束的方式

1、进程的主线程返回(最好的方式)

2、进程中的一个线程调用ExitProcess函数(不推荐)

3、其他进程中的某个线程调用TerminateProcess函数(不推荐)

4、进程中的所有线程执行完毕(这个很少出现)

9.2 方式1

进程的主线程返回(最好的方式)

主线程就是main、winmain函数代表的线程,也就是进程中的第一个线程。无论这个进程还有多少其他线程,只要主线程一退出,那么这个进程就结束了,其他线程自然就被系统杀死了。

#include <Windows.h>

#include <stdio.h>

DWORD WINAPI ThreadProc(__in LPVOID lpParameter)

{

wprintf(TEXT("Thread sleeping!\n"));

Sleep(INFINITE); // 让这个线程无限沉睡下去

return 0;

}

int main()

{

DWORD dwThreadID = 0;

CreateThread(NULL, 0, ThreadProc, NULL, 0, &dwThreadID); // 创建一个线程

wprintf(TEXT("The sleeping thread ID is %d\n"), dwThreadID);

getchar();

return 0;

}

可以通过工具查看进程当前情况

9.3 方式2

进程中的一个线程调用ExitProcess函数(不推荐)

这个函数只能结束本进程,不能把其他的线程结束掉

9.4 方式3

其他进程中的某个线程调用TerminateProcess函数(不推荐)

只要你有足够的权限,你就可以使用这个TerminateProcess函数,结束系统中的任意进程。同时这个TerminateProcess函数是一个异步函数,下面是退出edge的代码

9.5 进程结束时的动作

1. 所有线程结束

2. 所有用户GDI对象被释放,所有内核对象被关闭。

例如画笔,背景刷,句柄,设备句柄,申请的内存等。

3. 退出码被设置。(退出码就是main或者WinMain的返回值,或者ExitProcess, TerminateProcess函数给出的返回值)

BOOL GetExitCodeProcess(

HANDLE hProcess,

PDWORD * pdwExitCode);

如果进程依然在运行,那么这个函数返回STILL _ACTIVE。

4. 进程内核对象的状态变为有信号状态。

WaitForSingleObject(

__in HANDLE hHandle,

__in DWORD dwMillionseconds);

5. 进程内核对象的计数减一 。

6. 如果在进程中,直接调用ExitProcess或者TerminateProcess函数,那么C++类的销毁函数可能不会被调用!!!!

9.6 项目:自制CMD

#include <Windows.h>

#include <stdio.h>

int main()

{

WCHAR ws_Directory[MAX_PATH] ;

WCHAR str_Command[MAX_PATH] = { 0 };

STARTUPINFO start_info = { 0 };

PROCESS_INFORMATION info = { 0 };

start_info.dwFillAttribute = sizeof(start_info);

// 不区分大小写,比较输入的内容是否是quit,如果是就退出循环

while(CompareStringOrdinal(str_Command, 4, L"quit", 4, TRUE) != CSTR_EQUAL)

{

GetCurrentDirectoryW(MAX_PATH, (LPWSTR)ws_Directory);

wprintf(L"%s : ", ws_Directory);

_getws_s(str_Command, MAX_PATH);

CreateProcessW(NULL, str_Command, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, (LPSTARTUPINFOW)&start_info, &info);

}

wprintf(L"press any key to quit\n");

return 0;

}

父进程在创建完子进程后,立即关闭子进程的进程句柄就可以断绝父子关系了

十、用户提权

10.1 简介

用户权限的提升:

UAC (User Account Control, 用户帐户控制) 是微软为提高系统安全而在Windows Vista中引入的新技术。要关闭它,可以进行如下操作:

Windows登录账户后给session一个token (令牌),计算机中每启动一个进程,这个进程就代表你去工作,计算机需要给进程令牌。如果没有以管理员权限运行,给的就是默认的令牌,就是削减过权限的filter token

10.2 查看程序是否有管理员权限

1、获取到的权限枚举

进程权限的检查:

// pElevationType:当前的权限类型:

typedef enum {

TokenElevationTypeDefault,

TokenElevationTypeFull,

TokenElevationTypeLimited

} TOKEN_ELEVATION_TYPE, *PTOKEN_ELEVATION_TYPE;

TokenElevationTypeDefault:进程以缺省的用户身份运行,或者没有使用UAC功能。用户没有启动UAC功能,那么程序使用的令牌一定不是Filter Token

TokenElevationTypeFull:进程的权限已经被提升,进程令牌没有使用filter令牌

TokenElevapionTypelimited:进程使用的令牌是Filter令牌。此时通过GetTokenInformation函数,并使用TokenLinkedToken标志,来获取原令牌。

1. 先通过GetTokenInformation函数来确定当前程序的令牌是否位Filter Token

2. 如果是,那么通过GetTokenInformation函数,来获得FilterToken 的原始令牌。

3. 在获得原始令牌后,通过CheckTokenMembership函数确认在原始令牌中有没有管理员的帐号。如果有,那么说明启动当前程序的账户是管理员帐号,否则就不是。CheckTokenMembership确认一个SID是否在一个Token中可用。

4. 如果不是Flter Token,那么直接使用IsUserAnAdmin函数,来确定启动当前程序的帐号

是否为管理员帐号。

#include <Windows.h>

#include <stdio.h>

#include <shlobj.h>

// 作用: 函数用于确认运行当前程序的账号是否为管理员账号

// 参数: pElevationType : 令牌提升类型

//       pIsAdmin : 是否是管理员

// 返回: 函数返回TRUE代表函数执行成功

BOOL GetProcessElevation(TOKEN_ELEVATION_TYPE * pElevationType, BOOL * pIsAdmin)

{

HANDLE hToken = NULL;

DWORD dwSize;

// 获得当前进程的令牌句柄

if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))

return(FALSE);

BOOL bResult = FALSE;

// 看看这个令牌的权限提升类型是哪一种: TokenElevationTypeDefault、TokenElevationTypeFull、TokenElevapionTypelimited

if (GetTokenInformation(hToken, TokenElevationType, pElevationType, sizeof(TOKEN_ELEVATION_TYPE), &dwSize))

{

// 创建一个管理员SID

BYTE adminSID[SECURITY_MAX_SID_SIZE];

dwSize = sizeof(adminSID);

CreateWellKnownSid(WinBuiltinAdministratorsSid, NULL, &adminSID, &dwSize);

if (*pElevationType == TokenElevationTypeLimited)

{

// 通过Filter Token来获得原始的Token

HANDLE hUnfilteredToken = NULL ;

// TokenLinkedToken标志,表示要获得Filter Token的原始Token

GetTokenInformation(hToken, TokenLinkedToken, (VOID*)&hUnfilteredToken, sizeof(HANDLE), &dwSize);

// 检查原始Token中,管理员账户adminSID是否被激活,如果被激活,那么说明启动这个程序的账号是管理员账号

// CheckTokenMembership函数结果被保存在pIsAdmin参数中,函数的返回值表示这个函数是否执行成功

if (CheckTokenMembership(hUnfilteredToken, &adminSID, pIsAdmin))

{

bResult = TRUE;

}

// 不要忘记关闭原始令牌

CloseHandle(hUnfilteredToken);

}

else

{

// 如果是原始令牌,只要IsUserAnAdmin就可以确定,启动当前程序的账号是否是管理员账号

*pIsAdmin = IsUserAnAdmin();

bResult = TRUE;

}

}

// 不要忘记关闭进程令牌

CloseHandle(hToken);

return(bResult);

}

int main()

{

TOKEN_ELEVATION_TYPE Type;

BOOL IsAdmin;

BOOL ret = GetProcessElevation(&Type, &IsAdmin);

if (ret)

{

wprintf(L"%d, Type\n");

if (IsAdmin)

wprintf(L"On Admin running!\n");

else

wprintf(L"On normal acount running!\n");

}

getchar();

return 0;

}

10.3 程序权限调整

#include <Windows.h>

#include <stdio.h>

#include <processthreadsapi.h>

// 作用: 函数将进程权限提升成具有调试权限的进程,这个权限应该是进程所能具有的最大权限

//       这个函数成功的前提,启动这个进程的账号必须是管理员账号

// 参数: fEnable : 为TRUE表示授予当前进程调试权限,为FALSE表示取消当前进程调试权限

// 返回: 函数返回TRUE代表函数调整权限成功

BOOL EnableDebugPrivilege(BOOL fEnable)

{

// 调试权限可以让该进程能够读取其他进程的信息

BOOL fok = FALSE;

HANDLE hToken;

// 获取当前进程的令牌

// 这个函数第一个参数是当前进程句柄,第二个参数是进程对获得的令牌有哪些操作权限

if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))

{

// 开始激活当前令牌的调试权限

TOKEN_PRIVILEGES tp; // 结构体,表示令牌权限

tp.PrivilegeCount = 1; // 我们启动调试权限,所以给1

// 查找调试权限的LVID,如果第一个参数是NULL,表示获取本地的某个权限的LVID

// 函数LookupPrivilegeValue获取本地系统的调试的LVID

LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid);

// 在tp.Privileges[0].Attributes属性中设置开启调试权限还是关闭调试权限

// Attributes=SE_SE_PRIVILEGE_ENABLED时,激活权限

// Attributes=0时,关闭权限

tp.Privileges[0].Attributes = fEnable ? SE_PRIVILEGE_ENABLED : 0;

// AdjustTokenPrivileges激活或者关闭tp中给定的权限

AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL);

// 确定是否激活成功

fok = (GetLastError() == ERROR_SUCCESS);

CloseHandle(hToken);

}

return(fok);

}

int main()

{

if (EnableDebugPrivilege(TRUE))

{

wprintf(L"Enable Debug privilege is ok!\n");

}

else

{

wprintf(L"Enable Debug privilege is Failure!\n");

}

EnableDebugPrivilege(FALSE);

getchar();

return 0;

}

10.4 以管理员权限启动其他程序

SHELLEXECUTEINFO ShExecInfo = { 0 };

ShExecInfo.cbSize = sizeof(SHELLEXECUTEINFO);

ShExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS;

ShExecInfo.hwnd = NULL;

ShExecInfo.lpVerb = L"runas";

ShExecInfo.lpFile = L"cmd";

ShExecInfo.lpParameters = L"";

ShExecInfo.lpDirectory = NULL;

ShExecInfo.nShow = SW_SHOW;

ShExecInfo.hInstApp = NULL;

ShellExecuteEx(&ShExecInfo);

十一、获取所有进程和进程中DLL

11.1 获取所有进程

#include <stdio.h>

#include <stdlib.h>

#include <Windows.h>

#include <TlHelp32.h>

int main(int argc, char* argv[])

{

HANDLE hProcessSnap = NULL;

PROCESSENTRY32 pe32 = {0};

//在使用这个结构前,先设置它的大小

pe32.dwSize = sizeof(PROCESSENTRY32);

//给系统内所有的进程拍个快照

hProcessSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);

if (hProcessSnap == INVALID_HANDLE_VALUE)

{

printf_s("CreatToolhelp32Snapshot error!\n");

return -1;

}

//遍历进程快照,轮流显示每个进程的信息

BOOL bRet = ::Process32First(hProcessSnap,&pe32);

while (bRet)

{

printf_s("进程名称:%ls\n",pe32.szExeFile);   //这里得到的应该是宽字符,用%ls或者%S,不然无法正常打印

printf_s("进程ID %u\n\n",pe32.th32ProcessID);

bRet = ::Process32Next(hProcessSnap,&pe32);

}

//不要忘记清除掉snapshot对象

::CloseHandle(hProcessSnap);

return 0;

}

11.2 获取所有DLL

class Dll

{

public:

Dll(std::wstring name, std::wstring path);

~Dll();

std::wstring getDllName(void) const;

std::wstring getDllPath(void) const;

private:

std::wstring dllName;

std::wstring dllPath;

};

bool GetDllFromProcess(std::vector<Dll>& dllVec, const DWORD& processId)

{

//创建系统快照,并返回进程句柄

HANDLE handle = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, processId);

if (handle == INVALID_HANDLE_VALUE)

return false;

//枚举进程模块

MODULEENTRY32 info;

info.dwSize = sizeof(MODULEENTRY32);

Module32First(handle, &info);

while (Module32Next(handle, &info) != FALSE)

{

dllVec.emplace_back(Dll(std::wstring(info.szModule), std::wstring(info.szExePath)));

}

CloseHandle(handle);

return true;

}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1161735.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CTS分析思路

目录 原理简介&#xff1a; Cts测试原理&#xff1a; CTS报告与日志目录 CTS报告目录如下​编辑 log查看 举例 原理简介&#xff1a; Cts环境搭建和测试方法&#xff0c;大家可以自行查询网上资料。 Cts测试原理&#xff1a; 输入命令后&#xff0c;会安装一系列的测试…

短视频账号矩阵系统/剪辑/矩阵/无人直播/文案引流爆款

一、 短视频账号矩阵源码开发包含哪几方面&#xff1f; 1. 界面设计&#xff1a;需要根据用户需求&#xff0c;设计出优美简洁的UI界面&#xff0c;使用户可以方便快捷地管理自己的短视频账号。 2. 数据存储&#xff1a;需要将用户的账号信息、数据统计等信息存储在数据库中&a…

pytorch dropout 置零 + 补偿性放缩

一句话概括&#xff1a;&#xff08;训练过程中&#xff09;Dropout 操作 随机置零 非置零元素进行后补偿性放缩。以保证dropout前后数据scale不变。 详细解释(来自chatgpt): 在 PyTorch 中&#xff0c;dropout 的操作不仅仅是将某些元素置零。为了确保期望输出在训练和测试…

WoShop跨境电商商城源码(多语言多货币多商户进出口电商平台)

一、跨境电商商城系统源码包括以下几个部分 前端框架&#xff1a;uni-app,vue 后端框架&#xff1a;ThinkPHP5.wokerman 支付系统&#xff1a;PayPal、USDT等主流支付平台 语言包&#xff1a;跨境电商支持15种语言&#xff0c;后续会增加 前端&#xff1a;包含APP端、小程序端、…

无需数据搬迁,10倍性能提升!携程的统一分析之旅

作者&#xff1a;携程技术中心大数据总监 许鹏 携程自 2022 年起引入了 StarRocks&#xff0c;目前已经成为了集团内部的主要技术栈&#xff0c;应用到酒店、机票、商旅、度假、市场、火车票等多个关键业务线。目前&#xff0c;携程内部已经拥有超过 10 个 StarRocks 集群&…

c语言函数宏的几种封装方式

c语言函数宏的几种封装方式 在c语言开发中&#xff0c;除了使用函数封装代码之外&#xff0c;也经常使用宏来封装一些重要或简洁的代码。 宏在c开发有三种&#xff1a;预定义宏&#xff0c;不带参宏&#xff0c;和带参数宏&#xff0c;通常&#xff0c;带参数宏也叫函数宏&am…

HR应用人才测评来提升人才价值

对于企业而言&#xff0c;产出价值是最重要的&#xff0c;但企业拥有人才&#xff0c;才能产出更多的价值&#xff0c;做HR这么久发现很多企业&#xff0c;都欠缺人才管理的测评技术&#xff0c;导致很多人才被埋没或者浪费&#xff0c;这也说明一个很大的问题&#xff0c;一定…

Github 自动化部署到GitHub Pages

1.准备工作 新建仓库 新建项目 配置 vite.config.ts base: ./,部署应用包时的基本URL&#xff0c;例&#xff1a;vue-cli 5.x 配置 publicPath 推送到远程仓库 2.配置 GitHub Token 点击 Settings -> Actions -> General 找到 Workflow permissions&#xff0c;选中第…

sqlserver涉及到三种排序后生成的数字

with temp as (select 1 标识,2023-01-01 日期,a 项目union all select 1,2023-01-01,a union all select 2,2023-01-01,a union all select 2,2023-01-01,b union all select 3,2023-01-01,a union all select 3,2023-01-01,b union all select 3,2023-01-01,c union all …

耳朵小戴什么耳机合适,2023年适合小耳道的蓝牙无线耳机分享

你们是否曾为了追求音乐的同时&#xff0c;担心自己的听力健康呢&#xff1f;尤其是耳朵小的群体&#xff0c;佩戴入耳式时间一长&#xff0c;就会感觉耳道存在一定的疼痛感&#xff0c;不过别担心&#xff0c;现在有了一种完美的解决方案——骨传导耳机&#xff01;这种炫酷的…

解决Windows Server 2012 由于没有远程桌面授权服务器可以提供需求可证

刚开始提示 之后就登录不了 &#xff08;如下图提示&#xff09; 由于windows server 2012 R2 安装了 远程桌面角色&#xff0c;但是这个角色是120天免费的&#xff0c;需要购买授权的。解决方法是取消/删除这个角色&#xff0c;就可以恢复正常的远程 一直下一步 远程桌面服…

2023年阿里云双11活动云服务器可选实例规格、配置及活动价格分享

阿里云服务器2023年双11活动价格是多少&#xff1f;轻量应用服务器2核2G3M带宽轻量服务器87元1年、2核4G4M带宽165元1年&#xff1b;云服务器经济型e实例2核2G3M配置99元1年&#xff1b;计算型c7实例2核4G1M配置864.79元1年&#xff1b;通用型g7实例2核8G1M配置1089.91元1年&am…

Linux C语言进阶-D9字符指针与字符串

初始化字符指针&#xff1a;把内存中字符串的首地址赋予指针&#xff0c;并不是把该字符串复制到指针中。 char str[] "Hello World"; char *p str; 在C编程中&#xff0c;当一个字符指针指向一个字符串常量时&#xff0c;不能修改指针指向的对象的值 char * p &…

【PC】第26赛季第2轮更新公告

正式服维护日期 ※ 下列时间可能会根据维护情况而发生变化。 11月8日上午8:00 – 下午4:30 地图轮换 ※ 地图轮换将于北京时间每周三上午10点进行。 ※ 在随机选择地图的区域中&#xff0c;各地图将按大型地图25%、小型地图12.5%的概率随机匹配。 测试服 普通比赛&#xf…

Capybara库如何批量下载新浪图片

按照要求写一个使用Capybara库的下载程序。该程序使用Ruby下载新浪新闻的图片&#xff0c;并使用爬虫IP服务器duoip的8000端口进行下载。 require capybara require mechanize# 创建一个爬虫IP服务器实例 proxy Mechanize.new爬虫IP主机: duoip,爬虫IP端口: 8000# 访问新浪新闻…

黑马最新「SpringBoot3+Vue3」全套教程上线,练手很香

转眼2023年仅剩2个月&#xff0c;大家的“卷”也进入了白热化阶段&#xff0c;毕竟10月随份子的钱还没还完&#xff0c;双11又付了一大笔尾款&#xff0c;还要准备回家过年的钱……为了更卷&#xff08;赚更多钱&#xff09;&#xff0c;又又又有程序员来找播妞要新教程了。 “…

全球地表动态水体产品(数据集V2.0)(2000-2020年/8天/250米)

摘要 全球地表动态水体产品(8天/250米)GSWED(Global Surface Water Extent Dataset)由中国科学院空天信息创新研究院湿地与环境遥感研究团队制作而成。本套数据集是基于MODIS数据构建了一套全球地表水体NDVI阈值时空参数集,在此基础上使用遥感大数据云平台进行研发的,使用G…

全优学堂功能说明

1. 快速使用系统 系统首页 #2. 功能列表 使用端功能模块PC端微信生态微信生态接入配置课程班级管理课程配置&#xff0c;支持多种收费方式、多校区配置班级所有学员班级课表上课记录学生学生信息、综合信息查询&#xff1a;报读课程、订单、课表、上课记录、变动日志家长绑定…

小程序 - 起步

小程序代码的构成-项目结构 了解项目的基本组成结构 ①用来存放所有小程序的页面 ②utils 用来存放工具性质的模块&#xff08;例如&#xff1a;格式化时间的自定义模块&#xff09; ③app.js 小程序项目的入口文件 ④app.json 小程序项目的全局配置文件 ⑤app.wxss 小程序项…

苹果手机如何导出微信聊天记录?3个方法,教你快速导出!

微信聊天记录不仅只是几句话、几张图片、几个视频&#xff0c;更是我们与好友之间的感情见证。所以说&#xff0c;把微信上的一些重要聊天记录导出或者备份是很有必要的事情。 如果出现误删或者手机损坏、丢失等情况&#xff0c;那么这些珍贵的聊天记录就会消失。如何导出微信…