News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

Unevenly Aligned Stack

Started by Tight_Coder_Ex, May 21, 2011, 08:33:11 PM

Previous topic - Next topic

Tight_Coder_Ex

I just encountered some odd behavior in OLLY, WINDBG & IDA and the problem was all of a sudden the stack was not evenly aligned anymore bits 0 and/or 1 not null.


PaintWnd       proc    private uses ebx

        LOCAL   TxtM : TEXTMETRIC, Ps : PAINTSTRUCT, Rc : RECT,  Top:DWORD

                lea     eax, Ps                         ; Get pointer to local variable
                push    eax                             ;  2 : LPPAINTSTRUCT
                push    dword ptr [esi]                 ;  1 : hwnd
                call    BeginPaint                      ; User32 API
                sub     esp, 8                          ; Setup for EndPaint API in epilogue

        .if     !eax                                    ; Was DC returned in EAX
                int     3                       ; Will develop error routine if necessar

      .else
                mov     ebx, offset ListParams

                lea     eax, Rc
                push    eax                             ;  2 : lpRect
                lodsd
                mov     edi, eax                        ; Save permanent copy
                push    eax                             ;  1 : hWnd
                call    GetClientRect                   ; User32 API

                lea     eax, TxtM
                push    eax                             ;  2 : lptm
                push    Ps.hdc                          ;  1 : hdc
                call    GetTextMetrics                  ; Gdi32 API

THERE IS MORE CODE HERE, BUT NOT RELEVANT TO POST

     .endif                             ; BeginPaint returned null
                call    EndPaint
                clc                                     ; Allow processing
                ret

PaintWnd       endp


   Ps = 6fd54
   Rc = 6fd44
Top = 6fe38
TxtM = 6fd97

I soon discovered TEXTMETRIC to be 35 bytes in length and even /Zp4 compiler switch didn't solve problem. I modified TEXTMETRIC to have one more field called tmPadding and this solved my problem.

Does anyone know, could this or will this cause me grief at some point having changed windows.inc

qWord

Quotesub     esp, 8                          ; Setup for EndPaint API in epilogue
BeginPaint == stdcall. Or what are you doing here? Maybe the arguments are overwritten by BeginPaint()

Also:
- you know that you must preserve ebx,edi and esi in API callbacks?
- you are using LODS and I cant see where ESI gets loaded
FPU in a trice: SmplMath
It's that simple!

Tight_Coder_Ex

I have although not particularly unique, but peculular to my applications a method by which I handle messages where ESI is established at the entry point to current windows default procedure
  MWndProc      proc
                mov     esi, offset MMap - 8
                jmp     ScanMap
  MWndProc      endp


Kernel code has already preserved ESI, EDI etc, so I can use them freely without compromise.  ScanMap, the message handler of my design does need EBX be preserved though.

My application works fine, and TxtM exists at 6FD96 after having modifiled 'windows.inc'

This is the bit of code in ScanMap that sets ESI, just before my message handler is called which is in EAX


     @Hdl:        lea    esi, [esp + 4]
                  call   eax

MichaelW

The TEXTMETRIC structure is supposed to have a length of 56 bytes. The alignment rules are specified here under Structure and Union Layout. The structure definition in the MASM32 windows.inc lacks an alignment specification. At least for ML 6.14 the /Zp command line option appears to affect only the intra-structure alignment (which due to the order of the members from largest to smallest is not required for this structure), where specifying an alignment on the structure definition affects both the intra-structure alignment and the inter-structure alignment.

;=========================================================================
    include \masm32\include\masm32rt.inc
;=========================================================================

_TEXTMETRICA STRUCT 4
  tmHeight              DWORD      ?
  tmAscent              DWORD      ?
  tmDescent             DWORD      ?
  tmInternalLeading     DWORD      ?
  tmExternalLeading     DWORD      ?
  tmAveCharWidth        DWORD      ?
  tmMaxCharWidth        DWORD      ?
  tmWeight              DWORD      ?
  tmOverhang            DWORD      ?
  tmDigitizedAspectX    DWORD      ?
  tmDigitizedAspectY    DWORD      ?
  tmFirstChar           BYTE       ?
  tmLastChar            BYTE       ?
  tmDefaultChar         BYTE       ?
  tmBreakChar           BYTE       ?
  tmItalic              BYTE       ?
  tmUnderlined          BYTE       ?
  tmStruckOut           BYTE       ?
  tmPitchAndFamily      BYTE       ?
  tmCharSet             BYTE       ?
_TEXTMETRICA ENDS

;=========================================================================

printf MACRO format:REQ, args:VARARG
    IFNB <args>
        invoke crt_printf, cfm$(format), args
    ELSE
        invoke crt_printf, cfm$(format)
    ENDIF
    EXITM <>
ENDM

;=========================================================================
    .data
    .code
;=========================================================================
start:
;=========================================================================

    printf( "%d\n",SIZEOF TEXTMETRICA )
    printf( "%d\n",SIZEOF _TEXTMETRICA )

    inkey "Press any key to exit..."
    exit
;=========================================================================
end start


53
56

eschew obfuscation

Tight_Coder_Ex

Thanks Micheal, I hadn't noticed the alignment option of STRUCT, but now stack is aligned as expected without adding a field to TEXTMETRIC. :U

hutch--

TC,

You are living dangerously not preserving ESI and EDI as well, the OS expects them to be the same on exit as entry, especially as you are calling a number of API functions that conform to the Intel ABI.
Download site for MASM32      New MASM Forum
https://masm32.com          https://masm32.com/board/index.php

dedndave

especially because.....
"MWndProc" sounds like a callback function   :P
this is when EBX, EBP, ESI, EDI and DF should be preserved
and i have noticed that, sometimes, you can get away with it - lol
quite by accident, i forgot to preserve EDI, and it worked
well - it worked once - it would undoubtedly fail under the right circumstances

Tight_Coder_Ex

Quote from: hutch-- on May 22, 2011, 11:07:55 AM
You are living dangerously

I would like to think of it as unorthadox  :dazzled: and this snippet from kernel shows why it works.


7E41870C        55                        push    ebp
7E41870D        8BEC                      mov     ebp, esp
7E41870F        56                        push    esi
7E418710        57                        push    edi
7E418711        53                        push    ebx
7E418712        68 CDABBADC               push    DCBAABCD
7E418717        56                        push    esi
7E418718        FF75 18                   push    dword ptr [ebp+18]      lParam
7E41871B        FF75 14                   push    dword ptr [ebp+14]      wParam
7E41871E        FF75 10                   push    dword ptr [ebp+10]      Msg
7E418721        FF75 0C                   push    dword ptr [ebp+C]       hWnd
7E418724        64:A1 18000000            mov     eax, fs:[18]
7E41872A        8088 B40F0000 01          or      byte ptr [eax+FB4], 1

7E418731        FF55 08                   call    [ebp+8]        Entry point to application defined windows procedure

7E418734        64:8B0D 18000000          mov     ecx, fs:[18]
7E41873B        80A1 B40F0000 00          and     byte ptr [ecx+FB4], 0

M$ even made it so you don't have to use "ret 10H" upon completion

7E418742        817C24 04 CDABBADC        cmp     dword ptr [esp+4], DCBAABCD
7E41874A        0F85 607C0200             jnz     7E4403B0

7E418750        83C4 08                   add     esp, 8       Waste 712 & 717

Here is why I don't need bother saving registers in my callback funtions,
except EBX because ScanMap needs it if windows is sub/super classed

7E418753        5B                        pop     ebx
7E418754        5F                        pop     edi
7E418755        5E                        pop     esi
7E418756        5D                        pop     ebp
7E418757        C2 1400                   ret     14


Quote from: dedndave on May 22, 2011, 11:14:04 AM
it would undoubtedly fail under the right circumstances

So far even when I specify default processing either through CallWindowProc or DefWindowProc I haven't had any problems, but if there is one, that's where it'll probably be.  If it does rear its ugly head  :naughty: then I'll just preserve registers for that event.

drizz

The disassembly shows that the caller checks the stack "DCBAABCD" for "programmers" who think wndproc is cdecl instead of stdcall. They probably also compile this subroutine without FPO to preserve non-volatille registers for the same "programmers".
The truth cannot be learned ... it can only be recognized.

qWord

Quote from: msdn, Argument Passing and Naming Conventions
Microsoft Specific
...
The compiler generates prolog and epilog code to save and restore the ESI, EDI, EBX, and EBP registers, if they are used in the function.
...
Argument Passing and Naming Conventions
FPU in a trice: SmplMath
It's that simple!

drizz

It probably is a masm proc since that "push esi" looks suspicious.

proc_7E41870C proc stdcall <FORCEFRAME> uses esi edi ebx, pCallback:PTR WNDPROC, hWnd:HWND, uMsg:DWORD, lParam:LPARAM, wParam:WPARAM
push 0DCBAABCDh
push esi ; in case [esp+5*4] gets overwritten
invoke pCallback,hWnd,uMsg,lParam,wParam
cmp [esp+sizeof dword],dword ptr 0DCBAABCDh
jne FixMe
add esp,8
ret
proc_7E41870C endp


The truth cannot be learned ... it can only be recognized.