简体   繁体   English

使用 Masm + LOCALS 在 Windows 上的 x64 程序集中正确设置堆栈指针

[英]Correctly Setting Up Stack Pointer in x64 Assembly on Windows with Masm + LOCALS

I have the follow simple program I'm trying to build targeting x64 on Windows 10 building with MASM:我有以下简单程序,我正在尝试在使用 MASM 构建的 Windows 10 上构建目标 x64:

WSTR MACRO lbl:req,qstr:VARARG
LOCAL arg,unq,qot,q
    lbl LABEL WORD
    FOR arg,<qstr>
        qot SubStr <arg>,1,1
        q = 0
        IFIDNI qot,<!'>;'
            q = 1
        ELSEIFIDNI qot,<!">;"
            q = 1
        ELSE
            DW arg
        ENDIF
        IF q EQ 1
            unq SubStr <arg>,2,@SizeStr(<arg>)-2
        %   FORC c,<unq>
                DW "&c"
            ENDM
        ENDIF
    ENDM
    DW 0
ENDM

L MACRO qstr:VARARG
LOCAL sym,seg
    seg EQU <.code>
    %IFIDNI <@CurSeg>,<_DATA>
        seg EQU <.data>
    ENDIF
    .CONST
        ALIGN 4
        WSTR sym,qstr
    seg
    EXITM <OFFSET sym>
ENDM

extrn   LoadCursorW: PROC
extrn   MessageBoxW: PROC
extrn   ExitProcess: PROC
extrn   GetModuleHandleW: PROC
extrn   RegisterClassExW: PROC
extrn   CreateWindowExW: PROC
extrn   GetLastError: PROC
extrn   DefWindowProcW: PROC

.data
wstr windowClassName,"AsmTestClass",0,0
wstr windowTitle,"AsmTest",0,0

HWND_DESKTOP        textequ <0h>
MB_OK           textequ <0h>

WM_CREATE       textequ <0001h>
WM_DESTROY      textequ <0002h>
WM_SIZE         textequ <0005h>
WM_PAINT        textequ <000fh>

CS_VREDRAW      textequ <0001h>
CS_HREDRAW      textequ <0002h>

WS_OVERLAPPED   textequ <00000000h>
WS_CAPTION      textequ <00c00000h>
WS_SYSMENU      textequ <00080000h>
WS_MINIMIZEBOX  textequ <00020000h>

CW_USEDEFAULT   textequ <80000000h>

IDI_APPLICATION textequ <00007f00h>

WINDOW_WIDTH        DWORD   800
WINDOW_HEIGHT   DWORD   600

WNDCLASSEX STRUCT DWORD
  cbSize            DWORD   ?
  style             DWORD   ?
  lpfnWndProc       QWORD   ?
  cbClsExtra        DWORD   ?
  cbWndExtra        DWORD   ?
  hInstance         QWORD   ?
  hIcon             QWORD   ?
  hCursor           QWORD   ?
  hbrBackground     QWORD   ?
  lpszMenuName      QWORD   ?
  lpszClassName     QWORD   ?
  hIconSm           QWORD   ?
WNDCLASSEX ENDS

.code
main proc

LOCAL wc:WNDCLASSEX
LOCAL hWnd:QWORD
LOCAL hInstance:QWORD
LOCAL hCursor:QWORD
LOCAL ATOM:WORD

    ; hInstance = GetModuleHandle(NULL)
    mov     rcx, 0
    call        GetModuleHandleW
    mov     hInstance, rax

    ; hCursor = LoadCursor(NULL,IDI_APPLICATION)
    mov     edx, IDI_APPLICATION
    xor     ecx, ecx
    call        LoadCursorW
    mov     hCursor, rax
  
    ; Setup Window Class
    mov     wc.cbSize, SIZEOF WNDCLASSEX
    mov     wc.style, CS_VREDRAW or CS_HREDRAW
    lea     rax, OFFSET WndProc
    mov     wc.lpfnWndProc, rax
    mov     wc.cbClsExtra, 0
    mov     wc.cbWndExtra, 0
    lea     rax, hInstance
    mov     wc.hInstance, rax
    mov     wc.hbrBackground, 0
    mov     wc.lpszMenuName, 0
    lea     rax, hCursor
    mov     wc.hCursor, rax
    lea     rax, windowClassName
    mov     wc.lpszClassName, rax
    mov     wc.hIconSm, 0
    lea     rcx, wc
    call        RegisterClassExW
    mov     ATOM, ax

    ; CreateWindowExW
    mov     QWORD PTR [rsp+88], 0               ;   lpParam
    lea     rax, hInstance
    mov     QWORD PTR [rsp+80], rax             ;   hInstance
    mov     QWORD PTR [rsp+72], 0               ;   hMenu
    mov     QWORD PTR [rsp+64], 0               ;   hWndParent
    mov     edx, WINDOW_HEIGHT
    mov     DWORD PTR [rsp+56], edx             ;   nHeight
    mov     edx, WINDOW_WIDTH
    mov     DWORD PTR [rsp+48], edx             ;   nWidth
    mov     DWORD PTR [rsp+40], CW_USEDEFAULT   ;   Y
    mov     DWORD PTR [rsp+32], CW_USEDEFAULT   ;   X
    mov     r9d, WS_OVERLAPPED or WS_CAPTION or WS_SYSMENU or WS_MINIMIZEBOX        ; dwStyle
    lea     r8, windowTitle                     ;   lpWindowName
    lea     rdx, windowClassName                    ;   lpClassName
    xor     ecx,ecx                             ;   dwExStyle
    call        CreateWindowExW

    cmp     rax,  0
    je      WindowFailed
    jmp     WindowSuccess
WindowFailed:
    call        GetLastError
    ; to-do check error
WindowSuccess:
    mov     hWnd, rax

    mov     rcx, HWND_DESKTOP               ; hWnd
    lea     rdx, L("Hello x64 World!",0,0)  ; lpText
    lea     r8, L("Win64 Demo",0,0)         ; lpCaption
    mov     r9d, MB_OK                      ; uType 
    call        MessageBoxW
    mov     ecx, eax                            ; uExitCode
    call        ExitProcess

main endp

WndProc proc
    LOCAL hWnd:QWORD
    LOCAL uMsg:DWORD
    LOCAL wParam:QWORD
    LOCAL lParam:QWORD
    LOCAL result:QWORD

    mov     lParam, r9
    mov     wParam, r8
    mov     uMsg, edx
    mov     hWnd, rcx
    
    ; msg handler
    cmp     uMsg,WM_CREATE
    je      create
    cmp     uMsg,WM_PAINT
    je      paint
    cmp     uMsg,WM_DESTROY
    je      destroy
    cmp     uMsg,WM_SIZE
    je      resize  

    ; default
    call        DefWindowProcW
    mov     result,rax
    jmp     finish

create:
    mov     result, 0
    jmp     finish

paint:
    mov     result, 0
    jmp     finish

destroy:
    mov     result, 0
    jmp     finish

resize:
    mov     result, 0
    jmp     finish

finish: 
    mov     rax, result
    
    ret 

WndProc endp
End

Masm is automatically adding the following code: Masm 会自动添加以下代码:

main proc
push        rbp  
mov         rbp,rsp  
add         rsp,0FFFFFFFFFFFFFF90h  
..
main endp

WndProc proc
push        rbp  
mov         rbp,rsp  
add         rsp,0FFFFFFFFFFFFFFD8h  
...
leave
ret
WndProc endp

However when WndProc is routine us called an access violation is triggered on call to DefWindowProcW然而,当 WndProc 是例行程序时,我们调用 DefWindowProcW 时触发访问冲突

I suspect this is because stack pointer is not correctly setup, but masm seems to be adding code for this.我怀疑这是因为堆栈指针设置不正确,但 masm 似乎正在为此添加代码。 How to do this correctly?如何正确执行此操作?

The code added by masm masm添加的代码

add         rsp,0FFFFFFFFFFFFFFD8h

It equivalent of sub rsp,40.它相当于sub rsp,40。 Which is not divisible by 16, adding sub rsp,8 to my code fixes the crash.它不能被 16 整除,在我的代码中添加 sub rsp,8 可以修复崩溃。

WndProc proc
    LOCAL hWnd:QWORD
    LOCAL uMsg:DWORD
    LOCAL wParam:QWORD
    LOCAL lParam:QWORD
    LOCAL result:QWORD

    sub     rsp, 8

Is the best way to resolve this, or is there a way for masm to auto calculate correct value / or predict what needs to be corrected in advance?是解决此问题的最佳方法,还是 masm 有办法自动计算正确值/或预测需要提前更正的内容?

You could use this to round rsp down to a 16 byte boundary:您可以使用它来将 rsp 向下舍入到 16 字节边界:

        and     rsp,0fffffff0h

I tested using VS2015, and usage of LOCAL causes MASM to auto-generate that code.我使用 VS2015 进行了测试,使用 LOCAL 会导致 MASM 自动生成该代码。 Using VS2015, with a wmain instead of WndProc, upon entry, the last nibble of rsp == 8, apparently anticipating that the entry code will use rbp as a frame pointer:使用 VS2015,使用 wmain 而不是 WndProc,在进入时,rsp == 8 的最后一个半字节,显然预计进入代码将使用 rbp 作为帧指针:

        push    rbp
        mov     rbp,rsp

putting rsp on a 16 byte boundary, or upon entry, rbp may randomly be on either an 8 byte or 16 byte boundary.将 rsp 放在 16 字节边界上,或者在进入时,rbp 可能随机位于 8 字节或 16 字节边界上。 I'm not sure of the API rules.我不确定 API 规则。

If on entry, the last nibble of rsp is always 8, then you could use dummy LOCALS so that all the LOCALS take up 8 + some multiple of 16 bytes.如果在输入时,rsp 的最后一个半字节始终是 8,那么您可以使用虚拟 LOCALS,以便所有 LOCALS 占用 8 + 16 字节的一些倍数。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM