proxmark3/client/src/cmdhfmf.c

7502 lines
266 KiB
C

//-----------------------------------------------------------------------------
// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// See LICENSE.txt for the text of the license.
//-----------------------------------------------------------------------------
// High frequency MIFARE commands
//-----------------------------------------------------------------------------
#include "cmdhfmf.h"
#include <ctype.h>
#include "cmdparser.h" // command_t
#include "commonutil.h" // ARRAYLEN
#include "comms.h" // clearCommandBuffer
#include "fileutils.h"
#include "cmdtrace.h"
#include "mifare/mifaredefault.h" // mifare default key array
#include "cliparser.h" // argtable
#include "hardnested_bf_core.h" // SetSIMDInstr
#include "mifare/mad.h"
#include "nfc/ndef.h"
#include "protocols.h"
#include "util_posix.h" // msclock
#include "cmdhfmfhard.h"
#include "crapto1/crapto1.h" // prng_successor
#include "cmdhf14a.h" // exchange APDU
#include "crypto/libpcrypto.h"
#include "wiegand_formats.h"
#include "wiegand_formatutils.h"
#define MIFARE_4K_MAXBLOCK 256
#define MIFARE_2K_MAXBLOCK 128
#define MIFARE_1K_MAXBLOCK 64
#define MIFARE_MINI_MAXBLOCK 20
#define MIFARE_MINI_MAXSECTOR 5
#define MIFARE_1K_MAXSECTOR 16
#define MIFARE_2K_MAXSECTOR 32
#define MIFARE_4K_MAXSECTOR 40
static int CmdHelp(const char *Cmd);
/*
static int usage_hf14_keybrute(void) {
PrintAndLogEx(NORMAL, "J_Run's 2nd phase of multiple sector nested authentication key recovery");
PrintAndLogEx(NORMAL, "You have a known 4 last bytes of a key recovered with mf_nonce_brute tool.");
PrintAndLogEx(NORMAL, "First 2 bytes of key will be bruteforced");
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(NORMAL, " ---[ This attack is obsolete, try hardnested instead ]---");
PrintAndLogEx(NORMAL, "Options:");
PrintAndLogEx(NORMAL, " h this help");
PrintAndLogEx(NORMAL, " <block number> target block number");
PrintAndLogEx(NORMAL, " <A|B> target key type");
PrintAndLogEx(NORMAL, " <key> candidate key from mf_nonce_brute tool");
PrintAndLogEx(NORMAL, "Examples:");
PrintAndLogEx(NORMAL, _YELLOW_(" hf mf keybrute --blk 1 -k 000011223344"));
return 0;
}
*/
int mfc_ev1_print_signature(uint8_t *uid, uint8_t uidlen, uint8_t *signature, int signature_len) {
// ref: MIFARE Classic EV1 Originality Signature Validation
#define PUBLIC_MFCEV1_ECDA_KEYLEN 33
const ecdsa_publickey_t nxp_mfc_public_keys[] = {
{"NXP Mifare Classic MFC1C14_x", "044F6D3F294DEA5737F0F46FFEE88A356EED95695DD7E0C27A591E6F6F65962BAF"},
};
uint8_t i;
bool is_valid = false;
for (i = 0; i < ARRAYLEN(nxp_mfc_public_keys); i++) {
int dl = 0;
uint8_t key[PUBLIC_MFCEV1_ECDA_KEYLEN];
param_gethex_to_eol(nxp_mfc_public_keys[i].value, 0, key, PUBLIC_MFCEV1_ECDA_KEYLEN, &dl);
int res = ecdsa_signature_r_s_verify(MBEDTLS_ECP_DP_SECP128R1, key, uid, uidlen, signature, signature_len, false);
is_valid = (res == 0);
if (is_valid)
break;
}
PrintAndLogEx(INFO, "");
PrintAndLogEx(INFO, "--- " _CYAN_("Tag Signature"));
if (is_valid == false || i == ARRAYLEN(nxp_mfc_public_keys)) {
PrintAndLogEx(INFO, " Elliptic curve parameters: NID_secp128r1");
PrintAndLogEx(INFO, " TAG IC Signature: %s", sprint_hex_inrow(signature, 32));
PrintAndLogEx(SUCCESS, " Signature verification: " _RED_("failed"));
return PM3_ESOFT;
}
PrintAndLogEx(INFO, " IC signature public key name: %s", nxp_mfc_public_keys[i].desc);
PrintAndLogEx(INFO, "IC signature public key value: %s", nxp_mfc_public_keys[i].value);
PrintAndLogEx(INFO, " Elliptic curve parameters: NID_secp128r1");
PrintAndLogEx(INFO, " TAG IC Signature: %s", sprint_hex_inrow(signature, 32));
PrintAndLogEx(SUCCESS, " Signature verification: " _GREEN_("successful"));
return PM3_SUCCESS;
}
static int GetHFMF14AUID(uint8_t *uid, int *uidlen) {
clearCommandBuffer();
SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0);
PacketResponseNG resp;
if (!WaitForResponseTimeout(CMD_ACK, &resp, 2500)) {
PrintAndLogEx(WARNING, "iso14443a card select failed");
DropField();
return PM3_ERFTRANS;
}
iso14a_card_select_t card;
memcpy(&card, (iso14a_card_select_t *)resp.data.asBytes, sizeof(iso14a_card_select_t));
memcpy(uid, card.uid, card.uidlen * sizeof(uint8_t));
*uidlen = card.uidlen;
return PM3_SUCCESS;
}
static char *GenerateFilename(const char *prefix, const char *suffix) {
if (! IfPm3Iso14443a()) {
return NULL;
}
uint8_t uid[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
int uidlen = 0;
char *fptr = calloc(sizeof(char) * (strlen(prefix) + strlen(suffix)) + sizeof(uid) * 2 + 1, sizeof(uint8_t));
int res = GetHFMF14AUID(uid, &uidlen);
if (res != PM3_SUCCESS || !uidlen) {
PrintAndLogEx(WARNING, "No tag found.");
free(fptr);
return NULL;
}
strcpy(fptr, prefix);
FillFileNameByUID(fptr, uid, suffix, uidlen);
return fptr;
}
static int32_t initSectorTable(sector_t **src, int32_t items) {
(*src) = calloc(items, sizeof(sector_t));
if (*src == NULL)
return -1;
// empty e_sector
for (int i = 0; i < items; ++i) {
for (int j = 0; j < 2; ++j) {
(*src)[i].Key[j] = 0xffffffffffff;
(*src)[i].foundKey[j] = false;
}
}
return items;
}
static void decode_print_st(uint16_t blockno, uint8_t *data) {
if (mfIsSectorTrailer(blockno)) {
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "----------------------- " _CYAN_("Sector trailer decoder") " -----------------------");
PrintAndLogEx(INFO, "key A........ " _GREEN_("%s"), sprint_hex_inrow(data, 6));
PrintAndLogEx(INFO, "acr.......... " _GREEN_("%s"), sprint_hex_inrow(data + 6, 3));
PrintAndLogEx(INFO, "user / gpb... " _GREEN_("%02x"), data[9]);
PrintAndLogEx(INFO, "key B........ " _GREEN_("%s"), sprint_hex_inrow(data + 10, 6));
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, " # | Access rights");
PrintAndLogEx(INFO, "----+-----------------------------------------------------------------");
if (mfValidateAccessConditions(&data[6]) == false) {
PrintAndLogEx(WARNING, _RED_("Invalid Access Conditions"));
}
int bln = mfFirstBlockOfSector(mfSectorNum(blockno));
int blinc = (mfNumBlocksPerSector(mfSectorNum(blockno)) > 4) ? 5 : 1;
for (int i = 0; i < 4; i++) {
PrintAndLogEx(INFO, "%3d%c| " _YELLOW_("%s"), bln, ((blinc > 1) && (i < 3) ? '+' : ' '), mfGetAccessConditionsDesc(i, &data[6]));
bln += blinc;
}
PrintAndLogEx(INFO, "----------------------------------------------------------------------");
PrintAndLogEx(NORMAL, "");
}
}
static uint8_t NumOfSectors(char card) {
switch (card) {
case '0' :
return MIFARE_MINI_MAXSECTOR;
case '1' :
return MIFARE_1K_MAXSECTOR;
case '2' :
return MIFARE_2K_MAXSECTOR;
case '4' :
return MIFARE_4K_MAXSECTOR;
default :
return 0;
}
}
static char GetFormatFromSector(uint8_t sectors) {
switch (sectors) {
case MIFARE_MINI_MAXSECTOR:
return '0';
case MIFARE_1K_MAXSECTOR:
return '1';
case MIFARE_2K_MAXSECTOR:
return '2';
case MIFARE_4K_MAXSECTOR:
return '4';
default :
return ' ';
}
}
static bool mfc_value(const uint8_t *d, int32_t *val) {
// values
int32_t a = (int32_t)MemLeToUint4byte(d);
uint32_t a_inv = MemLeToUint4byte(d + 4);
uint32_t b = MemLeToUint4byte(d + 8);
int val_checks = (
(a == b) && (a == ~a_inv) &&
(d[12] == (~d[13] & 0xFF)) &&
(d[14] == (~d[15] & 0xFF))
);
if (val) {
*val = a;
}
return val_checks;
}
static void mf_print_block(uint8_t blockno, uint8_t *d, bool verbose) {
if (blockno == 0) {
PrintAndLogEx(INFO, "%3d | " _RED_("%s"), blockno, sprint_hex_ascii(d, MFBLOCK_SIZE));
} else if (mfIsSectorTrailer(blockno)) {
PrintAndLogEx(INFO, "%3d | " _YELLOW_("%s"), blockno, sprint_hex_ascii(d, MFBLOCK_SIZE));
} else {
int32_t value = 0;
if (verbose && mfc_value(d, &value)) {
PrintAndLogEx(INFO, "%3d | " _CYAN_("%s") " %"PRIi32, blockno, sprint_hex_ascii(d, MFBLOCK_SIZE), value);
} else {
PrintAndLogEx(INFO, "%3d | %s ", blockno, sprint_hex_ascii(d, MFBLOCK_SIZE));
}
}
}
static void mf_print_blocks(uint16_t n, uint8_t *d, bool verbose) {
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "----+-------------------------------------------------+-----------------");
PrintAndLogEx(INFO, "blk | data | ascii");
PrintAndLogEx(INFO, "----+-------------------------------------------------+-----------------");
for (uint16_t i = 0; i < n; i++) {
mf_print_block(i, d + (i * MFBLOCK_SIZE), verbose);
}
PrintAndLogEx(INFO, "----+-------------------------------------------------+-----------------");
PrintAndLogEx(HINT, _CYAN_("cyan") " = value block with decoded value");
// MAD detection
if (HasMADKey(d)) {
PrintAndLogEx(HINT, "MAD key detected. Try " _YELLOW_("`hf mf mad`") " for more details");
}
PrintAndLogEx(NORMAL, "");
}
static int mf_print_keys(uint16_t n, uint8_t *d) {
uint8_t sectors = 0;
switch (n) {
case MIFARE_MINI_MAXBLOCK:
sectors = MIFARE_MINI_MAXSECTOR;
break;
case MIFARE_2K_MAXBLOCK:
sectors = MIFARE_2K_MAXSECTOR;
break;
case MIFARE_4K_MAXBLOCK:
sectors = MIFARE_4K_MAXSECTOR;
break;
case MIFARE_1K_MAXBLOCK:
default:
sectors = MIFARE_1K_MAXSECTOR;
break;
}
sector_t *e_sector = calloc(sectors, sizeof(sector_t));
if (e_sector == NULL) {
return PM3_EMALLOC;
}
for (uint16_t i = 0; i < n; i++) {
if (mfIsSectorTrailer(i)) {
e_sector[mfSectorNum(i)].foundKey[0] = 1;
e_sector[mfSectorNum(i)].Key[0] = bytes_to_num(d + (i * MFBLOCK_SIZE), 6);
e_sector[mfSectorNum(i)].foundKey[1] = 1;
e_sector[mfSectorNum(i)].Key[1] = bytes_to_num(d + (i * MFBLOCK_SIZE) + 10, 6);
}
}
printKeyTable(sectors, e_sector);
free(e_sector);
return PM3_SUCCESS;
}
/*
static void mf_print_values(uint16_t n, uint8_t *d) {
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "Looking for value blocks...");
PrintAndLogEx(NORMAL, "");
uint8_t cnt = 0;
int32_t value = 0;
for (uint16_t i = 0; i < n; i++) {
if (mfc_value(d + (i * MFBLOCK_SIZE), &value)) {
PrintAndLogEx(INFO, "%03d | " _YELLOW_("%" PRIi32) " " _YELLOW_("0x%" PRIX32), i, value, value);
++cnt;
}
}
if (cnt) {
PrintAndLogEx(INFO, "Found %u value blocks in file", cnt);
PrintAndLogEx(NORMAL, "");
}
}
*/
static void mf_print_sector_hdr(uint8_t sector) {
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, " # | sector " _GREEN_("%02d") " / " _GREEN_("0x%02X") " | ascii", sector, sector);
PrintAndLogEx(INFO, "----+-------------------------------------------------+-----------------");
}
static bool mf_write_block(const uint8_t *key, uint8_t keytype, uint8_t blockno, uint8_t *block) {
uint8_t data[26];
memcpy(data, key, MFKEY_SIZE);
memcpy(data + 10, block, MFBLOCK_SIZE);
clearCommandBuffer();
SendCommandMIX(CMD_HF_MIFARE_WRITEBL, blockno, keytype, 0, data, sizeof(data));
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) {
PrintAndLogEx(FAILED, "Command execute timeout");
return PM3_ETIMEOUT;
}
return (resp.oldarg[0] & 0xff);
}
static int CmdHF14AMfAcl(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf mf acl",
"Print decoded MIFARE access rights (ACL), \n"
" A = key A\n"
" B = key B\n"
" AB = both key A and B\n"
" ACCESS = access bytes inside sector trailer block\n"
" Increment, decrement, transfer, restore is for value blocks",
"hf mf acl\n"
"hf mf acl -d FF0780\n");
void *argtable[] = {
arg_param_begin,
arg_str1("d", "data", "<hex>", "ACL bytes specified as 3 hex bytes"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
int acllen = 0;
uint8_t acl[3] = {0};
CLIGetHexWithReturn(ctx, 1, acl, &acllen);
CLIParserFree(ctx);
PrintAndLogEx(NORMAL, "");
// look up common default ACL bytes and print a fingerprint line about it.
if (memcmp(acl, "\xFF\x07\x80", 3) == 0) {
PrintAndLogEx(INFO, "ACL... " _GREEN_("%s") " (transport configuration)", sprint_hex(acl, sizeof(acl)));
}
if (mfValidateAccessConditions(acl) == false) {
PrintAndLogEx(ERR, _RED_("Invalid Access Conditions, NEVER write these on a card!"));
}
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, " # | Access rights");
PrintAndLogEx(INFO, "----+-----------------------------------------------------------------");
for (int i = 0; i < 4; i++) {
PrintAndLogEx(INFO, "%3d | " _YELLOW_("%s"), i, mfGetAccessConditionsDesc(i, acl));
}
PrintAndLogEx(NORMAL, "");
return PM3_SUCCESS;
}
static int CmdHF14AMfDarkside(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf mf darkside",
"Darkside attack",
"hf mf darkside\n"
"hf mf darkside --blk 16\n"
"hf mf darkside --blk 16 -b\n");
void *argtable[] = {
arg_param_begin,
arg_int0(NULL, "blk", "<dec> ", "Target block"),
arg_lit0("b", NULL, "Target key B instead of default key A"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
uint8_t blockno = arg_get_u32_def(ctx, 1, 0);
uint8_t key_type = MIFARE_AUTH_KEYA;
if (arg_get_lit(ctx, 2)) {
PrintAndLogEx(INFO, "Targeting key B");
key_type = MIFARE_AUTH_KEYB;
}
CLIParserFree(ctx);
uint64_t key = 0;
uint64_t t1 = msclock();
int isOK = mfDarkside(blockno, key_type, &key);
t1 = msclock() - t1;
switch (isOK) {
case PM3_EOPABORTED:
PrintAndLogEx(WARNING, "button pressed or aborted via keyboard. aborted");
return PM3_EOPABORTED;
case -2 :
PrintAndLogEx(FAILED, "card is not vulnerable to Darkside attack (doesn't send NACK on authentication requests)");
return PM3_ESOFT;
case -3 :
PrintAndLogEx(FAILED, "card is not vulnerable to Darkside attack (its random number generator is not predictable)");
return PM3_ESOFT;
case -4 :
PrintAndLogEx(FAILED, "card is not vulnerable to Darkside attack (its random number generator seems to be based on the wellknown");
PrintAndLogEx(FAILED, "generating polynomial with 16 effective bits only, but shows unexpected behaviour");
return PM3_ESOFT;
default :
PrintAndLogEx(SUCCESS, "found valid key: "_GREEN_("%012" PRIx64), key);
break;
}
PrintAndLogEx(SUCCESS, "time in darkside " _YELLOW_("%.0f") " seconds\n", (float)t1 / 1000.0);
return PM3_SUCCESS;
}
static int CmdHF14AMfWrBl(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf mf wrbl",
"Write MIFARE Classic block with 16 hex bytes of data\n"
" \n"
"Sector 0 / Block 0 - Manufacturer block\n"
"When writing to block 0 you must use a VALID block 0 data (UID, BCC, SAK, ATQA)\n"
"Writing an invalid block 0 means rendering your Magic GEN2 card undetectable. \n"
"Look in the magic_cards_notes.md file for help to resolve it.",
"hf mf wrbl --blk 1 -k FFFFFFFFFFFF -d 000102030405060708090a0b0c0d0e0f"
);
void *argtable[] = {
arg_param_begin,
arg_int1(NULL, "blk", "<dec>", "block number"),
arg_lit0("a", NULL, "input key type is key A (def)"),
arg_lit0("b", NULL, "input key type is key B"),
arg_lit0(NULL, "force", "enforce block0 writes"),
arg_str0("k", "key", "<hex>", "key, 6 hex bytes"),
arg_str0("d", "data", "<hex>", "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;;
}
bool force = arg_get_lit(ctx, 4);
int keylen = 0;
uint8_t key[6] = {0};
CLIGetHexWithReturn(ctx, 5, key, &keylen);
uint8_t block[MFBLOCK_SIZE] = {0x00};
int blen = 0;
CLIGetHexWithReturn(ctx, 6, block, &blen);
CLIParserFree(ctx);
if (blen != MFBLOCK_SIZE) {
PrintAndLogEx(WARNING, "block data must include 16 HEX bytes. Got %i", blen);
return PM3_EINVARG;
}
if (b > 255) {
return PM3_EINVARG;
}
if (b == 0 && force == false) {
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "Targeting Sector 0 / Block 0 - Manufacturer block");
PrintAndLogEx(INFO, "Read the helptext for details before writing to this block");
PrintAndLogEx(INFO, "You must use param `" _YELLOW_("--force") "` to write to this block");
PrintAndLogEx(NORMAL, "");
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)));
uint8_t data[26];
memcpy(data, key, sizeof(key));
memcpy(data + 10, block, sizeof(block));
clearCommandBuffer();
SendCommandMIX(CMD_HF_MIFARE_WRITEBL, blockno, keytype, 0, data, sizeof(data));
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) {
PrintAndLogEx(FAILED, "Command execute timeout");
return PM3_ETIMEOUT;
}
uint8_t isok = resp.oldarg[0] & 0xff;
if (isok) {
PrintAndLogEx(SUCCESS, "Write ( " _GREEN_("ok") " )");
PrintAndLogEx(HINT, "try `" _YELLOW_("hf mf rdbl") "` to verify");
} else {
PrintAndLogEx(FAILED, "Write ( " _RED_("fail") " )");
// suggest the opposite keytype than what was used.
PrintAndLogEx(HINT, "Maybe access rights? Try specify keytype `" _YELLOW_("hf mf wrbl -%c ...") "` instead", (keytype == MF_KEY_A) ? 'b' : 'a');
}
return PM3_SUCCESS;
}
static int CmdHF14AMfRdBl(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf mf rdbl",
"Read MIFARE Classic block",
"hf mf rdbl --blk 0 -k FFFFFFFFFFFF\n"
"hf mf rdbl --blk 3 -v -> get block 3, decode sector trailer\n"
);
void *argtable[] = {
arg_param_begin,
arg_int1(NULL, "blk", "<dec>", "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", "<hex>", "key, 6 hex bytes"),
arg_lit0("v", "verbose", "verbose output"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, false);
int b = arg_get_int_def(ctx, 1, 0);
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);
bool verbose = arg_get_lit(ctx, 5);
CLIParserFree(ctx);
if (b > 255) {
return PM3_EINVARG;
}
uint8_t blockno = (uint8_t)b;
uint8_t data[16] = {0};
int res = mfReadBlock(blockno, keytype, key, data);
if (res == PM3_SUCCESS) {
uint8_t sector = mfSectorNum(blockno);
mf_print_sector_hdr(sector);
mf_print_block(blockno, data, verbose);
if (verbose) {
decode_print_st(blockno, data);
}
}
PrintAndLogEx(NORMAL, "");
return res;
}
static int CmdHF14AMfRdSc(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf mf rdsc",
"Read MIFARE Classic sector",
"hf mf rdsc -s 0 -k FFFFFFFFFFFF\n"
);
void *argtable[] = {
arg_param_begin,
arg_lit0("a", NULL, "input key specified is A key (def)"),
arg_lit0("b", NULL, "input key specified is B key"),
arg_str0("k", "key", "<hex>", "key specified as 6 hex bytes"),
arg_int1("s", "sec", "<dec>", "sector number"),
arg_lit0("v", "verbose", "verbose output"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, false);
uint8_t keytype = MF_KEY_A;
if (arg_get_lit(ctx, 1) && arg_get_lit(ctx, 2)) {
CLIParserFree(ctx);
PrintAndLogEx(WARNING, "Input key type must be A or B");
return PM3_EINVARG;
} else if (arg_get_lit(ctx, 2)) {
keytype = MF_KEY_B;
}
int keylen = 0;
uint8_t key[6] = {0};
CLIGetHexWithReturn(ctx, 3, key, &keylen);
int s = arg_get_int_def(ctx, 4, 0);
bool verbose = arg_get_lit(ctx, 5);
CLIParserFree(ctx);
if (s > MIFARE_4K_MAXSECTOR) {
PrintAndLogEx(WARNING, "Sector number must be less then 40");
return PM3_EINVARG;
}
uint8_t sector = (uint8_t)s;
uint8_t sc_size = mfNumBlocksPerSector(sector) * MFBLOCK_SIZE;
uint8_t *data = calloc(sc_size, sizeof(uint8_t));
if (data == NULL) {
PrintAndLogEx(ERR, "failed to allocate memory");
return PM3_EMALLOC;
}
int res = mfReadSector(sector, keytype, key, data);
if (res == PM3_SUCCESS) {
uint8_t blocks = mfNumBlocksPerSector(sector);
uint8_t start = mfFirstBlockOfSector(sector);
mf_print_sector_hdr(sector);
for (int i = 0; i < blocks; i++) {
mf_print_block(start + i, data + (i * MFBLOCK_SIZE), verbose);
}
if (verbose) {
decode_print_st(start + blocks - 1, data + ((blocks - 1) * MFBLOCK_SIZE));
}
}
free(data);
PrintAndLogEx(NORMAL, "");
return PM3_SUCCESS;
}
static int FastDumpWithEcFill(uint8_t numsectors) {
PacketResponseNG resp;
mfc_eload_t payload;
payload.sectorcnt = numsectors;
payload.keytype = MF_KEY_A;
// ecfill key A
clearCommandBuffer();
SendCommandNG(CMD_HF_MIFARE_EML_LOAD, (uint8_t *)&payload, sizeof(payload));
bool res = WaitForResponseTimeout(CMD_HF_MIFARE_EML_LOAD, &resp, 2500);
if (res == false) {
PrintAndLogEx(WARNING, "Command execute timeout");
return PM3_ETIMEOUT;
}
if (resp.status != PM3_SUCCESS) {
PrintAndLogEx(INFO, "fast dump reported back failure w KEY A, swapping to KEY B");
// ecfill key B
payload.keytype = MF_KEY_B;
clearCommandBuffer();
SendCommandNG(CMD_HF_MIFARE_EML_LOAD, (uint8_t *)&payload, sizeof(payload));
res = WaitForResponseTimeout(CMD_HF_MIFARE_EML_LOAD, &resp, 2500);
if (res == false) {
PrintAndLogEx(WARNING, "Command execute timeout");
return PM3_ETIMEOUT;
}
if (resp.status != PM3_SUCCESS) {
PrintAndLogEx(INFO, "fast dump reported back failure w KEY B");
PrintAndLogEx(INFO, "Dump file is " _RED_("PARTIAL") " complete");
}
}
return PM3_SUCCESS;
}
static int CmdHF14AMfDump(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf mf dump",
"Dump MIFARE Classic tag to binary file\n"
"If no <name> given, UID will be used as filename",
"hf mf dump --mini --> MIFARE Mini\n"
"hf mf dump --1k --> MIFARE Classic 1k\n"
"hf mf dump --2k --> MIFARE 2k\n"
"hf mf dump --4k --> MIFARE 4k\n"
"hf mf dump --keys hf-mf-066C8B78-key.bin --> MIFARE 1k with keys from specified file\n");
void *argtable[] = {
arg_param_begin,
arg_str0("f", "file", "<fn>", "filename of dump"),
arg_str0("k", "keys", "<fn>", "filename of keys"),
arg_lit0(NULL, "mini", "MIFARE Classic Mini / S20"),
arg_lit0(NULL, "1k", "MIFARE Classic 1k / S50 (def)"),
arg_lit0(NULL, "2k", "MIFARE Classic/Plus 2k"),
arg_lit0(NULL, "4k", "MIFARE Classic 4k / S70"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
int datafnlen = 0;
char dataFilename[FILE_PATH_SIZE] = {0};
CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)dataFilename, FILE_PATH_SIZE, &datafnlen);
int keyfnlen = 0;
char keyFilename[FILE_PATH_SIZE] = {0};
CLIParamStrToBuf(arg_get_str(ctx, 2), (uint8_t *)keyFilename, FILE_PATH_SIZE, &keyfnlen);
bool m0 = arg_get_lit(ctx, 3);
bool m1 = arg_get_lit(ctx, 4);
bool m2 = arg_get_lit(ctx, 5);
bool m4 = arg_get_lit(ctx, 6);
CLIParserFree(ctx);
uint64_t t1 = msclock();
// validations
if ((m0 + m1 + m2 + m4) > 1) {
PrintAndLogEx(WARNING, "Only specify one MIFARE Type");
return PM3_EINVARG;
} else if ((m0 + m1 + m2 + m4) == 0) {
m1 = true;
}
uint8_t numSectors = MIFARE_1K_MAXSECTOR;
if (m0) {
numSectors = MIFARE_MINI_MAXSECTOR;
} else if (m1) {
numSectors = MIFARE_1K_MAXSECTOR;
} else if (m2) {
numSectors = MIFARE_2K_MAXSECTOR;
} else if (m4) {
numSectors = MIFARE_4K_MAXSECTOR;
} else {
PrintAndLogEx(WARNING, "Please specify a MIFARE Type");
return PM3_EINVARG;
}
uint8_t sectorNo, blockNo;
uint8_t keyA[40][6];
uint8_t keyB[40][6];
uint8_t rights[40][4];
uint8_t carddata[256][16];
FILE *f;
PacketResponseNG resp;
char *fptr;
// Select card to get UID/UIDLEN/ATQA/SAK information
clearCommandBuffer();
SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0);
if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) {
PrintAndLogEx(WARNING, "iso14443a card select timeout");
return PM3_ETIMEOUT;
}
uint64_t select_status = resp.oldarg[0];
if (select_status == 0) {
PrintAndLogEx(WARNING, "iso14443a card select failed");
return PM3_SUCCESS;
}
// store card info
iso14a_card_select_t card;
memcpy(&card, (iso14a_card_select_t *)resp.data.asBytes, sizeof(iso14a_card_select_t));
if (keyFilename[0] == 0x00) {
fptr = GenerateFilename("hf-mf-", "-key.bin");
if (fptr == NULL)
return PM3_ESOFT;
strcpy(keyFilename, fptr);
free(fptr);
}
if ((f = fopen(keyFilename, "rb")) == NULL) {
PrintAndLogEx(WARNING, "Could not find file " _YELLOW_("%s"), keyFilename);
return PM3_EFILE;
}
PrintAndLogEx(INFO, "Using `" _YELLOW_("%s") "`", keyFilename);
// Read keys A from file
size_t bytes_read;
for (sectorNo = 0; sectorNo < numSectors; sectorNo++) {
bytes_read = fread(keyA[sectorNo], 1, MFKEY_SIZE, f);
if (bytes_read != MFKEY_SIZE) {
PrintAndLogEx(ERR, "File reading error.");
fclose(f);
return PM3_EFILE;
}
}
// Read keys B from file
for (sectorNo = 0; sectorNo < numSectors; sectorNo++) {
bytes_read = fread(keyB[sectorNo], 1, MFKEY_SIZE, f);
if (bytes_read != MFKEY_SIZE) {
PrintAndLogEx(ERR, "File reading error.");
fclose(f);
return PM3_EFILE;
}
}
fclose(f);
PrintAndLogEx(INFO, "Reading sector access bits...");
PrintAndLogEx(INFO, "." NOLF);
uint8_t tries;
mf_readblock_t payload;
uint8_t current_key;
for (sectorNo = 0; sectorNo < numSectors; sectorNo++) {
current_key = MF_KEY_A;
for (tries = 0; tries < MIFARE_SECTOR_RETRY; tries++) {
PrintAndLogEx(NORMAL, "." NOLF);
fflush(stdout);
payload.blockno = mfFirstBlockOfSector(sectorNo) + mfNumBlocksPerSector(sectorNo) - 1;
payload.keytype = current_key;
memcpy(payload.key, current_key == MF_KEY_A ? keyA[sectorNo] : keyB[sectorNo], sizeof(payload.key));
clearCommandBuffer();
SendCommandNG(CMD_HF_MIFARE_READBL, (uint8_t *)&payload, sizeof(mf_readblock_t));
if (WaitForResponseTimeout(CMD_HF_MIFARE_READBL, &resp, 1500)) {
uint8_t *data = resp.data.asBytes;
if (resp.status == PM3_SUCCESS) {
rights[sectorNo][0] = ((data[7] & 0x10) >> 2) | ((data[8] & 0x1) << 1) | ((data[8] & 0x10) >> 4); // C1C2C3 for data area 0
rights[sectorNo][1] = ((data[7] & 0x20) >> 3) | ((data[8] & 0x2) << 0) | ((data[8] & 0x20) >> 5); // C1C2C3 for data area 1
rights[sectorNo][2] = ((data[7] & 0x40) >> 4) | ((data[8] & 0x4) >> 1) | ((data[8] & 0x40) >> 6); // C1C2C3 for data area 2
rights[sectorNo][3] = ((data[7] & 0x80) >> 5) | ((data[8] & 0x8) >> 2) | ((data[8] & 0x80) >> 7); // C1C2C3 for sector trailer
break;
} else if (tries == (MIFARE_SECTOR_RETRY / 2)) { // after half unsuccessful tries, give key B a go
PrintAndLogEx(WARNING, "\ntrying with key B instead...");
current_key = MF_KEY_B;
PrintAndLogEx(INFO, "." NOLF);
} else if (tries == (MIFARE_SECTOR_RETRY - 1)) { // on last try set defaults
PrintAndLogEx(FAILED, "\ncould not get access rights for sector %2d. Trying with defaults...", sectorNo);
rights[sectorNo][0] = rights[sectorNo][1] = rights[sectorNo][2] = 0x00;
rights[sectorNo][3] = 0x01;
}
} else {
PrintAndLogEx(FAILED, "\ncommand execute timeout when trying to read access rights for sector %2d. Trying with defaults...", sectorNo);
rights[sectorNo][0] = rights[sectorNo][1] = rights[sectorNo][2] = 0x00;
rights[sectorNo][3] = 0x01;
}
}
}
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(SUCCESS, "Finished reading sector access bits");
PrintAndLogEx(INFO, "Dumping all blocks from card...");
for (sectorNo = 0; sectorNo < numSectors; sectorNo++) {
for (blockNo = 0; blockNo < mfNumBlocksPerSector(sectorNo); blockNo++) {
bool received = false;
current_key = MF_KEY_A;
for (tries = 0; tries < MIFARE_SECTOR_RETRY; tries++) {
if (blockNo == mfNumBlocksPerSector(sectorNo) - 1) { // sector trailer. At least the Access Conditions can always be read with key A.
payload.blockno = mfFirstBlockOfSector(sectorNo) + blockNo;
payload.keytype = current_key;
memcpy(payload.key, current_key == MF_KEY_A ? keyA[sectorNo] : keyB[sectorNo], sizeof(payload.key));
clearCommandBuffer();
SendCommandNG(CMD_HF_MIFARE_READBL, (uint8_t *)&payload, sizeof(mf_readblock_t));
received = WaitForResponseTimeout(CMD_HF_MIFARE_READBL, &resp, 1500);
} else { // data block. Check if it can be read with key A or key B
uint8_t data_area = (sectorNo < 32) ? blockNo : blockNo / 5;
if ((rights[sectorNo][data_area] == 0x03) || (rights[sectorNo][data_area] == 0x05)) { // only key B would work
payload.blockno = mfFirstBlockOfSector(sectorNo) + blockNo;
payload.keytype = MF_KEY_B;
memcpy(payload.key, keyB[sectorNo], sizeof(payload.key));
clearCommandBuffer();
SendCommandNG(CMD_HF_MIFARE_READBL, (uint8_t *)&payload, sizeof(mf_readblock_t));
received = WaitForResponseTimeout(CMD_HF_MIFARE_READBL, &resp, 1500);
} else if (rights[sectorNo][data_area] == 0x07) { // no key would work
PrintAndLogEx(WARNING, "access rights do not allow reading of sector %2d block %3d", sectorNo, blockNo);
// where do you want to go?? Next sector or block?
break;
} else { // key A would work
payload.blockno = mfFirstBlockOfSector(sectorNo) + blockNo;
payload.keytype = current_key;
memcpy(payload.key, current_key == MF_KEY_A ? keyA[sectorNo] : keyB[sectorNo], sizeof(payload.key));
clearCommandBuffer();
SendCommandNG(CMD_HF_MIFARE_READBL, (uint8_t *)&payload, sizeof(mf_readblock_t));
received = WaitForResponseTimeout(CMD_HF_MIFARE_READBL, &resp, 1500);
}
}
if (received) {
if (resp.status == PM3_SUCCESS) {
// break the re-try loop
break;
}
if ((current_key == MF_KEY_A) && (tries == (MIFARE_SECTOR_RETRY / 2))) {
// Half the tries failed with key A. Swap for key B
current_key = MF_KEY_B;
// clear out keyA since it failed.
memset(keyA[sectorNo], 0x00, sizeof(keyA[sectorNo]));
}
}
}
if (received) {
uint8_t *data = resp.data.asBytes;
if (blockNo == mfNumBlocksPerSector(sectorNo) - 1) { // sector trailer. Fill in the keys.
data[0] = (keyA[sectorNo][0]);
data[1] = (keyA[sectorNo][1]);
data[2] = (keyA[sectorNo][2]);
data[3] = (keyA[sectorNo][3]);
data[4] = (keyA[sectorNo][4]);
data[5] = (keyA[sectorNo][5]);
data[10] = (keyB[sectorNo][0]);
data[11] = (keyB[sectorNo][1]);
data[12] = (keyB[sectorNo][2]);
data[13] = (keyB[sectorNo][3]);
data[14] = (keyB[sectorNo][4]);
data[15] = (keyB[sectorNo][5]);
}
if (resp.status == PM3_SUCCESS) {
memcpy(carddata[mfFirstBlockOfSector(sectorNo) + blockNo], data, 16);
PrintAndLogEx(SUCCESS, "successfully read block %2d of sector %2d.", blockNo, sectorNo);
} else {
PrintAndLogEx(FAILED, "could not read block %2d of sector %2d", blockNo, sectorNo);
break;
}
} else {
PrintAndLogEx(WARNING, "command execute timeout when trying to read block %2d of sector %2d.", blockNo, sectorNo);
break;
}
}
}
PrintAndLogEx(SUCCESS, "time: %" PRIu64 " seconds\n", (msclock() - t1) / 1000);
PrintAndLogEx(SUCCESS, "\nSucceeded in dumping all blocks");
if (strlen(dataFilename) < 1) {
fptr = GenerateFilename("hf-mf-", "-dump");
if (fptr == NULL)
return PM3_ESOFT;
strcpy(dataFilename, fptr);
free(fptr);
}
uint16_t bytes = 16 * (mfFirstBlockOfSector(numSectors - 1) + mfNumBlocksPerSector(numSectors - 1));
saveFile(dataFilename, ".bin", (uint8_t *)carddata, bytes);
saveFileEML(dataFilename, (uint8_t *)carddata, bytes, MFBLOCK_SIZE);
iso14a_mf_extdump_t xdump;
xdump.card_info = card;
xdump.dump = (uint8_t *)carddata;
xdump.dumplen = bytes;
saveFileJSON(dataFilename, jsfCardMemory, (uint8_t *)&xdump, sizeof(xdump), NULL);
return PM3_SUCCESS;
}
static int CmdHF14AMfRestore(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf mf restore",
"Restore MIFARE Classic dump file to tag.\n"
"\n"
"The key file and dump file will program the card sector trailers.\n"
"By default we authenticate to card with key B 0xFFFFFFFFFFFF.\n"
"If access rights in dump file is all zeros, it will be replaced with default values\n"
"\n"
"`--uid` param is used for filename templates `hf-mf-<uid>-dump.bin` and `hf-mf-<uid>-key.bin.\n"
" If not specified, it will read the card uid instead.\n"
" `--ka` param you can indicate that the key file should be used for authentication instead.\n"
" if so we also try both B/A keys",
"hf mf restore\n"
"hf mf restore --1k --uid 04010203\n"
"hf mf restore --1k --uid 04010203 -k hf-mf-AABBCCDD-key.bin\n"
"hf mf restore --4k"
);
void *argtable[] = {
arg_param_begin,
arg_lit0(NULL, "mini", "MIFARE Classic Mini / S20"),
arg_lit0(NULL, "1k", "MIFARE Classic 1k / S50 (def)"),
arg_lit0(NULL, "2k", "MIFARE Classic/Plus 2k"),
arg_lit0(NULL, "4k", "MIFARE Classic 4k / S70"),
arg_str0("u", "uid", "<hex>", "uid, (4|7|10 hex bytes)"),
arg_str0("f", "file", "<fn>", "specify dump filename (bin/eml/json)"),
arg_str0("k", "kfn", "<fn>", "key filename"),
arg_lit0(NULL, "ka", "use specified keyfile to authenticate"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
bool m0 = arg_get_lit(ctx, 1);
bool m1 = arg_get_lit(ctx, 2);
bool m2 = arg_get_lit(ctx, 3);
bool m4 = arg_get_lit(ctx, 4);
int uidlen = 0;
char uid[20] = {0};
CLIParamStrToBuf(arg_get_str(ctx, 5), (uint8_t *)uid, sizeof(uid), &uidlen);
int datafnlen = 0;
char datafilename[FILE_PATH_SIZE] = {0};
CLIParamStrToBuf(arg_get_str(ctx, 6), (uint8_t *)datafilename, FILE_PATH_SIZE, &datafnlen);
int keyfnlen = 0;
char keyfilename[FILE_PATH_SIZE] = {0};
CLIParamStrToBuf(arg_get_str(ctx, 7), (uint8_t *)keyfilename, FILE_PATH_SIZE, &keyfnlen);
bool use_keyfile_for_auth = arg_get_lit(ctx, 8);
CLIParserFree(ctx);
// validations
if ((m0 + m1 + m2 + m4) > 1) {
PrintAndLogEx(WARNING, "Only specify one MIFARE Type");
return PM3_EINVARG;
} else if ((m0 + m1 + m2 + m4) == 0) {
m1 = true;
}
uint8_t sectors = MIFARE_1K_MAXSECTOR;
if (m0) {
sectors = MIFARE_MINI_MAXSECTOR;
} else if (m1) {
sectors = MIFARE_1K_MAXSECTOR;
} else if (m2) {
sectors = MIFARE_2K_MAXSECTOR;
} else if (m4) {
sectors = MIFARE_4K_MAXSECTOR;
} else {
PrintAndLogEx(WARNING, "Please specify a MIFARE Type");
return PM3_EINVARG;
}
// if user specified UID, use it in file templates
if (uidlen) {
if (keyfnlen == 0) {
snprintf(keyfilename, FILE_PATH_SIZE, "hf-mf-%s-key.bin", uid);
keyfnlen = strlen(keyfilename);
}
if (datafnlen == 0) {
snprintf(datafilename, FILE_PATH_SIZE, "hf-mf-%s-dump.bin", uid);
datafnlen = strlen(datafilename);
}
}
// try reading card uid and create filename
if (keyfnlen == 0) {
char *fptr = GenerateFilename("hf-mf-", "-key.bin");
if (fptr == NULL)
return PM3_ESOFT;
strcpy(keyfilename, fptr);
free(fptr);
}
FILE *f;
if ((f = fopen(keyfilename, "rb")) == NULL) {
PrintAndLogEx(WARNING, "Could not find file " _YELLOW_("%s"), keyfilename);
return PM3_EFILE;
}
// key arrays
uint8_t keyA[40][6];
uint8_t keyB[40][6];
// read key file
size_t bytes_read;
for (uint8_t s = 0; s < sectors; s++) {
bytes_read = fread(keyA[s], 1, 6, f);
if (bytes_read != 6) {
PrintAndLogEx(ERR, "File reading error " _YELLOW_("%s"), keyfilename);
fclose(f);
return PM3_EFILE;
}
}
for (uint8_t s = 0; s < sectors; s++) {
bytes_read = fread(keyB[s], 1, 6, f);
if (bytes_read != 6) {
PrintAndLogEx(ERR, "File reading error " _YELLOW_("%s"), keyfilename);
fclose(f);
return PM3_EFILE;
}
}
fclose(f);
// try reading card uid and create filename
if (datafnlen == 0) {
char *fptr = GenerateFilename("hf-mf-", "-dump.bin");
if (fptr == NULL)
return PM3_ESOFT;
strcpy(datafilename, fptr);
free(fptr);
}
// read dump file
uint8_t *dump = NULL;
bytes_read = 0;
int res = pm3_load_dump(datafilename, (void **)&dump, &bytes_read, (MFBLOCK_SIZE * MIFARE_4K_MAXBLOCK));
if (res != PM3_SUCCESS) {
return res;
}
// default authentication key
uint8_t default_key[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
PrintAndLogEx(INFO, "Restoring " _YELLOW_("%s")" to card", datafilename);
// main loop for restoreing.
// a bit more complicated than needed
// this is because of two things.
// 1. we are setting keys from a key file or using the existing ones in the dump
// 2. we need to authenticate against a card which might not have default keys.
uint8_t *ref_dump = dump;
for (uint8_t s = 0; s < sectors; s++) {
for (uint8_t b = 0; b < mfNumBlocksPerSector(s); b++) {
uint8_t bldata[MFBLOCK_SIZE] = {0x00};
memcpy(bldata, dump, MFBLOCK_SIZE);
// if sector trailer
if (mfNumBlocksPerSector(s) - 1 == b) {
if (use_keyfile_for_auth == false) {
// replace KEY A
bldata[0] = (keyA[s][0]);
bldata[1] = (keyA[s][1]);
bldata[2] = (keyA[s][2]);
bldata[3] = (keyA[s][3]);
bldata[4] = (keyA[s][4]);
bldata[5] = (keyA[s][5]);
// replace KEY B
bldata[10] = (keyB[s][0]);
bldata[11] = (keyB[s][1]);
bldata[12] = (keyB[s][2]);
bldata[13] = (keyB[s][3]);
bldata[14] = (keyB[s][4]);
bldata[15] = (keyB[s][5]);
}
// ensure access right isn't messed up.
if (mfValidateAccessConditions(&bldata[6]) == false) {
PrintAndLogEx(WARNING, "Invalid Access Conditions on sector %i, replacing by default values", s);
memcpy(bldata + 6, "\xFF\x07\x80\x69", 4);
}
}
if (bytes_read) {
dump += MFBLOCK_SIZE;
bytes_read -= MFBLOCK_SIZE;
}
uint8_t wdata[26];
memcpy(wdata + 10, bldata, sizeof(bldata));
if (use_keyfile_for_auth) {
for (int8_t kt = MF_KEY_B; kt > -1; kt--) {
if (kt == MF_KEY_A)
memcpy(wdata, keyA[s], 6);
else
memcpy(wdata, keyB[s], 6);
PrintAndLogEx(INFO, "block %3d: %s", mfFirstBlockOfSector(s) + b, sprint_hex(bldata, sizeof(bldata)));
clearCommandBuffer();
SendCommandMIX(CMD_HF_MIFARE_WRITEBL, mfFirstBlockOfSector(s) + b, kt, 0, wdata, sizeof(wdata));
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) {
uint8_t isOK = resp.oldarg[0] & 0xff;
if (isOK == 0) {
if (b == 0) {
PrintAndLogEx(INFO, "Writing to manufacture block w key %c ( " _RED_("fail") " )", (kt == MF_KEY_A) ? 'A' : 'B');
} else {
PrintAndLogEx(FAILED, "Write to block %u w key %c ( " _RED_("fail") " ) ", b, (kt == MF_KEY_A) ? 'A' : 'B');
}
} else {
// if success, skip to next block
break;
}
} else {
PrintAndLogEx(WARNING, "Command execute timeout");
}
}
} else {
// use default key to authenticate for the write command
memcpy(wdata, default_key, 6);
PrintAndLogEx(INFO, "block %3d: %s", mfFirstBlockOfSector(s) + b, sprint_hex(bldata, sizeof(bldata)));
clearCommandBuffer();
SendCommandMIX(CMD_HF_MIFARE_WRITEBL, mfFirstBlockOfSector(s) + b, MF_KEY_B, 0, wdata, sizeof(wdata));
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) {
uint8_t isOK = resp.oldarg[0] & 0xff;
if (isOK == 0) {
if (b == 0) {
PrintAndLogEx(INFO, "Writing to manufacture block w key B ( " _RED_("fail") " )");
} else {
PrintAndLogEx(FAILED, "Write to block %u w key B ( " _RED_("fail") " )", b);
}
}
} else {
PrintAndLogEx(WARNING, "Command execute timeout");
}
} // end use_keyfile_for_auth
} // end loop B
} // end loop S
free(ref_dump);
PrintAndLogEx(INFO, "Done!");
return PM3_SUCCESS;
}
static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't find keys...
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf mf nested",
"Execute Nested attack against MIFARE Classic card for key recovery",
"hf mf nested --blk 0 -a -k FFFFFFFFFFFF --tblk 4 --ta --> Use block 0 Key A to find block 4 Key A (single sector key recovery)\n"
"hf mf nested --mini --blk 0 -a -k FFFFFFFFFFFF --> Key recovery against MIFARE Mini\n"
"hf mf nested --1k --blk 0 -a -k FFFFFFFFFFFF --> Key recovery against MIFARE Classic 1k\n"
"hf mf nested --2k --blk 0 -a -k FFFFFFFFFFFF --> Key recovery against MIFARE 2k\n"
"hf mf nested --4k --blk 0 -a -k FFFFFFFFFFFF --> Key recovery against MIFARE 4k");
void *argtable[] = {
arg_param_begin,
arg_str0("k", "key", "<hex>", "Key specified as 12 hex symbols"),
arg_lit0(NULL, "mini", "MIFARE Classic Mini / S20"),
arg_lit0(NULL, "1k", "MIFARE Classic 1k / S50"),
arg_lit0(NULL, "2k", "MIFARE Classic/Plus 2k"),
arg_lit0(NULL, "4k", "MIFARE Classic 4k / S70"),
arg_int0(NULL, "blk", "<dec>", "Input block number"),
arg_lit0("a", NULL, "Input key specified is A key (default)"),
arg_lit0("b", NULL, "Input key specified is B key"),
arg_int0(NULL, "tblk", "<dec>", "Target block number"),
arg_lit0(NULL, "ta", "Target A key (default)"),
arg_lit0(NULL, "tb", "Target B key"),
arg_lit0(NULL, "emu", "Fill simulator keys from found keys"),
arg_lit0(NULL, "dump", "Dump found keys to file"),
arg_lit0(NULL, "mem", "Use dictionary from flashmemory"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, false);
int keylen = 0;
uint8_t key[6] = {0};
CLIGetHexWithReturn(ctx, 1, key, &keylen);
bool m0 = arg_get_lit(ctx, 2);
bool m1 = arg_get_lit(ctx, 3);
bool m2 = arg_get_lit(ctx, 4);
bool m4 = arg_get_lit(ctx, 5);
uint8_t blockNo = arg_get_u32_def(ctx, 6, 0);
uint8_t keyType = MF_KEY_A;
if (arg_get_lit(ctx, 7) && arg_get_lit(ctx, 8)) {
CLIParserFree(ctx);
PrintAndLogEx(WARNING, "Input key type must be A or B");
return PM3_EINVARG;
} else if (arg_get_lit(ctx, 8)) {
keyType = MF_KEY_B;
}
int trgBlockNo = arg_get_int_def(ctx, 9, -1);
uint8_t trgKeyType = MF_KEY_A;
if (arg_get_lit(ctx, 10) && arg_get_lit(ctx, 11)) {
CLIParserFree(ctx);
PrintAndLogEx(WARNING, "Target key type must be A or B");
return PM3_EINVARG;
} else if (arg_get_lit(ctx, 11)) {
trgKeyType = MF_KEY_B;
}
bool transferToEml = arg_get_lit(ctx, 12);
bool createDumpFile = arg_get_lit(ctx, 13);
bool singleSector = trgBlockNo > -1;
bool use_flashmemory = arg_get_lit(ctx, 14);
CLIParserFree(ctx);
//validations
if ((m0 + m1 + m2 + m4) > 1) {
PrintAndLogEx(WARNING, "Only specify one MIFARE Type");
return PM3_EINVARG;
}
uint8_t SectorsCnt = 1;
if (m0) {
SectorsCnt = MIFARE_MINI_MAXSECTOR;
} else if (m1) {
SectorsCnt = MIFARE_1K_MAXSECTOR;
} else if (m2) {
SectorsCnt = MIFARE_2K_MAXSECTOR;
} else if (m4) {
SectorsCnt = MIFARE_4K_MAXSECTOR;
}
if (singleSector) {
uint8_t MinSectorsCnt = 0;
// find a MIFARE type that can accommodate the provided block number
uint8_t s = MAX(mfSectorNum(trgBlockNo), mfSectorNum(blockNo));
if (s < MIFARE_MINI_MAXSECTOR) {
MinSectorsCnt = MIFARE_MINI_MAXSECTOR;
} else if (s < MIFARE_1K_MAXSECTOR) {
MinSectorsCnt = MIFARE_1K_MAXSECTOR;
} else if (s < MIFARE_2K_MAXSECTOR) {
MinSectorsCnt = MIFARE_2K_MAXSECTOR;
} else if (s < MIFARE_4K_MAXSECTOR) {
MinSectorsCnt = MIFARE_4K_MAXSECTOR;
} else {
PrintAndLogEx(WARNING, "Provided block out of possible MIFARE Type memory map");
return PM3_EINVARG;
}
if (SectorsCnt == 1) {
SectorsCnt = MinSectorsCnt;
} else if (SectorsCnt < MinSectorsCnt) {
PrintAndLogEx(WARNING, "Provided block out of provided MIFARE Type memory map");
return PM3_EINVARG;
}
}
if (SectorsCnt == 1) {
SectorsCnt = MIFARE_1K_MAXSECTOR;
}
if (keylen != 6) {
PrintAndLogEx(WARNING, "Input key must include 12 HEX symbols");
return PM3_EINVARG;
}
sector_t *e_sector = NULL;
uint8_t keyBlock[(ARRAYLEN(g_mifare_default_keys) + 1) * 6];
uint64_t key64 = 0;
// check if tag doesn't have static nonce
if (detect_classic_static_nonce() == NONCE_STATIC) {
PrintAndLogEx(WARNING, "Static nonce detected. Quitting...");
PrintAndLogEx(INFO, "\t Try use " _YELLOW_("`hf mf staticnested`"));
return PM3_EOPABORTED;
}
// check if we can authenticate to sector
if (mfCheckKeys(blockNo, keyType, true, 1, key, &key64) != PM3_SUCCESS) {
PrintAndLogEx(WARNING, "Wrong key. Can't authenticate to block:%3d key type:%c", blockNo, keyType ? 'B' : 'A');
return PM3_EOPABORTED;
}
if (singleSector) {
int16_t isOK = mfnested(blockNo, keyType, key, trgBlockNo, trgKeyType, keyBlock, true);
switch (isOK) {
case PM3_ETIMEOUT:
PrintAndLogEx(ERR, "Command execute timeout\n");
break;
case PM3_EOPABORTED:
PrintAndLogEx(WARNING, "Button pressed. Aborted.\n");
break;
case PM3_EFAILED:
PrintAndLogEx(FAILED, "Tag isn't vulnerable to Nested Attack (PRNG is not predictable).\n");
break;
case PM3_ESOFT:
PrintAndLogEx(FAILED, "No valid key found");
break;
case PM3_SUCCESS:
key64 = bytes_to_num(keyBlock, 6);
// transfer key to the emulator
if (transferToEml) {
uint8_t sectortrailer;
if (trgBlockNo < 32 * 4) { // 4 block sector
sectortrailer = trgBlockNo | 0x03;
} else { // 16 block sector
sectortrailer = trgBlockNo | 0x0f;
}
mfEmlGetMem(keyBlock, sectortrailer, 1);
if (trgKeyType == MF_KEY_A)
num_to_bytes(key64, 6, keyBlock);
else
num_to_bytes(key64, 6, &keyBlock[10]);
mfEmlSetMem(keyBlock, sectortrailer, 1);
PrintAndLogEx(SUCCESS, "Key transferred to emulator memory.");
}
return PM3_SUCCESS;
default :
PrintAndLogEx(ERR, "Unknown error.\n");
}
return PM3_SUCCESS;
} else { // ------------------------------------ multiple sectors working
uint64_t t1 = msclock();
e_sector = calloc(SectorsCnt, sizeof(sector_t));
if (e_sector == NULL) return PM3_EMALLOC;
// add our known key
e_sector[mfSectorNum(blockNo)].foundKey[keyType] = 1;
e_sector[mfSectorNum(blockNo)].Key[keyType] = key64;
//test current key and additional standard keys first
// add parameter key
memcpy(keyBlock + (ARRAYLEN(g_mifare_default_keys) * 6), key, 6);
for (int cnt = 0; cnt < ARRAYLEN(g_mifare_default_keys); cnt++) {
num_to_bytes(g_mifare_default_keys[cnt], 6, (uint8_t *)(keyBlock + cnt * 6));
}
PrintAndLogEx(SUCCESS, "Testing known keys. Sector count "_YELLOW_("%d"), SectorsCnt);
int res = mfCheckKeys_fast(SectorsCnt, true, true, 1, ARRAYLEN(g_mifare_default_keys) + 1, keyBlock, e_sector, use_flashmemory);
if (res == PM3_SUCCESS) {
PrintAndLogEx(SUCCESS, "Fast check found all keys");
goto jumptoend;
}
uint64_t t2 = msclock() - t1;
PrintAndLogEx(SUCCESS, "Time to check " _YELLOW_("%zu") " known keys: %.0f seconds\n", ARRAYLEN(g_mifare_default_keys), (float)t2 / 1000.0);
PrintAndLogEx(SUCCESS, "enter nested key recovery");
// nested sectors
bool calibrate = true;
for (trgKeyType = MF_KEY_A; trgKeyType <= MF_KEY_B; ++trgKeyType) {
for (uint8_t sectorNo = 0; sectorNo < SectorsCnt; ++sectorNo) {
for (int i = 0; i < MIFARE_SECTOR_RETRY; i++) {
if (e_sector[sectorNo].foundKey[trgKeyType]) continue;
int16_t isOK = mfnested(blockNo, keyType, key, mfFirstBlockOfSector(sectorNo), trgKeyType, keyBlock, calibrate);
switch (isOK) {
case PM3_ETIMEOUT:
PrintAndLogEx(ERR, "Command execute timeout\n");
break;
case PM3_EOPABORTED:
PrintAndLogEx(WARNING, "button pressed. Aborted.\n");
break;
case PM3_EFAILED :
PrintAndLogEx(FAILED, "Tag isn't vulnerable to Nested Attack (PRNG is not predictable).\n");
break;
case PM3_ESOFT:
//key not found
calibrate = false;
continue;
case PM3_SUCCESS:
calibrate = false;
e_sector[sectorNo].foundKey[trgKeyType] = 1;
e_sector[sectorNo].Key[trgKeyType] = bytes_to_num(keyBlock, 6);
mfCheckKeys_fast(SectorsCnt, true, true, 2, 1, keyBlock, e_sector, false);
continue;
default :
PrintAndLogEx(ERR, "Unknown error.\n");
}
free(e_sector);
return PM3_ESOFT;
}
}
}
t1 = msclock() - t1;
PrintAndLogEx(SUCCESS, "time in nested " _YELLOW_("%.0f") " seconds\n", (float)t1 / 1000.0);
// 20160116 If Sector A is found, but not Sector B, try just reading it of the tag?
PrintAndLogEx(INFO, "trying to read key B...");
for (int i = 0; i < SectorsCnt; i++) {
// KEY A but not KEY B
if (e_sector[i].foundKey[0] && !e_sector[i].foundKey[1]) {
uint8_t sectrail = (mfFirstBlockOfSector(i) + mfNumBlocksPerSector(i) - 1);
PrintAndLogEx(SUCCESS, "reading block %d", sectrail);
mf_readblock_t payload;
payload.blockno = sectrail;
payload.keytype = MF_KEY_A;
num_to_bytes(e_sector[i].Key[0], 6, payload.key); // KEY A
clearCommandBuffer();
SendCommandNG(CMD_HF_MIFARE_READBL, (uint8_t *)&payload, sizeof(mf_readblock_t));
PacketResponseNG resp;
if (!WaitForResponseTimeout(CMD_HF_MIFARE_READBL, &resp, 1500)) continue;
if (resp.status != PM3_SUCCESS) continue;
uint8_t *data = resp.data.asBytes;
key64 = bytes_to_num(data + 10, 6);
if (key64) {
PrintAndLogEx(SUCCESS, "data: %s", sprint_hex(data + 10, 6));
e_sector[i].foundKey[1] = true;
e_sector[i].Key[1] = key64;
}
}
}
jumptoend:
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(SUCCESS, _GREEN_("found keys:"));
//print them
printKeyTable(SectorsCnt, e_sector);
// transfer them to the emulator
if (transferToEml) {
// fast push mode
g_conn.block_after_ACK = true;
for (int i = 0; i < SectorsCnt; i++) {
mfEmlGetMem(keyBlock, mfFirstBlockOfSector(i) + mfNumBlocksPerSector(i) - 1, 1);
if (e_sector[i].foundKey[0])
num_to_bytes(e_sector[i].Key[0], 6, keyBlock);
if (e_sector[i].foundKey[1])
num_to_bytes(e_sector[i].Key[1], 6, &keyBlock[10]);
if (i == SectorsCnt - 1) {
// Disable fast mode on last packet
g_conn.block_after_ACK = false;
}
mfEmlSetMem(keyBlock, mfFirstBlockOfSector(i) + mfNumBlocksPerSector(i) - 1, 1);
}
PrintAndLogEx(SUCCESS, "keys transferred to emulator memory.");
}
// Create dump file
if (createDumpFile) {
char *fptr = GenerateFilename("hf-mf-", "-key.bin");
if (createMfcKeyDump(fptr, SectorsCnt, e_sector) != PM3_SUCCESS) {
PrintAndLogEx(ERR, "Failed to save keys to file");
free(e_sector);
free(fptr);
return PM3_EFILE;
}
free(fptr);
}
free(e_sector);
}
return PM3_SUCCESS;
}
static int CmdHF14AMfNestedStatic(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf mf staticnested",
"Execute Nested attack against MIFARE Classic card with static nonce for key recovery.\n"
"Supply a known key from one block to recover all keys",
"hf mf staticnested --mini --blk 0 -a -k FFFFFFFFFFFF\n"
"hf mf staticnested --1k --blk 0 -a -k FFFFFFFFFFFF\n"
"hf mf staticnested --2k --blk 0 -a -k FFFFFFFFFFFF\n"
"hf mf staticnested --4k --blk 0 -a -k FFFFFFFFFFFF\n");
void *argtable[] = {
arg_param_begin,
arg_str0("k", "key", "<hex>", "Known key (12 hex symbols)"),
arg_lit0(NULL, "mini", "MIFARE Classic Mini / S20"),
arg_lit0(NULL, "1k", "MIFARE Classic 1k / S50"),
arg_lit0(NULL, "2k", "MIFARE Classic/Plus 2k"),
arg_lit0(NULL, "4k", "MIFARE Classic 4k / S70"),
arg_int0(NULL, "blk", "<dec>", "Input block number"),
arg_lit0("a", NULL, "Input key specified is keyA (def)"),
arg_lit0("b", NULL, "Input key specified is keyB"),
arg_lit0("e", "emukeys", "Fill simulator keys from found keys"),
arg_lit0(NULL, "dumpkeys", "Dump found keys to file"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, false);
int keylen = 0;
uint8_t key[6] = {0};
CLIGetHexWithReturn(ctx, 1, key, &keylen);
bool m0 = arg_get_lit(ctx, 2);
bool m1 = arg_get_lit(ctx, 3);
bool m2 = arg_get_lit(ctx, 4);
bool m4 = arg_get_lit(ctx, 5);
uint8_t blockNo = arg_get_u32_def(ctx, 6, 0);
uint8_t keyType = MF_KEY_A;
if (arg_get_lit(ctx, 7) && arg_get_lit(ctx, 8)) {
CLIParserFree(ctx);
PrintAndLogEx(WARNING, "Input key type must be A or B");
return PM3_EINVARG;
} else if (arg_get_lit(ctx, 8)) {
keyType = MF_KEY_B;
}
bool transferToEml = arg_get_lit(ctx, 9);
bool createDumpFile = arg_get_lit(ctx, 10);
CLIParserFree(ctx);
//validations
if ((m0 + m1 + m2 + m4) > 1) {
PrintAndLogEx(WARNING, "Only specify one MIFARE Type");
return PM3_EINVARG;
}
uint8_t SectorsCnt = 1;
if (m0) {
SectorsCnt = MIFARE_MINI_MAXSECTOR;
} else if (m1) {
SectorsCnt = MIFARE_1K_MAXSECTOR;
} else if (m2) {
SectorsCnt = MIFARE_2K_MAXSECTOR;
} else if (m4) {
SectorsCnt = MIFARE_4K_MAXSECTOR;
} else {
PrintAndLogEx(WARNING, "Please specify a MIFARE Type");
return PM3_EINVARG;
}
if (keylen != 6) {
PrintAndLogEx(WARNING, "Input key must include 12 HEX symbols");
return PM3_EINVARG;
}
sector_t *e_sector = NULL;
uint8_t trgKeyType = MF_KEY_A;
uint8_t keyBlock[(ARRAYLEN(g_mifare_default_keys) + 1) * 6];
uint64_t key64 = 0;
// check if tag have static nonce
if (detect_classic_static_nonce() != NONCE_STATIC) {
PrintAndLogEx(WARNING, "Normal nonce detected, or failed read of card. Quitting...");
PrintAndLogEx(INFO, "\t Try use " _YELLOW_("`hf mf nested`"));
return PM3_EOPABORTED;
}
// check if we can authenticate to sector
if (mfCheckKeys(blockNo, keyType, true, 1, key, &key64) != PM3_SUCCESS) {
PrintAndLogEx(WARNING, "Wrong key. Can't authenticate to block: %3d key type: %c", blockNo, keyType ? 'B' : 'A');
return PM3_EOPABORTED;
}
if (IfPm3Flash()) {
PrintAndLogEx(INFO, "RDV4 with flashmemory supported detected.");
}
uint64_t t1 = msclock();
e_sector = calloc(SectorsCnt, sizeof(sector_t));
if (e_sector == NULL)
return PM3_EMALLOC;
// add our known key
e_sector[mfSectorNum(blockNo)].foundKey[keyType] = 1;
e_sector[mfSectorNum(blockNo)].Key[keyType] = key64;
//test current key and additional standard keys first
// add parameter key
memcpy(keyBlock + (ARRAYLEN(g_mifare_default_keys) * 6), key, 6);
for (int cnt = 0; cnt < ARRAYLEN(g_mifare_default_keys); cnt++) {
num_to_bytes(g_mifare_default_keys[cnt], 6, (uint8_t *)(keyBlock + cnt * 6));
}
PrintAndLogEx(SUCCESS, "Testing known keys. Sector count "_YELLOW_("%d"), SectorsCnt);
int res = mfCheckKeys_fast(SectorsCnt, true, true, 1, ARRAYLEN(g_mifare_default_keys) + 1, keyBlock, e_sector, false);
if (res == PM3_SUCCESS) {
// all keys found
PrintAndLogEx(SUCCESS, "Fast check found all keys");
goto jumptoend;
}
uint64_t t2 = msclock() - t1;
PrintAndLogEx(SUCCESS, "Time to check "_YELLOW_("%zu") " known keys: %.0f seconds\n", ARRAYLEN(g_mifare_default_keys), (float)t2 / 1000.0);
PrintAndLogEx(SUCCESS, "enter static nested key recovery");
// nested sectors
for (trgKeyType = MF_KEY_A; trgKeyType <= MF_KEY_B; ++trgKeyType) {
for (uint8_t sectorNo = 0; sectorNo < SectorsCnt; ++sectorNo) {
for (int i = 0; i < 1; i++) {
if (e_sector[sectorNo].foundKey[trgKeyType]) continue;
int16_t isOK = mfStaticNested(blockNo, keyType, key, mfFirstBlockOfSector(sectorNo), trgKeyType, keyBlock);
switch (isOK) {
case PM3_ETIMEOUT :
PrintAndLogEx(ERR, "Command execute timeout");
break;
case PM3_EOPABORTED :
PrintAndLogEx(WARNING, "aborted via keyboard.");
break;
case PM3_ESOFT :
continue;
case PM3_SUCCESS :
e_sector[sectorNo].foundKey[trgKeyType] = 1;
e_sector[sectorNo].Key[trgKeyType] = bytes_to_num(keyBlock, 6);
// mfCheckKeys_fast(SectorsCnt, true, true, 2, 1, keyBlock, e_sector, false);
continue;
default :
PrintAndLogEx(ERR, "unknown error.\n");
}
free(e_sector);
return PM3_ESOFT;
}
}
}
t1 = msclock() - t1;
PrintAndLogEx(SUCCESS, "time in static nested " _YELLOW_("%.0f") " seconds\n", (float)t1 / 1000.0);
// 20160116 If Sector A is found, but not Sector B, try just reading it of the tag?
PrintAndLogEx(INFO, "trying to read key B...");
for (int i = 0; i < SectorsCnt; i++) {
// KEY A but not KEY B
if (e_sector[i].foundKey[0] && !e_sector[i].foundKey[1]) {
uint8_t sectrail = (mfFirstBlockOfSector(i) + mfNumBlocksPerSector(i) - 1);
PrintAndLogEx(SUCCESS, "reading block %d", sectrail);
mf_readblock_t payload;
payload.blockno = sectrail;
payload.keytype = MF_KEY_A;
num_to_bytes(e_sector[i].Key[0], 6, payload.key); // KEY A
clearCommandBuffer();
SendCommandNG(CMD_HF_MIFARE_READBL, (uint8_t *)&payload, sizeof(mf_readblock_t));
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_HF_MIFARE_READBL, &resp, 1500) == false) {
continue;
}
if (resp.status != PM3_SUCCESS) continue;
uint8_t *data = resp.data.asBytes;
key64 = bytes_to_num(data + 10, 6);
if (key64) {
PrintAndLogEx(SUCCESS, "data: %s", sprint_hex(data + 10, 6));
e_sector[i].foundKey[1] = true;
e_sector[i].Key[1] = key64;
}
}
}
jumptoend:
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(SUCCESS, _GREEN_("found keys:"));
//print them
printKeyTable(SectorsCnt, e_sector);
// transfer them to the emulator
if (transferToEml) {
// fast push mode
g_conn.block_after_ACK = true;
for (int i = 0; i < SectorsCnt; i++) {
mfEmlGetMem(keyBlock, mfFirstBlockOfSector(i) + mfNumBlocksPerSector(i) - 1, 1);
if (e_sector[i].foundKey[0])
num_to_bytes(e_sector[i].Key[0], 6, keyBlock);
if (e_sector[i].foundKey[1])
num_to_bytes(e_sector[i].Key[1], 6, &keyBlock[10]);
if (i == SectorsCnt - 1) {
// Disable fast mode on last packet
g_conn.block_after_ACK = false;
}
mfEmlSetMem(keyBlock, mfFirstBlockOfSector(i) + mfNumBlocksPerSector(i) - 1, 1);
}
PrintAndLogEx(SUCCESS, "keys transferred to emulator memory.");
}
// Create dump file
if (createDumpFile) {
char *fptr = GenerateFilename("hf-mf-", "-key.bin");
if (createMfcKeyDump(fptr, SectorsCnt, e_sector) != PM3_SUCCESS) {
PrintAndLogEx(ERR, "Failed to save keys to file");
free(e_sector);
free(fptr);
return PM3_EFILE;
}
free(fptr);
}
free(e_sector);
return PM3_SUCCESS;
}
static int CmdHF14AMfNestedHard(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf mf hardnested",
"Nested attack for hardened MIFARE Classic cards.\n"
"if card is EV1, command can detect and use known key see example below\n"
"\n"
"`--i<X>` set type of SIMD instructions. Without this flag programs autodetect it.\n"
" or \n"
" hf mf hardnested -r --tk [known target key]\n"
"Add the known target key to check if it is present in the remaining key space\n"
" hf mf hardnested --blk 0 -a -k A0A1A2A3A4A5 --tblk 4 --ta --tk FFFFFFFFFFFF\n"
,
"hf mf hardnested --tblk 4 --ta --> works for MFC EV1\n"
"hf mf hardnested --blk 0 -a -k FFFFFFFFFFFF --tblk 4 --ta\n"
"hf mf hardnested --blk 0 -a -k FFFFFFFFFFFF --tblk 4 --ta -w\n"
"hf mf hardnested --blk 0 -a -k FFFFFFFFFFFF --tblk 4 --ta -f nonces.bin -w -s\n"
"hf mf hardnested -r\n"
"hf mf hardnested -r --tk a0a1a2a3a4a5\n"
"hf mf hardnested -t --tk a0a1a2a3a4a5\n"
"hf mf hardnested --blk 0 -a -k a0a1a2a3a4a5 --tblk 4 --ta --tk FFFFFFFFFFFF\n"
);
void *argtable[] = {
arg_param_begin,
arg_str0("k", "key", "<hex>", "Key, 12 hex bytes"), // 1
arg_int0(NULL, "blk", "<dec>", "Input block number"), // 2
arg_lit0("a", NULL, "Input key A (def)"), // 3
arg_lit0("b", NULL, "Input key B"),
arg_int0(NULL, "tblk", "<dec>", "Target block number"),
arg_lit0(NULL, "ta", "Target key A"),
arg_lit0(NULL, "tb", "Target key B"),
arg_str0(NULL, "tk", "<hex>", "Target key, 12 hex bytes"), // 8
arg_str0("u", "uid", "<hex>", "R/W `hf-mf-<UID>-nonces.bin` instead of default name"),
arg_str0("f", "file", "<fn>", "R/W <name> instead of default name"),
arg_lit0("r", "read", "Read `hf-mf-<UID>-nonces.bin` if tag present, otherwise `nonces.bin`, and start attack"),
arg_lit0("s", "slow", "Slower acquisition (required by some non standard cards)"),
arg_lit0("t", "tests", "Run tests"),
arg_lit0("w", "wr", "Acquire nonces and UID, and write them to file `hf-mf-<UID>-nonces.bin`"),
arg_lit0(NULL, "in", "None (use CPU regular instruction set)"),
#if defined(COMPILER_HAS_SIMD_X86)
arg_lit0(NULL, "im", "MMX"),
arg_lit0(NULL, "is", "SSE2"),
arg_lit0(NULL, "ia", "AVX"),
arg_lit0(NULL, "i2", "AVX2"),
#endif
#if defined(COMPILER_HAS_SIMD_AVX512)
arg_lit0(NULL, "i5", "AVX512"),
#endif
#if defined(COMPILER_HAS_SIMD_NEON)
arg_lit0(NULL, "ie", "NEON"),
#endif
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, false);
int keylen = 0;
uint8_t key[6] = {0};
CLIGetHexWithReturn(ctx, 1, key, &keylen);
uint8_t blockno = arg_get_u32_def(ctx, 2, 0);
uint8_t keytype = MF_KEY_A;
if (arg_get_lit(ctx, 3) && arg_get_lit(ctx, 4)) {
CLIParserFree(ctx);
PrintAndLogEx(WARNING, "Input key type must be A or B");
return PM3_EINVARG;
} else if (arg_get_lit(ctx, 4)) {
keytype = MF_KEY_B;
}
uint8_t trg_blockno = arg_get_u32_def(ctx, 5, 0);
uint8_t trg_keytype = MF_KEY_A;
if (arg_get_lit(ctx, 6) && arg_get_lit(ctx, 7)) {
CLIParserFree(ctx);
PrintAndLogEx(WARNING, "Input key type must be A or B");
return PM3_EINVARG;
} else if (arg_get_lit(ctx, 7)) {
trg_keytype = MF_KEY_B;
}
int trg_keylen = 0;
uint8_t trg_key[6] = {0};
CLIGetHexWithReturn(ctx, 8, trg_key, &trg_keylen);
int uidlen = 0;
char uid[14] = {0};
CLIParamStrToBuf(arg_get_str(ctx, 9), (uint8_t *)uid, sizeof(uid), &uidlen);
int fnlen = 0;
char filename[FILE_PATH_SIZE] = {0};
CLIParamStrToBuf(arg_get_str(ctx, 10), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
bool nonce_file_read = arg_get_lit(ctx, 11);
bool slow = arg_get_lit(ctx, 12);
bool tests = arg_get_lit(ctx, 13);
bool nonce_file_write = arg_get_lit(ctx, 14);
bool in = arg_get_lit(ctx, 15);
#if defined(COMPILER_HAS_SIMD_X86)
bool im = arg_get_lit(ctx, 16);
bool is = arg_get_lit(ctx, 17);
bool ia = arg_get_lit(ctx, 18);
bool i2 = arg_get_lit(ctx, 19);
#endif
#if defined(COMPILER_HAS_SIMD_AVX512)
bool i5 = arg_get_lit(ctx, 20);
#endif
#if defined(COMPILER_HAS_SIMD_NEON)
bool ie = arg_get_lit(ctx, 16);
#endif
CLIParserFree(ctx);
// set SIM instructions
SetSIMDInstr(SIMD_AUTO);
#if defined(COMPILER_HAS_SIMD_AVX512)
if (i5)
SetSIMDInstr(SIMD_AVX512);
#endif
#if defined(COMPILER_HAS_SIMD_X86)
if (i2)
SetSIMDInstr(SIMD_AVX2);
if (ia)
SetSIMDInstr(SIMD_AVX);
if (is)
SetSIMDInstr(SIMD_SSE2);
if (im)
SetSIMDInstr(SIMD_MMX);
#endif
#if defined(COMPILER_HAS_SIMD_NEON)
if (ie)
SetSIMDInstr(SIMD_NEON);
#endif
if (in)
SetSIMDInstr(SIMD_NONE);
bool known_target_key = (trg_keylen);
if (nonce_file_read) {
char *fptr = GenerateFilename("hf-mf-", "-nonces.bin");
if (fptr == NULL)
strncpy(filename, "nonces.bin", FILE_PATH_SIZE - 1);
else
strncpy(filename, fptr, FILE_PATH_SIZE - 1);
free(fptr);
}
if (nonce_file_write) {
char *fptr = GenerateFilename("hf-mf-", "-nonces.bin");
if (fptr == NULL) {
return PM3_EFILE;
}
strncpy(filename, fptr, FILE_PATH_SIZE - 1);
free(fptr);
}
if (uidlen) {
snprintf(filename, FILE_PATH_SIZE, "hf-mf-%s-nonces.bin", uid);
}
// detect MFC EV1 Signature
if (detect_mfc_ev1_signature() && keylen == 0) {
PrintAndLogEx(INFO, "MIFARE Classic EV1 card detected");
blockno = 69;
keytype = MF_KEY_B;
memcpy(key, g_mifare_signature_key_b, sizeof(g_mifare_signature_key_b));
}
if (known_target_key == false && nonce_file_read == false) {
// check if tag doesn't have static nonce
if (detect_classic_static_nonce() == NONCE_STATIC) {
PrintAndLogEx(WARNING, "Static nonce detected. Quitting...");
PrintAndLogEx(HINT, "\tTry use `" _YELLOW_("hf mf staticnested") "`");
return PM3_EOPABORTED;
}
uint64_t key64 = 0;
// check if we can authenticate to sector
if (mfCheckKeys(blockno, keytype, true, 1, key, &key64) != PM3_SUCCESS) {
PrintAndLogEx(WARNING, "Key is wrong. Can't authenticate to block: %3d key type: %c", blockno, (keytype == MF_KEY_B) ? 'B' : 'A');
return PM3_EWRONGANSWER;
}
}
PrintAndLogEx(INFO, "Target block no " _YELLOW_("%3d") ", target key type: " _YELLOW_("%c") ", known target key: " _YELLOW_("%02x%02x%02x%02x%02x%02x%s"),
trg_blockno,
(trg_keytype == MF_KEY_B) ? 'B' : 'A',
trg_key[0], trg_key[1], trg_key[2], trg_key[3], trg_key[4], trg_key[5],
known_target_key ? "" : " (not set)"
);
PrintAndLogEx(INFO, "File action: " _YELLOW_("%s") ", Slow: " _YELLOW_("%s") ", Tests: " _YELLOW_("%d"),
nonce_file_write ? "write" : nonce_file_read ? "read" : "none",
slow ? "Yes" : "No",
tests);
uint64_t foundkey = 0;
int16_t isOK = mfnestedhard(blockno, keytype, key, trg_blockno, trg_keytype, known_target_key ? trg_key : NULL, nonce_file_read, nonce_file_write, slow, tests, &foundkey, filename);
if ((tests == 0) && IfPm3Iso14443a()) {
DropField();
}
if (isOK) {
switch (isOK) {
case PM3_ETIMEOUT :
PrintAndLogEx(ERR, "Error: No response from Proxmark3.\n");
break;
case PM3_EOPABORTED:
PrintAndLogEx(WARNING, "Button pressed. Aborted.\n");
break;
case PM3_ESTATIC_NONCE:
PrintAndLogEx(ERR, "Error: Static encrypted nonce detected. Aborted.\n");
break;
default :
break;
}
}
return isOK;
}
static int CmdHF14AMfAutoPWN(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf mf autopwn",
"This command automates the key recovery process on MIFARE Classic cards.\n"
"It uses the fchk, chk, darkside, nested, hardnested and staticnested to recover keys.\n"
"If all keys are found, it try dumping card content both to file and emulator memory.",
"hf mf autopwn\n"
"hf mf autopwn -s 0 -a -k FFFFFFFFFFFF --> target MFC 1K card, Sector 0 with known key A 'FFFFFFFFFFFF'\n"
"hf mf autopwn --1k -f mfc_default_keys --> target MFC 1K card, default dictionary\n"
"hf mf autopwn --1k -s 0 -a -k FFFFFFFFFFFF -f mfc_default_keys --> combo of the two above samples"
);
void *argtable[] = {
arg_param_begin,
arg_str0("k", "key", "<hex>", "Known key, 12 hex bytes"),
arg_int0("s", "sector", "<dec>", "Input sector number"),
arg_lit0("a", NULL, "Input key A (def)"),
arg_lit0("b", NULL, "Input key B"),
arg_str0("f", "file", "<fn>", "filename of dictionary"),
arg_lit0(NULL, "slow", "Slower acquisition (required by some non standard cards)"),
arg_lit0("l", "legacy", "legacy mode (use the slow `hf mf chk`)"),
arg_lit0("v", "verbose", "verbose output (statistics)"),
arg_lit0(NULL, "mini", "MIFARE Classic Mini / S20"),
arg_lit0(NULL, "1k", "MIFARE Classic 1k / S50 (default)"),
arg_lit0(NULL, "2k", "MIFARE Classic/Plus 2k"),
arg_lit0(NULL, "4k", "MIFARE Classic 4k / S70"),
arg_lit0(NULL, "in", "None (use CPU regular instruction set)"),
#if defined(COMPILER_HAS_SIMD_X86)
arg_lit0(NULL, "im", "MMX"),
arg_lit0(NULL, "is", "SSE2"),
arg_lit0(NULL, "ia", "AVX"),
arg_lit0(NULL, "i2", "AVX2"),
#endif
#if defined(COMPILER_HAS_SIMD_AVX512)
arg_lit0(NULL, "i5", "AVX512"),
#endif
#if defined(COMPILER_HAS_SIMD_NEON)
arg_lit0(NULL, "ie", "NEON"),
#endif
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
int keylen = 0;
uint8_t key[6] = {0};
int32_t res = CLIParamHexToBuf(arg_get_str(ctx, 1), key, sizeof(key), &keylen);
if (res) {
CLIParserFree(ctx);
PrintAndLogEx(FAILED, "Error parsing key bytes");
return PM3_EINVARG;
}
bool known_key = (keylen == 6);
uint8_t sectorno = arg_get_u32_def(ctx, 2, 0);
uint8_t keytype = MF_KEY_A;
if (arg_get_lit(ctx, 3) && arg_get_lit(ctx, 4)) {
CLIParserFree(ctx);
PrintAndLogEx(WARNING, "Input key type must be A or B");
return PM3_EINVARG;
} else if (arg_get_lit(ctx, 4)) {
keytype = MF_KEY_B;
}
int fnlen = 0;
char filename[FILE_PATH_SIZE] = {0};
CLIParamStrToBuf(arg_get_str(ctx, 5), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
bool has_filename = (fnlen > 0);
bool slow = arg_get_lit(ctx, 6);
bool legacy_mfchk = arg_get_lit(ctx, 7);
bool verbose = arg_get_lit(ctx, 8);
bool m0 = arg_get_lit(ctx, 9);
bool m1 = arg_get_lit(ctx, 10);
bool m2 = arg_get_lit(ctx, 11);
bool m4 = arg_get_lit(ctx, 12);
bool in = arg_get_lit(ctx, 13);
#if defined(COMPILER_HAS_SIMD_X86)
bool im = arg_get_lit(ctx, 14);
bool is = arg_get_lit(ctx, 15);
bool ia = arg_get_lit(ctx, 16);
bool i2 = arg_get_lit(ctx, 17);
#endif
#if defined(COMPILER_HAS_SIMD_AVX512)
bool i5 = arg_get_lit(ctx, 18);
#endif
#if defined(COMPILER_HAS_SIMD_NEON)
bool ie = arg_get_lit(ctx, 14);
#endif
CLIParserFree(ctx);
//validations
if ((m0 + m1 + m2 + m4) > 1) {
PrintAndLogEx(WARNING, "Only specify one MIFARE Type");
return PM3_EINVARG;
} else if ((m0 + m1 + m2 + m4) == 0) {
m1 = true;
}
uint8_t sector_cnt = MIFARE_1K_MAXSECTOR;
uint16_t block_cnt = MIFARE_1K_MAXBLOCK;
if (m0) {
sector_cnt = MIFARE_MINI_MAXSECTOR;
block_cnt = MIFARE_MINI_MAXBLOCK;
} else if (m1) {
sector_cnt = MIFARE_1K_MAXSECTOR;
block_cnt = MIFARE_1K_MAXBLOCK;
} else if (m2) {
sector_cnt = MIFARE_2K_MAXSECTOR;
block_cnt = MIFARE_2K_MAXBLOCK;
} else if (m4) {
sector_cnt = MIFARE_4K_MAXSECTOR;
block_cnt = MIFARE_4K_MAXBLOCK;
} else {
PrintAndLogEx(WARNING, "Please specify a MIFARE Type");
return PM3_EINVARG;
}
// set SIM instructions
SetSIMDInstr(SIMD_AUTO);
#if defined(COMPILER_HAS_SIMD_AVX512)
if (i5)
SetSIMDInstr(SIMD_AVX512);
#endif
#if defined(COMPILER_HAS_SIMD_X86)
if (i2)
SetSIMDInstr(SIMD_AVX2);
if (ia)
SetSIMDInstr(SIMD_AVX);
if (is)
SetSIMDInstr(SIMD_SSE2);
if (im)
SetSIMDInstr(SIMD_MMX);
#endif
#if defined(COMPILER_HAS_SIMD_NEON)
if (ie)
SetSIMDInstr(SIMD_NEON);
#endif
if (in)
SetSIMDInstr(SIMD_NONE);
// Nested and Hardnested parameter
uint64_t key64 = 0;
bool calibrate = true;
// Attack key storage variables
uint8_t *keyBlock = NULL;
uint32_t key_cnt = 0;
sector_t *e_sector;
uint8_t tmp_key[6] = {0};
// Nested and Hardnested returned status
uint64_t foundkey = 0;
int isOK = 0;
int current_sector_i = 0, current_key_type_i = 0;
// Dumping and transfere to simulater memory
uint8_t block[16] = {0x00};
int bytes;
// Settings
int prng_type = PM3_EUNDEF;
uint8_t num_found_keys = 0;
// ------------------------------
// Select card to get UID/UIDLEN/ATQA/SAK information
clearCommandBuffer();
SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0);
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) {
PrintAndLogEx(WARNING, "iso14443a card select timeout");
return PM3_ETIMEOUT;
}
uint64_t select_status = resp.oldarg[0];
if (select_status == 0) {
PrintAndLogEx(WARNING, "iso14443a card select failed");
return PM3_ECARDEXCHANGE;
}
// store card info
iso14a_card_select_t card;
memcpy(&card, (iso14a_card_select_t *)resp.data.asBytes, sizeof(iso14a_card_select_t));
// detect MFC EV1 Signature
bool is_ev1 = detect_mfc_ev1_signature();
if (is_ev1) {
// hidden sectors on MFC EV1
sector_cnt += 2;
}
// create/initialize key storage structure
uint32_t e_sector_size = sector_cnt > sectorno ? sector_cnt : sectorno + 1;
res = initSectorTable(&e_sector, e_sector_size);
if (res != e_sector_size) {
free(e_sector);
return PM3_EMALLOC;
}
if (is_ev1) {
PrintAndLogEx(INFO, "MIFARE Classic EV1 card detected");
// Store the keys
e_sector[16].Key[MF_KEY_A] = bytes_to_num((uint8_t *)g_mifare_signature_key_a, sizeof(g_mifare_signature_key_a));
e_sector[16].foundKey[MF_KEY_A] = 'D';
e_sector[17].Key[MF_KEY_A] = bytes_to_num((uint8_t *)g_mifare_signature_key_a, sizeof(g_mifare_signature_key_a));
e_sector[17].foundKey[MF_KEY_A] = 'D';
e_sector[17].Key[MF_KEY_B] = bytes_to_num((uint8_t *)g_mifare_signature_key_b, sizeof(g_mifare_signature_key_b));
e_sector[17].foundKey[MF_KEY_B] = 'D';
// use found key if not supplied
if (known_key == false) {
known_key = true;
sectorno = 17;
keytype = MF_KEY_B;
memcpy(key, g_mifare_signature_key_b, sizeof(g_mifare_signature_key_b));
}
}
// read uid to generate a filename for the key file
char *fptr = GenerateFilename("hf-mf-", "-key.bin");
// check if tag doesn't have static nonce
int has_staticnonce = detect_classic_static_nonce();
// card prng type (weak=1 / hard=0 / select/card comm error = negative value)
if (has_staticnonce == NONCE_NORMAL) {
prng_type = detect_classic_prng();
if (prng_type < 0) {
PrintAndLogEx(FAILED, "\nNo tag detected or other tag communication error (%u)", prng_type);
free(e_sector);
free(fptr);
return PM3_ESOFT;
}
}
// print parameters
if (verbose) {
PrintAndLogEx(INFO, "======================= " _YELLOW_("SETTINGS") " =======================");
PrintAndLogEx(INFO, " card sectors .. " _YELLOW_("%d"), sector_cnt);
PrintAndLogEx(INFO, " key supplied .. " _YELLOW_("%s"), known_key ? "True" : "False");
PrintAndLogEx(INFO, " known sector .. " _YELLOW_("%d"), sectorno);
PrintAndLogEx(INFO, " keytype ....... " _YELLOW_("%c"), (keytype == MF_KEY_B) ? 'B' : 'A');
PrintAndLogEx(INFO, " known key ..... " _YELLOW_("%s"), sprint_hex(key, sizeof(key)));
if (has_staticnonce == NONCE_STATIC)
PrintAndLogEx(INFO, " card PRNG ..... " _YELLOW_("STATIC"));
else if (has_staticnonce == NONCE_NORMAL)
PrintAndLogEx(INFO, " card PRNG ..... " _YELLOW_("%s"), prng_type ? "WEAK" : "HARD");
else
PrintAndLogEx(INFO, " card PRNG ..... " _YELLOW_("Could not determine PRNG,") " " _RED_("read failed."));
PrintAndLogEx(INFO, " dictionary .... " _YELLOW_("%s"), strlen(filename) ? filename : "NONE");
PrintAndLogEx(INFO, " legacy mode ... " _YELLOW_("%s"), legacy_mfchk ? "True" : "False");
PrintAndLogEx(INFO, "========================================================================");
}
// Start the timer
uint64_t t1 = msclock();
// check the user supplied key
if (known_key == false) {
PrintAndLogEx(WARNING, "no known key was supplied, key recovery might fail");
} else {
if (verbose) {
PrintAndLogEx(INFO, "======================= " _YELLOW_("START KNOWN KEY ATTACK") " =======================");
}
if (mfCheckKeys(mfFirstBlockOfSector(sectorno), keytype, true, 1, key, &key64) == PM3_SUCCESS) {
PrintAndLogEx(INFO, "target sector %3u key type %c -- using valid key [ " _GREEN_("%s") "] (used for nested / hardnested attack)",
sectorno,
(keytype == MF_KEY_B) ? 'B' : 'A',
sprint_hex(key, sizeof(key))
);
// Store the key for the nested / hardnested attack (if supplied by the user)
e_sector[sectorno].Key[keytype] = key64;
e_sector[sectorno].foundKey[keytype] = 'U';
++num_found_keys;
} else {
known_key = false;
PrintAndLogEx(FAILED, "Key is wrong. Can't authenticate to sector"_RED_("%3d") " key type "_RED_("%c") " key " _RED_("%s"),
sectorno,
(keytype == MF_KEY_B) ? 'B' : 'A',
sprint_hex(key, sizeof(key))
);
PrintAndLogEx(WARNING, "falling back to dictionary");
}
// Check if the user supplied key is used by other sectors
for (int i = 0; i < sector_cnt; i++) {
for (int j = MF_KEY_A; j <= MF_KEY_B; j++) {
if (e_sector[i].foundKey[j] == 0) {
if (mfCheckKeys(mfFirstBlockOfSector(i), j, true, 1, key, &key64) == PM3_SUCCESS) {
e_sector[i].Key[j] = bytes_to_num(key, 6);
e_sector[i].foundKey[j] = 'U';
// If the user supplied secctor / keytype was wrong --> just be nice and correct it ;)
if (known_key == false) {
num_to_bytes(e_sector[i].Key[j], 6, key);
known_key = true;
sectorno = i;
keytype = j;
PrintAndLogEx(SUCCESS, "target sector %3u key type %c -- found valid key [ " _GREEN_("%s") " ] (used for nested / hardnested attack)",
i,
(j == MF_KEY_B) ? 'B' : 'A',
sprint_hex_inrow(key, sizeof(key))
);
} else {
PrintAndLogEx(SUCCESS, "target sector %3u key type %c -- found valid key [ " _GREEN_("%s") " ]",
i,
(j == MF_KEY_B) ? 'B' : 'A',
sprint_hex_inrow(key, sizeof(key))
);
}
++num_found_keys;
}
}
}
}
if (num_found_keys == sector_cnt * 2) {
goto all_found;
}
}
bool load_success = true;
// Load the dictionary
if (has_filename) {
res = loadFileDICTIONARY_safe(filename, (void **) &keyBlock, 6, &key_cnt);
if (res != PM3_SUCCESS || key_cnt == 0 || keyBlock == NULL) {
PrintAndLogEx(FAILED, "An error occurred while loading the dictionary! (we will use the default keys now)");
if (keyBlock != NULL) {
free(keyBlock);
}
load_success = false;
}
}
if (has_filename == false || load_success == false) {
keyBlock = calloc(ARRAYLEN(g_mifare_default_keys), 6);
if (keyBlock == NULL) {
free(e_sector);
free(fptr);
return PM3_EMALLOC;
}
for (int cnt = 0; cnt < ARRAYLEN(g_mifare_default_keys); cnt++) {
num_to_bytes(g_mifare_default_keys[cnt], 6, keyBlock + cnt * 6);
}
key_cnt = ARRAYLEN(g_mifare_default_keys);
PrintAndLogEx(SUCCESS, "loaded " _GREEN_("%2d") " keys from hardcoded default array", key_cnt);
}
// Use the dictionary to find sector keys on the card
if (verbose) PrintAndLogEx(INFO, "======================= " _YELLOW_("START DICTIONARY ATTACK") " =======================");
if (legacy_mfchk) {
PrintAndLogEx(INFO, "." NOLF);
// Check all the sectors
for (int i = 0; i < sector_cnt; i++) {
for (int j = 0; j < 2; j++) {
// Check if the key is known
if (e_sector[i].foundKey[j] == 0) {
for (uint32_t k = 0; k < key_cnt; k++) {
PrintAndLogEx(NORMAL, "." NOLF);
fflush(stdout);
if (mfCheckKeys(mfFirstBlockOfSector(i), j, true, 1, (keyBlock + (6 * k)), &key64) == PM3_SUCCESS) {
e_sector[i].Key[j] = bytes_to_num((keyBlock + (6 * k)), 6);
e_sector[i].foundKey[j] = 'D';
++num_found_keys;
break;
}
}
}
}
}
PrintAndLogEx(NORMAL, "");
} else {
uint32_t chunksize = key_cnt > (PM3_CMD_DATA_SIZE / 6) ? (PM3_CMD_DATA_SIZE / 6) : key_cnt;
bool firstChunk = true, lastChunk = false;
for (uint8_t strategy = 1; strategy < 3; strategy++) {
PrintAndLogEx(INFO, "running strategy %u", strategy);
// main keychunk loop
for (uint32_t i = 0; i < key_cnt; i += chunksize) {
if (kbd_enter_pressed()) {
PrintAndLogEx(WARNING, "\naborted via keyboard!\n");
i = key_cnt;
strategy = 3;
break; // Exit the loop
}
uint32_t size = ((key_cnt - i) > chunksize) ? chunksize : key_cnt - i;
// last chunk?
if (size == key_cnt - i)
lastChunk = true;
res = mfCheckKeys_fast(sector_cnt, firstChunk, lastChunk, strategy, size, keyBlock + (i * 6), e_sector, false);
if (firstChunk)
firstChunk = false;
// all keys, aborted
if (res == PM3_SUCCESS) {
i = key_cnt;
strategy = 3;
break; // Exit the loop
}
} // end chunks of keys
firstChunk = true;
lastChunk = false;
} // end strategy
}
// Analyse the dictionary attack
for (int i = 0; i < sector_cnt; i++) {
for (int j = MF_KEY_A; j <= MF_KEY_B; j++) {
if (e_sector[i].foundKey[j] == 1) {
e_sector[i].foundKey[j] = 'D';
num_to_bytes(e_sector[i].Key[j], 6, tmp_key);
// Store valid credentials for the nested / hardnested attack if none exist
if (known_key == false) {
num_to_bytes(e_sector[i].Key[j], 6, key);
known_key = true;
sectorno = i;
keytype = j;
PrintAndLogEx(SUCCESS, "target sector %3u key type %c -- found valid key [ " _GREEN_("%s") " ] (used for nested / hardnested attack)",
i,
(j == MF_KEY_B) ? 'B' : 'A',
sprint_hex_inrow(tmp_key, sizeof(tmp_key))
);
} else {
PrintAndLogEx(SUCCESS, "target sector %3u key type %c -- found valid key [ " _GREEN_("%s") " ]",
i,
(j == MF_KEY_B) ? 'B' : 'A',
sprint_hex_inrow(tmp_key, sizeof(tmp_key))
);
}
}
}
}
// Check if at least one sector key was found
if (known_key == false) {
// Check if the darkside attack can be used
if (prng_type && has_staticnonce != NONCE_STATIC) {
if (verbose) {
PrintAndLogEx(INFO, "======================= " _YELLOW_("START DARKSIDE ATTACK") " =======================");
}
isOK = mfDarkside(mfFirstBlockOfSector(sectorno), keytype + 0x60, &key64);
switch (isOK) {
case PM3_EOPABORTED :
PrintAndLogEx(WARNING, "\nButton pressed or aborted via keyboard");
goto noValidKeyFound;
case -2 :
PrintAndLogEx(FAILED, "\nCard is not vulnerable to Darkside attack (doesn't send NACK on authentication requests).");
goto noValidKeyFound;
case -3 :
PrintAndLogEx(FAILED, "\nCard is not vulnerable to Darkside attack (its random number generator is not predictable).");
goto noValidKeyFound;
case -4 :
PrintAndLogEx(FAILED, "\nCard is not vulnerable to Darkside attack (its random number generator seems to be based on the wellknown");
PrintAndLogEx(FAILED, "generating polynomial with 16 effective bits only, but shows unexpected behaviour.");
goto noValidKeyFound;
default :
PrintAndLogEx(SUCCESS, "\nFound valid key [ " _GREEN_("%012" PRIx64) " ]\n", key64);
break;
}
// Store the keys
num_to_bytes(key64, 6, key);
e_sector[sectorno].Key[keytype] = key64;
e_sector[sectorno].foundKey[keytype] = 'S';
PrintAndLogEx(SUCCESS, "target sector %3u key type %c -- found valid key [ " _GREEN_("%012" PRIx64) " ] (used for nested / hardnested attack)",
sectorno,
(keytype == MF_KEY_B) ? 'B' : 'A',
key64
);
} else {
noValidKeyFound:
PrintAndLogEx(FAILED, "No usable key was found!");
free(keyBlock);
free(e_sector);
free(fptr);
return PM3_ESOFT;
}
}
free(keyBlock);
// Clear the needed variables
num_to_bytes(0, 6, tmp_key);
bool nested_failed = false;
// Iterate over each sector and key(A/B)
for (current_sector_i = 0; current_sector_i < sector_cnt; current_sector_i++) {
for (current_key_type_i = 0; current_key_type_i < 2; current_key_type_i++) {
// If the key is already known, just skip it
if (e_sector[current_sector_i].foundKey[current_key_type_i] == 0) {
if (has_staticnonce == NONCE_STATIC)
goto tryStaticnested;
// Try the found keys are reused
if (bytes_to_num(tmp_key, 6) != 0) {
// <!> The fast check --> mfCheckKeys_fast(sector_cnt, true, true, 2, 1, tmp_key, e_sector, false);
// <!> Returns false keys, so we just stick to the slower mfchk.
for (int i = 0; i < sector_cnt; i++) {
for (int j = MF_KEY_A; j <= MF_KEY_B; j++) {
// Check if the sector key is already broken
if (e_sector[i].foundKey[j])
continue;
// Check if the key works
if (mfCheckKeys(mfFirstBlockOfSector(i), j, true, 1, tmp_key, &key64) == PM3_SUCCESS) {
e_sector[i].Key[j] = bytes_to_num(tmp_key, 6);
e_sector[i].foundKey[j] = 'R';
PrintAndLogEx(SUCCESS, "target sector %3u key type %c -- found valid key [ " _GREEN_("%s") " ]",
i,
(j == MF_KEY_B) ? 'B' : 'A',
sprint_hex_inrow(tmp_key, sizeof(tmp_key))
);
}
}
}
}
// Clear the last found key
num_to_bytes(0, 6, tmp_key);
if (current_key_type_i == MF_KEY_B) {
if (e_sector[current_sector_i].foundKey[0] && !e_sector[current_sector_i].foundKey[1]) {
if (verbose) {
PrintAndLogEx(INFO, "======================= " _YELLOW_("START READ B KEY ATTACK") " =======================");
PrintAndLogEx(INFO, "reading B key of sector %3d with key type %c",
current_sector_i,
(current_key_type_i == MF_KEY_B) ? 'B' : 'A');
}
uint8_t sectrail = (mfFirstBlockOfSector(current_sector_i) + mfNumBlocksPerSector(current_sector_i) - 1);
mf_readblock_t payload;
payload.blockno = sectrail;
payload.keytype = MF_KEY_A;
num_to_bytes(e_sector[current_sector_i].Key[0], 6, payload.key); // KEY A
clearCommandBuffer();
SendCommandNG(CMD_HF_MIFARE_READBL, (uint8_t *)&payload, sizeof(mf_readblock_t));
if (WaitForResponseTimeout(CMD_HF_MIFARE_READBL, &resp, 1500) == false) goto skipReadBKey;
if (resp.status != PM3_SUCCESS) goto skipReadBKey;
uint8_t *data = resp.data.asBytes;
key64 = bytes_to_num(data + 10, 6);
if (key64) {
e_sector[current_sector_i].foundKey[current_key_type_i] = 'A';
e_sector[current_sector_i].Key[current_key_type_i] = key64;
num_to_bytes(key64, 6, tmp_key);
PrintAndLogEx(SUCCESS, "target sector %3u key type %c -- found valid key [ " _GREEN_("%s") " ]",
current_sector_i,
(current_key_type_i == MF_KEY_B) ? 'B' : 'A',
sprint_hex_inrow(tmp_key, sizeof(tmp_key))
);
} else {
if (verbose) {
PrintAndLogEx(WARNING, "unknown B key: sector: %3d key type: %c",
current_sector_i,
(current_key_type_i == MF_KEY_B) ? 'B' : 'A'
);
PrintAndLogEx(INFO, " -- reading the B key was not possible, maybe due to access rights?");
}
}
}
}
// Use the nested / hardnested attack
skipReadBKey:
if (e_sector[current_sector_i].foundKey[current_key_type_i] == 0) {
if (has_staticnonce == NONCE_STATIC)
goto tryStaticnested;
if (prng_type && (nested_failed == false)) {
uint8_t retries = 0;
if (verbose) {
PrintAndLogEx(INFO, "======================= " _YELLOW_("START NESTED ATTACK") " =======================");
PrintAndLogEx(INFO, "sector no %3d, target key type %c",
current_sector_i,
(current_key_type_i == MF_KEY_B) ? 'B' : 'A');
}
tryNested:
isOK = mfnested(mfFirstBlockOfSector(sectorno), keytype, key, mfFirstBlockOfSector(current_sector_i), current_key_type_i, tmp_key, calibrate);
switch (isOK) {
case PM3_ETIMEOUT: {
PrintAndLogEx(ERR, "\nError: No response from Proxmark3.");
free(e_sector);
free(fptr);
return isOK;
}
case PM3_EOPABORTED: {
PrintAndLogEx(WARNING, "\nButton pressed. Aborted.");
free(e_sector);
free(fptr);
return isOK;
}
case PM3_EFAILED: {
PrintAndLogEx(FAILED, "Tag isn't vulnerable to Nested Attack (PRNG is probably not predictable).");
PrintAndLogEx(FAILED, "Nested attack failed --> try hardnested");
goto tryHardnested;
}
case PM3_ESOFT: {
// key not found
calibrate = false;
// this can happen on some old cards, it's worth trying some more before switching to slower hardnested
if (retries++ < MIFARE_SECTOR_RETRY) {
PrintAndLogEx(FAILED, "Nested attack failed, trying again (%i/%i)", retries, MIFARE_SECTOR_RETRY);
goto tryNested;
} else {
PrintAndLogEx(FAILED, "Nested attack failed, moving to hardnested");
nested_failed = true;
goto tryHardnested;
}
break;
}
case PM3_SUCCESS: {
calibrate = false;
e_sector[current_sector_i].Key[current_key_type_i] = bytes_to_num(tmp_key, 6);
e_sector[current_sector_i].foundKey[current_key_type_i] = 'N';
break;
}
default: {
PrintAndLogEx(ERR, "unknown Error.\n");
free(e_sector);
free(fptr);
return isOK;
}
}
} else {
tryHardnested: // If the nested attack fails then we try the hardnested attack
if (verbose) {
PrintAndLogEx(INFO, "======================= " _YELLOW_("START HARDNESTED ATTACK") " =======================");
PrintAndLogEx(INFO, "sector no %3d, target key type %c, Slow %s",
current_sector_i,
(current_key_type_i == MF_KEY_B) ? 'B' : 'A',
slow ? "Yes" : "No");
}
isOK = mfnestedhard(mfFirstBlockOfSector(sectorno), keytype, key, mfFirstBlockOfSector(current_sector_i), current_key_type_i, NULL, false, false, slow, 0, &foundkey, NULL);
DropField();
if (isOK) {
switch (isOK) {
case PM3_ETIMEOUT: {
PrintAndLogEx(ERR, "\nError: No response from Proxmark3");
break;
}
case PM3_EOPABORTED: {
PrintAndLogEx(NORMAL, "\nButton pressed, user aborted");
break;
}
case PM3_ESTATIC_NONCE: {
PrintAndLogEx(ERR, "\nError: Static encrypted nonce detected. Aborted.\n");
break;
}
default: {
break;
}
}
free(e_sector);
free(fptr);
return PM3_ESOFT;
}
// Copy the found key to the tmp_key variale (for the following print statement, and the mfCheckKeys above)
num_to_bytes(foundkey, 6, tmp_key);
e_sector[current_sector_i].Key[current_key_type_i] = foundkey;
e_sector[current_sector_i].foundKey[current_key_type_i] = 'H';
}
if (has_staticnonce == NONCE_STATIC) {
tryStaticnested:
if (verbose) {
PrintAndLogEx(INFO, "======================= " _YELLOW_("START STATIC NESTED ATTACK") " =======================");
PrintAndLogEx(INFO, "sector no %3d, target key type %c",
current_sector_i,
(current_key_type_i == MF_KEY_B) ? 'B' : 'A');
}
isOK = mfStaticNested(sectorno, keytype, key, mfFirstBlockOfSector(current_sector_i), current_key_type_i, tmp_key);
DropField();
switch (isOK) {
case PM3_ETIMEOUT: {
PrintAndLogEx(ERR, "\nError: No response from Proxmark3");
free(e_sector);
free(fptr);
return isOK;
}
case PM3_EOPABORTED: {
PrintAndLogEx(WARNING, "\nButton pressed, user aborted");
free(e_sector);
free(fptr);
return isOK;
}
case PM3_SUCCESS: {
e_sector[current_sector_i].Key[current_key_type_i] = bytes_to_num(tmp_key, 6);
e_sector[current_sector_i].foundKey[current_key_type_i] = 'C';
break;
}
default: {
break;
}
}
}
// Check if the key was found
if (e_sector[current_sector_i].foundKey[current_key_type_i]) {
PrintAndLogEx(SUCCESS, "target sector %3u key type %c -- found valid key [ " _GREEN_("%s") " ]",
current_sector_i,
(current_key_type_i == MF_KEY_B) ? 'B' : 'A',
sprint_hex_inrow(tmp_key, sizeof(tmp_key))
);
}
}
}
}
}
all_found:
// Show the results to the user
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(SUCCESS, _GREEN_("found keys:"));
printKeyTable(sector_cnt, e_sector);
// Dump the keys
PrintAndLogEx(NORMAL, "");
if (createMfcKeyDump(fptr, sector_cnt, e_sector) != PM3_SUCCESS) {
PrintAndLogEx(ERR, "Failed to save keys to file");
}
// clear emulator mem
clearCommandBuffer();
SendCommandNG(CMD_HF_MIFARE_EML_MEMCLR, NULL, 0);
PrintAndLogEx(SUCCESS, "transferring keys to simulator memory (Cmd Error: 04 can occur)");
for (current_sector_i = 0; current_sector_i < sector_cnt; current_sector_i++) {
mfEmlGetMem(block, current_sector_i, 1);
if (e_sector[current_sector_i].foundKey[0])
num_to_bytes(e_sector[current_sector_i].Key[0], 6, block);
if (e_sector[current_sector_i].foundKey[1])
num_to_bytes(e_sector[current_sector_i].Key[1], 6, block + 10);
mfEmlSetMem(block, mfFirstBlockOfSector(current_sector_i) + mfNumBlocksPerSector(current_sector_i) - 1, 1);
}
// use ecfill trick
FastDumpWithEcFill(sector_cnt);
bytes = block_cnt * MFBLOCK_SIZE;
uint8_t *dump = calloc(bytes, sizeof(uint8_t));
if (dump == NULL) {
PrintAndLogEx(ERR, "Fail, cannot allocate memory");
free(e_sector);
free(fptr);
return PM3_EMALLOC;
}
PrintAndLogEx(INFO, "downloading the card content from emulator memory");
if (!GetFromDevice(BIG_BUF_EML, dump, bytes, 0, NULL, 0, NULL, 2500, false)) {
PrintAndLogEx(ERR, "Fail, transfer from device time-out");
free(e_sector);
free(dump);
free(fptr);
return PM3_ETIMEOUT;
}
free(fptr);
fptr = GenerateFilename("hf-mf-", "-dump");
if (fptr == NULL) {
free(dump);
free(e_sector);
free(fptr);
return PM3_ESOFT;
}
strcpy(filename, fptr);
free(fptr);
saveFile(filename, ".bin", dump, bytes);
saveFileEML(filename, dump, bytes, MFBLOCK_SIZE);
iso14a_mf_extdump_t xdump;
xdump.card_info = card;
xdump.dump = dump;
xdump.dumplen = bytes;
saveFileJSON(filename, jsfCardMemory, (uint8_t *)&xdump, sizeof(xdump), NULL);
// Generate and show statistics
t1 = msclock() - t1;
PrintAndLogEx(INFO, "autopwn execution time: " _YELLOW_("%.0f") " seconds", (float)t1 / 1000.0);
free(dump);
free(e_sector);
return PM3_SUCCESS;
}
static int mfLoadKeys(uint8_t **pkeyBlock, uint32_t *pkeycnt, uint8_t *userkey, int userkeylen, const char *filename, int fnlen) {
// Handle Keys
*pkeycnt = 0;
*pkeyBlock = NULL;
uint8_t *p;
// Handle user supplied key
// (it considers *pkeycnt and *pkeyBlock as possibly non-null so logic can be easily reordered)
if (userkeylen >= 6) {
int numKeys = userkeylen / 6;
p = realloc(*pkeyBlock, (*pkeycnt + numKeys) * 6);
if (!p) {
PrintAndLogEx(FAILED, "cannot allocate memory for Keys");
free(*pkeyBlock);
return PM3_EMALLOC;
}
*pkeyBlock = p;
memcpy(*pkeyBlock + *pkeycnt * 6, userkey, numKeys * 6);
for (int i = 0; i < numKeys; i++) {
PrintAndLogEx(INFO, "[%2d] key %s", *pkeycnt + i, sprint_hex(*pkeyBlock + (*pkeycnt + i) * 6, 6));
}
*pkeycnt += numKeys;
}
// Handle default keys
p = realloc(*pkeyBlock, (*pkeycnt + ARRAYLEN(g_mifare_default_keys)) * 6);
if (!p) {
PrintAndLogEx(FAILED, "cannot allocate memory for Keys");
free(*pkeyBlock);
return PM3_EMALLOC;
}
*pkeyBlock = p;
// Copy default keys to list
for (int i = 0; i < ARRAYLEN(g_mifare_default_keys); i++) {
num_to_bytes(g_mifare_default_keys[i], 6, (uint8_t *)(*pkeyBlock + (*pkeycnt + i) * 6));
PrintAndLogEx(DEBUG, "[%2d] key %s", *pkeycnt + i, sprint_hex(*pkeyBlock + (*pkeycnt + i) * 6, 6));
}
*pkeycnt += ARRAYLEN(g_mifare_default_keys);
// Handle user supplied dictionary file
if (fnlen > 0) {
uint32_t loaded_numKeys = 0;
uint8_t *keyBlock_tmp = NULL;
int res = loadFileDICTIONARY_safe(filename, (void **) &keyBlock_tmp, 6, &loaded_numKeys);
if (res != PM3_SUCCESS || loaded_numKeys == 0 || *pkeyBlock == NULL) {
PrintAndLogEx(FAILED, "An error occurred while loading the dictionary!");
free(keyBlock_tmp);
free(*pkeyBlock);
return PM3_EFILE;
} else {
p = realloc(*pkeyBlock, (*pkeycnt + loaded_numKeys) * 6);
if (!p) {
PrintAndLogEx(FAILED, "cannot allocate memory for Keys");
free(keyBlock_tmp);
free(*pkeyBlock);
return PM3_EMALLOC;
}
*pkeyBlock = p;
memcpy(*pkeyBlock + *pkeycnt * 6, keyBlock_tmp, loaded_numKeys * 6);
*pkeycnt += loaded_numKeys;
free(keyBlock_tmp);
}
}
return PM3_SUCCESS;
}
static int CmdHF14AMfChk_fast(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf mf fchk",
"This is a improved checkkeys method speedwise. It checks MIFARE Classic tags sector keys against a dictionary file with keys",
"hf mf fchk --mini -k FFFFFFFFFFFF --> Key recovery against MIFARE Mini\n"
"hf mf fchk --1k -k FFFFFFFFFFFF --> Key recovery against MIFARE Classic 1k\n"
"hf mf fchk --2k -k FFFFFFFFFFFF --> Key recovery against MIFARE 2k\n"
"hf mf fchk --4k -k FFFFFFFFFFFF --> Key recovery against MIFARE 4k\n"
"hf mf fchk --1k -f mfc_default_keys.dic --> Target 1K using default dictionary file\n"
"hf mf fchk --1k --emu --> Target 1K, write keys to emulator memory\n"
"hf mf fchk --1k --dump --> Target 1K, write keys to file\n"
"hf mf fchk --1k --mem --> Target 1K, use dictionary from flash memory");
void *argtable[] = {
arg_param_begin,
arg_strx0("k", "key", "<hex>", "Key specified as 12 hex symbols"),
arg_lit0(NULL, "mini", "MIFARE Classic Mini / S20"),
arg_lit0(NULL, "1k", "MIFARE Classic 1k / S50 (default)"),
arg_lit0(NULL, "2k", "MIFARE Classic/Plus 2k"),
arg_lit0(NULL, "4k", "MIFARE Classic 4k / S70"),
arg_lit0(NULL, "emu", "Fill simulator keys from found keys"),
arg_lit0(NULL, "dump", "Dump found keys to binary file"),
arg_lit0(NULL, "mem", "Use dictionary from flashmemory"),
arg_str0("f", "file", "<fn>", "filename of dictionary"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
int keylen = 0;
uint8_t key[255 * 6] = {0};
CLIGetHexWithReturn(ctx, 1, key, &keylen);
bool m0 = arg_get_lit(ctx, 2);
bool m1 = arg_get_lit(ctx, 3);
bool m2 = arg_get_lit(ctx, 4);
bool m4 = arg_get_lit(ctx, 5);
bool transferToEml = arg_get_lit(ctx, 6);
bool createDumpFile = arg_get_lit(ctx, 7);
bool use_flashmemory = arg_get_lit(ctx, 8);
int fnlen = 0;
char filename[FILE_PATH_SIZE] = {0};
CLIParamStrToBuf(arg_get_str(ctx, 9), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
CLIParserFree(ctx);
//validations
if ((m0 + m1 + m2 + m4) > 1) {
PrintAndLogEx(WARNING, "Only specify one MIFARE Type");
return PM3_EINVARG;
} else if ((m0 + m1 + m2 + m4) == 0) {
m1 = true;
}
uint8_t sectorsCnt = 1;
if (m0) {
sectorsCnt = MIFARE_MINI_MAXSECTOR;
} else if (m1) {
sectorsCnt = MIFARE_1K_MAXSECTOR;
} else if (m2) {
sectorsCnt = MIFARE_2K_MAXSECTOR;
} else if (m4) {
sectorsCnt = MIFARE_4K_MAXSECTOR;
} else {
PrintAndLogEx(WARNING, "Please specify a MIFARE Type");
return PM3_EINVARG;
}
uint8_t *keyBlock = NULL;
uint32_t keycnt = 0;
int ret = mfLoadKeys(&keyBlock, &keycnt, key, keylen, filename, fnlen);
if (ret != PM3_SUCCESS) {
return ret;
}
// create/initialize key storage structure
sector_t *e_sector = NULL;
int32_t res = initSectorTable(&e_sector, sectorsCnt);
if (res != sectorsCnt) {
free(keyBlock);
return PM3_EMALLOC;
}
uint32_t chunksize = keycnt > (PM3_CMD_DATA_SIZE / 6) ? (PM3_CMD_DATA_SIZE / 6) : keycnt;
bool firstChunk = true, lastChunk = false;
int i = 0;
// time
uint64_t t1 = msclock();
if (use_flashmemory) {
PrintAndLogEx(SUCCESS, "Using dictionary in flash memory");
mfCheckKeys_fast(sectorsCnt, true, true, 1, 0, keyBlock, e_sector, use_flashmemory);
} else {
// strategys. 1= deep first on sector 0 AB, 2= width first on all sectors
for (uint8_t strategy = 1; strategy < 3; strategy++) {
PrintAndLogEx(INFO, "Running strategy %u", strategy);
// main keychunk loop
for (i = 0; i < keycnt; i += chunksize) {
if (kbd_enter_pressed()) {
PrintAndLogEx(WARNING, "\naborted via keyboard!\n");
goto out;
}
uint32_t size = ((keycnt - i) > chunksize) ? chunksize : keycnt - i;
// last chunk?
if (size == keycnt - i)
lastChunk = true;
res = mfCheckKeys_fast(sectorsCnt, firstChunk, lastChunk, strategy, size, keyBlock + (i * 6), e_sector, false);
if (firstChunk)
firstChunk = false;
// all keys, aborted
if (res == PM3_SUCCESS || res == 2)
goto out;
} // end chunks of keys
firstChunk = true;
lastChunk = false;
} // end strategy
}
out:
t1 = msclock() - t1;
PrintAndLogEx(INFO, "time in checkkeys (fast) " _YELLOW_("%.1fs") "\n", (float)(t1 / 1000.0));
// check..
uint8_t found_keys = 0;
for (i = 0; i < sectorsCnt; ++i) {
if (e_sector[i].foundKey[0])
found_keys++;
if (e_sector[i].foundKey[1])
found_keys++;
}
if (found_keys == 0) {
PrintAndLogEx(WARNING, "No keys found");
} else {
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(SUCCESS, _GREEN_("found keys:"));
printKeyTable(sectorsCnt, e_sector);
if (use_flashmemory && found_keys == (sectorsCnt << 1)) {
PrintAndLogEx(SUCCESS, "Card dumped as well. run " _YELLOW_("`%s %c`"),
"hf mf esave",
GetFormatFromSector(sectorsCnt)
);
}
if (transferToEml) {
// fast push mode
g_conn.block_after_ACK = true;
uint8_t block[16] = {0x00};
for (i = 0; i < sectorsCnt; ++i) {
uint8_t b = mfFirstBlockOfSector(i) + mfNumBlocksPerSector(i) - 1;
mfEmlGetMem(block, b, 1);
if (e_sector[i].foundKey[0])
num_to_bytes(e_sector[i].Key[0], 6, block);
if (e_sector[i].foundKey[1])
num_to_bytes(e_sector[i].Key[1], 6, block + 10);
if (i == sectorsCnt - 1) {
// Disable fast mode on last packet
g_conn.block_after_ACK = false;
}
mfEmlSetMem(block, b, 1);
}
PrintAndLogEx(SUCCESS, "Found keys have been transferred to the emulator memory");
if (found_keys == (sectorsCnt << 1)) {
FastDumpWithEcFill(sectorsCnt);
}
}
if (createDumpFile) {
char *fptr = GenerateFilename("hf-mf-", "-key.bin");
if (createMfcKeyDump(fptr, sectorsCnt, e_sector) != PM3_SUCCESS) {
PrintAndLogEx(ERR, "Failed to save keys to file");
}
free(fptr);
}
}
free(keyBlock);
free(e_sector);
PrintAndLogEx(NORMAL, "");
return PM3_SUCCESS;
}
static int CmdHF14AMfChk(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf mf chk",
"Check keys on MIFARE Classic card",
"hf mf chk --mini -k FFFFFFFFFFFF --> Check all sectors, all keys against MIFARE Mini\n"
"hf mf chk --1k -k FFFFFFFFFFFF --> Check all sectors, all keys against MIFARE Classic 1k\n"
"hf mf chk --2k -k FFFFFFFFFFFF --> Check all sectors, all keys against MIFARE 2k\n"
"hf mf chk --4k -k FFFFFFFFFFFF --> Check all sectors, all keys against MIFARE 4k\n"
"hf mf chk --1k --emu --> Check all sectors, all keys, 1K, and write to emulator memory\n"
"hf mf chk --1k --dump --> Check all sectors, all keys, 1K, and write to file\n"
"hf mf chk -a --tblk 0 -f mfc_default_keys.dic --> Check dictionary against block 0, key A");
void *argtable[] = {
arg_param_begin,
arg_strx0("k", "key", "<hex>", "Key specified as 12 hex symbols"),
arg_int0(NULL, "tblk", "<dec>", "Target block number"),
arg_lit0("a", NULL, "Target Key A"),
arg_lit0("b", NULL, "Target Key B"),
arg_lit0("*", "all", "Target both key A & B (default)"),
arg_lit0(NULL, "mini", "MIFARE Classic Mini / S20"),
arg_lit0(NULL, "1k", "MIFARE Classic 1k / S50 (default)"),
arg_lit0(NULL, "2k", "MIFARE Classic/Plus 2k"),
arg_lit0(NULL, "4k", "MIFARE Classic 4k / S70"),
arg_lit0(NULL, "emu", "Fill simulator keys from found keys"),
arg_lit0(NULL, "dump", "Dump found keys to binary file"),
arg_str0("f", "file", "<fn>", "Filename of dictionary"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
int keylen = 0;
uint8_t key[255 * 6] = {0};
CLIGetHexWithReturn(ctx, 1, key, &keylen);
int blockNo = arg_get_int_def(ctx, 2, -1);
uint8_t keyType = 2;
if ((arg_get_lit(ctx, 3) && arg_get_lit(ctx, 4)) || arg_get_lit(ctx, 5)) {
keyType = 2;
} else if (arg_get_lit(ctx, 3)) {
keyType = MF_KEY_A;
} else if (arg_get_lit(ctx, 4)) {
keyType = MF_KEY_B;
}
bool m0 = arg_get_lit(ctx, 6);
bool m1 = arg_get_lit(ctx, 7);
bool m2 = arg_get_lit(ctx, 8);
bool m4 = arg_get_lit(ctx, 9);
bool transferToEml = arg_get_lit(ctx, 10);
bool createDumpFile = arg_get_lit(ctx, 11);
int fnlen = 0;
char filename[FILE_PATH_SIZE] = {0};
CLIParamStrToBuf(arg_get_str(ctx, 12), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
CLIParserFree(ctx);
bool singleSector = blockNo > -1;
if (! singleSector) {
// start from first trailer block
blockNo = 3;
}
//validations
if ((m0 + m1 + m2 + m4) > 1) {
PrintAndLogEx(WARNING, "Only specify one MIFARE Type");
return PM3_EINVARG;
}
uint8_t SectorsCnt = 1;
if (m0) {
SectorsCnt = MIFARE_MINI_MAXSECTOR;
} else if (m1) {
SectorsCnt = MIFARE_1K_MAXSECTOR;
} else if (m2) {
SectorsCnt = MIFARE_2K_MAXSECTOR;
} else if (m4) {
SectorsCnt = MIFARE_4K_MAXSECTOR;
}
if (singleSector) {
uint8_t MinSectorsCnt = 0;
// find a MIFARE type that can accommodate the provided block number
uint8_t s = mfSectorNum(blockNo);
if (s < MIFARE_MINI_MAXSECTOR) {
MinSectorsCnt = MIFARE_MINI_MAXSECTOR;
} else if (s < MIFARE_1K_MAXSECTOR) {
MinSectorsCnt = MIFARE_1K_MAXSECTOR;
} else if (s < MIFARE_2K_MAXSECTOR) {
MinSectorsCnt = MIFARE_2K_MAXSECTOR;
} else if (s < MIFARE_4K_MAXSECTOR) {
MinSectorsCnt = MIFARE_4K_MAXSECTOR;
} else {
PrintAndLogEx(WARNING, "Provided block out of possible MIFARE Type memory map");
return PM3_EINVARG;
}
if (SectorsCnt == 1) {
SectorsCnt = MinSectorsCnt;
} else if (SectorsCnt < MinSectorsCnt) {
PrintAndLogEx(WARNING, "Provided block out of provided MIFARE Type memory map");
return PM3_EINVARG;
}
}
if (SectorsCnt == 1) {
SectorsCnt = MIFARE_1K_MAXSECTOR;
}
uint8_t *keyBlock = NULL;
uint32_t keycnt = 0;
int ret = mfLoadKeys(&keyBlock, &keycnt, key, keylen, filename, fnlen);
if (ret != PM3_SUCCESS) {
return ret;
}
uint64_t key64 = 0;
// create/initialize key storage structure
sector_t *e_sector = NULL;
int32_t res = initSectorTable(&e_sector, SectorsCnt);
if (res != SectorsCnt) {
free(keyBlock);
return PM3_EMALLOC;
}
uint8_t trgKeyType = MF_KEY_A;
uint16_t max_keys = keycnt > KEYS_IN_BLOCK ? KEYS_IN_BLOCK : keycnt;
PrintAndLogEx(INFO, "Start check for keys...");
PrintAndLogEx(INFO, "." NOLF);
// fast push mode
g_conn.block_after_ACK = true;
// clear trace log by first check keys call only
bool clearLog = true;
// time
uint64_t t1 = msclock();
// check keys.
for (trgKeyType = (keyType == 2) ? 0 : keyType; trgKeyType < 2; (keyType == 2) ? (++trgKeyType) : (trgKeyType = 2)) {
// loop sectors but block is used as to keep track of from which blocks to test
int b = blockNo;
for (int i = mfSectorNum(b); i < SectorsCnt; ++i) {
// skip already found keys.
if (e_sector[i].foundKey[trgKeyType]) continue;
for (uint32_t c = 0; c < keycnt; c += max_keys) {
PrintAndLogEx(NORMAL, "." NOLF);
fflush(stdout);
if (kbd_enter_pressed()) {
PrintAndLogEx(WARNING, "\naborted via keyboard!\n");
goto out;
}
uint32_t size = keycnt - c > max_keys ? max_keys : keycnt - c;
if (mfCheckKeys(b, trgKeyType, clearLog, size, &keyBlock[6 * c], &key64) == PM3_SUCCESS) {
e_sector[i].Key[trgKeyType] = key64;
e_sector[i].foundKey[trgKeyType] = true;
clearLog = false;
break;
}
clearLog = false;
}
if (singleSector)
break;
b < 127 ? (b += 4) : (b += 16);
}
}
t1 = msclock() - t1;
PrintAndLogEx(INFO, "\ntime in checkkeys " _YELLOW_("%.0f") " seconds\n", (float)t1 / 1000.0);
// 20160116 If Sector A is found, but not Sector B, try just reading it of the tag?
if (keyType != MF_KEY_B) {
PrintAndLogEx(INFO, "testing to read key B...");
// loop sectors but block is used as to keep track of from which blocks to test
int b = blockNo;
for (int i = mfSectorNum(b); i < SectorsCnt; i++) {
// KEY A but not KEY B
if (e_sector[i].foundKey[0] && !e_sector[i].foundKey[1]) {
uint8_t sectrail = mfSectorTrailerOfSector(i);
PrintAndLogEx(INFO, "Sector: %u, First block: %u, Last block: %u, Num of blocks: %u", i, mfFirstBlockOfSector(i), sectrail, mfNumBlocksPerSector(i));
PrintAndLogEx(INFO, "Reading sector trailer");
mf_readblock_t payload;
payload.blockno = sectrail;
payload.keytype = MF_KEY_A;
// Use key A
num_to_bytes(e_sector[i].Key[0], 6, payload.key);
clearCommandBuffer();
SendCommandNG(CMD_HF_MIFARE_READBL, (uint8_t *)&payload, sizeof(mf_readblock_t));
PacketResponseNG resp;
if (!WaitForResponseTimeout(CMD_HF_MIFARE_READBL, &resp, 1500)) continue;
if (resp.status != PM3_SUCCESS) continue;
uint8_t *data = resp.data.asBytes;
key64 = bytes_to_num(data + 10, 6);
if (key64) {
PrintAndLogEx(NORMAL, "Data:%s", sprint_hex(data + 10, 6));
e_sector[i].foundKey[1] = 1;
e_sector[i].Key[1] = key64;
}
}
if (singleSector)
break;
b < 127 ? (b += 4) : (b += 16);
}
}
out:
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(SUCCESS, _GREEN_("found keys:"));
//print keys
if (singleSector)
printKeyTableEx(1, e_sector, mfSectorNum(blockNo));
else
printKeyTable(SectorsCnt, e_sector);
if (transferToEml) {
// fast push mode
g_conn.block_after_ACK = true;
uint8_t block[16] = {0x00};
for (int i = 0; i < SectorsCnt; ++i) {
uint8_t blockno = mfFirstBlockOfSector(i) + mfNumBlocksPerSector(i) - 1;
mfEmlGetMem(block, blockno, 1);
if (e_sector[i].foundKey[0])
num_to_bytes(e_sector[i].Key[0], 6, block);
if (e_sector[i].foundKey[1])
num_to_bytes(e_sector[i].Key[1], 6, block + 10);
if (i == SectorsCnt - 1) {
// Disable fast mode on last packet
g_conn.block_after_ACK = false;
}
mfEmlSetMem(block, blockno, 1);
}
PrintAndLogEx(SUCCESS, "Found keys have been transferred to the emulator memory");
}
if (createDumpFile) {
char *fptr = GenerateFilename("hf-mf-", "-key.bin");
if (createMfcKeyDump(fptr, SectorsCnt, e_sector) != PM3_SUCCESS) {
PrintAndLogEx(ERR, "Failed to save keys to file");
}
free(fptr);
}
free(keyBlock);
free(e_sector);
// Disable fast mode and send a dummy command to make it effective
g_conn.block_after_ACK = false;
SendCommandNG(CMD_PING, NULL, 0);
if (!WaitForResponseTimeout(CMD_PING, NULL, 1000)) {
PrintAndLogEx(WARNING, "command execution time out");
return PM3_ETIMEOUT;
}
PrintAndLogEx(NORMAL, "");
return PM3_SUCCESS;
}
void showSectorTable(sector_t *k_sector, uint8_t k_sectorsCount) {
if (k_sector != NULL) {
printKeyTable(k_sectorsCount, k_sector);
free(k_sector);
}
}
void readerAttack(sector_t *k_sector, uint8_t k_sectorsCount, nonces_t data, bool setEmulatorMem, bool verbose) {
uint64_t key = 0;
bool success = false;
if (k_sector == NULL) {
int32_t res = initSectorTable(&k_sector, k_sectorsCount);
if (res != k_sectorsCount) {
free(k_sector);
return;
}
}
success = mfkey32_moebius(&data, &key);
if (success) {
uint8_t sector = data.sector;
uint8_t keytype = data.keytype;
PrintAndLogEx(INFO, "Reader is trying authenticate with: Key %s, sector %02d: [%012" PRIx64 "]"
, (keytype == MF_KEY_B) ? "B" : "A"
, sector
, key
);
k_sector[sector].Key[keytype] = key;
k_sector[sector].foundKey[keytype] = true;
//set emulator memory for keys
if (setEmulatorMem) {
uint8_t memBlock[16] = {0, 0, 0, 0, 0, 0, 0xff, 0x0F, 0x80, 0x69, 0, 0, 0, 0, 0, 0};
num_to_bytes(k_sector[sector].Key[0], 6, memBlock);
num_to_bytes(k_sector[sector].Key[1], 6, memBlock + 10);
//iceman, guessing this will not work so well for 4K tags.
PrintAndLogEx(INFO, "Setting Emulator Memory Block %02d: [%s]"
, (sector * 4) + 3
, sprint_hex(memBlock, sizeof(memBlock))
);
mfEmlSetMem(memBlock, (sector * 4) + 3, 1);
}
}
free(k_sector);
}
static int CmdHF14AMfSim(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf mf sim",
"Simulate MIFARE Classic family type based upon\n"
"ISO/IEC 14443 type A tag with 4,7 or 10 byte UID\n"
"from emulator memory. See `hf mf eload` first.\n"
"The UID from emulator memory will be used if not specified.",
"hf mf sim --mini --> MIFARE Mini\n"
"hf mf sim --1k --> MIFARE Classic 1k (default)\n"
"hf mf sim --1k -u 0a0a0a0a --> MIFARE Classic 1k with 4b UID\n"
"hf mf sim --1k -u 11223344556677 --> MIFARE Classic 1k with 7b UID\n"
"hf mf sim --1k -u 11223344 -i -x --> Perform reader attack in interactive mode\n"
"hf mf sim --2k --> MIFARE 2k\n"
"hf mf sim --4k --> MIFARE 4k"
);
void *argtable[] = {
arg_param_begin,
arg_str0("u", "uid", "<hex>", "<4|7|10> hex bytes UID"),
arg_lit0(NULL, "mini", "MIFARE Classic Mini / S20"),
arg_lit0(NULL, "1k", "MIFARE Classic 1k / S50"),
arg_lit0(NULL, "2k", "MIFARE Classic/Plus 2k"),
arg_lit0(NULL, "4k", "MIFARE Classic 4k / S70"),
arg_str0(NULL, "atqa", "<hex>", "Provide explicit ATQA (2 bytes, overrides option t)"),
arg_str0(NULL, "sak", "<hex>", "Provide explicit SAK (1 bytes, overrides option t)"),
arg_int0("n", "num", "<dec> ", "Automatically exit simulation after <numreads> blocks have been read by reader. 0 = infinite"),
arg_lit0("i", "interactive", "Console will not be returned until simulation finishes or is aborted"),
arg_lit0("x", NULL, "Performs the 'reader attack', nr/ar attack against a reader"),
arg_lit0("e", "emukeys", "Fill simulator keys from found keys"),
arg_lit0("v", "verbose", "verbose output"),
arg_lit0(NULL, "cve", "trigger CVE 2021_0430"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
uint16_t flags = 0;
int uidlen = 0;
uint8_t uid[10] = {0};
CLIGetHexWithReturn(ctx, 1, uid, &uidlen);
char uidsize[8] = {0};
if (uidlen > 0) {
switch (uidlen) {
case 10:
flags |= FLAG_10B_UID_IN_DATA;
snprintf(uidsize, sizeof(uidsize), "10 byte");
break;
case 7:
flags |= FLAG_7B_UID_IN_DATA;
snprintf(uidsize, sizeof(uidsize), "7 byte");
break;
case 4:
flags |= FLAG_4B_UID_IN_DATA;
snprintf(uidsize, sizeof(uidsize), "4 byte");
break;
default:
PrintAndLogEx(WARNING, "Invalid parameter for UID");
CLIParserFree(ctx);
return PM3_EINVARG;
}
}
bool m0 = arg_get_lit(ctx, 2);
bool m1 = arg_get_lit(ctx, 3);
bool m2 = arg_get_lit(ctx, 4);
bool m4 = arg_get_lit(ctx, 5);
int atqalen = 0;
uint8_t atqa[2] = {0};
CLIGetHexWithReturn(ctx, 6, atqa, &atqalen);
int saklen = 0;
uint8_t sak[1] = {0};
CLIGetHexWithReturn(ctx, 7, sak, &saklen);
uint8_t exitAfterNReads = arg_get_u32_def(ctx, 8, 0);
if (arg_get_lit(ctx, 9)) {
flags |= FLAG_INTERACTIVE;
}
if (arg_get_lit(ctx, 10)) {
flags |= FLAG_NR_AR_ATTACK;
}
bool setEmulatorMem = arg_get_lit(ctx, 11);
bool verbose = arg_get_lit(ctx, 12);
if (arg_get_lit(ctx, 13)) {
flags |= FLAG_CVE21_0430;
}
CLIParserFree(ctx);
nonces_t data[1];
sector_t *k_sector = NULL;
//Validations
if (atqalen > 0) {
if (atqalen != 2) {
PrintAndLogEx(WARNING, "Wrong ATQA length");
return PM3_EINVARG;
}
flags |= FLAG_FORCED_ATQA;
}
if (saklen > 0) {
if (saklen != 1) {
PrintAndLogEx(WARNING, "Wrong SAK length");
return PM3_EINVARG;
}
flags |= FLAG_FORCED_SAK;
}
// Use UID, SAK, ATQA from EMUL, if uid not defined
if ((flags & (FLAG_4B_UID_IN_DATA | FLAG_7B_UID_IN_DATA | FLAG_10B_UID_IN_DATA)) == 0) {
flags |= FLAG_UID_IN_EMUL;
}
uint8_t k_sectorsCount = 40;
char csize[13] = { 0 };
if ((m0 + m1 + m2 + m4) > 1) {
PrintAndLogEx(WARNING, "Only specify one MIFARE Type");
return PM3_EINVARG;
}
if (m0) {
flags |= FLAG_MF_MINI;
snprintf(csize, sizeof(csize), "MINI");
k_sectorsCount = MIFARE_MINI_MAXSECTOR;
} else if (m1) {
flags |= FLAG_MF_1K;
snprintf(csize, sizeof(csize), "1K");
k_sectorsCount = MIFARE_1K_MAXSECTOR;
} else if (m2) {
flags |= FLAG_MF_2K;
snprintf(csize, sizeof(csize), "2K with RATS");
k_sectorsCount = MIFARE_2K_MAXSECTOR;
} else if (m4) {
flags |= FLAG_MF_4K;
snprintf(csize, sizeof(csize), "4K");
k_sectorsCount = MIFARE_4K_MAXSECTOR;
} else {
PrintAndLogEx(WARNING, "Please specify a MIFARE Type");
return PM3_EINVARG;
}
PrintAndLogEx(INFO, _YELLOW_("MIFARE %s") " | %s UID " _YELLOW_("%s") ""
, csize
, uidsize
, (uidlen == 0) ? "N/A" : sprint_hex(uid, uidlen)
);
PrintAndLogEx(INFO, "Options [ numreads: %d, flags: %d (0x%02x) ]"
, exitAfterNReads
, flags
, flags);
struct {
uint16_t flags;
uint8_t exitAfter;
uint8_t uid[10];
uint16_t atqa;
uint8_t sak;
} PACKED payload;
payload.flags = flags;
payload.exitAfter = exitAfterNReads;
memcpy(payload.uid, uid, uidlen);
payload.atqa = (atqa[1] << 8) | atqa[0];
payload.sak = sak[0];
clearCommandBuffer();
SendCommandNG(CMD_HF_MIFARE_SIMULATE, (uint8_t *)&payload, sizeof(payload));
PacketResponseNG resp;
if (flags & FLAG_INTERACTIVE) {
PrintAndLogEx(INFO, "Press pm3-button or send another cmd to abort simulation");
while (!kbd_enter_pressed()) {
if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500)) continue;
if (!(flags & FLAG_NR_AR_ATTACK)) break;
if ((resp.oldarg[0] & 0xffff) != CMD_HF_MIFARE_SIMULATE) break;
memcpy(data, resp.data.asBytes, sizeof(data));
readerAttack(k_sector, k_sectorsCount, data[0], setEmulatorMem, verbose);
}
showSectorTable(k_sector, k_sectorsCount);
} else {
PrintAndLogEx(INFO, "Press pm3-button to abort simulation");
}
return PM3_SUCCESS;
}
/*
static int CmdHF14AMfKeyBrute(const char *Cmd) {
uint8_t blockNo = 0, keytype = MF_KEY_A;
uint8_t key[6] = {0, 0, 0, 0, 0, 0};
uint64_t foundkey = 0;
char cmdp = tolower(param_getchar(Cmd, 0));
if (cmdp == 'h') return usage_hf14_keybrute();
// block number
blockNo = param_get8(Cmd, 0);
// keytype
cmdp = tolower(param_getchar(Cmd, 1));
if (cmdp == 'b') keytype = MF_KEY_B;
// key
if (param_gethex(Cmd, 2, key, 12)) return usage_hf14_keybrute();
uint64_t t1 = msclock();
if (mfKeyBrute(blockNo, keytype, key, &foundkey))
PrintAndLogEx(SUCCESS, "found valid key: %012" PRIx64 " \n", foundkey);
else
PrintAndLogEx(FAILED, "key not found");
t1 = msclock() - t1;
PrintAndLogEx(SUCCESS, "\ntime in keybrute " _YELLOW_("%.0f") " seconds\n", (float)t1 / 1000.0);
return PM3_SUCCESS;
}
*/
void printKeyTable(uint8_t sectorscnt, sector_t *e_sector) {
return printKeyTableEx(sectorscnt, e_sector, 0);
}
void printKeyTableEx(uint8_t sectorscnt, sector_t *e_sector, uint8_t start_sector) {
char strA[12 + 1] = {0};
char strB[12 + 1] = {0};
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(SUCCESS, "-----+-----+--------------+---+--------------+----");
PrintAndLogEx(SUCCESS, " Sec | Blk | key A |res| key B |res");
PrintAndLogEx(SUCCESS, "-----+-----+--------------+---+--------------+----");
for (uint8_t i = 0; i < sectorscnt; i++) {
snprintf(strA, sizeof(strA), "------------");
snprintf(strB, sizeof(strB), "------------");
if (e_sector[i].foundKey[0])
snprintf(strA, sizeof(strA), "%012" PRIX64, e_sector[i].Key[0]);
if (e_sector[i].foundKey[1])
snprintf(strB, sizeof(strB), "%012" PRIX64, e_sector[i].Key[1]);
if (e_sector[i].foundKey[0] > 1) {
PrintAndLogEx(SUCCESS, " "_YELLOW_("%03d")" | %03d | " _GREEN_("%s")" | " _YELLOW_("%c")" | " _GREEN_("%s")" | " _YELLOW_("%c")
, i
, mfSectorTrailerOfSector(i)
, strA, e_sector[i].foundKey[0]
, strB, e_sector[i].foundKey[1]
);
} else {
// keep track if we use start_sector or i...
uint8_t s = start_sector;
if (start_sector == 0)
s = i;
PrintAndLogEx(SUCCESS, " "_YELLOW_("%03d")" | %03d | " _GREEN_("%s")" | " _YELLOW_("%d")" | " _GREEN_("%s")" | " _YELLOW_("%d")
, s
, mfSectorTrailerOfSector(s)
, strA, e_sector[i].foundKey[0]
, strB, e_sector[i].foundKey[1]
);
}
}
PrintAndLogEx(SUCCESS, "-----+-----+--------------+---+--------------+----");
if (e_sector[0].foundKey[0] > 1) {
PrintAndLogEx(INFO, "( "
_YELLOW_("D") ":Dictionary / "
_YELLOW_("S") ":darkSide / "
_YELLOW_("U") ":User / "
_YELLOW_("R") ":Reused / "
_YELLOW_("N") ":Nested / "
_YELLOW_("H") ":Hardnested / "
_YELLOW_("C") ":statiCnested / "
_YELLOW_("A") ":keyA "
" )"
);
} else {
PrintAndLogEx(SUCCESS, "( " _YELLOW_("0") ":Failed / " _YELLOW_("1") ":Success )");
}
// MAD detection
if (e_sector[MF_MAD1_SECTOR].foundKey[0] && e_sector[MF_MAD1_SECTOR].Key[MF_KEY_A] == 0xA0A1A2A3A4A5) {
PrintAndLogEx(HINT, "MAD key detected. Try " _YELLOW_("`hf mf mad`") " for more details");
}
PrintAndLogEx(NORMAL, "");
}
// EMULATOR COMMANDS
static int CmdHF14AMfEGetBlk(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf mf egetblk",
"Get emulator memory block",
"hf mf egetblk --blk 0 -> get block 0 (manufacturer)\n"
"hf mf egetblk --blk 3 -v -> get block 3, decode sector trailer\n"
);
void *argtable[] = {
arg_param_begin,
arg_int1("b", "blk", "<dec>", "block number"),
arg_lit0("v", "verbose", "verbose output"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, false);
int b = arg_get_int_def(ctx, 1, 0);
bool verbose = arg_get_lit(ctx, 2);
CLIParserFree(ctx);
if (b > 255) {
return PM3_EINVARG;
}
uint8_t blockno = (uint8_t)b;
uint8_t data[16] = {0x00};
if (mfEmlGetMem(data, blockno, 1) == PM3_SUCCESS) {
uint8_t sector = mfSectorNum(blockno);
mf_print_sector_hdr(sector);
mf_print_block(blockno, data, verbose);
}
if (verbose) {
decode_print_st(blockno, data);
} else {
PrintAndLogEx(NORMAL, "");
}
return PM3_SUCCESS;
}
static int CmdHF14AMfEGetSc(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf mf egetsc",
"Get emulator memory sector",
"hf mf egetsc -s 0"
);
void *argtable[] = {
arg_param_begin,
arg_int1("s", "sec", "<dec>", "sector number"),
arg_lit0("v", "verbose", "verbose output"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, false);
int s = arg_get_int_def(ctx, 1, 0);
bool verbose = arg_get_lit(ctx, 2);
CLIParserFree(ctx);
if (s > 39) {
PrintAndLogEx(WARNING, "Sector number must be less then 40");
return PM3_EINVARG;
}
uint8_t sector = (uint8_t)s;
mf_print_sector_hdr(sector);
uint8_t blocks = mfNumBlocksPerSector(sector);
uint8_t start = mfFirstBlockOfSector(sector);
uint8_t data[16] = {0};
for (int i = 0; i < blocks; i++) {
int res = mfEmlGetMem(data, start + i, 1);
if (res == PM3_SUCCESS) {
mf_print_block(start + i, data, verbose);
}
}
if (verbose) {
decode_print_st(start + blocks - 1, data);
} else {
PrintAndLogEx(NORMAL, "");
}
return PM3_SUCCESS;
}
static int CmdHF14AMfEClear(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf mf eclr",
"It set card emulator memory to empty data blocks and key A/B FFFFFFFFFFFF",
"hf mf eclr"
);
void *argtable[] = {
arg_param_begin,
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
CLIParserFree(ctx);
clearCommandBuffer();
SendCommandNG(CMD_HF_MIFARE_EML_MEMCLR, NULL, 0);
return PM3_SUCCESS;
}
static int CmdHF14AMfESet(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf mf esetblk",
"Set emulator memory block",
"hf mf esetblk --blk 1 -d 000102030405060708090a0b0c0d0e0f"
);
void *argtable[] = {
arg_param_begin,
arg_int1("b", "blk", "<dec>", "block number"),
arg_str0("d", "data", "<hex>", "bytes to write, 16 hex bytes"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, false);
int b = arg_get_int_def(ctx, 1, 0);
uint8_t data[16] = {0x00};
int datalen = 0;
int res = CLIParamHexToBuf(arg_get_str(ctx, 2), data, sizeof(data), &datalen);
CLIParserFree(ctx);
if (res) {
PrintAndLogEx(FAILED, "Error parsing bytes");
return PM3_EINVARG;
}
if (b > 255) {
return PM3_EINVARG;
}
if (datalen != sizeof(data)) {
PrintAndLogEx(WARNING, "block data must include 16 HEX bytes. Got %i", datalen);
return PM3_EINVARG;
}
// 1 - blocks count
return mfEmlSetMem(data, b, 1);
}
int CmdHF14AMfELoad(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf mf eload",
"Load emulator memory with data from (bin/eml/json) dump file",
"hf mf eload -f hf-mf-01020304.bin\n"
"hf mf eload --4k -f hf-mf-01020304.eml\n"
);
void *argtable[] = {
arg_param_begin,
arg_str1("f", "file", "<fn>", "filename of dump"),
arg_lit0(NULL, "mini", "MIFARE Classic Mini / S20"),
arg_lit0(NULL, "1k", "MIFARE Classic 1k / S50 (def)"),
arg_lit0(NULL, "2k", "MIFARE Classic/Plus 2k"),
arg_lit0(NULL, "4k", "MIFARE Classic 4k / S70"),
arg_lit0(NULL, "ul", "MIFARE Ultralight family"),
arg_lit0("m", "mem", "use RDV4 spiffs"),
arg_int0("q", "qty", "<dec>", "manually set number of blocks (overrides)"),
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 m0 = arg_get_lit(ctx, 2);
bool m1 = arg_get_lit(ctx, 3);
bool m2 = arg_get_lit(ctx, 4);
bool m4 = arg_get_lit(ctx, 5);
bool mu = arg_get_lit(ctx, 6);
bool use_spiffs = arg_get_lit(ctx, 7);
int numblks = arg_get_int_def(ctx, 8, -1);
CLIParserFree(ctx);
// validations
if ((m0 + m1 + m2 + m4 + mu) > 1) {
PrintAndLogEx(WARNING, "Only specify one MIFARE Type");
return PM3_EINVARG;
} else if ((m0 + m1 + m2 + m4 + mu) == 0) {
m1 = true;
}
uint8_t block_width = 16;
uint16_t block_cnt = MIFARE_1K_MAXBLOCK;
uint8_t hdr_len = 0;
if (m0) {
block_cnt = MIFARE_MINI_MAXBLOCK;
} else if (m1) {
block_cnt = MIFARE_1K_MAXBLOCK;
} else if (m2) {
block_cnt = MIFARE_2K_MAXBLOCK;
} else if (m4) {
block_cnt = MIFARE_4K_MAXBLOCK;
} else if (mu) {
block_cnt = MFU_MAX_BLOCKS;
block_width = MFU_BLOCK_SIZE;
hdr_len = MFU_DUMP_PREFIX_LENGTH;
} else {
PrintAndLogEx(WARNING, "Please specify a MIFARE Type");
return PM3_EINVARG;
}
PrintAndLogEx(INFO, "%d blocks ( %u bytes ) to upload", block_cnt, block_cnt * block_width);
if (numblks > 0) {
block_cnt = MIN(numblks, block_cnt);
PrintAndLogEx(INFO, "overriding number of blocks, will use %d blocks ( %u bytes )", block_cnt, block_cnt * block_width);
}
// use RDV4 spiffs
if (use_spiffs && IfPm3Flash() == false) {
PrintAndLogEx(WARNING, "Device not compiled to support spiffs");
return PM3_EINVARG;
}
if (use_spiffs) {
if (fnlen > 32) {
PrintAndLogEx(WARNING, "filename too long for spiffs, expected 32, got %u", fnlen);
return PM3_EINVARG;
}
clearCommandBuffer();
SendCommandNG(CMD_SPIFFS_ELOAD, (uint8_t *)filename, fnlen);
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_SPIFFS_ELOAD, &resp, 2000) == false) {
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
return PM3_ETIMEOUT;
}
if (resp.status != PM3_SUCCESS) {
PrintAndLogEx(FAILED, "Loading file from spiffs to emulatore memory failed");
return PM3_EFLASH;
}
PrintAndLogEx(SUCCESS, "File transfered from spiffs to device emulator memory");
return PM3_SUCCESS;
}
uint8_t *data = NULL;
size_t bytes_read = 0;
int res = pm3_load_dump(filename, (void **)&data, &bytes_read, (block_width * block_cnt + hdr_len));
if (res != PM3_SUCCESS) {
return res;
}
// 64 or 256 blocks.
if ((bytes_read % block_width) != 0) {
PrintAndLogEx(FAILED, "File content error. Size doesn't match blockwidth ");
free(data);
return PM3_ESOFT;
}
// convert plain or old mfu format to new format
if (block_width == MFU_BLOCK_SIZE) {
res = convert_mfu_dump_format(&data, &bytes_read, true);
if (res != PM3_SUCCESS) {
PrintAndLogEx(FAILED, "Failed convert on load to new Ultralight/NTAG format");
free(data);
return res;
}
mfu_dump_t *mfu_dump = (mfu_dump_t *)data;
printMFUdumpEx(mfu_dump, mfu_dump->pages + 1, 0);
// update expected blocks to match converted data.
block_cnt = bytes_read / MFU_BLOCK_SIZE;
PrintAndLogEx(INFO, "MIFARE Ultralight override, will use %d blocks ( %u bytes )", block_cnt, block_cnt * block_width);
}
PrintAndLogEx(INFO, "Uploading to emulator memory");
PrintAndLogEx(INFO, "." NOLF);
// fast push mode
g_conn.block_after_ACK = true;
size_t offset = 0;
int cnt = 0;
while (bytes_read && cnt < block_cnt) {
if (bytes_read == block_width) {
// Disable fast mode on last packet
g_conn.block_after_ACK = false;
}
if (mfEmlSetMem_xt(data + offset, cnt, 1, block_width) != PM3_SUCCESS) {
PrintAndLogEx(FAILED, "Can't set emulator mem at block: %3d", cnt);
free(data);
return PM3_ESOFT;
}
PrintAndLogEx(NORMAL, "." NOLF);
fflush(stdout);
cnt++;
offset += block_width;
bytes_read -= block_width;
}
free(data);
PrintAndLogEx(NORMAL, "");
if (block_width == MFU_BLOCK_SIZE) {
PrintAndLogEx(HINT, "You are ready to simulate. See " _YELLOW_("`hf mfu sim -h`"));
// MFU / NTAG
if ((cnt != block_cnt)) {
PrintAndLogEx(WARNING, "Warning, Ultralight/Ntag file content, Loaded %d blocks of expected %d blocks into emulator memory", cnt, block_cnt);
return PM3_SUCCESS;
}
} else {
PrintAndLogEx(HINT, "You are ready to simulate. See " _YELLOW_("`hf mf sim -h`"));
// MFC
if ((cnt != block_cnt)) {
PrintAndLogEx(WARNING, "Error, file content, Only loaded %d blocks, must be %d blocks into emulator memory", cnt, block_cnt);
return PM3_SUCCESS;
}
}
PrintAndLogEx(INFO, "Done!");
return PM3_SUCCESS;
}
static int CmdHF14AMfESave(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf mf esave",
"Save emulator memory into three files (BIN/EML/JSON) ",
"hf mf esave\n"
"hf mf esave --4k\n"
"hf mf esave --4k -f hf-mf-01020304.eml"
);
void *argtable[] = {
arg_param_begin,
arg_str0("f", "file", "<fn>", "filename of dump"),
arg_lit0(NULL, "mini", "MIFARE Classic Mini / S20"),
arg_lit0(NULL, "1k", "MIFARE Classic 1k / S50 (def)"),
arg_lit0(NULL, "2k", "MIFARE Classic/Plus 2k"),
arg_lit0(NULL, "4k", "MIFARE Classic 4k / S70"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
int fnlen = 0;
char filename[FILE_PATH_SIZE];
CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
bool m0 = arg_get_lit(ctx, 2);
bool m1 = arg_get_lit(ctx, 3);
bool m2 = arg_get_lit(ctx, 4);
bool m4 = arg_get_lit(ctx, 5);
CLIParserFree(ctx);
// validations
if ((m0 + m1 + m2 + m4) > 1) {
PrintAndLogEx(WARNING, "Only specify one MIFARE Type");
return PM3_EINVARG;
} else if ((m0 + m1 + m2 + m4) == 0) {
m1 = true;
}
uint16_t block_cnt = MIFARE_1K_MAXBLOCK;
if (m0) {
block_cnt = MIFARE_MINI_MAXBLOCK;
} else if (m1) {
block_cnt = MIFARE_1K_MAXBLOCK;
} else if (m2) {
block_cnt = MIFARE_2K_MAXBLOCK;
} else if (m4) {
block_cnt = MIFARE_4K_MAXBLOCK;
}
int bytes = block_cnt * MFBLOCK_SIZE;
// reserv memory
uint8_t *dump = calloc(bytes, sizeof(uint8_t));
if (dump == NULL) {
PrintAndLogEx(WARNING, "Fail, cannot allocate memory");
return PM3_EMALLOC;
}
memset(dump, 0, bytes);
PrintAndLogEx(INFO, "downloading %u bytes from emulator memory", bytes);
if (!GetFromDevice(BIG_BUF_EML, dump, bytes, 0, NULL, 0, NULL, 2500, false)) {
PrintAndLogEx(WARNING, "Fail, transfer from device time-out");
free(dump);
return PM3_ETIMEOUT;
}
// user supplied filename?
if (fnlen < 1) {
char *fptr = filename;
fptr += snprintf(fptr, sizeof(filename), "hf-mf-");
FillFileNameByUID(fptr, dump, "-dump", 4);
}
saveFile(filename, ".bin", dump, bytes);
saveFileEML(filename, dump, bytes, MFBLOCK_SIZE);
iso14a_mf_extdump_t xdump = {0};
xdump.card_info.ats_len = 0;
// Check for 4 bytes uid: bcc corrected and single size uid bits in ATQA
if ((dump[0] ^ dump[1] ^ dump[2] ^ dump[3]) == dump[4] && (dump[6] & 0xc0) == 0) {
xdump.card_info.uidlen = 4;
memcpy(xdump.card_info.uid, dump, xdump.card_info.uidlen);
xdump.card_info.sak = dump[5];
memcpy(xdump.card_info.atqa, &dump[6], sizeof(xdump.card_info.atqa));
}
// Check for 7 bytes UID: double size uid bits in ATQA
else if ((dump[8] & 0xc0) == 0x40) {
xdump.card_info.uidlen = 7;
memcpy(xdump.card_info.uid, dump, xdump.card_info.uidlen);
xdump.card_info.sak = dump[7];
memcpy(xdump.card_info.atqa, &dump[8], sizeof(xdump.card_info.atqa));
} else {
PrintAndLogEx(WARNING, "Invalid dump. UID/SAK/ATQA not found");
}
xdump.dump = dump;
xdump.dumplen = bytes;
saveFileJSON(filename, jsfCardMemory, (uint8_t *)&xdump, sizeof(xdump), NULL);
free(dump);
return PM3_SUCCESS;
}
static int CmdHF14AMfEView(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf mf eview",
"It displays emulator memory",
"hf mf eview\n"
"hf mf eview --4k"
);
void *argtable[] = {
arg_param_begin,
arg_lit0(NULL, "mini", "MIFARE Classic Mini / S20"),
arg_lit0(NULL, "1k", "MIFARE Classic 1k / S50 (def)"),
arg_lit0(NULL, "2k", "MIFARE Classic/Plus 2k"),
arg_lit0(NULL, "4k", "MIFARE Classic 4k / S70"),
arg_lit0("v", "verbose", "verbose output"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
bool m0 = arg_get_lit(ctx, 1);
bool m1 = arg_get_lit(ctx, 2);
bool m2 = arg_get_lit(ctx, 3);
bool m4 = arg_get_lit(ctx, 4);
bool verbose = arg_get_lit(ctx, 5);
CLIParserFree(ctx);
// validations
if ((m0 + m1 + m2 + m4) > 1) {
PrintAndLogEx(WARNING, "Only specify one MIFARE Type");
return PM3_EINVARG;
} else if ((m0 + m1 + m2 + m4) == 0) {
m1 = true;
}
uint16_t block_cnt = MIFARE_1K_MAXBLOCK;
if (m0) {
block_cnt = MIFARE_MINI_MAXBLOCK;
} else if (m1) {
block_cnt = MIFARE_1K_MAXBLOCK;
} else if (m2) {
block_cnt = MIFARE_2K_MAXBLOCK;
} else if (m4) {
block_cnt = MIFARE_4K_MAXBLOCK;
} else {
PrintAndLogEx(WARNING, "Please specify a MIFARE Type");
return PM3_EINVARG;
}
int bytes = block_cnt * MFBLOCK_SIZE;
uint8_t *dump = calloc(bytes, sizeof(uint8_t));
if (dump == NULL) {
PrintAndLogEx(WARNING, "Fail, cannot allocate memory");
return PM3_EMALLOC;
}
PrintAndLogEx(INFO, "downloading emulator memory");
if (!GetFromDevice(BIG_BUF_EML, dump, bytes, 0, NULL, 0, NULL, 2500, false)) {
PrintAndLogEx(WARNING, "Fail, transfer from device time-out");
free(dump);
return PM3_ETIMEOUT;
}
mf_print_blocks(block_cnt, dump, verbose);
if (verbose) {
mf_print_keys(block_cnt, dump);
}
free(dump);
return PM3_SUCCESS;
}
static int CmdHF14AMfECFill(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf mf ecfill",
"Dump card and transfer the data to emulator memory.\n"
"Keys must be in the emulator memory",
"hf mf ecfill --> use key type A\n"
"hf mf ecfill --4k -b --> target 4K card with key type B"
);
void *argtable[] = {
arg_param_begin,
arg_lit0("a", NULL, "input key type is key A(def)"),
arg_lit0("b", NULL, "input key type is key B"),
arg_lit0(NULL, "mini", "MIFARE Classic Mini / S20"),
arg_lit0(NULL, "1k", "MIFARE Classic 1k / S50 (def)"),
arg_lit0(NULL, "2k", "MIFARE Classic/Plus 2k"),
arg_lit0(NULL, "4k", "MIFARE Classic 4k / S70"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
uint8_t keytype = MF_KEY_A;
if (arg_get_lit(ctx, 1) && arg_get_lit(ctx, 2)) {
CLIParserFree(ctx);
PrintAndLogEx(WARNING, "Input key type must be A or B");
return PM3_EINVARG;
} else if (arg_get_lit(ctx, 2)) {
keytype = MF_KEY_B;
}
bool m0 = arg_get_lit(ctx, 3);
bool m1 = arg_get_lit(ctx, 4);
bool m2 = arg_get_lit(ctx, 5);
bool m4 = arg_get_lit(ctx, 6);
CLIParserFree(ctx);
// validations
if ((m0 + m1 + m2 + m4) > 1) {
PrintAndLogEx(WARNING, "Only specify one MIFARE Type");
return PM3_EINVARG;
} else if ((m0 + m1 + m2 + m4) == 0) {
m1 = true;
}
uint8_t sectors_cnt = MIFARE_1K_MAXSECTOR;
if (m0) {
sectors_cnt = MIFARE_MINI_MAXSECTOR;
} else if (m1) {
sectors_cnt = MIFARE_1K_MAXSECTOR;
} else if (m2) {
sectors_cnt = MIFARE_2K_MAXSECTOR;
} else if (m4) {
sectors_cnt = MIFARE_4K_MAXSECTOR;
} else {
PrintAndLogEx(WARNING, "Please specify a MIFARE Type");
return PM3_EINVARG;
}
mfc_eload_t payload = {
.sectorcnt = sectors_cnt,
.keytype = keytype
};
clearCommandBuffer();
SendCommandNG(CMD_HF_MIFARE_EML_LOAD, (uint8_t *)&payload, sizeof(payload));
// 2021, iceman: should get a response from device when its done.
return PM3_SUCCESS;
}
static int CmdHF14AMfEKeyPrn(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf mf ekeyprn",
"Download and print the keys from emulator memory",
"hf mf ekeyprn --1k --> print MFC 1K keyset\n"
"hf mf ekeyprn -w --> write keys to binary file"
);
void *argtable[] = {
arg_param_begin,
arg_lit0("w", "write", "write keys to binary file `hf-mf-<UID>-key.bin`"),
arg_lit0(NULL, "mini", "MIFARE Classic Mini / S20"),
arg_lit0(NULL, "1k", "MIFARE Classic 1k / S50 (def)"),
arg_lit0(NULL, "2k", "MIFARE Classic/Plus 2k"),
arg_lit0(NULL, "4k", "MIFARE Classic 4k / S70"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
bool create_dumpfile = arg_get_lit(ctx, 1);
bool m0 = arg_get_lit(ctx, 2);
bool m1 = arg_get_lit(ctx, 3);
bool m2 = arg_get_lit(ctx, 4);
bool m4 = arg_get_lit(ctx, 5);
CLIParserFree(ctx);
// validations
if ((m0 + m1 + m2 + m4) > 1) {
PrintAndLogEx(WARNING, "Only specify one MIFARE Type");
return PM3_EINVARG;
} else if ((m0 + m1 + m2 + m4) == 0) {
m1 = true;
}
uint8_t sectors_cnt = MIFARE_1K_MAXSECTOR;
if (m0) {
sectors_cnt = MIFARE_MINI_MAXSECTOR;
} else if (m1) {
sectors_cnt = MIFARE_1K_MAXSECTOR;
} else if (m2) {
sectors_cnt = MIFARE_2K_MAXSECTOR;
} else if (m4) {
sectors_cnt = MIFARE_4K_MAXSECTOR;
} else {
PrintAndLogEx(WARNING, "Please specify a MIFARE Type");
return PM3_EINVARG;
}
sector_t *e_sector = NULL;
// create/initialize key storage structure
int32_t res = initSectorTable(&e_sector, sectors_cnt);
if (res != sectors_cnt) {
free(e_sector);
return PM3_EMALLOC;
}
// read UID from EMUL
uint8_t data[16];
if (mfEmlGetMem(data, 0, 1) != PM3_SUCCESS) {
PrintAndLogEx(WARNING, "error get block 0");
free(e_sector);
return PM3_ESOFT;
}
// assuming 4byte UID.
uint8_t uid[4];
memcpy(uid, data, sizeof(uid));
// download keys from EMUL
for (int i = 0; i < sectors_cnt; i++) {
if (mfEmlGetMem(data, mfFirstBlockOfSector(i) + mfNumBlocksPerSector(i) - 1, 1) != PM3_SUCCESS) {
PrintAndLogEx(WARNING, "error get block %d", mfFirstBlockOfSector(i) + mfNumBlocksPerSector(i) - 1);
e_sector[i].foundKey[0] = false;
e_sector[i].foundKey[1] = false;
} else {
e_sector[i].foundKey[0] = true;
e_sector[i].Key[0] = bytes_to_num(data, 6);
e_sector[i].foundKey[1] = true;
e_sector[i].Key[1] = bytes_to_num(data + 10, 6);
}
}
// print keys
printKeyTable(sectors_cnt, e_sector);
// dump the keys
if (create_dumpfile) {
char filename[FILE_PATH_SIZE] = {0};
char *fptr = filename;
fptr += snprintf(fptr, sizeof(filename), "hf-mf-");
FillFileNameByUID(fptr + strlen(fptr), uid, "-key", sizeof(uid));
createMfcKeyDump(filename, sectors_cnt, e_sector);
}
free(e_sector);
return PM3_SUCCESS;
}
// CHINESE MAGIC COMMANDS
static int CmdHF14AMfCSetUID(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf mf csetuid",
"Set UID, ATQA, and SAK for magic gen1a card",
"hf mf csetuid -u 01020304\n"
"hf mf csetuid -w -u 01020304 --atqa 0004 --sak 08"
);
void *argtable[] = {
arg_param_begin,
arg_lit0("w", "wipe", "wipes card with backdoor cmd`"),
arg_str0("u", "uid", "<hex>", "UID, 4/7 hex bytes"),
arg_str0("a", "atqa", "<hex>", "ATQA, 2 hex bytes"),
arg_str0("s", "sak", "<hex>", "SAK, 1 hex byte"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
uint8_t wipe_card = arg_get_lit(ctx, 1);
int uidlen = 0;
uint8_t uid[7] = {0x00};
CLIGetHexWithReturn(ctx, 2, uid, &uidlen);
int alen = 0;
uint8_t atqa[2] = {0x00};
CLIGetHexWithReturn(ctx, 3, atqa, &alen);
int slen = 0;
uint8_t sak[1] = {0x00};
CLIGetHexWithReturn(ctx, 4, sak, &slen);
CLIParserFree(ctx);
// sanity checks
if (uidlen != 4 && uidlen != 7) {
PrintAndLogEx(FAILED, "UID must be 4 or 7 hex bytes. Got %d", uidlen);
return PM3_EINVARG;
}
if (alen && alen != 2) {
PrintAndLogEx(FAILED, "ATQA must be 2 hex bytes. Got %d", alen);
return PM3_EINVARG;
}
if (slen && slen != 1) {
PrintAndLogEx(FAILED, "SAK must be 1 hex byte. Got %d", slen);
return PM3_EINVARG;
}
uint8_t old_uid[7] = {0};
uint8_t verify_uid[7] = {0};
int res = mfCSetUID(
uid,
uidlen,
(alen) ? atqa : NULL,
(slen) ? sak : NULL,
old_uid,
verify_uid,
wipe_card
);
if (res) {
PrintAndLogEx(ERR, "Can't set UID. error %d", res);
return PM3_ESOFT;
}
res = memcmp(uid, verify_uid, uidlen);
PrintAndLogEx(SUCCESS, "Old UID... %s", sprint_hex(old_uid, uidlen));
PrintAndLogEx(SUCCESS, "New UID... %s ( %s )",
sprint_hex(verify_uid, uidlen),
(res == 0) ? _GREEN_("verified") : _RED_("fail")
);
return PM3_SUCCESS;
}
static int CmdHF14AMfCWipe(const char *cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf mf cwipe",
"Wipe gen1 magic chinese card.\n"
"Set UID / ATQA / SAK / Data / Keys / Access to default values",
"hf mf cwipe\n"
"hf mf cwipe -u 09080706 -a 0004 -s 18 --> set UID, ATQA and SAK and wipe card");
void *argtable[] = {
arg_param_begin,
arg_str0("u", "uid", "<hex>", "UID, 4 hex bytes"),
arg_str0("a", "atqa", "<hex>", "ATQA, 2 hex bytes"),
arg_str0("s", "sak", "<hex>", "SAK, 1 hex byte"),
arg_param_end
};
CLIExecWithReturn(ctx, cmd, argtable, true);
int uidlen = 0;
uint8_t uid[8] = {0x00};
CLIGetHexWithReturn(ctx, 1, uid, &uidlen);
int alen = 0;
uint8_t atqa[2] = {0x00};
CLIGetHexWithReturn(ctx, 2, atqa, &alen);
int slen = 0;
uint8_t sak[1] = {0x00};
CLIGetHexWithReturn(ctx, 3, sak, &slen);
CLIParserFree(ctx);
if (uidlen && uidlen != 4) {
PrintAndLogEx(ERR, "UID length must be 4 bytes, got %d", uidlen);
return PM3_EINVARG;
}
if (alen && alen != 2) {
PrintAndLogEx(ERR, "ATQA length must be 2 bytes, got %d", alen);
return PM3_EINVARG;
}
if (slen && slen != 1) {
PrintAndLogEx(ERR, "SAK length must be 1 byte, got %d", slen);
return PM3_EINVARG;
}
int res = mfCWipe((uidlen) ? uid : NULL, (alen) ? atqa : NULL, (slen) ? sak : NULL);
if (res) {
PrintAndLogEx(ERR, "Can't wipe card. error %d", res);
return PM3_ESOFT;
}
PrintAndLogEx(SUCCESS, "Card wiped successfully");
return PM3_SUCCESS;
}
static int CmdHF14AMfCSetBlk(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf mf csetblk",
"Set block data on a magic gen1a card",
"hf mf csetblk --blk 1 -d 000102030405060708090a0b0c0d0e0f"
);
void *argtable[] = {
arg_param_begin,
arg_int1("b", "blk", "<dec>", "block number"),
arg_str0("d", "data", "<hex>", "bytes to write, 16 hex bytes"),
arg_lit0("w", "wipe", "wipes card with backdoor cmd before writing"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, false);
int b = arg_get_int_def(ctx, 1, -1);
uint8_t data[MFBLOCK_SIZE] = {0x00};
int datalen = 0;
CLIGetHexWithReturn(ctx, 2, data, &datalen);
uint8_t wipe_card = arg_get_lit(ctx, 3);
CLIParserFree(ctx);
if (b < 0 || b >= MIFARE_1K_MAXBLOCK) {
PrintAndLogEx(FAILED, "target block number out-of-range, got %i", b);
return PM3_EINVARG;
}
if (datalen != MFBLOCK_SIZE) {
PrintAndLogEx(FAILED, "expected 16 bytes data, got %i", datalen);
return PM3_EINVARG;
}
uint8_t params = MAGIC_SINGLE;
if (wipe_card) {
params |= MAGIC_WIPE;
}
PrintAndLogEx(INFO, "Writing block number:%2d data:%s", b, sprint_hex_inrow(data, sizeof(data)));
int res = mfCSetBlock(b, data, NULL, params);
if (res) {
PrintAndLogEx(ERR, "Can't write block. error=%d", res);
return PM3_ESOFT;
}
return PM3_SUCCESS;
}
static int CmdHF14AMfCLoad(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf mf cload",
"Load magic gen1a card with data from (bin/eml/json) dump file\n"
"or from emulator memory.",
"hf mf cload --emu\n"
"hf mf cload -f hf-mf-01020304.eml\n"
);
void *argtable[] = {
arg_param_begin,
arg_str0("f", "file", "<fn>", "filename of dump"),
arg_lit0(NULL, "emu", "from emulator memory"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, false);
int fnlen = 0;
char filename[FILE_PATH_SIZE] = {0};
CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
bool fill_from_emulator = arg_get_lit(ctx, 2);
CLIParserFree(ctx);
if (fill_from_emulator) {
PrintAndLogEx(INFO, "Start upload to emulator memory");
PrintAndLogEx(INFO, "." NOLF);
for (int b = 0; b < MIFARE_1K_MAXBLOCK; b++) {
int flags = 0;
uint8_t buf8[MFBLOCK_SIZE] = {0x00};
// read from emul memory
if (mfEmlGetMem(buf8, b, 1)) {
PrintAndLogEx(WARNING, "Can't read from emul block: %d", b);
return PM3_ESOFT;
}
// switch on field and send magic sequence
if (b == 0) {
flags = MAGIC_INIT + MAGIC_WUPC;
}
// just write
if (b == 1) {
flags = 0;
}
// Done. Magic Halt and switch off field.
if (b == ((MFBLOCK_SIZE * 4) - 1)) {
flags = MAGIC_HALT + MAGIC_OFF;
}
// write to card
if (mfCSetBlock(b, buf8, NULL, flags)) {
PrintAndLogEx(WARNING, "Can't set magic card block: %d", b);
return PM3_ESOFT;
}
PrintAndLogEx(NORMAL, "." NOLF);
fflush(stdout);
}
PrintAndLogEx(NORMAL, "");
return PM3_SUCCESS;
}
// reserve memory
uint8_t *data = NULL;
size_t bytes_read = 0;
int res = pm3_load_dump(filename, (void **)&data, &bytes_read, (MFBLOCK_SIZE * MIFARE_4K_MAXBLOCK));
if (res != PM3_SUCCESS) {
return res;
}
// 64 or 256blocks.
if (bytes_read != (MIFARE_1K_MAXBLOCK * MFBLOCK_SIZE) &&
bytes_read != (MIFARE_4K_MAXBLOCK * MFBLOCK_SIZE)) {
PrintAndLogEx(ERR, "File content error. Read %zu bytes", bytes_read);
free(data);
return PM3_EFILE;
}
PrintAndLogEx(INFO, "Copying to magic gen1a card");
PrintAndLogEx(INFO, "." NOLF);
int blockno = 0;
int flags = 0;
while (bytes_read) {
// switch on field and send magic sequence
if (blockno == 0) {
flags = MAGIC_INIT + MAGIC_WUPC;
}
// write
if (blockno == 1) {
flags = 0;
}
// switch off field
if (blockno == MFBLOCK_SIZE * 4 - 1) {
flags = MAGIC_HALT + MAGIC_OFF;
}
if (mfCSetBlock(blockno, data + (MFBLOCK_SIZE * blockno), NULL, flags)) {
PrintAndLogEx(WARNING, "Can't set magic card block: %d", blockno);
free(data);
return PM3_ESOFT;
}
bytes_read -= MFBLOCK_SIZE;
PrintAndLogEx(NORMAL, "." NOLF);
fflush(stdout);
blockno++;
// magic card type - mifare 1K
if (blockno >= MIFARE_1K_MAXBLOCK) break;
}
PrintAndLogEx(NORMAL, "\n");
free(data);
// confirm number written blocks. Must be 64 or 256 blocks
if (blockno != MIFARE_1K_MAXBLOCK) {
if (blockno != MIFARE_4K_MAXBLOCK) {
PrintAndLogEx(ERR, "File content error. There must be %u blocks", MIFARE_4K_MAXBLOCK);
return PM3_EFILE;
}
PrintAndLogEx(ERR, "File content error. There must be %d blocks", MIFARE_1K_MAXBLOCK);
return PM3_EFILE;
}
PrintAndLogEx(SUCCESS, "Card loaded " _YELLOW_("%d") " blocks from file", blockno);
PrintAndLogEx(INFO, "Done!");
return PM3_SUCCESS;
}
static int CmdHF14AMfCGetBlk(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf mf cgetblk",
"Get block data from magic Chinese card.\n"
"Only works with magic gen1a cards",
"hf mf cgetblk --blk 0 --> get block 0 (manufacturer)\n"
"hf mf cgetblk --blk 3 -v --> get block 3, decode sector trailer\n"
);
void *argtable[] = {
arg_param_begin,
arg_int1("b", "blk", "<dec>", "block number"),
arg_lit0("v", "verbose", "verbose output"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, false);
int b = arg_get_int_def(ctx, 1, 0);
bool verbose = arg_get_lit(ctx, 2);
CLIParserFree(ctx);
if (b > 255) {
return PM3_EINVARG;
}
uint8_t blockno = (uint8_t)b;
uint8_t data[16] = {0};
int res = mfCGetBlock(blockno, data, MAGIC_SINGLE);
if (res) {
PrintAndLogEx(ERR, "Can't read block. error=%d", res);
return PM3_ESOFT;
}
uint8_t sector = mfSectorNum(blockno);
mf_print_sector_hdr(sector);
mf_print_block(blockno, data, verbose);
if (verbose) {
decode_print_st(blockno, data);
} else {
PrintAndLogEx(NORMAL, "");
}
return PM3_SUCCESS;
}
static int CmdHF14AMfCGetSc(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf mf cgetsc",
"Get sector data from magic Chinese card.\n"
"Only works with magic gen1a cards",
"hf mf cgetsc -s 0"
);
void *argtable[] = {
arg_param_begin,
arg_int1("s", "sec", "<dec>", "sector number"),
arg_lit0("v", "verbose", "verbose output"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, false);
int s = arg_get_int_def(ctx, 1, 0);
bool verbose = arg_get_lit(ctx, 2);
CLIParserFree(ctx);
if (s > 39) {
PrintAndLogEx(WARNING, "Sector number must be less then 40");
return PM3_EINVARG;
}
uint8_t sector = (uint8_t)s;
mf_print_sector_hdr(sector);
uint8_t blocks = 4;
uint8_t start = sector * 4;
if (sector >= 32) {
blocks = 16;
start = 128 + (sector - 32) * 16;
}
int flags = MAGIC_INIT + MAGIC_WUPC;
uint8_t data[16] = {0};
for (int i = 0; i < blocks; i++) {
if (i == 1) flags = 0;
if (i == blocks - 1) flags = MAGIC_HALT + MAGIC_OFF;
int res = mfCGetBlock(start + i, data, flags);
if (res) {
PrintAndLogEx(ERR, "Can't read block. %d error=%d", start + i, res);
return PM3_ESOFT;
}
mf_print_block(start + i, data, verbose);
}
if (verbose) {
decode_print_st(start + blocks - 1, data);
} else {
PrintAndLogEx(NORMAL, "");
}
return PM3_SUCCESS;
}
static int CmdHF14AMfCSave(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf mf csave",
"Save magic gen1a card memory into three files (BIN/EML/JSON)"
"or into emulator memory",
"hf mf csave\n"
"hf mf csave --4k"
);
void *argtable[] = {
arg_param_begin,
arg_str0("f", "file", "<fn>", "filename of dump"),
arg_lit0(NULL, "mini", "MIFARE Classic Mini / S20"),
arg_lit0(NULL, "1k", "MIFARE Classic 1k / S50 (def)"),
arg_lit0(NULL, "2k", "MIFARE Classic/Plus 2k"),
arg_lit0(NULL, "4k", "MIFARE Classic 4k / S70"),
arg_lit0(NULL, "emu", "from emulator memory"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
int fnlen = 0;
char filename[FILE_PATH_SIZE];
CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
bool m0 = arg_get_lit(ctx, 2);
bool m1 = arg_get_lit(ctx, 3);
bool m2 = arg_get_lit(ctx, 4);
bool m4 = arg_get_lit(ctx, 5);
bool fill_emulator = arg_get_lit(ctx, 6);
CLIParserFree(ctx);
// validations
if ((m0 + m1 + m2 + m4) > 1) {
PrintAndLogEx(WARNING, "Only specify one MIFARE Type");
return PM3_EINVARG;
} else if ((m0 + m1 + m2 + m4) == 0) {
m1 = true;
}
char s[6];
memset(s, 0, sizeof(s));
uint16_t block_cnt = MIFARE_1K_MAXBLOCK;
if (m0) {
block_cnt = MIFARE_MINI_MAXBLOCK;
strncpy(s, "Mini", 5);
} else if (m1) {
block_cnt = MIFARE_1K_MAXBLOCK;
strncpy(s, "1K", 3);
} else if (m2) {
block_cnt = MIFARE_2K_MAXBLOCK;
strncpy(s, "2K", 3);
} else if (m4) {
block_cnt = MIFARE_4K_MAXBLOCK;
strncpy(s, "4K", 3);
} else {
PrintAndLogEx(WARNING, "Please specify a MIFARE Type");
return PM3_EINVARG;
}
PrintAndLogEx(SUCCESS, "Dumping magic Gen1a MIFARE Classic " _GREEN_("%s") " card memory", s);
PrintAndLogEx(INFO, "." NOLF);
// Select card to get UID/UIDLEN information
clearCommandBuffer();
SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0);
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) {
PrintAndLogEx(WARNING, "iso14443a card select timeout");
return PM3_ETIMEOUT;
}
/*
0: couldn't read
1: OK, with ATS
2: OK, no ATS
3: proprietary Anticollision
*/
uint64_t select_status = resp.oldarg[0];
if (select_status == 0) {
PrintAndLogEx(WARNING, "iso14443a card select failed");
return PM3_SUCCESS;
}
// store card info
iso14a_card_select_t card;
memcpy(&card, (iso14a_card_select_t *)resp.data.asBytes, sizeof(iso14a_card_select_t));
// reserve memory
uint16_t bytes = block_cnt * MFBLOCK_SIZE;
uint8_t *dump = calloc(bytes, sizeof(uint8_t));
if (dump == NULL) {
PrintAndLogEx(WARNING, "Fail, cannot allocate memory");
return PM3_EMALLOC;
}
// switch on field and send magic sequence
uint8_t flags = MAGIC_INIT + MAGIC_WUPC;
for (uint16_t i = 0; i < block_cnt; i++) {
// read
if (i == 1) {
flags = 0;
}
// switch off field
if (i == block_cnt - 1) {
flags = MAGIC_HALT + MAGIC_OFF;
}
if (mfCGetBlock(i, dump + (i * MFBLOCK_SIZE), flags)) {
PrintAndLogEx(WARNING, "Can't get magic card block: %d", i);
PrintAndLogEx(HINT, "Verify your card size, and try again or try another tag position");
free(dump);
return PM3_ESOFT;
}
PrintAndLogEx(NORMAL, "." NOLF);
fflush(stdout);
}
PrintAndLogEx(NORMAL, "");
if (fill_emulator) {
PrintAndLogEx(INFO, "uploading to emulator memory");
PrintAndLogEx(INFO, "." NOLF);
// fast push mode
g_conn.block_after_ACK = true;
for (int i = 0; i < block_cnt; i += 5) {
if (i == block_cnt - 1) {
// Disable fast mode on last packet
g_conn.block_after_ACK = false;
}
if (mfEmlSetMem(dump + (i * MFBLOCK_SIZE), i, 5) != PM3_SUCCESS) {
PrintAndLogEx(WARNING, "Can't set emul block: " _YELLOW_("%d"), i);
}
PrintAndLogEx(NORMAL, "." NOLF);
fflush(stdout);
}
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(SUCCESS, "uploaded " _YELLOW_("%d") " bytes to emulator memory", bytes);
}
// user supplied filename?
if (fnlen < 1) {
char *fptr = filename;
fptr += snprintf(fptr, sizeof(filename), "hf-mf-");
FillFileNameByUID(fptr, card.uid, "-dump", card.uidlen);
}
saveFile(filename, ".bin", dump, bytes);
saveFileEML(filename, dump, bytes, MFBLOCK_SIZE);
iso14a_mf_extdump_t xdump;
xdump.card_info = card;
xdump.dump = dump;
xdump.dumplen = bytes;
saveFileJSON(filename, jsfCardMemory, (uint8_t *)&xdump, sizeof(xdump), NULL);
free(dump);
return PM3_SUCCESS;
}
static int CmdHF14AMfCView(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf mf cview",
"View `magic gen1a` card memory",
"hf mf cview\n"
"hf mf cview --4k"
);
void *argtable[] = {
arg_param_begin,
arg_lit0(NULL, "mini", "MIFARE Classic Mini / S20"),
arg_lit0(NULL, "1k", "MIFARE Classic 1k / S50 (def)"),
arg_lit0(NULL, "2k", "MIFARE Classic/Plus 2k"),
arg_lit0(NULL, "4k", "MIFARE Classic 4k / S70"),
arg_lit0("v", "verbose", "verbose output"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
bool m0 = arg_get_lit(ctx, 1);
bool m1 = arg_get_lit(ctx, 2);
bool m2 = arg_get_lit(ctx, 3);
bool m4 = arg_get_lit(ctx, 4);
bool verbose = arg_get_lit(ctx, 5);
CLIParserFree(ctx);
// validations
if ((m0 + m1 + m2 + m4) > 1) {
PrintAndLogEx(WARNING, "Only specify one MIFARE Type");
return PM3_EINVARG;
} else if ((m0 + m1 + m2 + m4) == 0) {
m1 = true;
}
char s[6];
memset(s, 0, sizeof(s));
uint16_t block_cnt = MIFARE_1K_MAXBLOCK;
if (m0) {
block_cnt = MIFARE_MINI_MAXBLOCK;
strncpy(s, "Mini", 5);
} else if (m1) {
block_cnt = MIFARE_1K_MAXBLOCK;
strncpy(s, "1K", 3);
} else if (m2) {
block_cnt = MIFARE_2K_MAXBLOCK;
strncpy(s, "2K", 3);
} else if (m4) {
block_cnt = MIFARE_4K_MAXBLOCK;
strncpy(s, "4K", 3);
} else {
PrintAndLogEx(WARNING, "Please specify a MIFARE Type");
return PM3_EINVARG;
}
PrintAndLogEx(SUCCESS, "View magic Gen1a MIFARE Classic " _GREEN_("%s"), s);
PrintAndLogEx(INFO, "." NOLF);
// Select card to get UID/UIDLEN information
clearCommandBuffer();
SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0);
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) {
PrintAndLogEx(WARNING, "iso14443a card select timeout");
return PM3_ETIMEOUT;
}
/*
0: couldn't read
1: OK, with ATS
2: OK, no ATS
3: proprietary Anticollision
*/
uint64_t select_status = resp.oldarg[0];
if (select_status == 0) {
PrintAndLogEx(WARNING, "iso14443a card select failed");
return PM3_ERFTRANS;
}
iso14a_card_select_t card;
memcpy(&card, (iso14a_card_select_t *)resp.data.asBytes, sizeof(iso14a_card_select_t));
// reserve memory
uint16_t bytes = block_cnt * MFBLOCK_SIZE;
uint8_t *dump = calloc(bytes, sizeof(uint8_t));
if (dump == NULL) {
PrintAndLogEx(WARNING, "Fail, cannot allocate memory");
return PM3_EMALLOC;
}
// switch on field and send magic sequence
uint8_t flags = MAGIC_INIT + MAGIC_WUPC;
for (uint16_t i = 0; i < block_cnt; i++) {
// read
if (i == 1) {
flags = 0;
}
// switch off field
if (i == block_cnt - 1) {
flags = MAGIC_HALT + MAGIC_OFF;
}
if (mfCGetBlock(i, dump + (i * MFBLOCK_SIZE), flags)) {
PrintAndLogEx(WARNING, "Can't get magic card block: " _YELLOW_("%u"), i);
PrintAndLogEx(HINT, "Verify your card size, and try again or try another tag position");
free(dump);
return PM3_ESOFT;
}
PrintAndLogEx(NORMAL, "." NOLF);
fflush(stdout);
}
PrintAndLogEx(NORMAL, "");
mf_print_blocks(block_cnt, dump, verbose);
if (verbose) {
mf_print_keys(block_cnt, dump);
}
free(dump);
return PM3_SUCCESS;
}
//needs nt, ar, at, Data to decrypt
static int CmdHf14AMfDecryptBytes(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf mf decrypt",
"Decrypt Crypto-1 encrypted bytes given some known state of crypto. See tracelog to gather needed values",
"hf mf decrypt --nt b830049b --ar 9248314a --at 9280e203 -d 41e586f9\n"
" -> 41e586f9 becomes 3003999a\n"
" -> which annotates 30 03 [99 9a] read block 3 [crc]"
);
void *argtable[] = {
arg_param_begin,
arg_str1(NULL, "nt", "<hex>", "tag nonce"),
arg_str1(NULL, "ar", "<hex>", "ar_enc, encrypted reader response"),
arg_str1(NULL, "at", "<hex>", "at_enc, encrypted tag response"),
arg_str1("d", "data", "<hex>", "encrypted data, taken directly after at_enc and forward"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, false);
uint32_t nt = 0;
int res = arg_get_u32_hexstr_def(ctx, 1, 0, &nt);
if (res != 1) {
CLIParserFree(ctx);
PrintAndLogEx(WARNING, "check `nt` parameter");
return PM3_EINVARG;
}
uint32_t ar_enc = 0;
res = arg_get_u32_hexstr_def(ctx, 2, 0, &ar_enc);
if (res != 1) {
CLIParserFree(ctx);
PrintAndLogEx(WARNING, "check `ar` parameter");
return PM3_EINVARG;
}
uint32_t at_enc = 0;
res = arg_get_u32_hexstr_def(ctx, 3, 0, &at_enc);
if (res != 1) {
CLIParserFree(ctx);
PrintAndLogEx(WARNING, "check `at` parameter");
return PM3_EINVARG;
}
int datalen = 0;
uint8_t data[512] = {0x00};
CLIGetHexWithReturn(ctx, 4, data, &datalen);
CLIParserFree(ctx);
PrintAndLogEx(INFO, "nt....... %08X", nt);
PrintAndLogEx(INFO, "ar enc... %08X", ar_enc);
PrintAndLogEx(INFO, "at enc... %08X", at_enc);
return tryDecryptWord(nt, ar_enc, at_enc, data, datalen);
}
static int CmdHf14AMfSetMod(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf mf setmod",
"Sets the load modulation strength of a MIFARE Classic EV1 card",
"hf mf setmod -k ffffffffffff -0"
);
void *argtable[] = {
arg_param_begin,
arg_lit0("0", NULL, "normal modulation"),
arg_lit0("1", NULL, "strong modulation (def)"),
arg_str0("k", "key", "<hex>", "key A, Sector 0, 6 hex bytes"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
bool m0 = arg_get_lit(ctx, 1);
bool m1 = arg_get_lit(ctx, 2);
int keylen = 0;
uint8_t key[6] = {0};
CLIGetHexWithReturn(ctx, 3, key, &keylen);
CLIParserFree(ctx);
if (m0 + m1 > 1) {
PrintAndLogEx(WARNING, "please select one modulation");
return PM3_EINVARG;
}
uint8_t data[7] = {0};
memcpy(data + 1, key, 6);
if (m1) {
data[0] = 1;
} else {
data[0] = 0;
}
clearCommandBuffer();
SendCommandNG(CMD_HF_MIFARE_SETMOD, data, sizeof(data));
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_HF_MIFARE_SETMOD, &resp, 1500) == false) {
PrintAndLogEx(WARNING, "Command execute timeout");
return PM3_ETIMEOUT;
}
if (resp.status == PM3_SUCCESS)
PrintAndLogEx(SUCCESS, "Change ( " _GREEN_("ok") " )");
else
PrintAndLogEx(FAILED, "Change ( " _RED_("fail") " )");
return resp.status;
}
// MIFARE NACK bug detection
static int CmdHf14AMfNack(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf mf nack",
"Test a MIFARE Classic based card for the NACK bug",
"hf mf nack"
);
void *argtable[] = {
arg_param_begin,
arg_lit0("v", "verbose", "verbose output`"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
bool verbose = arg_get_lit(ctx, 1);
CLIParserFree(ctx);
if (verbose)
PrintAndLogEx(INFO, "Started testing card for NACK bug. Press Enter to abort");
detect_classic_nackbug(verbose);
return PM3_SUCCESS;
}
/*
static int CmdHF14AMfice(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf mf ice",
"Collect MIFARE Classic nonces to file",
"hf mf ice\n"
"hf mf ice -f nonces.bin");
void *argtable[] = {
arg_param_begin,
arg_str0("f", "file", "<fn>", "filename of nonce dump"),
arg_u64_0(NULL, "limit", "<dec>", "nonces to be collected"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
int fnlen = 0;
char filename[FILE_PATH_SIZE] = {0};
CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
uint32_t limit = arg_get_u32_def(ctx, 2, 50000);
CLIParserFree(ctx);
// Validations
char *fptr;
if (filename[0] == '\0') {
fptr = GenerateFilename("hf-mf-", "-nonces.bin");
if (fptr == NULL)
return PM3_EFILE;
strcpy(filename, fptr);
free(fptr);
}
uint8_t blockNo = 0;
uint8_t keyType = MF_KEY_A;
uint8_t trgBlockNo = 0;
uint8_t trgKeyType = MF_KEY_B;
bool slow = false;
bool initialize = true;
bool acquisition_completed = false;
uint32_t total_num_nonces = 0;
PacketResponseNG resp;
uint32_t part_limit = 3000;
PrintAndLogEx(NORMAL, "Collecting "_YELLOW_("%u")" nonces \n", limit);
FILE *fnonces = NULL;
if ((fnonces = fopen(filename, "wb")) == NULL) {
PrintAndLogEx(WARNING, "Could not create file " _YELLOW_("%s"), filename);
return PM3_EFILE;
}
clearCommandBuffer();
uint64_t t1 = msclock();
do {
if (kbd_enter_pressed()) {
PrintAndLogEx(WARNING, "\naborted via keyboard!\n");
break;
}
uint32_t flags = 0;
flags |= initialize ? 0x0001 : 0;
flags |= slow ? 0x0002 : 0;
clearCommandBuffer();
SendCommandMIX(CMD_HF_MIFARE_ACQ_NONCES, blockNo + keyType * 0x100, trgBlockNo + trgKeyType * 0x100, flags, NULL, 0);
if (!WaitForResponseTimeout(CMD_ACK, &resp, 3000)) goto out;
if (resp.oldarg[0]) goto out;
uint32_t items = resp.oldarg[2];
fwrite(resp.data.asBytes, 1, items * 4, fnonces);
fflush(fnonces);
total_num_nonces += items;
if (total_num_nonces > part_limit) {
PrintAndLogEx(INFO, "Total nonces %u\n", total_num_nonces);
part_limit += 3000;
}
acquisition_completed = (total_num_nonces > limit);
initialize = false;
} while (!acquisition_completed);
out:
PrintAndLogEx(SUCCESS, "time: %" PRIu64 " seconds\n", (msclock() - t1) / 1000);
if (fnonces) {
fflush(fnonces);
fclose(fnonces);
}
clearCommandBuffer();
SendCommandMIX(CMD_HF_MIFARE_ACQ_NONCES, blockNo + keyType * 0x100, trgBlockNo + trgKeyType * 0x100, 4, NULL, 0);
return PM3_SUCCESS;
}
*/
static int CmdHF14AMfAuth4(const char *Cmd) {
uint8_t keyn[20] = {0};
int keynlen = 0;
uint8_t key[16] = {0};
int keylen = 0;
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf mf auth4",
"Executes AES authentication command in ISO14443-4",
"hf mf auth4 4000 000102030405060708090a0b0c0d0e0f -> executes authentication\n"
"hf mf auth4 9003 FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -> executes authentication\n");
void *argtable[] = {
arg_param_begin,
arg_str1(NULL, NULL, "<Key Num (HEX 2 bytes)>", NULL),
arg_str1(NULL, NULL, "<Key Value (HEX 16 bytes)>", NULL),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
CLIGetHexWithReturn(ctx, 1, keyn, &keynlen);
CLIGetHexWithReturn(ctx, 2, key, &keylen);
CLIParserFree(ctx);
if (keynlen != 2) {
PrintAndLogEx(ERR, "<Key Num> must be 2 bytes long instead of: %d", keynlen);
return PM3_ESOFT;
}
if (keylen != 16) {
PrintAndLogEx(ERR, "<Key Value> must be 16 bytes long instead of: %d", keylen);
return PM3_ESOFT;
}
return MifareAuth4(NULL, keyn, key, true, false, true, true, false);
}
// https://www.nxp.com/docs/en/application-note/AN10787.pdf
static int CmdHF14AMfMAD(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf mf mad",
"Checks and prints MIFARE Application Directory (MAD)",
"hf mf mad -> shows MAD if exists\n"
"hf mf mad --aid e103 -k ffffffffffff -b -> shows NDEF data if exists. read card with custom key and key B\n"
"hf mf mad --dch -k ffffffffffff -> decode CardHolder information\n");
void *argtable[] = {
arg_param_begin,
arg_lit0("v", "verbose", "show technical data"),
arg_str0(NULL, "aid", "<aid>", "print all sectors with specified aid"),
arg_str0("k", "key", "<key>", "key for printing sectors"),
arg_lit0("b", "keyb", "use key B for access printing sectors (by default: key A)"),
arg_lit0(NULL, "be", "(optional, BigEndian)"),
arg_lit0(NULL, "dch", "decode Card Holder information"),
arg_str0("f", "file", "<fn>", "load dump file and decode MAD"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
bool verbose = arg_get_lit(ctx, 1);
uint8_t aid[2] = {0};
int aidlen = 0;
CLIGetHexWithReturn(ctx, 2, aid, &aidlen);
uint8_t userkey[6] = {0};
int keylen = 0;
CLIGetHexWithReturn(ctx, 3, userkey, &keylen);
bool keyB = arg_get_lit(ctx, 4);
bool swapmad = arg_get_lit(ctx, 5);
bool decodeholder = arg_get_lit(ctx, 6);
int fnlen = 0;
char filename[FILE_PATH_SIZE] = {0};
CLIParamStrToBuf(arg_get_str(ctx, 7), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
CLIParserFree(ctx);
if (fnlen > 0) {
// read dump file
uint8_t *dump = NULL;
size_t bytes_read = 0;
int res = pm3_load_dump(filename, (void **)&dump, &bytes_read, (MFBLOCK_SIZE * MIFARE_4K_MAXBLOCK));
if (res != PM3_SUCCESS) {
return res;
}
uint16_t block_cnt = MIN(MIFARE_1K_MAXBLOCK, (bytes_read / MFBLOCK_SIZE));
if (bytes_read == 320)
block_cnt = MIFARE_MINI_MAXBLOCK;
else if (bytes_read == 2048)
block_cnt = MIFARE_2K_MAXBLOCK;
else if (bytes_read == 4096)
block_cnt = MIFARE_4K_MAXBLOCK;
if (verbose) {
PrintAndLogEx(INFO, "File: " _YELLOW_("%s"), filename);
PrintAndLogEx(INFO, "File size %zu bytes, file blocks %d (0x%x)", bytes_read, block_cnt, block_cnt);
}
// MAD detection
if (HasMADKey(dump) == false) {
PrintAndLogEx(FAILED, "No MAD key was detected in the dump file");
free(dump);
return PM3_ESOFT;
}
MADPrintHeader();
bool haveMAD2 = false;
MAD1DecodeAndPrint(dump, swapmad, verbose, &haveMAD2);
int sector = DetectHID(dump, 0x484d);
if (sector > -1) {
// decode it
PrintAndLogEx(INFO, "");
PrintAndLogEx(INFO, _CYAN_("HID PACS detected"));
uint8_t pacs_sector[MFBLOCK_SIZE * 3] = {0};
memcpy(pacs_sector, dump + (sector * 4 * 16), sizeof(pacs_sector));
if (pacs_sector[16] == 0x02) {
PrintAndLogEx(SUCCESS, "Raw...... " _GREEN_("%s"), sprint_hex_inrow(pacs_sector + 24, 8));
//todo: remove preamble/sentinel
uint32_t top = 0, mid = 0, bot = 0;
char hexstr[16 + 1] = {0};
hex_to_buffer((uint8_t *)hexstr, pacs_sector + 24, 8, sizeof(hexstr) - 1, 0, 0, true);
hexstring_to_u96(&top, &mid, &bot, hexstr);
char binstr[64 + 1];
hextobinstring(binstr, hexstr);
char *pbin = binstr;
while (strlen(pbin) && *(++pbin) == '0');
PrintAndLogEx(SUCCESS, "Binary... " _GREEN_("%s"), pbin);
PrintAndLogEx(INFO, "Wiegand decode");
wiegand_message_t packed = initialize_message_object(top, mid, bot, 0);
HIDTryUnpack(&packed);
}
}
free(dump);
return PM3_SUCCESS;
}
if (g_session.pm3_present == false)
return PM3_ENOTTY;
uint8_t sector0[16 * 4] = {0};
uint8_t sector10[16 * 4] = {0};
bool got_first = true;
if (mfReadSector(MF_MAD1_SECTOR, MF_KEY_A, (uint8_t *)g_mifare_mad_key, sector0) != PM3_SUCCESS) {
PrintAndLogEx(WARNING, "error, read sector 0. card doesn't have MAD or doesn't have MAD on default keys");
got_first = false;
} else {
PrintAndLogEx(INFO, "Authentication ( " _GREEN_("ok") " )");
}
// User supplied key
if (got_first == false && keylen == 6) {
PrintAndLogEx(INFO, "Trying user specified key...");
if (mfReadSector(MF_MAD1_SECTOR, MF_KEY_A, userkey, sector0) != PM3_SUCCESS) {
PrintAndLogEx(ERR, "error, read sector 0. card doesn't have MAD or the custom key is wrong");
} else {
PrintAndLogEx(INFO, "Authentication ( " _GREEN_("ok") " )");
got_first = true;
}
}
// Both default and user supplied key failed
if (got_first == false) {
return PM3_ESOFT;
}
MADPrintHeader();
bool haveMAD2 = false;
MAD1DecodeAndPrint(sector0, swapmad, verbose, &haveMAD2);
if (haveMAD2) {
if (mfReadSector(MF_MAD2_SECTOR, MF_KEY_A, (uint8_t *)g_mifare_mad_key, sector10)) {
PrintAndLogEx(ERR, "error, read sector 0x10. card doesn't have MAD or doesn't have MAD on default keys");
return PM3_ESOFT;
}
MAD2DecodeAndPrint(sector10, swapmad, verbose);
}
if (aidlen == 2 || decodeholder) {
uint16_t mad[7 + 8 + 8 + 8 + 8] = {0};
size_t madlen = 0;
if (MADDecode(sector0, sector10, mad, &madlen, swapmad)) {
PrintAndLogEx(ERR, "can't decode MAD");
return PM3_ESOFT;
}
// copy default NDEF key
uint8_t akey[6] = {0};
memcpy(akey, g_mifare_ndef_key, 6);
// user specified key
if (keylen == 6) {
memcpy(akey, userkey, 6);
}
uint16_t aaid = 0x0004;
if (aidlen == 2) {
aaid = (aid[0] << 8) + aid[1];
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "-------------- " _CYAN_("AID 0x%04x") " ---------------", aaid);
for (int i = 0; i < madlen; i++) {
if (aaid == mad[i]) {
uint8_t vsector[16 * 4] = {0};
if (mfReadSector(i + 1, keyB ? MF_KEY_B : MF_KEY_A, akey, vsector)) {
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(ERR, "error, read sector %d", i + 1);
return PM3_ESOFT;
}
for (int j = 0; j < (verbose ? 4 : 3); j ++)
PrintAndLogEx(NORMAL, " [%03d] %s", (i + 1) * 4 + j, sprint_hex(&vsector[j * 16], 16));
}
}
}
if (decodeholder) {
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "-------- " _CYAN_("Card Holder Info 0x%04x") " --------", aaid);
uint8_t data[4096] = {0};
int datalen = 0;
for (int i = 0; i < madlen; i++) {
if (aaid == mad[i]) {
uint8_t vsector[16 * 4] = {0};
if (mfReadSector(i + 1, keyB ? MF_KEY_B : MF_KEY_A, akey, vsector)) {
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(ERR, "error, read sector %d", i + 1);
return PM3_ESOFT;
}
memcpy(&data[datalen], vsector, 16 * 3);
datalen += 16 * 3;
}
}
if (!datalen) {
PrintAndLogEx(WARNING, "no Card Holder Info data");
return PM3_SUCCESS;
}
MADCardHolderInfoDecode(data, datalen, verbose);
}
}
if (verbose) {
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "------------ " _CYAN_("MAD sector raw") " -------------");
for (int i = 0; i < 4; i ++)
PrintAndLogEx(INFO, "[%d] %s", i, sprint_hex(&sector0[i * 16], 16));
}
return PM3_SUCCESS;
}
int CmdHFMFNDEFRead(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf mf ndefread",
"Prints NFC Data Exchange Format (NDEF)",
"hf mf ndefread -> shows NDEF parsed data\n"
"hf mf ndefread -vv -> shows NDEF parsed and raw data\n"
"hf mf ndefread --aid e103 -k ffffffffffff -b -> shows NDEF data with custom AID, key and with key B\n"
"hf mf ndefread -f myfilename -> save raw NDEF to file"
);
void *argtable[] = {
arg_param_begin,
arg_litn("v", "verbose", 0, 2, "show technical data"),
arg_str0(NULL, "aid", "<aid>", "replace default aid for NDEF"),
arg_str0("k", "key", "<key>", "replace default key for NDEF"),
arg_lit0("b", "keyb", "use key B for access sectors (by default: key A)"),
arg_str0("f", "file", "<fn>", "save raw NDEF to file"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
bool verbose = arg_get_lit(ctx, 1);
bool verbose2 = arg_get_lit(ctx, 1) > 1;
uint8_t aid[2] = {0};
int aidlen;
CLIGetHexWithReturn(ctx, 2, aid, &aidlen);
uint8_t key[6] = {0};
int keylen;
CLIGetHexWithReturn(ctx, 3, key, &keylen);
bool keyB = arg_get_lit(ctx, 4);
int fnlen = 0;
char filename[FILE_PATH_SIZE] = {0};
CLIParamStrToBuf(arg_get_str(ctx, 5), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
CLIParserFree(ctx);
uint16_t ndef_aid = NDEF_MFC_AID;
if (aidlen == 2)
ndef_aid = (aid[0] << 8) + aid[1];
uint8_t ndefkey[6] = {0};
memcpy(ndefkey, g_mifare_ndef_key, 6);
if (keylen == 6) {
memcpy(ndefkey, key, 6);
}
uint8_t sector0[MFBLOCK_SIZE * 4] = {0};
uint8_t sector10[MFBLOCK_SIZE * 4] = {0};
uint8_t data[4096] = {0};
int datalen = 0;
if (verbose)
PrintAndLogEx(INFO, "reading MAD v1 sector");
if (mfReadSector(MF_MAD1_SECTOR, MF_KEY_A, g_mifare_mad_key, sector0)) {
PrintAndLogEx(ERR, "error, read sector 0. card doesn't have MAD or doesn't have MAD on default keys");
PrintAndLogEx(HINT, "Try " _YELLOW_("`hf mf ndefread -k `") " with your custom key");
return PM3_ESOFT;
}
bool haveMAD2 = false;
int res = MADCheck(sector0, NULL, verbose, &haveMAD2);
if (res != PM3_SUCCESS) {
PrintAndLogEx(ERR, "MAD error %d", res);
return res;
}
if (haveMAD2) {
if (verbose)
PrintAndLogEx(INFO, "reading MAD v2 sector");
if (mfReadSector(MF_MAD2_SECTOR, MF_KEY_A, g_mifare_mad_key, sector10)) {
PrintAndLogEx(ERR, "error, read sector 0x10. card doesn't have MAD or doesn't have MAD on default keys");
PrintAndLogEx(HINT, "Try " _YELLOW_("`hf mf ndefread -k `") " with your custom key");
return PM3_ESOFT;
}
}
uint16_t mad[7 + 8 + 8 + 8 + 8] = {0};
size_t madlen = 0;
res = MADDecode(sector0, (haveMAD2 ? sector10 : NULL), mad, &madlen, false);
if (res != PM3_SUCCESS) {
PrintAndLogEx(ERR, "can't decode MAD");
return res;
}
PrintAndLogEx(INFO, "reading data from tag");
for (int i = 1; i <= madlen; i++) {
if (ndef_aid == mad[i]) {
uint8_t vsector[MFBLOCK_SIZE * 4] = {0};
if (mfReadSector(i, keyB ? MF_KEY_B : MF_KEY_A, ndefkey, vsector)) {
PrintAndLogEx(ERR, "error, reading sector %d ", i + 1);
return PM3_ESOFT;
}
memcpy(&data[datalen], vsector, MFBLOCK_SIZE * 3);
datalen += MFBLOCK_SIZE * 3;
PrintAndLogEx(INPLACE, "%d", i);
}
}
PrintAndLogEx(NORMAL, "");
if (datalen == 0) {
PrintAndLogEx(WARNING, "no NDEF data");
return PM3_SUCCESS;
}
if (verbose2) {
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "--- " _CYAN_("MFC NDEF raw") " ----------------");
print_buffer(data, datalen, 1);
}
if (fnlen != 0) {
saveFile(filename, ".bin", data, datalen);
}
res = NDEFDecodeAndPrint(data, datalen, verbose);
if (res != PM3_SUCCESS) {
PrintAndLogEx(INFO, "Trying to parse NDEF records w/o NDEF header");
res = NDEFRecordsDecodeAndPrint(data, datalen, verbose);
}
if (verbose == false) {
PrintAndLogEx(HINT, "Try " _YELLOW_("`hf mf ndefread -v`") " for more details");
} else {
if (verbose2 == false) {
PrintAndLogEx(HINT, "Try " _YELLOW_("`hf mf ndefread -vv`") " for more details");
}
}
return PM3_SUCCESS;
}
// https://www.nxp.com/docs/en/application-note/AN1305.pdf
int CmdHFMFNDEFFormat(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf mf ndefformat",
"format MIFARE Classic Tag as a NFC tag with Data Exchange Format (NDEF)\n"
"If no <name> given, UID will be used as filename. \n"
"It will try default keys and MAD keys to detect if tag is already formatted in order to write.\n"
"\n"
"If not, it will try finding a key file based on your UID. ie, if you ran autopwn before",
"hf mf ndefformat\n"
// "hf mf ndefformat --mini --> MIFARE Mini\n"
"hf mf ndefformat --1k --> MIFARE Classic 1k\n"
// "hf mf ndefformat --2k --> MIFARE 2k\n"
// "hf mf ndefformat --4k --> MIFARE 4k\n"
"hf mf ndefformat --keys hf-mf-01020304-key.bin --> MIFARE 1k with keys from specified file\n"
);
void *argtable[] = {
arg_param_begin,
arg_str0("k", "keys", "<fn>", "filename of keys"),
arg_lit0(NULL, "mini", "MIFARE Classic Mini / S20"),
arg_lit0(NULL, "1k", "MIFARE Classic 1k / S50 (def)"),
arg_lit0(NULL, "2k", "MIFARE Classic/Plus 2k"),
arg_lit0(NULL, "4k", "MIFARE Classic 4k / S70"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
int keyfnlen = 0;
char keyFilename[FILE_PATH_SIZE] = {0};
CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)keyFilename, FILE_PATH_SIZE, &keyfnlen);
bool m0 = arg_get_lit(ctx, 2);
bool m1 = arg_get_lit(ctx, 3);
bool m2 = arg_get_lit(ctx, 4);
bool m4 = arg_get_lit(ctx, 5);
CLIParserFree(ctx);
// validations
if ((m0 + m1 + m2 + m4) > 1) {
PrintAndLogEx(WARNING, "Only specify one MIFARE Type");
return PM3_EINVARG;
} else if ((m0 + m1 + m2 + m4) == 0) {
m1 = true;
}
uint8_t numSectors = MIFARE_1K_MAXSECTOR;
if (m0) {
numSectors = MIFARE_MINI_MAXSECTOR;
} else if (m1) {
numSectors = MIFARE_1K_MAXSECTOR;
} else if (m2) {
numSectors = MIFARE_2K_MAXSECTOR;
} else if (m4) {
numSectors = MIFARE_4K_MAXSECTOR;
} else {
PrintAndLogEx(WARNING, "Please specify a MIFARE Type");
return PM3_EINVARG;
}
if (g_session.pm3_present == false)
return PM3_ENOTTY;
// Select card to get UID/UIDLEN/ATQA/SAK information
clearCommandBuffer();
SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0);
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) {
PrintAndLogEx(WARNING, "iso14443a card select timeout");
return PM3_ETIMEOUT;
}
uint64_t select_status = resp.oldarg[0];
if (select_status == 0) {
PrintAndLogEx(WARNING, "iso14443a card select failed");
return PM3_SUCCESS;
}
DropField();
// init keys to default key
uint8_t keyA[MIFARE_4K_MAXSECTOR][MFKEY_SIZE];
uint8_t keyB[MIFARE_4K_MAXSECTOR][MFKEY_SIZE];
for (uint8_t i = 0; i < MIFARE_4K_MAXSECTOR; i++) {
memcpy(keyA[i], g_mifare_default_key, sizeof(g_mifare_default_key));
memcpy(keyB[i], g_mifare_default_key, sizeof(g_mifare_default_key));
}
// test if MAD key is used
uint64_t key64 = 0;
// check if we can authenticate to sector
if (mfCheckKeys(0, MF_KEY_A, true, 1, (uint8_t *)g_mifare_mad_key, &key64) == PM3_SUCCESS) {
// if used, assume KEY A is MAD/NDEF set.
memcpy(keyA[0], g_mifare_mad_key, sizeof(g_mifare_mad_key));
memcpy(keyB[0], g_mifare_mad_key_b, sizeof(g_mifare_mad_key_b));
for (uint8_t i = 1; i < MIFARE_4K_MAXSECTOR; i++) {
memcpy(keyA[i], g_mifare_ndef_key, sizeof(g_mifare_ndef_key));
}
}
// Do we have a keyfile based from UID?
if (keyfnlen == 0) {
char *fptr = GenerateFilename("hf-mf-", "-key.bin");
if (fptr) {
strcpy(keyFilename, fptr);
}
free(fptr);
DropField();
}
// load key file if exist
if (strlen(keyFilename)) {
FILE *f;
if ((f = fopen(keyFilename, "rb")) == NULL) {
// PrintAndLogEx(WARNING, "Could not find file " _YELLOW_("%s"), keyFilename);
goto skipfile;
}
PrintAndLogEx(INFO, "Using `" _YELLOW_("%s") "`", keyFilename);
// Read keys A from file
size_t bytes_read;
for (uint8_t i = 0; i < numSectors; i++) {
bytes_read = fread(keyA[i], 1, MFKEY_SIZE, f);
if (bytes_read != MFKEY_SIZE) {
PrintAndLogEx(ERR, "File reading error.");
fclose(f);
return PM3_EFILE;
}
}
// Read keys B from file
for (uint8_t i = 0; i < numSectors; i++) {
bytes_read = fread(keyB[i], 1, MFKEY_SIZE, f);
if (bytes_read != MFKEY_SIZE) {
PrintAndLogEx(ERR, "File reading error.");
fclose(f);
return PM3_EFILE;
}
}
fclose(f);
}
skipfile:
;
uint8_t firstblocks[8][16] = {
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x14, 0x01, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1 },
{ 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1 },
{ 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0x78, 0x77, 0x88, 0xC1, 0x89, 0xEC, 0xA9, 0x7F, 0x8C, 0x2A },
{ 0x03, 0x00, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0xD3, 0xF7, 0xD3, 0xF7, 0xD3, 0xF7, 0x7F, 0x07, 0x88, 0x40, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF },
};
// main loop
for (int i = 0; i < numSectors; i++) {
for (int j = 0; j < mfNumBlocksPerSector(j); j++) {
uint8_t b = (mfFirstBlockOfSector(i) + j);
uint8_t block[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
switch (b) {
case 0:
continue;
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
memcpy(block, firstblocks[b], MFBLOCK_SIZE);
break;
default: {
if (mfIsSectorTrailer(j)) {
// ST NDEF
memcpy(block, firstblocks[7], MFBLOCK_SIZE);
}
break;
}
}
// write to card, try B key first
if (mf_write_block(keyB[i], MF_KEY_B, b, block) == 0) {
// try A key,
if (mf_write_block(keyA[i], MF_KEY_A, b, block) == 0) {
return PM3_EFAILED;
}
}
PrintAndLogEx(INPLACE, "Formatting block %u", b);
}
}
PrintAndLogEx(NORMAL, "");
return PM3_SUCCESS;
}
int CmdHFMFNDEFWrite(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf mf ndefwrite",
"Write raw NDEF hex bytes to tag. This commands assumes tag already been NFC/NDEF formatted.\n",
"hf mf ndefwrite -d 0300FE -> write empty record to tag\n"
"hf mf ndefwrite -f myfilename\n"
"hf mf ndefwrite -d 033fd1023a53709101195405656e2d55534963656d616e2054776974746572206c696e6b5101195502747769747465722e636f6d2f686572726d616e6e31303031\n"
);
void *argtable[] = {
arg_param_begin,
arg_str0("d", NULL, "<hex>", "raw NDEF hex bytes"),
arg_str0("f", "file", "<fn>", "write raw NDEF file to tag"),
arg_lit0("p", NULL, "fix NDEF record headers / terminator block if missing"),
arg_lit0(NULL, "mini", "MIFARE Classic Mini / S20"),
arg_lit0(NULL, "1k", "MIFARE Classic 1k / S50 (def)"),
arg_lit0(NULL, "2k", "MIFARE Classic/Plus 2k"),
arg_lit0(NULL, "4k", "MIFARE Classic 4k / S70"),
arg_lit0("v", "verbose", "verbose output"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, false);
uint8_t raw[4096] = {0};
int rawlen;
CLIGetHexWithReturn(ctx, 1, raw, &rawlen);
int fnlen = 0;
char filename[FILE_PATH_SIZE] = {0};
CLIParamStrToBuf(arg_get_str(ctx, 2), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
bool fix_msg = arg_get_lit(ctx, 3);
bool m0 = arg_get_lit(ctx, 4);
bool m1 = arg_get_lit(ctx, 5);
bool m2 = arg_get_lit(ctx, 6);
bool m4 = arg_get_lit(ctx, 7);
bool verbose = arg_get_lit(ctx, 8);
CLIParserFree(ctx);
// validations
if ((m0 + m1 + m2 + m4) > 1) {
PrintAndLogEx(WARNING, "Only specify one MIFARE Type");
return PM3_EINVARG;
} else if ((m0 + m1 + m2 + m4) == 0) {
m1 = true;
}
uint8_t numSectors = MIFARE_1K_MAXSECTOR;
if (m0) {
numSectors = MIFARE_MINI_MAXSECTOR;
} else if (m1) {
numSectors = MIFARE_1K_MAXSECTOR;
} else if (m2) {
numSectors = MIFARE_2K_MAXSECTOR;
} else if (m4) {
numSectors = MIFARE_4K_MAXSECTOR;
} else {
PrintAndLogEx(WARNING, "Please specify a MIFARE Type");
return PM3_EINVARG;
}
if (verbose) {
PrintAndLogEx(INFO, "Number of sectors selected: %u", numSectors);
}
if (g_session.pm3_present == false) {
PrintAndLogEx(FAILED, "No Proxmark3 device present");
return PM3_ENOTTY;
}
if ((rawlen && fnlen) || (rawlen == 0 && fnlen == 0)) {
PrintAndLogEx(WARNING, "Please specify either raw hex or filename");
return PM3_EINVARG;
}
// test if MAD key is used
uint64_t key64 = 0;
// check if we can authenticate to sector
int res = mfCheckKeys(0, MF_KEY_A, true, 1, (uint8_t *)g_mifare_mad_key, &key64);
if (res != PM3_SUCCESS) {
PrintAndLogEx(FAILED, "Sector 0 failed to authenticate with MAD default key");
PrintAndLogEx(HINT, "Verify that the tag NDEF formatted");
return res;
}
// NDEF for MIFARE CLASSIC has different memory size available.
int32_t bytes = rawlen;
// read dump file
if (fnlen) {
uint8_t *dump = NULL;
size_t bytes_read = 0;
res = pm3_load_dump(filename, (void **)&dump, &bytes_read, sizeof(raw));
if (res != PM3_SUCCESS) {
return res;
}
memcpy(raw, dump, bytes_read);
bytes = bytes_read;
free(dump);
}
// Has raw bytes ndef message header?bytes
switch (raw[0]) {
case 0x00:
case 0x01:
case 0x02:
case 0x03:
case 0xFD:
case 0xFE:
break;
default: {
if (fix_msg == false) {
PrintAndLogEx(WARNING, "raw NDEF message doesn't have a proper header, continuing...");
} else {
if (bytes + 2 > sizeof(raw)) {
PrintAndLogEx(WARNING, "no room for header, exiting...");
return PM3_EMALLOC;
}
uint8_t tmp_raw[4096];
memcpy(tmp_raw, raw, sizeof(tmp_raw));
raw[0] = 0x03;
raw[1] = bytes;
memcpy(raw + 2, tmp_raw, sizeof(raw) - 2);
bytes += 2;
PrintAndLogEx(SUCCESS, "Added generic message header (0x03)");
}
}
}
// Has raw bytes ndef a terminator block?
if (raw[bytes - 1] != 0xFE) {
if (fix_msg == false) {
PrintAndLogEx(WARNING, "raw NDEF message doesn't have a terminator block, continuing...");
} else {
if (bytes + 1 > sizeof(raw)) {
PrintAndLogEx(WARNING, "no room for terminator block, exiting...");
return PM3_EMALLOC;
}
raw[bytes] = 0xFE;
bytes++;
PrintAndLogEx(SUCCESS, "Added terminator block (0xFE)");
}
}
if (verbose) {
PrintAndLogEx(INFO, "Num of Bytes... %u", bytes);
print_buffer(raw, bytes, 0);
}
// read MAD Sector 0, block1,2
uint8_t sector0[MFBLOCK_SIZE * 4] = {0};
if (mfReadSector(MF_MAD1_SECTOR, MF_KEY_A, g_mifare_mad_key, sector0)) {
PrintAndLogEx(ERR, "error, read sector 0. card doesn't have MAD or doesn't have MAD on default keys");
PrintAndLogEx(HINT, "Try " _YELLOW_("`hf mf ndefread -k `") " with your custom key");
return PM3_ESOFT;
}
// decode MAD
uint16_t mad[7 + 8 + 8 + 8 + 8] = {0};
size_t madlen = 0;
res = MADDecode(sector0, NULL, mad, &madlen, false);
if (res != PM3_SUCCESS) {
PrintAndLogEx(ERR, "can't decode MAD");
return res;
}
// how much memory do I have available ?
// Skip sector 0 since its used for MAD
uint8_t freemem[MIFARE_4K_MAXSECTOR] = {0};
uint16_t sum = 0;
uint8_t block_no = 0;
for (uint8_t i = 1; i < madlen; i++) {
freemem[i] = (mad[i] == NDEF_MFC_AID);
if (freemem[i]) {
if (block_no == 0) {
block_no = mfFirstBlockOfSector(i);
}
if (verbose) {
PrintAndLogEx(INFO, "Sector %u is NDEF formatted", i);
}
sum += (MFBLOCK_SIZE * 3);
}
}
if (verbose) {
PrintAndLogEx(INFO, "Total avail ndef mem... %u", sum);
PrintAndLogEx(INFO, "First block............ %u", block_no);
}
if (sum < bytes) {
PrintAndLogEx(WARNING, "Raw NDEF message is larger than available NDEF formatted memory");
return PM3_EINVARG;
}
// main loop - write blocks
uint8_t *ptr_raw = raw;
while (bytes > 0) {
uint8_t block[MFBLOCK_SIZE] = { 0x00 };
if (bytes < MFBLOCK_SIZE) {
memcpy(block, ptr_raw, bytes);
} else {
memcpy(block, ptr_raw, MFBLOCK_SIZE);
ptr_raw += MFBLOCK_SIZE;
}
// write to card, try B key first
if (mf_write_block(g_mifare_default_key, MF_KEY_B, block_no, block) == 0) {
// try A key,
if (mf_write_block(g_mifare_ndef_key, MF_KEY_A, block_no, block) == 0) {
return PM3_EFAILED;
}
}
PrintAndLogEx(INPLACE, "%u", block_no);
// find next available block
block_no++;
if (mfIsSectorTrailer(block_no)) {
block_no++;
// skip sectors which isn't ndef formatted
while (freemem[mfSectorNum(block_no)] == 0) {
block_no++;
}
}
bytes -= MFBLOCK_SIZE;
}
PrintAndLogEx(NORMAL, "");
return PM3_SUCCESS;
}
static int CmdHFMFPersonalize(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf mf personalize",
"Personalize the UID of a MIFARE Classic EV1 card. This is only possible \n"
"if it is a 7Byte UID card and if it is not already personalized.",
"hf mf personalize --f0 -> double size UID\n"
"hf mf personalize --f1 -> double size UID, optional usage of selection process shortcut\n"
"hf mf personalize --f2 -> single size random ID\n"
"hf mf personalize --f3 -> single size NUID\n"
"hf mf personalize -b -k B0B1B2B3B4B5 --f3 -> use key B = 0xB0B1B2B3B4B5"
);
void *argtable[] = {
arg_param_begin,
arg_lit0("a", NULL, "use key A to authenticate sector 0 (def)"),
arg_lit0("b", NULL, "use key B to authenticate sector 0"),
arg_str0("k", "key", "<hex>", "key (def FFFFFFFFFFFF)"),
arg_lit0(NULL, "f0", "UIDFO, double size UID"),
arg_lit0(NULL, "f1", "UIDF1, double size UID, optional usage of selection process shortcut"),
arg_lit0(NULL, "f2", "UIDF2, single size random ID"),
arg_lit0(NULL, "f3", "UIDF3, single size NUID"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
bool use_a = arg_get_lit(ctx, 1);
bool use_b = arg_get_lit(ctx, 2);
if (use_a + use_b > 1) {
PrintAndLogEx(ERR, "error, use only one key type");
CLIParserFree(ctx);
return PM3_EINVARG;
}
uint8_t keytype = 0;
if (use_b) {
keytype = 1;
}
uint8_t key[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
int key_len;
int res = CLIParamHexToBuf(arg_get_str(ctx, 3), key, 6, &key_len);
if (res || (!res && key_len && key_len != 6)) {
PrintAndLogEx(ERR, "ERROR: not a valid key. Key must be 12 hex digits");
CLIParserFree(ctx);
return PM3_EINVARG;
}
bool f0 = arg_get_lit(ctx, 4);
bool f1 = arg_get_lit(ctx, 5);
bool f2 = arg_get_lit(ctx, 6);
bool f3 = arg_get_lit(ctx, 7);
CLIParserFree(ctx);
uint8_t tmp = f0 + f1 + f2 + f3;
if (tmp > 1) {
PrintAndLogEx(WARNING, "select only one key type");
return PM3_EINVARG;
}
if (tmp == 0) {
PrintAndLogEx(WARNING, "select one key type");
return PM3_EINVARG;
}
uint8_t pers_option = MIFARE_EV1_UIDF3;
if (f0) {
pers_option = MIFARE_EV1_UIDF0;
} else if (f1) {
pers_option = MIFARE_EV1_UIDF1;
} else if (f2) {
pers_option = MIFARE_EV1_UIDF2;
}
CLIParserFree(ctx);
struct {
uint8_t keytype;
uint8_t pers_option;
uint8_t key[6];
} PACKED payload;
payload.keytype = keytype;
payload.pers_option = pers_option;
memcpy(payload.key, key, sizeof(payload.key));
clearCommandBuffer();
SendCommandNG(CMD_HF_MIFARE_PERSONALIZE_UID, (uint8_t *)&payload, sizeof(payload));
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_HF_MIFARE_PERSONALIZE_UID, &resp, 2500) == false) {
return PM3_ETIMEOUT;
}
if (resp.status == PM3_SUCCESS) {
PrintAndLogEx(SUCCESS, "Personalization ( %s )", _GREEN_("ok"));
} else {
PrintAndLogEx(FAILED, "Personalization ( %s )", _RED_("fail"));
}
return PM3_SUCCESS;
}
static int CmdHF14AMfList(const char *Cmd) {
return CmdTraceListAlias(Cmd, "hf mf", "mf");
}
static int CmdHf14AGen3UID(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf mf gen3uid",
"Set UID for magic Gen3 card _without_ changes to manufacturer block 0",
"hf mf gen3uid --uid 01020304 --> set 4 byte uid\n"
"hf mf gen3uid --uid 01020304050607 --> set 7 byte uid"
);
void *argtable[] = {
arg_param_begin,
arg_str0("u", "uid", "<hex>", "UID 4/7 hex bytes"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
uint8_t uid[7] = {0};
int uidlen = 0;
CLIGetHexWithReturn(ctx, 1, uid, &uidlen);
CLIParserFree(ctx);
// sanity checks
if (uidlen != 4 && uidlen != 7) {
PrintAndLogEx(FAILED, "UID must be 4 or 7 hex bytes. Got %d", uidlen);
return PM3_EINVARG;
}
uint8_t old_uid[10] = {0};
int res = mfGen3UID(uid, uidlen, old_uid);
if (res != PM3_SUCCESS) {
PrintAndLogEx(ERR, "Can't set UID");
PrintAndLogEx(HINT, "Are you sure your card is a Gen3 ?");
return PM3_ESOFT;
}
PrintAndLogEx(SUCCESS, "Old UID... %s", sprint_hex(old_uid, uidlen));
PrintAndLogEx(SUCCESS, "New UID... %s", sprint_hex(uid, uidlen));
return PM3_SUCCESS;
}
static int CmdHf14AGen3Block(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf mf gen3blk",
"Overwrite full manufacturer block for magic Gen3 card\n"
" - You can specify part of manufacturer block as\n"
" 4/7-bytes for UID change only\n"
"\n"
"NOTE: BCC, SAK, ATQA will be calculated automatically"
,
"hf mf gen3blk --> print current data\n"
"hf mf gen3blk -d 01020304 --> set 4 byte uid\n"
"hf mf gen3blk -d 01020304050607 --> set 7 byte uid \n"
"hf mf gen3blk -d 01020304FFFFFFFF0102030405060708"
);
void *argtable[] = {
arg_param_begin,
arg_str0("d", "data", "<hex>", "manufacturer block data up to 16 hex bytes"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
uint8_t data[MFBLOCK_SIZE] = {0x00};
int datalen = 0;
CLIGetHexWithReturn(ctx, 1, data, &datalen);
CLIParserFree(ctx);
uint8_t new_block[MFBLOCK_SIZE] = {0x00};
int res = mfGen3Block(data, datalen, new_block);
if (res) {
PrintAndLogEx(ERR, "Can't change manufacturer block data. error %d", res);
return PM3_ESOFT;
}
PrintAndLogEx(SUCCESS, "Current block... %s", sprint_hex_inrow(new_block, sizeof(new_block)));
return PM3_SUCCESS;
}
static int CmdHf14AGen3Freeze(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf mf gen3freeze",
"Perma lock further UID changes. No more UID changes available after operation completed\n"
"\nNote: operation is " _RED_("! irreversible !"),
"hf mf gen3freeze -y"
);
void *argtable[] = {
arg_param_begin,
arg_lit1("y", "yes", "confirm UID lock operation"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, false);
bool confirm = arg_get_lit(ctx, 1);
CLIParserFree(ctx);
if (confirm == false) {
PrintAndLogEx(INFO, "please confirm that you want to perma lock the card");
return PM3_SUCCESS;
}
int res = mfGen3Freeze();
if (res != PM3_SUCCESS) {
PrintAndLogEx(ERR, "Can't lock UID changes. error %d", res);
} else {
PrintAndLogEx(SUCCESS, "MFC Gen3 UID card is now perma-locked");
}
return res;
}
static int CmdHf14AMfSuperCard(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf mf supercard",
"Extract info from a `super card`",
"hf mf supercard");
void *argtable[] = {
arg_param_begin,
arg_lit0("r", "reset", "reset card"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
bool reset_card = arg_get_lit(ctx, 1);
CLIParserFree(ctx);
bool activate_field = true;
bool keep_field_on = true;
int res = 0;
if (reset_card) {
keep_field_on = false;
uint8_t response[6];
int resplen = 0;
// --------------- RESET CARD ----------------
uint8_t aRESET[] = { 0x00, 0xa6, 0xc0, 0x00 };
res = ExchangeAPDU14a(aRESET, sizeof(aRESET), activate_field, keep_field_on, response, sizeof(response), &resplen);
if (res != PM3_SUCCESS) {
PrintAndLogEx(FAILED, "Super card reset [ " _RED_("fail") " ]");
DropField();
return res;
}
PrintAndLogEx(SUCCESS, "Super card reset ( " _GREEN_("ok") " )");
return PM3_SUCCESS;
}
uint8_t responseA[22];
uint8_t responseB[22];
int respAlen = 0;
int respBlen = 0;
// --------------- First ----------------
uint8_t aFIRST[] = { 0x00, 0xa6, 0xb0, 0x00, 0x10 };
res = ExchangeAPDU14a(aFIRST, sizeof(aFIRST), activate_field, keep_field_on, responseA, sizeof(responseA), &respAlen);
if (res != PM3_SUCCESS) {
DropField();
return res;
}
// --------------- Second ----------------
activate_field = false;
keep_field_on = false;
uint8_t aSECOND[] = { 0x00, 0xa6, 0xb0, 0x01, 0x10 };
res = ExchangeAPDU14a(aSECOND, sizeof(aSECOND), activate_field, keep_field_on, responseB, sizeof(responseB), &respBlen);
if (res != PM3_SUCCESS) {
DropField();
return res;
}
// uint8_t inA[] = { 0x72, 0xD7, 0xF4, 0x3E, 0xFD, 0xAB, 0xF2, 0x35, 0xFD, 0x49, 0xEE, 0xDC, 0x44, 0x95, 0x43, 0xC4};
// uint8_t inB[] = { 0xF0, 0xA2, 0x67, 0x6A, 0x04, 0x6A, 0x72, 0x12, 0x76, 0xA4, 0x1D, 0x02, 0x1F, 0xEA, 0x20, 0x85};
uint8_t outA[16] = {0};
uint8_t outB[16] = {0};
uint8_t key[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88};
for (uint8_t i = 0; i < 16; i += 8) {
des_decrypt(outA + i, responseA + i, key);
des_decrypt(outB + i, responseB + i, key);
}
PrintAndLogEx(DEBUG, " in : %s", sprint_hex_inrow(responseA, respAlen));
PrintAndLogEx(DEBUG, "out : %s", sprint_hex_inrow(outA, sizeof(outA)));
PrintAndLogEx(DEBUG, " in : %s", sprint_hex_inrow(responseB, respAlen));
PrintAndLogEx(DEBUG, "out : %s", sprint_hex_inrow(outB, sizeof(outB)));
if (memcmp(outA, "\x01\x01\x01\x01\x01\x01\x01\x01", 8) == 0) {
PrintAndLogEx(INFO, "No trace recorded");
return PM3_SUCCESS;
}
// second trace?
if (memcmp(outB, "\x01\x01\x01\x01\x01\x01\x01\x01", 8) == 0) {
PrintAndLogEx(INFO, "Only one trace recorded");
return PM3_SUCCESS;
}
nonces_t data;
// first
uint16_t NT0 = (outA[6] << 8) | outA[7];
data.cuid = bytes_to_num(outA, 4);
data.nonce = prng_successor(NT0, 31);
data.nr = bytes_to_num(outA + 8, 4);
data.ar = bytes_to_num(outA + 12, 4);
data.at = 0;
// second
NT0 = (outB[6] << 8) | outB[7];
data.nonce2 = prng_successor(NT0, 31);;
data.nr2 = bytes_to_num(outB + 8, 4);
data.ar2 = bytes_to_num(outB + 12, 4);
data.sector = mfSectorNum(outA[5]);
data.keytype = outA[4];
data.state = FIRST;
PrintAndLogEx(DEBUG, "A Sector %02x", data.sector);
PrintAndLogEx(DEBUG, "A NT %08x", data.nonce);
PrintAndLogEx(DEBUG, "A NR %08x", data.nr);
PrintAndLogEx(DEBUG, "A AR %08x", data.ar);
PrintAndLogEx(DEBUG, "");
PrintAndLogEx(DEBUG, "B NT %08x", data.nonce2);
PrintAndLogEx(DEBUG, "B NR %08x", data.nr2);
PrintAndLogEx(DEBUG, "B AR %08x", data.ar2);
uint64_t key64 = -1;
res = mfkey32_moebius(&data, &key64);
if (res) {
PrintAndLogEx(SUCCESS, "UID: %s Sector %02x key %c [ " _GREEN_("%12" PRIX64) " ]"
, sprint_hex_inrow(outA, 4)
, data.sector
, (data.keytype == 0x60) ? 'A' : 'B'
, key64);
} else {
PrintAndLogEx(FAILED, "failed to recover any key");
}
return PM3_SUCCESS;
}
static int CmdHF14AMfWipe(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf mf wipe",
"Wipe card to zeros and default keys/acc. This command takes a key file to wipe card\n"
"Will use UID from card to generate keyfile name if not specified.\n"
"New A/B keys..... FF FF FF FF FF FF\n"
"New acc rights... FF 07 80\n"
"New GPB.......... 69",
"hf mf wipe --> reads card uid to generate file name\n"
"hf mf wipe --gen2 --> force write to S0, B0 manufacture block\n"
"hf mf wipe -f mykey.bin --> use mykey.bin\n"
);
void *argtable[] = {
arg_param_begin,
arg_str0("f", "file", "<fn>", "key filename"),
arg_lit0(NULL, "gen2", "force write to Sector 0, block 0 (GEN2)"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
int keyfnlen = 0;
char keyFilename[FILE_PATH_SIZE] = {0};
CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)keyFilename, FILE_PATH_SIZE, &keyfnlen);
bool gen2 = arg_get_lit(ctx, 2);
CLIParserFree(ctx);
char *fptr;
if (keyfnlen == 0) {
fptr = GenerateFilename("hf-mf-", "-key.bin");
if (fptr == NULL)
return PM3_ESOFT;
strcpy(keyFilename, fptr);
free(fptr);
}
uint8_t *keys;
size_t keyslen = 0;
if (loadFile_safeEx(keyFilename, ".bin", (void **)&keys, (size_t *)&keyslen, false) != PM3_SUCCESS) {
PrintAndLogEx(FAILED, "failed to load key file");
return PM3_ESOFT;
}
uint8_t keyA[MIFARE_4K_MAXSECTOR * 6];
uint8_t keyB[MIFARE_4K_MAXSECTOR * 6];
uint8_t num_sectors = 0;
uint8_t mf[MFBLOCK_SIZE];
switch (keyslen) {
case (MIFARE_MINI_MAXSECTOR * 2 * 6): {
PrintAndLogEx(INFO, "Loaded keys matching MIFARE Classic Mini 320b");
memcpy(keyA, keys, (MIFARE_MINI_MAXSECTOR * 6));
memcpy(keyB, keys + (MIFARE_MINI_MAXSECTOR * 6), (MIFARE_MINI_MAXSECTOR * 6));
num_sectors = NumOfSectors('0');
memcpy(mf, "\x11\x22\x33\x44\x44\x09\x04\x00\x62\x63\x64\x65\x66\x67\x68\x69", MFBLOCK_SIZE);
break;
}
case (MIFARE_1K_MAXSECTOR * 2 * 6): {
PrintAndLogEx(INFO, "Loaded keys matching MIFARE Classic 1K");
memcpy(keyA, keys, (MIFARE_1K_MAXSECTOR * 6));
memcpy(keyB, keys + (MIFARE_1K_MAXSECTOR * 6), (MIFARE_1K_MAXSECTOR * 6));
num_sectors = NumOfSectors('1');
memcpy(mf, "\x11\x22\x33\x44\x44\x08\x04\x00\x62\x63\x64\x65\x66\x67\x68\x69", MFBLOCK_SIZE);
break;
}
case (MIFARE_4K_MAXSECTOR * 2 * 6): {
PrintAndLogEx(INFO, "Loaded keys matching MIFARE Classic 4K");
memcpy(keyA, keys, (MIFARE_4K_MAXSECTOR * 6));
memcpy(keyB, keys + (MIFARE_4K_MAXSECTOR * 6), (MIFARE_4K_MAXSECTOR * 6));
num_sectors = NumOfSectors('4');
memcpy(mf, "\x11\x22\x33\x44\x44\x18\x02\x00\x62\x63\x64\x65\x66\x67\x68\x69", MFBLOCK_SIZE);
break;
}
default: {
PrintAndLogEx(INFO, "wrong key file size");
goto out;
}
}
if (gen2)
PrintAndLogEx(INFO, "Forcing overwrite of sector 0 / block 0 ");
else
PrintAndLogEx(INFO, "Skipping sector 0 / block 0");
PrintAndLogEx(NORMAL, "");
uint8_t zeros[MFBLOCK_SIZE] = {0};
memset(zeros, 0x00, sizeof(zeros));
uint8_t st[MFBLOCK_SIZE] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0x80, 0x69, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
// time to wipe card
for (uint8_t s = 0; s < num_sectors; s++) {
for (uint8_t b = 0; b < mfNumBlocksPerSector(s); b++) {
// Skipp write to manufacture block if not enforced
if (s == 0 && b == 0 && gen2 == false) {
continue;
}
uint8_t data[26];
memset(data, 0, sizeof(data));
if (mfIsSectorTrailer(b)) {
memcpy(data + 10, st, sizeof(st));
} else {
memcpy(data + 10, zeros, sizeof(zeros));
}
// add correct manufacture block if UID Gen2
if (s == 0 && b == 0 && gen2) {
memcpy(data + 10, mf, sizeof(mf));
}
// try both A/B keys, start with B key first
for (int8_t kt = MF_KEY_B; kt > -1; kt--) {
if (kt == MF_KEY_A)
memcpy(data, keyA + (s * 6), 6);
else
memcpy(data, keyB + (s * 6), 6);
PrintAndLogEx(INFO, "block %3d: %s" NOLF, mfFirstBlockOfSector(s) + b, sprint_hex(data + 10, MFBLOCK_SIZE));
clearCommandBuffer();
SendCommandMIX(CMD_HF_MIFARE_WRITEBL, mfFirstBlockOfSector(s) + b, kt, 0, data, sizeof(data));
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) {
uint8_t isOK = resp.oldarg[0] & 0xff;
if (isOK == 0) {
PrintAndLogEx(NORMAL, "( " _RED_("fail") " )");
} else {
PrintAndLogEx(NORMAL, "( " _GREEN_("ok") " )");
break;
}
} else {
PrintAndLogEx(WARNING, "Command execute timeout");
}
}
}
}
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "Done!");
out:
free(keys);
return PM3_SUCCESS;
}
static int CmdHF14AMfView(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf mf view",
"Print a MIFARE Classic dump file (bin/eml/json)",
"hf mf view -f hf-mf-01020304-dump.bin"
);
void *argtable[] = {
arg_param_begin,
arg_str1("f", "file", "<fn>", "filename of dump"),
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, (MFBLOCK_SIZE * MIFARE_4K_MAXBLOCK));
if (res != PM3_SUCCESS) {
return res;
}
uint16_t block_cnt = MIN(MIFARE_1K_MAXBLOCK, (bytes_read / MFBLOCK_SIZE));
if (bytes_read == 320)
block_cnt = MIFARE_MINI_MAXBLOCK;
else if (bytes_read == 2048)
block_cnt = MIFARE_2K_MAXBLOCK;
else if (bytes_read == 4096)
block_cnt = MIFARE_4K_MAXBLOCK;
if (verbose) {
PrintAndLogEx(INFO, "File: " _YELLOW_("%s"), filename);
PrintAndLogEx(INFO, "File size %zu bytes, file blocks %d (0x%x)", bytes_read, block_cnt, block_cnt);
}
mf_print_blocks(block_cnt, dump, verbose);
if (verbose) {
mf_print_keys(block_cnt, dump);
}
free(dump);
return PM3_SUCCESS;
}
// Read block from Gen4 GTU card
static int CmdHF14AGen4GetBlk(const char *cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf mf ggetblk",
"Get block data from magic gen4 GTU card.",
"hf mf ggetblk --blk 0 --> get block 0 (manufacturer)\n"
"hf mf ggetblk --blk 3 -v --> get block 3, decode sector trailer\n"
);
void *argtable[] = {
arg_param_begin,
arg_int1("b", "blk", "<dec>", "block number"),
arg_lit0("v", "verbose", "verbose output"),
arg_str0("p", "pwd", "<hex>", "password 4bytes"),
arg_param_end
};
CLIExecWithReturn(ctx, cmd, argtable, false);
int b = arg_get_int_def(ctx, 1, 0);
bool verbose = arg_get_lit(ctx, 2);
int pwd_len = 0;
uint8_t pwd[4] = {0};
CLIGetHexWithReturn(ctx, 3, pwd, &pwd_len);
CLIParserFree(ctx);
//validate args
if (b > MIFARE_4K_MAXBLOCK) {
return PM3_EINVARG;
}
if (pwd_len != 4 && pwd_len != 0) {
PrintAndLogEx(FAILED, "Must specify 4 bytes, got " _YELLOW_("%u"), pwd_len);
return PM3_EINVARG;
}
uint8_t blockno = (uint8_t)b;
uint8_t data[16] = {0};
PrintAndLogEx(NORMAL, "Block: %x", blockno) ;
int res = mfG4GetBlock(pwd, blockno, data, MAGIC_INIT | MAGIC_OFF);
if (res) {
PrintAndLogEx(ERR, "Can't read block. error=%d", res);
return PM3_ESOFT;
}
uint8_t sector = mfSectorNum(blockno);
mf_print_sector_hdr(sector);
mf_print_block(blockno, data, verbose);
if (verbose) {
decode_print_st(blockno, data);
} else {
PrintAndLogEx(NORMAL, "");
}
return PM3_SUCCESS;
}
// Load dump to Gen4 GTU card
static int CmdHF14AGen4Load(const char *cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf mf gload",
"Load magic gen4 gtu card with data from (bin/eml/json) dump file\n"
"or from emulator memory.",
"hf mf gload --emu\n"
"hf mf gload -f hf-mf-01020304.eml\n"
"hf mf gload -p AABBCCDD --4k -v -f hf-mf-01020304-dump.bin\n"
"\n"
"Card must be configured beforehand with `script run hf_mf_ultimatecard`.\n"
"Blocks are 16 bytes long."
);
void *argtable[] = {
arg_param_begin,
arg_lit0(NULL, "mini", "MIFARE Classic Mini / S20"),
arg_lit0(NULL, "1k", "MIFARE Classic 1k / S50 (def)"),
arg_lit0(NULL, "2k", "MIFARE Classic/Plus 2k"),
arg_lit0(NULL, "4k", "MIFARE Classic 4k / S70"),
arg_str0("p", "pwd", "<hex>", "password 4bytes"),
arg_lit0("v", "verbose", "verbose output"),
arg_str0("f", "file", "<fn>", "filename of dump"),
arg_lit0(NULL, "emu", "from emulator memory"),
arg_int0(NULL, "start", "<dec>", "index of block to start writing (default 0)"),
arg_int0(NULL, "end", "<dec>", "index of block to end writing (default last block)"),
arg_param_end
};
CLIExecWithReturn(ctx, cmd, argtable, false);
bool m0 = arg_get_lit(ctx, 1);
bool m1 = arg_get_lit(ctx, 2);
bool m2 = arg_get_lit(ctx, 3);
bool m4 = arg_get_lit(ctx, 4);
int pwd_len = 0;
uint8_t pwd[4] = {0};
CLIGetHexWithReturn(ctx, 5, pwd, &pwd_len);
bool verbose = arg_get_lit(ctx, 6);
int fnlen = 0;
char filename[FILE_PATH_SIZE] = {0};
CLIParamStrToBuf(arg_get_str(ctx, 7), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
bool fill_from_emulator = arg_get_lit(ctx, 8);
int start = arg_get_int_def(ctx, 9, 0);
int end = arg_get_int_def(ctx, 10, -1);
CLIParserFree(ctx);
// validations
if (pwd_len != 4 && pwd_len != 0) {
PrintAndLogEx(FAILED, "Must specify 4 bytes, got " _YELLOW_("%u"), pwd_len);
return PM3_EINVARG;
}
if ((m0 + m1 + m2 + m4) > 1) {
PrintAndLogEx(WARNING, "Only specify one MIFARE Type");
return PM3_EINVARG;
} else if ((m0 + m1 + m2 + m4) == 0) {
m1 = true;
}
char s[6];
memset(s, 0, sizeof(s));
uint16_t block_cnt = MIFARE_1K_MAXBLOCK;
if (m0) {
block_cnt = MIFARE_MINI_MAXBLOCK;
strncpy(s, "Mini", 5);
} else if (m1) {
block_cnt = MIFARE_1K_MAXBLOCK;
strncpy(s, "1K", 3);
} else if (m2) {
block_cnt = MIFARE_2K_MAXBLOCK;
strncpy(s, "2K", 3);
} else if (m4) {
block_cnt = MIFARE_4K_MAXBLOCK;
strncpy(s, "4K", 3);
} else {
PrintAndLogEx(WARNING, "Please specify a MIFARE Type");
return PM3_EINVARG;
}
if (fill_from_emulator && (fnlen != 0)) {
PrintAndLogEx(WARNING, "Please specify file or emulator memory, but not both");
return PM3_EINVARG;
}
if (!fill_from_emulator && (fnlen == 0)) {
PrintAndLogEx(WARNING, "Please specify file or emulator memory");
return PM3_EINVARG;
}
if (end == -1) {
end = block_cnt - 1;
}
if (start < 0 || end < 0) {
PrintAndLogEx(WARNING, "start and end must be positive integers");
return PM3_EINVARG ;
}
if (start > end) {
PrintAndLogEx(WARNING, "start cannot be more than end");
return PM3_EINVARG ;
}
if (start >= block_cnt) {
PrintAndLogEx(WARNING, "Last block for Mifare %s is %d. Start is too high.", s, block_cnt - 1) ;
return PM3_EINVARG ;
}
if (end >= block_cnt) {
PrintAndLogEx(WARNING, "Last block for Mifare %s is %d. End is too high.", s, block_cnt - 1) ;
return PM3_EINVARG ;
}
uint8_t *data = NULL;
size_t bytes_read = 0;
if (fill_from_emulator) {
data = calloc(block_cnt * MFBLOCK_SIZE, sizeof(uint8_t));
if (data == NULL) {
PrintAndLogEx(WARNING, "Fail, cannot allocate memory");
return PM3_EMALLOC;
}
PrintAndLogEx(INFO, "downloading emulator memory");
if (GetFromDevice(BIG_BUF_EML, data, block_cnt * MFBLOCK_SIZE, 0, NULL, 0, NULL, 2500, false) == false) {
PrintAndLogEx(WARNING, "Fail, transfer from device time-out");
free(data);
return PM3_ETIMEOUT;
}
} else {
// read from file
int res = pm3_load_dump(filename, (void **)&data, &bytes_read, (MFBLOCK_SIZE * MIFARE_4K_MAXBLOCK));
if (res != PM3_SUCCESS) {
return res;
}
// check file size corresponds to card size.
if (bytes_read != (block_cnt * MFBLOCK_SIZE)) {
PrintAndLogEx(ERR, "File content error. Read %zu bytes, expected %i", bytes_read, block_cnt * MFBLOCK_SIZE);
if (data != NULL) free(data);
return PM3_EFILE;
}
}
if (verbose) {
if (fnlen != 0) {
PrintAndLogEx(INFO, "File: " _YELLOW_("%s"), filename);
PrintAndLogEx(INFO, "File size %zu bytes, file blocks %d (0x%x)", bytes_read, block_cnt, block_cnt);
} else {
PrintAndLogEx(INFO, "Read %d blocks from emulator memory", block_cnt);
}
}
PrintAndLogEx(INFO, "Copying to magic gen4 GTU MIFARE Classic " _GREEN_("%s"), s);
PrintAndLogEx(INFO, "Starting block: %d. Ending block: %d.", start, end);
// copy to card
for (uint16_t blockno = start; blockno <= end; blockno++) {
// 4k writes can be long, so we split status each 64 block boundary.
if (blockno % 64 == 0 || blockno == start) {
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "" NOLF) ;
}
PrintAndLogEx(NORMAL, "." NOLF);
fflush(stdout);
// write block
uint8_t flags = 0 ;
if (blockno == start) flags |= MAGIC_INIT ;
if (blockno == end) flags |= MAGIC_OFF ;
int res=mfG4SetBlock(pwd, blockno, data + (blockno * MFBLOCK_SIZE), flags);
if ( res != PM3_SUCCESS) {
PrintAndLogEx(WARNING, "Can't set magic card block: %d. error=%d", blockno, res);
PrintAndLogEx(HINT, "Verify your card size, and try again or try another tag position");
free(data);
return PM3_ESOFT;
}
}
PrintAndLogEx(NORMAL, "\n");
if (data != NULL) free(data);
PrintAndLogEx(SUCCESS, "Card loaded " _YELLOW_("%d") " blocks from %s", end - start + 1,
(fill_from_emulator ? "emulator memory" : "file"));
PrintAndLogEx(INFO, "Done!");
return PM3_SUCCESS;
}
// Write block to Gen4 GTU card
static int CmdHF14AGen4SetBlk(const char *cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf mf gsetblk",
"Set block data on a magic gen4 GTU card",
"hf mf gsetblk --blk 1 -d 000102030405060708090a0b0c0d0e0f"
);
void *argtable[] = {
arg_param_begin,
arg_int1("b", "blk", "<dec>", "block number"),
arg_str0("d", "data", "<hex>", "bytes to write, 16 hex bytes"),
arg_str0("p", "pwd", "<hex>", "password 4bytes"),
arg_param_end
};
CLIExecWithReturn(ctx, cmd, argtable, false);
int b = arg_get_int_def(ctx, 1, -1);
uint8_t data[MFBLOCK_SIZE] = {0x00};
int datalen = 0;
CLIGetHexWithReturn(ctx, 2, data, &datalen);
int pwd_len = 0;
uint8_t pwd[4] = {0};
CLIGetHexWithReturn(ctx, 3, pwd, &pwd_len);
CLIParserFree(ctx);
// validations
if (pwd_len != 4 && pwd_len != 0) {
PrintAndLogEx(FAILED, "Must specify 4 bytes, got " _YELLOW_("%u"), pwd_len);
return PM3_EINVARG;
}
CLIParserFree(ctx);
if (b < 0 || b >= MIFARE_4K_MAXBLOCK) {
PrintAndLogEx(FAILED, "target block number out-of-range, got %i", b);
return PM3_EINVARG;
}
if (datalen != MFBLOCK_SIZE) {
PrintAndLogEx(FAILED, "expected 16 bytes data, got %i", datalen);
return PM3_EINVARG;
}
// write block
PrintAndLogEx(INFO, "Writing block number:%2d data:%s", b, sprint_hex_inrow(data, sizeof(data)));
uint8_t blockno = (uint8_t)b;
int res = mfG4SetBlock(pwd, blockno, data, MAGIC_INIT | MAGIC_OFF);
if (res) {
PrintAndLogEx(ERR, "Can't write block. error=%d", res);
return PM3_ESOFT;
}
return PM3_SUCCESS;
}
static int CmdHF14AGen4View(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf mf gview",
"View `magic gen4 gtu` card memory",
"hf mf gview\n"
"hf mf gview --4k"
);
void *argtable[] = {
arg_param_begin,
arg_lit0(NULL, "mini", "MIFARE Classic Mini / S20"),
arg_lit0(NULL, "1k", "MIFARE Classic 1k / S50 (def)"),
arg_lit0(NULL, "2k", "MIFARE Classic/Plus 2k"),
arg_lit0(NULL, "4k", "MIFARE Classic 4k / S70"),
arg_str0("p", "pwd", "<hex>", "password 4bytes"),
arg_lit0("v", "verbose", "verbose output"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
bool m0 = arg_get_lit(ctx, 1);
bool m1 = arg_get_lit(ctx, 2);
bool m2 = arg_get_lit(ctx, 3);
bool m4 = arg_get_lit(ctx, 4);
int pwd_len = 0;
uint8_t pwd[4] = {0};
CLIGetHexWithReturn(ctx, 5, pwd, &pwd_len);
bool verbose = arg_get_lit(ctx, 6);
CLIParserFree(ctx);
// validations
if (pwd_len != 4 && pwd_len != 0) {
PrintAndLogEx(FAILED, "Must specify 4 bytes, got " _YELLOW_("%u"), pwd_len);
return PM3_EINVARG;
}
if ((m0 + m1 + m2 + m4) > 1) {
PrintAndLogEx(WARNING, "Only specify one MIFARE Type");
return PM3_EINVARG;
} else if ((m0 + m1 + m2 + m4) == 0) {
m1 = true;
}
char s[6];
memset(s, 0, sizeof(s));
uint16_t block_cnt = MIFARE_1K_MAXBLOCK;
if (m0) {
block_cnt = MIFARE_MINI_MAXBLOCK;
strncpy(s, "Mini", 5);
} else if (m1) {
block_cnt = MIFARE_1K_MAXBLOCK;
strncpy(s, "1K", 3);
} else if (m2) {
block_cnt = MIFARE_2K_MAXBLOCK;
strncpy(s, "2K", 3);
} else if (m4) {
block_cnt = MIFARE_4K_MAXBLOCK;
strncpy(s, "4K", 3);
} else {
PrintAndLogEx(WARNING, "Please specify a MIFARE Type");
return PM3_EINVARG;
}
PrintAndLogEx(SUCCESS, "View magic gen4 GTU MIFARE Classic " _GREEN_("%s"), s);
// reserve memory
uint16_t bytes = block_cnt * MFBLOCK_SIZE;
uint8_t *dump = calloc(bytes, sizeof(uint8_t));
if (dump == NULL) {
PrintAndLogEx(WARNING, "Fail, cannot allocate memory");
return PM3_EMALLOC;
}
for (uint16_t i = 0; i < block_cnt; i++) {
// 4k READs can be long, so we split status each 64 blocks.
if (i % 64 == 0) {
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "" NOLF) ;
}
PrintAndLogEx(NORMAL, "." NOLF);
fflush(stdout);
uint8_t flags = 0 ;
if (i == 0) flags |= MAGIC_INIT ;
if (i+1 == block_cnt) flags |= MAGIC_OFF ;
int res = mfG4GetBlock(pwd, i, dump + (i * MFBLOCK_SIZE), flags);
if ( res != PM3_SUCCESS) {
PrintAndLogEx(WARNING, "Can't get magic card block: %u. error=%d", i, res);
PrintAndLogEx(HINT, "Verify your card size, and try again or try another tag position");
free(dump);
return PM3_ESOFT;
}
}
PrintAndLogEx(NORMAL, "");
mf_print_blocks(block_cnt, dump, verbose);
if (verbose) {
mf_print_keys(block_cnt, dump);
}
free(dump);
return PM3_SUCCESS;
}
static int CmdHF14AMfValue(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf mf value",
"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 -b --dec 10\n"
"hf mf value --blk 16 -k FFFFFFFFFFFF -b --get\n"
"hf mf value --get -d 87D612007829EDFF87D6120011EE11EE\n"
);
void *argtable[] = {
arg_param_begin,
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, 9, data, &dlen);
CLIParserFree(ctx);
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 (g_session.pm3_present == false)
return PM3_ENOTTY;
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;
}
static command_t CommandTable[] = {
{"help", CmdHelp, AlwaysAvailable, "This help"},
{"list", CmdHF14AMfList, AlwaysAvailable, "List MIFARE history"},
{"-----------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("recovery") " -----------------------"},
{"darkside", CmdHF14AMfDarkside, IfPm3Iso14443a, "Darkside attack"},
{"nested", CmdHF14AMfNested, IfPm3Iso14443a, "Nested attack"},
{"hardnested", CmdHF14AMfNestedHard, AlwaysAvailable, "Nested attack for hardened MIFARE Classic cards"},
{"staticnested", CmdHF14AMfNestedStatic, IfPm3Iso14443a, "Nested attack against static nonce MIFARE Classic cards"},
{"autopwn", CmdHF14AMfAutoPWN, IfPm3Iso14443a, "Automatic key recovery tool for MIFARE Classic"},
// {"keybrute", CmdHF14AMfKeyBrute, IfPm3Iso14443a, "J_Run's 2nd phase of multiple sector nested authentication key recovery"},
{"nack", CmdHf14AMfNack, IfPm3Iso14443a, "Test for MIFARE NACK bug"},
{"chk", CmdHF14AMfChk, IfPm3Iso14443a, "Check keys"},
{"fchk", CmdHF14AMfChk_fast, IfPm3Iso14443a, "Check keys fast, targets all keys on card"},
{"decrypt", CmdHf14AMfDecryptBytes, AlwaysAvailable, "[nt] [ar_enc] [at_enc] [data] - to decrypt sniff or trace"},
{"supercard", CmdHf14AMfSuperCard, IfPm3Iso14443a, "Extract info from a `super card`"},
{"-----------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("operations") " -----------------------"},
{"auth4", CmdHF14AMfAuth4, IfPm3Iso14443a, "ISO14443-4 AES authentication"},
{"acl", CmdHF14AMfAcl, AlwaysAvailable, "Decode and print MIFARE Classic access rights bytes"},
{"dump", CmdHF14AMfDump, IfPm3Iso14443a, "Dump MIFARE Classic tag to binary file"},
{"mad", CmdHF14AMfMAD, AlwaysAvailable, "Checks and prints MAD"},
{"personalize", CmdHFMFPersonalize, IfPm3Iso14443a, "Personalize UID (MIFARE Classic EV1 only)"},
{"rdbl", CmdHF14AMfRdBl, IfPm3Iso14443a, "Read MIFARE Classic block"},
{"rdsc", CmdHF14AMfRdSc, IfPm3Iso14443a, "Read MIFARE Classic sector"},
{"restore", CmdHF14AMfRestore, IfPm3Iso14443a, "Restore MIFARE Classic binary file to tag"},
{"setmod", CmdHf14AMfSetMod, IfPm3Iso14443a, "Set MIFARE Classic EV1 load modulation strength"},
{"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"},
{"-----------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("simulation") " -----------------------"},
{"sim", CmdHF14AMfSim, IfPm3Iso14443a, "Simulate MIFARE card"},
{"ecfill", CmdHF14AMfECFill, IfPm3Iso14443a, "Fill emulator memory with help of keys from emulator"},
{"eclr", CmdHF14AMfEClear, IfPm3Iso14443a, "Clear emulator memory"},
{"egetblk", CmdHF14AMfEGetBlk, IfPm3Iso14443a, "Get emulator memory block"},
{"egetsc", CmdHF14AMfEGetSc, IfPm3Iso14443a, "Get emulator memory sector"},
{"ekeyprn", CmdHF14AMfEKeyPrn, IfPm3Iso14443a, "Print keys from emulator memory"},
{"eload", CmdHF14AMfELoad, IfPm3Iso14443a, "Load from file emul dump"},
{"esave", CmdHF14AMfESave, IfPm3Iso14443a, "Save to file emul dump"},
{"esetblk", CmdHF14AMfESet, IfPm3Iso14443a, "Set emulator memory block"},
{"eview", CmdHF14AMfEView, IfPm3Iso14443a, "View emulator memory"},
{"-----------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("magic gen1") " -----------------------"},
{"cgetblk", CmdHF14AMfCGetBlk, IfPm3Iso14443a, "Read block from card"},
{"cgetsc", CmdHF14AMfCGetSc, IfPm3Iso14443a, "Read sector from card"},
{"cload", CmdHF14AMfCLoad, IfPm3Iso14443a, "Load dump to card"},
{"csave", CmdHF14AMfCSave, IfPm3Iso14443a, "Save dump from card into file or emulator"},
{"csetblk", CmdHF14AMfCSetBlk, IfPm3Iso14443a, "Write block to card"},
{"csetuid", CmdHF14AMfCSetUID, IfPm3Iso14443a, "Set UID on card"},
{"cview", CmdHF14AMfCView, IfPm3Iso14443a, "View card"},
{"cwipe", CmdHF14AMfCWipe, IfPm3Iso14443a, "Wipe card to default UID/Sectors/Keys"},
{"-----------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("magic gen3") " -----------------------"},
{"gen3uid", CmdHf14AGen3UID, IfPm3Iso14443a, "Set UID without changing manufacturer block"},
{"gen3blk", CmdHf14AGen3Block, IfPm3Iso14443a, "Overwrite manufacturer block"},
{"gen3freeze", CmdHf14AGen3Freeze, IfPm3Iso14443a, "Perma lock UID changes. irreversible"},
{"-----------", CmdHelp, IfPm3Iso14443a, "-------------------- " _CYAN_("magic gen4 GTU") " --------------------------"},
{"ggetblk", CmdHF14AGen4GetBlk, IfPm3Iso14443a, "Read block from card"},
{"gload", CmdHF14AGen4Load, IfPm3Iso14443a, "Load dump to card"},
{"gsetblk", CmdHF14AGen4SetBlk, IfPm3Iso14443a, "Write block to card"},
{"gview", CmdHF14AGen4View, IfPm3Iso14443a, "View card"},
{"-----------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("ndef") " -----------------------"},
// {"ice", CmdHF14AMfice, IfPm3Iso14443a, "collect MIFARE Classic nonces to file"},
{"ndefformat", CmdHFMFNDEFFormat, IfPm3Iso14443a, "Format MIFARE Classic Tag as NFC Tag"},
{"ndefread", CmdHFMFNDEFRead, IfPm3Iso14443a, "Read and print NDEF records from card"},
{"ndefwrite", CmdHFMFNDEFWrite, IfPm3Iso14443a, "Write NDEF records to card"},
{NULL, NULL, NULL, NULL}
};
static int CmdHelp(const char *Cmd) {
(void)Cmd; // Cmd is not used so far
CmdsHelp(CommandTable);
return PM3_SUCCESS;
}
int CmdHFMF(const char *Cmd) {
clearCommandBuffer();
return CmdsParse(CommandTable, Cmd);
}