Merge remote-tracking branch 'noproto/dev' into ulcdict

This commit is contained in:
noproto
2024-10-28 21:24:23 -04:00
71 changed files with 701 additions and 3012 deletions

View File

@@ -7,7 +7,7 @@ App(
icon="A_BadUsb_14",
order=70,
resources="resources",
fap_libs=["assets"],
fap_libs=["assets", "ble_profile"],
fap_icon="icon.png",
fap_category="USB",
)

View File

@@ -35,6 +35,7 @@ static void bad_usb_load_settings(BadUsbApp* app) {
FuriString* temp_str = furi_string_alloc();
uint32_t version = 0;
uint32_t interface = 0;
if(flipper_format_file_open_existing(fff, BAD_USB_SETTINGS_PATH)) {
do {
@@ -44,6 +45,8 @@ static void bad_usb_load_settings(BadUsbApp* app) {
break;
if(!flipper_format_read_string(fff, "layout", temp_str)) break;
if(!flipper_format_read_uint32(fff, "interface", &interface, 1)) break;
if(interface > BadUsbHidInterfaceBle) break;
state = true;
} while(0);
@@ -53,6 +56,7 @@ static void bad_usb_load_settings(BadUsbApp* app) {
if(state) {
furi_string_set(app->keyboard_layout, temp_str);
app->interface = interface;
Storage* fs_api = furi_record_open(RECORD_STORAGE);
FileInfo layout_file_info;
@@ -64,6 +68,7 @@ static void bad_usb_load_settings(BadUsbApp* app) {
}
} else {
furi_string_set(app->keyboard_layout, BAD_USB_SETTINGS_DEFAULT_LAYOUT);
app->interface = BadUsbHidInterfaceUsb;
}
furi_string_free(temp_str);
@@ -79,6 +84,9 @@ static void bad_usb_save_settings(BadUsbApp* app) {
fff, BAD_USB_SETTINGS_FILE_TYPE, BAD_USB_SETTINGS_VERSION))
break;
if(!flipper_format_write_string(fff, "layout", app->keyboard_layout)) break;
uint32_t interface_id = app->interface;
if(!flipper_format_write_uint32(fff, "interface", (const uint32_t*)&interface_id, 1))
break;
} while(0);
}
@@ -86,6 +94,11 @@ static void bad_usb_save_settings(BadUsbApp* app) {
furi_record_close(RECORD_STORAGE);
}
void bad_usb_set_interface(BadUsbApp* app, BadUsbHidInterface interface) {
app->interface = interface;
bad_usb_view_set_interface(app->bad_usb_view, interface);
}
BadUsbApp* bad_usb_app_alloc(char* arg) {
BadUsbApp* app = malloc(sizeof(BadUsbApp));
@@ -117,7 +130,11 @@ BadUsbApp* bad_usb_app_alloc(char* arg) {
// Custom Widget
app->widget = widget_alloc();
view_dispatcher_add_view(
app->view_dispatcher, BadUsbAppViewError, widget_get_view(app->widget));
app->view_dispatcher, BadUsbAppViewWidget, widget_get_view(app->widget));
// Popup
app->popup = popup_alloc();
view_dispatcher_add_view(app->view_dispatcher, BadUsbAppViewPopup, popup_get_view(app->popup));
app->var_item_list = variable_item_list_alloc();
view_dispatcher_add_view(
@@ -163,9 +180,13 @@ void bad_usb_app_free(BadUsbApp* app) {
bad_usb_view_free(app->bad_usb_view);
// Custom Widget
view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewError);
view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewWidget);
widget_free(app->widget);
// Popup
view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewPopup);
popup_free(app->popup);
// Config menu
view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewConfig);
variable_item_list_free(app->var_item_list);

View File

@@ -13,6 +13,7 @@
#include <notification/notification_messages.h>
#include <gui/modules/variable_item_list.h>
#include <gui/modules/widget.h>
#include <gui/modules/popup.h>
#include "views/bad_usb_view.h"
#include <furi_hal_usb.h>
@@ -33,6 +34,7 @@ struct BadUsbApp {
NotificationApp* notifications;
DialogsApp* dialogs;
Widget* widget;
Popup* popup;
VariableItemList* var_item_list;
BadUsbAppError error;
@@ -41,11 +43,15 @@ struct BadUsbApp {
BadUsb* bad_usb_view;
BadUsbScript* bad_usb_script;
BadUsbHidInterface interface;
FuriHalUsbInterface* usb_if_prev;
};
typedef enum {
BadUsbAppViewError,
BadUsbAppViewWidget,
BadUsbAppViewPopup,
BadUsbAppViewWork,
BadUsbAppViewConfig,
} BadUsbAppView;
void bad_usb_set_interface(BadUsbApp* app, BadUsbHidInterface interface);

View File

@@ -1,9 +1,12 @@
#include "bad_usb_hid.h"
#include <extra_profiles/hid_profile.h>
#include <bt/bt_service/bt.h>
#include <storage/storage.h>
#define TAG "BadUSB HID"
#define HID_BT_KEYS_STORAGE_NAME ".bt_hid.keys"
void* hid_usb_init(FuriHalUsbHidConfig* hid_cfg) {
furi_check(furi_hal_usb_set_config(&usb_hid, hid_cfg));
return NULL;
@@ -69,6 +72,155 @@ static const BadUsbHidApi hid_api_usb = {
.release_all = hid_usb_release_all,
.get_led_state = hid_usb_get_led_state,
};
const BadUsbHidApi* bad_usb_hid_get_interface() {
return &hid_api_usb;
typedef struct {
Bt* bt;
FuriHalBleProfileBase* profile;
HidStateCallback state_callback;
void* callback_context;
bool is_connected;
} BleHidInstance;
static const BleProfileHidParams ble_hid_params = {
.device_name_prefix = "BadUSB",
.mac_xor = 0x0002,
};
static void hid_ble_connection_status_callback(BtStatus status, void* context) {
furi_assert(context);
BleHidInstance* ble_hid = context;
ble_hid->is_connected = (status == BtStatusConnected);
if(ble_hid->state_callback) {
ble_hid->state_callback(ble_hid->is_connected, ble_hid->callback_context);
}
}
void* hid_ble_init(FuriHalUsbHidConfig* hid_cfg) {
UNUSED(hid_cfg);
BleHidInstance* ble_hid = malloc(sizeof(BleHidInstance));
ble_hid->bt = furi_record_open(RECORD_BT);
bt_disconnect(ble_hid->bt);
// Wait 2nd core to update nvm storage
furi_delay_ms(200);
bt_keys_storage_set_storage_path(ble_hid->bt, APP_DATA_PATH(HID_BT_KEYS_STORAGE_NAME));
ble_hid->profile = bt_profile_start(ble_hid->bt, ble_profile_hid, (void*)&ble_hid_params);
furi_check(ble_hid->profile);
furi_hal_bt_start_advertising();
bt_set_status_changed_callback(ble_hid->bt, hid_ble_connection_status_callback, ble_hid);
return ble_hid;
}
void hid_ble_deinit(void* inst) {
BleHidInstance* ble_hid = inst;
furi_assert(ble_hid);
bt_set_status_changed_callback(ble_hid->bt, NULL, NULL);
bt_disconnect(ble_hid->bt);
// Wait 2nd core to update nvm storage
furi_delay_ms(200);
bt_keys_storage_set_default_path(ble_hid->bt);
furi_check(bt_profile_restore_default(ble_hid->bt));
furi_record_close(RECORD_BT);
free(ble_hid);
}
void hid_ble_set_state_callback(void* inst, HidStateCallback cb, void* context) {
BleHidInstance* ble_hid = inst;
furi_assert(ble_hid);
ble_hid->state_callback = cb;
ble_hid->callback_context = context;
}
bool hid_ble_is_connected(void* inst) {
BleHidInstance* ble_hid = inst;
furi_assert(ble_hid);
return ble_hid->is_connected;
}
bool hid_ble_kb_press(void* inst, uint16_t button) {
BleHidInstance* ble_hid = inst;
furi_assert(ble_hid);
return ble_profile_hid_kb_press(ble_hid->profile, button);
}
bool hid_ble_kb_release(void* inst, uint16_t button) {
BleHidInstance* ble_hid = inst;
furi_assert(ble_hid);
return ble_profile_hid_kb_release(ble_hid->profile, button);
}
bool hid_ble_consumer_press(void* inst, uint16_t button) {
BleHidInstance* ble_hid = inst;
furi_assert(ble_hid);
return ble_profile_hid_consumer_key_press(ble_hid->profile, button);
}
bool hid_ble_consumer_release(void* inst, uint16_t button) {
BleHidInstance* ble_hid = inst;
furi_assert(ble_hid);
return ble_profile_hid_consumer_key_release(ble_hid->profile, button);
}
bool hid_ble_release_all(void* inst) {
BleHidInstance* ble_hid = inst;
furi_assert(ble_hid);
bool state = ble_profile_hid_kb_release_all(ble_hid->profile);
state &= ble_profile_hid_consumer_key_release_all(ble_hid->profile);
return state;
}
uint8_t hid_ble_get_led_state(void* inst) {
UNUSED(inst);
FURI_LOG_W(TAG, "hid_ble_get_led_state not implemented");
return 0;
}
static const BadUsbHidApi hid_api_ble = {
.init = hid_ble_init,
.deinit = hid_ble_deinit,
.set_state_callback = hid_ble_set_state_callback,
.is_connected = hid_ble_is_connected,
.kb_press = hid_ble_kb_press,
.kb_release = hid_ble_kb_release,
.consumer_press = hid_ble_consumer_press,
.consumer_release = hid_ble_consumer_release,
.release_all = hid_ble_release_all,
.get_led_state = hid_ble_get_led_state,
};
const BadUsbHidApi* bad_usb_hid_get_interface(BadUsbHidInterface interface) {
if(interface == BadUsbHidInterfaceUsb) {
return &hid_api_usb;
} else {
return &hid_api_ble;
}
}
void bad_usb_hid_ble_remove_pairing(void) {
Bt* bt = furi_record_open(RECORD_BT);
bt_disconnect(bt);
// Wait 2nd core to update nvm storage
furi_delay_ms(200);
furi_hal_bt_stop_advertising();
bt_keys_storage_set_storage_path(bt, APP_DATA_PATH(HID_BT_KEYS_STORAGE_NAME));
bt_forget_bonded_devices(bt);
// Wait 2nd core to update nvm storage
furi_delay_ms(200);
bt_keys_storage_set_default_path(bt);
furi_check(bt_profile_restore_default(bt));
furi_record_close(RECORD_BT);
}

View File

@@ -7,6 +7,11 @@ extern "C" {
#include <furi.h>
#include <furi_hal.h>
typedef enum {
BadUsbHidInterfaceUsb,
BadUsbHidInterfaceBle,
} BadUsbHidInterface;
typedef struct {
void* (*init)(FuriHalUsbHidConfig* hid_cfg);
void (*deinit)(void* inst);
@@ -21,7 +26,7 @@ typedef struct {
uint8_t (*get_led_state)(void* inst);
} BadUsbHidApi;
const BadUsbHidApi* bad_usb_hid_get_interface();
const BadUsbHidApi* bad_usb_hid_get_interface(BadUsbHidInterface interface);
void bad_usb_hid_ble_remove_pairing(void);

View File

@@ -650,7 +650,7 @@ static void bad_usb_script_set_default_keyboard_layout(BadUsbScript* bad_usb) {
memcpy(bad_usb->layout, hid_asciimap, MIN(sizeof(hid_asciimap), sizeof(bad_usb->layout)));
}
BadUsbScript* bad_usb_script_open(FuriString* file_path) {
BadUsbScript* bad_usb_script_open(FuriString* file_path, BadUsbHidInterface interface) {
furi_assert(file_path);
BadUsbScript* bad_usb = malloc(sizeof(BadUsbScript));
@@ -660,7 +660,7 @@ BadUsbScript* bad_usb_script_open(FuriString* file_path) {
bad_usb->st.state = BadUsbStateInit;
bad_usb->st.error[0] = '\0';
bad_usb->hid = bad_usb_hid_get_interface();
bad_usb->hid = bad_usb_hid_get_interface(interface);
bad_usb->thread = furi_thread_alloc_ex("BadUsbWorker", 2048, bad_usb_worker, bad_usb);
furi_thread_start(bad_usb->thread);

View File

@@ -34,7 +34,7 @@ typedef struct {
typedef struct BadUsbScript BadUsbScript;
BadUsbScript* bad_usb_script_open(FuriString* file_path);
BadUsbScript* bad_usb_script_open(FuriString* file_path, BadUsbHidInterface interface);
void bad_usb_script_close(BadUsbScript* bad_usb);

View File

@@ -1,12 +1,17 @@
REM This is BadUSB demo script for ChromeOS by kowalski7cc
REM This is BadUSB demo script for Chrome and ChromeOS by kowalski7cc
REM Exit from Overview
ESC
REM Open a new tab
CTRL t
REM wait for some slower chromebooks
DELAY 1000
REM Make sure we have omnibox focus
CTRL l
DELAY 200
REM Open an empty editable page
DEFAULT_DELAY 50
STRING data:text/html, <html contenteditable autofocus><style>body{font-family:monospace;}
STRING data:text/html, <html contenteditable autofocus><title>Flipper Zero BadUSB Demo</title><style>body{font-family:monospace;}
ENTER
DELAY 500

View File

@@ -5,14 +5,22 @@ REM Check the `lsusb` command to know your own devices IDs
REM This is BadUSB demo script for Linux/Gnome
REM Exit from Overview
ESC
DELAY 200
REM Open terminal window
DELAY 1000
ALT F2
DELAY 500
STRING gnome-terminal --maximize
DELAY 500
DELAY 1000
REM Let's guess user terminal, based on (almost) glib order with ptyxis now default in Fedora 41
STRING sh -c "xdg-terminal-exec||kgx||ptyxis||gnome-terminal||mate-terminal||xfce4-terminal||tilix||konsole||xterm"
DELAY 300
ENTER
REM It can take a bit to open the correct terminal
DELAY 1500
REM Make sure we are running in a POSIX-compliant shell
STRING env sh
ENTER
DELAY 750
REM Clear the screen in case some banner was displayed
STRING clear

View File

@@ -0,0 +1,59 @@
#include "../bad_usb_app_i.h"
enum SubmenuIndex {
ConfigIndexKeyboardLayout,
ConfigIndexBleUnpair,
};
void bad_usb_scene_config_select_callback(void* context, uint32_t index) {
BadUsbApp* bad_usb = context;
view_dispatcher_send_custom_event(bad_usb->view_dispatcher, index);
}
static void draw_menu(BadUsbApp* bad_usb) {
VariableItemList* var_item_list = bad_usb->var_item_list;
variable_item_list_reset(var_item_list);
variable_item_list_add(var_item_list, "Keyboard Layout (global)", 0, NULL, NULL);
variable_item_list_add(var_item_list, "Remove Pairing", 0, NULL, NULL);
}
void bad_usb_scene_config_on_enter(void* context) {
BadUsbApp* bad_usb = context;
VariableItemList* var_item_list = bad_usb->var_item_list;
variable_item_list_set_enter_callback(
var_item_list, bad_usb_scene_config_select_callback, bad_usb);
draw_menu(bad_usb);
variable_item_list_set_selected_item(var_item_list, 0);
view_dispatcher_switch_to_view(bad_usb->view_dispatcher, BadUsbAppViewConfig);
}
bool bad_usb_scene_config_on_event(void* context, SceneManagerEvent event) {
BadUsbApp* bad_usb = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
consumed = true;
if(event.event == ConfigIndexKeyboardLayout) {
scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneConfigLayout);
} else if(event.event == ConfigIndexBleUnpair) {
scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneConfirmUnpair);
} else {
furi_crash("Unknown key type");
}
}
return consumed;
}
void bad_usb_scene_config_on_exit(void* context) {
BadUsbApp* bad_usb = context;
VariableItemList* var_item_list = bad_usb->var_item_list;
variable_item_list_reset(var_item_list);
}

View File

@@ -1,4 +1,7 @@
ADD_SCENE(bad_usb, file_select, FileSelect)
ADD_SCENE(bad_usb, work, Work)
ADD_SCENE(bad_usb, error, Error)
ADD_SCENE(bad_usb, config, Config)
ADD_SCENE(bad_usb, config_layout, ConfigLayout)
ADD_SCENE(bad_usb, confirm_unpair, ConfirmUnpair)
ADD_SCENE(bad_usb, unpair_done, UnpairDone)

View File

@@ -0,0 +1,53 @@
#include "../bad_usb_app_i.h"
void bad_usb_scene_confirm_unpair_widget_callback(
GuiButtonType type,
InputType input_type,
void* context) {
UNUSED(input_type);
SceneManagerEvent event = {.type = SceneManagerEventTypeCustom, .event = type};
bad_usb_scene_confirm_unpair_on_event(context, event);
}
void bad_usb_scene_confirm_unpair_on_enter(void* context) {
BadUsbApp* bad_usb = context;
Widget* widget = bad_usb->widget;
widget_add_button_element(
widget, GuiButtonTypeLeft, "Cancel", bad_usb_scene_confirm_unpair_widget_callback, context);
widget_add_button_element(
widget,
GuiButtonTypeRight,
"Unpair",
bad_usb_scene_confirm_unpair_widget_callback,
context);
widget_add_text_box_element(
widget, 0, 0, 128, 64, AlignCenter, AlignTop, "\e#Unpair the Device?\e#\n", false);
view_dispatcher_switch_to_view(bad_usb->view_dispatcher, BadUsbAppViewWidget);
}
bool bad_usb_scene_confirm_unpair_on_event(void* context, SceneManagerEvent event) {
BadUsbApp* bad_usb = context;
SceneManager* scene_manager = bad_usb->scene_manager;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
consumed = true;
if(event.event == GuiButtonTypeRight) {
scene_manager_next_scene(scene_manager, BadUsbSceneUnpairDone);
} else if(event.event == GuiButtonTypeLeft) {
scene_manager_previous_scene(scene_manager);
}
}
return consumed;
}
void bad_usb_scene_confirm_unpair_on_exit(void* context) {
BadUsbApp* bad_usb = context;
Widget* widget = bad_usb->widget;
widget_reset(widget);
}

View File

@@ -43,7 +43,7 @@ void bad_usb_scene_error_on_enter(void* context) {
"Disconnect from\nPC or phone to\nuse this function.");
}
view_dispatcher_switch_to_view(app->view_dispatcher, BadUsbAppViewError);
view_dispatcher_switch_to_view(app->view_dispatcher, BadUsbAppViewWidget);
}
bool bad_usb_scene_error_on_event(void* context, SceneManagerEvent event) {

View File

@@ -0,0 +1,39 @@
#include "../bad_usb_app_i.h"
static void bad_usb_scene_unpair_done_popup_callback(void* context) {
BadUsbApp* bad_usb = context;
scene_manager_search_and_switch_to_previous_scene(bad_usb->scene_manager, BadUsbSceneConfig);
}
void bad_usb_scene_unpair_done_on_enter(void* context) {
BadUsbApp* bad_usb = context;
Popup* popup = bad_usb->popup;
bad_usb_hid_ble_remove_pairing();
popup_set_icon(popup, 48, 4, &I_DolphinDone_80x58);
popup_set_header(popup, "Done", 20, 19, AlignLeft, AlignBottom);
popup_set_callback(popup, bad_usb_scene_unpair_done_popup_callback);
popup_set_context(popup, bad_usb);
popup_set_timeout(popup, 1500);
popup_enable_timeout(popup);
view_dispatcher_switch_to_view(bad_usb->view_dispatcher, BadUsbAppViewPopup);
}
bool bad_usb_scene_unpair_done_on_event(void* context, SceneManagerEvent event) {
BadUsbApp* bad_usb = context;
UNUSED(bad_usb);
UNUSED(event);
bool consumed = false;
return consumed;
}
void bad_usb_scene_unpair_done_on_exit(void* context) {
BadUsbApp* bad_usb = context;
Popup* popup = bad_usb->popup;
UNUSED(popup);
popup_reset(popup);
}

View File

@@ -20,14 +20,27 @@ bool bad_usb_scene_work_on_event(void* context, SceneManagerEvent event) {
bad_usb_script_close(app->bad_usb_script);
app->bad_usb_script = NULL;
scene_manager_next_scene(app->scene_manager, BadUsbSceneConfigLayout);
if(app->interface == BadUsbHidInterfaceBle) {
scene_manager_next_scene(app->scene_manager, BadUsbSceneConfig);
} else {
scene_manager_next_scene(app->scene_manager, BadUsbSceneConfigLayout);
}
}
consumed = true;
} else if(event.event == InputKeyOk) {
bad_usb_script_start_stop(app->bad_usb_script);
consumed = true;
} else if(event.event == InputKeyRight) {
bad_usb_script_pause_resume(app->bad_usb_script);
if(bad_usb_view_is_idle_state(app->bad_usb_view)) {
bad_usb_set_interface(
app,
app->interface == BadUsbHidInterfaceBle ? BadUsbHidInterfaceUsb :
BadUsbHidInterfaceBle);
bad_usb_script_close(app->bad_usb_script);
app->bad_usb_script = bad_usb_script_open(app->file_path, app->interface);
} else {
bad_usb_script_pause_resume(app->bad_usb_script);
}
consumed = true;
}
} else if(event.type == SceneManagerEventTypeTick) {
@@ -39,7 +52,9 @@ bool bad_usb_scene_work_on_event(void* context, SceneManagerEvent event) {
void bad_usb_scene_work_on_enter(void* context) {
BadUsbApp* app = context;
app->bad_usb_script = bad_usb_script_open(app->file_path);
bad_usb_view_set_interface(app->bad_usb_view, app->interface);
app->bad_usb_script = bad_usb_script_open(app->file_path, app->interface);
bad_usb_script_set_keyboard_layout(app->bad_usb_script, app->keyboard_layout);
FuriString* file_name;

View File

@@ -18,6 +18,7 @@ typedef struct {
BadUsbState state;
bool pause_wait;
uint8_t anim_frame;
BadUsbHidInterface interface;
} BadUsbModel;
static void bad_usb_draw_callback(Canvas* canvas, void* _model) {
@@ -40,14 +41,24 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) {
furi_string_reset(disp_str);
canvas_draw_icon(canvas, 22, 24, &I_UsbTree_48x22);
if(model->interface == BadUsbHidInterfaceBle) {
canvas_draw_icon(canvas, 22, 24, &I_Bad_BLE_48x22);
} else {
canvas_draw_icon(canvas, 22, 24, &I_UsbTree_48x22);
}
BadUsbWorkerState state = model->state.state;
if((state == BadUsbStateIdle) || (state == BadUsbStateDone) ||
(state == BadUsbStateNotConnected)) {
elements_button_center(canvas, "Run");
elements_button_left(canvas, "Layout");
if(model->interface == BadUsbHidInterfaceBle) {
elements_button_right(canvas, "USB");
elements_button_left(canvas, "Config");
} else {
elements_button_right(canvas, "BLE");
elements_button_left(canvas, "Layout");
}
} else if((state == BadUsbStateRunning) || (state == BadUsbStateDelay)) {
elements_button_center(canvas, "Stop");
if(!model->pause_wait) {
@@ -266,6 +277,10 @@ void bad_usb_view_set_state(BadUsb* bad_usb, BadUsbState* st) {
true);
}
void bad_usb_view_set_interface(BadUsb* bad_usb, BadUsbHidInterface interface) {
with_view_model(bad_usb->view, BadUsbModel * model, { model->interface = interface; }, true);
}
bool bad_usb_view_is_idle_state(BadUsb* bad_usb) {
bool is_idle = false;
with_view_model(

View File

@@ -23,4 +23,6 @@ void bad_usb_view_set_layout(BadUsb* bad_usb, const char* layout);
void bad_usb_view_set_state(BadUsb* bad_usb, BadUsbState* st);
void bad_usb_view_set_interface(BadUsb* bad_usb, BadUsbHidInterface interface);
bool bad_usb_view_is_idle_state(BadUsb* bad_usb);

View File

@@ -209,6 +209,15 @@ App(
sources=["plugins/supported_cards/hworld.c"],
)
App(
appid="trt_parser",
apptype=FlipperAppType.PLUGIN,
entry_point="trt_plugin_ep",
targets=["f7"],
requires=["nfc"],
sources=["plugins/supported_cards/trt.c"],
)
App(
appid="nfc_start",
targets=["f7"],

View File

@@ -0,0 +1,97 @@
// Flipper Zero parser for for Tianjin Railway Transit (TRT)
// https://en.wikipedia.org/wiki/Tianjin_Metro
// Reverse engineering and parser development by @Torron (Github: @zinongli)
#include "nfc_supported_card_plugin.h"
#include <flipper_application.h>
#include <nfc/protocols/mf_ultralight/mf_ultralight.h>
#include <bit_lib.h>
#define TAG "TrtParser"
#define LATEST_SALE_MARKER 0x02
#define FULL_SALE_TIME_STAMP_PAGE 0x09
#define BALANCE_PAGE 0x08
#define SALE_RECORD_TIME_STAMP_A 0x0C
#define SALE_RECORD_TIME_STAMP_B 0x0E
#define SALE_YEAR_OFFSET 2000
static bool trt_parse(const NfcDevice* device, FuriString* parsed_data) {
furi_assert(device);
furi_assert(parsed_data);
const MfUltralightData* data = nfc_device_get_data(device, NfcProtocolMfUltralight);
bool parsed = false;
do {
uint8_t latest_sale_page = 0;
// Look for sale record signature
if(data->page[SALE_RECORD_TIME_STAMP_A].data[0] == LATEST_SALE_MARKER) {
latest_sale_page = SALE_RECORD_TIME_STAMP_A;
} else if(data->page[SALE_RECORD_TIME_STAMP_B].data[0] == LATEST_SALE_MARKER) {
latest_sale_page = SALE_RECORD_TIME_STAMP_B;
} else {
break;
}
// Check if the sale record was backed up
const uint8_t* partial_record_pointer = &data->page[latest_sale_page - 1].data[0];
const uint8_t* full_record_pointer = &data->page[FULL_SALE_TIME_STAMP_PAGE].data[0];
uint32_t latest_sale_record = bit_lib_get_bits_32(partial_record_pointer, 3, 20);
uint32_t latest_sale_full_record = bit_lib_get_bits_32(full_record_pointer, 0, 27);
if(latest_sale_record != (latest_sale_full_record & 0xFFFFF))
break; // check if the copy matches
if((latest_sale_record == 0) || (latest_sale_full_record == 0))
break; // prevent false positive
// Parse date
// yyy yyyymmmm dddddhhh hhnnnnnn
uint16_t sale_year = ((latest_sale_full_record & 0x7F00000) >> 20) + SALE_YEAR_OFFSET;
uint8_t sale_month = (latest_sale_full_record & 0xF0000) >> 16;
uint8_t sale_day = (latest_sale_full_record & 0xF800) >> 11;
uint8_t sale_hour = (latest_sale_full_record & 0x7C0) >> 6;
uint8_t sale_minute = latest_sale_full_record & 0x3F;
// Parse balance
uint16_t balance = bit_lib_get_bits_16(&data->page[BALANCE_PAGE].data[2], 0, 16);
uint16_t balance_yuan = balance / 100;
uint8_t balance_cent = balance % 100;
// Format string for rendering
furi_string_cat_printf(parsed_data, "\e#TRT Tianjin Metro\n");
furi_string_cat_printf(parsed_data, "Single-Use Ticket\n");
furi_string_cat_printf(parsed_data, "Balance: %u.%02u RMB\n", balance_yuan, balance_cent);
furi_string_cat_printf(
parsed_data,
"Sale Date: \n%04u-%02d-%02d %02d:%02d",
sale_year,
sale_month,
sale_day,
sale_hour,
sale_minute);
parsed = true;
} while(false);
return parsed;
}
/* Actual implementation of app<>plugin interface */
static const NfcSupportedCardsPlugin trt_plugin = {
.protocol = NfcProtocolMfUltralight,
.verify = NULL,
.read = NULL,
.parse = trt_parse,
};
/* Plugin descriptor to comply with basic plugin specification */
static const FlipperAppPluginDescriptor trt_plugin_descriptor = {
.appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID,
.ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION,
.entry_point = &trt_plugin,
};
/* Plugin entry point - must return a pointer to const descriptor */
const FlipperAppPluginDescriptor* trt_plugin_ep(void) {
return &trt_plugin_descriptor;
}

View File

@@ -1351,3 +1351,8 @@ E69DD9015A43
C8382A233993
7B304F2A12A6
FC9418BF788B
# H World Hotel Chain Room Keys
543071543071
5F01015F0101
200510241234

View File

@@ -45,6 +45,7 @@ static void dict_attack_draw_callback(Canvas* canvas, void* model) {
furi_string_set(m->header, "PRNG Analysis");
break;
case MfClassicNestedPhaseDictAttack:
case MfClassicNestedPhaseDictAttackVerify:
case MfClassicNestedPhaseDictAttackResume:
furi_string_set(m->header, "Nested Dictionary");
break;
@@ -91,6 +92,7 @@ static void dict_attack_draw_callback(Canvas* canvas, void* model) {
float dict_progress = 0;
if(m->nested_phase == MfClassicNestedPhaseAnalyzePRNG ||
m->nested_phase == MfClassicNestedPhaseDictAttack ||
m->nested_phase == MfClassicNestedPhaseDictAttackVerify ||
m->nested_phase == MfClassicNestedPhaseDictAttackResume) {
// Phase: Nested dictionary attack
uint8_t target_sector =

View File

@@ -999,12 +999,13 @@ static void subghz_cli_command_chat(Cli* cli, FuriString* args) {
chat_event = subghz_chat_worker_get_event_chat(subghz_chat);
switch(chat_event.event) {
case SubGhzChatEventInputData:
if(chat_event.c == CliKeyETX) {
if(chat_event.c == CliSymbolAsciiETX) {
printf("\r\n");
chat_event.event = SubGhzChatEventUserExit;
subghz_chat_worker_put_event_chat(subghz_chat, &chat_event);
break;
} else if((chat_event.c == CliKeyBackspace) || (chat_event.c == CliKeyDEL)) {
} else if(
(chat_event.c == CliSymbolAsciiBackspace) || (chat_event.c == CliSymbolAsciiDel)) {
size_t len = furi_string_utf8_length(input);
if(len > furi_string_utf8_length(name)) {
printf("%s", "\e[D\e[1P");
@@ -1026,7 +1027,7 @@ static void subghz_cli_command_chat(Cli* cli, FuriString* args) {
}
furi_string_set(input, sysmsg);
}
} else if(chat_event.c == CliKeyCR) {
} else if(chat_event.c == CliSymbolAsciiCR) {
printf("\r\n");
furi_string_push_back(input, '\r');
furi_string_push_back(input, '\n');
@@ -1040,7 +1041,7 @@ static void subghz_cli_command_chat(Cli* cli, FuriString* args) {
furi_string_printf(input, "%s", furi_string_get_cstr(name));
printf("%s", furi_string_get_cstr(input));
fflush(stdout);
} else if(chat_event.c == CliKeyLF) {
} else if(chat_event.c == CliSymbolAsciiLF) {
//cut out the symbol \n
} else {
putc(chat_event.c, stdout);

View File

@@ -1,6 +1,5 @@
#pragma once
#include <cli/cli.h>
#include <cli/cli_ansi.h>
void subghz_on_system_start(void);