[Bug 36863] Mass Effect 3 [Origin] crash on startup (broken EA Origins in-game overlay/hook engine 'igo32.dll' needs 'opengl32.dll' prelinked < 2GB address range)

wine-bugs at winehq.org wine-bugs at winehq.org
Wed Jul 9 17:58:26 CDT 2014


https://bugs.winehq.org/show_bug.cgi?id=36863

Anastasius Focht <focht at gmx.net> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
           Keywords|                            |obfuscation
             Status|UNCONFIRMED                 |NEW
                 CC|                            |focht at 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 at 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

-- 
Do not reply to this email, post in Bugzilla using the
above URL to reply.
You are receiving this mail because:
You are watching all bug changes.



More information about the wine-bugs mailing list