写这个系列的Blog相当于重新从底层认识C

内存中存放了我们运行的program,那么我们的program大体上包括了code(function)和data,当然还有还多library等其它次要的东西。这些数据在运行软件的时候就被CPU加载进Memory里面了。那么内存就被分为了至少两个部分,一个部分是存放数据的,一个地方是存放code的。那么存放参数,局部变量等数据的地方就叫做stack(栈)。每个function被调用的时候就会开辟出自己的一个stack存储自己的data。那么function结束后,会跳回原来的function,那么stack也要回复到调用function前。

那么这个开辟stack,之后再恢复stack的过程就叫做堆栈平衡。

直接用Visual Studio的disassembler调试一个function看到底发生了什么吧:

0.png

call 函数之前有两个push,push两个参数到当前的stack顶部 这个时候esp会往上移两个4/8 Bytes

call 之后的 add esp,8 相当于把stack顶部的两个参数当成垃圾不管了

然后我们进入call看一下:

1.png

00381850 - 00381878 运行完之后 stack的变化如下:

2.png

这里[a]就是[ebp + 8] 就是在args/parameters里面第个参数,[b] 就是[ebp + C] 就是args/parameters里面第个参数

ebp + 4的值是在是call的下一个地址,因为每次call的时候都会往stack里面放入下一条instruction的address用于return。

ret 这个instruction等价于 pop eip, 然后jmp eip。如果Hacker通过修改程序使得Buffer(缓冲区)写满,正好溢出到这个ebp+4的位置。那么当这个function return的时候就会返回到Hacker的函数里面。这个技术叫做缓冲区溢出

当前function结束之后,就要将stack恢复到调用之前的状态。

3.png

0038187E - 00381891 就是为了恢复到调用前stack的状态。

我们还记得call之后会 (add esp, 4*参数的数量) 来使得stack完全恢复到调用前的状态。

我们仔细观察后还可以发现eax就是最终return的值。 在Intel白皮书也指出了

eax 只有两个功能: 1, 作为return result 2. 某些特定opcode的参数,比如stos 就会把eax的值放到edi的地址。

另外rep会通过ecx的值来进行计数重复次数。

Conclusion:

  1. 堆栈平衡原理, ebp栈底, esp栈顶
  2. eax是一个很重要的Register,通常用来存储返回结果。
  3. call之后会push 下一个instruction的地址到stack里面
  4. 缓冲区溢出到ebp+4的位置会导致跳转到Hacker自己写的函数。