PROWAREtech
Intel IA-32 Assembly Tutorial - A Guide to the Basics of x86 Assembly - Page 08
The PROC Directive
A procedure is declared using the PROC
and ENDP
directives. The RET
instruction ends the procedure.
sample_proc PROC
.
.
.
ret
sample_proc ENDP
The CALL
instruction calls a procedure by directing the processor to begin execution at a new memory location.
The procedure uses a RET
instruction to bring the processor back to the point in the program where the procedure
was called. The CALL
instruction pushes its return address on the stack and copies the called procedure's address
into the instruction pointer. When the procedure is ready to return, its RET
instruction pops the return address
from the stack into the instruction pointer register.
The USES
operator is used with the PROC directive and lists the names of all the registers modified within a
procedure. It automatically pushes and pops instructions specified.
sample_proc PROC USES ecx edx
inc edx
add ecx,edx
ret
sample_proc ENDP
Local variables are declared using the LOCAL
directive. It must be used immediately
following the PROC
directive.
qsort PROC USES eax ebx ecx edx
LOCAL temp:SDWORD, left:PTR SDWORD, right:PTR SDWORD, pivot:SDWORD, tempArray[5]:BYTE
.
.
.
ret
qsort ENDP
Variable types consist of BYTE
, SBYTE
, WORD
, SWORD
, DWORD
, SDWORD
,
FWORD
, QWORD
, TBYTE
, PTR BYTE
, PTR SBYTE
, PTR WORD
, PTR SWORD
,
PTR DWORD
, PTR SDWORD
, PTR QWORD
and PTR TBYTE
.
Local variables are created on the runtime stack. The STACK
directive reserves stack space. Make sure there is enough stack space to hold the allocated
local variables. For example:
.stack 8192
Stack parameters comes is two varieties: register parameters and stack parameters. Register parameters are faster but make the code harder to read/more cryptic.
Values passed to a procedure are arguments.
When values are received by the called procedure, they are parameters.
High-level languages use the runtime stack for parameters.
The following little procedure increments its parameter by one and then returns its value. The parameter is passed by reference.
.386
.model FLAT
PUBLIC _increment@4
_INCR SEGMENT
_increment@4 PROC NEAR
mov eax, [esp+4]
inc DWORD PTR [eax]
mov eax, DWORD PTR [eax] ;C/C++ expects the return value to be stored in EAX
ret 4 ;add 4 to the stack pointer (ESP); this is STDCALL calling convention
_increment@4 ENDP
_INCR ENDS
END
The previous procedure would be declared like this in C++:
extern "C" int __stdcall increment(int &x);
Here is the same procedure using a __cdecl
calling convention.
.386
.model FLAT
PUBLIC _increment
_INCR SEGMENT
_increment PROC NEAR
mov eax, [esp+4]
inc DWORD PTR [eax]
mov eax, DWORD PTR [eax]
ret 0 ;stack pointer (ESP) will have 4 added to it by the calling procedure; this is CDECL calling convention
_increment ENDP
_INCR ENDS
END
C++ declaration:
extern "C" int increment(int &x);
This procedure adds three 32-bit integers:
TITLE C++ declaration: 'extern "C" int add(int a, int b, int c);'
.386
.model FLAT
PUBLIC _add
_TEXT SEGMENT
_add PROC NEAR
; this could be simplified
mov edx, [esp+4] ;first parameter: a
mov ecx, [esp+8] ;second parameter: b
mov ebx, [esp+12] ;third parameter: c
xor eax, eax
add eax, edx
add eax, ecx
add eax, ebx
ret 0
_add ENDP
_TEXT ENDS
END
This procedure multiplies two 32-bit integers:
TITLE 'extern "C" int multiply(int a, int b);'
.386
.model FLAT
PUBLIC _multiply
_TEXT SEGMENT
_multiply PROC NEAR
mov eax, DWORD PTR [esp+4]
imul eax, DWORD PTR [esp+8]
ret 0
_multiply ENDP
_TEXT ENDS
END
The PROTO Directive
The PROTO
directive creates a prototype for an existing procedure. A prototype, just like in C/C++, declares a procedure's name and parameter list.
ArraySum PROTO, ptrArray:PTR DWORD, sizArray:DWORD
ArraySum PROC, ptrArray:PTR DWORD, sizArray:DWORD
.
.
.
ArraySum ENDP
Example calling some Windows API functions using CALL
(these use STDCALL calling convention):
.686
.model flat,stdcall
.stack 8192
.data
WndMsg BYTE "Testing CALL instruction.",0
WndTtl BYTE "TEST",0
.code
MessageBoxA PROTO, hwnd:DWORD, message:PTR BYTE, windowTitle:PTR BYTE, options:DWORD
GetActiveWindow PROTO
pop PROC
call GetActiveWindow
push 0 ;options (0 = just an OK button)
mov ebx,offset WndTtl
push ebx
mov ebx,offset WndMsg
push ebx
push eax ;GetActiveWindow returned HWND value in EAX
call MessageBoxA
ret
pop ENDP
END
The LEA
(load effective address) instruction returns the offset of any type of indirect operand.
The assembler OFFSET
operator
only returns constant assembly time offsets.
LEA
gets the offset at runtime.
increment PROC
LOCAL num:DWORD
lea esi,num ;load effective address (get a pointer to num)
xor eax,eax ;zero out EAX
mov [esi],eax ;move zero into num
inc DWORD PTR[esi] ;increment num using its address
increment ENDP