如何识别其他进程中需终止的线程(线程ID与起始地址均动态变化)
跨进程终止动态特征线程的识别与实现方案
因为thread id和线程起始地址均为运行时动态分配的参数,无法作为稳定匹配依据,需要提取线程的固定不变特征完成识别,可选用的稳定特征优先级从高到低如下:
- 线程自定义名称:多数开发框架都支持给线程设置独立名称,匹配精度最高、实现成本最低
- 线程调用栈固定符号序列:线程执行逻辑固定的情况下,调用栈中会存在固定的业务函数符号,可作为匹配依据
- 线程本地存储(TLS)固定标记:如果业务逻辑会在指定线程的TLS中写入固定标识值,可读取该值完成匹配
- 运行特征辅助匹配:可结合线程的CPU占用率、打开的句柄列表等特征做二次校验
Windows平台实现示例
Windows下可通过ToolHelp API遍历目标进程的所有线程,匹配线程名称后执行终止操作:
#include <windows.h> #include <tlhelp32.h> #include <stdio.h> /** * @brief 终止目标进程中指定名称的线程 * @param dwPid 目标进程ID * @param targetThreadName 目标线程的名称 * @return 找到并终止成功返回TRUE,否则返回FALSE */ BOOL TerminateTargetThread(DWORD dwPid, const wchar_t* targetThreadName) { HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); if (hSnap == INVALID_HANDLE_VALUE) return FALSE; THREADENTRY32 te = {.dwSize = sizeof(THREADENTRY32)}; BOOL found = FALSE; if (Thread32First(hSnap, &te)) { do { // 过滤非目标进程的线程 if (te.th32OwnerProcessID != dwPid) continue; HANDLE hThread = OpenThread(THREAD_QUERY_LIMITED_INFORMATION | THREAD_TERMINATE, FALSE, te.th32ThreadID); if (hThread == NULL) continue; // 查询线程名称,仅Windows 10 1607及以上版本支持 PWSTR pThreadName = NULL; HRESULT hr = GetThreadDescription(hThread, &pThreadName); if (SUCCEEDED(hr)) { if (wcscmp(pThreadName, targetThreadName) == 0) { TerminateThread(hThread, 0); found = TRUE; LocalFree(pThreadName); CloseHandle(hThread); break; } LocalFree(pThreadName); } CloseHandle(hThread); } while (Thread32Next(hSnap, &te)); } CloseHandle(hSnap); return found; }
如果目标线程未设置名称,可结合DbgHelp库的StackWalk64接口遍历线程调用栈,匹配固定的业务函数符号即可完成识别。
Linux平台实现示例
Linux下线程为轻量级进程,所有线程信息存储在/proc/[进程ID]/task/目录下,可直接读取线程名称匹配:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <dirent.h> #include <signal.h> #include <sys/types.h> /** * @brief 终止目标进程中指定名称的线程 * @param pid 目标进程ID * @param target_thread_name 目标线程的名称 * @return 找到并终止成功返回1,否则返回0,出错返回-1 */ int terminate_target_thread(pid_t pid, const char* target_thread_name) { char task_path[256]; snprintf(task_path, sizeof(task_path), "/proc/%d/task", pid); DIR* task_dir = opendir(task_path); if (!task_dir) return -1; struct dirent* entry; int found = 0; while ((entry = readdir(task_dir)) != NULL) { if (entry->d_name[0] == '.') continue; pid_t tid = atoi(entry->d_name); char comm_path[256]; snprintf(comm_path, sizeof(comm_path), "/proc/%d/task/%d/comm", pid, tid); FILE* comm_fp = fopen(comm_path, "r"); if (!comm_fp) continue; char thread_name[256] = {0}; if (fgets(thread_name, sizeof(thread_name), comm_fp)) { // 移除末尾换行符 thread_name[strcspn(thread_name, "\n")] = '\0'; if (strcmp(thread_name, target_thread_name) == 0) { // 用tgkill精准发送终止信号到指定线程 tgkill(pid, tid, SIGKILL); found = 1; fclose(comm_fp); break; } } fclose(comm_fp); } closedir(task_dir); return found; }
如果需要基于调用栈匹配,可通过ptrace attach到目标线程,读取寄存器后遍历栈内存匹配符号即可。
注意:跨进程终止线程属于高危操作,会导致目标进程中该线程持有的锁、内存、文件句柄等资源无法正常释放,极易引发目标进程崩溃、数据损坏等问题,仅在明确可接受风险的场景下使用。
内容的提问来源于stack exchange,提问作者Ahmed Mahmoud
相关产品推荐
相关产品推荐

