Merge branch 'master' into allin
update
This commit is contained in:
@@ -9,6 +9,7 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "cmdlfem4x.h"
|
||||
#include "cmdlfem4x05.h"
|
||||
#include "cmdlfem4x50.h"
|
||||
|
||||
#include <stdio.h>
|
||||
@@ -30,6 +31,9 @@
|
||||
#include "cmddata.h"
|
||||
#include "cmdlf.h"
|
||||
#include "lfdemod.h"
|
||||
#include "generator.h"
|
||||
#include "cliparser.h"
|
||||
#include "cmdhw.h"
|
||||
|
||||
static uint64_t g_em410xid = 0;
|
||||
|
||||
@@ -116,75 +120,6 @@ static int usage_lf_em410x_brute(void) {
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
//////////////// 4205 / 4305 commands
|
||||
static int usage_lf_em4x05_dump(void) {
|
||||
PrintAndLogEx(NORMAL, "Dump EM4x05/EM4x69. Tag must be on antenna. ");
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
PrintAndLogEx(NORMAL, "Usage: lf em 4x05_dump [h] [f <filename prefix>] <pwd>");
|
||||
PrintAndLogEx(NORMAL, "Options:");
|
||||
PrintAndLogEx(NORMAL, " h - this help");
|
||||
PrintAndLogEx(NORMAL, " f <filename prefix> - overide filename prefix (optional). Default is based on UID");
|
||||
PrintAndLogEx(NORMAL, " pwd - password (hex) (optional)");
|
||||
PrintAndLogEx(NORMAL, "Examples:");
|
||||
PrintAndLogEx(NORMAL, " lf em 4x05_dump");
|
||||
PrintAndLogEx(NORMAL, " lf em 4x05_dump 11223344");
|
||||
PrintAndLogEx(NORMAL, " lf em 4x05_dump f card1 11223344");
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
static int usage_lf_em4x05_wipe(void) {
|
||||
PrintAndLogEx(NORMAL, "Wipe EM4x05/EM4x69. Tag must be on antenna. ");
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
PrintAndLogEx(NORMAL, "Usage: lf em 4x05_wipe [h] <pwd>");
|
||||
PrintAndLogEx(NORMAL, "Options:");
|
||||
PrintAndLogEx(NORMAL, " h - this help");
|
||||
PrintAndLogEx(NORMAL, " c - chip type : 0 em4205");
|
||||
PrintAndLogEx(NORMAL, " 1 em4305 (default)");
|
||||
PrintAndLogEx(NORMAL, " pwd - password (hex) (optional)");
|
||||
PrintAndLogEx(NORMAL, "Examples:");
|
||||
PrintAndLogEx(NORMAL, " lf em 4x05_wipe");
|
||||
PrintAndLogEx(NORMAL, " lf em 4x05_wipe 11223344");
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
static int usage_lf_em4x05_read(void) {
|
||||
PrintAndLogEx(NORMAL, "Read EM4x05/EM4x69. Tag must be on antenna. ");
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
PrintAndLogEx(NORMAL, "Usage: lf em 4x05_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 4x05_read 1");
|
||||
PrintAndLogEx(NORMAL, " lf em 4x05_read 1 11223344");
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
static int usage_lf_em4x05_write(void) {
|
||||
PrintAndLogEx(NORMAL, "Write EM4x05/4x69. Tag must be on antenna. ");
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
PrintAndLogEx(NORMAL, "Usage: lf em 4x05_write [h] <address> <data> <pwd>");
|
||||
PrintAndLogEx(NORMAL, "Options:");
|
||||
PrintAndLogEx(NORMAL, " h - this help");
|
||||
PrintAndLogEx(NORMAL, " address - memory address to write to. (0-15)");
|
||||
PrintAndLogEx(NORMAL, " data - data to write (hex)");
|
||||
PrintAndLogEx(NORMAL, " pwd - password (hex) (optional)");
|
||||
PrintAndLogEx(NORMAL, "Examples:");
|
||||
PrintAndLogEx(NORMAL, " lf em 4x05_write 1 deadc0de");
|
||||
PrintAndLogEx(NORMAL, " lf em 4x05_write 1 deadc0de 11223344");
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
static int usage_lf_em4x05_info(void) {
|
||||
PrintAndLogEx(NORMAL, "Tag information EM4205/4305/4469//4569 tags. Tag must be on antenna.");
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
PrintAndLogEx(NORMAL, "Usage: lf em 4x05_info [h] <pwd>");
|
||||
PrintAndLogEx(NORMAL, "Options:");
|
||||
PrintAndLogEx(NORMAL, " h - this help");
|
||||
PrintAndLogEx(NORMAL, " pwd - password (hex) (optional)");
|
||||
PrintAndLogEx(NORMAL, "Examples:");
|
||||
PrintAndLogEx(NORMAL, " lf em 4x05_info");
|
||||
PrintAndLogEx(NORMAL, " lf em 4x05_info deadc0de");
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
/* Read the ID of an EM410x tag.
|
||||
* Format:
|
||||
* 1111 1111 1 <-- standard non-repeatable header
|
||||
@@ -691,712 +626,6 @@ static int CmdEM410xClone(const char *Cmd) {
|
||||
return resp.status;
|
||||
}
|
||||
|
||||
//**************** Start of EM4x50 Code ************************
|
||||
|
||||
// even parity COLUMN
|
||||
static bool EM_ColParityTest(uint8_t *bs, size_t size, uint8_t rows, uint8_t cols, uint8_t pType) {
|
||||
if (rows * cols > size) return false;
|
||||
uint8_t colP = 0;
|
||||
|
||||
for (uint8_t c = 0; c < cols - 1; c++) {
|
||||
for (uint8_t r = 0; r < rows; r++) {
|
||||
colP ^= bs[(r * cols) + c];
|
||||
}
|
||||
if (colP != pType) return false;
|
||||
colP = 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#define EM_PREAMBLE_LEN 6
|
||||
// download samples from device and copy to Graphbuffer
|
||||
static bool downloadSamplesEM(void) {
|
||||
|
||||
// 8 bit preamble + 32 bit word response (max clock (128) * 40bits = 5120 samples)
|
||||
uint8_t got[6000];
|
||||
if (!GetFromDevice(BIG_BUF, got, sizeof(got), 0, NULL, 0, NULL, 2500, false)) {
|
||||
PrintAndLogEx(WARNING, "(downloadSamplesEM) command execution time out");
|
||||
return false;
|
||||
}
|
||||
|
||||
setGraphBuf(got, sizeof(got));
|
||||
// set signal properties low/high/mean/amplitude and is_noise detection
|
||||
computeSignalProperties(got, sizeof(got));
|
||||
RepaintGraphWindow();
|
||||
if (getSignalProperties()->isnoise) {
|
||||
PrintAndLogEx(DEBUG, "No tag found - signal looks like noise");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// em_demod
|
||||
static bool doPreambleSearch(size_t *startIdx) {
|
||||
|
||||
// sanity check
|
||||
if (DemodBufferLen < EM_PREAMBLE_LEN) {
|
||||
PrintAndLogEx(DEBUG, "DEBUG: Error - EM4305 demodbuffer too small");
|
||||
return false;
|
||||
}
|
||||
|
||||
// set size to 20 to only test first 14 positions for the preamble
|
||||
size_t size = (20 > DemodBufferLen) ? DemodBufferLen : 20;
|
||||
*startIdx = 0;
|
||||
// skip first two 0 bits as they might have been missed in the demod
|
||||
uint8_t preamble[EM_PREAMBLE_LEN] = {0, 0, 1, 0, 1, 0};
|
||||
|
||||
if (!preambleSearchEx(DemodBuffer, preamble, EM_PREAMBLE_LEN, &size, startIdx, true)) {
|
||||
PrintAndLogEx(DEBUG, "DEBUG: Error - EM4305 preamble not found :: %zu", *startIdx);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool detectFSK(void) {
|
||||
// detect fsk clock
|
||||
if (GetFskClock("", false) == 0) {
|
||||
PrintAndLogEx(DEBUG, "DEBUG: Error - EM: FSK clock failed");
|
||||
return false;
|
||||
}
|
||||
// demod
|
||||
int ans = FSKrawDemod(0, 0, 0, 0, false);
|
||||
if (ans != PM3_SUCCESS) {
|
||||
PrintAndLogEx(DEBUG, "DEBUG: Error - EM: FSK Demod failed");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// PSK clocks should be easy to detect ( but difficult to demod a non-repeating pattern... )
|
||||
static bool detectPSK(void) {
|
||||
int ans = GetPskClock("", false);
|
||||
if (ans <= 0) {
|
||||
PrintAndLogEx(DEBUG, "DEBUG: Error - EM: PSK clock failed");
|
||||
return false;
|
||||
}
|
||||
//demod
|
||||
//try psk1 -- 0 0 6 (six errors?!?)
|
||||
ans = PSKDemod(0, 0, 6, false);
|
||||
if (ans != PM3_SUCCESS) {
|
||||
PrintAndLogEx(DEBUG, "DEBUG: Error - EM: PSK1 Demod failed");
|
||||
|
||||
//try psk1 inverted
|
||||
ans = PSKDemod(0, 1, 6, false);
|
||||
if (ans != PM3_SUCCESS) {
|
||||
PrintAndLogEx(DEBUG, "DEBUG: Error - EM: PSK1 inverted Demod failed");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// either PSK1 or PSK1 inverted is ok from here.
|
||||
// lets check PSK2 later.
|
||||
return true;
|
||||
}
|
||||
// try manchester - NOTE: ST only applies to T55x7 tags.
|
||||
static bool detectASK_MAN(void) {
|
||||
bool stcheck = false;
|
||||
if (ASKDemod_ext(0, 0, 0, 0, false, false, false, 1, &stcheck) != PM3_SUCCESS) {
|
||||
PrintAndLogEx(DEBUG, "DEBUG: Error - EM: ASK/Manchester Demod failed");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool detectASK_BI(void) {
|
||||
int ans = ASKbiphaseDemod(0, 0, 1, 50, false);
|
||||
if (ans != PM3_SUCCESS) {
|
||||
PrintAndLogEx(DEBUG, "DEBUG: Error - EM: ASK/biphase normal demod failed");
|
||||
|
||||
ans = ASKbiphaseDemod(0, 1, 1, 50, false);
|
||||
if (ans != PM3_SUCCESS) {
|
||||
PrintAndLogEx(DEBUG, "DEBUG: Error - EM: ASK/biphase inverted demod failed");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
static bool detectNRZ(void) {
|
||||
int ans = NRZrawDemod(0, 0, 1, false);
|
||||
if (ans != PM3_SUCCESS) {
|
||||
PrintAndLogEx(DEBUG, "DEBUG: Error - EM: NRZ normal demod failed");
|
||||
|
||||
ans = NRZrawDemod(0, 1, 1, false);
|
||||
if (ans != PM3_SUCCESS) {
|
||||
PrintAndLogEx(DEBUG, "DEBUG: Error - EM: NRZ inverted demod failed");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// param: idx - start index in demoded data.
|
||||
static int setDemodBufferEM(uint32_t *word, size_t idx) {
|
||||
|
||||
//test for even parity bits.
|
||||
uint8_t parity[45] = {0};
|
||||
memcpy(parity, DemodBuffer, 45);
|
||||
if (!EM_ColParityTest(DemodBuffer + idx + EM_PREAMBLE_LEN, 45, 5, 9, 0)) {
|
||||
PrintAndLogEx(DEBUG, "DEBUG: Error - End Parity check failed");
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
|
||||
// test for even parity bits and remove them. (leave out the end row of parities so 36 bits)
|
||||
if (!removeParity(DemodBuffer, idx + EM_PREAMBLE_LEN, 9, 0, 36)) {
|
||||
PrintAndLogEx(DEBUG, "DEBUG: Error - EM, failed removing parity");
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
setDemodBuff(DemodBuffer, 32, 0);
|
||||
*word = bytebits_to_byteLSBF(DemodBuffer, 32);
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
// FSK, PSK, ASK/MANCHESTER, ASK/BIPHASE, ASK/DIPHASE, NRZ
|
||||
// should cover 90% of known used configs
|
||||
// the rest will need to be manually demoded for now...
|
||||
static int demodEM4x05resp(uint32_t *word) {
|
||||
size_t idx = 0;
|
||||
*word = 0;
|
||||
if (detectASK_MAN() && doPreambleSearch(&idx))
|
||||
return setDemodBufferEM(word, idx);
|
||||
|
||||
if (detectASK_BI() && doPreambleSearch(&idx))
|
||||
return setDemodBufferEM(word, idx);
|
||||
|
||||
if (detectNRZ() && doPreambleSearch(&idx))
|
||||
return setDemodBufferEM(word, idx);
|
||||
|
||||
if (detectFSK() && doPreambleSearch(&idx))
|
||||
return setDemodBufferEM(word, idx);
|
||||
|
||||
if (detectPSK()) {
|
||||
if (doPreambleSearch(&idx))
|
||||
return setDemodBufferEM(word, idx);
|
||||
|
||||
psk1TOpsk2(DemodBuffer, DemodBufferLen);
|
||||
if (doPreambleSearch(&idx))
|
||||
return setDemodBufferEM(word, idx);
|
||||
}
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
|
||||
//////////////// 4205 / 4305 commands
|
||||
#include "util_posix.h" // msclock
|
||||
static int EM4x05ReadWord_ext(uint8_t addr, uint32_t pwd, bool usePwd, uint32_t *word) {
|
||||
|
||||
struct {
|
||||
uint32_t password;
|
||||
uint8_t address;
|
||||
uint8_t usepwd;
|
||||
} PACKED payload;
|
||||
|
||||
payload.password = pwd;
|
||||
payload.address = addr;
|
||||
payload.usepwd = usePwd;
|
||||
|
||||
clearCommandBuffer();
|
||||
SendCommandNG(CMD_LF_EM4X_READWORD, (uint8_t *)&payload, sizeof(payload));
|
||||
PacketResponseNG resp;
|
||||
if (!WaitForResponseTimeout(CMD_LF_EM4X_READWORD, &resp, 10000)) {
|
||||
PrintAndLogEx(WARNING, "(EM4x05ReadWord_ext) timeout while waiting for reply.");
|
||||
return PM3_ETIMEOUT;
|
||||
}
|
||||
|
||||
if (downloadSamplesEM() == false) {
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
return demodEM4x05resp(word);
|
||||
}
|
||||
|
||||
static int CmdEM4x05Demod(const char *Cmd) {
|
||||
// uint8_t ctmp = tolower(param_getchar(Cmd, 0));
|
||||
// if (ctmp == 'h') return usage_lf_em4x05_demod();
|
||||
uint32_t word = 0;
|
||||
return demodEM4x05resp(&word);
|
||||
}
|
||||
|
||||
static int CmdEM4x05Dump(const char *Cmd) {
|
||||
uint8_t addr = 0;
|
||||
uint32_t pwd = 0;
|
||||
bool usePwd = false;
|
||||
uint8_t cmdp = 0;
|
||||
uint8_t bytes[4] = {0};
|
||||
uint32_t data[16];
|
||||
char preferredName[FILE_PATH_SIZE] = {0};
|
||||
char optchk[10];
|
||||
|
||||
while (param_getchar(Cmd, cmdp) != 0x00) {
|
||||
switch (tolower(param_getchar(Cmd, cmdp))) {
|
||||
case 'h':
|
||||
return usage_lf_em4x05_dump();
|
||||
break;
|
||||
case 'f': // since f could match in password, lets confirm it is 1 character only for an option
|
||||
param_getstr(Cmd, cmdp, optchk, sizeof(optchk));
|
||||
if (strlen(optchk) == 1) { // Have a single character f so filename no password
|
||||
param_getstr(Cmd, cmdp + 1, preferredName, FILE_PATH_SIZE);
|
||||
cmdp += 2;
|
||||
break;
|
||||
} // if not a single 'f' dont break and flow onto default as should be password
|
||||
|
||||
default : // for backwards-compatibility options should be > 'f' else assume its the hex password`
|
||||
// for now use default input of 1 as invalid (unlikely 1 will be a valid password...)
|
||||
pwd = param_get32ex(Cmd, cmdp, 1, 16);
|
||||
if (pwd != 1)
|
||||
usePwd = true;
|
||||
cmdp++;
|
||||
};
|
||||
}
|
||||
|
||||
int success = PM3_SUCCESS;
|
||||
int status;
|
||||
uint32_t lock_bits = 0x00; // no blocks locked
|
||||
|
||||
uint32_t word = 0;
|
||||
PrintAndLogEx(NORMAL, "Addr | data | ascii |lck| info");
|
||||
PrintAndLogEx(NORMAL, "-----+----------+-------+---+-----");
|
||||
|
||||
// To flag any blocks locked we need to read blocks 14 and 15 first
|
||||
// dont swap endin until we get block lock flags.
|
||||
status = EM4x05ReadWord_ext(14, pwd, usePwd, &word);
|
||||
if (status != PM3_SUCCESS)
|
||||
success = PM3_ESOFT; // If any error ensure fail is set so not to save invalid data
|
||||
if (word != 0x00)
|
||||
lock_bits = word;
|
||||
data[14] = word;
|
||||
|
||||
status = EM4x05ReadWord_ext(15, pwd, usePwd, &word);
|
||||
if (status != PM3_SUCCESS)
|
||||
success = PM3_ESOFT; // If any error ensure fail is set so not to save invalid data
|
||||
if (word != 0x00) // assume block 15 is the current lock block
|
||||
lock_bits = word;
|
||||
data[15] = word;
|
||||
|
||||
// Now read blocks 0 - 13 as we have 14 and 15
|
||||
for (; addr < 14; addr++) {
|
||||
|
||||
if (addr == 2) {
|
||||
if (usePwd) {
|
||||
data[addr] = BSWAP_32(pwd);
|
||||
num_to_bytes(pwd, 4, bytes);
|
||||
PrintAndLogEx(NORMAL, " %02u | %08X | %s | %c | password", addr, pwd, sprint_ascii(bytes, 4), ((lock_bits >> addr) & 1) ? 'x' : ' ');
|
||||
} else {
|
||||
data[addr] = 0x00; // Unknown password, but not used to set to zeros
|
||||
PrintAndLogEx(NORMAL, " 02 | | | | " _RED_("cannot read"));
|
||||
}
|
||||
} else {
|
||||
// success &= EM4x05ReadWord_ext(addr, pwd, usePwd, &word);
|
||||
status = EM4x05ReadWord_ext(addr, pwd, usePwd, &word); // Get status for single read
|
||||
if (status != PM3_SUCCESS)
|
||||
success = PM3_ESOFT; // If any error ensure fail is set so not to save invalid data
|
||||
data[addr] = BSWAP_32(word);
|
||||
if (status == PM3_SUCCESS) {
|
||||
num_to_bytes(word, 4, bytes);
|
||||
PrintAndLogEx(NORMAL, " %02d | %08X | %s | %c |", addr, word, sprint_ascii(bytes, 4), ((lock_bits >> addr) & 1) ? 'x' : ' ');
|
||||
} else
|
||||
PrintAndLogEx(NORMAL, " %02d | | | | " _RED_("Fail"), addr);
|
||||
}
|
||||
}
|
||||
// Print blocks 14 and 15
|
||||
// Both lock bits are protected with bit idx 14 (special case)
|
||||
PrintAndLogEx(NORMAL, " %02d | %08X | %s | %c | Lock", 14, data[14], sprint_ascii(bytes, 4), ((lock_bits >> 14) & 1) ? 'x' : ' ');
|
||||
PrintAndLogEx(NORMAL, " %02d | %08X | %s | %c | Lock", 15, data[15], sprint_ascii(bytes, 4), ((lock_bits >> 14) & 1) ? 'x' : ' ');
|
||||
// Update endian for files
|
||||
data[14] = BSWAP_32(data[14]);
|
||||
data[15] = BSWAP_32(data[15]);
|
||||
|
||||
if (success == PM3_SUCCESS) { // all ok save dump to file
|
||||
// saveFileEML will add .eml extension to filename
|
||||
// saveFile (binary) passes in the .bin extension.
|
||||
if (strcmp(preferredName, "") == 0) // Set default filename, if not set by user
|
||||
sprintf(preferredName, "lf-4x05-%08X-dump", BSWAP_32(data[1]));
|
||||
|
||||
saveFileEML(preferredName, (uint8_t *)data, 16 * sizeof(uint32_t), sizeof(uint32_t));
|
||||
saveFile(preferredName, ".bin", data, sizeof(data));
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
static int CmdEM4x05Read(const char *Cmd) {
|
||||
uint8_t addr;
|
||||
uint32_t pwd;
|
||||
bool usePwd = false;
|
||||
|
||||
uint8_t ctmp = tolower(param_getchar(Cmd, 0));
|
||||
if (strlen(Cmd) == 0 || ctmp == 'h') return usage_lf_em4x05_read();
|
||||
|
||||
addr = param_get8ex(Cmd, 0, 50, 10);
|
||||
pwd = param_get32ex(Cmd, 1, 0xFFFFFFFF, 16);
|
||||
|
||||
if (addr > 15) {
|
||||
PrintAndLogEx(NORMAL, "Address must be between 0 and 15");
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
if (pwd == 0xFFFFFFFF) {
|
||||
PrintAndLogEx(NORMAL, "Reading address %02u", addr);
|
||||
} else {
|
||||
usePwd = true;
|
||||
PrintAndLogEx(NORMAL, "Reading address %02u | password %08X", addr, pwd);
|
||||
}
|
||||
|
||||
uint32_t word = 0;
|
||||
int status = EM4x05ReadWord_ext(addr, pwd, usePwd, &word);
|
||||
if (status == PM3_SUCCESS)
|
||||
PrintAndLogEx(NORMAL, "Address %02d | %08X - %s", addr, word, (addr > 13) ? "Lock" : "");
|
||||
else
|
||||
PrintAndLogEx(NORMAL, "Read Address %02d | " _RED_("Fail"), addr);
|
||||
return status;
|
||||
}
|
||||
|
||||
static int CmdEM4x05Write(const char *Cmd) {
|
||||
uint8_t ctmp = tolower(param_getchar(Cmd, 0));
|
||||
if (strlen(Cmd) == 0 || ctmp == 'h') return usage_lf_em4x05_write();
|
||||
|
||||
bool usePwd = false;
|
||||
uint8_t addr;
|
||||
uint32_t data, pwd;
|
||||
|
||||
addr = param_get8ex(Cmd, 0, 50, 10);
|
||||
data = param_get32ex(Cmd, 1, 0, 16);
|
||||
pwd = param_get32ex(Cmd, 2, 0xFFFFFFFF, 16);
|
||||
|
||||
if (addr > 15) {
|
||||
PrintAndLogEx(NORMAL, "Address must be between 0 and 15");
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
if (pwd == 0xFFFFFFFF)
|
||||
PrintAndLogEx(NORMAL, "Writing address %d data %08X", addr, data);
|
||||
else {
|
||||
usePwd = true;
|
||||
PrintAndLogEx(NORMAL, "Writing address %d data %08X using password %08X", addr, data, pwd);
|
||||
}
|
||||
|
||||
struct {
|
||||
uint32_t password;
|
||||
uint32_t data;
|
||||
uint8_t address;
|
||||
uint8_t usepwd;
|
||||
} PACKED payload;
|
||||
|
||||
payload.password = pwd;
|
||||
payload.data = data;
|
||||
payload.address = addr;
|
||||
payload.usepwd = usePwd;
|
||||
|
||||
clearCommandBuffer();
|
||||
SendCommandNG(CMD_LF_EM4X_WRITEWORD, (uint8_t *)&payload, sizeof(payload));
|
||||
PacketResponseNG resp;
|
||||
if (!WaitForResponseTimeout(CMD_LF_EM4X_WRITEWORD, &resp, 2000)) {
|
||||
PrintAndLogEx(ERR, "Error occurred, device did not respond during write operation.");
|
||||
return PM3_ETIMEOUT;
|
||||
}
|
||||
|
||||
if (!downloadSamplesEM())
|
||||
return PM3_ENODATA;
|
||||
|
||||
//need 0 bits demoded (after preamble) to verify write cmd
|
||||
uint32_t dummy = 0;
|
||||
int status = demodEM4x05resp(&dummy);
|
||||
if (status == PM3_SUCCESS)
|
||||
PrintAndLogEx(SUCCESS, "Success writing to tag");
|
||||
|
||||
PrintAndLogEx(SUCCESS, "Done");
|
||||
PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf em 4x05_read`") " to verify");
|
||||
return status;
|
||||
}
|
||||
static int CmdEM4x05Wipe(const char *Cmd) {
|
||||
uint8_t addr = 0;
|
||||
uint32_t pwd = 0;
|
||||
uint8_t cmdp = 0;
|
||||
uint8_t chipType = 1; // em4305
|
||||
uint32_t chipInfo = 0x00040072; // Chip info/User Block normal 4305 Chip Type
|
||||
uint32_t chipUID = 0x614739AE; // UID normally readonly, but just in case
|
||||
uint32_t blockData = 0x00000000; // UserBlock/Password (set to 0x00000000 for a wiped card1
|
||||
uint32_t config = 0x0001805F; // Default config (no password)
|
||||
int success = PM3_SUCCESS;
|
||||
char cmdStr [100];
|
||||
char optchk[10];
|
||||
|
||||
while (param_getchar(Cmd, cmdp) != 0x00) {
|
||||
// check if cmd is a 1 byte option
|
||||
param_getstr(Cmd, cmdp, optchk, sizeof(optchk));
|
||||
if (strlen(optchk) == 1) { // Have a single character so option not part of password
|
||||
switch (tolower(param_getchar(Cmd, cmdp))) {
|
||||
case 'c': // chip type
|
||||
if (param_getchar(Cmd, cmdp) != 0x00)
|
||||
chipType = param_get8ex(Cmd, cmdp + 1, 0, 10);
|
||||
cmdp += 2;
|
||||
break;
|
||||
case 'h': // return usage_lf_em4x05_wipe();
|
||||
default : // Unknown or 'h' send help
|
||||
return usage_lf_em4x05_wipe();
|
||||
break;
|
||||
};
|
||||
} else { // Not a single character so assume password
|
||||
pwd = param_get32ex(Cmd, cmdp, 1, 16);
|
||||
cmdp++;
|
||||
}
|
||||
}
|
||||
|
||||
switch (chipType) {
|
||||
case 0 : // em4205
|
||||
chipInfo = 0x00040070;
|
||||
config = 0x0001805F;
|
||||
break;
|
||||
case 1 : // em4305
|
||||
chipInfo = 0x00040072;
|
||||
config = 0x0001805F;
|
||||
break;
|
||||
default : // Type 0/Default : EM4305
|
||||
chipInfo = 0x00040072;
|
||||
config = 0x0001805F;
|
||||
}
|
||||
|
||||
// block 0 : User Data or Chip Info
|
||||
sprintf(cmdStr, "%d %08X %08X", 0, chipInfo, pwd);
|
||||
CmdEM4x05Write(cmdStr);
|
||||
// block 1 : UID - this should be read only for EM4205 and EM4305 not sure about others
|
||||
sprintf(cmdStr, "%d %08X %08X", 1, chipUID, pwd);
|
||||
CmdEM4x05Write(cmdStr);
|
||||
// block 2 : password
|
||||
sprintf(cmdStr, "%d %08X %08X", 2, blockData, pwd);
|
||||
CmdEM4x05Write(cmdStr);
|
||||
pwd = blockData; // Password should now have changed, so use new password
|
||||
// block 3 : user data
|
||||
sprintf(cmdStr, "%d %08X %08X", 3, blockData, pwd);
|
||||
CmdEM4x05Write(cmdStr);
|
||||
// block 4 : config
|
||||
sprintf(cmdStr, "%d %08X %08X", 4, config, pwd);
|
||||
CmdEM4x05Write(cmdStr);
|
||||
|
||||
// Remainder of user/data blocks
|
||||
for (addr = 5; addr < 14; addr++) {// Clear user data blocks
|
||||
sprintf(cmdStr, "%d %08X %08X", addr, blockData, pwd);
|
||||
CmdEM4x05Write(cmdStr);
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
static void printEM4x05config(uint32_t wordData) {
|
||||
uint16_t datarate = (((wordData & 0x3F) + 1) * 2);
|
||||
uint8_t encoder = ((wordData >> 6) & 0xF);
|
||||
char enc[14];
|
||||
memset(enc, 0, sizeof(enc));
|
||||
|
||||
uint8_t PSKcf = (wordData >> 10) & 0x3;
|
||||
char cf[10];
|
||||
memset(cf, 0, sizeof(cf));
|
||||
uint8_t delay = (wordData >> 12) & 0x3;
|
||||
char cdelay[33];
|
||||
memset(cdelay, 0, sizeof(cdelay));
|
||||
uint8_t numblks = EM4x05_GET_NUM_BLOCKS(wordData);
|
||||
uint8_t LWR = numblks + 5 - 1; //last word read
|
||||
switch (encoder) {
|
||||
case 0:
|
||||
snprintf(enc, sizeof(enc), "NRZ");
|
||||
break;
|
||||
case 1:
|
||||
snprintf(enc, sizeof(enc), "Manchester");
|
||||
break;
|
||||
case 2:
|
||||
snprintf(enc, sizeof(enc), "Biphase");
|
||||
break;
|
||||
case 3:
|
||||
snprintf(enc, sizeof(enc), "Miller");
|
||||
break;
|
||||
case 4:
|
||||
snprintf(enc, sizeof(enc), "PSK1");
|
||||
break;
|
||||
case 5:
|
||||
snprintf(enc, sizeof(enc), "PSK2");
|
||||
break;
|
||||
case 6:
|
||||
snprintf(enc, sizeof(enc), "PSK3");
|
||||
break;
|
||||
case 7:
|
||||
snprintf(enc, sizeof(enc), "Unknown");
|
||||
break;
|
||||
case 8:
|
||||
snprintf(enc, sizeof(enc), "FSK1");
|
||||
break;
|
||||
case 9:
|
||||
snprintf(enc, sizeof(enc), "FSK2");
|
||||
break;
|
||||
default:
|
||||
snprintf(enc, sizeof(enc), "Unknown");
|
||||
break;
|
||||
}
|
||||
|
||||
switch (PSKcf) {
|
||||
case 0:
|
||||
snprintf(cf, sizeof(cf), "RF/2");
|
||||
break;
|
||||
case 1:
|
||||
snprintf(cf, sizeof(cf), "RF/8");
|
||||
break;
|
||||
case 2:
|
||||
snprintf(cf, sizeof(cf), "RF/4");
|
||||
break;
|
||||
case 3:
|
||||
snprintf(cf, sizeof(cf), "unknown");
|
||||
break;
|
||||
}
|
||||
|
||||
switch (delay) {
|
||||
case 0:
|
||||
snprintf(cdelay, sizeof(cdelay), "no delay");
|
||||
break;
|
||||
case 1:
|
||||
snprintf(cdelay, sizeof(cdelay), "BP/8 or 1/8th bit period delay");
|
||||
break;
|
||||
case 2:
|
||||
snprintf(cdelay, sizeof(cdelay), "BP/4 or 1/4th bit period delay");
|
||||
break;
|
||||
case 3:
|
||||
snprintf(cdelay, sizeof(cdelay), "no delay");
|
||||
break;
|
||||
}
|
||||
uint8_t readLogin = (wordData & EM4x05_READ_LOGIN_REQ) >> 18;
|
||||
uint8_t readHKL = (wordData & EM4x05_READ_HK_LOGIN_REQ) >> 19;
|
||||
uint8_t writeLogin = (wordData & EM4x05_WRITE_LOGIN_REQ) >> 20;
|
||||
uint8_t writeHKL = (wordData & EM4x05_WRITE_HK_LOGIN_REQ) >> 21;
|
||||
uint8_t raw = (wordData & EM4x05_READ_AFTER_WRITE) >> 22;
|
||||
uint8_t disable = (wordData & EM4x05_DISABLE_ALLOWED) >> 23;
|
||||
uint8_t rtf = (wordData & EM4x05_READER_TALK_FIRST) >> 24;
|
||||
uint8_t pigeon = (wordData & (1 << 26)) >> 26;
|
||||
PrintAndLogEx(INFO, "ConfigWord: %08X (Word 4)\n", wordData);
|
||||
PrintAndLogEx(INFO, "Config Breakdown:");
|
||||
PrintAndLogEx(INFO, " Data Rate: %02u | "_YELLOW_("RF/%u"), wordData & 0x3F, datarate);
|
||||
PrintAndLogEx(INFO, " Encoder: %u | " _YELLOW_("%s"), encoder, enc);
|
||||
PrintAndLogEx(INFO, " PSK CF: %u | %s", PSKcf, cf);
|
||||
PrintAndLogEx(INFO, " Delay: %u | %s", delay, cdelay);
|
||||
PrintAndLogEx(INFO, " LastWordR: %02u | Address of last word for default read - meaning %u blocks are output", LWR, numblks);
|
||||
PrintAndLogEx(INFO, " ReadLogin: %u | Read login is %s", readLogin, readLogin ? _YELLOW_("required") : _GREEN_("not required"));
|
||||
PrintAndLogEx(INFO, " ReadHKL: %u | Read housekeeping words login is %s", readHKL, readHKL ? _YELLOW_("required") : _GREEN_("not required"));
|
||||
PrintAndLogEx(INFO, "WriteLogin: %u | Write login is %s", writeLogin, writeLogin ? _YELLOW_("required") : _GREEN_("not required"));
|
||||
PrintAndLogEx(INFO, " WriteHKL: %u | Write housekeeping words login is %s", writeHKL, writeHKL ? _YELLOW_("required") : _GREEN_("not Required"));
|
||||
PrintAndLogEx(INFO, " R.A.W.: %u | Read after write is %s", raw, raw ? "on" : "off");
|
||||
PrintAndLogEx(INFO, " Disable: %u | Disable command is %s", disable, disable ? "accepted" : "not accepted");
|
||||
PrintAndLogEx(INFO, " R.T.F.: %u | Reader talk first is %s", rtf, rtf ? _YELLOW_("enabled") : "disabled");
|
||||
PrintAndLogEx(INFO, " Pigeon: %u | Pigeon mode is %s\n", pigeon, pigeon ? _YELLOW_("enabled") : "disabled");
|
||||
}
|
||||
|
||||
static void printEM4x05info(uint32_t block0, uint32_t serial) {
|
||||
|
||||
uint8_t chipType = (block0 >> 1) & 0xF;
|
||||
uint8_t cap = (block0 >> 5) & 3;
|
||||
uint16_t custCode = (block0 >> 9) & 0x3FF;
|
||||
|
||||
char ctstr[50];
|
||||
snprintf(ctstr, sizeof(ctstr), "\n Chip Type: %u | ", chipType);
|
||||
switch (chipType) {
|
||||
case 9:
|
||||
snprintf(ctstr + strlen(ctstr), sizeof(ctstr) - strlen(ctstr), _YELLOW_("%s"), "EM4305");
|
||||
break;
|
||||
case 8:
|
||||
snprintf(ctstr + strlen(ctstr), sizeof(ctstr) - strlen(ctstr), _YELLOW_("%s"), "EM4205");
|
||||
break;
|
||||
case 4:
|
||||
snprintf(ctstr + strlen(ctstr), sizeof(ctstr) - strlen(ctstr), _YELLOW_("%s"), "Unknown");
|
||||
break;
|
||||
case 2:
|
||||
snprintf(ctstr + strlen(ctstr), sizeof(ctstr) - strlen(ctstr), _YELLOW_("%s"), "EM4469");
|
||||
break;
|
||||
//add more here when known
|
||||
default:
|
||||
snprintf(ctstr + strlen(ctstr), sizeof(ctstr) - strlen(ctstr), _YELLOW_("%s"), "Unknown");
|
||||
break;
|
||||
}
|
||||
PrintAndLogEx(SUCCESS, "%s", ctstr);
|
||||
|
||||
switch (cap) {
|
||||
case 3:
|
||||
PrintAndLogEx(SUCCESS, " Cap Type: %u | 330pF", cap);
|
||||
break;
|
||||
case 2:
|
||||
PrintAndLogEx(SUCCESS, " Cap Type: %u | %spF", cap, (chipType == 2) ? "75" : "210");
|
||||
break;
|
||||
case 1:
|
||||
PrintAndLogEx(SUCCESS, " Cap Type: %u | 250pF", cap);
|
||||
break;
|
||||
case 0:
|
||||
PrintAndLogEx(SUCCESS, " Cap Type: %u | no resonant capacitor", cap);
|
||||
break;
|
||||
default:
|
||||
PrintAndLogEx(SUCCESS, " Cap Type: %u | unknown", cap);
|
||||
break;
|
||||
}
|
||||
|
||||
PrintAndLogEx(SUCCESS, " Cust Code: %03u | %s", custCode, (custCode == 0x200) ? "Default" : "Unknown");
|
||||
if (serial != 0)
|
||||
PrintAndLogEx(SUCCESS, "\n Serial #: " _YELLOW_("%08X"), serial);
|
||||
}
|
||||
|
||||
static void printEM4x05ProtectionBits(uint32_t word) {
|
||||
for (uint8_t i = 0; i < 15; i++) {
|
||||
PrintAndLogEx(INFO, " Word: %02u | %s", i, (((1 << i) & word) || i < 2) ? _RED_("write Locked") : "unlocked");
|
||||
if (i == 14)
|
||||
PrintAndLogEx(INFO, " Word: %02u | %s", i + 1, (((1 << i) & word) || i < 2) ? _RED_("write locked") : "unlocked");
|
||||
}
|
||||
}
|
||||
|
||||
//quick test for EM4x05/EM4x69 tag
|
||||
bool EM4x05IsBlock0(uint32_t *word) {
|
||||
return (EM4x05ReadWord_ext(0, 0, false, word) == PM3_SUCCESS);
|
||||
}
|
||||
|
||||
static int CmdEM4x05Info(const char *Cmd) {
|
||||
#define EM_SERIAL_BLOCK 1
|
||||
#define EM_CONFIG_BLOCK 4
|
||||
#define EM_PROT1_BLOCK 14
|
||||
#define EM_PROT2_BLOCK 15
|
||||
uint32_t pwd;
|
||||
uint32_t word = 0, block0 = 0, serial = 0;
|
||||
bool usePwd = false;
|
||||
uint8_t ctmp = tolower(param_getchar(Cmd, 0));
|
||||
if (ctmp == 'h') return usage_lf_em4x05_info();
|
||||
|
||||
// for now use default input of 1 as invalid (unlikely 1 will be a valid password...)
|
||||
pwd = param_get32ex(Cmd, 0, 0xFFFFFFFF, 16);
|
||||
|
||||
if (pwd != 0xFFFFFFFF)
|
||||
usePwd = true;
|
||||
|
||||
// read word 0 (chip info)
|
||||
// block 0 can be read even without a password.
|
||||
if (EM4x05IsBlock0(&block0) == false)
|
||||
return PM3_ESOFT;
|
||||
|
||||
// read word 1 (serial #) doesn't need pwd
|
||||
// continue if failed, .. non blocking fail.
|
||||
EM4x05ReadWord_ext(EM_SERIAL_BLOCK, 0, false, &serial);
|
||||
printEM4x05info(block0, serial);
|
||||
|
||||
// read word 4 (config block)
|
||||
// needs password if one is set
|
||||
if (EM4x05ReadWord_ext(EM_CONFIG_BLOCK, pwd, usePwd, &word) != PM3_SUCCESS)
|
||||
return PM3_ESOFT;
|
||||
|
||||
printEM4x05config(word);
|
||||
|
||||
// read word 14 and 15 to see which is being used for the protection bits
|
||||
if (EM4x05ReadWord_ext(EM_PROT1_BLOCK, pwd, usePwd, &word) != PM3_SUCCESS) {
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
// if status bit says this is not the used protection word
|
||||
if (!(word & 0x8000)) {
|
||||
if (EM4x05ReadWord_ext(EM_PROT2_BLOCK, pwd, usePwd, &word) != PM3_SUCCESS)
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
|
||||
//something went wrong
|
||||
if (!(word & 0x8000))
|
||||
return PM3_ESOFT;
|
||||
|
||||
printEM4x05ProtectionBits(word);
|
||||
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
static command_t CommandTable[] = {
|
||||
{"help", CmdHelp, AlwaysAvailable, "This help"},
|
||||
{"----------", CmdHelp, AlwaysAvailable, "----------------------- " _CYAN_("EM 410x") " -----------------------"},
|
||||
@@ -1409,12 +638,16 @@ static command_t CommandTable[] = {
|
||||
{"410x_spoof", CmdEM410xWatchnSpoof, IfPm3Lf, "watches for EM410x 125/134 kHz tags, and replays them. (option 'h' for 134)" },
|
||||
{"410x_clone", CmdEM410xClone, IfPm3Lf, "write EM410x UID to T55x7 or Q5/T5555 tag"},
|
||||
{"----------", CmdHelp, AlwaysAvailable, "-------------------- " _CYAN_("EM 4x05 / 4x69") " -------------------"},
|
||||
{"4x05_chk", CmdEM4x05Chk, IfPm3Lf, "Check passwords from dictionary"},
|
||||
{"4x05_demod", CmdEM4x05Demod, AlwaysAvailable, "demodulate a EM4x05/EM4x69 tag from the GraphBuffer"},
|
||||
{"4x05_dump", CmdEM4x05Dump, IfPm3Lf, "dump EM4x05/EM4x69 tag"},
|
||||
{"4x05_wipe", CmdEM4x05Wipe, IfPm3Lf, "wipe EM4x05/EM4x69 tag"},
|
||||
{"4x05_info", CmdEM4x05Info, IfPm3Lf, "tag information EM4x05/EM4x69"},
|
||||
{"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, AlwaysAvailable, "Attempt to recover em4x05 commands from sample buffer"},
|
||||
{"4x05_brute", CmdEM4x05Brute, IfPm3Lf, "Bruteforce password"},
|
||||
{"----------", CmdHelp, AlwaysAvailable, "----------------------- " _CYAN_("EM 4x50") " -----------------------"},
|
||||
{"4x50_dump", CmdEM4x50Dump, IfPm3EM4x50, "dump EM4x50 tag"},
|
||||
{"4x50_info", CmdEM4x50Info, IfPm3EM4x50, "tag information EM4x50"},
|
||||
|
||||
Reference in New Issue
Block a user