mirror of
https://github.com/jayofelony/pwnagotchi.git
synced 2025-07-01 18:37:27 -04:00
Compare commits
2 Commits
v2.9.2
...
wpa-2-patc
Author | SHA1 | Date | |
---|---|---|---|
744a50aa67 | |||
eb6e229d36 |
63
.github/workflows/publish.yml
vendored
63
.github/workflows/publish.yml
vendored
@ -2,54 +2,71 @@ name: Publish
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
version:
|
||||||
|
description: 'Version number'
|
||||||
|
required: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
name: ${{ matrix.name }}
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- name: "Raspberry Pi 32-bit"
|
|
||||||
id: "32bit"
|
|
||||||
- name: "Raspberry Pi 64-bit"
|
|
||||||
id: "64bit"
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
with:
|
|
||||||
path: publish/build
|
|
||||||
|
|
||||||
- name: Extract version from file
|
- name: Extract version from file
|
||||||
id: get_version
|
id: get_version
|
||||||
run: |
|
run: |
|
||||||
VERSION=$(cut -d "'" -f2 < publish/build/pwnagotchi/_version.py)
|
VERSION=$(cut -d "'" -f2 < pwnagotchi/_version.py)
|
||||||
echo "VERSION=$VERSION" >> $GITHUB_ENV
|
echo "VERSION=$VERSION" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Get latest tag
|
||||||
|
uses: actions-ecosystem/action-get-latest-tag@v1
|
||||||
|
id: get-latest-tag
|
||||||
|
|
||||||
|
- name: Set LAST_VERSION as an environment variable
|
||||||
|
run: echo "LAST_VERSION=${{ steps.get-latest-tag.outputs.tag }}" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Generate release notes
|
||||||
|
id: generate_release_notes
|
||||||
|
run: |
|
||||||
|
COMMITS=$(git log --merges --pretty=format:"* %s" $LAST_VERSION--$VERSION | sed 's/$/\\n/g')
|
||||||
|
CONTRIBUTORS=$(git shortlog -sn $LAST_VERSION--$VERSION | awk '{print "* @" $2}' | sed 's/$/\\n/g')
|
||||||
|
RELEASE_BODY="**Full Changelog**: https://github.com/jayofelony/pwnagotchi/compare/$LAST_VERSION...$VERSION"
|
||||||
|
echo "RELEASE_BODY=$RELEASE_BODY" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Install qemu dependencies
|
- name: Install qemu dependencies
|
||||||
run: sudo apt update && sudo apt install qemu-user-static qemu-utils xz-utils -y
|
run: sudo apt update && sudo apt install qemu-user-static qemu-utils xz-utils -y
|
||||||
|
|
||||||
- name: Build ${{ matrix.name }} img file
|
- name: Build img file
|
||||||
run: cd publish/build; ls -la .; pwd; make packer; make ${{ matrix.id }}
|
run: ls -la .; pwd; make all
|
||||||
|
|
||||||
- name: Change name of .img.xz to add version
|
- name: Transfer 32bit.img to docker and give permissions
|
||||||
run: |
|
run: sudo chown runner:docker "pwnagotchi-32bit.img"
|
||||||
sudo chown runner:docker "pwnagotchi-${{ matrix.id }}.img"
|
|
||||||
mv "pwnagotchi-${{ matrix.id }}.img" "pwnagotchi-${{ env.VERSION }}-${{ matrix.id }}.img"
|
- name: Transfer 64bit.img to docker and give permissions
|
||||||
|
run: sudo chown runner:docker "pwnagotchi-64bit.img"
|
||||||
|
|
||||||
- name: PiShrink
|
- name: PiShrink
|
||||||
run: |
|
run: |
|
||||||
wget https://raw.githubusercontent.com/Drewsif/PiShrink/master/pishrink.sh
|
wget https://raw.githubusercontent.com/Drewsif/PiShrink/master/pishrink.sh
|
||||||
chmod +x pishrink.sh
|
chmod +x pishrink.sh
|
||||||
sudo mv pishrink.sh /usr/local/bin
|
sudo mv pishrink.sh /usr/local/bin
|
||||||
sudo pishrink.sh -aZ "pwnagotchi-${{ env.VERSION }}-${{ matrix.id }}.img"
|
find /home/runner/work/ -type f -name "*.img" -exec sudo pishrink.sh -aZ {} \;
|
||||||
|
|
||||||
|
- name: Change name of 32.img.xz to add version
|
||||||
|
run: mv "pwnagotchi-32bit.img.xz" "pwnagotchi-$VERSION-32bit.img.xz"
|
||||||
|
|
||||||
|
- name: Change name of 64.img.xz to add version
|
||||||
|
run: mv "pwnagotchi-64bit.img.xz" "pwnagotchi-$VERSION-64bit.img.xz"
|
||||||
|
|
||||||
- name: Release
|
- name: Release
|
||||||
uses: softprops/action-gh-release@v2
|
uses: softprops/action-gh-release@v1
|
||||||
with:
|
with:
|
||||||
prerelease: false
|
prerelease: true
|
||||||
make_latest: true
|
|
||||||
tag_name: v${{ env.VERSION }}
|
tag_name: v${{ env.VERSION }}
|
||||||
name: Pwnagotchi v${{ env.VERSION }}
|
name: Pwnagotchi v${{ env.VERSION }}
|
||||||
files: pwnagotchi-${{ env.VERSION }}-${{ matrix.id }}.img.xz
|
files: |
|
||||||
generate_release_notes: true
|
pwnagotchi-${{ env.VERSION }}-32bit.img.xz
|
||||||
|
pwnagotchi-${{ env.VERSION }}-64bit.img.xz
|
||||||
|
body: ${{ env.RELEASE_BODY }}
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,2 +0,0 @@
|
|||||||
|
|
||||||
*.pyc
|
|
19
.idea/deployment.xml
generated
19
.idea/deployment.xml
generated
@ -1,6 +1,23 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="PublishConfigData" filePermissions="493" folderPermissions="493" remoteFilesAllowedToDisappearOnAutoupload="false" confirmBeforeUploading="false">
|
<component name="PublishConfigData" serverName="pwnagotchi" filePermissions="493" folderPermissions="493" remoteFilesAllowedToDisappearOnAutoupload="false" confirmBeforeUploading="false">
|
||||||
<option name="confirmBeforeUploading" value="false" />
|
<option name="confirmBeforeUploading" value="false" />
|
||||||
|
<serverData>
|
||||||
|
<paths name="pwnagotchi">
|
||||||
|
<serverdata>
|
||||||
|
<mappings>
|
||||||
|
<mapping deploy="/usr/local/lib/python3.11/dist-packages/pwnagotchi" local="$PROJECT_DIR$/pwnagotchi" web="/" />
|
||||||
|
<mapping deploy="/usr/local/bin" local="$PROJECT_DIR$/bin" />
|
||||||
|
<mapping local="" />
|
||||||
|
</mappings>
|
||||||
|
<excludedPaths>
|
||||||
|
<excludedPath local="true" path="$PROJECT_DIR$/venv" />
|
||||||
|
<excludedPath local="true" path="$PROJECT_DIR$/pwnagotchi.egg-info" />
|
||||||
|
<excludedPath local="true" path="$PROJECT_DIR$/dist" />
|
||||||
|
<excludedPath local="true" path="$PROJECT_DIR$/builder/packer-builder-arm" />
|
||||||
|
</excludedPaths>
|
||||||
|
</serverdata>
|
||||||
|
</paths>
|
||||||
|
</serverData>
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
2
.idea/misc.xml
generated
2
.idea/misc.xml
generated
@ -3,7 +3,7 @@
|
|||||||
<component name="Black">
|
<component name="Black">
|
||||||
<option name="sdkName" value="Python 3.11 (pwnagotchi)" />
|
<option name="sdkName" value="Python 3.11 (pwnagotchi)" />
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.12 (pwnagotchi)" project-jdk-type="Python SDK" />
|
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.10" project-jdk-type="Python SDK" />
|
||||||
<component name="PythonCompatibilityInspectionAdvertiser">
|
<component name="PythonCompatibilityInspectionAdvertiser">
|
||||||
<option name="version" value="3" />
|
<option name="version" value="3" />
|
||||||
</component>
|
</component>
|
||||||
|
2
.idea/pwnagotchi.iml
generated
2
.idea/pwnagotchi.iml
generated
@ -4,7 +4,7 @@
|
|||||||
<content url="file://$MODULE_DIR$">
|
<content url="file://$MODULE_DIR$">
|
||||||
<excludeFolder url="file://$MODULE_DIR$/venv" />
|
<excludeFolder url="file://$MODULE_DIR$/venv" />
|
||||||
</content>
|
</content>
|
||||||
<orderEntry type="jdk" jdkName="Python 3.12 (pwnagotchi)" jdkType="Python SDK" />
|
<orderEntry type="jdk" jdkName="Python 3.10" jdkType="Python SDK" />
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
</component>
|
</component>
|
||||||
<component name="PyDocumentationSettings">
|
<component name="PyDocumentationSettings">
|
||||||
|
10
.idea/sshConfigs.xml
generated
10
.idea/sshConfigs.xml
generated
@ -1,10 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="SshConfigs">
|
|
||||||
<configs>
|
|
||||||
<sshConfig authType="PASSWORD" connectionConfig="{"serverAliveInterval":300}" id="8b69df7d-cec5-421f-8edf-53ed6233f6b6" port="22" customName="pwnagotchi" nameFormat="CUSTOM" useOpenSSHConfig="true">
|
|
||||||
<option name="customName" value="pwnagotchi" />
|
|
||||||
</sshConfig>
|
|
||||||
</configs>
|
|
||||||
</component>
|
|
||||||
</project>
|
|
14
.idea/webServers.xml
generated
14
.idea/webServers.xml
generated
@ -1,14 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="WebServers">
|
|
||||||
<option name="servers">
|
|
||||||
<webServer id="cf2a1148-a103-4472-a782-7debdc6dabf9" name="pwnagotchi">
|
|
||||||
<fileTransfer accessType="SFTP" port="22" sshConfigId="8b69df7d-cec5-421f-8edf-53ed6233f6b6" sshConfig="pwnagotchi">
|
|
||||||
<advancedOptions>
|
|
||||||
<advancedOptions dataProtectionLevel="Private" passiveMode="true" shareSSLContext="true" />
|
|
||||||
</advancedOptions>
|
|
||||||
</fileTransfer>
|
|
||||||
</webServer>
|
|
||||||
</option>
|
|
||||||
</component>
|
|
||||||
</project>
|
|
24
Makefile
24
Makefile
@ -1,4 +1,4 @@
|
|||||||
PACKER_VERSION := 1.11.0
|
PACKER_VERSION := 1.10.1
|
||||||
PWN_HOSTNAME := pwnagotchi
|
PWN_HOSTNAME := pwnagotchi
|
||||||
PWN_VERSION := $(shell cut -d"'" -f2 < pwnagotchi/_version.py)
|
PWN_VERSION := $(shell cut -d"'" -f2 < pwnagotchi/_version.py)
|
||||||
|
|
||||||
@ -26,7 +26,7 @@ UNSHARE := $(UNSHARE) --uts
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
# sudo apt-get install qemu-user-static qemu-utils
|
# sudo apt-get install qemu-user-static qemu-utils
|
||||||
all: packer image
|
all: clean packer image
|
||||||
|
|
||||||
update_langs:
|
update_langs:
|
||||||
@for lang in pwnagotchi/locale/*/; do\
|
@for lang in pwnagotchi/locale/*/; do\
|
||||||
@ -40,23 +40,21 @@ compile_langs:
|
|||||||
./scripts/language.sh compile $$(basename $$lang); \
|
./scripts/language.sh compile $$(basename $$lang); \
|
||||||
done
|
done
|
||||||
|
|
||||||
packer:
|
packer: clean
|
||||||
curl https://releases.hashicorp.com/packer/$(PACKER_VERSION)/packer_$(PACKER_VERSION)_linux_amd64.zip -o /tmp/packer.zip
|
curl https://releases.hashicorp.com/packer/$(PACKER_VERSION)/packer_$(PACKER_VERSION)_linux_amd64.zip -o /tmp/packer.zip
|
||||||
unzip -o /tmp/packer.zip -d /tmp
|
unzip /tmp/packer.zip -d /tmp
|
||||||
sudo mv /tmp/packer /usr/bin/packer
|
sudo mv /tmp/packer /usr/bin/packer
|
||||||
|
|
||||||
image: packer
|
image: clean packer
|
||||||
export LC_ALL=en_GB.UTF-8
|
|
||||||
cd builder && sudo /usr/bin/packer init combined.json.pkr.hcl && sudo $(UNSHARE) /usr/bin/packer build -var "pwn_hostname=$(PWN_HOSTNAME)" -var "pwn_version=$(PWN_VERSION)" combined.json.pkr.hcl
|
cd builder && sudo /usr/bin/packer init combined.json.pkr.hcl && sudo $(UNSHARE) /usr/bin/packer build -var "pwn_hostname=$(PWN_HOSTNAME)" -var "pwn_version=$(PWN_VERSION)" combined.json.pkr.hcl
|
||||||
|
|
||||||
32bit: packer
|
bullseye: clean packer
|
||||||
export LC_ALL=en_GB.UTF-8
|
cd builder && sudo /usr/bin/packer init data/32bit/raspberrypi32.json.pkr.hcl && sudo $(UNSHARE) /usr/bin/packer build -var "pwn_hostname=$(PWN_HOSTNAME)" -var "pwn_version=$(PWN_VERSION)" data/32bit/raspberrypi32.json.pkr.hcl
|
||||||
cd builder && sudo /usr/bin/packer init raspberrypi32.json.pkr.hcl && sudo $(UNSHARE) /usr/bin/packer build -var "pwn_hostname=$(PWN_HOSTNAME)" -var "pwn_version=$(PWN_VERSION)" raspberrypi32.json.pkr.hcl
|
sudo pishrink -vaZ pwnagotchi-32bit.img
|
||||||
|
|
||||||
64bit: packer
|
bookworm: clean packer
|
||||||
export LC_ALL=en_GB.UTF-8
|
cd builder && sudo /usr/bin/packer init data/64bit/raspberrypi64.json.pkr.hcl && sudo $(UNSHARE) /usr/bin/packer build -var "pwn_hostname=$(PWN_HOSTNAME)" -var "pwn_version=$(PWN_VERSION)" data/64bit/raspberrypi64.json.pkr.hcl
|
||||||
cd builder && sudo /usr/bin/packer init raspberrypi64.json.pkr.hcl && sudo $(UNSHARE) /usr/bin/packer build -var "pwn_hostname=$(PWN_HOSTNAME)" -var "pwn_version=$(PWN_VERSION)" raspberrypi64.json.pkr.hcl
|
sudo pishrink -vaZ pwnagotchi-64bit.img
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
- rm -rf /tmp/packer*
|
- rm -rf /tmp/packer*
|
||||||
- rm -rf /tmp/LICENSE.txt
|
|
||||||
|
17
README.md
17
README.md
@ -3,15 +3,11 @@ This is the main source for all forks:
|
|||||||
- RPiZeroW (32bit)
|
- RPiZeroW (32bit)
|
||||||
- RPiZero2W, RPi3, RPi4, RPi5 (64bit)
|
- RPiZero2W, RPi3, RPi4, RPi5 (64bit)
|
||||||
|
|
||||||
**For installation docs check out the [wiki](https://github.com/jayofelony/pwnagotchi/wiki)!**
|
|
||||||
|
|
||||||
[GH Sponsor](https://github.com/sponsors/jayofelony)
|
[GH Sponsor](https://github.com/sponsors/jayofelony)
|
||||||
|
|
||||||
**Proudly partnering with [PiSugar](https://www.pisugar.com)!!**
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
[Pwnagotchi](https://pwnagotchi.org/) is an [A2C](https://hackernoon.com/intuitive-rl-intro-to-advantage-actor-critic-a2c-4ff545978752)-based "AI" leveraging [bettercap](https://www.bettercap.org/) that learns from its surrounding Wi-Fi environment to maximize the crackable WPA key material it captures (either passively, or by performing authentication and association attacks). This material is collected as PCAP files containing any form of handshake supported by [hashcat](https://hashcat.net/hashcat/), including [PMKIDs](https://www.evilsocket.net/2019/02/13/Pwning-WiFi-networks-with-bettercap-and-the-PMKID-client-less-attack/),
|
[Pwnagotchi](https://pwnagotchi.ai/) is an [A2C](https://hackernoon.com/intuitive-rl-intro-to-advantage-actor-critic-a2c-4ff545978752)-based "AI" leveraging [bettercap](https://www.bettercap.org/) that learns from its surrounding Wi-Fi environment to maximize the crackable WPA key material it captures (either passively, or by performing authentication and association attacks). This material is collected as PCAP files containing any form of handshake supported by [hashcat](https://hashcat.net/hashcat/), including [PMKIDs](https://www.evilsocket.net/2019/02/13/Pwning-WiFi-networks-with-bettercap-and-the-PMKID-client-less-attack/),
|
||||||
full and half WPA handshakes.
|
full and half WPA handshakes.
|
||||||
|
|
||||||

|

|
||||||
@ -26,17 +22,18 @@ Multiple units within close physical proximity can "talk" to each other, adverti
|
|||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
https://github.com/jayofelony/pwnagotchi/wiki
|
https://www.pwnagotchi.ai
|
||||||
https://www.pwnagotchi.org
|
|
||||||
|
|
||||||
## Links
|
## Links
|
||||||
|
|
||||||
| | Official Links |
|
| | Official Links |
|
||||||
|-----------|-------------------------------------------------------------|
|
|-----------|-------------------------------------------------------------|
|
||||||
| Website | [pwnagotchi.org](https://pwnagotchi.org/) |
|
| Website | [pwnagotchi.ai](https://pwnagotchi.ai/) |
|
||||||
| Forum | [discord.gg](https://discord.gg/PGgnzFbz4M) |
|
| Forum | [community.pwnagotchi.ai](https://community.pwnagotchi.ai/) |
|
||||||
|
| Slack | [pwnagotchi.slack.com](https://invite.pwnagotchi.ai/) |
|
||||||
| Subreddit | [r/pwnagotchi](https://www.reddit.com/r/pwnagotchi/) |
|
| Subreddit | [r/pwnagotchi](https://www.reddit.com/r/pwnagotchi/) |
|
||||||
|
| Twitter | [@pwnagotchi](https://twitter.com/pwnagotchi) |
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
`pwnagotchi` created by [@evilsocket](https://twitter.com/evilsocket) and updated by [us](https://github.com/jayofelony/pwnagotchi/graphs/contributors). It is released under the GPL3 license.
|
`pwnagotchi` is made with ♥ by [@evilsocket](https://twitter.com/evilsocket) and the [amazing dev team](https://github.com/evilsocket/pwnagotchi/graphs/contributors). It is released under the GPL3 license.
|
||||||
|
258
bin/pwnagotchi
258
bin/pwnagotchi
@ -7,7 +7,6 @@ import sys
|
|||||||
import toml
|
import toml
|
||||||
import requests
|
import requests
|
||||||
import os
|
import os
|
||||||
import re
|
|
||||||
|
|
||||||
import pwnagotchi
|
import pwnagotchi
|
||||||
from pwnagotchi import utils
|
from pwnagotchi import utils
|
||||||
@ -19,83 +18,85 @@ from pwnagotchi import fs
|
|||||||
from pwnagotchi.utils import DottedTomlEncoder, parse_version as version_to_tuple
|
from pwnagotchi.utils import DottedTomlEncoder, parse_version as version_to_tuple
|
||||||
|
|
||||||
|
|
||||||
def pwnagotchi_cli():
|
def do_clear(display):
|
||||||
def do_clear(display):
|
logging.info("clearing the display ...")
|
||||||
logging.info("clearing the display ...")
|
display.clear()
|
||||||
display.clear()
|
sys.exit(0)
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
def do_manual_mode(agent):
|
|
||||||
logging.info("entering manual mode ...")
|
|
||||||
|
|
||||||
agent.mode = 'manual'
|
def do_manual_mode(agent):
|
||||||
agent.last_session.parse(agent.view(), args.skip_session)
|
logging.info("entering manual mode ...")
|
||||||
if not args.skip_session:
|
|
||||||
logging.info(
|
agent.mode = 'manual'
|
||||||
"the last session lasted %s (%d completed epochs, trained for %d), average reward:%s (min:%s max:%s)" % (
|
agent.last_session.parse(agent.view(), args.skip_session)
|
||||||
agent.last_session.duration_human,
|
if not args.skip_session:
|
||||||
agent.last_session.epochs,
|
logging.info(
|
||||||
agent.last_session.train_epochs,
|
"the last session lasted %s (%d completed epochs, trained for %d), average reward:%s (min:%s max:%s)" % (
|
||||||
agent.last_session.avg_reward,
|
agent.last_session.duration_human,
|
||||||
agent.last_session.min_reward,
|
agent.last_session.epochs,
|
||||||
agent.last_session.max_reward))
|
agent.last_session.train_epochs,
|
||||||
|
agent.last_session.avg_reward,
|
||||||
|
agent.last_session.min_reward,
|
||||||
|
agent.last_session.max_reward))
|
||||||
|
|
||||||
|
while True:
|
||||||
|
display.on_manual_mode(agent.last_session)
|
||||||
|
time.sleep(5)
|
||||||
|
if grid.is_connected():
|
||||||
|
plugins.on('internet_available', agent)
|
||||||
|
|
||||||
|
|
||||||
|
def do_auto_mode(agent):
|
||||||
|
logging.info("entering auto mode ...")
|
||||||
|
|
||||||
|
agent.mode = 'auto'
|
||||||
|
agent.start()
|
||||||
|
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
# recon on all channels
|
||||||
|
agent.recon()
|
||||||
|
# get nearby access points grouped by channel
|
||||||
|
channels = agent.get_access_points_by_channel()
|
||||||
|
# for each channel
|
||||||
|
for ch, aps in channels:
|
||||||
|
agent.set_channel(ch)
|
||||||
|
|
||||||
|
if not agent.is_stale() and agent.any_activity():
|
||||||
|
logging.info("%d access points on channel %d" % (len(aps), ch))
|
||||||
|
|
||||||
|
# for each ap on this channel
|
||||||
|
for ap in aps:
|
||||||
|
# send an association frame in order to get for a PMKID
|
||||||
|
agent.associate(ap)
|
||||||
|
# deauth all client stations in order to get a full handshake
|
||||||
|
for sta in ap['clients']:
|
||||||
|
agent.deauth(ap, sta)
|
||||||
|
time.sleep(1) # delay to not trigger nexmon firmware bugs
|
||||||
|
|
||||||
|
# An interesting effect of this:
|
||||||
|
#
|
||||||
|
# From Pwnagotchi's perspective, the more new access points
|
||||||
|
# and / or client stations nearby, the longer one epoch of
|
||||||
|
# its relative time will take ... basically, in Pwnagotchi's universe,
|
||||||
|
# Wi-Fi electromagnetic fields affect time like gravitational fields
|
||||||
|
# affect ours ... neat ^_^
|
||||||
|
agent.next_epoch()
|
||||||
|
|
||||||
while True:
|
|
||||||
display.on_manual_mode(agent.last_session)
|
|
||||||
time.sleep(5)
|
|
||||||
if grid.is_connected():
|
if grid.is_connected():
|
||||||
plugins.on('internet_available', agent)
|
plugins.on('internet_available', agent)
|
||||||
|
|
||||||
def do_auto_mode(agent):
|
except Exception as e:
|
||||||
logging.info("entering auto mode ...")
|
if str(e).find("wifi.interface not set") > 0:
|
||||||
|
logging.exception("main loop exception due to unavailable wifi device, likely programmatically disabled (%s)", e)
|
||||||
agent.mode = 'auto'
|
logging.info("sleeping 60 seconds then advancing to next epoch to allow for cleanup code to trigger")
|
||||||
agent.start()
|
time.sleep(60)
|
||||||
|
|
||||||
while True:
|
|
||||||
try:
|
|
||||||
# recon on all channels
|
|
||||||
agent.recon()
|
|
||||||
# get nearby access points grouped by channel
|
|
||||||
channels = agent.get_access_points_by_channel()
|
|
||||||
# for each channel
|
|
||||||
for ch, aps in channels:
|
|
||||||
time.sleep(1)
|
|
||||||
agent.set_channel(ch)
|
|
||||||
|
|
||||||
if not agent.is_stale() and agent.any_activity():
|
|
||||||
logging.info("%d access points on channel %d" % (len(aps), ch))
|
|
||||||
|
|
||||||
# for each ap on this channel
|
|
||||||
for ap in aps:
|
|
||||||
# send an association frame in order to get for a PMKID
|
|
||||||
agent.associate(ap)
|
|
||||||
# deauth all client stations in order to get a full handshake
|
|
||||||
for sta in ap['clients']:
|
|
||||||
agent.deauth(ap, sta)
|
|
||||||
time.sleep(1) # delay to not trigger nexmon firmware bugs
|
|
||||||
|
|
||||||
# An interesting effect of this:
|
|
||||||
#
|
|
||||||
# From Pwnagotchi's perspective, the more new access points
|
|
||||||
# and / or client stations nearby, the longer one epoch of
|
|
||||||
# its relative time will take ... basically, in Pwnagotchi's universe,
|
|
||||||
# Wi-Fi electromagnetic fields affect time like gravitational fields
|
|
||||||
# affect ours ... neat ^_^
|
|
||||||
agent.next_epoch()
|
agent.next_epoch()
|
||||||
|
else:
|
||||||
|
logging.exception("main loop exception (%s)", e)
|
||||||
|
|
||||||
if grid.is_connected():
|
|
||||||
plugins.on('internet_available', agent)
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
if str(e).find("wifi.interface not set") > 0:
|
|
||||||
logging.exception("main loop exception due to unavailable wifi device, likely programmatically disabled (%s)", e)
|
|
||||||
logging.info("sleeping 60 seconds then advancing to next epoch to allow for cleanup code to trigger")
|
|
||||||
time.sleep(60)
|
|
||||||
agent.next_epoch()
|
|
||||||
else:
|
|
||||||
logging.exception("main loop exception (%s)", e)
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
def add_parsers(parser):
|
def add_parsers(parser):
|
||||||
"""
|
"""
|
||||||
Adds the plugins and google subcommands
|
Adds the plugins and google subcommands
|
||||||
@ -132,8 +133,6 @@ def pwnagotchi_cli():
|
|||||||
help="Print the configuration.")
|
help="Print the configuration.")
|
||||||
|
|
||||||
# Jayofelony added these
|
# Jayofelony added these
|
||||||
parser.add_argument('--wizard', dest="wizard", action="store_true", default=False,
|
|
||||||
help="Interactive installation of your personal configuration.")
|
|
||||||
parser.add_argument('--check-update', dest="check_update", action="store_true", default=False,
|
parser.add_argument('--check-update', dest="check_update", action="store_true", default=False,
|
||||||
help="Check for updates on Pwnagotchi. And tells current version.")
|
help="Check for updates on Pwnagotchi. And tells current version.")
|
||||||
parser.add_argument('--donate', dest="donate", action="store_true", default=False,
|
parser.add_argument('--donate', dest="donate", action="store_true", default=False,
|
||||||
@ -158,113 +157,9 @@ def pwnagotchi_cli():
|
|||||||
print(pwnagotchi.__version__)
|
print(pwnagotchi.__version__)
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
if args.wizard:
|
|
||||||
def is_valid_hostname(hostname):
|
|
||||||
if len(hostname) > 255:
|
|
||||||
return False
|
|
||||||
if hostname[-1] == ".":
|
|
||||||
hostname = hostname[:-1] # strip exactly one dot from the right, if present
|
|
||||||
allowed = re.compile("(?!-)[A-Z\d-]{1,63}(?<!-)$", re.IGNORECASE)
|
|
||||||
return all(allowed.match(x) for x in hostname.split("."))
|
|
||||||
|
|
||||||
pwn_restore = input("Do you want to restore the previous configuration?\n\n"
|
|
||||||
"[Y/N]: ")
|
|
||||||
if pwn_restore in ('y', 'yes'):
|
|
||||||
os.system("cp -f /etc/pwnagotchi/config.toml.bak /etc/pwnagotchi/config.toml")
|
|
||||||
print("Your previous configuration is restored, and I will restart in 5 seconds.")
|
|
||||||
time.sleep(5)
|
|
||||||
os.system("service pwnagotchi restart")
|
|
||||||
else:
|
|
||||||
pwn_check = input("This will create a new configuration file and overwrite your current backup, are you sure?\n\n"
|
|
||||||
"[Y/N]: ")
|
|
||||||
if pwn_check.lower() in ('y', 'yes'):
|
|
||||||
os.system("mv -f /etc/pwnagotchi/config.toml /etc/pwnagotchi/config.toml.bak")
|
|
||||||
with open("/etc/pwnagotchi/config.toml", "a+") as f:
|
|
||||||
f.write("# Do not edit this file if you do not know what you are doing!!!\n\n")
|
|
||||||
# Set pwnagotchi name
|
|
||||||
print("Welcome to the interactive installation of your personal Pwnagotchi configuration!\n"
|
|
||||||
"My name is Jayofelony, how may I call you?\n\n")
|
|
||||||
pwn_name = input("Pwnagotchi name (no spaces): ")
|
|
||||||
if pwn_name == "":
|
|
||||||
pwn_name = "Pwnagotchi"
|
|
||||||
print("I shall go by Pwnagotchi from now on!")
|
|
||||||
pwn_name = f"main.name = \"{pwn_name}\"\n"
|
|
||||||
f.write(pwn_name)
|
|
||||||
else:
|
|
||||||
if is_valid_hostname(pwn_name):
|
|
||||||
print(f"I shall go by {pwn_name} from now on!")
|
|
||||||
pwn_name = f"main.name = \"{pwn_name}\"\n"
|
|
||||||
f.write(pwn_name)
|
|
||||||
else:
|
|
||||||
print("You have chosen an invalid name. Please start over.")
|
|
||||||
exit()
|
|
||||||
pwn_whitelist = input("How many networks do you want to whitelist? "
|
|
||||||
"We will also ask a MAC for each network?\n"
|
|
||||||
"Each SSID and BSSID count as 1 network. \n\n"
|
|
||||||
"Be sure to use digits as your answer.\n\n"
|
|
||||||
"Amount of networks: ")
|
|
||||||
if int(pwn_whitelist) > 0:
|
|
||||||
f.write("main.whitelist = [\n")
|
|
||||||
for x in range(int(pwn_whitelist)):
|
|
||||||
ssid = input("SSID (Name): ")
|
|
||||||
bssid = input("BSSID (MAC): ")
|
|
||||||
f.write(f"\t\"{ssid}\",\n")
|
|
||||||
f.write(f"\t\"{bssid}\",\n")
|
|
||||||
f.write("]\n")
|
|
||||||
# set bluetooth tether
|
|
||||||
pwn_bluetooth = input("Do you want to enable BT-Tether?\n\n"
|
|
||||||
"[Y/N] ")
|
|
||||||
if pwn_bluetooth.lower() in ('y', 'yes'):
|
|
||||||
f.write("main.plugins.bt-tether.enabled = true\n\n")
|
|
||||||
pwn_bluetooth_device = input("What device do you use? Android or iOS?\n\n"
|
|
||||||
"Device: ")
|
|
||||||
if pwn_bluetooth_device.lower() == "android":
|
|
||||||
f.write("main.plugins.bt-tether.devices.android-phone.enabled = true\n")
|
|
||||||
pwn_bluetooth_mac = input("What is the bluetooth MAC of your device?\n\n"
|
|
||||||
"MAC: ")
|
|
||||||
if pwn_bluetooth_mac != "":
|
|
||||||
f.write(f"main.plugins.bt-tether.devices.android-phone.mac = \"{pwn_bluetooth_mac}\"\n")
|
|
||||||
elif pwn_bluetooth_device.lower() == "ios":
|
|
||||||
f.write("main.plugins.bt-tether.devices.ios-phone.enabled = true\n")
|
|
||||||
pwn_bluetooth_mac = input("What is the bluetooth MAC of your device?\n\n"
|
|
||||||
"MAC: ")
|
|
||||||
if pwn_bluetooth_mac != "":
|
|
||||||
f.write(f"main.plugins.bt-tether.devices.ios-phone.mac = \"{pwn_bluetooth_mac}\"\n")
|
|
||||||
# set up display settings
|
|
||||||
pwn_display_enabled = input("Do you want to enable a display?\n\n"
|
|
||||||
"[Y/N]: ")
|
|
||||||
if pwn_display_enabled.lower() in ('y', 'yes'):
|
|
||||||
f.write("ui.display.enabled = true\n")
|
|
||||||
pwn_display_type = input("What display do you use?\n\n"
|
|
||||||
"Be sure to check for the correct display type @ \n"
|
|
||||||
"https://github.com/jayofelony/pwnagotchi/blob/master/pwnagotchi/utils.py#L240-L501\n\n"
|
|
||||||
"Display type: ")
|
|
||||||
if pwn_display_type != "":
|
|
||||||
f.write(f"ui.display.type = \"{pwn_display_type}\"\n")
|
|
||||||
pwn_display_invert = input("Do you want to invert the display colors?\n"
|
|
||||||
"N = Black background\n"
|
|
||||||
"Y = White background\n\n"
|
|
||||||
"[Y/N]: ")
|
|
||||||
if pwn_display_invert.lower() in ('y', 'yes'):
|
|
||||||
f.write("ui.invert = true\n")
|
|
||||||
f.close()
|
|
||||||
if pwn_bluetooth.lower() in ('y', 'yes'):
|
|
||||||
if pwn_bluetooth_device.lower == "android":
|
|
||||||
print("To visit the webui when connected with your phone, visit: http://192.168.44.44:8080\n"
|
|
||||||
"Your configuration is done, and I will restart in 5 seconds.")
|
|
||||||
elif pwn_bluetooth_device.lower == "ios":
|
|
||||||
print("To visit the webui when connected with your phone, visit: http://172.20.10.6:8080\n"
|
|
||||||
"Your configuration is done, and I will restart in 5 seconds.")
|
|
||||||
else:
|
|
||||||
print("Your configuration is done, and I will restart in 5 seconds.")
|
|
||||||
time.sleep(5)
|
|
||||||
os.system("service pwnagotchi restart")
|
|
||||||
else:
|
|
||||||
print("Ok, doing nothing.")
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
if args.donate:
|
if args.donate:
|
||||||
print("Donations can be made @ \n "
|
print("Donations can made @ \n "
|
||||||
|
"https://www.patreon.com/pwnagotchi_torch \n "
|
||||||
"https://github.com/sponsors/jayofelony \n\n"
|
"https://github.com/sponsors/jayofelony \n\n"
|
||||||
"But only if you really want to!")
|
"But only if you really want to!")
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
@ -277,7 +172,8 @@ def pwnagotchi_cli():
|
|||||||
local = version_to_tuple(pwnagotchi.__version__)
|
local = version_to_tuple(pwnagotchi.__version__)
|
||||||
remote = version_to_tuple(latest_ver)
|
remote = version_to_tuple(latest_ver)
|
||||||
if remote > local:
|
if remote > local:
|
||||||
user_input = input("There is a new version available! Update from v%s to v%s?\n[Y/N] " % (pwnagotchi.__version__, latest_ver))
|
user_input = input("There is a new version available! Update from v%s to v%s?\n[y(es)/n(o)]"
|
||||||
|
% (pwnagotchi.__version__, latest_ver))
|
||||||
# input validation
|
# input validation
|
||||||
if user_input.lower() in ('y', 'yes'):
|
if user_input.lower() in ('y', 'yes'):
|
||||||
if os.path.exists('/root/.auto-update'):
|
if os.path.exists('/root/.auto-update'):
|
||||||
@ -323,7 +219,7 @@ def pwnagotchi_cli():
|
|||||||
|
|
||||||
def usr1_handler(*unused):
|
def usr1_handler(*unused):
|
||||||
logging.info('Received USR1 singal. Restart process ...')
|
logging.info('Received USR1 singal. Restart process ...')
|
||||||
agent._restart("MANU" if args.do_manual else "AUTO")
|
restart("MANU" if args.do_manual else "AUTO")
|
||||||
|
|
||||||
signal.signal(signal.SIGUSR1, usr1_handler)
|
signal.signal(signal.SIGUSR1, usr1_handler)
|
||||||
|
|
||||||
@ -331,7 +227,3 @@ def pwnagotchi_cli():
|
|||||||
do_manual_mode(agent)
|
do_manual_mode(agent)
|
||||||
else:
|
else:
|
||||||
do_auto_mode(agent)
|
do_auto_mode(agent)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
pwnagotchi_cli()
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
packer {
|
packer {
|
||||||
required_plugins {
|
required_plugins {
|
||||||
arm-image = {
|
arm = {
|
||||||
source = "github.com/solo-io/arm-image"
|
version = "1.0.0"
|
||||||
version = ">= 0.0.1"
|
source = "github.com/cdecoux/builder-arm"
|
||||||
}
|
}
|
||||||
ansible = {
|
ansible = {
|
||||||
source = "github.com/hashicorp/ansible"
|
source = "github.com/hashicorp/ansible"
|
||||||
@ -19,26 +19,64 @@ variable "pwn_version" {
|
|||||||
type = string
|
type = string
|
||||||
}
|
}
|
||||||
|
|
||||||
source "arm-image" "rpi64-pwnagotchi" {
|
source "arm" "rpi64-pwnagotchi" {
|
||||||
image_type = "raspberrypi"
|
file_checksum_url = "https://downloads.raspberrypi.org/raspios_lite_arm64/images/raspios_lite_arm64-2023-12-11/2023-12-11-raspios-bookworm-arm64-lite.img.xz.sha256"
|
||||||
iso_url = "https://downloads.raspberrypi.com/raspios_lite_arm64/images/raspios_lite_arm64-2024-07-04/2024-07-04-raspios-bookworm-arm64-lite.img.xz"
|
file_urls = ["https://downloads.raspberrypi.org/raspios_lite_arm64/images/raspios_lite_arm64-2023-12-11/2023-12-11-raspios-bookworm-arm64-lite.img.xz"]
|
||||||
iso_checksum = "sha256:43d150e7901583919e4eb1f0fa83fe0363af2d1e9777a5bb707d696d535e2599"
|
file_checksum_type = "sha256"
|
||||||
output_filename = "../../../pwnagotchi-64bit.img"
|
file_target_extension = "xz"
|
||||||
qemu_binary = "qemu-aarch64-static"
|
file_unarchive_cmd = ["unxz", "$ARCHIVE_PATH"]
|
||||||
target_image_size = 19969908736
|
image_path = "../pwnagotchi-64bit.img"
|
||||||
image_mounts = ["/boot/firmware","/"]
|
qemu_binary_source_path = "/usr/libexec/qemu-binfmt/aarch64-binfmt-P"
|
||||||
|
qemu_binary_destination_path = "/usr/libexec/qemu-binfmt/aarch64-binfmt-P"
|
||||||
|
image_build_method = "resize"
|
||||||
|
image_size = "9G"
|
||||||
|
image_type = "dos"
|
||||||
|
image_partitions {
|
||||||
|
name = "boot"
|
||||||
|
type = "c"
|
||||||
|
start_sector = "8192"
|
||||||
|
filesystem = "fat"
|
||||||
|
size = "256M"
|
||||||
|
mountpoint = "/boot/firmware"
|
||||||
|
}
|
||||||
|
image_partitions {
|
||||||
|
name = "root"
|
||||||
|
type = "83"
|
||||||
|
start_sector = "532480"
|
||||||
|
filesystem = "ext4"
|
||||||
|
size = "0"
|
||||||
|
mountpoint = "/"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
source "arm-image" "rpi32-pwnagotchi" {
|
source "arm" "rpi32-pwnagotchi" {
|
||||||
image_type = "raspberrypi"
|
file_checksum_url = "https://downloads.raspberrypi.com/raspios_oldstable_lite_armhf/images/raspios_oldstable_lite_armhf-2023-12-06/2023-12-05-raspios-bullseye-armhf-lite.img.xz.sha256"
|
||||||
iso_url = "https://downloads.raspberrypi.com/raspios_lite_armhf/images/raspios_lite_armhf-2024-07-04/2024-07-04-raspios-bookworm-armhf-lite.img.xz"
|
file_urls = ["https://downloads.raspberrypi.com/raspios_oldstable_lite_armhf/images/raspios_oldstable_lite_armhf-2023-12-06/2023-12-05-raspios-bullseye-armhf-lite.img.xz"]
|
||||||
iso_checksum = "sha256:df9c192d66d35e1ce67acde33a5b5f2b81ff02d2b986ea52f1f6ea211d646a1b"
|
file_checksum_type = "sha256"
|
||||||
output_filename = "../../../pwnagotchi-32bit.img"
|
file_target_extension = "xz"
|
||||||
qemu_binary = "qemu-arm-static"
|
file_unarchive_cmd = ["unxz", "$ARCHIVE_PATH"]
|
||||||
qemu_args = ["-cpu", "arm1176"]
|
image_path = "../pwnagotchi-32bit.img"
|
||||||
image_arch = "arm"
|
qemu_binary_source_path = "/usr/libexec/qemu-binfmt/arm-binfmt-P"
|
||||||
target_image_size = 19969908736
|
qemu_binary_destination_path = "/usr/libexec/qemu-binfmt/arm-binfmt-P"
|
||||||
image_mounts = ["/boot/firmware","/"]
|
image_build_method = "resize"
|
||||||
|
image_size = "9G"
|
||||||
|
image_type = "dos"
|
||||||
|
image_partitions {
|
||||||
|
name = "boot"
|
||||||
|
type = "c"
|
||||||
|
start_sector = "8192"
|
||||||
|
filesystem = "fat"
|
||||||
|
size = "256M"
|
||||||
|
mountpoint = "/boot"
|
||||||
|
}
|
||||||
|
image_partitions {
|
||||||
|
name = "root"
|
||||||
|
type = "83"
|
||||||
|
start_sector = "532480"
|
||||||
|
filesystem = "ext4"
|
||||||
|
size = "0"
|
||||||
|
mountpoint = "/"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# a build block invokes sources and runs provisioning steps on them. The
|
# a build block invokes sources and runs provisioning steps on them. The
|
||||||
@ -46,7 +84,7 @@ source "arm-image" "rpi32-pwnagotchi" {
|
|||||||
# https://www.packer.io/docs/from-1.5/blocks/build
|
# https://www.packer.io/docs/from-1.5/blocks/build
|
||||||
build {
|
build {
|
||||||
name = "Raspberry Pi 64 Pwnagotchi"
|
name = "Raspberry Pi 64 Pwnagotchi"
|
||||||
sources = ["source.arm-image.rpi64-pwnagotchi"]
|
sources = ["source.arm.rpi64-pwnagotchi"]
|
||||||
|
|
||||||
provisioner "file" {
|
provisioner "file" {
|
||||||
destination = "/usr/bin/"
|
destination = "/usr/bin/"
|
||||||
@ -63,13 +101,6 @@ build {
|
|||||||
provisioner "shell" {
|
provisioner "shell" {
|
||||||
inline = ["chmod +x /usr/bin/*"]
|
inline = ["chmod +x /usr/bin/*"]
|
||||||
}
|
}
|
||||||
provisioner "shell" {
|
|
||||||
inline = ["mkdir -p /usr/local/src/pwnagotchi"]
|
|
||||||
}
|
|
||||||
provisioner "file" {
|
|
||||||
destination = "/usr/local/src/pwnagotchi/"
|
|
||||||
source = "../"
|
|
||||||
}
|
|
||||||
|
|
||||||
provisioner "file" {
|
provisioner "file" {
|
||||||
destination = "/etc/systemd/system/"
|
destination = "/etc/systemd/system/"
|
||||||
@ -92,13 +123,13 @@ build {
|
|||||||
provisioner "ansible-local" {
|
provisioner "ansible-local" {
|
||||||
command = "ANSIBLE_FORCE_COLOR=1 PYTHONUNBUFFERED=1 PWN_VERSION=${var.pwn_version} PWN_HOSTNAME=${var.pwn_hostname} ansible-playbook"
|
command = "ANSIBLE_FORCE_COLOR=1 PYTHONUNBUFFERED=1 PWN_VERSION=${var.pwn_version} PWN_HOSTNAME=${var.pwn_hostname} ansible-playbook"
|
||||||
extra_arguments = ["--extra-vars \"ansible_python_interpreter=/usr/bin/python3\""]
|
extra_arguments = ["--extra-vars \"ansible_python_interpreter=/usr/bin/python3\""]
|
||||||
playbook_file = "raspberrypi64.yml"
|
playbook_file = "data/64bit/raspberrypi64.yml"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
build {
|
build {
|
||||||
name = "Raspberry Pi 32 Pwnagotchi"
|
name = "Raspberry Pi 32 Pwnagotchi"
|
||||||
sources = ["source.arm-image.rpi32-pwnagotchi"]
|
sources = ["source.arm.rpi32-pwnagotchi"]
|
||||||
provisioner "file" {
|
provisioner "file" {
|
||||||
destination = "/usr/bin/"
|
destination = "/usr/bin/"
|
||||||
sources = [
|
sources = [
|
||||||
@ -111,16 +142,10 @@ build {
|
|||||||
"data/32bit/usr/bin/pwnlib",
|
"data/32bit/usr/bin/pwnlib",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
provisioner "shell" {
|
|
||||||
inline = ["mkdir -p /usr/local/src/pwnagotchi"]
|
|
||||||
}
|
|
||||||
provisioner "file" {
|
|
||||||
destination = "/usr/local/src/pwnagotchi/"
|
|
||||||
source = "../"
|
|
||||||
}
|
|
||||||
provisioner "shell" {
|
provisioner "shell" {
|
||||||
inline = ["chmod +x /usr/bin/*"]
|
inline = ["chmod +x /usr/bin/*"]
|
||||||
}
|
}
|
||||||
|
|
||||||
provisioner "file" {
|
provisioner "file" {
|
||||||
destination = "/etc/systemd/system/"
|
destination = "/etc/systemd/system/"
|
||||||
sources = [
|
sources = [
|
||||||
@ -142,6 +167,7 @@ build {
|
|||||||
provisioner "ansible-local" {
|
provisioner "ansible-local" {
|
||||||
command = "ANSIBLE_FORCE_COLOR=1 PYTHONUNBUFFERED=1 PWN_VERSION=${var.pwn_version} PWN_HOSTNAME=${var.pwn_hostname} ansible-playbook"
|
command = "ANSIBLE_FORCE_COLOR=1 PYTHONUNBUFFERED=1 PWN_VERSION=${var.pwn_version} PWN_HOSTNAME=${var.pwn_hostname} ansible-playbook"
|
||||||
extra_arguments = ["--extra-vars \"ansible_python_interpreter=/usr/bin/python3\""]
|
extra_arguments = ["--extra-vars \"ansible_python_interpreter=/usr/bin/python3\""]
|
||||||
playbook_file = "raspberrypi32.yml"
|
playbook_dir = "data/32bit/extras/"
|
||||||
|
playbook_file = "data/32bit/raspberrypi32.yml"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -2,7 +2,7 @@ _show_complete()
|
|||||||
{
|
{
|
||||||
local cur opts node_names all_options opt_line
|
local cur opts node_names all_options opt_line
|
||||||
all_options="
|
all_options="
|
||||||
pwnagotchi -h --help -C --config -U --user-config --manual --skip-session --clear --debug --version --print-config --wizard --check-update --donate {plugins,google}
|
pwnagotchi -h --help -C --config -U --user-config --manual --skip-session --clear --debug --version --print-config --check-update --donate {plugins,google}
|
||||||
pwnagotchi plugins -h --help {list,install,enable,disable,uninstall,update,upgrade}
|
pwnagotchi plugins -h --help {list,install,enable,disable,uninstall,update,upgrade}
|
||||||
pwnagotchi plugins list -i --installed -h --help
|
pwnagotchi plugins list -i --installed -h --help
|
||||||
pwnagotchi plugins install -h --help
|
pwnagotchi plugins install -h --help
|
62
builder/data/32bit/etc/dhcpcd.conf
Normal file
62
builder/data/32bit/etc/dhcpcd.conf
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
# A sample configuration for dhcpcd.
|
||||||
|
# See dhcpcd.conf(5) for details.
|
||||||
|
|
||||||
|
# Allow users of this group to interact with dhcpcd via the control socket.
|
||||||
|
#controlgroup wheel
|
||||||
|
|
||||||
|
# Inform the DHCP server of our hostname for DDNS.
|
||||||
|
hostname
|
||||||
|
|
||||||
|
# Use the hardware address of the interface for the Client ID.
|
||||||
|
clientid
|
||||||
|
# or
|
||||||
|
# Use the same DUID + IAID as set in DHCPv6 for DHCPv4 ClientID as per RFC4361.
|
||||||
|
# Some non-RFC compliant DHCP servers do not reply with this set.
|
||||||
|
# In this case, comment out duid and enable clientid above.
|
||||||
|
#duid
|
||||||
|
|
||||||
|
# Persist interface configuration when dhcpcd exits.
|
||||||
|
persistent
|
||||||
|
|
||||||
|
# Rapid commit support.
|
||||||
|
# Safe to enable by default because it requires the equivalent option set
|
||||||
|
# on the server to actually work.
|
||||||
|
option rapid_commit
|
||||||
|
|
||||||
|
# A list of options to request from the DHCP server.
|
||||||
|
option domain_name_servers, domain_name, domain_search, host_name
|
||||||
|
option classless_static_routes
|
||||||
|
# Respect the network MTU. This is applied to DHCP routes.
|
||||||
|
option interface_mtu
|
||||||
|
|
||||||
|
# Most distributions have NTP support.
|
||||||
|
#option ntp_servers
|
||||||
|
|
||||||
|
# A ServerID is required by RFC2131.
|
||||||
|
require dhcp_server_identifier
|
||||||
|
|
||||||
|
# Generate SLAAC address using the Hardware Address of the interface
|
||||||
|
#slaac hwaddr
|
||||||
|
# OR generate Stable Private IPv6 Addresses based from the DUID
|
||||||
|
slaac private
|
||||||
|
|
||||||
|
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||||
|
# !! DO NOT EDIT THESE LINES BELOW PLEASE !!
|
||||||
|
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||||
|
|
||||||
|
# static IP configuration:
|
||||||
|
denyinterfaces wlan0
|
||||||
|
|
||||||
|
interface eth0
|
||||||
|
static domain_name_servers=8.8.8.8 1.1.1.1
|
||||||
|
metric 201
|
||||||
|
|
||||||
|
interface usb0
|
||||||
|
static ip_address=10.0.0.2/24
|
||||||
|
static routers=10.0.0.1
|
||||||
|
static domain_name_servers=10.0.0.1 8.8.8.8 1.1.1.1
|
||||||
|
metric 202
|
||||||
|
|
||||||
|
interface bnep0
|
||||||
|
static domain_name_servers=8.8.8.8 1.1.1.1
|
||||||
|
metric 203
|
@ -0,0 +1,6 @@
|
|||||||
|
[Unit]
|
||||||
|
After=hciuart.service bluetooth.service
|
||||||
|
Before=
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
ExecStartPre=/bin/sleep 5
|
@ -1,6 +1,6 @@
|
|||||||
[Unit]
|
[Unit]
|
||||||
Description=pwnagotchi Deep Reinforcement Learning instrumenting bettercap for WiFI pwning.
|
Description=pwnagotchi Deep Reinforcement Learning instrumenting bettercap for WiFI pwning.
|
||||||
Documentation=https://pwnagotchi.org
|
Documentation=https://pwnagotchi.ai
|
||||||
Wants=network.target
|
Wants=network.target
|
||||||
After=pwngrid-peer.service
|
After=pwngrid-peer.service
|
||||||
|
|
33
builder/data/32bit/etc/update-motd.d/01-motd
Executable file
33
builder/data/32bit/etc/update-motd.d/01-motd
Executable file
@ -0,0 +1,33 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
_hostname=$(hostname)
|
||||||
|
_version=$(cut -d"'" -f2 < /usr/local/lib/python3.9/dist-packages/pwnagotchi/_version.py)
|
||||||
|
echo
|
||||||
|
echo "(◕‿‿◕) $_hostname"
|
||||||
|
echo
|
||||||
|
echo " Hi! I'm a pwnagotchi $_version, please take good care of me!"
|
||||||
|
echo " Here are some basic things you need to know to raise me properly!"
|
||||||
|
echo
|
||||||
|
echo " If you want to change my configuration, use /etc/pwnagotchi/config.toml"
|
||||||
|
echo " All plugin config files are located in /etc/pwnagotchi/conf.d/"
|
||||||
|
echo " Read the readme if you want to use gdrivesync plugin!!"
|
||||||
|
echo
|
||||||
|
echo " All the configuration options can be found on /etc/pwnagotchi/default.toml,"
|
||||||
|
echo " but don't change this file because I will recreate it every time I'm restarted!"
|
||||||
|
echo
|
||||||
|
echo " I use oPwnGrid as my main API, you can check stats at https://opwngrid.xyz"
|
||||||
|
echo
|
||||||
|
echo " I'm managed by systemd. Here are some basic commands."
|
||||||
|
echo
|
||||||
|
echo " If you want to know what I'm doing, you can check my logs with the command"
|
||||||
|
echo " - pwnlog"
|
||||||
|
echo " - sudo pwnagotchi --version, to check the current version"
|
||||||
|
echo " - sudo pwnagotchi --donate, to see how you can donate to this project"
|
||||||
|
echo " - sudo pwnagotchi --check-update, to see if there is a new version available"
|
||||||
|
echo
|
||||||
|
echo " If you want to know if I'm running, you can use"
|
||||||
|
echo " sudo systemctl status pwnagotchi"
|
||||||
|
echo
|
||||||
|
echo " You can restart me using"
|
||||||
|
echo " pwnkill"
|
||||||
|
echo
|
||||||
|
echo " You learn more about me at https://pwnagotchi.ai/"
|
40
builder/data/32bit/extras/nexmon.yml
Normal file
40
builder/data/32bit/extras/nexmon.yml
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
# Install nexmon to fix wireless scanning (takes 2.5G of space)
|
||||||
|
- name: clone nexmon repository
|
||||||
|
git:
|
||||||
|
repo: https://github.com/DrSchottky/nexmon.git
|
||||||
|
dest: /usr/local/src/nexmon
|
||||||
|
|
||||||
|
- name: make firmware
|
||||||
|
shell: "source ./setup_env.sh && make"
|
||||||
|
args:
|
||||||
|
executable: /bin/bash
|
||||||
|
chdir: /usr/local/src/nexmon/
|
||||||
|
|
||||||
|
- name: "make firmware patch ({{ item.name }})"
|
||||||
|
shell: "source ./setup_env.sh && cd /usr/local/src/nexmon/patches/{{ item.patch }}/nexmon/ && make"
|
||||||
|
args:
|
||||||
|
executable: /bin/bash
|
||||||
|
chdir: /usr/local/src/nexmon/
|
||||||
|
environment:
|
||||||
|
QEMU_UNAME: "{{ item.kernel }}"
|
||||||
|
ARCHFLAGS: "{{ item.arch_flags }}"
|
||||||
|
|
||||||
|
- name: "install new firmware ({{ item.name }})"
|
||||||
|
copy:
|
||||||
|
src: "/usr/local/src/nexmon/patches/{{ item.patch }}/nexmon/{{ item.firmware }}"
|
||||||
|
dest: "/usr/lib/firmware/brcm/{{ item.firmware }}"
|
||||||
|
follow: true
|
||||||
|
environment:
|
||||||
|
QEMU_UNAME: "{{ item.kernel }}"
|
||||||
|
ARCHFLAGS: "{{ item.arch_flags }}"
|
||||||
|
|
||||||
|
- name: backup original driver
|
||||||
|
command: "mv /usr/lib/modules/{{ item.kernel }}/kernel/drivers/net/wireless/broadcom/brcm80211/brcmfmac/brcmfmac.ko.xz /usr/lib/modules/{{ item.kernel }}/kernel/drivers/net/wireless/broadcom/brcm80211/brcmfmac/brcmfmac.ko.xz.orig"
|
||||||
|
|
||||||
|
- name: copy modified driver
|
||||||
|
copy:
|
||||||
|
src: "/usr/local/src/nexmon/patches/driver/brcmfmac_6.1.y-nexmon/brcmfmac.ko"
|
||||||
|
dest: "/usr/lib/modules/{{ item.kernel }}/kernel/drivers/net/wireless/broadcom/brcm80211/brcmfmac/brcmfmac.ko"
|
||||||
|
|
||||||
|
- name : load brcmfmac drivers
|
||||||
|
command: "/sbin/depmod -a {{ item.kernel }}"
|
@ -1,8 +1,8 @@
|
|||||||
packer {
|
packer {
|
||||||
required_plugins {
|
required_plugins {
|
||||||
arm-image = {
|
arm = {
|
||||||
source = "github.com/solo-io/arm-image"
|
version = "1.0.0"
|
||||||
version = ">= 0.0.1"
|
source = "github.com/cdecoux/builder-arm"
|
||||||
}
|
}
|
||||||
ansible = {
|
ansible = {
|
||||||
source = "github.com/hashicorp/ansible"
|
source = "github.com/hashicorp/ansible"
|
||||||
@ -19,21 +19,38 @@ variable "pwn_version" {
|
|||||||
type = string
|
type = string
|
||||||
}
|
}
|
||||||
|
|
||||||
source "arm-image" "rpi32-pwnagotchi" {
|
source "arm" "rpi32-pwnagotchi" {
|
||||||
image_type = "raspberrypi"
|
file_checksum_url = "https://downloads.raspberrypi.com/raspios_oldstable_lite_armhf/images/raspios_oldstable_lite_armhf-2023-12-06/2023-12-05-raspios-bullseye-armhf-lite.img.xz.sha256"
|
||||||
iso_url = "https://downloads.raspberrypi.com/raspios_lite_armhf/images/raspios_lite_armhf-2024-07-04/2024-07-04-raspios-bookworm-armhf-lite.img.xz"
|
file_urls = ["https://downloads.raspberrypi.com/raspios_oldstable_lite_armhf/images/raspios_oldstable_lite_armhf-2023-12-06/2023-12-05-raspios-bullseye-armhf-lite.img.xz"]
|
||||||
iso_checksum = "sha256:df9c192d66d35e1ce67acde33a5b5f2b81ff02d2b986ea52f1f6ea211d646a1b"
|
file_checksum_type = "sha256"
|
||||||
output_filename = "../../../pwnagotchi-32bit.img"
|
file_target_extension = "xz"
|
||||||
qemu_binary = "qemu-arm-static"
|
file_unarchive_cmd = ["unxz", "$ARCHIVE_PATH"]
|
||||||
qemu_args = ["-cpu", "arm1176"]
|
image_path = "../../pwnagotchi-32bit.img"
|
||||||
image_arch = "arm"
|
qemu_binary_source_path = "/usr/libexec/qemu-binfmt/arm-binfmt-P"
|
||||||
image_mounts = ["/boot/firmware","/"]
|
qemu_binary_destination_path = "/usr/libexec/qemu-binfmt/arm-binfmt-P"
|
||||||
target_image_size = 19969908736
|
image_build_method = "resize"
|
||||||
|
image_size = "9G"
|
||||||
|
image_type = "dos"
|
||||||
|
image_partitions {
|
||||||
|
name = "boot"
|
||||||
|
type = "c"
|
||||||
|
start_sector = "8192"
|
||||||
|
filesystem = "fat"
|
||||||
|
size = "256M"
|
||||||
|
mountpoint = "/boot"
|
||||||
|
}
|
||||||
|
image_partitions {
|
||||||
|
name = "root"
|
||||||
|
type = "83"
|
||||||
|
start_sector = "532480"
|
||||||
|
filesystem = "ext4"
|
||||||
|
size = "0"
|
||||||
|
mountpoint = "/"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
build {
|
build {
|
||||||
name = "Raspberry Pi 32 Pwnagotchi"
|
name = "Raspberry Pi 32 Pwnagotchi"
|
||||||
sources = ["source.arm-image.rpi32-pwnagotchi"]
|
sources = ["source.arm.rpi32-pwnagotchi"]
|
||||||
provisioner "file" {
|
provisioner "file" {
|
||||||
destination = "/usr/bin/"
|
destination = "/usr/bin/"
|
||||||
sources = [
|
sources = [
|
||||||
@ -49,13 +66,7 @@ build {
|
|||||||
provisioner "shell" {
|
provisioner "shell" {
|
||||||
inline = ["chmod +x /usr/bin/*"]
|
inline = ["chmod +x /usr/bin/*"]
|
||||||
}
|
}
|
||||||
provisioner "shell" {
|
|
||||||
inline = ["mkdir -p /usr/local/src/pwnagotchi"]
|
|
||||||
}
|
|
||||||
provisioner "file" {
|
|
||||||
destination = "/usr/local/src/pwnagotchi/"
|
|
||||||
source = "../"
|
|
||||||
}
|
|
||||||
provisioner "file" {
|
provisioner "file" {
|
||||||
destination = "/etc/systemd/system/"
|
destination = "/etc/systemd/system/"
|
||||||
sources = [
|
sources = [
|
||||||
@ -77,6 +88,7 @@ build {
|
|||||||
provisioner "ansible-local" {
|
provisioner "ansible-local" {
|
||||||
command = "ANSIBLE_FORCE_COLOR=1 PYTHONUNBUFFERED=1 PWN_VERSION=${var.pwn_version} PWN_HOSTNAME=${var.pwn_hostname} ansible-playbook"
|
command = "ANSIBLE_FORCE_COLOR=1 PYTHONUNBUFFERED=1 PWN_VERSION=${var.pwn_version} PWN_HOSTNAME=${var.pwn_hostname} ansible-playbook"
|
||||||
extra_arguments = ["--extra-vars \"ansible_python_interpreter=/usr/bin/python3\""]
|
extra_arguments = ["--extra-vars \"ansible_python_interpreter=/usr/bin/python3\""]
|
||||||
playbook_file = "raspberrypi32.yml"
|
playbook_dir = "data/32bit/extras/"
|
||||||
|
playbook_file = "data/32bit/raspberrypi32.yml"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -4,15 +4,66 @@
|
|||||||
gather_facts: true
|
gather_facts: true
|
||||||
become: true
|
become: true
|
||||||
vars:
|
vars:
|
||||||
|
boards:
|
||||||
|
- {
|
||||||
|
kernel: "6.1.21+",
|
||||||
|
name: "PiZeroW",
|
||||||
|
firmware: "brcmfmac43430-sdio.bin",
|
||||||
|
patch: "bcm43430a1/7_45_41_46",
|
||||||
|
cpu: arm1176,
|
||||||
|
arch_flags: "-arch armv6l"
|
||||||
|
}
|
||||||
|
- {
|
||||||
|
kernel: "6.1.21-v7+",
|
||||||
|
name: "PiZero2W",
|
||||||
|
firmware: "brcmfmac43436-sdio.bin",
|
||||||
|
patch: "bcm43436b0/9_88_4_65",
|
||||||
|
cpu: any, #cortex-a53
|
||||||
|
arch_flags: "-arch armv7l"
|
||||||
|
}
|
||||||
|
- {
|
||||||
|
kernel: "6.1.21-v7l+",
|
||||||
|
name: "Pi4b_32",
|
||||||
|
firmware: "brcmfmac43455-sdio.bin",
|
||||||
|
patch: "bcm43455c0/7_45_206",
|
||||||
|
cpu: any, #cortex-a72
|
||||||
|
arch_flags: "-arch armv7l"
|
||||||
|
}
|
||||||
kernel:
|
kernel:
|
||||||
min: "6.6"
|
min: "6.1"
|
||||||
full: "6.6.31+rpt-rpi-v6"
|
full: "6.1.21+"
|
||||||
|
full_2w: "6.1.21-v7+"
|
||||||
|
full_4b: "6.1.21-v7l+"
|
||||||
|
arch: "v6l"
|
||||||
pwnagotchi:
|
pwnagotchi:
|
||||||
hostname: "{{ lookup('env', 'PWN_HOSTNAME') | default('pwnagotchi', true) }}"
|
hostname: "{{ lookup('env', 'PWN_HOSTNAME') | default('pwnagotchi', true) }}"
|
||||||
version: "{{ lookup('env', 'PWN_VERSION') | default('pwnagotchi', true) }}"
|
version: "{{ lookup('env', 'PWN_VERSION') | default('pwnagotchi-torch', true) }}"
|
||||||
|
custom_plugin_dir: "/usr/local/share/pwnagotchi/custom-plugins"
|
||||||
|
system:
|
||||||
|
boot_options:
|
||||||
|
- "#### pwnagotchi additions"
|
||||||
|
- "# this pwnagotchi image is 32-bit only no v8+ headers to build nexmon for 64 bit"
|
||||||
|
- "arm_64bit=0"
|
||||||
|
- "# dwc2 for RNDIS. comment out, and remove dwc2 and g_ether from cmdline.txt for X306 usb battery hat"
|
||||||
|
- "dtoverlay=dwc2"
|
||||||
|
- "dtoverlay=spi1-3cs"
|
||||||
|
- "dtparam=i2c1=on"
|
||||||
|
- "dtparam=i2c_arm=on"
|
||||||
|
- "dtparam=spi=on"
|
||||||
|
- "gpu_mem=16"
|
||||||
|
- "#### audio out on pins 18 and 19"
|
||||||
|
- "#dtoverlay=audremap,pins_18_19"
|
||||||
|
- "#### touchscreen on waveshare touch e-paper"
|
||||||
|
- "#dtoverlay=goodix,interrupt=27,reset=22"
|
||||||
|
- "#### for PWM backlighting on pimoroni displayhatmini"
|
||||||
|
- "dtoverlay=pwm-2chan,pin=12,func=4,pin2=13,func2=4"
|
||||||
|
modules:
|
||||||
|
- "i2c-dev"
|
||||||
services:
|
services:
|
||||||
enable:
|
enable:
|
||||||
- bettercap.service
|
- bettercap.service
|
||||||
|
- bluetooth.service
|
||||||
|
- dphys-swapfile.service
|
||||||
- fstrim.timer
|
- fstrim.timer
|
||||||
- pwnagotchi.service
|
- pwnagotchi.service
|
||||||
- pwngrid-peer.service
|
- pwngrid-peer.service
|
||||||
@ -21,24 +72,25 @@
|
|||||||
- apt-daily-upgrade.timer
|
- apt-daily-upgrade.timer
|
||||||
- apt-daily.service
|
- apt-daily.service
|
||||||
- apt-daily.timer
|
- apt-daily.timer
|
||||||
- bluetooth.service
|
|
||||||
- ifup@wlan0.service
|
- ifup@wlan0.service
|
||||||
|
- triggerhappy.service
|
||||||
|
- wpa_supplicant.service
|
||||||
packages:
|
packages:
|
||||||
caplets:
|
caplets:
|
||||||
source: "https://github.com/jayofelony/caplets.git"
|
source: "https://github.com/jayofelony/caplets.git"
|
||||||
branch: "lite" # or master
|
|
||||||
bettercap:
|
bettercap:
|
||||||
source: "https://github.com/jayofelony/bettercap.git"
|
source: "https://github.com/jayofelony/bettercap.git"
|
||||||
branch: "lite" # or master
|
url: "https://github.com/jayofelony/bettercap/releases/download/2.32.2/bettercap-2.32.2-armhf.zip"
|
||||||
pwngrid:
|
ui: "https://github.com/bettercap/ui/releases/download/v1.3.0/ui.zip"
|
||||||
|
opwngrid:
|
||||||
source: "https://github.com/jayofelony/pwngrid.git"
|
source: "https://github.com/jayofelony/pwngrid.git"
|
||||||
url: "https://github.com/jayofelony/pwngrid/releases/download/v1.10.7/pwngrid-1.10.7-armhf.zip"
|
url: "https://github.com/jayofelony/pwngrid/releases/download/v1.10.7/pwngrid-1.10.7-armhf.zip"
|
||||||
torch:
|
torch:
|
||||||
wheel: "torch-2.1.0a0+gita8e7c98-cp311-cp311-linux_armv6l.whl"
|
wheel: "torch-2.1.0a0+gitunknown-cp39-cp39-linux_armv6l.whl"
|
||||||
url: "https://github.com/Sniffleupagus/Torch4Pizero/releases/download/py0torch-bookworm-2024-05/torch-2.1.0a0+gita8e7c98-cp311-cp311-linux_armv6l.whl"
|
url: "https://github.com/Sniffleupagus/Torch4Pizero/releases/download/v1.0.0/torch-2.1.0a0+gitunknown-cp39-cp39-linux_armv6l.whl"
|
||||||
torchvision:
|
torchvision:
|
||||||
wheel: "torchvision-0.16.0+fbb4cc5-cp311-cp311-linux_armv6l.whl"
|
wheel: "torchvision-0.16.0a0-cp39-cp39-linux_armv6l.whl"
|
||||||
url: "https://github.com/Sniffleupagus/Torch4Pizero/releases/download/py0torch-bookworm-2024-05/torchvision-0.16.0+fbb4cc5-cp311-cp311-linux_armv6l.whl"
|
url: "https://github.com/Sniffleupagus/Torch4Pizero/releases/download/v1.0.0/torchvision-0.16.0a0-cp39-cp39-linux_armv6l.whl"
|
||||||
apt:
|
apt:
|
||||||
downgrade:
|
downgrade:
|
||||||
- libpcap-dev_1.9.1-4_armhf.deb
|
- libpcap-dev_1.9.1-4_armhf.deb
|
||||||
@ -53,145 +105,106 @@
|
|||||||
- firmware-realtek
|
- firmware-realtek
|
||||||
- libpcap-dev
|
- libpcap-dev
|
||||||
- libpcap0.8
|
- libpcap0.8
|
||||||
- libpcap0.8-dbg
|
|
||||||
- libpcap0.8-dev
|
- libpcap0.8-dev
|
||||||
|
- libpcap0.8-dbg
|
||||||
remove:
|
remove:
|
||||||
|
- avahi-daemon
|
||||||
- nfs-common
|
- nfs-common
|
||||||
- triggerhappy
|
- triggerhappy
|
||||||
|
- wpasupplicant
|
||||||
install:
|
install:
|
||||||
- aircrack-ng
|
|
||||||
- autoconf
|
- autoconf
|
||||||
|
- bc
|
||||||
- bison
|
- bison
|
||||||
- bluez
|
- bluez
|
||||||
- bluez-tools
|
|
||||||
- build-essential
|
- build-essential
|
||||||
- curl
|
- curl
|
||||||
|
- dkms
|
||||||
- dphys-swapfile
|
- dphys-swapfile
|
||||||
|
- espeak-ng
|
||||||
|
- evtest
|
||||||
- fbi
|
- fbi
|
||||||
- firmware-atheros
|
|
||||||
- firmware-brcm80211
|
|
||||||
- firmware-libertas
|
|
||||||
- firmware-misc-nonfree
|
|
||||||
- firmware-realtek
|
|
||||||
- flex
|
- flex
|
||||||
|
- fonts-dejavu
|
||||||
|
- fonts-dejavu-core
|
||||||
|
- fonts-dejavu-extra
|
||||||
|
- fonts-freefont-ttf
|
||||||
- g++
|
- g++
|
||||||
- gawk
|
- gawk
|
||||||
- gcc-arm-none-eabi
|
- gcc-arm-none-eabi
|
||||||
- git
|
- git
|
||||||
- libatlas-base-dev
|
- libatlas-base-dev
|
||||||
|
- libavcodec58
|
||||||
|
- libavformat58
|
||||||
|
- libblas-dev
|
||||||
|
- libbluetooth-dev
|
||||||
|
- libbz2-dev
|
||||||
|
- libc-ares-dev
|
||||||
- libc6-dev
|
- libc6-dev
|
||||||
- libcpuinfo-dev
|
- libcpuinfo-dev
|
||||||
- libcurl-ocaml-dev
|
|
||||||
- libdbus-1-dev
|
- libdbus-1-dev
|
||||||
- libdbus-glib-1-dev
|
- libdbus-glib-1-dev
|
||||||
|
- libeigen3-dev
|
||||||
|
- libelf-dev
|
||||||
|
- libffi-dev
|
||||||
- libfl-dev
|
- libfl-dev
|
||||||
|
- libfuse-dev
|
||||||
|
- libgdbm-dev
|
||||||
|
- libgl1-mesa-glx
|
||||||
- libgmp3-dev
|
- libgmp3-dev
|
||||||
|
- libgstreamer1.0-0
|
||||||
|
- libhdf5-dev
|
||||||
|
- liblapack-dev
|
||||||
|
- libncursesw5-dev
|
||||||
- libnetfilter-queue-dev
|
- libnetfilter-queue-dev
|
||||||
- libopenblas-dev # https://stackoverflow.com/questions/14570011/explain-why-numpy-should-not-be-imported-from-source-directory
|
- libopenblas-dev
|
||||||
- libopenjp2-7
|
- libopenjp2-7
|
||||||
|
- libopenmpi-dev
|
||||||
|
- libopenmpi3
|
||||||
- libpcap-dev
|
- libpcap-dev
|
||||||
#- libraspberrypi-bin ## seems to be provided by raspi-utils now
|
- libprotobuf-dev
|
||||||
|
- libraspberrypi-bin
|
||||||
- libraspberrypi-dev
|
- libraspberrypi-dev
|
||||||
- libraspberrypi-doc
|
- libraspberrypi-doc
|
||||||
- libraspberrypi0
|
- libraspberrypi0
|
||||||
- libsleef-dev
|
- libsleef-dev
|
||||||
|
- libsqlite3-dev
|
||||||
- libssl-dev
|
- libssl-dev
|
||||||
- libssl-ocaml-dev
|
- libswscale5
|
||||||
|
- libtiff5
|
||||||
- libtool
|
- libtool
|
||||||
|
- libts-bin
|
||||||
- libusb-1.0-0-dev
|
- libusb-1.0-0-dev
|
||||||
|
- lsof
|
||||||
- make
|
- make
|
||||||
- ntp
|
- python3-flask
|
||||||
- pkg-config
|
- python3-flask-cors
|
||||||
- python3-dev
|
- python3-flaskext.wtf
|
||||||
|
- python3-pil
|
||||||
- python3-pip
|
- python3-pip
|
||||||
- python3-protobuf
|
- python3-protobuf
|
||||||
- python3-setuptools
|
|
||||||
- python3-smbus
|
- python3-smbus
|
||||||
- qpdf
|
- qpdf
|
||||||
- raspberrypi-kernel-headers
|
- raspberrypi-kernel-headers
|
||||||
- rsync
|
- rsync
|
||||||
|
- screen
|
||||||
- tcpdump
|
- tcpdump
|
||||||
- texinfo
|
- texinfo
|
||||||
|
- time
|
||||||
|
- tk-dev
|
||||||
- unzip
|
- unzip
|
||||||
|
- vim
|
||||||
- wget
|
- wget
|
||||||
- wl
|
- wl
|
||||||
- xxd
|
- xxd
|
||||||
- zlib1g-dev
|
- zlib1g-dev
|
||||||
environment:
|
|
||||||
ARCHFLAGS: "-arch armv6l"
|
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
# First we install packages
|
|
||||||
- name: install packages
|
|
||||||
apt:
|
|
||||||
name: "{{ packages.apt.install }}"
|
|
||||||
state: latest
|
|
||||||
update_cache: yes
|
|
||||||
install_recommends: no
|
|
||||||
|
|
||||||
- name: update pip3, setuptools, wheel
|
|
||||||
shell: "python3 -m pip install --upgrade pip setuptools wheel --break-system-packages"
|
|
||||||
args:
|
|
||||||
executable: /bin/bash
|
|
||||||
chdir: /usr/local/src
|
|
||||||
|
|
||||||
- name: install 32bit torch
|
|
||||||
shell: "python3 -m pip install {{ packages.torch.url }} {{ packages.torchvision.url }} --break-system-packages"
|
|
||||||
args:
|
|
||||||
executable: /bin/bash
|
|
||||||
environment:
|
|
||||||
QEMU_UNAME: "{{ kernel.full }}"
|
|
||||||
ARCHFLAGS: "-arch armv6l"
|
|
||||||
|
|
||||||
# Now we set up /boot/firmware
|
|
||||||
- name: Create pi user
|
- name: Create pi user
|
||||||
copy:
|
copy:
|
||||||
dest: /boot/firmware/userconf
|
dest: /boot/userconf
|
||||||
content: |
|
content: |
|
||||||
pi:$5$733Efsksay$SEFUKemv8FaNAu6X4GUfxdSzSDh6PbpOcdtNe5b7Nt0
|
pi:$6$3jNr0GA9KIyt4hmM$efeVIopdMQ8DGgEPCWWlbx3mJJNAYci1lEXGdlky0xPyjqwKNbwTL5SrCcpb4144C4IvzWjn7Iv.QjqmU7iyT/
|
||||||
|
|
||||||
- name: enable ssh on boot
|
|
||||||
file:
|
|
||||||
path: /boot/firmware/ssh
|
|
||||||
state: touch
|
|
||||||
|
|
||||||
- name: remove current rc.local
|
|
||||||
file:
|
|
||||||
path: /etc/rc.local
|
|
||||||
state: absent
|
|
||||||
|
|
||||||
- name: change root partition
|
|
||||||
replace:
|
|
||||||
dest: /boot/firmware/cmdline.txt
|
|
||||||
backup: no
|
|
||||||
regexp: "root=PARTUUID=[a-zA-Z0-9\\-]+"
|
|
||||||
replace: "root=/dev/mmcblk0p2"
|
|
||||||
|
|
||||||
- name: configure /boot/firmware/cmdline.txt
|
|
||||||
lineinfile:
|
|
||||||
path: /boot/firmware/cmdline.txt
|
|
||||||
backrefs: True
|
|
||||||
state: present
|
|
||||||
backup: no
|
|
||||||
regexp: '(.*)$'
|
|
||||||
line: '\1 modules-load=dwc2,g_ether'
|
|
||||||
|
|
||||||
- name: setup /boot/firmware/config.txt
|
|
||||||
blockinfile:
|
|
||||||
path: /boot/firmware/config.txt
|
|
||||||
insertafter: EOF
|
|
||||||
block: |
|
|
||||||
dtparam=i2c1=on
|
|
||||||
dtparam=i2c_arm=on
|
|
||||||
dtparam=spi=on
|
|
||||||
gpu_mem=1
|
|
||||||
dtoverlay=dwc2
|
|
||||||
#dtoverlay=disable-wifi
|
|
||||||
enable_uart=1
|
|
||||||
|
|
||||||
[pi0]
|
|
||||||
dtoverlay=spi0-2cs
|
|
||||||
#dtoverlay=disable-wifi
|
|
||||||
|
|
||||||
- name: change hostname
|
- name: change hostname
|
||||||
lineinfile:
|
lineinfile:
|
||||||
@ -210,14 +223,26 @@
|
|||||||
state: present
|
state: present
|
||||||
when: hostname.changed
|
when: hostname.changed
|
||||||
|
|
||||||
# Now we disable sap and a2dp, we don't use them on rpi
|
- name: Create custom plugin directory
|
||||||
- name: disable sap plugin for bluetooth.service
|
file:
|
||||||
lineinfile:
|
path: '{{ pwnagotchi.custom_plugin_dir }}'
|
||||||
dest: /lib/systemd/system/bluetooth.service
|
state: directory
|
||||||
regexp: '^ExecStart=/usr/libexec/bluetooth/bluetoothd$'
|
|
||||||
line: 'ExecStart=/usr/libexec/bluetooth/bluetoothd --noplugin=sap,a2dp'
|
- name: update apt package cache
|
||||||
|
apt:
|
||||||
|
update_cache: yes
|
||||||
|
|
||||||
|
- name: install packages
|
||||||
|
apt:
|
||||||
|
name: "{{ packages.apt.install }}"
|
||||||
state: present
|
state: present
|
||||||
|
|
||||||
|
- name: update pip3, setuptools, wheel
|
||||||
|
shell: "python3 -m pip install --upgrade pip setuptools wheel"
|
||||||
|
args:
|
||||||
|
executable: /bin/bash
|
||||||
|
chdir: /usr/local/src
|
||||||
|
|
||||||
###########################################
|
###########################################
|
||||||
#
|
#
|
||||||
# libpcap v1.9 - build from source
|
# libpcap v1.9 - build from source
|
||||||
@ -251,61 +276,17 @@
|
|||||||
dest: /usr/local/lib/libpcap.so.0.8
|
dest: /usr/local/lib/libpcap.so.0.8
|
||||||
state: link
|
state: link
|
||||||
|
|
||||||
# install latest hcxtools
|
###############################################################
|
||||||
- name: clone hcxtools
|
# Install nexmon to fix wireless scanning (takes 2.5G of space)
|
||||||
git:
|
###############################################################
|
||||||
repo: https://github.com/ZerBea/hcxtools.git
|
|
||||||
dest: /usr/local/src/hcxtools
|
|
||||||
|
|
||||||
- name: install hcxtools
|
# Install nexmon for all boards
|
||||||
shell: "make && make install"
|
- name: build and install nexmon as needed
|
||||||
args:
|
include_tasks: nexmon.yml
|
||||||
executable: /bin/bash
|
loop: "{{ boards }}"
|
||||||
chdir: /usr/local/src/hcxtools
|
|
||||||
|
|
||||||
- name: remove hcxtools directory
|
|
||||||
file:
|
|
||||||
state: absent
|
|
||||||
path: /usr/local/src/hcxtools
|
|
||||||
|
|
||||||
# Installing nexmon
|
|
||||||
- name: clone nexmon repository
|
|
||||||
git:
|
|
||||||
repo: https://github.com/DrSchottky/nexmon.git
|
|
||||||
dest: /usr/local/src/nexmon
|
|
||||||
|
|
||||||
- name: make firmware
|
|
||||||
shell: "source ./setup_env.sh && make"
|
|
||||||
args:
|
|
||||||
executable: /bin/bash
|
|
||||||
chdir: /usr/local/src/nexmon/
|
|
||||||
environment:
|
|
||||||
QEMU_UNAME: "{{ kernel.full }}"
|
|
||||||
ARCHFLAGS: "-arch armv6l"
|
|
||||||
|
|
||||||
- name: make firmware patch (bcm43430a1)
|
|
||||||
shell: "source ./setup_env.sh && cd /usr/local/src/nexmon/patches/bcm43430a1/7_45_41_46/nexmon/ && make"
|
|
||||||
args:
|
|
||||||
executable: /bin/bash
|
|
||||||
chdir: /usr/local/src/nexmon/
|
|
||||||
environment:
|
|
||||||
QEMU_UNAME: "{{ kernel.full }}"
|
|
||||||
ARCHFLAGS: "-arch armv6l"
|
|
||||||
|
|
||||||
- name: install new firmware (bcm43430a1)
|
|
||||||
copy:
|
|
||||||
src: /usr/local/src/nexmon/patches/bcm43430a1/7_45_41_46/nexmon/brcmfmac43430-sdio.bin
|
|
||||||
dest: /usr/lib/firmware/brcm/brcmfmac43430-sdio.bin
|
|
||||||
follow: true
|
|
||||||
|
|
||||||
- name: copy modified driver
|
|
||||||
copy:
|
|
||||||
src: "/usr/local/src/nexmon/patches/driver/brcmfmac_{{ kernel.min }}.y-nexmon/brcmfmac.ko"
|
|
||||||
dest: "/usr/lib/modules/{{ kernel.full }}/kernel/drivers/net/wireless/broadcom/brcm80211/brcmfmac/brcmfmac.ko"
|
|
||||||
environment:
|
|
||||||
QEMU_UNAME: "{{ kernel.full }}"
|
|
||||||
ARCHFLAGS: "-arch armv6l"
|
|
||||||
|
|
||||||
|
# some pizero2w have the pizeroW wifi chip
|
||||||
|
# could this be a link instead of a copy? and force, only if not a link?
|
||||||
- name: copy 43430-sdio as 43436s-sdio for the special 43430/1 /2
|
- name: copy 43430-sdio as 43436s-sdio for the special 43430/1 /2
|
||||||
copy:
|
copy:
|
||||||
src: /usr/lib/firmware/brcm/brcmfmac43430-sdio.bin
|
src: /usr/lib/firmware/brcm/brcmfmac43430-sdio.bin
|
||||||
@ -320,14 +301,9 @@
|
|||||||
loop:
|
loop:
|
||||||
- /usr/lib/firmware/brcm/brcmfmac43430-sdio.clm_blob
|
- /usr/lib/firmware/brcm/brcmfmac43430-sdio.clm_blob
|
||||||
- /usr/lib/firmware/brcm/brcmfmac43430-sdio.raspberrypi,model-zero-w.clm_blob
|
- /usr/lib/firmware/brcm/brcmfmac43430-sdio.raspberrypi,model-zero-w.clm_blob
|
||||||
|
- /usr/lib/firmware/brcm/brcmfmac43430b0-sdio.raspberrypi,model-zero-2-w.clm_blob
|
||||||
- name: backup original driver
|
- /usr/lib/firmware/brcm/brcmfmac43436-sdio.raspberrypi,model-zero-2-w.clm_blob
|
||||||
command: "mv /usr/lib/modules/{{ kernel.full }}/kernel/drivers/net/wireless/broadcom/brcm80211/brcmfmac/brcmfmac.ko.xz /usr/lib/modules/{{ kernel.full }}/kernel/drivers/net/wireless/broadcom/brcm80211/brcmfmac/brcmfmac.ko.xz.orig"
|
- /usr/lib/firmware/brcm/brcmfmac43430-sdio.raspberrypi,3-model-b.clm_blob
|
||||||
|
|
||||||
- name: load brcmfmac drivers
|
|
||||||
command: "/sbin/depmod {{ kernel.full }}"
|
|
||||||
environment:
|
|
||||||
QEMU_UNAME: "{{ kernel.full }}"
|
|
||||||
|
|
||||||
# To shrink the final image, remove the nexmon directory (takes 2.5G of space) post build and installation
|
# To shrink the final image, remove the nexmon directory (takes 2.5G of space) post build and installation
|
||||||
- name: Delete nexmon content & directory
|
- name: Delete nexmon content & directory
|
||||||
@ -335,36 +311,58 @@
|
|||||||
state: absent
|
state: absent
|
||||||
path: /usr/local/src/nexmon/
|
path: /usr/local/src/nexmon/
|
||||||
|
|
||||||
- name: Create custom config directory
|
- name: clone pwnagotchi repository
|
||||||
file:
|
git:
|
||||||
path: /etc/pwnagotchi/conf.d/
|
repo: https://github.com/jayofelony/pwnagotchi.git
|
||||||
state: directory
|
dest: /usr/local/src/pwnagotchi
|
||||||
|
register: pwnagotchigit
|
||||||
#- name: clone pwnagotchi repository
|
|
||||||
# git:
|
|
||||||
# repo: https://github.com/jayofelony/pwnagotchi.git
|
|
||||||
# dest: /usr/local/src/pwnagotchi
|
|
||||||
|
|
||||||
|
# is this even necessary? Can't we just link from /home/pi/pwnagotchi to /usr/local/{bin,lib,etc}
|
||||||
|
# then just git update in the home dir and encourage hacking?
|
||||||
|
# make owned by pi.pi, and custom plugins.
|
||||||
- name: build pwnagotchi wheel
|
- name: build pwnagotchi wheel
|
||||||
command: "pip3 install . --no-cache-dir --break-system-packages"
|
command: "python3 setup.py sdist bdist_wheel"
|
||||||
args:
|
args:
|
||||||
chdir: /usr/local/src/pwnagotchi
|
chdir: /usr/local/src/pwnagotchi
|
||||||
|
when: (pwnagotchigit.changed) or (pip_packages['pwnagotchi'] is undefined) or (pip_packages['pwnagotchi'] != pwnagotchi_version)
|
||||||
|
|
||||||
|
- name: download torch whl
|
||||||
|
get_url:
|
||||||
|
url: "{{ packages.torch.url }}"
|
||||||
|
dest: /usr/local/src/
|
||||||
|
|
||||||
|
- name: download torchvision whl
|
||||||
|
get_url:
|
||||||
|
url: "{{ packages.torchvision.url }}"
|
||||||
|
dest: /usr/local/src/
|
||||||
|
|
||||||
|
- name: install 32-bit pwnagotchi wheel and dependencies with 32-bit torch wheels
|
||||||
|
pip:
|
||||||
|
name:
|
||||||
|
- "{{ lookup('fileglob', '/usr/local/src/pwnagotchi/dist/pwnagotchi*.whl') }}"
|
||||||
|
- "{{ packages.torch.url }}"
|
||||||
|
- "{{ packages.torchvision.url }}"
|
||||||
|
extra_args: "--no-cache-dir"
|
||||||
|
environment:
|
||||||
|
QEMU_CPU: arm1176
|
||||||
|
QEMU_UNAME: "{{ kernel.full }}"
|
||||||
|
when: (pwnagotchigit.changed) or (pip_packages['pwnagotchi'] is undefined) or (pip_packages['pwnagotchi'] != pwnagotchi_version)
|
||||||
|
|
||||||
- name: create /usr/local/share/pwnagotchi/ folder
|
- name: create /usr/local/share/pwnagotchi/ folder
|
||||||
file:
|
file:
|
||||||
path: /usr/local/share/pwnagotchi/
|
path: /usr/local/share/pwnagotchi/
|
||||||
state: directory
|
state: directory
|
||||||
|
|
||||||
- name: Create custom plugin directory
|
|
||||||
file:
|
|
||||||
path: /usr/local/share/pwnagotchi/custom-plugins/
|
|
||||||
state: directory
|
|
||||||
|
|
||||||
- name: remove pwnagotchi folder
|
- name: remove pwnagotchi folder
|
||||||
file:
|
file:
|
||||||
state: absent
|
state: absent
|
||||||
path: /usr/local/src/pwnagotchi
|
path: /usr/local/src/pwnagotchi
|
||||||
|
|
||||||
|
- name: remove torch whl
|
||||||
|
file:
|
||||||
|
state: absent
|
||||||
|
path: "{{ lookup('fileglob', '/usr/local/src/torch*.whl') }}"
|
||||||
|
|
||||||
##########################################
|
##########################################
|
||||||
#
|
#
|
||||||
# pwngrid, bettercap
|
# pwngrid, bettercap
|
||||||
@ -373,14 +371,14 @@
|
|||||||
|
|
||||||
- name: Install go-1.21
|
- name: Install go-1.21
|
||||||
unarchive:
|
unarchive:
|
||||||
src: https://go.dev/dl/go1.22.3.linux-armv6l.tar.gz
|
src: https://go.dev/dl/go1.21.6.linux-armv6l.tar.gz
|
||||||
dest: /usr/local
|
dest: /usr/local
|
||||||
remote_src: yes
|
remote_src: yes
|
||||||
register: golang
|
register: golang
|
||||||
|
|
||||||
- name: Update .bashrc for go-1.21
|
- name: Update .bashrc for go-1.21
|
||||||
blockinfile:
|
blockinfile:
|
||||||
dest: /etc/profile
|
dest: /home/pi/.bashrc
|
||||||
state: present
|
state: present
|
||||||
block: |
|
block: |
|
||||||
export GOPATH=$HOME/go
|
export GOPATH=$HOME/go
|
||||||
@ -388,42 +386,25 @@
|
|||||||
when: golang.changed
|
when: golang.changed
|
||||||
|
|
||||||
- name: download pwngrid
|
- name: download pwngrid
|
||||||
git:
|
unarchive:
|
||||||
repo: "{{ packages.pwngrid.source }}"
|
remote_src: yes
|
||||||
dest: /usr/local/src/pwngrid
|
src: "{{ packages.opwngrid.url }}"
|
||||||
|
dest: /usr/local/bin/
|
||||||
|
mode: 0755
|
||||||
|
|
||||||
- name: install pwngrid
|
- name: download and install bettercap
|
||||||
shell: "export GOPATH=$HOME/go && export PATH=/usr/local/go/bin:$PATH:$GOPATH/bin && go mod tidy && make && make install"
|
unarchive:
|
||||||
args:
|
src: "{{ packages.bettercap.url }}"
|
||||||
executable: /bin/bash
|
dest: /usr/local/bin
|
||||||
chdir: /usr/local/src/pwngrid
|
remote_src: yes
|
||||||
|
exclude:
|
||||||
- name: remove pwngrid folder
|
- README.md
|
||||||
file:
|
- LICENSE.md
|
||||||
state: absent
|
mode: 0755
|
||||||
path: /usr/local/src/pwngrid
|
|
||||||
|
|
||||||
- name: download bettercap
|
|
||||||
git:
|
|
||||||
repo: "{{ packages.bettercap.source }}"
|
|
||||||
version: "{{ packages.bettercap.branch }} "
|
|
||||||
dest: /usr/local/src/bettercap
|
|
||||||
|
|
||||||
- name: install bettercap 2.32.4
|
|
||||||
shell: "export GOPATH=$HOME/go && export PATH=/usr/local/go/bin:$PATH:$GOPATH/bin && go mod tidy && make && make install"
|
|
||||||
args:
|
|
||||||
executable: /bin/bash
|
|
||||||
chdir: /usr/local/src/bettercap
|
|
||||||
|
|
||||||
- name: remove bettercap folder
|
|
||||||
file:
|
|
||||||
state: absent
|
|
||||||
path: /usr/local/src/bettercap
|
|
||||||
|
|
||||||
- name: clone bettercap caplets
|
- name: clone bettercap caplets
|
||||||
git:
|
git:
|
||||||
repo: "{{ packages.caplets.source }}"
|
repo: "{{ packages.caplets.source }}"
|
||||||
version: "{{ packages.caplets.branch }}"
|
|
||||||
dest: /tmp/caplets
|
dest: /tmp/caplets
|
||||||
register: capletsgit
|
register: capletsgit
|
||||||
|
|
||||||
@ -433,11 +414,31 @@
|
|||||||
target: install
|
target: install
|
||||||
when: capletsgit.changed
|
when: capletsgit.changed
|
||||||
|
|
||||||
|
- name: download and install bettercap ui
|
||||||
|
unarchive:
|
||||||
|
src: "{{ packages.bettercap.ui }}"
|
||||||
|
dest: /usr/local/share/bettercap/
|
||||||
|
remote_src: yes
|
||||||
|
mode: 0755
|
||||||
|
|
||||||
|
# to always have the bettercap webui available (because why not?)
|
||||||
|
- name: copy pwnagotchi-manual over pwnagotchi-auto caplet
|
||||||
|
ansible.builtin.copy:
|
||||||
|
src: /usr/local/share/bettercap/caplets/pwnagotchi-manual.cap
|
||||||
|
dest: /usr/local/share/bettercap/caplets/pwnagotchi-auto.cap
|
||||||
|
force: true
|
||||||
|
ignore_errors: true
|
||||||
|
|
||||||
- name: create /etc/pwnagotchi folder
|
- name: create /etc/pwnagotchi folder
|
||||||
file:
|
file:
|
||||||
path: /etc/pwnagotchi
|
path: /etc/pwnagotchi
|
||||||
state: directory
|
state: directory
|
||||||
|
|
||||||
|
- name: create log folder
|
||||||
|
file:
|
||||||
|
path: /home/pi/logs
|
||||||
|
state: directory
|
||||||
|
|
||||||
- name: check if user configuration exists
|
- name: check if user configuration exists
|
||||||
stat:
|
stat:
|
||||||
path: /etc/pwnagotchi/config.toml
|
path: /etc/pwnagotchi/config.toml
|
||||||
@ -450,19 +451,67 @@
|
|||||||
# Add your configuration overrides on this file any configuration changes done to default.toml will be lost!
|
# Add your configuration overrides on this file any configuration changes done to default.toml will be lost!
|
||||||
# Example:
|
# Example:
|
||||||
# ui.display.enabled = true
|
# ui.display.enabled = true
|
||||||
# ui.display.type = "waveshare_4"
|
# ui.display.type = "waveshare_2"
|
||||||
when: not user_config.stat.exists
|
when: not user_config.stat.exists
|
||||||
|
|
||||||
- name: Delete motd
|
|
||||||
file:
|
|
||||||
state: absent
|
|
||||||
path: /etc/motd
|
|
||||||
|
|
||||||
- name: Delete motd 10-uname
|
- name: Delete motd 10-uname
|
||||||
file:
|
file:
|
||||||
state: absent
|
state: absent
|
||||||
path: /etc/update-motd.d/10-uname
|
path: /etc/update-motd.d/10-uname
|
||||||
|
|
||||||
|
- name: enable ssh on boot
|
||||||
|
file:
|
||||||
|
path: /boot/ssh
|
||||||
|
state: touch
|
||||||
|
|
||||||
|
- name: adjust /boot/config.txt
|
||||||
|
lineinfile:
|
||||||
|
dest: /boot/config.txt
|
||||||
|
insertafter: EOF
|
||||||
|
line: '{{ item }}'
|
||||||
|
with_items: "{{system.boot_options}}"
|
||||||
|
|
||||||
|
- name: adjust /etc/modules
|
||||||
|
lineinfile:
|
||||||
|
dest: /etc/modules
|
||||||
|
insertafter: EOF
|
||||||
|
line: '{{ item }}'
|
||||||
|
with_items: "{{system.modules}}"
|
||||||
|
|
||||||
|
- name: change root partition
|
||||||
|
replace:
|
||||||
|
dest: /boot/cmdline.txt
|
||||||
|
backup: no
|
||||||
|
regexp: "root=PARTUUID=[a-zA-Z0-9\\-]+"
|
||||||
|
replace: "root=/dev/mmcblk0p2"
|
||||||
|
|
||||||
|
- name: configure /boot/cmdline.txt
|
||||||
|
lineinfile:
|
||||||
|
path: /boot/cmdline.txt
|
||||||
|
backrefs: True
|
||||||
|
state: present
|
||||||
|
backup: no
|
||||||
|
regexp: '(.*)$'
|
||||||
|
line: '\1 modules-load=dwc2,g_ether'
|
||||||
|
|
||||||
|
- name: Add pwnlog alias
|
||||||
|
lineinfile:
|
||||||
|
dest: /home/pi/.bashrc
|
||||||
|
line: "\nalias pwnlog='tail -f -n300 /etc/pwnagotchi/log/pwn*.log | sed --unbuffered \"s/,[[:digit:]]\\{3\\}\\]//g\" | cut -d \" \" -f 2-'"
|
||||||
|
insertafter: EOF
|
||||||
|
|
||||||
|
- name: Add pwnver alias
|
||||||
|
lineinfile:
|
||||||
|
dest: /home/pi/.bashrc
|
||||||
|
line: "\nalias pwnver='python3 -c \"import pwnagotchi as p; print(p.__version__)\"'"
|
||||||
|
insertafter: EOF
|
||||||
|
|
||||||
|
- name: Add pwnkill alias to restart pwnagotchi with a signal
|
||||||
|
lineinfile:
|
||||||
|
dest: /home/pi/.bashrc
|
||||||
|
line: "\nalias pwnkill='sudo killall -USR1 pwnagotchi'"
|
||||||
|
insertafter: EOF
|
||||||
|
|
||||||
- name: add firmware packages to hold
|
- name: add firmware packages to hold
|
||||||
dpkg_selections:
|
dpkg_selections:
|
||||||
name: "{{ item }}"
|
name: "{{ item }}"
|
||||||
@ -482,7 +531,16 @@
|
|||||||
enabled: true
|
enabled: true
|
||||||
state: stopped
|
state: stopped
|
||||||
with_items: "{{ services.enable }}"
|
with_items: "{{ services.enable }}"
|
||||||
register: enabled
|
|
||||||
|
#- name: remove golang build libraries
|
||||||
|
# file:
|
||||||
|
# state: absent
|
||||||
|
# path: /root/go
|
||||||
|
|
||||||
|
#- name: remove golang
|
||||||
|
# file:
|
||||||
|
# state: absent
|
||||||
|
# path: /usr/local/go
|
||||||
|
|
||||||
- name: make /root readable, becauase that's where all the files are
|
- name: make /root readable, becauase that's where all the files are
|
||||||
file:
|
file:
|
||||||
@ -496,65 +554,53 @@
|
|||||||
group: pi
|
group: pi
|
||||||
recurse: true
|
recurse: true
|
||||||
|
|
||||||
|
- name: remove unnecessary apt packages
|
||||||
|
apt:
|
||||||
|
name: "{{ packages.apt.remove }}"
|
||||||
|
state: absent
|
||||||
|
purge: yes
|
||||||
|
|
||||||
|
- name: remove dependencies that are no longer required
|
||||||
|
apt:
|
||||||
|
autoremove: yes
|
||||||
|
|
||||||
|
- name: clean apt cache
|
||||||
|
apt:
|
||||||
|
autoclean: true
|
||||||
|
|
||||||
|
- name: remove golang build libraries
|
||||||
|
file:
|
||||||
|
state: absent
|
||||||
|
path: /root/go
|
||||||
|
|
||||||
- name: remove pre-collected packages zip
|
- name: remove pre-collected packages zip
|
||||||
file:
|
file:
|
||||||
path: /root/go_pkgs.tgz
|
path: /root/go_pkgs.tgz
|
||||||
state: absent
|
state: absent
|
||||||
|
|
||||||
- name: remove /root/go folder
|
- name: remove golang
|
||||||
file:
|
|
||||||
state: absent
|
|
||||||
path: /root/go
|
|
||||||
|
|
||||||
- name: remove /usr/local/go folder
|
|
||||||
file:
|
file:
|
||||||
state: absent
|
state: absent
|
||||||
path: /usr/local/go
|
path: /usr/local/go
|
||||||
|
|
||||||
- name: remove pip cache
|
- name: remove /root/.cache (pip cache)
|
||||||
file:
|
file:
|
||||||
state: absent
|
state: absent
|
||||||
path: /root/.cache/pip
|
path: /root/.cache
|
||||||
|
|
||||||
- name: remove ssh keys
|
- name: remove ssh keys
|
||||||
file:
|
file:
|
||||||
state: absent
|
state: absent
|
||||||
path: "{{ item }}"
|
path: "{{ item }}"
|
||||||
with_fileglob:
|
with_fileglob:
|
||||||
- "/etc/ssh/ssh_host*_key*"
|
- "/etc/ssh/ssh_host*_key*"
|
||||||
|
|
||||||
- name: regenerate ssh keys
|
- name: regenerate ssh keys
|
||||||
shell: "dpkg-reconfigure openssh-server"
|
shell: "dpkg-reconfigure openssh-server"
|
||||||
args:
|
args:
|
||||||
executable: /bin/bash
|
executable: /bin/bash
|
||||||
|
|
||||||
# Now we remove packages
|
|
||||||
- name: remove unnecessary apt packages
|
|
||||||
apt:
|
|
||||||
name: "{{ packages.apt.remove }}"
|
|
||||||
state: absent
|
|
||||||
purge: yes
|
|
||||||
register: removed
|
|
||||||
|
|
||||||
- name: remove dependencies that are no longer required
|
|
||||||
apt:
|
|
||||||
autoremove: yes
|
|
||||||
when: removed.changed
|
|
||||||
|
|
||||||
- name: install rpi-sys-mods again?
|
|
||||||
apt:
|
|
||||||
state: present
|
|
||||||
name: raspberrypi-sys-mods
|
|
||||||
update_cache: yes
|
|
||||||
install_recommends: no
|
|
||||||
|
|
||||||
- name: clean apt cache
|
|
||||||
apt:
|
|
||||||
autoclean: true
|
|
||||||
when: removed.changed
|
|
||||||
|
|
||||||
handlers:
|
handlers:
|
||||||
- name: reload systemd services
|
- name: reload systemd services
|
||||||
systemd:
|
systemd:
|
||||||
daemon_reload: yes
|
daemon_reload: yes
|
||||||
when: enabled.changed
|
|
28
builder/data/32bit/usr/bin/bettercap-launcher
Executable file
28
builder/data/32bit/usr/bin/bettercap-launcher
Executable file
@ -0,0 +1,28 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
source /usr/bin/pwnlib
|
||||||
|
|
||||||
|
# we need to decrypt something
|
||||||
|
if is_crypted_mode; then
|
||||||
|
while ! is_decrypted; do
|
||||||
|
echo "Waiting for decryption..."
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# check if wifi driver is bugged
|
||||||
|
if ! check_brcm; then
|
||||||
|
if ! reload_brcm; then
|
||||||
|
echo "Could not reload wifi driver. Reboot"
|
||||||
|
reboot
|
||||||
|
fi
|
||||||
|
sleep 10
|
||||||
|
fi
|
||||||
|
|
||||||
|
# start wlan0mon
|
||||||
|
start_monitor_interface
|
||||||
|
|
||||||
|
if is_auto_mode_no_delete; then
|
||||||
|
/usr/local/bin/bettercap -no-colors -caplet pwnagotchi-auto -iface wlan0mon
|
||||||
|
else
|
||||||
|
/usr/local/bin/bettercap -no-colors -caplet pwnagotchi-manual -iface wlan0mon
|
||||||
|
fi
|
149
builder/data/32bit/usr/bin/decryption-webserver
Executable file
149
builder/data/32bit/usr/bin/decryption-webserver
Executable file
@ -0,0 +1,149 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
from http.server import HTTPServer, BaseHTTPRequestHandler
|
||||||
|
from urllib.parse import parse_qsl
|
||||||
|
|
||||||
|
|
||||||
|
_HTML_FORM_TEMPLATE = """
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Decryption</title>
|
||||||
|
<style>
|
||||||
|
body {{ text-align: center; padding: 150px; }}
|
||||||
|
h1 {{ font-size: 50px; }}
|
||||||
|
body {{ font: 20px Helvetica, sans-serif; color: #333; }}
|
||||||
|
article {{ display: block; text-align: center; width: 650px; margin: 0 auto;}}
|
||||||
|
input {{
|
||||||
|
padding: 12px 20px;
|
||||||
|
margin: 8px 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
}}
|
||||||
|
input[type=password] {{
|
||||||
|
width: 75%;
|
||||||
|
font-size: 24px;
|
||||||
|
}}
|
||||||
|
input[type=submit] {{
|
||||||
|
cursor: pointer;
|
||||||
|
width: 75%;
|
||||||
|
}}
|
||||||
|
input[type=submit]:hover {{
|
||||||
|
background-color: #d9d9d9;
|
||||||
|
}}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<article>
|
||||||
|
<h1>Decryption</h1>
|
||||||
|
<p>Some of your files are encrypted.</p>
|
||||||
|
<p>Please provide the decryption password.</p>
|
||||||
|
<div>
|
||||||
|
<form action="/set-password" method="POST">
|
||||||
|
{password_fields}
|
||||||
|
<input type="submit" value="Submit">
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"""
|
||||||
|
|
||||||
|
POST_RESPONSE = """
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<style>
|
||||||
|
/* Center the loader */
|
||||||
|
#loader {
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
top: 50%;
|
||||||
|
z-index: 1;
|
||||||
|
width: 150px;
|
||||||
|
height: 150px;
|
||||||
|
margin: -75px 0 0 -75px;
|
||||||
|
border: 16px solid #f3f3f3;
|
||||||
|
border-radius: 50%;
|
||||||
|
border-top: 16px solid #3498db;
|
||||||
|
width: 120px;
|
||||||
|
height: 120px;
|
||||||
|
-webkit-animation: spin 2s linear infinite;
|
||||||
|
animation: spin 2s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@-webkit-keyframes spin {
|
||||||
|
0% { -webkit-transform: rotate(0deg); }
|
||||||
|
100% { -webkit-transform: rotate(360deg); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes spin {
|
||||||
|
0% { transform: rotate(0deg); }
|
||||||
|
100% { transform: rotate(360deg); }
|
||||||
|
}
|
||||||
|
|
||||||
|
#myDiv {
|
||||||
|
display: none;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script type="text/javascript">
|
||||||
|
|
||||||
|
function checkPwnagotchi() {
|
||||||
|
var target = 'http://' + document.location.hostname + ':8080/';
|
||||||
|
var xhr = new XMLHttpRequest();
|
||||||
|
xhr.open('GET', target);
|
||||||
|
xhr.onreadystatechange = function () {
|
||||||
|
if (xhr.readyState == 4) {
|
||||||
|
if (xhr.status == 200 || xhr.status == 401) {
|
||||||
|
window.location.replace(target);
|
||||||
|
}else{
|
||||||
|
setTimeout(checkPwnagotchi, 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
xhr.send();
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout(checkPwnagotchi, 1000);
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body style="margin:0;">
|
||||||
|
|
||||||
|
<div id="loader"></div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"""
|
||||||
|
|
||||||
|
HTML_FORM = None
|
||||||
|
|
||||||
|
|
||||||
|
class SimpleHTTPRequestHandler(BaseHTTPRequestHandler):
|
||||||
|
|
||||||
|
def do_GET(self):
|
||||||
|
self.send_response(200)
|
||||||
|
self.end_headers()
|
||||||
|
self.wfile.write(HTML_FORM.encode())
|
||||||
|
|
||||||
|
def do_POST(self):
|
||||||
|
content_length = int(self.headers['Content-Length'])
|
||||||
|
body = self.rfile.read(content_length)
|
||||||
|
for mapping, password in parse_qsl(body.decode('UTF-8')):
|
||||||
|
with open('/tmp/.pwnagotchi-secret-{}'.format(mapping), 'wt') as pwfile:
|
||||||
|
pwfile.write(password)
|
||||||
|
self.send_response(200)
|
||||||
|
self.end_headers()
|
||||||
|
self.wfile.write(POST_RESPONSE.encode())
|
||||||
|
|
||||||
|
|
||||||
|
with open('/root/.pwnagotchi-crypted') as crypted_file:
|
||||||
|
mappings = [line.split()[0] for line in crypted_file.readlines()]
|
||||||
|
fields = ''.join(['<label for="{m}">Passphrase for {m}:</label>\n<input type="password" id="{m}" name="{m}" value=""><br>'.format(m=m)
|
||||||
|
for m in mappings])
|
||||||
|
HTML_FORM = _HTML_FORM_TEMPLATE.format(password_fields=fields)
|
||||||
|
|
||||||
|
httpd = HTTPServer(('0.0.0.0', 80), SimpleHTTPRequestHandler)
|
||||||
|
httpd.serve_forever()
|
@ -10,8 +10,7 @@ if is_crypted_mode; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if is_auto_mode; then
|
if is_auto_mode; then
|
||||||
/usr/local/src/pwnagotchi/env/bin/pwnagotchi
|
/usr/local/bin/pwnagotchi
|
||||||
systemctl restart bettercap
|
|
||||||
else
|
else
|
||||||
/usr/local/src/pwnagotchi/env/bin/pwnagotchi --manual
|
/usr/local/bin/pwnagotchi --manual
|
||||||
fi
|
fi
|
@ -1,5 +1,13 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# check if brcm is stuck
|
||||||
|
check_brcm() {
|
||||||
|
if [[ "$(journalctl -n10 -k --since -5m | grep -c 'brcmf_cfg80211_nexmon_set_channel.*Set Channel failed')" -ge 5 ]]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
# reload mod
|
# reload mod
|
||||||
reload_brcm() {
|
reload_brcm() {
|
||||||
if ! modprobe -r brcmfmac; then
|
if ! modprobe -r brcmfmac; then
|
||||||
@ -18,10 +26,8 @@ reload_brcm() {
|
|||||||
start_monitor_interface() {
|
start_monitor_interface() {
|
||||||
rfkill unblock all
|
rfkill unblock all
|
||||||
ifconfig wlan0 up
|
ifconfig wlan0 up
|
||||||
sleep 3
|
|
||||||
iw dev wlan0 set power_save off
|
iw dev wlan0 set power_save off
|
||||||
iw phy "$(iw phy | head -1 | cut -d" " -f2)" interface add wlan0mon type monitor
|
iw phy "$(iw phy | head -1 | cut -d" " -f2)" interface add wlan0mon type monitor
|
||||||
sleep 2
|
|
||||||
rfkill unblock all
|
rfkill unblock all
|
||||||
ifconfig wlan0 down
|
ifconfig wlan0 down
|
||||||
ifconfig wlan0mon up
|
ifconfig wlan0mon up
|
||||||
@ -35,7 +41,7 @@ stop_monitor_interface() {
|
|||||||
ifconfig wlan0 up
|
ifconfig wlan0 up
|
||||||
}
|
}
|
||||||
|
|
||||||
# returns 0 if the specified network interface is up
|
# returns 0 if the specificed network interface is up
|
||||||
is_interface_up() {
|
is_interface_up() {
|
||||||
if grep -qi 'up' /sys/class/net/"$1"/operstate; then
|
if grep -qi 'up' /sys/class/net/"$1"/operstate; then
|
||||||
return 0
|
return 0
|
@ -0,0 +1,36 @@
|
|||||||
|
_show_complete()
|
||||||
|
{
|
||||||
|
local cur opts node_names all_options opt_line
|
||||||
|
all_options="
|
||||||
|
pwnagotchi -h --help -C --config -U --user-config --manual --skip-session --clear --debug --version --print-config --check-update --donate {plugins,google}
|
||||||
|
pwnagotchi plugins -h --help {list,install,enable,disable,uninstall,update,upgrade}
|
||||||
|
pwnagotchi plugins list -i --installed -h --help
|
||||||
|
pwnagotchi plugins install -h --help
|
||||||
|
pwnagotchi plugins uninstall -h --help
|
||||||
|
pwnagotchi plugins enable -h --help
|
||||||
|
pwnagotchi plugins disable -h --help
|
||||||
|
pwnagotchi plugins update -h --help
|
||||||
|
pwnagotchi plugins upgrade -h --help
|
||||||
|
pwnagotchi google -h --help {login,refresh}
|
||||||
|
pwnagotchi google login -h --help
|
||||||
|
pwnagotchi google refresh -h --help
|
||||||
|
"
|
||||||
|
COMPREPLY=()
|
||||||
|
cur="${COMP_WORDS[COMP_CWORD]}"
|
||||||
|
# shellcheck disable=SC2124
|
||||||
|
cmd="${COMP_WORDS[@]:0:${#COMP_WORDS[@]}-1}"
|
||||||
|
opt_line="$(grep -m1 "$cmd" <<<"$all_options")"
|
||||||
|
if [[ ${cur} == -* ]] ; then
|
||||||
|
opts="$(echo "$opt_line" | tr ' ' '\n' | awk '/^ *-/{gsub("[^a-zA-Z0-9-]","",$1);print $1}')"
|
||||||
|
# shellcheck disable=SC2207
|
||||||
|
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# shellcheck disable=SC2086
|
||||||
|
opts="$(echo $opt_line | grep -Po '{\K[^}]+' | tr ',' '\n')"
|
||||||
|
# shellcheck disable=SC2207
|
||||||
|
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
|
||||||
|
}
|
||||||
|
|
||||||
|
complete -F _show_complete pwnagotchi
|
26
builder/data/64bit/etc/dphys-swapfile
Normal file
26
builder/data/64bit/etc/dphys-swapfile
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
# /etc/dphys-swapfile - user settings for dphys-swapfile package
|
||||||
|
# author Neil Franklin, last modification 2010.05.05
|
||||||
|
# copyright ETH Zuerich Physics Departement
|
||||||
|
# use under either modified/non-advertising BSD or GPL license
|
||||||
|
|
||||||
|
# this file is sourced with . so full normal sh syntax applies
|
||||||
|
|
||||||
|
# the default settings are added as commented out CONF_*=* lines
|
||||||
|
|
||||||
|
|
||||||
|
# where we want the swapfile to be, this is the default
|
||||||
|
#CONF_SWAPFILE=/var/swap
|
||||||
|
|
||||||
|
# set size to absolute value, leaving empty (default) then uses computed value
|
||||||
|
# you most likely don't want this, unless you have an special disk situation
|
||||||
|
CONF_SWAPSIZE=2048
|
||||||
|
|
||||||
|
# set size to computed value, this times RAM size, dynamically adapts,
|
||||||
|
# guarantees that there is enough swap without wasting disk space on excess
|
||||||
|
#CONF_SWAPFACTOR=2
|
||||||
|
|
||||||
|
# restrict size (computed and absolute!) to maximally this limit
|
||||||
|
# can be set to empty for no limit, but beware of filled partitions!
|
||||||
|
# this is/was a (outdated?) 32bit kernel limit (in MBytes), do not overrun it
|
||||||
|
# but is also sensible on 64bit to prevent filling /var or even / partition
|
||||||
|
#CONF_MAXSWAP=2048
|
13
builder/data/64bit/etc/systemd/system/bettercap.service
Normal file
13
builder/data/64bit/etc/systemd/system/bettercap.service
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=bettercap api.rest service.
|
||||||
|
Documentation=https://bettercap.org
|
||||||
|
Wants=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
ExecStart=/usr/bin/bettercap-launcher
|
||||||
|
Restart=always
|
||||||
|
RestartSec=30
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
20
builder/data/64bit/etc/systemd/system/bluetooth.service
Normal file
20
builder/data/64bit/etc/systemd/system/bluetooth.service
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Bluetooth service
|
||||||
|
Documentation=man:bluetoothd(8)
|
||||||
|
ConditionPathIsDirectory=/sys/class/bluetooth
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=dbus
|
||||||
|
BusName=org.bluez
|
||||||
|
ExecStart=/usr/libexec/bluetooth/bluetoothd --noplugin=sap,a2dp
|
||||||
|
NotifyAccess=main
|
||||||
|
#WatchdogSec=10
|
||||||
|
#Restart=on-failure
|
||||||
|
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
|
||||||
|
LimitNPROC=1
|
||||||
|
ProtectHome=true
|
||||||
|
ProtectSystem=full
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=bluetooth.target
|
||||||
|
Alias=dbus-org.bluez.service
|
20
builder/data/64bit/etc/systemd/system/pwnagotchi.service
Normal file
20
builder/data/64bit/etc/systemd/system/pwnagotchi.service
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=pwnagotchi Deep Reinforcement Learning instrumenting bettercap for WiFI pwning.
|
||||||
|
Documentation=https://pwnagotchi.org
|
||||||
|
Wants=network.target
|
||||||
|
After=pwngrid-peer.service
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
WorkingDirectory=~
|
||||||
|
ExecStart=/usr/bin/pwnagotchi-launcher
|
||||||
|
ExecStopPost=/usr/bin/bash -c "if egrep -qi 'personality.clear_on_exit[ =]*true' /etc/pwnagotchi/config.toml ; then /usr/local/bin/pwnagotchi --clear; fi"
|
||||||
|
Restart=always
|
||||||
|
RestartSec=30
|
||||||
|
TasksMax=infinity
|
||||||
|
LimitNPROC=infinity
|
||||||
|
StandardOutput=null
|
||||||
|
StandardError=null
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
16
builder/data/64bit/etc/systemd/system/pwngrid-peer.service
Normal file
16
builder/data/64bit/etc/systemd/system/pwngrid-peer.service
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=pwngrid peer service.
|
||||||
|
Documentation=https://pwnagotchi.ai
|
||||||
|
Wants=network.target
|
||||||
|
After=bettercap.service
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Environment=LD_PRELOAD=/usr/local/lib/libpcap.so.1
|
||||||
|
Environment=LD_LIBRARY_PATH=/usr/local/lib
|
||||||
|
Type=simple
|
||||||
|
ExecStart=/usr/local/bin/pwngrid -keys /etc/pwnagotchi -peers /root/peers -address 127.0.0.1:8666 -client-token /root/.api-enrollment.json -wait -log /etc/pwnagotchi/log/pwngrid-peer.log -iface wlan0mon
|
||||||
|
Restart=always
|
||||||
|
RestartSec=30
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
@ -2,7 +2,7 @@
|
|||||||
_hostname=$(hostname)
|
_hostname=$(hostname)
|
||||||
_version=$(cut -d"'" -f2 < /usr/local/lib/python3.11/dist-packages/pwnagotchi/_version.py)
|
_version=$(cut -d"'" -f2 < /usr/local/lib/python3.11/dist-packages/pwnagotchi/_version.py)
|
||||||
echo
|
echo
|
||||||
echo "(◕‿‿◕) $_hostname"
|
echo "(☉_☉ ) $_hostname"
|
||||||
echo
|
echo
|
||||||
echo " Hi! I'm a pwnagotchi $_version, please take good care of me!"
|
echo " Hi! I'm a pwnagotchi $_version, please take good care of me!"
|
||||||
echo " Here are some basic things you need to know to raise me properly!"
|
echo " Here are some basic things you need to know to raise me properly!"
|
||||||
@ -20,7 +20,6 @@ echo " I'm managed by systemd. Here are some basic commands."
|
|||||||
echo
|
echo
|
||||||
echo " If you want to know what I'm doing, you can check my logs with the command"
|
echo " If you want to know what I'm doing, you can check my logs with the command"
|
||||||
echo " - pwnlog"
|
echo " - pwnlog"
|
||||||
echo " - sudo pwnagotchi --wizard, to help set up a config.toml"
|
|
||||||
echo " - sudo pwnagotchi --version, to check the current version"
|
echo " - sudo pwnagotchi --version, to check the current version"
|
||||||
echo " - sudo pwnagotchi --donate, to see how you can donate to this project"
|
echo " - sudo pwnagotchi --donate, to see how you can donate to this project"
|
||||||
echo " - sudo pwnagotchi --check-update, to see if there is a new version available"
|
echo " - sudo pwnagotchi --check-update, to see if there is a new version available"
|
@ -1,8 +1,8 @@
|
|||||||
packer {
|
packer {
|
||||||
required_plugins {
|
required_plugins {
|
||||||
arm-image = {
|
arm = {
|
||||||
source = "github.com/solo-io/arm-image"
|
version = "1.0.0"
|
||||||
version = ">= 0.0.1"
|
source = "github.com/cdecoux/builder-arm"
|
||||||
}
|
}
|
||||||
ansible = {
|
ansible = {
|
||||||
source = "github.com/hashicorp/ansible"
|
source = "github.com/hashicorp/ansible"
|
||||||
@ -19,22 +19,44 @@ variable "pwn_version" {
|
|||||||
type = string
|
type = string
|
||||||
}
|
}
|
||||||
|
|
||||||
source "arm-image" "rpi64-pwnagotchi" {
|
source "arm" "rpi64-pwnagotchi" {
|
||||||
image_type = "raspberrypi"
|
file_checksum_url = "https://downloads.raspberrypi.org/raspios_lite_arm64/images/raspios_lite_arm64-2023-12-11/2023-12-11-raspios-bookworm-arm64-lite.img.xz.sha256"
|
||||||
iso_url = "https://downloads.raspberrypi.com/raspios_lite_arm64/images/raspios_lite_arm64-2024-07-04/2024-07-04-raspios-bookworm-arm64-lite.img.xz"
|
file_urls = ["https://downloads.raspberrypi.org/raspios_lite_arm64/images/raspios_lite_arm64-2023-12-11/2023-12-11-raspios-bookworm-arm64-lite.img.xz"]
|
||||||
iso_checksum = "sha256:43d150e7901583919e4eb1f0fa83fe0363af2d1e9777a5bb707d696d535e2599"
|
file_checksum_type = "sha256"
|
||||||
output_filename = "../../../pwnagotchi-64bit.img"
|
file_target_extension = "xz"
|
||||||
qemu_binary = "qemu-aarch64-static"
|
file_unarchive_cmd = ["unxz", "$ARCHIVE_PATH"]
|
||||||
target_image_size = 19969908736
|
image_path = "../../../pwnagotchi-64bit.img"
|
||||||
image_mounts = ["/boot/firmware","/"]
|
qemu_binary_source_path = "/usr/libexec/qemu-binfmt/aarch64-binfmt-P"
|
||||||
|
qemu_binary_destination_path = "/usr/libexec/qemu-binfmt/aarch64-binfmt-P"
|
||||||
|
image_build_method = "resize"
|
||||||
|
image_size = "9G"
|
||||||
|
image_type = "dos"
|
||||||
|
image_partitions {
|
||||||
|
name = "boot"
|
||||||
|
type = "c"
|
||||||
|
start_sector = "8192"
|
||||||
|
filesystem = "fat"
|
||||||
|
size = "256M"
|
||||||
|
mountpoint = "/boot/firmware"
|
||||||
|
}
|
||||||
|
image_partitions {
|
||||||
|
name = "root"
|
||||||
|
type = "83"
|
||||||
|
start_sector = "532480"
|
||||||
|
filesystem = "ext4"
|
||||||
|
size = "0"
|
||||||
|
mountpoint = "/"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# a build block invokes sources and runs provisioning steps on them. The
|
# a build block invokes sources and runs provisioning steps on them. The
|
||||||
# documentation for build blocks can be found here:
|
# documentation for build blocks can be found here:
|
||||||
# https://www.packer.io/docs/from-1.5/blocks/build
|
# https://www.packer.io/docs/from-1.5/blocks/build
|
||||||
build {
|
build {
|
||||||
name = "Raspberry Pi 64 Pwnagotchi"
|
name = "Raspberry Pi 64 Pwnagotchi"
|
||||||
sources = ["source.arm-image.rpi64-pwnagotchi"]
|
sources = ["source.arm.rpi64-pwnagotchi"]
|
||||||
|
|
||||||
provisioner "file" {
|
provisioner "file" {
|
||||||
destination = "/usr/bin/"
|
destination = "/usr/bin/"
|
||||||
@ -51,13 +73,7 @@ build {
|
|||||||
provisioner "shell" {
|
provisioner "shell" {
|
||||||
inline = ["chmod +x /usr/bin/*"]
|
inline = ["chmod +x /usr/bin/*"]
|
||||||
}
|
}
|
||||||
provisioner "shell" {
|
|
||||||
inline = ["mkdir -p /usr/local/src/pwnagotchi"]
|
|
||||||
}
|
|
||||||
provisioner "file" {
|
|
||||||
destination = "/usr/local/src/pwnagotchi/"
|
|
||||||
source = "../"
|
|
||||||
}
|
|
||||||
provisioner "file" {
|
provisioner "file" {
|
||||||
destination = "/etc/systemd/system/"
|
destination = "/etc/systemd/system/"
|
||||||
sources = [
|
sources = [
|
||||||
@ -79,6 +95,6 @@ build {
|
|||||||
provisioner "ansible-local" {
|
provisioner "ansible-local" {
|
||||||
command = "ANSIBLE_FORCE_COLOR=1 PYTHONUNBUFFERED=1 PWN_VERSION=${var.pwn_version} PWN_HOSTNAME=${var.pwn_hostname} ansible-playbook"
|
command = "ANSIBLE_FORCE_COLOR=1 PYTHONUNBUFFERED=1 PWN_VERSION=${var.pwn_version} PWN_HOSTNAME=${var.pwn_hostname} ansible-playbook"
|
||||||
extra_arguments = ["--extra-vars \"ansible_python_interpreter=/usr/bin/python3\""]
|
extra_arguments = ["--extra-vars \"ansible_python_interpreter=/usr/bin/python3\""]
|
||||||
playbook_file = "raspberrypi64.yml"
|
playbook_file = "data/64bit/raspberrypi64.yml"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -5,12 +5,22 @@
|
|||||||
become: true
|
become: true
|
||||||
vars:
|
vars:
|
||||||
kernel:
|
kernel:
|
||||||
min: "6.6"
|
min: "6.1"
|
||||||
full: "6.6.31+rpt-rpi-v8"
|
full: "6.1.0-rpi8-rpi-v8"
|
||||||
full_pi5: "6.6.31+rpt-rpi-2712"
|
full_pi5: "6.1.0-rpi8-rpi-2712"
|
||||||
pwnagotchi:
|
pwnagotchi:
|
||||||
hostname: "{{ lookup('env', 'PWN_HOSTNAME') | default('pwnagotchi', true) }}"
|
hostname: "{{ lookup('env', 'PWN_HOSTNAME') | default('pwnagotchi', true) }}"
|
||||||
version: "{{ lookup('env', 'PWN_VERSION') | default('pwnagotchi', true) }}"
|
version: "{{ lookup('env', 'PWN_VERSION') | default('pwnagotchi', true) }}"
|
||||||
|
system:
|
||||||
|
boot_options:
|
||||||
|
- "dtoverlay=dwc2"
|
||||||
|
- "dtoverlay=spi1-3cs"
|
||||||
|
- "dtparam=i2c1=on"
|
||||||
|
- "dtparam=i2c_arm=on"
|
||||||
|
- "dtparam=spi=on"
|
||||||
|
- "gpu_mem=16"
|
||||||
|
modules:
|
||||||
|
- "i2c-dev"
|
||||||
services:
|
services:
|
||||||
enable:
|
enable:
|
||||||
- bettercap.service
|
- bettercap.service
|
||||||
@ -27,11 +37,10 @@
|
|||||||
packages:
|
packages:
|
||||||
caplets:
|
caplets:
|
||||||
source: "https://github.com/jayofelony/caplets.git"
|
source: "https://github.com/jayofelony/caplets.git"
|
||||||
branch: "lite" # or master
|
|
||||||
bettercap:
|
bettercap:
|
||||||
source: "https://github.com/jayofelony/bettercap.git"
|
source: "https://github.com/jayofelony/bettercap.git"
|
||||||
url: "https://github.com/jayofelony/bettercap/releases/download/2.32.4/bettercap-2.32.4.zip"
|
url: "https://github.com/jayofelony/bettercap/releases/download/2.32.4/bettercap-2.32.4.zip"
|
||||||
branch: "lite" # or master
|
ui: "https://github.com/bettercap/ui/releases/download/v1.3.0/ui.zip"
|
||||||
pwngrid:
|
pwngrid:
|
||||||
source: "https://github.com/jayofelony/pwngrid.git"
|
source: "https://github.com/jayofelony/pwngrid.git"
|
||||||
url: "https://github.com/jayofelony/pwngrid/releases/download/v1.10.5/pwngrid-1.10.5-aarch64.zip"
|
url: "https://github.com/jayofelony/pwngrid/releases/download/v1.10.5/pwngrid-1.10.5-aarch64.zip"
|
||||||
@ -52,17 +61,22 @@
|
|||||||
- libpcap0.8-dbg
|
- libpcap0.8-dbg
|
||||||
- libpcap0.8-dev
|
- libpcap0.8-dev
|
||||||
remove:
|
remove:
|
||||||
|
- avahi-daemon
|
||||||
- dhpys-swapfile
|
- dhpys-swapfile
|
||||||
|
- libcurl-ocaml-dev
|
||||||
|
- libssl-ocaml-dev
|
||||||
- nfs-common
|
- nfs-common
|
||||||
- triggerhappy
|
- triggerhappy
|
||||||
|
- wpasupplicant
|
||||||
install:
|
install:
|
||||||
- aircrack-ng
|
- aircrack-ng
|
||||||
- autoconf
|
- autoconf
|
||||||
|
- bc
|
||||||
- bison
|
- bison
|
||||||
- bluez
|
- bluez
|
||||||
- bluez-tools
|
|
||||||
- build-essential
|
- build-essential
|
||||||
- curl
|
- curl
|
||||||
|
- dkms
|
||||||
- dphys-swapfile
|
- dphys-swapfile
|
||||||
- fbi
|
- fbi
|
||||||
- firmware-atheros
|
- firmware-atheros
|
||||||
@ -71,40 +85,85 @@
|
|||||||
- firmware-misc-nonfree
|
- firmware-misc-nonfree
|
||||||
- firmware-realtek
|
- firmware-realtek
|
||||||
- flex
|
- flex
|
||||||
|
- fonts-dejavu
|
||||||
|
- fonts-dejavu-core
|
||||||
|
- fonts-dejavu-extra
|
||||||
|
- fonts-freefont-ttf
|
||||||
- g++
|
- g++
|
||||||
- gawk
|
- gawk
|
||||||
- gcc-arm-none-eabi
|
- gcc-arm-none-eabi
|
||||||
- git
|
- git
|
||||||
|
- hcxtools
|
||||||
|
- libatlas-base-dev
|
||||||
|
- libavcodec59
|
||||||
|
- libavformat59
|
||||||
|
- libblas-dev
|
||||||
|
- libbluetooth-dev
|
||||||
|
- libbz2-dev
|
||||||
|
- libc-ares-dev
|
||||||
- libc6-dev
|
- libc6-dev
|
||||||
|
- libcap-dev
|
||||||
- libcurl-ocaml-dev
|
- libcurl-ocaml-dev
|
||||||
- libdbus-1-dev
|
- libdbus-1-dev
|
||||||
- libdbus-glib-1-dev
|
- libdbus-glib-1-dev
|
||||||
|
- libeigen3-dev
|
||||||
|
- libelf-dev
|
||||||
|
- libffi-dev
|
||||||
- libfl-dev
|
- libfl-dev
|
||||||
|
- libfuse-dev
|
||||||
|
- libgdbm-dev
|
||||||
|
- libgl1-mesa-glx
|
||||||
- libgmp3-dev
|
- libgmp3-dev
|
||||||
|
- libgstreamer1.0-0
|
||||||
|
- libhdf5-dev
|
||||||
|
- liblapack-dev
|
||||||
|
- libncursesw5-dev
|
||||||
- libnetfilter-queue-dev
|
- libnetfilter-queue-dev
|
||||||
|
- libopenblas-dev
|
||||||
|
- libopenjp2-7
|
||||||
|
- libopenmpi-dev
|
||||||
|
- libopenmpi3
|
||||||
- libpcap-dev
|
- libpcap-dev
|
||||||
#- libraspberrypi-bin ## seems to be provided by raspi-utils now
|
- libraspberrypi-bin
|
||||||
- libraspberrypi-dev
|
- libraspberrypi-dev
|
||||||
- libraspberrypi-doc
|
- libraspberrypi-doc
|
||||||
- libraspberrypi0
|
- libraspberrypi0
|
||||||
|
- libsqlite3-dev
|
||||||
- libssl-dev
|
- libssl-dev
|
||||||
- libssl-ocaml-dev
|
- libssl-ocaml-dev
|
||||||
|
- libswscale5
|
||||||
|
- libtiff6
|
||||||
- libtool
|
- libtool
|
||||||
- libusb-1.0-0-dev
|
- libusb-1.0-0-dev
|
||||||
|
- lsof
|
||||||
- make
|
- make
|
||||||
- ntp
|
- python3-dbus
|
||||||
- pkg-config
|
- python3-flask
|
||||||
- python3-dev
|
- python3-flask-cors
|
||||||
|
- python3-flaskext.wtf
|
||||||
|
- python3-gast
|
||||||
|
- python3-pil
|
||||||
- python3-pip
|
- python3-pip
|
||||||
|
- python3-pycryptodome
|
||||||
|
- python3-requests
|
||||||
|
- python3-scapy
|
||||||
- python3-setuptools
|
- python3-setuptools
|
||||||
- python3-smbus
|
- python3-smbus
|
||||||
|
- python3-smbus2
|
||||||
|
- python3-spidev
|
||||||
|
- python3-tweepy
|
||||||
|
- python3-werkzeug
|
||||||
|
- python3-yaml
|
||||||
- qpdf
|
- qpdf
|
||||||
- raspberrypi-kernel-headers
|
- raspberrypi-kernel-headers
|
||||||
- raspberrypi-sys-mods
|
|
||||||
- rsync
|
- rsync
|
||||||
|
- screen
|
||||||
- tcpdump
|
- tcpdump
|
||||||
- texinfo
|
- texinfo
|
||||||
|
- time
|
||||||
|
- tk-dev
|
||||||
- unzip
|
- unzip
|
||||||
|
- vim
|
||||||
- wget
|
- wget
|
||||||
- wl
|
- wl
|
||||||
- xxd
|
- xxd
|
||||||
@ -117,42 +176,28 @@
|
|||||||
- name: install packages
|
- name: install packages
|
||||||
apt:
|
apt:
|
||||||
name: "{{ packages.apt.install }}"
|
name: "{{ packages.apt.install }}"
|
||||||
state: latest
|
state: present
|
||||||
update_cache: yes
|
update_cache: yes
|
||||||
install_recommends: no
|
install_recommends: false
|
||||||
|
|
||||||
- name: update pip3, setuptools, wheel
|
|
||||||
shell: "python3 -m pip install --upgrade pip setuptools wheel --break-system-packages"
|
|
||||||
args:
|
|
||||||
executable: /bin/bash
|
|
||||||
chdir: /usr/local/src
|
|
||||||
|
|
||||||
- name: build pwnagotchi wheel
|
|
||||||
command: "pip3 install . --no-cache-dir --break-system-packages"
|
|
||||||
args:
|
|
||||||
chdir: /usr/local/src/pwnagotchi
|
|
||||||
|
|
||||||
- name: remove pwnagotchi folder
|
|
||||||
file:
|
|
||||||
state: absent
|
|
||||||
path: /usr/local/src/pwnagotchi
|
|
||||||
|
|
||||||
# Now we set up /boot/firmware
|
# Now we set up /boot/firmware
|
||||||
- name: Create pi user
|
- name: Create pi user
|
||||||
copy:
|
copy:
|
||||||
dest: /boot/firmware/userconf
|
dest: /boot/firmware/userconf
|
||||||
content: |
|
content: |
|
||||||
pi:$5$733Efsksay$SEFUKemv8FaNAu6X4GUfxdSzSDh6PbpOcdtNe5b7Nt0
|
pi:$6$3jNr0GA9KIyt4hmM$efeVIopdMQ8DGgEPCWWlbx3mJJNAYci1lEXGdlky0xPyjqwKNbwTL5SrCcpb4144C4IvzWjn7Iv.QjqmU7iyT/
|
||||||
|
|
||||||
- name: enable ssh on boot
|
- name: enable ssh on boot
|
||||||
file:
|
file:
|
||||||
path: /boot/firmware/ssh
|
path: /boot/firmware/ssh
|
||||||
state: touch
|
state: touch
|
||||||
|
|
||||||
- name: remove current rc.local
|
- name: adjust /boot/firmware/config.txt
|
||||||
file:
|
lineinfile:
|
||||||
path: /etc/rc.local
|
dest: /boot/firmware/config.txt
|
||||||
state: absent
|
insertafter: EOF
|
||||||
|
line: '{{ item }}'
|
||||||
|
with_items: "{{ system.boot_options }}"
|
||||||
|
|
||||||
- name: change root partition
|
- name: change root partition
|
||||||
replace:
|
replace:
|
||||||
@ -170,35 +215,6 @@
|
|||||||
regexp: '(.*)$'
|
regexp: '(.*)$'
|
||||||
line: '\1 modules-load=dwc2,g_ether'
|
line: '\1 modules-load=dwc2,g_ether'
|
||||||
|
|
||||||
- name: setup /boot/firmware/config.txt
|
|
||||||
blockinfile:
|
|
||||||
path: /boot/firmware/config.txt
|
|
||||||
insertafter: EOF
|
|
||||||
block: |
|
|
||||||
dtparam=i2c1=on
|
|
||||||
dtparam=i2c_arm=on
|
|
||||||
dtparam=spi=on
|
|
||||||
gpu_mem=1
|
|
||||||
dtoverlay=dwc2
|
|
||||||
#dtoverlay=disable-wifi
|
|
||||||
enable_uart=1
|
|
||||||
|
|
||||||
[pi02w]
|
|
||||||
dtoverlay=spi0-2cs
|
|
||||||
#dtoverlay=disable-wifi
|
|
||||||
|
|
||||||
[pi3]
|
|
||||||
dtoverlay=spi0-2cs
|
|
||||||
#dtoverlay=disable-wifi
|
|
||||||
|
|
||||||
[pi4]
|
|
||||||
dtoverlay=spi0-2cs
|
|
||||||
#dtoverlay=disable-wifi
|
|
||||||
|
|
||||||
[pi5]
|
|
||||||
dtoverlay=spi0-2cs
|
|
||||||
#dtoverlay=disable-wifi
|
|
||||||
|
|
||||||
- name: change hostname
|
- name: change hostname
|
||||||
lineinfile:
|
lineinfile:
|
||||||
dest: /etc/hostname
|
dest: /etc/hostname
|
||||||
@ -258,6 +274,7 @@
|
|||||||
state: link
|
state: link
|
||||||
|
|
||||||
# install latest hcxtools
|
# install latest hcxtools
|
||||||
|
|
||||||
- name: clone hcxtools
|
- name: clone hcxtools
|
||||||
git:
|
git:
|
||||||
repo: https://github.com/ZerBea/hcxtools.git
|
repo: https://github.com/ZerBea/hcxtools.git
|
||||||
@ -274,13 +291,13 @@
|
|||||||
state: absent
|
state: absent
|
||||||
path: /usr/local/src/hcxtools
|
path: /usr/local/src/hcxtools
|
||||||
|
|
||||||
# Installing nexmon
|
|
||||||
- name: clone nexmon repository
|
- name: clone nexmon repository
|
||||||
git:
|
git:
|
||||||
repo: https://github.com/DrSchottky/nexmon.git
|
repo: https://github.com/DrSchottky/nexmon.git
|
||||||
dest: /usr/local/src/nexmon
|
dest: /usr/local/src/nexmon
|
||||||
|
|
||||||
# FIRST WE BUILD DRIVER FOR RPi5
|
# FIRST WE BUILD DRIVER FOR RPi5
|
||||||
|
|
||||||
- name: make firmware, RPi5
|
- name: make firmware, RPi5
|
||||||
shell: "source ./setup_env.sh && make"
|
shell: "source ./setup_env.sh && make"
|
||||||
args:
|
args:
|
||||||
@ -307,6 +324,11 @@
|
|||||||
QEMU_UNAME: "{{ kernel.full_pi5 }}"
|
QEMU_UNAME: "{{ kernel.full_pi5 }}"
|
||||||
ARCHFLAGS: "-arch aarch64"
|
ARCHFLAGS: "-arch aarch64"
|
||||||
|
|
||||||
|
- name: Delete the modified driver, RPi5
|
||||||
|
file:
|
||||||
|
state: absent
|
||||||
|
path: '/usr/local/src/nexmon/patches/driver/brcmfmac_{{ kernel.min }}.y-nexmon/brcmfmac.ko'
|
||||||
|
|
||||||
- name: backup original driver, RPi5
|
- name: backup original driver, RPi5
|
||||||
command: "mv /usr/lib/modules/{{ kernel.full_pi5 }}/kernel/drivers/net/wireless/broadcom/brcm80211/brcmfmac/brcmfmac.ko.xz /usr/lib/modules/{{ kernel.full_pi5 }}/kernel/drivers/net/wireless/broadcom/brcm80211/brcmfmac/brcmfmac.ko.xz.orig"
|
command: "mv /usr/lib/modules/{{ kernel.full_pi5 }}/kernel/drivers/net/wireless/broadcom/brcm80211/brcmfmac/brcmfmac.ko.xz /usr/lib/modules/{{ kernel.full_pi5 }}/kernel/drivers/net/wireless/broadcom/brcm80211/brcmfmac/brcmfmac.ko.xz.orig"
|
||||||
|
|
||||||
@ -321,6 +343,7 @@
|
|||||||
path: /usr/local/src/nexmon/
|
path: /usr/local/src/nexmon/
|
||||||
|
|
||||||
# NOW WE BUILD DRIVERS FOR RPi4, RPizero2w and RPi3
|
# NOW WE BUILD DRIVERS FOR RPi4, RPizero2w and RPi3
|
||||||
|
|
||||||
- name: clone nexmon repository
|
- name: clone nexmon repository
|
||||||
git:
|
git:
|
||||||
repo: https://github.com/DrSchottky/nexmon.git
|
repo: https://github.com/DrSchottky/nexmon.git
|
||||||
@ -351,6 +374,7 @@
|
|||||||
follow: true
|
follow: true
|
||||||
|
|
||||||
# NOW WE BUILD DRIVERS FOR RPiZero2W, RPi 3
|
# NOW WE BUILD DRIVERS FOR RPiZero2W, RPi 3
|
||||||
|
|
||||||
- name: make firmware patch (bcm43436b0)
|
- name: make firmware patch (bcm43436b0)
|
||||||
shell: "source ./setup_env.sh && cd /usr/local/src/nexmon/patches/bcm43436b0/9_88_4_65/nexmon/ && make"
|
shell: "source ./setup_env.sh && cd /usr/local/src/nexmon/patches/bcm43436b0/9_88_4_65/nexmon/ && make"
|
||||||
args:
|
args:
|
||||||
@ -375,12 +399,6 @@
|
|||||||
QEMU_UNAME: "{{ kernel.full }}"
|
QEMU_UNAME: "{{ kernel.full }}"
|
||||||
ARCHFLAGS: "-arch aarch64"
|
ARCHFLAGS: "-arch aarch64"
|
||||||
|
|
||||||
- name: install new firmware (bcm43430a1)
|
|
||||||
copy:
|
|
||||||
src: /usr/local/src/nexmon/patches/bcm43430a1/7_45_41_46/nexmon/brcmfmac43430-sdio.bin
|
|
||||||
dest: /usr/lib/firmware/brcm/brcmfmac43430-sdio.bin
|
|
||||||
follow: true
|
|
||||||
|
|
||||||
- name: copy modified driver, RPi4
|
- name: copy modified driver, RPi4
|
||||||
copy:
|
copy:
|
||||||
src: "/usr/local/src/nexmon/patches/driver/brcmfmac_{{ kernel.min }}.y-nexmon/brcmfmac.ko"
|
src: "/usr/local/src/nexmon/patches/driver/brcmfmac_{{ kernel.min }}.y-nexmon/brcmfmac.ko"
|
||||||
@ -389,6 +407,12 @@
|
|||||||
QEMU_UNAME: "{{ kernel.full }}"
|
QEMU_UNAME: "{{ kernel.full }}"
|
||||||
ARCHFLAGS: "-arch aarch64"
|
ARCHFLAGS: "-arch aarch64"
|
||||||
|
|
||||||
|
- name: install new firmware (bcm43430a1)
|
||||||
|
copy:
|
||||||
|
src: /usr/local/src/nexmon/patches/bcm43430a1/7_45_41_46/nexmon/brcmfmac43430-sdio.bin
|
||||||
|
dest: /usr/lib/firmware/brcm/brcmfmac43430-sdio.bin
|
||||||
|
follow: true
|
||||||
|
|
||||||
- name: copy 43430-sdio as 43436s-sdio for the special 43430/1 /2
|
- name: copy 43430-sdio as 43436s-sdio for the special 43430/1 /2
|
||||||
copy:
|
copy:
|
||||||
src: /usr/lib/firmware/brcm/brcmfmac43430-sdio.bin
|
src: /usr/lib/firmware/brcm/brcmfmac43430-sdio.bin
|
||||||
@ -408,7 +432,6 @@
|
|||||||
- /usr/lib/firmware/brcm/brcmfmac43436-sdio.clm_blob
|
- /usr/lib/firmware/brcm/brcmfmac43436-sdio.clm_blob
|
||||||
- /usr/lib/firmware/brcm/brcmfmac43436-sdio.raspberrypi,model-zero-2-w.clm_blob
|
- /usr/lib/firmware/brcm/brcmfmac43436-sdio.raspberrypi,model-zero-2-w.clm_blob
|
||||||
- /usr/lib/firmware/brcm/brcmfmac43455-sdio.clm_blob
|
- /usr/lib/firmware/brcm/brcmfmac43455-sdio.clm_blob
|
||||||
- /usr/lib/firmware/brcm/BCM43430A1.raspberrypi,model-zero-2-w.hcd
|
|
||||||
|
|
||||||
- name: backup original driver
|
- name: backup original driver
|
||||||
command: "mv /usr/lib/modules/{{ kernel.full }}/kernel/drivers/net/wireless/broadcom/brcm80211/brcmfmac/brcmfmac.ko.xz /usr/lib/modules/{{ kernel.full }}/kernel/drivers/net/wireless/broadcom/brcm80211/brcmfmac/brcmfmac.ko.xz.orig"
|
command: "mv /usr/lib/modules/{{ kernel.full }}/kernel/drivers/net/wireless/broadcom/brcm80211/brcmfmac/brcmfmac.ko.xz /usr/lib/modules/{{ kernel.full }}/kernel/drivers/net/wireless/broadcom/brcm80211/brcmfmac/brcmfmac.ko.xz.orig"
|
||||||
@ -424,31 +447,46 @@
|
|||||||
state: absent
|
state: absent
|
||||||
path: /usr/local/src/nexmon/
|
path: /usr/local/src/nexmon/
|
||||||
|
|
||||||
|
- name: Create custom plugin directory
|
||||||
|
file:
|
||||||
|
path: /usr/local/share/pwnagotchi/custom-plugins/
|
||||||
|
state: directory
|
||||||
|
|
||||||
- name: Create custom config directory
|
- name: Create custom config directory
|
||||||
file:
|
file:
|
||||||
path: /etc/pwnagotchi/conf.d/
|
path: /etc/pwnagotchi/conf.d/
|
||||||
state: directory
|
state: directory
|
||||||
|
|
||||||
|
- name: clone pwnagotchi repository
|
||||||
|
git:
|
||||||
|
repo: https://github.com/jayofelony/pwnagotchi.git
|
||||||
|
dest: /usr/local/src/pwnagotchi
|
||||||
|
|
||||||
|
- name: build pwnagotchi wheel
|
||||||
|
command: "pip3 install . --no-cache-dir --break-system-packages"
|
||||||
|
args:
|
||||||
|
chdir: /usr/local/src/pwnagotchi
|
||||||
|
|
||||||
|
- name: remove pwnagotchi folder
|
||||||
|
file:
|
||||||
|
state: absent
|
||||||
|
path: /usr/local/src/pwnagotchi
|
||||||
|
|
||||||
- name: create /usr/local/share/pwnagotchi/ folder
|
- name: create /usr/local/share/pwnagotchi/ folder
|
||||||
file:
|
file:
|
||||||
path: /usr/local/share/pwnagotchi/
|
path: /usr/local/share/pwnagotchi/
|
||||||
state: directory
|
state: directory
|
||||||
|
|
||||||
- name: Create custom plugin directory
|
|
||||||
file:
|
|
||||||
path: /usr/local/share/pwnagotchi/custom-plugins/
|
|
||||||
state: directory
|
|
||||||
|
|
||||||
- name: Install go-1.21
|
- name: Install go-1.21
|
||||||
unarchive:
|
unarchive:
|
||||||
src: https://go.dev/dl/go1.22.3.linux-arm64.tar.gz
|
src: https://go.dev/dl/go1.21.5.linux-arm64.tar.gz
|
||||||
dest: /usr/local
|
dest: /usr/local
|
||||||
remote_src: yes
|
remote_src: yes
|
||||||
register: golang
|
register: golang
|
||||||
|
|
||||||
- name: Update .bashrc for go-1.21
|
- name: Update .bashrc for go-1.21
|
||||||
blockinfile:
|
blockinfile:
|
||||||
dest: /etc/profile
|
dest: /home/pi/.bashrc
|
||||||
state: present
|
state: present
|
||||||
block: |
|
block: |
|
||||||
export GOPATH=$HOME/go
|
export GOPATH=$HOME/go
|
||||||
@ -474,7 +512,6 @@
|
|||||||
- name: download bettercap
|
- name: download bettercap
|
||||||
git:
|
git:
|
||||||
repo: "{{ packages.bettercap.source }}"
|
repo: "{{ packages.bettercap.source }}"
|
||||||
version: "{{ packages.bettercap.branch }}"
|
|
||||||
dest: /usr/local/src/bettercap
|
dest: /usr/local/src/bettercap
|
||||||
|
|
||||||
- name: install bettercap 2.32.4
|
- name: install bettercap 2.32.4
|
||||||
@ -501,7 +538,6 @@
|
|||||||
- name: clone bettercap caplets
|
- name: clone bettercap caplets
|
||||||
git:
|
git:
|
||||||
repo: "{{ packages.caplets.source }}"
|
repo: "{{ packages.caplets.source }}"
|
||||||
version: "{{ packages.caplets.branch }}"
|
|
||||||
dest: /tmp/caplets
|
dest: /tmp/caplets
|
||||||
register: capletsgit
|
register: capletsgit
|
||||||
|
|
||||||
@ -511,6 +547,21 @@
|
|||||||
target: install
|
target: install
|
||||||
when: capletsgit.changed
|
when: capletsgit.changed
|
||||||
|
|
||||||
|
- name: download and install bettercap ui
|
||||||
|
unarchive:
|
||||||
|
src: "{{ packages.bettercap.ui }}"
|
||||||
|
dest: /usr/local/share/bettercap/
|
||||||
|
remote_src: yes
|
||||||
|
mode: 0755
|
||||||
|
|
||||||
|
# to always have the bettercap webui available (because why not?)
|
||||||
|
- name: copy pwnagotchi-manual over pwnagotchi-auto caplet
|
||||||
|
ansible.builtin.copy:
|
||||||
|
src: /usr/local/share/bettercap/caplets/pwnagotchi-manual.cap
|
||||||
|
dest: /usr/local/share/bettercap/caplets/pwnagotchi-auto.cap
|
||||||
|
force: true
|
||||||
|
ignore_errors: true
|
||||||
|
|
||||||
- name: create /etc/pwnagotchi folder
|
- name: create /etc/pwnagotchi folder
|
||||||
file:
|
file:
|
||||||
path: /etc/pwnagotchi
|
path: /etc/pwnagotchi
|
||||||
@ -528,7 +579,7 @@
|
|||||||
# Add your configuration overrides on this file any configuration changes done to default.toml will be lost!
|
# Add your configuration overrides on this file any configuration changes done to default.toml will be lost!
|
||||||
# Example:
|
# Example:
|
||||||
# ui.display.enabled = true
|
# ui.display.enabled = true
|
||||||
# ui.display.type = "waveshare_4"
|
# ui.display.type = "waveshare_2"
|
||||||
when: not user_config.stat.exists
|
when: not user_config.stat.exists
|
||||||
|
|
||||||
- name: Delete motd
|
- name: Delete motd
|
||||||
@ -541,6 +592,24 @@
|
|||||||
state: absent
|
state: absent
|
||||||
path: /etc/update-motd.d/10-uname
|
path: /etc/update-motd.d/10-uname
|
||||||
|
|
||||||
|
- name: Add pwnlog alias
|
||||||
|
lineinfile:
|
||||||
|
dest: /home/pi/.bashrc
|
||||||
|
line: "\nalias pwnlog='tail -f -n300 /etc/pwnagotchi/log/pwn*.log | sed --unbuffered \"s/,[[:digit:]]\\{3\\}\\]//g\" | cut -d \" \" -f 2-'"
|
||||||
|
insertafter: EOF
|
||||||
|
|
||||||
|
- name: Add pwnver alias
|
||||||
|
lineinfile:
|
||||||
|
dest: /home/pi/.bashrc
|
||||||
|
line: "\nalias pwnver='python3 -c \"import pwnagotchi as p; print(p.__version__)\"'"
|
||||||
|
insertafter: EOF
|
||||||
|
|
||||||
|
- name: Add pwnkill alias to restart pwnagotchi with a signal
|
||||||
|
lineinfile:
|
||||||
|
dest: /home/pi/.bashrc
|
||||||
|
line: "\nalias pwnkill='sudo killall -USR1 pwnagotchi'"
|
||||||
|
insertafter: EOF
|
||||||
|
|
||||||
- name: add firmware packages to hold
|
- name: add firmware packages to hold
|
||||||
dpkg_selections:
|
dpkg_selections:
|
||||||
name: "{{ item }}"
|
name: "{{ item }}"
|
15
builder/data/64bit/root/settings.yaml
Normal file
15
builder/data/64bit/root/settings.yaml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
client_config_backend: file
|
||||||
|
client_config_file: /root/client_secrets.json
|
||||||
|
client_config:
|
||||||
|
client_id: <YOUR CLIENT ID>
|
||||||
|
client_secret: <YOUR CLIENT SECRET>
|
||||||
|
|
||||||
|
save_credentials: True
|
||||||
|
save_credentials_backend: file
|
||||||
|
save_credentials_file: /root/credentials.json
|
||||||
|
|
||||||
|
get_refresh_token: True
|
||||||
|
|
||||||
|
oauth_scope:
|
||||||
|
- https://www.googleapis.com/auth/drive
|
||||||
|
- https://www.googleapis.com/auth/drive.install
|
@ -9,6 +9,7 @@ if is_crypted_mode; then
|
|||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# start mon0
|
||||||
start_monitor_interface
|
start_monitor_interface
|
||||||
|
|
||||||
if is_auto_mode_no_delete; then
|
if is_auto_mode_no_delete; then
|
2
builder/data/64bit/usr/bin/hdmioff
Executable file
2
builder/data/64bit/usr/bin/hdmioff
Executable file
@ -0,0 +1,2 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
sudo /usr/bin/tvservice -o
|
2
builder/data/64bit/usr/bin/hdmion
Executable file
2
builder/data/64bit/usr/bin/hdmion
Executable file
@ -0,0 +1,2 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
sudo /usr/bin/tvservice -p
|
3
builder/data/64bit/usr/bin/monstart
Executable file
3
builder/data/64bit/usr/bin/monstart
Executable file
@ -0,0 +1,3 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
source /usr/bin/pwnlib
|
||||||
|
start_monitor_interface
|
3
builder/data/64bit/usr/bin/monstop
Executable file
3
builder/data/64bit/usr/bin/monstop
Executable file
@ -0,0 +1,3 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
source /usr/bin/pwnlib
|
||||||
|
stop_monitor_interface
|
16
builder/data/64bit/usr/bin/pwnagotchi-launcher
Executable file
16
builder/data/64bit/usr/bin/pwnagotchi-launcher
Executable file
@ -0,0 +1,16 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
source /usr/bin/pwnlib
|
||||||
|
|
||||||
|
# we need to decrypt something
|
||||||
|
if is_crypted_mode; then
|
||||||
|
while ! is_decrypted; do
|
||||||
|
echo "Waiting for decryption..."
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
if is_auto_mode; then
|
||||||
|
/usr/local/bin/pwnagotchi
|
||||||
|
else
|
||||||
|
/usr/local/bin/pwnagotchi --manual
|
||||||
|
fi
|
191
builder/data/64bit/usr/bin/pwnlib
Executable file
191
builder/data/64bit/usr/bin/pwnlib
Executable file
@ -0,0 +1,191 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# well ... it blinks the led
|
||||||
|
blink_led() {
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
for i in $(seq 1 "$1"); do
|
||||||
|
echo 0 >/sys/class/leds/led0/brightness
|
||||||
|
sleep 0.3
|
||||||
|
echo 1 >/sys/class/leds/led0/brightness
|
||||||
|
sleep 0.3
|
||||||
|
done
|
||||||
|
echo 0 >/sys/class/leds/led0/brightness
|
||||||
|
sleep 0.3
|
||||||
|
}
|
||||||
|
|
||||||
|
# reload mod
|
||||||
|
reload_brcm() {
|
||||||
|
if ! modprobe -r brcmfmac; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
sleep 1
|
||||||
|
if ! modprobe brcmfmac; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
sleep 2
|
||||||
|
iw dev wlan0 set power_save off
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# starts mon0
|
||||||
|
start_monitor_interface() {
|
||||||
|
rfkill unblock all
|
||||||
|
ifconfig wlan0 up
|
||||||
|
sleep 3
|
||||||
|
iw dev wlan0 set power_save off
|
||||||
|
iw phy "$(iw phy | head -1 | cut -d" " -f2)" interface add wlan0mon type monitor
|
||||||
|
sleep 2
|
||||||
|
rfkill unblock all
|
||||||
|
ifconfig wlan0 down
|
||||||
|
ifconfig wlan0mon up
|
||||||
|
iw dev wlan0mon set power_save off
|
||||||
|
}
|
||||||
|
|
||||||
|
# stops mon0
|
||||||
|
stop_monitor_interface() {
|
||||||
|
ifconfig wlan0mon down && iw dev wlan0mon del
|
||||||
|
reload_brcm
|
||||||
|
ifconfig wlan0 up
|
||||||
|
}
|
||||||
|
|
||||||
|
# returns 0 if the specified network interface is up
|
||||||
|
is_interface_up() {
|
||||||
|
if grep -qi 'up' /sys/class/net/"$1"/operstate; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# returns 0 if conditions for AUTO mode are met
|
||||||
|
is_auto_mode() {
|
||||||
|
# check override file first
|
||||||
|
if [ -f /root/.pwnagotchi-manual ]; then
|
||||||
|
# remove the override file if found
|
||||||
|
rm -rf /root/.pwnagotchi-manual
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# check override file first
|
||||||
|
if [ -f /root/.pwnagotchi-auto ]; then
|
||||||
|
# remove the override file if found
|
||||||
|
rm -rf /root/.pwnagotchi-auto
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# if usb0 is up, we're in MANU
|
||||||
|
if is_interface_up usb0; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# if eth0 is up (for other boards), we're in MANU
|
||||||
|
if is_interface_up eth0; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# no override, but none of the interfaces is up -> AUTO
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# returns 0 if conditions for AUTO mode are met
|
||||||
|
is_auto_mode_no_delete() {
|
||||||
|
# check override file first
|
||||||
|
if [ -f /root/.pwnagotchi-manual ]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# check override file first
|
||||||
|
if [ -f /root/.pwnagotchi-auto ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# if usb0 is up, we're in MANU
|
||||||
|
if is_interface_up usb0; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# if eth0 is up (for other boards), we're in MANU
|
||||||
|
if is_interface_up eth0; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# no override, but none of the interfaces is up -> AUTO
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# check if we need to decrypt something
|
||||||
|
is_crypted_mode() {
|
||||||
|
if [ -f /root/.pwnagotchi-crypted ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# decryption loop
|
||||||
|
is_decrypted() {
|
||||||
|
while read -r mapping container mount; do
|
||||||
|
# mapping = name the device or file will be mapped to
|
||||||
|
# container = the luks encrypted device or file
|
||||||
|
# mount = the mountpoint
|
||||||
|
|
||||||
|
# fail if not mounted
|
||||||
|
if ! mountpoint -q "$mount" >/dev/null 2>&1; then
|
||||||
|
if [ -f /tmp/.pwnagotchi-secret-"$mapping" ]; then
|
||||||
|
</tmp/.pwnagotchi-secret-"$mapping" read -r SECRET
|
||||||
|
if ! test -b /dev/disk/by-id/dm-uuid-*"$(cryptsetup luksUUID "$container" | tr -d -)"*; then
|
||||||
|
if echo -n "$SECRET" | cryptsetup luksOpen -d- "$container" "$mapping" >/dev/null 2>&1; then
|
||||||
|
echo "Container decrypted!"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if mount /dev/mapper/"$mapping" "$mount" >/dev/null 2>&1; then
|
||||||
|
echo "Mounted /dev/mapper/$mapping to $mount"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! ip -4 addr show wlan0 | grep inet >/dev/null 2>&1; then
|
||||||
|
>/dev/null 2>&1 ip addr add 192.168.0.10/24 dev wlan0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! pgrep -f decryption-webserver >/dev/null 2>&1; then
|
||||||
|
>/dev/null 2>&1 decryption-webserver &
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! pgrep wpa_supplicant >/dev/null 2>&1; then
|
||||||
|
>/tmp/wpa_supplicant.conf cat <<EOF
|
||||||
|
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
|
||||||
|
update_config=1
|
||||||
|
ap_scan=2
|
||||||
|
|
||||||
|
network={
|
||||||
|
ssid="DECRYPT-ME"
|
||||||
|
mode=2
|
||||||
|
key_mgmt=WPA-PSK
|
||||||
|
psk="pwnagotchi"
|
||||||
|
frequency=2437
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
>/dev/null 2>&1 wpa_supplicant -u -s -O -D nl80211 -i wlan0 -c /tmp/wpa_supplicant.conf &
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! pgrep dnsmasq >/dev/null 2>&1; then
|
||||||
|
>/dev/null 2>&1 dnsmasq -k -p 53 -h -O "6,192.168.0.10" -A "/#/192.168.0.10" -i wlan0 -K -F 192.168.0.50,192.168.0.60,255.255.255.0,24h &
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
done </root/.pwnagotchi-crypted
|
||||||
|
|
||||||
|
# overwrite passwords
|
||||||
|
python3 -c 'print("A"*4096)' | tee /tmp/.pwnagotchi-secret-* >/dev/null
|
||||||
|
# delete
|
||||||
|
rm /tmp/.pwnagotchi-secret-*
|
||||||
|
sync # flush
|
||||||
|
|
||||||
|
pkill wpa_supplicant
|
||||||
|
pkill dnsmasq
|
||||||
|
pid="$(pgrep -f "decryption-webserver")"
|
||||||
|
[[ -n "$pid" ]] && kill "$pid"
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
@ -1,6 +0,0 @@
|
|||||||
# /etc/modules: kernel modules to load at boot time.
|
|
||||||
#
|
|
||||||
# This file contains the names of kernel modules that should be loaded
|
|
||||||
# at boot time, one per line. Lines beginning with "#" are ignored.
|
|
||||||
# Parameters can be specified after the module name.
|
|
||||||
i2c-dev
|
|
@ -1,16 +0,0 @@
|
|||||||
#!/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.
|
|
||||||
|
|
||||||
timedatectl set-ntp true
|
|
||||||
|
|
||||||
exit 0
|
|
@ -1 +1 @@
|
|||||||
__version__ = '2.9.2'
|
__version__ = '2.8.5'
|
||||||
|
@ -4,9 +4,7 @@ import os
|
|||||||
import re
|
import re
|
||||||
import logging
|
import logging
|
||||||
import asyncio
|
import asyncio
|
||||||
#import _thread
|
import _thread
|
||||||
import threading
|
|
||||||
import subprocess
|
|
||||||
|
|
||||||
import pwnagotchi
|
import pwnagotchi
|
||||||
import pwnagotchi.utils as utils
|
import pwnagotchi.utils as utils
|
||||||
@ -256,7 +254,7 @@ class Agent(Client, Automata, AsyncAdvertiser, AsyncTrainer):
|
|||||||
txt = '%d (%d)' % (len(self._handshakes), tot)
|
txt = '%d (%d)' % (len(self._handshakes), tot)
|
||||||
|
|
||||||
if self._last_pwnd is not None:
|
if self._last_pwnd is not None:
|
||||||
txt += ' [%s]' % self._last_pwnd
|
txt += ' [%s]' % self._last_pwnd[:11] # So it doesn't overlap with fix_brcmfmac_plugin
|
||||||
|
|
||||||
self._view.set('shakes', txt)
|
self._view.set('shakes', txt)
|
||||||
|
|
||||||
@ -271,9 +269,9 @@ class Agent(Client, Automata, AsyncAdvertiser, AsyncTrainer):
|
|||||||
self._save_recovery_data()
|
self._save_recovery_data()
|
||||||
pwnagotchi.reboot()
|
pwnagotchi.reboot()
|
||||||
|
|
||||||
def _restart(self, mode='AUTO'):
|
def _restart(self):
|
||||||
self._save_recovery_data()
|
self._save_recovery_data()
|
||||||
pwnagotchi.restart(mode)
|
pwnagotchi.restart("AUTO")
|
||||||
|
|
||||||
def _save_recovery_data(self):
|
def _save_recovery_data(self):
|
||||||
logging.warning("writing recovery data to %s ...", RECOVERY_DATA_FILE)
|
logging.warning("writing recovery data to %s ...", RECOVERY_DATA_FILE)
|
||||||
@ -306,8 +304,7 @@ class Agent(Client, Automata, AsyncAdvertiser, AsyncTrainer):
|
|||||||
raise
|
raise
|
||||||
|
|
||||||
def start_session_fetcher(self):
|
def start_session_fetcher(self):
|
||||||
#_thread.start_new_thread(self._fetch_stats, ())
|
_thread.start_new_thread(self._fetch_stats, ())
|
||||||
threading.Thread(target=self._fetch_stats, args=(), name="Session Fetcher", daemon=True).start()
|
|
||||||
|
|
||||||
def _fetch_stats(self):
|
def _fetch_stats(self):
|
||||||
while True:
|
while True:
|
||||||
@ -390,8 +387,7 @@ class Agent(Client, Automata, AsyncAdvertiser, AsyncTrainer):
|
|||||||
|
|
||||||
def start_event_polling(self):
|
def start_event_polling(self):
|
||||||
# start a thread and pass in the mainloop
|
# start a thread and pass in the mainloop
|
||||||
#_thread.start_new_thread(self._event_poller, (asyncio.get_event_loop(),))
|
_thread.start_new_thread(self._event_poller, (asyncio.get_event_loop(),))
|
||||||
threading.Thread(target=self._event_poller, args=(asyncio.get_event_loop(),), name="Event Polling", daemon=True).start()
|
|
||||||
|
|
||||||
def is_module_running(self, module):
|
def is_module_running(self, module):
|
||||||
s = self.session()
|
s = self.session()
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import logging
|
import logging
|
||||||
import gymnasium as gym
|
import gym
|
||||||
from gymnasium import spaces
|
from gym import spaces
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
import pwnagotchi.ai.featurizer as featurizer
|
import pwnagotchi.ai.featurizer as featurizer
|
||||||
@ -51,7 +51,7 @@ class Environment(gym.Env):
|
|||||||
'state_v': None
|
'state_v': None
|
||||||
}
|
}
|
||||||
|
|
||||||
self.action_space = spaces.multi_discrete.MultiDiscrete([p.space_size() for p in Environment.params if p.trainable])
|
self.action_space = spaces.MultiDiscrete([p.space_size() for p in Environment.params if p.trainable])
|
||||||
self.observation_space = spaces.Box(low=0, high=1, shape=featurizer.shape, dtype=np.float32)
|
self.observation_space = spaces.Box(low=0, high=1, shape=featurizer.shape, dtype=np.float32)
|
||||||
self.reward_range = reward.range
|
self.reward_range = reward.range
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from gymnasium import spaces
|
from gym import spaces
|
||||||
|
|
||||||
|
|
||||||
class Parameter(object):
|
class Parameter(object):
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# import _thread
|
import _thread
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
import random
|
import random
|
||||||
@ -111,8 +111,7 @@ class AsyncTrainer(object):
|
|||||||
return self._training_epochs
|
return self._training_epochs
|
||||||
|
|
||||||
def start_ai(self):
|
def start_ai(self):
|
||||||
#_thread.start_new_thread(self._ai_worker, ())
|
_thread.start_new_thread(self._ai_worker, ())
|
||||||
threading.Thread(target=self._ai_worker, args=(), name="AI Worker", daemon=True).start()
|
|
||||||
|
|
||||||
def _save_ai(self):
|
def _save_ai(self):
|
||||||
logging.info("[AI] saving model to %s ..." % self._nn_path)
|
logging.info("[AI] saving model to %s ..." % self._nn_path)
|
||||||
|
@ -100,6 +100,7 @@ class Client(object):
|
|||||||
await asyncio.sleep(sleep_time)
|
await asyncio.sleep(sleep_time)
|
||||||
continue
|
continue
|
||||||
except OSError:
|
except OSError:
|
||||||
|
sleep_time = min_sleep + max_sleep * random.random()
|
||||||
logging.warning('connection to the bettercap endpoint failed...')
|
logging.warning('connection to the bettercap endpoint failed...')
|
||||||
pwnagotchi.restart("AUTO")
|
pwnagotchi.restart("AUTO")
|
||||||
|
|
||||||
|
@ -24,27 +24,30 @@ main.plugins.auto-update.interval = 1
|
|||||||
|
|
||||||
main.plugins.bt-tether.enabled = false
|
main.plugins.bt-tether.enabled = false
|
||||||
|
|
||||||
|
# Configuration for Android Phone
|
||||||
main.plugins.bt-tether.devices.android-phone.enabled = false
|
main.plugins.bt-tether.devices.android-phone.enabled = false
|
||||||
main.plugins.bt-tether.devices.android-phone.search_order = 1
|
main.plugins.bt-tether.devices.android-phone.search_order = 1
|
||||||
main.plugins.bt-tether.devices.android-phone.mac = ""
|
main.plugins.bt-tether.devices.android-phone.mac = "" # Bluetooth MAC address of the Android phone
|
||||||
main.plugins.bt-tether.devices.android-phone.ip = "192.168.44.44"
|
main.plugins.bt-tether.devices.android-phone.ip = "192.168.44.44" # Static IP of the Pwnagotchi
|
||||||
main.plugins.bt-tether.devices.android-phone.netmask = 24
|
main.plugins.bt-tether.devices.android-phone.netmask = 24 # Netmask of the PAN
|
||||||
main.plugins.bt-tether.devices.android-phone.interval = 1
|
main.plugins.bt-tether.devices.android-phone.interval = 1 # Search interval in minutes
|
||||||
main.plugins.bt-tether.devices.android-phone.scantime = 10
|
main.plugins.bt-tether.devices.android-phone.scantime = 10 # Duration of each search in seconds
|
||||||
main.plugins.bt-tether.devices.android-phone.max_tries = 10
|
main.plugins.bt-tether.devices.android-phone.max_tries = 10 # Maximum attempts to find the phone
|
||||||
main.plugins.bt-tether.devices.android-phone.share_internet = true
|
main.plugins.bt-tether.devices.android-phone.share_internet = false # Enable internet sharing via Bluetooth
|
||||||
main.plugins.bt-tether.devices.android-phone.priority = 1
|
main.plugins.bt-tether.devices.android-phone.priority = 1 # Priority level for tethering
|
||||||
|
|
||||||
|
# Configuration for iOS Phone
|
||||||
main.plugins.bt-tether.devices.ios-phone.enabled = false
|
main.plugins.bt-tether.devices.ios-phone.enabled = false
|
||||||
main.plugins.bt-tether.devices.ios-phone.search_order = 2
|
main.plugins.bt-tether.devices.ios-phone.search_order = 1
|
||||||
main.plugins.bt-tether.devices.ios-phone.mac = ""
|
main.plugins.bt-tether.devices.ios-phone.mac = "" # Bluetooth MAC address of the iOS phone
|
||||||
main.plugins.bt-tether.devices.ios-phone.ip = "172.20.10.6"
|
main.plugins.bt-tether.devices.ios-phone.ip = "" # Static IP of the Pwnagotchi when tethered to iOS
|
||||||
main.plugins.bt-tether.devices.ios-phone.netmask = 24
|
main.plugins.bt-tether.devices.ios-phone.netmask = 24 # Netmask of the PAN
|
||||||
main.plugins.bt-tether.devices.ios-phone.interval = 5
|
main.plugins.bt-tether.devices.ios-phone.interval = 1 # Search interval in minutes
|
||||||
main.plugins.bt-tether.devices.ios-phone.scantime = 20
|
main.plugins.bt-tether.devices.ios-phone.scantime = 10 # Duration of each search in seconds
|
||||||
main.plugins.bt-tether.devices.ios-phone.max_tries = 0
|
main.plugins.bt-tether.devices.ios-phone.max_tries = 10 # Maximum attempts to find the phone
|
||||||
main.plugins.bt-tether.devices.ios-phone.share_internet = true
|
main.plugins.bt-tether.devices.ios-phone.share_internet = false # Enable internet sharing via Bluetooth
|
||||||
main.plugins.bt-tether.devices.ios-phone.priority = 999
|
main.plugins.bt-tether.devices.ios-phone.priority = 1 # Priority level for tethering (edited)
|
||||||
|
|
||||||
|
|
||||||
main.plugins.fix_services.enabled = true
|
main.plugins.fix_services.enabled = true
|
||||||
|
|
||||||
@ -77,8 +80,9 @@ main.plugins.onlinehashcrack.email = ""
|
|||||||
main.plugins.onlinehashcrack.dashboard = ""
|
main.plugins.onlinehashcrack.dashboard = ""
|
||||||
main.plugins.onlinehashcrack.single_files = false
|
main.plugins.onlinehashcrack.single_files = false
|
||||||
|
|
||||||
main.plugins.pisugar3.enabled = false
|
main.plugins.pisugar2.enabled = false
|
||||||
main.plugins.pisugar3.shutdown = 5
|
main.plugins.pisugar2.shutdown = 5
|
||||||
|
main.plugins.pisugar2.sync_rtc_on_boot = false
|
||||||
|
|
||||||
main.plugins.session-stats.enabled = true
|
main.plugins.session-stats.enabled = true
|
||||||
main.plugins.session-stats.save_directory = "/var/tmp/pwnagotchi/sessions/"
|
main.plugins.session-stats.save_directory = "/var/tmp/pwnagotchi/sessions/"
|
||||||
@ -111,8 +115,9 @@ main.mon_stop_cmd = "/usr/bin/monstop"
|
|||||||
main.mon_max_blind_epochs = 50
|
main.mon_max_blind_epochs = 50
|
||||||
main.no_restart = false
|
main.no_restart = false
|
||||||
|
|
||||||
|
main.filter = ""
|
||||||
|
|
||||||
main.log.path = "/etc/pwnagotchi/log/pwnagotchi.log"
|
main.log.path = "/etc/pwnagotchi/log/pwnagotchi.log"
|
||||||
main.log.path-debug = "/etc/pwnagotchi/log/pwnagotchi-debug.log"
|
|
||||||
main.log.rotation.enabled = true
|
main.log.rotation.enabled = true
|
||||||
main.log.rotation.size = "10M"
|
main.log.rotation.size = "10M"
|
||||||
|
|
||||||
@ -150,8 +155,8 @@ personality.bond_encounters_factor = 20000
|
|||||||
personality.throttle_a = 0.4
|
personality.throttle_a = 0.4
|
||||||
personality.throttle_d = 0.9
|
personality.throttle_d = 0.9
|
||||||
|
|
||||||
ui.invert = false # false = black background, true = white background
|
personality.clear_on_exit = true # clear display when shutting down cleanly
|
||||||
ui.cursor = true
|
|
||||||
ui.fps = 0.0
|
ui.fps = 0.0
|
||||||
ui.font.name = "DejaVuSansMono" # for japanese: fonts-japanese-gothic
|
ui.font.name = "DejaVuSansMono" # for japanese: fonts-japanese-gothic
|
||||||
ui.font.size_offset = 0 # will be added to the font size
|
ui.font.size_offset = 0 # will be added to the font size
|
||||||
|
@ -3,8 +3,7 @@ import re
|
|||||||
import tempfile
|
import tempfile
|
||||||
import contextlib
|
import contextlib
|
||||||
import shutil
|
import shutil
|
||||||
#import _thread
|
import _thread
|
||||||
import threading
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from time import sleep
|
from time import sleep
|
||||||
@ -86,8 +85,7 @@ def setup_mounts(config):
|
|||||||
if interval:
|
if interval:
|
||||||
logging.debug("[FS] Starting thread to sync %s (interval: %d)",
|
logging.debug("[FS] Starting thread to sync %s (interval: %d)",
|
||||||
options['mount'], interval)
|
options['mount'], interval)
|
||||||
threading.Thread(target=m.daemonize, args=(interval,),name="File Sys", daemon=True).start()
|
_thread.start_new_thread(m.daemonize, (interval,))
|
||||||
#_thread.start_new_thread(m.daemonize, (interval,))
|
|
||||||
else:
|
else:
|
||||||
logging.debug("[FS] Not syncing %s, because interval is 0",
|
logging.debug("[FS] Not syncing %s, because interval is 0",
|
||||||
options['mount'])
|
options['mount'])
|
||||||
|
@ -92,7 +92,7 @@ def update_data(last_session):
|
|||||||
'build': "Pwnagotchi by Jayofelony",
|
'build': "Pwnagotchi by Jayofelony",
|
||||||
'plugins': enabled,
|
'plugins': enabled,
|
||||||
'language': language,
|
'language': language,
|
||||||
'bettercap': subprocess.getoutput("bettercap -version"),
|
'bettercap': subprocess.getoutput("bettercap -version").split(".\n\n")[1],
|
||||||
'opwngrid': subprocess.getoutput("pwngrid -version")
|
'opwngrid': subprocess.getoutput("pwngrid -version")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Binary file not shown.
@ -248,7 +248,7 @@ msgid "minutes"
|
|||||||
msgstr "minuten"
|
msgstr "minuten"
|
||||||
|
|
||||||
msgid "seconds"
|
msgid "seconds"
|
||||||
msgstr "sekondes"
|
msgstr "seconds"
|
||||||
|
|
||||||
msgid "hour"
|
msgid "hour"
|
||||||
msgstr "oere"
|
msgstr "oere"
|
||||||
|
Binary file not shown.
@ -1,16 +1,16 @@
|
|||||||
# Icelandic Translation.
|
# SOME DESCRIPTIVE TITLE.
|
||||||
# Copyright (C) 2019
|
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||||
# This file is distributed under the same license as the pwnagotchi package..
|
# This file is distributed under the same license as the PACKAGE package.
|
||||||
# Sean Duggan <sean.duggan@pm.me>, 2024.
|
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||||
#
|
#
|
||||||
#, fuzzy
|
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2024-11-17 20:51+0100\n"
|
"POT-Creation-Date: 2023-11-16 21:10+0100\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <sean.duggan@pm.me>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
"Language: Icelandic\n"
|
"Language: Icelandic\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
@ -18,219 +18,218 @@ msgstr ""
|
|||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
|
||||||
msgid "ZzzzZZzzzzZzzz"
|
msgid "ZzzzZZzzzzZzzz"
|
||||||
msgstr "ZzzzZZzzzzZzzz"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Hi, I'm Pwnagotchi! Starting ..."
|
msgid "Hi, I'm Pwnagotchi! Starting ..."
|
||||||
msgstr "Hæ, Ég heiti Pwnagotchi! Ræsi.."
|
msgstr ""
|
||||||
|
|
||||||
msgid "New day, new hunt, new pwns!"
|
msgid "New day, new hunt, new pwns!"
|
||||||
msgstr "Nýr dagur, Ný veiði, Ný pwns!"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Hack the Planet!"
|
msgid "Hack the Planet!"
|
||||||
msgstr "Hakkaðu plánetuna!"
|
msgstr ""
|
||||||
|
|
||||||
msgid "AI ready."
|
msgid "AI ready."
|
||||||
msgstr "AI tilbúið."
|
msgstr ""
|
||||||
|
|
||||||
msgid "The neural network is ready."
|
msgid "The neural network is ready."
|
||||||
msgstr "Tauganetið er tilbúið."
|
msgstr ""
|
||||||
|
|
||||||
msgid "Generating keys, do not turn off ..."
|
msgid "Generating keys, do not turn off ..."
|
||||||
msgstr "Bý til lykla, ekki slökkva á."
|
msgstr ""
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Hey, channel {channel} is free! Your AP will say thanks."
|
msgid "Hey, channel {channel} is free! Your AP will say thanks."
|
||||||
msgstr "Hæ, Rás {channel} er líka ókeypis! AP þinn mun þakka fyrir."
|
msgstr ""
|
||||||
|
|
||||||
msgid "Reading last session logs ..."
|
msgid "Reading last session logs ..."
|
||||||
msgstr "Les fyrri leiðarbók..."
|
msgstr ""
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Read {lines_so_far} log lines so far ..."
|
msgid "Read {lines_so_far} log lines so far ..."
|
||||||
msgstr "Ég las {lines_so_far} leiðarbók línur hingað til..."
|
msgstr ""
|
||||||
|
|
||||||
msgid "I'm bored ..."
|
msgid "I'm bored ..."
|
||||||
msgstr "Mér leiðist ..."
|
msgstr ""
|
||||||
|
|
||||||
msgid "Let's go for a walk!"
|
msgid "Let's go for a walk!"
|
||||||
msgstr "Förum í göngutúr!"
|
msgstr ""
|
||||||
|
|
||||||
msgid "This is the best day of my life!"
|
msgid "This is the best day of my life!"
|
||||||
msgstr "Þetta er besti dagur lífs míns!"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Shitty day :/"
|
msgid "Shitty day :/"
|
||||||
msgstr "Skítadagur :/"
|
msgstr ""
|
||||||
|
|
||||||
msgid "I'm extremely bored ..."
|
msgid "I'm extremely bored ..."
|
||||||
msgstr "Mér leiðist óskaplega mikið ..."
|
msgstr ""
|
||||||
|
|
||||||
msgid "I'm very sad ..."
|
msgid "I'm very sad ..."
|
||||||
msgstr "Ég er mjög leiður ..."
|
msgstr ""
|
||||||
|
|
||||||
msgid "I'm sad"
|
msgid "I'm sad"
|
||||||
msgstr "Ég er leiður"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Leave me alone ..."
|
msgid "Leave me alone ..."
|
||||||
msgstr "Láttu mig í friði ..."
|
msgstr ""
|
||||||
|
|
||||||
msgid "I'm mad at you!"
|
msgid "I'm mad at you!"
|
||||||
msgstr "Ég er reiður út í þig!"
|
msgstr ""
|
||||||
|
|
||||||
msgid "I'm living the life!"
|
msgid "I'm living the life!"
|
||||||
msgstr "Ég lifi besta lífi!"
|
msgstr ""
|
||||||
|
|
||||||
msgid "I pwn therefore I am."
|
msgid "I pwn therefore I am."
|
||||||
msgstr "Ég pwn þess vegna er ég."
|
msgstr ""
|
||||||
|
|
||||||
msgid "So many networks!!!"
|
msgid "So many networks!!!"
|
||||||
msgstr "Svo mörg net!!!"
|
msgstr ""
|
||||||
|
|
||||||
msgid "I'm having so much fun!"
|
msgid "I'm having so much fun!"
|
||||||
msgstr "Mér finnst svo gaman!"
|
msgstr ""
|
||||||
|
|
||||||
msgid "My crime is that of curiosity ..."
|
msgid "My crime is that of curiosity ..."
|
||||||
msgstr "Glæpur minn er forvitni ..."
|
msgstr ""
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Hello {name}! Nice to meet you."
|
msgid "Hello {name}! Nice to meet you."
|
||||||
msgstr "Hæ {name}! Gaman að hitta þig!"
|
msgstr ""
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Yo {name}! Sup?"
|
msgid "Yo {name}! Sup?"
|
||||||
msgstr "Yo {name}! Sup?"
|
msgstr ""
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Hey {name} how are you doing?"
|
msgid "Hey {name} how are you doing?"
|
||||||
msgstr "Hæ {name} Hvernig hefurðu það?"
|
msgstr ""
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Unit {name} is nearby!"
|
msgid "Unit {name} is nearby!"
|
||||||
msgstr "Tæki {name} er nálægt!"
|
msgstr ""
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Uhm ... goodbye {name}"
|
msgid "Uhm ... goodbye {name}"
|
||||||
msgstr "Uhm ... bless {name}"
|
msgstr ""
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "{name} is gone ..."
|
msgid "{name} is gone ..."
|
||||||
msgstr "{name} er farinn ..."
|
msgstr ""
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Whoops ... {name} is gone."
|
msgid "Whoops ... {name} is gone."
|
||||||
msgstr "Úps ... {name} er farinn."
|
msgstr ""
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "{name} missed!"
|
msgid "{name} missed!"
|
||||||
msgstr "{name} saknað!'"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Missed!"
|
msgid "Missed!"
|
||||||
msgstr "Saknað!'"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Good friends are a blessing!"
|
msgid "Good friends are a blessing!"
|
||||||
msgstr "Góðir vinir eru blessun!"
|
msgstr ""
|
||||||
|
|
||||||
msgid "I love my friends!"
|
msgid "I love my friends!"
|
||||||
msgstr "Ég elska vini mína!"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Nobody wants to play with me ..."
|
msgid "Nobody wants to play with me ..."
|
||||||
msgstr "Enginn vill leika við mig ..."
|
msgstr ""
|
||||||
|
|
||||||
msgid "I feel so alone ..."
|
msgid "I feel so alone ..."
|
||||||
msgstr "Mér finnst ég vera svo ein ..."
|
msgstr ""
|
||||||
|
|
||||||
msgid "Where's everybody?!"
|
msgid "Where's everybody?!"
|
||||||
msgstr "Hvar eru allir?!"
|
msgstr ""
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Napping for {secs}s ..."
|
msgid "Napping for {secs}s ..."
|
||||||
msgstr "Sef í {secs}s ..."
|
msgstr ""
|
||||||
|
|
||||||
msgid "Zzzzz"
|
msgid "Zzzzz"
|
||||||
msgstr "Zzzzz"
|
msgstr ""
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "ZzzZzzz ({secs}s)"
|
msgid "ZzzZzzz ({secs}s)"
|
||||||
msgstr "ZzzZzzz ({secs})"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Good night."
|
msgid "Good night."
|
||||||
msgstr "Góða nótt."
|
msgstr ""
|
||||||
|
|
||||||
msgid "Zzz"
|
msgid "Zzz"
|
||||||
msgstr "Zzz"
|
msgstr ""
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Waiting for {secs}s ..."
|
msgid "Waiting for {secs}s ..."
|
||||||
msgstr "Bíð eftir {secs} ..."
|
msgstr ""
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Looking around ({secs}s)"
|
msgid "Looking around ({secs}s)"
|
||||||
msgstr "Horfi í kringum mig ({secs}s)"
|
msgstr ""
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Hey {what} let's be friends!"
|
msgid "Hey {what} let's be friends!"
|
||||||
msgstr "Hæ {what} við skulum vera vinir!"
|
msgstr ""
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Associating to {what}"
|
msgid "Associating to {what}"
|
||||||
msgstr "Tengist við {what}"
|
msgstr ""
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Yo {what}!"
|
msgid "Yo {what}!"
|
||||||
msgstr "Yo {what}!"
|
msgstr ""
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Just decided that {mac} needs no WiFi!"
|
msgid "Just decided that {mac} needs no WiFi!"
|
||||||
msgstr "Ég ákvað að {mac} þurfi ekki WiFi!"
|
msgstr ""
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Deauthenticating {mac}"
|
msgid "Deauthenticating {mac}"
|
||||||
msgstr "Afvotta {mac}"
|
msgstr ""
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Kickbanning {mac}!"
|
msgid "Kickbanning {mac}!"
|
||||||
msgstr "Sparkbanna {mac}!"
|
msgstr ""
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Cool, we got {num} new handshake{plural}!"
|
msgid "Cool, we got {num} new handshake{plural}!"
|
||||||
msgstr "Flott! við fengum {num} ný handatök {plural}!"
|
msgstr ""
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "You have {count} new message{plural}!"
|
msgid "You have {count} new message{plural}!"
|
||||||
msgstr "þú hefur {count} ný skilaboð!"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Oops, something went wrong ... Rebooting ..."
|
msgid "Oops, something went wrong ... Rebooting ..."
|
||||||
msgstr "Oops, eitthvað brotnaði ... Endurræsi ..."
|
msgstr ""
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Uploading data to {to} ..."
|
msgid "Uploading data to {to} ..."
|
||||||
msgstr "Hleð upp gögnum til {to} ..."
|
msgstr ""
|
||||||
|
|
||||||
#, fuzzy, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Downloading from {name} ..."
|
msgid "Downloading from {name} ..."
|
||||||
msgstr "Hleð upp gögnum til {to} ..."
|
msgstr ""
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Kicked {num} stations\n"
|
msgid "Kicked {num} stations\n"
|
||||||
msgstr "Sparkaði {num} stöðvar\n"
|
msgstr ""
|
||||||
|
|
||||||
#, fuzzy
|
|
||||||
msgid "Made >999 new friends\n"
|
msgid "Made >999 new friends\n"
|
||||||
msgstr "Eignaðist {num} nýja vini\n"
|
msgstr ""
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Made {num} new friends\n"
|
msgid "Made {num} new friends\n"
|
||||||
msgstr "Eignaðist {num} nýja vini\n"
|
msgstr ""
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Got {num} handshakes\n"
|
msgid "Got {num} handshakes\n"
|
||||||
msgstr "Fékk {num} ný handabönd\n"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Met 1 peer"
|
msgid "Met 1 peer"
|
||||||
msgstr "Hitti 1 jafningja"
|
msgstr ""
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Met {num} peers"
|
msgid "Met {num} peers"
|
||||||
msgstr "Hitti {num} jafningja"
|
msgstr ""
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid ""
|
msgid ""
|
||||||
@ -238,24 +237,21 @@ msgid ""
|
|||||||
"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi "
|
"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi "
|
||||||
"#pwnlog #pwnlife #hacktheplanet #skynet"
|
"#pwnlog #pwnlife #hacktheplanet #skynet"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Ég hef verið að pwna í {duration} og sparkað {deauthed} viðskiptavinum! Ég "
|
|
||||||
"hef líka hitt {associated} nýja vini og borðað {handshakes} handabönd! "
|
|
||||||
"#pwnagotchi #pwnlog #pwnlife #hacktheplanet #skynet"
|
|
||||||
|
|
||||||
msgid "hours"
|
msgid "hours"
|
||||||
msgstr "klukkustundir"
|
msgstr ""
|
||||||
|
|
||||||
msgid "minutes"
|
msgid "minutes"
|
||||||
msgstr "mínútur"
|
msgstr ""
|
||||||
|
|
||||||
msgid "seconds"
|
msgid "seconds"
|
||||||
msgstr "sekúndur"
|
msgstr ""
|
||||||
|
|
||||||
msgid "hour"
|
msgid "hour"
|
||||||
msgstr "klukkustund"
|
msgstr ""
|
||||||
|
|
||||||
msgid "minute"
|
msgid "minute"
|
||||||
msgstr "mínútu"
|
msgstr ""
|
||||||
|
|
||||||
msgid "second"
|
msgid "second"
|
||||||
msgstr "sekúndu"
|
msgstr ""
|
||||||
|
Binary file not shown.
@ -8,8 +8,8 @@ msgstr ""
|
|||||||
"Project-Id-Version: 0.0.1\n"
|
"Project-Id-Version: 0.0.1\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2023-11-16 21:51+0100\n"
|
"POT-Creation-Date: 2023-11-16 21:51+0100\n"
|
||||||
"PO-Revision-Date: 2024-08-23 20:40+0900\n"
|
"PO-Revision-Date: 2019-10-16 15:05+0200\n"
|
||||||
"Last-Translator: mendoitarou_ <42207304+mendoitarou@users.noreply.github.com>\n"
|
"Last-Translator: wytshadow <24534649+wytshadow@users.noreply.github.com>\n"
|
||||||
"Language-Team: pwnagotchi <24534649+wytshadow@users.noreply.github.com>\n"
|
"Language-Team: pwnagotchi <24534649+wytshadow@users.noreply.github.com>\n"
|
||||||
"Language: Japanese\n"
|
"Language: Japanese\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
@ -243,12 +243,10 @@ msgstr ""
|
|||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Uploading data to {to} ..."
|
msgid "Uploading data to {to} ..."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"{to} にデータをアップロードしてるよ..."
|
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Downloading from {name} ..."
|
msgid "Downloading from {name} ..."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"{name} からダウンロードしてるよ..."
|
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Kicked {num} stations\n"
|
msgid "Kicked {num} stations\n"
|
||||||
|
Binary file not shown.
@ -1,18 +1,18 @@
|
|||||||
# pwnagotchi Brazilian Portuguese translation file.
|
# SOME DESCRIPTIVE TITLE.
|
||||||
# Copyright (C) 2024
|
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||||
# This file is distributed under the same license as the pwnagotchi package.
|
# This file is distributed under the same license as the PACKAGE package.
|
||||||
# Fabiano F O <fabfernandes@hotmail.com>, 2024.
|
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||||
#
|
#
|
||||||
|
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2024-03-25 22:30+0100\n"
|
"POT-Creation-Date: 2023-11-17 15:46+0100\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: Fabiano F O <fabfernandes@hotmail.com>\n"
|
"Last-Translator: Foxy <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
"Language: Brazilian Portuguese\n"
|
"Language: Portuguese (Brazil)\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=utf-8\n"
|
"Content-Type: text/plain; charset=utf-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
@ -21,13 +21,13 @@ msgid "ZzzzZZzzzzZzzz"
|
|||||||
msgstr "ZzzzZZzzzzZzzz"
|
msgstr "ZzzzZZzzzzZzzz"
|
||||||
|
|
||||||
msgid "Hi, I'm Pwnagotchi! Starting ..."
|
msgid "Hi, I'm Pwnagotchi! Starting ..."
|
||||||
msgstr "Olá, sou Pwnagotchi! Iniciando ..."
|
msgstr "Olá, Eu sou Pwnagotchi! Iniciando ..."
|
||||||
|
|
||||||
msgid "New day, new hunt, new pwns!"
|
msgid "New day, new hunt, new pwns!"
|
||||||
msgstr "Novo dia, Nova caçada, Novos pwns!"
|
msgstr "Um novo dia, Uma nova caça e novos pwns!"
|
||||||
|
|
||||||
msgid "Hack the Planet!"
|
msgid "Hack the Planet!"
|
||||||
msgstr "Hackeie o Planeta!"
|
msgstr "Burle o Planeta!"
|
||||||
|
|
||||||
msgid "AI ready."
|
msgid "AI ready."
|
||||||
msgstr "IA pronta."
|
msgstr "IA pronta."
|
||||||
@ -36,64 +36,64 @@ msgid "The neural network is ready."
|
|||||||
msgstr "A rede neural está pronta."
|
msgstr "A rede neural está pronta."
|
||||||
|
|
||||||
msgid "Generating keys, do not turn off ..."
|
msgid "Generating keys, do not turn off ..."
|
||||||
msgstr "Gerando chaves, não desligue ..."
|
msgstr "Criando chaves, não desligue o sistema ..."
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Hey, channel {channel} is free! Your AP will say thanks."
|
msgid "Hey, channel {channel} is free! Your AP will say thanks."
|
||||||
msgstr "Ei, o canal {channel} está livre! Seu AP vai agradecer."
|
msgstr "Ei, canal {channel} está livre! Seu AP vai agradecer."
|
||||||
|
|
||||||
msgid "Reading last session logs ..."
|
msgid "Reading last session logs ..."
|
||||||
msgstr "Lendo os logs da última sessão ..."
|
msgstr "Lendo os logs da ultima sessão"
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Read {lines_so_far} log lines so far ..."
|
msgid "Read {lines_so_far} log lines so far ..."
|
||||||
msgstr "Li {lines_so_far} linhas de logs até agora ..."
|
msgstr "Leia {lines_so_far} linha de logs até agora ..."
|
||||||
|
|
||||||
msgid "I'm bored ..."
|
msgid "I'm bored ..."
|
||||||
msgstr "Estou entediado ..."
|
msgstr "Eu estou entediado ..."
|
||||||
|
|
||||||
msgid "Let's go for a walk!"
|
msgid "Let's go for a walk!"
|
||||||
msgstr "Vamos dar um passeio!"
|
msgstr "Vamos ir numa caminhada!"
|
||||||
|
|
||||||
msgid "This is the best day of my life!"
|
msgid "This is the best day of my life!"
|
||||||
msgstr "Este é o melhor dia da minha vida!"
|
msgstr "Esse é o melhor dia da minha vida!"
|
||||||
|
|
||||||
msgid "Shitty day :/"
|
msgid "Shitty day :/"
|
||||||
msgstr "Que dia ruim :/"
|
msgstr "Dia ruim :/"
|
||||||
|
|
||||||
msgid "I'm extremely bored ..."
|
msgid "I'm extremely bored ..."
|
||||||
msgstr "Estou extremamente entediado ..."
|
msgstr "Eu estou extremamente entediado ..."
|
||||||
|
|
||||||
msgid "I'm very sad ..."
|
msgid "I'm very sad ..."
|
||||||
msgstr "Estou muito triste ..."
|
msgstr "Eu estou muito triste ..."
|
||||||
|
|
||||||
msgid "I'm sad"
|
msgid "I'm sad"
|
||||||
msgstr "Estou triste"
|
msgstr "Eu estou triste"
|
||||||
|
|
||||||
msgid "Leave me alone ..."
|
msgid "Leave me alone ..."
|
||||||
msgstr "Me deixe em paz ..."
|
msgstr "Me deixe em paz ..."
|
||||||
|
|
||||||
msgid "I'm mad at you!"
|
msgid "I'm mad at you!"
|
||||||
msgstr "Estou bravo com você!"
|
msgstr "Eu estou bravo com você!"
|
||||||
|
|
||||||
msgid "I'm living the life!"
|
msgid "I'm living the life!"
|
||||||
msgstr "Estou aproveitando a vida!"
|
msgstr "Eu estou vivendo a vida!"
|
||||||
|
|
||||||
msgid "I pwn therefore I am."
|
msgid "I pwn therefore I am."
|
||||||
msgstr "Eu pwn, logo existo."
|
msgstr "Eu pwn então Eu sou."
|
||||||
|
|
||||||
msgid "So many networks!!!"
|
msgid "So many networks!!!"
|
||||||
msgstr "Uau! Quantas redes!!"
|
msgstr "Tantas redes!!!"
|
||||||
|
|
||||||
msgid "I'm having so much fun!"
|
msgid "I'm having so much fun!"
|
||||||
msgstr "Estou me divertindo muito!"
|
msgstr "Eu estou tendo muita diversão"
|
||||||
|
|
||||||
msgid "My crime is that of curiosity ..."
|
msgid "My crime is that of curiosity ..."
|
||||||
msgstr "Meu crime é a curiosidade ..."
|
msgstr "Meu crime é de curiosidade ..."
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Hello {name}! Nice to meet you."
|
msgid "Hello {name}! Nice to meet you."
|
||||||
msgstr "Olá {name}! Prazer em conhecê-lo."
|
msgstr "Olá {name}! É bom em conhecê-lo"
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Yo {name}! Sup?"
|
msgid "Yo {name}! Sup?"
|
||||||
@ -101,33 +101,33 @@ msgstr "Ei {name}! Como vai?"
|
|||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Hey {name} how are you doing?"
|
msgid "Hey {name} how are you doing?"
|
||||||
msgstr "Ei {name}, como você está?"
|
msgstr "Ei {name} como você está indo?"
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Unit {name} is nearby!"
|
msgid "Unit {name} is nearby!"
|
||||||
msgstr "A unidade {name} está próxima!"
|
msgstr ""
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Uhm ... goodbye {name}"
|
msgid "Uhm ... goodbye {name}"
|
||||||
msgstr "Hmm ... tchau {name}"
|
msgstr ""
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "{name} is gone ..."
|
msgid "{name} is gone ..."
|
||||||
msgstr "{name} desapareceu ..."
|
msgstr ""
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Whoops ... {name} is gone."
|
msgid "Whoops ... {name} is gone."
|
||||||
msgstr "Oops ... {name} desapareceu."
|
msgstr ""
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "{name} missed!"
|
msgid "{name} missed!"
|
||||||
msgstr "Perdi {name}!"
|
msgstr "{name} errou!"
|
||||||
|
|
||||||
msgid "Missed!"
|
msgid "Missed!"
|
||||||
msgstr "Perdi!"
|
msgstr "Errei!"
|
||||||
|
|
||||||
msgid "Good friends are a blessing!"
|
msgid "Good friends are a blessing!"
|
||||||
msgstr "Bons amigos são uma bênção!"
|
msgstr "Bom amigos são uma bensão!"
|
||||||
|
|
||||||
msgid "I love my friends!"
|
msgid "I love my friends!"
|
||||||
msgstr "Eu amo meus amigos!"
|
msgstr "Eu amo meus amigos!"
|
||||||
@ -136,7 +136,7 @@ msgid "Nobody wants to play with me ..."
|
|||||||
msgstr "Ninguém quer brincar comigo ..."
|
msgstr "Ninguém quer brincar comigo ..."
|
||||||
|
|
||||||
msgid "I feel so alone ..."
|
msgid "I feel so alone ..."
|
||||||
msgstr "Me sinto tão sozinho ..."
|
msgstr "Estou me sentindo sozinho"
|
||||||
|
|
||||||
msgid "Where's everybody?!"
|
msgid "Where's everybody?!"
|
||||||
msgstr "Onde está todo mundo?!"
|
msgstr "Onde está todo mundo?!"
|
||||||
@ -160,27 +160,27 @@ msgstr "Zzz"
|
|||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Waiting for {secs}s ..."
|
msgid "Waiting for {secs}s ..."
|
||||||
msgstr "Aguardando {secs}s ..."
|
msgstr "Esperando por {secs}s ..."
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Looking around ({secs}s)"
|
msgid "Looking around ({secs}s)"
|
||||||
msgstr "Olhando em volta ... ({secs}s)"
|
msgstr "Olhando por volta ({secs}s)"
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Hey {what} let's be friends!"
|
msgid "Hey {what} let's be friends!"
|
||||||
msgstr "Ei {what}, vamos ser amigos!"
|
msgstr "Ei {what} vamos ser amigos!"
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Associating to {what}"
|
msgid "Associating to {what}"
|
||||||
msgstr "Associando a {what}"
|
msgstr "Associando para {what}"
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Yo {what}!"
|
msgid "Yo {what}!"
|
||||||
msgstr "Olá {what}!"
|
msgstr "Ei {what}!"
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Just decided that {mac} needs no WiFi!"
|
msgid "Just decided that {mac} needs no WiFi!"
|
||||||
msgstr "Acabei de decidir que {mac} não precisa de WiFi!"
|
msgstr "Apenas decidindo que {mac} não precisa de WiFi!"
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Deauthenticating {mac}"
|
msgid "Deauthenticating {mac}"
|
||||||
@ -192,11 +192,11 @@ msgstr "Banindo {mac}!"
|
|||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Cool, we got {num} new handshake{plural}!"
|
msgid "Cool, we got {num} new handshake{plural}!"
|
||||||
msgstr "Legal, conseguimos {num} novo{plural} handshake{plural}!"
|
msgstr "Legal, conseguimos {num} novos handshake{plural}!"
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "You have {count} new message{plural}!"
|
msgid "You have {count} new message{plural}!"
|
||||||
msgstr "Você tem {count} nova{plural} messagem{plural}!"
|
msgstr "Você tem {count} novas messagem{plural}!"
|
||||||
|
|
||||||
msgid "Oops, something went wrong ... Rebooting ..."
|
msgid "Oops, something went wrong ... Rebooting ..."
|
||||||
msgstr "Oops, algo deu errado ... Reiniciando ..."
|
msgstr "Oops, algo deu errado ... Reiniciando ..."
|
||||||
@ -207,7 +207,7 @@ msgstr "Enviando dados para {to} ..."
|
|||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Downloading from {name} ..."
|
msgid "Downloading from {name} ..."
|
||||||
msgstr "Baixando de {name} ..."
|
msgstr "Instalando para {name} ..."
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Kicked {num} stations\n"
|
msgid "Kicked {num} stations\n"
|
||||||
@ -225,11 +225,11 @@ msgid "Got {num} handshakes\n"
|
|||||||
msgstr "Peguei {num} handshakes\n"
|
msgstr "Peguei {num} handshakes\n"
|
||||||
|
|
||||||
msgid "Met 1 peer"
|
msgid "Met 1 peer"
|
||||||
msgstr "Conheci 1 peer"
|
msgstr "Encontrei 1 pessoa"
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Met {num} peers"
|
msgid "Met {num} peers"
|
||||||
msgstr "Conheci {num} peers"
|
msgstr "Encontrei {num} pessoas"
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid ""
|
msgid ""
|
||||||
@ -237,8 +237,8 @@ msgid ""
|
|||||||
"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi "
|
"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi "
|
||||||
"#pwnlog #pwnlife #hacktheplanet #skynet"
|
"#pwnlog #pwnlife #hacktheplanet #skynet"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Estou pwning há {duration} e expulsei {deauthed} clientes! Também conheci "
|
"Estou navegando há {duration} e expulsei {deauthed} clientes! Também conheci "
|
||||||
"{associated} novos amigos e comi {handshakes} handshakes! #pwnagotchi "
|
"{associamos} novos amigos e comi {handshakes} handshakes! #pwnagotchi "
|
||||||
"#pwnlog #pwnlife #hacktheplanet #skynet"
|
"#pwnlog #pwnlife #hacktheplanet #skynet"
|
||||||
|
|
||||||
msgid "hours"
|
msgid "hours"
|
||||||
|
Binary file not shown.
@ -1,260 +0,0 @@
|
|||||||
# SOME DESCRIPTIVE TITLE.
|
|
||||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
|
||||||
# This file is distributed under the same license as the PACKAGE package.
|
|
||||||
# FIRST AUTHOR <dragan.miljkovic29@gmail.com>, 2024.
|
|
||||||
#
|
|
||||||
#, fuzzy
|
|
||||||
msgid ""
|
|
||||||
msgstr ""
|
|
||||||
"Project-Id-Version: Srpski prevod v1\n"
|
|
||||||
"Report-Msgid-Bugs-To: \n"
|
|
||||||
"POT-Creation-Date: 2024-02-16 15:26-0300\n"
|
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
|
||||||
"Language-Team: RS <dragan.miljkovic29@gmail.com>\n"
|
|
||||||
"Language: Serbian\n"
|
|
||||||
"MIME-Version: 1.0\n"
|
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
|
||||||
|
|
||||||
msgid "ZzzzZZzzzzZzzz"
|
|
||||||
msgstr "ZzzzZZzzzzZzzz"
|
|
||||||
|
|
||||||
msgid "Hi, I'm Pwnagotchi! Starting ..."
|
|
||||||
msgstr "Zdravo, ja sam Pwnagotchi! Započinjem ..."
|
|
||||||
|
|
||||||
msgid "New day, new hunt, new pwns!"
|
|
||||||
msgstr "Novi dan, novi lov, novi pnwovi!"
|
|
||||||
|
|
||||||
msgid "Hack the Planet!"
|
|
||||||
msgstr "Hakuj Planetu!"
|
|
||||||
|
|
||||||
msgid "AI ready."
|
|
||||||
msgstr "AI spreman."
|
|
||||||
|
|
||||||
msgid "The neural network is ready."
|
|
||||||
msgstr "Neuronska mreža je spremna."
|
|
||||||
|
|
||||||
msgid "Generating keys, do not turn off ..."
|
|
||||||
msgstr "Generišem ključeve, ne isključuj me..."
|
|
||||||
|
|
||||||
#, python-brace-format
|
|
||||||
msgid "Hey, channel {channel} is free! Your AP will say thanks."
|
|
||||||
msgstr "Hej, kanal {channel} je slobodan! Tvoj AP će ti se zahvaliti."
|
|
||||||
|
|
||||||
msgid "Reading last session logs ..."
|
|
||||||
msgstr "Čitanje logova poslednje sesije ..."
|
|
||||||
|
|
||||||
#, python-brace-format
|
|
||||||
msgid "Read {lines_so_far} log lines so far ..."
|
|
||||||
msgstr "Pročitao {lines_so_far} log linija do sada ..."
|
|
||||||
|
|
||||||
msgid "I'm bored ..."
|
|
||||||
msgstr "Dosadno mi je ..."
|
|
||||||
|
|
||||||
msgid "Let's go for a walk!"
|
|
||||||
msgstr "Hajmo u šetnju!"
|
|
||||||
|
|
||||||
msgid "This is the best day of my life!"
|
|
||||||
msgstr "Ovo je najbolji dan mog života!"
|
|
||||||
|
|
||||||
msgid "Shitty day :/"
|
|
||||||
msgstr "Sranje dan :/"
|
|
||||||
|
|
||||||
msgid "I'm extremely bored ..."
|
|
||||||
msgstr "Užasno mi je dosadno ..."
|
|
||||||
|
|
||||||
msgid "I'm very sad ..."
|
|
||||||
msgstr "Jako sam tužan ..."
|
|
||||||
|
|
||||||
msgid "I'm sad"
|
|
||||||
msgstr "Tužan sam"
|
|
||||||
|
|
||||||
msgid "Leave me alone ..."
|
|
||||||
msgstr "Ostavi me na miru ..."
|
|
||||||
|
|
||||||
msgid "I'm mad at you!"
|
|
||||||
msgstr "Ljut sam na tebe!"
|
|
||||||
|
|
||||||
msgid "I'm living the life!"
|
|
||||||
msgstr "Živim ga!"
|
|
||||||
|
|
||||||
msgid "I pwn therefore I am."
|
|
||||||
msgstr "Ja pwnujem dakle postojim."
|
|
||||||
|
|
||||||
msgid "So many networks!!!"
|
|
||||||
msgstr "Tako mnogo mreža!!!"
|
|
||||||
|
|
||||||
msgid "I'm having so much fun!"
|
|
||||||
msgstr "Previše se zabavljam!"
|
|
||||||
|
|
||||||
msgid "My crime is that of curiosity ..."
|
|
||||||
msgstr "Moj zločin je radoznalost ..."
|
|
||||||
|
|
||||||
#, python-brace-format
|
|
||||||
msgid "Hello {name}! Nice to meet you."
|
|
||||||
msgstr "Zdravo {name}! Drago mi je da te upoznam."
|
|
||||||
|
|
||||||
#, python-brace-format
|
|
||||||
msgid "Yo {name}! Sup?"
|
|
||||||
msgstr "Ej {name}! Šta ima?"
|
|
||||||
|
|
||||||
#, python-brace-format
|
|
||||||
msgid "Hey {name} how are you doing?"
|
|
||||||
msgstr "Hej {name} kako si?"
|
|
||||||
|
|
||||||
#, python-brace-format
|
|
||||||
msgid "Unit {name} is nearby!"
|
|
||||||
msgstr "Jedinica {name} je blizu!"
|
|
||||||
|
|
||||||
#, python-brace-format
|
|
||||||
msgid "Uhm ... goodbye {name}"
|
|
||||||
msgstr "Umm ... doviđenja {name}"
|
|
||||||
|
|
||||||
#, python-brace-format
|
|
||||||
msgid "{name} is gone ..."
|
|
||||||
msgstr "{name} je nestao ..."
|
|
||||||
|
|
||||||
#, python-brace-format
|
|
||||||
msgid "Whoops ... {name} is gone."
|
|
||||||
msgstr "Ups ... {name} je nestao."
|
|
||||||
|
|
||||||
#, python-brace-format
|
|
||||||
msgid "{name} missed!"
|
|
||||||
msgstr "{name} promašen!"
|
|
||||||
|
|
||||||
msgid "Missed!"
|
|
||||||
msgstr "Promašen!"
|
|
||||||
|
|
||||||
msgid "Good friends are a blessing!"
|
|
||||||
msgstr "Dobri prijatelji su blagoslov!"
|
|
||||||
|
|
||||||
msgid "I love my friends!"
|
|
||||||
msgstr "Volim svoje prijatelje!"
|
|
||||||
|
|
||||||
msgid "Nobody wants to play with me ..."
|
|
||||||
msgstr "Niko ne želi da se igra sa mnom ..."
|
|
||||||
|
|
||||||
msgid "I feel so alone ..."
|
|
||||||
msgstr "Osećam se toliko usamljeno ..."
|
|
||||||
|
|
||||||
msgid "Where's everybody?!"
|
|
||||||
msgstr "Gde su svi?!"
|
|
||||||
|
|
||||||
#, python-brace-format
|
|
||||||
msgid "Napping for {secs}s ..."
|
|
||||||
msgstr "Dremam {secs}s ..."
|
|
||||||
|
|
||||||
msgid "Zzzzz"
|
|
||||||
msgstr "Zzzzz"
|
|
||||||
|
|
||||||
#, python-brace-format
|
|
||||||
msgid "ZzzZzzz ({secs}s)"
|
|
||||||
msgstr "ZzzZzzz ({secs}s)"
|
|
||||||
|
|
||||||
msgid "Good night."
|
|
||||||
msgstr "Laku noć."
|
|
||||||
|
|
||||||
msgid "Zzz"
|
|
||||||
msgstr "Zzz"
|
|
||||||
|
|
||||||
#, python-brace-format
|
|
||||||
msgid "Waiting for {secs}s ..."
|
|
||||||
msgstr "Čekam {secs}s ..."
|
|
||||||
|
|
||||||
#, python-brace-format
|
|
||||||
msgid "Looking around ({secs}s)"
|
|
||||||
msgstr "Gledam unaokolo ({secs}s)"
|
|
||||||
|
|
||||||
#, python-brace-format
|
|
||||||
msgid "Hey {what} let's be friends!"
|
|
||||||
msgstr "Hej {what} hajde da budemo prijatelji!"
|
|
||||||
|
|
||||||
#, python-brace-format
|
|
||||||
msgid "Associating to {what}"
|
|
||||||
msgstr "Povezujem se sa {what}"
|
|
||||||
|
|
||||||
#, python-brace-format
|
|
||||||
msgid "Yo {what}!"
|
|
||||||
msgstr "Ej {what}!"
|
|
||||||
|
|
||||||
#, python-brace-format
|
|
||||||
msgid "Just decided that {mac} needs no WiFi!"
|
|
||||||
msgstr "Upravo sam odlučio da {mac} ne zahteva WiFi!"
|
|
||||||
|
|
||||||
#, python-brace-format
|
|
||||||
msgid "Deauthenticating {mac}"
|
|
||||||
msgstr "Deauthenticating {mac}"
|
|
||||||
|
|
||||||
#, python-brace-format
|
|
||||||
msgid "Kickbanning {mac}!"
|
|
||||||
msgstr "Kickbanning {mac}!"
|
|
||||||
|
|
||||||
#, python-brace-format
|
|
||||||
msgid "Cool, we got {num} new handshake{plural}!"
|
|
||||||
msgstr "Kul, imamo {num} novih handshakeova"
|
|
||||||
|
|
||||||
#, python-brace-format
|
|
||||||
msgid "You have {count} new message{plural}!"
|
|
||||||
msgstr "Imas {count} novih poruka"
|
|
||||||
|
|
||||||
msgid "Oops, something went wrong ... Rebooting ..."
|
|
||||||
msgstr "Ups, nešto je poslo po zlu ... Restartujem ..."
|
|
||||||
|
|
||||||
#, python-brace-format
|
|
||||||
msgid "Uploading data to {to} ..."
|
|
||||||
msgstr "Otpremam podatke na {to} ..."
|
|
||||||
|
|
||||||
#, python-brace-format
|
|
||||||
msgid "Downloading from {name} ..."
|
|
||||||
msgstr "Preuzimam od {name} ..."
|
|
||||||
|
|
||||||
#, python-brace-format
|
|
||||||
msgid "Kicked {num} stations\n"
|
|
||||||
msgstr "Kickovano {num} stanica\n"
|
|
||||||
|
|
||||||
msgid "Made >999 new friends\n"
|
|
||||||
msgstr "Stekao sam >999 novih prijatelja\n"
|
|
||||||
|
|
||||||
#, python-brace-format
|
|
||||||
msgid "Made {num} new friends\n"
|
|
||||||
msgstr "Stekao sam {num} novih prijatelja\n"
|
|
||||||
|
|
||||||
#, python-brace-format
|
|
||||||
msgid "Got {num} handshakes\n"
|
|
||||||
msgstr "Imam {num} handshakeova\n"
|
|
||||||
|
|
||||||
msgid "Met 1 peer"
|
|
||||||
msgstr "Sreo 1 peera"
|
|
||||||
|
|
||||||
#, python-brace-format
|
|
||||||
msgid "Met {num} peers"
|
|
||||||
msgstr "Sreo {num} peerova"
|
|
||||||
|
|
||||||
#, python-brace-format
|
|
||||||
msgid ""
|
|
||||||
"I've been pwning for {duration} and kicked {deauthed} clients! I've also met "
|
|
||||||
"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi "
|
|
||||||
"#pwnlog #pwnlife #hacktheplanet #skynet"
|
|
||||||
msgstr ""
|
|
||||||
"Pwnujem već {duration} i kickovao sam {deauthed} klijenata! Takođe sam sreo "
|
|
||||||
"{associated} novih prijatelja i pojeo {handshakes} handshakeova! #pwnagotchi "
|
|
||||||
"#pwnlog #pwnlife #hacktheplanet #skynet"
|
|
||||||
|
|
||||||
msgid "hours"
|
|
||||||
msgstr "sati"
|
|
||||||
|
|
||||||
msgid "minutes"
|
|
||||||
msgstr "minuta"
|
|
||||||
|
|
||||||
msgid "seconds"
|
|
||||||
msgstr "sekundi"
|
|
||||||
|
|
||||||
msgid "hour"
|
|
||||||
msgstr "sat"
|
|
||||||
|
|
||||||
msgid "minute"
|
|
||||||
msgstr "minut"
|
|
||||||
|
|
||||||
msgid "second"
|
|
||||||
msgstr "sekunda"
|
|
Binary file not shown.
@ -9,8 +9,8 @@ msgstr ""
|
|||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2023-11-16 21:10+0100\n"
|
"POT-Creation-Date: 2023-11-16 21:10+0100\n"
|
||||||
"PO-Revision-Date: 2024-03-27 18:40+0800\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: AlanLeung <admin@mcnot.pro>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
"Language: Twi\n"
|
"Language: Twi\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
@ -18,218 +18,218 @@ msgstr ""
|
|||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
|
||||||
msgid "ZzzzZZzzzzZzzz"
|
msgid "ZzzzZZzzzzZzzz"
|
||||||
msgstr "ZzzzZZzzzzZzzz"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Hi, I'm Pwnagotchi! Starting ..."
|
msgid "Hi, I'm Pwnagotchi! Starting ..."
|
||||||
msgstr "HI!我是Pwnagotchi!\n程式啟動..."
|
msgstr ""
|
||||||
|
|
||||||
msgid "New day, new hunt, new pwns!"
|
msgid "New day, new hunt, new pwns!"
|
||||||
msgstr "新的一天!\n新的狩獵!新的入侵!"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Hack the Planet!"
|
msgid "Hack the Planet!"
|
||||||
msgstr "我要駭入\n地球的所有人!"
|
msgstr ""
|
||||||
|
|
||||||
msgid "AI ready."
|
msgid "AI ready."
|
||||||
msgstr "人工智慧已啟動。"
|
msgstr ""
|
||||||
|
|
||||||
msgid "The neural network is ready."
|
msgid "The neural network is ready."
|
||||||
msgstr "神經網路已啟動。"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Generating keys, do not turn off ..."
|
msgid "Generating keys, do not turn off ..."
|
||||||
msgstr "產生金鑰中,\n請勿關閉..."
|
msgstr ""
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Hey, channel {channel} is free! Your AP will say thanks."
|
msgid "Hey, channel {channel} is free! Your AP will say thanks."
|
||||||
msgstr "嘿,{channel}很順暢!\n你的WIFI會感謝你的。"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Reading last session logs ..."
|
msgid "Reading last session logs ..."
|
||||||
msgstr "正在閱讀最後的會話紀錄..."
|
msgstr ""
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Read {lines_so_far} log lines so far ..."
|
msgid "Read {lines_so_far} log lines so far ..."
|
||||||
msgstr "目前已經閱讀了 {lines_so_far} 行的紀錄..."
|
msgstr ""
|
||||||
|
|
||||||
msgid "I'm bored ..."
|
msgid "I'm bored ..."
|
||||||
msgstr "我好無聊..."
|
msgstr ""
|
||||||
|
|
||||||
msgid "Let's go for a walk!"
|
msgid "Let's go for a walk!"
|
||||||
msgstr "我們! 散步! 散步散步散步散步"
|
msgstr ""
|
||||||
|
|
||||||
msgid "This is the best day of my life!"
|
msgid "This is the best day of my life!"
|
||||||
msgstr "這是我生命中最棒的一天!"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Shitty day :/"
|
msgid "Shitty day :/"
|
||||||
msgstr "糟糕的一天 :/"
|
msgstr ""
|
||||||
|
|
||||||
msgid "I'm extremely bored ..."
|
msgid "I'm extremely bored ..."
|
||||||
msgstr "我超無聊的...炒雞 炒雞的那種"
|
msgstr ""
|
||||||
|
|
||||||
msgid "I'm very sad ..."
|
msgid "I'm very sad ..."
|
||||||
msgstr "我好難過..."
|
msgstr ""
|
||||||
|
|
||||||
msgid "I'm sad"
|
msgid "I'm sad"
|
||||||
msgstr "嗚嗚嗚...."
|
msgstr ""
|
||||||
|
|
||||||
msgid "Leave me alone ..."
|
msgid "Leave me alone ..."
|
||||||
msgstr "尼奏凱啦臭臭"
|
msgstr ""
|
||||||
|
|
||||||
msgid "I'm mad at you!"
|
msgid "I'm mad at you!"
|
||||||
msgstr "喔氣氣氣氣氣ˋ^ˊ"
|
msgstr ""
|
||||||
|
|
||||||
msgid "I'm living the life!"
|
msgid "I'm living the life!"
|
||||||
msgstr "真是充實的一生!"
|
msgstr ""
|
||||||
|
|
||||||
msgid "I pwn therefore I am."
|
msgid "I pwn therefore I am."
|
||||||
msgstr "我駭故我在."
|
msgstr ""
|
||||||
|
|
||||||
msgid "So many networks!!!"
|
msgid "So many networks!!!"
|
||||||
msgstr "好多網路啊!!!吃! 吃他! 吃光光!!!"
|
msgstr ""
|
||||||
|
|
||||||
msgid "I'm having so much fun!"
|
msgid "I'm having so much fun!"
|
||||||
msgstr "我玩的超級開心!"
|
msgstr ""
|
||||||
|
|
||||||
msgid "My crime is that of curiosity ..."
|
msgid "My crime is that of curiosity ..."
|
||||||
msgstr "我的缺點就是\n太好奇了..."
|
msgstr ""
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Hello {name}! Nice to meet you."
|
msgid "Hello {name}! Nice to meet you."
|
||||||
msgstr "尼豪{name}!\n很高興認識你!!!!"
|
msgstr ""
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Yo {name}! Sup?"
|
msgid "Yo {name}! Sup?"
|
||||||
msgstr "嗨 {name}! 你來攻打我的村莊?"
|
msgstr ""
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Hey {name} how are you doing?"
|
msgid "Hey {name} how are you doing?"
|
||||||
msgstr "嗨 {name} 你最近過得如何˙ˇ˙?"
|
msgstr ""
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Unit {name} is nearby!"
|
msgid "Unit {name} is nearby!"
|
||||||
msgstr "{name}\n就在附近!"
|
msgstr ""
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Uhm ... goodbye {name}"
|
msgid "Uhm ... goodbye {name}"
|
||||||
msgstr "哦嗚 ... \n拜拜{name}"
|
msgstr ""
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "{name} is gone ..."
|
msgid "{name} is gone ..."
|
||||||
msgstr "{name}\n不見了 ..."
|
msgstr ""
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Whoops ... {name} is gone."
|
msgid "Whoops ... {name} is gone."
|
||||||
msgstr "哦歐...\n{name}\n不見了。"
|
msgstr ""
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "{name} missed!"
|
msgid "{name} missed!"
|
||||||
msgstr "我剛剛錯過了{name}!"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Missed!"
|
msgid "Missed!"
|
||||||
msgstr "又錯過了!"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Good friends are a blessing!"
|
msgid "Good friends are a blessing!"
|
||||||
msgstr "有個好朋友\n真幸福!"
|
msgstr ""
|
||||||
|
|
||||||
msgid "I love my friends!"
|
msgid "I love my friends!"
|
||||||
msgstr "我喜歡\n我的朋友!"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Nobody wants to play with me ..."
|
msgid "Nobody wants to play with me ..."
|
||||||
msgstr "沒人想跟我玩..."
|
msgstr ""
|
||||||
|
|
||||||
msgid "I feel so alone ..."
|
msgid "I feel so alone ..."
|
||||||
msgstr "我覺得好孤單..."
|
msgstr ""
|
||||||
|
|
||||||
msgid "Where's everybody?!"
|
msgid "Where's everybody?!"
|
||||||
msgstr "大家都去哪裡了?!"
|
msgstr ""
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Napping for {secs}s ..."
|
msgid "Napping for {secs}s ..."
|
||||||
msgstr "我想瞇{secs}秒一下..."
|
msgstr ""
|
||||||
|
|
||||||
msgid "Zzzzz"
|
msgid "Zzzzz"
|
||||||
msgstr "Zzzzz"
|
msgstr ""
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "ZzzZzzz ({secs}s)"
|
msgid "ZzzZzzz ({secs}s)"
|
||||||
msgstr "ZzzZzzz({secs}秒)"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Good night."
|
msgid "Good night."
|
||||||
msgstr "晚安!"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Zzz"
|
msgid "Zzz"
|
||||||
msgstr "Zzz"
|
msgstr ""
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Waiting for {secs}s ..."
|
msgid "Waiting for {secs}s ..."
|
||||||
msgstr "等我{secs}秒..."
|
msgstr ""
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Looking around ({secs}s)"
|
msgid "Looking around ({secs}s)"
|
||||||
msgstr "環顧四周({secs}秒)"
|
msgstr ""
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Hey {what} let's be friends!"
|
msgid "Hey {what} let's be friends!"
|
||||||
msgstr "嗨\n{what}\n讓我們來當朋友吧!"
|
msgstr ""
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Associating to {what}"
|
msgid "Associating to {what}"
|
||||||
msgstr "正在連接\n{what}"
|
msgstr ""
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Yo {what}!"
|
msgid "Yo {what}!"
|
||||||
msgstr "喲,\n{what}!"
|
msgstr ""
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Just decided that {mac} needs no WiFi!"
|
msgid "Just decided that {mac} needs no WiFi!"
|
||||||
msgstr "我要讓\n{mac}\n斷線!\n他不需要上網!"
|
msgstr ""
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Deauthenticating {mac}"
|
msgid "Deauthenticating {mac}"
|
||||||
msgstr "解除\n{mac}\n的授權中"
|
msgstr ""
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Kickbanning {mac}!"
|
msgid "Kickbanning {mac}!"
|
||||||
msgstr "把\n{mac}\n踢出中!"
|
msgstr ""
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Cool, we got {num} new handshake{plural}!"
|
msgid "Cool, we got {num} new handshake{plural}!"
|
||||||
msgstr "酷耶,我們抓到{num}個\n新的握手包{plural}!"
|
msgstr ""
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "You have {count} new message{plural}!"
|
msgid "You have {count} new message{plural}!"
|
||||||
msgstr "你有{count}個新訊息{plural}!"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Oops, something went wrong ... Rebooting ..."
|
msgid "Oops, something went wrong ... Rebooting ..."
|
||||||
msgstr "哦歐,有些地方出錯了...\n重新啟動中..."
|
msgstr ""
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Uploading data to {to} ..."
|
msgid "Uploading data to {to} ..."
|
||||||
msgstr "正在上傳資料到 {to} ..."
|
msgstr ""
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Downloading from {name} ..."
|
msgid "Downloading from {name} ..."
|
||||||
msgstr "正在從 {name} 下載資料..."
|
msgstr ""
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Kicked {num} stations\n"
|
msgid "Kicked {num} stations\n"
|
||||||
msgstr "踢了 {num} 個設備\n"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Made >999 new friends\n"
|
msgid "Made >999 new friends\n"
|
||||||
msgstr "交了 >999 個新朋友\n"
|
msgstr ""
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Made {num} new friends\n"
|
msgid "Made {num} new friends\n"
|
||||||
msgstr "交了 {num} 個新朋友\n"
|
msgstr ""
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Got {num} handshakes\n"
|
msgid "Got {num} handshakes\n"
|
||||||
msgstr "捕獲了 {num} 個握手包\n"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Met 1 peer"
|
msgid "Met 1 peer"
|
||||||
msgstr "遇到了 1 個同好"
|
msgstr ""
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Met {num} peers"
|
msgid "Met {num} peers"
|
||||||
msgstr "遇到了 {num} 個同好"
|
msgstr ""
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid ""
|
msgid ""
|
||||||
@ -237,24 +237,21 @@ msgid ""
|
|||||||
"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi "
|
"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi "
|
||||||
"#pwnlog #pwnlife #hacktheplanet #skynet"
|
"#pwnlog #pwnlife #hacktheplanet #skynet"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"我花了{duration}的時間\n駭入和踢了{deauthed}好多設備."
|
|
||||||
"我還交了好多{associated}新朋友,\n而且抓到了{handshakes}握手包!"
|
|
||||||
"#pwnagotchi#入侵日志 #駭客人生 #入侵整個星球 #天網 #我好棒˙ˇ˙"
|
|
||||||
|
|
||||||
msgid "hours"
|
msgid "hours"
|
||||||
msgstr "時"
|
msgstr ""
|
||||||
|
|
||||||
msgid "minutes"
|
msgid "minutes"
|
||||||
msgstr "分"
|
msgstr ""
|
||||||
|
|
||||||
msgid "seconds"
|
msgid "seconds"
|
||||||
msgstr "秒"
|
msgstr ""
|
||||||
|
|
||||||
msgid "hour"
|
msgid "hour"
|
||||||
msgstr "時"
|
msgstr ""
|
||||||
|
|
||||||
msgid "minute"
|
msgid "minute"
|
||||||
msgstr "分"
|
msgstr ""
|
||||||
|
|
||||||
msgid "second"
|
msgid "second"
|
||||||
msgstr "秒"
|
msgstr ""
|
||||||
|
@ -8,7 +8,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2024-11-17 20:51+0100\n"
|
"POT-Creation-Date: 2024-02-16 15:26-0300\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
|
@ -217,44 +217,24 @@ class LastSession(object):
|
|||||||
def setup_logging(args, config):
|
def setup_logging(args, config):
|
||||||
cfg = config['main']['log']
|
cfg = config['main']['log']
|
||||||
filename = cfg['path']
|
filename = cfg['path']
|
||||||
filenameDebug = cfg['path-debug']
|
|
||||||
|
|
||||||
#global formatter
|
formatter = logging.Formatter("[%(asctime)s] [%(levelname)s] %(message)s")
|
||||||
formatter = logging.Formatter("[%(asctime)s] [%(levelname)s] [%(threadName)s] : %(message)s")
|
root = logging.getLogger()
|
||||||
logger = logging.getLogger()
|
|
||||||
|
|
||||||
for handler in logger.handlers:
|
root.setLevel(logging.DEBUG if args.debug else logging.INFO)
|
||||||
handler.setLevel(logging.DEBUG if args.debug else logging.INFO)
|
|
||||||
handler.setFormatter(formatter)
|
|
||||||
|
|
||||||
|
|
||||||
logger.setLevel(logging.DEBUG if args.debug else logging.INFO)
|
|
||||||
|
|
||||||
if filename:
|
if filename:
|
||||||
# since python default log rotation might break session data in different files,
|
# since python default log rotation might break session data in different files,
|
||||||
# we need to do log rotation ourselves
|
# we need to do log rotation ourselves
|
||||||
log_rotation(filename, cfg)
|
log_rotation(filename, cfg)
|
||||||
log_rotation(filenameDebug, cfg)
|
|
||||||
|
|
||||||
|
file_handler = logging.FileHandler(filename)
|
||||||
|
file_handler.setFormatter(formatter)
|
||||||
|
root.addHandler(file_handler)
|
||||||
|
|
||||||
|
console_handler = logging.StreamHandler()
|
||||||
# File handler for logging all normal messages
|
console_handler.setFormatter(formatter)
|
||||||
file_handler = logging.FileHandler(filename) #creates new
|
root.addHandler(console_handler)
|
||||||
file_handler.setLevel(logging.INFO)
|
|
||||||
file_handler.setFormatter(formatter)
|
|
||||||
logger.addHandler(file_handler)
|
|
||||||
|
|
||||||
# File handler for logging all debug messages
|
|
||||||
file_handler = logging.FileHandler(filenameDebug) #creates new
|
|
||||||
file_handler.setLevel(logging.DEBUG)
|
|
||||||
file_handler.setFormatter(formatter)
|
|
||||||
logger.addHandler(file_handler)
|
|
||||||
|
|
||||||
# Console handler for logging debug messages if args.debug is true else just log normal
|
|
||||||
#console_handler = logging.StreamHandler() #creates new
|
|
||||||
#console_handler.setLevel(logging.DEBUG if args.debug else logging.INFO)
|
|
||||||
#console_handler.setFormatter(formatter)
|
|
||||||
#logger.addHandler(console_handler)
|
|
||||||
|
|
||||||
if not args.debug:
|
if not args.debug:
|
||||||
# disable scapy and tensorflow logging
|
# disable scapy and tensorflow logging
|
||||||
@ -270,8 +250,6 @@ def setup_logging(args, config):
|
|||||||
requests_log.prpagate = False
|
requests_log.prpagate = False
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def log_rotation(filename, cfg):
|
def log_rotation(filename, cfg):
|
||||||
rotation = cfg['rotation']
|
rotation = cfg['rotation']
|
||||||
if not rotation['enabled']:
|
if not rotation['enabled']:
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
#import _thread
|
import _thread
|
||||||
import threading
|
|
||||||
import logging
|
import logging
|
||||||
import time
|
import time
|
||||||
|
|
||||||
@ -42,8 +41,7 @@ class AsyncAdvertiser(object):
|
|||||||
|
|
||||||
def start_advertising(self):
|
def start_advertising(self):
|
||||||
if self._config['personality']['advertise']:
|
if self._config['personality']['advertise']:
|
||||||
#_thread.start_new_thread(self._adv_poller, ())
|
_thread.start_new_thread(self._adv_poller, ())
|
||||||
threading.Thread(target=self._adv_poller,args=(), name="Grid", daemon=True).start()
|
|
||||||
|
|
||||||
grid.set_advertisement_data(self._advertisement)
|
grid.set_advertisement_data(self._advertisement)
|
||||||
grid.advertise(True)
|
grid.advertise(True)
|
||||||
|
@ -6,7 +6,6 @@ import logging
|
|||||||
import os
|
import os
|
||||||
import threading
|
import threading
|
||||||
import pwnagotchi.grid
|
import pwnagotchi.grid
|
||||||
import prctl
|
|
||||||
|
|
||||||
default_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "default")
|
default_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "default")
|
||||||
loaded = {}
|
loaded = {}
|
||||||
@ -73,7 +72,6 @@ def toggle_plugin(name, enable=True):
|
|||||||
|
|
||||||
def on(event_name, *args, **kwargs):
|
def on(event_name, *args, **kwargs):
|
||||||
for plugin_name in loaded.keys():
|
for plugin_name in loaded.keys():
|
||||||
|
|
||||||
one(plugin_name, event_name, *args, **kwargs)
|
one(plugin_name, event_name, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
@ -84,10 +82,7 @@ def locked_cb(lock_name, cb, *args, **kwargs):
|
|||||||
locks[lock_name] = threading.Lock()
|
locks[lock_name] = threading.Lock()
|
||||||
|
|
||||||
with locks[lock_name]:
|
with locks[lock_name]:
|
||||||
# Setting the thread name using prctl
|
cb(*args, *kwargs)
|
||||||
plugin_name, plugin_cb = lock_name.split("::")
|
|
||||||
prctl.set_name(f"{plugin_name}.{plugin_cb}")
|
|
||||||
cb(*args, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
def one(plugin_name, event_name, *args, **kwargs):
|
def one(plugin_name, event_name, *args, **kwargs):
|
||||||
@ -100,10 +95,8 @@ def one(plugin_name, event_name, *args, **kwargs):
|
|||||||
if callback is not None and callable(callback):
|
if callback is not None and callable(callback):
|
||||||
try:
|
try:
|
||||||
lock_name = "%s::%s" % (plugin_name, cb_name)
|
lock_name = "%s::%s" % (plugin_name, cb_name)
|
||||||
thread_name = f'{plugin_name}.{cb_name}'
|
locked_cb_args = (lock_name, callback, *args, *kwargs)
|
||||||
thread = threading.Thread(target=locked_cb, args=(lock_name, callback, *args, *kwargs), name=thread_name, daemon=True)
|
_thread.start_new_thread(locked_cb, locked_cb_args)
|
||||||
thread.start()
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error("error while running %s.%s : %s" % (plugin_name, cb_name, e))
|
logging.error("error while running %s.%s : %s" % (plugin_name, cb_name, e))
|
||||||
logging.error(e, exc_info=True)
|
logging.error(e, exc_info=True)
|
||||||
|
@ -199,9 +199,6 @@ def list_plugins(args, config, pattern='*'):
|
|||||||
available_not_installed = set(available.keys()) - set(installed.keys())
|
available_not_installed = set(available.keys()) - set(installed.keys())
|
||||||
|
|
||||||
max_len_list = available_and_installed if args.installed else available_not_installed
|
max_len_list = available_and_installed if args.installed else available_not_installed
|
||||||
if not max_len_list:
|
|
||||||
print('Maybe try: sudo pwnagotchi plugins update')
|
|
||||||
return 1
|
|
||||||
max_len = max(map(len, max_len_list))
|
max_len = max(map(len, max_len_list))
|
||||||
header = line.format(name='Plugin', width=max_len, version='Version', enabled='Active', status='Status')
|
header = line.format(name='Plugin', width=max_len, version='Version', enabled='Active', status='Status')
|
||||||
line_length = max(max_len, len('Plugin')) + len(header) - len('Plugin') - 12 # lol
|
line_length = max(max_len, len('Plugin')) + len(header) - len('Plugin') - 12 # lol
|
||||||
@ -242,7 +239,7 @@ def list_plugins(args, config, pattern='*'):
|
|||||||
print('-' * line_length)
|
print('-' * line_length)
|
||||||
|
|
||||||
if not found:
|
if not found:
|
||||||
print('Maybe try: sudo pwnagotchi plugins update')
|
logging.info('Maybe try: pwnagotchi plugins update')
|
||||||
return 1
|
return 1
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
@ -29,7 +29,6 @@ def check(version, repo, native=True):
|
|||||||
latest = resp.json()
|
latest = resp.json()
|
||||||
info['available'] = latest_ver = latest['tag_name'].replace('v', '')
|
info['available'] = latest_ver = latest['tag_name'].replace('v', '')
|
||||||
is_armhf = info['arch'].startswith('arm')
|
is_armhf = info['arch'].startswith('arm')
|
||||||
is_aarch = info['arch'].startswith('aarch')
|
|
||||||
|
|
||||||
local = version_to_tuple(info['current'])
|
local = version_to_tuple(info['current'])
|
||||||
remote = version_to_tuple(latest_ver)
|
remote = version_to_tuple(latest_ver)
|
||||||
@ -45,14 +44,6 @@ def check(version, repo, native=True):
|
|||||||
(info['arch'] in download_url or (is_armhf and 'armhf' in download_url))):
|
(info['arch'] in download_url or (is_armhf and 'armhf' in download_url))):
|
||||||
info['url'] = download_url
|
info['url'] = download_url
|
||||||
break
|
break
|
||||||
elif is_aarch:
|
|
||||||
# check if this release is compatible with arm64/aarch64
|
|
||||||
for asset in latest['assets']:
|
|
||||||
download_url = asset['browser_download_url']
|
|
||||||
if (download_url.endswith('.zip') and
|
|
||||||
(info['arch'] in download_url or (is_aarch and 'aarch' in download_url))):
|
|
||||||
info['url'] = download_url
|
|
||||||
break
|
|
||||||
|
|
||||||
return info
|
return info
|
||||||
|
|
||||||
@ -230,7 +221,7 @@ class AutoUpdate(plugins.Plugin):
|
|||||||
if num_installed > 0:
|
if num_installed > 0:
|
||||||
display.update(force=True, new_data={'status': 'Rebooting ...'})
|
display.update(force=True, new_data={'status': 'Rebooting ...'})
|
||||||
time.sleep(3)
|
time.sleep(3)
|
||||||
pwnagotchi.reboot()
|
os.system("service pwnagotchi restart")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error("[update] %s" % e)
|
logging.error("[update] %s" % e)
|
||||||
|
@ -578,7 +578,7 @@ class BTTether(plugins.Plugin):
|
|||||||
def on_ui_setup(self, ui):
|
def on_ui_setup(self, ui):
|
||||||
with ui._lock:
|
with ui._lock:
|
||||||
ui.add_element('bluetooth', LabeledValue(color=BLACK, label='BT', value='-',
|
ui.add_element('bluetooth', LabeledValue(color=BLACK, label='BT', value='-',
|
||||||
position=(ui.width() / 2 - 20, 0),
|
position=(ui.width() / 2 - 10, 0),
|
||||||
label_font=fonts.Bold, text_font=fonts.Medium))
|
label_font=fonts.Bold, text_font=fonts.Medium))
|
||||||
|
|
||||||
def on_ui_update(self, ui):
|
def on_ui_update(self, ui):
|
||||||
|
@ -4,7 +4,6 @@ import subprocess
|
|||||||
import time
|
import time
|
||||||
import random
|
import random
|
||||||
from io import TextIOWrapper
|
from io import TextIOWrapper
|
||||||
import os
|
|
||||||
|
|
||||||
import pwnagotchi
|
import pwnagotchi
|
||||||
from pwnagotchi import plugins
|
from pwnagotchi import plugins
|
||||||
@ -29,21 +28,21 @@ class FixServices(plugins.Plugin):
|
|||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.options = dict()
|
self.options = dict()
|
||||||
self.pattern = re.compile(r'ieee80211 phy0: brcmf_cfg80211_add_iface: iface validation failed: err=-95')
|
self.pattern = re.compile(r'brcmf_cfg80211_nexmon_set_channel.*?Set Channel failed')
|
||||||
self.pattern2 = re.compile(r'wifi error while hopping to channel')
|
self.pattern2 = re.compile(r'wifi error while hopping to channel')
|
||||||
self.pattern3 = re.compile(r'Firmware has halted or crashed')
|
self.pattern3 = re.compile(r'Firmware has halted or crashed')
|
||||||
self.pattern4 = re.compile(r'error 400: could not find interface wlan0mon')
|
self.pattern4 = re.compile(r'error 400: could not find interface wlan0mon')
|
||||||
self.pattern5 = re.compile(r'fatal error: concurrent map iteration and map write')
|
|
||||||
self.pattern6 = re.compile(r'panic: runtime error')
|
|
||||||
self.pattern7 = re.compile(r'ieee80211 phy0: _brcmf_set_multicast_list: Setting allmulti failed, -110')
|
|
||||||
self.isReloadingMon = False
|
self.isReloadingMon = False
|
||||||
self.connection = None
|
self.connection = None
|
||||||
self.LASTTRY = 0
|
self.LASTTRY = 0
|
||||||
|
self._status = "--"
|
||||||
|
self._count = 0
|
||||||
|
|
||||||
def on_loaded(self):
|
def on_loaded(self):
|
||||||
"""
|
"""
|
||||||
Gets called when the plugin gets loaded
|
Gets called when the plugin gets loaded
|
||||||
"""
|
"""
|
||||||
|
self._status = "ld"
|
||||||
logging.info("[Fix_Services] plugin loaded.")
|
logging.info("[Fix_Services] plugin loaded.")
|
||||||
|
|
||||||
def on_ready(self, agent):
|
def on_ready(self, agent):
|
||||||
@ -51,13 +50,31 @@ class FixServices(plugins.Plugin):
|
|||||||
stdout=subprocess.PIPE).stdout))[-10:])
|
stdout=subprocess.PIPE).stdout))[-10:])
|
||||||
try:
|
try:
|
||||||
cmd_output = subprocess.check_output("ip link show wlan0mon", shell=True)
|
cmd_output = subprocess.check_output("ip link show wlan0mon", shell=True)
|
||||||
logging.debug("[Fix_Services ip link show wlan0mon]: %s" % repr(cmd_output))
|
logging.info("[Fix_Services ip link show wlan0mon]: %s" % repr(cmd_output))
|
||||||
if ",UP," in str(cmd_output):
|
if ",UP," in str(cmd_output):
|
||||||
logging.debug("wlan0mon is up.")
|
logging.info("wlan0mon is up.")
|
||||||
|
self._status = "up"
|
||||||
|
|
||||||
|
if len(self.pattern.findall(last_lines)) >= 3:
|
||||||
|
self._status = "XX"
|
||||||
|
if hasattr(agent, 'view'):
|
||||||
|
display = agent.view()
|
||||||
|
display.set('status', 'Blind-Bug detected. Restarting.')
|
||||||
|
display.update(force=True)
|
||||||
|
logging.info('[Fix_Services] Blind-Bug detected. Restarting.')
|
||||||
|
try:
|
||||||
|
self._tryTurningItOffAndOnAgain(agent)
|
||||||
|
except Exception as err:
|
||||||
|
logging.warning("[Fix_Services turnOffAndOn] %s" % repr(err))
|
||||||
|
|
||||||
|
else:
|
||||||
|
logging.info("[Fix_Services] Logs look good!")
|
||||||
|
self._status = ""
|
||||||
|
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
logging.error("[Fix_Services ip link show wlan0mon]: %s" % repr(err))
|
logging.error("[Fix_Services ip link show wlan0mon]: %s" % repr(err))
|
||||||
try:
|
try:
|
||||||
|
self._status = "xx"
|
||||||
self._tryTurningItOffAndOnAgain(agent)
|
self._tryTurningItOffAndOnAgain(agent)
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
logging.error("[Fix_Services OffNOn]: %s" % repr(err))
|
logging.error("[Fix_Services OffNOn]: %s" % repr(err))
|
||||||
@ -67,12 +84,12 @@ class FixServices(plugins.Plugin):
|
|||||||
# apparently this only gets messages from bettercap going to syslog, not from syslog
|
# apparently this only gets messages from bettercap going to syslog, not from syslog
|
||||||
def on_bcap_sys_log(self, agent, event):
|
def on_bcap_sys_log(self, agent, event):
|
||||||
if re.search('wifi error while hopping to channel', event['data']['Message']):
|
if re.search('wifi error while hopping to channel', event['data']['Message']):
|
||||||
logging.debug("[Fix_Services]SYSLOG MATCH: %s" % event['data']['Message'])
|
logging.info("[Fix_Services]SYSLOG MATCH: %s" % event['data']['Message'])
|
||||||
logging.debug("[Fix_Services]**** restarting wifi.recon")
|
logging.info("[Fix_Services]**** restarting wifi.recon")
|
||||||
try:
|
try:
|
||||||
result = agent.run("wifi.recon off; wifi.recon on")
|
result = agent.run("wifi.recon off; wifi.recon on")
|
||||||
if result["success"]:
|
if result["success"]:
|
||||||
logging.debug("[Fix_Services] wifi.recon flip: success!")
|
logging.info("[Fix_Services] wifi.recon flip: success!")
|
||||||
if hasattr(agent, 'view'):
|
if hasattr(agent, 'view'):
|
||||||
display = agent.view()
|
display = agent.view()
|
||||||
if display:
|
if display:
|
||||||
@ -92,36 +109,45 @@ class FixServices(plugins.Plugin):
|
|||||||
other_last_lines = ''.join(list(TextIOWrapper(subprocess.Popen(['journalctl', '-n10'],
|
other_last_lines = ''.join(list(TextIOWrapper(subprocess.Popen(['journalctl', '-n10'],
|
||||||
stdout=subprocess.PIPE).stdout))[-10:])
|
stdout=subprocess.PIPE).stdout))[-10:])
|
||||||
other_other_last_lines = ''.join(
|
other_other_last_lines = ''.join(
|
||||||
list(TextIOWrapper(subprocess.Popen(['tail', '-n10', '/etc/pwnagotchi/log/pwnagotchi.log'],
|
list(TextIOWrapper(subprocess.Popen(['tail', '-n10', '/var/log/pwnagotchi.log'],
|
||||||
stdout=subprocess.PIPE).stdout))[-10:])
|
stdout=subprocess.PIPE).stdout))[-10:])
|
||||||
# don't check if we ran a reset recently
|
# don't check if we ran a reset recently
|
||||||
logging.debug("[Fix_Services]**** epoch")
|
logging.debug("[Fix_Services]**** epoch")
|
||||||
if time.time() - self.LASTTRY > 180:
|
if time.time() - self.LASTTRY > 180:
|
||||||
# get last 10 lines
|
# get last 10 lines
|
||||||
display = agent.view()
|
display = None
|
||||||
|
|
||||||
logging.debug("[Fix_Services]**** checking")
|
logging.debug("[Fix_Services]**** checking")
|
||||||
if len(self.pattern.findall(last_lines)) >= 1:
|
|
||||||
subprocess.check_output("monstop", shell=True)
|
# Look for pattern 1
|
||||||
subprocess.check_output("monstart", shell=True)
|
if len(self.pattern.findall(last_lines)) >= 3:
|
||||||
display.set('status', 'Wifi channel stuck. Restarting recon.')
|
logging.info("[Fix_Services]**** Should trigger a reload of the wlan0mon device:\n%s" % last_lines)
|
||||||
display.update(force=True)
|
if hasattr(agent, 'view'):
|
||||||
pwnagotchi.restart("AUTO")
|
display = agent.view()
|
||||||
|
display.set('status', 'Blind-Bug detected. Restarting.')
|
||||||
|
display.update(force=True)
|
||||||
|
logging.info('[Fix_Services] Blind-Bug detected. Restarting.')
|
||||||
|
try:
|
||||||
|
self._tryTurningItOffAndOnAgain(agent)
|
||||||
|
except Exception as err:
|
||||||
|
logging.warning("[Fix_Services] TTOAOA: %s" % repr(err))
|
||||||
|
|
||||||
# Look for pattern 2
|
# Look for pattern 2
|
||||||
elif len(self.pattern2.findall(other_last_lines)) >= 5:
|
elif len(self.pattern2.findall(other_last_lines)) >= 5:
|
||||||
logging.debug("[Fix_Services]**** Should trigger a reload of the wlan0mon device:\n%s" % last_lines)
|
logging.info("[Fix_Services]**** Should trigger a reload of the wlan0mon device:\n%s" % last_lines)
|
||||||
if hasattr(agent, 'view'):
|
if hasattr(agent, 'view'):
|
||||||
|
display = agent.view()
|
||||||
display.set('status', 'Wifi channel stuck. Restarting recon.')
|
display.set('status', 'Wifi channel stuck. Restarting recon.')
|
||||||
display.update(force=True)
|
display.update(force=True)
|
||||||
logging.debug('[Fix_Services] Wifi channel stuck. Restarting recon.')
|
logging.info('[Fix_Services] Wifi channel stuck. Restarting recon.')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
result = agent.run("wifi.recon off; wifi.recon on")
|
result = agent.run("wifi.recon off; wifi.recon on")
|
||||||
if result["success"]:
|
if result["success"]:
|
||||||
logging.debug("[Fix_Services] wifi.recon flip: success!")
|
logging.info("[Fix_Services] wifi.recon flip: success!")
|
||||||
if display:
|
if display:
|
||||||
display.update(force=True, new_data={"status": "Wifi recon flipped!",
|
display.update(force=True, new_data={"status": "Wifi recon flipped!",
|
||||||
|
"brcmfmac_status": self._status,
|
||||||
"face": faces.COOL})
|
"face": faces.COOL})
|
||||||
else:
|
else:
|
||||||
print("Wifi recon flipped\nthat was easy!")
|
print("Wifi recon flipped\nthat was easy!")
|
||||||
@ -133,65 +159,32 @@ class FixServices(plugins.Plugin):
|
|||||||
|
|
||||||
# Look for pattern 3
|
# Look for pattern 3
|
||||||
elif len(self.pattern3.findall(other_last_lines)) >= 1:
|
elif len(self.pattern3.findall(other_last_lines)) >= 1:
|
||||||
logging.debug("[Fix_Services] Firmware has halted or crashed. Restarting wlan0mon.")
|
logging.info("[Fix_Services] Firmware has halted or crashed. Restarting wlan0mon.")
|
||||||
if hasattr(agent, 'view'):
|
if hasattr(agent, 'view'):
|
||||||
|
display = agent.view()
|
||||||
display.set('status', 'Firmware has halted or crashed. Restarting wlan0mon.')
|
display.set('status', 'Firmware has halted or crashed. Restarting wlan0mon.')
|
||||||
display.update(force=True)
|
display.update(force=True)
|
||||||
try:
|
try:
|
||||||
# Run the monstart command to restart wlan0mon
|
# Run the monstart command to restart wlan0mon
|
||||||
cmd_output = subprocess.check_output("monstart", shell=True)
|
cmd_output = subprocess.check_output("monstart", shell=True)
|
||||||
logging.debug("[Fix_Services monstart]: %s" % repr(cmd_output))
|
logging.info("[Fix_Services monstart]: %s" % repr(cmd_output))
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
logging.error("[Fix_Services monstart]: %s" % repr(err))
|
logging.error("[Fix_Services monstart]: %s" % repr(err))
|
||||||
|
|
||||||
# Look for pattern 4
|
# Look for pattern 4
|
||||||
elif len(self.pattern4.findall(other_other_last_lines)) >= 3:
|
elif len(self.pattern4.findall(other_other_last_lines)) >= 3:
|
||||||
logging.debug("[Fix_Services] wlan0 is down!")
|
logging.info("[Fix_Services] wlan0 is down!")
|
||||||
if hasattr(agent, 'view'):
|
if hasattr(agent, 'view'):
|
||||||
|
display = agent.view()
|
||||||
display.set('status', 'Restarting wlan0 now!')
|
display.set('status', 'Restarting wlan0 now!')
|
||||||
display.update(force=True)
|
display.update(force=True)
|
||||||
try:
|
try:
|
||||||
# Run the monstart command to restart wlan0mon
|
# Run the monstart command to restart wlan0mon
|
||||||
cmd_output = subprocess.check_output("monstart", shell=True)
|
cmd_output = subprocess.check_output("monstart", shell=True)
|
||||||
logging.debug("[Fix_Services monstart]: %s" % repr(cmd_output))
|
logging.info("[Fix_Services monstart]: %s" % repr(cmd_output))
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
logging.error("[Fix_Services monstart]: %s" % repr(err))
|
logging.error("[Fix_Services monstart]: %s" % repr(err))
|
||||||
|
|
||||||
# Look for pattern 5
|
|
||||||
elif len(self.pattern5.findall(other_other_last_lines)) >= 1:
|
|
||||||
logging.debug("[Fix_Services] Bettercap has crashed!")
|
|
||||||
if hasattr(agent, 'view'):
|
|
||||||
display.set('status', 'Restarting pwnagotchi!')
|
|
||||||
display.update(force=True)
|
|
||||||
os.system("systemctl restart bettercap")
|
|
||||||
pwnagotchi.restart("AUTO")
|
|
||||||
|
|
||||||
# Look for pattern 6
|
|
||||||
elif len(self.pattern6.findall(other_other_last_lines)) >= 1:
|
|
||||||
logging.debug("[Fix_Services] Bettercap has crashed!")
|
|
||||||
if hasattr(agent, 'view'):
|
|
||||||
display.set('status', 'Restarting pwnagotchi!')
|
|
||||||
display.update(force=True)
|
|
||||||
os.system("systemctl restart bettercap")
|
|
||||||
pwnagotchi.restart("AUTO")
|
|
||||||
|
|
||||||
# Look for pattern 7
|
|
||||||
elif len(self.pattern7.findall(other_other_last_lines)) >= 1:
|
|
||||||
logging.debug("[Fix_Services] Monitor mode failed!")
|
|
||||||
try:
|
|
||||||
result = agent.run("wifi.recon off; wifi.recon on")
|
|
||||||
if result["success"]:
|
|
||||||
logging.debug("[Fix_Services] wifi.recon flip: success!")
|
|
||||||
if display:
|
|
||||||
display.update(force=True, new_data={"status": "Wifi recon flipped!",
|
|
||||||
"face": faces.COOL})
|
|
||||||
else:
|
|
||||||
print("Wifi recon flipped\nthat was easy!")
|
|
||||||
else:
|
|
||||||
logging.warning("[Fix_Services] wifi.recon flip: FAILED: %s" % repr(result))
|
|
||||||
|
|
||||||
except Exception as err:
|
|
||||||
logging.error("[Fix_Services wifi.recon flip] %s" % repr(err))
|
|
||||||
else:
|
else:
|
||||||
print("logs look good")
|
print("logs look good")
|
||||||
|
|
||||||
@ -204,7 +197,7 @@ class FixServices(plugins.Plugin):
|
|||||||
elif level == "debug":
|
elif level == "debug":
|
||||||
logging.debug(message)
|
logging.debug(message)
|
||||||
else:
|
else:
|
||||||
logging.debug(message)
|
logging.info(message)
|
||||||
|
|
||||||
if ui:
|
if ui:
|
||||||
ui.update(force=force, new_data=displayData)
|
ui.update(force=force, new_data=displayData)
|
||||||
@ -219,16 +212,17 @@ class FixServices(plugins.Plugin):
|
|||||||
# avoid overlapping restarts, but allow it if it's been a while
|
# avoid overlapping restarts, but allow it if it's been a while
|
||||||
# (in case the last attempt failed before resetting "isReloadingMon")
|
# (in case the last attempt failed before resetting "isReloadingMon")
|
||||||
if self.isReloadingMon and (time.time() - self.LASTTRY) < 180:
|
if self.isReloadingMon and (time.time() - self.LASTTRY) < 180:
|
||||||
logging.debug("[Fix_Services] Duplicate attempt ignored")
|
logging.info("[Fix_Services] Duplicate attempt ignored")
|
||||||
else:
|
else:
|
||||||
self.isReloadingMon = True
|
self.isReloadingMon = True
|
||||||
self.LASTTRY = time.time()
|
self.LASTTRY = time.time()
|
||||||
|
|
||||||
|
self._status = "BL"
|
||||||
if hasattr(connection, 'view'):
|
if hasattr(connection, 'view'):
|
||||||
display = connection.view()
|
display = connection.view()
|
||||||
if display:
|
if display:
|
||||||
display.update(force=True, new_data={"status": "I'm blind! Try turning it off and on again",
|
display.update(force=True, new_data={"status": "I'm blind! Try turning it off and on again",
|
||||||
"face": faces.BORED})
|
"brcmfmac_status": self._status, "face": faces.BORED})
|
||||||
else:
|
else:
|
||||||
display = None
|
display = None
|
||||||
|
|
||||||
@ -244,9 +238,9 @@ class FixServices(plugins.Plugin):
|
|||||||
# is it up?
|
# is it up?
|
||||||
try:
|
try:
|
||||||
cmd_output = subprocess.check_output("ip link show wlan0mon", shell=True)
|
cmd_output = subprocess.check_output("ip link show wlan0mon", shell=True)
|
||||||
logging.debug("[Fix_Services ip link show wlan0mon]: %s" % repr(cmd_output))
|
logging.info("[Fix_Services ip link show wlan0mon]: %s" % repr(cmd_output))
|
||||||
if ",UP," in str(cmd_output):
|
if ",UP," in str(cmd_output):
|
||||||
logging.debug("wlan0mon is up. Skip reset?")
|
logging.info("wlan0mon is up. Skip reset?")
|
||||||
# not reliable, so don't skip just yet
|
# not reliable, so don't skip just yet
|
||||||
# print("wlan0mon is up. Skipping reset.")
|
# print("wlan0mon is up. Skipping reset.")
|
||||||
# self.isReloadingMon = False
|
# self.isReloadingMon = False
|
||||||
@ -267,10 +261,11 @@ class FixServices(plugins.Plugin):
|
|||||||
except Exception as err:
|
except Exception as err:
|
||||||
logging.error("[Fix_Services wifi.recon off] error %s" % (repr(err)))
|
logging.error("[Fix_Services wifi.recon off] error %s" % (repr(err)))
|
||||||
|
|
||||||
logging.debug("[Fix_Services] recon paused. Now trying wlan0mon reload")
|
logging.info("[Fix_Services] recon paused. Now trying wlan0mon reload")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
cmd_output = subprocess.check_output("monstop", shell=True)
|
cmd_output = subprocess.check_output("monstop", shell=True)
|
||||||
|
self._status = "dn"
|
||||||
self.logPrintView("info", "[Fix_Services] wlan0mon down and deleted: %s" % cmd_output,
|
self.logPrintView("info", "[Fix_Services] wlan0mon down and deleted: %s" % cmd_output,
|
||||||
display, {"status": "wlan0mon d-d-d-down!", "face": faces.BORED})
|
display, {"status": "wlan0mon d-d-d-down!", "face": faces.BORED})
|
||||||
except Exception as nope:
|
except Exception as nope:
|
||||||
@ -290,6 +285,7 @@ class FixServices(plugins.Plugin):
|
|||||||
cmd_output = subprocess.check_output("sudo modprobe -r brcmfmac", shell=True)
|
cmd_output = subprocess.check_output("sudo modprobe -r brcmfmac", shell=True)
|
||||||
self.logPrintView("info", "[Fix_Services] unloaded brcmfmac", display,
|
self.logPrintView("info", "[Fix_Services] unloaded brcmfmac", display,
|
||||||
{"status": "Turning it off #%s" % tries, "face": faces.SMART})
|
{"status": "Turning it off #%s" % tries, "face": faces.SMART})
|
||||||
|
self._status = "ul"
|
||||||
|
|
||||||
# reload the module
|
# reload the module
|
||||||
try:
|
try:
|
||||||
@ -297,24 +293,28 @@ class FixServices(plugins.Plugin):
|
|||||||
cmd_output = subprocess.check_output("sudo modprobe brcmfmac", shell=True)
|
cmd_output = subprocess.check_output("sudo modprobe brcmfmac", shell=True)
|
||||||
|
|
||||||
self.logPrintView("info", "[Fix_Services] reloaded brcmfmac")
|
self.logPrintView("info", "[Fix_Services] reloaded brcmfmac")
|
||||||
|
self._status = "rl"
|
||||||
|
|
||||||
# success! now make the mon0
|
# success! now make the mon0
|
||||||
try:
|
try:
|
||||||
cmd_output = subprocess.check_output("monstart", shell=True)
|
cmd_output = subprocess.check_output("monstart", shell=True)
|
||||||
self.logPrintView("info", "[Fix_Services interface add wlan0mon worked #%s: %s"
|
self.logPrintView("info", "[Fix_Services interface add wlan0mon worked #%s: %s"
|
||||||
% (tries, cmd_output))
|
% (tries, cmd_output))
|
||||||
|
self._status = "up"
|
||||||
try:
|
try:
|
||||||
# try accessing mon0 in bettercap
|
# try accessing mon0 in bettercap
|
||||||
result = connection.run("set wifi.interface wlan0mon")
|
result = connection.run("set wifi.interface wlan0mon")
|
||||||
if "success" in result:
|
if "success" in result:
|
||||||
logging.debug("[Fix_Services set wifi.interface wlan0mon worked!")
|
logging.info("[Fix_Services set wifi.interface wlan0mon worked!")
|
||||||
|
self._status = ""
|
||||||
|
self._count = self._count + 1
|
||||||
# stop looping and get back to recon
|
# stop looping and get back to recon
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
logging.debug(
|
logging.info(
|
||||||
"[Fix_Services set wifi.interfaceface wlan0mon] failed? %s" % repr(result))
|
"[Fix_Services set wifi.interfaceface wlan0mon] failed? %s" % repr(result))
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
logging.debug(
|
logging.info(
|
||||||
"[Fix_Services set wifi.interface wlan0mon] except: %s" % repr(err))
|
"[Fix_Services set wifi.interface wlan0mon] except: %s" % repr(err))
|
||||||
except Exception as cerr: #
|
except Exception as cerr: #
|
||||||
if not display:
|
if not display:
|
||||||
@ -333,38 +333,41 @@ class FixServices(plugins.Plugin):
|
|||||||
|
|
||||||
tries = tries + 1
|
tries = tries + 1
|
||||||
if tries < 3:
|
if tries < 3:
|
||||||
logging.debug("[Fix_Services] wlan0mon didn't make it. trying again")
|
logging.info("[Fix_Services] wlan0mon didn't make it. trying again")
|
||||||
if not display:
|
if not display:
|
||||||
print(" wlan0mon didn't make it. trying again")
|
print(" wlan0mon didn't make it. trying again")
|
||||||
else:
|
else:
|
||||||
logging.debug("[Fix_Services] wlan0mon loading failed, no choice but to reboot ..")
|
logging.info("[Fix_Services] wlan0mon loading failed, no choice but to reboot ..")
|
||||||
pwnagotchi.reboot()
|
pwnagotchi.reboot()
|
||||||
|
|
||||||
# exited the loop, so hopefully it loaded
|
# exited the loop, so hopefully it loaded
|
||||||
if tries < 3:
|
if tries < 3:
|
||||||
if display:
|
if display:
|
||||||
display.update(force=True, new_data={"status": "And back on again...",
|
display.update(force=True, new_data={"status": "And back on again...",
|
||||||
|
"brcmfmac_status": self._status,
|
||||||
"face": faces.INTENSE})
|
"face": faces.INTENSE})
|
||||||
else:
|
else:
|
||||||
print("And back on again...")
|
print("And back on again...")
|
||||||
logging.debug("[Fix_Services] wlan0mon back up")
|
logging.info("[Fix_Services] wlan0mon back up")
|
||||||
else:
|
else:
|
||||||
self.LASTTRY = time.time()
|
self.LASTTRY = time.time()
|
||||||
|
|
||||||
time.sleep(8 + tries * 2) # give it a bit before restarting recon in bettercap
|
time.sleep(8 + tries * 2) # give it a bit before restarting recon in bettercap
|
||||||
self.isReloadingMon = False
|
self.isReloadingMon = False
|
||||||
|
|
||||||
logging.debug("[Fix_Services] re-enable recon")
|
logging.info("[Fix_Services] re-enable recon")
|
||||||
try:
|
try:
|
||||||
result = connection.run("wifi.clear; wifi.recon on")
|
result = connection.run("wifi.clear; wifi.recon on")
|
||||||
|
|
||||||
if "success" in result: # and result["success"] is True:
|
if "success" in result: # and result["success"] is True:
|
||||||
|
self._status = ""
|
||||||
if display:
|
if display:
|
||||||
display.update(force=True, new_data={"status": "I can see again! (probably)",
|
display.update(force=True, new_data={"status": "I can see again! (probably)",
|
||||||
|
"brcmfmac_status": self._status,
|
||||||
"face": faces.HAPPY})
|
"face": faces.HAPPY})
|
||||||
else:
|
else:
|
||||||
print("I can see again")
|
print("I can see again")
|
||||||
logging.debug("[Fix_Services] wifi.recon on")
|
logging.info("[Fix_Services] wifi.recon on")
|
||||||
self.LASTTRY = time.time() + 120 # 2-minute pause until next time.
|
self.LASTTRY = time.time() + 120 # 2-minute pause until next time.
|
||||||
else:
|
else:
|
||||||
logging.error("[Fix_Services] wifi.recon did not start up")
|
logging.error("[Fix_Services] wifi.recon did not start up")
|
||||||
@ -385,14 +388,25 @@ class FixServices(plugins.Plugin):
|
|||||||
else:
|
else:
|
||||||
pos = (ui.width() / 2 + 35, ui.height() - 11)
|
pos = (ui.width() / 2 + 35, ui.height() - 11)
|
||||||
|
|
||||||
logging.debug("Got here")
|
logging.info("Got here")
|
||||||
|
ui.add_element('brcmfmac_status', Text(color=BLACK, value='--', position=pos, font=fonts.Small))
|
||||||
|
|
||||||
# called when the ui is updated
|
# called when the ui is updated
|
||||||
def on_ui_update(self, ui):
|
def on_ui_update(self, ui):
|
||||||
return
|
# update those elements
|
||||||
|
if self._status:
|
||||||
|
ui.set('brcmfmac_status', "wlan0mon %s" % self._status)
|
||||||
|
else:
|
||||||
|
ui.set('brcmfmac_status', "rst#%s" % self._count)
|
||||||
|
|
||||||
def on_unload(self, ui):
|
def on_unload(self, ui):
|
||||||
return
|
with ui._lock:
|
||||||
|
try:
|
||||||
|
ui.remove_element('brcmfmac_status')
|
||||||
|
logging.info("[Fix_Services] unloaded")
|
||||||
|
except Exception as err:
|
||||||
|
logging.info("[Fix_Services] unload err %s " % repr(err))
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
# run from command line to brute force a reload
|
# run from command line to brute force a reload
|
||||||
|
@ -14,9 +14,8 @@ class GPIOButtons(plugins.Plugin):
|
|||||||
self.running = False
|
self.running = False
|
||||||
self.ports = {}
|
self.ports = {}
|
||||||
self.commands = None
|
self.commands = None
|
||||||
self.options = dict()
|
|
||||||
|
|
||||||
def runcommand(self, channel):
|
def runCommand(self, channel):
|
||||||
command = self.ports[channel]
|
command = self.ports[channel]
|
||||||
logging.info(f"Button Pressed! Running command: {command}")
|
logging.info(f"Button Pressed! Running command: {command}")
|
||||||
process = subprocess.Popen(command, shell=True, stdin=None, stdout=open("/dev/null", "w"), stderr=None,
|
process = subprocess.Popen(command, shell=True, stdin=None, stdout=open("/dev/null", "w"), stderr=None,
|
||||||
@ -36,8 +35,8 @@ class GPIOButtons(plugins.Plugin):
|
|||||||
gpio = int(gpio)
|
gpio = int(gpio)
|
||||||
self.ports[gpio] = command
|
self.ports[gpio] = command
|
||||||
GPIO.setup(gpio, GPIO.IN, GPIO.PUD_UP)
|
GPIO.setup(gpio, GPIO.IN, GPIO.PUD_UP)
|
||||||
GPIO.add_event_detect(gpio, GPIO.FALLING, callback=self.runcommand, bouncetime=600)
|
GPIO.add_event_detect(gpio, GPIO.FALLING, callback=self.runCommand, bouncetime=600)
|
||||||
# set pimoroni display hat mini LED off/dim
|
#set pimoroni display hat mini LED off/dim
|
||||||
GPIO.setup(17, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
|
GPIO.setup(17, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
|
||||||
GPIO.setup(22, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
|
GPIO.setup(22, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
|
||||||
GPIO.setup(27, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
|
GPIO.setup(27, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
|
||||||
|
@ -98,7 +98,7 @@ class GPS(plugins.Plugin):
|
|||||||
lat_pos = (127, 74)
|
lat_pos = (127, 74)
|
||||||
lon_pos = (122, 84)
|
lon_pos = (122, 84)
|
||||||
alt_pos = (127, 94)
|
alt_pos = (127, 94)
|
||||||
elif ui.is_waveshare2in7():
|
elif ui.is_waveshare27inch():
|
||||||
lat_pos = (6, 120)
|
lat_pos = (6, 120)
|
||||||
lon_pos = (1, 135)
|
lon_pos = (1, 135)
|
||||||
alt_pos = (6, 150)
|
alt_pos = (6, 150)
|
||||||
|
@ -5,6 +5,7 @@ import glob
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
import pwnagotchi.grid as grid
|
import pwnagotchi.grid as grid
|
||||||
|
import pwnagotchi.plugins
|
||||||
import pwnagotchi.plugins as plugins
|
import pwnagotchi.plugins as plugins
|
||||||
from pwnagotchi.utils import StatusFile, WifiInfo, extract_from_pcap
|
from pwnagotchi.utils import StatusFile, WifiInfo, extract_from_pcap
|
||||||
from threading import Lock
|
from threading import Lock
|
||||||
@ -47,7 +48,7 @@ class Grid(plugins.Plugin):
|
|||||||
__version__ = '1.0.1'
|
__version__ = '1.0.1'
|
||||||
__license__ = 'GPL3'
|
__license__ = 'GPL3'
|
||||||
__description__ = 'This plugin signals the unit cryptographic identity and list of pwned networks and list of pwned ' \
|
__description__ = 'This plugin signals the unit cryptographic identity and list of pwned networks and list of pwned ' \
|
||||||
'networks to opwngrid.xyz '
|
'networks to api.pwnagotchi.ai '
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.options = dict()
|
self.options = dict()
|
||||||
@ -86,10 +87,10 @@ class Grid(plugins.Plugin):
|
|||||||
agent.view().on_unread_messages(self.unread_messages, self.total_messages)
|
agent.view().on_unread_messages(self.unread_messages, self.total_messages)
|
||||||
|
|
||||||
def check_handshakes(self, agent):
|
def check_handshakes(self, agent):
|
||||||
logging.debug("checking pcap's")
|
logging.debug("checking pcaps")
|
||||||
config = agent.config()
|
config = agent.config()
|
||||||
|
|
||||||
pcap_files = glob.glob(os.path.join(config['bettercap']['handshakes'], "*.pcap"))
|
pcap_files = glob.glob(os.path.join(agent.config()['bettercap']['handshakes'], "*.pcap"))
|
||||||
num_networks = len(pcap_files)
|
num_networks = len(pcap_files)
|
||||||
reported = self.report.data_field_or('reported', default=[])
|
reported = self.report.data_field_or('reported', default=[])
|
||||||
num_reported = len(reported)
|
num_reported = len(reported)
|
||||||
|
@ -253,6 +253,7 @@ class Logtail(plugins.Plugin):
|
|||||||
"""
|
"""
|
||||||
logging.info("Logtail plugin loaded.")
|
logging.info("Logtail plugin loaded.")
|
||||||
|
|
||||||
|
|
||||||
def on_webhook(self, path, request):
|
def on_webhook(self, path, request):
|
||||||
if not self.ready:
|
if not self.ready:
|
||||||
return "Plugin not ready"
|
return "Plugin not ready"
|
||||||
|
@ -130,7 +130,7 @@ class MemTemp(plugins.Plugin):
|
|||||||
except Exception:
|
except Exception:
|
||||||
# Set default position based on screen type
|
# Set default position based on screen type
|
||||||
if ui.is_waveshare_v2():
|
if ui.is_waveshare_v2():
|
||||||
h_pos = (175, 84)
|
h_pos = (178, 84)
|
||||||
v_pos = (197, 74)
|
v_pos = (197, 74)
|
||||||
elif ui.is_waveshare_v1():
|
elif ui.is_waveshare_v1():
|
||||||
h_pos = (170, 80)
|
h_pos = (170, 80)
|
||||||
|
150
pwnagotchi/plugins/default/net-pos.py
Normal file
150
pwnagotchi/plugins/default/net-pos.py
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
import logging
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import threading
|
||||||
|
import requests
|
||||||
|
import time
|
||||||
|
import pwnagotchi.plugins as plugins
|
||||||
|
from pwnagotchi.utils import StatusFile
|
||||||
|
|
||||||
|
|
||||||
|
class NetPos(plugins.Plugin):
|
||||||
|
__author__ = 'zenzen san'
|
||||||
|
__version__ = '2.0.3'
|
||||||
|
__license__ = 'GPL3'
|
||||||
|
__description__ = """Saves a json file with the access points with more signal
|
||||||
|
whenever a handshake is captured.
|
||||||
|
When internet is available the files are converted in geo locations
|
||||||
|
using Mozilla LocationService """
|
||||||
|
|
||||||
|
API_URL = 'https://location.services.mozilla.com/v1/geolocate?key={api}'
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.report = StatusFile('/root/.net_pos_saved', data_format='json')
|
||||||
|
self.skip = list()
|
||||||
|
self.ready = False
|
||||||
|
self.lock = threading.Lock()
|
||||||
|
self.options = dict()
|
||||||
|
|
||||||
|
def on_loaded(self):
|
||||||
|
if 'api_key' not in self.options or ('api_key' in self.options and not self.options['api_key']):
|
||||||
|
logging.error("NET-POS: api_key isn't set. Can't use mozilla's api.")
|
||||||
|
return
|
||||||
|
if 'api_url' in self.options:
|
||||||
|
self.API_URL = self.options['api_url']
|
||||||
|
self.ready = True
|
||||||
|
logging.info("net-pos plugin loaded.")
|
||||||
|
logging.debug(f"net-pos: use api_url: {self.API_URL}")
|
||||||
|
|
||||||
|
def _append_saved(self, path):
|
||||||
|
to_save = list()
|
||||||
|
if isinstance(path, str):
|
||||||
|
to_save.append(path)
|
||||||
|
elif isinstance(path, list):
|
||||||
|
to_save += path
|
||||||
|
else:
|
||||||
|
raise TypeError("Expected list or str, got %s" % type(path))
|
||||||
|
|
||||||
|
with open('/root/.net_pos_saved', 'a') as saved_file:
|
||||||
|
for x in to_save:
|
||||||
|
saved_file.write(x + "\n")
|
||||||
|
|
||||||
|
def on_internet_available(self, agent):
|
||||||
|
if self.lock.locked():
|
||||||
|
return
|
||||||
|
with self.lock:
|
||||||
|
if self.ready:
|
||||||
|
config = agent.config()
|
||||||
|
display = agent.view()
|
||||||
|
reported = self.report.data_field_or('reported', default=list())
|
||||||
|
handshake_dir = config['bettercap']['handshakes']
|
||||||
|
|
||||||
|
all_files = os.listdir(handshake_dir)
|
||||||
|
all_np_files = [os.path.join(handshake_dir, filename)
|
||||||
|
for filename in all_files
|
||||||
|
if filename.endswith('.net-pos.json')]
|
||||||
|
new_np_files = set(all_np_files) - set(reported) - set(self.skip)
|
||||||
|
|
||||||
|
if new_np_files:
|
||||||
|
logging.debug("NET-POS: Found %d new net-pos files. Fetching positions ...", len(new_np_files))
|
||||||
|
display.set('status', f"Found {len(new_np_files)} new net-pos files. Fetching positions ...")
|
||||||
|
display.update(force=True)
|
||||||
|
for idx, np_file in enumerate(new_np_files):
|
||||||
|
|
||||||
|
geo_file = np_file.replace('.net-pos.json', '.geo.json')
|
||||||
|
if os.path.exists(geo_file):
|
||||||
|
# got already the position
|
||||||
|
reported.append(np_file)
|
||||||
|
self.report.update(data={'reported': reported})
|
||||||
|
continue
|
||||||
|
|
||||||
|
try:
|
||||||
|
geo_data = self._get_geo_data(np_file) # returns json obj
|
||||||
|
except requests.exceptions.RequestException as req_e:
|
||||||
|
logging.error("NET-POS: %s - RequestException: %s", np_file, req_e)
|
||||||
|
self.skip += np_file
|
||||||
|
continue
|
||||||
|
except json.JSONDecodeError as js_e:
|
||||||
|
logging.error("NET-POS: %s - JSONDecodeError: %s, removing it...", np_file, js_e)
|
||||||
|
os.remove(np_file)
|
||||||
|
continue
|
||||||
|
except OSError as os_e:
|
||||||
|
logging.error("NET-POS: %s - OSError: %s", np_file, os_e)
|
||||||
|
self.skip += np_file
|
||||||
|
continue
|
||||||
|
|
||||||
|
with open(geo_file, 'w+t') as sf:
|
||||||
|
json.dump(geo_data, sf)
|
||||||
|
|
||||||
|
reported.append(np_file)
|
||||||
|
self.report.update(data={'reported': reported})
|
||||||
|
|
||||||
|
display.set('status', f"Fetching positions ({idx + 1}/{len(new_np_files)})")
|
||||||
|
display.update(force=True)
|
||||||
|
|
||||||
|
def on_handshake(self, agent, filename, access_point, client_station):
|
||||||
|
netpos = self._get_netpos(agent)
|
||||||
|
if not netpos['wifiAccessPoints']:
|
||||||
|
return
|
||||||
|
|
||||||
|
netpos["ts"] = int("%.0f" % time.time())
|
||||||
|
netpos_filename = filename.replace('.pcap', '.net-pos.json')
|
||||||
|
logging.debug("NET-POS: Saving net-location to %s", netpos_filename)
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open(netpos_filename, 'w+t') as net_pos_file:
|
||||||
|
json.dump(netpos, net_pos_file)
|
||||||
|
except OSError as os_e:
|
||||||
|
logging.error("NET-POS: %s", os_e)
|
||||||
|
|
||||||
|
def _get_netpos(self, agent):
|
||||||
|
aps = agent.get_access_points()
|
||||||
|
netpos = dict()
|
||||||
|
netpos['wifiAccessPoints'] = list()
|
||||||
|
# 6 seems a good number to save a wifi networks location
|
||||||
|
for access_point in sorted(aps, key=lambda i: i['rssi'], reverse=True)[:6]:
|
||||||
|
netpos['wifiAccessPoints'].append({'macAddress': access_point['mac'],
|
||||||
|
'signalStrength': access_point['rssi']})
|
||||||
|
return netpos
|
||||||
|
|
||||||
|
def _get_geo_data(self, path, timeout=30):
|
||||||
|
geourl = self.API_URL.format(api=self.options['api_key'])
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open(path, "r") as json_file:
|
||||||
|
data = json.load(json_file)
|
||||||
|
except json.JSONDecodeError as js_e:
|
||||||
|
raise js_e
|
||||||
|
except OSError as os_e:
|
||||||
|
raise os_e
|
||||||
|
|
||||||
|
try:
|
||||||
|
result = requests.post(geourl,
|
||||||
|
json=data,
|
||||||
|
timeout=timeout)
|
||||||
|
return_geo = result.json()
|
||||||
|
if data["ts"]:
|
||||||
|
return_geo["ts"] = data["ts"]
|
||||||
|
return return_geo
|
||||||
|
except requests.exceptions.RequestException as req_e:
|
||||||
|
raise req_e
|
@ -142,6 +142,6 @@ class OnlineHashCrack(plugins.Plugin):
|
|||||||
for row in csv.DictReader(cracked_list):
|
for row in csv.DictReader(cracked_list):
|
||||||
if row['password']:
|
if row['password']:
|
||||||
filename = re.sub(r'[^a-zA-Z0-9]', '', row['ESSID']) + '_' + row['BSSID'].replace(':','')
|
filename = re.sub(r'[^a-zA-Z0-9]', '', row['ESSID']) + '_' + row['BSSID'].replace(':','')
|
||||||
if os.path.exists( os.path.join(handshake_dir, filename+'.pcap')):
|
if os.path.exists( os.path.join(handshake_dir, filename+'.pcap') ):
|
||||||
with open(os.path.join(handshake_dir, filename+'.pcap.cracked'), 'w') as f:
|
with open(os.path.join(handshake_dir, filename+'.pcap.cracked'), 'w') as f:
|
||||||
f.write(row['password'])
|
f.write(row['password'])
|
||||||
|
42
pwnagotchi/plugins/default/paw-gps.py
Normal file
42
pwnagotchi/plugins/default/paw-gps.py
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import logging
|
||||||
|
import requests
|
||||||
|
import pwnagotchi.plugins as plugins
|
||||||
|
|
||||||
|
'''
|
||||||
|
You need an bluetooth connection to your android phone which is running PAW server with the GPS "hack" from Systemik and edited by shaynemk
|
||||||
|
GUIDE HERE: https://community.pwnagotchi.ai/t/setting-up-paw-gps-on-android
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
class PawGPS(plugins.Plugin):
|
||||||
|
__author__ = 'leont'
|
||||||
|
__version__ = '1.0.1'
|
||||||
|
__name__ = 'pawgps'
|
||||||
|
__license__ = 'GPL3'
|
||||||
|
__description__ = 'Saves GPS coordinates whenever an handshake is captured. The GPS data is get from PAW on android.'
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.options = dict()
|
||||||
|
|
||||||
|
def on_loaded(self):
|
||||||
|
logging.info("[paw-gps] plugin loaded")
|
||||||
|
if 'ip' not in self.options or ('ip' in self.options and self.options['ip'] is None) or (len('ip' in self.options and self.options['ip']) is 0):
|
||||||
|
logging.info("[paw-gps] no IP Address defined in the config file, will uses paw server default (192.168.44.1:8080)")
|
||||||
|
|
||||||
|
def on_handshake(self, agent, filename, access_point, client_station):
|
||||||
|
if 'ip' not in self.options or ('ip' in self.options and self.options['ip'] is None or (len('ip' in self.options and self.options['ip']) is 0)):
|
||||||
|
ip = "192.168.44.1:8080"
|
||||||
|
else:
|
||||||
|
ip = self.options['ip']
|
||||||
|
|
||||||
|
try:
|
||||||
|
gps = requests.get('http://' + ip + '/gps.xhtml')
|
||||||
|
try:
|
||||||
|
gps_filename = filename.replace('.pcap', '.paw-gps.json')
|
||||||
|
logging.info("[paw-gps] saving GPS data to %s" % (gps_filename))
|
||||||
|
with open(gps_filename, 'w+t') as f:
|
||||||
|
f.write(gps.text)
|
||||||
|
except Exception as error:
|
||||||
|
logging.error(f"[paw-gps] encountered error while saving gps data: {error}")
|
||||||
|
except Exception as error:
|
||||||
|
logging.error(f"[paw-gps] encountered error while getting gps data: {error}")
|
@ -1,105 +0,0 @@
|
|||||||
# Based on UPS Lite v1.1 from https://github.com/xenDE
|
|
||||||
|
|
||||||
import logging
|
|
||||||
import time
|
|
||||||
|
|
||||||
from pwnagotchi.ui.components import LabeledValue
|
|
||||||
from pwnagotchi.ui.view import BLACK
|
|
||||||
import pwnagotchi.ui.fonts as fonts
|
|
||||||
import pwnagotchi.plugins as plugins
|
|
||||||
import pwnagotchi
|
|
||||||
|
|
||||||
|
|
||||||
class UPS:
|
|
||||||
def __init__(self):
|
|
||||||
# only import when the module is loaded and enabled
|
|
||||||
import smbus
|
|
||||||
# 0 = /dev/i2c-0 (port I2C0), 1 = /dev/i2c-1 (port I2C1)
|
|
||||||
self._bus = smbus.SMBus(1)
|
|
||||||
|
|
||||||
def voltage(self):
|
|
||||||
try:
|
|
||||||
low = self._bus.read_byte_data(0x57, 0x23)
|
|
||||||
high = self._bus.read_byte_data(0x57, 0x22)
|
|
||||||
v = (((high << 8) + low) / 1000)
|
|
||||||
return v
|
|
||||||
except:
|
|
||||||
return 0.0
|
|
||||||
|
|
||||||
def capacity(self):
|
|
||||||
battery_level = 0
|
|
||||||
# battery_v = self.voltage()
|
|
||||||
try:
|
|
||||||
battery_level = self._bus.read_byte_data(0x57, 0x2a)
|
|
||||||
return battery_level
|
|
||||||
except:
|
|
||||||
return battery_level
|
|
||||||
|
|
||||||
def status(self):
|
|
||||||
stat02 = self._bus.read_byte_data(0x57, 0x02)
|
|
||||||
stat03 = self._bus.read_byte_data(0x57, 0x03)
|
|
||||||
stat04 = self._bus.read_byte_data(0x57, 0x04)
|
|
||||||
return stat02, stat03, stat04
|
|
||||||
|
|
||||||
|
|
||||||
class PiSugar3(plugins.Plugin):
|
|
||||||
__author__ = 'taiyonemo@protonmail.com'
|
|
||||||
__editor__ = 'jayofelony'
|
|
||||||
__version__ = '1.0.1'
|
|
||||||
__license__ = 'GPL3'
|
|
||||||
__description__ = 'A plugin that will add a percentage indicator for the PiSugar 3'
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.ups = None
|
|
||||||
self.lasttemp = 69
|
|
||||||
self.drot = 0 # display rotation
|
|
||||||
self.nextDChg = 0 # last time display changed, rotate on updates after 5 seconds
|
|
||||||
self.options = dict()
|
|
||||||
|
|
||||||
def on_loaded(self):
|
|
||||||
self.ups = UPS()
|
|
||||||
logging.info("[PiSugar3] plugin loaded.")
|
|
||||||
|
|
||||||
def on_ui_setup(self, ui):
|
|
||||||
try:
|
|
||||||
ui.add_element('bat', LabeledValue(color=BLACK, label='BAT', value='0%', position=(ui.width() / 2 + 10, 0),
|
|
||||||
label_font=fonts.Bold, text_font=fonts.Medium))
|
|
||||||
except Exception as err:
|
|
||||||
logging.warning("[PiSugar3] setup err: %s" % repr(err))
|
|
||||||
|
|
||||||
def on_unload(self, ui):
|
|
||||||
try:
|
|
||||||
with ui._lock:
|
|
||||||
ui.remove_element('bat')
|
|
||||||
except Exception as err:
|
|
||||||
logging.warning("[PiSugar3] unload err: %s" % repr(err))
|
|
||||||
|
|
||||||
def on_ui_update(self, ui):
|
|
||||||
capacity = self.ups.capacity()
|
|
||||||
voltage = self.ups.voltage()
|
|
||||||
stats = self.ups.status()
|
|
||||||
temp = stats[2] - 40
|
|
||||||
if temp != self.lasttemp:
|
|
||||||
logging.debug("[PiSugar3] (chg %X, info %X, temp %d)" % (stats[0], stats[1], temp))
|
|
||||||
self.lasttemp = temp
|
|
||||||
|
|
||||||
if stats[0] & 0x80: # charging, or has power connected
|
|
||||||
ui._state._state['bat'].label = "CHG"
|
|
||||||
else:
|
|
||||||
ui._state._state['bat'].label = "BAT"
|
|
||||||
|
|
||||||
if time.time() > self.nextDChg:
|
|
||||||
self.drot = (self.drot + 1) % 3
|
|
||||||
self.nextDChg = time.time() + 5
|
|
||||||
|
|
||||||
if self.drot == 0: # show battery voltage
|
|
||||||
ui.set('bat', "%2.2fv" % voltage)
|
|
||||||
elif self.drot == 1:
|
|
||||||
ui.set('bat', "%2i%%" % capacity)
|
|
||||||
else:
|
|
||||||
ui.set('bat', "%2i\xb0" % temp)
|
|
||||||
|
|
||||||
if capacity <= self.options['shutdown']:
|
|
||||||
logging.info('[PiSugar3] Empty battery (<= %s%%): shutting down' % self.options['shutdown'])
|
|
||||||
ui.update(force=True, new_data={'status': 'Battery exhausted, bye ...'})
|
|
||||||
pwnagotchi.shutdown()
|
|
@ -18,14 +18,12 @@ def systemd_dropin(name, content):
|
|||||||
|
|
||||||
systemctl("daemon-reload")
|
systemctl("daemon-reload")
|
||||||
|
|
||||||
|
|
||||||
def systemctl(command, unit=None):
|
def systemctl(command, unit=None):
|
||||||
if unit:
|
if unit:
|
||||||
os.system("/bin/systemctl %s %s" % (command, unit))
|
os.system("/bin/systemctl %s %s" % (command, unit))
|
||||||
else:
|
else:
|
||||||
os.system("/bin/systemctl %s" % command)
|
os.system("/bin/systemctl %s" % command)
|
||||||
|
|
||||||
|
|
||||||
def run_task(name, options):
|
def run_task(name, options):
|
||||||
task_service_name = "switcher-%s-task.service" % name
|
task_service_name = "switcher-%s-task.service" % name
|
||||||
# save all the commands to a shell script
|
# save all the commands to a shell script
|
||||||
@ -59,7 +57,7 @@ def run_task(name, options):
|
|||||||
""" % (name, task_service_name, name))
|
""" % (name, task_service_name, name))
|
||||||
|
|
||||||
if 'reboot' in options and options['reboot']:
|
if 'reboot' in options and options['reboot']:
|
||||||
# create an indication file!
|
# create a indication file!
|
||||||
# if this file is set, we want the switcher-tasks to run
|
# if this file is set, we want the switcher-tasks to run
|
||||||
open('/root/.switcher', 'a').close()
|
open('/root/.switcher', 'a').close()
|
||||||
|
|
||||||
@ -100,7 +98,6 @@ def run_task(name, options):
|
|||||||
systemctl("daemon-reload")
|
systemctl("daemon-reload")
|
||||||
systemctl("start", task_service_name)
|
systemctl("start", task_service_name)
|
||||||
|
|
||||||
|
|
||||||
class Switcher(plugins.Plugin):
|
class Switcher(plugins.Plugin):
|
||||||
__author__ = '33197631+dadav@users.noreply.github.com'
|
__author__ = '33197631+dadav@users.noreply.github.com'
|
||||||
__version__ = '0.0.1'
|
__version__ = '0.0.1'
|
||||||
|
@ -14,7 +14,7 @@ from dateutil.parser import parse
|
|||||||
|
|
||||||
the plugin does the following:
|
the plugin does the following:
|
||||||
- search for *.pcap files in your /handshakes/ dir
|
- search for *.pcap files in your /handshakes/ dir
|
||||||
- for every found .pcap file it looks for a .geo.json or .gps.json or file with
|
- for every found .pcap file it looks for a .geo.json or .gps.json or .paw-gps.json file with
|
||||||
latitude+longitude data inside and shows this position on the map
|
latitude+longitude data inside and shows this position on the map
|
||||||
- if also an .cracked file with a plaintext password inside exist, it reads the content and shows the
|
- if also an .cracked file with a plaintext password inside exist, it reads the content and shows the
|
||||||
position as green instead of red and the password inside the infopox of the position
|
position as green instead of red and the password inside the infopox of the position
|
||||||
@ -87,8 +87,7 @@ class Webgpsmap(plugins.Plugin):
|
|||||||
# returns all positions
|
# returns all positions
|
||||||
try:
|
try:
|
||||||
self.ALREADY_SENT = list()
|
self.ALREADY_SENT = list()
|
||||||
response_data = bytes(
|
response_data = bytes(json.dumps(self.load_gps_from_dir(self.config['bettercap']['handshakes'])), "utf-8")
|
||||||
json.dumps(self.load_gps_from_dir(self.config['bettercap']['handshakes'])), "utf-8")
|
|
||||||
response_status = 200
|
response_status = 200
|
||||||
response_mimetype = "application/json"
|
response_mimetype = "application/json"
|
||||||
response_header_contenttype = 'application/json'
|
response_header_contenttype = 'application/json'
|
||||||
@ -101,8 +100,7 @@ class Webgpsmap(plugins.Plugin):
|
|||||||
self.ALREADY_SENT = list()
|
self.ALREADY_SENT = list()
|
||||||
json_data = json.dumps(self.load_gps_from_dir(self.config['bettercap']['handshakes']))
|
json_data = json.dumps(self.load_gps_from_dir(self.config['bettercap']['handshakes']))
|
||||||
html_data = self.get_html()
|
html_data = self.get_html()
|
||||||
html_data = html_data.replace('var positions = [];',
|
html_data = html_data.replace('var positions = [];', 'var positions = ' + json_data + ';positionsLoaded=true;drawPositions();')
|
||||||
'var positions = ' + json_data + ';positionsLoaded=true;drawPositions();')
|
|
||||||
response_data = bytes(html_data, "utf-8")
|
response_data = bytes(html_data, "utf-8")
|
||||||
response_status = 200
|
response_status = 200
|
||||||
response_mimetype = "application/xhtml+xml"
|
response_mimetype = "application/xhtml+xml"
|
||||||
@ -165,8 +163,7 @@ class Webgpsmap(plugins.Plugin):
|
|||||||
|
|
||||||
all_files = os.listdir(handshake_dir)
|
all_files = os.listdir(handshake_dir)
|
||||||
# print(all_files)
|
# print(all_files)
|
||||||
all_pcap_files = [os.path.join(handshake_dir, filename) for filename in all_files if
|
all_pcap_files = [os.path.join(handshake_dir, filename) for filename in all_files if filename.endswith('.pcap')]
|
||||||
filename.endswith('.pcap')]
|
|
||||||
all_geo_or_gps_files = []
|
all_geo_or_gps_files = []
|
||||||
for filename_pcap in all_pcap_files:
|
for filename_pcap in all_pcap_files:
|
||||||
filename_base = filename_pcap[:-5] # remove ".pcap"
|
filename_base = filename_pcap[:-5] # remove ".pcap"
|
||||||
@ -183,18 +180,22 @@ class Webgpsmap(plugins.Plugin):
|
|||||||
if check_for in all_files:
|
if check_for in all_files:
|
||||||
filename_position = str(os.path.join(handshake_dir, check_for))
|
filename_position = str(os.path.join(handshake_dir, check_for))
|
||||||
|
|
||||||
|
logging.debug("[webgpsmap] search for .paw-gps.json")
|
||||||
|
check_for = os.path.basename(filename_base) + ".paw-gps.json"
|
||||||
|
if check_for in all_files:
|
||||||
|
filename_position = str(os.path.join(handshake_dir, check_for))
|
||||||
|
|
||||||
logging.debug(f"[webgpsmap] end search for position data files and use {filename_position}")
|
logging.debug(f"[webgpsmap] end search for position data files and use {filename_position}")
|
||||||
|
|
||||||
if filename_position is not None:
|
if filename_position is not None:
|
||||||
all_geo_or_gps_files.append(filename_position)
|
all_geo_or_gps_files.append(filename_position)
|
||||||
|
|
||||||
# all_geo_or_gps_files = set(all_geo_or_gps_files) - set(SKIP) # remove skipped networks? No!
|
# all_geo_or_gps_files = set(all_geo_or_gps_files) - set(SKIP) # remove skipped networks? No!
|
||||||
|
|
||||||
if newest_only:
|
if newest_only:
|
||||||
all_geo_or_gps_files = set(all_geo_or_gps_files) - set(self.ALREADY_SENT)
|
all_geo_or_gps_files = set(all_geo_or_gps_files) - set(self.ALREADY_SENT)
|
||||||
|
|
||||||
logging.info(
|
logging.info(f"[webgpsmap] Found {len(all_geo_or_gps_files)} position-data files from {len(all_pcap_files)} handshakes. Fetching positions ...")
|
||||||
f"[webgpsmap] Found {len(all_geo_or_gps_files)} position-data files from {len(all_pcap_files)} handshakes. Fetching positions ...")
|
|
||||||
|
|
||||||
for pos_file in all_geo_or_gps_files:
|
for pos_file in all_geo_or_gps_files:
|
||||||
try:
|
try:
|
||||||
@ -212,7 +213,9 @@ class Webgpsmap(plugins.Plugin):
|
|||||||
pos_type = 'gps'
|
pos_type = 'gps'
|
||||||
elif pos.type() == PositionFile.GEO:
|
elif pos.type() == PositionFile.GEO:
|
||||||
pos_type = 'geo'
|
pos_type = 'geo'
|
||||||
gps_data[ssid + "_" + mac] = {
|
elif pos.type() == PositionFile.PAWGPS:
|
||||||
|
pos_type = 'paw'
|
||||||
|
gps_data[ssid+"_"+mac] = {
|
||||||
'ssid': ssid,
|
'ssid': ssid,
|
||||||
'mac': mac,
|
'mac': mac,
|
||||||
'type': pos_type,
|
'type': pos_type,
|
||||||
@ -221,7 +224,7 @@ class Webgpsmap(plugins.Plugin):
|
|||||||
'acc': pos.accuracy(),
|
'acc': pos.accuracy(),
|
||||||
'ts_first': pos.timestamp_first(),
|
'ts_first': pos.timestamp_first(),
|
||||||
'ts_last': pos.timestamp_last(),
|
'ts_last': pos.timestamp_last(),
|
||||||
}
|
}
|
||||||
|
|
||||||
# get ap password if exist
|
# get ap password if exist
|
||||||
check_for = os.path.basename(pos_file).split(".")[0] + ".pcap.cracked"
|
check_for = os.path.basename(pos_file).split(".")[0] + ".pcap.cracked"
|
||||||
@ -262,6 +265,7 @@ class PositionFile:
|
|||||||
"""
|
"""
|
||||||
GPS = 1
|
GPS = 1
|
||||||
GEO = 2
|
GEO = 2
|
||||||
|
PAWGPS = 3
|
||||||
|
|
||||||
def __init__(self, path):
|
def __init__(self, path):
|
||||||
self._file = path
|
self._file = path
|
||||||
@ -278,7 +282,7 @@ class PositionFile:
|
|||||||
"""
|
"""
|
||||||
Returns the mac from filename
|
Returns the mac from filename
|
||||||
"""
|
"""
|
||||||
parsed_mac = re.search(r'.*_?([a-zA-Z0-9]{12})\.(?:gps|geo)\.json', self._filename)
|
parsed_mac = re.search(r'.*_?([a-zA-Z0-9]{12})\.(?:gps|geo|paw-gps)\.json', self._filename)
|
||||||
if parsed_mac:
|
if parsed_mac:
|
||||||
mac = parsed_mac.groups()[0]
|
mac = parsed_mac.groups()[0]
|
||||||
return mac
|
return mac
|
||||||
@ -288,7 +292,7 @@ class PositionFile:
|
|||||||
"""
|
"""
|
||||||
Returns the ssid from filename
|
Returns the ssid from filename
|
||||||
"""
|
"""
|
||||||
parsed_ssid = re.search(r'(.+)_[a-zA-Z0-9]{12}\.(?:gps|geo)\.json', self._filename)
|
parsed_ssid = re.search(r'(.+)_[a-zA-Z0-9]{12}\.(?:gps|geo|paw-gps)\.json', self._filename)
|
||||||
if parsed_ssid:
|
if parsed_ssid:
|
||||||
return parsed_ssid.groups()[0]
|
return parsed_ssid.groups()[0]
|
||||||
return None
|
return None
|
||||||
@ -350,6 +354,8 @@ class PositionFile:
|
|||||||
return PositionFile.GPS
|
return PositionFile.GPS
|
||||||
if self._file.endswith('.geo.json'):
|
if self._file.endswith('.geo.json'):
|
||||||
return PositionFile.GEO
|
return PositionFile.GEO
|
||||||
|
if self._file.endswith('.paw-gps.json'):
|
||||||
|
return PositionFile.PAWGPS
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def lat(self):
|
def lat(self):
|
||||||
@ -396,7 +402,9 @@ class PositionFile:
|
|||||||
|
|
||||||
def accuracy(self):
|
def accuracy(self):
|
||||||
if self.type() == PositionFile.GPS:
|
if self.type() == PositionFile.GPS:
|
||||||
return 50.0 # a default
|
return 50.0 # a default
|
||||||
|
if self.type() == PositionFile.PAWGPS:
|
||||||
|
return 50.0 # a default
|
||||||
if self.type() == PositionFile.GEO:
|
if self.type() == PositionFile.GEO:
|
||||||
try:
|
try:
|
||||||
return self._json['accuracy']
|
return self._json['accuracy']
|
||||||
|
@ -1,33 +0,0 @@
|
|||||||
LOOK_R = '( ⚆_⚆)'
|
|
||||||
LOOK_L = '(☉_☉ )'
|
|
||||||
LOOK_R_HAPPY = '( ◕‿◕)'
|
|
||||||
LOOK_L_HAPPY = '(◕‿◕ )'
|
|
||||||
SLEEP = '(⇀‿‿↼)'
|
|
||||||
SLEEP2 = '(≖‿‿≖)'
|
|
||||||
AWAKE = '(◕‿‿◕)'
|
|
||||||
BORED = '(-__-)'
|
|
||||||
INTENSE = '(°▃▃°)'
|
|
||||||
COOL = '(⌐■_■)'
|
|
||||||
HAPPY = '(•‿‿•)'
|
|
||||||
GRATEFUL = '(^‿‿^)'
|
|
||||||
EXCITED = '(ᵔ◡◡ᵔ)'
|
|
||||||
MOTIVATED = '(☼‿‿☼)'
|
|
||||||
DEMOTIVATED = '(≖__≖)'
|
|
||||||
SMART = '(✜‿‿✜)'
|
|
||||||
LONELY = '(ب__ب)'
|
|
||||||
SAD = '(╥☁╥ )'
|
|
||||||
ANGRY = "(-_-')"
|
|
||||||
FRIEND = '(♥‿‿♥)'
|
|
||||||
BROKEN = '(☓‿‿☓)'
|
|
||||||
DEBUG = '(#__#)'
|
|
||||||
UPLOAD = '(1__0)'
|
|
||||||
UPLOAD1 = '(1__1)'
|
|
||||||
UPLOAD2 = '(0__1)'
|
|
||||||
PNG = False
|
|
||||||
POSITION_X = 0
|
|
||||||
POSITION_Y = 40
|
|
||||||
|
|
||||||
|
|
||||||
def load_from_config(config):
|
|
||||||
for face_name, face_value in config.items():
|
|
||||||
globals()[face_name.upper()] = face_value
|
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user