为何POP寄存器操作会改变IP指针?ROP技术相关疑问
理解POP指令如何改变ROP中的代码流差异
咱们一步一步拆解这两个场景的执行流程,就能搞清楚为什么pop ax会改变代码走向了:
正常call + ret的执行逻辑
当你在main里执行call func时,CPU会做两件事:
- 把当前EIP寄存器里的下一条指令地址(也就是
call执行完后本该回到的位置)压入栈中; - 把EIP的值改成
func的地址,跳转到func执行。
进入func后只执行ret指令,ret的作用很直接:把栈顶的数值弹出到EIP里。这时候栈顶正好是刚才call压入的返回地址,所以EIP会被设置回原地址,程序就正常回到main继续执行了。
带pop ax的ROP场景执行逻辑
再看你调试的这个ROP示例:
- 先执行
push 0x44:把0x44压入栈,此时栈指针ESP指向这个0x44; - 然后执行
call func:和之前一样,CPU把当前的返回地址压入栈,ESP会向下移动(栈是向下增长的),现在栈顶是这个返回地址,下面紧跟着刚才push的0x44; - 进入
func后先执行pop ax:这个指令会把当前栈顶的返回地址弹出并赋值给AX寄存器,同时ESP向上移动(相当于把栈顶的元素“移除”),此时栈顶就变成了之前push的0x44; - 接下来执行
ret:ret读取当前栈顶的0x44,把它赋值给EIP——这就导致CPU直接跳转到0x44地址执行,完全偏离了原本的返回流程,这就是ROP技术利用的核心逻辑:通过调整栈的内容,让ret指令跳转到我们指定的地址。
两者的核心差异点
- 正常
ret场景:call压入的返回地址始终在栈顶,ret直接复用这个地址回到原流程; - 带
pop ax的场景:pop ax主动“消耗”了call压入的返回地址(把它转移到AX里),让栈顶变成了我们预先push的0x44,后续ret就会加载这个自定义地址到EIP,从而实现代码流劫持。
另外你问到的pop ax的作用:没错,它确实会把栈顶的数值赋值给AX寄存器,同时调整栈指针ESP的位置,让栈顶指向栈中的下一个元素。
内容的提问来源于stack exchange,提问作者Ahmed Ezzat
相关产品推荐
相关产品推荐

