为什么程序需要栈帧或影子栈?

why procedure need stack frame or shadow stack?

(抱歉我的英语不好,因为我来自韩国)

我试过这段代码

lea rcx, QWORD PTR [message]
call [print_message] ;it covered return address to bad address

xor rcx, rcx
ret

然后崩溃了...
在那之后,我尝试了另一种方式

sub rsp, 8   ;shadow stack

lea rcx, QWORD PTR [message]
call [print_message]

add rsp, 8
ret

; stack frame
push rbp
mov rbp, rsp


lea rcx, QWORD PTR [message]
call [print_message]

mov rsp, rbp
pop rbp
ret

这两个代码是有效的,但问题是...,为什么程序需要这些东西?
这让我很好奇

真正的代码问题来了来自

extern __imp_MessageBoxA : QWORD

.data

message db "1234", 0

.code

entry_point proc

sub rsp, 8

xor ecx, ecx
lea rdx, QWORD PTR [message]
lea r8, QWORD PTR [message]
mov r9, 0
call [__imp_MessageBoxA] ;stdcall

add rsp, 8
ret

entry_point endp
end

不幸的是,我对 64 位代码没有太多经验,所以我不知道确切的细节:

实际上,您不需要影子堆栈或堆栈框架。但是一些 64 位函数需要 rsp 是 16 字节对齐的。

也就是说调用函数时rsp的值必须是16的倍数

如果您的函数如下所示:

myFunction:
    lea rcx, QWORD PTR [message]
    call [print_message] ;it covered return address to bad address
    ...

...那么rsp是指令call myFunction之前的16的倍数。而call myFunction压栈8个字节,所以rsp不再是16的倍数(但是rsp的值可以写成16*n+8)。

当您执行 call [print_message] 时,rsp 不是 16 的倍数,如果函数 print_message 要求 rsp 为 16 字节对齐,程序会崩溃.

指令 sub rsp, 8push rbp 将从 rsp 中减去 8,因此 rsp 的值再次是 16 的倍数。

背景是某些 CPU 指令,这些指令需要一个地址是 16 的倍数作为参数。示例:

print_message:
    sub rsp, 24
      ; The next instruction will crash if rsp is not
      ; a multiple of 16. This is the case if rsp was
      ; not a multiple of 16 before the
      ; "call print_message" instruction
    paddd xmm0, [rsp]