Faking ASM Return Address?

2019-07-07 19:12发布

问题:

Would it be possible to fake the return address at, ebp + 4.

I'm currently writing a DLL that you would inject into a game, in which it would call game functions to do things, but the functions I call check the return address against the program itself, and if its outside their base it detects it.

So basically is there any way to fake the return address in any way?

It works like this:

if ( (_BYTE *)retaddr - (_BYTE *)unusedPadding >= (unsigned int)&byte_A6132A )
  {
    dword_11E59F8 |= 0x200000u;
    dword_162D06C = 0;
    result = (void (*)())sub_51FEE0(dword_11E59FC, v5, (_BYTE *)retaddr - (_BYTE *)unusedPadding, ebx0, edi0, a1);
  }

回答1:

Better way:

    push returnshere
    push your_second_argument
    push your_first_argument
    push address_of_fragment_in_exe
    jmp  function_you_want_to_call
returnshere:
    ; more code

Where address_of_ret_in_exe is the address of this fragment:

    add esp, 8 ;4 * number of arguments pushed
    ret

This has the advantage of not editing the game binary. I've seen more than one game that checksummed itself so if you edited it, even in slack space, you're in trouble. If they went through so much trouble as to verify calls come from the game binary, than they likely have defenses against the game binary from being edited. Just be glad they don't trace the call graph.



回答2:

You mean so on entry to the called function, the return address at [esp] is not the actual call site?

You can emulate call by pushing whatever you want and then jumping. Of course, the function you call this way will return to the return address you give it. There would also be a significant perf penalty for mismatched call/ret, because you'll break the CPU's return-address predictor stack.


Can you put some trampoline functions at an acceptable address range, and call through them? Although that's really inconvenient in a 32bit ABI which passes args on the stack. I guess you'd have to pass an extra arg to the trampoline function that it could use to stash the return address, instead of copying all the args.

So the trampoline could be something like:

mov   [esp+20], esi      ; save esi in a dummy arg slot
mov   esi, [esp]         ; save the orig return address in a call-preserved reg
add   esp, 4             ; call with the original args
call  lib_function
push  esi                ; restore the orig return address
mov   esi, [esp+20]      ; and restore esi
ret                      ; return to orig return address

That's not wonderful, and takes a lot of code for something that needs to be duplicated for every library functions. (And it doesn't make a stack frame, so might hurt debugging / exception handling?) For functions without many args it might be shorter to do something like

push   [esp+8]         ; 2nd arg
push   [esp+8]         ; 1st arg
call  lib_function
add    esp, 8
ret

Using indirect calls would let you use the same trampoline for multiple functions, but at the cost of branch mispredicts if there isn't a simple short pattern.

And of course none of these trampolines can work unless you can stick them in memory at an address the library will accept calls from.