How exactly do I make a procedure that accepts arguments? Do I have to have it take them off the stack by itself, or can the process be automated with MASM? If I have to do it myself, how do I have to balance the stack frame?
Most procedures you use are STDCALL, that means the proc balances the stack. An automated stack frame will modify the RET opcode to include the number of bytes to remove from the stack,for example if you have 2 DWORD parameters it will change it to RET 8. So no, you do not have to worry about balancing the stack in STDCALL, it is handled in the stack frame. C calling convention requires that the caller balance the stack, in that case you are responsible but specific to MASM, it will also insert the necessary ADD ESP,xxx into your code so you need not worry about it either.
Just to explain a bit about stack frames,which seem to be a hot topic these days, this is the general way they are built...
CALL:
1. Push the parameters onto the stack
2. push the return address
3. jump to the function
PROC:
1. Save the value of EBP (stack base pointer usually PUSH it)
2. Save any registers in the USES directive
3. Move ESP into EBP (the top of the entry stack is now seen as the base)
4. SUB the number of local bytes from ESP (make room for the local data)
5. Execute the proc...
6. Move EBP into ESP (reset the top of the stack)
7. Restore the USES registers
8. Restore EBP (usually POP it)
9. Adjust RET to remove the parameters in bytes from the stack (ie RET 8)
10. POP the return address
11. Jump to the return address
Could I get a quick code example?
In MASM syntax...
MyProc PROTO :DWORD,:DWORD,:DWORD
invoke MyProc, 1,2,3
MyProc PROC uses EDI ESI EBX Param1:DWORD, Param2:DWORD,Param3:DWORD
RET
MyProc ENDP
And then I can just use Param1-Param3 like I would any other memory location?
Yup, they assemble as an offset from EBP but you can use them as normal memory labels, mov data in and out perform math etc... The only exception is that you cannot directly get the offset of the memory location as it is not known at compile time, you must use LEA to caclulate it like this...
lea eax, Param1
Hi Bied,
Here is an example demonstrating how to code procedures at low level:
.386
.model flat, stdcall
option casemap :none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
public StdOut@4
.code
StdOut@4:
push ebp
mov ebp,esp
add esp,-12 ;3 dwords
; LOCAL hOutPut :DWORD
; LOCAL bWritten :DWORD
; LOCAL sl :DWORD
invoke GetStdHandle,STD_OUTPUT_HANDLE
mov [ebp-4], eax ; hOutPut
invoke lstrlen,[ebp+8] ; lpszText
mov [ebp-12], eax
lea eax,[ebp-8] ; bWritten
invoke WriteFile,[ebp-4],[ebp+8],[ebp-12],eax,NULL ; [ebp-12] = sl
mov eax,[ebp-8]
leave
ret 4
end
Main application:
.386
.model flat, stdcall
option casemap :none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib
StdOut PROTO :DWORD
.data
Message db 'Hello my friend!',0
.code
start:
invoke StdOut,ADDR Message
invoke ExitProcess,0
END start
[attachment deleted by admin]
Hi donkey,
In your listing of the operations under PROC, for MASM the order should be:
1. Save the value of EBP (stack base pointer usually PUSH it)
2. Move ESP into EBP (the top of the entry stack is now seen as the base)
3. SUB the number of local bytes from ESP (make room for the local data)
4. Save any registers in the USES directive
...
MyProc proc uses eax ebx ecx edx param1:DWORD, param2:DWORD
LOCAL local1:DWORD, local2:DWORD, local3:DWORD
mov eax, param1
mov eax, param2
mov eax, local1
mov eax, local2
mov eax, local3
ret
MyProc endp
00401000 55 push ebp
00401001 8BEC mov ebp,esp
00401003 83C4F4 add esp,0FFFFFFF4h
00401006 50 push eax
00401007 53 push ebx
00401008 51 push ecx
00401009 52 push edx
0040100A 8B4508 mov eax,[ebp+8]
0040100D 8B450C mov eax,[ebp+0Ch]
00401010 8B45FC mov eax,[ebp-4]
00401013 8B45F8 mov eax,[ebp-8]
00401016 8B45F4 mov eax,[ebp-0Ch]
00401019 5A pop edx
0040101A 59 pop ecx
0040101B 5B pop ebx
0040101C 58 pop eax
0040101D C9 leave
0040101E C20800 ret 8
Hi MichaelW,
Depends on which assembler you use :),
GoAsm...
0040105C . 55 PUSH EBP
0040105D . 57 PUSH EDI
0040105E . 56 PUSH ESI
0040105F . 53 PUSH EBX
00401060 . 89E5 MOV EBP,ESP
00401062 . 83EC 04 SUB ESP,4
00401065 . 8B45 18 MOV EAX,DWORD PTR SS:[EBP+18]
00401068 . 6A 00 PUSH 0 ; /Style = MB_OK|MB_APPLMODAL
0040106A . 50 PUSH EAX ; |Title
0040106B . 55 PUSH EBP ; |Text
0040106C . 830424 FC ADD DWORD PTR SS:[ESP],-4 ; |
00401070 . 6A 00 PUSH 0 ; |hOwner = NULL
00401072 . E8 A72F0000 CALL <JMP.&USER32.MessageBoxA> ; \MessageBoxA
; ...
0040108E . 89EC MOV ESP,EBP
00401090 . 5B POP EBX
00401091 . 5E POP ESI
00401092 . 5F POP EDI
00401093 . 5D POP EBP
00401094 . C2 1000 RETN 10
Note that at offset 0040106B it pushes an offset to a local but never uses LEA, the source line was...
invoke MessageBox,NULL,offset testlocal,eax,0
Should the USES stuff be pushed as in MASM it would be more difficult to handle a "registerless" invoke, by doing them prior to the mov it makes the math exceedingly easy.