mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2025-08-14 10:37:23 -07:00
commit
3b01177e5f
15 changed files with 594 additions and 74 deletions
|
@ -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 \
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
27
client/dictionaries/mfp_default_keys.dic
Normal file
27
client/dictionaries/mfp_default_keys.dic
Normal 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
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
39
client/mifare/mifaredefault.c
Normal file
39
client/mifare/mifaredefault.c
Normal 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);
|
|
@ -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
|
||||
|
|
|
@ -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--) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue