News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests
NB: Posting URL's See here: Posted URL Change

Main Menu

How to use "ret" in a dll?

Started by Paulo, September 07, 2013, 06:07:16 AM

Previous topic - Next topic

Paulo

Hi all

It's probably something stupid I'm doing, but how does one use ret in a dll without
the dll terminating and returning to the app that called it?

For example, I have a dll that needs to wait a certain amount at various places so instead of repeating the code at the various points,
I call a procedure with eax containing the required wait time then return, but the dll exits on the ret.


; ===========================
PausePeriod:
; ===========================

        push eax
        call Sleep
        ret  ; <----- here is the problem



I would have thought that the program counter would be able to store multiple calls and returns.

Ideas anyone?
Thanks.

dedndave

you aren't saving much by not calling Sleep, directly

but, let's say you did want it that way.....

TimeDelay PROC

    INVOKE  Sleep,eax
    ret

TimeDelay ENDP


now to use it....

    mov     eax,10     ;10 mS
    call    TimeDelay


sometimes, an API has several parms to pass - and this type of call is useful
in this case, however, Sleep has only 1 parm to begin with
so, i would just call it inline.....

    INVOKE  Sleep,10

Paulo

Thank you dedndave.
So if I understand you correctly, one can use the ret if the procedure is explicitly declared with PROC
instead of just jumping(calling) to an arbitrary label and returning?

dedndave

in point of fact, you do not need a PROC
and, sometimes it is advantageous to NOT use one

here is an example....
i have a function with LOCAL variables
i would like to access them from a small support function.....

MyFunc PROC

    LOCAL  SomeVar :DWORD

;....some code

    mov     SomeVar,eax

    call    Support

;....some more code

    call    Support

;....still more code

    ret                        ;here, the assembler generates an epilogue

Support:

    mov     eax,SomeVar

;support code

    retn                       ;by using an explicit RETN, the assembler does not generate an epilogue

MyFunc ENDP


the same would apply if you want to access passed parameters or other labels with local scope

otherwise, use a PROC
it helps make the code more legible

dedndave

i might add.....

there is a difference between "jumping" and "calling" a label

if you JMP SomeLabel, the instruction pointer (EIP) is set to the address of SomeLabel and execution continues

if you CALL SomeLabel, the address of the next instruction (after the CALL) is pushed onto the stack
then, the instruction pointer is set to the address of SomeLabel, as with JMP

the difference comes when the processor encounters a RET instruction
the return address is popped off the stack and into EIP, so execution returns to the original stream
that doesn't happen with JMP

Paulo

I understand the jmp and call but the ret problem got me as usually with an exe I just use ret when returning from a call and no problem.
However when I used ret in a dll there was an error.

Given that there are only really two types of ret (retn and retf), the compiler changes the ret to either a retn or retf.
From what I understand of the differences between retn and retf is that it's a throw back to the days of segmented memory where retn
pops the instruction pointer whilst retf pops both the instruction pointer and the code segment but since segments are a thing of the past,
it would make no difference.

dedndave

#6
you can pretty much assume that all 32-bit protected mode RET's are NEAR, for now
later, if you get into more advanced topics, you may see a FAR RET

but, it may not be the type of RET that is throwing you
it may be that the assembler generates an epilogue whenever a RET
instruction is encountered inside a PROC that has a stack frame
the code generated may not be obvious, because all you see is RET in the source - lol

if you have a PROC that has passed parameters, USES, or local variables,
the assembler generates a prologue and epilogue to maintain a stack frame

MyFunc PROC USES EBX ESI EDI lpszString:LPSTR

    LOCAL   SomeVar :DWORD

    mov     edi,lpszString

;
;
;
    mov     SomeVar,edi
;
;
;
    mov     eax,SomeVar
    ret

MyFunc ENDP


the actual code generated by the assembler might look like this....
MyFunc PROC

    push    ebp
    mov     ebp,esp
    sub     esp,4                 ;reserve space on the stack for SomeVar
    push    ebx
    push    esi
    push    edi
;
    mov     edi,[ebp+8]           ;[EBP+8] = stack address of lpszString

;
;
;
    mov     [ebp-4],edi           ;[EBP-4] = stack address of SomeVar
;
;
;
    mov     eax,[ebp-4]
    pop     edi
    pop     esi
    pop     ebx
    leave                         ;discard the local variables - same as MOV ESP,EBP then POP EBP
    retn    4                     ;discard the passed parameter lpszString

MyFunc ENDP


EDIT: corrected that last one...
moved the SUB ESP,4 up a few lines to adjust the stack before registers are pushed   :P

it would be nice if the assembler preserved registers before the PUSH EBP and MOV EBP,ESP
but, i think the code above is what actually happens when you use masm

Paulo

Thanks dedndave, I get the idea.
I will experiment this weekend with different scenarios and see what code is generated.

jj2007

Quote from: Paulo on September 07, 2013, 06:07:16 AMhow does one use ret in a dll without the dll terminating and returning to the app that called it?

Hi Paulo,

Dave covered already the essential points, but for the sake of clarity:

1. There is no difference between a proc and its ret in "main" and "dll" code. Once you loaded the dll, it behaves exactly like "own" code - until you explicitly unload it with FreeLibrary.

2. Inside a proc with arguments, ret is a macro:

include \masm32\include\masm32rt.inc

.code
MyProc proc uses esi onearg:DWORD
  mov esi, onearg
  call Inside
  ret      ; surely this "ret" is a pop esi, leave, retn 4

Inside:
  MsgBox 0, esi, "Hi", MB_OK
  ret      ; but what about this one???
MyProc endp

start:   invoke MyProc, chr$("Hello World")
   exit

end start


Put an int 3 after the MsgBox, launch Olly and see what happens... ;-)

P.S.: Contrary to your expectations, the snippet will not crash (and the reason is the leave). Now assemble the snippet with JWasm and see the difference...

Antariy

One more addition to the Dave's and Jochen's points:

using it such way:

Inside:
  MsgBox 0, esi, "Hi", MB_OK
  retn      ; but what about this one???


(or RETN XX)

it will work correctly :t

hutch--

The only difference between a procedure in an EXE file and a DLL file is with the DLL you must EXPORT the DLL procedure so that it is visible to the calling EXE file. You use a NEAR return "retn" in win32 code as all addresses are NEAR addresses. You can routinely move a procedure in code from an EXE file to a DLL and it will work the same way as long as you EXPORT it so it is visible.

CALL and RET(n) are matched instructions and it is good design to maintain the CALL RET pairing. I have seen folks try and short circuit this with direct jumps on procedure exit but it messes up the CALL RET pairing.

With PROC design, you are better to use a proc with either a stack frame or not in MASM as it tells the assembler where a proc begins and ends. This keeps label names local to the proc as well as local variable names as local to the proc which means you don't have to have an endless set of unique label and variable names. You can of course use CALL RET locally within a procedure if you want that type of code design.

Paulo

Thank you dedndave, jj2007, Antariy and hutch, I have a much better understanding of ret(n) now.