From: dekoninggans@gmail.com Date: Wed, 18 May 2011 12:33:32 +0000 (+0000) Subject: Added iClass eavesdrop support for Proxmark3 X-Git-Tag: v1.0.0~263 X-Git-Url: http://cvs.zerfleddert.de/cgi-bin/gitweb.cgi/proxmark3-svn/commitdiff_plain/cee5a30d53c7aff4c4830eae53eaf58414ecf806 Added iClass eavesdrop support for Proxmark3 --- diff --git a/armsrc/Makefile b/armsrc/Makefile index 8d9e2b4a..087ddcf8 100644 --- a/armsrc/Makefile +++ b/armsrc/Makefile @@ -36,6 +36,7 @@ ARMSRC = fpgaloader.c \ $(SRC_ISO14443a) \ $(SRC_ISO14443b) \ legic_prng.c \ + iclass.c \ crc.c # stdint.h provided locally until GCC 4.5 becomes C99 compliant diff --git a/armsrc/appmain.c b/armsrc/appmain.c index aa70e677..47fa2f99 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -706,6 +706,13 @@ void UsbPacketReceived(uint8_t *packet, int len) break; #endif +#ifdef WITH_ISO14443a + // Makes use of ISO14443a FPGA Firmware + case CMD_SNOOP_ICLASS: + SnoopIClass(); + break; +#endif + case CMD_SIMULATE_TAG_HF_LISTEN: SimulateTagHfListen(); break; diff --git a/armsrc/apps.h b/armsrc/apps.h index 7fdde6f1..2d15a907 100644 --- a/armsrc/apps.h +++ b/armsrc/apps.h @@ -1,6 +1,6 @@ //----------------------------------------------------------------------------- // Jonathan Westhues, Aug 2005 -// Gerhard de Koning Gans, April 2008 +// Gerhard de Koning Gans, April 2008, May 2011 // // This code is licensed to you under the terms of the GNU GPL, version 2 or, // at your option, any later version. See the LICENSE.txt file for the text of @@ -116,6 +116,9 @@ void BruteforceIso15693Afi(uint32_t speed); // find an AFI of a tag - atrox void DirectTag15693Command(uint32_t datalen,uint32_t speed, uint32_t recv, uint8_t data[]); // send arbitrary commands from CLI - atrox void SetDebugIso15693(uint32_t flag); +/// iclass.h +void RAMFUNC SnoopIClass(void); + /// util.h #endif diff --git a/armsrc/iclass.c b/armsrc/iclass.c new file mode 100644 index 00000000..d1bb8df3 --- /dev/null +++ b/armsrc/iclass.c @@ -0,0 +1,923 @@ +//----------------------------------------------------------------------------- +// Gerhard de Koning Gans - May 2008 +// Hagen Fritsch - June 2010 +// Gerhard de Koning Gans - May 2011 +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// Routines to support iClass. +//----------------------------------------------------------------------------- +// Based on ISO14443a implementation. Still in experimental phase. +// Contribution made during a security research at Radboud University Nijmegen +// +// Please feel free to contribute and extend iClass support!! +//----------------------------------------------------------------------------- +// +// TODO: +// ===== +// - iClass emulation +// - reader emulation +// +// FIX: +// ==== +// We still have sometimes a demodulation error when snooping iClass communication. +// The resulting trace of a read-block-03 command may look something like this: +// +// + 22279: : 0c 03 e8 01 +// +// ...with an incorrect answer... +// +// + 85: 0: TAG ff! ff! ff! ff! ff! ff! ff! ff! bb 33 bb 00 01! 0e! 04! bb !crc +// +// We still left the error signalling bytes in the traces like 0xbb +// +// A correct trace should look like this: +// +// + 21112: : 0c 03 e8 01 +// + 85: 0: TAG ff ff ff ff ff ff ff ff ea f5 +// +//----------------------------------------------------------------------------- + +#include "proxmark3.h" +#include "apps.h" +#include "util.h" +#include "string.h" + +#include "iclass.h" + +static uint8_t *trace = (uint8_t *) BigBuf; +static int traceLen = 0; +static int rsamples = 0; + +// CARD TO READER +// Sequence D: 11110000 modulation with subcarrier during first half +// Sequence E: 00001111 modulation with subcarrier during second half +// Sequence F: 00000000 no modulation with subcarrier +// READER TO CARD +// Sequence X: 00001100 drop after half a period +// Sequence Y: 00000000 no drop +// Sequence Z: 11000000 drop at start +#define SEC_D 0xf0 +#define SEC_E 0x0f +#define SEC_F 0x00 +#define SEC_X 0x0c +#define SEC_Y 0x00 +#define SEC_Z 0xc0 + +static const uint8_t OddByteParity[256] = { + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1 +}; + +//static const uint8_t MajorityNibble[16] = { 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1 }; +//static const uint8_t MajorityNibble[16] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; + +// BIG CHANGE - UNDERSTAND THIS BEFORE WE COMMIT +#define RECV_CMD_OFFSET 3032 +#define RECV_RES_OFFSET 3096 +#define DMA_BUFFER_OFFSET 3160 +#define DMA_BUFFER_SIZE 4096 +#define TRACE_LENGTH 3000 + + +//----------------------------------------------------------------------------- +// The software UART that receives commands from the reader, and its state +// variables. +//----------------------------------------------------------------------------- +static struct { + enum { + STATE_UNSYNCD, + STATE_START_OF_COMMUNICATION, + STATE_RECEIVING + } state; + uint16_t shiftReg; + int bitCnt; + int byteCnt; + int byteCntMax; + int posCnt; + int nOutOfCnt; + int OutOfCnt; + int syncBit; + int parityBits; + int samples; + int highCnt; + int swapper; + int counter; + int bitBuffer; + int dropPosition; + uint8_t *output; +} Uart; + +static RAMFUNC int MillerDecoding(int bit) +{ + int error = 0; + int bitright; + + if(!Uart.bitBuffer) { + Uart.bitBuffer = bit ^ 0xFF0; + return FALSE; + } + else { + Uart.bitBuffer <<= 4; + Uart.bitBuffer ^= bit; + } + + /*if(Uart.swapper) { + Uart.output[Uart.byteCnt] = Uart.bitBuffer & 0xFF; + Uart.byteCnt++; + Uart.swapper = 0; + if(Uart.byteCnt > 15) { return TRUE; } + } + else { + Uart.swapper = 1; + }*/ + + if(Uart.state != STATE_UNSYNCD) { + Uart.posCnt++; + + if((Uart.bitBuffer & Uart.syncBit) ^ Uart.syncBit) { + bit = 0x00; + } + else { + bit = 0x01; + } + if(((Uart.bitBuffer << 1) & Uart.syncBit) ^ Uart.syncBit) { + bitright = 0x00; + } + else { + bitright = 0x01; + } + if(bit != bitright) { bit = bitright; } + + + // So, now we only have to deal with *bit*, lets see... + if(Uart.posCnt == 1) { + // measurement first half bitperiod + if(!bit) { + // Drop in first half means that we are either seeing + // an SOF or an EOF. + + if(Uart.nOutOfCnt == 1) { + // End of Communication + Uart.state = STATE_UNSYNCD; + Uart.highCnt = 0; + if(Uart.byteCnt == 0) { + // Its not straightforward to show single EOFs + // So just leave it and do not return TRUE + Uart.output[Uart.byteCnt] = 0xf0; + Uart.byteCnt++; + + // Calculate the parity bit for the client... + Uart.parityBits = 1; + } + else { + return TRUE; + } + } + else if(Uart.state != STATE_START_OF_COMMUNICATION) { + // When not part of SOF or EOF, it is an error + Uart.state = STATE_UNSYNCD; + Uart.highCnt = 0; + error = 4; + } + } + } + else { + // measurement second half bitperiod + // Count the bitslot we are in... (ISO 15693) + Uart.nOutOfCnt++; + + if(!bit) { + if(Uart.dropPosition) { + if(Uart.state == STATE_START_OF_COMMUNICATION) { + error = 1; + } + else { + error = 7; + } + // It is an error if we already have seen a drop in current frame + Uart.state = STATE_UNSYNCD; + Uart.highCnt = 0; + } + else { + Uart.dropPosition = Uart.nOutOfCnt; + } + } + + Uart.posCnt = 0; + + + if(Uart.nOutOfCnt == Uart.OutOfCnt && Uart.OutOfCnt == 4) { + Uart.nOutOfCnt = 0; + + if(Uart.state == STATE_START_OF_COMMUNICATION) { + if(Uart.dropPosition == 4) { + Uart.state = STATE_RECEIVING; + Uart.OutOfCnt = 256; + } + else if(Uart.dropPosition == 3) { + Uart.state = STATE_RECEIVING; + Uart.OutOfCnt = 4; + //Uart.output[Uart.byteCnt] = 0xdd; + //Uart.byteCnt++; + } + else { + Uart.state = STATE_UNSYNCD; + Uart.highCnt = 0; + } + Uart.dropPosition = 0; + } + else { + // RECEIVING DATA + // 1 out of 4 + if(!Uart.dropPosition) { + Uart.state = STATE_UNSYNCD; + Uart.highCnt = 0; + error = 9; + } + else { + Uart.shiftReg >>= 2; + + // Swap bit order + Uart.dropPosition--; + //if(Uart.dropPosition == 1) { Uart.dropPosition = 2; } + //else if(Uart.dropPosition == 2) { Uart.dropPosition = 1; } + + Uart.shiftReg ^= ((Uart.dropPosition & 0x03) << 6); + Uart.bitCnt += 2; + Uart.dropPosition = 0; + + if(Uart.bitCnt == 8) { + Uart.output[Uart.byteCnt] = (Uart.shiftReg & 0xff); + Uart.byteCnt++; + + // Calculate the parity bit for the client... + Uart.parityBits <<= 1; + Uart.parityBits ^= OddByteParity[(Uart.shiftReg & 0xff)]; + + Uart.bitCnt = 0; + Uart.shiftReg = 0; + } + } + } + } + else if(Uart.nOutOfCnt == Uart.OutOfCnt) { + // RECEIVING DATA + // 1 out of 256 + if(!Uart.dropPosition) { + Uart.state = STATE_UNSYNCD; + Uart.highCnt = 0; + error = 3; + } + else { + Uart.dropPosition--; + Uart.output[Uart.byteCnt] = (Uart.dropPosition & 0xff); + Uart.byteCnt++; + + // Calculate the parity bit for the client... + Uart.parityBits <<= 1; + Uart.parityBits ^= OddByteParity[(Uart.dropPosition & 0xff)]; + + Uart.bitCnt = 0; + Uart.shiftReg = 0; + Uart.nOutOfCnt = 0; + Uart.dropPosition = 0; + } + } + + /*if(error) { + Uart.output[Uart.byteCnt] = 0xAA; + Uart.byteCnt++; + Uart.output[Uart.byteCnt] = error & 0xFF; + Uart.byteCnt++; + Uart.output[Uart.byteCnt] = 0xAA; + Uart.byteCnt++; + Uart.output[Uart.byteCnt] = (Uart.bitBuffer >> 8) & 0xFF; + Uart.byteCnt++; + Uart.output[Uart.byteCnt] = Uart.bitBuffer & 0xFF; + Uart.byteCnt++; + Uart.output[Uart.byteCnt] = (Uart.syncBit >> 3) & 0xFF; + Uart.byteCnt++; + Uart.output[Uart.byteCnt] = 0xAA; + Uart.byteCnt++; + return TRUE; + }*/ + } + + } + else { + bit = Uart.bitBuffer & 0xf0; + bit >>= 4; + bit ^= 0x0F; // drops become 1s ;-) + if(bit) { + // should have been high or at least (4 * 128) / fc + // according to ISO this should be at least (9 * 128 + 20) / fc + if(Uart.highCnt == 8) { + // we went low, so this could be start of communication + // it turns out to be safer to choose a less significant + // syncbit... so we check whether the neighbour also represents the drop + Uart.posCnt = 1; // apparently we are busy with our first half bit period + Uart.syncBit = bit & 8; + Uart.samples = 3; + if(!Uart.syncBit) { Uart.syncBit = bit & 4; Uart.samples = 2; } + else if(bit & 4) { Uart.syncBit = bit & 4; Uart.samples = 2; bit <<= 2; } + if(!Uart.syncBit) { Uart.syncBit = bit & 2; Uart.samples = 1; } + else if(bit & 2) { Uart.syncBit = bit & 2; Uart.samples = 1; bit <<= 1; } + if(!Uart.syncBit) { Uart.syncBit = bit & 1; Uart.samples = 0; + if(Uart.syncBit && (Uart.bitBuffer & 8)) { + Uart.syncBit = 8; + + // the first half bit period is expected in next sample + Uart.posCnt = 0; + Uart.samples = 3; + } + } + else if(bit & 1) { Uart.syncBit = bit & 1; Uart.samples = 0; } + + Uart.syncBit <<= 4; + Uart.state = STATE_START_OF_COMMUNICATION; + Uart.bitCnt = 0; + Uart.byteCnt = 0; + Uart.parityBits = 0; + Uart.nOutOfCnt = 0; + Uart.OutOfCnt = 4; // Start at 1/4, could switch to 1/256 + Uart.dropPosition = 0; + Uart.shiftReg = 0; + error = 0; + } + else { + Uart.highCnt = 0; + } + } + else { + if(Uart.highCnt < 8) { + Uart.highCnt++; + } + } + } + + return FALSE; +} + +//============================================================================= +// ISO 14443 Type A - Manchester +//============================================================================= + +static struct { + enum { + DEMOD_UNSYNCD, + DEMOD_START_OF_COMMUNICATION, + DEMOD_START_OF_COMMUNICATION2, + DEMOD_START_OF_COMMUNICATION3, + DEMOD_SOF_COMPLETE, + DEMOD_MANCHESTER_D, + DEMOD_MANCHESTER_E, + DEMOD_END_OF_COMMUNICATION, + DEMOD_END_OF_COMMUNICATION2, + DEMOD_MANCHESTER_F, + DEMOD_ERROR_WAIT + } state; + int bitCount; + int posCount; + int syncBit; + int parityBits; + uint16_t shiftReg; + int buffer; + int buffer2; + int buffer3; + int buff; + int samples; + int len; + enum { + SUB_NONE, + SUB_FIRST_HALF, + SUB_SECOND_HALF, + SUB_BOTH + } sub; + uint8_t *output; +} Demod; + +static RAMFUNC int ManchesterDecoding(int v) +{ + int bit; + int modulation; + int error = 0; + + bit = Demod.buffer; + Demod.buffer = Demod.buffer2; + Demod.buffer2 = Demod.buffer3; + Demod.buffer3 = v; + + if(Demod.buff < 3) { + Demod.buff++; + return FALSE; + } + + if(Demod.state==DEMOD_UNSYNCD) { + Demod.output[Demod.len] = 0xfa; + Demod.syncBit = 0; + //Demod.samples = 0; + Demod.posCount = 1; // This is the first half bit period, so after syncing handle the second part + /* if(bit & 0x08) { Demod.syncBit = 0x08; } + if(!Demod.syncBit) { + if(bit & 0x04) { Demod.syncBit = 0x04; } + } + else if(bit & 0x04) { Demod.syncBit = 0x04; bit <<= 4; } + if(!Demod.syncBit) { + if(bit & 0x02) { Demod.syncBit = 0x02; } + } + else if(bit & 0x02) { Demod.syncBit = 0x02; bit <<= 4; } + if(!Demod.syncBit) { + if(bit & 0x01) { Demod.syncBit = 0x01; } + + if(Demod.syncBit && (Demod.buffer & 0x08)) { + Demod.syncBit = 0x08; + + // The first half bitperiod is expected in next sample + Demod.posCount = 0; + Demod.output[Demod.len] = 0xfb; + } + } + else if(bit & 0x01) { Demod.syncBit = 0x01; } + */ + + if(bit & 0x08) { + Demod.syncBit = 0x08; + } + + if(bit & 0x04) { + if(Demod.syncBit) { + bit <<= 4; + } + Demod.syncBit = 0x04; + } + + if(bit & 0x02) { + if(Demod.syncBit) { + bit <<= 2; + } + Demod.syncBit = 0x02; + } + + if(bit & 0x01 && Demod.syncBit) { + Demod.syncBit = 0x01; + } + + if(Demod.syncBit) { + Demod.len = 0; + Demod.state = DEMOD_START_OF_COMMUNICATION; + Demod.sub = SUB_FIRST_HALF; + Demod.bitCount = 0; + Demod.shiftReg = 0; + Demod.parityBits = 0; + Demod.samples = 0; + if(Demod.posCount) { + //if(trigger) LED_A_OFF(); // Not useful in this case... + switch(Demod.syncBit) { + case 0x08: Demod.samples = 3; break; + case 0x04: Demod.samples = 2; break; + case 0x02: Demod.samples = 1; break; + case 0x01: Demod.samples = 0; break; + } + // SOF must be long burst... otherwise stay unsynced!!! + if(!(Demod.buffer & Demod.syncBit) || !(Demod.buffer2 & Demod.syncBit)) { + Demod.state = DEMOD_UNSYNCD; + } + } + else { + // SOF must be long burst... otherwise stay unsynced!!! + if(!(Demod.buffer2 & Demod.syncBit) || !(Demod.buffer3 & Demod.syncBit)) { + Demod.state = DEMOD_UNSYNCD; + error = 0x88; + } + + } + error = 0; + + } + } + else { + modulation = bit & Demod.syncBit; + modulation |= ((bit << 1) ^ ((Demod.buffer & 0x08) >> 3)) & Demod.syncBit; + //modulation = ((bit << 1) ^ ((Demod.buffer & 0x08) >> 3)) & Demod.syncBit; + + Demod.samples += 4; + + if(Demod.posCount==0) { + Demod.posCount = 1; + if(modulation) { + Demod.sub = SUB_FIRST_HALF; + } + else { + Demod.sub = SUB_NONE; + } + } + else { + Demod.posCount = 0; + /*(modulation && (Demod.sub == SUB_FIRST_HALF)) { + if(Demod.state!=DEMOD_ERROR_WAIT) { + Demod.state = DEMOD_ERROR_WAIT; + Demod.output[Demod.len] = 0xaa; + error = 0x01; + } + }*/ + //else if(modulation) { + if(modulation) { + if(Demod.sub == SUB_FIRST_HALF) { + Demod.sub = SUB_BOTH; + } + else { + Demod.sub = SUB_SECOND_HALF; + } + } + else if(Demod.sub == SUB_NONE) { + if(Demod.state == DEMOD_SOF_COMPLETE) { + Demod.output[Demod.len] = 0x0f; + Demod.len++; + Demod.parityBits <<= 1; + Demod.parityBits ^= OddByteParity[0x0f]; + Demod.state = DEMOD_UNSYNCD; +// error = 0x0f; + return TRUE; + } + else { + Demod.state = DEMOD_ERROR_WAIT; + error = 0x33; + } + /*if(Demod.state!=DEMOD_ERROR_WAIT) { + Demod.state = DEMOD_ERROR_WAIT; + Demod.output[Demod.len] = 0xaa; + error = 0x01; + }*/ + } + + switch(Demod.state) { + case DEMOD_START_OF_COMMUNICATION: + if(Demod.sub == SUB_BOTH) { + //Demod.state = DEMOD_MANCHESTER_D; + Demod.state = DEMOD_START_OF_COMMUNICATION2; + Demod.posCount = 1; + Demod.sub = SUB_NONE; + } + else { + Demod.output[Demod.len] = 0xab; + Demod.state = DEMOD_ERROR_WAIT; + error = 0xd2; + } + break; + case DEMOD_START_OF_COMMUNICATION2: + if(Demod.sub == SUB_SECOND_HALF) { + Demod.state = DEMOD_START_OF_COMMUNICATION3; + } + else { + Demod.output[Demod.len] = 0xab; + Demod.state = DEMOD_ERROR_WAIT; + error = 0xd3; + } + break; + case DEMOD_START_OF_COMMUNICATION3: + if(Demod.sub == SUB_SECOND_HALF) { +// Demod.state = DEMOD_MANCHESTER_D; + Demod.state = DEMOD_SOF_COMPLETE; + //Demod.output[Demod.len] = Demod.syncBit & 0xFF; + //Demod.len++; + } + else { + Demod.output[Demod.len] = 0xab; + Demod.state = DEMOD_ERROR_WAIT; + error = 0xd4; + } + break; + case DEMOD_SOF_COMPLETE: + case DEMOD_MANCHESTER_D: + case DEMOD_MANCHESTER_E: + // OPPOSITE FROM ISO14443 - 11110000 = 0 (1 in 14443) + // 00001111 = 1 (0 in 14443) + if(Demod.sub == SUB_SECOND_HALF) { // SUB_FIRST_HALF + Demod.bitCount++; + Demod.shiftReg = (Demod.shiftReg >> 1) ^ 0x100; + Demod.state = DEMOD_MANCHESTER_D; + } + else if(Demod.sub == SUB_FIRST_HALF) { // SUB_SECOND_HALF + Demod.bitCount++; + Demod.shiftReg >>= 1; + Demod.state = DEMOD_MANCHESTER_E; + } + else if(Demod.sub == SUB_BOTH) { + Demod.state = DEMOD_MANCHESTER_F; + } + else { + Demod.state = DEMOD_ERROR_WAIT; + error = 0x55; + } + break; + + case DEMOD_MANCHESTER_F: + // Tag response does not need to be a complete byte! + if(Demod.len > 0 || Demod.bitCount > 0) { + if(Demod.bitCount > 1) { // was > 0, do not interpret last closing bit, is part of EOF + Demod.shiftReg >>= (9 - Demod.bitCount); + Demod.output[Demod.len] = Demod.shiftReg & 0xff; + Demod.len++; + // No parity bit, so just shift a 0 + Demod.parityBits <<= 1; + } + + Demod.state = DEMOD_UNSYNCD; + return TRUE; + } + else { + Demod.output[Demod.len] = 0xad; + Demod.state = DEMOD_ERROR_WAIT; + error = 0x03; + } + break; + + case DEMOD_ERROR_WAIT: + Demod.state = DEMOD_UNSYNCD; + break; + + default: + Demod.output[Demod.len] = 0xdd; + Demod.state = DEMOD_UNSYNCD; + break; + } + + /*if(Demod.bitCount>=9) { + Demod.output[Demod.len] = Demod.shiftReg & 0xff; + Demod.len++; + + Demod.parityBits <<= 1; + Demod.parityBits ^= ((Demod.shiftReg >> 8) & 0x01); + + Demod.bitCount = 0; + Demod.shiftReg = 0; + }*/ + if(Demod.bitCount>=8) { + Demod.shiftReg >>= 1; + Demod.output[Demod.len] = (Demod.shiftReg & 0xff); + Demod.len++; + + // FOR ISO15639 PARITY NOT SEND OTA, JUST CALCULATE IT FOR THE CLIENT + Demod.parityBits <<= 1; + Demod.parityBits ^= OddByteParity[(Demod.shiftReg & 0xff)]; + + Demod.bitCount = 0; + Demod.shiftReg = 0; + } + + if(error) { + Demod.output[Demod.len] = 0xBB; + Demod.len++; + Demod.output[Demod.len] = error & 0xFF; + Demod.len++; + Demod.output[Demod.len] = 0xBB; + Demod.len++; + Demod.output[Demod.len] = bit & 0xFF; + Demod.len++; + Demod.output[Demod.len] = Demod.buffer & 0xFF; + Demod.len++; + // Look harder ;-) + Demod.output[Demod.len] = Demod.buffer2 & 0xFF; + Demod.len++; + Demod.output[Demod.len] = Demod.syncBit & 0xFF; + Demod.len++; + Demod.output[Demod.len] = 0xBB; + Demod.len++; + return TRUE; + } + + } + + } // end (state != UNSYNCED) + + return FALSE; +} + +//============================================================================= +// Finally, a `sniffer' for ISO 14443 Type A +// Both sides of communication! +//============================================================================= + +//----------------------------------------------------------------------------- +// Record the sequence of commands sent by the reader to the tag, with +// triggering so that we start recording at the point that the tag is moved +// near the reader. +//----------------------------------------------------------------------------- +void RAMFUNC SnoopIClass(void) +{ +// #define RECV_CMD_OFFSET 2032 // original (working as of 21/2/09) values +// #define RECV_RES_OFFSET 2096 // original (working as of 21/2/09) values +// #define DMA_BUFFER_OFFSET 2160 // original (working as of 21/2/09) values +// #define DMA_BUFFER_SIZE 4096 // original (working as of 21/2/09) values +// #define TRACE_LENGTH 2000 // original (working as of 21/2/09) values + + // We won't start recording the frames that we acquire until we trigger; + // a good trigger condition to get started is probably when we see a + // response from the tag. + int triggered = FALSE; // FALSE to wait first for card + + // The command (reader -> tag) that we're receiving. + // The length of a received command will in most cases be no more than 18 bytes. + // So 32 should be enough! + uint8_t *receivedCmd = (((uint8_t *)BigBuf) + RECV_CMD_OFFSET); + // The response (tag -> reader) that we're receiving. + uint8_t *receivedResponse = (((uint8_t *)BigBuf) + RECV_RES_OFFSET); + + // 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; + + traceLen = 0; // uncommented to fix ISSUE 15 - gerhard - jan2011 + + // The DMA buffer, used to stream samples from the FPGA + int8_t *dmaBuf = ((int8_t *)BigBuf) + DMA_BUFFER_OFFSET; + int lastRxCounter; + int8_t *upTo; + int smpl; + int maxBehindBy = 0; + + // Count of samples received so far, so that we can include timing + // information in the trace buffer. + int samples = 0; + rsamples = 0; + + memset(trace, 0x44, RECV_CMD_OFFSET); + + // Set up the demodulator for tag -> reader responses. + Demod.output = receivedResponse; + Demod.len = 0; + Demod.state = DEMOD_UNSYNCD; + + // Setup for the DMA. + FpgaSetupSsc(); + upTo = dmaBuf; + lastRxCounter = DMA_BUFFER_SIZE; + FpgaSetupSscDma((uint8_t *)dmaBuf, DMA_BUFFER_SIZE); + + // And the reader -> tag commands + memset(&Uart, 0, sizeof(Uart)); + Uart.output = receivedCmd; + Uart.byteCntMax = 32; // was 100 (greg)//////////////////////////////////////////////////////////////////////// + Uart.state = STATE_UNSYNCD; + + // And put the FPGA in the appropriate mode + // Signal field is off with the appropriate LED + LED_D_OFF(); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_SNIFFER); + SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + + int div = 0; + //int div2 = 0; + int decbyte = 0; + int decbyter = 0; + + // And now we loop, receiving samples. + for(;;) { + LED_A_ON(); + WDT_HIT(); + int behindBy = (lastRxCounter - AT91C_BASE_PDC_SSC->PDC_RCR) & + (DMA_BUFFER_SIZE-1); + if(behindBy > maxBehindBy) { + maxBehindBy = behindBy; + if(behindBy > 400) { + Dbprintf("blew circular buffer! behindBy=0x%x", behindBy); + goto done; + } + } + if(behindBy < 1) continue; + + LED_A_OFF(); + smpl = upTo[0]; + upTo++; + lastRxCounter -= 1; + 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 = DMA_BUFFER_SIZE; + } + + //samples += 4; + samples += 1; + //div2++; + + //if(div2 > 3) { + //div2 = 0; + //decbyte ^= ((smpl & 0x01) << (3 - div)); + //decbyte ^= (((smpl & 0x01) | ((smpl & 0x02) >> 1)) << (3 - div)); // better already... + //decbyte ^= (((smpl & 0x01) | ((smpl & 0x02) >> 1) | ((smpl & 0x04) >> 2)) << (3 - div)); // even better... + if(smpl & 0xF) { + decbyte ^= (1 << (3 - div)); + } + //decbyte ^= (MajorityNibble[(smpl & 0x0F)] << (3 - div)); + + // FOR READER SIDE COMMUMICATION... + //decbyte ^= ((smpl & 0x10) << (3 - div)); + decbyter <<= 2; + decbyter ^= (smpl & 0x30); + + div++; + + if((div + 1) % 2 == 0) { + smpl = decbyter; + if(MillerDecoding((smpl & 0xF0) >> 4)) { + rsamples = samples - Uart.samples; + LED_C_ON(); + //if(triggered) { + trace[traceLen++] = ((rsamples >> 0) & 0xff); + trace[traceLen++] = ((rsamples >> 8) & 0xff); + trace[traceLen++] = ((rsamples >> 16) & 0xff); + trace[traceLen++] = ((rsamples >> 24) & 0xff); + trace[traceLen++] = ((Uart.parityBits >> 0) & 0xff); + trace[traceLen++] = ((Uart.parityBits >> 8) & 0xff); + trace[traceLen++] = ((Uart.parityBits >> 16) & 0xff); + trace[traceLen++] = ((Uart.parityBits >> 24) & 0xff); + trace[traceLen++] = Uart.byteCnt; + memcpy(trace+traceLen, receivedCmd, Uart.byteCnt); + traceLen += Uart.byteCnt; + if(traceLen > TRACE_LENGTH) break; + //} + /* And ready to receive another command. */ + Uart.state = STATE_UNSYNCD; + /* And also reset the demod code, which might have been */ + /* false-triggered by the commands from the reader. */ + Demod.state = DEMOD_UNSYNCD; + LED_B_OFF(); + Uart.byteCnt = 0; + } + decbyter = 0; + } + + if(div > 3) { + smpl = decbyte; + if(ManchesterDecoding(smpl & 0x0F)) { + rsamples = samples - Demod.samples; + LED_B_ON(); + + // timestamp, as a count of samples + trace[traceLen++] = ((rsamples >> 0) & 0xff); + trace[traceLen++] = ((rsamples >> 8) & 0xff); + trace[traceLen++] = ((rsamples >> 16) & 0xff); + trace[traceLen++] = 0x80 | ((rsamples >> 24) & 0xff); + trace[traceLen++] = ((Demod.parityBits >> 0) & 0xff); + trace[traceLen++] = ((Demod.parityBits >> 8) & 0xff); + trace[traceLen++] = ((Demod.parityBits >> 16) & 0xff); + trace[traceLen++] = ((Demod.parityBits >> 24) & 0xff); + // length + trace[traceLen++] = Demod.len; + memcpy(trace+traceLen, receivedResponse, Demod.len); + traceLen += Demod.len; + if(traceLen > TRACE_LENGTH) break; + + triggered = TRUE; + + // And ready to receive another response. + memset(&Demod, 0, sizeof(Demod)); + Demod.output = receivedResponse; + Demod.state = DEMOD_UNSYNCD; + LED_C_OFF(); + } + + div = 0; + decbyte = 0x00; + } + //} + + if(BUTTON_PRESS()) { + DbpString("cancelled_a"); + goto done; + } + } + + DbpString("COMMAND FINISHED"); + + Dbprintf("%x %x %x", maxBehindBy, Uart.state, Uart.byteCnt); + Dbprintf("%x %x %x", Uart.byteCntMax, 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]); + LED_A_OFF(); + LED_B_OFF(); + LED_C_OFF(); + LED_D_OFF(); +} + diff --git a/armsrc/iclass.h b/armsrc/iclass.h new file mode 100644 index 00000000..579773de --- /dev/null +++ b/armsrc/iclass.h @@ -0,0 +1,6 @@ +#ifndef __ISOICLASS_H +#define __ISOICLASS_H +#include "common.h" + + +#endif /* __ISOICLASS_H */ diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index 8c3293a6..1c5e9265 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -644,7 +644,7 @@ void RAMFUNC SnoopIso14443a(void) // Count of samples received so far, so that we can include timing // information in the trace buffer. int samples = 0; - int rsamples = 0; + int rsamples = 0; memset(trace, 0x44, RECV_CMD_OFFSET); diff --git a/client/Makefile b/client/Makefile index 7ca03ce1..6479d6cc 100644 --- a/client/Makefile +++ b/client/Makefile @@ -53,6 +53,7 @@ CMDSRCS = \ cmdhf14b.c \ cmdhf15.c \ cmdhflegic.c \ + cmdhficlass.c \ cmdhw.c \ cmdlf.c \ cmdlfem4x.c \ diff --git a/client/cmdhf.c b/client/cmdhf.c index 176d99cc..15b9e984 100644 --- a/client/cmdhf.c +++ b/client/cmdhf.c @@ -18,6 +18,7 @@ #include "cmdhf14b.h" #include "cmdhf15.h" #include "cmdhflegic.h" +#include "cmdhficlass.h" static int CmdHelp(const char *Cmd); @@ -35,6 +36,7 @@ static command_t CommandTable[] = {"14b", CmdHF14B, 1, "{ ISO14443B RFIDs... }"}, {"15", CmdHF15, 1, "{ ISO15693 RFIDs... }"}, {"legic", CmdHFLegic, 0, "{ LEGIC RFIDs... }"}, + {"iclass", CmdHFiClass, 1, "{ ICLASS RFIDs... }"}, {"tune", CmdHFTune, 0, "Continuously measure HF antenna tuning"}, {NULL, NULL, 0, NULL} }; diff --git a/client/cmdhficlass.c b/client/cmdhficlass.c new file mode 100644 index 00000000..03241467 --- /dev/null +++ b/client/cmdhficlass.c @@ -0,0 +1,191 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2010 iZsh , Hagen Fritsch +// Copyright (C) 2011 Gerhard de Koning Gans +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// High frequency iClass commands +//----------------------------------------------------------------------------- + +#include +#include +#include +#include "iso14443crc.h" // Can also be used for iClass, using 0xE012 as CRC-type +#include "data.h" +#include "proxusb.h" +#include "ui.h" +#include "cmdparser.h" +#include "cmdhficlass.h" +#include "common.h" + +static int CmdHelp(const char *Cmd); + +int CmdHFiClassList(const char *Cmd) +{ + uint8_t got[1920]; + GetFromBigBuf(got, sizeof(got)); + + PrintAndLog("recorded activity:"); + PrintAndLog(" ETU :rssi: who bytes"); + PrintAndLog("---------+----+----+-----------"); + + int i = 0; + int prev = -1; + + for (;;) { + if(i >= 1900) { + break; + } + + bool isResponse; + int timestamp = *((uint32_t *)(got+i)); + if (timestamp & 0x80000000) { + timestamp &= 0x7fffffff; + isResponse = 1; + } else { + isResponse = 0; + } + + int metric = 0; + int parityBits = *((uint32_t *)(got+i+4)); + // 4 bytes of additional information... + // maximum of 32 additional parity bit information + // + // TODO: + // at each quarter bit period we can send power level (16 levels) + // or each half bit period in 256 levels. + + + int len = got[i+8]; + + if (len > 100) { + break; + } + if (i + len >= 1900) { + 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[3] == 0x44) { break; } + + char line[1000] = ""; + int j; + for (j = 0; j < len; j++) { + int oddparity = 0x01; + int k; + + for (k=0;k<8;k++) { + oddparity ^= (((frame[j] & 0xFF) >> k) & 0x01); + } + + //if((parityBits >> (len - j - 1)) & 0x01) { + if (isResponse && (oddparity != ((parityBits >> (len - j - 1)) & 0x01))) { + sprintf(line+(j*4), "%02x! ", frame[j]); + } + else { + sprintf(line+(j*4), "%02x ", frame[j]); + } + } + + char *crc; + crc = ""; + if (len > 2) { + uint8_t b1, b2; + for (j = 0; j < (len - 1); j++) { + // gives problems... search for the reason.. + /*if(frame[j] == 0xAA) { + switch(frame[j+1]) { + case 0x01: + crc = "[1] Two drops close after each other"; + break; + case 0x02: + crc = "[2] Potential SOC with a drop in second half of bitperiod"; + break; + case 0x03: + crc = "[3] Segment Z after segment X is not possible"; + break; + case 0x04: + crc = "[4] Parity bit of a fully received byte was wrong"; + break; + default: + crc = "[?] Unknown error"; + break; + } + break; + }*/ + } + + if (strlen(crc)==0) { + if(!isResponse && len == 4) { + // Rough guess that this is a command from the reader + // For iClass the command byte is not part of the CRC + ComputeCrc14443(CRC_ICLASS, &frame[1], len-3, &b1, &b2); + } + else { + // For other data.. CRC might not be applicable (UPDATE commands etc.) + ComputeCrc14443(CRC_ICLASS, frame, len-2, &b1, &b2); + } + //printf("%1x %1x",(unsigned)b1,(unsigned)b2); + if (b1 != frame[len-2] || b2 != frame[len-1]) { + crc = (isResponse & (len < 8)) ? "" : " !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; +} + +/*void iso14a_set_timeout(uint32_t timeout) { + UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_SET_TIMEOUT, 0, timeout}}; + SendCommand(&c); +}*/ + +int CmdHFiClassSnoop(const char *Cmd) +{ + UsbCommand c = {CMD_SNOOP_ICLASS}; + SendCommand(&c); + return 0; +} + +static command_t CommandTable[] = +{ + {"help", CmdHelp, 1, "This help"}, + {"list", CmdHFiClassList, 0, "List iClass history"}, + {"snoop", CmdHFiClassSnoop, 0, "Eavesdrop iClass communication"}, + {NULL, NULL, 0, NULL} +}; + +int CmdHFiClass(const char *Cmd) +{ + CmdsParse(CommandTable, Cmd); + return 0; +} + +int CmdHelp(const char *Cmd) +{ + CmdsHelp(CommandTable); + return 0; +} diff --git a/client/cmdhficlass.h b/client/cmdhficlass.h new file mode 100644 index 00000000..a8ae4d3e --- /dev/null +++ b/client/cmdhficlass.h @@ -0,0 +1,20 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2010 iZsh +// Copyright (C) 2011 Gerhard de Koning Gans +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// High frequency iClass support +//----------------------------------------------------------------------------- + +#ifndef CMDHFICLASS_H__ +#define CMDHFICLASS_H__ + +int CmdHFiClass(const char *Cmd); + +int CmdHFiClassSnoop(const char *Cmd); +int CmdHFiClassList(const char *Cmd); + +#endif diff --git a/common/iso14443crc.h b/common/iso14443crc.h index 91c6ae1e..e70573ef 100644 --- a/common/iso14443crc.h +++ b/common/iso14443crc.h @@ -15,6 +15,7 @@ //----------------------------------------------------------------------------- #define CRC_14443_A 0x6363 /* ITU-V.41 */ #define CRC_14443_B 0xFFFF /* ISO/IEC 13239 (formerly ISO/IEC 3309) */ +#define CRC_ICLASS 0xE012 /* ICLASS PRERFIX */ void ComputeCrc14443(int CrcType, unsigned char *Data, int Length, diff --git a/include/usb_cmd.h b/include/usb_cmd.h index 5e514355..20a2b7ea 100644 --- a/include/usb_cmd.h +++ b/include/usb_cmd.h @@ -91,6 +91,7 @@ typedef struct { #define CMD_READER_LEGIC_RF 0x0388 #define CMD_WRITER_LEGIC_RF 0x0399 #define CMD_READER_MIFARE 0x0389 +#define CMD_SNOOP_ICLASS 0x0392 // For measurements of the antenna tuning #define CMD_MEASURE_ANTENNA_TUNING 0x0400