Just Call Me Koko пре 3 година
родитељ
комит
3375672ab5

+ 30 - 0
esp32_marauder/CommandLine.cpp

@@ -116,6 +116,7 @@ void CommandLine::runCommand(String input) {
     
     // WiFi sniff/scan
     Serial.println(HELP_SCANAP_CMD);
+    Serial.println(HELP_SCANSTA_CMD);
     Serial.println(HELP_SNIFF_RAW_CMD);
     Serial.println(HELP_SNIFF_BEACON_CMD);
     Serial.println(HELP_SNIFF_PROBE_CMD);
@@ -131,6 +132,7 @@ void CommandLine::runCommand(String input) {
     // WiFi Aux
     Serial.println(HELP_LIST_AP_CMD_A);
     Serial.println(HELP_LIST_AP_CMD_B);
+    Serial.println(HELP_LIST_AP_CMD_C);
     Serial.println(HELP_SEL_CMD_A);
     Serial.println(HELP_SEL_CMD_B);
     Serial.println(HELP_SSID_CMD_A);
@@ -258,6 +260,15 @@ void CommandLine::runCommand(String input) {
       #endif
       wifi_scan_obj.StartScan(WIFI_SCAN_RAW_CAPTURE, TFT_WHITE);
     }
+    // Scan stations
+    else if (cmd_args.get(0) == SCANSTA_CMD) {    
+      Serial.println("Starting Station scan. Stop with " + (String)STOPSCAN_CMD);  
+      #ifdef HAS_SCREEN
+        display_obj.clearScreen();
+        menu_function_obj.drawStatusBar();
+      #endif
+      wifi_scan_obj.StartScan(WIFI_SCAN_STATION, TFT_ORANGE);
+    }
     // Beacon sniff
     else if (cmd_args.get(0) == SNIFF_BEACON_CMD) {
       Serial.println("Starting Beacon sniff. Stop with " + (String)STOPSCAN_CMD);
@@ -498,6 +509,7 @@ void CommandLine::runCommand(String input) {
   if (cmd_args.get(0) == LIST_AP_CMD) {
     int ap_sw = this->argSearch(&cmd_args, "-a");
     int ss_sw = this->argSearch(&cmd_args, "-s");
+    int cl_sw = this->argSearch(&cmd_args, "-c");
 
     // List APs
     if (ap_sw != -1) {
@@ -517,6 +529,24 @@ void CommandLine::runCommand(String input) {
           Serial.println("[" + (String)i + "] " + ssids->get(i).essid);
       }
     }
+    else if (cl_sw != -1) {
+      char sta_mac[] = "00:00:00:00:00:00";
+      for (int x = 0; x < access_points->size(); x++) {
+        Serial.println("[" + (String)x + "] " + access_points->get(x).essid + " " + (String)access_points->get(x).rssi + ":");
+        for (int i = 0; i < access_points->get(x).stations->size(); i++) {
+          wifi_scan_obj.getMAC(sta_mac, stations->get(access_points->get(x).stations->get(i)).mac, 0);
+          if (stations->get(i).selected) {
+            Serial.print("  [" + (String)i + "] ");
+            Serial.print(sta_mac);
+            Serial.println(" (selected)");
+          }
+          else {
+            Serial.print("  [" + (String)i + "] ");
+            Serial.println(sta_mac);
+          }
+        }
+      }
+    }
     else {
       Serial.println("You did not specify which list to show");
       return;

+ 4 - 0
esp32_marauder/CommandLine.h

@@ -24,6 +24,7 @@ extern SDInterface sd_obj;
 extern Settings settings_obj;
 extern LinkedList<AccessPoint>* access_points;
 extern LinkedList<ssid>* ssids;
+extern LinkedList<Station>* stations;
 extern const String PROGMEM version_number;
 
 //// Commands
@@ -38,6 +39,7 @@ const char PROGMEM SETTINGS_CMD[] = "settings";
 
 // WiFi sniff/scan
 const char PROGMEM SCANAP_CMD[] = "scanap";
+const char PROGMEM SCANSTA_CMD[] = "scansta";
 const char PROGMEM SNIFF_RAW_CMD[] = "sniffraw";
 const char PROGMEM SNIFF_BEACON_CMD[] = "sniffbeacon";
 const char PROGMEM SNIFF_PROBE_CMD[] = "sniffprobe";
@@ -77,6 +79,7 @@ const char PROGMEM HELP_SETTINGS_CMD[] = "settings [-s <setting> enable/disable>
 
 // WiFi sniff/scan
 const char PROGMEM HELP_SCANAP_CMD[] = "scanap";
+const char PROGMEM HELP_SCANSTA_CMD[] = "scansta";
 const char PROGMEM HELP_SNIFF_RAW_CMD[] = "sniffraw";
 const char PROGMEM HELP_SNIFF_BEACON_CMD[] = "sniffbeacon";
 const char PROGMEM HELP_SNIFF_PROBE_CMD[] = "sniffprobe";
@@ -92,6 +95,7 @@ const char PROGMEM HELP_ATTACK_CMD[] = "attack -t <beacon [-l/-r/-a]/deauth [-s
 // WiFi Aux
 const char PROGMEM HELP_LIST_AP_CMD_A[] = "list -s";
 const char PROGMEM HELP_LIST_AP_CMD_B[] = "list -a";
+const char PROGMEM HELP_LIST_AP_CMD_C[] = "list -c";
 const char PROGMEM HELP_SEL_CMD_A[] = "select -a <index (comma separated)>";
 const char PROGMEM HELP_SEL_CMD_B[] = "select -s <index (comma separated)>";
 const char PROGMEM HELP_SSID_CMD_A[] = "ssid -a [-g <count>/-n <name>]";

+ 9 - 2
esp32_marauder/MenuFunctions.cpp

@@ -867,6 +867,7 @@ void MenuFunctions::main(uint32_t currentTime)
       // Stop the current scan
       if ((wifi_scan_obj.currentScanMode == WIFI_SCAN_PROBE) ||
           (wifi_scan_obj.currentScanMode == WIFI_SCAN_RAW_CAPTURE) ||
+          (wifi_scan_obj.currentScanMode == WIFI_SCAN_STATION) ||
           (wifi_scan_obj.currentScanMode == WIFI_SCAN_AP) ||
           (wifi_scan_obj.currentScanMode == WIFI_SCAN_TARGET_AP) ||
           (wifi_scan_obj.currentScanMode == WIFI_SCAN_TARGET_AP_FULL) ||
@@ -914,6 +915,7 @@ void MenuFunctions::main(uint32_t currentTime)
     {
       // Stop the current scan
       if ((wifi_scan_obj.currentScanMode == WIFI_SCAN_PROBE) ||
+          (wifi_scan_obj.currentScanMode == WIFI_SCAN_RAW_CAPTURE) ||
           (wifi_scan_obj.currentScanMode == WIFI_SCAN_RAW_CAPTURE) ||
           (wifi_scan_obj.currentScanMode == WIFI_SCAN_AP) ||
           (wifi_scan_obj.currentScanMode == WIFI_SCAN_TARGET_AP) ||
@@ -1565,11 +1567,11 @@ void MenuFunctions::RunSetup()
     this->drawStatusBar();
     wifi_scan_obj.StartScan(WIFI_SCAN_PWN, TFT_RED);
   });
-  addNodes(&wifiSnifferMenu, text_table1[48], TFT_ORANGE, NULL, ESPRESSIF, [this]() {
+  /*addNodes(&wifiSnifferMenu, text_table1[48], TFT_ORANGE, NULL, ESPRESSIF, [this]() {
     display_obj.clearScreen();
     this->drawStatusBar();
     wifi_scan_obj.StartScan(WIFI_SCAN_ESPRESSIF, TFT_ORANGE);
-  });
+  });*/
   addNodes(&wifiSnifferMenu, text_table1[49], TFT_MAGENTA, NULL, BEACON_SNIFF, [this]() {
     display_obj.clearScreen();
     this->drawStatusBar();
@@ -1580,6 +1582,11 @@ void MenuFunctions::RunSetup()
     this->drawStatusBar();
     wifi_scan_obj.StartScan(WIFI_SCAN_RAW_CAPTURE, TFT_WHITE);
   });
+  addNodes(&wifiSnifferMenu, text_table1[59], TFT_ORANGE, NULL, PACKET_MONITOR, [this]() {
+    display_obj.clearScreen();
+    this->drawStatusBar();
+    wifi_scan_obj.StartScan(WIFI_SCAN_STATION, TFT_WHITE);
+  });
 
   // Build WiFi attack menu
   wifiAttackMenu.parentMenu = &wifiMenu; // Main Menu is second menu parent

+ 206 - 1
esp32_marauder/WiFiScan.cpp

@@ -8,6 +8,7 @@ int num_eapol = 0;
 
 LinkedList<ssid>* ssids;
 LinkedList<AccessPoint>* access_points;
+LinkedList<Station>* stations;
 
 extern "C" int ieee80211_raw_frame_sanity_check(int32_t arg, int32_t arg2, int32_t arg3){
     if (arg == 31337)
@@ -133,6 +134,7 @@ void WiFiScan::RunSetup() {
     
   ssids = new LinkedList<ssid>();
   access_points = new LinkedList<AccessPoint>();
+  stations = new LinkedList<Station>();
 
   #ifdef HAS_BT
     NimBLEDevice::setScanFilterMode(CONFIG_BTDM_SCAN_DUPL_TYPE_DEVICE);
@@ -283,6 +285,8 @@ void WiFiScan::StartScan(uint8_t scan_mode, uint16_t color)
     RunBeaconScan(scan_mode, color);
   else if (scan_mode == WIFI_SCAN_RAW_CAPTURE)
     RunRawScan(scan_mode, color);
+  else if (scan_mode == WIFI_SCAN_STATION)
+    RunStationScan(scan_mode, color);
   else if (scan_mode == WIFI_SCAN_TARGET_AP)
     RunAPScan(scan_mode, color);
   else if (scan_mode == WIFI_SCAN_TARGET_AP_FULL)
@@ -433,6 +437,7 @@ void WiFiScan::StopScan(uint8_t scan_mode)
   if ((currentScanMode == WIFI_SCAN_PROBE) ||
   (currentScanMode == WIFI_SCAN_AP) ||
   (currentScanMode == WIFI_SCAN_RAW_CAPTURE) ||
+  (currentScanMode == WIFI_SCAN_STATION) ||
   (currentScanMode == WIFI_SCAN_TARGET_AP) ||
   (currentScanMode == WIFI_SCAN_TARGET_AP_FULL) ||
   (currentScanMode == WIFI_SCAN_PWN) ||
@@ -1094,6 +1099,45 @@ void WiFiScan::RunBeaconScan(uint8_t scan_mode, uint16_t color)
   initTime = millis();
 }
 
+void WiFiScan::RunStationScan(uint8_t scan_mode, uint16_t color)
+{
+  sd_obj.openCapture("station");
+
+  #ifdef MARAUDER_FLIPPER
+    flipper_led.sniffLED();
+  #else
+    led_obj.setMode(MODE_SNIFF);
+  #endif
+  
+  #ifdef HAS_SCREEN
+    display_obj.TOP_FIXED_AREA_2 = 48;
+    display_obj.tteBar = true;
+    display_obj.print_delay_1 = 15;
+    display_obj.print_delay_2 = 10;
+    display_obj.initScrollValues(true);
+    display_obj.tft.setTextWrap(false);
+    display_obj.tft.setTextColor(TFT_WHITE, color);
+    #ifndef MARAUDER_MINI
+      display_obj.tft.fillRect(0,16,240,16, color);
+      display_obj.tft.drawCentreString(text_table1[58],120,16,2);
+      display_obj.touchToExit();
+    #endif
+    display_obj.tft.setTextColor(TFT_GREEN, TFT_BLACK);
+    display_obj.setupScrollArea(display_obj.TOP_FIXED_AREA_2, BOT_FIXED_AREA);
+  #endif
+  
+  esp_wifi_init(&cfg);
+  esp_wifi_set_storage(WIFI_STORAGE_RAM);
+  esp_wifi_set_mode(WIFI_MODE_NULL);
+  esp_wifi_start();
+  esp_wifi_set_promiscuous(true);
+  esp_wifi_set_promiscuous_filter(&filt);
+  esp_wifi_set_promiscuous_rx_cb(&stationSnifferCallback);
+  esp_wifi_set_channel(set_channel, WIFI_SECOND_CHAN_NONE);
+  this->wifi_initialized = true;
+  initTime = millis();
+}
+
 void WiFiScan::RunRawScan(uint8_t scan_mode, uint16_t color)
 {
   sd_obj.openCapture("raw");
@@ -1595,6 +1639,7 @@ void WiFiScan::apSnifferCallbackFull(void* buf, wifi_promiscuous_pkt_type_t type
         ap.bssid[4] = snifferPacket->payload[14];
         ap.bssid[5] = snifferPacket->payload[15];
         ap.selected = false;
+        ap.stations = new LinkedList<int>();
         
         ap.beacon = new LinkedList<char>();
 
@@ -1741,7 +1786,8 @@ void WiFiScan::apSnifferCallback(void* buf, wifi_promiscuous_pkt_type_t type)
                            snifferPacket->payload[15]},
                           false,
                           NULL,
-                          snifferPacket->rx_ctrl.rssi};
+                          snifferPacket->rx_ctrl.rssi,
+                          new LinkedList<int>()};
 
         access_points->add(ap);
 
@@ -1828,6 +1874,164 @@ void WiFiScan::beaconSnifferCallback(void* buf, wifi_promiscuous_pkt_type_t type
   }
 }
 
+void WiFiScan::stationSnifferCallback(void* buf, wifi_promiscuous_pkt_type_t type) {
+  bool save_packet = settings_obj.loadSetting<bool>(text_table4[7]);
+  
+  wifi_promiscuous_pkt_t *snifferPacket = (wifi_promiscuous_pkt_t*)buf;
+  WifiMgmtHdr *frameControl = (WifiMgmtHdr*)snifferPacket->payload;
+  wifi_pkt_rx_ctrl_t ctrl = (wifi_pkt_rx_ctrl_t)snifferPacket->rx_ctrl;
+  int len = snifferPacket->rx_ctrl.sig_len;
+
+  String display_string = "";
+  String mac = "";
+
+  if (type == WIFI_PKT_MGMT)
+  {
+    len -= 4;
+    int fctl = ntohs(frameControl->fctl);
+    const wifi_ieee80211_packet_t *ipkt = (wifi_ieee80211_packet_t *)snifferPacket->payload;
+    const WifiMgmtHdr *hdr = &ipkt->hdr;
+  }
+
+  char ap_addr[] = "00:00:00:00:00:00";
+  char dst_addr[] = "00:00:00:00:00:00";
+
+  int ap_index = 0;
+
+  // Check if frame has ap in list of APs and determine position
+  uint8_t frame_offset = 0;
+  int offsets[2] = {10, 4};
+  bool matched_ap = false;
+  bool ap_is_src = false;
+
+  bool mac_match = true;
+
+  for (int y = 0; y < 2; y++) {
+    for (int i = 0; i < access_points->size(); i++) {
+      mac_match = true;
+      
+      for (int x = 0; x < 6; x++) {
+        //Serial.println((String)snifferPacket->payload[x + 10] + " | " + (String)access_points->get(i).bssid[x]);
+        if (snifferPacket->payload[x + offsets[y]] != access_points->get(i).bssid[x]) {
+          mac_match = false;
+          break;
+        }
+      }
+      if (mac_match) {
+        matched_ap = true;
+        if (offsets[y] == 10)
+          ap_is_src = true;
+        ap_index = i;
+        getMAC(ap_addr, snifferPacket->payload, offsets[y]);
+        break;
+      }
+    }
+    if (matched_ap)
+      break;
+  }
+
+  // If did not find ap from list in frame, drop frame
+  if (!matched_ap)
+    return;
+  else {
+    if (ap_is_src)
+      frame_offset = 4;
+    else
+      frame_offset = 10;
+  }
+  /*else {
+    // Check to make sure frame does not go to broadcast
+    char sta_addr[] = "00:00:00:00:00:00";
+    getMAC(dst_addr, snifferPacket->payload, 4);
+    if (strcmp(dst_addr, "ff:ff:ff:ff:ff:ff") == 0)
+      Serial.print("Frame destination is broadcast -> ");
+    if (ap_is_src) {
+      Serial.print("src: ");
+      getMAC(sta_addr, snifferPacket->payload, 4);
+    }
+    else {
+      Serial.print("dst: ");
+      getMAC(sta_addr, snifferPacket->payload, 10);
+    }
+    Serial.print(ap_addr);
+    Serial.print(" sta: ");
+    Serial.println(sta_addr);
+  }*/
+
+  /*  Stuff to care about now
+   *  ap_is_src
+   *  ap_index
+   */
+  
+
+  // Check if we already have this station
+  bool in_list = false;
+  for (int i = 0; i < stations->size(); i++) {
+    mac_match = true;
+    
+    for (int x = 0; x < 6; x++) {
+      //Serial.println((String)snifferPacket->payload[x + 10] + " | " + (String)access_points->get(i).bssid[x]);
+      if (snifferPacket->payload[x + frame_offset] != stations->get(i).mac[x]) {
+        mac_match = false;
+        //Serial.println("MACs do not match");
+        break;
+      }
+    }
+    if (mac_match) {
+      in_list = true;
+      break;
+    }
+  }
+
+  getMAC(dst_addr, snifferPacket->payload, 4);
+
+  // Check if dest is broadcast
+  if ((in_list) || (strcmp(dst_addr, "ff:ff:ff:ff:ff:ff") == 0))
+    return;
+  
+  // Add to list of stations
+  Station sta = {
+                {snifferPacket->payload[frame_offset],
+                 snifferPacket->payload[frame_offset + 1],
+                 snifferPacket->payload[frame_offset + 2],
+                 snifferPacket->payload[frame_offset + 3],
+                 snifferPacket->payload[frame_offset + 4],
+                 snifferPacket->payload[frame_offset + 5]},
+                false};
+
+  stations->add(sta);
+
+  // Print findings to serial
+  Serial.print((String)stations->size() + ": ");
+  
+  char sta_addr[] = "00:00:00:00:00:00";
+  
+  if (ap_is_src) {
+    Serial.print("ap: ");
+    Serial.print(ap_addr);
+    Serial.print(" -> sta: ");
+    getMAC(sta_addr, snifferPacket->payload, 4);
+    Serial.println(sta_addr);
+  }
+  else {
+    Serial.print("sta: ");
+    getMAC(sta_addr, snifferPacket->payload, 10);
+    Serial.print(sta_addr);
+    Serial.print(" -> ap: ");
+    Serial.println(ap_addr);
+  }
+
+  // Add station index to AP in list
+  //access_points->get(ap_index).stations->add(stations->size() - 1);
+
+  AccessPoint ap = access_points->get(ap_index);
+  ap.stations->add(stations->size() - 1);
+
+  access_points->set(ap_index, ap);
+
+  //Serial.println(access_points->get(ap_index).essid + ": " + (String)access_points->get(ap_index).stations->size());
+}
+
 void WiFiScan::rawSnifferCallback(void* buf, wifi_promiscuous_pkt_type_t type)
 {
   bool save_packet = settings_obj.loadSetting<bool>(text_table4[7]);
@@ -3176,6 +3380,7 @@ void WiFiScan::main(uint32_t currentTime)
   // WiFi operations
   if ((currentScanMode == WIFI_SCAN_PROBE) ||
   (currentScanMode == WIFI_SCAN_AP) ||
+  (currentScanMode == WIFI_SCAN_STATION) ||
   (currentScanMode == WIFI_SCAN_TARGET_AP) ||
   (currentScanMode == WIFI_SCAN_PWN) ||
   (currentScanMode == WIFI_SCAN_ESPRESSIF) ||

+ 9 - 0
esp32_marauder/WiFiScan.h

@@ -65,6 +65,7 @@
 #define WIFI_SCAN_ACTIVE_EAPOL 23
 #define WIFI_ATTACK_DEAUTH_MANUAL 24
 #define WIFI_SCAN_RAW_CAPTURE 25
+#define WIFI_SCAN_STATION 26
 
 #define GRAPH_REFRESH 100
 
@@ -97,6 +98,12 @@ struct AccessPoint {
   bool selected;
   LinkedList<char>* beacon;
   int rssi;
+  LinkedList<int>* stations;
+};
+
+struct Station {
+  uint8_t mac[6];
+  bool selected;
 };
 
 class WiFiScan
@@ -254,6 +261,7 @@ class WiFiScan
     void RunPwnScan(uint8_t scan_mode, uint16_t color);
     void RunBeaconScan(uint8_t scan_mode, uint16_t color);
     void RunRawScan(uint8_t scan_mode, uint16_t color);
+    void RunStationScan(uint8_t scan_mode, uint16_t color);
     void RunDeauthScan(uint8_t scan_mode, uint16_t color);
     void RunEapolScan(uint8_t scan_mode, uint16_t color);
     void RunProbeScan(uint8_t scan_mode, uint16_t color);
@@ -324,6 +332,7 @@ class WiFiScan
     static void pwnSnifferCallback(void* buf, wifi_promiscuous_pkt_type_t type);
     static void beaconSnifferCallback(void* buf, wifi_promiscuous_pkt_type_t type);
     static void rawSnifferCallback(void* buf, wifi_promiscuous_pkt_type_t type);
+    static void stationSnifferCallback(void* buf, wifi_promiscuous_pkt_type_t type);
     static void apSnifferCallback(void* buf, wifi_promiscuous_pkt_type_t type);
     static void apSnifferCallbackFull(void* buf, wifi_promiscuous_pkt_type_t type);
     static void deauthSnifferCallback(void* buf, wifi_promiscuous_pkt_type_t type);

+ 3 - 3
esp32_marauder/configs.h

@@ -5,14 +5,14 @@
   #define POLISH_POTATO
   
   //#define MARAUDER_MINI
-  //#define MARAUDER_V4
+  #define MARAUDER_V4
   //#define MARAUDER_V6
   //#define MARAUDER_KIT
   //#define GENERIC_ESP32
-  #define MARAUDER_FLIPPER
+  //#define MARAUDER_FLIPPER
   //#define ESP32_LDDB
 
-  #define MARAUDER_VERSION "v0.9.20"
+  #define MARAUDER_VERSION "v0.9.21"
 
   //// BUTTON DEFINITIONS
   #ifdef MARAUDER_MINI

+ 2 - 1
esp32_marauder/lang_var.h

@@ -97,6 +97,7 @@ PROGMEM const char text1_55[] = "Join WiFi";
 PROGMEM const char text1_56[] = "Select APs";
 PROGMEM const char text1_57[] = "AP Clone Spam";
 PROGMEM const char text1_58[] = "Raw Capture";
+PROGMEM const char text1_59[] = "Station Sniff";
 
 
 //SDInterface.cpp texts
@@ -173,7 +174,7 @@ PROGMEM const char text4_44[] = " AP Scan ";
 
 //Making tables
 PROGMEM const char *text_table0[] = {text0_0,text0_1, text0_2, text0_3, text0_4, text0_5, text0_6, text0_7, text0_8};
-PROGMEM const char *text_table1[] = {text1_0,text1_1,text1_2,text1_3,text1_4,text1_5,text1_6,text1_7,text1_8,text1_9,text1_10,text1_11,text1_12,text1_13,text1_14,text1_15,text1_16,text1_17,text1_18,text1_19,text1_20,text1_21,text1_22,text1_23,text1_24,text1_25,text1_26,text1_27,text1_28,text1_29,text1_30,text1_31,text1_32,text1_33,text1_34,text1_35,text1_36,text1_37,text1_38,text1_39,text1_40,text1_41,text1_42,text1_43,text1_44,text1_45,text1_46,text1_47,text1_48,text1_49,text1_50,text1_51,text1_52,text1_53,text1_54,text1_55,text1_56,text1_57,text1_58};
+PROGMEM const char *text_table1[] = {text1_0,text1_1,text1_2,text1_3,text1_4,text1_5,text1_6,text1_7,text1_8,text1_9,text1_10,text1_11,text1_12,text1_13,text1_14,text1_15,text1_16,text1_17,text1_18,text1_19,text1_20,text1_21,text1_22,text1_23,text1_24,text1_25,text1_26,text1_27,text1_28,text1_29,text1_30,text1_31,text1_32,text1_33,text1_34,text1_35,text1_36,text1_37,text1_38,text1_39,text1_40,text1_41,text1_42,text1_43,text1_44,text1_45,text1_46,text1_47,text1_48,text1_49,text1_50,text1_51,text1_52,text1_53,text1_54,text1_55,text1_56,text1_57,text1_58,text1_59};
 PROGMEM const char *text_table2[] = {text2_0,text2_1,text2_2,text2_3,text2_4,text2_5,text2_6,text2_7,text2_8,text2_9,text2_10,text2_11,text2_12,text2_13,text2_14};
 PROGMEM const char *text_table3[] = {text3_0,text3_1,text3_2,text3_3,text3_4,text3_5};
 PROGMEM const char *text_table4[] = {text4_0,text4_1,text4_2,text4_3,text4_4,text4_5,text4_6,text4_7,text1_54,text4_9,text4_10,text4_11,text4_12,text4_13,text4_14,text4_15,text4_16,text4_17,text4_18,text4_19,text4_20,text4_21,text4_22,text4_23,text4_24,text4_25,text4_26,text4_27,text4_28,text4_29,text4_30,text4_31,text4_32,text4_33,text4_34,text4_35,text4_36,text4_37,text4_38,text4_39,text4_40,text4_41,text4_42,text4_43,text4_44};