mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2025-01-13 11:40:02 +08:00
deleted nearly all EM4x50 code; new EM4x50 code is in 'cmdlfem4x50.c' (device side instead GraphBuffer)
This commit is contained in:
parent
78511df9f2
commit
9aafc094b6
1 changed files with 4 additions and 365 deletions
|
@ -116,41 +116,6 @@ static int usage_lf_em410x_brute(void) {
|
|||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
//////////////// 4050 / 4450 commands
|
||||
static int usage_lf_em4x50_demod(void) {
|
||||
PrintAndLogEx(NORMAL, "Usage: lf em 4x50_demod [h]");
|
||||
PrintAndLogEx(NORMAL, "Options:");
|
||||
PrintAndLogEx(NORMAL, " h - this help");
|
||||
PrintAndLogEx(NORMAL, "Examples:");
|
||||
PrintAndLogEx(NORMAL, " lf em 4x50_demod");
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
static int usage_lf_em4x50_dump(void) {
|
||||
PrintAndLogEx(NORMAL, "Dump EM4x50/EM4x69. Tag must be on antenna. ");
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
PrintAndLogEx(NORMAL, "Usage: lf em 4x50_dump [h] <pwd>");
|
||||
PrintAndLogEx(NORMAL, "Options:");
|
||||
PrintAndLogEx(NORMAL, " h - this help");
|
||||
PrintAndLogEx(NORMAL, " pwd - password (hex) (optional)");
|
||||
PrintAndLogEx(NORMAL, "Examples:");
|
||||
PrintAndLogEx(NORMAL, " lf em 4x50_dump");
|
||||
PrintAndLogEx(NORMAL, " lf em 4x50_dump 11223344");
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
static int usage_lf_em4x50_read(void) {
|
||||
PrintAndLogEx(NORMAL, "Read EM 4x50/EM4x69. Tag must be on antenna. ");
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
PrintAndLogEx(NORMAL, "Usage: lf em 4x50_read [h] <address> <pwd>");
|
||||
PrintAndLogEx(NORMAL, "Options:");
|
||||
PrintAndLogEx(NORMAL, " h - this help");
|
||||
PrintAndLogEx(NORMAL, " address - memory address to read. (0-15)");
|
||||
PrintAndLogEx(NORMAL, " pwd - password (hex) (optional)");
|
||||
PrintAndLogEx(NORMAL, "Examples:");
|
||||
PrintAndLogEx(NORMAL, " lf em 4x50_read 1");
|
||||
PrintAndLogEx(NORMAL, " lf em 4x50_read 1 11223344");
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
//////////////// 4205 / 4305 commands
|
||||
static int usage_lf_em4x05_dump(void) {
|
||||
PrintAndLogEx(NORMAL, "Dump EM4x05/EM4x69. Tag must be on antenna. ");
|
||||
|
@ -723,329 +688,6 @@ static bool EM_ColParityTest(uint8_t *bs, size_t size, uint8_t rows, uint8_t col
|
|||
return true;
|
||||
}
|
||||
|
||||
// even parity ROW
|
||||
static bool EM_RowParityTest(uint8_t *bs, size_t size, uint8_t rows, uint8_t cols, uint8_t pType) {
|
||||
if (rows * cols > size) return false;
|
||||
uint8_t rowP = 0;
|
||||
|
||||
for (uint8_t r = 0; r < rows - 1; r++) {
|
||||
for (uint8_t c = 0; c < cols; c++) {
|
||||
rowP ^= bs[(r * cols) + c];
|
||||
}
|
||||
if (rowP != pType) return false;
|
||||
rowP = 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// EM word parity test.
|
||||
// 9*5 = 45 bits in total
|
||||
// 012345678|r0
|
||||
// 012345678|r1
|
||||
// 012345678|r2
|
||||
// 012345678|r3
|
||||
// ------------
|
||||
//c012345678| 0
|
||||
// |- must be zero
|
||||
|
||||
/*
|
||||
static int EMwordparitytest(uint8_t *bits) {
|
||||
|
||||
// last row/col parity must be 0
|
||||
if (bits[44] != 0) return PM3_ESOFT;
|
||||
|
||||
// col parity check
|
||||
uint8_t c1 = bytebits_to_byte(bits, 8) ^ bytebits_to_byte(bits + 9, 8) ^ bytebits_to_byte(bits + 18, 8) ^ bytebits_to_byte(bits + 27, 8);
|
||||
uint8_t c2 = bytebits_to_byte(bits + 36, 8);
|
||||
if (c1 != c2) return PM3_ESOFT;
|
||||
|
||||
// row parity check
|
||||
uint8_t rowP = 0;
|
||||
for (uint8_t i = 0; i < 36; ++i) {
|
||||
|
||||
rowP ^= bits[i];
|
||||
if (i > 0 && (i % 9) == 0) {
|
||||
|
||||
if (rowP != EVEN)
|
||||
return PM3_ESOFT;
|
||||
|
||||
rowP = 0;
|
||||
}
|
||||
}
|
||||
// all checks ok.
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
*/
|
||||
|
||||
//////////////// 4050 / 4450 commands
|
||||
|
||||
static 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) PrintAndLogEx(NORMAL, ""); //parity byte spacer
|
||||
PrintAndLogEx(NORMAL, "%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)
|
||||
);
|
||||
}
|
||||
|
||||
PrintAndLogEx(SUCCESS, "Parity checks | %s", (pTest) ? _GREEN_("Passed") : _RED_("Fail"));
|
||||
}
|
||||
return code;
|
||||
}
|
||||
|
||||
/* Read the transmitted data of an EM4x50 tag from the graphbuffer
|
||||
* Format:
|
||||
*
|
||||
* XXXXXXXX [row parity bit (even)] <- 8 bits plus parity
|
||||
* XXXXXXXX [row parity bit (even)] <- 8 bits plus parity
|
||||
* XXXXXXXX [row parity bit (even)] <- 8 bits plus parity
|
||||
* XXXXXXXX [row parity bit (even)] <- 8 bits plus parity
|
||||
* CCCCCCC0 <- column parity bits
|
||||
* 0 <- stop bit
|
||||
* LW <- Listen Window
|
||||
*
|
||||
* This pattern repeats for every block of data being transmitted.
|
||||
* Transmission starts with two Listen Windows (LW - a modulated
|
||||
* pattern of 320 cycles each (32/32/128/64/64)).
|
||||
*
|
||||
* Note that this data may or may not be the UID. It is whatever data
|
||||
* is stored in the blocks defined in the control word First and Last
|
||||
* Word Read values. UID is stored in block 32.
|
||||
*/
|
||||
//completed by Marshmellow
|
||||
int EM4x50Read(const char *Cmd, bool verbose) {
|
||||
int clk = 0, invert = 0, tol = 0, phaseoff;
|
||||
int i = 0, j = 0, startblock, skip, block, start, end, low = 0, high = 0;
|
||||
uint32_t Code[6];
|
||||
char tmp[6];
|
||||
char tmp2[20];
|
||||
bool complete = false;
|
||||
|
||||
int tmpbuff[MAX_GRAPH_TRACE_LEN / 64];
|
||||
memset(tmpbuff, 0, sizeof(tmpbuff));
|
||||
|
||||
// get user entry if any
|
||||
sscanf(Cmd, "%i %i", &clk, &invert);
|
||||
|
||||
uint8_t bits[MAX_GRAPH_TRACE_LEN] = {0};
|
||||
size_t size = getFromGraphBuf(bits);
|
||||
|
||||
if (size < 4000) {
|
||||
if (verbose || g_debugMode) PrintAndLogEx(ERR, "Error: EM4x50 - Too little data in Graphbuffer");
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
|
||||
computeSignalProperties(bits, size);
|
||||
|
||||
// get fuzzed HI / LOW limits in signal
|
||||
getHiLo(&high, &low, 75, 75);
|
||||
|
||||
// get to first full low to prime loop and skip incomplete first pulse
|
||||
size_t offset = 0;
|
||||
getNextHigh(bits, size, high, &offset);
|
||||
getNextLow(bits, size, low, &offset);
|
||||
|
||||
i = (int)offset;
|
||||
skip = offset;
|
||||
|
||||
// set clock
|
||||
if (clk == 0) {
|
||||
DetectASKClock(bits, size, &clk, 0);
|
||||
if (clk == 0) {
|
||||
if (verbose || g_debugMode) PrintAndLogEx(ERR, "Error: EM4x50 - didn't find a clock");
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
}
|
||||
// tolerance
|
||||
tol = clk / 8;
|
||||
|
||||
// populate tmpbuff buffer with pulse lengths
|
||||
while (i < size) {
|
||||
// measure from low to low
|
||||
while ((i < size) && (bits[i] > low))
|
||||
++i;
|
||||
start = i;
|
||||
|
||||
while ((i < size) && (bits[i] < high))
|
||||
++i;
|
||||
|
||||
while ((i < size) && (bits[i] > low))
|
||||
++i;
|
||||
|
||||
if (j >= (MAX_GRAPH_TRACE_LEN / 64)) {
|
||||
break;
|
||||
}
|
||||
tmpbuff[j++] = i - start;
|
||||
}
|
||||
|
||||
// look for data start - should be 2 pairs of LW (pulses of clk*3,clk*2)
|
||||
start = -1;
|
||||
for (i = 0; i < j - 4 ; ++i) {
|
||||
skip += tmpbuff[i];
|
||||
if (tmpbuff[i] >= clk * 3 - tol && tmpbuff[i] <= clk * 3 + tol) //3 clocks
|
||||
if (tmpbuff[i + 1] >= clk * 2 - tol && tmpbuff[i + 1] <= clk * 2 + tol) //2 clocks
|
||||
if (tmpbuff[i + 2] >= clk * 3 - tol && tmpbuff[i + 2] <= clk * 3 + tol) //3 clocks
|
||||
if (tmpbuff[i + 3] >= clk - tol) { //1.5 to 2 clocks - depends on bit following
|
||||
start = i + 4;
|
||||
break;
|
||||
}
|
||||
}
|
||||
startblock = i + 4;
|
||||
|
||||
// skip over the remainder of LW
|
||||
skip += (tmpbuff[i + 1] + tmpbuff[i + 2] + clk);
|
||||
|
||||
if (tmpbuff[i + 3] > clk)
|
||||
phaseoff = tmpbuff[i + 3] - clk;
|
||||
else
|
||||
phaseoff = 0;
|
||||
|
||||
// now do it again to find the end
|
||||
for (i += 3; i < j - 4 ; ++i) {
|
||||
if (tmpbuff[i] >= clk * 3 - tol && tmpbuff[i] <= clk * 3 + tol) //3 clocks
|
||||
if (tmpbuff[i + 1] >= clk * 2 - tol && tmpbuff[i + 1] <= clk * 2 + tol) //2 clocks
|
||||
if (tmpbuff[i + 2] >= clk * 3 - tol && tmpbuff[i + 2] <= clk * 3 + tol) //3 clocks
|
||||
if (tmpbuff[i + 3] >= clk - tol) { //1.5 to 2 clocks - depends on bit following
|
||||
complete = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
end = i;
|
||||
|
||||
// report back
|
||||
if (verbose || g_debugMode) {
|
||||
if (start >= 0) {
|
||||
PrintAndLogEx(INFO, "\nNote: one block = 50 bits (32 data, 12 parity, 6 marker)");
|
||||
} else {
|
||||
PrintAndLogEx(INFO, "No data found!, clock tried: " _YELLOW_("%d"), clk);
|
||||
PrintAndLogEx(HINT, "Try again with more samples");
|
||||
PrintAndLogEx(HINT, " or after a " _YELLOW_("'data askedge'") " command to clean up the read");
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
} else if (start < 0) {
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
|
||||
start = skip;
|
||||
|
||||
snprintf(tmp2, sizeof(tmp2), "%d %d 1000 %d", clk, invert, clk * 47);
|
||||
|
||||
// save GraphBuffer - to restore it later
|
||||
save_restoreGB(GRAPH_SAVE);
|
||||
|
||||
// get rid of leading crap
|
||||
snprintf(tmp, sizeof(tmp), "%i", skip);
|
||||
CmdLtrim(tmp);
|
||||
|
||||
bool AllPTest = true;
|
||||
|
||||
// now work through remaining buffer printing out data blocks
|
||||
block = 0;
|
||||
i = startblock;
|
||||
while (block < 6) {
|
||||
if (verbose || g_debugMode) PrintAndLogEx(NORMAL, "\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;
|
||||
}
|
||||
if (i >= j - 4) break; //next LW not found
|
||||
skip += clk;
|
||||
if (tmpbuff[i + 1] > clk)
|
||||
phaseoff = tmpbuff[i + 1] - clk;
|
||||
else
|
||||
phaseoff = 0;
|
||||
|
||||
i += 2;
|
||||
|
||||
if (ASKDemod(tmp2, false, false, 1) != PM3_SUCCESS) {
|
||||
save_restoreGB(GRAPH_RESTORE);
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
|
||||
//set DemodBufferLen to just one block
|
||||
DemodBufferLen = skip / clk;
|
||||
//test parities
|
||||
bool pTest = EM_RowParityTest(DemodBuffer, DemodBufferLen, 5, 9, 0);
|
||||
pTest &= EM_ColParityTest(DemodBuffer, DemodBufferLen, 5, 9, 0);
|
||||
AllPTest &= pTest;
|
||||
//get output
|
||||
Code[block] = OutputEM4x50_Block(DemodBuffer, DemodBufferLen, verbose, pTest);
|
||||
PrintAndLogEx(DEBUG, "\nskipping %d samples, bits:%d", skip, 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) {
|
||||
if (!complete) {
|
||||
PrintAndLogEx(WARNING, _RED_("* **Warning!"));
|
||||
PrintAndLogEx(INFO, "Partial data - no end found!");
|
||||
PrintAndLogEx(HINT, "Try again with more samples.");
|
||||
}
|
||||
PrintAndLogEx(INFO, "Found data at sample: %i - using clock: %i", start, clk);
|
||||
end = block;
|
||||
PrintAndLogEx(INFO, "blk | data");
|
||||
PrintAndLogEx(INFO, "----+--------------");
|
||||
for (block = 0; block < end; block++) {
|
||||
PrintAndLogEx(INFO, "%03d | %08x", block, Code[block]);
|
||||
}
|
||||
PrintAndLogEx(INFO, "----+--------------");
|
||||
PrintAndLogEx((AllPTest) ? SUCCESS : WARNING, "Parities checks | %s", (AllPTest) ? _GREEN_("Passed") : _RED_("Fail"));
|
||||
|
||||
if (AllPTest == false) {
|
||||
PrintAndLogEx(HINT, "Try cleaning the read samples with " _YELLOW_("'data askedge'"));
|
||||
}
|
||||
}
|
||||
|
||||
//restore GraphBuffer
|
||||
save_restoreGB(GRAPH_RESTORE);
|
||||
return AllPTest ? PM3_SUCCESS : PM3_ESOFT;
|
||||
}
|
||||
|
||||
static int CmdEM4x50Demod(const char *Cmd) {
|
||||
uint8_t ctmp = tolower(param_getchar(Cmd, 0));
|
||||
if (ctmp == 'h') return usage_lf_em4x50_demod();
|
||||
return EM4x50Read(Cmd, true);
|
||||
}
|
||||
|
||||
static int CmdEM4x50Read(const char *Cmd) {
|
||||
uint8_t ctmp = tolower(param_getchar(Cmd, 0));
|
||||
if (ctmp == 'h') return usage_lf_em4x50_read();
|
||||
lf_read(false, 24000);
|
||||
return EM4x50Read(Cmd, true);
|
||||
}
|
||||
|
||||
static int CmdEM4x50Dump(const char *Cmd) {
|
||||
uint8_t ctmp = tolower(param_getchar(Cmd, 0));
|
||||
if (ctmp == 'h') return usage_lf_em4x50_dump();
|
||||
PrintAndLogEx(NORMAL, "no implemented yet");
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
#define EM_PREAMBLE_LEN 6
|
||||
// download samples from device and copy to Graphbuffer
|
||||
static bool downloadSamplesEM(void) {
|
||||
|
@ -1753,13 +1395,10 @@ static command_t CommandTable[] = {
|
|||
{"4x05_read", CmdEM4x05Read, IfPm3Lf, "read word data from EM4x05/EM4x69"},
|
||||
{"4x05_write", CmdEM4x05Write, IfPm3Lf, "write word data to EM4x05/EM4x69"},
|
||||
{"----------", CmdHelp, AlwaysAvailable, "----------------------- " _CYAN_("EM 4x50") " -----------------------"},
|
||||
{"4x50_demod", CmdEM4x50Demod, AlwaysAvailable, "demodulate a EM4x50 tag from the GraphBuffer"},
|
||||
{"4x50_dump", CmdEM4x50Dump, IfPm3Lf, "dump EM4x50 tag"},
|
||||
{"4x50_read", CmdEM4x50Read, IfPm3Lf, "read word data from EM4x50"},
|
||||
{"4x50_info", CmdEM4x50Info, IfPm3Lf, "read complete data from EM4x50"},
|
||||
{"4x50_write", CmdEM4x50Write, IfPm3Lf, "write word data to EM4x50"},
|
||||
{"4x50_write_password", CmdEM4x50WritePassword, IfPm3Lf, "change passwword of EM4x50 tag"},
|
||||
{"4x50_sread", CmdEM4x50SRead, IfPm3Lf, "read word data from EM4x50 on device"},
|
||||
{"4x50_info", CmdEM4x50Info, IfPm3EM4x50, "read complete data from EM4x50"},
|
||||
{"4x50_write", CmdEM4x50Write, IfPm3EM4x50, "write word data to EM4x50"},
|
||||
{"4x50_write_password", CmdEM4x50WritePassword, IfPm3EM4x50, "change passwword of EM4x50 tag"},
|
||||
{"4x50_read", CmdEM4x50Read, IfPm3EM4x50, "read word data from EM4x50"},
|
||||
{NULL, NULL, NULL, NULL}
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue