[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);
|
@@ -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);
|
||||
}
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
|
Reference in New Issue
Block a user