nrfsniff.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508
  1. #include <furi.h>
  2. #include <furi_hal.h>
  3. #include <gui/gui.h>
  4. #include <input/input.h>
  5. #include <notification/notification_messages.h>
  6. #include <stdlib.h>
  7. #include <nrf24.h>
  8. #include <toolbox/stream/file_stream.h>
  9. #include "stdstring.h"
  10. #define LOGITECH_MAX_CHANNEL 85
  11. #define COUNT_THRESHOLD 2
  12. #define DEFAULT_SAMPLE_TIME 4000
  13. #define MAX_ADDRS 100
  14. #define MAX_CONFIRMED 32
  15. #define NRFSNIFF_APP_PATH_FOLDER STORAGE_APP_DATA_PATH_PREFIX
  16. #define NRFSNIFF_APP_FILENAME "addresses.txt"
  17. #define TAG "nrfsniff"
  18. typedef enum {
  19. EventTypeTick,
  20. EventTypeKey,
  21. } EventType;
  22. typedef struct {
  23. EventType type;
  24. InputEvent input;
  25. } PluginEvent;
  26. typedef struct {
  27. FuriMutex* mutex;
  28. } PluginState;
  29. char rate_text_fmt[] = "Transfer rate: %dMbps";
  30. char sample_text_fmt[] = "Sample Time: %d ms";
  31. char channel_text_fmt[] = "Channel: %d Sniffing: %s";
  32. char preamble_text_fmt[] = "Preamble: %02X";
  33. char sniff_text_fmt[] = "Found: %d Unique: %u";
  34. char addresses_header_text[] = "Address,rate";
  35. char sniffed_address_fmt[] = "%s,%d";
  36. char rate_text[46];
  37. char channel_text[38];
  38. char sample_text[32];
  39. char preamble_text[14];
  40. char sniff_text[38];
  41. char sniffed_address[14];
  42. uint8_t target_channel = 0;
  43. uint32_t found_count = 0;
  44. uint32_t unique_saved_count = 0;
  45. uint32_t sample_time = DEFAULT_SAMPLE_TIME;
  46. uint8_t target_rate = 8; // rate can be either 8 (2Mbps) or 0 (1Mbps)
  47. uint8_t target_preamble[] = {0xAA, 0x00};
  48. uint8_t sniffing_state = false;
  49. char top_address[12];
  50. uint8_t candidates[MAX_ADDRS][5] = {0}; // last 100 sniffed addresses
  51. uint32_t counts[MAX_ADDRS];
  52. uint8_t confirmed[MAX_CONFIRMED][5] = {0}; // first 32 confirmed addresses
  53. uint8_t confirmed_idx = 0;
  54. uint32_t total_candidates = 0;
  55. uint32_t candidate_idx = 0;
  56. static int get_addr_index(uint8_t* addr, uint8_t addr_size) {
  57. for(uint32_t i = 0; i < total_candidates; i++) {
  58. uint8_t* arr_item = candidates[i];
  59. if(!memcmp(arr_item, addr, addr_size)) return i;
  60. }
  61. return -1;
  62. }
  63. static int get_highest_idx() {
  64. uint32_t highest = 0;
  65. int highest_idx = 0;
  66. for(uint32_t i = 0; i < total_candidates; i++) {
  67. if(counts[i] > highest) {
  68. highest = counts[i];
  69. highest_idx = i;
  70. }
  71. }
  72. return highest_idx;
  73. }
  74. // if array is full, start over from beginning
  75. static void insert_addr(uint8_t* addr, uint8_t addr_size) {
  76. if(candidate_idx >= MAX_ADDRS) candidate_idx = 0;
  77. memcpy(candidates[candidate_idx], addr, addr_size);
  78. counts[candidate_idx] = 1;
  79. if(total_candidates < MAX_ADDRS) total_candidates++;
  80. candidate_idx++;
  81. }
  82. static void render_callback(Canvas* const canvas, void* ctx) {
  83. furi_assert(ctx);
  84. const PluginState* plugin_state = ctx;
  85. furi_mutex_acquire(plugin_state->mutex, FuriWaitForever);
  86. uint8_t rate = 2;
  87. char sniffing[] = "Yes";
  88. // border around the edge of the screen
  89. canvas_draw_frame(canvas, 0, 0, 128, 64);
  90. canvas_set_font(canvas, FontSecondary);
  91. if(target_rate == 0) rate = 1;
  92. if(!sniffing_state) strcpy(sniffing, "No");
  93. snprintf(rate_text, sizeof(rate_text), rate_text_fmt, (int)rate);
  94. snprintf(channel_text, sizeof(channel_text), channel_text_fmt, (int)target_channel, sniffing);
  95. snprintf(sample_text, sizeof(sample_text), sample_text_fmt, (int)sample_time);
  96. //snprintf(preamble_text, sizeof(preamble_text), preamble_text_fmt, target_preamble[0]);
  97. snprintf(sniff_text, sizeof(sniff_text), sniff_text_fmt, found_count, unique_saved_count);
  98. snprintf(
  99. sniffed_address, sizeof(sniffed_address), sniffed_address_fmt, top_address, (int)rate);
  100. canvas_draw_str_aligned(canvas, 10, 10, AlignLeft, AlignBottom, rate_text);
  101. canvas_draw_str_aligned(canvas, 10, 20, AlignLeft, AlignBottom, sample_text);
  102. canvas_draw_str_aligned(canvas, 10, 30, AlignLeft, AlignBottom, channel_text);
  103. //canvas_draw_str_aligned(canvas, 10, 30, AlignLeft, AlignBottom, preamble_text);
  104. canvas_draw_str_aligned(canvas, 10, 40, AlignLeft, AlignBottom, sniff_text);
  105. canvas_draw_str_aligned(canvas, 30, 50, AlignLeft, AlignBottom, addresses_header_text);
  106. canvas_draw_str_aligned(canvas, 30, 60, AlignLeft, AlignBottom, sniffed_address);
  107. furi_mutex_release(plugin_state->mutex);
  108. }
  109. static void input_callback(InputEvent* input_event, void* ctx) {
  110. furi_assert(ctx);
  111. FuriMessageQueue* event_queue = ctx;
  112. PluginEvent event = {.type = EventTypeKey, .input = *input_event};
  113. furi_message_queue_put(event_queue, &event, FuriWaitForever);
  114. }
  115. static void hexlify(uint8_t* in, uint8_t size, char* out) {
  116. memset(out, 0, size * 2);
  117. for(int i = 0; i < size; i++)
  118. snprintf(out + strlen(out), sizeof(out + strlen(out)), "%02X", in[i]);
  119. }
  120. static bool save_addr_to_file(
  121. Storage* storage,
  122. uint8_t* data,
  123. uint8_t size,
  124. NotificationApp* notification) {
  125. size_t file_size = 0;
  126. uint8_t linesize = 0;
  127. char filepath[42] = {0};
  128. char addrline[14] = {0};
  129. char ending[4];
  130. uint8_t* file_contents;
  131. uint8_t rate = 1;
  132. Stream* stream = file_stream_alloc(storage);
  133. if(target_rate == 8) rate = 2;
  134. snprintf(ending, sizeof(ending), ",%d\n", rate);
  135. hexlify(data, size, addrline);
  136. nrf_strcat(addrline, ending);
  137. linesize = strlen(addrline);
  138. strcpy(filepath, NRFSNIFF_APP_PATH_FOLDER);
  139. nrf_strcat(filepath, "/");
  140. nrf_strcat(filepath, NRFSNIFF_APP_FILENAME);
  141. stream_seek(stream, 0, StreamOffsetFromStart);
  142. // check if address already exists in file
  143. if(file_stream_open(stream, filepath, FSAM_READ_WRITE, FSOM_OPEN_APPEND)) {
  144. bool found = false;
  145. file_size = stream_size(stream);
  146. stream_seek(stream, 0, StreamOffsetFromStart);
  147. if(file_size > 0) {
  148. file_contents = malloc(file_size + 1);
  149. memset(file_contents, 0, file_size + 1);
  150. if(stream_read(stream, file_contents, file_size) > 0) {
  151. char* line = nrf_strtok((char*)file_contents, "\n");
  152. while(line != NULL) {
  153. if(!memcmp(line, addrline, 12)) {
  154. found = true;
  155. break;
  156. }
  157. line = nrf_strtok(NULL, "\n");
  158. }
  159. }
  160. free(file_contents);
  161. }
  162. if(found) {
  163. FURI_LOG_I(TAG, "Address exists in file. Ending save process.");
  164. stream_free(stream);
  165. return false;
  166. } else {
  167. if(stream_write(stream, (uint8_t*)addrline, linesize) != linesize) {
  168. FURI_LOG_I(TAG, "Failed to write bytes to file stream.");
  169. stream_free(stream);
  170. return false;
  171. } else {
  172. FURI_LOG_I(TAG, "Found a new address: %s", addrline);
  173. FURI_LOG_I(TAG, "Save successful!");
  174. notification_message(notification, &sequence_success);
  175. stream_free(stream);
  176. unique_saved_count++;
  177. return true;
  178. }
  179. }
  180. } else {
  181. FURI_LOG_I(TAG, "Cannot open file \"%s\"", filepath);
  182. stream_free(stream);
  183. return false;
  184. }
  185. }
  186. void alt_address(uint8_t* addr, uint8_t* altaddr) {
  187. uint8_t macmess_hi_b[4];
  188. uint32_t macmess_hi;
  189. uint8_t macmess_lo;
  190. uint8_t preserved;
  191. uint8_t tmpaddr[5];
  192. // swap bytes
  193. for(int i = 0; i < 5; i++)
  194. tmpaddr[i] = addr[4 - i];
  195. // get address into 32-bit and 8-bit variables
  196. memcpy(macmess_hi_b, tmpaddr, 4);
  197. macmess_lo = tmpaddr[4];
  198. macmess_hi = bytes_to_int32(macmess_hi_b, true);
  199. //preserve lowest bit from hi to shift to low
  200. preserved = macmess_hi & 1;
  201. macmess_hi >>= 1;
  202. macmess_lo >>= 1;
  203. macmess_lo = (preserved << 7) | macmess_lo;
  204. int32_to_bytes(macmess_hi, macmess_hi_b, true);
  205. memcpy(tmpaddr, macmess_hi_b, 4);
  206. tmpaddr[4] = macmess_lo;
  207. // swap bytes back
  208. for(int i = 0; i < 5; i++)
  209. altaddr[i] = tmpaddr[4 - i];
  210. }
  211. static bool previously_confirmed(uint8_t* addr) {
  212. bool found = false;
  213. for(int i = 0; i < MAX_CONFIRMED; i++) {
  214. if(!memcmp(confirmed[i], addr, 5)) {
  215. found = true;
  216. break;
  217. }
  218. }
  219. return found;
  220. }
  221. static void wrap_up(Storage* storage, NotificationApp* notification) {
  222. uint8_t ch;
  223. uint8_t addr[5];
  224. uint8_t altaddr[5];
  225. char trying[12];
  226. int idx;
  227. uint8_t rate = 0;
  228. if(target_rate == 8) rate = 2;
  229. nrf24_set_idle(nrf24_HANDLE);
  230. while(true) {
  231. idx = get_highest_idx();
  232. if(counts[idx] < COUNT_THRESHOLD) break;
  233. counts[idx] = 0;
  234. memcpy(addr, candidates[idx], 5);
  235. hexlify(addr, 5, trying);
  236. FURI_LOG_I(TAG, "trying address %s", trying);
  237. ch = nrf24_find_channel(nrf24_HANDLE, addr, addr, 5, rate, 2, LOGITECH_MAX_CHANNEL, false);
  238. FURI_LOG_I(TAG, "find_channel returned %d", (int)ch);
  239. if(ch > LOGITECH_MAX_CHANNEL) {
  240. alt_address(addr, altaddr);
  241. hexlify(altaddr, 5, trying);
  242. FURI_LOG_I(TAG, "trying alternate address %s", trying);
  243. ch = nrf24_find_channel(
  244. nrf24_HANDLE, altaddr, altaddr, 5, rate, 2, LOGITECH_MAX_CHANNEL, false);
  245. FURI_LOG_I(TAG, "find_channel returned %d", (int)ch);
  246. memcpy(addr, altaddr, 5);
  247. }
  248. if(ch <= LOGITECH_MAX_CHANNEL) {
  249. hexlify(addr, 5, top_address);
  250. found_count++;
  251. save_addr_to_file(storage, addr, 5, notification);
  252. if(confirmed_idx < MAX_CONFIRMED) memcpy(confirmed[confirmed_idx++], addr, 5);
  253. break;
  254. }
  255. }
  256. }
  257. static void clear_cache() {
  258. found_count = 0;
  259. unique_saved_count = 0;
  260. confirmed_idx = 0;
  261. candidate_idx = 0;
  262. target_channel = 2;
  263. total_candidates = 0;
  264. memset(candidates, 0, sizeof(candidates));
  265. memset(counts, 0, sizeof(counts));
  266. memset(confirmed, 0, sizeof(confirmed));
  267. }
  268. static void start_sniffing() {
  269. nrf24_init_promisc_mode(nrf24_HANDLE, target_channel, target_rate);
  270. }
  271. int32_t nrfsniff_app(void* p) {
  272. UNUSED(p);
  273. uint8_t address[5] = {0};
  274. uint32_t start = 0;
  275. hexlify(address, 5, top_address);
  276. FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent));
  277. PluginState* plugin_state = malloc(sizeof(PluginState));
  278. plugin_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
  279. if(!plugin_state->mutex) {
  280. furi_message_queue_free(event_queue);
  281. FURI_LOG_E(TAG, "cannot create mutex\r\n");
  282. free(plugin_state);
  283. return 255;
  284. }
  285. uint8_t attempts = 0;
  286. bool otg_was_enabled = furi_hal_power_is_otg_enabled();
  287. while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) {
  288. furi_hal_power_enable_otg();
  289. furi_delay_ms(10);
  290. }
  291. furi_delay_ms(100);
  292. nrf24_init();
  293. bool nrf_ready = false;
  294. if(nrf24_check_connected(nrf24_HANDLE)) {
  295. nrf_ready = true;
  296. } else {
  297. nrf_ready = false;
  298. FURI_LOG_E(TAG, "NRF24 not connected");
  299. }
  300. // Set system callbacks
  301. ViewPort* view_port = view_port_alloc();
  302. view_port_draw_callback_set(view_port, render_callback, plugin_state);
  303. view_port_input_callback_set(view_port, input_callback, event_queue);
  304. // Open GUI and register view_port
  305. Gui* gui = furi_record_open(RECORD_GUI);
  306. gui_add_view_port(gui, view_port, GuiLayerFullscreen);
  307. NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
  308. Storage* storage = furi_record_open(RECORD_STORAGE);
  309. storage_common_migrate(storage, EXT_PATH("nrfsniff"), NRFSNIFF_APP_PATH_FOLDER);
  310. storage_common_mkdir(storage, NRFSNIFF_APP_PATH_FOLDER);
  311. PluginEvent event;
  312. for(bool processing = true; processing;) {
  313. FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
  314. furi_mutex_acquire(plugin_state->mutex, FuriWaitForever);
  315. if(event_status == FuriStatusOk) {
  316. // press events
  317. if(event.type == EventTypeKey) {
  318. if(event.input.type == InputTypePress ||
  319. (event.input.type == InputTypeLong && event.input.key == InputKeyBack)) {
  320. switch(event.input.key) {
  321. case InputKeyUp:
  322. // toggle rate 1/2Mbps
  323. if(!sniffing_state) {
  324. if(target_rate == 0)
  325. target_rate = 8;
  326. else
  327. target_rate = 0;
  328. }
  329. break;
  330. case InputKeyDown:
  331. // toggle preamble
  332. if(!sniffing_state) {
  333. if(target_preamble[0] == 0x55)
  334. target_preamble[0] = 0xAA;
  335. else
  336. target_preamble[0] = 0x55;
  337. nrf24_set_src_mac(nrf24_HANDLE, target_preamble, 2);
  338. }
  339. break;
  340. case InputKeyRight:
  341. // increment channel
  342. //if(!sniffing_state && target_channel <= LOGITECH_MAX_CHANNEL)
  343. // target_channel++;
  344. sample_time += 500;
  345. break;
  346. case InputKeyLeft:
  347. // decrement channel
  348. //if(!sniffing_state && target_channel > 0) target_channel--;
  349. if(sample_time > 500) sample_time -= 500;
  350. break;
  351. case InputKeyOk:
  352. // toggle sniffing
  353. if(nrf_ready) {
  354. sniffing_state = !sniffing_state;
  355. if(sniffing_state) {
  356. clear_cache();
  357. start_sniffing();
  358. start = furi_get_tick();
  359. } else {
  360. wrap_up(storage, notification);
  361. }
  362. } else {
  363. notification_message(notification, &sequence_error);
  364. if(nrf24_check_connected(nrf24_HANDLE)) {
  365. nrf_ready = true;
  366. } else {
  367. nrf_ready = false;
  368. FURI_LOG_E(TAG, "NRF24 not connected");
  369. }
  370. }
  371. break;
  372. case InputKeyBack:
  373. if(nrf_ready) {
  374. if(sniffing_state) {
  375. wrap_up(storage, notification);
  376. }
  377. } else {
  378. if(nrf24_check_connected(nrf24_HANDLE)) {
  379. nrf_ready = true;
  380. } else {
  381. nrf_ready = false;
  382. FURI_LOG_E(TAG, "NRF24 not connected");
  383. }
  384. }
  385. processing = false;
  386. break;
  387. default:
  388. break;
  389. }
  390. }
  391. }
  392. }
  393. if(sniffing_state) {
  394. if(nrf24_sniff_address(nrf24_HANDLE, 5, address)) {
  395. int idx;
  396. uint8_t* top_addr;
  397. if(!previously_confirmed(address)) {
  398. idx = get_addr_index(address, 5);
  399. if(idx == -1)
  400. insert_addr(address, 5);
  401. else
  402. counts[idx]++;
  403. top_addr = candidates[get_highest_idx()];
  404. hexlify(top_addr, 5, top_address);
  405. }
  406. }
  407. if(furi_get_tick() - start >= sample_time) {
  408. target_channel++;
  409. if(target_channel > LOGITECH_MAX_CHANNEL) target_channel = 2;
  410. {
  411. wrap_up(storage, notification);
  412. start_sniffing();
  413. }
  414. start = furi_get_tick();
  415. }
  416. }
  417. furi_mutex_release(plugin_state->mutex);
  418. view_port_update(view_port);
  419. }
  420. clear_cache();
  421. sample_time = DEFAULT_SAMPLE_TIME;
  422. target_rate = 8; // rate can be either 8 (2Mbps) or 0 (1Mbps)
  423. sniffing_state = false;
  424. nrf24_deinit();
  425. if(furi_hal_power_is_otg_enabled() && !otg_was_enabled) {
  426. furi_hal_power_disable_otg();
  427. }
  428. view_port_enabled_set(view_port, false);
  429. gui_remove_view_port(gui, view_port);
  430. furi_record_close(RECORD_GUI);
  431. furi_record_close(RECORD_NOTIFICATION);
  432. furi_record_close(RECORD_STORAGE);
  433. view_port_free(view_port);
  434. furi_message_queue_free(event_queue);
  435. furi_mutex_free(plugin_state->mutex);
  436. free(plugin_state);
  437. return 0;
  438. }