https://bugs.winehq.org/show_bug.cgi?id=36863
Anastasius Focht focht@gmx.net changed:
What |Removed |Added ---------------------------------------------------------------------------- Keywords| |obfuscation Status|UNCONFIRMED |NEW CC| |focht@gmx.net Component|directx-d3dx9 |opengl Summary|Mass Effect 3 [Origin] |Mass Effect 3 [Origin] |crash on startup |crash on startup (broken EA | |Origins in-game | |overlay/hook engine | |'igo32.dll' needs | |'opengl32.dll' prelinked < | |2GB address range) Ever confirmed|0 |1
--- Comment #10 from Anastasius Focht focht@gmx.net --- Hello folks,
confirming.
It's a bug in EA Origin's in-game overlay hook engine 'igo32.dll' which is uncovered by differences in user process address space layout Windows vs. Linux.
The hook dll is injected in all child processes, starting from the initial process (origin launcher or game).
It checks the process addresses spaces for loaded third-party dlls, patched entry points (already hooked/hacked) and the like.
A number of 'user32.dll', 'opengl32.dll', 'd3d[8|9|10|11].dll' APIs get hooked. The hook engine seems a bit more flexible than Valve's 'gameoverlayrenderer' when it comes to handling of different kinds of entry points (hotchpatch entry is not required).
It disassembles the entry points to figure out the optimal patch pattern (instruction length) and sets up a trampoline for it.
The first choice is the 5-byte 'jmp' relative instruction (opcode 0xE9) which is limited to a range of 2GB from the jump instruction address. 32-bit Windows applications can get almost 3GB of user mode virtual address space. Windows core dlls will be still mapped below the 2GB boundary for compatibility reasons (also limiting how big any one contiguous block can be).
The final jump distance to 'igo32.dll' is validated if 2GB is exceeded (>0x7FFF0000). The hooker emits an IAT style thunk with an absolute jump in that case and gets it _really_ wrong.
Various 32-bit builtin dlls are mapped to high address range in my 64-bit WINEPREFIX (WoW64):
--- snip -- Modules: Module Address Debug info Name (182 modules) PE 390000- 3a6000 Deferred xinput1_3 PE 3b0000- 3db000 Deferred physxextensions PE 400000- 1b16000 Export masseffect3 PE 2100000- 22e5000 Deferred d3dx9_42 PE 3550000- 40b8000 Deferred eacore PE 7980000- 7992000 Deferred physxloader PE 7ab0000- 7b0d000 Deferred physxcooking PE 7c20000- 7f7a000 Deferred physxcore PE 7f80000- 7fed000 Deferred cudart32_41_4 PE 10000000-1020a000 Export igo32 PE 18000000-1803b000 Deferred binkw32 PE 40000000-400f0000 Export awc ELF 7b800000-7ba62000 Dwarf kernel32<elf> -PE 7b810000-7ba62000 \ kernel32 ELF 7bc00000-7bcee000 Dwarf ntdll<elf> -PE 7bc10000-7bcee000 \ ntdll ELF 7bf00000-7bf04000 Dwarf <wine-loader> ... ELF f630a000-f630e000 Deferred libnvidia-tls.so.331.67 ELF f630e000-f6327000 Deferred userenv<elf> -PE f6310000-f6327000 \ userenv ELF f6327000-f6369000 Dwarf d3d9<elf> -PE f6330000-f6369000 \ d3d9 ELF f63c1000-f63dd000 Deferred wsock32<elf> -PE f63d0000-f63dd000 \ wsock32 ELF f63dd000-f63f5000 Deferred d3dx10_42<elf> -PE f63e0000-f63f5000 \ d3dx10_42 ELF f63f5000-f6468000 Deferred setupapi<elf> -PE f6400000-f6468000 \ setupapi ELF f6468000-f6484000 Deferred dinput8<elf> -PE f6470000-f6484000 \ dinput8 ... ELF f6e3c000-f6f58000 Deferred opengl32<elf> -PE f6e60000-f6f58000 \ opengl32 ELF f6f58000-f70cc000 Dwarf wined3d<elf> -PE f6f70000-f70cc000 \ wined3d ELF f70cc000-f70f3000 Deferred dxgi<elf> -PE f70d0000-f70f3000 \ dxgi ... ELF f7546000-f76fc000 Dwarf libwine.so.1 ELF f76fd000-f771e000 Deferred ld-linux.so.2 ELF f771e000-f771f000 Deferred [vdso].so --- snip ---
The hooker saves 8 bytes from API entry away to another structure and sets up a "reverse trampoline".
Basically it looks like this...
0x1xxxxxxx <hook dll/code> ... trampoline_structXX: 0x6xxxxxxx <old entry opcode save> final_trampolineXX: 0x6xxxxxxx jmp 0x1xxxxxxx ... 0xfxxxxxxx (APIENTRY+0) jmp [<abs>] -> 0xFF,0x25,[32-bit thunk table address] 0xfxxxxxxx (APIENTRY+6) [thunk table, 4-byte ptr -> final trampoline 0x6xxxxxxx] 0xfxxxxxxx (APIENTRY+A) <unchanged original opcodes> ...
The code is broken: only a part(!) of the 32-bit thunk table entry gets written out hence the execution of the absolute (indirect) jump will pick up a garbage address, depending on what partial instruction is present at 'APIENTRY+8'.
Some "genius" tried to be clever by using atomic 'LOCK CMPXCHG8B QWORD PTR DS:[EDI]' batch write, missing out the remainder.
Examples:
--- snip --- F6EF9B5A FF25 609BEFF6 jmp [F6EF9B60] ; thunk entry value: 0x83F00040 F6EF9B60 40 inc eax F6EF9B61 00F0 add al,dh F6EF9B63 83EC 40 sub esp,40 F6EF9B66 E8 65BFF7FF call F6E75AD0 ; __x86.get_pc_thunk.bx --- snip ---
--- snip --- F6EE8B4A FF25 508BEEF6 jmp [F6EE8B50] ; thunk entry value: 0x40EC0040 F6EE8B50 40 inc eax F6EE8B51 00EC add ah,ch F6EE8B53 40 inc eax F6EE8B54 E8 77BFF7FF call F6E64AD0 ; __x86.get_pc_thunk.bx --- snip ---
To avoid the broken "reverse trampoline" code, I prelinked 'opengl32.dll' to a virtual address < 2GB.
It allowed the game to run a bit further - only to run into next hook problem which should be subject to a new bug:
'Direct3DCreate9' -> 'wined3d_caps_gl_ctx_create' -> hooked 'wglCreateContext'
The hook calls Wine 'wglCreateContext' _and_ immediately 'wglGetCurrentDC' and 'wglGetCurrentContext' which fails because the caps GL context is made current _after_ return to 'wined3d_caps_gl_ctx_create'.
Regards