第3篇:戏说程序栈-call指令和ret指令

承接上文,抛出的几个问题?

  • 被调用者必须知道从哪里获取参数
  • 被调用者必须知道从哪里获取返回地址
  • 调用者必须知道从哪里获取返回值

回到上面的问题,其实如果你理解一点基本的汇编代码的语法话,解析程序栈底层的操作问题,一切都迎刃而解,但我们没有打算用汇编代码来作为一个引例。考虑并不是所有人都有汇编代码的基础,所以本篇会用一个具体的例子分解函数调用过程中每个步骤。

好,回到前文的例子,我们这里需要对前面的过程调用做一个完整的流程说明。

程序栈的执行流

从前文的一个粗略的例子,我们已经知道调用者调用被调用者会用到call指令,被调用者在执行结束时以ret指令返回,我们这里将进一步说明如何支援call和return指令。以下是一段反编译后的代码段

备注:例子中的代码片段引用紫Washinton University 计算机科学的网上公开课section 5的示例,本文做了一些修改和扩充


目前指令指针(又叫%eip指针)指示的地址是call指令所在行的地址是0x804854e,&esp指针指向栈顶位置保存着一个724的整数,可能是该过程的一个参数,该示例描述的是目前将要执行call指令(尚未执行)的程序状态

当执行call 0x8048b90这条语句,被调用者函数位于内存地址0x8048b90的位置,接下来会发生什么呢?
由于此时已经读取了call 0x8048b90这条语句,但我们还没完成对call 0x8048b90的调用,此时%eip指针已经向前指向call指令语句的下一条指令,即会如图变化所示:eip指针存储的内存地址更新为0x8048553

111.png

接下来发生的事情,是将call 0x8048b90语句的下一条指令的地址,并将该地址值压入栈,此时栈的变化如下图所示:这里伴随着push指令的发生两个变化

  • 返回地址(即紧接着call指令的下一条指令所在行的地址)压入栈。
  • 栈指针的向低地址递减4个字节,即此时%esp指针只想0xfe05.


    111.png

接下来发生的事情,会跳转到8048b90这个地址即被调用者函数的所在行的起始地址。你必须要知道为什么是8048b90这个地址,而不是其他内存地址?生成这个地址的方式如下图,这种称为相对寻址法

2019-10-11 14-14-47屏幕截图.png

你要注意到指令中的常数是063d,当然示例用的小端机器的表示方法,所以在代码片段里显示为 3d06,但我们逻辑上的表示是063d,编译器会将该内存地址常数和eip指针的当前值作加法生成新的内存地址值,在这里即被调用者函数的内存地址。
可能有人会问,为什么不让编译器自行决定任意一个可用的内存地址呢

对不起!我们不是编译器的设计者,作为C/C++程序员,只要理解到编译器使用的寻址原理,并在知道在生成汇编代码时,编译器已经在底层做了这些工作,我们没必要“打烂沙盘,问到底”。

此时,我们真正在意的是,eip指针已经被替换为0x8048b90这个地址,换句话说,call指令告诉编译器可以执行jmp指令跳转到该地址即被调用者函数本体中的第一条指令,

如下图所示(左边的图例):此时的程序状态包含了如下特征

  • 对CPU/寄存器的控制权已经从调用者函数本体转移到被调用者函数的本体
  • 当前eip指针指向的是被调用者函数的本体中的第一条指令的地址,即0x8048b90。

下图被调用者函数的中间指令集不是本文讨论的内容,因此其中间的指令集我用“...”忽略了,当被调用函数到达该本体的结尾之时,即eip指针指向ret指令所在行的地址0x8048591,如下图(右边的图例)所示,此时的程序状态是:

  • %esp指针指向栈中的返回地址,但此时还没执行出栈操作。
111.png

跟接着,就弹出栈顶的返回地址(即pop操作),返回地址出栈是为了取得该地址,并跳转到该地址指向原来调用者函数本体紧接call 指令所在行下一条指令。此时程序的状态变化如下

  • %esp指针会向高地址移动4个字节,即esp递增4,即指向0xfe09
  • 被出栈的返回地址会被传入%eip指针,即0x8048553
  • 控制权将从被调用者函数本体转移到调用者函数。

如下图变化所示


111.png

返回值的处理

从上面的示例中,我们都没有谈及到返回值是如何从被调用者传递给调用这函数的。是为了简化上面的示例分析,按照惯例,被调用者函数的返回值会放在eax寄存器中,eax的选择是相当随意的,可能是%ecx或%edx等,具体根据不同的C/C++编译器的实现而定。

  • 调用者函数必须保证在调用可能返回一个值的被调用者函数之前,保存(eax)寄存器中的信息,因为被调用者函数和调用者函数共用同一个寄存器,被调用函数执行后会覆盖(eax)寄存器中的信息,这是寄存器保存操作中的约定。
  • 被调用者函数在执行ret指令时,会将(计算过)的适合4个字节的任意类型的返回值保存到(通常是%eax)寄存器,也可能是其他寄存器, x86环境中的eax寄存器只有4个字节。
    • 如果要返回大于4个字节的数据类型,最好的方式是返回一个自定义类型的对象的指针,而不是对象本身。
  • 返回时,调用者函数在%eax寄存器(也可能是其他寄存器)中找到返回值。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,753评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,668评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,090评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,010评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,054评论 6 395
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,806评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,484评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,380评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,873评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,021评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,158评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,838评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,499评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,044评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,159评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,449评论 3 374
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,136评论 2 356

推荐阅读更多精彩内容

  • 原文地址:C语言函数调用栈(一)C语言函数调用栈(二) 0 引言 程序的执行过程可看作连续的函数调用。当一个函数执...
    小猪啊呜阅读 4,619评论 1 19
  • Return-Oriented-Programming(ROP FTW) Author: Saif El-Sher...
    RealSys阅读 3,322评论 0 2
  • 1.地址总线,数据总线,控制总线在哪里,它们有什么作用?答:它们都是cpu连接外部组件的线路。地址总线:地址总线A...
    MagicalGuy阅读 1,460评论 0 1
  • 首先寄存器使用惯例:eip :指令地址寄存器,保存程序计数器的值,当前执行的指令的下一条指令的地址值,16位中为i...
    扎Zn了老Fe阅读 1,980评论 0 0
  • 半红的太阳沉在赤山山头,余辉斜阳洒在高粱地上,高梁地旁的泥潭里蛙声阵阵,不时的泛起令人作呕的酸腐的臭气。王占槐斜卧...
    宗思源阅读 495评论 0 3