From 98e44176a93cd46406dad5ebf9e993c94434edf4 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Thu, 18 Jan 2024 16:11:21 +0100 Subject: [PATCH] lf em 4x05 view is a new command to view dump files. lf em 4x05 view got detailed breakdown of config word bits. protection bits are guarded by verbose flag and identifcation got better generic instead of an wrongly absolut match --- CHANGELOG.md | 3 + client/src/cmdlfem4x05.c | 1062 +++++++++++++++++++++++++------------- client/src/util.c | 12 +- 3 files changed, 721 insertions(+), 356 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6872ddacf..0196272e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,9 @@ 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] + - Change `lf em 4x05 info` - use `-v` verbose flag to see protection bits (@iceman1001) + - Changed `lf em 4x05 info` - output now shows detailed bits (@iceman1001) + - Added `lf em 4x05 view` - now supports viewing of dump files (@iceman1001) - Changed `hf 15 eview/view` - now supports the `-z` flag for dense output (@iceman1001) - Changed `hf mf cload` - now supports specifying tag size (@augustozanellato) - Added `hf 14b raw --pico` - now supports picopass anticollision over ISO14443-B (@iceman1001) diff --git a/client/src/cmdlfem4x05.c b/client/src/cmdlfem4x05.c index f93db58d3..5a46e9150 100644 --- a/client/src/cmdlfem4x05.c +++ b/client/src/cmdlfem4x05.c @@ -47,6 +47,16 @@ #define EM_PREAMBLE_LEN 8 +typedef struct { + uint16_t cnt; + uint32_t value; +} em4x05_unlock_item_t; + + +static const char *em4x05_annotation[] = {"Info/User", "UID", "Password", "User", "Config", "User", "User", "User", "User", "User", "User", "User", "User", "User", "Lock", "Lock"}; +static const char *em4x69_annotation [] = {"Info", "UID", "Password", "Lock", "Config", "User", "User", "User", "User", "User", "User", "User", "User", "User", "User", "User"}; + + static int CmdHelp(const char *Cmd); // 1 = EM4x69 @@ -335,7 +345,6 @@ static int em4x05_demod_resp(uint32_t *word, bool onlyPreamble) { return res; } - //////////////// 4205 / 4305 commands static bool em4x05_verify_write(uint8_t addr, uint32_t pwd, bool use_pwd, uint32_t data) { @@ -401,7 +410,7 @@ static int em4x05_login_ext(uint32_t pwd) { clearCommandBuffer(); SendCommandNG(CMD_LF_EM4X_LOGIN, (uint8_t *)&payload, sizeof(payload)); PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_LF_EM4X_LOGIN, &resp, 10000)) { + if (WaitForResponseTimeout(CMD_LF_EM4X_LOGIN, &resp, 10000) == false) { PrintAndLogEx(WARNING, "(em4x05_login_ext) timeout while waiting for reply."); return PM3_ETIMEOUT; } @@ -428,7 +437,7 @@ int em4x05_read_word_ext(uint8_t addr, uint32_t pwd, bool use_pwd, uint32_t *wor clearCommandBuffer(); SendCommandNG(CMD_LF_EM4X_READWORD, (uint8_t *)&payload, sizeof(payload)); PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_LF_EM4X_READWORD, &resp, 10000)) { + if (WaitForResponseTimeout(CMD_LF_EM4X_READWORD, &resp, 10000) == false) { PrintAndLogEx(WARNING, "(em4x05_read_word_ext) timeout while waiting for reply."); return PM3_ETIMEOUT; } @@ -455,7 +464,7 @@ int em4x05_write_word_ext(uint8_t addr, uint32_t pwd, bool use_pwd, uint32_t dat clearCommandBuffer(); SendCommandNG(CMD_LF_EM4X_WRITEWORD, (uint8_t *)&payload, sizeof(payload)); PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_LF_EM4X_WRITEWORD, &resp, 2000)) { + if (WaitForResponseTimeout(CMD_LF_EM4X_WRITEWORD, &resp, 2000) == false) { PrintAndLogEx(ERR, "Error occurred, device did not respond during write operation."); return PM3_ETIMEOUT; } @@ -476,13 +485,559 @@ static int em4x05_protect(uint32_t pwd, bool use_pwd, uint32_t data) { clearCommandBuffer(); SendCommandNG(CMD_LF_EM4X_PROTECTWORD, (uint8_t *)&payload, sizeof(payload)); PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_LF_EM4X_PROTECTWORD, &resp, 2000)) { + if (WaitForResponseTimeout(CMD_LF_EM4X_PROTECTWORD, &resp, 2000) == false) { PrintAndLogEx(ERR, "Error occurred, device did not respond during write operation."); return PM3_ETIMEOUT; } return PM3_SUCCESS; } +static const char *print_em4x05_known(uint32_t word) { + + // EM4305_DEFAULT_CONFIG_BLOCK same as PRESCO + if ((word & EM4305_PRESCO_CONFIG_BLOCK) == EM4305_PRESCO_CONFIG_BLOCK) { + return "EM4305 DEFAULT / PRESCO"; + } + + // EM4305_PAXTON_CONFIG_BLOCK same as unique + if ((word & EM4305_EM_UNIQUE_CONFIG_BLOCK) == EM4305_EM_UNIQUE_CONFIG_BLOCK) { + return "EM UNIQUE / PAXTON"; + } + + if ((word & EM4305_VISA2000_CONFIG_BLOCK) == EM4305_VISA2000_CONFIG_BLOCK) { + return "VISA2000"; + } + + if ((word & EM4305_VIKING_CONFIG_BLOCK) == EM4305_VIKING_CONFIG_BLOCK) { + return "VIKING"; + } + + if ((word & EM4305_NORALSY_CONFIG_BLOCK) == EM4305_NORALSY_CONFIG_BLOCK) { + return "NORALSY"; + } + + if ((word & EM4305_SECURAKEY_CONFIG_BLOCK) == EM4305_SECURAKEY_CONFIG_BLOCK) { + return "SECURAKEY"; + } + + // EM4305_HID_26_CONFIG_BLOCK sane as AWID + // EM4305_PARADOX_CONFIG_BLOCK same as AWID + if ((word & EM4305_AWID_CONFIG_BLOCK) == EM4305_AWID_CONFIG_BLOCK) { + return "HID26 / PARADOX / AWID"; + } + + if ((word & EM4305_PYRAMID_CONFIG_BLOCK) == EM4305_PYRAMID_CONFIG_BLOCK) { + return "PYRAMID"; + } + + if ((word & EM4305_IOPROX_CONFIG_BLOCK) == EM4305_IOPROX_CONFIG_BLOCK) { + return "IOPROX"; + } + + // EM4305_KERI_CONFIG_BLOCK same as Indala + if ((word & EM4305_INDALA_64_CONFIG_BLOCK) == EM4305_INDALA_64_CONFIG_BLOCK) { + return "INDALA 64 / KERI"; + } + + if ((word & EM4305_INDALA_224_CONFIG_BLOCK) == EM4305_INDALA_224_CONFIG_BLOCK) { + return "INDALA 224"; + } + + if ((word & EM4305_MOTOROLA_CONFIG_BLOCK) == EM4305_MOTOROLA_CONFIG_BLOCK) { + return "MOTOROLA"; + } + + if ((word & EM4305_NEXWATCH_CONFIG_BLOCK) == EM4305_NEXWATCH_CONFIG_BLOCK) { + return "NEXWATCH"; + } + + // EM4305_NEDAP_64_CONFIG_BLOCK same as jablotron + if ((word & EM4305_JABLOTRON_CONFIG_BLOCK) == EM4305_JABLOTRON_CONFIG_BLOCK) { + return "JABLOTRON / NEDAP 64"; + } + + if ((word & EM4305_GUARDPROXII_CONFIG_BLOCK) == EM4305_GUARDPROXII_CONFIG_BLOCK) { + return "GUARD PROXII"; + } + + if ((word & EM4305_NEDAP_128_CONFIG_BLOCK) == EM4305_NEDAP_128_CONFIG_BLOCK) { + return "NEDAP 128"; + } + + if ((word & EM4305_PAC_CONFIG_BLOCK) == EM4305_PAC_CONFIG_BLOCK) { + return "PAC/Stanley"; + } + + if ((word & EM4305_VERICHIP_CONFIG_BLOCK) == EM4305_VERICHIP_CONFIG_BLOCK) { + return "VERICHIP"; + } + return ""; +} + +static void printEM4x05config(em_tech_type_t card_type, uint32_t wordData) { + uint16_t datarate = (((wordData & 0x3F) + 1) * 2); + uint8_t encoder = ((wordData >> 6) & 0xF); + char enc[14]; + memset(enc, 0, sizeof(enc)); + + uint8_t PSKcf = (wordData >> 10) & 0x3; + char cf[20]; + memset(cf, 0, sizeof(cf)); + + uint8_t delay = (wordData >> 12) & 0x3; + char cdelay[33]; + memset(cdelay, 0, sizeof(cdelay)); + + uint8_t numblks = EM4x05_GET_NUM_BLOCKS(wordData); + + uint8_t LWR = numblks + 5 - 1; //last word read + switch (encoder) { + case 0: + snprintf(enc, sizeof(enc), "NRZ"); + break; + case 1: + snprintf(enc, sizeof(enc), "Manchester"); + break; + case 2: + snprintf(enc, sizeof(enc), "Biphase"); + break; + case 3: + snprintf(enc, sizeof(enc), "Miller"); + break; + case 4: + snprintf(enc, sizeof(enc), "PSK1"); + break; + case 5: + snprintf(enc, sizeof(enc), "PSK2"); + break; + case 6: + snprintf(enc, sizeof(enc), "PSK3"); + break; + case 7: + snprintf(enc, sizeof(enc), "Unknown"); + break; + case 8: + snprintf(enc, sizeof(enc), "FSK1"); + break; + case 9: + snprintf(enc, sizeof(enc), "FSK2"); + break; + default: + snprintf(enc, sizeof(enc), "Unknown"); + break; + } + + switch (PSKcf) { + case 0: + snprintf(cf, sizeof(cf), "PSK RF/2"); + break; + case 1: + snprintf(cf, sizeof(cf), "PSK RF/8"); + break; + case 2: + snprintf(cf, sizeof(cf), "PSK RF/4"); + break; + case 3: + snprintf(cf, sizeof(cf), "unknown"); + break; + } + + switch (delay) { + case 0: + snprintf(cdelay, sizeof(cdelay), "no delay"); + break; + case 1: + snprintf(cdelay, sizeof(cdelay), "BP/8 or 1/8th bit period delay"); + break; + case 2: + snprintf(cdelay, sizeof(cdelay), "BP/4 or 1/4th bit period delay"); + break; + case 3: + snprintf(cdelay, sizeof(cdelay), "no delay"); + break; + } + uint8_t readLogin = (wordData & EM4x05_READ_LOGIN_REQ) >> 18; + uint8_t readHKL = (wordData & EM4x05_READ_HK_LOGIN_REQ) >> 19; + uint8_t writeLogin = (wordData & EM4x05_WRITE_LOGIN_REQ) >> 20; + uint8_t writeHKL = (wordData & EM4x05_WRITE_HK_LOGIN_REQ) >> 21; + uint8_t raw = (wordData & EM4x05_READ_AFTER_WRITE) >> 22; + uint8_t disable = (wordData & EM4x05_DISABLE_ALLOWED) >> 23; + uint8_t rtf = (wordData & EM4x05_READER_TALK_FIRST) >> 24; + uint8_t invert = (wordData & EM4x05_INVERT) >> 25; + uint8_t pigeon = (wordData & EM4x05_PIGEON) >> 26; + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "--- " _CYAN_("Config Information") " ------------------------"); + PrintAndLogEx(INFO, "Data rate........ "_YELLOW_("RF/%u") " ( %02u )", datarate, (wordData & 0x3F)); + PrintAndLogEx(INFO, "Encoder.......... " _YELLOW_("%s") " ( %u )", enc, encoder); + PrintAndLogEx(INFO, " ident..... " _YELLOW_("%s"), print_em4x05_known(wordData)); + PrintAndLogEx(INFO, "Default read..... " _YELLOW_("%u") " blocks", numblks); + PrintAndLogEx(INFO, "Last word read... " _YELLOW_("%u") " th block", LWR); + + uint8_t bits[32+1] = {0}; + num_to_bytebitsLSBF(wordData, 32, bits); + const char *bs = sprint_bytebits_bin(bits, 32); + + PrintAndLogEx(INFO, ""); + PrintAndLogEx(INFO, "Config........... %08X", wordData); + PrintAndLogEx(INFO, "--------------------------------"); + PrintAndLogEx(INFO, "0 LSB 31"); + PrintAndLogEx(INFO, "%s", bs); + + char datarate_str[10] = {0}; + snprintf(datarate_str, sizeof(datarate_str), "RF/%u", datarate); + + PrintAndLogEx(INFO, "%s", sprint_breakdown_bin(C_NONE, bs, 32, 0, 6, datarate_str)); + PrintAndLogEx(INFO, "%s", sprint_breakdown_bin(C_NONE, bs, 32, 6, 4, enc)); + + if (card_type == EM_4369 || card_type == EM_4469) { + PrintAndLogEx(INFO, "%s", sprint_breakdown_bin(C_NONE, bs, 32, 10, 2, cf)); + } else { + PrintAndLogEx(INFO, "%s", sprint_breakdown_bin( (PSKcf) ? C_RED : C_NONE, bs, 32, 10, 2, "Must be 0")); + } + + if (card_type == EM_4305) { + PrintAndLogEx(INFO, "%s", sprint_breakdown_bin(C_NONE, bs, 32, 12, 2, "Delayed ON")); + } else { + PrintAndLogEx(INFO, "%s", sprint_breakdown_bin( (delay) ? C_RED : C_NONE, bs, 32, 12, 2, "Must be 0")); + } + + PrintAndLogEx(INFO, "%s", sprint_breakdown_bin(C_NONE, bs, 32, 14, 4, "LWR, Last default read word ")); + + if (readLogin) { + PrintAndLogEx(INFO, "%s", sprint_breakdown_bin(C_RED, bs, 32, 18, 1, "Read login required")); + } else { + PrintAndLogEx(INFO, "%s", sprint_breakdown_bin(C_GREEN, bs, 32, 18, 1, "Read login not required")); + } + + if (card_type == EM_4369 || card_type == EM_4469) { + if (readHKL) { + PrintAndLogEx(INFO, "%s", sprint_breakdown_bin(C_RED, bs, 32, 19, 1, "Read HK, login required")); + } else { + PrintAndLogEx(INFO, "%s", sprint_breakdown_bin(C_GREEN, bs, 32, 19, 1, "Read HK, not required")); + } + } else { + PrintAndLogEx(INFO, "%s", sprint_breakdown_bin( (readHKL) ? C_RED : C_NONE, bs, 32, 19, 1, "Must be 0")); + } + + if (writeLogin) { + PrintAndLogEx(INFO, "%s", sprint_breakdown_bin(C_RED, bs, 32, 20, 1, "Write login required")); + } else { + PrintAndLogEx(INFO, "%s", sprint_breakdown_bin(C_GREEN, bs, 32, 20, 1, "Write login not required")); + } + + if (card_type == EM_4369 || card_type == EM_4469) { + if (writeHKL) { + PrintAndLogEx(INFO, "%s", sprint_breakdown_bin(C_NONE, bs, 32, 21, 1, "Write HK, login required")); + } else { + PrintAndLogEx(INFO, "%s", sprint_breakdown_bin(C_NONE, bs, 32, 21, 1, "Write HK, login not required")); + } + } else { + PrintAndLogEx(INFO, "%s", sprint_breakdown_bin( (writeHKL) ? C_RED : C_NONE, bs, 32, 21, 1, "Must be 0")); + } + + if (card_type == EM_4369 || card_type == EM_4469) { + if (raw) { + PrintAndLogEx(INFO, "%s", sprint_breakdown_bin(C_NONE, bs, 32, 22, 1, "Read after write is on")); + } else { + PrintAndLogEx(INFO, "%s", sprint_breakdown_bin(C_NONE, bs, 32, 22, 1, "Read after write is OFF")); + } + } else { + PrintAndLogEx(INFO, "%s", sprint_breakdown_bin( (raw) ? C_RED : C_NONE, bs, 32, 22, 1, "Must be 0")); + } + + if (disable) { + PrintAndLogEx(INFO, "%s", sprint_breakdown_bin(C_NONE, bs, 32, 23, 1, "Disable command accepted")); + } else { + PrintAndLogEx(INFO, "%s", sprint_breakdown_bin(C_NONE, bs, 32, 23, 1, "Disable command not accepted")); + } + + if (rtf) { + PrintAndLogEx(INFO, "%s", sprint_breakdown_bin(C_RED, bs, 32, 24, 1, "R.T.F, Reader talk first enabled")); + } else { + PrintAndLogEx(INFO, "%s", sprint_breakdown_bin(C_NONE, bs, 32, 24, 1, "R.T.F. Reader talk first disabled")); + } + + if (card_type == EM_4369) { + if (invert) { + PrintAndLogEx(INFO, "%s", sprint_breakdown_bin(C_RED, bs, 32, 25, 1, "Invert data? yes")); + } else { + PrintAndLogEx(INFO, "%s", sprint_breakdown_bin(C_NONE, bs, 32, 25, 1, "Invert data? no")); + } + } else { + PrintAndLogEx(INFO, "%s", sprint_breakdown_bin( (invert) ? C_RED : C_NONE, bs, 32, 25, 1, "Must be 0")); + } + + if (card_type == EM_4305) { + if (pigeon) { + PrintAndLogEx(INFO, "%s", sprint_breakdown_bin(C_YELLOW, bs, 32, 26, 1, "Pigeon mode enabled")); + } else { + PrintAndLogEx(INFO, "%s", sprint_breakdown_bin(C_NONE, bs, 32, 26, 1, "Pigeon mode disabled")); + } + } else { + PrintAndLogEx(INFO, "%s", sprint_breakdown_bin( (pigeon) ? C_RED : C_NONE, bs, 32, 26, 1, "Must be 0")); + } + + PrintAndLogEx(INFO, "%s", sprint_breakdown_bin(C_NONE, bs, 32, 27, 5, "RFU, Reserved for future use")); + PrintAndLogEx(INFO, "--------------------------------"); +} + +static void printEM4x05info(uint32_t block0, uint32_t serial) { + + uint8_t chipType = (block0 >> 1) & 0xF; + uint8_t cap = (block0 >> 5) & 3; + uint16_t custCode = (block0 >> 9) & 0x2FF; + + /* bits + // 0, rfu + // 1,2,3,4 chip type + // 5,6 resonant cap + // 7,8, rfu + // 9 - 18 customer code + // 19, rfu + + 98765432109876543210 + 001000000000 + // 00100000000001111000 + xxx---- + // 1100 + // 011 + // 00100000000 + */ + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "--- " _CYAN_("Tag Information") " ---------------------------"); + + PrintAndLogEx(SUCCESS, "Chip type..... " _YELLOW_("%s"), em_get_card_str(block0)); + if (serial != 0) { + PrintAndLogEx(SUCCESS, "Serialno...... " _YELLOW_("%08X"), serial); + } + PrintAndLogEx(SUCCESS, "Block0........ %08x", block0); + + switch (cap) { + case 3: + PrintAndLogEx(SUCCESS, "Cap type...... 330pF ( %u )", cap); + break; + case 2: + PrintAndLogEx(SUCCESS, "Cap type...... %spF ( %u )", (chipType == 4) ? "75" : "210", cap); + break; + case 1: + PrintAndLogEx(SUCCESS, "Cap type...... 250pF ( %u )", cap); + break; + case 0: + PrintAndLogEx(SUCCESS, "Cap type...... no resonant capacitor ( %u )", cap); + break; + default: + PrintAndLogEx(SUCCESS, "Cap type...... unknown ( %u )", cap); + break; + } + + PrintAndLogEx(SUCCESS, "Custum code... %s ( %u )", (custCode == 0x200) ? "default" : "unknown", custCode); +} + +static void printEM4x05ProtectionBits(uint32_t word, uint8_t addr) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "--- " _CYAN_("Protection") " --------------------------------"); + PrintAndLogEx(INFO, ""); + PrintAndLogEx(INFO, "Protection word "_YELLOW_("%i") " - %08X", addr, word); + PrintAndLogEx(INFO, ""); + PrintAndLogEx(INFO, " # | status"); + PrintAndLogEx(INFO, "----+-------------"); + for (uint8_t i = 0; i < 15; i++) { + PrintAndLogEx(INFO, " %02u | %s", i, ((1 << i) & word) ? _RED_("write locked") : "unlocked"); + if (i == 14) { + PrintAndLogEx(INFO, " %02u | %s", i + 1, ((1 << i) & word) ? _RED_("write locked") : "unlocked"); + } + } + PrintAndLogEx(INFO, "----+-------------"); +} + +//quick test for EM4x05/EM4x69 tag +bool em4x05_isblock0(uint32_t *word) { + return (em4x05_read_word_ext(0, 0, false, word) == PM3_SUCCESS); +} + +static bool is_cancelled(void) { + if (kbd_enter_pressed()) { + PrintAndLogEx(WARNING, "\naborted via keyboard!\n"); + return true; + } + return false; +} + +static void em4x05_print_hdr(void) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "Addr | data | ascii |lck| info"); + PrintAndLogEx(INFO, "-----+----------+-------+---+-----"); +} + +static void em4x05_print_footer(void) { + PrintAndLogEx(INFO, "-----+----------+-------+---+-----"); + PrintAndLogEx(NORMAL, ""); +} + +static void em4x05_print_blocks(uint32_t cardtype, uint8_t *data, uint8_t dlen) { + + // must have 4 byte alignment + if ((data == NULL) || (dlen % EM4X05_BLOCK_SIZE) != 0) { + return; + } + + uint32_t *d = (uint32_t*)data; + + uint8_t i; + for (i = 0; i< (dlen >> 2); i++) { + d[i] = BSWAP_32(d[i]); + } + i = 0; + + bool got_lock_bits = false; + uint32_t lock = 0; + bool p15_active = false; + uint8_t lockbit; + uint8_t rev[EM4X05_BLOCK_SIZE] = {0}; + + if (cardtype == EM_4205 || cardtype == EM_4305) { + + // which protection block is ACTIVE? + if ((d[EM4305_PROT1_BLOCK] & 0x00008000) != 0x00) { + got_lock_bits = true; + lock = d[EM4305_PROT1_BLOCK]; + } else if ((d[EM4305_PROT2_BLOCK] & 0x00008000) != 0x00) { + // assume block 15 is the current lock block + p15_active = true; + got_lock_bits = true; + lock = d[EM4305_PROT2_BLOCK]; + } + + // Now read blocks 0 - 13 as we have 14 and 15 + for (; i < 14; i++) { + + lockbit = ((lock >> i) & 1); + + // hack: since sprint_ascii doesnt handle MSB/LSB swaps + reverse_array_copy(data + (i * EM4X05_BLOCK_SIZE), EM4X05_BLOCK_SIZE, rev); + + if (i == EM_SERIAL_BLOCK) { + PrintAndLogEx(INFO, " %02u | " _GREEN_("%08X") " | %s | %s | " _GREEN_("%s") + , i + , d[i] + , sprint_ascii(rev, EM4X05_BLOCK_SIZE) + , (got_lock_bits) ? (lockbit ? _RED_("x") : " ") : _YELLOW_("?") + , em4x05_annotation[i] + ); + } else if ( i == EM_CONFIG_BLOCK) { + PrintAndLogEx(INFO, " %02u | " _YELLOW_("%08X") " | %s | %s | " _YELLOW_("%s") + , i + , d[i] + , sprint_ascii(rev, EM4X05_BLOCK_SIZE) + , (got_lock_bits) ? (lockbit ? _RED_("x") : " ") : _YELLOW_("?") + , em4x05_annotation[i] + ); + } else { + + PrintAndLogEx(INFO, " %02u | %08X | %s | %s | %s" + , i + , d[i] + , sprint_ascii(rev, EM4X05_BLOCK_SIZE) + , (got_lock_bits) ? (lockbit ? _RED_("x") : " ") : _YELLOW_("?") + , em4x05_annotation[i] + ); + } + } + + // Print blocks 14 and 15 + // Both lock bits are protected with bit idx 14 (special case) + + i = 14; + lockbit = ((lock >> i) & 1); + reverse_array_copy(data + (i * EM4X05_BLOCK_SIZE), EM4X05_BLOCK_SIZE, rev); + + if (p15_active) { + PrintAndLogEx(INFO, " %02u | %08X | %s | %s | %-10s %s" + , i + , d[i] + , sprint_ascii(rev, EM4X05_BLOCK_SIZE) + , (got_lock_bits) ? (lockbit ? _RED_("x") : " ") : _YELLOW_("?") + , em4x05_annotation[i] + , "" + ); + } else { + PrintAndLogEx(INFO, " %02u | " _GREEN_("%08X") " | %s | %s | %-10s %s" + , i + , d[i] + , sprint_ascii(rev, EM4X05_BLOCK_SIZE) + , (got_lock_bits) ? (lockbit ? _RED_("x") : " ") : _YELLOW_("?") + , em4x05_annotation[i] + , _GREEN_("active") + ); + } + + i = 15; + // beware lock bit of word15 is pr14 + lockbit = (d[EM4305_PROT1_BLOCK] >> 14) & 1; + reverse_array_copy(data + (i * EM4X05_BLOCK_SIZE), EM4X05_BLOCK_SIZE, rev); + + if (p15_active) { + PrintAndLogEx(INFO, " %02u | " _GREEN_("%08X") " | %s | %s | %-10s %s" + , i + , d[i] + , sprint_ascii(rev, EM4X05_BLOCK_SIZE) + , (got_lock_bits) ? (lockbit ? _RED_("x") : " ") : _YELLOW_("?") + , em4x05_annotation[i] + , _GREEN_("active") + ); + } else { + PrintAndLogEx(INFO, " %02u | %08X | %s | %s | %-10s %s" + , i + , d[i] + , sprint_ascii(rev, EM4X05_BLOCK_SIZE) + , (got_lock_bits) ? (lockbit ? _RED_("x") : " ") : _YELLOW_("?") + , em4x05_annotation[i] + , "" + ); + } + } + + if (cardtype == EM_4369 || cardtype == EM_4469) { + + // To flag any blocks locked we need to read block 3 first + // don't swap endian until we get block lock flags. + // data[EM4469_PROT_BLOCK] = word; + for (; i < 16; i++) { + + lockbit = ((d[EM4469_PROT_BLOCK] >> (i * 2)) & 3); + reverse_array_copy(data + (i * EM4X05_BLOCK_SIZE), EM4X05_BLOCK_SIZE, rev); + + if (i == EM_SERIAL_BLOCK) { + PrintAndLogEx(INFO, " %02u | " _GREEN_("%08X") " | %s | %s | " _GREEN_("%s") + , i + , d[i] + , sprint_ascii(rev, EM4X05_BLOCK_SIZE) + , (got_lock_bits) ? (lockbit ? _RED_("x") : " ") : _YELLOW_("?") + , em4x69_annotation[i] + ); + } else if ( i == EM_CONFIG_BLOCK) { + PrintAndLogEx(INFO, " %02u | " _YELLOW_("%08X") " | %s | %s | " _YELLOW_("%s") + , i + , d[i] + , sprint_ascii(rev, EM4X05_BLOCK_SIZE) + , (got_lock_bits) ? (lockbit ? _RED_("x") : " ") : _YELLOW_("?") + , em4x69_annotation[i] + ); + } else { + PrintAndLogEx(INFO, " %02u | %08X | %s | %s | %s" + , i + , d[i] + , sprint_ascii(rev, EM4X05_BLOCK_SIZE) + , (got_lock_bits) ? (lockbit ? _RED_("x") : " ") : _YELLOW_("?") + , em4x69_annotation[i] + ); + } + } + } +} + int CmdEM4x05Demod(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "lf em 4x05 demod", @@ -526,7 +1081,14 @@ int CmdEM4x05Dump(const char *Cmd) { bool nosave = arg_get_lit(ctx, 3); CLIParserFree(ctx); - uint8_t addr = 0; + // sanitize checks + uint32_t block0 = 0; + // read word 0 (chip info) + // block 0 can be read even without a password. + if (em4x05_isblock0(&block0) == false) { + return PM3_ESOFT; + } + uint32_t pwd = 0; bool usePwd = false; if (inputpwd != 0xFFFFFFFFFFFFFFFF) { @@ -540,23 +1102,6 @@ int CmdEM4x05Dump(const char *Cmd) { pwd = (inputpwd & 0xFFFFFFFF); } - uint32_t block0 = 0; - // read word 0 (chip info) - // block 0 can be read even without a password. - if (em4x05_isblock0(&block0) == false) - return PM3_ESOFT; - - uint8_t bytes[4] = {0}; - uint32_t data[16]; - - int success = PM3_SUCCESS; - uint32_t lock_bits = 0x00; // no blocks locked - bool gotLockBits = false; - uint32_t word = 0; - - const char *info[] = {"Info/User", "UID", "Password", "User", "Config", "User", "User", "User", "User", "User", "User", "User", "User", "User", "Lock", "Lock"}; - const char *info4x69 [] = {"Info", "UID", "Password", "Lock", "Config", "User", "User", "User", "User", "User", "User", "User", "User", "User", "User", "User"}; - // EM4305 vs EM4469 em_tech_type_t card_type = em_get_card_type(block0); @@ -566,9 +1111,9 @@ int CmdEM4x05Dump(const char *Cmd) { // Test first if the password is correct int status = em4x05_login_ext(pwd); if (status == PM3_SUCCESS) { - PrintAndLogEx(INFO, "password is " _GREEN_("correct")); + PrintAndLogEx(INFO, "password ( " _GREEN_("ok") " )"); } else if (status == PM3_EFAILED) { - PrintAndLogEx(WARNING, "password is " _RED_("incorrect") ", will try without password"); + PrintAndLogEx(WARNING, "password ( " _RED_("fail") ") , will try without password"); usePwd = false; } else if (status != PM3_EFAILED) { PrintAndLogEx(WARNING, "Login attempt: no answer from tag"); @@ -576,9 +1121,16 @@ int CmdEM4x05Dump(const char *Cmd) { } } - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "Addr | data | ascii |lck| info"); - PrintAndLogEx(INFO, "-----+----------+-------+---+-----"); + em4x05_print_hdr(); + + uint8_t bytes[4] = {0}; + uint32_t data[16] = {0}; + + uint32_t lock_bits = 0x00; // no blocks locked + bool gotLockBits = false; + uint8_t addr = 0; + uint32_t word = 0; + int success = PM3_SUCCESS; if (card_type == EM_4205 || card_type == EM_4305 || card_type == EM_UNKNOWN) { bool lockInPW2 = false; @@ -594,6 +1146,7 @@ int CmdEM4x05Dump(const char *Cmd) { } else { success = PM3_ESOFT; // If any error ensure fail is set so not to save invalid data } + int status15 = em4x05_read_word_ext(EM4305_PROT2_BLOCK, pwd, usePwd, &word); if (status15 == PM3_SUCCESS) { if ((word & 0x00008000) != 0x00) { // assume block 15 is the current lock block @@ -605,47 +1158,55 @@ int CmdEM4x05Dump(const char *Cmd) { } else { success = PM3_ESOFT; // If any error ensure fail is set so not to save invalid data } + uint32_t lockbit; // Now read blocks 0 - 13 as we have 14 and 15 for (; addr < 14; addr++) { + lockbit = (lock_bits >> addr) & 1; + if (addr == 2) { if (usePwd) { data[addr] = BSWAP_32(pwd); num_to_bytes(pwd, 4, bytes); - PrintAndLogEx(INFO, " %02u | %08X | %s | %s | %s", addr, pwd, sprint_ascii(bytes, 4), gotLockBits ? (lockbit ? _RED_("x") : " ") : _YELLOW_("?"), info[addr]); + PrintAndLogEx(INFO, " %02u | %08X | %s | %s | %s", addr, pwd, sprint_ascii(bytes, 4), gotLockBits ? (lockbit ? _RED_("x") : " ") : _YELLOW_("?"), em4x05_annotation[addr]); } else { data[addr] = 0x00; // Unknown password, but not used to set to zeros - PrintAndLogEx(INFO, " %02u | | | | %-10s " _YELLOW_("write only"), addr, info[addr]); + PrintAndLogEx(INFO, " %02u | | | | %-10s " _YELLOW_("write only"), addr, em4x05_annotation[addr]); } } else { // success &= em4x05_read_word_ext(addr, pwd, usePwd, &word); int status = em4x05_read_word_ext(addr, pwd, usePwd, &word); // Get status for single read - if (status != PM3_SUCCESS) + if (status != PM3_SUCCESS) { success = PM3_ESOFT; // If any error ensure fail is set so not to save invalid data + } + data[addr] = BSWAP_32(word); if (status == PM3_SUCCESS) { num_to_bytes(word, 4, bytes); - PrintAndLogEx(INFO, " %02u | %08X | %s | %s | %s", addr, word, sprint_ascii(bytes, 4), gotLockBits ? (lockbit ? _RED_("x") : " ") : _YELLOW_("?"), info[addr]); + PrintAndLogEx(INFO, " %02u | %08X | %s | %s | %s", addr, word, sprint_ascii(bytes, 4), gotLockBits ? (lockbit ? _RED_("x") : " ") : _YELLOW_("?"), em4x05_annotation[addr]); } else - PrintAndLogEx(INFO, " %02u | | | | %-10s %s", addr, info[addr], status == PM3_EFAILED ? _RED_("read denied") : _RED_("read failed")); + PrintAndLogEx(INFO, " %02u | | | | %-10s %s", addr, em4x05_annotation[addr], status == PM3_EFAILED ? _RED_("read denied") : _RED_("read failed")); } } + // Print blocks 14 and 15 // Both lock bits are protected with bit idx 14 (special case) + addr = 14; if (status14 == PM3_SUCCESS) { lockbit = (lock_bits >> addr) & 1; - PrintAndLogEx(INFO, " %02u | %08X | %s | %s | %-10s %s", addr, data[addr], sprint_ascii(bytes, 4), gotLockBits ? (lockbit ? _RED_("x") : " ") : _YELLOW_("?"), info[addr], lockInPW2 ? "" : _GREEN_("active")); + PrintAndLogEx(INFO, " %02u | %08X | %s | %s | %-10s %s", addr, data[addr], sprint_ascii(bytes, 4), gotLockBits ? (lockbit ? _RED_("x") : " ") : _YELLOW_("?"), em4x05_annotation[addr], lockInPW2 ? "" : _GREEN_("active")); } else { - PrintAndLogEx(INFO, " %02u | | | | %-10s %s", addr, info[addr], status14 == PM3_EFAILED ? _RED_("read denied") : _RED_("read failed")); + PrintAndLogEx(INFO, " %02u | | | | %-10s %s", addr, em4x05_annotation[addr], status14 == PM3_EFAILED ? _RED_("read denied") : _RED_("read failed")); } + addr = 15; if (status15 == PM3_SUCCESS) { lockbit = (lock_bits >> 14) & 1; // beware lock bit of word15 is pr14 - PrintAndLogEx(INFO, " %02u | %08X | %s | %s | %-10s %s", addr, data[addr], sprint_ascii(bytes, 4), gotLockBits ? (lockbit ? _RED_("x") : " ") : _YELLOW_("?"), info[addr], lockInPW2 ? _GREEN_("active") : ""); + PrintAndLogEx(INFO, " %02u | %08X | %s | %s | %-10s %s", addr, data[addr], sprint_ascii(bytes, 4), gotLockBits ? (lockbit ? _RED_("x") : " ") : _YELLOW_("?"), em4x05_annotation[addr], lockInPW2 ? _GREEN_("active") : ""); } else { - PrintAndLogEx(INFO, " %02u | | | | %-10s %s", addr, info[addr], status15 == PM3_EFAILED ? _RED_("read denied") : _RED_("read failed")); + PrintAndLogEx(INFO, " %02u | | | | %-10s %s", addr, em4x05_annotation[addr], status15 == PM3_EFAILED ? _RED_("read denied") : _RED_("read failed")); } // Update endian for files data[14] = BSWAP_32(data[14]); @@ -665,15 +1226,16 @@ int CmdEM4x05Dump(const char *Cmd) { } for (; addr < 16; addr++) { + uint32_t lockbit = (lock_bits >> (addr * 2)) & 3; if (addr == 2) { if (usePwd) { data[addr] = BSWAP_32(pwd); num_to_bytes(pwd, 4, bytes); - PrintAndLogEx(INFO, " %02u | %08X | %s | %s | %s", addr, pwd, sprint_ascii(bytes, 4), gotLockBits ? (lockbit ? _RED_("x") : " ") : _YELLOW_("?"), info4x69[addr]); + PrintAndLogEx(INFO, " %02u | %08X | %s | %s | %s", addr, pwd, sprint_ascii(bytes, 4), gotLockBits ? (lockbit ? _RED_("x") : " ") : _YELLOW_("?"), em4x69_annotation[addr]); } else { data[addr] = 0x00; // Unknown password, but not used to set to zeros - PrintAndLogEx(INFO, " %02u | | | | %-10s " _YELLOW_("write only"), addr, info4x69[addr]); + PrintAndLogEx(INFO, " %02u | | | | %-10s " _YELLOW_("write only"), addr, em4x69_annotation[addr]); } } else { @@ -685,9 +1247,9 @@ int CmdEM4x05Dump(const char *Cmd) { data[addr] = BSWAP_32(word); if (status == PM3_SUCCESS) { num_to_bytes(word, 4, bytes); - PrintAndLogEx(INFO, " %02u | %08X | %s | %s | %s", addr, word, sprint_ascii(bytes, 4), gotLockBits ? (lockbit ? _RED_("x") : " ") : _YELLOW_("?"), info4x69[addr]); + PrintAndLogEx(INFO, " %02u | %08X | %s | %s | %s", addr, word, sprint_ascii(bytes, 4), gotLockBits ? (lockbit ? _RED_("x") : " ") : _YELLOW_("?"), em4x69_annotation[addr]); } else { - PrintAndLogEx(INFO, " %02u | | | | %-10s %s", addr, info4x69[addr], status == PM3_EFAILED ? _RED_("read denied") : _RED_("read failed")); + PrintAndLogEx(INFO, " %02u | | | | %-10s %s", addr, em4x69_annotation[addr], status == PM3_EFAILED ? _RED_("read denied") : _RED_("read failed")); } } } @@ -695,6 +1257,8 @@ int CmdEM4x05Dump(const char *Cmd) { } else { } + em4x05_print_footer(); + if (nosave) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "Called with no save option"); @@ -722,6 +1286,7 @@ int CmdEM4x05Dump(const char *Cmd) { else pm3_save_dump(filename, (uint8_t *)data, sizeof(data), jsfEM4x05); } + PrintAndLogEx(NORMAL, ""); return success; } @@ -966,277 +1531,6 @@ int CmdEM4x05Wipe(const char *Cmd) { return PM3_SUCCESS; } -static const char *printEM4x05_known(uint32_t word) { - - switch (word) { -// case EM4305_DEFAULT_CONFIG_BLOCK: - case EM4305_PRESCO_CONFIG_BLOCK: { - return "EM4305 DEFAULT / PRESCO"; - } -// case EM4305_PAXTON_CONFIG_BLOCK: - case EM4305_EM_UNIQUE_CONFIG_BLOCK: { - return "EM UNIQUE / PAXTON"; - } - case EM4305_VISA2000_CONFIG_BLOCK: { - return "VISA2000"; - } - case EM4305_VIKING_CONFIG_BLOCK: { - return "VIKING"; - } - case EM4305_NORALSY_CONFIG_BLOCK: { - return "NORALSY"; - } - case EM4305_SECURAKEY_CONFIG_BLOCK: { - return "SECURAKEY"; - } -// case EM4305_HID_26_CONFIG_BLOCK: -// case EM4305_PARADOX_CONFIG_BLOCK: - case EM4305_AWID_CONFIG_BLOCK: { - return "HID26 / PARADOX / AWID"; - } - case EM4305_PYRAMID_CONFIG_BLOCK: { - return "PYRAMID"; - } - case EM4305_IOPROX_CONFIG_BLOCK: { - return "IOPROX"; - } -// case EM4305_KERI_CONFIG_BLOCK: - case EM4305_INDALA_64_CONFIG_BLOCK: { - return "INDALA 64 / KERI"; - } - case EM4305_INDALA_224_CONFIG_BLOCK: { - return "INDALA 224"; - } - case EM4305_MOTOROLA_CONFIG_BLOCK: { - return "MOTOROLA"; - } - case EM4305_NEXWATCH_CONFIG_BLOCK: { - return "NEXWATCH"; - } -// case EM4305_NEDAP_64_CONFIG_BLOCK: - case EM4305_JABLOTRON_CONFIG_BLOCK: { - return "JABLOTRON / NEDAP 64"; - } - case EM4305_GUARDPROXII_CONFIG_BLOCK: { - return "GUARD PROXII"; - } - case EM4305_NEDAP_128_CONFIG_BLOCK: { - return "NEDAP 128"; - } - case EM4305_PAC_CONFIG_BLOCK: { - return "PAC/Stanley"; - } - case EM4305_VERICHIP_CONFIG_BLOCK: { - return "VERICHIP"; - } - } - return ""; -} - -static void printEM4x05config(em_tech_type_t card_type, uint32_t wordData) { - uint16_t datarate = (((wordData & 0x3F) + 1) * 2); - uint8_t encoder = ((wordData >> 6) & 0xF); - char enc[14]; - memset(enc, 0, sizeof(enc)); - - uint8_t PSKcf = (wordData >> 10) & 0x3; - char cf[10]; - memset(cf, 0, sizeof(cf)); - uint8_t delay = (wordData >> 12) & 0x3; - char cdelay[33]; - memset(cdelay, 0, sizeof(cdelay)); - uint8_t numblks = EM4x05_GET_NUM_BLOCKS(wordData); - uint8_t LWR = numblks + 5 - 1; //last word read - switch (encoder) { - case 0: - snprintf(enc, sizeof(enc), "NRZ"); - break; - case 1: - snprintf(enc, sizeof(enc), "Manchester"); - break; - case 2: - snprintf(enc, sizeof(enc), "Biphase"); - break; - case 3: - snprintf(enc, sizeof(enc), "Miller"); - break; - case 4: - snprintf(enc, sizeof(enc), "PSK1"); - break; - case 5: - snprintf(enc, sizeof(enc), "PSK2"); - break; - case 6: - snprintf(enc, sizeof(enc), "PSK3"); - break; - case 7: - snprintf(enc, sizeof(enc), "Unknown"); - break; - case 8: - snprintf(enc, sizeof(enc), "FSK1"); - break; - case 9: - snprintf(enc, sizeof(enc), "FSK2"); - break; - default: - snprintf(enc, sizeof(enc), "Unknown"); - break; - } - - switch (PSKcf) { - case 0: - snprintf(cf, sizeof(cf), "RF/2"); - break; - case 1: - snprintf(cf, sizeof(cf), "RF/8"); - break; - case 2: - snprintf(cf, sizeof(cf), "RF/4"); - break; - case 3: - snprintf(cf, sizeof(cf), "unknown"); - break; - } - - switch (delay) { - case 0: - snprintf(cdelay, sizeof(cdelay), "no delay"); - break; - case 1: - snprintf(cdelay, sizeof(cdelay), "BP/8 or 1/8th bit period delay"); - break; - case 2: - snprintf(cdelay, sizeof(cdelay), "BP/4 or 1/4th bit period delay"); - break; - case 3: - snprintf(cdelay, sizeof(cdelay), "no delay"); - break; - } - uint8_t readLogin = (wordData & EM4x05_READ_LOGIN_REQ) >> 18; - uint8_t readHKL = (wordData & EM4x05_READ_HK_LOGIN_REQ) >> 19; - uint8_t writeLogin = (wordData & EM4x05_WRITE_LOGIN_REQ) >> 20; - uint8_t writeHKL = (wordData & EM4x05_WRITE_HK_LOGIN_REQ) >> 21; - uint8_t raw = (wordData & EM4x05_READ_AFTER_WRITE) >> 22; - uint8_t disable = (wordData & EM4x05_DISABLE_ALLOWED) >> 23; - uint8_t rtf = (wordData & EM4x05_READER_TALK_FIRST) >> 24; - uint8_t invert = (wordData & EM4x05_INVERT) >> 25; - uint8_t pigeon = (wordData & EM4x05_PIGEON) >> 26; - - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "--- " _CYAN_("Config Information") " ------------------------"); - PrintAndLogEx(INFO, "ConfigWord: %08X ( " _YELLOW_("%s") " )", wordData, printEM4x05_known(wordData)); - - PrintAndLogEx(INFO, " Data Rate: %02u | "_YELLOW_("RF/%u"), wordData & 0x3F, datarate); - PrintAndLogEx(INFO, " Encoder: %u | " _YELLOW_("%s"), encoder, enc); - if (card_type == EM_4369 || card_type == EM_4469) { - PrintAndLogEx(INFO, " PSK CF: %u | %s", PSKcf, cf); - } else if (PSKcf != 0) { - PrintAndLogEx(INFO, "co10..c011: %u | %s", PSKcf, _RED_("Not used, must be set to logic 0")); - } - if (card_type == EM_4305) { - PrintAndLogEx(INFO, " Delay: %u | %s", delay, cdelay); - } else if (delay != 0) { - PrintAndLogEx(INFO, "co12..c013: %u | %s", delay, _RED_("Not used, must be set to logic 0")); - } - PrintAndLogEx(INFO, " LastWordR: %02u | Address of last word for default read - meaning %u blocks are output", LWR, numblks); - PrintAndLogEx(INFO, " ReadLogin: %u | Read login is %s", readLogin, readLogin ? _YELLOW_("required") : _GREEN_("not required")); - if (card_type == EM_4369 || card_type == EM_4469) { - PrintAndLogEx(INFO, " ReadHKL: %u | Read housekeeping words (3,4) login is %s", readHKL, readHKL ? _YELLOW_("required") : _GREEN_("not required")); - } else if (readHKL != 0) { - PrintAndLogEx(INFO, " c019: %u | %s", readHKL, _RED_("Not used, must be set to logic 0")); - } - PrintAndLogEx(INFO, "WriteLogin: %u | Write login is %s", writeLogin, writeLogin ? _YELLOW_("required") : _GREEN_("not required")); - if (card_type == EM_4369 || card_type == EM_4469) { - PrintAndLogEx(INFO, " WriteHKL: %u | Write housekeeping words (2,3,4) login is %s", writeHKL, writeHKL ? _YELLOW_("required") : _GREEN_("not Required")); - } else if (writeHKL != 0) { - PrintAndLogEx(INFO, " c021: %u | %s", writeHKL, _RED_("Not used, must be set to logic 0")); - } - if (card_type == EM_4369 || card_type == EM_4469) { - PrintAndLogEx(INFO, " R.A.W.: %u | Read after write is %s", raw, raw ? "on" : "off"); - } else if (raw != 0) { - PrintAndLogEx(INFO, " c022: %u | %s", raw, _RED_("Not used, must be set to logic 0")); - } - PrintAndLogEx(INFO, " Disable: %u | Disable command is %s", disable, disable ? "accepted" : "not accepted"); - PrintAndLogEx(INFO, " R.T.F.: %u | Reader talk first is %s", rtf, rtf ? _YELLOW_("enabled") : "disabled"); - if (card_type == EM_4369) { - PrintAndLogEx(INFO, " Invert: %u | Invert data? %s", invert, invert ? _YELLOW_("yes") : "no"); - } else if (invert != 0) { - PrintAndLogEx(INFO, " c025: %u | %s", invert, _RED_("Not used, must be set to logic 0")); - } - if (card_type == EM_4305) { - PrintAndLogEx(INFO, " Pigeon: %u | Pigeon mode is %s", pigeon, pigeon ? _YELLOW_("enabled") : "disabled"); - } else if (pigeon != 0) { - PrintAndLogEx(INFO, " c026: %u | %s", pigeon, _RED_("Not used, must be set to logic 0")); - } -} - -static void printEM4x05info(uint32_t block0, uint32_t serial) { - - uint8_t chipType = (block0 >> 1) & 0xF; - uint8_t cap = (block0 >> 5) & 3; - uint16_t custCode = (block0 >> 9) & 0x2FF; - - /* bits - // 0, rfu - // 1,2,3,4 chip type - // 5,6 resonant cap - // 7,8, rfu - // 9 - 18 customer code - // 19, rfu - - 98765432109876543210 - 001000000000 - // 00100000000001111000 - xxx---- - // 1100 - // 011 - // 00100000000 - */ - - PrintAndLogEx(INFO, "--- " _CYAN_("Tag Information") " ---------------------------"); - - PrintAndLogEx(SUCCESS, " Block0: " _GREEN_("%08x"), block0); - PrintAndLogEx(SUCCESS, " Chip Type: %3u | " _YELLOW_("%s"), chipType, em_get_card_str(block0)); - - switch (cap) { - case 3: - PrintAndLogEx(SUCCESS, " Cap Type: %3u | 330pF", cap); - break; - case 2: - PrintAndLogEx(SUCCESS, " Cap Type: %3u | %spF", cap, (chipType == 4) ? "75" : "210"); - break; - case 1: - PrintAndLogEx(SUCCESS, " Cap Type: %3u | 250pF", cap); - break; - case 0: - PrintAndLogEx(SUCCESS, " Cap Type: %3u | no resonant capacitor", cap); - break; - default: - PrintAndLogEx(SUCCESS, " Cap Type: %3u | unknown", cap); - break; - } - - PrintAndLogEx(SUCCESS, " Cust Code: 0x%x | %s", custCode, (custCode == 0x200) ? "Default" : "Unknown"); - if (serial != 0) - PrintAndLogEx(SUCCESS, " Serial #: " _YELLOW_("%08X"), serial); -} - -static void printEM4x05ProtectionBits(uint32_t word, uint8_t addr) { - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "--- " _CYAN_("Protection") " --------------------------------"); - PrintAndLogEx(INFO, "ProtectionWord: %08X (Word %i)", word, addr); - for (uint8_t i = 0; i < 15; i++) { - PrintAndLogEx(INFO, " Word: %02u | %s", i, ((1 << i) & word) ? _RED_("write Locked") : "unlocked"); - if (i == 14) - PrintAndLogEx(INFO, " Word: %02u | %s", i + 1, ((1 << i) & word) ? _RED_("write locked") : "unlocked"); - } -} - -//quick test for EM4x05/EM4x69 tag -bool em4x05_isblock0(uint32_t *word) { - return (em4x05_read_word_ext(0, 0, false, word) == PM3_SUCCESS); -} - int CmdEM4x05Info(const char *Cmd) { CLIParserContext *ctx; @@ -1249,10 +1543,12 @@ int CmdEM4x05Info(const char *Cmd) { void *argtable[] = { arg_param_begin, arg_str0("p", "pwd", "", "optional - password, 4 hex bytes"), + arg_lit0("v", "verbose", "Verbose output"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); uint64_t inputpwd = arg_get_u64_hexstr_def(ctx, 1, 0xFFFFFFFFFFFFFFFF); + bool verbose = arg_get_lit(ctx, 2); CLIParserFree(ctx); bool use_pwd = false; @@ -1266,8 +1562,9 @@ int CmdEM4x05Info(const char *Cmd) { // read word 0 (chip info) // block 0 can be read even without a password. - if (em4x05_isblock0(&block0) == false) + if (em4x05_isblock0(&block0) == false) { return PM3_ESOFT; + } // based on Block0 , decide type. em_tech_type_t card_type = em_get_card_type(block0); @@ -1288,6 +1585,10 @@ int CmdEM4x05Info(const char *Cmd) { printEM4x05config(card_type, word); + // skip printing lock bits if not VERBOSE + if (verbose == false) { + return PM3_SUCCESS; + } // if 4469 read EM4469_PROT_BLOCK // if 4305 read 14,15 if (card_type == EM_4205 || card_type == EM_4305) { @@ -1301,13 +1602,16 @@ int CmdEM4x05Info(const char *Cmd) { printEM4x05ProtectionBits(word, EM4305_PROT1_BLOCK); return PM3_SUCCESS; } else { // if status bit says this is not the used protection word - if (em4x05_read_word_ext(EM4305_PROT2_BLOCK, pwd, use_pwd, &word) != PM3_SUCCESS) + if (em4x05_read_word_ext(EM4305_PROT2_BLOCK, pwd, use_pwd, &word) != PM3_SUCCESS) { return PM3_ESOFT; + } + if (word & 0x8000) { printEM4x05ProtectionBits(word, EM4305_PROT2_BLOCK); return PM3_SUCCESS; } } + } else if (card_type == EM_4369 || card_type == EM_4469) { // read word 3 to see which is being used for the protection bits if (em4x05_read_word_ext(EM4469_PROT_BLOCK, pwd, use_pwd, &word) != PM3_SUCCESS) { @@ -1320,13 +1624,6 @@ int CmdEM4x05Info(const char *Cmd) { return PM3_ESOFT; } -static bool is_cancelled(void) { - if (kbd_enter_pressed()) { - PrintAndLogEx(WARNING, "\naborted via keyboard!\n"); - return true; - } - return false; -} // load a default pwd file. int CmdEM4x05Chk(const char *Cmd) { @@ -1491,11 +1788,6 @@ int CmdEM4x05Brute(const char *Cmd) { return PM3_SUCCESS; } -typedef struct { - uint16_t cnt; - uint32_t value; -} em4x05_unlock_item_t; - static int unlock_write_protect(bool use_pwd, uint32_t pwd, uint32_t data, bool verbose) { struct { @@ -1516,26 +1808,30 @@ static int unlock_write_protect(bool use_pwd, uint32_t pwd, uint32_t data, bool return PM3_ETIMEOUT; } - if (em4x05_download_samples() == false) + if (em4x05_download_samples() == false) { return PM3_ENODATA; + } uint32_t dummy = 0; int status = em4x05_demod_resp(&dummy, true); - if (status == PM3_SUCCESS && verbose) + if (status == PM3_SUCCESS && verbose) { PrintAndLogEx(SUCCESS, "Data written and verified"); - else if (status == PM3_EFAILED) + } else if (status == PM3_EFAILED) { PrintAndLogEx(ERR, "Tag denied PROTECT operation"); - else + } else { PrintAndLogEx(DEBUG, "No answer from tag"); + } return status; } -static int unlock_reset(bool use_pwd, uint32_t pwd, uint32_t data, bool verbose) { - if (verbose) - PrintAndLogEx(INFO, "resetting the " _RED_("active") " lock block"); +static int unlock_reset(bool use_pwd, uint32_t pwd, uint32_t data, bool verbose) { + if (verbose) { + PrintAndLogEx(INFO, "resetting the " _RED_("active") " lock block"); + } return unlock_write_protect(use_pwd, pwd, data, false); } + static void unlock_add_item(em4x05_unlock_item_t *array, uint8_t len, uint32_t value) { uint8_t i = 0; @@ -1891,7 +2187,7 @@ int CmdEM4x05Unlock(const char *Cmd) { // compute number of bits flipped PrintAndLogEx(INFO, "Bitflips: %2u events => %s", bitcount32(bitflips), bitstring); PrintAndLogEx(INFO, "New protection word => " _CYAN_("%08X") "\n", word14b); - PrintAndLogEx(INFO, "Try " _YELLOW_("`lf em 4x05 dump`")); + PrintAndLogEx(INFO, "Try " _YELLOW_("`lf em 4x05 dump --ns`")); } if (verbose) { @@ -1899,8 +2195,9 @@ int CmdEM4x05Unlock(const char *Cmd) { PrintAndLogEx(INFO, " idx | value | cnt | flipped bits"); PrintAndLogEx(INFO, "-----+----------+-----+------"); for (uint8_t i = 0; i < 64; i++) { - if (flipped[i].cnt == 0) + if (flipped[i].cnt == 0) { break; + } PrintAndLogEx(INFO, " %3u | %08X | %3u | %u", i, flipped[i].value, flipped[i].cnt, bitcount32(search_value ^ flipped[i].value)); } @@ -1937,6 +2234,7 @@ static uint32_t em4x05_Sniff_GetBlock(const char *bits, bool fwd) { value += (bits[idx] - '0'); parity += (bits[idx] - '0'); } + parity = parity % 2; if (parity != (bits[8] - '0')) parityerror = true; @@ -1947,16 +2245,19 @@ static uint32_t em4x05_Sniff_GetBlock(const char *bits, bool fwd) { value += (bits[idx] - '0'); parity += (bits[idx] - '0'); } + parity = parity % 2; if (parity != (bits[17] - '0')) parityerror = true; parity = 0; + for (idx = 18; idx < 26; idx++) { value <<= 1; value += (bits[idx] - '0'); parity += (bits[idx] - '0'); } + parity = parity % 2; if (parity != (bits[26] - '0')) parityerror = true; @@ -1967,6 +2268,7 @@ static uint32_t em4x05_Sniff_GetBlock(const char *bits, bool fwd) { value += (bits[idx] - '0'); parity += (bits[idx] - '0'); } + parity = parity % 2; if (parity != (bits[35] - '0')) parityerror = true; @@ -1974,11 +2276,12 @@ static uint32_t em4x05_Sniff_GetBlock(const char *bits, bool fwd) { if (parityerror) PrintAndLogEx(ERR, "parity error : "); - if (!fwd) { + if (fwd == false) { uint32_t t1 = value; value = 0; - for (idx = 0; idx < 32; idx++) + for (idx = 0; idx < 32; idx++) { value |= (((t1 >> idx) & 1) << (31 - idx)); + } } return value; } @@ -2005,8 +2308,8 @@ int CmdEM4x05Sniff(const char *Cmd) { CLIParserFree(ctx); const char *cmdText; - char dataText[100]; - char blkAddr[4]; + char dataText[100] = {0}; + char blkAddr[4] = {0}; int i; int ZeroWidth; // 32-42 "1" is 32 int CycleWidth; @@ -2167,18 +2470,77 @@ int CmdEM4x05Sniff(const char *Cmd) { return PM3_SUCCESS; } +static int CmdEM4x05View(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf em ex05 view", + "Print a EM4205/4305/4369/4469 dump file (bin/eml/json)\n" + "note:\n" + "We don't track if password is known in current dump file formats.\n" + "All zeros password block might be filler data", + "lf em 4x05 view -f lf-4x05-01020304-dump.json" + ); + void *argtable[] = { + arg_param_begin, + arg_str1("f", "file", "", "Specify a filename for dump file"), + arg_lit0("v", "verbose", "Verbose output"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + int fnlen = 0; + char filename[FILE_PATH_SIZE]; + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + bool verbose = arg_get_lit(ctx, 2); + CLIParserFree(ctx); + + // read dump file + uint8_t *dump = NULL; + size_t bytes_read = 0; + int res = pm3_load_dump(filename, (void **)&dump, &bytes_read, (MFU_MAX_BYTES + MFU_DUMP_PREFIX_LENGTH)); + if (res != PM3_SUCCESS) { + return res; + } + + uint8_t cardtype = EM_UNKNOWN; + if (strstr(filename, "4369")) { + cardtype = EM_4369; + } else if (strstr(filename, "4469")) { + cardtype = EM_4469; + } else if (strstr(filename, "4x05")) { + cardtype = EM_4305; + } + + if (verbose) { + uint32_t block0 = bytes_to_num(dump, EM4X05_BLOCK_SIZE); + uint32_t serial = bytes_to_num(dump + EM4X05_BLOCK_SIZE, EM4X05_BLOCK_SIZE); + uint32_t config = bytes_to_num(dump + (EM_CONFIG_BLOCK * EM4X05_BLOCK_SIZE), EM4X05_BLOCK_SIZE); + printEM4x05info(block0, serial); + printEM4x05config(cardtype, config); + } + + em4x05_print_hdr(); + em4x05_print_blocks(cardtype, dump, bytes_read); + em4x05_print_footer(); + free(dump); + + PrintAndLogEx(INFO, "Note:"); + PrintAndLogEx(INFO, "All ZEROS password block might be filler data"); + return PM3_SUCCESS; +} + static command_t CommandTable[] = { - {"help", CmdHelp, AlwaysAvailable, "This help"}, - {"brute", CmdEM4x05Brute, IfPm3Lf, "Bruteforce password"}, - {"chk", CmdEM4x05Chk, IfPm3Lf, "Check passwords from dictionary"}, - {"demod", CmdEM4x05Demod, AlwaysAvailable, "Demodulate a EM4x05/EM4x69 tag from the GraphBuffer"}, - {"dump", CmdEM4x05Dump, IfPm3Lf, "Dump EM4x05/EM4x69 tag"}, - {"info", CmdEM4x05Info, IfPm3Lf, "Tag information"}, - {"read", CmdEM4x05Read, IfPm3Lf, "Read word data from EM4x05/EM4x69"}, - {"sniff", CmdEM4x05Sniff, AlwaysAvailable, "Attempt to recover em4x05 commands from sample buffer"}, - {"unlock", CmdEM4x05Unlock, IfPm3Lf, "Execute tear off against EM4x05/EM4x69"}, - {"wipe", CmdEM4x05Wipe, IfPm3Lf, "Wipe EM4x05/EM4x69 tag"}, - {"write", CmdEM4x05Write, IfPm3Lf, "Write word data to EM4x05/EM4x69"}, + {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"-----------", CmdHelp, AlwaysAvailable, "----------------------- " _CYAN_("general") " -----------------------"}, + {"brute", CmdEM4x05Brute, IfPm3Lf, "Bruteforce password"}, + {"chk", CmdEM4x05Chk, IfPm3Lf, "Check passwords from dictionary"}, + {"demod", CmdEM4x05Demod, AlwaysAvailable, "Demodulate a EM4x05/EM4x69 tag from the GraphBuffer"}, + {"dump", CmdEM4x05Dump, IfPm3Lf, "Dump EM4x05/EM4x69 tag"}, + {"info", CmdEM4x05Info, IfPm3Lf, "Tag information"}, + {"read", CmdEM4x05Read, IfPm3Lf, "Read word data from EM4x05/EM4x69"}, + {"sniff", CmdEM4x05Sniff, AlwaysAvailable, "Attempt to recover em4x05 commands from sample buffer"}, + {"unlock", CmdEM4x05Unlock, IfPm3Lf, "Execute tear off against EM4x05/EM4x69"}, + {"view", CmdEM4x05View, AlwaysAvailable, "Display content from tag dump file"}, + {"wipe", CmdEM4x05Wipe, IfPm3Lf, "Wipe EM4x05/EM4x69 tag"}, + {"write", CmdEM4x05Write, IfPm3Lf, "Write word data to EM4x05/EM4x69"}, {NULL, NULL, NULL, NULL} }; diff --git a/client/src/util.c b/client/src/util.c index 4a8aced6e..47f0ce8ba 100644 --- a/client/src/util.c +++ b/client/src/util.c @@ -565,18 +565,18 @@ char *sprint_breakdown_bin(color_t color, const char* bs, int width, int padn, i } const char *prepad = " "; - const char *postmarker = "................................"; + const char *postmarker = " ................................"; static char buf[32 + 120] = {0}; memset(buf, 0, sizeof(buf)); - int8_t end = (width - padn - 1 - bits); + int8_t end = (width - padn - bits); if (end < 0) { end = 0; } switch (color) { case C_GREEN: { - snprintf(buf, sizeof(buf), "%.*s" _GREEN_("%.*s") " %.*s - " _GREEN_("%s") + snprintf(buf, sizeof(buf), "%.*s" _GREEN_("%.*s") "%.*s - " _GREEN_("%s") , padn, prepad , bits, bs + padn , end, postmarker @@ -585,7 +585,7 @@ char *sprint_breakdown_bin(color_t color, const char* bs, int width, int padn, i break; } case C_RED: { - snprintf(buf, sizeof(buf), "%.*s" _RED_("%.*s") " %.*s - " _RED_("%s") + snprintf(buf, sizeof(buf), "%.*s" _RED_("%.*s") "%.*s - " _RED_("%s") , padn, prepad , bits, bs + padn , end, postmarker @@ -594,7 +594,7 @@ char *sprint_breakdown_bin(color_t color, const char* bs, int width, int padn, i break; } case C_YELLOW: { - snprintf(buf, sizeof(buf), "%.*s" _YELLOW_("%.*s") " %.*s - " _YELLOW_("%s") + snprintf(buf, sizeof(buf), "%.*s" _YELLOW_("%.*s") "%.*s - " _YELLOW_("%s") , padn, prepad , bits, bs + padn , end, postmarker @@ -604,7 +604,7 @@ char *sprint_breakdown_bin(color_t color, const char* bs, int width, int padn, i } case C_NONE: default: { - snprintf(buf, sizeof(buf), "%.*s%.*s %.*s - %s" + snprintf(buf, sizeof(buf), "%.*s%.*s%.*s - %s" , padn, prepad , bits, bs + padn , end, postmarker