From 22e2470051913380bbfa50b0ef9b7ad999364cdc Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Sun, 21 Jun 2015 11:07:05 +0200 Subject: [PATCH] ADD: @marshmellow42 fudan detection in hf mfu ADD: @marshmellow42 14b reader changes. ADD: @pwpiwi 14b fixes --- armsrc/appmain.c | 67 +---- armsrc/iso14443b.c | 131 +++++----- client/cmdhf.c | 20 +- client/cmdhf14b.c | 495 +++++++++++++++--------------------- client/cmdhf14b.h | 7 +- client/cmdhfmfu.c | 41 ++- client/cmdhfmfu.h | 3 +- client/lualibs/commands.lua | 6 +- cp2tau | 4 - fpga/fpga_hf.bit | Bin 42175 -> 42175 bytes fpga/fpga_hf.v | 5 +- fpga/hi_read_rx_xcorr.v | 76 ++---- include/mifare.h | 2 +- include/usb_cmd.h | 7 +- 14 files changed, 349 insertions(+), 515 deletions(-) delete mode 100644 cp2tau diff --git a/armsrc/appmain.c b/armsrc/appmain.c index c7061aab7..9db69f0c8 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -18,12 +18,13 @@ #include "util.h" #include "printf.h" #include "string.h" + #include + #include "legicrf.h" #include #include "lfsampling.h" #include "BigBuf.h" - #ifdef WITH_LCD #include "LCD.h" #endif @@ -249,55 +250,6 @@ void MeasureAntennaTuningHf(void) } -void SimulateTagHfListen(void) -{ - // ToDo: historically this used the free buffer, which was 2744 Bytes long. - // There might be a better size to be defined: - #define HF_14B_SNOOP_BUFFER_SIZE 2744 - uint8_t *dest = BigBuf_malloc(HF_14B_SNOOP_BUFFER_SIZE); - uint8_t v = 0; - int i; - int p = 0; - - // We're using this mode just so that I can test it out; the simulated - // tag mode would work just as well and be simpler. - FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR | FPGA_HF_READER_RX_XCORR_848_KHZ | FPGA_HF_READER_RX_XCORR_SNOOP); - - // We need to listen to the high-frequency, peak-detected path. - SetAdcMuxFor(GPIO_MUXSEL_HIPKD); - - FpgaSetupSsc(); - - i = 0; - for(;;) { - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = 0xff; - } - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - uint8_t r = (uint8_t)AT91C_BASE_SSC->SSC_RHR; - - v <<= 1; - if(r & 1) { - v |= 1; - } - p++; - - if(p >= 8) { - dest[i] = v; - v = 0; - p = 0; - i++; - - if(i >= HF_14B_SNOOP_BUFFER_SIZE) { - break; - } - } - } - } - DbpString("simulate tag (now type bitsamples)"); -} - void ReadMem(int addr) { const uint8_t *data = ((uint8_t *)addr); @@ -781,19 +733,16 @@ void UsbPacketReceived(uint8_t *packet, int len) #endif #ifdef WITH_ISO14443b - case CMD_ACQUIRE_RAW_ADC_SAMPLES_ISO_14443: - AcquireRawAdcSamplesIso14443b(c->arg[0]); - break; case CMD_READ_SRI512_TAG: ReadSTMemoryIso14443b(0x0F); break; case CMD_READ_SRIX4K_TAG: ReadSTMemoryIso14443b(0x7F); break; - case CMD_SNOOP_ISO_14443: + case CMD_SNOOP_ISO_14443B: SnoopIso14443b(); break; - case CMD_SIMULATE_TAG_ISO_14443: + case CMD_SIMULATE_TAG_ISO_14443B: SimulateIso14443bTag(); break; case CMD_ISO_14443B_COMMAND: @@ -816,10 +765,6 @@ void UsbPacketReceived(uint8_t *packet, int len) EPA_PACE_Collect_Nonce(c); break; - // case CMD_EPA_: - // EpaFoo(c); - // break; - case CMD_READER_MIFARE: ReaderMifare(c->arg[0]); break; @@ -936,10 +881,6 @@ void UsbPacketReceived(uint8_t *packet, int len) break; #endif - case CMD_SIMULATE_TAG_HF_LISTEN: - SimulateTagHfListen(); - break; - case CMD_BUFF_CLEAR: BigBuf_Clear(); break; diff --git a/armsrc/iso14443b.c b/armsrc/iso14443b.c index 160ec1eca..53f7e74d0 100644 --- a/armsrc/iso14443b.c +++ b/armsrc/iso14443b.c @@ -17,7 +17,6 @@ #include "iso14443crc.h" #define RECEIVE_SAMPLES_TIMEOUT 2000 -#define ISO14443B_DMA_BUFFER_SIZE 512 //============================================================================= // An ISO 14443 Type B tag. We listen for commands from the reader, using @@ -238,7 +237,11 @@ static int Handle14443bUartBit(int bit) } else if(Uart.shiftReg == 0x000) { // this is an EOF byte LED_A_OFF(); // Finished receiving + if (Uart.byteCnt != 0) { return TRUE; + } + Uart.posCnt = 0; + Uart.state = STATE_ERROR_WAIT; } else { // this is an error Uart.posCnt = 0; @@ -715,38 +718,38 @@ static void GetSamplesFor14443bDemod(int n, bool quiet) uint8_t *receivedResponse = BigBuf_malloc(MAX_FRAME_SIZE); // The DMA buffer, used to stream samples from the FPGA - int8_t *dmaBuf = (int8_t*) BigBuf_malloc(ISO14443B_DMA_BUFFER_SIZE); + int8_t *dmaBuf = (int8_t*) BigBuf_malloc(DMA_BUFFER_SIZE); // Set up the demodulator for tag -> reader responses. DemodInit(receivedResponse); // Setup and start DMA. - FpgaSetupSscDma((uint8_t*) dmaBuf, ISO14443B_DMA_BUFFER_SIZE); + FpgaSetupSscDma((uint8_t*) dmaBuf, DMA_BUFFER_SIZE); int8_t *upTo = dmaBuf; - lastRxCounter = ISO14443B_DMA_BUFFER_SIZE; + lastRxCounter = DMA_BUFFER_SIZE; // Signal field is ON with the appropriate LED: LED_D_ON(); // And put the FPGA in the appropriate mode - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR | FPGA_HF_READER_RX_XCORR_848_KHZ); for(;;) { int behindBy = lastRxCounter - AT91C_BASE_PDC_SSC->PDC_RCR; if(behindBy > max) max = behindBy; - while(((lastRxCounter-AT91C_BASE_PDC_SSC->PDC_RCR) & (ISO14443B_DMA_BUFFER_SIZE-1)) > 2) { + while(((lastRxCounter-AT91C_BASE_PDC_SSC->PDC_RCR) & (DMA_BUFFER_SIZE-1)) > 2) { ci = upTo[0]; cq = upTo[1]; upTo += 2; - if(upTo >= dmaBuf + ISO14443B_DMA_BUFFER_SIZE) { + if(upTo >= dmaBuf + DMA_BUFFER_SIZE) { upTo = dmaBuf; AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) upTo; - AT91C_BASE_PDC_SSC->PDC_RNCR = ISO14443B_DMA_BUFFER_SIZE; + AT91C_BASE_PDC_SSC->PDC_RNCR = DMA_BUFFER_SIZE; } lastRxCounter -= 2; if(lastRxCounter <= 0) { - lastRxCounter += ISO14443B_DMA_BUFFER_SIZE; + lastRxCounter += DMA_BUFFER_SIZE; } samples += 2; @@ -768,7 +771,7 @@ static void GetSamplesFor14443bDemod(int n, bool quiet) //Tracing if (tracing && Demod.len > 0) { uint8_t parity[MAX_PARITY_SIZE]; - GetParity(Demod.output, Demod.len, parity); + //GetParity(Demod.output, Demod.len, parity); LogTrace(Demod.output, Demod.len, 0, 0, parity, FALSE); } } @@ -881,22 +884,6 @@ static void CodeIso14443bAsReader(const uint8_t *cmd, int len) } -//----------------------------------------------------------------------------- -// Read an ISO 14443B tag. We send it some set of commands, and record the -// responses. -// The command name is misleading, it actually decodes the reponse in HEX -// into the output buffer (read the result using hexsamples, not hisamples) -// -// obsolete function only for test -//----------------------------------------------------------------------------- -void AcquireRawAdcSamplesIso14443b(uint32_t parameter) -{ - uint8_t cmd1[] = { 0x05, 0x00, 0x08, 0x39, 0x73 }; // REQB with AFI=0, Request All, N=0 - - SendRawCommand14443B(sizeof(cmd1),1,1,cmd1); -} - - /** Convenience function to encode, transmit and trace iso 14443b comms **/ @@ -941,8 +928,7 @@ void ReadSTMemoryIso14443b(uint32_t dwLast) // Now give it time to spin up. // Signal field is on with the appropriate LED LED_D_ON(); - FpgaWriteConfWord( - FPGA_MAJOR_MODE_HF_READER_RX_XCORR | FPGA_HF_READER_RX_XCORR_848_KHZ); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR | FPGA_HF_READER_RX_XCORR_848_KHZ); SpinDelay(200); // First command: wake up the tag using the INITIATE command @@ -954,10 +940,10 @@ void ReadSTMemoryIso14443b(uint32_t dwLast) // LED_A_OFF(); if (Demod.len == 0) { - DbpString("No response from tag"); - return; + DbpString("No response from tag"); + return; } else { - Dbprintf("Randomly generated UID from tag (+ 2 byte CRC): %x %x %x", + Dbprintf("Randomly generated UID from tag (+ 2 byte CRC): %02x %02x %02x", Demod.output[0], Demod.output[1],Demod.output[2]); } // There is a response, SELECT the uid @@ -971,19 +957,19 @@ void ReadSTMemoryIso14443b(uint32_t dwLast) GetSamplesFor14443bDemod(RECEIVE_SAMPLES_TIMEOUT, TRUE); // LED_A_OFF(); if (Demod.len != 3) { - Dbprintf("Expected 3 bytes from tag, got %d", Demod.len); - return; + Dbprintf("Expected 3 bytes from tag, got %d", Demod.len); + return; } // Check the CRC of the answer: ComputeCrc14443(CRC_14443_B, Demod.output, 1 , &cmd1[2], &cmd1[3]); if(cmd1[2] != Demod.output[1] || cmd1[3] != Demod.output[2]) { - DbpString("CRC Error reading select response."); - return; + DbpString("CRC Error reading select response."); + return; } // Check response from the tag: should be the same UID as the command we just sent: if (cmd1[1] != Demod.output[0]) { - Dbprintf("Bad response to SELECT from Tag, aborting: %x %x", cmd1[1], Demod.output[0]); - return; + Dbprintf("Bad response to SELECT from Tag, aborting: %02x %02x", cmd1[1], Demod.output[0]); + return; } // Tag is now selected, // First get the tag's UID: @@ -995,19 +981,21 @@ void ReadSTMemoryIso14443b(uint32_t dwLast) GetSamplesFor14443bDemod(RECEIVE_SAMPLES_TIMEOUT, TRUE); // LED_A_OFF(); if (Demod.len != 10) { - Dbprintf("Expected 10 bytes from tag, got %d", Demod.len); - return; + Dbprintf("Expected 10 bytes from tag, got %d", Demod.len); + return; } // The check the CRC of the answer (use cmd1 as temporary variable): ComputeCrc14443(CRC_14443_B, Demod.output, 8, &cmd1[2], &cmd1[3]); - if(cmd1[2] != Demod.output[8] || cmd1[3] != Demod.output[9]) { - Dbprintf("CRC Error reading block! - Below: expected, got %x %x", - (cmd1[2]<<8)+cmd1[3], (Demod.output[8]<<8)+Demod.output[9]); + if(cmd1[2] != Demod.output[8] || cmd1[3] != Demod.output[9]) { + Dbprintf("CRC Error reading block! Expected: %04x got: %04x", + (cmd1[2]<<8)+cmd1[3], + (Demod.output[8]<<8)+Demod.output[9] + ); // Do not return;, let's go on... (we should retry, maybe ?) } Dbprintf("Tag UID (64 bits): %08x %08x", - (Demod.output[7]<<24) + (Demod.output[6]<<16) + (Demod.output[5]<<8) + Demod.output[4], - (Demod.output[3]<<24) + (Demod.output[2]<<16) + (Demod.output[1]<<8) + Demod.output[0]); + (Demod.output[7]<<24) + (Demod.output[6]<<16) + (Demod.output[5]<<8) + Demod.output[4], + (Demod.output[3]<<24) + (Demod.output[2]<<16) + (Demod.output[1]<<8) + Demod.output[0]); // Now loop to read all 16 blocks, address from 0 to last block Dbprintf("Tag memory dump, block 0 to %d",dwLast); @@ -1033,17 +1021,18 @@ void ReadSTMemoryIso14443b(uint32_t dwLast) // The check the CRC of the answer (use cmd1 as temporary variable): ComputeCrc14443(CRC_14443_B, Demod.output, 4, &cmd1[2], &cmd1[3]); if(cmd1[2] != Demod.output[4] || cmd1[3] != Demod.output[5]) { - Dbprintf("CRC Error reading block! - Below: expected, got %x %x", - (cmd1[2]<<8)+cmd1[3], (Demod.output[4]<<8)+Demod.output[5]); + Dbprintf("CRC Error reading block! Expected: %04x got: %04x", + (cmd1[2]<<8)+cmd1[3], + (Demod.output[4]<<8)+Demod.output[5] + ); // Do not return;, let's go on... (we should retry, maybe ?) } // Now print out the memory location: - Dbprintf("Address=%x, Contents=%x, CRC=%x", i, - (Demod.output[3]<<24) + (Demod.output[2]<<16) + (Demod.output[1]<<8) + Demod.output[0], - (Demod.output[4]<<8)+Demod.output[5]); - if (i == 0xff) { - break; - } + Dbprintf("Address=%02x, Contents=%08x, CRC=%04x", i, + (Demod.output[3]<<24) + (Demod.output[2]<<16) + (Demod.output[1]<<8) + Demod.output[0], + (Demod.output[4]<<8)+Demod.output[5] + ); + if (i == 0xff) break; i++; } } @@ -1063,7 +1052,7 @@ void ReadSTMemoryIso14443b(uint32_t dwLast) * Memory usage for this function, (within BigBuf) * Last Received command (reader->tag) - MAX_FRAME_SIZE * Last Received command (tag->reader) - MAX_FRAME_SIZE - * DMA Buffer - ISO14443B_DMA_BUFFER_SIZE + * DMA Buffer - DMA_BUFFER_SIZE * Demodulated samples received - all the rest */ void RAMFUNC SnoopIso14443b(void) @@ -1080,7 +1069,7 @@ void RAMFUNC SnoopIso14443b(void) set_tracing(TRUE); // The DMA buffer, used to stream samples from the FPGA - int8_t *dmaBuf = (int8_t*) BigBuf_malloc(ISO14443B_DMA_BUFFER_SIZE); + int8_t *dmaBuf = (int8_t*) BigBuf_malloc(DMA_BUFFER_SIZE); int lastRxCounter; int8_t *upTo; int ci, cq; @@ -1098,22 +1087,20 @@ void RAMFUNC SnoopIso14443b(void) Dbprintf(" Trace: %i bytes", BigBuf_max_traceLen()); Dbprintf(" Reader -> tag: %i bytes", MAX_FRAME_SIZE); Dbprintf(" tag -> Reader: %i bytes", MAX_FRAME_SIZE); - Dbprintf(" DMA: %i bytes", ISO14443B_DMA_BUFFER_SIZE); + Dbprintf(" DMA: %i bytes", DMA_BUFFER_SIZE); // Signal field is off, no reader signal, no tag signal LEDsoff(); // 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); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR | FPGA_HF_READER_RX_XCORR_848_KHZ | FPGA_HF_READER_RX_XCORR_SNOOP); SetAdcMuxFor(GPIO_MUXSEL_HIPKD); // Setup for the DMA. FpgaSetupSsc(); upTo = dmaBuf; - lastRxCounter = ISO14443B_DMA_BUFFER_SIZE; - FpgaSetupSscDma((uint8_t*) dmaBuf, ISO14443B_DMA_BUFFER_SIZE); + lastRxCounter = DMA_BUFFER_SIZE; + FpgaSetupSscDma((uint8_t*) dmaBuf, DMA_BUFFER_SIZE); uint8_t parity[MAX_PARITY_SIZE]; bool TagIsActive = FALSE; @@ -1122,7 +1109,7 @@ void RAMFUNC SnoopIso14443b(void) // And now we loop, receiving samples. for(;;) { int behindBy = (lastRxCounter - AT91C_BASE_PDC_SSC->PDC_RCR) & - (ISO14443B_DMA_BUFFER_SIZE-1); + (DMA_BUFFER_SIZE-1); if(behindBy > maxBehindBy) { maxBehindBy = behindBy; } @@ -1133,14 +1120,14 @@ void RAMFUNC SnoopIso14443b(void) cq = upTo[1]; upTo += 2; lastRxCounter -= 2; - if(upTo >= dmaBuf + ISO14443B_DMA_BUFFER_SIZE) { + if(upTo >= dmaBuf + DMA_BUFFER_SIZE) { upTo = dmaBuf; - lastRxCounter += ISO14443B_DMA_BUFFER_SIZE; + lastRxCounter += DMA_BUFFER_SIZE; AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dmaBuf; - AT91C_BASE_PDC_SSC->PDC_RNCR = ISO14443B_DMA_BUFFER_SIZE; + AT91C_BASE_PDC_SSC->PDC_RNCR = DMA_BUFFER_SIZE; WDT_HIT(); - if(behindBy > (9*ISO14443B_DMA_BUFFER_SIZE/10)) { // TODO: understand whether we can increase/decrease as we want or not? - Dbprintf("blew circular buffer! behindBy=0x%x", behindBy); + if(behindBy > (9*DMA_BUFFER_SIZE/10)) { // TODO: understand whether we can increase/decrease as we want or not? + Dbprintf("blew circular buffer! behindBy=%d", behindBy); break; } if(!tracing) { @@ -1158,7 +1145,7 @@ void RAMFUNC SnoopIso14443b(void) if (!TagIsActive) { // no need to try decoding reader data if the tag is sending if(Handle14443bUartBit(ci & 0x01)) { if(triggered && tracing) { - GetParity(Uart.output, Uart.byteCnt, parity); + //GetParity(Uart.output, Uart.byteCnt, parity); LogTrace(Uart.output,Uart.byteCnt,samples, samples,parity,TRUE); } /* And ready to receive another command. */ @@ -1169,7 +1156,7 @@ void RAMFUNC SnoopIso14443b(void) } if(Handle14443bUartBit(cq & 0x01)) { if(triggered && tracing) { - GetParity(Uart.output, Uart.byteCnt, parity); + //GetParity(Uart.output, Uart.byteCnt, parity); LogTrace(Uart.output,Uart.byteCnt,samples, samples, parity, TRUE); } /* And ready to receive another command. */ @@ -1182,13 +1169,13 @@ void RAMFUNC SnoopIso14443b(void) } if(!ReaderIsActive) { // no need to try decoding tag data if the reader is sending - and we cannot afford the time - if(Handle14443bSamplesDemod(ci & 0xFE, cq & 0xFE)) { + if(Handle14443bSamplesDemod(ci, cq)) { //Use samples as a time measurement if(tracing) { uint8_t parity[MAX_PARITY_SIZE]; - GetParity(Demod.output, Demod.len, parity); + //GetParity(Demod.output, Demod.len, parity); LogTrace(Demod.output, Demod.len,samples, samples, parity, FALSE); } triggered = TRUE; @@ -1196,7 +1183,7 @@ void RAMFUNC SnoopIso14443b(void) // And ready to receive another response. DemodReset(); } - TagIsActive = (Demod.state > DEMOD_PHASE_REF_TRAINING); + TagIsActive = (Demod.state > DEMOD_GOT_FALLING_EDGE_OF_SOF); } } @@ -1243,7 +1230,7 @@ void SendRawCommand14443B(uint32_t datalen, uint32_t recv, uint8_t powerfield, u */ // if(!GETBIT(GPIO_LED_D)) { // if field is off - // FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); + // FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR | FPGA_HF_READER_RX_XCORR_848_KHZ); // // Signal field is on with the appropriate LED // LED_D_ON(); // SpinDelay(200); diff --git a/client/cmdhf.c b/client/cmdhf.c index 614dcdc44..4efa55bf5 100644 --- a/client/cmdhf.c +++ b/client/cmdhf.c @@ -36,6 +36,7 @@ int CmdHFTune(const char *Cmd) return 0; } + void annotateIso14443a(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize) { switch(cmd[0]) @@ -92,7 +93,6 @@ void annotateIso14443a(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize) else snprintf(exp,size,"PWD-AUTH"); break; - case MIFARE_ULEV1_FASTREAD : { if ( cmdsize >=3 && cmd[2] <= 0xE6) snprintf(exp,size,"READ RANGE (%d-%d)",cmd[1],cmd[2]); @@ -483,11 +483,13 @@ uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *trace, ui oddparity ^= (((frame[j] & 0xFF) >> k) & 0x01); } uint8_t parityBits = parityBytes[j>>3]; - if (isResponse && (oddparity != ((parityBits >> (7-(j&0x0007))) & 0x01))) { + if (protocol != ISO_14443B && isResponse && (oddparity != ((parityBits >> (7-(j&0x0007))) & 0x01))) { snprintf(line[j/16]+(( j % 16) * 4),110, "%02x! ", frame[j]); + } else { snprintf(line[j/16]+(( j % 16) * 4),110, "%02x ", frame[j]); } + } if (markCRCBytes) { @@ -663,6 +665,11 @@ int CmdHFSearch(const char *Cmd){ PrintAndLog("\nValid ISO14443A Tag Found - Quiting Search\n"); return ans; } + ans = HF14BReader(false); + if (ans) { + PrintAndLog("\nValid ISO14443B Tag Found - Quiting Search\n"); + return ans; + } ans = HFiClassReader("", false, false); if (ans) { PrintAndLog("\nValid iClass Tag (or PicoPass Tag) Found - Quiting Search\n"); @@ -673,12 +680,7 @@ int CmdHFSearch(const char *Cmd){ PrintAndLog("\nValid ISO15693 Tag Found - Quiting Search\n"); return ans; } - - - //14b has issues currently... - //ans = CmdHF14BRead(Cmd); - //if (ans > 0) return ans; - + PrintAndLog("\nno known/supported 13.56 MHz tags found\n"); return 0; } @@ -697,7 +699,7 @@ static command_t CommandTable[] = {"topaz", CmdHFTopaz, 1, "{ TOPAZ (NFC Type 1) RFIDs... }"}, {"tune", CmdHFTune, 0, "Continuously measure HF antenna tuning"}, {"list", CmdHFList, 1, "List protocol data in trace buffer"}, - {"search", CmdHFSearch, 1, "Search for known HF tags [preliminary]"}, + {"search", CmdHFSearch, 1, "Search for known HF tags"}, {NULL, NULL, 0, NULL} }; diff --git a/client/cmdhf14b.c b/client/cmdhf14b.c index 52888bfdf..0144111e7 100644 --- a/client/cmdhf14b.c +++ b/client/cmdhf14b.c @@ -23,129 +23,9 @@ #include "cmdhf14b.h" #include "cmdmain.h" #include "cmdhf14a.h" -//#include "sleep.h" -#include "cmddata.h" static int CmdHelp(const char *Cmd); -int CmdHF14BDemod(const char *Cmd) -{ - int i, j, iold; - int isum, qsum; - int outOfWeakAt; - bool negateI, negateQ; - - uint8_t data[256]; - int dataLen = 0; - - // As received, the samples are pairs, correlations against I and Q - // square waves. So estimate angle of initial carrier (or just - // quadrant, actually), and then do the demod. - - // First, estimate where the tag starts modulating. - for (i = 0; i < GraphTraceLen; i += 2) { - if (abs(GraphBuffer[i]) + abs(GraphBuffer[i + 1]) > 40) { - break; - } - } - if (i >= GraphTraceLen) { - PrintAndLog("too weak to sync"); - return 0; - } - PrintAndLog("out of weak at %d", i); - outOfWeakAt = i; - - // Now, estimate the phase in the initial modulation of the tag - isum = 0; - qsum = 0; - for (; i < (outOfWeakAt + 16); i += 2) { - isum += GraphBuffer[i + 0]; - qsum += GraphBuffer[i + 1]; - } - negateI = (isum < 0); - negateQ = (qsum < 0); - - // Turn the correlation pairs into soft decisions on the bit. - j = 0; - for (i = 0; i < GraphTraceLen / 2; i++) { - int si = GraphBuffer[j]; - int sq = GraphBuffer[j + 1]; - if (negateI) si = -si; - if (negateQ) sq = -sq; - GraphBuffer[i] = si + sq; - j += 2; - } - GraphTraceLen = i; - - i = outOfWeakAt / 2; - while (GraphBuffer[i] > 0 && i < GraphTraceLen) - i++; - if (i >= GraphTraceLen) goto demodError; - - iold = i; - while (GraphBuffer[i] < 0 && i < GraphTraceLen) - i++; - if (i >= GraphTraceLen) goto demodError; - if ((i - iold) > 23) goto demodError; - - PrintAndLog("make it to demod loop"); - - for (;;) { - iold = i; - while (GraphBuffer[i] >= 0 && i < GraphTraceLen) - i++; - if (i >= GraphTraceLen) goto demodError; - if ((i - iold) > 6) goto demodError; - - uint16_t shiftReg = 0; - if (i + 20 >= GraphTraceLen) goto demodError; - - for (j = 0; j < 10; j++) { - int soft = GraphBuffer[i] + GraphBuffer[i + 1]; - - if (abs(soft) < (abs(isum) + abs(qsum)) / 20) { - PrintAndLog("weak bit"); - } - - shiftReg >>= 1; - if(GraphBuffer[i] + GraphBuffer[i+1] >= 0) { - shiftReg |= 0x200; - } - - i+= 2; - } - - if ((shiftReg & 0x200) && !(shiftReg & 0x001)) - { - // valid data byte, start and stop bits okay - PrintAndLog(" %02x", (shiftReg >> 1) & 0xff); - data[dataLen++] = (shiftReg >> 1) & 0xff; - if (dataLen >= sizeof(data)) { - return 0; - } - } else if (shiftReg == 0x000) { - // this is EOF - break; - } else { - goto demodError; - } - } - - uint8_t first, second; - ComputeCrc14443(CRC_14443_B, data, dataLen-2, &first, &second); - PrintAndLog("CRC: %02x %02x (%s)\n", first, second, - (first == data[dataLen-2] && second == data[dataLen-1]) ? - "ok" : "****FAIL****"); - - RepaintGraphWindow(); - return 0; - -demodError: - PrintAndLog("demod error"); - RepaintGraphWindow(); - return 0; -} - int CmdHF14BList(const char *Cmd) { PrintAndLog("Deprecated command, use 'hf list 14b' instead"); @@ -153,28 +33,20 @@ int CmdHF14BList(const char *Cmd) return 0; } -int CmdHF14Sim(const char *Cmd) +int CmdHF14BSim(const char *Cmd) { - UsbCommand c={CMD_SIMULATE_TAG_ISO_14443}; + UsbCommand c={CMD_SIMULATE_TAG_ISO_14443B}; clearCommandBuffer(); - SendCommand(&c); - return 0; -} - -int CmdHFSimlisten(const char *Cmd) -{ - UsbCommand c = {CMD_SIMULATE_TAG_HF_LISTEN}; - clearCommandBuffer(); - SendCommand(&c); - return 0; + SendCommand(&c); + return 0; } int CmdHF14BSnoop(const char *Cmd) { - UsbCommand c = {CMD_SNOOP_ISO_14443}; + UsbCommand c = {CMD_SNOOP_ISO_14443B}; clearCommandBuffer(); - SendCommand(&c); - return 0; + SendCommand(&c); + return 0; } /* New command to read the contents of a SRI512 tag @@ -183,10 +55,10 @@ int CmdHF14BSnoop(const char *Cmd) */ int CmdSri512Read(const char *Cmd) { - UsbCommand c = {CMD_READ_SRI512_TAG, {strtol(Cmd, NULL, 0), 0, 0}}; + UsbCommand c = {CMD_READ_SRI512_TAG, {strtol(Cmd, NULL, 0), 0, 0}}; clearCommandBuffer(); - SendCommand(&c); - return 0; + SendCommand(&c); + return 0; } /* New command to read the contents of a SRIX4K tag @@ -195,14 +67,14 @@ int CmdSri512Read(const char *Cmd) */ int CmdSrix4kRead(const char *Cmd) { - UsbCommand c = {CMD_READ_SRIX4K_TAG, {strtol(Cmd, NULL, 0), 0, 0}}; + UsbCommand c = {CMD_READ_SRIX4K_TAG, {strtol(Cmd, NULL, 0), 0, 0}}; clearCommandBuffer(); - SendCommand(&c); - return 0; + SendCommand(&c); + return 0; } int rawClose(void){ - UsbCommand resp; + UsbCommand resp; UsbCommand c = {CMD_ISO_14443B_COMMAND, {0, 0, 0}}; clearCommandBuffer(); SendCommand(&c); @@ -212,54 +84,54 @@ int rawClose(void){ return 0; } -int HF14BCmdRaw(bool reply, bool *crc, uint8_t power_trace, uint8_t *data, uint8_t *datalen, bool verbose){ +int HF14BCmdRaw(bool reply, bool *crc, bool power, uint8_t *data, uint8_t *datalen, bool verbose){ UsbCommand resp; - UsbCommand c = {CMD_ISO_14443B_COMMAND, {0, 0, 0}}; // len,recv,power/trace - if(*crc) - { - uint8_t first, second; - ComputeCrc14443(CRC_14443_B, data, *datalen, &first, &second); - data[*datalen] = first; - data[*datalen + 1] = second; - *datalen += 2; - } - - c.arg[0] = *datalen; - c.arg[1] = reply; - c.arg[2] = power_trace; - memcpy(c.d.asBytes,data,*datalen); + UsbCommand c = {CMD_ISO_14443B_COMMAND, {0, 0, 0}}; // len,recv,power + if(*crc) + { + uint8_t first, second; + ComputeCrc14443(CRC_14443_B, data, *datalen, &first, &second); + data[*datalen] = first; + data[*datalen + 1] = second; + *datalen += 2; + } + + c.arg[0] = *datalen; + c.arg[1] = reply; + c.arg[2] = power; + memcpy(c.d.asBytes,data,*datalen); clearCommandBuffer(); - SendCommand(&c); - - if (!reply) return 1; + SendCommand(&c); - if (!WaitForResponseTimeout(CMD_ACK,&resp,1000)) { - if (verbose) PrintAndLog("timeout while waiting for reply."); - return 0; - } - *datalen = resp.arg[0]; - if (verbose) PrintAndLog("received %u octets", *datalen); - if(*datalen<2) return 0; + if (!reply) return 1; - memcpy(data, resp.d.asBytes, *datalen); - if (verbose) PrintAndLog("%s", sprint_hex(data, *datalen)); + if (!WaitForResponseTimeout(CMD_ACK,&resp,1000)) { + if (verbose) PrintAndLog("timeout while waiting for reply."); + return 0; + } + *datalen = resp.arg[0]; + if (verbose) PrintAndLog("received %u octets", *datalen); + if(*datalen<2) return 0; - uint8_t first, second; - ComputeCrc14443(CRC_14443_B, data, *datalen-2, &first, &second); - if(data[*datalen-2] == first && data[*datalen-1] == second) { - if (verbose) PrintAndLog("CRC OK"); - *crc = true; - } else { - if (verbose) PrintAndLog("CRC failed"); - *crc = false; - } - return 1; + memcpy(data, resp.d.asBytes, *datalen); + if (verbose) PrintAndLog("%s", sprint_hex(data, *datalen)); + + uint8_t first, second; + ComputeCrc14443(CRC_14443_B, data, *datalen-2, &first, &second); + if(data[*datalen-2] == first && data[*datalen-1] == second) { + if (verbose) PrintAndLog("CRC OK"); + *crc = true; + } else { + if (verbose) PrintAndLog("CRC failed"); + *crc = false; + } + return 1; } int CmdHF14BCmdRaw (const char *Cmd) { bool reply = true; bool crc = false; - uint8_t power_trace = 0; + bool power = false; char buf[5]=""; uint8_t data[100] = {0x00}; uint8_t datalen = 0; @@ -290,7 +162,7 @@ int CmdHF14BCmdRaw (const char *Cmd) { break; case 'p': case 'P': - power_trace |= 1; + power = true; break; default: PrintAndLog("Invalid option"); @@ -322,55 +194,41 @@ int CmdHF14BCmdRaw (const char *Cmd) { return 0; } - return HF14BCmdRaw(reply, &crc, power_trace, data, &datalen, true); + return HF14BCmdRaw(reply, &crc, power, data, &datalen, true); } static void print_atqb_resp(uint8_t *data){ - PrintAndLog (" UID: %s", sprint_hex(data+1,4)); - PrintAndLog (" App Data: %s", sprint_hex(data+5,4)); - PrintAndLog (" Protocol: %s", sprint_hex(data+9,3)); - uint8_t BitRate = data[9]; - if (!BitRate) - PrintAndLog (" Bit Rate: 106 kbit/s only PICC <-> PCD"); - if (BitRate & 0x10) - PrintAndLog (" Bit Rate: 212 kbit/s PICC -> PCD supported"); - if (BitRate & 0x20) - PrintAndLog (" Bit Rate: 424 kbit/s PICC -> PCD supported"); - if (BitRate & 0x40) - PrintAndLog (" Bit Rate: 847 kbit/s PICC -> PCD supported"); - if (BitRate & 0x01) - PrintAndLog (" Bit Rate: 212 kbit/s PICC <- PCD supported"); - if (BitRate & 0x02) - PrintAndLog (" Bit Rate: 424 kbit/s PICC <- PCD supported"); - if (BitRate & 0x04) - PrintAndLog (" Bit Rate: 847 kbit/s PICC <- PCD supported"); - if (BitRate & 0x80) - PrintAndLog (" Same bit rate <-> required"); + PrintAndLog (" UID: %s", sprint_hex(data+1,4)); + PrintAndLog (" App Data: %s", sprint_hex(data+5,4)); + PrintAndLog (" Protocol: %s", sprint_hex(data+9,3)); + uint8_t BitRate = data[9]; + if (!BitRate) PrintAndLog (" Bit Rate: 106 kbit/s only PICC <-> PCD"); + if (BitRate & 0x10) PrintAndLog (" Bit Rate: 212 kbit/s PICC -> PCD supported"); + if (BitRate & 0x20) PrintAndLog (" Bit Rate: 424 kbit/s PICC -> PCD supported"); + if (BitRate & 0x40) PrintAndLog (" Bit Rate: 847 kbit/s PICC -> PCD supported"); + if (BitRate & 0x01) PrintAndLog (" Bit Rate: 212 kbit/s PICC <- PCD supported"); + if (BitRate & 0x02) PrintAndLog (" Bit Rate: 424 kbit/s PICC <- PCD supported"); + if (BitRate & 0x04) PrintAndLog (" Bit Rate: 847 kbit/s PICC <- PCD supported"); + if (BitRate & 0x80) PrintAndLog (" Same bit rate <-> required"); - uint16_t maxFrame = data[10]>>4; - if (maxFrame < 5) - maxFrame = 8*maxFrame + 16; - else if (maxFrame == 5) - maxFrame = 64; - else if (maxFrame == 6) - maxFrame = 96; - else if (maxFrame == 7) - maxFrame = 128; - else if (maxFrame == 8) - maxFrame = 256; - else - maxFrame = 257; + uint16_t maxFrame = data[10]>>4; + if (maxFrame < 5) maxFrame = 8 * maxFrame + 16; + else if (maxFrame == 5) maxFrame = 64; + else if (maxFrame == 6) maxFrame = 96; + else if (maxFrame == 7) maxFrame = 128; + else if (maxFrame == 8) maxFrame = 256; + else maxFrame = 257; - PrintAndLog ("Max Frame Size: %d%s",maxFrame, (maxFrame == 257) ? "+ RFU" : ""); + PrintAndLog ("Max Frame Size: %d%s", maxFrame, (maxFrame == 257) ? "+ RFU" : ""); - uint8_t protocolT = data[10] & 0xF; - PrintAndLog (" Protocol Type: Protocol is %scompliant with ISO/IEC 14443-4",(protocolT) ? "" : "not " ); - PrintAndLog ("Frame Wait Int: %d", data[11]>>4); - PrintAndLog (" App Data Code: Application is %s",(data[11]&4) ? "Standard" : "Proprietary"); - PrintAndLog (" Frame Options: NAD is %ssupported",(data[11]&2) ? "" : "not "); - PrintAndLog (" Frame Options: CID is %ssupported",(data[11]&1) ? "" : "not "); - - return; + uint8_t protocolT = data[10] & 0xF; + PrintAndLog (" Protocol Type: Protocol is %scompliant with ISO/IEC 14443-4",(protocolT) ? "" : "not " ); + PrintAndLog ("Frame Wait Int: %d", data[11]>>4); + PrintAndLog (" App Data Code: Application is %s",(data[11]&4) ? "Standard" : "Proprietary"); + PrintAndLog (" Frame Options: NAD is %ssupported",(data[11]&2) ? "" : "not "); + PrintAndLog (" Frame Options: CID is %ssupported",(data[11]&1) ? "" : "not "); + + return; } char *get_ST_Chip_Model(uint8_t data){ @@ -393,79 +251,149 @@ char *get_ST_Chip_Model(uint8_t data){ static void print_st_info(uint8_t *data){ //uid = first 8 bytes in data - PrintAndLog(" UID: %s", sprint_hex(data,8)); - PrintAndLog(" MFG: %02X, %s", data[1], getTagInfo(data[1])); - PrintAndLog("Chip: %02X, %s", data[2]>>2, get_ST_Chip_Model(data[2]>>2)); + PrintAndLog(" UID: %s", sprint_hex(SwapEndian64(data,8,8),8)); + PrintAndLog(" MFG: %02X, %s", data[6], getTagInfo(data[6])); + PrintAndLog("Chip: %02X, %s", data[5]>>2, get_ST_Chip_Model(data[5]>>2)); return; } -int HF14BStdRead(uint8_t *data, uint8_t *datalen){ - bool crc = true; - *datalen = 3; - //std read cmd - data[0] = 0x05; - data[1] = 0x00; - data[2] = 0x08; +int HF14BStdReader(uint8_t *data, uint8_t *datalen){ - if (HF14BCmdRaw(true, &crc, 0, data, datalen, false)==0) return 0; + //05 00 00 = find one tag in field + //1d xx xx xx xx 20 00 08 01 00 = attrib xx=crc + //a3 = ? (resp 03 e2 c2) + //02 = ? (resp 02 6a d3) + // 022b (resp 02 67 00 [29 5b]) + // 0200a40400 (resp 02 67 00 [29 5b]) + // 0200a4040c07a0000002480300 (resp 02 67 00 [29 5b]) + // 0200a4040c07a0000002480200 (resp 02 67 00 [29 5b]) + // 0200a4040006a0000000010100 (resp 02 6a 82 [4b 4c]) + // 0200a4040c09d27600002545500200 (resp 02 67 00 [29 5b]) + // 0200a404000cd2760001354b414e4d30310000 (resp 02 6a 82 [4b 4c]) + // 0200a404000ca000000063504b43532d313500 (resp 02 6a 82 [4b 4c]) + // 0200a4040010a000000018300301000000000000000000 (resp 02 6a 82 [4b 4c]) + //03 = ? (resp 03 [e3 c2]) + //c2 = ? (resp c2 [66 15]) + //b2 = ? (resp a3 [e9 67]) + bool crc = true; + *datalen = 3; + //std read cmd + data[0] = 0x05; + data[1] = 0x00; + data[2] = 0x00; + + if (HF14BCmdRaw(true, &crc, false, data, datalen, false)==0) return 0; if (data[0] != 0x50 || *datalen != 14 || !crc) return 0; - PrintAndLog ("\n14443-3b tag found:"); - print_atqb_resp(data); + PrintAndLog ("\n14443-3b tag found:"); + print_atqb_resp(data); - return 1; -} + return 1; + } -int HF14B_ST_Read(uint8_t *data, uint8_t *datalen){ - bool crc = true; - *datalen = 2; +int HF14B_ST_Reader(uint8_t *data, uint8_t *datalen){ + bool crc = true; + *datalen = 2; //wake cmd - data[0] = 0x06; - data[1] = 0x00; + data[0] = 0x06; + data[1] = 0x00; //leave power on // verbose on for now for testing - turn off when functional - if (HF14BCmdRaw(true, &crc, 1, data, datalen, true)==0) return rawClose(); + if (HF14BCmdRaw(true, &crc, true, data, datalen, false)==0) return rawClose(); if (*datalen != 3 || !crc) return rawClose(); - uint8_t chipID = data[0]; + uint8_t chipID = data[0]; // select - data[0] = 0x0E; - data[1] = chipID; - *datalen = 2; + data[0] = 0x0E; + data[1] = chipID; + *datalen = 2; //leave power on // verbose on for now for testing - turn off when functional - if (HF14BCmdRaw(true, &crc, 1, data, datalen, true)==0) return rawClose(); + if (HF14BCmdRaw(true, &crc, true, data, datalen, false)==0) return rawClose(); if (*datalen != 3 || !crc || data[0] != chipID) return rawClose(); // get uid - data[0] = 0x0B; - *datalen = 1; + data[0] = 0x0B; + *datalen = 1; //power off // verbose on for now for testing - turn off when functional - if (HF14BCmdRaw(true, &crc, 1, data, datalen, true)==0) return 0; + if (HF14BCmdRaw(true, &crc, true, data, datalen, false)==0) return 0; rawClose(); if (*datalen != 10 || !crc) return 0; PrintAndLog("\n14443-3b ST tag found:"); print_st_info(data); - return 1; + return 1; +} + +// test for other 14b type tags (mimic another reader - don't have tags to identify) +int HF14B_Other_Reader(uint8_t *data, uint8_t *datalen){ + bool crc = true; + *datalen = 4; + //std read cmd + data[0] = 0x00; + data[1] = 0x0b; + data[2] = 0x3f; + data[3] = 0x80; + + if (HF14BCmdRaw(true, &crc, false, data, datalen, false)!=0) { + if (*datalen > 2 || !crc) { + PrintAndLog ("\n14443-3b tag found:"); + PrintAndLog ("Unknown tag type answered to a 0x000b3f80 command ans:"); + PrintAndLog ("%s",sprint_hex(data,*datalen)); + return 1; + } + } + + crc = false; + *datalen = 1; + data[0] = 0x0a; + + if (HF14BCmdRaw(true, &crc, false, data, datalen, false)!=0) { + if (*datalen > 0) { + PrintAndLog ("\n14443-3b tag found:"); + PrintAndLog ("Unknown tag type answered to a 0x0A command ans:"); + PrintAndLog ("%s",sprint_hex(data,*datalen)); + return 1; + } + } + + crc = false; + *datalen = 1; + data[0] = 0x0c; + + if (HF14BCmdRaw(true, &crc, false, data, datalen, false)!=0) { + if (*datalen > 0) { + PrintAndLog ("\n14443-3b tag found:"); + PrintAndLog ("Unknown tag type answered to a 0x0C command ans:"); + PrintAndLog ("%s",sprint_hex(data,*datalen)); + return 1; + } + } + + return 0; + } int HF14BReader(bool verbose){ - uint8_t data[100]; - uint8_t datalen = 5; - - // try std 14b (atqb) - if (HF14BStdRead(data, &datalen)) return 1; + uint8_t data[100]; + uint8_t datalen = 5; - // try st 14b - if (HF14B_ST_Read(data, &datalen)) return 1; + // try std 14b (atqb) + if (HF14BStdReader(data, &datalen)) return 1; + + // try st 14b + if (HF14B_ST_Reader(data, &datalen)) return 1; + + // try unknown 14b read commands (to be identified later) + // could be read of calypso, CEPAS, moneo, or pico pass. + if (HF14B_Other_Reader(data, &datalen)) return 1; if (verbose) PrintAndLog("no 14443B tag found"); return 0; @@ -475,19 +403,6 @@ int CmdHF14BReader(const char *Cmd){ return HF14BReader(true); } -int CmdHFRawSamples(const char *Cmd){ - UsbCommand resp; - UsbCommand c = {CMD_ACQUIRE_RAW_ADC_SAMPLES_ISO_14443, {strtol(Cmd,NULL,0), 0, 0}}; - SendCommand(&c); - - if (!WaitForResponseTimeout(CMD_ACK,&resp,1000)) { - PrintAndLog("timeout while waiting for reply."); - return 0; - } - getSamples("39999", true); - return 1; -} - int CmdHF14BWrite( const char *Cmd){ /* * For SRIX4K blocks 00 - 7F @@ -557,29 +472,27 @@ int CmdHF14BWrite( const char *Cmd){ static command_t CommandTable[] = { - {"help", CmdHelp, 1, "This help"}, - {"demod", CmdHF14BDemod, 1, "Demodulate ISO14443 Type B from tag"}, - {"getsamples", CmdHFRawSamples,0, "[atqb=0 or ST=1] Send wake cmd and Get raw HF samples to GraphBuffer"}, - {"list", CmdHF14BList, 0, "[Deprecated] List ISO 14443b history"}, - {"reader", CmdHF14BReader, 0, "Find 14b tag (HF ISO 14443b)"}, - {"sim", CmdHF14Sim, 0, "Fake ISO 14443 tag"}, - {"simlisten", CmdHFSimlisten, 0, "Get HF samples as fake tag"}, - {"snoop", CmdHF14BSnoop, 0, "Eavesdrop ISO 14443"}, - {"sri512read", CmdSri512Read, 0, "Read contents of a SRI512 tag"}, - {"srix4kread", CmdSrix4kRead, 0, "Read contents of a SRIX4K tag"}, - {"raw", CmdHF14BCmdRaw, 0, "Send raw hex data to tag"}, - {"write", CmdHF14BWrite, 0, "Write data to a SRI512 | SRIX4K tag"}, - {NULL, NULL, 0, NULL} + {"help", CmdHelp, 1, "This help"}, + {"list", CmdHF14BList, 0, "[Deprecated] List ISO 14443b history"}, + {"reader", CmdHF14BReader, 0, "Find 14b tag (HF ISO 14443b)"}, + {"sim", CmdHF14BSim, 0, "Fake ISO 14443B tag"}, + + {"snoop", CmdHF14BSnoop, 0, "Eavesdrop ISO 14443B"}, + {"sri512read", CmdSri512Read, 0, "Read contents of a SRI512 tag"}, + {"srix4kread", CmdSrix4kRead, 0, "Read contents of a SRIX4K tag"}, + {"raw", CmdHF14BCmdRaw, 0, "Send raw hex data to tag"}, + {"write", CmdHF14BWrite, 0, "Write data to a SRI512 | SRIX4K tag"}, + {NULL, NULL, 0, NULL} }; int CmdHF14B(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; } diff --git a/client/cmdhf14b.h b/client/cmdhf14b.h index 66fd430f2..ef79317a5 100644 --- a/client/cmdhf14b.h +++ b/client/cmdhf14b.h @@ -12,16 +12,13 @@ #define CMDHF14B_H__ int CmdHF14B(const char *Cmd); - -int CmdHF14BDemod(const char *Cmd); int CmdHF14BList(const char *Cmd); int CmdHF14BReader(const char *Cmd); -int HF14BReader(bool verbose); -int CmdHF14Sim(const char *Cmd); -int CmdHFSimlisten(const char *Cmd); +int CmdHF14BSim(const char *Cmd); int CmdHF14BSnoop(const char *Cmd); int CmdSri512Read(const char *Cmd); int CmdSrix4kRead(const char *Cmd); int CmdHF14BWrite( const char *cmd); +int HF14BReader(bool verbose); #endif diff --git a/client/cmdhfmfu.c b/client/cmdhfmfu.c index 8b5dca829..e10c65195 100644 --- a/client/cmdhfmfu.c +++ b/client/cmdhfmfu.c @@ -57,13 +57,13 @@ uint8_t default_pwd_pack[KEYS_PWD_COUNT][4] = { {0x32,0x0C,0x16,0x17}, // PACK 0x80,0x80 -- AMiiboo (sniffed) }; -#define MAX_UL_TYPES 17 -uint16_t UL_TYPES_ARRAY[MAX_UL_TYPES] = {UNKNOWN, UL, UL_C, UL_EV1_48, UL_EV1_128, NTAG, NTAG_203, - NTAG_210, NTAG_212, NTAG_213, NTAG_215, NTAG_216, MY_D, MY_D_NFC, MY_D_MOVE, MY_D_MOVE_NFC, MY_D_MOVE_LEAN}; +#define MAX_UL_TYPES 18 +uint32_t UL_TYPES_ARRAY[MAX_UL_TYPES] = {UNKNOWN, UL, UL_C, UL_EV1_48, UL_EV1_128, NTAG, NTAG_203, + NTAG_210, NTAG_212, NTAG_213, NTAG_215, NTAG_216, MY_D, MY_D_NFC, MY_D_MOVE, MY_D_MOVE_NFC, MY_D_MOVE_LEAN, FUDAN_UL}; uint8_t UL_MEMORY_ARRAY[MAX_UL_TYPES] = {MAX_UL_BLOCKS, MAX_UL_BLOCKS, MAX_ULC_BLOCKS, MAX_ULEV1a_BLOCKS, MAX_ULEV1b_BLOCKS, MAX_NTAG_203, MAX_NTAG_203, MAX_NTAG_210, MAX_NTAG_212, MAX_NTAG_213, - MAX_NTAG_215, MAX_NTAG_216, MAX_UL_BLOCKS, MAX_MY_D_NFC, MAX_MY_D_MOVE, MAX_MY_D_MOVE, MAX_MY_D_MOVE_LEAN}; + MAX_NTAG_215, MAX_NTAG_216, MAX_UL_BLOCKS, MAX_MY_D_NFC, MAX_MY_D_MOVE, MAX_MY_D_MOVE, MAX_MY_D_MOVE_LEAN, MAX_UL_BLOCKS}; static int CmdHelp(const char *Cmd); @@ -259,6 +259,25 @@ static int ulev1_readSignature( uint8_t *response, uint16_t responseLength ){ return len; } +//make sure field is off before calling this function +static int ul_fudan_check( void ){ + iso14a_card_select_t card; + if ( !ul_select(&card) ) + return UL_ERROR; + + UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_RAW | ISO14A_NO_DISCONNECT, 4, 0}}; + + uint8_t cmd[4] = {0x30,0x00,0x02,0xa7}; //wrong crc on purpose should be 0xa8 + memcpy(c.d.asBytes, cmd, 4); + clearCommandBuffer(); + SendCommand(&c); + UsbCommand resp; + if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500)) return UL_ERROR; + if (resp.arg[0] != 1) return UL_ERROR; + + return (!resp.d.asBytes[0]) ? FUDAN_UL : UL; //if response == 0x00 then Fudan, else Genuine NXP +} + static int ul_print_default( uint8_t *data){ uint8_t uid[7]; @@ -365,15 +384,17 @@ int ul_print_type(uint32_t tagtype, uint8_t spaces){ else if ( tagtype & NTAG_I2C_2K ) PrintAndLog("%sTYPE : NTAG I%sC 1904bytes (NT3H1201FHK)", spacer, "\xFD"); else if ( tagtype & MY_D ) - PrintAndLog("%sTYPE : INFINEON my-d\0153 (SLE 66RxxS)", spacer); + PrintAndLog("%sTYPE : INFINEON my-d\x99 (SLE 66RxxS)", spacer); else if ( tagtype & MY_D_NFC ) - PrintAndLog("%sTYPE : INFINEON my-d\0153 NFC (SLE 66RxxP)", spacer); + PrintAndLog("%sTYPE : INFINEON my-d\x99 NFC (SLE 66RxxP)", spacer); else if ( tagtype & MY_D_MOVE ) - PrintAndLog("%sTYPE : INFINEON my-d\0153 move (SLE 66R01P)", spacer); + PrintAndLog("%sTYPE : INFINEON my-d\x99 move (SLE 66R01P)", spacer); else if ( tagtype & MY_D_MOVE_NFC ) - PrintAndLog("%sTYPE : INFINEON my-d\0153 move NFC (SLE 66R01P)", spacer); + PrintAndLog("%sTYPE : INFINEON my-d\x99 move NFC (SLE 66R01P)", spacer); else if ( tagtype & MY_D_MOVE_LEAN ) PrintAndLog("%sTYPE : INFINEON my-d\x99 move lean (SLE 66R01L)", spacer); + else if ( tagtype & FUDAN_UL ) + PrintAndLog("%sTYPE : FUDAN Ultralight Compatible (or other compatible) %s", spacer, (tagtype & MAGIC) ? "" : "" ); else PrintAndLog("%sTYPE : Unknown %06x", spacer, tagtype); return 0; @@ -607,6 +628,10 @@ uint32_t GetHF14AMfU_Type(void){ ul_switch_off_field(); } } + if (tagtype & UL) { + tagtype = ul_fudan_check(); + ul_switch_off_field(); + } } else { ul_switch_off_field(); // Infinition MY-D tests Exam high nibble diff --git a/client/cmdhfmfu.h b/client/cmdhfmfu.h index 132e4f908..6c9e3ea1b 100644 --- a/client/cmdhfmfu.h +++ b/client/cmdhfmfu.h @@ -45,7 +45,8 @@ typedef enum TAGTYPE_UL { MY_D_MOVE_LEAN= 0x008000, NTAG_I2C_1K = 0x010000, NTAG_I2C_2K = 0x020000, - MAGIC = 0x040000, + FUDAN_UL = 0x040000, + MAGIC = 0x080000, UL_MAGIC = UL | MAGIC, UL_C_MAGIC = UL_C | MAGIC, UL_ERROR = 0xFFFFFF, diff --git a/client/lualibs/commands.lua b/client/lualibs/commands.lua index bedb83678..fd186fb7c 100644 --- a/client/lualibs/commands.lua +++ b/client/lualibs/commands.lua @@ -59,7 +59,6 @@ local _commands = { --// For the 13.56 MHz tags CMD_ACQUIRE_RAW_ADC_SAMPLES_ISO_15693 = 0x0300, - CMD_ACQUIRE_RAW_ADC_SAMPLES_ISO_14443 = 0x0301, CMD_READ_SRI512_TAG = 0x0303, CMD_READ_SRIX4K_TAG = 0x0304, CMD_READER_ISO_15693 = 0x0310, @@ -76,9 +75,8 @@ local _commands = { CMD_SIMULATE_HITAG = 0x0371, CMD_READER_HITAG = 0x0372, - CMD_SIMULATE_TAG_HF_LISTEN = 0x0380, - CMD_SIMULATE_TAG_ISO_14443 = 0x0381, - CMD_SNOOP_ISO_14443 = 0x0382, + CMD_SIMULATE_TAG_ISO_14443B = 0x0381, + CMD_SNOOP_ISO_14443B = 0x0382, CMD_SNOOP_ISO_14443a = 0x0383, CMD_SIMULATE_TAG_ISO_14443a = 0x0384, CMD_READER_ISO_14443a = 0x0385, diff --git a/cp2tau b/cp2tau deleted file mode 100644 index 8b6ee4b43..000000000 --- a/cp2tau +++ /dev/null @@ -1,4 +0,0 @@ -cp armsrc/obj/*.elf /z -cp armsrc/obj/*.s19 /z -cp bootrom/obj/*.elf /z -cp bootrom/obj/*.s19 /z diff --git a/fpga/fpga_hf.bit b/fpga/fpga_hf.bit index 53078a782422c09596f006d95c85ae45da20072d..49bec2242cb7720524289f3eba1397bbc2a22867 100644 GIT binary patch literal 42175 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 XtOg7VK>9$d4j3624kD(dpoRbdAA(}7 diff --git a/fpga/fpga_hf.v b/fpga/fpga_hf.v index a2100df65..8a465e75c 100644 --- a/fpga/fpga_hf.v +++ b/fpga/fpga_hf.v @@ -73,9 +73,6 @@ wire hi_read_rx_xcorr_848 = conf_word[0]; // and whether to drive the coil (reader) or just short it (snooper) wire hi_read_rx_xcorr_snoop = conf_word[1]; -// Divide the expected subcarrier frequency for hi_read_rx_xcorr by 4 -wire hi_read_rx_xcorr_quarter = conf_word[2]; - // For the high-frequency simulated tag: what kind of modulation to use. wire [2:0] hi_simulate_mod_type = conf_word[2:0]; @@ -102,7 +99,7 @@ hi_read_rx_xcorr hrxc( hrxc_ssp_frame, hrxc_ssp_din, ssp_dout, hrxc_ssp_clk, cross_hi, cross_lo, hrxc_dbg, - hi_read_rx_xcorr_848, hi_read_rx_xcorr_snoop, hi_read_rx_xcorr_quarter + hi_read_rx_xcorr_848, hi_read_rx_xcorr_snoop ); hi_simulate hs( diff --git a/fpga/hi_read_rx_xcorr.v b/fpga/hi_read_rx_xcorr.v index 06142637b..bb1515548 100644 --- a/fpga/hi_read_rx_xcorr.v +++ b/fpga/hi_read_rx_xcorr.v @@ -10,7 +10,7 @@ module hi_read_rx_xcorr( ssp_frame, ssp_din, ssp_dout, ssp_clk, cross_hi, cross_lo, dbg, - xcorr_is_848, snoop, xcorr_quarter_freq + xcorr_is_848, snoop ); input pck0, ck_1356meg, ck_1356megb; output pwr_lo, pwr_hi, pwr_oe1, pwr_oe2, pwr_oe3, pwr_oe4; @@ -20,58 +20,34 @@ module hi_read_rx_xcorr( output ssp_frame, ssp_din, ssp_clk; input cross_hi, cross_lo; output dbg; - input xcorr_is_848, snoop, xcorr_quarter_freq; + input xcorr_is_848, snoop; // Carrier is steady on through this, unless we're snooping. assign pwr_hi = ck_1356megb & (~snoop); assign pwr_oe1 = 1'b0; -assign pwr_oe2 = 1'b0; assign pwr_oe3 = 1'b0; assign pwr_oe4 = 1'b0; -reg ssp_clk; -reg ssp_frame; +(* clock_signal = "yes" *) reg fc_div_2; +always @(negedge ck_1356megb) + fc_div_2 <= fc_div_2 + 1; -reg fc_div_2; -always @(posedge ck_1356meg) - fc_div_2 = ~fc_div_2; - -reg fc_div_4; -always @(posedge fc_div_2) - fc_div_4 = ~fc_div_4; - -reg fc_div_8; -always @(posedge fc_div_4) - fc_div_8 = ~fc_div_8; - -reg adc_clk; - -always @(xcorr_is_848 or xcorr_quarter_freq or ck_1356meg) - if(~xcorr_quarter_freq) - begin +(* clock_signal = "yes" *) reg adc_clk; +always @(xcorr_is_848, ck_1356megb, fc_div_2) if(xcorr_is_848) // The subcarrier frequency is fc/16; we will sample at fc, so that // means the subcarrier is 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 1 1 ... - adc_clk <= ck_1356meg; + adc_clk <= ck_1356megb; else // The subcarrier frequency is fc/32; we will sample at fc/2, and // the subcarrier will look identical. adc_clk <= fc_div_2; - end - else - begin - if(xcorr_is_848) - // The subcarrier frequency is fc/64 - adc_clk <= fc_div_4; - else - // The subcarrier frequency is fc/128 - adc_clk <= fc_div_8; - end + // When we're a reader, we just need to do the BPSK demod; but when we're an // eavesdropper, we also need to pick out the commands sent by the reader, // using AM. Do this the same way that we do it for the simulated tag. -reg after_hysteresis, after_hysteresis_prev; +reg after_hysteresis, after_hysteresis_prev, after_hysteresis_prev_prev; reg [11:0] has_been_low_for; always @(negedge adc_clk) begin @@ -97,7 +73,6 @@ end // Let us report a correlation every 4 subcarrier cycles, or 4*16 samples, // so we need a 6-bit counter. reg [5:0] corr_i_cnt; -reg [5:0] corr_q_cnt; // And a couple of registers in which to accumulate the correlations. // we would add at most 32 times adc_d, the result can be held in 13 bits. // Need one additional bit because it can be negative as well @@ -105,32 +80,38 @@ reg signed [13:0] corr_i_accum; reg signed [13:0] corr_q_accum; reg signed [7:0] corr_i_out; reg signed [7:0] corr_q_out; +// clock and frame signal for communication to ARM +reg ssp_clk; +reg ssp_frame; + + // ADC data appears on the rising edge, so sample it on the falling edge always @(negedge adc_clk) begin + corr_i_cnt <= corr_i_cnt + 1; + // These are the correlators: we correlate against in-phase and quadrature // versions of our reference signal, and keep the (signed) result to // send out later over the SSP. - if(corr_i_cnt == 7'd63) + if(corr_i_cnt == 7'd0) begin if(snoop) begin - // highest 7 significant bits of tag signal (signed), 1 bit reader signal: - corr_i_out <= {corr_i_accum[13:7], after_hysteresis_prev}; - corr_q_out <= {corr_q_accum[13:7], after_hysteresis}; + // 7 most significant bits of tag signal (signed), 1 bit reader signal: + corr_i_out <= {corr_i_accum[13:7], after_hysteresis_prev_prev}; + corr_q_out <= {corr_q_accum[13:7], after_hysteresis_prev}; + after_hysteresis_prev_prev <= after_hysteresis; end else begin - // highest 8 significant bits of tag signal + // 8 most significant bits of tag signal corr_i_out <= corr_i_accum[13:6]; corr_q_out <= corr_q_accum[13:6]; end corr_i_accum <= adc_d; corr_q_accum <= adc_d; - corr_q_cnt <= 4; - corr_i_cnt <= 0; end else begin @@ -139,13 +120,11 @@ begin else corr_i_accum <= corr_i_accum + adc_d; - if(corr_q_cnt[3]) - corr_q_accum <= corr_q_accum - adc_d; - else + if(corr_i_cnt[3] == corr_i_cnt[2]) // phase shifted by pi/2 corr_q_accum <= corr_q_accum + adc_d; + else + corr_q_accum <= corr_q_accum - adc_d; - corr_i_cnt <= corr_i_cnt + 1; - corr_q_cnt <= corr_q_cnt + 1; end // The logic in hi_simulate.v reports 4 samples per bit. We report two @@ -172,7 +151,7 @@ begin end // set ssp_frame signal for corr_i_cnt = 0..3 and corr_i_cnt = 32..35 - // (two frames with 8 Bits each) + // (send two frames with 8 Bits each) if(corr_i_cnt[5:2] == 4'b0000 || corr_i_cnt[5:2] == 4'b1000) ssp_frame = 1'b1; else @@ -186,5 +165,6 @@ assign dbg = corr_i_cnt[3]; // Unused. assign pwr_lo = 1'b0; +assign pwr_oe2 = 1'b0; endmodule diff --git a/include/mifare.h b/include/mifare.h index 89ff758e2..ad86886db 100644 --- a/include/mifare.h +++ b/include/mifare.h @@ -11,7 +11,7 @@ #ifndef _MIFARE_H_ #define _MIFARE_H_ -#include "../include/common.h" +#include "common.h" //----------------------------------------------------------------------------- // ISO 14443A diff --git a/include/usb_cmd.h b/include/usb_cmd.h index 345793eca..2080f2de0 100644 --- a/include/usb_cmd.h +++ b/include/usb_cmd.h @@ -100,7 +100,6 @@ typedef struct{ // For the 13.56 MHz tags #define CMD_ACQUIRE_RAW_ADC_SAMPLES_ISO_15693 0x0300 -#define CMD_ACQUIRE_RAW_ADC_SAMPLES_ISO_14443 0x0301 #define CMD_READ_SRI512_TAG 0x0303 #define CMD_READ_SRIX4K_TAG 0x0304 #define CMD_ISO_14443B_COMMAND 0x0305 @@ -118,9 +117,8 @@ typedef struct{ #define CMD_SIMULATE_HITAG 0x0371 #define CMD_READER_HITAG 0x0372 -#define CMD_SIMULATE_TAG_HF_LISTEN 0x0380 -#define CMD_SIMULATE_TAG_ISO_14443 0x0381 -#define CMD_SNOOP_ISO_14443 0x0382 +#define CMD_SIMULATE_TAG_ISO_14443B 0x0381 +#define CMD_SNOOP_ISO_14443B 0x0382 #define CMD_SNOOP_ISO_14443a 0x0383 #define CMD_SIMULATE_TAG_ISO_14443a 0x0384 #define CMD_READER_ISO_14443a 0x0385 @@ -128,7 +126,6 @@ typedef struct{ #define CMD_READER_LEGIC_RF 0x0388 #define CMD_WRITER_LEGIC_RF 0x0389 #define CMD_EPA_PACE_COLLECT_NONCE 0x038A -//#define CMD_EPA_ 0x038B #define CMD_SNOOP_ICLASS 0x0392 #define CMD_SIMULATE_TAG_ICLASS 0x0393