Merge pull request #1678 from mwalker33/Mifare-Value

Mifare Classic Value operations
This commit is contained in:
Iceman 2022-06-06 07:47:21 +02:00 committed by GitHub
commit f09eaa8c3a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 317 additions and 11 deletions

View file

@ -1470,6 +1470,10 @@ static void PacketReceived(PacketCommandNG *packet) {
MifareWriteBlock(packet->oldarg[0], packet->oldarg[1], packet->data.asBytes);
break;
}
case CMD_HF_MIFARE_VALUE: {
MifareValue(packet->oldarg[0], packet->oldarg[1], packet->data.asBytes);
break;
}
case CMD_HF_MIFAREU_WRITEBL: {
MifareUWriteBlock(packet->oldarg[0], packet->oldarg[1], packet->data.asBytes);
break;

View file

@ -446,6 +446,70 @@ void MifareWriteBlock(uint8_t arg0, uint8_t arg1, uint8_t *datain) {
set_tracing(false);
}
void MifareValue(uint8_t arg0, uint8_t arg1, uint8_t *datain) {
// params
uint8_t blockNo = arg0;
uint8_t keyType = arg1;
uint64_t ui64Key = 0;
uint8_t blockdata[16] = {0x00};
ui64Key = bytes_to_num(datain, 6);
memcpy(blockdata, datain + 10, 16);
// variables
uint8_t action = datain[9];
uint8_t isOK = 0;
uint8_t uid[10] = {0x00};
uint32_t cuid = 0;
struct Crypto1State mpcs = {0, 0};
struct Crypto1State *pcs;
pcs = &mpcs;
iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);
clear_trace();
set_tracing(true);
LED_A_ON();
LED_B_OFF();
LED_C_OFF();
while (true) {
if (!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) {
if (g_dbglevel >= DBG_ERROR) Dbprintf("Can't select card");
break;
};
if (mifare_classic_auth(pcs, cuid, blockNo, keyType, ui64Key, AUTH_FIRST)) {
if (g_dbglevel >= DBG_ERROR) Dbprintf("Auth error");
break;
};
if (mifare_classic_value(pcs, cuid, blockNo, blockdata, action)) {
if (g_dbglevel >= DBG_ERROR) Dbprintf("Write block error");
break;
};
if (mifare_classic_halt(pcs, cuid)) {
if (g_dbglevel >= DBG_ERROR) Dbprintf("Halt error");
break;
};
isOK = 1;
break;
}
crypto1_deinit(pcs);
if (g_dbglevel >= 2) DbpString("WRITE BLOCK FINISHED");
reply_mix(CMD_ACK, isOK, 0, 0, 0, 0);
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
LEDsoff();
set_tracing(false);
}
// Arg0 : Block to write to.
// Arg1 : 0 = use no authentication.
// 1 = use 0x1A authentication.

View file

@ -26,6 +26,7 @@ void MifareUC_Auth(uint8_t arg0, uint8_t *keybytes);
void MifareUReadCard(uint8_t arg0, uint16_t arg1, uint8_t arg2, uint8_t *datain);
void MifareReadSector(uint8_t arg0, uint8_t arg1, uint8_t *datain);
void MifareWriteBlock(uint8_t arg0, uint8_t arg1, uint8_t *datain);
void MifareValue(uint8_t arg0, uint8_t arg1, uint8_t *datain);
void MifareUWriteBlockCompat(uint8_t arg0, uint8_t arg1, uint8_t *datain);
void MifareUWriteBlock(uint8_t arg0, uint8_t arg1, uint8_t *datain);

View file

@ -456,6 +456,67 @@ 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;
uint32_t pos = 0;
uint8_t par[3] = {0x00, 0x00, 0x00}; // enough for 18 Bytes to send
uint8_t res = 0;
uint8_t d_block[18], d_block_enc[18];
uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE] = {0x00};
uint8_t receivedAnswerPar[MAX_MIFARE_PARITY_SIZE] = {0x00};
uint8_t command = MIFARE_CMD_INC;
if (action == 0x01)
command = MIFARE_CMD_DEC;
// Send increment or decrement command
len = mifare_sendcmd_short(pcs, 1, command, blockNo, receivedAnswer, receivedAnswerPar, NULL);
if ((len != 1) || (receivedAnswer[0] != 0x0A)) { // 0x0a - ACK
if (g_dbglevel >= DBG_ERROR) Dbprintf("Cmd Error: %02x", receivedAnswer[0]);
return 1;
}
memcpy(d_block, blockData, 4);
AddCrc14A(d_block, 4);
// crypto
for (pos = 0; pos < 6; pos++) {
d_block_enc[pos] = crypto1_byte(pcs, 0x00, 0) ^ d_block[pos];
par[pos >> 3] |= (((filter(pcs->odd) ^ oddparity8(d_block[pos])) & 0x01) << (7 - (pos & 0x0007)));
}
ReaderTransmitPar(d_block_enc, 6, par, NULL);
// Receive the response NO Response means OK ... i.e. NOT NACK
len = ReaderReceive(receivedAnswer, receivedAnswerPar);
if (len != 0) { // Something not right, len == 0 (no response is ok as its waiting for transfer
res = 0;
res |= (crypto1_bit(pcs, 0, 0) ^ BIT(receivedAnswer[0], 0)) << 0;
res |= (crypto1_bit(pcs, 0, 0) ^ BIT(receivedAnswer[0], 1)) << 1;
res |= (crypto1_bit(pcs, 0, 0) ^ BIT(receivedAnswer[0], 2)) << 2;
res |= (crypto1_bit(pcs, 0, 0) ^ BIT(receivedAnswer[0], 3)) << 3;
if ((len != 1) || (res != 0x0A)) {
if (g_dbglevel >= DBG_ERROR) Dbprintf("Cmd send data2 Error: %02x", res);
return 2;
}
} else {
// send trnasfer (commit the change)
len = mifare_sendcmd_short(pcs, 1, MIFARE_CMD_TRANSFER, blockNo, receivedAnswer, receivedAnswerPar, NULL);
if ((len != 1) || (receivedAnswer[0] != 0x0A)) { // 0x0a - ACK
if (g_dbglevel >= DBG_ERROR) Dbprintf("Cmd Error: %02x", receivedAnswer[0]);
return 1;
}
}
return 0;
}
int mifare_ultra_writeblock_compat(uint8_t blockNo, uint8_t *blockData) {
// variables
uint16_t len = 0;

View file

@ -76,6 +76,7 @@ int mifare_classic_readblock(struct Crypto1State *pcs, uint32_t uid, uint8_t blo
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_value(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t *blockData, uint8_t action);
// Ultralight/NTAG...
int mifare_ul_ev1_auth(uint8_t *keybytes, uint8_t *pack);

View file

@ -6293,30 +6293,204 @@ static int CmdHF14AGen4View(const char *Cmd) {
}
static int CmdHF14AMfValue(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf mf value",
"Decode of a MIFARE value block",
CLIParserInit(&ctx, "hf mf value1",
"MIFARE Classic value data commands\n",
"hf mf value --blk 16 -k FFFFFFFFFFFF --set 1000\n"
"hf mf value --blk 16 -k FFFFFFFFFFFF --inc 10\n"
"hf mf value --blk 16 -k FFFFFFFFFFFF --dec 10 -b\n"
"hf mf value --blk 16 -k FFFFFFFFFFFF --get -b\n"
"hf mf value -d 87D612007829EDFF87D6120011EE11EE\n"
);
void *argtable[] = {
arg_param_begin,
arg_str1("d", "data", "<hex>", "16 hex bytes"),
arg_str0("k", "key", "<hex>", "key, 6 hex bytes"),
arg_lit0("a", NULL, "input key type is key A (def)"),
arg_lit0("b", NULL, "input key type is key B"),
arg_u64_0(NULL, "inc", "<dec>", "Incremenet value by X (0 - 2147483647)"),
arg_u64_0(NULL, "dec", "<dec>", "Dcrement value by X (0 - 2147483647)"),
arg_u64_0(NULL, "set", "<dec>", "Set value to X (-2147483647 - 2147483647)"),
arg_lit0(NULL, "get", "Get value from block"),
arg_int0(NULL, "blk", "<dec>", "block number"),
arg_str0("d", "data", "<hex>", "block data to extract values from (16 hex bytes)"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, false);
uint8_t blockno = (uint8_t)arg_get_int_def(ctx, 8, 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, 1, key, &keylen);
/*
Value /Value Value BLK /BLK BLK /BLK
00000000 FFFFFFFF 00000000 10 EF 10 EF
BLK is used to referece where the backup come from, I suspect its just the current block for the actual value ?
increment and decrement are an unsigned value
set value is a signed value
We are getting signed and/or bigger values to allow a defult to be set meaning users did not supply that option.
*/
int64_t incval = (int64_t)arg_get_u64_def(ctx, 4, -1); // Inc by -1 is invalid, so not set.
int64_t decval = (int64_t)arg_get_u64_def(ctx, 5, -1); // Inc by -1 is invalid, so not set.
int64_t setval = (int64_t)arg_get_u64_def(ctx, 6, 0x7FFFFFFFFFFFFFFF ); // out of bounds (for int32) so not set
bool getval = arg_get_lit(ctx, 7);
uint8_t block[MFBLOCK_SIZE] = {0x00};
int dlen = 0;
uint8_t data[16] = {0};
CLIGetHexWithReturn(ctx, 1, data, &dlen);
CLIGetHexWithReturn(ctx, 9, data, &dlen);
CLIParserFree(ctx);
int32_t value = 0;
if (mfc_value(data, &value)) {
PrintAndLogEx(SUCCESS, "Dec... " _YELLOW_("%" PRIi32), value);
PrintAndLogEx(SUCCESS, "Hex... " _YELLOW_("0x%" PRIX32), value);
} else {
PrintAndLogEx(FAILED, "No value block detected");
uint8_t action = 3; // 0 Increment, 1 - Decrement, 2 - Set, 3 - Get, 4 - Decode from data
uint32_t value = 0;
uint8_t isok = true;
// Need to check we only have 1 of inc/dec/set and get the value from the selected option
int optionsprovided = 0;
if (incval != -1) {
optionsprovided++;
action = 0;
if ((incval <=0) || (incval > 2147483647)) {
PrintAndLogEx(WARNING, "increment value must be between 1 and 2147483647. Got %lli", incval);
return PM3_EINVARG;
} else
value = (uint32_t)incval;
}
if (decval != -1) {
optionsprovided++;
action = 1;
if ((decval <= 0) || (decval > 2147483647)) {
PrintAndLogEx(WARNING, "decrement value must be between 1 and 2147483647. Got %lli", decval);
return PM3_EINVARG;
} else
value = (uint32_t)decval;
}
if (setval != 0x7FFFFFFFFFFFFFFF) {
optionsprovided++;
action = 2;
if ((setval < -2147483647) || (setval > 2147483647)) {
PrintAndLogEx(WARNING, "set value must be between -2147483647 and 2147483647. Got %lli", setval);
return PM3_EINVARG;
} else
value = (uint32_t)setval;
}
if (dlen != 0) {
optionsprovided++;
action = 4;
if (dlen != 16) {
PrintAndLogEx(WARNING,"date length must be 16 hex bytes long, got %d",dlen);
return PM3_EINVARG;
}
}
if (optionsprovided > 1) { // more then one option provided
PrintAndLogEx(WARNING,"must have one and only one of --inc, --dec, --set or --data");
return PM3_EINVARG;
}
// dont want to write value data and break something
if ((blockno == 0) || (mfIsSectorTrailer (blockno))) {
PrintAndLogEx(WARNING, "invlaid block number, should be a data block ");
return PM3_EINVARG;
}
if (action < 3) {
if (action <= 1) { // increment/decrement value
memcpy (block, (uint8_t *)&value, 4);
uint8_t cmddata[26];
memcpy(cmddata, key, sizeof(key)); // Key == 6 data went to 10, so lets offset 9 for inc/dec
if (action == 0)
PrintAndLogEx(INFO, "value increment by : %d", value);
else
PrintAndLogEx(INFO, "value decrement by : %d", value);
PrintAndLogEx(INFO, "Writing block no %d, key %c - %s", blockno, (keytype == MF_KEY_B) ? 'B' : 'A', sprint_hex_inrow(key, sizeof(key)));
cmddata[9] = action; // 00 if increment, 01 if decrement.
memcpy(cmddata + 10, block, sizeof(block));
clearCommandBuffer();
SendCommandMIX(CMD_HF_MIFARE_VALUE, blockno, keytype, 0, cmddata, sizeof(cmddata));
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) {
PrintAndLogEx(FAILED, "Command execute timeout");
return PM3_ETIMEOUT;
}
isok = resp.oldarg[0] & 0xff;
} else { // set value
// To set a value block (or setup) we can use the normal mifare classic write block
// So build the command options can call CMD_HF_MIFARE_WRITEBL
PrintAndLogEx(INFO, "set value to : %d", (int32_t)value);
uint8_t writedata[26] = {0x00};
int32_t invertvalue = value ^ 0xFFFFFFFF;
memcpy(writedata, key, sizeof(key));
memcpy(writedata + 10, (uint8_t *)&value, 4);
memcpy(writedata + 14, (uint8_t *)&invertvalue, 4);
memcpy(writedata + 18, (uint8_t *)&value, 4);
writedata[22] = blockno;
writedata[23] = (blockno ^ 0xFF);
writedata[24] = blockno;
writedata[25] = (blockno ^ 0xFF);
clearCommandBuffer();
SendCommandMIX(CMD_HF_MIFARE_WRITEBL, blockno, keytype, 0, writedata, sizeof(writedata));
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) {
PrintAndLogEx(FAILED, "Command execute timeout");
return PM3_ETIMEOUT;
}
isok = resp.oldarg[0] & 0xff;
}
if (isok) {
PrintAndLogEx(SUCCESS, "Update ... : " _GREEN_("success"));
getval = true; // all ok so set flag to read current value
} else {
PrintAndLogEx(FAILED, "Update ... : " _RED_("failed"));
}
}
// If all went well getval will be true, so read the current value and display
if (getval) {
int32_t readvalue;
int res = -1;
if (action == 4) {
res = PM3_SUCCESS; // alread have data from command line
} else {
res = mfReadBlock(blockno, keytype, key, data);
}
if (res == PM3_SUCCESS) {
if (mfc_value(data, &readvalue)) {
PrintAndLogEx(SUCCESS, "Dec ...... : " _YELLOW_("%" PRIi32), readvalue);
PrintAndLogEx(SUCCESS, "Hex ...... : " _YELLOW_("0x%" PRIX32), readvalue);
} else {
PrintAndLogEx(FAILED, "No value block detected");
}
} else {
PrintAndLogEx(FAILED, "failed to read value block");
}
}
return PM3_SUCCESS;
}
@ -6346,7 +6520,7 @@ static command_t CommandTable[] = {
{"rdsc", CmdHF14AMfRdSc, IfPm3Iso14443a, "Read MIFARE Classic sector"},
{"restore", CmdHF14AMfRestore, IfPm3Iso14443a, "Restore MIFARE Classic binary file to BLANK tag"},
{"setmod", CmdHf14AMfSetMod, IfPm3Iso14443a, "Set MIFARE Classic EV1 load modulation strength"},
{"value", CmdHF14AMfValue, AlwaysAvailable, "Decode a value block"},
{"value", CmdHF14AMfValue, AlwaysAvailable, "Value blocks"},
{"view", CmdHF14AMfView, AlwaysAvailable, "Display content from tag dump file"},
{"wipe", CmdHF14AMfWipe, IfPm3Iso14443a, "Wipe card to zeros and default keys/acc"},
{"wrbl", CmdHF14AMfWrBl, IfPm3Iso14443a, "Write MIFARE Classic block"},

View file

@ -614,6 +614,7 @@ typedef struct {
#define CMD_HF_MIFARE_READSC 0x0621
#define CMD_HF_MIFAREU_READCARD 0x0721
#define CMD_HF_MIFARE_WRITEBL 0x0622
#define CMD_HF_MIFARE_VALUE 0x0627
#define CMD_HF_MIFAREU_WRITEBL 0x0722
#define CMD_HF_MIFAREU_WRITEBL_COMPAT 0x0723