From 8afd8fd2b6ec351ff2b309709a15eec0b8c1c548 Mon Sep 17 00:00:00 2001 From: rioux Date: Wed, 23 Jun 2021 10:00:56 +0200 Subject: [PATCH] Standalone update for lf nexwatch --- CHANGELOG.md | 1 + armsrc/Standalone/Makefile.hal | 7 +- armsrc/Standalone/Makefile.inc | 4 + armsrc/Standalone/lf_nexid.c | 343 +++++++++++++++++++++++++++++++++ 4 files changed, 353 insertions(+), 2 deletions(-) create mode 100644 armsrc/Standalone/lf_nexid.c diff --git a/CHANGELOG.md b/CHANGELOG.md index 87429b2ea..fddd51abe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ All notable changes to this project will be documented in this file. This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log... ## [unreleased][unreleased] + - Added Standalone mode for nexwatch ID credentials (@Guilhem7, @MaximeBosca) - Fix `lf em 4x50/4x70 *` reverted a missunderstanding in byte order macros (@iceman1001) - Added more keys (@equipter) - Changed `hf nfc ndefread` - ndef parser now handles more types (@iceman1001) diff --git a/armsrc/Standalone/Makefile.hal b/armsrc/Standalone/Makefile.hal index b413b1ac6..8a51a39e8 100644 --- a/armsrc/Standalone/Makefile.hal +++ b/armsrc/Standalone/Makefile.hal @@ -29,6 +29,9 @@ define KNOWN_STANDALONE_DEFINITIONS | LF_ICEHID | LF HID collector to flashmem | | (RDV4 only) | | +----------------------------------------------------------+ +| LF_NEXID | LF Nexwatch collector to flashmem | +| (RDV4 only) | | ++----------------------------------------------------------+ | LF_PROXBRUTE | HID ProxII bruteforce | | | - Brad Antoniewicz | +----------------------------------------------------------+ @@ -76,10 +79,10 @@ define KNOWN_STANDALONE_DEFINITIONS +----------------------------------------------------------+ endef -STANDALONE_MODES := LF_SKELETON LF_EM4100EMUL LF_EM4100RSWB LF_EM4100RWC LF_HIDBRUTE LF_ICEHID LF_PROXBRUTE LF_SAMYRUN LF_THAREXDE +STANDALONE_MODES := LF_SKELETON LF_EM4100EMUL LF_EM4100RSWB LF_EM4100RWC LF_HIDBRUTE LF_ICEHID LF_PROXBRUTE LF_SAMYRUN LF_THAREXDE LF_NEXID STANDALONE_MODES += HF_14ASNIFF HF_AVEFUL HF_BOG HF_COLIN HF_CRAFTBYTE HF_ICECLASS HF_LEGIC HF_MATTYRUN HF_MSDSAL HF_TCPRST HF_TMUDFORD HF_YOUNG STANDALONE_MODES_REQ_SMARTCARD := -STANDALONE_MODES_REQ_FLASH := LF_ICEHID LF_THAREXDE HF_14ASNIFF HF_BOG HF_COLIN HF_ICECLASS +STANDALONE_MODES_REQ_FLASH := LF_ICEHID LF_NEXID LF_THAREXDE HF_14ASNIFF HF_BOG HF_COLIN HF_ICECLASS ifneq ($(filter $(STANDALONE),$(STANDALONE_MODES)),) STANDALONE_PLATFORM_DEFS += -DWITH_STANDALONE_$(STANDALONE) ifneq ($(filter $(STANDALONE),$(STANDALONE_MODES_REQ_SMARTCARD)),) diff --git a/armsrc/Standalone/Makefile.inc b/armsrc/Standalone/Makefile.inc index c4590866f..79c6ab979 100644 --- a/armsrc/Standalone/Makefile.inc +++ b/armsrc/Standalone/Makefile.inc @@ -49,6 +49,10 @@ endif ifneq (,$(findstring WITH_STANDALONE_LF_ICEHID,$(APP_CFLAGS))) SRC_STANDALONE = lf_icehid.c endif +# WITH_STANDALONE_LF_NEXID +ifneq (,$(findstring WITH_STANDALONE_LF_NEXID,$(APP_CFLAGS))) + SRC_STANDALONE = lf_nexid.c +endif # WITH_STANDALONE_LF_EM4100EMUL ifneq (,$(findstring WITH_STANDALONE_LF_EM4100EMUL,$(APP_CFLAGS))) SRC_STANDALONE = lf_em4100emul.c diff --git a/armsrc/Standalone/lf_nexid.c b/armsrc/Standalone/lf_nexid.c new file mode 100644 index 000000000..9ee423b79 --- /dev/null +++ b/armsrc/Standalone/lf_nexid.c @@ -0,0 +1,343 @@ +//----------------------------------------------------------------------------- +// 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 +#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) { + DbpString(_YELLOW_(" Nexwatch credentials detection module") " - a.k.a NexID (jrjgjk & Zolorah)"); +} + +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); +}