毫无意义地尝试在不使用寄存器的情况下比较呼叫中的 rsp



我想写一个小的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 没有被保留,可能就没有必要调整它,但我认为这种方法非常有用,值得一提。

我想一个简单的解决方案就是使用 submov 来调整堆栈,而不是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 

最新更新