pazdinho_
pazdinho_

Reputation: 45

How to turn off position-independent code compilation in Microsoft Visual Studio C/C++ compiler?

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!

Upvotes: 2

Views: 480

Answers (1)

Margaret Bloom
Margaret Bloom

Reputation: 44126

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.

Upvotes: 3

Related Questions