Page 1 of 2
Get value from the stack
Posted: Thu Jul 12, 2007 11:00 am
by d4n1l0d
When I call in C
Code: Select all
testfunc( 10 ); //10 is a 32bit value
The number "10" is passed to the stack, how can I get it in asm code?
I tried this way:
Code: Select all
testfunc:
pop eax ; EIP value
pop ebx ; number 10
push eax ; back the EIP value
ret ; return
but, I want another way....
And I want to have a back value ( return value ), how do I do that?
Posted: Thu Jul 12, 2007 11:08 am
by XCHG
C Compilers use the CDECL calling convention. You can
click here to read the article that I have written related to the CDECL calling convention. In the CDECL calling convention, you push the parameters from right to left just like the
STDCALL calling convention; the only difference is that in the CDECL calling convention, the caller procedure is responsible for clearing the parameters off the stack. However, you can build a stack frame as shown below to get your values:
Code: Select all
__TestFunc:
; void __TestFunc (DWORD InNumber); CDECL;
PUSH EAX
PUSH EBP
MOV EBP , ESP
MOV EAX , DWORD PTR [EBP + (3*4)] ; InNumber
POP EBP
POP EAX
RET
When the ESP is moved to the EBP (Base Pointer), there you will have a copy of ESP that will not be changed if ESP is decremented. Now you can see that before I have moved the ESP to the EBP, I have pushed 2 parameters onto the stack and when I have called the [TestFunc] function, I have automatically pushed the EIP of the instruction after the CALL into the stack therefore, right at the point where I have moved the ESP to the EBP, I already have 3 DWORD values pushed onto the stack (EIP, EAX and EBP) thus the offset from EBP to the first (and in here also the last) parameter is 3*4 (3 parameters each of which is 4 bytes long).
I hope that helps.
Posted: Thu Jul 12, 2007 12:59 pm
by d4n1l0d
+/-
Would this be ok?:
Code: Select all
plus3 (dword value1, dword value2, dword value3 ) {
return value1 + value2 + value3;
}
Code: Select all
;asmcode
plus3:
push ebp ;
mov ebp,esp ;
mov eax,[epb + 0x8] ; first value
add eax,[epb + 0xC] ; second value
add eax,[epb + 0x10] ; third value [ sorry, no 0xG here anymore xD ]
pop ebp ;
;( and the return value is at eax )
ret
EDIT.: Calling the function pushes the values from right to left ( so plus3( value1, value2 ,value3 ) will be pushed this way on the stack: ( see the picture )
Posted: Thu Jul 12, 2007 1:03 pm
by hailstorm
Yep! That's it!
Posted: Thu Jul 12, 2007 1:16 pm
by d4n1l0d
In the case that I had byte or word values, would be ok to do this?? :
Code: Select all
;asmcode
test:
push ebp ;
mov ebp,esp ;
mov ax,[epb + 0x8] ; first value ( the word )
mov al,[epb + 0xA] ; second value ( the byte )
pop ebp ;
Posted: Thu Jul 12, 2007 1:36 pm
by XCHG
d4n1l0d,
You don't seem to have read my post. The offset to the first parameter (the leftmost parameter) from the EBP is:
(N+1) * 4
where N is the number of 32-bit registers that you have pushed onto the stack to create the stack frame including the EBP register. In my example, you see that I have pushed 2 registers onto the stack so the offset to the first parameter (leftmost) will be:
(2 + 1) * 4 = 3*4
Which is why I provided the offset [EBP + (3*4)]
There are two more things that you have to know:
• It is really not optimal to push BYTE values onto the stack. Any *sane* high level language will NOT push a BYTE value onto the stack for a function/procedure's parameter. They will push a 32-bit value with the BYTE value set as the low order byte of the 32-bit value. I remember Pascal doing that in the stone age; Delphi does that too.
• We don't have 0xG in hexadecimal. Hexadecimal letters start with 0 and end with 0xF (0...0xF = 0...15 = 16 values hence HEXadecimal).
Posted: Thu Jul 12, 2007 1:40 pm
by d4n1l0d
WTF xD
I did not see that G value there( and how I wrote it, I don't know), sorry, it should be 0x10 ( fucking 0xG xD )
Now thing's became clear, thanks
Posted: Thu Jul 12, 2007 1:57 pm
by XCHG
This example that I just coded might help you more:
Code: Select all
; ——————————————————————————————————————————————————
PUSH DWORD 321 ; Int3
PUSH DWORD 4574 ; Int2
PUSH DWORD 324 ; Int1
CALL __FindBiggest ; Call the function
ADD ESP , (3*4) ; Remove 3 DWORD parameters off the stack
JMP $
; ——————————————————————————————————————————————————
__FindBiggest:
; DWORD __FindBiggest (DWORD Int1, DWORD Int2, DWORD Int3); CDECL;
PUSH EDX ; Preserve the Data Register
PUSH EBP ; Preserve the Base Pointer
MOV EBP , ESP ; Create the Stack Frame
MOV EAX , DWORD PTR [EBP + (3*4)] ; EAX = Int1
MOV EDX , DWORD PTR [EBP + (4*4)] ; EDX = Int2
CMP EAX , EDX ; Compare EAX with EDX
CMOVB EAX , EDX ; EAX = EDX (If EDX > EAX)
MOV EDX , DWORD PTR [EBP + (5*4)] ; EDX = Int3
CMP EAX , EDX ; Compare EAX with EDX
CMOVB EAX , EDX ; EAX = EDX (if EDX > EAX)
POP EBP ; Restore the Base Pointer
POP EDX ; Restore the Data Register
RET ; Return but don't remove parameters
; ——————————————————————————————————————————————————
Posted: Thu Jul 12, 2007 2:16 pm
by d4n1l0d
Ok, look this function ( kind of memsetb )
Code: Select all
void *memset(void *dest, char val, size_t count)
{
char *temp = (char *)dest;
for( ; count != 0; count--) *temp++ = val;
return dest;
}
; +---------+
; | count |
; +---------+ <--+
; | value | | 4 bytes
; +---------+ <--+
; | dest |
; +---------+
; | * | -> caller's offset
; +---------+
;
memsetb: ; memsetb( void *dest, char *value, int count )
push edi ;
push ebp ;
mov ebp,esp ;
mov eax, [ebp + (3*4)]; dest
mov edi,eax ;
mov eax, [ebp + (4*4)]; char
mov ecx, [ebp + (5*4)]; count
rep stosb
mov eax,edi ; return value
pop ebp
pop edi
ret ;
is it ok?
Posted: Thu Jul 12, 2007 2:49 pm
by XCHG
All of the offsets are perfect. Though there are somethings that are wrong/not-optimal with the code:
• Always use the CLD instruction before string instructions such as STOSB/MOVSB and etc if you are planning to copy/store by going forward in memory.
• You could simply move the [dest] parameter to EDI instead of copying to EAX first.
•
(IMPORTANT): when the STOSB instruction is done, it has decremented ECX to zero and incremented EDI as many times as the original EDI. For example if EDI was 10 at first and ECX was 5, when STOSB is done, ECX will be 0 and EDI will be 15 (10 + original ECX = 10 + 5 = 15). Therefore when you are setting the return value of the function by moving EDI to EAX, you are getting an inaccurate pointer. If I were you, I would simply access the [dest] parameter from the stack and move it to EAX at the end of the function because the parameter in the stack is untouched.
I just coded a simple [memsetb] function for you to get some ideas (it divides the number of characters to move by 4 to see if it can use STOSD instead of STOSB to move 4 bytes at a time):
Code: Select all
; ——————————————————————————————————————————————————
PUSH DWORD 1024 ; [NumberofBytes]
PUSH DWORD 'A' ; [Value] (Pushed as a DWORD)
PUSH DWORD OFFSET String1 ; [Destination]
CALL __MemSetB ; Call the function
ADD ESP , (3*4) ; Remove 3 DWORD parameters
JMP $
; ——————————————————————————————————————————————————
__MemSetB:
; void __MemSetB (void* Destination, char Value, DWORD NumberofBytes); CDECL;
PUSH EBX ; Preserve the Base Index
PUSH ECX ; Preserve the Count Register
PUSH EDX ; Preserve the Data Register
PUSH EDI ; Preserve the Destination Index
PUSHFD ; Preserve the 32-bit Flags Register
PUSH EBP ; Preserve the Base Pointer
MOV EBP , ESP ; Creat the Stack Frame
CLD ; Copy and move forward (IMPORTANT)
XOR EAX , EAX ; Set the result to zero (Failure
MOV ECX , DWORD PTR [EBP + (9*4)] ; *ECX = [NumberofBytes]
TEST ECX , ECX ; If the number of bytes is zero ...
JZ .EP ; ... Jump to the end of the function
MOV EDI , DWORD PTR [EBP + (7*4)] ; *EDI = [Destination]
MOV EAX , DWORD PTR [EBP + (8*4)] ; *EAX = [Value]
AND EAX , 0x000000FF ; Only keep the Least Significant Byte
MOV EDX , ECX ; Get a copy of [NumberofBytes] in EDX
AND EDX , 0x00000003 ; EDX = [NumberofBytes] MOD 4 (Remainder)
SHR ECX , 0x00000002 ; ECX = [NumberofBytes] DIV 4 (Quotient)
TEST ECX , ECX ; See if we have to move any DWORDs
JZ .MoveBytes ; Jump to ... if not
.MoveDWORDs: ; Start storing 4 bytes at a time
MOV EBX , EAX ; EBX = [Value]
SHL EBX , 0x00000008 ; Byte#1 of EBX = [Value]
OR EAX , EBX ; EAX = 0x0000[Value][Value]
MOV EBX , EAX ; EBX = 0x0000[Value][Value]
SHL EBX , 0x00000010 ; EBX = 0x[Value][Value]0000
OR EAX , EBX ; EAX = [Value][Value][Value][Value]
REP STOSD ; Store 4 characters at a time in ES:EDI
.MoveBytes: ; Move as many as 0 to 3 bytes
TEST EDX , EDX ; See if we have to move any bytes (if left)
JZ .SetResult ; Set the return value if not
MOV ECX , EDX ; ECX = Number of bytes to copy
REP STOSB ; Start storing from AL to ES:EDI
.SetResult: ; Set the result of the funcion
MOV EAX , DWORD PTR [EBP + (7*4)] ; Set the result to [Destination]
.EP: ; End of the procedure
POP EBP ; Restore the Base Pointer
POPFD ; Restore the 32-bit Flags Register
POP EDI ; Restore the Destination Index
POP EDX ; Restore the Data Register
POP ECX ; Restore the Count Register
POP EBX ; Restore the Base Index
RET ; Return to the calling procedure
; ——————————————————————————————————————————————————
Hope that helps.
Posted: Thu Jul 12, 2007 2:56 pm
by d4n1l0d
in mine memsetb I was planning to return the address + count ( that's way I return the value of EDI )
and, thank you very much for helping me
Posted: Fri Jul 13, 2007 1:32 am
by hailstorm
In my honest opinion (sorry XCHG) it is best to push ebp first, second, set esp in ebp and push the registers you want to save as last. This way, using a near call, all the parameters can be access from ebp+08h.
But that's just my point of view...
Posted: Fri Jul 13, 2007 4:17 am
by Brendan
Hi,
In my honest opinion (sorry Hailstorm), it's best not to use EBP at all....
My turn for a "SetBytes()" I guess:
Code: Select all
;——————————————————————————————————————————————————
PUSH DWORD 1024 ; [NumberofBytes]
PUSH DWORD 'A' ; [Value] (Pushed as a DWORD)
PUSH DWORD OFFSET String1 ; [Destination]
CALL __MemSetB ; Call the function
ADD ESP , (3*4) ; Remove 3 DWORD parameters
JMP $
;——————————————————————————————————————————————————
__SetBytes:
cmp dword [esp+3*4],0 ;Should any bytes be set?
jne .doIt ; yes
ret
.doIt:
push edi
push ecx
pushfd
mov edi,[esp+5*4] ;edi = address of bytes to set
mov ecx,[esp+6*4] ;ecx = number of bytes to set
mov eax,[esp+7*4] ;eax = value
cld
cmp ecx,16 ;Is it too small to worry about?
jb .doBytes ; yes, skip the setup costs and do it the easy way
push edx
mov ah,al ;eax = 0x????[val][val]
mov edx,eax ;edx = 0x????[val][val]
shl eax,16 ;eax = 0x[val][val]0000
mov ax,dx ;eax = 0x[val][val][val][val]
mov edx,0x00000003 ;edx = 0x00000003
test edi,edx ;Is the start address on a 4 byte boundary?
je .dwordAligned ; yes
.doOne:
stosb ;Do one byte
dec ecx ;Decrease number of bytes left to do
test edi,edx ;Is the address on a 4 byte boundary yet?
jne .doOne ; no, keep going
.dwordAligned:
and edx,ecx ;edx = bytes remaining after dwords done
shr ecx,2 ;ecx = number of dwords to set (must be 3 or more)
rep stosd ;Set the dwords
or ecx,edx ;ecx = bytes remaining
pop edx
je .done ;Everything done if ECX = 0
.doBytes:
rep stosb ;Set the bytes
.done:
popfd
mov eax,edi ;Return value = address + count
pop ecx
pop edi
ret
Completely untested of course...
Cheers,
Brendan
Posted: Fri Jul 13, 2007 5:51 am
by XCHG
Brendan,
Of course you can avoid using EBP but what if you are coding a recursive procedure/function? you wouldn't like to calculate the new offsets for each of the parameters when your procedure is being called over and over again by itself
Posted: Fri Jul 13, 2007 5:57 am
by hailstorm
The reason why I like the "push ebp, mov ebp, esp" method, is because of it's simplicity and simplicity it implies. You always know where your parameters are on the stack, without the hassle of calculating offsets based on the number of registers you pushed on the stack.