Don’t trust the return address


Sometimes people ask, "So I know how to get my return address [use the _ReturnAddress() intrinsic]; how do I figure out what DLL that return address belongs to?"

Beware.

Even if you figure out which DLL the return address belongs to [use Get­Module­Handle­Ex(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS)], that doesn't mean that that is actually the DLL that called you.

A common trick is to search through a "trusted" DLL for some code bytes that coincidentally match ones you (the attacker) want to execute. This can be something as simple as a "retd" instruction, which are quite abundant. The attacker then builds a stack frame that looks like this, for, say, a function that takes two parameters.

trusted_retd
hacked parameter 1
hacked parameter 2
hacker_code_addr

After building this stack frame, the attacker then jumps to the start of the function being attacked.

The function being attacked looks at the return address and sees trusted_retd, which resides in a trusted DLL. It then foolishly trusts the caller and allows some unsafe operation to occur, using hacked parameters 1 and 2. The function being attacked then does a "retd 8" to return and clean the parameters. This transfers control to the trusted_retd, which performs a simple retd, which now gives control to the hacker_code_addr, and the hacker can use the result to continue his nefarious work.

This is why you should be concerned if somebody says, "This code verifies that its caller is trusted..." How do they know who the caller really is?

Comments (17)
  1. ep says:

    So is there a way to verify that my caller is trusted and allows some operations?

  2. Peter Torr says:

    Well, in .NET you have IPermission.Demand() to see if all your callers have the right permissions, or SecurityAction.LinkDemand if you just want to check your immediate caller (which is generally a really bad idea — avoid using LinkDemands to enforce security; they’re better suited to improving API usability).

  3. Phaeron says:

    There are other reasons why the return address shouldn’t be used to determine the caller: the source may be a dynamically generated code fragment, and the call to the DLL function may be a tail call (jmp dword ptr [__imp__DefWindowProc@16]).

    Incidentally, what is the "sanctioned" method of emulating GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS) on pre-XP platforms? The only way I know of is to VirtualQuery() the address and case the AllocationBase to HMODULE.

  4. Raymond Chen says:

    zz: It looks like they focused on my phrase "You dummy". I didn’t mean it to be as insulting to the user as it turned out. I should have written "Psst" instead, but what’s done is done.

  5. Steve Sheppard says:

    No, you did the right thing. You wrote what you meant, they CHOSE to be insulted by it. Don’t play the PC game by trying to guess what they will insulted by because the answer is eveything. Anyone who reads your blog regularly knows what you mean and to hell with idiots like that guy.

    Keep up the good work!

  6. Peter Lund/firefly@diku.dk says:

    why would anyone try to get the return address?

  7. Raymond Chen says:

    Because they want to do things "only if their caller is trusted".

  8. Peter Lund/firefly@diku.dk says:

    But, but… that *really* makes no sense :)

    Reminds me of the Poly/Turbo/Borland Pascal programmers I’ve met who wanted an "exit2" pseudo procedure that would leave not just one but /two/ procedures.

    (exit is like return in C without an argument, Pascal can have nested functions unlike C (but like gcc))

    I thought at least *somebody* had had a real need for it?

  9. Recently on Raymond Chens blog he had a post about not trusting return addresses. Specifically to not use the _ReturnAddress() intrinsic and GetModuleHandleEx to figure out if the caller is trusted. I had to try and come up with a…

  10. Because there sometimes isn’t enough information to determine what the "right" order is.

  11. Counterintuitive results from the file search.

  12. Thanks to code injection.

  13. No really, you can’t.

Comments are closed.

Skip to main content