From 80fe72357062b8c5b22936d84e46e9b2c1e4b6e2 Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Sun, 18 Jan 2015 20:21:53 +0100 Subject: [PATCH 01/25] Generic trace pt1: Moved arm-side trace functionality into util-package --- armsrc/iso14443a.c | 54 ----------------------------------------- armsrc/util.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 54 deletions(-) diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index d326be2c..f5348e77 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -199,60 +199,6 @@ void AppendCrc14443a(uint8_t* data, int len) ComputeCrc14443(CRC_14443_A,data,len,data+len,data+len+1); } -// The function LogTrace() is also used by the iClass implementation in iClass.c -bool RAMFUNC LogTrace(const uint8_t *btBytes, uint16_t iLen, uint32_t timestamp_start, uint32_t timestamp_end, uint8_t *parity, bool readerToTag) -{ - if (!tracing) return FALSE; - - uint16_t num_paritybytes = (iLen-1)/8 + 1; // number of valid paritybytes in *parity - uint16_t duration = timestamp_end - timestamp_start; - - // Return when trace is full - if (traceLen + sizeof(iLen) + sizeof(timestamp_start) + sizeof(duration) + num_paritybytes + iLen >= TRACE_SIZE) { - tracing = FALSE; // don't trace any more - return FALSE; - } - - // Traceformat: - // 32 bits timestamp (little endian) - // 16 bits duration (little endian) - // 16 bits data length (little endian, Highest Bit used as readerToTag flag) - // y Bytes data - // x Bytes parity (one byte per 8 bytes data) - - // timestamp (start) - trace[traceLen++] = ((timestamp_start >> 0) & 0xff); - trace[traceLen++] = ((timestamp_start >> 8) & 0xff); - trace[traceLen++] = ((timestamp_start >> 16) & 0xff); - trace[traceLen++] = ((timestamp_start >> 24) & 0xff); - - // duration - trace[traceLen++] = ((duration >> 0) & 0xff); - trace[traceLen++] = ((duration >> 8) & 0xff); - - // data length - trace[traceLen++] = ((iLen >> 0) & 0xff); - trace[traceLen++] = ((iLen >> 8) & 0xff); - - // readerToTag flag - if (!readerToTag) { - trace[traceLen - 1] |= 0x80; - } - - // data bytes - if (btBytes != NULL && iLen != 0) { - memcpy(trace + traceLen, btBytes, iLen); - } - traceLen += iLen; - - // parity bytes - if (parity != NULL && iLen != 0) { - memcpy(trace + traceLen, parity, num_paritybytes); - } - traceLen += num_paritybytes; - - return TRUE; -} //============================================================================= // ISO 14443 Type A - Miller decoder diff --git a/armsrc/util.c b/armsrc/util.c index 674f1b91..cce9bed8 100644 --- a/armsrc/util.c +++ b/armsrc/util.c @@ -428,4 +428,64 @@ uint32_t RAMFUNC GetCountSspClk(){ } } +/** + This is a function to store traces. All protocols can use this generic tracer-function. + The traces produced by calling this function can be fetched on the client-side + by 'hf list raw', alternatively 'hf list ' for protocol-specific + annotation of commands/responses. + +**/ +bool RAMFUNC LogTrace(const uint8_t *btBytes, uint16_t iLen, uint32_t timestamp_start, uint32_t timestamp_end, uint8_t *parity, bool readerToTag) +{ + if (!tracing) return FALSE; + + uint16_t num_paritybytes = (iLen-1)/8 + 1; // number of valid paritybytes in *parity + uint16_t duration = timestamp_end - timestamp_start; + + // Return when trace is full + if (traceLen + sizeof(iLen) + sizeof(timestamp_start) + sizeof(duration) + num_paritybytes + iLen >= TRACE_SIZE) { + tracing = FALSE; // don't trace any more + return FALSE; + } + + // Traceformat: + // 32 bits timestamp (little endian) + // 16 bits duration (little endian) + // 16 bits data length (little endian, Highest Bit used as readerToTag flag) + // y Bytes data + // x Bytes parity (one byte per 8 bytes data) + + // timestamp (start) + trace[traceLen++] = ((timestamp_start >> 0) & 0xff); + trace[traceLen++] = ((timestamp_start >> 8) & 0xff); + trace[traceLen++] = ((timestamp_start >> 16) & 0xff); + trace[traceLen++] = ((timestamp_start >> 24) & 0xff); + + // duration + trace[traceLen++] = ((duration >> 0) & 0xff); + trace[traceLen++] = ((duration >> 8) & 0xff); + + // data length + trace[traceLen++] = ((iLen >> 0) & 0xff); + trace[traceLen++] = ((iLen >> 8) & 0xff); + + // readerToTag flag + if (!readerToTag) { + trace[traceLen - 1] |= 0x80; + } + + // data bytes + if (btBytes != NULL && iLen != 0) { + memcpy(trace + traceLen, btBytes, iLen); + } + traceLen += iLen; + + // parity bytes + if (parity != NULL && iLen != 0) { + memcpy(trace + traceLen, parity, num_paritybytes); + } + traceLen += num_paritybytes; + + return TRUE; +} From 355c8b4a7df083c13d82963fb9d14548647e91b1 Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Sun, 18 Jan 2015 20:23:58 +0100 Subject: [PATCH 02/25] Generic trace pt2: made iso14443b use standard trace format --- armsrc/iso14443.c | 102 ++++++++++++++++++++++++--------------------- armsrc/iso14443a.c | 8 ---- armsrc/iso14443a.h | 3 -- armsrc/util.c | 16 +++++++ armsrc/util.h | 9 ++++ 5 files changed, 80 insertions(+), 58 deletions(-) diff --git a/armsrc/iso14443.c b/armsrc/iso14443.c index e9483189..4d7d4fa8 100644 --- a/armsrc/iso14443.c +++ b/armsrc/iso14443.c @@ -618,7 +618,7 @@ static RAMFUNC int Handle14443SamplesDemod(int ci, int cq) } /* - * Demodulate the samples we received from the tag + * Demodulate the samples we received from the tag, also log to tracebuffer * weTx: set to 'TRUE' if we behave like a reader * set to 'FALSE' if we behave like a snooper * quiet: set to 'TRUE' to disable debug output @@ -698,6 +698,12 @@ static void GetSamplesFor14443Demod(int weTx, int n, int quiet) } AT91C_BASE_PDC_SSC->PDC_PTCR = AT91C_PDC_RXTDIS; if (!quiet) Dbprintf("%x %x %x", max, gotFrame, Demod.len); + //Tracing + if (tracing && Demod.len > 0) { + uint8_t parity[MAX_PARITY_SIZE]; + GetParity(Demod.output , Demod.len, parity); + LogTrace(Demod.output,Demod.len, 0, 0, parity, FALSE); + } } //----------------------------------------------------------------------------- @@ -853,6 +859,20 @@ void AcquireRawAdcSamplesIso14443(uint32_t parameter) SendRawCommand14443B(sizeof(cmd1),1,1,cmd1); } +/** + Convenience function to encode, transmit and trace iso 14443b comms + **/ +static void CodeAndTransmit14443bAsReader(const uint8_t *cmd, int len) +{ + CodeIso14443bAsReader(cmd, len); + TransmitFor14443(); + if (tracing) { + uint8_t parity[MAX_PARITY_SIZE]; + GetParity(cmd, len, parity); + LogTrace(cmd,len, 0, 0, parity, TRUE); + } +} + //----------------------------------------------------------------------------- // Read a SRI512 ISO 14443 tag. // @@ -864,6 +884,9 @@ void AcquireRawAdcSamplesIso14443(uint32_t parameter) //----------------------------------------------------------------------------- void ReadSTMemoryIso14443(uint32_t dwLast) { + clear_trace(); + set_tracing(TRUE); + uint8_t i = 0x00; FpgaDownloadAndGo(FPGA_BITSTREAM_HF); @@ -885,8 +908,8 @@ void ReadSTMemoryIso14443(uint32_t dwLast) // First command: wake up the tag using the INITIATE command uint8_t cmd1[] = { 0x06, 0x00, 0x97, 0x5b}; - CodeIso14443bAsReader(cmd1, sizeof(cmd1)); - TransmitFor14443(); + + CodeAndTransmit14443bAsReader(cmd1, sizeof(cmd1)); // LED_A_ON(); GetSamplesFor14443Demod(TRUE, 2000,TRUE); // LED_A_OFF(); @@ -903,8 +926,8 @@ void ReadSTMemoryIso14443(uint32_t dwLast) cmd1[0] = 0x0E; // 0x0E is SELECT cmd1[1] = Demod.output[0]; ComputeCrc14443(CRC_14443_B, cmd1, 2, &cmd1[2], &cmd1[3]); - CodeIso14443bAsReader(cmd1, sizeof(cmd1)); - TransmitFor14443(); + CodeAndTransmit14443bAsReader(cmd1, sizeof(cmd1)); + // LED_A_ON(); GetSamplesFor14443Demod(TRUE, 2000,TRUE); // LED_A_OFF(); @@ -927,8 +950,8 @@ void ReadSTMemoryIso14443(uint32_t dwLast) // First get the tag's UID: cmd1[0] = 0x0B; ComputeCrc14443(CRC_14443_B, cmd1, 1 , &cmd1[1], &cmd1[2]); - CodeIso14443bAsReader(cmd1, 3); // Only first three bytes for this one - TransmitFor14443(); + CodeAndTransmit14443bAsReader(cmd1, 3); // Only first three bytes for this one + // LED_A_ON(); GetSamplesFor14443Demod(TRUE, 2000,TRUE); // LED_A_OFF(); @@ -959,8 +982,8 @@ void ReadSTMemoryIso14443(uint32_t dwLast) } cmd1[1] = i; ComputeCrc14443(CRC_14443_B, cmd1, 2, &cmd1[2], &cmd1[3]); - CodeIso14443bAsReader(cmd1, sizeof(cmd1)); - TransmitFor14443(); + CodeAndTransmit14443bAsReader(cmd1, sizeof(cmd1)); + // LED_A_ON(); GetSamplesFor14443Demod(TRUE, 2000,TRUE); // LED_A_OFF(); @@ -1097,20 +1120,15 @@ void RAMFUNC SnoopIso14443(void) samples += 2; #define HANDLE_BIT_IF_BODY \ - if(triggered) { \ - trace[traceLen++] = ((samples >> 0) & 0xff); \ - trace[traceLen++] = ((samples >> 8) & 0xff); \ - trace[traceLen++] = ((samples >> 16) & 0xff); \ - trace[traceLen++] = ((samples >> 24) & 0xff); \ - trace[traceLen++] = 0; \ - trace[traceLen++] = 0; \ - trace[traceLen++] = 0; \ - trace[traceLen++] = 0; \ - trace[traceLen++] = Uart.byteCnt; \ - memcpy(trace+traceLen, receivedCmd, Uart.byteCnt); \ - traceLen += Uart.byteCnt; \ - if(traceLen > 1000) break; \ - } \ + if(triggered && tracing) {\ + uint8_t parity[MAX_PARITY_SIZE];\ + GetParity(receivedCmd, Uart.byteCnt, parity);\ + LogTrace(receivedCmd,Uart.byteCnt,samples, samples,parity,TRUE);\ + if(!tracing) {\ + DbpString("Reached trace limit");\ + break;\ + }\ + }\ /* And ready to receive another command. */ \ memset(&Uart, 0, sizeof(Uart)); \ Uart.output = receivedCmd; \ @@ -1130,28 +1148,18 @@ void RAMFUNC SnoopIso14443(void) } if(Handle14443SamplesDemod(ci, cq)) { - // timestamp, as a count of samples - trace[traceLen++] = ((samples >> 0) & 0xff); - trace[traceLen++] = ((samples >> 8) & 0xff); - trace[traceLen++] = ((samples >> 16) & 0xff); - trace[traceLen++] = 0x80 | ((samples >> 24) & 0xff); - // correlation metric (~signal strength estimate) - if(Demod.metricN != 0) { - Demod.metric /= Demod.metricN; - } - trace[traceLen++] = ((Demod.metric >> 0) & 0xff); - trace[traceLen++] = ((Demod.metric >> 8) & 0xff); - trace[traceLen++] = ((Demod.metric >> 16) & 0xff); - trace[traceLen++] = ((Demod.metric >> 24) & 0xff); - // length - trace[traceLen++] = Demod.len; - memcpy(trace+traceLen, receivedResponse, Demod.len); - traceLen += Demod.len; - if(traceLen > DEMOD_TRACE_SIZE) { - DbpString("Reached trace limit"); - goto done; - } + //Use samples as a time measurement + if(tracing) + { + uint8_t parity[MAX_PARITY_SIZE]; + GetParity(receivedResponse, Demod.len, parity); + LogTrace(receivedResponse,Demod.len,samples, samples,parity,FALSE); + if(!tracing) { + DbpString("Reached trace limit"); + break; + } + } triggered = TRUE; LED_A_OFF(); LED_B_ON(); @@ -1175,7 +1183,7 @@ done: LED_C_OFF(); AT91C_BASE_PDC_SSC->PDC_PTCR = AT91C_PDC_RXTDIS; DbpString("Snoop statistics:"); - Dbprintf(" Max behind by: %i", maxBehindBy); + Dbprintf(" Max behind by: %i", maxBehindBy); Dbprintf(" Uart State: %x", Uart.state); Dbprintf(" Uart ByteCnt: %i", Uart.byteCnt); Dbprintf(" Uart ByteCntMax: %i", Uart.byteCntMax); @@ -1220,8 +1228,8 @@ void SendRawCommand14443B(uint32_t datalen, uint32_t recv,uint8_t powerfield, ui SpinDelay(200); } - CodeIso14443bAsReader(data, datalen); - TransmitFor14443(); + CodeAndTransmit14443bAsReader(data, datalen); + if(recv) { uint16_t iLen = MIN(Demod.len,USB_CMD_DATA_SIZE); diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index f5348e77..d91b24d7 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -148,14 +148,6 @@ void iso14a_set_trigger(bool enable) { trigger = enable; } -void iso14a_clear_trace() { - memset(trace, 0x44, TRACE_SIZE); - traceLen = 0; -} - -void iso14a_set_tracing(bool enable) { - tracing = enable; -} void iso14a_set_timeout(uint32_t timeout) { iso14a_timeout = timeout; diff --git a/armsrc/iso14443a.h b/armsrc/iso14443a.h index c595d5e1..c3710388 100644 --- a/armsrc/iso14443a.h +++ b/armsrc/iso14443a.h @@ -87,7 +87,4 @@ extern int iso14443a_select_card(uint8_t *uid_ptr, iso14a_card_select_t *resp_da extern void iso14a_set_trigger(bool enable); extern void iso14a_set_timeout(uint32_t timeout); -extern void iso14a_clear_trace(); -extern void iso14a_set_tracing(bool enable); - #endif /* __ISO14443A_H */ diff --git a/armsrc/util.c b/armsrc/util.c index cce9bed8..a4f55879 100644 --- a/armsrc/util.c +++ b/armsrc/util.c @@ -427,6 +427,22 @@ uint32_t RAMFUNC GetCountSspClk(){ return tmp_count; } } +void iso14a_clear_trace() { + clear_trace(); +} + +void iso14a_set_tracing(bool enable) { + set_tracing(enable); +} + +void clear_trace() { + memset(trace, 0x44, TRACE_SIZE); + traceLen = 0; +} + +void set_tracing(bool enable) { + tracing = enable; +} /** This is a function to store traces. All protocols can use this generic tracer-function. diff --git a/armsrc/util.h b/armsrc/util.h index d7eacd70..141d74b9 100644 --- a/armsrc/util.h +++ b/armsrc/util.h @@ -43,6 +43,15 @@ void LEDsoff(); int BUTTON_CLICKED(int ms); int BUTTON_HELD(int ms); void FormatVersionInformation(char *dst, int len, const char *prefix, void *version_information); +// @deprecated +void iso14a_clear_trace(); +// @deprecated +void iso14a_set_tracing(bool enable); +void clear_trace(); +void set_tracing(bool enable); + +// The function LogTrace() is also used by the iClass implementation in iclass.c and both iso14443a, iso14443b and mifare +bool RAMFUNC LogTrace(const uint8_t *btBytes, uint16_t iLen, uint32_t timestamp_start, uint32_t timestamp_end, uint8_t *parity, bool readerToTag); void StartTickCount(); uint32_t RAMFUNC GetTickCount(); From 9e8255d4e99eb2917df13be92402e75a73417696 Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Wed, 21 Jan 2015 23:53:40 +0100 Subject: [PATCH 03/25] Generic tracing pt.3 : reworking how iso14443b-traces are stored in ARM-memory --- armsrc/appmain.c | 2 +- armsrc/iso14443.c | 18 +++++++----------- armsrc/iso14443a.c | 3 --- armsrc/util.c | 9 ++++++++- client/cmdhf.c | 24 ++++++++++++++++-------- 5 files changed, 32 insertions(+), 24 deletions(-) diff --git a/armsrc/appmain.c b/armsrc/appmain.c index 530dc39c..bca31533 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -986,7 +986,7 @@ void UsbPacketReceived(uint8_t *packet, int len) void __attribute__((noreturn)) AppMain(void) { SpinDelay(100); - + clear_trace(); if(common_area.magic != COMMON_AREA_MAGIC || common_area.version != 1) { /* Initialize common area */ memset(&common_area, 0, sizeof(common_area)); diff --git a/armsrc/iso14443.c b/armsrc/iso14443.c index 4d7d4fa8..3c8e1fdd 100644 --- a/armsrc/iso14443.c +++ b/armsrc/iso14443.c @@ -628,30 +628,26 @@ static void GetSamplesFor14443Demod(int weTx, int n, int quiet) int max = 0; int gotFrame = FALSE; -//# define DMA_BUFFER_SIZE 8 - int8_t *dmaBuf; - int lastRxCounter; - int8_t *upTo; int ci, cq; int samples = 0; // Clear out the state of the "UART" that receives from the tag. - memset(BigBuf, 0x00, 400); - Demod.output = (uint8_t *)BigBuf; + memset(Demod.output, 0x00, MAX_FRAME_SIZE); + Demod.output = ((uint8_t *)BigBuf) + RECV_RESP_OFFSET; Demod.len = 0; Demod.state = DEMOD_UNSYNCD; // And the UART that receives from the reader - Uart.output = (((uint8_t *)BigBuf) + 1024); - Uart.byteCntMax = 100; + Uart.output = ((uint8_t *)BigBuf) + RECV_CMD_OFFSET; + Uart.byteCntMax = MAX_FRAME_SIZE; Uart.state = STATE_UNSYNCD; - // Setup for the DMA. - dmaBuf = (int8_t *)(BigBuf + 32); - upTo = dmaBuf; + // The DMA buffer, used to stream samples from the FPGA + int8_t *dmaBuf = ((int8_t *)BigBuf) + DMA_BUFFER_OFFSET; + int8_t *upTo= dmaBuf; lastRxCounter = DEMOD_DMA_BUFFER_SIZE; FpgaSetupSscDma((uint8_t *)dmaBuf, DEMOD_DMA_BUFFER_SIZE); diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index d91b24d7..54c1db40 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -22,10 +22,7 @@ #include "mifareutil.h" static uint32_t iso14a_timeout; -uint8_t *trace = (uint8_t *) BigBuf+TRACE_OFFSET; int rsamples = 0; -int traceLen = 0; -int tracing = TRUE; uint8_t trigger = 0; // the block number for the ISO14443-4 PCB static uint8_t iso14_pcb_blocknum = 0; diff --git a/armsrc/util.c b/armsrc/util.c index a4f55879..38f41750 100644 --- a/armsrc/util.c +++ b/armsrc/util.c @@ -13,6 +13,9 @@ #include "string.h" #include "apps.h" +uint8_t *trace = (uint8_t *) BigBuf+TRACE_OFFSET; +int traceLen = 0; +int tracing = TRUE; void print_result(char *name, uint8_t *buf, size_t len) { @@ -463,7 +466,6 @@ bool RAMFUNC LogTrace(const uint8_t *btBytes, uint16_t iLen, uint32_t timestamp_ tracing = FALSE; // don't trace any more return FALSE; } - // Traceformat: // 32 bits timestamp (little endian) // 16 bits duration (little endian) @@ -502,6 +504,11 @@ bool RAMFUNC LogTrace(const uint8_t *btBytes, uint16_t iLen, uint32_t timestamp_ } traceLen += num_paritybytes; + if(traceLen +4 < TRACE_SIZE) + { //If it hadn't been cleared, for whatever reason.. + memset(trace+traceLen,0x44, 4); + } + return TRUE; } diff --git a/client/cmdhf.c b/client/cmdhf.c index 9acc9825..37366802 100644 --- a/client/cmdhf.c +++ b/client/cmdhf.c @@ -413,15 +413,18 @@ uint16_t printTraceLine(uint16_t tracepos, uint8_t* trace, uint8_t protocol, boo if (tracepos + data_len + parity_len >= TRACE_SIZE) { return TRACE_SIZE; } - uint8_t *frame = trace + tracepos; tracepos += data_len; uint8_t *parityBytes = trace + tracepos; tracepos += parity_len; + //--- Draw the data column + //char line[16][110]; char line[16][110]; - for (int j = 0; j < data_len; j++) { + + for (int j = 0; j < data_len && j/16 < 16; j++) { + int oddparity = 0x01; int k; @@ -430,11 +433,17 @@ uint16_t printTraceLine(uint16_t tracepos, uint8_t* trace, uint8_t protocol, boo } uint8_t parityBits = parityBytes[j>>3]; - if (isResponse && (oddparity != ((parityBits >> (7-(j&0x0007))) & 0x01))) { - sprintf(line[j/16]+((j%16)*4), "%02x! ", frame[j]); + snprintf(line[j/16]+(( j % 16) * 4),110, "%02x! ", frame[j]); + } else { - sprintf(line[j/16]+((j%16)*4), "%02x ", frame[j]); + snprintf(line[j/16]+(( j % 16) * 4),110, "%02x! ", frame[j]); + } + } + if(data_len == 0) + { + if(data_len == 0){ + sprintf(line[0],""); } } //--- Draw the CRC column @@ -479,8 +488,8 @@ uint16_t printTraceLine(uint16_t tracepos, uint8_t* trace, uint8_t protocol, boo annotateIso14443b(explanation,sizeof(explanation),frame,data_len); } - int num_lines = (data_len - 1)/16 + 1; - for (int j = 0; j < num_lines; j++) { + int num_lines = MIN((data_len - 1)/16 + 1, 16); + for (int j = 0; j < num_lines ; j++) { if (j == 0) { PrintAndLog(" %9d | %9d | %s | %-64s| %s| %s", (timestamp - first_timestamp), @@ -573,7 +582,6 @@ int CmdHFList(const char *Cmd) uint16_t tracepos = 0; GetFromBigBuf(trace, TRACE_SIZE, 0); WaitForResponse(CMD_ACK, NULL); - PrintAndLog("Recorded Activity"); PrintAndLog(""); PrintAndLog("Start = Start of Start Bit, End = End of last modulation. Src = Source of Transfer"); From 388c92bde5f597677b3cc34c5ace425e7074bc9e Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Thu, 22 Jan 2015 00:19:20 +0100 Subject: [PATCH 04/25] Generic tracing pt.4: Deprecated old 'hf 14b list' command --- client/cmdhf14b.c | 79 ++--------------------------------------------- 1 file changed, 3 insertions(+), 76 deletions(-) diff --git a/client/cmdhf14b.c b/client/cmdhf14b.c index e3d0fc23..3aaf45fa 100644 --- a/client/cmdhf14b.c +++ b/client/cmdhf14b.c @@ -145,82 +145,9 @@ demodError: int CmdHF14BList(const char *Cmd) { - uint8_t got[TRACE_BUFFER_SIZE]; - GetFromBigBuf(got,sizeof(got),0); - WaitForResponse(CMD_ACK,NULL); - - PrintAndLog("recorded activity:"); - PrintAndLog(" time :rssi: who bytes"); - PrintAndLog("---------+----+----+-----------"); - - int i = 0; - int prev = -1; - - for(;;) { - - if(i >= TRACE_BUFFER_SIZE) { break; } - - bool isResponse; - int timestamp = *((uint32_t *)(got+i)); - if(timestamp & 0x80000000) { - timestamp &= 0x7fffffff; - isResponse = 1; - } else { - isResponse = 0; - } - int metric = *((uint32_t *)(got+i+4)); - - int len = got[i+8]; - - if(len > 100) { - break; - } - if(i + len >= TRACE_BUFFER_SIZE) { - break; - } - - uint8_t *frame = (got+i+9); - - // Break and stick with current result if buffer was not completely full - if (frame[0] == 0x44 && frame[1] == 0x44 && frame[2] == 0x44 && frame[3] == 0x44) break; - - char line[1000] = ""; - int j; - for(j = 0; j < len; j++) { - sprintf(line+(j*3), "%02x ", frame[j]); - } - - char *crc; - if(len > 2) { - uint8_t b1, b2; - ComputeCrc14443(CRC_14443_B, frame, len-2, &b1, &b2); - if(b1 != frame[len-2] || b2 != frame[len-1]) { - crc = "**FAIL CRC**"; - } else { - crc = ""; - } - } else { - crc = "(SHORT)"; - } - - char metricString[100]; - if(isResponse) { - sprintf(metricString, "%3d", metric); - } else { - strcpy(metricString, " "); - } - - PrintAndLog(" +%7d: %s: %s %s %s", - (prev < 0 ? 0 : timestamp - prev), - metricString, - (isResponse ? "TAG" : " "), line, crc); - - prev = timestamp; - i += (len + 9); - } - return 0; + PrintAndLog("Deprecated command, use 'hf list 14b' instead"); + return 0; } - int CmdHF14BRead(const char *Cmd) { UsbCommand c = {CMD_ACQUIRE_RAW_ADC_SAMPLES_ISO_14443, {strtol(Cmd, NULL, 0), 0, 0}}; @@ -458,7 +385,7 @@ static command_t CommandTable[] = { {"help", CmdHelp, 1, "This help"}, {"demod", CmdHF14BDemod, 1, "Demodulate ISO14443 Type B from tag"}, - {"list", CmdHF14BList, 0, "List ISO 14443 history"}, + {"list", CmdHF14BList, 0, "[Deprecated] List ISO 14443b history"}, {"read", CmdHF14BRead, 0, "Read HF tag (ISO 14443)"}, {"sim", CmdHF14Sim, 0, "Fake ISO 14443 tag"}, {"simlisten", CmdHFSimlisten, 0, "Get HF samples as fake tag"}, From 3fd26a683ddc9e166392f722c23ab21fb618ce08 Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Thu, 22 Jan 2015 00:33:49 +0100 Subject: [PATCH 05/25] On another note; a nice udev-rule to have pm3 appear as /dev/pm3-1 (or /dev/pm3-2 if you have multiple) --- driver/77-mm-usb-device-blacklist.rules | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/driver/77-mm-usb-device-blacklist.rules b/driver/77-mm-usb-device-blacklist.rules index 986c9446..80086f49 100644 --- a/driver/77-mm-usb-device-blacklist.rules +++ b/driver/77-mm-usb-device-blacklist.rules @@ -7,4 +7,12 @@ # # proxmark3 +ACTION!="add|change", GOTO="mm_usb_device_blacklist_end" +SUBSYSTEM!="tty", GOTO="mm_ignore" + +ATTRS{idVendor}=="2d2d" ATTRS{idProduct}=="504d", ENV{ID_MM_DEVICE_IGNORE}="1" SYMLINK+="pm3-%n" + +LABEL="mm_ignore" ATTRS{idVendor}=="2d2d" ATTRS{idProduct}=="504d", ENV{ID_MM_DEVICE_IGNORE}="1" + +LABEL="mm_usb_device_blacklist_end" From aeadbdb216f3901ffe9d6ae766aebe43ac244853 Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Mon, 26 Jan 2015 22:10:05 +0100 Subject: [PATCH 06/25] Generic tracing: Some fixes in iso14443b snooping, to how DMA access is performed, sizes and buffers. --- armsrc/iso14443.c | 183 ++++++++++++++++++++++------------------------ 1 file changed, 88 insertions(+), 95 deletions(-) diff --git a/armsrc/iso14443.c b/armsrc/iso14443.c index 3c8e1fdd..0a6d2d67 100644 --- a/armsrc/iso14443.c +++ b/armsrc/iso14443.c @@ -19,11 +19,11 @@ //static void GetSamplesFor14443(int weTx, int n); -#define DEMOD_TRACE_SIZE 4096 +/*#define DEMOD_TRACE_SIZE 4096 #define READER_TAG_BUFFER_SIZE 2048 #define TAG_READER_BUFFER_SIZE 2048 #define DEMOD_DMA_BUFFER_SIZE 1024 - +*/ //============================================================================= // An ISO 14443 Type B tag. We listen for commands from the reader, using // a UART kind of thing that's implemented in software. When we get a @@ -617,6 +617,24 @@ static RAMFUNC int Handle14443SamplesDemod(int ci, int cq) return FALSE; } +static void DemodReset() +{ + // Clear out the state of the "UART" that receives from the tag. + Demod.output = ((uint8_t *)BigBuf) + RECV_RESP_OFFSET; + Demod.len = 0; + Demod.state = DEMOD_UNSYNCD; + memset(Demod.output, 0x00, MAX_FRAME_SIZE); + +} + +static void UartReset() +{ + // And the UART that receives from the reader + Uart.output = ((uint8_t *)BigBuf) + RECV_CMD_OFFSET; + Uart.byteCntMax = MAX_FRAME_SIZE; + Uart.state = STATE_UNSYNCD; +} + /* * Demodulate the samples we received from the tag, also log to tracebuffer * weTx: set to 'TRUE' if we behave like a reader @@ -634,22 +652,14 @@ static void GetSamplesFor14443Demod(int weTx, int n, int quiet) int samples = 0; - // Clear out the state of the "UART" that receives from the tag. - memset(Demod.output, 0x00, MAX_FRAME_SIZE); - Demod.output = ((uint8_t *)BigBuf) + RECV_RESP_OFFSET; - Demod.len = 0; - Demod.state = DEMOD_UNSYNCD; - - // And the UART that receives from the reader - Uart.output = ((uint8_t *)BigBuf) + RECV_CMD_OFFSET; - Uart.byteCntMax = MAX_FRAME_SIZE; - Uart.state = STATE_UNSYNCD; + DemodReset(); + UartReset(); // The DMA buffer, used to stream samples from the FPGA int8_t *dmaBuf = ((int8_t *)BigBuf) + DMA_BUFFER_OFFSET; int8_t *upTo= dmaBuf; - lastRxCounter = DEMOD_DMA_BUFFER_SIZE; - FpgaSetupSscDma((uint8_t *)dmaBuf, DEMOD_DMA_BUFFER_SIZE); + lastRxCounter = DMA_BUFFER_SIZE; + FpgaSetupSscDma((uint8_t *)dmaBuf, DMA_BUFFER_SIZE); // Signal field is ON with the appropriate LED: if (weTx) LED_D_ON(); else LED_D_OFF(); @@ -662,20 +672,20 @@ static void GetSamplesFor14443Demod(int weTx, int n, int quiet) int behindBy = lastRxCounter - AT91C_BASE_PDC_SSC->PDC_RCR; if(behindBy > max) max = behindBy; - while(((lastRxCounter-AT91C_BASE_PDC_SSC->PDC_RCR) & (DEMOD_DMA_BUFFER_SIZE-1)) + while(((lastRxCounter-AT91C_BASE_PDC_SSC->PDC_RCR) & (DMA_BUFFER_SIZE-1)) > 2) { ci = upTo[0]; cq = upTo[1]; upTo += 2; - if(upTo - dmaBuf > DEMOD_DMA_BUFFER_SIZE) { - upTo -= DEMOD_DMA_BUFFER_SIZE; + if(upTo - dmaBuf > DMA_BUFFER_SIZE) { + upTo -= DMA_BUFFER_SIZE; AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) upTo; - AT91C_BASE_PDC_SSC->PDC_RNCR = DEMOD_DMA_BUFFER_SIZE; + AT91C_BASE_PDC_SSC->PDC_RNCR = DMA_BUFFER_SIZE; } lastRxCounter -= 2; if(lastRxCounter <= 0) { - lastRxCounter += DEMOD_DMA_BUFFER_SIZE; + lastRxCounter += DMA_BUFFER_SIZE; } samples += 2; @@ -1031,18 +1041,18 @@ void RAMFUNC SnoopIso14443(void) int triggered = TRUE; FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - // The command (reader -> tag) that we're working on receiving. - uint8_t *receivedCmd = (uint8_t *)(BigBuf) + DEMOD_TRACE_SIZE; - // The response (tag -> reader) that we're working on receiving. - uint8_t *receivedResponse = (uint8_t *)(BigBuf) + DEMOD_TRACE_SIZE + READER_TAG_BUFFER_SIZE; - // As we receive stuff, we copy it from receivedCmd or receivedResponse - // into trace, along with its length and other annotations. - uint8_t *trace = (uint8_t *)BigBuf; - int traceLen = 0; + clear_trace(); + set_tracing(TRUE); + + // The command (reader -> tag) that we're receiving. + uint8_t *receivedCmd = ((uint8_t *)BigBuf) + RECV_CMD_OFFSET; + + // The response (tag -> reader) that we're receiving. + uint8_t *receivedResponse = ((uint8_t *)BigBuf) + RECV_RESP_OFFSET; // The DMA buffer, used to stream samples from the FPGA. - int8_t *dmaBuf = (int8_t *)(BigBuf) + DEMOD_TRACE_SIZE + READER_TAG_BUFFER_SIZE + TAG_READER_BUFFER_SIZE; + int8_t *dmaBuf = ((int8_t *)BigBuf) + DMA_BUFFER_OFFSET; int lastRxCounter; int8_t *upTo; int ci, cq; @@ -1052,30 +1062,20 @@ void RAMFUNC SnoopIso14443(void) // information in the trace buffer. int samples = 0; - // Initialize the trace buffer - memset(trace, 0x44, DEMOD_TRACE_SIZE); - - // Set up the demodulator for tag -> reader responses. - Demod.output = receivedResponse; - Demod.len = 0; - Demod.state = DEMOD_UNSYNCD; - - // And the reader -> tag commands - memset(&Uart, 0, sizeof(Uart)); - Uart.output = receivedCmd; - Uart.byteCntMax = 100; - Uart.state = STATE_UNSYNCD; + DemodReset(); + UartReset(); // Print some debug information about the buffer sizes Dbprintf("Snooping buffers initialized:"); - Dbprintf(" Trace: %i bytes", DEMOD_TRACE_SIZE); - Dbprintf(" Reader -> tag: %i bytes", READER_TAG_BUFFER_SIZE); - Dbprintf(" tag -> Reader: %i bytes", TAG_READER_BUFFER_SIZE); - Dbprintf(" DMA: %i bytes", DEMOD_DMA_BUFFER_SIZE); + Dbprintf(" Trace: %i bytes", TRACE_SIZE); + Dbprintf(" Reader -> tag: %i bytes", MAX_FRAME_SIZE); + Dbprintf(" tag -> Reader: %i bytes", MAX_FRAME_SIZE); + Dbprintf(" DMA: %i bytes", DMA_BUFFER_SIZE); - // And put the FPGA in the appropriate mode - // Signal field is off with the appropriate LED - LED_D_OFF(); + // Signal field is off with the appropriate LED + LED_D_OFF(); + + // And put the FPGA in the appropriate mode FpgaWriteConfWord( FPGA_MAJOR_MODE_HF_READER_RX_XCORR | FPGA_HF_READER_RX_XCORR_848_KHZ | FPGA_HF_READER_RX_XCORR_SNOOP); @@ -1084,20 +1084,20 @@ void RAMFUNC SnoopIso14443(void) // Setup for the DMA. FpgaSetupSsc(); upTo = dmaBuf; - lastRxCounter = DEMOD_DMA_BUFFER_SIZE; - FpgaSetupSscDma((uint8_t *)dmaBuf, DEMOD_DMA_BUFFER_SIZE); - + lastRxCounter = DMA_BUFFER_SIZE; + FpgaSetupSscDma((uint8_t *)dmaBuf, DMA_BUFFER_SIZE); + uint8_t parity[MAX_PARITY_SIZE]; LED_A_ON(); // And now we loop, receiving samples. for(;;) { int behindBy = (lastRxCounter - AT91C_BASE_PDC_SSC->PDC_RCR) & - (DEMOD_DMA_BUFFER_SIZE-1); + (DMA_BUFFER_SIZE-1); if(behindBy > maxBehindBy) { maxBehindBy = behindBy; - if(behindBy > (DEMOD_DMA_BUFFER_SIZE-2)) { // TODO: understand whether we can increase/decrease as we want or not? + if(behindBy > (DMA_BUFFER_SIZE-2)) { // TODO: understand whether we can increase/decrease as we want or not? Dbprintf("blew circular buffer! behindBy=0x%x", behindBy); - goto done; + break; } } if(behindBy < 2) continue; @@ -1106,42 +1106,37 @@ void RAMFUNC SnoopIso14443(void) cq = upTo[1]; upTo += 2; lastRxCounter -= 2; - if(upTo - dmaBuf > DEMOD_DMA_BUFFER_SIZE) { - upTo -= DEMOD_DMA_BUFFER_SIZE; - lastRxCounter += DEMOD_DMA_BUFFER_SIZE; + if(upTo - dmaBuf > DMA_BUFFER_SIZE) { + upTo -= DMA_BUFFER_SIZE; + lastRxCounter += DMA_BUFFER_SIZE; AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) upTo; - AT91C_BASE_PDC_SSC->PDC_RNCR = DEMOD_DMA_BUFFER_SIZE; + AT91C_BASE_PDC_SSC->PDC_RNCR = DMA_BUFFER_SIZE; } samples += 2; -#define HANDLE_BIT_IF_BODY \ - if(triggered && tracing) {\ - uint8_t parity[MAX_PARITY_SIZE];\ - GetParity(receivedCmd, Uart.byteCnt, parity);\ - LogTrace(receivedCmd,Uart.byteCnt,samples, samples,parity,TRUE);\ - if(!tracing) {\ - DbpString("Reached trace limit");\ - break;\ - }\ - }\ - /* And ready to receive another command. */ \ - memset(&Uart, 0, sizeof(Uart)); \ - Uart.output = receivedCmd; \ - Uart.byteCntMax = 100; \ - Uart.state = STATE_UNSYNCD; \ - /* And also reset the demod code, which might have been */ \ - /* false-triggered by the commands from the reader. */ \ - memset(&Demod, 0, sizeof(Demod)); \ - Demod.output = receivedResponse; \ - Demod.state = DEMOD_UNSYNCD; \ - if(Handle14443UartBit(ci & 1)) { - HANDLE_BIT_IF_BODY - } + if(triggered && tracing) { + GetParity(receivedCmd, Uart.byteCnt, parity); + LogTrace(receivedCmd,Uart.byteCnt,samples, samples,parity,TRUE); + } + /* And ready to receive another command. */ + UartReset(); + /* And also reset the demod code, which might have been */ + /* false-triggered by the commands from the reader. */ + DemodReset(); + } if(Handle14443UartBit(cq & 1)) { - HANDLE_BIT_IF_BODY - } + if(triggered && tracing) { + GetParity(receivedCmd, Uart.byteCnt, parity); + LogTrace(receivedCmd,Uart.byteCnt,samples, samples,parity,TRUE); + } + /* And ready to receive another command. */ + UartReset(); + /* And also reset the demod code, which might have been */ + /* false-triggered by the commands from the reader. */ + DemodReset(); + } if(Handle14443SamplesDemod(ci, cq)) { @@ -1151,33 +1146,31 @@ void RAMFUNC SnoopIso14443(void) uint8_t parity[MAX_PARITY_SIZE]; GetParity(receivedResponse, Demod.len, parity); LogTrace(receivedResponse,Demod.len,samples, samples,parity,FALSE); - if(!tracing) { - DbpString("Reached trace limit"); - break; - } } triggered = TRUE; LED_A_OFF(); LED_B_ON(); // And ready to receive another response. - memset(&Demod, 0, sizeof(Demod)); - Demod.output = receivedResponse; - Demod.state = DEMOD_UNSYNCD; + DemodReset(); } - WDT_HIT(); + WDT_HIT(); + + if(!tracing) { + DbpString("Reached trace limit"); + break; + } if(BUTTON_PRESS()) { DbpString("cancelled"); - goto done; + break; } } - -done: + FpgaDisableSscDma(); LED_A_OFF(); LED_B_OFF(); LED_C_OFF(); - AT91C_BASE_PDC_SSC->PDC_PTCR = AT91C_PDC_RXTDIS; + AT91C_BASE_PDC_SSC->PDC_PTCR = AT91C_PDC_RXTDIS; DbpString("Snoop statistics:"); Dbprintf(" Max behind by: %i", maxBehindBy); Dbprintf(" Uart State: %x", Uart.state); From 03dc174036b7258baf1ef2504e810a685163137e Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Tue, 27 Jan 2015 09:06:01 +0100 Subject: [PATCH 07/25] Minor refactoring --- armsrc/iso14443.c | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/armsrc/iso14443.c b/armsrc/iso14443.c index 0a6d2d67..39112fdf 100644 --- a/armsrc/iso14443.c +++ b/armsrc/iso14443.c @@ -154,7 +154,7 @@ static struct { static int Handle14443UartBit(int bit) { switch(Uart.state) { - case STATE_UNSYNCD: + case STATE_UNSYNCD: LED_A_OFF(); if(!bit) { // we went low, so this could be the beginning @@ -1045,12 +1045,6 @@ void RAMFUNC SnoopIso14443(void) clear_trace(); set_tracing(TRUE); - // The command (reader -> tag) that we're receiving. - uint8_t *receivedCmd = ((uint8_t *)BigBuf) + RECV_CMD_OFFSET; - - // The response (tag -> reader) that we're receiving. - uint8_t *receivedResponse = ((uint8_t *)BigBuf) + RECV_RESP_OFFSET; - // The DMA buffer, used to stream samples from the FPGA. int8_t *dmaBuf = ((int8_t *)BigBuf) + DMA_BUFFER_OFFSET; int lastRxCounter; @@ -1117,8 +1111,8 @@ void RAMFUNC SnoopIso14443(void) if(Handle14443UartBit(ci & 1)) { if(triggered && tracing) { - GetParity(receivedCmd, Uart.byteCnt, parity); - LogTrace(receivedCmd,Uart.byteCnt,samples, samples,parity,TRUE); + GetParity(Uart.output, Uart.byteCnt, parity); + LogTrace(Uart.output,Uart.byteCnt,samples, samples,parity,TRUE); } /* And ready to receive another command. */ UartReset(); @@ -1128,8 +1122,8 @@ void RAMFUNC SnoopIso14443(void) } if(Handle14443UartBit(cq & 1)) { if(triggered && tracing) { - GetParity(receivedCmd, Uart.byteCnt, parity); - LogTrace(receivedCmd,Uart.byteCnt,samples, samples,parity,TRUE); + GetParity(Uart.output, Uart.byteCnt, parity); + LogTrace(Uart.output,Uart.byteCnt,samples, samples,parity,TRUE); } /* And ready to receive another command. */ UartReset(); @@ -1144,8 +1138,8 @@ void RAMFUNC SnoopIso14443(void) if(tracing) { uint8_t parity[MAX_PARITY_SIZE]; - GetParity(receivedResponse, Demod.len, parity); - LogTrace(receivedResponse,Demod.len,samples, samples,parity,FALSE); + GetParity(Demod.output, Demod.len, parity); + LogTrace(Demod.output,Demod.len,samples, samples,parity,FALSE); } triggered = TRUE; LED_A_OFF(); From 16b75f27c3e688b42c7d277dd1e6e99e32cc38c7 Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Tue, 27 Jan 2015 16:34:11 +0100 Subject: [PATCH 08/25] Minor tweaks to iso14443b snoop tracing --- armsrc/iso14443.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/armsrc/iso14443.c b/armsrc/iso14443.c index 39112fdf..1191c5bf 100644 --- a/armsrc/iso14443.c +++ b/armsrc/iso14443.c @@ -633,6 +633,8 @@ static void UartReset() Uart.output = ((uint8_t *)BigBuf) + RECV_CMD_OFFSET; Uart.byteCntMax = MAX_FRAME_SIZE; Uart.state = STATE_UNSYNCD; + Uart.byteCnt = 0; + Uart.bitCnt = 0; } /* @@ -1109,11 +1111,13 @@ void RAMFUNC SnoopIso14443(void) samples += 2; - if(Handle14443UartBit(ci & 1)) { + if(Handle14443UartBit(ci & 1)) { if(triggered && tracing) { GetParity(Uart.output, Uart.byteCnt, parity); LogTrace(Uart.output,Uart.byteCnt,samples, samples,parity,TRUE); } + if(Uart.byteCnt==0) Dbprintf("[1] Error, Uart.byteCnt==0, Uart.bitCnt=%d", Uart.bitCnt); + /* And ready to receive another command. */ UartReset(); /* And also reset the demod code, which might have been */ @@ -1125,6 +1129,8 @@ void RAMFUNC SnoopIso14443(void) GetParity(Uart.output, Uart.byteCnt, parity); LogTrace(Uart.output,Uart.byteCnt,samples, samples,parity,TRUE); } + if(Uart.byteCnt==0) Dbprintf("[2] Error, Uart.byteCnt==0, Uart.bitCnt=%d", Uart.bitCnt); + /* And ready to receive another command. */ UartReset(); /* And also reset the demod code, which might have been */ From 08e8317c2168fbdc788afe7b19f4c918bf1e4a6f Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Tue, 27 Jan 2015 16:34:45 +0100 Subject: [PATCH 09/25] More annotations to iso14443b protocol listings --- client/cmdhf.c | 41 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/client/cmdhf.c b/client/cmdhf.c index 37366802..0f31da4d 100644 --- a/client/cmdhf.c +++ b/client/cmdhf.c @@ -158,9 +158,28 @@ NXP/Philips CUSTOM COMMANDS #define MIFARE_ULC_AUTH_1 0x1A #define MIFARE_ULC_AUTH_2 0xAF +/** +06 00 = INITIATE +0E xx = SELECT ID (xx = Chip-ID) +0B = Get UID +08 yy = Read Block (yy = block number) +09 yy dd dd dd dd = Write Block (yy = block number; dd dd dd dd = data to be written) +0C = Reset to Inventory +0F = Completion +0A 11 22 33 44 55 66 = Authenticate (11 22 33 44 55 66 = data to authenticate) +**/ + #define ISO14443B_REQB 0x05 #define ISO14443B_ATTRIB 0x1D #define ISO14443B_HALT 0x50 +#define ISO14443B_INITIATE 0x06 +#define ISO14443B_SELECT 0x0E +#define ISO14443B_GET_UID 0x0B +#define ISO14443B_READ_BLK 0x08 +#define ISO14443B_WRITE_BLK 0x09 +#define ISO14443B_RESET 0x0C +#define ISO14443B_COMPLETION 0x0F +#define ISO14443B_AUTHENTICATE 0x0A //First byte is 26 #define ISO15693_INVENTORY 0x01 @@ -288,13 +307,33 @@ void annotateIso15693(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize) } } } + +/** +06 00 = INITIATE +0E xx = SELECT ID (xx = Chip-ID) +0B = Get UID +08 yy = Read Block (yy = block number) +09 yy dd dd dd dd = Write Block (yy = block number; dd dd dd dd = data to be written) +0C = Reset to Inventory +0F = Completion +0A 11 22 33 44 55 66 = Authenticate (11 22 33 44 55 66 = data to authenticate) +**/ + void annotateIso14443b(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize) { switch(cmd[0]){ case ISO14443B_REQB : snprintf(exp,size,"REQB");break; case ISO14443B_ATTRIB : snprintf(exp,size,"ATTRIB");break; case ISO14443B_HALT : snprintf(exp,size,"HALT");break; - default: snprintf(exp,size ,"?");break; + case ISO14443B_INITIATE : snprintf(exp,size,"INITIATE");break; + case ISO14443B_SELECT : snprintf(exp,size,"SELECT(%d)",cmd[1]);break; + case ISO14443B_GET_UID : snprintf(exp,size,"GET UID");break; + case ISO14443B_READ_BLK : snprintf(exp,size,"READ_BLK(%d)", cmd[1]);break; + case ISO14443B_WRITE_BLK : snprintf(exp,size,"WRITE_BLK(%d)",cmd[1]);break; + case ISO14443B_RESET : snprintf(exp,size,"RESET");break; + case ISO14443B_COMPLETION : snprintf(exp,size,"COMPLETION");break; + case ISO14443B_AUTHENTICATE : snprintf(exp,size,"AUTHENTICATE");break; + default : snprintf(exp,size ,"?");break; } } From 3000dc4e7e72dd8661d174f7bcde511ff73eb99f Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Sat, 7 Feb 2015 20:49:40 +0100 Subject: [PATCH 10/25] Generic tracing; removed iso14a_XX-functions, removed traceLen as global varible --- armsrc/BigBuf.c | 96 ++++++++++++++++++++++++++++++++++++++++++-- armsrc/BigBuf.h | 5 ++- armsrc/appmain.c | 5 ++- armsrc/apps.h | 3 +- armsrc/hitag2.c | 12 +++--- armsrc/iclass.c | 25 ++++++------ armsrc/iso14443.c | 2 +- armsrc/iso14443a.c | 32 +++++++-------- armsrc/mifarecmd.c | 42 +++++++++---------- armsrc/mifaresniff.c | 8 ++-- armsrc/util.c | 88 ---------------------------------------- armsrc/util.h | 9 ----- 12 files changed, 160 insertions(+), 167 deletions(-) diff --git a/armsrc/BigBuf.c b/armsrc/BigBuf.c index 7f56e9a0..1b5e5482 100644 --- a/armsrc/BigBuf.c +++ b/armsrc/BigBuf.c @@ -26,10 +26,9 @@ static uint16_t BigBuf_hi = BIGBUF_SIZE; // pointer to the emulator memory. static uint8_t *emulator_memory = NULL; -// trace related global variables -// (only one left). ToDo: make this static as well? -uint16_t traceLen = 0; - +// trace related variables +static uint16_t traceLen = 0; +int tracing = 1; //Last global one.. todo static? // get the address of BigBuf uint8_t *BigBuf_get_addr(void) @@ -95,3 +94,92 @@ uint16_t BigBuf_max_traceLen(void) { return BigBuf_hi; } + +void clear_trace() { + uint8_t *trace = BigBuf_get_addr(); + uint16_t max_traceLen = BigBuf_max_traceLen(); + memset(trace, 0x44, max_traceLen); + traceLen = 0; +} + +void set_tracing(bool enable) { + tracing = enable; +} + +/** + * Get the number of bytes traced + * @return + */ +uint16_t BigBuf_get_traceLen(void) +{ + return traceLen; +} + +/** + This is a function to store traces. All protocols can use this generic tracer-function. + The traces produced by calling this function can be fetched on the client-side + by 'hf list raw', alternatively 'hf list ' for protocol-specific + annotation of commands/responses. + +**/ +bool RAMFUNC LogTrace(const uint8_t *btBytes, uint16_t iLen, uint32_t timestamp_start, uint32_t timestamp_end, uint8_t *parity, bool readerToTag) +{ + if (!tracing) return FALSE; + + uint8_t *trace = BigBuf_get_addr(); + + uint16_t num_paritybytes = (iLen-1)/8 + 1; // number of valid paritybytes in *parity + uint16_t duration = timestamp_end - timestamp_start; + + // Return when trace is full + uint16_t max_traceLen = BigBuf_max_traceLen(); + + if (traceLen + sizeof(iLen) + sizeof(timestamp_start) + sizeof(duration) + num_paritybytes + iLen >= max_traceLen) { + tracing = FALSE; // don't trace any more + return FALSE; + } + // Traceformat: + // 32 bits timestamp (little endian) + // 16 bits duration (little endian) + // 16 bits data length (little endian, Highest Bit used as readerToTag flag) + // y Bytes data + // x Bytes parity (one byte per 8 bytes data) + + // timestamp (start) + trace[traceLen++] = ((timestamp_start >> 0) & 0xff); + trace[traceLen++] = ((timestamp_start >> 8) & 0xff); + trace[traceLen++] = ((timestamp_start >> 16) & 0xff); + trace[traceLen++] = ((timestamp_start >> 24) & 0xff); + + // duration + trace[traceLen++] = ((duration >> 0) & 0xff); + trace[traceLen++] = ((duration >> 8) & 0xff); + + // data length + trace[traceLen++] = ((iLen >> 0) & 0xff); + trace[traceLen++] = ((iLen >> 8) & 0xff); + + // readerToTag flag + if (!readerToTag) { + trace[traceLen - 1] |= 0x80; + } + + // data bytes + if (btBytes != NULL && iLen != 0) { + memcpy(trace + traceLen, btBytes, iLen); + } + traceLen += iLen; + + // parity bytes + if (parity != NULL && iLen != 0) { + memcpy(trace + traceLen, parity, num_paritybytes); + } + traceLen += num_paritybytes; + + if(traceLen +4 < max_traceLen) + { //If it hadn't been cleared, for whatever reason.. + memset(trace+traceLen,0x44, 4); + } + + return TRUE; +} diff --git a/armsrc/BigBuf.h b/armsrc/BigBuf.h index 9d89a4f0..5cca1000 100644 --- a/armsrc/BigBuf.h +++ b/armsrc/BigBuf.h @@ -29,6 +29,9 @@ extern uint8_t *BigBuf_malloc(uint16_t); extern void BigBuf_free(void); extern void BigBuf_free_keep_EM(void); -extern uint16_t traceLen; +uint16_t BigBuf_get_traceLen(void); +void clear_trace(); +void set_tracing(bool enable); +bool RAMFUNC LogTrace(const uint8_t *btBytes, uint16_t iLen, uint32_t timestamp_start, uint32_t timestamp_end, uint8_t *parity, bool readerToTag); #endif /* __BIGBUF_H */ diff --git a/armsrc/appmain.c b/armsrc/appmain.c index 7c50a51e..bbf772ac 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -24,6 +24,7 @@ #include "legicrf.h" #include #include "lfsampling.h" +#include "BigBuf.h" #ifdef WITH_LCD #include "LCD.h" #endif @@ -916,10 +917,10 @@ void UsbPacketReceived(uint8_t *packet, int len) uint8_t *BigBuf = BigBuf_get_addr(); for(size_t i=0; iarg[1]; i += USB_CMD_DATA_SIZE) { size_t len = MIN((c->arg[1] - i),USB_CMD_DATA_SIZE); - cmd_send(CMD_DOWNLOADED_RAW_ADC_SAMPLES_125K,i,len,traceLen,BigBuf+c->arg[0]+i,len); + cmd_send(CMD_DOWNLOADED_RAW_ADC_SAMPLES_125K,i,len,BigBuf_get_traceLen(),BigBuf+c->arg[0]+i,len); } // Trigger a finish downloading signal with an ACK frame - cmd_send(CMD_ACK,1,0,traceLen,getSamplingConfig(),sizeof(sample_config)); + cmd_send(CMD_ACK,1,0,BigBuf_get_traceLen(),getSamplingConfig(),sizeof(sample_config)); LED_B_OFF(); break; diff --git a/armsrc/apps.h b/armsrc/apps.h index 58a2a621..0f7714e7 100644 --- a/armsrc/apps.h +++ b/armsrc/apps.h @@ -148,8 +148,7 @@ void ReaderIso14443a(UsbCommand * c); bool RAMFUNC LogTrace(const uint8_t *btBytes, uint16_t len, uint32_t timestamp_start, uint32_t timestamp_end, uint8_t *parity, bool readerToTag); void GetParity(const uint8_t *pbtCmd, uint16_t len, uint8_t *parity); void iso14a_set_trigger(bool enable); -void iso14a_clear_trace(); -void iso14a_set_tracing(bool enable); + void RAMFUNC SniffMifare(uint8_t param); /// epa.h diff --git a/armsrc/hitag2.c b/armsrc/hitag2.c index 4a2d9d9d..7ca79930 100644 --- a/armsrc/hitag2.c +++ b/armsrc/hitag2.c @@ -742,8 +742,8 @@ void SnoopHitag(uint32_t type) { memset(auth_table, 0x00, AUTH_TABLE_LENGTH); // Clean up trace and prepare it for storing frames - iso14a_set_tracing(TRUE); - iso14a_clear_trace(); + set_tracing(TRUE); + clear_trace(); DbpString("Starting Hitag2 snoop"); LED_D_ON(); @@ -955,8 +955,8 @@ void SimulateHitagTag(bool tag_mem_supplied, byte_t* data) { memset(auth_table, 0x00, AUTH_TABLE_LENGTH); // Clean up trace and prepare it for storing frames - iso14a_set_tracing(TRUE); - iso14a_clear_trace(); + set_tracing(TRUE); + clear_trace(); DbpString("Starting Hitag2 simulation"); LED_D_ON(); @@ -1142,8 +1142,8 @@ void ReaderHitag(hitag_function htf, hitag_data* htd) { bSuccessful = false; // Clean up trace and prepare it for storing frames - iso14a_set_tracing(TRUE); - iso14a_clear_trace(); + set_tracing(TRUE); + clear_trace(); DbpString("Starting Hitag reader family"); diff --git a/armsrc/iclass.c b/armsrc/iclass.c index 1a375118..41c9b8b5 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -652,9 +652,8 @@ void RAMFUNC SnoopIClass(void) // The DMA buffer, used to stream samples from the FPGA uint8_t *dmaBuf = BigBuf_malloc(DMA_BUFFER_SIZE); - // reset traceLen to 0 - iso14a_set_tracing(TRUE); - iso14a_clear_trace(); + set_tracing(TRUE); + clear_trace(); iso14a_set_trigger(FALSE); int lastRxCounter; @@ -805,12 +804,12 @@ void RAMFUNC SnoopIClass(void) DbpString("COMMAND FINISHED"); Dbprintf("%x %x %x", maxBehindBy, Uart.state, Uart.byteCnt); - Dbprintf("%x %x %x", Uart.byteCntMax, traceLen, (int)Uart.output[0]); + Dbprintf("%x %x %x", Uart.byteCntMax, BigBuf_get_traceLen(), (int)Uart.output[0]); done: AT91C_BASE_PDC_SSC->PDC_PTCR = AT91C_PDC_RXTDIS; Dbprintf("%x %x %x", maxBehindBy, Uart.state, Uart.byteCnt); - Dbprintf("%x %x %x", Uart.byteCntMax, traceLen, (int)Uart.output[0]); + Dbprintf("%x %x %x", Uart.byteCntMax, BigBuf_get_traceLen(), (int)Uart.output[0]); LED_A_OFF(); LED_B_OFF(); LED_C_OFF(); @@ -987,8 +986,8 @@ void SimulateIClass(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain FpgaDownloadAndGo(FPGA_BITSTREAM_HF); // Enable and clear the trace - iso14a_set_tracing(TRUE); - iso14a_clear_trace(); + set_tracing(TRUE); + clear_trace(); uint8_t csn_crc[] = { 0x03, 0x1f, 0xec, 0x8a, 0xf7, 0xff, 0x12, 0xe0, 0x00, 0x00 }; if(simType == 0) { @@ -1488,8 +1487,8 @@ void setupIclassReader() { FpgaDownloadAndGo(FPGA_BITSTREAM_HF); // Reset trace buffer - iso14a_set_tracing(TRUE); - iso14a_clear_trace(); + set_tracing(TRUE); + clear_trace(); // Setup SSC FpgaSetupSsc(); @@ -1585,14 +1584,14 @@ void ReaderIClass(uint8_t arg0) { int read_status= 0; bool abort_after_read = arg0 & FLAG_ICLASS_READER_ONLY_ONCE; bool get_cc = arg0 & FLAG_ICLASS_READER_GET_CC; - + set_tracing(TRUE); setupIclassReader(); size_t datasize = 0; while(!BUTTON_PRESS()) { - if(traceLen > BigBuf_max_traceLen()) { + if(!tracing) { DbpString("Trace full"); break; } @@ -1658,13 +1657,13 @@ void ReaderIClass_Replay(uint8_t arg0, uint8_t *MAC) { uint8_t resp[ICLASS_BUFFER_SIZE]; setupIclassReader(); - + set_tracing(TRUE); while(!BUTTON_PRESS()) { WDT_HIT(); - if(traceLen > BigBuf_max_traceLen()) { + if(!tracing) { DbpString("Trace full"); break; } diff --git a/armsrc/iso14443.c b/armsrc/iso14443.c index 92d05782..c7f49f14 100644 --- a/armsrc/iso14443.c +++ b/armsrc/iso14443.c @@ -1192,7 +1192,7 @@ void RAMFUNC SnoopIso14443(void) Dbprintf(" Uart State: %x", Uart.state); Dbprintf(" Uart ByteCnt: %i", Uart.byteCnt); Dbprintf(" Uart ByteCntMax: %i", Uart.byteCntMax); - Dbprintf(" Trace length: %i", traceLen); + Dbprintf(" Trace length: %i", BigBuf_get_traceLen()); } /* diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index 336250ed..05ffb941 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -20,7 +20,7 @@ #include "iso14443a.h" #include "crapto1.h" #include "mifareutil.h" - +#include "BigBuf.h" static uint32_t iso14a_timeout; int rsamples = 0; uint8_t trigger = 0; @@ -549,8 +549,8 @@ void RAMFUNC SnoopIso14443a(uint8_t param) { uint8_t *dmaBuf = BigBuf_malloc(DMA_BUFFER_SIZE); // init trace buffer - iso14a_clear_trace(); - iso14a_set_tracing(TRUE); + clear_trace(); + set_tracing(TRUE); uint8_t *data = dmaBuf; uint8_t previous_data = 0; @@ -674,7 +674,7 @@ void RAMFUNC SnoopIso14443a(uint8_t param) { FpgaDisableSscDma(); Dbprintf("maxDataLen=%d, Uart.state=%x, Uart.len=%d", maxDataLen, Uart.state, Uart.len); - Dbprintf("traceLen=%d, Uart.output[0]=%08x", traceLen, (uint32_t)Uart.output[0]); + Dbprintf("traceLen=%d, Uart.output[0]=%08x", BigBuf_get_traceLen(), (uint32_t)Uart.output[0]); LEDsoff(); } @@ -1010,8 +1010,8 @@ void SimulateIso14443aTag(int tagType, int uid_1st, int uid_2nd, byte_t* data) free_buffer_pointer = BigBuf_malloc(ALLOCATED_TAG_MODULATION_BUFFER_SIZE); // clear trace - iso14a_clear_trace(); - iso14a_set_tracing(TRUE); + clear_trace(); + set_tracing(TRUE); // Prepare the responses of the anticollision phase // there will be not enough time to do this at the moment the reader sends it REQA @@ -1867,10 +1867,10 @@ void ReaderIso14443a(UsbCommand *c) uint8_t par[MAX_PARITY_SIZE]; if(param & ISO14A_CONNECT) { - iso14a_clear_trace(); + clear_trace(); } - iso14a_set_tracing(TRUE); + set_tracing(TRUE); if(param & ISO14A_REQUEST_TRIGGER) { iso14a_set_trigger(TRUE); @@ -1966,8 +1966,8 @@ void ReaderMifare(bool first_try) // free eventually allocated BigBuf memory. We want all for tracing. BigBuf_free(); - iso14a_clear_trace(); - iso14a_set_tracing(TRUE); + clear_trace(); + set_tracing(TRUE); byte_t nt_diff = 0; uint8_t par[1] = {0}; // maximum 8 Bytes to be sent here, 1 byte parity is therefore enough @@ -2140,7 +2140,7 @@ void ReaderMifare(bool first_try) FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LEDsoff(); - iso14a_set_tracing(FALSE); + set_tracing(FALSE); } /** @@ -2198,8 +2198,8 @@ void Mifare1ksim(uint8_t flags, uint8_t exitAfterNReads, uint8_t arg2, uint8_t * // free eventually allocated BigBuf memory but keep Emulator Memory BigBuf_free_keep_EM(); // clear trace - iso14a_clear_trace(); - iso14a_set_tracing(TRUE); + clear_trace(); + set_tracing(TRUE); // Authenticate response - nonce uint32_t nonce = bytes_to_num(rAUTH_NT, 4); @@ -2644,7 +2644,7 @@ void Mifare1ksim(uint8_t flags, uint8_t exitAfterNReads, uint8_t arg2, uint8_t * } } } - if (MF_DBGLEVEL >= 1) Dbprintf("Emulator stopped. Tracing: %d trace length: %d ", tracing, traceLen); + if (MF_DBGLEVEL >= 1) Dbprintf("Emulator stopped. Tracing: %d trace length: %d ", tracing, BigBuf_get_traceLen()); } @@ -2661,8 +2661,8 @@ void RAMFUNC SniffMifare(uint8_t param) { // C(red) A(yellow) B(green) LEDsoff(); // init trace buffer - iso14a_clear_trace(); - iso14a_set_tracing(TRUE); + clear_trace(); + set_tracing(TRUE); // The command (reader -> tag) that we're receiving. // The length of a received command will in most cases be no more than 18 bytes. diff --git a/armsrc/mifarecmd.c b/armsrc/mifarecmd.c index 4279e63f..a16cbf16 100644 --- a/armsrc/mifarecmd.c +++ b/armsrc/mifarecmd.c @@ -41,7 +41,7 @@ void MifareReadBlock(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain) pcs = &mpcs; // clear trace - iso14a_clear_trace(); + clear_trace(); iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); LED_A_ON(); @@ -98,7 +98,7 @@ void MifareUC_Auth1(uint8_t arg0, uint8_t *datain){ LED_B_OFF(); LED_C_OFF(); - iso14a_clear_trace(); + clear_trace(); iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); if(!iso14443a_select_card(uid, NULL, &cuid)) { @@ -162,7 +162,7 @@ void MifareUReadBlock(uint8_t arg0,uint8_t *datain) LED_B_OFF(); LED_C_OFF(); - iso14a_clear_trace(); + clear_trace(); iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); int len = iso14443a_select_card(uid, NULL, &cuid); @@ -213,7 +213,7 @@ void MifareReadSector(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain) pcs = &mpcs; // clear trace - iso14a_clear_trace(); + clear_trace(); iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); @@ -276,7 +276,7 @@ void MifareUReadCard(uint8_t arg0, int arg1, uint8_t *datain) if (MF_DBGLEVEL >= MF_DBG_ALL) Dbprintf("Pages %d",Pages); - iso14a_clear_trace(); + clear_trace(); iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); int len = iso14443a_select_card(uid, NULL, &cuid); @@ -350,7 +350,7 @@ void MifareWriteBlock(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain) pcs = &mpcs; // clear trace - iso14a_clear_trace(); + clear_trace(); iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); @@ -411,7 +411,7 @@ void MifareUWriteBlock(uint8_t arg0, uint8_t *datain) uint8_t uid[10] = {0x00}; uint32_t cuid; - iso14a_clear_trace(); + clear_trace(); iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); LED_A_ON(); @@ -458,7 +458,7 @@ void MifareUWriteBlock_Special(uint8_t arg0, uint8_t *datain) uint8_t uid[10] = {0x00}; uint32_t cuid; - iso14a_clear_trace(); + clear_trace(); iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); LED_A_ON(); @@ -537,8 +537,8 @@ void MifareNested(uint32_t arg0, uint32_t arg1, uint32_t calibrate, uint8_t *dat // free eventually allocated BigBuf memory BigBuf_free(); // clear trace - iso14a_clear_trace(); - iso14a_set_tracing(false); + clear_trace(); + set_tracing(false); iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); @@ -709,7 +709,7 @@ void MifareNested(uint32_t arg0, uint32_t arg1, uint32_t calibrate, uint8_t *dat FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LEDsoff(); - iso14a_set_tracing(TRUE); + set_tracing(TRUE); } //----------------------------------------------------------------------------- @@ -738,8 +738,8 @@ void MifareChkKeys(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain) MF_DBGLEVEL = MF_DBG_NONE; // clear trace - iso14a_clear_trace(); - iso14a_set_tracing(TRUE); + clear_trace(); + set_tracing(TRUE); iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); @@ -829,8 +829,8 @@ void MifareECardLoad(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datai uint8_t uid[10]; // clear trace - iso14a_clear_trace(); - iso14a_set_tracing(false); + clear_trace(); + set_tracing(false); iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); @@ -931,8 +931,8 @@ void MifareCSetBlock(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datai LED_B_OFF(); LED_C_OFF(); - iso14a_clear_trace(); - iso14a_set_tracing(TRUE); + clear_trace(); + set_tracing(TRUE); iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); } @@ -1049,8 +1049,8 @@ void MifareCGetBlock(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datai LED_B_OFF(); LED_C_OFF(); - iso14a_clear_trace(); - iso14a_set_tracing(TRUE); + clear_trace(); + set_tracing(TRUE); iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); } @@ -1136,7 +1136,7 @@ void Mifare_DES_Auth1(uint8_t arg0, uint8_t *datain){ uint8_t uid[10] = {0x00}; uint32_t cuid; - iso14a_clear_trace(); + clear_trace(); iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); int len = iso14443a_select_card(uid, NULL, &cuid); @@ -1183,4 +1183,4 @@ void Mifare_DES_Auth2(uint32_t arg0, uint8_t *datain){ cmd_send(CMD_ACK, isOK, 0, 0, dataout, sizeof(dataout)); FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LEDsoff(); -} +} diff --git a/armsrc/mifaresniff.c b/armsrc/mifaresniff.c index 59e84697..0cc2963b 100644 --- a/armsrc/mifaresniff.c +++ b/armsrc/mifaresniff.c @@ -139,7 +139,7 @@ bool RAMFUNC MfSniffLogic(const uint8_t *data, uint16_t len, uint8_t *parity, ui } bool RAMFUNC MfSniffSend(uint16_t maxTimeoutMs) { - if (traceLen && (GetTickCount() > timerData + maxTimeoutMs)) { + if (BigBuf_get_traceLen() && (GetTickCount() > timerData + maxTimeoutMs)) { return intMfSniffSend(); } return FALSE; @@ -149,7 +149,7 @@ bool RAMFUNC MfSniffSend(uint16_t maxTimeoutMs) { bool intMfSniffSend() { int pckSize = 0; - int pckLen = traceLen; + int pckLen = BigBuf_get_traceLen(); int pckNum = 0; uint8_t *trace = BigBuf_get_addr(); @@ -157,7 +157,7 @@ bool intMfSniffSend() { while (pckLen > 0) { pckSize = MIN(USB_CMD_DATA_SIZE, pckLen); LED_B_ON(); - cmd_send(CMD_ACK, 1, traceLen, pckSize, trace + traceLen - pckLen, pckSize); + cmd_send(CMD_ACK, 1, BigBuf_get_traceLen(), pckSize, trace + BigBuf_get_traceLen() - pckLen, pckSize); LED_B_OFF(); pckLen -= pckSize; @@ -168,7 +168,7 @@ bool intMfSniffSend() { cmd_send(CMD_ACK,2,0,0,0,0); LED_B_OFF(); - iso14a_clear_trace(); + clear_trace(); return TRUE; } diff --git a/armsrc/util.c b/armsrc/util.c index 4948fce8..88931927 100644 --- a/armsrc/util.c +++ b/armsrc/util.c @@ -14,7 +14,6 @@ #include "apps.h" #include "BigBuf.h" -int tracing = TRUE; void print_result(char *name, uint8_t *buf, size_t len) { @@ -429,91 +428,4 @@ uint32_t RAMFUNC GetCountSspClk(){ return tmp_count; } } -void iso14a_clear_trace() { - clear_trace(); -} - -void iso14a_set_tracing(bool enable) { - set_tracing(enable); -} - -void clear_trace() { - uint8_t *trace = BigBuf_get_addr(); - uint16_t max_traceLen = BigBuf_max_traceLen(); - memset(trace, 0x44, max_traceLen); - traceLen = 0; -} - -void set_tracing(bool enable) { - tracing = enable; -} - -/** - This is a function to store traces. All protocols can use this generic tracer-function. - The traces produced by calling this function can be fetched on the client-side - by 'hf list raw', alternatively 'hf list ' for protocol-specific - annotation of commands/responses. - -**/ -bool RAMFUNC LogTrace(const uint8_t *btBytes, uint16_t iLen, uint32_t timestamp_start, uint32_t timestamp_end, uint8_t *parity, bool readerToTag) -{ - if (!tracing) return FALSE; - - uint8_t *trace = BigBuf_get_addr(); - - uint16_t num_paritybytes = (iLen-1)/8 + 1; // number of valid paritybytes in *parity - uint16_t duration = timestamp_end - timestamp_start; - - // Return when trace is full - uint16_t max_traceLen = BigBuf_max_traceLen(); - - if (traceLen + sizeof(iLen) + sizeof(timestamp_start) + sizeof(duration) + num_paritybytes + iLen >= max_traceLen) { - tracing = FALSE; // don't trace any more - return FALSE; - } - // Traceformat: - // 32 bits timestamp (little endian) - // 16 bits duration (little endian) - // 16 bits data length (little endian, Highest Bit used as readerToTag flag) - // y Bytes data - // x Bytes parity (one byte per 8 bytes data) - - // timestamp (start) - trace[traceLen++] = ((timestamp_start >> 0) & 0xff); - trace[traceLen++] = ((timestamp_start >> 8) & 0xff); - trace[traceLen++] = ((timestamp_start >> 16) & 0xff); - trace[traceLen++] = ((timestamp_start >> 24) & 0xff); - - // duration - trace[traceLen++] = ((duration >> 0) & 0xff); - trace[traceLen++] = ((duration >> 8) & 0xff); - - // data length - trace[traceLen++] = ((iLen >> 0) & 0xff); - trace[traceLen++] = ((iLen >> 8) & 0xff); - - // readerToTag flag - if (!readerToTag) { - trace[traceLen - 1] |= 0x80; - } - - // data bytes - if (btBytes != NULL && iLen != 0) { - memcpy(trace + traceLen, btBytes, iLen); - } - traceLen += iLen; - - // parity bytes - if (parity != NULL && iLen != 0) { - memcpy(trace + traceLen, parity, num_paritybytes); - } - traceLen += num_paritybytes; - - if(traceLen +4 < max_traceLen) - { //If it hadn't been cleared, for whatever reason.. - memset(trace+traceLen,0x44, 4); - } - - return TRUE; -} diff --git a/armsrc/util.h b/armsrc/util.h index e0066302..bf5d0cc8 100644 --- a/armsrc/util.h +++ b/armsrc/util.h @@ -43,15 +43,6 @@ void LEDsoff(); int BUTTON_CLICKED(int ms); int BUTTON_HELD(int ms); void FormatVersionInformation(char *dst, int len, const char *prefix, void *version_information); -// @deprecated -void iso14a_clear_trace(); -// @deprecated -void iso14a_set_tracing(bool enable); -void clear_trace(); -void set_tracing(bool enable); - -// The function LogTrace() is also used by the iClass implementation in iclass.c and both iso14443a, iso14443b and mifare -bool RAMFUNC LogTrace(const uint8_t *btBytes, uint16_t iLen, uint32_t timestamp_start, uint32_t timestamp_end, uint8_t *parity, bool readerToTag); void StartTickCount(); uint32_t RAMFUNC GetTickCount(); From aabb719dc4e9af383e20c79647612755957bca00 Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Sat, 7 Feb 2015 20:55:17 +0100 Subject: [PATCH 11/25] Moved LogTraceHitag to BigBuf (no changes to the function ... yet) --- armsrc/BigBuf.c | 26 ++++++++++++++++++++++++++ armsrc/BigBuf.h | 2 +- armsrc/hitag2.c | 27 +-------------------------- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/armsrc/BigBuf.c b/armsrc/BigBuf.c index 1b5e5482..18db66f3 100644 --- a/armsrc/BigBuf.c +++ b/armsrc/BigBuf.c @@ -183,3 +183,29 @@ bool RAMFUNC LogTrace(const uint8_t *btBytes, uint16_t iLen, uint32_t timestamp_ return TRUE; } +int LogTraceHitag(const uint8_t * btBytes, int iBits, int iSamples, uint32_t dwParity, int bReader) +{ + static uint16_t traceLen = 0; + uint8_t *trace = BigBuf_get_addr(); + + // Return when trace is full + if (traceLen + sizeof(rsamples) + sizeof(dwParity) + sizeof(iBits) + nbytes(iBits) > BigBuf_max_traceLen()) return FALSE; + + // Trace the random, i'm curious + rsamples += iSamples; + trace[traceLen++] = ((rsamples >> 0) & 0xff); + trace[traceLen++] = ((rsamples >> 8) & 0xff); + trace[traceLen++] = ((rsamples >> 16) & 0xff); + trace[traceLen++] = ((rsamples >> 24) & 0xff); + if (!bReader) { + trace[traceLen - 1] |= 0x80; + } + trace[traceLen++] = ((dwParity >> 0) & 0xff); + trace[traceLen++] = ((dwParity >> 8) & 0xff); + trace[traceLen++] = ((dwParity >> 16) & 0xff); + trace[traceLen++] = ((dwParity >> 24) & 0xff); + trace[traceLen++] = iBits; + memcpy(trace + traceLen, btBytes, nbytes(iBits)); + traceLen += nbytes(iBits); + return TRUE; +} diff --git a/armsrc/BigBuf.h b/armsrc/BigBuf.h index 5cca1000..be558979 100644 --- a/armsrc/BigBuf.h +++ b/armsrc/BigBuf.h @@ -33,5 +33,5 @@ uint16_t BigBuf_get_traceLen(void); void clear_trace(); void set_tracing(bool enable); bool RAMFUNC LogTrace(const uint8_t *btBytes, uint16_t iLen, uint32_t timestamp_start, uint32_t timestamp_end, uint8_t *parity, bool readerToTag); - +int LogTraceHitag(const uint8_t * btBytes, int iBits, int iSamples, uint32_t dwParity, int bReader); #endif /* __BIGBUF_H */ diff --git a/armsrc/hitag2.c b/armsrc/hitag2.c index 7ca79930..4b173d6f 100644 --- a/armsrc/hitag2.c +++ b/armsrc/hitag2.c @@ -21,6 +21,7 @@ #include "util.h" #include "hitag2.h" #include "string.h" +#include "BigBuf.h" static bool bQuiet; @@ -30,32 +31,6 @@ static bool bPwd; static bool bSuccessful; -static int LogTraceHitag(const uint8_t * btBytes, int iBits, int iSamples, uint32_t dwParity, int bReader) -{ - static uint16_t traceLen = 0; - uint8_t *trace = BigBuf_get_addr(); - - // Return when trace is full - if (traceLen + sizeof(rsamples) + sizeof(dwParity) + sizeof(iBits) + nbytes(iBits) > BigBuf_max_traceLen()) return FALSE; - - // Trace the random, i'm curious - rsamples += iSamples; - trace[traceLen++] = ((rsamples >> 0) & 0xff); - trace[traceLen++] = ((rsamples >> 8) & 0xff); - trace[traceLen++] = ((rsamples >> 16) & 0xff); - trace[traceLen++] = ((rsamples >> 24) & 0xff); - if (!bReader) { - trace[traceLen - 1] |= 0x80; - } - trace[traceLen++] = ((dwParity >> 0) & 0xff); - trace[traceLen++] = ((dwParity >> 8) & 0xff); - trace[traceLen++] = ((dwParity >> 16) & 0xff); - trace[traceLen++] = ((dwParity >> 24) & 0xff); - trace[traceLen++] = iBits; - memcpy(trace + traceLen, btBytes, nbytes(iBits)); - traceLen += nbytes(iBits); - return TRUE; -} struct hitag2_tag { uint32_t uid; From 665775c844a3f8f1efe055cd25462b3cd7a7441a Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Sat, 7 Feb 2015 21:05:14 +0100 Subject: [PATCH 12/25] Some documentation and formatting to LogTraceHitag --- armsrc/BigBuf.c | 25 ++++++++++++++++++------- armsrc/util.c | 2 +- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/armsrc/BigBuf.c b/armsrc/BigBuf.c index 18db66f3..6e99a793 100644 --- a/armsrc/BigBuf.c +++ b/armsrc/BigBuf.c @@ -185,27 +185,38 @@ bool RAMFUNC LogTrace(const uint8_t *btBytes, uint16_t iLen, uint32_t timestamp_ } int LogTraceHitag(const uint8_t * btBytes, int iBits, int iSamples, uint32_t dwParity, int bReader) { - static uint16_t traceLen = 0; + + if (!tracing) return FALSE; + uint8_t *trace = BigBuf_get_addr(); - + uint16_t iLen = nbytes(iBits); // Return when trace is full - if (traceLen + sizeof(rsamples) + sizeof(dwParity) + sizeof(iBits) + nbytes(iBits) > BigBuf_max_traceLen()) return FALSE; + if (traceLen + sizeof(rsamples) + sizeof(dwParity) + sizeof(iBits) + iLen > BigBuf_max_traceLen()) return FALSE; + + //Hitag traces appear to use this traceformat: + // 32 bits timestamp (little endian,Highest Bit used as readerToTag flag) + // 32 bits parity + // 8 bits size (number of bits in the trace entry) + // y Bytes data - // Trace the random, i'm curious rsamples += iSamples; trace[traceLen++] = ((rsamples >> 0) & 0xff); trace[traceLen++] = ((rsamples >> 8) & 0xff); trace[traceLen++] = ((rsamples >> 16) & 0xff); trace[traceLen++] = ((rsamples >> 24) & 0xff); + if (!bReader) { - trace[traceLen - 1] |= 0x80; + trace[traceLen - 1] |= 0x80; } + trace[traceLen++] = ((dwParity >> 0) & 0xff); trace[traceLen++] = ((dwParity >> 8) & 0xff); trace[traceLen++] = ((dwParity >> 16) & 0xff); trace[traceLen++] = ((dwParity >> 24) & 0xff); trace[traceLen++] = iBits; - memcpy(trace + traceLen, btBytes, nbytes(iBits)); - traceLen += nbytes(iBits); + + memcpy(trace + traceLen, btBytes, iLen); + traceLen += iLen; + return TRUE; } diff --git a/armsrc/util.c b/armsrc/util.c index 88931927..74fba94b 100644 --- a/armsrc/util.c +++ b/armsrc/util.c @@ -35,7 +35,7 @@ void print_result(char *name, uint8_t *buf, size_t len) { } size_t nbytes(size_t nbits) { - return (nbits/8)+((nbits%8)>0); + return (nbits >> 3)+((nbits % 8) > 0); } uint32_t SwapBits(uint32_t value, int nrbits) { From beefe5bc4dd7df83b618f02c25083bd27861942d Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Sat, 7 Feb 2015 21:22:53 +0100 Subject: [PATCH 13/25] Minor dox --- armsrc/BigBuf.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/armsrc/BigBuf.c b/armsrc/BigBuf.c index 6e99a793..0c666bce 100644 --- a/armsrc/BigBuf.c +++ b/armsrc/BigBuf.c @@ -183,8 +183,12 @@ bool RAMFUNC LogTrace(const uint8_t *btBytes, uint16_t iLen, uint32_t timestamp_ return TRUE; } -int LogTraceHitag(const uint8_t * btBytes, int iBits, int iSamples, uint32_t dwParity, int bReader) +int LogTraceHitag(const uint8_t * btBytes, int iBits, int iSamples, uint32_t dwParity, int readerToTag) { + /** + Todo, rewrite the logger to use the generic functionality instead. It should be noted, however, + that this logger takes number of bits as argument, not number of bytes. + **/ if (!tracing) return FALSE; @@ -196,7 +200,7 @@ int LogTraceHitag(const uint8_t * btBytes, int iBits, int iSamples, uint32_t dwP //Hitag traces appear to use this traceformat: // 32 bits timestamp (little endian,Highest Bit used as readerToTag flag) // 32 bits parity - // 8 bits size (number of bits in the trace entry) + // 8 bits size (number of bits in the trace entry, not number of bytes) // y Bytes data rsamples += iSamples; @@ -205,7 +209,7 @@ int LogTraceHitag(const uint8_t * btBytes, int iBits, int iSamples, uint32_t dwP trace[traceLen++] = ((rsamples >> 16) & 0xff); trace[traceLen++] = ((rsamples >> 24) & 0xff); - if (!bReader) { + if (!readerToTag) { trace[traceLen - 1] |= 0x80; } From 3bba7deac0b4e1908db70e6a005d275859458c15 Mon Sep 17 00:00:00 2001 From: Jesse Hallio Date: Tue, 10 Feb 2015 04:31:53 +0200 Subject: [PATCH 14/25] Add settable ATQA and SAK to hf mf csetuid command. --- client/cmdhfmf.c | 55 ++++++++++++++++++++++++++++++++++++--------- client/mifarehost.c | 37 ++++++++++++++++-------------- client/mifarehost.h | 2 +- 3 files changed, 65 insertions(+), 29 deletions(-) diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index f225359d..d0852ea5 100644 --- a/client/cmdhfmf.c +++ b/client/cmdhfmf.c @@ -1433,27 +1433,60 @@ int CmdHF14AMfCSetUID(const char *Cmd) uint8_t wipeCard = 0; uint8_t uid[8] = {0x00}; uint8_t oldUid[8] = {0x00}; + uint8_t atqa[2] = {0x00}; + uint8_t sak[1] = {0x00}; + uint8_t atqaPresent = 1; int res; + char ctmp; + int argi=0; - if (strlen(Cmd) < 1 || param_getchar(Cmd, 0) == 'h') { - PrintAndLog("Usage: hf mf csetuid "); - PrintAndLog("sample: hf mf csetuid 01020304 w"); - PrintAndLog("Set UID for magic Chinese card (only works with!!!)"); - PrintAndLog("If you want wipe card then add 'w' into command line. \n"); + if (strlen(Cmd) < 1 || param_getchar(Cmd, argi) == 'h') { + PrintAndLog("Usage: hf mf csetuid [ATQA 4 hex symbols SAK 2 hex symbols] [w]"); + PrintAndLog("sample: hf mf csetuid 01020304"); + PrintAndLog("sample: hf mf csetuid 01020304 0004 08 w"); + PrintAndLog("Set UID, ATQA, and SAK for magic Chinese card (only works with such cards)"); + PrintAndLog("If you also want to wipe the card then add 'w' at the end of the command line."); return 0; - } + } - if (param_getchar(Cmd, 0) && param_gethex(Cmd, 0, uid, 8)) { + if (param_getchar(Cmd, argi) && param_gethex(Cmd, argi, uid, 8)) { PrintAndLog("UID must include 8 HEX symbols"); return 1; } + argi++; + + ctmp = param_getchar(Cmd, argi); + if (ctmp == 'w' || ctmp == 'W') { + wipeCard = 1; + atqaPresent = 0; + } + + if (atqaPresent) { + if (param_getchar(Cmd, argi)) { + if (param_gethex(Cmd, argi, atqa, 4)) { + PrintAndLog("ATQA must include 4 HEX symbols"); + return 1; + } + argi++; + if (!param_getchar(Cmd, argi) || param_gethex(Cmd, argi, sak, 2)) { + PrintAndLog("SAK must include 2 HEX symbols"); + return 1; + } + argi++; + } else + atqaPresent = 0; + } + + if(!wipeCard) { + ctmp = param_getchar(Cmd, argi); + if (ctmp == 'w' || ctmp == 'W') { + wipeCard = 1; + } + } - char ctmp = param_getchar(Cmd, 1); - if (ctmp == 'w' || ctmp == 'W') wipeCard = 1; - PrintAndLog("--wipe card:%s uid:%s", (wipeCard)?"YES":"NO", sprint_hex(uid, 4)); - res = mfCSetUID(uid, oldUid, wipeCard); + res = mfCSetUID(uid, (atqaPresent)?atqa:NULL, (atqaPresent)?sak:NULL, oldUid, wipeCard); if (res) { PrintAndLog("Can't set UID. error=%d", res); return 1; diff --git a/client/mifarehost.c b/client/mifarehost.c index 7f784850..35499b83 100644 --- a/client/mifarehost.c +++ b/client/mifarehost.c @@ -231,28 +231,31 @@ int mfEmlSetMem(uint8_t *data, int blockNum, int blocksCount) { // "MAGIC" CARD -int mfCSetUID(uint8_t *uid, uint8_t *oldUID, bool wantWipe) { - +int mfCSetUID(uint8_t *uid, uint8_t *atqa, uint8_t *sak, uint8_t *oldUID, bool wantWipe) { uint8_t oldblock0[16] = {0x00}; uint8_t block0[16] = {0x00}; - memcpy(block0, uid, 4); - block0[4] = block0[0]^block0[1]^block0[2]^block0[3]; // Mifare UID BCC - // mifare classic SAK(byte 5) and ATQA(byte 6 and 7) - //block0[5] = 0x08; - //block0[6] = 0x04; - //block0[7] = 0x00; - - block0[5] = 0x01; //sak - block0[6] = 0x01; - block0[7] = 0x0f; - + int old = mfCGetBlock(0, oldblock0, CSETBLOCK_SINGLE_OPER); - if ( old == 0) { - memcpy(block0+8, oldblock0+8, 8); - PrintAndLog("block 0: %s", sprint_hex(block0,16)); + if (old == 0) { + memcpy(block0, oldblock0, 16); + PrintAndLog("old block 0: %s", sprint_hex(block0,16)); } else { - PrintAndLog("Couldn't get olddata. Will write over the last bytes of Block 0."); + PrintAndLog("Couldn't get old data. Will write over the last bytes of Block 0."); } + + // fill in the new values + // UID + memcpy(block0, uid, 4); + // Mifare UID BCC + block0[4] = block0[0]^block0[1]^block0[2]^block0[3]; + // mifare classic SAK(byte 5) and ATQA(byte 6 and 7, reversed) + if (sak!=NULL) + block0[5]=sak[0]; + if (atqa!=NULL) { + block0[6]=atqa[1]; + block0[7]=atqa[0]; + } + PrintAndLog("new block 0: %s", sprint_hex(block0,16)); return mfCSetBlock(0, block0, oldUID, wantWipe, CSETBLOCK_SINGLE_OPER); } diff --git a/client/mifarehost.h b/client/mifarehost.h index 96eb75f7..a11f11d5 100644 --- a/client/mifarehost.h +++ b/client/mifarehost.h @@ -55,7 +55,7 @@ int mfCheckKeys (uint8_t blockNo, uint8_t keyType, uint8_t keycnt, uint8_t * key int mfEmlGetMem(uint8_t *data, int blockNum, int blocksCount); int mfEmlSetMem(uint8_t *data, int blockNum, int blocksCount); -int mfCSetUID(uint8_t *uid, uint8_t *oldUID, bool wantWipe); +int mfCSetUID(uint8_t *uid, uint8_t *atqa, uint8_t *sak, uint8_t *oldUID, bool wantWipe); int mfCSetBlock(uint8_t blockNo, uint8_t *data, uint8_t *uid, bool wantWipe, uint8_t params); int mfCGetBlock(uint8_t blockNo, uint8_t *data, uint8_t params); From 070e36d421f2526b5e1b68fd7590da1711816ee9 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Wed, 11 Feb 2015 17:08:16 +0100 Subject: [PATCH 15/25] ADD: identification of EM4233 --- client/cmdhf15.c | 1 + 1 file changed, 1 insertion(+) diff --git a/client/cmdhf15.c b/client/cmdhf15.c index d6ab2000..b843730f 100644 --- a/client/cmdhf15.c +++ b/client/cmdhf15.c @@ -136,6 +136,7 @@ const productName uidmapping[] = { { 0xE016040000000000LL, 24, "EM-Marin SA (Skidata Keycard-eco); EM4034? no 'read', just 'readmulti'" }, { 0xE0160c0000000000LL, 24, "EM-Marin SA; EM4035?" }, { 0xE016100000000000LL, 24, "EM-Marin SA (Skidata); EM4135; 36x64bit start page 13" }, + { 0xE016240000000000LL, 24, "EM-Marin SA (Skidata); EM4233;" }, { 0xE016940000000000LL, 24, "EM-Marin SA (Skidata); 51x64bit" }, { 0xE017000000000000LL, 16, "KSW Microtec GmbH Germany" }, From 7554370c309e934787410041e2f747ed656acce0 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Wed, 11 Feb 2015 21:14:34 +0100 Subject: [PATCH 16/25] bugfix hf 14a sim / hf mf sim: polarity of tag subcarrier modulation was wrong --- fpga/fpga_hf.bit | Bin 42175 -> 42175 bytes fpga/hi_iso14443a.v | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/fpga/fpga_hf.bit b/fpga/fpga_hf.bit index 8b0c7a3788ef438c94eaed81346a08f24927f954..4910e6ac50d6e3cb405bacee3eef47de443b0147 100644 GIT binary patch literal 42175 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 S@ZWLO0seoql9uvb@_zvy&2EtZ literal 42175 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 diff --git a/fpga/hi_iso14443a.v b/fpga/hi_iso14443a.v index 3f614fdd..46adda12 100644 --- a/fpga/hi_iso14443a.v +++ b/fpga/hi_iso14443a.v @@ -570,7 +570,7 @@ assign pwr_oe3 = 1'b0; // TAGSIM_MOD: short circuit antenna with different resistances (modulated by sub_carrier modulated by mod_sig_coil) // for pwr_oe4 = 1 (tristate): antenna load = 10k || 33 = 32,9 Ohms // for pwr_oe4 = 0 (active): antenna load = 10k || 33 || 33 = 16,5 Ohms -assign pwr_oe4 = ~(mod_sig_coil & sub_carrier & (mod_type == `TAGSIM_MOD)); +assign pwr_oe4 = mod_sig_coil & sub_carrier & (mod_type == `TAGSIM_MOD); // This is all LF, so doesn't matter. assign pwr_oe2 = 1'b0; From 19a700a8b54f948623cb35f6f297dc03601f9950 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Wed, 11 Feb 2015 18:30:36 +0100 Subject: [PATCH 17/25] hf 14a: if the tag supports it, set default timeout according to ATS hf epa: remove explicit but arbitrary timeout settings Bugfix: don't timeout when frame transmission has already started --- armsrc/epa.c | 5 ----- armsrc/iso14443a.c | 32 ++++++++++++++++++++++++++++++-- armsrc/iso14443a.h | 1 - client/cmdhf14a.c | 26 +++++++++++--------------- 4 files changed, 41 insertions(+), 23 deletions(-) diff --git a/armsrc/epa.c b/armsrc/epa.c index bec79e61..0006d59d 100644 --- a/armsrc/epa.c +++ b/armsrc/epa.c @@ -257,9 +257,6 @@ void EPA_PACE_Collect_Nonce(UsbCommand *c) return; } - // increase the timeout (at least some cards really do need this!) - iso14a_set_timeout(0x0002FFFF); - // read the CardAccess file // this array will hold the CardAccess file uint8_t card_access[256] = {0}; @@ -426,8 +423,6 @@ int EPA_Setup() // power up the field iso14443a_setup(FPGA_HF_ISO14443A_READER_MOD); - iso14a_set_timeout(10500); - // select the card return_code = iso14443a_select_card(uid, &card_select_info, NULL); if (return_code != 1) { diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index a7102168..f2fa1ff2 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -141,16 +141,40 @@ const uint8_t OddByteParity[256] = { 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1 }; + void iso14a_set_trigger(bool enable) { trigger = enable; } - void iso14a_set_timeout(uint32_t timeout) { iso14a_timeout = timeout; + if(MF_DBGLEVEL >= 3) Dbprintf("ISO14443A Timeout set to %ld (%dms)", iso14a_timeout, iso14a_timeout / 106); } + +void iso14a_set_ATS_timeout(uint8_t *ats) { + + uint8_t tb1; + uint8_t fwi; + uint32_t fwt; + + if (ats[0] > 1) { // there is a format byte T0 + if ((ats[1] & 0x20) == 0x20) { // there is an interface byte TB(1) + if ((ats[1] & 0x10) == 0x10) { // there is an interface byte TA(1) preceding TB(1) + tb1 = ats[3]; + } else { + tb1 = ats[2]; + } + fwi = (tb1 & 0xf0) >> 4; // frame waiting indicator (FWI) + fwt = 256 * 16 * (1 << fwi); // frame waiting time (FWT) in 1/fc + + iso14a_set_timeout(fwt/(8*16)); + } + } +} + + //----------------------------------------------------------------------------- // Generate the parity value for a byte sequence // @@ -1600,7 +1624,7 @@ static int GetIso14443aAnswerFromTag(uint8_t *receivedResponse, uint8_t *receive if(ManchesterDecoding(b, offset, 0)) { NextTransferTime = MAX(NextTransferTime, Demod.endTime - (DELAY_AIR2ARM_AS_READER + DELAY_ARM2AIR_AS_READER)/16 + FRAME_DELAY_TIME_PICC_TO_PCD); return TRUE; - } else if (c++ > iso14a_timeout) { + } else if (c++ > iso14a_timeout && Demod.state == DEMOD_UNSYNCD) { return FALSE; } } @@ -1798,6 +1822,10 @@ int iso14443a_select_card(byte_t *uid_ptr, iso14a_card_select_t *p_hi14a_card, u // reset the PCB block number iso14_pcb_blocknum = 0; + + // set default timeout based on ATS + iso14a_set_ATS_timeout(resp); + return 1; } diff --git a/armsrc/iso14443a.h b/armsrc/iso14443a.h index c3710388..1e978e88 100644 --- a/armsrc/iso14443a.h +++ b/armsrc/iso14443a.h @@ -85,6 +85,5 @@ extern void iso14443a_setup(uint8_t fpga_minor_mode); extern int iso14_apdu(uint8_t *cmd, uint16_t cmd_len, void *data); extern int iso14443a_select_card(uint8_t *uid_ptr, iso14a_card_select_t *resp_data, uint32_t *cuid_ptr); extern void iso14a_set_trigger(bool enable); -extern void iso14a_set_timeout(uint32_t timeout); #endif /* __ISO14443A_H */ diff --git a/client/cmdhf14a.c b/client/cmdhf14a.c index 147e790e..744b3875 100644 --- a/client/cmdhf14a.c +++ b/client/cmdhf14a.c @@ -129,11 +129,6 @@ int CmdHF14AList(const char *Cmd) return 0; } -void iso14a_set_timeout(uint32_t timeout) { - UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_SET_TIMEOUT, 0, timeout}}; - SendCommand(&c); -} - int CmdHF14AReader(const char *Cmd) { UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0}}; @@ -346,7 +341,7 @@ int CmdHF14AReader(const char *Cmd) SendCommand(&c); WaitForResponse(CMD_ACK,&resp); uint8_t isOK = resp.arg[0] & 0xff; - PrintAndLog(" Answers to chinese magic backdoor commands: %s", (isOK ? "YES" : "NO") ); + PrintAndLog("Answers to chinese magic backdoor commands: %s", (isOK ? "YES" : "NO") ); // disconnect c.cmd = CMD_READER_ISO_14443a; @@ -510,12 +505,13 @@ int CmdHF14ACmdRaw(const char *cmd) { uint8_t active=0; uint8_t active_select=0; uint16_t numbits=0; - uint16_t timeout=0; + uint32_t timeout=0; uint8_t bTimeout=0; char buf[5]=""; int i=0; uint8_t data[USB_CMD_DATA_SIZE]; - unsigned int datalen=0, temp; + uint16_t datalen=0; + uint32_t temp; if (strlen(cmd)<2) { PrintAndLog("Usage: hf 14a raw [-r] [-c] [-p] [-f] [-b] [-t] <0A 0B 0C ... hex>"); @@ -525,7 +521,7 @@ int CmdHF14ACmdRaw(const char *cmd) { PrintAndLog(" -a active signal field ON without select"); PrintAndLog(" -s active signal field ON with select"); PrintAndLog(" -b number of bits to send. Useful for send partial byte"); - PrintAndLog(" -t timeout"); + PrintAndLog(" -t timeout in ms"); return 0; } @@ -561,7 +557,7 @@ int CmdHF14ACmdRaw(const char *cmd) { case 't': bTimeout=1; sscanf(cmd+i+2,"%d",&temp); - timeout = temp & 0xFFFF; + timeout = temp; i+=3; while(cmd[i]!=' ' && cmd[i]!='\0') { i++; } i+=2; @@ -610,13 +606,13 @@ int CmdHF14ACmdRaw(const char *cmd) { c.arg[0] |= ISO14A_NO_SELECT; } if(bTimeout){ - #define MAX_TIMEOUT 624*105 // max timeout is 624 ms + #define MAX_TIMEOUT 40542464 // (2^32-1) * (8*16) / 13560000Hz * 1000ms/s = c.arg[0] |= ISO14A_SET_TIMEOUT; - c.arg[2] = timeout * 105; // each bit is about 9.4 us - if(c.arg[2]>MAX_TIMEOUT) { - c.arg[2] = MAX_TIMEOUT; - PrintAndLog("Set timeout to 624 ms. The max we can wait for response"); + if(timeout > MAX_TIMEOUT) { + timeout = MAX_TIMEOUT; + PrintAndLog("Set timeout to 40542 seconds (11.26 hours). The max we can wait for response"); } + c.arg[2] = 13560000 / 1000 / (8*16) * timeout; // timeout in ETUs (time to transfer 1 bit, approx. 9.4 us) } if(power) c.arg[0] |= ISO14A_NO_DISCONNECT; From 8b9393d3bd2c7fb4761b8317673b63ee96621b22 Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Thu, 12 Feb 2015 08:21:58 +0100 Subject: [PATCH 18/25] fixed issue #65 --- client/cmdhf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/cmdhf.c b/client/cmdhf.c index 07a4aa49..7f1246cc 100644 --- a/client/cmdhf.c +++ b/client/cmdhf.c @@ -475,7 +475,7 @@ uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *trace, ui snprintf(line[j/16]+(( j % 16) * 4),110, "%02x! ", frame[j]); } else { - snprintf(line[j/16]+(( j % 16) * 4),110, "%02x! ", frame[j]); + snprintf(line[j/16]+(( j % 16) * 4),110, "%02x ", frame[j]); } } if(data_len == 0) From 699bb9dc275d2474f2c2091800fbc04f636edb8c Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Fri, 13 Feb 2015 19:59:28 +0100 Subject: [PATCH 19/25] Removed some dev- printouts --- client/cmdlf.c | 1 - client/util.c | 1 - 2 files changed, 2 deletions(-) diff --git a/client/cmdlf.c b/client/cmdlf.c index b7c1b13f..f268eaa2 100644 --- a/client/cmdlf.c +++ b/client/cmdlf.c @@ -414,7 +414,6 @@ int CmdLFSetConfig(const char *Cmd) uint8_t cmdp =0; while(param_getchar(Cmd, cmdp) != 0x00) { - PrintAndLog("working %c", param_getchar(Cmd, cmdp)); switch(param_getchar(Cmd, cmdp)) { case 'h': diff --git a/client/util.c b/client/util.c index 6b47eab9..edd9aebc 100644 --- a/client/util.c +++ b/client/util.c @@ -237,7 +237,6 @@ uint8_t param_get8(const char *line, int paramnum) uint8_t param_getdec(const char *line, int paramnum, uint8_t *destination) { uint8_t val = param_get8ex(line, paramnum, 255, 10); - printf("read %i", (int8_t ) val); if( (int8_t) val == -1) return 1; (*destination) = val; return 0; From 0cd2a41ac0e27ba7647ecfaa0d713552e9684460 Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Sat, 14 Feb 2015 19:55:23 +0100 Subject: [PATCH 20/25] Made 125KHz default sampling, instead of 134KHz for LF --- armsrc/lfsampling.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/armsrc/lfsampling.c b/armsrc/lfsampling.c index 348549ef..138814b7 100644 --- a/armsrc/lfsampling.c +++ b/armsrc/lfsampling.c @@ -13,7 +13,7 @@ #include "lfsampling.h" -sample_config config = { 1, 8, 1, 88, 0 } ; +sample_config config = { 1, 8, 1, 95, 0 } ; void printConfig() { From 428d6221600c747e46623362f0dd9f10cf9666dd Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Sat, 14 Feb 2015 20:42:23 +0100 Subject: [PATCH 21/25] Fixed bug with iclass dump which prevented saving to file --- armsrc/iclass.c | 4 ++-- client/cmdhficlass.c | 9 ++++----- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/armsrc/iclass.c b/armsrc/iclass.c index 41c9b8b5..2a0ba0d2 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -1704,7 +1704,7 @@ void ReaderIClass_Replay(uint8_t arg0, uint8_t *MAC) { //Set card_data to all zeroes, we'll fill it with data memset(card_data,0x0,USB_CMD_DATA_SIZE); uint8_t failedRead =0; - uint8_t stored_data_length =0; + uint32_t stored_data_length =0; //then loop around remaining blocks for(int block=0; block < cardsize; block++){ @@ -1723,7 +1723,6 @@ void ReaderIClass_Replay(uint8_t arg0, uint8_t *MAC) { //Fill up the buffer memcpy(card_data+stored_data_length,resp,8); stored_data_length += 8; - if(stored_data_length +8 > USB_CMD_DATA_SIZE) {//Time to send this off and start afresh cmd_send(CMD_ACK, @@ -1742,6 +1741,7 @@ void ReaderIClass_Replay(uint8_t arg0, uint8_t *MAC) { Dbprintf("Failed to dump block %d", block); } } + //Send off any remaining data if(stored_data_length > 0) { diff --git a/client/cmdhficlass.c b/client/cmdhficlass.c index 03b39021..b458ae03 100644 --- a/client/cmdhficlass.c +++ b/client/cmdhficlass.c @@ -329,8 +329,8 @@ int CmdHFiClassReader_Dump(const char *Cmd) printvar("MAC", MAC, 4); uint8_t iclass_data[32000] = {0}; - uint8_t iclass_datalen = 0; - uint8_t iclass_blocksFailed = 0;//Set to 1 if dump was incomplete + uint32_t iclass_datalen = 0; + uint32_t iclass_blocksFailed = 0;//Set to 1 if dump was incomplete UsbCommand d = {CMD_READER_ICLASS_REPLAY, {readerType}}; memcpy(d.d.asBytes, MAC, 4); @@ -346,11 +346,11 @@ int CmdHFiClassReader_Dump(const char *Cmd) } if(WaitForResponseTimeout(CMD_ACK,&resp,4500)) { - uint64_t dataLength = resp.arg[0]; + uint32_t dataLength = resp.arg[0]; iclass_blocksFailed |= resp.arg[1]; - if(dataLength > 0) { + PrintAndLog("Got %d bytes data (total so far %d)" ,dataLength,iclass_datalen); memcpy(iclass_data, resp.d.asBytes,dataLength); iclass_datalen += dataLength; }else @@ -368,7 +368,6 @@ int CmdHFiClassReader_Dump(const char *Cmd) CSN[0],CSN[1],CSN[2],CSN[3], CSN[4],CSN[5],CSN[6],CSN[7]); saveFile(filename,"bin",iclass_data, iclass_datalen ); - } //Aaaand we're finished return 0; From 7781a65656e334a44ded56f6378a167a30afa2e5 Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Sat, 14 Feb 2015 21:15:53 +0100 Subject: [PATCH 22/25] Started work on 'hf iclass eload' - only client side so far, not yet supported in the device --- client/cmdhficlass.c | 75 +++++++++++++++++++++++++++++++++++++++++++- include/usb_cmd.h | 1 + 2 files changed, 75 insertions(+), 1 deletion(-) diff --git a/client/cmdhficlass.c b/client/cmdhficlass.c index b458ae03..dd0a8e2f 100644 --- a/client/cmdhficlass.c +++ b/client/cmdhficlass.c @@ -379,6 +379,78 @@ int CmdHFiClassReader_Dump(const char *Cmd) return 0; } +int hf_iclass_eload_usage() +{ + PrintAndLog("Loads iclass tag-dump into emulator memory on device"); + PrintAndLog("Usage: hf iclass eload f "); + PrintAndLog(""); + PrintAndLog("Example: hf iclass eload f iclass_tagdump-aa162d30f8ff12f1.bin"); + return 0; + +} + +int iclassEmlSetMem(uint8_t *data, int blockNum, int blocksCount) { + UsbCommand c = {CMD_MIFARE_EML_MEMSET, {blockNum, blocksCount, 0}}; + memcpy(c.d.asBytes, data, blocksCount * 16); + SendCommand(&c); + return 0; +} +int CmdHFiClassELoad(const char *Cmd) +{ + + char opt = param_getchar(Cmd, 0); + if (strlen(Cmd)<1 || opt == 'h') + return hf_iclass_eload_usage(); + + //File handling and reading + FILE *f; + char filename[FILE_PATH_SIZE]; + if(opt == 'f' && param_getstr(Cmd, 1, filename) > 0) + { + f = fopen(filename, "rb"); + }else{ + return hf_iclass_eload_usage(); + } + + if(!f) { + PrintAndLog("Failed to read from file '%s'", filename); + return 1; + } + + fseek(f, 0, SEEK_END); + long fsize = ftell(f); + fseek(f, 0, SEEK_SET); + + uint8_t *dump = malloc(fsize); + size_t bytes_read = fread(dump, 1, fsize, f); + fclose(f); + + //Validate + + if (bytes_read < fsize) + { + prnlog("Error, could only read %d bytes (should be %d)",bytes_read, fsize ); + free(dump); + return 1; + } + //Send to device + uint32_t bytes_sent = 0; + uint32_t bytes_remaining = bytes_read; + + while(bytes_remaining > 0){ + uint32_t bytes_in_packet = MIN(USB_CMD_DATA_SIZE, bytes_remaining); + UsbCommand c = {CMD_ICLASS_EML_MEMSET, {bytes_sent,bytes_in_packet,0}}; + memcpy(c.d.asBytes, dump, bytes_in_packet); + SendCommand(&c); + bytes_remaining -= bytes_in_packet; + bytes_sent += bytes_in_packet; + } + free(dump); + PrintAndLog("Sent %d bytes of data to device emulator memory", bytes_sent); + return 0; +} + + int CmdHFiClass_iso14443A_write(const char *Cmd) { uint8_t readerType = 0; @@ -427,7 +499,7 @@ int CmdHFiClass_iso14443A_write(const char *Cmd) memcpy(CSN,data,8); memcpy(CCNR,data+8,8); PrintAndLog("DEBUG: %s",sprint_hex(CSN,8)); - PrintAndLog("DEBUG: %s",sprint_hex(CCNR,8)); + PrintAndLog("DEBUG: %s",sprint_hex(CCNR,8)); PrintAndLog("isOk:%02x", isOK); } else { PrintAndLog("Command execute timeout"); @@ -513,6 +585,7 @@ static command_t CommandTable[] = {"dump", CmdHFiClassReader_Dump, 0, "Authenticate and Dump iClass tag"}, {"write", CmdHFiClass_iso14443A_write, 0, "Authenticate and Write iClass block"}, {"loclass", CmdHFiClass_loclass, 1, "Use loclass to perform bruteforce of reader attack dump"}, + {"eload", CmdHFiClassELoad, 0, "[experimental] Load data into iclass emulator memory"}, {NULL, NULL, 0, NULL} }; diff --git a/include/usb_cmd.h b/include/usb_cmd.h index 6ee6509e..d9a950ae 100644 --- a/include/usb_cmd.h +++ b/include/usb_cmd.h @@ -131,6 +131,7 @@ typedef struct{ #define CMD_READER_ICLASS 0x0394 #define CMD_READER_ICLASS_REPLAY 0x0395 #define CMD_ICLASS_ISO14443A_WRITE 0x0397 +#define CMD_ICLASS_EML_MEMSET 0x0398 // For measurements of the antenna tuning #define CMD_MEASURE_ANTENNA_TUNING 0x0400 From 534445139c7c356d05a7fa7ade38cb8dc787ada5 Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Sat, 14 Feb 2015 21:17:08 +0100 Subject: [PATCH 23/25] Reformatted --- client/cmdhficlass.c | 440 +++++++++++++++++++++---------------------- 1 file changed, 220 insertions(+), 220 deletions(-) diff --git a/client/cmdhficlass.c b/client/cmdhficlass.c index dd0a8e2f..2d6a9beb 100644 --- a/client/cmdhficlass.c +++ b/client/cmdhficlass.c @@ -34,11 +34,11 @@ static int CmdHelp(const char *Cmd); int xorbits_8(uint8_t val) { - uint8_t res = val ^ (val >> 1); //1st pass - res = res ^ (res >> 1); // 2nd pass - res = res ^ (res >> 2); // 3rd pass - res = res ^ (res >> 4); // 4th pass - return res & 1; + uint8_t res = val ^ (val >> 1); //1st pass + res = res ^ (res >> 1); // 2nd pass + res = res ^ (res >> 2); // 3rd pass + res = res ^ (res >> 4); // 4th pass + return res & 1; } int CmdHFiClassList(const char *Cmd) @@ -49,44 +49,44 @@ int CmdHFiClassList(const char *Cmd) int CmdHFiClassSnoop(const char *Cmd) { - UsbCommand c = {CMD_SNOOP_ICLASS}; - SendCommand(&c); - return 0; + UsbCommand c = {CMD_SNOOP_ICLASS}; + SendCommand(&c); + return 0; } #define NUM_CSNS 15 int CmdHFiClassSim(const char *Cmd) { - uint8_t simType = 0; - uint8_t CSN[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + uint8_t simType = 0; + uint8_t CSN[8] = {0, 0, 0, 0, 0, 0, 0, 0}; - if (strlen(Cmd)<1) { - PrintAndLog("Usage: hf iclass sim [0 ] | x"); - PrintAndLog(" options"); - PrintAndLog(" 0 simulate the given CSN"); - PrintAndLog(" 1 simulate default CSN"); - PrintAndLog(" 2 iterate CSNs, gather MACs"); - PrintAndLog(" sample: hf iclass sim 0 031FEC8AF7FF12E0"); - PrintAndLog(" sample: hf iclass sim 2"); - return 0; - } + if (strlen(Cmd)<1) { + PrintAndLog("Usage: hf iclass sim [0 ] | x"); + PrintAndLog(" options"); + PrintAndLog(" 0 simulate the given CSN"); + PrintAndLog(" 1 simulate default CSN"); + PrintAndLog(" 2 iterate CSNs, gather MACs"); + PrintAndLog(" sample: hf iclass sim 0 031FEC8AF7FF12E0"); + PrintAndLog(" sample: hf iclass sim 2"); + return 0; + } - simType = param_get8(Cmd, 0); + simType = param_get8(Cmd, 0); - if(simType == 0) - { - if (param_gethex(Cmd, 1, CSN, 16)) { - PrintAndLog("A CSN should consist of 16 HEX symbols"); - return 1; - } - PrintAndLog("--simtype:%02x csn:%s", simType, sprint_hex(CSN, 8)); + if(simType == 0) + { + if (param_gethex(Cmd, 1, CSN, 16)) { + PrintAndLog("A CSN should consist of 16 HEX symbols"); + return 1; + } + PrintAndLog("--simtype:%02x csn:%s", simType, sprint_hex(CSN, 8)); - } - if(simType > 2) - { - PrintAndLog("Undefined simptype %d", simType); - return 1; - } - uint8_t numberOfCSNs=0; + } + if(simType > 2) + { + PrintAndLog("Undefined simptype %d", simType); + return 1; + } + uint8_t numberOfCSNs=0; if(simType == 2) { @@ -103,23 +103,23 @@ int CmdHFiClassSim(const char *Cmd) 0x00,0x73,0xd8,0x75,0x58,0xff,0x12,0xe0 , 0x0c,0x90,0x32,0xf3,0x5d,0xff,0x12,0xe0 }; */ - - uint8_t csns[8*NUM_CSNS] = { - 0x00, 0x0B, 0x0F, 0xFF, 0xF7, 0xFF, 0x12, 0xE0, - 0x00, 0x04, 0x0E, 0x08, 0xF7, 0xFF, 0x12, 0xE0, - 0x00, 0x09, 0x0D, 0x05, 0xF7, 0xFF, 0x12, 0xE0, - 0x00, 0x0A, 0x0C, 0x06, 0xF7, 0xFF, 0x12, 0xE0, - 0x00, 0x0F, 0x0B, 0x03, 0xF7, 0xFF, 0x12, 0xE0, - 0x00, 0x08, 0x0A, 0x0C, 0xF7, 0xFF, 0x12, 0xE0, - 0x00, 0x0D, 0x09, 0x09, 0xF7, 0xFF, 0x12, 0xE0, - 0x00, 0x0E, 0x08, 0x0A, 0xF7, 0xFF, 0x12, 0xE0, - 0x00, 0x03, 0x07, 0x17, 0xF7, 0xFF, 0x12, 0xE0, - 0x00, 0x3C, 0x06, 0xE0, 0xF7, 0xFF, 0x12, 0xE0, - 0x00, 0x01, 0x05, 0x1D, 0xF7, 0xFF, 0x12, 0xE0, - 0x00, 0x02, 0x04, 0x1E, 0xF7, 0xFF, 0x12, 0xE0, - 0x00, 0x07, 0x03, 0x1B, 0xF7, 0xFF, 0x12, 0xE0, - 0x00, 0x00, 0x02, 0x24, 0xF7, 0xFF, 0x12, 0xE0, - 0x00, 0x05, 0x01, 0x21, 0xF7, 0xFF, 0x12, 0xE0 }; + + uint8_t csns[8*NUM_CSNS] = { + 0x00, 0x0B, 0x0F, 0xFF, 0xF7, 0xFF, 0x12, 0xE0, + 0x00, 0x04, 0x0E, 0x08, 0xF7, 0xFF, 0x12, 0xE0, + 0x00, 0x09, 0x0D, 0x05, 0xF7, 0xFF, 0x12, 0xE0, + 0x00, 0x0A, 0x0C, 0x06, 0xF7, 0xFF, 0x12, 0xE0, + 0x00, 0x0F, 0x0B, 0x03, 0xF7, 0xFF, 0x12, 0xE0, + 0x00, 0x08, 0x0A, 0x0C, 0xF7, 0xFF, 0x12, 0xE0, + 0x00, 0x0D, 0x09, 0x09, 0xF7, 0xFF, 0x12, 0xE0, + 0x00, 0x0E, 0x08, 0x0A, 0xF7, 0xFF, 0x12, 0xE0, + 0x00, 0x03, 0x07, 0x17, 0xF7, 0xFF, 0x12, 0xE0, + 0x00, 0x3C, 0x06, 0xE0, 0xF7, 0xFF, 0x12, 0xE0, + 0x00, 0x01, 0x05, 0x1D, 0xF7, 0xFF, 0x12, 0xE0, + 0x00, 0x02, 0x04, 0x1E, 0xF7, 0xFF, 0x12, 0xE0, + 0x00, 0x07, 0x03, 0x1B, 0xF7, 0xFF, 0x12, 0xE0, + 0x00, 0x00, 0x02, 0x24, 0xF7, 0xFF, 0x12, 0xE0, + 0x00, 0x05, 0x01, 0x21, 0xF7, 0xFF, 0x12, 0xE0 }; memcpy(c.d.asBytes, csns, 8*NUM_CSNS); @@ -164,124 +164,124 @@ int CmdHFiClassSim(const char *Cmd) SendCommand(&c); } - return 0; + return 0; } int CmdHFiClassReader(const char *Cmd) { - UsbCommand c = {CMD_READER_ICLASS, {0}}; - SendCommand(&c); - UsbCommand resp; - while(!ukbhit()){ - if (WaitForResponseTimeout(CMD_ACK,&resp,4500)) { - uint8_t isOK = resp.arg[0] & 0xff; - uint8_t * data = resp.d.asBytes; + UsbCommand c = {CMD_READER_ICLASS, {0}}; + SendCommand(&c); + UsbCommand resp; + while(!ukbhit()){ + if (WaitForResponseTimeout(CMD_ACK,&resp,4500)) { + uint8_t isOK = resp.arg[0] & 0xff; + uint8_t * data = resp.d.asBytes; - PrintAndLog("isOk:%02x", isOK); - if( isOK == 0){ - //Aborted - PrintAndLog("Quitting..."); - return 0; - } - if(isOK > 0) - { - PrintAndLog("CSN: %s",sprint_hex(data,8)); - } - if(isOK >= 1) - { - PrintAndLog("CC: %s",sprint_hex(data+8,8)); - }else{ - PrintAndLog("No CC obtained"); - } - } else { - PrintAndLog("Command execute timeout"); - } - } + PrintAndLog("isOk:%02x", isOK); + if( isOK == 0){ + //Aborted + PrintAndLog("Quitting..."); + return 0; + } + if(isOK > 0) + { + PrintAndLog("CSN: %s",sprint_hex(data,8)); + } + if(isOK >= 1) + { + PrintAndLog("CC: %s",sprint_hex(data+8,8)); + }else{ + PrintAndLog("No CC obtained"); + } + } else { + PrintAndLog("Command execute timeout"); + } + } - return 0; + return 0; } int CmdHFiClassReader_Replay(const char *Cmd) { - uint8_t readerType = 0; - uint8_t MAC[4]={0x00, 0x00, 0x00, 0x00}; + uint8_t readerType = 0; + uint8_t MAC[4]={0x00, 0x00, 0x00, 0x00}; - if (strlen(Cmd)<1) { - PrintAndLog("Usage: hf iclass replay "); - PrintAndLog(" sample: hf iclass replay 00112233"); - return 0; - } + if (strlen(Cmd)<1) { + PrintAndLog("Usage: hf iclass replay "); + PrintAndLog(" sample: hf iclass replay 00112233"); + return 0; + } - if (param_gethex(Cmd, 0, MAC, 8)) { - PrintAndLog("MAC must include 8 HEX symbols"); - return 1; - } + if (param_gethex(Cmd, 0, MAC, 8)) { + PrintAndLog("MAC must include 8 HEX symbols"); + return 1; + } - UsbCommand c = {CMD_READER_ICLASS_REPLAY, {readerType}}; - memcpy(c.d.asBytes, MAC, 4); - SendCommand(&c); + UsbCommand c = {CMD_READER_ICLASS_REPLAY, {readerType}}; + memcpy(c.d.asBytes, MAC, 4); + SendCommand(&c); - return 0; + return 0; } int CmdHFiClassReader_Dump(const char *Cmd) { - uint8_t readerType = 0; - uint8_t MAC[4]={0x00,0x00,0x00,0x00}; - uint8_t KEY[8]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; - uint8_t CSN[8]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; - uint8_t CCNR[12]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; - //uint8_t CC_temp[8]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; - uint8_t div_key[8]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; - uint8_t keytable[128] = {0}; - int elite = 0; - uint8_t *used_key; - int i; - if (strlen(Cmd)<1) - { - PrintAndLog("Usage: hf iclass dump [e]"); - PrintAndLog(" Key - A 16 byte master key"); - PrintAndLog(" e - If 'e' is specified, the key is interpreted as the 16 byte"); - PrintAndLog(" Custom Key (KCus), which can be obtained via reader-attack"); - PrintAndLog(" See 'hf iclass sim 2'. This key should be on iclass-format"); - PrintAndLog(" sample: hf iclass dump 0011223344556677"); + uint8_t readerType = 0; + uint8_t MAC[4]={0x00,0x00,0x00,0x00}; + uint8_t KEY[8]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + uint8_t CSN[8]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + uint8_t CCNR[12]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + //uint8_t CC_temp[8]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + uint8_t div_key[8]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + uint8_t keytable[128] = {0}; + int elite = 0; + uint8_t *used_key; + int i; + if (strlen(Cmd)<1) + { + PrintAndLog("Usage: hf iclass dump [e]"); + PrintAndLog(" Key - A 16 byte master key"); + PrintAndLog(" e - If 'e' is specified, the key is interpreted as the 16 byte"); + PrintAndLog(" Custom Key (KCus), which can be obtained via reader-attack"); + PrintAndLog(" See 'hf iclass sim 2'. This key should be on iclass-format"); + PrintAndLog(" sample: hf iclass dump 0011223344556677"); - return 0; - } + return 0; + } - if (param_gethex(Cmd, 0, KEY, 16)) - { - PrintAndLog("KEY must include 16 HEX symbols"); - return 1; - } + if (param_gethex(Cmd, 0, KEY, 16)) + { + PrintAndLog("KEY must include 16 HEX symbols"); + return 1; + } - if (param_getchar(Cmd, 1) == 'e') - { - PrintAndLog("Elite switch on"); - elite = 1; + if (param_getchar(Cmd, 1) == 'e') + { + PrintAndLog("Elite switch on"); + elite = 1; - //calc h2 - hash2(KEY, keytable); - printarr_human_readable("keytable", keytable, 128); + //calc h2 + hash2(KEY, keytable); + printarr_human_readable("keytable", keytable, 128); - } + } - UsbCommand resp; - uint8_t key_sel[8] = {0}; - uint8_t key_sel_p[8] = { 0 }; + UsbCommand resp; + uint8_t key_sel[8] = {0}; + uint8_t key_sel_p[8] = { 0 }; - UsbCommand c = {CMD_READER_ICLASS, {0}}; - c.arg[0] = FLAG_ICLASS_READER_ONLY_ONCE| FLAG_ICLASS_READER_GET_CC; - SendCommand(&c); - + UsbCommand c = {CMD_READER_ICLASS, {0}}; + c.arg[0] = FLAG_ICLASS_READER_ONLY_ONCE| FLAG_ICLASS_READER_GET_CC; + SendCommand(&c); - if (!WaitForResponseTimeout(CMD_ACK,&resp,4500)) - { - PrintAndLog("Command execute timeout"); - return 0; - } + + if (!WaitForResponseTimeout(CMD_ACK,&resp,4500)) + { + PrintAndLog("Command execute timeout"); + return 0; + } uint8_t isOK = resp.arg[0] & 0xff; uint8_t * data = resp.d.asBytes; @@ -366,7 +366,7 @@ int CmdHFiClassReader_Dump(const char *Cmd) //create a preferred filename snprintf(filename, 100,"iclass_tagdump-%02x%02x%02x%02x%02x%02x%02x%02x", CSN[0],CSN[1],CSN[2],CSN[3], - CSN[4],CSN[5],CSN[6],CSN[7]); + CSN[4],CSN[5],CSN[6],CSN[7]); saveFile(filename,"bin",iclass_data, iclass_datalen ); } //Aaaand we're finished @@ -376,7 +376,7 @@ int CmdHFiClassReader_Dump(const char *Cmd) } - return 0; + return 0; } int hf_iclass_eload_usage() @@ -453,80 +453,80 @@ int CmdHFiClassELoad(const char *Cmd) int CmdHFiClass_iso14443A_write(const char *Cmd) { - uint8_t readerType = 0; - uint8_t MAC[4]={0x00,0x00,0x00,0x00}; - uint8_t KEY[8]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; - uint8_t CSN[8]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; - uint8_t CCNR[12]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; - uint8_t div_key[8]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + uint8_t readerType = 0; + uint8_t MAC[4]={0x00,0x00,0x00,0x00}; + uint8_t KEY[8]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + uint8_t CSN[8]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + uint8_t CCNR[12]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + uint8_t div_key[8]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; - uint8_t blockNo=0; - uint8_t bldata[8]={0}; + uint8_t blockNo=0; + uint8_t bldata[8]={0}; - if (strlen(Cmd)<3) - { - PrintAndLog("Usage: hf iclass write "); - PrintAndLog(" sample: hf iclass write 0011223344556677 10 AAAAAAAAAAAAAAAA"); - return 0; - } + if (strlen(Cmd)<3) + { + PrintAndLog("Usage: hf iclass write "); + PrintAndLog(" sample: hf iclass write 0011223344556677 10 AAAAAAAAAAAAAAAA"); + return 0; + } - if (param_gethex(Cmd, 0, KEY, 16)) - { - PrintAndLog("KEY must include 16 HEX symbols"); - return 1; - } - - blockNo = param_get8(Cmd, 1); - if (blockNo>32) - { - PrintAndLog("Error: Maximum number of blocks is 32 for iClass 2K Cards!"); - return 1; - } - if (param_gethex(Cmd, 2, bldata, 8)) - { - PrintAndLog("Block data must include 8 HEX symbols"); - return 1; - } - - UsbCommand c = {CMD_ICLASS_ISO14443A_WRITE, {0}}; - SendCommand(&c); - UsbCommand resp; + if (param_gethex(Cmd, 0, KEY, 16)) + { + PrintAndLog("KEY must include 16 HEX symbols"); + return 1; + } - if (WaitForResponseTimeout(CMD_ACK,&resp,4500)) { - uint8_t isOK = resp.arg[0] & 0xff; - uint8_t * data = resp.d.asBytes; - - memcpy(CSN,data,8); - memcpy(CCNR,data+8,8); - PrintAndLog("DEBUG: %s",sprint_hex(CSN,8)); - PrintAndLog("DEBUG: %s",sprint_hex(CCNR,8)); - PrintAndLog("isOk:%02x", isOK); - } else { - PrintAndLog("Command execute timeout"); - } + blockNo = param_get8(Cmd, 1); + if (blockNo>32) + { + PrintAndLog("Error: Maximum number of blocks is 32 for iClass 2K Cards!"); + return 1; + } + if (param_gethex(Cmd, 2, bldata, 8)) + { + PrintAndLog("Block data must include 8 HEX symbols"); + return 1; + } - diversifyKey(CSN,KEY, div_key); + UsbCommand c = {CMD_ICLASS_ISO14443A_WRITE, {0}}; + SendCommand(&c); + UsbCommand resp; - PrintAndLog("Div Key: %s",sprint_hex(div_key,8)); - doMAC(CCNR, 12,div_key, MAC); + if (WaitForResponseTimeout(CMD_ACK,&resp,4500)) { + uint8_t isOK = resp.arg[0] & 0xff; + uint8_t * data = resp.d.asBytes; - UsbCommand c2 = {CMD_ICLASS_ISO14443A_WRITE, {readerType,blockNo}}; - memcpy(c2.d.asBytes, bldata, 8); - memcpy(c2.d.asBytes+8, MAC, 4); - SendCommand(&c2); + memcpy(CSN,data,8); + memcpy(CCNR,data+8,8); + PrintAndLog("DEBUG: %s",sprint_hex(CSN,8)); + PrintAndLog("DEBUG: %s",sprint_hex(CCNR,8)); + PrintAndLog("isOk:%02x", isOK); + } else { + PrintAndLog("Command execute timeout"); + } - if (WaitForResponseTimeout(CMD_ACK,&resp,1500)) { - uint8_t isOK = resp.arg[0] & 0xff; - uint8_t * data = resp.d.asBytes; + diversifyKey(CSN,KEY, div_key); - if (isOK) - PrintAndLog("isOk:%02x data:%s", isOK, sprint_hex(data, 4)); - else - PrintAndLog("isOk:%02x", isOK); - } else { - PrintAndLog("Command execute timeout"); - } - return 0; + PrintAndLog("Div Key: %s",sprint_hex(div_key,8)); + doMAC(CCNR, 12,div_key, MAC); + + UsbCommand c2 = {CMD_ICLASS_ISO14443A_WRITE, {readerType,blockNo}}; + memcpy(c2.d.asBytes, bldata, 8); + memcpy(c2.d.asBytes+8, MAC, 4); + SendCommand(&c2); + + if (WaitForResponseTimeout(CMD_ACK,&resp,1500)) { + uint8_t isOK = resp.arg[0] & 0xff; + uint8_t * data = resp.d.asBytes; + + if (isOK) + PrintAndLog("isOk:%02x data:%s", isOK, sprint_hex(data, 4)); + else + PrintAndLog("isOk:%02x", isOK); + } else { + PrintAndLog("Command execute timeout"); + } + return 0; } int CmdHFiClass_loclass(const char *Cmd) { @@ -550,13 +550,13 @@ int CmdHFiClass_loclass(const char *Cmd) char fileName[255] = {0}; if(opt == 'f') { - if(param_getstr(Cmd, 1, fileName) > 0) - { - return bruteforceFileNoKeys(fileName); - }else - { - PrintAndLog("You must specify a filename"); - } + if(param_getstr(Cmd, 1, fileName) > 0) + { + return bruteforceFileNoKeys(fileName); + }else + { + PrintAndLog("You must specify a filename"); + } } else if(opt == 't') { @@ -591,12 +591,12 @@ static command_t CommandTable[] = int CmdHFiClass(const char *Cmd) { - CmdsParse(CommandTable, Cmd); - return 0; + CmdsParse(CommandTable, Cmd); + return 0; } int CmdHelp(const char *Cmd) { - CmdsHelp(CommandTable); - return 0; + CmdsHelp(CommandTable); + return 0; } From a4749080d947e31a3af08bf235173e8ed853d7b2 Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Sat, 14 Feb 2015 21:18:39 +0100 Subject: [PATCH 24/25] Removed un-implemented 'hf iclass write', it's confusing to have there since there's no actual support for this on the device side --- client/cmdhficlass.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/cmdhficlass.c b/client/cmdhficlass.c index 2d6a9beb..6c92893a 100644 --- a/client/cmdhficlass.c +++ b/client/cmdhficlass.c @@ -583,7 +583,7 @@ static command_t CommandTable[] = {"reader",CmdHFiClassReader, 0, "Read an iClass tag"}, {"replay",CmdHFiClassReader_Replay, 0, "Read an iClass tag via Reply Attack"}, {"dump", CmdHFiClassReader_Dump, 0, "Authenticate and Dump iClass tag"}, - {"write", CmdHFiClass_iso14443A_write, 0, "Authenticate and Write iClass block"}, +// {"write", CmdHFiClass_iso14443A_write, 0, "Authenticate and Write iClass block"}, {"loclass", CmdHFiClass_loclass, 1, "Use loclass to perform bruteforce of reader attack dump"}, {"eload", CmdHFiClassELoad, 0, "[experimental] Load data into iclass emulator memory"}, {NULL, NULL, 0, NULL} From e80aeb969524acf070170d4224b8fb660e6c19f6 Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Sat, 14 Feb 2015 21:30:26 +0100 Subject: [PATCH 25/25] Started iclass emulator support on device side. Not functional yet --- armsrc/BigBuf.c | 13 +++++++++++++ armsrc/BigBuf.h | 1 + armsrc/appmain.c | 3 +++ 3 files changed, 17 insertions(+) diff --git a/armsrc/BigBuf.c b/armsrc/BigBuf.c index 0c666bce..703ade65 100644 --- a/armsrc/BigBuf.c +++ b/armsrc/BigBuf.c @@ -224,3 +224,16 @@ int LogTraceHitag(const uint8_t * btBytes, int iBits, int iSamples, uint32_t dwP return TRUE; } +// Emulator memory +uint8_t emlSet(uint8_t *data, uint32_t offset, uint32_t length){ + uint8_t* mem = BigBuf_get_EM_addr(); + if(offset+length < CARD_MEMORY_SIZE) + { + memcpy(mem+offset, data, length); + return 0; + }else + { + Dbprintf("Error, trying to set memory outside of bounds! %d > %d", (offset+length), CARD_MEMORY_SIZE); + return 1; + } +} diff --git a/armsrc/BigBuf.h b/armsrc/BigBuf.h index be558979..b44a1263 100644 --- a/armsrc/BigBuf.h +++ b/armsrc/BigBuf.h @@ -34,4 +34,5 @@ void clear_trace(); void set_tracing(bool enable); bool RAMFUNC LogTrace(const uint8_t *btBytes, uint16_t iLen, uint32_t timestamp_start, uint32_t timestamp_end, uint8_t *parity, bool readerToTag); int LogTraceHitag(const uint8_t * btBytes, int iBits, int iSamples, uint32_t dwParity, int bReader); +uint8_t emlSet(uint8_t *data, uint32_t offset, uint32_t length); #endif /* __BIGBUF_H */ diff --git a/armsrc/appmain.c b/armsrc/appmain.c index 43f1df02..3da34777 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -900,6 +900,9 @@ void UsbPacketReceived(uint8_t *packet, int len) case CMD_READER_ICLASS_REPLAY: ReaderIClass_Replay(c->arg[0], c->d.asBytes); break; + case CMD_ICLASS_EML_MEMSET: + emlSet(c->d.asBytes,c->arg[0], c->arg[1]); + break; #endif case CMD_SIMULATE_TAG_HF_LISTEN: