3 * this searches the sorted tables for the given RNG data, retrieves the matching
4 * PRNG state, checks it is correct, and then rolls back the PRNG to recover the key
7 * This is a multi threaded version. After discussions with mwalker33 about how to make this multi threaded
8 * version he concluded that the file lookups would be ideal. So we don't do it inside the individual file searches but
9 * rather we can put each file to search in each thread instead. Come up with ways to make it faster!
11 * When testing remember OS cache fiddles with your mind and results. Running same test values will be much faster second run
14 #include "ht2crackutils.h"
19 // a global mutex to prevent interlaced printing from different threads
20 pthread_mutex_t print_lock
;
22 static int global_found
= 0;
23 static int thread_count
= 2;
24 static int g_bitoffset
= 0;
25 static uint8_t g_rngmatch
[6];
26 static uint8_t g_rngstate
[6];
33 typedef struct thread_args
{
39 #define AEND "\x1b[0m"
40 #define _RED_(s) "\x1b[31m" s AEND
41 #define _GREEN_(s) "\x1b[32m" s AEND
42 #define _YELLOW_(s) "\x1b[33m" s AEND
43 #define _CYAN_(s) "\x1b[36m" s AEND
45 #define INPUTFILE "sorted/%02x/%02x.bin"
48 static void print_hex(const uint8_t *data
, const size_t len
) {
49 if (data
== NULL
|| len
== 0) return;
51 for (size_t i
= 0; i
< len
; i
++) {
52 printf("%02X", data
[i
]);
58 static int datacmp(const void *p1
, const void *p2
) {
61 return memcmp(d1
, d2
, DATASIZE
- 6);
64 static int loadrngdata(rngdata_t
*r
, char *file
) {
72 printf("loadrngdata: invalid params\n");
76 fd
= open(file
, O_RDONLY
);
79 printf("cannot open file %s\n", file
);
83 if (fstat(fd
, &filestat
)) {
84 printf("cannot stat file %s\n", file
);
88 if (filestat
.st_size
< 6) {
89 printf("file %s is too small\n", file
);
93 data
= mmap((caddr_t
)0, filestat
.st_size
, PROT_READ
, MAP_PRIVATE
, fd
, 0);
94 if (data
== MAP_FAILED
) {
95 printf("cannot mmap file %s\n", file
);
99 r
->len
= filestat
.st_size
/ 2;
100 // printf("r->len = %d\n", r->len);
102 r
->data
= (unsigned char *)calloc(1, r
->len
);
104 printf("cannot calloc\n");
110 for (i
= 0; (i
< filestat
.st_size
) && (j
< r
->len
); i
++) {
111 if ((data
[i
] != 0x0a) && (data
[i
] != 0x0d) && (data
[i
] != 0x20)) {
113 r
->data
[j
] = hex2bin(data
[i
]) << 4;
116 r
->data
[j
] |= hex2bin(data
[i
]);
125 munmap(data
, filestat
.st_size
);
131 static int makecand(unsigned char *c
, rngdata_t
*r
, int bitoffset
) {
136 if (!c
|| !r
|| (bitoffset
> ((r
->len
* 8) - 48))) {
137 printf("makecand: invalid params\n");
141 bytenum
= bitoffset
/ 8;
142 bitnum
= bitoffset
% 8;
144 for (i
= 0; i
< 6; i
++) {
146 c
[i
] = r
->data
[bytenum
+ i
];
148 c
[i
] = (r
->data
[bytenum
+ i
] << bitnum
) | (r
->data
[bytenum
+ i
+ 1] >> (8 - bitnum
));
155 // test the candidate against the next or previous rng data
156 static int testcand(const unsigned char *f
, const unsigned char *rt
, int fwd
) {
161 unsigned char buf
[6];
163 // build the prng state at the candidate
165 for (i
= 0; i
< 6; i
++) {
166 hstate
.shiftreg
= (hstate
.shiftreg
<< 8) | f
[i
+ 4];
171 // roll forwards 48 bits
172 hitag2_nstep(&hstate
, 48);
174 // roll backwards 48 bits
175 rollback(&hstate
, 48);
179 // get 48 bits of RNG from the rolled to state
180 ks1
= hitag2_nstep(&hstate
, 24);
181 ks2
= hitag2_nstep(&hstate
, 24);
183 writebuf(buf
, ks1
, 3);
184 writebuf(buf
+ 3, ks2
, 3);
187 if (!memcmp(buf
, rt
, 6)) {
194 static int searchcand(unsigned char *c
, unsigned char *rt
, int fwd
, unsigned char *m
, unsigned char *s
) {
196 if (!c
|| !rt
|| !m
|| !s
) {
197 printf("searchcand: invalid params\n");
203 unsigned char item
[10];
204 unsigned char *found
= NULL
;
206 snprintf(file
, sizeof(file
), INPUTFILE
, c
[0], c
[1]);
208 int fd
= open(file
, O_RDONLY
);
210 printf("cannot open table file %s\n", file
);
214 struct stat filestat
;
215 if (fstat(fd
, &filestat
)) {
216 printf("cannot stat file %s\n", file
);
220 data
= mmap((caddr_t
)0, filestat
.st_size
, PROT_READ
, MAP_PRIVATE
, fd
, 0);
221 if (data
== MAP_FAILED
) {
222 printf("cannot mmap file %s\n", file
);
226 memcpy(item
, c
+ 2, 4);
228 found
= (unsigned char *)bsearch(item
, data
, filestat
.st_size
/ DATASIZE
, DATASIZE
, datacmp
);
232 // our candidate is in the table
233 // go backwards and see if there are other matches
234 while (((found
- data
) >= DATASIZE
) && (!memcmp(found
- DATASIZE
, item
, 4))) {
235 found
= found
- DATASIZE
;
238 // now test all matches
239 while (((found
- data
) <= (filestat
.st_size
- DATASIZE
)) && (!memcmp(found
, item
, 4))) {
240 if (testcand(found
, rt
, fwd
)) {
242 memcpy(m
+ 2, found
, 4);
243 memcpy(s
, found
+ 4, 6);
245 munmap(data
, filestat
.st_size
);
250 found
= found
+ DATASIZE
;
254 munmap(data
, filestat
.st_size
);
261 static void *brute_thread(void *arguments
) {
263 struct thread_args
*args
= (struct thread_args
*) arguments
;
267 r
.data
= calloc(1, args
->r
.len
);
268 memcpy(r
.data
, args
->r
.data
, args
->r
.len
);
270 int bitlen
= (r
.len
* 8);
272 for (int i
= args
->idx
; i
<= bitlen
- 48; i
+= thread_count
) {
275 if ((i
% 100) == 0) {
276 pthread_mutex_lock(&print_lock
);
277 printf("searching on bit %d\n", i
);
278 pthread_mutex_unlock(&print_lock
);
281 if (__atomic_load_n(&global_found
, __ATOMIC_ACQUIRE
) == 1) {
285 uint8_t l_cand
[6] = {0};
286 if (makecand(l_cand
, &r
, i
) == 0) {
287 pthread_mutex_lock(&print_lock
);
288 printf("cannot makecand, %d\n", i
);
289 pthread_mutex_unlock(&print_lock
);
292 // printf("cand: %02x %02x %02x %02x %02x %02x : ", cand[0], cand[1], cand[2], cand[3], cand[4], cand[5]);
296 /* make following or preceding RNG test data to confirm match */
297 uint8_t l_rngtest
[6] = {0};
298 if (i
< (bitlen
- 96)) {
300 if (makecand(l_rngtest
, &r
, i
+ 48) == 0) {
301 pthread_mutex_lock(&print_lock
);
302 printf("cannot makecand rngtest %d + 48\n", i
);
303 pthread_mutex_unlock(&print_lock
);
310 if (makecand(l_rngtest
, &r
, i
- 48) == 0) {
311 pthread_mutex_lock(&print_lock
);
312 printf("cannot makecand rngtest %d - 48\n", i
);
313 pthread_mutex_unlock(&print_lock
);
321 if (searchcand(l_cand
, l_rngtest
, fwd
, l_match
, l_state
)) {
322 __sync_fetch_and_add(&global_found
, 1);
323 __sync_fetch_and_add(&g_bitoffset
, i
);
324 memcpy(g_rngmatch
, l_match
, sizeof(l_match
));
325 memcpy(g_rngstate
, l_state
, sizeof(l_state
));
336 static void rollbackrng(Hitag_State
*hstate
, const unsigned char *s
, int offset
) {
340 printf("rollbackrng: invalid params\n");
344 // build prng at recovered offset
345 hstate
->shiftreg
= 0;
346 for (i
= 0; i
< 6; i
++) {
347 hstate
->shiftreg
= (hstate
->shiftreg
<< 8) | s
[i
];
350 printf("recovered prng state at offset %d:\n", offset
);
353 // rollback to state after auth
354 rollback(hstate
, offset
);
356 // rollback through auth (aR, p3)
357 rollback(hstate
, 64);
359 printf("prng state after initialisation:\n");
365 static uint64_t recoverkey(Hitag_State
*hstate
, char *uidstr
, char *nRstr
) {
376 // key lower 16 bits are lower 16 bits of prng state
377 key
= hstate
->shiftreg
& 0xffff;
378 nRxork
= (hstate
->shiftreg
>> 16) & 0xffffffff;
379 uid
= rev32(hexreversetoulong(uidstr
));
380 nRenc
= rev32(hexreversetoulong(nRstr
));
383 // rollback and extract bits b
384 for (i
= 0; i
< 32; i
++) {
385 hstate
->shiftreg
= ((hstate
->shiftreg
) << 1) | ((uidtmp
>> 31) & 0x1);
386 uidtmp
= uidtmp
<< 1;
387 b
= (b
<< 1) | fnf(hstate
->shiftreg
);
390 printf("end state:\n");
395 printf("nRenc:\t\t");
396 printbin2(nRenc
, 32);
405 keyupper
= nRxork
^ nR
;
406 key
= key
| (keyupper
<< 16);
415 int main(int argc
, char *argv
[]) {
418 printf("%s rngdatafile UID nR\n", argv
[0]);
423 if (!loadrngdata(&rng
, argv
[1])) {
424 printf("loadrngdata failed\n");
429 if (!strncmp(argv
[2], "0x", 2)) {
430 uidstr
= argv
[2] + 2;
436 if (!strncmp(argv
[3], "0x", 2)) {
442 #if !defined(_WIN32) || !defined(__WIN32__)
443 thread_count
= sysconf(_SC_NPROCESSORS_CONF
);
444 if (thread_count
< 2)
448 printf("\nBruteforce using " _YELLOW_("%d") " threads\n", thread_count
);
450 pthread_t threads
[thread_count
];
453 // create a mutex to avoid interlacing print commands from our different threads
454 pthread_mutex_init(&print_lock
, NULL
);
456 // findmatch(&rng, rngmatch, rngstate, &bitoffset)
459 for (int i
= 0; i
< thread_count
; ++i
) {
460 targs
*a
= calloc(1, rng
.len
+ sizeof(targs
));
461 a
->r
.data
= calloc(1, rng
.len
);
466 memcpy(a
->r
.data
, rng
.data
, rng
.len
);
468 pthread_create(&threads
[i
], NULL
, brute_thread
, (void *)a
);
473 // wait for threads to terminate:
474 for (int i
= 0; i
< thread_count
; ++i
) {
475 pthread_join(threads
[i
], &res
);
479 if (global_found
== false) {
480 printf("\n" _RED_("!!!") " failed to find a key\n\n");
482 printf("Found match:\n");
483 printf("rngmatch.... ");
484 print_hex(g_rngmatch
, sizeof(g_rngmatch
));
485 printf("rngstate.... ");
486 print_hex(g_rngstate
, sizeof(g_rngstate
));
487 printf("bitoffset... %d\n", g_bitoffset
);
490 rollbackrng(&hstate
, g_rngstate
, g_bitoffset
);
492 uint64_t keyrev
= recoverkey(&hstate
, uidstr
, nRstr
);
493 uint64_t key
= rev64(keyrev
);
495 printf("keyrev:\t\t");
500 for (int i
= 0; i
< 6; i
++) {
501 printf("%02X", (int)(key
& 0xff));
507 pthread_mutex_destroy(&print_lock
);