mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2024-11-10 09:32:41 +08:00
Standalone update for lf nexwatch
This commit is contained in:
parent
11362c66f1
commit
8afd8fd2b6
4 changed files with 353 additions and 2 deletions
|
@ -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...
|
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]
|
## [unreleased][unreleased]
|
||||||
|
- Added Standalone mode for nexwatch ID credentials (@Guilhem7, @MaximeBosca)
|
||||||
- Fix `lf em 4x50/4x70 *` reverted a missunderstanding in byte order macros (@iceman1001)
|
- Fix `lf em 4x50/4x70 *` reverted a missunderstanding in byte order macros (@iceman1001)
|
||||||
- Added more keys (@equipter)
|
- Added more keys (@equipter)
|
||||||
- Changed `hf nfc ndefread` - ndef parser now handles more types (@iceman1001)
|
- Changed `hf nfc ndefread` - ndef parser now handles more types (@iceman1001)
|
||||||
|
|
|
@ -29,6 +29,9 @@ define KNOWN_STANDALONE_DEFINITIONS
|
||||||
| LF_ICEHID | LF HID collector to flashmem |
|
| LF_ICEHID | LF HID collector to flashmem |
|
||||||
| (RDV4 only) | |
|
| (RDV4 only) | |
|
||||||
+----------------------------------------------------------+
|
+----------------------------------------------------------+
|
||||||
|
| LF_NEXID | LF Nexwatch collector to flashmem |
|
||||||
|
| (RDV4 only) | |
|
||||||
|
+----------------------------------------------------------+
|
||||||
| LF_PROXBRUTE | HID ProxII bruteforce |
|
| LF_PROXBRUTE | HID ProxII bruteforce |
|
||||||
| | - Brad Antoniewicz |
|
| | - Brad Antoniewicz |
|
||||||
+----------------------------------------------------------+
|
+----------------------------------------------------------+
|
||||||
|
@ -76,10 +79,10 @@ define KNOWN_STANDALONE_DEFINITIONS
|
||||||
+----------------------------------------------------------+
|
+----------------------------------------------------------+
|
||||||
endef
|
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 += 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_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)),)
|
ifneq ($(filter $(STANDALONE),$(STANDALONE_MODES)),)
|
||||||
STANDALONE_PLATFORM_DEFS += -DWITH_STANDALONE_$(STANDALONE)
|
STANDALONE_PLATFORM_DEFS += -DWITH_STANDALONE_$(STANDALONE)
|
||||||
ifneq ($(filter $(STANDALONE),$(STANDALONE_MODES_REQ_SMARTCARD)),)
|
ifneq ($(filter $(STANDALONE),$(STANDALONE_MODES_REQ_SMARTCARD)),)
|
||||||
|
|
|
@ -49,6 +49,10 @@ endif
|
||||||
ifneq (,$(findstring WITH_STANDALONE_LF_ICEHID,$(APP_CFLAGS)))
|
ifneq (,$(findstring WITH_STANDALONE_LF_ICEHID,$(APP_CFLAGS)))
|
||||||
SRC_STANDALONE = lf_icehid.c
|
SRC_STANDALONE = lf_icehid.c
|
||||||
endif
|
endif
|
||||||
|
# WITH_STANDALONE_LF_NEXID
|
||||||
|
ifneq (,$(findstring WITH_STANDALONE_LF_NEXID,$(APP_CFLAGS)))
|
||||||
|
SRC_STANDALONE = lf_nexid.c
|
||||||
|
endif
|
||||||
# WITH_STANDALONE_LF_EM4100EMUL
|
# WITH_STANDALONE_LF_EM4100EMUL
|
||||||
ifneq (,$(findstring WITH_STANDALONE_LF_EM4100EMUL,$(APP_CFLAGS)))
|
ifneq (,$(findstring WITH_STANDALONE_LF_EM4100EMUL,$(APP_CFLAGS)))
|
||||||
SRC_STANDALONE = lf_em4100emul.c
|
SRC_STANDALONE = lf_em4100emul.c
|
||||||
|
|
343
armsrc/Standalone/lf_nexid.c
Normal file
343
armsrc/Standalone/lf_nexid.c
Normal file
|
@ -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 <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) {
|
||||||
|
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);
|
||||||
|
}
|
Loading…
Reference in a new issue