From 979c76556a665499ee946cdb143d3fe3768286cb Mon Sep 17 00:00:00 2001 From: marshmellow42 Date: Wed, 5 Jul 2017 12:11:48 -0400 Subject: [PATCH 1/4] fix some iclass reader bugs (#350) endless loop in reader if no tag was found button press on pm3 did not cancel. led_b was left on in some cases also moved 14b detection to last in hf search to help speed up the command for the other tags. 14b is slow (does multiple tests) Thanks to @Fl0-0 and @pwpiwi for their idenfication of some of the issues. --- armsrc/iclass.c | 20 +++++++++++++++----- client/cmdhf.c | 11 ++++++----- client/cmdhficlass.c | 10 +++++++--- 3 files changed, 28 insertions(+), 13 deletions(-) diff --git a/armsrc/iclass.c b/armsrc/iclass.c index f69d0be2..32f9594b 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -49,6 +49,7 @@ #include "iso15693tools.h" #include "protocols.h" #include "optimized_cipher.h" +#include "usb_cdc.h" // for usb_poll_validate_length static int timeout = 4096; @@ -1681,7 +1682,7 @@ void ReaderIClass(uint8_t arg0) { uint8_t card_data[6 * 8]={0}; memset(card_data, 0xFF, sizeof(card_data)); - uint8_t last_csn[8]={0}; + uint8_t last_csn[8]={0,0,0,0,0,0,0,0}; uint8_t resp[ICLASS_BUFFER_SIZE]; memset(resp, 0xFF, sizeof(resp)); //Read conf block CRC(0x01) => 0xfa 0x22 @@ -1707,9 +1708,11 @@ void ReaderIClass(uint8_t arg0) { setupIclassReader(); uint16_t tryCnt=0; - while(!BUTTON_PRESS()) + bool userCancelled = BUTTON_PRESS() || usb_poll_validate_length(); + while(!userCancelled) { - if (try_once && tryCnt > 5) break; + // if only looking for one card try 2 times if we missed it the first time + if (try_once && tryCnt > 2) break; tryCnt++; if(!tracing) { DbpString("Trace full"); @@ -1759,7 +1762,8 @@ void ReaderIClass(uint8_t arg0) { // with 0xFF:s in block 3 and 4. LED_B_ON(); - //Send back to client, but don't bother if we already sent this + //Send back to client, but don't bother if we already sent this - + // only useful if looping in arm (not try_once && not abort_after_read) if(memcmp(last_csn, card_data, 8) != 0) { // If caller requires that we get Conf, CC, AA, continue until we got it @@ -1767,6 +1771,7 @@ void ReaderIClass(uint8_t arg0) { cmd_send(CMD_ACK,result_status,0,0,card_data,sizeof(card_data)); if(abort_after_read) { LED_A_OFF(); + LED_B_OFF(); return; } //Save that we already sent this.... @@ -1775,8 +1780,13 @@ void ReaderIClass(uint8_t arg0) { } LED_B_OFF(); + userCancelled = BUTTON_PRESS() || usb_poll_validate_length(); + } + if (userCancelled) { + cmd_send(CMD_ACK,0xFF,0,0,card_data, 0); + } else { + cmd_send(CMD_ACK,0,0,0,card_data, 0); } - cmd_send(CMD_ACK,0,0,0,card_data, 0); LED_A_OFF(); } diff --git a/client/cmdhf.c b/client/cmdhf.c index dcfb1bdd..eb2ba9b2 100644 --- a/client/cmdhf.c +++ b/client/cmdhf.c @@ -673,16 +673,17 @@ int CmdHFSearch(const char *Cmd){ PrintAndLog("\nValid iClass Tag (or PicoPass Tag) Found - Quiting Search\n"); return ans; } - ans = HF14BInfo(false); - if (ans) { - PrintAndLog("\nValid ISO14443B Tag Found - Quiting Search\n"); - return ans; - } ans = HF15Reader("", false); if (ans) { PrintAndLog("\nValid ISO15693 Tag Found - Quiting Search\n"); return ans; } + //14b is longest test currently (and rarest chip type) ... put last + ans = HF14BInfo(false); + if (ans) { + PrintAndLog("\nValid ISO14443B Tag Found - Quiting Search\n"); + return ans; + } PrintAndLog("\nno known/supported 13.56 MHz tags found\n"); return 0; } diff --git a/client/cmdhficlass.c b/client/cmdhficlass.c index 05ca39e4..7630b3a1 100644 --- a/client/cmdhficlass.c +++ b/client/cmdhficlass.c @@ -191,8 +191,12 @@ int HFiClassReader(const char *Cmd, bool loop, bool verbose) { uint8_t readStatus = resp.arg[0] & 0xff; uint8_t *data = resp.d.asBytes; - // no tag found - if( readStatus == 0) continue; + // no tag found or button pressed + if( (readStatus == 0 && !loop) || readStatus == 0xFF) { + // abort + if (verbose) PrintAndLog("Quitting..."); + return 0; + } if( readStatus & FLAG_ICLASS_READER_CSN) { PrintAndLog(" CSN: %s",sprint_hex(data,8)); @@ -1708,7 +1712,7 @@ static command_t CommandTable[] = {"loclass", CmdHFiClass_loclass, 1, "[options..] Use loclass to perform bruteforce of reader attack dump"}, {"managekeys", CmdHFiClassManageKeys, 1, "[options..] Manage the keys to use with iClass"}, {"readblk", CmdHFiClass_ReadBlock, 0, "[options..] Authenticate and Read iClass block"}, - {"reader", CmdHFiClassReader, 0, " Read an iClass tag"}, + {"reader", CmdHFiClassReader, 0, " Look for iClass tags until a key or the pm3 button is pressed"}, {"readtagfile", CmdHFiClassReadTagFile, 1, "[options..] Display Content from tagfile"}, {"replay", CmdHFiClassReader_Replay, 0, " Read an iClass tag via Reply Attack"}, {"sim", CmdHFiClassSim, 0, "[options..] Simulate iClass tag"}, From 52244230d318b663c5a389d57713e16139f0e069 Mon Sep 17 00:00:00 2001 From: Henrik Johansson Date: Wed, 5 Jul 2017 20:16:54 +0200 Subject: [PATCH 2/4] hitag2 write (#310) Usage is similar to hitagS write: lf hitag write 24 KEY pagenumber 32bitvalue * added changlelog entry --- CHANGELOG.md | 3 + armsrc/appmain.c | 7 +- armsrc/apps.h | 1 + armsrc/hitag2.c | 1142 ++++++++++++++++++++++++++++--------------- client/cmdlfhitag.c | 5 +- include/hitag2.h | 3 +- 6 files changed, 763 insertions(+), 398 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5df98dd6..045d9b2d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,9 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac ## [3.0.0][2017-06-05] +### Added +- Added lf hitag write 24, the command writes a block to hitag2 tags in crypto mode (henjo) + ### Added - Added hf mf hardnested, an attack working for hardened Mifare cards (EV1, Mifare Plus SL1) where hf mf nested fails - Added experimental testmode write option for t55xx (danger) (marshmellow) diff --git a/armsrc/appmain.c b/armsrc/appmain.c index 30d5ed58..4411a0c5 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -1051,7 +1051,12 @@ void UsbPacketReceived(uint8_t *packet, int len) ReadHitagS((hitag_function)c->arg[0],(hitag_data*)c->d.asBytes); break; case CMD_WR_HITAG_S://writer for Hitag tags args=data to write,page and key or challenge - WritePageHitagS((hitag_function)c->arg[0],(hitag_data*)c->d.asBytes,c->arg[2]); + if ((hitag_function)c->arg[0] < 10) { + WritePageHitagS((hitag_function)c->arg[0],(hitag_data*)c->d.asBytes,c->arg[2]); + } + else if ((hitag_function)c->arg[0] >= 10) { + WriterHitag((hitag_function)c->arg[0],(hitag_data*)c->d.asBytes, c->arg[2]); + } break; #endif diff --git a/armsrc/apps.h b/armsrc/apps.h index 1f140587..aa5b47fb 100644 --- a/armsrc/apps.h +++ b/armsrc/apps.h @@ -182,6 +182,7 @@ void iClass_ReadCheck(uint8_t blockNo, uint8_t keyType); void SnoopHitag(uint32_t type); void SimulateHitagTag(bool tag_mem_supplied, byte_t* data); void ReaderHitag(hitag_function htf, hitag_data* htd); +void WriterHitag(hitag_function htf, hitag_data* htd, int page); //hitagS.h void SimulateHitagSTag(bool tag_mem_supplied, byte_t* data); diff --git a/armsrc/hitag2.c b/armsrc/hitag2.c index dd0211da..7702025b 100644 --- a/armsrc/hitag2.c +++ b/armsrc/hitag2.c @@ -64,6 +64,13 @@ static struct hitag2_tag tag = { }, }; +static enum { + WRITE_STATE_START = 0x0, + WRITE_STATE_PAGENUM_WRITTEN, + WRITE_STATE_PROG +} writestate; + + // ToDo: define a meaningful maximum size for auth_table. The bigger this is, the lower will be the available memory for traces. // Historically it used to be FREE_BUFFER_SIZE, which was 2744. #define AUTH_TABLE_LENGTH 2744 @@ -74,6 +81,7 @@ static size_t auth_table_len = AUTH_TABLE_LENGTH; static byte_t password[4]; static byte_t NrAr[8]; static byte_t key[8]; +static byte_t writedata[4]; static uint64_t cipher_state; /* Following is a modified version of cryptolib.com/ciphers/hitag2/ */ @@ -106,13 +114,13 @@ static const u32 ht2_f5c = 0x7907287B; // 0111 1001 0000 0111 0010 1000 0111 101 static u32 _f20 (const u64 x) { - u32 i5; - + u32 i5; + i5 = ((ht2_f4a >> i4 (x, 1, 2, 4, 5)) & 1)* 1 - + ((ht2_f4b >> i4 (x, 7,11,13,14)) & 1)* 2 - + ((ht2_f4b >> i4 (x,16,20,22,25)) & 1)* 4 - + ((ht2_f4b >> i4 (x,27,28,30,32)) & 1)* 8 - + ((ht2_f4a >> i4 (x,33,42,43,45)) & 1)*16; + + ((ht2_f4b >> i4 (x, 7,11,13,14)) & 1)* 2 + + ((ht2_f4b >> i4 (x,16,20,22,25)) & 1)* 4 + + ((ht2_f4b >> i4 (x,27,28,30,32)) & 1)* 8 + + ((ht2_f4a >> i4 (x,33,42,43,45)) & 1)*16; return (ht2_f5c >> i5) & 1; } @@ -135,10 +143,10 @@ static u64 _hitag2_round (u64 *state) u64 x = *state; x = (x >> 1) + - ((((x >> 0) ^ (x >> 2) ^ (x >> 3) ^ (x >> 6) - ^ (x >> 7) ^ (x >> 8) ^ (x >> 16) ^ (x >> 22) - ^ (x >> 23) ^ (x >> 26) ^ (x >> 30) ^ (x >> 41) - ^ (x >> 42) ^ (x >> 43) ^ (x >> 46) ^ (x >> 47)) & 1) << 47); + ((((x >> 0) ^ (x >> 2) ^ (x >> 3) ^ (x >> 6) + ^ (x >> 7) ^ (x >> 8) ^ (x >> 16) ^ (x >> 22) + ^ (x >> 23) ^ (x >> 26) ^ (x >> 30) ^ (x >> 41) + ^ (x >> 42) ^ (x >> 43) ^ (x >> 46) ^ (x >> 47)) & 1) << 47); *state = x; return _f20 (x); @@ -169,19 +177,19 @@ static int hitag2_init(void) static void hitag2_cipher_reset(struct hitag2_tag *tag, const byte_t *iv) { uint64_t key = ((uint64_t)tag->sectors[2][2]) | - ((uint64_t)tag->sectors[2][3] << 8) | - ((uint64_t)tag->sectors[1][0] << 16) | - ((uint64_t)tag->sectors[1][1] << 24) | - ((uint64_t)tag->sectors[1][2] << 32) | - ((uint64_t)tag->sectors[1][3] << 40); + ((uint64_t)tag->sectors[2][3] << 8) | + ((uint64_t)tag->sectors[1][0] << 16) | + ((uint64_t)tag->sectors[1][1] << 24) | + ((uint64_t)tag->sectors[1][2] << 32) | + ((uint64_t)tag->sectors[1][3] << 40); uint32_t uid = ((uint32_t)tag->sectors[0][0]) | - ((uint32_t)tag->sectors[0][1] << 8) | - ((uint32_t)tag->sectors[0][2] << 16) | - ((uint32_t)tag->sectors[0][3] << 24); + ((uint32_t)tag->sectors[0][1] << 8) | + ((uint32_t)tag->sectors[0][2] << 16) | + ((uint32_t)tag->sectors[0][3] << 24); uint32_t iv_ = (((uint32_t)(iv[0]))) | - (((uint32_t)(iv[1])) << 8) | - (((uint32_t)(iv[2])) << 16) | - (((uint32_t)(iv[3])) << 24); + (((uint32_t)(iv[1])) << 8) | + (((uint32_t)(iv[2])) << 16) | + (((uint32_t)(iv[3])) << 24); tag->cs = _hitag2_init(rev64(key), rev32(uid), rev32(iv_)); } @@ -222,6 +230,7 @@ static int hitag2_cipher_transcrypt(uint64_t* cs, byte_t *data, unsigned int byt #define HITAG_T_WAIT_1 200 /* T_wresp should be 199..206 */ #define HITAG_T_WAIT_2 90 /* T_wresp should be 199..206 */ #define HITAG_T_WAIT_MAX 300 /* bit more than HITAG_T_WAIT_1 + HITAG_T_WAIT_2 */ +#define HITAG_T_PROG 614 #define HITAG_T_TAG_ONE_HALF_PERIOD 10 #define HITAG_T_TAG_TWO_HALF_PERIOD 25 @@ -293,105 +302,105 @@ static void hitag2_handle_reader_command(byte_t* rx, const size_t rxlen, byte_t* // Try to find out which command was send by selecting on length (in bits) switch (rxlen) { // Received 11000 from the reader, request for UID, send UID - case 05: { - // Always send over the air in the clear plaintext mode - if(rx_air[0] != 0xC0) { - // Unknown frame ? - return; - } - *txlen = 32; - memcpy(tx,tag.sectors[0],4); - tag.crypto_active = 0; + case 05: { + // Always send over the air in the clear plaintext mode + if(rx_air[0] != 0xC0) { + // Unknown frame ? + return; } + *txlen = 32; + memcpy(tx,tag.sectors[0],4); + tag.crypto_active = 0; + } break; // Read/Write command: ..xx x..y yy with yyy == ~xxx, xxx is sector number - case 10: { - unsigned int sector = (~( ((rx[0]<<2)&0x04) | ((rx[1]>>6)&0x03) ) & 0x07); - // Verify complement of sector index - if(sector != ((rx[0]>>3)&0x07)) { - //DbpString("Transmission error (read/write)"); - return; - } - - switch (rx[0] & 0xC6) { - // Read command: 11xx x00y - case 0xC0: - memcpy(tx,tag.sectors[sector],4); - *txlen = 32; - break; - - // Inverted Read command: 01xx x10y - case 0x44: - for (size_t i=0; i<4; i++) { - tx[i] = tag.sectors[sector][i] ^ 0xff; - } - *txlen = 32; - break; - - // Write command: 10xx x01y - case 0x82: - // Prepare write, acknowledge by repeating command - memcpy(tx,rx,nbytes(rxlen)); - *txlen = rxlen; - tag.active_sector = sector; - tag.state=TAG_STATE_WRITING; - break; - - // Unknown command - default: - Dbprintf("Unknown command: %02x %02x",rx[0],rx[1]); - return; - break; - } + case 10: { + unsigned int sector = (~( ((rx[0]<<2)&0x04) | ((rx[1]>>6)&0x03) ) & 0x07); + // Verify complement of sector index + if(sector != ((rx[0]>>3)&0x07)) { + //DbpString("Transmission error (read/write)"); + return; } + + switch (rx[0] & 0xC6) { + // Read command: 11xx x00y + case 0xC0: + memcpy(tx,tag.sectors[sector],4); + *txlen = 32; + break; + + // Inverted Read command: 01xx x10y + case 0x44: + for (size_t i=0; i<4; i++) { + tx[i] = tag.sectors[sector][i] ^ 0xff; + } + *txlen = 32; + break; + + // Write command: 10xx x01y + case 0x82: + // Prepare write, acknowledge by repeating command + memcpy(tx,rx,nbytes(rxlen)); + *txlen = rxlen; + tag.active_sector = sector; + tag.state=TAG_STATE_WRITING; + break; + + // Unknown command + default: + Dbprintf("Unknown command: %02x %02x",rx[0],rx[1]); + return; + break; + } + } break; // Writing data or Reader password - case 32: { - if(tag.state == TAG_STATE_WRITING) { - // These are the sector contents to be written. We don't have to do anything else. - memcpy(tag.sectors[tag.active_sector],rx,nbytes(rxlen)); - tag.state=TAG_STATE_RESET; + case 32: { + if(tag.state == TAG_STATE_WRITING) { + // These are the sector contents to be written. We don't have to do anything else. + memcpy(tag.sectors[tag.active_sector],rx,nbytes(rxlen)); + tag.state=TAG_STATE_RESET; + return; + } else { + // Received RWD password, respond with configuration and our password + if(memcmp(rx,tag.sectors[1],4) != 0) { + DbpString("Reader password is wrong"); return; - } else { - // Received RWD password, respond with configuration and our password - if(memcmp(rx,tag.sectors[1],4) != 0) { - DbpString("Reader password is wrong"); - return; - } - *txlen = 32; - memcpy(tx,tag.sectors[3],4); } + *txlen = 32; + memcpy(tx,tag.sectors[3],4); } + } break; // Received RWD authentication challenge and respnse - case 64: { - // Store the authentication attempt - if (auth_table_len < (AUTH_TABLE_LENGTH-8)) { - memcpy(auth_table+auth_table_len,rx,8); - auth_table_len += 8; - } - - // Reset the cipher state - hitag2_cipher_reset(&tag,rx); - // Check if the authentication was correct - if(!hitag2_cipher_authenticate(&(tag.cs),rx+4)) { - // The reader failed to authenticate, do nothing - Dbprintf("auth: %02x%02x%02x%02x%02x%02x%02x%02x Failed!",rx[0],rx[1],rx[2],rx[3],rx[4],rx[5],rx[6],rx[7]); - return; - } - // Succesful, but commented out reporting back to the Host, this may delay to much. - // Dbprintf("auth: %02x%02x%02x%02x%02x%02x%02x%02x OK!",rx[0],rx[1],rx[2],rx[3],rx[4],rx[5],rx[6],rx[7]); - - // Activate encryption algorithm for all further communication - tag.crypto_active = 1; - - // Use the tag password as response - memcpy(tx,tag.sectors[3],4); - *txlen = 32; + case 64: { + // Store the authentication attempt + if (auth_table_len < (AUTH_TABLE_LENGTH-8)) { + memcpy(auth_table+auth_table_len,rx,8); + auth_table_len += 8; } + + // Reset the cipher state + hitag2_cipher_reset(&tag,rx); + // Check if the authentication was correct + if(!hitag2_cipher_authenticate(&(tag.cs),rx+4)) { + // The reader failed to authenticate, do nothing + Dbprintf("auth: %02x%02x%02x%02x%02x%02x%02x%02x Failed!",rx[0],rx[1],rx[2],rx[3],rx[4],rx[5],rx[6],rx[7]); + return; + } + // Succesful, but commented out reporting back to the Host, this may delay to much. + // Dbprintf("auth: %02x%02x%02x%02x%02x%02x%02x%02x OK!",rx[0],rx[1],rx[2],rx[3],rx[4],rx[5],rx[6],rx[7]); + + // Activate encryption algorithm for all further communication + tag.crypto_active = 1; + + // Use the tag password as response + memcpy(tx,tag.sectors[3],4); + *txlen = 32; + } break; } @@ -459,25 +468,25 @@ static bool hitag2_password(byte_t* rx, const size_t rxlen, byte_t* tx, size_t* // Try to find out which command was send by selecting on length (in bits) switch (rxlen) { // No answer, try to resurrect - case 0: { - // Stop if there is no answer (after sending password) - if (bPwd) { - DbpString("Password failed!"); - return false; - } - *txlen = 5; - memcpy(tx,"\xc0",nbytes(*txlen)); - } break; + case 0: { + // Stop if there is no answer (after sending password) + if (bPwd) { + DbpString("Password failed!"); + return false; + } + *txlen = 5; + memcpy(tx,"\xc0",nbytes(*txlen)); + } break; // Received UID, tag password - case 32: { - if (!bPwd) { - *txlen = 32; - memcpy(tx,password,4); - bPwd = true; - memcpy(tag.sectors[blocknr],rx,4); - blocknr++; - } else { + case 32: { + if (!bPwd) { + *txlen = 32; + memcpy(tx,password,4); + bPwd = true; + memcpy(tag.sectors[blocknr],rx,4); + blocknr++; + } else { if(blocknr == 1){ //store password in block1, the TAG answers with Block3, but we need the password in memory @@ -488,112 +497,173 @@ static bool hitag2_password(byte_t* rx, const size_t rxlen, byte_t* tx, size_t* blocknr++; if (blocknr > 7) { - DbpString("Read succesful!"); - bSuccessful = true; - return false; + DbpString("Read succesful!"); + bSuccessful = true; + return false; } *txlen = 10; tx[0] = 0xc0 | (blocknr << 3) | ((blocknr^7) >> 2); tx[1] = ((blocknr^7) << 6); - } - } break; + } + } break; // Unexpected response - default: { - Dbprintf("Uknown frame length: %d",rxlen); - return false; - } break; + default: { + Dbprintf("Uknown frame length: %d",rxlen); + return false; + } break; } return true; } -static bool hitag2_crypto(byte_t* rx, const size_t rxlen, byte_t* tx, size_t* txlen) { +static bool hitag2_write_page(byte_t* rx, const size_t rxlen, byte_t* tx, size_t* txlen) +{ + switch (writestate) { + case WRITE_STATE_START: + *txlen = 10; + tx[0] = 0x82 | (blocknr << 3) | ((blocknr^7) >> 2); + tx[1] = ((blocknr^7) << 6); + writestate = WRITE_STATE_PAGENUM_WRITTEN; + break; + case WRITE_STATE_PAGENUM_WRITTEN: + // Check if page number was received correctly + if ((rxlen == 10) && + (rx[0] == (0x82 | (blocknr << 3) | ((blocknr^7) >> 2))) && + (rx[1] == (((blocknr & 0x3) ^ 0x3) << 6))) { + *txlen = 32; + memset(tx, 0, HITAG_FRAME_LEN); + memcpy(tx, writedata, 4); + writestate = WRITE_STATE_PROG; + } else { + Dbprintf("hitag2_write_page: Page number was not received correctly: rxlen=%d rx=%02x%02x%02x%02x", + rxlen, rx[0], rx[1], rx[2], rx[3]); + bSuccessful = false; + return false; + } + break; + case WRITE_STATE_PROG: + if (rxlen == 0) { + bSuccessful = true; + } else { + bSuccessful = false; + Dbprintf("hitag2_write_page: unexpected rx data (%d) after page write", rxlen); + } + return false; + default: + DbpString("hitag2_write_page: Unknown state %d"); + bSuccessful = false; + return false; + } + + return true; +} + +static bool hitag2_crypto(byte_t* rx, const size_t rxlen, byte_t* tx, size_t* txlen, bool write) { // Reset the transmission frame length *txlen = 0; - if(bCrypto) { + if(bCrypto) { hitag2_cipher_transcrypt(&cipher_state,rx,rxlen/8,rxlen%8); + } + if (bCrypto && !bAuthenticating && write) { + if (!hitag2_write_page(rx, rxlen, tx, txlen)) { + return false; + } + } + else + { + // Try to find out which command was send by selecting on length (in bits) switch (rxlen) { - // No answer, try to resurrect - case 0: { - // Stop if there is no answer while we are in crypto mode (after sending NrAr) - if (bCrypto) { - // Failed during authentication - if (bAuthenticating) { - DbpString("Authentication failed!"); - return false; - } else { - // Failed reading a block, could be (read/write) locked, skip block and re-authenticate - if (blocknr == 1) { - // Write the low part of the key in memory - memcpy(tag.sectors[1],key+2,4); - } else if (blocknr == 2) { - // Write the high part of the key in memory - tag.sectors[2][0] = 0x00; - tag.sectors[2][1] = 0x00; - tag.sectors[2][2] = key[0]; - tag.sectors[2][3] = key[1]; - } else { - // Just put zero's in the memory (of the unreadable block) - memset(tag.sectors[blocknr],0x00,4); - } - blocknr++; - bCrypto = false; - } + // No answer, try to resurrect + case 0: + { + // Stop if there is no answer while we are in crypto mode (after sending NrAr) + if (bCrypto) { + // Failed during authentication + if (bAuthenticating) { + DbpString("Authentication failed!"); + return false; } else { - *txlen = 5; - memcpy(tx,"\xc0",nbytes(*txlen)); - } - } break; - - // Received UID, crypto tag answer - case 32: { - if (!bCrypto) { - uint64_t ui64key = key[0] | ((uint64_t)key[1]) << 8 | ((uint64_t)key[2]) << 16 | ((uint64_t)key[3]) << 24 | ((uint64_t)key[4]) << 32 | ((uint64_t)key[5]) << 40; - uint32_t ui32uid = rx[0] | ((uint32_t)rx[1]) << 8 | ((uint32_t)rx[2]) << 16 | ((uint32_t)rx[3]) << 24; - cipher_state = _hitag2_init(rev64(ui64key), rev32(ui32uid), 0); - memset(tx,0x00,4); - memset(tx+4,0xff,4); - hitag2_cipher_transcrypt(&cipher_state,tx+4,4,0); - *txlen = 64; - bCrypto = true; - bAuthenticating = true; - } else { - // Check if we received answer tag (at) - if (bAuthenticating) { - bAuthenticating = false; - } else { - // Store the received block - memcpy(tag.sectors[blocknr],rx,4); - blocknr++; - } - if (blocknr > 7) { - DbpString("Read succesful!"); - bSuccessful = true; - return false; - } - *txlen = 10; - tx[0] = 0xc0 | (blocknr << 3) | ((blocknr^7) >> 2); - tx[1] = ((blocknr^7) << 6); + // Failed reading a block, could be (read/write) locked, skip block and re-authenticate + if (blocknr == 1) { + // Write the low part of the key in memory + memcpy(tag.sectors[1],key+2,4); + } else if (blocknr == 2) { + // Write the high part of the key in memory + tag.sectors[2][0] = 0x00; + tag.sectors[2][1] = 0x00; + tag.sectors[2][2] = key[0]; + tag.sectors[2][3] = key[1]; + } else { + // Just put zero's in the memory (of the unreadable block) + memset(tag.sectors[blocknr],0x00,4); + } + blocknr++; + bCrypto = false; } - } break; - - // Unexpected response - default: { - Dbprintf("Uknown frame length: %d",rxlen); - return false; - } break; + } else { + *txlen = 5; + memcpy(tx,"\xc0",nbytes(*txlen)); + } + break; + } + // Received UID, crypto tag answer + case 32: { + if (!bCrypto) { + uint64_t ui64key = key[0] | ((uint64_t)key[1]) << 8 | ((uint64_t)key[2]) << 16 | ((uint64_t)key[3]) << 24 | ((uint64_t)key[4]) << 32 | ((uint64_t)key[5]) << 40; + uint32_t ui32uid = rx[0] | ((uint32_t)rx[1]) << 8 | ((uint32_t)rx[2]) << 16 | ((uint32_t)rx[3]) << 24; + Dbprintf("hitag2_crypto: key=0x%x%x uid=0x%x", (uint32_t) ((rev64(ui64key)) >> 32), (uint32_t) ((rev64(ui64key)) & 0xffffffff), rev32(ui32uid)); + cipher_state = _hitag2_init(rev64(ui64key), rev32(ui32uid), 0); + memset(tx,0x00,4); + memset(tx+4,0xff,4); + hitag2_cipher_transcrypt(&cipher_state, tx+4, 4, 0); + *txlen = 64; + bCrypto = true; + bAuthenticating = true; + } else { + // Check if we received answer tag (at) + if (bAuthenticating) { + bAuthenticating = false; + if (write) { + if (!hitag2_write_page(rx, rxlen, tx, txlen)) { + return false; + } + break; + } + } else { + // Store the received block + memcpy(tag.sectors[blocknr],rx,4); + blocknr++; + } + + if (blocknr > 7) { + DbpString("Read succesful!"); + bSuccessful = true; + return false; + } else { + *txlen = 10; + tx[0] = 0xc0 | (blocknr << 3) | ((blocknr^7) >> 2); + tx[1] = ((blocknr^7) << 6); + } + } + } break; + + // Unexpected response + default: { + Dbprintf("Uknown frame length: %d",rxlen); + return false; + } break; + } } - - if(bCrypto) { - // We have to return now to avoid double encryption - if (!bAuthenticating) { - hitag2_cipher_transcrypt(&cipher_state,tx,*txlen/8,*txlen%8); - } + if(bCrypto) { + // We have to return now to avoid double encryption + if (!bAuthenticating) { + hitag2_cipher_transcrypt(&cipher_state,tx,*txlen/8,*txlen%8); + } } return true; @@ -607,34 +677,34 @@ static bool hitag2_authenticate(byte_t* rx, const size_t rxlen, byte_t* tx, size // Try to find out which command was send by selecting on length (in bits) switch (rxlen) { // No answer, try to resurrect - case 0: { - // Stop if there is no answer while we are in crypto mode (after sending NrAr) - if (bCrypto) { - DbpString("Authentication failed!"); - return false; - } - *txlen = 5; - memcpy(tx,"\xc0",nbytes(*txlen)); - } break; + case 0: { + // Stop if there is no answer while we are in crypto mode (after sending NrAr) + if (bCrypto) { + DbpString("Authentication failed!"); + return false; + } + *txlen = 5; + memcpy(tx,"\xc0",nbytes(*txlen)); + } break; // Received UID, crypto tag answer - case 32: { - if (!bCrypto) { - *txlen = 64; - memcpy(tx,NrAr,8); - bCrypto = true; - } else { - DbpString("Authentication succesful!"); - // We are done... for now - return false; - } - } break; + case 32: { + if (!bCrypto) { + *txlen = 64; + memcpy(tx,NrAr,8); + bCrypto = true; + } else { + DbpString("Authentication succesful!"); + // We are done... for now + return false; + } + } break; // Unexpected response - default: { - Dbprintf("Uknown frame length: %d",rxlen); - return false; - } break; + default: { + Dbprintf("Uknown frame length: %d",rxlen); + return false; + } break; } return true; @@ -648,50 +718,50 @@ static bool hitag2_test_auth_attempts(byte_t* rx, const size_t rxlen, byte_t* tx // Try to find out which command was send by selecting on length (in bits) switch (rxlen) { - // No answer, try to resurrect - case 0: { - // Stop if there is no answer while we are in crypto mode (after sending NrAr) - if (bCrypto) { - Dbprintf("auth: %02x%02x%02x%02x%02x%02x%02x%02x Failed, removed entry!",NrAr[0],NrAr[1],NrAr[2],NrAr[3],NrAr[4],NrAr[5],NrAr[6],NrAr[7]); + // No answer, try to resurrect + case 0: { + // Stop if there is no answer while we are in crypto mode (after sending NrAr) + if (bCrypto) { + Dbprintf("auth: %02x%02x%02x%02x%02x%02x%02x%02x Failed, removed entry!",NrAr[0],NrAr[1],NrAr[2],NrAr[3],NrAr[4],NrAr[5],NrAr[6],NrAr[7]); - // Removing failed entry from authentiations table - memcpy(auth_table+auth_table_pos,auth_table+auth_table_pos+8,8); - auth_table_len -= 8; + // Removing failed entry from authentiations table + memcpy(auth_table+auth_table_pos,auth_table+auth_table_pos+8,8); + auth_table_len -= 8; - // Return if we reached the end of the authentications table - bCrypto = false; - if (auth_table_pos == auth_table_len) { - return false; - } - - // Copy the next authentication attempt in row (at the same position, b/c we removed last failed entry) - memcpy(NrAr,auth_table+auth_table_pos,8); + // Return if we reached the end of the authentications table + bCrypto = false; + if (auth_table_pos == auth_table_len) { + return false; } - *txlen = 5; - memcpy(tx,"\xc0",nbytes(*txlen)); - } break; + + // Copy the next authentication attempt in row (at the same position, b/c we removed last failed entry) + memcpy(NrAr,auth_table+auth_table_pos,8); + } + *txlen = 5; + memcpy(tx,"\xc0",nbytes(*txlen)); + } break; - // Received UID, crypto tag answer, or read block response - case 32: { - if (!bCrypto) { - *txlen = 64; - memcpy(tx,NrAr,8); - bCrypto = true; - } else { - Dbprintf("auth: %02x%02x%02x%02x%02x%02x%02x%02x OK",NrAr[0],NrAr[1],NrAr[2],NrAr[3],NrAr[4],NrAr[5],NrAr[6],NrAr[7]); - bCrypto = false; - if ((auth_table_pos+8) == auth_table_len) { - return false; - } - auth_table_pos += 8; - memcpy(NrAr,auth_table+auth_table_pos,8); + // Received UID, crypto tag answer, or read block response + case 32: { + if (!bCrypto) { + *txlen = 64; + memcpy(tx,NrAr,8); + bCrypto = true; + } else { + Dbprintf("auth: %02x%02x%02x%02x%02x%02x%02x%02x OK",NrAr[0],NrAr[1],NrAr[2],NrAr[3],NrAr[4],NrAr[5],NrAr[6],NrAr[7]); + bCrypto = false; + if ((auth_table_pos+8) == auth_table_len) { + return false; } - } break; + auth_table_pos += 8; + memcpy(NrAr,auth_table+auth_table_pos,8); + } + } break; - default: { - Dbprintf("Uknown frame length: %d",rxlen); - return false; - } break; + default: { + Dbprintf("Uknown frame length: %d",rxlen); + return false; + } break; } return true; @@ -704,32 +774,32 @@ static bool hitag2_read_uid(byte_t* rx, const size_t rxlen, byte_t* tx, size_t* // Try to find out which command was send by selecting on length (in bits) switch (rxlen) { // No answer, try to resurrect - case 0: { - // Just starting or if there is no answer - *txlen = 5; - memcpy(tx,"\xc0",nbytes(*txlen)); - } break; + case 0: { + // Just starting or if there is no answer + *txlen = 5; + memcpy(tx,"\xc0",nbytes(*txlen)); + } break; // Received UID - case 32: { - // Check if we received answer tag (at) - if (bAuthenticating) { - bAuthenticating = false; - } else { - // Store the received block - memcpy(tag.sectors[blocknr],rx,4); - blocknr++; - } - if (blocknr > 0) { - //DbpString("Read successful!"); - bSuccessful = true; - return false; - } - } break; - // Unexpected response - default: { - Dbprintf("Uknown frame length: %d",rxlen); + case 32: { + // Check if we received answer tag (at) + if (bAuthenticating) { + bAuthenticating = false; + } else { + // Store the received block + memcpy(tag.sectors[blocknr],rx,4); + blocknr++; + } + if (blocknr > 0) { + //DbpString("Read successful!"); + bSuccessful = true; return false; - } break; + } + } break; + // Unexpected response + default: { + Dbprintf("Uknown frame length: %d",rxlen); + return false; + } break; } return true; } @@ -756,7 +826,7 @@ void SnoopHitag(uint32_t type) { auth_table_pos = 0; BigBuf_free(); - auth_table = (byte_t *)BigBuf_malloc(AUTH_TABLE_LENGTH); + auth_table = (byte_t *)BigBuf_malloc(AUTH_TABLE_LENGTH); memset(auth_table, 0x00, AUTH_TABLE_LENGTH); DbpString("Starting Hitag2 snoop"); @@ -792,7 +862,6 @@ void SnoopHitag(uint32_t type) { AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; // Reset the received frame, frame count and timing info - memset(rx,0x00,sizeof(rx)); frame_count = 0; response = 0; overflow = 0; @@ -826,7 +895,7 @@ void SnoopHitag(uint32_t type) { // Only handle if reader frame and rising edge, or tag frame and falling edge if (reader_frame != rising_edge) { - overflow += ra; + overflow += ra; continue; } @@ -935,14 +1004,14 @@ void SnoopHitag(uint32_t type) { // Reset the timer to restart while-loop that receives frames AT91C_BASE_TC1->TC_CCR = AT91C_TC_SWTRG; } - LED_A_ON(); + LED_A_ON(); LED_B_OFF(); LED_C_OFF(); LED_D_OFF(); AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; - AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; + AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - LED_A_OFF(); + LED_A_OFF(); // Dbprintf("frame received: %d",frame_count); // Dbprintf("Authentication Attempts: %d",(auth_table_len/8)); @@ -968,9 +1037,9 @@ void SimulateHitagTag(bool tag_mem_supplied, byte_t* data) { auth_table_len = 0; auth_table_pos = 0; - byte_t* auth_table; + byte_t* auth_table; BigBuf_free(); - auth_table = (byte_t *)BigBuf_malloc(AUTH_TABLE_LENGTH); + auth_table = (byte_t *)BigBuf_malloc(AUTH_TABLE_LENGTH); memset(auth_table, 0x00, AUTH_TABLE_LENGTH); DbpString("Starting Hitag2 simulation"); @@ -1012,7 +1081,7 @@ void SimulateHitagTag(bool tag_mem_supplied, byte_t* data) { AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_TC1); AT91C_BASE_PIOA->PIO_BSR = GPIO_SSC_FRAME; - // Disable timer during configuration + // Disable timer during configuration AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; // Capture mode, default timer source = MCK/2 (TIMER_CLOCK1), TIOA is external trigger, @@ -1163,52 +1232,53 @@ void ReaderHitag(hitag_function htf, hitag_data* htd) { // Check configuration switch(htf) { - case RHT2F_PASSWORD: { - Dbprintf("List identifier in password mode"); - memcpy(password,htd->pwd.password,4); - blocknr = 0; - bQuitTraceFull = false; - bQuiet = false; - bPwd = false; - } break; - case RHT2F_AUTHENTICATE: { - DbpString("Authenticating using nr,ar pair:"); - memcpy(NrAr,htd->auth.NrAr,8); - Dbhexdump(8,NrAr,false); - bQuiet = false; - bCrypto = false; - bAuthenticating = false; - bQuitTraceFull = true; - } break; - case RHT2F_CRYPTO: { - DbpString("Authenticating using key:"); - memcpy(key,htd->crypto.key,6); //HACK; 4 or 6?? I read both in the code. - Dbhexdump(6,key,false); - blocknr = 0; - bQuiet = false; - bCrypto = false; - bAuthenticating = false; - bQuitTraceFull = true; - } break; - case RHT2F_TEST_AUTH_ATTEMPTS: { - Dbprintf("Testing %d authentication attempts",(auth_table_len/8)); - auth_table_pos = 0; - memcpy(NrAr, auth_table, 8); - bQuitTraceFull = false; - bQuiet = false; - bCrypto = false; - } break; - case RHT2F_UID_ONLY: { - blocknr = 0; - bQuiet = false; - bCrypto = false; - bAuthenticating = false; - bQuitTraceFull = true; - } break; - default: { - Dbprintf("Error, unknown function: %d",htf); - return; - } break; + case RHT2F_PASSWORD: { + Dbprintf("List identifier in password mode"); + memcpy(password,htd->pwd.password,4); + blocknr = 0; + bQuitTraceFull = false; + bQuiet = false; + bPwd = false; + } break; + case RHT2F_AUTHENTICATE: { + DbpString("Authenticating using nr,ar pair:"); + memcpy(NrAr,htd->auth.NrAr,8); + Dbhexdump(8,NrAr,false); + bQuiet = false; + bCrypto = false; + bAuthenticating = false; + bQuitTraceFull = true; + } break; + case RHT2F_CRYPTO: + { + DbpString("Authenticating using key:"); + memcpy(key,htd->crypto.key,6); //HACK; 4 or 6?? I read both in the code. + Dbhexdump(6,key,false); + blocknr = 0; + bQuiet = false; + bCrypto = false; + bAuthenticating = false; + bQuitTraceFull = true; + } break; + case RHT2F_TEST_AUTH_ATTEMPTS: { + Dbprintf("Testing %d authentication attempts",(auth_table_len/8)); + auth_table_pos = 0; + memcpy(NrAr, auth_table, 8); + bQuitTraceFull = false; + bQuiet = false; + bCrypto = false; + } break; + case RHT2F_UID_ONLY: { + blocknr = 0; + bQuiet = false; + bCrypto = false; + bAuthenticating = false; + bQuitTraceFull = true; + } break; + default: { + Dbprintf("Error, unknown function: %d",htf); + return; + } break; } LED_D_ON(); @@ -1239,7 +1309,7 @@ void ReaderHitag(hitag_function htf, hitag_data* htd) { AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_TC1); AT91C_BASE_PIOA->PIO_BSR = GPIO_SSC_FRAME; - // Disable timer during configuration + // Disable timer during configuration AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; // Capture mode, defaul timer source = MCK/2 (TIMER_CLOCK1), TIOA is external trigger, @@ -1256,26 +1326,26 @@ void ReaderHitag(hitag_function htf, hitag_data* htd) { lastbit = 1; bStop = false; - // Tag specific configuration settings (sof, timings, etc.) - if (htf < 10){ - // hitagS settings - reset_sof = 1; - t_wait = 200; - //DbpString("Configured for hitagS reader"); - } else if (htf < 20) { - // hitag1 settings - reset_sof = 1; - t_wait = 200; - //DbpString("Configured for hitag1 reader"); - } else if (htf < 30) { - // hitag2 settings - reset_sof = 4; - t_wait = HITAG_T_WAIT_2; - //DbpString("Configured for hitag2 reader"); + // Tag specific configuration settings (sof, timings, etc.) + if (htf < 10){ + // hitagS settings + reset_sof = 1; + t_wait = 200; + //DbpString("Configured for hitagS reader"); + } else if (htf < 20) { + // hitag1 settings + reset_sof = 1; + t_wait = 200; + //DbpString("Configured for hitag1 reader"); + } else if (htf < 30) { + // hitag2 settings + reset_sof = 4; + t_wait = HITAG_T_WAIT_2; + //DbpString("Configured for hitag2 reader"); } else { - Dbprintf("Error, unknown hitag reader type: %d",htf); - return; - } + Dbprintf("Error, unknown hitag reader type: %d",htf); + return; + } uint8_t attempt_count=0; while(!bStop && !BUTTON_PRESS()) { // Watchdog hit @@ -1299,27 +1369,27 @@ void ReaderHitag(hitag_function htf, hitag_data* htd) { // By default reset the transmission buffer tx = txbuf; switch(htf) { - case RHT2F_PASSWORD: { - bStop = !hitag2_password(rx,rxlen,tx,&txlen); - } break; - case RHT2F_AUTHENTICATE: { - bStop = !hitag2_authenticate(rx,rxlen,tx,&txlen); - } break; - case RHT2F_CRYPTO: { - bStop = !hitag2_crypto(rx,rxlen,tx,&txlen); - } break; - case RHT2F_TEST_AUTH_ATTEMPTS: { - bStop = !hitag2_test_auth_attempts(rx,rxlen,tx,&txlen); - } break; - case RHT2F_UID_ONLY: { - bStop = !hitag2_read_uid(rx, rxlen, tx, &txlen); - attempt_count++; //attempt 3 times to get uid then quit - if (!bStop && attempt_count == 3) bStop = true; - } break; - default: { - Dbprintf("Error, unknown function: %d",htf); - return; - } break; + case RHT2F_PASSWORD: { + bStop = !hitag2_password(rx,rxlen,tx,&txlen); + } break; + case RHT2F_AUTHENTICATE: { + bStop = !hitag2_authenticate(rx,rxlen,tx,&txlen); + } break; + case RHT2F_CRYPTO: { + bStop = !hitag2_crypto(rx,rxlen,tx,&txlen, false); + } break; + case RHT2F_TEST_AUTH_ATTEMPTS: { + bStop = !hitag2_test_auth_attempts(rx,rxlen,tx,&txlen); + } break; + case RHT2F_UID_ONLY: { + bStop = !hitag2_read_uid(rx, rxlen, tx, &txlen); + attempt_count++; //attempt 3 times to get uid then quit + if (!bStop && attempt_count == 3) bStop = true; + } break; + default: { + Dbprintf("Error, unknown function: %d",htf); + return; + } break; } // Send and store the reader command @@ -1333,12 +1403,12 @@ void ReaderHitag(hitag_function htf, hitag_data* htd) { // All timer values are in terms of T0 units while(AT91C_BASE_TC0->TC_CV < T0*(t_wait+(HITAG_T_TAG_HALF_PERIOD*lastbit))); - //Dbprintf("DEBUG: Sending reader frame"); + //Dbprintf("DEBUG: Sending reader frame"); // Transmit the reader frame hitag_reader_send_frame(tx,txlen); - // Enable and reset external trigger in timer for capturing future frames + // Enable and reset external trigger in timer for capturing future frames AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; // Add transmitted frame to total count @@ -1355,7 +1425,7 @@ void ReaderHitag(hitag_function htf, hitag_data* htd) { } } } - + // Reset values for receiving frames memset(rx,0x00,sizeof(rx)); rxlen = 0; @@ -1363,7 +1433,7 @@ void ReaderHitag(hitag_function htf, hitag_data* htd) { bSkip = true; tag_sof = reset_sof; response = 0; - //Dbprintf("DEBUG: Waiting to receive frame"); + //Dbprintf("DEBUG: Waiting to receive frame"); uint32_t errorCount = 0; // Receive frame, watch for at most T0*EOF periods @@ -1377,7 +1447,7 @@ void ReaderHitag(hitag_function htf, hitag_data* htd) { AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG; LED_B_ON(); - + // Capture tag frame (manchester decoding using only falling edges) if(ra >= HITAG_T_EOF) { if (rxlen != 0) { @@ -1429,8 +1499,8 @@ void ReaderHitag(hitag_function htf, hitag_data* htd) { rxlen++; } } else { - //Dbprintf("DEBUG: Wierd2"); - errorCount++; + //Dbprintf("DEBUG: Wierd2"); + errorCount++; // Ignore wierd value, is to small to mean anything } } @@ -1453,3 +1523,285 @@ void ReaderHitag(hitag_function htf, hitag_data* htd) { //DbpString("All done"); cmd_send(CMD_ACK,bSuccessful,0,0,(byte_t*)tag.sectors,48); } + +void WriterHitag(hitag_function htf, hitag_data* htd, int page) { + int frame_count; + int response; + byte_t rx[HITAG_FRAME_LEN]; + size_t rxlen=0; + byte_t txbuf[HITAG_FRAME_LEN]; + byte_t* tx = txbuf; + size_t txlen=0; + int lastbit; + bool bSkip; + int reset_sof; + int tag_sof; + int t_wait = HITAG_T_WAIT_MAX; + bool bStop; + bool bQuitTraceFull = false; + + FpgaDownloadAndGo(FPGA_BITSTREAM_LF); + // Reset the return status + bSuccessful = false; + + // Clean up trace and prepare it for storing frames + set_tracing(TRUE); + clear_trace(); + + //DbpString("Starting Hitag reader family"); + + // Check configuration + switch(htf) { + case WHT2F_CRYPTO: + { + DbpString("Authenticating using key:"); + memcpy(key,htd->crypto.key,6); //HACK; 4 or 6?? I read both in the code. + memcpy(writedata, htd->crypto.data, 4); + Dbhexdump(6,key,false); + blocknr = page; + bQuiet = false; + bCrypto = false; + bAuthenticating = false; + bQuitTraceFull = true; + writestate = WRITE_STATE_START; + } break; + default: { + Dbprintf("Error, unknown function: %d",htf); + return; + } break; + } + + LED_D_ON(); + hitag2_init(); + + // Configure output and enable pin that is connected to the FPGA (for modulating) + AT91C_BASE_PIOA->PIO_OER = GPIO_SSC_DOUT; + AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DOUT; + + // Set fpga in edge detect with reader field, we can modulate as reader now + FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_EDGE_DETECT | FPGA_LF_EDGE_DETECT_READER_FIELD); + + // Set Frequency divisor which will drive the FPGA and analog mux selection + FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 95); //125Khz + SetAdcMuxFor(GPIO_MUXSEL_LOPKD); + RELAY_OFF(); + + // Disable modulation at default, which means enable the field + LOW(GPIO_SSC_DOUT); + + // Give it a bit of time for the resonant antenna to settle. + SpinDelay(30); + + // Enable Peripheral Clock for TIMER_CLOCK0, used to measure exact timing before answering + AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_TC0); + + // Enable Peripheral Clock for TIMER_CLOCK1, used to capture edges of the tag frames + AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_TC1); + AT91C_BASE_PIOA->PIO_BSR = GPIO_SSC_FRAME; + + // Disable timer during configuration + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; + + // Capture mode, defaul timer source = MCK/2 (TIMER_CLOCK1), TIOA is external trigger, + // external trigger rising edge, load RA on falling edge of TIOA. + AT91C_BASE_TC1->TC_CMR = AT91C_TC_CLKS_TIMER_DIV1_CLOCK | AT91C_TC_ETRGEDG_FALLING | AT91C_TC_ABETRG | AT91C_TC_LDRA_FALLING; + + // Enable and reset counters + AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; + + // Reset the received frame, frame count and timing info + frame_count = 0; + response = 0; + lastbit = 1; + bStop = false; + + // Tag specific configuration settings (sof, timings, etc.) + if (htf < 10){ + // hitagS settings + reset_sof = 1; + t_wait = 200; + //DbpString("Configured for hitagS reader"); + } else if (htf < 20) { + // hitag1 settings + reset_sof = 1; + t_wait = 200; + //DbpString("Configured for hitag1 reader"); + } else if (htf < 30) { + // hitag2 settings + reset_sof = 4; + t_wait = HITAG_T_WAIT_2; + //DbpString("Configured for hitag2 reader"); + } else { + Dbprintf("Error, unknown hitag reader type: %d",htf); + return; + } + while(!bStop && !BUTTON_PRESS()) { + // Watchdog hit + WDT_HIT(); + + // Check if frame was captured and store it + if(rxlen > 0) { + frame_count++; + if (!bQuiet) { + if (!LogTraceHitag(rx,rxlen,response,0,false)) { + DbpString("Trace full"); + if (bQuitTraceFull) { + break; + } else { + bQuiet = true; + } + } + } + } + + // By default reset the transmission buffer + tx = txbuf; + switch(htf) { + case WHT2F_CRYPTO: { + bStop = !hitag2_crypto(rx,rxlen,tx,&txlen, true); + } break; + default: { + Dbprintf("Error, unknown function: %d",htf); + return; + } break; + } + + // Send and store the reader command + // Disable timer 1 with external trigger to avoid triggers during our own modulation + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; + + // Wait for HITAG_T_WAIT_2 carrier periods after the last tag bit before transmitting, + // Since the clock counts since the last falling edge, a 'one' means that the + // falling edge occured halfway the period. with respect to this falling edge, + // we need to wait (T_Wait2 + half_tag_period) when the last was a 'one'. + // All timer values are in terms of T0 units + while(AT91C_BASE_TC0->TC_CV < T0*(t_wait+(HITAG_T_TAG_HALF_PERIOD*lastbit))); + + //Dbprintf("DEBUG: Sending reader frame"); + + // Transmit the reader frame + hitag_reader_send_frame(tx,txlen); + + // Enable and reset external trigger in timer for capturing future frames + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; + + // Add transmitted frame to total count + if(txlen > 0) { + frame_count++; + if (!bQuiet) { + // Store the frame in the trace + if (!LogTraceHitag(tx,txlen,HITAG_T_WAIT_2,0,true)) { + if (bQuitTraceFull) { + break; + } else { + bQuiet = true; + } + } + } + } + + // Reset values for receiving frames + memset(rx,0x00,sizeof(rx)); + rxlen = 0; + lastbit = 1; + bSkip = true; + tag_sof = reset_sof; + response = 0; + //Dbprintf("DEBUG: Waiting to receive frame"); + uint32_t errorCount = 0; + + // Receive frame, watch for at most T0*EOF periods + while (AT91C_BASE_TC1->TC_CV < T0*HITAG_T_WAIT_MAX) { + // Check if falling edge in tag modulation is detected + if(AT91C_BASE_TC1->TC_SR & AT91C_TC_LDRAS) { + // Retrieve the new timing values + int ra = (AT91C_BASE_TC1->TC_RA/T0); + + // Reset timer every frame, we have to capture the last edge for timing + AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG; + + LED_B_ON(); + + // Capture tag frame (manchester decoding using only falling edges) + if(ra >= HITAG_T_EOF) { + if (rxlen != 0) { + //Dbprintf("DEBUG: Wierd1"); + } + // Capture the T0 periods that have passed since last communication or field drop (reset) + // We always recieve a 'one' first, which has the falling edge after a half period |-_| + response = ra-HITAG_T_TAG_HALF_PERIOD; + } else if(ra >= HITAG_T_TAG_CAPTURE_FOUR_HALF) { + // Manchester coding example |-_|_-|-_| (101) + + //need to test to verify we don't exceed memory... + //if ( ((rxlen+2) / 8) > HITAG_FRAME_LEN) { + // break; + //} + rx[rxlen / 8] |= 0 << (7-(rxlen%8)); + rxlen++; + rx[rxlen / 8] |= 1 << (7-(rxlen%8)); + rxlen++; + } else if(ra >= HITAG_T_TAG_CAPTURE_THREE_HALF) { + // Manchester coding example |_-|...|_-|-_| (0...01) + + //need to test to verify we don't exceed memory... + //if ( ((rxlen+2) / 8) > HITAG_FRAME_LEN) { + // break; + //} + rx[rxlen / 8] |= 0 << (7-(rxlen%8)); + rxlen++; + // We have to skip this half period at start and add the 'one' the second time + if (!bSkip) { + rx[rxlen / 8] |= 1 << (7-(rxlen%8)); + rxlen++; + } + lastbit = !lastbit; + bSkip = !bSkip; + } else if(ra >= HITAG_T_TAG_CAPTURE_TWO_HALF) { + // Manchester coding example |_-|_-| (00) or |-_|-_| (11) + + //need to test to verify we don't exceed memory... + //if ( ((rxlen+2) / 8) > HITAG_FRAME_LEN) { + // break; + //} + if (tag_sof) { + // Ignore bits that are transmitted during SOF + tag_sof--; + } else { + // bit is same as last bit + rx[rxlen / 8] |= lastbit << (7-(rxlen%8)); + rxlen++; + } + } else { + //Dbprintf("DEBUG: Wierd2"); + errorCount++; + // Ignore wierd value, is to small to mean anything + } + } + //if we saw over 100 wierd values break it probably isn't hitag... + if (errorCount >100) break; + // We can break this loop if we received the last bit from a frame + if (AT91C_BASE_TC1->TC_CV > T0*HITAG_T_EOF) { + if (rxlen>0) break; + } + } + + // Wait some extra time for flash to be programmed + if ((rxlen == 0) && (writestate == WRITE_STATE_PROG)) + { + AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG; + while(AT91C_BASE_TC0->TC_CV < T0*(HITAG_T_PROG - HITAG_T_WAIT_MAX)); + } + } + //Dbprintf("DEBUG: Done waiting for frame"); + + LED_B_OFF(); + LED_D_OFF(); + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; + AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + //Dbprintf("frame received: %d",frame_count); + //DbpString("All done"); + cmd_send(CMD_ACK,bSuccessful,0,0,(byte_t*)tag.sectors,48); +} diff --git a/client/cmdlfhitag.c b/client/cmdlfhitag.c index 718cb703..103f2b77 100644 --- a/client/cmdlfhitag.c +++ b/client/cmdlfhitag.c @@ -349,7 +349,9 @@ int CmdLFHitagWP(const char *Cmd) { c.arg[2]= param_get32ex(Cmd, 2, 0, 10); num_to_bytes(param_get32ex(Cmd,3,0,16),4,htd->auth.data); } break; - case 04: { //WHTSF_KEY + case 04: + case 24: + { //WHTSF_KEY num_to_bytes(param_get64ex(Cmd,1,0,16),6,htd->crypto.key); c.arg[2]= param_get32ex(Cmd, 2, 0, 10); num_to_bytes(param_get32ex(Cmd,3,0,16),4,htd->crypto.data); @@ -363,6 +365,7 @@ int CmdLFHitagWP(const char *Cmd) { PrintAndLog(" 04 (set to 0 if no authentication is needed) write page on a Hitag S tag"); PrintAndLog(" Hitag1 (1*)"); PrintAndLog(" Hitag2 (2*)"); + PrintAndLog(" 24 (set to 0 if no authentication is needed) write page on a Hitag S tag"); return 1; } break; } diff --git a/include/hitag2.h b/include/hitag2.h index 66770d98..2406c649 100644 --- a/include/hitag2.h +++ b/include/hitag2.h @@ -27,8 +27,9 @@ typedef enum { RHT2F_PASSWORD = 21, RHT2F_AUTHENTICATE = 22, RHT2F_CRYPTO = 23, + WHT2F_CRYPTO = 24, RHT2F_TEST_AUTH_ATTEMPTS = 25, - RHT2F_UID_ONLY = 26 + RHT2F_UID_ONLY = 26, } hitag_function; typedef struct { From 067bfc8b762f86d5ebb65ecca9e1ba3a304ecd84 Mon Sep 17 00:00:00 2001 From: Michael Farrell Date: Thu, 6 Jul 2017 04:22:02 +1000 Subject: [PATCH 3/4] Refactoring uart interface (#341) * uart: Major cleanups - Adds documentation to the uart API. - Fixes a buffer overflow issue in `uart_receive`, where the maximum parameter was ignored. - Splits the maximum length and bytes recieved variables in `uart_receive`. - Downsizes the receive buffer to the minimum required, saving 16MiB of RAM at runtime. - Refactors the POSIX and Win32 implementations of uart into separate files. - Removes the unused `uart_{get,set}_parity` functions, which were not implemented on Win32. --- client/Makefile | 7 +- client/flasher.c | 7 +- client/proxmark3.c | 16 +- uart/README.md | 13 ++ {client => uart}/uart.h | 79 ++++--- client/uart.c => uart/uart_posix.c | 327 ++++++++++------------------- uart/uart_win32.c | 136 ++++++++++++ 7 files changed, 315 insertions(+), 270 deletions(-) create mode 100644 uart/README.md rename {client => uart}/uart.h (57%) rename client/uart.c => uart/uart_posix.c (69%) create mode 100644 uart/uart_win32.c diff --git a/client/Makefile b/client/Makefile index a1d4f8d8..d05bdc44 100644 --- a/client/Makefile +++ b/client/Makefile @@ -13,13 +13,13 @@ RM = rm -f MV = mv #COMMON_FLAGS = -m32 -VPATH = ../common ../zlib +VPATH = ../common ../zlib ../uart OBJDIR = obj LDLIBS = -L/opt/local/lib -L/usr/local/lib -lreadline -lpthread -lm LUALIB = ../liblua/liblua.a LDFLAGS = $(COMMON_FLAGS) -CFLAGS = -std=c99 -D_ISOC99_SOURCE -I. -I../include -I../common -I../zlib -I/opt/local/include -I../liblua -Wall $(COMMON_FLAGS) -g -O3 +CFLAGS = -std=c99 -D_ISOC99_SOURCE -I. -I../include -I../common -I../zlib -I../uart -I/opt/local/include -I../liblua -Wall $(COMMON_FLAGS) -g -O3 CXXFLAGS = -I../include -Wall -O3 LUAPLATFORM = generic @@ -78,7 +78,8 @@ DEPFLAGS = -MT $@ -MMD -MP -MF $(OBJDIR)/$*.Td # make temporary to final dependeny files after successful compilation POSTCOMPILE = $(MV) -f $(OBJDIR)/$*.Td $(OBJDIR)/$*.d -CORESRCS = uart.c \ +CORESRCS = uart_posix.c \ + uart_win32.c \ util.c \ util_posix.c diff --git a/client/flasher.c b/client/flasher.c index 48317c1e..f257d994 100644 --- a/client/flasher.c +++ b/client/flasher.c @@ -19,6 +19,8 @@ #ifdef _WIN32 # define unlink(x) +#else +# include #endif static serial_port sp; @@ -52,8 +54,7 @@ void ReceiveCommand(UsbCommand* rxcmd) { byte_t* prx = prxcmd; size_t rxlen; while (true) { - rxlen = sizeof(UsbCommand) - (prx-prxcmd); - if (uart_receive(sp,prx,&rxlen)) { + if (uart_receive(sp, prx, sizeof(UsbCommand) - (prx-prxcmd), &rxlen)) { prx += rxlen; if ((prx-prxcmd) >= sizeof(UsbCommand)) { return; @@ -129,7 +130,7 @@ int main(int argc, char **argv) fprintf(stderr,"Waiting for Proxmark to appear on %s",serial_port_name); do { - sleep(1); + msleep(1000); fprintf(stderr, "."); } while (!OpenProxmark(0)); fprintf(stderr," Found.\n"); diff --git a/client/proxmark3.c b/client/proxmark3.c index 460aea29..fa389dd1 100644 --- a/client/proxmark3.c +++ b/client/proxmark3.c @@ -57,26 +57,22 @@ struct receiver_arg { int run; }; -byte_t rx[0x1000000]; +byte_t rx[sizeof(UsbCommand)]; byte_t* prx = rx; static void *uart_receiver(void *targ) { struct receiver_arg *arg = (struct receiver_arg*)targ; size_t rxlen; - size_t cmd_count; while (arg->run) { - rxlen = sizeof(UsbCommand); - if (uart_receive(sp, prx, &rxlen)) { + rxlen = 0; + if (uart_receive(sp, prx, sizeof(UsbCommand) - (prx-rx), &rxlen)) { prx += rxlen; - if (((prx-rx) % sizeof(UsbCommand)) != 0) { + if (prx-rx < sizeof(UsbCommand)) { continue; } - cmd_count = (prx-rx) / sizeof(UsbCommand); - - for (size_t i = 0; i < cmd_count; i++) { - UsbCommandReceived((UsbCommand*)(rx+(i*sizeof(UsbCommand)))); - } + + UsbCommandReceived((UsbCommand*)rx); } prx = rx; diff --git a/uart/README.md b/uart/README.md new file mode 100644 index 00000000..218e983c --- /dev/null +++ b/uart/README.md @@ -0,0 +1,13 @@ +# uart + +This contains functionality for talking to UART/Serial devices on different platforms. The official client will build either `uart_posix.c` and `uart_win32.c`. Build targets for these files are contained in `client/Makefile`. + +If you want to implement support for other platforms, you need to implement the methods provided in `uart.h`. + +## Implementing a new driver + +Each driver is called with a string, typically containing a path or other reference to a serial port on the host. The methods outlined in `uart.h` need to be implemented. + +The hardware uses `common/usb_cdc.c` to implement a USB CDC endpoint exposed by the Atmel MCU. + + diff --git a/client/uart.h b/uart/uart.h similarity index 57% rename from client/uart.h rename to uart/uart.h index 747c0f26..fe75a683 100644 --- a/client/uart.h +++ b/uart/uart.h @@ -27,12 +27,10 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * @file uart.h - * @brief - * */ -#ifndef _RS232_H_ -#define _RS232_H_ +#ifndef _PM3_UART_H_ +#define _PM3_UART_H_ #include #include @@ -43,45 +41,58 @@ typedef unsigned char byte_t; -// Handle platform specific includes -#ifndef _WIN32 - #include - #include - #include - #include - #include - #include - #include - #include - #include -#else - #include -#endif - -typedef enum { - SP_INVALID = 0x00, // invalid value, error occured - SP_NONE = 0x01, // no parity (default) - SP_EVEN = 0x02, // even parity - SP_ODD = 0x03 // odd parity -} serial_port_parity; - -// Define shortcut to types to make code more readable +/* serial_port is declared as a void*, which you should cast to whatever type + * makes sense to your connection method. Both the posix and win32 + * implementations define their own structs in place. + */ typedef void* serial_port; + +/* Returned by uart_open if the serial port specified was invalid. + */ #define INVALID_SERIAL_PORT (void*)(~1) + +/* Returned by uart_open if the serial port specified is in use by another + * process. + */ #define CLAIMED_SERIAL_PORT (void*)(~2) +/* Given a user-specified port name, connect to the port and return a structure + * used for future references to that port. + * + * On errors, this method returns INVALID_SERIAL_PORT or CLAIMED_SERIAL_PORT. + */ serial_port uart_open(const char* pcPortName); + +/* Closes the given port. + */ void uart_close(const serial_port sp); -bool uart_set_speed(serial_port sp, const uint32_t uiPortSpeed); -uint32_t uart_get_speed(const serial_port sp); +/* Reads from the given serial port for up to 30ms. + * pbtRx: A pointer to a buffer for the returned data to be written to. + * pszMaxRxLen: The maximum data size we want to be sent. + * pszRxLen: The number of bytes that we were actually sent. + * + * Returns TRUE if any data was fetched, even if it was less than pszMaxRxLen. + * + * Returns FALSE if there was an error reading from the device. Note that a + * partial read may have completed into the buffer by the corresponding + * implementation, so pszRxLen should be checked to see if any data was written. + */ +bool uart_receive(const serial_port sp, byte_t* pbtRx, size_t pszMaxRxLen, size_t* pszRxLen); -bool uart_set_parity(serial_port sp, serial_port_parity spp); -serial_port_parity uart_get_parity(const serial_port sp); - -bool uart_receive(const serial_port sp, byte_t* pbtRx, size_t* pszRxLen); +/* Sends a buffer to a given serial port. + * pbtTx: A pointer to a buffer containing the data to send. + * szTxLen: The amount of data to be sent. + */ bool uart_send(const serial_port sp, const byte_t* pbtTx, const size_t szTxLen); -#endif // _PROXMARK3_RS232_H_ +/* Sets the current speed of the serial port, in baud. + */ +bool uart_set_speed(serial_port sp, const uint32_t uiPortSpeed); +/* Gets the current speed of the serial port, in baud. + */ +uint32_t uart_get_speed(const serial_port sp); + +#endif // _PM3_UART_H_ diff --git a/client/uart.c b/uart/uart_posix.c similarity index 69% rename from client/uart.c rename to uart/uart_posix.c index 4b2fee99..45e0d3d2 100644 --- a/client/uart.c +++ b/uart/uart_posix.c @@ -26,17 +26,26 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * @file uart.c - * @brief + * @file uart_posix.c * + * This version of the library has functionality removed which was not used by + * proxmark3 project. */ #include "uart.h" -// Test if we are dealing with unix operating systems +// Test if we are dealing with posix operating systems #ifndef _WIN32 - #include +#include +#include +#include +#include +#include +#include +#include +#include + typedef struct termios term_info; typedef struct { int fd; // Serial port file descriptor @@ -124,6 +133,99 @@ void uart_close(const serial_port sp) { free(sp); } +bool uart_receive(const serial_port sp, byte_t* pbtRx, size_t pszMaxRxLen, size_t* pszRxLen) { + int res; + int byteCount; + fd_set rfds; + struct timeval tv; + + // Reset the output count + *pszRxLen = 0; + + do { + // Reset file descriptor + FD_ZERO(&rfds); + FD_SET(((serial_port_unix*)sp)->fd,&rfds); + tv = timeout; + res = select(((serial_port_unix*)sp)->fd+1, &rfds, NULL, NULL, &tv); + + // Read error + if (res < 0) { + return false; + } + + // Read time-out + if (res == 0) { + if (*pszRxLen == 0) { + // Error, we received no data + return false; + } else { + // We received some data, but nothing more is available + return true; + } + } + + // Retrieve the count of the incoming bytes + res = ioctl(((serial_port_unix*)sp)->fd, FIONREAD, &byteCount); + if (res < 0) return false; + + // Cap the number of bytes, so we don't overrun the buffer + if (pszMaxRxLen - (*pszRxLen) < byteCount) { + byteCount = pszMaxRxLen - (*pszRxLen); + } + + // There is something available, read the data + res = read(((serial_port_unix*)sp)->fd, pbtRx+(*pszRxLen), byteCount); + + // Stop if the OS has some troubles reading the data + if (res <= 0) return false; + + *pszRxLen += res; + + if (*pszRxLen == pszMaxRxLen) { + // We have all the data we wanted. + return true; + } + + } while (byteCount); + + return true; +} + +bool uart_send(const serial_port sp, const byte_t* pbtTx, const size_t szTxLen) { + int32_t res; + size_t szPos = 0; + fd_set rfds; + struct timeval tv; + + while (szPos < szTxLen) { + // Reset file descriptor + FD_ZERO(&rfds); + FD_SET(((serial_port_unix*)sp)->fd,&rfds); + tv = timeout; + res = select(((serial_port_unix*)sp)->fd+1, NULL, &rfds, NULL, &tv); + + // Write error + if (res < 0) { + return false; + } + + // Write time-out + if (res == 0) { + return false; + } + + // Send away the bytes + res = write(((serial_port_unix*)sp)->fd,pbtTx+szPos,szTxLen-szPos); + + // Stop if the OS has some troubles sending the data + if (res <= 0) return false; + + szPos += res; + } + return true; +} + bool uart_set_speed(serial_port sp, const uint32_t uiPortSpeed) { const serial_port_unix* spu = (serial_port_unix*)sp; speed_t stPortSpeed; @@ -211,220 +313,5 @@ uint32_t uart_get_speed(const serial_port sp) { return uiPortSpeed; } -bool uart_set_parity(serial_port sp, serial_port_parity spp) { - struct termios ti; - const serial_port_unix* spu = (serial_port_unix*)sp; - if (tcgetattr(spu->fd,&ti) == -1) return false; - switch(spp) { - case SP_INVALID: return false; - case SP_NONE: ti.c_cflag &= ~(PARENB | PARODD); break; - case SP_EVEN: ti.c_cflag |= PARENB; ti.c_cflag &= ~(PARODD); break; - case SP_ODD: ti.c_cflag |= PARENB | PARODD; break; - } - return (tcsetattr(spu->fd,TCSANOW,&ti) != -1); -} - -serial_port_parity uart_get_parity(const serial_port sp) { - struct termios ti; - const serial_port_unix* spu = (serial_port_unix*)sp; - if (tcgetattr(spu->fd,&ti) == -1) return SP_INVALID; - - if (ti.c_cflag & PARENB) { - if (ti.c_cflag & PARODD) { - return SP_ODD; - } else { - return SP_EVEN; - } - } else { - return SP_NONE; - } -} - -bool uart_cts(const serial_port sp) { - char status; - if (ioctl(((serial_port_unix*)sp)->fd,TIOCMGET,&status) < 0) return false; - return (status & TIOCM_CTS); -} - -bool uart_receive(const serial_port sp, byte_t* pbtRx, size_t* pszRxLen) { - - int res; - int byteCount; - fd_set rfds; - struct timeval tv; - - // Reset the output count - *pszRxLen = 0; - - do { - // Reset file descriptor - FD_ZERO(&rfds); - FD_SET(((serial_port_unix*)sp)->fd,&rfds); - tv = timeout; - res = select(((serial_port_unix*)sp)->fd+1, &rfds, NULL, NULL, &tv); - - // Read error - if (res < 0) { - return false; - } - - // Read time-out - if (res == 0) { - if (*pszRxLen == 0) { - // Error, we received no data - return false; - } else { - // We received some data, but nothing more is available - return true; - } - } - - // Retrieve the count of the incoming bytes - res = ioctl(((serial_port_unix*)sp)->fd, FIONREAD, &byteCount); - if (res < 0) return false; - - // There is something available, read the data - res = read(((serial_port_unix*)sp)->fd,pbtRx+(*pszRxLen),byteCount); - - // Stop if the OS has some troubles reading the data - if (res <= 0) return false; - - *pszRxLen += res; - - if(res==byteCount) - return true; - - } while (byteCount); - - return true; -} - -bool uart_send(const serial_port sp, const byte_t* pbtTx, const size_t szTxLen) { - int32_t res; - size_t szPos = 0; - fd_set rfds; - struct timeval tv; - - while (szPos < szTxLen) { - // Reset file descriptor - FD_ZERO(&rfds); - FD_SET(((serial_port_unix*)sp)->fd,&rfds); - tv = timeout; - res = select(((serial_port_unix*)sp)->fd+1, NULL, &rfds, NULL, &tv); - - // Write error - if (res < 0) { - return false; - } - - // Write time-out - if (res == 0) { - return false; - } - - // Send away the bytes - res = write(((serial_port_unix*)sp)->fd,pbtTx+szPos,szTxLen-szPos); - - // Stop if the OS has some troubles sending the data - if (res <= 0) return false; - - szPos += res; - } - return true; -} - -#else -// The windows serial port implementation - -typedef struct { - HANDLE hPort; // Serial port handle - DCB dcb; // Device control settings - COMMTIMEOUTS ct; // Serial port time-out configuration -} serial_port_windows; - -void upcase(char *p) { - while(*p != '\0') { - if(*p >= 97 && *p <= 122) { - *p -= 32; - } - ++p; - } -} - -serial_port uart_open(const char* pcPortName) { - char acPortName[255]; - serial_port_windows* sp = malloc(sizeof(serial_port_windows)); - - // Copy the input "com?" to "\\.\COM?" format - sprintf(acPortName,"\\\\.\\%s",pcPortName); - upcase(acPortName); - - // Try to open the serial port - sp->hPort = CreateFileA(acPortName,GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,0,NULL); - if (sp->hPort == INVALID_HANDLE_VALUE) { - uart_close(sp); - return INVALID_SERIAL_PORT; - } - - // Prepare the device control - memset(&sp->dcb, 0, sizeof(DCB)); - sp->dcb.DCBlength = sizeof(DCB); - if(!BuildCommDCBA("baud=9600 data=8 parity=N stop=1",&sp->dcb)) { - uart_close(sp); - return INVALID_SERIAL_PORT; - } - - // Update the active serial port - if(!SetCommState(sp->hPort,&sp->dcb)) { - uart_close(sp); - return INVALID_SERIAL_PORT; - } - - sp->ct.ReadIntervalTimeout = 0; - sp->ct.ReadTotalTimeoutMultiplier = 0; - sp->ct.ReadTotalTimeoutConstant = 30; - sp->ct.WriteTotalTimeoutMultiplier = 0; - sp->ct.WriteTotalTimeoutConstant = 30; - - if(!SetCommTimeouts(sp->hPort,&sp->ct)) { - uart_close(sp); - return INVALID_SERIAL_PORT; - } - - PurgeComm(sp->hPort, PURGE_RXABORT | PURGE_RXCLEAR); - - return sp; -} - -void uart_close(const serial_port sp) { - CloseHandle(((serial_port_windows*)sp)->hPort); - free(sp); -} - -bool uart_set_speed(serial_port sp, const uint32_t uiPortSpeed) { - serial_port_windows* spw; - spw = (serial_port_windows*)sp; - spw->dcb.BaudRate = uiPortSpeed; - return SetCommState(spw->hPort, &spw->dcb); -} - -uint32_t uart_get_speed(const serial_port sp) { - const serial_port_windows* spw = (serial_port_windows*)sp; - if (!GetCommState(spw->hPort, (serial_port)&spw->dcb)) { - return spw->dcb.BaudRate; - } - return 0; -} - -bool uart_receive(const serial_port sp, byte_t* pbtRx, size_t* pszRxLen) { - ReadFile(((serial_port_windows*)sp)->hPort,pbtRx,*pszRxLen,(LPDWORD)pszRxLen,NULL); - return (*pszRxLen != 0); -} - -bool uart_send(const serial_port sp, const byte_t* pbtTx, const size_t szTxLen) { - DWORD dwTxLen = 0; - return WriteFile(((serial_port_windows*)sp)->hPort,pbtTx,szTxLen,&dwTxLen,NULL); - return (dwTxLen != 0); -} - #endif + diff --git a/uart/uart_win32.c b/uart/uart_win32.c new file mode 100644 index 00000000..af521ead --- /dev/null +++ b/uart/uart_win32.c @@ -0,0 +1,136 @@ +/* + * Generic uart / rs232/ serial port library + * + * Copyright (c) 2013, Roel Verdult + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @file uart_win32.c + * + * Note: the win32 version of this library has also been seen under the GPLv3+ + * license as part of the libnfc project, which appears to have additional + * contributors. + * + * This version of the library has functionality removed which was not used by + * proxmark3 project. + */ + +#include "uart.h" + +// The windows serial port implementation +#ifdef _WIN32 +#include + +typedef struct { + HANDLE hPort; // Serial port handle + DCB dcb; // Device control settings + COMMTIMEOUTS ct; // Serial port time-out configuration +} serial_port_windows; + +void upcase(char *p) { + while(*p != '\0') { + if(*p >= 97 && *p <= 122) { + *p -= 32; + } + ++p; + } +} + +serial_port uart_open(const char* pcPortName) { + char acPortName[255]; + serial_port_windows* sp = malloc(sizeof(serial_port_windows)); + + // Copy the input "com?" to "\\.\COM?" format + sprintf(acPortName,"\\\\.\\%s",pcPortName); + upcase(acPortName); + + // Try to open the serial port + sp->hPort = CreateFileA(acPortName,GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,0,NULL); + if (sp->hPort == INVALID_HANDLE_VALUE) { + uart_close(sp); + return INVALID_SERIAL_PORT; + } + + // Prepare the device control + memset(&sp->dcb, 0, sizeof(DCB)); + sp->dcb.DCBlength = sizeof(DCB); + if(!BuildCommDCBA("baud=9600 data=8 parity=N stop=1",&sp->dcb)) { + uart_close(sp); + return INVALID_SERIAL_PORT; + } + + // Update the active serial port + if(!SetCommState(sp->hPort,&sp->dcb)) { + uart_close(sp); + return INVALID_SERIAL_PORT; + } + + sp->ct.ReadIntervalTimeout = 0; + sp->ct.ReadTotalTimeoutMultiplier = 0; + sp->ct.ReadTotalTimeoutConstant = 30; + sp->ct.WriteTotalTimeoutMultiplier = 0; + sp->ct.WriteTotalTimeoutConstant = 30; + + if(!SetCommTimeouts(sp->hPort,&sp->ct)) { + uart_close(sp); + return INVALID_SERIAL_PORT; + } + + PurgeComm(sp->hPort, PURGE_RXABORT | PURGE_RXCLEAR); + + return sp; +} + +void uart_close(const serial_port sp) { + CloseHandle(((serial_port_windows*)sp)->hPort); + free(sp); +} + +bool uart_receive(const serial_port sp, byte_t* pbtRx, size_t pszMaxRxLen, size_t* pszRxLen) { + ReadFile(((serial_port_windows*)sp)->hPort,pbtRx,pszMaxRxLen,(LPDWORD)pszRxLen,NULL); + return (*pszRxLen != 0); +} + +bool uart_send(const serial_port sp, const byte_t* pbtTx, const size_t szTxLen) { + DWORD dwTxLen = 0; + return WriteFile(((serial_port_windows*)sp)->hPort,pbtTx,szTxLen,&dwTxLen,NULL); + return (dwTxLen != 0); +} + +bool uart_set_speed(serial_port sp, const uint32_t uiPortSpeed) { + serial_port_windows* spw; + spw = (serial_port_windows*)sp; + spw->dcb.BaudRate = uiPortSpeed; + return SetCommState(spw->hPort, &spw->dcb); +} + +uint32_t uart_get_speed(const serial_port sp) { + const serial_port_windows* spw = (serial_port_windows*)sp; + if (!GetCommState(spw->hPort, (serial_port)&spw->dcb)) { + return spw->dcb.BaudRate; + } + return 0; +} + +#endif From 03a4837bfa94719bfce8a3f723e3c7d358bc8f0d Mon Sep 17 00:00:00 2001 From: Fl0-0 Date: Wed, 5 Jul 2017 21:16:15 +0200 Subject: [PATCH 4/4] Fix CmdEM410xBrute string format warning --- client/cmdlfem4x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/cmdlfem4x.c b/client/cmdlfem4x.c index f8e00e9b..013c2f3f 100644 --- a/client/cmdlfem4x.c +++ b/client/cmdlfem4x.c @@ -405,7 +405,7 @@ int CmdEM410xBrute(const char *Cmd) return 0; } - sprintf(testuid, "%010lX", bytes_to_num(uidBlock + 5*c, 5)); + sprintf(testuid, "%010" PRIX64, bytes_to_num(uidBlock + 5*c, 5)); PrintAndLog("Bruteforce %d / %d: simulating UID %s, clock %d", c + 1, uidcnt, testuid, clock); ConstructEM410xEmulGraph(testuid, clock);