2

I'm wondering if it's possible to turn off this position-independent code thing to change the assembly call instruction from something like

call [rip+0x1234] // this address is relative to the current code position

to something like

call qword ptr[0x12345678] // this address is absolute and doesn't crash the program

The reason for this is that I create a remote thread by calling CreateRemoteThread from WinAPI and I want to be able to call other WinAPI functions inside by just typing normal code, but now as the PIC is on, every time I call any function inside the remote thread I get an access violation and the process crashes. It's also worth mentioning that I know every process has different imported functions addresses, but I'm only calling functions from kernel32.dll which is loaded at the same absolute address in every process as far as I know, so this isn't a problem.

Is there some macro or compiler option for this? I would also like to change that behaviour only for compilation of one file, not all in the solution.

Of course my CPU architecture is amd64 but I thinks that's irrelevant.

Thanks in advance!

Michael Petch
  • 46,082
  • 8
  • 107
  • 198
pazdinho_
  • 43
  • 1
  • 5
  • So your plan is to just copy a function generated by something you don't have full control over directly into another process and hope for the best? – Anders Sep 17 '22 at 09:08
  • @Anders no, my plan is to be able to call `kernel32.dll` functions inside remote thread routine, that's all – pazdinho_ Sep 17 '22 at 09:31
  • 2
    Very unlikely that you can. RIP-relative addressing is more efficient than `[disp32]` so using `call qword ptr [abs address]` would be a pessimization (costing 1 extra byte of code size per such instruction). That's why compilers always use RIP-relative addressing to load or store static data, only using absolute addressing in non-PIE (e.g. on Linux) for putting the address of a symbol into a register. (`mov ecx, OFFSET symbol` is only 5 bytes.) See [Why does this MOVSS instruction use RIP-relative addressing?](https://stackoverflow.com/q/44967075) – Peter Cordes Sep 17 '22 at 09:41
  • 1
    problem not in this (anyway not exist any shared global variables which hold addresses of dll). problem - you dont know how write base independed code in c++. despite this is not too hard. use class. store all function addresses and data which you need in this class. you must not access any global variables. but you can access class/struct members – RbMm Sep 17 '22 at 16:12

1 Answers1

3

If I understood you correctly, this is wrong on at least three levels.

  1. There is no guarantee the new host process is importing the APIs you call. If you call CryptAcquireContext the linker will be sure to add an entry for advapi32.dll and CryptAcquireContext in the PE built, which is the source binary. The target may not have such import in the first place.

  2. call QWORD PTR [target] still uses a 32-bit RIP-relative displacement in MASM syntax, for a symbol name not a literal number. The import address fixed by the loader can very well be above 2GiB (and almost always is). I don't think you can force cl (try with the link option /LARGEADDRESSAWARE:no, but it's a link option, not a compiler one) to not use a RIP-relative address for these calls. But even if you did, that would work for your PE, not for the target PE. Your PE would need to be loaded below the 2GiB (for signed 32-bit absolute [disp32]) while it's almost certain the target PE will not be.

    gcc -fno-pie -mcmodel=large would use indirect calls with 64-bit absolute addresses (mov reg, imm64 / call reg) for everything, not assuming that call targets are within +-2GiB, but MSVC probably doesn't have such an option.

  3. An absolute address needs a relocation. You may be able to force cl/link to emit a PE that is not moveable and avoid the need for relocations but, again, this is for your PE, not the target one.

In short, your assembly code and your binary executable are tied together to create a process where your code will work as expected.
If you migrate to another process you have to make up for the differences and it won't be like writing normal code (including accessing global data).

If you are already writing a DLL the easiest thing is to use SetWindowsHookEx to hook the newly created remote thread with a function in your DLL. Windows will automatically inject your whole DLL into the target process. Use any hook you know will be called at least once (this is easy since you control the code of the remote thread).

Otherwise, you can move the injected code in a DLL and inject a shim/loader that loads said DLL from disk/memory.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Margaret Bloom
  • 41,768
  • 5
  • 78
  • 124
  • I made some edits to clarify your points and correct some details, like that `[disp32]` addressing can only access the low 2GiB of address space, not 4G unless you also used a `67h` address-size prefix. Because it's signed absolute. You should probably have a look to make sure I understood the points you were trying to make and didn't distort things. – Peter Cordes Sep 20 '22 at 06:57
  • @PeterCordes Thanks, I always forget about this sign-extension effect :) – Margaret Bloom Sep 20 '22 at 10:22