Author Topic: About the stack  (Read 603 times)

buraks

  • Regular Member
  • *
  • Posts: 2
About the stack
« on: January 26, 2020, 11:00:47 AM »
Hello,

There is a caller in an exe:


   push par1
   push par2
   call myFunction


And myFunction is in a DLL:


myFunction proc param1:dword, param2:dword
   invoke MessageBox........

   pop ebp
   mov eax, 1
   retn
myFunction endp


why do we need the last 3 lines in myFunction instead of just "ret"?
That is:


myFunction proc param1:dword, param2:dword
   invoke MessageBox.........

   ret
myFunction endp



jj2007

  • Member
  • *****
  • Posts: 10087
  • Assembler is fun ;-)
    • MasmBasic
Re: About the stack
« Reply #1 on: January 26, 2020, 02:56:05 PM »
You don't need them, a simple "ret" would do the job:

Code: [Select]
include \masm32\include\masm32rt.inc ; plain Masm32 for the fans of pure assembler

.code
myFunction proc param1:dword, param2:dword
   invoke MessageBox, 0, param1, param2, MB_OK

   ret ; comment out to see the difference

   pop ebp
   mov eax, 1
   retn 2*DWORD
myFunction endp

start:
print str$(esp), " stack", 13, 10
push chr$("Title")
push chr$("Text")
call myFunction
inkey str$(esp), " stack", 13, 10
exit

end start

"ret" is a built-in macro, "retn" is an instruction; to understand what really happens, use a debugger like Olly

AW

  • Member
  • *****
  • Posts: 2563
  • Let's Make ASM Great Again!
Re: About the stack
« Reply #2 on: January 26, 2020, 06:19:51 PM »
AFAIK, RETN means return near and is obsolete because in the flat model all calls are near (or far, you decide).
The Intel manuals from this century do not even mention RETN, they mention RET and it has the opcode C3.

What MASM does is a trick, when it sees RET, it fills up the Epilogue for you.

You can prevent MASM from being a smart ass by prepend this before the procedure(s):

OPTION PROLOGUE:NONE
OPTION EPILOGUE:NONE




jj2007

  • Member
  • *****
  • Posts: 10087
  • Assembler is fun ;-)
    • MasmBasic
Re: About the stack
« Reply #3 on: January 26, 2020, 08:23:39 PM »
Here is a more detailed version showing what happens under the hood:
Code: [Select]
include \masm32\include\masm32rt.inc ; plain Masm32 for the fans of pure assembler

.code
myFunction1 proc param1:dword, param2:dword
   print param1
   print str$(param2), 13, 10

   nops 2
   ret ; the ret macro
myFunction1 endp
  nop

myFunction2 proc param1:dword, param2:dword
   print param1
   print str$(param2), 13, 10

   nops 2
   pop ebp ; the version that UAsm will use for the ret macro
   mov eax, 1
   retn 2*DWORD
myFunction2 endp
  nop

myFunction3 proc param1:dword, param2:dword
   print param1
   print str$(param2), 13, 10

   nops 2
   leave ; the version that MASM will use for the ret macro
   retn 2*DWORD
myFunction3 endp
  nop

start:
  print str$(esp), " stack", 13, 10
  int 3
  invoke myFunction1, chr$("calling "), 1
  invoke myFunction2, chr$("calling "), 2
  invoke myFunction3, chr$("calling "), 3
  inkey str$(esp), " stack", 13, 10
  exit

end start

The nops serve to make the disassembly look clearer. "print" is a Masm32 macro that uses Stdout:
Code: [Select]
Command                             Comments
 push ebp                            ; NewFile.myFunction1(guessed Arg1,Arg2)
 mov ebp, esp
 push dword ptr [ebp+8]              ; ÚArg1 => [Arg1]
 call StdOut                         ; ÀNewFile.StdOut
 push offset ??001F                  ; ÚArg2 = NewFile.??001F
 push dword ptr [ebp+0C]             ; ³Arg1 => [Arg2]
 call dwtoa                          ; ÀNewFile.dwtoa
 push offset ??001F                  ; ÚArg1 = NewFile.??001F
 call StdOut                         ; ÀNewFile.StdOut
 push offset ??0026                  ; ÚArg1 = ASCII CR,LF
 call StdOut                         ; ÀNewFile.StdOut
 nop
 nop
 pop ebp                             ; voilà, this is
 retn 8                              ; the ret MACRO!
 nop                                 ;
 push ebp                            ; NewFile.myFunction2(guessed Arg1,Arg2)
 mov ebp, esp
 push dword ptr [ebp+8]              ; ÚArg1 => [ARG.1]
 call StdOut                         ; ÀNewFile.StdOut
 push offset ??002D                  ; ÚArg2 = NewFile.??002D
 push dword ptr [ebp+0C]             ; ³Arg1 => [ARG.2]
 call dwtoa                          ; ÀNewFile.dwtoa
 push offset ??002D                  ; ÚArg1 = NewFile.??002D
 call StdOut                         ; ÀNewFile.StdOut
 push offset ??0034                  ; ÚArg1 = ASCII CR,LF
 call StdOut                         ; ÀNewFile.StdOut
 nop
 nop
 pop ebp                             ; looks like the
 mov eax, 1                          ; ret MACRO, but you
 retn 8                              ; added a return value
 nop
 push ebp                            ; NewFile.myFunction3(guessed Arg1,Arg2)
 mov ebp, esp
 push dword ptr [ebp+8]              ; ÚArg1 => [ARG.1]
 call StdOut                         ; ÀNewFile.StdOut
 push offset ??003B                  ; ÚArg2 = NewFile.??003B
 push dword ptr [ebp+0C]             ; ³Arg1 => [ARG.2]
 call dwtoa                          ; ÀNewFile.dwtoa
 push offset ??003B                  ; ÚArg1 = NewFile.??003B
 call StdOut                         ; ÀNewFile.StdOut
 push offset ??0042                  ; ÚArg1 = ASCII CR,LF
 call StdOut                         ; ÀNewFile.StdOut
 nop
 nop
 leave                               ; the shortest version
 retn 8
 nop

Normally, procedures ("functions") first create a stack frame using
 push ebp                            ; NewFile.myFunction2(guessed Arg1,Arg2)
 mov ebp, esp

... then do their job, and after that quit using either...
 pop ebp
 retn DWORD*(number of args)

... or ...
 leave
 retn DWORD*(number of args)

It gets slightly more complicated with local variables:
myFunction1 proc arg1, arg2
Local v1, v2, v3

Code: [Select]
  Command                             Comments
  push ebp                            ; NewFile.myFunction1(guessed Arg1,Arg2)
  mov ebp, esp
  add esp, -0C                        ; create space for 3 local DWORDs
...
  mov esp, ebp                        ; the ret MACRO again...
  pop ebp
  retn 8

This would be an equivalent but shorter return:
Code: [Select]
  leave
  retn 8

As you can when comparing the screenshot from the Intel manual to the disassembly, it is the C2 0800 encoding, i.e. near return and pop imm16.

Debuggers like Olly use retn to distinguish the instruction from the ret macro used by MASM and compatible assemblers like UAsm and AsmC.

Likewise, with MASM & friends you can use retn to override the macro creation as in your code:

   pop ebp
   mov eax, 1
   retn

So ret means "insert the macro, e.g. leave plus retn 8", retn means "use only the ret instruction"

AW

  • Member
  • *****
  • Posts: 2563
  • Let's Make ASM Great Again!
Re: About the stack
« Reply #4 on: January 26, 2020, 09:21:43 PM »
Holly crap  :badgrin:, Intel says RET is an instruction but some people insist it is a macro.

Yeah, everything is a macro and we are all Charlie.  :icon_idea:




hutch--

  • Administrator
  • Member
  • ******
  • Posts: 7027
  • Mnemonic Driven API Grinder
    • The MASM32 SDK
Re: About the stack
« Reply #5 on: January 26, 2020, 09:30:44 PM »
 :biggrin:

If in doubt, trust the opcode.  :tongue:
hutch at movsd dot com
http://www.masm32.com    :biggrin:  :skrewy:

jj2007

  • Member
  • *****
  • Posts: 10087
  • Assembler is fun ;-)
    • MasmBasic
Re: About the stack
« Reply #6 on: January 26, 2020, 09:59:29 PM »
Holly crap  :badgrin:, Intel says RET is an instruction but some people insist it is a macro.

The Microsoft Macro Assembler (MASM) inserts one, two, three or more instructions when it sees ret. Same for UAsm and AsmC. Commonly such behaviour is expected from a macro :cool:

In the words of an assembler developer:
RET is what triggers epilogue generation

I am no expert for other assemblers (this is a MASM forum, including the UAsm and AsmC clones), but it seems that FASM does the same (answer by vid):
Quote
there is no "ret" isntruction, i believe you mean "retn". "ret" is pseudo-opcode (macro in FASM) to return

It seems even more confusing in FASM:
Quote
Have you spelled RET as "ret" (i.e. all lowercase)? It is important because the proc macro defines the ret macro but not RET nor any other combination of lower and upper case.

Google "nasm" "ret macro" to see how NASM behaves (messy as well, apparently RET is the macro, ret is the instruction; FASM does it the other way round :rolleyes:).

Another disassembly of a simple MASM ret - quitting from somealgo proc uses esi edi ebx arg1, arg2, arg3:
Code: [Select]
pop ebx
pop edi
pop esi
mov esp, ebp
pop ebp
retn 0C

The conclusion:
- use ret (the macro) to let the assembler (MASM, UAsm, AsmC) decide which instructions are needed to quit the proc
- use retn (the instruction) if you want to do it manually, and you know what you are doing.

AW

  • Member
  • *****
  • Posts: 2563
  • Let's Make ASM Great Again!
Re: About the stack
« Reply #7 on: January 26, 2020, 10:39:14 PM »
The only conclusions are:
1) RETN is not an Intel instruction. It is a clue for some  debuggers and assemblers to insert opcode C3 straight away.
2) RET does not trigger anything, it is an Intel Instruction. Assemblers watch it to insert the Epilogue, in the same sense that you watch the Traffic Lights to move on when they turn green.

Anyway, I think you got the idea by now.

felipe

  • Member
  • *****
  • Posts: 1265
  • Eagles are just great!
Re: About the stack
« Reply #8 on: January 27, 2020, 05:22:05 AM »
I found this a little weird. I have always used "ret" and i have review some code that i wrote in 32 and 64 bits, that uses it, with dissasemblers and debuggers just now and i didn't see any extra line added to my code, even if i don't used "option epilogue:none". So any idea on this? I have always used masm (masm, ml, ml64).  :icon_idea:
Felipe.

daydreamer

  • Member
  • *****
  • Posts: 1146
  • I also want a stargate
Re: About the stack
« Reply #9 on: January 27, 2020, 05:46:04 AM »
if you use qeditor,there is lots of old windows help,in help meny,from the simplest macro usage to the advanced macro making,even mnemonic/opcodes dated for older cpus,but mostly still in use
Quote from Flashdance
Nick  :  When you give up your dream, you die
*wears a flameproof asbestos suit*
Gone serverside programming p:  :D
I love assembly,because its legal to write
princess:lea eax,luke
:)

jj2007

  • Member
  • *****
  • Posts: 10087
  • Assembler is fun ;-)
    • MasmBasic
Re: About the stack
« Reply #10 on: January 27, 2020, 06:17:07 AM »
i didn't see any extra line added to my code

Post an example, Felipe.

felipe

  • Member
  • *****
  • Posts: 1265
  • Eagles are just great!
Re: About the stack
« Reply #11 on: January 27, 2020, 06:58:03 AM »
As an example you can check this lousy program (just to see the "ret" in action in some procedures in the program, ignore the errors mentioned in the post, i mean that's another topic  :rolleyes:):
http://masm32.com/board/index.php?topic=6235.0

Checking the code in the post, with the dissasembly with ida or using a debugger like x32dbg will show you the same code pertaining to the "ret" instruction.  :icon_idea:

I don't want to put images of that here, you can try if you want and it's possible to you.

I have olly too, but i have not tried with it yet to see how this "rets" show up. I will do it soon as i can.  :thup:
Felipe.

caballero

  • Member
  • *****
  • Posts: 1335
  • Matrix - Noah
    • abre ojos ensamblador
Re: About the stack
« Reply #12 on: January 27, 2020, 07:34:25 AM »
I have not done the test. Maybe all of this go from here. If I'm not wrong, you can define a macro with the name of an instruction that replaces it. In the case of "RET" this can be useful to exit from procedures using "PROC", for example to exit with arguments, etc. Perhaps the clean "RET" can be in a procedure of the type
Code: [Select]
myproc:
 ...
ret
The logic of the error is hidden among the most unexpected lines of the program

felipe

  • Member
  • *****
  • Posts: 1265
  • Eagles are just great!
Re: About the stack
« Reply #13 on: January 27, 2020, 08:05:16 AM »
I have done the same with olly and nothing is changed. As you can see in the link i posted above, as example this piece of code:

Code: [Select]
send_messag1    proc                                                    ; Makes the first message box.
                push    MB_OKCANCEL
                lea     ebx,titl
                push    ebx
                lea     ebx,content1
                push    ebx
                push    0
                call    MessageBox
                mov     value_retorn,eax                                ; We store here the button pushed.
                ret
send_messag1    endp

Looks in x32dbg as this:
Quote
00401029 | 6A 01                         | push 1                                                  |
0040102B | 8D 1D 00 30 40 00             | lea ebx,dword ptr ds:[403000]                           | 00403000:"System message"
00401031 | 53                            | push ebx                                                |
00401032 | 8D 1D 0F 30 40 00             | lea ebx,dword ptr ds:[40300F]                           | 0040300F:"Log out now?"
00401038 | 53                            | push ebx                                                |
00401039 | 6A 00                         | push 0                                                  |
0040103B | E8 5E 00 00 00                | call <logout.MessageBoxA>                               |
00401040 | A3 6C 30 40 00                | mov dword ptr ds:[40306C],eax                           |
00401045 | C3                            | ret                                                     |

And as i said, with ida and olly i see the same, there are no lines aggregated to the "original" code. So any ideas?  :icon_idea:
Felipe.

felipe

  • Member
  • *****
  • Posts: 1265
  • Eagles are just great!
Re: About the stack
« Reply #14 on: January 27, 2020, 08:10:23 AM »
Oh i think i got it now, probably this "masm" behavior (don't care about other assemblers for now) of adding code when you use "ret" only happens when you use the "proc" directive, passing parameters with it, like in this code:

Code: [Select]
myFunction1 proc param1:dword, param2:dword

Correct me if i'm wrong or ignore me if i'm right... :tongue:
Felipe.