mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2025-08-23 22:55:37 -07:00
read file works
This commit is contained in:
parent
99c4a3a9c0
commit
84ada9cc14
5 changed files with 232 additions and 9 deletions
|
@ -27,6 +27,8 @@ CipurseContext cipurseContext;
|
||||||
|
|
||||||
static int CIPURSEExchangeEx(bool ActivateField, bool LeaveFieldON, sAPDU apdu, bool IncludeLe, uint16_t Le, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
|
static int CIPURSEExchangeEx(bool ActivateField, bool LeaveFieldON, sAPDU apdu, bool IncludeLe, uint16_t Le, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
|
||||||
uint8_t data[APDU_RES_LEN] = {0};
|
uint8_t data[APDU_RES_LEN] = {0};
|
||||||
|
uint8_t securedata[APDU_RES_LEN] = {0};
|
||||||
|
sAPDU secapdu;
|
||||||
|
|
||||||
*ResultLen = 0;
|
*ResultLen = 0;
|
||||||
if (sw) *sw = 0;
|
if (sw) *sw = 0;
|
||||||
|
@ -47,7 +49,10 @@ static int CIPURSEExchangeEx(bool ActivateField, bool LeaveFieldON, sAPDU apdu,
|
||||||
uint16_t xle = IncludeLe ? 0x100 : 0x00;
|
uint16_t xle = IncludeLe ? 0x100 : 0x00;
|
||||||
if (xle == 0x100 && Le != 0)
|
if (xle == 0x100 && Le != 0)
|
||||||
xle = Le;
|
xle = Le;
|
||||||
if (APDUEncodeS(&apdu, false, xle, data, &datalen)) {
|
|
||||||
|
CipurseCAPDUReqEncode(&cipurseContext, &apdu, &secapdu, securedata, IncludeLe, Le);
|
||||||
|
|
||||||
|
if (APDUEncodeS(&secapdu, false, xle, data, &datalen)) {
|
||||||
PrintAndLogEx(ERR, "APDU encoding error.");
|
PrintAndLogEx(ERR, "APDU encoding error.");
|
||||||
return 201;
|
return 201;
|
||||||
}
|
}
|
||||||
|
@ -62,14 +67,18 @@ static int CIPURSEExchangeEx(bool ActivateField, bool LeaveFieldON, sAPDU apdu,
|
||||||
|
|
||||||
if (GetAPDULogging())
|
if (GetAPDULogging())
|
||||||
PrintAndLogEx(SUCCESS, "<<<< %s", sprint_hex(Result, *ResultLen));
|
PrintAndLogEx(SUCCESS, "<<<< %s", sprint_hex(Result, *ResultLen));
|
||||||
|
|
||||||
if (*ResultLen < 2) {
|
if (*ResultLen < 2) {
|
||||||
return 200;
|
return 200;
|
||||||
}
|
}
|
||||||
|
|
||||||
*ResultLen -= 2;
|
size_t rlen = 0;
|
||||||
isw = Result[*ResultLen] * 0x0100 + Result[*ResultLen + 1];
|
CipurseCAPDURespDecode(&cipurseContext, Result, *ResultLen, securedata, &rlen, &isw);
|
||||||
if (sw)
|
memcpy(Result, securedata, rlen);
|
||||||
|
if (ResultLen != NULL)
|
||||||
|
*ResultLen = rlen;
|
||||||
|
|
||||||
|
if (sw != NULL)
|
||||||
*sw = isw;
|
*sw = isw;
|
||||||
|
|
||||||
if (isw != 0x9000) {
|
if (isw != 0x9000) {
|
||||||
|
@ -105,6 +114,20 @@ int CIPURSEMutalAuthenticate(uint8_t keyIndex, uint8_t *params, uint8_t paramsle
|
||||||
return CIPURSEExchangeEx(false, true, (sAPDU) {0x00, 0x82, 0x00, keyIndex, paramslen, params}, true, 0x10, Result, MaxResultLen, ResultLen, sw);
|
return CIPURSEExchangeEx(false, true, (sAPDU) {0x00, 0x82, 0x00, keyIndex, paramslen, params}, true, 0x10, Result, MaxResultLen, ResultLen, sw);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int CIPURSESelectFile(uint16_t fileID, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
|
||||||
|
uint8_t fileIdBin[] = {fileID >> 8, fileID & 0xff};
|
||||||
|
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 CIPURSEReadBinary(uint16_t offset, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
|
||||||
|
return CIPURSEExchangeEx(false, true, (sAPDU) {0x00, 0xb0, (offset >> 8) & 0x7f, offset & 0xff, 0, NULL}, true, 0, Result, MaxResultLen, ResultLen, sw);
|
||||||
|
}
|
||||||
|
|
||||||
bool CIPURSEChannelAuthenticate(uint8_t keyIndex, uint8_t *key, bool verbose) {
|
bool CIPURSEChannelAuthenticate(uint8_t keyIndex, uint8_t *key, bool verbose) {
|
||||||
uint8_t buf[APDU_RES_LEN] = {0};
|
uint8_t buf[APDU_RES_LEN] = {0};
|
||||||
size_t len = 0;
|
size_t len = 0;
|
||||||
|
@ -149,7 +172,8 @@ bool CIPURSEChannelAuthenticate(uint8_t keyIndex, uint8_t *key, bool verbose) {
|
||||||
if (verbose)
|
if (verbose)
|
||||||
PrintAndLogEx(INFO, "Authentication " _GREEN_("OK"));
|
PrintAndLogEx(INFO, "Authentication " _GREEN_("OK"));
|
||||||
|
|
||||||
CipurseCChannelSetSecurityLevels(&cpc, CPSMACed, CPSMACed);
|
//CipurseCChannelSetSecurityLevels(&cpc, CPSMACed, CPSMACed);
|
||||||
|
CipurseCChannelSetSecurityLevels(&cpc, CPSPlain, CPSPlain);
|
||||||
memcpy(&cipurseContext, &cpc, sizeof(CipurseContext));
|
memcpy(&cipurseContext, &cpc, sizeof(CipurseContext));
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -14,10 +14,11 @@
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "emv/apduinfo.h"
|
#include "emv/apduinfo.h"
|
||||||
|
|
||||||
|
|
||||||
#include <jansson.h>
|
#include <jansson.h>
|
||||||
#include "emv/apduinfo.h" // sAPDU
|
#include "emv/apduinfo.h" // sAPDU
|
||||||
|
|
||||||
|
#define CIPURSE_DEFAULT_KEY {0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73}
|
||||||
|
|
||||||
int CIPURSESelect(bool ActivateField, bool LeaveFieldON, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw);
|
int CIPURSESelect(bool ActivateField, bool LeaveFieldON, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw);
|
||||||
|
|
||||||
int CIPURSEChallenge(uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw);
|
int CIPURSEChallenge(uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw);
|
||||||
|
@ -26,9 +27,9 @@ int CIPURSEMutalAuthenticate(uint8_t keyIndex, uint8_t *params, uint8_t paramsle
|
||||||
int CIPURSECreateFile(uint16_t fileID, uint8_t *fileAttr);
|
int CIPURSECreateFile(uint16_t fileID, uint8_t *fileAttr);
|
||||||
int CIPURSEDeleteFile(uint16_t fileID);
|
int CIPURSEDeleteFile(uint16_t fileID);
|
||||||
|
|
||||||
int CIPURSESelectFile(uint16_t fileID);
|
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 *data, uint16_t *datalen);
|
||||||
int CIPURSEReadBinary(uint16_t offset, uint8_t *data, uint16_t *datalen);
|
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);
|
int CIPURSEUpdateBinary(uint16_t offset, uint8_t *data, uint16_t datalen);
|
||||||
|
|
||||||
bool CIPURSEChannelAuthenticate(uint8_t keyIndex, uint8_t *key, bool verbose);
|
bool CIPURSEChannelAuthenticate(uint8_t keyIndex, uint8_t *key, bool verbose);
|
||||||
|
|
|
@ -167,6 +167,10 @@ void CipurseCChannelSetSecurityLevels(CipurseContext *ctx, CipurseChannelSecurit
|
||||||
ctx->ResponseSecurity = resp;
|
ctx->ResponseSecurity = resp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isCipurseCChannelSecuritySet(CipurseContext *ctx) {
|
||||||
|
return ((ctx->RequestSecurity != CPSNone) && (ctx->ResponseSecurity != CPSNone));
|
||||||
|
}
|
||||||
|
|
||||||
void CipurseCSetRandomFromPICC(CipurseContext *ctx, uint8_t *random) {
|
void CipurseCSetRandomFromPICC(CipurseContext *ctx, uint8_t *random) {
|
||||||
if (ctx == NULL)
|
if (ctx == NULL)
|
||||||
return;
|
return;
|
||||||
|
@ -310,3 +314,88 @@ bool CipurseCCheckMACPadded(CipurseContext *ctx, uint8_t *data, size_t datalen,
|
||||||
CipurseCCalcMACPadded(ctx, data, datalen, xmac);
|
CipurseCCalcMACPadded(ctx, data, datalen, xmac);
|
||||||
return (memcmp(mac, xmac, CIPURSE_MAC_LENGTH) == 0);
|
return (memcmp(mac, xmac, CIPURSE_MAC_LENGTH) == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CipurseCAPDUReqEncode(CipurseContext *ctx, sAPDU *srcapdu, sAPDU *dstapdu, uint8_t *dstdatabuf, bool includeLe, uint8_t Le) {
|
||||||
|
uint8_t mac[CIPURSE_MAC_LENGTH] = {0};
|
||||||
|
|
||||||
|
memcpy(dstapdu, srcapdu, sizeof(sAPDU));
|
||||||
|
|
||||||
|
if (isCipurseCChannelSecuritySet(ctx) == false)
|
||||||
|
return;
|
||||||
|
|
||||||
|
dstapdu->CLA |= 0x04;
|
||||||
|
dstapdu->data = dstdatabuf;
|
||||||
|
dstapdu->data[0] = CipurseCGetSMI(ctx, includeLe);
|
||||||
|
dstapdu->Lc++;
|
||||||
|
memcpy(&dstdatabuf[1], srcapdu->data, srcapdu->Lc);
|
||||||
|
if (includeLe) {
|
||||||
|
dstapdu->data[dstapdu->Lc] = Le;
|
||||||
|
dstapdu->Lc++;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (ctx->RequestSecurity) {
|
||||||
|
case CPSNone:
|
||||||
|
break;
|
||||||
|
case CPSPlain:
|
||||||
|
CipurseCCalcMACPadded(ctx, (uint8_t *)dstapdu, dstapdu->Lc + 5, mac);
|
||||||
|
break;
|
||||||
|
case CPSMACed:
|
||||||
|
dstapdu->Lc += CIPURSE_MAC_LENGTH;
|
||||||
|
CipurseCCalcMACPadded(ctx, (uint8_t *)dstapdu, dstapdu->Lc + 5, mac);
|
||||||
|
memcpy(&dstdatabuf[dstapdu->Lc - CIPURSE_MAC_LENGTH], mac, CIPURSE_MAC_LENGTH);
|
||||||
|
break;
|
||||||
|
case CPSEncrypted:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void CipurseCAPDURespDecode(CipurseContext *ctx, uint8_t *srcdata, size_t srcdatalen, uint8_t *dstdata, size_t *dstdatalen, uint16_t *sw) {
|
||||||
|
if (dstdatalen != NULL)
|
||||||
|
*dstdatalen = 0;
|
||||||
|
if (sw != NULL)
|
||||||
|
*sw = 0;
|
||||||
|
|
||||||
|
if (srcdatalen < 2)
|
||||||
|
return;
|
||||||
|
|
||||||
|
srcdatalen -= 2;
|
||||||
|
uint16_t xsw = srcdata[srcdatalen] * 0x0100 + srcdata[srcdatalen + 1];
|
||||||
|
if (sw)
|
||||||
|
*sw = xsw;
|
||||||
|
|
||||||
|
if (isCipurseCChannelSecuritySet(ctx) == false) {
|
||||||
|
memcpy(dstdata, srcdata, srcdatalen);
|
||||||
|
if (dstdatalen != NULL)
|
||||||
|
*dstdatalen = srcdatalen;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (ctx->RequestSecurity) {
|
||||||
|
case CPSNone:
|
||||||
|
break;
|
||||||
|
case CPSPlain:
|
||||||
|
memcpy(dstdata, srcdata, srcdatalen);
|
||||||
|
if (dstdatalen != NULL)
|
||||||
|
*dstdatalen = srcdatalen;
|
||||||
|
break;
|
||||||
|
case CPSMACed:
|
||||||
|
if (srcdatalen < CIPURSE_MAC_LENGTH)
|
||||||
|
return;
|
||||||
|
srcdatalen -= CIPURSE_MAC_LENGTH;
|
||||||
|
if (CipurseCCheckMACPadded(ctx, srcdata, srcdatalen, &srcdata[srcdatalen]) == false) {
|
||||||
|
PrintAndLogEx(WARNING, "APDU MAC is not valid!");
|
||||||
|
}
|
||||||
|
memcpy(dstdata, srcdata, srcdatalen);
|
||||||
|
if (dstdatalen != NULL)
|
||||||
|
*dstdatalen = srcdatalen;
|
||||||
|
break;
|
||||||
|
case CPSEncrypted:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#define __CIPURSECRYPTO_H__
|
#define __CIPURSECRYPTO_H__
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
#include "emv/apduinfo.h" // sAPDU
|
||||||
|
|
||||||
#define CIPURSE_KVV_LENGTH 4
|
#define CIPURSE_KVV_LENGTH 4
|
||||||
#define CIPURSE_AES_KEY_LENGTH 16
|
#define CIPURSE_AES_KEY_LENGTH 16
|
||||||
|
@ -61,6 +62,7 @@ void CipurseCAuthenticateHost(CipurseContext *ctx, uint8_t *authdata);
|
||||||
bool CipurseCCheckCT(CipurseContext *ctx, uint8_t *CT);
|
bool CipurseCCheckCT(CipurseContext *ctx, uint8_t *CT);
|
||||||
|
|
||||||
void CipurseCChannelSetSecurityLevels(CipurseContext *ctx, CipurseChannelSecurityLevel req, CipurseChannelSecurityLevel resp);
|
void CipurseCChannelSetSecurityLevels(CipurseContext *ctx, CipurseChannelSecurityLevel req, CipurseChannelSecurityLevel resp);
|
||||||
|
bool isCipurseCChannelSecuritySet(CipurseContext *ctx);
|
||||||
|
|
||||||
void AddISO9797M2Padding(uint8_t *ddata, size_t *ddatalen, uint8_t *sdata, size_t sdatalen, size_t blocklen);
|
void AddISO9797M2Padding(uint8_t *ddata, size_t *ddatalen, uint8_t *sdata, size_t sdatalen, size_t blocklen);
|
||||||
size_t FindISO9797M2PaddingDataLen(uint8_t *data, size_t datalen);
|
size_t FindISO9797M2PaddingDataLen(uint8_t *data, size_t datalen);
|
||||||
|
@ -73,4 +75,8 @@ void CipurseCChannelEncrypt(CipurseContext *ctx, uint8_t *data, size_t datalen,
|
||||||
void CipurseCChannelDecrypt(CipurseContext *ctx, uint8_t *data, size_t datalen, uint8_t *plaindata, size_t *plaindatalen);
|
void CipurseCChannelDecrypt(CipurseContext *ctx, uint8_t *data, size_t datalen, uint8_t *plaindata, size_t *plaindatalen);
|
||||||
void CipurseCGetKVV(uint8_t *key, uint8_t *kvv);
|
void CipurseCGetKVV(uint8_t *key, uint8_t *kvv);
|
||||||
|
|
||||||
|
void CipurseCAPDUReqEncode(CipurseContext *ctx, sAPDU *srcapdu, sAPDU *dstapdu, uint8_t *dstdatabuf, bool includeLe, uint8_t Le);
|
||||||
|
void CipurseCAPDURespDecode(CipurseContext *ctx, uint8_t *srcdata, size_t srcdatalen, uint8_t *dstdata, size_t *dstdatalen, uint16_t *sw);
|
||||||
|
|
||||||
|
|
||||||
#endif /* __CIPURSECRYPTO_H__ */
|
#endif /* __CIPURSECRYPTO_H__ */
|
||||||
|
|
|
@ -148,6 +148,108 @@ static int CmdHFCipurseAuth(const char *Cmd) {
|
||||||
return bres ? PM3_SUCCESS : PM3_ESOFT;
|
return bres ? PM3_SUCCESS : PM3_ESOFT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int CmdHFCipurseReadFile(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 read",
|
||||||
|
"Read file by file ID with key ID and key",
|
||||||
|
"hf cipurse read -f 2ff7 -> Authenticate with keyID=1 and key = 7373...7373 and read file with id 2ff7\n"
|
||||||
|
"hf cipurse auth -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_int0("o", "offset", "<dec>", "offset for reading data from 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);
|
||||||
|
|
||||||
|
uint8_t hdata[250] = {0};
|
||||||
|
int hdatalen = sizeof(hdata);
|
||||||
|
CLIGetHexWithReturn(ctx, 4, hdata, &hdatalen);
|
||||||
|
if (hdatalen && hdatalen != 16) {
|
||||||
|
PrintAndLogEx(ERR, _RED_("ERROR:") " key length for AES128 must be 16 bytes only.");
|
||||||
|
CLIParserFree(ctx);
|
||||||
|
return PM3_EINVARG;
|
||||||
|
}
|
||||||
|
if (hdatalen)
|
||||||
|
memcpy(key, hdata, CIPURSE_AES_KEY_LENGTH);
|
||||||
|
|
||||||
|
uint16_t fileId = 0x2ff7;
|
||||||
|
|
||||||
|
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];
|
||||||
|
|
||||||
|
size_t offset = arg_get_int_def(ctx, 6, 0);
|
||||||
|
|
||||||
|
SetAPDULogging(APDULogging);
|
||||||
|
|
||||||
|
CLIParserFree(ctx);
|
||||||
|
|
||||||
|
int 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 offset %d key id: %d key: %s", fileId, offset, keyId, sprint_hex(key, CIPURSE_AES_KEY_LENGTH));
|
||||||
|
|
||||||
|
bool bres = CIPURSEChannelAuthenticate(keyId, key, verbose);
|
||||||
|
if (bres == false) {
|
||||||
|
if (verbose == false)
|
||||||
|
PrintAndLogEx(ERR, "Authentication " _RED_("ERROR"));
|
||||||
|
DropField();
|
||||||
|
return PM3_ESOFT;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = CIPURSEReadBinary(offset, 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(INFO, "File id: %x is empty", fileId);
|
||||||
|
else
|
||||||
|
PrintAndLogEx(INFO, "File id: %x data[%d]: %s", fileId, len, sprint_hex(buf, len));
|
||||||
|
|
||||||
|
DropField();
|
||||||
|
return PM3_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -169,6 +271,7 @@ static command_t CommandTable[] = {
|
||||||
{"help", CmdHelp, AlwaysAvailable, "This help."},
|
{"help", CmdHelp, AlwaysAvailable, "This help."},
|
||||||
{"info", CmdHFCipurseInfo, IfPm3Iso14443a, "Info about Cipurse tag."},
|
{"info", CmdHFCipurseInfo, IfPm3Iso14443a, "Info about Cipurse tag."},
|
||||||
{"auth", CmdHFCipurseAuth, IfPm3Iso14443a, "Authentication."},
|
{"auth", CmdHFCipurseAuth, IfPm3Iso14443a, "Authentication."},
|
||||||
|
{"read", CmdHFCipurseReadFile, IfPm3Iso14443a, "Read file."},
|
||||||
{NULL, NULL, 0, NULL}
|
{NULL, NULL, 0, NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue