X-Git-Url: http://cvs.zerfleddert.de/cgi-bin/gitweb.cgi/proxmark3-svn/blobdiff_plain/b9957414a5fc9a661fed27d95838726eeeedadd9..6763dc17a3f76370c766bfdb39179bde3ff7619f:/client/cmdlft55xx.c?ds=inline diff --git a/client/cmdlft55xx.c b/client/cmdlft55xx.c index 8ab1892d..ed980f9b 100644 --- a/client/cmdlft55xx.c +++ b/client/cmdlft55xx.c @@ -7,24 +7,24 @@ // Low frequency T55xx commands //----------------------------------------------------------------------------- +#include "cmdlft55xx.h" + #include #include #include #include #include -#include "proxmark3.h" +#include "comms.h" #include "ui.h" #include "graph.h" #include "cmdmain.h" #include "cmdparser.h" #include "cmddata.h" #include "cmdlf.h" -#include "cmdlft55xx.h" #include "util.h" -#include "data.h" #include "lfdemod.h" -#include "cmdhf14a.h" //for getTagInfo #include "protocols.h" +#include "taginfo.h" #define T55x7_CONFIGURATION_BLOCK 0x00 #define T55x7_PAGE0 0x00 @@ -67,6 +67,8 @@ int usage_t55xx_read(){ PrintAndLog(" p - OPTIONAL password (8 hex characters)"); PrintAndLog(" o - OPTIONAL override safety check"); PrintAndLog(" 1 - OPTIONAL read Page 1 instead of Page 0"); + PrintAndLog(" r - OPTIONAL downlink encoding '0' fixed bit length (default), '1' long leading reference"); + PrintAndLog(" '2' leading zero, '3' 1 of 4 coding reference"); PrintAndLog(" ****WARNING****"); PrintAndLog(" Use of read with password on a tag not configured for a pwd"); PrintAndLog(" can damage the tag"); @@ -86,6 +88,8 @@ int usage_t55xx_write(){ PrintAndLog(" p - OPTIONAL password 4bytes (8 hex characters)"); PrintAndLog(" 1 - OPTIONAL write Page 1 instead of Page 0"); PrintAndLog(" t - OPTIONAL test mode write - ****DANGER****"); + PrintAndLog(" r - OPTIONAL downlink encoding '0' fixed bit length (default), '1' long leading reference"); + PrintAndLog(" '2' leading zero, '3' 1 of 4 coding reference"); PrintAndLog(""); PrintAndLog("Examples:"); PrintAndLog(" lf t55xx write b 3 d 11223344 - write 11223344 to block 3"); @@ -132,6 +136,8 @@ int usage_t55xx_detect(){ PrintAndLog("Options:"); PrintAndLog(" 1 - if set, use Graphbuffer otherwise read data from tag."); PrintAndLog(" p - OPTIONAL password (8 hex characters)"); + PrintAndLog(" r - OPTIONAL downlink encoding '0' fixed bit length (default), '1' long leading reference"); + PrintAndLog(" '2' leading zero, '3' 1 of 4 coding reference"); PrintAndLog(""); PrintAndLog("Examples:"); PrintAndLog(" lf t55xx detect"); @@ -162,7 +168,7 @@ int usage_t55xx_wakup(){ PrintAndLog(" - [required] password 4bytes (8 hex symbols)"); PrintAndLog(""); PrintAndLog("Examples:"); - PrintAndLog(" lf t55xx wakeup 11223344 - send wakeup password"); + PrintAndLog(" lf t55xx wakeup 11223344 - send wakeup password"); return 0; } int usage_t55xx_bruteforce(){ @@ -172,13 +178,16 @@ int usage_t55xx_bruteforce(){ PrintAndLog(" password must be 4 bytes (8 hex symbols)"); PrintAndLog("Options:"); PrintAndLog(" h - this help"); + PrintAndLog(" r - OPTIONAL downlink encoding '0' fixed bit length (default)"); + PrintAndLog(" '1' long leading reference, '2' leading zero "); + PrintAndLog(" '3' 1 of 4 coding reference, '4' special - try all downlink modes"); PrintAndLog(" - 4 byte hex value to start pwd search at"); PrintAndLog(" - 4 byte hex value to end pwd search at"); PrintAndLog(" i <*.dic> - loads a default keys dictionary file <*.dic>"); PrintAndLog(""); PrintAndLog("Examples:"); - PrintAndLog(" lf t55xx bruteforce aaaaaaaa bbbbbbbb"); - PrintAndLog(" lf t55xx bruteforce i default_pwd.dic"); + PrintAndLog(" lf t55xx bruteforce [r 2] aaaaaaaa bbbbbbbb"); + PrintAndLog(" lf t55xx bruteforce [r 2] i default_pwd.dic"); PrintAndLog(""); return 0; } @@ -207,7 +216,7 @@ void printT5xxHeader(uint8_t page){ int CmdT55xxSetConfig(const char *Cmd) { uint8_t offset = 0; - char modulation[5] = {0x00}; + char modulation[6] = {0x00}; char tmp = 0x00; uint8_t bitRate = 0; uint8_t rates[9] = {8,16,32,40,50,64,100,128,0}; @@ -236,7 +245,7 @@ int CmdT55xxSetConfig(const char *Cmd) { cmdp+=2; break; case 'd': - param_getstr(Cmd, cmdp+1, modulation); + param_getstr(Cmd, cmdp+1, modulation, sizeof(modulation)); cmdp += 2; if ( strcmp(modulation, "FSK" ) == 0) { @@ -311,12 +320,12 @@ 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, bool 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 0; + if ( !AquireData(T55x7_PAGE0, T55x7_CONFIGURATION_BLOCK, false, 0,downlink_mode ) ) return 0; if ( !tryDetectModulation() ) { PrintAndLog("Safety Check: Could not detect if PWD bit is set in config block. Exits."); return 0; @@ -330,7 +339,7 @@ int T55xxReadBlock(uint8_t block, bool page1, bool usepwd, bool override, uint32 } } - if (!AquireData(page1, block, usepwd, password) ) return 0; + if (!AquireData(page1, block, usepwd, password,downlink_mode) ) return 0; if (!DecodeT55xxBlock()) return 0; char blk[10]={0}; @@ -342,6 +351,8 @@ int T55xxReadBlock(uint8_t block, bool page1, bool usepwd, bool override, uint32 int CmdT55xxReadBlock(const char *Cmd) { uint8_t block = REGULAR_READ_MODE_BLOCK; uint32_t password = 0; //default to blank Block 7 + uint8_t downlink_mode = 0; + bool usepwd = false; bool override = false; bool page1 = false; @@ -372,6 +383,12 @@ 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: PrintAndLog("Unknown parameter '%c'", param_getchar(Cmd, cmdp)); errors = true; @@ -386,7 +403,7 @@ 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(){ @@ -419,23 +436,23 @@ bool DecodeT55xxBlock(){ break; case DEMOD_PSK1: // skip first 160 samples to allow antenna to settle in (psk gets inverted occasionally otherwise) - save_restoreGB(1); + save_restoreGB(GRAPH_SAVE); CmdLtrim("160"); snprintf(cmdStr, sizeof(buf),"%d %d 6", bitRate[config.bitrate], config.inverted ); ans = PSKDemod(cmdStr, false); //undo trim samples - save_restoreGB(0); + save_restoreGB(GRAPH_RESTORE); break; case DEMOD_PSK2: //inverted won't affect this case DEMOD_PSK3: //not fully implemented // skip first 160 samples to allow antenna to settle in (psk gets inverted occasionally otherwise) - save_restoreGB(1); + save_restoreGB(GRAPH_SAVE); CmdLtrim("160"); snprintf(cmdStr, sizeof(buf),"%d 0 6", bitRate[config.bitrate] ); ans = PSKDemod(cmdStr, false); psk1TOpsk2(DemodBuffer, DemodBufferLen); //undo trim samples - save_restoreGB(0); + save_restoreGB(GRAPH_RESTORE); break; case DEMOD_NRZ: snprintf(cmdStr, sizeof(buf),"%d %d 1", bitRate[config.bitrate], config.inverted ); @@ -465,6 +482,7 @@ int CmdT55xxDetect(const char *Cmd){ bool usepwd = false; uint32_t password = 0; uint8_t cmdp = 0; + uint8_t downlink_mode = 0; while(param_getchar(Cmd, cmdp) != 0x00 && !errors) { switch(param_getchar(Cmd, cmdp)) { @@ -482,6 +500,12 @@ int CmdT55xxDetect(const char *Cmd){ useGB = 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: PrintAndLog("Unknown parameter '%c'", param_getchar(Cmd, cmdp)); errors = true; @@ -491,13 +515,21 @@ int CmdT55xxDetect(const char *Cmd){ if (errors) return usage_t55xx_detect(); if ( !useGB) { - if ( !AquireData(T55x7_PAGE0, T55x7_CONFIGURATION_BLOCK, usepwd, password) ) + if ( !AquireData(T55x7_PAGE0, T55x7_CONFIGURATION_BLOCK, usepwd, password,downlink_mode) ) return 0; } if ( !tryDetectModulation() ) PrintAndLog("Could not detect modulation automatically. Try setting it manually with \'lf t55xx config\'"); - + else { + // Add downlink mode to reference. + switch (downlink_mode) { + case 0 : PrintAndLog ("Downlink : r 0 - default/fixed bit length"); break; + case 1 : PrintAndLog ("Downlink : r 1 - long leading reference"); break; + case 2 : PrintAndLog ("Downlink : r 2 - leading zero reference"); break; + case 3 : PrintAndLog ("Downlink : r 3 - 1 of 4 coding reference"); break; + } + } return 1; } @@ -507,8 +539,8 @@ bool tryDetectModulation(){ t55xx_conf_block_t tests[15]; int bitRate=0; uint8_t fc1 = 0, fc2 = 0, ans = 0; - int clk=0; - ans = fskClocks(&fc1, &fc2, (uint8_t *)&clk, false); + int clk = 0, firstClockEdge = 0; + ans = fskClocks(&fc1, &fc2, (uint8_t *)&clk, false, &firstClockEdge); if (ans && ((fc1==10 && fc2==8) || (fc1==8 && fc2==5))) { if ( FSKrawDemod("0 0", false) && test(DEMOD_FSK, &tests[hits].offset, &bitRate, clk, &tests[hits].Q5)) { tests[hits].modulation = DEMOD_FSK; @@ -594,7 +626,7 @@ bool tryDetectModulation(){ clk = GetPskClock("", false, false); if (clk>0) { // allow undo - save_restoreGB(1); + save_restoreGB(GRAPH_SAVE); // skip first 160 samples to allow antenna to settle in (psk gets inverted occasionally otherwise) CmdLtrim("160"); if ( PSKDemod("0 0 6", false) && test(DEMOD_PSK1, &tests[hits].offset, &bitRate, clk, &tests[hits].Q5)) { @@ -638,7 +670,7 @@ bool tryDetectModulation(){ } } // inverse waves does not affect this demod //undo trim samples - save_restoreGB(0); + save_restoreGB(GRAPH_RESTORE); } } if ( hits == 1) { @@ -898,6 +930,8 @@ 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 + uint32_t downlink_mode = 0; + bool usepwd = false; bool page1 = false; bool gotdata = false; @@ -935,6 +969,12 @@ int CmdT55xxWriteBlock(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: PrintAndLog("Unknown parameter '%c'", param_getchar(Cmd, cmdp)); errors = true; @@ -952,17 +992,19 @@ int CmdT55xxWriteBlock(const char *Cmd) { UsbCommand resp; c.d.asBytes[0] = (page1) ? 0x2 : 0; c.d.asBytes[0] |= (testMode) ? 0x4 : 0; - + c.d.asBytes[0] |= (downlink_mode << 3); + char pwdStr[16] = {0}; snprintf(pwdStr, sizeof(pwdStr), "pwd: 0x%08X", password); PrintAndLog("Writing page %d block: %02d data: 0x%08X %s", page1, block, data, (usepwd) ? pwdStr : "" ); - + //Password mode if (usepwd) { c.arg[2] = password; c.d.asBytes[0] |= 0x1; } + clearCommandBuffer(); SendCommand(&c); if (!WaitForResponseTimeout(CMD_ACK, &resp, 1000)){ @@ -980,7 +1022,7 @@ int CmdT55xxReadTrace(const char *Cmd) { return usage_t55xx_trace(); if (strlen(Cmd)==0) - if ( !AquireData( T55x7_PAGE1, REGULAR_READ_MODE_BLOCK, pwdmode, password ) ) + if ( !AquireData( T55x7_PAGE1, REGULAR_READ_MODE_BLOCK, pwdmode, password,0 ) ) return 0; if ( config.Q5 ) { @@ -1070,7 +1112,7 @@ void printT55x7Trace( t55x7_tracedata_t data, uint8_t repeat ){ PrintAndLog("-- T55x7 Trace Information ----------------------------------"); PrintAndLog("-------------------------------------------------------------"); PrintAndLog(" ACL Allocation class (ISO/IEC 15963-1) : 0x%02X (%d)", data.acl, data.acl); - PrintAndLog(" MFC Manufacturer ID (ISO/IEC 7816-6) : 0x%02X (%d) - %s", data.mfc, data.mfc, getTagInfo(data.mfc)); + PrintAndLog(" MFC Manufacturer ID (ISO/IEC 7816-6) : 0x%02X (%d) - %s", data.mfc, data.mfc, getManufacturerName(data.mfc)); PrintAndLog(" CID : 0x%02X (%d) - %s", data.cid, data.cid, GetModelStrFromCID(data.cid)); PrintAndLog(" ICR IC Revision : %d", data.icr ); PrintAndLog(" Manufactured"); @@ -1144,7 +1186,7 @@ int CmdT55xxInfo(const char *Cmd){ return usage_t55xx_info(); if (strlen(Cmd)==0) - if ( !AquireData( T55x7_PAGE0, T55x7_CONFIGURATION_BLOCK, pwdmode, password ) ) + if ( !AquireData( T55x7_PAGE0, T55x7_CONFIGURATION_BLOCK, pwdmode, password,0 ) ) return 1; if (!DecodeT55xxBlock()) return 1; @@ -1212,20 +1254,21 @@ int CmdT55xxDump(const char *Cmd){ printT5xxHeader(0); for ( uint8_t i = 0; i <8; ++i) - T55xxReadBlock(i, 0, usepwd, override, password); + T55xxReadBlock(i, 0, usepwd, override, password,0); printT5xxHeader(1); for ( uint8_t i = 0; i<4; i++) - T55xxReadBlock(i, 1, usepwd, override, password); + T55xxReadBlock(i, 1, usepwd, override, password,0); return 1; } -int AquireData( uint8_t page, uint8_t block, bool pwdmode, uint32_t password ){ +int AquireData( uint8_t page, uint8_t block, bool pwdmode, uint32_t password, uint8_t downlink_mode ){ // arg0 bitmodes: // bit0 = pwdmode // bit1 = page to read from uint8_t arg0 = (page<<1) | pwdmode; + arg0 |= (downlink_mode << 3); UsbCommand c = {CMD_T55XX_READ_BLOCK, {arg0, block, password}}; clearCommandBuffer(); @@ -1355,8 +1398,7 @@ int CmdResetRead(const char *Cmd) { } uint8_t got[BIGBUF_SIZE-1]; - GetFromBigBuf(got,sizeof(got),0); - WaitForResponse(CMD_ACK,NULL); + GetFromBigBuf(got, sizeof(got), 0, NULL, -1 , 0); setGraphBuf(got, sizeof(got)); return 1; } @@ -1404,9 +1446,23 @@ int CmdT55xxBruteForce(const char *Cmd) { uint32_t start_password = 0x00000000; //start password uint32_t end_password = 0xFFFFFFFF; //end password bool found = false; - + uint8_t downlink_mode = 0; + bool try_all_dl_modes = false; + uint8_t dl_mode = 0; + uint8_t cmd_offset = 0; + int cmd_opt = 0; + char cmdp = param_getchar(Cmd, 0); + if (cmdp == 'h' || cmdp == 'H') return usage_t55xx_bruteforce(); + if (cmdp == 'r' || cmdp == 'R') { + downlink_mode = param_getchar(Cmd, 1) - '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_opt += 2; // To help start/end passwords for range to be found + cmd_offset += 4; // r x To help the string offset for filename start position in cmd + cmdp = param_getchar(Cmd, 2); // get 3rd option, as this is fixed order. + } keyBlock = calloc(stKeyBlock, 6); if (keyBlock == NULL) return 1; @@ -1415,7 +1471,7 @@ int CmdT55xxBruteForce(const char *Cmd) { int len = strlen(Cmd+2); if (len > FILE_PATH_SIZE) len = FILE_PATH_SIZE; - memcpy(filename, Cmd+2, len); + memcpy(filename, Cmd+2+cmd_offset, len); FILE * f = fopen( filename , "r"); @@ -1433,7 +1489,7 @@ int CmdT55xxBruteForce(const char *Cmd) { //The line start with # is comment, skip if( buf[0]=='#' ) continue; - if (!isxdigit(buf[0])) { + if (!isxdigit((unsigned char)buf[0])) { PrintAndLog("File content error. '%s' must include 8 HEX symbols", buf); continue; } @@ -1480,20 +1536,32 @@ int CmdT55xxBruteForce(const char *Cmd) { testpwd = bytes_to_num(keyBlock + 4*c, 4); PrintAndLog("Testing %08X", testpwd); + + // Try each downlink_mode if asked to + // donwlink_mode will = 0 if > 3 or set to 0, so loop from 0 - 3 + for (dl_mode = downlink_mode; dl_mode <= 3; dl_mode++){ + if ( !AquireData(T55x7_PAGE0, T55x7_CONFIGURATION_BLOCK, true, testpwd, dl_mode)) { + PrintAndLog("Aquireing data from device failed. Quitting"); + free(keyBlock); + return 0; + } - if ( !AquireData(T55x7_PAGE0, T55x7_CONFIGURATION_BLOCK, true, testpwd)) { - PrintAndLog("Aquireing data from device failed. Quitting"); - free(keyBlock); - return 0; - } - - found = tryDetectModulation(); + found = tryDetectModulation(); - if ( found ) { - PrintAndLog("Found valid password: [%08X]", testpwd); - free(keyBlock); - return 0; - } + if ( found ) { + PrintAndLog("Found valid password: [%08X]", testpwd); + free(keyBlock); + switch (dl_mode) { + case 0 : PrintAndLog ("Downlink : r 0 - default/fixed bit length"); break; + case 1 : PrintAndLog ("Downlink : r 1 - long leading reference"); break; + case 2 : PrintAndLog ("Downlink : r 2 - leading zero reference"); break; + case 3 : PrintAndLog ("Downlink : r 3 - 1 of 4 coding reference"); break; + } + return 0; + } + if (!try_all_dl_modes) // Exit loop if not trying all downlink modes + dl_mode = 4; + } } PrintAndLog("Password NOT found."); free(keyBlock); @@ -1503,8 +1571,8 @@ int CmdT55xxBruteForce(const char *Cmd) { // Try to read Block 7, first :) // incremental pwd range search - start_password = param_get32ex(Cmd, 0, 0, 16); - end_password = param_get32ex(Cmd, 1, 0, 16); + start_password = param_get32ex(Cmd, cmd_opt , 0, 16); + end_password = param_get32ex(Cmd, cmd_opt+1 , 0, 16); if ( start_password >= end_password ) { free(keyBlock); @@ -1525,29 +1593,41 @@ int CmdT55xxBruteForce(const char *Cmd) { free(keyBlock); return 0; } + // Try each downlink_mode if asked to + // donwlink_mode will = 0 if > 3 or set to 0, so loop from 0 - 3 + for (dl_mode = downlink_mode; dl_mode <= 3; dl_mode++){ + if (!AquireData(T55x7_PAGE0, T55x7_CONFIGURATION_BLOCK, true, i,dl_mode)) { + PrintAndLog("Aquireing data from device failed. Quitting"); + free(keyBlock); + return 0; + } + found = tryDetectModulation(); - if (!AquireData(T55x7_PAGE0, T55x7_CONFIGURATION_BLOCK, true, i)) { - PrintAndLog("Aquireing data from device failed. Quitting"); - free(keyBlock); - return 0; + if (found) break; + if (!try_all_dl_modes) // Exit loop if not trying all downlink modes + dl_mode = 4; } - found = tryDetectModulation(); - if (found) break; i++; } - PrintAndLog(""); - - if (found) + if (found){ PrintAndLog("Found valid password: [%08x]", i); - else + switch (dl_mode) { + case 0 : PrintAndLog ("Downlink : r 0 - default/fixed bit length"); break; + case 1 : PrintAndLog ("Downlink : r 1 - long leading reference"); break; + case 2 : PrintAndLog ("Downlink : r 2 - leading Zero reference"); break; + case 3 : PrintAndLog ("Downlink : r 3 - 1 of 4 coding reference"); break; + } + } + else{ + PrintAndLog(""); PrintAndLog("Password NOT found. Last tried: [%08x]", --i); + } free(keyBlock); return 0; } - // note length of data returned is different for different chips. // 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... @@ -1555,16 +1635,16 @@ 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; + int clk = 0, firstClockEdge = 0; bool st = true; if ( getData ) { - if ( !AquireData(T55x7_PAGE1, 1, false, 0) ) + if ( !AquireData(T55x7_PAGE1, 1, false, 0,0) ) return false; } // try fsk clock detect. if successful it cannot be any other type of modulation... (in theory...) - ans = fskClocks(&fc1, &fc2, (uint8_t *)&clk, false); + ans = fskClocks(&fc1, &fc2, (uint8_t *)&clk, false, &firstClockEdge); if (ans && ((fc1==10 && fc2==8) || (fc1==8 && fc2==5))) { if ( FSKrawDemod("0 0", false) && preambleSearchEx(DemodBuffer,preamble,sizeof(preamble),&DemodBufferLen,&startIdx,false) && @@ -1688,7 +1768,7 @@ int CmdT55xxDetectPage1(const char *Cmd){ if (errors) return usage_t55xx_detectP1(); if ( !useGB ) { - if ( !AquireData(T55x7_PAGE1, 1, usepwd, password) ) + if ( !AquireData(T55x7_PAGE1, 1, usepwd, password,0) ) return false; } bool success = tryDetectP1(false); @@ -1698,7 +1778,7 @@ int CmdT55xxDetectPage1(const char *Cmd){ static command_t CommandTable[] = { {"help", CmdHelp, 1, "This help"}, - {"bruteforce",CmdT55xxBruteForce,0, " [i <*.dic>] Simple bruteforce attack to find password"}, + {"bruteforce",CmdT55xxBruteForce,0, " [i <*.dic>] Simple bruteforce attack to find password"}, {"config", CmdT55xxSetConfig, 1, "Set/Get T55XX configuration (modulation, inverted, offset, rate)"}, {"detect", CmdT55xxDetect, 1, "[1] Try detecting the tag modulation from reading the configuration block."}, {"p1detect", CmdT55xxDetectPage1,1, "[1] Try detecting if this is a t55xx tag by reading page 1"},