nfc mf classic upgrades

by noproto
This commit is contained in:
MX
2025-12-11 00:24:50 +03:00
parent caad1ef268
commit 0a7eb30a15
10 changed files with 240 additions and 32 deletions

View File

@@ -163,11 +163,14 @@ NfcCommand mf_classic_poller_handler_start(MfClassicPoller* instance) {
instance->mfc_event.type = MfClassicPollerEventTypeRequestMode;
command = instance->callback(instance->general_event, instance->context);
if(instance->mfc_event_data.poller_mode.mode == MfClassicPollerModeDictAttackStandard) {
if(instance->mfc_event_data.poller_mode.mode == MfClassicPollerModeDictAttackStandard ||
instance->mfc_event_data.poller_mode.mode == MfClassicPollerModeDictAttackCUID) {
mf_classic_copy(instance->data, instance->mfc_event_data.poller_mode.data);
instance->mode_ctx.dict_attack_ctx.mode = instance->mfc_event_data.poller_mode.mode;
instance->state = MfClassicPollerStateRequestKey;
} else if(instance->mfc_event_data.poller_mode.mode == MfClassicPollerModeDictAttackEnhanced) {
mf_classic_copy(instance->data, instance->mfc_event_data.poller_mode.data);
instance->mode_ctx.dict_attack_ctx.mode = instance->mfc_event_data.poller_mode.mode;
instance->state = MfClassicPollerStateAnalyzeBackdoor;
} else if(instance->mfc_event_data.poller_mode.mode == MfClassicPollerModeRead) {
instance->state = MfClassicPollerStateRequestReadSector;
@@ -590,7 +593,22 @@ NfcCommand mf_classic_poller_handler_analyze_backdoor(MfClassicPoller* instance)
(error == MfClassicErrorProtocol || error == MfClassicErrorTimeout)) {
FURI_LOG_D(TAG, "No backdoor identified");
dict_attack_ctx->backdoor = MfClassicBackdoorNone;
instance->state = MfClassicPollerStateRequestKey;
// Check if any keys were cached - if so, go directly to nested attack
bool has_cached_keys = false;
for(uint8_t sector = 0; sector < instance->sectors_total; sector++) {
if(mf_classic_is_key_found(instance->data, sector, MfClassicKeyTypeA) ||
mf_classic_is_key_found(instance->data, sector, MfClassicKeyTypeB)) {
has_cached_keys = true;
break;
}
}
if(has_cached_keys) {
instance->state = MfClassicPollerStateNestedController;
} else {
instance->state = MfClassicPollerStateRequestKey;
}
} else if(error == MfClassicErrorNone) {
FURI_LOG_I(TAG, "Backdoor identified: v%d", backdoor_version);
dict_attack_ctx->backdoor = mf_classic_backdoor_keys[next_key_index].type;
@@ -687,7 +705,15 @@ NfcCommand mf_classic_poller_handler_request_key(MfClassicPoller* instance) {
command = instance->callback(instance->general_event, instance->context);
if(instance->mfc_event_data.key_request_data.key_provided) {
dict_attack_ctx->current_key = instance->mfc_event_data.key_request_data.key;
instance->state = MfClassicPollerStateAuthKeyA;
dict_attack_ctx->requested_key_type = instance->mfc_event_data.key_request_data.key_type;
// In CUID mode, go directly to the appropriate Auth state based on key_type
if(dict_attack_ctx->mode == MfClassicPollerModeDictAttackCUID &&
dict_attack_ctx->requested_key_type == MfClassicKeyTypeB) {
instance->state = MfClassicPollerStateAuthKeyB;
} else {
instance->state = MfClassicPollerStateAuthKeyA;
}
} else {
instance->state = MfClassicPollerStateNextSector;
}
@@ -701,7 +727,12 @@ NfcCommand mf_classic_poller_handler_auth_a(MfClassicPoller* instance) {
if(mf_classic_is_key_found(
instance->data, dict_attack_ctx->current_sector, MfClassicKeyTypeA)) {
instance->state = MfClassicPollerStateAuthKeyB;
// In CUID mode, skip directly to RequestKey since we test keys by specific type
if(dict_attack_ctx->mode == MfClassicPollerModeDictAttackCUID) {
instance->state = MfClassicPollerStateRequestKey;
} else {
instance->state = MfClassicPollerStateAuthKeyB;
}
} else {
uint8_t block = mf_classic_get_first_block_num_of_sector(dict_attack_ctx->current_sector);
uint64_t key =
@@ -722,7 +753,12 @@ NfcCommand mf_classic_poller_handler_auth_a(MfClassicPoller* instance) {
instance->state = MfClassicPollerStateReadSector;
} else {
mf_classic_poller_halt(instance);
instance->state = MfClassicPollerStateAuthKeyB;
// In CUID mode, skip directly to RequestKey since we test keys by specific type
if(dict_attack_ctx->mode == MfClassicPollerModeDictAttackCUID) {
instance->state = MfClassicPollerStateRequestKey;
} else {
instance->state = MfClassicPollerStateAuthKeyB;
}
}
}
@@ -735,8 +771,11 @@ NfcCommand mf_classic_poller_handler_auth_b(MfClassicPoller* instance) {
if(mf_classic_is_key_found(
instance->data, dict_attack_ctx->current_sector, MfClassicKeyTypeB)) {
if(mf_classic_is_key_found(
instance->data, dict_attack_ctx->current_sector, MfClassicKeyTypeA)) {
// In CUID mode, just request next key since we iterate by key_idx
if(dict_attack_ctx->mode == MfClassicPollerModeDictAttackCUID) {
instance->state = MfClassicPollerStateRequestKey;
} else if(mf_classic_is_key_found(
instance->data, dict_attack_ctx->current_sector, MfClassicKeyTypeA)) {
instance->state = MfClassicPollerStateNextSector;
} else {
instance->state = MfClassicPollerStateRequestKey;
@@ -774,12 +813,20 @@ NfcCommand mf_classic_poller_handler_next_sector(MfClassicPoller* instance) {
MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx;
dict_attack_ctx->current_sector++;
if(dict_attack_ctx->current_sector == instance->sectors_total) {
instance->state = MfClassicPollerStateSuccess;
} else {
instance->mfc_event.type = MfClassicPollerEventTypeNextSector;
instance->mfc_event_data.next_sector_data.current_sector = dict_attack_ctx->current_sector;
command = instance->callback(instance->general_event, instance->context);
// In CUID mode, NFC app manages sector based on key_idx - read it back
if(dict_attack_ctx->mode == MfClassicPollerModeDictAttackCUID) {
dict_attack_ctx->current_sector =
instance->mfc_event_data.next_sector_data.current_sector;
}
instance->state = MfClassicPollerStateRequestKey;
}

View File

@@ -45,6 +45,7 @@ typedef enum {
MfClassicPollerModeRead, /**< Poller reading mode. */
MfClassicPollerModeWrite, /**< Poller writing mode. */
MfClassicPollerModeDictAttackStandard, /**< Poller dictionary attack mode. */
MfClassicPollerModeDictAttackCUID, /**< Poller CUID dictionary attack mode. */
MfClassicPollerModeDictAttackEnhanced, /**< Poller enhanced dictionary attack mode. */
} MfClassicPollerMode;
@@ -129,6 +130,7 @@ typedef struct {
*/
typedef struct {
MfClassicKey key; /**< Key to be used by poller. */
MfClassicKeyType key_type; /**< Key type (A or B) for CUID dict attack mode. */
bool key_provided; /**< Flag indicating if key is provided. */
} MfClassicPollerEventDataKeyRequest;

View File

@@ -128,10 +128,12 @@ typedef struct {
uint8_t current_sector;
MfClassicKey current_key;
MfClassicKeyType current_key_type;
MfClassicKeyType requested_key_type; // Key type requested from app (for CUID mode)
bool auth_passed;
uint16_t current_block;
uint8_t reuse_key_sector;
MfClassicBackdoor backdoor;
MfClassicPollerMode mode; // Current attack mode
// Enhanced dictionary attack and nested nonce collection
bool enhanced_dict;
MfClassicNestedPhase nested_phase;