Search code examples
assemblywinapistructx86-64masm

MASM struct in data section vs struct in the stack


I have to call WinApi SHFileOperationW function, but before this I need to fill this struct

typedef struct _SHFILEOPSTRUCTW {
HWND         hwnd;
UINT         wFunc;
PCZZWSTR     pFrom;
PCZZWSTR     pTo;
FILEOP_FLAGS fFlags;
BOOL         fAnyOperationsAborted;
LPVOID       hNameMappings;
PCWSTR       lpszProgressTitle;
} SHFILEOPSTRUCTW, *LPSHFILEOPSTRUCTW;

In MASM I defined this like that:

SHFILEOPSTRUCT struct 
    hwnd                  dq ?
    wFunc                 dd ?
    pFrom                 dq ?
    pTo                   dq ?
    fFlags                dw ?
    fAnyOperationsAborted dd ?
    hNameMappings         dq ?
    lpszProgressTitle     dq ?
SHFILEOPSTRUCT ends

In .data section I init my struct:

fos SHFILEOPSTRUCT <?>

So when I filling my struct with mov operations and load the address in rcx(1 arg of function) it cause error, but if I push struct fields in the stack and pass to function RSP its work just fine. Why so? Need I fill all the fields even if it 0? (does not work ↓)

mov [fos.hwnd], 0                       ; hWnd
mov [fos.wFunc], FO_DELETE              ; Delete 
mov rax, offset filename
mov [fos.pFrom], rax                    ; Filename
mov [fos.fFlags], FOF_ALLOWUNDO or FOF_NOCONFIRMATION or FOF_SILENT                           
lea rcx, fos

vs (it's work ↓)

push 0
push 0
push 0
push FOF_ALLOWUNDO or FOF_NOCONFIRMATION or FOF_SILENT
push 0
pushaddr filename
push FO_DELETE 
push 0
mov rcx, rsp

And then ofc I allocate some space in the stack and call the func:

sub rsp, 28h 
call SHFileOperationW

Solution

  • The C/C++ headers in the Windows SDK assume the platform's default alignment is used.

    this controlled by /Zp (Struct Member Alignment) option in both msvc and ML/ML64 ( The alignment can be 1, 2, 4, 8 or 16)

    look also How align works with data packing

    difference betweem MSVC and ML64: MSVC use /Zp16 as default for x64 and ARM64 but ML64 not.

    so you need or direct set /Zp16 in ML64 command line (the best) or direct set aligment in STRUCT declaration:

    like SHFILEOPSTRUCTW struct 16


    also declare variable In .data section - mean use global variable and bad solution. this must be local variable, so in stack. code can look like

    .code
    
    SHFILEOPSTRUCTW struct 16
        hwnd                  dq ?
        wFunc                 dd ?
        pFrom                 dq ?
        pTo                   dq ?
        fFlags                dw ?
        fAnyOperationsAborted dd ?
        hNameMappings         dq ?
        lpszProgressTitle     dq ?
    SHFILEOPSTRUCTW ends
    
    FO_DELETE           EQU 03h
    
    FOF_NOCONFIRMATION  EQU 10h
    FOF_ALLOWUNDO       EQU 40h
    FOF_SILENT          EQU 04h
    
    EXTERN __imp_SHFileOperationW:QWORD
    
    VarSize EQU ((SIZEOF SHFILEOPSTRUCTW + 15) and not 15)
    fos EQU [rsp + 20h]
    
    
    er_ff proc
    
        sub rsp, 28h + VarSize
        xor eax,eax
        mov fos[SHFILEOPSTRUCTW.hwnd],rax
        mov fos[SHFILEOPSTRUCTW.wFunc],FO_DELETE
        mov fos[SHFILEOPSTRUCTW.pFrom],rcx
        mov fos[SHFILEOPSTRUCTW.pTo],rax
        mov fos[SHFILEOPSTRUCTW.fFlags],FOF_ALLOWUNDO or FOF_NOCONFIRMATION or FOF_SILENT
        lea rcx,fos
        call __imp_SHFileOperationW
        add rsp, 28h + VarSize 
        ret
    er_ff endp
    
    end
    

    so use SHFILEOPSTRUCTW struct 16 ot better ml64 /c /Cp /Zp16 "$(InputFileName)"