mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2024-09-21 15:56:21 +08:00
commit
0fbc723c64
|
@ -2040,7 +2040,7 @@ void T55xx_ChkPwds(uint8_t flags) {
|
|||
if (isok != sizeof(counter))
|
||||
goto OUT;
|
||||
|
||||
pwdCount = counter[1] << 8 | counter[0];
|
||||
pwdCount = (uint16_t)(counter[1] << 8 | counter[0]);
|
||||
|
||||
if (pwdCount == 0 || pwdCount == 0xFFFF)
|
||||
goto OUT;
|
||||
|
|
|
@ -205,6 +205,7 @@ CMDSRCS = crapto1/crapto1.c \
|
|||
cmdhffido.c \
|
||||
cmdhffelica.c \
|
||||
cmdhfthinfilm.c \
|
||||
cmdhfcryptorf.c \
|
||||
cmdhflto.c \
|
||||
cmdhw.c \
|
||||
cmdlf.c \
|
||||
|
@ -251,7 +252,8 @@ CMDSRCS = crapto1/crapto1.c \
|
|||
flash.c \
|
||||
wiegand_formats.c \
|
||||
wiegand_formatutils.c \
|
||||
cardhelper.c
|
||||
cardhelper.c \
|
||||
settings.c
|
||||
|
||||
cpu_arch = $(shell uname -m)
|
||||
ifneq ($(findstring 86, $(cpu_arch)), )
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include "cmdhffido.h" // FIDO authenticators
|
||||
#include "cmdhfthinfilm.h" // Thinfilm
|
||||
#include "cmdhflto.h" // LTO-CM
|
||||
#include "cmdhfcryptorf.h" // CryptoRF
|
||||
#include "cmdtrace.h" // trace list
|
||||
#include "ui.h"
|
||||
#include "cmddata.h"
|
||||
|
@ -149,11 +150,22 @@ int CmdHFSearch(const char *Cmd) {
|
|||
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)
|
||||
PROMPT_CLEARLINE;
|
||||
PrintAndLogEx(INPLACE, "Searching for ISO14443-B tag...");
|
||||
if (IfPm3Iso14443a()) {
|
||||
if (IfPm3Iso14443b()) {
|
||||
if (readHF14B(false) == 1) {
|
||||
PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("ISO14443-B tag") "found\n");
|
||||
res = PM3_SUCCESS;
|
||||
|
@ -281,6 +293,7 @@ static command_t CommandTable[] = {
|
|||
{"14a", CmdHF14A, AlwaysAvailable, "{ ISO14443A RFIDs... }"},
|
||||
{"14b", CmdHF14B, AlwaysAvailable, "{ ISO14443B RFIDs... }"},
|
||||
{"15", CmdHF15, AlwaysAvailable, "{ ISO15693 RFIDs... }"},
|
||||
// {"cryptorf", CmdHFCryptoRF, AlwaysAvailable, "{ CryptoRF RFIDs... }"},
|
||||
{"epa", CmdHFEPA, AlwaysAvailable, "{ German Identification Card... }"},
|
||||
{"felica", CmdHFFelica, AlwaysAvailable, "{ ISO18092 / Felica RFIDs... }"},
|
||||
{"fido", CmdHFFido, AlwaysAvailable, "{ FIDO and FIDO2 authenticators... }"},
|
||||
|
|
|
@ -1245,6 +1245,135 @@ int CmdHF14A(const char *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) {
|
||||
clearCommandBuffer();
|
||||
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]);
|
||||
|
||||
bool isMifareClassic = true;
|
||||
bool isMifareDesfire = false;
|
||||
bool isMifareDESFire = false;
|
||||
bool isMifarePlus = 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) {
|
||||
case 0x00:
|
||||
isMifareClassic = false;
|
||||
|
@ -1305,8 +1488,9 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) {
|
|||
if (tagT != UL_ERROR) {
|
||||
ul_print_type(tagT, 0);
|
||||
isMifareUltralight = true;
|
||||
printTag("MIFARE Ultralight/C/NTAG Compatible");
|
||||
} else {
|
||||
PrintAndLogEx(SUCCESS, "TYPE: Possible AZTEK (iso14443a compliant)");
|
||||
printTag("Possible AZTEK (iso14443a compliant)");
|
||||
}
|
||||
|
||||
// reconnect for further tests
|
||||
|
@ -1323,64 +1507,26 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) {
|
|||
return select_status;
|
||||
}
|
||||
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:
|
||||
PrintAndLogEx(SUCCESS, "TYPE: " _YELLOW_("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"));
|
||||
printTag("FM11RF005SH (Shanghai Metro)");
|
||||
break;
|
||||
case 0x20:
|
||||
PrintAndLogEx(SUCCESS, "TYPE: " _YELLOW_("NXP MIFARE DESFire 4k | DESFire EV1 2k/4k/8k | Plus 2k/4k SL3 | 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;
|
||||
printTag("JCOP 31/41");
|
||||
break;
|
||||
case 0x28:
|
||||
PrintAndLogEx(SUCCESS, "TYPE: " _YELLOW_("JCOP31 or JCOP41 v2.3.1"));
|
||||
printTag("JCOP31 or JCOP41 v2.3.1");
|
||||
break;
|
||||
case 0x38:
|
||||
PrintAndLogEx(SUCCESS, "TYPE: " _YELLOW_("Nokia 6212 or 6131 MIFARE CLASSIC 4K"));
|
||||
break;
|
||||
case 0x88:
|
||||
PrintAndLogEx(SUCCESS, "TYPE: " _YELLOW_("Infineon MIFARE CLASSIC 1K"));
|
||||
printTag("Nokia 6212 or 6131");
|
||||
break;
|
||||
case 0x98:
|
||||
PrintAndLogEx(SUCCESS, "TYPE: " _YELLOW_("Gemplus MPCOS"));
|
||||
printTag("Gemplus MPCOS");
|
||||
break;
|
||||
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
|
||||
|
@ -1455,12 +1601,14 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) {
|
|||
);
|
||||
pos++;
|
||||
}
|
||||
|
||||
if (tc1) {
|
||||
PrintAndLogEx(SUCCESS, " - TC1 : NAD is%s supported, CID is%s supported",
|
||||
(card.ats[pos] & 0x01) ? "" : " NOT",
|
||||
(card.ats[pos] & 0x02) ? "" : " NOT");
|
||||
pos++;
|
||||
}
|
||||
|
||||
if (card.ats[0] > pos && card.ats[0] < card.ats_len - 2) {
|
||||
const char *tip = "";
|
||||
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) {
|
||||
case 0x10:
|
||||
PrintAndLogEx(SUCCESS, " 1x -> MIFARE DESFire");
|
||||
isMifareDesfire = true;
|
||||
isMifareDESFire = true;
|
||||
isMifareClassic = false;
|
||||
isMifarePlus = false;
|
||||
break;
|
||||
case 0x20:
|
||||
PrintAndLogEx(SUCCESS, " 2x -> MIFARE Plus");
|
||||
isMifarePlus = true;
|
||||
isMifareDesfire = false;
|
||||
isMifareDESFire = false;
|
||||
isMifareClassic = false;
|
||||
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");
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
if (isMifareClassic) {
|
||||
|
@ -1623,21 +1782,12 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) {
|
|||
|
||||
res = detect_classic_static_nonce();
|
||||
if (res == 1)
|
||||
PrintAndLogEx(SUCCESS, "Static nonce: " _YELLOW_("yes") );
|
||||
PrintAndLogEx(SUCCESS, "Static nonce: " _YELLOW_("yes"));
|
||||
if (res == 2 && verbose)
|
||||
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;
|
||||
}
|
||||
|
|
609
client/cmdhfcryptorf.c
Normal file
609
client/cmdhfcryptorf.c
Normal 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
20
client/cmdhfcryptorf.h
Normal 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
|
|
@ -1847,7 +1847,7 @@ int readFelicaUid(bool verbose) {
|
|||
}
|
||||
|
||||
static command_t CommandTable[] = {
|
||||
{"----------- General -----------", CmdHelp, IfPm3Iso14443a, ""},
|
||||
{"----------- General -----------", CmdHelp, AlwaysAvailable, ""},
|
||||
{"help", CmdHelp, AlwaysAvailable, "This help"},
|
||||
{"list", CmdHFFelicaList, AlwaysAvailable, "List ISO 18092/FeliCa history"},
|
||||
{"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"},
|
||||
{"rdunencrypted", CmdHFFelicaReadWithoutEncryption, IfPm3Felica, "read Block Data from 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"},
|
||||
{"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."},
|
||||
|
@ -1874,7 +1874,7 @@ static command_t CommandTable[] = {
|
|||
//{"readv2", CmdHFFelicaNotImplementedYet, IfPm3Felica, "read Block Data from authentication-required Service."},
|
||||
//{"writev2", CmdHFFelicaNotImplementedYet, IfPm3Felica, "write Block Data to authentication-required Service."},
|
||||
//{"uprandomid", CmdHFFelicaNotImplementedYet, IfPm3Felica, "update Random ID (IDr)."},
|
||||
{"----------- FeliCa Light -----------", CmdHelp, IfPm3Iso14443a, ""},
|
||||
{"----------- FeliCa Light -----------", CmdHelp, AlwaysAvailable, ""},
|
||||
{"litesim", CmdHFFelicaSimLite, IfPm3Felica, "<NDEF2> - only reply to poll request"},
|
||||
{"litedump", CmdHFFelicaDumpLite, IfPm3Felica, "Wait for and try dumping FelicaLite"},
|
||||
// {"sim", CmdHFFelicaSim, IfPm3Felica, "<UID> -- Simulate ISO 18092/FeliCa tag"}
|
||||
|
|
|
@ -2050,7 +2050,7 @@ static int CmdHFiClassReadTagFile(const char *Cmd) {
|
|||
|
||||
if (verbose) {
|
||||
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, "start " _YELLOW_("0x%02x") "end " _YELLOW_("0x%02x"), (startblock == 0) ? 6 : startblock, endblock);
|
||||
}
|
||||
|
|
|
@ -1013,43 +1013,47 @@ static int CmdLegicDump(const char *Cmd) {
|
|||
saveFile(filename, ".bin", data, readlen);
|
||||
saveFileEML(filename, data, readlen, 8);
|
||||
saveFileJSON(filename, jsfLegic, data, readlen);
|
||||
free(data);
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
static int CmdLegicRestore(const char *Cmd) {
|
||||
|
||||
char filename[FILE_PATH_SIZE] = {0x00};
|
||||
size_t fileNlen = 0;
|
||||
bool errors = false, shall_obsfuscate = false;
|
||||
bool errors = false, shall_obsfuscate = false, have_filename = false;
|
||||
size_t numofbytes;
|
||||
uint8_t cmdp = 0;
|
||||
|
||||
memset(filename, 0, sizeof(filename));
|
||||
|
||||
while (param_getchar(Cmd, cmdp) != 0x00 && !errors) {
|
||||
switch (tolower(param_getchar(Cmd, cmdp))) {
|
||||
case 'h':
|
||||
case 'h': {
|
||||
errors = true;
|
||||
break;
|
||||
case 'f':
|
||||
fileNlen = param_getstr(Cmd, cmdp + 1, filename, FILE_PATH_SIZE);
|
||||
if (!fileNlen)
|
||||
errors = true;
|
||||
|
||||
if (fileNlen > FILE_PATH_SIZE - 5)
|
||||
fileNlen = FILE_PATH_SIZE - 5;
|
||||
}
|
||||
case 'f': {
|
||||
if (param_getstr(Cmd, cmdp + 1, filename, FILE_PATH_SIZE) >= FILE_PATH_SIZE) {
|
||||
PrintAndLogEx(FAILED, "Filename too long");
|
||||
break;
|
||||
}
|
||||
have_filename = true;
|
||||
cmdp += 2;
|
||||
break;
|
||||
case 'x':
|
||||
}
|
||||
case 'x': {
|
||||
shall_obsfuscate = true;
|
||||
cmdp++;
|
||||
break;
|
||||
default:
|
||||
}
|
||||
default: {
|
||||
PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp));
|
||||
errors = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (have_filename == false)
|
||||
errors = true;
|
||||
|
||||
//Validations
|
||||
if (errors || cmdp == 0) return usage_legic_restore();
|
||||
|
||||
|
@ -1076,7 +1080,7 @@ static int CmdLegicRestore(const char *Cmd) {
|
|||
}
|
||||
|
||||
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);
|
||||
return PM3_EFILE;
|
||||
}
|
||||
|
@ -1132,45 +1136,54 @@ static int CmdLegicRestore(const char *Cmd) {
|
|||
static int CmdLegicELoad(const char *Cmd) {
|
||||
|
||||
size_t numofbytes = 256;
|
||||
int fileNameLen = 0;
|
||||
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;
|
||||
|
||||
while (param_getchar(Cmd, cmdp) != 0x00 && !errors) {
|
||||
switch (tolower(param_getchar(Cmd, cmdp))) {
|
||||
case 'h' :
|
||||
case 'h' : {
|
||||
return usage_legic_eload();
|
||||
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;
|
||||
}
|
||||
case 'f' : {
|
||||
if (param_getstr(Cmd, cmdp + 1, filename, FILE_PATH_SIZE) >= FILE_PATH_SIZE) {
|
||||
PrintAndLogEx(FAILED, "Filename too long");
|
||||
break;
|
||||
}
|
||||
have_filename = true;
|
||||
cmdp += 2;
|
||||
break;
|
||||
case 'x':
|
||||
}
|
||||
case 'x': {
|
||||
shall_obsfuscate = true;
|
||||
cmdp++;
|
||||
break;
|
||||
case '0' :
|
||||
}
|
||||
case '0' : {
|
||||
numofbytes = 22;
|
||||
cmdp++;
|
||||
break;
|
||||
case '1' :
|
||||
}
|
||||
case '1' : {
|
||||
numofbytes = 256;
|
||||
cmdp++;
|
||||
break;
|
||||
case '2' :
|
||||
}
|
||||
case '2' : {
|
||||
numofbytes = 1024;
|
||||
cmdp++;
|
||||
break;
|
||||
default :
|
||||
}
|
||||
default : {
|
||||
PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp));
|
||||
errors = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (have_filename == false)
|
||||
errors = true;
|
||||
|
||||
//Validations
|
||||
if (errors || strlen(Cmd) == 0) return usage_legic_eload();
|
||||
|
||||
|
@ -1358,8 +1371,8 @@ static command_t CommandTable[] = {
|
|||
{"sim", CmdLegicSim, IfPm3Legicrf, "Start tag simulator"},
|
||||
{"wrbl", CmdLegicWrbl, IfPm3Legicrf, "Write data to a LEGIC Prime tag"},
|
||||
{"crc", CmdLegicCalcCrc, AlwaysAvailable, "Calculate Legic CRC over given bytes"},
|
||||
{"eload", CmdLegicELoad, IfPm3Legicrf, "Load binary dump to emulator memory"},
|
||||
{"esave", CmdLegicESave, IfPm3Legicrf, "Save emulator memory to binary file"},
|
||||
{"eload", CmdLegicELoad, AlwaysAvailable, "Load binary dump to emulator memory"},
|
||||
{"esave", CmdLegicESave, AlwaysAvailable, "Save emulator memory to binary file"},
|
||||
{"wipe", CmdLegicWipe, IfPm3Legicrf, "Wipe a LEGIC Prime tag"},
|
||||
{NULL, NULL, NULL, NULL}
|
||||
};
|
||||
|
|
|
@ -3712,7 +3712,7 @@ int CmdHF14AMfELoad(const char *Cmd) {
|
|||
}
|
||||
}
|
||||
|
||||
PrintAndLogEx(INFO, "Copying to emulator memory");
|
||||
PrintAndLogEx(INFO, "Uploading to emulator memory");
|
||||
|
||||
// fast push mode
|
||||
conn.block_after_ACK = true;
|
||||
|
|
|
@ -2808,6 +2808,7 @@ static int CmdHF14MfuNDEF(const char *Cmd) {
|
|||
if (status == -1) {
|
||||
DropField();
|
||||
PrintAndLogEx(ERR, "Error: tag didn't answer to READ");
|
||||
free(records);
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -94,6 +94,10 @@ int JsonSaveStr(json_t *root, const char *path, const char *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) {
|
||||
char *msg = sprint_hex_inrow(data, datalen);
|
||||
if (msg && strlen(msg) && msg[strlen(msg) - 1] == ' ')
|
||||
|
|
|
@ -24,6 +24,7 @@ const char *GetApplicationDataName(tlv_tag_t tag);
|
|||
|
||||
int JsonSaveJsonObject(json_t *root, const char *path, json_t *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 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);
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
// this define is needed for scandir/alphasort to work
|
||||
#define _GNU_SOURCE
|
||||
#include "fileutils.h"
|
||||
#include "settings.h"
|
||||
|
||||
#include <dirent.h>
|
||||
#include <ctype.h>
|
||||
|
@ -425,6 +426,9 @@ int saveFileJSON(const char *preferredName, JSONFileType ftype, uint8_t *data, s
|
|||
}
|
||||
}
|
||||
break;
|
||||
case jsfSettings:
|
||||
settings_save_callback (root);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -863,7 +867,9 @@ int loadFileJSON(const char *preferredName, void *data, size_t maxdatalen, size_
|
|||
}
|
||||
*datalen = sptr;
|
||||
}
|
||||
|
||||
if (!strcmp(ctype,"settings")) {
|
||||
settings_load_callback (root);
|
||||
}
|
||||
PrintAndLogEx(SUCCESS, "loaded from JSON file " _YELLOW_("%s"), fileName);
|
||||
out:
|
||||
json_decref(root);
|
||||
|
|
|
@ -62,6 +62,7 @@ typedef enum {
|
|||
jsfT55x7,
|
||||
jsfT5555,
|
||||
jsfMfPlusKeys,
|
||||
jsfSettings,
|
||||
} JSONFileType;
|
||||
|
||||
typedef enum {
|
||||
|
|
|
@ -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)
|
|
@ -27,6 +27,7 @@
|
|||
#include "comms.h"
|
||||
#include "fileutils.h"
|
||||
#include "flash.h"
|
||||
#include "settings.h"
|
||||
|
||||
static void showBanner(void) {
|
||||
g_printAndLog = PRINTANDLOG_PRINT;
|
||||
|
@ -581,6 +582,12 @@ int main(int argc, char *argv[]) {
|
|||
set_my_executable_path();
|
||||
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++) {
|
||||
|
||||
if (argv[i][0] != '-') {
|
||||
|
|
194
client/settings.c
Normal file
194
client/settings.c
Normal 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
40
client/settings.h
Normal 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
|
Loading…
Reference in a new issue