[Bug 16365] Archlord Episode 3 Client crashes on startup (decrypting files with a RC4 session key derived from MD5 hash fails, only 40 bits are used, salt is dropped)
wine-bugs at winehq.org
wine-bugs at winehq.org
Sun Nov 10 07:08:26 CST 2013
http://bugs.winehq.org/show_bug.cgi?id=16365
Anastasius Focht <focht at gmx.net> changed:
What |Removed |Added
----------------------------------------------------------------------------
URL| |http://www.gamefront.com/fi
| |les/13750760/
CC| |focht at 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 at 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 at 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 at 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 at 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 at 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/bfd2c533beef454a61b24f92790f91099e607365:/dlls/rsaenh/rsaenh.c#l3896
--- 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/bfd2c533beef454a61b24f92790f91099e607365:/dlls/rsaenh/rsaenh.c#l3966
--- 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
--
Configure bugmail: http://bugs.winehq.org/userprefs.cgi?tab=email
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