From 07f61627801576a17dcc6af835e3d6691739fa5a Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Tue, 21 Mar 2023 19:49:52 +0100 Subject: [PATCH] added support for the magic card called GDM. --- CHANGELOG.md | 2 + armsrc/appmain.c | 16 ++++++- armsrc/mifarecmd.c | 75 ++++++++++++++++++++++++++++-- armsrc/mifarecmd.h | 5 ++ armsrc/mifareutil.c | 22 +++++++-- armsrc/mifareutil.h | 2 + client/src/cmdhfmf.c | 84 ++++++++++++++++++++++++++++++++++ client/src/mifare/mifarehost.c | 4 ++ include/pm3_cmd.h | 4 ++ include/protocols.h | 2 + 10 files changed, 205 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 59959b489..1cceee09b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ 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 `hf mf gdmsetblk` - Support Gen4 GDM write block (@iceman1001) + - Changed `hf 14a info` - detect Gen GDM magic tags (@iceman1001) - Changed CLI max string argument length limit from 512 to 4096 (@iceman1001) - Fixed `data asn1` - now handles bad input better (@iceman1001) - Added new public key for signature MIFARE Plus Troika (@iceman100) diff --git a/armsrc/appmain.c b/armsrc/appmain.c index 66d1380b5..d435f9967 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -1686,7 +1686,7 @@ static void PacketReceived(PacketCommandNG *packet) { MifareECardLoadExt(payload->sectorcnt, payload->keytype); break; } - // Work with "magic Chinese" card + // Gen1a / 1b - "magic Chinese" card case CMD_HF_MIFARE_CSETBL: { MifareCSetBlock(packet->oldarg[0], packet->oldarg[1], packet->data.asBytes); break; @@ -1713,6 +1713,7 @@ static void PacketReceived(PacketCommandNG *packet) { MifareGen3Freez(); break; } + // Gen 4 GTU magic cards case CMD_HF_MIFARE_G4_RDBL: { struct p { uint8_t blockno; @@ -1723,7 +1724,6 @@ static void PacketReceived(PacketCommandNG *packet) { MifareG4ReadBlk(payload->blockno, payload->pwd, payload->workFlags); break; } - // Gen 4 GTU magic cards case CMD_HF_MIFARE_G4_WRBL: { struct p { uint8_t blockno; @@ -1735,6 +1735,18 @@ static void PacketReceived(PacketCommandNG *packet) { MifareG4WriteBlk(payload->blockno, payload->pwd, payload->data, payload->workFlags); break; } + + case CMD_HF_MIFARE_G4_GDM_WRBL: { + struct p { + uint8_t blockno; + uint8_t keytype; + uint8_t key[6]; + uint8_t data[16]; // data to be written + } PACKED; + struct p *payload = (struct p *) packet->data.asBytes; + MifareWriteBlockGDM(payload->blockno, payload->keytype, payload->key, payload->data); + break; + } case CMD_HF_MIFARE_PERSONALIZE_UID: { struct p { uint8_t keytype; diff --git a/armsrc/mifarecmd.c b/armsrc/mifarecmd.c index e198d37c7..448cadc8f 100644 --- a/armsrc/mifarecmd.c +++ b/armsrc/mifarecmd.c @@ -446,6 +446,73 @@ void MifareWriteBlock(uint8_t arg0, uint8_t arg1, uint8_t *datain) { set_tracing(false); } +void MifareWriteBlockGDM(uint8_t blockno, uint8_t keytype, uint8_t *key, uint8_t *datain) { + + int retval = PM3_SUCCESS; + + // check args + if (datain == NULL) { + retval = PM3_EINVARG; + goto OUT; + } + + uint8_t *par = BigBuf_malloc(MAX_PARITY_SIZE); + if (par == NULL) { + retval = PM3_EMALLOC; + goto OUT; + } + + uint8_t *uid = BigBuf_malloc(10); + if (uid == NULL) { + retval = PM3_EMALLOC; + goto OUT; + } + + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + clear_trace(); + set_tracing(true); + + // variables + uint32_t cuid = 0; + struct Crypto1State mpcs = {0, 0}; + struct Crypto1State *pcs; + pcs = &mpcs; + + uint64_t ui64key = bytes_to_num(key, 6); + + if (iso14443a_select_card(uid, NULL, &cuid, true, 0, true) == false) { + retval = PM3_ESOFT; + goto OUT; + } + + if (mifare_classic_authex_2(pcs, cuid, blockno, keytype, ui64key, AUTH_FIRST, NULL, NULL, true)) { + retval = PM3_ESOFT; + goto OUT; + }; + + if (mifare_classic_writeblock_ex(pcs, cuid, blockno, datain, true)) { + retval = PM3_ESOFT; + goto OUT; + }; + + if (mifare_classic_halt(pcs, cuid)) { + retval = PM3_ESOFT; + goto OUT; + }; + + if (g_dbglevel >= 2) DbpString("WRITE BLOCK FINISHED"); + +OUT: + crypto1_deinit(pcs); + + reply_ng(CMD_HF_MIFARE_G4_GDM_WRBL, retval, NULL, 0); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); + set_tracing(false); + BigBuf_free(); +} + + void MifareValue(uint8_t arg0, uint8_t arg1, uint8_t *datain) { // params uint8_t blockNo = arg0; @@ -2356,7 +2423,7 @@ void MifareCIdent(bool is_mfc) { uint8_t rats[4] = { ISO14443A_CMD_RATS, 0x80, 0x31, 0x73 }; uint8_t rdblf0[4] = { ISO14443A_CMD_READBLOCK, 0xF0, 0x8D, 0x5f}; uint8_t rdbl00[4] = { ISO14443A_CMD_READBLOCK, 0x00, 0x02, 0xa8}; - uint8_t gen3gmd[4] = { MIFARE_MAGIC_GDM_AUTH_KEYA, 0x00, 0x6C, 0x92}; + uint8_t gen4gmd[4] = { MIFARE_MAGIC_GDM_AUTH_KEYA, 0x00, 0x6C, 0x92}; uint8_t gen4GetConf[8] = { GEN_4GTU_CMD, 0x00, 0x00, 0x00, 0x00, GEN_4GTU_GETCNF, 0, 0}; uint8_t *par = BigBuf_malloc(MAX_PARITY_SIZE); uint8_t *buf = BigBuf_malloc(PM3_CMD_DATA_SIZE); @@ -2493,17 +2560,17 @@ void MifareCIdent(bool is_mfc) { } } - // magic MFC Gen3 test 2 + // magic MFC Gen4 GDM test if (isGen != MAGIC_GEN_3) { FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); SpinDelay(40); iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); res = iso14443a_select_card(uid, NULL, &cuid, true, 0, true); if (res == 2) { - ReaderTransmit(gen3gmd, sizeof(gen3gmd), NULL); + ReaderTransmit(gen4gmd, sizeof(gen4gmd), NULL); res = ReaderReceive(buf, par); if (res == 4) { - isGen = MAGIC_GEN_3; + isGen = MAGIC_GEN_4GDM; } } } diff --git a/armsrc/mifarecmd.h b/armsrc/mifarecmd.h index 30179aa3b..f471ccecc 100644 --- a/armsrc/mifarecmd.h +++ b/armsrc/mifarecmd.h @@ -46,16 +46,21 @@ void MifareEMemGet(uint8_t blockno, uint8_t blockcnt); int MifareECardLoad(uint8_t sectorcnt, uint8_t keytype); int MifareECardLoadExt(uint8_t sectorcnt, uint8_t keytype); +// MFC GEN1a /1b void MifareCSetBlock(uint32_t arg0, uint32_t arg1, uint8_t *datain); // Work with "magic Chinese" card void MifareCGetBlock(uint32_t arg0, uint32_t arg1, uint8_t *datain); void MifareCIdent(bool is_mfc); // is "magic chinese" card? void MifareHasStaticNonce(void); // Has the tag a static nonce? +// MFC GEN3 int DoGen3Cmd(uint8_t *cmd, uint8_t cmd_len); void MifareGen3UID(uint8_t uidlen, uint8_t *uid); // Gen 3 magic card set UID without manufacturer block void MifareGen3Blk(uint8_t block_len, uint8_t *block); // Gen 3 magic card overwrite manufacturer block void MifareGen3Freez(void); // Gen 3 magic card lock further UID changes +// MFC GEN4 GDM +void MifareWriteBlockGDM(uint8_t blockno, uint8_t keytype, uint8_t *key, uint8_t *datain); + // MFC GEN4 GTU void MifareG4ReadBlk(uint8_t blockno, uint8_t *pwd, uint8_t workFlags); void MifareG4WriteBlk(uint8_t blockno, uint8_t *pwd, uint8_t *data, uint8_t workFlags); diff --git a/armsrc/mifareutil.c b/armsrc/mifareutil.c index 5c0905e68..c349860dd 100644 --- a/armsrc/mifareutil.c +++ b/armsrc/mifareutil.c @@ -137,8 +137,10 @@ uint16_t mifare_sendcmd_short(struct Crypto1State *pcs, uint8_t crypted, uint8_t int mifare_classic_auth(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t keyType, uint64_t ui64Key, uint8_t isNested) { return mifare_classic_authex(pcs, uid, blockNo, keyType, ui64Key, isNested, NULL, NULL); } - int mifare_classic_authex(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t keyType, uint64_t ui64Key, uint8_t isNested, uint32_t *ntptr, uint32_t *timing) { + return mifare_classic_authex_2(pcs, uid, blockNo, keyType, ui64Key, isNested, NULL, NULL, false); +} +int mifare_classic_authex_2(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t keyType, uint64_t ui64Key, uint8_t isNested, uint32_t *ntptr, uint32_t *timing, bool is_gdm) { int len; uint32_t pos, nt, ntpp; // Supplied tag nonce uint8_t par[1] = {0x00}; @@ -150,8 +152,9 @@ int mifare_classic_authex(struct Crypto1State *pcs, uint32_t uid, uint8_t blockN // "random" reader nonce: num_to_bytes(prng_successor(GetTickCount(), 32), 4, nr); - // Transmit MIFARE_CLASSIC_AUTH - len = mifare_sendcmd_short(pcs, isNested, 0x60 + (keyType & 0x01), blockNo, receivedAnswer, receivedAnswerPar, timing); + // Transmit MIFARE_CLASSIC_AUTH 0x60, 0x61 or GDM 0x80 + uint8_t cmdbyte = (is_gdm) ? MIFARE_MAGIC_GDM_AUTH_KEYA : MIFARE_AUTH_KEYA + (keyType & 0x01); + len = mifare_sendcmd_short(pcs, isNested, cmdbyte, blockNo, receivedAnswer, receivedAnswerPar, timing); if (len != 4) return 1; // Save the tag nonce (nt) @@ -411,6 +414,10 @@ int mifare_ultra_readblock(uint8_t blockNo, uint8_t *blockData) { } int mifare_classic_writeblock(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t *blockData) { + return mifare_classic_writeblock_ex(pcs, uid, blockNo, blockData, false); +} + +int mifare_classic_writeblock_ex(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t *blockData, bool is_gdm) { // variables uint16_t len = 0; uint32_t pos = 0; @@ -421,8 +428,12 @@ int mifare_classic_writeblock(struct Crypto1State *pcs, uint32_t uid, uint8_t bl uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE] = {0x00}; uint8_t receivedAnswerPar[MAX_MIFARE_PARITY_SIZE] = {0x00}; - // command MIFARE_CLASSIC_WRITEBLOCK - len = mifare_sendcmd_short(pcs, 1, ISO14443A_CMD_WRITEBLOCK, blockNo, receivedAnswer, receivedAnswerPar, NULL); + // command MIFARE_MAGIC_GDM_WRITEBLOCK + if (is_gdm) { + len = mifare_sendcmd_short(pcs, 1, MIFARE_MAGIC_GDM_WRITEBLOCK, blockNo, receivedAnswer, receivedAnswerPar, NULL); + } else { + len = mifare_sendcmd_short(pcs, 1, ISO14443A_CMD_WRITEBLOCK, blockNo, receivedAnswer, receivedAnswerPar, NULL); + } if ((len != 1) || (receivedAnswer[0] != 0x0A)) { // 0x0a - ACK if (g_dbglevel >= DBG_ERROR) Dbprintf("Cmd Error: %02x", receivedAnswer[0]); @@ -456,6 +467,7 @@ int mifare_classic_writeblock(struct Crypto1State *pcs, uint32_t uid, uint8_t bl return 0; } + int mifare_classic_value(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t *blockData, uint8_t action) { // variables uint16_t len = 0; diff --git a/armsrc/mifareutil.h b/armsrc/mifareutil.h index aa5ef7e59..c7c6b4a94 100644 --- a/armsrc/mifareutil.h +++ b/armsrc/mifareutil.h @@ -72,10 +72,12 @@ uint16_t mifare_sendcmd_short(struct Crypto1State *pcs, uint8_t crypted, uint8_t // mifare classic int mifare_classic_auth(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t keyType, uint64_t ui64Key, uint8_t isNested); int mifare_classic_authex(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t keyType, uint64_t ui64Key, uint8_t isNested, uint32_t *ntptr, uint32_t *timing); +int mifare_classic_authex_2(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t keyType, uint64_t ui64Key, uint8_t isNested, uint32_t *ntptr, uint32_t *timing, bool is_gdm); int mifare_classic_readblock(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t *blockData); int mifare_classic_halt(struct Crypto1State *pcs, uint32_t uid); int mifare_classic_halt_ex(struct Crypto1State *pcs); int mifare_classic_writeblock(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t *blockData); +int mifare_classic_writeblock_ex(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t *blockData, bool is_gdm); int mifare_classic_value(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t *blockData, uint8_t action); // Ultralight/NTAG... diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index d5a629e90..900f201d2 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -7560,6 +7560,88 @@ static int CmdHF14AGen4Save(const char *Cmd) { return PM3_SUCCESS; } +static int CmdHF14AGen4_GDM_SetBlk(const char *Cmd) { + + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mf gdmsetblk", + "Set block data on a magic gen4 GDM card", + "hf mf gdmsetblk --blk 1 -d 000102030405060708090a0b0c0d0e0f" + ); + void *argtable[] = { + arg_param_begin, + arg_int1(NULL, "blk", "", "block number"), + arg_lit0("a", NULL, "input key type is key A (def)"), + arg_lit0("b", NULL, "input key type is key B"), + arg_str0("k", "key", "", "key, 6 hex bytes"), + arg_str0("d", "data", "", "bytes to write, 16 hex bytes"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int b = arg_get_int_def(ctx, 1, 1); + + uint8_t keytype = MF_KEY_A; + if (arg_get_lit(ctx, 2) && arg_get_lit(ctx, 3)) { + CLIParserFree(ctx); + PrintAndLogEx(WARNING, "Input key type must be A or B"); + return PM3_EINVARG; + } else if (arg_get_lit(ctx, 3)) { + keytype = MF_KEY_B;; + } + + int keylen = 0; + uint8_t key[6] = {0}; + CLIGetHexWithReturn(ctx, 4, key, &keylen); + + uint8_t block[MFBLOCK_SIZE] = {0x00}; + int blen = 0; + CLIGetHexWithReturn(ctx, 5, block, &blen); + CLIParserFree(ctx); + + if (blen != MFBLOCK_SIZE) { + PrintAndLogEx(WARNING, "expected 16 HEX bytes. got %i", blen); + return PM3_EINVARG; + } + + if (b < 0 || b >= MIFARE_4K_MAXBLOCK) { + PrintAndLogEx(FAILED, "target block number out-of-range, got %i", b); + return PM3_EINVARG; + } + + uint8_t blockno = (uint8_t)b; + + PrintAndLogEx(INFO, "Writing block no %d, key %c - %s", blockno, (keytype == MF_KEY_B) ? 'B' : 'A', sprint_hex_inrow(key, sizeof(key))); + PrintAndLogEx(INFO, "data: %s", sprint_hex(block, sizeof(block))); + + struct p { + uint8_t blockno; + uint8_t keytype; + uint8_t key[6]; + uint8_t data[MFBLOCK_SIZE]; // data to be written + } PACKED payload; + + payload.blockno = blockno; + payload.keytype = keytype; + memcpy(payload.key, key, sizeof(payload.key)); + memcpy(payload.data, block, sizeof(payload.data)); + + clearCommandBuffer(); + SendCommandNG(CMD_HF_MIFARE_G4_GDM_WRBL, (uint8_t *)&payload, sizeof(payload)); + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_HF_MIFARE_G4_GDM_WRBL, &resp, 1500) == false) { + PrintAndLogEx(WARNING, "command execute timeout"); + return PM3_ETIMEOUT; + } + + if (resp.status == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "Write ( " _GREEN_("ok") " )"); + PrintAndLogEx(HINT, "try `" _YELLOW_("hf mf rdbl") "` to verify"); + } else { + PrintAndLogEx(FAILED, "Write ( " _RED_("fail") " )"); + PrintAndLogEx(HINT, "Maybe access rights? Try specify keytype `" _YELLOW_("hf mf gdmsetblk -%c ...") "` instead", (keytype == MF_KEY_A) ? 'b' : 'a'); + } + return PM3_SUCCESS; +} static int CmdHF14AMfValue(const char *Cmd) { @@ -7825,6 +7907,8 @@ static command_t CommandTable[] = { {"gsave", CmdHF14AGen4Save, IfPm3Iso14443a, "Save dump from card into file or emulator"}, {"gsetblk", CmdHF14AGen4SetBlk, IfPm3Iso14443a, "Write block to card"}, {"gview", CmdHF14AGen4View, IfPm3Iso14443a, "View card"}, + {"-----------", CmdHelp, IfPm3Iso14443a, "-------------------- " _CYAN_("magic gen4 GDM") " --------------------------"}, + {"gdmsetblk", CmdHF14AGen4_GDM_SetBlk, IfPm3Iso14443a, "Write block to card"}, {"-----------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("ndef") " -----------------------"}, // {"ice", CmdHF14AMfice, IfPm3Iso14443a, "collect MIFARE Classic nonces to file"}, {"ndefformat", CmdHFMFNDEFFormat, IfPm3Iso14443a, "Format MIFARE Classic Tag as NFC Tag"}, diff --git a/client/src/mifare/mifarehost.c b/client/src/mifare/mifarehost.c index fe2cd1897..a897a507e 100644 --- a/client/src/mifare/mifarehost.c +++ b/client/src/mifare/mifarehost.c @@ -1214,6 +1214,7 @@ int mfG4SetBlock(uint8_t *pwd, uint8_t blockno, uint8_t *data, uint8_t workFlags return PM3_SUCCESS; } + // variables uint32_t cuid = 0; // uid part used for crypto1. @@ -1414,6 +1415,9 @@ int detect_mf_magic(bool is_mfc) { case MAGIC_GEN_4GTU: PrintAndLogEx(SUCCESS, "Magic capabilities : " _GREEN_("Gen 4 GTU")); break; + case MAGIC_GEN_4GDM: + PrintAndLogEx(SUCCESS, "Magic capabilities : " _GREEN_("Gen 4 GDM")); + break; case MAGIC_GEN_UNFUSED: PrintAndLogEx(SUCCESS, "Magic capabilities : " _GREEN_("Write Once / FUID")); break; diff --git a/include/pm3_cmd.h b/include/pm3_cmd.h index 40a263442..f490cc419 100644 --- a/include/pm3_cmd.h +++ b/include/pm3_cmd.h @@ -686,6 +686,10 @@ typedef struct { #define CMD_HF_MIFARE_G4_RDBL 0x0860 #define CMD_HF_MIFARE_G4_WRBL 0x0861 +// Gen 4 GDM magic cards +#define CMD_HF_MIFARE_G4_GDM_RDBL 0x0870 +#define CMD_HF_MIFARE_G4_GDM_WRBL 0x0871 + #define CMD_UNKNOWN 0xFFFF //Mifare simulation flags diff --git a/include/protocols.h b/include/protocols.h index 0cc89abb7..333dcdb71 100644 --- a/include/protocols.h +++ b/include/protocols.h @@ -192,6 +192,7 @@ ISO 7816-4 Basic interindustry commands. For command APDU's. #define MIFARE_MAGIC_GDM_AUTH_KEYA 0x80 #define MIFARE_MAGIC_GDM_AUTH_KEYB 0x81 +#define MIFARE_MAGIC_GDM_WRITEBLOCK 0xA8 #define MIFARE_EV1_PERSONAL_UID 0x40 #define MIFARE_EV1_SETMODE 0x43 @@ -259,6 +260,7 @@ ISO 7816-4 Basic interindustry commands. For command APDU's. #define MAGIC_NTAG21X 7 #define MAGIC_GEN_3 8 #define MAGIC_GEN_4GTU 9 +#define MAGIC_GEN_4GDM 10 // Commands for configuration of Gen4 GTU cards. // see https://github.com/RfidResearchGroup/proxmark3/blob/master/doc/magic_cards_notes.md