[FL-2759], [FL-2766] NFC collect params for mfkey32 attack (#1643)
* nfc: start nfc over rpc * nfc: add detect reader state * nfc: add reader analyzer * nfc: rework reader analyzer * reader_analyzer: print collected nonces to debug * reader analyzer: add save on SD card * reader_analyzer: separate mfkey related part to different file * mfkey32: add logic for collecting parameters * nfc: rework pcap with reader analyzer * nfc: add logger for reader * nfc: clean up * nfc: add detect reader view * nfc: add detect reader and mfkey nonces scenes * nfc: add mfkey comlplete scene * nfc: add new assets * nfc: fix gui * nfc: fix iso14443-4 UID emulation * nfc: add no sd card notification * nfc: fix grammar Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
		
							parent
							
								
									ed2c607dd3
								
							
						
					
					
						commit
						1853359d78
					
				| @ -94,6 +94,11 @@ Nfc* nfc_alloc() { | ||||
|     view_dispatcher_add_view( | ||||
|         nfc->view_dispatcher, NfcViewDictAttack, dict_attack_get_view(nfc->dict_attack)); | ||||
| 
 | ||||
|     // Detect Reader
 | ||||
|     nfc->detect_reader = detect_reader_alloc(); | ||||
|     view_dispatcher_add_view( | ||||
|         nfc->view_dispatcher, NfcViewDetectReader, detect_reader_get_view(nfc->detect_reader)); | ||||
| 
 | ||||
|     // Generator
 | ||||
|     nfc->generator = NULL; | ||||
| 
 | ||||
| @ -158,6 +163,10 @@ void nfc_free(Nfc* nfc) { | ||||
|     view_dispatcher_remove_view(nfc->view_dispatcher, NfcViewDictAttack); | ||||
|     dict_attack_free(nfc->dict_attack); | ||||
| 
 | ||||
|     // Detect Reader
 | ||||
|     view_dispatcher_remove_view(nfc->view_dispatcher, NfcViewDetectReader); | ||||
|     detect_reader_free(nfc->detect_reader); | ||||
| 
 | ||||
|     // Worker
 | ||||
|     nfc_worker_stop(nfc->worker); | ||||
|     nfc_worker_free(nfc->worker); | ||||
|  | ||||
| @ -28,6 +28,7 @@ | ||||
| #include <lib/nfc/parsers/nfc_supported_card.h> | ||||
| 
 | ||||
| #include "views/dict_attack.h" | ||||
| #include "views/detect_reader.h" | ||||
| 
 | ||||
| #include <nfc/scenes/nfc_scene.h> | ||||
| #include <nfc/helpers/nfc_custom_event.h> | ||||
| @ -71,6 +72,7 @@ struct Nfc { | ||||
|     TextBox* text_box; | ||||
|     Widget* widget; | ||||
|     DictAttack* dict_attack; | ||||
|     DetectReader* detect_reader; | ||||
| 
 | ||||
|     const NfcGenerator* generator; | ||||
| }; | ||||
| @ -85,6 +87,7 @@ typedef enum { | ||||
|     NfcViewTextBox, | ||||
|     NfcViewWidget, | ||||
|     NfcViewDictAttack, | ||||
|     NfcViewDetectReader, | ||||
| } NfcView; | ||||
| 
 | ||||
| Nfc* nfc_alloc(); | ||||
|  | ||||
| @ -48,4 +48,6 @@ ADD_SCENE(nfc, rpc, Rpc) | ||||
| ADD_SCENE(nfc, exit_confirm, ExitConfirm) | ||||
| ADD_SCENE(nfc, retry_confirm, RetryConfirm) | ||||
| ADD_SCENE(nfc, detect_reader, DetectReader) | ||||
| ADD_SCENE(nfc, mfkey_nonces_info, MfkeyNoncesInfo) | ||||
| ADD_SCENE(nfc, mfkey_complete, MfkeyComplete) | ||||
| ADD_SCENE(nfc, nfc_data_info, NfcDataInfo) | ||||
|  | ||||
| @ -1,126 +1,48 @@ | ||||
| #include "../nfc_i.h" | ||||
| #include <dolphin/dolphin.h> | ||||
| 
 | ||||
| #define NFC_SCENE_DETECT_READER_LOG_SIZE_MAX (200) | ||||
| 
 | ||||
| enum { | ||||
|     NfcSceneDetectReaderStateWidget, | ||||
|     NfcSceneDetectReaderStateTextBox, | ||||
| }; | ||||
| 
 | ||||
| bool nfc_detect_reader_worker_callback(NfcWorkerEvent event, void* context) { | ||||
|     UNUSED(event); | ||||
|     furi_assert(context); | ||||
|     Nfc* nfc = context; | ||||
|     view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventWorkerExit); | ||||
|     view_dispatcher_send_custom_event(nfc->view_dispatcher, event); | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_detect_reader_widget_callback(GuiButtonType result, InputType type, void* context) { | ||||
|     furi_assert(context); | ||||
|     Nfc* nfc = context; | ||||
|     if(type == InputTypeShort) { | ||||
|         view_dispatcher_send_custom_event(nfc->view_dispatcher, result); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void nfc_detect_reader_textbox_callback(void* context) { | ||||
| void nfc_scene_detect_reader_callback(void* context) { | ||||
|     furi_assert(context); | ||||
|     Nfc* nfc = context; | ||||
|     view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); | ||||
| } | ||||
| 
 | ||||
| // Add widget with device name or inform that data received
 | ||||
| static void nfc_scene_detect_reader_widget_config(Nfc* nfc, bool data_received) { | ||||
|     Widget* widget = nfc->widget; | ||||
|     widget_reset(widget); | ||||
| 
 | ||||
|     widget_add_icon_element(widget, 0, 14, &I_Reader_detect); | ||||
|     widget_add_string_element( | ||||
|         widget, 64, 3, AlignCenter, AlignTop, FontSecondary, "Hold Near Reader"); | ||||
|     widget_add_string_element(widget, 55, 22, AlignLeft, AlignTop, FontPrimary, "Emulating..."); | ||||
| 
 | ||||
|     if(data_received) { | ||||
|         widget_add_button_element( | ||||
|             widget, GuiButtonTypeCenter, "Log", nfc_scene_detect_reader_widget_callback, nfc); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_detect_reader_on_enter(void* context) { | ||||
|     Nfc* nfc = context; | ||||
|     DOLPHIN_DEED(DolphinDeedNfcEmulate); | ||||
|     FuriHalNfcDevData nfc_params = { | ||||
|         .uid = {0x36, 0x9C, 0xe7, 0xb1, 0x0A, 0xC1, 0x34}, | ||||
|         .uid_len = 7, | ||||
|         .atqa = {0x44, 0x00}, | ||||
|         .sak = 0x08, | ||||
|         .type = FuriHalNfcTypeA, | ||||
|     }; | ||||
|     nfc->dev->dev_data.nfc_data = nfc_params; | ||||
| 
 | ||||
|     // Setup Widget
 | ||||
|     nfc_scene_detect_reader_widget_config(nfc, false); | ||||
|     // Setup TextBox
 | ||||
|     TextBox* text_box = nfc->text_box; | ||||
|     text_box_set_font(text_box, TextBoxFontHex); | ||||
|     text_box_set_focus(text_box, TextBoxFocusEnd); | ||||
|     string_reset(nfc->text_box_store); | ||||
| 
 | ||||
|     // Set Widget state and view
 | ||||
|     scene_manager_set_scene_state( | ||||
|         nfc->scene_manager, NfcSceneDetectReader, NfcSceneDetectReaderStateWidget); | ||||
|     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); | ||||
|     // Start worker
 | ||||
|     memset(&nfc->dev->dev_data.reader_data, 0, sizeof(NfcReaderRequestData)); | ||||
|     detect_reader_set_callback(nfc->detect_reader, nfc_scene_detect_reader_callback, nfc); | ||||
|     nfc_worker_start( | ||||
|         nfc->worker, | ||||
|         NfcWorkerStateUidEmulate, | ||||
|         NfcWorkerStateAnalyzeReader, | ||||
|         &nfc->dev->dev_data, | ||||
|         nfc_detect_reader_worker_callback, | ||||
|         nfc); | ||||
| 
 | ||||
|     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDetectReader); | ||||
| 
 | ||||
|     nfc_blink_start(nfc); | ||||
| } | ||||
| 
 | ||||
| bool nfc_scene_detect_reader_on_event(void* context, SceneManagerEvent event) { | ||||
|     Nfc* nfc = context; | ||||
|     NfcReaderRequestData* reader_data = &nfc->dev->dev_data.reader_data; | ||||
|     uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneDetectReader); | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(event.event == NfcCustomEventWorkerExit) { | ||||
|             // Add data button to widget if data is received for the first time
 | ||||
|             if(!string_size(nfc->text_box_store)) { | ||||
|                 nfc_scene_detect_reader_widget_config(nfc, true); | ||||
|             } | ||||
|             // Update TextBox data
 | ||||
|             if(string_size(nfc->text_box_store) < NFC_SCENE_DETECT_READER_LOG_SIZE_MAX) { | ||||
|                 string_cat_printf(nfc->text_box_store, "R:"); | ||||
|                 for(uint16_t i = 0; i < reader_data->size; i++) { | ||||
|                     string_cat_printf(nfc->text_box_store, " %02X", reader_data->data[i]); | ||||
|                 } | ||||
|                 string_push_back(nfc->text_box_store, '\n'); | ||||
|                 text_box_set_text(nfc->text_box, string_get_cstr(nfc->text_box_store)); | ||||
|             } | ||||
|             memset(reader_data, 0, sizeof(NfcReaderRequestData)); | ||||
|         if(event.event == NfcCustomEventViewExit) { | ||||
|             nfc_worker_stop(nfc->worker); | ||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneMfkeyNoncesInfo); | ||||
|             consumed = true; | ||||
|         } else if(event.event == GuiButtonTypeCenter && state == NfcSceneDetectReaderStateWidget) { | ||||
|             view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox); | ||||
|             scene_manager_set_scene_state( | ||||
|                 nfc->scene_manager, NfcSceneDetectReader, NfcSceneDetectReaderStateTextBox); | ||||
|             consumed = true; | ||||
|         } else if(event.event == NfcCustomEventViewExit && state == NfcSceneDetectReaderStateTextBox) { | ||||
|             view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); | ||||
|             scene_manager_set_scene_state( | ||||
|                 nfc->scene_manager, NfcSceneDetectReader, NfcSceneDetectReaderStateWidget); | ||||
|             consumed = true; | ||||
|         } | ||||
|     } else if(event.type == SceneManagerEventTypeBack) { | ||||
|         if(state == NfcSceneDetectReaderStateTextBox) { | ||||
|             view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); | ||||
|             scene_manager_set_scene_state( | ||||
|                 nfc->scene_manager, NfcSceneDetectReader, NfcSceneDetectReaderStateWidget); | ||||
|         } else if(event.event == NfcWorkerEventDetectReaderMfkeyCollected) { | ||||
|             detect_reader_inc_nonce_cnt(nfc->detect_reader); | ||||
|             consumed = true; | ||||
|         } | ||||
|     } | ||||
| @ -135,9 +57,7 @@ void nfc_scene_detect_reader_on_exit(void* context) { | ||||
|     nfc_worker_stop(nfc->worker); | ||||
| 
 | ||||
|     // Clear view
 | ||||
|     widget_reset(nfc->widget); | ||||
|     text_box_reset(nfc->text_box); | ||||
|     string_reset(nfc->text_box_store); | ||||
|     detect_reader_reset(nfc->detect_reader); | ||||
| 
 | ||||
|     nfc_blink_stop(nfc); | ||||
| } | ||||
|  | ||||
							
								
								
									
										49
									
								
								applications/nfc/scenes/nfc_scene_mfkey_complete.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								applications/nfc/scenes/nfc_scene_mfkey_complete.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,49 @@ | ||||
| #include "../nfc_i.h" | ||||
| 
 | ||||
| void nfc_scene_mfkey_complete_callback(GuiButtonType result, InputType type, void* context) { | ||||
|     Nfc* nfc = context; | ||||
|     if(type == InputTypeShort) { | ||||
|         view_dispatcher_send_custom_event(nfc->view_dispatcher, result); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_mfkey_complete_on_enter(void* context) { | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     widget_add_string_element(nfc->widget, 64, 0, AlignCenter, AlignTop, FontPrimary, "Complete!"); | ||||
|     widget_add_string_multiline_element( | ||||
|         nfc->widget, | ||||
|         64, | ||||
|         32, | ||||
|         AlignCenter, | ||||
|         AlignCenter, | ||||
|         FontSecondary, | ||||
|         "Now use mfkey32v2\nto extract keys"); | ||||
|     widget_add_button_element( | ||||
|         nfc->widget, GuiButtonTypeCenter, "OK", nfc_scene_mfkey_complete_callback, nfc); | ||||
| 
 | ||||
|     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); | ||||
| } | ||||
| 
 | ||||
| bool nfc_scene_mfkey_complete_on_event(void* context, SceneManagerEvent event) { | ||||
|     Nfc* nfc = context; | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(event.event == GuiButtonTypeCenter) { | ||||
|             consumed = scene_manager_search_and_switch_to_previous_scene( | ||||
|                 nfc->scene_manager, NfcSceneStart); | ||||
|         } | ||||
|     } else if(event.event == SceneManagerEventTypeBack) { | ||||
|         consumed = | ||||
|             scene_manager_search_and_switch_to_previous_scene(nfc->scene_manager, NfcSceneStart); | ||||
|     } | ||||
| 
 | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_mfkey_complete_on_exit(void* context) { | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     widget_reset(nfc->widget); | ||||
| } | ||||
							
								
								
									
										55
									
								
								applications/nfc/scenes/nfc_scene_mfkey_nonces_info.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								applications/nfc/scenes/nfc_scene_mfkey_nonces_info.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,55 @@ | ||||
| #include "../nfc_i.h" | ||||
| #include <lib/nfc/helpers/mfkey32.h> | ||||
| 
 | ||||
| void nfc_scene_mfkey_nonces_info_callback(GuiButtonType result, InputType type, void* context) { | ||||
|     Nfc* nfc = context; | ||||
|     if(type == InputTypeShort) { | ||||
|         view_dispatcher_send_custom_event(nfc->view_dispatcher, result); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_mfkey_nonces_info_on_enter(void* context) { | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     string_t temp_str; | ||||
|     string_init(temp_str); | ||||
| 
 | ||||
|     uint16_t nonces_saved = mfkey32_get_auth_sectors(temp_str); | ||||
|     widget_add_text_scroll_element(nfc->widget, 0, 22, 128, 42, string_get_cstr(temp_str)); | ||||
|     string_printf(temp_str, "Nonces saved %d", nonces_saved); | ||||
|     widget_add_string_element( | ||||
|         nfc->widget, 0, 0, AlignLeft, AlignTop, FontPrimary, string_get_cstr(temp_str)); | ||||
|     widget_add_string_element( | ||||
|         nfc->widget, 0, 12, AlignLeft, AlignTop, FontSecondary, "Authenticated sectors:"); | ||||
| 
 | ||||
|     widget_add_button_element( | ||||
|         nfc->widget, GuiButtonTypeRight, "Next", nfc_scene_mfkey_nonces_info_callback, nfc); | ||||
| 
 | ||||
|     string_clear(temp_str); | ||||
| 
 | ||||
|     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); | ||||
| } | ||||
| 
 | ||||
| bool nfc_scene_mfkey_nonces_info_on_event(void* context, SceneManagerEvent event) { | ||||
|     Nfc* nfc = context; | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(event.event == GuiButtonTypeRight) { | ||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneMfkeyComplete); | ||||
|             consumed = true; | ||||
|         } | ||||
|     } else if(event.type == SceneManagerEventTypeBack) { | ||||
|         consumed = | ||||
|             scene_manager_search_and_switch_to_previous_scene(nfc->scene_manager, NfcSceneStart); | ||||
|     } | ||||
| 
 | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_mfkey_nonces_info_on_exit(void* context) { | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     // Clear view
 | ||||
|     widget_reset(nfc->widget); | ||||
| } | ||||
| @ -49,7 +49,12 @@ bool nfc_scene_start_on_event(void* context, SceneManagerEvent event) { | ||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneRead); | ||||
|             consumed = true; | ||||
|         } else if(event.event == SubmenuIndexDetectReader) { | ||||
|             bool sd_exist = storage_sd_status(nfc->dev->storage) == FSE_OK; | ||||
|             if(sd_exist) { | ||||
|                 scene_manager_next_scene(nfc->scene_manager, NfcSceneDetectReader); | ||||
|             } else { | ||||
|                 scene_manager_next_scene(nfc->scene_manager, NfcSceneDictNotFound); | ||||
|             } | ||||
|             consumed = true; | ||||
|         } else if(event.event == SubmenuIndexSaved) { | ||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneFileSelect); | ||||
| @ -61,7 +66,6 @@ bool nfc_scene_start_on_event(void* context, SceneManagerEvent event) { | ||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneSetType); | ||||
|             consumed = true; | ||||
|         } else if(event.event == SubmenuIndexDebug) { | ||||
|             scene_manager_set_scene_state(nfc->scene_manager, NfcSceneStart, SubmenuIndexDebug); | ||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneDebug); | ||||
|             consumed = true; | ||||
|         } | ||||
|  | ||||
							
								
								
									
										115
									
								
								applications/nfc/views/detect_reader.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								applications/nfc/views/detect_reader.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,115 @@ | ||||
| #include "detect_reader.h" | ||||
| 
 | ||||
| #include <gui/elements.h> | ||||
| 
 | ||||
| struct DetectReader { | ||||
|     View* view; | ||||
|     DetectReaderDoneCallback callback; | ||||
|     void* context; | ||||
| }; | ||||
| 
 | ||||
| typedef struct { | ||||
|     uint16_t nonces; | ||||
| } DetectReaderViewModel; | ||||
| 
 | ||||
| static void detect_reader_draw_callback(Canvas* canvas, void* model) { | ||||
|     DetectReaderViewModel* m = model; | ||||
|     char text[32] = {}; | ||||
| 
 | ||||
|     snprintf(text, sizeof(text), "Tap the reader several times"); | ||||
|     canvas_draw_str_aligned(canvas, 64, 0, AlignCenter, AlignTop, "Tap the reader several times"); | ||||
| 
 | ||||
|     if(m->nonces == 0) { | ||||
|         canvas_set_font(canvas, FontPrimary); | ||||
|         canvas_draw_str_aligned(canvas, 52, 22, AlignLeft, AlignTop, "Emulating..."); | ||||
|         canvas_set_font(canvas, FontSecondary); | ||||
|         canvas_draw_str_aligned(canvas, 52, 35, AlignLeft, AlignTop, "MIFARE Classic"); | ||||
|         canvas_draw_icon(canvas, 0, 13, &I_Tap_reader_36x38); | ||||
|     } else { | ||||
|         canvas_set_font(canvas, FontPrimary); | ||||
|         canvas_draw_str_aligned(canvas, 54, 22, AlignLeft, AlignTop, "Collecting..."); | ||||
|         canvas_set_font(canvas, FontSecondary); | ||||
|         snprintf(text, sizeof(text), "Nonces: %d", m->nonces); | ||||
|         canvas_draw_str_aligned(canvas, 54, 35, AlignLeft, AlignTop, text); | ||||
|         elements_button_right(canvas, "Next"); | ||||
|         canvas_draw_icon(canvas, 6, 15, &I_ArrowC_1_36x36); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static bool detect_reader_input_callback(InputEvent* event, void* context) { | ||||
|     DetectReader* detect_reader = context; | ||||
|     furi_assert(detect_reader->callback); | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     uint8_t nonces = 0; | ||||
|     with_view_model( | ||||
|         detect_reader->view, (DetectReaderViewModel * model) { | ||||
|             nonces = model->nonces; | ||||
|             return false; | ||||
|         }); | ||||
| 
 | ||||
|     if(event->type == InputTypeShort) { | ||||
|         if(event->key == InputKeyRight) { | ||||
|             if(nonces > 0) { | ||||
|                 detect_reader->callback(detect_reader->context); | ||||
|                 consumed = true; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| DetectReader* detect_reader_alloc() { | ||||
|     DetectReader* detect_reader = malloc(sizeof(DetectReader)); | ||||
|     detect_reader->view = view_alloc(); | ||||
|     view_allocate_model(detect_reader->view, ViewModelTypeLocking, sizeof(DetectReaderViewModel)); | ||||
|     view_set_draw_callback(detect_reader->view, detect_reader_draw_callback); | ||||
|     view_set_input_callback(detect_reader->view, detect_reader_input_callback); | ||||
|     view_set_context(detect_reader->view, detect_reader); | ||||
| 
 | ||||
|     return detect_reader; | ||||
| } | ||||
| 
 | ||||
| void detect_reader_free(DetectReader* detect_reader) { | ||||
|     furi_assert(detect_reader); | ||||
| 
 | ||||
|     view_free(detect_reader->view); | ||||
|     free(detect_reader); | ||||
| } | ||||
| 
 | ||||
| void detect_reader_reset(DetectReader* detect_reader) { | ||||
|     furi_assert(detect_reader); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         detect_reader->view, (DetectReaderViewModel * model) { | ||||
|             model->nonces = 0; | ||||
|             return false; | ||||
|         }); | ||||
| } | ||||
| 
 | ||||
| View* detect_reader_get_view(DetectReader* detect_reader) { | ||||
|     furi_assert(detect_reader); | ||||
| 
 | ||||
|     return detect_reader->view; | ||||
| } | ||||
| 
 | ||||
| void detect_reader_set_callback( | ||||
|     DetectReader* detect_reader, | ||||
|     DetectReaderDoneCallback callback, | ||||
|     void* context) { | ||||
|     furi_assert(detect_reader); | ||||
|     furi_assert(callback); | ||||
| 
 | ||||
|     detect_reader->callback = callback; | ||||
|     detect_reader->context = context; | ||||
| } | ||||
| 
 | ||||
| void detect_reader_inc_nonce_cnt(DetectReader* detect_reader) { | ||||
|     furi_assert(detect_reader); | ||||
|     with_view_model( | ||||
|         detect_reader->view, (DetectReaderViewModel * model) { | ||||
|             model->nonces++; | ||||
|             return false; | ||||
|         }); | ||||
| } | ||||
							
								
								
									
										23
									
								
								applications/nfc/views/detect_reader.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								applications/nfc/views/detect_reader.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,23 @@ | ||||
| #pragma once | ||||
| #include <stdint.h> | ||||
| #include <gui/view.h> | ||||
| #include <gui/modules/widget.h> | ||||
| 
 | ||||
| typedef struct DetectReader DetectReader; | ||||
| 
 | ||||
| typedef void (*DetectReaderDoneCallback)(void* context); | ||||
| 
 | ||||
| DetectReader* detect_reader_alloc(); | ||||
| 
 | ||||
| void detect_reader_free(DetectReader* detect_reader); | ||||
| 
 | ||||
| void detect_reader_reset(DetectReader* detect_reader); | ||||
| 
 | ||||
| View* detect_reader_get_view(DetectReader* detect_reader); | ||||
| 
 | ||||
| void detect_reader_set_callback( | ||||
|     DetectReader* detect_reader, | ||||
|     DetectReaderDoneCallback callback, | ||||
|     void* context); | ||||
| 
 | ||||
| void detect_reader_inc_nonce_cnt(DetectReader* detect_reader); | ||||
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.6 KiB | 
							
								
								
									
										
											BIN
										
									
								
								assets/icons/NFC/Tap_reader_36x38.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								assets/icons/NFC/Tap_reader_36x38.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 3.7 KiB | 
| @ -217,7 +217,6 @@ bool furi_hal_nfc_listen( | ||||
|     } | ||||
|     rfalLowPowerModeStop(); | ||||
|     rfalNfcDiscoverParam params = { | ||||
|         .compMode = RFAL_COMPLIANCE_MODE_NFC, | ||||
|         .techs2Find = RFAL_NFC_LISTEN_TECH_A, | ||||
|         .totalDuration = 1000, | ||||
|         .devLimit = 1, | ||||
| @ -230,6 +229,11 @@ bool furi_hal_nfc_listen( | ||||
|         .notifyCb = NULL, | ||||
|         .activate_after_sak = activate_after_sak, | ||||
|     }; | ||||
|     if(FURI_BIT(sak, 5)) { | ||||
|         params.compMode = RFAL_COMPLIANCE_MODE_EMV; | ||||
|     } else { | ||||
|         params.compMode = RFAL_COMPLIANCE_MODE_NFC; | ||||
|     } | ||||
|     params.lmConfigPA.nfcidLen = uid_len; | ||||
|     memcpy(params.lmConfigPA.nfcid, uid, uid_len); | ||||
|     params.lmConfigPA.SENS_RES[0] = atqa[0]; | ||||
| @ -271,6 +275,10 @@ void furi_hal_nfc_listen_sleep() { | ||||
|     st25r3916ExecuteCommand(ST25R3916_CMD_GOTO_SLEEP); | ||||
| } | ||||
| 
 | ||||
| void furi_hal_nfc_stop_cmd() { | ||||
|     st25r3916ExecuteCommand(ST25R3916_CMD_STOP); | ||||
| } | ||||
| 
 | ||||
| bool furi_hal_nfc_listen_rx(FuriHalNfcTxRxContext* tx_rx, uint32_t timeout_ms) { | ||||
|     furi_assert(tx_rx); | ||||
| 
 | ||||
| @ -283,6 +291,9 @@ bool furi_hal_nfc_listen_rx(FuriHalNfcTxRxContext* tx_rx, uint32_t timeout_ms) { | ||||
|             if(st25r3916GetInterrupt(ST25R3916_IRQ_MASK_RXE)) { | ||||
|                 furi_hal_nfc_read_fifo(tx_rx->rx_data, &tx_rx->rx_bits); | ||||
|                 data_received = true; | ||||
|                 if(tx_rx->sniff_rx) { | ||||
|                     tx_rx->sniff_rx(tx_rx->rx_data, tx_rx->rx_bits, false, tx_rx->sniff_context); | ||||
|                 } | ||||
|                 break; | ||||
|             } | ||||
|             continue; | ||||
| @ -497,14 +508,14 @@ static bool furi_hal_nfc_transparent_tx_rx(FuriHalNfcTxRxContext* tx_rx, uint16_ | ||||
|     furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_nfc); | ||||
|     st25r3916ExecuteCommand(ST25R3916_CMD_UNMASK_RECEIVE_DATA); | ||||
| 
 | ||||
|     if(tx_rx->sniff_tx) { | ||||
|         tx_rx->sniff_tx(tx_rx->tx_data, tx_rx->tx_bits, false, tx_rx->sniff_context); | ||||
|     } | ||||
| 
 | ||||
|     // Manually wait for interrupt
 | ||||
|     furi_hal_gpio_init(&gpio_nfc_irq_rfid_pull, GpioModeInput, GpioPullDown, GpioSpeedVeryHigh); | ||||
|     st25r3916ClearAndEnableInterrupts(ST25R3916_IRQ_MASK_RXE); | ||||
| 
 | ||||
|     if(tx_rx->sniff_tx) { | ||||
|         tx_rx->sniff_tx(tx_rx->tx_data, tx_rx->tx_bits, false, tx_rx->sniff_context); | ||||
|     } | ||||
| 
 | ||||
|     uint32_t irq = 0; | ||||
|     uint8_t rxe = 0; | ||||
|     uint32_t start = DWT->CYCCNT; | ||||
|  | ||||
| @ -120,6 +120,8 @@ void furi_hal_nfc_field_off(); | ||||
|  */ | ||||
| void furi_hal_nfc_start_sleep(); | ||||
| 
 | ||||
| void furi_hal_nfc_stop_cmd(); | ||||
| 
 | ||||
| /** NFC stop sleep
 | ||||
|  */ | ||||
| void furi_hal_nfc_exit_sleep(); | ||||
|  | ||||
							
								
								
									
										230
									
								
								lib/nfc/helpers/mfkey32.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										230
									
								
								lib/nfc/helpers/mfkey32.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,230 @@ | ||||
| #include "mfkey32.h" | ||||
| 
 | ||||
| #include <furi/furi.h> | ||||
| #include <storage/storage.h> | ||||
| #include <stream/stream.h> | ||||
| #include <stream/buffered_file_stream.h> | ||||
| #include <m-array.h> | ||||
| 
 | ||||
| #include <lib/nfc/protocols/mifare_classic.h> | ||||
| #include <lib/nfc/protocols/nfc_util.h> | ||||
| 
 | ||||
| #define TAG "Mfkey32" | ||||
| 
 | ||||
| #define MFKEY32_LOGS_PATH EXT_PATH("nfc/.mfkey32.log") | ||||
| 
 | ||||
| typedef enum { | ||||
|     Mfkey32StateIdle, | ||||
|     Mfkey32StateAuthReceived, | ||||
|     Mfkey32StateAuthNtSent, | ||||
|     Mfkey32StateAuthArNrReceived, | ||||
| } Mfkey32State; | ||||
| 
 | ||||
| typedef struct { | ||||
|     uint32_t cuid; | ||||
|     uint8_t sector; | ||||
|     MfClassicKey key; | ||||
|     uint32_t nt0; | ||||
|     uint32_t nr0; | ||||
|     uint32_t ar0; | ||||
|     uint32_t nt1; | ||||
|     uint32_t nr1; | ||||
|     uint32_t ar1; | ||||
| } Mfkey32Params; | ||||
| 
 | ||||
| ARRAY_DEF(Mfkey32Params, Mfkey32Params, M_POD_OPLIST); | ||||
| 
 | ||||
| typedef struct { | ||||
|     uint8_t sector; | ||||
|     MfClassicKey key; | ||||
|     uint32_t nt; | ||||
|     uint32_t nr; | ||||
|     uint32_t ar; | ||||
| } Mfkey32Nonce; | ||||
| 
 | ||||
| struct Mfkey32 { | ||||
|     Mfkey32State state; | ||||
|     Stream* file_stream; | ||||
|     Mfkey32Params_t params_arr; | ||||
|     Mfkey32Nonce nonce; | ||||
|     uint32_t cuid; | ||||
|     Mfkey32ParseDataCallback callback; | ||||
|     void* context; | ||||
| }; | ||||
| 
 | ||||
| Mfkey32* mfkey32_alloc(uint32_t cuid) { | ||||
|     Mfkey32* instance = malloc(sizeof(Mfkey32)); | ||||
|     instance->cuid = cuid; | ||||
|     instance->state = Mfkey32StateIdle; | ||||
|     Storage* storage = furi_record_open(RECORD_STORAGE); | ||||
|     instance->file_stream = buffered_file_stream_alloc(storage); | ||||
|     if(!buffered_file_stream_open( | ||||
|            instance->file_stream, MFKEY32_LOGS_PATH, FSAM_WRITE, FSOM_OPEN_APPEND)) { | ||||
|         buffered_file_stream_close(instance->file_stream); | ||||
|         stream_free(instance->file_stream); | ||||
|         free(instance); | ||||
|         instance = NULL; | ||||
|     } else { | ||||
|         Mfkey32Params_init(instance->params_arr); | ||||
|     } | ||||
| 
 | ||||
|     furi_record_close(RECORD_STORAGE); | ||||
| 
 | ||||
|     return instance; | ||||
| } | ||||
| 
 | ||||
| void mfkey32_free(Mfkey32* instance) { | ||||
|     furi_assert(instance != NULL); | ||||
| 
 | ||||
|     Mfkey32Params_clear(instance->params_arr); | ||||
|     buffered_file_stream_close(instance->file_stream); | ||||
|     stream_free(instance->file_stream); | ||||
|     free(instance); | ||||
| } | ||||
| 
 | ||||
| void mfkey32_set_callback(Mfkey32* instance, Mfkey32ParseDataCallback callback, void* context) { | ||||
|     furi_assert(instance); | ||||
|     furi_assert(callback); | ||||
| 
 | ||||
|     instance->callback = callback; | ||||
|     instance->context = context; | ||||
| } | ||||
| 
 | ||||
| static bool mfkey32_write_params(Mfkey32* instance, Mfkey32Params* params) { | ||||
|     string_t str; | ||||
|     string_init_printf( | ||||
|         str, | ||||
|         "Sector %d key %c cuid %08x nt0 %08x nr0 %08x ar0 %08x nt1 %08x nr1 %08x ar1 %08x\n", | ||||
|         params->sector, | ||||
|         params->key == MfClassicKeyA ? 'A' : 'B', | ||||
|         params->cuid, | ||||
|         params->nt0, | ||||
|         params->nr0, | ||||
|         params->ar0, | ||||
|         params->nt1, | ||||
|         params->nr1, | ||||
|         params->ar1); | ||||
|     bool write_success = stream_write_string(instance->file_stream, str); | ||||
|     string_clear(str); | ||||
|     return write_success; | ||||
| } | ||||
| 
 | ||||
| static void mfkey32_add_params(Mfkey32* instance) { | ||||
|     Mfkey32Nonce* nonce = &instance->nonce; | ||||
|     bool nonce_added = false; | ||||
|     // Search if we partially collected params
 | ||||
|     if(Mfkey32Params_size(instance->params_arr)) { | ||||
|         Mfkey32Params_it_t it; | ||||
|         for(Mfkey32Params_it(it, instance->params_arr); !Mfkey32Params_end_p(it); | ||||
|             Mfkey32Params_next(it)) { | ||||
|             Mfkey32Params* params = Mfkey32Params_ref(it); | ||||
|             if((params->sector == nonce->sector) && (params->key == nonce->key)) { | ||||
|                 params->nt1 = nonce->nt; | ||||
|                 params->nr1 = nonce->nr; | ||||
|                 params->ar1 = nonce->ar; | ||||
|                 nonce_added = true; | ||||
|                 FURI_LOG_I( | ||||
|                     TAG, | ||||
|                     "Params for sector %d key %c collected", | ||||
|                     params->sector, | ||||
|                     params->key == MfClassicKeyA ? 'A' : 'B'); | ||||
|                 // Write on sd card
 | ||||
|                 if(mfkey32_write_params(instance, params)) { | ||||
|                     Mfkey32Params_remove(instance->params_arr, it); | ||||
|                     if(instance->callback) { | ||||
|                         instance->callback(Mfkey32EventParamCollected, instance->context); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     if(!nonce_added) { | ||||
|         Mfkey32Params params = { | ||||
|             .sector = nonce->sector, | ||||
|             .key = nonce->key, | ||||
|             .cuid = instance->cuid, | ||||
|             .nt0 = nonce->nt, | ||||
|             .nr0 = nonce->nr, | ||||
|             .ar0 = nonce->ar, | ||||
|         }; | ||||
|         Mfkey32Params_push_back(instance->params_arr, params); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void mfkey32_process_data( | ||||
|     Mfkey32* instance, | ||||
|     uint8_t* data, | ||||
|     uint16_t len, | ||||
|     bool reader_to_tag, | ||||
|     bool crc_dropped) { | ||||
|     furi_assert(instance); | ||||
|     furi_assert(data); | ||||
| 
 | ||||
|     Mfkey32Nonce* nonce = &instance->nonce; | ||||
|     uint16_t data_len = len; | ||||
|     if((data_len > 3) && !crc_dropped) { | ||||
|         data_len -= 2; | ||||
|     } | ||||
| 
 | ||||
|     bool data_processed = false; | ||||
|     if(instance->state == Mfkey32StateIdle) { | ||||
|         if(reader_to_tag) { | ||||
|             if((data[0] == 0x60) || (data[0] == 0x61)) { | ||||
|                 nonce->key = data[0] == 0x60 ? MfClassicKeyA : MfClassicKeyB; | ||||
|                 nonce->sector = mf_classic_get_sector_by_block(data[1]); | ||||
|                 instance->state = Mfkey32StateAuthReceived; | ||||
|                 data_processed = true; | ||||
|             } | ||||
|         } | ||||
|     } else if(instance->state == Mfkey32StateAuthReceived) { | ||||
|         if(!reader_to_tag) { | ||||
|             if(len == 4) { | ||||
|                 nonce->nt = nfc_util_bytes2num(data, 4); | ||||
|                 instance->state = Mfkey32StateAuthNtSent; | ||||
|                 data_processed = true; | ||||
|             } | ||||
|         } | ||||
|     } else if(instance->state == Mfkey32StateAuthNtSent) { | ||||
|         if(reader_to_tag) { | ||||
|             if(len == 8) { | ||||
|                 nonce->nr = nfc_util_bytes2num(data, 4); | ||||
|                 nonce->ar = nfc_util_bytes2num(&data[4], 4); | ||||
|                 mfkey32_add_params(instance); | ||||
|                 instance->state = Mfkey32StateIdle; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     if(!data_processed) { | ||||
|         instance->state = Mfkey32StateIdle; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| uint16_t mfkey32_get_auth_sectors(string_t data_str) { | ||||
|     furi_assert(data_str); | ||||
| 
 | ||||
|     uint16_t nonces_num = 0; | ||||
|     Storage* storage = furi_record_open(RECORD_STORAGE); | ||||
|     Stream* file_stream = buffered_file_stream_alloc(storage); | ||||
|     string_t temp_str; | ||||
|     string_init(temp_str); | ||||
| 
 | ||||
|     do { | ||||
|         if(!buffered_file_stream_open( | ||||
|                file_stream, MFKEY32_LOGS_PATH, FSAM_READ, FSOM_OPEN_EXISTING)) | ||||
|             break; | ||||
|         while(true) { | ||||
|             if(!stream_read_line(file_stream, temp_str)) break; | ||||
|             size_t uid_pos = string_search_str(temp_str, "cuid"); | ||||
|             string_left(temp_str, uid_pos); | ||||
|             string_push_back(temp_str, '\n'); | ||||
|             string_cat(data_str, temp_str); | ||||
|             nonces_num++; | ||||
|         } | ||||
|     } while(false); | ||||
| 
 | ||||
|     buffered_file_stream_close(file_stream); | ||||
|     stream_free(file_stream); | ||||
|     string_clear(temp_str); | ||||
| 
 | ||||
|     return nonces_num; | ||||
| } | ||||
							
								
								
									
										27
									
								
								lib/nfc/helpers/mfkey32.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								lib/nfc/helpers/mfkey32.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,27 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <lib/nfc/protocols/mifare_classic.h> | ||||
| #include <m-string.h> | ||||
| 
 | ||||
| typedef struct Mfkey32 Mfkey32; | ||||
| 
 | ||||
| typedef enum { | ||||
|     Mfkey32EventParamCollected, | ||||
| } Mfkey32Event; | ||||
| 
 | ||||
| typedef void (*Mfkey32ParseDataCallback)(Mfkey32Event event, void* context); | ||||
| 
 | ||||
| Mfkey32* mfkey32_alloc(uint32_t cuid); | ||||
| 
 | ||||
| void mfkey32_free(Mfkey32* instance); | ||||
| 
 | ||||
| void mfkey32_process_data( | ||||
|     Mfkey32* instance, | ||||
|     uint8_t* data, | ||||
|     uint16_t len, | ||||
|     bool reader_to_tag, | ||||
|     bool crc_dropped); | ||||
| 
 | ||||
| void mfkey32_set_callback(Mfkey32* instance, Mfkey32ParseDataCallback callback, void* context); | ||||
| 
 | ||||
| uint16_t mfkey32_get_auth_sectors(string_t string); | ||||
							
								
								
									
										72
									
								
								lib/nfc/helpers/nfc_debug_log.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								lib/nfc/helpers/nfc_debug_log.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,72 @@ | ||||
| #include "nfc_debug_log.h" | ||||
| 
 | ||||
| #include <m-string.h> | ||||
| #include <storage/storage.h> | ||||
| #include <stream/buffered_file_stream.h> | ||||
| 
 | ||||
| #define TAG "NfcDebugLog" | ||||
| 
 | ||||
| #define NFC_DEBUG_PCAP_FILENAME EXT_PATH("nfc/debug.txt") | ||||
| 
 | ||||
| struct NfcDebugLog { | ||||
|     Stream* file_stream; | ||||
|     string_t data_str; | ||||
| }; | ||||
| 
 | ||||
| NfcDebugLog* nfc_debug_log_alloc() { | ||||
|     NfcDebugLog* instance = malloc(sizeof(NfcDebugLog)); | ||||
| 
 | ||||
|     Storage* storage = furi_record_open(RECORD_STORAGE); | ||||
|     instance->file_stream = buffered_file_stream_alloc(storage); | ||||
| 
 | ||||
|     if(!buffered_file_stream_open( | ||||
|            instance->file_stream, NFC_DEBUG_PCAP_FILENAME, FSAM_WRITE, FSOM_OPEN_APPEND)) { | ||||
|         buffered_file_stream_close(instance->file_stream); | ||||
|         stream_free(instance->file_stream); | ||||
|         instance->file_stream = NULL; | ||||
|     } | ||||
| 
 | ||||
|     if(!instance->file_stream) { | ||||
|         free(instance); | ||||
|         instance = NULL; | ||||
|     } else { | ||||
|         string_init(instance->data_str); | ||||
|     } | ||||
|     furi_record_close(RECORD_STORAGE); | ||||
| 
 | ||||
|     return instance; | ||||
| } | ||||
| 
 | ||||
| void nfc_debug_log_free(NfcDebugLog* instance) { | ||||
|     furi_assert(instance); | ||||
|     furi_assert(instance->file_stream); | ||||
|     furi_assert(instance->data_str); | ||||
| 
 | ||||
|     buffered_file_stream_close(instance->file_stream); | ||||
|     stream_free(instance->file_stream); | ||||
|     string_clear(instance->data_str); | ||||
| 
 | ||||
|     free(instance); | ||||
| } | ||||
| 
 | ||||
| void nfc_debug_log_process_data( | ||||
|     NfcDebugLog* instance, | ||||
|     uint8_t* data, | ||||
|     uint16_t len, | ||||
|     bool reader_to_tag, | ||||
|     bool crc_dropped) { | ||||
|     furi_assert(instance); | ||||
|     furi_assert(instance->file_stream); | ||||
|     furi_assert(instance->data_str); | ||||
|     furi_assert(data); | ||||
|     UNUSED(crc_dropped); | ||||
| 
 | ||||
|     string_printf(instance->data_str, "%lu %c:", furi_get_tick(), reader_to_tag ? 'R' : 'T'); | ||||
|     uint16_t data_len = len; | ||||
|     for(size_t i = 0; i < data_len; i++) { | ||||
|         string_cat_printf(instance->data_str, " %02x", data[i]); | ||||
|     } | ||||
|     string_push_back(instance->data_str, '\n'); | ||||
| 
 | ||||
|     stream_write_string(instance->file_stream, instance->data_str); | ||||
| } | ||||
							
								
								
									
										17
									
								
								lib/nfc/helpers/nfc_debug_log.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								lib/nfc/helpers/nfc_debug_log.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,17 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <stdint.h> | ||||
| #include <stdbool.h> | ||||
| 
 | ||||
| typedef struct NfcDebugLog NfcDebugLog; | ||||
| 
 | ||||
| NfcDebugLog* nfc_debug_log_alloc(); | ||||
| 
 | ||||
| void nfc_debug_log_free(NfcDebugLog* instance); | ||||
| 
 | ||||
| void nfc_debug_log_process_data( | ||||
|     NfcDebugLog* instance, | ||||
|     uint8_t* data, | ||||
|     uint16_t len, | ||||
|     bool reader_to_tag, | ||||
|     bool crc_dropped); | ||||
| @ -1,7 +1,9 @@ | ||||
| #include "nfc_debug_pcap.h" | ||||
| 
 | ||||
| #include <storage/storage.h> | ||||
| #include <stream/buffered_file_stream.h> | ||||
| #include <furi_hal_nfc.h> | ||||
| #include <furi_hal_rtc.h> | ||||
| #include <stream_buffer.h> | ||||
| 
 | ||||
| #define TAG "NfcDebugPcap" | ||||
| 
 | ||||
| @ -16,23 +18,20 @@ | ||||
| #define DATA_PCD_TO_PICC_CRC_DROPPED 0xFA | ||||
| 
 | ||||
| #define NFC_DEBUG_PCAP_FILENAME EXT_PATH("nfc/debug.pcap") | ||||
| #define NFC_DEBUG_PCAP_BUFFER_SIZE 64 | ||||
| 
 | ||||
| struct NfcDebugPcapWorker { | ||||
|     bool alive; | ||||
|     Storage* storage; | ||||
|     File* file; | ||||
|     StreamBufferHandle_t stream; | ||||
|     FuriThread* thread; | ||||
| struct NfcDebugPcap { | ||||
|     Stream* file_stream; | ||||
| }; | ||||
| 
 | ||||
| static File* nfc_debug_pcap_open(Storage* storage) { | ||||
|     File* file = storage_file_alloc(storage); | ||||
|     if(!storage_file_open(file, NFC_DEBUG_PCAP_FILENAME, FSAM_WRITE, FSOM_OPEN_APPEND)) { | ||||
|         storage_file_free(file); | ||||
|         return NULL; | ||||
|     } | ||||
|     if(!storage_file_tell(file)) { | ||||
| static Stream* nfc_debug_pcap_open(Storage* storage) { | ||||
|     Stream* stream = NULL; | ||||
|     stream = buffered_file_stream_alloc(storage); | ||||
|     if(!buffered_file_stream_open(stream, NFC_DEBUG_PCAP_FILENAME, FSAM_WRITE, FSOM_OPEN_APPEND)) { | ||||
|         buffered_file_stream_close(stream); | ||||
|         stream_free(stream); | ||||
|         stream = NULL; | ||||
|     } else { | ||||
|         if(!stream_tell(stream)) { | ||||
|             struct { | ||||
|                 uint32_t magic; | ||||
|                 uint16_t major, minor; | ||||
| @ -46,18 +45,67 @@ static File* nfc_debug_pcap_open(Storage* storage) { | ||||
|                 .snaplen = FURI_HAL_NFC_DATA_BUFF_SIZE, | ||||
|                 .link_type = DLT_ISO_14443, | ||||
|             }; | ||||
|         if(storage_file_write(file, &pcap_hdr, sizeof(pcap_hdr)) != sizeof(pcap_hdr)) { | ||||
|             if(stream_write(stream, (uint8_t*)&pcap_hdr, sizeof(pcap_hdr)) != sizeof(pcap_hdr)) { | ||||
|                 FURI_LOG_E(TAG, "Failed to write pcap header"); | ||||
|                 buffered_file_stream_close(stream); | ||||
|                 stream_free(stream); | ||||
|                 stream = NULL; | ||||
|             } | ||||
|         } | ||||
|     return file; | ||||
|     } | ||||
|     return stream; | ||||
| } | ||||
| 
 | ||||
| static void | ||||
|     nfc_debug_pcap_write(NfcDebugPcapWorker* instance, uint8_t event, uint8_t* data, uint16_t len) { | ||||
| NfcDebugPcap* nfc_debug_pcap_alloc() { | ||||
|     NfcDebugPcap* instance = malloc(sizeof(NfcDebugPcap)); | ||||
| 
 | ||||
|     Storage* storage = furi_record_open(RECORD_STORAGE); | ||||
|     instance->file_stream = nfc_debug_pcap_open(storage); | ||||
|     if(!instance->file_stream) { | ||||
|         free(instance); | ||||
|         instance = NULL; | ||||
|     } | ||||
|     furi_record_close(RECORD_STORAGE); | ||||
| 
 | ||||
|     return instance; | ||||
| } | ||||
| 
 | ||||
| void nfc_debug_pcap_free(NfcDebugPcap* instance) { | ||||
|     furi_assert(instance); | ||||
|     furi_assert(instance->file_stream); | ||||
| 
 | ||||
|     buffered_file_stream_close(instance->file_stream); | ||||
|     stream_free(instance->file_stream); | ||||
| 
 | ||||
|     free(instance); | ||||
| } | ||||
| 
 | ||||
| void nfc_debug_pcap_process_data( | ||||
|     NfcDebugPcap* instance, | ||||
|     uint8_t* data, | ||||
|     uint16_t len, | ||||
|     bool reader_to_tag, | ||||
|     bool crc_dropped) { | ||||
|     furi_assert(instance); | ||||
|     furi_assert(data); | ||||
|     FuriHalRtcDateTime datetime; | ||||
|     furi_hal_rtc_get_datetime(&datetime); | ||||
| 
 | ||||
|     uint8_t event = 0; | ||||
|     if(reader_to_tag) { | ||||
|         if(crc_dropped) { | ||||
|             event = DATA_PCD_TO_PICC_CRC_DROPPED; | ||||
|         } else { | ||||
|             event = DATA_PCD_TO_PICC; | ||||
|         } | ||||
|     } else { | ||||
|         if(crc_dropped) { | ||||
|             event = DATA_PICC_TO_PCD_CRC_DROPPED; | ||||
|         } else { | ||||
|             event = DATA_PICC_TO_PCD; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     struct { | ||||
|         // https://wiki.wireshark.org/Development/LibpcapFileFormat#record-packet-header
 | ||||
|         uint32_t ts_sec; | ||||
| @ -77,90 +125,6 @@ static void | ||||
|         .event = event, | ||||
|         .len = len << 8 | len >> 8, | ||||
|     }; | ||||
|     xStreamBufferSend(instance->stream, &pkt_hdr, sizeof(pkt_hdr), FuriWaitForever); | ||||
|     xStreamBufferSend(instance->stream, data, len, FuriWaitForever); | ||||
| } | ||||
| 
 | ||||
| static void | ||||
|     nfc_debug_pcap_write_tx(uint8_t* data, uint16_t bits, bool crc_dropped, void* context) { | ||||
|     NfcDebugPcapWorker* instance = context; | ||||
|     uint8_t event = crc_dropped ? DATA_PCD_TO_PICC_CRC_DROPPED : DATA_PCD_TO_PICC; | ||||
|     nfc_debug_pcap_write(instance, event, data, bits / 8); | ||||
| } | ||||
| 
 | ||||
| static void | ||||
|     nfc_debug_pcap_write_rx(uint8_t* data, uint16_t bits, bool crc_dropped, void* context) { | ||||
|     NfcDebugPcapWorker* instance = context; | ||||
|     uint8_t event = crc_dropped ? DATA_PICC_TO_PCD_CRC_DROPPED : DATA_PICC_TO_PCD; | ||||
|     nfc_debug_pcap_write(instance, event, data, bits / 8); | ||||
| } | ||||
| 
 | ||||
| int32_t nfc_debug_pcap_thread(void* context) { | ||||
|     NfcDebugPcapWorker* instance = context; | ||||
|     uint8_t buffer[NFC_DEBUG_PCAP_BUFFER_SIZE]; | ||||
| 
 | ||||
|     while(instance->alive) { | ||||
|         size_t ret = | ||||
|             xStreamBufferReceive(instance->stream, buffer, NFC_DEBUG_PCAP_BUFFER_SIZE, 50); | ||||
|         if(storage_file_write(instance->file, buffer, ret) != ret) { | ||||
|             FURI_LOG_E(TAG, "Failed to write pcap data"); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| NfcDebugPcapWorker* nfc_debug_pcap_alloc(Storage* storage) { | ||||
|     NfcDebugPcapWorker* instance = malloc(sizeof(NfcDebugPcapWorker)); | ||||
| 
 | ||||
|     instance->alive = true; | ||||
| 
 | ||||
|     instance->storage = storage; | ||||
| 
 | ||||
|     instance->file = nfc_debug_pcap_open(storage); | ||||
| 
 | ||||
|     instance->stream = xStreamBufferCreate(4096, 1); | ||||
| 
 | ||||
|     instance->thread = furi_thread_alloc(); | ||||
|     furi_thread_set_name(instance->thread, "PcapWorker"); | ||||
|     furi_thread_set_stack_size(instance->thread, 1024); | ||||
|     furi_thread_set_callback(instance->thread, nfc_debug_pcap_thread); | ||||
|     furi_thread_set_context(instance->thread, instance); | ||||
|     furi_thread_start(instance->thread); | ||||
| 
 | ||||
|     return instance; | ||||
| } | ||||
| 
 | ||||
| void nfc_debug_pcap_free(NfcDebugPcapWorker* instance) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     instance->alive = false; | ||||
| 
 | ||||
|     furi_thread_join(instance->thread); | ||||
|     furi_thread_free(instance->thread); | ||||
| 
 | ||||
|     vStreamBufferDelete(instance->stream); | ||||
| 
 | ||||
|     if(instance->file) storage_file_free(instance->file); | ||||
| 
 | ||||
|     instance->storage = NULL; | ||||
| 
 | ||||
|     free(instance); | ||||
| } | ||||
| 
 | ||||
| void nfc_debug_pcap_prepare_tx_rx( | ||||
|     NfcDebugPcapWorker* instance, | ||||
|     FuriHalNfcTxRxContext* tx_rx, | ||||
|     bool is_picc) { | ||||
|     if(!instance || !instance->file) return; | ||||
| 
 | ||||
|     if(is_picc) { | ||||
|         tx_rx->sniff_tx = nfc_debug_pcap_write_rx; | ||||
|         tx_rx->sniff_rx = nfc_debug_pcap_write_tx; | ||||
|     } else { | ||||
|         tx_rx->sniff_tx = nfc_debug_pcap_write_tx; | ||||
|         tx_rx->sniff_rx = nfc_debug_pcap_write_rx; | ||||
|     } | ||||
| 
 | ||||
|     tx_rx->sniff_context = instance; | ||||
|     stream_write(instance->file_stream, (uint8_t*)&pkt_hdr, sizeof(pkt_hdr)); | ||||
|     stream_write(instance->file_stream, data, len); | ||||
| } | ||||
|  | ||||
| @ -1,21 +1,17 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <furi_hal_nfc.h> | ||||
| #include <storage/storage.h> | ||||
| #include <stdint.h> | ||||
| #include <stdbool.h> | ||||
| 
 | ||||
| typedef struct NfcDebugPcapWorker NfcDebugPcapWorker; | ||||
| typedef struct NfcDebugPcap NfcDebugPcap; | ||||
| 
 | ||||
| NfcDebugPcapWorker* nfc_debug_pcap_alloc(Storage* storage); | ||||
| NfcDebugPcap* nfc_debug_pcap_alloc(); | ||||
| 
 | ||||
| void nfc_debug_pcap_free(NfcDebugPcapWorker* instance); | ||||
| void nfc_debug_pcap_free(NfcDebugPcap* instance); | ||||
| 
 | ||||
| /** Prepare tx/rx context for debug pcap logging, if enabled.
 | ||||
|  * | ||||
|  * @param      instance NfcDebugPcapWorker* instance, can be NULL | ||||
|  * @param      tx_rx   TX/RX context to log | ||||
|  * @param      is_picc if true, record Flipper as PICC, else PCD. | ||||
|  */ | ||||
| void nfc_debug_pcap_prepare_tx_rx( | ||||
|     NfcDebugPcapWorker* instance, | ||||
|     FuriHalNfcTxRxContext* tx_rx, | ||||
|     bool is_picc); | ||||
| void nfc_debug_pcap_process_data( | ||||
|     NfcDebugPcap* instance, | ||||
|     uint8_t* data, | ||||
|     uint16_t len, | ||||
|     bool reader_to_tag, | ||||
|     bool crc_dropped); | ||||
|  | ||||
							
								
								
									
										261
									
								
								lib/nfc/helpers/reader_analyzer.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										261
									
								
								lib/nfc/helpers/reader_analyzer.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,261 @@ | ||||
| #include "reader_analyzer.h" | ||||
| #include <stream_buffer.h> | ||||
| #include <lib/nfc/protocols/nfc_util.h> | ||||
| #include <lib/nfc/protocols/mifare_classic.h> | ||||
| #include <m-array.h> | ||||
| 
 | ||||
| #include "mfkey32.h" | ||||
| #include "nfc_debug_pcap.h" | ||||
| #include "nfc_debug_log.h" | ||||
| 
 | ||||
| #define TAG "ReaderAnalyzer" | ||||
| 
 | ||||
| #define READER_ANALYZER_MAX_BUFF_SIZE (1024) | ||||
| 
 | ||||
| typedef struct { | ||||
|     bool reader_to_tag; | ||||
|     bool crc_dropped; | ||||
|     uint16_t len; | ||||
| } ReaderAnalyzerHeader; | ||||
| 
 | ||||
| typedef enum { | ||||
|     ReaderAnalyzerNfcDataMfClassic, | ||||
| } ReaderAnalyzerNfcData; | ||||
| 
 | ||||
| struct ReaderAnalyzer { | ||||
|     FuriHalNfcDevData nfc_data; | ||||
| 
 | ||||
|     bool alive; | ||||
|     StreamBufferHandle_t stream; | ||||
|     FuriThread* thread; | ||||
| 
 | ||||
|     ReaderAnalyzerParseDataCallback callback; | ||||
|     void* context; | ||||
| 
 | ||||
|     ReaderAnalyzerMode mode; | ||||
|     Mfkey32* mfkey32; | ||||
|     NfcDebugLog* debug_log; | ||||
|     NfcDebugPcap* pcap; | ||||
| }; | ||||
| 
 | ||||
| const FuriHalNfcDevData reader_analyzer_nfc_data[] = { | ||||
|     [ReaderAnalyzerNfcDataMfClassic] = | ||||
|         {.sak = 0x08, | ||||
|          .atqa = {0x44, 0x00}, | ||||
|          .interface = FuriHalNfcInterfaceRf, | ||||
|          .type = FuriHalNfcTypeA, | ||||
|          .uid_len = 7, | ||||
|          .uid = {0x04, 0x77, 0x70, 0x2A, 0x23, 0x4F, 0x80}, | ||||
|          .cuid = 0x2A234F80}, | ||||
| }; | ||||
| 
 | ||||
| void reader_analyzer_parse(ReaderAnalyzer* instance, uint8_t* buffer, size_t size) { | ||||
|     if(size < sizeof(ReaderAnalyzerHeader)) return; | ||||
| 
 | ||||
|     size_t bytes_i = 0; | ||||
|     while(bytes_i < size) { | ||||
|         ReaderAnalyzerHeader* header = (ReaderAnalyzerHeader*)&buffer[bytes_i]; | ||||
|         uint16_t len = header->len; | ||||
|         if(bytes_i + len > size) break; | ||||
|         bytes_i += sizeof(ReaderAnalyzerHeader); | ||||
|         if(instance->mfkey32) { | ||||
|             mfkey32_process_data( | ||||
|                 instance->mfkey32, | ||||
|                 &buffer[bytes_i], | ||||
|                 len, | ||||
|                 header->reader_to_tag, | ||||
|                 header->crc_dropped); | ||||
|         } | ||||
|         if(instance->pcap) { | ||||
|             nfc_debug_pcap_process_data( | ||||
|                 instance->pcap, &buffer[bytes_i], len, header->reader_to_tag, header->crc_dropped); | ||||
|         } | ||||
|         if(instance->debug_log) { | ||||
|             nfc_debug_log_process_data( | ||||
|                 instance->debug_log, | ||||
|                 &buffer[bytes_i], | ||||
|                 len, | ||||
|                 header->reader_to_tag, | ||||
|                 header->crc_dropped); | ||||
|         } | ||||
|         bytes_i += len; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| int32_t reader_analyzer_thread(void* context) { | ||||
|     ReaderAnalyzer* reader_analyzer = context; | ||||
|     uint8_t buffer[READER_ANALYZER_MAX_BUFF_SIZE] = {}; | ||||
| 
 | ||||
|     while(reader_analyzer->alive || !xStreamBufferIsEmpty(reader_analyzer->stream)) { | ||||
|         size_t ret = xStreamBufferReceive( | ||||
|             reader_analyzer->stream, buffer, READER_ANALYZER_MAX_BUFF_SIZE, 50); | ||||
|         if(ret) { | ||||
|             reader_analyzer_parse(reader_analyzer, buffer, ret); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| ReaderAnalyzer* reader_analyzer_alloc() { | ||||
|     ReaderAnalyzer* instance = malloc(sizeof(ReaderAnalyzer)); | ||||
| 
 | ||||
|     instance->nfc_data = reader_analyzer_nfc_data[ReaderAnalyzerNfcDataMfClassic]; | ||||
|     instance->alive = false; | ||||
|     instance->stream = | ||||
|         xStreamBufferCreate(READER_ANALYZER_MAX_BUFF_SIZE, sizeof(ReaderAnalyzerHeader)); | ||||
| 
 | ||||
|     instance->thread = furi_thread_alloc(); | ||||
|     furi_thread_set_name(instance->thread, "ReaderAnalyzerWorker"); | ||||
|     furi_thread_set_stack_size(instance->thread, 2048); | ||||
|     furi_thread_set_callback(instance->thread, reader_analyzer_thread); | ||||
|     furi_thread_set_context(instance->thread, instance); | ||||
|     furi_thread_set_priority(instance->thread, FuriThreadPriorityLow); | ||||
| 
 | ||||
|     return instance; | ||||
| } | ||||
| 
 | ||||
| static void reader_analyzer_mfkey_callback(Mfkey32Event event, void* context) { | ||||
|     furi_assert(context); | ||||
|     ReaderAnalyzer* instance = context; | ||||
| 
 | ||||
|     if(event == Mfkey32EventParamCollected) { | ||||
|         if(instance->callback) { | ||||
|             instance->callback(ReaderAnalyzerEventMfkeyCollected, instance->context); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void reader_analyzer_start(ReaderAnalyzer* instance, ReaderAnalyzerMode mode) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     xStreamBufferReset(instance->stream); | ||||
|     if(mode & ReaderAnalyzerModeDebugLog) { | ||||
|         instance->debug_log = nfc_debug_log_alloc(); | ||||
|     } | ||||
|     if(mode & ReaderAnalyzerModeMfkey) { | ||||
|         instance->mfkey32 = mfkey32_alloc(instance->nfc_data.cuid); | ||||
|         if(instance->mfkey32) { | ||||
|             mfkey32_set_callback(instance->mfkey32, reader_analyzer_mfkey_callback, instance); | ||||
|         } | ||||
|     } | ||||
|     if(mode & ReaderAnalyzerModeDebugPcap) { | ||||
|         instance->pcap = nfc_debug_pcap_alloc(); | ||||
|     } | ||||
| 
 | ||||
|     instance->alive = true; | ||||
|     furi_thread_start(instance->thread); | ||||
| } | ||||
| 
 | ||||
| void reader_analyzer_stop(ReaderAnalyzer* instance) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     instance->alive = false; | ||||
|     furi_thread_join(instance->thread); | ||||
| 
 | ||||
|     if(instance->debug_log) { | ||||
|         nfc_debug_log_free(instance->debug_log); | ||||
|         instance->debug_log = NULL; | ||||
|     } | ||||
|     if(instance->mfkey32) { | ||||
|         mfkey32_free(instance->mfkey32); | ||||
|         instance->mfkey32 = NULL; | ||||
|     } | ||||
|     if(instance->pcap) { | ||||
|         nfc_debug_pcap_free(instance->pcap); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void reader_analyzer_free(ReaderAnalyzer* instance) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     reader_analyzer_stop(instance); | ||||
|     furi_thread_free(instance->thread); | ||||
|     vStreamBufferDelete(instance->stream); | ||||
|     free(instance); | ||||
| } | ||||
| 
 | ||||
| void reader_analyzer_set_callback( | ||||
|     ReaderAnalyzer* instance, | ||||
|     ReaderAnalyzerParseDataCallback callback, | ||||
|     void* context) { | ||||
|     furi_assert(instance); | ||||
|     furi_assert(callback); | ||||
| 
 | ||||
|     instance->callback = callback; | ||||
|     instance->context = context; | ||||
| } | ||||
| 
 | ||||
| NfcProtocol | ||||
|     reader_analyzer_guess_protocol(ReaderAnalyzer* instance, uint8_t* buff_rx, uint16_t len) { | ||||
|     furi_assert(instance); | ||||
|     furi_assert(buff_rx); | ||||
|     UNUSED(len); | ||||
|     NfcProtocol protocol = NfcDeviceProtocolUnknown; | ||||
| 
 | ||||
|     if((buff_rx[0] == 0x60) || (buff_rx[0] == 0x61)) { | ||||
|         protocol = NfcDeviceProtocolMifareClassic; | ||||
|     } | ||||
| 
 | ||||
|     return protocol; | ||||
| } | ||||
| 
 | ||||
| FuriHalNfcDevData* reader_analyzer_get_nfc_data(ReaderAnalyzer* instance) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     return &instance->nfc_data; | ||||
| } | ||||
| 
 | ||||
| static void reader_analyzer_write( | ||||
|     ReaderAnalyzer* instance, | ||||
|     uint8_t* data, | ||||
|     uint16_t len, | ||||
|     bool reader_to_tag, | ||||
|     bool crc_dropped) { | ||||
|     ReaderAnalyzerHeader header = { | ||||
|         .reader_to_tag = reader_to_tag, .crc_dropped = crc_dropped, .len = len}; | ||||
|     size_t data_sent = 0; | ||||
|     data_sent = xStreamBufferSend( | ||||
|         instance->stream, &header, sizeof(ReaderAnalyzerHeader), FuriWaitForever); | ||||
|     if(data_sent != sizeof(ReaderAnalyzerHeader)) { | ||||
|         FURI_LOG_W(TAG, "Sent %d out of %d bytes", data_sent, sizeof(ReaderAnalyzerHeader)); | ||||
|     } | ||||
|     data_sent = xStreamBufferSend(instance->stream, data, len, FuriWaitForever); | ||||
|     if(data_sent != len) { | ||||
|         FURI_LOG_W(TAG, "Sent %d out of %d bytes", data_sent, len); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void | ||||
|     reader_analyzer_write_rx(uint8_t* data, uint16_t bits, bool crc_dropped, void* context) { | ||||
|     UNUSED(crc_dropped); | ||||
|     ReaderAnalyzer* reader_analyzer = context; | ||||
|     uint16_t bytes = bits < 8 ? 1 : bits / 8; | ||||
|     reader_analyzer_write(reader_analyzer, data, bytes, false, crc_dropped); | ||||
| } | ||||
| 
 | ||||
| static void | ||||
|     reader_analyzer_write_tx(uint8_t* data, uint16_t bits, bool crc_dropped, void* context) { | ||||
|     UNUSED(crc_dropped); | ||||
|     ReaderAnalyzer* reader_analyzer = context; | ||||
|     uint16_t bytes = bits < 8 ? 1 : bits / 8; | ||||
|     reader_analyzer_write(reader_analyzer, data, bytes, true, crc_dropped); | ||||
| } | ||||
| 
 | ||||
| void reader_analyzer_prepare_tx_rx( | ||||
|     ReaderAnalyzer* instance, | ||||
|     FuriHalNfcTxRxContext* tx_rx, | ||||
|     bool is_picc) { | ||||
|     furi_assert(instance); | ||||
|     furi_assert(tx_rx); | ||||
| 
 | ||||
|     if(is_picc) { | ||||
|         tx_rx->sniff_tx = reader_analyzer_write_rx; | ||||
|         tx_rx->sniff_rx = reader_analyzer_write_tx; | ||||
|     } else { | ||||
|         tx_rx->sniff_rx = reader_analyzer_write_rx; | ||||
|         tx_rx->sniff_tx = reader_analyzer_write_tx; | ||||
|     } | ||||
| 
 | ||||
|     tx_rx->sniff_context = instance; | ||||
| } | ||||
							
								
								
									
										41
									
								
								lib/nfc/helpers/reader_analyzer.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								lib/nfc/helpers/reader_analyzer.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,41 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <stdint.h> | ||||
| #include <lib/nfc/nfc_device.h> | ||||
| 
 | ||||
| typedef enum { | ||||
|     ReaderAnalyzerModeDebugLog = 0x01, | ||||
|     ReaderAnalyzerModeMfkey = 0x02, | ||||
|     ReaderAnalyzerModeDebugPcap = 0x04, | ||||
| } ReaderAnalyzerMode; | ||||
| 
 | ||||
| typedef enum { | ||||
|     ReaderAnalyzerEventMfkeyCollected, | ||||
| } ReaderAnalyzerEvent; | ||||
| 
 | ||||
| typedef struct ReaderAnalyzer ReaderAnalyzer; | ||||
| 
 | ||||
| typedef void (*ReaderAnalyzerParseDataCallback)(ReaderAnalyzerEvent event, void* context); | ||||
| 
 | ||||
| ReaderAnalyzer* reader_analyzer_alloc(); | ||||
| 
 | ||||
| void reader_analyzer_free(ReaderAnalyzer* instance); | ||||
| 
 | ||||
| void reader_analyzer_set_callback( | ||||
|     ReaderAnalyzer* instance, | ||||
|     ReaderAnalyzerParseDataCallback callback, | ||||
|     void* context); | ||||
| 
 | ||||
| void reader_analyzer_start(ReaderAnalyzer* instance, ReaderAnalyzerMode mode); | ||||
| 
 | ||||
| void reader_analyzer_stop(ReaderAnalyzer* instance); | ||||
| 
 | ||||
| NfcProtocol | ||||
|     reader_analyzer_guess_protocol(ReaderAnalyzer* instance, uint8_t* buff_rx, uint16_t len); | ||||
| 
 | ||||
| FuriHalNfcDevData* reader_analyzer_get_nfc_data(ReaderAnalyzer* instance); | ||||
| 
 | ||||
| void reader_analyzer_prepare_tx_rx( | ||||
|     ReaderAnalyzer* instance, | ||||
|     FuriHalNfcTxRxContext* tx_rx, | ||||
|     bool is_picc); | ||||
| @ -28,9 +28,7 @@ NfcWorker* nfc_worker_alloc() { | ||||
|     } | ||||
|     nfc_worker_change_state(nfc_worker, NfcWorkerStateReady); | ||||
| 
 | ||||
|     if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { | ||||
|         nfc_worker->debug_pcap_worker = nfc_debug_pcap_alloc(nfc_worker->storage); | ||||
|     } | ||||
|     nfc_worker->reader_analyzer = reader_analyzer_alloc(nfc_worker->storage); | ||||
| 
 | ||||
|     return nfc_worker; | ||||
| } | ||||
| @ -42,7 +40,7 @@ void nfc_worker_free(NfcWorker* nfc_worker) { | ||||
| 
 | ||||
|     furi_record_close(RECORD_STORAGE); | ||||
| 
 | ||||
|     if(nfc_worker->debug_pcap_worker) nfc_debug_pcap_free(nfc_worker->debug_pcap_worker); | ||||
|     reader_analyzer_free(nfc_worker->reader_analyzer); | ||||
| 
 | ||||
|     free(nfc_worker); | ||||
| } | ||||
| @ -105,6 +103,8 @@ int32_t nfc_worker_task(void* context) { | ||||
|         nfc_worker_mf_ultralight_read_auth(nfc_worker); | ||||
|     } else if(nfc_worker->state == NfcWorkerStateMfClassicDictAttack) { | ||||
|         nfc_worker_mf_classic_dict_attack(nfc_worker); | ||||
|     } else if(nfc_worker->state == NfcWorkerStateAnalyzeReader) { | ||||
|         nfc_worker_analyze_reader(nfc_worker); | ||||
|     } | ||||
|     furi_hal_nfc_sleep(); | ||||
|     nfc_worker_change_state(nfc_worker, NfcWorkerStateReady); | ||||
| @ -117,7 +117,11 @@ static bool nfc_worker_read_mf_ultralight(NfcWorker* nfc_worker, FuriHalNfcTxRxC | ||||
|     MfUltralightReader reader = {}; | ||||
|     MfUltralightData data = {}; | ||||
| 
 | ||||
|     nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, tx_rx, false); | ||||
|     if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { | ||||
|         reader_analyzer_prepare_tx_rx(nfc_worker->reader_analyzer, tx_rx, false); | ||||
|         reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog); | ||||
|     } | ||||
| 
 | ||||
|     do { | ||||
|         // Read card
 | ||||
|         if(!furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 200)) break; | ||||
| @ -127,6 +131,10 @@ static bool nfc_worker_read_mf_ultralight(NfcWorker* nfc_worker, FuriHalNfcTxRxC | ||||
|         read_success = true; | ||||
|     } while(false); | ||||
| 
 | ||||
|     if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { | ||||
|         reader_analyzer_stop(nfc_worker->reader_analyzer); | ||||
|     } | ||||
| 
 | ||||
|     return read_success; | ||||
| } | ||||
| 
 | ||||
| @ -134,7 +142,11 @@ static bool nfc_worker_read_mf_classic(NfcWorker* nfc_worker, FuriHalNfcTxRxCont | ||||
|     furi_assert(nfc_worker->callback); | ||||
|     bool read_success = false; | ||||
| 
 | ||||
|     nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, tx_rx, false); | ||||
|     if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { | ||||
|         reader_analyzer_prepare_tx_rx(nfc_worker->reader_analyzer, tx_rx, false); | ||||
|         reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog); | ||||
|     } | ||||
| 
 | ||||
|     do { | ||||
|         // Try to read supported card
 | ||||
|         FURI_LOG_I(TAG, "Try read supported card ..."); | ||||
| @ -162,6 +174,9 @@ static bool nfc_worker_read_mf_classic(NfcWorker* nfc_worker, FuriHalNfcTxRxCont | ||||
|         } | ||||
|     } while(false); | ||||
| 
 | ||||
|     if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { | ||||
|         reader_analyzer_stop(nfc_worker->reader_analyzer); | ||||
|     } | ||||
|     return read_success; | ||||
| } | ||||
| 
 | ||||
| @ -169,13 +184,21 @@ static bool nfc_worker_read_mf_desfire(NfcWorker* nfc_worker, FuriHalNfcTxRxCont | ||||
|     bool read_success = false; | ||||
|     MifareDesfireData* data = &nfc_worker->dev_data->mf_df_data; | ||||
| 
 | ||||
|     nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, tx_rx, false); | ||||
|     if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { | ||||
|         reader_analyzer_prepare_tx_rx(nfc_worker->reader_analyzer, tx_rx, false); | ||||
|         reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog); | ||||
|     } | ||||
| 
 | ||||
|     do { | ||||
|         if(!furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 300)) break; | ||||
|         if(!mf_df_read_card(tx_rx, data)) break; | ||||
|         read_success = true; | ||||
|     } while(false); | ||||
| 
 | ||||
|     if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { | ||||
|         reader_analyzer_stop(nfc_worker->reader_analyzer); | ||||
|     } | ||||
| 
 | ||||
|     return read_success; | ||||
| } | ||||
| 
 | ||||
| @ -184,7 +207,11 @@ static bool nfc_worker_read_bank_card(NfcWorker* nfc_worker, FuriHalNfcTxRxConte | ||||
|     EmvApplication emv_app = {}; | ||||
|     EmvData* result = &nfc_worker->dev_data->emv_data; | ||||
| 
 | ||||
|     nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, tx_rx, false); | ||||
|     if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { | ||||
|         reader_analyzer_prepare_tx_rx(nfc_worker->reader_analyzer, tx_rx, false); | ||||
|         reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog); | ||||
|     } | ||||
| 
 | ||||
|     do { | ||||
|         // Read card
 | ||||
|         if(!furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 300)) break; | ||||
| @ -211,6 +238,10 @@ static bool nfc_worker_read_bank_card(NfcWorker* nfc_worker, FuriHalNfcTxRxConte | ||||
|         read_success = true; | ||||
|     } while(false); | ||||
| 
 | ||||
|     if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { | ||||
|         reader_analyzer_stop(nfc_worker->reader_analyzer); | ||||
|     } | ||||
| 
 | ||||
|     return read_success; | ||||
| } | ||||
| 
 | ||||
| @ -320,18 +351,14 @@ void nfc_worker_read(NfcWorker* nfc_worker) { | ||||
| 
 | ||||
| void nfc_worker_emulate_uid(NfcWorker* nfc_worker) { | ||||
|     FuriHalNfcTxRxContext tx_rx = {}; | ||||
|     nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, &tx_rx, true); | ||||
|     FuriHalNfcDevData* data = &nfc_worker->dev_data->nfc_data; | ||||
|     NfcReaderRequestData* reader_data = &nfc_worker->dev_data->reader_data; | ||||
| 
 | ||||
|     // TODO add support for RATS
 | ||||
|     // Now remove bit 6 in SAK to support ISO-14443A-3 emulation
 | ||||
|     // Need to save ATS to support ISO-14443A-4 emulation
 | ||||
|     uint8_t sak = data->sak; | ||||
|     FURI_BIT_CLEAR(sak, 5); | ||||
| 
 | ||||
|     while(nfc_worker->state == NfcWorkerStateUidEmulate) { | ||||
|         if(furi_hal_nfc_listen(data->uid, data->uid_len, data->atqa, sak, true, 100)) { | ||||
|         if(furi_hal_nfc_listen(data->uid, data->uid_len, data->atqa, data->sak, false, 100)) { | ||||
|             if(furi_hal_nfc_tx_rx(&tx_rx, 100)) { | ||||
|                 reader_data->size = tx_rx.rx_bits / 8; | ||||
|                 if(reader_data->size > 0) { | ||||
| @ -349,7 +376,6 @@ void nfc_worker_emulate_uid(NfcWorker* nfc_worker) { | ||||
| 
 | ||||
| void nfc_worker_emulate_apdu(NfcWorker* nfc_worker) { | ||||
|     FuriHalNfcTxRxContext tx_rx = {}; | ||||
|     nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, &tx_rx, true); | ||||
|     FuriHalNfcDevData params = { | ||||
|         .uid = {0xCF, 0x72, 0xd4, 0x40}, | ||||
|         .uid_len = 4, | ||||
| @ -358,6 +384,11 @@ void nfc_worker_emulate_apdu(NfcWorker* nfc_worker) { | ||||
|         .type = FuriHalNfcTypeA, | ||||
|     }; | ||||
| 
 | ||||
|     if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { | ||||
|         reader_analyzer_prepare_tx_rx(nfc_worker->reader_analyzer, &tx_rx, true); | ||||
|         reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog); | ||||
|     } | ||||
| 
 | ||||
|     while(nfc_worker->state == NfcWorkerStateEmulateApdu) { | ||||
|         if(furi_hal_nfc_listen(params.uid, params.uid_len, params.atqa, params.sak, false, 300)) { | ||||
|             FURI_LOG_D(TAG, "POS terminal detected"); | ||||
| @ -370,6 +401,10 @@ void nfc_worker_emulate_apdu(NfcWorker* nfc_worker) { | ||||
|         furi_hal_nfc_sleep(); | ||||
|         furi_delay_ms(20); | ||||
|     } | ||||
| 
 | ||||
|     if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { | ||||
|         reader_analyzer_stop(nfc_worker->reader_analyzer); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void nfc_worker_emulate_mf_ultralight(NfcWorker* nfc_worker) { | ||||
| @ -484,7 +519,6 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) { | ||||
| 
 | ||||
| void nfc_worker_emulate_mf_classic(NfcWorker* nfc_worker) { | ||||
|     FuriHalNfcTxRxContext tx_rx = {}; | ||||
|     nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, &tx_rx, true); | ||||
|     FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; | ||||
|     MfClassicEmulator emulator = { | ||||
|         .cuid = nfc_util_bytes2num(&nfc_data->uid[nfc_data->uid_len - 4], 4), | ||||
| @ -525,6 +559,11 @@ void nfc_worker_mf_ultralight_read_auth(NfcWorker* nfc_worker) { | ||||
|     MfUltralightReader reader = {}; | ||||
|     mf_ul_reset(data); | ||||
| 
 | ||||
|     if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { | ||||
|         reader_analyzer_prepare_tx_rx(nfc_worker->reader_analyzer, &tx_rx, true); | ||||
|         reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog); | ||||
|     } | ||||
| 
 | ||||
|     uint32_t key = 0; | ||||
|     uint16_t pack = 0; | ||||
|     while(nfc_worker->state == NfcWorkerStateReadMfUltralightReadAuth) { | ||||
| @ -577,4 +616,61 @@ void nfc_worker_mf_ultralight_read_auth(NfcWorker* nfc_worker) { | ||||
|             furi_delay_ms(10); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { | ||||
|         reader_analyzer_stop(nfc_worker->reader_analyzer); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void nfc_worker_reader_analyzer_callback(ReaderAnalyzerEvent event, void* context) { | ||||
|     furi_assert(context); | ||||
|     NfcWorker* nfc_worker = context; | ||||
| 
 | ||||
|     if(event == ReaderAnalyzerEventMfkeyCollected) { | ||||
|         if(nfc_worker->callback) { | ||||
|             nfc_worker->callback(NfcWorkerEventDetectReaderMfkeyCollected, nfc_worker->context); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void nfc_worker_analyze_reader(NfcWorker* nfc_worker) { | ||||
|     FuriHalNfcTxRxContext tx_rx = {}; | ||||
| 
 | ||||
|     ReaderAnalyzer* reader_analyzer = nfc_worker->reader_analyzer; | ||||
|     FuriHalNfcDevData* nfc_data = reader_analyzer_get_nfc_data(reader_analyzer); | ||||
|     MfClassicEmulator emulator = { | ||||
|         .cuid = nfc_util_bytes2num(&nfc_data->uid[nfc_data->uid_len - 4], 4), | ||||
|         .data = nfc_worker->dev_data->mf_classic_data, | ||||
|         .data_changed = false, | ||||
|     }; | ||||
|     NfcaSignal* nfca_signal = nfca_signal_alloc(); | ||||
|     tx_rx.nfca_signal = nfca_signal; | ||||
|     reader_analyzer_prepare_tx_rx(reader_analyzer, &tx_rx, true); | ||||
|     reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeMfkey); | ||||
|     reader_analyzer_set_callback(reader_analyzer, nfc_worker_reader_analyzer_callback, nfc_worker); | ||||
| 
 | ||||
|     rfal_platform_spi_acquire(); | ||||
| 
 | ||||
|     FURI_LOG_D(TAG, "Start reader analyzer"); | ||||
|     while(nfc_worker->state == NfcWorkerStateAnalyzeReader) { | ||||
|         furi_hal_nfc_stop_cmd(); | ||||
|         furi_delay_ms(5); | ||||
|         furi_hal_nfc_listen_start(nfc_data); | ||||
|         if(furi_hal_nfc_listen_rx(&tx_rx, 300)) { | ||||
|             NfcProtocol protocol = | ||||
|                 reader_analyzer_guess_protocol(reader_analyzer, tx_rx.rx_data, tx_rx.rx_bits / 8); | ||||
|             if(protocol == NfcDeviceProtocolMifareClassic) { | ||||
|                 mf_classic_emulator(&emulator, &tx_rx); | ||||
|             } | ||||
|         } else { | ||||
|             FURI_LOG_D(TAG, "No data from reader"); | ||||
|             continue; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     rfal_platform_spi_release(); | ||||
| 
 | ||||
|     reader_analyzer_stop(nfc_worker->reader_analyzer); | ||||
| 
 | ||||
|     nfca_signal_free(nfca_signal); | ||||
| } | ||||
|  | ||||
| @ -16,6 +16,7 @@ typedef enum { | ||||
|     NfcWorkerStateMfClassicEmulate, | ||||
|     NfcWorkerStateReadMfUltralightReadAuth, | ||||
|     NfcWorkerStateMfClassicDictAttack, | ||||
|     NfcWorkerStateAnalyzeReader, | ||||
|     // Debug
 | ||||
|     NfcWorkerStateEmulateApdu, | ||||
|     NfcWorkerStateField, | ||||
| @ -54,8 +55,12 @@ typedef enum { | ||||
|     NfcWorkerEventFoundKeyA, | ||||
|     NfcWorkerEventFoundKeyB, | ||||
| 
 | ||||
|     // Detect Reader events
 | ||||
|     NfcWorkerEventDetectReaderMfkeyCollected, | ||||
| 
 | ||||
|     // Mifare Ultralight events
 | ||||
|     NfcWorkerEventMfUltralightPassKey, | ||||
| 
 | ||||
| } NfcWorkerEvent; | ||||
| 
 | ||||
| typedef bool (*NfcWorkerCallback)(NfcWorkerEvent event, void* context); | ||||
|  | ||||
| @ -12,8 +12,7 @@ | ||||
| #include <lib/nfc/protocols/mifare_classic.h> | ||||
| #include <lib/nfc/protocols/mifare_desfire.h> | ||||
| #include <lib/nfc/protocols/nfca.h> | ||||
| 
 | ||||
| #include "helpers/nfc_debug_pcap.h" | ||||
| #include <lib/nfc/helpers/reader_analyzer.h> | ||||
| 
 | ||||
| struct NfcWorker { | ||||
|     FuriThread* thread; | ||||
| @ -27,7 +26,7 @@ struct NfcWorker { | ||||
| 
 | ||||
|     NfcWorkerState state; | ||||
| 
 | ||||
|     NfcDebugPcapWorker* debug_pcap_worker; | ||||
|     ReaderAnalyzer* reader_analyzer; | ||||
| }; | ||||
| 
 | ||||
| void nfc_worker_change_state(NfcWorker* nfc_worker, NfcWorkerState state); | ||||
| @ -49,3 +48,5 @@ void nfc_worker_mf_ultralight_read_auth(NfcWorker* nfc_worker); | ||||
| void nfc_worker_mf_ul_auth_attack(NfcWorker* nfc_worker); | ||||
| 
 | ||||
| void nfc_worker_emulate_apdu(NfcWorker* nfc_worker); | ||||
| 
 | ||||
| void nfc_worker_analyze_reader(NfcWorker* nfc_worker); | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 gornekich
						gornekich