4.5 x64dbg 探索钩子劫持技术

news2025/1/10 22:09:56

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/733431.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

oracle启动/关闭/查看监听+启动/关闭/查看数据库实例命令

启动oracle第一步启动监听,第二步启动数据库实例 (1)输入su oracle进入oracle用户状态 (2)这里的密码是你的root密码 1 启动/关闭/查看监听命令 (1)启动监听—— lsnrctl start &am…

C#学习之路-封装

封装 被定义为"把一个或多个项目封闭在一个物理的或者逻辑的包中"。在面向对象程序设计方法论中,封装是为了防止对实现细节的访问。 抽象和封装是面向对象程序设计的相关特性。抽象允许相关信息可视化,封装则使开发者实现所需级别的抽象。 C…

编码和调制

编码与调制 消息是以二进制的形式存放在数据当中的,这种数据的表现形式是信号,而信源发出的原始信号就叫做基带信号,基带信号又可以分为数字基带信号和模拟基带信号。 信号需要在信道中进行传输,信道分为模拟信道和数字信…

libdrm编译调试

本文主要介绍libdrm的代码下载、编译和调试的工作。新版本的libdrm不再采用configure && make的方式编译,而是改用meson && ninja编译方式,近些年很多多媒体的开源软件包的构建系统有向后者靠拢的趋势,典型的比如gstream及其…

16、SQL注入之查询方式及报错盲注

目录 前言SQL注入报错盲注补充: Access暴力猜解不出的问题? 前言 当进行SQL注入时,有很多注入会出现无回显的情况,其中不回显的原因可能是SQL语句查询方式的问题导致,这个时候我们需要用到相关的报错或盲注进行后续操作,同时作为…

进程间通信的介绍

目录 进程间通信的目的 进程间通信发展 进程间通信分类 进程间通信的分析 进程间通信的目的 数据传输:一个进程需要将它的数据发送给另一个进程资源共享:多个进程之间共享同样的资源。通知事件:一个进程需要向另一个或一组进程发送消息&a…

PWM+RC 滤波的DAC 输出的数学理论

PWM示意图 PWM 本质上其实就是一种周期一定,占空比可调的方波。典型PWM 波形如下 图所示: PWM分段函数 图中的PWM 波形可以用如下分段函数表示: 函数中:T 是单片机中计数脉冲的基本周期,也就是STM32F4 定时器的计数频率…

Couldn‘t find a tree builder with the features you requested: lxml

这是一个常见于Python爬虫代码的报错。 报错原因:BeautifulSoup的解析方法之一,xml,需要安装好lxml库才行 解决办法:安装 lxml 库即可。 pip install lxml 安装好之后,BeautifulSoup就能正常解析了。 然后&#xff…

RabbitMQ系列(26)--RabbitMQ实现高可用负载均衡

前言:我们以往只能连接一个指定的队列,不能自由地连接其他的队列,当我们连接的那个指定队列宕机了,生产者和消费者都没办法往队列发送消息和消费消息,而且生产者和消费者也不能自动的连接到其他正常运行的队列&#xf…

嵌入式开发--XW09A触摸芯片的使用

XW09A触摸芯片 XW09A是厦门市芯网电子科技有限公司出品的一颗触摸芯片,支持9键多点触摸,I2C接口,带中断引脚。 以下摘抄自芯片手册 极高的灵敏度,可穿透13mm 的玻璃,感应到手指的触摸 超强的抗干扰和ESD 能力,不加任何…

设计模式-迪米特法则

代码世界中类间的耦合关系会直接影响代码可复用性、可读性、可扩展性等。这种耦合关系就如同人之间朋友关系一样,志不同道不合不应相于谋,否则最终只会落得个互相伤害的下场。代码组织时也应如此,应按照一定的原则处理好类之间的关系&#xf…

C++常用库函数 3.数据转换函数

函数名&#xff1a;abs 函数原型&#xff1a;int abs(int n)&#xff1b; 参数&#xff1a;n 整数值。 所需头文件&#xff1a;<cstdlib> 功能&#xff1a;求绝对值。 返回值&#xff1a;返回 n 的绝对值。函数名&#xff1a;atof&#xff0c;atoi&#xff0c;atol …

第42节:cesium 火焰效果(含源码+视频)

结果示例: 完整源码: <template><div class="viewer"><!-- :shouldAnimate="true" 添加动画 --><vc-viewer @ready

linux 命令之find

find 命令 1. 作用 命令用于在指定目录下以及其子母路查找文件和目录。 2. 语法 find [path] [expression] 不指定path的默认为当前路径 2.1 常见的参数 -name pattern&#xff1a;按文件名查找&#xff0c;支持使用通配符 * 和 ?。 -type type&#xff1a;按文件类型查…

NVIC的中断挂起寄存器和EXTI的中断挂起寄存器的区别

&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;仅个人拙见&#xff0c;若有错误&#xff0c;欢迎大家指正&#xff0…

Spring MVC 程序开发

什么是 Spring MVC&#xff1f; 官⽅对于 Spring MVC 的描述是这样的&#xff1a; Spring Web MVC is the original web framework built on the Servlet API and has been included in the Spring Framework from the very beginning. The formal name, “Spring Web MVC,”…

数据特征降维 | 核主元分析KPCA数据降维

文章目录 效果一览文章概述部分源码部分源码参考资料效果一览 文章概述 数据特征降维 | 核主元分析KPCA,主要用于数据降维。 部分源码 部分源码 %% 清空环境变量 warning off % 关闭报警信息 close all % 关闭开启的图窗 clear …

SpringBoot(六)SpringBoot项目部署到腾讯云服务器

这篇文章&#xff0c;可以说是干货满满。关注我的同学应该直到&#xff0c;之前我有几篇SpringBoot的文章&#xff0c;介绍了如何搭建本地服务器&#xff08;没看过的同学可以系统地看下我的SpringBoot专栏&#xff0c;保证你会有很多的收获&#xff09;。但我们那都是在本地玩…

CSDN1周年的创作纪念日【个人总结】

机缘 2022年的7月&#xff0c;第一次了解到这个平台。 得知这个平台可以实现以下功能&#xff1a; 例如&#xff1a; 实战项目中的经验分享日常学习过程中的记录通过文章进行技术交流… 收获 其实出发点是我自己整合笔记&#xff0c;顺带佛系分享给大家看&#xff0c;但是好…

在原有J-IM基础上改造,开发记录方便日后学习,主要区别加入mysql持久化,但是不完善、仅供参考

在原有J-IM基础上改造&#xff0c;开发记录方便日后学习&#xff0c;主要区别加入mysql持久化&#xff0c;但是不完善、仅供参考 原站地址 https://gitee.com/xchao/j-im 改造后的地址 https://gitee.com/lbx_1397372495/jim-chat 本地启动测试 1&#xff0c;修改mysql 数…