Merge pull request #484 from merlokk/mfp_check

hf mfp check
This commit is contained in:
Iceman 2019-12-02 09:58:55 +01:00 committed by GitHub
commit 3b01177e5f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 594 additions and 74 deletions

View file

@ -134,6 +134,7 @@ CORESRCS = uart_posix.c \
CMDSRCS = crapto1/crapto1.c \
crapto1/crypto1.c \
mifare/mifaredefault.c \
mifare/mfkey.c \
tea.c \
fido/additional_ca.c \

View file

@ -551,30 +551,29 @@ int CmdHF14ASniff(const char *Cmd) {
return PM3_SUCCESS;
}
int ExchangeRAW14a(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen) {
int ExchangeRAW14a(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen, bool silentMode) {
static uint8_t responseNum = 0;
uint16_t cmdc = 0;
*dataoutlen = 0;
if (activateField) {
responseNum = 1;
PacketResponseNG resp;
// Anticollision + SELECT card
SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0, NULL, 0);
if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500)) {
PrintAndLogEx(ERR, "Proxmark3 connection timeout.");
if (!silentMode) PrintAndLogEx(ERR, "Proxmark3 connection timeout.");
return 1;
}
// check result
if (resp.oldarg[0] == 0) {
PrintAndLogEx(ERR, "No card in field.");
if (!silentMode) PrintAndLogEx(ERR, "No card in field.");
return 1;
}
if (resp.oldarg[0] != 1 && resp.oldarg[0] != 2) {
PrintAndLogEx(ERR, "Card not in iso14443-4. res=%" PRId64 ".", resp.oldarg[0]);
if (!silentMode) PrintAndLogEx(ERR, "Card not in iso14443-4. res=%" PRId64 ".", resp.oldarg[0]);
return 1;
}
@ -583,12 +582,12 @@ int ExchangeRAW14a(uint8_t *datain, int datainlen, bool activateField, bool leav
uint8_t rats[] = { 0xE0, 0x80 }; // FSDI=8 (FSD=256), CID=0
SendCommandOLD(CMD_HF_ISO14443A_READER, ISO14A_RAW | ISO14A_APPEND_CRC | ISO14A_NO_DISCONNECT, 2, 0, rats, 2);
if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500)) {
PrintAndLogEx(ERR, "Proxmark3 connection timeout.");
if (!silentMode) PrintAndLogEx(ERR, "Proxmark3 connection timeout.");
return 1;
}
if (resp.oldarg[0] == 0) { // ats_len
PrintAndLogEx(ERR, "Can't get ATS.");
if (!silentMode) PrintAndLogEx(ERR, "Can't get ATS.");
return 1;
}
}
@ -610,7 +609,7 @@ int ExchangeRAW14a(uint8_t *datain, int datainlen, bool activateField, bool leav
int iLen = resp.oldarg[0];
if (!iLen) {
PrintAndLogEx(ERR, "No card response.");
if (!silentMode) PrintAndLogEx(ERR, "No card response.");
return 1;
}
@ -619,12 +618,12 @@ int ExchangeRAW14a(uint8_t *datain, int datainlen, bool activateField, bool leav
*dataoutlen = 0;
if (maxdataoutlen && *dataoutlen > maxdataoutlen) {
PrintAndLogEx(ERR, "Buffer too small(%d). Needs %d bytes", *dataoutlen, maxdataoutlen);
if (!silentMode) PrintAndLogEx(ERR, "Buffer too small(%d). Needs %d bytes", *dataoutlen, maxdataoutlen);
return 2;
}
if (recv[0] != data[0]) {
PrintAndLogEx(ERR, "iso14443-4 framing error. Card send %2x must be %2x", dataout[0], data[0]);
if (!silentMode) PrintAndLogEx(ERR, "iso14443-4 framing error. Card send %2x must be %2x", dataout[0], data[0]);
return 2;
}
@ -632,12 +631,12 @@ int ExchangeRAW14a(uint8_t *datain, int datainlen, bool activateField, bool leav
// CRC Check
if (iLen == -1) {
PrintAndLogEx(ERR, "ISO 14443A CRC error.");
if (!silentMode) PrintAndLogEx(ERR, "ISO 14443A CRC error.");
return 3;
}
} else {
PrintAndLogEx(ERR, "Reply timeout.");
if (!silentMode) PrintAndLogEx(ERR, "Reply timeout.");
return 4;
}

View file

@ -1,7 +1,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 2010 iZsh <izsh at fail0verflow.com>
// 2011, Merlok
// 2015,216,2017 iceman, marshmellow, piwi
// 2011,2019 Merlok
// 2015,2016,2017 iceman, marshmellow, piwi
// This code is licensed to you under the terms of the GNU GPL, version 2 or,
// at your option, any later version. See the LICENSE.txt file for the text of
// the license.
@ -30,6 +30,6 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search);
const char *getTagInfo(uint8_t uid);
int Hf14443_4aGetCardData(iso14a_card_select_t *card);
int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen);
int ExchangeRAW14a(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen);
int ExchangeRAW14a(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen, bool silentMode);
#endif

View file

@ -13,7 +13,7 @@
#include <ctype.h>
#include "cmdparser.h" // command_t
#include "commonutil.h" // ARRAYLEN
#include "commonutil.h" // ARRAYLEN
#include "comms.h" // clearCommandBuffer
#include "fileutils.h"
#include "cmdtrace.h"
@ -4211,7 +4211,7 @@ static int CmdHF14AMfAuth4(const char *Cmd) {
return PM3_ESOFT;
}
return MifareAuth4(NULL, keyn, key, true, false, true);
return MifareAuth4(NULL, keyn, key, true, false, true, true, false);
}
// https://www.nxp.com/docs/en/application-note/AN10787.pdf

View file

@ -25,6 +25,8 @@
#include "cliparser/cliparser.h"
#include "emv/dump.h"
#include "mifare/mifaredefault.h"
#include "util_posix.h"
#include "fileutils.h"
static const uint8_t DefaultKey[16] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
@ -88,7 +90,7 @@ static int CmdHFMFPInfo(const char *cmd) {
int datalen = 0;
// https://github.com/Proxmark/proxmark3/blob/master/client/luascripts/mifarePlus.lua#L161
uint8_t cmd[3 + 16] = {0xa8, 0x90, 0x90, 0x00};
int res = ExchangeRAW14a(cmd, sizeof(cmd), false, false, data, sizeof(data), &datalen);
int res = ExchangeRAW14a(cmd, sizeof(cmd), false, false, data, sizeof(data), &datalen, false);
if (!res && datalen > 1 && data[0] == 0x09) {
SLmode = 0;
}
@ -105,7 +107,7 @@ static int CmdHFMFPInfo(const char *cmd) {
DropField();
return 0;
return PM3_SUCCESS;
}
static int CmdHFMFPWritePerso(const char *cmd) {
@ -169,7 +171,7 @@ static int CmdHFMFPWritePerso(const char *cmd) {
}
PrintAndLogEx(INFO, "Write OK.");
return 0;
return PM3_SUCCESS;
}
uint16_t CardAddresses[] = {0x9000, 0x9001, 0x9002, 0x9003, 0x9004, 0xA000, 0xA001, 0xA080, 0xA081, 0xC000, 0xC001};
@ -245,7 +247,7 @@ static int CmdHFMFPInitPerso(const char *cmd) {
PrintAndLogEx(INFO, "Done.");
return 0;
return PM3_SUCCESS;
}
static int CmdHFMFPCommitPerso(const char *cmd) {
@ -286,7 +288,7 @@ static int CmdHFMFPCommitPerso(const char *cmd) {
}
PrintAndLogEx(INFO, "Switch level OK.");
return 0;
return PM3_SUCCESS;
}
static int CmdHFMFPAuth(const char *cmd) {
@ -324,7 +326,7 @@ static int CmdHFMFPAuth(const char *cmd) {
return 1;
}
return MifareAuth4(NULL, keyn, key, true, false, verbose);
return MifareAuth4(NULL, keyn, key, true, false, true, verbose, false);
}
static int CmdHFMFPRdbl(const char *cmd) {
@ -392,7 +394,7 @@ static int CmdHFMFPRdbl(const char *cmd) {
PrintAndLogEx(INFO, "--block:%d sector[%d]:%02x key:%04x", blockn, mfNumBlocksPerSector(sectorNum), sectorNum, uKeyNum);
mf4Session mf4session;
int res = MifareAuth4(&mf4session, keyn, key, true, true, verbose);
int res = MifareAuth4(&mf4session, keyn, key, true, true, true, verbose, false);
if (res) {
PrintAndLogEx(ERR, "Authentication error: %d", res);
return res;
@ -491,7 +493,7 @@ static int CmdHFMFPRdsc(const char *cmd) {
PrintAndLogEx(INFO, "--sector[%d]:%02x key:%04x", mfNumBlocksPerSector(sectorNum), sectorNum, uKeyNum);
mf4Session mf4session;
int res = MifareAuth4(&mf4session, keyn, key, true, true, verbose);
int res = MifareAuth4(&mf4session, keyn, key, true, true, true, verbose, false);
if (res) {
PrintAndLogEx(ERR, "Authentication error: %d", res);
return res;
@ -532,7 +534,7 @@ static int CmdHFMFPRdsc(const char *cmd) {
}
DropField();
return 0;
return PM3_SUCCESS;
}
static int CmdHFMFPWrbl(const char *cmd) {
@ -595,7 +597,7 @@ static int CmdHFMFPWrbl(const char *cmd) {
PrintAndLogEx(INFO, "--block:%d sector[%d]:%02x key:%04x", blockNum & 0xff, mfNumBlocksPerSector(sectorNum), sectorNum, uKeyNum);
mf4Session mf4session;
int res = MifareAuth4(&mf4session, keyn, key, true, true, verbose);
int res = MifareAuth4(&mf4session, keyn, key, true, true, true, verbose, false);
if (res) {
PrintAndLogEx(ERR, "Authentication error: %d", res);
return res;
@ -634,7 +636,333 @@ static int CmdHFMFPWrbl(const char *cmd) {
DropField();
PrintAndLogEx(INFO, "Write OK.");
return 0;
return PM3_SUCCESS;
}
#define AES_KEY_LEN 16
#define MAX_KEYS_LIST_LEN 1024
int MFPKeyCheck(uint8_t startSector, uint8_t endSector, uint8_t startKeyAB, uint8_t endKeyAB,
uint8_t keyList[MAX_KEYS_LIST_LEN][AES_KEY_LEN], size_t keyListLen, uint8_t foundKeys[2][64][AES_KEY_LEN + 1],
bool verbose) {
int res;
bool selectCard = true;
uint8_t keyn[2] = {0};
// sector number from 0
for (uint8_t sector = startSector; sector <= endSector; sector++) {
// 0-keyA 1-keyB
for(uint8_t keyAB = startKeyAB; keyAB <= endKeyAB; keyAB++) {
// main cycle with key check
for (int i = 0; i < keyListLen; i++) {
if (i % 10 == 0) {
if (!verbose)
printf(".");
if (kbd_enter_pressed()) {
PrintAndLogEx(WARNING, "\nAborted via keyboard!\n");
DropField();
return PM3_EOPABORTED;
}
}
uint16_t uKeyNum = 0x4000 + sector * 2 + keyAB;
keyn[0] = uKeyNum >> 8;
keyn[1] = uKeyNum & 0xff;
for (int retry = 0; retry < 4; retry++) {
res = MifareAuth4(NULL, keyn, keyList[i], selectCard, true, false, false, true);
if (res != 2)
break;
if (verbose)
PrintAndLogEx(WARNING, "retried[%d]...", retry);
else
printf("R");
DropField();
selectCard = true;
msleep(100);
}
if (verbose)
PrintAndLogEx(WARNING, "sector %02d key %d [%s] res: %d", sector, keyAB, sprint_hex_inrow(keyList[i], 16), res);
// key for [sector,keyAB] found
if (res == 0) {
if (verbose)
PrintAndLogEx(INFO, "Found key for sector %d key %s [%s]", sector, keyAB == 0 ? "A" : "B", sprint_hex_inrow(keyList[i], 16));
else
printf("+");
foundKeys[keyAB][sector][0] = 0x01;
memcpy(&foundKeys[keyAB][sector][1], keyList[i], AES_KEY_LEN);
DropField();
selectCard = true;
msleep(50);
break;
}
// 5 - auth error (rnd not equal)
if (res != 5) {
if (verbose)
PrintAndLogEx(ERR, "Exchange error. Aborted.");
else
printf("E");
DropField();
return PM3_ECARDEXCHANGE;
}
selectCard = false;
}
}
}
DropField();
return PM3_SUCCESS;
}
void Fill2bPattern(uint8_t keyList[MAX_KEYS_LIST_LEN][AES_KEY_LEN], size_t *keyListLen, uint32_t *startPattern) {
for (uint32_t pt = *startPattern; pt < 0x10000; pt++) {
keyList[*keyListLen][0] = (pt >> 8) & 0xff;
keyList[*keyListLen][1] = pt & 0xff;
memcpy(&keyList[*keyListLen][2], &keyList[*keyListLen][0], 2);
memcpy(&keyList[*keyListLen][4], &keyList[*keyListLen][0], 4);
memcpy(&keyList[*keyListLen][8], &keyList[*keyListLen][0], 8);
(*keyListLen)++;
*startPattern = pt;
if (*keyListLen == MAX_KEYS_LIST_LEN)
break;
}
(*startPattern)++;
}
static int CmdHFMFPChk(const char *cmd) {
int res;
uint8_t keyList[MAX_KEYS_LIST_LEN][AES_KEY_LEN] = {{0}};
size_t keyListLen = 0;
uint8_t foundKeys[2][64][AES_KEY_LEN + 1] = {{{0}}};
CLIParserInit("hf mfp chk",
"Checks keys with Mifare Plus card.",
"Usage:\n"
" hf mfp chk -k 000102030405060708090a0b0c0d0e0f -> check key on sector 0 as key A and B\n"
" hf mfp chk -s 2 -a -> check default key list on sector 2, key A\n"
" hf mfp chk -d mfp_default_keys -s0 -e6 -> check keys from dictionary against sectors 0-6\n"
" hf mfp chk --pattern1b -j keys -> check all 1-byte keys pattern and save found keys to json\n"
" hf mfp chk --pattern2b --startp2b FA00 -> check all 2-byte keys pattern. Start from key FA00FA00...FA00\n");
void *argtable[] = {
arg_param_begin,
arg_lit0("aA", "keya", "check only key A (by default check all keys)."),
arg_lit0("bB", "keyb", "check only key B (by default check all keys)."),
arg_int0("sS", "startsec", "Start sector Num (0..255)", NULL),
arg_int0("eE", "endsec", "End sector Num (0..255)", NULL),
arg_str0("kK", "key", "<Key>", "Key for checking (HEX 16 bytes)"),
arg_str0("dD", "dict", "<file>", "file with keys dictionary"),
arg_lit0(NULL, "pattern1b", "check all 1-byte combinations of key (0000...0000, 0101...0101, 0202...0202, ...)"),
arg_lit0(NULL, "pattern2b", "check all 2-byte combinations of key (0000...0000, 0001...0001, 0002...0002, ...)"),
arg_str0(NULL, "startp2b", "<Pattern>", "Start key (2-byte HEX) for 2-byte search (use with `--pattern2b`)"),
arg_str0("jJ", "json", "<file>", "json file to save keys"),
arg_lit0("vV", "verbose", "verbose mode."),
arg_param_end
};
CLIExecWithReturn(cmd, argtable, true);
bool keyA = arg_get_lit(1);
bool keyB = arg_get_lit(2);
uint8_t startSector = arg_get_int_def(3, 0);
uint8_t endSector = arg_get_int_def(4, 0);
uint8_t vkey[16] = {0};
int vkeylen = 0;
CLIGetHexWithReturn(5, vkey, &vkeylen);
if (vkeylen > 0) {
if (vkeylen == 16) {
memcpy(&keyList[keyListLen], vkey, 16);
keyListLen++;
} else {
PrintAndLogEx(ERR, "Specified key must have 16 bytes length.");
CLIParserFree();
return PM3_EINVARG;
}
}
uint8_t dict_filename[FILE_PATH_SIZE + 2] = {0};
int dict_filenamelen = 0;
if (CLIParamStrToBuf(arg_get_str(6), dict_filename, FILE_PATH_SIZE, &dict_filenamelen)) {
PrintAndLogEx(FAILED, "File name too long or invalid.");
CLIParserFree();
return PM3_EINVARG;
}
bool pattern1b = arg_get_lit(7);
bool pattern2b = arg_get_lit(8);
if (pattern1b && pattern2b) {
PrintAndLogEx(ERR, "Pattern search mode must be 2-byte or 1-byte only.");
CLIParserFree();
return PM3_EINVARG;
}
if (dict_filenamelen && (pattern1b || pattern2b)) {
PrintAndLogEx(ERR, "Pattern search mode and dictionary mode can't be used in one command.");
CLIParserFree();
return PM3_EINVARG;
}
uint32_t startPattern = 0x0000;
uint8_t vpattern[2];
int vpatternlen = 0;
CLIGetHexWithReturn(9, vpattern, &vpatternlen);
if (vpatternlen > 0) {
if (vpatternlen > 0 && vpatternlen <= 2) {
startPattern = (vpattern[0] << 8) + vpattern[1];
} else {
PrintAndLogEx(ERR, "Pattern must be 2-byte length.");
CLIParserFree();
return PM3_EINVARG;
}
if (!pattern2b)
PrintAndLogEx(WARNING, "Pattern entered, but search mode not is 2-byte search.");
}
uint8_t jsonname[250] = {0};
int jsonnamelen = 0;
if (CLIParamStrToBuf(arg_get_str(10), jsonname, sizeof(jsonname), &jsonnamelen)) {
PrintAndLogEx(ERR, "Invalid json name.");
CLIParserFree();
return PM3_EINVARG;
}
jsonname[jsonnamelen] = 0;
bool verbose = arg_get_lit(11);
CLIParserFree();
uint8_t startKeyAB = 0;
uint8_t endKeyAB = 1;
if (keyA && !keyB)
endKeyAB = 0;
if (!keyA && keyB)
startKeyAB = 1;
if (endSector < startSector)
endSector = startSector;
// 1-byte pattern search mode
if (pattern1b) {
for (int i = 0; i < 0x100; i++)
memset(keyList[i], i, 16);
keyListLen = 0x100;
}
// 2-byte pattern search mode
if (pattern2b)
Fill2bPattern(keyList, &keyListLen, &startPattern);
// dictionary mode
size_t endFilePosition = 0;
if (dict_filenamelen) {
uint16_t keycnt = 0;
res = loadFileDICTIONARYEx((char *)dict_filename, keyList, sizeof(keyList), NULL, 16, &keycnt, 0, &endFilePosition, true);
keyListLen = keycnt;
if (endFilePosition)
PrintAndLogEx(SUCCESS, "First part of dictionary successfully loaded.");
}
if (keyListLen == 0) {
for (int i = 0; i < g_mifare_plus_default_keys_len; i++) {
if (hex_to_bytes(g_mifare_plus_default_keys[i], keyList[keyListLen], 16) != 16)
break;
keyListLen++;
}
}
if (keyListLen == 0) {
PrintAndLogEx(ERR, "Key list is empty. Nothing to check.");
return PM3_EINVARG;
}
if (!verbose)
printf("Search keys:");
while (true) {
res = MFPKeyCheck(startSector, endSector, startKeyAB, endKeyAB, keyList, keyListLen, foundKeys, verbose);
if (res == PM3_EOPABORTED)
break;
if (pattern2b && startPattern < 0x10000) {
if (!verbose)
printf("p");
keyListLen = 0;
Fill2bPattern(keyList, &keyListLen, &startPattern);
continue;
}
if (dict_filenamelen && endFilePosition) {
if (!verbose)
printf("d");
uint16_t keycnt = 0;
res = loadFileDICTIONARYEx((char *)dict_filename, keyList, sizeof(keyList), NULL, 16, &keycnt, endFilePosition, &endFilePosition, false);
keyListLen = keycnt;
continue;
}
break;
}
if (!verbose)
printf("\n");
// print result
bool printedHeader = false;
for (uint8_t sector = startSector; sector <= endSector; sector++) {
if (foundKeys[0][sector][0] || foundKeys[1][sector][0]) {
if (!printedHeader) {
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, ".------.--------------------------------.--------------------------------.");
PrintAndLogEx(INFO, "|sector| key A | key B |");
PrintAndLogEx(INFO, "|------|--------------------------------|--------------------------------|");
printedHeader = true;
}
PrintAndLogEx(INFO, "| %02d |%32s|%32s|",
sector,
(foundKeys[0][sector][0] == 0) ? "------ " : sprint_hex_inrow(&foundKeys[0][sector][1], AES_KEY_LEN),
(foundKeys[1][sector][0] == 0) ? "------ " : sprint_hex_inrow(&foundKeys[1][sector][1], AES_KEY_LEN));
}
}
if (!printedHeader)
PrintAndLogEx(INFO, "No keys found(");
else
PrintAndLogEx(INFO, "'------'--------------------------------'--------------------------------'\n");
// save keys to json
if ((jsonnamelen > 0) && printedHeader) {
// Mifare Plus info
SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0);
PacketResponseNG resp;
WaitForResponse(CMD_ACK, &resp);
iso14a_card_select_t card;
memcpy(&card, (iso14a_card_select_t *)resp.data.asBytes, sizeof(iso14a_card_select_t));
uint64_t select_status = resp.oldarg[0]; // 0: couldn't read, 1: OK, with ATS, 2: OK, no ATS, 3: proprietary Anticollision
uint8_t data[10 + 1 + 2 + 1 + 256 + 2 * 64 * (AES_KEY_LEN + 1)] = {0};
uint8_t atslen = 0;
if (select_status == 1 || select_status == 2) {
memcpy(data, card.uid, card.uidlen);
data[10] = card.sak;
data[11] = card.atqa[1];
data[12] = card.atqa[0];
atslen = card.ats_len;
data[13] = atslen;
memcpy(&data[14], card.ats, atslen);
}
// length: UID(10b)+SAK(1b)+ATQA(2b)+ATSlen(1b)+ATS(atslen)+foundKeys[2][64][AES_KEY_LEN + 1]
memcpy(&data[14 + atslen], foundKeys, 2 * 64 * (AES_KEY_LEN + 1));
saveFileJSON((char *)jsonname, jsfMfPlusKeys, data, 64);
}
return PM3_SUCCESS;
}
static int CmdHFMFPMAD(const char *cmd) {
@ -728,7 +1056,7 @@ static int CmdHFMFPMAD(const char *cmd) {
}
}
return 0;
return PM3_SUCCESS;
}
static int CmdHFMFPNDEF(const char *cmd) {
@ -832,7 +1160,7 @@ static int CmdHFMFPNDEF(const char *cmd) {
NDEFDecodeAndPrint(data, datalen, verbose);
return 0;
return PM3_SUCCESS;
}
static command_t CommandTable[] = {
@ -845,6 +1173,7 @@ static command_t CommandTable[] = {
{"rdbl", CmdHFMFPRdbl, IfPm3Iso14443a, "Read blocks"},
{"rdsc", CmdHFMFPRdsc, IfPm3Iso14443a, "Read sectors"},
{"wrbl", CmdHFMFPWrbl, IfPm3Iso14443a, "Write blocks"},
{"chk", CmdHFMFPChk, IfPm3Iso14443a, "Check keys"},
{"mad", CmdHFMFPMAD, IfPm3Iso14443a, "Checks and prints MAD"},
{"ndef", CmdHFMFPNDEF, IfPm3Iso14443a, "Prints NDEF records from card"},
{NULL, NULL, 0, NULL}
@ -853,7 +1182,7 @@ static command_t CommandTable[] = {
static int CmdHelp(const char *Cmd) {
(void)Cmd; // Cmd is not used so far
CmdsHelp(CommandTable);
return 0;
return PM3_SUCCESS;
}
int CmdHFMFP(const char *Cmd) {

View file

@ -0,0 +1,27 @@
ffffffffffffffffffffffffffffffff
00000000000000000000000000000000
a0a1a2a3a4a5a6a7a0a1a2a3a4a5a6a7
b0b1b2b3b4b5b6b7b0b1b2b3b4b5b6b7
d3f7d3f7d3f7d3f7d3f7d3f7d3f7d3f7
11111111111111111111111111111111
22222222222222222222222222222222
33333333333333333333333333333333
44444444444444444444444444444444
55555555555555555555555555555555
66666666666666666666666666666666
77777777777777777777777777777777
88888888888888888888888888888888
99999999999999999999999999999999
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
cccccccccccccccccccccccccccccccc
dddddddddddddddddddddddddddddddd
eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
000102030405060708090a0b0c0d0e0f
0102030405060708090a0b0c0d0e0f10
00010203040506070809101112131415
01020304050607080910111213141516
16151413121110090807060504030201
15141312111009080706050403020100
0f0e0d0c0b0a09080706050403020100
100f0e0d0c0b0a090807060504030201

View file

@ -352,6 +352,36 @@ int saveFileJSON(const char *preferredName, JSONFileType ftype, uint8_t *data, s
case jsf15:
case jsfLegic:
case jsfT5555:
case jsfMfPlusKeys:
JsonSaveStr(root, "FileType", "mfp");
JsonSaveBufAsHexCompact(root, "$.Card.UID", &data[0], 7);
JsonSaveBufAsHexCompact(root, "$.Card.SAK", &data[10], 1);
JsonSaveBufAsHexCompact(root, "$.Card.ATQA", &data[11], 2);
uint8_t atslen = data[13];
if (atslen > 0)
JsonSaveBufAsHexCompact(root, "$.Card.ATS", &data[14], atslen);
uint8_t vdata[2][64][16 + 1] = {0};
memcpy(vdata, &data[14 + atslen], 2 * 64 * 17);
for (size_t i = 0; i < datalen; i++) {
char path[PATH_MAX_LENGTH] = {0};
if (vdata[0][i][0]) {
memset(path, 0x00, sizeof(path));
sprintf(path, "$.SectorKeys.%d.KeyA", mfSectorNum(i));
JsonSaveBufAsHexCompact(root, path, &vdata[0][i][1], 16);
}
if (vdata[1][i][0]) {
memset(path, 0x00, sizeof(path));
sprintf(path, "$.SectorKeys.%d.KeyB", mfSectorNum(i));
JsonSaveBufAsHexCompact(root, path, &vdata[1][i][1], 16);
}
}
break;
default:
break;
}
@ -715,20 +745,29 @@ out:
}
int loadFileDICTIONARY(const char *preferredName, void *data, size_t *datalen, uint8_t keylen, uint16_t *keycnt) {
// t5577 == 4bytes
// mifare == 6 bytes
// mf plus == 16 bytes
// iclass == 8 bytes
// default to 6 bytes.
if (keylen != 4 && keylen != 6 && keylen != 8 && keylen != 16) {
keylen = 6;
}
return loadFileDICTIONARYEx(preferredName, data, 0, datalen, keylen, keycnt, 0, NULL, true);
}
int loadFileDICTIONARYEx(const char *preferredName, void *data, size_t maxdatalen, size_t *datalen, uint8_t keylen, uint16_t *keycnt,
size_t startFilePosition, size_t *endFilePosition, bool verbose) {
if (endFilePosition)
*endFilePosition = 0;
if (data == NULL) return PM3_EINVARG;
uint16_t vkeycnt = 0;
char *path;
if (searchFile(&path, DICTIONARIES_SUBDIR, preferredName, ".dic", false) != PM3_SUCCESS)
return PM3_EFILE;
// t5577 == 4bytes
// mifare == 6 bytes
// iclass == 8 bytes
// default to 6 bytes.
if (keylen != 4 && keylen != 6 && keylen != 8) {
keylen = 6;
}
// double up since its chars
keylen <<= 1;
@ -743,10 +782,19 @@ int loadFileDICTIONARY(const char *preferredName, void *data, size_t *datalen, u
retval = PM3_EFILE;
goto out;
}
if (startFilePosition)
fseek(f, startFilePosition, SEEK_SET);
// read file
while (fgets(line, sizeof(line), f)) {
while (!feof(f)) {
size_t filepos = ftell(f);
if (!fgets(line, sizeof(line), f)) {
if (endFilePosition)
*endFilePosition = 0;
break;
}
// add null terminator
line[keylen] = 0;
@ -758,23 +806,32 @@ int loadFileDICTIONARY(const char *preferredName, void *data, size_t *datalen, u
if (line[0] == '#')
continue;
if (!isxdigit(line[0])) {
PrintAndLogEx(FAILED, "file content error. '%s' must include " _BLUE_("%2d") "HEX symbols", line, keylen);
if (!CheckStringIsHEXValue(line))
continue;
// cant store more data
if (maxdatalen && (counter + (keylen >> 1) > maxdatalen)) {
retval = 1;
if (endFilePosition)
*endFilePosition = filepos;
break;
}
uint64_t key = strtoull(line, NULL, 16);
num_to_bytes(key, keylen >> 1, data + counter);
(*keycnt)++;
if (hex_to_bytes(line, data + counter, keylen >> 1) != (keylen >> 1))
continue;
vkeycnt++;
memset(line, 0, sizeof(line));
counter += (keylen >> 1);
}
fclose(f);
PrintAndLogEx(SUCCESS, "loaded " _GREEN_("%2d") "keys from dictionary file " _YELLOW_("%s"), *keycnt, path);
if (verbose)
PrintAndLogEx(SUCCESS, "loaded " _GREEN_("%2d") "keys from dictionary file " _YELLOW_("%s"), vkeycnt, path);
if (datalen)
*datalen = counter;
if (keycnt)
*keycnt = vkeycnt;
out:
free(path);
return retval;
@ -790,9 +847,10 @@ int loadFileDICTIONARY_safe(const char *preferredName, void **pdata, uint8_t key
// t5577 == 4bytes
// mifare == 6 bytes
// mf plus == 16 bytes
// iclass == 8 bytes
// default to 6 bytes.
if (keylen != 4 && keylen != 6 && keylen != 8) {
if (keylen != 4 && keylen != 6 && keylen != 8 && keylen != 16) {
keylen = 6;
}
@ -848,10 +906,8 @@ int loadFileDICTIONARY_safe(const char *preferredName, void **pdata, uint8_t key
if (line[0] == '#')
continue;
if (!isxdigit(line[0])) {
PrintAndLogEx(FAILED, "file content error. '%s' must include " _BLUE_("%2d") "HEX symbols", line, keylen);
if (CheckStringIsHEXValue(line))
continue;
}
uint64_t key = strtoull(line, NULL, 16);

View file

@ -61,6 +61,7 @@ typedef enum {
jsfLegic,
jsfT55x7,
jsfT5555,
jsfMfPlusKeys,
} JSONFileType;
typedef enum {
@ -175,13 +176,32 @@ int loadFileJSON(const char *preferredName, void *data, size_t maxdatalen, size_
*
* @param preferredName
* @param data The data array to store the loaded bytes from file
* @param maxdatalen maximum size of data array in bytes
* @param datalen the number of bytes loaded from file
* @param datalen the number of bytes loaded from file. may be NULL
* @param keylen the number of bytes a key per row is
* @param keycnt key count that lays in data. may be NULL
* @return 0 for ok, 1 for failz
*/
int loadFileDICTIONARY(const char *preferredName, void *data, size_t *datalen, uint8_t keylen, uint16_t *keycnt);
/**
* @brief Utility function to load data from a DICTIONARY textfile. This method takes a preferred name.
* E.g. mfc_default_keys.dic
* can be executed several times for big dictionaries and checks length of buffer
*
* @param preferredName
* @param data The data array to store the loaded bytes from file
* @param maxdatalen maximum size of data array in bytes
* @param datalen the number of bytes loaded from file. may be NULL
* @param keylen the number of bytes a key per row is
* @param keycnt key count that lays in data. may be NULL
* @param startFilePosition start position in dictionary file. used for big dictionaries.
* @param endFilePosition in case we have keys in file and maxdatalen reached it returns current key position in file. may be NULL
* @param verbose print messages if true
* @return 0 for ok, 1 for failz
*/
int loadFileDICTIONARYEx(const char *preferredName, void *data, size_t maxdatalen, size_t *datalen, uint8_t keylen, uint16_t *keycnt,
size_t startFilePosition, size_t *endFilePosition, bool verbose);
/**
* @brief Utility function to load data safely from a DICTIONARY textfile. This method takes a preferred name.
* E.g. mfc_default_keys.dic

View file

@ -168,21 +168,24 @@ int CalculateMAC(mf4Session *session, MACType_t mtype, uint8_t blockNum, uint8_t
return aes_cmac8(NULL, session->Kmac, macdata, mac, macdatalen);
}
int MifareAuth4(mf4Session *session, uint8_t *keyn, uint8_t *key, bool activateField, bool leaveSignalON, bool verbose) {
int MifareAuth4(mf4Session *session, uint8_t *keyn, uint8_t *key, bool activateField, bool leaveSignalON, bool dropFieldIfError, bool verbose, bool silentMode) {
uint8_t data[257] = {0};
int datalen = 0;
uint8_t RndA[17] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00};
uint8_t RndB[17] = {0};
if (silentMode)
verbose = false;
if (session)
session->Authenticated = false;
uint8_t cmd1[] = {0x70, keyn[1], keyn[0], 0x00};
int res = ExchangeRAW14a(cmd1, sizeof(cmd1), activateField, true, data, sizeof(data), &datalen);
int res = ExchangeRAW14a(cmd1, sizeof(cmd1), activateField, true, data, sizeof(data), &datalen, silentMode);
if (res) {
PrintAndLogEx(ERR, "Exchande raw error: %d", res);
DropField();
if (!silentMode) PrintAndLogEx(ERR, "Exchande raw error: %d", res);
if (dropFieldIfError) DropField();
return 2;
}
@ -190,20 +193,20 @@ int MifareAuth4(mf4Session *session, uint8_t *keyn, uint8_t *key, bool activateF
PrintAndLogEx(INFO, "<phase1: %s", sprint_hex(data, datalen));
if (datalen < 1) {
PrintAndLogEx(ERR, "Card response wrong length: %d", datalen);
DropField();
if (!silentMode) PrintAndLogEx(ERR, "Card response wrong length: %d", datalen);
if (dropFieldIfError) DropField();
return 3;
}
if (data[0] != 0x90) {
PrintAndLogEx(ERR, "Card response error: %02x", data[2]);
DropField();
if (!silentMode) PrintAndLogEx(ERR, "Card response error: %02x", data[2]);
if (dropFieldIfError) DropField();
return 3;
}
if (datalen != 19) { // code 1b + 16b + crc 2b
PrintAndLogEx(ERR, "Card response must be 19 bytes long instead of: %d", datalen);
DropField();
if (!silentMode) PrintAndLogEx(ERR, "Card response must be 19 bytes long instead of: %d", datalen);
if (dropFieldIfError) DropField();
return 3;
}
@ -223,10 +226,10 @@ int MifareAuth4(mf4Session *session, uint8_t *keyn, uint8_t *key, bool activateF
if (verbose)
PrintAndLogEx(INFO, ">phase2: %s", sprint_hex(cmd2, 33));
res = ExchangeRAW14a(cmd2, sizeof(cmd2), false, true, data, sizeof(data), &datalen);
res = ExchangeRAW14a(cmd2, sizeof(cmd2), false, true, data, sizeof(data), &datalen, silentMode);
if (res) {
PrintAndLogEx(ERR, "Exchande raw error: %d", res);
DropField();
if (!silentMode) PrintAndLogEx(ERR, "Exchande raw error: %d", res);
if (dropFieldIfError) DropField();
return 4;
}
@ -241,12 +244,12 @@ int MifareAuth4(mf4Session *session, uint8_t *keyn, uint8_t *key, bool activateF
}
if (memcmp(&raw[4], &RndA[1], 16)) {
PrintAndLogEx(ERR, "\nAuthentication FAILED. rnd not equal");
if (!silentMode) PrintAndLogEx(ERR, "\nAuthentication FAILED. rnd is not equal");
if (verbose) {
PrintAndLogEx(ERR, "RndA reader: %s", sprint_hex(&RndA[1], 16));
PrintAndLogEx(ERR, "RndA card: %s", sprint_hex(&raw[4], 16));
}
DropField();
if (dropFieldIfError) DropField();
return 5;
}
@ -311,7 +314,7 @@ static int intExchangeRAW14aPlus(uint8_t *datain, int datainlen, bool activateFi
if (VerboseMode)
PrintAndLogEx(INFO, ">>> %s", sprint_hex(datain, datainlen));
int res = ExchangeRAW14a(datain, datainlen, activateField, leaveSignalON, dataout, maxdataoutlen, dataoutlen);
int res = ExchangeRAW14a(datain, datainlen, activateField, leaveSignalON, dataout, maxdataoutlen, dataoutlen, false);
if (VerboseMode)
PrintAndLogEx(INFO, "<<< %s", sprint_hex(dataout, *dataoutlen));
@ -380,7 +383,7 @@ int mfpReadSector(uint8_t sectorNo, uint8_t keyType, uint8_t *key, uint8_t *data
PrintAndLogEx(INFO, "--sector[%d]:%02x key:%04x", mfNumBlocksPerSector(sectorNo), sectorNo, uKeyNum);
mf4Session session;
int res = MifareAuth4(&session, keyn, key, true, true, verbose);
int res = MifareAuth4(&session, keyn, key, true, true, true, verbose, false);
if (res) {
PrintAndLogEx(ERR, "Sector %d authentication error: %d", sectorNo, res);
return res;

View file

@ -45,7 +45,7 @@ void mfpSetVerboseMode(bool verbose);
const char *mfpGetErrorDescription(uint8_t errorCode);
int CalculateMAC(mf4Session *session, MACType_t mtype, uint8_t blockNum, uint8_t blockCount, uint8_t *data, int datalen, uint8_t *mac, bool verbose);
int MifareAuth4(mf4Session *session, uint8_t *keyn, uint8_t *key, bool activateField, bool leaveSignalON, bool verbose);
int MifareAuth4(mf4Session *session, uint8_t *keyn, uint8_t *key, bool activateField, bool leaveSignalON, bool dropFieldIfError, bool verbose, bool silentMode);
int MFPWritePerso(uint8_t *keyNum, uint8_t *key, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen);
int MFPCommitPerso(bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen);

View file

@ -0,0 +1,39 @@
//-----------------------------------------------------------------------------
// Copyright (C) 2017 Merlok
//
// This code is licensed to you under the terms of the GNU GPL, version 2 or,
// at your option, any later version. See the LICENSE.txt file for the text of
// the license.
//-----------------------------------------------------------------------------
// Mifare default constants
//-----------------------------------------------------------------------------
#include "mifaredefault.h"
#include "commonutil.h" // ARRAYLEN
const char* g_mifare_plus_default_keys[] = {
"ffffffffffffffffffffffffffffffff", // default key
"00000000000000000000000000000000",
"a0a1a2a3a4a5a6a7a0a1a2a3a4a5a6a7", // MAD key
"b0b1b2b3b4b5b6b7b0b1b2b3b4b5b6b7",
"d3f7d3f7d3f7d3f7d3f7d3f7d3f7d3f7", // NDEF key
"11111111111111111111111111111111",
"22222222222222222222222222222222",
"33333333333333333333333333333333",
"44444444444444444444444444444444",
"55555555555555555555555555555555",
"66666666666666666666666666666666",
"77777777777777777777777777777777",
"88888888888888888888888888888888",
"99999999999999999999999999999999",
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
"cccccccccccccccccccccccccccccccc",
"dddddddddddddddddddddddddddddddd",
"eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
"000102030405060708090a0b0c0d0e0f",
"0102030405060708090a0b0c0d0e0f10",
"00010203040506070809101112131415",
"01020304050607080910111213141516"
};
size_t g_mifare_plus_default_keys_len = ARRAYLEN(g_mifare_plus_default_keys);

View file

@ -44,4 +44,7 @@ static const uint8_t g_mifare_ndef_key[] = {0xd3, 0xf7, 0xd3, 0xf7, 0xd3, 0xf7};
static const uint8_t g_mifarep_mad_key[] = {0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7};
static const uint8_t g_mifarep_ndef_key[] = {0xd3, 0xf7, 0xd3, 0xf7, 0xd3, 0xf7, 0xd3, 0xf7, 0xd3, 0xf7, 0xd3, 0xf7, 0xd3, 0xf7, 0xd3, 0xf7};
extern const char* g_mifare_plus_default_keys[];
extern size_t g_mifare_plus_default_keys_len;
#endif

View file

@ -394,6 +394,46 @@ void print_blocks(uint32_t *data, size_t len) {
}
}
int hex_to_bytes(const char *hexValue, uint8_t *bytesValue, size_t maxBytesValueLen) {
char buf[4] = {0};
int indx = 0;
int bytesValueLen = 0;
while (hexValue[indx]) {
if (hexValue[indx] == '\t' || hexValue[indx] == ' ') {
indx++;
continue;
}
if (isxdigit(hexValue[indx])) {
buf[strlen(buf)] = hexValue[indx];
} else {
// if we have symbols other than spaces and hex
return -1;
}
if (maxBytesValueLen && bytesValueLen >= maxBytesValueLen) {
// if we dont have space in buffer and have symbols to translate
return -2;
}
if (strlen(buf) >= 2) {
uint32_t temp = 0;
sscanf(buf, "%x", &temp);
bytesValue[bytesValueLen] = (uint8_t)(temp & 0xff);
memset(buf, 0, sizeof(buf));
bytesValueLen++;
}
indx++;
}
if (strlen(buf) > 0)
//error when not completed hex bytes
return -3;
return bytesValueLen;
}
// takes a number (uint64_t) and creates a binarray in dest.
void num_to_bytebits(uint64_t n, size_t len, uint8_t *dest) {
while (len--) {

View file

@ -55,6 +55,7 @@ char *sprint_ascii_ex(const uint8_t *data, const size_t len, const size_t min_st
void print_blocks(uint32_t *data, size_t len);
int hex_to_bytes(const char *hexValue, uint8_t *bytesValue, size_t maxBytesValueLen);
void num_to_bytebits(uint64_t n, size_t len, uint8_t *dest);
void num_to_bytebitsLSBF(uint64_t n, size_t len, uint8_t *dest);
uint8_t *SwapEndian64(const uint8_t *src, const size_t len, const uint8_t blockSize);

View file

@ -590,6 +590,8 @@ typedef struct {
#define PM3_EWRONGANSVER -16
// Memory out-of-bounds error client/pm3: error when a read/write is outside the expected array
#define PM3_EOUTOFBOUND -17
// exchange with card error client/pm3: error when cant get answer from card or got an incorrect answer
#define PM3_ECARDEXCHANGE -18
// No data pm3: no data available, no host frame available (not really an error)
#define PM3_ENODATA -98
// Quit program client: reserved, order to quit the program