我想写一个小的thunk,它将调用一个底层函数,然后比较调用前后rsp
的值。至关重要的是,此代码不应破坏任何寄存器。
显而易见的实现是简单地在调用之前push rsp
,然后在之后进行比较。问题是push rsp
本身会从堆栈中减去 8,因此您实际上应该将保存的 rsp
值与调用后的值加 8 进行比较。
这里有一种方法可以做到这一点:
foo_thunk:
push rsp ; save the value of rsp
call foo ; call the underlying function
add rsp, 8 ; adjust rsp by 8, popping the saved value
cmp rsp, [rsp - 8]
jne bad_rsp ; bad foo!
ret
问题是这会访问高于rsp
的值[rsp - 8]
- 即,不是在堆栈上,而是在堆栈上方的模糊区域。这应该是您有红色区域的地方,但当您没有红区时则不是。
有哪些替代方案?性能和代码大小很重要。
我从最初的问题中假设您想在出现错误时分支到 bad_rsp 之前将 rsp 调整 8,您可以使用
foo_thunk:
sub rsp, 8
mov [rsp], rsp
call foo
cmp rsp, [rsp]
lea rsp, [rsp+8]
jne bad_rsp
ret
(当然,一旦你确定 rsp 没有被保留,可能就没有必要调整它,但我认为这种方法非常有用,值得一提。
我想一个简单的解决方案就是使用 sub
和 mov
来调整堆栈,而不是push
:
foo_thunk:
sub rsp, 8 ; 16 to maintain alignment
mov [rsp], rsp ; save the value of rsp
call foo ; call the underlying function
cmp rsp, [rsp]
jne bad_rsp ; bad foo!
add rsp, 8 ; pop the stack
ret
当然,如果被调用方foo
弄乱了堆栈指针,它可能根本不会返回到调用 thunk,因此此检查的用处有限。最好在被叫方作为ret
之前的检查。
原始调用者已将任何参数放在堆栈上,您真的搞砸了。将支票放入callee
也可以解决这个问题。
自我模组替代品,仅检查rsp
的低 32b 部分:
foo_thunk:
mov [cs:check_esp_part+2],esp
call foo ; call the underlying function
check_esp_part:
cmp esp,0x12345678
jne bad_rsp ; bad foo!
ret