https://bugs.winehq.org/show_bug.cgi?id=46957
Anastasius Focht focht@gmx.net changed:
What |Removed |Added ---------------------------------------------------------------------------- URL| |https://archive.org/downloa | |d/SCDEMO_201606/SC_DEMO.EXE Component|-unknown |kernel32 Ever confirmed|0 |1 CC| |focht@gmx.net Summary|Safecracker (1998): |Safecracker (1998) |Installer freezes on wine |installer freezes after |versions above 3.21 |selection of installation | |directory (broken | |installer, local buffer | |from previous stack | |frame/out of scope data | |passed to Win32 API) Keywords|regression |download, Installer Status|UNCONFIRMED |NEW
--- Comment #3 from Anastasius Focht focht@gmx.net --- Hello folks,
confirming. The installer is horribly broken and just works by pure chance on some systems. Even with your reported Wine 3.21 or older "working", it doesn't work for me for obvious reasons with any Wine version.
--- quote --- When run with debug flag the installer does not freeze but crashes instead. --- quote ---
That's expected. Usage of relay thunks leads to different stack usage which is a key aspect in this bug.
--- snip --- $ WINEDEBUG=+seh,+relay wine ./SC_DEMO.EXE >>log.txt 2>&1 ... 0054:Call KERNEL32.CreateProcessA(00000000,00172628 "C:\users\focht\Temp\~0000.exe Z:\home\focht\DOWN~NTG\SC_DEMO.cfg",00000000,00000000,00000000,00000000,00000000,00000000,0190f420,0190f410) ret=f754b8df ... 0056:Call KERNEL32.__wine_kernel_init() ret=7bc77f9f 0056:Ret KERNEL32.__wine_kernel_init() retval=7b47aca4 ret=7bc77f9f 0054:Ret KERNEL32.CreateProcessA() retval=00000001 ret=f754b8df ... 0056:Call KERNEL32.lstrlenA(0032ef2c "c:\SC_DEMO") ret=00410a9d 0056:Ret KERNEL32.lstrlenA() retval=0000000a ret=00410a9d 0056:Call KERNEL32.lstrlenA(0032eec8 "c:\SC_DEMO\Xtras") ret=00410aa9 0056:Ret KERNEL32.lstrlenA() retval=00000010 ret=00410aa9 0056:Call KERNEL32.lstrcpyA(0032ec00,0032ef2c "c:\SC_DEMO") ret=00410ac1 0056:Ret KERNEL32.lstrcpyA() retval=0032ec00 ret=00410ac1 0056:Call KERNEL32.lstrcpyA(0032ef2c,0032ec00 "c:\SC_DEMO\Xtras") ret=00410b40 0056:Ret KERNEL32.lstrcpyA() retval=0032ef2c ret=00410b40 0056:Call KERNEL32.SetCurrentDirectoryA(0032ef2c "c:\SC_DEMO\XT\xf22") ret=00422701 0056:Ret KERNEL32.SetCurrentDirectoryA() retval=00000000 ret=00422701 0056:Call KERNEL32.GetLastError() ret=0042277f 0056:Ret KERNEL32.GetLastError() retval=00000002 ret=0042277f 0056:Call KERNEL32.GetLastError() ret=00424b68 0056:Ret KERNEL32.GetLastError() retval=00000002 ret=00424b68 0056:Call KERNEL32.GetLastError() ret=00424b68 0056:Ret KERNEL32.GetLastError() retval=00000002 ret=00424b68 0056:Call KERNEL32.CreateDirectoryA(0032ef2c "c:\SC_DEMO\XT\xf22",00000000) ret=004227ad 0056:Ret KERNEL32.CreateDirectoryA() retval=00000001 ret=004227ad ... <endless looping due to Win32 API failure (corrupted buffers)> --- snip ---
Disassembly:
--- snip --- 00410AE3 PUSH EBP 00410AE4 MOV EBP,ESP 00410AE6 SUB ESP,12C 00410AEC PUSH EBX 00410AED PUSH ESI 00410AEE PUSH EDI 00410AEF MOV ESI,DWORD PTR SS:[ARG.1] 00410AF2 CMP BYTE PTR DS:[ESI+1],3A 00410AF6 JNE SHORT 00410B04 00410AF8 MOV AL,BYTE PTR DS:[ESI] 00410AFA OR AL,20 00410AFC MOVSX EAX,AL 00410AFF SUB EAX,60 00410B02 JMP SHORT 00410B09 00410B04 MOV EAX,DWORD PTR DS:[44E210] 00410B09 CMP DWORD PTR DS:[44E5BC],EAX 00410B0F JE SHORT 00410B16 00410B11 MOV DWORD PTR DS:[44E5BC],EAX 00410B16 MOV EDI,DWORD PTR DS:[<&KERNEL32.lstrcatA>] 00410B1C MOV EBX,DWORD PTR SS:[ARG.2] 00410B1F MOV BYTE PTR SS:[LOCAL.75],0 00410B26 PUSH EBX ; arg2 = 0032DE4C 00410B27 PUSH ESI ; arg1 = 004469A8 = "c:\SC_DEMO" 00410B28 CALL 00410A70 ; genius "strdup" using local stack 00410B2D ADD ESP,8 00410B30 TEST EAX,EAX ; stack buffer within 00410A70 scope (!) 00410B32 JZ 00410BBC 00410B38 PUSH EAX ; src = 0032DBF0 00410B39 PUSH EBX ; dest = 0032DE4C 00410B3A CALL DWORD PTR DS:[<&KERNEL32.lstrcpyA>] 00410B40 CMP BYTE PTR DS:[EBX+1],3A 00410B44 JNE SHORT 00410B54 00410B46 CMP BYTE PTR DS:[EBX+2],0 00410B4A JNE SHORT 00410B54 00410B4C MOV BYTE PTR DS:[EBX+2],5C 00410B50 MOV BYTE PTR DS:[EBX+3],0 00410B54 PUSH EBX 00410B55 CALL 004216C0 00410B5A ADD ESP,4 00410B5D CMP EAX,3 00410B60 JBE SHORT 00410B26 00410B62 PUSH EBX 00410B63 CALL 004226F0 00410B68 ADD ESP,4 00410B6B TEST EAX,EAX 00410B6D JZ SHORT 00410B26 00410B6F PUSH EBX 00410B70 CALL 004227A0 00410B75 ADD ESP,4 00410B78 TEST EAX,EAX 00410B7A JNZ SHORT 00410BA4 00410B7C PUSH OFFSET 004464F4 ; ASCII "DIRECTORY: "" 00410B81 LEA EAX,[LOCAL.75] 00410B87 PUSH EAX 00410B88 CALL EDI 00410B8A PUSH EBX 00410B8B LEA EAX,[LOCAL.75] 00410B91 PUSH EAX 00410B92 CALL EDI 00410B94 PUSH OFFSET 0044557C ; ASCII """ 00410B99 LEA EAX,[LOCAL.75] 00410B9F PUSH EAX 00410BA0 CALL EDI 00410BA2 JMP SHORT 00410B26 00410BA4 PUSH 30 ; Type = MB_OK|MB_ICONEXCLAMATION ... 00410BA6 PUSH 0 ; Caption = NULL 00410BA8 PUSH OFFSET 0044E670 ; Text = "Invalid directory." 00410BAD PUSH 0 ; hOwner = NULL 00410BAF CALL DWORD PTR DS:[<&USER32.MessageBoxA>] 00410BB5 MOV EAX,2 00410BBA JMP SHORT 00410BDF 00410BBC PUSH EBX 00410BBD CALL 004226F0 00410BC2 ADD ESP,4 00410BC5 CMP BYTE PTR SS:[LOCAL.75],0 00410BCC JE SHORT 00410BDD 00410BCE LEA EAX,[LOCAL.75] 00410BD4 PUSH EAX 00410BD5 CALL 00419802 00410BDA ADD ESP,4 00410BDD XOR EAX,EAX 00410BDF POP EDI 00410BE0 POP ESI 00410BE1 POP EBX 00410BE2 MOV ESP,EBP 00410BE4 POP EBP 00410BE5 RETN --- snip ---
The genius "strdup":
--- snip --- 00410A70 PUSH EBP 00410A71 XOR EAX,EAX 00410A73 MOV EBP,ESP 00410A75 MOV ECX,40 00410A7A SUB ESP,104 ; reserve local buffer 00410A80 MOV BYTE PTR SS:[LOCAL.65],0 00410A87 PUSH ESI 00410A88 PUSH EDI 00410A89 LEA EDI,[LOCAL.65+1] 00410A8F REP STOS DWORD PTR ES:[EDI] ; zero out local buffer 00410A91 STOS WORD PTR ES:[EDI] 00410A93 STOS BYTE PTR ES:[EDI] 00410A94 PUSH DWORD PTR SS:[ARG.2] ; String => [ARG.2] "C:" = 3 00410A97 CALL DWORD PTR DS:[<&KERNEL32.lstrlenA>] 00410A9D MOV EDI,EAX 00410A9F MOV ESI,DWORD PTR SS:[ARG.1] 00410AA2 PUSH ESI ; String => [ARG.1] "C:\SC_DEMO" = 10 00410AA3 CALL DWORD PTR DS:[<&KERNEL32.lstrlenA>] 00410AA9 CMP EAX,EDI 00410AAB JNE SHORT 00410AB1 00410AAD XOR EAX,EAX 00410AAF JMP SHORT 00410ADD 00410AB1 PUSH DWORD PTR SS:[ARG.2] ; Src => [ARG.2] = "C:" 00410AB4 LEA ECX,[LOCAL.65] ; buffer = 0032DBF0 00410ABA PUSH ECX ; Dest => OFFSET LOCAL.65 00410ABB CALL DWORD PTR DS:[<&KERNEL32.lstrcpyA>] 00410AC1 MOV AL,BYTE PTR DS:[EDI+ESI] 00410AC4 MOV BYTE PTR SS:[EDI+EBP-104],AL 00410ACB INC EDI 00410ACC MOV AL,BYTE PTR DS:[EDI+ESI] 00410ACF TEST AL,AL 00410AD1 JZ SHORT 00410AD7 00410AD3 CMP AL,5C 00410AD5 JNE SHORT 00410AC1 00410AD7 LEA EAX,[LOCAL.65] ; that braindamage ... 00410ADD POP EDI 00410ADE POP ESI 00410ADF MOV ESP,EBP ; local buf > stack top 00410AE1 POP EBP 00410AE2 RETN --- snip ---
As soon as 00410ADF is executed (stack frame removed, local stack var = out of scope), any APC delivered to that thread will potentially corrupt the buffer. Easy to demonstrate by debugging. Single step -> signal/exception handling -> corrupts buffer.
Even with some hackery (I can't find the ticket right now), that avoids touching the immediate range above current ESP during signal/exception handling, it won't work.
After the function return, 'kernel32.lstrcpyA' is executed (see first snippet -> 00410B3A).
https://source.winehq.org/git/wine.git/blob/HEAD:/dlls/kernel32/string.c#l76
--- snip --- $ objdump -S -d /home/focht/projects/wine/mainline-install-x86_64/lib/wine/kernel32.dll.so | awk -F"\n" -v RS="\n\n" '$1 ~ /lstrcpyA/'
7b491530 <lstrcpyA>: /*********************************************************************** * lstrcpyA (KERNEL32.@) * lstrcpy (KERNEL32.@) */ LPSTR WINAPI lstrcpyA( LPSTR dst, LPCSTR src ) { 7b491530: 8d 4c 24 04 lea 0x4(%esp),%ecx 7b491534: 83 e4 f0 and $0xfffffff0,%esp 7b491537: ff 71 fc pushl -0x4(%ecx) 7b49153a: 55 push %ebp 7b49153b: 89 e5 mov %esp,%ebp 7b49153d: 53 push %ebx 7b49153e: 51 push %ecx 7b49153f: 81 ec d8 00 00 00 sub $0xd8,%esp 7b491545: 8b 01 mov (%ecx),%eax __TRY { /* this is how Windows does it */ memmove( dst, src, strlen(src)+1 ); } __EXCEPT_PAGE_FAULT 7b491547: c7 85 48 ff ff ff f0 movl $0x7b4a5af0,-0xb8(%ebp) 7b49154e: 5a 4a 7b { 7b491551: 89 85 34 ff ff ff mov %eax,-0xcc(%ebp) 7b491557: 8b 41 04 mov 0x4(%ecx),%eax __EXCEPT_PAGE_FAULT 7b49155a: 6a 00 push $0x0 { 7b49155c: 89 85 30 ff ff ff mov %eax,-0xd0(%ebp) __EXCEPT_PAGE_FAULT 7b491562: 8d 85 54 ff ff ff lea -0xac(%ebp),%eax 7b491568: 50 push %eax 7b491569: e8 fc ff ff ff call 7b49156a <lstrcpyA+0x3a> ... --- snip ---
There is quite an amount of stack space required by SEH setup (exception registration record, signal buffer, various ptrs) which now overlaps/reuses the area of former stack frame. Lets assume that one is not touched (no exception) hence the buffer content is still there ...
The next Win32 API call will for sure corrupt it in between the final native API (ntdll) call: 'kernel32.SetCurrentDirectoryA'
https://source.winehq.org/git/wine.git/blob/HEAD:/dlls/kernel32/path.c#l1813
--- snip --- objdump -S -d /home/focht/projects/wine/mainline-install-x86_64/lib/wine/kernel32.dll.so | awk -F"\n" -v RS="\n\n" '$1 ~ /SetCurrentDirectoryA/' 7b478dc0 <SetCurrentDirectoryA>: { 7b478dc0: 8d 4c 24 04 lea 0x4(%esp),%ecx 7b478dc4: 83 e4 f0 and $0xfffffff0,%esp 7b478dc7: ff 71 fc pushl -0x4(%ecx) 7b478dca: 55 push %ebp 7b478dcb: 89 e5 mov %esp,%ebp 7b478dcd: 53 push %ebx 7b478dce: 51 push %ecx 7b478dcf: 83 ec 18 sub $0x18,%esp if (!(dirW = FILE_name_AtoW( dir, FALSE ))) return FALSE; 7b478dd2: 6a 00 push $0x0 7b478dd4: ff 31 pushl (%ecx) 7b478dd6: e8 65 a2 fd ff call 7b453040 <FILE_name_AtoW> 7b478ddb: 83 c4 10 add $0x10,%esp 7b478dde: 31 d2 xor %edx,%edx 7b478de0: 85 c0 test %eax,%eax 7b478de2: 74 26 je 7b478e0a <SetCurrentDirectoryA+0x4a> RtlInitUnicodeString( &strW, dirW ); 7b478de4: 83 ec 08 sub $0x8,%esp 7b478de7: 8d 5d f0 lea -0x10(%ebp),%ebx 7b478dea: 50 push %eax 7b478deb: 53 push %ebx 7b478dec: e8 df 20 fc ff call 7b43aed0 <RtlInitUnicodeString> status = RtlSetCurrentDirectory_U( &strW ); 7b478df1: 83 ec 04 sub $0x4,%esp 7b478df4: 53 push %ebx 7b478df5: e8 e6 21 fc ff call 7b43afe0 <RtlSetCurrentDirectory_U> 7b478dfa: 89 c3 mov %eax,%ebx if (status != STATUS_SUCCESS) SetLastError( RtlNtStatusToDosError(status) ); 7b478dfc: 83 c4 0c add $0xc,%esp 7b478dff: 85 c0 test %eax,%eax 7b478e01: 75 1d jne 7b478e20 <SetCurrentDirectoryA+0x60> return !status; 7b478e03: 31 d2 xor %edx,%edx 7b478e05: 85 db test %ebx,%ebx 7b478e07: 0f 94 c2 sete %dl } 7b478e0a: 8d 65 f8 lea -0x8(%ebp),%esp 7b478e0d: 89 d0 mov %edx,%eax 7b478e0f: 59 pop %ecx 7b478e10: 5b pop %ebx 7b478e11: 5d pop %ebp 7b478e12: 8d 61 fc lea -0x4(%ecx),%esp 7b478e15: c2 04 00 ret $0x4 7b478e18: 90 nop 7b478e19: 8d b4 26 00 00 00 00 lea 0x0(%esi,%eiz,1),%esi if (status != STATUS_SUCCESS) SetLastError( RtlNtStatusToDosError(status) ); 7b478e20: 83 ec 0c sub $0xc,%esp 7b478e23: 50 push %eax 7b478e24: e8 ff 20 fc ff call 7b43af28 <RtlNtStatusToDosError> 7b478e29: 64 a3 34 00 00 00 mov %eax,%fs:0x34 7b478e2f: 83 c4 0c add $0xc,%esp 7b478e32: eb cf jmp 7b478e03 <SetCurrentDirectoryA+0x43> --- snip ---
One of the leaf functions (FILE_name_AtoW, RtlInitUnicodeString, ..) will always touch the area of the former app 00410A70 sub-routine stack frame, corrupting the contents.
Unfortunately there is not much that can be done about here.
ProtectionID scan of the offending executable '~0000.exe':
--- snip --- -=[ ProtectionID v0.6.9.0 DECEMBER]=- (c) 2003-2017 CDKiLLER & TippeX Build 24/12/17-21:05:42 Ready... Scanning -> C:\users\focht\Temp~0000.exe File Type : 32-Bit Exe (Subsystem : Win GUI / 2), Size : 345088 (054400h) Byte(s) | Machine: 0x14C (I386) Compilation TimeStamp : 0x327E7F39 -> Mon 04th Nov 1996 23:41:45 (GMT) [TimeStamp] 0x327E7F39 -> Mon 04th Nov 1996 23:41:45 (GMT) | PE Header | - | Offset: 0x00000088 | VA: 0x00400088 | - [File Heuristics] -> Flag #1 : 00000000000001001100000000000000 (0x0004C000) [Entrypoint Section Entropy] : 6.41 (section #0) ".text " | Size : 0x3ABE6 (240614) byte(s) [DllCharacteristics] -> Flag : (0x0000) -> NONE [SectionCount] 6 (0x6) | ImageSize 0x5D000 (380928) byte(s) [VersionInfo] Company Name : 2020 Software [VersionInfo] Product Name : 2020 Software Install [VersionInfo] Product Version : 5. 0. 0. 1 [VersionInfo] File Description : Install [VersionInfo] File Version : 5. 0. 0. 1 [VersionInfo] Original FileName : Install.exe [VersionInfo] Internal Name : Install [VersionInfo] Legal Copyrights : Copyright © 1996 [ModuleReport] [IAT] Modules -> VERSION.dll | WINMM.dll | MPR.dll | KERNEL32.dll | USER32.dll | GDI32.dll | comdlg32.dll | WINSPOOL.DRV | ADVAPI32.dll | SHELL32.dll | COMCTL32.dll | ole32.dll [CompilerDetect] -> Visual C++ 3.0 [!] File appears to have no protection or is using an unknown protection - Scan Took : 0.244 Second(s) [0000000F4h (244) tick(s)] [506 of 580 scan(s) done] --- snip ---
$ sha1sum SC_DEMO.EXE f3ceb9d47776c7a9a9909c634b9ee4d976967338 SC_DEMO.EXE
$ du -sh SC_DEMO.EXE 18M SC_DEMO.EXE
$ wine --version wine-4.7-177-gb479382737
Regards