Number of Bytes |
C/C++ Types |
Assembly Type |
1 |
char |
BYTE |
2 |
short |
WORD |
4 |
int, float |
DWORD |
8 |
double |
QWORD |
mov reg,DWORD[address]Accessing integers in memory is very common--your local variables, arrays, and so on are usually integers.
mov eax,DWORD[myInt] ; read this int into eax
ret
myInt:
dd 0xa3a2a1a0 ; "data DWORD" containing this value
movzx reg,BYTE[address]Accessing data as bytes is useful for string processing, or to understand what really shows up in memory.
movzx eax,BYTE[myInt + 2] ; read this byte into eax
ret
myInt:
dd 0xa3a2a1a0 ; "data DWORD" containing this value
mov reg,DWORD[array + 4*index]You can also compute this manually, using "imul" and "add".
mov eax,DWORD[myArr + 4*2] ; eax = myArr[2]
ret
myArr:
dd 10 ; that's myArr[0]
dd 11 ; and myArr[1]
dd 12 ; and myArr[2]
dd 13 ; and myArr[3]
someConstant:Here's an example where we index into a little 4-integer array:
dd constant0
dd constant1
mov eax,DWORD[myArr + 4*2] ; eax = myArr[2]
ret
myArr:
dd 10 ; that's myArr[0]
dd 11 ; and myArr[1]
dd 12 ; and myArr[2]
dd 13 ; and myArr[3]
sub esp,NThis is very common for allocating temporary arrays, strings, local variables, and so on. You'll get a horrible crash if you forget to put the stack pointer back, though!
... use N bytes of space starting at esp here ...
add esp,N
sub esp,32 ; Claim this many bytes of stack space
mov DWORD[esp+28],13 ; Store a constant into that space
mov eax,DWORD[esp+28] ; Load it back
add esp,32 ; Clean up the stack
ret
Here's another example where I allocate a 100-integer array, and loop over the array:
sub esp,4*100; claim 100 integers of space
mov eax,0; i
mov ecx,esp; ecx points to start of our array
loopstart:
mov DWORD[ecx+4*eax],eax; arr[i]=i;
add eax,1 ; i++
cmp eax,100; if (i<100) ...
jl loopstart; ... then goto start
mov eax,DWORD[ecx+4*36]; return array element 36
add esp,4*100; clean up array
ret
push regHere's an example where I save and restore ebp. "esp", "ebp", "edi", and "esi" are all preserved registers, which means other functions won't change them, but you must save and restore them before you can use them too!
... use reg here (change its value) ...
pop reg
... now reg is back to its old value ...
push ebp
mov ebp,17
mov eax,ebp
pop ebp
ret
push ebp; stash old value of ebp on the stackA stack frame is useful in long functions, since it provides a fixed reference point for doing addressing, and makes it easier to clean off the stack at the end of the function.
mov ebp,esp; ebp == stack pointer at start of function
... do the work of the function here, including pushing values and adding to the stack ...
mov esp,ebp; restore stack pointer (easier than figuring the correct "add"!)
pop ebp; restore ebp
push ebp; stash old value of ebp on the stack
mov ebp,esp; ebp == stack pointer at start of function
sub esp,1000 ; make some room on the stack
mov DWORD[ebp-4],7 ; local variables are at negative offsets from the base pointer
mov eax,DWORD[ebp-4]; same local variable
mov esp,ebp; restore stack pointer (easier than figuring the correct "add"!)
pop ebp; restore ebp
ret
(Try this in NetRun now!)
push firstArgIf a function takes two or more arguments, the first (leftmost) argument should be sitting on top of the stack:
call someFunc
pop reg
push secondArgHere's an example of calling a one-argument function. I don't care about the return value, so I just use "add" to clean the parameter off the stack:
push firstArg
call someFunc ; same as someFunc(firstArg,secondArg);
pop reg1
pop reg2
push 17
extern print_int
call print_int
add esp,4
ret