前言

汇编语言是学习逆向的基础,本文通过从汇编的层面分析函数调用来了解压栈、跳转、执行、返回的具体实现流程以及对堆栈的应用。

知识有限,如果有错误或则不清楚的地方还请您指出。

您的鼓励是我写文章的动力。

1. 函数调用的说明

在介绍函数调用的具体流程前,我们先来了解一下几个知识点。

程序是顺序执行的,cpu是怎么进行实现的呢?

程序的执行离不开一个关键的寄存器。

我们首先来了解一下关于程序计数器的概念。

百度百科:

vector swap函数_swap函数_swap函数

由此可见,程序计数器的功能就是存放下一条要执行指令的地址。

在X86汇编中,执行程序计数功能的寄存器叫做EIP,指令指针寄存器。

我们可以写个简单的程序,查看反汇编了解一下。

swap函数_vector swap函数_swap函数

EIP寄存器里面存放的就是即将要执行指令的地址。

在经典的操作系统里,栈总是向下增长的。栈顶由esp寄存器定位。压栈操作使栈顶的地址减小,弹出操作使栈顶地址增大。

函数的调用离不开栈,栈中保存了函数调用当中的所有信息。

为了能够正确的处理函数调用的堆栈平衡swap函数,我们需要更正两个寄存器ebp,esp的位置:

1. 把ebp提升到esp的位置。

2. esp的值是动态变化的,随着申请更多的临时变量,e’s’p会不断减小。

3. 调用函数后栈中的结构图。

在函数调用过程中,参数需要提前压栈,而在函数调用结束后,之前压栈的参数也需要退栈。

这样就有一个问题,退栈的工作是交给调用者完成还是交给被调用函数完成?

这就需要有个约定。

常见调用约定的堆栈平衡方式:

调用约定

堆栈平衡方式

__stdcall

函数自己平衡

__cdecl

调用者负责平衡

__thiscall

调用者负责平衡

__fastcall

调用者负责平衡

__naked

有编写者负责

2. 函数调用的流程

了解了基本的概念之后,回到主题:函数调用

0x210000 call swap;
0x210005 mov ecx,eax;

call指令可以分解成两个指令:

push 0x210005;
jmp swap;

首先push当前指令下一条指令的地址swap函数,目的是调用完函数后可以跳回来;

然后修改eip,跳到函数的地址。

ret指令表示取出当前栈顶值作为返回地址,将eip修改为该地址。

执行完这一步一个基本的函数调用就完成了。

3. 函数调用汇编实验

测试程序

在main函数中调用一个交换两个数的函数。

#include 


void _stdcall swap(int& a,int& b);


//交换两个数
void _stdcall swap(int& a, int& b)
{
   int c = a;
   a = b;
   b = c;
}


void main()
{

   int a = 1;
   int b = 2;
   printf("交换前:a =%d,b=%dn",a,b);
   swap(a,b);
   printf("交换后:a= %d,b=%dn",a,b);

   getchar();

}

程序的运行结果:

swap函数_swap函数_vector swap函数

真正调用函数前先将参数进行压栈。

vector swap函数_swap函数_swap函数

我们通过观察寄存器和内存来查看变化过程:

swap函数_vector swap函数_swap函数

发现参数已经被压入栈中。

F11进入swap函数后,可以发现内存当中凭空出现一段数,它是什么?

———END———
限 时 特 惠:本站每日持续更新海量各大内部创业教程,一年会员只需128元,全站资源免费下载点击查看详情
站 长 微 信:jiumai99

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注