diff options
Diffstat (limited to 'utils/nwztools/upgtools/keysig_search.c')
-rw-r--r-- | utils/nwztools/upgtools/keysig_search.c | 344 |
1 files changed, 257 insertions, 87 deletions
diff --git a/utils/nwztools/upgtools/keysig_search.c b/utils/nwztools/upgtools/keysig_search.c index 7652efa233..2740dc1461 100644 --- a/utils/nwztools/upgtools/keysig_search.c +++ b/utils/nwztools/upgtools/keysig_search.c | |||
@@ -23,6 +23,118 @@ | |||
23 | #include "mg.h" | 23 | #include "mg.h" |
24 | #include <string.h> | 24 | #include <string.h> |
25 | #include <stdio.h> | 25 | #include <stdio.h> |
26 | #include <stdlib.h> | ||
27 | #include <pthread.h> | ||
28 | |||
29 | /** Generic search code */ | ||
30 | |||
31 | /* The generator sends chunks to the workers. The exact type of chunks depends | ||
32 | * on the method used. */ | ||
33 | static struct | ||
34 | { | ||
35 | pthread_mutex_t mutex; /* mutex for the whole structure */ | ||
36 | pthread_cond_t avail_cond; /* condition to signal available or stop */ | ||
37 | pthread_cond_t req_cond; /* condition to signal request or stop */ | ||
38 | bool stop; /* if true, stop searcg */ | ||
39 | void *chunk; /* pointer to chunk (NULL if not available) */ | ||
40 | size_t chunk_sz; /* chunk size */ | ||
41 | }g_producer; | ||
42 | |||
43 | /* init producer */ | ||
44 | static void producer_init(void) | ||
45 | { | ||
46 | pthread_cond_init(&g_producer.avail_cond, NULL); | ||
47 | pthread_cond_init(&g_producer.req_cond, NULL); | ||
48 | pthread_mutex_init(&g_producer.mutex, NULL); | ||
49 | g_producer.stop = false; | ||
50 | g_producer.chunk = NULL; | ||
51 | g_producer.chunk_sz = 0; | ||
52 | } | ||
53 | |||
54 | /* consumer get: called by worker to get a new chunk, return NULL to stop */ | ||
55 | static void *consumer_get(size_t *sz) | ||
56 | { | ||
57 | pthread_mutex_lock(&g_producer.mutex); | ||
58 | /* loop until stop or new chunk */ | ||
59 | while(true) | ||
60 | { | ||
61 | /* stop if requested */ | ||
62 | if(g_producer.stop) | ||
63 | { | ||
64 | pthread_mutex_unlock(&g_producer.mutex); | ||
65 | return NULL; | ||
66 | } | ||
67 | if(g_producer.chunk) | ||
68 | break; | ||
69 | /* request a new chunk */ | ||
70 | pthread_cond_signal(&g_producer.req_cond); | ||
71 | /* wait for availability */ | ||
72 | pthread_cond_wait(&g_producer.avail_cond, &g_producer.mutex); | ||
73 | } | ||
74 | void *c = g_producer.chunk; | ||
75 | if(sz) | ||
76 | *sz = g_producer.chunk_sz; | ||
77 | g_producer.chunk = NULL; | ||
78 | pthread_mutex_unlock(&g_producer.mutex); | ||
79 | /* request a new chunk, so that if other consumers are waiting, the producer | ||
80 | * will also wake them up */ | ||
81 | pthread_cond_signal(&g_producer.req_cond); | ||
82 | return c; | ||
83 | } | ||
84 | |||
85 | /* stop: called by worker to stop the search */ | ||
86 | static void consumer_stop(void) | ||
87 | { | ||
88 | pthread_mutex_lock(&g_producer.mutex); | ||
89 | /* set stop */ | ||
90 | g_producer.stop = true; | ||
91 | /* wake up everyone */ | ||
92 | pthread_cond_broadcast(&g_producer.req_cond); | ||
93 | pthread_cond_broadcast(&g_producer.avail_cond); | ||
94 | pthread_mutex_unlock(&g_producer.mutex); | ||
95 | } | ||
96 | |||
97 | /* producer yield: called by generator to give a new chunk, return true to stop */ | ||
98 | static bool producer_yield(void *chunk, size_t sz) | ||
99 | { | ||
100 | pthread_mutex_lock(&g_producer.mutex); | ||
101 | /* wait until stop or request */ | ||
102 | while(true) | ||
103 | { | ||
104 | /* stop if requested */ | ||
105 | if(g_producer.stop) | ||
106 | { | ||
107 | pthread_mutex_unlock(&g_producer.mutex); | ||
108 | return true; | ||
109 | } | ||
110 | /* if the chunk is empty, fill it */ | ||
111 | if(g_producer.chunk == NULL) | ||
112 | break; | ||
113 | /* otherwise wait for request */ | ||
114 | pthread_cond_wait(&g_producer.req_cond, &g_producer.mutex); | ||
115 | } | ||
116 | g_producer.chunk = malloc(sz); | ||
117 | memcpy(g_producer.chunk, chunk, sz); | ||
118 | g_producer.chunk_sz = sz; | ||
119 | /* signal availability */ | ||
120 | pthread_cond_signal(&g_producer.avail_cond); | ||
121 | pthread_mutex_unlock(&g_producer.mutex); | ||
122 | return false; | ||
123 | } | ||
124 | |||
125 | static void producer_stop(void) | ||
126 | { | ||
127 | pthread_mutex_lock(&g_producer.mutex); | ||
128 | /* if we are not already stopping and there is a chunk still waiting to | ||
129 | * be consumed, wait until next request */ | ||
130 | if(!g_producer.stop && g_producer.chunk) | ||
131 | pthread_cond_wait(&g_producer.req_cond, &g_producer.mutex); | ||
132 | /* set stop */ | ||
133 | g_producer.stop = true; | ||
134 | /* wake up everyone */ | ||
135 | pthread_cond_broadcast(&g_producer.avail_cond); | ||
136 | pthread_mutex_unlock(&g_producer.mutex); | ||
137 | } | ||
26 | 138 | ||
27 | /* Key search methods | 139 | /* Key search methods |
28 | * | 140 | * |
@@ -41,16 +153,18 @@ | |||
41 | * towards very unbalanced strings (only digits or only letters). | 153 | * towards very unbalanced strings (only digits or only letters). |
42 | */ | 154 | */ |
43 | 155 | ||
44 | static uint8_t g_cipher[8]; | 156 | static struct |
45 | static keysig_notify_fn_t g_notify; | 157 | { |
46 | static uint8_t g_key[8]; | 158 | pthread_mutex_t mutex; |
47 | static void *g_user; | 159 | uint8_t *enc_buf; |
160 | size_t enc_buf_sz; | ||
161 | bool found_keysig; | ||
162 | uint8_t key[NWZ_KEY_SIZE]; /* result */ | ||
163 | uint8_t sig[NWZ_SIG_SIZE]; /* result */ | ||
164 | }g_keysig_search; | ||
165 | |||
48 | static bool is_hex[256]; | 166 | static bool is_hex[256]; |
49 | static bool is_init = false; | 167 | static bool is_init = false; |
50 | static uint64_t g_tot_count; | ||
51 | static uint64_t g_cur_count; | ||
52 | static int g_last_percent; | ||
53 | static int g_last_subpercent; | ||
54 | 168 | ||
55 | static void keysig_search_init() | 169 | static void keysig_search_init() |
56 | { | 170 | { |
@@ -71,105 +185,169 @@ static inline bool is_full_ascii(uint8_t *arr) | |||
71 | return true; | 185 | return true; |
72 | } | 186 | } |
73 | 187 | ||
74 | static inline bool check_stupid() | 188 | struct upg_header_t |
75 | { | 189 | { |
76 | uint8_t res[8]; | 190 | uint8_t sig[NWZ_SIG_SIZE]; |
77 | // display progress | 191 | uint32_t nr_files; |
78 | g_cur_count++; | 192 | uint32_t pad; // make sure structure size is a multiple of 8 |
79 | int percent = (g_cur_count * 100ULL) / g_tot_count; | 193 | } __attribute__((packed)); |
80 | int subpercent = ((g_cur_count * 1000ULL) / g_tot_count) % 10; | 194 | |
81 | if(percent != g_last_percent) | 195 | static bool check_key(uint8_t key[NWZ_KEY_SIZE]) |
82 | { | 196 | { |
83 | cprintf(RED, "%d%%", percent); | 197 | struct upg_header_t hdr; |
84 | fflush(stdout); | 198 | mg_decrypt_fw(g_keysig_search.enc_buf, sizeof(hdr.sig), (void *)&hdr, key); |
85 | g_last_subpercent = 0; | 199 | if(is_full_ascii(hdr.sig)) |
86 | } | ||
87 | if(subpercent != g_last_subpercent) | ||
88 | { | 200 | { |
89 | cprintf(WHITE, "."); | 201 | /* the signature looks correct, so decrypt the header futher to be sure */ |
90 | fflush(stdout); | 202 | mg_decrypt_fw(g_keysig_search.enc_buf, sizeof(hdr), (void *)&hdr, key); |
203 | /* we expect the number of files to be small and the padding to be 0 */ | ||
204 | if(hdr.nr_files == 0 || hdr.nr_files > 10 || hdr.pad != 0) | ||
205 | return false; | ||
206 | cprintf(RED, " Found key: %.8s (sig=%.8s, nr_files=%u)\n", key, hdr.sig, (unsigned)hdr.nr_files); | ||
207 | pthread_mutex_lock(&g_keysig_search.mutex); | ||
208 | g_keysig_search.found_keysig = true; | ||
209 | memcpy(g_keysig_search.key, key, NWZ_KEY_SIZE); | ||
210 | memcpy(g_keysig_search.sig, hdr.sig, NWZ_SIG_SIZE); | ||
211 | pthread_mutex_unlock(&g_keysig_search.mutex); | ||
212 | consumer_stop(); | ||
213 | return true; | ||
91 | } | 214 | } |
92 | g_last_percent = percent; | ||
93 | g_last_subpercent = subpercent; | ||
94 | |||
95 | mg_decrypt_fw(g_cipher, 8, res, g_key); | ||
96 | if(is_full_ascii(res)) | ||
97 | return g_notify(g_user, g_key, res); | ||
98 | return false; | 215 | return false; |
99 | } | 216 | } |
100 | 217 | ||
101 | static bool search_stupid_rec(int rem_digit, int rem_letter, int pos) | 218 | /** Hex search */ |
219 | |||
220 | struct hex_chunk_t | ||
102 | { | 221 | { |
103 | if(pos == 8) | 222 | uint8_t key[NWZ_KEY_SIZE]; /* partially pre-filled key */ |
104 | return check_stupid(); | 223 | int pos; |
105 | if(rem_digit > 0) | 224 | int rem_letters; |
225 | int rem_digits; | ||
226 | }; | ||
227 | |||
228 | static bool hex_rec(bool producer, struct hex_chunk_t *ch) | ||
229 | { | ||
230 | /* we list the first 4 pos in generator, and remaining 4 in workers */ | ||
231 | if(producer && ch->pos == 4) | ||
232 | { | ||
233 | //printf("yield(%.8s,%d,%d,%d)\n", ch->key, ch->pos, ch->rem_digits, ch->rem_letters); | ||
234 | return producer_yield(ch, sizeof(struct hex_chunk_t)); | ||
235 | } | ||
236 | /* filled the key ? */ | ||
237 | if(!producer && ch->pos == NWZ_KEY_SIZE) | ||
238 | return check_key(ch->key); | ||
239 | /* list next possibilities | ||
240 | * | ||
241 | * NOTE (42) Since the cipher is DES, the key is actually 56-bit: the least | ||
242 | * significant bit of each byte is an (unused) parity bit. We thus only | ||
243 | * generate keys where the least significant bit is 0. */ | ||
244 | int p = ch->pos++; | ||
245 | if(ch->rem_digits > 0) | ||
106 | { | 246 | { |
107 | for(int i = '0'; i <= '9'; i++) | 247 | ch->rem_digits--; |
248 | /* NOTE (42) */ | ||
249 | for(int i = '0'; i <= '9'; i += 2) | ||
108 | { | 250 | { |
109 | g_key[pos] = i; | 251 | ch->key[p] = i; |
110 | if(search_stupid_rec(rem_digit - 1, rem_letter, pos + 1)) | 252 | if(hex_rec(producer, ch)) |
111 | return true; | 253 | return true; |
112 | } | 254 | } |
255 | ch->rem_digits++; | ||
113 | } | 256 | } |
114 | if(rem_letter > 0) | 257 | if(ch->rem_letters > 0) |
115 | { | 258 | { |
116 | for(int i = 'a' - 1; i <= 'f'; i++) | 259 | ch->rem_letters--; |
260 | /* NOTE (42) */ | ||
261 | for(int i = 'a'; i <= 'f'; i += 2) | ||
117 | { | 262 | { |
118 | g_key[pos] = i; | 263 | ch->key[p] = i; |
119 | if(search_stupid_rec(rem_digit, rem_letter - 1, pos + 1)) | 264 | if(hex_rec(producer, ch)) |
120 | return true; | 265 | return true; |
121 | } | 266 | } |
267 | ch->rem_letters++; | ||
122 | } | 268 | } |
269 | ch->pos--; | ||
123 | return false; | 270 | return false; |
124 | } | 271 | } |
125 | 272 | ||
126 | static bool search_stupid(int rem_digit, int rem_letter) | 273 | static void *hex_worker(void *arg) |
127 | { | 274 | { |
128 | cprintf(WHITE, "\n Looking for keys with "); | 275 | (void) arg; |
129 | cprintf(RED, "%d", rem_digit); | 276 | while(true) |
130 | cprintf(WHITE, " digits and "); | 277 | { |
131 | cprintf(RED, "%d", rem_letter); | 278 | struct hex_chunk_t *ch = consumer_get(NULL); |
132 | cprintf(WHITE, " letters..."); | 279 | if(ch == NULL) |
133 | fflush(stdout); | 280 | break; |
134 | return search_stupid_rec(rem_digit, rem_letter, 0); | 281 | hex_rec(false, ch); |
282 | } | ||
283 | return NULL; | ||
135 | } | 284 | } |
136 | 285 | ||
137 | bool keysig_search_ascii_stupid(uint8_t *cipher, keysig_notify_fn_t notify, void *user) | 286 | static bool hex_producer_list(int nr_digits, int nr_letters) |
138 | { | 287 | { |
139 | keysig_search_init(); | 288 | struct hex_chunk_t ch; |
140 | memcpy(g_cipher, cipher, 8); | 289 | cprintf(BLUE, " Listing keys with %d letters and %d digits\n", nr_letters, |
141 | g_notify = notify; | 290 | nr_digits); |
142 | g_user = user; | 291 | memset(ch.key, ' ', 8); |
143 | // compute number of possibilities | 292 | ch.pos = 0; |
144 | g_cur_count = 0; | 293 | ch.rem_letters = nr_letters; |
145 | g_tot_count = 1; | 294 | ch.rem_digits = nr_digits; |
146 | g_last_percent = -1; | 295 | return hex_rec(true, &ch); |
147 | for(int i = 0; i < 8; i++) | 296 | } |
148 | g_tot_count *= 16ULL; | 297 | |
149 | cprintf(WHITE, " Search space:"); | 298 | void *hex_producer(void *arg) |
150 | cprintf(RED, " %llu", (unsigned long long)g_tot_count); | 299 | { |
300 | (void) arg; | ||
151 | // sorted by probability: | 301 | // sorted by probability: |
152 | bool ret = search_stupid(5, 3) // 5 digits, 3 letters: 0.281632 | 302 | bool stop = hex_producer_list(5, 3) // 5 digits, 3 letters: 0.281632 |
153 | || search_stupid(6, 2) // 6 digits, 2 letters: 0.234693 | 303 | || hex_producer_list(6, 2) // 6 digits, 2 letters: 0.234693 |
154 | || search_stupid(4, 4) // 4 digits, 4 letters: 0.211224 | 304 | || hex_producer_list(4, 4) // 4 digits, 4 letters: 0.211224 |
155 | || search_stupid(7, 1) // 7 digits, 1 letters: 0.111759 | 305 | || hex_producer_list(7, 1) // 7 digits, 1 letters: 0.111759 |
156 | || search_stupid(3, 5) // 3 digits, 5 letters: 0.101388 | 306 | || hex_producer_list(3, 5) // 3 digits, 5 letters: 0.101388 |
157 | || search_stupid(2, 6) // 2 digits, 6 letters: 0.030416 | 307 | || hex_producer_list(2, 6) // 2 digits, 6 letters: 0.030416 |
158 | || search_stupid(8, 0) // 8 digits, 0 letters: 0.023283 | 308 | || hex_producer_list(8, 0) // 8 digits, 0 letters: 0.023283 |
159 | || search_stupid(1, 7) // 1 digits, 7 letters: 0.005214 | 309 | || hex_producer_list(1, 7) // 1 digits, 7 letters: 0.005214 |
160 | || search_stupid(0, 8);// 0 digits, 8 letters: 0.000391 | 310 | || hex_producer_list(0, 8);// 0 digits, 8 letters: 0.000391 |
161 | cprintf(OFF, "\n"); | 311 | if(!stop) |
162 | return ret; | 312 | producer_stop(); |
313 | return NULL; | ||
163 | } | 314 | } |
164 | 315 | ||
165 | bool keysig_search_ascii_brute(uint8_t *cipher, keysig_notify_fn_t notify, void *user) | 316 | typedef void *(*routine_t)(void *); |
317 | |||
318 | bool keysig_search(int method, uint8_t *enc_buf, size_t buf_sz, | ||
319 | keysig_notify_fn_t notify, void *user, int nr_threads) | ||
166 | { | 320 | { |
167 | (void) cipher; | 321 | /* init producer */ |
168 | (void) notify; | 322 | producer_init(); |
169 | (void) user; | 323 | /* init search */ |
170 | keysig_search_init(); | 324 | keysig_search_init(); |
171 | cprintf(RED, "Unimplemented\n"); | 325 | pthread_mutex_init(&g_keysig_search.mutex, NULL); |
172 | return false; | 326 | g_keysig_search.enc_buf = enc_buf; |
327 | g_keysig_search.enc_buf_sz = buf_sz; | ||
328 | g_keysig_search.found_keysig = false; | ||
329 | /* get methods */ | ||
330 | routine_t worker_fn = NULL; | ||
331 | routine_t producer_fn = NULL; | ||
332 | if(method == KEYSIG_SEARCH_ASCII_HEX) | ||
333 | { | ||
334 | worker_fn = hex_worker; | ||
335 | producer_fn = hex_producer; | ||
336 | } | ||
337 | /* create workers */ | ||
338 | pthread_t *worker = malloc(sizeof(pthread_t) * nr_threads); | ||
339 | pthread_t producer; | ||
340 | for(int i = 0; i < nr_threads; i++) | ||
341 | pthread_create(&worker[i], NULL, worker_fn, NULL); | ||
342 | pthread_create(&producer, NULL, producer_fn, NULL); | ||
343 | /* wait for all threads */ | ||
344 | pthread_join(producer, NULL); | ||
345 | for(int i = 0; i < nr_threads; i++) | ||
346 | pthread_join(worker[i], NULL); | ||
347 | free(worker); | ||
348 | if(g_keysig_search.found_keysig) | ||
349 | notify(user, g_keysig_search.key, g_keysig_search.sig); | ||
350 | return g_keysig_search.found_keysig; | ||
173 | } | 351 | } |
174 | 352 | ||
175 | struct keysig_search_desc_t keysig_search_desc[KEYSIG_SEARCH_LAST] = | 353 | struct keysig_search_desc_t keysig_search_desc[KEYSIG_SEARCH_LAST] = |
@@ -177,19 +355,11 @@ struct keysig_search_desc_t keysig_search_desc[KEYSIG_SEARCH_LAST] = | |||
177 | [KEYSIG_SEARCH_NONE] = | 355 | [KEYSIG_SEARCH_NONE] = |
178 | { | 356 | { |
179 | .name = "none", | 357 | .name = "none", |
180 | .fn = NULL, | ||
181 | .comment = "don't use", | 358 | .comment = "don't use", |
182 | }, | 359 | }, |
183 | [KEYSIG_SEARCH_ASCII_STUPID] = | 360 | [KEYSIG_SEARCH_ASCII_HEX] = |
184 | { | 361 | { |
185 | .name = "ascii-hex", | 362 | .name = "ascii-hex", |
186 | .fn = keysig_search_ascii_stupid, | ||
187 | .comment = "Try to find an hexadecimal ascii string keysig" | 363 | .comment = "Try to find an hexadecimal ascii string keysig" |
188 | }, | 364 | }, |
189 | [KEYSIG_SEARCH_ASCII_BRUTE] = | ||
190 | { | ||
191 | .name = "ascii-brute", | ||
192 | .fn = keysig_search_ascii_brute, | ||
193 | .comment = "Brute force all ASCII keys" | ||
194 | }, | ||
195 | }; | 365 | }; |