mirror of
https://github.com/jayofelony/pwnagotchi.git
synced 2025-07-01 18:37:27 -04:00
Create gps_listener.py
Receive GPS coordinates via termux-location and save whenever an handshake is captured. Signed-off-by: Kris Henriksen <krishenriksen@users.noreply.github.com>
This commit is contained in:
233
pwnagotchi/plugins/default/gps_listener.py
Normal file
233
pwnagotchi/plugins/default/gps_listener.py
Normal file
@ -0,0 +1,233 @@
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import subprocess
|
||||
import threading
|
||||
|
||||
import pwnagotchi.plugins as plugins
|
||||
import pwnagotchi.ui.fonts as fonts
|
||||
from pwnagotchi.ui.components import LabeledValue
|
||||
from pwnagotchi.ui.view import BLACK
|
||||
|
||||
"""
|
||||
# Android
|
||||
# Termux:API : https://f-droid.org/en/packages/com.termux.api/
|
||||
# Termux : https://f-droid.org/en/packages/com.termux/
|
||||
pkg install termux-api socat bc
|
||||
|
||||
-----
|
||||
#!/data/data/com.termux/files/usr/bin/bash
|
||||
|
||||
# Server details
|
||||
SERVER_IP="192.168.44.45" # IP of the socat receiver
|
||||
SERVER_PORT="5000" # UDP port to send data to
|
||||
|
||||
# Function to calculate checksum
|
||||
calculate_checksum() {
|
||||
local sentence="$1"
|
||||
local checksum=0
|
||||
# Loop through each character in the sentence
|
||||
for ((i = 0; i < ${#sentence}; i++)); do
|
||||
checksum=$((checksum ^ $(printf '%d' "'${sentence:i:1}")))
|
||||
done
|
||||
# Return checksum in hexadecimal
|
||||
printf "%02X" $checksum
|
||||
}
|
||||
|
||||
# Infinite loop to send GPS data
|
||||
while true; do
|
||||
# Get location data
|
||||
LOCATION=$(termux-location -p gps)
|
||||
|
||||
# Extract latitude, longitude, altitude, speed, and bearing
|
||||
LATITUDE=$(echo "$LOCATION" | jq '.latitude')
|
||||
LONGITUDE=$(echo "$LOCATION" | jq '.longitude')
|
||||
ALTITUDE=$(echo "$LOCATION" | jq '.altitude')
|
||||
SPEED=$(echo "$LOCATION" | jq '.speed') # Speed in meters per second
|
||||
BEARING=$(echo "$LOCATION" | jq '.bearing')
|
||||
|
||||
# Convert speed from meters per second to knots and km/h
|
||||
SPEED_KNOTS=$(echo "$SPEED" | awk '{printf "%.1f", $1 * 1.943844}')
|
||||
SPEED_KMH=$(echo "$SPEED" | awk '{printf "%.1f", $1 * 3.6}')
|
||||
|
||||
# Format latitude and longitude for NMEA
|
||||
LAT_DEGREES=$(printf "%.0f" "${LATITUDE%.*}")
|
||||
LAT_MINUTES=$(echo "(${LATITUDE#${LAT_DEGREES}} * 60)" | bc -l)
|
||||
LAT_DIRECTION=$(if (( $(echo "$LATITUDE >= 0" | bc -l) )); then echo "N"; else echo "S"; fi)
|
||||
LON_DEGREES=$(printf "%.0f" "${LONGITUDE%.*}")
|
||||
LON_MINUTES=$(echo "(${LONGITUDE#${LON_DEGREES}} * 60)" | bc -l)
|
||||
LON_DIRECTION=$(if (( $(echo "$LONGITUDE >= 0" | bc -l) )); then echo "E"; else echo "W"; fi)
|
||||
|
||||
# Format the NMEA GGA sentence
|
||||
RAW_NMEA_GGA="GPGGA,123519,$(printf "%02d%07.4f" ${LAT_DEGREES#-} $LAT_MINUTES),$LAT_DIRECTION,$(printf "%03d%07.4f" ${LON_DEGREES#-} $LON_MINUTES),$LON_DIRECTION,1,08,0.9,$(printf "%.1f" $ALTITUDE),M,46.9,M,,"
|
||||
CHECKSUM=$(calculate_checksum "$RAW_NMEA_GGA")
|
||||
NMEA_GGA="\$${RAW_NMEA_GGA}*${CHECKSUM}"
|
||||
|
||||
# Format the VTG sentence
|
||||
RAW_NMEA_VTG="GPVTG,$(printf "%.1f" $BEARING),T,,M,$(printf "%.1f" $SPEED_KNOTS),N,$(printf "%.1f" $SPEED_KMH),K"
|
||||
CHECKSUM_VTG=$(calculate_checksum "$RAW_NMEA_VTG")
|
||||
NMEA_VTG="\$${RAW_NMEA_VTG}*${CHECKSUM_VTG}"
|
||||
|
||||
# Send data via UDP
|
||||
echo "$NMEA_GGA"
|
||||
echo "$NMEA_GGA" | socat - UDP:$SERVER_IP:$SERVER_PORT
|
||||
#echo "$NMEA_VTG"
|
||||
#echo "$NMEA_VTG" | socat - UDP:$SERVER_IP:$SERVER_PORT
|
||||
|
||||
sleep 1
|
||||
done
|
||||
-----
|
||||
|
||||
# Pwnagotchi
|
||||
main.plugins.gps_listener.enabled = true
|
||||
|
||||
# packages
|
||||
sudo apt-get install socat
|
||||
"""
|
||||
|
||||
class GPS(plugins.Plugin):
|
||||
__author__ = 'https://github.com/krishenriksen'
|
||||
__version__ = "1.0.0"
|
||||
__license__ = "GPL3"
|
||||
__description__ = "Receive GPS coordinates via termux-location and save whenever an handshake is captured."
|
||||
|
||||
def __init__(self):
|
||||
self.listen_ip = self.get_ip_address('bnep0')
|
||||
self.listen_port = "5000"
|
||||
self.write_virtual_serial = "/dev/ttyUSB1"
|
||||
self.read_virtual_serial = "/dev/ttyUSB0"
|
||||
self.baud_rate = "19200"
|
||||
self.socat_process = None
|
||||
self.stop_event = threading.Event()
|
||||
self.status_lock = threading.Lock()
|
||||
self.status = '-'
|
||||
self.socat_thread = threading.Thread(target=self.run_socat)
|
||||
|
||||
def get_ip_address(self, interface):
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["ip", "addr", "show", interface],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=True
|
||||
)
|
||||
for line in result.stdout.split('\n'):
|
||||
if 'inet ' in line:
|
||||
ip_address = line.strip().split()[1].split('/')[0]
|
||||
return ip_address
|
||||
except subprocess.CalledProcessError:
|
||||
logging.warning(f"Could not get IP address for interface {interface}")
|
||||
return None
|
||||
|
||||
def set_status(self, status):
|
||||
with self.status_lock:
|
||||
self.status = status
|
||||
|
||||
def get_status(self):
|
||||
with self.status_lock:
|
||||
return self.status
|
||||
|
||||
def on_loaded(self):
|
||||
logging.info("GPS Listener plugin loaded")
|
||||
self.cleanup_virtual_serial_ports()
|
||||
self.create_virtual_serial_ports()
|
||||
self.socat_thread.start()
|
||||
|
||||
def cleanup_virtual_serial_ports(self):
|
||||
if os.path.exists(self.write_virtual_serial):
|
||||
self.log.info(f"Removing old {self.write_virtual_serial}")
|
||||
os.remove(self.write_virtual_serial)
|
||||
|
||||
if os.path.exists(self.read_virtual_serial):
|
||||
self.log.info(f"Removing old {self.read_virtual_serial}")
|
||||
os.remove(self.read_virtual_serial)
|
||||
|
||||
def create_virtual_serial_ports(self):
|
||||
self.socat_process = subprocess.Popen(
|
||||
["socat", "-d", "-d", f"pty,link={self.write_virtual_serial},mode=777",
|
||||
f"pty,link={self.read_virtual_serial},mode=777"],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
)
|
||||
|
||||
def run_socat(self):
|
||||
while not self.stop_event.is_set():
|
||||
self.socat_process = subprocess.Popen(
|
||||
["socat", f"UDP-RECVFROM:{self.listen_port},reuseaddr,bind={self.listen_ip}", "-"],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
text=True
|
||||
)
|
||||
|
||||
self.set_status('C')
|
||||
|
||||
with open(self.write_virtual_serial, 'w') as serial_port:
|
||||
for line in self.socat_process.stdout:
|
||||
if self.stop_event.is_set():
|
||||
break
|
||||
serial_port.write(line)
|
||||
serial_port.flush() # Ensure the data is written immediately
|
||||
self.status = 'C'
|
||||
|
||||
self.socat_process.wait()
|
||||
if self.stop_event.is_set():
|
||||
break
|
||||
|
||||
self.set_status('-')
|
||||
|
||||
def cleanup(self):
|
||||
if self.socat_process:
|
||||
self.socat_process.terminate()
|
||||
self.socat_process.wait() # Ensure the process is reaped
|
||||
self.stop_event.set()
|
||||
self.socat_thread.join()
|
||||
self.cleanup_virtual_serial_ports()
|
||||
|
||||
def on_ready(self, agent):
|
||||
if os.path.exists(self.read_virtual_serial):
|
||||
logging.info(
|
||||
f"enabling bettercap's gps module for {self.read_virtual_serial}"
|
||||
)
|
||||
try:
|
||||
agent.run("gps off")
|
||||
except Exception:
|
||||
logging.info(f"bettercap gps module was already off")
|
||||
pass
|
||||
|
||||
agent.run(f"set gps.device {self.read_virtual_serial}")
|
||||
agent.run(f"set gps.baudrate {self.baud_rate}")
|
||||
agent.run("gps on")
|
||||
|
||||
logging.info(f"bettercap gps module enabled on {self.read_virtual_serial}")
|
||||
else:
|
||||
self.set_status('NF')
|
||||
logging.warning("no GPS detected")
|
||||
|
||||
def on_handshake(self, agent, filename, access_point, client_station):
|
||||
info = agent.session()
|
||||
coordinates = info["gps"]
|
||||
gps_filename = filename.replace(".pcap", ".gps.json")
|
||||
|
||||
if coordinates and all([
|
||||
# avoid 0.000... measurements
|
||||
coordinates["Latitude"], coordinates["Longitude"]
|
||||
]):
|
||||
self.set_status('S')
|
||||
logging.info(f"saving GPS to {gps_filename} ({coordinates})")
|
||||
with open(gps_filename, "w+t") as fp:
|
||||
json.dump(coordinates, fp)
|
||||
else:
|
||||
logging.warning("not saving GPS. Couldn't find location.")
|
||||
|
||||
def on_ui_setup(self, ui):
|
||||
with ui._lock:
|
||||
ui.add_element('gps', LabeledValue(color=BLACK, label='GPS', value='-', position=(ui.width() / 2 - 40, 0), label_font=fonts.Bold, text_font=fonts.Medium))
|
||||
|
||||
def on_unload(self, ui):
|
||||
self.cleanup()
|
||||
|
||||
with ui._lock:
|
||||
ui.remove_element('gps')
|
||||
|
||||
def on_ui_update(self, ui):
|
||||
ui.set('gps', self.get_status())
|
Reference in New Issue
Block a user