From: iceman1001 Date: Thu, 28 Jul 2016 19:41:44 +0000 (+0200) Subject: CHG: merged the forum user @jason 's fixes to LEGIC. *UNTESTED* X-Git-Url: http://cvs.zerfleddert.de/cgi-bin/gitweb.cgi/proxmark3-svn/commitdiff_plain/3e134b4c20a5528b8264cd87d3fdebd0b2df6fd9 CHG: merged the forum user @jason 's fixes to LEGIC. *UNTESTED* CHG: changed the CRC implementations. --- diff --git a/armsrc/appmain.c b/armsrc/appmain.c index 59011df2..5896eae7 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -1058,6 +1058,10 @@ void UsbPacketReceived(uint8_t *packet, int len) LegicRfWriter(c->arg[1], c->arg[0]); break; + case CMD_RAW_WRITER_LEGIC_RF: + LegicRfRawWriter(c->arg[0], c->arg[1]); + break; + case CMD_READER_LEGIC_RF: LegicRfReader(c->arg[0], c->arg[1]); break; diff --git a/armsrc/legicrf.c b/armsrc/legicrf.c index 5ad1fdf1..a725c0bc 100644 --- a/armsrc/legicrf.c +++ b/armsrc/legicrf.c @@ -325,12 +325,11 @@ static void LegicCommonInit(void) { crc_init(&legic_crc, 4, 0x19 >> 1, 0x5, 0); } +/* Switch off carrier, make sure tag is reset */ static void switch_off_tag_rwd(void) { - /* Switch off carrier, make sure tag is reset */ AT91C_BASE_PIOA->PIO_CODR = GPIO_SSC_DOUT; SpinDelay(10); - WDT_HIT(); } /* calculate crc for a legic command */ @@ -372,11 +371,12 @@ int legic_read_byte(int byte_index, int cmd_sz) { * * wait until the tag sends back an ACK ('1' bit unencrypted) * * forward the prng based on the timing */ +//int legic_write_byte(int byte, int addr, int addr_sz, int PrngCorrection) { int legic_write_byte(int byte, int addr, int addr_sz) { - //do not write UID, CRC, DCF - if(addr <= 0x06) + //do not write UID, CRC + if(addr <= 0x04) { return 0; - + } //== send write command ============================== crc_clear(&legic_crc); crc_update(&legic_crc, 0, 1); /* CMD_WRITE */ @@ -390,10 +390,13 @@ int legic_write_byte(int byte, int addr, int addr_sz) { |(0x00 <<0)); //CMD = W uint32_t cmd_sz = addr_sz+1+8+4; //crc+data+cmd - legic_prng_forward(2); /* we wait anyways */ + legic_prng_forward(4); /* we wait anyways */ while(timer->TC_CV < 387) ; /* ~ 258us */ frame_send_rwd(cmd, cmd_sz); + AT91C_BASE_PIOA->PIO_ODR = GPIO_SSC_DIN; + AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DIN; + //== wait for ack ==================================== int t, old_level=0, edges=0; int next_bit_at =0; @@ -413,7 +416,7 @@ int legic_write_byte(int byte, int addr, int addr_sz) { int c = t/TAG_TIME_BIT; timer->TC_CCR = AT91C_TC_SWTRG; while(timer->TC_CV > 1) ; /* Wait till the clock has reset */ - legic_prng_forward(c); + legic_prng_forward(c-1); return 0; } } @@ -423,6 +426,11 @@ int legic_write_byte(int byte, int addr, int addr_sz) { } int LegicRfReader(int offset, int bytes) { + + // ice_legic_setup(); + // ice_legic_select_card(); + // return 0; + int byte_index=0, cmd_sz=0, card_sz=0; LegicCommonInit(); @@ -434,13 +442,18 @@ int LegicRfReader(int offset, int bytes) { uint32_t tag_type = perform_setup_phase_rwd(SESSION_IV); switch_off_tag_rwd(); //we lose to mutch time with dprintf switch(tag_type) { + case 0x0d: + DbpString("MIM22 card found, reading card ..."); + cmd_sz = 6; + card_sz = 22; + break; case 0x1d: - DbpString("MIM 256 card found, reading card ..."); + DbpString("MIM256 card found, reading card ..."); cmd_sz = 9; card_sz = 256; break; case 0x3d: - DbpString("MIM 1024 card found, reading card ..."); + DbpString("MIM1024 card found, reading card ..."); cmd_sz = 11; card_sz = 1024; break; @@ -469,7 +482,7 @@ int LegicRfReader(int offset, int bytes) { BigBuf[byte_index] = r; WDT_HIT(); byte_index++; - if(byte_index & 0x10) LED_C_ON(); else LED_C_OFF(); + if (byte_index & 0x10) LED_C_ON(); else LED_C_OFF(); } LED_B_OFF(); LED_C_OFF(); @@ -479,6 +492,47 @@ int LegicRfReader(int offset, int bytes) { return 0; } +/*int _LegicRfWriter(int bytes, int offset, int addr_sz, uint8_t *BigBuf, int RoundBruteforceValue) { + int byte_index=0; + + LED_B_ON(); + perform_setup_phase_rwd(SESSION_IV); + //legic_prng_forward(2); + while(byte_index < bytes) { + int r; + + //check if the DCF should be changed + if ( (offset == 0x05) && (bytes == 0x02) ) { + //write DCF in reverse order (addr 0x06 before 0x05) + r = legic_write_byte(BigBuf[(0x06-byte_index)], (0x06-byte_index), addr_sz, RoundBruteforceValue); + //legic_prng_forward(1); + if(r == 0) { + byte_index++; + r = legic_write_byte(BigBuf[(0x06-byte_index)], (0x06-byte_index), addr_sz, RoundBruteforceValue); + } + //legic_prng_forward(1); + } + else { + r = legic_write_byte(BigBuf[byte_index+offset], byte_index+offset, addr_sz, RoundBruteforceValue); + } + if((r != 0) || BUTTON_PRESS()) { + Dbprintf("operation aborted @ 0x%03.3x", byte_index); + switch_off_tag_rwd(); + LED_B_OFF(); + LED_C_OFF(); + return -1; + } + + WDT_HIT(); + byte_index++; + if(byte_index & 0x10) LED_C_ON(); else LED_C_OFF(); + } + LED_B_OFF(); + LED_C_OFF(); + DbpString("write successful"); + return 0; +}*/ + void LegicRfWriter(int bytes, int offset) { int byte_index=0, addr_sz=0; uint8_t *BigBuf = BigBuf_get_addr(); @@ -489,32 +543,56 @@ void LegicRfWriter(int bytes, int offset) { uint32_t tag_type = perform_setup_phase_rwd(SESSION_IV); switch_off_tag_rwd(); switch(tag_type) { + case 0x0d: + if(offset+bytes > 22) { + Dbprintf("Error: can not write to 0x%03.3x on MIM22", offset+bytes); + return; + } + addr_sz = 5; + Dbprintf("MIM22 card found, writing 0x%02.2x - 0x%02.2x ...", offset, offset+bytes); + break; case 0x1d: if(offset+bytes > 0x100) { - Dbprintf("Error: can not write to 0x%03.3x on MIM 256", offset+bytes); + Dbprintf("Error: can not write to 0x%03.3x on MIM256", offset+bytes); return; } addr_sz = 8; - Dbprintf("MIM 256 card found, writing 0x%02.2x - 0x%02.2x ...", offset, offset+bytes); + Dbprintf("MIM256 card found, writing 0x%02.2x - 0x%02.2x ...", offset, offset+bytes); break; case 0x3d: if(offset+bytes > 0x400) { - Dbprintf("Error: can not write to 0x%03.3x on MIM 1024", offset+bytes); + Dbprintf("Error: can not write to 0x%03.3x on MIM1024", offset+bytes); return; } addr_sz = 10; - Dbprintf("MIM 1024 card found, writing 0x%03.3x - 0x%03.3x ...", offset, offset+bytes); + Dbprintf("MIM1024 card found, writing 0x%03.3x - 0x%03.3x ...", offset, offset+bytes); break; default: Dbprintf("No or unknown card found, aborting"); return; } +#if 1 LED_B_ON(); perform_setup_phase_rwd(SESSION_IV); - legic_prng_forward(2); + while(byte_index < bytes) { - int r = legic_write_byte(BigBuf[byte_index+offset], byte_index+offset, addr_sz); + int r; + + //check if the DCF should be changed + if ( ((byte_index+offset) == 0x05) && (bytes >= 0x02) ) { + //write DCF in reverse order (addr 0x06 before 0x05) + r = legic_write_byte(BigBuf[(0x06-byte_index)], (0x06-byte_index), addr_sz); + + // write second byte on success... + if(r == 0) { + byte_index++; + r = legic_write_byte(BigBuf[(0x06-byte_index)], (0x06-byte_index), addr_sz); + } + } + else { + r = legic_write_byte(BigBuf[byte_index+offset], byte_index+offset, addr_sz); + } if((r != 0) || BUTTON_PRESS()) { Dbprintf("operation aborted @ 0x%03.3x", byte_index); switch_off_tag_rwd(); @@ -522,6 +600,76 @@ void LegicRfWriter(int bytes, int offset) { LED_C_OFF(); return; } + + WDT_HIT(); + byte_index++; + if(byte_index & 0x10) LED_C_ON(); else LED_C_OFF(); + } + LED_B_OFF(); + LED_C_OFF(); + DbpString("write successful"); +#else + for(byte_index = -2; byte_index < 200; byte_index++) + { + Dbprintf("+ Try RndValue %d...", byte_index); + if(_LegicRfWriter(bytes, offset, addr_sz, BigBuf, byte_index) == 0) + break; + } +#endif + +} + +void LegicRfRawWriter(int offset, int byte) { + int byte_index=0, addr_sz=0; + + LegicCommonInit(); + + DbpString("setting up legic card"); + uint32_t tag_type = perform_setup_phase_rwd(SESSION_IV); + switch_off_tag_rwd(); + switch(tag_type) { + case 0x0d: + if(offset > 22) { + Dbprintf("Error: can not write to 0x%03.3x on MIM22", offset); + return; + } + addr_sz = 5; + Dbprintf("MIM22 card found, writing at addr 0x%02.2x - value 0x%02.2x ...", offset, byte); + break; + case 0x1d: + if(offset > 0x100) { + Dbprintf("Error: can not write to 0x%03.3x on MIM256", offset); + return; + } + addr_sz = 8; + Dbprintf("MIM256 card found, writing at addr 0x%02.2x - value 0x%02.2x ...", offset, byte); + break; + case 0x3d: + if(offset > 0x400) { + Dbprintf("Error: can not write to 0x%03.3x on MIM1024", offset); + return; + } + addr_sz = 10; + Dbprintf("MIM1024 card found, writing at addr 0x%03.3x - value 0x%03.3x ...", offset, byte); + break; + default: + Dbprintf("No or unknown card found, aborting"); + return; + } + Dbprintf("integer value: %d offset: %d addr_sz: %d", byte, offset, addr_sz); + LED_B_ON(); + perform_setup_phase_rwd(SESSION_IV); + //legic_prng_forward(2); + + int r = legic_write_byte(byte, offset, addr_sz); + + if((r != 0) || BUTTON_PRESS()) { + Dbprintf("operation aborted @ 0x%03.3x (%1d)", byte_index, r); + switch_off_tag_rwd(); + LED_B_OFF(); + LED_C_OFF(); + return; + WDT_HIT(); byte_index++; if(byte_index & 0x10) LED_C_ON(); else LED_C_OFF(); @@ -756,3 +904,757 @@ void LegicRfSimulate(int phase, int frame, int reqresp) LED_C_OFF(); } + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- +// Code up a string of octets at layer 2 (including CRC, we don't generate +// that here) so that they can be transmitted to the reader. Doesn't transmit +// them yet, just leaves them ready to send in ToSend[]. +//----------------------------------------------------------------------------- +// static void CodeLegicAsTag(const uint8_t *cmd, int len) +// { + // int i; + + // ToSendReset(); + + // // Transmit a burst of ones, as the initial thing that lets the + // // reader get phase sync. This (TR1) must be > 80/fs, per spec, + // // but tag that I've tried (a Paypass) exceeds that by a fair bit, + // // so I will too. + // for(i = 0; i < 20; i++) { + // ToSendStuffBit(1); + // ToSendStuffBit(1); + // ToSendStuffBit(1); + // ToSendStuffBit(1); + // } + + // // Send SOF. + // for(i = 0; i < 10; i++) { + // ToSendStuffBit(0); + // ToSendStuffBit(0); + // ToSendStuffBit(0); + // ToSendStuffBit(0); + // } + // for(i = 0; i < 2; i++) { + // ToSendStuffBit(1); + // ToSendStuffBit(1); + // ToSendStuffBit(1); + // ToSendStuffBit(1); + // } + + // for(i = 0; i < len; i++) { + // int j; + // uint8_t b = cmd[i]; + + // // Start bit + // ToSendStuffBit(0); + // ToSendStuffBit(0); + // ToSendStuffBit(0); + // ToSendStuffBit(0); + + // // Data bits + // for(j = 0; j < 8; j++) { + // if(b & 1) { + // ToSendStuffBit(1); + // ToSendStuffBit(1); + // ToSendStuffBit(1); + // ToSendStuffBit(1); + // } else { + // ToSendStuffBit(0); + // ToSendStuffBit(0); + // ToSendStuffBit(0); + // ToSendStuffBit(0); + // } + // b >>= 1; + // } + + // // Stop bit + // ToSendStuffBit(1); + // ToSendStuffBit(1); + // ToSendStuffBit(1); + // ToSendStuffBit(1); + // } + + // // Send EOF. + // for(i = 0; i < 10; i++) { + // ToSendStuffBit(0); + // ToSendStuffBit(0); + // ToSendStuffBit(0); + // ToSendStuffBit(0); + // } + // for(i = 0; i < 2; i++) { + // ToSendStuffBit(1); + // ToSendStuffBit(1); + // ToSendStuffBit(1); + // ToSendStuffBit(1); + // } + + // // Convert from last byte pos to length + // ToSendMax++; +// } + +//----------------------------------------------------------------------------- +// The software UART that receives commands from the reader, and its state +// variables. +//----------------------------------------------------------------------------- +static struct { + enum { + STATE_UNSYNCD, + STATE_GOT_FALLING_EDGE_OF_SOF, + STATE_AWAITING_START_BIT, + STATE_RECEIVING_DATA + } state; + uint16_t shiftReg; + int bitCnt; + int byteCnt; + int byteCntMax; + int posCnt; + uint8_t *output; +} Uart; + +/* Receive & handle a bit coming from the reader. + * + * This function is called 4 times per bit (every 2 subcarrier cycles). + * Subcarrier frequency fs is 212kHz, 1/fs = 4,72us, i.e. function is called every 9,44us + * + * LED handling: + * LED A -> ON once we have received the SOF and are expecting the rest. + * LED A -> OFF once we have received EOF or are in error state or unsynced + * + * Returns: true if we received a EOF + * false if we are still waiting for some more + */ +// static RAMFUNC int HandleLegicUartBit(uint8_t bit) +// { + // switch(Uart.state) { + // case STATE_UNSYNCD: + // if(!bit) { + // // we went low, so this could be the beginning of an SOF + // Uart.state = STATE_GOT_FALLING_EDGE_OF_SOF; + // Uart.posCnt = 0; + // Uart.bitCnt = 0; + // } + // break; + + // case STATE_GOT_FALLING_EDGE_OF_SOF: + // Uart.posCnt++; + // if(Uart.posCnt == 2) { // sample every 4 1/fs in the middle of a bit + // if(bit) { + // if(Uart.bitCnt > 9) { + // // we've seen enough consecutive + // // zeros that it's a valid SOF + // Uart.posCnt = 0; + // Uart.byteCnt = 0; + // Uart.state = STATE_AWAITING_START_BIT; + // LED_A_ON(); // Indicate we got a valid SOF + // } else { + // // didn't stay down long enough + // // before going high, error + // Uart.state = STATE_UNSYNCD; + // } + // } else { + // // do nothing, keep waiting + // } + // Uart.bitCnt++; + // } + // if(Uart.posCnt >= 4) Uart.posCnt = 0; + // if(Uart.bitCnt > 12) { + // // Give up if we see too many zeros without + // // a one, too. + // LED_A_OFF(); + // Uart.state = STATE_UNSYNCD; + // } + // break; + + // case STATE_AWAITING_START_BIT: + // Uart.posCnt++; + // if(bit) { + // if(Uart.posCnt > 50/2) { // max 57us between characters = 49 1/fs, max 3 etus after low phase of SOF = 24 1/fs + // // stayed high for too long between + // // characters, error + // Uart.state = STATE_UNSYNCD; + // } + // } else { + // // falling edge, this starts the data byte + // Uart.posCnt = 0; + // Uart.bitCnt = 0; + // Uart.shiftReg = 0; + // Uart.state = STATE_RECEIVING_DATA; + // } + // break; + + // case STATE_RECEIVING_DATA: + // Uart.posCnt++; + // if(Uart.posCnt == 2) { + // // time to sample a bit + // Uart.shiftReg >>= 1; + // if(bit) { + // Uart.shiftReg |= 0x200; + // } + // Uart.bitCnt++; + // } + // if(Uart.posCnt >= 4) { + // Uart.posCnt = 0; + // } + // if(Uart.bitCnt == 10) { + // if((Uart.shiftReg & 0x200) && !(Uart.shiftReg & 0x001)) + // { + // // this is a data byte, with correct + // // start and stop bits + // Uart.output[Uart.byteCnt] = (Uart.shiftReg >> 1) & 0xff; + // Uart.byteCnt++; + + // if(Uart.byteCnt >= Uart.byteCntMax) { + // // Buffer overflowed, give up + // LED_A_OFF(); + // Uart.state = STATE_UNSYNCD; + // } else { + // // so get the next byte now + // Uart.posCnt = 0; + // Uart.state = STATE_AWAITING_START_BIT; + // } + // } else if (Uart.shiftReg == 0x000) { + // // this is an EOF byte + // LED_A_OFF(); // Finished receiving + // Uart.state = STATE_UNSYNCD; + // if (Uart.byteCnt != 0) { + // return TRUE; + // } + // } else { + // // this is an error + // LED_A_OFF(); + // Uart.state = STATE_UNSYNCD; + // } + // } + // break; + + // default: + // LED_A_OFF(); + // Uart.state = STATE_UNSYNCD; + // break; + // } + + // return FALSE; +// } + + +static void UartReset() +{ + Uart.byteCntMax = MAX_FRAME_SIZE; + Uart.state = STATE_UNSYNCD; + Uart.byteCnt = 0; + Uart.bitCnt = 0; + Uart.posCnt = 0; + memset(Uart.output, 0x00, MAX_FRAME_SIZE); +} + +// static void UartInit(uint8_t *data) +// { + // Uart.output = data; + // UartReset(); +// } + +//============================================================================= +// An LEGIC reader. We take layer two commands, code them +// appropriately, and then send them to the tag. We then listen for the +// tag's response, which we leave in the buffer to be demodulated on the +// PC side. +//============================================================================= + +static struct { + enum { + DEMOD_UNSYNCD, + DEMOD_PHASE_REF_TRAINING, + DEMOD_AWAITING_FALLING_EDGE_OF_SOF, + DEMOD_GOT_FALLING_EDGE_OF_SOF, + DEMOD_AWAITING_START_BIT, + DEMOD_RECEIVING_DATA + } state; + int bitCount; + int posCount; + int thisBit; + uint16_t shiftReg; + uint8_t *output; + int len; + int sumI; + int sumQ; +} Demod; + +/* + * Handles reception of a bit from the tag + * + * This function is called 2 times per bit (every 4 subcarrier cycles). + * Subcarrier frequency fs is 212kHz, 1/fs = 4,72us, i.e. function is called every 9,44us + * + * LED handling: + * LED C -> ON once we have received the SOF and are expecting the rest. + * LED C -> OFF once we have received EOF or are unsynced + * + * Returns: true if we received a EOF + * false if we are still waiting for some more + * + */ + + #ifndef SUBCARRIER_DETECT_THRESHOLD + # define SUBCARRIER_DETECT_THRESHOLD 8 + #endif + + // Subcarrier amplitude v = sqrt(ci^2 + cq^2), approximated here by max(abs(ci),abs(cq)) + 1/2*min(abs(ci),abs(cq))) +#ifndef CHECK_FOR_SUBCARRIER +# define CHECK_FOR_SUBCARRIER() { v = MAX(ai, aq) + MIN(halfci, halfcq); } +#endif + +// The soft decision on the bit uses an estimate of just the +// quadrant of the reference angle, not the exact angle. +// Subcarrier amplitude v = sqrt(ci^2 + cq^2), approximated here by max(abs(ci),abs(cq)) + 1/2*min(abs(ci),abs(cq))) +#define MAKE_SOFT_DECISION() { \ + if(Demod.sumI > 0) \ + v = ci; \ + else \ + v = -ci; \ + \ + if(Demod.sumQ > 0) \ + v += cq; \ + else \ + v -= cq; \ + \ + } + +static RAMFUNC int HandleLegicSamplesDemod(int ci, int cq) +{ + int v = 0; + int ai = ABS(ci); + int aq = ABS(cq); + int halfci = (ai >> 1); + int halfcq = (aq >> 1); + + switch(Demod.state) { + case DEMOD_UNSYNCD: + + CHECK_FOR_SUBCARRIER() + + if(v > SUBCARRIER_DETECT_THRESHOLD) { // subcarrier detected + Demod.state = DEMOD_PHASE_REF_TRAINING; + Demod.sumI = ci; + Demod.sumQ = cq; + Demod.posCount = 1; + } + break; + + case DEMOD_PHASE_REF_TRAINING: + if(Demod.posCount < 8) { + + CHECK_FOR_SUBCARRIER() + + if (v > SUBCARRIER_DETECT_THRESHOLD) { + // set the reference phase (will code a logic '1') by averaging over 32 1/fs. + // note: synchronization time > 80 1/fs + Demod.sumI += ci; + Demod.sumQ += cq; + ++Demod.posCount; + } else { + // subcarrier lost + Demod.state = DEMOD_UNSYNCD; + } + } else { + Demod.state = DEMOD_AWAITING_FALLING_EDGE_OF_SOF; + } + break; + + case DEMOD_AWAITING_FALLING_EDGE_OF_SOF: + + MAKE_SOFT_DECISION() + + //Dbprintf("ICE: %d %d %d %d %d", v, Demod.sumI, Demod.sumQ, ci, cq ); + // logic '0' detected + if (v <= 0) { + + Demod.state = DEMOD_GOT_FALLING_EDGE_OF_SOF; + + // start of SOF sequence + Demod.posCount = 0; + } else { + // maximum length of TR1 = 200 1/fs + if(Demod.posCount > 25*2) Demod.state = DEMOD_UNSYNCD; + } + ++Demod.posCount; + break; + + case DEMOD_GOT_FALLING_EDGE_OF_SOF: + ++Demod.posCount; + + MAKE_SOFT_DECISION() + + if(v > 0) { + // low phase of SOF too short (< 9 etu). Note: spec is >= 10, but FPGA tends to "smear" edges + if(Demod.posCount < 10*2) { + Demod.state = DEMOD_UNSYNCD; + } else { + LED_C_ON(); // Got SOF + Demod.state = DEMOD_AWAITING_START_BIT; + Demod.posCount = 0; + Demod.len = 0; + } + } else { + // low phase of SOF too long (> 12 etu) + if(Demod.posCount > 13*2) { + Demod.state = DEMOD_UNSYNCD; + LED_C_OFF(); + } + } + break; + + case DEMOD_AWAITING_START_BIT: + ++Demod.posCount; + + MAKE_SOFT_DECISION() + + if(v > 0) { + // max 19us between characters = 16 1/fs, max 3 etu after low phase of SOF = 24 1/fs + if(Demod.posCount > 3*2) { + Demod.state = DEMOD_UNSYNCD; + LED_C_OFF(); + } + } else { + // start bit detected + Demod.bitCount = 0; + Demod.posCount = 1; // this was the first half + Demod.thisBit = v; + Demod.shiftReg = 0; + Demod.state = DEMOD_RECEIVING_DATA; + } + break; + + case DEMOD_RECEIVING_DATA: + + MAKE_SOFT_DECISION() + + if(Demod.posCount == 0) { + // first half of bit + Demod.thisBit = v; + Demod.posCount = 1; + } else { + // second half of bit + Demod.thisBit += v; + Demod.shiftReg >>= 1; + // logic '1' + if(Demod.thisBit > 0) + Demod.shiftReg |= 0x200; + + ++Demod.bitCount; + + if(Demod.bitCount == 10) { + + uint16_t s = Demod.shiftReg; + + if((s & 0x200) && !(s & 0x001)) { + // stop bit == '1', start bit == '0' + uint8_t b = (s >> 1); + Demod.output[Demod.len] = b; + ++Demod.len; + Demod.state = DEMOD_AWAITING_START_BIT; + } else { + Demod.state = DEMOD_UNSYNCD; + LED_C_OFF(); + + if(s == 0x000) { + // This is EOF (start, stop and all data bits == '0' + return TRUE; + } + } + } + Demod.posCount = 0; + } + break; + + default: + Demod.state = DEMOD_UNSYNCD; + LED_C_OFF(); + break; + } + return FALSE; +} + +// Clear out the state of the "UART" that receives from the tag. +static void DemodReset() { + Demod.len = 0; + Demod.state = DEMOD_UNSYNCD; + Demod.posCount = 0; + Demod.sumI = 0; + Demod.sumQ = 0; + Demod.bitCount = 0; + Demod.thisBit = 0; + Demod.shiftReg = 0; + memset(Demod.output, 0x00, MAX_FRAME_SIZE); +} + +static void DemodInit(uint8_t *data) { + Demod.output = data; + DemodReset(); +} + +/* + * Demodulate the samples we received from the tag, also log to tracebuffer + * quiet: set to 'TRUE' to disable debug output + */ + #define LEGIC_DMA_BUFFER_SIZE 256 +static void GetSamplesForLegicDemod(int n, bool quiet) +{ + int max = 0; + bool gotFrame = FALSE; + int lastRxCounter = LEGIC_DMA_BUFFER_SIZE; + int ci, cq, samples = 0; + + BigBuf_free(); + + // And put the FPGA in the appropriate mode + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR | FPGA_HF_READER_RX_XCORR_QUARTER_FREQ); + + // The response (tag -> reader) that we're receiving. + // Set up the demodulator for tag -> reader responses. + DemodInit(BigBuf_malloc(MAX_FRAME_SIZE)); + + // The DMA buffer, used to stream samples from the FPGA + int8_t *dmaBuf = (int8_t*) BigBuf_malloc(LEGIC_DMA_BUFFER_SIZE); + int8_t *upTo = dmaBuf; + + // Setup and start DMA. + if ( !FpgaSetupSscDma((uint8_t*) dmaBuf, LEGIC_DMA_BUFFER_SIZE) ){ + if (MF_DBGLEVEL > 1) Dbprintf("FpgaSetupSscDma failed. Exiting"); + return; + } + + // Signal field is ON with the appropriate LED: + LED_D_ON(); + for(;;) { + int behindBy = lastRxCounter - AT91C_BASE_PDC_SSC->PDC_RCR; + if(behindBy > max) max = behindBy; + + while(((lastRxCounter-AT91C_BASE_PDC_SSC->PDC_RCR) & (LEGIC_DMA_BUFFER_SIZE-1)) > 2) { + ci = upTo[0]; + cq = upTo[1]; + upTo += 2; + if(upTo >= dmaBuf + LEGIC_DMA_BUFFER_SIZE) { + upTo = dmaBuf; + AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) upTo; + AT91C_BASE_PDC_SSC->PDC_RNCR = LEGIC_DMA_BUFFER_SIZE; + } + lastRxCounter -= 2; + if(lastRxCounter <= 0) + lastRxCounter = LEGIC_DMA_BUFFER_SIZE; + + samples += 2; + + gotFrame = HandleLegicSamplesDemod(ci , cq ); + if ( gotFrame ) + break; + } + + if(samples > n || gotFrame) + break; + } + + FpgaDisableSscDma(); + + if (!quiet && Demod.len == 0) { + Dbprintf("max behindby = %d, samples = %d, gotFrame = %d, Demod.len = %d, Demod.sumI = %d, Demod.sumQ = %d", + max, + samples, + gotFrame, + Demod.len, + Demod.sumI, + Demod.sumQ + ); + } + + //Tracing + if (Demod.len > 0) { + uint8_t parity[MAX_PARITY_SIZE] = {0x00}; + LogTrace(Demod.output, Demod.len, 0, 0, parity, FALSE); + } +} +//----------------------------------------------------------------------------- +// Transmit the command (to the tag) that was placed in ToSend[]. +//----------------------------------------------------------------------------- +static void TransmitForLegic(void) +{ + int c; + + FpgaSetupSsc(); + + while(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) + AT91C_BASE_SSC->SSC_THR = 0xff; + + // Signal field is ON with the appropriate Red LED + LED_D_ON(); + + // Signal we are transmitting with the Green LED + LED_B_ON(); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX | FPGA_HF_READER_TX_SHALLOW_MOD); + + for(c = 0; c < 10;) { + if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { + AT91C_BASE_SSC->SSC_THR = 0xff; + c++; + } + if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { + volatile uint32_t r = AT91C_BASE_SSC->SSC_RHR; + (void)r; + } + WDT_HIT(); + } + + c = 0; + for(;;) { + if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { + AT91C_BASE_SSC->SSC_THR = ToSend[c]; + legic_prng_forward(1); // forward the lfsr + c++; + if(c >= ToSendMax) { + break; + } + } + if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { + volatile uint32_t r = AT91C_BASE_SSC->SSC_RHR; + (void)r; + } + WDT_HIT(); + } + LED_B_OFF(); +} + + +//----------------------------------------------------------------------------- +// Code a layer 2 command (string of octets, including CRC) into ToSend[], +// so that it is ready to transmit to the tag using TransmitForLegic(). +//----------------------------------------------------------------------------- +static void CodeLegicBitsAsReader(const uint8_t *cmd, int bits) +{ + int i, j; + uint8_t b; + + ToSendReset(); + + // Send SOF + for(i = 0; i < 7; i++) { + ToSendStuffBit(1); + } + + for(i = 0; i < bits; i++) { + // Start bit + ToSendStuffBit(0); + + // Data bits + b = cmd[i]; + for(j = 0; j < 8; j++) { + if(b & 1) { + ToSendStuffBit(1); + } else { + ToSendStuffBit(0); + } + b >>= 1; + } + } + + // Convert from last character reference to length + ++ToSendMax; +} + +/** + Convenience function to encode, transmit and trace Legic comms + **/ +static void CodeAndTransmitLegicAsReader(const uint8_t *cmd, int bits) +{ + CodeLegicBitsAsReader(cmd, bits); + TransmitForLegic(); + if (tracing) { + uint8_t parity[1] = {0x00}; + LogTrace(cmd, bits, 0, 0, parity, TRUE); + } +} + +int ice_legic_select_card() +{ + //int cmd_size=0, card_size=0; + uint8_t wakeup[] = { 0x7F}; + uint8_t getid[] = {0x19}; + + legic_prng_init(SESSION_IV); + + // first, wake up the tag, 7bits + CodeAndTransmitLegicAsReader(wakeup, 7); + + GetSamplesForLegicDemod(1000, TRUE); + + // frame_clean(¤t_frame); + //frame_receive_rwd(¤t_frame, 6, 1); + + legic_prng_forward(1); /* we wait anyways */ + + //while(timer->TC_CV < 387) ; /* ~ 258us */ + //frame_send_rwd(0x19, 6); + CodeAndTransmitLegicAsReader(getid, sizeof(getid)); + GetSamplesForLegicDemod(1000, TRUE); + + //if (Demod.len < 14) return 2; + Dbprintf("CARD TYPE: %02x LEN: %d", Demod.output[0], Demod.len); + + switch(Demod.output[0]) { + case 0x1d: + DbpString("MIM 256 card found"); + // cmd_size = 9; + // card_size = 256; + break; + case 0x3d: + DbpString("MIM 1024 card found"); + // cmd_size = 11; + // card_size = 1024; + break; + default: + return -1; + } + + // if(bytes == -1) + // bytes = card_size; + + // if(bytes + offset >= card_size) + // bytes = card_size - offset; + + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + set_tracing(FALSE); + return 1; +} + +// Set up LEGIC communication +void ice_legic_setup() { + + // standard things. + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + BigBuf_free(); BigBuf_Clear_ext(false); + clear_trace(); + set_tracing(TRUE); + DemodReset(); + UartReset(); + + // Set up the synchronous serial port + FpgaSetupSsc(); + + // connect Demodulated Signal to ADC: + SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + + // Signal field is on with the appropriate LED + LED_D_ON(); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX | FPGA_HF_READER_TX_SHALLOW_MOD); + SpinDelay(200); + // Start the timer + //StartCountSspClk(); + + // initalize CRC + crc_init(&legic_crc, 4, 0x19 >> 1, 0x5, 0); + + // initalize prng + legic_prng_init(0); +} \ No newline at end of file diff --git a/armsrc/legicrf.h b/armsrc/legicrf.h index 57ab7e6d..69fb442f 100644 --- a/armsrc/legicrf.h +++ b/armsrc/legicrf.h @@ -14,5 +14,9 @@ extern void LegicRfSimulate(int phase, int frame, int reqresp); extern int LegicRfReader(int bytes, int offset); extern void LegicRfWriter(int bytes, int offset); +extern void LegicRfRawWriter(int offset, int bytes); + +int ice_legic_select_card(); +void ice_legic_setup(); #endif /* __LEGICRF_H */ diff --git a/armsrc/util.h b/armsrc/util.h index 0fb445d6..486f22e9 100644 --- a/armsrc/util.h +++ b/armsrc/util.h @@ -14,6 +14,10 @@ #include #include #include "common.h" +#include "string.h" +#include "apps.h" +#include "BigBuf.h" +#include "proxmark3.h" #define BYTEx(x, n) (((x) >> (n * 8)) & 0xff ) @@ -27,9 +31,17 @@ #define BUTTON_DOUBLE_CLICK -2 #define BUTTON_ERROR -99 +#ifndef BSWAP_16 +# define BSWAP_16(x) ((( ((x) & 0xFF00 ) >> 8))| ( (((x) & 0x00FF) << 8))) +#endif +#ifndef BITMASK +# define BITMASK(X) (1 << (X)) +#endif + void print_result(char *name, uint8_t *buf, size_t len); size_t nbytes(size_t nbits); uint32_t SwapBits(uint32_t value, int nrbits); +uint32_t reflect(uint32_t v, int b); void num_to_bytes(uint64_t n, size_t len, uint8_t* dest); uint64_t bytes_to_num(uint8_t* src, size_t len); void rol(uint8_t *data, const size_t len); @@ -52,6 +64,7 @@ uint32_t RAMFUNC GetCountUS(); //uint32_t RAMFUNC GetDeltaCountUS(); void StartCountSspClk(); +void ResetSspClk(void); uint32_t RAMFUNC GetCountSspClk(); #endif diff --git a/client/cmdhflegic.c b/client/cmdhflegic.c index 0262f81c..d6aaaf3d 100644 --- a/client/cmdhflegic.c +++ b/client/cmdhflegic.c @@ -7,27 +7,21 @@ //----------------------------------------------------------------------------- // High frequency Legic commands //----------------------------------------------------------------------------- - -#include -#include -#include "proxmark3.h" -#include "data.h" -#include "ui.h" -#include "cmdparser.h" #include "cmdhflegic.h" -#include "cmdmain.h" -#include "util.h" -#include "crc.h" + static int CmdHelp(const char *Cmd); int usage_legic_calccrc8(void){ - PrintAndLog("Calculates the legic crc8 on the input hexbytes."); + PrintAndLog("Calculates the legic crc8/crc16 on the input hexbytes."); PrintAndLog("There must be an even number of hexsymbols as input."); - PrintAndLog("Usage: hf legic crc8 "); + PrintAndLog("Usage: hf legic crc8 [h] b u "); PrintAndLog("Options :"); - PrintAndLog(" : hex bytes in a string"); + PrintAndLog(" b : hex bytes"); + PrintAndLog(" u : MCC hexbyte"); PrintAndLog(""); - PrintAndLog("Sample : hf legic crc8 deadbeef1122"); + PrintAndLog("Samples :"); + PrintAndLog(" hf legic crc8 b deadbeef1122"); + PrintAndLog(" hf legic crc8 b deadbeef1122 u 9A"); return 0; } @@ -64,8 +58,10 @@ int CmdLegicDecode(const char *Cmd) { int crc = 0; int wrp = 0; int wrc = 0; - uint8_t data_buf[1024]; // receiver buffer, should be 1024.. - char token_type[4]; + uint8_t data_buf[1052]; // receiver buffer, should be 1024.. + char token_type[5]; + int dcf; + int bIsSegmented = 0; // download EML memory, where the "legic read" command puts the data. GetEMLFromBigBuf(data_buf, sizeof(data_buf), 0); @@ -89,71 +85,120 @@ int CmdLegicDecode(const char *Cmd) { (calc_crc == crc) ? "OK":"Fail" ); + + token_type[0] = 0; + dcf = ((int)data_buf[6] << 8) | (int)data_buf[5]; + + // New unwritten media? + if(dcf == 0xFFFF) { + + PrintAndLog("DCF: %d (%02x %02x), Token Type=NM (New Media)", + dcf, + data_buf[5], + data_buf[6] + ); + + } else if(dcf > 60000) { // Master token? + + int fl = 0; + + if(data_buf[6] == 0xec) { + strncpy(token_type, "XAM", sizeof(token_type)); + fl = 1; + stamp_len = 0x0c - (data_buf[5] >> 4); + } else { switch (data_buf[5] & 0x7f) { case 0x00 ... 0x2f: strncpy(token_type, "IAM",sizeof(token_type)); + fl = (0x2f - (data_buf[5] & 0x7f)) + 1; break; case 0x30 ... 0x6f: strncpy(token_type, "SAM",sizeof(token_type)); + fl = (0x6f - (data_buf[5] & 0x7f)) + 1; break; case 0x70 ... 0x7f: strncpy(token_type, "GAM",sizeof(token_type)); - break; - default: - strncpy(token_type, "???",sizeof(token_type)); + fl = (0x7f - (data_buf[5] & 0x7f)) + 1; break; } stamp_len = 0xfc - data_buf[6]; + } - PrintAndLog("DCF: %02x %02x, Token Type=%s (OLE=%01u), Stamp len=%02u", + PrintAndLog("DCF: %d (%02x %02x), Token Type=%s (OLE=%01u), OL=%02u, FL=%02u", + dcf, data_buf[5], data_buf[6], token_type, (data_buf[5]&0x80)>>7, - stamp_len + stamp_len, + fl ); - PrintAndLog("WRP=%02u, WRC=%01u, RD=%01u, raw=%02x, SSC=%02x", + } else { // Is IM(-S) type of card... + + if(data_buf[7] == 0x9F && data_buf[8] == 0xFF) { + bIsSegmented = 1; + strncpy(token_type, "IM-S", sizeof(token_type)); + } else { + strncpy(token_type, "IM", sizeof(token_type)); + } + + PrintAndLog("DCF: %d (%02x %02x), Token Type=%s (OLE=%01u)", + dcf, + data_buf[5], + data_buf[6], + token_type, + (data_buf[5]&0x80)>>7 + ); + } + + // Makes no sence to show this on blank media... + if(dcf != 0xFFFF) { + + if(bIsSegmented) { + PrintAndLog("WRP=%02u, WRC=%01u, RD=%01u, SSC=%02x", data_buf[7]&0x0f, (data_buf[7]&0x70)>>4, (data_buf[7]&0x80)>>7, - data_buf[7], data_buf[8] ); + } + // Header area is only available on IM-S cards, on master tokens this data is the master token data itself + if(bIsSegmented || dcf > 60000) { + if(dcf > 60000) { + PrintAndLog("Master token data"); + PrintAndLog("%s", sprint_hex(data_buf+8, 14)); + } else { PrintAndLog("Remaining Header Area"); PrintAndLog("%s", sprint_hex(data_buf+9, 13)); + } + } + } + uint8_t segCrcBytes[8] = {0x00}; uint32_t segCalcCRC = 0; uint32_t segCRC = 0; - // see if user area is xored or just zeros. - int numOfZeros = 0; - for (int index=22; index < 256; ++index){ - if ( data_buf[index] == 0x00 ) - ++numOfZeros; - } - // if possible zeros is less then 60%, lets assume data is xored - // 256 - 22 (header) = 234 - // 1024 - 22 (header) = 1002 - int isXored = (numOfZeros*100/stamp_len) < 50; - PrintAndLog("is data xored? %d ( %d %)", isXored, (numOfZeros*100/stamp_len)); - print_hex_break( data_buf, 33, 16); - - return 0; + // Data card? + if(dcf <= 60000) { PrintAndLog("\nADF: User Area"); PrintAndLog("------------------------------------------------------"); + + if(bIsSegmented) { + + // Data start point on segmented cards i = 22; - // 64 potential segements - // how to detect there is no segments?!? - for ( segmentNum=0; segmentNum<64; segmentNum++ ) { + + // decode segments + for (segmentNum=1; segmentNum < 128; segmentNum++ ) + { segment_len = ((data_buf[i+1]^crc)&0x0f) * 256 + (data_buf[i]^crc); segment_flag = ((data_buf[i+1]^crc)&0xf0)>>4; - wrp = (data_buf[i+2]^crc); wrc = ((data_buf[i+3]^crc)&0x70)>>4; @@ -198,11 +243,10 @@ int CmdLegicDecode(const char *Cmd) { PrintAndLog("WRC protected area: (I %d | K %d| WRC %d)", i, k, wrc); PrintAndLog("\nrow | data"); PrintAndLog("-----+------------------------------------------------"); - // de-xor? if not zero, assume it needs xoring. - if ( isXored) { - for ( k=i; k < wrc; ++k) + + for ( k=i; k < (i+wrc); ++k) data_buf[k] ^= crc; - } + print_hex_break( data_buf+i, wrc, 16); i += wrc; @@ -213,16 +257,14 @@ int CmdLegicDecode(const char *Cmd) { PrintAndLog("\nrow | data"); PrintAndLog("-----+------------------------------------------------"); - if (isXored) { - for (k=i; k < wrp_len; ++k) + for (k=i; k < (i+wrp_len); ++k) data_buf[k] ^= crc; - } print_hex_break( data_buf+i, wrp_len, 16); i += wrp_len; - // does this one work? + // does this one work? (Answer: Only if KGH/BGH is used with BCD encoded card number! So maybe this will show just garbage...) if( wrp_len == 8 ) PrintAndLog("Card ID: %2X%02X%02X", data_buf[i-4]^crc, data_buf[i-3]^crc, data_buf[i-2]^crc); } @@ -230,10 +272,9 @@ int CmdLegicDecode(const char *Cmd) { PrintAndLog("Remaining segment payload: (I %d | K %d | Remain LEN %d)", i, k, remain_seg_payload_len); PrintAndLog("\nrow | data"); PrintAndLog("-----+------------------------------------------------"); - if ( isXored ) { - for ( k=i; k < remain_seg_payload_len; ++k) + + for ( k=i; k < (i+remain_seg_payload_len); ++k) data_buf[k] ^= crc; - } print_hex_break( data_buf+i, remain_seg_payload_len, 16); @@ -245,6 +286,56 @@ int CmdLegicDecode(const char *Cmd) { if (segment_flag & 0x8) return 0; } // end for loop + + } else { + + // Data start point on unsegmented cards + i = 8; + + wrp = data_buf[7] & 0x0F; + wrc = (data_buf[7] & 0x07) >> 4; + + bool hasWRC = (wrc > 0); + bool hasWRP = (wrp > wrc); + int wrp_len = (wrp - wrc); + int remain_seg_payload_len = (1024 - 22 - wrp); // Any chance to get physical card size here!? + + PrintAndLog("Unsegmented card - WRP: %02u, WRC: %02u, RD: %01u", + wrp, + wrc, + (data_buf[7] & 0x80) >> 7 + ); + + if ( hasWRC ) { + PrintAndLog("WRC protected area: (I %d | WRC %d)", i, wrc); + PrintAndLog("\nrow | data"); + PrintAndLog("-----+------------------------------------------------"); + print_hex_break( data_buf+i, wrc, 16); + i += wrc; + } + + if ( hasWRP ) { + PrintAndLog("Remaining write protected area: (I %d | WRC %d | WRP %d | WRP_LEN %d)", i, wrc, wrp, wrp_len); + PrintAndLog("\nrow | data"); + PrintAndLog("-----+------------------------------------------------"); + print_hex_break( data_buf+i, wrp_len, 16); + i += wrp_len; + + // does this one work? (Answer: Only if KGH/BGH is used with BCD encoded card number! So maybe this will show just garbage...) + if( wrp_len == 8 ) + PrintAndLog("Card ID: %2X%02X%02X", data_buf[i-4], data_buf[i-3], data_buf[i-2]); + } + + PrintAndLog("Remaining segment payload: (I %d | Remain LEN %d)", i, remain_seg_payload_len); + PrintAndLog("\nrow | data"); + PrintAndLog("-----+------------------------------------------------"); + print_hex_break( data_buf+i, remain_seg_payload_len, 16); + i += remain_seg_payload_len; + + PrintAndLog("-----+------------------------------------------------\n"); + } + } + return 0; } @@ -417,8 +508,37 @@ int CmdLegicRfWrite(const char *Cmd) { return 0; } +//TODO: write a help text (iceman) +int CmdLegicRfRawWrite(const char *Cmd) { + char answer; + UsbCommand c = { CMD_RAW_WRITER_LEGIC_RF, {0,0,0} }; + int res = sscanf(Cmd, " 0x%"llx" 0x%"llx, &c.arg[0], &c.arg[1]); + if(res != 2) { + PrintAndLog("Please specify the offset and value as two hex strings"); + return -1; + } + + if (c.arg[0] == 0x05 || c.arg[0] == 0x06) { + PrintAndLog("############# DANGER !! #############"); + PrintAndLog("# changing the DCF is irreversible #"); + PrintAndLog("#####################################"); + PrintAndLog("do youe really want to continue? y(es) n(o)"); + scanf(" %c", &answer); + if (answer == 'y' || answer == 'Y') { + SendCommand(&c); + return 0; + } + return -1; + } + + clearCommandBuffer(); + SendCommand(&c); + return 0; +} + +//TODO: write a help text (iceman) int CmdLegicRfFill(const char *Cmd) { - UsbCommand cmd = {CMD_WRITER_LEGIC_RF}; + UsbCommand cmd = {CMD_WRITER_LEGIC_RF, {0,0,0} }; int res = sscanf(Cmd, " 0x%"llx" 0x%"llx" 0x%"llx, &cmd.arg[0], &cmd.arg[1], &cmd.arg[2]); if(res != 3) { PrintAndLog("Please specify the offset, length and value as two hex strings"); @@ -427,14 +547,14 @@ int CmdLegicRfFill(const char *Cmd) { int i; UsbCommand c = {CMD_DOWNLOADED_SIM_SAMPLES_125K, {0, 0, 0}}; - for(i = 0; i < 48; i++) { - c.d.asBytes[i] = cmd.arg[2]; - } - + memcpy(c.d.asBytes, cmd.arg[2], 48); + for(i = 0; i < 22; i++) { c.arg[0] = i*48; + + clearCommandBuffer(); SendCommand(&c); - WaitForResponse(CMD_ACK,NULL); + WaitForResponse(CMD_ACK, NULL); } clearCommandBuffer(); SendCommand(&cmd); @@ -443,20 +563,64 @@ int CmdLegicRfFill(const char *Cmd) { int CmdLegicCalcCrc8(const char *Cmd){ - int len = strlen(Cmd); - if ( len & 1 ) return usage_legic_calccrc8(); + uint8_t *data; + uint8_t cmdp = 0, uidcrc = 0, type=0; + bool errors = false; + int len = 0; - // add 1 for null terminator. - uint8_t *data = malloc(len+1); - if ( data == NULL ) return 1; - - if (param_gethex(Cmd, 0, data, len )) { - free(data); - return usage_legic_calccrc8(); + while(param_getchar(Cmd, cmdp) != 0x00) { + switch(param_getchar(Cmd, cmdp)) { + case 'b': + case 'B': + data = malloc(len); + if ( data == NULL ) { + PrintAndLog("Can't allocate memory. exiting"); + errors = true; + break; + } + param_gethex_ex(Cmd, cmdp+1, data, &len); + // if odd symbols, (hexbyte must be two symbols) + if ( len & 1 ) errors = true; + + len >>= 1; + cmdp += 2; + break; + case 'u': + case 'U': + uidcrc = param_get8ex(Cmd, cmdp+1, 0, 16); + cmdp += 2; + break; + case 'c': + case 'C': + type = param_get8ex(Cmd, cmdp+1, 0, 10); + cmdp += 2; + break; + case 'h': + case 'H': + errors = true; + break; + default: + PrintAndLog("Unknown parameter '%c'", param_getchar(Cmd, cmdp)); + errors = true; + break; + } + if (errors) break; + } + //Validations + if (errors){ + if (data != NULL) free(data); + return usage_legic_calccrc8(); + } + + switch (type){ + case 16: + PrintAndLog("LEGIC CRC16: %X", CRC16Legic(data, len, uidcrc)); + break; + default: + PrintAndLog("LEGIC CRC8: %X", CRC8Legic(data, len) ); + break; } - uint32_t checksum = CRC8Legic(data, len/2); - PrintAndLog("Bytes: %s || CRC8: %X", sprint_hex(data, len/2), checksum ); free(data); return 0; } @@ -469,6 +633,7 @@ static command_t CommandTable[] = { {"load", CmdLegicLoad, 0, " -- Restore samples"}, {"sim", CmdLegicRfSim, 0, "[phase drift [frame drift [req/resp drift]]] Start tag simulator (use after load or read)"}, {"write", CmdLegicRfWrite,0, " -- Write sample buffer (user after load or read)"}, + {"writeRaw",CmdLegicRfRawWrite, 0, "
-- Write direct to address"}, {"fill", CmdLegicRfFill, 0, " -- Fill/Write tag with constant value"}, {"crc8", CmdLegicCalcCrc8, 1, "Calculate Legic CRC8 over given hexbytes"}, {NULL, NULL, 0, NULL} diff --git a/client/cmdhflegic.h b/client/cmdhflegic.h index 31082155..a22de93d 100644 --- a/client/cmdhflegic.h +++ b/client/cmdhflegic.h @@ -11,6 +11,16 @@ #ifndef CMDHFLEGIC_H__ #define CMDHFLEGIC_H__ +#include +#include +#include "proxmark3.h" +#include "data.h" +#include "ui.h" +#include "cmdparser.h" +#include "cmdmain.h" +#include "util.h" +#include "crc.h" + int CmdHFLegic(const char *Cmd); int CmdLegicRFRead(const char *Cmd); @@ -19,9 +29,12 @@ int CmdLegicLoad(const char *Cmd); int CmdLegicSave(const char *Cmd); int CmdLegicRfSim(const char *Cmd); int CmdLegicRfWrite(const char *Cmd); +int CmdLegicRfRawWrite(const char *Cmd); int CmdLegicRfFill(const char *Cmd); int CmdLegicCalcCrc8(const char *Cmd); int usage_legic_calccrc8(void); +int usage_legic_load(void); +int usage_legic_read(void); #endif diff --git a/client/util.c b/client/util.c index 4bbc992e..4129daaf 100644 --- a/client/util.c +++ b/client/util.c @@ -9,15 +9,13 @@ //----------------------------------------------------------------------------- #include "util.h" -#include "proxmark3.h" #define MAX_BIN_BREAK_LENGTH (3072+384+1) #ifndef _WIN32 #include #include -int ukbhit(void) -{ +int ukbhit(void) { int cnt = 0; int error; static struct termios Otty, Ntty; @@ -553,4 +551,20 @@ uint32_t SwapBits(uint32_t value, int nrbits) { newvalue ^= ((value >> i) & 1) << (nrbits - 1 - i); } return newvalue; -} \ No newline at end of file +} +/* + ref http://www.csm.ornl.gov/~dunigan/crc.html + Returns the value v with the bottom b [0,32] bits reflected. + Example: reflect(0x3e23L,3) == 0x3e26 +*/ +uint32_t reflect(uint32_t v, int b) { + uint32_t t = v; + for ( int i = 0; i < b; ++i) { + if (t & 1) + v |= BITMASK((b-1)-i); + else + v &= ~BITMASK((b-1)-i); + t>>=1; + } + return v; +} diff --git a/client/util.h b/client/util.h index e492fd49..655f823b 100644 --- a/client/util.h +++ b/client/util.h @@ -15,7 +15,11 @@ #include #include #include "data.h" //for FILE_PATH_SIZE +#include "proxmark3.h" +#ifndef BITMASK +# define BITMASK(X) (1 << (X)) +#endif #ifndef ROTR # define ROTR(x,n) (((uintmax_t)(x) >> (n)) | ((uintmax_t)(x) << ((sizeof(x) * 8) - (n)))) #endif @@ -26,10 +30,14 @@ # define MAX(a, b) (((a) > (b)) ? (a) : (b)) #endif #ifndef BSWAP_32 -#define BSWAP_32(x) \ +# define BSWAP_32(x) \ ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \ (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24)) #endif +#ifndef BSWAP_16 +# define BSWAP_16(x) ((( ((x) & 0xFF00 ) >> 8))| ( (((x) & 0x00FF) << 8))) +#endif + #define TRUE 1 #define FALSE 0 #define EVEN 0 @@ -93,4 +101,5 @@ int32_t le24toh (uint8_t data[3]); uint32_t le32toh (uint8_t *data); uint32_t PackBits(uint8_t start, uint8_t len, uint8_t* bits); void rol(uint8_t *data, const size_t len); -uint32_t SwapBits(uint32_t value, int nrbits); \ No newline at end of file +uint32_t SwapBits(uint32_t value, int nrbits); +uint32_t reflect(uint32_t v, int b); \ No newline at end of file diff --git a/common/bucketsort.c b/common/bucketsort.c index 2aca2632..162869e3 100644 --- a/common/bucketsort.c +++ b/common/bucketsort.c @@ -1,6 +1,6 @@ #include "bucketsort.h" -void bucket_sort_intersect(uint32_t* const estart, uint32_t* const estop, +extern void bucket_sort_intersect(uint32_t* const estart, uint32_t* const estop, uint32_t* const ostart, uint32_t* const ostop, bucket_info_t *bucket_info, bucket_array_t bucket) { diff --git a/common/bucketsort.h b/common/bucketsort.h index d5423aa5..be504508 100644 --- a/common/bucketsort.h +++ b/common/bucketsort.h @@ -1,7 +1,9 @@ #ifndef BUCKETSORT_H__ #define BUCKETSORT_H__ + #include #include + typedef struct bucket { uint32_t *head; uint32_t *bp; diff --git a/common/crc.c b/common/crc.c index 6c2f6994..451282bf 100644 --- a/common/crc.c +++ b/common/crc.c @@ -5,66 +5,136 @@ //----------------------------------------------------------------------------- // Generic CRC calculation code. //----------------------------------------------------------------------------- +// the Check value below in the comments is CRC of the string '123456789' +// #include "crc.h" -#include "util.h" -#include -#include -void crc_init(crc_t *crc, int order, uint32_t polynom, uint32_t initial_value, uint32_t final_xor) -{ +void crc_init_ref(crc_t *crc, int order, uint32_t polynom, uint32_t initial_value, uint32_t final_xor, bool refin, bool refout) { + crc_init(crc, order, polynom, initial_value, final_xor); + crc->refin = refin; + crc->refout = refout; + crc_clear(crc); +} + +void crc_init(crc_t *crc, int order, uint32_t polynom, uint32_t initial_value, uint32_t final_xor) { crc->order = order; + crc->topbit = BITMASK( order-1 ); crc->polynom = polynom; crc->initial_value = initial_value; crc->final_xor = final_xor; crc->mask = (1L<refin = FALSE; + crc->refout = FALSE; crc_clear(crc); } -void crc_update(crc_t *crc, uint32_t data, int data_width) -{ - for( int i=0; i < data_width; i++) { - int oldstate = crc->state; - crc->state = crc->state >> 1; - if( (oldstate^data) & 1 ) { - crc->state ^= crc->polynom; - } - data >>= 1; +void crc_clear(crc_t *crc) { + crc->state = crc->initial_value & crc->mask; + if (crc->refin) + crc->state = reflect(crc->state, crc->order); +} + +void crc_update(crc_t *crc, uint32_t indata, int data_width){ + + //reflected + if (crc->refin) indata = reflect(indata, data_width); + + // Bring the next byte into the remainder. + crc->state ^= indata << (crc->order - data_width); + + for( uint8_t bit = data_width; bit > 0; --bit) { + // Try to divide the current data bit. + if (crc->state & crc->topbit) + crc->state = (crc->state << 1) ^ crc->polynom; + else + crc->state = (crc->state << 1); } } -void crc_clear(crc_t *crc) -{ - crc->state = crc->initial_value & crc->mask; +uint32_t crc_finish(crc_t *crc) { + uint32_t val = crc->state; + if (crc->refout) val = reflect(val, crc->order); + return ( val ^ crc->final_xor ) & crc->mask; } -uint32_t crc_finish(crc_t *crc) -{ - return ( crc->state ^ crc->final_xor ) & crc->mask; +/* +static void print_crc(crc_t *crc) { + printf(" Order %d\n Poly %x\n Init %x\n Final %x\n Mask %x\n topbit %x\n RefIn %s\n RefOut %s\n State %x\n", + crc->order, + crc->polynom, + crc->initial_value, + crc->final_xor, + crc->mask, + crc->topbit, + (crc->refin) ? "TRUE":"FALSE", + (crc->refout) ? "TRUE":"FALSE", + crc->state + ); } +*/ -//credits to iceman +// width=8 poly=0x31 init=0x00 refin=true refout=true xorout=0x00 check=0xA1 name="CRC-8/MAXIM" uint32_t CRC8Maxim(uint8_t *buff, size_t size) { crc_t crc; - crc_init(&crc, 9, 0x8c, 0x00, 0x00); - crc_clear(&crc); + crc_init_ref(&crc, 8, 0x31, 0, 0, TRUE, TRUE); + for ( int i=0; i < size; ++i) + crc_update(&crc, buff[i], 8); + return crc_finish(&crc); +} + - for (size_t i=0; i < size; ++i) +// width=4 poly=0xC, reversed poly=0x7 init=0x5 refin=true refout=true xorout=0x0000 check= name="CRC-4/LEGIC" +// width=8 poly=0x63, reversed poly=0x8D init=0x55 refin=true refout=true xorout=0x0000 check=0xC6 name="CRC-8/LEGIC" +// the CRC needs to be reversed before returned. +uint32_t CRC8Legic(uint8_t *buff, size_t size) { + crc_t crc; + crc_init_ref(&crc, 8, 0x63, 0x55, 0, TRUE, TRUE); + for ( int i = 0; i < size; ++i) crc_update(&crc, buff[i], 8); + return reflect(crc_finish(&crc), 8); +} +// credits to marshmellow +// width=8 poly=0xA3, reversed poly=0x8B, init=0xB0 refin=true refout=true xorout=0x00 check=0x28 name="CRC-8/JA" +uint32_t CRC8ja(uint8_t *buff, size_t size) { + crc_t crc; + crc_init_ref(&crc, 8, 0xA3, 0x42, 0x00, TRUE, TRUE); + for ( int i=0; i < size; ++i) + crc_update(&crc, buff[i], 8); return crc_finish(&crc); + //return reflect(crc_finish(&crc), 8); } -//credits to iceman -uint32_t CRC8Legic(uint8_t *buff, size_t size) { +// This CRC-16 is used in Legic Advant systems. +// width=8 poly=0xB400, reversed poly=0x init=depends refin=true refout=true xorout=0x0000 check= name="CRC-16/LEGIC" +uint32_t CRC16Legic(uint8_t *buff, size_t size, uint8_t uidcrc) { - // Poly 0x63, reversed poly 0xC6, Init 0x55, Final 0x00 + #define CRC16_POLY_LEGIC 0xB400 + //uint8_t initial = reflect(uidcrc, 8); + uint16_t initial = uidcrc; + initial |= initial << 8; crc_t crc; - crc_init(&crc, 8, 0xC6, 0x55, 0); - crc_clear(&crc); - - for ( int i = 0; i < size; ++i) + crc_init_ref(&crc, 16, CRC16_POLY_LEGIC, initial, 0, TRUE, TRUE); + for ( int i=0; i < size; ++i) crc_update(&crc, buff[i], 8); - return SwapBits(crc_finish(&crc), 8); + return reflect(crc_finish(&crc), 16); } +//w=16 poly=0x3d65 init=0x0000 refin=true refout=true xorout=0xffff check=0xea82 name="CRC-16/DNP" +uint32_t CRC16_DNP(uint8_t *buff, size_t size) { + crc_t crc; + crc_init_ref(&crc, 16, 0x3d65, 0, 0xffff, TRUE, TRUE); + for ( int i=0; i < size; ++i) + crc_update(&crc, buff[i], 8); + + return BSWAP_16(crc_finish(&crc)); +} +//width=16 poly=0x1021 init=0x1d0f refin=false refout=false xorout=0x0000 check=0xe5cc name="CRC-16/AUG-CCITT" +uint32_t CRC16_CCITT(uint8_t *buff, size_t size) { + crc_t crc; + crc_init(&crc, 16, 0x1021, 0x1d0f, 0); + for ( int i=0; i < size; ++i) + crc_update(&crc, buff[i], 8); + return crc_finish(&crc); +} \ No newline at end of file diff --git a/common/crc.h b/common/crc.h index fc76dc48..48e0d1e6 100644 --- a/common/crc.h +++ b/common/crc.h @@ -9,8 +9,10 @@ #ifndef __CRC_H #define __CRC_H -#include +#include //uint32+ +#include //bool #include +#include "util.h" // reflect, bswap_16 typedef struct crc { uint32_t state; @@ -19,13 +21,25 @@ typedef struct crc { uint32_t initial_value; uint32_t final_xor; uint32_t mask; + int topbit; + bool refin; /* Parameter: Reflect input bytes? */ + bool refout; /* Parameter: Reflect output CRC? */ } crc_t; +/* Initialize a crc structure. order is the order of the polynom, e.g. 32 for a CRC-32 + * polynom is the CRC polynom. initial_value is the initial value of a clean state. + * final_xor is XORed onto the state before returning it from crc_result(). + * refin is the setting for reversing (bitwise) the bytes during crc + * refot is the setting for reversing (bitwise) the crc byte before returning it. + */ +extern void crc_init_ref(crc_t *crc, int order, uint32_t polynom, uint32_t initial_value, uint32_t final_xor, bool refin, bool refout); + /* Initialize a crc structure. order is the order of the polynom, e.g. 32 for a CRC-32 * polynom is the CRC polynom. initial_value is the initial value of a clean state. * final_xor is XORed onto the state before returning it from crc_result(). */ extern void crc_init(crc_t *crc, int order, uint32_t polynom, uint32_t initial_value, uint32_t final_xor); + /* Update the crc state. data is the data of length data_width bits (only the * data_width lower-most bits are used). */ @@ -40,9 +54,24 @@ extern uint32_t crc_finish(crc_t *crc); // Calculate CRC-8/Maxim checksum uint32_t CRC8Maxim(uint8_t *buff, size_t size); +// Calculate CRC-4/Legic checksum +uint32_t CRC4Legic(uint8_t *buff, size_t size); + // Calculate CRC-8/Legic checksum uint32_t CRC8Legic(uint8_t *buff, size_t size); +// Calculate CRC-16/Legic checksum +// the initial_value is based on the previous legic_Crc8 of the UID. +// ie: uidcrc = 0x78 then initial_value == 0x7878 +uint32_t CRC16Legic(uint8_t *buff, size_t size, uint8_t uidcrc); + +// Calculate CRC-8/ja checksum +uint32_t CRC8ja(uint8_t *buff, size_t size); + +// test crc 16. +uint32_t CRC16_DNP(uint8_t *buff, size_t size); + + /* Static initialization of a crc structure */ #define CRC_INITIALIZER(_order, _polynom, _initial_value, _final_xor) { \ .state = ((_initial_value) & ((1L<<(_order))-1)), \ @@ -50,6 +79,9 @@ uint32_t CRC8Legic(uint8_t *buff, size_t size); .polynom = (_polynom), \ .initial_value = (_initial_value), \ .final_xor = (_final_xor), \ - .mask = ((1L<<(_order))-1) } + .mask = ((1L<<(_order))-1) \ + .refin = FALSE, \ + .refout = FALSE \ + } #endif /* __CRC_H */ diff --git a/common/crc16.c b/common/crc16.c index 9d3f68c9..21310b79 100644 --- a/common/crc16.c +++ b/common/crc16.c @@ -9,11 +9,9 @@ #include "crc16.h" #define CRC16_POLY_CCITT 0x1021 #define CRC16_POLY 0x8408 -#define CRC16_POLY_LEGIC 0xB400 -unsigned short update_crc16( unsigned short crc, unsigned char c ) -{ - unsigned short i, v, tcrc = 0; +uint16_t update_crc16( uint16_t crc, unsigned char c ) { + uint16_t i, v, tcrc = 0; v = (crc ^ c) & 0xff; for (i = 0; i < 8; i++) { @@ -29,8 +27,8 @@ uint16_t crc16(uint8_t const *message, int length, uint16_t remainder, uint16_t if (length == 0) return (~remainder); - for (int byte = 0; byte < length; ++byte) { - remainder ^= (message[byte] << 8); + for (uint32_t i = 0; i < length; ++i) { + remainder ^= (message[i] << 8); for (uint8_t bit = 8; bit > 0; --bit) { if (remainder & 0x8000) { remainder = (remainder << 1) ^ polynomial; @@ -47,25 +45,6 @@ uint16_t crc16_ccitt(uint8_t const *message, int length) { } uint16_t crc16_ccitt_kermit(uint8_t const *message, int length) { - return bit_reverse_uint16(crc16(message, length, 0x0000, CRC16_POLY_CCITT)); -} - -//ICEMAN: not working yet, -// This CRC-16 is used in Legic Advant systems. -uint16_t crc16_legic(uint8_t const *message, int length, uint16_t inital) { - return crc16(message, length, inital, CRC16_POLY_LEGIC); -} - -uint16_t bit_reverse_uint16 (uint16_t value) { - const uint16_t mask0 = 0x5555; - const uint16_t mask1 = 0x3333; - const uint16_t mask2 = 0x0F0F; - const uint16_t mask3 = 0x00FF; - - value = (((~mask0) & value) >> 1) | ((mask0 & value) << 1); - value = (((~mask1) & value) >> 2) | ((mask1 & value) << 2); - value = (((~mask2) & value) >> 4) | ((mask2 & value) << 4); - value = (((~mask3) & value) >> 8) | ((mask3 & value) << 8); - - return value; + uint16_t val = crc16(message, length, 0x0000, CRC16_POLY_CCITT); + return SwapBits(val, 16); } diff --git a/common/crc16.h b/common/crc16.h index d53c7766..3db2cd79 100644 --- a/common/crc16.h +++ b/common/crc16.h @@ -5,14 +5,14 @@ //----------------------------------------------------------------------------- // CRC16 //----------------------------------------------------------------------------- -#include - #ifndef __CRC16_H #define __CRC16_H + +#include +#include "util.h" + unsigned short update_crc16(unsigned short crc, unsigned char c); uint16_t crc16(uint8_t const *message, int length, uint16_t remainder, uint16_t polynomial); uint16_t crc16_ccitt(uint8_t const *message, int length); uint16_t crc16_ccitt_kermit(uint8_t const *message, int length); -uint16_t crc16_legic(uint8_t const *message, int length, uint16_t inital); -uint16_t bit_reverse_uint16 (uint16_t value); #endif diff --git a/common/crc32.h b/common/crc32.h index 0dd2a328..7df7c79d 100644 --- a/common/crc32.h +++ b/common/crc32.h @@ -9,7 +9,7 @@ #ifndef __CRC32_H #define __CRC32_H -void crc32 (const uint8_t *data, const size_t len, uint8_t *crc); -void crc32_append (uint8_t *data, const size_t len); +void crc32 (const uint8_t *data, const size_t len, uint8_t *crc); +void crc32_append (uint8_t *data, const size_t len); #endif diff --git a/common/crc64.c b/common/crc64.c index 709c64d6..adf62d97 100644 --- a/common/crc64.c +++ b/common/crc64.c @@ -74,9 +74,7 @@ const uint64_t crc64_table[] = { void crc64 (const uint8_t *data, const size_t len, uint64_t *crc) { - for (size_t i = 0; i < len; i++) - { - //uint8_t tableIndex = (((uint8_t)(*crc >> 56)) ^ data[i]) & 0xff; + for (size_t i = 0; i < len; i++) { uint8_t tableIndex = (((uint8_t)(*crc >> 56)) ^ data[i]) & 0xff; *crc = crc64_table[tableIndex] ^ (*crc << 8); }