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

View File

@@ -28,9 +28,7 @@ NfcWorker* nfc_worker_alloc() {
}
nfc_worker_change_state(nfc_worker, NfcWorkerStateReady);
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
nfc_worker->debug_pcap_worker = nfc_debug_pcap_alloc(nfc_worker->storage);
}
nfc_worker->reader_analyzer = reader_analyzer_alloc(nfc_worker->storage);
return nfc_worker;
}
@@ -42,7 +40,7 @@ void nfc_worker_free(NfcWorker* nfc_worker) {
furi_record_close(RECORD_STORAGE);
if(nfc_worker->debug_pcap_worker) nfc_debug_pcap_free(nfc_worker->debug_pcap_worker);
reader_analyzer_free(nfc_worker->reader_analyzer);
free(nfc_worker);
}
@@ -105,6 +103,8 @@ int32_t nfc_worker_task(void* context) {
nfc_worker_mf_ultralight_read_auth(nfc_worker);
} else if(nfc_worker->state == NfcWorkerStateMfClassicDictAttack) {
nfc_worker_mf_classic_dict_attack(nfc_worker);
} else if(nfc_worker->state == NfcWorkerStateAnalyzeReader) {
nfc_worker_analyze_reader(nfc_worker);
}
furi_hal_nfc_sleep();
nfc_worker_change_state(nfc_worker, NfcWorkerStateReady);
@@ -117,7 +117,11 @@ static bool nfc_worker_read_mf_ultralight(NfcWorker* nfc_worker, FuriHalNfcTxRxC
MfUltralightReader reader = {};
MfUltralightData data = {};
nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, tx_rx, false);
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
reader_analyzer_prepare_tx_rx(nfc_worker->reader_analyzer, tx_rx, false);
reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog);
}
do {
// Read card
if(!furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 200)) break;
@@ -127,6 +131,10 @@ static bool nfc_worker_read_mf_ultralight(NfcWorker* nfc_worker, FuriHalNfcTxRxC
read_success = true;
} while(false);
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
reader_analyzer_stop(nfc_worker->reader_analyzer);
}
return read_success;
}
@@ -134,7 +142,11 @@ static bool nfc_worker_read_mf_classic(NfcWorker* nfc_worker, FuriHalNfcTxRxCont
furi_assert(nfc_worker->callback);
bool read_success = false;
nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, tx_rx, false);
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
reader_analyzer_prepare_tx_rx(nfc_worker->reader_analyzer, tx_rx, false);
reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog);
}
do {
// Try to read supported card
FURI_LOG_I(TAG, "Try read supported card ...");
@@ -162,6 +174,9 @@ static bool nfc_worker_read_mf_classic(NfcWorker* nfc_worker, FuriHalNfcTxRxCont
}
} while(false);
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
reader_analyzer_stop(nfc_worker->reader_analyzer);
}
return read_success;
}
@@ -169,13 +184,21 @@ static bool nfc_worker_read_mf_desfire(NfcWorker* nfc_worker, FuriHalNfcTxRxCont
bool read_success = false;
MifareDesfireData* data = &nfc_worker->dev_data->mf_df_data;
nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, tx_rx, false);
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
reader_analyzer_prepare_tx_rx(nfc_worker->reader_analyzer, tx_rx, false);
reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog);
}
do {
if(!furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 300)) break;
if(!mf_df_read_card(tx_rx, data)) break;
read_success = true;
} while(false);
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
reader_analyzer_stop(nfc_worker->reader_analyzer);
}
return read_success;
}
@@ -184,7 +207,11 @@ static bool nfc_worker_read_bank_card(NfcWorker* nfc_worker, FuriHalNfcTxRxConte
EmvApplication emv_app = {};
EmvData* result = &nfc_worker->dev_data->emv_data;
nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, tx_rx, false);
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
reader_analyzer_prepare_tx_rx(nfc_worker->reader_analyzer, tx_rx, false);
reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog);
}
do {
// Read card
if(!furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 300)) break;
@@ -211,6 +238,10 @@ static bool nfc_worker_read_bank_card(NfcWorker* nfc_worker, FuriHalNfcTxRxConte
read_success = true;
} while(false);
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
reader_analyzer_stop(nfc_worker->reader_analyzer);
}
return read_success;
}
@@ -320,18 +351,14 @@ void nfc_worker_read(NfcWorker* nfc_worker) {
void nfc_worker_emulate_uid(NfcWorker* nfc_worker) {
FuriHalNfcTxRxContext tx_rx = {};
nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, &tx_rx, true);
FuriHalNfcDevData* data = &nfc_worker->dev_data->nfc_data;
NfcReaderRequestData* reader_data = &nfc_worker->dev_data->reader_data;
// TODO add support for RATS
// Now remove bit 6 in SAK to support ISO-14443A-3 emulation
// Need to save ATS to support ISO-14443A-4 emulation
uint8_t sak = data->sak;
FURI_BIT_CLEAR(sak, 5);
while(nfc_worker->state == NfcWorkerStateUidEmulate) {
if(furi_hal_nfc_listen(data->uid, data->uid_len, data->atqa, sak, true, 100)) {
if(furi_hal_nfc_listen(data->uid, data->uid_len, data->atqa, data->sak, false, 100)) {
if(furi_hal_nfc_tx_rx(&tx_rx, 100)) {
reader_data->size = tx_rx.rx_bits / 8;
if(reader_data->size > 0) {
@@ -349,7 +376,6 @@ void nfc_worker_emulate_uid(NfcWorker* nfc_worker) {
void nfc_worker_emulate_apdu(NfcWorker* nfc_worker) {
FuriHalNfcTxRxContext tx_rx = {};
nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, &tx_rx, true);
FuriHalNfcDevData params = {
.uid = {0xCF, 0x72, 0xd4, 0x40},
.uid_len = 4,
@@ -358,6 +384,11 @@ void nfc_worker_emulate_apdu(NfcWorker* nfc_worker) {
.type = FuriHalNfcTypeA,
};
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
reader_analyzer_prepare_tx_rx(nfc_worker->reader_analyzer, &tx_rx, true);
reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog);
}
while(nfc_worker->state == NfcWorkerStateEmulateApdu) {
if(furi_hal_nfc_listen(params.uid, params.uid_len, params.atqa, params.sak, false, 300)) {
FURI_LOG_D(TAG, "POS terminal detected");
@@ -370,6 +401,10 @@ void nfc_worker_emulate_apdu(NfcWorker* nfc_worker) {
furi_hal_nfc_sleep();
furi_delay_ms(20);
}
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
reader_analyzer_stop(nfc_worker->reader_analyzer);
}
}
void nfc_worker_emulate_mf_ultralight(NfcWorker* nfc_worker) {
@@ -484,7 +519,6 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) {
void nfc_worker_emulate_mf_classic(NfcWorker* nfc_worker) {
FuriHalNfcTxRxContext tx_rx = {};
nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, &tx_rx, true);
FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data;
MfClassicEmulator emulator = {
.cuid = nfc_util_bytes2num(&nfc_data->uid[nfc_data->uid_len - 4], 4),
@@ -525,6 +559,11 @@ void nfc_worker_mf_ultralight_read_auth(NfcWorker* nfc_worker) {
MfUltralightReader reader = {};
mf_ul_reset(data);
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
reader_analyzer_prepare_tx_rx(nfc_worker->reader_analyzer, &tx_rx, true);
reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog);
}
uint32_t key = 0;
uint16_t pack = 0;
while(nfc_worker->state == NfcWorkerStateReadMfUltralightReadAuth) {
@@ -577,4 +616,61 @@ void nfc_worker_mf_ultralight_read_auth(NfcWorker* nfc_worker) {
furi_delay_ms(10);
}
}
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
reader_analyzer_stop(nfc_worker->reader_analyzer);
}
}
static void nfc_worker_reader_analyzer_callback(ReaderAnalyzerEvent event, void* context) {
furi_assert(context);
NfcWorker* nfc_worker = context;
if(event == ReaderAnalyzerEventMfkeyCollected) {
if(nfc_worker->callback) {
nfc_worker->callback(NfcWorkerEventDetectReaderMfkeyCollected, nfc_worker->context);
}
}
}
void nfc_worker_analyze_reader(NfcWorker* nfc_worker) {
FuriHalNfcTxRxContext tx_rx = {};
ReaderAnalyzer* reader_analyzer = nfc_worker->reader_analyzer;
FuriHalNfcDevData* nfc_data = reader_analyzer_get_nfc_data(reader_analyzer);
MfClassicEmulator emulator = {
.cuid = nfc_util_bytes2num(&nfc_data->uid[nfc_data->uid_len - 4], 4),
.data = nfc_worker->dev_data->mf_classic_data,
.data_changed = false,
};
NfcaSignal* nfca_signal = nfca_signal_alloc();
tx_rx.nfca_signal = nfca_signal;
reader_analyzer_prepare_tx_rx(reader_analyzer, &tx_rx, true);
reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeMfkey);
reader_analyzer_set_callback(reader_analyzer, nfc_worker_reader_analyzer_callback, nfc_worker);
rfal_platform_spi_acquire();
FURI_LOG_D(TAG, "Start reader analyzer");
while(nfc_worker->state == NfcWorkerStateAnalyzeReader) {
furi_hal_nfc_stop_cmd();
furi_delay_ms(5);
furi_hal_nfc_listen_start(nfc_data);
if(furi_hal_nfc_listen_rx(&tx_rx, 300)) {
NfcProtocol protocol =
reader_analyzer_guess_protocol(reader_analyzer, tx_rx.rx_data, tx_rx.rx_bits / 8);
if(protocol == NfcDeviceProtocolMifareClassic) {
mf_classic_emulator(&emulator, &tx_rx);
}
} else {
FURI_LOG_D(TAG, "No data from reader");
continue;
}
}
rfal_platform_spi_release();
reader_analyzer_stop(nfc_worker->reader_analyzer);
nfca_signal_free(nfca_signal);
}

View File

@@ -16,6 +16,7 @@ typedef enum {
NfcWorkerStateMfClassicEmulate,
NfcWorkerStateReadMfUltralightReadAuth,
NfcWorkerStateMfClassicDictAttack,
NfcWorkerStateAnalyzeReader,
// Debug
NfcWorkerStateEmulateApdu,
NfcWorkerStateField,
@@ -54,8 +55,12 @@ typedef enum {
NfcWorkerEventFoundKeyA,
NfcWorkerEventFoundKeyB,
// Detect Reader events
NfcWorkerEventDetectReaderMfkeyCollected,
// Mifare Ultralight events
NfcWorkerEventMfUltralightPassKey,
} NfcWorkerEvent;
typedef bool (*NfcWorkerCallback)(NfcWorkerEvent event, void* context);

View File

@@ -12,8 +12,7 @@
#include <lib/nfc/protocols/mifare_classic.h>
#include <lib/nfc/protocols/mifare_desfire.h>
#include <lib/nfc/protocols/nfca.h>
#include "helpers/nfc_debug_pcap.h"
#include <lib/nfc/helpers/reader_analyzer.h>
struct NfcWorker {
FuriThread* thread;
@@ -27,7 +26,7 @@ struct NfcWorker {
NfcWorkerState state;
NfcDebugPcapWorker* debug_pcap_worker;
ReaderAnalyzer* reader_analyzer;
};
void nfc_worker_change_state(NfcWorker* nfc_worker, NfcWorkerState state);
@@ -49,3 +48,5 @@ void nfc_worker_mf_ultralight_read_auth(NfcWorker* nfc_worker);
void nfc_worker_mf_ul_auth_attack(NfcWorker* nfc_worker);
void nfc_worker_emulate_apdu(NfcWorker* nfc_worker);
void nfc_worker_analyze_reader(NfcWorker* nfc_worker);