The MASM Forum Archive 2004 to 2012

General Forums => The Campus => Topic started by: ecube on July 19, 2010, 01:16:02 AM

Title: the stack
Post by: ecube on July 19, 2010, 01:16:02 AM
I been playing around with the stack a lot lately yet i'm having troubles understanding something take for example


.data?
mybuff db 256 dup(?)
.code
start:
invoke func1,33,0,0
invoke ExitProcess,0

func1 proc p1,p2,p3
push esp
call func2
ret
func1 endp

func2 proc p1,p2,p3
invoke dwtoa,p1,addr mybuff             <-------------------gives some random number definitely not 33
invoke MessageBox,0,addr mybuff,NULL,MB_OK
ret
func2 endp
Title: Re: the stack
Post by: sinsi on July 19, 2010, 01:31:15 AM
'push esp' will push the value of esp, which points to the return address, not the parameter list for func1 (I assume that's what you want?).
Title: Re: the stack
Post by: ecube on July 19, 2010, 01:32:09 AM
thanks and yeah how I push the param list of the first function, without doing each 1 individually?
Title: Re: the stack
Post by: sinsi on July 19, 2010, 01:35:30 AM
Just use invoke as normal

func1 proc p1,p2,p3
  invoke func2,p1,p2,p3
  ret
func1 endp

Title: Re: the stack
Post by: ecube on July 19, 2010, 01:36:03 AM
that's not what i'm after :\ I want to push the whole param list without doing each one individually
Title: Re: the stack
Post by: sinsi on July 19, 2010, 01:42:36 AM

func1 proc p1,p2,p3
  jmp func2
func1 endp

Not sure what you are after...you want to reuse the params as they are on the stack without pushing them again?
Title: Re: the stack
Post by: MichaelW on July 19, 2010, 01:50:47 AM
If you need to replicate the parameters on the stack I can see multiple methods, but none better than pushing them individually.
Title: Re: the stack
Post by: KeepingRealBusy on July 19, 2010, 01:56:42 AM
You can't depend on the stack parameters being the same on return as they were at the call.  People have been known to load a parameter and save a register on top of that parameter (see the laboratory). In other cases, the parameter is used as a work value (a count), so  always push them again.
Title: Re: the stack
Post by: ecube on July 19, 2010, 02:17:20 AM
what if you dont know the exact count for instance? just keep pushin? heh
Title: Re: the stack
Post by: KeepingRealBusy on July 19, 2010, 02:35:12 AM
I honestly don't understand your question. Could you rephrase it with an example?

Dave.
Title: Re: the stack
Post by: ecube on July 19, 2010, 02:38:17 AM
my question is very simple how can you pass on the entire stack to another function, without pushing individual parameters,  or is there a way to get the count of parameters from the stack? without knowing ahead of time it takes 5 params for instance.
Title: Re: the stack
Post by: dedndave on July 19, 2010, 03:09:42 AM
not without saving ESP in a variable ahead of time or something

you might try something like this

RetAddr dw ?

func1 proc p1,p2,p3
pop RetAddr
call func2
jmp dword ptr RetAddr
func1 endp

func2 proc p1,p2,p3
invoke dwtoa,p1,addr mybuff             <-------------------gives some random number definitely not 33
invoke MessageBox,0,addr mybuff,NULL,MB_OK
ret
func2 endp


another approach might be to make room on the stack before the call to func1
then, you could put the return address there and run func2 from func1
Title: Re: the stack
Post by: hutch-- on July 19, 2010, 03:11:19 AM
Cube,

You can pass the address of the first and calculate the rest in the called proc but a simple set of pushes is usually more efficient.  coded up a test piece years ago to test this ands the MOV MEM and adjust ESP was a lot slower than the pushes. PUSH POP are generally optimised in the procressor.

You could rewrite the proc to take one pointer to a structure in EAX but its a different animal to what you are after.
Title: Re: the stack
Post by: ecube on July 19, 2010, 03:28:31 AM

RetAddr dd ?

func1 proc p1,p2,p3
pop RetAddr
call func2
ret
func1 endp

func2 proc p1,p2,p3
invoke dwtoa,p1,addr mybuff             <-------------------gives some random number definitely not 33
invoke MessageBox,0,addr mybuff,NULL,MB_OK
ret
func2 endp


this works but what's weird is parameter p1 from func1 shows up as p2 in func2, and p2 in p3

also does this do any damage, even though it's calling a function with only 3 params?

push [ebp+56]
push [ebp+52]
push [ebp+48]
push [ebp+44]
push [ebp+40]
push [ebp+36]
push [ebp+32]
push [ebp+28]
push [ebp+24]
push [ebp+20]
push [ebp+16]
push [ebp+12]
push [ebp+8]
call func2
Title: Re: the stack
Post by: KeepingRealBusy on July 19, 2010, 03:31:50 AM
If one function called a second function with 3 parameters, and the second called the third function with the same 3 parameters, and the parameters were not needed anymore by the second function (maybe just the return value in eax), then the second function could pop the return address from the stack into a fixed memory DWORD, then call the third function (the parameters are still on the stack), then push the fixed memory DWORD to restore the return address. The second function would have to maintain the stack frame itself and do a ret 0 and not try to adjust the stack for the 3 parameters, or the third function would have to maintain the stack frame itself and not adjust the stack for the 3 parameters, or finally, the second function could adjust adjust the stack upon return from the third function with lea esp,[esp+12] to reinstate the 3 parameters that the third function removed before the second function pushed the fixed DWORD back on the stack.

Messy, fail prone, but doable. MASM lets you get into as much trouble as you want.

Dave.
Title: Re: the stack
Post by: ecube on July 19, 2010, 03:33:01 AM
Quote from: hutch-- on July 19, 2010, 03:11:19 AM
Cube,

You can pass the address of the first and calculate the rest in the called proc

can you give simple example please
Title: Re: the stack
Post by: KeepingRealBusy on July 19, 2010, 03:34:23 AM
Let me correct my first reply: "lea esp,[esp-12]".

Dave.
Title: Re: the stack
Post by: ecube on July 19, 2010, 03:53:21 AM
lea esp,[esp+4] before the call fixed it, I went with daves method, thanks guys.
Title: Re: the stack
Post by: jj2007 on July 19, 2010, 08:02:10 AM
Quote from: KeepingRealBusy on July 19, 2010, 03:31:50 AM
Messy, fail prone, but doable. MASM lets you get into as much trouble as you want.
Yes :green

include \masm32\include\masm32rt.inc

MyTest PROTO: DWORD, :DWORD
MyFake PROTO: DWORD, :DWORD

.code
AppName db "Masm32 is great!", 0
Hello db "A message:", 0
start: mov ebx, esp
invoke MyTest, offset AppName, addr Hello
invoke MyFake, offset AppName, addr Hello
.if ebx!=esp
MsgBox 0, "The stack is not correct", "Hi", MB_OK
.endif
exit

MyFake proc arg1:DWORD, arg2:DWORD
  MsgBox 0, arg1, "Now we are curious, aren't we?", MB_OK
  jmp TheTrick
MyFake endp

MyTest proc arg1:DWORD, arg2:DWORD
LOCAL lv1, lv2, locbuf[260]:BYTE
TheTrick::
  MsgBox 0, arg1, arg2, MB_OK
  ret
MyTest endp

end start
Title: Re: the stack
Post by: dedndave on July 19, 2010, 12:35:19 PM
well - it may help to simply think in terms of "what should the stack look like when we get to func2"
on the stack, you want:
1) p3
2) p2
3) p1
4) return address

if #4 is the return address inside func1, then the previous methods will work
but, if there is no code in func1 after the transfer to func2, there is an even simpler method
func1 proc p1,p2,p3
;presumably some code here
jmp func2
func1 endp

func2 proc p1,p2,p3
invoke dwtoa,p1,addr mybuff
invoke MessageBox,0,addr mybuff,NULL,MB_OK
ret
func2 endp


or even simpler...
func1 proc p1,p2,p3
;presumably some code here
func1 endp

func2 proc p1,p2,p3
invoke dwtoa,p1,addr mybuff
invoke MessageBox,0,addr mybuff,NULL,MB_OK
ret
func2 endp

which let's execution "fall through" to func2

with older assemblers, we could nest procedures
that seems to be a little trickier with newer masm versions - haven't played with it much

at any rate, it may help to turn off the default prologue and epilogue when playing with these kind of tricks
the mysterious p2 to p1 shift probably occured because the prologue pushes EBP
        OPTION  PROLOGUE:NONE
        OPTION  EPILOGUE:NONE

;E^Cube stuff here

        OPTION  PROLOGUE:PrologueDef
        OPTION  EPILOGUE:EpilogueDef
Title: Re: the stack
Post by: sinsi on July 19, 2010, 12:43:42 PM
Quote from: dedndave on July 19, 2010, 12:35:19 PM
func1 proc p1,p2,p3
;presumably some code here
jmp func2
func1 endp


I didn't have the nice comment but at least my code was indented  :bg
Quote from: sinsi on July 19, 2010, 01:42:36 AM

func1 proc p1,p2,p3
  jmp func2
func1 endp
Title: Re: the stack
Post by: dedndave on July 19, 2010, 12:47:30 PM
lol - yah - it can even be easier than that   :lol
func1:
func2 proc p1,p2,p3
invoke dwtoa,p1,addr mybuff
invoke MessageBox,0,addr mybuff,NULL,MB_OK
ret
func2 endp

or ...
func1   TEXTEQU  <func2>
Title: Re: the stack
Post by: jj2007 on July 19, 2010, 01:11:38 PM
Quote from: dedndave on July 19, 2010, 12:35:19 PM
but, if there is no code in func1 after the transfer to func2, there is an even simpler method
func1 proc p1,p2,p3
;presumably some code here
jmp func2
func1 endp

func2 proc p1,p2,p3
invoke dwtoa,p1,addr mybuff
invoke MessageBox,0,addr mybuff,NULL,MB_OK
ret
func2 endp


or even simpler...
func1 proc p1,p2,p3
;presumably some code here
func1 endp

func2 proc p1,p2,p3
invoke dwtoa,p1,addr mybuff
invoke MessageBox,0,addr mybuff,NULL,MB_OK
ret
func2 endp

which let's execution "fall through" to func2

Dave, you are up for surprises :green
(take my example to find out - Olly is your friend)
Title: Re: the stack
Post by: dedndave on July 19, 2010, 01:25:18 PM
no surprises here Jochen - lol
        OPTION  PROLOGUE:NONE
        OPTION  EPILOGUE:NONE

;E^Cube stuff here

        OPTION  PROLOGUE:PrologueDef
        OPTION  EPILOGUE:EpilogueDef


in fact, i considered just using this at the beginning of my programs
        OPTION  PROLOGUE:NONE
        OPTION  EPILOGUE:NONE

because i always prefer to write that way
but, that would break compatibility with everyone in the forum   :P

back, before the advent of the internet, i used to use some abbreviations, too
i couldn't use my old abbreviations for the same reason - noone else would get it - lol
BPT     EQU     Byte Ptr
WPT     EQU     Word Ptr
DPT     EQU     Dword Ptr
OFS     EQU     Offset
SHO     EQU     Short

        mov BPT WordLabel,10h
        mov     ax,OFS ByteLabel
        jmp SHO BranchLabel
Title: Re: the stack
Post by: jj2007 on July 19, 2010, 01:35:59 PM
Quote from: dedndave on July 19, 2010, 01:25:18 PM
no surprises here Jochen - lol

Dave,

fallthrough works fine if there are no local variables. Otherwise there is trouble, because part II of the framing, the sub esp, nnn is being done twice. Here is an example that works with local variables.

include \masm32\include\masm32rt.inc

MyTest PROTO: DWORD, :DWORD
MyFake PROTO: DWORD, :DWORD

.code
AppName db "Masm32 is great!", 0

start: mov ebx, esp
; int 3
invoke MyTest, offset AppName, chr$("The real thing:")
invoke MyFake, offset AppName, chr$("The fake:")
.if ebx!=esp
MsgBox 0, "The stack is not correct", "Hi", MB_OK
.endif
exit

MyFake proc arg1:DWORD, arg2:DWORD
LOCAL lv1, LocRetVal, locbuf[260]:BYTE
  MsgBox 0, arg1, "Now we are curious, aren't we?", MB_YESNOCANCEL
  mov LocRetVal, eax
  jmp TheTrick
MyFake endp

MyTest proc arg1:DWORD, arg2:DWORD
LOCAL lv1, WhatEver, locbuf[260]:BYTE
  mov WhatEver, IDNO
TheTrick::
  MsgBox 0, str$(WhatEver), arg2, MB_OK
  ret
MyTest endp

end start
Title: Re: the stack
Post by: dedndave on July 19, 2010, 01:40:11 PM
if i turn off prologues (and epilogues), the locals are literal and visible in the code   :bg