1 //-----------------------------------------------------------------------------
2 // Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
14 // See LICENSE.txt for the text of the license.
15 //-----------------------------------------------------------------------------
16 // High frequency proximity cards from ISO14443A / Fudan commands
17 //-----------------------------------------------------------------------------
19 #include "cmdhffudan.h"
24 #include "cliparser.h"
25 #include "cmdparser.h" // command_t
29 #include "mifare.h" // xiso
31 #include "fileutils.h" // saveFile
33 #include "commonutil.h" // MemLeToUint2byte
34 #include "protocols.h" // ISO14 defines
35 #include "crc16.h" // compute_crc
36 #include "util_posix.h" // msclock
38 #define FUDAN_BLOCK_READ_RETRY 3
39 #define MAX_FUDAN_BLOCK_SIZE 4
40 #define MAX_FUDAN_05_BLOCKS 8 // 16
41 #define MAX_FUDAN_08_BLOCKS 64
44 # define AddCrc14A(data, len) compute_crc(CRC_14443_A, (data), (len), (data)+(len), (data)+(len)+1)
48 // iceman: these types are quite unsure.
57 static void fudan_print_blocks(uint16_t n
, uint8_t *d
) {
58 PrintAndLogEx(NORMAL
, "");
59 PrintAndLogEx(INFO
, "----+-------------+-----------------");
60 PrintAndLogEx(INFO
, "blk | data | ascii");
61 PrintAndLogEx(INFO
, "----+-------------+-----------------");
62 for (uint16_t b
= 0; b
< n
; b
++) {
63 PrintAndLogEx(INFO
, "%3d | %s ", b
, sprint_hex_ascii(d
+ (b
* MAX_FUDAN_BLOCK_SIZE
), MAX_FUDAN_BLOCK_SIZE
));
65 PrintAndLogEx(INFO
, "----+-------------+-----------------");
66 PrintAndLogEx(NORMAL
, "");
69 static char *GenerateFilename(iso14a_card_select_t
*card
, const char *prefix
, const char *suffix
) {
73 char *fptr
= calloc(sizeof(char) * (strlen(prefix
) + strlen(suffix
)) + sizeof(card
->uid
) * 2 + 1, sizeof(uint8_t));
75 FillFileNameByUID(fptr
, card
->uid
, suffix
, card
->uidlen
);
79 static fudan_type_t
fudan_detected(iso14a_card_select_t
*card
) {
81 if ((card
->sak
& 0x0A) == 0x0A) {
83 uint8_t atqa
= MemLeToUint2byte(card
->atqa
);
84 if ((atqa
& 0x0003) == 0x0003) {
86 // printTag("FM11RF005SH (FUDAN Shanghai Metro)");
88 } else if ((atqa
& 0x0005) == 0x0005) {
89 // printTag("FM11RF005M (FUDAN MIFARE Classic clone)");
91 } else if ((atqa
& 0x0008) == 0x0008) {
92 // printTag("FM11RF008M (FUDAN MIFARE Classic clone)");
96 } else if ((card
->sak
& 0x53) == 0x53) {
97 // printTag("FM11RF08SH (FUDAN)");
103 static int fudan_get_type(iso14a_card_select_t
*card
, bool verbose
) {
109 clearCommandBuffer();
110 SendCommandMIX(CMD_HF_ISO14443A_READER
, ISO14A_CONNECT
| ISO14A_NO_DISCONNECT
, 0, 0, NULL
, 0);
111 PacketResponseNG resp
;
112 if (WaitForResponseTimeout(CMD_ACK
, &resp
, 2500) == false) {
113 PrintAndLogEx(DEBUG
, "iso14443a card select timeout");
117 memcpy(card
, (iso14a_card_select_t
*)resp
.data
.asBytes
, sizeof(iso14a_card_select_t
));
123 3: proprietary Anticollision
125 uint64_t select_status
= resp
.oldarg
[0];
127 if (select_status
== 0) {
128 PrintAndLogEx(DEBUG
, "iso14443a card select failed");
133 if (select_status
== 3) {
135 PrintAndLogEx(INFO
, "Card doesn't support standard iso14443-3 anticollision");
136 PrintAndLogEx(SUCCESS
, "ATQA: %02X %02X", card
->atqa
[1], card
->atqa
[0]);
142 PrintAndLogEx(SUCCESS
, " UID: " _GREEN_("%s"), sprint_hex(card
->uid
, card
->uidlen
));
144 PrintAndLogEx(SUCCESS
, "ATQA: " _GREEN_("%02X %02X"), card
->atqa
[1], card
->atqa
[0]);
145 PrintAndLogEx(SUCCESS
, " SAK: " _GREEN_("%02X [%" PRIu64
"]"), card
->sak
, select_status
);
147 if (card
->ats_len
>= 3) { // a valid ATS consists of at least the length byte (TL) and 2 CRC bytes
148 if (card
->ats_len
== card
->ats
[0] + 2)
149 PrintAndLogEx(SUCCESS
, " ATS: " _GREEN_("%s"), sprint_hex(card
->ats
, card
->ats
[0]));
151 PrintAndLogEx(SUCCESS
, " ATS: [%d] " _GREEN_("%s"), card
->ats_len
, sprint_hex(card
->ats
, card
->ats_len
));
158 int read_fudan_uid(bool loop
, bool verbose
) {
161 iso14a_card_select_t card
;
163 int res
= fudan_get_type(&card
, verbose
);
166 if (res
!= PM3_SUCCESS
) {
175 PrintAndLogEx(DEBUG
, "command execution time out");
178 PrintAndLogEx(DEBUG
, "fudan card select failed");
187 res
= handle_hf_plot(verbose
);
188 if (res
!= PM3_SUCCESS
) {
189 PrintAndLogEx(DEBUG
, "plot failed");
195 PrintAndLogEx(NORMAL
, "");
198 } while (loop
&& kbd_enter_pressed() == false);
204 static int CmdHFFudanReader(const char *Cmd
) {
205 CLIParserContext
*ctx
;
206 CLIParserInit(&ctx
, "hf fudan reader",
209 "hf fudan reader -@ -> continuous reader mode"
214 arg_lit0("v", "verbose", "verbose output"),
215 arg_lit0("@", NULL
, "optional - continuous reader mode"),
218 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
220 bool verbose
= arg_get_lit(ctx
, 1);
221 bool cm
= arg_get_lit(ctx
, 2);
226 PrintAndLogEx(INFO
, "Press " _GREEN_("<Enter>") " to exit");
229 read_fudan_uid(cm
, verbose
);
235 static int CmdHFFudanDump(const char *Cmd
) {
236 CLIParserContext
*ctx
;
237 CLIParserInit(&ctx
, "hf fudan dump",
238 "Dump FUDAN tag to file (bin/json)\n"
239 "If no <name> given, UID will be used as filename",
240 "hf fudan dump -f mydump --> dump using filename\n"
245 arg_str0("f", "file", "<fn>", "Specify a filename for dump file"),
246 arg_lit0(NULL
, "ns", "no save to file"),
249 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
252 char dataFilename
[FILE_PATH_SIZE
] = {0};
253 CLIParamStrToBuf(arg_get_str(ctx
, 1), (uint8_t *)dataFilename
, FILE_PATH_SIZE
, &datafnlen
);
254 bool nosave
= arg_get_lit(ctx
, 2);
257 // Select card to get UID/UIDLEN/ATQA/SAK information
258 // leaves the field on
259 iso14a_card_select_t card
;
260 int res
= fudan_get_type(&card
, false);
261 if (res
!= PM3_SUCCESS
) {
262 PrintAndLogEx(FAILED
, "failed to select a fudan card. Exiting...");
268 fudan_type_t t
= fudan_detected(&card
);
269 if (t
== FUDAN_NONE
) {
270 PrintAndLogEx(FAILED
, "failed to detect a fudan card. Exiting...");
277 uint8_t num_blocks
= MAX_FUDAN_05_BLOCKS
;
280 num_blocks
= MAX_FUDAN_08_BLOCKS
;
290 uint8_t carddata
[num_blocks
* MAX_FUDAN_BLOCK_SIZE
];
293 uint16_t flags
= (ISO14A_NO_SELECT
| ISO14A_NO_DISCONNECT
| ISO14A_NO_RATS
| ISO14A_RAW
);
294 uint32_t argtimeout
= 0;
295 uint32_t numbits
= 0;
297 PrintAndLogEx(SUCCESS
, "." NOLF
);
299 for (uint8_t b
= 0; b
< num_blocks
; b
++) {
302 uint8_t cmd
[4] = {ISO14443A_CMD_READBLOCK
, b
, 0x00, 0x00};
305 for (uint8_t tries
= 0; tries
< FUDAN_BLOCK_READ_RETRY
; tries
++) {
307 clearCommandBuffer();
308 PacketResponseNG resp
;
309 SendCommandMIX(CMD_HF_ISO14443A_READER
, flags
, sizeof(cmd
) | ((uint32_t)(numbits
<< 16)), argtimeout
, cmd
, sizeof(cmd
));
311 if (WaitForResponseTimeout(CMD_ACK
, &resp
, 1500)) {
312 if (resp
.status
== PM3_SUCCESS
) {
313 uint8_t *data
= resp
.data
.asBytes
;
314 memcpy(carddata
+ (b
* MAX_FUDAN_BLOCK_SIZE
), data
, MAX_FUDAN_BLOCK_SIZE
);
315 PrintAndLogEx(NORMAL
, "." NOLF
);
318 PrintAndLogEx(NORMAL
, "");
319 PrintAndLogEx(FAILED
, "could not read block %2d", b
);
322 PrintAndLogEx(NORMAL
, "");
323 PrintAndLogEx(WARNING
, "command execution time out when trying to read block %2d", b
);
330 PrintAndLogEx(SUCCESS
, "\nSucceeded in dumping all blocks");
332 fudan_print_blocks(num_blocks
, carddata
);
335 PrintAndLogEx(INFO
, "Called with no save option");
336 PrintAndLogEx(NORMAL
, "");
340 // create filename if none was given
341 if (strlen(dataFilename
) < 1) {
342 char *fptr
= GenerateFilename(&card
, "hf-fudan-", "-dump");
346 strcpy(dataFilename
, fptr
);
350 pm3_save_dump(dataFilename
, (uint8_t *)carddata
, sizeof(carddata
), jsfFudan
);
354 static int CmdHFFudanWrBl(const char *Cmd
) {
356 CLIParserContext
*ctx
;
357 CLIParserInit(&ctx
, "hf fudan wrbl",
358 "Write fudan block with 4 hex bytes of data\n",
359 "hf fudan wrbl --blk 1 -k FFFFFFFFFFFF -d 01020304"
363 arg_int1(NULL
, "blk", "<dec>", "block number"),
364 arg_str0("k", "key", "<hex>", "key, 6 hex bytes"),
365 arg_str0("d", "data", "<hex>", "bytes to write, 4 hex bytes"),
368 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
370 int b
= arg_get_int_def(ctx
, 1, 1);
373 uint8_t key
[6] = {0};
374 CLIGetHexWithReturn(ctx
, 2, key
, &keylen
);
376 uint8_t block
[MAX_FUDAN_BLOCK_SIZE
] = {0x00};
378 CLIGetHexWithReturn(ctx
, 3, block
, &blen
);
381 if (blen
!= MAX_FUDAN_BLOCK_SIZE
) {
382 PrintAndLogEx(WARNING
, "block data must include 4 HEX bytes. Got %i", blen
);
390 PrintAndLogEx(SUCCESS
, "Not implemented yet. Feel free to contribute!");
394 uint8_t blockno = (uint8_t)b;
396 PrintAndLogEx(INFO, "Writing block no %d, key %s", blockno, sprint_hex_inrow(key, sizeof(key)));
397 PrintAndLogEx(INFO, "data: %s", sprint_hex(block, sizeof(block)));
400 memcpy(data, key, sizeof(key));
401 memcpy(data + 10, block, sizeof(block));
402 clearCommandBuffer();
403 SendCommandMIX(CMD_HF_MIFARE_WRITEBL, blockno, 0, 0, data, sizeof(data));
405 PacketResponseNG resp;
406 if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) {
407 PrintAndLogEx(FAILED, "command execution time out");
411 uint8_t isok = resp.oldarg[0] & 0xff;
413 PrintAndLogEx(SUCCESS, "Write ( " _GREEN_("ok") " )");
414 PrintAndLogEx(HINT, "try `" _YELLOW_("hf fudan rdbl") "` to verify");
416 PrintAndLogEx(FAILED, "Write ( " _RED_("fail") " )");
422 static int CmdHFFudanRdBl(const char *Cmd
) {
423 CLIParserContext
*ctx
;
424 CLIParserInit(&ctx
, "hf fudan rdbl",
426 "hf fudan rdbl --blk 0 -k FFFFFFFFFFFF\n"
427 "hf fudan rdbl --blk 3 -v\n"
431 arg_int1(NULL
, "blk", "<dec>", "block number"),
432 arg_str0("k", "key", "<hex>", "key, 6 hex bytes"),
433 arg_lit0("v", "verbose", "verbose output"),
436 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
437 int b
= arg_get_int_def(ctx
, 1, 0);
440 uint8_t key
[6] = {0};
441 CLIGetHexWithReturn(ctx
, 2, key
, &keylen
);
442 // bool verbose = arg_get_lit(ctx, 3);
449 PrintAndLogEx(SUCCESS
, "Not implemented yet. Feel free to contribute!");
450 PrintAndLogEx(NORMAL
, "");
455 static int CmdHFFudanView(const char *Cmd
) {
457 CLIParserContext
*ctx
;
458 CLIParserInit(&ctx
, "hf fudan view",
459 "Print a FUDAN dump file (bin/eml/json)",
460 "hf fudan view -f hf-fudan-01020304-dump.bin"
464 arg_str1("f", "file", "<fn>", "Specify a filename for dump file"),
467 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
469 char filename
[FILE_PATH_SIZE
];
470 CLIParamStrToBuf(arg_get_str(ctx
, 1), (uint8_t *)filename
, FILE_PATH_SIZE
, &fnlen
);
474 uint8_t *dump
= NULL
;
475 size_t bytes_read
= 0;
476 int res
= pm3_load_dump(filename
, (void **)&dump
, &bytes_read
, (MAX_FUDAN_BLOCK_SIZE
* MAX_FUDAN_08_BLOCKS
));
477 if (res
!= PM3_SUCCESS
) {
481 uint16_t block_cnt
= MIN(MAX_FUDAN_05_BLOCKS
, (bytes_read
/ MAX_FUDAN_BLOCK_SIZE
));
483 fudan_print_blocks(block_cnt
, dump
);
489 static int CmdHelp(const char *Cmd
);
491 static command_t CommandTable
[] = {
492 {"help", CmdHelp
, AlwaysAvailable
, "This help"},
493 {"reader", CmdHFFudanReader
, IfPm3Iso14443a
, "Act like a fudan reader"},
494 {"dump", CmdHFFudanDump
, IfPm3Iso14443a
, "Dump FUDAN tag to binary file"},
495 //{"sim", CmdHFFudanSim, IfPm3Iso14443a, "Simulate a fudan tag"},
496 {"rdbl", CmdHFFudanRdBl
, IfPm3Iso14443a
, "Read a fudan tag"},
497 {"view", CmdHFFudanView
, AlwaysAvailable
, "Display content from tag dump file"},
498 {"wrbl", CmdHFFudanWrBl
, IfPm3Iso14443a
, "Write a fudan tag"},
499 {NULL
, NULL
, 0, NULL
}
502 static int CmdHelp(const char *Cmd
) {
503 (void)Cmd
; // Cmd is not used so far
504 CmdsHelp(CommandTable
);
508 int CmdHFFudan(const char *Cmd
) {
509 clearCommandBuffer();
510 return CmdsParse(CommandTable
, Cmd
);