[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( |     view_dispatcher_add_view( | ||||||
|         nfc->view_dispatcher, NfcViewDictAttack, dict_attack_get_view(nfc->dict_attack)); |         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
 |     // Generator
 | ||||||
|     nfc->generator = NULL; |     nfc->generator = NULL; | ||||||
| 
 | 
 | ||||||
| @ -158,6 +163,10 @@ void nfc_free(Nfc* nfc) { | |||||||
|     view_dispatcher_remove_view(nfc->view_dispatcher, NfcViewDictAttack); |     view_dispatcher_remove_view(nfc->view_dispatcher, NfcViewDictAttack); | ||||||
|     dict_attack_free(nfc->dict_attack); |     dict_attack_free(nfc->dict_attack); | ||||||
| 
 | 
 | ||||||
|  |     // Detect Reader
 | ||||||
|  |     view_dispatcher_remove_view(nfc->view_dispatcher, NfcViewDetectReader); | ||||||
|  |     detect_reader_free(nfc->detect_reader); | ||||||
|  | 
 | ||||||
|     // Worker
 |     // Worker
 | ||||||
|     nfc_worker_stop(nfc->worker); |     nfc_worker_stop(nfc->worker); | ||||||
|     nfc_worker_free(nfc->worker); |     nfc_worker_free(nfc->worker); | ||||||
|  | |||||||
| @ -28,6 +28,7 @@ | |||||||
| #include <lib/nfc/parsers/nfc_supported_card.h> | #include <lib/nfc/parsers/nfc_supported_card.h> | ||||||
| 
 | 
 | ||||||
| #include "views/dict_attack.h" | #include "views/dict_attack.h" | ||||||
|  | #include "views/detect_reader.h" | ||||||
| 
 | 
 | ||||||
| #include <nfc/scenes/nfc_scene.h> | #include <nfc/scenes/nfc_scene.h> | ||||||
| #include <nfc/helpers/nfc_custom_event.h> | #include <nfc/helpers/nfc_custom_event.h> | ||||||
| @ -71,6 +72,7 @@ struct Nfc { | |||||||
|     TextBox* text_box; |     TextBox* text_box; | ||||||
|     Widget* widget; |     Widget* widget; | ||||||
|     DictAttack* dict_attack; |     DictAttack* dict_attack; | ||||||
|  |     DetectReader* detect_reader; | ||||||
| 
 | 
 | ||||||
|     const NfcGenerator* generator; |     const NfcGenerator* generator; | ||||||
| }; | }; | ||||||
| @ -85,6 +87,7 @@ typedef enum { | |||||||
|     NfcViewTextBox, |     NfcViewTextBox, | ||||||
|     NfcViewWidget, |     NfcViewWidget, | ||||||
|     NfcViewDictAttack, |     NfcViewDictAttack, | ||||||
|  |     NfcViewDetectReader, | ||||||
| } NfcView; | } NfcView; | ||||||
| 
 | 
 | ||||||
| Nfc* nfc_alloc(); | Nfc* nfc_alloc(); | ||||||
|  | |||||||
| @ -48,4 +48,6 @@ ADD_SCENE(nfc, rpc, Rpc) | |||||||
| ADD_SCENE(nfc, exit_confirm, ExitConfirm) | ADD_SCENE(nfc, exit_confirm, ExitConfirm) | ||||||
| ADD_SCENE(nfc, retry_confirm, RetryConfirm) | ADD_SCENE(nfc, retry_confirm, RetryConfirm) | ||||||
| ADD_SCENE(nfc, detect_reader, DetectReader) | 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) | ADD_SCENE(nfc, nfc_data_info, NfcDataInfo) | ||||||
|  | |||||||
| @ -1,126 +1,48 @@ | |||||||
| #include "../nfc_i.h" | #include "../nfc_i.h" | ||||||
| #include <dolphin/dolphin.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) { | bool nfc_detect_reader_worker_callback(NfcWorkerEvent event, void* context) { | ||||||
|     UNUSED(event); |     UNUSED(event); | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     Nfc* nfc = context; |     Nfc* nfc = context; | ||||||
|     view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventWorkerExit); |     view_dispatcher_send_custom_event(nfc->view_dispatcher, event); | ||||||
|     return true; |     return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void nfc_scene_detect_reader_widget_callback(GuiButtonType result, InputType type, void* context) { | void nfc_scene_detect_reader_callback(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) { |  | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     Nfc* nfc = context; |     Nfc* nfc = context; | ||||||
|     view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); |     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) { | void nfc_scene_detect_reader_on_enter(void* context) { | ||||||
|     Nfc* nfc = context; |     Nfc* nfc = context; | ||||||
|     DOLPHIN_DEED(DolphinDeedNfcEmulate); |     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
 |     detect_reader_set_callback(nfc->detect_reader, nfc_scene_detect_reader_callback, nfc); | ||||||
|     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)); |  | ||||||
|     nfc_worker_start( |     nfc_worker_start( | ||||||
|         nfc->worker, |         nfc->worker, | ||||||
|         NfcWorkerStateUidEmulate, |         NfcWorkerStateAnalyzeReader, | ||||||
|         &nfc->dev->dev_data, |         &nfc->dev->dev_data, | ||||||
|         nfc_detect_reader_worker_callback, |         nfc_detect_reader_worker_callback, | ||||||
|         nfc); |         nfc); | ||||||
| 
 | 
 | ||||||
|  |     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDetectReader); | ||||||
|  | 
 | ||||||
|     nfc_blink_start(nfc); |     nfc_blink_start(nfc); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool nfc_scene_detect_reader_on_event(void* context, SceneManagerEvent event) { | bool nfc_scene_detect_reader_on_event(void* context, SceneManagerEvent event) { | ||||||
|     Nfc* nfc = context; |     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; |     bool consumed = false; | ||||||
| 
 | 
 | ||||||
|     if(event.type == SceneManagerEventTypeCustom) { |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|         if(event.event == NfcCustomEventWorkerExit) { |         if(event.event == NfcCustomEventViewExit) { | ||||||
|             // Add data button to widget if data is received for the first time
 |             nfc_worker_stop(nfc->worker); | ||||||
|             if(!string_size(nfc->text_box_store)) { |             scene_manager_next_scene(nfc->scene_manager, NfcSceneMfkeyNoncesInfo); | ||||||
|                 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)); |  | ||||||
|             consumed = true; |             consumed = true; | ||||||
|         } else if(event.event == GuiButtonTypeCenter && state == NfcSceneDetectReaderStateWidget) { |         } else if(event.event == NfcWorkerEventDetectReaderMfkeyCollected) { | ||||||
|             view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox); |             detect_reader_inc_nonce_cnt(nfc->detect_reader); | ||||||
|             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); |  | ||||||
|             consumed = true; |             consumed = true; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @ -135,9 +57,7 @@ void nfc_scene_detect_reader_on_exit(void* context) { | |||||||
|     nfc_worker_stop(nfc->worker); |     nfc_worker_stop(nfc->worker); | ||||||
| 
 | 
 | ||||||
|     // Clear view
 |     // Clear view
 | ||||||
|     widget_reset(nfc->widget); |     detect_reader_reset(nfc->detect_reader); | ||||||
|     text_box_reset(nfc->text_box); |  | ||||||
|     string_reset(nfc->text_box_store); |  | ||||||
| 
 | 
 | ||||||
|     nfc_blink_stop(nfc); |     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); |             scene_manager_next_scene(nfc->scene_manager, NfcSceneRead); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|         } else if(event.event == SubmenuIndexDetectReader) { |         } 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); |                 scene_manager_next_scene(nfc->scene_manager, NfcSceneDetectReader); | ||||||
|  |             } else { | ||||||
|  |                 scene_manager_next_scene(nfc->scene_manager, NfcSceneDictNotFound); | ||||||
|  |             } | ||||||
|             consumed = true; |             consumed = true; | ||||||
|         } else if(event.event == SubmenuIndexSaved) { |         } else if(event.event == SubmenuIndexSaved) { | ||||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneFileSelect); |             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); |             scene_manager_next_scene(nfc->scene_manager, NfcSceneSetType); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|         } else if(event.event == SubmenuIndexDebug) { |         } else if(event.event == SubmenuIndexDebug) { | ||||||
|             scene_manager_set_scene_state(nfc->scene_manager, NfcSceneStart, SubmenuIndexDebug); |  | ||||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneDebug); |             scene_manager_next_scene(nfc->scene_manager, NfcSceneDebug); | ||||||
|             consumed = true; |             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(); |     rfalLowPowerModeStop(); | ||||||
|     rfalNfcDiscoverParam params = { |     rfalNfcDiscoverParam params = { | ||||||
|         .compMode = RFAL_COMPLIANCE_MODE_NFC, |  | ||||||
|         .techs2Find = RFAL_NFC_LISTEN_TECH_A, |         .techs2Find = RFAL_NFC_LISTEN_TECH_A, | ||||||
|         .totalDuration = 1000, |         .totalDuration = 1000, | ||||||
|         .devLimit = 1, |         .devLimit = 1, | ||||||
| @ -230,6 +229,11 @@ bool furi_hal_nfc_listen( | |||||||
|         .notifyCb = NULL, |         .notifyCb = NULL, | ||||||
|         .activate_after_sak = activate_after_sak, |         .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; |     params.lmConfigPA.nfcidLen = uid_len; | ||||||
|     memcpy(params.lmConfigPA.nfcid, uid, uid_len); |     memcpy(params.lmConfigPA.nfcid, uid, uid_len); | ||||||
|     params.lmConfigPA.SENS_RES[0] = atqa[0]; |     params.lmConfigPA.SENS_RES[0] = atqa[0]; | ||||||
| @ -271,6 +275,10 @@ void furi_hal_nfc_listen_sleep() { | |||||||
|     st25r3916ExecuteCommand(ST25R3916_CMD_GOTO_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) { | bool furi_hal_nfc_listen_rx(FuriHalNfcTxRxContext* tx_rx, uint32_t timeout_ms) { | ||||||
|     furi_assert(tx_rx); |     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)) { |             if(st25r3916GetInterrupt(ST25R3916_IRQ_MASK_RXE)) { | ||||||
|                 furi_hal_nfc_read_fifo(tx_rx->rx_data, &tx_rx->rx_bits); |                 furi_hal_nfc_read_fifo(tx_rx->rx_data, &tx_rx->rx_bits); | ||||||
|                 data_received = true; |                 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; |                 break; | ||||||
|             } |             } | ||||||
|             continue; |             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); |     furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_nfc); | ||||||
|     st25r3916ExecuteCommand(ST25R3916_CMD_UNMASK_RECEIVE_DATA); |     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
 |     // Manually wait for interrupt
 | ||||||
|     furi_hal_gpio_init(&gpio_nfc_irq_rfid_pull, GpioModeInput, GpioPullDown, GpioSpeedVeryHigh); |     furi_hal_gpio_init(&gpio_nfc_irq_rfid_pull, GpioModeInput, GpioPullDown, GpioSpeedVeryHigh); | ||||||
|     st25r3916ClearAndEnableInterrupts(ST25R3916_IRQ_MASK_RXE); |     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; |     uint32_t irq = 0; | ||||||
|     uint8_t rxe = 0; |     uint8_t rxe = 0; | ||||||
|     uint32_t start = DWT->CYCCNT; |     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_start_sleep(); | ||||||
| 
 | 
 | ||||||
|  | void furi_hal_nfc_stop_cmd(); | ||||||
|  | 
 | ||||||
| /** NFC stop sleep
 | /** NFC stop sleep
 | ||||||
|  */ |  */ | ||||||
| void furi_hal_nfc_exit_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 "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 <furi_hal_rtc.h> | ||||||
| #include <stream_buffer.h> |  | ||||||
| 
 | 
 | ||||||
| #define TAG "NfcDebugPcap" | #define TAG "NfcDebugPcap" | ||||||
| 
 | 
 | ||||||
| @ -16,23 +18,20 @@ | |||||||
| #define DATA_PCD_TO_PICC_CRC_DROPPED 0xFA | #define DATA_PCD_TO_PICC_CRC_DROPPED 0xFA | ||||||
| 
 | 
 | ||||||
| #define NFC_DEBUG_PCAP_FILENAME EXT_PATH("nfc/debug.pcap") | #define NFC_DEBUG_PCAP_FILENAME EXT_PATH("nfc/debug.pcap") | ||||||
| #define NFC_DEBUG_PCAP_BUFFER_SIZE 64 |  | ||||||
| 
 | 
 | ||||||
| struct NfcDebugPcapWorker { | struct NfcDebugPcap { | ||||||
|     bool alive; |     Stream* file_stream; | ||||||
|     Storage* storage; |  | ||||||
|     File* file; |  | ||||||
|     StreamBufferHandle_t stream; |  | ||||||
|     FuriThread* thread; |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static File* nfc_debug_pcap_open(Storage* storage) { | static Stream* nfc_debug_pcap_open(Storage* storage) { | ||||||
|     File* file = storage_file_alloc(storage); |     Stream* stream = NULL; | ||||||
|     if(!storage_file_open(file, NFC_DEBUG_PCAP_FILENAME, FSAM_WRITE, FSOM_OPEN_APPEND)) { |     stream = buffered_file_stream_alloc(storage); | ||||||
|         storage_file_free(file); |     if(!buffered_file_stream_open(stream, NFC_DEBUG_PCAP_FILENAME, FSAM_WRITE, FSOM_OPEN_APPEND)) { | ||||||
|         return NULL; |         buffered_file_stream_close(stream); | ||||||
|     } |         stream_free(stream); | ||||||
|     if(!storage_file_tell(file)) { |         stream = NULL; | ||||||
|  |     } else { | ||||||
|  |         if(!stream_tell(stream)) { | ||||||
|             struct { |             struct { | ||||||
|                 uint32_t magic; |                 uint32_t magic; | ||||||
|                 uint16_t major, minor; |                 uint16_t major, minor; | ||||||
| @ -46,18 +45,67 @@ static File* nfc_debug_pcap_open(Storage* storage) { | |||||||
|                 .snaplen = FURI_HAL_NFC_DATA_BUFF_SIZE, |                 .snaplen = FURI_HAL_NFC_DATA_BUFF_SIZE, | ||||||
|                 .link_type = DLT_ISO_14443, |                 .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"); |                 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 | NfcDebugPcap* nfc_debug_pcap_alloc() { | ||||||
|     nfc_debug_pcap_write(NfcDebugPcapWorker* instance, uint8_t event, uint8_t* data, uint16_t len) { |     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; |     FuriHalRtcDateTime datetime; | ||||||
|     furi_hal_rtc_get_datetime(&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 { |     struct { | ||||||
|         // https://wiki.wireshark.org/Development/LibpcapFileFormat#record-packet-header
 |         // https://wiki.wireshark.org/Development/LibpcapFileFormat#record-packet-header
 | ||||||
|         uint32_t ts_sec; |         uint32_t ts_sec; | ||||||
| @ -77,90 +125,6 @@ static void | |||||||
|         .event = event, |         .event = event, | ||||||
|         .len = len << 8 | len >> 8, |         .len = len << 8 | len >> 8, | ||||||
|     }; |     }; | ||||||
|     xStreamBufferSend(instance->stream, &pkt_hdr, sizeof(pkt_hdr), FuriWaitForever); |     stream_write(instance->file_stream, (uint8_t*)&pkt_hdr, sizeof(pkt_hdr)); | ||||||
|     xStreamBufferSend(instance->stream, data, len, FuriWaitForever); |     stream_write(instance->file_stream, data, len); | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 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; |  | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,21 +1,17 @@ | |||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include <furi_hal_nfc.h> | #include <stdint.h> | ||||||
| #include <storage/storage.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.
 | void nfc_debug_pcap_process_data( | ||||||
|  * |     NfcDebugPcap* instance, | ||||||
|  * @param      instance NfcDebugPcapWorker* instance, can be NULL |     uint8_t* data, | ||||||
|  * @param      tx_rx   TX/RX context to log |     uint16_t len, | ||||||
|  * @param      is_picc if true, record Flipper as PICC, else PCD. |     bool reader_to_tag, | ||||||
|  */ |     bool crc_dropped); | ||||||
| void nfc_debug_pcap_prepare_tx_rx( |  | ||||||
|     NfcDebugPcapWorker* instance, |  | ||||||
|     FuriHalNfcTxRxContext* tx_rx, |  | ||||||
|     bool is_picc); |  | ||||||
|  | |||||||
							
								
								
									
										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); |     nfc_worker_change_state(nfc_worker, NfcWorkerStateReady); | ||||||
| 
 | 
 | ||||||
|     if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { |     nfc_worker->reader_analyzer = reader_analyzer_alloc(nfc_worker->storage); | ||||||
|         nfc_worker->debug_pcap_worker = nfc_debug_pcap_alloc(nfc_worker->storage); |  | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|     return nfc_worker; |     return nfc_worker; | ||||||
| } | } | ||||||
| @ -42,7 +40,7 @@ void nfc_worker_free(NfcWorker* nfc_worker) { | |||||||
| 
 | 
 | ||||||
|     furi_record_close(RECORD_STORAGE); |     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); |     free(nfc_worker); | ||||||
| } | } | ||||||
| @ -105,6 +103,8 @@ int32_t nfc_worker_task(void* context) { | |||||||
|         nfc_worker_mf_ultralight_read_auth(nfc_worker); |         nfc_worker_mf_ultralight_read_auth(nfc_worker); | ||||||
|     } else if(nfc_worker->state == NfcWorkerStateMfClassicDictAttack) { |     } else if(nfc_worker->state == NfcWorkerStateMfClassicDictAttack) { | ||||||
|         nfc_worker_mf_classic_dict_attack(nfc_worker); |         nfc_worker_mf_classic_dict_attack(nfc_worker); | ||||||
|  |     } else if(nfc_worker->state == NfcWorkerStateAnalyzeReader) { | ||||||
|  |         nfc_worker_analyze_reader(nfc_worker); | ||||||
|     } |     } | ||||||
|     furi_hal_nfc_sleep(); |     furi_hal_nfc_sleep(); | ||||||
|     nfc_worker_change_state(nfc_worker, NfcWorkerStateReady); |     nfc_worker_change_state(nfc_worker, NfcWorkerStateReady); | ||||||
| @ -117,7 +117,11 @@ static bool nfc_worker_read_mf_ultralight(NfcWorker* nfc_worker, FuriHalNfcTxRxC | |||||||
|     MfUltralightReader reader = {}; |     MfUltralightReader reader = {}; | ||||||
|     MfUltralightData data = {}; |     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 { |     do { | ||||||
|         // Read card
 |         // Read card
 | ||||||
|         if(!furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 200)) break; |         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; |         read_success = true; | ||||||
|     } while(false); |     } while(false); | ||||||
| 
 | 
 | ||||||
|  |     if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { | ||||||
|  |         reader_analyzer_stop(nfc_worker->reader_analyzer); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     return read_success; |     return read_success; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -134,7 +142,11 @@ static bool nfc_worker_read_mf_classic(NfcWorker* nfc_worker, FuriHalNfcTxRxCont | |||||||
|     furi_assert(nfc_worker->callback); |     furi_assert(nfc_worker->callback); | ||||||
|     bool read_success = false; |     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 { |     do { | ||||||
|         // Try to read supported card
 |         // Try to read supported card
 | ||||||
|         FURI_LOG_I(TAG, "Try 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); |     } while(false); | ||||||
| 
 | 
 | ||||||
|  |     if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { | ||||||
|  |         reader_analyzer_stop(nfc_worker->reader_analyzer); | ||||||
|  |     } | ||||||
|     return read_success; |     return read_success; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -169,13 +184,21 @@ static bool nfc_worker_read_mf_desfire(NfcWorker* nfc_worker, FuriHalNfcTxRxCont | |||||||
|     bool read_success = false; |     bool read_success = false; | ||||||
|     MifareDesfireData* data = &nfc_worker->dev_data->mf_df_data; |     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 { |     do { | ||||||
|         if(!furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 300)) break; |         if(!furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 300)) break; | ||||||
|         if(!mf_df_read_card(tx_rx, data)) break; |         if(!mf_df_read_card(tx_rx, data)) break; | ||||||
|         read_success = true; |         read_success = true; | ||||||
|     } while(false); |     } while(false); | ||||||
| 
 | 
 | ||||||
|  |     if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { | ||||||
|  |         reader_analyzer_stop(nfc_worker->reader_analyzer); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     return read_success; |     return read_success; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -184,7 +207,11 @@ static bool nfc_worker_read_bank_card(NfcWorker* nfc_worker, FuriHalNfcTxRxConte | |||||||
|     EmvApplication emv_app = {}; |     EmvApplication emv_app = {}; | ||||||
|     EmvData* result = &nfc_worker->dev_data->emv_data; |     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 { |     do { | ||||||
|         // Read card
 |         // Read card
 | ||||||
|         if(!furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 300)) break; |         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; |         read_success = true; | ||||||
|     } while(false); |     } while(false); | ||||||
| 
 | 
 | ||||||
|  |     if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { | ||||||
|  |         reader_analyzer_stop(nfc_worker->reader_analyzer); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     return read_success; |     return read_success; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -320,18 +351,14 @@ void nfc_worker_read(NfcWorker* nfc_worker) { | |||||||
| 
 | 
 | ||||||
| void nfc_worker_emulate_uid(NfcWorker* nfc_worker) { | void nfc_worker_emulate_uid(NfcWorker* nfc_worker) { | ||||||
|     FuriHalNfcTxRxContext tx_rx = {}; |     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; |     FuriHalNfcDevData* data = &nfc_worker->dev_data->nfc_data; | ||||||
|     NfcReaderRequestData* reader_data = &nfc_worker->dev_data->reader_data; |     NfcReaderRequestData* reader_data = &nfc_worker->dev_data->reader_data; | ||||||
| 
 | 
 | ||||||
|     // TODO add support for RATS
 |     // 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
 |     // 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) { |     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)) { |             if(furi_hal_nfc_tx_rx(&tx_rx, 100)) { | ||||||
|                 reader_data->size = tx_rx.rx_bits / 8; |                 reader_data->size = tx_rx.rx_bits / 8; | ||||||
|                 if(reader_data->size > 0) { |                 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) { | void nfc_worker_emulate_apdu(NfcWorker* nfc_worker) { | ||||||
|     FuriHalNfcTxRxContext tx_rx = {}; |     FuriHalNfcTxRxContext tx_rx = {}; | ||||||
|     nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, &tx_rx, true); |  | ||||||
|     FuriHalNfcDevData params = { |     FuriHalNfcDevData params = { | ||||||
|         .uid = {0xCF, 0x72, 0xd4, 0x40}, |         .uid = {0xCF, 0x72, 0xd4, 0x40}, | ||||||
|         .uid_len = 4, |         .uid_len = 4, | ||||||
| @ -358,6 +384,11 @@ void nfc_worker_emulate_apdu(NfcWorker* nfc_worker) { | |||||||
|         .type = FuriHalNfcTypeA, |         .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) { |     while(nfc_worker->state == NfcWorkerStateEmulateApdu) { | ||||||
|         if(furi_hal_nfc_listen(params.uid, params.uid_len, params.atqa, params.sak, false, 300)) { |         if(furi_hal_nfc_listen(params.uid, params.uid_len, params.atqa, params.sak, false, 300)) { | ||||||
|             FURI_LOG_D(TAG, "POS terminal detected"); |             FURI_LOG_D(TAG, "POS terminal detected"); | ||||||
| @ -370,6 +401,10 @@ void nfc_worker_emulate_apdu(NfcWorker* nfc_worker) { | |||||||
|         furi_hal_nfc_sleep(); |         furi_hal_nfc_sleep(); | ||||||
|         furi_delay_ms(20); |         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) { | 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) { | void nfc_worker_emulate_mf_classic(NfcWorker* nfc_worker) { | ||||||
|     FuriHalNfcTxRxContext tx_rx = {}; |     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; |     FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; | ||||||
|     MfClassicEmulator emulator = { |     MfClassicEmulator emulator = { | ||||||
|         .cuid = nfc_util_bytes2num(&nfc_data->uid[nfc_data->uid_len - 4], 4), |         .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 = {}; |     MfUltralightReader reader = {}; | ||||||
|     mf_ul_reset(data); |     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; |     uint32_t key = 0; | ||||||
|     uint16_t pack = 0; |     uint16_t pack = 0; | ||||||
|     while(nfc_worker->state == NfcWorkerStateReadMfUltralightReadAuth) { |     while(nfc_worker->state == NfcWorkerStateReadMfUltralightReadAuth) { | ||||||
| @ -577,4 +616,61 @@ void nfc_worker_mf_ultralight_read_auth(NfcWorker* nfc_worker) { | |||||||
|             furi_delay_ms(10); |             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, |     NfcWorkerStateMfClassicEmulate, | ||||||
|     NfcWorkerStateReadMfUltralightReadAuth, |     NfcWorkerStateReadMfUltralightReadAuth, | ||||||
|     NfcWorkerStateMfClassicDictAttack, |     NfcWorkerStateMfClassicDictAttack, | ||||||
|  |     NfcWorkerStateAnalyzeReader, | ||||||
|     // Debug
 |     // Debug
 | ||||||
|     NfcWorkerStateEmulateApdu, |     NfcWorkerStateEmulateApdu, | ||||||
|     NfcWorkerStateField, |     NfcWorkerStateField, | ||||||
| @ -54,8 +55,12 @@ typedef enum { | |||||||
|     NfcWorkerEventFoundKeyA, |     NfcWorkerEventFoundKeyA, | ||||||
|     NfcWorkerEventFoundKeyB, |     NfcWorkerEventFoundKeyB, | ||||||
| 
 | 
 | ||||||
|  |     // Detect Reader events
 | ||||||
|  |     NfcWorkerEventDetectReaderMfkeyCollected, | ||||||
|  | 
 | ||||||
|     // Mifare Ultralight events
 |     // Mifare Ultralight events
 | ||||||
|     NfcWorkerEventMfUltralightPassKey, |     NfcWorkerEventMfUltralightPassKey, | ||||||
|  | 
 | ||||||
| } NfcWorkerEvent; | } NfcWorkerEvent; | ||||||
| 
 | 
 | ||||||
| typedef bool (*NfcWorkerCallback)(NfcWorkerEvent event, void* context); | typedef bool (*NfcWorkerCallback)(NfcWorkerEvent event, void* context); | ||||||
|  | |||||||
| @ -12,8 +12,7 @@ | |||||||
| #include <lib/nfc/protocols/mifare_classic.h> | #include <lib/nfc/protocols/mifare_classic.h> | ||||||
| #include <lib/nfc/protocols/mifare_desfire.h> | #include <lib/nfc/protocols/mifare_desfire.h> | ||||||
| #include <lib/nfc/protocols/nfca.h> | #include <lib/nfc/protocols/nfca.h> | ||||||
| 
 | #include <lib/nfc/helpers/reader_analyzer.h> | ||||||
| #include "helpers/nfc_debug_pcap.h" |  | ||||||
| 
 | 
 | ||||||
| struct NfcWorker { | struct NfcWorker { | ||||||
|     FuriThread* thread; |     FuriThread* thread; | ||||||
| @ -27,7 +26,7 @@ struct NfcWorker { | |||||||
| 
 | 
 | ||||||
|     NfcWorkerState state; |     NfcWorkerState state; | ||||||
| 
 | 
 | ||||||
|     NfcDebugPcapWorker* debug_pcap_worker; |     ReaderAnalyzer* reader_analyzer; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| void nfc_worker_change_state(NfcWorker* nfc_worker, NfcWorkerState state); | 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_mf_ul_auth_attack(NfcWorker* nfc_worker); | ||||||
| 
 | 
 | ||||||
| void nfc_worker_emulate_apdu(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