News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests

Main Menu

My first function

Started by StarsInTheSky, May 10, 2015, 05:05:56 PM

Previous topic - Next topic

StarsInTheSky

OK so investingating Mikl's code in my earlier thread, I made this first code to just pop up some values:

include \masm32\include\masm32rt.inc

image_base=400000h

.data
buffer      dd 'ZM','EP';Signantures
      dw 14Ch     ;Machine
      dw 1     ;count of section
szInfoText db "API Addresses:",13,10
     db "LoadLibraryA:  0x%08lX",13,10
     db "MessageBoxA:  0x%08lX",0
Result db "Result:", 0

cBuff db 70 DUP (0)

_MessageBox dd 0
_LoadLibrary dd 0

.code
start:
    xor ebx,ebx
    mov eax, MessageBoxA
    push eax

    sub eax,_MessageBox-buffer+image_base+4
    mov _MessageBox,eax

    mov eax, LoadLibraryA
    push eax

    sub eax, _LoadLibrary-buffer+image_base+4
    mov _LoadLibrary,eax
   
    push offset szInfoText
    push offset cBuff
    call wsprintfA

    push 0
    push offset Result
    push offset cBuff
    push 0
    call MessageBoxA

    push _MessageBox
    push _LoadLibrary
   
    push offset szInfoText
    push offset cBuff
    call wsprintfA
   
    push 0
    push offset Result
    push offset cBuff
    push 0
    call MessageBoxA

    call ExitProcess
end start


the repetetive nature of this made me wish for my first function:

include \masm32\include\masm32rt.inc

image_base=400000h

.data
buffer      dd 'ZM','EP';Signantures
      dw 14Ch     ;Machine
      dw 1     ;count of section
szInfoText db "API Addresses:",13,10
     db "LoadLibraryA:  0x%08lX",13,10
     db "MessageBoxA:  0x%08lX",0
Result db "Result:", 0

cBuff db 70 DUP (0)

_MessageBox dd 0
_LoadLibrary dd 0

.code
start:
    xor ebx,ebx
    mov eax, MessageBoxA
    push eax

    sub eax,_MessageBox-buffer+image_base+4
    mov _MessageBox,eax

    mov eax, LoadLibraryA
    push eax

    sub eax, _LoadLibrary-buffer+image_base+4
    mov _LoadLibrary,eax
   
    call ShowMyMessage
    add esp, 8

    push _MessageBox
    push _LoadLibrary
   
    call ShowMyMessage
    add esp, 8

    call ExitProcess


PUBLIC ShowMyMessage
ShowMyMessage PROC
  ; Subroutine Prologue
  push ebp     ; Save the old base pointer value.
  mov ebp, esp ; Set the new base pointer value.
  sub esp, 70   ; Make room for my 70 byte buffer.
  push edi     ; Save the values of registers that the function
  push esi     ; will modify. This function uses EDI and ESI.
  ; (no need to save EBX, EBP, or ESP)

  ; Subroutine Body
  mov eax, [ebp+8]   ; Move value of parameter 1 into EAX
  mov esi, [ebp+12]  ; Move value of parameter 2 into ESI
  ;mov edi, [ebp+16]  ; Move value of parameter 3 into EDI

  push esi
  push eax
  push offset szInfoText
  push [ebp-70] ;our 70 byte buffer
  call wsprintfA
   
  push 0
  push offset Result
  push [ebp-70]
  push 0
  call MessageBoxA

  ; Subroutine Epilogue
  pop esi      ; Recover register values
  pop  edi
  mov esp, ebp ; Deallocate local variables
  pop ebp ; Restore the caller's base pointer value
  ret
ShowMyMessage ENDP

end start


this fails silently. It seems to fail inside wsprintfA.

I am trying to declare a local buffer within my function and then send that buffer to wsprintfA. I am suspecting that is done wrong.

Any pointer to how to correct this?

StarsInTheSky

I figured it out. so I need to send a pointer to my local variable.

the call to wsprinfA becomes:
  lea edi, [ebp-70]
  push esi
  push eax
  push offset szInfoText
  push edi
  call wsprintfA


it works

dedndave

2 points....

1) normally, you can use INVOKE to push variables (INVOKE is an internal masm macro)

    INVOKE  MessageBox,hWnd,offset szMessageText,offset szTitleText,MB_OK

the last argument listed is the first to be pushed
sometimes, i use PUSH rather than INVOKE, if i want to call a function in between pushes
constants, like MB_OK, MB_YESNO, etc, are defined in windows.inc

2) the windows ABI is a set of rules that should be followed when writing win32 functions
even though the rules are not always required,
if you write a function that follows them, you can re-use that function in places where they are required
the rules include all kinds of things but here, we are mainly concerned with these:

a) EAX may be destroyed across the call, and is used to return a status or return value

b) ECX and EDX may be destroyed across the call
in assembly language, we can also use them to return values
but, it's clumsy to get them if the function is called from a compiled language

c) EBX, EBP, ESI, and EDI must be preserved across the call

d) if you set the direction flag inside a function, you should clear it before returning

there is a different ABI for 64-bit programs

windows API functions follow the same rules
so, you know that EAX, ECX, EDX are volatile across a call and the others are preserved

dedndave

here is an example of a win32 function

create a prototype in the project include file, or otherwise near the beginning of the program
SomeFunc PROTO :HWND,:DWORD
this allows the use of INVOKE with the function

SomeFunc PROC USES EBX ESI EDI hWnd:HWND,dwArg2:DWORD

;---------------------------

    LOCAL   dwLocal1      :DWORD
    LOCAL   rcLocal2      :RECT
    LOCAL   abLocal3[24]  :BYTE

;---------------------------

;some code here

    INVOKE  GetClientRect,hWnd,addr rcLocal2

;some more code here

    lea     edx,abLocal3

;some more code here

    mov     eax,dwArg2
    imul    eax,5
    mov     dwLocal1,eax

;some more code here

    ret

SomeFunc ENDP


i might call it like this...

    INVOKE  SomeFunc,hWnd,70

StarsInTheSky

thank you dave.
I had missed using lea on the local.

Vortex

Hi StarsInTheSky,

You must balance the stack manually if you are calling C functions without using the invoke macro :

include     \masm32\include\masm32rt.inc

.data

format      db 'testing wsprintf : %s',0
string      db 'This is a demo.'

.data?

buffer      db 64 dup(?)

.code

start:

    push    OFFSET string
    push    OFFSET format
    push    OFFSET buffer
    call    wsprintf
    add     esp,3*4

    invoke  StdOut,ADDR buffer

    invoke  ExitProcess,0

END start

StarsInTheSky

Hi Vortex,

Oh thank you. I was only doing that for my own function, but not when calling wsprintfA.

From now on I will check the function to see whether they are cdecl :t

https://msdn.microsoft.com/en-us/library/windows/desktop/ms647550(v=vs.85).aspx

I read somewhere else that MessageBoxA is a stdcall and will clean up by itself. But reading on msdn, there is no mentioning of it.

It just says winapi ?

https://msdn.microsoft.com/en-us/library/windows/desktop/ms645505(v=vs.85).aspx


dedndave

win api functions are StdCall
you can look at the prototypes
MessageBox is in winuser.inc i think
msvcrt.inc for wsprintf

the default convention for PROTO is StdCall

StarsInTheSky

thank you for the helpful answers :)  :t

MichaelW

wsprintf is part of the Windows API, in User32, but note that it does use the C calling convention.
Well Microsoft, here's another nice mess you've gotten us into.

Oliver Scantleberry

"Well Microsoft, here's another nice mess you've gotten us into."

I'm pretty sure that Ralph said to Alice, "Well Alice, here's another fine mess that you've gotten us into."



Oliver Scantleberry

Quote from: dedndave on May 10, 2015, 08:53:12 PM
2 points....

1) normally, you can use INVOKE to push variables (INVOKE is an internal masm macro)

    INVOKE  MessageBox,hWnd,offset szMessageText,offset szTitleText,MB_OK



I thought that you couldn't use "OFFSET" in an INVOKE statement. You had to use "ADDR". What's the story?

hutch--

Oliver,

ADDR does a bit more than OFFSET but if the address IS an OFFSET in the .DATA or .DATA? sections, it works fine. MASM actually maintains the distinction between an OFFSET versus a LOCAL where the ADDR operator simplifies it for INVOKE usage.

Oliver Scantleberry

Quote from: hutch-- on December 01, 2015, 07:32:39 AM
Oliver,

ADDR does a bit more than OFFSET but if the address IS an OFFSET in the .DATA or .DATA? sections, it works fine. MASM actually maintains the distinction between an OFFSET versus a LOCAL where the ADDR operator simplifies it for INVOKE usage.

That makes sense. Thanks Hutch.