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
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
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
The TEXTMETRIC structure is supposed to have a length of 56 bytes. The alignment rules are specified here (http://msdn.microsoft.com/en-us/library/aa290049.aspx) 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
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
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.
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
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.
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".
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 (http://msdn.microsoft.com/en-us/library/984x0h58(v=VS.71).aspx)
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