挂起与恢复进程是指暂停或恢复进程的工作状态,以达到一定的控制和管理效果。在 Windows 操作系统中,可以使用系统提供的函数实现进程的挂起和恢复,以达到对进程的控制和调度。需要注意,过度使用进程挂起/恢复操作可能会造成系统性能的降低,导致死锁等问题,因此在使用时应该谨慎而慎重。同时,通过和其他进程之间协同工作,也可以通过更加灵活的方式,实现进程的协调、交互等相应的功能,从而实现更加高效和可靠的进程管理。
要实现挂起进程,首先我们需要实现挂起线程,因为挂起进程的实现原理是通过调用SuspendThread函数循环将进程内的所有线程全部挂起后实现的,而要实现挂起线程则我们需要先确定指定进程内的线程信息,要实现枚举进程内的线程信息则可以通过以下几个步骤实现。
首先通过CreateToolhelp32Snapshot得到当前系统下所有的进程快照,并通过遍历进程的方式寻找是否符合我们所需要枚举的进程名,如果是则调用CreateToolhelp32Snapshot并通过传入TH32CS_SNAPTHREAD代表枚举线程,通过循环的方式遍历进程内的线程,每次通过调用OpenThread打开线程,并调用ZwQueryInformationThread查询该线程的入口信息以及线程所在的模块信息,最后以此输出即可得到当前进程内的所有线程信息。- #include <iostream>
- #include <Windows.h>
- #include <TlHelp32.h>
- #include <Psapi.h>
- using namespace std;
- typedef enum _THREADINFOCLASS
- {
- ThreadBasicInformation,
- ThreadTimes,
- ThreadPriority,
- ThreadBasePriority,
- ThreadAffinityMask,
- ThreadImpersonationToken,
- ThreadDescriptorTableEntry,
- ThreadEnableAlignmentFaultFixup,
- ThreadEventPair_Reusable,
- ThreadQuerySetWin32StartAddress,
- ThreadZeroTlsCell,
- ThreadPerformanceCount,
- ThreadAmILastThread,
- ThreadIdealProcessor,
- ThreadPriorityBoost,
- ThreadSetTlsArrayAddress,
- ThreadIsIoPending,
- ThreadHideFromDebugger,
- ThreadBreakOnTermination,
- MaxThreadInfoClass
- }THREADINFOCLASS;
- typedef struct _CLIENT_ID
- {
- HANDLE UniqueProcess;
- HANDLE UniqueThread;
- }CLIENT_ID;
- typedef struct _THREAD_BASIC_INFORMATION
- {
- LONG ExitStatus;
- PVOID TebBaseAddress;
- CLIENT_ID ClientId;
- LONG AffinityMask;
- LONG Priority;
- LONG BasePriority;
- }THREAD_BASIC_INFORMATION, *PTHREAD_BASIC_INFORMATION;
- extern "C" LONG(__stdcall * ZwQueryInformationThread)
- (
- IN HANDLE ThreadHandle,
- IN THREADINFOCLASS ThreadInformationClass,
- OUT PVOID ThreadInformation,
- IN ULONG ThreadInformationLength,
- OUT PULONG ReturnLength OPTIONAL
- ) = NULL;
- // 枚举进程内的线程
- BOOL EnumThread(char *ProcessName)
- {
- // 进程快照句柄
- HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
- PROCESSENTRY32 process = { sizeof(PROCESSENTRY32) };
- // 遍历进程
- while (Process32Next(hProcessSnap, &process))
- {
- // char* 转 string
- string s_szExeFile = process.szExeFile;
- if (s_szExeFile == ProcessName)
- {
- HANDLE hThreadSnap = INVALID_HANDLE_VALUE;
- THREADENTRY32 te32;
- // 创建线程快照
- hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
- if (hThreadSnap == INVALID_HANDLE_VALUE)
- {
- return FALSE;
- }
- // 为快照分配内存空间
- te32.dwSize = sizeof(THREADENTRY32);
- // 获取第一个线程的信息
- if (!Thread32First(hThreadSnap, &te32))
- {
- return FALSE;
- }
- // 遍历线程
- while (Thread32Next(hThreadSnap, &te32))
- {
- // 判断线程是否属于本进程
- if (te32.th32OwnerProcessID == process.th32ProcessID)
- {
- // 打开线程
- HANDLE hThread = OpenThread(
- THREAD_ALL_ACCESS, // 访问权限
- FALSE, // 由此线程创建的进程不继承线程的句柄
- te32.th32ThreadID // 线程 ID
- );
- if (hThread == NULL)
- {
- return FALSE;
- }
- // 将区域设置设置为从操作系统获取的ANSI代码页
- setlocale(LC_ALL, ".ACP");
- // 获取 ntdll.dll 的模块句柄
- HINSTANCE hNTDLL = ::GetModuleHandle("ntdll");
- // 从 ntdll.dll 中取出 ZwQueryInformationThread
- (FARPROC&)ZwQueryInformationThread = GetProcAddress(hNTDLL, "ZwQueryInformationThread");
- // 获取线程入口地址
- PVOID startaddr; // 用来接收线程入口地址
- ZwQueryInformationThread(
- hThread, // 线程句柄
- ThreadQuerySetWin32StartAddress, // 线程信息类型 ThreadQuerySetWin32StartAddress 线程入口地址
- &startaddr, // 指向缓冲区的指针
- sizeof(startaddr), // 缓冲区的大小
- NULL
- );
- // 获取线程所在模块
- THREAD_BASIC_INFORMATION tbi; // _THREAD_BASIC_INFORMATION 结构体对象
- TCHAR modname[MAX_PATH]; // 用来接收模块全路径
- ZwQueryInformationThread(
- hThread, // 线程句柄
- ThreadBasicInformation, // 线程信息类型,ThreadBasicInformation :线程基本信息
- &tbi, // 指向缓冲区的指针
- sizeof(tbi), // 缓冲区的大小
- NULL
- );
- // 检查入口地址是否位于某模块中
- GetMappedFileName(
- OpenProcess( // 进程句柄
- PROCESS_ALL_ACCESS, // 访问权限
- FALSE, // 由此线程创建的进程不继承线程的句柄
- (DWORD)tbi.ClientId.UniqueProcess // 唯一进程 ID
- ),
- startaddr, // 要检查的地址
- modname, // 用来接收模块名的指针
- MAX_PATH // 缓冲区大小
- );
- std::cout << "线程ID: " << te32.th32ThreadID << " 线程入口: " << startaddr << " 所在模块: " << modname << std::endl;
- }
- }
- }
- }
- }
- int main(int argc, char* argv[])
- {
- EnumThread("lyshark.exe");
- system("pause");
- return 0;
- }
复制代码 读者可自行编译并运行上述代码,通过调用SuspendProcess函数并以此传入需要挂起的进程PID以及一个状态,当该状态为TRUE时则代表挂起进程,而当状态值为FALSE时则代表为恢复一个进程,当一个进程被挂起后其会出现卡死的现象,当恢复后一切都会变得正常。
本文作者: 王瑞
本文链接: https://www.lyshark.com/post/5fbc3082.html
版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |