proxmark3/client/cmdlfem4x.c
marshmellow42 6e98444637 lfdemod reduce duplicate code
remove check for best start position in demods and adjust the clock
detect to always return best start position to the demods
also small bug fix in lf em em4x50read
2015-04-02 23:55:12 -04:00

621 lines
16 KiB
C

//-----------------------------------------------------------------------------
// Copyright (C) 2010 iZsh <izsh at fail0verflow.com>
//
// This code is licensed to you under the terms of the GNU GPL, version 2 or,
// at your option, any later version. See the LICENSE.txt file for the text of
// the license.
//-----------------------------------------------------------------------------
// Low frequency EM4x commands
//-----------------------------------------------------------------------------
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include "proxmark3.h"
#include "ui.h"
#include "util.h"
#include "graph.h"
#include "cmdparser.h"
#include "cmddata.h"
#include "cmdlf.h"
#include "cmdlfem4x.h"
#include "lfdemod.h"
char *global_em410xId;
static int CmdHelp(const char *Cmd);
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;
}
/* Read the ID of an EM410x tag.
* Format:
* 1111 1111 1 <-- standard non-repeatable header
* XXXX [row parity bit] <-- 10 rows of 5 bits for our 40 bit tag ID
* ....
* CCCC <-- each bit here is parity for the 10 bits above in corresponding column
* 0 <-- stop bit, end of tag
*/
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;
}
// emulate an EM410X tag
int CmdEM410xSim(const char *Cmd)
{
int i, n, j, binary[4], parity[4];
char cmdp = param_getchar(Cmd, 0);
uint8_t uid[5] = {0x00};
if (cmdp == 'h' || cmdp == 'H') {
PrintAndLog("Usage: lf em4x 410xsim <UID>");
PrintAndLog("");
PrintAndLog(" sample: lf em4x 410xsim 0F0368568B");
return 0;
}
if (param_gethex(Cmd, 0, uid, 10)) {
PrintAndLog("UID must include 10 HEX symbols");
return 0;
}
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);
CmdLFSim("0"); //240 start_gap.
return 0;
}
/* Function is equivalent of lf read + data samples + em410xread
* looped until an EM410x tag is detected
*
* Why is CmdSamples("16000")?
* TBD: Auto-grow sample size based on detected sample rate. IE: If the
* rate gets lower, then grow the number of samples
* Changed by martin, 4000 x 4 = 16000,
* see http://www.proxmark.org/forum/viewtopic.php?pid=7235#p7235
*/
int CmdEM410xWatch(const char *Cmd)
{
do {
if (ukbhit()) {
printf("\naborted via keyboard!\n");
break;
}
CmdLFRead("s");
getSamples("8192",true); //capture enough to get 2 full messages
} while (!CmdEM410xRead(""));
return 0;
}
//currently only supports manchester modulations
int CmdEM410xWatchnSpoof(const char *Cmd)
{
CmdEM410xWatch(Cmd);
PrintAndLog("# Replaying captured ID: %s",global_em410xId);
CmdLFaskSim("");
return 0;
}
int CmdEM410xWrite(const char *Cmd)
{
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);
// Check ID
if (id == 0xFFFFFFFFFFFFFFFF) {
PrintAndLog("Error! ID is required.\n");
return 0;
}
if (id >= 0x10000000000) {
PrintAndLog("Error! Given EM410x ID is longer than 40 bits.\n");
return 0;
}
// Check Card
if (card == 0xFF) {
PrintAndLog("Error! Card type required.\n");
return 0;
}
if (card < 0) {
PrintAndLog("Error! Bad card type selected.\n");
return 0;
}
// Check Clock
if (card == 1)
{
// Default: 64
if (clock == 0)
clock = 64;
// Allowed clock rates: 16, 32 and 64
if ((clock != 16) && (clock != 32) && (clock != 64)) {
PrintAndLog("Error! Clock rate %d not valid. Supported clock rates are 16, 32 and 64.\n", clock);
return 0;
}
}
else if (clock != 0)
{
PrintAndLog("Error! Clock rate is only supported on T55x7 tags.\n");
return 0;
}
if (card == 1) {
PrintAndLog("Writing %s tag with UID 0x%010" PRIx64 " (clock rate: %d)", "T55x7", id, clock);
// NOTE: We really should pass the clock in as a separate argument, but to
// provide for backwards-compatibility for older firmware, and to avoid
// having to add another argument to CMD_EM410X_WRITE_TAG, we just store
// the clock rate in bits 8-15 of the card value
card = (card & 0xFF) | (((uint64_t)clock << 8) & 0xFF00);
}
else if (card == 0)
PrintAndLog("Writing %s tag with UID 0x%010" PRIx64, "T5555", id, clock);
else {
PrintAndLog("Error! Bad card type selected.\n");
return 0;
}
UsbCommand c = {CMD_EM410X_WRITE_TAG, {card, (uint32_t)(id >> 32), (uint32_t)id}};
SendCommand(&c);
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:
*
* 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
* CCCCCCCC <- 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.
*/
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<GraphTraceLen))
++i;
start= i;
while ((GraphBuffer[i] < high) && (i<GraphTraceLen))
++i;
while ((GraphBuffer[i] > low) && (i<GraphTraceLen))
++i;
if (j>=(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;
}
}
} else tol = clk/8;
// 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<end; block++){
PrintAndLog("Block %d: %08x",block,Code[block]);
}
if (AllPTest)
PrintAndLog("Parities Passed");
else
PrintAndLog("Parities Failed");
}
//restore GraphBuffer
save_restoreGB(0);
return (int)AllPTest;
}
int CmdEM4x50Read(const char *Cmd)
{
return EM4x50Read(Cmd, true);
}
int CmdReadWord(const char *Cmd)
{
int Word = -1; //default to invalid 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;
}
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);
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;
}
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 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;
}
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, "<UID> -- 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, "<UID> <'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, "<Word> -- Read EM4xxx word data"},
{"readwordPWD", CmdReadWordPWD, 1, "<Word> <Password> -- Read EM4xxx word data in password mode"},
{"writeword", CmdWriteWord, 1, "<Data> <Word> -- Write EM4xxx word data"},
{"writewordPWD", CmdWriteWordPWD, 1, "<Data> <Word> <Password> -- Write EM4xxx word data in password mode"},
{NULL, NULL, 0, NULL}
};
int CmdLFEM4X(const char *Cmd)
{
CmdsParse(CommandTable, Cmd);
return 0;
}
int CmdHelp(const char *Cmd)
{
CmdsHelp(CommandTable);
return 0;
}