hf mfu sim: add support to inject card replies

This commit is contained in:
Philippe Teuwen
2026-01-19 21:47:49 +01:00
parent 40727695d5
commit faca1550b8
8 changed files with 100 additions and 43 deletions
+1 -1
View File
@@ -246,7 +246,7 @@ void RunMod(void) {
FLAG_SET_UID_IN_DATA(flags, 7);
Dbprintf("Starting simulation, press " _GREEN_("pm3 button") " to stop and go back to search state.");
SimulateIso14443aTag(7, flags, card.uid, 0, NULL, 0, false, false);
SimulateIso14443aTag(7, flags, card.uid, 0);
// Go back to search state if user presses pm3-button
state = STATE_SEARCH;
+6 -6
View File
@@ -89,22 +89,22 @@ void RunMod(void) {
Dbprintf("Starting simulation, press " _GREEN_("pm3 button") " to stop and go back to search state.");
if (card.sak == 0x08 && card.atqa[0] == 0x04 && card.atqa[1] == 0) {
DbpString("Mifare Classic 1k");
SimulateIso14443aTag(1, flags, card.uid, 0, NULL, 0, false, false);
SimulateIso14443aTag(1, flags, card.uid, 0);
} else if (card.sak == 0x08 && card.atqa[0] == 0x44 && card.atqa[1] == 0) {
DbpString("Mifare Classic 4k ");
SimulateIso14443aTag(8, flags, card.uid, 0, NULL, 0, false, false);
SimulateIso14443aTag(8, flags, card.uid, 0);
} else if (card.sak == 0x00 && card.atqa[0] == 0x44 && card.atqa[1] == 0) {
DbpString("Mifare Ultralight");
SimulateIso14443aTag(2, flags, card.uid, 0, NULL, 0, false, false);
SimulateIso14443aTag(2, flags, card.uid, 0);
} else if (card.sak == 0x20 && card.atqa[0] == 0x04 && card.atqa[1] == 0x03) {
DbpString("Mifare DESFire");
SimulateIso14443aTag(3, flags, card.uid, 0, NULL, 0, false, false);
SimulateIso14443aTag(3, flags, card.uid, 0);
} else if (card.sak == 0x20 && card.atqa[0] == 0x44 && card.atqa[1] == 0x03) {
DbpString("Mifare DESFire Ev1/Plus/JCOP");
SimulateIso14443aTag(3, flags, card.uid, 0, NULL, 0, false, false);
SimulateIso14443aTag(3, flags, card.uid, 0);
} else {
Dbprintf("Unrecognized tag type -- defaulting to Mifare Classic emulation");
SimulateIso14443aTag(1, flags, card.uid, 0, NULL, 0, false, false);
SimulateIso14443aTag(1, flags, card.uid, 0);
}
// Go back to search state if user presses pm3-button
+7 -7
View File
@@ -253,25 +253,25 @@ void RunMod(void) {
if (uids[selected].sak == 0x08 && uids[selected].atqa[0] == 0x04 && uids[selected].atqa[1] == 0) {
DbpString("Mifare Classic 1k");
SimulateIso14443aTag(1, flags, data, 0, NULL, 0, false, false);
SimulateIso14443aTag(1, flags, data, 0);
} else if (uids[selected].sak == 0x18 && uids[selected].atqa[0] == 0x02 && uids[selected].atqa[1] == 0) {
DbpString("Mifare Classic 4k (4b uid)");
SimulateIso14443aTag(8, flags, data, 0, NULL, 0, false, false);
SimulateIso14443aTag(8, flags, data, 0);
} else if (uids[selected].sak == 0x08 && uids[selected].atqa[0] == 0x44 && uids[selected].atqa[1] == 0) {
DbpString("Mifare Classic 4k (7b uid)");
SimulateIso14443aTag(8, flags, data, 0, NULL, 0, false, false);
SimulateIso14443aTag(8, flags, data, 0);
} else if (uids[selected].sak == 0x00 && uids[selected].atqa[0] == 0x44 && uids[selected].atqa[1] == 0) {
DbpString("Mifare Ultralight");
SimulateIso14443aTag(2, flags, data, 0, NULL, 0, false, false);
SimulateIso14443aTag(2, flags, data, 0);
} else if (uids[selected].sak == 0x20 && uids[selected].atqa[0] == 0x04 && uids[selected].atqa[1] == 0x03) {
DbpString("Mifare DESFire");
SimulateIso14443aTag(3, flags, data, 0, NULL, 0, false, false);
SimulateIso14443aTag(3, flags, data, 0);
} else if (uids[selected].sak == 0x20 && uids[selected].atqa[0] == 0x44 && uids[selected].atqa[1] == 0x03) {
DbpString("Mifare DESFire Ev1/Plus/JCOP");
SimulateIso14443aTag(3, flags, data, 0, NULL, 0, false, false);
SimulateIso14443aTag(3, flags, data, 0);
} else {
Dbprintf("Unrecognized tag type -- defaulting to Mifare Classic emulation");
SimulateIso14443aTag(1, flags, data, 0, NULL, 0, false, false);
SimulateIso14443aTag(1, flags, data, 0);
}
} else if (button_pressed == BUTTON_SINGLE_CLICK) {
+8 -4
View File
@@ -1779,13 +1779,17 @@ static void PacketReceived(PacketCommandNG *packet) {
uint8_t uid[10];
uint8_t exitAfter;
uint8_t rats[20];
bool ulauth_z1;
bool ulauth_z2;
uint8_t ulauth_1a1_len;
uint8_t ulauth_1a2_len;
uint8_t ulauth_1a1[16];
uint8_t ulauth_1a2[16];
} PACKED;
struct p *payload = (struct p *) packet->data.asBytes;
SimulateIso14443aTag(payload->tagtype, payload->flags, payload->uid,
SimulateIso14443aTagEx(payload->tagtype, payload->flags, payload->uid,
payload->exitAfter, payload->rats, sizeof(payload->rats),
payload->ulauth_z1, payload->ulauth_z2); // ## Simulate iso14443a tag - pass tag type & UID
payload->ulauth_1a1, payload->ulauth_1a1_len,
payload->ulauth_1a2, payload->ulauth_1a2_len
); // ## Simulate iso14443a tag - pass tag type & UID
break;
}
case CMD_HF_ISO14443A_SIM_AID: {
+15 -12
View File
@@ -1621,9 +1621,14 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data,
// response to send, and send it.
// 'hf 14a sim'
//-----------------------------------------------------------------------------
void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *useruid, uint8_t exitAfterNReads,
uint8_t *ats, size_t ats_len, bool ulauth_z1, bool ulauth_z2) {
void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *useruid, uint8_t exitAfterNReads) {
SimulateIso14443aTagEx(tagType, flags, useruid, exitAfterNReads, NULL, 0, NULL, 0, NULL, 0);
}
void SimulateIso14443aTagEx(uint8_t tagType, uint16_t flags, uint8_t *useruid, uint8_t exitAfterNReads,
uint8_t *ats, size_t ats_len,
uint8_t *ulauth_1a1, uint8_t ulauth_1a1_len,
uint8_t *ulauth_1a2, uint8_t ulauth_1a2_len) {
#define ATTACK_KEY_COUNT 16
#define ULC_TAG_NONCE "\x01\x02\x03\x04\x05\x06\x07\x08"
@@ -2065,8 +2070,8 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *useruid, uin
// our very random TAG NONCE
memcpy(dynamic_response_info.response + 1, ULC_TAG_NONCE, 8);
if (ulauth_z1) {
memset(dynamic_response_info.response + 1, 0, 8);
if (ulauth_1a1_len == 8 && ulauth_1a1 != NULL) {
memcpy(dynamic_response_info.response + 1, ulauth_1a1, ulauth_1a1_len);
} else {
// encrypt TAG NONCE
tdes_nxp_send(dynamic_response_info.response + 1, dynamic_response_info.response + 1, 8, ulc_key, ulc_iv, 2);
@@ -2104,9 +2109,8 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *useruid, uin
// OK response
dynamic_response_info.response[0] = 0x00;
if (ulauth_z2) {
// try empty auth but with correct CRC and 0x00 command
memset(dynamic_response_info.response + 1, 0, 8);
if (ulauth_1a2_len == 8 && ulauth_1a2 != NULL) {
memcpy(dynamic_response_info.response + 1, ulauth_1a2, ulauth_1a2_len);
} else {
// rol RndA
rol(rnd_ab, 8);
@@ -2139,8 +2143,8 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *useruid, uin
memcpy(dynamic_response_info.response + 1, ULC_TAG_NONCE, 8);
memcpy(dynamic_response_info.response + 9, ULC_TAG_NONCE, 8);
if (ulauth_z1) {
memset(dynamic_response_info.response + 1, 0, 16);
if (ulauth_1a1_len == 16 && ulauth_1a1 != NULL) {
memcpy(dynamic_response_info.response + 1, ulauth_1a1, ulauth_1a1_len);
} else {
// encrypt TAG NONCE
aes128_nxp_send(dynamic_response_info.response + 1, dynamic_response_info.response + 1, 16, ulc_key, ulc_iv);
@@ -2180,9 +2184,8 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *useruid, uin
// OK response
dynamic_response_info.response[0] = 0x00;
if (ulauth_z2) {
// try empty auth but with correct CRC and 0x00 command
memset(dynamic_response_info.response + 1, 0, 16);
if (ulauth_1a2_len == 16 && ulauth_1a2 != NULL) {
memcpy(dynamic_response_info.response + 1, ulauth_1a2, ulauth_1a2_len);
} else {
// rol RndA
rol(rnd_ab, 16);
+6 -2
View File
@@ -142,8 +142,12 @@ RAMFUNC bool MillerDecoding(uint8_t bit, uint32_t non_real_time);
RAMFUNC int ManchesterDecoding(uint8_t bit, uint16_t offset, uint32_t non_real_time);
void RAMFUNC SniffIso14443a(uint8_t param);
void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *useruid, uint8_t exitAfterNReads,
uint8_t *ats, size_t ats_len, bool ulauth_z1, bool ulauth_z2);
void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *useruid, uint8_t exitAfterNReads);
void SimulateIso14443aTagEx(uint8_t tagType, uint16_t flags, uint8_t *useruid, uint8_t exitAfterNReads,
uint8_t *ats, size_t ats_len,
uint8_t *ulauth_1a1, uint8_t ulauth_1a1_len,
uint8_t *ulauth_1a2, uint8_t ulauth_1a2_len);
void SimulateIso14443aTagAID(uint8_t tagType, uint16_t flags, uint8_t *uid,
uint8_t *ats, size_t ats_len, uint8_t *aid, size_t aid_len,
+55 -9
View File
@@ -921,8 +921,8 @@ int CmdHF14ASim(const char *Cmd) {
arg_lit0("x", NULL, "Performs the 'reader attack', nr/ar attack against a reader"),
arg_lit0(NULL, "sk", "Fill simulator keys from found keys"),
arg_lit0("v", "verbose", "verbose output"),
arg_lit0(NULL, "z1", "ULC/ULAES Auth - all zero handshake part 1"),
arg_lit0(NULL, "z2", "ULC/ULAES Auth - all zero handshake part 2"),
arg_str0(NULL, "1a1", "<hex>", "<8|16> hex bytes ULC/ULAES Auth reply step1: ek(RndB)"),
arg_str0(NULL, "1a2", "<hex>", "<8|16> hex bytes ULC/ULAES Auth reply step2: ek(RndA')"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, false);
@@ -956,11 +956,53 @@ int CmdHF14ASim(const char *Cmd) {
bool setEmulatorMem = arg_get_lit(ctx, 5);
bool verbose = arg_get_lit(ctx, 6);
bool ulauth_z1 = arg_get_lit(ctx, 7);
bool ulauth_z2 = arg_get_lit(ctx, 8);
int ulauth_1a1_len = 0;
int ulauth_1a2_len = 0;
uint8_t ulauth_1a1[16] = {0};
uint8_t ulauth_1a2[16] = {0};
CLIGetHexWithReturn(ctx, 7, ulauth_1a1, &ulauth_1a1_len);
CLIGetHexWithReturn(ctx, 8, ulauth_1a2, &ulauth_1a2_len);
CLIParserFree(ctx);
if (ulauth_1a1_len > 0) {
switch (tagtype) {
case 13: // ULC
if (ulauth_1a1_len != 8) {
PrintAndLogEx(ERR, "ULC --1a1 length must be 8 bytes");
return PM3_EINVARG;
}
break;
case 14: // ULAES
if (ulauth_1a1_len != 16) {
PrintAndLogEx(ERR, "ULAES --1a1 length must be 16 bytes");
return PM3_EINVARG;
}
break;
default:
PrintAndLogEx(ERR, "--1a1 option is only valid for tag types 13 (ULC) and 14 (ULAES)");
return PM3_EINVARG;
}
}
if (ulauth_1a2_len > 0) {
switch (tagtype) {
case 13: // ULC
if (ulauth_1a2_len != 8) {
PrintAndLogEx(ERR, "ULC --1a2 length must be 8 bytes");
return PM3_EINVARG;
}
break;
case 14: // ULAES
if (ulauth_1a2_len != 16) {
PrintAndLogEx(ERR, "ULAES --1a2 length must be 16 bytes");
return PM3_EINVARG;
}
break;
default:
PrintAndLogEx(ERR, "--1a2 option is only valid for tag types 13 (ULC) and 14 (ULAES)");
return PM3_EINVARG;
}
}
if (tagtype > 14) {
PrintAndLogEx(ERR, "Undefined tag %d", tagtype);
return PM3_EINVARG;
@@ -976,16 +1018,20 @@ int CmdHF14ASim(const char *Cmd) {
uint8_t uid[10];
uint8_t exitAfter;
uint8_t rats[20];
bool ulauth_z1;
bool ulauth_z2;
uint8_t ulauth_1a1_len;
uint8_t ulauth_1a2_len;
uint8_t ulauth_1a1[16];
uint8_t ulauth_1a2[16];
} PACKED payload;
payload.tagtype = tagtype;
payload.flags = flags;
payload.exitAfter = exitAfterNReads;
payload.ulauth_z1 = ulauth_z1;
payload.ulauth_z2 = ulauth_z2;
payload.ulauth_1a1_len = ulauth_1a1_len;
payload.ulauth_1a2_len = ulauth_1a2_len;
memcpy(payload.uid, uid, uid_len);
memcpy(payload.ulauth_1a1, ulauth_1a1, ulauth_1a1_len);
memcpy(payload.ulauth_1a2, ulauth_1a2, ulauth_1a2_len);
clearCommandBuffer();
SendCommandNG(CMD_HF_ISO14443A_SIMULATE, (uint8_t *)&payload, sizeof(payload));
+2 -2
View File
@@ -4125,8 +4125,8 @@ static int CmdHF14AMfUSim(const char *Cmd) {
arg_str0("u", "uid", "<hex>", "<4|7|10> hex bytes UID"),
arg_int0("n", "num", "<dec>", "Exit simulation after <numreads> blocks. 0 = infinite"),
arg_lit0("v", "verbose", "Verbose output"),
arg_lit0(NULL, "z1", "ULC/ULAES Auth - all zero handshake part 1"),
arg_lit0(NULL, "z2", "ULC/ULAES Auth - all zero handshake part 2"),
arg_str0(NULL, "1a1", "<hex>", "<8|16> hex bytes ULC/ULAES Auth reply step1: ek(RndB)"),
arg_str0(NULL, "1a2", "<hex>", "<8|16> hex bytes ULC/ULAES Auth reply step2: ek(RndA')"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, false);