365充值真人注册-super365体育官网下载-365bet体育开户

7.6 实现进程挂起与恢复

7.6 实现进程挂起与恢复

挂起与恢复进程是指暂停或恢复进程的工作状态,以达到一定的控制和管理效果。在 Windows 操作系统中,可以使用系统提供的函数实现进程的挂起和恢复,以达到对进程的控制和调度。需要注意,过度使用进程挂起/恢复操作可能会造成系统性能的降低,导致死锁等问题,因此在使用时应该谨慎而慎重。同时,通过和其他进程之间协同工作,也可以通过更加灵活的方式,实现进程的协调、交互等相应的功能,从而实现更加高效和可靠的进程管理。

要实现挂起进程,首先我们需要实现挂起线程,因为挂起进程的实现原理是通过调用SuspendThread函数循环将进程内的所有线程全部挂起后实现的,而要实现挂起线程则我们需要先确定指定进程内的线程信息,要实现枚举进程内的线程信息则可以通过以下几个步骤实现。

首先通过CreateToolhelp32Snapshot得到当前系统下所有的进程快照,并通过遍历进程的方式寻找是否符合我们所需要枚举的进程名,如果是则调用CreateToolhelp32Snapshot并通过传入TH32CS_SNAPTHREAD代表枚举线程,通过循环的方式遍历进程内的线程,每次通过调用OpenThread打开线程,并调用ZwQueryInformationThread查询该线程的入口信息以及线程所在的模块信息,最后以此输出即可得到当前进程内的所有线程信息。

#include

#include

#include

#include

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;

}

读者可自行运行上述代码片段,即可枚举出当前运行进程lyshark.exe中所有的后动线程信息,如下图所示;

当我们能够得到当前进程内的线程信息后,接下来就是实现如何挂起或恢复进程内的特定线程,挂起线程可以使用SuspendThread其函数声明如下:

DWORD SuspendThread(

HANDLE hThread

);

其中,hThread 是一个指向线程句柄的指针,指向要挂起的线程的句柄,该函数返回挂起前线程的线程计数器值,表示被挂起线程在挂起前还未执行的指令数目。

可以多次调用 SuspendThread 函数将同一个线程进行多次挂起,每次返回被挂起前线程的线程计数器值,每调用一次则会阻塞该线程,其状态会变为挂起状态。当该线程被 ResumeThread 恢复时,它将继续从上次挂起时的位置开始执行。

ResumeThread 函数声明如下:

DWORD ResumeThread(

HANDLE hThread

);

其中,hThread 是线程句柄,指向要恢复的线程的句柄。

调用 ResumeThread 函数可以让一个被挂起的线程从上次挂起的位置开始继续执行,函数返回值是被恢复的线程的先前挂起次数。当被恢复的线程的挂起计数器归零时,其状态将自动变为非挂起状态,并开始继续执行。

当有了上述两个函数的支持那么挂起线程将变得很容易实现了,首先后去所有进程快照,接着就是直接打开OpenThread()符合要求的线程,此时只需要调用SuspendThread(hThread)即可挂起一个线程,调用ResumeThread(hThread)则可以恢复一个线程,具体实现代码如下所示;

#include

#include

#include

int Start_Stop_Thread(DWORD Pid, DWORD ThreadID, BOOL flag)

{

THREADENTRY32 te32 = { 0 };

te32.dwSize = sizeof(THREADENTRY32);

// 获取全部线程快照

HANDLE hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);

if (INVALID_HANDLE_VALUE != hThreadSnap)

{

// 获取快照中第一条信息

BOOL bRet = Thread32First(hThreadSnap, &te32);

while (bRet)

{

// 只过滤出 pid 里面的线程

if (Pid == te32.th32OwnerProcessID)

{

// 判断是否为ThreadID,暂停指定的TID

if (ThreadID == te32.th32ThreadID)

{

// 打开线程

HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, te32.th32ThreadID);

if (flag == TRUE)

{

ResumeThread(hThread); // 恢复线程

}

else

{

SuspendThread(hThread); // 暂停线程

}

CloseHandle(hThreadSnap);

}

}

// 获取快照中下一条信息

bRet = Thread32Next(hThreadSnap, &te32);

}

return 0;

}

return -1;

}

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

{

// 暂停或恢复进程ID = 4204 里面的线程ID = 10056

int ret = Start_Stop_Thread(4204, 10056, TRUE); // TRUE = 恢复线程 FALSE = 挂起线程

printf("状态: %d \n", ret);

system("pause");

return 0;

}

当有了上述功能的支持以后,那么实现挂起进程将变得很容易,读者只需要在特定一个进程内枚举出所有的活动线程,并通过循环的方式逐个挂起即可实现挂起整个进程的效果,这段完整代码如下所示;

#include

#include

#include

#include

#pragma comment(lib,"psapi.lib")

#define NT_SUCCESS(Status) ((NTSTATUS)(Status) >= 0)

#define SystemProcessesAndThreadsInformation 5 // 功能号

typedef DWORD(WINAPI* PQUERYSYSTEM)(UINT, PVOID, DWORD, PDWORD);

// 线程状态的枚举常量

typedef enum _THREAD_STATE

{

StateInitialized, // 初始化状态

StateReady, // 准备状态

StateRunning, // 运行状态

StateStandby,

StateTerminated, // 关闭

StateWait, // 等待

StateTransition, // 切换

StateUnknown

}THREAD_STATE;

// 线程处于等待的原因的枚举常量

typedef enum _KWAIT_REASON

{

Executive,

FreePage,

PageIn,

PoolAllocation,

DelayExecution,

Suspended,

UserRequest,

WrExecutive,

WrFreePage,

WrPageIn,

WrPoolAllocation,

WrDelayExecution,

WrSuspended,

WrUserRequest,

WrEventPair,

WrQueue,

WrLpcReceive,

WrLpcReply,

WrVirtualMemory,

WrPageOut,

WrRendezvous,

Spare2,

Spare3,

Spare4,

Spare5,

Spare6,

WrKernel,

MaximumWaitReason

}KWAIT_REASON;

typedef LONG NTSTATUS;

typedef LONG KPRIORITY;

typedef struct _CLIENT_ID

{

DWORD UniqueProcess;

DWORD UniqueThread;

} CLIENT_ID, * PCLIENT_ID;

typedef struct _VM_COUNTERS

{

SIZE_T PeakVirtualSize;

SIZE_T VirtualSize;

ULONG PageFaultCount;

SIZE_T PeakWorkingSetSize;

SIZE_T WorkingSetSize;

SIZE_T QuotaPeakPagedPoolUsage;

SIZE_T QuotaPagedPoolUsage;

SIZE_T QuotaPeakNonPagedPoolUsage;

SIZE_T QuotaNonPagedPoolUsage;

SIZE_T PagefileUsage;

SIZE_T PeakPagefileUsage;

} VM_COUNTERS;

// 线程信息结构体

typedef struct _SYSTEM_THREAD_INFORMATION

{

LARGE_INTEGER KernelTime;

LARGE_INTEGER UserTime;

LARGE_INTEGER CreateTime;

ULONG WaitTime;

PVOID StartAddress;

CLIENT_ID ClientId;

KPRIORITY Priority;

KPRIORITY BasePriority;

ULONG ContextSwitchCount;

LONG State;// 状态,是THREAD_STATE枚举类型中的一个值

LONG WaitReason;//等待原因, KWAIT_REASON中的一个值

} SYSTEM_THREAD_INFORMATION, * PSYSTEM_THREAD_INFORMATION;

typedef struct _UNICODE_STRING

{

USHORT Length;

USHORT MaximumLength;

PWSTR Buffer;

} UNICODE_STRING, * PUNICODE_STRING;

// 进程信息结构体

typedef struct _SYSTEM_PROCESS_INFORMATION

{

ULONG NextEntryDelta; // 指向下一个结构体的指针

ULONG ThreadCount; // 本进程的总线程数

ULONG Reserved1[6]; // 保留

LARGE_INTEGER CreateTime; // 进程的创建时间

LARGE_INTEGER UserTime; // 在用户层的使用时间

LARGE_INTEGER KernelTime; // 在内核层的使用时间

UNICODE_STRING ProcessName; // 进程名

KPRIORITY BasePriority;

ULONG ProcessId; // 进程ID

ULONG InheritedFromProcessId;

ULONG HandleCount; // 进程的句柄总数

ULONG Reserved2[2]; // 保留

VM_COUNTERS VmCounters;

IO_COUNTERS IoCounters;

SYSTEM_THREAD_INFORMATION Threads[5]; // 子线程信息数组

}SYSTEM_PROCESS_INFORMATION, * PSYSTEM_PROCESS_INFORMATION;

// 获取线程是被是否被挂起 1=表示线程被挂起 0=表示线程正常 -1=未知状态

int IsThreadSuspend(DWORD dwProcessID, DWORD dwThreadID)

{

int nRet = 0;

NTSTATUS Status = 0;

PQUERYSYSTEM NtQuerySystemInformation = NULL;

PSYSTEM_PROCESS_INFORMATION pInfo = { 0 };

// 获取函数地址

NtQuerySystemInformation = (PQUERYSYSTEM) GetProcAddress(LoadLibrary("ntdll.dll"), "NtQuerySystemInformation");

DWORD dwSize = 0;

// 获取信息所需的缓冲区大小

Status = NtQuerySystemInformation(SystemProcessesAndThreadsInformation,// 要获取的信息的类型

NULL, // 用于接收信息的缓冲区

0, // 缓冲区大小

&dwSize

);

// 申请缓冲区

char* pBuff = new char[dwSize];

pInfo = (PSYSTEM_PROCESS_INFORMATION)pBuff;

if (pInfo == NULL)

return -1;

// 再次调用函数, 获取信息

Status = NtQuerySystemInformation(SystemProcessesAndThreadsInformation, // 要获取的信息的类型

pInfo, // 用于接收信息的缓冲区

dwSize, // 缓冲区大小

&dwSize

);

if (!NT_SUCCESS(Status))

{

/*如果函数执行失败*/

delete[] pInfo;

return -1;

}

// 遍历结构体,找到对应的进程

while (1)

{

// 判断是否还有下一个进程

if (pInfo->NextEntryDelta == 0)

break;

// 判断是否找到了ID

if (pInfo->ProcessId == dwProcessID)

{

// 找到该进程下的对应的线程,也就是遍历所有线程

for (DWORD i = 0; i < pInfo->ThreadCount; i++)

{

if (pInfo->Threads[i].ClientId.UniqueThread == dwThreadID)

{

// 找到线程

// 如果线程被挂起

if (pInfo->Threads[i].State == StateWait&& pInfo->Threads[i].WaitReason == Suspended)

{

nRet = 1;

break;

}

}

}

break;

}

// 迭代到下一个节点

pInfo = (PSYSTEM_PROCESS_INFORMATION)(((PUCHAR)pInfo) + pInfo->NextEntryDelta);

}

delete[] pBuff;

return nRet;

}

// 设置进程状态 挂起/非挂起

int SuspendProcess(DWORD dwProcessID, BOOL fSuspend)

{

HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, dwProcessID);

if (hSnapshot != INVALID_HANDLE_VALUE)

{

THREADENTRY32 te = { sizeof(te) };

BOOL fOk = Thread32First(hSnapshot, &te);

for (; fOk; fOk = Thread32Next(hSnapshot, &te))

{

if (te.th32OwnerProcessID == dwProcessID)

{

HANDLE hThread = OpenThread(THREAD_SUSPEND_RESUME,FALSE, te.th32ThreadID);

if (hThread != NULL)

{

if (fSuspend)

{

if (IsThreadSuspend(dwProcessID, te.th32ThreadID) == 0)

{

SuspendThread(hThread);

}

else

{

return 0;

}

}

else

{

if (IsThreadSuspend(dwProcessID, te.th32ThreadID) == 1)

{

ResumeThread(hThread);

}

else

{

return 0;

}

}

}

CloseHandle(hThread);

}

}

}

CloseHandle(hSnapshot);

return 1;

}

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

{

// 挂起进程

SuspendProcess(20308, TRUE);

// 恢复进程

SuspendProcess(20308, FALSE);

return 0;

}

读者可自行编译并运行上述代码,通过调用SuspendProcess函数并以此传入需要挂起的进程PID以及一个状态,当该状态为TRUE时则代表挂起进程,而当状态值为FALSE时则代表为恢复一个进程,当一个进程被挂起后其会出现卡死的现象,当恢复后一切都会变得正常。

本文作者: 王瑞 本文链接: https://www.lyshark.com/post/5fbc3082.html 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!

相关推荐