mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2025-08-21 05:43:48 -07:00
Merge pull request #2726 from n-hutton/emv_sim_cleanup_squash
Cleanup PR to emv contactless to contact bridge
This commit is contained in:
commit
dc5d456db5
7 changed files with 356 additions and 603 deletions
734
armsrc/emvsim.c
734
armsrc/emvsim.c
|
@ -27,19 +27,13 @@
|
||||||
// /!\ Printing Debug message is disrupting emulation,
|
// /!\ Printing Debug message is disrupting emulation,
|
||||||
// Only use with caution during debugging
|
// Only use with caution during debugging
|
||||||
|
|
||||||
// These are the old flags which have changed in master since this fork was created.
|
// indices into responses array copied from mifare sim init:
|
||||||
// Just a temp fix and not intended to go into master
|
#define ATQA 0
|
||||||
#define FLAG_4B_UID_IN_DATA_OLD 0x02
|
#define SAK 1
|
||||||
#define FLAG_7B_UID_IN_DATA_OLD 0x04
|
#define SAKuid 2
|
||||||
#define FLAG_10B_UID_IN_DATA_OLD 0x08
|
#define UIDBCC1 3
|
||||||
#define FLAG_UID_IN_EMUL_OLD 0x10
|
#define UIDBCC2 8
|
||||||
#define FLAG_MF_MINI_OLD 0x80
|
#define UIDBCC3 13
|
||||||
#define FLAG_MF_1K_OLD 0x100
|
|
||||||
#define FLAG_MF_2K_OLD 0x200
|
|
||||||
#define FLAG_MF_4K_OLD 0x400
|
|
||||||
#define FLAG_FORCED_ATQA 0x800
|
|
||||||
#define FLAG_FORCED_SAK 0x1000
|
|
||||||
#define FLAG_CVE21_0430_OLD 0x2000
|
|
||||||
|
|
||||||
#include "emvsim.h"
|
#include "emvsim.h"
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
|
@ -48,6 +42,7 @@
|
||||||
#include "BigBuf.h"
|
#include "BigBuf.h"
|
||||||
#include "string.h"
|
#include "string.h"
|
||||||
#include "mifareutil.h"
|
#include "mifareutil.h"
|
||||||
|
#include "mifaresim.h"
|
||||||
#include "fpgaloader.h"
|
#include "fpgaloader.h"
|
||||||
#include "proxmark3_arm.h"
|
#include "proxmark3_arm.h"
|
||||||
#include "protocols.h"
|
#include "protocols.h"
|
||||||
|
@ -57,322 +52,263 @@
|
||||||
#include "ticks.h"
|
#include "ticks.h"
|
||||||
#include "i2c_direct.h"
|
#include "i2c_direct.h"
|
||||||
|
|
||||||
#pragma GCC diagnostic ignored "-Wunused-variable"
|
// Hardcoded response to the reader for file not found, plus the checksum
|
||||||
#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
|
|
||||||
#pragma GCC diagnostic ignored "-Wunused-function"
|
|
||||||
|
|
||||||
static uint8_t filenotfound[] = {0x02, 0x6a, 0x82, 0x93, 0x2f};
|
static uint8_t filenotfound[] = {0x02, 0x6a, 0x82, 0x93, 0x2f};
|
||||||
|
|
||||||
// query and response that inserts PDOL so as to continue process...
|
// TLV response for PPSE directory request
|
||||||
static uint8_t fci_query[] = {0x02, 0x00, 0xa4, 0x04, 0x00, 0x07, 0xa0, 0x00, 0x00, 0x00, 0x03, 0x10, 0x10, 0x00, 0x56, 0x3f};
|
static uint8_t pay1_response[] = { 0x6F, 0x1E, 0x84, 0x0E };
|
||||||
static uint8_t fci_template[] = {0x02, 0x6f, 0x5e, 0x84, 0x07, 0xa0, 0x00, 0x00, 0x00, 0x03, 0x10, 0x10, 0xa5, 0x53, 0x50, 0x0a, 0x56, 0x69, 0x73, 0x61, 0x20, 0x44, 0x65, 0x62, 0x69, 0x74, 0x9f, 0x38, 0x18, 0x9f, 0x66, 0x04, 0x9f, 0x02, 0x06, 0x9f, 0x03, 0x06, 0x9f, 0x1a, 0x02, 0x95, 0x05, 0x5f, 0x2a, 0x02, 0x9a, 0x03, 0x9c, 0x01, 0x9f, 0x37, 0x04, 0x5f, 0x2d, 0x02, 0x65, 0x6e, 0x9f, 0x11, 0x01, 0x01, 0x9f, 0x12, 0x0a, 0x56, 0x69, 0x73, 0x61, 0x20, 0x44, 0x65, 0x62, 0x69, 0x74, 0xbf, 0x0c, 0x13, 0x9f, 0x5a, 0x05, 0x31, 0x08, 0x26, 0x08, 0x26, 0x9f, 0x0a, 0x08, 0x00, 0x01, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0x90, 0x00, 0xd8, 0x15};
|
|
||||||
|
|
||||||
static uint8_t pay1_response[] = { 0x6F, 0x1E, 0x84, 0x0E, 0x31, 0x50, 0x41, 0x59 };
|
// The WTX we want to send out... The format:
|
||||||
|
// 0xf2 is the command
|
||||||
|
// 0x0e is the time to wait (currently at max)
|
||||||
|
// The remaining bytes are CRC, precalculated for speed
|
||||||
|
static uint8_t extend_resp[] = {0xf2, 0x0e, 0x66, 0xb8};
|
||||||
|
|
||||||
|
|
||||||
|
// For reference, we have here the pay1 template we receive from the card, and the pay2 template we send back to the reader
|
||||||
|
// These can be inspected at https://emvlab.org/tlvutils/
|
||||||
|
// Note that the pay2 template is coded for visa ps in the UK - other countries may have different templates. Refer:
|
||||||
|
// https://mstcompany.net/blog/acquiring-emv-transaction-flow-part-3-get-processing-options-with-and-without-pdol
|
||||||
|
// Specifically, 9F5A: Application Program Identifier: 3108260826 might have to become 31 0840 0840 for USA for example.
|
||||||
|
// todo: see if this can be read from the card and automatically populated rather than hard coded
|
||||||
|
//static uint8_t fci_template_pay1[] = {0xff, 0x6f, 0x3b, 0x84, 0x07, 0xa0, 0x00, 0x00, 0x00, 0x03, 0x10, 0x10, 0xa5, 0x30, 0x50, 0x0a, 0x56, 0x69, 0x73, 0x61, 0x20, 0x44, 0x65, 0x62, 0x69, 0x74, 0x5f, 0x2d, 0x02, 0x65, 0x6e, 0x9f, 0x12, 0x0a, 0x56, 0x69, 0x73, 0x61, 0x20, 0x44, 0x65, 0x62, 0x69, 0x74, 0x9f, 0x11, 0x01, 0x01, 0xbf, 0x0c, 0x0b, 0x9f, 0x0a, 0x08, 0x00, 0x01, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0x90, 0x00, 0x17, 0x48};
|
||||||
|
static uint8_t fci_template_pay2[] = {0x02, 0x6f, 0x5e, 0x84, 0x07, 0xa0, 0x00, 0x00, 0x00, 0x03, 0x10, 0x10, 0xa5, 0x53, 0x50, 0x0a, 0x56, 0x69, 0x73, 0x61, 0x20, 0x44, 0x65, 0x62, 0x69, 0x74, 0x9f, 0x38, 0x18, 0x9f, 0x66, 0x04, 0x9f, 0x02, 0x06, 0x9f, 0x03, 0x06, 0x9f, 0x1a, 0x02, 0x95, 0x05, 0x5f, 0x2a, 0x02, 0x9a, 0x03, 0x9c, 0x01, 0x9f, 0x37, 0x04, 0x5f, 0x2d, 0x02, 0x65, 0x6e, 0x9f, 0x11, 0x01, 0x01, 0x9f, 0x12, 0x0a, 0x56, 0x69, 0x73, 0x61, 0x20, 0x44, 0x65, 0x62, 0x69, 0x74, 0xbf, 0x0c, 0x13, 0x9f, 0x5a, 0x05, 0x31, 0x08, 0x26, 0x08, 0x26, 0x9f, 0x0a, 0x08, 0x00, 0x01, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0x90, 0x00, 0xd8, 0x15};
|
||||||
|
|
||||||
|
// This is the hardcoded response that a contactless card would respond with when asked to select PPSE.
|
||||||
|
// It is a TLV structure, and can be seen here:
|
||||||
|
// https://emvlab.org/tlvutils/?data=6f3e840e325041592e5359532e4444463031a52cbf0c2961274f07a0000000031010500a566973612044656269749f0a080001050100000000bf6304df200180
|
||||||
|
// The first byte is the class byte, and the payload is followed by 0x9000, which is the success code, and the CRC (precalculated)
|
||||||
static uint8_t pay2_response[] = { 0x03, 0x6f, 0x3e, 0x84, 0x0e, 0x32, 0x50, 0x41, 0x59, 0x2e, 0x53, 0x59, 0x53, 0x2e, 0x44, 0x44, 0x46, 0x30, 0x31, 0xa5, 0x2c, 0xbf, 0x0c, 0x29, 0x61, 0x27, 0x4f, 0x07, 0xa0, 0x00, 0x00, 0x00, 0x03, 0x10, 0x10, 0x50, 0x0a, 0x56, 0x69, 0x73, 0x61, 0x20, 0x44, 0x65, 0x62, 0x69, 0x74, 0x9f, 0x0a, 0x08, 0x00, 0x01, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0xbf, 0x63, 0x04, 0xdf, 0x20, 0x01, 0x80, 0x90, 0x00, 0x07, 0x9d};
|
static uint8_t pay2_response[] = { 0x03, 0x6f, 0x3e, 0x84, 0x0e, 0x32, 0x50, 0x41, 0x59, 0x2e, 0x53, 0x59, 0x53, 0x2e, 0x44, 0x44, 0x46, 0x30, 0x31, 0xa5, 0x2c, 0xbf, 0x0c, 0x29, 0x61, 0x27, 0x4f, 0x07, 0xa0, 0x00, 0x00, 0x00, 0x03, 0x10, 0x10, 0x50, 0x0a, 0x56, 0x69, 0x73, 0x61, 0x20, 0x44, 0x65, 0x62, 0x69, 0x74, 0x9f, 0x0a, 0x08, 0x00, 0x01, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0xbf, 0x63, 0x04, 0xdf, 0x20, 0x01, 0x80, 0x90, 0x00, 0x07, 0x9d};
|
||||||
|
|
||||||
static bool MifareSimInit(uint16_t flags, uint8_t *datain, uint16_t atqa, uint8_t sak, tag_response_info_t **responses, uint32_t *cuid, uint8_t *uid_len, uint8_t **rats, uint8_t *rats_len) {
|
void ExecuteEMVSim(uint8_t *receivedCmd, uint16_t receivedCmd_len, uint8_t *receivedCmd_copy, uint16_t receivedCmd_len_copy);
|
||||||
|
|
||||||
// SPEC: https://www.nxp.com/docs/en/application-note/AN10833.pdf
|
typedef enum {
|
||||||
// ATQA
|
STATE_DEFAULT,
|
||||||
static uint8_t rATQA_Mini[] = {0x04, 0x00}; // indicate Mifare classic Mini 4Byte UID
|
SELECT_PAY1,
|
||||||
static uint8_t rATQA_1k[] = {0x04, 0x00}; // indicate Mifare classic 1k 4Byte UID
|
SELECT_PAY1_AID,
|
||||||
static uint8_t rATQA_2k[] = {0x04, 0x00}; // indicate Mifare classic 2k 4Byte UID
|
REQUESTING_CARD_PDOL,
|
||||||
static uint8_t rATQA_4k[] = {0x02, 0x00}; // indicate Mifare classic 4k 4Byte UID
|
GENERATE_AC,
|
||||||
|
} SystemState;
|
||||||
|
|
||||||
// SAK
|
static SystemState currentState = STATE_DEFAULT;
|
||||||
static uint8_t rSAK_Mini = 0x09; // mifare Mini
|
|
||||||
static uint8_t rSAK_1k = 0x08; // mifare 1k
|
|
||||||
static uint8_t rSAK_2k = 0x08; // mifare 2k with RATS support
|
|
||||||
static uint8_t rSAK_4k = 0x18; // mifare 4k
|
|
||||||
|
|
||||||
static uint8_t rUIDBCC1[] = {0x00, 0x00, 0x00, 0x00, 0x00}; // UID 1st cascade level
|
// This is the main entry point for the EMV attack, everything before this has just been setup/handshaking.
|
||||||
static uint8_t rUIDBCC1b4[] = {0x00, 0x00, 0x00, 0x00}; // UID 1st cascade level, last 4 bytes
|
// In order to meet the timing requirements, as soon as the proxmark sees a command it immediately
|
||||||
static uint8_t rUIDBCC1b3[] = {0x00, 0x00, 0x00}; // UID 1st cascade level, last 3 bytes
|
// caches the command to process and responds with a WTX
|
||||||
static uint8_t rUIDBCC1b2[] = {0x00, 0x00}; // UID 1st cascade level, last 2 bytes
|
// (waiting time extension). When it get the response to this WTX, it can process the cached command through the I2C interface.
|
||||||
static uint8_t rUIDBCC1b1[] = {0x00}; // UID 1st cascade level, last byte
|
//
|
||||||
static uint8_t rUIDBCC2[] = {0x00, 0x00, 0x00, 0x00, 0x00}; // UID 2nd cascade level
|
// The full flow is:
|
||||||
static uint8_t rUIDBCC2b4[] = {0x00, 0x00, 0x00, 0x00}; // UID 2st cascade level, last 4 bytes
|
// 1. Handshake with RATS
|
||||||
static uint8_t rUIDBCC2b3[] = {0x00, 0x00, 0x00}; // UID 2st cascade level, last 3 bytes
|
// 2. Reader attempts to find out which payment environment the proxmark supports (may start with SELECT OSE for example)
|
||||||
static uint8_t rUIDBCC2b2[] = {0x00, 0x00}; // UID 2st cascade level, last 2 bytes
|
// 3. Reader eventually makes a request for the PAY2 application (select PPSE) (contactless payment)
|
||||||
static uint8_t rUIDBCC2b1[] = {0x00}; // UID 2st cascade level, last byte
|
// 4. We read the PAY1 environment and transform it into PAY2 to respond
|
||||||
static uint8_t rUIDBCC3[] = {0x00, 0x00, 0x00, 0x00, 0x00}; // UID 3nd cascade level
|
// 5. Reader will select AID we responded in step 4
|
||||||
static uint8_t rUIDBCC3b4[] = {0x00, 0x00, 0x00, 0x00}; // UID 3st cascade level, last 4 bytes
|
// 6. We get the response from selecting the PAY1 AID and transform it into PAY2 response (fci template)
|
||||||
static uint8_t rUIDBCC3b3[] = {0x00, 0x00, 0x00}; // UID 3st cascade level, last 3 bytes
|
// - This is important as it contains the PDOL (processing data object list) which specifies the data which is
|
||||||
static uint8_t rUIDBCC3b2[] = {0x00, 0x00}; // UID 3st cascade level, last 2 bytes
|
// signed by the card and sent to the reader to verify the transaction.
|
||||||
static uint8_t rUIDBCC3b1[] = {0x00}; // UID 3st cascade level, last byte
|
// 7. The reader will then issue 'get processing options' which seems to be used here to provide the fields to be signed
|
||||||
|
// as specified by the PDOL.
|
||||||
|
// 8. In contactless flow, GPO should at least return the Application Interchange Profile (AIP) and
|
||||||
|
// Application File Locator (AFL). However, here we return track 2 data, the cryptogram, everything. This completes the transaction.
|
||||||
|
// 9. To construct this final response, behind the scenes we need to interact with the card to make it think its completing a contact transaction:
|
||||||
|
// - Request PDOL to prime the card (response not used)
|
||||||
|
// - Rearrange the GPO data provided into a 'generate AC' command for the card
|
||||||
|
// - Extract the cryptogram, track 2 data and anything else required
|
||||||
|
// - Respond. Transaction is complete
|
||||||
|
void ExecuteEMVSim(uint8_t *receivedCmd, uint16_t receivedCmd_len, uint8_t *receivedCmd_copy, uint16_t receivedCmd_len_copy) {
|
||||||
|
uint8_t responseToReader[MAX_FRAME_SIZE] = {0x00};
|
||||||
|
uint16_t responseToReader_len;
|
||||||
|
|
||||||
static uint8_t rATQA[] = {0x00, 0x00}; // Current ATQA
|
// special print me
|
||||||
static uint8_t rSAK[] = {0x00, 0x00, 0x00}; // Current SAK, CRC
|
Dbprintf("\nrecvd from reader:");
|
||||||
static uint8_t rSAKuid[] = {0x04, 0xda, 0x17}; // UID incomplete cascade bit, CRC
|
Dbhexdump(receivedCmd_len, receivedCmd, false);
|
||||||
|
Dbprintf("");
|
||||||
|
|
||||||
// RATS answer for 2K NXP mifare classic (with CRC)
|
// use annotate to give some hints about the command
|
||||||
static uint8_t rRATS[] = {0x0c, 0x75, 0x77, 0x80, 0x02, 0xc1, 0x05, 0x2f, 0x2f, 0x01, 0xbc, 0xd6, 0x60, 0xd3};
|
annotate(&receivedCmd[1], receivedCmd_len-1);
|
||||||
|
|
||||||
*uid_len = 0;
|
// This is a common request from the reader which we can just immediately respond to since we know we can't
|
||||||
|
// handle it.
|
||||||
|
if (receivedCmd[6] == 'O' && receivedCmd[7] == 'S' && receivedCmd[8] == 'E') {
|
||||||
|
Dbprintf("We saw OSE... ignore it!");
|
||||||
|
EmSendCmd(filenotfound, sizeof(filenotfound));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// By default use 1K tag
|
// We want to modify corrupted request
|
||||||
memcpy(rATQA, rATQA_1k, sizeof(rATQA));
|
if ((receivedCmd_len > 5 && receivedCmd[0] != 0x03 && receivedCmd[0] != 0x02 && receivedCmd[1] == 0 && receivedCmd[4] == 0) || (receivedCmd[2] == 0xa8)) {
|
||||||
rSAK[0] = rSAK_1k;
|
Dbprintf("We saw signing request... modifying it into a generate ac transaction !!!!");
|
||||||
|
|
||||||
//by default RATS not supported
|
currentState = GENERATE_AC;
|
||||||
*rats_len = 0;
|
|
||||||
*rats = NULL;
|
|
||||||
|
|
||||||
// -- Determine the UID
|
memcpy(receivedCmd, (unsigned char[]){ 0x03, 0x80, 0xae, 0x80, 0x00, 0x1d }, 6);
|
||||||
// Can be set from emulator memory or incoming data
|
|
||||||
// Length: 4,7,or 10 bytes
|
|
||||||
|
|
||||||
// Get UID, SAK, ATQA from EMUL
|
for (int i = 0; i < 29; i++) {
|
||||||
if ((flags & FLAG_UID_IN_EMUL_OLD) == FLAG_UID_IN_EMUL_OLD) {
|
receivedCmd[6 + i] = receivedCmd[12 + i];
|
||||||
uint8_t block0[16];
|
}
|
||||||
emlGet(block0, 0, 16);
|
|
||||||
|
|
||||||
// If uid size defined, copy only uid from EMUL to use, backward compatibility for 'hf_colin.c', 'hf_mattyrun.c'
|
// clear final byte just in case
|
||||||
if ((flags & (FLAG_4B_UID_IN_DATA_OLD | FLAG_7B_UID_IN_DATA_OLD | FLAG_10B_UID_IN_DATA_OLD)) != 0) {
|
receivedCmd[35] = 0;
|
||||||
memcpy(datain, block0, 10); // load 10bytes from EMUL to the datain pointer. to be used below.
|
|
||||||
} else {
|
receivedCmd_len = 35 + 3; // Core command is 35, then there is control code and the crc
|
||||||
// Check for 4 bytes uid: bcc corrected and single size uid bits in ATQA
|
|
||||||
if ((block0[0] ^ block0[1] ^ block0[2] ^ block0[3]) == block0[4] && (block0[6] & 0xc0) == 0) {
|
Dbprintf("\nthe command has now become:");
|
||||||
flags |= FLAG_4B_UID_IN_DATA_OLD;
|
Dbhexdump(receivedCmd_len, receivedCmd, false);
|
||||||
memcpy(datain, block0, 4);
|
}
|
||||||
rSAK[0] = block0[5];
|
|
||||||
memcpy(rATQA, &block0[6], sizeof(rATQA));
|
// Seems unlikely
|
||||||
|
if (receivedCmd_len >= 9 && receivedCmd[6] == '1' && receivedCmd[7] == 'P' && receivedCmd[8] == 'A') {
|
||||||
|
Dbprintf("We saw 1PA... !!!!");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Request more time for 2PAY and respond with a modified 1PAY request. We literally just change the 2 to a 1.
|
||||||
|
if (receivedCmd_len >= 9 && receivedCmd[6] == '2' && receivedCmd[7] == 'P' && receivedCmd[8] == 'A') {
|
||||||
|
Dbprintf("We saw 2PA... switching it to 1PAY !!!!");
|
||||||
|
receivedCmd[6] = '1';
|
||||||
|
currentState = SELECT_PAY1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We are selecting a short AID - assume it is pay2 aid
|
||||||
|
if (receivedCmd[2] == 0xA4 && receivedCmd[5] == 0x07) {
|
||||||
|
Dbprintf("Selecting pay2 AID");
|
||||||
|
currentState = SELECT_PAY1_AID;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t rnd_resp[] = {0xb2, 0x67, 0xc7};
|
||||||
|
if (memcmp(receivedCmd, rnd_resp, sizeof(rnd_resp)) == 0) {
|
||||||
|
Dbprintf("We saw bad response... !");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We have received the response from a WTX command! Process the cached command at this point.
|
||||||
|
if (memcmp(receivedCmd, extend_resp, sizeof(extend_resp)) == 0) {
|
||||||
|
// Special case: if we are about to do a generate AC, we also need to
|
||||||
|
// make a request for pdol first (and discard response)...
|
||||||
|
if (receivedCmd_copy[1] == 0x80 && receivedCmd_copy[2] == 0xae) {
|
||||||
|
Dbprintf("We are about to do a generate AC... we need to request PDOL first...");
|
||||||
|
uint8_t pdol_request[] = { 0x80, 0xa8, 0x00, 0x00, 0x02, 0x83, 0x00 };
|
||||||
|
|
||||||
|
currentState = REQUESTING_CARD_PDOL;
|
||||||
|
CmdSmartRaw(0xff, &(pdol_request[0]), sizeof(pdol_request), (&responseToReader[0]), &responseToReader_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send the cached command to the card via ISO7816
|
||||||
|
// This is minus 3 because we don't include the first byte (prepend), plus we don't want to send the
|
||||||
|
// last two bytes (CRC) to the card.
|
||||||
|
// On the return, the first class byte must be the same, so it's preserved in responseToReader
|
||||||
|
CmdSmartRaw(receivedCmd_copy[0], &(receivedCmd_copy[1]), receivedCmd_len_copy - 3, (&responseToReader[0]), &responseToReader_len);
|
||||||
|
|
||||||
|
// Print the unadultered response we got from the card here
|
||||||
|
Dbprintf("The response from the card is ==> :");
|
||||||
|
Dbhexdump(responseToReader_len, responseToReader, false);
|
||||||
|
|
||||||
|
// We have passed the reader's query to the card, but before we return it, we need to check if we need to modify
|
||||||
|
// the response to 'pretend' to be a PAY2 environment.
|
||||||
|
// This is always the same response for VISA, the only currently supported card
|
||||||
|
if (currentState == SELECT_PAY1) {
|
||||||
|
Dbprintf("We saw a PAY1 response... modifying it to a PAY2 response !!!!");
|
||||||
|
|
||||||
|
if (!memcmp(&responseToReader[1], &pay1_response[0], sizeof(pay1_response)) == 0) {
|
||||||
|
Dbprintf("The response from the card is not a PAY1 response. This is unexpected and probably fatal.");
|
||||||
}
|
}
|
||||||
// Check for 7 bytes UID: double size uid bits in ATQA
|
|
||||||
else if ((block0[8] & 0xc0) == 0x40) {
|
if (pay2_response[0] != responseToReader[0]) {
|
||||||
flags |= FLAG_7B_UID_IN_DATA_OLD;
|
Dbprintf("The first byte of the PAY2 response is different from the request. This is unexpected and probably fatal.");
|
||||||
memcpy(datain, block0, 7);
|
|
||||||
rSAK[0] = block0[7];
|
|
||||||
memcpy(rATQA, &block0[8], sizeof(rATQA));
|
|
||||||
} else {
|
|
||||||
Dbprintf("ERROR: " _RED_("Invalid dump. UID/SAK/ATQA not found"));
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
memcpy(responseToReader, &pay2_response[0], sizeof(pay2_response));
|
||||||
|
responseToReader_len = sizeof(pay2_response);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
if (responseToReader[0] != 0xff && responseToReader[1] == 0x77 && true) {
|
||||||
|
Dbprintf("we have detected a generate ac response, lets repackage it!!");
|
||||||
|
Dbhexdump(responseToReader_len, responseToReader, false); // special print
|
||||||
|
|
||||||
// Tune tag type, if defined directly
|
// 11 and 12 are trans counter.
|
||||||
// Otherwise use defined by default or extracted from EMUL
|
// 16 to 24 are the cryptogram
|
||||||
if ((flags & FLAG_MF_MINI_OLD) == FLAG_MF_MINI_OLD) {
|
// 27 to 34 is issuer application data
|
||||||
memcpy(rATQA, rATQA_Mini, sizeof(rATQA));
|
Dbprintf("atc: %d %d, cryptogram: %d ", responseToReader[11], responseToReader[12], responseToReader[13]);
|
||||||
rSAK[0] = rSAK_Mini;
|
|
||||||
if (g_dbglevel > DBG_NONE) Dbprintf("Enforcing Mifare Mini ATQA/SAK");
|
|
||||||
} else if ((flags & FLAG_MF_1K_OLD) == FLAG_MF_1K_OLD) {
|
|
||||||
memcpy(rATQA, rATQA_1k, sizeof(rATQA));
|
|
||||||
rSAK[0] = rSAK_1k;
|
|
||||||
if (g_dbglevel > DBG_NONE) Dbprintf("Enforcing Mifare 1K ATQA/SAK (!!!!)");
|
|
||||||
} else if ((flags & FLAG_MF_2K_OLD) == FLAG_MF_2K_OLD) {
|
|
||||||
memcpy(rATQA, rATQA_2k, sizeof(rATQA));
|
|
||||||
rSAK[0] = rSAK_2k;
|
|
||||||
*rats = rRATS;
|
|
||||||
*rats_len = sizeof(rRATS);
|
|
||||||
if (g_dbglevel > DBG_NONE) Dbprintf("Enforcing Mifare 2K ATQA/SAK with RATS support");
|
|
||||||
} else if ((flags & FLAG_MF_4K_OLD) == FLAG_MF_4K_OLD) {
|
|
||||||
memcpy(rATQA, rATQA_4k, sizeof(rATQA));
|
|
||||||
rSAK[0] = rSAK_4k;
|
|
||||||
if (g_dbglevel > DBG_NONE) Dbprintf("Enforcing Mifare 4K ATQA/SAK");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prepare UID arrays
|
// then, on the template:
|
||||||
if ((flags & FLAG_4B_UID_IN_DATA_OLD) == FLAG_4B_UID_IN_DATA_OLD) { // get UID from datain
|
// 60 and 61 for counter
|
||||||
memcpy(rUIDBCC1, datain, 4);
|
// 45 to 53 for cryptogram
|
||||||
*uid_len = 4;
|
// 35 to 42 for issuer application data
|
||||||
if (g_dbglevel >= DBG_EXTENDED)
|
uint8_t template[] = { 0x00, 0x77, 0x47, 0x82, 0x02, 0x39, 0x00, 0x57, 0x13, 0x47,
|
||||||
Dbprintf("MifareSimInit - FLAG_4B_UID_IN_DATA_OLD => Get UID from datain: %02X - Flag: %02X - UIDBCC1: %02X", FLAG_4B_UID_IN_DATA_OLD, flags, rUIDBCC1);
|
0x62, 0x28, 0x00, 0x05, 0x93, 0x38, 0x64, 0xd2, 0x70, 0x92,
|
||||||
|
0x01, 0x00, 0x00, 0x01, 0x42, 0x00, 0x00, 0x0f, 0x5f, 0x34,
|
||||||
|
0x01, 0x00, 0x9f, 0x10, 0x07, 0x06, 0x01, 0x12, 0x03, 0xa0,
|
||||||
|
0x20, 0x00, 0x9f, 0x26, 0x08, 0x56, 0xcb, 0x4e, 0xe1, 0xa4,
|
||||||
|
0xef, 0xac, 0x74, 0x9f, 0x27, 0x01, 0x80, 0x9f, 0x36, 0x02,
|
||||||
|
0x00, 0x07, 0x9f, 0x6c, 0x02, 0x3e, 0x00, 0x9f, 0x6e, 0x04,
|
||||||
|
0x20, 0x70, 0x00, 0x00, 0x90, 0x00, 0xff, 0xff};
|
||||||
|
|
||||||
// save CUID
|
// do the replacement
|
||||||
*cuid = bytes_to_num(rUIDBCC1, 4);
|
template[0] = responseToReader[0]; // class bit 0
|
||||||
// BCC
|
|
||||||
rUIDBCC1[4] = rUIDBCC1[0] ^ rUIDBCC1[1] ^ rUIDBCC1[2] ^ rUIDBCC1[3];
|
template[60] = responseToReader[10];
|
||||||
if (g_dbglevel > DBG_NONE) {
|
template[61] = responseToReader[11];
|
||||||
Dbprintf("4B UID: %02x%02x%02x%02x", rUIDBCC1[0], rUIDBCC1[1], rUIDBCC1[2], rUIDBCC1[3]);
|
|
||||||
|
// Copy responseToReader[15..23] to template[45..53]
|
||||||
|
for (int i = 0; i <= 8; i++) {
|
||||||
|
template[45 + i] = responseToReader[15 + i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy responseToReader[26..32] to template[35..41]
|
||||||
|
for (int i = 0; i <= 6; i++) {
|
||||||
|
template[35 + i] = responseToReader[26 + i];
|
||||||
|
}
|
||||||
|
|
||||||
|
Dbprintf("\nrearranged is: ");
|
||||||
|
responseToReader_len = sizeof(template);
|
||||||
|
|
||||||
|
// We DO NOT add the CRC here, this way we can avoid a million penny payments!
|
||||||
|
// The CRC is calculated here, but doesn't include the class bit at the beginning, plus
|
||||||
|
// also obvisously doesn't include the CRC bytes itself.
|
||||||
|
AddCrc14A(&template[0], responseToReader_len - 2);
|
||||||
|
|
||||||
|
responseToReader_len = sizeof(template);
|
||||||
|
memcpy(responseToReader, &template[0], responseToReader_len);
|
||||||
|
|
||||||
|
Dbprintf("\nafter crc rearranged is: ");
|
||||||
|
Dbhexdump(responseToReader_len, &responseToReader[0], false); // special print
|
||||||
}
|
}
|
||||||
|
|
||||||
// Correct uid size bits in ATQA
|
// If we would return a PAY1 fci response, we instead return a PAY2 fci response
|
||||||
rATQA[0] = (rATQA[0] & 0x3f) | 0x00; // single size uid
|
if (currentState == SELECT_PAY1_AID) {
|
||||||
|
Dbprintf("We saw a PAY1 response... modifying it to a PAY2 response for outgoing !!!!");
|
||||||
} else if ((flags & FLAG_7B_UID_IN_DATA_OLD) == FLAG_7B_UID_IN_DATA_OLD) {
|
memcpy(responseToReader, fci_template_pay2, sizeof(fci_template_pay2));
|
||||||
memcpy(&rUIDBCC1[1], datain, 3);
|
responseToReader_len = sizeof(fci_template_pay2);
|
||||||
memcpy(rUIDBCC2, datain + 3, 4);
|
|
||||||
*uid_len = 7;
|
|
||||||
if (g_dbglevel >= DBG_EXTENDED)
|
|
||||||
Dbprintf("MifareSimInit - FLAG_7B_UID_IN_DATA_OLD => Get UID from datain: %02X - Flag: %02X - UIDBCC1: %02X", FLAG_7B_UID_IN_DATA_OLD, flags, rUIDBCC1);
|
|
||||||
|
|
||||||
// save CUID
|
|
||||||
*cuid = bytes_to_num(rUIDBCC2, 4);
|
|
||||||
// CascadeTag, CT
|
|
||||||
rUIDBCC1[0] = MIFARE_SELECT_CT;
|
|
||||||
// BCC
|
|
||||||
rUIDBCC1[4] = rUIDBCC1[0] ^ rUIDBCC1[1] ^ rUIDBCC1[2] ^ rUIDBCC1[3];
|
|
||||||
rUIDBCC2[4] = rUIDBCC2[0] ^ rUIDBCC2[1] ^ rUIDBCC2[2] ^ rUIDBCC2[3];
|
|
||||||
if (g_dbglevel > DBG_NONE) {
|
|
||||||
Dbprintf("7B UID: %02x %02x %02x %02x %02x %02x %02x",
|
|
||||||
rUIDBCC1[1], rUIDBCC1[2], rUIDBCC1[3], rUIDBCC2[0], rUIDBCC2[1], rUIDBCC2[2], rUIDBCC2[3]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Correct uid size bits in ATQA
|
EmSendCmd(responseToReader, responseToReader_len);
|
||||||
rATQA[0] = (rATQA[0] & 0x3f) | 0x40; // double size uid
|
|
||||||
|
|
||||||
} else if ((flags & FLAG_10B_UID_IN_DATA_OLD) == FLAG_10B_UID_IN_DATA_OLD) {
|
return;
|
||||||
memcpy(&rUIDBCC1[1], datain, 3);
|
|
||||||
memcpy(&rUIDBCC2[1], datain + 3, 3);
|
|
||||||
memcpy(rUIDBCC3, datain + 6, 4);
|
|
||||||
*uid_len = 10;
|
|
||||||
if (g_dbglevel >= DBG_EXTENDED)
|
|
||||||
Dbprintf("MifareSimInit - FLAG_10B_UID_IN_DATA_OLD => Get UID from datain: %02X - Flag: %02X - UIDBCC1: %02X", FLAG_10B_UID_IN_DATA_OLD, flags, rUIDBCC1);
|
|
||||||
|
|
||||||
// save CUID
|
|
||||||
*cuid = bytes_to_num(rUIDBCC3, 4);
|
|
||||||
// CascadeTag, CT
|
|
||||||
rUIDBCC1[0] = MIFARE_SELECT_CT;
|
|
||||||
rUIDBCC2[0] = MIFARE_SELECT_CT;
|
|
||||||
// BCC
|
|
||||||
rUIDBCC1[4] = rUIDBCC1[0] ^ rUIDBCC1[1] ^ rUIDBCC1[2] ^ rUIDBCC1[3];
|
|
||||||
rUIDBCC2[4] = rUIDBCC2[0] ^ rUIDBCC2[1] ^ rUIDBCC2[2] ^ rUIDBCC2[3];
|
|
||||||
rUIDBCC3[4] = rUIDBCC3[0] ^ rUIDBCC3[1] ^ rUIDBCC3[2] ^ rUIDBCC3[3];
|
|
||||||
|
|
||||||
if (g_dbglevel > DBG_NONE) {
|
|
||||||
Dbprintf("10B UID: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x",
|
|
||||||
rUIDBCC1[1], rUIDBCC1[2], rUIDBCC1[3],
|
|
||||||
rUIDBCC2[1], rUIDBCC2[2], rUIDBCC2[3],
|
|
||||||
rUIDBCC3[0], rUIDBCC3[1], rUIDBCC3[2], rUIDBCC3[3]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Correct uid size bits in ATQA
|
|
||||||
rATQA[0] = (rATQA[0] & 0x3f) | 0x80; // triple size uid
|
|
||||||
} else {
|
|
||||||
Dbprintf("ERROR: " _RED_("UID size not defined"));
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flags & FLAG_FORCED_ATQA) {
|
// Send a request for more time, and cache the command we want to process
|
||||||
rATQA[0] = atqa >> 8;
|
EmSendCmd(extend_resp, 4);
|
||||||
rATQA[1] = atqa & 0xff;
|
|
||||||
}
|
|
||||||
if (flags & FLAG_FORCED_SAK) {
|
|
||||||
rSAK[0] = sak;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (g_dbglevel > DBG_NONE) {
|
|
||||||
Dbprintf("ATQA : %02X %02X", rATQA[1], rATQA[0]);
|
|
||||||
Dbprintf("SAK : %02X", rSAK[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// clone UIDs for byte-frame anti-collision multiple tag selection procedure
|
|
||||||
memcpy(rUIDBCC1b4, &rUIDBCC1[1], 4);
|
|
||||||
memcpy(rUIDBCC1b3, &rUIDBCC1[2], 3);
|
|
||||||
memcpy(rUIDBCC1b2, &rUIDBCC1[3], 2);
|
|
||||||
memcpy(rUIDBCC1b1, &rUIDBCC1[4], 1);
|
|
||||||
if (*uid_len >= 7) {
|
|
||||||
memcpy(rUIDBCC2b4, &rUIDBCC2[1], 4);
|
|
||||||
memcpy(rUIDBCC2b3, &rUIDBCC2[2], 3);
|
|
||||||
memcpy(rUIDBCC2b2, &rUIDBCC2[3], 2);
|
|
||||||
memcpy(rUIDBCC2b1, &rUIDBCC2[4], 1);
|
|
||||||
}
|
|
||||||
if (*uid_len == 10) {
|
|
||||||
memcpy(rUIDBCC3b4, &rUIDBCC3[1], 4);
|
|
||||||
memcpy(rUIDBCC3b3, &rUIDBCC3[2], 3);
|
|
||||||
memcpy(rUIDBCC3b2, &rUIDBCC3[3], 2);
|
|
||||||
memcpy(rUIDBCC3b1, &rUIDBCC3[4], 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate actual CRC
|
|
||||||
AddCrc14A(rSAK, sizeof(rSAK) - 2);
|
|
||||||
|
|
||||||
#define TAG_RESPONSE_COUNT 18
|
|
||||||
static tag_response_info_t responses_init[TAG_RESPONSE_COUNT] = {
|
|
||||||
{ .response = rATQA, .response_n = sizeof(rATQA) }, // Answer to request - respond with card type
|
|
||||||
{ .response = rSAK, .response_n = sizeof(rSAK) }, //
|
|
||||||
{ .response = rSAKuid, .response_n = sizeof(rSAKuid) }, //
|
|
||||||
// Do not reorder. Block used via relative index of rUIDBCC1
|
|
||||||
{ .response = rUIDBCC1, .response_n = sizeof(rUIDBCC1) }, // Anticollision cascade1 - respond with first part of uid
|
|
||||||
{ .response = rUIDBCC1b4, .response_n = sizeof(rUIDBCC1b4)},
|
|
||||||
{ .response = rUIDBCC1b3, .response_n = sizeof(rUIDBCC1b3)},
|
|
||||||
{ .response = rUIDBCC1b2, .response_n = sizeof(rUIDBCC1b2)},
|
|
||||||
{ .response = rUIDBCC1b1, .response_n = sizeof(rUIDBCC1b1)},
|
|
||||||
// Do not reorder. Block used via relative index of rUIDBCC2
|
|
||||||
{ .response = rUIDBCC2, .response_n = sizeof(rUIDBCC2) }, // Anticollision cascade2 - respond with 2nd part of uid
|
|
||||||
{ .response = rUIDBCC2b4, .response_n = sizeof(rUIDBCC2b4)},
|
|
||||||
{ .response = rUIDBCC2b3, .response_n = sizeof(rUIDBCC2b3)},
|
|
||||||
{ .response = rUIDBCC2b2, .response_n = sizeof(rUIDBCC2b2)},
|
|
||||||
{ .response = rUIDBCC2b1, .response_n = sizeof(rUIDBCC2b1)},
|
|
||||||
// Do not reorder. Block used via relative index of rUIDBCC3
|
|
||||||
{ .response = rUIDBCC3, .response_n = sizeof(rUIDBCC3) }, // Anticollision cascade3 - respond with 3th part of uid
|
|
||||||
{ .response = rUIDBCC3b4, .response_n = sizeof(rUIDBCC3b4)},
|
|
||||||
{ .response = rUIDBCC3b3, .response_n = sizeof(rUIDBCC3b3)},
|
|
||||||
{ .response = rUIDBCC3b2, .response_n = sizeof(rUIDBCC3b2)},
|
|
||||||
{ .response = rUIDBCC3b1, .response_n = sizeof(rUIDBCC3b1)}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Prepare ("precompile") the responses of the anticollision phase.
|
|
||||||
// There will be not enough time to do this at the moment the reader sends its REQA or SELECT
|
|
||||||
// There are 18 predefined responses with a total of 53 bytes data to transmit.
|
|
||||||
// Coded responses need one byte per bit to transfer (data, parity, start, stop, correction)
|
|
||||||
// 53 * 8 data bits, 53 * 1 parity bits, 18 start bits, 18 stop bits, 18 correction bits -> need 571 bytes buffer
|
|
||||||
#define ALLOCATED_TAG_MODULATION_BUFFER_SIZE 571
|
|
||||||
|
|
||||||
uint8_t *free_buffer = BigBuf_malloc(ALLOCATED_TAG_MODULATION_BUFFER_SIZE);
|
|
||||||
// modulation buffer pointer and current buffer free space size
|
|
||||||
uint8_t *free_buffer_pointer = free_buffer;
|
|
||||||
size_t free_buffer_size = ALLOCATED_TAG_MODULATION_BUFFER_SIZE;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < TAG_RESPONSE_COUNT; i++) {
|
|
||||||
if (prepare_allocated_tag_modulation(&responses_init[i], &free_buffer_pointer, &free_buffer_size) == false) {
|
|
||||||
Dbprintf("Not enough modulation buffer size, exit after %d elements", i);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
*responses = responses_init;
|
|
||||||
|
|
||||||
// indices into responses array:
|
|
||||||
#define ATQA 0
|
|
||||||
#define SAK 1
|
|
||||||
#define SAKuid 2
|
|
||||||
#define UIDBCC1 3
|
|
||||||
#define UIDBCC2 8
|
|
||||||
#define UIDBCC3 13
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*xxxxxxxxxxxxxxxxxx.
|
* EMVsim - simulate an EMV contactless card transaction by
|
||||||
*
|
*
|
||||||
*@param flags :
|
*@param flags: See pm3_cmd.h for the full definitions
|
||||||
*@param exitAfterNReads, exit simulation after n blocks have been read, 0 is infinite ...
|
*@param exitAfterNReads, exit simulation after n transactions (default 1)
|
||||||
|
*@param uid, UID - must be length 7
|
||||||
|
*@param atqa, override for ATQA, flags indicate if this is used
|
||||||
|
*@param sak, override for SAK, flags indicate if this is used
|
||||||
* (unless reader attack mode enabled then it runs util it gets enough nonces to recover all keys attmpted)
|
* (unless reader attack mode enabled then it runs util it gets enough nonces to recover all keys attmpted)
|
||||||
*/
|
*/
|
||||||
void EMVsim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain, uint16_t atqa, uint8_t sak) {
|
void EMVsim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *uid, uint16_t atqa, uint8_t sak) {
|
||||||
|
|
||||||
tag_response_info_t *responses;
|
tag_response_info_t *responses;
|
||||||
uint8_t cardSTATE = MFEMUL_NOFIELD;
|
uint8_t cardSTATE = MFEMUL_NOFIELD;
|
||||||
uint8_t uid_len = 0; // 4, 7, 10
|
uint8_t uid_len = 0; // 7
|
||||||
uint32_t cuid = 0, authTimer = 0;
|
uint32_t cuid = 0;
|
||||||
uint32_t nr, ar;
|
|
||||||
|
|
||||||
uint8_t cardWRBL = 0;
|
|
||||||
uint8_t cardAUTHSC = 0;
|
|
||||||
uint8_t cardAUTHKEY = AUTHKEYNONE; // no authentication
|
|
||||||
uint32_t cardRr = 0;
|
|
||||||
uint32_t ans = 0;
|
|
||||||
uint32_t cardINTREG = 0;
|
|
||||||
uint8_t cardINTBLOCK = 0;
|
|
||||||
|
|
||||||
struct Crypto1State mpcs = {0, 0};
|
|
||||||
struct Crypto1State *pcs;
|
|
||||||
pcs = &mpcs;
|
|
||||||
|
|
||||||
//uint32_t numReads = 0; //Counts numer of times reader reads a block
|
|
||||||
uint8_t receivedCmd[MAX_FRAME_SIZE] = {0x00};
|
uint8_t receivedCmd[MAX_FRAME_SIZE] = {0x00};
|
||||||
uint8_t receivedCmd_copy[MAX_FRAME_SIZE] = {0x00};
|
uint8_t receivedCmd_copy[MAX_FRAME_SIZE] = {0x00};
|
||||||
uint8_t receivedCmd_dec[MAX_FRAME_SIZE] = {0x00};
|
|
||||||
//uint8_t convenient_buffer[64] = {0x00};
|
|
||||||
uint8_t receivedCmd_par[MAX_MIFARE_PARITY_SIZE] = {0x00};
|
uint8_t receivedCmd_par[MAX_MIFARE_PARITY_SIZE] = {0x00};
|
||||||
uint8_t responseToReader[MAX_FRAME_SIZE] = {0x00};
|
|
||||||
uint16_t responseToReader_len;
|
|
||||||
uint16_t receivedCmd_len;
|
uint16_t receivedCmd_len;
|
||||||
uint16_t receivedCmd_len_copy = 0;
|
uint16_t receivedCmd_len_copy = 0;
|
||||||
|
|
||||||
|
@ -383,34 +319,28 @@ void EMVsim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain, uint16_t a
|
||||||
uint8_t *rats = NULL;
|
uint8_t *rats = NULL;
|
||||||
uint8_t rats_len = 0;
|
uint8_t rats_len = 0;
|
||||||
|
|
||||||
// if fct is called with NULL we need to assign some memory since this pointer is passaed around
|
// if fct is called with NULL we need to assign some memory since this pointer is passed around
|
||||||
uint8_t datain_tmp[10] = {0};
|
uint8_t uid_tmp[10] = {0};
|
||||||
if (datain == NULL) {
|
if (uid == NULL) {
|
||||||
datain = datain_tmp;
|
uid = uid_tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Here, we collect UID,sector,keytype,NT,AR,NR,NT2,AR2,NR2
|
|
||||||
// This will be used in the reader-only attack.
|
|
||||||
|
|
||||||
//allow collecting up to 7 sets of nonces to allow recovery of up to 7 keys
|
|
||||||
#define ATTACK_KEY_COUNT 7 // keep same as define in cmdhfmf.c -> readerAttack() (Cannot be more than 7)
|
|
||||||
nonces_t ar_nr_resp[ATTACK_KEY_COUNT * 2]; // *2 for 2 separate attack types (nml, moebius) 36 * 7 * 2 bytes = 504 bytes
|
|
||||||
memset(ar_nr_resp, 0x00, sizeof(ar_nr_resp));
|
|
||||||
|
|
||||||
uint8_t ar_nr_collected[ATTACK_KEY_COUNT * 2]; // *2 for 2nd attack type (moebius)
|
|
||||||
memset(ar_nr_collected, 0x00, sizeof(ar_nr_collected));
|
|
||||||
bool gettingMoebius = false;
|
|
||||||
|
|
||||||
const tUart14a *uart = GetUart14a();
|
const tUart14a *uart = GetUart14a();
|
||||||
|
|
||||||
// free eventually allocated BigBuf memory but keep Emulator Memory
|
// free eventually allocated BigBuf memory but keep Emulator Memory
|
||||||
BigBuf_free_keep_EM();
|
BigBuf_free_keep_EM();
|
||||||
|
|
||||||
if (MifareSimInit(flags, datain, atqa, sak, &responses, &cuid, &uid_len, &rats, &rats_len) == false) {
|
// Print all arguments going into mifare sim init
|
||||||
|
Dbprintf("EMVsim: flags: %04x, uid: %p, atqa: %04x, sak: %02x", flags, uid, atqa, sak);
|
||||||
|
|
||||||
|
if (MifareSimInit(flags, uid, atqa, sak, &responses, &cuid, &uid_len, &rats, &rats_len) == false) {
|
||||||
BigBuf_free_keep_EM();
|
BigBuf_free_keep_EM();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Print all the outputs after the sim init
|
||||||
|
Dbprintf("EMVsim: cuid: %08x, uid_len: %d, rats: %p, rats_len: %d", cuid, uid_len, rats, rats_len);
|
||||||
|
|
||||||
// We need to listen to the high-frequency, peak-detected path.
|
// We need to listen to the high-frequency, peak-detected path.
|
||||||
iso14443a_setup(FPGA_HF_ISO14443A_TAGSIM_LISTEN);
|
iso14443a_setup(FPGA_HF_ISO14443A_TAGSIM_LISTEN);
|
||||||
|
|
||||||
|
@ -420,8 +350,6 @@ void EMVsim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain, uint16_t a
|
||||||
LED_D_ON();
|
LED_D_ON();
|
||||||
ResetSspClk();
|
ResetSspClk();
|
||||||
|
|
||||||
uint8_t *p_em = BigBuf_get_EM_addr();
|
|
||||||
|
|
||||||
int counter = 0;
|
int counter = 0;
|
||||||
bool finished = false;
|
bool finished = false;
|
||||||
bool button_pushed = BUTTON_PRESS();
|
bool button_pushed = BUTTON_PRESS();
|
||||||
|
@ -445,9 +373,6 @@ void EMVsim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain, uint16_t a
|
||||||
int res = EmGetCmd(receivedCmd, sizeof(receivedCmd), &receivedCmd_len, receivedCmd_par);
|
int res = EmGetCmd(receivedCmd, sizeof(receivedCmd), &receivedCmd_len, receivedCmd_par);
|
||||||
|
|
||||||
if (res == 2) { //Field is off!
|
if (res == 2) { //Field is off!
|
||||||
if ((flags & FLAG_CVE21_0430_OLD) == FLAG_CVE21_0430_OLD) {
|
|
||||||
p_em[1] = 0x21;
|
|
||||||
}
|
|
||||||
LEDsoff();
|
LEDsoff();
|
||||||
if (cardSTATE != MFEMUL_NOFIELD) {
|
if (cardSTATE != MFEMUL_NOFIELD) {
|
||||||
Dbprintf("cardSTATE = MFEMUL_NOFIELD");
|
Dbprintf("cardSTATE = MFEMUL_NOFIELD");
|
||||||
|
@ -600,17 +525,17 @@ void EMVsim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain, uint16_t a
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(receivedCmd_dec, receivedCmd, receivedCmd_len);
|
|
||||||
|
|
||||||
// all commands must have a valid CRC
|
// all commands must have a valid CRC
|
||||||
if (!CheckCrc14A(receivedCmd_dec, receivedCmd_len)) {
|
if (!CheckCrc14A(receivedCmd, receivedCmd_len)) {
|
||||||
if (g_dbglevel >= DBG_EXTENDED) Dbprintf("[MFEMUL_WORK] All commands must have a valid CRC %02X (%d)", receivedCmd_dec, receivedCmd_len);
|
if (g_dbglevel >= DBG_EXTENDED)
|
||||||
|
Dbprintf("[MFEMUL_WORK] All commands must have a valid CRC %02X (%d)", receivedCmd,
|
||||||
|
receivedCmd_len);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// rule 13 of 7.5.3. in ISO 14443-4. chaining shall be continued
|
// rule 13 of 7.5.3. in ISO 14443-4. chaining shall be continued
|
||||||
// BUT... ACK --> NACK
|
// BUT... ACK --> NACK
|
||||||
if (receivedCmd_len == 1 && receivedCmd_dec[0] == CARD_ACK) {
|
if (receivedCmd_len == 1 && receivedCmd[0] == CARD_ACK) {
|
||||||
Dbprintf("[MFEMUL_WORK] ACK --> NACK !!");
|
Dbprintf("[MFEMUL_WORK] ACK --> NACK !!");
|
||||||
EmSend4bit(CARD_NACK_NA);
|
EmSend4bit(CARD_NACK_NA);
|
||||||
FpgaDisableTracing();
|
FpgaDisableTracing();
|
||||||
|
@ -618,7 +543,7 @@ void EMVsim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain, uint16_t a
|
||||||
}
|
}
|
||||||
|
|
||||||
// rule 12 of 7.5.3. in ISO 14443-4. R(NAK) --> R(ACK)
|
// rule 12 of 7.5.3. in ISO 14443-4. R(NAK) --> R(ACK)
|
||||||
if (receivedCmd_len == 1 && receivedCmd_dec[0] == CARD_NACK_NA) {
|
if (receivedCmd_len == 1 && receivedCmd[0] == CARD_NACK_NA) {
|
||||||
Dbprintf("[MFEMUL_WORK] NACK --> NACK !!");
|
Dbprintf("[MFEMUL_WORK] NACK --> NACK !!");
|
||||||
EmSend4bit(CARD_ACK);
|
EmSend4bit(CARD_ACK);
|
||||||
FpgaDisableTracing();
|
FpgaDisableTracing();
|
||||||
|
@ -626,7 +551,7 @@ void EMVsim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain, uint16_t a
|
||||||
}
|
}
|
||||||
|
|
||||||
// case MFEMUL_WORK => CMD RATS
|
// case MFEMUL_WORK => CMD RATS
|
||||||
if (receivedCmd_len == 4 && receivedCmd_dec[0] == ISO14443A_CMD_RATS && receivedCmd_dec[1] == 0x80) {
|
if (receivedCmd_len == 4 && receivedCmd[0] == ISO14443A_CMD_RATS && receivedCmd[1] == 0x80) {
|
||||||
if (rats && rats_len) {
|
if (rats && rats_len) {
|
||||||
EmSendCmd(rats, rats_len);
|
EmSendCmd(rats, rats_len);
|
||||||
FpgaDisableTracing();
|
FpgaDisableTracing();
|
||||||
|
@ -641,9 +566,9 @@ void EMVsim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain, uint16_t a
|
||||||
}
|
}
|
||||||
|
|
||||||
// case MFEMUL_WORK => ISO14443A_CMD_NXP_DESELECT
|
// case MFEMUL_WORK => ISO14443A_CMD_NXP_DESELECT
|
||||||
if (receivedCmd_len == 3 && receivedCmd_dec[0] == ISO14443A_CMD_NXP_DESELECT) {
|
if (receivedCmd_len == 3 && receivedCmd[0] == ISO14443A_CMD_NXP_DESELECT) {
|
||||||
if (rats && rats_len) {
|
if (rats && rats_len) {
|
||||||
EmSendCmd(receivedCmd_dec, receivedCmd_len);
|
EmSendCmd(receivedCmd, receivedCmd_len);
|
||||||
|
|
||||||
FpgaDisableTracing();
|
FpgaDisableTracing();
|
||||||
if (g_dbglevel >= DBG_EXTENDED)
|
if (g_dbglevel >= DBG_EXTENDED)
|
||||||
|
@ -658,111 +583,12 @@ void EMVsim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain, uint16_t a
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The WTX we want to send out...
|
|
||||||
//static uint8_t extend_resp[] = {0xf2, 0x01, 0x91, 0x40};
|
|
||||||
//static uint8_t extend_resp[] = {0xf2, 0x02, 0x0a, 0x72};
|
|
||||||
//static uint8_t extend_resp[] = {0xf2, 0x03, 0x83, 0x63};
|
|
||||||
//static uint8_t extend_resp[] = {0xf2, 0x04, 0x3c, 0x17};
|
|
||||||
//static uint8_t extend_resp[] = {0xf2, 0x05, 0x50, 0x6b};
|
|
||||||
//static uint8_t extend_resp[] = {0xf2, 0x06, 0x2e, 0x34};
|
|
||||||
//static uint8_t extend_resp[] = {0xf2, 0x07, 0xa7, 0x25};
|
|
||||||
//static uint8_t extend_resp[] = {0xf2, 0x08, 0x50, 0xdd}; // This works
|
|
||||||
//static uint8_t extend_resp[] = {0xf2, 0x09, 0xd9, 0xcc};
|
|
||||||
//static uint8_t extend_resp[] = {0xf2, 0x0a, 0x42, 0xfe};
|
|
||||||
//static uint8_t extend_resp[] = {0xf2, 0x0b, 0xcb, 0xef};
|
|
||||||
//static uint8_t extend_resp[] = {0xf2, 0x0c, 0x74, 0x9b};
|
|
||||||
//static uint8_t extend_resp[] = {0xf2, 0x0d, 0xfd, 0x8a};
|
|
||||||
static uint8_t extend_resp[] = {0xf2, 0x0e, 0x66, 0xb8};
|
|
||||||
|
|
||||||
// special print me
|
// From this point onwards is where the 'magic' happens
|
||||||
Dbprintf("\nrecvd from reader:");
|
ExecuteEMVSim(receivedCmd, receivedCmd_len, receivedCmd_copy, receivedCmd_len_copy);
|
||||||
Dbhexdump(receivedCmd_len, receivedCmd, false);
|
|
||||||
Dbprintf("");
|
|
||||||
|
|
||||||
// lets handle some obvious stuff here!
|
// We want to keep a copy of the command we just saw, because we will process it once we get the
|
||||||
if (receivedCmd[6] == 'O' && receivedCmd[7] == 'S' && receivedCmd[8] == 'E') {
|
// WTX response
|
||||||
Dbprintf("We saw OSE... ignore it!");
|
|
||||||
//Full: 02 6a 82 93 2f
|
|
||||||
EmSendCmd(filenotfound, 5);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// rather than asing for more time, lets just send the response with the PDOL there too
|
|
||||||
// there are two of this for some reason?? Ach, this one is not at the card read level, that is why.
|
|
||||||
if (memcmp(&fci_query[0], receivedCmd, sizeof(fci_query)) == 0 && false) {
|
|
||||||
Dbprintf("***** returning fast FCI response...!");
|
|
||||||
//uint8_t modified_response[] = { 0x03, 0x77, 0x0e, 0x82, 0x02, 0x39, 0x80, 0x94, 0x08, 0x18, 0x01, 0x02, 0x01, 0x20, 0x01, 0x04, 0x00, 0x90, 0x00, 0x03, 0xec };
|
|
||||||
//uint8_t modified_response[] = { 0x03, 0x77, 0x0e, 0x82, 0x02, 0x39, 0x80, 0x94, 0x08, 0x18, 0x01, 0x02, 0x01, 0x20, 0x01, 0x04, 0x00, 0x90, 0x00, 0x03, 0xec };
|
|
||||||
EmSendCmd(&fci_template[0], sizeof(fci_template));
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We want to modify corrupted request
|
|
||||||
if ((receivedCmd_len > 5 && receivedCmd[0] != 0x03 && receivedCmd[0] != 0x02 && receivedCmd[1] == 0 && receivedCmd[4] == 0) || (receivedCmd[2] == 0xa8)) {
|
|
||||||
//if (receivedCmd[2] == 0xa8) {
|
|
||||||
Dbprintf("We saw signing request... modifying it into a generate ac transaction !!!!");
|
|
||||||
receivedCmd[0] = 0x03;
|
|
||||||
receivedCmd[1] = 0x80;
|
|
||||||
receivedCmd[2] = 0xae;
|
|
||||||
receivedCmd[3] = 0x80;
|
|
||||||
receivedCmd[4] = 0x00;
|
|
||||||
receivedCmd[5] = 0x1d;
|
|
||||||
|
|
||||||
for (int i = 0; i < 29; i++) {
|
|
||||||
receivedCmd[6 + i] = receivedCmd[12 + i];
|
|
||||||
}
|
|
||||||
|
|
||||||
// clear final byte just in case
|
|
||||||
receivedCmd[35] = 0;
|
|
||||||
|
|
||||||
receivedCmd_len = 35 + 3; // Core command is 35, then there is control code and hte crc
|
|
||||||
|
|
||||||
Dbprintf("\nthe command has now become:");
|
|
||||||
Dbhexdump(receivedCmd_len, receivedCmd, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Seems unlikely
|
|
||||||
if (receivedCmd_len >= 9 && receivedCmd[6] == '1' && receivedCmd[7] == 'P' && receivedCmd[8] == 'A') {
|
|
||||||
Dbprintf("We saw 1PA... !!!!");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Request more time for 2PAY and respond with a modified 1PAY request
|
|
||||||
if (receivedCmd_len >= 9 && receivedCmd[6] == '2' && receivedCmd[7] == 'P' && receivedCmd[8] == 'A') {
|
|
||||||
Dbprintf("We saw 2PA... switching it to 1PAY !!!!");
|
|
||||||
receivedCmd[6] = '1';
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint8_t rnd_resp[] = {0xb2, 0x67, 0xc7};
|
|
||||||
if (memcmp(receivedCmd, rnd_resp, sizeof(rnd_resp)) == 0) {
|
|
||||||
Dbprintf("We saw bad response... !");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We have received the response from a WTX command! Process the cached command at this point.
|
|
||||||
if (memcmp(receivedCmd, extend_resp, sizeof(extend_resp)) == 0) {
|
|
||||||
// Special case: if we are about to do a generate AC, we also need to
|
|
||||||
// make a request for pdol...
|
|
||||||
if (receivedCmd_copy[1] == 0x80 && receivedCmd_copy[2] == 0xae) {
|
|
||||||
Dbprintf("We are about to do a generate AC... we need to request PDOL first...");
|
|
||||||
uint8_t pdol_request[] = { 0x80, 0xa8, 0x00, 0x00, 0x02, 0x83, 0x00 };
|
|
||||||
|
|
||||||
CmdSmartRaw(0xff, &(pdol_request[0]), sizeof(pdol_request), (&responseToReader[0]), &responseToReader_len);
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is minus 3 because we don't include the first byte (prepend), plus we don't want to send the
|
|
||||||
// last two bytes (CRC) to the card
|
|
||||||
CmdSmartRaw(receivedCmd_copy[0], &(receivedCmd_copy[1]), receivedCmd_len_copy - 3, (&responseToReader[0]), &responseToReader_len);
|
|
||||||
EmSendCmd(responseToReader, responseToReader_len);
|
|
||||||
|
|
||||||
Dbprintf("Sent delayed command to card...");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send a request for more time, and cache the command we want to process
|
|
||||||
EmSendCmd(extend_resp, 4);
|
|
||||||
|
|
||||||
// copy the command and its length (minus 1???)
|
|
||||||
Dbprintf("Caching command for later processing... its length is %d", receivedCmd_len);
|
Dbprintf("Caching command for later processing... its length is %d", receivedCmd_len);
|
||||||
memcpy(receivedCmd_copy, receivedCmd, receivedCmd_len);
|
memcpy(receivedCmd_copy, receivedCmd, receivedCmd_len);
|
||||||
receivedCmd_len_copy = receivedCmd_len;
|
receivedCmd_len_copy = receivedCmd_len;
|
||||||
|
@ -785,3 +611,71 @@ void EMVsim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain, uint16_t a
|
||||||
set_tracing(false);
|
set_tracing(false);
|
||||||
BigBuf_free_keep_EM();
|
BigBuf_free_keep_EM();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// annotate iso 7816
|
||||||
|
void annotate(uint8_t *cmd, uint8_t cmdsize) {
|
||||||
|
if (cmdsize < 2) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// From https://mvallim.github.io/emv-qrcode/docs/EMV_v4.3_Book_3_Application_Specification_20120607062110791.pdf
|
||||||
|
// section 6.3.2
|
||||||
|
switch (cmd[1]) {
|
||||||
|
case ISO7816_APPLICATION_BLOCK: {
|
||||||
|
Dbprintf("APPLICATION BLOCK");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ISO7816_APPLICATION_UNBLOCK: {
|
||||||
|
Dbprintf("APPLICATION UNBLOCK");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ISO7816_CARD_BLOCK: {
|
||||||
|
Dbprintf("CARD BLOCK");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ISO7816_EXTERNAL_AUTHENTICATION: {
|
||||||
|
Dbprintf("EXTERNAL AUTHENTICATE");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ISO7816_GENERATE_APPLICATION_CRYPTOGRAM: {
|
||||||
|
Dbprintf("GENERATE APPLICATION CRYPTOGRAM");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ISO7816_GET_CHALLENGE: {
|
||||||
|
Dbprintf("GET CHALLENGE");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ISO7816_GET_DATA: {
|
||||||
|
Dbprintf("GET DATA");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ISO7816_GET_PROCESSING_OPTIONS: {
|
||||||
|
Dbprintf("GET PROCESSING OPTIONS");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ISO7816_INTERNAL_AUTHENTICATION: {
|
||||||
|
Dbprintf("INTERNAL AUTHENTICATION");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ISO7816_PIN_CHANGE: {
|
||||||
|
Dbprintf("PIN CHANGE");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ISO7816_READ_RECORDS: {
|
||||||
|
Dbprintf("READ RECORDS");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ISO7816_SELECT_FILE: {
|
||||||
|
Dbprintf("SELECT FILE");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ISO7816_VERIFY: {
|
||||||
|
Dbprintf("VERIFY");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
Dbprintf("NOT RECOGNISED");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -25,5 +25,6 @@
|
||||||
#define AUTHKEYNONE 0xff
|
#define AUTHKEYNONE 0xff
|
||||||
|
|
||||||
void EMVsim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain, uint16_t atqa, uint8_t sak);
|
void EMVsim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain, uint16_t atqa, uint8_t sak);
|
||||||
|
void annotate(uint8_t *cmd, uint8_t cmdsize);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -36,11 +36,6 @@
|
||||||
#include "i2c.h"
|
#include "i2c.h"
|
||||||
#include "i2c_direct.h"
|
#include "i2c_direct.h"
|
||||||
|
|
||||||
static uint8_t fci_template[] = {0x02, 0x6f, 0x5e, 0x84, 0x07, 0xa0, 0x00, 0x00, 0x00, 0x03, 0x10, 0x10, 0xa5, 0x53, 0x50, 0x0a, 0x56, 0x69, 0x73, 0x61, 0x20, 0x44, 0x65, 0x62, 0x69, 0x74, 0x9f, 0x38, 0x18, 0x9f, 0x66, 0x04, 0x9f, 0x02, 0x06, 0x9f, 0x03, 0x06, 0x9f, 0x1a, 0x02, 0x95, 0x05, 0x5f, 0x2a, 0x02, 0x9a, 0x03, 0x9c, 0x01, 0x9f, 0x37, 0x04, 0x5f, 0x2d, 0x02, 0x65, 0x6e, 0x9f, 0x11, 0x01, 0x01, 0x9f, 0x12, 0x0a, 0x56, 0x69, 0x73, 0x61, 0x20, 0x44, 0x65, 0x62, 0x69, 0x74, 0xbf, 0x0c, 0x13, 0x9f, 0x5a, 0x05, 0x31, 0x08, 0x26, 0x08, 0x26, 0x9f, 0x0a, 0x08, 0x00, 0x01, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0x90, 0x00, 0xd8, 0x15};
|
|
||||||
|
|
||||||
static uint8_t pay1_response[] = { 0x6F, 0x1E, 0x84, 0x0E, 0x31, 0x50, 0x41, 0x59 };
|
|
||||||
static uint8_t pay2_response[] = { 0x03, 0x6f, 0x3e, 0x84, 0x0e, 0x32, 0x50, 0x41, 0x59, 0x2e, 0x53, 0x59, 0x53, 0x2e, 0x44, 0x44, 0x46, 0x30, 0x31, 0xa5, 0x2c, 0xbf, 0x0c, 0x29, 0x61, 0x27, 0x4f, 0x07, 0xa0, 0x00, 0x00, 0x00, 0x03, 0x10, 0x10, 0x50, 0x0a, 0x56, 0x69, 0x73, 0x61, 0x20, 0x44, 0x65, 0x62, 0x69, 0x74, 0x9f, 0x0a, 0x08, 0x00, 0x01, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0xbf, 0x63, 0x04, 0xdf, 0x20, 0x01, 0x80, 0x90, 0x00, 0x07, 0x9d};
|
|
||||||
|
|
||||||
static void SmartCardDirectSend(uint8_t prepend, const smart_card_raw_t *p, uint8_t *output, uint16_t *olen) {
|
static void SmartCardDirectSend(uint8_t prepend, const smart_card_raw_t *p, uint8_t *output, uint16_t *olen) {
|
||||||
LED_D_ON();
|
LED_D_ON();
|
||||||
|
|
||||||
|
@ -50,9 +45,6 @@ static void SmartCardDirectSend(uint8_t prepend, const smart_card_raw_t *p, uint
|
||||||
// check if alloacted...
|
// check if alloacted...
|
||||||
smartcard_command_t flags = p->flags;
|
smartcard_command_t flags = p->flags;
|
||||||
|
|
||||||
//if ((flags & SC_CLEARLOG) == SC_CLEARLOG)
|
|
||||||
//clear_trace();
|
|
||||||
|
|
||||||
if ((flags & SC_LOG) == SC_LOG)
|
if ((flags & SC_LOG) == SC_LOG)
|
||||||
set_tracing(true);
|
set_tracing(true);
|
||||||
else
|
else
|
||||||
|
@ -65,10 +57,9 @@ static void SmartCardDirectSend(uint8_t prepend, const smart_card_raw_t *p, uint
|
||||||
if ((flags & SC_SELECT) == SC_SELECT) {
|
if ((flags & SC_SELECT) == SC_SELECT) {
|
||||||
smart_card_atr_t card;
|
smart_card_atr_t card;
|
||||||
bool gotATR = GetATR(&card, true);
|
bool gotATR = GetATR(&card, true);
|
||||||
//reply_old(CMD_ACK, gotATR, sizeof(smart_card_atr_t), 0, &card, sizeof(smart_card_atr_t));
|
|
||||||
if (gotATR == false) {
|
if (gotATR == false) {
|
||||||
Dbprintf("No ATR received...\n");
|
Dbprintf("No ATR received...\n");
|
||||||
//reply_ng(CMD_SMART_RAW, PM3_ESOFT, NULL, 0);
|
|
||||||
goto OUT;
|
goto OUT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -92,8 +83,6 @@ static void SmartCardDirectSend(uint8_t prepend, const smart_card_raw_t *p, uint
|
||||||
);
|
);
|
||||||
|
|
||||||
if (res == false && g_dbglevel > 3) {
|
if (res == false && g_dbglevel > 3) {
|
||||||
//DbpString(I2C_ERROR);
|
|
||||||
//reply_ng(CMD_SMART_RAW, PM3_ESOFT, NULL, 0);
|
|
||||||
Dbprintf("SmartCardDirectSend: I2C_BufferWrite failed\n");
|
Dbprintf("SmartCardDirectSend: I2C_BufferWrite failed\n");
|
||||||
goto OUT;
|
goto OUT;
|
||||||
}
|
}
|
||||||
|
@ -109,11 +98,8 @@ static void SmartCardDirectSend(uint8_t prepend, const smart_card_raw_t *p, uint
|
||||||
}
|
}
|
||||||
|
|
||||||
if (len == 2 && resp[1] == 0x61) {
|
if (len == 2 && resp[1] == 0x61) {
|
||||||
//Dbprintf("Data to be read: len = %d\n", len);
|
|
||||||
//Dbprintf("\n");
|
|
||||||
|
|
||||||
uint8_t cmd_getresp[] = {0x00, ISO7816_GET_RESPONSE, 0x00, 0x00, resp[2]};
|
uint8_t cmd_getresp[] = {0x00, ISO7816_GET_RESPONSE, 0x00, 0x00, resp[2]};
|
||||||
//smart_card_raw_t *payload = calloc(1, sizeof(smart_card_raw_t) + sizeof(cmd_getresp));
|
|
||||||
smart_card_raw_t *payload = (smart_card_raw_t *)BigBuf_calloc(sizeof(smart_card_raw_t) + sizeof(cmd_getresp));
|
smart_card_raw_t *payload = (smart_card_raw_t *)BigBuf_calloc(sizeof(smart_card_raw_t) + sizeof(cmd_getresp));
|
||||||
payload->flags = SC_RAW | SC_LOG;
|
payload->flags = SC_RAW | SC_LOG;
|
||||||
payload->len = sizeof(cmd_getresp);
|
payload->len = sizeof(cmd_getresp);
|
||||||
|
@ -129,8 +115,6 @@ static void SmartCardDirectSend(uint8_t prepend, const smart_card_raw_t *p, uint
|
||||||
resp[2] = 0x82;
|
resp[2] = 0x82;
|
||||||
AddCrc14A(resp, 3);
|
AddCrc14A(resp, 3);
|
||||||
|
|
||||||
//Dbhexdump(5, &resp[0], false); // special print
|
|
||||||
//EmSendCmd(&resp[0], 5);
|
|
||||||
memcpy(output, resp, 5);
|
memcpy(output, resp, 5);
|
||||||
*olen = 5;
|
*olen = 5;
|
||||||
}
|
}
|
||||||
|
@ -142,9 +126,6 @@ static void SmartCardDirectSend(uint8_t prepend, const smart_card_raw_t *p, uint
|
||||||
resp[2] = 0x82;
|
resp[2] = 0x82;
|
||||||
AddCrc14A(resp, 3);
|
AddCrc14A(resp, 3);
|
||||||
|
|
||||||
//Dbhexdump(5, &resp[0], false); // special print
|
|
||||||
//EmSendCmd14443aRaw(&resp[0], 5);
|
|
||||||
//EmSendCmd(&resp[0], 5);
|
|
||||||
memcpy(output, resp, 5);
|
memcpy(output, resp, 5);
|
||||||
*olen = 5;
|
*olen = 5;
|
||||||
FpgaDisableTracing();
|
FpgaDisableTracing();
|
||||||
|
@ -154,108 +135,23 @@ static void SmartCardDirectSend(uint8_t prepend, const smart_card_raw_t *p, uint
|
||||||
Dbprintf("***** sending it over the wire... len: %d =>\n", len);
|
Dbprintf("***** sending it over the wire... len: %d =>\n", len);
|
||||||
resp[1] = prepend;
|
resp[1] = prepend;
|
||||||
|
|
||||||
// if we have a generate AC request, lets extract the data and populate the template
|
|
||||||
if (resp[1] != 0xff && resp[2] == 0x77) {
|
|
||||||
Dbprintf("we have detected a generate ac response, lets repackage it!");
|
|
||||||
Dbhexdump(len, &resp[1], false); // special print
|
|
||||||
// 11 and 12 are trans counter.
|
|
||||||
// 16 to 24 are the cryptogram
|
|
||||||
// 27 to 34 is issuer application data
|
|
||||||
Dbprintf("atc: %d %d, cryptogram: %d ", resp[11], resp[12], resp[13]);
|
|
||||||
|
|
||||||
// then, on the template:
|
|
||||||
// 61 and 62 for counter
|
|
||||||
// 46 to 54 for cryptogram
|
|
||||||
// 36 to 43 for issuer application data
|
|
||||||
|
|
||||||
uint8_t template[] = { 0x00, 0x00, 0x77, 0x47, 0x82, 0x02, 0x39, 0x00, 0x57, 0x13, 0x47, 0x62, 0x28, 0x00, 0x05, 0x93, 0x38, 0x64, 0xd2, 0x70, 0x92, 0x01, 0x00, 0x00, 0x01, 0x42, 0x00, 0x00, 0x0f, 0x5f, 0x34, 0x01, 0x00, 0x9f, 0x10, 0x07, 0x06, 0x01, 0x12, 0x03, 0xa0, 0x20, 0x00, 0x9f, 0x26, 0x08, 0x56, 0xcb, 0x4e, 0xe1, 0xa4, 0xef, 0xac, 0x74, 0x9f, 0x27, 0x01, 0x80, 0x9f, 0x36, 0x02, 0x00, 0x07, 0x9f, 0x6c, 0x02, 0x3e, 0x00, 0x9f, 0x6e, 0x04, 0x20, 0x70, 0x00, 0x00, 0x90, 0x00, 0xff, 0xff};
|
|
||||||
|
|
||||||
// do the replacement
|
|
||||||
template[1] = resp[1]; // class bit
|
|
||||||
|
|
||||||
template[61] = resp[11];
|
|
||||||
template[62] = resp[12];
|
|
||||||
|
|
||||||
template[46] = resp[16];
|
|
||||||
template[47] = resp[17];
|
|
||||||
template[48] = resp[18];
|
|
||||||
template[49] = resp[19];
|
|
||||||
template[50] = resp[20];
|
|
||||||
template[51] = resp[21];
|
|
||||||
template[52] = resp[22];
|
|
||||||
template[53] = resp[23];
|
|
||||||
template[54] = resp[24];
|
|
||||||
|
|
||||||
template[36] = resp[27];
|
|
||||||
template[37] = resp[28];
|
|
||||||
template[38] = resp[29];
|
|
||||||
template[39] = resp[30];
|
|
||||||
template[40] = resp[31];
|
|
||||||
template[41] = resp[32];
|
|
||||||
template[42] = resp[33];
|
|
||||||
|
|
||||||
Dbprintf("\nrearranged is: ");
|
|
||||||
len = sizeof(template);
|
|
||||||
Dbhexdump(len, &template[0], false); // special print
|
|
||||||
|
|
||||||
AddCrc14A(&template[1], len - 3);
|
|
||||||
Dbprintf("\nafter crc rearranged is: ");
|
|
||||||
Dbhexdump(len, &template[0], false); // special print
|
|
||||||
Dbprintf("\n");
|
|
||||||
|
|
||||||
//EmSendCmd(&template[1], len-1);
|
|
||||||
memcpy(output, &template[1], len - 1);
|
|
||||||
*olen = len - 1;
|
|
||||||
|
|
||||||
BigBuf_free();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Dbhexdump(len, &resp[1], false); // special print
|
|
||||||
AddCrc14A(&resp[1], len);
|
AddCrc14A(&resp[1], len);
|
||||||
Dbhexdump(len + 2, &resp[1], false); // special print
|
Dbhexdump(len + 2, &resp[1], false);
|
||||||
|
|
||||||
// Check we don't want to modify the response (application profile response)
|
|
||||||
//uint8_t modifyme[] = {0x03, 0x77, 0x0e, 0x82, 0x02};
|
|
||||||
|
|
||||||
BigBuf_free();
|
BigBuf_free();
|
||||||
|
|
||||||
if (prepend == 0xff) {
|
if (prepend == 0xff) {
|
||||||
Dbprintf("pdol request, we can can the response...");
|
Dbprintf("pdol request, we can ignore the response...");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (memcmp(&resp[2], &pay1_response[0], sizeof(pay1_response)) == 0 && true) {
|
memcpy(output, &resp[1], len + 2);
|
||||||
Dbprintf("Switching out the pay1 response for a pay2 response...");
|
*olen = len + 2;
|
||||||
//EmSendCmd(&pay2_response[0], sizeof(pay2_response));
|
|
||||||
memcpy(output, &pay2_response[0], sizeof(pay2_response));
|
|
||||||
*olen = sizeof(pay2_response);
|
|
||||||
} else if (memcmp(&resp[1], &fci_template[0], 2) == 0 && true) {
|
|
||||||
Dbprintf("***** modifying response to have full fci template...!");
|
|
||||||
//EmSendCmd(&fci_template[0], sizeof(fci_template));
|
|
||||||
memcpy(output, &fci_template[0], sizeof(fci_template));
|
|
||||||
*olen = sizeof(fci_template);
|
|
||||||
} else {
|
|
||||||
//Dbprintf("***** not modifying response...");
|
|
||||||
//EmSendCmd(&resp[1], len + 2);
|
|
||||||
memcpy(output, &resp[1], len + 2);
|
|
||||||
*olen = len + 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
BigBuf_free();
|
BigBuf_free();
|
||||||
|
|
||||||
//memcpy(saved_command, &resp[1], len+2);
|
|
||||||
//saved_command_len = len+2;
|
|
||||||
//EmSendCmd14443aRaw(&resp[1], len+2);
|
|
||||||
//FpgaDisableTracing();
|
|
||||||
//EmSend4bit(encrypted_data ? mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA) : CARD_NACK_NA);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//reply_ng(CMD_SMART_RAW, PM3_SUCCESS, resp, len);
|
|
||||||
|
|
||||||
OUT:
|
OUT:
|
||||||
//BigBuf_free();
|
|
||||||
//set_tracing(false);
|
|
||||||
LEDsoff();
|
LEDsoff();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -269,7 +165,6 @@ int CmdSmartRaw(const uint8_t prepend, const uint8_t *data, int dlen, uint8_t *o
|
||||||
dlen = data[4] + 5;
|
dlen = data[4] + 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
//smart_card_raw_t *payload = calloc(1, sizeof(smart_card_raw_t) + dlen);
|
|
||||||
smart_card_raw_t *payload = (smart_card_raw_t *)BigBuf_calloc(sizeof(smart_card_raw_t) + dlen);
|
smart_card_raw_t *payload = (smart_card_raw_t *)BigBuf_calloc(sizeof(smart_card_raw_t) + dlen);
|
||||||
if (payload == NULL) {
|
if (payload == NULL) {
|
||||||
Dbprintf("failed to allocate memory");
|
Dbprintf("failed to allocate memory");
|
||||||
|
@ -285,7 +180,6 @@ int CmdSmartRaw(const uint8_t prepend, const uint8_t *data, int dlen, uint8_t *o
|
||||||
bool use_t0 = true;
|
bool use_t0 = true;
|
||||||
|
|
||||||
if (active || active_select) {
|
if (active || active_select) {
|
||||||
|
|
||||||
payload->flags |= (SC_CONNECT | SC_CLEARLOG);
|
payload->flags |= (SC_CONNECT | SC_CLEARLOG);
|
||||||
if (active_select)
|
if (active_select)
|
||||||
payload->flags |= SC_SELECT;
|
payload->flags |= SC_SELECT;
|
||||||
|
@ -296,7 +190,6 @@ int CmdSmartRaw(const uint8_t prepend, const uint8_t *data, int dlen, uint8_t *o
|
||||||
payload->flags |= SC_WAIT;
|
payload->flags |= SC_WAIT;
|
||||||
payload->wait_delay = timeout;
|
payload->wait_delay = timeout;
|
||||||
}
|
}
|
||||||
//Dbprintf("SIM Card timeout... %u ms", payload->wait_delay);
|
|
||||||
|
|
||||||
if (dlen > 0) {
|
if (dlen > 0) {
|
||||||
if (use_t0)
|
if (use_t0)
|
||||||
|
@ -305,66 +198,7 @@ int CmdSmartRaw(const uint8_t prepend, const uint8_t *data, int dlen, uint8_t *o
|
||||||
payload->flags |= SC_RAW;
|
payload->flags |= SC_RAW;
|
||||||
}
|
}
|
||||||
|
|
||||||
////uint8_t *buf = calloc(PM3_CMD_DATA_SIZE, sizeof(uint8_t));
|
|
||||||
//uint8_t *buf = BigBuf_calloc(PM3_CMD_DATA_SIZE, sizeof(uint8_t));
|
|
||||||
//if (buf == NULL) {
|
|
||||||
// Dbprintf("failed to allocate memory");
|
|
||||||
// free(payload);
|
|
||||||
// return PM3_EMALLOC;
|
|
||||||
//}
|
|
||||||
|
|
||||||
|
|
||||||
//clearCommandBuffer();
|
|
||||||
//SendCommandNG(CMD_SMART_RAW, (uint8_t *)payload, sizeof(smart_card_raw_t) + dlen);
|
|
||||||
|
|
||||||
//for (int i = 0; i < dlen; i++) {
|
|
||||||
// Dbprintf("%02x ", data[i]);
|
|
||||||
//}
|
|
||||||
|
|
||||||
SmartCardDirectSend(prepend, payload, output, olen);
|
SmartCardDirectSend(prepend, payload, output, olen);
|
||||||
|
|
||||||
//if (reply == false) {
|
|
||||||
// Dbprintf("failed to talk to smart card!!!");
|
|
||||||
// goto out;
|
|
||||||
//}
|
|
||||||
|
|
||||||
//// reading response from smart card
|
|
||||||
//int len = smart_response(buf, PM3_CMD_DATA_SIZE);
|
|
||||||
//if (len < 0) {
|
|
||||||
// free(payload);
|
|
||||||
// free(buf);
|
|
||||||
// return PM3_ESOFT;
|
|
||||||
//}
|
|
||||||
|
|
||||||
//if (buf[0] == 0x6C) {
|
|
||||||
|
|
||||||
// // request more bytes to download
|
|
||||||
// data[4] = buf[1];
|
|
||||||
// memcpy(payload->data, data, dlen);
|
|
||||||
// clearCommandBuffer();
|
|
||||||
// SendCommandNG(CMD_SMART_RAW, (uint8_t *)payload, sizeof(smart_card_raw_t) + dlen);
|
|
||||||
|
|
||||||
// len = smart_response(buf, PM3_CMD_DATA_SIZE);
|
|
||||||
|
|
||||||
// data[4] = 0;
|
|
||||||
//}
|
|
||||||
|
|
||||||
//if (decode_tlv && len > 4) {
|
|
||||||
// TLVPrintFromBuffer(buf, len - 2);
|
|
||||||
//} else {
|
|
||||||
// if (len > 2) {
|
|
||||||
// Dbprintf("Response data:");
|
|
||||||
// Dbprintf(" # | bytes | ascii");
|
|
||||||
// Dbprintf("---+-------------------------------------------------+-----------------");
|
|
||||||
// print_hex_break(buf, len, 16);
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
//memcpy(buffer, buf, len);
|
|
||||||
|
|
||||||
//out:
|
|
||||||
//free(payload);
|
|
||||||
//free(buf);
|
|
||||||
return PM3_SUCCESS;
|
return PM3_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -198,7 +198,7 @@ static uint8_t MifareMaxSector(uint16_t flags) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool MifareSimInit(uint16_t flags, uint8_t *uid, uint16_t atqa, uint8_t sak, tag_response_info_t **responses, uint32_t *cuid, uint8_t *uid_len, uint8_t **rats, uint8_t *rats_len) {
|
bool MifareSimInit(uint16_t flags, uint8_t *uid, uint16_t atqa, uint8_t sak, tag_response_info_t **responses, uint32_t *cuid, uint8_t *uid_len, uint8_t **rats, uint8_t *rats_len) {
|
||||||
|
|
||||||
uint8_t uid_tmp[10] = {0};
|
uint8_t uid_tmp[10] = {0};
|
||||||
// SPEC: https://www.nxp.com/docs/en/application-note/AN10833.pdf
|
// SPEC: https://www.nxp.com/docs/en/application-note/AN10833.pdf
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#define __MIFARESIM_H
|
#define __MIFARESIM_H
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
#include "mifare.h"
|
||||||
|
|
||||||
#ifndef CheckCrc14A
|
#ifndef CheckCrc14A
|
||||||
# define CheckCrc14A(data, len) check_crc(CRC_14443_A, (data), (len))
|
# define CheckCrc14A(data, len) check_crc(CRC_14443_A, (data), (len))
|
||||||
|
@ -42,5 +43,6 @@
|
||||||
#define AUTHKEYNONE 0xff
|
#define AUTHKEYNONE 0xff
|
||||||
|
|
||||||
void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *uid, uint16_t atqa, uint8_t sak);
|
void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *uid, uint16_t atqa, uint8_t sak);
|
||||||
|
bool MifareSimInit(uint16_t flags, uint8_t *uid, uint16_t atqa, uint8_t sak, tag_response_info_t **responses, uint32_t *cuid, uint8_t *uid_len, uint8_t **rats, uint8_t *rats_len);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -643,21 +643,21 @@ static int CmdEMVSmartToNFC(const char *Cmd) {
|
||||||
};
|
};
|
||||||
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
||||||
|
|
||||||
int uid_len = 0;
|
int uidlen = 0;
|
||||||
uint8_t uid[7] = {0};
|
uint8_t uid[7] = {0};
|
||||||
CLIGetHexWithReturn(ctx, 2, uid, &uid_len);
|
CLIGetHexWithReturn(ctx, 2, uid, &uidlen);
|
||||||
|
|
||||||
if (uid_len == 0) {
|
if (uidlen == 0) {
|
||||||
PrintAndLogEx(SUCCESS, "No UID provided, using default.");
|
PrintAndLogEx(SUCCESS, "No UID provided, using default.");
|
||||||
uint8_t default_uid[7] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
|
uint8_t default_uid[7] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
|
||||||
memcpy(uid, default_uid, sizeof(default_uid));
|
memcpy(uid, default_uid, sizeof(default_uid));
|
||||||
uid_len = sizeof(default_uid);
|
uidlen = sizeof(default_uid);
|
||||||
} else if (uid_len != 7) {
|
} else if (uidlen != 7) {
|
||||||
PrintAndLogEx(FAILED, "UID must be 7 bytes long.");
|
PrintAndLogEx(FAILED, "UID must be 7 bytes long.");
|
||||||
return PM3_EINVARG;
|
return PM3_EINVARG;
|
||||||
}
|
}
|
||||||
|
|
||||||
PrintAndLogEx(SUCCESS, "UID length is %d", uid_len);
|
PrintAndLogEx(SUCCESS, "UID length is %d", uidlen);
|
||||||
|
|
||||||
bool testMode = arg_get_lit(ctx, 1);
|
bool testMode = arg_get_lit(ctx, 1);
|
||||||
bool show_apdu = true;
|
bool show_apdu = true;
|
||||||
|
@ -670,7 +670,7 @@ static int CmdEMVSmartToNFC(const char *Cmd) {
|
||||||
|
|
||||||
CLIParserFree(ctx);
|
CLIParserFree(ctx);
|
||||||
|
|
||||||
// todo: check this is relevant for us.
|
// todo for PR: check this is relevant for us.
|
||||||
SetAPDULogging(show_apdu);
|
SetAPDULogging(show_apdu);
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
|
@ -681,14 +681,30 @@ static int CmdEMVSmartToNFC(const char *Cmd) {
|
||||||
uint8_t sak;
|
uint8_t sak;
|
||||||
} PACKED payload;
|
} PACKED payload;
|
||||||
|
|
||||||
memcpy(payload.uid, uid, uid_len);
|
memcpy(payload.uid, uid, uidlen);
|
||||||
payload.flags = 0x1204;
|
|
||||||
|
// Set up the flags for 2K mifare sim with RATS
|
||||||
|
uint16_t flags = 0;
|
||||||
|
|
||||||
|
FLAG_SET_UID_IN_DATA(flags, uidlen);
|
||||||
|
if (IS_FLAG_UID_IN_EMUL(flags)) {
|
||||||
|
PrintAndLogEx(WARNING, "Invalid parameter for UID");
|
||||||
|
CLIParserFree(ctx);
|
||||||
|
return PM3_EINVARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
FLAG_SET_MF_SIZE(flags, MIFARE_2K_MAX_BYTES);
|
||||||
|
|
||||||
|
flags |= FLAG_ATQA_IN_DATA;
|
||||||
|
flags |= FLAG_SAK_IN_DATA;
|
||||||
|
|
||||||
|
payload.flags = flags;
|
||||||
payload.exitAfter = 0x1;
|
payload.exitAfter = 0x1;
|
||||||
payload.atqa = 0x0;
|
payload.atqa = 0x0;
|
||||||
payload.sak = 0x20;
|
payload.sak = 0x20;
|
||||||
|
|
||||||
clearCommandBuffer();
|
clearCommandBuffer();
|
||||||
SendCommandNG(0x0386, (uint8_t *)&payload, sizeof(payload));
|
SendCommandNG(CMD_HF_ISO14443A_EMV_SIMULATE, (uint8_t *)&payload, sizeof(payload));
|
||||||
|
|
||||||
PrintAndLogEx(INFO, "Press " _GREEN_("pm3 button") " to abort simulation");
|
PrintAndLogEx(INFO, "Press " _GREEN_("pm3 button") " to abort simulation");
|
||||||
|
|
||||||
|
|
|
@ -489,6 +489,12 @@ ISO 7816-4 Basic interindustry commands. For command APDU's.
|
||||||
#define ISO7816_EXTERNAL_AUTHENTICATION 0x82
|
#define ISO7816_EXTERNAL_AUTHENTICATION 0x82
|
||||||
#define ISO7816_GET_CHALLENGE 0x84
|
#define ISO7816_GET_CHALLENGE 0x84
|
||||||
#define ISO7816_MANAGE_CHANNEL 0x70
|
#define ISO7816_MANAGE_CHANNEL 0x70
|
||||||
|
#define ISO7816_APPLICATION_BLOCK 0x1E
|
||||||
|
#define ISO7816_APPLICATION_UNBLOCK 0x18
|
||||||
|
#define ISO7816_CARD_BLOCK 0x16
|
||||||
|
#define ISO7816_GENERATE_APPLICATION_CRYPTOGRAM 0xAE
|
||||||
|
#define ISO7816_GET_PROCESSING_OPTIONS 0xA8
|
||||||
|
#define ISO7816_PIN_CHANGE 0x24
|
||||||
|
|
||||||
#define ISO7816_GET_RESPONSE 0xC0
|
#define ISO7816_GET_RESPONSE 0xC0
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue