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 d326be2c5..f5348e775 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 674f1b91f..cce9bed80 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 e9483189d..4d7d4fa8f 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 f5348e775..d91b24d76 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 c595d5e12..c37103886 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 cce9bed80..a4f558794 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 d7eacd705..141d74b9d 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 530dc39cd..bca31533d 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 4d7d4fa8f..3c8e1fdda 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 d91b24d76..54c1db407 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 a4f558794..38f417507 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 9acc9825b..373668027 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 e3d0fc230..3aaf45fa8 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 986c94461..80086f49c 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 3c8e1fdda..0a6d2d67f 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 0a6d2d67f..39112fdfc 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 39112fdfc..1191c5bf5 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 373668027..0f31da4d6 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 7f56e9a01..1b5e54823 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 9d89a4f0f..5cca1000f 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 7c50a51ec..bbf772aca 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 58a2a6219..0f7714e7b 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 4a2d9d9d4..7ca799305 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 1a3751182..41c9b8b51 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 92d057820..c7f49f140 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 336250ede..05ffb9417 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 4279e63f2..a16cbf166 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 59e846975..0cc2963bc 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 4948fce8f..889319279 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 e00663028..bf5d0cc81 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 1b5e54823..18db66f3b 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 5cca1000f..be558979a 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 7ca799305..4b173d6f2 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 18db66f3b..6e99a793f 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 889319279..74fba94b7 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 6e99a793f..0c666bce8 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 f225359d7..d0852ea5b 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 7f7848508..35499b836 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 96eb75f70..a11f11d50 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 d6ab2000a..b843730f6 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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%0DFjfQJp0 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 LoP&sIDX1X;X@DZ5 diff --git a/fpga/hi_iso14443a.v b/fpga/hi_iso14443a.v index 3f614fdd8..46adda12c 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 bec79e61d..0006d59d0 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 a7102168e..f2fa1ff29 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 c37103886..1e978e880 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 147e790ec..744b38758 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 07a4aa495..7f1246cc3 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 b7c1b13f7..f268eaa23 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 6b47eab9e..edd9aebce 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 348549ef5..138814b7e 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 41c9b8b51..2a0ba0d2f 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 03b39021e..b458ae03b 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 b458ae03b..dd0a8e2fd 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 6ee6509eb..d9a950aef 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 dd0a8e2fd..2d6a9beb9 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 2d6a9beb9..6c92893ae 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 0c666bce8..703ade658 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 be558979a..b44a12630 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 43f1df020..3da34777e 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: