Repository: tenable/pi_sniffer
Branch: master
Commit: 0d19e5ea340e
Files: 39
Total size: 152.5 KB
Directory structure:
gitextract_gsji9l86/
├── .gitignore
├── LICENSE
├── README.md
├── configs/
│ ├── kismet.conf
│ ├── ntp.conf
│ └── rc.local
├── image_gen/
│ └── create_image.sh
├── pi_sniffer/
│ ├── CMakeLists.txt
│ ├── pi_sniffer.conf
│ └── src/
│ ├── ap.cpp
│ ├── ap.hpp
│ ├── client.cpp
│ ├── client.hpp
│ ├── configuration.cpp
│ ├── configuration.hpp
│ ├── input/
│ │ ├── kismet_drone.cpp
│ │ ├── kismet_drone.hpp
│ │ ├── pcap.cpp
│ │ └── pcap.hpp
│ ├── main.cpp
│ ├── packet.cpp
│ ├── packet.hpp
│ ├── probed_network.cpp
│ ├── probed_network.hpp
│ ├── protocols/
│ │ ├── eapol11.cpp
│ │ ├── eapol11.hpp
│ │ ├── ieee80211.cpp
│ │ ├── ieee80211.hpp
│ │ ├── llcsnap.cpp
│ │ └── llcsnap.hpp
│ ├── stats.cpp
│ ├── stats.hpp
│ └── util/
│ ├── convert.cpp
│ ├── convert.hpp
│ ├── kml_maker.cpp
│ ├── kml_maker.hpp
│ ├── pcap_output.cpp
│ └── pcap_output.hpp
└── ui/
└── display_handler.py
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
# Compiled source #
###################
*.com
*.class
*.dll
*.exe
*.o
*.so
# Packages #
############
# it's better to unpack these files and commit the raw source
# git has its own built in compression methods
*.7z
*.dmg
*.gz
*.iso
*.jar
*.rar
*.tar
*.zip
*.img
# Logs and databases #
######################
*.log
*.sql
*.sqlite
*.pcap
*.csv
# OS generated files #
######################
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
================================================
FILE: LICENSE
================================================
This file is part of Pi Sniffer.
Pi Sniffer is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Pi Sniffer is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Foobar. If not, see .
================================================
FILE: README.md
================================================
# Pi Sniffer
Pi Sniffer is a Wi-Fi sniffer built on the Raspberry Pi Zero W. While there are many excellent sniffing platforms out there, Pi Sniffer is unique for it's small size, real time display of captured data, and handling of user input.
## Current Release Image
You can download an an RPI image of this project from the "Releases" page. If you don't trust that, you can generate your own release by using the image_gen/create_image.sh script.
## Project Goals
The goal of this project was to create a Wi-Fi sniffer that I could carry around in my pocket, easily view real time status, decrypt packets on the fly, and change antenna channels as needed. Also, I wanted this project to be cheap (less than $100) and require no soldering.
## Hardware
The project was conceived with the goal to avoid any type of soldering. While Pi Sniffer does require the GPIO header on the Raspberry Pi Zero W, you can buy that pre-soldered. So I'm gonna claim no soldering required.
The base install requires:
* [Raspberry Pi Zero WH](https://www.adafruit.com/product/3708)
* [Adafruit 128x64 OLED Bonnet](https://www.adafruit.com/product/3531)
* A power source. I suggest one of the following:
* [Anker PowerCore 5000](https://www.amazon.com/dp/B01CU1EC6Y)
* [Anker E1 Astro](https://www.amazon.com/Anker-bar-Sized-Portable-High-Speed-Technology/dp/B00P7N0320)
* [UPS-Lite](https://www.tindie.com/products/rachel/ups-lite-for-raspberry-pi-zero/)
* Any SD card 8GB or larger
Additionally, you can configure the device with any of the following add-ons (and still reasonably be called pocket sized):
* [Secondary antenna by CanaKit](https://www.amazon.com/CanaKit-Raspberry-Wireless-Adapter-Dongle/dp/B00GFAN498)
* [Ublox-7 GPS](https://www.amazon.com/WINGONEER%C2%AE%C2%AE-Antenna-VK-172-Receiver-Windows/dp/B07F6TJG9L)
* [MicroUSB to USB adapters](https://www.amazon.com/Ksmile%C2%AE-Female-Adapter-SamSung-tablets/dp/B01C6032G0)
* [USB MiniHub](https://www.adafruit.com/product/2991)
## Software
Download the release image and flash it to an SD card. Stick the SD card into your RPI Zero WH and you should be good to go! By default, SSH should be enabled. Use the default pi:raspberry credentials. The device's hostname is pisniffer so something along the following lines should get you in:
```sh
ssh pi@pisniffer.local
```
## Controls
Pi Sniffer isn't unique just due to it's size but it also offers controls. The user can start and stop sniffing. Change channels. Deauth clients. And more. Here are some images showing how to use the controls.
### Start, Stop, and Shutdown
To start sniffing hit the #6 button. To stop sniffing hit the #5 button. To shutdown the device hold #5 and #6.

### Channel Hoppping
To change to a specific channel, rotate to the antenna screen and hit #6. This will cycle you through the available channels plus hopping.

### Deauth Attack
To deauth a client, find them in the client view and hit #6.

### Lock display
Sometimes it's beneficial to lock the screen and controls. To do so, rotate to the lock screen and hit #6. To unlock you need to hit #5 and push up on the joystick at the same time.

## Issues and Pull Requests
Issues and pull requests are welcome. I only ask that you provide enough information to recreate the issue or information about why the pull request should be accepted.
================================================
FILE: configs/kismet.conf
================================================
# Version of Kismet config
version=2009-newcore
# Do we process the contents of data frames? If this is enabled, data
# frames will be truncated to the headers only immediately after frame type
# detection. This will disable IP detection, etc, however it is likely
# safer (and definitely more polite) if monitoring networks you do not own.
hidedata=true
# Do we allow plugins to be used? This will load plugins from the system
# and user plugin directiories when set to true (See the README for the default
# plugin locations).
allowplugins=false
# See the README for full information on the new source format
# ncsource=interface:options
# for example:
ncsource=wlan0:uuid=00000000-0000-0000-0000-000000000001
ncsource=wlan1:uuid=00000000-0000-0000-0000-000000000002
# ncsource=wifi0:type=madwifi
# ncsource=wlan0:name=intel,hop=false,channel=11
# Control which channels we like to spend more time on. By default, the list
# of channels is pulled from the driver automatically. By setting preferred channels,
# if they are present in the channel list, they'll be set with a timing delay so that
# more time is spent on them. Since 1, 6, 11 are the common default channels, it makes
# sense to spend more time monitoring them.
# For finer control, see further down in the config for the channellist= directives.
preferredchannels=1,6,11
# How many channels per second do we hop? (1-10)
channelvelocity=3
# Default channel lists
# These channel lists MUST BE PRESENT for Kismet to work properly. While it is
# possible to change these, it is not recommended. These are used when the supported
# channel list can not be found for the source; to force using these instead of
# the detected supported channels, override with channellist= in the source defintion
#
# IN GENERAL, if you think you want to modify these, what you REALLY want to do is
# copy them and use channellist= in the packet source.
channellist=IEEE80211b:1:3,6:3,11:3,2,7,3,8,4,9,5,10
channellist=IEEE80211a:36,40,44,48,52,56,60,64,149,153,157,161,165
channellist=IEEE80211ab:1:3,6:3,11:3,2,7,3,8,4,9,5,10,36,40,44,48,52,56,60,64,149,153,157,161,165
# Client/server listen config
listen=tcp://127.0.0.1:2501
# People allowed to connect, comma seperated IP addresses or network/mask
# blocks. Netmasks can be expressed as dotted quad (/255.255.255.0) or as
# numbers (/24)
allowedhosts=127.0.0.1
# Maximum number of concurrent GUI's
maxclients=1
# Maximum backlog before we start throwing out or killing clients. The
# bigger this number, the more memory and the more power it will use.
maxbacklog=50
# Server + Drone config options. To have a Kismet server export live packets
# as if it were a drone, uncomment these.
dronelisten=tcp://127.0.0.1:3501
droneallowedhosts=127.0.0.1
dronemaxclients=1
droneringlen=65535
# Do we have a GPS?
gps=true
# Do we use a locally serial attached GPS, or use a gpsd server, or
# use a fixed virtual gps?
# (Pick only one)
gpstype=gpsd
# Host:port that GPSD is running on. This can be localhost OR remote!
gpshost=localhost:2947
# gpstype=serial
# What serial device do we look for the GPS on?
# gpsdevice=/dev/rfcomm0
# gpstype=virtual
# gpsposition=100,-50
# gpsaltitude=1234
# Do we lock the mode? This overrides coordinates of lock "0", which will
# generate some bad information until you get a GPS lock, but it will
# fix problems with GPS units with broken NMEA that report lock 0
gpsmodelock=false
# Do we try to reconnect if we lose our link to the GPS, or do we just
# let it die and be disabled?
gpsreconnect=true
# Do we export packets over tun/tap virtual interfaces?
tuntap_export=false
tuntap_device=kistap0
# Is transmission of the keys to the client allowed? This may be a security
# risk for some. If you disable this, you will not be able to query keys from
# a client.
allowkeytransmit=true
# How often (in seconds) do we write all our data files (0 to disable)
writeinterval=0
# Do we use sound?
# Not to be confused with GUI sound parameter, this controls wether or not the
# server itself will play sound. Primarily for headless or automated systems.
enablesound=false
# Format of the pcap dump (PPI or 80211)
pcapdumpformat=ppi
# Default log title
logdefault=Kismet
# logtemplate - Filename logging template.
# This is, at first glance, really nasty and ugly, but you'll hardly ever
# have to touch it so don't complain too much.
#
# %p is replaced by the logging prefix + '/'
# %n is replaced by the logging instance name
# %d is replaced by the starting date as Mon-DD-YYYY
# %D is replaced by the current date as YYYYMMDD
# %t is replaced by the starting time as HH-MM-SS
# %i is replaced by the increment log in the case of multiple logs
# %l is replaced by the log type (pcapdump, strings, etc)
# %h is replaced by the home directory
logtemplate=%p%n-%D-%t-%i.%l
# Where state info, etc, is stored. You shouldnt ever need to change this.
# This is a directory.
configdir=/tmp/.kismet/
================================================
FILE: configs/ntp.conf
================================================
# /etc/ntp.conf, configuration for ntpd; see ntp.conf(5) for help
driftfile /var/lib/ntp/ntp.drift
# Leap seconds definition provided by tzdata
leapfile /usr/share/zoneinfo/leap-seconds.list
# Enable this if you want statistics to be logged.
#statsdir /var/log/ntpstats/
statistics loopstats peerstats clockstats
filegen loopstats file loopstats type day enable
filegen peerstats file peerstats type day enable
filegen clockstats file clockstats type day enable
# You do need to talk to an NTP server or two (or three).
#server ntp.your-provider.example
# pool.ntp.org maps to about 1000 low-stratum NTP servers. Your server will
# pick a different set every time it starts up. Please consider joining the
# pool:
pool 0.debian.pool.ntp.org iburst
pool 1.debian.pool.ntp.org iburst
pool 2.debian.pool.ntp.org iburst
pool 3.debian.pool.ntp.org iburst
# Access control configuration; see /usr/share/doc/ntp-doc/html/accopt.html for
# details. The web page
# might also be helpful.
#
# Note that "restrict" applies to both servers and clients, so a configuration
# that might be intended to block requests from certain clients could also end
# up blocking replies from your own upstream servers.
# By default, exchange time with everybody, but don't allow configuration.
restrict -4 default kod notrap nomodify nopeer noquery limited
restrict -6 default kod notrap nomodify nopeer noquery limited
# Local users may interrogate the ntp server more closely.
restrict 127.0.0.1
restrict ::1
# Needed for adding pool entries
restrict source notrap nomodify noquery
# Clients from this (example!) subnet have unlimited access, but only if
# cryptographically authenticated.
#restrict 192.168.123.0 mask 255.255.255.0 notrust
# If you want to provide time to your local subnet, change the next line.
# (Again, the address is an example only.)
#broadcast 192.168.123.255
# If you want to listen to time broadcasts on your local subnet, de-comment the
# next lines. Please do this only if you trust everybody on the network!
#disable auth
#broadcastclient
# GPS Serial data reference
server 127.127.28.0 minpoll 4 maxpoll 4
fudge 127.127.28.0 time1 0.0 refid GPS
# GPS PPS reference
server 127.127.28.1 minpoll 4 maxpoll 4 prefer
fudge 127.127.28.1 refid PPS
================================================
FILE: configs/rc.local
================================================
#!/bin/sh -e
#
# rc.local
#
# This script is executed at the end of each multiuser runlevel.
# Make sure that the script will "exit 0" on success or any other
# value on error.
#
# In order to enable or disable this script just change the execution
# bits.
#
# By default this script does nothing.
# Print the IP address
_IP=$(hostname -I) || true
if [ "$_IP" ]; then
printf "My IP address is %s\n" "$_IP"
fi
(python3 /home/pi/display_handler.py > /dev/null)&
exit 0
================================================
FILE: image_gen/create_image.sh
================================================
#!/usr/bin/env bash
# based on: https://wiki.debian.org/RaspberryPi/qemu-user-static
# and https://z4ziggy.wordpress.com/2015/05/04/from-bochs-to-chroot/
#
# and pwnagotchi's old create_sibling script:
# https://raw.githubusercontent.com/evilsocket/pwnagotchi/55bac8a8b913c158132953d8186d278621df032a/scripts/create_sibling.sh
set -eu
if [[ "$EUID" -ne 0 ]]; then
echo "Run this script as root!"
exit 1
fi
REQUIREMENTS=( wget gunzip git dd e2fsck resize2fs parted losetup qemu-system-x86_64 )
DEBREQUIREMENTS=( wget gzip git parted qemu-system-x86 qemu-user-static )
REPO_DIR="$(dirname "$(dirname "$(realpath "$0")")")"
TMP_DIR="${REPO_DIR}/tmp"
MNT_DIR="${TMP_DIR}/mnt"
THIS_DIR=$(pwd)
HOST_NAME="pisniffer"
OUTPUT_NAME="pi_sniffer.img"
IMAGE_SIZE="7"
function check_dependencies() {
if [ -f /etc/debian_version ];
then
echo "[+] Checking Debian dependencies"
for REQ in "${DEBREQUIREMENTS[@]}"; do
if ! dpkg -s "$REQ" >/dev/null 2>&1; then
echo "Dependency check failed for ${REQ}; use 'apt install ${REQ}' to install"
exit 1
fi
done
fi
echo "[+] Checking dependencies"
for REQ in "${REQUIREMENTS[@]}"; do
if ! type "$REQ" >/dev/null 2>&1; then
echo "Dependency check failed for ${REQ}"
exit 1
fi
done
if ! test -e /usr/bin/qemu-arm-static; then
echo "[-] You need the package \"qemu-user-static\" for this to work."
exit 1
fi
if ! systemctl is-active systemd-binfmt.service >/dev/null 2>&1; then
mkdir -p "/lib/binfmt.d"
echo ':qemu-arm:M::\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x28\x00:\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\xfe\xff\xff\xff:/usr/bin/qemu-arm-static:F' > /lib/binfmt.d/qemu-arm-static.conf
systemctl restart systemd-binfmt.service
fi
}
function get_raspbian() {
URL="https://downloads.raspberrypi.org/raspbian_lite_latest"
echo "[+] Downloading raspbian lite latest to raspbian.zip"
mkdir -p "${TMP_DIR}"
wget --show-progress -qcO "${TMP_DIR}/raspbian.zip" "$URL"
echo "[+] Unpacking raspbian.zip to raspbian.img"
gunzip -c "${TMP_DIR}/raspbian.zip" > "${TMP_DIR}/raspbian.img"
}
function setup_raspbian(){
# Note that we 'extend' the raspbian.img
echo "[+] Resizing full image to ${IMAGE_SIZE}G"
# Full disk-space using image (appends to raspbian image)
dd if=/dev/zero bs=1G count="${IMAGE_SIZE}" >> "${TMP_DIR}/raspbian.img"
truncate --size="${IMAGE_SIZE}"G "${TMP_DIR}/raspbian.img"
echo "[+] Setup loop device"
mkdir -p "${MNT_DIR}"
LOOP_PATH="$(losetup --find --partscan --show "${TMP_DIR}/raspbian.img")"
PART2_START="$(parted -s "$LOOP_PATH" -- print | awk '$1==2{ print $2 }')"
parted -s "$LOOP_PATH" rm 2
parted -s "$LOOP_PATH" mkpart primary "$PART2_START" 100%
echo "[+] Check FS"
e2fsck -y -f "${LOOP_PATH}p2"
echo "[+] Resize FS"
resize2fs "${LOOP_PATH}p2"
echo "[+] Device is ${LOOP_PATH}"
echo "[+] Unmount if already mounted with other img"
mountpoint -q "${MNT_DIR}" && umount -R "${MNT_DIR}"
echo "[+] Mount /"
mount -o rw "${LOOP_PATH}p2" "${MNT_DIR}"
echo "[+] Mount /boot"
mount -o rw "${LOOP_PATH}p1" "${MNT_DIR}/boot"
mount --bind /dev "${MNT_DIR}/dev/"
mount --bind /sys "${MNT_DIR}/sys/"
mount --bind /proc "${MNT_DIR}/proc/"
mount --bind /dev/pts "${MNT_DIR}/dev/pts"
cp /usr/bin/qemu-arm-static "${MNT_DIR}/usr/bin"
cp /etc/resolv.conf "${MNT_DIR}/etc/resolv.conf"
}
function provision_raspbian() {
# copy in pi sniffer
cd ${MNT_DIR}/home/pi/
cp -r ${REPO_DIR}/pi_sniffer .
cp ${REPO_DIR}/ui/* .
cp ${REPO_DIR}/configs/rc.local ../../etc/
chmod +x /etc/rc.local
cp ${REPO_DIR}/configs/kismet.conf .
cp ${REPO_DIR}/configs/ntp.conf ../../etc/
cd ${MNT_DIR}
sed -i'' 's/^\([^#]\)/#\1/g' etc/ld.so.preload # add comments
echo "[+] Run chroot commands"
LANG=C LC_ALL=C LC_CTYPE=C chroot . bin/bash -x </etc/dphys-swapfile
systemctl enable dphys-swapfile.service
# compile pi sniffer
cd home/pi/pi_sniffer
mkdir build
cd build
cmake ..
make
cd /
# configure pwnagotchi
echo -e "$HOST_NAME" > /etc/hostname
sed -i "s@^127\.0\.0\.1 .*@127.0.0.1 localhost "$HOST_NAME" "$HOST_NAME".local@g" /etc/hosts
# interface dependencies
pip3 install adafruit-circuitpython-ssd1306 spidev RPI.GPIO adafruit-blinka
echo "dtparam=i2c_arm=on" >> boot/config.txt
echo "dtparam=spi=on" >> boot/config.txt
echo "i2c-dev" >> etc/modules
echo "hi" >> boot/ssh
# Re4son-Kernel
echo "deb http://http.re4son-kernel.com/re4son/ kali-pi main" > /etc/apt/sources.list.d/re4son.list
wget -O - https://re4son-kernel.com/keys/http/archive-key.asc | apt-key add -
apt update
apt install -y kalipi-kernel kalipi-bootloader kalipi-re4son-firmware kalipi-kernel-headers libraspberrypi0 libraspberrypi-dev libraspberrypi-doc libraspberrypi-bin
# Fix PARTUUID
PUUID_ROOT="\$(blkid "\$(df / --output=source | tail -1)" | grep -Po 'PARTUUID="\K[^"]+')"
PUUID_BOOT="\$(blkid "\$(df /boot --output=source | tail -1)" | grep -Po 'PARTUUID="\K[^"]+')"
# sed regex info: search for line containing / followed by whitespace or /boot (second sed)
# in this line, search for PARTUUID= followed by letters, numbers or "-"
# replace that match with the new PARTUUID
sed -i "/\/[ ]\+/s/PARTUUID=[A-Za-z0-9-]\+/PARTUUID=\$PUUID_ROOT/g" /etc/fstab
sed -i "/\/boot/s/PARTUUID=[A-Za-z0-9-]\+/PARTUUID=\$PUUID_BOOT/g" /etc/fstab
sed -i "s/root=[^ ]\+/root=PARTUUID=\${PUUID_ROOT}/g" /boot/cmdline.txt
# delete keys
find /etc/ssh/ -name "ssh_host_*key*" -delete
# slows down boot
systemctl disable apt-daily.timer apt-daily.service apt-daily-upgrade.timer apt-daily-upgrade.service
# unecessary services
systemctl disable triggerhappy bluetooth wpa_supplicant
EOF
sed -i'' 's/^#//g' etc/ld.so.preload
cd "${REPO_DIR}"
umount -R "${MNT_DIR}"
losetup -D "$(losetup -l | awk '/raspbian\.img/{print $1}')"
mv "${TMP_DIR}/raspbian.img" "${OUTPUT_NAME}"
}
check_dependencies
get_raspbian "latest"
setup_raspbian
provision_raspbian
echo -e "[+] Done."
================================================
FILE: pi_sniffer/CMakeLists.txt
================================================
cmake_minimum_required(VERSION 2.8)
# Options
option(debug "Build with debug flags." Off)
set(PROJECT_NAME pi_sniffer)
project(${PROJECT_NAME})
# compiler flags
if (debug)
set(CMAKE_CXX_FLAGS "-g -std=c++0x -Wall -Wextra -Weffc++ -Wshadow -pedantic -Wcast-align -Wcast-qual -Woverloaded-virtual -Wstrict-null-sentinel -Wswitch-default -Winit-self -Wlogical-op -Wno-deprecated-declarations")
else()
set(CMAKE_CXX_FLAGS "-O2 -std=c++0x -Wall -Wextra -Wno-deprecated-declarations")
endif()
#package locations
find_package(Boost 1.67 COMPONENTS system thread program_options filesystem atomic REQUIRED)
find_package(OpenSSL REQUIRED)
# includes
include_directories(SYSTEM ${Boost_INCLUDE_DIR})
include_directories(SYSTEM ${OPENSSL_INCLUDE_DIR})
include_directories(SYSTEM "src/")
# compilation units
add_executable(${PROJECT_NAME}
src/main.cpp
src/ap.cpp
src/client.cpp
src/probed_network.cpp
src/configuration.cpp
src/packet.cpp
src/stats.cpp
src/input/kismet_drone.cpp
src/input/pcap.cpp
src/protocols/ieee80211.cpp
src/protocols/llcsnap.cpp
src/protocols/eapol11.cpp
src/util/kml_maker.cpp
src/util/pcap_output.cpp
src/util/convert.cpp)
# linking comp / libs
target_link_libraries(${PROJECT_NAME} ${Boost_LIBRARIES} ${OPENSSL_LIBRARIES} -lpugixml -ltins)
================================================
FILE: pi_sniffer/pi_sniffer.conf
================================================
================================================
FILE: pi_sniffer/src/ap.cpp
================================================
#include "ap.hpp"
#include "util/convert.hpp"
AP::AP() :
m_bssid(),
m_lastseen(0),
m_firstseen(0),
m_lastSignal(0),
m_bestSignal(-100),
m_channel(0),
m_wps(false),
m_parsed_beacon(false),
m_ssid(),
m_mac(),
m_encryption(),
m_clients(0),
m_data(0),
m_accesslock(),
m_lat(0),
m_long(0),
m_alt(0),
m_bestLat(0),
m_bestLong(0),
m_bestAlt(0)
{
}
AP::~AP()
{
}
bool AP::has_wps() const
{
return m_wps;
}
void AP::set_wps(bool p_wps)
{
m_wps = p_wps;
}
void AP::set_last_seen(boost::uint32_t p_epoch_time)
{
if (m_firstseen == 0)
{
m_firstseen = p_epoch_time;
}
m_lastseen = p_epoch_time;
}
boost::uint32_t AP::get_last_seen() const
{
return m_lastseen;
}
boost::uint32_t AP::get_first_seen() const
{
return m_firstseen;
}
void AP::set_location_info(boost::int8_t p_signal,
double p_lat, double p_long,
double p_alt, bool p_gps)
{
if (p_signal == 0)
{
return;
}
m_lastSignal = p_signal;
if (p_gps)
{
boost::mutex::scoped_lock routerLock(m_accesslock);
m_lat = p_lat;
m_long = p_long;
m_alt = p_alt;
if (m_lastSignal > m_bestSignal)
{
m_bestSignal = m_lastSignal;
m_bestLat = p_lat;
m_bestLong = p_long;
m_bestAlt = p_alt;
}
}
else
{
if (m_lastSignal > m_bestSignal)
{
m_bestSignal = m_lastSignal;
}
}
}
boost::int8_t AP::get_last_signal() const
{
return m_lastSignal;
}
boost::int8_t AP::get_best_signal() const
{
return m_bestSignal;
}
double AP::get_latitude()
{
boost::mutex::scoped_lock routerLock(m_accesslock);
return m_lat;
}
double AP::get_best_latitude()
{
boost::mutex::scoped_lock routerLock(m_accesslock);
return m_bestLat;
}
double AP::get_longitude()
{
boost::mutex::scoped_lock routerLock(m_accesslock);
return m_long;
}
double AP::get_best_longitude()
{
boost::mutex::scoped_lock routerLock(m_accesslock);
return m_bestLong;
}
double AP::get_altitude()
{
boost::mutex::scoped_lock routerLock(m_accesslock);
return m_alt;
}
double AP::get_best_altitude()
{
boost::mutex::scoped_lock routerLock(m_accesslock);
return m_bestAlt;
}
void AP::set_ssid(const std::string& p_ssid)
{
// only accept ascii, I guess
for (unsigned int i = 0; i < p_ssid.size(); i++)
{
if (p_ssid[i] > 0x7e || p_ssid[i] < 0x20)
{
return;
}
}
boost::mutex::scoped_lock routerLock(m_accesslock);
m_ssid.assign(p_ssid);
}
std::string AP::get_ssid()
{
boost::mutex::scoped_lock routerLock(m_accesslock);
return m_ssid;
}
boost::uint64_t AP::get_bssid()
{
boost::mutex::scoped_lock routerLock(m_accesslock);
return m_bssid;
}
void AP::set_mac(const std::string& p_mac)
{
boost::mutex::scoped_lock routerLock(m_accesslock);
m_mac.assign(p_mac);
m_bssid = string_mac_to_int(p_mac);
}
std::string AP::get_mac()
{
boost::mutex::scoped_lock routerLock(m_accesslock);
return m_mac;
}
void AP::set_channel(boost::uint8_t p_channel)
{
m_channel = p_channel;
}
boost::uint8_t AP::get_channel() const
{
return m_channel;
}
void AP::set_encryption(const std::string& p_encryption)
{
boost::mutex::scoped_lock routerLock(m_accesslock);
m_encryption.assign(p_encryption);
}
std::string AP::get_encryption()
{
boost::mutex::scoped_lock routerLock(m_accesslock);
return m_encryption;
}
void AP::increment_client()
{
++m_clients;
}
boost::uint32_t AP::get_client_count() const
{
return m_clients;
}
void AP::increment_data_packet()
{
++m_data;
}
boost::uint32_t AP::get_data_count() const
{
return m_data;
}
void AP::set_beacon_parsed()
{
m_parsed_beacon = true;
}
bool AP::get_beacon_parsed() const
{
return m_parsed_beacon;
}
================================================
FILE: pi_sniffer/src/ap.hpp
================================================
#ifndef AP_HPP
#define AP_HPP
#include
#include
#include
/*!
* This object represents an access point, typically a standard router but sometimes more
* interesting things like cars, doorbells, tablets, etc. This object attempts to track the
* general state and location of the the AP.
*
* Note that this object is r/w on at least two threads. I've made the assumption that
* int32 assignment is atomic, otherwise all other accesses require lock access.
*/
class AP
{
public:
AP();
~AP();
// Set to true if the WPS tag indicates the AP has WPS configured (0x02)
void set_wps(bool p_wps);
bool has_wps() const;
// Updated to track where we've seen the AP
void set_last_seen(boost::uint32_t p_time);
boost::uint32_t get_last_seen() const;
boost::uint32_t get_first_seen() const;
// Signal strength and GPS location information. We make no attempt to
// triangulate the actual location of the AP. Instead, we just call the "best"
// GPS coordinates whereever the signal strength is strongest. I think that
// is a fine enough solution.
void set_location_info(boost::int8_t p_signal, double p_lat, double p_long, double p_alt, bool p_gps);
boost::int8_t get_last_signal() const;
boost::int8_t get_best_signal() const;
double get_latitude();
double get_best_latitude();
double get_longitude();
double get_best_longitude();
double get_altitude();
double get_best_altitude();
// Store the broadcasted name. Currently only accepting ascii names.
void set_ssid(const std::string& p_ssid);
std::string get_ssid();
// Store the devices mac address
void set_mac(const std::string& p_mac);
std::string get_mac();
boost::uint64_t get_bssid();
// Store the channel we observed this device on
void set_channel(boost::uint8_t p_channel);
boost::uint8_t get_channel() const;
// Store a string representation of the encryption used (Open, WEP, WPA-EAP/PSK, WPA2-EAP/PSK)
void set_encryption(const std::string& p_encryption);
std::string get_encryption();
// Increment observed associated clients. This relies on a mechanism outside of the
// object to ensure that duplicate clients aren't being tracked.
void increment_client();
boost::uint32_t get_client_count() const;
// Track how much data we've seen go across this AP
void increment_data_packet();
boost::uint32_t get_data_count() const;
// We only want to parse this devices beacon/probe response once in order to save
// processing time.
void set_beacon_parsed();
bool get_beacon_parsed() const;
private:
AP(const AP& p_rhs);
AP& operator=(const AP& p_rhs);
private:
boost::uint64_t m_bssid;
boost::uint32_t m_lastseen;
boost::uint32_t m_firstseen;
boost::int8_t m_lastSignal;
boost::int8_t m_bestSignal;
boost::uint8_t m_channel;
bool m_wps;
bool m_parsed_beacon;
std::string m_ssid;
std::string m_mac;
std::string m_encryption;
boost::uint32_t m_clients;
boost::uint32_t m_data;
boost::mutex m_accesslock;
double m_lat;
double m_long;
double m_alt;
double m_bestLat;
double m_bestLong;
double m_bestAlt;
};
#endif
================================================
FILE: pi_sniffer/src/client.cpp
================================================
#include "client.hpp"
#include "util/convert.hpp"
Client::Client() :
m_lastseen(0),
m_firstseen(0),
m_associated_mac(0),
m_lastSignal(0),
m_bestSignal(-100),
m_accesslock(),
m_mac(),
m_lat(0),
m_long(0),
m_alt(0),
m_bestLat(0),
m_bestLong(0),
m_bestAlt(0)
{
}
Client::~Client()
{
}
void Client::set_last_seen(uint64_t p_epoch_time)
{
if (m_firstseen == 0)
{
m_firstseen = p_epoch_time;
}
m_lastseen = p_epoch_time;
}
boost::uint64_t Client::get_last_seen()
{
boost::mutex::scoped_lock clientLock(m_accesslock);
return m_lastseen;
}
boost::uint64_t Client::get_first_seen()
{
boost::mutex::scoped_lock clientLock(m_accesslock);
return m_firstseen;
}
void Client::set_location_info(boost::int8_t p_signal, double p_lat, double p_long, double p_alt, bool p_gps_on)
{
if (p_signal == 0)
{
return;
}
m_lastSignal = p_signal;
if (p_gps_on)
{
m_lat = p_lat;
m_long = p_long;
m_alt = p_alt;
if (m_lastSignal > m_bestSignal)
{
m_bestLat = p_lat;
m_bestLong = p_long;
m_bestAlt = p_alt;
m_bestSignal = m_lastSignal;
}
}
else if (m_lastSignal != 0)
{
if (m_lastSignal > m_bestSignal)
{
m_bestSignal = m_lastSignal;
}
}
}
boost::uint64_t Client::get_associated()
{
boost::mutex::scoped_lock clientLock(m_accesslock);
return m_associated_mac;
}
std::string Client::get_associated_str()
{
boost::mutex::scoped_lock clientLock(m_accesslock);
return printable_mac(reinterpret_cast(&m_associated_mac), 6, true);
}
void Client::set_associated(boost::uint64_t p_associated_mac)
{
boost::mutex::scoped_lock clientLock(m_accesslock);
m_associated_mac = p_associated_mac;
}
boost::int8_t Client::get_last_signal() const
{
return m_lastSignal;
}
boost::int8_t Client::get_best_signal() const
{
return m_bestSignal;
}
double Client::get_latitude()
{
boost::mutex::scoped_lock clientLock(m_accesslock);
return m_lat;
}
double Client::get_best_latitude()
{
boost::mutex::scoped_lock clientLock(m_accesslock);
return m_bestLat;
}
double Client::get_longitude()
{
boost::mutex::scoped_lock clientLock(m_accesslock);
return m_long;
}
double Client::get_best_longitude()
{
boost::mutex::scoped_lock clientLock(m_accesslock);
return m_bestLong;
}
double Client::get_altitude()
{
boost::mutex::scoped_lock clientLock(m_accesslock);
return m_alt;
}
double Client::get_best_altitude()
{
boost::mutex::scoped_lock clientLock(m_accesslock);
return m_bestAlt;
}
void Client::set_mac(const std::string& p_mac)
{
boost::mutex::scoped_lock clientLock(m_accesslock);
m_mac.assign(p_mac);
}
std::string Client::get_mac()
{
boost::mutex::scoped_lock clientLock(m_accesslock);
return m_mac;
}
================================================
FILE: pi_sniffer/src/client.hpp
================================================
#ifndef CLIENT_HPP
#define CLIENT_HPP
#include
#include
/**
* This object represents an observed client. Clients should only be created if
* we observe it associated with an AP.
*
* Note that this object is r/w on at least two threads. I've made the assumption that
* int32 assignment is atomic, otherwise all other accesses require lock access.
*/
class Client
{
public:
Client();
~Client();
// Updated to track where we've seen the AP
void set_last_seen(boost::uint64_t p_epoch_time);
boost::uint64_t get_last_seen();
boost::uint64_t get_first_seen();
// Signal strength and GPS location information. We make no attempt to
// triangulate the actual location of the AP. Instead, we just call the "best"
// GPS coordinates whereever the signal strength is strongest. I think that
// is a fine enough solution.
void set_location_info(boost::int8_t p_signal, double p_lat, double p_long, double p_alt, bool p_gps_on);
boost::int8_t get_last_signal() const;
boost::int8_t get_best_signal() const;
double get_latitude();
double get_best_latitude();
double get_longitude();
double get_best_longitude();
double get_altitude();
double get_best_altitude();
// store the devices mac address
void set_mac(const std::string& p_mac);
std::string get_mac();
// store the mac of the station we are associated with
void set_associated(boost::uint64_t p_associated_mac);
boost::uint64_t get_associated();
std::string get_associated_str();
private:
Client(const Client& p_rhs);
Client& operator=(const Client& p_rhs);
private:
boost::uint64_t m_lastseen;
boost::uint64_t m_firstseen;
boost::uint64_t m_associated_mac;
boost::int8_t m_lastSignal;
boost::int8_t m_bestSignal;
boost::mutex m_accesslock;
std::string m_mac;
double m_lat;
double m_long;
double m_alt;
double m_bestLat;
double m_bestLong;
double m_bestAlt;
};
#endif
================================================
FILE: pi_sniffer/src/configuration.cpp
================================================
#include "configuration.hpp"
#include "util/convert.hpp"
#include
#include
#include
Configuration::Configuration() :
m_wep_decrypter(),
m_wpa_decrypter(),
m_output_path(),
m_wep_keys(),
m_wpa_keys(),
m_pcap(false),
m_wigle(false),
m_kml(false),
m_client_csv(false),
m_probe_csv(false),
m_ap_clients_csv(false)
{
}
Configuration::~Configuration()
{
}
void Configuration::parse_configuration(const std::string& p_config_location)
{
pugi::xml_document config;
if (!config.load_file(p_config_location.c_str()))
{
throw std::runtime_error("Failed to load the configuration file: " + p_config_location);
}
const pugi::xml_node fullConfig = config.child("pi_sniffer");
if (fullConfig.empty())
{
throw std::runtime_error("Failed to find the root node: ");
}
const pugi::xml_node wifi_decrypt = fullConfig.child("wifidecrypt");
for (pugi::xml_node_iterator it = wifi_decrypt.begin();
it != wifi_decrypt.end(); ++it)
{
parse_wifi_key(*it);
}
const pugi::xml_node output = fullConfig.child("output");
for (pugi::xml_node_iterator it = output.begin();
it != output.end(); ++it)
{
parse_output(*it);
}
}
void Configuration::parse_wifi_key(const pugi::xml_node& p_key)
{
std::string key_type(p_key.attribute("type").as_string());
if (key_type.empty())
{
throw std::runtime_error("key missing the type attribute.");
}
std::string key(p_key.attribute("key").as_string());
if (key.empty())
{
throw std::runtime_error("key missing the key attribute.");
}
if (key_type == "wep")
{
std::string bssid(p_key.attribute("bssid").as_string());
if (bssid.empty())
{
throw std::runtime_error("key missing the bssid attribute.");
}
boost::uint64_t index = string_mac_to_int(bssid);
// convert to actual hex
std::string hex_key(string_to_hex(key));
if (hex_key.size() != 5 && hex_key.size() != 13 && hex_key.size() != 16)
{
throw std::runtime_error("The WEP key must be 5, 13, or 16 bytes long.");
}
m_wep_decrypter.add_password(int_mac_to_array(index), hex_key);
m_wep_keys.insert(bssid);
}
else if (key_type == "wpa")
{
std::string ssid(p_key.attribute("ssid").as_string());
if (ssid.empty())
{
throw std::runtime_error("key missing the ssid attribute.");
}
m_wpa_decrypter.add_ap_data(key, ssid);
m_wpa_keys.insert(ssid);
}
else
{
throw std::runtime_error("Unknown key type: " + key_type + ". Options are wep or wpa");
}
}
void Configuration::parse_output(const pugi::xml_node& p_output)
{
std::string type(p_output.attribute("type").as_string());
std::string path(p_output.attribute("path").as_string());
if (!type.empty())
{
if (type.compare("pcap") == 0)
{
std::string enabled(p_output.attribute("enabled").as_string());
if (enabled.compare("true") == 0)
{
m_pcap = true;
}
}
else if (type.compare("wigle") == 0)
{
std::string enabled(p_output.attribute("enabled").as_string());
if (enabled.compare("true") == 0)
{
m_wigle = true;
}
}
else if (type.compare("kml") == 0)
{
std::string enabled(p_output.attribute("enabled").as_string());
if (enabled.compare("true") == 0)
{
m_kml = true;
}
}
else if (type.compare("client_csv") == 0)
{
std::string enabled(p_output.attribute("enabled").as_string());
if (enabled.compare("true") == 0)
{
m_client_csv = true;
}
}
else if (type.compare("probe_csv") == 0)
{
std::string enabled(p_output.attribute("enabled").as_string());
if (enabled.compare("true") == 0)
{
m_probe_csv = true;
}
}
else if (type.compare("ap_clients_csv") == 0)
{
std::string enabled(p_output.attribute("enabled").as_string());
if (enabled.compare("true") == 0)
{
m_ap_clients_csv = true;
}
}
}
else if (!path.empty())
{
m_output_path.assign(p_output.attribute("path").as_string());
if (!boost::filesystem::exists(m_output_path))
{
// create directory
try
{
boost::filesystem::create_directories(m_output_path);
}
catch (const std::exception&)
{
// ignore it
}
}
if (!boost::filesystem::is_directory(m_output_path))
{
throw std::runtime_error("The output path is not a directory.");
}
}
}
bool Configuration::has_wep_key(const std::string& p_bssid) const
{
return m_wep_keys.find(p_bssid) != m_wep_keys.end();
}
bool Configuration::has_wpa_key(const std::string& p_ssid) const
{
return m_wpa_keys.find(p_ssid) != m_wpa_keys.end();
}
================================================
FILE: pi_sniffer/src/configuration.hpp
================================================
#ifndef CONFIG_HPP
#define CONFIG_HPP
#include
#include
#include
#include
namespace pugi
{
struct xml_node;
}
/**
* Parses the configuration file passed on the command line. Also holds the information.
* The configuration information is largely:
*
* 1. What type of files to output.
* 2. Where to output the files.
* 3. Decryption keys.
*/
class Configuration
{
public:
Configuration();
~Configuration();
void parse_configuration(const std::string& p_config_location);
const std::string& get_output_path() const
{
return m_output_path;
}
bool get_pcap() const
{
return m_pcap;
}
bool get_wigle() const
{
return m_wigle;
}
bool get_kml() const
{
return m_kml;
}
bool get_client_csv() const
{
return m_client_csv;
}
bool get_probe_csv() const
{
return m_probe_csv;
}
bool get_ap_clients_csv() const
{
return m_ap_clients_csv;
}
bool has_wep_key(const std::string& p_bssid) const;
bool has_wpa_key(const std::string& p_ssid) const;
private:
void parse_wifi_key(const pugi::xml_node& p_key);
void parse_output(const pugi::xml_node& p_output);
public:
// Note: its a touch odd that configuration holds the decryptors but here we are
Tins::Crypto::WEPDecrypter m_wep_decrypter;
Tins::Crypto::WPA2Decrypter m_wpa_decrypter;
private:
//! The file path to the output directory
std::string m_output_path;
//! AP we have wep keys for
std::set m_wep_keys;
//! AP we have wpa keys for
std::set m_wpa_keys;
//! indicates if we should write ppi pcap file to disk
bool m_pcap;
//! indicates if we should write wigle csv to disk
bool m_wigle;
//! indicates if we should write out the kml information for routers
bool m_kml;
//! indicates if we should write out the clients to a csv file
bool m_client_csv;
//! indicates if we should write out the probes to a csv file
bool m_probe_csv;
//! indicates if we should write out the ap client csv file
bool m_ap_clients_csv;
};
#endif
================================================
FILE: pi_sniffer/src/input/kismet_drone.cpp
================================================
#include "kismet_drone.hpp"
#include "packet.hpp"
#include "stats.hpp"
#include
#include
#include
namespace
{
// hold the packet data that goes down to the protocols
std::string s_drone_data;
#pragma pack(push, 1)
struct drone_trans_double
{
uint32_t mantissal;
uint32_t mantissah;
uint16_t exponent;
uint16_t sign;
};
struct ieee_double_t
{
unsigned int mantissal:32;
unsigned int mantissah:20;
unsigned int exponent:11;
unsigned int sign:1;
};
#pragma pack(pop)
void double_conversion_drone(double& x, drone_trans_double* y)
{
ieee_double_t* locfl = (ieee_double_t *)&(x);
(locfl)->mantissal = ntohl((y)->mantissal);
(locfl)->mantissah = ntohl((y)->mantissah);
(locfl)->exponent = ntohs((y)->exponent);
(locfl)->sign = ntohs((y)->sign);
}
}
KismetDrone::KismetDrone(const std::string& p_address, std::size_t p_port) :
m_ip(p_address),
m_port(),
m_io_service(),
m_socket(m_io_service),
m_deadline(m_io_service)
{
std::stringstream portString;
portString << p_port;
m_port = portString.str();
m_deadline.expires_at(boost::posix_time::pos_infin);
check_deadline();
}
KismetDrone::~KismetDrone()
{
close();
}
void KismetDrone::close()
{
try
{
m_socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both);
m_socket.close();
}
catch (...)
{
}
}
void KismetDrone::check_deadline()
{
if (m_deadline.expires_at() <= boost::asio::deadline_timer::traits_type::now())
{
boost::system::error_code ignored_ec;
m_socket.close(ignored_ec);
m_deadline.expires_at(boost::posix_time::pos_infin);
}
m_deadline.async_wait(boost::lambda::bind(&KismetDrone::check_deadline, this));
}
bool KismetDrone::connect()
{
std::cout << "Drone connecting..." << std::endl;
try
{
boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::address::from_string(m_ip), atoi(m_port.c_str()));
// set a 2 second deadline for the connection
boost::system::error_code ec = boost::asio::error::would_block;
m_deadline.expires_from_now(boost::posix_time::seconds(5));
m_socket.async_connect(endpoint, boost::lambda::var(ec) = boost::lambda::_1);
do
{
m_io_service.run_one();
}
while (ec == boost::asio::error::would_block);
if (ec || !m_socket.is_open())
{
return false;
}
}
catch(const std::exception& e)
{
return false;
}
return true;
}
bool KismetDrone::read(boost::uint32_t p_length, std::string& p_data)
{
boost::system::error_code ec = boost::asio::error::would_block;
m_deadline.expires_from_now(boost::posix_time::seconds(5));
boost::asio::streambuf response;
boost::asio::async_read(m_socket, response, boost::asio::transfer_exactly(p_length), boost::lambda::var(ec) = boost::lambda::_1);
do
{
m_io_service.run_one();
}
while (ec == boost::asio::error::would_block);
if (ec)
{
return false;
}
p_data.assign(std::istreambuf_iterator(&response), std::istreambuf_iterator());
response.consume(p_length);
return true;
}
bool KismetDrone::get_packet(Packet& p_packet)
{
boost::uint32_t type = 0;
boost::uint32_t read_length = 0;
boost::uint32_t offset = 0;
boost::uint32_t bitmap = 0;
while (type != 3)
{
if (!read(12, s_drone_data) || s_drone_data.size() != 12)
{
return false;
}
// check for sentinel
if (static_cast(s_drone_data[0]) != 0xde ||
static_cast(s_drone_data[3]) != 0xef)
{
return false;
}
// get the packet type (will indicate if we have a packet or not)
type = *reinterpret_cast(&s_drone_data[0] + 4);
type = ntohl(type);
// get length of the packet and read it all in
read_length = *reinterpret_cast(&s_drone_data[0] + 8);
read_length = ntohl(read_length);
if (!read(read_length, s_drone_data) || s_drone_data.size() != read_length)
{
return false;
}
bitmap = *reinterpret_cast(&s_drone_data[0]);
bitmap = ntohl(bitmap);
if ((bitmap & 1) == 0)
{
// missing radio header.
type = 2;
continue;
}
offset = *reinterpret_cast(&s_drone_data[0] + 4);
offset = ntohl(offset) + 8;
if (read_length == 12 || offset == 8)
{
// This is an empty packet.
type = 2;
}
}
if ((offset + 44) >= read_length)
{
// invalid packet
return false;
}
if ((bitmap & 2) == 2)
{
// gps data present
boost::uint16_t gps_size = *reinterpret_cast(&s_drone_data[0] + 38);
gps_size = ntohs(gps_size);
if (gps_size == 68)
{
p_packet.m_gps_on = true;
double_conversion_drone(p_packet.m_lat, reinterpret_cast(&s_drone_data[0] + 46));
double_conversion_drone(p_packet.m_long, reinterpret_cast(&s_drone_data[0] + 46 + sizeof(drone_trans_double)));
double_conversion_drone(p_packet.m_alt, reinterpret_cast(&s_drone_data[0] + 46 + (sizeof(drone_trans_double) * 2)));
}
}
// grab the signal metadata
boost::int16_t dbm = *reinterpret_cast(&s_drone_data[0] + 18);
dbm = ntohs(dbm);
// grab the metadata in the radio header
boost::uint32_t time = *reinterpret_cast(&s_drone_data[0] + (offset + 28));
time = ntohl(time);
// skip over the metadata
p_packet.m_data = reinterpret_cast(&s_drone_data[0]) + (offset + 44);
p_packet.m_length = read_length - (offset + 44);
// update stats
p_packet.m_stats.increment_packets();
p_packet.m_time = time;
p_packet.m_signal = dbm;
return true;
}
================================================
FILE: pi_sniffer/src/input/kismet_drone.hpp
================================================
#ifndef KISMET_DRONE_HPP
#define KISMET_DRONE_HPP
#include
#include
#include
class Packet;
class KismetDrone
{
public:
KismetDrone(const std::string& p_address, std::size_t p_port);
~KismetDrone();
bool get_packet(Packet& p_packet);
bool connect();
private:
bool read(boost::uint32_t p_length, std::string& p_data);
void check_deadline();
void close();
private:
KismetDrone(const KismetDrone& p_rhs);
KismetDrone& operator=(const KismetDrone& p_rhs);
private:
// the ip address to connect to
std::string m_ip;
// the port to connect to
std::string m_port;
// the IO service associated with our blocking socket
boost::asio::io_service m_io_service;
// the blocking socket we use for communication
boost::asio::ip::tcp::socket m_socket;
// Timer to use with async socket operations
boost::asio::deadline_timer m_deadline;
};
#endif
================================================
FILE: pi_sniffer/src/input/pcap.cpp
================================================
#include "pcap.hpp"
#include "packet.hpp"
#include
#include
#include
namespace
{
unsigned char data[65535] = {0};
#pragma pack(push, 1)
struct pcap_header
{
boost::uint32_t magic_number;
boost::uint16_t version_major;
boost::uint16_t version_minor;
boost::uint32_t thiszone;
boost::uint32_t sigfigs;
boost::uint32_t snaplen;
boost::uint32_t network;
};
struct packet_header
{
boost::uint32_t ts_sec;
boost::uint32_t ts_usec;
boost::uint32_t incl_len;
boost::uint32_t orig_len;
};
struct ppi_packetheader
{
boost::uint8_t pph_version;
boost::uint8_t pph_flags;
boost::uint16_t pph_len;
boost::uint32_t pph_dlt;
};
struct ppi_fieldheader
{
boost::uint16_t pfh_type;
boost::uint16_t pfh_datalen;
};
struct gps_fields
{
boost::uint8_t gps_revision;
boost::uint8_t gps_pad;
boost::uint16_t gps_length;
boost::uint32_t gps_present;
boost::uint32_t gps_lat;
boost::uint32_t gps_long;
boost::uint32_t gps_alt;
boost::uint32_t gps_app;
};
struct radiotap_header
{
boost::uint8_t version;
boost::uint8_t pad;
boost::uint16_t len;
boost::uint32_t present;
};
struct ppi_common
{
boost::uint64_t tsft;
boost::uint16_t flags;
boost::uint16_t rate;
boost::uint16_t frequency;
boost::uint16_t channel_type;
boost::uint8_t hopset;
boost::uint8_t pattern;
boost::uint8_t rssi;
boost::uint8_t noise;
};
#pragma pack(pop)
// harris crap
double fixed_3_7_to_flt(boost::uint32_t in)
{
boost::int32_t remapped = in - (180 * 10000000);
return static_cast(remapped) / 10000000;
}
double fixed_6_4_to_flt(boost::uint32_t in)
{
boost::int32_t remapped_in = in - (180000 * 10000);
double ret = (double) ((double) remapped_in / 10000);
return ret;
}
}
PCAP::PCAP(std::string p_filename) :
m_file(p_filename.c_str(), std::ios::binary),
m_linktype(0)
{
}
PCAP::~PCAP()
{
}
bool PCAP::initialize()
{
if (!m_file.is_open())
{
return false;
}
m_file.read(reinterpret_cast(&data[0]), sizeof(pcap_header));
if (m_file.gcount() != sizeof(pcap_header))
{
return false;
}
struct pcap_header* header = reinterpret_cast(data);
if (header->magic_number != 0xa1b2c3d4)
{
return false;
}
if (header->network != 192 && header->network != 127 && header->network != 105)
{
return false;
}
m_linktype = header->network;
return true;
}
bool PCAP::eof() const
{
return m_file.eof() && m_file.good();
}
bool PCAP::get_packet(Packet& p_packet)
{
m_file.read(reinterpret_cast(&data[0]), sizeof(packet_header));
if (m_file.gcount() != sizeof(packet_header))
{
return false;
}
struct packet_header* header = reinterpret_cast(data);
p_packet.m_time = header->ts_sec;
p_packet.m_stats.increment_packets();
boost::uint32_t length = header->incl_len;
m_file.read(reinterpret_cast(&data[0]), length);
if (m_file.gcount() != static_cast(length) ||
length < static_cast(4))
{
return false;
}
switch (m_linktype)
{
case 127:
return do_radiotap(p_packet, length);
case 192:
return do_ppi(p_packet, length);
default:
//uhm... ok
break;
}
p_packet.m_data = data;
p_packet.m_length = length;
return true;
}
bool PCAP::do_radiotap(Packet& p_packet, boost::uint32_t p_length)
{
struct radiotap_header* radio_header = reinterpret_cast(data);
if (radio_header->version != 0)
{
return false;
}
if (p_length < radio_header->len)
{
return false;
}
bool has_fcs = false;
unsigned char* radio_tags = data + sizeof(radiotap_header);
if ((radio_header->present & 0x01) != 0)
{
// mac timestamp
radio_tags += 8;
}
if ((radio_header->present & 0x02) != 0)
{
// flags
unsigned char flags = radio_tags[0];
if (flags & 0x10)
{
has_fcs = true;
}
radio_tags += 1;
}
if ((radio_header->present & 0x04) != 0)
{
// rate
radio_tags += 1;
}
if ((radio_header->present & 0x08) != 0)
{
// channel frequency / type
radio_tags += 4;
}
if ((radio_header->present & 0x10) != 0)
{
// fhss
radio_tags += 2;
}
if ((radio_header->present & 0x20) != 0)
{
//signal
p_packet.m_signal = radio_tags[0];
}
p_packet.m_data = data + radio_header->len;
p_packet.m_length = p_length - radio_header->len;
if (has_fcs)
{
p_packet.m_length -= 4;
}
return true;
}
bool PCAP::do_ppi(Packet& p_packet, boost::uint32_t p_length)
{
struct ppi_packetheader* ppi_header = reinterpret_cast(data);
if (ppi_header->pph_version != 0)
{
return false;
}
if (ppi_header->pph_dlt != 105)
{
return false;
}
if (p_length < ppi_header->pph_len )
{
return false;
}
// check the next field type
struct ppi_fieldheader* field_header = reinterpret_cast(data + sizeof(ppi_packetheader));
if (field_header->pfh_type == 0x7532)
{
// gps info
struct gps_fields* gps = reinterpret_cast(data + sizeof(ppi_packetheader) + sizeof(ppi_fieldheader));
if (gps->gps_length == field_header->pfh_datalen)
{
if (gps->gps_present == 0x2000000e)
{
p_packet.m_gps_on = true;
p_packet.m_lat = fixed_3_7_to_flt(gps->gps_lat);
p_packet.m_long = fixed_3_7_to_flt(gps->gps_long);
p_packet.m_alt = fixed_6_4_to_flt(gps->gps_alt);
// try the next field
field_header = reinterpret_cast(
reinterpret_cast(gps) + gps->gps_length);
}
}
}
if (field_header->pfh_type == 0x0002)
{
// 802.11 common
struct ppi_common* common = reinterpret_cast(reinterpret_cast(field_header) + 4);
p_packet.m_signal = common->rssi;
}
p_packet.m_data = data + ppi_header->pph_len;
p_packet.m_length = p_length - ppi_header->pph_len;
return true;
}
================================================
FILE: pi_sniffer/src/input/pcap.hpp
================================================
#ifndef PCAP_HPP
#define PCAP_HPP
#include
#include
#include
class Packet;
class PCAP
{
public:
PCAP(std::string p_filename);
~PCAP();
bool initialize();
bool eof() const;
bool get_packet(Packet& p_packet);
private:
bool do_radiotap(Packet& p_packet, boost::uint32_t p_length);
bool do_ppi(Packet& p_packet, boost::uint32_t p_length);
private:
std::ifstream m_file;
std::size_t m_linktype;
};
#endif
================================================
FILE: pi_sniffer/src/main.cpp
================================================
#include
#include
#include
#include
#include "util/convert.hpp"
#include "ap.hpp"
#include "client.hpp"
#include "packet.hpp"
#include "protocols/ieee80211.hpp"
#include "input/kismet_drone.hpp"
#include "input/pcap.hpp"
namespace
{
bool parseCommandLine(int p_argCount, char* p_argArray[],
std::string& p_server_address, std::size_t& p_server_port, std::string& p_file, Configuration& p_config)
{
boost::program_options::options_description description("options");
description.add_options()("help,h", "A list of command line options")
("version,v", "Display version information")
("config,c", boost::program_options::value()->default_value(std::string("/home/pi/pi_sniffer/pi_sniffer.conf"), ""), "The path to the configuration file")
("file,f", boost::program_options::value(),"The file to parse.")
("kismet-address,k", boost::program_options::value(), "The address of the kismet server.")
("kismet-port,p", boost::program_options::value(), "The port of the kismet server.");
boost::program_options::variables_map argv_map;
try
{
boost::program_options::store(
boost::program_options::parse_command_line(
p_argCount, p_argArray, description), argv_map);
}
catch (const std::exception& e)
{
std::cerr << e.what() << "\n" << std::endl;
std::cout << description << std::endl;
return false;
}
boost::program_options::notify(argv_map);
if (argv_map.empty() || argv_map.count("help"))
{
std::cout << description << std::endl;
return false;
}
if (argv_map.count("version"))
{
std::cout << "🦞 Pi Sniffer Alpha 🦞" << std::endl;
return false;
}
if (argv_map.count("config"))
{
try
{
p_config.parse_configuration(argv_map["config"].as());
}
catch (const std::runtime_error& e)
{
std::cerr << "Failed config parsing: " << e.what() << std::endl;
return false;
}
}
if (argv_map.count("file"))
{
p_file = argv_map["file"].as();
return true;
}
if (argv_map.count("kismet-port") && argv_map.count("kismet-address"))
{
p_server_port = argv_map["kismet-port"].as();
p_server_address = argv_map["kismet-address"].as();
return true;
}
return false;
}
void fileThread(Packet& p_packet, const std::string& p_file)
{
IEEE80211 link_layer;
PCAP file_input(p_file);
file_input.initialize();
std::cout << "Reading: " << p_file << std::endl;
while (file_input.get_packet(p_packet))
{
link_layer.handle_packet(p_packet);
p_packet.reset();
}
}
void protocolThread(Packet& p_packet, const std::string& p_kismet_address, const std::size_t p_kismet_port)
{
try
{
IEEE80211 link_layer;
while (!p_packet.m_shutdown)
{
KismetDrone input(p_kismet_address, p_kismet_port);
input.connect();
while (!p_packet.m_shutdown && input.get_packet(p_packet))
{
link_layer.handle_packet(p_packet);
p_packet.reset();
}
if (!p_packet.m_shutdown)
{
// for some reason we lost sync with kismet? I've seen kismet simply stop sending data as
// well. Which is strange, but I think not our fault. Sleep 5 seconds and let try to
// reconnect.
sleep(5);
}
}
}
catch (const std::runtime_error& e)
{
}
}
}
int main(int p_argCount, char* p_argArray[])
{
/* for better or worse, holds on global structures used by protocol
* processing and the user interface */
try
{
Packet packet;
std::stringstream timeStream;
boost::uint32_t start = time(NULL);
timeStream << start;
packet.m_startTime.assign(timeStream.str());
std::string file;
std::string interface;
std::string kismet_address;
std::size_t kismet_port = 0;
if (!parseCommandLine(p_argCount, p_argArray, kismet_address, kismet_port, file, packet.get_config()))
{
return EXIT_FAILURE;
}
// spawn protocol thread
boost::thread protoThread;
if (file.empty())
{
protoThread = boost::thread(protocolThread, boost::ref(packet), kismet_address, kismet_port);
}
else
{
protoThread = boost::thread(fileThread, boost::ref(packet), file);
}
// ui server will be on the main thread
boost::asio::io_service io_service;
boost::asio::ip::udp::socket ui_sock(io_service, boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), 1270));
while (packet.m_shutdown == false)
{
boost::array recv_buf;
boost::system::error_code error;
boost::asio::ip::udp::endpoint remote_endpoint;
int length = ui_sock.receive_from(boost::asio::buffer(recv_buf), remote_endpoint, 0, error);
if (error && error != boost::asio::error::message_size)
{
packet.m_shutdown = true;
continue;
}
if (length == 2 && recv_buf.data()[0] == 's')
{
packet.m_shutdown = true;
continue;
}
else if (length == 2 && recv_buf.data()[0] == 'o')
{
std::stringstream response;
response << (time(NULL) - start) << ","
<< (packet.m_stats.get_unencrypted() + packet.m_stats.get_wep() + packet.m_stats.get_wpa()) << ","
<< packet.m_stats.get_unencrypted() << ","
<< packet.m_stats.get_wep() << ","
<< packet.m_stats.get_wpa() << ","
<< packet.m_stats.get_packets() << ","
<< packet.m_stats.get_beacons() << ","
<< packet.m_stats.get_data_packets() << ","
<< packet.m_stats.get_encrypted() << ","
<< packet.m_stats.get_eapol() << std::endl;
boost::system::error_code ignored_error;
ui_sock.send_to(boost::asio::buffer(response.str()), remote_endpoint, 0, ignored_error);
}
else if (length == 2 && recv_buf.data()[0] == 'l')
{
std::stringstream response;
std::vector recent;
packet.get_recent_ap(30, recent);
for (std::vector::iterator it = recent.begin();
it != recent.end(); ++it)
{
response << (*it)->get_ssid() << "," << (*it)->get_mac() << std::endl;
}
response << std::endl;
std::string output(response.str());
if (output.size() == 1)
{
output.push_back('\n');
}
boost::system::error_code ignored_error;
ui_sock.send_to(boost::asio::buffer(output), remote_endpoint, 0, ignored_error);
}
else if (length == 2 && recv_buf.data()[0] == 'c')
{
std::stringstream response;
std::vector recent;
packet.get_recent_client(30, recent);
for (std::vector::iterator it = recent.begin();
it != recent.end(); ++it)
{
response << (*it)->get_mac() << std::endl;
}
response << std::endl;
std::string output(response.str());
if (output.size() == 1)
{
output.push_back('\n');
}
boost::system::error_code ignored_error;
ui_sock.send_to(boost::asio::buffer(output), remote_endpoint, 0, ignored_error);
}
else if (length == 19 && recv_buf.data()[0] == 'r')
{
std::string mac(recv_buf.data() + 1, 17);
boost::uint64_t lookup_mac = string_mac_to_int(mac);
AP* router = packet.find_ap(lookup_mac);
if (router != NULL)
{
std::stringstream result;
result << ((int)router->get_channel() & 0xff) << ","
<< router->get_encryption() << ","
<< ((int)router->get_last_signal()) << ","
<< router->get_client_count() << std::endl << std::endl;
boost::system::error_code ignored_error;
ui_sock.send_to(boost::asio::buffer(result.str()), remote_endpoint, 0, ignored_error);
}
}
else if (length == 19 && recv_buf.data()[0] == 'c')
{
std::string mac(recv_buf.data() + 1, 17);
boost::uint64_t lookup_mac = string_mac_to_int(mac);
Client* client = packet.find_client(lookup_mac, false);
if (client != NULL)
{
std::stringstream result;
result << ((int)client->get_last_signal()) << ","
<< client->get_associated_str() << std::endl << std::endl;
boost::system::error_code ignored_error;
ui_sock.send_to(boost::asio::buffer(result.str()), remote_endpoint, 0, ignored_error);
}
}
else if (length == '2' && recv_buf.data()[0] == 'f')
{
// flash output
if (packet.get_const_config().get_wigle())
{
packet.write_wigle_output(packet.m_startTime);
}
if (packet.get_const_config().get_kml())
{
packet.write_kml_output(packet.m_startTime);
}
if (packet.get_const_config().get_client_csv())
{
packet.write_client_csv_output(packet.m_startTime);
}
if (packet.get_const_config().get_probe_csv())
{
packet.write_probe_csv_output(packet.m_startTime);
}
if (packet.get_const_config().get_ap_clients_csv())
{
packet.write_ap_clients_csv_output(packet.m_startTime);
}
}
}
// join the protocol thread back in
packet.m_shutdown = true;
protoThread.interrupt();
protoThread.join();
if (packet.get_const_config().get_wigle())
{
packet.write_wigle_output(packet.m_startTime);
}
if (packet.get_const_config().get_kml())
{
packet.write_kml_output(packet.m_startTime);
}
if (packet.get_const_config().get_client_csv())
{
packet.write_client_csv_output(packet.m_startTime);
}
if (packet.get_const_config().get_probe_csv())
{
packet.write_probe_csv_output(packet.m_startTime);
}
if (packet.get_const_config().get_ap_clients_csv())
{
packet.write_ap_clients_csv_output(packet.m_startTime);
}
}
catch (const std::runtime_error& e)
{
std::cerr << e.what() << std::endl;
return EXIT_FAILURE;
}
catch (const std::exception& e)
{
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
================================================
FILE: pi_sniffer/src/packet.cpp
================================================
#include "packet.hpp"
#include "client.hpp"
#include "ap.hpp"
#include "probed_network.hpp"
#include "util/convert.hpp"
#include "util/kml_maker.hpp"
#include
#include
#include
Packet::Packet() :
m_devices(),
m_clients(),
m_probed_networks(),
m_router_mutex(),
m_client_mutex(),
m_probe_mutex(),
m_configuration(),
m_stats(),
m_data(NULL),
m_length(0),
m_time(0),
m_lat(0),
m_long(0),
m_alt(0),
m_signal(0),
m_gps_on(false),
m_current_client(NULL),
m_current_router(NULL),
m_from_client(false),
m_startTime(),
m_shutdown(false)
{
}
Packet::~Packet()
{
}
void Packet::reset()
{
// note: don't zero out m_time
m_data = NULL;
m_length = 0;
m_signal = 0;
m_lat = 0;
m_long = 0;
m_alt = 0;
m_gps_on = false;
}
const Configuration& Packet::get_const_config() const
{
return m_configuration;
}
Configuration& Packet::get_config()
{
return m_configuration;
}
void Packet::get_recent_ap(boost::uint32_t p_seconds, std::vector& p_routers)
{
// this approach has the short coming that we need packets to be regurlarly flowing.
// although hopefully that's the case.
boost::uint32_t cutoff = m_time - p_seconds;
// loop over everything. yes there are better approaches but they require much more code
// and I don't think the router map should ever get big enough for that to pay off
// get read lock for router lookup
boost::upgrade_lock readLock(m_router_mutex);
for (boost::ptr_unordered_map::iterator iter = m_devices.begin();
iter != m_devices.end(); ++iter)
{
if (iter->second->get_last_seen() >= cutoff)
{
std::vector::iterator v_iter = p_routers.begin();
for ( ; v_iter != p_routers.end(); ++v_iter)
{
if ((*v_iter)->get_last_seen() < iter->second->get_last_seen())
{
break;
}
}
p_routers.insert(v_iter, iter->second);
}
}
}
void Packet::get_recent_client(boost::uint32_t p_seconds, std::vector& p_clients)
{
// this approach has the short coming that we need packets to be regurlarly flowing.
// although hopefully that's the case.
boost::uint32_t cutoff = m_time - p_seconds;
// loop over everything. yes there are better approaches but they require much more code
// and I don't think the router map should ever get big enough for that to pay off
// get read lock for router lookup
boost::upgrade_lock readLock(m_client_mutex);
for (boost::ptr_unordered_map::iterator iter = m_clients.begin();
iter != m_clients.end(); ++iter)
{
if (iter->second->get_last_seen() >= cutoff)
{
std::vector::iterator v_iter = p_clients.begin();
for ( ; v_iter != p_clients.end(); ++v_iter)
{
if ((*v_iter)->get_last_seen() < iter->second->get_last_seen())
{
break;
}
}
p_clients.insert(v_iter, iter->second);
}
}
}
AP* Packet::find_ap(boost::uint64_t p_mac)
{
{
// get read lock for router lookup
boost::upgrade_lock readLock(m_router_mutex);
// find the router and return it if it exists
boost::ptr_unordered_map::iterator iter = m_devices.find(p_mac);
if (iter != m_devices.end())
{
m_current_router = (*iter).second;
m_current_router->set_last_seen(m_time);
if (m_gps_on)
{
m_current_router->set_location_info(m_signal, m_lat, m_long, m_alt, m_gps_on);
}
else if (m_signal != 0)
{
m_current_router->set_location_info(m_signal, m_lat, m_long, m_alt, m_gps_on);
}
return m_current_router;
}
// upgrade to a write lock and create the new router object
boost::upgrade_to_unique_lock writeLock(readLock);
m_current_router = &m_devices[p_mac];
}
// this is a new router so update it.
unsigned char* mac = reinterpret_cast(&p_mac);
std::string mac_string(printable_mac(mac, 6));
m_current_router->set_mac(mac_string);
m_current_router->set_last_seen(m_time);
if (m_gps_on)
{
m_current_router->set_location_info(m_signal, m_lat, m_long, m_alt, m_gps_on);
}
else if (m_signal != 0)
{
m_current_router->set_location_info(m_signal, m_lat, m_long, m_alt, m_gps_on);
}
return m_current_router;
}
Client* Packet::find_client(boost::uint64_t p_src_mac, bool p_associated)
{
{
// get read lock for client lookup
boost::upgrade_lock readLock(m_client_mutex);
// if the client already exists we can simply return it
boost::ptr_unordered_map::iterator iter = m_clients.find(p_src_mac);
if (iter != m_clients.end())
{
m_current_client = (*iter)->second;
m_current_client->set_last_seen(m_time);
m_current_client->set_location_info(m_signal, m_lat, m_long, m_alt, m_gps_on);
if (m_current_router && p_associated && m_current_client->get_associated() == 0)
{
m_current_client->set_associated(m_current_router->get_bssid());
m_current_router->increment_client();
}
return m_current_client;
}
// upgrade to a write lock and create the new client object
boost::upgrade_to_unique_lock writeLock(readLock);
m_current_client = &m_clients[p_src_mac];
}
if (m_current_router && p_associated)
{
m_current_client->set_associated(m_current_router->get_bssid());
m_current_router->increment_client();
}
unsigned char* mac = reinterpret_cast(&p_src_mac);
std::string mac_address(printable_mac(mac, 6));
m_current_client->set_mac(mac_address);
m_current_client->set_last_seen(m_time);
m_current_client->set_location_info(m_signal, m_lat, m_long, m_alt, m_gps_on);
return m_current_client;
}
void Packet::write_wigle_output(const std::string& p_time)
{
std::string filename(m_configuration.get_output_path() + "pi_sniffer_wigle_" + p_time + ".csv");
// create the file
std::filebuf wigle_output;
wigle_output.open(filename, std::ios::out);
if (!wigle_output.is_open())
{
std::cerr << "Failed to write " << filename << std::endl;
return;
}
std::ostream os(&wigle_output);
// header
os << "WigleWifi-1.4\n";
// data fields
os << "MAC,SSID,AuthMode,FirstSeen,Channel,RSSI,CurrentLatitude,CurrentLongitude,AltitudeMeters,AccuracyMeters,Type\n";
// time
char buffer[32] = {0};
std::string time;
// loop over the router
boost::upgrade_lock readLock(m_router_mutex);
for (boost::ptr_unordered_map::iterator it = m_devices.begin();
it != m_devices.end(); ++it)
{
os << it->second->get_mac() << ",";
if (it->second->get_ssid() == "")
{
os << ",";
}
else
{
os << it->second->get_ssid() << ",";
}
if (it->second->get_encryption().find("/") != std::string::npos)
{
os << "[WPA-PSK][WPA2-PSK]";
}
else if (it->second->get_encryption() == "None")
{
//leave it blank
}
else
{
os << "[" << it->second->get_encryption() << "]";
}
if (it->second->has_wps())
{
os << "[WPS]";
}
os << ",";
time_t start = it->second->get_first_seen();
strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", localtime(&start));
time.assign(buffer);
os << time << "," << (int)it->second->get_channel() << ",";
os << static_cast(it->second->get_best_signal()) << ",";
os << it->second->get_best_latitude() << ",";
os << it->second->get_best_longitude() << ",";
os << it->second->get_best_altitude() << ",";
os << ","; // don't really have accuracy info
os << "WIFI" << "\n";
}
// close it
wigle_output.close();
}
void Packet::write_kml_output(const std::string& p_time)
{
std::string filename(m_configuration.get_output_path() + "pi_sniffer_map_" + p_time);
KML_Maker kml_maker;
boost::upgrade_lock readLock(m_router_mutex);
kml_maker.load_aps(m_devices);
kml_maker.write_all(filename);
}
void Packet::write_client_csv_output(const std::string& p_time)
{
std::string filename(m_configuration.get_output_path() + "pi_sniffer_clients_" + p_time + ".csv");
// create the file
std::filebuf client_output;
client_output.open(filename, std::ios::out);
if (!client_output.is_open())
{
std::cerr << "Failed to write " << filename << std::endl;
return;
}
std::ostream os(&client_output);
// data fields
os << "MAC,BSSID,RSSI,Lat,Long,FirstSeen,LastSeen" << std::endl;
// loop over the router
boost::upgrade_lock readLock(m_router_mutex);
for (boost::ptr_unordered_map::iterator it = m_clients.begin();
it != m_clients.end(); ++it)
{
os << it->second->get_mac() << ",";
os << it->second->get_associated_str() << ",";
os << static_cast(it->second->get_best_signal()) << ",";
os << it->second->get_best_latitude() << ",";
os << it->second->get_best_longitude() << ",";
os << it->second->get_first_seen() << ",";
os << it->second->get_last_seen() << std::endl;
}
client_output.close();
}
void Packet::write_probe_csv_output(const std::string& p_time)
{
std::string filename(m_configuration.get_output_path() + "pi_sniffer_probes_" + p_time + ".csv");
// create the file
std::filebuf client_output;
client_output.open(filename, std::ios::out);
if (!client_output.is_open())
{
std::cerr << "Failed to write " << filename << std::endl;
return;
}
std::ostream os(&client_output);
os << "Probe,Count" << std::endl;
boost::upgrade_lock readLock(m_probe_mutex);
for (boost::ptr_map::iterator it = m_probed_networks.begin();
it != m_probed_networks.end(); ++it)
{
os << it->first << "," << it->second->get_clients_count() << std::endl;
}
client_output.close();
}
void Packet::write_ap_clients_csv_output(const std::string& p_time)
{
std::string filename(m_configuration.get_output_path() + "pi_sniffer_ap_clients_" + p_time + ".csv");
// create the file
std::filebuf ap_clients_output;
ap_clients_output.open(filename, std::ios::out);
if (!ap_clients_output.is_open())
{
std::cerr << "Failed to write " << filename << std::endl;
return;
}
std::ostream os(&ap_clients_output);
// data fields
os << "Clients,SSID,MAC,\n";
// loop over the router
boost::upgrade_lock readLock(m_router_mutex);
for (boost::ptr_unordered_map::iterator it = m_devices.begin();
it != m_devices.end(); ++it)
{
if (it->second->get_mac() == "00:00:00:00:00:00")
{
continue;
}
os << it->second->get_client_count() << ",";
if (it->second->get_ssid() == "")
{
os << ",";
}
else
{
os << it->second->get_ssid() << ",";
}
os << it->second->get_mac() << std::endl;
}
// close it
ap_clients_output.close();
}
void Packet::add_probe_network(const std::string& p_network, const std::string& p_client)
{
if (p_network.size() < 3)
{
return;
}
// only accept ascii, I guess
for (unsigned int i = 0; i < p_network.size(); i++)
{
if (p_network[i] > 0x7e || p_network[i] < 0x20)
{
return;
}
}
{
// get read lock for client lookup
boost::upgrade_lock readLock(m_probe_mutex);
if (m_probed_networks.find(p_network) == m_probed_networks.end())
{
// upgrade to a write lock and insert the new probe network
boost::upgrade_to_unique_lock writeLock(readLock);
Probed_Network* new_probe = &m_probed_networks[p_network];
new_probe->set_name(p_network);
new_probe->add_client(string_mac_to_int(p_client));
}
else
{
// upgrade to a write lock and add the client to the prexisting probe network
boost::upgrade_to_unique_lock writeLock(readLock);
m_probed_networks[p_network].add_client(string_mac_to_int(p_client));
}
}
}
================================================
FILE: pi_sniffer/src/packet.hpp
================================================
#ifndef PACKET_HPP
#define PACKET_HPP
#include
#include
#include
#include
#include
#include
#include "stats.hpp"
#include "configuration.hpp"
class Client;
class AP;
class Probed_Network;
/**
* Packet is an unfortunate design decision. It, more or less, holds or owns all the
* objects in the system. This is done for convience so that all protocols have access
* to all data. It's also manipulated by both the UI thread and the packet thread.
*
* On the packet side, packets are read into the data member variable and the object
* is sent down the protocol stack (80211 -> snap -> eapol).
*
* On the UI side, the packet's devices/client maps are queried as the user sees fit.
*
* As mentioned, this object (in particularly the maps) is accessed via two threads
* so be cautious.
*/
class Packet
{
public:
Packet();
~Packet();
// resets the member variables associated with packet data
void reset();
// find the access point with the given mac in m_devices
AP* find_ap(boost::uint64_t p_mac);
// find the client with the given mac in m_clients
Client* find_client(boost::uint64_t p_mac, bool p_associated);
// store a probe request
void add_probe_network(const std::string& p_network, const std::string& p_client);
// get a const reference to the configuration
const Configuration& get_const_config() const;
// get the configuration
Configuration& get_config();
// get all access points seen in the last p_seconds seconds
void get_recent_ap(boost::uint32_t p_seconds, std::vector& p_routers);
// get all clients seen in the last p_seconds seconds
void get_recent_client(boost::uint32_t p_seconds, std::vector& p_clients);
// file output functions
void write_wigle_output(const std::string& p_time);
void write_kml_output(const std::string& p_time);
void write_client_csv_output(const std::string& p_time);
void write_probe_csv_output(const std::string& p_time);
void write_ap_clients_csv_output(const std::string& p_time);
private:
Packet(const Packet& p_rhs);
Packet& operator=(const Packet& p_rhs);
private:
// All observered routers: mac to router mapping
boost::ptr_unordered_map m_devices;
// All observed clients: mac to client mapping
boost::ptr_unordered_map m_clients;
// All the probe requests seend (ssid -> Probed object)
boost::ptr_map m_probed_networks;
// mutex for accessing m_devices
boost::shared_mutex m_router_mutex;
// mutex for accessing m_clients
boost::shared_mutex m_client_mutex;
// mutex for accessing probes
boost::shared_mutex m_probe_mutex;
// holds the parsed configuration data
Configuration m_configuration;
public:
// Holds basic counter statistics
Stats m_stats;
// the below should *only* be accessed via the packet thread (except m_shudown)
// packet information provided by the input method (pcap or kismet)
const unsigned char* m_data;
std::size_t m_length;
boost::uint32_t m_time;
double m_lat;
double m_long;
double m_alt;
boost::int8_t m_signal;
bool m_gps_on;
// cached result of the current devices we are operating on
Client* m_current_client;
AP* m_current_router;
bool m_from_client;
// created when the program starts up. largely used for output
std::string m_startTime;
// cross thread indicator that its time to shutdown
bool m_shutdown;
};
#endif
================================================
FILE: pi_sniffer/src/probed_network.cpp
================================================
#include "probed_network.hpp"
Probed_Network::Probed_Network() :
m_accesslock(),
m_name(),
m_clients()
{
}
Probed_Network::~Probed_Network()
{
}
void Probed_Network::set_name(const std::string& p_name)
{
m_name.assign(p_name);
}
void Probed_Network::add_client(boost::uint64_t p_mac)
{
m_clients.insert(p_mac);
}
================================================
FILE: pi_sniffer/src/probed_network.hpp
================================================
#ifndef PROBED_NETWORK_HPP
#define PROBED_NETWORK_HPP
#include
#include
#include
#include
/**
* A simple object that stores an SSID that was probed and all the client macs
* that probed it. This *can be* prob
*/
class Probed_Network
{
public:
Probed_Network();
~Probed_Network();
// stores the name probed for
void set_name(const std::string& p_name);
// add a client to the set of STA that probed this ssid
void add_client(boost::uint64_t p_mac);
std::size_t get_clients_count() const
{
return m_clients.size();
}
private:
boost::mutex m_accesslock;
std::string m_name;
std::set m_clients;
};
#endif
================================================
FILE: pi_sniffer/src/protocols/eapol11.cpp
================================================
#include "eapol11.hpp"
#include "packet.hpp"
#include "ap.hpp"
#include
#include
namespace
{
#pragma pack(push, 1)
struct key_struct
{
boost::uint8_t type;
boost::uint16_t key_info;
boost::uint16_t key_length;
boost::uint8_t replay_counter[8];
boost::uint8_t key_nonce[32];
boost::uint8_t key_iv[16];
boost::uint8_t key_rsc[8];
boost::uint8_t key_id[8];
boost::uint8_t key_mic[16];
boost::uint16_t key_data_length;
};
BOOST_STATIC_ASSERT(sizeof(key_struct) == 95);
#pragma pack(pop)
}
EAPOL::EAPOL()
{
}
EAPOL::~EAPOL()
{
}
bool EAPOL::handle_packet(Packet& p_packet)
{
// too short to be an eapol (or an interesting one at least)
if (p_packet.m_length < 4)
{
return false;
}
const boost::uint16_t length =
ntohs(*reinterpret_cast(p_packet.m_data + 2));
if (static_cast(length + 4) > p_packet.m_length)
{
return false;
}
if (p_packet.m_data[1] == 0)
{
return false;
}
p_packet.m_data += 4;
p_packet.m_length -= 4;
if (p_packet.m_length < sizeof(key_struct))
{
return false;
}
const struct key_struct* key_data =
reinterpret_cast(p_packet.m_data);
if (key_data->type != 1 && key_data->type != 2)
{
// these aren't key types we are interested in
return false;
}
// Used to build HCCAPX here. However, it appears that cap2hccapx is
// significantly better than my crap. Maybe think of a way to isolate eapol
// and a beacon for each auth attempt? Otherwise its fine to just convert
// to hccapx at the end of the run.
p_packet.m_stats.increment_eapol();
return true;
}
================================================
FILE: pi_sniffer/src/protocols/eapol11.hpp
================================================
#ifndef EAPOL_HPP
#define EAPOL_HPP
class Packet;
class EAPOL
{
public:
EAPOL();
~EAPOL();
bool handle_packet(Packet& p_packet);
};
#endif //EAPOL_HPP
================================================
FILE: pi_sniffer/src/protocols/ieee80211.cpp
================================================
#include "ieee80211.hpp"
#include "packet.hpp"
#include "client.hpp"
#include "ap.hpp"
#include "probed_network.hpp"
#include "util/convert.hpp"
#include
#include
#include
#include
#include
#include
#include
#include
IEEE80211::IEEE80211() :
m_snap(),
m_pcap_out()
{
}
IEEE80211::~IEEE80211()
{
}
bool IEEE80211::handle_packet(Packet& p_packet)
{
if (p_packet.m_length < 8)
{
return false;
}
switch (p_packet.m_data[0])
{
case 0x00:
do_association(p_packet);
break;
case 0x40:
do_probe_request(p_packet);
break;
case 0x50:
do_probe_response(p_packet);
break;
case 0x80:
do_beacon(p_packet);
break;
case 0x08:
do_data(p_packet);
break;
case 0x88:
do_qos(p_packet);
break;
default:
break;
}
return true;
}
AP* IEEE80211::get_ap(Packet& p_packet, std::size_t p_ssid_offset, int p_depth)
{
boost::uint64_t bssid_mac = (*reinterpret_cast(
p_packet.m_data + p_ssid_offset));
bssid_mac = (bssid_mac >> 16);
bssid_mac = (bssid_mac << 16);
bssid_mac = be64toh(bssid_mac);
if (bssid_mac == 0 && p_depth == 1)
{
return get_ap(p_packet, p_ssid_offset - 6, 0);
}
return p_packet.find_ap(bssid_mac);
}
Client* IEEE80211::get_client(Packet& p_packet, std::size_t p_src_offset, bool p_associated)
{
boost::uint64_t src_mac = (*reinterpret_cast(
p_packet.m_data + p_src_offset));
src_mac = (src_mac >> 16);
src_mac = (src_mac << 16);
src_mac = be64toh(src_mac);
if (src_mac == 0x0000ffffffffffffULL)
{
return NULL;
}
return p_packet.find_client(src_mac, p_associated);
}
void IEEE80211::do_probe_request(Packet& p_packet)
{
if (p_packet.get_const_config().get_pcap())
{
m_pcap_out.add_packet(p_packet);
}
if (p_packet.m_length <= 26)
{
return;
}
// don't want to allocate a client for this since we only track associated
// clients. However, we want to track probbed networks, so extract the transmitter
boost::uint64_t src_mac = (*reinterpret_cast(
p_packet.m_data + 8));
src_mac = (src_mac >> 16);
src_mac = (src_mac << 16);
src_mac = be64toh(src_mac);
unsigned char* mac = reinterpret_cast(&src_mac);
std::string mac_address(printable_mac(mac, 6));
// TODO this should really reuse the tagged parameters loop
if (p_packet.m_data[24] == 0)
{
if (p_packet.m_length < static_cast(24 + p_packet.m_data[25]))
{
return;
}
std::string ssid(reinterpret_cast(p_packet.m_data + 26), p_packet.m_data[25]);
p_packet.add_probe_network(ssid, mac_address);
}
}
void IEEE80211::do_probe_response(Packet& p_packet)
{
do_beacon(p_packet);
}
void IEEE80211::do_beacon(Packet& p_packet)
{
if (p_packet.get_const_config().get_pcap())
{
m_pcap_out.add_packet(p_packet);
}
AP* found = get_ap(p_packet, 14, 1);
p_packet.m_stats.increment_beacons();
if (found->get_beacon_parsed())
{
// let's only do this once
return;
}
if (p_packet.m_length < 36)
{
return;
}
const unsigned char* management_frame = p_packet.m_data + 24;
boost::uint16_t capabilities = *reinterpret_cast(management_frame + 10);
if ((capabilities & 0x0010) != 0)
{
found->set_encryption("WEP");
}
else
{
found->set_encryption("None");
p_packet.m_stats.increment_unencrypted();
}
boost::uint16_t tagged_length = p_packet.m_length - 36;
if (tagged_length == 0)
{
return;
}
bool found_ssid = false;
bool wpa = false;
bool wpa2 = false;
bool psk = false;
bool eap = false;
const unsigned char* tagged_data = management_frame + 12;
while (tagged_length > 2)
{
if (tagged_length < (tagged_data[1] + 2))
{
break;
}
boost::uint8_t tag = tagged_data[0];
boost::uint8_t length = tagged_data[1];
const unsigned char* value = tagged_data + 2;
// skip over this data
tagged_length -= (tagged_data[1] + 2);
tagged_data += (tagged_data[1] + 2);
switch (tag)
{
case 0:
if (found_ssid)
{
break;
}
found_ssid = true;
if (length != 0)
{
if (value[0] != 0)
{
std::string ssid(reinterpret_cast(value), length);
found->set_ssid(ssid);
}
}
else
{
found->set_ssid("");
}
break;
case 3:
found->set_channel(value[0]);
break;
case 0x30: // RSN
{
if (length <= 8)
{
break;
}
// jump version and oui lead in
value += 6;
boost::uint16_t pairwise = *reinterpret_cast(value);
if (length <= (8 + (pairwise * 4)))
{
break;
}
value += 2;
for (boost::uint16_t pair_loop = pairwise ; pair_loop > 0; --pair_loop)
{
if (value[3] == 0x04)
{
wpa2 = true;
}
value += 4;
}
if (!wpa2)
{
wpa = true;
}
boost::uint16_t auth = *reinterpret_cast(value);
if (length <= (10 + (pairwise * 4) + (auth * 4)))
{
break;
}
value += 2;
for ( ; auth > 0; --auth)
{
if (value[3] == 0x02)
{
psk = true;
}
else if(value[3] == 1)
{
eap = true;
}
value += 4;
}
break;
}
case 0xdd:
if (length > 4 && memcmp(value, "\x00\x50\xf2", 3) == 0) // MS
{
switch(value[3])
{
case 1: // WPA IE
{
if (length <= 12)
{
break;
}
wpa = true;
// jump version and oui lead in
value += 10;
boost::uint16_t pairwise = *reinterpret_cast(value);
if (length <= (12 + (pairwise * 4)))
{
break;
}
value += 2;
for (boost::uint16_t pair_loop = pairwise; pair_loop > 0; --pair_loop)
{
value += 4;
}
boost::uint16_t auth = *reinterpret_cast(value);
if (length <= (14 + (pairwise * 4) + (auth * 4)))
{
break;
}
for ( ; auth > 0; --auth)
{
if (value[3] == 0x02)
{
psk = true;
}
else if (value[3] == 1)
{
eap = true;
}
value += 4;
}
break;
}
case 4: // wps
// skip over oui and type
value += 4;
length -= 4;
while (length > 4)
{
boost::uint16_t type = ntohs(*reinterpret_cast(value));
value += 2;
length -= 2;
boost::uint16_t inner_length = ntohs(*reinterpret_cast(value));
value += 2;
length -= 2;
if (inner_length > length)
{
break;
}
switch (type)
{
case 0x1011:
if (found->get_ssid().empty())
{
found->set_ssid(std::string(reinterpret_cast(value), inner_length));
}
break;
case 0x1044:
if (*value == 0x02)
{
found->set_wps(true);
p_packet.m_stats.increment_wps();
}
break;
default:
break;
}
value += inner_length;
length -= inner_length;
}
break;
default:
break;
}
}
break;
default:
break;
}
}
// libtins wants a shot at the beacon... honestly we should just rewrite to use libtins
if (p_packet.m_current_router &&
p_packet.get_const_config().has_wpa_key(p_packet.m_current_router->get_ssid()))
{
try
{
boost::scoped_ptr tinsPacket(Tins::Dot11::from_bytes(p_packet.m_data, p_packet.m_length));
if (tinsPacket.get() != NULL)
{
p_packet.get_config().m_wpa_decrypter.decrypt(*tinsPacket);
}
}
catch (const std::exception&)
{
}
}
found->set_beacon_parsed();
std::string encryption;
if (wpa)
{
encryption.append("WPA");
}
if (wpa2)
{
if (!encryption.empty())
{
encryption.push_back('/');
}
encryption.append("WPA2");
}
if (wpa || wpa2)
{
p_packet.m_stats.increment_wpa();
}
else if (found->get_encryption() == "WEP")
{
p_packet.m_stats.increment_wep();
}
if (psk)
{
encryption.append("-PSK");
}
else if (eap)
{
encryption.append("-EAP");
}
if (!encryption.empty())
{
found->set_encryption(encryption);
}
}
void IEEE80211::do_association(Packet& p_packet)
{
if (p_packet.get_const_config().get_pcap())
{
m_pcap_out.add_packet(p_packet);
}
AP* found = get_ap(p_packet, 14);
if (p_packet.m_length < 36)
{
return;
}
const unsigned char* management_frame = p_packet.m_data + 24;
boost::uint16_t tagged_length = p_packet.m_length - 36;
if (tagged_length == 0)
{
return;
}
bool found_ssid = false;
const unsigned char* tagged_data = management_frame + 4;
while (tagged_length > 2)
{
if (tagged_length < (tagged_data[1] + 2))
{
break;
}
boost::uint8_t tag = tagged_data[0];
boost::uint8_t length = tagged_data[1];
const unsigned char* value = tagged_data + 2;
tagged_length -= (tagged_data[1] + 2);
tagged_data += (tagged_data[1] + 2);
switch (tag)
{
case 0:
if (found_ssid)
{
break;
}
found_ssid = true;
if (length != 0)
{
if (value[0] != 0)
{
std::string ssid(reinterpret_cast(value), length);
found->set_ssid(ssid);
}
}
else
{
found->set_ssid("");
}
break;
case 3:
found->set_channel(value[0]);
break;
default:
break;
}
}
}
void IEEE80211::do_data(Packet& p_packet)
{
AP* found = NULL;
Client* client = NULL;
if ((p_packet.m_data[1] & 0x03) == 0x03)
{
found = get_ap(p_packet, 8);
client = get_client(p_packet, 22, true);
if (client == NULL)
{
return;
}
p_packet.m_from_client = true;
p_packet.m_length -= 6;
p_packet.m_data += 6;
}
else if ((p_packet.m_data[1] & 0x02) == 2)
{
found = get_ap(p_packet, 8);
if (memcmp(p_packet.m_data + 10, p_packet.m_data + 16, 6) != 0)
{
client = get_client(p_packet, 14, true);
if (client == NULL)
{
return;
}
p_packet.m_from_client = true;
}
else
{
p_packet.m_from_client = false;
}
}
else if((p_packet.m_data[1] & 0x01) == 1)
{
found = get_ap(p_packet, 2);
client = get_client(p_packet, 8, true);
if (client == NULL)
{
return;
}
p_packet.m_from_client = true;
}
else
{
found = get_ap(p_packet, 14);
client = get_client(p_packet, 8, true);
if (client == NULL)
{
return;
}
p_packet.m_from_client = true;
}
found->increment_data_packet();
p_packet.m_stats.increment_data_packets();
if (p_packet.m_length > 24)
{
handle_data(p_packet, 24);
}
}
void IEEE80211::do_qos(Packet& p_packet)
{
AP* found = NULL;
Client* client = NULL;
if ((p_packet.m_data[1] & 0x03) == 0x03)
{
found = get_ap(p_packet, 8);
client = get_client(p_packet, 22, true);
if (client == NULL)
{
return;
}
p_packet.m_from_client = true;
p_packet.m_length -= 6;
p_packet.m_data += 6;
}
else if ((p_packet.m_data[1] & 0x02) == 2)
{
found = get_ap(p_packet, 8);
client = get_client(p_packet, 14, true);
if (client == NULL)
{
return;
}
p_packet.m_from_client = false;
}
else if ((p_packet.m_data[1] & 0x01) == 1)
{
found = get_ap(p_packet, 2);
client = get_client(p_packet, 8, true);
if (client == NULL)
{
return;
}
p_packet.m_from_client = true;
}
else
{
found = get_ap(p_packet, 14);
client = get_client(p_packet, 8, true);
if (client == NULL)
{
return;
}
p_packet.m_from_client = true;
}
found->increment_data_packet();
p_packet.m_stats.increment_data_packets();
if (p_packet.m_length > 26)
{
handle_data(p_packet, 26);
}
}
void IEEE80211::handle_data(Packet& p_packet, boost::uint32_t p_increment)
{
// check if we have a snap header
if (memcmp(p_packet.m_data + p_increment, "\xaa\xaa\x03", 3) == 0)
{
if (p_packet.get_const_config().get_pcap())
{
m_pcap_out.add_packet(p_packet);
}
// libtins wants a shot at the handshake... honestly we should just rewrite to use libtins
if (p_packet.m_current_router &&
p_packet.get_const_config().has_wpa_key(p_packet.m_current_router->get_ssid()))
{
try
{
boost::scoped_ptr tinsPacket(Tins::Dot11::from_bytes(p_packet.m_data, p_packet.m_length));
if (tinsPacket.get() != NULL)
{
p_packet.get_config().m_wpa_decrypter.decrypt(*tinsPacket);
}
}
catch (const std::exception&)
{
}
}
p_packet.m_length -= p_increment;
p_packet.m_data += p_increment;
m_snap.handle_packet(p_packet);
}
else
{
// is this wep or wpa?
const std::string& encryption(p_packet.m_current_router->get_encryption());
if (encryption == "WEP")
{
p_packet.m_stats.increment_encrypted();
handle_wep(p_packet);
}
else if (!encryption.empty())
{
p_packet.m_stats.increment_encrypted();
handle_wpa(p_packet);
}
else if (p_packet.get_config().get_pcap())
{
// cleartext that isn't SNAP
m_pcap_out.add_packet(p_packet);
}
}
}
void IEEE80211::handle_wep(Packet& p_packet)
{
if (p_packet.m_current_router &&
p_packet.get_const_config().has_wep_key(p_packet.m_current_router->get_mac()))
{
try
{
boost::scoped_ptr tinsPacket(Tins::Dot11::from_bytes(p_packet.m_data, p_packet.m_length));
if (tinsPacket.get() != NULL)
{
bool return_value = p_packet.get_config().m_wep_decrypter.decrypt(*tinsPacket);
if (return_value)
{
std::vector decrypted = tinsPacket->serialize();
if (!decrypted.empty())
{
p_packet.m_stats.increment_decrypted();
p_packet.m_data = &decrypted[0];
p_packet.m_length = decrypted.size();
if (p_packet.get_const_config().get_pcap())
{
// write the decrypted version to the pcap
m_pcap_out.add_packet(p_packet);
}
if (memcmp(p_packet.m_data, "\xaa\xaa\x03\x00\x00", 5) == 0)
{
// we only really handle snap at this point
m_snap.handle_packet(p_packet);
}
return;
}
}
// decryption failed for some reason
p_packet.m_stats.increment_failed_decrypt();
}
}
catch (std::exception&)
{
}
}
if (p_packet.get_const_config().get_pcap())
{
m_pcap_out.add_packet(p_packet);
}
}
void IEEE80211::handle_wpa(Packet& p_packet)
{
if (p_packet.m_current_router &&
p_packet.get_const_config().has_wpa_key(p_packet.m_current_router->get_ssid()))
{
try
{
boost::scoped_ptr tinsPacket(Tins::Dot11::from_bytes(p_packet.m_data, p_packet.m_length));
if (tinsPacket.get() != NULL)
{
bool return_value = p_packet.get_config().m_wpa_decrypter.decrypt(*tinsPacket);
if (return_value)
{
std::vector decrypted = tinsPacket->serialize();
if (!decrypted.empty())
{
p_packet.m_stats.increment_decrypted();
p_packet.m_data = &decrypted[0];
p_packet.m_length = decrypted.size();
if (p_packet.get_const_config().get_pcap())
{
// write the decrypted version to the pcap
m_pcap_out.add_packet(p_packet);
}
if (memcmp(p_packet.m_data, "\xaa\xaa\x03\x00\x00", 5) == 0)
{
// we only really handle snap at this point
m_snap.handle_packet(p_packet);
}
return;
}
}
// decryption failed for some reason
p_packet.m_stats.increment_failed_decrypt();
}
}
catch (std::exception&)
{
}
}
if (p_packet.get_const_config().get_pcap())
{
m_pcap_out.add_packet(p_packet);
}
}
================================================
FILE: pi_sniffer/src/protocols/ieee80211.hpp
================================================
#include
#include
#include
#include "llcsnap.hpp"
#include "util/pcap_output.hpp"
class AP;
class Client;
class IEEE80211
{
public:
IEEE80211();
~IEEE80211();
bool handle_packet(Packet& p_packet);
private:
AP* get_ap(Packet& p_packet, std::size_t p_ssid_offset, int p_depth = 0);
Client* get_client(Packet& p_packet, std::size_t p_src_offset, bool p_associated);
void do_probe_request(Packet& p_packet);
void do_probe_response(Packet& p_packet);
void do_beacon(Packet& p_packet);
void do_data(Packet& p_packet);
void do_qos(Packet& p_packet);
void do_association(Packet& p_packet);
void handle_data(Packet& p_packet, boost::uint32_t p_increment);
void handle_wep(Packet& p_packet);
void handle_wpa(Packet& p_packet);
private:
LLCSNAP m_snap;
PcapOutput m_pcap_out;
};
================================================
FILE: pi_sniffer/src/protocols/llcsnap.cpp
================================================
#include "llcsnap.hpp"
#include
#include
#include "packet.hpp"
LLCSNAP::LLCSNAP() :
m_eapol()
{
}
LLCSNAP::~LLCSNAP()
{
}
bool LLCSNAP::handle_packet(Packet& p_packet)
{
if (p_packet.m_length < 8)
{
return false;
}
boost::uint16_t next_proto = *reinterpret_cast(p_packet.m_data + 6);
next_proto = ntohs(next_proto);
p_packet.m_data += 8;
p_packet.m_length -= 8;
if (next_proto == 0x888e)
{
m_eapol.handle_packet(p_packet);
}
return true;
}
================================================
FILE: pi_sniffer/src/protocols/llcsnap.hpp
================================================
#ifndef SNAP_HPP
#define SNAP_HPP
class Packet;
#include "eapol11.hpp"
class LLCSNAP
{
public:
LLCSNAP();
~LLCSNAP();
bool handle_packet(Packet& p_packet);
private:
EAPOL m_eapol;
};
#endif
================================================
FILE: pi_sniffer/src/stats.cpp
================================================
#include "stats.hpp"
Stats::Stats() :
m_accesslock(),
m_unecrypted(0),
m_wep(0),
m_wpa(0),
m_wps(0),
m_data(0),
m_encrypted(0),
m_decrypted(0),
m_failed_decrypt(0),
m_packets(0),
m_beacons(0),
m_eapol(0)
{
}
Stats::~Stats()
{
}
void Stats::increment_unencrypted()
{
++m_unecrypted;
}
boost::uint32_t Stats::get_unencrypted() const
{
return m_unecrypted;
}
void Stats::increment_wep()
{
++m_wep;
}
boost::uint32_t Stats::get_wep() const
{
return m_wep;
}
void Stats::increment_wpa()
{
++m_wpa;
}
boost::uint32_t Stats::get_wpa() const
{
return m_wpa;
}
void Stats::increment_wps()
{
++m_wps;
}
void Stats::increment_data_packets()
{
++m_data;
}
boost::uint32_t Stats::get_data_packets() const
{
return m_data;
}
void Stats::increment_packets()
{
++m_packets;
}
boost::uint32_t Stats::get_packets() const
{
return m_packets;
}
void Stats::increment_beacons()
{
++m_beacons;
}
boost::uint32_t Stats::get_beacons() const
{
return m_beacons;
}
void Stats::increment_encrypted()
{
++m_encrypted;
}
boost::uint32_t Stats::get_encrypted() const
{
return m_encrypted;
}
boost::uint32_t Stats::get_decrypted() const
{
return m_decrypted;
}
void Stats::increment_decrypted()
{
++m_decrypted;
}
boost::uint32_t Stats::get_failed_decrypt() const
{
return m_failed_decrypt;
}
void Stats::increment_failed_decrypt()
{
++m_failed_decrypt;
}
boost::uint32_t Stats::get_eapol() const
{
return m_eapol;
}
void Stats::increment_eapol()
{
++m_eapol;
}
================================================
FILE: pi_sniffer/src/stats.hpp
================================================
#ifndef STATS_HPP
#define STATS_HPP
#include
#include
/**
* Stats is a an object that is largely a wrapper simple counters. The information
* in stats is tracked to get a high level overview of what the engine has seen.
* Most useful in a overview type screen or shutdown stats.
*/
class Stats
{
public:
Stats();
~Stats();
void increment_unencrypted();
boost::uint32_t get_unencrypted() const;
void increment_wep();
boost::uint32_t get_wep() const;
void increment_wpa();
boost::uint32_t get_wpa() const;
void increment_wps();
void increment_data_packets();
boost::uint32_t get_data_packets() const;
void increment_packets();
boost::uint32_t get_packets() const;
void increment_beacons();
boost::uint32_t get_beacons() const;
void increment_probe_requests();
boost::uint32_t get_encrypted() const;
void increment_encrypted();
boost::uint32_t get_decrypted() const;
void increment_decrypted();
boost::uint32_t get_failed_decrypt() const;
void increment_failed_decrypt();
boost::uint32_t get_eapol() const;
void increment_eapol();
private:
Stats(const Stats& p_rhs);
Stats& operator=(const Stats& p_rhs);
private:
boost::mutex m_accesslock;
boost::uint32_t m_unecrypted;
boost::uint32_t m_wep;
boost::uint32_t m_wpa;
boost::uint32_t m_wps;
boost::uint32_t m_data;
boost::uint32_t m_encrypted;
boost::uint32_t m_decrypted;
boost::uint32_t m_failed_decrypt;
boost::uint32_t m_packets;
boost::uint32_t m_beacons;
boost::uint32_t m_eapol;
};
#endif
================================================
FILE: pi_sniffer/src/util/convert.cpp
================================================
#include "convert.hpp"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#ifdef OS_WINDOWS
#include
#endif
namespace
{
const unsigned char k_hex[] =
{
'0','1','2','3','4','5','6','7',
'8','9','a','b','c','d','e','f'
};
const unsigned char k_hex_int[] =
{
0, 1, 2, 3, 4, 5, 6, 7,
8, 9, 10, 11, 12, 13, 14, 15
};
const char b64_table[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static const char reverse_table[128] =
{
64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 62, 64, 64, 64, 63,
52, 53, 54, 55, 56, 57, 58, 59,
60, 61, 64, 64, 64, 64, 64, 64,
64, 0, 1, 2, 3, 4, 5, 6,
7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22,
23, 24, 25, 64, 64, 64, 64, 64,
64, 26, 27, 28, 29, 30, 31, 32,
33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48,
49, 50, 51, 64, 64, 64, 64, 64
};
}
std::string printable_mac(const unsigned char* p_data, std::size_t p_length, bool p_reverse)
{
std::string return_string;
for(std::size_t byte = 0; byte < p_length; ++byte)
{
if (p_reverse)
{
return_string.push_back(k_hex[p_data[byte] & 0x0F]);
return_string.push_back(k_hex[(p_data[byte] >> 4) & 0x0F]);
}
else
{
return_string.push_back(k_hex[(p_data[byte] >> 4) & 0x0F]);
return_string.push_back(k_hex[p_data[byte] & 0x0F]);
}
if(byte + 1 != p_length)
{
return_string.push_back(':');
}
}
if (p_reverse)
{
std::reverse(return_string.begin(), return_string.end());
}
return return_string;
}
boost::uint64_t string_mac_to_int(const std::string& p_mac)
{
std::vector octets;
boost::algorithm::split(octets, p_mac, boost::is_any_of(":"));
if (octets.size() != 6)
{
throw std::runtime_error("Malformed MAC address");
}
boost::uint64_t return_value = 0;
BOOST_FOREACH(std::string& octet, octets)
{
unsigned int convert = 0;
std::stringstream in;
in << std::hex << octet;
in >> convert;
convert = (convert & 0xff);
return_value = (return_value << 8);
return_value |= (convert & 0xff);
}
return return_value;
}
Tins::HWAddress<6> int_mac_to_array(boost::uint64_t p_mac)
{
boost::array address;
for (int i = 5, j=0; i >= 0; --i, ++j)
{
address[i] = reinterpret_cast(&p_mac)[j];
}
Tins::HWAddress<6> return_value(address.c_array());
return return_value;
}
std::string string_to_hex(const std::string& p_mac1)
{
std::string p_mac(p_mac1);
std::string return_value;
unsigned char hex_value = 0;
if ((p_mac.size() % 2) != 0)
{
throw std::runtime_error("Hex strings must have both nibbles");
}
// replace the a-f values
for (std::size_t i = 0; i < p_mac.size(); ++i)
{
switch (p_mac[i])
{
case '0':
p_mac[i] = 0x00;
break;
case '1':
p_mac[i] = 0x01;
break;
case '2':
p_mac[i] = 0x02;
break;
case '3':
p_mac[i] = 0x03;
break;
case '4':
p_mac[i] = 0x04;
break;
case '5':
p_mac[i] = 0x05;
break;
case '6':
p_mac[i] = 0x06;
break;
case '7':
p_mac[i] = 0x07;
break;
case '8':
p_mac[i] = 0x08;
break;
case '9':
p_mac[i] = 0x09;
break;
case 'a':
p_mac[i] = 0x0a;
break;
case 'b':
p_mac[i] = 0x0b;
break;
case 'c':
p_mac[i] = 0x0c;
break;
case 'd':
p_mac[i] = 0x0d;
break;
case 'e':
p_mac[i] = 0x0e;
break;
case 'f':
p_mac[i] = 0x0f;
break;
default:
std::stringstream error;
error << "Non hex value in decrypt key: " << p_mac[i];
throw std::runtime_error(error.str());
}
if ((i % 2) == 0)
{
hex_value = p_mac[i];
hex_value = (hex_value << 4) & 0xf0;
}
else
{
hex_value |= (p_mac[i] & 0x0f);
return_value.push_back(hex_value);
hex_value = 0;
}
}
return return_value;
}
================================================
FILE: pi_sniffer/src/util/convert.hpp
================================================
#include
#include
#include
#include
#include
std::string printable_mac(const unsigned char* p_data, std::size_t p_length, bool p_reverse = true);
boost::uint64_t string_mac_to_int(const std::string& p_mac);
Tins::HWAddress<6> int_mac_to_array(boost::uint64_t p_mac);
std::string string_to_hex(const std::string& p_hex);
================================================
FILE: pi_sniffer/src/util/kml_maker.cpp
================================================
#include "kml_maker.hpp"
#include
#include
#include
#include
#include
#include
#include "ap.hpp"
namespace
{
std::string s_header("\n\n\t\n");
std::string s_footer("\t\t\n\t\n");
}
KML_Maker::KML_Maker() :
m_open(),
m_wep(),
m_wpa()
{
}
KML_Maker::~KML_Maker()
{
}
void KML_Maker::load_aps(boost::ptr_unordered_map& p_ap)
{
for (boost::ptr_unordered_map::iterator current_ap = p_ap.begin();
current_ap != p_ap.end(); ++current_ap)
{
if (current_ap->second->get_best_longitude() > 1.0 || current_ap->second->get_best_longitude() < -1.0)
{
if (current_ap->second->get_encryption() == "WEP")
{
m_wep[current_ap->first] = current_ap->second;
}
else if (current_ap->second->get_encryption().find("WPA") != std::string::npos)
{
m_wpa[current_ap->first] = current_ap->second;
}
else
{
m_open[current_ap->first] = current_ap->second;
}
}
}
}
std::string KML_Maker::write_color(const std::string& p_color) const
{
std::string return_value;
return_value.append("\t\t\n");
return_value.append("\t\t\n");
return return_value;
}
std::string KML_Maker::write_ap(const std::map& p_aps) const
{
std::string return_value;
for (std::map::const_iterator current_ap = p_aps.begin();
current_ap != p_aps.end(); ++current_ap)
{
return_value.append("\t\t\t\n\t\t\t\tsecond->get_ssid());
return_value.append("]]>\n\t\t\t\t\n\t\t\t\t\t");
return_value.append(current_ap->second->get_mac());
return_value.append("
RSSI: ");
std::stringstream rssi;
rssi << static_cast(current_ap->second->get_best_signal());
return_value.append(rssi.str());
return_value.append("
Channel: ");
std::stringstream channel;
channel << (int)current_ap->second->get_channel();
return_value.append(channel.str());
return_value.append("
Encryption: ");
return_value.append(current_ap->second->get_encryption());
return_value.append("
First Seen: ");
const long int timestamp = current_ap->second->get_first_seen();
std::tm* t = std::localtime(×tamp);
std::stringstream firstTime;
firstTime << std::put_time(t, "%Y-%m-%d %I:%M:%S %p");
return_value.append(firstTime.str());
return_value.append("]]>\n\t\t\t\t\n");
return_value.append("\t\t\t\t#");
if (current_ap->second->get_encryption() == "WEP")
{
return_value.append("pink");
}
else if (current_ap->second->get_encryption().find("WPA") != std::string::npos)
{
return_value.append("green");
}
else
{
return_value.append("blue");
}
return_value.append("\n\t\t\t\t\n\t\t\t\t\t");
std::stringstream longitude;
longitude << current_ap->second->get_best_longitude();
return_value.append(longitude.str());
return_value.append(",");
std::stringstream latitude;
latitude << current_ap->second->get_best_latitude();
return_value.append(latitude.str());
return_value.append("\n");
return_value.append("\t\t\t\t\n\t\t\t\n");
}
return return_value;
}
void KML_Maker::write_all(const std::string& p_filename) const
{
write_open(p_filename);
write_wep(p_filename);
write_wpa(p_filename);
}
void KML_Maker::write_open(const std::string& p_filename) const
{
if (m_open.empty())
{
return;
}
std::string filename(p_filename + "_open.kml");
std::ofstream open_ap;
open_ap.open(filename);
if (!open_ap.is_open())
{
return;
}
open_ap << s_header;
open_ap << write_color("blue");
open_ap << "\t\t" << filename << "\n";
open_ap << write_ap(m_open);
open_ap << s_footer;
open_ap.close();
}
void KML_Maker::write_wep(const std::string& p_filename) const
{
if (m_wep.empty())
{
return;
}
std::string filename(p_filename + "_wep.kml");
std::ofstream wep_ap;
wep_ap.open(filename);
if (!wep_ap.is_open())
{
return;
}
wep_ap << s_header;
wep_ap << write_color("pink");
wep_ap << "\t\t" << filename << "\n";
wep_ap << write_ap(m_wep);
wep_ap << s_footer;
wep_ap.close();
}
void KML_Maker::write_wpa(const std::string& p_filename) const
{
if (m_wpa.empty())
{
return;
}
std::string filename(p_filename + "_wpa.kml");
std::ofstream wpa_ap;
wpa_ap.open(filename);
if (!wpa_ap.is_open())
{
return;
}
wpa_ap << s_header;
wpa_ap << write_color("green");
wpa_ap << "\t\t" << filename << "\n";
wpa_ap << write_ap(m_wpa);
wpa_ap << s_footer;
wpa_ap.close();
}
================================================
FILE: pi_sniffer/src/util/kml_maker.hpp
================================================
#ifndef KML_MAKER_HPP
#define KML_MAKER_HPP
#include