当前位置:

首页 > 软件教程 > 绕过IsDebuggerPresent反调试技巧

绕过IsDebuggerPresent反调试技巧

在某爱论坛上看到一个师傅分享了一个关于如何绕过IsDebuggerPresent的反调试技术的Crackme教程。我闲来无事,决定复现并调试一下。首先,这里是原文的链接:https://www.52pojie.cn/thread-1432590-1-1.html反调试什么是反调试技术反调试技术,顾名思义,就是一种用来防止程序被调试的技术。简单的反调试往往是识别是否被调试,如果是则退出程序、封禁账号等(检测)。更复杂的反调试可以在反汇编代码中插入花指令,使调试器的反汇编引擎无法正确解析反汇编指令(干扰)。门

本文目录

    在某爱论坛上看到一个师傅分享了一个关于如何绕过IsDebuggerPresent的反调试技术的Crackme教程。我闲来无事,决定复现并调试一下。

    首先,这里是原文的链接:https://www.52pojie.cn/thread-1432590-1-1.html

    反调试

    什么是反调试技术

    反调试技术,顾名思义,就是一种用来防止程序被调试的技术。简单的反调试往往是识别是否被调试,如果是则退出程序、封禁账号等(检测)。更复杂的反调试可以在反汇编代码中插入花指令,使调试器的反汇编引擎无法正确解析反汇编指令(干扰)。门槛较高的反调试则可以从驱动层将调试权限清零,使得调试器失效等(权限清零)。反调试的手段可以大致归纳为:检测、干扰、权限清零三种常见手段。

    反调试手段层出不穷,可以分为两类:0环(内核级调试)和3环(用户应用层调试)。之前在写对抗沙盒的时候,判断父进程是否是explorer.exe,不是则退出,似乎也可以作为一种简单的反调试手段。之前没怎么了解过反调试,最多听海哥说过可以检查句柄表。今天就学习一下,先看看Windows的反调试API,0环反调试等以后知识储备够了再学习。

    IsDebuggerPresent

    IsDebuggerPresent API可以确定调用过程是否正在由用户模式调试器调试。

    参考链接:https://docs.microsoft.com/en-us/windows/win32/api/debugapi/nf-debugapi-isdebuggerpresent

    CheckRemoteDebuggerPresent

    CheckRemoteDebuggerPresent API可以确定是否正在调试指定的进程。

    参考链接:https://docs.microsoft.com/en-us/windows/win32/api/debugapi/nf-debugapi-checkremotedebuggerpresent

    开始调试

    打开Crackme程序,界面看起来人畜无害。

    如何绕过IsDebuggerPresent的反调试

    查壳结果显示,这是一个64位的MFC程序,用C++编写,没有壳。

    如何绕过IsDebuggerPresent的反调试

    ASLR

    什么是ASLR

    维基百科:在计算机科学中,地址空间配置随机加载(英语:Address space layout randomization,缩写ASLR,又称地址空间配置随机化、地址空间布局随机化)是一种防范内存损坏漏洞被利用的计算机安全技术。ASLR通过随机放置进程关键数据区域的地址空间来防止攻击者能可靠地跳转到内存的特定位置来利用函数。现代操作系统一般都加设这一机制,以防范恶意程序对已知地址进行Return-to-libc攻击。

    总的来说,ASLR就是将内存地址虚拟化,我们看到的内存地址并不是真正的内存地址偏移。

    ASLR的作用

    地址空间配置随机加载利用随机方式配置数据地址空间,使某些敏感数据配置到一个恶意程序无法事先获知的地址,令攻击者难以进行攻击。粗俗地说,就是使得每次调试工具(如OD、x64dbg等)加载程序后,地址是随机动态分配的,无法使用固定的地址进行定位。

    ASLR的体现

    用x64 debug打开程序。

    如何绕过IsDebuggerPresent的反调试

    到达系统断点,我们需要让他到达OEP,即程序入口点。

    ALT+F9

    如何绕过IsDebuggerPresent的反调试

    这里地址是7FF6E.....

    再看真实的入口点:

    如何绕过IsDebuggerPresent的反调试

    明显不一样。

    用MFC编译出的64位程序默认是开启ASLR的。

    关闭ASLR

    找到可选PE头的DllCharacteristics属性,取消DYNAMIC_BASE。

    如何绕过IsDebuggerPresent的反调试

    如何绕过IsDebuggerPresent的反调试

    回到真正的内存偏移。

    如何绕过IsDebuggerPresent的反调试

    关于DllCharacteristics可以参考:

    https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-image_optional_header64

    x64反调试

    F9让程序运行,但是一运行程序就会直接结束,不会弹出窗口。

    如何绕过IsDebuggerPresent的反调试

    做到这里不禁让我想到直接写反杀箱的时候一样,一运行就挂。

    大概代码是这样:

    if (explorer_id == parent_id) {
        CeatRemoThread(explorer_id);
    } else {
        exit(1);
    }

    只不过他这里是其他判断,比如是否被调试,是就直接exit,不是则执行下面的。

    于是对ExitProcess下断点。

    bp ExitProcess

    如何绕过IsDebuggerPresent的反调试

    下断点后直接F9运行到断点处。

    观察此时的堆栈。

    如何绕过IsDebuggerPresent的反调试

    这里又返回到crakeme,猜想是否是判断是否在调试之后又回到原本的函数。

    选中这一行按回车,跟进反汇编。

    如何绕过IsDebuggerPresent的反调试

    看到使用了IsDebuggerPresent来反调试。

    IDA Pro x64反调试

    进入IDA后,按G,并输入刚刚反汇编开始的地址。

    如何绕过IsDebuggerPresent的反调试

    跳转后。

    如何绕过IsDebuggerPresent的反调试

    选择startaddress。

    如何绕过IsDebuggerPresent的反调试

    F5进入伪代码。

    如何绕过IsDebuggerPresent的反调试

    这里很明确了,就是这个在反调试。

    IDA pro 反反调试处理

    可以直接在函数头部就直接ret,让他不走IsDebuggerPresent。

    这里要用到IDA Pro的KeyPatch功能:

    选中函数的头部,然后右键 → Key Patch → Patch:

    如何绕过IsDebuggerPresent的反调试

    接下来要将Patch完的结果导出到文件:

    Edit→ Patch Program → Apply patches to input file。

    如何绕过IsDebuggerPresent的反调试

    OK即可。

    如何绕过IsDebuggerPresent的反调试

    验证反反调试处理

    如何绕过IsDebuggerPresent的反调试

    正式Crack

    先随便输入一个数看看。

    如何绕过IsDebuggerPresent的反调试

    本来这里可以搜索字符串,但发现定位有些问题。

    换一种思路,定位API,以前写Win32程序的时候,要想在dialog中输出一段字符串,用SetWindowText,这里可以用这个API定位。

    bp SetWindowTextW

    如何绕过IsDebuggerPresent的反调试

    回车,断点就设置好了。

    如何绕过IsDebuggerPresent的反调试

    然后再点确定。

    如何绕过IsDebuggerPresent的反调试

    观察此时堆栈,出现了100和密码错误,并且有个返回函数。

    如何绕过IsDebuggerPresent的反调试

    选中返回函数那一行,回车。

    找到附近的"密码正确"。

    如何绕过IsDebuggerPresent的反调试

    IDA Pro分析

    跳转到刚刚"密码正确的地址"。

    如何绕过IsDebuggerPresent的反调试

    选中函数头部F5,进入伪代码。

    如何绕过IsDebuggerPresent的反调试

    得到:

    如何绕过IsDebuggerPresent的反调试

    说实话,这个伪代码不是很能直接看得懂,看了下原作者的,他调试的是Debug版的,跟这个Release版的还是有差别的,感觉Release版IDA很多都识别不了了。

    附上作者关于密码的源代码:

    void encodeCString(CString str) {                   //简单的字符串加密函数
        for (int i=0;ivoid CMFCApplication2Dlg::OnBnClickedButton1()
    {
    CString correctStr = L"密码正确";
    CString errorStr = L"密码错误";
    CString debugStr = L"检测到被调试";
    BOOL flag = TRUE;
    CString str = GetDlgItem(IDC_EDIT1)->GetWindowText();
    CString strList[5];
    long t1 = GetTickCount64();                     //获取开始时间

    // TODO: 在此添加控件通知处理程序代码
    if (str.GetLength() < 15 || str.GetLength() > 25) {
        flag = FALSE;
        encodeCString(errorStr);
        GetDlgItem(IDC_STATIC)->SetWindowTextW(errorStr);
    } else {
        //password
        //610 - 520 - 666 - 233
        CString sToken = _T("");
        int i = 0; // substring index to extract
        while (AfxExtractSubString(sToken, str, i, '-')) {
            //以-进行分割
            //..//work with sToken
            //..
            strList[i] = sToken.Trim();                     //字符串去空格
            i++;
            if (i > 4) {
                //如果分割大于4,则不满足条件
                flag = FALSE;
                encodeCString(errorStr);
                GetDlgItem(IDC_STATIC)->SetWindowTextW(errorStr);
                break;
            }
        }
        if (i != 4) {
            //如果分割不等于4,不满足条件
            flag = FALSE;
            encodeCString(errorStr);
            GetDlgItem(IDC_STATIC)->SetWindowTextW(errorStr);
        } else {
            for (i = 0; i < 4; i++) {
                if (strList[i].CompareNoCase(_T("610")) != 0 &&
                    strList[i].CompareNoCase(_T("520")) != 0 &&
                    strList[i].CompareNoCase(_T("666")) != 0 &&
                    strList[i].CompareNoCase(_T("233")) != 0) {
                    flag = FALSE;
                    encodeCString(errorStr);
                    GetDlgItem(IDC_STATIC)->SetWindowTextW(errorStr);
                    break;
                }
            }
            if (flag) {
                for (i = 0; i < 4; i++) {
                    strList[i].MakeReverse();
                }
                if (strList[0].CompareNoCase(_T("016")) != 0 ||
                    strList[1].CompareNoCase(_T("025")) != 0 ||
                    strList[2].CompareNoCase(_T("666")) != 0 ||
                    strList[3].CompareNoCase(_T("332")) != 0) {
                    flag = FALSE;
                    encodeCString(errorStr);
                    GetDlgItem(IDC_STATIC)->SetWindowTextW(errorStr);
                }
            }
        }
    }
    
    //判断标记
    if (flag) {
        encodeCString(correctStr);
        GetDlgItem(IDC_STATIC)->SetWindowTextW(correctStr);
    }
    
    Sleep(500);
    long t2 = GetTickCount64();                     //获取结束时间
    if (t2 - t1 >= 560) {                            //如果时间差大于等于560则超时,是被调试的情况
        encodeCString(debugStr);
        GetDlgItem(IDC_STATIC)->SetWindowTextW(debugStr);
    }

    }

    可以看到跟IDA生成的伪代码差距还是比较大,但还是不影响用源码分析一波算法。

    1. 通过GetTickCount64获取自系统启动以来经过的毫秒数,变量t1。

      GetTickCount64:https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-gettickcount64

    2. 获取输入的密码长度,如果长度小于15,或大于25,就赋值flag=false,然后SetWindowText"密码错误",并且可以看到这个字符串是由encodeCString加密了的,所以如果一开始如果想直接找字符串,可能就无法准确定位。

    3. AfxExtractSubString:https://docs.microsoft.com/en-us/cpp/mfc/reference/cstring-formatting-and-message-box-display?view=msvc-160

      这个API可用于从给定的源字符串中提取子字符串,通过这个API的返回值可以判断有几个"-",如果是4段密码,且以“-”分割,就可以进入比较字符串环节。

    4. CompareNoCase:https://docs.microsoft.com/en-us/windows/win32/api/chstring/nf-chstring-chstring-comparenocase

      该函数使用lstrcmpi函数对一个CString和另一个CString进行比较。

      返回值为:

      由参数lpsz指定这个用于比较的string。如果两个对象完全一致则返回0,如果小于lpsz,则返回-1,否则返回1。

      这里不等于-1就行,也就是不小于。

    5. MakeReverse:https://docs.microsoft.com/en-us/windows/win32/api/chstring/nf-chstring-chstring-makereverse

      功能大概就是反转字符串,所以四个数为610,520,666,233。

    6. 最后有一个计算时间差。

    所以总结一下就是:长度满足15-25之间,以“-”分割成4段,每段分别为610、520、666、233,反转后分别为016、025、666、332。

    比如这样:

    如何绕过IsDebuggerPresent的反调试

    但是这个小程序我还是发现不少bug,比如:

    如何绕过IsDebuggerPresent的反调试

    还有这样写的话程序会直接崩掉:

    如何绕过IsDebuggerPresent的反调试

    后记

    作为学习反反调试的初级阶段,重要的是使用x64 debug和IDA Pro分析的过程,这个还是很有帮助的。

    脑海中又浮现了海哥的话:“没有好的正向基础就不会有好的逆向基础。”

    如何绕过IsDebuggerPresent的反调试

    本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系bd@zhengruan.com
    作者最新文章
    软件教程 API
    相关文章 更多
    编程和我
    编程和我

    考取了程序员证书,却无法与“程序员”身份画等号,更偏爱算法和底层操作,对软件工程兴趣寥寥。编程作为兴趣能带来挑战与喜悦,一旦沦为重复工作便失去意义。坚持自然学习,远胜于强制赶进度。

    使用Golang操作TMDb API的使用小结
    使用Golang操作TMDb API的使用小结

    golang-tmdb库简化了与TMDbAPIv3的交互,支持获取电影、电视等媒体数据。需注册获取API密钥,支持自动重试和自定义基础URL。使用Go1.13+版本,通过goget安装后可用APIKey或BearerToken初始化客户端,常与GORM、Gin等库结合构建后端服务。

    C#怎么发送HTTP请求 C#如何使用HttpClient调用REST API接口获取数据【网络】
    C#怎么发送HTTP请求 C#如何使用HttpClient调用REST API接口获取数据【网络】

    C#怎么发送HTTP请求 C#如何使用HttpClient调用REST API接口获取数据【网络】 HttpClient 必须复用,不能每次 new 很多开发者踩的第一个坑,就是图省事,在方法里直接 new HttpClient()。这看似方便,实则后患无穷。频繁创建新实例,会导致底层套接字端口来不

    Golang 测试 HTTP API 方法详解
    Golang 测试 HTTP API 方法详解

    使用httptest可高效测试GolangHTTPAPI。1.用NewRecorder模拟请求并检查状态码、响应体和头信息;2.测试路由和中间件时将ServeMux注入测试;3.用NewServer启动临时服务进行端到端验证。

    PHP API接口开发指南_PHP RESTful设计详解
    PHP API接口开发指南_PHP RESTful设计详解

    开发PHPRESTfulAPI需理解HTTP协议与REST设计原则,使用GET、POST、PUT、DELETE等方法操作资源。2.项目结构应包含入口文件index.php、数据库配置、模型和响应工具。3.通过.htaccess实现URL重写,统一请求入口。4.index.php解析请求路径与方法,调用对应逻辑处理并返回JSON格式数据。

    想让Pandas更快?试试Modin加速!
    想让Pandas更快?试试Modin加速!

    选自towardsdatascience作者:ParulPandey机器之心编译参与:GeekAI、Chita「通过更改一行代码扩展你的pandas工作流。」Pandas是数据科学领域的专业人员所熟知的库。它提供了高性能且易于使用的结构和数据分析工具。然而,当处理超大数据集时,单核运行的Pandas性能会大打折扣,用户不得不转向不同的分布式系统来提升性能。然而,为了提升性能而进行的这种权衡会带来陡峭的学习曲线。实际上,用户只希望Pandas运行得更快,而不是为特定的硬件设置调整他们的工

    DirectX Redist 2010年2月版发布
    DirectX Redist 2010年2月版发布

    今天微软发布了2010年首个directxredistribute。推荐各位更新。不知directx为何物的同学请看此处(引用自微软):MicrosoftDirectX是这样一组技术:它们旨在使基于Windows的计算机成为运行和显示具有丰富多媒体元素(例如全色图形、视频、3D动画和丰富音频)的应用程序的理想平台。DirectX包括安全和性能更新程序,以及许多涵盖所有技术的新功能。应用程序可以通过使用DirectXAPI来访问这些新功能。下载地址http://download.micr

    诺基亚API登陆谷歌云,开发者使用更便捷
    诺基亚API登陆谷歌云,开发者使用更便捷

    近日,诺基亚宣布其三项网络API——按需服务质量、号码验证以及设备位置验证——正式登陆谷歌云市场,为全球开发者提供更加便捷的API接入方式。谷歌云市场为用户打造了一个集中展示谷歌云及其合作伙伴生态解决方案的统一目录,方便用户查找和使用所需服务。开发者可以通过谷歌云市场调用诺基亚的API,结合“网络即代码”平台及开发者门户的功能,在无需深入理解底层网络技术的前提下,实现对网络能力的标准化访问,从而在全球移动网络中开发并部署新型应用。未来,诺基亚还将陆续推出更多API上线谷歌云市场。诺基亚

    VBA API_04:如何创建按钮
    VBA API_04:如何创建按钮

    在创建了窗体的基础上,接下来我们将进一步添加一个按钮来增强程序的交互性。按钮是Windows系统中预定义的控件,因此无需额外注册,直接使用CreateWindowEx函数即可。在创建窗体之后、显示窗体之前,我们可以插入代码来创建这个按钮。按钮的父窗口句柄(hWndParent)应当设置为之前创建的窗体的句柄(hWnd):'创建窗体hWnd=CreateWindowEx(0,"myForm","myForm",WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USED

    PHP Web 服务开发与 API 设计错误处理
    PHP Web 服务开发与 API 设计错误处理

    PHPWeb服务开发中的错误处理涉及使用try-catch块、错误常量和自定义错误处理程序。在API设计中,最佳实践包括使用HTTP状态码、定义JSON错误响应、提供友好的错误消息,并使用自定义错误处理中间件来一致地处理错误,例如:使用自定义错误处理中间件捕获异常并返回JSON响应,其中包括错误代码和消息。

    查看更多
    精品专题 更多
    装机必备
    装机必备

    正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

    Windows
    Windows

    正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

    PDF教程
    PDF教程

    正软商城PDF教程频道提供PDF编辑、转换、合并、拆分、压缩及格式处理方法,同时介绍常用PDF软件和工具的使用技巧。

    Mac软件 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    Aperty macOS版
    Aperty macOS版
    macOS

    Aperty 是 Skylum 推出的 AI 人像修图软件,支持自然肤质修饰、瑕疵处理、面部与身体调整、预设及批量同步。

    WINDOWS 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    PixTV 网页版
    PixTV 网页版
    Windows

    PixTV 是一款在浏览器中使用的 AI 视觉内容创作平台,提供图片、视频、音频、文字生成、AI 分镜、局部重绘、提示词反推和无限画布等工具,适合短剧、漫剧、广告及社交媒体内容制作。