Commit | Line | Data |
7214af3e |
1 | // License information for this code is in license.txt |
2 | |
3 | #include <string.h> |
4 | #include <ctype.h> |
5 | #include "MpqCrypt.h" |
6 | #include "SFTypes.h" |
7 | |
8 | bool bCryptTableInit = false; |
9 | UInt32 dwCryptTable[0x500]; |
10 | UInt32 dwHashTableKey; |
11 | UInt32 dwBlockTableKey; |
12 | |
13 | // The InitCryptTable, HashString, DecryptData, and DetectFileKey are |
14 | // based on the versions in StormLib which were written by Ladislav |
15 | // Zezula, but may have been modified somewhat by Quantam or ShadowFlare. |
16 | bool InitCryptTable() |
17 | { |
18 | UInt32 seed = 0x00100001; |
19 | UInt32 index1 = 0; |
20 | UInt32 index2 = 0; |
21 | int i; |
22 | |
23 | if (!bCryptTableInit) |
24 | { |
25 | for(index1 = 0; index1 < 0x100; index1++) |
26 | { |
27 | for(index2 = index1, i = 0; i < 5; i++, index2 += 0x100) |
28 | { |
29 | UInt32 temp1, temp2; |
30 | |
31 | seed = (seed * 125 + 3) % 0x2AAAAB; |
32 | temp1 = (seed & 0xFFFF) << 0x10; |
33 | |
34 | seed = (seed * 125 + 3) % 0x2AAAAB; |
35 | temp2 = (seed & 0xFFFF); |
36 | |
37 | dwCryptTable[index2] = (temp1 | temp2); |
38 | } |
39 | } |
40 | |
41 | bCryptTableInit = true; |
42 | } |
43 | |
44 | return true; |
45 | } |
46 | |
47 | UInt32 HashString(const char *lpszString, UInt32 dwHashType) |
48 | { |
49 | UInt32 seed1 = 0x7FED7FED; |
50 | UInt32 seed2 = 0xEEEEEEEE; |
51 | int ch; |
52 | |
53 | char szNull = 0; |
54 | if (!lpszString) |
55 | lpszString = &szNull; |
56 | |
57 | if (dwHashType==HASH_KEY) |
58 | while (strchr(lpszString,'\\')!=NULL) lpszString = strchr(lpszString,'\\')+1; |
59 | while (*lpszString != 0) |
60 | { |
61 | ch = toupper(*lpszString++); |
62 | |
63 | seed1 = dwCryptTable[(dwHashType << 8) + ch] ^ (seed1 + seed2); |
64 | seed2 = ch + seed1 + seed2 + (seed2 << 5) + 3; |
65 | } |
66 | |
67 | return seed1; |
68 | } |
69 | |
70 | // The EncryptData function is based on the DecryptData function by |
71 | // Ladislav Zezula, but adapted by Quantam to encrypt rather than decrypt. |
72 | bool EncryptData(UInt8 *lpbyBuffer, UInt32 dwLength, UInt32 dwKey) |
73 | { |
74 | UInt32 *lpdwBuffer = (UInt32 *)lpbyBuffer; |
75 | UInt32 seed = 0xEEEEEEEE; |
76 | UInt32 ch; |
77 | |
78 | if (!lpbyBuffer) |
79 | return false; |
80 | |
81 | // Round to DWORDs |
82 | dwLength >>= 2; |
83 | |
84 | while(dwLength-- > 0) |
85 | |
86 | { |
87 | seed += dwCryptTable[0x400 + (dwKey & 0xFF)]; |
88 | ch = *lpdwBuffer ^ (dwKey + seed); |
89 | |
90 | dwKey = ((~dwKey << 0x15) + 0x11111111) | (dwKey >> 0x0B); |
91 | seed = *lpdwBuffer + seed + (seed << 5) + 3; |
92 | |
93 | *lpdwBuffer++ = ch; |
94 | } |
95 | |
96 | return true; |
97 | } |
98 | |
99 | bool DecryptData(UInt8 *lpbyBuffer, UInt32 dwLength, UInt32 dwKey) |
100 | { |
101 | UInt32 *lpdwBuffer = (UInt32 *)lpbyBuffer; |
102 | UInt32 seed = 0xEEEEEEEE; |
103 | UInt32 ch; |
104 | |
105 | if (!lpbyBuffer) |
106 | return false; |
107 | |
108 | // Round to DWORDs |
109 | dwLength >>= 2; |
110 | |
111 | while(dwLength-- > 0) |
112 | { |
113 | seed += dwCryptTable[0x400 + (dwKey & 0xFF)]; |
114 | ch = *lpdwBuffer ^ (dwKey + seed); |
115 | |
116 | dwKey = ((~dwKey << 0x15) + 0x11111111) | (dwKey >> 0x0B); |
117 | seed = ch + seed + (seed << 5) + 3; |
118 | |
119 | *lpdwBuffer++ = ch; |
120 | } |
121 | |
122 | return true; |
123 | } |
124 | |
125 | //----------------------------------------------------------------------------- |
126 | // Functions tries to get file decryption key. The trick comes from block |
127 | // positions which are stored at the begin of each compressed file. We know the |
128 | // file size, that means we know number of blocks that means we know the first |
129 | // DWORD value in block position. And if we know encrypted and decrypted value, |
130 | // we can find the decryption key !!! |
131 | // |
132 | // hf - MPQ file handle |
133 | // block - DWORD array of block positions |
134 | // ch - Decrypted value of the first block pos |
135 | |
136 | UInt32 DetectFileSeed(UInt32 * block, UInt32 decrypted, UInt32 blocksize) |
137 | { |
138 | UInt32 saveSeed1; |
139 | UInt32 temp = *block ^ decrypted; // temp = seed1 + seed2 |
140 | // temp = seed1 + stormBuffer[0x400 + (seed1 & 0xFF)] + 0xEEEEEEEE |
141 | temp -= 0xEEEEEEEE; // temp = seed1 + stormBuffer[0x400 + (seed1 & 0xFF)] |
142 | |
143 | |
144 | for(int i = 0; i < 0x100; i++) // Try all 256 possibilities |
145 | { |
146 | UInt32 seed1; |
147 | UInt32 seed2 = 0xEEEEEEEE; |
148 | UInt32 ch; |
149 | |
150 | // Try the first DWORD (We exactly know the value) |
151 | seed1 = temp - dwCryptTable[0x400 + i]; |
152 | seed2 += dwCryptTable[0x400 + (seed1 & 0xFF)]; |
153 | ch = block[0] ^ (seed1 + seed2); |
154 | |
155 | if(ch != decrypted) |
156 | continue; |
157 | |
158 | saveSeed1 = seed1 + 1; |
159 | |
160 | // If OK, continue and test the second value. We don't know exactly the value, |
161 | // but we know that the second one has a value less than or equal to the |
162 | // size of the block position table plus the block size |
163 | seed1 = ((~seed1 << 0x15) + 0x11111111) | (seed1 >> 0x0B); |
164 | seed2 = ch + seed2 + (seed2 << 5) + 3; |
165 | |
166 | seed2 += dwCryptTable[0x400 + (seed1 & 0xFF)]; |
167 | ch = block[1] ^ (seed1 + seed2); |
168 | |
169 | if(ch <= decrypted + blocksize) |
170 | return saveSeed1; |
171 | } |
172 | return 0; |
173 | } |