453 lines
14 KiB
C++
453 lines
14 KiB
C++
/* This software is licensed under the MIT License: https://github.com/spacehuhntech/esp8266_deauther */
|
|
|
|
#include "Scan.h"
|
|
|
|
#include "settings.h"
|
|
#include "wifi.h"
|
|
|
|
Scan::Scan() {
|
|
list = new SimpleList<uint16_t>;
|
|
}
|
|
|
|
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, true);
|
|
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
|
|
wifi::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
|
|
wifi::stopAP();
|
|
wifi_promiscuous_enable(true);
|
|
}
|
|
|
|
/* Stop scan */
|
|
else if (mode == SCAN_MODE_OFF) {
|
|
wifi_promiscuous_enable(false);
|
|
|
|
if (settings::getWebSettings().enabled) wifi::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::getSnifferSettings().channel_time)) {
|
|
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, true);
|
|
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);
|
|
} |