Merge pull request #1996 from jerji/master

Add ability to simulate paradox fobs from fc and cn
This commit is contained in:
Iceman 2023-06-04 21:59:28 +02:00 committed by GitHub
commit 005fddfe6d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 143 additions and 94 deletions

View file

@ -70,7 +70,9 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac
- Updated documentation for installation on macOS with MacPorts (@linuxgemini) - Updated documentation for installation on macOS with MacPorts (@linuxgemini)
- Added possible Paxton id to hitag2 tag info output - Added possible Paxton id to hitag2 tag info output
- Changed `hf mf sim` - reduce 50ms threshold to 6ms for reset to idle #1974 (@net147) - Changed `hf mf sim` - reduce 50ms threshold to 6ms for reset to idle #1974 (@net147)
- Updated `amiibo_tools.lua` with new identifiers and create a python script `update_amiibo_tools_lua.py` to automate the process in the future. (@CorySolovewicz) - Update `amiibo_tools.lua` with new identifiers and create a python script `update_amiibo_tools_lua.py` to automate the process in the future. (@CorySolovewicz)
- Added `lf paradox sim --fc --cn` - Simulates Paradox fob from facility code and card number (jerji)
## [Nitride.4.16191][2023-01-29] ## [Nitride.4.16191][2023-01-29]
- Changed `build_all_firmwares.sh` to fit GENERIC 256kb firmware images (@doegox) - Changed `build_all_firmwares.sh` to fit GENERIC 256kb firmware images (@doegox)

View file

@ -1680,7 +1680,7 @@ int CmdLFfind(const char *Cmd) {
goto out; goto out;
} }
} }
if (demodParadox(true) == PM3_SUCCESS) { if (demodParadox(true, false) == PM3_SUCCESS) {
PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("Paradox ID") " found!"); PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("Paradox ID") " found!");
if (search_cont) { if (search_cont) {
found++; found++;

View file

@ -20,7 +20,6 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include <ctype.h>
#include "commonutil.h" // ARRAYLEN #include "commonutil.h" // ARRAYLEN
#include "cmdparser.h" // command_t #include "cmdparser.h" // command_t
#include "comms.h" #include "comms.h"
@ -53,7 +52,55 @@ static const uint8_t paradox_lut[] = {
// Paradox Prox demod - FSK2a RF/50 with preamble of 00001111 (then manchester encoded) // Paradox Prox demod - FSK2a RF/50 with preamble of 00001111 (then manchester encoded)
// print full Paradox Prox ID and some bit format details if found // print full Paradox Prox ID and some bit format details if found
int demodParadox(bool verbose) { // This function will calculate the bitstream for a paradox card and place the result in bs.
// It returns the calculated CRC from the fc and cn.
// CRC calculation by mwalker33
static uint8_t GetParadoxBits(const uint32_t fc, const uint32_t cn, unsigned int *bs) {
uint8_t manchester[13] = { 0x00 }; // check size needed
uint32_t t1;
manchester[0] = 0x0F; // preamble
manchester[1] = 0x05; // Leading zeros - Note: from this byte on, is part of the CRC calculation
manchester[2] = 0x55; // Leading zeros its 4 bits out for the CRC, so we need to move
manchester[3] = 0x55; // Leading zeros back 4 bits once we have the crc (done below)
// add FC
t1 = manchesterEncode2Bytes(fc);
manchester[4] = (t1 >> 8) & 0xFF;
manchester[5] = t1 & 0xFF;
// add cn
t1 = manchesterEncode2Bytes(cn);
manchester[6] = (t1 >> 24) & 0xFF;
manchester[7] = (t1 >> 16) & 0xFF;
manchester[8] = (t1 >> 8) & 0xFF;
manchester[9] = t1 & 0xFF;
uint8_t crc = (CRC8Maxim(manchester + 1, 9) ^ 0x6) & 0xFF;
// add crc
t1 = manchesterEncode2Bytes(crc);
manchester[10] = (t1 >> 8) & 0xFF;
manchester[11] = t1 & 0xFF;
// move left 4 bits left 4 bits - Now that we have the CRC we need to re-align the data.
for (int i = 1; i < 12; i++)
manchester[i] = (manchester[i] << 4) + (manchester[i + 1] >> 4);
// Add trailing 1010 (11)
manchester[11] |= (1 << 3);
manchester[11] |= (1 << 1);
// move into tag blocks
for (int i = 0; i < 12; i++)
bs[1 + (i / 4)] += (manchester[i] << (8 * (3 - i % 4)));
return crc;
}
int demodParadox(bool verbose, bool oldChksum) {
(void) verbose; // unused so far (void) verbose; // unused so far
//raw fsk demod no manchester decoding no start bit finding just get binary from wave //raw fsk demod no manchester decoding no start bit finding just get binary from wave
uint8_t bits[MAX_GRAPH_TRACE_LEN] = {0}; uint8_t bits[MAX_GRAPH_TRACE_LEN] = {0};
@ -134,34 +181,39 @@ int demodParadox(bool verbose) {
uint32_t fc = ((hi & 0x3) << 6) | (lo >> 26); uint32_t fc = ((hi & 0x3) << 6) | (lo >> 26);
uint32_t cardnum = (lo >> 10) & 0xFFFF; uint32_t cardnum = (lo >> 10) & 0xFFFF;
uint8_t chksum = (lo >> 2) & 0xFF; uint8_t chksum = (lo >> 2) & 0xFF;
if (oldChksum) {
// Calc CRC & Checksum
// 000088f0b - FC: 8 - Card: 36619 - Checksum: 05 - RAW: 0f55555559595aa559a5566a
// checksum?
uint8_t calc_chksum = 0x47;
uint8_t pos = 0;
for (uint8_t i = 0; i < 8; i++) {
uint8_t ice = rawhex[i + 1];
for (uint8_t j = 0x80; j > 0; j >>= 2) {
// Calc CRC & Checksum if (ice & j) {
// 000088f0b - FC: 8 - Card: 36619 - Checksum: 05 - RAW: 0f55555559595aa559a5566a calc_chksum ^= paradox_lut[pos];
// checksum? }
uint8_t calc_chksum = 0x47; pos++;
uint8_t pos = 0;
for (uint8_t i = 0; i < 8; i++) {
uint8_t ice = rawhex[i + 1];
for (uint8_t j = 0x80; j > 0; j >>= 2) {
if (ice & j) {
calc_chksum ^= paradox_lut[pos];
} }
pos++;
} }
uint32_t crc = CRC8Maxim(rawhex + 1, 8);
PrintAndLogEx(INFO, " FSK/MAN raw : %s", sprint_hex(rawhex, sizeof(rawhex)));
PrintAndLogEx(INFO, " raw : %s = (maxim crc8) %02x == %02x", sprint_hex(rawhex + 1, 8), crc,
calc_chksum);
// PrintAndLogEx(DEBUG, " OTHER sample CRC-8/MAXIM : 55 55 69 A5 55 6A 59 5A = FC");
} }
uint32_t crc = CRC8Maxim(rawhex + 1, 8);
PrintAndLogEx(DEBUG, " FSK/MAN raw : %s", sprint_hex(rawhex, sizeof(rawhex)));
PrintAndLogEx(DEBUG, " raw : %s = (maxim crc8) %02x == %02x", sprint_hex(rawhex + 1, 8), crc, calc_chksum);
// PrintAndLogEx(DEBUG, " OTHER sample CRC-8/MAXIM : 55 55 69 A5 55 6A 59 5A = FC");
uint32_t rawLo = bytebits_to_byte(bits + idx + 64, 32); uint32_t rawLo = bytebits_to_byte(bits + idx + 64, 32);
uint32_t rawHi = bytebits_to_byte(bits + idx + 32, 32); uint32_t rawHi = bytebits_to_byte(bits + idx + 32, 32);
uint32_t rawHi2 = bytebits_to_byte(bits + idx, 32); uint32_t rawHi2 = bytebits_to_byte(bits + idx, 32);
uint32_t blocks[4] = {0};
uint8_t crc = GetParadoxBits(fc, cardnum, blocks);
if (chksum != crc)
PrintAndLogEx(ERR, "CRC Error! Calculated CRC is " _GREEN_("%d") " but card CRC is " _RED_("%d") ".", crc, chksum);
PrintAndLogEx(INFO, "Paradox - ID: " _GREEN_("%x%08x") " FC: " _GREEN_("%d") " Card: " _GREEN_("%d") ", Checksum: %02x, Raw: %08x%08x%08x", PrintAndLogEx(INFO, "Paradox - ID: " _GREEN_("%x%08x") " FC: " _GREEN_("%d") " Card: " _GREEN_("%d") ", Checksum: %02x, Raw: %08x%08x%08x",
hi >> 10, hi >> 10,
(hi & 0x3) << 26 | (lo >> 10), (hi & 0x3) << 26 | (lo >> 10),
@ -185,32 +237,37 @@ static int CmdParadoxDemod(const char *Cmd) {
CLIParserContext *ctx; CLIParserContext *ctx;
CLIParserInit(&ctx, "lf paradox demod", CLIParserInit(&ctx, "lf paradox demod",
"Try to find Paradox preamble, if found decode / descramble data", "Try to find Paradox preamble, if found decode / descramble data",
"lf paradox demod" "lf paradox demod --old -> Display previous checksum version"
); );
void *argtable[] = { void *argtable[] = {
arg_param_begin, arg_param_begin,
arg_lit0(NULL, "old", "optional - Display previous checksum version"),
arg_param_end arg_param_end
}; };
CLIExecWithReturn(ctx, Cmd, argtable, true); CLIExecWithReturn(ctx, Cmd, argtable, true);
bool old = arg_get_lit(ctx, 1);
CLIParserFree(ctx); CLIParserFree(ctx);
return demodParadox(true); return demodParadox(true, old);
} }
static int CmdParadoxReader(const char *Cmd) { static int CmdParadoxReader(const char *Cmd) {
CLIParserContext *ctx; CLIParserContext *ctx;
CLIParserInit(&ctx, "lf paradox reader", CLIParserInit(&ctx, "lf paradox reader",
"read a Paradox tag", "read a Paradox tag",
"lf Paradox reader -@ -> continuous reader mode" "lf paradox reader -@ -> continuous reader mode\n"
"lf paradox reader --old -> Display previous checksum version"
); );
void *argtable[] = { void *argtable[] = {
arg_param_begin, arg_param_begin,
arg_lit0("@", NULL, "optional - continuous reader mode"), arg_lit0("@", NULL, "optional - continuous reader mode"),
arg_lit0(NULL, "old", "optional - Display previous checksum version"),
arg_param_end arg_param_end
}; };
CLIExecWithReturn(ctx, Cmd, argtable, true); CLIExecWithReturn(ctx, Cmd, argtable, true);
bool cm = arg_get_lit(ctx, 1); bool cm = arg_get_lit(ctx, 1);
bool old = arg_get_lit(ctx, 2);
CLIParserFree(ctx); CLIParserFree(ctx);
if (cm) { if (cm) {
@ -219,7 +276,7 @@ static int CmdParadoxReader(const char *Cmd) {
do { do {
lf_read(false, 10000); lf_read(false, 10000);
demodParadox(!cm); demodParadox(!cm, old);
} while (cm && !kbd_enter_pressed()); } while (cm && !kbd_enter_pressed());
return PM3_SUCCESS; return PM3_SUCCESS;
@ -230,7 +287,7 @@ static int CmdParadoxClone(const char *Cmd) {
CLIParserContext *ctx; CLIParserContext *ctx;
CLIParserInit(&ctx, "lf paradox clone", CLIParserInit(&ctx, "lf paradox clone",
"clone a paradox tag to a T55x7, Q5/T5555 or EM4305/4469 tag.", "clone a paradox tag to a T55x7, Q5/T5555 or EM4305/4469 tag.",
"lf paradox clone --fc 96 --cn 40426 -> encode for T55x7 tag with fc and cn\n" "lf paradox clone --fc 96 --cn 40426 -> encode for T55x7 tag with fc and cn\n"
"lf paradox clone --raw 0f55555695596a6a9999a59a -> encode for T55x7 tag\n" "lf paradox clone --raw 0f55555695596a6a9999a59a -> encode for T55x7 tag\n"
"lf paradox clone --raw 0f55555695596a6a9999a59a --q5 -> encode for Q5/T5555 tag\n" "lf paradox clone --raw 0f55555695596a6a9999a59a --q5 -> encode for Q5/T5555 tag\n"
"lf paradox clone --raw 0f55555695596a6a9999a59a --em -> encode for EM4305/4469" "lf paradox clone --raw 0f55555695596a6a9999a59a --em -> encode for EM4305/4469"
@ -263,6 +320,16 @@ static int CmdParadoxClone(const char *Cmd) {
return PM3_EINVARG; return PM3_EINVARG;
} }
if ((fc || cn) && raw_len != 0) {
PrintAndLogEx(FAILED, "Can't specify both FC/CN and RAW at the same time");
return PM3_EINVARG;
}
if (fc > 999 || cn > 99999) {
PrintAndLogEx(FAILED, "FC has a max value of 999 and CN has a max value of 99999");
return PM3_EINVARG;
}
uint32_t blocks[4] = {0}; uint32_t blocks[4] = {0};
if (raw_len != 0) { if (raw_len != 0) {
@ -275,44 +342,8 @@ static int CmdParadoxClone(const char *Cmd) {
blocks[i] = bytes_to_num(raw + ((i - 1) * 4), sizeof(uint32_t)); blocks[i] = bytes_to_num(raw + ((i - 1) * 4), sizeof(uint32_t));
} }
} else { } else {
uint8_t manchester[13] = { 0x00 }; // check size needed //This function generates the bitstream and puts it in blocks. it returns the crc, but we don't need it here
uint32_t t1; GetParadoxBits(fc, cn, blocks);
manchester[0] = 0x0F; // preamble
manchester[1] = 0x05; // Leading zeros - Note: from this byte on, is part of the CRC calculation
manchester[2] = 0x55; // Leading zeros its 4 bits out for the CRC, so we need too move
manchester[3] = 0x55; // Leading zeros back 4 bits once we have the crc (done below)
// add FC
t1 = manchesterEncode2Bytes(fc);
manchester[4] = (t1 >> 8) & 0xFF;
manchester[5] = t1 & 0xFF;
// add cn
t1 = manchesterEncode2Bytes(cn);
manchester[6] = (t1 >> 24) & 0xFF;
manchester[7] = (t1 >> 16) & 0xFF;
manchester[8] = (t1 >> 8) & 0xFF;
manchester[9] = t1 & 0xFF;
uint8_t crc = (CRC8Maxim(manchester + 1, 9) ^ 0x6) & 0xFF;
// add crc
t1 = manchesterEncode2Bytes(crc);
manchester[10] = (t1 >> 8) & 0xFF;
manchester[11] = t1 & 0xFF;
// move left 4 bits left 4 bits - Now that we have the CRC we need to re-align the data.
for (int i = 1; i < 12; i++)
manchester[i] = (manchester[i] << 4) + (manchester[i + 1] >> 4);
// Add trailing 1010 (11)
manchester[11] |= (1 << 3);
manchester[11] |= (1 << 1);
// move into tag blocks
for (int i = 0; i < 12; i++)
blocks[1 + (i / 4)] += (manchester[i] << (8 * (3 - i % 4)));
} }
// Paradox - FSK2a, data rate 50, 3 data blocks // Paradox - FSK2a, data rate 50, 3 data blocks
@ -355,12 +386,15 @@ static int CmdParadoxSim(const char *Cmd) {
CLIParserInit(&ctx, "lf paradox sim", CLIParserInit(&ctx, "lf paradox sim",
"Enables simulation of paradox card with specified card number.\n" "Enables simulation of paradox card with specified card number.\n"
"Simulation runs until the button is pressed or another USB command is issued.", "Simulation runs until the button is pressed or another USB command is issued.",
"lf paradox sim --raw 0f55555695596a6a9999a59a" "lf paradox sim --raw 0f55555695596a6a9999a59a -> simulate tag\n"
"lf paradox sim --fc 96 --cn 40426 -> simulate tag with fc and cn\n"
); );
void *argtable[] = { void *argtable[] = {
arg_param_begin, arg_param_begin,
arg_str0("r", "raw", "<hex>", " raw hex data. 12 bytes"), arg_str0("r", "raw", "<hex>", "raw hex data. 12 bytes"),
arg_u64_0(NULL, "fc", "<dec>", "facility code"),
arg_u64_0(NULL, "cn", "<dec>", "card number"),
arg_param_end arg_param_end
}; };
CLIExecWithReturn(ctx, Cmd, argtable, false); CLIExecWithReturn(ctx, Cmd, argtable, false);
@ -369,13 +403,32 @@ static int CmdParadoxSim(const char *Cmd) {
// skip first block, 3*4 = 12 bytes left // skip first block, 3*4 = 12 bytes left
uint8_t raw[12] = {0}; uint8_t raw[12] = {0};
CLIGetHexWithReturn(ctx, 1, raw, &raw_len); CLIGetHexWithReturn(ctx, 1, raw, &raw_len);
uint32_t fc = arg_get_u32_def(ctx, 2, 0);
uint32_t cn = arg_get_u32_def(ctx, 3, 0);
CLIParserFree(ctx); CLIParserFree(ctx);
if (raw_len != 12) { if ((fc || cn) && raw_len != 0) {
PrintAndLogEx(ERR, "Data must be 12 bytes (24 HEX characters) %d", raw_len); PrintAndLogEx(FAILED, "Can't specify both FC/CN and RAW at the same time");
return PM3_EINVARG; return PM3_EINVARG;
} }
if (fc > 999 || cn > 99999) {
PrintAndLogEx(FAILED, "FC has a max value of 999 and CN has a max value of 99999");
return PM3_EINVARG;
}
if (raw_len != 0) {
if (raw_len != 12) {
PrintAndLogEx(ERR, "Data must be 12 bytes (24 HEX characters) %d", raw_len);
return PM3_EINVARG;
}
} else {
uint32_t blocks[4] = {0};
GetParadoxBits(fc, cn, blocks);
for (uint8_t i = 1; i < ARRAYLEN(blocks); i++) {
num_to_bytes(blocks[i], sizeof(uint32_t), raw + ((i - 1) * 4));
}
}
PrintAndLogEx(SUCCESS, "Simulating Paradox - raw " _YELLOW_("%s"), sprint_hex_inrow(raw, sizeof(raw))); PrintAndLogEx(SUCCESS, "Simulating Paradox - raw " _YELLOW_("%s"), sprint_hex_inrow(raw, sizeof(raw)));
uint8_t bs[sizeof(raw) * 8]; uint8_t bs[sizeof(raw) * 8];
@ -404,21 +457,8 @@ static int CmdParadoxSim(const char *Cmd) {
return PM3_SUCCESS; return PM3_SUCCESS;
} }
/*
if (sscanf(Cmd, "%u %u", &fc, &cn) != 2) return usage_lf_paradox_sim();
facilitycode = (fc & 0x000000FF);
cardnumber = (cn & 0x0000FFFF);
// if ( GetParadoxBits(facilitycode, cardnumber, bs) != PM3_SUCCESS) {
// PrintAndLogEx(ERR, "Error with tag bitstream generation.");
// return 1;
// }
PrintAndLogEx(NORMAL, "Simulating Paradox - Facility Code: %u, CardNumber: %u", facilitycode, cardnumber);
*/
static command_t CommandTable[] = { static command_t CommandTable[] = {
{"help", CmdHelp, AlwaysAvailable, "This help"}, {"help", CmdHelp, AlwaysAvailable, "This help"},
{"demod", CmdParadoxDemod, AlwaysAvailable, "demodulate a Paradox FSK tag from the GraphBuffer"}, {"demod", CmdParadoxDemod, AlwaysAvailable, "demodulate a Paradox FSK tag from the GraphBuffer"},

View file

@ -22,6 +22,6 @@
int CmdLFParadox(const char *Cmd); int CmdLFParadox(const char *Cmd);
int demodParadox(bool verbose); int demodParadox(bool verbose, bool oldChksum);
int detectParadox(uint8_t *dest, size_t *size, int *wave_start_idx); int detectParadox(uint8_t *dest, size_t *size, int *wave_start_idx);
#endif #endif

View file

@ -9658,13 +9658,14 @@
"command": "lf paradox demod", "command": "lf paradox demod",
"description": "Try to find Paradox preamble, if found decode / descramble data", "description": "Try to find Paradox preamble, if found decode / descramble data",
"notes": [ "notes": [
"lf paradox demod" "lf paradox demod --old -> Display previous checksum version"
], ],
"offline": true, "offline": true,
"options": [ "options": [
"-h, --help This help" "-h, --help This help",
"--old optional - Display previous checksum version"
], ],
"usage": "lf paradox demod [-h]" "usage": "lf paradox demod [-h] [--old]"
}, },
"lf paradox help": { "lf paradox help": {
"command": "lf paradox help", "command": "lf paradox help",
@ -9678,27 +9679,32 @@
"command": "lf paradox reader", "command": "lf paradox reader",
"description": "read a Paradox tag", "description": "read a Paradox tag",
"notes": [ "notes": [
"lf Paradox reader -@ -> continuous reader mode" "lf paradox reader -@ -> continuous reader mode",
"lf paradox reader --old -> Display previous checksum version"
], ],
"offline": false, "offline": false,
"options": [ "options": [
"-h, --help This help", "-h, --help This help",
"-@ optional - continuous reader mode" "-@ optional - continuous reader mode",
"--old optional - Display previous checksum version"
], ],
"usage": "lf paradox reader [-h@]" "usage": "lf paradox reader [-h@] [--old]"
}, },
"lf paradox sim": { "lf paradox sim": {
"command": "lf paradox sim", "command": "lf paradox sim",
"description": "Enables simulation of paradox card with specified card number. Simulation runs until the button is pressed or another USB command is issued.", "description": "Enables simulation of paradox card with specified card number. Simulation runs until the button is pressed or another USB command is issued.",
"notes": [ "notes": [
"lf paradox sim --raw 0f55555695596a6a9999a59a" "lf paradox sim --raw 0f55555695596a6a9999a59a -> simulate tag",
"lf paradox sim --fc 96 --cn 40426 -> simulate tag with fc and cn"
], ],
"offline": false, "offline": false,
"options": [ "options": [
"-h, --help This help", "-h, --help This help",
"-r, --raw <hex> raw hex data. 12 bytes" "-r, --raw <hex> raw hex data. 12 bytes",
"--fc <dec> facility code",
"--cn <dec> card number"
], ],
"usage": "lf paradox sim [-h] [-r <hex>]" "usage": "lf paradox sim [-h] [-r <hex>] [--fc <dec>] [--cn <dec>]"
}, },
"lf pcf7931 config": { "lf pcf7931 config": {
"command": "lf pcf7931 config", "command": "lf pcf7931 config",
@ -12018,6 +12024,7 @@
"metadata": { "metadata": {
"commands_extracted": 755, "commands_extracted": 755,
"extracted_by": "PM3Help2JSON v1.00", "extracted_by": "PM3Help2JSON v1.00",
"extracted_on": "2023-06-02T08:44:26" "extracted_on": "2023-06-04T15:36:56"
} }
} }