From ee22a824ce6ee0048245298b2ebba0be5fb02b6c Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Tue, 3 Aug 2021 23:22:19 +0200 Subject: [PATCH] Add support for Electra demodulation --- CHANGELOG.md | 1 + armsrc/Standalone/lf_icehid.c | 5 +- armsrc/lfops.c | 65 ++++++++++++++++--------- client/src/cmddata.c | 5 +- client/src/cmdlfem410x.c | 58 ++++++++++++++-------- client/src/cmdlfem410x.h | 2 +- common/lfdemod.c | 91 ++++++++++++++++++++++++++--------- 7 files changed, 158 insertions(+), 69 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c3781798..41d6b5eec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ All notable changes to this project will be documented in this file. This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log... ## [unreleased][unreleased] + - Added support to demodulate Electra tags and column parity check for EM410x (@doegox) - Fix demod plot for various demodulations (@doegox) - Fix `lf t55xx detect/rdbl/dump` - to override if user set `lf config` and use default values during operation (@iceman1001) - Added `hf iclass encode --wiegand/--fc/--cn` - direct fmt/fc/cn support (@bettse) diff --git a/armsrc/Standalone/lf_icehid.c b/armsrc/Standalone/lf_icehid.c index 0b1e6cbfe..01b164afd 100644 --- a/armsrc/Standalone/lf_icehid.c +++ b/armsrc/Standalone/lf_icehid.c @@ -96,8 +96,9 @@ static uint32_t IceEM410xdemod(void) { return PM3_ESOFT; } - errCnt = Em410xDecode(dest, &size, &idx, &hi, &lo); - if (errCnt != 1) { + int type = Em410xDecode(dest, &size, &idx, &hi, &lo); + // Did we find a Short EM or a Long EM? + if ((type & (0x1 | 0x2)) == 0) { BigBuf_free(); return PM3_ESOFT; } diff --git a/armsrc/lfops.c b/armsrc/lfops.c index 10ee379cd..5c42ab564 100644 --- a/armsrc/lfops.c +++ b/armsrc/lfops.c @@ -1476,31 +1476,52 @@ int lf_em410x_watch(int findone, uint32_t *high, uint64_t *low) { WDT_HIT(); - errCnt = Em410xDecode(dest, &size, &idx, &hi, &lo); - if (errCnt == 1) { - if (size == 128) { - Dbprintf("EM XL TAG ID: " _GREEN_("%06x%08x%08x") " - ( %05d_%03d_%08d )", - hi, - (uint32_t)(lo >> 32), - (uint32_t)lo, - (uint32_t)(lo & 0xFFFF), - (uint32_t)((lo >> 16LL) & 0xFF), - (uint32_t)(lo & 0xFFFFFF)); + int type = Em410xDecode(dest, &size, &idx, &hi, &lo); + if (type & 0x1) { + Dbprintf("EM TAG ID: " _GREEN_("%02x%08x") " - ( %05d_%03d_%08d )", + (uint32_t)(lo >> 32), + (uint32_t)lo, + (uint32_t)(lo & 0xFFFF), + (uint32_t)((lo >> 16LL) & 0xFF), + (uint32_t)(lo & 0xFFFFFF)); + } + if (type & 0x2) { + Dbprintf("EM XL TAG ID: " _GREEN_("%06x%08x%08x") " - ( %05d_%03d_%08d )", + hi, + (uint32_t)(lo >> 32), + (uint32_t)lo, + (uint32_t)(lo & 0xFFFF), + (uint32_t)((lo >> 16LL) & 0xFF), + (uint32_t)(lo & 0xFFFFFF)); + } + if (type & 0x4) { + uint64_t data = (lo << 20) >> 20; + // Convert back to Short ID + uint64_t id = ((uint64_t)hi << 16 ) | (lo >> 48); + if ((data & 0xFFFFFFFF) == 0) { + Dbprintf("EM TAG ID: " _GREEN_("%02x%08x") " - ( %05d_%03d_%08d ) Electra "_GREEN_("%i"), + (uint32_t)(id >> 32), + (uint32_t)id, + (uint32_t)(id & 0xFFFF), + (uint32_t)((id >> 16LL) & 0xFF), + (uint32_t)(id & 0xFFFFFF), + (uint32_t)(data >> 32)); } else { - Dbprintf("EM TAG ID: " _GREEN_("%02x%08x") " - ( %05d_%03d_%08d )", - (uint32_t)(lo >> 32), - (uint32_t)lo, - (uint32_t)(lo & 0xFFFF), - (uint32_t)((lo >> 16LL) & 0xFF), - (uint32_t)(lo & 0xFFFFFF)); - } - - if (findone) { - *high = hi; - *low = lo; - break; + Dbprintf("EM TAG ID: " _GREEN_("%02x%08x") " - ( %05d_%03d_%08d ) on 128b frame with data "_GREEN_("%03x%08x"), + (uint32_t)(id >> 32), + (uint32_t)id, + (uint32_t)(id & 0xFFFF), + (uint32_t)((id >> 16LL) & 0xFF), + (uint32_t)(id & 0xFFFFFF), + (uint32_t)(data >> 32), + (uint32_t)data); } } + if ((type > 0) && findone) { + *high = hi; + *low = lo; + break; + } hi = lo = size = idx = 0; clk = invert = 0; } diff --git a/client/src/cmddata.c b/client/src/cmddata.c index 32e2600de..2e8ba515c 100644 --- a/client/src/cmddata.c +++ b/client/src/cmddata.c @@ -592,10 +592,11 @@ static int Cmdmandecoderaw(const char *Cmd) { uint64_t id = 0; uint32_t hi = 0; size_t idx = 0; - if (Em410xDecode(bits, &size, &idx, &hi, &id) == 1) { + int res = Em410xDecode(bits, &size, &idx, &hi, &id); + if (res > 0) { //need to adjust to set bitstream back to manchester encoded data //setDemodBuff(bits, size, idx); - printEM410x(hi, id, false); + printEM410x(hi, id, false, res); } } setDemodBuff(bits, size, 0); diff --git a/client/src/cmdlfem410x.c b/client/src/cmdlfem410x.c index 5619b9a15..2b5295593 100644 --- a/client/src/cmdlfem410x.c +++ b/client/src/cmdlfem410x.c @@ -100,35 +100,55 @@ static void em410x_construct_emul_graph(uint8_t *uid, uint8_t clock, uint8_t gap } //print 64 bit EM410x ID in multiple formats -void printEM410x(uint32_t hi, uint64_t id, bool verbose) { +void printEM410x(uint32_t hi, uint64_t id, bool verbose, int type) { if (!id && !hi) return; - uint64_t n = 1; - uint64_t id2lo = 0; - uint8_t m, i; - for (m = 5; m > 0; m--) { - for (i = 0; i < 8; i++) { - id2lo = (id2lo << 1LL) | ((id & (n << (i + ((m - 1) * 8)))) >> (i + ((m - 1) * 8))); - } - } - if (verbose == false) { - - if (hi) { - PrintAndLogEx(SUCCESS, "EM 410x ID "_GREEN_("%06X%016" PRIX64), hi, id); - } else { + if (type & 0x1) { // Short ID PrintAndLogEx(SUCCESS, "EM 410x ID "_GREEN_("%010" PRIX64), id); } + if (type & 0x2) { // Long ID + PrintAndLogEx(SUCCESS, "EM 410x XL ID "_GREEN_("%06X%016" PRIX64), hi, id); + } + if (type & 0x4) { // Short Extended ID + uint64_t data = (id << 20) >> 20; + // Convert back to Short ID + id = ((uint64_t)hi << 16 ) | (id >> 48); + if ((data & 0xFFFFFFFF) == 0) { + PrintAndLogEx(SUCCESS, "EM 410x ID "_GREEN_("%010" PRIX64)" Electra "_GREEN_("%03" PRIu64), id, data >> 32); + } else { + PrintAndLogEx(SUCCESS, "EM 410x ID "_GREEN_("%010" PRIX64)" on 128b frame with data "_GREEN_("%011" PRIX64), id, data); + } + } return; } - if (hi) { + if (type & 0x2) { // Long ID //output 88 bit em id - PrintAndLogEx(SUCCESS, "EM 410x ID "_GREEN_("%06X%016" PRIX64), hi, id); - PrintAndLogEx(SUCCESS, "EM410x XL ( RF/%d )", g_DemodClock); - } else { + PrintAndLogEx(SUCCESS, "EM 410x XL ID "_GREEN_("%06X%016" PRIX64)" ( RF/%d )", hi, id, g_DemodClock); + } + if (type & 0x4) { // Short Extended ID + PrintAndLogEx(SUCCESS, "EM 410x Short ID found on a 128b frame"); + uint64_t data = (id << 20) >> 20; + PrintAndLogEx(SUCCESS, " Data after ID: "_GREEN_("%011" PRIX64), data); + if ((data & 0xFFFFFFFF) == 0) { + PrintAndLogEx(SUCCESS, " Possibly an Electra (RO), 0x"_GREEN_("%03" PRIX64)" = "_GREEN_("%03" PRIu64), data >> 32, data >> 32); + } + PrintAndLogEx(SUCCESS, " Short ID details:"); + // Convert back to Short ID + id = ((uint64_t)hi << 16 ) | (id >> 48); + } + if (type & (0x4 | 0x1)) { // Short Extended or Short ID //output 40 bit em id + uint64_t n = 1; + uint64_t id2lo = 0; + uint8_t m, i; + for (m = 5; m > 0; m--) { + for (i = 0; i < 8; i++) { + id2lo = (id2lo << 1LL) | ((id & (n << (i + ((m - 1) * 8)))) >> (i + ((m - 1) * 8))); + } + } PrintAndLogEx(SUCCESS, "EM 410x ID "_GREEN_("%010" PRIX64), id); PrintAndLogEx(SUCCESS, "EM410x ( RF/%d )", g_DemodClock); PrintAndLogEx(INFO, "-------- " _CYAN_("Possible de-scramble patterns") " ---------"); @@ -252,7 +272,7 @@ int AskEm410xDecode(bool verbose, uint32_t *hi, uint64_t *lo) { printDemodBuff(0, false, false, true); } - printEM410x(*hi, *lo, verbose); + printEM410x(*hi, *lo, verbose, ans); g_em410xid = *lo; return PM3_SUCCESS; } diff --git a/client/src/cmdlfem410x.h b/client/src/cmdlfem410x.h index d31812b56..92f2f3fcf 100644 --- a/client/src/cmdlfem410x.h +++ b/client/src/cmdlfem410x.h @@ -16,7 +16,7 @@ int CmdLFEM410X(const char *Cmd); int demodEM410x(bool verbose); -void printEM410x(uint32_t hi, uint64_t id, bool verbose); +void printEM410x(uint32_t hi, uint64_t id, bool verbose, int type); int AskEm410xDecode(bool verbose, uint32_t *hi, uint64_t *lo); int AskEm410xDemod(int clk, int invert, int maxErr, size_t maxLen, bool amplify, uint32_t *hi, uint64_t *lo, bool verbose); diff --git a/common/lfdemod.c b/common/lfdemod.c index 3c32834f6..d202d9aa0 100644 --- a/common/lfdemod.c +++ b/common/lfdemod.c @@ -253,6 +253,55 @@ size_t removeParity(uint8_t *bits, size_t startIdx, uint8_t pLen, uint8_t pType, return bitCnt; } +static size_t removeEm410xParity(uint8_t *bits, size_t startIdx, bool isLong, bool *validShort, bool *validShortExtended, bool *validLong) { + uint32_t parityWd = 0; + size_t bitCnt = 0; + bool validColParity = false; + bool validRowParity = true; + bool validRowParitySkipColP = true; + *validShort = false; + *validShortExtended = false; + *validLong = false; + uint8_t bLen = isLong ? 110 : 55; + uint16_t parityCol[4] = { 0, 0, 0, 0 }; + for (int word = 0; word < bLen; word += 5) { + for (int bit = 0; bit < 5; bit++) { + if (word + bit >= bLen) break; + parityWd = (parityWd << 1) | bits[startIdx + word + bit]; + if ((word <= 50) && (bit < 4)) + parityCol[bit] = (parityCol[bit] << 1) | bits[startIdx + word + bit]; + bits[bitCnt++] = (bits[startIdx + word + bit]); + } + if (word + 5 > bLen) break; + + bitCnt--; // overwrite parity with next data + validRowParity &= parityTest(parityWd, 5, 0) != 0; + if (word == 50) { // column parity nibble on short EM and on Electra + validColParity = parityTest(parityCol[0], 11, 0) != 0; + validColParity &= parityTest(parityCol[1], 11, 0) != 0; + validColParity &= parityTest(parityCol[2], 11, 0) != 0; + validColParity &= parityTest(parityCol[3], 11, 0) != 0; + } else { + validRowParitySkipColP &= parityTest(parityWd, 5, 0) != 0; + } + parityWd = 0; + } + if (!isLong && validRowParitySkipColP && validColParity) { + *validShort = true; + } + if (isLong && validRowParity) { + *validLong = true; + } + if (isLong && validRowParitySkipColP && validColParity) { + *validShortExtended = true; + } + if (*validShort || *validShortExtended || *validLong) { + return bitCnt; + } else { + return 0; + } +} + // by marshmellow // takes a array of binary values, length of bits per parity (includes parity bit), // Parity Type (1 for odd; 0 for even; 2 Always 1's; 3 Always 0's), and binary Length (length to run) @@ -2122,7 +2171,6 @@ int Em410xDecode(uint8_t *bits, size_t *size, size_t *start_idx, uint32_t *hi, u if (bits[1] > 1) return -1; if (*size < 64) return -2; - uint8_t fmtlen; *start_idx = 0; // preamble 0111111111 @@ -2131,32 +2179,29 @@ int Em410xDecode(uint8_t *bits, size_t *size, size_t *start_idx, uint32_t *hi, u if (!preambleSearch(bits, preamble, sizeof(preamble), size, start_idx)) return -4; - // (iceman) if the preamble doesn't find two occuriences, this identification fails. - fmtlen = (*size == 128) ? 22 : 10; + bool validShort = false; + bool validShortExtended = false; + bool validLong = false; + *size = removeEm410xParity(bits, *start_idx + sizeof(preamble), *size == 128, &validShort, &validShortExtended, &validLong); - //skip last 4bit parity row for simplicity - *size = removeParity(bits, *start_idx + sizeof(preamble), 5, 0, fmtlen * 5); - - switch (*size) { - case 40: { - // std em410x format - *hi = 0; - *lo = ((uint64_t)(bytebits_to_byte(bits, 8)) << 32) | (bytebits_to_byte(bits + 8, 32)); - break; - } - case 88: { - // long em format - *hi = (bytebits_to_byte(bits, 24)); - *lo = ((uint64_t)(bytebits_to_byte(bits + 24, 32)) << 32) | (bytebits_to_byte(bits + 24 + 32, 32)); - break; - } - default: - return -6; + if (validShort) { + // std em410x format + *hi = 0; + *lo = ((uint64_t)(bytebits_to_byte(bits, 8)) << 32) | (bytebits_to_byte(bits + 8, 32)); + // 1 = Short + return 1; } - return 1; + if (validShortExtended || validLong) { + // store in long em format + *hi = (bytebits_to_byte(bits, 24)); + *lo = ((uint64_t)(bytebits_to_byte(bits + 24, 32)) << 32) | (bytebits_to_byte(bits + 24 + 32, 32)); + // 2 = Long + // 4 = ShortExtended + return ((int)validShortExtended << 2) + ((int)validLong << 1); + } + return -6; } - // loop to get raw HID waveform then FSK demodulate the TAG ID from it int HIDdemodFSK(uint8_t *dest, size_t *size, uint32_t *hi2, uint32_t *hi, uint32_t *lo, int *waveStartIdx) { //make sure buffer has data