mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2025-08-21 13:53:55 -07:00
read file attributes
This commit is contained in:
parent
012b7e5701
commit
c5c15de700
3 changed files with 261 additions and 5 deletions
|
@ -126,9 +126,12 @@ int CIPURSESelectFile(uint16_t fileID, uint8_t *Result, size_t MaxResultLen, siz
|
|||
return CIPURSEExchangeEx(false, true, (sAPDU) {0x00, 0xa4, 0x00, 0x00, 02, fileIdBin}, true, 0, Result, MaxResultLen, ResultLen, sw);
|
||||
}
|
||||
|
||||
int CIPURSEReadFileAttributes(uint8_t *data, uint16_t *datalen) {
|
||||
//CIPURSEExchangeEx(false, true, (sAPDU) {0x00, 0x82, 0x00, keyIndex, paramslen, params}, true, 0x10, Result, MaxResultLen, ResultLen, sw);
|
||||
return 2;
|
||||
int CIPURSESelectMFFile(uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
|
||||
return CIPURSEExchangeEx(false, true, (sAPDU) {0x00, 0xa4, 0x00, 0x00, 0, NULL}, true, 0, Result, MaxResultLen, ResultLen, sw);
|
||||
}
|
||||
|
||||
int CIPURSEReadFileAttributes(uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
|
||||
return CIPURSEExchangeEx(false, true, (sAPDU) {0x80, 0xce, 0x00, 0x00, 0, NULL}, true, 0, Result, MaxResultLen, ResultLen, sw);
|
||||
}
|
||||
|
||||
int CIPURSEReadBinary(uint16_t offset, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
|
||||
|
@ -209,3 +212,128 @@ void CIPURSEPrintInfoFile(uint8_t *data, size_t len) {
|
|||
PrintAndLogEx(INFO, "CIPURSE version %d revision %d", data[0], data[1]);
|
||||
}
|
||||
|
||||
static void CIPURSEPrintFileDescriptor(uint8_t desc) {
|
||||
if (desc == 0x01)
|
||||
PrintAndLogEx(INFO, "Binary file");
|
||||
else if (desc == 0x11)
|
||||
PrintAndLogEx(INFO, "Binary file with transactions");
|
||||
else if (desc == 0x02)
|
||||
PrintAndLogEx(INFO, "Linear record file");
|
||||
else if (desc == 0x12)
|
||||
PrintAndLogEx(INFO, "Linear record file with transactions");
|
||||
else if (desc == 0x06)
|
||||
PrintAndLogEx(INFO, "Cyclic record file");
|
||||
else if (desc == 0x16)
|
||||
PrintAndLogEx(INFO, "Cyclic record file with transactions");
|
||||
else if (desc == 0x1E)
|
||||
PrintAndLogEx(INFO, "Linear value-record file");
|
||||
else if (desc == 0x1F)
|
||||
PrintAndLogEx(INFO, "Linear value-record file with transactions");
|
||||
else
|
||||
PrintAndLogEx(INFO, "Unknown file 0x%02x", desc);
|
||||
}
|
||||
|
||||
static void CIPURSEPrintKeyAttrib(uint8_t *attr) {
|
||||
PrintAndLogEx(INFO, "-------- KEY ATTRIBUTES --------");
|
||||
PrintAndLogEx(INFO, "Additional info: 0x%02x", attr[0]);
|
||||
PrintAndLogEx(INFO, "Key length: %d", attr[1]);
|
||||
PrintAndLogEx(INFO, "Algorithm ID: 0x%02x", attr[2]);
|
||||
PrintAndLogEx(INFO, "Security attr: 0x%02x", attr[3]);
|
||||
PrintAndLogEx(INFO, "KVV: 0x%02x%02x%02x", attr[4], attr[5], attr[6]);
|
||||
PrintAndLogEx(INFO, "-------------------------------");
|
||||
}
|
||||
|
||||
void CIPURSEPrintFileAttr(uint8_t *fileAttr, size_t len) {
|
||||
if (len < 7) {
|
||||
PrintAndLogEx(ERR, "Attributes length " _RED_("ERROR"));
|
||||
return;
|
||||
}
|
||||
|
||||
PrintAndLogEx(INFO, "--------- FILE ATTRIBUTES ---------");
|
||||
if (fileAttr[0] == 0x38) {
|
||||
PrintAndLogEx(INFO, "Type: MF, ADF");
|
||||
if (fileAttr[1] == 0x00) {
|
||||
PrintAndLogEx(INFO, "Type: MF");
|
||||
} else {
|
||||
if ((fileAttr[1] & 0xe0) == 0x00)
|
||||
PrintAndLogEx(INFO, "Type: Unknown");
|
||||
if ((fileAttr[1] & 0xe0) == 0x20)
|
||||
PrintAndLogEx(INFO, "Type: CIPURSE L");
|
||||
if ((fileAttr[1] & 0xe0) == 0x40)
|
||||
PrintAndLogEx(INFO, "Type: CIPURSE S");
|
||||
if ((fileAttr[1] & 0xe0) == 0x60)
|
||||
PrintAndLogEx(INFO, "Type: CIPURSE T");
|
||||
if ((fileAttr[1] & 0x02) == 0x00)
|
||||
PrintAndLogEx(INFO, "Autoselect on PxSE select OFF");
|
||||
else
|
||||
PrintAndLogEx(INFO, "Autoselect on PxSE select ON");
|
||||
if ((fileAttr[1] & 0x01) == 0x00)
|
||||
PrintAndLogEx(INFO, "PxSE select returns FCPTemplate OFF");
|
||||
else
|
||||
PrintAndLogEx(INFO, "PxSE select returns FCPTemplate ON");
|
||||
}
|
||||
|
||||
PrintAndLogEx(INFO, "File ID: 0x%02x%02x", fileAttr[2], fileAttr[3]);
|
||||
|
||||
PrintAndLogEx(INFO, "Maximum number of custom EFs: %d", fileAttr[4]);
|
||||
PrintAndLogEx(INFO, "Maximum number of EFs with SFID: %d", fileAttr[5]);
|
||||
uint8_t keyNum = fileAttr[6];
|
||||
PrintAndLogEx(INFO, "Keys assigned: %d", keyNum);
|
||||
|
||||
if (len >= 9) {
|
||||
PrintAndLogEx(INFO, "SMR entries: %02x%02x", fileAttr[7], fileAttr[8]);
|
||||
}
|
||||
|
||||
if (len >= 10 + keyNum + 1) {
|
||||
PrintAndLogEx(INFO, "ART: %s", sprint_hex(&fileAttr[9], keyNum + 1));
|
||||
}
|
||||
|
||||
if (len >= 11 + keyNum + 1 + keyNum * 7) {
|
||||
for (int i = 0; i < keyNum; i++) {
|
||||
PrintAndLogEx(INFO, "Key %d Attributes: %s", i, sprint_hex(&fileAttr[11 + keyNum + 1 + i * 7], 7));
|
||||
CIPURSEPrintKeyAttrib(&fileAttr[11 + keyNum + 1 + i * 7]);
|
||||
}
|
||||
}
|
||||
// MF
|
||||
if (fileAttr[1] == 0x00) {
|
||||
PrintAndLogEx(INFO, "Total memory size: %d", (fileAttr[len - 6] << 16) + (fileAttr[len - 1] << 5) + fileAttr[len - 4]);
|
||||
PrintAndLogEx(INFO, "Free memory size: %d", (fileAttr[len - 3] << 16) + (fileAttr[len - 2] << 8) + fileAttr[len - 1]);
|
||||
|
||||
} else {
|
||||
int ptr = 11 + keyNum + 1 + keyNum * 7;
|
||||
if (len > ptr)
|
||||
PrintAndLogEx(INFO, "TLV file control: %s", sprint_hex(&fileAttr[ptr], len - ptr));
|
||||
}
|
||||
} else {
|
||||
PrintAndLogEx(INFO, "Type: EF");
|
||||
CIPURSEPrintFileDescriptor(fileAttr[0]);
|
||||
if (fileAttr[1] == 0)
|
||||
PrintAndLogEx(INFO, "SFI: not assigned");
|
||||
else
|
||||
PrintAndLogEx(INFO, "SFI: 0x%02x", fileAttr[1]);
|
||||
|
||||
PrintAndLogEx(INFO, "File ID: 0x%02x%02x", fileAttr[2], fileAttr[3]);
|
||||
|
||||
if (fileAttr[0] == 0x01 || fileAttr[0] == 0x11)
|
||||
PrintAndLogEx(INFO, "File size: %d", (fileAttr[4] << 8) + fileAttr[5]);
|
||||
else
|
||||
PrintAndLogEx(INFO, "Record num: %d record size: %d", fileAttr[4], fileAttr[5]);
|
||||
|
||||
PrintAndLogEx(INFO, "Keys assigned: %d", fileAttr[6]);
|
||||
|
||||
if (len >= 9) {
|
||||
PrintAndLogEx(INFO, "SMR entries: %02x%02x", fileAttr[7], fileAttr[8]);
|
||||
}
|
||||
|
||||
if (len >= 10) {
|
||||
PrintAndLogEx(INFO, "ART: %s", sprint_hex(&fileAttr[9], len - 9));
|
||||
if (fileAttr[6] + 1 != len - 9)
|
||||
PrintAndLogEx(WARNING, "ART length is wrong");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -31,12 +31,15 @@ int CIPURSEMutalAuthenticate(uint8_t keyIndex, uint8_t *params, uint8_t paramsle
|
|||
int CIPURSECreateFile(uint16_t fileID, uint8_t *fileAttr);
|
||||
int CIPURSEDeleteFile(uint16_t fileID);
|
||||
|
||||
int CIPURSESelectMFFile(uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) ;
|
||||
int CIPURSESelectFile(uint16_t fileID, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw);
|
||||
int CIPURSEReadFileAttributes(uint8_t *data, uint16_t *datalen);
|
||||
int CIPURSEReadFileAttributes(uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw);
|
||||
int CIPURSEReadBinary(uint16_t offset, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw);
|
||||
int CIPURSEUpdateBinary(uint16_t offset, uint8_t *data, uint16_t datalen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw);
|
||||
|
||||
bool CIPURSEChannelAuthenticate(uint8_t keyIndex, uint8_t *key, bool verbose);
|
||||
void CIPURSECSetActChannelSecurityLevels(CipurseChannelSecurityLevel req, CipurseChannelSecurityLevel resp);
|
||||
|
||||
void CIPURSEPrintFileAttr(uint8_t *fileAttr, size_t len);
|
||||
|
||||
#endif /* __CIPURSECORE_H__ */
|
||||
|
|
|
@ -437,7 +437,7 @@ static int CmdHFCipurseWriteFile(const char *Cmd) {
|
|||
res = CIPURSEUpdateBinary(offset, hdata, hdatalen, buf, sizeof(buf), &len, &sw);
|
||||
if (res != 0 || sw != 0x9000) {
|
||||
if (verbose == false)
|
||||
PrintAndLogEx(ERR, "File read " _RED_("ERROR") ". Card returns 0x%04x.", sw);
|
||||
PrintAndLogEx(ERR, "File write " _RED_("ERROR") ". Card returns 0x%04x.", sw);
|
||||
DropField();
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
|
@ -448,6 +448,130 @@ static int CmdHFCipurseWriteFile(const char *Cmd) {
|
|||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
static int CmdHFCipurseReadFileAttr(const char *Cmd) {
|
||||
uint8_t buf[APDU_RES_LEN] = {0};
|
||||
size_t len = 0;
|
||||
uint16_t sw = 0;
|
||||
uint8_t key[] = CIPURSE_DEFAULT_KEY;
|
||||
|
||||
CLIParserContext *ctx;
|
||||
CLIParserInit(&ctx, "hf cipurse aread",
|
||||
"Read file attributes by file ID with key ID and key",
|
||||
"hf cipurse aread -f 2ff7 -> Authenticate with keyID=1 and key = 7373...7373 and read file with id 2ff7\n"
|
||||
"hf cipurse aread -n 2 -k 65656565656565656565656565656565 -f 2ff7 -> Authenticate with specified key and read file\n");
|
||||
|
||||
void *argtable[] = {
|
||||
arg_param_begin,
|
||||
arg_lit0("a", "apdu", "show APDU requests and responses"),
|
||||
arg_lit0("v", "verbose", "show technical data"),
|
||||
arg_int0("n", "keyid", "<dec>", "key id"),
|
||||
arg_str0("k", "key", "<hex>", "key for authenticate"),
|
||||
arg_str0("f", "file", "<hex>", "file ID"),
|
||||
arg_lit0(NULL, "noauth", "read file attributes without authentication"),
|
||||
arg_str0(NULL, "sreq", "<plain|mac(default)|encode>", "communication reader-PICC security level"),
|
||||
arg_str0(NULL, "sresp", "<plain|mac(default)|encode>", "communication PICC-reader security level"),
|
||||
arg_lit0(NULL, "sel-adf","show info about ADF itself"),
|
||||
arg_lit0(NULL, "sel-mf", "show info about master file"),
|
||||
arg_param_end
|
||||
};
|
||||
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
||||
|
||||
bool APDULogging = arg_get_lit(ctx, 1);
|
||||
bool verbose = arg_get_lit(ctx, 2);
|
||||
uint8_t keyId = arg_get_int_def(ctx, 3, 1);
|
||||
|
||||
CipurseChannelSecurityLevel sreq = CPSMACed;
|
||||
CipurseChannelSecurityLevel sresp = CPSMACed;
|
||||
int res = CLIParseKeyAndSecurityLevels(ctx, 4, 7, 8, key, &sreq, &sresp);
|
||||
if (res) {
|
||||
CLIParserFree(ctx);
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
uint16_t fileId = 0x2ff7;
|
||||
|
||||
uint8_t hdata[250] = {0};
|
||||
int hdatalen = sizeof(hdata);
|
||||
CLIGetHexWithReturn(ctx, 5, hdata, &hdatalen);
|
||||
if (hdatalen && hdatalen != 2) {
|
||||
PrintAndLogEx(ERR, _RED_("ERROR:") " file id length must be 2 bytes only.");
|
||||
CLIParserFree(ctx);
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
if (hdatalen)
|
||||
fileId = (hdata[0] << 8) + hdata[1];
|
||||
|
||||
bool noAuth = arg_get_lit(ctx, 6);
|
||||
|
||||
bool seladf = arg_get_lit(ctx, 9);
|
||||
bool selmf = arg_get_lit(ctx, 10);
|
||||
|
||||
SetAPDULogging(APDULogging);
|
||||
|
||||
CLIParserFree(ctx);
|
||||
|
||||
res = CIPURSESelect(true, true, buf, sizeof(buf), &len, &sw);
|
||||
if (res != 0 || sw != 0x9000) {
|
||||
PrintAndLogEx(ERR, "Cipurse select " _RED_("error") ". Card returns 0x%04x.", sw);
|
||||
DropField();
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
|
||||
if (verbose)
|
||||
PrintAndLogEx(INFO, "File id: %x key id: %d key: %s", fileId, keyId, sprint_hex(key, CIPURSE_AES_KEY_LENGTH));
|
||||
|
||||
if (noAuth == false) {
|
||||
bool bres = CIPURSEChannelAuthenticate(keyId, key, verbose);
|
||||
if (bres == false) {
|
||||
if (verbose == false)
|
||||
PrintAndLogEx(ERR, "Authentication " _RED_("ERROR"));
|
||||
DropField();
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
|
||||
// set channel security levels
|
||||
CIPURSECSetActChannelSecurityLevels(sreq, sresp);
|
||||
}
|
||||
|
||||
if (seladf == false) {
|
||||
if (selmf)
|
||||
res = CIPURSESelectMFFile(buf, sizeof(buf), &len, &sw);
|
||||
else
|
||||
res = CIPURSESelectFile(fileId, buf, sizeof(buf), &len, &sw);
|
||||
|
||||
if (res != 0 || sw != 0x9000) {
|
||||
if (verbose == false)
|
||||
PrintAndLogEx(ERR, "File select " _RED_("ERROR") ". Card returns 0x%04x.", sw);
|
||||
DropField();
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
}
|
||||
|
||||
if (verbose)
|
||||
PrintAndLogEx(INFO, "Select file 0x%x " _GREEN_("OK"), fileId);
|
||||
|
||||
res = CIPURSEReadFileAttributes(buf, sizeof(buf), &len, &sw);
|
||||
if (res != 0 || sw != 0x9000) {
|
||||
if (verbose == false)
|
||||
PrintAndLogEx(ERR, "File read " _RED_("ERROR") ". Card returns 0x%04x.", sw);
|
||||
DropField();
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
|
||||
if (len == 0) {
|
||||
PrintAndLogEx(WARNING, "File id: %x attributes is empty", fileId);
|
||||
DropField();
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
if (verbose)
|
||||
PrintAndLogEx(INFO, "File id: %x attributes[%d]: %s", fileId, len, sprint_hex(buf, len));
|
||||
|
||||
CIPURSEPrintFileAttr(buf, len);
|
||||
|
||||
DropField();
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -469,6 +593,7 @@ static command_t CommandTable[] = {
|
|||
{"auth", CmdHFCipurseAuth, IfPm3Iso14443a, "Authentication."},
|
||||
{"read", CmdHFCipurseReadFile, IfPm3Iso14443a, "Read binary file."},
|
||||
{"write", CmdHFCipurseWriteFile, IfPm3Iso14443a, "Write binary file."},
|
||||
{"aread", CmdHFCipurseReadFileAttr, IfPm3Iso14443a, "Read file attributes."},
|
||||
{NULL, NULL, 0, NULL}
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue