mirror of
https://github.com/DarkFlippers/unleashed-firmware.git
synced 2025-12-12 04:34:43 +04:00
nfc mf classic upgrades
by noproto
This commit is contained in:
@@ -113,6 +113,9 @@ typedef struct {
|
||||
uint16_t nested_target_key;
|
||||
uint16_t msb_count;
|
||||
bool enhanced_dict;
|
||||
uint16_t current_key_idx; // Current key index for CUID dictionary mode
|
||||
uint8_t*
|
||||
cuid_key_indices_bitmap; // Bitmap of key indices present in CUID dictionary (256 bits = 32 bytes)
|
||||
} NfcMfClassicDictAttackContext;
|
||||
|
||||
typedef struct {
|
||||
|
||||
@@ -2,12 +2,22 @@
|
||||
|
||||
#include <bit_lib/bit_lib.h>
|
||||
#include <dolphin/dolphin.h>
|
||||
#include <toolbox/stream/buffered_file_stream.h>
|
||||
|
||||
#define TAG "NfcMfClassicDictAttack"
|
||||
#define TAG "NfcMfClassicDictAttack"
|
||||
#define BIT(x, n) ((x) >> (n) & 1)
|
||||
|
||||
// TODO FL-3926: Fix lag when leaving the dictionary attack view after Hardnested
|
||||
// TODO FL-3926: Re-enters backdoor detection between user and system dictionary if no backdoor is found
|
||||
|
||||
// KeysDict structure definition for inline CUID dictionary allocation
|
||||
struct KeysDict {
|
||||
Stream* stream;
|
||||
size_t key_size;
|
||||
size_t key_size_symbols;
|
||||
size_t total_keys;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
DictAttackStateCUIDDictInProgress,
|
||||
DictAttackStateUserDictInProgress,
|
||||
@@ -31,11 +41,22 @@ NfcCommand nfc_dict_attack_worker_callback(NfcGenericEvent event, void* context)
|
||||
instance->nfc_dict_context.is_card_present = false;
|
||||
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventCardLost);
|
||||
} else if(mfc_event->type == MfClassicPollerEventTypeRequestMode) {
|
||||
uint32_t state =
|
||||
scene_manager_get_scene_state(instance->scene_manager, NfcSceneMfClassicDictAttack);
|
||||
bool is_cuid_dict = (state == DictAttackStateCUIDDictInProgress);
|
||||
|
||||
const MfClassicData* mfc_data =
|
||||
nfc_device_get_data(instance->nfc_device, NfcProtocolMfClassic);
|
||||
mfc_event->data->poller_mode.mode = (instance->nfc_dict_context.enhanced_dict) ?
|
||||
MfClassicPollerModeDictAttackEnhanced :
|
||||
MfClassicPollerModeDictAttackStandard;
|
||||
|
||||
// Select mode based on dictionary type
|
||||
if(is_cuid_dict) {
|
||||
mfc_event->data->poller_mode.mode = MfClassicPollerModeDictAttackCUID;
|
||||
} else if(instance->nfc_dict_context.enhanced_dict) {
|
||||
mfc_event->data->poller_mode.mode = MfClassicPollerModeDictAttackEnhanced;
|
||||
} else {
|
||||
mfc_event->data->poller_mode.mode = MfClassicPollerModeDictAttackStandard;
|
||||
}
|
||||
|
||||
mfc_event->data->poller_mode.data = mfc_data;
|
||||
instance->nfc_dict_context.sectors_total =
|
||||
mf_classic_get_total_sectors_num(mfc_data->type);
|
||||
@@ -46,12 +67,57 @@ NfcCommand nfc_dict_attack_worker_callback(NfcGenericEvent event, void* context)
|
||||
view_dispatcher_send_custom_event(
|
||||
instance->view_dispatcher, NfcCustomEventDictAttackDataUpdate);
|
||||
} else if(mfc_event->type == MfClassicPollerEventTypeRequestKey) {
|
||||
uint32_t state =
|
||||
scene_manager_get_scene_state(instance->scene_manager, NfcSceneMfClassicDictAttack);
|
||||
bool is_cuid_dict = (state == DictAttackStateCUIDDictInProgress);
|
||||
|
||||
MfClassicKey key = {};
|
||||
if(keys_dict_get_next_key(
|
||||
instance->nfc_dict_context.dict, key.data, sizeof(MfClassicKey))) {
|
||||
bool key_found = false;
|
||||
|
||||
if(is_cuid_dict) {
|
||||
// CUID dictionary: read 7 bytes (1 byte key_idx + 6 bytes key) and filter by exact key_idx
|
||||
uint16_t target_key_idx = instance->nfc_dict_context.current_key_idx;
|
||||
|
||||
// Check if this key index exists in the bitmap (only valid for 0-255)
|
||||
if(target_key_idx < 256 &&
|
||||
BIT(instance->nfc_dict_context.cuid_key_indices_bitmap[target_key_idx / 8],
|
||||
target_key_idx % 8)) {
|
||||
uint8_t key_with_idx[sizeof(MfClassicKey) + 1];
|
||||
|
||||
while(keys_dict_get_next_key(
|
||||
instance->nfc_dict_context.dict, key_with_idx, sizeof(MfClassicKey) + 1)) {
|
||||
// Extract key_idx from first byte
|
||||
uint8_t key_idx = key_with_idx[0];
|
||||
|
||||
instance->nfc_dict_context.dict_keys_current++;
|
||||
|
||||
// Only use key if it matches the exact current key index
|
||||
if(key_idx == (uint8_t)target_key_idx) {
|
||||
// Copy the actual key (starts at byte 1)
|
||||
memcpy(key.data, &key_with_idx[1], sizeof(MfClassicKey));
|
||||
key_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Standard dictionary: read 12 bytes
|
||||
if(keys_dict_get_next_key(
|
||||
instance->nfc_dict_context.dict, key.data, sizeof(MfClassicKey))) {
|
||||
key_found = true;
|
||||
instance->nfc_dict_context.dict_keys_current++;
|
||||
}
|
||||
}
|
||||
|
||||
if(key_found) {
|
||||
mfc_event->data->key_request_data.key = key;
|
||||
// In CUID mode, set key_type based on key_idx (odd = B, even = A)
|
||||
if(is_cuid_dict) {
|
||||
uint16_t target_key_idx = instance->nfc_dict_context.current_key_idx;
|
||||
mfc_event->data->key_request_data.key_type =
|
||||
(target_key_idx % 2 == 0) ? MfClassicKeyTypeA : MfClassicKeyTypeB;
|
||||
}
|
||||
mfc_event->data->key_request_data.key_provided = true;
|
||||
instance->nfc_dict_context.dict_keys_current++;
|
||||
if(instance->nfc_dict_context.dict_keys_current % 10 == 0) {
|
||||
view_dispatcher_send_custom_event(
|
||||
instance->view_dispatcher, NfcCustomEventDictAttackDataUpdate);
|
||||
@@ -72,10 +138,27 @@ NfcCommand nfc_dict_attack_worker_callback(NfcGenericEvent event, void* context)
|
||||
view_dispatcher_send_custom_event(
|
||||
instance->view_dispatcher, NfcCustomEventDictAttackDataUpdate);
|
||||
} else if(mfc_event->type == MfClassicPollerEventTypeNextSector) {
|
||||
uint32_t state =
|
||||
scene_manager_get_scene_state(instance->scene_manager, NfcSceneMfClassicDictAttack);
|
||||
bool is_cuid_dict = (state == DictAttackStateCUIDDictInProgress);
|
||||
|
||||
keys_dict_rewind(instance->nfc_dict_context.dict);
|
||||
instance->nfc_dict_context.dict_keys_current = 0;
|
||||
instance->nfc_dict_context.current_sector =
|
||||
mfc_event->data->next_sector_data.current_sector;
|
||||
|
||||
// In CUID mode, increment the key index and calculate sector from it
|
||||
if(is_cuid_dict) {
|
||||
instance->nfc_dict_context.current_key_idx++;
|
||||
// Calculate sector from key_idx (each sector has 2 keys: A and B)
|
||||
instance->nfc_dict_context.current_sector =
|
||||
instance->nfc_dict_context.current_key_idx / 2;
|
||||
// Write back to event data so poller can read it
|
||||
mfc_event->data->next_sector_data.current_sector =
|
||||
instance->nfc_dict_context.current_sector;
|
||||
} else {
|
||||
instance->nfc_dict_context.current_sector =
|
||||
mfc_event->data->next_sector_data.current_sector;
|
||||
}
|
||||
|
||||
view_dispatcher_send_custom_event(
|
||||
instance->view_dispatcher, NfcCustomEventDictAttackDataUpdate);
|
||||
} else if(mfc_event->type == MfClassicPollerEventTypeFoundKeyA) {
|
||||
@@ -153,18 +236,51 @@ static void nfc_scene_mf_classic_dict_attack_prepare_view(NfcApp* instance) {
|
||||
break;
|
||||
}
|
||||
|
||||
instance->nfc_dict_context.dict = keys_dict_alloc(
|
||||
furi_string_get_cstr(cuid_dict_path),
|
||||
KeysDictModeOpenExisting,
|
||||
sizeof(MfClassicKey));
|
||||
// Manually create KeysDict and scan once to count + populate bitmap
|
||||
KeysDict* dict = malloc(sizeof(KeysDict));
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
dict->stream = buffered_file_stream_alloc(storage);
|
||||
dict->key_size = sizeof(MfClassicKey) + 1;
|
||||
dict->key_size_symbols = dict->key_size * 2 + 1;
|
||||
dict->total_keys = 0;
|
||||
|
||||
if(keys_dict_get_total_keys(instance->nfc_dict_context.dict) == 0) {
|
||||
keys_dict_free(instance->nfc_dict_context.dict);
|
||||
if(!buffered_file_stream_open(
|
||||
dict->stream,
|
||||
furi_string_get_cstr(cuid_dict_path),
|
||||
FSAM_READ_WRITE,
|
||||
FSOM_OPEN_EXISTING)) {
|
||||
buffered_file_stream_close(dict->stream);
|
||||
free(dict);
|
||||
state = DictAttackStateUserDictInProgress;
|
||||
break;
|
||||
}
|
||||
|
||||
// Allocate and populate bitmap of key indices present in CUID dictionary
|
||||
instance->nfc_dict_context.cuid_key_indices_bitmap = malloc(32);
|
||||
memset(instance->nfc_dict_context.cuid_key_indices_bitmap, 0, 32);
|
||||
|
||||
// Scan dictionary once to count keys and populate bitmap
|
||||
uint8_t key_with_idx[dict->key_size];
|
||||
while(keys_dict_get_next_key(dict, key_with_idx, dict->key_size)) {
|
||||
uint8_t key_idx = key_with_idx[0];
|
||||
// Set bit for this key index
|
||||
instance->nfc_dict_context.cuid_key_indices_bitmap[key_idx / 8] |=
|
||||
(1 << (key_idx % 8));
|
||||
dict->total_keys++;
|
||||
}
|
||||
keys_dict_rewind(dict);
|
||||
|
||||
if(dict->total_keys == 0) {
|
||||
keys_dict_free(dict);
|
||||
free(instance->nfc_dict_context.cuid_key_indices_bitmap);
|
||||
instance->nfc_dict_context.cuid_key_indices_bitmap = NULL;
|
||||
state = DictAttackStateUserDictInProgress;
|
||||
break;
|
||||
}
|
||||
|
||||
instance->nfc_dict_context.dict = dict;
|
||||
dict_attack_set_header(instance->dict_attack, "MF Classic CUID Dictionary");
|
||||
instance->nfc_dict_context.current_key_idx = 0; // Initialize key index for CUID mode
|
||||
} while(false);
|
||||
|
||||
furi_string_free(cuid_dict_path);
|
||||
@@ -265,6 +381,10 @@ bool nfc_scene_mf_classic_dict_attack_on_event(void* context, SceneManagerEvent
|
||||
nfc_poller_stop(instance->poller);
|
||||
nfc_poller_free(instance->poller);
|
||||
keys_dict_free(instance->nfc_dict_context.dict);
|
||||
if(instance->nfc_dict_context.cuid_key_indices_bitmap) {
|
||||
free(instance->nfc_dict_context.cuid_key_indices_bitmap);
|
||||
instance->nfc_dict_context.cuid_key_indices_bitmap = NULL;
|
||||
}
|
||||
scene_manager_set_scene_state(
|
||||
instance->scene_manager,
|
||||
NfcSceneMfClassicDictAttack,
|
||||
@@ -309,6 +429,10 @@ bool nfc_scene_mf_classic_dict_attack_on_event(void* context, SceneManagerEvent
|
||||
nfc_poller_stop(instance->poller);
|
||||
nfc_poller_free(instance->poller);
|
||||
keys_dict_free(instance->nfc_dict_context.dict);
|
||||
if(instance->nfc_dict_context.cuid_key_indices_bitmap) {
|
||||
free(instance->nfc_dict_context.cuid_key_indices_bitmap);
|
||||
instance->nfc_dict_context.cuid_key_indices_bitmap = NULL;
|
||||
}
|
||||
scene_manager_set_scene_state(
|
||||
instance->scene_manager,
|
||||
NfcSceneMfClassicDictAttack,
|
||||
@@ -366,6 +490,12 @@ void nfc_scene_mf_classic_dict_attack_on_exit(void* context) {
|
||||
|
||||
keys_dict_free(instance->nfc_dict_context.dict);
|
||||
|
||||
// Free CUID bitmap if allocated
|
||||
if(instance->nfc_dict_context.cuid_key_indices_bitmap) {
|
||||
free(instance->nfc_dict_context.cuid_key_indices_bitmap);
|
||||
instance->nfc_dict_context.cuid_key_indices_bitmap = NULL;
|
||||
}
|
||||
|
||||
instance->nfc_dict_context.current_sector = 0;
|
||||
instance->nfc_dict_context.sectors_total = 0;
|
||||
instance->nfc_dict_context.sectors_read = 0;
|
||||
@@ -381,6 +511,7 @@ void nfc_scene_mf_classic_dict_attack_on_exit(void* context) {
|
||||
instance->nfc_dict_context.nested_target_key = 0;
|
||||
instance->nfc_dict_context.msb_count = 0;
|
||||
instance->nfc_dict_context.enhanced_dict = false;
|
||||
instance->nfc_dict_context.current_key_idx = 0;
|
||||
|
||||
// Clean up temporary files used for nested dictionary attack
|
||||
if(keys_dict_check_presence(NFC_APP_MF_CLASSIC_DICT_USER_NESTED_PATH)) {
|
||||
|
||||
Reference in New Issue
Block a user