Merge pull request #6 from RfidResearchGroup/master

Update
This commit is contained in:
Bjoern Kerler 2020-04-06 14:02:38 +02:00 committed by GitHub
commit 0fbc723c64
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 1239 additions and 721 deletions

View file

@ -2040,7 +2040,7 @@ void T55xx_ChkPwds(uint8_t flags) {
if (isok != sizeof(counter)) if (isok != sizeof(counter))
goto OUT; goto OUT;
pwdCount = counter[1] << 8 | counter[0]; pwdCount = (uint16_t)(counter[1] << 8 | counter[0]);
if (pwdCount == 0 || pwdCount == 0xFFFF) if (pwdCount == 0 || pwdCount == 0xFFFF)
goto OUT; goto OUT;

View file

@ -205,6 +205,7 @@ CMDSRCS = crapto1/crapto1.c \
cmdhffido.c \ cmdhffido.c \
cmdhffelica.c \ cmdhffelica.c \
cmdhfthinfilm.c \ cmdhfthinfilm.c \
cmdhfcryptorf.c \
cmdhflto.c \ cmdhflto.c \
cmdhw.c \ cmdhw.c \
cmdlf.c \ cmdlf.c \
@ -251,7 +252,8 @@ CMDSRCS = crapto1/crapto1.c \
flash.c \ flash.c \
wiegand_formats.c \ wiegand_formats.c \
wiegand_formatutils.c \ wiegand_formatutils.c \
cardhelper.c cardhelper.c \
settings.c
cpu_arch = $(shell uname -m) cpu_arch = $(shell uname -m)
ifneq ($(findstring 86, $(cpu_arch)), ) ifneq ($(findstring 86, $(cpu_arch)), )

View file

@ -34,6 +34,7 @@
#include "cmdhffido.h" // FIDO authenticators #include "cmdhffido.h" // FIDO authenticators
#include "cmdhfthinfilm.h" // Thinfilm #include "cmdhfthinfilm.h" // Thinfilm
#include "cmdhflto.h" // LTO-CM #include "cmdhflto.h" // LTO-CM
#include "cmdhfcryptorf.h" // CryptoRF
#include "cmdtrace.h" // trace list #include "cmdtrace.h" // trace list
#include "ui.h" #include "ui.h"
#include "cmddata.h" #include "cmddata.h"
@ -149,11 +150,22 @@ int CmdHFSearch(const char *Cmd) {
res = PM3_SUCCESS; res = PM3_SUCCESS;
} }
} }
/*
// 14b and iclass is the longest test (put last)
PROMPT_CLEARLINE;
PrintAndLogEx(INPLACE, "Searching for CryptoRF tag...");
if (IfPm3Iso14443b()) {
if (readHFCryptoRF(false) == PM3_SUCCESS) {
PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("CryptoRF tag") "found\n");
res = PM3_SUCCESS;
}
}
*/
// 14b and iclass is the longest test (put last) // 14b and iclass is the longest test (put last)
PROMPT_CLEARLINE; PROMPT_CLEARLINE;
PrintAndLogEx(INPLACE, "Searching for ISO14443-B tag..."); PrintAndLogEx(INPLACE, "Searching for ISO14443-B tag...");
if (IfPm3Iso14443a()) { if (IfPm3Iso14443b()) {
if (readHF14B(false) == 1) { if (readHF14B(false) == 1) {
PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("ISO14443-B tag") "found\n"); PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("ISO14443-B tag") "found\n");
res = PM3_SUCCESS; res = PM3_SUCCESS;
@ -281,6 +293,7 @@ static command_t CommandTable[] = {
{"14a", CmdHF14A, AlwaysAvailable, "{ ISO14443A RFIDs... }"}, {"14a", CmdHF14A, AlwaysAvailable, "{ ISO14443A RFIDs... }"},
{"14b", CmdHF14B, AlwaysAvailable, "{ ISO14443B RFIDs... }"}, {"14b", CmdHF14B, AlwaysAvailable, "{ ISO14443B RFIDs... }"},
{"15", CmdHF15, AlwaysAvailable, "{ ISO15693 RFIDs... }"}, {"15", CmdHF15, AlwaysAvailable, "{ ISO15693 RFIDs... }"},
// {"cryptorf", CmdHFCryptoRF, AlwaysAvailable, "{ CryptoRF RFIDs... }"},
{"epa", CmdHFEPA, AlwaysAvailable, "{ German Identification Card... }"}, {"epa", CmdHFEPA, AlwaysAvailable, "{ German Identification Card... }"},
{"felica", CmdHFFelica, AlwaysAvailable, "{ ISO18092 / Felica RFIDs... }"}, {"felica", CmdHFFelica, AlwaysAvailable, "{ ISO18092 / Felica RFIDs... }"},
{"fido", CmdHFFido, AlwaysAvailable, "{ FIDO and FIDO2 authenticators... }"}, {"fido", CmdHFFido, AlwaysAvailable, "{ FIDO and FIDO2 authenticators... }"},

View file

@ -1245,6 +1245,135 @@ int CmdHF14A(const char *Cmd) {
return CmdsParse(CommandTable, Cmd); return CmdsParse(CommandTable, Cmd);
} }
static void printTag(char *tag) {
PrintAndLogEx(SUCCESS, _YELLOW_(" %s"), tag);
}
typedef enum {
mtNone = 0,
mtClassic = 1,
mtMini = 2,
mtDESFire = 4,
mtPlus = 8,
mtUltralight = 16,
mtOther = 32
} nxp_mifare_type;
// According to NXP AN10833 Rev 3.6 MIFARE Type Identification, Table 6
int detect_nxp_card(uint8_t sak, uint16_t atqa) {
int type = mtNone;
if (sak == 0x00) {
printTag("MIFARE Ultralight C / Ultralight CL2");
type = mtUltralight;
}
if (sak == 0x01) {
printTag("TNP3xxx (Activision Game Appliance)");
type = mtOther;
}
if ((sak & 0x04) == 0x04) {
printTag("Any MIFARE CL1");
type |= mtDESFire;
}
if ((sak & 0x08) == 0x08) {
printTag("MIFARE Classic 1K / Classic 1K CL2");
printTag("MIFARE Plus 2K / Plus EV1 2K");
printTag("MIFARE Plus CL2 2K / Plus CL2 EV1 2K");
type |= mtClassic;
type |= mtPlus;
}
if ((sak & 0x09) == 0x09) {
printTag("MIFARE Mini 0.3K / Mini CL2 0.3K");
type |= mtMini;
}
if ((sak & 0x10) == 0x10) {
printTag("MIFARE Plus 2K / Plus CL2 2K");
type |= mtPlus;
}
if ((sak & 0x11) == 0x11) {
printTag("MIFARE Plus 4K / Plus CL2 4K");
type |= mtPlus;
}
if ((sak & 0x18) == 0x18) {
if (atqa == 0x0042) {
printTag("MIFARE Plus 4K / Plus EV1 4K");
printTag("MIFARE Plus CL2 4K / Plus CL2 EV1 4K");
type |= mtPlus;
} else {
printTag("MIFARE Classic 4K / Classic 4K CL2");
type |= mtClassic;
}
}
if ((sak & 0x20) == 0x20) {
if (atqa == 0x0344) {
printTag("MIFARE DESFire EV1 2K/4K/8K / DESFire EV1 CL2 2K/4K/8K");
type |= mtDESFire;
} else {
printTag("MIFARE Plus 2K / Plus EV1 2K");
printTag("MIFARE Plus 4K / Plus EV1 4K");
printTag("MIFARE Plus CL2 2K / Plus CL2 EV1 4K");
printTag("MIFARE Plus CL2 4K / Plus CL2 EV1 4K");
type |= mtPlus;
}
}
if ((sak & 0x24) == 0x24) {
if (atqa == 0x0344) {
printTag("MIFARE DESFire CL1 / DESFire EV1 CL1");
type |= mtDESFire;
}
}
if ((sak & 0x28) == 0x28) {
if (atqa == 0x0344) {
printTag("MIFARE DESFire CL1 / DESFire EV1 CL1");
type |= mtDESFire;
}
}
return type;
}
typedef struct {
uint8_t uid0;
uint8_t uid1;
char *desc;
} uidname;
const uidname uidmap[] = {
// UID0, UID1, TEXT
{0x02, 0x00, "SR176"},
{0x02, 0x03, "SRIX4K"},
{0x02, 0x0C, "SRT512"},
{0x02, 0x0F, "SRI2K"},
{0x02, 0x1B, "25TB512-AC"},
{0x02, 0x3D, "SRIX4K"},
{0x02, 0x3F, "25TB02K"},
{0x02, 0x4D, "SRIX512"},
{0x02, 0x6D, "SRI512"},
{0x02, 0x7D, "SRI4K"},
{0x02, 0x84, "M24SR64-Y"},
{0x02, 0xA3, "25TA02KB-P"},
{0x02, 0xC4, "25TA64K"},
{0x02, 0xE3, "25TA02KB"},
{0x02, 0xE4, "25TA512B"},
{0x02, 0xF3, "25TA02KB-D"},
{0x11, 0x22, "NTAG21x Modifiable"},
{0x00, 0x00, "None"}
};
void getTagLabel(uint8_t uid0, uint8_t uid1) {
int i = 0;
while (uidmap[i].uid0 != 0x00) {
if ((uidmap[i].uid0 == uid0) && (uidmap[i].uid1 == uid1)) {
PrintAndLogEx(SUCCESS, _YELLOW_(" %s"), uidmap[i].desc);
return;
}
i += 1;
}
return;
}
int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) {
clearCommandBuffer(); clearCommandBuffer();
SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0, NULL, 0); SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0, NULL, 0);
@ -1290,10 +1419,64 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) {
PrintAndLogEx(SUCCESS, " SAK: " _GREEN_("%02x [%" PRIu64 "]"), card.sak, resp.oldarg[0]); PrintAndLogEx(SUCCESS, " SAK: " _GREEN_("%02x [%" PRIu64 "]"), card.sak, resp.oldarg[0]);
bool isMifareClassic = true; bool isMifareClassic = true;
bool isMifareDesfire = false; bool isMifareDESFire = false;
bool isMifarePlus = false; bool isMifarePlus = false;
bool isMifareUltralight = false; bool isMifareUltralight = false;
int nxptype = mtNone;
// Double & triple sized UID, can be mapped to a manufacturer.
if (card.uidlen <= 4) {
nxptype = detect_nxp_card(card.sak, ((card.atqa[1] << 8) + card.atqa[0]));
if ((nxptype & mtClassic) == mtClassic) isMifareClassic = true;
else isMifareClassic = false;
if ((nxptype & mtDESFire) == mtDESFire) {
isMifareDESFire = true;
} else {
isMifareDESFire = false;
}
if ((nxptype & mtPlus) == mtPlus) isMifarePlus = true;
else isMifarePlus = false;
if ((nxptype & mtUltralight) == mtUltralight) isMifareUltralight = true;
else isMifareUltralight = false;
if ((nxptype & mtOther) == mtOther) isMifareClassic = true;
}
if (card.uidlen > 4) {
PrintAndLogEx(SUCCESS, "MANUFACTURER: " _YELLOW_("%s"), getTagInfo(card.uid[0]));
PrintAndLogEx(SUCCESS, "Possible Type:");
switch (card.uid[0]) {
case 0x04: // NXP
nxptype = detect_nxp_card(card.sak, ((card.atqa[1] << 8) + card.atqa[0]));
if ((nxptype & mtClassic) == mtClassic) isMifareClassic = true;
else isMifareClassic = false;
if ((nxptype & mtDESFire) == mtDESFire) {
isMifareDESFire = true;
} else {
isMifareDESFire = false;
}
if ((nxptype & mtPlus) == mtPlus) isMifarePlus = true;
else isMifarePlus = false;
if ((nxptype & mtUltralight) == mtUltralight) isMifareUltralight = true;
else isMifareUltralight = false;
if ((nxptype & mtOther) == mtOther) isMifareClassic = true;
break;
case 0x05: // Infineon
if ((card.uid[1] & 0xF0) == 0x10) {
printTag("my-d(tm) command set SLE 66R04/16/32P, SLE 66R04/16/32S");
} else if ((card.uid[1] & 0xF0) == 0x20) {
printTag("my-d(tm) command set SLE 66R01/16/32P (Type 2 Tag)");
} else if ((card.uid[1] & 0xF0) == 0x30) {
printTag("my-d(tm) move lean SLE 66R01P/66R01PN");
} else if ((card.uid[1] & 0xF0) == 0x70) {
printTag("my-d(tm) move lean SLE 66R01L");
}
if (card.sak == 0x88) {
printTag("Infineon MIFARE CLASSIC 1K");
}
getTagLabel(card.uid[0], card.uid[1]);
break;
default:
getTagLabel(card.uid[0], card.uid[1]);
switch (card.sak) { switch (card.sak) {
case 0x00: case 0x00:
isMifareClassic = false; isMifareClassic = false;
@ -1305,8 +1488,9 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) {
if (tagT != UL_ERROR) { if (tagT != UL_ERROR) {
ul_print_type(tagT, 0); ul_print_type(tagT, 0);
isMifareUltralight = true; isMifareUltralight = true;
printTag("MIFARE Ultralight/C/NTAG Compatible");
} else { } else {
PrintAndLogEx(SUCCESS, "TYPE: Possible AZTEK (iso14443a compliant)"); printTag("Possible AZTEK (iso14443a compliant)");
} }
// reconnect for further tests // reconnect for further tests
@ -1323,64 +1507,26 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) {
return select_status; return select_status;
} }
break; break;
case 0x01:
PrintAndLogEx(SUCCESS, "TYPE: " _YELLOW_("NXP TNP3xxx Activision Game Appliance"));
break;
case 0x04:
PrintAndLogEx(SUCCESS, "TYPE: " _YELLOW_("NXP MIFARE (various !DESFire !DESFire EV1)"));
isMifareClassic = false;
isMifareDesfire = true;
break;
case 0x08:
PrintAndLogEx(SUCCESS, "TYPE: " _YELLOW_("NXP MIFARE CLASSIC 1k | Plus 2k SL1 | 1k Ev1"));
break;
case 0x09:
PrintAndLogEx(SUCCESS, "TYPE: " _YELLOW_("NXP MIFARE Mini 0.3k"));
break;
case 0x0A: case 0x0A:
PrintAndLogEx(SUCCESS, "TYPE: " _YELLOW_("FM11RF005SH (Shanghai Metro)")); printTag("FM11RF005SH (Shanghai Metro)");
break;
case 0x10:
PrintAndLogEx(SUCCESS, "TYPE: " _YELLOW_("NXP MIFARE Plus 2k SL2"));
isMifarePlus = true;
break;
case 0x11:
PrintAndLogEx(SUCCESS, "TYPE: " _YELLOW_("NXP MIFARE Plus 4k SL2"));
isMifarePlus = true;
break;
case 0x18:
PrintAndLogEx(SUCCESS, "TYPE: " _YELLOW_("NXP MIFARE Classic 4k | Plus 4k SL1 | 4k Ev1"));
break; break;
case 0x20: case 0x20:
PrintAndLogEx(SUCCESS, "TYPE: " _YELLOW_("NXP MIFARE DESFire 4k | DESFire EV1 2k/4k/8k | Plus 2k/4k SL3 | JCOP 31/41")); printTag("JCOP 31/41");
isMifareClassic = false;
isMifareDesfire = true;
isMifarePlus = true;
break;
case 0x24:
PrintAndLogEx(SUCCESS, "TYPE: " _YELLOW_("NXP MIFARE DESFire | DESFire EV1"));
isMifareClassic = false;
isMifareDesfire = true;
break; break;
case 0x28: case 0x28:
PrintAndLogEx(SUCCESS, "TYPE: " _YELLOW_("JCOP31 or JCOP41 v2.3.1")); printTag("JCOP31 or JCOP41 v2.3.1");
break; break;
case 0x38: case 0x38:
PrintAndLogEx(SUCCESS, "TYPE: " _YELLOW_("Nokia 6212 or 6131 MIFARE CLASSIC 4K")); printTag("Nokia 6212 or 6131");
break;
case 0x88:
PrintAndLogEx(SUCCESS, "TYPE: " _YELLOW_("Infineon MIFARE CLASSIC 1K"));
break; break;
case 0x98: case 0x98:
PrintAndLogEx(SUCCESS, "TYPE: " _YELLOW_("Gemplus MPCOS")); printTag("Gemplus MPCOS");
break; break;
default: default:
; break;
}
break;
} }
// Double & triple sized UID, can be mapped to a manufacturer.
if (card.uidlen > 4) {
PrintAndLogEx(SUCCESS, "MANUFACTURER: " _YELLOW_("%s"), getTagInfo(card.uid[0]));
} }
// try to request ATS even if tag claims not to support it // try to request ATS even if tag claims not to support it
@ -1455,12 +1601,14 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) {
); );
pos++; pos++;
} }
if (tc1) { if (tc1) {
PrintAndLogEx(SUCCESS, " - TC1 : NAD is%s supported, CID is%s supported", PrintAndLogEx(SUCCESS, " - TC1 : NAD is%s supported, CID is%s supported",
(card.ats[pos] & 0x01) ? "" : " NOT", (card.ats[pos] & 0x01) ? "" : " NOT",
(card.ats[pos] & 0x02) ? "" : " NOT"); (card.ats[pos] & 0x02) ? "" : " NOT");
pos++; pos++;
} }
if (card.ats[0] > pos && card.ats[0] < card.ats_len - 2) { if (card.ats[0] > pos && card.ats[0] < card.ats_len - 2) {
const char *tip = ""; const char *tip = "";
if (card.ats[0] - pos >= 7) { if (card.ats[0] - pos >= 7) {
@ -1477,14 +1625,14 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) {
switch (card.ats[pos + 2] & 0xf0) { switch (card.ats[pos + 2] & 0xf0) {
case 0x10: case 0x10:
PrintAndLogEx(SUCCESS, " 1x -> MIFARE DESFire"); PrintAndLogEx(SUCCESS, " 1x -> MIFARE DESFire");
isMifareDesfire = true; isMifareDESFire = true;
isMifareClassic = false; isMifareClassic = false;
isMifarePlus = false; isMifarePlus = false;
break; break;
case 0x20: case 0x20:
PrintAndLogEx(SUCCESS, " 2x -> MIFARE Plus"); PrintAndLogEx(SUCCESS, " 2x -> MIFARE Plus");
isMifarePlus = true; isMifarePlus = true;
isMifareDesfire = false; isMifareDESFire = false;
isMifareClassic = false; isMifareClassic = false;
break; break;
} }
@ -1607,6 +1755,17 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) {
PrintAndLogEx(INFO, "proprietary non iso14443-4 card found, RATS not supported"); PrintAndLogEx(INFO, "proprietary non iso14443-4 card found, RATS not supported");
} }
if (isMifareUltralight) {
PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf mfu info`"));
}
if (isMifarePlus) {
PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf mfp info`"));
}
if (isMifareDESFire) {
PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf mfdes info`"));
}
if (((card.sak & 0x08) == 0x08) || ((card.sak & 0x18) == 0x18)) {
detect_classic_magic(); detect_classic_magic();
if (isMifareClassic) { if (isMifareClassic) {
@ -1623,21 +1782,12 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) {
res = detect_classic_static_nonce(); res = detect_classic_static_nonce();
if (res == 1) if (res == 1)
PrintAndLogEx(SUCCESS, "Static nonce: " _YELLOW_("yes") ); PrintAndLogEx(SUCCESS, "Static nonce: " _YELLOW_("yes"));
if (res == 2 && verbose) if (res == 2 && verbose)
PrintAndLogEx(SUCCESS, "Static nonce: " _RED_("fail")); PrintAndLogEx(SUCCESS, "Static nonce: " _RED_("fail"));
}
if (isMifareUltralight) {
PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf mfu info`"));
} }
if (isMifarePlus) {
PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf mfp info`"));
} }
if (isMifareDesfire) {
PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf mfdes info`"));
}
return select_status; return select_status;
} }

609
client/cmdhfcryptorf.c Normal file
View file

@ -0,0 +1,609 @@
//-----------------------------------------------------------------------------
// Copyright (C) 2020 iceman
//
// 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.
//-----------------------------------------------------------------------------
// High frequency CryptoRF commands (ISO14443B)
//-----------------------------------------------------------------------------
#include "cmdhfcryptorf.h"
#include <ctype.h>
#include "fileutils.h"
#include "cmdparser.h" // command_t
#include "comms.h" // clearCommandBuffer
#include "cmdtrace.h"
#include "crc16.h"
#include "cmdhf14a.h"
#include "protocols.h" // definitions of ISO14B protocol
#define TIMEOUT 2000
static int CmdHelp(const char *Cmd);
static int usage_hf_cryptorf_info(void) {
PrintAndLogEx(NORMAL, "Usage: hf cryptorf info [h] [v]\n"
"Options:\n"
" h this help\n"
" v verbose\n"
"\n"
"Example:\n"
_YELLOW_(" hf cryptorf info")
);
return PM3_SUCCESS;
}
static int usage_hf_cryptorf_reader(void) {
PrintAndLogEx(NORMAL, "Usage: hf cryptorf reader [h] [v]\n"
"Options:\n"
" h this help\n"
" v verbose\n"
"\n"
"Example:\n"
_YELLOW_(" hf cryptorf reader")
);
return PM3_SUCCESS;
}
static int usage_hf_cryptorf_sniff(void) {
PrintAndLogEx(NORMAL, "It get data from the field and saves it into command buffer\n"
"Buffer accessible from command " _YELLOW_("'hf list cryptorf'") "\n"
"Usage: hf cryptorf sniff [h]\n"
"Options:\n"
" h this help\n"
"\n"
"Example:\n"
_YELLOW_(" hf cryptorf sniff")
);
return PM3_SUCCESS;
}
static int usage_hf_cryptorf_sim(void) {
PrintAndLogEx(NORMAL, "Emulating CryptoRF tag with 4 UID / PUPI\n"
"Usage: hf cryptorf sim [h] [u <uid>]\n"
"Options:\n"
" h this help\n"
" u 4byte UID/PUPI\n"
"\n"
"Example:\n"
_YELLOW_(" hf cryptorf sim")
);
return PM3_SUCCESS;
}
static int usage_hf_cryptorf_dump(void) {
PrintAndLogEx(NORMAL, "This command dumps the contents of a ISO-14443-B tag and save it to file\n"
"\n"
"Usage: hf cryptorf dump [h] [card memory] <f filname> \n"
"Options:\n"
" h this help\n"
" f <name> filename, if no <name> UID will be used as filename\n"
"\n"
"Examples:\n"
"\thf cryptorf dump\n"
"\thf cryptorf dump f mydump");
return PM3_SUCCESS;
}
static int usage_hf_cryptorf_eload(void) {
PrintAndLogEx(NORMAL, "It loads a binary dump into emulator memory\n"
"Usage: hf cryptorf eload [f <file name w/o `.eml`>]\n"
"Options:\n"
" h this help\n"
" f <name> filename, if no <name> UID will be used as filename\n"
"\n"
"Examples:\n"
_YELLOW_(" hf cryptorf eload f filename")
);
return PM3_SUCCESS;
}
static int usage_hf_cryptorf_esave(void) {
PrintAndLogEx(NORMAL, "It saves bin/eml/json dump file of emulator memory\n"
" Usage: hf cryptorf esave [f <file name w/o `.eml`>]\n"
"Options:\n"
" h this help\n"
" f <name> filename, if no <name> UID will be used as filename\n"
"\n"
"Examples:\n"
_YELLOW_(" hf cryptorf esave ")
_YELLOW_(" hf cryptorf esave f filename")
);
return PM3_SUCCESS;
}
static int switch_off_field_cryptorf(void) {
clearCommandBuffer();
SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_DISCONNECT, 0, 0, NULL, 0);
return PM3_SUCCESS;
}
static int CmdHFCryptoRFList(const char *Cmd) {
(void)Cmd; // Cmd is not used so far
CmdTraceList("14b");
return PM3_SUCCESS;
}
static int CmdHFCryptoRFSim(const char *Cmd) {
char cmdp = tolower(param_getchar(Cmd, 0));
if (cmdp == 'h') return usage_hf_cryptorf_sim();
uint32_t pupi = 0;
if (cmdp == 'u') {
pupi = param_get32ex(Cmd, 1, 0, 16);
}
clearCommandBuffer();
SendCommandMIX(CMD_HF_ISO14443B_SIMULATE, pupi, 0, 0, NULL, 0);
return PM3_SUCCESS;
}
static int CmdHFCryptoRFSniff(const char *Cmd) {
char cmdp = tolower(param_getchar(Cmd, 0));
if (cmdp == 'h') return usage_hf_cryptorf_sniff();
clearCommandBuffer();
SendCommandNG(CMD_HF_ISO14443B_SNIFF, NULL, 0);
return PM3_SUCCESS;
}
static bool get_14b_UID(iso14b_card_select_t *card) {
if (!card)
return false;
int8_t retry = 3;
PacketResponseNG resp;
// test for 14b SR
while (retry--) {
clearCommandBuffer();
SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_CONNECT | ISO14B_SELECT_SR | ISO14B_DISCONNECT, 0, 0, NULL, 0);
if (WaitForResponseTimeout(CMD_ACK, &resp, TIMEOUT)) {
uint8_t status = resp.oldarg[0];
if (status == 0) {
memcpy(card, (iso14b_card_select_t *)resp.data.asBytes, sizeof(iso14b_card_select_t));
return true;
}
}
} // retry
// test 14b standard
retry = 3;
while (retry--) {
clearCommandBuffer();
SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_CONNECT | ISO14B_SELECT_STD | ISO14B_DISCONNECT, 0, 0, NULL, 0);
if (WaitForResponseTimeout(CMD_ACK, &resp, TIMEOUT)) {
uint8_t status = resp.oldarg[0];
if (status == 0) {
memcpy(card, (iso14b_card_select_t *)resp.data.asBytes, sizeof(iso14b_card_select_t));
return true;
}
}
} // retry
if (retry <= 0)
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
return false;
}
static int CmdHFCryptoRFInfo(const char *Cmd) {
char cmdp = tolower(param_getchar(Cmd, 0));
if (cmdp == 'h') return usage_hf_cryptorf_info();
bool verbose = (cmdp == 'v');
int res = infoHFCryptoRF(verbose);
if (res != PM3_SUCCESS && verbose) {
PrintAndLogEx(FAILED, "no 14443-B tag found");
}
return res;
}
static int CmdHFCryptoRFReader(const char *Cmd) {
char cmdp = tolower(param_getchar(Cmd, 0));
if (cmdp == 'h') return usage_hf_cryptorf_reader();
bool verbose = (cmdp == 'v');
int res = readHFCryptoRF(verbose);
if (res != PM3_SUCCESS && verbose) {
PrintAndLogEx(FAILED, "no 14443-B tag found");
}
return res;
}
// need to write to file
static int CmdHFCryptoRFDump(const char *Cmd) {
uint8_t fileNameLen = 0;
char filename[FILE_PATH_SIZE] = {0};
char *fptr = filename;
bool errors = false;
uint8_t cmdp = 0, cardtype = 1;
uint16_t cardsize = 0;
uint8_t blocks = 0;
iso14b_card_select_t card;
while (param_getchar(Cmd, cmdp) != 0x00 && !errors) {
switch (tolower(param_getchar(Cmd, cmdp))) {
case 'h':
return usage_hf_cryptorf_dump();
case 'f':
fileNameLen = param_getstr(Cmd, cmdp + 1, filename, FILE_PATH_SIZE);
cmdp += 2;
break;
default:
if (cmdp == 0) {
cardtype = param_get8ex(Cmd, cmdp, 1, 10);
cmdp++;
} else {
PrintAndLogEx(WARNING, "Unknown parameter '%c'\n", param_getchar(Cmd, cmdp));
errors = true;
break;
}
}
}
//Validations
if (errors) return usage_hf_cryptorf_dump();
switch (cardtype) {
case 2:
cardsize = (512 / 8) + 4;
blocks = 0x0F;
break;
case 1:
default:
cardsize = (4096 / 8) + 4;
blocks = 0x7F;
break;
}
if (!get_14b_UID(&card)) {
PrintAndLogEx(WARNING, "No tag found.");
return PM3_SUCCESS;
}
if (fileNameLen < 1) {
PrintAndLogEx(INFO, "Using UID as filename");
fptr += sprintf(fptr, "hf-cryptorf-");
FillFileNameByUID(fptr, card.uid, "-dump", card.uidlen);
}
// detect blocksize from card :)
PrintAndLogEx(NORMAL, "Reading memory from tag UID %s", sprint_hex(card.uid, card.uidlen));
uint8_t data[cardsize];
memset(data, 0, sizeof(data));
int blocknum = 0;
uint8_t *recv = NULL;
PacketResponseNG resp;
clearCommandBuffer();
SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_CONNECT | ISO14B_SELECT_SR, 0, 0, NULL, 0);
//select
if (WaitForResponseTimeout(CMD_ACK, &resp, 2000)) {
if (resp.oldarg[0]) {
PrintAndLogEx(INFO, "failed to select %" PRId64 " | %" PRId64, resp.oldarg[0], resp.oldarg[1]);
goto out;
}
}
uint8_t req[2] = {ISO14443B_READ_BLK};
for (int retry = 0; retry < 5; retry++) {
req[1] = blocknum;
clearCommandBuffer();
SendCommandOLD(CMD_HF_ISO14443B_COMMAND, ISO14B_APPEND_CRC | ISO14B_RAW, 2, 0, req, sizeof(req));
if (WaitForResponseTimeout(CMD_ACK, &resp, 2000)) {
uint8_t status = resp.oldarg[0] & 0xFF;
if (status > 0) {
continue;
}
uint16_t len = (resp.oldarg[1] & 0xFFFF);
recv = resp.data.asBytes;
if (!check_crc(CRC_14443_B, recv, len)) {
PrintAndLogEx(FAILED, "crc fail, retrying one more time");
continue;
}
memcpy(data + (blocknum * 4), resp.data.asBytes, 4);
if (blocknum == 0xFF) {
//last read.
break;
}
retry = 0;
blocknum++;
if (blocknum > blocks) {
// read config block
blocknum = 0xFF;
}
printf(".");
fflush(stdout);
}
}
if (blocknum != 0xFF) {
PrintAndLogEx(NORMAL, "\n Dump failed");
goto out;
}
PrintAndLogEx(NORMAL, "\n");
PrintAndLogEx(NORMAL, "block# | data | ascii");
PrintAndLogEx(NORMAL, "---------+--------------+----------");
for (int i = 0; i <= blocks; i++) {
PrintAndLogEx(NORMAL,
"%3d/0x%02X | %s | %s",
i,
i,
sprint_hex(data + (i * 4), 4),
sprint_ascii(data + (i * 4), 4)
);
}
PrintAndLogEx(NORMAL, "\n");
size_t datalen = (blocks + 1) * 4;
saveFileEML(filename, data, datalen, 4);
saveFile(filename, ".bin", data, datalen);
out:
return switch_off_field_cryptorf();
}
static int CmdHFCryptoRFELoad(const char *Cmd) {
size_t datalen = 1024;
char filename[FILE_PATH_SIZE] = {0x00};
bool errors = false, has_filename = false;
uint8_t cmdp = 0;
while (param_getchar(Cmd, cmdp) != 0x00 && !errors) {
switch (tolower(param_getchar(Cmd, cmdp))) {
case 'h' :
return usage_hf_cryptorf_eload();
case 'f' :
if (param_getstr(Cmd, cmdp + 1, filename, FILE_PATH_SIZE) >= FILE_PATH_SIZE) {
PrintAndLogEx(FAILED, "Filename too long");
errors = true;
break;
}
has_filename = true;
cmdp += 2;
break;
default :
PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp));
errors = true;
break;
}
}
if (has_filename == false)
errors = true;
//Validations
if (errors || strlen(Cmd) == 0) return usage_hf_cryptorf_eload();
// set up buffer
uint8_t *data = calloc(datalen, sizeof(uint8_t));
if (!data) {
PrintAndLogEx(WARNING, "Fail, cannot allocate memory");
return PM3_EMALLOC;
}
if (loadFile_safe(filename, ".bin", (void **)&data, &datalen) != PM3_SUCCESS) {
free(data);
PrintAndLogEx(WARNING, "Error, reading file");
return PM3_EFILE;
}
PrintAndLogEx(SUCCESS, "Uploading to emulator memory");
/*
// fast push mode
conn.block_after_ACK = true;
//Send to device
uint32_t bytes_sent = 0;
uint32_t bytes_remaining = bytes_read;
while (bytes_remaining > 0) {
uint32_t bytes_in_packet = MIN(PM3_CMD_DATA_SIZE, bytes_remaining);
if (bytes_in_packet == bytes_remaining) {
// Disable fast mode on last packet
conn.block_after_ACK = false;
}
clearCommandBuffer();
SendCommandOLD(CMD_HF_CRYPTORF_EML_MEMSET, bytes_sent, bytes_in_packet, 0, data + bytes_sent, bytes_in_packet);
bytes_remaining -= bytes_in_packet;
bytes_sent += bytes_in_packet;
}
*/
free(data);
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(SUCCESS, "Done");
return PM3_SUCCESS;
}
static int CmdHFCryptoRFESave(const char *Cmd) {
char filename[FILE_PATH_SIZE] = {0};
char *fptr = filename;
int fileNameLen = 0;
size_t numofbytes = 1024;
bool errors = false;
uint8_t cmdp = 0;
while (param_getchar(Cmd, cmdp) != 0x00 && !errors) {
switch (tolower(param_getchar(Cmd, cmdp))) {
case 'h' :
return usage_hf_cryptorf_esave();
case 'f' :
fileNameLen = param_getstr(Cmd, cmdp + 1, filename, FILE_PATH_SIZE);
if (!fileNameLen)
errors = true;
if (fileNameLen > FILE_PATH_SIZE - 5)
fileNameLen = FILE_PATH_SIZE - 5;
cmdp += 2;
break;
default :
PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp));
errors = true;
break;
}
}
//Validations
if (errors || strlen(Cmd) == 0) return usage_hf_cryptorf_esave();
// set up buffer
uint8_t *data = calloc(numofbytes, sizeof(uint8_t));
if (!data) {
PrintAndLogEx(WARNING, "Fail, cannot allocate memory");
return PM3_EMALLOC;
}
// download emulator memory
PrintAndLogEx(SUCCESS, "Reading emulator memory...");
if (!GetFromDevice(BIG_BUF_EML, data, numofbytes, 0, NULL, 0, NULL, 2500, false)) {
PrintAndLogEx(WARNING, "Fail, transfer from device time-out");
free(data);
return PM3_ETIMEOUT;
}
// user supplied filename?
if (fileNameLen < 1) {
PrintAndLogEx(INFO, "Using UID as filename");
fptr += sprintf(fptr, "hf-cryptorf-");
FillFileNameByUID(fptr, data, "-dump", 4);
}
saveFile(filename, ".bin", data, numofbytes);
//needs to change
saveFileEML(filename, data, numofbytes, 8);
//needs to change
saveFileJSON(filename, jsfRaw, data, numofbytes);
return PM3_SUCCESS;
}
static command_t CommandTable[] = {
{"help", CmdHelp, AlwaysAvailable, "This help"},
{"dump", CmdHFCryptoRFDump, IfPm3Iso14443b, "Read all memory pages of an CryptoRF tag, save to file"},
{"info", CmdHFCryptoRFInfo, IfPm3Iso14443b, "Tag information"},
{"list", CmdHFCryptoRFList, AlwaysAvailable, "List ISO 14443B history"},
{"reader", CmdHFCryptoRFReader, IfPm3Iso14443b, "Act as a CryptoRF reader to identify a tag"},
{"sim", CmdHFCryptoRFSim, IfPm3Iso14443b, "Fake CryptoRF tag"},
{"sniff", CmdHFCryptoRFSniff, IfPm3Iso14443b, "Eavesdrop CryptoRF"},
{"eload", CmdHFCryptoRFELoad, AlwaysAvailable, "Load binary dump to emulator memory"},
{"esave", CmdHFCryptoRFESave, AlwaysAvailable, "Save emulator memory to binary file"},
{NULL, NULL, NULL, NULL}
};
static int CmdHelp(const char *Cmd) {
(void)Cmd; // Cmd is not used so far
CmdsHelp(CommandTable);
return PM3_SUCCESS;
}
int CmdHFCryptoRF(const char *Cmd) {
clearCommandBuffer();
return CmdsParse(CommandTable, Cmd);
}
// Print extented information about tag.
int infoHFCryptoRF(bool verbose) {
int res = PM3_ESOFT;
// 14b get and print UID only (general info)
clearCommandBuffer();
SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_CONNECT | ISO14B_SELECT_STD | ISO14B_DISCONNECT, 0, 0, NULL, 0);
PacketResponseNG resp;
if (!WaitForResponseTimeout(CMD_ACK, &resp, TIMEOUT)) {
if (verbose) PrintAndLogEx(WARNING, "command execution timeout");
switch_off_field_cryptorf();
return false;
}
iso14b_card_select_t card;
memcpy(&card, (iso14b_card_select_t *)resp.data.asBytes, sizeof(iso14b_card_select_t));
uint64_t status = resp.oldarg[0];
switch (status) {
case 0:
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(SUCCESS, " UID : %s", sprint_hex(card.uid, card.uidlen));
PrintAndLogEx(SUCCESS, " ATQB : %s", sprint_hex(card.atqb, sizeof(card.atqb)));
PrintAndLogEx(SUCCESS, " CHIPID : %02X", card.chipid);
res = PM3_SUCCESS;
break;
case 2:
if (verbose) PrintAndLogEx(FAILED, "ISO 14443-3 ATTRIB fail");
break;
case 3:
if (verbose) PrintAndLogEx(FAILED, "ISO 14443-3 CRC fail");
break;
default:
if (verbose) PrintAndLogEx(FAILED, "ISO 14443-b card select failed");
break;
}
return res;
}
// get and print general info cryptoRF
int readHFCryptoRF(bool verbose) {
int res = PM3_ESOFT;
// 14b get and print UID only (general info)
clearCommandBuffer();
SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_CONNECT | ISO14B_SELECT_STD | ISO14B_DISCONNECT, 0, 0, NULL, 0);
PacketResponseNG resp;
if (!WaitForResponseTimeout(CMD_ACK, &resp, TIMEOUT)) {
if (verbose) PrintAndLogEx(WARNING, "command execution timeout");
return PM3_ETIMEOUT;
}
iso14b_card_select_t card;
memcpy(&card, (iso14b_card_select_t *)resp.data.asBytes, sizeof(iso14b_card_select_t));
uint64_t status = resp.oldarg[0];
switch (status) {
case 0:
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(SUCCESS, " UID : %s", sprint_hex(card.uid, card.uidlen));
PrintAndLogEx(SUCCESS, " ATQB : %s", sprint_hex(card.atqb, sizeof(card.atqb)));
PrintAndLogEx(SUCCESS, " CHIPID : %02X", card.chipid);
res = PM3_SUCCESS;
break;
case 2:
if (verbose) PrintAndLogEx(FAILED, "ISO 14443-3 ATTRIB fail");
break;
case 3:
if (verbose) PrintAndLogEx(FAILED, "ISO 14443-3 CRC fail");
break;
default:
if (verbose) PrintAndLogEx(FAILED, "ISO 14443-b card select failed");
break;
}
return res;
}

20
client/cmdhfcryptorf.h Normal file
View file

@ -0,0 +1,20 @@
//-----------------------------------------------------------------------------
// Copyright (C) 2020 iceman
//
// 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.
//-----------------------------------------------------------------------------
// High frequency CryptoRF commands (ISO14443B)
//-----------------------------------------------------------------------------
#ifndef CMDHFCRYPTORF_H__
#define CMDHFCRYPTORF_H__
#include "common.h"
int CmdHFCryptoRF(const char *Cmd);
int infoHFCryptoRF(bool verbose);
int readHFCryptoRF(bool verbose);
#endif

View file

@ -1847,7 +1847,7 @@ int readFelicaUid(bool verbose) {
} }
static command_t CommandTable[] = { static command_t CommandTable[] = {
{"----------- General -----------", CmdHelp, IfPm3Iso14443a, ""}, {"----------- General -----------", CmdHelp, AlwaysAvailable, ""},
{"help", CmdHelp, AlwaysAvailable, "This help"}, {"help", CmdHelp, AlwaysAvailable, "This help"},
{"list", CmdHFFelicaList, AlwaysAvailable, "List ISO 18092/FeliCa history"}, {"list", CmdHFFelicaList, AlwaysAvailable, "List ISO 18092/FeliCa history"},
{"reader", CmdHFFelicaReader, IfPm3Felica, "Act like an ISO18092/FeliCa reader"}, {"reader", CmdHFFelicaReader, IfPm3Felica, "Act like an ISO18092/FeliCa reader"},
@ -1855,7 +1855,7 @@ static command_t CommandTable[] = {
{"raw", CmdHFFelicaCmdRaw, IfPm3Felica, "Send raw hex data to tag"}, {"raw", CmdHFFelicaCmdRaw, IfPm3Felica, "Send raw hex data to tag"},
{"rdunencrypted", CmdHFFelicaReadWithoutEncryption, IfPm3Felica, "read Block Data from authentication-not-required Service."}, {"rdunencrypted", CmdHFFelicaReadWithoutEncryption, IfPm3Felica, "read Block Data from authentication-not-required Service."},
{"wrunencrypted", CmdHFFelicaWriteWithoutEncryption, IfPm3Felica, "write Block Data to an authentication-not-required Service."}, {"wrunencrypted", CmdHFFelicaWriteWithoutEncryption, IfPm3Felica, "write Block Data to an authentication-not-required Service."},
{"----------- FeliCa Standard -----------", CmdHelp, IfPm3Iso14443a, ""}, {"----------- FeliCa Standard -----------", CmdHelp, AlwaysAvailable, ""},
//{"dump", CmdHFFelicaDump, IfPm3Felica, "Wait for and try dumping FeliCa"}, //{"dump", CmdHFFelicaDump, IfPm3Felica, "Wait for and try dumping FeliCa"},
{"rqservice", CmdHFFelicaRequestService, IfPm3Felica, "verify the existence of Area and Service, and to acquire Key Version."}, {"rqservice", CmdHFFelicaRequestService, IfPm3Felica, "verify the existence of Area and Service, and to acquire Key Version."},
{"rqresponse", CmdHFFelicaRequestResponse, IfPm3Felica, "verify the existence of a card and its Mode."}, {"rqresponse", CmdHFFelicaRequestResponse, IfPm3Felica, "verify the existence of a card and its Mode."},
@ -1874,7 +1874,7 @@ static command_t CommandTable[] = {
//{"readv2", CmdHFFelicaNotImplementedYet, IfPm3Felica, "read Block Data from authentication-required Service."}, //{"readv2", CmdHFFelicaNotImplementedYet, IfPm3Felica, "read Block Data from authentication-required Service."},
//{"writev2", CmdHFFelicaNotImplementedYet, IfPm3Felica, "write Block Data to authentication-required Service."}, //{"writev2", CmdHFFelicaNotImplementedYet, IfPm3Felica, "write Block Data to authentication-required Service."},
//{"uprandomid", CmdHFFelicaNotImplementedYet, IfPm3Felica, "update Random ID (IDr)."}, //{"uprandomid", CmdHFFelicaNotImplementedYet, IfPm3Felica, "update Random ID (IDr)."},
{"----------- FeliCa Light -----------", CmdHelp, IfPm3Iso14443a, ""}, {"----------- FeliCa Light -----------", CmdHelp, AlwaysAvailable, ""},
{"litesim", CmdHFFelicaSimLite, IfPm3Felica, "<NDEF2> - only reply to poll request"}, {"litesim", CmdHFFelicaSimLite, IfPm3Felica, "<NDEF2> - only reply to poll request"},
{"litedump", CmdHFFelicaDumpLite, IfPm3Felica, "Wait for and try dumping FelicaLite"}, {"litedump", CmdHFFelicaDumpLite, IfPm3Felica, "Wait for and try dumping FelicaLite"},
// {"sim", CmdHFFelicaSim, IfPm3Felica, "<UID> -- Simulate ISO 18092/FeliCa tag"} // {"sim", CmdHFFelicaSim, IfPm3Felica, "<UID> -- Simulate ISO 18092/FeliCa tag"}

View file

@ -2050,7 +2050,7 @@ static int CmdHFiClassReadTagFile(const char *Cmd) {
if (verbose) { if (verbose) {
PrintAndLogEx(INFO, "File: " _YELLOW_("%s"), filename); PrintAndLogEx(INFO, "File: " _YELLOW_("%s"), filename);
PrintAndLogEx(INFO, "File size %d bytes, file blocks %d (0x%02x)", bytes_read, bytes_read >> 3, bytes_read >> 3); PrintAndLogEx(INFO, "File size %zu bytes, file blocks %d (0x%x)", bytes_read, (uint16_t)(bytes_read >> 3), (uint16_t)(bytes_read >> 3));
PrintAndLogEx(INFO, "Printing blocks from"); PrintAndLogEx(INFO, "Printing blocks from");
PrintAndLogEx(INFO, "start " _YELLOW_("0x%02x") "end " _YELLOW_("0x%02x"), (startblock == 0) ? 6 : startblock, endblock); PrintAndLogEx(INFO, "start " _YELLOW_("0x%02x") "end " _YELLOW_("0x%02x"), (startblock == 0) ? 6 : startblock, endblock);
} }

View file

@ -1013,43 +1013,47 @@ static int CmdLegicDump(const char *Cmd) {
saveFile(filename, ".bin", data, readlen); saveFile(filename, ".bin", data, readlen);
saveFileEML(filename, data, readlen, 8); saveFileEML(filename, data, readlen, 8);
saveFileJSON(filename, jsfLegic, data, readlen); saveFileJSON(filename, jsfLegic, data, readlen);
free(data);
return PM3_SUCCESS; return PM3_SUCCESS;
} }
static int CmdLegicRestore(const char *Cmd) { static int CmdLegicRestore(const char *Cmd) {
char filename[FILE_PATH_SIZE] = {0x00}; char filename[FILE_PATH_SIZE] = {0x00};
size_t fileNlen = 0; bool errors = false, shall_obsfuscate = false, have_filename = false;
bool errors = false, shall_obsfuscate = false;
size_t numofbytes; size_t numofbytes;
uint8_t cmdp = 0; uint8_t cmdp = 0;
memset(filename, 0, sizeof(filename));
while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { while (param_getchar(Cmd, cmdp) != 0x00 && !errors) {
switch (tolower(param_getchar(Cmd, cmdp))) { switch (tolower(param_getchar(Cmd, cmdp))) {
case 'h': case 'h': {
errors = true; errors = true;
break; break;
case 'f': }
fileNlen = param_getstr(Cmd, cmdp + 1, filename, FILE_PATH_SIZE); case 'f': {
if (!fileNlen) if (param_getstr(Cmd, cmdp + 1, filename, FILE_PATH_SIZE) >= FILE_PATH_SIZE) {
errors = true; PrintAndLogEx(FAILED, "Filename too long");
break;
if (fileNlen > FILE_PATH_SIZE - 5) }
fileNlen = FILE_PATH_SIZE - 5; have_filename = true;
cmdp += 2; cmdp += 2;
break; break;
case 'x': }
case 'x': {
shall_obsfuscate = true; shall_obsfuscate = true;
cmdp++; cmdp++;
break; break;
default: }
default: {
PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp));
errors = true; errors = true;
break; break;
} }
} }
}
if (have_filename == false)
errors = true;
//Validations //Validations
if (errors || cmdp == 0) return usage_legic_restore(); if (errors || cmdp == 0) return usage_legic_restore();
@ -1076,7 +1080,7 @@ static int CmdLegicRestore(const char *Cmd) {
} }
if (card.cardsize != numofbytes) { if (card.cardsize != numofbytes) {
PrintAndLogEx(WARNING, "Fail, filesize and cardsize is not equal. [%zu != %u]", card.cardsize, numofbytes); PrintAndLogEx(WARNING, "Fail, filesize and cardsize is not equal. [%u != %zu]", card.cardsize, numofbytes);
free(data); free(data);
return PM3_EFILE; return PM3_EFILE;
} }
@ -1132,45 +1136,54 @@ static int CmdLegicRestore(const char *Cmd) {
static int CmdLegicELoad(const char *Cmd) { static int CmdLegicELoad(const char *Cmd) {
size_t numofbytes = 256; size_t numofbytes = 256;
int fileNameLen = 0;
char filename[FILE_PATH_SIZE] = {0x00}; char filename[FILE_PATH_SIZE] = {0x00};
bool errors = false, shall_obsfuscate = false; bool errors = false, shall_obsfuscate = false, have_filename = false;
uint8_t cmdp = 0; uint8_t cmdp = 0;
while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { while (param_getchar(Cmd, cmdp) != 0x00 && !errors) {
switch (tolower(param_getchar(Cmd, cmdp))) { switch (tolower(param_getchar(Cmd, cmdp))) {
case 'h' : case 'h' : {
return usage_legic_eload(); return usage_legic_eload();
case 'f' : }
fileNameLen = param_getstr(Cmd, cmdp + 1, filename, FILE_PATH_SIZE); case 'f' : {
if (!fileNameLen) if (param_getstr(Cmd, cmdp + 1, filename, FILE_PATH_SIZE) >= FILE_PATH_SIZE) {
errors = true; PrintAndLogEx(FAILED, "Filename too long");
if (fileNameLen > FILE_PATH_SIZE - 5) break;
fileNameLen = FILE_PATH_SIZE - 5; }
have_filename = true;
cmdp += 2; cmdp += 2;
break; break;
case 'x': }
case 'x': {
shall_obsfuscate = true; shall_obsfuscate = true;
cmdp++; cmdp++;
break; break;
case '0' : }
case '0' : {
numofbytes = 22; numofbytes = 22;
cmdp++; cmdp++;
break; break;
case '1' : }
case '1' : {
numofbytes = 256; numofbytes = 256;
cmdp++; cmdp++;
break; break;
case '2' : }
case '2' : {
numofbytes = 1024; numofbytes = 1024;
cmdp++; cmdp++;
break; break;
default : }
default : {
PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp));
errors = true; errors = true;
break; break;
} }
} }
}
if (have_filename == false)
errors = true;
//Validations //Validations
if (errors || strlen(Cmd) == 0) return usage_legic_eload(); if (errors || strlen(Cmd) == 0) return usage_legic_eload();
@ -1358,8 +1371,8 @@ static command_t CommandTable[] = {
{"sim", CmdLegicSim, IfPm3Legicrf, "Start tag simulator"}, {"sim", CmdLegicSim, IfPm3Legicrf, "Start tag simulator"},
{"wrbl", CmdLegicWrbl, IfPm3Legicrf, "Write data to a LEGIC Prime tag"}, {"wrbl", CmdLegicWrbl, IfPm3Legicrf, "Write data to a LEGIC Prime tag"},
{"crc", CmdLegicCalcCrc, AlwaysAvailable, "Calculate Legic CRC over given bytes"}, {"crc", CmdLegicCalcCrc, AlwaysAvailable, "Calculate Legic CRC over given bytes"},
{"eload", CmdLegicELoad, IfPm3Legicrf, "Load binary dump to emulator memory"}, {"eload", CmdLegicELoad, AlwaysAvailable, "Load binary dump to emulator memory"},
{"esave", CmdLegicESave, IfPm3Legicrf, "Save emulator memory to binary file"}, {"esave", CmdLegicESave, AlwaysAvailable, "Save emulator memory to binary file"},
{"wipe", CmdLegicWipe, IfPm3Legicrf, "Wipe a LEGIC Prime tag"}, {"wipe", CmdLegicWipe, IfPm3Legicrf, "Wipe a LEGIC Prime tag"},
{NULL, NULL, NULL, NULL} {NULL, NULL, NULL, NULL}
}; };

View file

@ -3712,7 +3712,7 @@ int CmdHF14AMfELoad(const char *Cmd) {
} }
} }
PrintAndLogEx(INFO, "Copying to emulator memory"); PrintAndLogEx(INFO, "Uploading to emulator memory");
// fast push mode // fast push mode
conn.block_after_ACK = true; conn.block_after_ACK = true;

View file

@ -2808,6 +2808,7 @@ static int CmdHF14MfuNDEF(const char *Cmd) {
if (status == -1) { if (status == -1) {
DropField(); DropField();
PrintAndLogEx(ERR, "Error: tag didn't answer to READ"); PrintAndLogEx(ERR, "Error: tag didn't answer to READ");
free(records);
return PM3_ESOFT; return PM3_ESOFT;
} }
} }

View file

@ -94,6 +94,10 @@ int JsonSaveStr(json_t *root, const char *path, const char *value) {
return JsonSaveJsonObject(root, path, json_string(value)); return JsonSaveJsonObject(root, path, json_string(value));
}; };
int JsonSaveBoolean(json_t *root, const char *path, bool value) {
return JsonSaveJsonObject(root, path, json_boolean(value));
}
int JsonSaveBufAsHexCompact(json_t *elm, const char *path, uint8_t *data, size_t datalen) { int JsonSaveBufAsHexCompact(json_t *elm, const char *path, uint8_t *data, size_t datalen) {
char *msg = sprint_hex_inrow(data, datalen); char *msg = sprint_hex_inrow(data, datalen);
if (msg && strlen(msg) && msg[strlen(msg) - 1] == ' ') if (msg && strlen(msg) && msg[strlen(msg) - 1] == ' ')

View file

@ -24,6 +24,7 @@ const char *GetApplicationDataName(tlv_tag_t tag);
int JsonSaveJsonObject(json_t *root, const char *path, json_t *value); int JsonSaveJsonObject(json_t *root, const char *path, json_t *value);
int JsonSaveStr(json_t *root, const char *path, const char *value); int JsonSaveStr(json_t *root, const char *path, const char *value);
int JsonSaveBoolean(json_t *root, const char *path, bool value);
int JsonSaveInt(json_t *root, const char *path, int value); int JsonSaveInt(json_t *root, const char *path, int value);
int JsonSaveBufAsHexCompact(json_t *elm, const char *path, uint8_t *data, size_t datalen); int JsonSaveBufAsHexCompact(json_t *elm, const char *path, uint8_t *data, size_t datalen);
int JsonSaveBufAsHex(json_t *elm, const char *path, uint8_t *data, size_t datalen); int JsonSaveBufAsHex(json_t *elm, const char *path, uint8_t *data, size_t datalen);

View file

@ -38,6 +38,7 @@
// this define is needed for scandir/alphasort to work // this define is needed for scandir/alphasort to work
#define _GNU_SOURCE #define _GNU_SOURCE
#include "fileutils.h" #include "fileutils.h"
#include "settings.h"
#include <dirent.h> #include <dirent.h>
#include <ctype.h> #include <ctype.h>
@ -425,6 +426,9 @@ int saveFileJSON(const char *preferredName, JSONFileType ftype, uint8_t *data, s
} }
} }
break; break;
case jsfSettings:
settings_save_callback (root);
break;
default: default:
break; break;
} }
@ -863,7 +867,9 @@ int loadFileJSON(const char *preferredName, void *data, size_t maxdatalen, size_
} }
*datalen = sptr; *datalen = sptr;
} }
if (!strcmp(ctype,"settings")) {
settings_load_callback (root);
}
PrintAndLogEx(SUCCESS, "loaded from JSON file " _YELLOW_("%s"), fileName); PrintAndLogEx(SUCCESS, "loaded from JSON file " _YELLOW_("%s"), fileName);
out: out:
json_decref(root); json_decref(root);

View file

@ -62,6 +62,7 @@ typedef enum {
jsfT55x7, jsfT55x7,
jsfT5555, jsfT5555,
jsfMfPlusKeys, jsfMfPlusKeys,
jsfSettings,
} JSONFileType; } JSONFileType;
typedef enum { typedef enum {

View file

@ -1,543 +0,0 @@
local utils = require('utils')
local cmds = require('commands')
local getopt = require('getopt')
local ansicolors = require('ansicolors')
--[[
script to create a clone-dump with new crc
Author: mosci
my Fork: https://github.com/icsom/proxmark3.git
Upstream: https://github.com/Proxmark/proxmark3.git
1. read tag-dump, xor byte 22..end with byte 0x05 of the inputfile
2. write to outfile
3. set byte 0x05 to newcrc
4. until byte 0x21 plain like in inputfile
5. from 0x22..end xored with newcrc
6. calculate new crc on each segment (needs to know the new MCD & MSN0..2)
simplest usage:
read a valid legic tag with 'hf legic reader'
save the dump with 'hf legic dump o orig'
place your 'empty' tag on the reader and run 'script run Legic_clone -i orig.bin -w'
you will see some output like:
read 1024 bytes from orig.bin
place your empty tag onto the PM3 to read and display the MCD & MSN0..2
the values will be shown below
confirm when ready [y/n] ?y
#db# setting up legic card
#db# MIM 256 card found, reading card ...
#db# Card read, use 'hf legic decode' or
#db# 'data hexsamples 8' to view results
0b ad c0 de <- !! here you'll see the MCD & MSN of your empty tag, which has to be typed in manually as seen below !!
type in MCD as 2-digit value - e.g.: 00 (default: 79 )
> 0b
type in MSN0 as 2-digit value - e.g.: 01 (default: 28 )
> ad
type in MSN1 as 2-digit value - e.g.: 02 (default: d1 )
> c0
type in MSN2 as 2-digit value - e.g.: 03 (default: 43 )
> de
MCD:0b, MSN:ad c0 de, MCC:79 <- this crc is calculated from the MCD & MSN and must match the one on yout empty tag
wrote 1024 bytes to myLegicClone.hex
enter number of bytes to write? (default: 86 )
loaded 1024 samples
#db# setting up legic card
#db# MIM 256 card found, writing 0x00 - 0x01 ...
#db# write successful
...
#db# setting up legic card
#db# MIM 256 card found, writing 0x56 - 0x01 ...
#db# write successful
proxmark3>
the default value (number of bytes to write) is calculated over all valid segments and should be ok - just hit enter, wait until write has finished
and your clone should be ready (except there has to be a additional KGH-CRC to be calculated - which credentials are unknown until yet)
the '-w' switch will only work with my fork - it needs the binary legic_crc8 which is not part of the proxmark3-master-branch
also the ability to write DCF is not possible with the proxmark3-master-branch
but creating dumpfile-clone files will be possible (without valid segment-crc - this has to done manually with)
(example) Legic-Prime Layout with 'Kaba Group Header'
+----+----+----+----+----+----+----+----+
0x00|MCD |MSN0|MSN1|MSN2|MCC | 60 | ea | 9f |
+----+----+----+----+----+----+----+----+
0x08| ff | 00 | 00 | 00 | 11 |Bck0|Bck1|Bck2|
+----+----+----+----+----+----+----+----+
0x10|Bck3|Bck4|Bck5|BCC | 00 | 00 |Seg0|Seg1|
+----+----+----+----+----+----+----+----+
0x18|Seg2|Seg3|SegC|Stp0|Stp1|Stp2|Stp3|UID0|
+----+----+----+----+----+----+----+----+
0x20|UID1|UID2|kghC|
+----+----+----+
MCD= ManufacturerID (1 Byte)
MSN0..2= ManufactureSerialNumber (3 Byte)
MCC= CRC (1 Byte) calculated over MCD,MSN0..2
DCF= DecrementalField (2 Byte) 'credential' (enduser-Tag) seems to have always DCF-low=0x60 DCF-high=0xea
Bck0..5= Backup (6 Byte) Bck0 'dirty-flag', Bck1..5 SegmentHeader-Backup
BCC= BackupCRC (1 Byte) CRC calculated over Bck1..5
Seg0..3= SegmentHeader (on MIM 4 Byte )
SegC= SegmentCRC (1 Byte) calculated over MCD,MSN0..2,Seg0..3
Stp0..n= Stamp0... (variable length) length = Segment-Len - UserData - 1
UID0..n= UserDater (variable length - with KGH hex 0x00-0x63 / dec 0-99) length = Segment-Len - WRP - WRC - 1
kghC= KabaGroupHeader (1 Byte + addr 0x0c must be 0x11)
as seen on this example: addr 0x05..0x08 & 0x0c must have been set to this values - otherwise kghCRC will not be created by a official reader (not accepted)
--]]
copyright = ''
author = 'Mosci'
version = 'v1.0.2'
desc = [[
This is a script which creates a clone-dump of a dump from a Legic Prime Tag (MIM256 or MIM1024)
(created with 'hf legic dump f my_dump')
]]
example = [[
script run legic_clone -i my_dump.bin -o my_clone.bin -c f8
script run legic_clone -i my_dump.bin -d -s
]]
usage = [[
script run legic_clone -h -i <file> -o <file> -c <crc> -d -s -w
]]
arguments = [[
required :
-i <input file> (file to read data from, must be in binary format (*.bin))
optional :
-h - Help text
-o <output file> - requires option -c to be given
-c <new-tag crc> - requires option -o to be given
-d - Display content of found Segments
-s - Display summary at the end
-w - write directly to Tag - a file myLegicClone.bin will be generated also
e.g.:
hint: using the CRC '00' will result in a plain dump ( -c 00 )
]]
local bxor = bit32.bxor
-- we need always 2 digits
local function prepend_zero(s)
if (string.len(s) == 1) then
return '0' .. s
else
if (string.len(s) == 0) then
return '00'
else
return s
end
end
end
---
-- This is only meant to be used when errors occur
local function oops(err)
print('ERROR:', err)
core.clearCommandBuffer()
return nil, err
end
-- read LEGIC data
local function readlegicdata( offset, length, iv )
-- Read data
local command = Command:newMIX{
cmd = cmds.CMD_HF_LEGIC_READER
, arg1 = offset
, arg2 = length
, arg3 = iv
, data = nil
}
local result, err = command:sendMIX()
if not result then return oops(err) end
-- result is a packed data structure, data starts at offset 33
return result
end
---
-- Usage help
local function help()
print(copyright)
print(author)
print(version)
print(desc)
print(ansicolors.cyan..'Usage'..ansicolors.reset)
print(usage)
print(ansicolors.cyan..'Arguments'..ansicolors.reset)
print(arguments)
print(ansicolors.cyan..'Example usage'..ansicolors.reset)
print(example)
end
-- Check availability of file
local function file_check(file_name)
local file_found = io.open(file_name, "r")
if not file_found then
file_found = false
else
file_found = true
end
return file_found
end
--- xor-wrapper
-- xor all from addr 0x22 (start counting from 1 => 23)
local function xorme(hex, xor, index)
if ( index >= 23 ) then
return ('%02x'):format(bxor( tonumber(hex,16) , tonumber(xor,16) ))
else
return hex
end
end
-- read input-file into array
local function getInputBytes(infile)
local line
local bytes = {}
local fhi,err = io.open(infile,"rb")
if err then print("OOps ... faild to read from file ".. infile); return false; end
str = fhi:read("*all")
for c in (str or ''):gmatch'.' do
bytes[#bytes+1] = ('%02x'):format(c:byte())
end
fhi:close()
print("\nread ".. #bytes .." bytes from ".. infile)
return bytes
end
-- write to file
local function writeOutputBytes(bytes, outfile)
local fho,err = io.open(outfile,"wb")
if err then print("OOps ... faild to open output-file ".. outfile); return false; end
for i = 1, #bytes do
fho:write(string.char(tonumber(bytes[i],16)))
end
fho:close()
print("\nwrote ".. #bytes .." bytes to " .. outfile)
return true
end
-- xore certain bytes
local function xorBytes(inBytes, crc)
local bytes = {}
for index = 1, #inBytes do
bytes[index] = xorme(inBytes[index], crc, index)
end
if (#inBytes == #bytes) then
-- replace crc
bytes[5] = string.sub(crc,-2)
return bytes
else
print("error: byte-count missmatch")
return false
end
end
-- get raw segment-data
function getSegmentData(bytes, start, index)
local raw, len, valid, last, wrp, wrc, rd, crc
local segment = {}
segment[0] = bytes[start]..' '..bytes[start+1]..' '..bytes[start+2]..' '..bytes[start+3]
-- flag = high nibble of byte 1
segment[1] = string.sub(bytes[start+1],0,1)
-- valid = bit 6 of byte 1
segment[2] = tonumber(bit32.extract('0x'..bytes[start+1],6,1),16)
-- last = bit 7 of byte 1
segment[3] = tonumber(bit32.extract('0x'..bytes[start+1],7,1),16)
-- len = (byte 0)+(bit0-3 of byte 1)
segment[4] = tonumber(('%03x'):format(tonumber(bit32.extract('0x'..bytes[start+1],0,3),16)..tonumber(bytes[start],16)),16)
-- wrp (write proteted) = byte 2
segment[5] = tonumber(bytes[start+2])
-- wrc (write control) - bit 4-6 of byte 3
segment[6] = tonumber(bit32.extract('0x'..bytes[start+3],4,3),16)
-- rd (read disabled) - bit 7 of byte 3
segment[7] = tonumber(bit32.extract('0x'..bytes[start+3],7,1),16)
-- crc byte 4
segment[8] = bytes[start+4]
-- segment index
segment[9] = index
-- # crc-byte
segment[10] = start+4
return segment
end
--- Kaba Group Header
-- checks if a segment does have a kghCRC
-- returns boolean false if no kgh has being detected or the kghCRC if a kgh was detected
function CheckKgh(bytes, segStart, segEnd)
if (bytes[8]=='9f' and bytes[9]=='ff' and bytes[13]=='11') then
local i
local data = {}
segStart = tonumber(segStart, 10)
segEnd = tonumber(segEnd, 10)
local dataLen = segEnd-segStart-5
--- gather creadentials for verify
local WRP = bytes[(segStart+2)]
local WRC = ("%02x"):format(tonumber(bit32.extract("0x"..bytes[segStart+3],4,3),16))
local RD = ("%02x"):format(tonumber(bit32.extract("0x"..bytes[segStart+3],7,1),16))
local XX = "00"
cmd = bytes[1]..bytes[2]..bytes[3]..bytes[4]..WRP..WRC..RD..XX
for i = (segStart+5), (segStart+5+dataLen-2) do
cmd = cmd..bytes[i]
end
local KGH = ("%02x"):format(utils.Crc8Legic(cmd))
if (KGH == bytes[segEnd-1]) then
return KGH
else
return false
end
else
return false
end
end
-- get only the addresses of segemnt-crc's and the length of bytes
function getSegmentCrcBytes(bytes)
local start = 23
local index = 0
local crcbytes = {}
repeat
seg = getSegmentData(bytes,start,index)
crcbytes[index] = seg[10]
start = start + seg[4]
index = index + 1
until (seg[3] == 1 or tonumber(seg[9]) == 126 )
crcbytes[index] = start
return crcbytes
end
-- print segment-data (hf legic info like)
function displaySegments(bytes)
--display segment header(s)
start = 23
index = '00'
--repeat until last-flag ist set to 1 or segment-index has reached 126
repeat
wrc = ''
wrp = ''
pld = ''
Seg = getSegmentData(bytes, start, index)
KGH = CheckKgh(bytes, start, (start+tonumber(Seg[4],10)))
printSegment(Seg)
-- wrc
if (Seg[6] > 0) then
print("WRC protected area:")
-- length of wrc = wrc
for i=1, Seg[6] do
-- starts at (segment-start + segment-header + segment-crc)-1
wrc = wrc..bytes[(start+4+1+i)-1]..' '
end
print(wrc)
elseif (Seg[5] > 0) then
print("Remaining write protected area:")
-- length of wrp = (wrp-wrc)
for i=1, (Seg[5]-Seg[6]) do
-- starts at (segment-start + segment-header + segment-crc + wrc)-1
wrp = wrp..bytes[(start+4+1+Seg[6]+i)-1]..' '
end
print(wrp)
end
-- payload
print("Remaining segment payload:")
--length of payload = segment-len - segment-header - segment-crc - wrp -wrc
for i=1, (Seg[4]-4-1-Seg[5]-Seg[6]) do
-- starts at (segment-start + segment-header + segment-crc + segment-wrp + segemnt-wrc)-1
pld = pld..bytes[(start+4+1+Seg[5]+Seg[6]+i)-1]..' '
end
print(pld)
if (KGH) then
print("'Kaba Group Header' detected")
end
start = start+Seg[4]
index = prepend_zero(tonumber(Seg[9])+1)
until (Seg[3] == 1 or tonumber(Seg[9]) == 126 )
end
-- print Segment values
function printSegment(SegmentData)
res = "\nSegment "..SegmentData[9]..": "
res = res.. "raw header="..SegmentData[0]..", "
res = res.. "flag="..SegmentData[1].." (valid="..SegmentData[2].." last="..SegmentData[3].."), "
res = res.. "len="..("%04d"):format(SegmentData[4])..", "
res = res.. "WRP="..prepend_zero(SegmentData[5])..", "
res = res.. "WRC="..prepend_zero(SegmentData[6])..", "
res = res.. "RD="..SegmentData[7]..", "
res = res.. "crc="..SegmentData[8]
print(res)
end
-- write clone-data to tag
function writeToTag(plainBytes)
local SegCrcs = {}
local output
local readbytes
if(utils.confirm("\nplace your empty tag onto the PM3 to restore the data of the input file\nthe CRCs will be calculated as needed\n confirm when ready") == false) then
return
end
readbytes = readlegicdata(0, 4, 0x55)
-- gather MCD & MSN from new Tag - this must be enterd manually
print("\nthese are the MCD MSN0 MSN1 MSN2 from the Tag that has being read:")
plainBytes[1] = ('%02x'):format(readbytes:byte(33))
plainBytes[2] = ('%02x'):format(readbytes:byte(34))
plainBytes[3] = ('%02x'):format(readbytes:byte(35))
plainBytes[4] = ('%02x'):format(readbytes:byte(36))
MCD = plainBytes[1]
MSN0 = plainBytes[2]
MSN1 = plainBytes[3]
MSN2 = plainBytes[4]
-- calculate crc8 over MCD & MSN
cmd = MCD..MSN0..MSN1..MSN2
MCC = ("%02x"):format(utils.Crc8Legic(cmd))
print("MCD:"..MCD..", MSN:"..MSN0.." "..MSN1.." "..MSN2..", MCC:"..MCC)
-- calculate new Segment-CRC for each valid segment
SegCrcs = getSegmentCrcBytes(plainBytes)
for i=0, (#SegCrcs-1) do
-- SegCrcs[i]-4 = address of first byte of segmentHeader (low byte segment-length)
segLen = tonumber(("%1x"):format(tonumber(bit32.extract("0x"..plainBytes[(SegCrcs[i]-3)],0,3),16))..("%02x"):format(tonumber(plainBytes[SegCrcs[i]-4],16)),16)
segStart = (SegCrcs[i]-4)
segEnd = (SegCrcs[i]-4+segLen)
KGH = CheckKgh(plainBytes,segStart,segEnd)
if (KGH) then
print("'Kaba Group Header' detected - re-calculate...")
end
cmd = MCD..MSN0..MSN1..MSN2..plainBytes[SegCrcs[i]-4]..plainBytes[SegCrcs[i]-3]..plainBytes[SegCrcs[i]-2]..plainBytes[SegCrcs[i]-1]
plainBytes[SegCrcs[i]] = ("%02x"):format(utils.Crc8Legic(cmd))
end
-- apply MCD & MSN to plain data
plainBytes[1] = MCD
plainBytes[2] = MSN0
plainBytes[3] = MSN1
plainBytes[4] = MSN2
plainBytes[5] = MCC
-- prepare plainBytes for writing (xor plain data with new MCC)
bytes = xorBytes(plainBytes, MCC)
-- write data to file
if (writeOutputBytes(bytes, "myLegicClone.bin")) then
-- write pm3-buffer to Tag
cmd = ('hf legic restore f myLegicClone')
core.console(cmd)
end
end
-- main function
function main(args)
-- some variables
local i = 0
local oldcrc, newcrc, infile, outfile
local bytes = {}
local segments = {}
-- parse arguments for the script
for o, a in getopt.getopt(args, 'hwsdc:i:o:') do
-- output file
if o == 'o' then
outfile = a
ofs = true
if (file_check(a)) then
local answer = utils.confirm('\nthe output-file '..a..' already exists!\nthis will delete the previous content!\ncontinue?')
if (answer==false) then return oops('quiting') end
end
end
-- input file
if o == 'i' then
infile = a
if (file_check(infile)==false) then
return oops('input file: '..infile..' not found')
else
bytes = getInputBytes(infile)
oldcrc = bytes[5]
ifs = true
if (bytes == false) then return oops('couldnt get input bytes') end
end
i = i+1
end
-- new crc
if o == 'c' then
newcrc = a:lower()
ncs = true
end
-- display segments switch
if o == 'd' then ds = true; end
-- display summary switch
if o == 's' then ss = true; end
-- write to tag switch
if o == 'w' then ws = true; end
-- help
if o == 'h' then return help() end
end
if (not ifs) then return oops('option -i <input file> is required but missing') end
-- bytes to plain
bytes = xorBytes(bytes, oldcrc)
-- show segments (works only on plain bytes)
if (ds) then
print("+------------------------------------------- Segments -------------------------------------------+")
displaySegments(bytes);
end
if (ofs and ncs) then
-- xor bytes with new crc
newBytes = xorBytes(bytes, newcrc)
-- write output
if (writeOutputBytes(newBytes, outfile)) then
-- show summary if requested
if (ss) then
-- information
res = "\n+-------------------------------------------- Summary -------------------------------------------+"
res = res .."\ncreated clone_dump from\n\t"..infile.." crc: "..oldcrc.."\ndump_file:"
res = res .."\n\t"..outfile.." crc: "..string.sub(newcrc,-2)
res = res .."\nyou may load the new file with: hf legic eload "..outfile
res = res .."\n\nif you don't write to tag immediately ('-w' switch) you will need to recalculate each segmentCRC"
res = res .."\nafter writing this dump to a tag!"
res = res .."\n\na segmentCRC gets calculated over MCD,MSN0..3,Segment-Header0..3"
res = res .."\ne.g. (based on Segment00 of the data from "..infile.."):"
res = res .."\nhf legic crc d "..bytes[1]..bytes[2]..bytes[3]..bytes[4]..bytes[23]..bytes[24]..bytes[25]..bytes[26].." u "..newcrc.." c 8"
-- this can not be calculated without knowing the new MCD, MSN0..2
print(res)
end
end
else
if (ss) then
-- show why the output-file was not written
print("\nnew file not written - some arguments are missing ..")
print("output file: ".. (ofs and outfile or "not given"))
print("new crc: ".. (ncs and newcrc or "not given"))
end
end
-- write to tag
if (ws and ( #bytes == 1024 or #bytes == 256)) then
writeToTag(bytes)
end
end
-- call main with arguments
main(args)

View file

@ -27,6 +27,7 @@
#include "comms.h" #include "comms.h"
#include "fileutils.h" #include "fileutils.h"
#include "flash.h" #include "flash.h"
#include "settings.h"
static void showBanner(void) { static void showBanner(void) {
g_printAndLog = PRINTANDLOG_PRINT; g_printAndLog = PRINTANDLOG_PRINT;
@ -581,6 +582,12 @@ int main(int argc, char *argv[]) {
set_my_executable_path(); set_my_executable_path();
set_my_user_directory(); set_my_user_directory();
// Settings Load and Test
// settings_load ();
// settings_save ();
// printf ("Ver : %s\n",mySettings.version);
// End Settings
for (int i = 1; i < argc; i++) { for (int i = 1; i < argc; i++) {
if (argv[i][0] != '-') { if (argv[i][0] != '-') {

194
client/settings.c Normal file
View file

@ -0,0 +1,194 @@
/*****************************************************************************
* WARNING
*
* THIS CODE IS CREATED FOR EXPERIMENTATION AND EDUCATIONAL USE ONLY.
*
* USAGE OF THIS CODE IN OTHER WAYS MAY INFRINGE UPON THE INTELLECTUAL
* PROPERTY OF OTHER PARTIES, SUCH AS INSIDE SECURE AND HID GLOBAL,
* AND MAY EXPOSE YOU TO AN INFRINGEMENT ACTION FROM THOSE PARTIES.
*
* THIS CODE SHOULD NEVER BE USED TO INFRINGE PATENTS OR INTELLECTUAL PROPERTY RIGHTS.
*
*****************************************************************************
*
* This file is part of loclass. It is a reconstructon of the cipher engine
* used in iClass, and RFID techology.
*
* The implementation is based on the work performed by
* Flavio D. Garcia, Gerhard de Koning Gans, Roel Verdult and
* Milosch Meriac in the paper "Dismantling IClass".
*
* Copyright (C) 2014 Martin Holst Swende
*
* This is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation, or, at your option, any later version.
*
* This file 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.
*
* You should have received a copy of the GNU General Public License
* along with loclass. If not, see <http://www.gnu.org/licenses/>.
*
*
****************************************************************************/
//-----------------------------------------------------------------------------
// Settings Functions
//-----------------------------------------------------------------------------
#include "settings.h"
#include "comms.h"
#include "emv/emvjson.h"
// Load all settings into memory (struct)
int settings_load (void) {
// loadFileJson wants these, so pass in place holder values, though not used
// in settings load;
uint8_t dummyData = 0x00;
size_t dummyDL = 0x00;
// clear all settings
memset (&mySettings,0x00,sizeof(mySettings));
if (loadFileJSON(settingsFilename, &dummyData, sizeof(dummyData), &dummyDL) == PM3_SUCCESS) {
printf ("==> Settings Loaded\n");
mySettings.loaded = true;
}
// Test results
/*
bool os_windows_usecolor;
bool os_windows_useansicolor;
int window_xpos;
int window_ypos;
int window_hsize;
int window_wsize;
bool use_emojis
bool use_hints
*/
printf (" Settings Version : [%s]\n", mySettings.version);
printf (" os_windows_usecolor (bool) : [%d]\n", mySettings.os_windows_usecolor);
printf (" os_windows_useAnsicolor (bool) : [%d]\n", mySettings.os_windows_useansicolor);
printf (" window_xpos (int) : [%d]\n", mySettings.window_xpos);
printf (" window_ypos (int) : [%d]\n", mySettings.window_ypos);
printf (" window_hsize (int) : [%d]\n", mySettings.window_hsize);
printf (" window_wsize (int) : [%d]\n", mySettings.window_wsize);
printf (" use emoji (bool) : [%d]\n", mySettings.use_emojis);
printf (" use hints (bool) : [%d]\n", mySettings.use_hints);
return PM3_SUCCESS;
}
// Save all settings from memory (struct) to file
int settings_save(void) {
// Note sure if backup has value ?
char backupFilename[500];
snprintf(backupFilename, sizeof(backupFilename),"%s.bak",settingsFilename);
if (fileExists (backupFilename)) {
if (remove (backupFilename) != 0) {
PrintAndLogEx (FAILED, "Error - could not delete old settings backup file \"%s\"",backupFilename);
return PM3_ESOFT;
}
}
if (fileExists (settingsFilename)) {
if (rename (settingsFilename,backupFilename) != 0) {
PrintAndLogEx (FAILED, "Error - could not backup settings file \"%s\" to \"%s\"",settingsFilename,backupFilename);
return PM3_ESOFT;
}
}
uint8_t dummyData = 0x00;
size_t dummyDL = 0x00;
if (saveFileJSON(settingsFilename, jsfSettings, &dummyData, dummyDL) == PM3_SUCCESS)
PrintAndLogEx (NORMAL, "settings have been saved to \"%s\"",settingsFilename);
return PM3_SUCCESS;
}
void settings_save_callback(json_t *root) {
printf ("==> Save Settings\n");
//JsonSaveStr(root, "FileType", "settings");
//JsonSaveStr (root,"Test1.Test2","test settings");
/*
"version": "1.0 Nov 2019",
"os.windows.usecolor": true,
"os.windows.useAnsiColor": true,
"window.xpos": 10,
"window.ypos": 10,
"window.hsize": 300,
"window.wsize": 600
*/
JsonSaveStr (root,"FileType","settings");
JsonSaveStr (root,"version","1.0 Nov 2019");//mySettings.version);
JsonSaveBoolean (root,"os.windows.useColor", mySettings.os_windows_usecolor);
JsonSaveBoolean (root,"os.windows.useAnsiColor", mySettings.os_windows_useansicolor);
JsonSaveInt (root,"window.xpos", mySettings.window_xpos);
JsonSaveInt (root,"window.ypos", mySettings.window_ypos);
JsonSaveInt (root,"window.hsize", mySettings.window_hsize);
JsonSaveInt (root,"window.wsize", mySettings.window_wsize);
JsonSaveBoolean (root,"client.useEmojis", mySettings.use_emojis);
JsonSaveBoolean (root,"client.useHints", mySettings.use_hints);
}
void settings_load_callback(json_t *root) {
json_error_t up_error = {0};
int b1;
int i1;
const char *s1;
if (json_unpack_ex(root, &up_error , 0, "{s:s}","version", &s1) == 0)
strncpy (mySettings.version,s1,sizeof (mySettings.version) - 1);
else
strncpy (mySettings.version,"unknown",sizeof (mySettings.version) - 1);
// os.windows...
if (json_unpack_ex(root,&up_error, 0, "{s:b}","os.windows.useColor",&b1) == 0)
mySettings.os_windows_usecolor = b1;
else // default
mySettings.os_windows_useansicolor = false;
if (json_unpack_ex(root,&up_error, 0, "{s:b}","os.windows.useAnsiColor",&b1) == 0)
mySettings.os_windows_useansicolor = b1;
else // default
mySettings.os_windows_useansicolor = false;
// window...
if (json_unpack_ex(root,&up_error, 0, "{s:i}","window.xpos",&i1) == 0)
mySettings.window_xpos = i1;
else // default
mySettings.window_xpos = 0;
if (json_unpack_ex(root,&up_error, 0, "{s:i}","window.ypos",&i1) == 0)
mySettings.window_ypos = i1;
else // default
mySettings.window_ypos = 0;
if (json_unpack_ex(root,&up_error, 0, "{s:i}","window.hsize",&i1) == 0)
mySettings.window_hsize = i1;
else // default
mySettings.window_hsize = 0;
if (json_unpack_ex(root,&up_error, 0, "{s:i}","window.wsize",&i1) == 0)
mySettings.window_wsize = i1;
else // default
mySettings.window_wsize = 0;
// Use EMOJIS
if (json_unpack_ex(root,&up_error, 0, "{s:b}","client.useEmojis",&b1) == 0)
mySettings.use_emojis = b1;
else // default
mySettings.use_emojis = false;
// Use Hints
if (json_unpack_ex(root,&up_error, 0, "{s:b}","client.useHints",&b1) == 0)
mySettings.use_hints = b1;
else // default
mySettings.use_hints = false;
}

40
client/settings.h Normal file
View file

@ -0,0 +1,40 @@
//-----------------------------------------------------------------------------
// Copyright (C) 2009 Michael Gernoth <michael at gernoth.net>
// Copyright (C) 2010 iZsh <izsh at fail0verflow.com>
//
// 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.
//-----------------------------------------------------------------------------
// Settings Functions
//-----------------------------------------------------------------------------
#ifndef settings_h
#define settings_h
#include "fileutils.h"
#define settingsFilename "settings.json"
typedef struct {
bool loaded;
char version[20];
bool os_windows_usecolor;
bool os_windows_useansicolor;
int window_xpos;
int window_ypos;
int window_hsize;
int window_wsize;
bool use_emojis;
bool use_hints;
} settings_t;
// Settings struct so as to be available to other modules by including settings.h
settings_t mySettings;
int settings_load (void);
int settings_save (void);
void settings_save_callback (json_t *root);
void settings_load_callback (json_t *root);
#endif