Anti Debug专题
19、通过ParentProcess检测调试器
一、课程目标
本节课主要学习如何通过检查父进程信息来检测调试器的存在。这是一种基于进程关系的反调试技术,通过分析当前进程的父进程是否为正常的系统进程来判断是否处于调试环境中。通过本课的学习,你将能够:
- 理解Windows进程层次结构和父进程概念
- 掌握获取和分析父进程信息的方法
- 学会编写基于父进程检测的反调试代码
- 理解调试器对进程创建的影响
- 了解该技术的检测和绕过方法
二、名词解释表
| 名词 | 解释 |
|---|---|
| ParentProcess | 父进程,创建当前进程的进程 |
| ProcessId | 进程标识符,系统分配给进程的唯一编号 |
| 系统进程 | Windows系统核心进程,如explorer.exe、svchost.exe等 |
| 进程树 | 进程之间的父子关系形成的树状结构 |
| 进程创建链 | 进程创建的完整链条 |
| 调试器进程 | 用于调试其他进程的特殊进程 |
三、技术原理
3.1 父进程概念
在Windows系统中,每个进程都有一个父进程(除了系统初始化进程)。父进程是创建当前进程的那个进程。正常情况下,应用程序的父进程应该是explorer.exe或其他系统进程。
3.2 调试器环境中的特殊行为
在调试器环境中,进程的创建方式会发生变化:
- 正常环境:应用程序通常由explorer.exe启动,父进程是正常的系统进程
- 调试环境:应用程序由调试器启动,父进程是调试器进程
3.3 检测原理
通过获取当前进程的父进程信息并分析其特征,可以判断程序是否在调试器中运行。如果父进程是已知的调试器进程,则很可能处于调试环境中。
四、代码实现
4.1 基础ParentProcess检测
#include <windows.h>
#include <tlhelp32.h>
#include <psapi.h>
#include <stdio.h>
// 获取当前进程的父进程ID
DWORD GetParentProcessId() {
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnapshot == INVALID_HANDLE_VALUE) {
return 0;
}
PROCESSENTRY32 pe32;
pe32.dwSize = sizeof(PROCESSENTRY32);
DWORD parentId = 0;
DWORD currentProcessId = GetCurrentProcessId();
if (Process32First(hSnapshot, &pe32)) {
do {
if (pe32.th32ProcessID == currentProcessId) {
parentId = pe32.th32ParentProcessID;
break;
}
} while (Process32Next(hSnapshot, &pe32));
}
CloseHandle(hSnapshot);
return parentId;
}
// 获取进程名称
BOOL GetProcessNameById(DWORD processId, char* processName, DWORD nameSize) {
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processId);
if (hProcess == NULL) {
// 尝试使用PROCESS_QUERY_LIMITED_INFORMATION权限
hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, processId);
if (hProcess == NULL) {
return FALSE;
}
}
BOOL result = FALSE;
// 尝试使用GetProcessImageFileName
char imagePath[MAX_PATH];
if (GetProcessImageFileNameA(hProcess, imagePath, sizeof(imagePath))) {
// 提取文件名
char* fileName = strrchr(imagePath, '\\');
if (fileName != NULL) {
fileName++; // 跳过反斜杠
strncpy_s(processName, nameSize, fileName, _TRUNCATE);
result = TRUE;
}
}
CloseHandle(hProcess);
return result;
}
// 基础ParentProcess检测
BOOL DetectDebuggerViaParentProcess() {
printf("=== ParentProcess检测 ===\n");
DWORD parentId = GetParentProcessId();
if (parentId == 0) {
printf("无法获取父进程ID。\n");
return FALSE;
}
printf("当前进程ID: %lu\n", GetCurrentProcessId());
printf("父进程ID: %lu\n", parentId);
char parentName[MAX_PATH] = {0};
if (GetProcessNameById(parentId, parentName, sizeof(parentName))) {
printf("父进程名称: %s\n", parentName);
} else {
printf("无法获取父进程名称。\n");
return FALSE;
}
// 常见的调试器进程名称
const char* debuggerNames[] = {
"ollydbg.exe",
"x32dbg.exe",
"x64dbg.exe",
"windbg.exe",
"idaq.exe",
"idaq64.exe",
"cheatengine-x86_64.exe",
"cheatengine-i386.exe",
"immunitydebugger.exe",
"radare2.exe",
"visual studio",
"devenv.exe",
"msvsmon.exe"
};
// 转换为小写进行比较
char lowerParentName[MAX_PATH];
strcpy_s(lowerParentName, parentName);
_strlwr_s(lowerParentName);
BOOL debuggerDetected = FALSE;
for (int i = 0; i < sizeof(debuggerNames)/sizeof(debuggerNames[0]); i++) {
char lowerDebugger[MAX_PATH];
strcpy_s(lowerDebugger, debuggerNames[i]);
_strlwr_s(lowerDebugger);
if (strstr(lowerParentName, lowerDebugger) != NULL) {
printf("检测到调试器父进程: %s\n", parentName);
debuggerDetected = TRUE;
break;
}
}
if (!debuggerDetected) {
printf("父进程不是已知调试器,行为正常。\n");
}
return debuggerDetected;
}
4.2 增强版ParentProcess检测
// 增强版ParentProcess检测
BOOL EnhancedParentProcessDetection() {
printf("=== 增强版ParentProcess检测 ===\n");
DWORD parentId = GetParentProcessId();
if (parentId == 0) {
printf("无法获取父进程ID。\n");
return FALSE;
}
printf("父进程ID: %lu\n", parentId);
char parentName[MAX_PATH] = {0};
if (!GetProcessNameById(parentId, parentName, sizeof(parentName))) {
printf("无法获取父进程名称。\n");
return FALSE;
}
printf("父进程名称: %s\n", parentName);
// 更全面的调试器检测列表
const char* suspiciousNames[] = {
"ollydbg",
"x32dbg",
"x64dbg",
"windbg",
"ida",
"cheatengine",
"immunity",
"radare",
"debug",
"dbg",
"visual studio",
"devenv",
"msvsmon",
"dbgview",
"process hacker",
"process monitor",
"procmon",
"wireshark",
"fiddler"
};
char lowerParentName[MAX_PATH];
strcpy_s(lowerParentName, parentName);
_strlwr_s(lowerParentName);
BOOL suspiciousDetected = FALSE;
for (int i = 0; i < sizeof(suspiciousNames)/sizeof(suspiciousNames[0]); i++) {
char lowerSuspicious[MAX_PATH];
strcpy_s(lowerSuspicious, suspiciousNames[i]);
_strlwr_s(lowerSuspicious);
if (strstr(lowerParentName, lowerSuspicious) != NULL) {
printf("检测到可疑父进程: %s (包含 '%s')\n", parentName, suspiciousNames[i]);
suspiciousDetected = TRUE;
}
}
// 检查父进程是否为系统进程
const char* systemProcessNames[] = {
"explorer.exe",
"svchost.exe",
"services.exe",
"winlogon.exe",
"csrss.exe",
"lsass.exe",
"smss.exe"
};
BOOL isSystemProcess = FALSE;
char lowerSystemName[MAX_PATH];
for (int i = 0; i < sizeof(systemProcessNames)/sizeof(systemProcessNames[0]); i++) {
strcpy_s(lowerSystemName, systemProcessNames[i]);
_strlwr_s(lowerSystemName);
_strlwr_s(lowerParentName);
if (strstr(lowerParentName, lowerSystemName) != NULL) {
isSystemProcess = TRUE;
break;
}
}
if (!isSystemProcess && !suspiciousDetected) {
printf("父进程既不是系统进程也不是已知调试器,可能存在风险。\n");
// 可以根据需要返回TRUE来标记为可疑
}
return suspiciousDetected;
}
// 检查进程创建链
BOOL CheckProcessCreationChain() {
printf("=== 进程创建链检测 ===\n");
DWORD currentProcessId = GetCurrentProcessId();
DWORD processId = currentProcessId;
printf("进程创建链:\n");
// 最多追溯10层
for (int i = 0; i < 10; i++) {
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnapshot == INVALID_HANDLE_VALUE) {
break;
}
PROCESSENTRY32 pe32;
pe32.dwSize = sizeof(PROCESSENTRY32);
DWORD parentId = 0;
BOOL found = FALSE;
if (Process32First(hSnapshot, &pe32)) {
do {
if (pe32.th32ProcessID == processId) {
parentId = pe32.th32ParentProcessID;
printf(" [%d] PID: %lu -> PPID: %lu (%s)\n",
i, processId, parentId, pe32.szExeFile);
found = TRUE;
break;
}
} while (Process32Next(hSnapshot, &pe32));
}
CloseHandle(hSnapshot);
if (!found || parentId == 0) {
break;
}
// 检查父进程是否为调试器
char parentName[MAX_PATH];
if (GetProcessNameById(parentId, parentName, sizeof(parentName))) {
char lowerParentName[MAX_PATH];
strcpy_s(lowerParentName, parentName);
_strlwr_s(lowerParentName);
const char* debuggerNames[] = {
"ollydbg", "x32dbg", "x64dbg", "windbg", "ida",
"cheatengine", "immunity", "radare", "devenv"
};
for (int j = 0; j < sizeof(debuggerNames)/sizeof(debuggerNames[0]); j++) {
char lowerDebugger[MAX_PATH];
strcpy_s(lowerDebugger, debuggerNames[j]);
_strlwr_s(lowerDebugger);
if (strstr(lowerParentName, lowerDebugger) != NULL) {
printf(" 在创建链中检测到调试器: %s\n", parentName);
return TRUE;
}
}
}
processId = parentId;
}
return FALSE;
}
4.3 基于NT API的ParentProcess检测
// NT API相关定义
typedef enum _PROCESSINFOCLASS {
ProcessBasicInformation = 0,
} PROCESSINFOCLASS;
typedef struct _PROCESS_BASIC_INFORMATION {
NTSTATUS ExitStatus;
PVOID PebBaseAddress;
ULONG_PTR AffinityMask;
KPRIORITY BasePriority;
ULONG_PTR UniqueProcessId;
ULONG_PTR InheritedFromUniqueProcessId;
} PROCESS_BASIC_INFORMATION, *PPROCESS_BASIC_INFORMATION;
typedef LONG NTSTATUS;
#define STATUS_SUCCESS 0
typedef NTSTATUS (NTAPI *PNtQueryInformationProcess)(
HANDLE ProcessHandle,
PROCESSINFOCLASS ProcessInformationClass,
PVOID ProcessInformation,
ULONG ProcessInformationLength,
PULONG ReturnLength
);
// 使用NT API获取父进程ID
DWORD GetParentProcessIdViaNTAPI() {
HMODULE hNtdll = GetModuleHandle(L"ntdll.dll");
if (hNtdll == NULL) {
return 0;
}
PNtQueryInformationProcess NtQueryInformationProcess =
(PNtQueryInformationProcess)GetProcAddress(hNtdll, "NtQueryInformationProcess");
if (NtQueryInformationProcess == NULL) {
return 0;
}
PROCESS_BASIC_INFORMATION pbi = {0};
ULONG returnLength = 0;
NTSTATUS status = NtQueryInformationProcess(
GetCurrentProcess(),
ProcessBasicInformation,
&pbi,
sizeof(pbi),
&returnLength
);
if (status == STATUS_SUCCESS) {
return (DWORD)pbi.InheritedFromUniqueProcessId;
}
return 0;
}
// 比较Toolhelp和NT API的结果
BOOL CompareParentProcessDetectionMethods() {
printf("=== 父进程检测方法比较 ===\n");
DWORD toolhelpParentId = GetParentProcessId();
DWORD ntapiParentId = GetParentProcessIdViaNTAPI();
printf("Toolhelp方法获取的父进程ID: %lu\n", toolhelpParentId);
printf("NT API方法获取的父进程ID: %lu\n", ntapiParentId);
if (toolhelpParentId != ntapiParentId) {
printf("两种方法结果不一致,可能存在调试器。\n");
return TRUE;
}
char toolhelpParentName[MAX_PATH] = {0};
char ntapiParentName[MAX_PATH] = {0};
if (GetProcessNameById(toolhelpParentId, toolhelpParentName, sizeof(toolhelpParentName)) &&
GetProcessNameById(ntapiParentId, ntapiParentName, sizeof(ntapiParentName))) {
printf("Toolhelp父进程名称: %s\n", toolhelpParentName);
printf("NT API父进程名称: %s\n", ntapiParentName);
if (_stricmp(toolhelpParentName, ntapiParentName) != 0) {
printf("两种方法获取的父进程名称不一致,可能存在调试器。\n");
return TRUE;
}
}
return FALSE;
}
4.4 完整的ParentProcess检测实现
// ParentProcess检测工具类
class ParentProcessDetector {
public:
static void DisplayProcessInfo() {
printf("=== 进程信息 ===\n");
printf("当前进程ID: %lu\n", GetCurrentProcessId());
printf("当前进程句柄: 0x%p\n", GetCurrentProcess());
printf("\n");
}
static BOOL DetectKnownParentProcessIssues() {
printf("=== ParentProcess相关检测 ===\n");
BOOL detected = FALSE;
// 基础检测
if (DetectDebuggerViaParentProcess()) {
detected = TRUE;
}
// 增强检测
if (EnhancedParentProcessDetection()) {
detected = TRUE;
}
// 进程创建链检测
if (CheckProcessCreationChain()) {
detected = TRUE;
}
// 方法比较
if (CompareParentProcessDetectionMethods()) {
detected = TRUE;
}
if (!detected) {
printf("未检测到ParentProcess相关异常。\n");
}
return detected;
}
static BOOL DetectSuspiciousParentProcessBehavior() {
printf("=== 可疑ParentProcess行为检测 ===\n");
DWORD parentId = GetParentProcessId();
if (parentId == 0) {
printf("无法获取父进程ID。\n");
return FALSE;
}
char parentName[MAX_PATH] = {0};
if (!GetProcessNameById(parentId, parentName, sizeof(parentName))) {
printf("无法获取父进程名称。\n");
return FALSE;
}
printf("父进程名称: %s\n", parentName);
// 检查父进程的创建时间
HANDLE hParentProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, parentId);
if (hParentProcess != NULL) {
FILETIME creationTime, exitTime, kernelTime, userTime;
if (GetProcessTimes(hParentProcess, &creationTime, &exitTime, &kernelTime, &userTime)) {
// 检查父进程是否在当前进程之后创建(异常情况)
FILETIME currentCreationTime;
HANDLE hCurrentProcess = GetCurrentProcess();
if (GetProcessTimes(hCurrentProcess, ¤tCreationTime, &exitTime, &kernelTime, &userTime)) {
// 比较时间
LARGE_INTEGER parentTime, currentTime;
parentTime.LowPart = creationTime.dwLowDateTime;
parentTime.HighPart = creationTime.dwHighDateTime;
currentTime.LowPart = currentCreationTime.dwLowDateTime;
currentTime.HighPart = currentCreationTime.dwHighDateTime;
if (parentTime.QuadPart > currentTime.QuadPart) {
printf("父进程创建时间晚于当前进程,异常行为。\n");
CloseHandle(hParentProcess);
return TRUE;
}
}
}
CloseHandle(hParentProcess);
}
// 检查父进程是否具有GUI
HWND parentWindow = GetWindow(GetDesktopWindow(), GW_CHILD);
BOOL hasGui = FALSE;
while (parentWindow != NULL) {
DWORD windowProcessId;
GetWindowThreadProcessId(parentWindow, &windowProcessId);
if (windowProcessId == parentId) {
hasGui = TRUE;
break;
}
parentWindow = GetWindow(parentWindow, GW_HWNDNEXT);
}
if (!hasGui) {
printf("父进程没有GUI窗口,可能是控制台调试器。\n");
// 根据需要可以返回TRUE
}
return FALSE;
}
};
4.5 反调试实现
// 简单的ParentProcess反调试
VOID SimpleParentProcessAntiDebug() {
if (ParentProcessDetector::DetectKnownParentProcessIssues()) {
printf("通过ParentProcess检测到调试器存在!程序即将退出。\n");
ExitProcess(1);
}
}
// 多层次ParentProcess检测
BOOL MultiLayerParentProcessDetection() {
// 第一层:基础检测
if (ParentProcessDetector::DetectKnownParentProcessIssues()) {
return TRUE;
}
// 第二层:可疑行为检测
if (ParentProcessDetector::DetectSuspiciousParentProcessBehavior()) {
return TRUE;
}
// 第三层:定期检测
static DWORD lastCheck = 0;
DWORD currentTime = GetTickCount();
if (currentTime - lastCheck > 3000) { // 每3秒检测一次
lastCheck = currentTime;
if (ParentProcessDetector::DetectKnownParentProcessIssues()) {
return TRUE;
}
}
return FALSE;
}
// 增强版反调试
VOID EnhancedParentProcessAntiDebug() {
// 多次检测
for (int i = 0; i < 3; i++) {
if (MultiLayerParentProcessDetection()) {
printf("第%d次ParentProcess检测发现调试环境!\n", i + 1);
// 随机化响应
int response = rand() % 4;
switch (response) {
case 0:
ExitProcess(0);
case 1:
printf("发生未知错误。\n");
Sleep(5000);
exit(1);
case 2:
// 执行错误指令
__debugbreak();
case 3:
// 进入无限循环
while (1) {
Sleep(1000);
}
}
}
// 随机延迟
Sleep(rand() % 100 + 50);
}
printf("ParentProcess反调试检测通过。\n");
}
4.6 绕过ParentProcess检测的方法
// ParentProcess检测绕过技术
class ParentProcessObfuscator {
public:
// 修改父进程信息(需要高级权限)
static BOOL SpoofParentProcess() {
printf("尝试伪造父进程信息...\n");
// 这需要非常高级的技术,通常涉及内核模式操作
// 实际实现极其复杂且可能违法
return FALSE;
}
// 创建中间进程来隐藏调试器
static BOOL CreateIntermediateProcess() {
printf("创建中间进程来隐藏调试器痕迹...\n");
// 通过创建一个正常的中间进程来启动目标程序
// 这样可以使得最终程序的父进程看起来正常
return FALSE;
}
// 模拟正常父进程行为
static BOOL SimulateNormalParentProcess() {
printf("模拟正常父进程行为...\n");
// 可以通过修改进程信息来模拟正常行为
return FALSE;
}
};
// 综合绕过方法
VOID ComprehensiveParentProcessBypass() {
// 创建中间进程
ParentProcessObfuscator::CreateIntermediateProcess();
// 模拟正常行为
ParentProcessObfuscator::SimulateNormalParentProcess();
printf("ParentProcess检测绕过完成。\n");
}
4.7 完整测试程序
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
// 前面实现的函数声明
BOOL DetectDebuggerViaParentProcess();
BOOL MultiLayerParentProcessDetection();
VOID ComprehensiveParentProcessBypass();
// 显示系统进程信息
VOID DisplaySystemProcessInfo() {
printf("=== 系统进程信息 ===\n");
// 显示一些常见的系统进程
const char* systemProcesses[] = {
"explorer.exe",
"svchost.exe",
"services.exe",
"winlogon.exe",
"csrss.exe",
"lsass.exe"
};
printf("常见系统进程:\n");
for (int i = 0; i < sizeof(systemProcesses)/sizeof(systemProcesses[0]); i++) {
printf(" %s\n", systemProcesses[i]);
}
printf("\n");
}
// 性能测试
VOID PerformanceTest() {
const int iterations = 100;
printf("=== 性能测试 (%d次调用) ===\n", iterations);
// 测试ParentProcess检测方法
DWORD start = GetTickCount();
for (int i = 0; i < iterations; i++) {
DetectDebuggerViaParentProcess();
}
DWORD parentTime = GetTickCount() - start;
printf("ParentProcess检测耗时: %lu ms\n", parentTime);
printf("\n");
}
// 主程序
int main() {
srand((unsigned int)time(NULL));
printf("通过ParentProcess检测调试器演示程序\n");
printf("===============================\n\n");
// 显示系统进程信息
DisplaySystemProcessInfo();
// 显示进程信息
ParentProcessDetector::DisplayProcessInfo();
// 基础ParentProcess检测
DetectDebuggerViaParentProcess();
// 增强版检测
EnhancedParentProcessDetection();
// 进程创建链检测
CheckProcessCreationChain();
// 方法比较
CompareParentProcessDetectionMethods();
// 可疑行为检测
ParentProcessDetector::DetectSuspiciousParentProcessBehavior();
// 性能测试
PerformanceTest();
// 实际应用示例
printf("=== 反调试检测 ===\n");
if (MultiLayerParentProcessDetection()) {
printf("检测到调试环境,执行反调试措施。\n");
// 这里可以执行各种反调试措施
// 为演示目的,我们只是显示信息而不真正退出
printf("(演示模式:不实际退出程序)\n");
} else {
printf("未检测到调试环境,程序正常运行。\n");
MessageBoxW(NULL, L"ParentProcess检测通过,程序正常运行", L"提示", MB_OK);
}
// 演示绕过方法
printf("\n=== 绕过演示 ===\n");
printf("执行ParentProcess绕过...\n");
ComprehensiveParentProcessBypass();
printf("绕过完成后再次检测:\n");
if (MultiLayerParentProcessDetection()) {
printf("仍然检测到调试环境。\n");
} else {
printf("检测结果显示未发现ParentProcess异常。\n");
}
return 0;
}
4.8 高级技巧和注意事项
// 抗干扰版本(防止简单的Hook)
BOOL AntiTamperParentProcessDetection() {
// 多次调用并验证
BOOL results[3];
for (int i = 0; i < 3; i++) {
results[i] = MultiLayerParentProcessDetection();
Sleep(10); // 简短延迟
}
// 检查结果一致性
for (int i = 1; i < 3; i++) {
if (results[i] != results[0]) {
// 结果不一致,可能是被干扰了
return TRUE; // 假设存在调试环境
}
}
return results[0];
}
// 时间差检测增强版
BOOL TimeBasedParentProcessEnhancedDetection() {
DWORD start = GetTickCount();
// 执行多次ParentProcess检测
for (int i = 0; i < 10; i++) {
if (MultiLayerParentProcessDetection()) {
return TRUE;
}
}
DWORD end = GetTickCount();
// 如果执行时间过长,可能是被调试
if ((end - start) > 2000) { // 超过2秒
return TRUE;
}
return FALSE;
}
// 综合检测函数
BOOL ComprehensiveParentProcessDetection() {
// 抗干扰检测
if (AntiTamperParentProcessDetection()) {
return TRUE;
}
// 时间差检测
if (TimeBasedParentProcessEnhancedDetection()) {
return TRUE;
}
// 其他ParentProcess检测
if (ParentProcessDetector::DetectSuspiciousParentProcessBehavior()) {
return TRUE;
}
return FALSE;
}
// 动态获取API地址(避免静态导入)
FARPROC GetDynamicAPIAddress(LPCSTR moduleName, LPCSTR functionName) {
// 动态加载模块
HMODULE hModule = LoadLibraryA(moduleName);
if (hModule == NULL) {
return NULL;
}
// 获取函数地址
FARPROC pfn = GetProcAddress(hModule, functionName);
return pfn;
}
// 检测ParentProcess调用的完整性
BOOL ValidateParentProcessCall() {
// 可以通过检查相关函数代码的完整性来验证未被修改
// 这需要更高级的技术,如代码校验和检查
return TRUE;
}
// 多线程环境下的ParentProcess检测
BOOL MultiThreadParentProcessDetection() {
printf("=== 多线程ParentProcess检测 ===\n");
// 在多线程环境中进行检测可以增加检测的可靠性
return FALSE;
}
五、课后作业
-
基础练习:
- 在不同Windows版本下测试上述代码的兼容性
- 研究ParentProcess在不同启动方式下的行为差异
- 实现对ParentProcess信息的完整验证
-
进阶练习:
- 实现一个完整的ParentProcess行为监控器
- 研究如何检测通过进程伪造绕过检测的调试器
- 设计一个多层检测机制,结合ParentProcess和其他反调试技术
-
思考题:
- ParentProcess检测方法有哪些明显的局限性?
- 如何提高ParentProcess检测的准确性和隐蔽性?
- 现代调试器采用了哪些技术来对抗ParentProcess检测?
-
扩展阅读:
- 研究Windows进程管理机制
- 了解进程创建和管理的内部原理
- 学习现代反反调试技术