diff --git a/client/src/cmdlfem4x.c b/client/src/cmdlfem4x.c index f20610a2e..1625aa2ec 100644 --- a/client/src/cmdlfem4x.c +++ b/client/src/cmdlfem4x.c @@ -646,6 +646,7 @@ static command_t CommandTable[] = { {"4x05_read", CmdEM4x05Read, IfPm3Lf, "read word data from EM4x05/EM4x69"}, {"4x05_write", CmdEM4x05Write, IfPm3Lf, "write word data to EM4x05/EM4x69"}, {"4x05_unlock", CmdEM4x05Unlock, IfPm3Lf, "execute tear off against EM4x05/EM4x69"}, + {"4x05_sniff", CmdEM4x05Sniff, IfPm3Lf, "Attempt to recover em4x05 commands from sample buffer"}, {"----------", CmdHelp, AlwaysAvailable, "----------------------- " _CYAN_("EM 4x50") " -----------------------"}, {"4x50_dump", CmdEM4x50Dump, IfPm3EM4x50, "dump EM4x50 tag"}, {"4x50_info", CmdEM4x50Info, IfPm3EM4x50, "tag information EM4x50"}, diff --git a/client/src/cmdlfem4x05.c b/client/src/cmdlfem4x05.c index cf27030a4..a680d286f 100644 --- a/client/src/cmdlfem4x05.c +++ b/client/src/cmdlfem4x05.c @@ -1597,3 +1597,227 @@ int CmdEM4x05Unlock(const char *Cmd) { PrintAndLogEx(NORMAL, ""); return exit_code; } + +static size_t em4x05_Sniff_GetNextBitStart (size_t idx, size_t sc, int *data, size_t *pulsesamples) +{ + while ((idx < sc) && (data[idx] <= 10)) // find a going high + idx++; + + while ((idx < sc) && (data[idx] > -10)) // find going low may need to add something here it SHOULD be a small clk around 0, but white seems to extend a bit. + idx++; + + (*pulsesamples) = 0; + while ((idx < sc) && ((data[idx+1] - data[idx]) < 10 )) { // find "sharp rise" + (*pulsesamples)++; + idx++; + } + + return idx; +} + +uint32_t static em4x05_Sniff_GetBlock (char *bits, bool fwd) { + uint32_t value = 0; + uint8_t idx; + + for (idx = 0; idx < 8; idx++) { + value <<= 1; + value += (bits[idx] - '0'); + } + for (idx = 9; idx < 17; idx++) { + value <<= 1; + value += (bits[idx] - '0'); + } + for (idx = 18; idx < 26; idx++) { + value <<= 1; + value += (bits[idx] - '0'); + } + for (idx = 27; idx < 35; idx++) { + value <<= 1; + value += (bits[idx] - '0'); + } + + if (!fwd) { + uint32_t t1 = value; + value = 0; + for (idx = 0; idx < 32; idx++) + value |= (((t1 >> idx) & 1) << (31 - idx)); + } + return value; +} + +int CmdEM4x05Sniff(const char *Cmd) { + + bool sampleData = true; + bool haveData = false; + size_t idx = 0; + char cmdText [100]; + char dataText [100]; + char blkAddr[4]; + char bits[80]; + int bitidx; + int ZeroWidth; // 32-42 "1" is 32 + int CycleWidth; + size_t pulseSamples; + size_t pktOffset; + int i; + bool eop = false; + uint32_t tmpValue; + bool pwd = false; + bool fwd = false; + + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf em 4x05_sniff", + "Sniff EM4x05 commands sent from a programmer", + "lf em 4x05_sniff -> sniff via lf sniff\n" + "lf em 4x05_sniff -1 -> sniff from data loaded into the buffer\n" + "lf em 4x05_sniff -r -> reverse the bit order when showing block data" + ); + + void *argtable[] = { + arg_param_begin, + arg_lit0("1", "buf","Use the data in the buffer"), + arg_lit0("r", "rev", "Reverse the bit order for data blocks"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + sampleData = !arg_get_lit(ctx,1); + fwd = arg_get_lit(ctx,2); + + // setup and sample data from Proxmark + // if not directed to existing sample/graphbuffer + if (sampleData) { + CmdLFSniff(""); + } + + // Headings + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, _CYAN_("EM4x05 command detection")); + PrintAndLogEx(SUCCESS, "offset | Command | Data | blk | raw"); + PrintAndLogEx(SUCCESS, "-------+-------------+----------+-----+------------------------------------------------------------"); + + idx = 0; + // loop though sample buffer + while (idx < GraphTraceLen) { + eop = false; + haveData = false; + pwd = false; + + idx = em4x05_Sniff_GetNextBitStart (idx, GraphTraceLen, GraphBuffer, &pulseSamples); + pktOffset = idx; + if (pulseSamples >= 10) { // Should be 18 so a bit less to allow for processing + + // Use first bit to get "0" bit samples as a reference + ZeroWidth = idx; + idx = em4x05_Sniff_GetNextBitStart (idx, GraphTraceLen, GraphBuffer, &pulseSamples); + ZeroWidth = idx - ZeroWidth; + + // printf ("Zero width after 2 0 bits : %llu 0 Zero width %d\n",idx,ZeroWidth); + + if (ZeroWidth <= 50) { + pktOffset -= ZeroWidth; + // ZeroSamples = 0; + memset(bits,0x00,sizeof(bits)); + bitidx = 0; + + while ((idx < GraphTraceLen) && !eop) { + CycleWidth = idx; + idx = em4x05_Sniff_GetNextBitStart (idx, GraphTraceLen, GraphBuffer, &pulseSamples); + + CycleWidth = idx - CycleWidth; + if ((CycleWidth > 300) || (CycleWidth < (ZeroWidth-5))) { // to long or too short + eop = true; + bits[bitidx++] = '0'; // Append last zero from the last bit find + cmdText[0] = 0; + // Memo6->Lines->Add("Bit widths... : "+T1); + // EM4305 command lengths + // Login 0011 => 4 + 45 => 49 + // Write Word 0101 => 4 + 7 + 45 => 56 + // Read Word 1001 => 4 + 7 => 11 + // Protect 1100 => 4 + 45 => 49 + // Disable 1010 => 4 + 45 => 49 + // -> disaable 1010 11111111 0 11111111 0 11111111 0 11111111 0 00000000 0 + + // logon + if ((strncmp (bits,"0011",4) == 0) && (bitidx == 49)) { + haveData = true; + pwd = true; + sprintf (cmdText,"Logon"); + sprintf (blkAddr," "); + tmpValue = em4x05_Sniff_GetBlock (&bits[4], fwd); + sprintf (dataText,"%08X",tmpValue); + } + + // write + if ((strncmp (bits,"0101",4) == 0) && (bitidx == 56)) { + haveData = true; + sprintf (cmdText,"Write"); + tmpValue = 0; + tmpValue = (bits[4] - '0') + ((bits[5] - '0') << 1) + ((bits[6] - '0') << 2) + ((bits[7] - '0') << 3); + sprintf (blkAddr,"%d",tmpValue); + if (tmpValue == 2) + pwd = true; + tmpValue = em4x05_Sniff_GetBlock (&bits[11], fwd); + sprintf (dataText,"%08X",tmpValue); + } + + // write 2 test + if ((strncmp (bits,"00101",5) == 0) && (bitidx == 57)) { + haveData = true; + sprintf (cmdText,"Write"); + tmpValue = 0; + tmpValue = (bits[5] - '0') + ((bits[6] - '0') << 1) + ((bits[7] - '0') << 2) + ((bits[8] - '0') << 3); + sprintf (blkAddr,"%d",tmpValue); + if (tmpValue == 2) + pwd = true; + tmpValue = em4x05_Sniff_GetBlock (&bits[12], fwd); + sprintf (dataText,"%08X",tmpValue); + } +/* + if ((Bits.SubString(1,4) == "1001") && (Bits.Length() == 11)) { + Addr = Bits.SubString(5,7); + Data = ""; + Memo6->Lines->Add("Read : "+Addr+" "+Data); + } + + if ((Bits.SubString(1,4) == "1100") && (Bits.Length() == 49)) { + Addr = ""; + Data = Bits.SubString(5,45); + Memo6->Lines->Add("Protect : "+Addr+" "+Data); + } + + if ((Bits.SubString(1,4) == "1010") && (Bits.Length() == 49)) { + Addr = ""; + Data = Bits.SubString(5,45); + Memo6->Lines->Add("Disable : "+Addr+" "+Data); + } +*/ + // Memo6->Lines->Add("Raw : "+Bits); + bits[bitidx] = 0; + // printf ("%s\n",bits); + } else { + i = (CycleWidth - ZeroWidth) / 30; + bits[bitidx++] = '0'; + for (int ii = 0; ii < i; ii++) + bits[bitidx++] = '1'; + } + } + } + } + idx++; + + // Print results + if (haveData) { //&& (minWidth > 1) && (maxWidth > minWidth)){ + if (pwd) + PrintAndLogEx(SUCCESS, "%6llu | %-10s | "_YELLOW_("%8s")" | "_YELLOW_("%3s")" | %s", pktOffset, cmdText, dataText, blkAddr, bits); + else + PrintAndLogEx(SUCCESS, "%6llu | %-10s | "_GREEN_("%8s")" | "_GREEN_("%3s")" | %s", pktOffset, cmdText, dataText, blkAddr, bits); + } + } + + // footer + PrintAndLogEx(SUCCESS, "---------------------------------------------------------------------------------------------------"); + PrintAndLogEx(NORMAL, ""); + + return PM3_SUCCESS; +} diff --git a/client/src/cmdlfem4x05.h b/client/src/cmdlfem4x05.h index 0c41f431f..276afebc2 100644 --- a/client/src/cmdlfem4x05.h +++ b/client/src/cmdlfem4x05.h @@ -27,5 +27,6 @@ int CmdEM4x05Wipe(const char *Cmd); int CmdEM4x05Info(const char *Cmd); int CmdEM4x05Chk(const char *Cmd); int CmdEM4x05Unlock(const char *Cmd); +int CmdEM4x05Sniff(const char *Cmd); #endif