X-Git-Url: http://git.zerfleddert.de/cgi-bin/gitweb.cgi/proxmark3-svn/blobdiff_plain/f83c41c75b19aa3519e88dcf6b97e85c71131edd..23f0a7d862fb41d21dd90403cb657e91705bf886:/client/cmdlfem4x.c diff --git a/client/cmdlfem4x.c b/client/cmdlfem4x.c index b915aa5a..79e8a8fe 100644 --- a/client/cmdlfem4x.c +++ b/client/cmdlfem4x.c @@ -19,6 +19,7 @@ #include "cmddata.h" #include "cmdlf.h" #include "cmdlfem4x.h" +#include "lfdemod.h" char *global_em410xId; static int CmdHelp(const char *Cmd); @@ -27,10 +28,10 @@ int CmdEMdemodASK(const char *Cmd) { char cmdp = param_getchar(Cmd, 0); int findone = (cmdp == '1') ? 1 : 0; - UsbCommand c={CMD_EM410X_DEMOD}; - c.arg[0]=findone; - SendCommand(&c); - return 0; + UsbCommand c={CMD_EM410X_DEMOD}; + c.arg[0]=findone; + SendCommand(&c); + return 0; } /* Read the ID of an EM410x tag. @@ -43,21 +44,21 @@ int CmdEMdemodASK(const char *Cmd) */ int CmdEM410xRead(const char *Cmd) { - uint32_t hi=0; - uint64_t lo=0; - - if(!AskEm410xDemod("", &hi, &lo)) return 0; - PrintAndLog("EM410x pattern found: "); - printEM410x(hi, lo); - if (hi){ - PrintAndLog ("EM410x XL pattern found"); - return 0; - } - char id[12] = {0x00}; - sprintf(id, "%010llx",lo); - - global_em410xId = id; - return 1; + uint32_t hi=0; + uint64_t lo=0; + + if(!AskEm410xDemod("", &hi, &lo)) return 0; + PrintAndLog("EM410x pattern found: "); + printEM410x(hi, lo); + if (hi){ + PrintAndLog ("EM410x XL pattern found"); + return 0; + } + char id[12] = {0x00}; + sprintf(id, "%010llx",lo); + + global_em410xId = id; + return 1; } // emulate an EM410X tag @@ -83,52 +84,52 @@ int CmdEM410xSim(const char *Cmd) PrintAndLog("Starting simulating UID %02X%02X%02X%02X%02X", uid[0],uid[1],uid[2],uid[3],uid[4]); PrintAndLog("Press pm3-button to about simulation"); - /* clock is 64 in EM410x tags */ - int clock = 64; - - /* clear our graph */ - ClearGraph(0); - - /* write 9 start bits */ - for (i = 0; i < 9; i++) - AppendGraph(0, clock, 1); - - /* for each hex char */ - parity[0] = parity[1] = parity[2] = parity[3] = 0; - for (i = 0; i < 10; i++) - { - /* read each hex char */ - sscanf(&Cmd[i], "%1x", &n); - for (j = 3; j >= 0; j--, n/= 2) - binary[j] = n % 2; - - /* append each bit */ - AppendGraph(0, clock, binary[0]); - AppendGraph(0, clock, binary[1]); - AppendGraph(0, clock, binary[2]); - AppendGraph(0, clock, binary[3]); - - /* append parity bit */ - AppendGraph(0, clock, binary[0] ^ binary[1] ^ binary[2] ^ binary[3]); - - /* keep track of column parity */ - parity[0] ^= binary[0]; - parity[1] ^= binary[1]; - parity[2] ^= binary[2]; - parity[3] ^= binary[3]; - } - - /* parity columns */ - AppendGraph(0, clock, parity[0]); - AppendGraph(0, clock, parity[1]); - AppendGraph(0, clock, parity[2]); - AppendGraph(0, clock, parity[3]); - - /* stop bit */ - AppendGraph(1, clock, 0); + /* clock is 64 in EM410x tags */ + int clock = 64; + + /* clear our graph */ + ClearGraph(0); + + /* write 9 start bits */ + for (i = 0; i < 9; i++) + AppendGraph(0, clock, 1); + + /* for each hex char */ + parity[0] = parity[1] = parity[2] = parity[3] = 0; + for (i = 0; i < 10; i++) + { + /* read each hex char */ + sscanf(&Cmd[i], "%1x", &n); + for (j = 3; j >= 0; j--, n/= 2) + binary[j] = n % 2; + + /* append each bit */ + AppendGraph(0, clock, binary[0]); + AppendGraph(0, clock, binary[1]); + AppendGraph(0, clock, binary[2]); + AppendGraph(0, clock, binary[3]); + + /* append parity bit */ + AppendGraph(0, clock, binary[0] ^ binary[1] ^ binary[2] ^ binary[3]); + + /* keep track of column parity */ + parity[0] ^= binary[0]; + parity[1] ^= binary[1]; + parity[2] ^= binary[2]; + parity[3] ^= binary[3]; + } + + /* parity columns */ + AppendGraph(0, clock, parity[0]); + AppendGraph(0, clock, parity[1]); + AppendGraph(0, clock, parity[2]); + AppendGraph(0, clock, parity[3]); + + /* stop bit */ + AppendGraph(1, clock, 0); - CmdLFSim("0"); //240 start_gap. - return 0; + CmdLFSim("0"); //240 start_gap. + return 0; } /* Function is equivalent of lf read + data samples + em410xread @@ -156,6 +157,7 @@ int CmdEM410xWatch(const char *Cmd) return 0; } +//currently only supports manchester modulations int CmdEM410xWatchnSpoof(const char *Cmd) { CmdEM410xWatch(Cmd); @@ -164,6 +166,65 @@ int CmdEM410xWatchnSpoof(const char *Cmd) return 0; } +bool EM_EndParityTest(uint8_t *BitStream, size_t size, uint8_t rows, uint8_t cols, uint8_t pType) +{ + if (rows*cols>size) return false; + uint8_t colP=0; + //assume last row is a parity row and do not test + for (uint8_t colNum = 0; colNum < cols-1; colNum++) { + for (uint8_t rowNum = 0; rowNum < rows; rowNum++) { + colP ^= BitStream[(rowNum*cols)+colNum]; + } + if (colP != pType) return false; + } + return true; +} + +bool EM_ByteParityTest(uint8_t *BitStream, size_t size, uint8_t rows, uint8_t cols, uint8_t pType) +{ + if (rows*cols>size) return false; + uint8_t rowP=0; + //assume last row is a parity row and do not test + for (uint8_t rowNum = 0; rowNum < rows-1; rowNum++) { + for (uint8_t colNum = 0; colNum < cols; colNum++) { + rowP ^= BitStream[(rowNum*cols)+colNum]; + } + if (rowP != pType) return false; + } + return true; +} + +uint32_t OutputEM4x50_Block(uint8_t *BitStream, size_t size, bool verbose, bool pTest) +{ + if (size<45) return 0; + uint32_t code = bytebits_to_byte(BitStream,8); + code = code<<8 | bytebits_to_byte(BitStream+9,8); + code = code<<8 | bytebits_to_byte(BitStream+18,8); + code = code<<8 | bytebits_to_byte(BitStream+27,8); + if (verbose || g_debugMode){ + for (uint8_t i = 0; i<5; i++){ + if (i == 4) PrintAndLog(""); + PrintAndLog("%d%d%d%d%d%d%d%d %d -> 0x%02x", + BitStream[i*9], + BitStream[i*9+1], + BitStream[i*9+2], + BitStream[i*9+3], + BitStream[i*9+4], + BitStream[i*9+5], + BitStream[i*9+6], + BitStream[i*9+7], + BitStream[i*9+8], + bytebits_to_byte(BitStream+i*9,8) + ); + } + if (pTest) + PrintAndLog("Parity Passed"); + else + PrintAndLog("Parity Failed"); + } + //PrintAndLog("Code: %08x",code); + return code; +} /* Read the transmitted data of an EM4x50 tag * Format: * @@ -183,135 +244,192 @@ int CmdEM410xWatchnSpoof(const char *Cmd) * is stored in the blocks defined in the control word First and Last * Word Read values. UID is stored in block 32. */ +int EM4x50Read(const char *Cmd, bool verbose) +{ + uint8_t fndClk[]={0,8,16,32,40,50,64}; + int clk = 0; + int invert = 0; + sscanf(Cmd, "%i %i", &clk, &invert); + int tol = 0; + int i, j, startblock, skip, block, start, end, low, high, minClk; + bool complete= false; + int tmpbuff[MAX_GRAPH_TRACE_LEN / 64]; + save_restoreGB(1); + uint32_t Code[6]; + char tmp[6]; + + char tmp2[20]; + high= low= 0; + memset(tmpbuff, 0, MAX_GRAPH_TRACE_LEN / 64); + + // first get high and low values + for (i = 0; i < GraphTraceLen; i++) + { + if (GraphBuffer[i] > high) + high = GraphBuffer[i]; + else if (GraphBuffer[i] < low) + low = GraphBuffer[i]; + } + + // populate a buffer with pulse lengths + i= 0; + j= 0; + minClk= 255; + while (i < GraphTraceLen) + { + // measure from low to low + while ((GraphBuffer[i] > low) && (i low) && (i=(MAX_GRAPH_TRACE_LEN/64)) { + break; + } + tmpbuff[j++]= i - start; + if (i-start < minClk) minClk = i-start; + } + // set clock + if (!clk){ + for (uint8_t clkCnt = 0; clkCnt<7; clkCnt++) { + tol = fndClk[clkCnt]/8; + if (fndClk[clkCnt]-tol >= minClk) { + clk=fndClk[clkCnt]; + break; + } + } + } + + // look for data start - should be 2 pairs of LW (pulses of clk*3,clk*2) + start= -1; + skip= 0; + for (i= 0; i < j - 4 ; ++i) + { + skip += tmpbuff[i]; + if (tmpbuff[i] >= clk*3-tol && tmpbuff[i] <= clk*3+tol) + if (tmpbuff[i+1] >= clk*2-tol && tmpbuff[i+1] <= clk*2+tol) + if (tmpbuff[i+2] >= clk*3-tol && tmpbuff[i+2] <= clk*3+tol) + if (tmpbuff[i+3] >= clk-tol) + { + start= i + 4; + break; + } + } + startblock= i + 4; + + // skip over the remainder of LW + skip += tmpbuff[i+1] + tmpbuff[i+2] + clk + clk/8; + + int phaseoff = tmpbuff[i+3]-clk; + + // now do it again to find the end + end = skip; + for (i += 3; i < j - 4 ; ++i) + { + end += tmpbuff[i]; + if (tmpbuff[i] >= clk*3-tol && tmpbuff[i] <= clk*3 + tol) + if (tmpbuff[i+1] >= clk*2-tol && tmpbuff[i+1] <= clk*2 + tol) + if (tmpbuff[i+2] >= clk*3-tol && tmpbuff[i+2] <= clk*3 + tol) + if (tmpbuff[i+3] >= clk-tol) + { + complete= true; + break; + } + } + end = i; + // report back + if (verbose || g_debugMode) { + if (start >= 0) { + PrintAndLog("\nNote: should print 45 bits then 0177 (end of block)"); + PrintAndLog(" for each block"); + PrintAndLog(" Also, sometimes the demod gets out of sync and "); + PrintAndLog(" inverts the output - when this happens the 0177"); + PrintAndLog(" will be 3 extra 1's at the end"); + PrintAndLog(" 'data askedge' command may fix that"); + } else { + PrintAndLog("No data found!"); + PrintAndLog("Try again with more samples."); + return 0; + } + if (!complete) + { + PrintAndLog("*** Warning!"); + PrintAndLog("Partial data - no end found!"); + PrintAndLog("Try again with more samples."); + } + } else if (start < 0) return 0; + start=skip; + snprintf(tmp2, sizeof(tmp2),"%d %d 1000 %d", clk, invert, clk*47); + // get rid of leading crap + snprintf(tmp, sizeof(tmp),"%i",skip); + CmdLtrim(tmp); + bool pTest; + bool AllPTest=true; + // now work through remaining buffer printing out data blocks + block = 0; + i = startblock; + while (block < 6) + { + if (verbose || g_debugMode) PrintAndLog("\nBlock %i:", block); + skip = phaseoff; + + // look for LW before start of next block + for ( ; i < j - 4 ; ++i) + { + skip += tmpbuff[i]; + if (tmpbuff[i] >= clk*3-tol && tmpbuff[i] <= clk*3+tol) + if (tmpbuff[i+1] >= clk-tol) + break; + } + skip += clk; + phaseoff = tmpbuff[i+1]-clk; + i += 2; + if (ASKmanDemod(tmp2, false, false)<1) return 0; + //set DemodBufferLen to just one block + DemodBufferLen = skip/clk; + //test parities + pTest = EM_ByteParityTest(DemodBuffer,DemodBufferLen,5,9,0); + pTest &= EM_EndParityTest(DemodBuffer,DemodBufferLen,5,9,0); + AllPTest &= pTest; + //get output + Code[block]=OutputEM4x50_Block(DemodBuffer,DemodBufferLen,verbose, pTest); + if (g_debugMode) PrintAndLog("\nskipping %d samples, bits:%d",start, skip/clk); + //skip to start of next block + snprintf(tmp,sizeof(tmp),"%i",skip); + CmdLtrim(tmp); + block++; + if (i>=end) break; //in case chip doesn't output 6 blocks + } + //print full code: + if (verbose || g_debugMode || AllPTest){ + PrintAndLog("Found data at sample: %i - using clock: %i",skip,clk); + //PrintAndLog("\nSummary:"); + end=block; + for (block=0; block high) - high = GraphBuffer[i]; - else if (GraphBuffer[i] < low) - low = GraphBuffer[i]; - } - - /* populate a buffer with pulse lengths */ - i= 0; - j= 0; - while (i < GraphTraceLen) - { - // measure from low to low - while ((GraphBuffer[i] > low) && (i low) && (i=(MAX_GRAPH_TRACE_LEN/64)) { - break; - } - tmpbuff[j++]= i - start; - } - - /* look for data start - should be 2 pairs of LW (pulses of 192,128) */ - start= -1; - skip= 0; - for (i= 0; i < j - 4 ; ++i) - { - skip += tmpbuff[i]; - if (tmpbuff[i] >= 190 && tmpbuff[i] <= 194) - if (tmpbuff[i+1] >= 126 && tmpbuff[i+1] <= 130) - if (tmpbuff[i+2] >= 190 && tmpbuff[i+2] <= 194) - if (tmpbuff[i+3] >= 126 && tmpbuff[i+3] <= 130) - { - start= i + 3; - break; - } - } - startblock= i + 3; - - /* skip over the remainder of the LW */ - skip += tmpbuff[i+1]+tmpbuff[i+2]; - while (skip < MAX_GRAPH_TRACE_LEN && GraphBuffer[skip] > low) - ++skip; - skip += 8; - - /* now do it again to find the end */ - end= start; - for (i += 3; i < j - 4 ; ++i) - { - end += tmpbuff[i]; - if (tmpbuff[i] >= 190 && tmpbuff[i] <= 194) - if (tmpbuff[i+1] >= 126 && tmpbuff[i+1] <= 130) - if (tmpbuff[i+2] >= 190 && tmpbuff[i+2] <= 194) - if (tmpbuff[i+3] >= 126 && tmpbuff[i+3] <= 130) - { - complete= true; - break; - } - } - - if (start >= 0) - PrintAndLog("Found data at sample: %i",skip); - else - { - PrintAndLog("No data found!"); - PrintAndLog("Try again with more samples."); - return 0; - } - - if (!complete) - { - PrintAndLog("*** Warning!"); - PrintAndLog("Partial data - no end found!"); - PrintAndLog("Try again with more samples."); - } - - /* get rid of leading crap */ - sprintf(tmp,"%i",skip); - CmdLtrim(tmp); - - /* now work through remaining buffer printing out data blocks */ - block= 0; - i= startblock; - while (block < 6) - { - PrintAndLog("Block %i:", block); - // mandemod routine needs to be split so we can call it for data - // just print for now for debugging - CmdManchesterDemod("i 64"); - skip= 0; - /* look for LW before start of next block */ - for ( ; i < j - 4 ; ++i) - { - skip += tmpbuff[i]; - if (tmpbuff[i] >= 190 && tmpbuff[i] <= 194) - if (tmpbuff[i+1] >= 126 && tmpbuff[i+1] <= 130) - break; - } - while (GraphBuffer[skip] > low) - ++skip; - skip += 8; - sprintf(tmp,"%i",skip); - CmdLtrim(tmp); - start += skip; - block++; - } - return 0; + return EM4x50Read(Cmd, true); } int CmdEM410xWrite(const char *Cmd) { - uint64_t id = 0xFFFFFFFFFFFFFFFF; // invalid id value - int card = 0xFF; // invalid card value + uint64_t id = 0xFFFFFFFFFFFFFFFF; // invalid id value + int card = 0xFF; // invalid card value unsigned int clock = 0; // invalid clock value sscanf(Cmd, "%" PRIx64 " %d %d", &id, &card, &clock); @@ -370,133 +488,133 @@ int CmdEM410xWrite(const char *Cmd) return 0; } - UsbCommand c = {CMD_EM410X_WRITE_TAG, {card, (uint32_t)(id >> 32), (uint32_t)id}}; - SendCommand(&c); + UsbCommand c = {CMD_EM410X_WRITE_TAG, {card, (uint32_t)(id >> 32), (uint32_t)id}}; + SendCommand(&c); - return 0; + return 0; } int CmdReadWord(const char *Cmd) { int Word = -1; //default to invalid word - UsbCommand c; - - sscanf(Cmd, "%d", &Word); - + UsbCommand c; + + sscanf(Cmd, "%d", &Word); + if ( (Word > 15) | (Word < 0) ) { - PrintAndLog("Word must be between 0 and 15"); - return 1; - } - - PrintAndLog("Reading word %d", Word); - - c.cmd = CMD_EM4X_READ_WORD; - c.d.asBytes[0] = 0x0; //Normal mode - c.arg[0] = 0; - c.arg[1] = Word; - c.arg[2] = 0; - SendCommand(&c); - return 0; + PrintAndLog("Word must be between 0 and 15"); + return 1; + } + + PrintAndLog("Reading word %d", Word); + + c.cmd = CMD_EM4X_READ_WORD; + c.d.asBytes[0] = 0x0; //Normal mode + c.arg[0] = 0; + c.arg[1] = Word; + c.arg[2] = 0; + SendCommand(&c); + return 0; } int CmdReadWordPWD(const char *Cmd) { int Word = -1; //default to invalid word - int Password = 0xFFFFFFFF; //default to blank password - UsbCommand c; - - sscanf(Cmd, "%d %x", &Word, &Password); - + int Password = 0xFFFFFFFF; //default to blank password + UsbCommand c; + + sscanf(Cmd, "%d %x", &Word, &Password); + if ( (Word > 15) | (Word < 0) ) { - PrintAndLog("Word must be between 0 and 15"); - return 1; - } - - PrintAndLog("Reading word %d with password %08X", Word, Password); - - c.cmd = CMD_EM4X_READ_WORD; - c.d.asBytes[0] = 0x1; //Password mode - c.arg[0] = 0; - c.arg[1] = Word; - c.arg[2] = Password; - SendCommand(&c); - return 0; + PrintAndLog("Word must be between 0 and 15"); + return 1; + } + + PrintAndLog("Reading word %d with password %08X", Word, Password); + + c.cmd = CMD_EM4X_READ_WORD; + c.d.asBytes[0] = 0x1; //Password mode + c.arg[0] = 0; + c.arg[1] = Word; + c.arg[2] = Password; + SendCommand(&c); + return 0; } int CmdWriteWord(const char *Cmd) { - int Word = 16; //default to invalid block - int Data = 0xFFFFFFFF; //default to blank data - UsbCommand c; - - sscanf(Cmd, "%x %d", &Data, &Word); - - if (Word > 15) { - PrintAndLog("Word must be between 0 and 15"); - return 1; - } - - PrintAndLog("Writing word %d with data %08X", Word, Data); - - c.cmd = CMD_EM4X_WRITE_WORD; - c.d.asBytes[0] = 0x0; //Normal mode - c.arg[0] = Data; - c.arg[1] = Word; - c.arg[2] = 0; - SendCommand(&c); - return 0; + int Word = 16; //default to invalid block + int Data = 0xFFFFFFFF; //default to blank data + UsbCommand c; + + sscanf(Cmd, "%x %d", &Data, &Word); + + if (Word > 15) { + PrintAndLog("Word must be between 0 and 15"); + return 1; + } + + PrintAndLog("Writing word %d with data %08X", Word, Data); + + c.cmd = CMD_EM4X_WRITE_WORD; + c.d.asBytes[0] = 0x0; //Normal mode + c.arg[0] = Data; + c.arg[1] = Word; + c.arg[2] = 0; + SendCommand(&c); + return 0; } int CmdWriteWordPWD(const char *Cmd) { - int Word = 16; //default to invalid word - int Data = 0xFFFFFFFF; //default to blank data - int Password = 0xFFFFFFFF; //default to blank password - UsbCommand c; - - sscanf(Cmd, "%x %d %x", &Data, &Word, &Password); - - if (Word > 15) { - PrintAndLog("Word must be between 0 and 15"); - return 1; - } - - PrintAndLog("Writing word %d with data %08X and password %08X", Word, Data, Password); - - c.cmd = CMD_EM4X_WRITE_WORD; - c.d.asBytes[0] = 0x1; //Password mode - c.arg[0] = Data; - c.arg[1] = Word; - c.arg[2] = Password; - SendCommand(&c); - return 0; + int Word = 16; //default to invalid word + int Data = 0xFFFFFFFF; //default to blank data + int Password = 0xFFFFFFFF; //default to blank password + UsbCommand c; + + sscanf(Cmd, "%x %d %x", &Data, &Word, &Password); + + if (Word > 15) { + PrintAndLog("Word must be between 0 and 15"); + return 1; + } + + PrintAndLog("Writing word %d with data %08X and password %08X", Word, Data, Password); + + c.cmd = CMD_EM4X_WRITE_WORD; + c.d.asBytes[0] = 0x1; //Password mode + c.arg[0] = Data; + c.arg[1] = Word; + c.arg[2] = Password; + SendCommand(&c); + return 0; } static command_t CommandTable[] = { - {"help", CmdHelp, 1, "This help"}, - {"em410xdemod", CmdEMdemodASK, 0, "[findone] -- Extract ID from EM410x tag (option 0 for continuous loop, 1 for only 1 tag)"}, - {"em410xread", CmdEM410xRead, 1, "[clock rate] -- Extract ID from EM410x tag"}, - {"em410xsim", CmdEM410xSim, 0, " -- Simulate EM410x tag"}, - {"em410xwatch", CmdEM410xWatch, 0, "['h'] -- Watches for EM410x 125/134 kHz tags (option 'h' for 134)"}, - {"em410xspoof", CmdEM410xWatchnSpoof, 0, "['h'] --- Watches for EM410x 125/134 kHz tags, and replays them. (option 'h' for 134)" }, - {"em410xwrite", CmdEM410xWrite, 1, " <'0' T5555> <'1' T55x7> [clock rate] -- Write EM410x UID to T5555(Q5) or T55x7 tag, optionally setting clock rate"}, - {"em4x50read", CmdEM4x50Read, 1, "Extract data from EM4x50 tag"}, - {"readword", CmdReadWord, 1, " -- Read EM4xxx word data"}, - {"readwordPWD", CmdReadWordPWD, 1, " -- Read EM4xxx word data in password mode"}, - {"writeword", CmdWriteWord, 1, " -- Write EM4xxx word data"}, - {"writewordPWD", CmdWriteWordPWD, 1, " -- Write EM4xxx word data in password mode"}, - {NULL, NULL, 0, NULL} + {"help", CmdHelp, 1, "This help"}, + {"em410xdemod", CmdEMdemodASK, 0, "[findone] -- Extract ID from EM410x tag (option 0 for continuous loop, 1 for only 1 tag)"}, + {"em410xread", CmdEM410xRead, 1, "[clock rate] -- Extract ID from EM410x tag"}, + {"em410xsim", CmdEM410xSim, 0, " -- Simulate EM410x tag"}, + {"em410xwatch", CmdEM410xWatch, 0, "['h'] -- Watches for EM410x 125/134 kHz tags (option 'h' for 134)"}, + {"em410xspoof", CmdEM410xWatchnSpoof, 0, "['h'] --- Watches for EM410x 125/134 kHz tags, and replays them. (option 'h' for 134)" }, + {"em410xwrite", CmdEM410xWrite, 1, " <'0' T5555> <'1' T55x7> [clock rate] -- Write EM410x UID to T5555(Q5) or T55x7 tag, optionally setting clock rate"}, + {"em4x50read", CmdEM4x50Read, 1, "Extract data from EM4x50 tag"}, + {"readword", CmdReadWord, 1, " -- Read EM4xxx word data"}, + {"readwordPWD", CmdReadWordPWD, 1, " -- Read EM4xxx word data in password mode"}, + {"writeword", CmdWriteWord, 1, " -- Write EM4xxx word data"}, + {"writewordPWD", CmdWriteWordPWD, 1, " -- Write EM4xxx word data in password mode"}, + {NULL, NULL, 0, NULL} }; int CmdLFEM4X(const char *Cmd) { - CmdsParse(CommandTable, Cmd); - return 0; + CmdsParse(CommandTable, Cmd); + return 0; } int CmdHelp(const char *Cmd) { - CmdsHelp(CommandTable); - return 0; + CmdsHelp(CommandTable); + return 0; }