函数过程调用栈分析

首先寄存器使用惯例:
eip :指令地址寄存器,保存程序计数器的值,当前执行的指令的下一条指令的地址值,16位中为ip,32位为eip。eip不可以直接赋值,一般都是cpu自动加1来更新,指令call和ret以及jmp可以改变eip的值。
另外汇编代码格式有ATT和intel格式,gcc和objdump的默认格式就是ATT。几个小区别,1首先是指令ATT汇编指令后面有一个l,比如intel格式为mov,ATT格式为movl
2寄存器,ATT格式有%,比如intel格式为ebp,ATT格式为%ebp
3还有一个最主要的区别就是操作指令的,操作数的顺序是相反的,源操作数和目的操作数的顺序相反。
关于栈中的寄存器:
esp :栈指针,又叫栈顶寄存器,总是指向栈顶元素,已经压入栈的最顶上的那个元素,而不是待压入的。栈指针可以移动,通过上下移动实现栈的开辟和释放。栈是向小地址方向生长的。esp寄存器中保存的是当前栈的栈顶元素的地址。
ebp :帧指针,又叫栈基址寄存器,总是指向当前栈的栈底元素。保存的是当前栈的栈底元素的地址。帧指针不可以移动,用来作为当前栈的基址,通过对基址ebp的偏移来寻址访问栈中的其他元素的值。比如:0x8(%ebp)ebp所指向的地址值在加上0x8的地址。
ebp永远都是针对当前栈的,所以当一个函数调用了另外一个函数的时候,就需要先将调用函数的栈的ebp给入栈保存,这样避免了与被调用函数的栈ebp冲突,然后在被调用函数的栈结束调用释放的时候,恢复现场的时候,在将调用函数的ebp弹出来,这样又可以回到调用函数的栈了。
栈通过栈指针栈顶esp和帧指针栈底ebp来固定栈框。
eax :保存函数的返回值的惯用寄存器
% :直接寻址寄存器
( ) :内存间接寻址
$ :立即数
例如:movl $8, %eax #把立即数8存到寄存器eax中
movl $8, (%esp) #把立即数8存到内存esp所指的内存地址中。
这里主要来通过函数调用来彻底弄清楚栈的过程,这里直接利用bufbomb中的一段简单的c代码,通过反汇编来分析一下其汇编代码。这个c代码很简单,就是test函数中调用了getbuf函数。



其反汇编代码为:



这里的汇编代码比较长我们来截取一下只看关于栈和函数调用的部分:





来看一下不管是test还是getbuf都有的几句汇编语言:


函数的实现过程都是通过栈过程,一开始我们已经讲过了栈有两个指针用来固定栈框的,ebp栈底指针和而esp栈顶指针。
push %ebp #保存旧的ebp的值,也就是保存当前函数的调用者的栈的栈基址。(因为每一个栈都有一个ebp是不可以移动的,但是名字又一样,怎么区分呢,那就是开始时先把原来的ebp保存起来,然后生成自己的,调用结束后,在把原来的恢复,弹出来,自己的释放掉,这样人家原来的ebp又可以继续在自己的栈中作为基址了。不然就覆盖了回不去了)相对于test来说可能就是main之类的调用test的函数的栈的ebp,对于getbuf来说就是test的ebp。将ebp压入栈中,这个时候esp自动-4,指向新压入的ebp元素的位置处。
mov %esp,%ebp #使帧指针ebp指向当前的esp处,也就是初始化生成一个当前栈的基址帧指针ebp,也就是当前栈的栈底表示出来固定住。对于test来说就是test的栈底,对于getbuf来说就是getbuf的栈的栈底。
sub $0x38,%esp #通过栈指针esp向下移动为当前函数开辟自己的栈,esp指向了栈顶,从ebp到esp为当前函数的栈空间。
以上三条指令就是所有栈都有的栈开辟汇编指令。
接下来来看栈释放的汇编指令:



这里是两个函数中不同的栈释放恢复指令,有点不一样但是原理是一样的。
add $0x24,%esp #释放当前栈开辟的空间,在test函数开始的时候通过栈指针esp减0x24移动来开辟了当前test函数自己的栈空间。现在将esp加上0x24也就是使esp从新移动到了栈底ebp处,就将原来开辟的栈空间释放掉了。另外一条常用的汇编指令是:
mov %ebp,%esp #将ebp的值给esp,也就是把esp指向当前的栈底
pop %ebx #把之前保存的寄存器ebx恢复
pop %ebp #弹出旧的ebp,也就是把调用test函数的函数的栈的ebp弹出恢复。
ret # 弹出返回地址。这一条指令相当于
pop %eip 将指令寄存器恢复,也就是让调用test函数的函数的栈知道接下来应该继续执行的哪一条指令。
leave # 是将当前栈的空间释放掉,弹出旧的ebp,相当于下面两条汇编指令:
mov %ebp,%esp
pop %ebp
由此可见栈的释放就是三个过程:
释放当前栈的空间
弹出旧的ebp
弹出返回地址

过程与esp的移动结合:(有很多人不明白,我这里已经用自己的话写的非常白话了,我觉得最后的理解方式是通过gdb调试一下,那里不明白就调试出来里面到底是什么就会自己豁然开朗)

栈释放主要包括:1将当前开放的栈空间释放掉,通过esp的向上移动,移动到自己的栈底ebp处(保存的旧的调用者的ebp的位置处),2恢复原来的现场,主要包括两部分,一部分是将原来的栈基址帧指针ebp复原,也就是在自己栈中保存的ebp弹出来,这个时候esp自动加4,变到原来保存旧ebp处位置上上面一个位置(一般为调用者的栈中保存的返回地址的地方,返回地址是调用当前函数时,结束后回来继续应该执行的下一条指令的eip的地址。比如call指令的下一条指令的eip的地址


比如说上面test中call getbuf这条eip的下一条eip的地址为0x8048e50
在执行call指令的时候相当于:
push eip(0x8048e50) (系统自动将返回地址压入栈,输入调用者的一部分,比如这里在test的栈中,而getbuf的栈是从getbuf压入test的ebp开始,也就是getbuf的栈底元素是test的ebp)
jmp getbuf(0x8049262)
),这个时候保存的旧的ebp已经弹出,之前为当前栈生成的栈基址帧指针ebp也就没有了已经。也就是被调用函数开辟的栈已经完全复原了,像什么没有发生一样。第二个部分也是最后一个部分,就是调用者需要知道我应该继续执行那一条指令(不然回来了找不到原来的指令执行到哪里了),也就是把返回地址eip的地址弹出来,esp自动加4指向原来保存返回地址的位置上面一个位置。这样调用函数又继续正常执行了。

图解函数调用过程:
1首先是test函数的栈结构,其中黄色是test函数的栈,绿色是调用test函数的函数的栈,比如main函数之类的。


2 test执行到call指令:
call 8049262 <getbuf>
首先 系统自动压入返回地址 push eip 这里call的下一条eip的地址是0x8048e50
然后 Jmp到jmp getbuf(0x8049262)
随着返回地址的入栈,esp自动下移,esp-4:这个时候仍是黄色的,因为我们在前面已经分析过,返回地址是属于调用者的栈结构的。


3跳到getbuf的函数的入口地址以后开始getbuf的栈,蓝色的代表getbuf的栈
push %ebp #保存旧的ebp的值,也就是保存当前函数的调用者的栈的栈基址。对于getbuf来说就是test的ebp。将ebp压入栈中,这个时候esp自动-4,指向新压入的ebp元素的位置处。
mov %esp,%ebp #使帧指针ebp指向当前的esp处,也就是初始化生成一个当前栈getbuf栈的基址帧指针ebp,也就是当前栈的栈底表示出来固定住。对于getbuf来说就是getbuf的栈的栈底。


4 getbuf继续栈开辟
sub $0x38,%esp 通过esp移动开辟一个getbuf的栈空间,esp此时指向getb这个栈的栈顶,此时getbuf的栈框已经固定住。


接下来再来看函数调用完以后返回到test函数,现场恢复:
1leave的第一步:
mov %ebp,%esp #将ebp的值给esp,也就是把esp指向当前的栈底,把开辟的蓝色空间收回


2 leave的第二步:
pop %ebp #弹出旧的ebp,也就是把调用test函数的函数的栈的ebp弹出恢复。此时蓝色框已经完全没有了,ebp也没有了,为了getbuf开的空间也已经完全释放了。


3 ret 弹出返回地址以后:


这里以上仅是简单的函数调用,调用的函数不需要传入参数,还有调用的函数需要传入参数的时候等,在bufbomb中我们会具体遇到。再具体分析。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容