码农知识堂 - 1000bd
  •   Python
  •   PHP
  •   JS/TS
  •   JAVA
  •   C/C++
  •   C#
  •   GO
  •   Kotlin
  •   Swift
  • 4.5 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-09
    70.4.5 x64dbg 探索钩子劫持技术07-09
    71.4.4 x64dbg 绕过反调试保护机制07-0872.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
    收起

    钩子劫持技术是计算机编程中的一种技术,它们可以让开发者拦截系统函数或应用程序函数的调用,并在函数调用前或调用后执行自定义代码,钩子劫持技术通常用于病毒和恶意软件,也可以让开发者扩展或修改系统函数的功能,从而提高软件的性能和增加新功能。

    4.5.1 探索反汇编写出函数原理

    钩子劫持技术的实现一般需要在对端内存中通过create_alloc()函数准备一块空间,并通过assemble_write_memory()函数,将一段汇编代码转为机器码,并循环写出自定义指令集到堆中,函数write_opcode_from_assemble()就是我们自己实现的,该函数传入一个汇编指令列表,自动转为机器码并写出到堆内,函数的核心代码如下所示。

    def write_opcode_from_assemble(dbg_ptr,asm_list):
        addr_count = 0
        addr = dbg_ptr.create_alloc(1024)
        if addr != 0:
            for index in asm_list:
                asm_size = dbg_ptr.assemble_code_size(index)
                if asm_size != 0:
                    # print("长度: {}".format(asm_size))
                    write = dbg_ptr.assemble_write_memory(addr + addr_count, index)
                    if write == True:
                        addr_count = addr_count + asm_size
                    else:
                        dbg_ptr.delete_alloc(addr)
                        return 0
                else:
                    dbg_ptr.delete_alloc(addr)
                    return 0
        else:
            return 0
        return addr
    

    我们以写出一段MessageBox弹窗代码为例,首先通过get_module_from_function函数获取到位于user32.dll模块内MessageBoxA的函数地址,该函数的栈传参数为五个,其中前四个为push压栈,最后一个则是调用call,为了构建这个指令集需要在asm_list写出所需参数列表及调用函数地址,并通过set_local_protect设置可执行属性,通过set_register将当前EIP设置到写出位置,并执行程序。

    from LyScript32 import MyDebug
    
    def write_opcode_from_assemble(dbg_ptr,asm_list):
                  pass
    
    if __name__ == "__main__":
        dbg = MyDebug()
        dbg.connect()
    
        # 得到messagebox内存地址
        msg_ptr = dbg.get_module_from_function("user32.dll","MessageBoxA")
        call = "call {}".format(str(hex(msg_ptr)))
        print("函数地址: {}".format(call))
    
        # 写出指令集到内存
        asm_list = ['push 0','push 0','push 0','push 0',call]
        write_addr = write_opcode_from_assemble(dbg,asm_list)
        print("写出地址: {}".format(hex(write_addr)))
    
        # 设置执行属性
        dbg.set_local_protect(write_addr,32,1024)
    
        # 将EIP设置到指令集位置
        dbg.set_register("eip",write_addr)
    
        # 执行代码
        dbg.set_debug("Run")
        dbg.close()
    

    运行上述代码片段,则首先会在0x3130000的位置处写出调用MessageBox的指令集。

    当执行set_debug("Run")则会执行如下图所示代码,这些代码则是经过填充的,由于此处仅仅只是一个演示案例,所以不具备任何实战性,读者在该案例中学会指令的替换是如何实现的即可;

    4.5.2 实现Hook改写MsgBox弹窗

    在之前的内容中笔者通过封装write_opcode_from_assemble函数实现了自定义写出内存的功能,本章将继续探索Hook劫持技术的实现原理,如下案例中我们先来实现一个Hook通用模板,在代码中实现中转机制,代码中以MessageBoxA函数为案例实现修改汇编参数传递。

    from LyScript32 import MyDebug
    
    # 传入汇编列表,写出到内存
    def assemble(dbg, address=0, asm_list=[]):
        asm_len_count = 0
        for index in range(0,len(asm_list)):
            # 写出到内存
            dbg.assemble_at(address, asm_list[index])
            # print("地址: {} --> 长度计数器: {} --> 写出: {}".format(hex(address + asm_len_count), asm_len_count,asm_list[index]))
            # 得到asm长度
            asm_len_count = dbg.assemble_code_size(asm_list[index])
            # 地址每次递增
            address = address + asm_len_count
    
    if __name__ == "__main__":
        dbg = MyDebug()
        connect_flag = dbg.connect()
        print("连接状态: {}".format(connect_flag))
    
        # 找到MessageBoxA
        messagebox_address = dbg.get_module_from_function("user32.dll","MessageBoxA")
        print("MessageBoxA内存地址 = {}".format(hex(messagebox_address)))
    
        # 分配空间
        HookMem = dbg.create_alloc(1024)
        print("自定义内存空间: {}".format(hex(HookMem)))
    
        # 写出MessageBoxA内存地址,跳转地址
        asm = [
            f"push {hex(HookMem)}",
            "ret"
        ]
    
        # 将列表中的汇编指令写出到内存
        assemble(dbg,messagebox_address,asm)
    
        dbg.close()
    

    如上代码中,通过找到user32.dll库中的MessageBoxA函数,并返回其内存地址。接着,程序会分配1024字节大小的自定义内存空间,获取刚刚写入的内存地址,并将其写入到MessageBoxA函数的内存地址中,代码运行后读者可看到如下图所示的提示信息;

    提示:解释一下为什么需要增加asm列表中的指令集,此处的指令集作用只有一个那就是跳转,当原始MessageBoxA函数被调用时,则此处通过push;ret的组合跳转到我们自定义的HookMem内存空间中,而此内存空间中后期则需要填充我们自己的弹窗代码片段,所以需要提前通过HookMem = dbg.create_alloc(1024)构建出这段内存区域;

    由于MessageBox弹窗需要使用两个变量这两个变量依次代表标题和内容,所以我们通过create_alloc函数在对端内存中分配两块堆空间,并依次将弹窗字符串通过write_memory_byte写出到内存中,至此弹窗内容也算填充好了,其中txt代表标题,而box则代表内容;

        # 定义两个变量,存放字符串
        MsgBoxAddr = dbg.create_alloc(512)
        MsgTextAddr = dbg.create_alloc(512)
    
        # 填充字符串内容
        # lyshark 标题
        txt = [0x6c, 0x79, 0x73, 0x68, 0x61, 0x72, 0x6b]
        # 内容 lyshark.com
        box = [0x6C, 0x79, 0x73, 0x68, 0x61, 0x72, 0x6B, 0x2E, 0x63, 0x6F, 0x6D]
    
        for txt_count in range(0,len(txt)):
            dbg.write_memory_byte(MsgBoxAddr + txt_count, txt[txt_count])
    
        for box_count in range(0,len(box)):
            dbg.write_memory_byte(MsgTextAddr + box_count, box[box_count])
    
        print("标题地址: {} 内容: {}".format(hex(MsgBoxAddr),hex(MsgTextAddr)))
    

    紧接着,我们需要跳转到MessageBoxA函数所在内存中,并提取出该函数调用时的核心汇编指令集,如下图所示则是弹窗的具体实现流程;

    而对于一个完整的弹窗来说,只需要提取出核心代码即可不必提取所有指令集,但需要注意的是图中的call 0x75B20E20地址需要进行替换,根据系统的不同此处的地址也不会相同,在提取时需要格外注意;

        # 此处是MessageBox替换后的片段
        PatchCode =\
        [
            "mov edi, edi",
            "push ebp",
            "mov ebp,esp",
            "push -1",
            "push 0",
            "push dword ptr ss:[ebp+0x14]",
            f"push {hex(MsgBoxAddr)}",
            f"push {hex(MsgTextAddr)}",
            "push dword ptr ss:[ebp+0x8]",
            "call 0x75B20E20",
            "pop ebp",
            "ret 0x10"
        ]
    
        # 写出到自定义内存
        assemble(dbg, HookMem, PatchCode)
    

    如上则是替换弹窗的代码解释,将这段代码整合在一起,读者则可实现一段替换弹窗功能的代码,如下弹窗中的消息替换成我们自己的版权信息,此处完整代码实现如下所示;

    from LyScript32 import MyDebug
    
    # 传入汇编列表,写出到内存
    def assemble(dbg, address=0, asm_list=[]):
        asm_len_count = 0
        for index in range(0,len(asm_list)):
            # 写出到内存
            dbg.assemble_at(address, asm_list[index])
            # print("地址: {} --> 长度计数器: {} --> 写出: {}".format(hex(address + asm_len_count), asm_len_count,asm_list[index]))
            # 得到asm长度
            asm_len_count = dbg.assemble_code_size(asm_list[index])
            # 地址每次递增
            address = address + asm_len_count
    
    if __name__ == "__main__":
        dbg = MyDebug()
        connect_flag = dbg.connect()
        print("连接状态: {}".format(connect_flag))
    
        # 找到MessageBoxA
        messagebox_address = dbg.get_module_from_function("user32.dll","MessageBoxA")
        print("MessageBoxA内存地址 = {}".format(hex(messagebox_address)))
    
        # 分配空间
        HookMem = dbg.create_alloc(1024)
        print("自定义内存空间: {}".format(hex(HookMem)))
    
        # 写出FindWindowA内存地址,跳转地址
        asm = [
            f"push {hex(HookMem)}",
            "ret"
        ]
    
        # 将列表中的汇编指令写出到内存
        assemble(dbg,messagebox_address,asm)
    
        # 定义两个变量,存放字符串
        MsgBoxAddr = dbg.create_alloc(512)
        MsgTextAddr = dbg.create_alloc(512)
    
        # 填充字符串内容
        # lyshark 标题
        txt = [0x6c, 0x79, 0x73, 0x68, 0x61, 0x72, 0x6b]
        # 内容 lyshark.com
        box = [0x6C, 0x79, 0x73, 0x68, 0x61, 0x72, 0x6B, 0x2E, 0x63, 0x6F, 0x6D]
    
        for txt_count in range(0,len(txt)):
            dbg.write_memory_byte(MsgBoxAddr + txt_count, txt[txt_count])
    
        for box_count in range(0,len(box)):
            dbg.write_memory_byte(MsgTextAddr + box_count, box[box_count])
    
        print("标题地址: {} 内容: {}".format(hex(MsgBoxAddr),hex(MsgTextAddr)))
    
        # 此处是MessageBox替换后的片段
        PatchCode =\
        [
            "mov edi, edi",
            "push ebp",
            "mov ebp,esp",
            "push -1",
            "push 0",
            "push dword ptr ss:[ebp+0x14]",
            f"push {hex(MsgBoxAddr)}",
            f"push {hex(MsgTextAddr)}",
            "push dword ptr ss:[ebp+0x8]",
            "call 0x75B20E20",
            "pop ebp",
            "ret 0x10"
        ]
    
        # 写出到自定义内存
        assemble(dbg, HookMem, PatchCode)
    
        print("地址已被替换,可以运行了.")
        dbg.set_debug("Run")
        dbg.set_debug("Run")
    
        dbg.close()
    

    当如上代码被运行后,则会替换进程内MessageBoxA函数为我们自己的地址,运行输出效果如下图所示;

    读者可通过Ctrl+G并输入MessageBoxA跳转到原函数弹窗位置,此时输出的则是一个跳转地址0x6C0000该地址则代表我们自己的自定义内存区域,如下图所示;

    继续跟进这内存区域,读者可看到我们自己构建的MessageBoxA弹窗的核心代码片段,当这段代码被执行结束后则通过ret会返回到程序领空,如下图所示;

    至此,当用户再次打开弹窗按钮时,则不会提示原始内容,而是提示自定义弹窗,如下图所示;

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

  • 相关阅读:
    时间复杂度计算超全整理!!(数据结构和算法的第一步
    【详解JavaScript轮播图一】
    黑客技术(自学)——网络安全
    使用 Elasticsearch 作为向量数据库:深入研究 dense_vector 和 script_score
    231 基于matlab的北斗信号数据解析
    python的几种数据类型的花样玩法(三)
    常用的Git和Linux命令
    中小企业是否需要微软活动目录联合服务(ADFS)?
    【SwiftUI模块】0056、SwiftUI创建时尚的电影预订应用程序UI
    QT基础教程(QDebug和QString)
  • 原文地址:https://www.cnblogs.com/LyShark/p/17538321.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号