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 #include "ht2crackutils.h"
12 // a global mutex to prevent interlaced printing from different threads
13 pthread_mutex_t print_lock
;
15 static int global_found
= 0;
16 static int thread_count
= 2;
17 static int g_bitoffset
= 0;
18 static uint8_t g_rngmatch
[6];
19 static uint8_t g_rngstate
[6];
26 typedef struct thread_args
{
32 #define AEND "\x1b[0m"
33 #define _RED_(s) "\x1b[31m" s AEND
34 #define _GREEN_(s) "\x1b[32m" s AEND
35 #define _YELLOW_(s) "\x1b[33m" s AEND
36 #define _CYAN_(s) "\x1b[36m" s AEND
38 #define INPUTFILE "sorted/%02x/%02x.bin"
41 static void print_hex(const uint8_t *data
, const size_t len
) {
42 if (data
== NULL
|| len
== 0) return;
44 for (size_t i
= 0; i
< len
; i
++) {
45 printf("%02X", data
[i
]);
51 static int datacmp(const void *p1
, const void *p2
) {
52 unsigned char *d1
= (unsigned char *)p1
;
53 unsigned char *d2
= (unsigned char *)p2
;
55 return memcmp(d1
, d2
, DATASIZE
- 6);
58 static int loadrngdata(rngdata_t
*r
, char *file
) {
66 printf("loadrngdata: invalid params\n");
70 fd
= open(file
, O_RDONLY
);
73 printf("cannot open file %s\n", file
);
77 if (fstat(fd
, &filestat
)) {
78 printf("cannot stat file %s\n", file
);
82 if (filestat
.st_size
< 6) {
83 printf("file %s is too small\n", file
);
87 data
= mmap((caddr_t
)0, filestat
.st_size
, PROT_READ
, MAP_PRIVATE
, fd
, 0);
88 if (data
== MAP_FAILED
) {
89 printf("cannot mmap file %s\n", file
);
93 r
->len
= filestat
.st_size
/ 2;
94 // printf("r->len = %d\n", r->len);
96 r
->data
= (unsigned char *)calloc(1, r
->len
);
98 printf("cannot calloc\n");
104 for (i
= 0; (i
< filestat
.st_size
) && (j
< r
->len
); i
++) {
105 if ((data
[i
] != 0x0a) && (data
[i
] != 0x0d) && (data
[i
] != 0x20)) {
107 r
->data
[j
] = hex2bin(data
[i
]) << 4;
110 r
->data
[j
] |= hex2bin(data
[i
]);
119 munmap(data
, filestat
.st_size
);
125 static int makecand(unsigned char *c
, rngdata_t
*r
, int bitoffset
) {
130 if (!c
|| !r
|| (bitoffset
> ((r
->len
* 8) - 48))) {
131 printf("makecand: invalid params\n");
135 bytenum
= bitoffset
/ 8;
136 bitnum
= bitoffset
% 8;
138 for (i
= 0; i
< 6; i
++) {
140 c
[i
] = r
->data
[bytenum
+ i
];
142 c
[i
] = (r
->data
[bytenum
+ i
] << bitnum
) | (r
->data
[bytenum
+ i
+ 1] >> (8 - bitnum
));
149 // test the candidate against the next or previous rng data
150 static int testcand(const unsigned char *f
, unsigned char *rt
, int fwd
) {
155 unsigned char buf
[6];
157 // build the prng state at the candidate
159 for (i
= 0; i
< 6; i
++) {
160 hstate
.shiftreg
= (hstate
.shiftreg
<< 8) | f
[i
+ 4];
165 // roll forwards 48 bits
166 hitag2_nstep(&hstate
, 48);
168 // roll backwards 48 bits
169 rollback(&hstate
, 48);
173 // get 48 bits of RNG from the rolled to state
174 ks1
= hitag2_nstep(&hstate
, 24);
175 ks2
= hitag2_nstep(&hstate
, 24);
177 writebuf(buf
, ks1
, 3);
178 writebuf(buf
+ 3, ks2
, 3);
181 if (!memcmp(buf
, rt
, 6)) {
188 static int searchcand(unsigned char *c
, unsigned char *rt
, int fwd
, unsigned char *m
, unsigned char *s
) {
190 if (!c
|| !rt
|| !m
|| !s
) {
191 printf("searchcand: invalid params\n");
197 unsigned char item
[10];
198 unsigned char *found
= NULL
;
200 snprintf(file
, sizeof(file
), INPUTFILE
, c
[0], c
[1]);
202 int fd
= open(file
, O_RDONLY
);
204 printf("cannot open table file %s\n", file
);
208 struct stat filestat
;
209 if (fstat(fd
, &filestat
)) {
210 printf("cannot stat file %s\n", file
);
214 data
= mmap((caddr_t
)0, filestat
.st_size
, PROT_READ
, MAP_PRIVATE
, fd
, 0);
215 if (data
== MAP_FAILED
) {
216 printf("cannot mmap file %s\n", file
);
220 memcpy(item
, c
+ 2, 4);
222 found
= (unsigned char *)bsearch(item
, data
, filestat
.st_size
/ DATASIZE
, DATASIZE
, datacmp
);
226 // our candidate is in the table
227 // go backwards and see if there are other matches
228 while (((found
- data
) >= DATASIZE
) && (!memcmp(found
- DATASIZE
, item
, 4))) {
229 found
= found
- DATASIZE
;
232 // now test all matches
233 while (((found
- data
) <= (filestat
.st_size
- DATASIZE
)) && (!memcmp(found
, item
, 4))) {
234 if (testcand(found
, rt
, fwd
)) {
236 memcpy(m
+ 2, found
, 4);
237 memcpy(s
, found
+ 4, 6);
239 munmap(data
, filestat
.st_size
);
244 found
= found
+ DATASIZE
;
248 munmap(data
, filestat
.st_size
);
255 static void *brute_thread(void *arguments
) {
257 struct thread_args
*args
= (struct thread_args
*) arguments
;
261 r
.data
= calloc(1, args
->r
.len
);
262 memcpy(r
.data
, args
->r
.data
, args
->r
.len
);
264 int bitlen
= (r
.len
* 8);
266 for (int i
= args
->idx
; i
<= bitlen
- 48; i
+= thread_count
) {
269 if ((i
% 100) == 0) {
270 pthread_mutex_lock(&print_lock
);
271 printf("searching on bit %d\n", i
);
272 pthread_mutex_unlock(&print_lock
);
275 if (__atomic_load_n(&global_found
, __ATOMIC_ACQUIRE
) == 1) {
279 uint8_t l_cand
[6] = {0};
280 if (makecand(l_cand
, &r
, i
) == 0) {
281 pthread_mutex_lock(&print_lock
);
282 printf("cannot makecand, %d\n", i
);
283 pthread_mutex_unlock(&print_lock
);
286 // printf("cand: %02x %02x %02x %02x %02x %02x : ", cand[0], cand[1], cand[2], cand[3], cand[4], cand[5]);
290 /* make following or preceding RNG test data to confirm match */
291 uint8_t l_rngtest
[6] = {0};
292 if (i
< (bitlen
- 96)) {
294 if (makecand(l_rngtest
, &r
, i
+ 48) == 0) {
295 pthread_mutex_lock(&print_lock
);
296 printf("cannot makecand rngtest %d + 48\n", i
);
297 pthread_mutex_unlock(&print_lock
);
304 if (makecand(l_rngtest
, &r
, i
- 48) == 0) {
305 pthread_mutex_lock(&print_lock
);
306 printf("cannot makecand rngtest %d - 48\n", i
);
307 pthread_mutex_unlock(&print_lock
);
315 if (searchcand(l_cand
, l_rngtest
, fwd
, l_match
, l_state
)) {
316 __sync_fetch_and_add(&global_found
, 1);
317 __sync_fetch_and_add(&g_bitoffset
, i
);
318 memcpy(g_rngmatch
, l_match
, sizeof(l_match
));
319 memcpy(g_rngstate
, l_state
, sizeof(l_state
));
329 static void rollbackrng(Hitag_State
*hstate
, const unsigned char *s
, int offset
) {
333 printf("rollbackrng: invalid params\n");
337 // build prng at recovered offset
338 hstate
->shiftreg
= 0;
339 for (i
= 0; i
< 6; i
++) {
340 hstate
->shiftreg
= (hstate
->shiftreg
<< 8) | s
[i
];
343 printf("recovered prng state at offset %d:\n", offset
);
346 // rollback to state after auth
347 rollback(hstate
, offset
);
349 // rollback through auth (aR, p3)
350 rollback(hstate
, 64);
352 printf("prng state after initialisation:\n");
358 static uint64_t recoverkey(Hitag_State
*hstate
, char *uidstr
, char *nRstr
) {
369 // key lower 16 bits are lower 16 bits of prng state
370 key
= hstate
->shiftreg
& 0xffff;
371 nRxork
= (hstate
->shiftreg
>> 16) & 0xffffffff;
372 uid
= rev32(hexreversetoulong(uidstr
));
373 nRenc
= rev32(hexreversetoulong(nRstr
));
376 // rollback and extract bits b
377 for (i
= 0; i
< 32; i
++) {
378 hstate
->shiftreg
= ((hstate
->shiftreg
) << 1) | ((uidtmp
>> 31) & 0x1);
379 uidtmp
= uidtmp
<< 1;
380 b
= (b
<< 1) | fnf(hstate
->shiftreg
);
383 printf("end state:\n");
388 printf("nRenc:\t\t");
389 printbin2(nRenc
, 32);
398 keyupper
= nRxork
^ nR
;
399 key
= key
| (keyupper
<< 16);
408 int main(int argc
, char *argv
[]) {
411 printf("%s rngdatafile UID nR\n", argv
[0]);
416 if (!loadrngdata(&rng
, argv
[1])) {
417 printf("loadrngdata failed\n");
422 if (!strncmp(argv
[2], "0x", 2)) {
423 uidstr
= argv
[2] + 2;
429 if (!strncmp(argv
[3], "0x", 2)) {
435 #if !defined(_WIN32) || !defined(__WIN32__)
436 thread_count
= sysconf(_SC_NPROCESSORS_CONF
);
437 if (thread_count
< 2)
441 printf("\nBruteforce using " _YELLOW_("%d") " threads\n", thread_count
);
443 pthread_t threads
[thread_count
];
446 // create a mutex to avoid interlacing print commands from our different threads
447 pthread_mutex_init(&print_lock
, NULL
);
449 // findmatch(&rng, rngmatch, rngstate, &bitoffset)
452 for (int i
= 0; i
< thread_count
; ++i
) {
453 targs
*a
= calloc(1, rng
.len
+ sizeof(targs
));
454 a
->r
.data
= calloc(1, rng
.len
);
459 memcpy(a
->r
.data
, rng
.data
, rng
.len
);
461 pthread_create(&threads
[i
], NULL
, brute_thread
, (void *)a
);
464 // wait for threads to terminate:
465 for (int i
= 0; i
< thread_count
; ++i
) {
466 pthread_join(threads
[i
], &res
);
470 if (global_found
== false) {
471 printf("\n" _RED_("!!!") " failed to find a key\n\n");
473 printf("Found match:\n");
474 printf("rngmatch.... "); print_hex(g_rngmatch
, sizeof(g_rngmatch
));
475 printf("rngstate.... "); print_hex(g_rngstate
, sizeof(g_rngstate
));
476 printf("bitoffset... %d\n", g_bitoffset
);
479 rollbackrng(&hstate
, g_rngstate
, g_bitoffset
);
481 uint64_t keyrev
= recoverkey(&hstate
, uidstr
, nRstr
);
482 uint64_t key
= rev64(keyrev
);
484 printf("keyrev:\t\t");
489 for (int i
= 0; i
< 6; i
++) {
490 printf("%02X", (int)(key
& 0xff));
496 pthread_mutex_destroy(&print_lock
);