写这个系列的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看到底发生了什么吧:
call 函数之前有两个push,push两个参数到当前的stack顶部 这个时候esp会往上移两个4/8 Bytes
call 之后的 add esp,8
相当于把stack顶部的两个参数当成垃圾不管了
然后我们进入call看一下:
00381850 - 00381878
运行完之后 stack的变化如下:
这里[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恢复到调用之前的状态。
0038187E - 00381891
就是为了恢复到调用前stack的状态。
我们还记得call之后会 (add esp, 4*参数的数量) 来使得stack完全恢复到调用前的状态。
我们仔细观察后还可以发现eax就是最终return的值。 在Intel白皮书也指出了
eax
只有两个功能: 1, 作为return result 2. 某些特定opcode的参数,比如stos
就会把eax
的值放到edi
的地址。
另外rep
会通过ecx
的值来进行计数重复次数。
Conclusion:
- 堆栈平衡原理,
ebp
栈底,esp
栈顶 eax
是一个很重要的Register,通常用来存储返回结果。call
之后会push 下一个instruction的地址到stack里面- 缓冲区溢出到
ebp+4
的位置会导致跳转到Hacker自己写的函数。