函数使用了堆栈的字节超过_在C语言中如何访问堆栈?

cd9ac9a4063bfab4d88646d7c382b8c4.png

堆栈一般是用来保存变量之类的东西(静态变量在内存中,虽然堆栈就是内存的一部分,但为了防止歧义,还是分成两部分来说),一般情况下没必要去故意读取堆栈的值,变量用变量名就可以直接访问,但我曾经想要读取函数返回后代码继续执行的地址,因此想到了来读取堆栈(函数调用时,会向堆栈中压入参数和下一个代码执行的地址,这样就可以在函数返回后继续执行)。

先来测试一下我们能否读取堆栈:

#include<stdio.h>
int main()
{volatile int a=24;/*设置一个我们要读取的变量,volatile 可以告诉gcc不要优化这行代码,仅对变量有效*/volatile int b[2]={1,2};/*建立一个数组,这个数组是关键,这时b作为数组指针,指向第一个元素,即
1在堆栈中的储存位置,因此我们就可以利用b来读取堆栈的任意位置(该程序所拥有的堆栈)*/volatile int c=b[2];printf("%dn",c);//打印出指定位置堆栈的值
return 0;
}

当然,如果不设定编译器的参数,这样的代码可能是不会编译通过的(注意:可能),命令如下:

gcc -Wno-unused -m32 -S -O0 -o test.s test.c

源文件名为test.c,参数说明:

-Wno-unused:不警告未使用的变量(上面的程序不需要,但为了方便自己分析,放在这里)

-m32:编译为32位程序

-S:编译为汇编文件

-O0:优化等级为0

-o:重命名输出文件

现在让我们看看汇编文件是什么样的:

	.file	"test.c".def	___main;	.scl	2;	.type	32;	.endef.section .rdata,"dr"
LC0:.ascii "%d120".text.globl	_main.def	_main;	.scl	2;	.type	32;	.endef
_main:
LFB10:.cfi_startprocpushl	%ebp.cfi_def_cfa_offset 8.cfi_offset 5, -8movl	%esp, %ebp.cfi_def_cfa_register 5pushl	%esipushl	%ebxandl	$-16, %espsubl	$32, %esp.cfi_offset 6, -12.cfi_offset 3, -16call	___mainmovl	$24, 28(%esp)        //将24存入堆栈,位置是28+esp的值movl	$1, %ebx            //1存入ebxmovl	$2, %esi            //2存入esimovl	%ebx, 20(%esp)        //1存入20(%esp)movl	%esi, 24(%esp)        //2存入24(%esp)movl	28(%esp), %eax        //将28(%esp)的值存入eax,这里对应的代码就是c=b[2],即将24存入了eaxmovl	%eax, 16(%esp)        //剩下的就是将参数压入堆栈,然后调用printf,这里不再解释movl	16(%esp), %eaxmovl	%eax, 4(%esp)movl	$LC0, (%esp)call	_printfmovl	$0, %eaxleal	-8(%ebp), %esppopl	%ebx.cfi_restore 3popl	%esi.cfi_restore 6popl	%ebp.cfi_restore 5.cfi_def_cfa 4, 4ret.cfi_endproc
LFE10:.ident	"GCC: (MinGW.org GCC-6.3.0-1) 6.3.0".def	_printf;	.scl	2;	.type	32;	.endef

通过汇编的内容,我们可以看出可以使用一个数组来访问有效堆栈内的全部内容(超出堆栈界限会引发错误)。输出结果如下:

e981b70f5a55245311c9acba94bf94fb.png

当调用一个函数时(使用call指令),压入参数的同时会压下一个代码的地址,使函数返回后可以继续执行。现在来尝试获取这个地址,代码如下:

#include<stdio.h>
void fun()
{volatile int a[1];/*设置一个数组,使用这个数组来访问堆栈*/a[0]=14;printf("a[4]=%dn",a[4]);/*打印出call指令压入的地址,这里很有意思,我之前以为这个地址在
a[2],a[0]=14,a[1]是esp的值(C语言中所有函数的开头都会有push ebp的代码,将ebp的值保存进堆栈,然后
将esp保存进ebp),但实际上发现总会有两个不知名的值占据着a[2],a[3]的位置,具体可以参见汇编代码*/goto *(a[4]);/*使用goto语句可以让程序跳向任何合法地址,goto不仅可以用标号或者行号,还可以是任
何void*型的变量(前提是程序可以访问该地址),goto会被程序翻译为jmp指令,而(*(void(*)
(void))0x100000)();这样的跳转方式将会被翻译为call指令,会使堆栈中多出一个地址,具体要使用哪个需要
参考实际。*/
}
int main()
{
fun();
printf("hello");/*理论上如果上面的goto生效,那么hello将会被执行两次(调用fun函数时,堆栈被压入该
地址,然后使用了goto后,跳转到这里执行一次,打印出一个hello,在下面的return 0;语句中,程序会认为当
前还在fun函数,毕竟堆栈中的地址还没有释放,因此重新返回到这里,再执行一次),否则由于地址错误,程
序将被迫退出,不会在控制台看到hello*/
return 0;
}

下面是实际执行的情况,可见我们确实得到了之前压入的那个地址:

cfd487404fb2e6a1c7f0b6540cdbb897.png

以下是上面的那个程序的汇编程序:

	.file	"test.c".section .rdata,"dr"
LC0:.ascii "a[4]=%d120".text.globl	_fun.def	_fun;	.scl	2;	.type	32;	.endef
_fun:
LFB10:.cfi_startprocpushl	%ebp            //将ebp的值送入堆栈.cfi_def_cfa_offset 8.cfi_offset 5, -8movl	%esp, %ebp.cfi_def_cfa_register 5subl	$40, %esp        //空出40字节的位置用来储存变量movl	$14, -12(%ebp)    //14存入a[0],所以a的值即是ebp偏移12个字节,
//可以推断出a[1]在-8(%ebp),a[2]在-4(%ebp),a[3]在0(%esp),所以a[3]是之前保存的ebp的值
//那么a[4]就是call指令保存的值,这里比较令人好奇为什么a[0]在-12(%ebp)movl	4(%ebp), %eax    movl	%eax, 4(%esp)movl	$LC0, (%esp)call	_printfmovl	4(%ebp), %eaxjmp	*%eax                //goto被翻译为jmp指令,然后跳向了我们指定的地址.cfi_endproc
LFE10:.def	___main;	.scl	2;	.type	32;	.endef.section .rdata,"dr"
LC1:.ascii "hello0".text.globl	_main.def	_main;	.scl	2;	.type	32;	.endef
_main:
LFB11:.cfi_startprocpushl	%ebp.cfi_def_cfa_offset 8.cfi_offset 5, -8movl	%esp, %ebp.cfi_def_cfa_register 5andl	$-16, %espsubl	$16, %espcall	___maincall	_funmovl	$LC1, (%esp)call	_printfmovl	$0, %eaxleave.cfi_restore 5.cfi_def_cfa 4, 4ret.cfi_endproc
LFE11:.ident	"GCC: (MinGW.org GCC-6.3.0-1) 6.3.0".def	_printf;	.scl	2;	.type	32;	.endef

所以这里验证了我们可以通过操作数组来读取堆栈。

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

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

相关文章

怎么解决相位抵消_【录音教程】如何防止话筒拾音的声学相位抵消

设想一下你以单声道的模式为一个吉他歌手做扩声&#xff0c;一支人 声听起来比较古怪&#xff0c;有点“空”&#xff0c;同时有滤波器之类的效果声。为什么会这样&#xff1f;主唱话筒拾取歌手的演唱&#xff0c;同时拾取箱琴的话筒也串入了歌手的声音(见图1)。吉 他话筒离主唱…

win32 ipv6 bind 10014问题

这两天实验了一下&#xff0c;windows xp系统下的 ipv6 udp网络通信。要在windows xp下测试ipv6&#xff0c;首先在命令行窗口下 执行 ipv6 install 和 net start tcpipv6 命令。 在bind本地地址时&#xff0c;发现没法使用 in6addr_any 这通配地址。即下面这种使用方式&a…

string 找出所有数字 index_发现规律,解决整数转罗马数字

嗨&#xff0c;各位&#xff01;我们又准时见面了&#xff0c;即将迎来难得的周末时光&#xff0c;我们今天来一道相对简单的题目逻辑梳理的题目&#xff0c;原定的动态规划的常见题型我们放在周末进行更新。话不多说&#xff0c;我们先看题目&#xff1a; 01 . 题目罗马数字包…

ASP.NET MVC的路由

好久没写博文了&#xff0c;感觉最近好像少了点动力。唉&#xff01;这回就看看这个MVC的路由。 说这个路由机制其实不是MVC里面特有的&#xff0c;ASP.NET里面本身就有的&#xff0c;只不过在WebForm里面一般比较少用&#xff0c;而在MVC里就是把原本的路由扩展了。原本对不知…

win32 ipv6 sendto recvfrom

1. sendto 在windows xp下进行ipv6的udp网络通信时&#xff0c;目标地址应该像如下方式构造 addrinfo hins, *res; memset(&hins, 0, sizeof(hins)); hins.ai_family AF_INET6; hins.ai_socktype SOCK_DGRAM; hins.ai_protocal IPPROTO_UDP; hins.ai_flags AI_NUERICH…

vscode python环境配置_用VScode配置Python开发环境

前言VScode是一个相当优秀的IDE&#xff0c;具备开源、跨平台、模块化、插件丰富、启动时间快、颜值高、可高度定制等等优秀的特质&#xff0c;不愧是微软爸爸的私生子。所以用VScode来编写Python&#xff0c;也是相当的好用的。所以&#xff0c;今天我们就来讲讲&#xff0c;怎…

java情书_Java情书已写好,就差妹子了!

我是Java程序员我用我的方式表达对你的爱&#xff01;我能抽象出整个世界...但是我却不能抽象出你...你肯定是一个单例&#xff0c;因为你是那样的独一无二...所以我的世界并不完整...我可以重载甚至覆盖这个世界里的任何一种方法...但是却不能覆盖对你的思念...也许命中注定了…

JS中的正则表达式小小的技巧---TEST方法的使用

TEST 方法 返回一个 Boolean 值&#xff0c;它指出在被查找的字符串中是否存在模式。 rgexp.test(str) 参数 rgexp 必选项。包含正则表达式模式或可用标志的正则表达式对象。 str 必选项。要在其上测试查找的字符串。 说明 test 方法检查在字符串中是否存在一个模式&#xff0c…

windows下手动配置ipv6地址

在XP,2003等早期版本中&#xff0c;ipv6地址在“网络连接”的属性配置里是无法手工配置的&#xff0c;只能使用netsh配置。配置方法如下&#xff1a;首先&#xff0c;安装IPV6协议&#xff0c;ipv6 install 第二步&#xff0c;查看当前使用的本地连接信息&#xff1a; 如上图&a…

python10的因数_十五道Python小案例,学会这些,Python基础已过关!

十五道Python的小案例&#xff0c;不知道各位都会了多少&#xff01;一、猜数字1到4能组成多少个互不相同且不重复数字的三位数&#xff1f;分别是&#xff1f;分析&#xff1a;可填在百位、十位、个位的数字都是1、2、3、4。组成所有的排列后再去 掉不满足条件的排列。程序源代…

java 幽灵引用_Java 幽灵引用的作用

原标题&#xff1a;Java 幽灵引用的作用来源&#xff1a;imzoer&#xff0c;blog.csdn.net/imzoer/article/details/8044900找实习的时候&#xff0c;面试大摩&#xff0c;就遇到了这个问题&#xff0c;当时真不该跟面试官交流这个内容的。垃圾收集过程中&#xff0c;对象的可触…

unique_ptr使用简介

我们知道auto_ptr通过复制构造或者通过赋值后,原来的auto_ptr对象就报废了.所有权转移到新的对象中去了.而通过shared_ptr可以让多个智能指针对象同时拥有某一块内存的访问权.但假如我们不希望多个内存块被多个智能指针对象共享,同时又不会像auto_ptr那样不知不觉的就让原来的a…

python访问共享文件夹 exist false_python os.path.exists()对于存在的nfs挂载目录文件失败...

我基本上有一个网站的网站服务器,另一个只存储文件.文件服务器通过挂载其中一个目录连接到主文件服务器.该网站运行Django所以我主要处理python.无论如何,我似乎遇到了一些问题,即文件被报告为不存在,即使它们实际存在.基本上当我打电话时filepath /path/to/file/on/nfs/share…

什么是CouchDB?

※本文对CouchDB的解释是来自Apache CouchDB的官网的译文&#xff0c;如果有什么问题&#xff0c;请指正。 1.CouchDB简介 CouchDB是一种利用JSON文件&#xff0c;javascript作为MapReduce查询&#xff0c;以及常规HTTP作为API的一种数据库系统。 2.CouchDB&#xff0c;一个为W…

java assembly 打包_maven 使用assembly 进行打包的方法

1. pom 中添加assembly 插件要使用assembly 进项编译打包&#xff0c; 首先主要在pom 中的build中添加插件信息&#xff0c; 具体如图下所示&#xff1a;${project.artifactId}src/main/javasrc/main/resourcestrue**/*.xml**/*.properties${profile.dir}trueorg.apache.maven.…

rt5350 中断处理分析

1. arch/mips/kernel/genex.S j plat_irq_dispatch 2. arch/mips/rt2880/irq.c plat_irq_dispatch rt2880_irqdispatch do_IRQ do_IRQ定义在 include/asm-mips/irq.h 这个头文件中&#xff0c;asm-mips这个目录链接到asm这里&#xff0c;所以在 arch/mips/rt2880/ir…

python json数据格式数组内元素递增赋值_利用Python实现JSON格式数据的编码与解码操作...

导读JSON (全称&#xff1a;JavaScript Object Notation )是一种轻量级的数据交换格式&#xff0c;所谓的轻量级&#xff0c;是与 XML 数据结构相比而言的&#xff0c;在描述相同的数据下&#xff0c;JSON 所需的字符比XML 的要少&#xff0c;这就意味着相同意义的数据&#xf…

java注解中可使用对象_Java注解(二):实战 - 直接使用对象列表生成报表...

通过对Java注解(一)&#xff1a;介绍&#xff0c;思想及优点学习了解&#xff0c;相信大家对Java注解有一定程度的了解&#xff0c;本篇文章将实战项目中的应用来加深对Java注解的了解。本实例实现根据指定字段的JavaBean,生成对应列的报表。使用Java注解就是方便实现JavaBean与…

ASP.NET WebForm中使用WebApi

添加webapi.dll 可现在添加。 在WebForm使用WebApi需要在全局文件里配置路由。 using System.Web.Routing;protected void Application_Start(object sender, EventArgs e){RegisterRoutes(RouteTable.Routes);}public static void RegisterRoutes(RouteCollection routes){//C…

rt5350 中断初始化

linux 下的中断初始化&#xff0c;实际就是对 irq_desc 这个结构体进行初始化&#xff0c;其中最关键莫过于 irq_flow_handler_t handle_irq 中断处理函数 struct irqaction *action 用户自己设置的处理函数链表&#xff0c;由hand…