Reputation: 45
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
Reputation: 44126
If I understood you correctly, this is wrong on at least three levels.
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.
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.
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