1 程序的内存布局

一个程序的内存主要分为以下四个节

procmem
  • 数据:数据节(data section) 中的变量在程序初始加载时被放到这里, 称为静态值(static value) , 因为程序运行时它们可能并不发生变化, 还可以称为全局值(global value) , 因为程序的任何部分都可以使用它们。
  • 代码:包含了在执行程序任务时CPU所取得的指令。这些代码决定了程序是做什么的,以及程序中的任务如何协调工作。
  • :为程序执行期间需要的动态内存准备的,用于创建(分配)新的值,以及消除(释放) 不再需要的值。其内容在程序运行期间经常被改变。
  • :用于函数的局部变量和参数,以及控制程序执行流

2 寄存器

下面是最常用的x86寄存器,可以将它们归为以下四类:

  • 通用寄存器, CPU在执行期间使用。一般用于存储数据内存地址
  • 段寄存器, 用于定位内存节
  • 状态标志, 用于做出决定,在x86架构中,它是32位的,每一位是一个标志
    • ZF: 当一个运算的结果等于0时,ZF被置位,否则被清除。
    • CF: 当一个运算的结果相对于目标操作数太大或太小时,CF被置位,否则被清除。
    • SF: 当一个运算的结果为负数,SF被置位;若结果为正数,SF被清除。对算术运算,当运算结 果的最高位值为1时,SF也会被置位。
    • TF:用于调试。当它被置位时,x86处理器每次只执行一条指令。
  • 指令指针, 用于定位要执行的下一条指令
register

3 函数调用的实现流程

下面是函数调用最常见的实现流程:

  • 使用push指令将参数压入栈中。
  • 使用call memory_ location 来调用函数。此时, 当前指令地址(指EIP寄存器中的内容)被 压入栈中。这个地址会在函数结束后, 被用于返回到主代码。当函数开始执行时, EIP的值被设 为memory_location (即函数的起始地址)。
  • 通过函数的序言部分, 分配栈中用于局部变量的空间,EBP(基址指针)也被压入栈中。这样就 达到了为调用函数保存EBP的目的。
  • 函数开始做它的工作。
  • 通过函数的结语部分, 恢复栈。调整EIP 来释放局部变量, 恢复EBP, 以使得调用函数可以准 确地定位它的变量。leave 指令可以用作结语, 因为它的功能是使ESP等于EBP, 然后从栈中 弹出EBP。
  • 函数通过调用ret 指令返回。这个指令会从栈中弹出返回地址给EIP, 因此程序会从原来调用的 地方继续执行。