This is a simple Hello World code I copied from someone.
.386
.MODEL flat, stdcall
STD_OUTPUT_HANDLE EQU -11 ; std output device
;////
; Function prototypes
GetStdHandle PROTO, nStdHandle: DWORD
WriteConsoleA PROTO, handle: DWORD, lpBuffer:PTR BYTE, nNumberOfBytesToWrite:DWORD, lpNumberOfBytesWritten:PTR DWORD, lpReserved:DWORD
ExitProcess PROTO, dwExitCode: DWORD
;////
.data
consoleOutHandle dd ?
bytesWritten dd ?
message db "Hello darkness my old friend",13,10
lmessage equ $-message
;////
.code
main PROC
INVOKE GetStdHandle, STD_OUTPUT_HANDLE
mov consoleOutHandle, eax
mov edx, offset message
pushad
mov eax, lmessage
INVOKE WriteConsoleA, consoleOutHandle, edx, eax, offset bytesWritten, 0
popad
INVOKE ExitProcess, 0
main ENDP
END main
I wondered what the pushad and popad were for, so I removed them.
This is register data before and after WriteConsoleA
is invoked:
Before: EAX = 00000014 EBX = 00B1A000 ECX = 008C1005 EDX = 008C4008 ESI = 008C1005 EDI = 008C1005 EIP = 008C1026 ESP = 0081FCE8 EBP = 0081FCF4 EFL = 00000213
After: EAX = 00000001 EBX = 00B1A000 ECX = 00000014 EDX = 008C4004 ESI = 008C1005 EDI = 008C1005 EIP = 008C103A ESP = 0081FCE8 EBP = 0081FCF4 EFL = 00000202
I looked up the msdn page for the function but couldn't find anything about it.
Is it the function doing this? Do all functions behave like this?
The pushad
instruction pushes all the general purpose registers on to the stack so they can later be restored (with, surprisingly enough, popad
). Without that, a function which modifies registers without itself saving them first, may well leave you in an undesirable state.
In the case of Windows API calls, the stdcall calling convention is used (as per the .MODEL
directive at the top of your code), which states in part:
Registers
EAX
,ECX
, andEDX
are designated for use within the function. Return values are stored in theEAX
register.
That means those registers may not be preserved in the call. So, if you want to guarantee they retain their original values, you need to do that yourself, either with pushad ... popad
or the longer but more targeted:
push eax
push ecx
push edx
; call function
pop edx
pop ecx
pop eax
Whether that's necessary in this very simple example (where you just immediately exit the process after the WriteConsole
call), I can't say for sure. However, it's often prudent to protect yourself from the undesirable effects of functions that you call.