proxmark3/armsrc/Standalone/lf_nexid.c

344 lines
9.7 KiB
C
Raw Normal View History

2021-06-23 16:00:56 +08:00
//-----------------------------------------------------------------------------
// BOSCA Maxime, RIOUX Guilhem 2021
//
// 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.
//-----------------------------------------------------------------------------
// main code for Nexwatch ID / Magic number collector.
//-----------------------------------------------------------------------------
#include <inttypes.h>
#include "standalone.h" // standalone definitions
#include "proxmark3_arm.h"
#include "appmain.h"
#include "lfops.h"
#include "lfsampling.h"
#include "BigBuf.h"
#include "fpgaloader.h"
#include "util.h"
#include "dbprint.h"
#include "printf.h"
#include "spiffs.h"
#include "ticks.h"
#include "lfdemod.h"
#include "commonutil.h"
/*
* `lf_nexid` sniffs after LF Nexwatch ID credentials, and stores them in internal
* flash. It requires RDV4 hardware (for flash and battery).
*
* On entering stand-alone mode, this module will start reading/record LF Nexwatch ID credentials.
* Every found / collected credential will be written/appended to the logfile in flash
* as a text string.
*
* LEDs:
* - LED A: reading / record
* - LED B: writing to flash
* - LED C: unmounting/sync'ing flash (normally < 100ms)
*
* To retrieve log file from flash:
*
* 1. mem spiffs dump -s lf_nexcollect.log -d lf_nexcollect.log
* Copies log file from flash to your client.
*
* 2. exit the Proxmark3 client
*
* 3. more lf_nexcollect.log
*
* This module emits debug strings during normal operation -- so try it out in
* the lab connected to PM3 client before taking it into the field.
*
* To delete the log file from flash:
*
* 1. mem spiffs remove -f lf_nexcollect.log
*/
#define LF_NEXCOLLECT_LOGFILE "lf_nexcollect.log"
typedef enum {
SCRAMBLE,
DESCRAMBLE
} NexWatchScramble_t;
static void DownloadLogInstructions(void) {
Dbprintf("");
Dbprintf("[=] To get the logfile from flash and display it:");
Dbprintf("[=] " _YELLOW_("1.") " mem spiffs dump -s "LF_NEXCOLLECT_LOGFILE" -d "LF_NEXCOLLECT_LOGFILE);
Dbprintf("[=] " _YELLOW_("2.") " exit proxmark3 client");
Dbprintf("[=] " _YELLOW_("3.") " cat "LF_NEXCOLLECT_LOGFILE);
}
bool log_exists;
// scramble parity (1234) -> (4231)
static uint8_t nexwatch_parity_swap(uint8_t parity) {
uint8_t a = (((parity >> 3) & 1));
a |= (((parity >> 1) & 1) << 1);
a |= (((parity >> 2) & 1) << 2);
a |= ((parity & 1) << 3);
return a;
}
// parity check
// from 32b hex id, 4b mode,
static uint8_t nexwatch_parity(uint8_t hexid[5]) {
uint8_t p = 0;
for (uint8_t i = 0; i < 5; i++) {
p ^= NIBBLE_HIGH(hexid[i]);
p ^= NIBBLE_LOW(hexid[i]);
}
return nexwatch_parity_swap(p);
}
/// NETWATCH checksum
/// @param magic = 0xBE Quadrakey, 0x88 Nexkey
/// @param id = descrambled id (printed card number)
/// @param parity = the parity based upon the scrambled raw id.
static uint8_t nexwatch_checksum(uint8_t magic, uint32_t id, uint8_t parity) {
uint8_t a = ((id >> 24) & 0xFF);
a -= ((id >> 16) & 0xFF);
a -= ((id >> 8) & 0xFF);
a -= (id & 0xFF);
a -= magic;
a -= (reflect8(parity) >> 4);
return reflect8(a);
}
// Scrambled id ( 88 bit cardnumber format)
// ref:: http://www.proxmark.org/forum/viewtopic.php?pid=14662#p14662
static int nexwatch_scamble(NexWatchScramble_t action, uint32_t *id, uint32_t *scambled) {
// 255 = Not used/Unknown other values are the bit offset in the ID/FC values
uint8_t hex_2_id [] = {
31, 27, 23, 19, 15, 11, 7, 3,
30, 26, 22, 18, 14, 10, 6, 2,
29, 25, 21, 17, 13, 9, 5, 1,
28, 24, 20, 16, 12, 8, 4, 0
};
switch (action) {
case DESCRAMBLE: {
*id = 0;
for (uint8_t idx = 0; idx < 32; idx++) {
if (hex_2_id[idx] == 255)
continue;
bool bit_state = (*scambled >> hex_2_id[idx]) & 1;
*id |= (bit_state << (31 - idx));
}
break;
}
case SCRAMBLE: {
*scambled = 0;
for (uint8_t idx = 0; idx < 32; idx++) {
if (hex_2_id[idx] == 255)
continue;
bool bit_state = (*id >> idx) & 1;
*scambled |= (bit_state << (31 - hex_2_id[idx]));
}
break;
}
default:
break;
}
return PM3_SUCCESS;
}
static void append(uint8_t *entry, size_t entry_len) {
LED_B_ON();
if (log_exists == false) {
rdv40_spiffs_write(LF_NEXCOLLECT_LOGFILE, entry, entry_len, RDV40_SPIFFS_SAFETY_SAFE);
log_exists = true;
} else {
rdv40_spiffs_append(LF_NEXCOLLECT_LOGFILE, entry, entry_len, RDV40_SPIFFS_SAFETY_SAFE);
}
LED_B_OFF();
}
static int detectNexWatch(uint8_t *dest, size_t *size, bool *invert) {
uint8_t preamble[28] = {0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
// sanity check.
if (*size < 96) return -1;
size_t startIdx = 0;
if (!preambleSearch(dest, preamble, sizeof(preamble), size, &startIdx)) {
// if didn't find preamble try again inverting
uint8_t preamble_i[28] = {1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
if (!preambleSearch(dest, preamble_i, sizeof(preamble_i), size, &startIdx)) return -4;
*invert ^= 1;
}
// size tests?
return (int) startIdx;
}
static uint32_t PSKDemod(uint8_t *dest, size_t *size, int *startIdx) {
//buffer for result
int clk = 0, invert = 0;
//checks if the signal is just noise
if (getSignalProperties()->isnoise) {
return PM3_ESOFT;
}
//int pskRawDemod_ext(uint8_t *dest, size_t *size, int *clock, int *invert, int *startIdx)
int errCnt = pskRawDemod_ext(dest, size, &clk, &invert, startIdx);
if (errCnt > 100) {
BigBuf_free();
return PM3_ESOFT;
}
return PM3_SUCCESS;
}
static int demodNexWatch(void) {
uint8_t *dest = BigBuf_get_addr();
size_t size = MIN(16385, BigBuf_max_traceLen());
int startIdx = 0;
if (PSKDemod(dest, &size, &startIdx) != PM3_SUCCESS) {
return PM3_ESOFT;
}
bool invert = false;
int idx = detectNexWatch(dest, &size, &invert);
if (idx < 0) {
return PM3_ESOFT;
}
// skip the 4 first bits from the nexwatch preamble identification (we use 4 extra zeros..)
idx += 4;
// size = size -idx;
dest = dest + idx;
Dbprintf("[+] Id: %d, Size: %d", idx, size);
//setClockGrid(g_DemodClock, g_DemodStartIdx + (idx * g_DemodClock));
if (invert) {
Dbprintf("Inverted the demodulated data");
for (size_t i = 0; i < size; i++)
dest[i] ^= 1;
}
//got a good demod
uint32_t raw1 = bytebits_to_byte(dest, 32);
uint32_t raw2 = bytebits_to_byte(dest + 32, 32);
uint32_t raw3 = bytebits_to_byte(dest + 32 + 32, 32);
// get rawid
uint32_t rawid = 0;
for (uint8_t k = 0; k < 4; k++) {
for (uint8_t m = 0; m < 8; m++) {
rawid = (rawid << 1) | dest[m + k + (m * 4)];
}
}
// descrambled id
uint32_t cn = 0;
uint32_t scambled = bytebits_to_byte(dest + 8 + 32, 32);
nexwatch_scamble(DESCRAMBLE, &cn, &scambled);
uint8_t mode = bytebits_to_byte(dest + 72, 4);
uint8_t chk = bytebits_to_byte(dest + 80, 8);
// parity check
// from 32b hex id, 4b mode
uint8_t hex[5] = {0};
for (uint8_t i = 0; i < 5; i++) {
hex[i] = bytebits_to_byte(dest + 8 + 32 + (i * 8), 8);
}
// mode is only 4 bits.
hex[4] &= 0xf0;
uint8_t calc_parity = nexwatch_parity(hex);
uint8_t magic = 0;
// output
Dbprintf(" NexWatch raw id : " _YELLOW_("0x%08"PRIx32), rawid);
uint8_t temp_checksum;
for (; magic < 255; magic++) {
temp_checksum = nexwatch_checksum(magic, cn, calc_parity);
if (temp_checksum == chk) {
Dbprintf(" Magic number : " _GREEN_("0x%X"), magic);
break;
}
}
Dbprintf(" 88bit id : " _YELLOW_("%"PRIu32) " (" _YELLOW_("0x%08"PRIx32)")", cn, cn);
Dbprintf(" mode : %x", mode);
Dbprintf(" Raw : " _YELLOW_("%08"PRIX32"%08"PRIX32"%08"PRIX32), raw1, raw2, raw3);
uint8_t entry[81];
memset(entry, 0, sizeof(entry));
sprintf((char *)entry, "Nexwatch ID: %"PRIu32", Magic bytes: 0x%X, Mode: %x\n",
cn,
magic,
mode);
append(entry, strlen((char *)entry));
Dbprintf("%s", entry);
BigBuf_free();
return PM3_SUCCESS;
}
void ModInfo(void) {
2021-06-23 17:42:33 +08:00
DbpString(_YELLOW_(" Nexwatch credentials detection mode") " - a.k.a NexID (jrjgjk & Zolorah)");
2021-06-23 16:00:56 +08:00
}
void RunMod(void) {
FpgaDownloadAndGo(FPGA_BITSTREAM_LF);
LFSetupFPGAForADC(LF_DIVISOR_125, true);
BigBuf_Clear();
StandAloneMode();
Dbprintf(_YELLOW_("[=] Standalone mode nexid started"));
rdv40_spiffs_lazy_mount();
log_exists = exists_in_spiffs(LF_NEXCOLLECT_LOGFILE);
// the main loop for your standalone mode
for (;;) {
WDT_HIT();
// exit from IceHID, send a usbcommand.
if (data_available()) break;
// Was our button held down or pressed?
int button_pressed = BUTTON_HELD(280);
if (button_pressed == BUTTON_HOLD)
break;
LED_A_ON();
uint32_t res;
size_t size = MIN(16385, BigBuf_max_traceLen());
DoAcquisition_config(false, size);
res = demodNexWatch();
if (res == PM3_SUCCESS) {
LED_A_OFF();
continue;
}
}
LED_C_ON();
rdv40_spiffs_lazy_unmount();
LED_C_OFF();
LEDsoff();
DownloadLogInstructions();
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
}