[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:
gornekich
2022-09-03 15:25:36 +03:00
committed by GitHub
parent ed2c607dd3
commit 1853359d78
24 changed files with 1154 additions and 251 deletions

230
lib/nfc/helpers/mfkey32.c Normal file
View 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
View 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);

View 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);
}

View 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);

View File

@@ -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);
}

View File

@@ -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);

View 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;
}

View 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);