Linux下向Wine运行的Windows进程注入代码遇阻求助
问题背景
我在Linux环境下尝试向Wine运行的Windows游戏进程注入原生代码(仅用于学习),而非预编译DLL。通过ptrace附加目标进程,借助系统调用分配内存、写入代码,再用clone创建线程,进而通过pthread_create执行注入代码的方式,对原生Linux进程完全正常,调用Wine进程里libc的puts也能成功,但只要调用Windows API(比如MessageBoxA)或者游戏自身的函数,线程就会挂起。
核心原因:Wine的线程上下文与栈结构不兼容
Wine是API兼容层而非模拟器,但它对Windows线程的实现和Linux pthread存在本质区别:
- 线程上下文差异:Windows线程依赖TIB(线程信息块)存储线程局部数据、错误状态、模块列表等关键信息,Wine会为每个Windows线程维护对应的TIB结构。而用pthread_create创建的是纯Linux线程,没有初始化Wine的Windows线程上下文,调用Windows API时找不到必需的TIB数据,直接导致执行中断或挂起。
- 栈结构差异:Windows线程的栈由Wine按照Windows规范管理,和Linux pthread的栈布局完全不同。Windows x64调用约定虽然要求预留32字节shadow space,但更关键的是Wine的栈需要关联到对应的Windows线程对象,pthread的栈没有这个关联,API调用时无法正确处理栈上的线程相关数据。
解决方法
方法1:调用Wine的CreateThread创建Windows线程(推荐)
不要用pthread_create,而是在注入代码里调用Wine提供的kernel32.dll!CreateThread,让Wine自己创建符合要求的Windows线程。这个函数会自动初始化TIB、调整栈结构,确保Windows API能正常执行。
修正后的注入代码示例:
; 注入的入口代码:调用Wine的CreateThread创建Windows线程执行目标逻辑 push rbp mov rbp, rsp sub rsp, 40 ; 预留32字节shadow space + 8字节参数存储位 ; 构造CreateThread的参数(x64 fastcall约定:前4个参数用RCX/RDX/R8/R9,剩余参数压栈) xor rcx, rcx ; lpThreadAttributes = NULL mov rdx, 0x10000 ; dwStackSize = 0x10000(给线程分配足够栈空间) lea r8, [thread_entry]; lpStartAddress = 线程入口函数地址 xor r9, r9 ; lpParameter = NULL mov qword [rsp+32], 0 ; dwCreationFlags = 0(立即运行线程) mov qword [rsp+40], 0 ; lpThreadId = NULL(不需要线程ID) call <kernel32.dll的CreateThread导出地址> add rsp, 40 pop rbp ret ; 真正执行Windows API调用的线程入口 thread_entry: push rbp mov rbp, rsp sub rsp, 32 ; 符合Windows x64约定的32字节shadow space ; 调用MessageBoxA xor rcx, rcx ; hWnd = NULL lea rdx, [msg_text] ; lpText = 消息内容 lea r8, [msg_caption] ; lpCaption = 标题 mov r9, 0 ; uType = MB_OK call <user32.dll的MessageBoxA导出地址> add rsp, 32 pop rbp ret ; 数据段 msg_text: db 'MessageBoxA called successfully!', 0 msg_caption: db 'Yaaay!', 0
方法2:手动初始化Wine线程上下文(不推荐)
如果一定要基于现有pthread线程执行,可以手动调用Wine内部的线程初始化函数(比如wine_server_create_thread或NtCreateThreadEx的封装),但Wine的内部结构随版本变化很大,需要深入阅读对应版本的源码,找到初始化TIB和线程对象的逻辑,这个方法维护成本极高,不建议采用。
额外注意:栈对齐要求
Windows x64调用约定要求调用call指令前,RSP必须是16字节对齐。原有代码里sub rsp,32之后,需要确认RSP的对齐情况——因为call指令会压入8字节的返回地址,所以调用前RSP必须是16字节对齐,否则会导致未定义行为。
内容的提问来源于stack exchange,提问作者lem0nify

