1 //-----------------------------------------------------------------------------
2 // Copyright (C) 2018 Merlok
3 // Copyright (C) 2018 drHatson
5 // This code is licensed to you under the terms of the GNU GPL, version 2 or,
6 // at your option, any later version. See the LICENSE.txt file for the text of
8 //-----------------------------------------------------------------------------
9 // High frequency MIFARE Plus commands
10 //-----------------------------------------------------------------------------
26 #include "cliparser/cliparser.h"
27 #include "crypto/libpcrypto.h"
29 static const uint8_t DefaultKey
[16] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
33 const char *Description
;
36 static const PlusErrorsElm PlusErrors
[] = {
38 {0x00, "Transfer cannot be granted within the current authentication."},
39 {0x06, "Access Conditions not fulfilled. Block does not exist, block is not a value block."},
40 {0x07, "Too many read or write commands in the session or in the transaction."},
41 {0x08, "Invalid MAC in command or response"},
42 {0x09, "Block Number is not valid"},
43 {0x0a, "Invalid block number, not existing block number"},
44 {0x0b, "The current command code not available at the current card state."},
45 {0x0c, "Length error"},
46 {0x0f, "General Manipulation Error. Failure in the operation of the PICC (cannot write to the data block), etc."},
49 int PlusErrorsLen
= sizeof(PlusErrors
) / sizeof(PlusErrorsElm
);
51 const char * GetErrorDescription(uint8_t errorCode
) {
52 for(int i
= 0; i
< PlusErrorsLen
; i
++)
53 if (errorCode
== PlusErrors
[i
].Code
)
54 return PlusErrors
[i
].Description
;
56 return PlusErrors
[0].Description
;
59 static int CmdHelp(const char *Cmd
);
61 static bool VerboseMode
= false;
62 void SetVerboseMode(bool verbose
) {
63 VerboseMode
= verbose
;
66 int intExchangeRAW14aPlus(uint8_t *datain
, int datainlen
, bool activateField
, bool leaveSignalON
, uint8_t *dataout
, int maxdataoutlen
, int *dataoutlen
) {
68 PrintAndLog(">>> %s", sprint_hex(datain
, datainlen
));
70 int res
= ExchangeRAW14a(datain
, datainlen
, activateField
, leaveSignalON
, dataout
, maxdataoutlen
, dataoutlen
);
73 PrintAndLog("<<< %s", sprint_hex(dataout
, *dataoutlen
));
78 int MFPWritePerso(uint8_t *keyNum
, uint8_t *key
, bool activateField
, bool leaveSignalON
, uint8_t *dataout
, int maxdataoutlen
, int *dataoutlen
) {
79 uint8_t rcmd
[3 + 16] = {0xa8, keyNum
[1], keyNum
[0], 0x00};
80 memmove(&rcmd
[3], key
, 16);
82 return intExchangeRAW14aPlus(rcmd
, sizeof(rcmd
), activateField
, leaveSignalON
, dataout
, maxdataoutlen
, dataoutlen
);
85 int MFPCommitPerso(bool activateField
, bool leaveSignalON
, uint8_t *dataout
, int maxdataoutlen
, int *dataoutlen
) {
86 uint8_t rcmd
[1] = {0xaa};
88 return intExchangeRAW14aPlus(rcmd
, sizeof(rcmd
), activateField
, leaveSignalON
, dataout
, maxdataoutlen
, dataoutlen
);
91 int MFPReadBlock(mf4Session
*session
, bool plain
, uint8_t blockNum
, uint8_t blockCount
, bool activateField
, bool leaveSignalON
, uint8_t *dataout
, int maxdataoutlen
, int *dataoutlen
, uint8_t *mac
) {
92 uint8_t rcmd
[4 + 8] = {(plain
?(0x37):(0x33)), blockNum
, 0x00, blockCount
};
93 if (!plain
&& session
)
94 CalculateMAC(session
, mtypReadCmd
, blockNum
, blockCount
, rcmd
, 4, &rcmd
[4], VerboseMode
);
96 int res
= intExchangeRAW14aPlus(rcmd
, plain
?4:sizeof(rcmd
), activateField
, leaveSignalON
, dataout
, maxdataoutlen
, dataoutlen
);
103 if(session
&& mac
&& *dataoutlen
> 11)
104 CalculateMAC(session
, mtypReadResp
, blockNum
, blockCount
, dataout
, *dataoutlen
- 8 - 2, mac
, VerboseMode
);
109 int MFPWriteBlock(mf4Session
*session
, uint8_t blockNum
, uint8_t *data
, bool activateField
, bool leaveSignalON
, uint8_t *dataout
, int maxdataoutlen
, int *dataoutlen
, uint8_t *mac
) {
110 uint8_t rcmd
[1 + 2 + 16 + 8] = {0xA3, blockNum
, 0x00};
111 memmove(&rcmd
[3], data
, 16);
113 CalculateMAC(session
, mtypWriteCmd
, blockNum
, 1, rcmd
, 19, &rcmd
[19], VerboseMode
);
115 int res
= intExchangeRAW14aPlus(rcmd
, sizeof(rcmd
), activateField
, leaveSignalON
, dataout
, maxdataoutlen
, dataoutlen
);
122 if(session
&& mac
&& *dataoutlen
> 3)
123 CalculateMAC(session
, mtypWriteResp
, blockNum
, 1, dataout
, *dataoutlen
, mac
, VerboseMode
);
128 int CmdHFMFPInfo(const char *cmd
) {
130 if (cmd
&& strlen(cmd
) > 0)
131 PrintAndLog("WARNING: command don't have any parameters.\n");
133 // info about 14a part
137 UsbCommand c
= {CMD_READER_ISO_14443a
, {ISO14A_CONNECT
| ISO14A_NO_DISCONNECT
, 0, 0}};
141 WaitForResponse(CMD_ACK
,&resp
);
143 iso14a_card_select_t card
;
144 memcpy(&card
, (iso14a_card_select_t
*)resp
.d
.asBytes
, sizeof(iso14a_card_select_t
));
146 uint64_t select_status
= resp
.arg
[0]; // 0: couldn't read, 1: OK, with ATS, 2: OK, no ATS, 3: proprietary Anticollision
148 if (select_status
== 1 || select_status
== 2) {
149 PrintAndLog("----------------------------------------------");
150 PrintAndLog("Mifare Plus info:");
152 // MIFARE Type Identification Procedure
153 // https://www.nxp.com/docs/en/application-note/AN10833.pdf
154 uint16_t ATQA
= card
.atqa
[0] + (card
.atqa
[1] << 8);
155 if (ATQA
== 0x0004) PrintAndLog("ATQA: Mifare Plus 2k 4bUID");
156 if (ATQA
== 0x0002) PrintAndLog("ATQA: Mifare Plus 4k 4bUID");
157 if (ATQA
== 0x0044) PrintAndLog("ATQA: Mifare Plus 2k 7bUID");
158 if (ATQA
== 0x0042) PrintAndLog("ATQA: Mifare Plus 4k 7bUID");
160 uint8_t SLmode
= 0xff;
161 if (card
.sak
== 0x08) {
162 PrintAndLog("SAK: Mifare Plus 2k 7bUID");
163 if (select_status
== 2) SLmode
= 1;
165 if (card
.sak
== 0x18) {
166 PrintAndLog("SAK: Mifare Plus 4k 7bUID");
167 if (select_status
== 2) SLmode
= 1;
169 if (card
.sak
== 0x10) {
170 PrintAndLog("SAK: Mifare Plus 2k");
171 if (select_status
== 2) SLmode
= 2;
173 if (card
.sak
== 0x11) {
174 PrintAndLog("SAK: Mifare Plus 4k");
175 if (select_status
== 2) SLmode
= 2;
177 if (card
.sak
== 0x20) {
178 PrintAndLog("SAK: Mifare Plus SL0/SL3 or Mifare desfire");
179 if (card
.ats_len
> 0) {
183 uint8_t data
[250] = {0};
185 // https://github.com/Proxmark/proxmark3/blob/master/client/scripts/mifarePlus.lua#L161
186 uint8_t cmd
[3 + 16] = {0xa8, 0x90, 0x90, 0x00};
187 int res
= ExchangeRAW14a(cmd
, sizeof(cmd
), false, false, data
, sizeof(data
), &datalen
);
188 if (!res
&& datalen
> 1 && data
[0] == 0x09) {
195 PrintAndLog("Mifare Plus SL mode: SL%d", SLmode
);
197 PrintAndLog("Mifare Plus SL mode: unknown(");
199 PrintAndLog("Mifare Plus info not available.");
207 int CmdHFMFPWritePerso(const char *cmd
) {
208 uint8_t keyNum
[64] = {0};
210 uint8_t key
[64] = {0};
213 CLIParserInit("hf mfp wrp",
214 "Executes Write Perso command. Can be used in SL0 mode only.",
215 "Usage:\n\thf mfp wrp 4000 000102030405060708090a0b0c0d0e0f -> write key (00..0f) to key number 4000 \n"
216 "\thf mfp wrp 4000 -> write default key(0xff..0xff) to key number 4000");
220 arg_lit0("vV", "verbose", "show internal data."),
221 arg_str1(NULL
, NULL
, "<HEX key number (2b)>", NULL
),
222 arg_strx0(NULL
, NULL
, "<HEX key (16b)>", NULL
),
225 CLIExecWithReturn(cmd
, argtable
, true);
227 bool verbose
= arg_get_lit(1);
228 CLIGetHexWithReturn(2, keyNum
, &keyNumLen
);
229 CLIGetHexWithReturn(3, key
, &keyLen
);
232 SetVerboseMode(verbose
);
235 memmove(key
, DefaultKey
, 16);
239 if (keyNumLen
!= 2) {
240 PrintAndLog("Key number length must be 2 bytes instead of: %d", keyNumLen
);
244 PrintAndLog("Key length must be 16 bytes instead of: %d", keyLen
);
248 uint8_t data
[250] = {0};
251 int res
= MFPWritePerso(keyNum
, key
, true, false, data
, sizeof(data
), &datalen
);
253 PrintAndLog("Exchange error: %d", res
);
258 PrintAndLog("Command must return 3 bytes instead of: %d", datalen
);
262 if (data
[0] != 0x90) {
263 PrintAndLog("Command error: %02x %s", data
[0], GetErrorDescription(data
[0]));
266 PrintAndLog("Write OK.");
271 uint16_t CardAddresses
[] = {0x9000, 0x9001, 0x9002, 0x9003, 0x9004, 0xA000, 0xA001, 0xA080, 0xA081, 0xC000, 0xC001};
273 int CmdHFMFPInitPerso(const char *cmd
) {
275 uint8_t key
[256] = {0};
277 uint8_t keyNum
[2] = {0};
278 uint8_t data
[250] = {0};
281 CLIParserInit("hf mfp initp",
282 "Executes Write Perso command for all card's keys. Can be used in SL0 mode only.",
283 "Usage:\n\thf mfp initp 000102030405060708090a0b0c0d0e0f -> fill all the keys with key (00..0f)\n"
284 "\thf mfp initp -vv -> fill all the keys with default key(0xff..0xff) and show all the data exchange");
288 arg_litn("vV", "verbose", 0, 2, "show internal data."),
289 arg_strx0(NULL
, NULL
, "<HEX key (16b)>", NULL
),
292 CLIExecWithReturn(cmd
, argtable
, true);
294 bool verbose
= arg_get_lit(1);
295 bool verbose2
= arg_get_lit(1) > 1;
296 CLIGetHexWithReturn(2, key
, &keyLen
);
299 if (keyLen
&& keyLen
!= 16) {
300 PrintAndLog("Key length must be 16 bytes instead of: %d", keyLen
);
305 memmove(key
, DefaultKey
, 16);
307 SetVerboseMode(verbose2
);
308 for (uint16_t sn
= 0x4000; sn
< 0x4050; sn
++) {
310 keyNum
[1] = sn
& 0xff;
311 res
= MFPWritePerso(keyNum
, key
, (sn
== 0x4000), true, data
, sizeof(data
), &datalen
);
312 if (!res
&& (datalen
== 3) && data
[0] == 0x09) {
313 PrintAndLog("2k card detected.");
316 if (res
|| (datalen
!= 3) || data
[0] != 0x90) {
317 PrintAndLog("Write error on address %04x", sn
);
322 SetVerboseMode(verbose
);
323 for (int i
= 0; i
< sizeof(CardAddresses
) / 2; i
++) {
324 keyNum
[0] = CardAddresses
[i
] >> 8;
325 keyNum
[1] = CardAddresses
[i
] & 0xff;
326 res
= MFPWritePerso(keyNum
, key
, false, true, data
, sizeof(data
), &datalen
);
327 if (!res
&& (datalen
== 3) && data
[0] == 0x09) {
328 PrintAndLog("Skipped[%04x]...", CardAddresses
[i
]);
330 if (res
|| (datalen
!= 3) || data
[0] != 0x90) {
331 PrintAndLog("Write error on address %04x", CardAddresses
[i
]);
342 PrintAndLog("Done.");
347 int CmdHFMFPCommitPerso(const char *cmd
) {
348 CLIParserInit("hf mfp commitp",
349 "Executes Commit Perso command. Can be used in SL0 mode only.",
350 "Usage:\n\thf mfp commitp -> \n");
354 arg_lit0("vV", "verbose", "show internal data."),
355 arg_int0(NULL
, NULL
, "SL mode", NULL
),
358 CLIExecWithReturn(cmd
, argtable
, true);
360 bool verbose
= arg_get_lit(1);
363 SetVerboseMode(verbose
);
365 uint8_t data
[250] = {0};
368 int res
= MFPCommitPerso(true, false, data
, sizeof(data
), &datalen
);
370 PrintAndLog("Exchange error: %d", res
);
375 PrintAndLog("Command must return 3 bytes instead of: %d", datalen
);
379 if (data
[0] != 0x90) {
380 PrintAndLog("Command error: %02x %s", data
[0], GetErrorDescription(data
[0]));
383 PrintAndLog("Switch level OK.");
388 int CmdHFMFPAuth(const char *cmd
) {
389 uint8_t keyn
[250] = {0};
391 uint8_t key
[250] = {0};
394 CLIParserInit("hf mfp auth",
395 "Executes AES authentication command for Mifare Plus card",
396 "Usage:\n\thf mfp auth 4000 000102030405060708090a0b0c0d0e0f -> executes authentication\n"
397 "\thf mfp auth 9003 FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -v -> executes authentication and shows all the system data\n");
401 arg_lit0("vV", "verbose", "show internal data."),
402 arg_str1(NULL
, NULL
, "<Key Num (HEX 2 bytes)>", NULL
),
403 arg_str1(NULL
, NULL
, "<Key Value (HEX 16 bytes)>", NULL
),
406 CLIExecWithReturn(cmd
, argtable
, true);
408 bool verbose
= arg_get_lit(1);
409 CLIGetHexWithReturn(2, keyn
, &keynlen
);
410 CLIGetHexWithReturn(3, key
, &keylen
);
414 PrintAndLog("ERROR: <Key Num> must be 2 bytes long instead of: %d", keynlen
);
419 PrintAndLog("ERROR: <Key Value> must be 16 bytes long instead of: %d", keylen
);
423 return MifareAuth4(NULL
, keyn
, key
, true, false, verbose
);
426 int CmdHFMFPRdbl(const char *cmd
) {
427 uint8_t keyn
[2] = {0};
428 uint8_t key
[250] = {0};
431 CLIParserInit("hf mfp rdbl",
432 "Reads several blocks from Mifare Plus card.",
433 "Usage:\n\thf mfp rdbl 0 000102030405060708090a0b0c0d0e0f -> executes authentication and read block 0 data\n"
434 "\thf mfp rdbl 1 -v -> executes authentication and shows sector 1 data with default key 0xFF..0xFF and some additional data\n");
438 arg_lit0("vV", "verbose", "show internal data."),
439 arg_int0("nN", "count", "blocks count (by default 1).", NULL
),
440 arg_lit0("bB", "keyb", "use key B (by default keyA)."),
441 arg_lit0("pP", "plain", "plain communication mode between reader and card."),
442 arg_int1(NULL
, NULL
, "<Block Num (0..255)>", NULL
),
443 arg_str0(NULL
, NULL
, "<Key Value (HEX 16 bytes)>", NULL
),
446 CLIExecWithReturn(cmd
, argtable
, false);
448 bool verbose
= arg_get_lit(1);
449 int blocksCount
= arg_get_int_def(2, 1);
450 bool keyB
= arg_get_lit(3);
451 int plain
= arg_get_lit(4);
452 uint32_t blockn
= arg_get_int(5);
453 CLIGetHexWithReturn(6, key
, &keylen
);
456 SetVerboseMode(verbose
);
459 memmove(key
, DefaultKey
, 16);
464 PrintAndLog("ERROR: <Block Num> must be in range [0..255] instead of: %d", blockn
);
469 PrintAndLog("ERROR: <Key Value> must be 16 bytes long instead of: %d", keylen
);
473 // 3 blocks - wo iso14443-4 chaining
474 if (blocksCount
> 3) {
475 PrintAndLog("ERROR: blocks count must be less than 3 instead of: %d", blocksCount
);
479 if (blocksCount
> 1 && mfIsSectorTrailer(blockn
)) {
480 PrintAndLog("WARNING: trailer!");
483 uint8_t sectorNum
= mfSectorNum(blockn
& 0xff);
484 uint16_t uKeyNum
= 0x4000 + sectorNum
* 2 + (keyB
? 1 : 0);
485 keyn
[0] = uKeyNum
>> 8;
486 keyn
[1] = uKeyNum
& 0xff;
488 PrintAndLog("--block:%d sector[%d]:%02x key:%04x", blockn
, mfNumBlocksPerSector(sectorNum
), sectorNum
, uKeyNum
);
491 int res
= MifareAuth4(&session
, keyn
, key
, true, true, verbose
);
493 PrintAndLog("Authentication error: %d", res
);
497 uint8_t data
[250] = {0};
499 uint8_t mac
[8] = {0};
500 res
= MFPReadBlock(&session
, plain
, blockn
& 0xff, blocksCount
, false, false, data
, sizeof(data
), &datalen
, mac
);
502 PrintAndLog("Read error: %d", res
);
506 if (datalen
&& data
[0] != 0x90) {
507 PrintAndLog("Card read error: %02x %s", data
[0], GetErrorDescription(data
[0]));
511 if (datalen
!= 1 + blocksCount
* 16 + 8 + 2) {
512 PrintAndLog("Error return length:%d", datalen
);
517 for(int i
= 0; i
< blocksCount
; i
++) {
518 PrintAndLog("data[%03d]: %s", indx
, sprint_hex(&data
[1 + i
* 16], 16));
520 if (mfIsSectorTrailer(indx
) && i
!= blocksCount
- 1){
521 PrintAndLog("data[%03d]: ------------------- trailer -------------------", indx
);
526 if (memcmp(&data
[blocksCount
* 16 + 1], mac
, 8)) {
527 PrintAndLog("WARNING: mac not equal...");
528 PrintAndLog("MAC card: %s", sprint_hex(&data
[blocksCount
* 16 + 1], 8));
529 PrintAndLog("MAC reader: %s", sprint_hex(mac
, 8));
532 PrintAndLog("MAC: %s", sprint_hex(&data
[blocksCount
* 16 + 1], 8));
538 int CmdHFMFPRdsc(const char *cmd
) {
539 uint8_t keyn
[2] = {0};
540 uint8_t key
[250] = {0};
543 CLIParserInit("hf mfp rdsc",
544 "Reads one sector from Mifare Plus card.",
545 "Usage:\n\thf mfp rdsc 0 000102030405060708090a0b0c0d0e0f -> executes authentication and read sector 0 data\n"
546 "\thf mfp rdsc 1 -v -> executes authentication and shows sector 1 data with default key 0xFF..0xFF and some additional data\n");
550 arg_lit0("vV", "verbose", "show internal data."),
551 arg_lit0("bB", "keyb", "use key B (by default keyA)."),
552 arg_lit0("pP", "plain", "plain communication mode between reader and card."),
553 arg_int1(NULL
, NULL
, "<Sector Num (0..255)>", NULL
),
554 arg_str0(NULL
, NULL
, "<Key Value (HEX 16 bytes)>", NULL
),
557 CLIExecWithReturn(cmd
, argtable
, false);
559 bool verbose
= arg_get_lit(1);
560 bool keyB
= arg_get_lit(2);
561 bool plain
= arg_get_lit(3);
562 uint32_t sectorNum
= arg_get_int(4);
563 CLIGetHexWithReturn(5, key
, &keylen
);
566 SetVerboseMode(verbose
);
569 memmove(key
, DefaultKey
, 16);
573 if (sectorNum
> 39) {
574 PrintAndLog("ERROR: <Sector Num> must be in range [0..39] instead of: %d", sectorNum
);
579 PrintAndLog("ERROR: <Key Value> must be 16 bytes long instead of: %d", keylen
);
583 uint16_t uKeyNum
= 0x4000 + sectorNum
* 2 + (keyB
? 1 : 0);
584 keyn
[0] = uKeyNum
>> 8;
585 keyn
[1] = uKeyNum
& 0xff;
587 PrintAndLog("--sector[%d]:%02x key:%04x", mfNumBlocksPerSector(sectorNum
), sectorNum
, uKeyNum
);
590 int res
= MifareAuth4(&session
, keyn
, key
, true, true, verbose
);
592 PrintAndLog("Authentication error: %d", res
);
596 uint8_t data
[250] = {0};
598 uint8_t mac
[8] = {0};
599 for(int n
= mfFirstBlockOfSector(sectorNum
); n
< mfFirstBlockOfSector(sectorNum
) + mfNumBlocksPerSector(sectorNum
); n
++) {
600 res
= MFPReadBlock(&session
, plain
, n
& 0xff, 1, false, true, data
, sizeof(data
), &datalen
, mac
);
602 PrintAndLog("Read error: %d", res
);
607 if (datalen
&& data
[0] != 0x90) {
608 PrintAndLog("Card read error: %02x %s", data
[0], GetErrorDescription(data
[0]));
612 if (datalen
!= 1 + 16 + 8 + 2) {
613 PrintAndLog("Error return length:%d", datalen
);
618 PrintAndLog("data[%03d]: %s", n
, sprint_hex(&data
[1], 16));
620 if (memcmp(&data
[1 + 16], mac
, 8)) {
621 PrintAndLog("WARNING: mac on block %d not equal...", n
);
622 PrintAndLog("MAC card: %s", sprint_hex(&data
[1 + 16], 8));
623 PrintAndLog("MAC reader: %s", sprint_hex(mac
, 8));
626 PrintAndLog("MAC: %s", sprint_hex(&data
[1 + 16], 8));
634 int CmdHFMFPWrbl(const char *cmd
) {
635 uint8_t keyn
[2] = {0};
636 uint8_t key
[250] = {0};
638 uint8_t datain
[250] = {0};
641 CLIParserInit("hf mfp wrbl",
642 "Writes one block to Mifare Plus card.",
643 "Usage:\n\thf mfp wrbl 1 ff0000000000000000000000000000ff 000102030405060708090a0b0c0d0e0f -> writes block 1 data\n"
644 "\thf mfp wrbl 2 ff0000000000000000000000000000ff -v -> writes block 2 data with default key 0xFF..0xFF and some additional data\n");
648 arg_lit0("vV", "verbose", "show internal data."),
649 arg_lit0("bB", "keyb", "use key B (by default keyA)."),
650 arg_int1(NULL
, NULL
, "<Block Num (0..255)>", NULL
),
651 arg_str1(NULL
, NULL
, "<Data (HEX 16 bytes)>", NULL
),
652 arg_str0(NULL
, NULL
, "<Key (HEX 16 bytes)>", NULL
),
655 CLIExecWithReturn(cmd
, argtable
, false);
657 bool verbose
= arg_get_lit(1);
658 bool keyB
= arg_get_lit(2);
659 uint32_t blockNum
= arg_get_int(3);
660 CLIGetHexWithReturn(4, datain
, &datainlen
);
661 CLIGetHexWithReturn(5, key
, &keylen
);
664 SetVerboseMode(verbose
);
667 memmove(key
, DefaultKey
, 16);
672 PrintAndLog("ERROR: <Block Num> must be in range [0..255] instead of: %d", blockNum
);
677 PrintAndLog("ERROR: <Key> must be 16 bytes long instead of: %d", keylen
);
681 if (datainlen
!= 16) {
682 PrintAndLog("ERROR: <Data> must be 16 bytes long instead of: %d", datainlen
);
686 uint8_t sectorNum
= mfSectorNum(blockNum
& 0xff);
687 uint16_t uKeyNum
= 0x4000 + sectorNum
* 2 + (keyB
? 1 : 0);
688 keyn
[0] = uKeyNum
>> 8;
689 keyn
[1] = uKeyNum
& 0xff;
691 PrintAndLog("--block:%d sector[%d]:%02x key:%04x", blockNum
& 0xff, mfNumBlocksPerSector(sectorNum
), sectorNum
, uKeyNum
);
694 int res
= MifareAuth4(&session
, keyn
, key
, true, true, verbose
);
696 PrintAndLog("Authentication error: %d", res
);
700 uint8_t data
[250] = {0};
702 uint8_t mac
[8] = {0};
703 res
= MFPWriteBlock(&session
, blockNum
& 0xff, datain
, false, false, data
, sizeof(data
), &datalen
, mac
);
705 PrintAndLog("Write error: %d", res
);
710 if (datalen
!= 3 && (datalen
!= 3 + 8)) {
711 PrintAndLog("Error return length:%d", datalen
);
716 if (datalen
&& data
[0] != 0x90) {
717 PrintAndLog("Card write error: %02x %s", data
[0], GetErrorDescription(data
[0]));
722 if (memcmp(&data
[1], mac
, 8)) {
723 PrintAndLog("WARNING: mac not equal...");
724 PrintAndLog("MAC card: %s", sprint_hex(&data
[1], 8));
725 PrintAndLog("MAC reader: %s", sprint_hex(mac
, 8));
728 PrintAndLog("MAC: %s", sprint_hex(&data
[1], 8));
732 PrintAndLog("Write OK.");
736 static command_t CommandTable
[] =
738 {"help", CmdHelp
, 1, "This help"},
739 {"info", CmdHFMFPInfo
, 0, "Info about Mifare Plus tag"},
740 {"wrp", CmdHFMFPWritePerso
, 0, "Write Perso command"},
741 {"initp", CmdHFMFPInitPerso
, 0, "Fills all the card's keys"},
742 {"commitp", CmdHFMFPCommitPerso
, 0, "Move card to SL1 or SL3 mode"},
743 {"auth", CmdHFMFPAuth
, 0, "Authentication"},
744 {"rdbl", CmdHFMFPRdbl
, 0, "Read blocks"},
745 {"rdsc", CmdHFMFPRdsc
, 0, "Read sectors"},
746 {"wrbl", CmdHFMFPWrbl
, 0, "Write blocks"},
747 {NULL
, NULL
, 0, NULL
}
750 int CmdHFMFP(const char *Cmd
) {
751 (void)WaitForResponseTimeout(CMD_ACK
,NULL
,100);
752 CmdsParse(CommandTable
, Cmd
);
756 int CmdHelp(const char *Cmd
) {
757 CmdsHelp(CommandTable
);