七哥向前冲

You got a dream, you gotta to protect it!

站内搜索

选择搜索引擎,输入关键词开始搜索

Google
Bing
Yahoo
百度
💡 小贴士:选择不同的搜索引擎可能会得到不同的搜索结果

软件反调试(2)- 基于窗口列表的检测

发布日期:2025-07-02 |文章分类: 逆向分析

反调原理

通过枚举屏幕上所有的窗口,来检测当前是否打开了调试器

EnumWindows 枚举的时候,会进行窗口回调处理,直到所有窗口回调都结束后函数才返回

VS 提供了 Spy++ 可以查看窗口的信息,通过工具下的菜单可以打开 Spy++ 程序

20250702181531

打开 Spy++ 程序,并打开其搜索窗口,左键按下窗口上的圆饼,并将光标拖动到应用的窗口上

这时候,搜索窗口上就会显示要查看窗口的信息,如这里显示窗口标题为 x64dbg [管理员]

20250702182006

在打开 x64dbg 的情况下,通过 CheckDebugWindowByEnum 函数,编译并运行 anti02.exe 程序,终端显示输出如下的信息

20250702185742

除了通过 EnumWindows 的方式进行枚举,也可以通过 GetWindow 的方式进行遍历查找窗口

每个进程的主窗口通常都是顶级窗口,一个进程通常会有一个到多个顶级窗口,可以通过遍历顶级窗口搜索目标进程

通过 CheckDebugWindowByFind 函数,编译并运行 anti02.exe 程序,终端显示输出如下的信息

20250702185659

实现代码

完成的实现代码如下,包含前面说的两种方式 CheckDebugWindowByEnum 和 CheckDebugWindowByFind


#include <iostream>
#include <thread>
#include <string>
#include <vector>
#include <windows.h>
#include <TlHelp32.h>

BOOL isDebugging = FALSE;
std::string g_szWindowsTile = "";

BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam)
{
    char windows_title[256] = { 0 };
    char class_name[256] = { 0 };
    GetWindowText(hwnd, windows_title, 256);
    if (strlen(windows_title) > 0 && strstr(windows_title, g_szWindowsTile.c_str()) != 0)
    {
        std::cout << "handle:" << hwnd << " find debug windows:" << windows_title << std::endl;
        isDebugging = TRUE;
    }
    return TRUE;
}

bool CheckDebugWindowByEnum(const char* szWindowTitle)
{
    isDebugging = false;
    g_szWindowsTile = szWindowTitle;
    EnumWindows(EnumWindowsProc, NULL);
    return isDebugging;
}

bool CheckDebugWindowByFind(const char* szWindowName)
{
    std::vector<HWND> vec;
    HWND hWnd = GetTopWindow(0);
    while (hWnd)
    {
        if (GetParent(hWnd) == 0)
        {
            vec.push_back(hWnd);
        }
        hWnd = GetWindow(hWnd, GW_HWNDNEXT);
    }

    char szTempName[MAX_PATH] = { 0 };
    for (auto& v : vec)
    {
        GetWindowTextA(v, szTempName, MAX_PATH);
        if (strstr(szTempName, szWindowName) != 0)
        {
            std::cout << "find debug windows:" << szTempName << std::endl;
            return true;
        }
        memset(szTempName, 0, sizeof(szTempName));
    }

    return false;
}

void ThreadProc()
{
    while (true)
    {
        if (CheckDebugWindowByFind("x64dbg")) //CheckDebugWindowByEnum("x64dbg")
        {
            std::cout << "Debugging..." << std::endl;
        }
        else
        {
            std::cout << "Running..." << std::endl;
        }

        std::this_thread::sleep_for(std::chrono::milliseconds(1000));
    }
}

int main()
{
    std::thread thrd(ThreadProc);
    thrd.join();
    return 0;
}

逆向处理

和前面的[基于进程列表的检测]一样,修改内存中的 x64dbg 字符串,然后保存到补丁文件

重新运行补丁过的文件,已经去掉了基于窗口列表的反调试检测