[FL-2759], [FL-2766] NFC collect params for mfkey32 attack (#1643)
* nfc: start nfc over rpc * nfc: add detect reader state * nfc: add reader analyzer * nfc: rework reader analyzer * reader_analyzer: print collected nonces to debug * reader analyzer: add save on SD card * reader_analyzer: separate mfkey related part to different file * mfkey32: add logic for collecting parameters * nfc: rework pcap with reader analyzer * nfc: add logger for reader * nfc: clean up * nfc: add detect reader view * nfc: add detect reader and mfkey nonces scenes * nfc: add mfkey comlplete scene * nfc: add new assets * nfc: fix gui * nfc: fix iso14443-4 UID emulation * nfc: add no sd card notification * nfc: fix grammar Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
230
lib/nfc/helpers/mfkey32.c
Normal file
230
lib/nfc/helpers/mfkey32.c
Normal file
@@ -0,0 +1,230 @@
|
||||
#include "mfkey32.h"
|
||||
|
||||
#include <furi/furi.h>
|
||||
#include <storage/storage.h>
|
||||
#include <stream/stream.h>
|
||||
#include <stream/buffered_file_stream.h>
|
||||
#include <m-array.h>
|
||||
|
||||
#include <lib/nfc/protocols/mifare_classic.h>
|
||||
#include <lib/nfc/protocols/nfc_util.h>
|
||||
|
||||
#define TAG "Mfkey32"
|
||||
|
||||
#define MFKEY32_LOGS_PATH EXT_PATH("nfc/.mfkey32.log")
|
||||
|
||||
typedef enum {
|
||||
Mfkey32StateIdle,
|
||||
Mfkey32StateAuthReceived,
|
||||
Mfkey32StateAuthNtSent,
|
||||
Mfkey32StateAuthArNrReceived,
|
||||
} Mfkey32State;
|
||||
|
||||
typedef struct {
|
||||
uint32_t cuid;
|
||||
uint8_t sector;
|
||||
MfClassicKey key;
|
||||
uint32_t nt0;
|
||||
uint32_t nr0;
|
||||
uint32_t ar0;
|
||||
uint32_t nt1;
|
||||
uint32_t nr1;
|
||||
uint32_t ar1;
|
||||
} Mfkey32Params;
|
||||
|
||||
ARRAY_DEF(Mfkey32Params, Mfkey32Params, M_POD_OPLIST);
|
||||
|
||||
typedef struct {
|
||||
uint8_t sector;
|
||||
MfClassicKey key;
|
||||
uint32_t nt;
|
||||
uint32_t nr;
|
||||
uint32_t ar;
|
||||
} Mfkey32Nonce;
|
||||
|
||||
struct Mfkey32 {
|
||||
Mfkey32State state;
|
||||
Stream* file_stream;
|
||||
Mfkey32Params_t params_arr;
|
||||
Mfkey32Nonce nonce;
|
||||
uint32_t cuid;
|
||||
Mfkey32ParseDataCallback callback;
|
||||
void* context;
|
||||
};
|
||||
|
||||
Mfkey32* mfkey32_alloc(uint32_t cuid) {
|
||||
Mfkey32* instance = malloc(sizeof(Mfkey32));
|
||||
instance->cuid = cuid;
|
||||
instance->state = Mfkey32StateIdle;
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
instance->file_stream = buffered_file_stream_alloc(storage);
|
||||
if(!buffered_file_stream_open(
|
||||
instance->file_stream, MFKEY32_LOGS_PATH, FSAM_WRITE, FSOM_OPEN_APPEND)) {
|
||||
buffered_file_stream_close(instance->file_stream);
|
||||
stream_free(instance->file_stream);
|
||||
free(instance);
|
||||
instance = NULL;
|
||||
} else {
|
||||
Mfkey32Params_init(instance->params_arr);
|
||||
}
|
||||
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
void mfkey32_free(Mfkey32* instance) {
|
||||
furi_assert(instance != NULL);
|
||||
|
||||
Mfkey32Params_clear(instance->params_arr);
|
||||
buffered_file_stream_close(instance->file_stream);
|
||||
stream_free(instance->file_stream);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void mfkey32_set_callback(Mfkey32* instance, Mfkey32ParseDataCallback callback, void* context) {
|
||||
furi_assert(instance);
|
||||
furi_assert(callback);
|
||||
|
||||
instance->callback = callback;
|
||||
instance->context = context;
|
||||
}
|
||||
|
||||
static bool mfkey32_write_params(Mfkey32* instance, Mfkey32Params* params) {
|
||||
string_t str;
|
||||
string_init_printf(
|
||||
str,
|
||||
"Sector %d key %c cuid %08x nt0 %08x nr0 %08x ar0 %08x nt1 %08x nr1 %08x ar1 %08x\n",
|
||||
params->sector,
|
||||
params->key == MfClassicKeyA ? 'A' : 'B',
|
||||
params->cuid,
|
||||
params->nt0,
|
||||
params->nr0,
|
||||
params->ar0,
|
||||
params->nt1,
|
||||
params->nr1,
|
||||
params->ar1);
|
||||
bool write_success = stream_write_string(instance->file_stream, str);
|
||||
string_clear(str);
|
||||
return write_success;
|
||||
}
|
||||
|
||||
static void mfkey32_add_params(Mfkey32* instance) {
|
||||
Mfkey32Nonce* nonce = &instance->nonce;
|
||||
bool nonce_added = false;
|
||||
// Search if we partially collected params
|
||||
if(Mfkey32Params_size(instance->params_arr)) {
|
||||
Mfkey32Params_it_t it;
|
||||
for(Mfkey32Params_it(it, instance->params_arr); !Mfkey32Params_end_p(it);
|
||||
Mfkey32Params_next(it)) {
|
||||
Mfkey32Params* params = Mfkey32Params_ref(it);
|
||||
if((params->sector == nonce->sector) && (params->key == nonce->key)) {
|
||||
params->nt1 = nonce->nt;
|
||||
params->nr1 = nonce->nr;
|
||||
params->ar1 = nonce->ar;
|
||||
nonce_added = true;
|
||||
FURI_LOG_I(
|
||||
TAG,
|
||||
"Params for sector %d key %c collected",
|
||||
params->sector,
|
||||
params->key == MfClassicKeyA ? 'A' : 'B');
|
||||
// Write on sd card
|
||||
if(mfkey32_write_params(instance, params)) {
|
||||
Mfkey32Params_remove(instance->params_arr, it);
|
||||
if(instance->callback) {
|
||||
instance->callback(Mfkey32EventParamCollected, instance->context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!nonce_added) {
|
||||
Mfkey32Params params = {
|
||||
.sector = nonce->sector,
|
||||
.key = nonce->key,
|
||||
.cuid = instance->cuid,
|
||||
.nt0 = nonce->nt,
|
||||
.nr0 = nonce->nr,
|
||||
.ar0 = nonce->ar,
|
||||
};
|
||||
Mfkey32Params_push_back(instance->params_arr, params);
|
||||
}
|
||||
}
|
||||
|
||||
void mfkey32_process_data(
|
||||
Mfkey32* instance,
|
||||
uint8_t* data,
|
||||
uint16_t len,
|
||||
bool reader_to_tag,
|
||||
bool crc_dropped) {
|
||||
furi_assert(instance);
|
||||
furi_assert(data);
|
||||
|
||||
Mfkey32Nonce* nonce = &instance->nonce;
|
||||
uint16_t data_len = len;
|
||||
if((data_len > 3) && !crc_dropped) {
|
||||
data_len -= 2;
|
||||
}
|
||||
|
||||
bool data_processed = false;
|
||||
if(instance->state == Mfkey32StateIdle) {
|
||||
if(reader_to_tag) {
|
||||
if((data[0] == 0x60) || (data[0] == 0x61)) {
|
||||
nonce->key = data[0] == 0x60 ? MfClassicKeyA : MfClassicKeyB;
|
||||
nonce->sector = mf_classic_get_sector_by_block(data[1]);
|
||||
instance->state = Mfkey32StateAuthReceived;
|
||||
data_processed = true;
|
||||
}
|
||||
}
|
||||
} else if(instance->state == Mfkey32StateAuthReceived) {
|
||||
if(!reader_to_tag) {
|
||||
if(len == 4) {
|
||||
nonce->nt = nfc_util_bytes2num(data, 4);
|
||||
instance->state = Mfkey32StateAuthNtSent;
|
||||
data_processed = true;
|
||||
}
|
||||
}
|
||||
} else if(instance->state == Mfkey32StateAuthNtSent) {
|
||||
if(reader_to_tag) {
|
||||
if(len == 8) {
|
||||
nonce->nr = nfc_util_bytes2num(data, 4);
|
||||
nonce->ar = nfc_util_bytes2num(&data[4], 4);
|
||||
mfkey32_add_params(instance);
|
||||
instance->state = Mfkey32StateIdle;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!data_processed) {
|
||||
instance->state = Mfkey32StateIdle;
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t mfkey32_get_auth_sectors(string_t data_str) {
|
||||
furi_assert(data_str);
|
||||
|
||||
uint16_t nonces_num = 0;
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
Stream* file_stream = buffered_file_stream_alloc(storage);
|
||||
string_t temp_str;
|
||||
string_init(temp_str);
|
||||
|
||||
do {
|
||||
if(!buffered_file_stream_open(
|
||||
file_stream, MFKEY32_LOGS_PATH, FSAM_READ, FSOM_OPEN_EXISTING))
|
||||
break;
|
||||
while(true) {
|
||||
if(!stream_read_line(file_stream, temp_str)) break;
|
||||
size_t uid_pos = string_search_str(temp_str, "cuid");
|
||||
string_left(temp_str, uid_pos);
|
||||
string_push_back(temp_str, '\n');
|
||||
string_cat(data_str, temp_str);
|
||||
nonces_num++;
|
||||
}
|
||||
} while(false);
|
||||
|
||||
buffered_file_stream_close(file_stream);
|
||||
stream_free(file_stream);
|
||||
string_clear(temp_str);
|
||||
|
||||
return nonces_num;
|
||||
}
|
27
lib/nfc/helpers/mfkey32.h
Normal file
27
lib/nfc/helpers/mfkey32.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include <lib/nfc/protocols/mifare_classic.h>
|
||||
#include <m-string.h>
|
||||
|
||||
typedef struct Mfkey32 Mfkey32;
|
||||
|
||||
typedef enum {
|
||||
Mfkey32EventParamCollected,
|
||||
} Mfkey32Event;
|
||||
|
||||
typedef void (*Mfkey32ParseDataCallback)(Mfkey32Event event, void* context);
|
||||
|
||||
Mfkey32* mfkey32_alloc(uint32_t cuid);
|
||||
|
||||
void mfkey32_free(Mfkey32* instance);
|
||||
|
||||
void mfkey32_process_data(
|
||||
Mfkey32* instance,
|
||||
uint8_t* data,
|
||||
uint16_t len,
|
||||
bool reader_to_tag,
|
||||
bool crc_dropped);
|
||||
|
||||
void mfkey32_set_callback(Mfkey32* instance, Mfkey32ParseDataCallback callback, void* context);
|
||||
|
||||
uint16_t mfkey32_get_auth_sectors(string_t string);
|
72
lib/nfc/helpers/nfc_debug_log.c
Normal file
72
lib/nfc/helpers/nfc_debug_log.c
Normal file
@@ -0,0 +1,72 @@
|
||||
#include "nfc_debug_log.h"
|
||||
|
||||
#include <m-string.h>
|
||||
#include <storage/storage.h>
|
||||
#include <stream/buffered_file_stream.h>
|
||||
|
||||
#define TAG "NfcDebugLog"
|
||||
|
||||
#define NFC_DEBUG_PCAP_FILENAME EXT_PATH("nfc/debug.txt")
|
||||
|
||||
struct NfcDebugLog {
|
||||
Stream* file_stream;
|
||||
string_t data_str;
|
||||
};
|
||||
|
||||
NfcDebugLog* nfc_debug_log_alloc() {
|
||||
NfcDebugLog* instance = malloc(sizeof(NfcDebugLog));
|
||||
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
instance->file_stream = buffered_file_stream_alloc(storage);
|
||||
|
||||
if(!buffered_file_stream_open(
|
||||
instance->file_stream, NFC_DEBUG_PCAP_FILENAME, FSAM_WRITE, FSOM_OPEN_APPEND)) {
|
||||
buffered_file_stream_close(instance->file_stream);
|
||||
stream_free(instance->file_stream);
|
||||
instance->file_stream = NULL;
|
||||
}
|
||||
|
||||
if(!instance->file_stream) {
|
||||
free(instance);
|
||||
instance = NULL;
|
||||
} else {
|
||||
string_init(instance->data_str);
|
||||
}
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
void nfc_debug_log_free(NfcDebugLog* instance) {
|
||||
furi_assert(instance);
|
||||
furi_assert(instance->file_stream);
|
||||
furi_assert(instance->data_str);
|
||||
|
||||
buffered_file_stream_close(instance->file_stream);
|
||||
stream_free(instance->file_stream);
|
||||
string_clear(instance->data_str);
|
||||
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void nfc_debug_log_process_data(
|
||||
NfcDebugLog* instance,
|
||||
uint8_t* data,
|
||||
uint16_t len,
|
||||
bool reader_to_tag,
|
||||
bool crc_dropped) {
|
||||
furi_assert(instance);
|
||||
furi_assert(instance->file_stream);
|
||||
furi_assert(instance->data_str);
|
||||
furi_assert(data);
|
||||
UNUSED(crc_dropped);
|
||||
|
||||
string_printf(instance->data_str, "%lu %c:", furi_get_tick(), reader_to_tag ? 'R' : 'T');
|
||||
uint16_t data_len = len;
|
||||
for(size_t i = 0; i < data_len; i++) {
|
||||
string_cat_printf(instance->data_str, " %02x", data[i]);
|
||||
}
|
||||
string_push_back(instance->data_str, '\n');
|
||||
|
||||
stream_write_string(instance->file_stream, instance->data_str);
|
||||
}
|
17
lib/nfc/helpers/nfc_debug_log.h
Normal file
17
lib/nfc/helpers/nfc_debug_log.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef struct NfcDebugLog NfcDebugLog;
|
||||
|
||||
NfcDebugLog* nfc_debug_log_alloc();
|
||||
|
||||
void nfc_debug_log_free(NfcDebugLog* instance);
|
||||
|
||||
void nfc_debug_log_process_data(
|
||||
NfcDebugLog* instance,
|
||||
uint8_t* data,
|
||||
uint16_t len,
|
||||
bool reader_to_tag,
|
||||
bool crc_dropped);
|
@@ -1,7 +1,9 @@
|
||||
#include "nfc_debug_pcap.h"
|
||||
|
||||
#include <storage/storage.h>
|
||||
#include <stream/buffered_file_stream.h>
|
||||
#include <furi_hal_nfc.h>
|
||||
#include <furi_hal_rtc.h>
|
||||
#include <stream_buffer.h>
|
||||
|
||||
#define TAG "NfcDebugPcap"
|
||||
|
||||
@@ -16,48 +18,94 @@
|
||||
#define DATA_PCD_TO_PICC_CRC_DROPPED 0xFA
|
||||
|
||||
#define NFC_DEBUG_PCAP_FILENAME EXT_PATH("nfc/debug.pcap")
|
||||
#define NFC_DEBUG_PCAP_BUFFER_SIZE 64
|
||||
|
||||
struct NfcDebugPcapWorker {
|
||||
bool alive;
|
||||
Storage* storage;
|
||||
File* file;
|
||||
StreamBufferHandle_t stream;
|
||||
FuriThread* thread;
|
||||
struct NfcDebugPcap {
|
||||
Stream* file_stream;
|
||||
};
|
||||
|
||||
static File* nfc_debug_pcap_open(Storage* storage) {
|
||||
File* file = storage_file_alloc(storage);
|
||||
if(!storage_file_open(file, NFC_DEBUG_PCAP_FILENAME, FSAM_WRITE, FSOM_OPEN_APPEND)) {
|
||||
storage_file_free(file);
|
||||
return NULL;
|
||||
}
|
||||
if(!storage_file_tell(file)) {
|
||||
struct {
|
||||
uint32_t magic;
|
||||
uint16_t major, minor;
|
||||
uint32_t reserved[2];
|
||||
uint32_t snaplen;
|
||||
uint32_t link_type;
|
||||
} __attribute__((__packed__)) pcap_hdr = {
|
||||
.magic = PCAP_MAGIC,
|
||||
.major = PCAP_MAJOR,
|
||||
.minor = PCAP_MINOR,
|
||||
.snaplen = FURI_HAL_NFC_DATA_BUFF_SIZE,
|
||||
.link_type = DLT_ISO_14443,
|
||||
};
|
||||
if(storage_file_write(file, &pcap_hdr, sizeof(pcap_hdr)) != sizeof(pcap_hdr)) {
|
||||
FURI_LOG_E(TAG, "Failed to write pcap header");
|
||||
static Stream* nfc_debug_pcap_open(Storage* storage) {
|
||||
Stream* stream = NULL;
|
||||
stream = buffered_file_stream_alloc(storage);
|
||||
if(!buffered_file_stream_open(stream, NFC_DEBUG_PCAP_FILENAME, FSAM_WRITE, FSOM_OPEN_APPEND)) {
|
||||
buffered_file_stream_close(stream);
|
||||
stream_free(stream);
|
||||
stream = NULL;
|
||||
} else {
|
||||
if(!stream_tell(stream)) {
|
||||
struct {
|
||||
uint32_t magic;
|
||||
uint16_t major, minor;
|
||||
uint32_t reserved[2];
|
||||
uint32_t snaplen;
|
||||
uint32_t link_type;
|
||||
} __attribute__((__packed__)) pcap_hdr = {
|
||||
.magic = PCAP_MAGIC,
|
||||
.major = PCAP_MAJOR,
|
||||
.minor = PCAP_MINOR,
|
||||
.snaplen = FURI_HAL_NFC_DATA_BUFF_SIZE,
|
||||
.link_type = DLT_ISO_14443,
|
||||
};
|
||||
if(stream_write(stream, (uint8_t*)&pcap_hdr, sizeof(pcap_hdr)) != sizeof(pcap_hdr)) {
|
||||
FURI_LOG_E(TAG, "Failed to write pcap header");
|
||||
buffered_file_stream_close(stream);
|
||||
stream_free(stream);
|
||||
stream = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
return file;
|
||||
return stream;
|
||||
}
|
||||
|
||||
static void
|
||||
nfc_debug_pcap_write(NfcDebugPcapWorker* instance, uint8_t event, uint8_t* data, uint16_t len) {
|
||||
NfcDebugPcap* nfc_debug_pcap_alloc() {
|
||||
NfcDebugPcap* instance = malloc(sizeof(NfcDebugPcap));
|
||||
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
instance->file_stream = nfc_debug_pcap_open(storage);
|
||||
if(!instance->file_stream) {
|
||||
free(instance);
|
||||
instance = NULL;
|
||||
}
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
void nfc_debug_pcap_free(NfcDebugPcap* instance) {
|
||||
furi_assert(instance);
|
||||
furi_assert(instance->file_stream);
|
||||
|
||||
buffered_file_stream_close(instance->file_stream);
|
||||
stream_free(instance->file_stream);
|
||||
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void nfc_debug_pcap_process_data(
|
||||
NfcDebugPcap* instance,
|
||||
uint8_t* data,
|
||||
uint16_t len,
|
||||
bool reader_to_tag,
|
||||
bool crc_dropped) {
|
||||
furi_assert(instance);
|
||||
furi_assert(data);
|
||||
FuriHalRtcDateTime datetime;
|
||||
furi_hal_rtc_get_datetime(&datetime);
|
||||
|
||||
uint8_t event = 0;
|
||||
if(reader_to_tag) {
|
||||
if(crc_dropped) {
|
||||
event = DATA_PCD_TO_PICC_CRC_DROPPED;
|
||||
} else {
|
||||
event = DATA_PCD_TO_PICC;
|
||||
}
|
||||
} else {
|
||||
if(crc_dropped) {
|
||||
event = DATA_PICC_TO_PCD_CRC_DROPPED;
|
||||
} else {
|
||||
event = DATA_PICC_TO_PCD;
|
||||
}
|
||||
}
|
||||
|
||||
struct {
|
||||
// https://wiki.wireshark.org/Development/LibpcapFileFormat#record-packet-header
|
||||
uint32_t ts_sec;
|
||||
@@ -77,90 +125,6 @@ static void
|
||||
.event = event,
|
||||
.len = len << 8 | len >> 8,
|
||||
};
|
||||
xStreamBufferSend(instance->stream, &pkt_hdr, sizeof(pkt_hdr), FuriWaitForever);
|
||||
xStreamBufferSend(instance->stream, data, len, FuriWaitForever);
|
||||
}
|
||||
|
||||
static void
|
||||
nfc_debug_pcap_write_tx(uint8_t* data, uint16_t bits, bool crc_dropped, void* context) {
|
||||
NfcDebugPcapWorker* instance = context;
|
||||
uint8_t event = crc_dropped ? DATA_PCD_TO_PICC_CRC_DROPPED : DATA_PCD_TO_PICC;
|
||||
nfc_debug_pcap_write(instance, event, data, bits / 8);
|
||||
}
|
||||
|
||||
static void
|
||||
nfc_debug_pcap_write_rx(uint8_t* data, uint16_t bits, bool crc_dropped, void* context) {
|
||||
NfcDebugPcapWorker* instance = context;
|
||||
uint8_t event = crc_dropped ? DATA_PICC_TO_PCD_CRC_DROPPED : DATA_PICC_TO_PCD;
|
||||
nfc_debug_pcap_write(instance, event, data, bits / 8);
|
||||
}
|
||||
|
||||
int32_t nfc_debug_pcap_thread(void* context) {
|
||||
NfcDebugPcapWorker* instance = context;
|
||||
uint8_t buffer[NFC_DEBUG_PCAP_BUFFER_SIZE];
|
||||
|
||||
while(instance->alive) {
|
||||
size_t ret =
|
||||
xStreamBufferReceive(instance->stream, buffer, NFC_DEBUG_PCAP_BUFFER_SIZE, 50);
|
||||
if(storage_file_write(instance->file, buffer, ret) != ret) {
|
||||
FURI_LOG_E(TAG, "Failed to write pcap data");
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
NfcDebugPcapWorker* nfc_debug_pcap_alloc(Storage* storage) {
|
||||
NfcDebugPcapWorker* instance = malloc(sizeof(NfcDebugPcapWorker));
|
||||
|
||||
instance->alive = true;
|
||||
|
||||
instance->storage = storage;
|
||||
|
||||
instance->file = nfc_debug_pcap_open(storage);
|
||||
|
||||
instance->stream = xStreamBufferCreate(4096, 1);
|
||||
|
||||
instance->thread = furi_thread_alloc();
|
||||
furi_thread_set_name(instance->thread, "PcapWorker");
|
||||
furi_thread_set_stack_size(instance->thread, 1024);
|
||||
furi_thread_set_callback(instance->thread, nfc_debug_pcap_thread);
|
||||
furi_thread_set_context(instance->thread, instance);
|
||||
furi_thread_start(instance->thread);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
void nfc_debug_pcap_free(NfcDebugPcapWorker* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
instance->alive = false;
|
||||
|
||||
furi_thread_join(instance->thread);
|
||||
furi_thread_free(instance->thread);
|
||||
|
||||
vStreamBufferDelete(instance->stream);
|
||||
|
||||
if(instance->file) storage_file_free(instance->file);
|
||||
|
||||
instance->storage = NULL;
|
||||
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void nfc_debug_pcap_prepare_tx_rx(
|
||||
NfcDebugPcapWorker* instance,
|
||||
FuriHalNfcTxRxContext* tx_rx,
|
||||
bool is_picc) {
|
||||
if(!instance || !instance->file) return;
|
||||
|
||||
if(is_picc) {
|
||||
tx_rx->sniff_tx = nfc_debug_pcap_write_rx;
|
||||
tx_rx->sniff_rx = nfc_debug_pcap_write_tx;
|
||||
} else {
|
||||
tx_rx->sniff_tx = nfc_debug_pcap_write_tx;
|
||||
tx_rx->sniff_rx = nfc_debug_pcap_write_rx;
|
||||
}
|
||||
|
||||
tx_rx->sniff_context = instance;
|
||||
stream_write(instance->file_stream, (uint8_t*)&pkt_hdr, sizeof(pkt_hdr));
|
||||
stream_write(instance->file_stream, data, len);
|
||||
}
|
||||
|
@@ -1,21 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include <furi_hal_nfc.h>
|
||||
#include <storage/storage.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef struct NfcDebugPcapWorker NfcDebugPcapWorker;
|
||||
typedef struct NfcDebugPcap NfcDebugPcap;
|
||||
|
||||
NfcDebugPcapWorker* nfc_debug_pcap_alloc(Storage* storage);
|
||||
NfcDebugPcap* nfc_debug_pcap_alloc();
|
||||
|
||||
void nfc_debug_pcap_free(NfcDebugPcapWorker* instance);
|
||||
void nfc_debug_pcap_free(NfcDebugPcap* instance);
|
||||
|
||||
/** Prepare tx/rx context for debug pcap logging, if enabled.
|
||||
*
|
||||
* @param instance NfcDebugPcapWorker* instance, can be NULL
|
||||
* @param tx_rx TX/RX context to log
|
||||
* @param is_picc if true, record Flipper as PICC, else PCD.
|
||||
*/
|
||||
void nfc_debug_pcap_prepare_tx_rx(
|
||||
NfcDebugPcapWorker* instance,
|
||||
FuriHalNfcTxRxContext* tx_rx,
|
||||
bool is_picc);
|
||||
void nfc_debug_pcap_process_data(
|
||||
NfcDebugPcap* instance,
|
||||
uint8_t* data,
|
||||
uint16_t len,
|
||||
bool reader_to_tag,
|
||||
bool crc_dropped);
|
||||
|
261
lib/nfc/helpers/reader_analyzer.c
Normal file
261
lib/nfc/helpers/reader_analyzer.c
Normal file
@@ -0,0 +1,261 @@
|
||||
#include "reader_analyzer.h"
|
||||
#include <stream_buffer.h>
|
||||
#include <lib/nfc/protocols/nfc_util.h>
|
||||
#include <lib/nfc/protocols/mifare_classic.h>
|
||||
#include <m-array.h>
|
||||
|
||||
#include "mfkey32.h"
|
||||
#include "nfc_debug_pcap.h"
|
||||
#include "nfc_debug_log.h"
|
||||
|
||||
#define TAG "ReaderAnalyzer"
|
||||
|
||||
#define READER_ANALYZER_MAX_BUFF_SIZE (1024)
|
||||
|
||||
typedef struct {
|
||||
bool reader_to_tag;
|
||||
bool crc_dropped;
|
||||
uint16_t len;
|
||||
} ReaderAnalyzerHeader;
|
||||
|
||||
typedef enum {
|
||||
ReaderAnalyzerNfcDataMfClassic,
|
||||
} ReaderAnalyzerNfcData;
|
||||
|
||||
struct ReaderAnalyzer {
|
||||
FuriHalNfcDevData nfc_data;
|
||||
|
||||
bool alive;
|
||||
StreamBufferHandle_t stream;
|
||||
FuriThread* thread;
|
||||
|
||||
ReaderAnalyzerParseDataCallback callback;
|
||||
void* context;
|
||||
|
||||
ReaderAnalyzerMode mode;
|
||||
Mfkey32* mfkey32;
|
||||
NfcDebugLog* debug_log;
|
||||
NfcDebugPcap* pcap;
|
||||
};
|
||||
|
||||
const FuriHalNfcDevData reader_analyzer_nfc_data[] = {
|
||||
[ReaderAnalyzerNfcDataMfClassic] =
|
||||
{.sak = 0x08,
|
||||
.atqa = {0x44, 0x00},
|
||||
.interface = FuriHalNfcInterfaceRf,
|
||||
.type = FuriHalNfcTypeA,
|
||||
.uid_len = 7,
|
||||
.uid = {0x04, 0x77, 0x70, 0x2A, 0x23, 0x4F, 0x80},
|
||||
.cuid = 0x2A234F80},
|
||||
};
|
||||
|
||||
void reader_analyzer_parse(ReaderAnalyzer* instance, uint8_t* buffer, size_t size) {
|
||||
if(size < sizeof(ReaderAnalyzerHeader)) return;
|
||||
|
||||
size_t bytes_i = 0;
|
||||
while(bytes_i < size) {
|
||||
ReaderAnalyzerHeader* header = (ReaderAnalyzerHeader*)&buffer[bytes_i];
|
||||
uint16_t len = header->len;
|
||||
if(bytes_i + len > size) break;
|
||||
bytes_i += sizeof(ReaderAnalyzerHeader);
|
||||
if(instance->mfkey32) {
|
||||
mfkey32_process_data(
|
||||
instance->mfkey32,
|
||||
&buffer[bytes_i],
|
||||
len,
|
||||
header->reader_to_tag,
|
||||
header->crc_dropped);
|
||||
}
|
||||
if(instance->pcap) {
|
||||
nfc_debug_pcap_process_data(
|
||||
instance->pcap, &buffer[bytes_i], len, header->reader_to_tag, header->crc_dropped);
|
||||
}
|
||||
if(instance->debug_log) {
|
||||
nfc_debug_log_process_data(
|
||||
instance->debug_log,
|
||||
&buffer[bytes_i],
|
||||
len,
|
||||
header->reader_to_tag,
|
||||
header->crc_dropped);
|
||||
}
|
||||
bytes_i += len;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t reader_analyzer_thread(void* context) {
|
||||
ReaderAnalyzer* reader_analyzer = context;
|
||||
uint8_t buffer[READER_ANALYZER_MAX_BUFF_SIZE] = {};
|
||||
|
||||
while(reader_analyzer->alive || !xStreamBufferIsEmpty(reader_analyzer->stream)) {
|
||||
size_t ret = xStreamBufferReceive(
|
||||
reader_analyzer->stream, buffer, READER_ANALYZER_MAX_BUFF_SIZE, 50);
|
||||
if(ret) {
|
||||
reader_analyzer_parse(reader_analyzer, buffer, ret);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ReaderAnalyzer* reader_analyzer_alloc() {
|
||||
ReaderAnalyzer* instance = malloc(sizeof(ReaderAnalyzer));
|
||||
|
||||
instance->nfc_data = reader_analyzer_nfc_data[ReaderAnalyzerNfcDataMfClassic];
|
||||
instance->alive = false;
|
||||
instance->stream =
|
||||
xStreamBufferCreate(READER_ANALYZER_MAX_BUFF_SIZE, sizeof(ReaderAnalyzerHeader));
|
||||
|
||||
instance->thread = furi_thread_alloc();
|
||||
furi_thread_set_name(instance->thread, "ReaderAnalyzerWorker");
|
||||
furi_thread_set_stack_size(instance->thread, 2048);
|
||||
furi_thread_set_callback(instance->thread, reader_analyzer_thread);
|
||||
furi_thread_set_context(instance->thread, instance);
|
||||
furi_thread_set_priority(instance->thread, FuriThreadPriorityLow);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
static void reader_analyzer_mfkey_callback(Mfkey32Event event, void* context) {
|
||||
furi_assert(context);
|
||||
ReaderAnalyzer* instance = context;
|
||||
|
||||
if(event == Mfkey32EventParamCollected) {
|
||||
if(instance->callback) {
|
||||
instance->callback(ReaderAnalyzerEventMfkeyCollected, instance->context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void reader_analyzer_start(ReaderAnalyzer* instance, ReaderAnalyzerMode mode) {
|
||||
furi_assert(instance);
|
||||
|
||||
xStreamBufferReset(instance->stream);
|
||||
if(mode & ReaderAnalyzerModeDebugLog) {
|
||||
instance->debug_log = nfc_debug_log_alloc();
|
||||
}
|
||||
if(mode & ReaderAnalyzerModeMfkey) {
|
||||
instance->mfkey32 = mfkey32_alloc(instance->nfc_data.cuid);
|
||||
if(instance->mfkey32) {
|
||||
mfkey32_set_callback(instance->mfkey32, reader_analyzer_mfkey_callback, instance);
|
||||
}
|
||||
}
|
||||
if(mode & ReaderAnalyzerModeDebugPcap) {
|
||||
instance->pcap = nfc_debug_pcap_alloc();
|
||||
}
|
||||
|
||||
instance->alive = true;
|
||||
furi_thread_start(instance->thread);
|
||||
}
|
||||
|
||||
void reader_analyzer_stop(ReaderAnalyzer* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
instance->alive = false;
|
||||
furi_thread_join(instance->thread);
|
||||
|
||||
if(instance->debug_log) {
|
||||
nfc_debug_log_free(instance->debug_log);
|
||||
instance->debug_log = NULL;
|
||||
}
|
||||
if(instance->mfkey32) {
|
||||
mfkey32_free(instance->mfkey32);
|
||||
instance->mfkey32 = NULL;
|
||||
}
|
||||
if(instance->pcap) {
|
||||
nfc_debug_pcap_free(instance->pcap);
|
||||
}
|
||||
}
|
||||
|
||||
void reader_analyzer_free(ReaderAnalyzer* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
reader_analyzer_stop(instance);
|
||||
furi_thread_free(instance->thread);
|
||||
vStreamBufferDelete(instance->stream);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void reader_analyzer_set_callback(
|
||||
ReaderAnalyzer* instance,
|
||||
ReaderAnalyzerParseDataCallback callback,
|
||||
void* context) {
|
||||
furi_assert(instance);
|
||||
furi_assert(callback);
|
||||
|
||||
instance->callback = callback;
|
||||
instance->context = context;
|
||||
}
|
||||
|
||||
NfcProtocol
|
||||
reader_analyzer_guess_protocol(ReaderAnalyzer* instance, uint8_t* buff_rx, uint16_t len) {
|
||||
furi_assert(instance);
|
||||
furi_assert(buff_rx);
|
||||
UNUSED(len);
|
||||
NfcProtocol protocol = NfcDeviceProtocolUnknown;
|
||||
|
||||
if((buff_rx[0] == 0x60) || (buff_rx[0] == 0x61)) {
|
||||
protocol = NfcDeviceProtocolMifareClassic;
|
||||
}
|
||||
|
||||
return protocol;
|
||||
}
|
||||
|
||||
FuriHalNfcDevData* reader_analyzer_get_nfc_data(ReaderAnalyzer* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
return &instance->nfc_data;
|
||||
}
|
||||
|
||||
static void reader_analyzer_write(
|
||||
ReaderAnalyzer* instance,
|
||||
uint8_t* data,
|
||||
uint16_t len,
|
||||
bool reader_to_tag,
|
||||
bool crc_dropped) {
|
||||
ReaderAnalyzerHeader header = {
|
||||
.reader_to_tag = reader_to_tag, .crc_dropped = crc_dropped, .len = len};
|
||||
size_t data_sent = 0;
|
||||
data_sent = xStreamBufferSend(
|
||||
instance->stream, &header, sizeof(ReaderAnalyzerHeader), FuriWaitForever);
|
||||
if(data_sent != sizeof(ReaderAnalyzerHeader)) {
|
||||
FURI_LOG_W(TAG, "Sent %d out of %d bytes", data_sent, sizeof(ReaderAnalyzerHeader));
|
||||
}
|
||||
data_sent = xStreamBufferSend(instance->stream, data, len, FuriWaitForever);
|
||||
if(data_sent != len) {
|
||||
FURI_LOG_W(TAG, "Sent %d out of %d bytes", data_sent, len);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
reader_analyzer_write_rx(uint8_t* data, uint16_t bits, bool crc_dropped, void* context) {
|
||||
UNUSED(crc_dropped);
|
||||
ReaderAnalyzer* reader_analyzer = context;
|
||||
uint16_t bytes = bits < 8 ? 1 : bits / 8;
|
||||
reader_analyzer_write(reader_analyzer, data, bytes, false, crc_dropped);
|
||||
}
|
||||
|
||||
static void
|
||||
reader_analyzer_write_tx(uint8_t* data, uint16_t bits, bool crc_dropped, void* context) {
|
||||
UNUSED(crc_dropped);
|
||||
ReaderAnalyzer* reader_analyzer = context;
|
||||
uint16_t bytes = bits < 8 ? 1 : bits / 8;
|
||||
reader_analyzer_write(reader_analyzer, data, bytes, true, crc_dropped);
|
||||
}
|
||||
|
||||
void reader_analyzer_prepare_tx_rx(
|
||||
ReaderAnalyzer* instance,
|
||||
FuriHalNfcTxRxContext* tx_rx,
|
||||
bool is_picc) {
|
||||
furi_assert(instance);
|
||||
furi_assert(tx_rx);
|
||||
|
||||
if(is_picc) {
|
||||
tx_rx->sniff_tx = reader_analyzer_write_rx;
|
||||
tx_rx->sniff_rx = reader_analyzer_write_tx;
|
||||
} else {
|
||||
tx_rx->sniff_rx = reader_analyzer_write_rx;
|
||||
tx_rx->sniff_tx = reader_analyzer_write_tx;
|
||||
}
|
||||
|
||||
tx_rx->sniff_context = instance;
|
||||
}
|
41
lib/nfc/helpers/reader_analyzer.h
Normal file
41
lib/nfc/helpers/reader_analyzer.h
Normal file
@@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <lib/nfc/nfc_device.h>
|
||||
|
||||
typedef enum {
|
||||
ReaderAnalyzerModeDebugLog = 0x01,
|
||||
ReaderAnalyzerModeMfkey = 0x02,
|
||||
ReaderAnalyzerModeDebugPcap = 0x04,
|
||||
} ReaderAnalyzerMode;
|
||||
|
||||
typedef enum {
|
||||
ReaderAnalyzerEventMfkeyCollected,
|
||||
} ReaderAnalyzerEvent;
|
||||
|
||||
typedef struct ReaderAnalyzer ReaderAnalyzer;
|
||||
|
||||
typedef void (*ReaderAnalyzerParseDataCallback)(ReaderAnalyzerEvent event, void* context);
|
||||
|
||||
ReaderAnalyzer* reader_analyzer_alloc();
|
||||
|
||||
void reader_analyzer_free(ReaderAnalyzer* instance);
|
||||
|
||||
void reader_analyzer_set_callback(
|
||||
ReaderAnalyzer* instance,
|
||||
ReaderAnalyzerParseDataCallback callback,
|
||||
void* context);
|
||||
|
||||
void reader_analyzer_start(ReaderAnalyzer* instance, ReaderAnalyzerMode mode);
|
||||
|
||||
void reader_analyzer_stop(ReaderAnalyzer* instance);
|
||||
|
||||
NfcProtocol
|
||||
reader_analyzer_guess_protocol(ReaderAnalyzer* instance, uint8_t* buff_rx, uint16_t len);
|
||||
|
||||
FuriHalNfcDevData* reader_analyzer_get_nfc_data(ReaderAnalyzer* instance);
|
||||
|
||||
void reader_analyzer_prepare_tx_rx(
|
||||
ReaderAnalyzer* instance,
|
||||
FuriHalNfcTxRxContext* tx_rx,
|
||||
bool is_picc);
|
Reference in New Issue
Block a user