热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

逆向工程栈

为什么栈会逆增长多数的栈是逆增长的,它会从高地址向低地址增长。历史原因。当计算机尚未小型化的时候,它还有数个房间那么大。在那个时候,内

为什么栈会逆增长

多数的栈是逆增长的,它会从高地址向低地址增长。

历史原因。当计算机尚未小型化的时候,它还有数个房间那么大。在那个时候,内存就分为两个部分,即"堆/heap"和栈/stack。当然,在程序执行过程中,堆和栈到底会增长到什么地址并不好说,所以人们干脆把它们分开:

 

栈的用途

1)保存函数结束时的返回地址

x86

当程序使用call指令调用其他函数时,call指令结束后的返回地址将被保存在栈里;在call所调用的函数结束之后,程序将执行无条件跳转指令,跳转到这个返回地址。

CALL指令等价于PUSH返回地址和JMP函数地址的指令对。

被调用函数里的RET指令,会从栈中读取返回地址,然后跳转到这个地址,就相当于POP返回地址+JMP返回地址指令。

栈是有隐姓埋名的,溢出它很容易。直接使用无限递归,栈就会满

ARM

如果一个函数不调用其他函数,它就像树上的枝杈末端的叶子那样。这种函数就叫作叶函数leaf function。

叶函数的特点是,它不必保存LR寄存器的值。如果叶函数的代码短到用不了几个寄存器,那么它也可能根本不会使用数据栈。所以,调用叶函数的时候确实可能不会涉及栈操作。

 

2)参数传递

在x86平台的程序中,最常用的参数传递约定是cdecl。

push arg3

push arg2

push arg1

call f

add esp,12

被调用方法函数通过栈指针获取其所需的参数。

ESP

返回地址

ESP+4

arg1,它在IDA里记为arg_0

ESP+8

 

arg2,它在IDA里记为arg_4

ESP+0xC

arg3,它在IDA里记为arg_8

 

3)存储局部变量

通过向栈底调整栈指针的方法,函数即可在数据栈里分配也一片可用于存储局部变量的内存空间。可见,无论函数声明了多少个局部变量,都不影响它分配栈空间的速度。

4)SEH结构化异常处理

5)缓冲区溢出保护

6)alloca()函数

直接使用栈来分配内存,除些之外,它与malloc()比偶没有显著的区别。

函数尾声的代码会还原ESP的值,把数据还原为函数启动之前的状态,直接抛弃由alloca()函数分配的内存。所以程序不需要特地使用free()函数来释放由这个函数申请的内存。

 

栈的噪音

#includevoid f1(){int a=1,b=2,c=3;};void f2(){int a,b,c;printf("%d, %d, %d\n",a,b,c);}int main(){f1();f2();}MSVC$SG2752 '%d, %d, %d',0aH,00H_c$=-12 ;size=4_b$=-8 ;size=4_a$=-4 ;size=4_f1 PROCpush ebpmov ebp,espsub esp,12mov DWORD PTR _a$[ebp],1mov DWORD PTR _b$[ebp],2mov DWORD PTR _c$[ebp],3mov esp,ebppop ebpret 0_f1 ENDP_c$=-12 ;size=4_b$=-8 ;size=4_a$=-4 ;size=4_f2 PROCpush ebpmov mov ebp,espsub esp,12mov eax, DWORD PTR _c$[ebp]push eaxmov ecx, DWORD PTR _b$[ebp]push ecxmov edx, DWORD PTR _a$[ebp]push edxpush OFFSET $SG2752; ‘%d, %d, %d'call DWORD PTR __imp_printfadd esp,16mov esp,ebppop ebpret 0_f2 ENDPmain PROCpush ebpmov ebp,espcall _f1call _f2xor eax,eaxpop ebpret 0_main ENDP

在运行第二个函数时,栈中的所有值(即内存中的单元)受前一个函数的影响,而获得了前一个函数的变量的值。来格地说,这些地址的值不是随机值,而是可预测的伪随机值。


推荐阅读
  • 本文介绍如何使用线段树解决洛谷 P1531 我讨厌它问题,重点在于单点更新和区间查询最大值。 ... [详细]
  • 单片微机原理P3:80C51外部拓展系统
      外部拓展其实是个相对来说很好玩的章节,可以真正开始用单片机写程序了,比较重要的是外部存储器拓展,81C55拓展,矩阵键盘,动态显示,DAC和ADC。0.IO接口电路概念与存 ... [详细]
  • poj 3352 Road Construction ... [详细]
  • 本文提出了一种基于栈结构的高效四则运算表达式求值方法。该方法能够处理包含加、减、乘、除运算符以及十进制整数和小括号的算术表达式。通过定义和实现栈的基本操作,如入栈、出栈和判空等,算法能够准确地解析并计算输入的表达式,最终输出其计算结果。此方法不仅提高了计算效率,还增强了对复杂表达式的处理能力。 ... [详细]
  • 深入解析C语言中结构体的内存对齐机制及其优化方法
    为了提高CPU访问效率,C语言中的结构体成员在内存中遵循特定的对齐规则。本文详细解析了这些对齐机制,并探讨了如何通过合理的布局和编译器选项来优化结构体的内存使用,从而提升程序性能。 ... [详细]
  • 题目解析给定 n 个人和 n 种书籍,每个人都有一个包含自己喜好的书籍列表。目标是计算出满足以下条件的分配方案数量:1. 每个人都必须获得他们喜欢的书籍;2. 每本书只能分配给一个人。通过使用深度优先搜索算法,可以系统地探索所有可能的分配组合,确保每个分配方案都符合上述条件。该方法能够有效地处理这类组合优化问题,找到所有可行的解。 ... [详细]
  • 本文详细介绍了Java反射机制的基本概念、获取Class对象的方法、反射的主要功能及其在实际开发中的应用。通过具体示例,帮助读者更好地理解和使用Java反射。 ... [详细]
  • Ihavetwomethodsofgeneratingmdistinctrandomnumbersintherange[0..n-1]我有两种方法在范围[0.n-1]中生 ... [详细]
  • 零拷贝技术是提高I/O性能的重要手段,常用于Java NIO、Netty、Kafka等框架中。本文将详细解析零拷贝技术的原理及其应用。 ... [详细]
  • 最详尽的4K技术科普
    什么是4K?4K是一个分辨率的范畴,即40962160的像素分辨率,一般用于专业设备居多,目前家庭用的设备,如 ... [详细]
  • Java高并发与多线程(二):线程的实现方式详解
    本文将深入探讨Java中线程的三种主要实现方式,包括继承Thread类、实现Runnable接口和实现Callable接口,并分析它们之间的异同及其应用场景。 ... [详细]
  • 本文介绍了一种在ANSI C中动态分配二维数组的方法。通过创建指针数组并为每个指针分配连续空间,可以灵活地管理内存。文章还讨论了一些常见的错误和注意事项。 ... [详细]
  • 本文是Java并发编程系列的开篇之作,将详细解析Java 1.5及以上版本中提供的并发工具。文章假设读者已经具备同步和易失性关键字的基本知识,重点介绍信号量机制的内部工作原理及其在实际开发中的应用。 ... [详细]
  • 作文记录:合并区间的技巧与应用
    本文详细记录了合并区间问题的解题技巧与应用场景。首先介绍了问题背景和题目描述,接着从排序最大值的角度探讨了解决思路,并提供了具体的程序代码及运行结果。此外,还探讨了其他可能的解决方案。最后,对整个解题过程进行了总结,为读者提供了全面的理解和参考。 ... [详细]
  • 本文深入探讨了MDK链接脚本的应用与优化技巧。首先,文章介绍了链接脚本的基本概念及其在嵌入式系统开发中的重要性。接着,通过具体实例详细分析了链接脚本的结构和功能,特别是在程序在FLASH中运行时,如何优化链接脚本以提高系统性能。此外,文章还讨论了无需将程序加载到SRAM中的技术细节,为开发者提供了实用的参考和指导。 ... [详细]
author-avatar
乐果Meng_501
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有