Changed hf mf gdmcfg/gdmsetcfg commands to support Gen1a and GDM Alt magic wakeups

This was implemented with a new pair of RPCs CMD_HF_MIFARE_READBL_EX and CMD_HF_MIFARE_WRITEBL_EX
these RPCs support all combinations of read/write commands, wakeup, and auth options so
in time can replace the other MFC read/write commands too reduce armsrc code size
and complexity.

Also added config parsing for the gdm cfg block when reading with hf mf gdmcfg and
explicitly with hf mf gdmparsecfg.
This commit is contained in:
nvx 2024-01-26 20:09:08 +10:00
parent b4ae4c429f
commit 49f7ae57dc
11 changed files with 374 additions and 158 deletions

View file

@ -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... 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] ## [unreleased][unreleased]
- Added `hf mf gdmparsecfg` command to decode the GDM/USCUID config block and made `hf mf gdmcfg` show the parsed config on read (@nvx)
- Changed `hf mf gdmcfg/gdmsetcfg` commands to support Gen1a and GDM Alt magic wakeups (@nvx)
- Fixed `hf 15 writedsfid` - long wait after write and dont force -o flag (@iceman1001) - Fixed `hf 15 writedsfid` - long wait after write and dont force -o flag (@iceman1001)
- Changed `hf 14a sniff -i` - now supports the interactive flag (@iceman1001) - Changed `hf 14a sniff -i` - now supports the interactive flag (@iceman1001)
- Added `HF_15SIM` standalone mode that dump then simulate iso15 tags (@lnv42) - Added `HF_15SIM` standalone mode that dump then simulate iso15 tags (@lnv42)

View file

@ -1615,10 +1615,17 @@ static void PacketReceived(PacketCommandNG *packet) {
case CMD_HF_MIFARE_READBL: { case CMD_HF_MIFARE_READBL: {
mf_readblock_t *payload = (mf_readblock_t *)packet->data.asBytes; mf_readblock_t *payload = (mf_readblock_t *)packet->data.asBytes;
uint8_t outbuf[16]; uint8_t outbuf[16];
int16_t retval = mifare_cmd_readblocks(MIFARE_AUTH_KEYA + (payload->keytype & 1), payload->key, ISO14443A_CMD_READBLOCK, payload->blockno, 1, outbuf); int16_t retval = mifare_cmd_readblocks(MF_WAKE_WUPA, MIFARE_AUTH_KEYA + (payload->keytype & 1), payload->key, ISO14443A_CMD_READBLOCK, payload->blockno, 1, outbuf);
reply_ng(CMD_HF_MIFARE_READBL, retval, outbuf, sizeof(outbuf)); reply_ng(CMD_HF_MIFARE_READBL, retval, outbuf, sizeof(outbuf));
break; break;
} }
case CMD_HF_MIFARE_READBL_EX: {
mf_readblock_ex_t *payload = (mf_readblock_ex_t *)packet->data.asBytes;
uint8_t outbuf[16];
int16_t retval = mifare_cmd_readblocks(payload->wakeup, payload->auth_cmd, payload->key, payload->read_cmd, payload->block_no, 1, outbuf);
reply_ng(CMD_HF_MIFARE_READBL_EX, retval, outbuf, sizeof(outbuf));
break;
}
case CMD_HF_MIFAREU_READBL: { case CMD_HF_MIFAREU_READBL: {
MifareUReadBlock(packet->oldarg[0], packet->oldarg[1], packet->data.asBytes); MifareUReadBlock(packet->oldarg[0], packet->oldarg[1], packet->data.asBytes);
break; break;
@ -1645,7 +1652,7 @@ static void PacketReceived(PacketCommandNG *packet) {
uint8_t *key = packet->data.asBytes; uint8_t *key = packet->data.asBytes;
uint8_t *block_data = packet->data.asBytes + 10; uint8_t *block_data = packet->data.asBytes + 10;
int16_t retval = mifare_cmd_writeblocks(MIFARE_AUTH_KEYA + (key_type & 1), key, ISO14443A_CMD_WRITEBLOCK, block_no, 1, block_data); int16_t retval = mifare_cmd_writeblocks(MF_WAKE_WUPA, MIFARE_AUTH_KEYA + (key_type & 1), key, ISO14443A_CMD_WRITEBLOCK, block_no, 1, block_data);
// convert ng style retval to old status // convert ng style retval to old status
if (retval >= 0) { if (retval >= 0) {
@ -1655,6 +1662,12 @@ static void PacketReceived(PacketCommandNG *packet) {
reply_mix(CMD_ACK, retval, 0, 0, 0, 0); reply_mix(CMD_ACK, retval, 0, 0, 0, 0);
break; break;
} }
case CMD_HF_MIFARE_WRITEBL_EX: {
mf_writeblock_ex_t *payload = (mf_writeblock_ex_t *)packet->data.asBytes;
int16_t retval = mifare_cmd_writeblocks(payload->wakeup, payload->auth_cmd, payload->key, payload->write_cmd, payload->block_no, 1, payload->block_data);
reply_ng(CMD_HF_MIFARE_WRITEBL_EX, retval, NULL, 0);
break;
}
case CMD_HF_MIFARE_VALUE: { case CMD_HF_MIFARE_VALUE: {
MifareValue(packet->oldarg[0], packet->oldarg[1], packet->oldarg[2], packet->data.asBytes); MifareValue(packet->oldarg[0], packet->oldarg[1], packet->oldarg[2], packet->data.asBytes);
break; break;
@ -1822,26 +1835,6 @@ static void PacketReceived(PacketCommandNG *packet) {
MifareG4WriteBlk(payload->blockno, payload->pwd, payload->data, payload->workFlags); MifareG4WriteBlk(payload->blockno, payload->pwd, payload->data, payload->workFlags);
break; break;
} }
case CMD_HF_MIFARE_G4_GDM_CONFIG: {
struct p {
uint8_t key[6];
} PACKED;
struct p *payload = (struct p *) packet->data.asBytes;
uint8_t outbuf[16];
int16_t retval = mifare_cmd_readblocks(MIFARE_MAGIC_GDM_AUTH_KEY, payload->key, MIFARE_MAGIC_GDM_READ_CFG, 0, 1, outbuf);
reply_ng(CMD_HF_MIFARE_G4_GDM_CONFIG, retval, outbuf, sizeof(outbuf));
break;
}
case CMD_HF_MIFARE_G4_GDM_WRCFG: {
struct p {
uint8_t data[16];
} PACKED;
struct p *payload = (struct p *) packet->data.asBytes;
uint8_t key[6] = {0, 0, 0, 0, 0, 0};
int16_t retval = mifare_cmd_writeblocks(MIFARE_MAGIC_GDM_AUTH_KEY, key, MIFARE_MAGIC_GDM_WRITE_CFG, 0, 1, payload->data);
reply_ng(CMD_HF_MIFARE_G4_GDM_WRCFG, retval, NULL, 0);
break;
}
case CMD_HF_MIFARE_G4_GDM_WRBL: { case CMD_HF_MIFARE_G4_GDM_WRBL: {
struct p { struct p {
uint8_t blockno; uint8_t blockno;
@ -1849,7 +1842,7 @@ static void PacketReceived(PacketCommandNG *packet) {
uint8_t data[16]; // data to be written uint8_t data[16]; // data to be written
} PACKED; } PACKED;
struct p *payload = (struct p *) packet->data.asBytes; struct p *payload = (struct p *) packet->data.asBytes;
int16_t retval = mifare_cmd_writeblocks(MIFARE_MAGIC_GDM_AUTH_KEY, payload->key, MIFARE_MAGIC_GDM_WRITEBLOCK, payload->blockno, 1, payload->data); int16_t retval = mifare_cmd_writeblocks(MF_WAKE_WUPA, MIFARE_MAGIC_GDM_AUTH_KEY, payload->key, MIFARE_MAGIC_GDM_WRITEBLOCK, payload->blockno, 1, payload->data);
reply_ng(CMD_HF_MIFARE_G4_GDM_WRBL, retval, NULL, 0); reply_ng(CMD_HF_MIFARE_G4_GDM_WRBL, retval, NULL, 0);
break; break;
} }

View file

@ -144,8 +144,13 @@ static hf14a_config hf14aconfig = { 0, 0, 0, 0, 0 } ;
// Polling frames and configurations // Polling frames and configurations
static iso14a_polling_parameters_t WUPA_POLLING_PARAMETERS = { iso14a_polling_parameters_t WUPA_POLLING_PARAMETERS = {
.frames = { {{ 0x52 }, 1, 7, 0} }, .frames = { {{ ISO14443A_CMD_WUPA }, 1, 7, 0} },
.frame_count = 1,
.extra_timeout = 0,
};
iso14a_polling_parameters_t REQA_POLLING_PARAMETERS = {
.frames = { {{ ISO14443A_CMD_REQA }, 1, 7, 0} },
.frame_count = 1, .frame_count = 1,
.extra_timeout = 0, .extra_timeout = 0,
}; };

View file

@ -175,4 +175,7 @@ void DetectNACKbug(void);
bool GetIso14443aAnswerFromTag_Thinfilm(uint8_t *receivedResponse, uint8_t *received_len); bool GetIso14443aAnswerFromTag_Thinfilm(uint8_t *receivedResponse, uint8_t *received_len);
extern iso14a_polling_parameters_t WUPA_POLLING_PARAMETERS;
extern iso14a_polling_parameters_t REQA_POLLING_PARAMETERS;
#endif /* __ISO14443A_H */ #endif /* __ISO14443A_H */

View file

@ -56,46 +56,108 @@
static uint8_t dummy_answer = 0; static uint8_t dummy_answer = 0;
//----------------------------------------------------------------------------- // magic uid card generation 1 commands
// Select, Authenticate, Read a MIFARE tag. static uint8_t wupC1[] = { MIFARE_MAGICWUPC1 };
// key_auth_cmd is one of MIFARE_AUTH_KEYA, MIFARE_AUTH_KEYB, or MIFARE_MAGIC_GDM_AUTH_KEY static uint8_t wupC2[] = { MIFARE_MAGICWUPC2 };
// read_cmd is one of ISO14443A_CMD_READBLOCK, MIFARE_MAGIC_GDM_READBLOCK, or MIFARE_MAGIC_GDM_READ_CFG static uint8_t wipeC[] = { MIFARE_MAGICWIPEC };
// block_data must be 16*count bytes large // GDM alt magic wakeup
// block_no through block_no+count-1 normally needs to be within the same sector static uint8_t wupGDM1[] = { MIFARE_MAGIC_GDM_WUPC1 };
//----------------------------------------------------------------------------- static uint8_t wupGDM2[] = { MIFARE_MAGIC_GDM_WUPC2 };
int16_t mifare_cmd_readblocks(uint8_t key_auth_cmd, uint8_t *key, uint8_t read_cmd, uint8_t block_no, uint8_t count, uint8_t *block_data) {
uint64_t ui64key = bytes_to_num(key, 6); static bool mifare_wakeup_auth(struct Crypto1State *pcs, MifareWakeupType wakeup, uint8_t key_auth_cmd, uint8_t *key, uint8_t block_no) {
uint8_t uid[10] = {0x00};
uint32_t cuid = 0; uint32_t cuid = 0;
struct Crypto1State mpcs = {0, 0}; uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE] = {0x00};
struct Crypto1State *pcs = &mpcs; uint8_t receivedAnswerPar[MAX_MIFARE_PARITY_SIZE] = {0x00};
iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);
clear_trace(); clear_trace();
set_tracing(true); set_tracing(true);
uint32_t timeout = iso14a_get_timeout();
LED_A_ON(); LED_A_ON();
LED_B_OFF(); LED_B_OFF();
LED_C_OFF(); LED_C_OFF();
switch (wakeup) {
case MF_WAKE_NONE:
break;
case MF_WAKE_WUPA:
if (!iso14443a_select_cardEx(NULL, NULL, &cuid, true, 0, true, &WUPA_POLLING_PARAMETERS)) {
if (g_dbglevel >= DBG_ERROR) Dbprintf("Can't select card");
return false;
};
break;
case MF_WAKE_REQA:
if (!iso14443a_select_cardEx(NULL, NULL, &cuid, true, 0, true, &REQA_POLLING_PARAMETERS)) {
if (g_dbglevel >= DBG_ERROR) Dbprintf("Can't select card");
return false;
};
break;
case MF_WAKE_GEN1A:
ReaderTransmitBitsPar(wupC1, 7, NULL, NULL);
if (!ReaderReceive(receivedAnswer, receivedAnswerPar) || (receivedAnswer[0] != 0x0a)) {
if (g_dbglevel >= DBG_ERROR) Dbprintf("wupC1 error");
return false;
}
ReaderTransmit(wupC2, sizeof(wupC2), NULL);
if (!ReaderReceive(receivedAnswer, receivedAnswerPar) || (receivedAnswer[0] != 0x0a)) {
if (g_dbglevel >= DBG_INFO) Dbprintf("Assuming Magic Gen 1B tag. [wupC2 failed]");
}
break;
case MF_WAKE_GEN1B:
ReaderTransmitBitsPar(wupC1, 7, NULL, NULL);
if (!ReaderReceive(receivedAnswer, receivedAnswerPar) || (receivedAnswer[0] != 0x0a)) {
if (g_dbglevel >= DBG_ERROR) Dbprintf("wupC1 error");
return false;
}
break;
case MF_WAKE_GDM_ALT:
ReaderTransmitBitsPar(wupGDM1, 7, NULL, NULL);
if (!ReaderReceive(receivedAnswer, receivedAnswerPar) || (receivedAnswer[0] != 0x0a)) {
if (g_dbglevel >= DBG_ERROR) Dbprintf("wupGDM1 error");
return false;
}
ReaderTransmit(wupGDM2, sizeof(wupGDM2), NULL);
if (!ReaderReceive(receivedAnswer, receivedAnswerPar) || (receivedAnswer[0] != 0x0a)) {
if (g_dbglevel >= DBG_INFO) Dbprintf("wupGDM2 error");
// maybe this is fine on some tags?
}
break;
}
if (key_auth_cmd != 0) {
uint64_t ui64key = bytes_to_num(key, 6);
if (mifare_classic_authex_cmd(pcs, cuid, block_no, key_auth_cmd, ui64key, AUTH_FIRST, NULL, NULL, NULL)) {
if (g_dbglevel >= DBG_ERROR) Dbprintf("Auth error");
return false;
}
}
return true;
}
//-----------------------------------------------------------------------------
// Select, Authenticate, Read a MIFARE tag.
// wakeup determines the type of wakeup
// key_auth_cmd is usually one of MIFARE_AUTH_KEYA, MIFARE_AUTH_KEYB, MIFARE_MAGIC_GDM_AUTH_KEY or 0 to disable auth
// read_cmd is usually one of ISO14443A_CMD_READBLOCK, MIFARE_MAGIC_GDM_READBLOCK, or MIFARE_MAGIC_GDM_READ_CFG
// block_data must be 16*count bytes large
// block_no through block_no+count-1 normally needs to be within the same sector
//-----------------------------------------------------------------------------
int16_t mifare_cmd_readblocks(MifareWakeupType wakeup, uint8_t key_auth_cmd, uint8_t *key, uint8_t read_cmd, uint8_t block_no, uint8_t count, uint8_t *block_data) {
struct Crypto1State mpcs = {0, 0};
struct Crypto1State *pcs = (key_auth_cmd == 0) ? NULL : &mpcs;;
uint32_t timeout = iso14a_get_timeout();
int retval = PM3_SUCCESS; int retval = PM3_SUCCESS;
if (iso14443a_select_card(uid, NULL, &cuid, true, 0, true) == false) { if (mifare_wakeup_auth(pcs, wakeup, key_auth_cmd, key, block_no) == false) {
if (g_dbglevel >= DBG_ERROR) Dbprintf("Can't select card");
retval = PM3_ESOFT; retval = PM3_ESOFT;
goto OUT; goto OUT;
} }
if (mifare_classic_authex_cmd(pcs, cuid, block_no, key_auth_cmd, ui64key, AUTH_FIRST, NULL, NULL, NULL)) {
if (g_dbglevel >= DBG_ERROR) Dbprintf("Auth error");
retval = PM3_ESOFT;
goto OUT;
};
// frame waiting time (FWT) in 1/fc // frame waiting time (FWT) in 1/fc
uint32_t fwt = 256 * 16 * (1 << 7); uint32_t fwt = 256 * 16 * (1 << 7);
iso14a_set_timeout(fwt / (8 * 16)); iso14a_set_timeout(fwt / (8 * 16));
@ -127,42 +189,22 @@ OUT:
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Select, Authenticate, Write a MIFARE tag. // Select, Authenticate, Write a MIFARE tag.
// key_auth_cmd is one of MIFARE_AUTH_KEYA, MIFARE_AUTH_KEYB, or MIFARE_MAGIC_GDM_AUTH_KEY // wakeup determines the type of wakeup
// write_cmd is one of ISO14443A_CMD_WRITEBLOCK, MIFARE_MAGIC_GDM_WRITEBLOCK, or MIFARE_MAGIC_GDM_WRITE_CFG // key_auth_cmd is usually one of MIFARE_AUTH_KEYA, MIFARE_AUTH_KEYB, MIFARE_MAGIC_GDM_AUTH_KEY or 0 to disable auth
// write_cmd is usually one of ISO14443A_CMD_WRITEBLOCK, MIFARE_MAGIC_GDM_WRITEBLOCK, or MIFARE_MAGIC_GDM_WRITE_CFG
// block_data must be 16*count bytes large // block_data must be 16*count bytes large
// block_no through block_no+count-1 normally needs to be within the same sector // block_no through block_no+count-1 normally needs to be within the same sector
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
int16_t mifare_cmd_writeblocks(uint8_t key_auth_cmd, uint8_t *key, uint8_t write_cmd, uint8_t block_no, uint8_t count, uint8_t *block_data) { int16_t mifare_cmd_writeblocks(MifareWakeupType wakeup, uint8_t key_auth_cmd, uint8_t *key, uint8_t write_cmd, uint8_t block_no, uint8_t count, uint8_t *block_data) {
uint64_t ui64key = bytes_to_num(key, 6);
uint8_t uid[10] = {0x00};
uint32_t cuid = 0;
struct Crypto1State mpcs = {0, 0}; struct Crypto1State mpcs = {0, 0};
struct Crypto1State *pcs = &mpcs; struct Crypto1State *pcs = (key_auth_cmd == 0) ? NULL : &mpcs;;
iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);
clear_trace();
set_tracing(true);
LED_A_ON();
LED_B_OFF();
LED_C_OFF();
int retval = PM3_SUCCESS; int retval = PM3_SUCCESS;
if (!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) { if (mifare_wakeup_auth(pcs, wakeup, key_auth_cmd, key, block_no) == false) {
if (g_dbglevel >= DBG_ERROR) Dbprintf("Can't select card");
retval = PM3_ESOFT; retval = PM3_ESOFT;
goto OUT; goto OUT;
}; }
if (mifare_classic_authex_cmd(pcs, cuid, block_no, key_auth_cmd, ui64key, AUTH_FIRST, NULL, NULL, NULL)) {
if (g_dbglevel >= DBG_ERROR) Dbprintf("Auth error");
retval = PM3_ESOFT;
goto OUT;
};
for (uint8_t i = 0; i < count; i++) { for (uint8_t i = 0; i < count; i++) {
int res = mifare_classic_writeblock_ex(pcs, block_no + i, block_data + (i * 16), write_cmd); int res = mifare_classic_writeblock_ex(pcs, block_no + i, block_data + (i * 16), write_cmd);
@ -202,7 +244,7 @@ void MifareReadSector(uint8_t sector_no, uint8_t key_type, uint8_t *key) {
uint8_t num_blocks = NumBlocksPerSector(sector_no); uint8_t num_blocks = NumBlocksPerSector(sector_no);
uint8_t outbuf[16 * 16]; uint8_t outbuf[16 * 16];
int16_t retval = mifare_cmd_readblocks(MIFARE_AUTH_KEYA + (key_type & 1), key, ISO14443A_CMD_READBLOCK, block_no, num_blocks, outbuf); int16_t retval = mifare_cmd_readblocks(MF_WAKE_WUPA, MIFARE_AUTH_KEYA + (key_type & 1), key, ISO14443A_CMD_READBLOCK, block_no, num_blocks, outbuf);
reply_old(CMD_ACK, retval == PM3_SUCCESS, 0, 0, outbuf, 16 * num_blocks); reply_old(CMD_ACK, retval == PM3_SUCCESS, 0, 0, outbuf, 16 * num_blocks);
} }
@ -2204,13 +2246,6 @@ int MifareECardLoad(uint8_t sectorcnt, uint8_t keytype) {
// bit 5 - need to set datain instead of issuing USB reply (called via ARM for StandAloneMode14a) // bit 5 - need to set datain instead of issuing USB reply (called via ARM for StandAloneMode14a)
// bit 6 - wipe tag. // bit 6 - wipe tag.
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// magic uid card generation 1 commands
static uint8_t wupC1[] = { MIFARE_MAGICWUPC1 };
static uint8_t wupC2[] = { MIFARE_MAGICWUPC2 };
static uint8_t wipeC[] = { MIFARE_MAGICWIPEC };
// GDM alt magic wakeup
static uint8_t wupGDM1[] = { MIFARE_MAGIC_GDM_WUPC1 };
//static uint8_t wupGDM2[] = { MIFARE_MAGIC_GDM_WUPC2 };
void MifareCSetBlock(uint32_t arg0, uint32_t arg1, uint8_t *datain) { void MifareCSetBlock(uint32_t arg0, uint32_t arg1, uint8_t *datain) {
@ -2422,8 +2457,8 @@ static void mf_reset_card(void) {
void MifareCIdent(bool is_mfc, uint8_t keytype, uint8_t *key) { void MifareCIdent(bool is_mfc, uint8_t keytype, uint8_t *key) {
// variables // variables
uint8_t rec[1] = {0x00}; uint8_t rec[MAX_MIFARE_PARITY_SIZE] = {0x00};
uint8_t recpar[1] = {0x00}; uint8_t recpar[MAX_MIFARE_PARITY_SIZE] = {0x00};
uint8_t rats[4] = {ISO14443A_CMD_RATS, 0x80, 0x31, 0x73}; uint8_t rats[4] = {ISO14443A_CMD_RATS, 0x80, 0x31, 0x73};
uint8_t rdblf0[4] = {ISO14443A_CMD_READBLOCK, 0xF0, 0x8D, 0x5f}; uint8_t rdblf0[4] = {ISO14443A_CMD_READBLOCK, 0xF0, 0x8D, 0x5f};
uint8_t rdbl00[4] = {ISO14443A_CMD_READBLOCK, 0x00, 0x02, 0xa8}; uint8_t rdbl00[4] = {ISO14443A_CMD_READBLOCK, 0x00, 0x02, 0xa8};
@ -2622,7 +2657,7 @@ void MifareCIdent(bool is_mfc, uint8_t keytype, uint8_t *key) {
data[data_off++] = MAGIC_GDM_WUP_40; data[data_off++] = MAGIC_GDM_WUP_40;
} }
// GEM alt magic wakeup (20) // GDM alt magic wakeup (20)
ReaderTransmitBitsPar(wupGDM1, 7, NULL, NULL); ReaderTransmitBitsPar(wupGDM1, 7, NULL, NULL);
if (ReaderReceive(rec, recpar) && (rec[0] == 0x0a)) { if (ReaderReceive(rec, recpar) && (rec[0] == 0x0a)) {
data[data_off++] = MAGIC_GDM_WUP_20; data[data_off++] = MAGIC_GDM_WUP_20;
@ -2753,7 +2788,7 @@ void MifareHasStaticEncryptedNonce(uint8_t block_no, uint8_t key_type, uint8_t *
oldntenc = ntenc; oldntenc = ntenc;
} else if (ntenc == oldntenc) { } else if (ntenc == oldntenc) {
enc_counter++; enc_counter++;
} }
} }
if (enc_counter) { if (enc_counter) {

View file

@ -18,9 +18,10 @@
#define __MIFARECMD_H #define __MIFARECMD_H
#include "common.h" #include "common.h"
#include "pm3_cmd.h"
int16_t mifare_cmd_readblocks(uint8_t key_auth_cmd, uint8_t *key, uint8_t read_cmd, uint8_t block_no, uint8_t count, uint8_t *block_data); int16_t mifare_cmd_readblocks(MifareWakeupType wakeup, uint8_t key_auth_cmd, uint8_t *key, uint8_t read_cmd, uint8_t block_no, uint8_t count, uint8_t *block_data);
int16_t mifare_cmd_writeblocks(uint8_t key_auth_cmd, uint8_t *key, uint8_t write_cmd, uint8_t block_no, uint8_t count, uint8_t *block_data); int16_t mifare_cmd_writeblocks(MifareWakeupType wakeup, uint8_t key_auth_cmd, uint8_t *key, uint8_t write_cmd, uint8_t block_no, uint8_t count, uint8_t *block_data);
void MifareReadSector(uint8_t sector_no, uint8_t key_type, uint8_t *key); void MifareReadSector(uint8_t sector_no, uint8_t key_type, uint8_t *key);
void MifareValue(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain); void MifareValue(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain);

View file

@ -8444,57 +8444,51 @@ static int CmdHF14AGen4ChangePwd(const char *Cmd) {
return PM3_SUCCESS; return PM3_SUCCESS;
} }
static int CmdHF14AGen4_GDM_Cfg(const char *Cmd) { static void parse_gdm_cfg(const uint8_t *d) {
CLIParserContext *ctx; PrintAndLogEx(SUCCESS, "Config... " _YELLOW_("%s"), sprint_hex(d, MFBLOCK_SIZE));
CLIParserInit(&ctx, "hf mf gdmcfg", PrintAndLogEx(SUCCESS, " " _YELLOW_("%02X %02X") " .......................................... %s %s", d[0], d[1], (d[0] == 0x85 && d[1] == 0x00) ? "Magic wakeup disabled" : _GREEN_("Magic wakeup enabled"), (d[0] == 0x85 && d[1] == 0x00) ? "" : ((d[0] == 0x7A && d[1] == 0xFF) ? _GREEN_("with GDM config block access") : _RED_("without GDM config block access")));
"Get configuration data from magic gen4 GDM card.", PrintAndLogEx(SUCCESS, " " _YELLOW_("%02X") " ....................................... Magic wakeup style %s", d[2], ((d[2] == 0x85) ? "GDM 20(7)/23": "Gen1a 40(7)/43"));
"hf mf gdmcfg\n" PrintAndLogEx(SUCCESS, " " _YELLOW_("%02X %02X %02X") " .............................. Unknown", d[3], d[4], d[5]);
); PrintAndLogEx(SUCCESS, " " _YELLOW_("%02X") " ........................... %s", d[6], (d[6] == 0x5A) ? "Key B use blocked when readable by ACL" : "Key B use allowed when readable by ACL");
void *argtable[] = { PrintAndLogEx(SUCCESS, " " _YELLOW_("%02X") " ........................ %s", d[7], (d[7] == 0x5A) ? _GREEN_("Block 0 Direct Write Enabled (CUID)") : "Block 0 Direct Write Disabled (CUID)");
arg_param_begin, PrintAndLogEx(SUCCESS, " " _YELLOW_("%02X") " ..................... Unknown", d[8]);
arg_str0("k", "key", "<hex>", "key 6 bytes"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
int keylen = 0; const char *pers;
uint8_t key[6] = {0}; switch (d[9]) {
CLIGetHexWithReturn(ctx, 1, key, &keylen); case 0x5A:
CLIParserFree(ctx); pers = _YELLOW_("Unfused");
break;
// validate args case 0xC3:
if (keylen != 6 && keylen != 0) { pers = _CYAN_("UIDFO, double size UID");
PrintAndLogEx(FAILED, "Must specify 6 bytes, got " _YELLOW_("%u"), keylen); break;
return PM3_EINVARG; case 0xA5:
pers = _CYAN_("UIDF1, double size UID, optional usage of selection process shortcut");
break;
case 0x87:
pers = _GREEN_("UIDF2, single size random ID");
break;
case 0x69:
pers = _GREEN_("UIDF3, single size NUID");
break;
default:
pers = "4B UID from Block 0";
break;
} }
PrintAndLogEx(SUCCESS, " " _YELLOW_("%02X") " .................. MFC EV1 personalization: %s", d[9], pers);
struct p { PrintAndLogEx(SUCCESS, " " _YELLOW_("%02X") " ............... %s", d[10], (d[10] == 0x5A) ? _GREEN_("Shadow mode enabled") : "Shadow mode disabled");
uint8_t key[6]; PrintAndLogEx(SUCCESS, " " _YELLOW_("%02X") " ............. %s", d[11], (d[11] == 0x5A) ? _GREEN_("Magic auth enabled") : "Magic auth disabled");
} PACKED payload; PrintAndLogEx(SUCCESS, " " _YELLOW_("%02X") " ........... %s", d[12], (d[12] == 0x5A) ? _GREEN_("Static encrypted nonce enabled") : "Static encrypted nonce disabled");
memcpy(payload.key, key, sizeof(payload.key)); PrintAndLogEx(SUCCESS, " " _YELLOW_("%02X") " ......... %s", d[13], (d[13] == 0x5A) ? _GREEN_("MFC EV1 signature enabled") : "MFC EV1 signature disabled");
PrintAndLogEx(SUCCESS, " " _YELLOW_("%02X") " ...... Unknown", d[14]);
clearCommandBuffer(); PrintAndLogEx(SUCCESS, " " _YELLOW_("%02X") " ... SAK", d[15]);
SendCommandNG(CMD_HF_MIFARE_G4_GDM_CONFIG, (uint8_t *)&payload, sizeof(payload));
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_HF_MIFARE_G4_GDM_CONFIG, &resp, 1500) == false) {
PrintAndLogEx(WARNING, "command execute timeout");
return PM3_ETIMEOUT;
}
if (resp.status == PM3_SUCCESS) {
uint8_t *d = resp.data.asBytes;
PrintAndLogEx(SUCCESS, "config... %s", sprint_hex(d, resp.length));
PrintAndLogEx(NORMAL, "");
}
return resp.status;
} }
static int CmdHF14AGen4_GDM_SetCfg(const char *Cmd) { static int CmdHF14AGen4_GDM_ParseCfg(const char *Cmd) {
CLIParserContext *ctx; CLIParserContext *ctx;
CLIParserInit(&ctx, "hf mf gdmsetcfg", CLIParserInit(&ctx, "hf mf gdmparsecfg",
"Set configuration data on a magic gen4 GDM card", "Parse configuration data on a magic gen4 GDM card",
"hf mf gdmsetcfg -d 850000000000000000005A5A00000008" "hf mf gdmparsecfg -d 850000000000000000005A5A00000008"
); );
void *argtable[] = { void *argtable[] = {
arg_param_begin, arg_param_begin,
@ -8513,16 +8507,151 @@ static int CmdHF14AGen4_GDM_SetCfg(const char *Cmd) {
return PM3_EINVARG; return PM3_EINVARG;
} }
struct p { parse_gdm_cfg(block);
uint8_t data[MFBLOCK_SIZE];
} PACKED payload;
memcpy(payload.data, block, sizeof(payload.data)); return PM3_SUCCESS;
}
static int CmdHF14AGen4_GDM_Cfg(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf mf gdmcfg",
"Get configuration data from magic gen4 GDM card.",
"hf mf gdmcfg\n"
);
void *argtable[] = {
arg_param_begin,
arg_str0("k", "key", "<hex>", "key 6 bytes (only for regular wakeup)"),
arg_lit0(NULL, "gen1a", "use gen1a (40/43) magic wakeup"),
arg_lit0(NULL, "gdm", "use gdm alt (20/23) magic wakeup"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
int keylen = 0;
uint8_t key[6] = {0};
CLIGetHexWithReturn(ctx, 1, key, &keylen);
bool gen1a = arg_get_lit(ctx, 2);
bool gdm = arg_get_lit(ctx, 3);
CLIParserFree(ctx);
// validate args
if (keylen != 6 && keylen != 0) {
PrintAndLogEx(FAILED, "Must specify 6 bytes, got " _YELLOW_("%u"), keylen);
return PM3_EINVARG;
}
if (gen1a && gdm) {
PrintAndLogEx(FAILED, "Can only specify a single magic wakeup command");
return PM3_EINVARG;
}
if ((gen1a || gdm) && keylen != 0) {
PrintAndLogEx(FAILED, "Cannot use a key in combination with a magic wakeup");
return PM3_EINVARG;
}
mf_readblock_ex_t payload = {
.read_cmd = MIFARE_MAGIC_GDM_READ_CFG,
.block_no = 0,
};
memcpy(payload.key, key, sizeof(payload.key));
if (gen1a) {
payload.wakeup = MF_WAKE_GEN1A;
payload.auth_cmd = 0;
} else if (gdm) {
payload.wakeup = MF_WAKE_GDM_ALT;
payload.auth_cmd = 0;
} else {
payload.wakeup = MF_WAKE_WUPA;
payload.auth_cmd = MIFARE_MAGIC_GDM_AUTH_KEY;
}
clearCommandBuffer(); clearCommandBuffer();
SendCommandNG(CMD_HF_MIFARE_G4_GDM_WRCFG, (uint8_t *)&payload, sizeof(payload)); SendCommandNG(CMD_HF_MIFARE_READBL_EX, (uint8_t *)&payload, sizeof(payload));
PacketResponseNG resp; PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_HF_MIFARE_G4_GDM_WRCFG, &resp, 1500) == false) { if (WaitForResponseTimeout(CMD_HF_MIFARE_READBL_EX, &resp, 1500) == false) {
PrintAndLogEx(WARNING, "command execute timeout");
return PM3_ETIMEOUT;
}
if (resp.status == PM3_SUCCESS && resp.length == MFBLOCK_SIZE) {
parse_gdm_cfg(resp.data.asBytes);
PrintAndLogEx(NORMAL, "");
}
return resp.status;
}
static int CmdHF14AGen4_GDM_SetCfg(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf mf gdmsetcfg",
"Set configuration data on a magic gen4 GDM card",
"hf mf gdmsetcfg -d 850000000000000000005A5A00000008"
);
void *argtable[] = {
arg_param_begin,
arg_str1("d", "data", "<hex>", "bytes to write, 16 hex bytes"),
arg_str0("k", "key", "<hex>", "key 6 bytes (only for regular wakeup)"),
arg_lit0(NULL, "gen1a", "use gen1a (40/43) magic wakeup"),
arg_lit0(NULL, "gdm", "use gdm alt (20/23) magic wakeup"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, false);
uint8_t block[MFBLOCK_SIZE] = {0x00};
int blen = 0;
CLIGetHexWithReturn(ctx, 1, block, &blen);
int keylen = 0;
uint8_t key[6] = {0};
CLIGetHexWithReturn(ctx, 2, key, &keylen);
bool gen1a = arg_get_lit(ctx, 3);
bool gdm = arg_get_lit(ctx, 4);
CLIParserFree(ctx);
if (blen != MFBLOCK_SIZE) {
PrintAndLogEx(WARNING, "expected %u HEX bytes. got %i", MFBLOCK_SIZE, blen);
return PM3_EINVARG;
}
if (keylen != 6 && keylen != 0) {
PrintAndLogEx(FAILED, "Must specify 6 bytes, got " _YELLOW_("%u"), keylen);
return PM3_EINVARG;
}
if (gen1a && gdm) {
PrintAndLogEx(FAILED, "Can only specify a single magic wakeup command");
return PM3_EINVARG;
}
if ((gen1a || gdm) && keylen != 0) {
PrintAndLogEx(FAILED, "Cannot use a key in combination with a magic wakeup");
return PM3_EINVARG;
}
mf_writeblock_ex_t payload = {
.write_cmd = MIFARE_MAGIC_GDM_WRITE_CFG,
.block_no = 0,
};
memcpy(payload.block_data, block, sizeof(payload.block_data));
memcpy(payload.key, key, sizeof(payload.key));
if (gen1a) {
payload.wakeup = MF_WAKE_GEN1A;
payload.auth_cmd = 0;
} else if (gdm) {
payload.wakeup = MF_WAKE_GDM_ALT;
payload.auth_cmd = 0;
} else {
payload.wakeup = MF_WAKE_WUPA;
payload.auth_cmd = MIFARE_MAGIC_GDM_AUTH_KEY;
}
clearCommandBuffer();
SendCommandNG(CMD_HF_MIFARE_WRITEBL_EX, (uint8_t *)&payload, sizeof(payload));
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_HF_MIFARE_WRITEBL_EX, &resp, 1500) == false) {
PrintAndLogEx(WARNING, "command execute timeout"); PrintAndLogEx(WARNING, "command execute timeout");
return PM3_ETIMEOUT; return PM3_ETIMEOUT;
} }
@ -9344,6 +9473,7 @@ static command_t CommandTable[] = {
{"-----------", CmdHelp, IfPm3Iso14443a, "-------------------- " _CYAN_("magic gen4 GDM") " --------------------------"}, {"-----------", CmdHelp, IfPm3Iso14443a, "-------------------- " _CYAN_("magic gen4 GDM") " --------------------------"},
{"gdmcfg", CmdHF14AGen4_GDM_Cfg, IfPm3Iso14443a, "Read config block from card"}, {"gdmcfg", CmdHF14AGen4_GDM_Cfg, IfPm3Iso14443a, "Read config block from card"},
{"gdmsetcfg", CmdHF14AGen4_GDM_SetCfg, IfPm3Iso14443a, "Write config block to card"}, {"gdmsetcfg", CmdHF14AGen4_GDM_SetCfg, IfPm3Iso14443a, "Write config block to card"},
{"gdmparsecfg", CmdHF14AGen4_GDM_ParseCfg, AlwaysAvailable, "Parse config block to card"},
{"gdmsetblk", CmdHF14AGen4_GDM_SetBlk, IfPm3Iso14443a, "Write block to card"}, {"gdmsetblk", CmdHF14AGen4_GDM_SetBlk, IfPm3Iso14443a, "Write block to card"},
{"-----------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("ndef") " -----------------------"}, {"-----------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("ndef") " -----------------------"},
// {"ice", CmdHF14AMfice, IfPm3Iso14443a, "collect MIFARE Classic nonces to file"}, // {"ice", CmdHF14AMfice, IfPm3Iso14443a, "collect MIFARE Classic nonces to file"},

View file

@ -380,6 +380,7 @@ const static vocabulary_t vocabulary[] = {
{ 0, "hf mf gchpwd" }, { 0, "hf mf gchpwd" },
{ 0, "hf mf gdmcfg" }, { 0, "hf mf gdmcfg" },
{ 0, "hf mf gdmsetcfg" }, { 0, "hf mf gdmsetcfg" },
{ 1, "hf mf gdmparsecfg" },
{ 0, "hf mf gdmsetblk" }, { 0, "hf mf gdmsetblk" },
{ 0, "hf mf ndefformat" }, { 0, "hf mf ndefformat" },
{ 0, "hf mf ndefread" }, { 0, "hf mf ndefread" },
@ -838,4 +839,4 @@ const static vocabulary_t vocabulary[] = {
} }
#endif #endif
#endif #endif

View file

@ -1367,7 +1367,7 @@
}, },
"hf 14a sniff": { "hf 14a sniff": {
"command": "hf 14a sniff", "command": "hf 14a sniff",
"description": "Collect data from the field and save into command buffer. Buffer accessible from command 'hf 14a list'", "description": "Collect data from the field and save into command buffer. Buffer accessible from command `hf 14a list`",
"notes": [ "notes": [
"hf 14a sniff -c -r" "hf 14a sniff -c -r"
], ],
@ -1375,9 +1375,10 @@
"options": [ "options": [
"-h, --help This help", "-h, --help This help",
"-c, --card triggered by first data from card", "-c, --card triggered by first data from card",
"-r, --reader triggered by first 7-bit request from reader (REQ,WUP,...)" "-r, --reader triggered by first 7-bit request from reader (REQ, WUP)",
"-i, --interactive Console will not be returned until sniff finishes or is aborted"
], ],
"usage": "hf 14a sniff [-hcr]" "usage": "hf 14a sniff [-hcri]"
}, },
"hf 14b apdu": { "hf 14b apdu": {
"command": "hf 14b apdu", "command": "hf 14b apdu",
@ -1641,9 +1642,10 @@
"-f, --file <fn> Specify a filename for dump file", "-f, --file <fn> Specify a filename for dump file",
"--bs <dec> block size (def 4)", "--bs <dec> block size (def 4)",
"--ns no save to file", "--ns no save to file",
"-v, --verbose verbose output" "-v, --verbose verbose output",
"-z, --dense dense dump output style"
], ],
"usage": "hf 15 dump [-h*2ov] [-u <hex>] [--ua] [-f <fn>] [--bs <dec>] [--ns]" "usage": "hf 15 dump [-h*2ovz] [-u <hex>] [--ua] [-f <fn>] [--bs <dec>] [--ns]"
}, },
"hf 15 eload": { "hf 15 eload": {
"command": "hf 15 eload", "command": "hf 15 eload",
@ -1861,10 +1863,9 @@
"-o, --opt set OPTION Flag (needed for TI)", "-o, --opt set OPTION Flag (needed for TI)",
"-f, --file <fn> Specify a filename for dump file", "-f, --file <fn> Specify a filename for dump file",
"-r, --retry <dec> number of retries (def 3)", "-r, --retry <dec> number of retries (def 3)",
"--bs <dec> block size (def 4)",
"-v, --verbose verbose output" "-v, --verbose verbose output"
], ],
"usage": "hf 15 restore [-h*2ov] [-u <hex>] [--ua] [-f <fn>] [-r <dec>] [--bs <dec>]" "usage": "hf 15 restore [-h*2ov] [-u <hex>] [--ua] [-f <fn>] [-r <dec>]"
}, },
"hf 15 samples": { "hf 15 samples": {
"command": "hf 15 samples", "command": "hf 15 samples",
@ -4573,9 +4574,24 @@
"offline": false, "offline": false,
"options": [ "options": [
"-h, --help This help", "-h, --help This help",
"-k, --key <hex> key 6 bytes" "-k, --key <hex> key 6 bytes (only for regular wakeup)",
"--gen1a use gen1a (40/43) magic wakeup",
"--gdm use gdm alt (20/23) magic wakeup"
], ],
"usage": "hf mf gdmcfg [-h] [-k <hex>]" "usage": "hf mf gdmcfg [-h] [-k <hex>] [--gen1a] [--gdm]"
},
"hf mf gdmparsecfg": {
"command": "hf mf gdmparsecfg",
"description": "Parse configuration data on a magic gen4 GDM card",
"notes": [
"hf mf gdmparsecfg -d 850000000000000000005A5A00000008"
],
"offline": true,
"options": [
"-h, --help This help",
"-d, --data <hex> bytes to write, 16 hex bytes"
],
"usage": "hf mf gdmparsecfg [-h] -d <hex>"
}, },
"hf mf gdmsetblk": { "hf mf gdmsetblk": {
"command": "hf mf gdmsetblk", "command": "hf mf gdmsetblk",
@ -4602,9 +4618,12 @@
"offline": false, "offline": false,
"options": [ "options": [
"-h, --help This help", "-h, --help This help",
"-d, --data <hex> bytes to write, 16 hex bytes" "-d, --data <hex> bytes to write, 16 hex bytes",
"-k, --key <hex> key 6 bytes (only for regular wakeup)",
"--gen1a use gen1a (40/43) magic wakeup",
"--gdm use gdm alt (20/23) magic wakeup"
], ],
"usage": "hf mf gdmsetcfg [-h] -d <hex>" "usage": "hf mf gdmsetcfg [-h] -d <hex> [-k <hex>] [--gen1a] [--gdm]"
}, },
"hf mf gen3blk": { "hf mf gen3blk": {
"command": "hf mf gen3blk", "command": "hf mf gen3blk",
@ -4803,7 +4822,7 @@
}, },
"hf mf help": { "hf mf help": {
"command": "hf mf help", "command": "hf mf help",
"description": "help This help list List MIFARE history hardnested Nested attack for hardened MIFARE Classic cards decrypt Decrypt Crypto1 data from sniff or trace acl Decode and print MIFARE Classic access rights bytes mad Checks and prints MAD value Value blocks view Display content from tag dump file --------------------------------------------------------------------------------------- hf mf list available offline: yes Alias of `trace list -t mf -c` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol", "description": "help This help list List MIFARE history hardnested Nested attack for hardened MIFARE Classic cards decrypt Decrypt Crypto1 data from sniff or trace acl Decode and print MIFARE Classic access rights bytes mad Checks and prints MAD value Value blocks view Display content from tag dump file gdmparsecfg Parse config block to card --------------------------------------------------------------------------------------- hf mf list available offline: yes Alias of `trace list -t mf -c` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol",
"notes": [ "notes": [
"hf mf list --frame -> show frame delay times", "hf mf list --frame -> show frame delay times",
"hf mf list -1 -> use trace buffer" "hf mf list -1 -> use trace buffer"
@ -12354,8 +12373,8 @@
} }
}, },
"metadata": { "metadata": {
"commands_extracted": 712, "commands_extracted": 713,
"extracted_by": "PM3Help2JSON v1.00", "extracted_by": "PM3Help2JSON v1.00",
"extracted_on": "2024-01-25T00:53:56" "extracted_on": "2024-01-26T08:13:47"
} }
} }

View file

@ -543,6 +543,7 @@ Check column "offline" for their availability.
|`hf mf gchpwd `|N |`Change card access password. Warning!` |`hf mf gchpwd `|N |`Change card access password. Warning!`
|`hf mf gdmcfg `|N |`Read config block from card` |`hf mf gdmcfg `|N |`Read config block from card`
|`hf mf gdmsetcfg `|N |`Write config block to card` |`hf mf gdmsetcfg `|N |`Write config block to card`
|`hf mf gdmparsecfg `|Y |`Parse config block to card`
|`hf mf gdmsetblk `|N |`Write block to card` |`hf mf gdmsetblk `|N |`Write block to card`
|`hf mf ndefformat `|N |`Format MIFARE Classic Tag as NFC Tag` |`hf mf ndefformat `|N |`Format MIFARE Classic Tag as NFC Tag`
|`hf mf ndefread `|N |`Read and print NDEF records from card` |`hf mf ndefread `|N |`Read and print NDEF records from card`

View file

@ -292,6 +292,32 @@ typedef struct {
uint8_t key[6]; uint8_t key[6];
} PACKED mf_readblock_t; } PACKED mf_readblock_t;
typedef enum {
MF_WAKE_NONE,
MF_WAKE_WUPA, // 52(7) + anticoll
MF_WAKE_REQA, // 26(7) + anticoll
MF_WAKE_GEN1A, // 40(7)/43
MF_WAKE_GEN1B, // 40(7)
MF_WAKE_GDM_ALT, // 20(7)/23
} PACKED MifareWakeupType;
typedef struct {
MifareWakeupType wakeup;
uint8_t auth_cmd;
uint8_t key[6];
uint8_t read_cmd;
uint8_t block_no;
} PACKED mf_readblock_ex_t;
typedef struct {
MifareWakeupType wakeup;
uint8_t auth_cmd;
uint8_t key[6];
uint8_t write_cmd;
uint8_t block_no;
uint8_t block_data[16];
} PACKED mf_writeblock_ex_t;
typedef struct { typedef struct {
uint8_t sectorcnt; uint8_t sectorcnt;
uint8_t keytype; uint8_t keytype;
@ -645,10 +671,12 @@ typedef struct {
#define CMD_HF_MIFARE_STATIC_ENC 0x0616 #define CMD_HF_MIFARE_STATIC_ENC 0x0616
#define CMD_HF_MIFARE_READBL 0x0620 #define CMD_HF_MIFARE_READBL 0x0620
#define CMD_HF_MIFARE_READBL_EX 0x0628
#define CMD_HF_MIFAREU_READBL 0x0720 #define CMD_HF_MIFAREU_READBL 0x0720
#define CMD_HF_MIFARE_READSC 0x0621 #define CMD_HF_MIFARE_READSC 0x0621
#define CMD_HF_MIFAREU_READCARD 0x0721 #define CMD_HF_MIFAREU_READCARD 0x0721
#define CMD_HF_MIFARE_WRITEBL 0x0622 #define CMD_HF_MIFARE_WRITEBL 0x0622
#define CMD_HF_MIFARE_WRITEBL_EX 0x0629
#define CMD_HF_MIFARE_VALUE 0x0627 #define CMD_HF_MIFARE_VALUE 0x0627
#define CMD_HF_MIFAREU_WRITEBL 0x0722 #define CMD_HF_MIFAREU_WRITEBL 0x0722
#define CMD_HF_MIFAREU_WRITEBL_COMPAT 0x0723 #define CMD_HF_MIFAREU_WRITEBL_COMPAT 0x0723
@ -713,8 +741,6 @@ typedef struct {
// Gen 4 GDM magic cards // Gen 4 GDM magic cards
#define CMD_HF_MIFARE_G4_GDM_RDBL 0x0870 #define CMD_HF_MIFARE_G4_GDM_RDBL 0x0870
#define CMD_HF_MIFARE_G4_GDM_WRBL 0x0871 #define CMD_HF_MIFARE_G4_GDM_WRBL 0x0871
#define CMD_HF_MIFARE_G4_GDM_CONFIG 0x0872
#define CMD_HF_MIFARE_G4_GDM_WRCFG 0x0873
// HID SAM // HID SAM
#define CMD_HF_SAM_PICOPASS 0x0900 #define CMD_HF_SAM_PICOPASS 0x0900