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.
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
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?
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
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
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.
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
Thanks dedndave, I get the idea.
I will experiment this weekend with different scenarios and see what code is generated.
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 startPut 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...
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
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.
Thank you dedndave, jj2007, Antariy and hutch, I have a much better understanding of ret(n) now.