From 470f6a679dc84e27a7fa5e8c2690b93359fe899e Mon Sep 17 00:00:00 2001 From: WillyJL Date: Mon, 22 Sep 2025 20:08:29 +0200 Subject: [PATCH 1/5] CLI: Fix crash with unexpected plugin files (#4243) --- lib/toolbox/cli/cli_registry.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/toolbox/cli/cli_registry.c b/lib/toolbox/cli/cli_registry.c index 91f7c4046..45661ee90 100644 --- a/lib/toolbox/cli/cli_registry.c +++ b/lib/toolbox/cli/cli_registry.c @@ -136,9 +136,9 @@ void cli_registry_reload_external_commands( FURI_LOG_T(TAG, "Plugin: %s", plugin_filename); furi_string_set_str(plugin_name, plugin_filename); - furi_check(furi_string_end_with_str(plugin_name, ".fal")); + if(!furi_string_end_with_str(plugin_name, ".fal")) continue; furi_string_replace_all_str(plugin_name, ".fal", ""); - furi_check(furi_string_start_with_str(plugin_name, config->fal_prefix)); + if(!furi_string_start_with_str(plugin_name, config->fal_prefix)) continue; furi_string_replace_at(plugin_name, 0, strlen(config->fal_prefix), ""); CliRegistryCommand command = { From 5932dbe70a440bdeef6b1bd40004602dabfcac7b Mon Sep 17 00:00:00 2001 From: Amy Grace Date: Mon, 22 Sep 2025 12:27:59 -0600 Subject: [PATCH 2/5] Add an old JVC model to universal remotes (#4274) Co-authored-by: hedger --- .../resources/infrared/assets/audio.ir | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/applications/main/infrared/resources/infrared/assets/audio.ir b/applications/main/infrared/resources/infrared/assets/audio.ir index efd0382e3..4180ad8b1 100644 --- a/applications/main/infrared/resources/infrared/assets/audio.ir +++ b/applications/main/infrared/resources/infrared/assets/audio.ir @@ -4689,3 +4689,35 @@ protocol: NECext address: 87 7C 00 00 command: 1D E2 00 00 # +# Model: JVC RM-SRVNB1A for JVC RV-NB1 boombox (2004) +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.333333 +data: 8400 4200 526 1578 526 1578 526 1578 526 1578 526 1578 526 526 526 526 526 1578 526 526 526 526 526 526 526 526 526 526 526 526 526 526 526 526 526 25248 526 1578 526 1578 526 1578 526 1578 526 1578 526 526 526 526 526 1578 526 526 526 526 526 526 526 526 526 526 526 526 526 526 526 526 526 33664 +# +name: Vol_up +type: raw +frequency: 38000 +duty_cycle: 0.333333 +data: 8400 4200 526 1578 526 1578 526 1578 526 1578 526 1578 526 526 526 526 526 1578 526 1578 526 526 526 526 526 526 526 526 526 526 526 526 526 526 526 25248 526 1578 526 1578 526 1578 526 1578 526 1578 526 526 526 526 526 1578 526 1578 526 526 526 526 526 526 526 526 526 526 526 526 526 526 526 33664 +# +name: Vol_dn +type: raw +frequency: 38000 +duty_cycle: 0.333333 +data: 8400 4200 526 1578 526 1578 526 1578 526 1578 526 1578 526 526 526 526 526 1578 526 526 526 1578 526 526 526 526 526 526 526 526 526 526 526 526 526 25248 526 1578 526 1578 526 1578 526 1578 526 1578 526 526 526 526 526 1578 526 526 526 1578 526 526 526 526 526 526 526 526 526 526 526 526 526 33664 +# +name: Next +type: raw +frequency: 38000 +duty_cycle: 0.333333 +data: 8400 4200 526 1578 526 1578 526 1578 526 1578 526 1578 526 526 526 526 526 1578 526 1578 526 526 526 1578 526 1578 526 1578 526 1578 526 526 526 526 526 25248 526 1578 526 1578 526 1578 526 1578 526 1578 526 526 526 526 526 1578 526 1578 526 526 526 1578 526 1578 526 1578 526 1578 526 526 526 526 526 33664 +# +name: Prev +type: raw +frequency: 38000 +duty_cycle: 0.333333 +data: 8400 4200 526 1578 526 1578 526 1578 526 1578 526 1578 526 526 526 526 526 1578 526 526 526 1578 526 1578 526 1578 526 1578 526 1578 526 526 526 526 526 25248 526 1578 526 1578 526 1578 526 1578 526 1578 526 526 526 526 526 1578 526 526 526 1578 526 1578 526 1578 526 1578 526 1578 526 526 526 526 526 33664 +# From d19ad60c1a6521d26ded1edbb9f2f43b8b4b9b98 Mon Sep 17 00:00:00 2001 From: Zinong Li <131403964+zinongli@users.noreply.github.com> Date: Tue, 23 Sep 2025 17:02:45 +0400 Subject: [PATCH 3/5] GUI Bug Fix: Number Input Save Icon (#4273) * Update KeySaveBlockedSelected_24x11.png * assets: formatted images --------- Co-authored-by: hedger Co-authored-by: hedger --- .../Keyboard/KeySaveBlockedSelected_24x11.png | Bin 110 -> 96 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/assets/icons/Keyboard/KeySaveBlockedSelected_24x11.png b/assets/icons/Keyboard/KeySaveBlockedSelected_24x11.png index fe570a10c3790ca7702dad17af23aea531a7b5bd..477cdcced66dfa1cfea12202b99fd9a1385f003e 100644 GIT binary patch delta 65 zcmc~xn4n^#?&;zfQZXkvBOxKBfzhswY1#R9Rtbq52?FVdQ&MBb@0QfZ)`~Uy| delta 79 zcmYeuo1o%l>gnPbQZXla#sB~PtrI6s>}+go?Cf9I@bYsaePit1jW! h5ramM Date: Tue, 23 Sep 2025 15:43:45 +0200 Subject: [PATCH 4/5] NFC: Fix read crash with unexpectedly large MFC AUTH(0) response (#4265) This was noticeable with Chameleon Ultra NTAG emulation Co-authored-by: hedger --- lib/nfc/protocols/iso14443_3a/iso14443_3a_poller_i.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/nfc/protocols/iso14443_3a/iso14443_3a_poller_i.c b/lib/nfc/protocols/iso14443_3a/iso14443_3a_poller_i.c index 2cf66edba..5795c1bfa 100644 --- a/lib/nfc/protocols/iso14443_3a/iso14443_3a_poller_i.c +++ b/lib/nfc/protocols/iso14443_3a/iso14443_3a_poller_i.c @@ -42,7 +42,13 @@ static Iso14443_3aError iso14443_3a_poller_standard_frame_exchange( break; } + if(bit_buffer_get_capacity_bytes(rx_buffer) < + bit_buffer_get_size_bytes(instance->rx_buffer)) { + ret = Iso14443_3aErrorBufferOverflow; + break; + } bit_buffer_copy(rx_buffer, instance->rx_buffer); + if(!iso14443_crc_check(Iso14443CrcTypeA, instance->rx_buffer)) { ret = Iso14443_3aErrorWrongCrc; break; From f4138e59998cf6911b93e7766c2cad57e40edd76 Mon Sep 17 00:00:00 2001 From: Zinong Li <131403964+zinongli@users.noreply.github.com> Date: Wed, 24 Sep 2025 00:47:22 +0400 Subject: [PATCH 5/5] NFC: MFC 1k Banapass Parser (#4260) * seems to be working * faster reading * clone detection * linter: fixes --------- Co-authored-by: hedger Co-authored-by: hedger --- applications/main/nfc/application.fam | 9 + .../nfc/plugins/supported_cards/banapass.c | 284 ++++++++++++++++++ 2 files changed, 293 insertions(+) create mode 100644 applications/main/nfc/plugins/supported_cards/banapass.c diff --git a/applications/main/nfc/application.fam b/applications/main/nfc/application.fam index f645033b2..6342d18bf 100644 --- a/applications/main/nfc/application.fam +++ b/applications/main/nfc/application.fam @@ -265,3 +265,12 @@ App( requires=["cli"], sources=["nfc_cli.c"], ) + +App( + appid="banapass_parser", + apptype=FlipperAppType.PLUGIN, + entry_point="banapass_plugin_ep", + targets=["f7"], + requires=["nfc"], + sources=["plugins/supported_cards/banapass.c"], +) diff --git a/applications/main/nfc/plugins/supported_cards/banapass.c b/applications/main/nfc/plugins/supported_cards/banapass.c new file mode 100644 index 000000000..ac065c3ac --- /dev/null +++ b/applications/main/nfc/plugins/supported_cards/banapass.c @@ -0,0 +1,284 @@ +#include "nfc_supported_card_plugin.h" + +#include + +#include +#include +#include + +#define TAG "Banapass" + +static const uint64_t banapass_key_b_value_block = 0x019761AA8082; +static const uint64_t banapass_key_b_access_code = 0x574343467632; +typedef struct { + uint64_t a; + uint64_t b; +} MfClassicKeyPair; + +static MfClassicKeyPair banapass_keys_if_value_block[] = { + {.a = 0x6090D00632F5, .b = 0x019761AA8082}, + {.a = 0xA99164400748, .b = 0x62742819AD7C}, + {.a = 0xCC5075E42BA1, .b = 0xB9DF35A0814C}, + {.a = 0x8AF9C718F23D, .b = 0x58CD5C3673CB}, + {.a = 0xFC80E88EB88C, .b = 0x7A3CDAD7C023}, + {.a = 0x30424C029001, .b = 0x024E4E44001F}, + {.a = 0xECBBFA57C6AD, .b = 0x4757698143BD}, + {.a = 0x1D30972E6485, .b = 0xF8526D1A8D6D}, + {.a = 0x1300EC8C7E80, .b = 0xF80A65A87FFA}, + {.a = 0xDEB06ED4AF8E, .b = 0x4AD96BF28190}, + {.a = 0x000390014D41, .b = 0x0800F9917CB0}, + {.a = 0x730050555253, .b = 0x4146D4A956C4}, + {.a = 0x131157FBB126, .b = 0xE69DD9015A43}, + {.a = 0x337237F254D5, .b = 0x9A8389F32FBF}, + {.a = 0x7B8FB4A7100B, .b = 0xC8382A233993}, + {.a = 0x7B304F2A12A6, .b = 0xFC9418BF788B}, +}; + +static MfClassicKeyPair banapass_keys_if_access_code[] = { + {.a = 0x6090D00632F5, .b = 0x574343467632}, + {.a = 0xA99164400748, .b = 0x62742819AD7C}, + {.a = 0xCC5075E42BA1, .b = 0xB9DF35A0814C}, + {.a = 0x8AF9C718F23D, .b = 0x58CD5C3673CB}, + {.a = 0xFC80E88EB88C, .b = 0x7A3CDAD7C023}, + {.a = 0x30424C029001, .b = 0x024E4E44001F}, + {.a = 0xECBBFA57C6AD, .b = 0x4757698143BD}, + {.a = 0x1D30972E6485, .b = 0xF8526D1A8D6D}, + {.a = 0x1300EC8C7E80, .b = 0xF80A65A87FFA}, + {.a = 0xDEB06ED4AF8E, .b = 0x4AD96BF28190}, + {.a = 0x000390014D41, .b = 0x0800F9917CB0}, + {.a = 0x730050555253, .b = 0x4146D4A956C4}, + {.a = 0x131157FBB126, .b = 0xE69DD9015A43}, + {.a = 0x337237F254D5, .b = 0x9A8389F32FBF}, + {.a = 0x7B8FB4A7100B, .b = 0xC8382A233993}, + {.a = 0x7B304F2A12A6, .b = 0xFC9418BF788B}, +}; + +static bool banapass_verify(Nfc* nfc) { + bool verified = true; + + const uint8_t verify_sector = 0; + uint8_t block_num = mf_classic_get_first_block_num_of_sector(verify_sector); + FURI_LOG_D(TAG, "Verifying sector %u", verify_sector); + + MfClassicKey key_a_0 = {}; + bit_lib_num_to_bytes_be( + banapass_keys_if_value_block[0].a, COUNT_OF(key_a_0.data), key_a_0.data); + + MfClassicAuthContext auth_ctx = {}; + MfClassicError error = + mf_classic_poller_sync_auth(nfc, block_num, &key_a_0, MfClassicKeyTypeA, &auth_ctx); + + if(error != MfClassicErrorNone) { + FURI_LOG_D(TAG, "Failed to read block %u: %d", block_num, error); + verified = false; + } + + return verified; +} + +static bool banapass_read(Nfc* nfc, NfcDevice* device) { + furi_assert(nfc); + furi_assert(device); + + bool is_read = false; + + MfClassicData* data = mf_classic_alloc(); + nfc_device_copy_data(device, NfcProtocolMfClassic, data); + + do { + MfClassicType type = MfClassicType1k; + MfClassicError error = mf_classic_poller_sync_detect_type(nfc, &type); + if(error != MfClassicErrorNone) break; + if(type != MfClassicType1k) { + FURI_LOG_E(TAG, "Card not MIFARE Classic 1k"); + break; + } + + data->type = type; + MfClassicDeviceKeys keys = {}; + + // Access Code Read Attempt + for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) { + bit_lib_num_to_bytes_be( + banapass_keys_if_access_code[i].a, sizeof(MfClassicKey), keys.key_a[i].data); + FURI_BIT_SET(keys.key_a_mask, i); + bit_lib_num_to_bytes_be( + banapass_keys_if_access_code[i].b, sizeof(MfClassicKey), keys.key_b[i].data); + FURI_BIT_SET(keys.key_b_mask, i); + } + + MfClassicError error_access_code = mf_classic_poller_sync_read(nfc, &keys, data); + + if(error_access_code == MfClassicErrorNone) { + nfc_device_set_data(device, NfcProtocolMfClassic, data); + is_read = true; + break; + } + + // Value Block Read Attempt + for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) { + bit_lib_num_to_bytes_be( + banapass_keys_if_value_block[i].a, sizeof(MfClassicKey), keys.key_a[i].data); + FURI_BIT_SET(keys.key_a_mask, i); + bit_lib_num_to_bytes_be( + banapass_keys_if_value_block[i].b, sizeof(MfClassicKey), keys.key_b[i].data); + FURI_BIT_SET(keys.key_b_mask, i); + } + + MfClassicError error_value_block = mf_classic_poller_sync_read(nfc, &keys, data); + + if(error_value_block == MfClassicErrorNone) { + nfc_device_set_data(device, NfcProtocolMfClassic, data); + is_read = true; + break; + } + FURI_LOG_E(TAG, "Failed to read data. Bad keys?"); + } while(false); + + mf_classic_free(data); + + return is_read; +} + +static bool banapass_parse(const NfcDevice* device, FuriString* parsed_data) { + furi_assert(device); + + const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic); + + bool parsed = false; + + do { + // verify key + MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, 0); + uint64_t key_a = bit_lib_bytes_to_num_be(sec_tr->key_a.data, 6); + uint64_t key_b = bit_lib_bytes_to_num_be(sec_tr->key_b.data, 6); + if(key_a != banapass_keys_if_value_block[0].a) break; + + furi_string_set_str(parsed_data, "\e#Banapass\n"); + furi_string_cat_str( + parsed_data, "::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::"); + furi_string_cat_str(parsed_data, "\nBandai Namco Passport\n"); + + // banapass Magic is stored at block 1, byte 2-7 + uint8_t magic_bytes[6]; + for(int i = 0; i < 6; i++) { + magic_bytes[i] = data->block[1].data[2 + i]; + } + + // verify banapass magic + if(magic_bytes[0] != 'N' || magic_bytes[1] != 'B' || magic_bytes[2] != 'G' || + magic_bytes[3] != 'I' || magic_bytes[4] != 'C') + break; + + // banapass checksum is stored at block 1, starts from byte 8-15 + uint8_t check_sum[8]; + for(int i = 0; i < 8; i++) { + check_sum[i] = data->block[1].data[8 + i]; + } + + furi_string_cat_str( + parsed_data, "::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::"); + + bool is_block_2_null = true; + for(int i = 0; i < 16; i++) { + if(data->block[2].data[i] != 0) { + is_block_2_null = false; + break; + } + } + if(is_block_2_null) { + furi_string_cat_str( + parsed_data, + "\nPlease scan the clone at the\nnearest CHUNITHM or\nmaimai Cabinet for the\nAccess Code.\n"); + furi_string_cat_str( + parsed_data, "::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::"); + } else { + switch(key_b) { + case banapass_key_b_value_block: + int32_t value = 0; + uint8_t addr = 0; + bool value_found = mf_classic_block_to_value( + &data->block[2], &value, &addr); // block 2 is value block + if(value_found) { + furi_string_cat_printf(parsed_data, "\nValue: %08lX", value); + } else { + furi_string_cat_str(parsed_data, "\nPotential clone:\nInvalid value block."); + } + furi_string_cat_str( + parsed_data, + "\nPlease check the back of\nyour Bandai Namco Passport\nfor the Access Code.\n"); + furi_string_cat_str( + parsed_data, "::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::"); + break; + + case banapass_key_b_access_code: + // banapass access code is stored as decimal hex representation in block 2, starts from byte 6, len 10 bytes + uint8_t access_code[10]; + + furi_string_cat_printf(parsed_data, "\nAccess Code:\n"); + bool access_code_is_bcd = true; + + for(int i = 0; i < 10; i++) { + access_code[i] = data->block[2].data[6 + i]; + furi_string_cat_printf(parsed_data, "%02X", access_code[i]); + if(i % 2 == 1) { + furi_string_cat_str(parsed_data, " "); + } + if((access_code[i] >> 4) > 9) access_code_is_bcd = false; + if((access_code[i] & 0x0F) > 9) access_code_is_bcd = false; + } + furi_string_cat_printf( + parsed_data, "\nBCD valid: %s\n", access_code_is_bcd ? "Yes" : "No"); + if((access_code[0] >> 4) != 3) { + furi_string_cat_printf( + parsed_data, + "Potential clone:\nAccess Code preamble\nexpected 3, got %d\n", + (access_code[0] >> 4)); + } + furi_string_cat_str( + parsed_data, "::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::"); + + break; + default: + break; + } + } + furi_string_cat_str(parsed_data, "\nMagic:\n"); + for(int i = 0; i < 6; i++) { + furi_string_cat_printf(parsed_data, "%c", magic_bytes[i]); + } + furi_string_cat_str(parsed_data, "\nChecksum:\n"); + for(int i = 0; i < 8; i++) { + furi_string_cat_printf(parsed_data, "%02X ", check_sum[i]); + } + + furi_string_cat_str(parsed_data, "\n"); + furi_string_cat_str( + parsed_data, "::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::"); + + parsed = true; + + } while(false); + + return parsed; +} + +/* Actual implementation of app<>plugin interface */ +static const NfcSupportedCardsPlugin banapass_plugin = { + .protocol = NfcProtocolMfClassic, + .verify = banapass_verify, + .read = banapass_read, + .parse = banapass_parse, +}; + +/* Plugin descriptor to comply with basic plugin specification */ +static const FlipperAppPluginDescriptor banapass_plugin_descriptor = { + .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID, + .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION, + .entry_point = &banapass_plugin, +}; + +/* Plugin entry point - must return a pointer to const descriptor */ +const FlipperAppPluginDescriptor* banapass_plugin_ep(void) { + return &banapass_plugin_descriptor; +}