http://bugs.winehq.org/show_bug.cgi?id=16365
Anastasius Focht focht@gmx.net changed:
What |Removed |Added ---------------------------------------------------------------------------- URL| |http://www.gamefront.com/fi | |les/13750760/ CC| |focht@gmx.net Component|-unknown |rsaenh Summary|Archlord crashes with |Archlord Episode 3 Client |err:dbghelp:SymCleanup this |crashes on startup |process has not had |(decrypting files with a |SymInitialize() called for |RC4 session key derived |it |from MD5 hash fails, only | |40 bits are used, salt is | |dropped)
--- Comment #14 from Anastasius Focht focht@gmx.net 2013-11-10 07:08:26 CST --- Hello folks,
confirming.
It seems the game has several encrypted files and Wine doesn't properly decrypt them, leading to failure.
Example: "ini\sysstr.txt"
Map the file into memory, allocate decrypt buffer, copy file content:
--- snip --- $ pwd /home/focht/.wine/drive_c/Program Files/Codemasters/Archlord ... $ WINEDEBUG=+tid,+seh,+relay wine ./alefclient.exe >>log.txt 2>&1 ... 0025:Starting process L"C:\Program Files\Codemasters\Archlord\alefclient.exe" (entryproc=0x75a94a) ... 0025:Call KERNEL32.CreateFileA(007ee634 "ini\sysstr.txt",80000000,00000000,00000000,00000003,00000080,00000000) ret=004ea20d 0025:Ret KERNEL32.CreateFileA() retval=000000ac ret=004ea20d 0025:Call KERNEL32.CreateFileMappingA(000000ac,00000000,00000002,00000000,00000000,00000000) ret=004ea225 0025:Ret KERNEL32.CreateFileMappingA() retval=000000b0 ret=004ea225 0025:Call KERNEL32.MapViewOfFile(000000b0,00000004,00000000,00000000,00000000) ret=004ea248 0025:Ret KERNEL32.MapViewOfFile() retval=05a30000 ret=004ea248 0025:Call KERNEL32.GetFileSize(000000ac,00000000) ret=004ea25a 0025:Ret KERNEL32.GetFileSize() retval=0000401b ret=004ea25a ... 0025:Call ntdll.RtlAllocateHeap(00110000,00000000,0000401c) ret=7e25f194 0025:Ret ntdll.RtlAllocateHeap() retval=053be168 ret=7e25f194 0025:Ret msvcrt.??2@YAPAXI@Z() retval=053be168 ret=004e1be6 0025:Call msvcrt.memcpy(053be168,05a30000,0000401b) ret=004e1bf0 0025:Ret msvcrt.memcpy() retval=053be168 ret=004e1bf0 --- snip ---
Decrypting the content with session key (derived from MD5 hash + 4 char password "1111"):
--- snip --- ... 0025:Call advapi32.CryptSetProviderA(008038cc "Microsoft Base Cryptographic Provider v1.0",00000001) ret=004e9781 0025:Ret advapi32.CryptSetProviderA() retval=00000000 ret=004e9781 0025:Call advapi32.CryptAcquireContextA(0033f294,00000000,008038cc "Microsoft Base Cryptographic Provider v1.0",00000001,f0000000) ret=004e9796 0025:Call rsaenh.CPAcquireContext(053c22f0,00000000,f0000000,053c2370) ret=7e93e7eb ... 0025:Ret rsaenh.CPAcquireContext() retval=00000001 ret=7e93e7eb 0025:Ret advapi32.CryptAcquireContextA() retval=00000001 ret=004e9796 ... 0025:Call advapi32.CryptCreateHash(053c22e0,00008003,00000000,00000000,0033f278) ret=004e98cd 0025:Call rsaenh.CPCreateHash(00000002,00008003,00000000,00000000,053c2198) ret=7e93efb8 0025:Call ntdll.RtlAllocateHeap(00110000,00000000,00000170) ret=7cc507a6 0025:Ret ntdll.RtlAllocateHeap() retval=053c2608 ret=7cc507a6 0025:Call advapi32.MD5Init(053c2628) ret=7cc508c8 0025:Ret advapi32.MD5Init() retval=053c2628 ret=7cc508c8 0025:Ret rsaenh.CPCreateHash() retval=00000001 ret=7e93efb8 0025:Ret advapi32.CryptCreateHash() retval=00000001 ret=004e98cd ... 0025:Call advapi32.CryptHashData(053c2190,00802d18,00000004,00000000) ret=004e98fe 0025:Call rsaenh.CPHashData(00000002,00000004,00802d18,00000004,00000000) ret=7e9410c8 0025:Call advapi32.MD5Update(053c2628,00802d18,00000004) ret=7cc509d4 0025:Ret advapi32.MD5Update() retval=053c2640 ret=7cc509d4 0025:Ret rsaenh.CPHashData() retval=00000001 ret=7e9410c8 0025:Ret advapi32.CryptHashData() retval=00000001 ret=004e98fe ... 0025:Call advapi32.CryptDeriveKey(053c22e0,00006801,053c2190,00000004,0033f27c) ret=004e991d 0025:Call rsaenh.CPDeriveKey(00000002,00006801,00000004,00000004,053c21b0) ret=7e93f2d3 0025:Call ntdll.RtlAllocateHeap(00110000,00000000,000003e4) ret=7cc507a6 0025:Ret ntdll.RtlAllocateHeap() retval=053c2780 ret=7cc507a6 0025:Call advapi32.MD5Final(053c2628) ret=7cc50b2e 0025:Ret advapi32.MD5Final() retval=053c2680 ret=7cc50b2e 0025:Ret rsaenh.CPDeriveKey() retval=00000001 ret=7e93f2d3 ... 0025:Call advapi32.CryptDestroyHash(053c2190) ret=004e992c 0025:Call rsaenh.CPDestroyHash(00000002,00000004) ret=7e93f412 ... 0025:Ret rsaenh.CPDestroyHash() retval=00000001 ret=7e93f412 0025:Ret advapi32.CryptDestroyHash() retval=00000001 ret=004e992c ... 0025:Call advapi32.CryptDecrypt(053c21a8,00000000,00000000,00000000,053be168,0033f28c) ret=004e994f 0025:Call rsaenh.CPDecrypt(00000002,00000003,00000000,00000000,00000000,053be168,0033f28c) ret=7e93f14b 0025:Ret rsaenh.CPDecrypt() retval=00000001 ret=7e93f14b 0025:Ret advapi32.CryptDecrypt() retval=00000001 ret=004e994f ... 0025:Call advapi32.CryptDestroyKey(053c21a8) ret=004e9962 0025:Call rsaenh.CPDestroyKey(00000002,00000003) ret=7e93f534 ... 0025:Ret rsaenh.CPDestroyKey() retval=00000001 ret=7e93f534 0025:Ret advapi32.CryptDestroyKey() retval=00000001 ret=004e9962 ... 0025:Call rsaenh.CPReleaseContext(00000002,00000000) ret=7e93ecc6 ... 0025:Ret advapi32.CryptReleaseContext() retval=00000001 ret=004e998f --- snip ---
After decryption the code tries to parse "key=value" pair strings on the buffer:
--- snip --- ... 0025:Call ntdll.RtlAllocateHeap(00110000,00000000,00001000) ret=7e25f194 0025:Ret ntdll.RtlAllocateHeap() retval=053c2190 ret=7e25f194 0025:Ret msvcrt.??2@YAPAXI@Z() retval=053c2190 ret=004e1c1e 0025:Call msvcrt.memset(053c2190,00000000,00001000) ret=004e1c32 0025:Ret msvcrt.memset() retval=053c2190 ret=004e1c32 0025:Call msvcrt.memcpy(053c2190,053be168,00000013) ret=004ea127 0025:Ret msvcrt.memcpy() retval=053c2190 ret=004ea127 0025:Call ntdll.strcspn(053c2190 "\x8a\r|\xe6\x03\x07<\xe5n7\x03Gb\xc0\xe3\xa4\xfaR",00802f9c "=") ret=004e1c5c 0025:Ret ntdll.strcspn() retval=00000012 ret=004e1c5c 0025:Call ntdll.strcspn(053c2190 "\x8a\r|\xe6\x03\x07<\xe5n7\x03Gb\xc0\xe3\xa4\xfaR",00802f9c "=") ret=004e1ccd 0025:Ret ntdll.strcspn() retval=00000012 ret=004e1ccd 0025:Call msvcrt.??2@YAPAXI@Z(00000000) ret=004e1cf8 0025:Call ntdll.RtlAllocateHeap(00110000,00000000,00000000) ret=7e25f194 0025:Ret ntdll.RtlAllocateHeap() retval=053c3198 ret=7e25f194 0025:Ret msvcrt.??2@YAPAXI@Z() retval=053c3198 ret=004e1cf8 0025:Call msvcrt.memcpy(053c3198,053c21a3,ffffffff) ret=004e1d04 0025:trace:seh:raise_exception code=c0000005 flags=0 addr=0xf74f33f1 ip=f74f33f1 tid=0025 0025:trace:seh:raise_exception info[0]=00000001 0025:trace:seh:raise_exception info[1]=053d0000 0025:trace:seh:raise_exception eax=053cf03b ebx=f7562000 ecx=ffff30e7 edx=053cffb0 esi=0033f2bc edi=0033f28c 0025:trace:seh:raise_exception ebp=0033f278 esp=0033f258 cs=0023 ds=002b es=002b fs=0063 gs=006b flags=00010286 ... --- snip ---
This obviously fails because the buffer content was incorrectly decrypted -> garbage strings to strcspn().
Dumping 40-bit session key:
--- snip --- 0x0553d87c: bf679cb5 00000019 00000000 00000000 0x0553d88c: 00000000 00000000 00000000 00000000 0x0553d89c: 00000000 00000000 00000000 00000000 --- snip ---
128-bit hash -> 88 bits leftover, salt value retrieved by CryptGetKeyParam( KP_SALT)
--- snip --- Wine-dbg>bt Backtrace: =>0 0x7e944600 MD5Final+0x161(ctx=0x553d6e8) [/home/focht/projects/wine/wine-git/dlls/advapi32/crypt_md5.c:179] in advapi32 (0x0033ee58) 1 0x7d862b2e finalize_hash_impl+0xc1(aiAlgid=0x8003, pHashContext=0x553d6e8, pbHashValue="") [/home/focht/projects/wine/wine-git/dlls/rsaenh/implglue.c:142] in rsaenh (0x0033ee88) 2 0x7d86d3be finalize_hash+0x1c6(pCryptHash=0x553d6c8) [/home/focht/projects/wine/wine-git/dlls/rsaenh/rsaenh.c:721] in rsaenh (0x0033ef28) 3 0x7d872820 RSAENH_CPGetHashParam+0x248(hProv=0x2, hHash=0x4, dwParam=0x2, pbData="`╥SÉ∙ê} `╥Sα≡3", pdwDataLen=0x33f0a8, dwFlags=0) [/home/focht/projects/wine/wine-git/dlls/rsaenh/rsaenh.c:3341] in rsaenh (0x0033ef88) ... Wine-dbg>p *ctx {i={0x20, 0}, buf={0xbf679cb5, 0x58476a19, 0xf7421e19, 0xbace7066}, in="???", digest="????????"} ... --- snip ---
-> final session key
--- snip --- Wine-dbg>x/10x pbHashValue 0x0553d7b8: bf679cb5 58476a19 f7421e19 bace7066 0x0553d7c8: 00000000 00000000 00000000 00000000 0x0553d7d8: 00000000 00000000 ... --- snip ---
When I dumped the key in RSAENH_CPDeriveKey() -> setup_key() -> setup_key_impl() -> rc4_ready() I got a surprise ...
Only 40-bit were actually used!
--- snip --- 0x0553d87c: bf679cb5 00000019 00000000 00000000 0x0553d88c: 00000000 00000000 00000000 00000000 0x0553d89c: 00000000 00000000 00000000 00000000 --- snip ---
/* make RC4 perm and shuffle */ for (x = 0; x < 256; x++) { s[x] = x; }
--- snip --- 0x0553d87c: 03020100 07060504 0b0a0908 0f0e0d0c 0x0553d88c: 13121110 17161514 1b1a1918 1f1e1d1c 0x0553d89c: 23222120 27262524 2b2a2928 2f2e2d2c 0x0553d8ac: 33323130 37363534 3b3a3938 3f3e3d3c --- snip ---
for (j = x = y = 0; x < 256; x++) { y = (y + prng->rc4.buf[x] + key[j++]) & 255; if (j == keylen) { j = 0; } tmp = s[x]; s[x] = s[y]; s[y] = tmp; }
Result:
--- snip --- 0x0553d87c: 7dbb52b5 15a59f9a d2c7bd0f b4f9ebde 0x0553d88c: c5f37acd 341dacf2 047f654c 1f1e161c ... --- snip ---
Source: http://source.winehq.org/git/wine.git/blob/bfd2c533beef454a61b24f92790f91099...
--- snip --- BOOL WINAPI RSAENH_CPDeriveKey(HCRYPTPROV hProv, ALG_ID Algid, HCRYPTHASH hBaseData, DWORD dwFlags, HCRYPTKEY *phKey) { CRYPTKEY *pCryptKey, *pMasterKey; CRYPTHASH *pCryptHash; BYTE abHashValue[RSAENH_MAX_HASH_SIZE*2]; DWORD dwLen; ... case ALG_CLASS_DATA_ENCRYPT: *phKey = new_key(hProv, Algid, dwFlags, &pCryptKey); if (*phKey == (HCRYPTKEY)INVALID_HANDLE_VALUE) return FALSE;
/* * We derive the key material from the hash. * If the hash value is not large enough for the claimed key, we have to construct * a larger binary value based on the hash. This is documented in MSDN: CryptDeriveKey. */ dwLen = RSAENH_MAX_HASH_SIZE; RSAENH_CPGetHashParam(pCryptHash->hProv, hBaseData, HP_HASHVAL, abHashValue, &dwLen, 0);
if (dwLen < pCryptKey->dwKeyLen) { ... }
memcpy(pCryptKey->abKeyValue, abHashValue, RSAENH_MIN(dwLen, sizeof(pCryptKey->abKeyValue))); break; ... default: SetLastError(NTE_BAD_ALGID); return FALSE; }
setup_key(pCryptKey); return TRUE; } --- snip ---
The culprit: http://source.winehq.org/git/wine.git/blob/bfd2c533beef454a61b24f92790f91099...
--- snip --- 3966 memcpy(pCryptKey->abKeyValue, abHashValue, 3967 RSAENH_MIN(pCryptKey->dwKeyLen, sizeof(pCryptKey->abKeyValue))); --- snip ---
IMHO "pCryptKey->dwKeyLen" should be "dwLen"
With that part fixed all files get properly decrypted and the client/launcher actually starts.
Although it seems this game version is abandoned (domain/website for sale) it still proves to be a useful test case for Wine ;-)
$ sha1sum ArchlordEpisode3.exe 52b81c9148536c504fe3d697ddf808d80b2e76ed ArchlordEpisode3.exe
$ du -sh ArchlordEpisode3.exe 1.5G ArchlordEpisode3.exe
$ wine --version wine-1.7.6-109-g917d303
Regards