[Bug 46957] Safecracker (1998) installer freezes after selection of installation directory ( broken installer, local buffer from previous stack frame/ out of scope data passed to Win32 API)
wine-bugs at winehq.org
wine-bugs at winehq.org
Sat May 4 05:44:03 CDT 2019
https://bugs.winehq.org/show_bug.cgi?id=46957
Anastasius Focht <focht at 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 at 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 at 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
--
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