Get value from the stack

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
User avatar
d4n1l0d
Member
Member
Posts: 42
Joined: Sat Dec 02, 2006 4:12 pm

Get value from the stack

Post 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?
User avatar
XCHG
Member
Member
Posts: 416
Joined: Sat Nov 25, 2006 3:55 am
Location: Wisconsin
Contact:

Post 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.
On the field with sword and shield amidst the din of dying of men's wails. War is waged and the battle will rage until only the righteous prevails.
User avatar
d4n1l0d
Member
Member
Posts: 42
Joined: Sat Dec 02, 2006 4:12 pm

Post 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 )
Attachments
now its ok
now its ok
this.png (14.05 KiB) Viewed 1456 times
Last edited by d4n1l0d on Thu Jul 12, 2007 1:59 pm, edited 5 times in total.
User avatar
hailstorm
Member
Member
Posts: 110
Joined: Wed Nov 02, 2005 12:00 am
Location: The Netherlands

Post by hailstorm »

Yep! That's it! :wink:
User avatar
d4n1l0d
Member
Member
Posts: 42
Joined: Sat Dec 02, 2006 4:12 pm

Post by d4n1l0d »

In the case that I had byte or word values, would be ok to do this?? :

Code: Select all

 test (word value1, byte value2) 

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		;
User avatar
XCHG
Member
Member
Posts: 416
Joined: Sat Nov 25, 2006 3:55 am
Location: Wisconsin
Contact:

Post 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).
On the field with sword and shield amidst the din of dying of men's wails. War is waged and the battle will rage until only the righteous prevails.
User avatar
d4n1l0d
Member
Member
Posts: 42
Joined: Sat Dec 02, 2006 4:12 pm

Post 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
User avatar
XCHG
Member
Member
Posts: 416
Joined: Sat Nov 25, 2006 3:55 am
Location: Wisconsin
Contact:

Post 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
; ——————————————————————————————————————————————————
On the field with sword and shield amidst the din of dying of men's wails. War is waged and the battle will rage until only the righteous prevails.
User avatar
d4n1l0d
Member
Member
Posts: 42
Joined: Sat Dec 02, 2006 4:12 pm

Post 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?
User avatar
XCHG
Member
Member
Posts: 416
Joined: Sat Nov 25, 2006 3:55 am
Location: Wisconsin
Contact:

Post 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.
On the field with sword and shield amidst the din of dying of men's wails. War is waged and the battle will rage until only the righteous prevails.
User avatar
d4n1l0d
Member
Member
Posts: 42
Joined: Sat Dec 02, 2006 4:12 pm

Post by d4n1l0d »

in mine memsetb I was planning to return the address + count ( that's way I return the value of EDI ) :D
and, thank you very much for helping me
User avatar
hailstorm
Member
Member
Posts: 110
Joined: Wed Nov 02, 2005 12:00 am
Location: The Netherlands

Post 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...
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Post 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
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
User avatar
XCHG
Member
Member
Posts: 416
Joined: Sat Nov 25, 2006 3:55 am
Location: Wisconsin
Contact:

Post 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 :roll:
On the field with sword and shield amidst the din of dying of men's wails. War is waged and the battle will rage until only the righteous prevails.
User avatar
hailstorm
Member
Member
Posts: 110
Joined: Wed Nov 02, 2005 12:00 am
Location: The Netherlands

Post 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.
Post Reply