1.原理
函数调用 CALL 指令可拆分为两步操作:
1)、将调用者的下一条指令(EIP)的地址压栈
2)、跳转至将要调用的函数地址中(相对偏移或绝对地址)
那么在执行到子函数首地址位置时,返回地址(即调用函数中调用位置下一条指令的地址)就已经存在于堆栈中了,并且是 ESP 指向地址的值。下面通过栈帧的概念,了解编译器在接下来对堆栈进行的操作。
简言之,栈帧就是利用 EBP(栈帧指针,请注意不是 ESP)寄存器访问栈内部局部变量、参数、函数返回地址等的手段。程序运行中,ESP 寄存器的值随时变化,访问栈中函数的局部变量、参数时,若以 ESP 值为基准编写程序会十分困难,并且也很难使 CPU 引用到正确的地址。
所以,调用某函数时,先要把用作基准点(函数起始地址)的 ESP 值保存到 EBP,并维持在函数内部。这样,无论 ESP 的值如何变化,以 EBP 的值为基准能够安全访问到相关函数的局部变量、参数、返回地址,这就是 EBP 寄存器作为栈帧指针的作用。
在函数体代码的任何位置,EBP 寄存器指向的地址始终存储属于它的调用函数的 EBP 的值,根据这个原理可逐级向调用函数、调用函数的调用函数进行遍历,向上回溯。
这样有什么用呢?在将属于调用函数的 EBP 的值压栈之前,ESP 指向的地址存储的是由 CALL 指令压栈的调用函数中调用位置的下一条指令的地址(原 EIP)。那么根据这个逻辑,可以通过上面回溯的各级 EBP 的值,并根据 EBP+sizeof(ULONG_PTR) 获取到函数调用者函数体中的地址(当前函数的返回地址)。有了每级调用的函数体中的地址,那么获取该函数的详细信息及函数符号就变得容易了。
2.对抗思路
分配内存地址作为基地址的内存空间,并将以当前

最低0.47元/天 解锁文章
1233

被折叠的 条评论
为什么被折叠?



