#include "Scan.h" Scan::Scan() { list = new SimpleList; } void Scan::sniffer(uint8_t* buf, uint16_t len) { if (!isSniffing()) return; packets++; if (len < 28) return; // drop frames that are too short to have a valid MAC header if ((buf[12] == 0xc0) || (buf[12] == 0xa0)) { tmpDeauths++; return; } // drop beacon frames, probe requests/responses and deauth/disassociation frames if ((buf[12] == 0x80) || (buf[12] == 0x40) || (buf[12] == 0x50) /* || buf[12] == 0xc0 || buf[12] == 0xa0*/) return; // only allow data frames // if(buf[12] != 0x08 && buf[12] != 0x88) return; uint8_t* macTo = &buf[16]; uint8_t* macFrom = &buf[22]; if (macBroadcast(macTo) || macBroadcast(macFrom) || !macValid(macTo) || !macValid(macFrom) || macMulticast(macTo) || macMulticast(macFrom)) return; int accesspointNum = findAccesspoint(macFrom); if (accesspointNum >= 0) { stations.add(macTo, accesspoints.getID(accesspointNum)); } else { accesspointNum = findAccesspoint(macTo); if (accesspointNum >= 0) { stations.add(macFrom, accesspoints.getID(accesspointNum)); } } } int Scan::findAccesspoint(uint8_t* mac) { for (int i = 0; i < accesspoints.count(); i++) { if (memcmp(accesspoints.getMac(i), mac, 6) == 0) return i; } return -1; } void Scan::start(uint8_t mode) { start(mode, sniffTime, scan_continue_mode, continueTime, channelHop, wifi_channel); } void Scan::start(uint8_t mode, uint32_t time, uint8_t nextmode, uint32_t continueTime, bool channelHop, uint8_t channel) { if (mode != SCAN_MODE_OFF) stop(); setWifiChannel(channel); Scan::continueStartTime = currentTime; Scan::snifferPacketTime = continueStartTime; Scan::snifferOutputTime = continueStartTime; Scan::continueTime = continueTime; Scan::sniffTime = time; Scan::channelHop = channelHop; Scan::scanMode = mode; Scan::scan_continue_mode = nextmode; if ((sniffTime > 0) && (sniffTime < 1000)) sniffTime = 1000; // Serial.printf("mode: %u, time: %u, continue-mode: %u, continueTime: %u, channelHop: %u, channel: %u\r\n", mode, // time, scan_continue_mode, continueTime, channelHop, channel); /* AP Scan */ if ((mode == SCAN_MODE_APS) || (mode == SCAN_MODE_ALL)) { // remove old results accesspoints.removeAll(); stations.removeAll(); // start AP scan prntln(SC_START_AP); WiFi.scanNetworks(true, true); } /* Station Scan */ else if (mode == SCAN_MODE_STATIONS) { // start station scan if (accesspoints.count() < 1) { start(SCAN_MODE_ALL); // Serial.println(str(SC_ERROR_NO_AP)); return; } snifferStartTime = currentTime; prnt(SC_START_CLIENT); if (sniffTime > 0) prnt(String(sniffTime / 1000) + S); else prnt(SC_INFINITELY); if (!channelHop) { prnt(SC_ON_CHANNEL); prnt(wifi_channel); } prntln(); // enable sniffer stopAP(); wifi_promiscuous_enable(true); } else if (mode == SCAN_MODE_SNIFFER) { deauths = tmpDeauths; tmpDeauths = 0; snifferStartTime = currentTime; prnt(SS_START_SNIFFER); if (sniffTime > 0) prnt(String(sniffTime / 1000) + S); else prnt(SC_INFINITELY); prnt(SC_ON_CHANNEL); prntln(channelHop ? str(SC_ONE_TO) + (String)14 : (String)wifi_channel); // enable sniffer stopAP(); wifi_promiscuous_enable(true); } /* Stop scan */ else if (mode == SCAN_MODE_OFF) { wifi_promiscuous_enable(false); if (settings.getWebInterface()) resumeAP(); prntln(SC_STOPPED); save(true); if (scan_continue_mode != SCAN_MODE_OFF) { prnt(SC_RESTART); prnt(int(continueTime / 1000)); prntln(SC_CONTINUE); } } /* ERROR */ else { prnt(SC_ERROR_MODE); prntln(mode); return; } } void Scan::update() { if (scanMode == SCAN_MODE_OFF) { // restart scan if it is continuous if (scan_continue_mode != SCAN_MODE_OFF) { if (currentTime - continueStartTime > continueTime) start(scan_continue_mode); } return; } // sniffer if (isSniffing()) { // update packet list every 1s if (currentTime - snifferPacketTime > 1000) { snifferPacketTime = currentTime; list->add(packets); if (list->size() > SCAN_PACKET_LIST_SIZE) list->remove(0); deauths = tmpDeauths; tmpDeauths = 0; packets = 0; } // print status every 3s if (currentTime - snifferOutputTime > 3000) { char s[100]; if (sniffTime > 0) { sprintf(s, str(SC_OUTPUT_A).c_str(), getPercentage(), packets, stations.count(), deauths); } else { sprintf(s, str(SC_OUTPUT_B).c_str(), packets, stations.count(), deauths); } prnt(String(s)); snifferOutputTime = currentTime; } // channel hopping if (channelHop && (currentTime - snifferChannelTime > settings.getChTime())) { snifferChannelTime = currentTime; if (scanMode == SCAN_MODE_STATIONS) nextChannel(); // go to next channel an AP is on else setChannel(wifi_channel + 1); // go to next channel } } // APs if ((scanMode == SCAN_MODE_APS) || (scanMode == SCAN_MODE_ALL)) { int16_t results = WiFi.scanComplete(); if (results >= 0) { for (int16_t i = 0; i < results && i < 256; i++) { if (channelHop || (WiFi.channel(i) == wifi_channel)) accesspoints.add(i, false); } accesspoints.sort(); accesspoints.printAll(); if (scanMode == SCAN_MODE_ALL) { delay(30); start(SCAN_MODE_STATIONS); } else start(SCAN_MODE_OFF); } } // Stations else if ((sniffTime > 0) && (currentTime > snifferStartTime + sniffTime)) { wifi_promiscuous_enable(false); if (scanMode == SCAN_MODE_STATIONS) { stations.sort(); stations.printAll(); } start(SCAN_MODE_OFF); } } void Scan::setup() { save(true); } void Scan::stop() { scan_continue_mode = SCAN_MODE_OFF; start(SCAN_MODE_OFF); } void Scan::setChannel(uint8_t ch) { if (ch > 14) ch = 1; else if (ch < 1) ch = 14; wifi_promiscuous_enable(0); setWifiChannel(ch); wifi_promiscuous_enable(1); } void Scan::nextChannel() { if (accesspoints.count() > 1) { uint8_t ch = wifi_channel; do { ch++; if (ch > 14) ch = 1; } while (!apWithChannel(ch)); setChannel(ch); } } bool Scan::apWithChannel(uint8_t ch) { for (int i = 0; i < accesspoints.count(); i++) if (accesspoints.getCh(i) == ch) return true; return false; } void Scan::save(bool force, String filePath) { String tmp = FILE_PATH; FILE_PATH = filePath; save(true); FILE_PATH = tmp; } void Scan::save(bool force) { if (!(accesspoints.changed || stations.changed) && !force) return; // Accesspoints String buf = String(OPEN_CURLY_BRACKET) + String(DOUBLEQUOTES) + str(SC_JSON_APS) + String(DOUBLEQUOTES) + String( DOUBLEPOINT) + String(OPEN_BRACKET); // {"aps":[ if (!writeFile(FILE_PATH, buf)) { // overwrite old file prnt(F_ERROR_SAVING); prntln(FILE_PATH); return; } buf = String(); // clear buffer uint32_t apCount = accesspoints.count(); for (uint32_t i = 0; i < apCount; i++) { buf += String(OPEN_BRACKET) + String(DOUBLEQUOTES) + escape(accesspoints.getSSID(i)) + String(DOUBLEQUOTES) + String(COMMA); // ["ssid", buf += String(DOUBLEQUOTES) + escape(accesspoints.getNameStr(i)) + String(DOUBLEQUOTES) + String(COMMA); // "name", buf += String(accesspoints.getCh(i)) + String(COMMA); // 1, buf += String(accesspoints.getRSSI(i)) + String(COMMA); // -30, buf += String(DOUBLEQUOTES) + accesspoints.getEncStr(i) + String(DOUBLEQUOTES) + String(COMMA); // "wpa2", buf += String(DOUBLEQUOTES) + accesspoints.getMacStr(i) + String(DOUBLEQUOTES) + String(COMMA); // "00:11:22:00:11:22", buf += String(DOUBLEQUOTES) + accesspoints.getVendorStr(i) + String(DOUBLEQUOTES) + String(COMMA); // "vendor", buf += b2s(accesspoints.getSelected(i)) + String(CLOSE_BRACKET); // false] if (i < apCount - 1) buf += String(COMMA); // , if (buf.length() >= 1024) { if (!appendFile(FILE_PATH, buf)) { prnt(F_ERROR_SAVING); prntln(FILE_PATH); return; } buf = String(); // clear buffer } } // Stations buf += String(CLOSE_BRACKET) + String(COMMA) + String(DOUBLEQUOTES) + str(SC_JSON_STATIONS) + String(DOUBLEQUOTES) + String(DOUBLEPOINT) + String(OPEN_BRACKET); // ],"stations":[; uint32_t stationCount = stations.count(); for (uint32_t i = 0; i < stationCount; i++) { buf += String(OPEN_BRACKET) + String(DOUBLEQUOTES) + stations.getMacStr(i) + String(DOUBLEQUOTES) + String(COMMA); // ["00:11:22:00:11:22", buf += String(stations.getCh(i)) + String(COMMA); // 1, buf += String(DOUBLEQUOTES) + stations.getNameStr(i) + String(DOUBLEQUOTES) + String(COMMA); // "name", buf += String(DOUBLEQUOTES) + stations.getVendorStr(i) + String(DOUBLEQUOTES) + String(COMMA); // "vendor", buf += String(*stations.getPkts(i)) + String(COMMA); // 123, buf += String(stations.getAP(i)) + String(COMMA); // 0, buf += String(DOUBLEQUOTES) + stations.getTimeStr(i) + String(DOUBLEQUOTES) + String(COMMA); // "<1min", buf += b2s(stations.getSelected(i)) + String(CLOSE_BRACKET); // false] if (i < stationCount - 1) buf += String(COMMA); // , if (buf.length() >= 1024) { if (!appendFile(FILE_PATH, buf)) { prnt(F_ERROR_SAVING); prntln(FILE_PATH); return; } buf = String(); // clear buffer } } buf += String(CLOSE_BRACKET) + String(CLOSE_CURLY_BRACKET); // ]} if (!appendFile(FILE_PATH, buf)) { prnt(F_ERROR_SAVING); prntln(FILE_PATH); return; } accesspoints.changed = false; stations.changed = false; prnt(SC_SAVED_IN); prntln(FILE_PATH); } uint32_t Scan::countSelected() { return accesspoints.selected() + stations.selected() + names.selected(); } uint32_t Scan::countAll() { return accesspoints.count() + stations.count() + names.count(); } bool Scan::isScanning() { return scanMode != SCAN_MODE_OFF; } bool Scan::isSniffing() { return scanMode == SCAN_MODE_STATIONS || scanMode == SCAN_MODE_SNIFFER; } uint8_t Scan::getPercentage() { if (!isSniffing()) return 0; return (currentTime - snifferStartTime) / (sniffTime / 100); } void Scan::selectAll() { accesspoints.selectAll(); stations.selectAll(); names.selectAll(); } void Scan::deselectAll() { accesspoints.deselectAll(); stations.deselectAll(); names.deselectAll(); } void Scan::printAll() { accesspoints.printAll(); stations.printAll(); names.printAll(); ssids.printAll(); } void Scan::printSelected() { accesspoints.printSelected(); stations.printSelected(); names.printSelected(); } uint32_t Scan::getPackets(int i) { if (list->size() < SCAN_PACKET_LIST_SIZE) { uint8_t translatedNum = SCAN_PACKET_LIST_SIZE - list->size(); if (i >= translatedNum) return list->get(i - translatedNum); return 0; } else { return list->get(i); } } String Scan::getMode() { switch (scanMode) { case SCAN_MODE_OFF: return str(SC_MODE_OFF); case SCAN_MODE_APS: return str(SC_MODE_AP); case SCAN_MODE_STATIONS: return str(SC_MODE_ST); case SCAN_MODE_ALL: return str(SC_MODE_ALL); case SCAN_MODE_SNIFFER: return str(SC_MODE_SNIFFER); default: return String(); } } double Scan::getScaleFactor(uint8_t height) { return (double)height / (double)getMaxPacket(); } uint32_t Scan::getMaxPacket() { uint16_t max = 0; for (uint8_t i = 0; i < list->size(); i++) { if (list->get(i) > max) max = list->get(i); } return max; } uint32_t Scan::getPacketRate() { return list->get(list->size() - 1); }