mirror of
https://github.com/D4C1-Labs/Flipper-ARF.git
synced 2026-03-30 19:06:01 +00:00
Compare commits
10 Commits
dev-8ff5e3
...
dev-8e0a81
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c6265ea29b | ||
|
|
8e0a81b89d | ||
|
|
6f39fd4803 | ||
|
|
41d10f9b3d | ||
|
|
1f97aa2e3c | ||
|
|
5b9038173b | ||
|
|
fde0a57595 | ||
|
|
3fb40944e6 | ||
|
|
e61cfa765a | ||
|
|
fd0dd6c324 |
66
CHANGELOG.md
66
CHANGELOG.md
@@ -1,66 +0,0 @@
|
||||
## Main changes
|
||||
- Current API: 87.6
|
||||
* SubGHz: Signal Settings Improvements (PR #968 | by @Dmitry422)
|
||||
* Apps: Build tag (**17feb2026**) - **Check out more Apps updates and fixes by following** [this link](https://github.com/xMasterX/all-the-plugins/commits/dev)
|
||||
## Other changes
|
||||
* MFKey: Update to v4.1 (by @noproto & @dchristle)
|
||||
<br><br>
|
||||
#### Known NFC post-refactor regressions list:
|
||||
- Mifare Mini clones reading is broken (original mini working fine) (OFW)
|
||||
- While reading some EMV capable cards via NFC->Read flipper may crash due to Desfire poller issue, read those cards via Extra actions->Read specific card type->EMV
|
||||
|
||||
----
|
||||
|
||||
[-> How to install firmware](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/HowToInstall.md)
|
||||
|
||||
[-> Unleashed FW Web Installer](https://web.unleashedflip.com)
|
||||
|
||||
## Please support development of the project
|
||||
|
||||
| Service | Remark | QR Code | Link/Wallet |
|
||||
|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------|
|
||||
| <img src="https://cdn.simpleicons.org/patreon/dark/white" alt="Patreon" width="14"/> **Patreon** | | <div align="center"><a href="https://github.com/user-attachments/assets/a88a90a5-28c3-40b4-864a-0c0b79494a42"><img src="https://github.com/user-attachments/assets/da3a864d-d1c7-42cc-8a86-6fcaf26663ec" alt="QR image"/></a></div> | [patreon.com/mmxdev](https://patreon.com/mmxdev) |
|
||||
| <img src="https://cdn.simpleicons.org/boosty" alt="Boosty" width="14"/> **Boosty** | patreon alternative | <div align="center"><a href="https://github.com/user-attachments/assets/893c0760-f738-42c1-acaa-916019a7bdf8"><img src="https://github.com/user-attachments/assets/da3a864d-d1c7-42cc-8a86-6fcaf26663ec" alt="QR image"/></a></div> | [boosty.to/mmxdev](https://boosty.to/mmxdev) |
|
||||
| <img src="https://gist.githubusercontent.com/m-xim/255a3ef36c886dec144a58864608084c/raw/71da807b4abbd1582e511c9ea30fad27f78d642a/cloudtips_icon.svg" alt="Cloudtips" width="14"/> CloudTips | only RU payments accepted | <div align="center"><a href="https://github.com/user-attachments/assets/5de31d6a-ef24-4d30-bd8e-c06af815332a"><img src="https://github.com/user-attachments/assets/da3a864d-d1c7-42cc-8a86-6fcaf26663ec" alt="QR image"/></a></div> | [pay.cloudtips.ru/p/7b3e9d65](https://pay.cloudtips.ru/p/7b3e9d65) |
|
||||
| <img src="https://raw.githubusercontent.com/gist/PonomareVlad/55c8708f11702b4df629ae61129a9895/raw/1657350724dab66f2ad68ea034c480a2df2a1dfd/YooMoney.svg" alt="YooMoney" width="14"/> YooMoney | only RU payments accepted | <div align="center"><a href="https://github.com/user-attachments/assets/33454f79-074b-4349-b453-f94fdadc3c68"><img src="https://github.com/user-attachments/assets/da3a864d-d1c7-42cc-8a86-6fcaf26663ec" alt="QR image"/></a></div> | [yoomoney.ru/fundraise/XA49mgQLPA0.221209](https://yoomoney.ru/fundraise/XA49mgQLPA0.221209) |
|
||||
| <img src="https://cdn.simpleicons.org/tether" alt="USDT" width="14"/> USDT | TRC20 | <div align="center"><a href="https://github.com/user-attachments/assets/0500498d-18ed-412d-a1a4-8a66d0b6f057"><img src="https://github.com/user-attachments/assets/da3a864d-d1c7-42cc-8a86-6fcaf26663ec" alt="QR image"/></a></div> | `TSXcitMSnWXUFqiUfEXrTVpVewXy2cYhrs` |
|
||||
| <img src="https://cdn.simpleicons.org/ethereum" alt="ETH" width="14"/> ETH | BSC/ERC20-Tokens | <div align="center"><a href="https://github.com/user-attachments/assets/0f323e98-c524-4f41-abb2-f4f1cec83ab6"><img src="https://github.com/user-attachments/assets/da3a864d-d1c7-42cc-8a86-6fcaf26663ec" alt="QR image"/></a></div> | `0xFebF1bBc8229418FF2408C07AF6Afa49152fEc6a` |
|
||||
| <img src="https://cdn.simpleicons.org/bitcoin" alt="BTC" width="14"/> BTC | | <div align="center"><a href="https://github.com/user-attachments/assets/5a904d45-947e-4b92-9f0f-7fbaaa7b37f8"><img src="https://github.com/user-attachments/assets/da3a864d-d1c7-42cc-8a86-6fcaf26663ec" alt="QR image"/></a></div> | `bc1q0np836jk9jwr4dd7p6qv66d04vamtqkxrecck9` |
|
||||
| <img src="https://cdn.simpleicons.org/solana" alt="SOL" width="13"/> SOL | Solana/Tokens | <div align="center"><a href="https://github.com/user-attachments/assets/ab33c5e0-dd59-497b-9c91-ceb89c36b34d"><img src="https://github.com/user-attachments/assets/da3a864d-d1c7-42cc-8a86-6fcaf26663ec" alt="QR image"/></a></div> | `DSgwouAEgu8iP5yr7EHHDqMNYWZxAqXWsTEeqCAXGLj8` |
|
||||
| <img src="https://cdn.simpleicons.org/dogecoin" alt="DOGE" width="14"/> DOGE | | <div align="center"><a href="https://github.com/user-attachments/assets/2937edd0-5c85-4465-a444-14d4edb481c0"><img src="https://github.com/user-attachments/assets/da3a864d-d1c7-42cc-8a86-6fcaf26663ec" alt="QR image"/></a></div> | `D6R6gYgBn5LwTNmPyvAQR6bZ9EtGgFCpvv` |
|
||||
| <img src="https://cdn.simpleicons.org/litecoin" alt="LTC" width="14"/> LTC | | <div align="center"><a href="https://github.com/user-attachments/assets/441985fe-f028-4400-83c1-c215760c1e74"><img src="https://github.com/user-attachments/assets/da3a864d-d1c7-42cc-8a86-6fcaf26663ec" alt="QR image"/></a></div> | `ltc1q3ex4ejkl0xpx3znwrmth4lyuadr5qgv8tmq8z9` |
|
||||
| <img src="https://bitcoincash.org/img/green/bitcoin-cash-circle.svg" alt="BCH" width="14"/> BCH | | <div align="center"><a href="https://github.com/user-attachments/assets/7f365976-19a3-4777-b17e-4bfba5f69eff"><img src="https://github.com/user-attachments/assets/da3a864d-d1c7-42cc-8a86-6fcaf26663ec" alt="QR image"/></a></div> | `qquxfyzntuqufy2dx0hrfr4sndp0tucvky4sw8qyu3` |
|
||||
| <img src="https://cdn.simpleicons.org/monero" alt="XMR" width="14"/> XMR | Monero | <div align="center"><a href="https://github.com/user-attachments/assets/96186c06-61e7-4b4d-b716-6eaf1779bfd8"><img src="https://github.com/user-attachments/assets/da3a864d-d1c7-42cc-8a86-6fcaf26663ec" alt="QR image"/></a></div> | `41xUz92suUu1u5Mu4qkrcs52gtfpu9rnZRdBpCJ244KRHf6xXSvVFevdf2cnjS7RAeYr5hn9MsEfxKoFDRSctFjG5fv1Mhn` |
|
||||
| <img src="https://cdn.simpleicons.org/ton" alt="TON" width="14"/> TON | | <div align="center"><a href="https://github.com/user-attachments/assets/92a57e57-7462-42b7-a342-6f22c6e600c1"><img src="https://github.com/user-attachments/assets/da3a864d-d1c7-42cc-8a86-6fcaf26663ec" alt="QR image"/></a></div> | `UQCOqcnYkvzOZUV_9bPE_8oTbOrOF03MnF-VcJyjisTZmsxa` |
|
||||
|
||||
|
||||
#### Thanks to our sponsors who supported project in the past and special thanks to sponsors who supports us on regular basis:
|
||||
@mishamyte, ClaraCrazy, Pathfinder [Count Zero cDc], callmezimbra, Quen0n, MERRON, grvpvl (lvpvrg), art_col, ThurstonWaffles, Moneron, UterGrooll, LUCFER, Northpirate, zloepuzo, T.Rat, Alexey B., ionelife, ...
|
||||
and all other great people who supported our project and me (xMasterX), thanks to you all!
|
||||
|
||||
|
||||
## **Recommended update option - Web Updater**
|
||||
|
||||
### What `e`, ` `, `c` means? What I need to download if I don't want to use Web updater?
|
||||
What build I should download and what this name means - `flipper-z-f7-update-(version)(e / c).tgz` ? <br>
|
||||
`flipper-z` = for Flipper Zero device<br>
|
||||
`f7` = Hardware version - same for all flipper zero devices<br>
|
||||
`update` = Update package, contains updater, all assets (plugins, IR libs, etc.), and firmware itself<br>
|
||||
`(version)` = Firmware version<br>
|
||||
| Designation | [Base Apps](https://github.com/xMasterX/all-the-plugins#default-pack) | [Extra Apps](https://github.com/xMasterX/all-the-plugins#extra-pack) |
|
||||
|-----|:---:|:---:|
|
||||
| ` ` | ✅ | |
|
||||
| `c` | | |
|
||||
| `e` | ✅ | ✅ |
|
||||
|
||||
**To enable RGB Backlight support go into LCD & Notifications settings**
|
||||
|
||||
⚠️RGB backlight [hardware mod](https://github.com/quen0n/flipperzero-firmware-rgb#readme), works only on modded flippers! do not enable on non modded device!
|
||||
|
||||
|
||||
Firmware Self-update package (update from microSD) - `flipper-z-f7-update-(version).tgz` for mobile app / qFlipper / web<br>
|
||||
Archive of `scripts` folder (contains scripts for FW/plugins development) - `flipper-z-any-scripts-(version).tgz`<br>
|
||||
SDK files for plugins development and uFBT - `flipper-z-f7-sdk-(version).zip`
|
||||
|
||||
|
||||
|
||||
@@ -182,7 +182,7 @@ Contributions are welcome if they:
|
||||
> Non-automotive features are considered out-of-scope for now.
|
||||
|
||||
### This code is a mess!
|
||||

|
||||

|
||||
---
|
||||
|
||||
## Citations & References
|
||||
|
||||
@@ -14,7 +14,9 @@ enum {
|
||||
SubmenuIndexUnlock = SubmenuIndexCommonMax,
|
||||
SubmenuIndexUnlockByReader,
|
||||
SubmenuIndexUnlockByPassword,
|
||||
SubmenuIndexDictAttack
|
||||
SubmenuIndexDictAttack,
|
||||
SubmenuIndexWriteKeepKey, // ULC: write data pages, keep target card's existing key
|
||||
SubmenuIndexWriteCopyKey, // ULC: write all pages including key from source card
|
||||
};
|
||||
|
||||
enum {
|
||||
@@ -214,8 +216,26 @@ static void nfc_scene_read_and_saved_menu_on_enter_mf_ultralight(NfcApp* instanc
|
||||
if(is_locked ||
|
||||
(data->type != MfUltralightTypeNTAG213 && data->type != MfUltralightTypeNTAG215 &&
|
||||
data->type != MfUltralightTypeNTAG216 && data->type != MfUltralightTypeUL11 &&
|
||||
data->type != MfUltralightTypeUL21 && data->type != MfUltralightTypeOrigin)) {
|
||||
data->type != MfUltralightTypeUL21 && data->type != MfUltralightTypeOrigin &&
|
||||
data->type != MfUltralightTypeMfulC)) {
|
||||
submenu_remove_item(submenu, SubmenuIndexCommonWrite);
|
||||
} else if(data->type == MfUltralightTypeMfulC) {
|
||||
// Replace the generic Write item with two ULC-specific options so the user
|
||||
// can choose whether to keep or overwrite the target card's 3DES key.
|
||||
// This avoids any mid-write dialog/view-switching complexity entirely.
|
||||
submenu_remove_item(submenu, SubmenuIndexCommonWrite);
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
"Write (Keep Key)",
|
||||
SubmenuIndexWriteKeepKey,
|
||||
nfc_protocol_support_common_submenu_callback,
|
||||
instance);
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
"Write (Copy Key)",
|
||||
SubmenuIndexWriteCopyKey,
|
||||
nfc_protocol_support_common_submenu_callback,
|
||||
instance);
|
||||
}
|
||||
|
||||
if(is_locked) {
|
||||
@@ -291,6 +311,14 @@ static bool nfc_scene_read_and_saved_menu_on_event_mf_ultralight(
|
||||
scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightCDictAttack);
|
||||
}
|
||||
consumed = true;
|
||||
} else if(event.event == SubmenuIndexWriteKeepKey) {
|
||||
instance->mf_ultralight_c_write_context.copy_key = false;
|
||||
scene_manager_next_scene(instance->scene_manager, NfcSceneWrite);
|
||||
consumed = true;
|
||||
} else if(event.event == SubmenuIndexWriteCopyKey) {
|
||||
instance->mf_ultralight_c_write_context.copy_key = true;
|
||||
scene_manager_next_scene(instance->scene_manager, NfcSceneWrite);
|
||||
consumed = true;
|
||||
}
|
||||
}
|
||||
return consumed;
|
||||
@@ -307,12 +335,139 @@ static NfcCommand
|
||||
if(mf_ultralight_event->type == MfUltralightPollerEventTypeRequestMode) {
|
||||
mf_ultralight_event->data->poller_mode = MfUltralightPollerModeWrite;
|
||||
furi_string_reset(instance->text_box_store);
|
||||
if(instance->mf_ultralight_c_dict_context.dict) {
|
||||
keys_dict_free(instance->mf_ultralight_c_dict_context.dict);
|
||||
}
|
||||
instance->mf_ultralight_c_dict_context.dict = NULL;
|
||||
instance->mf_ultralight_c_write_context.dict_state = NfcMfUltralightCWriteDictIdle;
|
||||
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventCardDetected);
|
||||
} else if(mf_ultralight_event->type == MfUltralightPollerEventTypeAuthRequest) {
|
||||
// Skip auth during the read phase of write - we'll authenticate
|
||||
// against the target card in RequestWriteData using source key or dict attack
|
||||
mf_ultralight_event->data->auth_context.skip_auth = true;
|
||||
} else if(mf_ultralight_event->type == MfUltralightPollerEventTypeRequestKey) {
|
||||
// Dict attack key provider - user dict first, then system dict
|
||||
if(!instance->mf_ultralight_c_dict_context.dict &&
|
||||
instance->mf_ultralight_c_write_context.dict_state == NfcMfUltralightCWriteDictIdle) {
|
||||
if(keys_dict_check_presence(NFC_APP_MF_ULTRALIGHT_C_DICT_USER_PATH)) {
|
||||
instance->mf_ultralight_c_dict_context.dict = keys_dict_alloc(
|
||||
NFC_APP_MF_ULTRALIGHT_C_DICT_USER_PATH,
|
||||
KeysDictModeOpenExisting,
|
||||
sizeof(MfUltralightC3DesAuthKey));
|
||||
instance->mf_ultralight_c_write_context.dict_state = NfcMfUltralightCWriteDictUser;
|
||||
}
|
||||
if(!instance->mf_ultralight_c_dict_context.dict) {
|
||||
instance->mf_ultralight_c_dict_context.dict = keys_dict_alloc(
|
||||
NFC_APP_MF_ULTRALIGHT_C_DICT_SYSTEM_PATH,
|
||||
KeysDictModeOpenExisting,
|
||||
sizeof(MfUltralightC3DesAuthKey));
|
||||
instance->mf_ultralight_c_write_context.dict_state =
|
||||
NfcMfUltralightCWriteDictSystem;
|
||||
}
|
||||
}
|
||||
MfUltralightC3DesAuthKey key = {};
|
||||
bool got_key = false;
|
||||
if(instance->mf_ultralight_c_dict_context.dict) {
|
||||
got_key = keys_dict_get_next_key(
|
||||
instance->mf_ultralight_c_dict_context.dict,
|
||||
key.data,
|
||||
sizeof(MfUltralightC3DesAuthKey));
|
||||
}
|
||||
if(!got_key &&
|
||||
instance->mf_ultralight_c_write_context.dict_state == NfcMfUltralightCWriteDictUser) {
|
||||
// Exhausted user dict, switch to system dict
|
||||
if(instance->mf_ultralight_c_dict_context.dict) {
|
||||
keys_dict_free(instance->mf_ultralight_c_dict_context.dict);
|
||||
}
|
||||
instance->mf_ultralight_c_dict_context.dict = keys_dict_alloc(
|
||||
NFC_APP_MF_ULTRALIGHT_C_DICT_SYSTEM_PATH,
|
||||
KeysDictModeOpenExisting,
|
||||
sizeof(MfUltralightC3DesAuthKey));
|
||||
instance->mf_ultralight_c_write_context.dict_state = NfcMfUltralightCWriteDictSystem;
|
||||
if(instance->mf_ultralight_c_dict_context.dict) {
|
||||
got_key = keys_dict_get_next_key(
|
||||
instance->mf_ultralight_c_dict_context.dict,
|
||||
key.data,
|
||||
sizeof(MfUltralightC3DesAuthKey));
|
||||
}
|
||||
}
|
||||
if(got_key) {
|
||||
mf_ultralight_event->data->key_request_data.key = key;
|
||||
mf_ultralight_event->data->key_request_data.key_provided = true;
|
||||
FURI_LOG_D(
|
||||
"MfULC",
|
||||
"Trying dict key: "
|
||||
"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
|
||||
key.data[0],
|
||||
key.data[1],
|
||||
key.data[2],
|
||||
key.data[3],
|
||||
key.data[4],
|
||||
key.data[5],
|
||||
key.data[6],
|
||||
key.data[7],
|
||||
key.data[8],
|
||||
key.data[9],
|
||||
key.data[10],
|
||||
key.data[11],
|
||||
key.data[12],
|
||||
key.data[13],
|
||||
key.data[14],
|
||||
key.data[15]);
|
||||
} else {
|
||||
mf_ultralight_event->data->key_request_data.key_provided = false;
|
||||
FURI_LOG_D("MfULC", "Dict exhausted - no more keys");
|
||||
if(instance->mf_ultralight_c_dict_context.dict) {
|
||||
keys_dict_free(instance->mf_ultralight_c_dict_context.dict);
|
||||
instance->mf_ultralight_c_dict_context.dict = NULL;
|
||||
}
|
||||
instance->mf_ultralight_c_write_context.dict_state =
|
||||
NfcMfUltralightCWriteDictExhausted;
|
||||
}
|
||||
} else if(mf_ultralight_event->type == MfUltralightPollerEventTypeRequestWriteData) {
|
||||
mf_ultralight_event->data->write_data =
|
||||
nfc_device_get_data(instance->nfc_device, NfcProtocolMfUltralight);
|
||||
// Reset dict context so RequestKey starts fresh for the write-phase auth
|
||||
if(instance->mf_ultralight_c_dict_context.dict) {
|
||||
keys_dict_free(instance->mf_ultralight_c_dict_context.dict);
|
||||
instance->mf_ultralight_c_dict_context.dict = NULL;
|
||||
}
|
||||
instance->mf_ultralight_c_write_context.dict_state = NfcMfUltralightCWriteDictIdle;
|
||||
} else if(mf_ultralight_event->type == MfUltralightPollerEventTypeWriteKeyRequest) {
|
||||
// Apply the user's key choice - read from static, not scene state (scene manager
|
||||
// resets state to 0 on scene entry, wiping any value set before next_scene).
|
||||
bool keep_key = !instance->mf_ultralight_c_write_context.copy_key;
|
||||
mf_ultralight_event->data->write_key_skip = keep_key;
|
||||
|
||||
if(mf_ultralight_event->data->key_request_data.key_provided) {
|
||||
MfUltralightC3DesAuthKey found_key = mf_ultralight_event->data->key_request_data.key;
|
||||
FURI_LOG_D(
|
||||
"MfULC",
|
||||
"WriteKeyRequest: target key = "
|
||||
"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
|
||||
found_key.data[0],
|
||||
found_key.data[1],
|
||||
found_key.data[2],
|
||||
found_key.data[3],
|
||||
found_key.data[4],
|
||||
found_key.data[5],
|
||||
found_key.data[6],
|
||||
found_key.data[7],
|
||||
found_key.data[8],
|
||||
found_key.data[9],
|
||||
found_key.data[10],
|
||||
found_key.data[11],
|
||||
found_key.data[12],
|
||||
found_key.data[13],
|
||||
found_key.data[14],
|
||||
found_key.data[15]);
|
||||
}
|
||||
FURI_LOG_D(
|
||||
"MfULC",
|
||||
"WriteKeyRequest: decision = %s (copy_key=%d)",
|
||||
keep_key ? "KEEP target key (pages 44-47 NOT written)" :
|
||||
"OVERWRITE with source key (pages 44-47 WILL be written)",
|
||||
(int)instance->mf_ultralight_c_write_context.copy_key);
|
||||
} else if(mf_ultralight_event->type == MfUltralightPollerEventTypeCardMismatch) {
|
||||
furi_string_set(instance->text_box_store, "Card of the same\ntype should be\n presented");
|
||||
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventWrongCard);
|
||||
@@ -323,6 +478,7 @@ static NfcCommand
|
||||
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerFailure);
|
||||
command = NfcCommandStop;
|
||||
} else if(mf_ultralight_event->type == MfUltralightPollerEventTypeWriteFail) {
|
||||
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerFailure);
|
||||
command = NfcCommandStop;
|
||||
} else if(mf_ultralight_event->type == MfUltralightPollerEventTypeWriteSuccess) {
|
||||
furi_string_reset(instance->text_box_store);
|
||||
@@ -334,9 +490,18 @@ static NfcCommand
|
||||
}
|
||||
|
||||
static void nfc_scene_write_on_enter_mf_ultralight(NfcApp* instance) {
|
||||
// Free any dict the write callback opened (dict_state != Idle means we own it).
|
||||
// After a DictAttack scene, on_exit now NULLs the pointer so a simple NULL check
|
||||
// is safe here too — but the state enum is the authoritative ownership record.
|
||||
if(instance->mf_ultralight_c_write_context.dict_state != NfcMfUltralightCWriteDictIdle &&
|
||||
instance->mf_ultralight_c_dict_context.dict) {
|
||||
keys_dict_free(instance->mf_ultralight_c_dict_context.dict);
|
||||
}
|
||||
instance->mf_ultralight_c_dict_context.dict = NULL;
|
||||
instance->mf_ultralight_c_write_context.dict_state = NfcMfUltralightCWriteDictIdle;
|
||||
furi_string_set(instance->text_box_store, "\nApply the\ntarget\ncard now");
|
||||
instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolMfUltralight);
|
||||
nfc_poller_start(instance->poller, nfc_scene_write_poller_callback_mf_ultralight, instance);
|
||||
furi_string_set(instance->text_box_store, "Apply the initial\ncard only");
|
||||
}
|
||||
|
||||
const NfcProtocolSupportBase nfc_protocol_support_mf_ultralight = {
|
||||
|
||||
@@ -126,6 +126,18 @@ typedef struct {
|
||||
size_t dict_keys_current;
|
||||
} NfcMfUltralightCDictContext;
|
||||
|
||||
typedef enum {
|
||||
NfcMfUltralightCWriteDictIdle, /**< No dict open; safe to open either dict. */
|
||||
NfcMfUltralightCWriteDictUser, /**< User dict currently open. */
|
||||
NfcMfUltralightCWriteDictSystem, /**< System dict currently open. */
|
||||
NfcMfUltralightCWriteDictExhausted, /**< All dicts tried; do not re-open. */
|
||||
} NfcMfUltralightCWriteDictState;
|
||||
|
||||
typedef struct {
|
||||
bool copy_key; /**< True = overwrite target 3DES key with source key pages. */
|
||||
NfcMfUltralightCWriteDictState dict_state; /**< Which dict is open for write-phase auth. */
|
||||
} NfcMfUltralightCWriteContext;
|
||||
|
||||
struct NfcApp {
|
||||
DialogsApp* dialogs;
|
||||
Storage* storage;
|
||||
@@ -165,6 +177,7 @@ struct NfcApp {
|
||||
SlixUnlock* slix_unlock;
|
||||
NfcMfClassicDictAttackContext nfc_dict_context;
|
||||
NfcMfUltralightCDictContext mf_ultralight_c_dict_context;
|
||||
NfcMfUltralightCWriteContext mf_ultralight_c_write_context;
|
||||
Mfkey32Logger* mfkey32_logger;
|
||||
MfUserDict* mf_user_dict;
|
||||
MfClassicKeyCache* mfc_key_cache;
|
||||
|
||||
@@ -77,6 +77,15 @@ void nfc_scene_mf_ultralight_c_dict_attack_prepare_view(NfcApp* instance) {
|
||||
// Set attack type to Ultralight C
|
||||
dict_attack_set_type(instance->dict_attack, DictAttackTypeMfUltralightC);
|
||||
|
||||
// Guard: if a previous write phase left a dict handle open, close it now.
|
||||
// Without this, navigating write->back->read->dict-attack would open the same
|
||||
// file twice, corrupting VFS state and causing a ViewPort lockup.
|
||||
if(instance->mf_ultralight_c_dict_context.dict) {
|
||||
keys_dict_free(instance->mf_ultralight_c_dict_context.dict);
|
||||
instance->mf_ultralight_c_dict_context.dict = NULL;
|
||||
instance->mf_ultralight_c_write_context.dict_state = NfcMfUltralightCWriteDictIdle;
|
||||
}
|
||||
|
||||
if(state == DictAttackStateUserDictInProgress) {
|
||||
do {
|
||||
if(!keys_dict_check_presence(NFC_APP_MF_ULTRALIGHT_C_DICT_USER_PATH)) {
|
||||
@@ -167,6 +176,7 @@ bool nfc_scene_mf_ultralight_c_dict_attack_on_event(void* context, SceneManagerE
|
||||
nfc_poller_stop(instance->poller);
|
||||
nfc_poller_free(instance->poller);
|
||||
keys_dict_free(instance->mf_ultralight_c_dict_context.dict);
|
||||
instance->mf_ultralight_c_dict_context.dict = NULL;
|
||||
scene_manager_set_scene_state(
|
||||
instance->scene_manager,
|
||||
NfcSceneMfUltralightCDictAttack,
|
||||
@@ -199,6 +209,7 @@ bool nfc_scene_mf_ultralight_c_dict_attack_on_event(void* context, SceneManagerE
|
||||
nfc_poller_stop(instance->poller);
|
||||
nfc_poller_free(instance->poller);
|
||||
keys_dict_free(instance->mf_ultralight_c_dict_context.dict);
|
||||
instance->mf_ultralight_c_dict_context.dict = NULL;
|
||||
scene_manager_set_scene_state(
|
||||
instance->scene_manager,
|
||||
NfcSceneMfUltralightCDictAttack,
|
||||
@@ -230,6 +241,7 @@ void nfc_scene_mf_ultralight_c_dict_attack_on_exit(void* context) {
|
||||
NfcSceneMfUltralightCDictAttack,
|
||||
DictAttackStateUserDictInProgress);
|
||||
keys_dict_free(instance->mf_ultralight_c_dict_context.dict);
|
||||
instance->mf_ultralight_c_dict_context.dict = NULL;
|
||||
instance->mf_ultralight_c_dict_context.dict_keys_total = 0;
|
||||
instance->mf_ultralight_c_dict_context.dict_keys_current = 0;
|
||||
instance->mf_ultralight_c_dict_context.auth_success = false;
|
||||
|
||||
@@ -92,19 +92,30 @@ Hopping_Preset: FM95
|
||||
# Custom preset
|
||||
# format for CC1101 "Custom_preset_data:" XX YY XX YY .. 00 00 ZZ ZZ ZZ ZZ ZZ ZZ ZZ ZZ, where: XX-register, YY - register data, 00 00 - end load register, ZZ - 8 byte Pa table register
|
||||
|
||||
Custom_preset_name: A1
|
||||
Custom_preset_name: OOK_LR
|
||||
Custom_preset_module: CC1101
|
||||
Custom_preset_data: 02 0D 0B 06 08 32 07 04 14 00 13 00 12 30 11 F8 10 C9 15 14 18 18 19 16 1D 91 1C 00 1B 07 20 FB 22 00 21 55 00 00 00 С0 00 00 00 00 00 00
|
||||
Custom_preset_data: 02 0D 07 04 08 32 0B 06 10 F8 11 32 12 30 14 00 15 00 18 18 19 16 1B 07 1C 00 1D B1 20 FB 21 B6 22 11 00 00 00 C0 00 00 00 00 00 00
|
||||
|
||||
Custom_preset_name: FM95
|
||||
Custom_preset_name: OOK_U
|
||||
Custom_preset_module: CC1101
|
||||
Custom_preset_data: 02 0D 0B 06 08 32 07 04 14 00 13 02 12 04 11 83 10 67 15 24 18 18 19 16 1D 91 1C 00 1B 07 20 FB 22 10 21 56 00 00 C0 00 00 00 00 00 00 00
|
||||
Custom_preset_data: 02 0D 07 04 08 32 0B 06 10 F8 11 32 12 30 14 00 15 00 17 0C 18 18 19 16 1B 07 1C 00 1D B1 20 FB 21 B6 22 11 2C 81 2D 35 2E 09 00 00 00 C0 00 00 00 00 00 00
|
||||
|
||||
Custom_preset_name: AM_1
|
||||
Custom_preset_module: CC1101
|
||||
Custom_preset_data: 02 0D 07 04 08 32 0B 06 10 C9 11 F8 12 30 14 00 15 14 18 18 19 16 1B 07 1C 00 1D 91 20 FB 21 55 22 00 00 00 00 C0 00 00 00 00 00 00
|
||||
|
||||
Custom_preset_name: FSK_1
|
||||
Custom_preset_module: CC1101
|
||||
Custom_preset_data: 02 0D 03 47 08 32 0B 0C 10 C7 11 93 12 00 13 22 14 F8 15 35 18 18 19 1D 1B 04 1C 00 1D 92 20 FB 21 B6 22 17 00 00 12 0E 1D 34 60 84 C8 C0
|
||||
|
||||
Custom_preset_name: F3
|
||||
Custom_preset_module: CC1101
|
||||
Custom_preset_data: 02 0D 03 47 08 32 0B 06 0C 00 0D 10 0E B0 0F 71 10 CA 11 83 12 80 13 22 14 F8 15 42 16 07 17 30 18 18 19 1D 1A 1C 1B 43 1C 40 1D 91 20 FB 21 B6 22 00 23 E9 24 2A 25 00 26 1F 2C 81 2D 35 2E 09 00 00 12 0E 1D 34 60 84 C8 C0
|
||||
Custom_preset_data: 02 0D 03 47 08 32 0B 06 0C 00 0D 10 0D 0D 10 C5 11 83 12 80 13 22 14 F8 15 42 16 07 17 30 18 18 19 1D 1A 1C 1B 43 1C 40 1D 91 20 FB 21 B6 22 00 23 E9 24 2A 25 00 26 1F 2C 81 2D 35 2E 09 00 00 12 0E 1D 34 60 84 C8 C0
|
||||
|
||||
Custom_preset_name: FM95
|
||||
Custom_preset_module: CC1101
|
||||
Custom_preset_data: 02 0D 07 04 08 32 0B 06 10 67 11 83 12 04 13 02 15 24 18 18 19 16 1B 07 1C 00 1D 91 20 FB 21 56 22 10 00 00 C0 00 00 00 00 00 00 00
|
||||
|
||||
#2-FSK 200khz BW / 135kHz Filter/ 15.86Khz Deviation + Ramping
|
||||
Custom_preset_name: FM15k
|
||||
Custom_preset_module: CC1101
|
||||
Custom_preset_data: 02 0D 03 47 08 32 0B 06 15 32 14 00 13 00 12 00 11 32 10 A7 18 18 19 1D 1D 92 1C 00 1B 04 20 FB 22 17 21 B6 00 00 00 12 0E 34 60 C5 C1 C0
|
||||
Custom_preset_data: 02 0D 03 47 08 32 0B 06 10 A7 11 32 12 00 13 00 14 00 15 32 18 18 19 1D 1B 04 1C 00 1D 92 20 FB 21 B6 22 17 00 00 00 12 0E 34 60 C5 C1 C0
|
||||
|
||||
@@ -133,21 +133,21 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event)
|
||||
}
|
||||
//CC1101 Stop RX -> Start TX
|
||||
subghz_txrx_hopper_pause(subghz->txrx);
|
||||
// key concept: we start endless TX until user release OK button, and after this we send last
|
||||
// protocols repeats - this guarantee that one press OK will
|
||||
// be guarantee send the required minimum protocol data packets
|
||||
// for all of this we use subghz_block_generic_global.endless_tx in protocols _yield function.
|
||||
subghz->state_notifications = SubGhzNotificationStateTx;
|
||||
subghz_block_generic_global.endless_tx = true;
|
||||
if(!subghz_tx_start(
|
||||
subghz,
|
||||
subghz_history_get_raw_data(subghz->history, subghz->idx_menu_chosen))) {
|
||||
subghz_txrx_rx_start(subghz->txrx);
|
||||
subghz_txrx_hopper_unpause(subghz->txrx);
|
||||
subghz->state_notifications = SubGhzNotificationStateRx;
|
||||
} else {
|
||||
// key concept: we start endless TX until user release OK button, and after this we send last
|
||||
// protocols repeats - this guarantee that one press OK will
|
||||
// be guarantee send the required minimum protocol data packets
|
||||
// for all of this we use subghz_block_generic_global.endless_tx in protocols _yield function.
|
||||
subghz->state_notifications = SubGhzNotificationStateTx;
|
||||
subghz_block_generic_global.endless_tx = true;
|
||||
subghz_block_generic_global.endless_tx = false;
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
} else if(event.event == SubGhzCustomEventSceneReceiverInfoTxStop) {
|
||||
//CC1101 Stop Tx -> next tick event Start RX
|
||||
// user release OK
|
||||
|
||||
@@ -575,13 +575,15 @@ uint8_t mf_ultralight_get_write_end_page(MfUltralightType type) {
|
||||
furi_assert(
|
||||
type == MfUltralightTypeUL11 || type == MfUltralightTypeUL21 ||
|
||||
type == MfUltralightTypeNTAG213 || type == MfUltralightTypeNTAG215 ||
|
||||
type == MfUltralightTypeNTAG216 || type == MfUltralightTypeOrigin);
|
||||
type == MfUltralightTypeNTAG216 || type == MfUltralightTypeOrigin ||
|
||||
type == MfUltralightTypeMfulC);
|
||||
|
||||
uint8_t end_page = mf_ultralight_get_config_page_num(type);
|
||||
if(type == MfUltralightTypeNTAG213 || type == MfUltralightTypeNTAG215 ||
|
||||
type == MfUltralightTypeNTAG216) {
|
||||
end_page -= 1;
|
||||
} else if(type == MfUltralightTypeOrigin) {
|
||||
} else if(type == MfUltralightTypeOrigin || type == MfUltralightTypeMfulC) {
|
||||
// ULC: 48 pages total, write pages 4-47 (includes auth config + 3DES key)
|
||||
end_page = mf_ultralight_features[type].total_pages;
|
||||
}
|
||||
|
||||
|
||||
@@ -186,7 +186,7 @@ static MfUltralightCommand
|
||||
uint16_t pages_total = instance->data->pages_total;
|
||||
MfUltralightCommand command = MfUltralightCommandNotProcessedNAK;
|
||||
|
||||
FURI_LOG_T(TAG, "CMD_WRITE");
|
||||
FURI_LOG_T(TAG, "CMD_WRITE page %d", start_page);
|
||||
|
||||
do {
|
||||
bool do_i2c_check = mf_ultralight_is_i2c_tag(instance->data->type);
|
||||
@@ -197,12 +197,22 @@ static MfUltralightCommand
|
||||
break;
|
||||
}
|
||||
|
||||
if(!mf_ultralight_listener_check_access(
|
||||
instance, start_page, MfUltralightListenerAccessTypeWrite))
|
||||
break;
|
||||
// PATCHED: For Ultralight-C, allow writes to pages 44-47 (3DES key area)
|
||||
// This enables "magic card" emulation for key grabbing
|
||||
bool is_ulc_key_page = (instance->data->type == MfUltralightTypeMfulC) &&
|
||||
(start_page >= 44 && start_page <= 47);
|
||||
|
||||
if(mf_ultralight_static_lock_check_page(instance->static_lock, start_page)) break;
|
||||
if(mf_ultralight_dynamic_lock_check_page(instance, start_page)) break;
|
||||
if(!is_ulc_key_page) {
|
||||
// Normal access check for all other pages
|
||||
if(!mf_ultralight_listener_check_access(
|
||||
instance, start_page, MfUltralightListenerAccessTypeWrite))
|
||||
break;
|
||||
|
||||
if(mf_ultralight_static_lock_check_page(instance->static_lock, start_page)) break;
|
||||
if(mf_ultralight_dynamic_lock_check_page(instance, start_page)) break;
|
||||
} else {
|
||||
FURI_LOG_I(TAG, "MAGIC: Allowing write to ULC key page %d", start_page);
|
||||
}
|
||||
|
||||
const uint8_t* rx_data = bit_buffer_get_data(buffer);
|
||||
command =
|
||||
|
||||
@@ -456,7 +456,8 @@ static NfcCommand mf_ultralight_poller_handler_auth_ultralight_c(MfUltralightPol
|
||||
if(instance->mfu_event.data->key_request_data.key_provided) {
|
||||
instance->auth_context.tdes_key = instance->mfu_event.data->key_request_data.key;
|
||||
} else if(instance->mode == MfUltralightPollerModeDictAttack) {
|
||||
// TODO: Can logic be rearranged to request this key before reaching mf_ultralight_poller_handler_auth_ultralight_c in poller?
|
||||
// TODO: -nofl Can logic be rearranged to request this key
|
||||
// before reaching mf_ultralight_poller_handler_auth_ultralight_c in poller?
|
||||
FURI_LOG_D(TAG, "No initial key provided, requesting key from dictionary");
|
||||
// Trigger dictionary key request
|
||||
instance->mfu_event.type = MfUltralightPollerEventTypeRequestKey;
|
||||
@@ -697,9 +698,12 @@ static NfcCommand mf_ultralight_poller_handler_request_write_data(MfUltralightPo
|
||||
instance->mfu_event.type = MfUltralightPollerEventTypeRequestWriteData;
|
||||
instance->callback(instance->general_event, instance->context);
|
||||
|
||||
const MfUltralightData* write_data = instance->mfu_event.data->write_data;
|
||||
// Save write_data to instance field before any further events clobber the union
|
||||
instance->write_data = instance->mfu_event.data->write_data;
|
||||
const MfUltralightData* write_data = instance->write_data;
|
||||
const MfUltralightData* tag_data = instance->data;
|
||||
uint32_t features = mf_ultralight_get_feature_support_set(tag_data->type);
|
||||
instance->write_skip_key = false;
|
||||
|
||||
bool check_passed = false;
|
||||
do {
|
||||
@@ -737,6 +741,71 @@ static NfcCommand mf_ultralight_poller_handler_request_write_data(MfUltralightPo
|
||||
check_passed = true;
|
||||
} while(false);
|
||||
|
||||
// ULC: authenticate the target card before writing.
|
||||
// The read phase left the card unauthenticated (write-mode callback skips read-phase auth).
|
||||
// Ask callback for keys (cache first, then dict) until one authenticates, then fire
|
||||
// WriteKeyRequest so the callback can cache the found key and return the skip decision.
|
||||
if(check_passed &&
|
||||
mf_ultralight_support_feature(features, MfUltralightFeatureSupportAuthenticate)) {
|
||||
bool auth_ok = false;
|
||||
|
||||
while(!auth_ok) {
|
||||
// Request next key from callback (tries dict entries)
|
||||
memset(instance->mfu_event.data, 0, sizeof(MfUltralightPollerEventData));
|
||||
instance->mfu_event.type = MfUltralightPollerEventTypeRequestKey;
|
||||
instance->callback(instance->general_event, instance->context);
|
||||
|
||||
if(!instance->mfu_event.data->key_request_data.key_provided) {
|
||||
FURI_LOG_D(TAG, "ULC write: all keys exhausted");
|
||||
break;
|
||||
}
|
||||
instance->auth_context.tdes_key = instance->mfu_event.data->key_request_data.key;
|
||||
|
||||
// Halt+activate so the card is in a clean state for auth
|
||||
iso14443_3a_poller_halt(instance->iso14443_3a_poller);
|
||||
if(iso14443_3a_poller_activate(instance->iso14443_3a_poller, NULL) !=
|
||||
Iso14443_3aErrorNone) {
|
||||
FURI_LOG_E(TAG, "ULC write: card not responding (locked out?)");
|
||||
break;
|
||||
}
|
||||
|
||||
uint8_t output[MF_ULTRALIGHT_C_AUTH_DATA_SIZE];
|
||||
uint8_t RndA[MF_ULTRALIGHT_C_AUTH_RND_BLOCK_SIZE] = {0};
|
||||
furi_hal_random_fill_buf(RndA, sizeof(RndA));
|
||||
if(mf_ultralight_poller_authenticate_start(instance, RndA, output) !=
|
||||
MfUltralightErrorNone) {
|
||||
break;
|
||||
}
|
||||
uint8_t decoded_RndA[MF_ULTRALIGHT_C_AUTH_RND_BLOCK_SIZE] = {0};
|
||||
const uint8_t* RndB = output + MF_ULTRALIGHT_C_AUTH_RND_B_BLOCK_OFFSET;
|
||||
if(mf_ultralight_poller_authenticate_end(instance, RndB, output, decoded_RndA) !=
|
||||
MfUltralightErrorNone)
|
||||
continue;
|
||||
mf_ultralight_3des_shift_data(RndA);
|
||||
auth_ok = (memcmp(RndA, decoded_RndA, sizeof(decoded_RndA)) == 0);
|
||||
FURI_LOG_D(TAG, "ULC write auth attempt: %s", auth_ok ? "success" : "fail");
|
||||
}
|
||||
|
||||
if(auth_ok) {
|
||||
// Notify callback with the found key: it caches it and returns the skip decision
|
||||
MfUltralightC3DesAuthKey found_key = instance->auth_context.tdes_key;
|
||||
memset(instance->mfu_event.data, 0, sizeof(MfUltralightPollerEventData));
|
||||
instance->mfu_event.data->key_request_data.key = found_key;
|
||||
instance->mfu_event.data->key_request_data.key_provided = true;
|
||||
instance->mfu_event.type = MfUltralightPollerEventTypeWriteKeyRequest;
|
||||
instance->callback(instance->general_event, instance->context);
|
||||
instance->write_skip_key = instance->mfu_event.data->write_key_skip;
|
||||
FURI_LOG_D(
|
||||
TAG,
|
||||
"ULC write: key %s",
|
||||
instance->write_skip_key ? "kept (target unchanged)" : "overwrite with source");
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "ULC write auth failed - card locked");
|
||||
check_passed = false;
|
||||
instance->mfu_event.type = MfUltralightPollerEventTypeCardLocked;
|
||||
}
|
||||
}
|
||||
|
||||
if(!check_passed) {
|
||||
iso14443_3a_poller_halt(instance->iso14443_3a_poller);
|
||||
command = instance->callback(instance->general_event, instance->context);
|
||||
@@ -751,15 +820,52 @@ static NfcCommand mf_ultralight_poller_handler_write_pages(MfUltralightPoller* i
|
||||
NfcCommand command = NfcCommandContinue;
|
||||
|
||||
do {
|
||||
const MfUltralightData* write_data = instance->mfu_event.data->write_data;
|
||||
// Use the saved write_data pointer - the union was overwritten by WriteKeyRequest
|
||||
const MfUltralightData* write_data = instance->write_data;
|
||||
uint8_t end_page = mf_ultralight_get_write_end_page(write_data->type);
|
||||
|
||||
// If user chose to keep target's key, stop before the ULC key pages (44-47)
|
||||
if(instance->write_skip_key && write_data->type == MfUltralightTypeMfulC &&
|
||||
end_page > 44) {
|
||||
end_page = 44;
|
||||
}
|
||||
|
||||
if(instance->current_page == end_page) {
|
||||
instance->state = MfUltralightPollerStateWriteSuccess;
|
||||
break;
|
||||
}
|
||||
FURI_LOG_D(TAG, "Writing page %d", instance->current_page);
|
||||
MfUltralightError error = mf_ultralight_poller_write_page(
|
||||
instance, instance->current_page, &write_data->page[instance->current_page]);
|
||||
|
||||
// For ULC key pages (44-47): byte-order correction required.
|
||||
// Flipper stores each 8-byte DES sub-key MSB-first; the card expects each half reversed.
|
||||
// Transform: card_bytes = reverse(flipper[0..7]) || reverse(flipper[8..15])
|
||||
MfUltralightPage page_to_write;
|
||||
if(instance->current_page >= 44 && instance->current_page <= 47 &&
|
||||
write_data->type == MfUltralightTypeMfulC) {
|
||||
const uint8_t* raw = (const uint8_t*)&write_data->page[44];
|
||||
uint8_t xformed[16];
|
||||
for(int i = 0; i < 8; i++) {
|
||||
xformed[i] = raw[7 - i];
|
||||
}
|
||||
for(int i = 0; i < 8; i++) {
|
||||
xformed[8 + i] = raw[15 - i];
|
||||
}
|
||||
uint8_t page_offset = (instance->current_page - 44) * 4;
|
||||
memcpy(page_to_write.data, xformed + page_offset, 4);
|
||||
FURI_LOG_D(
|
||||
TAG,
|
||||
"Writing KEY page %d (byte-order corrected): %02X %02X %02X %02X",
|
||||
instance->current_page,
|
||||
page_to_write.data[0],
|
||||
page_to_write.data[1],
|
||||
page_to_write.data[2],
|
||||
page_to_write.data[3]);
|
||||
} else {
|
||||
page_to_write = write_data->page[instance->current_page];
|
||||
FURI_LOG_D(TAG, "Writing page %d", instance->current_page);
|
||||
}
|
||||
|
||||
MfUltralightError error =
|
||||
mf_ultralight_poller_write_page(instance, instance->current_page, &page_to_write);
|
||||
if(error != MfUltralightErrorNone) {
|
||||
instance->state = MfUltralightPollerStateWriteFail;
|
||||
instance->error = error;
|
||||
|
||||
@@ -28,6 +28,7 @@ typedef enum {
|
||||
MfUltralightPollerEventTypeWriteSuccess, /**< Poller wrote card successfully. */
|
||||
MfUltralightPollerEventTypeWriteFail, /**< Poller failed to write card. */
|
||||
MfUltralightPollerEventTypeRequestKey, /**< Poller requests key for dict attack. */
|
||||
MfUltralightPollerEventTypeWriteKeyRequest, /**< Poller asks user whether to overwrite 3DES key on target. */
|
||||
} MfUltralightPollerEventType;
|
||||
|
||||
/**
|
||||
@@ -67,6 +68,7 @@ typedef union {
|
||||
const MfUltralightData* write_data; /**< Data to be written to card. */
|
||||
MfUltralightPollerMode poller_mode; /**< Mode to operate in. */
|
||||
MfUltralightPollerKeyRequestData key_request_data; /**< Key request data. */
|
||||
bool write_key_skip; /**< Set to true by callback to skip writing 3DES key pages. */
|
||||
} MfUltralightPollerEventData;
|
||||
|
||||
/**
|
||||
|
||||
@@ -88,6 +88,8 @@ struct MfUltralightPoller {
|
||||
uint8_t tearing_flag_read;
|
||||
uint8_t tearing_flag_total;
|
||||
uint16_t current_page;
|
||||
bool write_skip_key; // If true, skip writing pages 44-47 (3DES key) during ULC write
|
||||
const MfUltralightData* write_data; // Saved pointer to source data for write phase
|
||||
MfUltralightError error;
|
||||
mbedtls_des3_context des_context;
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include "base.h"
|
||||
#include <flipper_format/flipper_format.h>
|
||||
|
||||
#define FIAT_MARELLI_PROTOCOL_NAME "Fiat Marelli"
|
||||
#define FIAT_MARELLI_PROTOCOL_NAME "MARELLI"
|
||||
|
||||
typedef struct SubGhzProtocolDecoderFiatMarelli SubGhzProtocolDecoderFiatMarelli;
|
||||
typedef struct SubGhzProtocolEncoderFiatMarelli SubGhzProtocolEncoderFiatMarelli;
|
||||
|
||||
@@ -1055,10 +1055,10 @@ static uint32_t subghz_protocol_keeloq_check_remote_controller_selector(
|
||||
case KEELOQ_LEARNING_SECURE:
|
||||
bool reset_seed_back = false;
|
||||
if((strcmp(furi_string_get_cstr(manufacture_code->name), "BFT") == 0)) {
|
||||
if(instance->seed == 0) {
|
||||
instance->seed = (fix & 0xFFFFFFF);
|
||||
reset_seed_back = true;
|
||||
}
|
||||
//if(instance->seed == 0) {
|
||||
instance->seed = (fix & 0xFFFFFFF);
|
||||
reset_seed_back = true;
|
||||
//}
|
||||
}
|
||||
man = subghz_protocol_keeloq_common_secure_learning(
|
||||
fix, instance->seed, manufacture_code->key);
|
||||
|
||||
Reference in New Issue
Block a user