[Bug 50108] New: Multiple applications using native API sandboxing / virtualization crash when being relay traced (Adobe Reader 11)

WineHQ Bugzilla wine-bugs at winehq.org
Mon Nov 9 14:38:11 CST 2020


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

            Bug ID: 50108
           Summary: Multiple applications using native API sandboxing /
                    virtualization crash when being relay traced (Adobe
                    Reader 11)
           Product: Wine
           Version: 5.21
          Hardware: x86
                OS: Linux
            Status: NEW
          Severity: normal
          Priority: P2
         Component: tools
          Assignee: wine-bugs at winehq.org
          Reporter: focht at gmx.net
      Distribution: ---

Hello folks,

encountered while revisiting some Adobe bugs with native API sandboxing enabled
(default).

Native API sandboxing works as designed under normal conditions, see my
no-relay references later.
Relay debugging is a developer / triager use-case.

Stable download link via Internet Archive:

https://web.archive.org/web/20200522053748/http://ardownload.adobe.com/pub/adobe/reader/win/11.x/11.0.04/en_US/AdbeRdr11004_en_US.exe

Acrobat Reader creates a sandboxed process (initially suspended) which crashes
in entry point when being relay traced.

--- snip ---
$ pwd
/home/focht/.wine/drive_c/Program Files (x86)/Adobe/Reader 11.0/Reader

$ WINEDEBUG=+seh,+relay,+server,+ntdll wine ./AcroRd32.exe >>log.txt 2>&1
...
0024:Call KERNEL32.CreateProcessAsUserW(00000100,0198e508 L"C:\\Program Files
(x86)\\Adobe\\Reader 11.0\\Reader\\AcroRd32.exe",0198e618 L"\"C:\\Program Files
(x86)\\Adobe\\Reader 11.0\\Reader\\AcroRd32.exe\" --channel=32.1.1899522388
--type=renderer",00000000,00000000,00000000,0100040c,00df6f68,00000000,0031f644,0031f694)
ret=0042b162 
...
0130: init_process_done( gui=1, module=00400000, ldt_copy=f7d5d9c0,
entry=00401039 )
0024: *wakeup* signaled=0
0130: init_process_done() = 0 { suspend=1 }
...
0024:Ret  KERNEL32.CreateProcessAsUserW() retval=00000001 ret=0042b162
...
0130:Call ntdll.RtlInitNlsTables(011d0000,011f0000,00fed810,7b081484)
ret=7b01d500
0130:Ret  ntdll.RtlInitNlsTables() retval=7b081484 ret=7b01d500
0130:Call ntdll.RtlResetRtlTranslations(7b081484) ret=7b01d50b
0130:Ret  ntdll.RtlResetRtlTranslations() retval=00000400 ret=7b01d50b
0130:Call ntdll.RtlInitUnicodeString(0031eb78,7b069b30 L"\\Registry\\Machine")
ret=7b0387b3
0130:Ret  ntdll.RtlInitUnicodeString() retval=00000024 ret=7b0387b3
0130:trace:seh:dispatch_exception code=c0000096 flags=0 addr=00A8C960
ip=00a8c960 tid=0130
0130:trace:seh:dispatch_exception  eax=7bc6c000 ebx=0031eb80 ecx=0031eb50
edx=02000000 esi=00000000 edi=00000000
0130:trace:seh:dispatch_exception  ebp=0031eb04 esp=0031ea64 cs=0023 ds=002b
es=002b fs=0063 gs=006b flags=00010202
0130:trace:seh:call_stack_handlers calling handler at 0040C7F0 code=c0000096
flags=0
0130:trace:seh:call_stack_handlers handler at 0040C7F0 returned 1
0130:trace:seh:call_stack_handlers calling handler at 7BC513A0 code=c0000096
flags=0
0130:trace:seh:__regs_RtlUnwind code=c0000096 flags=2
0130:trace:seh:__regs_RtlUnwind eax=00000000 ebx=0031eed8 ecx=0031e8a8
edx=0031eed8 esi=00000001 edi=00000000
0130:trace:seh:__regs_RtlUnwind ebp=0031e508 esp=0031e500 eip=7bc512a6 cs=0023
ds=002b fs=0063 gs=006b flags=00000206
0130:trace:seh:__regs_RtlUnwind calling handler at 7BC460F0 code=c0000096
flags=2
0130:trace:seh:__regs_RtlUnwind handler at 7BC460F0 returned 1
0130:trace:seh:__regs_RtlUnwind calling handler at 0040C7F0 code=c0000096
flags=2
0130:trace:seh:__regs_RtlUnwind handler at 0040C7F0 returned 1
0130: select( flags=3, cookie=0031d88c, timeout=0, size=0, prev_apc=0000,
result={}, data={}, context={} )
0130: select() = TIMEOUT { call={APC_NONE}, apc_handle=0000, context={} }
0130:exception c0000096 in PE entry point
(proc=7B0272D0,module=7B000000,reason=PROCESS_ATTACH,res=0031FD24)
0130:Ret  PE DLL (proc=7B0272D0,module=7B000000
L"kernelbase.dll",reason=PROCESS_ATTACH,res=0031FD24) retval=0
0130:Call PE DLL (proc=7B0272D0,module=7B000000
L"kernelbase.dll",reason=PROCESS_DETACH,res=0031FD24)
0130:Ret  PE DLL (proc=7B0272D0,module=7B000000
L"kernelbase.dll",reason=PROCESS_DETACH,res=0031FD24) retval=1
0130:err:module:LdrInitializeThunk "kernelbase.dll" failed to initialize,
aborting
0130:err:module:LdrInitializeThunk Initializing dlls for L"C:\\Program Files
(x86)\\Adobe\\Reader 11.0\\Reader\\AcroRd32.exe" failed, status c0000096
0130: terminate_process( handle=ffffffff, exit_code=-1073741674 )
0130: terminate_process() = 0 { self=1 }
0130: *killed* exit_code=-1073741674
--- snip ---

The crash is in kernelbase:init_locale() which calls RegCreateKeyExW ->
NtCreateKey()

NtCreateKey() happens to be the first of the hooked native API functions being
called.

Wine source:

https://source.winehq.org/git/wine.git/blob/70d77a439ab58dcf56664d1545aa0c4cd3edb31e:/dlls/kernelbase/locale.c#l699

--- snip ---
 699 /***********************************************************************
 700  *              init_locale
 701  */
 702 void init_locale(void)
 703 {
 704     UINT ansi_cp = 0, oem_cp = 0;
 705     USHORT *ansi_ptr, *oem_ptr;
 706     void *sort_ptr;
 707     LCID lcid = GetUserDefaultLCID();
 708     WCHAR bufferW[80];
 709     DYNAMIC_TIME_ZONE_INFORMATION timezone;
 710     GEOID geoid = GEOID_NOT_AVAILABLE;
 711     DWORD count, dispos, i;
 712     SIZE_T size;
 713     HKEY hkey;
 714 
 715     kernel32_handle = GetModuleHandleW( L"kernel32.dll" );
 716 
 717     GetLocaleInfoW( LOCALE_SYSTEM_DEFAULT, LOCALE_IDEFAULTANSICODEPAGE |
LOCALE_RETURN_NUMBER,
 718                     (WCHAR *)&ansi_cp, sizeof(ansi_cp)/sizeof(WCHAR) );
 719     GetLocaleInfoW( LOCALE_SYSTEM_DEFAULT, LOCALE_IDEFAULTMACCODEPAGE |
LOCALE_RETURN_NUMBER,
 720                     (WCHAR *)&mac_cp, sizeof(mac_cp)/sizeof(WCHAR) );
 721     GetLocaleInfoW( LOCALE_SYSTEM_DEFAULT, LOCALE_IDEFAULTCODEPAGE |
LOCALE_RETURN_NUMBER,
 722                     (WCHAR *)&oem_cp, sizeof(oem_cp)/sizeof(WCHAR) );
 723 
 724     NtGetNlsSectionPtr( 9, 0, NULL, &sort_ptr, &size );
 725     NtGetNlsSectionPtr( 12, NormalizationC, NULL, (void **)&norm_info,
&size );
 726     init_sortkeys( sort_ptr );
 727 
 728     if (!ansi_cp || NtGetNlsSectionPtr( 11, ansi_cp, NULL, (void
**)&ansi_ptr, &size ))
 729         NtGetNlsSectionPtr( 11, 1252, NULL, (void **)&ansi_ptr, &size );
 730     if (!oem_cp || NtGetNlsSectionPtr( 11, oem_cp, 0, (void **)&oem_ptr,
&size ))
 731         NtGetNlsSectionPtr( 11, 437, NULL, (void **)&oem_ptr, &size );
 732     NtCurrentTeb()->Peb->AnsiCodePageData = ansi_ptr;
 733     NtCurrentTeb()->Peb->OemCodePageData = oem_ptr;
 734     NtCurrentTeb()->Peb->UnicodeCaseTableData = sort.casemap;
 735     RtlInitNlsTables( ansi_ptr, oem_ptr, sort.casemap, &nls_info );
 736     RtlResetRtlTranslations( &nls_info );
 737 
 738     RegCreateKeyExW( HKEY_LOCAL_MACHINE,
L"System\\CurrentControlSet\\Control\\Nls",
 739                      0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS,
NULL, &nls_key, NULL );
 ...
--- snip ---

Entry point code of <ntdll.NtCreateKey> is read two times:

--- snip ---
0024:Call
KERNEL32.ReadProcessMemory(0000010c,7bc036ac,0031f7d0,00000018,0031f7cc)
ret=004614db
...
0024: read_process_memory( handle=010c, addr=7bc036ac )
0130: *signal* signal=19
0024: read_process_memory() = 0 {
data={8b,ff,55,8b,ec,5d,68,83,00,07,00,b8,00,c0,c6,7b,50,ff,50,04,c2,1c,00,90}
}
...
0024:Ret  KERNEL32.ReadProcessMemory() retval=00000001 ret=004614db
0024:Call
KERNEL32.ReadProcessMemory(0000010c,7bc036ac,0031f7d4,00000010,0031f7d0)
ret=004617e7
0024: read_process_memory( handle=010c, addr=7bc036ac )
0130: *signal* signal=19
0024: read_process_memory() = 0 {
data={8b,ff,55,8b,ec,5d,68,83,00,07,00,b8,00,c0,c6,7b} }
...
0024:Ret  KERNEL32.ReadProcessMemory() retval=00000001 ret=004617e7
--- snip ---

* 0x18 bytes = pre-analysis
* 0x10 bytes = unknown native API entry signature -> defaults to syscall style

The <ntdll.NtCreateKey> relay entry disassembly:

--- snip ---
7BC036AC | 8BFF               | mov edi,edi                   | hotpatch prolog
start
7BC036AE | 55                 | push ebp                      |
7BC036AF | 8BEC               | mov ebp,esp                   |
7BC036B1 | 5D                 | pop ebp                       | hotpatch prolog
end
7BC036B2 | 68 83000700        | push 70083                    |
7BC036B7 | B8 00C0C67B        | mov eax,ntdll.7BC6C000        |
7BC036BC | 50                 | push eax                      | not copied
7BC036BD | FF50 04            | call dword ptr ds:[eax+4]     | not copied
7BC036C0 | C2 1C00            | ret 1C                        | not copied
7BC036C3 | 90                 | nop                           |
--- snip ---

Wine source, emitting relay entry:

https://source.winehq.org/git/wine.git/blob/70d77a439ab58dcf56664d1545aa0c4cd3edb31e:/tools/winebuild/spec32.c#l212

--- snip ---
 212 /*******************************************************************
 213  *         output_relay_debug
 214  *
 215  * Output entry points for relay debugging
 216  */
 217 static void output_relay_debug( DLLSPEC *spec )
 218 {
...
 244     output( "\t.text\n" );
 245     output( "__wine_spec_relay_entry_points:\n" );
 246     output( "\tnop\n" );  /* to avoid 0 offset */
 247 
 248     for (i = spec->base; i <= spec->limit; i++)
 249     {
 250         ORDDEF *odp = spec->ordinals[i];
 251 
 252         if (!needs_relay( odp )) continue;
 253 
 254         switch (target_cpu)
 255         {
 256         case CPU_x86:
 257             output( "\t.align %d\n", get_alignment(4) );
 258             output( "\t.long 0x90909090,0x90909090\n" );
 259             output( ".L__wine_spec_relay_entry_point_%d:\n", i );
 260             output_cfi( ".cfi_startproc" );
 261             output( "\t.byte 0x8b,0xff,0x55,0x8b,0xec,0x5d\n" );  /*
hotpatch prolog */
 262             if (odp->flags & (FLAG_THISCALL | FLAG_FASTCALL))  /* add the
register arguments */
 263             {
 264                 output( "\tpopl %%eax\n" );
 265                 if ((odp->flags & FLAG_FASTCALL) && get_args_size( odp ) >
4) output( "\tpushl %%edx\n" );
 266                 output( "\tpushl %%ecx\n" );
 267                 output( "\tpushl %%eax\n" );
 268             }
 269             output( "\tpushl $%u\n", (odp->u.func.args_str_offset << 16) |
(i - spec->base) );
 270             output_cfi( ".cfi_adjust_cfa_offset 4" );
 271 
 272             if (UsePIC)
 273             {
 274                 output( "\tcall %s\n",
asm_name("__wine_spec_get_pc_thunk_eax") );
 275                 output( "1:\tleal
.L__wine_spec_relay_descr-1b(%%eax),%%eax\n" );
 276                 needs_get_pc_thunk = 1;
 277             }
 278             else output( "\tmovl $.L__wine_spec_relay_descr,%%eax\n" );
 279             output( "\tpushl %%eax\n" );
 280             output_cfi( ".cfi_adjust_cfa_offset 4" );
 281 
 282             output( "\tcall *4(%%eax)\n" );
 283             output_cfi( ".cfi_adjust_cfa_offset -8" );
 284             if (odp->type == TYPE_STDCALL)
 285                 output( "\tret $%u\n", get_args_size( odp ));
 286             else
 287                 output( "\tret\n" );
 288             output_cfi( ".cfi_endproc" );
 289             break;
--- snip ---

For reference, <ntdll.NtCreateKey> without relay trace (syscall style,fits into
16 bytes):

--- snip ---
7BC0B9B0 | B8 1C000000        | mov eax,1C             |
7BC0B9B5 | BA D0C5C07B        | mov edx,ntdll.7BC0C5D0 |
7BC0B9BA | FFD2               | call edx               |
7BC0B9BC | C2 1C00            | ret 1C                 |
7BC0B9BF | 90                 | nop                    |
...
7BC0C5D0 | FF25 18C0C67B      | jmp dword ptr ds:[<__wine_syscall_dispatcher>]
|
--- snip ---

The parent process writes a copy of the original native API entry and
trampoline code into child process (sandbox) address space:

--- snip ---
0024:Call
KERNEL32.WriteProcessMemory(0000010c,00a8c950,019da140,00000036,0031f7cc)
ret=00430409
...
0024: write_process_memory( handle=010c, addr=00a8c950,
data={8b,ff,55,8b,ec,5d,68,83,00,07,00,b8,00,c0,c6,7b,6c,00,00,00,00,00,00,00,83,ec,08,52,8b,54,24,0c,89,54,24,08,c7,44,24,0c,50,c9,a8,00,c7,44,24,04,70,8a,41,00,5a,c3}
)
0130: *signal* signal=19
0024: write_process_memory() = 0
...
0024:Ret  KERNEL32.WriteProcessMemory() retval=00000001 ret=00430409
--- snip ---

--- snip ---
00A8C950 | 8BFF               | mov edi,edi                              | org
copy of
00A8C952 | 55                 | push ebp                                 |
NtCreateKey
00A8C953 | 8BEC               | mov ebp,esp                              |
relay entry
00A8C955 | 5D                 | pop ebp                                  |
00A8C956 | 68 83000700        | push 70083                               |
00A8C95B | B8 00C0C67B        | mov eax,ntdll.7BC6C000                   |
00A8C960 | 6C                 | insb                                     |
*boom*
00A8C962 | 0000               | add byte ptr ds:[eax],al                 |
00A8C964 | 0000               | add byte ptr ds:[eax],al                 |
00A8C966 | 0000               | add byte ptr ds:[eax],al                 |
00A8C967 | 00
00A8C968 | 83EC 08            | sub esp,8                                |
trampoline
00A8C96C | 52                 | push edx                                 |
00A8C970 | 8B5424 0C          | mov edx,dword ptr ss:[esp+C]             |
00A8C974 | 895424 08          | mov dword ptr ss:[esp+8],edx             |
00A8C978 | C74424 0C 50C9A800 | mov dword ptr ss:[esp+C],A8C950          |
NtCreateKey
00A8C980 | C74424 04 708A4100 | mov dword ptr ss:[esp+4],acrord32.418A70 | hook
impl.
00A8C988 | 5A                 | pop edx                                  |
00A8C989 | C3                 | ret                                      |
--- snip ---

and patches original <ntdll.NtCreateKey> relay entry:

--- snip ---
0024:Call
KERNEL32.WriteProcessMemory(0000010c,7bc036ac,0031f7d4,0000000c,0031f798)
ret=004304ed
...
0024: write_process_memory( handle=010c, addr=7bc036ac,
data={b8,ff,55,8b,ec,ba,68,c9,a8,00,ff,e2} )
0130: *signal* signal=19
0024: write_process_memory() = 0
...
0024:Ret  KERNEL32.WriteProcessMemory() retval=00000001 ret=004304ed
--- snip ---

Patched <ntdll.NtCreateKey> relay entry:

--- snip ---
7BC036AC | B8 FF558BEC        | mov eax,EC8B55FF                         |
garbage #sysc
7BC036B1 | BA 68C9A800        | mov edx,A8C968                           |
trampoline
7BC036B6 | FFE2               | jmp edx                                  |
--- snip ---

It crashes when the hook calls the original relay entry code which was only
partially copied.

For reference how it looks like without relay - working as intended:

--- snip ---
00576950 | B8 1C000000        | mov eax,1C                               | org
copy of
00576955 | BA D0C5C07B        | mov edx,ntdll.7BC0C5D0                   |
NtCreateKey
0057695A | FFD2               | call edx                                 |
entry
0057695C | C2 1C00            | ret 1C                                   |
0057695F | 90                 | nop                                      |
00576960 | 6C                 | insb                                     | fill
00576961 | 0000               | add byte ptr ds:[eax],al                 |
00576963 | 0000               | add byte ptr ds:[eax],al                 |
00576965 | 0000               | add byte ptr ds:[eax],al                 |
00576967 | 00
00576968 | 83EC 08            | sub esp,8                                |
trampoline
0057696C | 52                 | push edx                                 |
00576970 | 895424 08          | mov dword ptr ss:[esp+8],edx             |
00576974 | C74424 0C 50695700 | mov dword ptr ss:[esp+C],576950          |
NtCreateKey
0057697C | C74424 04 708A4100 | mov dword ptr ss:[esp+4],acrord32.418A70 | hook
impl.
00576984 | 5A                 | pop edx                                  |
00576985 | C3                 | ret                                      |
--- snip ---

Patched <ntdll.NtCreateKey> entry:

--- snip ---
7BC0B9B0 | B8 1C000000        | mov eax,1C                               | org
#sysc
7BC0B9B5 | BA 68695700        | mov edx,576968                           |
trampoline
7BC0B9BA | FFE2               | jmp edx
7BC0B9BC | C2 1C00            | retn 1C
7BC0B9BF | 90                 | nop
--- snip ---

Yes, I know it's possible to disable relay thunks for native API (RelayExclude
-> ntdll.*), even for a sub-set (the ones being hooked) to avoid crashes.
Maybe something can be done without resorting to that functionality. If it's
not worth to do extra handling for NT syscall relay thunks then you might
resolve this one as 'WONTFIX'.

$ sha1sum AdbeRdr11004_en_US.exe 
9c295c16d374735bf292ef6c630c9ab392c22500  AdbeRdr11004_en_US.exe

$ du -sh AdbeRdr11004_en_US.exe 
49M    AdbeRdr11004_en_US.exe

$ wine --version
wine-5.21

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