悠扬随风 发表于 2023-10-2 05:54:53

7.6 实现进程挂起与恢复

挂起与恢复进程是指暂停或恢复进程的工作状态,以达到一定的控制和管理效果。在 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;               // 用来接收模块全路径
          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 许可协议。转载请注明出处!

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: 7.6 实现进程挂起与恢复