pwnagotchi on 64bit Raspberry Pi OS with Pytorch instead of Tensorflow

🧩 Syntax:
# Pi02w, DrS nexmon, stable_baselines3, pwnagotchi

# Install Raspberry Pi OS LITE 64-bit
#   Installed 64-bit Raspberry pi OS LITE from Raspberry Pi Imager menu
#
#
# Linux 6.1.21-v8+ #1642 SMP PREEMPT Mon Apr  3 17:24:16 BST 2023 aarch64 GNU/Linux
#

# not sure if these holds are strictly necessary, but OK for now
# until nexmon works on newer kernels
#
sudo apt-mark hold raspberrypi-kernel
sudo apt install raspberrypi-kernel-headers
sudo apt-mark hold raspberrypi-kernel-headers
sudo apt -y update
sudo apt -y upgrade
# on pizeroW:
# real	10m22.326s
# user	4m47.330s
# sys	2m49.348s



# follow this to have enough swap to build bettercap
# https://qengineering.eu/install-64-os-on-raspberry-pi-zero-2.html
#
# Not removing the old swap.. just add the big one for now....
#sudo /etc/init.d/dphys-swapfile stop
#sudo apt-get -y remove --purge dphys-swapfile
#sudo rm /var/swap
sudo fallocate -l 4G /var/swapfile
sudo chmod 600 /var/swapfile
sudo mkswap /var/swapfile
# start the swap service
sudo swapon /var/swapfile
# make the service permantent
sudo bash -c 'cat >> /etc/fstab' <<EOF
# add the following line to fstab
/var/swapfile   swap    swap     defaults   0       0
EOF
swapon -s


#
# pwngrid does some things that libpcap0.8_1.10.x won't do, so downgrade to
#    libpcap0.8_1.9.x
#
# Ubuntu debs, but they seem to work.
#
mkdir ~/Downloads
cd ~/Downloads
sudo apt install tcpdump  # before downgrading libpcap. it won't install after
                          # but seems to still work
                          # * may only need downgraded libpcap-dev package 
if [ $(uname -m) = "aarch64" ]; then
  export PKGARCH=arm64
elif [ $(uname -m) = "armv6l" ]; then
  export PKGARCH=armel
else # armv7l or armhf, probably
  export PKGARCH=armhf
fi

# skip this on armv6
if [ $PKGARCH != "armel" ]; then
  wget http://ports.ubuntu.com/pool/main/libp/libpcap/libpcap0.8-dev_1.9.1-3_$PKGARCH.deb
  wget http://ports.ubuntu.com/pool/main/libp/libpcap/libpcap-dev_1.9.1-3_$PKGARCH.deb
  wget http://ports.ubuntu.com/pool/main/libp/libpcap/libpcap0.8_1.9.1-3_$PKGARCH.deb
  sudo apt -y install ./libpcap*.deb  --allow-downgrades --allow-change-held-packages
  sudo apt-mark hold libpcap-dev libpcap0.8 libpcap0.8-dev
fi
# libpcap downloads for "armel" do not seem to exist at that site, so build it instead
# after the next apt stuff, since we need a lot of that to build with


# pwnagotchi, bettercap, dependancies from orangepi pastebin
cat > /tmp/dependencies << EOF
time
rsync
vim
wget
screen
git
build-essential
dkms
python3-pip  
python3-smbus
unzip
gawk
flex
bison
libopenmpi-dev
libatlas-base-dev
libelf-dev
libopenjp2-7
libtiff5
tcpdump
lsof
libgstreamer1.0-0
libavcodec58
libavformat58
libswscale5
libusb-1.0-0-dev
libnetfilter-queue-dev
libopenmpi3
dphys-swapfile
libdbus-1-dev 
libdbus-glib-1-dev
liblapack-dev 
libhdf5-dev 
libc-ares-dev 
libeigen3-dev
fonts-dejavu
fonts-dejavu-core
fonts-dejavu-extra
python3-pil
python3-smbus
libfuse-dev
libatlas-base-dev 
libopenblas-dev 
libblas-dev
bc
libgl1-mesa-glx
libncursesw5-dev 
libssl-dev 
libsqlite3-dev 
tk-dev 
libgdbm-dev 
libc6-dev 
libbz2-dev 
libffi-dev 
zlib1g-dev
fonts-freefont-ttf
fbi
python3-flask
python3-flask-cors
python3-flaskext.wtf
EOF

# load them in groups of 5. quicker than individual, but doesn't fail all of them
# if one goes wrong.

cat /tmp/dependencies | xargs -n5 sudo apt install -y

# now build libpcap, if PizeroW
if [ $(uname -m) = "armv6l" ]; then
  cd ~/git
  git clone https://github.com/the-tcpdump-group/libpcap.git
  cd libpcap
  git checkout libpcap-1.9
  sudo apt install flex bison
  ./configure
  make
  sudo make install
  sudo apt-mark hold libpcap-dev libpcap0.8 libpcap0.8-dev
fi

# RNDIS
#
# Skip RNDIS on pi3, pi4, others when you will use ethernet instead of
#  usb/otg/RNDIS to connect to a desktop computer
#
# Set up RNDIS (/boot/config.txt, cmdline.txt mods
#
# use_eem=0 lets RNDIS work properly with Macs
# host_addr= and dev_adder= make it show up as the same MAC every time
# instead of being a "brand new RNDIS device that needs to be configured
# on the host" every reboot
#
# if you modify the MACs in those lines, and change the IP range, you could
# have multiple RNDIS pwnies connected to the same host at the same time
#
# like change the host and dev macs on a second pwny, configure the host side
# of "Ethernet 12" or whatever it is as 10.69.0.1 instead of 10.0.0.1, and change
# /etc/network/interfaces.d/usb0-cfg on the pwny like:
#
# allow-hotplug usb0
#  iface usb0 inet static
#   address 10.69.0.2
#   netmask 255.255.255.0
#   network 10.69.0.0
#   broadcast 10.0.0.255
#   gateway 10.69.0.1
#
# and then (in theory) the computer should see one pwny on 10.0.0.2 and the
# other on 10.69.0.2. They might not be able to see each other
#
sudo sh -c "echo 'dtoverlay=dwc2' >>/boot/config.txt"
sudo sh -c "echo -n ' modules-load=dwc2,g_ether' >>/boot/cmdline.txt"
sudo sh -c "echo -n 'options g_ether use_eem=0 host_addr=f0:0d:ba:be:f0:0d dev_addr=58:70:77:6e:79:58' >/etc/modprobe.d/g_ether.conf"
sudo bash -c 'cat >> /etc/network/interfaces.d/usb0-cfg' <<EOF
allow-hotplug usb0
iface usb0 inet static
  address 10.0.0.2
  netmask 255.255.255.0
  network 10.0.0.0
  broadcast 10.0.0.255
  gateway 10.0.0.1
  metric 900
EOF


# Build Nexmon from Dr Schottky repository: https://github.com/DrSchottky/nexmon.git
#
# DrS version works with newer kernel versions and also support pi02w
#
# change "cd patches/bcm43436b0/9_88_4_65/nexmon/" to go into the
#   patches directory appropriate for your hardware
#
#
#
# required packages, and download nexmon
sudo apt -y install git libgmp3-dev gawk qpdf bison flex make autoconf libtool texinfo gcc-arm-none-eabi wl libfl-dev g++ xxd tcpdump

mkdir ~/git
cd ~/git
git clone --depth=1 https://github.com/DrSchottky/nexmon.git

#Build nexmon per DrS steps, as root because its easier because the "setup_env.sh"
cd nexmon
time sudo bash << EOS
source setup_env.sh
time make
# on Pi0w:
# real	6m40.961s
# user	4m5.222s
# sys	2m12.677s


# This picks a patch based on current system,
# by looking at what brcmfmac driver was loaded
# on boot.
#
# If you are building for a different system,
#  choose the appropirate dir instead of this
#  if..then
#
if dmesg | grep -q brcmfmac43430 ; then
  # Rpi0w, bananapim2zero
  cd patches/bcm43430a1/7_45_41_46/nexmon
elif dmesg | grep -q brcmfmac43455 ; then
  # Rpi3,4
  cd patches/bcm43455c0/7_45_206/nexmon
elif dmesg | grep -q brcmfmac43436 ; then
  # Rpi02w
  cd patches/bcm43436b0/9_88_4_65/nexmon
fi

# build and install the patched firmware
time make
# real	1m45.019s    on Pi02w
# user	2m23.316s
# sys	0m28.398s
# on Pi0w:
# real	8m13.289s
# user	7m4.773s
# sys	0m39.767s

# total time for build on pi0w:
# real	14m55.376s
# user	11m10.154s
# sys	2m52.652s

make backup-firmware
make install-firmware
EOS

#
# nexutil is OPTIONAL. Skip the next few lines if you don't want it

#
# patch the Makefile and ../libnexio/Makefile, adding β€œaarch64” to
#	the checks for armv6l and armv7l
#

patch utilities/nexutil/Makefile <<EOP
--- a/utilities/nexutil/Makefile
+++ b/utilities/nexutil/Makefile
@@ -1,13 +1,13 @@
 GIT_VERSION := \$(shell git describe --abbrev=4 --dirty --always --tags)
 USE_VENDOR_CMD := 0
 
-ifneq (\$(shell uname -m),\$(filter \$(shell uname -m), armv6l armv7l))
+ifneq (\$(shell uname -m),\$(filter \$(shell uname -m), armv6l aarch64 armv7l))
 all: libs/armeabi/nexutil
 else
 all: nexutil
 endif
 
-ifneq (\$(shell uname -m),\$(filter \$(shell uname -m), armv6l armv7l))
+ifneq (\$(shell uname -m),\$(filter \$(shell uname -m), armv6l aarch64 armv7l))
 libs/armeabi/nexutil: Android.mk nexutil.c ../libargp/local/armeabi/libargp.a ../libnexio/local/armeabi/libnexio.a FORCE
        touch nexutil.c
        \$(NDK_ROOT)/ndk-build NDK_APPLICATION_MK=`pwd`/Application.mk NDK_APP_OUT=. TARGET_PLATFORM=android-21 GIT_VERSION=\$(GIT_VERSION)
@@ -29,7 +29,7 @@ endif
 ../libnexio/libnexio.a: FORCE
        cd ../libnexio \&\& make
 
-ifneq (\$(shell uname -m),\$(filter \$(shell uname -m), armv6l armv7l))
+ifneq (\$(shell uname -m),\$(filter \$(shell uname -m), armv6l aarch64 armv7l))
 install: libs/armeabi/nexutil
        adb push \$< /sdcard/
        adb shell 'su -c "mount -o rw,remount /system"'
EOP

patch utilities/libnexio/Makefile <<EOP
--- a/utilities/libnexio/Makefile
+++ b/utilities/libnexio/Makefile
@@ -3,7 +3,7 @@ USE_VENDOR_CMD := 0
 
 all: libs/armeabi/libnexio.a
 
-ifneq (\$(shell uname -m),\$(filter \$(shell uname -m), armv6l armv7l))
+ifneq (\$(shell uname -m),\$(filter \$(shell uname -m), armv6l aarch64 armv7l))
 libs/armeabi/libnexio.a: Android.mk
        \$(NDK_ROOT)/ndk-build NDK_APPLICATION_MK=`pwd`/Application.mk NDK_APP_OUT=. TARGET_PLATFORM=android-21
 else
EOP

# patch patches/include/types.h to comment out (with β€œ//β€œ at front of line)
#	all 4 β€œ64” types, and size_t declarations
# patch generated from : diff --git a/patches/include/types.h b/patches/include/types.h

patch patches/include/types.h <<EOP
--- a/patches/include/types.h
+++ b/patches/include/types.h
@@ -11,19 +11,19 @@ typedef signed short int        int16_t;
 typedef unsigned short int      uint16_t;
 typedef signed int              int32_t;
 typedef unsigned int            uint32_t;
-typedef unsigned long long      uint64_t;
-typedef long long               int64_t;
+//typedef unsigned long long      uint64_t;
+//typedef long long               int64_t;
 typedef int8_t                  int8;
 typedef uint8_t                 uint8;
 typedef int16_t                 int16;
 typedef uint16_t                uint16;
 typedef int32_t                 int32;
 typedef uint32_t                uint32;
-typedef int64_t                 int64;
-typedef uint64_t                uint64;
+//typedef int64_t                 int64;
+//typedef uint64_t                uint64;
 typedef unsigned char           uchar_t;
 typedef uint32_t                wchar_t;
-typedef uint32_t                size_t;
+//typedef uint32_t                size_t;
 typedef uint32_t                addr_t;
 typedef int32_t                 pid_t;
 typedef uint32_t                uint;
EOP

# run inside another shell so the setup_env.sh doesn't mess up the bettercap build
bash <<EOS
source setup_env.sh
cd utilities/nexutil
make
sudo make install
nexutil -V # to see if it works and the firmware was loaded
EOS

# install .ko.xz so it will load on reboot
#
cd /lib/modules/$(uname -r)/kernel/drivers/net/wireless/broadcom/brcm80211/brcmfmac
sudo cp ~pi/git/nexmon/patches/driver/brcmfmac_$(uname -r | cut -d. -f 1-2).y-nexmon/brcmfmac.ko brcmfmac.ko.NEXMON
if [ -f brcmfmac.ko.xz ]; then
  sudo mv brcmfmac.ko.xz brcmfmac.ko.xz.ORIG
  sudo mv brcmfmac.ko.NEXMON brcmfmac.ko 
  sudo xz brcmfmac.ko
elif [ -f brcmfmac.ko ]; then
  sudo mv brcmfmac.ko brcmfmac.ko.ORIG
  sudo mv brcmfmac.ko.NEXMON brcmfmac.ko 
fi

# wpasupplicant seems to cause mon0 problems
# as an alternative to dumping wpasupplicant, try hiding the interface from it
if [ -f /etc/dhcpcd.conf ] ; then 
  echo denyinterfaces wlan0 | tee -a /etc/dhcpcd.conf
else
  sudo apt -y remove wpasupplicant
fi


sudo depmod -a
# sudo shutdown -r now # Reboot if you want to see that nexmon works after reboot


# Install Go and Bettercap
#

# Go, golang
#
# you could also try "sudo apt install golang", but that might be
# an older version
mkdir -p ~/Downloads
cd ~/Downloads
if [ $(uname -m) = "aarch64" ]; then
  export PKGARCH=arm64
elif [ $(uname -m) = "armv6l" ]; then
  export PKGARCH=armv6l
else
  # go does not have a downloadble package for armv7
  PKGARCH="NONE"
fi

if [ $PKGARCH != "NONE" ]; then
  wget https://go.dev/dl/go1.21.1.linux-$PKGARCH.tar.gz
  sudo rm -rf /usr/local/go && sudo tar -C /usr/local -xzf go1.20.5.linux-$PKGARCH.tar.gz
  echo export PATH=\$PATH:/usr/local/go/bin >>~/.bashrc
  source ~/.bashrc
else
  # try apt
  sudo apt install golang
fi

go version     # see that it installed

# Bettercap
#
# build latest source instead of a release
cd ~/git
git clone https://github.com/bettercap/bettercap.git
cd bettercap
time make build
# real	3m44.282s
# user	4m57.521s
# sys	1m6.948s
sudo mv bettercap /usr/bin

## install the caplets and the web ui in /usr/local/share/bettercap and quit
sudo bettercap -eval "caplets.update; ui.update; quit" -iface wlan0 || exit

# moved monitor mode check to after bettercap install. for some reason bettercap
# now needs an UP interface specified or it won't install caplets.
# so do the mon0 stuff after bettercap updates stuff, and use wlan0
#
# If bettercap dies saying "Interface Not Up" change the interface above to
# one that is up. Use mon0 after you run the few lines below....

# copy manu caplet over auto, so bettercap http UI is always available
sudo cp /usr/local/share/bettercap/caplets/pwnagotchi-manual.cap /usr/local/share/bettercap/caplets/pwnagotchi-auto.cap 



# check monitor mode
nexutil -V    # shows installed version, or errors if default system driver
sudo iw phy `iw dev wlan0 info | gawk '/wiphy/ {printf "phy" $2}'` interface add mon0 type monitor
sudo ifconfig wlan0 down && sudo ifconfig mon0 up
sudo tcpdump -i mon0           # see if it sees stuff, ^C to stop it



# PWNGrid
#
# depends on libpcap versions installed above
#
# if built from scratch, pwngrid needs to be run with environment variable set
#.   LD_PRELOAD=/usr/local/lib/libpcap.so
#
if [ $(uname -m) = β€œaarch64” ]; then
  export PWNGRID_PKG=pwngrid_linux_aarch64_v1.10.3.zip
else
  export PWNGRID_PKG=pwngrid_linux_armhf_v1.10.3.zip
fi


wget https://github.com/evilsocket/pwngrid/releases/download/v1.10.3/$PWNGRID_PKG
unzip $PWNGRID_PKG
sudo mv pwngrid /usr/bin/
## generate the keypair
time sudo pwngrid -generate -keys /etc/pwnagotchi
# on Pi0w:
# real    1m36.621s
# user    1m33.891s
# sys     0m1.042s

# And now for something completely different...

# STABLE-BASELINES3 and PYTORCH
#
cd ~
sudo apt install python3-pip      # just in case

### install torch 64-bit binary
#
# On Raspberry pi, pytorch and stable_baselines3 will
# install directly from default pip repository, and no longer
# need the separate install, but keeping it for historical
# reasons, or if you get an error at the requirements.txt step
#
###	sudo pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu
###	sudo pip install stable_baselines3       # no "[extra]"

# PWNAGOTCHI

###sudo raspi-config # enable SPI interface
# or run this... i think it kind of does the same thing
# armbian is different. use armbian-config
#
if ! grep "^dtparam=spi=on" /boot/config.txt; then
  echo "dtparam=spi=on" | sudo tee -a /boot/config.txt
  sudo dtparam spi=on        # enable it now, instead of a reboot. works on raspbian
fi

# clone your fave pwnagotchi repository
# I haven't checked in the changes yet to a branch in github, so
# just copy it, and apply the patches below. they probably work
# on other dists, too
cd ~/git
git clone https://github.com/Sniffleupagus/pwnagotchi-snflpgs.git
cd pwnagotchi-snflpgs


# more packages needed for torch
sudo apt install cpuinfo libsleef3

#
# requirements.txt for torch and stable-baselines3
#
# all of the versions are ">whatever" leftover version #s from
# the origial. The > should install the latest versions available
#
cat > requirements.txt << EOF
pycryptodome>=3.9.4
requests>=2.21.0
PyYAML>=5.3.1
scapy>=2.4.3
tweepy>=3.7.0
file-read-backwards>=2.0.0
inky>=1.2.0
smbus2>=0.3.0
Pillow>=5.4.1
spidev>=3.4
gast>=0.2.2
flask>=1.0.2
flask-cors>=3.0.7
flask-wtf>=0.14.3
dbus-python>=1.2.12
toml>=0.10.0
python-dateutil>=2.8.1
websockets>=8.1
gym
shimmy>=0.2.1
jinja2==2.11.3
MarkupSafe==1.1.1
EOF


# handful of dependencies
# try installing apt-versions first
for i in $(grep -v ^# requirements.txt | cut -d \> -f 1); do sudo apt -y install python3-$i; done

sudo apt -y install python3-yaml python3-pil python3-flaskext.wtf python3-dbus python3-dateutil python3-pandas python3-matplotlib python3-cloudpickle python3-networkx python3-sympy python3-requests

# add torch and stable-baselines3 to requirements after the apt updating to skip the old versions
cat >>requirements.txt << EOF
torch>=2.0.1
torchvision>=0.15.2
stable-baselines3==1.8.0
EOF

# then the pip versions
sudo pip install -r requirements.txt

# upgrade numpy to a later version than apt installs
pip install --upgrade numpy

sudo ln -s `pwd`/bin/pwnagotchi /usr/local/bin
sudo ln -s `pwd`/pwnagotchi /usr/local/lib/python3.9/dist-packages/pwnagotchi
sudo mkdir -p /usr/local/share/pwnagotchi/custom-plugins

# set up /etc/pwnagotchi/config.toml
#
# probably change the name, web.username, web.password
#
# led off, because the Led device isn't /sys/class/leds/led0, its the longer name
# personality.deauth = false, because injection crashes nexmon driver
#
sudo bash -c 'cat > /etc/pwnagotchi/config.toml' << EOF
main.name = "new_ai_CHANGEME"
main.custom_plugins = "/usr/local/share/pwnagotchi/custom-plugins"

main.plugins.led.enabled = false

personality.deauth = false

ui.display.enabled = false
ui.web.username = "pwny"
ui.web.password = "pwny1234"
EOF

# this patches default.toml to remove ai.params that are no longer used
# - alpha,epsilon and lr_schedule not used in the MlpPolicy
#	they are leftover from MlpLstmPolicy
#
# deuath and advertise disabled to trigger less nexmon bugs
#
# generated by:
#   diff --git a/pwnagotchi/defaults.toml b/pwnagotchi/defaults.toml

patch pwnagotchi/defaults.toml <<EOP
--- a/pwnagotchi/defaults.toml
+++ b/pwnagotchi/defaults.toml
@@ -137,10 +137,7 @@ ai.params.vf_coef = 0.25
 ai.params.ent_coef = 0.01
 ai.params.max_grad_norm = 0.5
 ai.params.learning_rate = 0.001
-ai.params.alpha = 0.99
-ai.params.epsilon = 0.00001
 ai.params.verbose = 1
-ai.params.lr_schedule = "constant"
 
 personality.advertise = false
 personality.deauth = false
EOP

# Patch pwnagotchi/ai/__init__.py to load stable_baselines3
#	instead of old stable_baselines. New one mostly drops
#	right in, except the A2C policy had to be changed.
#	I do not understand how the AI works, and do not understand
#	how this changes how it works. It builds and seems to work
# generated from:
#   diff --git a/pwnagotchi/ai/__init__.py b/pwnagotchi/ai/__init__.py

patch pwnagotchi/ai/__init__.py <<EOP
--- a/pwnagotchi/ai/__init__.py
+++ b/pwnagotchi/ai/__init__.py
@@ -18,15 +18,15 @@ def load(config, agent, epoch, from_disk=True):
         logging.info("[ai] bootstrapping dependencies ...")
 
         start = time.time()
-        from stable_baselines import A2C
+        from stable_baselines3 import A2C
         logging.debug("[ai] A2C imported in %.2fs" % (time.time() - start))
 
         start = time.time()
-        from stable_baselines.common.policies import MlpLstmPolicy
-        logging.debug("[ai] MlpLstmPolicy imported in %.2fs" % (time.time() - start))
+        from stable_baselines3.a2c import MlpPolicy
+        logging.debug("[ai] MlpPolicy imported in %.2fs" % (time.time() - start))
 
         start = time.time()
-        from stable_baselines.common.vec_env import DummyVecEnv
+        from stable_baselines3.common.vec_env import DummyVecEnv
         logging.debug("[ai] DummyVecEnv imported in %.2fs" % (time.time() - start))
 
         start = time.time()
@@ -39,7 +39,7 @@ def load(config, agent, epoch, from_disk=True):
         logging.info("[ai] creating model ...")
 
         start = time.time()
-        a2c = A2C(MlpLstmPolicy, env, **config['params'])
+        a2c = A2C(MlpPolicy, env, **config['params'])
         logging.debug("[ai] A2C created in %.2fs" % (time.time() - start))
 
         if from_disk and os.path.exists(config['path']):
EOP
################# end of diffs for __init__.py


#### Patch ai/gym.py to change cutoff from 140 to 150
####	this fixes the "shape (0,428) into shape (1,503)" error
####	Again, no clue how the AI works. This is more of a
####	workaround than a fix. But it seems to work.
# generated by:
#   diff --git a/pwnagotchi/ai/gym.py b/pwnagotchi/ai/gym.py

patch pwnagotchi/ai/gym.py <<EOP
--- a/pwnagotchi/ai/gym.py
+++ b/pwnagotchi/ai/gym.py
@@ -36,7 +36,7 @@ class Environment(gym.Env):
 
         # see https://github.com/evilsocket/pwnagotchi/issues/583
         self._supported_channels = agent.supported_channels()
-        self._extended_spectrum = any(ch > 140 for ch in self._supported_channels)
+        self._extended_spectrum = any(ch > 150 for ch in self._supported_channels)
         self._histogram_size, self._observation_shape = featurizer.describe(self._extended_spectrum)
 
         Environment.params += [
EOP
########## end edits for gym.py #############
         
# system configuration from pwnagotchi sourcecode builder/data directory
#
# Install all of the support scripts into
#	/usr/bin
#	/etc/systemd/system
#	/etc/network/interfaces.d
#
# and probably others...
#
for file in `find builder/data -type f`; do
  dest=${file#builder/data}
  if [ -s $dest ]; then
    echo File $dest exists. Skipping
  else
    echo Copying $file to $dest
    sudo cp -p $file $dest
  fi
done

# enable all of the services
sudo systemctl enable bettercap
sudo systemctl enable pwngrid-peer
sudo systemctl enable pwnagotchi

echo hold onto your butts
sudo sync
sudo reboot
Sniffleupagus

Sniffleupagus

Member