The jmp generates the instructions E9 7B C1 03 00, which is just a jump to the symbol, itself. Is I understand it, it SHOULD generate FF 24 25 7B C1 03 00.
Anyone have any suggestions? Is this a compiler/assembler bug, or is there a way for me to dereference that symbol in my inline asm?
I was trying to use this dinput8 proxy, but it uses the old llvm_asm!() macro, and that is now gone.
Doing what you are trying to do in Rust is new to me so the advice below is suspect.
I believe there should be a load from pDirectInput8Create into a register instruction (load the value stored at pDirectInput8Create) followed by jump indirect. Instead I think you have a jump direct.
My only option might be to fully define each function, and in my proxy functions just call and pass the values along, maybe, but I was hoping I could get away with being a little lazy, here.
What you're doing, i.e., loading the original DLL manually and forwarding calls manually - is not something I'd recommend. Instead, let the loader do the work for you; this is both easier to implement and much safer.
But if you have to, use AT&T syntax. Intel syntax is, uh, broken, I guess? In particular, it seems trying to jump to the address stored at a register or symbol makes the linker completely freak out. I couldn't get it to work no matter what and it seems others couldn't either, so just use AT&T.
Have a bunch of these in one file, like forwarders.rs, that would be generated by an external tool that reads through the DLL's exports and forwards all of them.
.section .drectve... etc will tell the linker to export this, and can be done on both MSVC and GNU this way.
One downside, though; you'll need to copy the original DLL to the same directory as your proxy.
Oh, nice! I didn't know you could use that syntax, here.
You can also use the full path to the original DLL and double escaped backslashes \\\\, with this. At least, you can in C, and I was able to get it to take a full path for two of the functions I need to export, but, 4 of them end up with this linker error
dinput8-46bf9ed8471a41fb.vvrhjiw8uoylgqx.rcgu.o : fatal error LNK1161: invalid export specification and I cannot find much info on it, yet.
Edit:
It's actually when I have more than two of these defined that it has an issue. Weird!
You can also use the full path to the original DLL and double escaped backslashes \\\\, with this. At least, you can in C, and I was able to get it to take a full path for two of the functions I need to export, but, 4 of them end up with this linker error
If I comment out all but two of them, it links just fine. More than that, and it throws that error.
The rest of what I have is just a DLLMain and then a bunch of external crate stuff. I can send that if you want, but you'll need a few crates. Would be weird if it were just this project causing it, though.
I can't reproduce the issue, unfortunately. Though using / instead of \\\\ makes it work for me (otherwise, it would be unable to find the symbols). Can you try that?
I am using the x86_64 MSCV toolchain, and I created a new library project and am having this same issue, if that helps. It also does this with an exe project.
I don't know why it generate such instructions for intel syntax, but it generate the expected instructions on my machine using AT&T syntax. maybe intel syntax is just broken, I don't know:
however, I think this instruction sequence won't work as expected, as the instruction can only encode 32 bit address, but a dll is (almost?) always loaded above 4GB address (except maybe for special system libraries)
since there's no 64 bit displacement instruction encoding except for the special case of RAX, EAX, AX, AL. you can either:
a) load address into RAX (using special instruction) and jmp indirectly
asm!(
// EDIT 2: missing a `$` prefix below
"movabs ${}, %rax",
// EDIT 1: missing parenthesis below
"jmpq *(%rax)",
/*
// just for the record, the original code before the first edit
// will jump to the *same* destination, (purely by accident),
// although it was not what I had in mind originally;
// the observable difference is the value in %rax register
"movabs {}, %rax",
"jmpq *%rax",
*/
sym pDirectInput8Create ,
options(noreturn, att_syntax),
);
or b) use RIP relative addressing (your .data section and .text are unlikely to be more than +/- 2GB apart):
**Creating library C:\Users\Nord\source\Rust\ExceptionHandlerAntiDebugRemover\target\debug\deps\dinput8.dll.lib and object C:\Users\Nord\source\Rust\ExceptionHandlerAntiDebugRemover\target\debug\deps\dinput8.dll.exp
dinput8.49v74ogyii1ljf38.rcgu.o : error LNK2017: 'ADDR32' relocation to '_ZN7dinput819pDirectInput8Create17h247735b1237f12b9E' invalid without /LARGEADDRESSAWARE:NO
LINK : fatal error LNK1165: link failed because of fixup errors
and I made sure I was compiling for 64 bit.
The second example compiles, but the game crashes, so I that's out.
The third suggestion with rip relative addressing seems to work, though! Thank you!
well, that's exactly what I commented about the 32 bit displacement restriction, the linker is saying it can't fixup the place holder (the 00 00 00 00 bytes in the object file) into the actual resolved symbole address (which is a 64 bit address)
I missed out a pair of parenthesis originally, but I edited the post since, in which version did the crash happen?
the 2GB limit is not about the target function you are trying to hook into, rather it's about the distance between the address of the code (in .text section) and the address of the static variable (in .data section). unless your dll size is larger than 2GB, you won't have a problem here.
post edit. I did see the edits. Could it be 32 bit limit? I don't know the limit on the jump instructions very well. dinput8.dll is actually kind far away
The dll I am jumping too is a different dll, by the way, and could be loaded anywhere. That is why I asked about the 2GB limit.
dinput8.dll is 0x305E10000 (that's 5 bytes wide) bytes away in the current launch I have. ASLR is on, on everything.
oopsie, I was missing an $ prefix in the first instruction too, let me edit again.
funny enough, before I edit the post, the code actually should work, although not the same code as what was originally in my mind. just by accident, the two typos canceled each other out.
as I already said, it doesn't matter, since you are using an indirect jmp, the actual address of the target function is resovled at runtime (instead of link time) and stored in the static variable pDirectInput8Create, which has type FARPROC, whatever the it alias to, it should be large enough to encode all jmp targets in the whole address space.
the 2GB limit only applies to the location of the static variable, NOT the value stored in that variable. am I not clear enough on this?
Ah, okay, I see. I was able to get the compiler to put the variable into rax, earlier, and then jmp [rax] and that seemed to crash, as well. That's why I thought maybe some of these instructions could have limitations.
It's weird, though, because that is what the example you edited should output, I think, and that seems to work, now, with the $ prefix added.
I think I might have something weird going on. I am not sure. I have to check, but the nightly compiler I just downloaded earlier today should be okay, but I am having weird things happen, like the link error when using the export syntax for the linker, and now maybe I am getting different output in than what godbolt has. Hmm..
I do think the stub functions are the best solution, as I can then use GetWindowsDirectoryA to get the path to the system directory and loader up dinput8.dll, as @Centri3 pointed out (%WINDIR% in the linker export method sadly doesn't work for this ).