Injecting code into Windows process running under Wine in Linux

607 views Asked by At

I'm trying to inject code into Windows process (a game) running under Wine in Linux (for learning purposes, of course). Not a pre-built DLL, but native code.

On Windows, we can use procedures like VirtualAllocEx and WriteProcessMemory to write code to the virtual memory of a foreign process and then create a thread in it via CreateRemoteThread, thus executing our code in the context of the foreign process. This is a popular approach, which is often used by all sorts of cheats and trainers for games. The popular game hacking program Cheat Engine has its own API that does this trick.

To do the same on Linux, you need a lot more, much more complex manipulations: attach to the process using ptrace, swapping registers and making system calls to allocate memory, write code there, then create a thread using the clone system call that will execute our code. And it will be even better and safer if the thread created by clone creates another thread using pthread_create, and the last one will already execute our code.

In order for a thread created with clone to jump to the desired code, the address of this code is written to the end of the memory allocated for its stack, and the clone system call itself is performed using the syscall instruction, followed by the ret instruction. This combination of instructions is easily found in libc.so.

I found an example in C on Github on how to do it correctly and implemented my own program. I won't include its source code here because it's quite large. Let me just say that it works as expected with native Linux processes. Moreover, I can also inject code that calls Linux routines (for example, puts from libc.so) into the Wine process and see the correct result:

lea rdi, [text]
call <libc.so code vaddr + `puts` offset>
ret

text:
db 'puts called successfully!', 0

But when I try to call any Windows procedure in the thread created by pthread_create (be it any procedure from the game code or, for example, MessageBoxA from user32.dll), the thread hangs:

sub rsp, 32
xor rcx, rcx
lea rdx, [text]
lea r8, [caption]
mov r9, 0
call <user32.dll code vaddr + `MessageBoxA` offset>
add rsp, 32
ret

text:
db 'MessageBoxA called successfully!', 0
caption:
db 'Yaaay!', 0

But why is this happening? After all, Wine is not an emulator, but just an API and system call compatibility layer. Doesn't this mean that the code itself runs natively on the processor, just like the code of any Linux programs, and I should be able to interoperate between them?

I suspect that the problem may be, for example, in the stack incompatibility. I once heard that Wine uses some kind of trick to convert the stack between Windows and Linux, but I don't know what exactly that trick is and I'm too bad to find and understand it in the Wine source code.

Could you explain to me exactly why my idea of calling Windows procedures from a thread created by pthread_create does not work and how can I make it work?

0

There are 0 answers