diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index 6ff96ec5b..e631dc9b4 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -1644,18 +1644,21 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, AddCrc14A(rPPS, sizeof(rPPS) - 2); - // EV1/NTAG, set PWD w AMIIBO algo if all zero. + // EV1/NTAG Amiibo: if no PWD is set, populate emulator memory with the UID-derived + // password and its PACK (0x8080) at the PWD/PACK pages, so the sim serves them from + // emulator memory like any other value. Auth and PACK are then read straight from + // memory, keeping eset/eview authoritative (what you set is what the sim serves). if (tagType == 7) { uint8_t pwd[4] = {0, 0, 0, 0}; - uint8_t gen_pwd[4] = {0, 0, 0, 0}; emlGet(pwd, (*pages - 1) * 4 + MFU_DUMP_PREFIX_LENGTH, sizeof(pwd)); - emlGet(rPACK, (*pages) * 4 + MFU_DUMP_PREFIX_LENGTH, sizeof(rPACK)); - - Uint4byteToMemBe(gen_pwd, ul_ev1_pwdgenB(data)); - if (memcmp(pwd, gen_pwd, sizeof(pwd)) == 0) { - rPACK[0] = 0x80; - rPACK[1] = 0x80; + if (memcmp(pwd, "\x00\x00\x00\x00", 4) == 0) { + uint8_t amiibo_pwd[4] = {0, 0, 0, 0}; + Uint4byteToMemLe(amiibo_pwd, ul_ev1_pwdgenB(data)); + emlSet(amiibo_pwd, (*pages - 1) * 4 + MFU_DUMP_PREFIX_LENGTH, sizeof(amiibo_pwd)); + uint8_t amiibo_pack[4] = {0x80, 0x80, 0x00, 0x00}; + emlSet(amiibo_pack, (*pages) * 4 + MFU_DUMP_PREFIX_LENGTH, sizeof(amiibo_pack)); } + emlGet(rPACK, (*pages) * 4 + MFU_DUMP_PREFIX_LENGTH, sizeof(rPACK)); } AddCrc14A(rPACK, sizeof(rPACK) - 2); @@ -2347,14 +2350,24 @@ void SimulateIso14443aTagEx(uint8_t tagType, uint16_t flags, uint8_t *useruid, u Dbhexdump(4, pwd, 0); } - if (memcmp(pwd, "\x00\x00\x00\x00", 4) == 0) { - Uint4byteToMemLe(pwd, ul_ev1_pwdgenB(useruid)); - if (g_dbglevel >= DBG_DEBUG) Dbprintf("Calc pwd... %02X %02X %02X %02X", pwd[0], pwd[1], pwd[2], pwd[3]); - } - + // PWD comes straight from emulator memory (read above). For an unset (all-zero) + // PWD, the Amiibo password is populated into memory at sim init, so there's no + // substitution here -- eset/eview stay authoritative. if (memcmp(receivedCmd + 1, pwd, 4) == 0) { if (g_dbglevel >= DBG_DEBUG) Dbprintf("Password match, responding with PACK."); - p_response = &responses[RESP_INDEX_PACK]; + + // Serve PACK straight from the PACK page (page `pages`) in emulator memory, so + // eset/eload of the PACK page is honored live. The Amiibo PACK (0x8080) for an + // unset PWD is populated into memory at sim init, so nothing special-cases here. + uint8_t pack_live[4] = {0, 0, 0, 0}; + emlGet(pack_live, pages * 4 + MFU_DUMP_PREFIX_LENGTH, 2); + + dynamic_response_info.response[0] = pack_live[0]; + dynamic_response_info.response[1] = pack_live[1]; + AddCrc14A(dynamic_response_info.response, 2); + dynamic_response_info.response_n = 4; // 2 PACK + 2 CRC + prepare_tag_modulation(&dynamic_response_info, DYNAMIC_MODULATION_BUFFER_SIZE); + p_response = &dynamic_response_info; } else { if (g_dbglevel >= DBG_DEBUG) Dbprintf("Password did not match, NACK_IV."); p_response = NULL; diff --git a/client/src/cmdhfmfu.c b/client/src/cmdhfmfu.c index 50220fc24..f4cc81394 100644 --- a/client/src/cmdhfmfu.c +++ b/client/src/cmdhfmfu.c @@ -24,6 +24,7 @@ #include "aes.h" #include "cmdhfmf.h" #include "cmdhf14a.h" +#include "mifare/mifarehost.h" // mf_eml_set_mem_xt #include "comms.h" #include "protocols.h" #include "generator.h" @@ -8053,6 +8054,71 @@ static int CmdHF14AMfUIncr(const char *Cmd) { return PM3_SUCCESS; } +static int CmdHF14AMfUeSetBlk(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mfu esetblk", + "Set emulator memory page(s). One page = 4 bytes; pass multiple\n" + "whole pages of data to set consecutive pages from --blk.", + "hf mfu esetblk --blk 4 -d 04E10CDA\n" + "hf mfu esetblk --blk 4 -d 04E10CDA993C8048 -> sets pages 4-5\n" + ); + void *argtable[] = { + arg_param_begin, + arg_int1("b", "blk", "", "page number to start at"), + arg_str0("d", "data", "", "bytes to write, whole pages (multiple of 4 bytes)"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int blk = arg_get_int_def(ctx, 1, 0); + + uint8_t data[MFU_MAX_BYTES] = {0x00}; + int datalen = 0; + int res = CLIParamHexToBuf(arg_get_str(ctx, 2), data, sizeof(data), &datalen); + CLIParserFree(ctx); + if (res) { + PrintAndLogEx(FAILED, "Error parsing bytes"); + return PM3_EINVARG; + } + + if (blk < 0) { + PrintAndLogEx(WARNING, "page number must be positive"); + return PM3_EINVARG; + } + + if (datalen == 0 || (datalen % MFU_BLOCK_SIZE) != 0) { + PrintAndLogEx(WARNING, "data must be whole pages (multiples of %d bytes). Got %i", MFU_BLOCK_SIZE, datalen); + return PM3_EINVARG; + } + + int count = datalen / MFU_BLOCK_SIZE; + + // live MFU emulator data region is MFU_MAX_BYTES (pages 0..254); page 255 is not round-trippable + if ((blk + count) * MFU_BLOCK_SIZE > MFU_MAX_BYTES) { + PrintAndLogEx(WARNING, "page range exceeds emulator memory (max page %u)", (MFU_MAX_BYTES / MFU_BLOCK_SIZE) - 1); + return PM3_EINVARG; + } + + // one esetblk is a single CMD_HF_MIFARE_EML_MEMSET; its payload (data + a 4-byte + // header) must fit the command buffer. Larger sets should use `hf mfu eload`. + if (datalen > (int)(PM3_CMD_DATA_SIZE - 4)) { + PrintAndLogEx(WARNING, "too many pages for one command: max %d pages (%d bytes). Use " _YELLOW_("`hf mfu eload`") " for larger sets", + (int)((PM3_CMD_DATA_SIZE - 4) / MFU_BLOCK_SIZE), (int)(PM3_CMD_DATA_SIZE - 4)); + return PM3_EINVARG; + } + + // MFU emulator page data starts after the 56-byte mfu_dump_t prefix, so shift the + // page index by MFU_DUMP_PREFIX_LENGTH/MFU_BLOCK_SIZE (=14), width = MFU_BLOCK_SIZE (4). + res = mf_eml_set_mem_xt(data, blk + (MFU_DUMP_PREFIX_LENGTH / MFU_BLOCK_SIZE), count, MFU_BLOCK_SIZE); + if (res != PM3_SUCCESS) { + PrintAndLogEx(FAILED, "Failed to set emulator memory"); + return res; + } + + PrintAndLogEx(SUCCESS, "Set " _YELLOW_("%d") " page(s) from page " _YELLOW_("%d"), count, blk); + return PM3_SUCCESS; +} + static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, {"list", CmdHF14AMfuList, AlwaysAvailable, "List MIFARE Ultralight / NTAG history"}, @@ -8082,6 +8148,7 @@ static command_t CommandTable[] = { {"eload", CmdHF14AMfUeLoad, IfPm3Iso14443a, "Upload file into emulator memory"}, {"esave", CmdHF14AMfuESave, IfPm3Iso14443a, "Save emulator memory to file"}, {"eview", CmdHF14AMfuEView, IfPm3Iso14443a, "View emulator memory"}, + {"esetblk", CmdHF14AMfUeSetBlk, IfPm3Iso14443a, "Set emulator memory block"}, {"sim", CmdHF14AMfUSim, IfPm3Iso14443a, "Simulate MIFARE Ultralight from emulator memory"}, {"-----------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("magic") " ----------------------------"}, {"setuid", CmdHF14AMfUCSetUid, IfPm3Iso14443a, "Set UID - MAGIC tags only"},