StuffIt 1-4 archive password cracker
1/*
2 * StuffIt 1-4 Password Cracker
3 * written by Claude
4 *
5 * This code is released to the public domain.
6 */
7
8#include <stdio.h>
9#include <stdlib.h>
10#include <string.h>
11#include <stdint.h>
12#include <signal.h>
13#include <err.h>
14
15/* character set for brute force is all printable ASCII */
16static const char charset[] =
17 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()-_=+[]{}|;:',.<>?/`~ ";
18
19typedef struct {
20 uint32_t subkeys[16][2];
21} StuffItDESKeySchedule;
22
23unsigned long long total_tested = 0;
24char last_password[9] = { 0 };
25StuffItDESKeySchedule initial_ks;
26int initial_ks_ready = 0;
27
28void siginfo_handler(int sig);
29void usage(char *p);
30int parse_hex(const char *hex, uint8_t *out, size_t len);
31uint32_t ReverseBits(uint32_t val);
32int test_password(const uint8_t mkey[8], const uint8_t passblock[8]);
33void brute_force(const uint8_t mkey[8], uint8_t *passblock, int pos, int max_len);
34static void StuffItDESSetKey(const uint8_t key[8], StuffItDESKeySchedule *ks);
35static inline void Encrypt(uint32_t *left, uint32_t right, uint32_t *subkey);
36static void StuffItDESCrypt(uint8_t data[8], StuffItDESKeySchedule *ks, int enc);
37
38int
39main(int argc, char **argv)
40{
41 uint8_t mkey[8];
42 uint8_t passblock[9] = {0};
43 int wflag = 0;
44 int min_len = -1, max_len = -1;
45
46 if (argc < 2)
47 usage(argv[0]);
48
49 if (parse_hex(argv[1], mkey, 8) != 0)
50 errx(1, "mkey must be 16 hex characters");
51
52 if (argc == 3 && strcmp(argv[2], "-w") == 0)
53 wflag = 1;
54 else if (argc == 4) {
55 min_len = atoi(argv[2]);
56 max_len = atoi(argv[3]);
57 if (min_len < 1)
58 errx(1, "invalid min value");
59 if (max_len > 8)
60 errx(1, "invalid max value");
61 if (min_len > max_len)
62 errx(1, "min cannot be greater than max");
63 } else
64 usage(argv[0]);
65
66 signal(SIGINFO, siginfo_handler);
67
68 /* wordlist mode */
69 if (wflag) {
70 char line[256];
71
72 while (fgets(line, sizeof(line), stdin)) {
73 size_t len = strlen(line);
74 if (len > 0 && line[len-1] == '\n')
75 line[--len] = 0;
76 if (len > 0 && line[len-1] == '\r')
77 line[--len] = 0;
78 if (len > 8)
79 len = 8;
80
81 memset(passblock, 0, sizeof(passblock));
82 memcpy(passblock, line, len);
83
84 /* for SIGINFO */
85 strncpy(last_password, line, 8);
86 last_password[8] = 0;
87
88 total_tested++;
89 if (test_password(mkey, passblock))
90 printf("%s\n", line);
91 }
92 return 0;
93 }
94
95 /* brute force mode */
96 for (int len = min_len; len <= max_len; len++) {
97 memset(passblock, 0, sizeof(passblock));
98 brute_force(mkey, passblock, 0, len);
99 }
100
101 return 0;
102}
103
104void
105siginfo_handler(int sig)
106{
107 (void)sig;
108 warnx("tested %llu passwords, last: %s", total_tested, last_password);
109}
110
111void
112usage(char *p)
113{
114 printf("StuffIt 1-4 archive password cracker\n");
115 printf("\n");
116 printf("Usage:\n");
117 printf(" %s <mkey_hex> <min> <max> - brute force length range\n", p);
118 printf(" %s <mkey_hex> -w - read passwords from stdin\n", p);
119 printf("\n");
120 printf("mkey_hex are 16 hex characters from the archive's 'MKey' "
121 "in its resource fork.\n");
122 printf("\n");
123 printf("Examples:\n");
124 printf("\tgzip -cd rockyou.txt.gz | %s 590e0f73e71c9b60 -w\n", p);
125 printf("\t%s 590e0f73e71c9b60 3 8\n", p);
126 printf("\n");
127 printf("Multiple passwords may verify against the hash, but will not "
128 "unlock the\n");
129 printf("archive (and will likely crash StuffIt). Let this keep "
130 "running while\n");
131 printf("you try the most likely of the passwords found.\n");
132 exit(1);
133}
134
135
136int
137parse_hex(const char *hex, uint8_t *out, size_t len)
138{
139 if (strlen(hex) != len * 2)
140 return -1;
141
142 for (size_t i = 0; i < len; i++) {
143 int hi, lo;
144 char c;
145
146 c = hex[i * 2];
147 if (c >= '0' && c <= '9')
148 hi = c - '0';
149 else if (c >= 'a' && c <= 'f')
150 hi = c - 'a' + 10;
151 else if (c >= 'A' && c <= 'F')
152 hi = c - 'A' + 10;
153 else
154 return -1;
155
156 c = hex[i * 2 + 1];
157 if (c >= '0' && c <= '9')
158 lo = c - '0';
159 else if (c >= 'a' && c <= 'f')
160 lo = c - 'a' + 10;
161 else if (c >= 'A' && c <= 'F')
162 lo = c - 'A' + 10;
163 else
164 return -1;
165
166 out[i] = (hi << 4) | lo;
167 }
168
169 return 0;
170}
171
172int
173test_password(const uint8_t mkey[8], const uint8_t passblock[8])
174{
175 StuffItDESKeySchedule ks;
176 uint8_t archivekey[8], archiveiv[8], verifyblock[8];
177 static const uint8_t initialkey[8] = {
178 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef
179 };
180
181 for (int i = 0; i < 8; i++)
182 archivekey[i] = initialkey[i] ^ (passblock[i] & 0x7f);
183
184 /* Initialize key schedule for initial key (once) */
185 if (!initial_ks_ready) {
186 StuffItDESSetKey(initialkey, &initial_ks);
187 initial_ks_ready = 1;
188 }
189
190 /* Encrypt archive key with initial key */
191 StuffItDESCrypt(archivekey, &initial_ks, 1);
192
193 /* Decrypt MKey to get archive IV */
194 memcpy(archiveiv, mkey, 8);
195 StuffItDESSetKey(archivekey, &ks);
196 StuffItDESCrypt(archiveiv, &ks, 0);
197
198 /* Verify: encrypt first 4 bytes of IV + {0,0,0,4} */
199 memcpy(verifyblock, archiveiv, 4);
200 verifyblock[4] = verifyblock[5] = verifyblock[6] = 0;
201 verifyblock[7] = 4;
202 StuffItDESCrypt(verifyblock, &ks, 1);
203
204 /* Check last 4 bytes match */
205 return memcmp(verifyblock + 4, archiveiv + 4, 4) == 0;
206}
207
208void
209brute_force(const uint8_t mkey[8], uint8_t *passblock, int pos, int max_len)
210{
211 int i;
212
213 if (pos > 0) {
214 for (i = 0; i < 8; i++)
215 last_password[i] = passblock[i];
216 last_password[8] = 0;
217
218 if (test_password(mkey, passblock)) {
219 /* found a match - convert to printable string */
220 char pwd[9] = { 0 };
221 for (i = 0; i < 8 && passblock[i]; i++)
222 pwd[i] = passblock[i];
223 printf("%s\n", pwd);
224 }
225 total_tested++;
226 }
227
228 if (pos >= max_len)
229 return;
230
231 for (i = 0; i < (int)sizeof(charset) - 1; i++) {
232 passblock[pos] = charset[i];
233 passblock[pos + 1] = 0;
234 brute_force(mkey, passblock, pos + 1, max_len);
235 }
236 passblock[pos] = 0;
237}
238
239static inline
240uint32_t RotateRight(uint32_t val, int n)
241{
242 return (val >> n) | (val << (32 - n));
243}
244
245uint32_t
246ReverseBits(uint32_t val)
247{
248 val = ((val >> 1) & 0x55555555) | ((val & 0x55555555) << 1);
249 val = ((val >> 2) & 0x33333333) | ((val & 0x33333333) << 2);
250 val = ((val >> 4) & 0x0F0F0F0F) | ((val & 0x0F0F0F0F) << 4);
251 val = ((val >> 8) & 0x00FF00FF) | ((val & 0x00FF00FF) << 8);
252 return (val >> 16) | (val << 16);
253}
254
255static inline uint32_t Nibble(const uint8_t key[8], int n)
256{
257 return (key[(n & 0x0f) >> 1] >> (((n ^ 1) & 1) << 2)) & 0x0f;
258}
259
260static const uint32_t DES_SPtrans[8][64] = {
261 {0x02080800,0x00080000,0x02000002,0x02080802,0x02000000,0x00080802,0x00080002,0x02000002,0x00080802,0x02080800,0x02080000,0x00000802,0x02000802,0x02000000,0x00000000,0x00080002,0x00080000,0x00000002,0x02000800,0x00080800,0x02080802,0x02080000,0x00000802,0x02000800,0x00000002,0x00000800,0x00080800,0x02080002,0x00000800,0x02000802,0x02080002,0x00000000,0x00000000,0x02080802,0x02000800,0x00080002,0x02080800,0x00080000,0x00000802,0x02000800,0x02080002,0x00000800,0x00080800,0x02000002,0x00080802,0x00000002,0x02000002,0x02080000,0x02080802,0x00080800,0x02080000,0x02000802,0x02000000,0x00000802,0x00080002,0x00000000,0x00080000,0x02000000,0x02000802,0x02080800,0x00000002,0x02080002,0x00000800,0x00080802},
262 {0x40108010,0x00000000,0x00108000,0x40100000,0x40000010,0x00008010,0x40008000,0x00108000,0x00008000,0x40100010,0x00000010,0x40008000,0x00100010,0x40108000,0x40100000,0x00000010,0x00100000,0x40008010,0x40100010,0x00008000,0x00108010,0x40000000,0x00000000,0x00100010,0x40008010,0x00108010,0x40108000,0x40000010,0x40000000,0x00100000,0x00008010,0x40108010,0x00100010,0x40108000,0x40008000,0x00108010,0x40108010,0x00100010,0x40000010,0x00000000,0x40000000,0x00008010,0x00100000,0x40100010,0x00008000,0x40000000,0x00108010,0x40008010,0x40108000,0x00008000,0x00000000,0x40000010,0x00000010,0x40108010,0x00108000,0x40100000,0x40100010,0x00100000,0x00008010,0x40008000,0x40008010,0x00000010,0x40100000,0x00108000},
263 {0x04000001,0x04040100,0x00000100,0x04000101,0x00040001,0x04000000,0x04000101,0x00040100,0x04000100,0x00040000,0x04040000,0x00000001,0x04040101,0x00000101,0x00000001,0x04040001,0x00000000,0x00040001,0x04040100,0x00000100,0x00000101,0x04040101,0x00040000,0x04000001,0x04040001,0x04000100,0x00040101,0x04040000,0x00040100,0x00000000,0x04000000,0x00040101,0x04040100,0x00000100,0x00000001,0x00040000,0x00000101,0x00040001,0x04040000,0x04000101,0x00000000,0x04040100,0x00040100,0x04040001,0x00040001,0x04000000,0x04040101,0x00000001,0x00040101,0x04000001,0x04000000,0x04040101,0x00040000,0x04000100,0x04000101,0x00040100,0x04000100,0x00000000,0x04040001,0x00000101,0x04000001,0x00040101,0x00000100,0x04040000},
264 {0x00401008,0x10001000,0x00000008,0x10401008,0x00000000,0x10400000,0x10001008,0x00400008,0x10401000,0x10000008,0x10000000,0x00001008,0x10000008,0x00401008,0x00400000,0x10000000,0x10400008,0x00401000,0x00001000,0x00000008,0x00401000,0x10001008,0x10400000,0x00001000,0x00001008,0x00000000,0x00400008,0x10401000,0x10001000,0x10400008,0x10401008,0x00400000,0x10400008,0x00001008,0x00400000,0x10000008,0x00401000,0x10001000,0x00000008,0x10400000,0x10001008,0x00000000,0x00001000,0x00400008,0x00000000,0x10400008,0x10401000,0x00001000,0x10000000,0x10401008,0x00401008,0x00400000,0x10401008,0x00000008,0x10001000,0x00401008,0x00400008,0x00401000,0x10400000,0x10001008,0x00001008,0x10000000,0x10000008,0x10401000},
265 {0x08000000,0x00010000,0x00000400,0x08010420,0x08010020,0x08000400,0x00010420,0x08010000,0x00010000,0x00000020,0x08000020,0x00010400,0x08000420,0x08010020,0x08010400,0x00000000,0x00010400,0x08000000,0x00010020,0x00000420,0x08000400,0x00010420,0x00000000,0x08000020,0x00000020,0x08000420,0x08010420,0x00010020,0x08010000,0x00000400,0x00000420,0x08010400,0x08010400,0x08000420,0x00010020,0x08010000,0x00010000,0x00000020,0x08000020,0x08000400,0x08000000,0x00010400,0x08010420,0x00000000,0x00010420,0x08000000,0x00000400,0x00010020,0x08000420,0x00000400,0x00000000,0x08010420,0x08010020,0x08010400,0x00000420,0x00010000,0x00010400,0x08010020,0x08000400,0x00000420,0x00000020,0x00010420,0x08010000,0x08000020},
266 {0x80000040,0x00200040,0x00000000,0x80202000,0x00200040,0x00002000,0x80002040,0x00200000,0x00002040,0x80202040,0x00202000,0x80000000,0x80002000,0x80000040,0x80200000,0x00202040,0x00200000,0x80002040,0x80200040,0x00000000,0x00002000,0x00000040,0x80202000,0x80200040,0x80202040,0x80200000,0x80000000,0x00002040,0x00000040,0x00202000,0x00202040,0x80002000,0x00002040,0x80000000,0x80002000,0x00202040,0x80202000,0x00200040,0x00000000,0x80002000,0x80000000,0x00002000,0x80200040,0x00200000,0x00200040,0x80202040,0x00202000,0x00000040,0x80202040,0x00202000,0x00200000,0x80002040,0x80000040,0x80200000,0x00202040,0x00000000,0x00002000,0x80000040,0x80002040,0x80202000,0x80200000,0x00002040,0x00000040,0x80200040},
267 {0x00004000,0x00000200,0x01000200,0x01000004,0x01004204,0x00004004,0x00004200,0x00000000,0x01000000,0x01000204,0x00000204,0x01004000,0x00000004,0x01004200,0x01004000,0x00000204,0x01000204,0x00004000,0x00004004,0x01004204,0x00000000,0x01000200,0x01000004,0x00004200,0x01004004,0x00004204,0x01004200,0x00000004,0x00004204,0x01004004,0x00000200,0x01000000,0x00004204,0x01004000,0x01004004,0x00000204,0x00004000,0x00000200,0x01000000,0x01004004,0x01000204,0x00004204,0x00004200,0x00000000,0x00000200,0x01000004,0x00000004,0x01000200,0x00000000,0x01000204,0x01000200,0x00004200,0x00000204,0x00004000,0x01004204,0x01000000,0x01004200,0x00000004,0x00004004,0x01004204,0x01000004,0x01004200,0x01004000,0x00004004},
268 {0x20800080,0x20820000,0x00020080,0x00000000,0x20020000,0x00800080,0x20800000,0x20820080,0x00000080,0x20000000,0x00820000,0x00020080,0x00820080,0x20020080,0x20000080,0x20800000,0x00020000,0x00820080,0x00800080,0x20020000,0x20820080,0x20000080,0x00000000,0x00820000,0x20000000,0x00800000,0x20020080,0x20800080,0x00800000,0x00020000,0x20820000,0x00000080,0x00800000,0x00020000,0x20000080,0x20820080,0x00020080,0x20000000,0x00000000,0x00820000,0x20800080,0x20020080,0x20020000,0x00800080,0x20820000,0x00000080,0x00800080,0x20020000,0x20820080,0x00800000,0x20800000,0x20000080,0x00820000,0x00020080,0x20020080,0x20800000,0x00000080,0x20820000,0x00820080,0x00000000,0x20000000,0x20800080,0x00020000,0x00820080}
269};
270
271static void
272StuffItDESSetKey(const uint8_t key[8], StuffItDESKeySchedule *ks)
273{
274 for (int i = 0; i < 16; i++) {
275 uint32_t subkey1 = ((Nibble(key, i) >> 2) | (Nibble(key, i + 13) << 2));
276 subkey1 |= ((Nibble(key, i + 11) >> 2) | (Nibble(key, i + 6) << 2)) << 8;
277 subkey1 |= ((Nibble(key, i + 3) >> 2) | (Nibble(key, i + 10) << 2)) << 16;
278 subkey1 |= ((Nibble(key, i + 8) >> 2) | (Nibble(key, i + 1) << 2)) << 24;
279 uint32_t subkey0 = ((Nibble(key, i + 9) | (Nibble(key, i) << 4)) & 0x3f);
280 subkey0 |= ((Nibble(key, i + 2) | (Nibble(key, i + 11) << 4)) & 0x3f) << 8;
281 subkey0 |= ((Nibble(key, i + 14) | (Nibble(key, i + 3) << 4)) & 0x3f) << 16;
282 subkey0 |= ((Nibble(key, i + 5) | (Nibble(key, i + 8) << 4)) & 0x3f) << 24;
283 ks->subkeys[i][0] = ReverseBits(subkey1);
284 ks->subkeys[i][1] = ReverseBits(subkey0);
285 }
286}
287
288static inline
289void Encrypt(uint32_t *left, uint32_t right, uint32_t *subkey)
290{
291 uint32_t u = right ^ subkey[0];
292 uint32_t t = RotateRight(right, 4) ^ subkey[1];
293 *left ^= DES_SPtrans[0][(u >> 2) & 0x3f] ^ DES_SPtrans[2][(u >> 10) & 0x3f] ^
294 DES_SPtrans[4][(u >> 18) & 0x3f] ^ DES_SPtrans[6][(u >> 26) & 0x3f] ^
295 DES_SPtrans[1][(t >> 2) & 0x3f] ^ DES_SPtrans[3][(t >> 10) & 0x3f] ^
296 DES_SPtrans[5][(t >> 18) & 0x3f] ^ DES_SPtrans[7][(t >> 26) & 0x3f];
297}
298
299static inline
300uint32_t ReadBE32(const uint8_t *p)
301{
302 return ((uint32_t)p[0] << 24) | ((uint32_t)p[1] << 16) | ((uint32_t)p[2] << 8) | p[3];
303}
304
305static inline
306void WriteBE32(uint8_t *p, uint32_t v)
307{
308 p[0] = (v >> 24);
309 p[1] = (v >> 16);
310 p[2] = (v >> 8);
311 p[3] = v;
312}
313
314static void
315StuffItDESCrypt(uint8_t data[8], StuffItDESKeySchedule *ks, int enc)
316{
317 uint32_t left = ReverseBits(ReadBE32(&data[0]));
318 uint32_t right = ReverseBits(ReadBE32(&data[4]));
319
320 right = RotateRight(right, 29);
321 left = RotateRight(left, 29);
322
323 if (enc) {
324 for (int i = 0; i < 16; i += 2) {
325 Encrypt(&left, right, ks->subkeys[i]);
326 Encrypt(&right, left, ks->subkeys[i + 1]);
327 }
328 } else {
329 for (int i = 15; i > 0; i -= 2) {
330 Encrypt(&left, right, ks->subkeys[i]);
331 Encrypt(&right, left, ks->subkeys[i - 1]);
332 }
333 }
334
335 left = RotateRight(left, 3);
336 right = RotateRight(right, 3);
337
338 WriteBE32(&data[0], ReverseBits(right));
339 WriteBE32(&data[4], ReverseBits(left));
340}