4.8 x64dbg 学会扫描应用堆栈
堆栈是计算机中的两种重要数据结构 堆(Heap)和栈(Stack)它们在计算机程序中起着关键作用,在内存中堆区(用于动态内存分配)和栈区(用于存储函数调用、局部变量等临时数据),进程在运行时会使用堆栈进行参数传递,这些参数包括局部变量,临时空间以及函数切换时所需要的栈帧等。
- 栈(Stack)是一种遵循后进先出(LIFO)原则的线性数据结构。它主要用于存储和管理程序中的临时数据,如函数调用和局部变量。栈的主要操作包括压栈(添加元素)和弹栈(移除元素)。
- 堆(Heap)是一种树形数据结构,通常用于实现优先队列。堆中的每个节点都有一个键值(key),并满足特定性质。最常见的堆类型是二叉堆(包括最大堆和最小堆)。堆在计算机程序中的应用包括堆排序算法和内存管理等。
而针对栈地址的分析在漏洞挖掘中尤为重要,栈溢出(Stack Overflow)是一种计算机程序中的运行时错误,通常发生在缓冲区(buffer)中。缓冲区是一段内存空间,用于临时存储数据。当程序试图向栈中写入过多数据时,可能导致栈溢出,从而破坏其他内存区域或导致程序崩溃,严重的则可能会导致黑客控制EIP指针,而执行恶意代码。
栈溢出的原因主要有以下几点:
递归调用过深:当函数递归调用自身的层次过深时,可能导致栈溢出。这是因为每次函数调用都会在栈中分配内存,用于存储函数的局部变量和返回地址。如果递归层数太多,可能导致栈空间不足,从而引发栈溢出。
局部变量占用过多栈空间:如果函数中的局部变量(尤其是数组和结构体)占用过多栈空间,可能导致栈溢出。这种情况下,可以考虑将部分局部变量移到堆内存中,以减小栈空间的压力。
缓冲区溢出:当程序向缓冲区写入的数据超过其分配的空间时,可能发生缓冲区溢出。这种溢出可能导致栈空间中的其他数据被破坏,从而引发栈溢出。
LyScript 插件中提供了针对堆栈的操作函数,对于堆的开辟与释放通常可使用create_alloc()及delete_alloc()在之前的文章中我们已经使用了堆创建函数,本章我们将重点学习针对栈的操作函数,栈操作函数有三种,其中push_stack用于入栈,pop_stack用于出栈,而最有用的还属peek_stack函数,该函数可用于检查指定堆栈位置处的内存参数,利用这个特性就可以实现,对堆栈地址的检测,或对堆栈的扫描等。
读者注意:由于peek_stack命令传入的堆栈下标位置默认从0开始,而输出的结果则一个十进制有符号长整数,一般而言有符号数会出现复数的情形,读者在使用时应更具自己的需求自行转换。
而针对有符号与无符号数的转换也很容易实现,long_to_ulong函数用于将有符号整数转换为无符号整数(long_to_ulong)而与之对应的ulong_to_long函数,则用于将无符号整数转换为有符号整数(ulong_to_long)。这些函数都接受一个整数参数(inter)和一个布尔参数(is_64)。当 is_64 为 False 时,函数处理32位整数;当 is_64 为 True 时,函数处理64位整数。
有符号整数转无符号数(long_to_ulong):通过将输入整数与相应位数的最大值执行按位与操作
(&)来实现转换。对于32位整数,使用(1 << 32) - 1计算最大值;对于64位整数,使用(1 << 64) - 1计算最大值。无符号整数转有符号数(ulong_to_long):通过计算输入整数与相应位数的最高位的差值来实现转换。首先,它使用按位与操作
(&)来计算输入整数与最高位之间的关系。对于32位整数,使用(1 << 31) - 1 和 (1 << 31);对于64位整数,使用(1 << 63) - 1和(1 << 63)。然后,将这两个结果相减以获得有符号整数。
from LyScript32 import MyDebug
# 有符号整数转无符号数
def long_to_ulong(inter,is_64 = False):
if is_64 == False:
return inter & ((1 << 32) - 1)
else:
return inter & ((1 << 64) - 1)
# 无符号整数转有符号数
def ulong_to_long(inter,is_64 = False):
if is_64 == False:
return (inter & ((1 << 31) - 1)) - (inter & (1 << 31))
else:
return (inter & ((1 << 63) - 1)) - (inter & (1 << 63))
if __name__ == "__main__":
dbg = MyDebug()
connect_flag = dbg.connect()
print("连接状态: {}".format(connect_flag))
for index in range(0,10):
# 默认返回有符号数
stack_address = dbg.peek_stack(index)
# 使用转换
print("默认有符号数: {:15} --> 转为无符号数: {:15} --> 转为有符号数: {:15}".
format(stack_address, long_to_ulong(stack_address),ulong_to_long(long_to_ulong(stack_address))))
dbg.close()
如上代码中我们在当前堆栈中向下扫描10条,并通过转换函数以此输出该堆栈信息的有符号与无符号形式,这段代码输出效果如下图所示;

我们继续完善这个功能,通过使用get_disasm_one_code()获取到堆栈的反汇编代码,并以此来进行更多的判断形势,如下代码中只需要增加反汇编一行功能即可。
if __name__ == "__main__":
dbg = MyDebug()
connect_flag = dbg.connect()
print("连接状态: {}".format(connect_flag))
for index in range(0,10):
# 默认返回有符号数
stack_address = dbg.peek_stack(index)
# 反汇编一行
dasm = dbg.get_disasm_one_code(stack_address)
# 根据地址得到模块基址
if stack_address <= 0:
mod_base = 0
else:
mod_base = dbg.get_base_from_address(long_to_ulong(stack_address))
print("stack => [{}] addr = {:10} base = {:10} dasm = {}".format(index, hex(long_to_ulong(stack_address)),hex(mod_base), dasm))
dbg.close()
运行上代码,将自动扫描前十行堆栈中的反汇编指令,并输出如下图所示的功能;

如上图我们可以得到堆栈处的反汇编参数,但如果我们需要检索堆栈特定区域内是否存在返回到模块的地址,该如何实现呢?
该功能的实现其实很简单,首先需要得到程序全局状态下的所有加载模块的基地址,然后得到当前堆栈内存地址内的实际地址,并通过实际内存地址得到模块基址,对比全局表即可拿到当前模块是返回到了哪个模块的。
if __name__ == "__main__":
dbg = MyDebug()
connect_flag = dbg.connect()
print("连接状态: {}".format(connect_flag))
# 得到程序加载过的所有模块信息
module_list = dbg.get_all_module()
# 向下扫描堆栈
for index in range(0,10):
# 默认返回有符号数
stack_address = dbg.peek_stack(index)
# 反汇编一行
dasm = dbg.get_disasm_one_code(stack_address)
# 根据地址得到模块基址
if stack_address <= 0:
mod_base = 0
else:
mod_base = dbg.get_base_from_address(long_to_ulong(stack_address))
# print("stack => [{}] addr = {:10} base = {:10} dasm = {}".format(index, hex(long_to_ulong(stack_address)),hex(mod_base), dasm))
if mod_base > 0:
for x in module_list:
if mod_base == x.get("base"):
print("stack => [{}] addr = {:10} base = {:10} dasm = {:15} return = {:10}"
.format(index,hex(long_to_ulong(stack_address)),hex(mod_base), dasm,
x.get("name")))
dbg.close()
运行如上代码片段,则会输出如下图所示的堆栈返回位置;

4.8 x64dbg 学会扫描应用堆栈的更多相关文章
- LyScript 实现对内存堆栈扫描
LyScript插件中提供了三种基本的堆栈操作方法,其中push_stack用于入栈,pop_stack用于出栈,而最有用的是peek_stack函数,该函数可用于检查指定堆栈位置处的内存参数,利用这 ...
- Windbg查看调用堆栈(k*)
无论是分析程序崩溃原因,还是解决程序hang问题,我们最常查看的就是程序调用堆栈.学会windbg调用堆栈命令,以及理解堆栈中的各个参数的意义就显得至关重要. 上图就是一个典型的Win ...
- NDK-JNI实战教程(二) JNI官方中文资料
声明 设计概述 JNI接口函数和指针 加载和链接本地方法 解析本地方法名 本地方法的参数 引用Java对象 全局和局部引用 实现局部引用 访问Java对象 访问基本类型数组 访问域和方法 报告编程错误 ...
- 3000本IT书籍下载地址
http://www.shouce.ren/post/d/id/112300 黑客攻防实战入门与提高.pdfhttp://www.shouce.ren/post/d/id/112299 黑 ...
- SQL Server-聚焦深入理解死锁以及避免死锁建议(三十三)
前言 终于进入死锁系列,前面也提到过我一直对隔离级别和死锁以及如何避免死锁等问题模棱两可,所以才鼓起了重新学习SQL Server系列的勇气,本节我们来讲讲SQL Server中的死锁,看到许多文章都 ...
- SQL Server-聚焦深入理解死锁以及避免死锁建议(转载)
前言 终于进入死锁系列,前面也提到过我一直对隔离级别和死锁以及如何避免死锁等问题模棱两可,所以才鼓起了重新学习SQL Server系列的勇气,本节我们来讲讲SQL Server中的死锁,看到许多文章都 ...
- JNI 接口规范
1. 简介 Java 本地接口概述 背景 JDK 1.0 本地方法接口 Java 运行时接口 原始本地接口和 Java/COM 接口 目标 Java 本地接口方法 利用 JNI 编程 JDK 1.1. ...
- 别逃避,是时候来给JVM一记重锤了
今天是猿灯塔“365天原创计划”第2天. 今天讲: 为什么写这个主题呢? 之前看到不少同学在讨论, 今天呢火星哥抽出点时间来帮大家整理一下关于JVM的一些知识点 一.JVM是什 ...
- angr原理与实践(一)——原理
1本文系原创,转载请说明出处 关注微信公众号 信安科研人,获取更多的原创安全资讯 编辑 网上已经有很多介绍angr的官方文档的博客,但是怎么去用angr做一次有意义且成就感满满的分析的教程很少 ...
- ZGC 最新一代垃圾回收器[NO]
ZGC(The Z Garbage Collector)是JDK 11中推出的一款低延迟垃圾回收器,ZGC可以说源自于 Azul System 公司开发的C4收集器[基本不用调优]它的设计目标包括: ...
随机推荐
- 用 Python 开发的 PDF 抽取Excel表格 2.0版
前些天向大家介绍了我开发的从PDF抽取表格小工具的使用方法(️点击直达),有同学反馈说有一些问题: 一页PDF有多张表,只能抽取第一个 有些表格线条是透明的,无法抽取 一页一页处理太麻烦,不能一次性抽 ...
- 【JAVA基础】时间处理
#时间处理 ##查询前台报表运单数据集 @ApiOperation(value = "查询前台报表运单数据集") @Permission(permissionPublic = tr ...
- Linux CentOS 7 离线安装.NET环境
下载 下载.NET 例如: aspnetcore-runtime-6.0.15-linux-x64.tar.gz 复制 复制到如下目录: /usr/local/dotnet/aspnetcore-ru ...
- Educational Codeforces Round 102 Personal Editorial(A~C,max Rating 1500)
1473A. Replacing Elements Rating 800 对数组排序,一旦数组中最大的数即a[n-1]是一个小于或等于d的数,直接输出YES即可,否则运用数组中最小的两个数加和替换最大 ...
- 什么是 Serverless 架构?
随着时间的推移,Serverless 架构变得越来越火热,凭借着极致弹性.按量付费.低成本运维等特性,在很多领域发挥着越来越重要的作用:机器学习领域在近些年也非常火热,并在越来越多的行业中得到应用. ...
- git速查
- 如何一键私有化部署 Laf ?
太长不看:Laf 上架了 Sealos 的模板市场,通过 Laf 应用模板即可一键部署! Laf 是一个完全开源的项目,除了使用公有云之外,还有大量的用户选择私有化部署 Laf.然而,私有化部署通常伴 ...
- C#排序算法4:希尔排序
希尔排序是插入排序的一种又称"缩小增量排序"(Diminishing Increment Sort),它是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序:随着增量逐渐减 ...
- Jinfo 查看 jvm 配置及使用 Jstat 查看堆内存使用与垃圾回收
本文为博主远传,未经允许不得转载: 1. Jinfo 查看正在运行的Java应用程序的扩展参数: 包含 JVM 参数与 java 系统参数 命令: jinfo pid 2. 使用 jstat 查看堆 ...
- [转帖]SQL Server 中如何移动tempdb到新的位置
https://www.cnblogs.com/OpenCoder/p/10322904.html 操作步骤:1.检查tempdb的逻辑名字和它的存在位置.可以使用下面语句: SELECT name, ...