汇编1 ----C语言函数1

构造以下C程序并在合适位置插入breakpoints

在Visual Studio 2015 CTP6对其反汇编。

下面来分析

z = add(1, 2);

009C170E 6A 02 push 2

????int z;

????z = add(1, 2);

009C1710 6A 01 push 1

009C1712 E8 8D FA FF FF call 009C11A4

009C1717 83 C4 08 add esp,8

009C171A 89 45 F8 mov dword ptr [ebp-8],eax

第一二行将参数1,2压栈,由此可证明C语言函数参数入栈顺序是从右向左。单步执行push 1 和push 2,观察寄存器的值,可知每将一个参数压入栈,ESP值都要减4。执行push 1后查看ESP所指内存的值。

可以看到刚刚压入的2和1,ESP是用来存储栈顶指针的寄存器。

接下来分析下一行 E8 8D FA FF FF

E8为call指令机器码,由小端机存储可知e8的值为ff ff fa 8d。

call目标地址=call指令当前地址+5字节(call指令占用空间)- e8后指令的补码。即009C11A4

call指令执行完毕后,跳转到函数的主体部分。记录返回地址009C1717。那么函数如何返回?单步执行call指令后发现ESP的值由00ACF744变成00ACF664,由于ESP变化较大,笔者猜测函数有可能使用了私有栈。而且00ACF664的内容与返回地址无关。我们直接将call之前ESP的地址减4并查看其内容

恰好为函数的返回地址,即call指令执行后,栈顶指针改变,返回地址被压入函数执行前的栈。

Call指令作用相当于 1.压栈返回地址 2.跳转到目标指令。

Intel官方手册上的说明:

Prior to branching to the first instruction of the called procedure, the CALL instruction pushes the address in the EIP

register onto the current stack. This address is then called the return-instruction pointer and it points to the

instruction where execution of the calling procedure should resume following a return from the called procedure.

Upon returning from a called procedure, the RET instruction pops the return-instruction pointer from the stack

back into the EIP register. Execution of the calling procedure then resumes.

以及CALL和RET指令的准备工作

Near call

When executing a near call, the processor does the following (see Figure 6-2):

1. Pushes the current value of the EIP register on the stack.

2. Loads the offset of the called procedure in the EIP register.

3. Begins execution of the called procedure.

When executing a near return, the processor performs these actions:

1. Pops the top-of-stack value (the return instruction pointer) into the EIP register.

2. If the RET instruction has an optional n argument, increments the stack pointer by the number of bytes

specified with the n operand to release parameters from the stack.

3. Resumes execution of the calling procedure.

Far call

1. Pushes the current value of the CS register on the stack.

2. Pushes the current value of the EIP register on the stack.

3. Loads the segment selector of the segment that contains the called procedure in the CS register.

4. Loads the offset of the called procedure in the EIP register.

5. Begins execution of the called procedure.

When executing a far return, the processor does the following:

1. Pops the top-of-stack value (the return instruction pointer) into the EIP register.

2. Pops the top-of-stack value (the segment selector for the code segment being returned to) into the CS register.

3. If the RET instruction has an optional n argument, increments the stack pointer by the number of bytes

specified with the n operand to release parameters from the stack.

4. Resumes execution of the calling procedure.

不难看出,far call是带有segment的跳转,用到了CS寄存器。

(未完待续)

时间: 2024-08-01 23:52:13

汇编1 ----C语言函数1的相关文章

从linux0.11中起动部分代码看汇编调用c语言函数

上一篇分析了c语言的函数调用栈情况,知道了c语言的函数调用机制后,我们来看一下,linux0.11中起动部分的代码是如何从汇编跳入c语言函数的.在LINUX 0.11中的head.s文件中会看到如下一段代码(linux0.11的启动分析部分会在另一部分中再分析,由于此文仅涉及c与汇编代码的问题,). after_page_tables: pushl $0 # These are the parameters to main :-) pushl $0 pushl $0 pushl $L6 # re

C语言中递归什么时候可以省略return引发的思考:通过内联汇编解读C语言函数return的本质

事情的经过是这样的,博主在用C写一个简单的业务时使用递归,由于粗心而忘了写return.结果发现返回的结果依然是正确的.经过半小时的反汇编调试,证明了我的猜想,现在在博客里分享.也是对C语言编译原理的一次加深理解. 引子: 首先我想以一道题目引例,比较能体现出问题. 例1: #include <stdio.h> /** 函数功能:用递归实现位运算加法 */ int Add_Recursion(int a,int b) { int carry_num = 0, add_num = 0; if (

C语言中递归什么时候能够省略return引发的思考:通过内联汇编解读C语言函数return的本质

事情的经过是这种,博主在用C写一个简单的业务时使用递归,因为粗心而忘了写return.结果发现返回的结果依旧是正确的.经过半小时的反汇编调试.证明了我的猜想,如今在博客里分享.也是对C语言编译原理的一次加深理解. 引子: 首先我想以一道题目引例,比較能体现出问题. 例1: #include <stdio.h> /** 函数功能:用递归实现位运算加法 */ int Add_Recursion(int a,int b) { int carry_num = 0, add_num = 0; if (b

汇编2 ----C语言函数2

上文讲到call之后,程序发生跳转.之后因为博主设错了一个断点,结果折腾了整整一周,真是欲哭无泪. 这才是正确的断点设置啊TAT.这是call之后的语句. 01281427 83 C4 08 add esp,8 执行call之后ESP的值减少4.在memory窗体中查看ESP的值. 由小端机存储可只栈顶正是函数的返回地址. 012813C0 55 push ebp 012813C1 8B EC mov ebp,esp 函数体开头两条指令是将EBP压栈,将ESP赋给EBP. Intel几个通用寄存

Linux汇编GAS调用C语言函数实例

Blum的书上只讲了C语言调用汇编,没讲汇编调用C语言.我自己尝试了下. 最终试验成功了,在此写出与大家分享.期间历经无数错误,无数异常,我不是醉了,而是跪了...好在最后好了. 程序实现一个换值功能,在main.s里定义a=10,b=20,然后调用C语言函数把a,b换值. 新建两个文件分别为main.s的汇编文件,还有pro.c的C语言函数文件. main.s的代码如下: .section .data a: .int 10 b: .int 20 .section .text .globl ma

[汇编与C语言关系]2. main函数与启动例程

为什么汇编程序的入口是_start,而C程序的入口是main函数呢?以下就来解释这个问题 在<x86汇编程序基础(AT&T语法)>一文中我们汇编和链接的步骤是: $ as hello.s -o hello.o $ ld hello.o -o hello 我们用gcc main.c -o main开编译一个c程序,其实际分为三个步骤:编译.汇编.链接 $ gcc -S main.c 生成汇编代码 $ gcc -c main.s 生成目标文件 $ gcc main.o 生成可执行文件 我们

ARM汇编的一般形式和汇编调用C语言

.text //代码段.global _start //表明程序入口_start: //入口函数 BL main //跳转到c语言中的main,不一定要转跳到main,也可以执行其他的汇编指令 一般工程中,纯汇编复杂,而且效率比C语言并没有提高多少,所以在没有操作系统的工程中,最好的开发方式就是用汇编调用C语言,使用C语言来完成所需要的工作(只有在对效率要求极高的时候才会使用汇编编写代码块),当然裸机开发没有现成的库,就算是简单的printf函数也是没法调用的,其实就是就是汇编的高级语言版本.个

C语言函数参数压栈顺序为何是从右到左?(从左向右的话,碰到printf的会陷入死循环)

上学期学习了汇编语言,并在操作系统实验中使用了汇编+C语言混合编程,中间也了解了一些C语言与汇编语言的对应关系. 由于汇编语言是底层的编程语言,各种函数参数都要直接控制栈进行存取,在混合编程中,要用汇编来调用C函数,当然就要知道参数的压栈情况了. 当知道C函数的参数压栈顺序是从右到左时,我觉得很奇怪,因为大多数情况下,人们的习惯是从左到右的,难不成设计者学咱们中国古代写字从右到左的习惯不成? 当时只是记下了这个规则而已,并没有去探究这其中的缘由,后来在实验中自己用汇编实现了printf和scan

借助动态代码生成技术在基于Webkit引擎的HTML5网页JS内调用易语言函数

作者:庄晓立(Liigo) 日期:2015年3月3日夜 原创链接:http://blog.csdn.net/liigo/article/details/44045177 版权所有,转载请注明出处:http://blog.csdn.net/liigo 前两天我协助解决了一个技术问题,在此稍作记录和总结. 具体来说,就是在使用基于Webkit引擎的封装组件wke的过程中,需要把一个易语言函数注册给JavaScript引擎,让它可以在网页里被调用(就像在网页里调用普通JavaScript函数一样).如