From 96e3e07faa48bf8f20f0a30e37ea9e6dae5fc23d Mon Sep 17 00:00:00 2001 From: Antiklesys Date: Fri, 12 Jul 2024 11:18:17 +0800 Subject: [PATCH] Implemented VB6 rng for iclass lookup elite key search Ported @bettse work from the Flipper Zero Picopass repository to use the lookup function with the VB6 rng --- CHANGELOG.md | 1 + client/src/cmdhficlass.c | 100 ++++++++++++++++++++++++++++++++------- client/src/cmdhficlass.h | 6 +++ 3 files changed, 90 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cc47f0156..89143f958 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Added `pm3_tears_for_fears.py` - a ISO14443b tear off script by Pierre Granier - Added new t55xx password (002BCFCF) sniffed from cheap cloner (@davidbeauchamp) - Fixed 'hf 14b sim' - now works (@michi-jung) +- Added VB6 Rng for iclass elite keys lookup by porting @bettse work in the Flipper Zero Picopass App (@antiklesys) ## [Aurora.4.18589][2024-05-28] - Fixed the pm3 regressiontests for Hitag2Crack (@iceman1001) diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index ca3451e36..f3bf3fd51 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -3774,29 +3774,84 @@ out: // this method tries to identify in which configuration mode a iCLASS / iCLASS SE reader is in. // Standard or Elite / HighSecurity mode. It uses a default key dictionary list in order to work. +#define INITIAL_SEED 0x429080 // VB6 KDF Seed Value + +// Functions for generating keys using RNG +uint32_t seed = INITIAL_SEED; +uint8_t key_state[8]; +bool prepared = false; + +void picopass_elite_reset(void) { + memset(key_state, 0, sizeof(key_state)); + seed = INITIAL_SEED; + prepared = false; +} + +uint32_t picopass_elite_lcg(void) { + uint32_t mod = 0x1000000; // 2^24 + uint32_t a = 0xFD43FD; + uint32_t c = 0xC39EC3; + + return (a * seed + c) % mod; +} + +uint32_t picopass_elite_rng(void) { + seed = picopass_elite_lcg(); + return seed; +} + +uint8_t picopass_elite_nextByte(void) { + return (picopass_elite_rng() >> 16) & 0xFF; +} + +void picopass_elite_nextKey(uint8_t* key) { + if(prepared) { + for(size_t i = 0; i < 7; i++) { + key_state[i] = key_state[i + 1]; + } + key_state[7] = picopass_elite_nextByte(); + } else { + for(size_t i = 0; i < 8; i++) { + key_state[i] = picopass_elite_nextByte(); + } + prepared = true; + } + memcpy(key, key_state, 8); +} + static int CmdHFiClassLookUp(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf iclass lookup", "This command take sniffed trace data and try to recovery a iCLASS Standard or iCLASS Elite key.", "hf iclass lookup --csn 9655a400f8ff12e0 --epurse f0ffffffffffffff --macs 0000000089cb984b -f iclass_default_keys.dic\n" - "hf iclass lookup --csn 9655a400f8ff12e0 --epurse f0ffffffffffffff --macs 0000000089cb984b -f iclass_default_keys.dic --elite" + "hf iclass lookup --csn 9655a400f8ff12e0 --epurse f0ffffffffffffff --macs 0000000089cb984b -f iclass_default_keys.dic --elite\n" + "hf iclass lookup --csn 9655a400f8ff12e0 --epurse f0ffffffffffffff --macs 0000000089cb984b --vb6rng" ); void *argtable[] = { arg_param_begin, - arg_str1("f", "file", "", "Dictionary file with default iclass keys"), + arg_str0("f", "file", "", "Dictionary file with default iclass keys"), arg_str1(NULL, "csn", "", "Specify CSN as 8 hex bytes"), arg_str1(NULL, "epurse", "", "Specify ePurse as 8 hex bytes"), arg_str1(NULL, "macs", "", "MACs"), arg_lit0(NULL, "elite", "Elite computations applied to key"), arg_lit0(NULL, "raw", "no computations applied to key"), + arg_lit0(NULL, "vb6rng", "use the VB6 rng for elite keys instead of a dictionary file"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); + bool use_vb6kdf = arg_get_lit(ctx, 7); int fnlen = 0; char filename[FILE_PATH_SIZE] = {0}; - CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + + bool use_elite = arg_get_lit(ctx, 5); + bool use_raw = arg_get_lit(ctx, 6); + if(use_vb6kdf){ + use_elite = true; + }else{ + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + } int csn_len = 0; uint8_t csn[8] = {0}; @@ -3834,15 +3889,12 @@ static int CmdHFiClassLookUp(const char *Cmd) { } } - bool use_elite = arg_get_lit(ctx, 5); - bool use_raw = arg_get_lit(ctx, 6); - CLIParserFree(ctx); uint8_t CCNR[12]; uint8_t MAC_TAG[4] = { 0, 0, 0, 0 }; - // stupid copy.. CCNR is a combo of epurse and reader nonce + // Stupid copy.. CCNR is a combo of epurse and reader nonce memcpy(CCNR, epurse, 8); memcpy(CCNR + 8, macs, 4); memcpy(MAC_TAG, macs + 4, 4); @@ -3853,20 +3905,34 @@ static int CmdHFiClassLookUp(const char *Cmd) { PrintAndLogEx(SUCCESS, " CCNR: " _GREEN_("%s"), sprint_hex(CCNR, sizeof(CCNR))); PrintAndLogEx(SUCCESS, "TAG MAC: %s", sprint_hex(MAC_TAG, sizeof(MAC_TAG))); - // run time + // Run time uint64_t t1 = msclock(); uint8_t *keyBlock = NULL; uint32_t keycount = 0; - // load keys - int res = loadFileDICTIONARY_safe(filename, (void **)&keyBlock, 8, &keycount); - if (res != PM3_SUCCESS || keycount == 0) { - free(keyBlock); - return res; + if (!use_vb6kdf) { + // Load keys + int res = loadFileDICTIONARY_safe(filename, (void **)&keyBlock, 8, &keycount); + if (res != PM3_SUCCESS || keycount == 0) { + free(keyBlock); + return res; + } + } else { + // Generate 5000 keys using VB6 KDF + keycount = 5000; + keyBlock = malloc(keycount * 8); + if (!keyBlock) { + return PM3_EMALLOC; + } + + picopass_elite_reset(); + for (uint32_t i = 0; i < keycount; i++) { + picopass_elite_nextKey(keyBlock + (i * 8)); + } } - //iclass_prekey_t + // Iclass_prekey_t iclass_prekey_t *prekey = calloc(keycount, sizeof(iclass_prekey_t)); if (!prekey) { free(keyBlock); @@ -3883,7 +3949,7 @@ static int CmdHFiClassLookUp(const char *Cmd) { PrintAndLogEx(INFO, "Sorting..."); - // sort mac list. + // Sort mac list qsort(prekey, keycount, sizeof(iclass_prekey_t), cmp_uint32); PrintAndLogEx(SUCCESS, "Searching for " _YELLOW_("%s") " key...", "DEBIT"); @@ -3891,7 +3957,7 @@ static int CmdHFiClassLookUp(const char *Cmd) { iclass_prekey_t lookup; memcpy(lookup.mac, MAC_TAG, 4); - // binsearch + // Binsearch item = (iclass_prekey_t *) bsearch(&lookup, prekey, keycount, sizeof(iclass_prekey_t), cmp_uint32); if (item != NULL) { @@ -3900,7 +3966,7 @@ static int CmdHFiClassLookUp(const char *Cmd) { } t1 = msclock() - t1; - PrintAndLogEx(SUCCESS, "time in iclass lookup " _YELLOW_("%.3f") " seconds", (float)t1 / 1000.0); + PrintAndLogEx(SUCCESS, "Time in iclass lookup " _YELLOW_("%.3f") " seconds", (float)t1 / 1000.0); free(prekey); free(keyBlock); diff --git a/client/src/cmdhficlass.h b/client/src/cmdhficlass.h index db242d496..b3b0844ab 100644 --- a/client/src/cmdhficlass.h +++ b/client/src/cmdhficlass.h @@ -36,4 +36,10 @@ void PrintPreCalc(iclass_prekey_t *list, uint32_t itemcnt); uint8_t get_pagemap(const picopass_hdr_t *hdr); bool check_known_default(uint8_t *csn, uint8_t *epurse, uint8_t *rmac, uint8_t *tmac, uint8_t *key); + +void picopass_elite_nextKey(uint8_t* key); +void picopass_elite_reset(void); +uint32_t picopass_elite_rng(void); +uint32_t picopass_elite_lcg(void); +uint8_t picopass_elite_nextByte(void); #endif