[Bug 12948] zmud does not run under Ubuntu 8.04 (...turn off system debuggers)

wine-bugs at winehq.org wine-bugs at winehq.org
Sun Jul 27 09:01:29 CDT 2008


http://bugs.winehq.org/show_bug.cgi?id=12948





--- Comment #7 from Anastasius Focht <focht at gmx.net>  2008-07-27 09:01:25 ---
Hello,

I found the problem ... prepare for journey into debuggers and race conditions
;-)
In short: it's a bug in the software protection code which the application is
bundled with - not fixable in Wine.

Prerequisite:

- enable wineserver tracing which slows it down sufficiently to observe and log
the behaviour
- "pre-start" wineserver with a console program (cmd.exe) just to let it sit
and wait

Following are relevant process/thread ids to follow snippets:

--- snip ---
process  tid      prio (all id:s are in hex)
..
00000017 
        0000001b    2    'Zmud.exe' (father, acting as debugger)
        00000018    1    'Zmud.exe' (father, acting as debugger)
00000019 
        0000001a    1    'Zmud.exe' (child, debuggee)
..
--- snip ---

The father - which will act as debugger later - creates the child process.
The child is created as "suspended" (hence the CREATE_SUSPENDED flags):

--- snip ---
0018: new_process( inherit_all=1, create_flags=00000004, socket_fd=16,
exe_file=0x44, hstdin=0x4, hstdout=0x8, hstderr=0xc, process_access=001f0fff,
process_attr=00000000, thread_access=001f03ff, thread_attr=00000000,
info={AllocationSize=1000,Size=5e4,Flags=0,DebugFlags=0,ConsoleHandle=(nil),ConsoleFlags=0,hStdInput=0x4,hStdOutput=0x8,hStdError=0xc,CurrentDirectory.Handle=(nil),dwX=0,dwY=0,dwXSize=0,dwYSize=0,dwXCountChars=0,dwYCountChars=0,dwFillAttribute=0,dwFlags=0,wShowWindow=1,CurrentDirectory.DosPath=L"C:\\Program
Files\\zMUD\\",DllPath=L"C:\\Program
Files\\zMUD;.;C:\\windows\\system32;C:\\windows\\system;C:\\windows;C:\\windows\\system32;C:\\windows",ImagePathName=L"C:\\Program
Files\\zMUD\\Zmud.exe",CommandLine=L"./Zmud.exe",WindowTitle=L"",Desktop=L"",ShellInfo=L"",RuntimeInfo=L""}, 
...
0018: new_process() = 0 { info=0x48, pid=0019, phandle=0x4c, tid=001a,
thandle=0x50 }
--- snip ---

The main thread of the child gets created and process init is done:

--- snip ---
001a: init_thread( unix_pid=17405, unix_tid=17405, debug_level=0,
teb=0x7ffd8000, peb=0x7ffdf000, entry=(nil), ldt_copy=0x601239e0, reply_fd=5,
wait_fd=7 )
001a: init_thread() = 0 { pid=0019, tid=001a, info_size=7598,
server_start=1c8efe01f98c966 (-3.6957770), version=341 }
...
001a: init_process_done( module=0x400000, entry=0x94ef73, gui=1 )
0018: *wakeup* signaled=0 cookie=0x16dd154
001a: *sent signal* signal=10
001a: init_process_done() = 0
--- snip ---

After the create process call returns, the father rewrites entry point in the
child with an 0xEB, 0xFE opcode sequence which is basically a jump to itself.
Remember: PE entry point is not executed yet due to suspended creation.
This opcode sequence will let the child enter an endless loop when the main
thread of the child process is resumed and the location is hit.

--- snip ---
0018: get_new_process_info( info=0x48 )
0018: get_new_process_info() = 0 { success=1, exit_code=259 }
..
0018: read_process_memory( handle=0x4c, addr=0x94ef73 )
..
0018: write_process_memory( handle=0x4c, addr=0x94ef73, data={eb,fe} )
..
0018: resume_thread( handle=0x50 )
0018: resume_thread() = 0 { count=1 }
--- snip ---

The father continuously suspends child's main thread, fetching thread context
and resumes it again until the EIP of rewritten code is reached:

--- snip ---
0018: suspend_thread( handle=0x50 )
001a: *sent signal* signal=10
0018: suspend_thread() = 0 { count=0 }
0018: get_thread_context( handle=0x50, flags=00010001, suspend=0 )
0018: get_thread_context() = PENDING { self=0, context={} }
001a: set_thread_context( 
..
001a: set_thread_context() = 0 { self=1 }
0018: suspend_thread( handle=0x50 )
0018: suspend_thread() = 0 { count=1 }
0018: get_thread_context( handle=0x50, flags=00010001, suspend=0 )
0018: get_thread_context() = 0 { ... }
001a: select( flags=4, cookie=0x7ffdb7b4, signal=(nil), prev_apc=(nil),
timeout=0, result={}, handles={} )
001a: select() = PENDING { apc_handle=(nil), timeout=1c8efe0224574ac
(+0.0000000), call={APC_NONE} }
0018: resume_thread( handle=0x50 )
0018: resume_thread() = 0 { count=2 }
0018: resume_thread( handle=0x50 )
001a: *wakeup* signaled=258 cookie=0x7ffdb7b4
0018: resume_thread() = 0 { count=1 }
..
--- snip ---

When the child entered the endless loop, the father will attach to the child
process to receive debug events.


More information about the wine-bugs mailing list