added the skeleton for fudan card support

This commit is contained in:
iceman1001 2022-07-09 12:25:03 +02:00
parent 11e097c1bb
commit e17fc29e41
8 changed files with 498 additions and 7 deletions

View file

@ -275,6 +275,7 @@ set (TARGET_SOURCES
${PM3_ROOT}/client/src/cmdhfepa.c ${PM3_ROOT}/client/src/cmdhfepa.c
${PM3_ROOT}/client/src/cmdhffelica.c ${PM3_ROOT}/client/src/cmdhffelica.c
${PM3_ROOT}/client/src/cmdhffido.c ${PM3_ROOT}/client/src/cmdhffido.c
${PM3_ROOT}/client/src/cmdhffudan.c
${PM3_ROOT}/client/src/cmdhfgallagher.c ${PM3_ROOT}/client/src/cmdhfgallagher.c
${PM3_ROOT}/client/src/cmdhfcipurse.c ${PM3_ROOT}/client/src/cmdhfcipurse.c
${PM3_ROOT}/client/src/cmdhficlass.c ${PM3_ROOT}/client/src/cmdhficlass.c

View file

@ -365,7 +365,7 @@ endif
CFLAGS ?= $(DEFCFLAGS) CFLAGS ?= $(DEFCFLAGS)
# We cannot just use CFLAGS+=... because it has impact on sub-makes if CFLAGS is defined in env: # We cannot just use CFLAGS+=... because it has impact on sub-makes if CFLAGS is defined in env:
PM3CFLAGS = $(CFLAGS) PM3CFLAGS = $(CFLAGS)
PM3CFLAGS += -I./src -I./include -I../include -I../common -I../common_fpga $(PM3INCLUDES) $(INCLUDES) PM3CFLAGS += -g -I./src -I./include -I../include -I../common -I../common_fpga $(PM3INCLUDES) $(INCLUDES)
# WIP Testing # WIP Testing
#PM3CFLAGS += -std=c11 -pedantic #PM3CFLAGS += -std=c11 -pedantic
@ -557,6 +557,7 @@ SRCS = mifare/aiddesfire.c \
cmdhfemrtd.c \ cmdhfemrtd.c \
cmdhffelica.c \ cmdhffelica.c \
cmdhffido.c \ cmdhffido.c \
cmdhffudan.c \
cmdhfgallagher.c \ cmdhfgallagher.c \
cmdhfksx6924.c \ cmdhfksx6924.c \
cmdhfcipurse.c \ cmdhfcipurse.c \

View file

@ -49,6 +49,7 @@
#include "cmdhfwaveshare.h" // Waveshare #include "cmdhfwaveshare.h" // Waveshare
#include "cmdhftexkom.h" // Texkom #include "cmdhftexkom.h" // Texkom
#include "cmdhfxerox.h" // Xerox #include "cmdhfxerox.h" // Xerox
#include "cmdhffudan.h" // Fudan cards
#include "cmdtrace.h" // trace list #include "cmdtrace.h" // trace list
#include "ui.h" #include "ui.h"
#include "proxgui.h" #include "proxgui.h"
@ -439,6 +440,7 @@ static command_t CommandTable[] = {
{"emrtd", CmdHFeMRTD, AlwaysAvailable, "{ Machine Readable Travel Document... }"}, {"emrtd", CmdHFeMRTD, AlwaysAvailable, "{ Machine Readable Travel Document... }"},
{"felica", CmdHFFelica, AlwaysAvailable, "{ ISO18092 / FeliCa RFIDs... }"}, {"felica", CmdHFFelica, AlwaysAvailable, "{ ISO18092 / FeliCa RFIDs... }"},
{"fido", CmdHFFido, AlwaysAvailable, "{ FIDO and FIDO2 authenticators... }"}, {"fido", CmdHFFido, AlwaysAvailable, "{ FIDO and FIDO2 authenticators... }"},
{"fudan", CmdHFFudan, AlwaysAvailable, "{ Fudan RFIDs... }"},
{"gallagher", CmdHFGallagher, AlwaysAvailable, "{ Gallagher DESFire RFIDs... }"}, {"gallagher", CmdHFGallagher, AlwaysAvailable, "{ Gallagher DESFire RFIDs... }"},
{"ksx6924", CmdHFKSX6924, AlwaysAvailable, "{ KS X 6924 (T-Money, Snapper+) RFIDs }"}, {"ksx6924", CmdHFKSX6924, AlwaysAvailable, "{ KS X 6924 (T-Money, Snapper+) RFIDs }"},
{"jooki", CmdHF_Jooki, AlwaysAvailable, "{ Jooki RFIDs... }"}, {"jooki", CmdHF_Jooki, AlwaysAvailable, "{ Jooki RFIDs... }"},

View file

@ -18,9 +18,9 @@
#include "cmdhf14a.h" #include "cmdhf14a.h"
#include <ctype.h> #include <ctype.h>
#include <string.h> #include <string.h>
#include "cmdparser.h" // command_t #include "cmdparser.h" // command_t
#include "commonutil.h" // ARRAYLEN #include "commonutil.h" // ARRAYLEN
#include "comms.h" // clearCommandBuffer #include "comms.h" // clearCommandBuffer
#include "cmdtrace.h" #include "cmdtrace.h"
#include "cliparser.h" #include "cliparser.h"
#include "cmdhfmf.h" #include "cmdhfmf.h"
@ -29,9 +29,9 @@
#include "emv/emvcore.h" #include "emv/emvcore.h"
#include "ui.h" #include "ui.h"
#include "crc16.h" #include "crc16.h"
#include "util_posix.h" // msclock #include "util_posix.h" // msclock
#include "aidsearch.h" #include "aidsearch.h"
#include "cmdhf.h" // handle HF plot #include "cmdhf.h" // handle HF plot
#include "cliparser.h" #include "cliparser.h"
#include "protocols.h" // definitions of ISO14A/7816 protocol, MAGIC_GEN_1A #include "protocols.h" // definitions of ISO14A/7816 protocol, MAGIC_GEN_1A
#include "iso7816/apduinfo.h" // GetAPDUCodeDescription #include "iso7816/apduinfo.h" // GetAPDUCodeDescription
@ -2301,7 +2301,8 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) {
PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`emv search -s`")); PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`emv search -s`"));
if (isFUDAN) { if (isFUDAN) {
PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf 14a raw`") " - since FUDAN is different"); PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf fudan dump`"));
/*
PrintAndLogEx(HINT, " hf 14a raw -a -b 7 -k 26"); PrintAndLogEx(HINT, " hf 14a raw -a -b 7 -k 26");
PrintAndLogEx(HINT, " hf 14a raw -k -c 3000"); PrintAndLogEx(HINT, " hf 14a raw -k -c 3000");
PrintAndLogEx(HINT, " hf 14a raw -k -c 3001"); PrintAndLogEx(HINT, " hf 14a raw -k -c 3001");
@ -2311,6 +2312,7 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) {
PrintAndLogEx(HINT, " hf 14a raw -k -c 3005"); PrintAndLogEx(HINT, " hf 14a raw -k -c 3005");
PrintAndLogEx(HINT, " hf 14a raw -k -c 3006"); PrintAndLogEx(HINT, " hf 14a raw -k -c 3006");
PrintAndLogEx(HINT, " hf 14a raw -c 3007"); PrintAndLogEx(HINT, " hf 14a raw -c 3007");
*/
} }
PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "");

420
client/src/cmdhffudan.c Normal file
View file

@ -0,0 +1,420 @@
//-----------------------------------------------------------------------------
// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program 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.
//
// See LICENSE.txt for the text of the license.
//-----------------------------------------------------------------------------
// High frequency proximity cards from ISO14443A / Fudan commands
//-----------------------------------------------------------------------------
#include "cmdhffudan.h"
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "cliparser.h"
#include "cmdparser.h" // command_t
#include "comms.h"
#include "cmdhf14a.h"
#include "cmddata.h"
#include "mifare.h" // xiso
#include "cmdhf.h" //
#include "fileutils.h" // saveFile
#include "ui.h"
#include "commonutil.h" // MemLeToUint2byte
#include "protocols.h" // ISO14 defines
#include "crc16.h" // compute_crc
#include "util_posix.h" // msclock
#define FUDAN_BLOCK_READ_RETRY 3
#define MAX_FUDAN_BLOCK_SIZE 4
#define MAX_FUDAN_05_BLOCKS 8 // 16
#define MAX_FUDAN_08_BLOCKS 64
#ifndef AddCrc14A
# define AddCrc14A(data, len) compute_crc(CRC_14443_A, (data), (len), (data)+(len), (data)+(len)+1)
#endif
// iceman: these types are quite unsure.
typedef enum {
FM11RF005M,
FM11RF008M,
FM11RF005SH,
FM11RF08SH,
FUDAN_NONE,
} fudan_type_t;
static void fudan_print_blocks(uint16_t n, uint8_t *d) {
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "----+-------------+-----------------");
PrintAndLogEx(INFO, "blk | data | ascii");
PrintAndLogEx(INFO, "----+-------------+-----------------");
for (uint16_t b = 0; b < n; b++) {
PrintAndLogEx(INFO, "%3d | %s ", b, sprint_hex_ascii(d + (b * MAX_FUDAN_BLOCK_SIZE), MAX_FUDAN_BLOCK_SIZE));
}
PrintAndLogEx(INFO, "----+-------------+-----------------");
PrintAndLogEx(NORMAL, "");
}
static char* GenerateFilename(iso14a_card_select_t *card, const char *prefix, const char *suffix) {
if (card == NULL) {
return NULL;
}
char *fptr = calloc(sizeof(char) * (strlen(prefix) + strlen(suffix)) + sizeof(card->uid) * 2 + 1, sizeof(uint8_t));
strcpy(fptr, prefix);
FillFileNameByUID(fptr, card->uid, suffix, card->uidlen);
return fptr;
}
static fudan_type_t fudan_detected(iso14a_card_select_t *card) {
if ((card->sak & 0x0A) == 0x0A) {
uint8_t atqa = MemLeToUint2byte(card->atqa);
if ((atqa & 0x0003) == 0x0003) {
// Uses Shanghai algo
// printTag("FM11RF005SH (FUDAN Shanghai Metro)");
return FM11RF005SH;
} else if ((atqa & 0x0005) == 0x0005) {
// printTag("FM11RF005M (FUDAN MIFARE Classic clone)");
return FM11RF005M;
} else if ((atqa & 0x0008) == 0x0008) {
// printTag("FM11RF008M (FUDAN MIFARE Classic clone)");
return FM11RF008M;
}
} else if ((card->sak & 0x53) == 0x53) {
// printTag("FM11RF08SH (FUDAN)");
return FM11RF08SH;
}
return FUDAN_NONE;
}
static int fudan_get_type(iso14a_card_select_t *card, bool verbose) {
if (card == NULL) {
return PM3_EINVARG;
}
clearCommandBuffer();
SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0, NULL, 0);
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) {
PrintAndLogEx(DEBUG, "iso14443a card select failed");
return PM3_ESOFT;
}
memcpy(card, (iso14a_card_select_t *)resp.data.asBytes, sizeof(iso14a_card_select_t));
/*
0: couldn't read
1: OK, with ATS
2: OK, no ATS
3: proprietary Anticollision
*/
uint64_t select_status = resp.oldarg[0];
if (select_status == 0) {
PrintAndLogEx(DEBUG, "iso14443a card select failed");
DropField();
return PM3_ESOFT;
}
if (select_status == 3) {
if (verbose) {
PrintAndLogEx(INFO, "Card doesn't support standard iso14443-3 anticollision");
PrintAndLogEx(SUCCESS, "ATQA: %02X %02X", card->atqa[1], card->atqa[0]);
}
DropField();
return PM3_ESOFT;
}
PrintAndLogEx(SUCCESS, " UID: " _GREEN_("%s"), sprint_hex(card->uid, card->uidlen));
if (verbose) {
PrintAndLogEx(SUCCESS, "ATQA: " _GREEN_("%02X %02X"), card->atqa[1], card->atqa[0]);
PrintAndLogEx(SUCCESS, " SAK: " _GREEN_("%02X [%" PRIu64 "]"), card->sak, select_status);
if (card->ats_len >= 3) { // a valid ATS consists of at least the length byte (TL) and 2 CRC bytes
if (card->ats_len == card->ats[0] + 2)
PrintAndLogEx(SUCCESS, " ATS: " _GREEN_("%s"), sprint_hex(card->ats, card->ats[0]));
else {
PrintAndLogEx(SUCCESS, " ATS: [%d] " _GREEN_("%s"), card->ats_len, sprint_hex(card->ats, card->ats_len));
}
}
}
return PM3_SUCCESS;
}
int read_fudan_uid(bool loop, bool verbose) {
do {
iso14a_card_select_t card;
int res = fudan_get_type(&card, verbose);
if (loop) {
if (res != PM3_SUCCESS) {
continue;
}
} else {
switch (res) {
case PM3_EFAILED:
case PM3_EINVARG:
return res;
case PM3_ETIMEOUT:
PrintAndLogEx(DEBUG, "command execution time out");
return res;
case PM3_ESOFT:
PrintAndLogEx(DEBUG, "fudan card select failed");
return PM3_ESOFT;
default:
break;
}
}
if (loop) {
res = handle_hf_plot();
if (res != PM3_SUCCESS) {
break;
}
}
// decoding code
if (loop == false) {
PrintAndLogEx(NORMAL, "");
}
} while (loop && kbd_enter_pressed() == false);
return PM3_SUCCESS;
}
static int CmdHFFudanReader(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf fudan reader",
"Read a fudan tag",
"hf fudan reader\n"
"hf fudan reader -@ -> continuous reader mode"
);
void *argtable[] = {
arg_param_begin,
arg_lit0("v", "verbose", "Verbose scan and output"),
arg_lit0("@", NULL, "optional - continuous reader mode"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
bool verbose = arg_get_lit(ctx, 1);
bool cm = arg_get_lit(ctx, 2);
CLIParserFree(ctx);
if (cm) {
PrintAndLogEx(INFO, "Press " _GREEN_("<Enter>") " to exit");
return read_fudan_uid(cm, verbose);
}
DropField();
return PM3_SUCCESS;
}
static int CmdHFFudanDump(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf fudan dump",
"Dump FUDAN tag to binary file\n"
"If no <name> given, UID will be used as filename",
"hf fudan dump -f mydump --> dump using filename\n"
);
void *argtable[] = {
arg_param_begin,
arg_str0("f", "file", "<fn>", "filename of dump"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
int datafnlen = 0;
char dataFilename[FILE_PATH_SIZE] = {0};
CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)dataFilename, FILE_PATH_SIZE, &datafnlen);
CLIParserFree(ctx);
// Select card to get UID/UIDLEN/ATQA/SAK information
// leaves the field on
iso14a_card_select_t card;
int res = fudan_get_type(&card, false);
if (res != PM3_SUCCESS) {
PrintAndLogEx(FAILED, "failed to select a fudan card. Exiting...");
DropField();
return PM3_SUCCESS;
}
// validations
fudan_type_t t = fudan_detected(&card);
if (t == FUDAN_NONE) {
PrintAndLogEx(FAILED, "failed to detect a fudan card. Exiting...");
DropField();
return PM3_SUCCESS;
}
// detect card size
// 512b, 8kbits
uint8_t num_blocks = MAX_FUDAN_05_BLOCKS;
switch(t) {
case FM11RF008M:
num_blocks = MAX_FUDAN_08_BLOCKS;
break;
case FM11RF005SH:
case FM11RF005M:
case FM11RF08SH:
case FUDAN_NONE:
default:
break;
}
uint8_t carddata[num_blocks * MAX_FUDAN_BLOCK_SIZE];
/*
PrintAndLogEx(HINT, " hf 14a raw -a -b 7 -k 26");
PrintAndLogEx(HINT, " hf 14a raw -k -c 3000");
PrintAndLogEx(HINT, " hf 14a raw -k -c 3001");
PrintAndLogEx(HINT, " hf 14a raw -k -c 3002");
PrintAndLogEx(HINT, " hf 14a raw -k -c 3003");
PrintAndLogEx(HINT, " hf 14a raw -k -c 3004");
PrintAndLogEx(HINT, " hf 14a raw -k -c 3005");
PrintAndLogEx(HINT, " hf 14a raw -k -c 3006");
PrintAndLogEx(HINT, " hf 14a raw -c 3007");
*/
//
uint16_t flags = (ISO14A_NO_SELECT | ISO14A_NO_DISCONNECT | ISO14A_NO_RATS | ISO14A_RAW);
uint32_t argtimeout = 0;
uint32_t numbits = 0;
PrintAndLogEx(SUCCESS, "." NOLF);
// dump memory
for (uint8_t b = 0; b < num_blocks; b++) {
// read block
uint8_t cmd[4] = {ISO14443A_CMD_READBLOCK, b, 0x00, 0x00};
AddCrc14A(cmd, 2);
for (uint8_t tries = 0; tries < FUDAN_BLOCK_READ_RETRY; tries++) {
clearCommandBuffer();
PacketResponseNG resp;
SendCommandOLD(CMD_HF_ISO14443A_READER, flags, sizeof(cmd) | ((uint32_t)(numbits << 16)), argtimeout, cmd, sizeof(cmd));
if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) {
if (resp.status == PM3_SUCCESS) {
uint8_t *data = resp.data.asBytes;
memcpy(carddata + (b * MAX_FUDAN_BLOCK_SIZE), data, MAX_FUDAN_BLOCK_SIZE);
PrintAndLogEx(NORMAL, "." NOLF);
break;
} else {
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(FAILED, "could not read block %2d", b);
}
} else {
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(WARNING, "command execute timeout when trying to read block %2d", b);
}
}
}
DropField();
PrintAndLogEx(SUCCESS, "\nSucceeded in dumping all blocks");
fudan_print_blocks(num_blocks, carddata);
// create filename if none was given
if (strlen(dataFilename) < 1) {
char *fptr = GenerateFilename(&card, "hf-fudan-", "-dump");
if (fptr == NULL)
return PM3_ESOFT;
strcpy(dataFilename, fptr);
free(fptr);
}
saveFile(dataFilename, ".bin", (uint8_t *)carddata, sizeof(carddata));
saveFileEML(dataFilename, (uint8_t *)carddata, sizeof(carddata), MAX_FUDAN_BLOCK_SIZE);
iso14a_mf_extdump_t xdump;
xdump.card_info = card;
xdump.dump = (uint8_t *)carddata;
xdump.dumplen = sizeof(carddata);
saveFileJSON(dataFilename, jsfFudan, (uint8_t *)&xdump, sizeof(xdump), NULL);
return PM3_SUCCESS;
}
static int CmdHFFudanView(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf fudan view",
"Print a FUDAN dump file (bin/eml/json)",
"hf fudan view -f hf-fudan-01020304-dump.bin"
);
void *argtable[] = {
arg_param_begin,
arg_str1("f", "file", "<fn>", "filename of dump"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, false);
int fnlen = 0;
char filename[FILE_PATH_SIZE];
CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
CLIParserFree(ctx);
// read dump file
uint8_t *dump = NULL;
size_t bytes_read = 0;
int res = pm3_load_dump(filename, (void **)&dump, &bytes_read, (MAX_FUDAN_BLOCK_SIZE * MAX_FUDAN_08_BLOCKS));
if (res != PM3_SUCCESS) {
return res;
}
uint16_t block_cnt = MIN(MAX_FUDAN_05_BLOCKS, (bytes_read / MAX_FUDAN_BLOCK_SIZE));
fudan_print_blocks(block_cnt, dump);
free(dump);
return PM3_SUCCESS;
}
static int CmdHelp(const char *Cmd);
static command_t CommandTable[] = {
{"help", CmdHelp, AlwaysAvailable, "This help"},
{"reader", CmdHFFudanReader, IfPm3Iso14443a, "Act like a fudan reader"},
{"dump", CmdHFFudanDump, IfPm3Iso14443a, "Dump FUDAN tag to binary file"},
//{"sim", CmdHFFudanSim, IfPm3Iso14443a, "Simulate a fudan tag"},
//{"rdbl", CmdHFFudanRead, IfPm3Iso14443a, "Read a fudan tag"},
{"view", CmdHFFudanView, AlwaysAvailable, "Display content from tag dump file"},
//{"wrbl", CmdHFFudanWrite, IfPm3Iso14443a, "Write a fudan tag"},
{NULL, NULL, 0, NULL}
};
static int CmdHelp(const char *Cmd) {
(void)Cmd; // Cmd is not used so far
CmdsHelp(CommandTable);
return PM3_SUCCESS;
}
int CmdHFFudan(const char *Cmd) {
clearCommandBuffer();
return CmdsParse(CommandTable, Cmd);
}

29
client/src/cmdhffudan.h Normal file
View file

@ -0,0 +1,29 @@
//-----------------------------------------------------------------------------
// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program 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.
//
// See LICENSE.txt for the text of the license.
//-----------------------------------------------------------------------------
// High frequency proximity cards from ISO14443A / FUDAN commands
//-----------------------------------------------------------------------------
#ifndef CMDHFFUDAN_H__
#define CMDHFFUDAN_H__
#include "common.h"
#include "pm3_cmd.h"
int CmdHFFudan(const char *Cmd);
int read_fudan_uid(bool loop, bool verbose);
#endif

View file

@ -396,6 +396,19 @@ int saveFileJSONex(const char *preferredName, JSONFileType ftype, uint8_t *data,
} }
break; break;
} }
case jsfFudan: {
iso14a_mf_extdump_t *xdump = (iso14a_mf_extdump_t *)(void *) data;
JsonSaveStr(root, "FileType", "fudan");
JsonSaveBufAsHexCompact(root, "$.Card.UID", xdump->card_info.uid, xdump->card_info.uidlen);
JsonSaveBufAsHexCompact(root, "$.Card.ATQA", xdump->card_info.atqa, 2);
JsonSaveBufAsHexCompact(root, "$.Card.SAK", &(xdump->card_info.sak), 1);
for (size_t i = 0; i < (xdump->dumplen / 4); i++) {
char path[PATH_MAX_LENGTH] = {0};
snprintf(path, sizeof(path), "$.blocks.%zu", i);
JsonSaveBufAsHexCompact(root, path, &xdump->dump[i * 4], 4);
}
break;
}
case jsfMfuMemory: { case jsfMfuMemory: {
JsonSaveStr(root, "FileType", "mfu"); JsonSaveStr(root, "FileType", "mfu");
@ -1108,6 +1121,28 @@ int loadFileJSONex(const char *preferredName, void *data, size_t maxdatalen, siz
*datalen = sptr; *datalen = sptr;
} }
if (!strcmp(ctype, "fudan")) {
size_t sptr = 0;
for (int i = 0; i < 256; i++) {
if (sptr + 4 > maxdatalen) {
retval = PM3_EMALLOC;
goto out;
}
char blocks[30] = {0};
snprintf(blocks, sizeof(blocks), "$.blocks.%d", i);
size_t len = 0;
JsonLoadBufAsHex(root, blocks, &udata[sptr], 4, &len);
if (!len)
break;
sptr += len;
}
*datalen = sptr;
}
if (!strcmp(ctype, "mfu")) { if (!strcmp(ctype, "mfu")) {
mfu_dump_t *mem = (mfu_dump_t *)udata; mfu_dump_t *mem = (mfu_dump_t *)udata;

View file

@ -47,6 +47,7 @@ typedef enum {
jsfEM4x69, jsfEM4x69,
jsfEM4x50, jsfEM4x50,
jsfFido, jsfFido,
jsfFudan,
} JSONFileType; } JSONFileType;
typedef enum { typedef enum {