码农知识堂 - 1000bd
  •   Python
  •   PHP
  •   JS/TS
  •   JAVA
  •   C/C++
  •   C#
  •   GO
  •   Kotlin
  •   Swift
  • 4.4 x64dbg 绕过反调试保护机制


    合集 - 灰帽黑客:攻守道(83)
    1.1.13 导出表劫持ShellCode加载09-012.1.12 进程注入ShellCode套接字09-013.1.10 内存ShellCode注入与格式化08-314.1.9 动态解密ShellCode反弹08-315.1.8 运用C编写ShellCode代码08-306.1.7 完善自定位ShellCode08-307.1.6 编写双管道ShellCode08-298.1.5 编写自定位ShellCode弹窗08-299.1.4 编写简易ShellCode弹窗08-2810.1.3 Metasploit 生成SSL加密载荷08-2811.1.1 Metasploit 工具简介08-2812.5.14 汇编语言:仿写Switch选择结构08-2413.5.13 汇编语言:仿写For循环语句08-2414.5.12 汇编语言:仿写While循环语句08-2415.5.11 汇编语言:仿写IF条件语句08-2416.5.10 汇编语言:汇编过程与结构08-2417.5.9 汇编语言:浮点数操作指令08-2318.5.8 汇编语言:汇编高效除法运算08-2319.5.7 汇编语言:汇编高效乘法运算08-2320.5.6 汇编语言:汇编高效数组寻址08-2321.5.5 汇编语言:函数调用约定08-2222.5.4 汇编语言:算数运算指令集08-2223.5.3 汇编语言:字符串操作指令08-2224.5.2 汇编语言:标志位测试指令08-2225.5.1 汇编语言:汇编语言概述08-2226.4.9 C++ Boost 命令行解析库08-2227.4.8 C++ Boost 应用JSON解析库08-2228.4.7 C++ Boost 多线程并发库08-2129.4.6 C++ Boost 函数绑定回调库08-2130.4.5 C++ Boost 文件目录操作库08-2131.4.4 C++ Boost 数据集序列化库08-2132.4.3 C++ Boost 日期时间操作库08-1833.4.2 C++ Boost 内存池管理库08-1834.4.1 C++ Boost 字符串处理库08-1835.11.1 C++ STL 应用字典与列表08-1736.10.1 C++ STL 模板适配与迭代器08-1737.9.1 C++ STL 排序、算数与集合08-1738.8.1 C++ STL 变易拷贝算法08-1639.7.1 C++ STL 非变易查找算法08-1640.6.1 C++ STL 序列映射容器08-1641.5.1 C++ STL 集合数据容器08-1642.4.1 C++ STL 动态链表容器08-1643.3.1 C++ STL 双向队列容器08-1644.2.1 C++ STL 数组向量容器08-1645.1.1 C++ STL 字符串构造函数08-1646.7.5 C/C++ 实现链表队列08-1547.7.4 C/C++ 实现链表栈08-1548.7.3 C/C++ 实现顺序栈08-1549.7.2 C/C++ 实现动态链表08-1550.7.1 C/C++ 实现动态数组08-1551.9.0 Python 内置模块应用08-1452.8.0 Python 使用进程与线程08-1453.7.0 Python 面向对象编程08-1454.6.0 Python 使用函数装饰器08-1455.5.0 Python 定义并使用函数08-1356.4.0 Python 变量与作用域08-1357.3.0 Python 迭代器与生成器08-1258.2.0 Python 数据结构与类型08-1159.21.1 使用PEfile分析PE文件08-1060.1.0 Python 标准输入与输出08-0961.1.8 运用C编写ShellCode代码07-1362.5.2 基于ROP漏洞挖掘与利用07-1263.5.1 缓冲区溢出与攻防博弈07-1264.2.0 熟悉CheatEngine修改器07-1165.4.9 x64dbg 内存处理与差异对比07-1166.4.10 x64dbg 反汇编功能的封装07-1167.4.8 x64dbg 学会扫描应用堆栈07-1068.4.7 x64dbg 应用层的钩子扫描07-1069.4.6 x64dbg 内存扫描与查壳实现07-0970.4.5 x64dbg 探索钩子劫持技术07-09
    71.4.4 x64dbg 绕过反调试保护机制07-08
    72.4.3 x64dbg 搜索内存可利用指令07-0773.4.2 x64dbg 针对PE文件的扫描07-0774.4.1 探索LyScript漏洞挖掘插件07-0675.1.5 为x64dbg编写插件07-0676.1.1 熟悉x64dbg调试器07-0677.1.7 完善自定位ShellCode后门07-0578.1.6 编写双管道ShellCode后门07-0479.1.5 编写自定位ShellCode弹窗07-0380.1.4 编写简易ShellCode弹窗07-0281.1.3 Metasploit 生成SSL加密载荷07-0182.1.1 Metasploit 工具简介06-3083.2.1 PE结构:文件映射进内存09-04
    收起

    在Windows平台下,应用程序为了保护自己不被调试器调试会通过各种方法限制进程调试自身,通常此类反调试技术会限制我们对其进行软件逆向与漏洞分析,下面是一些常见的反调试保护方法:

    • IsDebuggerPresent:检查当前程序是否在调试器环境下运行。
    • OutputDebugString:向调试器发送特定的字符串,以检查是否有调试器在运行。
    • CloseHandle:检查特定的句柄是否关闭,以判断是否有调试器在运行。
    • GetTickCount:检查程序运行的时间,以判断是否有调试器在运行。
    • PEB (Process Environment Block):检查PEB数据结构中的特定字段,以判断是否有调试器在运行。
    • SEH (Structured Exception Handling):检查异常处理程序是否被替换,以判断是否有调试器在运行。

    我们以第一种IsDebuggerPresent反调试为例,该函数用于检查当前程序是否在调试器的环境下运行。函数返回一个布尔值,如果当前程序正在被调试,则返回True,否则返回False。

    函数通过检查特定的内存地址来判断是否有调试器在运行。具体来说,该函数检查了PEB(进程环境块)数据结构中的_PEB_LDR_DATA字段,该字段标识当前程序是否处于调试状态。如果该字段的值为1,则表示当前程序正在被调试,否则表示当前程序没有被调试。

    获取PEB的方式有许多,虽然LyScript插件内提供了get_peb_address(dbg.get_process_id())系列函数可以直接获取到进程的PEB信息,但为了分析实现原理,笔者首先会通过代码来实现这个功能;

    如下代码,通过在目标程序中创建一个堆空间并向其中写入汇编指令,最后将程序的EIP寄存器设置为堆空间的首地址,以使得程序运行时执行堆空间中的汇编指令。

    具体来说,该代码通过调用MyDebug类的create_alloc方法创建一个堆空间,并通过调用assemble_at方法向堆空间写入汇编指令。该代码先写入mov eax,fs:[0x30]指令,该指令将FS寄存器的值加上0x30的偏移量存入EAX寄存器,从而得到_PEB数据结构的地址。

    然后,代码再写入mov eax,[eax+0x0C]指令,该指令将EAX寄存器加上0x0C的偏移量后的值存入EAX寄存器,从而得到_PEB_LDR_DATA数据结构的地址。最后,写入jmp eip指令,以使得程序回到原来的EIP位置。最后,代码通过调用set_register方法设置EIP寄存器的值为堆空间的首地址,以使得程序运行时执行堆空间中的汇编指令。

    from LyScript32 import MyDebug
    
    if __name__ == "__main__":
        dbg = MyDebug(address="127.0.0.1")
        dbg.connect()
    
        # 保存当前EIP
        eip = dbg.get_register("eip")
    
        # 创建堆
        heap_addres = dbg.create_alloc(1024)
        print("堆空间地址: {}".format(hex(heap_addres)))
    
        # 写出汇编指令
        # mov eax,fs:[0x30] 得到 _PEB
        dbg.assemble_at(heap_addres,"mov eax,fs:[0x30]")
        asmfs_size = dbg.get_disasm_operand_size(heap_addres)
    
        # 写出汇编指令
        # mov eax,[eax+0x0C] 得到 _PEB_LDR_DATA
        dbg.assemble_at(heap_addres + asmfs_size, "mov eax, [eax + 0x0C]")
        asmeax_size = dbg.get_disasm_operand_size(heap_addres + asmfs_size)
    
        # 跳转回EIP位置
        dbg.assemble_at(heap_addres+ asmfs_size + asmeax_size , "jmp {}".format(hex(eip)))
    
        # 设置EIP到堆首地址
        dbg.set_register("eip",heap_addres)
    
        dbg.close()
    

    当这段读入汇编指令被执行时,此时PEB入口地址将被返回给EAX寄存器,用户只需要取出该寄存器中的参数即可实现读取进程PEB的功能。

    当PEB入口地址得到之后,只需要检查PEB+2的位置标志,通过write_memory_byte()函数向此处写出0即可绕过反调试,从而让程序可以被正常调试。

    from LyScript32 import MyDebug
    
    if __name__ == "__main__":
        # 初始化
        dbg = MyDebug()
        dbg.connect()
    
        # 通过PEB找到调试标志位
        peb = dbg.get_peb_address(dbg.get_process_id())
        print("调试标志地址: 0x{:x}".format(peb+2))
    
        flag = dbg.read_memory_byte(peb+2)
        print("调试标志位: {}".format(flag))
    
        # 将调试标志设置为0即可过掉反调试
        nop_debug = dbg.write_memory_byte(peb+2,0)
        print("反调试绕过状态: {}".format(nop_debug))
        
        dbg.close()
    

    这里笔者继续拓展一个新知识点,如何实现绕过进程枚举功能,病毒会利用进程枚举函数Process32FirstW及Process32NextW枚举所有运行的进程以确认是否有调试器在运行,我们可以在特定的函数开头处写入SUB EAX,EAX RET指令让其无法调用枚举函数从而失效,写入汇编指令集需要依赖于set_assemble_opcde函数,只需要向函数内传入内存地址,则自动替换地址处的汇编指令集;

    from LyScript32 import MyDebug
    
    # 得到所需要的机器码
    def set_assemble_opcde(dbg,address):
        # 得到第一条长度
        opcode_size = dbg.assemble_code_size("sub eax,eax")
    
        # 写出汇编指令
        dbg.assemble_at(address, "sub eax,eax")
        dbg.assemble_at(address + opcode_size , "ret")
    
    if __name__ == "__main__":
        # 初始化
        dbg = MyDebug()
        dbg.connect()
    
        # 得到函数所在内存地址
        process32first = dbg.get_module_from_function("kernel32","Process32FirstW")
        process32next = dbg.get_module_from_function("kernel32","Process32NextW")
        print("process32first = 0x{:x} | process32next = 0x{:x}".format(process32first,process32next))
    
        # 替换函数位置为sub eax,eax ret
        set_assemble_opcde(dbg, process32first)
        set_assemble_opcde(dbg, process32next)
    
        dbg.close()
    

    当上述代码被运行后,则Process32FirstW与Process32FirstW函数位置将被依次写出返回指令,从而让进程枚举失效,输出效果图如下所示;

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

  • 相关阅读:
    行业追踪,2023-09-27
    ES6(1) let 和 const
    吉时利 Keithley 2700数据采集器技术参数
    Windows下实用工具汇总(更新……)
    机械工程英语第二版叶邦彦-汉语翻译最多单元版
    本地开发好的 SAP UI5 应用部署到 ABAP 服务器时,中文字符变成乱码的原因分析和解决方案
    mojo语言 入门体验 环境安装
    【部署之后的错误排查】远程不能访问
    SpringCloud 学习笔记总结 (二)
    一种单键开关机电路图
  • 原文地址:https://www.cnblogs.com/LyShark/p/17536708.html
  • 最新文章
  • 攻防演习之三天拿下官网站群
    数据安全治理学习——前期安全规划和安全管理体系建设
    企业安全 | 企业内一次钓鱼演练准备过程
    内网渗透测试 | Kerberos协议及其部分攻击手法
    0day的产生 | 不懂代码的"代码审计"
    安装scrcpy-client模块av模块异常,环境问题解决方案
    leetcode hot100【LeetCode 279. 完全平方数】java实现
    OpenWrt下安装Mosquitto
    AnatoMask论文汇总
    【AI日记】24.11.01 LangChain、openai api和github copilot
  • 热门文章
  • 十款代码表白小特效 一个比一个浪漫 赶紧收藏起来吧!!!
    奉劝各位学弟学妹们,该打造你的技术影响力了!
    五年了,我在 CSDN 的两个一百万。
    Java俄罗斯方块,老程序员花了一个周末,连接中学年代!
    面试官都震惊,你这网络基础可以啊!
    你真的会用百度吗?我不信 — 那些不为人知的搜索引擎语法
    心情不好的时候,用 Python 画棵樱花树送给自己吧
    通宵一晚做出来的一款类似CS的第一人称射击游戏Demo!原来做游戏也不是很难,连憨憨学妹都学会了!
    13 万字 C 语言从入门到精通保姆级教程2021 年版
    10行代码集2000张美女图,Python爬虫120例,再上征途
Copyright © 2022 侵权请联系2656653265@qq.com    京ICP备2022015340号-1
正则表达式工具 cron表达式工具 密码生成工具

京公网安备 11010502049817号