From 4e0e69ed631eb05543fe91b2bbf3339879d10c8e Mon Sep 17 00:00:00 2001 From: mwalker33 Date: Tue, 23 Jul 2019 09:50:28 +1000 Subject: [PATCH] Added T55 downlink mode support --- CHANGELOG.md | 1 + armsrc/appmain.c | 17 +- armsrc/apps.h | 11 +- armsrc/lfops.c | 674 ++++++++++++++++++++++++++++++------------- client/cmdlft55xx.c | 684 +++++++++++++++++++++++++++++++------------- client/cmdlft55xx.h | 10 +- client/scripting.c | 6 +- include/pm3_cmd.h | 29 +- 8 files changed, 1017 insertions(+), 415 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c78d813db..86321ee9a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -293,6 +293,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Added `hf fido` `assert` and `make` commands from fido2 protocol (authenticatorMakeCredential and authenticatorGetAssertion) (@merlokk) - Added trailer block decoding to `hf mf rdbl` and `hf mf cgetbl` (@merlokk) - Added `hf mf mad` and `hf mfp mad` MAD decode, check and print commands (@merlokk) + - Added T55x7 downlink mode support r 0 Default, 1 Long Leading 0, 2 Leading 0, 3 1 of 4 and 4 (in some commands) try all. ### Fixed - Changed driver file proxmark3.inf to support both old and new Product/Vendor IDs (piwi) diff --git a/armsrc/appmain.c b/armsrc/appmain.c index d9c17e3b3..1fed86574 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -873,12 +873,13 @@ static void PacketReceived(PacketCommandNG *packet) { case CMD_T55XX_READ_BLOCK: { struct p { uint32_t password; - uint8_t blockno; - uint8_t page; - bool pwdmode; + uint8_t blockno; + uint8_t page; + bool pwdmode; + uint8_t downlink_mode; } PACKED; struct p *payload = (struct p *) packet->data.asBytes; - T55xxReadBlock(payload->page, payload->pwdmode, false, payload->blockno, payload->password); + T55xxReadBlock(payload->page, payload->pwdmode, false, payload->blockno, payload->password,payload->downlink_mode); break; } case CMD_T55XX_WRITE_BLOCK: { @@ -887,15 +888,15 @@ static void PacketReceived(PacketCommandNG *packet) { break; } case CMD_T55XX_WAKEUP: { - T55xxWakeUp(packet->oldarg[0]); - break; + T55xxWakeUp(packet->oldarg[0],packet->oldarg[1]); + break; } case CMD_T55XX_RESET_READ: { - T55xxResetRead(); + T55xxResetRead(packet->data.asBytes[0]&0xff); break; } case CMD_T55XX_CHKPWDS: { - T55xx_ChkPwds(); + T55xx_ChkPwds(packet->data.asBytes[0]&0xff); break; } case CMD_PCF7931_READ: { diff --git a/armsrc/apps.h b/armsrc/apps.h index 41a1f0271..a508b74b0 100644 --- a/armsrc/apps.h +++ b/armsrc/apps.h @@ -102,12 +102,13 @@ void CopyVikingtoT55xx(uint32_t block1, uint32_t block2, uint8_t Q5); void WriteEM410x(uint32_t card, uint32_t id_hi, uint32_t id_lo); void CopyIndala64toT55x7(uint32_t hi, uint32_t lo); // Clone Indala 64-bit tag by UID to T55x7 void CopyIndala224toT55x7(uint32_t uid1, uint32_t uid2, uint32_t uid3, uint32_t uid4, uint32_t uid5, uint32_t uid6, uint32_t uid7); // Clone Indala 224-bit tag by UID to T55x7 -void T55xxResetRead(void); +void T55xxResetRead(uint8_t flags); +//id T55xxWriteBlock(uint32_t data, uint8_t blockno, uint32_t pwd, uint8_t flags); void T55xxWriteBlock(uint8_t *data); -void T55xxWriteBlockExt(uint32_t data, uint8_t blockno, uint32_t pwd, uint8_t flags); -void T55xxReadBlock(uint8_t page, bool pwd_mode, bool brute_mem, uint8_t block, uint32_t pwd); -void T55xxWakeUp(uint32_t Pwd); -void T55xx_ChkPwds(void); +// void T55xxWriteBlockExt(uint32_t data, uint8_t blockno, uint32_t pwd, uint8_t flags); +void T55xxReadBlock(uint8_t page, bool pwd_mode, bool brute_mem, uint8_t block, uint32_t pwd,uint8_t downlink_mode); +void T55xxWakeUp(uint32_t Pwd, uint8_t flags); +void T55xx_ChkPwds(uint8_t flags); void TurnReadLFOn(uint32_t delay); diff --git a/armsrc/lfops.c b/armsrc/lfops.c index 96fd3ae37..6efbc6bd9 100644 --- a/armsrc/lfops.c +++ b/armsrc/lfops.c @@ -46,31 +46,133 @@ // = 1us = 1.5ticks // 1fc = 8us = 12ticks /* -Default LF T55xx config is set to: - startgap = 31*8 - writegap = 17*8 - write_0 = 15*8 - write_1 = 47*8 - read_gap = 15*8 + ========================================================================================================== + T55x7 Timing + ========================================================================================================== + + // t55xx_config t_config = { 29 * 8, 17 * 8, 15 * 8, 47 * 8, 15 * 8 } ; + + ATA5577 Downlink Protocol Timings. + Note: All absolute times assume TC = 1 / fC = 8 μs (fC = 125 kHz) + ----------------------------------------------------------------------- + Fixed-bit-length Protocol | Normal Downlink | Fast Downlink | + ------------------------------+-----------------------------------+-----------------------------------+------ +| Parameter | Remark | Symbol | Min. | Typ. | Max. | Min. | Typ. | Max. | Unit | +|------------+--------+--------+-----------+-----------+-----------+-----------+-----------+-----------+------| +| Start gap | | Sgap | 8 | 15 | 50 | 8 | 15 | 50 | Tc | +| Write gap | | Wgap | 8 | 10 | 20 | 8 | 10 | 20 | Tc | +|------------+--------+--------+-----------+-----------+-----------+-----------+-----------+-----------+------| +| coding | 0 data | d0 | 16 | 24 | 32 | 8 | 12 | 16 | Tc | +| | 1 data | d1 | 48 | 56 | 64 | 24 | 28 | 32 | Tc | + ------------------------------------------------------------------------------------------------------------- + + ----------------------------------------------------------------------- + Long Leading Reference | Normal Downlink | Fast Downlink | + ------------------------------+-----------------------------------+-----------------------------------+------ +| Parameter | Remark | Symbol | Min. | Typ. | Max. | Min. | Typ. | Max. | Unit | +|-----------+--------+---------+-----------+-----------+-----------+-----------+-----------+-----------+------| +| Start gap | | Sgap | 8 | 10 | 50 | 8 | 10 | 50 | Tc | +| Write gap | | Wgap | 8 | 10 | 20 | 8 | 10 | 20 | Tc | +|-----------+--------+---------+-----------+-----------+-----------+-----------+-----------+-----------+------| +| Write | Ref | | 152 | 160 | 168 | 140 | 144 | 148 | Tc | +| data | Pulse | dref | 136 clocks + 0 data bit | 132 clocks + 0 data bit | Tc | +| coding |--------+---------+-----------------------------------+-----------------------------------+------| +| | 0 data | d0 |dref – 143 |dref – 136 |dref – 128 |dref – 135 |dref – 132 |dref – 124 | Tc | +| | 1 data | d1 |dref – 111 |dref – 104 |dref – 96 |dref – 119 |dref – 116 |dref – 112 | Tc | + ------------------------------------------------------------------------------------------------------------- + + ----------------------------------------------------------------------- + Leading-zero Reference | Normal Downlink | Fast Downlink | + ------------------------------+-----------------------------------+-----------------------------------+------ +| Parameter | Remark | Symbol | Min. | Typ. | Max. | Min. | Typ. | Max. | Unit | +|-----------+--------+---------+-----------+-----------+-----------+-----------+-----------+-----------+------| +| Start gap | | Sgap | 8 | 10 | 50 | 8 | 10 | 50 | Tc | +| Write gap | | Wgap | 8 | 10 | 20 | 8 | 10 | 20 | Tc | +|-----------+--------+---------+-----------+-----------+-----------+-----------+-----------+-----------+------| +| Write | Ref | dref | 12 | – | 72 | 8 | – | 68 | Tc | +| data | 0 data | d0 | dref – 7 | dref | dref + 8 | dref – 3 | dref | dref + 4 | Tc | +| coding | 1 data | d1 | dref + 9 | dref + 16 | dref + 24 | dref + 5 | dref + 8 | dref + 12 | Tc | + ------------------------------------------------------------------------------------------------------------- + + ----------------------------------------------------------------------- + 1-of-4 Coding | Normal Downlink | Fast Downlink | + ------------------------------+-----------------------------------+-----------------------------------+------ +| Parameter | Remark | Symbol | Min. | Typ. | Max. | Min. | Typ. | Max. | Unit | +|-----------+--------+---------+-----------+-----------+-----------+-----------+-----------+-----------+------| +| Start gap | | Sgap | 8 | 10 | 50 | 8 | 10 | 50 | Tc | +| Write gap | | Wgap | 8 | 10 | 20 | 8 | 10 | 20 | Tc | +|-----------+--------+---------+-----------+-----------+-----------+-----------+-----------+-----------+------| +| Write | Ref 00 | dref | 8 | – | 68 | 12 | – | 72 | Tc | +| data |00 data | d00 | dref – 7 | dref | dref + 8 | dref – 3 | dref | dref+ 4 | Tc | +| coding |01 data | d01 | dref + 9 | dref + 16 | dref + 24 | dref + 5 | dref + 8 | dref + 12 | Tc | +| |10 data | d10 | dref + 25 | dref + 32 | dref + 40 | dref + 13 | dref + 16 | dref + 20 | Tc | +| |11 data | d11 | dref + 41 | dref + 48 | dref + 56 | dref + 21 | dref + 24 | dref + 28 | Tc | + ------------------------------------------------------------------------------------------------------------- */ -t55xx_config t_config = { 29 * 8, 17 * 8, 15 * 8, 47 * 8, 15 * 8 } ; +// Initial values if not in flash +// Note: Moved * 8 to apply when used. Saving 28 bytes here (- the *8) and 28 bytes flash. +// StartGap WriteGap Bit 0/00 Bit 1/01 Bit 10 Bit 11 ReadGap +t55xx_config T55xx_Timing = {{{ 29 , 17 , 15 , 50 , 0 , 0 , 15 }, // Default Fixed + { 31 , 20 , 18 , 50 , 0 , 0 , 15 }, // Long Leading Ref. + { 31 , 20 , 18 , 40 , 0 , 0 , 15 }, // Leading 0 + { 29 , 17 , 15 , 31 , 47 , 63 , 15 } }}; // 1 of 4 +/* +// StartGap WriteGap Bit 0/00 Bit 1/01 Bit 10 Bit 11 ReadGap +t55xx_config T55xx_Timing = {{{ 29 * 8 , 17 * 8 , 15 * 8 , 50 * 8 , 0 , 0 , 15 * 8 }, // Default Fixed + { 31 * 8 , 20 * 8 , 18 * 8 , 50 * 8 , 0 , 0 , 15 * 8 }, // Long Leading Ref. + { 31 * 8 , 20 * 8 , 18 * 8 , 40 * 8 , 0 , 0 , 15 * 8 }, // Leading 0 + { 29 * 8 , 17 * 8 , 15 * 8 , 31 * 8 , 47 * 8, 63 * 8, 15 * 8 } }}; // 1 of 4 +*/ + +// Some defines for readability +#define T55xx_DLMode_Fixed 0 // Default Mode +#define T55xx_DLMode_LLR 1 // Long Leading Reference +#define T55xx_DLMode_Leading0 2 // Leading Zero +#define T55xx_DLMode_1of4 3 // 1 of 4 +#define T55xx_LongLeadingReference 4 // Value to tell Write Bit to send long reference void printT55xxConfig(void) { + int DLMode; + DbpString(_BLUE_("LF T55XX config")); - Dbprintf(" [a] startgap............%d*8 (%d)", t_config.start_gap / 8, t_config.start_gap); - Dbprintf(" [b] writegap............%d*8 (%d)", t_config.write_gap / 8, t_config.write_gap); - Dbprintf(" [c] write_0.............%d*8 (%d)", t_config.write_0 / 8, t_config.write_0); - Dbprintf(" [d] write_1.............%d*8 (%d)", t_config.write_1 / 8, t_config.write_1); - Dbprintf(" [e] readgap.............%d*8 (%d)", t_config.read_gap / 8, t_config.read_gap); + for (DLMode = 0; DLMode < 4; DLMode++) { + switch (DLMode){ + case T55xx_DLMode_Fixed : Dbprintf("r 0 fixed bit length (default)"); break; + case T55xx_DLMode_LLR : Dbprintf("r 1 long leading reference"); break; + case T55xx_DLMode_Leading0 : Dbprintf("r 2 leading zero"); break; + case T55xx_DLMode_1of4 : Dbprintf("r 3 1 of 4 coding reference"); break; + } + // Save 36 bytes by not showing dec. all data sheets and entry is the x * 8 value, so not sure what we get for the 36 bytes dec? + Dbprintf(" [a] startgap............%d*8", T55xx_Timing.m[DLMode].start_gap );//, T55xx_Timing.start_gap[DLMode]); + Dbprintf(" [b] writegap............%d*8", T55xx_Timing.m[DLMode].write_gap );//, T55xx_Timing.write_gap[DLMode]); + Dbprintf(" [c] write_0.............%d*8", T55xx_Timing.m[DLMode].write_0 );//, T55xx_Timing.write_0 [DLMode]); + Dbprintf(" [d] write_1.............%d*8", T55xx_Timing.m[DLMode].write_1 );//, T55xx_Timing.write_1 [DLMode]); + if (DLMode == T55xx_DLMode_1of4) { + Dbprintf(" [e] write_2.............%d*8", T55xx_Timing.m[DLMode].write_2 );//, T55xx_Timing.write_2 [DLMode]); + Dbprintf(" [f] write_3.............%d*8", T55xx_Timing.m[DLMode].write_3 );//, T55xx_Timing.write_3 [DLMode]); + } + Dbprintf(" [g] readgap.............%d*8", T55xx_Timing.m[DLMode].read_gap );//, T55xx_Timing.read_gap [DLMode]); + } } void setT55xxConfig(uint8_t arg0, t55xx_config *c) { - - if (c->start_gap != 0) t_config.start_gap = c->start_gap; - if (c->write_gap != 0) t_config.write_gap = c->write_gap; - if (c->write_0 != 0) t_config.write_0 = c->write_0; - if (c->write_1 != 0) t_config.write_1 = c->write_1; - if (c->read_gap != 0) t_config.read_gap = c->read_gap; + uint8_t DLMode; + + for (DLMode = 0; DLMode < 4; DLMode++) { + if (c->m[DLMode].start_gap != 0) T55xx_Timing.m[DLMode].start_gap = c->m[DLMode].start_gap; + if (c->m[DLMode].write_gap != 0) T55xx_Timing.m[DLMode].write_gap = c->m[DLMode].write_gap; + if (c->m[DLMode].write_0 != 0) T55xx_Timing.m[DLMode].write_0 = c->m[DLMode].write_0 ; + if (c->m[DLMode].write_1 != 0) T55xx_Timing.m[DLMode].write_1 = c->m[DLMode].write_1 ; + if (DLMode == T55xx_DLMode_1of4) { + if (c->m[DLMode].write_2 != 0) T55xx_Timing.m[DLMode].write_2 = c->m[DLMode].write_2 ; + if (c->m[DLMode].write_3 != 0) T55xx_Timing.m[DLMode].write_3 = c->m[DLMode].write_3 ; + } + else{ + T55xx_Timing.m[DLMode].write_2 = 0x00; + T55xx_Timing.m[DLMode].write_3 = 0x00; + } + if (c->m[DLMode].read_gap != 0) T55xx_Timing.m[DLMode].read_gap = c->m[DLMode].read_gap ; + } printT55xxConfig(); @@ -93,27 +195,32 @@ void setT55xxConfig(uint8_t arg0, t55xx_config *c) { return; } - memcpy(buf, &t_config, T55XX_CONFIG_LEN); + memcpy(buf, &T55xx_Timing, T55XX_CONFIG_LEN); Flash_CheckBusy(BUSY_TIMEOUT); Flash_WriteEnable(); Flash_Erase4k(3, 0xD); - res = Flash_Write(T55XX_CONFIG_OFFSET, buf, T55XX_CONFIG_LEN); - if (res == T55XX_CONFIG_LEN && DBGLEVEL > 1) { + // if not a settings erase, write data + if (buf[0] != 0xff) { + res = Flash_Write(T55XX_CONFIG_OFFSET, buf, T55XX_CONFIG_LEN); + + if (res == T55XX_CONFIG_LEN && DBGLEVEL > 1) { DbpString("T55XX Config save success"); - } + } + } BigBuf_free(); #endif } t55xx_config *getT55xxConfig(void) { - return &t_config; + return &T55xx_Timing;//_FixedBit; } void loadT55xxConfig(void) { #ifdef WITH_FLASH + if (!FlashInit()) { return; } @@ -135,7 +242,7 @@ void loadT55xxConfig(void) { return; } - memcpy((uint8_t *)&t_config, buf, T55XX_CONFIG_LEN); + memcpy((uint8_t *)&T55xx_Timing, buf, T55XX_CONFIG_LEN); if (isok == T55XX_CONFIG_LEN) { if (DBGLEVEL > 1) DbpString("T55XX Config load success"); @@ -1354,187 +1461,302 @@ void TurnReadLF_off(uint32_t delay) { WaitUS(delay); } -// Write one bit to card -void T55xxWriteBit(int bit) { - if (!bit) - TurnReadLFOn(t_config.write_0); - else - TurnReadLFOn(t_config.write_1); - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - WaitUS(t_config.write_gap); +// Macro for code readability +#define BitStream_Byte(X) ((X) >> 3) +#define BitStream_Bit(X) ((X) & 7) +#define t55_send_PwdMode (arg & 0x01) +#define t55_send_Page ((arg & 0x02) >> 1) +#define t55_send_TestMode ((arg & 0x04) >> 2) +#define t55_send_RegReadMode ((arg & 0x20) >> 5) +#define t55_send_ReadCmd ((arg & 0x40) >> 6) +#define t55_send_Reset ((arg & 0x80) >> 7) + +// Write one bit to chip +void T55xxWriteBit(uint8_t bit, uint8_t downlink_idx) { + + // Dbprintf ("%d",bit); + // If bit = 4 Send Long Leading Reference which is (138*8) + WRITE_0 + switch (bit){ + case 0 : TurnReadLFOn(T55xx_Timing.m[downlink_idx].write_0 * 8); break; // Send bit 0/00 + case 1 : TurnReadLFOn(T55xx_Timing.m[downlink_idx].write_1 * 8); break; // Send bit 1/01 + case 2 : TurnReadLFOn(T55xx_Timing.m[downlink_idx].write_2 * 8); break; // Send bits 10 (1 of 4) + case 3 : TurnReadLFOn(T55xx_Timing.m[downlink_idx].write_3 * 8); break; // Send bits 11 (1 of 4) + case 4 : TurnReadLFOn((T55xx_Timing.m[downlink_idx].write_0 + 136) * 8); break; // Send Long Leading Reference + } + + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + WaitUS(T55xx_Timing.m[downlink_idx].write_gap * 8); +} + +// Function to abstract an Arbitrary length byte array to store bit pattern. +// bit_array - Array to hold data/bit pattern +// start_offset - bit location to start storing new bits. +// data - upto 32 bits of data to store +// num_bits - how many bits (low x bits of data) Max 32 bits at a time +// max_len - how many bytes can the bit_array hold (ensure no buffer overflow) +// returns "Next" bit offset / bits stored (for next store) +uint8_t T55xx_SetBits (uint8_t *BitStream, uint8_t start_offset, uint32_t data , uint8_t num_bits, uint8_t max_len) +{ + int8_t offset; + int8_t NextOffset = start_offset; + + // Check if data will fit. + if ((start_offset + num_bits) <= (max_len*8)) { + // Loop through the data and store + for (offset = (num_bits-1); offset >= 0; offset--) { + + if ((data >> offset) & 1) BitStream[BitStream_Byte(NextOffset)] |= (1 << BitStream_Bit(NextOffset)); // Set the bit to 1 + else BitStream[BitStream_Byte(NextOffset)] &= (0xff ^ (1 << BitStream_Bit(NextOffset))); // Set the bit to 0 + + NextOffset++; + } + } + else { + // Note: This should never happen unless some code changes cause it. + // So short message for coders when testing. + Dbprintf ("T55 too many bits"); + } + return NextOffset; +} + +// Send one downlink command to the card +// void T55xx_SendCMD (uint32_t Data, uint8_t Block, uint32_t Pwd, uint8_t arg) { +void T55xx_SendCMD (uint32_t Data, uint32_t Pwd, uint16_t arg) { + + /* + arg bits + xxxx xxxxxxx1 0x001 PwdMode + xxxx xxxxxx1x 0x002 Page + xxxx xxxxx1xx 0x004 testMode + xxxx xxx11xxx 0x018 downlink mode + xxxx xx1xxxxx 0x020 !reg_readmode + xxxx x1xxxxxx 0x040 called for a read, so no data packet + xxxx 1xxxxxxx 0x080 reset + xxx1 xxxxxxxx 0x100 brute force + 111x xxxxxxxx 0xE00 Block + + */ + + uint8_t downlink_mode = (arg >> 3) & 0x03; + uint8_t i = 0; + uint8_t BitStream[10]; // Max Downlink Command size ~74 bits, so 10 bytes (80 bits) + uint8_t BitStreamLen = 0; + uint8_t SendBits; + uint8_t start_wait = 4; + bool brute_mem = (arg & 0x100); + uint8_t Block = (arg >> 9) & 0x07; + + if (brute_mem) start_wait = 0; + + // Build Bit Stream to send. + memset (BitStream,0x00,sizeof(BitStream)); + + BitStreamLen = 0; // Ensure 0 bit index to start. + + // Add Leading 0 and 1 of 4 reference bit + if ((downlink_mode == T55xx_DLMode_Leading0) || (downlink_mode == T55xx_DLMode_1of4)) + BitStreamLen = T55xx_SetBits (BitStream, BitStreamLen, 0, 1,sizeof(BitStream)); + + // Add extra reference 0 for 1 of 4 + if (downlink_mode == T55xx_DLMode_1of4) + BitStreamLen = T55xx_SetBits (BitStream, BitStreamLen, 0, 1,sizeof(BitStream)); + + // Add Opcode + if (t55_send_Reset) { + // Reset : r*) 00 + BitStreamLen = T55xx_SetBits (BitStream, BitStreamLen, 0, 2,sizeof(BitStream)); + } + else { + if (t55_send_TestMode) Dbprintf("TestMODE"); + BitStreamLen = T55xx_SetBits (BitStream, BitStreamLen,t55_send_TestMode ? 0 : 1 , 1,sizeof(BitStream)); + BitStreamLen = T55xx_SetBits (BitStream, BitStreamLen,t55_send_TestMode ? 1 : t55_send_Page , 1,sizeof(BitStream)); + //if (PwdMode) { + if (t55_send_PwdMode) { + // Leading 0 and 1 of 4 00 fixed bits if passsword used + if ((downlink_mode == T55xx_DLMode_Leading0) || (downlink_mode == T55xx_DLMode_1of4)) { + BitStreamLen = T55xx_SetBits (BitStream, BitStreamLen, 0, 2,sizeof(BitStream)); + } + BitStreamLen = T55xx_SetBits (BitStream, BitStreamLen, Pwd, 32,sizeof(BitStream)); + } + + // Add Lock bit 0 + if (!t55_send_RegReadMode) BitStreamLen = T55xx_SetBits (BitStream, BitStreamLen, 0, 1,sizeof(BitStream)); + + // Add Data if a write command + if (!t55_send_ReadCmd) BitStreamLen = T55xx_SetBits (BitStream, BitStreamLen, Data, 32,sizeof(BitStream)); + + // Add Address + if (!t55_send_RegReadMode) BitStreamLen = T55xx_SetBits (BitStream, BitStreamLen, Block, 3,sizeof(BitStream)); + } + + // Send Bits to T55xx + // Set up FPGA, 125kHz + LFSetupFPGAForADC(95, true); + + // make sure tag is fully powered up... + WaitMS(start_wait); + + // Trigger T55x7 in mode. + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + WaitUS(T55xx_Timing.m[downlink_mode].start_gap * 8); + + // If long leading 0 send long reference pulse + if (downlink_mode == T55xx_DLMode_LLR) + T55xxWriteBit (T55xx_LongLeadingReference,downlink_mode);//Timing); // Send Long Leading Start Reference + + if ((downlink_mode == T55xx_DLMode_1of4) && (BitStreamLen > 0)) { // 1 of 4 need to send 2 bits at a time + for ( i = 0; i < BitStreamLen-1; i+=2 ) { + SendBits = (BitStream[BitStream_Byte(i )] >> (BitStream_Bit(i )) & 1) << 1; // Bit i + SendBits += (BitStream[BitStream_Byte(i+1)] >> (BitStream_Bit(i+1)) & 1); // Bit i+1; + T55xxWriteBit (SendBits & 3,downlink_mode);//Timing); + } + } + else { + for (i = 0; i < BitStreamLen; i++) { + SendBits = (BitStream[BitStream_Byte(i)] >> BitStream_Bit(i)); + T55xxWriteBit (SendBits & 1,downlink_mode);//Timing); + } + } } // Send T5577 reset command then read stream (see if we can identify the start of the stream) -void T55xxResetRead(void) { +void T55xxResetRead(uint8_t flags) { + + uint8_t downlink_mode = ((flags >> 3) & 3); + uint8_t arg = 0x80 | downlink_mode; + LED_A_ON(); + //clear buffer now so it does not interfere with timing later BigBuf_Clear_keep_EM(); - // Set up FPGA, 125kHz - LFSetupFPGAForADC(95, true); - // make sure tag is fully powered up... - WaitMS(4); - - // Trigger T55x7 in mode. - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - WaitUS(t_config.start_gap); - - // reset tag - op code 00 - T55xxWriteBit(0); - T55xxWriteBit(0); - - TurnReadLFOn(t_config.read_gap); + T55xx_SendCMD (0, 0, arg); + TurnReadLFOn(T55xx_Timing.m[downlink_mode].read_gap * 8); + // Acquisition DoPartialAcquisition(0, true, BigBuf_max_traceLen(), 0); // Turn the field off FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); // field off reply_mix(CMD_ACK, 0, 0, 0, 0, 0); + LED_A_OFF(); } // Write one card block in page 0, no lock -void T55xxWriteBlockExt(uint32_t data, uint8_t blockno, uint32_t pwd, uint8_t flags) { - LED_A_ON(); - bool pwd_mode = (flags & 0x1); - uint8_t page = (flags & 0x2) >> 1; - bool test_mode = (flags & 0x4) >> 2; - uint32_t i = 0; +//void T55xxWriteBlockExt(uint32_t data, uint8_t blockno, uint32_t pwd, uint8_t flags) { +void T55xxWriteBlock(uint8_t *data) { - // Set up FPGA, 125kHz - LFSetupFPGAForADC(95, true); + /* + flag bits + xxxxxxx1 0x01 PwdMode + xxxxxx1x 0x02 Page + xxxxx1xx 0x04 testMode + xxx11xxx 0x18 downlink mode + xx1xxxxx 0x20 !reg_readmode + x1xxxxxx 0x40 called for a read, so no data packet + 1xxxxxxx 0x80 reset + */ + + t55xx_write_block_t *c = (t55xx_write_block_t *)data; + // c->data, c->blockno, c->pwd, c->flags - // make sure tag is fully powered up... - WaitMS(4); + bool testMode = ((c->flags & 0x04) == 0x04); - // Trigger T55x7 in mode. - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - WaitUS(t_config.start_gap); + c->flags &= (0xff ^ 0x40); // Called for a write, so ensure it is clear/0 + + LED_A_ON (); + T55xx_SendCMD (c->data, c->pwd, c->flags | (c->blockno << 9)) ;//, false); - if (test_mode) { - Dbprintf("T55xx writing with %s", _YELLOW_("test mode enabled")); - // undocmented testmode opcode 01 - T55xxWriteBit(0); - T55xxWriteBit(1); - } else { - // std opcode 10 == page 0 - // std opcode 11 == page 1 - T55xxWriteBit(1); - T55xxWriteBit(page); - } + // Perform write (nominal is 5.6 ms for T55x7 and 18ms for E5550, + // so wait a little more) - if (pwd_mode) { - // Send pwd - for (i = 0x80000000; i != 0; i >>= 1) - T55xxWriteBit(pwd & i); - } - // Send lock bit - T55xxWriteBit(0); + // "there is a clock delay before programming" + // - programming takes ~5.6ms for t5577 ~18ms for E5550 or t5567 + // so we should wait 1 clock + 5.6ms then read response? + // but we need to know we are dealing with t5577 vs t5567 vs e5550 (or q5) marshmellow... + if (testMode) { + //TESTMODE TIMING TESTS: + // <566us does nothing + // 566-568 switches between wiping to 0s and doing nothing + // 5184 wipes and allows 1 block to be programmed. + // indefinite power on wipes and then programs all blocks with bitshifted data sent. + TurnReadLFOn(5184); - // Send data - for (i = 0x80000000; i != 0; i >>= 1) - T55xxWriteBit(data & i); + } else { + TurnReadLFOn(20 * 1000); + //could attempt to do a read to confirm write took + // as the tag should repeat back the new block + // until it is reset, but to confirm it we would + // need to know the current block 0 config mode for + // modulation clock an other details to demod the response... + // response should be (for t55x7) a 0 bit then (ST if on) + // block data written in on repeat until reset. - // Send block number - for (i = 0x04; i != 0; i >>= 1) - T55xxWriteBit(blockno & i); + //DoPartialAcquisition(20, true, 12000); + } + // turn field off + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - // Perform write (nominal is 5.6 ms for T55x7 and 18ms for E5550, - // so wait a little more) - - // "there is a clock delay before programming" - // - programming takes ~5.6ms for t5577 ~18ms for E5550 or t5567 - // so we should wait 1 clock + 5.6ms then read response? - // but we need to know we are dealing with t5577 vs t5567 vs e5550 (or q5) marshmellow... - if (test_mode) { - //TESTMODE TIMING TESTS: - // <566us does nothing - // 566-568 switches between wiping to 0s and doing nothing - // 5184 wipes and allows 1 block to be programmed. - // indefinite power on wipes and then programs all blocks with bitshifted data sent. - TurnReadLFOn(5184); - - } else { - TurnReadLFOn(20 * 1000); - - //could attempt to do a read to confirm write took - // as the tag should repeat back the new block - // until it is reset, but to confirm it we would - // need to know the current block 0 config mode for - // modulation clock an other details to demod the response... - // response should be (for t55x7) a 0 bit then (ST if on) - // block data written in on repeat until reset. - - //DoPartialAcquisition(20, true, 12000); - } - - // turn field off - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - LED_A_OFF(); + // cmd_send(CMD_ACK,0,0,0,0,0); + reply_ng(CMD_T55XX_WRITE_BLOCK, PM3_SUCCESS, NULL, 0); + LED_A_OFF (); } -// Write one card block in page 0, no lock +/* // uses NG format void T55xxWriteBlock(uint8_t *data) { t55xx_write_block_t *c = (t55xx_write_block_t *)data; T55xxWriteBlockExt(c->data, c->blockno, c->pwd, c->flags); - reply_ng(CMD_T55XX_WRITE_BLOCK, PM3_SUCCESS, NULL, 0); + // reply_ng(CMD_T55XX_WRITE_BLOCK, PM3_SUCCESS, NULL, 0); } - +*/ +/* // Read one card block in page [page] -void T55xxReadBlock(uint8_t page, bool pwd_mode, bool brute_mem, uint8_t block, uint32_t pwd) { - LED_A_ON(); - bool regular_readmode = (block == 0xFF); - uint8_t start_wait = 4; - size_t samples = 12000; - uint32_t i; +void T55xxReadBlockExt (uint16_t flags, uint8_t block, uint32_t pwd) { + / * + flag bits + xxxx xxxxxxx1 0x0001 PwdMode + xxxx xxxxxx1x 0x0002 Page + xxxx xxxxx1xx 0x0004 testMode + xxxx xxx11xxx 0x0018 downlink mode + xxxx xx1xxxxx 0x0020 !reg_readmode + xxxx x1xxxxxx 0x0040 called for a read, so no data packet + xxxx 1xxxxxxx 0x0080 reset + xxx1 xxxxxxxx 0x0100 brute/leave field on + * / + size_t samples = 12000; + bool brute_mem = (flags & 0x0100) >> 8; + + LED_A_ON(); - if (brute_mem) { - start_wait = 0; - samples = 1024; - } + if (brute_mem) samples = 1024; + + // Set Read Flag to ensure SendCMD does not add "data" to the packet + flags |= 0x40; - //clear buffer now so it does not interfere with timing later - BigBuf_Clear_keep_EM(); + // RegRead Mode true block = 0xff, so read without an address + if (block == 0xff) flags |= 0x20; + + //make sure block is at max 7 + block &= 0x7; - //make sure block is at max 7 - block &= 0x7; + //clear buffer now so it does not interfere with timing later + BigBuf_Clear_keep_EM(); - // Set up FPGA, 125kHz to power up the tag - LFSetupFPGAForADC(95, true); - // make sure tag is fully powered up... - WaitMS(start_wait); + T55xx_SendCMD (0, pwd, flags | (block << 9)); //, true); - // Trigger T55x7 Direct Access Mode with start gap - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - WaitUS(t_config.start_gap); + // Turn field on to read the response + // 137*8 seems to get to the start of data pretty well... + // but we want to go past the start and let the repeating data settle in... - // Opcode 1[page] - T55xxWriteBit(1); - T55xxWriteBit(page); //Page 0 + // TurnReadLFOn(210*8); // issues with block 1 reads so dropping down seemed to help + TurnReadLFOn(137*8); - if (pwd_mode) { - // Send Pwd - for (i = 0x80000000; i != 0; i >>= 1) - T55xxWriteBit(pwd & i); - } - // Send a zero bit separation - T55xxWriteBit(0); - - // Send Block number (if direct access mode) - if (!regular_readmode) - for (i = 0x04; i != 0; i >>= 1) - T55xxWriteBit(block & i); - - // Turn field on to read the response - // 137*8 seems to get to the start of data pretty well... - // but we want to go past the start and let the repeating data settle in... - TurnReadLFOn(150 * 8); - - // Acquisition - // Now do the acquisition - DoPartialAcquisition(0, true, samples, 0); + // Acquisition + // Now do the acquisition + DoPartialAcquisition(0, true, samples, 0); // Turn the field off if (!brute_mem) { @@ -1543,25 +1765,84 @@ void T55xxReadBlock(uint8_t page, bool pwd_mode, bool brute_mem, uint8_t block, LED_A_OFF(); } } +*/ +// Read one card block in page [page] +void T55xxReadBlock(uint8_t page, bool pwd_mode, bool brute_mem, uint8_t block, uint32_t pwd, uint8_t downlink_mode) { + /* + flag bits + xxxxxxx1 0x0001 PwdMode + xxxxxx1x 0x0002 Page + xxxxx1xx 0x0004 testMode + xxx11xxx 0x0018 downlink mode + xx1xxxxx 0x0020 !reg_readmode + x1xxxxxx 0x0040 called for a read, so no data packet + 1xxxxxxx 0x0080 reset + 1xxxxxxxx 0x0100 brute/leave field on + */ + uint16_t flags = 0x0040; // read packet + if (pwd_mode) flags |= 0x0001; + if (page) flags |= 0x0002; + flags |= (downlink_mode & 3) << 3; + if (brute_mem) flags |= 0x0100; + +// T55xxReadBlockExt (flags,block,pwd); + size_t samples = 12000; + // bool brute_mem = (flags & 0x0100) >> 8; + + LED_A_ON(); -void T55xx_ChkPwds() { + if (brute_mem) samples = 1024; + + //-- Set Read Flag to ensure SendCMD does not add "data" to the packet + //-- flags |= 0x40; + + // RegRead Mode true block = 0xff, so read without an address + if (block == 0xff) flags |= 0x20; + + //make sure block is at max 7 + block &= 0x7; + + //clear buffer now so it does not interfere with timing later + BigBuf_Clear_keep_EM(); + + T55xx_SendCMD (0, pwd, flags | (block << 9)); //, true); + + // Turn field on to read the response + // 137*8 seems to get to the start of data pretty well... + // but we want to go past the start and let the repeating data settle in... + + // TurnReadLFOn(210*8); // issues with block 1 reads so dropping down seemed to help + TurnReadLFOn(137*8); + + // Acquisition + // Now do the acquisition + DoPartialAcquisition(0, true, samples, 0); + + // Turn the field off + if (!brute_mem) { + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + reply_ng(CMD_T55XX_READ_BLOCK, PM3_SUCCESS, NULL, 0); + LED_A_OFF(); + } + +} + +void T55xx_ChkPwds(uint8_t flags) { DbpString("[+] T55XX Check pwds using flashmemory starting"); uint8_t ret = 0; // First get baseline and setup LF mode. // tends to mess up BigBuf - uint8_t *buf = BigBuf_get_addr(); - - uint32_t b1, baseline = 0; - + uint8_t *buf = BigBuf_get_addr(); + uint32_t b1, baseline = 0; + uint8_t downlink_mode = (flags >> 3) & 0x03; + // collect baseline for failed attempt uint8_t x = 32; while (x--) { b1 = 0; - -// T55xxReadBlock(uint8_t page, bool pwd_mode, bool brute_mem, uint8_t block, uint32_t pwd) - T55xxReadBlock(0, 0, true, 1, 0); + T55xxReadBlock(0, 0, true, 1, 0,downlink_mode); for (uint16_t j = 0; j < 1024; ++j) b1 += buf[j]; @@ -1576,8 +1857,8 @@ void T55xx_ChkPwds() { uint8_t *pwds = BigBuf_get_EM_addr(); uint16_t pwdCount = 0; uint32_t candidate = 0; - #ifdef WITH_FLASH + BigBuf_Clear_EM(); uint16_t isok = 0; uint8_t counter[2] = {0x00, 0x00}; @@ -1606,7 +1887,7 @@ void T55xx_ChkPwds() { pwd = bytes_to_num(pwds + i * 4, 4); - T55xxReadBlock(0, true, true, 0, pwd); + T55xxReadBlock(0, true, true, 0, pwd,downlink_mode); // calc mean of BigBuf 1024 samples. uint32_t sum = 0; @@ -1623,9 +1904,7 @@ void T55xx_ChkPwds() { Dbprintf("[=] Pwd %08X | ABS %u", pwd, curr); if (curr > prev) { - - - Dbprintf("[=] --> ABS %u Candidate %08X <--", curr, pwd); + Dbprintf("[=] --> ABS %u Candidate %08X <--", curr, pwd); candidate = pwd; prev = curr; } @@ -1640,36 +1919,31 @@ OUT: LEDsoff(); } -void T55xxWakeUp(uint32_t Pwd) { +void T55xxWakeUp(uint32_t Pwd, uint8_t flags) { + + flags |= 0x01 | 0x40 | 0x20; //Password | Read Call (no data) | reg_read no block LED_B_ON(); - uint32_t i = 0; - // Set up FPGA, 125kHz - LFSetupFPGAForADC(95, true); - // make sure tag is fully powered up... - WaitMS(4); - - // Trigger T55x7 Direct Access Mode - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - WaitUS(t_config.start_gap); - - // Opcode 10 - T55xxWriteBit(1); - T55xxWriteBit(0); //Page 0 - - // Send Pwd - for (i = 0x80000000; i != 0; i >>= 1) - T55xxWriteBit(Pwd & i); - - // Turn and leave field on to let the begin repeating transmission + T55xx_SendCMD (0, Pwd, flags); + + //-- Turn and leave field on to let the begin repeating transmission TurnReadLFOn(20 * 1000); } + /*-------------- Cloning routines -----------*/ void WriteT55xx(uint32_t *blockdata, uint8_t startblock, uint8_t numblocks) { - // write last block first and config block last (if included) - for (uint8_t i = numblocks + startblock; i > startblock; i--) - T55xxWriteBlockExt(blockdata[i - 1], i - 1, 0, 0); + + t55xx_write_block_t cmd; + cmd.pwd = 0; + cmd.flags = 0; + + for (uint8_t i = numblocks + startblock; i > startblock; i--) { + cmd.data = blockdata[i - 1]; + cmd.blockno = i - 1; + T55xxWriteBlock ((uint8_t *)&cmd); + } + } // Copy HID id to card and setup block 0 config diff --git a/client/cmdlft55xx.c b/client/cmdlft55xx.c index 9a23e660b..811a78b70 100644 --- a/client/cmdlft55xx.c +++ b/client/cmdlft55xx.c @@ -43,12 +43,14 @@ static int usage_t55xx_config() { return PM3_SUCCESS; } static int usage_t55xx_read() { - PrintAndLogEx(NORMAL, "Usage: lf t55xx read [b ] [p ] "); + PrintAndLogEx(NORMAL, "Usage: lf t55xx read [r ] b [p ] "); PrintAndLogEx(NORMAL, "Options:"); PrintAndLogEx(NORMAL, " b - block number to read. Between 0-7"); PrintAndLogEx(NORMAL, " p - OPTIONAL password (8 hex characters)"); PrintAndLogEx(NORMAL, " o - OPTIONAL override safety check"); PrintAndLogEx(NORMAL, " 1 - OPTIONAL read Page 1 instead of Page 0"); + PrintAndLogEx(NORMAL, " r - downlink encoding '0' fixed bit length (default), '1' long leading ref."); + PrintAndLogEx(NORMAL, " '2' leading zero, '3' 1 of 4 coding ref."); PrintAndLogEx(NORMAL, " ****WARNING****"); PrintAndLogEx(NORMAL, " Use of read with password on a tag not configured for a pwd"); PrintAndLogEx(NORMAL, " can damage the tag"); @@ -61,13 +63,15 @@ static int usage_t55xx_read() { return PM3_SUCCESS; } static int usage_t55xx_write() { - PrintAndLogEx(NORMAL, "Usage: lf t55xx write [b ] [d ] [p ] [1] [t]"); + PrintAndLogEx(NORMAL, "Usage: lf t55xx write [r ] b d [p ] [1] [t]"); PrintAndLogEx(NORMAL, "Options:"); PrintAndLogEx(NORMAL, " b - block number to write. Between 0-7"); PrintAndLogEx(NORMAL, " d - 4 bytes of data to write (8 hex characters)"); PrintAndLogEx(NORMAL, " p - OPTIONAL password 4bytes (8 hex characters)"); PrintAndLogEx(NORMAL, " 1 - OPTIONAL write Page 1 instead of Page 0"); PrintAndLogEx(NORMAL, " t - OPTIONAL test mode write - ****DANGER****"); + PrintAndLogEx(NORMAL, " r - downlink encoding '0' fixed bit length (default), '1' long leading ref."); + PrintAndLogEx(NORMAL, " '2' leading zero, '3' 1 of 4 coding ref."); PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "Examples:"); PrintAndLogEx(NORMAL, " lf t55xx write b 3 d 11223344 - write 11223344 to block 3"); @@ -76,24 +80,29 @@ static int usage_t55xx_write() { return PM3_SUCCESS; } static int usage_t55xx_trace() { - PrintAndLogEx(NORMAL, "Usage: lf t55xx trace [1]"); + PrintAndLogEx(NORMAL, "Usage: lf t55xx trace [r mode]"); PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " 1 - if set, use Graphbuffer otherwise read data from tag."); + PrintAndLogEx(NORMAL, " r - downlink encoding '0' fixed bit length (default), '1' long leading ref."); + PrintAndLogEx(NORMAL, " '2' leading zero, '3' 1 of 4 coding ref."); + // Command did not seem to support the 1 option (yet) so have removed the help lines + // PrintAndLogEx(NORMAL, " 1 - if set, use Graphbuffer otherwise read data from tag."); PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "Examples:"); PrintAndLogEx(NORMAL, " lf t55xx trace"); - PrintAndLogEx(NORMAL, " lf t55xx trace 1"); + // PrintAndLogEx(NORMAL, " lf t55xx trace 1"); PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } static int usage_t55xx_info() { - PrintAndLogEx(NORMAL, "Usage: lf t55xx info [1] [d [q]]"); + PrintAndLogEx(NORMAL, "Usage: lf t55xx info [1] [r ] [d [q]]"); PrintAndLogEx(NORMAL, "Options:"); PrintAndLogEx(NORMAL, " (default) - read data from tag."); PrintAndLogEx(NORMAL, " 1 - if set, use Graphbuffer instead of reading tag."); PrintAndLogEx(NORMAL, " d - 4 bytes of data (8 hex characters)"); PrintAndLogEx(NORMAL, " if set, use these data instead of reading tag."); PrintAndLogEx(NORMAL, " q - if set, provided data are interpreted as Q5 config."); + PrintAndLogEx(NORMAL, " r - downlink encoding '0' fixed bit length (default), '1' long leading ref."); + PrintAndLogEx(NORMAL, " '2' leading zero, '3' 1 of 4 coding ref."); PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "Examples:"); PrintAndLogEx(NORMAL, " lf t55xx info"); @@ -104,10 +113,12 @@ static int usage_t55xx_info() { return PM3_SUCCESS; } static int usage_t55xx_dump() { - PrintAndLogEx(NORMAL, "Usage: lf t55xx dump [o]"); + PrintAndLogEx(NORMAL, "Usage: lf t55xx dump [r ] [ [o]]"); PrintAndLogEx(NORMAL, "Options:"); PrintAndLogEx(NORMAL, " - OPTIONAL password 4bytes (8 hex symbols)"); PrintAndLogEx(NORMAL, " o - OPTIONAL override, force pwd read despite danger to card"); + PrintAndLogEx(NORMAL, " r - downlink encoding '0' fixed bit length (default), '1' long leading ref."); + PrintAndLogEx(NORMAL, " '2' leading zero, '3' 1 of 4 coding ref."); PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "Examples:"); PrintAndLogEx(NORMAL, " lf t55xx dump"); @@ -116,10 +127,13 @@ static int usage_t55xx_dump() { return PM3_SUCCESS; } static int usage_t55xx_detect() { - PrintAndLogEx(NORMAL, "Usage: lf t55xx detect [1] [p ]"); + PrintAndLogEx(NORMAL, "Usage: lf t55xx detect [1] [r ] [p ]"); PrintAndLogEx(NORMAL, "Options:"); PrintAndLogEx(NORMAL, " 1 - if set, use Graphbuffer otherwise read data from tag."); - PrintAndLogEx(NORMAL, " p - OPTIONAL password (8 hex characters)"); + PrintAndLogEx(NORMAL, " p - downlink encoding '0' fixed bit length (default)"); + PrintAndLogEx(NORMAL, " '1' long leading ref., '2' leading zero "); + PrintAndLogEx(NORMAL, " '3' 1 of 4 coding ref., '4' try all modes"); PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "Examples:"); PrintAndLogEx(NORMAL, " lf t55xx detect"); @@ -130,10 +144,13 @@ static int usage_t55xx_detect() { } static int usage_t55xx_detectP1() { PrintAndLogEx(NORMAL, "Command: Detect Page 1 of a t55xx chip"); - PrintAndLogEx(NORMAL, "Usage: lf t55xx p1detect [1] [p ]"); + PrintAndLogEx(NORMAL, "Usage: lf t55xx p1detect [1] [r ] [p ]"); PrintAndLogEx(NORMAL, "Options:"); PrintAndLogEx(NORMAL, " 1 - if set, use Graphbuffer otherwise read data from tag."); PrintAndLogEx(NORMAL, " p - OPTIONAL password (8 hex characters)"); + PrintAndLogEx(NORMAL, " r - downlink encoding '0' fixed bit length (default)"); + PrintAndLogEx(NORMAL, " '1' long leading ref., '2' leading zero "); + PrintAndLogEx(NORMAL, " '3' 1 of 4 coding ref., '4' try all modes"); PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "Examples:"); PrintAndLogEx(NORMAL, " lf t55xx p1detect"); @@ -143,11 +160,13 @@ static int usage_t55xx_detectP1() { return PM3_SUCCESS; } static int usage_t55xx_wakup() { - PrintAndLogEx(NORMAL, "Usage: lf t55xx wakeup [h] p "); - PrintAndLogEx(NORMAL, "This commands send the Answer-On-Request command and leaves the readerfield ON afterwards."); + PrintAndLogEx(NORMAL, "Usage: lf t55xx wakeup [h] [r ] p "); + PrintAndLogEx(NORMAL, "This commands sends the Answer-On-Request command and leaves the readerfield ON afterwards."); PrintAndLogEx(NORMAL, "Options:"); PrintAndLogEx(NORMAL, " h - this help"); PrintAndLogEx(NORMAL, " p - password 4bytes (8 hex symbols)"); + PrintAndLogEx(NORMAL, " r - downlink encoding '0' fixed bit length (default), '1' long leading ref."); + PrintAndLogEx(NORMAL, " '2' leading zero, '3' 1 of 4 coding ref."); PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "Examples:"); PrintAndLogEx(NORMAL, " lf t55xx wakeup p 11223344 - send wakeup password"); @@ -158,11 +177,14 @@ static int usage_t55xx_chk() { PrintAndLogEx(NORMAL, "press " _YELLOW_("'enter'") " to cancel the command"); PrintAndLogEx(NORMAL, "WARNING: this may brick non-password protected chips!"); PrintAndLogEx(NORMAL, "Try to reading block 7 before\n"); - PrintAndLogEx(NORMAL, "Usage: lf t55xx chk [h] [i <*.dic>]"); + PrintAndLogEx(NORMAL, "Usage: lf t55xx chk [h] [m] [r ] [i <*.dic>]"); PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h - this help"); - PrintAndLogEx(NORMAL, " m - use dictionary from flashmemory\n"); - PrintAndLogEx(NORMAL, " i <*.dic> - loads a default keys dictionary file <*.dic>"); + PrintAndLogEx(NORMAL, " h - this help"); + PrintAndLogEx(NORMAL, " m - use dictionary from flashmemory\n"); + PrintAndLogEx(NORMAL, " r - downlink encoding '0' fixed bit length (default)"); + PrintAndLogEx(NORMAL, " '1' long leading ref., '2' leading zero "); + PrintAndLogEx(NORMAL, " '3' 1 of 4 coding ref., '4' try all modes"); + PrintAndLogEx(NORMAL, " i <*.dic> - loads a default keys dictionary file <*.dic>"); PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "Examples:"); PrintAndLogEx(NORMAL, " lf t55xx chk m"); @@ -174,16 +196,19 @@ static int usage_t55xx_bruteforce() { PrintAndLogEx(NORMAL, "This command uses bruteforce to scan a number range"); PrintAndLogEx(NORMAL, "press " _YELLOW_("'enter'") " to cancel the command"); PrintAndLogEx(NORMAL, "WARNING: this may brick non-password protected chips!"); - PrintAndLogEx(NORMAL, "Try to reading block 7 before\n"); - PrintAndLogEx(NORMAL, "Usage: lf t55xx bruteforce [h] "); + PrintAndLogEx(NORMAL, "Try reading block 7 before\n"); + PrintAndLogEx(NORMAL, "Usage: lf t55xx bruteforce [h] [r ] "); PrintAndLogEx(NORMAL, " password must be 4 bytes (8 hex symbols)"); PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h - this help"); - PrintAndLogEx(NORMAL, " - 4 byte hex value to start pwd search at"); - PrintAndLogEx(NORMAL, " - 4 byte hex value to end pwd search at"); + PrintAndLogEx(NORMAL, " h - this help"); + PrintAndLogEx(NORMAL, " r - downlink encoding '0' fixed bit length (default)"); + PrintAndLogEx(NORMAL, " '1' long leading ref., '2' leading zero "); + PrintAndLogEx(NORMAL, " '3' 1 of 4 coding ref., '4' try all modes"); + PrintAndLogEx(NORMAL, " - 4 byte hex value to start pwd search at"); + PrintAndLogEx(NORMAL, " - 4 byte hex value to end pwd search at"); PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " lf t55xx bruteforce aaaaaa77 aaaaaa99"); + PrintAndLogEx(NORMAL, " lf t55xx bruteforce r 2 aaaaaa77 aaaaaa99"); PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } @@ -191,17 +216,20 @@ static int usage_t55xx_recoverpw() { PrintAndLogEx(NORMAL, "This command uses a few tricks to try to recover mangled password"); PrintAndLogEx(NORMAL, "press " _YELLOW_("'enter'") " to cancel the command"); PrintAndLogEx(NORMAL, "WARNING: this may brick non-password protected chips!"); - PrintAndLogEx(NORMAL, "Try to reading block 7 before\n"); - PrintAndLogEx(NORMAL, "Usage: lf t55xx recoverpw [password]"); + PrintAndLogEx(NORMAL, "Try reading block 7 before\n"); + PrintAndLogEx(NORMAL, "Usage: lf t55xx recoverpw [r ] [password]"); PrintAndLogEx(NORMAL, " password must be 4 bytes (8 hex symbols)"); PrintAndLogEx(NORMAL, " default password is 51243648, used by many cloners"); PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h - this help"); - PrintAndLogEx(NORMAL, " [password] - 4 byte hex value of password written by cloner"); + PrintAndLogEx(NORMAL, " h - this help"); + PrintAndLogEx(NORMAL, " r - downlink encoding '0' fixed bit length (default)"); + PrintAndLogEx(NORMAL, " '1' long leading ref., '2' leading zero "); + PrintAndLogEx(NORMAL, " '3' 1 of 4 coding ref., '4' try all modes"); + PrintAndLogEx(NORMAL, " [password] - 4 byte hex value of password written by cloner"); PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "Examples:"); PrintAndLogEx(NORMAL, " lf t55xx recoverpw"); - PrintAndLogEx(NORMAL, " lf t55xx recoverpw 51243648"); + PrintAndLogEx(NORMAL, " lf t55xx r 3 recoverpw 51243648"); PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } @@ -218,17 +246,22 @@ static int usage_t55xx_wipe() { return PM3_SUCCESS; } static int usage_lf_deviceconfig() { - PrintAndLogEx(NORMAL, "Sets t55x7 timings for direkt commands. The timings are set here in Field Clocks (FC), \nwhich is converted to (US) on device"); - PrintAndLogEx(NORMAL, "Usage: lf t55xx deviceconfig a b c d e p"); + PrintAndLogEx(NORMAL, "Sets t55x7 timings for direct commands. The timings are set here in Field Clocks (FC), \nwhich is converted to (US) on device"); + PrintAndLogEx(NORMAL, "Usage: lf t55xx deviceconfig [r ] a b c d e f g [p]"); PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h - This help"); - PrintAndLogEx(NORMAL, " a <8..255> - Set start gap"); - PrintAndLogEx(NORMAL, " b <8..255> - Set write gap"); - PrintAndLogEx(NORMAL, " c <8..255> - Set write ZERO gap"); - PrintAndLogEx(NORMAL, " d <8..255> - Set write ONE gap"); - PrintAndLogEx(NORMAL, " e <8..255> - Set read gap"); - PrintAndLogEx(NORMAL, " p - persist to flashmemory"); - PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, " h - This help"); + PrintAndLogEx(NORMAL, " a <8..255> - Set start gap"); + PrintAndLogEx(NORMAL, " b <8..255> - Set write gap"); + PrintAndLogEx(NORMAL, " c <8..255> - Set write ZERO gap"); + PrintAndLogEx(NORMAL, " d <8..255> - Set write ONE gap"); + PrintAndLogEx(NORMAL, " e <8..255> - Set write TWO gap (1 of 4 only)"); + PrintAndLogEx(NORMAL, " f <8..255> - Set write THREE gap (1 of 4 only)"); + PrintAndLogEx(NORMAL, " g <8..255> - Set read gap"); + PrintAndLogEx(NORMAL, " p - persist to flashmemory"); + PrintAndLogEx(NORMAL, " r - downlink encoding '0' fixed bit length (default), '1' long leading ref."); + PrintAndLogEx(NORMAL, " '2' leading zero, '3' 1 of 4 coding ref."); + PrintAndLogEx(NORMAL, " z - erase t55x7 timings (needs p and reboot to load defaults)"); + PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "Examples:"); PrintAndLogEx(NORMAL, " lf t55xx deviceconfig a 29 b 17 c 15 d 47 e 15 - default T55XX"); PrintAndLogEx(NORMAL, " lf t55xx deviceconfig a 55 b 14 c 21 d 30 - default EM4305"); @@ -359,13 +392,12 @@ static int CmdT55xxSetConfig(const char *Cmd) { return printConfiguration(config); } -int T55xxReadBlock(uint8_t block, bool page1, bool usepwd, bool override, uint32_t password) { +int T55xxReadBlock(uint8_t block, bool page1, bool usepwd, uint8_t override, uint32_t password, uint8_t downlink_mode) { //Password mode if (usepwd) { // try reading the config block and verify that PWD bit is set before doing this! if (!override) { - - if (!AquireData(T55x7_PAGE0, T55x7_CONFIGURATION_BLOCK, false, 0)) return PM3_ESOFT; + if (!AquireData(T55x7_PAGE0, T55x7_CONFIGURATION_BLOCK, false, 0,downlink_mode)) return PM3_ESOFT; if (!tryDetectModulation()) { PrintAndLogEx(NORMAL, "Safety Check: Could not detect if PWD bit is set in config block. Exits."); @@ -376,11 +408,14 @@ int T55xxReadBlock(uint8_t block, bool page1, bool usepwd, bool override, uint32 page1 = false; } } else { + // Show only if first for command i.e. override = 1 (override and display) override = 2 (override and dont display) + if ((override & 2) != 2) PrintAndLogEx(NORMAL, "Safety Check Overriden - proceeding despite risk"); } } - if (!AquireData(page1, block, usepwd, password)) return PM3_ESOFT; + + if (!AquireData(page1, block, usepwd, password, downlink_mode)) return PM3_ESOFT; if (!DecodeT55xxBlock()) return PM3_ESOFT; printT55xxBlock(block); @@ -388,13 +423,15 @@ int T55xxReadBlock(uint8_t block, bool page1, bool usepwd, bool override, uint32 } static int CmdT55xxReadBlock(const char *Cmd) { - uint8_t block = REGULAR_READ_MODE_BLOCK; - uint32_t password = 0; //default to blank Block 7 - bool usepwd = false; - bool override = false; - bool page1 = false; - bool errors = false; - uint8_t cmdp = 0; + uint8_t block = REGULAR_READ_MODE_BLOCK; + uint32_t password = 0; //default to blank Block 7 + bool usepwd = false; + bool override = false; + bool page1 = false; + bool errors = false; + uint8_t cmdp = 0; + uint8_t downlink_mode = 0; + while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { switch (tolower(param_getchar(Cmd, cmdp))) { case 'h': @@ -416,6 +453,13 @@ static int CmdT55xxReadBlock(const char *Cmd) { page1 = true; cmdp++; break; + case 'r': + case 'R': + downlink_mode = param_getchar(Cmd, cmdp+1) - '0'; + if (downlink_mode > 3) downlink_mode = 0; + cmdp +=2; + break; + default: PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); errors = true; @@ -430,7 +474,7 @@ static int CmdT55xxReadBlock(const char *Cmd) { } printT5xxHeader(page1); - return T55xxReadBlock(block, page1, usepwd, override, password); + return T55xxReadBlock(block, page1, usepwd, override, password, downlink_mode); } bool DecodeT55xxBlock(void) { @@ -512,11 +556,34 @@ static int SanityOfflineCheck(bool useGraphBuffer) { return PM3_SUCCESS; } +void T55xx_Print_DownlinkMode (uint8_t downlink_mode) +{ + char Msg[80]; + sprintf (Msg,"Downlink Mode used : "); + + switch (downlink_mode) { + case 0 : strcat (Msg,"default/fixed bit length"); break; + case 1 : strcat (Msg,"long leading reference (r 1)"); break; + case 2 : strcat (Msg,"leading zero reference (r 2)"); break; + case 3 : strcat (Msg,"1 of 4 coding reference (r 3)"); break; + default : + strcat (Msg,"default/fixed bit length"); break; + } + + PrintAndLogEx (NORMAL,Msg); +} +// static int CmdT55xxDetect(const char *Cmd) { - bool errors = false; - bool useGB = false, usepwd = false; - uint32_t password = 0; - uint8_t cmdp = 0; + + bool errors = false; + bool useGB = false; + bool usepwd = false; + bool try_all_dl_modes = false; + bool found = false; + uint32_t password = 0; + uint8_t cmdp = 0; + uint8_t downlink_mode = 0; + uint8_t dl_mode = 0; while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { switch (tolower(param_getchar(Cmd, cmdp))) { @@ -532,6 +599,12 @@ static int CmdT55xxDetect(const char *Cmd) { useGB = true; cmdp++; break; + case 'r': + downlink_mode = param_getchar(Cmd, cmdp+1) - '0'; + if (downlink_mode == 4) try_all_dl_modes = true; + if (downlink_mode > 3) downlink_mode = 0; + cmdp +=2; + break; default: PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); errors = true; @@ -544,16 +617,47 @@ static int CmdT55xxDetect(const char *Cmd) { if (SanityOfflineCheck(useGB) != PM3_SUCCESS) return PM3_ENODATA; if (!useGB) { - if (!AquireData(T55x7_PAGE0, T55x7_CONFIGURATION_BLOCK, usepwd, password)) - return PM3_ENODATA; + for (dl_mode = downlink_mode; dl_mode < 4; dl_mode++) { + found = AquireData(T55x7_PAGE0, T55x7_CONFIGURATION_BLOCK, usepwd, password,dl_mode); + + // found = false if password is supplied but wrong d/l mode + // so keep trying other modes (if requested) + /* if (!found) { + printf ("Aquire not found"); + return PM3_ENODATA; + } + */ + if (tryDetectModulation()) + { + T55xx_Print_DownlinkMode (dl_mode); + dl_mode = 4; + found = true; + } + else found = false; + + if (!try_all_dl_modes) dl_mode = 4; + } } + + if (useGB) found = tryDetectModulation(); + + if (!found) + PrintAndLogEx(WARNING, "Could not detect modulation automatically. Try setting it manually with " _YELLOW_("\'lf t55xx config\'")); + + + /* if (!useGB) { + if (!AquireData(T55x7_PAGE0, T55x7_CONFIGURATION_BLOCK, usepwd, password,downlink_mode)) + return PM3_ENODATA; + } if (!tryDetectModulation()) PrintAndLogEx(WARNING, "Could not detect modulation automatically. Try setting it manually with " _YELLOW_("\'lf t55xx config\'")); + else + T55xx_Print_DownlinkMode (downlink_mode); +*/ return PM3_SUCCESS; } - // detect configuration? bool tryDetectModulation(void) { @@ -997,10 +1101,14 @@ int printConfiguration(t55xx_conf_block_t b) { } static int CmdT55xxWakeUp(const char *Cmd) { - uint32_t password = 0; - uint8_t cmdp = 0; - bool errors = false; - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { + + uint32_t password = 0; + uint8_t cmdp = 0; + bool errors = false; + uint8_t downlink_mode = 0; + uint8_t flags = 0; + + while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { switch (tolower(param_getchar(Cmd, cmdp))) { case 'h': return usage_t55xx_wakup(); @@ -1009,30 +1117,40 @@ static int CmdT55xxWakeUp(const char *Cmd) { cmdp += 2; errors = false; break; + case 'r': + downlink_mode = param_getchar(Cmd, cmdp+1) - '0'; + if (downlink_mode > 3) downlink_mode = 0; + cmdp +=2; + break; default: PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); errors = true; break; } } + if (errors) return usage_t55xx_wakup(); + flags = (downlink_mode & 3) << 3; clearCommandBuffer(); - SendCommandMIX(CMD_T55XX_WAKEUP, password, 0, 0, NULL, 0); + SendCommandMIX(CMD_T55XX_WAKEUP, password, flags, 0, NULL, 0); PrintAndLogEx(SUCCESS, "Wake up command sent. Try read now"); + return PM3_SUCCESS; } static int CmdT55xxWriteBlock(const char *Cmd) { - uint8_t block = 0xFF; //default to invalid block - uint32_t data = 0; //default to blank Block - uint32_t password = 0; //default to blank Block 7 - bool usepwd = false; - bool page1 = false; - bool gotdata = false; - bool testMode = false; - bool errors = false; - uint8_t cmdp = 0; + uint8_t block = 0xFF; //default to invalid block + uint32_t data = 0; //default to blank Block + uint32_t password = 0; //default to blank Block 7 + bool usepwd = false; + bool page1 = false; + bool gotdata = false; + bool testMode = false; + bool errors = false; + uint8_t cmdp = 0; + uint32_t downlink_mode = 0; + while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { switch (tolower(param_getchar(Cmd, cmdp))) { case 'h': @@ -1064,6 +1182,11 @@ static int CmdT55xxWriteBlock(const char *Cmd) { page1 = true; cmdp++; break; + case 'r': + downlink_mode = param_getchar(Cmd, cmdp+1) - '0'; + if (downlink_mode > 3) downlink_mode = 0; + cmdp +=2; + break; default: PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); errors = true; @@ -1074,10 +1197,11 @@ static int CmdT55xxWriteBlock(const char *Cmd) { PacketResponseNG resp; uint8_t flags; - flags = (usepwd) ? 0x1 : 0; - flags |= (page1) ? 0x2 : 0; + flags = (usepwd) ? 0x1 : 0; + flags |= (page1) ? 0x2 : 0; flags |= (testMode) ? 0x4 : 0; - + flags |= (downlink_mode << 3); + char pwdStr[16] = {0}; snprintf(pwdStr, sizeof(pwdStr), "pwd: 0x%08X", password); @@ -1096,10 +1220,10 @@ static int CmdT55xxWriteBlock(const char *Cmd) { uses struct in pm3_cmd.h */ t55xx_write_block_t ng; - ng.data = data; - ng.pwd = password; + ng.data = data; + ng.pwd = password; ng.blockno = block; - ng.flags = flags; + ng.flags = flags; SendCommandNG(CMD_T55XX_WRITE_BLOCK, (uint8_t *)&ng, sizeof(ng)); if (!WaitForResponseTimeout(CMD_T55XX_WRITE_BLOCK, &resp, 2000)) { @@ -1110,16 +1234,26 @@ static int CmdT55xxWriteBlock(const char *Cmd) { } static int CmdT55xxReadTrace(const char *Cmd) { + uint8_t cmd_len = 0; + uint8_t downlink_mode = 0; + char cmdp = tolower(param_getchar(Cmd, 0)); - if (strlen(Cmd) > 1 || cmdp == 'h') return usage_t55xx_trace(); - - if (strlen(Cmd) == 0) { + if (cmdp == 'r') { + downlink_mode = param_getchar(Cmd, 1) - '0'; + if (downlink_mode > 3) downlink_mode = 0; + cmd_len = 3; + } + if ((strlen(Cmd) != cmd_len) || (cmdp == 'h')) return usage_t55xx_trace(); + + if (strlen(Cmd) == cmd_len) { // sanity check. if (SanityOfflineCheck(false) != PM3_SUCCESS) return PM3_ENODATA; bool pwdmode = false; uint32_t password = 0; - if (!AquireData(T55x7_PAGE1, T55x7_TRACE_BLOCK1, pwdmode, password)) +// REGULAR_READ_MODE_BLOCK - yeilds correct Page 1 Block 2 data i.e. + 32 bit offset. +// if (!AquireData(T55x7_PAGE1, T55x7_TRACE_BLOCK1, pwdmode, password,downlink_mode)) + if (!AquireData(T55x7_PAGE1, REGULAR_READ_MODE_BLOCK, pwdmode, password,downlink_mode)) return PM3_ENODATA; } @@ -1361,10 +1495,11 @@ static int CmdT55xxInfo(const char *Cmd) { Normal mode Extended mode */ - bool frombuff = false, gotdata = false, dataasq5 = false; - uint8_t cmdp = 0; - uint32_t block0 = 0; - + bool frombuff = false, gotdata = false, dataasq5 = false; + uint8_t cmdp = 0; + uint8_t downlink_mode = 0; + uint32_t block0 = 0; + while (param_getchar(Cmd, cmdp) != 0x00) { switch (tolower(param_getchar(Cmd, cmdp))) { case 'h': @@ -1382,6 +1517,11 @@ static int CmdT55xxInfo(const char *Cmd) { dataasq5 = true; cmdp += 2; break; + case 'r': + downlink_mode = param_getchar(Cmd, cmdp+1)- '0'; + if (downlink_mode > 3) downlink_mode = 0; + cmdp += 2; + break; default: PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); return usage_t55xx_info(); @@ -1400,7 +1540,7 @@ static int CmdT55xxInfo(const char *Cmd) { bool pwdmode = false; uint32_t password = 0; - if (!AquireData(T55x7_PAGE0, T55x7_CONFIGURATION_BLOCK, pwdmode, password)) + if (!AquireData(T55x7_PAGE0, T55x7_CONFIGURATION_BLOCK, pwdmode, password,downlink_mode)) return PM3_ENODATA; } if (!gotdata) { @@ -1495,30 +1635,43 @@ static int CmdT55xxInfo(const char *Cmd) { static int CmdT55xxDump(const char *Cmd) { - uint32_t password = 0; - bool override = false; - char cmdp = tolower(param_getchar(Cmd, 0)); - if (cmdp == 'h') return usage_t55xx_dump(); + uint32_t password = 0; + uint8_t override = false; + uint8_t cmd_opt_idx = 0; + uint8_t downlink_mode = 0; + uint8_t pwd_offset = 0; + char cmdp = tolower(param_getchar(Cmd, 0)); + - bool usepwd = (strlen(Cmd) > 0); + if (cmdp == 'h') return usage_t55xx_dump(); + if (cmdp == 'r') { + cmd_opt_idx++; + downlink_mode = param_getchar(Cmd, cmd_opt_idx++) - '0'; + if (downlink_mode > 3) downlink_mode = 0; + pwd_offset = 3; + } + bool usepwd = (strlen(Cmd) > pwd_offset); if (usepwd) { - password = param_get32ex(Cmd, 0, 0, 16); - if (param_getchar(Cmd, 1) == 'o') + password = param_get32ex(Cmd, cmd_opt_idx++, 0, 16); + if (param_getchar(Cmd, cmd_opt_idx++) == 'o') override = true; } printT5xxHeader(0); - for (uint8_t i = 0; i < 8; ++i) - T55xxReadBlock(i, 0, usepwd, override, password); - + for (uint8_t i = 0; i < 8; ++i) { + T55xxReadBlock(i, 0, usepwd, override, password,downlink_mode); + // idea for better user experience and display. + // only show override warning on the first block read + if (override) override |= 2; // flag not to show safty for 2nd and on. + } printT5xxHeader(1); for (uint8_t i = 0; i < 4; i++) - T55xxReadBlock(i, 1, usepwd, override, password); + T55xxReadBlock(i, 1, usepwd, override, password,downlink_mode); return PM3_SUCCESS; } -bool AquireData(uint8_t page, uint8_t block, bool pwdmode, uint32_t password) { +bool AquireData(uint8_t page, uint8_t block, bool pwdmode, uint32_t password, uint8_t downlink_mode) { // arg0 bitmodes: // b0 = pwdmode // b1 = page to read from @@ -1527,15 +1680,17 @@ bool AquireData(uint8_t page, uint8_t block, bool pwdmode, uint32_t password) { // arg2: password struct p { uint32_t password; - uint8_t blockno; - uint8_t page; - bool pwdmode; + uint8_t blockno; + uint8_t page; + bool pwdmode; + uint8_t downlink_mode; } PACKED; struct p payload; - payload.password = password; - payload.blockno = block; - payload.page = page & 0x1; - payload.pwdmode = pwdmode; + payload.password = password; + payload.blockno = block; + payload.page = page & 0x1; + payload.pwdmode = pwdmode; + payload.downlink_mode = downlink_mode; clearCommandBuffer(); SendCommandNG(CMD_T55XX_READ_BLOCK, (uint8_t *)&payload, sizeof(payload)); @@ -1795,9 +1950,20 @@ static void t55x7_create_config_block(int tagtype) { */ static int CmdResetRead(const char *Cmd) { - (void)Cmd; // Cmd is not used so far + + uint8_t downlink_mode = 0; + uint8_t flags = 0; + + + if (strlen (Cmd) == 3) + downlink_mode = param_getchar(Cmd, 1) - '0'; + + if (downlink_mode > 3) downlink_mode = 0; + + printf ("DL : %d\n",downlink_mode); + flags = downlink_mode << 3; clearCommandBuffer(); - SendCommandNG(CMD_T55XX_RESET_READ, NULL, 0); + SendCommandNG(CMD_T55XX_RESET_READ, &flags, sizeof(flags)); if (!WaitForResponseTimeout(CMD_ACK, NULL, 2500)) { PrintAndLogEx(WARNING, "command execution time out"); return PM3_ETIMEOUT; @@ -1853,13 +2019,44 @@ static bool IsCancelled(void) { // load a default pwd file. static int CmdT55xxChkPwds(const char *Cmd) { - char filename[FILE_PATH_SIZE] = {0}; - bool found = false; - uint8_t timeout = 0; - uint8_t *keyBlock = NULL; - - char cmdp = tolower(param_getchar(Cmd, 0)); - if (strlen(Cmd) == 0 || cmdp == 'h') return usage_t55xx_chk(); + char filename[FILE_PATH_SIZE] = {0}; + bool found = false; + uint8_t timeout = 0; + uint8_t *keyBlock = NULL; + bool from_flash = false; + bool try_all_dl_modes = false; + uint8_t downlink_mode = 0; + int len; + char cmdp; + bool use_pwd_file = false; + int dl_mode; // to try each downlink mode for each password + + + cmdp = tolower(param_getchar(Cmd,0)); + + if (cmdp == 'h') return usage_t55xx_chk(); + if (cmdp == 'm') { + from_flash = true; + Cmd +=2; + cmdp = tolower(param_getchar(Cmd,0)); + } + if (cmdp == 'r') { + Cmd += 2; + downlink_mode = param_getchar(Cmd,0 ) - '0'; // get 2nd option, as this is fixed order. + if (downlink_mode == 4) try_all_dl_modes = true; + if (downlink_mode > 3) downlink_mode = 0; + Cmd += 2; + cmdp = param_getchar(Cmd,0); + } + if (cmdp == 'i') { + Cmd+=2; + len = strlen(Cmd); + if (len > FILE_PATH_SIZE) len = FILE_PATH_SIZE; + memcpy(filename, Cmd, len); + use_pwd_file = true; + } + + /* // block 7, page1 = false, usepwd = false, override = false, pwd = 00000000 @@ -1872,10 +2069,11 @@ static int CmdT55xxChkPwds(const char *Cmd) { */ uint64_t t1 = msclock(); - - if (cmdp == 'm') { + uint8_t flags = downlink_mode << 3; + + if (from_flash) { clearCommandBuffer(); - SendCommandNG(CMD_T55XX_CHKPWDS, NULL, 0); + SendCommandNG(CMD_T55XX_CHKPWDS, &flags, sizeof(flags)); PacketResponseNG resp; while (!WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { @@ -1891,10 +2089,12 @@ static int CmdT55xxChkPwds(const char *Cmd) { if (resp.oldarg[0]) { PrintAndLogEx(SUCCESS, "\nFound a candidate [ " _YELLOW_("%08X") " ]. Trying to validate", resp.oldarg[1]); - if (AquireData(T55x7_PAGE0, T55x7_CONFIGURATION_BLOCK, true, resp.oldarg[1])) { + if (AquireData(T55x7_PAGE0, T55x7_CONFIGURATION_BLOCK, true, resp.oldarg[1],downlink_mode)) { found = tryDetectModulation(); if (found) { - PrintAndLogEx(SUCCESS, "Found valid password: [ " _GREEN_("%08") " ]", resp.oldarg[1]); + PrintAndLogEx(SUCCESS, "Found valid password: [ " _GREEN_("%08X") " ]", resp.oldarg[1]); + T55xx_Print_DownlinkMode (downlink_mode); + } else { PrintAndLogEx(WARNING, "Check pwd failed"); } @@ -1907,12 +2107,7 @@ static int CmdT55xxChkPwds(const char *Cmd) { goto out; } - if (cmdp == 'i') { - - int len = strlen(Cmd + 2); - if (len > FILE_PATH_SIZE) len = FILE_PATH_SIZE; - memcpy(filename, Cmd + 2, len); - + if (use_pwd_file) { uint16_t keycount = 0; size_t datalen = 0; @@ -1948,19 +2143,25 @@ static int CmdT55xxChkPwds(const char *Cmd) { curr_password = bytes_to_num(keyBlock + 4 * c, 4); PrintAndLogEx(INFO, "Testing %08X", curr_password); + for (dl_mode = downlink_mode; dl_mode <= 3; dl_mode++){ + + if (!AquireData(T55x7_PAGE0, T55x7_CONFIGURATION_BLOCK, true, curr_password,dl_mode)) { + continue; + } - if (!AquireData(T55x7_PAGE0, T55x7_CONFIGURATION_BLOCK, true, curr_password)) { - continue; - } - - found = tryDetectModulation(); - if (found) - break; - } - if (found) - PrintAndLogEx(SUCCESS, "Found valid password: [ " _GREEN_("%08X") " ]", curr_password); - else - PrintAndLogEx(WARNING, "Check pwd failed"); + found = tryDetectModulation(); + if (found) { + PrintAndLogEx(SUCCESS, "Found valid password: [ " _GREEN_("%08X") " ]", curr_password); + T55xx_Print_DownlinkMode (dl_mode); + dl_mode = 4; // Exit other downlink mode checks + c = keycount; // Exit loop + } + + if (!try_all_dl_modes) // Exit loop if not trying all downlink modes + dl_mode = 4; + } + } + if (!found) PrintAndLogEx(WARNING, "Check pwd failed"); } free(keyBlock); @@ -1976,16 +2177,25 @@ static int CmdT55xxBruteForce(const char *Cmd) { uint32_t start_password = 0x00000000; //start password uint32_t end_password = 0xFFFFFFFF; //end password - uint32_t curr = 0; - bool found = false; + uint32_t curr = 0; + uint8_t downlink_mode = 0; + uint8_t cmd_opt_idx = 0; + uint8_t found = 0; // > 0 if found xx1 xx downlink needed, 1 found + + char cmdp = tolower(param_getchar(Cmd, cmd_opt_idx)); - char cmdp = tolower(param_getchar(Cmd, 0)); if (cmdp == 'h') return usage_t55xx_bruteforce(); + if (cmdp == 'r') { // downlink mode supplied + cmd_opt_idx++; // skip over 'r' + downlink_mode = param_getchar (Cmd,cmd_opt_idx++) - '0'; + if (downlink_mode > 4) downlink_mode = 0; + } + uint64_t t1 = msclock(); - start_password = param_get32ex(Cmd, 0, 0, 16); - end_password = param_get32ex(Cmd, 1, 0, 16); + start_password = param_get32ex(Cmd, cmd_opt_idx++, 0, 16); + end_password = param_get32ex(Cmd, cmd_opt_idx++, 0, 16); curr = start_password; @@ -1995,7 +2205,7 @@ static int CmdT55xxBruteForce(const char *Cmd) { PrintAndLogEx(INFO, "Search password range [%08X -> %08X]", start_password, end_password); - while (!found) { + while (found == 0) { printf("."); fflush(stdout); @@ -2004,7 +2214,7 @@ static int CmdT55xxBruteForce(const char *Cmd) { return PM3_EOPABORTED; } - found = tryOnePassword(curr); + found = tryOnePassword(curr,downlink_mode); if (curr == end_password) break; @@ -2014,47 +2224,72 @@ static int CmdT55xxBruteForce(const char *Cmd) { PrintAndLogEx(NORMAL, ""); - if (found) - PrintAndLogEx(SUCCESS, "Found valid password: [ " _GREEN_("%08X") " ]", curr); + if (found) { + PrintAndLogEx(SUCCESS, "Found valid password: [ " _GREEN_("%08X") "]", curr-1); + T55xx_Print_DownlinkMode ((found >> 1) & 3); + } else - PrintAndLogEx(WARNING, "Bruteforce failed, last tried: [ " _YELLOW_("%08X") " ]", --curr); + PrintAndLogEx(WARNING, "Bruteforce failed, last tried: [ " _YELLOW_("%08X") " ]", curr); t1 = msclock() - t1; PrintAndLogEx(SUCCESS, "\nTime in bruteforce: %.0f seconds\n", (float)t1 / 1000.0); return PM3_SUCCESS; } -int tryOnePassword(uint32_t password) { - PrintAndLogEx(INFO, "Trying password %08X", password); +uint8_t tryOnePassword(uint32_t password, uint8_t downlink_mode) { + + bool try_all_dl_modes = false; + uint8_t dl_mode = 0; + + PrintAndLogEx(INFO, "Trying password %08X", password); - AquireData(T55x7_PAGE0, T55x7_CONFIGURATION_BLOCK, true, password); + if (downlink_mode == 4) try_all_dl_modes = true; + + downlink_mode = (downlink_mode & 3); // ensure 0-3 + + // check if dl mode 4 and loop if needed + for (dl_mode = downlink_mode; dl_mode < 4; dl_mode++){ - if (getSignalProperties()->isnoise == false) - return 0; + AquireData(T55x7_PAGE0, T55x7_CONFIGURATION_BLOCK, true, password,dl_mode); - if (tryDetectModulation()) - return 1; - else - return 0; + // if (getSignalProperties()->isnoise == false) { + // } else { + if (tryDetectModulation()) + { + return 1 + (dl_mode << 1); + } + // } + if (!try_all_dl_modes) dl_mode = 4; + } + return 0; } static int CmdT55xxRecoverPW(const char *Cmd) { - int bit = 0; + int bit = 0; uint32_t orig_password = 0x0; uint32_t curr_password = 0x0; uint32_t prev_password = 0xffffffff; - uint32_t mask = 0x0; - int found = 0; - char cmdp = tolower(param_getchar(Cmd, 0)); - if (cmdp == 'h') return usage_t55xx_recoverpw(); + uint32_t mask = 0x0; + uint8_t downlink_mode = 0; + uint8_t found = 0; + uint8_t cmd_opt_idx = 0; + + char cmdp = tolower(param_getchar(Cmd, cmd_opt_idx)); - orig_password = param_get32ex(Cmd, 0, 0x51243648, 16); //password used by handheld cloners + if (cmdp == 'h') return usage_t55xx_recoverpw(); + if (cmdp == 'r') { // downlink mode supplied + cmd_opt_idx++; // skip over 'r' + downlink_mode = param_getchar (Cmd,cmd_opt_idx++) - '0'; + if (downlink_mode > 4) downlink_mode = 0; + } + + orig_password = param_get32ex(Cmd, cmd_opt_idx++, 0x51243648, 16); //password used by handheld cloners // first try fliping each bit in the expected password while (bit < 32) { curr_password = orig_password ^ (1u << bit); - found = tryOnePassword(curr_password); - if (found == 1) + found = tryOnePassword(curr_password,downlink_mode); + if (found > 0) // xx1 for found xx = dl mode used goto out; bit++; @@ -2078,8 +2313,8 @@ static int CmdT55xxRecoverPW(const char *Cmd) { continue; } - found = tryOnePassword(curr_password); - if (found == 1) + found = tryOnePassword(curr_password,downlink_mode); + if (found > 0) goto out; bit++; @@ -2100,8 +2335,8 @@ static int CmdT55xxRecoverPW(const char *Cmd) { bit++; continue; } - found = tryOnePassword(curr_password); - if (found == 1) + found = tryOnePassword(curr_password,downlink_mode); + if (found > 0) goto out; bit++; @@ -2115,8 +2350,10 @@ out: PrintAndLogEx(NORMAL, ""); - if (found == 1) - PrintAndLogEx(SUCCESS, "Found valid password: [ " _GREEN_("%08X") " ]", curr_password); + if (found > 0) { + PrintAndLogEx(SUCCESS, "Found valid password: [ " _GREEN_("%08X") "]", curr_password); + T55xx_Print_DownlinkMode ((found >> 1) & 3); + } else PrintAndLogEx(WARNING, "Recover pwd failed"); @@ -2127,14 +2364,14 @@ out: // some return all page 1 (64 bits) and others return just that block (32 bits) // unfortunately the 64 bits makes this more likely to get a false positive... bool tryDetectP1(bool getData) { - uint8_t preamble[] = {1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1}; - size_t startIdx = 0; - uint8_t fc1 = 0, fc2 = 0, ans = 0; - int clk = 0, firstClockEdge = 0; - bool st = true; + uint8_t preamble[] = {1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1}; + size_t startIdx = 0; + uint8_t fc1 = 0, fc2 = 0, ans = 0; + int clk = 0, firstClockEdge = 0; + bool st = true; if (getData) { - if (!AquireData(T55x7_PAGE1, T55x7_TRACE_BLOCK1, false, 0)) + if (!AquireData(T55x7_PAGE1, T55x7_TRACE_BLOCK1, false, 0,0)) return false; } @@ -2237,11 +2474,15 @@ bool tryDetectP1(bool getData) { } // does this need to be a callable command? static int CmdT55xxDetectPage1(const char *Cmd) { - bool errors = false; - bool useGB = false; - bool usepwd = false; - uint32_t password = 0; - uint8_t cmdp = 0; + bool errors = false; + bool useGB = false; + bool usepwd = false; + bool try_all_dl_modes = false; + uint8_t found = 0; + uint32_t password = 0; + uint8_t cmdp = 0; + uint8_t downlink_mode = 0; + uint8_t dl_mode = 0; while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { switch (tolower(param_getchar(Cmd, cmdp))) { @@ -2257,6 +2498,12 @@ static int CmdT55xxDetectPage1(const char *Cmd) { useGB = true; cmdp++; break; + case 'r': + downlink_mode = param_getchar(Cmd, cmdp+1) - '0'; + if (downlink_mode == 4) try_all_dl_modes = true; + if (downlink_mode > 3) downlink_mode = 0; + cmdp +=2; + break; default: PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); errors = true; @@ -2266,21 +2513,42 @@ static int CmdT55xxDetectPage1(const char *Cmd) { if (errors) return usage_t55xx_detectP1(); if (!useGB) { - if (!AquireData(T55x7_PAGE1, T55x7_TRACE_BLOCK1, usepwd, password)) - return PM3_ENODATA; - } + for (dl_mode = downlink_mode; dl_mode < 4; dl_mode++) { + found = AquireData(T55x7_PAGE1, T55x7_TRACE_BLOCK1, usepwd, password,dl_mode); + //return PM3_ENODATA; + if (tryDetectP1(false)) //tryDetectModulation()) + { + found = dl_mode; + dl_mode = 4; + } + else found = false; + + if (!try_all_dl_modes) dl_mode = 4; + } + + } + + if (useGB) found = tryDetectP1(false); + + if (found) { + PrintAndLogEx(SUCCESS, "T55xx chip found!"); + T55xx_Print_DownlinkMode (found); + } + else + PrintAndLogEx(WARNING, "Could not detect modulation automatically. Try setting it manually with " _YELLOW_("\'lf t55xx config\'")); - if (tryDetectP1(false)) - PrintAndLogEx(SUCCESS, "T55xx chip found!"); return PM3_SUCCESS; } static int CmdT55xxSetDeviceConfig(const char *Cmd) { - uint8_t startgap = 0, writegap = 0; - uint8_t write0 = 0, write1 = 0, readgap = 0; - bool errors = false, shall_persist = false; - uint8_t cmdp = 0; + uint8_t startgap = 0, writegap = 0, readgap = 0; + uint8_t write0 = 0, write1 = 0, write2 = 0, write3 = 0; + bool errors = false, shall_persist = false; + uint8_t cmdp = 0; + uint8_t downlink_mode = 0; + bool erase = false; + while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { switch (tolower(param_getchar(Cmd, cmdp))) { case 'h': @@ -2302,13 +2570,30 @@ static int CmdT55xxSetDeviceConfig(const char *Cmd) { cmdp += 2; break; case 'e': + errors |= param_getdec(Cmd, cmdp + 1, &write2); + cmdp += 2; + break; + case 'f': + errors |= param_getdec(Cmd, cmdp + 1, &write3); + cmdp += 2; + break; + case 'g': errors |= param_getdec(Cmd, cmdp + 1, &readgap); cmdp += 2; break; + case 'r': + downlink_mode = param_getchar(Cmd, cmdp + 1) - '0'; + if (downlink_mode > 3) downlink_mode = 0; + cmdp += 2; + break; case 'p': shall_persist = true; cmdp++; break; + case 'z': + erase = true; + cmdp++; + break; default: PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); errors = 1; @@ -2319,8 +2604,21 @@ static int CmdT55xxSetDeviceConfig(const char *Cmd) { //Validations if (errors || cmdp == 0) return usage_lf_deviceconfig(); - t55xx_config conf = { startgap * 8, writegap * 8, write0 * 8, write1 * 8, readgap * 8 }; - +// printf ("DLmode %d\n",downlink_mode); + t55xx_config conf = {0}; + if (erase) { + memset (&conf,0xff, sizeof(conf)); + } + else { + + conf.m[downlink_mode].start_gap = startgap;// * 8; + conf.m[downlink_mode].write_gap = writegap;// * 8; + conf.m[downlink_mode].write_0 = write0 ;// * 8; + conf.m[downlink_mode].write_1 = write1 ;// * 8; + conf.m[downlink_mode].write_2 = write2 ;// * 8; + conf.m[downlink_mode].write_3 = write3 ;// * 8; + conf.m[downlink_mode].read_gap = readgap ;// * 8; + } clearCommandBuffer(); SendCommandOLD(CMD_SET_LF_T55XX_CONFIG, shall_persist, 0, 0, &conf, sizeof(t55xx_config)); return PM3_SUCCESS; @@ -2328,9 +2626,9 @@ static int CmdT55xxSetDeviceConfig(const char *Cmd) { static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, - {"bruteforce", CmdT55xxBruteForce, IfPm3Lf, " [i <*.dic>] Simple bruteforce attack to find password"}, + {"bruteforce", CmdT55xxBruteForce, IfPm3Lf, " Simple bruteforce attack to find password"}, {"config", CmdT55xxSetConfig, AlwaysAvailable, "Set/Get T55XX configuration (modulation, inverted, offset, rate)"}, - {"chk", CmdT55xxChkPwds, IfPm3Lf, "Check passwords"}, + {"chk", CmdT55xxChkPwds, IfPm3Lf, "Check passwords from dictionary/flash"}, {"detect", CmdT55xxDetect, AlwaysAvailable, "[1] Try detecting the tag modulation from reading the configuration block."}, {"deviceconfig", CmdT55xxSetDeviceConfig, IfPm3Lf, "Set/Get T55XX device configuration (startgap, writegap, write0, write1, readgap"}, {"p1detect", CmdT55xxDetectPage1, IfPm3Lf, "[1] Try detecting if this is a t55xx tag by reading page 1"}, diff --git a/client/cmdlft55xx.h b/client/cmdlft55xx.h index 56fc942fd..c61ef137d 100644 --- a/client/cmdlft55xx.h +++ b/client/cmdlft55xx.h @@ -148,9 +148,9 @@ char *GetModelStrFromCID(uint32_t cid); char *GetSelectedModulationStr(uint8_t id); void printT5xxHeader(uint8_t page); void printT55xxBlock(uint8_t blockNum); -int printConfiguration(t55xx_conf_block_t b); +int printConfiguration(t55xx_conf_block_t b); -int T55xxReadBlock(uint8_t block, bool page1, bool usepwd, bool override, uint32_t password); +int T55xxReadBlock(uint8_t block, bool page1, bool usepwd, uint8_t override, uint32_t password, uint8_t downlink_mode); bool GetT55xxBlockData(uint32_t *blockdata); bool DecodeT55xxBlock(void); bool tryDetectModulation(void); @@ -158,9 +158,9 @@ bool testKnownConfigBlock(uint32_t block0); bool tryDetectP1(bool getData); bool test(uint8_t mode, uint8_t *offset, int *fndBitRate, uint8_t clk, bool *Q5); -int special(const char *Cmd); -bool AquireData(uint8_t page, uint8_t block, bool pwdmode, uint32_t password); -int tryOnePassword(uint32_t password); +int special(const char *Cmd); +bool AquireData(uint8_t page, uint8_t block, bool pwdmode, uint32_t password, uint8_t downlink_mode); +uint8_t tryOnePassword(uint32_t password, uint8_t downlink_mode); void printT55x7Trace(t55x7_tracedata_t data, uint8_t repeat); void printT5555Trace(t5555_tracedata_t data, uint8_t repeat); diff --git a/client/scripting.c b/client/scripting.c index 572899e2d..a88f1dfea 100644 --- a/client/scripting.c +++ b/client/scripting.c @@ -903,7 +903,7 @@ static int l_T55xx_readblock(lua_State *L) { // try reading the config block and verify that PWD bit is set before doing this! if (!override) { - if (!AquireData(T55x7_PAGE0, T55x7_CONFIGURATION_BLOCK, false, 0)) { + if (!AquireData(T55x7_PAGE0, T55x7_CONFIGURATION_BLOCK, false, 0,0)) { return returnToLuaWithError(L, "Failed to read config block"); } @@ -920,7 +920,7 @@ static int l_T55xx_readblock(lua_State *L) { } } - if (!AquireData(usepage1, block, usepwd, password)) { + if (!AquireData(usepage1, block, usepwd, password,0)) { return returnToLuaWithError(L, "Failed to aquire data from card"); } @@ -977,7 +977,7 @@ static int l_T55xx_detect(lua_State *L) { if (!useGB) { - isok = AquireData(T55x7_PAGE0, T55x7_CONFIGURATION_BLOCK, usepwd, password); + isok = AquireData(T55x7_PAGE0, T55x7_CONFIGURATION_BLOCK, usepwd, password,0); if (isok == false) { return returnToLuaWithError(L, "Failed to aquire LF signal data"); } diff --git a/include/pm3_cmd.h b/include/pm3_cmd.h index d271d3099..297d52337 100644 --- a/include/pm3_cmd.h +++ b/include/pm3_cmd.h @@ -119,7 +119,7 @@ typedef struct { int divisor; int trigger_threshold; } sample_config; - +/* typedef struct { uint16_t start_gap; uint16_t write_gap; @@ -127,7 +127,34 @@ typedef struct { uint16_t write_1; uint16_t read_gap; } t55xx_config; +*/ +// Extended to support 1 of 4 timing +typedef struct { + uint8_t start_gap ; + uint8_t write_gap ; + uint8_t write_0 ; + uint8_t write_1 ; + uint8_t write_2 ; + uint8_t write_3 ; + uint8_t read_gap ; +} t55xx_config_t; +// This setup will allow for the 4 downlink modes "m" as well as other items if needed. +// Given the one struct we can then read/write to flash/client in one go. +typedef struct { + t55xx_config_t m[4]; // mode +} t55xx_config; + +/*typedef struct { + uint16_t start_gap [4]; + uint16_t write_gap [4]; + uint16_t write_0 [4]; + uint16_t write_1 [4]; + uint16_t write_2 [4]; + uint16_t write_3 [4]; + uint16_t read_gap [4]; +} t55xx_config; +*/ typedef struct { uint8_t version; uint32_t baudrate;