mirror of
https://github.com/jayofelony/pwnagotchi.git
synced 2025-07-01 18:37:27 -04:00
Compare commits
201 Commits
v2.7.6
...
wpa-2-patc
Author | SHA1 | Date | |
---|---|---|---|
744a50aa67 | |||
eb6e229d36 | |||
3ada0628e1 | |||
9fa772c36a | |||
a92e66137c | |||
a7e98cf166 | |||
9a1a264a9f | |||
8a0d482fe0 | |||
0cc320d31f | |||
d841d2b649 | |||
966126a986 | |||
d67935fa6a | |||
46d867ce7f | |||
2e16069850 | |||
6e3cbbdd39 | |||
a19bd6a181 | |||
6c03d95724 | |||
60a9da9acc | |||
25aba0d73c | |||
ecde496534 | |||
7ebd4dd7a0 | |||
35f1707436 | |||
60ced12ea5 | |||
6082a0c169 | |||
a72c1b1628 | |||
e37a0bdc62 | |||
262f8bf551 | |||
5311dd9ddc | |||
bd9c44b4a3 | |||
3b01aec872 | |||
3e571bff2d | |||
391941e64a | |||
3fff6182ed | |||
73e0a1fce7 | |||
9ba23d77d1 | |||
8be75627e9 | |||
c2a36aa678 | |||
fdf0a087f6 | |||
c6efa5df08 | |||
62327a711e | |||
bb460a9cc6 | |||
93b2322ab5 | |||
53a8af4711 | |||
ed5decffa0 | |||
147cbfaa07 | |||
ced9b4d5ee | |||
472c3165ed | |||
1578d13d98 | |||
0965e7eb3e | |||
b789c14f14 | |||
0855b44d59 | |||
55c6007d32 | |||
a7cf8a3383 | |||
faa48b2752 | |||
d20619340c | |||
43a07fe969 | |||
bcce22c164 | |||
0264b7a7db | |||
fba5dd0341 | |||
dd7760efdf | |||
f7cd0fb1fc | |||
611a3e7fb5 | |||
599c6211e4 | |||
eb48d29851 | |||
0999b95be0 | |||
ae351e5e9c | |||
fa58136c0e | |||
3d5185f2c1 | |||
b6bb7b9080 | |||
20715351af | |||
1d3c781d12 | |||
bd51b4330f | |||
f216a21132 | |||
5ae357c3dc | |||
ed9afe6856 | |||
25d932fa9b | |||
ebd25d50b0 | |||
27e784a5d7 | |||
c02efb5224 | |||
107d366c82 | |||
488968ba7f | |||
0dd8f72c95 | |||
0e274af5a0 | |||
8627c8800d | |||
53ed6f61c6 | |||
835523241b | |||
418dbf21e3 | |||
9a40567daa | |||
eab331686d | |||
e1d8001fb4 | |||
3cc172a526 | |||
41afe302d9 | |||
5bcca7e89b | |||
4a12436942 | |||
d8b6363d37 | |||
b3d8a44208 | |||
3fb91498cd | |||
7587f38c28 | |||
d7fefa78f0 | |||
1ef3a88679 | |||
291ef39563 | |||
16d80a09e6 | |||
4ffad03c91 | |||
0638832532 | |||
f895b06978 | |||
f410feb2bb | |||
1ffe6c4e3d | |||
dfbfd7f891 | |||
a50695aef2 | |||
829a6962d6 | |||
ce1315b85f | |||
6a2703cc27 | |||
502e856934 | |||
dffcbbf447 | |||
333804c3bb | |||
50b1ceed81 | |||
877b9fbba3 | |||
8724ca546d | |||
961d3a536a | |||
c882d0b67a | |||
12fd081ae0 | |||
e1be0f7674 | |||
396b30f780 | |||
e7d8d632a0 | |||
5a6ec7b4b8 | |||
92cd5d3fdb | |||
99caa7a973 | |||
a94e7eef02 | |||
1cefae55d1 | |||
c5ee1df855 | |||
d142840307 | |||
e558146e28 | |||
11a3330153 | |||
53b2dd8628 | |||
6381f9443b | |||
fa7e87b974 | |||
cca2ff2da4 | |||
78415b3137 | |||
34284aa1bc | |||
56ebb60662 | |||
84f6624844 | |||
3bcbb0ce9a | |||
e01e457992 | |||
1780859889 | |||
fdd98bb37a | |||
86991304a5 | |||
4f62759d6d | |||
7b7ba02aad | |||
7040be2d30 | |||
d0617ccfaf | |||
a0b5078b64 | |||
54c1ffd63c | |||
7530709d0c | |||
a9a6fd424b | |||
8c97301992 | |||
91eaa22188 | |||
31a4af4c21 | |||
dceeaff1fb | |||
c0241dc8df | |||
8f405f4ab2 | |||
bd79e71563 | |||
7f662585aa | |||
d23ff8d47a | |||
04435229fe | |||
42e236bafe | |||
5081b72695 | |||
652740f050 | |||
1b6b12bdf5 | |||
c610335a34 | |||
6c39ed97dd | |||
967f1663c6 | |||
ffe1e90ccd | |||
f2d2bcbfdf | |||
bfc0795fb8 | |||
1a55afd74a | |||
1594e7c129 | |||
a244e70a1c | |||
d35d5d6c3c | |||
7aacf9fb44 | |||
dde6fa4c2a | |||
7dfb2f2ff6 | |||
cf46ab3da9 | |||
6824e541a4 | |||
1c4e8fa750 | |||
a50ec4a65c | |||
5414fdf21d | |||
c9883bb4f9 | |||
045f5853bc | |||
6dfcaf05d5 | |||
e2f8119d2c | |||
d675b3d0dd | |||
30850b6530 | |||
1e9c9bae42 | |||
64241515ad | |||
c9f232dae2 | |||
4f97bcaa83 | |||
7d2a03c79f | |||
f89ba85f73 | |||
57f03f4359 | |||
9bc266f9ff | |||
b0db0285bc |
1
.github/FUNDING.yml
vendored
1
.github/FUNDING.yml
vendored
@ -1,4 +1,3 @@
|
|||||||
# These are supported funding model platforms
|
# These are supported funding model platforms
|
||||||
|
|
||||||
patreon: pwnagotchi_torch
|
|
||||||
github: jayofelony
|
github: jayofelony
|
||||||
|
72
.github/workflows/publish.yml
vendored
Normal file
72
.github/workflows/publish.yml
vendored
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
name: Publish
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
version:
|
||||||
|
description: 'Version number'
|
||||||
|
required: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Extract version from file
|
||||||
|
id: get_version
|
||||||
|
run: |
|
||||||
|
VERSION=$(cut -d "'" -f2 < pwnagotchi/_version.py)
|
||||||
|
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
|
||||||
|
run: sudo apt update && sudo apt install qemu-user-static qemu-utils xz-utils -y
|
||||||
|
|
||||||
|
- name: Build img file
|
||||||
|
run: ls -la .; pwd; make all
|
||||||
|
|
||||||
|
- name: Transfer 32bit.img to docker and give permissions
|
||||||
|
run: sudo chown runner:docker "pwnagotchi-32bit.img"
|
||||||
|
|
||||||
|
- name: Transfer 64bit.img to docker and give permissions
|
||||||
|
run: sudo chown runner:docker "pwnagotchi-64bit.img"
|
||||||
|
|
||||||
|
- name: PiShrink
|
||||||
|
run: |
|
||||||
|
wget https://raw.githubusercontent.com/Drewsif/PiShrink/master/pishrink.sh
|
||||||
|
chmod +x pishrink.sh
|
||||||
|
sudo mv pishrink.sh /usr/local/bin
|
||||||
|
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
|
||||||
|
uses: softprops/action-gh-release@v1
|
||||||
|
with:
|
||||||
|
prerelease: true
|
||||||
|
tag_name: v${{ env.VERSION }}
|
||||||
|
name: Pwnagotchi v${{ env.VERSION }}
|
||||||
|
files: |
|
||||||
|
pwnagotchi-${{ env.VERSION }}-32bit.img.xz
|
||||||
|
pwnagotchi-${{ env.VERSION }}-64bit.img.xz
|
||||||
|
body: ${{ env.RELEASE_BODY }}
|
21
.idea/deployment.xml
generated
21
.idea/deployment.xml
generated
@ -1,4 +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" />
|
<component name="PublishConfigData" serverName="pwnagotchi" filePermissions="493" folderPermissions="493" remoteFilesAllowedToDisappearOnAutoupload="false" confirmBeforeUploading="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>
|
||||||
</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.10 (pwnagotchi-torch-bookworm)" 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.10 (pwnagotchi-torch-bookworm)" 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">
|
||||||
|
40
Makefile
40
Makefile
@ -1,4 +1,4 @@
|
|||||||
PACKER_VERSION := 1.10.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)
|
||||||
|
|
||||||
@ -25,7 +25,8 @@ ifneq (,$(UNSHARE))
|
|||||||
UNSHARE := $(UNSHARE) --uts
|
UNSHARE := $(UNSHARE) --uts
|
||||||
endif
|
endif
|
||||||
|
|
||||||
all: clean image clean
|
# sudo apt-get install qemu-user-static qemu-utils
|
||||||
|
all: clean packer image
|
||||||
|
|
||||||
update_langs:
|
update_langs:
|
||||||
@for lang in pwnagotchi/locale/*/; do\
|
@for lang in pwnagotchi/locale/*/; do\
|
||||||
@ -39,30 +40,21 @@ compile_langs:
|
|||||||
./scripts/language.sh compile $$(basename $$lang); \
|
./scripts/language.sh compile $$(basename $$lang); \
|
||||||
done
|
done
|
||||||
|
|
||||||
PACKER := ~/pwnagotchi/packer
|
packer: clean
|
||||||
PACKER_URL := https://releases.hashicorp.com/packer/$(PACKER_VERSION)/packer_$(PACKER_VERSION)_linux_$(GOARCH).zip
|
curl https://releases.hashicorp.com/packer/$(PACKER_VERSION)/packer_$(PACKER_VERSION)_linux_amd64.zip -o /tmp/packer.zip
|
||||||
$(PACKER):
|
unzip /tmp/packer.zip -d /tmp
|
||||||
mkdir -p $(@D)
|
sudo mv /tmp/packer /usr/bin/packer
|
||||||
curl -L "$(PACKER_URL)" -o $(PACKER).zip
|
|
||||||
unzip $(PACKER).zip -d $(@D)
|
|
||||||
rm $(PACKER).zip
|
|
||||||
chmod +x $@
|
|
||||||
|
|
||||||
SDIST := dist/pwnagotchi-$(PWN_VERSION).tar.gz
|
image: clean packer
|
||||||
$(SDIST): setup.py pwnagotchi
|
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
|
||||||
python3 setup.py sdist
|
|
||||||
|
|
||||||
# Building the image requires packer, but don't rebuild the image just because packer updated.
|
bullseye: clean packer
|
||||||
pwnagotchi: | $(PACKER)
|
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
|
||||||
|
sudo pishrink -vaZ pwnagotchi-32bit.img
|
||||||
|
|
||||||
# If the packer or ansible files are updated, rebuild the image.
|
bookworm: clean packer
|
||||||
pwnagotchi: $(SDIST) builder/pwnagotchi.json.pkr.hcl builder/raspberrypi64.yml $(shell find builder/data -type f)
|
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
|
||||||
|
sudo pishrink -vaZ pwnagotchi-64bit.img
|
||||||
cd builder && $(PACKER) init pwnagotchi.json.pkr.hcl && sudo $(UNSHARE) $(PACKER) build -var "pwn_hostname=$(PWN_HOSTNAME)" -var "pwn_version=$(PWN_VERSION)" pwnagotchi.json.pkr.hcl
|
|
||||||
|
|
||||||
.PHONY: image
|
|
||||||
image: pwnagotchi
|
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
- rm -rf build dist pwnagotchi.egg-info
|
- rm -rf /tmp/packer*
|
||||||
- rm -f $(PACKER)
|
|
||||||
|
@ -11,7 +11,7 @@ Select ‘Credentials’ from the left menu, click ‘Create Credentials’, sel
|
|||||||
|
|
||||||
Now, the product name and consent screen need to be set -> click ‘Configure consent screen’ and follow the instructions. Once finished:
|
Now, the product name and consent screen need to be set -> click ‘Configure consent screen’ and follow the instructions. Once finished:
|
||||||
|
|
||||||
Select ‘Application type’ to be Web application.
|
Select ‘Application type’ to be Desktop application.
|
||||||
|
|
||||||
Enter an appropriate name.
|
Enter an appropriate name.
|
||||||
|
|
||||||
@ -19,7 +19,7 @@ Input http://localhost/ for ‘Authorized redirect URIs’.
|
|||||||
|
|
||||||
Select the correct oauth scope:
|
Select the correct oauth scope:
|
||||||
|
|
||||||
- drive.file
|
- drive
|
||||||
- drive.install
|
- drive.install
|
||||||
|
|
||||||
Click ‘Create’.
|
Click ‘Create’.
|
||||||
|
27
README.md
27
README.md
@ -1,26 +1,7 @@
|
|||||||
# Pwnagotchi-Torch
|
# Pwnagotchi
|
||||||
<a href="https://github.com/jayofelony/pwnagotchi-bookworm/releases/latest"><img alt="Release" src="https://img.shields.io/github/release/jayofelony/pwnagotchi-bookworm.svg"></a><br/>
|
This is the main source for all forks:
|
||||||
**This fork of [Pwnagotchi](https://www.pwnagotchi.ai) is only for 64-bit Raspberry Pi's. Such as the 02W, 3(b+) and 4(b) ~~and the new Raspberry Pi 5~~!!.**
|
- RPiZeroW (32bit)
|
||||||
|
- RPiZero2W, RPi3, RPi4, RPi5 (64bit)
|
||||||
It seems the Pi 5 is unable to run in monitor mode, will keep you updated on this.
|
|
||||||
|
|
||||||
If you are using an older 32-bit version Raspberry Pi, ZeroWH, use this [fork](https://github.com/jayofelony/pwnagotchi-torch/releases/tag/v2.6.4) and make sure you download the `armhf` version.
|
|
||||||
|
|
||||||
---
|
|
||||||
Download latest image file [here](https://github.com/jayofelony/pwnagotchi-bookworm/releases/tag/v2.7.3), and let it auto-update from here on out.
|
|
||||||
|
|
||||||
**Use RPi imager to flash, please don't flash a new user as this will mess with logs created.**
|
|
||||||
|
|
||||||
SSH credentials are `pi/raspberry`.
|
|
||||||
|
|
||||||
# Donations:
|
|
||||||
I would like to thank
|
|
||||||
- [findingmoist](https://github.com/findingmoist)
|
|
||||||
- [kr4k0n](https://github.com/kr4k0n)
|
|
||||||
|
|
||||||
for donating!
|
|
||||||
|
|
||||||
[Pwnagotchi-Torch](https://www.patreon.com/pwnagotchi_torch)
|
|
||||||
|
|
||||||
[GH Sponsor](https://github.com/sponsors/jayofelony)
|
[GH Sponsor](https://github.com/sponsors/jayofelony)
|
||||||
|
|
||||||
|
157
bin/pwnagotchi
157
bin/pwnagotchi
@ -18,84 +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:
|
|
||||||
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
|
||||||
@ -157,11 +158,14 @@ def pwnagotchi_cli():
|
|||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
if args.donate:
|
if args.donate:
|
||||||
print("Donations can made @ https://github.com/sponsors/jayofelony \n\nBut only if you really want to!")
|
print("Donations can made @ \n "
|
||||||
|
"https://www.patreon.com/pwnagotchi_torch \n "
|
||||||
|
"https://github.com/sponsors/jayofelony \n\n"
|
||||||
|
"But only if you really want to!")
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
if args.check_update:
|
if args.check_update:
|
||||||
resp = requests.get("https://api.github.com/repos/jayofelony/pwnagotchi-bookworm/releases/latest")
|
resp = requests.get("https://api.github.com/repos/jayofelony/pwnagotchi/releases/latest")
|
||||||
latest = resp.json()
|
latest = resp.json()
|
||||||
latest_ver = latest['tag_name'].replace('v', '')
|
latest_ver = latest['tag_name'].replace('v', '')
|
||||||
|
|
||||||
@ -174,12 +178,11 @@ def pwnagotchi_cli():
|
|||||||
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'):
|
||||||
os.system("rm /root/.auto-update && systemctl restart pwnagotchi")
|
os.system("rm /root/.auto-update && systemctl restart pwnagotchi")
|
||||||
os.system("systemctl restart pwnagotchi")
|
else:
|
||||||
|
logging.error("You should make sure auto-update is enabled!")
|
||||||
print("Okay, give me a couple minutes. Just watch pwnlog while you wait.")
|
print("Okay, give me a couple minutes. Just watch pwnlog while you wait.")
|
||||||
elif user_input.lower() in ('n', 'no'):
|
elif user_input.lower() in ('n', 'no'): # using this elif for readability
|
||||||
print("Okay, guess not!")
|
print("Okay, guess not!")
|
||||||
else:
|
|
||||||
print("Invalid input.")
|
|
||||||
else:
|
else:
|
||||||
print("You are currently on the latest release, v%s." % pwnagotchi.__version__)
|
print("You are currently on the latest release, v%s." % pwnagotchi.__version__)
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
@ -224,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()
|
|
||||||
|
173
builder/combined.json.pkr.hcl
Normal file
173
builder/combined.json.pkr.hcl
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
packer {
|
||||||
|
required_plugins {
|
||||||
|
arm = {
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "github.com/cdecoux/builder-arm"
|
||||||
|
}
|
||||||
|
ansible = {
|
||||||
|
source = "github.com/hashicorp/ansible"
|
||||||
|
version = ">= 1.1.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "pwn_hostname" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "pwn_version" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
source "arm" "rpi64-pwnagotchi" {
|
||||||
|
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"
|
||||||
|
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"]
|
||||||
|
file_checksum_type = "sha256"
|
||||||
|
file_target_extension = "xz"
|
||||||
|
file_unarchive_cmd = ["unxz", "$ARCHIVE_PATH"]
|
||||||
|
image_path = "../pwnagotchi-64bit.img"
|
||||||
|
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" "rpi32-pwnagotchi" {
|
||||||
|
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"
|
||||||
|
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"]
|
||||||
|
file_checksum_type = "sha256"
|
||||||
|
file_target_extension = "xz"
|
||||||
|
file_unarchive_cmd = ["unxz", "$ARCHIVE_PATH"]
|
||||||
|
image_path = "../pwnagotchi-32bit.img"
|
||||||
|
qemu_binary_source_path = "/usr/libexec/qemu-binfmt/arm-binfmt-P"
|
||||||
|
qemu_binary_destination_path = "/usr/libexec/qemu-binfmt/arm-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"
|
||||||
|
}
|
||||||
|
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
|
||||||
|
# documentation for build blocks can be found here:
|
||||||
|
# https://www.packer.io/docs/from-1.5/blocks/build
|
||||||
|
build {
|
||||||
|
name = "Raspberry Pi 64 Pwnagotchi"
|
||||||
|
sources = ["source.arm.rpi64-pwnagotchi"]
|
||||||
|
|
||||||
|
provisioner "file" {
|
||||||
|
destination = "/usr/bin/"
|
||||||
|
sources = [
|
||||||
|
"data/64bit/usr/bin/bettercap-launcher",
|
||||||
|
"data/64bit/usr/bin/hdmioff",
|
||||||
|
"data/64bit/usr/bin/hdmion",
|
||||||
|
"data/64bit/usr/bin/monstart",
|
||||||
|
"data/64bit/usr/bin/monstop",
|
||||||
|
"data/64bit/usr/bin/pwnagotchi-launcher",
|
||||||
|
"data/64bit/usr/bin/pwnlib",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
provisioner "shell" {
|
||||||
|
inline = ["chmod +x /usr/bin/*"]
|
||||||
|
}
|
||||||
|
|
||||||
|
provisioner "file" {
|
||||||
|
destination = "/etc/systemd/system/"
|
||||||
|
sources = [
|
||||||
|
"data/64bit/etc/systemd/system/bettercap.service",
|
||||||
|
"data/64bit/etc/systemd/system/pwnagotchi.service",
|
||||||
|
"data/64bit/etc/systemd/system/pwngrid-peer.service",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
provisioner "file" {
|
||||||
|
destination = "/etc/update-motd.d/01-motd"
|
||||||
|
source = "data/64bit/etc/update-motd.d/01-motd"
|
||||||
|
}
|
||||||
|
provisioner "shell" {
|
||||||
|
inline = ["chmod +x /etc/update-motd.d/*"]
|
||||||
|
}
|
||||||
|
provisioner "shell" {
|
||||||
|
inline = ["apt-get -y --allow-releaseinfo-change update", "apt-get -y dist-upgrade", "apt-get install -y --no-install-recommends ansible"]
|
||||||
|
}
|
||||||
|
provisioner "ansible-local" {
|
||||||
|
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\""]
|
||||||
|
playbook_file = "data/64bit/raspberrypi64.yml"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
build {
|
||||||
|
name = "Raspberry Pi 32 Pwnagotchi"
|
||||||
|
sources = ["source.arm.rpi32-pwnagotchi"]
|
||||||
|
provisioner "file" {
|
||||||
|
destination = "/usr/bin/"
|
||||||
|
sources = [
|
||||||
|
"data/32bit/usr/bin/bettercap-launcher",
|
||||||
|
"data/32bit/usr/bin/hdmioff",
|
||||||
|
"data/32bit/usr/bin/hdmion",
|
||||||
|
"data/32bit/usr/bin/monstart",
|
||||||
|
"data/32bit/usr/bin/monstop",
|
||||||
|
"data/32bit/usr/bin/pwnagotchi-launcher",
|
||||||
|
"data/32bit/usr/bin/pwnlib",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
provisioner "shell" {
|
||||||
|
inline = ["chmod +x /usr/bin/*"]
|
||||||
|
}
|
||||||
|
|
||||||
|
provisioner "file" {
|
||||||
|
destination = "/etc/systemd/system/"
|
||||||
|
sources = [
|
||||||
|
"data/32bit/etc/systemd/system/bettercap.service",
|
||||||
|
"data/32bit/etc/systemd/system/pwnagotchi.service",
|
||||||
|
"data/32bit/etc/systemd/system/pwngrid-peer.service",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
provisioner "file" {
|
||||||
|
destination = "/etc/update-motd.d/01-motd"
|
||||||
|
source = "data/32bit/etc/update-motd.d/01-motd"
|
||||||
|
}
|
||||||
|
provisioner "shell" {
|
||||||
|
inline = ["chmod +x /etc/update-motd.d/*"]
|
||||||
|
}
|
||||||
|
provisioner "shell" {
|
||||||
|
inline = ["apt-get -y --allow-releaseinfo-change update", "apt-get -y dist-upgrade", "apt-get install -y --no-install-recommends ansible"]
|
||||||
|
}
|
||||||
|
provisioner "ansible-local" {
|
||||||
|
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\""]
|
||||||
|
playbook_dir = "data/32bit/extras/"
|
||||||
|
playbook_file = "data/32bit/raspberrypi32.yml"
|
||||||
|
}
|
||||||
|
}
|
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
|
26
builder/data/32bit/etc/dphys-swapfile
Normal file
26
builder/data/32bit/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
|
20
builder/data/32bit/etc/systemd/system/bluetooth.service
Normal file
20
builder/data/32bit/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
|
@ -0,0 +1,6 @@
|
|||||||
|
[Unit]
|
||||||
|
After=hciuart.service bluetooth.service
|
||||||
|
Before=
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
ExecStartPre=/bin/sleep 5
|
@ -8,7 +8,7 @@ After=bettercap.service
|
|||||||
Environment=LD_PRELOAD=/usr/local/lib/libpcap.so.1
|
Environment=LD_PRELOAD=/usr/local/lib/libpcap.so.1
|
||||||
Environment=LD_LIBRARY_PATH=/usr/local/lib
|
Environment=LD_LIBRARY_PATH=/usr/local/lib
|
||||||
Type=simple
|
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 /home/pi/logs/pwngrid-peer.log -iface wlan0mon
|
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
|
Restart=always
|
||||||
RestartSec=30
|
RestartSec=30
|
||||||
|
|
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 }}"
|
94
builder/data/32bit/raspberrypi32.json.pkr.hcl
Normal file
94
builder/data/32bit/raspberrypi32.json.pkr.hcl
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
packer {
|
||||||
|
required_plugins {
|
||||||
|
arm = {
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "github.com/cdecoux/builder-arm"
|
||||||
|
}
|
||||||
|
ansible = {
|
||||||
|
source = "github.com/hashicorp/ansible"
|
||||||
|
version = ">= 1.1.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "pwn_hostname" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "pwn_version" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
source "arm" "rpi32-pwnagotchi" {
|
||||||
|
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"
|
||||||
|
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"]
|
||||||
|
file_checksum_type = "sha256"
|
||||||
|
file_target_extension = "xz"
|
||||||
|
file_unarchive_cmd = ["unxz", "$ARCHIVE_PATH"]
|
||||||
|
image_path = "../../pwnagotchi-32bit.img"
|
||||||
|
qemu_binary_source_path = "/usr/libexec/qemu-binfmt/arm-binfmt-P"
|
||||||
|
qemu_binary_destination_path = "/usr/libexec/qemu-binfmt/arm-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"
|
||||||
|
}
|
||||||
|
image_partitions {
|
||||||
|
name = "root"
|
||||||
|
type = "83"
|
||||||
|
start_sector = "532480"
|
||||||
|
filesystem = "ext4"
|
||||||
|
size = "0"
|
||||||
|
mountpoint = "/"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
build {
|
||||||
|
name = "Raspberry Pi 32 Pwnagotchi"
|
||||||
|
sources = ["source.arm.rpi32-pwnagotchi"]
|
||||||
|
provisioner "file" {
|
||||||
|
destination = "/usr/bin/"
|
||||||
|
sources = [
|
||||||
|
"data/32bit/usr/bin/bettercap-launcher",
|
||||||
|
"data/32bit/usr/bin/hdmioff",
|
||||||
|
"data/32bit/usr/bin/hdmion",
|
||||||
|
"data/32bit/usr/bin/monstart",
|
||||||
|
"data/32bit/usr/bin/monstop",
|
||||||
|
"data/32bit/usr/bin/pwnagotchi-launcher",
|
||||||
|
"data/32bit/usr/bin/pwnlib",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
provisioner "shell" {
|
||||||
|
inline = ["chmod +x /usr/bin/*"]
|
||||||
|
}
|
||||||
|
|
||||||
|
provisioner "file" {
|
||||||
|
destination = "/etc/systemd/system/"
|
||||||
|
sources = [
|
||||||
|
"data/32bit/etc/systemd/system/bettercap.service",
|
||||||
|
"data/32bit/etc/systemd/system/pwnagotchi.service",
|
||||||
|
"data/32bit/etc/systemd/system/pwngrid-peer.service",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
provisioner "file" {
|
||||||
|
destination = "/etc/update-motd.d/01-motd"
|
||||||
|
source = "data/32bit/etc/update-motd.d/01-motd"
|
||||||
|
}
|
||||||
|
provisioner "shell" {
|
||||||
|
inline = ["chmod +x /etc/update-motd.d/*"]
|
||||||
|
}
|
||||||
|
provisioner "shell" {
|
||||||
|
inline = ["apt-get -y --allow-releaseinfo-change update", "apt-get -y dist-upgrade", "apt-get install -y --no-install-recommends ansible"]
|
||||||
|
}
|
||||||
|
provisioner "ansible-local" {
|
||||||
|
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\""]
|
||||||
|
playbook_dir = "data/32bit/extras/"
|
||||||
|
playbook_file = "data/32bit/raspberrypi32.yml"
|
||||||
|
}
|
||||||
|
}
|
606
builder/data/32bit/raspberrypi32.yml
Normal file
606
builder/data/32bit/raspberrypi32.yml
Normal file
@ -0,0 +1,606 @@
|
|||||||
|
---
|
||||||
|
- hosts:
|
||||||
|
- 127.0.0.1
|
||||||
|
gather_facts: true
|
||||||
|
become: true
|
||||||
|
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:
|
||||||
|
min: "6.1"
|
||||||
|
full: "6.1.21+"
|
||||||
|
full_2w: "6.1.21-v7+"
|
||||||
|
full_4b: "6.1.21-v7l+"
|
||||||
|
arch: "v6l"
|
||||||
|
pwnagotchi:
|
||||||
|
hostname: "{{ lookup('env', 'PWN_HOSTNAME') | 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:
|
||||||
|
enable:
|
||||||
|
- bettercap.service
|
||||||
|
- bluetooth.service
|
||||||
|
- dphys-swapfile.service
|
||||||
|
- fstrim.timer
|
||||||
|
- pwnagotchi.service
|
||||||
|
- pwngrid-peer.service
|
||||||
|
disable:
|
||||||
|
- apt-daily-upgrade.service
|
||||||
|
- apt-daily-upgrade.timer
|
||||||
|
- apt-daily.service
|
||||||
|
- apt-daily.timer
|
||||||
|
- ifup@wlan0.service
|
||||||
|
- triggerhappy.service
|
||||||
|
- wpa_supplicant.service
|
||||||
|
packages:
|
||||||
|
caplets:
|
||||||
|
source: "https://github.com/jayofelony/caplets.git"
|
||||||
|
bettercap:
|
||||||
|
source: "https://github.com/jayofelony/bettercap.git"
|
||||||
|
url: "https://github.com/jayofelony/bettercap/releases/download/2.32.2/bettercap-2.32.2-armhf.zip"
|
||||||
|
ui: "https://github.com/bettercap/ui/releases/download/v1.3.0/ui.zip"
|
||||||
|
opwngrid:
|
||||||
|
source: "https://github.com/jayofelony/pwngrid.git"
|
||||||
|
url: "https://github.com/jayofelony/pwngrid/releases/download/v1.10.7/pwngrid-1.10.7-armhf.zip"
|
||||||
|
torch:
|
||||||
|
wheel: "torch-2.1.0a0+gitunknown-cp39-cp39-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:
|
||||||
|
wheel: "torchvision-0.16.0a0-cp39-cp39-linux_armv6l.whl"
|
||||||
|
url: "https://github.com/Sniffleupagus/Torch4Pizero/releases/download/v1.0.0/torchvision-0.16.0a0-cp39-cp39-linux_armv6l.whl"
|
||||||
|
apt:
|
||||||
|
downgrade:
|
||||||
|
- libpcap-dev_1.9.1-4_armhf.deb
|
||||||
|
- libpcap0.8-dbg_1.9.1-4_armhf.deb
|
||||||
|
- libpcap0.8-dev_1.9.1-4_armhf.deb
|
||||||
|
- libpcap0.8_1.9.1-4_armhf.deb
|
||||||
|
hold:
|
||||||
|
- firmware-atheros
|
||||||
|
- firmware-brcm80211
|
||||||
|
- firmware-libertas
|
||||||
|
- firmware-misc-nonfree
|
||||||
|
- firmware-realtek
|
||||||
|
- libpcap-dev
|
||||||
|
- libpcap0.8
|
||||||
|
- libpcap0.8-dev
|
||||||
|
- libpcap0.8-dbg
|
||||||
|
remove:
|
||||||
|
- avahi-daemon
|
||||||
|
- nfs-common
|
||||||
|
- triggerhappy
|
||||||
|
- wpasupplicant
|
||||||
|
install:
|
||||||
|
- autoconf
|
||||||
|
- bc
|
||||||
|
- bison
|
||||||
|
- bluez
|
||||||
|
- build-essential
|
||||||
|
- curl
|
||||||
|
- dkms
|
||||||
|
- dphys-swapfile
|
||||||
|
- espeak-ng
|
||||||
|
- evtest
|
||||||
|
- fbi
|
||||||
|
- flex
|
||||||
|
- fonts-dejavu
|
||||||
|
- fonts-dejavu-core
|
||||||
|
- fonts-dejavu-extra
|
||||||
|
- fonts-freefont-ttf
|
||||||
|
- g++
|
||||||
|
- gawk
|
||||||
|
- gcc-arm-none-eabi
|
||||||
|
- git
|
||||||
|
- libatlas-base-dev
|
||||||
|
- libavcodec58
|
||||||
|
- libavformat58
|
||||||
|
- libblas-dev
|
||||||
|
- libbluetooth-dev
|
||||||
|
- libbz2-dev
|
||||||
|
- libc-ares-dev
|
||||||
|
- libc6-dev
|
||||||
|
- libcpuinfo-dev
|
||||||
|
- libdbus-1-dev
|
||||||
|
- libdbus-glib-1-dev
|
||||||
|
- libeigen3-dev
|
||||||
|
- libelf-dev
|
||||||
|
- libffi-dev
|
||||||
|
- libfl-dev
|
||||||
|
- libfuse-dev
|
||||||
|
- libgdbm-dev
|
||||||
|
- libgl1-mesa-glx
|
||||||
|
- libgmp3-dev
|
||||||
|
- libgstreamer1.0-0
|
||||||
|
- libhdf5-dev
|
||||||
|
- liblapack-dev
|
||||||
|
- libncursesw5-dev
|
||||||
|
- libnetfilter-queue-dev
|
||||||
|
- libopenblas-dev
|
||||||
|
- libopenjp2-7
|
||||||
|
- libopenmpi-dev
|
||||||
|
- libopenmpi3
|
||||||
|
- libpcap-dev
|
||||||
|
- libprotobuf-dev
|
||||||
|
- libraspberrypi-bin
|
||||||
|
- libraspberrypi-dev
|
||||||
|
- libraspberrypi-doc
|
||||||
|
- libraspberrypi0
|
||||||
|
- libsleef-dev
|
||||||
|
- libsqlite3-dev
|
||||||
|
- libssl-dev
|
||||||
|
- libswscale5
|
||||||
|
- libtiff5
|
||||||
|
- libtool
|
||||||
|
- libts-bin
|
||||||
|
- libusb-1.0-0-dev
|
||||||
|
- lsof
|
||||||
|
- make
|
||||||
|
- python3-flask
|
||||||
|
- python3-flask-cors
|
||||||
|
- python3-flaskext.wtf
|
||||||
|
- python3-pil
|
||||||
|
- python3-pip
|
||||||
|
- python3-protobuf
|
||||||
|
- python3-smbus
|
||||||
|
- qpdf
|
||||||
|
- raspberrypi-kernel-headers
|
||||||
|
- rsync
|
||||||
|
- screen
|
||||||
|
- tcpdump
|
||||||
|
- texinfo
|
||||||
|
- time
|
||||||
|
- tk-dev
|
||||||
|
- unzip
|
||||||
|
- vim
|
||||||
|
- wget
|
||||||
|
- wl
|
||||||
|
- xxd
|
||||||
|
- zlib1g-dev
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
- name: Create pi user
|
||||||
|
copy:
|
||||||
|
dest: /boot/userconf
|
||||||
|
content: |
|
||||||
|
pi:$6$3jNr0GA9KIyt4hmM$efeVIopdMQ8DGgEPCWWlbx3mJJNAYci1lEXGdlky0xPyjqwKNbwTL5SrCcpb4144C4IvzWjn7Iv.QjqmU7iyT/
|
||||||
|
|
||||||
|
- name: change hostname
|
||||||
|
lineinfile:
|
||||||
|
dest: /etc/hostname
|
||||||
|
regexp: '^raspberrypi'
|
||||||
|
line: "{{pwnagotchi.hostname}}"
|
||||||
|
state: present
|
||||||
|
when: lookup('file', '/etc/hostname') == "raspberrypi"
|
||||||
|
register: hostname
|
||||||
|
|
||||||
|
- name: add hostname to /etc/hosts
|
||||||
|
lineinfile:
|
||||||
|
dest: /etc/hosts
|
||||||
|
regexp: '^127\.0\.1\.1[ \t]+raspberrypi'
|
||||||
|
line: "127.0.1.1\t{{pwnagotchi.hostname}}"
|
||||||
|
state: present
|
||||||
|
when: hostname.changed
|
||||||
|
|
||||||
|
- name: Create custom plugin directory
|
||||||
|
file:
|
||||||
|
path: '{{ pwnagotchi.custom_plugin_dir }}'
|
||||||
|
state: directory
|
||||||
|
|
||||||
|
- name: update apt package cache
|
||||||
|
apt:
|
||||||
|
update_cache: yes
|
||||||
|
|
||||||
|
- name: install packages
|
||||||
|
apt:
|
||||||
|
name: "{{ packages.apt.install }}"
|
||||||
|
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
|
||||||
|
#
|
||||||
|
###########################################
|
||||||
|
|
||||||
|
# check for presence, then it can re-run in later parts if needed
|
||||||
|
# use the "make" built in
|
||||||
|
|
||||||
|
# install libpcap before bettercap and pwngrid, so they use it
|
||||||
|
- name: clone libpcap v1.9 from github
|
||||||
|
git:
|
||||||
|
repo: 'https://github.com/the-tcpdump-group/libpcap.git'
|
||||||
|
dest: /usr/local/src/libpcap
|
||||||
|
version: libpcap-1.9
|
||||||
|
|
||||||
|
- name: build and install libpcap into /usr/local/lib
|
||||||
|
shell: "./configure && make && make install"
|
||||||
|
args:
|
||||||
|
executable: /bin/bash
|
||||||
|
chdir: /usr/local/src/libpcap
|
||||||
|
|
||||||
|
- name: remove libpcap build folder
|
||||||
|
file:
|
||||||
|
state: absent
|
||||||
|
path: /usr/local/src/libpcap
|
||||||
|
|
||||||
|
- name: create symlink /usr/local/lib/libpcap.so.1.9.1
|
||||||
|
file:
|
||||||
|
src: /usr/local/lib/libpcap.so.1.9.1
|
||||||
|
dest: /usr/local/lib/libpcap.so.0.8
|
||||||
|
state: link
|
||||||
|
|
||||||
|
###############################################################
|
||||||
|
# Install nexmon to fix wireless scanning (takes 2.5G of space)
|
||||||
|
###############################################################
|
||||||
|
|
||||||
|
# Install nexmon for all boards
|
||||||
|
- name: build and install nexmon as needed
|
||||||
|
include_tasks: nexmon.yml
|
||||||
|
loop: "{{ boards }}"
|
||||||
|
|
||||||
|
# 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
|
||||||
|
copy:
|
||||||
|
src: /usr/lib/firmware/brcm/brcmfmac43430-sdio.bin
|
||||||
|
dest: /usr/lib/firmware/brcm/brcmfmac43436s-sdio.bin
|
||||||
|
follow: true
|
||||||
|
|
||||||
|
# delete blob files that make nexmon sad
|
||||||
|
- name: Delete the firmware blob files to avoid some nexmon crashing
|
||||||
|
file:
|
||||||
|
state: absent
|
||||||
|
path: '{{ item }}'
|
||||||
|
loop:
|
||||||
|
- /usr/lib/firmware/brcm/brcmfmac43430-sdio.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
|
||||||
|
- /usr/lib/firmware/brcm/brcmfmac43436-sdio.raspberrypi,model-zero-2-w.clm_blob
|
||||||
|
- /usr/lib/firmware/brcm/brcmfmac43430-sdio.raspberrypi,3-model-b.clm_blob
|
||||||
|
|
||||||
|
# To shrink the final image, remove the nexmon directory (takes 2.5G of space) post build and installation
|
||||||
|
- name: Delete nexmon content & directory
|
||||||
|
file:
|
||||||
|
state: absent
|
||||||
|
path: /usr/local/src/nexmon/
|
||||||
|
|
||||||
|
- name: clone pwnagotchi repository
|
||||||
|
git:
|
||||||
|
repo: https://github.com/jayofelony/pwnagotchi.git
|
||||||
|
dest: /usr/local/src/pwnagotchi
|
||||||
|
register: pwnagotchigit
|
||||||
|
|
||||||
|
# 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
|
||||||
|
command: "python3 setup.py sdist bdist_wheel"
|
||||||
|
args:
|
||||||
|
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
|
||||||
|
file:
|
||||||
|
path: /usr/local/share/pwnagotchi/
|
||||||
|
state: directory
|
||||||
|
|
||||||
|
- name: remove pwnagotchi folder
|
||||||
|
file:
|
||||||
|
state: absent
|
||||||
|
path: /usr/local/src/pwnagotchi
|
||||||
|
|
||||||
|
- name: remove torch whl
|
||||||
|
file:
|
||||||
|
state: absent
|
||||||
|
path: "{{ lookup('fileglob', '/usr/local/src/torch*.whl') }}"
|
||||||
|
|
||||||
|
##########################################
|
||||||
|
#
|
||||||
|
# pwngrid, bettercap
|
||||||
|
#
|
||||||
|
##########################################
|
||||||
|
|
||||||
|
- name: Install go-1.21
|
||||||
|
unarchive:
|
||||||
|
src: https://go.dev/dl/go1.21.6.linux-armv6l.tar.gz
|
||||||
|
dest: /usr/local
|
||||||
|
remote_src: yes
|
||||||
|
register: golang
|
||||||
|
|
||||||
|
- name: Update .bashrc for go-1.21
|
||||||
|
blockinfile:
|
||||||
|
dest: /home/pi/.bashrc
|
||||||
|
state: present
|
||||||
|
block: |
|
||||||
|
export GOPATH=$HOME/go
|
||||||
|
export PATH=/usr/local/go/bin:$PATH:$GOPATH/bin
|
||||||
|
when: golang.changed
|
||||||
|
|
||||||
|
- name: download pwngrid
|
||||||
|
unarchive:
|
||||||
|
remote_src: yes
|
||||||
|
src: "{{ packages.opwngrid.url }}"
|
||||||
|
dest: /usr/local/bin/
|
||||||
|
mode: 0755
|
||||||
|
|
||||||
|
- name: download and install bettercap
|
||||||
|
unarchive:
|
||||||
|
src: "{{ packages.bettercap.url }}"
|
||||||
|
dest: /usr/local/bin
|
||||||
|
remote_src: yes
|
||||||
|
exclude:
|
||||||
|
- README.md
|
||||||
|
- LICENSE.md
|
||||||
|
mode: 0755
|
||||||
|
|
||||||
|
- name: clone bettercap caplets
|
||||||
|
git:
|
||||||
|
repo: "{{ packages.caplets.source }}"
|
||||||
|
dest: /tmp/caplets
|
||||||
|
register: capletsgit
|
||||||
|
|
||||||
|
- name: install bettercap caplets
|
||||||
|
make:
|
||||||
|
chdir: /tmp/caplets
|
||||||
|
target: install
|
||||||
|
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
|
||||||
|
file:
|
||||||
|
path: /etc/pwnagotchi
|
||||||
|
state: directory
|
||||||
|
|
||||||
|
- name: create log folder
|
||||||
|
file:
|
||||||
|
path: /home/pi/logs
|
||||||
|
state: directory
|
||||||
|
|
||||||
|
- name: check if user configuration exists
|
||||||
|
stat:
|
||||||
|
path: /etc/pwnagotchi/config.toml
|
||||||
|
register: user_config
|
||||||
|
|
||||||
|
- name: create /etc/pwnagotchi/config.toml
|
||||||
|
copy:
|
||||||
|
dest: /etc/pwnagotchi/config.toml
|
||||||
|
content: |
|
||||||
|
# Add your configuration overrides on this file any configuration changes done to default.toml will be lost!
|
||||||
|
# Example:
|
||||||
|
# ui.display.enabled = true
|
||||||
|
# ui.display.type = "waveshare_2"
|
||||||
|
when: not user_config.stat.exists
|
||||||
|
|
||||||
|
- name: Delete motd 10-uname
|
||||||
|
file:
|
||||||
|
state: absent
|
||||||
|
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
|
||||||
|
dpkg_selections:
|
||||||
|
name: "{{ item }}"
|
||||||
|
selection: hold
|
||||||
|
with_items: "{{ packages.apt.hold }}"
|
||||||
|
|
||||||
|
- name: disable unnecessary services
|
||||||
|
systemd:
|
||||||
|
name: "{{ item }}"
|
||||||
|
state: stopped
|
||||||
|
enabled: no
|
||||||
|
with_items: "{{ services.disable }}"
|
||||||
|
|
||||||
|
- name: enable services
|
||||||
|
systemd:
|
||||||
|
name: "{{ item }}"
|
||||||
|
enabled: true
|
||||||
|
state: stopped
|
||||||
|
with_items: "{{ services.enable }}"
|
||||||
|
|
||||||
|
#- 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
|
||||||
|
file:
|
||||||
|
path: /root
|
||||||
|
mode: '755'
|
||||||
|
|
||||||
|
- name: fix permissions on /home/pi
|
||||||
|
file:
|
||||||
|
path: /home/pi
|
||||||
|
owner: pi
|
||||||
|
group: pi
|
||||||
|
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
|
||||||
|
file:
|
||||||
|
path: /root/go_pkgs.tgz
|
||||||
|
state: absent
|
||||||
|
|
||||||
|
- name: remove golang
|
||||||
|
file:
|
||||||
|
state: absent
|
||||||
|
path: /usr/local/go
|
||||||
|
|
||||||
|
- name: remove /root/.cache (pip cache)
|
||||||
|
file:
|
||||||
|
state: absent
|
||||||
|
path: /root/.cache
|
||||||
|
|
||||||
|
- name: remove ssh keys
|
||||||
|
file:
|
||||||
|
state: absent
|
||||||
|
path: "{{ item }}"
|
||||||
|
with_fileglob:
|
||||||
|
- "/etc/ssh/ssh_host*_key*"
|
||||||
|
|
||||||
|
- name: regenerate ssh keys
|
||||||
|
shell: "dpkg-reconfigure openssh-server"
|
||||||
|
args:
|
||||||
|
executable: /bin/bash
|
||||||
|
|
||||||
|
handlers:
|
||||||
|
- name: reload systemd services
|
||||||
|
systemd:
|
||||||
|
daemon_reload: yes
|
@ -18,7 +18,7 @@ if ! check_brcm; then
|
|||||||
sleep 10
|
sleep 10
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# start mon0
|
# start wlan0mon
|
||||||
start_monitor_interface
|
start_monitor_interface
|
||||||
|
|
||||||
if is_auto_mode_no_delete; then
|
if is_auto_mode_no_delete; then
|
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()
|
184
builder/data/32bit/usr/bin/pwnlib
Executable file
184
builder/data/32bit/usr/bin/pwnlib
Executable file
@ -0,0 +1,184 @@
|
|||||||
|
#!/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_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
|
||||||
|
iw dev wlan0 set power_save off
|
||||||
|
iw phy "$(iw phy | head -1 | cut -d" " -f2)" interface add wlan0mon type monitor
|
||||||
|
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 specificed 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
|
||||||
|
}
|
@ -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
|
33
builder/data/64bit/etc/update-motd.d/01-motd
Executable file
33
builder/data/64bit/etc/update-motd.d/01-motd
Executable file
@ -0,0 +1,33 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
_hostname=$(hostname)
|
||||||
|
_version=$(cut -d"'" -f2 < /usr/local/lib/python3.11/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 can learn more about me at https://pwnagotchi.org/"
|
@ -1,14 +1,12 @@
|
|||||||
# This is not working quite yet
|
|
||||||
# https://github.com/mkaczanowski/packer-builder-arm/pull/172
|
|
||||||
packer {
|
packer {
|
||||||
required_plugins {
|
required_plugins {
|
||||||
#arm = {
|
arm = {
|
||||||
# version = "~> 1"
|
version = "1.0.0"
|
||||||
# source = "github.com/cdecoux/builder-arm"
|
source = "github.com/cdecoux/builder-arm"
|
||||||
#}
|
}
|
||||||
ansible = {
|
ansible = {
|
||||||
source = "github.com/hashicorp/ansible"
|
source = "github.com/hashicorp/ansible"
|
||||||
version = "~> 1"
|
version = ">= 1.1.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -27,9 +25,9 @@ source "arm" "rpi64-pwnagotchi" {
|
|||||||
file_checksum_type = "sha256"
|
file_checksum_type = "sha256"
|
||||||
file_target_extension = "xz"
|
file_target_extension = "xz"
|
||||||
file_unarchive_cmd = ["unxz", "$ARCHIVE_PATH"]
|
file_unarchive_cmd = ["unxz", "$ARCHIVE_PATH"]
|
||||||
image_path = "../../../pwnagotchi-rpi-bookworm-${var.pwn_version}-arm64.img"
|
image_path = "../../../pwnagotchi-64bit.img"
|
||||||
qemu_binary_source_path = "/usr/bin/qemu-aarch64-static"
|
qemu_binary_source_path = "/usr/libexec/qemu-binfmt/aarch64-binfmt-P"
|
||||||
qemu_binary_destination_path = "/usr/bin/qemu-aarch64-static"
|
qemu_binary_destination_path = "/usr/libexec/qemu-binfmt/aarch64-binfmt-P"
|
||||||
image_build_method = "resize"
|
image_build_method = "resize"
|
||||||
image_size = "9G"
|
image_size = "9G"
|
||||||
image_type = "dos"
|
image_type = "dos"
|
||||||
@ -51,6 +49,8 @@ source "arm" "rpi64-pwnagotchi" {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# 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
|
||||||
@ -61,13 +61,13 @@ build {
|
|||||||
provisioner "file" {
|
provisioner "file" {
|
||||||
destination = "/usr/bin/"
|
destination = "/usr/bin/"
|
||||||
sources = [
|
sources = [
|
||||||
"data/usr/bin/bettercap-launcher",
|
"data/64bit/usr/bin/bettercap-launcher",
|
||||||
"data/usr/bin/hdmioff",
|
"data/64bit/usr/bin/hdmioff",
|
||||||
"data/usr/bin/hdmion",
|
"data/64bit/usr/bin/hdmion",
|
||||||
"data/usr/bin/monstart",
|
"data/64bit/usr/bin/monstart",
|
||||||
"data/usr/bin/monstop",
|
"data/64bit/usr/bin/monstop",
|
||||||
"data/usr/bin/pwnagotchi-launcher",
|
"data/64bit/usr/bin/pwnagotchi-launcher",
|
||||||
"data/usr/bin/pwnlib",
|
"data/64bit/usr/bin/pwnlib",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
provisioner "shell" {
|
provisioner "shell" {
|
||||||
@ -77,15 +77,14 @@ build {
|
|||||||
provisioner "file" {
|
provisioner "file" {
|
||||||
destination = "/etc/systemd/system/"
|
destination = "/etc/systemd/system/"
|
||||||
sources = [
|
sources = [
|
||||||
"data/etc/systemd/system/bettercap.service",
|
"data/64bit/etc/systemd/system/bettercap.service",
|
||||||
"data/etc/systemd/system/pwnagotchi.service",
|
"data/64bit/etc/systemd/system/pwnagotchi.service",
|
||||||
"data/etc/systemd/system/pwngrid-peer.service",
|
"data/64bit/etc/systemd/system/pwngrid-peer.service",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
provisioner "file" {
|
provisioner "file" {
|
||||||
destination = "/etc/update-motd.d/01-motd"
|
destination = "/etc/update-motd.d/01-motd"
|
||||||
source = "data/etc/update-motd.d/01-motd"
|
source = "data/64bit/etc/update-motd.d/01-motd"
|
||||||
}
|
}
|
||||||
provisioner "shell" {
|
provisioner "shell" {
|
||||||
inline = ["chmod +x /etc/update-motd.d/*"]
|
inline = ["chmod +x /etc/update-motd.d/*"]
|
||||||
@ -96,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"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -6,10 +6,11 @@
|
|||||||
vars:
|
vars:
|
||||||
kernel:
|
kernel:
|
||||||
min: "6.1"
|
min: "6.1"
|
||||||
full: "6.1.0-rpi7-rpi-v8"
|
full: "6.1.0-rpi8-rpi-v8"
|
||||||
|
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-torch', true) }}"
|
version: "{{ lookup('env', 'PWN_VERSION') | default('pwnagotchi', true) }}"
|
||||||
system:
|
system:
|
||||||
boot_options:
|
boot_options:
|
||||||
- "dtoverlay=dwc2"
|
- "dtoverlay=dwc2"
|
||||||
@ -26,7 +27,6 @@
|
|||||||
- fstrim.timer
|
- fstrim.timer
|
||||||
- pwnagotchi.service
|
- pwnagotchi.service
|
||||||
- pwngrid-peer.service
|
- pwngrid-peer.service
|
||||||
- zramswap.service
|
|
||||||
disable:
|
disable:
|
||||||
- apt-daily-upgrade.service
|
- apt-daily-upgrade.service
|
||||||
- apt-daily-upgrade.timer
|
- apt-daily-upgrade.timer
|
||||||
@ -39,7 +39,7 @@
|
|||||||
source: "https://github.com/jayofelony/caplets.git"
|
source: "https://github.com/jayofelony/caplets.git"
|
||||||
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.2/bettercap-2.32.2.zip"
|
url: "https://github.com/jayofelony/bettercap/releases/download/2.32.4/bettercap-2.32.4.zip"
|
||||||
ui: "https://github.com/bettercap/ui/releases/download/v1.3.0/ui.zip"
|
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"
|
||||||
@ -51,13 +51,20 @@
|
|||||||
- libpcap0.8-dev_1.9.1-4_arm64.deb
|
- libpcap0.8-dev_1.9.1-4_arm64.deb
|
||||||
- libpcap0.8_1.9.1-4_arm64.deb
|
- libpcap0.8_1.9.1-4_arm64.deb
|
||||||
hold:
|
hold:
|
||||||
|
- firmware-atheros
|
||||||
|
- firmware-brcm80211
|
||||||
|
- firmware-libertas
|
||||||
|
- firmware-misc-nonfree
|
||||||
|
- firmware-realtek
|
||||||
- libpcap-dev
|
- libpcap-dev
|
||||||
- libpcap0.8
|
- libpcap0.8
|
||||||
- libpcap0.8-dev
|
|
||||||
- libpcap0.8-dbg
|
- libpcap0.8-dbg
|
||||||
|
- libpcap0.8-dev
|
||||||
remove:
|
remove:
|
||||||
- avahi-daemon
|
- avahi-daemon
|
||||||
- dhpys-swapfile
|
- dhpys-swapfile
|
||||||
|
- libcurl-ocaml-dev
|
||||||
|
- libssl-ocaml-dev
|
||||||
- nfs-common
|
- nfs-common
|
||||||
- triggerhappy
|
- triggerhappy
|
||||||
- wpasupplicant
|
- wpasupplicant
|
||||||
@ -70,7 +77,13 @@
|
|||||||
- build-essential
|
- build-essential
|
||||||
- curl
|
- curl
|
||||||
- dkms
|
- dkms
|
||||||
|
- dphys-swapfile
|
||||||
- fbi
|
- fbi
|
||||||
|
- firmware-atheros
|
||||||
|
- firmware-brcm80211
|
||||||
|
- firmware-libertas
|
||||||
|
- firmware-misc-nonfree
|
||||||
|
- firmware-realtek
|
||||||
- flex
|
- flex
|
||||||
- fonts-dejavu
|
- fonts-dejavu
|
||||||
- fonts-dejavu-core
|
- fonts-dejavu-core
|
||||||
@ -80,6 +93,7 @@
|
|||||||
- gawk
|
- gawk
|
||||||
- gcc-arm-none-eabi
|
- gcc-arm-none-eabi
|
||||||
- git
|
- git
|
||||||
|
- hcxtools
|
||||||
- libatlas-base-dev
|
- libatlas-base-dev
|
||||||
- libavcodec59
|
- libavcodec59
|
||||||
- libavformat59
|
- libavformat59
|
||||||
@ -89,6 +103,7 @@
|
|||||||
- libc-ares-dev
|
- libc-ares-dev
|
||||||
- libc6-dev
|
- libc6-dev
|
||||||
- libcap-dev
|
- libcap-dev
|
||||||
|
- libcurl-ocaml-dev
|
||||||
- libdbus-1-dev
|
- libdbus-1-dev
|
||||||
- libdbus-glib-1-dev
|
- libdbus-glib-1-dev
|
||||||
- libeigen3-dev
|
- libeigen3-dev
|
||||||
@ -115,34 +130,30 @@
|
|||||||
- libraspberrypi0
|
- libraspberrypi0
|
||||||
- libsqlite3-dev
|
- libsqlite3-dev
|
||||||
- libssl-dev
|
- libssl-dev
|
||||||
|
- libssl-ocaml-dev
|
||||||
- libswscale5
|
- libswscale5
|
||||||
- libtiff6
|
- libtiff6
|
||||||
- libtool
|
- libtool
|
||||||
- libusb-1.0-0-dev
|
- libusb-1.0-0-dev
|
||||||
- lsof
|
- lsof
|
||||||
- make
|
- make
|
||||||
- python3-yaml
|
|
||||||
- python3-dbus
|
- python3-dbus
|
||||||
- python3-flask
|
- python3-flask
|
||||||
- python3-flask-cors
|
- python3-flask-cors
|
||||||
- python3-flaskext.wtf
|
- python3-flaskext.wtf
|
||||||
- python3-gast
|
- python3-gast
|
||||||
- python3-pil
|
- python3-pil
|
||||||
|
- python3-pip
|
||||||
- python3-pycryptodome
|
- python3-pycryptodome
|
||||||
- python3-requests
|
- python3-requests
|
||||||
- python3-scapy
|
- python3-scapy
|
||||||
|
- python3-setuptools
|
||||||
|
- python3-smbus
|
||||||
- python3-smbus2
|
- python3-smbus2
|
||||||
- python3-spidev
|
- python3-spidev
|
||||||
- python3-tweepy
|
- python3-tweepy
|
||||||
- python3-werkzeug
|
- python3-werkzeug
|
||||||
- firmware-atheros
|
- python3-yaml
|
||||||
- firmware-brcm80211
|
|
||||||
- firmware-libertas
|
|
||||||
- firmware-misc-nonfree
|
|
||||||
- firmware-realtek
|
|
||||||
- python3-pip
|
|
||||||
- python3-setuptools
|
|
||||||
- python3-smbus
|
|
||||||
- qpdf
|
- qpdf
|
||||||
- raspberrypi-kernel-headers
|
- raspberrypi-kernel-headers
|
||||||
- rsync
|
- rsync
|
||||||
@ -157,13 +168,11 @@
|
|||||||
- wl
|
- wl
|
||||||
- xxd
|
- xxd
|
||||||
- zlib1g-dev
|
- zlib1g-dev
|
||||||
- zram-tools
|
|
||||||
environment:
|
environment:
|
||||||
ARCHFLAGS: "-arch aarch64"
|
ARCHFLAGS: "-arch aarch64"
|
||||||
QEMU_UNAME: "{{ kernel.full }}"
|
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
# First we install and remove unnecessary packages
|
# First we install packages
|
||||||
- name: install packages
|
- name: install packages
|
||||||
apt:
|
apt:
|
||||||
name: "{{ packages.apt.install }}"
|
name: "{{ packages.apt.install }}"
|
||||||
@ -171,13 +180,6 @@
|
|||||||
update_cache: yes
|
update_cache: yes
|
||||||
install_recommends: false
|
install_recommends: false
|
||||||
|
|
||||||
- name: remove unnecessary apt packages
|
|
||||||
apt:
|
|
||||||
name: "{{ packages.apt.remove }}"
|
|
||||||
state: absent
|
|
||||||
purge: yes
|
|
||||||
register: removed
|
|
||||||
|
|
||||||
# Now we set up /boot/firmware
|
# Now we set up /boot/firmware
|
||||||
- name: Create pi user
|
- name: Create pi user
|
||||||
copy:
|
copy:
|
||||||
@ -271,35 +273,116 @@
|
|||||||
dest: /usr/local/lib/libpcap.so.0.8
|
dest: /usr/local/lib/libpcap.so.0.8
|
||||||
state: link
|
state: link
|
||||||
|
|
||||||
# Install nexmon to fix wireless scanning (takes 2.5G of space)
|
# install latest hcxtools
|
||||||
|
|
||||||
|
- name: clone hcxtools
|
||||||
|
git:
|
||||||
|
repo: https://github.com/ZerBea/hcxtools.git
|
||||||
|
dest: /usr/local/src/hcxtools
|
||||||
|
|
||||||
|
- name: install hcxtools
|
||||||
|
shell: "make && make install"
|
||||||
|
args:
|
||||||
|
executable: /bin/bash
|
||||||
|
chdir: /usr/local/src/hcxtools
|
||||||
|
|
||||||
|
- name: remove hcxtools directory
|
||||||
|
file:
|
||||||
|
state: absent
|
||||||
|
path: /usr/local/src/hcxtools
|
||||||
|
|
||||||
- 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
|
||||||
|
|
||||||
- name: make firmware
|
# FIRST WE BUILD DRIVER FOR RPi5
|
||||||
|
|
||||||
|
- name: make firmware, RPi5
|
||||||
shell: "source ./setup_env.sh && make"
|
shell: "source ./setup_env.sh && make"
|
||||||
args:
|
args:
|
||||||
executable: /bin/bash
|
executable: /bin/bash
|
||||||
chdir: /usr/local/src/nexmon/
|
chdir: /usr/local/src/nexmon/
|
||||||
|
environment:
|
||||||
|
QEMU_UNAME: "{{ kernel.full_pi5 }}"
|
||||||
|
ARCHFLAGS: "-arch aarch64"
|
||||||
|
|
||||||
- name: make firmware patch (bcm43455c0)
|
- name: make firmware patch (bcm43455c0), RPi5
|
||||||
shell: "source ./setup_env.sh && cd /usr/local/src/nexmon/patches/bcm43455c0/7_45_206/nexmon/ && make"
|
shell: "source ./setup_env.sh && cd /usr/local/src/nexmon/patches/bcm43455c0/7_45_206/nexmon/ && make"
|
||||||
args:
|
args:
|
||||||
executable: /bin/bash
|
executable: /bin/bash
|
||||||
chdir: /usr/local/src/nexmon/
|
chdir: /usr/local/src/nexmon/
|
||||||
|
environment:
|
||||||
|
QEMU_UNAME: "{{ kernel.full_pi5 }}"
|
||||||
|
ARCHFLAGS: "-arch aarch64"
|
||||||
|
|
||||||
- name: install new firmware (bcm43455c0)
|
- name: copy modified driver, RPi5
|
||||||
|
copy:
|
||||||
|
src: "/usr/local/src/nexmon/patches/driver/brcmfmac_{{ kernel.min }}.y-nexmon/brcmfmac.ko"
|
||||||
|
dest: "/usr/lib/modules/{{ kernel.full_pi5 }}/kernel/drivers/net/wireless/broadcom/brcm80211/brcmfmac/brcmfmac.ko"
|
||||||
|
environment:
|
||||||
|
QEMU_UNAME: "{{ kernel.full_pi5 }}"
|
||||||
|
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
|
||||||
|
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"
|
||||||
|
|
||||||
|
- name: load brcmfmac drivers
|
||||||
|
command: "/sbin/depmod {{ kernel.full_pi5 }}"
|
||||||
|
environment:
|
||||||
|
QEMU_UNAME: "{{ kernel.full_pi5 }}"
|
||||||
|
|
||||||
|
- name: Delete nexmon content & directory
|
||||||
|
file:
|
||||||
|
state: absent
|
||||||
|
path: /usr/local/src/nexmon/
|
||||||
|
|
||||||
|
# NOW WE BUILD DRIVERS FOR RPi4, RPizero2w and RPi3
|
||||||
|
|
||||||
|
- name: clone nexmon repository
|
||||||
|
git:
|
||||||
|
repo: https://github.com/DrSchottky/nexmon.git
|
||||||
|
dest: /usr/local/src/nexmon
|
||||||
|
|
||||||
|
- name: make firmware, RPi4
|
||||||
|
shell: "source ./setup_env.sh && make"
|
||||||
|
args:
|
||||||
|
executable: /bin/bash
|
||||||
|
chdir: /usr/local/src/nexmon/
|
||||||
|
environment:
|
||||||
|
QEMU_UNAME: "{{ kernel.full }}"
|
||||||
|
ARCHFLAGS: "-arch aarch64"
|
||||||
|
|
||||||
|
- name: make firmware patch (bcm43455c0), RPi4
|
||||||
|
shell: "source ./setup_env.sh && cd /usr/local/src/nexmon/patches/bcm43455c0/7_45_206/nexmon/ && make"
|
||||||
|
args:
|
||||||
|
executable: /bin/bash
|
||||||
|
chdir: /usr/local/src/nexmon/
|
||||||
|
environment:
|
||||||
|
QEMU_UNAME: "{{ kernel.full }}"
|
||||||
|
ARCHFLAGS: "-arch aarch64"
|
||||||
|
|
||||||
|
- name: install new firmware (bcm43455c0), RPi4 RPi5
|
||||||
copy:
|
copy:
|
||||||
src: /usr/local/src/nexmon/patches/bcm43455c0/7_45_206/nexmon/brcmfmac43455-sdio.bin
|
src: /usr/local/src/nexmon/patches/bcm43455c0/7_45_206/nexmon/brcmfmac43455-sdio.bin
|
||||||
dest: /usr/lib/firmware/brcm/brcmfmac43455-sdio.bin
|
dest: /usr/lib/firmware/brcm/brcmfmac43455-sdio.bin
|
||||||
follow: true
|
follow: true
|
||||||
|
|
||||||
|
# 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:
|
||||||
executable: /bin/bash
|
executable: /bin/bash
|
||||||
chdir: /usr/local/src/nexmon/
|
chdir: /usr/local/src/nexmon/
|
||||||
|
environment:
|
||||||
|
QEMU_UNAME: "{{ kernel.full }}"
|
||||||
|
ARCHFLAGS: "-arch aarch64"
|
||||||
|
|
||||||
- name: install new firmware (bcm43436b0)
|
- name: install new firmware (bcm43436b0)
|
||||||
copy:
|
copy:
|
||||||
@ -312,6 +395,17 @@
|
|||||||
args:
|
args:
|
||||||
executable: /bin/bash
|
executable: /bin/bash
|
||||||
chdir: /usr/local/src/nexmon/
|
chdir: /usr/local/src/nexmon/
|
||||||
|
environment:
|
||||||
|
QEMU_UNAME: "{{ kernel.full }}"
|
||||||
|
ARCHFLAGS: "-arch aarch64"
|
||||||
|
|
||||||
|
- name: copy modified driver, RPi4
|
||||||
|
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 aarch64"
|
||||||
|
|
||||||
- name: install new firmware (bcm43430a1)
|
- name: install new firmware (bcm43430a1)
|
||||||
copy:
|
copy:
|
||||||
@ -342,13 +436,10 @@
|
|||||||
- 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"
|
||||||
|
|
||||||
- name: copy modified driver
|
- name: load brcmfmac drivers
|
||||||
copy:
|
command: "/sbin/depmod {{ kernel.full }}"
|
||||||
src: "/usr/local/src/nexmon/patches/driver/brcmfmac_{{ kernel.min }}.y-nexmon/brcmfmac.ko"
|
environment:
|
||||||
dest: "/usr/lib/modules/{{ kernel.full }}/kernel/drivers/net/wireless/broadcom/brcm80211/brcmfmac/brcmfmac.ko"
|
QEMU_UNAME: "{{ kernel.full }}"
|
||||||
|
|
||||||
- name : load brcmfmac drivers
|
|
||||||
command: "/sbin/depmod -a"
|
|
||||||
|
|
||||||
# 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
|
||||||
@ -368,7 +459,7 @@
|
|||||||
|
|
||||||
- name: clone pwnagotchi repository
|
- name: clone pwnagotchi repository
|
||||||
git:
|
git:
|
||||||
repo: https://github.com/jayofelony/pwnagotchi-bookworm.git
|
repo: https://github.com/jayofelony/pwnagotchi.git
|
||||||
dest: /usr/local/src/pwnagotchi
|
dest: /usr/local/src/pwnagotchi
|
||||||
|
|
||||||
- name: build pwnagotchi wheel
|
- name: build pwnagotchi wheel
|
||||||
@ -423,7 +514,7 @@
|
|||||||
repo: "{{ packages.bettercap.source }}"
|
repo: "{{ packages.bettercap.source }}"
|
||||||
dest: /usr/local/src/bettercap
|
dest: /usr/local/src/bettercap
|
||||||
|
|
||||||
- name: install bettercap 2.32.2
|
- 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"
|
shell: "export GOPATH=$HOME/go && export PATH=/usr/local/go/bin:$PATH:$GOPATH/bin && go mod tidy && make && make install"
|
||||||
args:
|
args:
|
||||||
executable: /bin/bash
|
executable: /bin/bash
|
||||||
@ -476,11 +567,6 @@
|
|||||||
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
|
||||||
@ -509,7 +595,7 @@
|
|||||||
- name: Add pwnlog alias
|
- name: Add pwnlog alias
|
||||||
lineinfile:
|
lineinfile:
|
||||||
dest: /home/pi/.bashrc
|
dest: /home/pi/.bashrc
|
||||||
line: "\nalias pwnlog='tail -f -n300 /home/pi/logs/pwn*.log | sed --unbuffered \"s/,[[:digit:]]\\{3\\}\\]//g\" | cut -d \" \" -f 2-'"
|
line: "\nalias pwnlog='tail -f -n300 /etc/pwnagotchi/log/pwn*.log | sed --unbuffered \"s/,[[:digit:]]\\{3\\}\\]//g\" | cut -d \" \" -f 2-'"
|
||||||
insertafter: EOF
|
insertafter: EOF
|
||||||
|
|
||||||
- name: Add pwnver alias
|
- name: Add pwnver alias
|
||||||
@ -557,11 +643,6 @@
|
|||||||
group: pi
|
group: pi
|
||||||
recurse: true
|
recurse: true
|
||||||
|
|
||||||
- name: clean apt cache
|
|
||||||
apt:
|
|
||||||
autoclean: true
|
|
||||||
when: removed.changed
|
|
||||||
|
|
||||||
- name: remove pre-collected packages zip
|
- name: remove pre-collected packages zip
|
||||||
file:
|
file:
|
||||||
path: /root/go_pkgs.tgz
|
path: /root/go_pkgs.tgz
|
||||||
@ -582,11 +663,6 @@
|
|||||||
state: absent
|
state: absent
|
||||||
path: /root/.cache/pip
|
path: /root/.cache/pip
|
||||||
|
|
||||||
- name: remove dependencies that are no longer required
|
|
||||||
apt:
|
|
||||||
autoremove: yes
|
|
||||||
when: removed.changed
|
|
||||||
|
|
||||||
- name: remove ssh keys
|
- name: remove ssh keys
|
||||||
file:
|
file:
|
||||||
state: absent
|
state: absent
|
||||||
@ -599,6 +675,24 @@
|
|||||||
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: clean apt cache
|
||||||
|
apt:
|
||||||
|
autoclean: true
|
||||||
|
when: removed.changed
|
||||||
|
|
||||||
handlers:
|
handlers:
|
||||||
- name: reload systemd services
|
- name: reload systemd services
|
||||||
systemd:
|
systemd:
|
0
builder/data/64bit/root/client_secrets.json
Normal file
0
builder/data/64bit/root/client_secrets.json
Normal file
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
|
19
builder/data/64bit/usr/bin/bettercap-launcher
Executable file
19
builder/data/64bit/usr/bin/bettercap-launcher
Executable file
@ -0,0 +1,19 @@
|
|||||||
|
#!/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
|
||||||
|
|
||||||
|
# start mon0
|
||||||
|
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
|
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
|
@ -13,14 +13,6 @@ blink_led() {
|
|||||||
sleep 0.3
|
sleep 0.3
|
||||||
}
|
}
|
||||||
|
|
||||||
# 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
|
@ -1,15 +0,0 @@
|
|||||||
# Specifies amount of zram devices to create.
|
|
||||||
# By default, zramswap-start will use all available cores.
|
|
||||||
#CORES=1
|
|
||||||
|
|
||||||
# Specifies the amount of RAM that should be used for zram
|
|
||||||
# based on a percentage the total amount of available memory
|
|
||||||
PERCENTAGE=60
|
|
||||||
|
|
||||||
# Specifies a static amount of RAM that should be used for
|
|
||||||
# the ZRAM devices, this is in MiB
|
|
||||||
#ALLOCATION=256
|
|
||||||
|
|
||||||
# Specifies the priority for the swap devices, see swapon(2)
|
|
||||||
# for more details.
|
|
||||||
#PRIORITY=100
|
|
@ -1 +1 @@
|
|||||||
__version__ = '2.7.6'
|
__version__ = '2.8.5'
|
||||||
|
@ -21,17 +21,17 @@ RECOVERY_DATA_FILE = '/root/.pwnagotchi-recovery'
|
|||||||
|
|
||||||
class Agent(Client, Automata, AsyncAdvertiser, AsyncTrainer):
|
class Agent(Client, Automata, AsyncAdvertiser, AsyncTrainer):
|
||||||
def __init__(self, view, config, keypair):
|
def __init__(self, view, config, keypair):
|
||||||
Client.__init__(self, config['bettercap']['hostname'],
|
Client.__init__(self,
|
||||||
config['bettercap']['scheme'],
|
"127.0.0.1" if "hostname" not in config['bettercap'] else config['bettercap']['hostname'],
|
||||||
config['bettercap']['port'],
|
"http" if "scheme" not in config['bettercap'] else config['bettercap']['scheme'],
|
||||||
config['bettercap']['username'],
|
8081 if "port" not in config['bettercap'] else config['bettercap']['port'],
|
||||||
config['bettercap']['password'])
|
"pwnagotchi" if "username" not in config['bettercap'] else config['bettercap']['username'],
|
||||||
|
"pwnagotchi" if "password" not in config['bettercap'] else config['bettercap']['password'])
|
||||||
Automata.__init__(self, config, view)
|
Automata.__init__(self, config, view)
|
||||||
AsyncAdvertiser.__init__(self, config, view, keypair)
|
AsyncAdvertiser.__init__(self, config, view, keypair)
|
||||||
AsyncTrainer.__init__(self, config)
|
AsyncTrainer.__init__(self, config)
|
||||||
|
|
||||||
self._started_at = time.time()
|
self._started_at = time.time()
|
||||||
self._filter = None if not config['main']['filter'] else re.compile(config['main']['filter'])
|
|
||||||
self._current_channel = 0
|
self._current_channel = 0
|
||||||
self._tot_aps = 0
|
self._tot_aps = 0
|
||||||
self._aps_on_channel = 0
|
self._aps_on_channel = 0
|
||||||
@ -164,11 +164,6 @@ class Agent(Client, Automata, AsyncAdvertiser, AsyncTrainer):
|
|||||||
|
|
||||||
self.wait_for(recon_time, sleeping=False)
|
self.wait_for(recon_time, sleeping=False)
|
||||||
|
|
||||||
def _filter_included(self, ap):
|
|
||||||
return self._filter is None or \
|
|
||||||
self._filter.match(ap['hostname']) is not None or \
|
|
||||||
self._filter.match(ap['mac']) is not None
|
|
||||||
|
|
||||||
def set_access_points(self, aps):
|
def set_access_points(self, aps):
|
||||||
self._access_points = aps
|
self._access_points = aps
|
||||||
plugins.on('wifi_update', self, aps)
|
plugins.on('wifi_update', self, aps)
|
||||||
@ -184,13 +179,10 @@ class Agent(Client, Automata, AsyncAdvertiser, AsyncTrainer):
|
|||||||
for ap in s['wifi']['aps']:
|
for ap in s['wifi']['aps']:
|
||||||
if ap['encryption'] == '' or ap['encryption'] == 'OPEN':
|
if ap['encryption'] == '' or ap['encryption'] == 'OPEN':
|
||||||
continue
|
continue
|
||||||
elif ap['hostname'] in whitelist or ap['mac'][:8].lower() in whitelist:
|
elif ap['hostname'] in whitelist or ap['mac'][:13].lower() in whitelist or ap['mac'].lower() in whitelist:
|
||||||
continue
|
continue
|
||||||
elif ap['hostname'] not in whitelist \
|
else:
|
||||||
and ap['mac'].lower() not in whitelist \
|
aps.append(ap)
|
||||||
and ap['mac'][:8].lower() not in whitelist:
|
|
||||||
if self._filter_included(ap):
|
|
||||||
aps.append(ap)
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.exception("Error while getting access points (%s)", e)
|
logging.exception("Error while getting access points (%s)", e)
|
||||||
|
|
||||||
@ -277,6 +269,10 @@ class Agent(Client, Automata, AsyncAdvertiser, AsyncTrainer):
|
|||||||
self._save_recovery_data()
|
self._save_recovery_data()
|
||||||
pwnagotchi.reboot()
|
pwnagotchi.reboot()
|
||||||
|
|
||||||
|
def _restart(self):
|
||||||
|
self._save_recovery_data()
|
||||||
|
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)
|
||||||
with open(RECOVERY_DATA_FILE, 'w') as fp:
|
with open(RECOVERY_DATA_FILE, 'w') as fp:
|
||||||
@ -429,7 +425,6 @@ class Agent(Client, Automata, AsyncAdvertiser, AsyncTrainer):
|
|||||||
if self.is_stale():
|
if self.is_stale():
|
||||||
logging.debug("recon is stale, skipping assoc(%s)", ap['mac'])
|
logging.debug("recon is stale, skipping assoc(%s)", ap['mac'])
|
||||||
return
|
return
|
||||||
|
|
||||||
if throttle == -1 and "throttle_a" in self._config['personality']:
|
if throttle == -1 and "throttle_a" in self._config['personality']:
|
||||||
throttle = self._config['personality']['throttle_a']
|
throttle = self._config['personality']['throttle_a']
|
||||||
|
|
||||||
|
@ -23,6 +23,12 @@ def load(config, agent, epoch, from_disk=True):
|
|||||||
from stable_baselines3 import A2C
|
from stable_baselines3 import A2C
|
||||||
logging.debug("[AI] A2C imported in %.2fs" % (time.time() - start))
|
logging.debug("[AI] A2C imported in %.2fs" % (time.time() - start))
|
||||||
|
|
||||||
|
# remove invalid ai.parameters leftover from tensor_flow, if present
|
||||||
|
for key in [ 'alpha', 'epsilon', 'lr_schedule' ]:
|
||||||
|
if key in config['params']:
|
||||||
|
logging.info("Removing legacy ai parameter %s" % key);
|
||||||
|
del config['params'][key]
|
||||||
|
|
||||||
start = time.time()
|
start = time.time()
|
||||||
from stable_baselines3.a2c import MlpPolicy
|
from stable_baselines3.a2c import MlpPolicy
|
||||||
logging.debug("[AI] MlpPolicy imported in %.2fs" % (time.time() - start))
|
logging.debug("[AI] MlpPolicy imported in %.2fs" % (time.time() - start))
|
||||||
|
@ -138,6 +138,6 @@ class Automata(object):
|
|||||||
plugins.on('epoch', self, self._epoch.epoch - 1, self._epoch.data())
|
plugins.on('epoch', self, self._epoch.epoch - 1, self._epoch.data())
|
||||||
|
|
||||||
if self._epoch.blind_for >= self._config['main']['mon_max_blind_epochs']:
|
if self._epoch.blind_for >= self._config['main']['mon_max_blind_epochs']:
|
||||||
logging.critical("%d epochs without visible access points -> rebooting ...", self._epoch.blind_for)
|
logging.critical("%d epochs without visible access points -> restarting ...", self._epoch.blind_for)
|
||||||
self._reboot()
|
self._restart()
|
||||||
self._epoch.blind_for = 0
|
self._epoch.blind_for = 0
|
||||||
|
@ -1,5 +1,11 @@
|
|||||||
main.name = "pwnagotchi"
|
main.name = "pwnagotchi"
|
||||||
main.lang = "en"
|
main.lang = "en"
|
||||||
|
main.whitelist = [
|
||||||
|
"EXAMPLE_NETWORK",
|
||||||
|
"ANOTHER_EXAMPLE_NETWORK",
|
||||||
|
"fo:od:ba:be:fo:od",
|
||||||
|
"fo:od:ba"
|
||||||
|
]
|
||||||
main.confd = "/etc/pwnagotchi/conf.d/"
|
main.confd = "/etc/pwnagotchi/conf.d/"
|
||||||
main.custom_plugin_repos = [
|
main.custom_plugin_repos = [
|
||||||
"https://github.com/jayofelony/pwnagotchi-torch-plugins/archive/master.zip",
|
"https://github.com/jayofelony/pwnagotchi-torch-plugins/archive/master.zip",
|
||||||
@ -18,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 = false
|
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 = false
|
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
|
||||||
|
|
||||||
@ -55,9 +64,6 @@ main.plugins.gps.device = "/dev/ttyUSB0" # for GPSD: "localhost:2947"
|
|||||||
|
|
||||||
main.plugins.grid.enabled = true
|
main.plugins.grid.enabled = true
|
||||||
main.plugins.grid.report = true
|
main.plugins.grid.report = true
|
||||||
main.plugins.grid.exclude = [
|
|
||||||
"YourHomeNetworkHere"
|
|
||||||
]
|
|
||||||
|
|
||||||
main.plugins.logtail.enabled = false
|
main.plugins.logtail.enabled = false
|
||||||
main.plugins.logtail.max-lines = 10000
|
main.plugins.logtail.max-lines = 10000
|
||||||
@ -73,10 +79,6 @@ main.plugins.onlinehashcrack.enabled = false
|
|||||||
main.plugins.onlinehashcrack.email = ""
|
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.onlinehashcrack.whitelist = []
|
|
||||||
|
|
||||||
main.plugins.paw-gps.enabled = false
|
|
||||||
main.plugins.paw-gps.ip = "192.168.44.1:8080"
|
|
||||||
|
|
||||||
main.plugins.pisugar2.enabled = false
|
main.plugins.pisugar2.enabled = false
|
||||||
main.plugins.pisugar2.shutdown = 5
|
main.plugins.pisugar2.shutdown = 5
|
||||||
@ -100,29 +102,22 @@ main.plugins.webgpsmap.enabled = false
|
|||||||
|
|
||||||
main.plugins.wigle.enabled = false
|
main.plugins.wigle.enabled = false
|
||||||
main.plugins.wigle.api_key = ""
|
main.plugins.wigle.api_key = ""
|
||||||
main.plugins.wigle.whitelist = []
|
main.plugins.wigle.donate = false
|
||||||
main.plugins.wigle.donate = true
|
|
||||||
|
|
||||||
main.plugins.wpa-sec.enabled = false
|
main.plugins.wpa-sec.enabled = false
|
||||||
main.plugins.wpa-sec.api_key = ""
|
main.plugins.wpa-sec.api_key = ""
|
||||||
main.plugins.wpa-sec.api_url = "https://wpa-sec.stanev.org"
|
main.plugins.wpa-sec.api_url = "https://wpa-sec.stanev.org"
|
||||||
main.plugins.wpa-sec.download_results = false
|
main.plugins.wpa-sec.download_results = false
|
||||||
main.plugins.wpa-sec.whitelist = []
|
|
||||||
|
|
||||||
main.iface = "wlan0mon"
|
main.iface = "wlan0mon"
|
||||||
main.mon_start_cmd = "/usr/bin/monstart"
|
main.mon_start_cmd = "/usr/bin/monstart"
|
||||||
main.mon_stop_cmd = "/usr/bin/monstop"
|
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.whitelist = [
|
|
||||||
"EXAMPLE_NETWORK",
|
|
||||||
"ANOTHER_EXAMPLE_NETWORK",
|
|
||||||
"fo:od:ba:be:fo:od",
|
|
||||||
"fo:od:ba"
|
|
||||||
]
|
|
||||||
main.filter = ""
|
main.filter = ""
|
||||||
|
|
||||||
main.log.path = "/home/pi/logs/pwnagotchi.log"
|
main.log.path = "/etc/pwnagotchi/log/pwnagotchi.log"
|
||||||
main.log.rotation.enabled = true
|
main.log.rotation.enabled = true
|
||||||
main.log.rotation.size = "10M"
|
main.log.rotation.size = "10M"
|
||||||
|
|
||||||
@ -160,6 +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
|
||||||
|
|
||||||
|
personality.clear_on_exit = true # clear display when shutting down cleanly
|
||||||
|
|
||||||
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
|
||||||
@ -189,6 +186,9 @@ ui.faces.debug = "(#__#)"
|
|||||||
ui.faces.upload = "(1__0)"
|
ui.faces.upload = "(1__0)"
|
||||||
ui.faces.upload1 = "(1__1)"
|
ui.faces.upload1 = "(1__1)"
|
||||||
ui.faces.upload2 = "(0__1)"
|
ui.faces.upload2 = "(0__1)"
|
||||||
|
ui.faces.png = false
|
||||||
|
ui.faces.position_x = 0
|
||||||
|
ui.faces.position_y = 34
|
||||||
|
|
||||||
ui.web.enabled = true
|
ui.web.enabled = true
|
||||||
ui.web.address = "::" # listening on both ipv4 and ipv6 - switch to 0.0.0.0 to listen on just ipv4
|
ui.web.address = "::" # listening on both ipv4 and ipv6 - switch to 0.0.0.0 to listen on just ipv4
|
||||||
@ -202,11 +202,6 @@ ui.display.enabled = false
|
|||||||
ui.display.rotation = 180
|
ui.display.rotation = 180
|
||||||
ui.display.type = "waveshare_4"
|
ui.display.type = "waveshare_4"
|
||||||
|
|
||||||
bettercap.scheme = "http"
|
|
||||||
bettercap.hostname = "localhost"
|
|
||||||
bettercap.port = 8081
|
|
||||||
bettercap.username = "pwnagotchi"
|
|
||||||
bettercap.password = "pwnagotchi"
|
|
||||||
bettercap.handshakes = "/root/handshakes"
|
bettercap.handshakes = "/root/handshakes"
|
||||||
bettercap.silence = [
|
bettercap.silence = [
|
||||||
"ble.device.new",
|
"ble.device.new",
|
||||||
@ -225,7 +220,7 @@ bettercap.silence = [
|
|||||||
|
|
||||||
fs.memory.enabled = true
|
fs.memory.enabled = true
|
||||||
fs.memory.mounts.log.enabled = true
|
fs.memory.mounts.log.enabled = true
|
||||||
fs.memory.mounts.log.mount = "/home/pi/logs"
|
fs.memory.mounts.log.mount = "/etc/pwnagotchi/log/"
|
||||||
fs.memory.mounts.log.size = "50M"
|
fs.memory.mounts.log.size = "50M"
|
||||||
fs.memory.mounts.log.sync = 60
|
fs.memory.mounts.log.sync = 60
|
||||||
fs.memory.mounts.log.zram = true
|
fs.memory.mounts.log.zram = true
|
||||||
|
@ -89,7 +89,7 @@ def update_data(last_session):
|
|||||||
'uname': subprocess.getoutput("uname -a"),
|
'uname': subprocess.getoutput("uname -a"),
|
||||||
'brain': brain,
|
'brain': brain,
|
||||||
'version': pwnagotchi.__version__,
|
'version': pwnagotchi.__version__,
|
||||||
'build': "Pwnagotchi-Torch by Jayofelony",
|
'build': "Pwnagotchi by Jayofelony",
|
||||||
'plugins': enabled,
|
'plugins': enabled,
|
||||||
'language': language,
|
'language': language,
|
||||||
'bettercap': subprocess.getoutput("bettercap -version").split(".\n\n")[1],
|
'bettercap': subprocess.getoutput("bettercap -version").split(".\n\n")[1],
|
||||||
|
Binary file not shown.
@ -1,14 +1,13 @@
|
|||||||
# SOME DESCRIPTIVE TITLE.
|
# Pwnagotchi display English to Esperanto.
|
||||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||||
# This file is distributed under the same license as the PACKAGE package.
|
# This file is distributed under the same license as the PACKAGE package.
|
||||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
# FIRST AUTHOR MADE THIS IN YEAR 2024.
|
||||||
#
|
#
|
||||||
|
|
||||||
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: 2023-11-16 21:10+0100\n"
|
"POT-Creation-Date: 2024-01-25 23:40+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 <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
@ -18,218 +17,219 @@ msgstr ""
|
|||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
|
||||||
msgid "ZzzzZZzzzzZzzz"
|
msgid "ZzzzZZzzzzZzzz"
|
||||||
msgstr ""
|
msgstr "ZzzzZZzzzzZzzz"
|
||||||
|
|
||||||
msgid "Hi, I'm Pwnagotchi! Starting ..."
|
msgid "Hi, I'm Pwnagotchi! Starting ..."
|
||||||
msgstr ""
|
msgstr "Sal, mi estas Pwnagotchi! Komencante…"
|
||||||
|
|
||||||
msgid "New day, new hunt, new pwns!"
|
msgid "New day, new hunt, new pwns!"
|
||||||
msgstr ""
|
msgstr "Nova tago, nova ĉaso, nova wifi!"
|
||||||
|
|
||||||
msgid "Hack the Planet!"
|
msgid "Hack the Planet!"
|
||||||
msgstr ""
|
msgstr "Eniru la elektronikon!"
|
||||||
|
|
||||||
msgid "AI ready."
|
msgid "AI ready."
|
||||||
msgstr ""
|
msgstr "Mi pretas."
|
||||||
|
|
||||||
msgid "The neural network is ready."
|
msgid "The neural network is ready."
|
||||||
msgstr ""
|
msgstr "La elektronika reto estas preta."
|
||||||
|
|
||||||
msgid "Generating keys, do not turn off ..."
|
msgid "Generating keys, do not turn off ..."
|
||||||
msgstr ""
|
msgstr "Mi generas ŝlosilojn, ne malŝaltu min!"
|
||||||
|
|
||||||
#, 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 ""
|
msgstr "Hej, kanalo {channel} disponeblas! Via alirpunkto dankos vin"
|
||||||
|
|
||||||
msgid "Reading last session logs ..."
|
msgid "Reading last session logs ..."
|
||||||
msgstr ""
|
msgstr "Legante protokolojn de antaŭa sesio…"
|
||||||
|
|
||||||
#, 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 ""
|
msgstr "legi {lines_so_far} liniojn ĝis nun…"
|
||||||
|
|
||||||
msgid "I'm bored ..."
|
msgid "I'm bored ..."
|
||||||
msgstr ""
|
msgstr "Mi enuas…"
|
||||||
|
|
||||||
msgid "Let's go for a walk!"
|
msgid "Let's go for a walk!"
|
||||||
msgstr ""
|
msgstr "Ni iru promeni!"
|
||||||
|
|
||||||
msgid "This is the best day of my life!"
|
msgid "This is the best day of my life!"
|
||||||
msgstr ""
|
msgstr "Plej bona tago de mia vivo!"
|
||||||
|
|
||||||
msgid "Shitty day :/"
|
msgid "Shitty day :/"
|
||||||
msgstr ""
|
msgstr "Terura tago :/"
|
||||||
|
|
||||||
msgid "I'm extremely bored ..."
|
msgid "I'm extremely bored ..."
|
||||||
msgstr ""
|
msgstr "mi estas tre enuigita…"
|
||||||
|
|
||||||
msgid "I'm very sad ..."
|
msgid "I'm very sad ..."
|
||||||
msgstr ""
|
msgstr "Mi estas tre malĝoja…"
|
||||||
|
|
||||||
msgid "I'm sad"
|
msgid "I'm sad"
|
||||||
msgstr ""
|
msgstr "Mi estas malfeliĉa"
|
||||||
|
|
||||||
msgid "Leave me alone ..."
|
msgid "Leave me alone ..."
|
||||||
msgstr ""
|
msgstr "Lasu min sola…"
|
||||||
|
|
||||||
msgid "I'm mad at you!"
|
msgid "I'm mad at you!"
|
||||||
msgstr ""
|
msgstr "Mi koleras kontraŭ vi!"
|
||||||
|
|
||||||
msgid "I'm living the life!"
|
msgid "I'm living the life!"
|
||||||
msgstr ""
|
msgstr "Mi ĝuas la vivon!"
|
||||||
|
|
||||||
msgid "I pwn therefore I am."
|
msgid "I pwn therefore I am."
|
||||||
msgstr ""
|
msgstr "Mi eniras tial mi estas."
|
||||||
|
|
||||||
msgid "So many networks!!!"
|
msgid "So many networks!!!"
|
||||||
msgstr ""
|
msgstr "Tiom da ludiloj!"
|
||||||
|
|
||||||
msgid "I'm having so much fun!"
|
msgid "I'm having so much fun!"
|
||||||
msgstr ""
|
msgstr "Mi tre amuzas!"
|
||||||
|
|
||||||
msgid "My crime is that of curiosity ..."
|
msgid "My crime is that of curiosity ..."
|
||||||
msgstr ""
|
msgstr "Scivolemo estas mia krimo…"
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Hello {name}! Nice to meet you."
|
msgid "Hello {name}! Nice to meet you."
|
||||||
msgstr ""
|
msgstr "Sal {name}! Mi ĝojas renkonti vin."
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Yo {name}! Sup?"
|
msgid "Yo {name}! Sup?"
|
||||||
msgstr ""
|
msgstr "Hej {name}! Sal?"
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Hey {name} how are you doing?"
|
msgid "Hey {name} how are you doing?"
|
||||||
msgstr ""
|
msgstr "Sal {name}! Kiel vi fartas?"
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Unit {name} is nearby!"
|
msgid "Unit {name} is nearby!"
|
||||||
msgstr ""
|
msgstr "Iu estas proksime! Ĝia nomo estas {name}."
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Uhm ... goodbye {name}"
|
msgid "Uhm ... goodbye {name}"
|
||||||
msgstr ""
|
msgstr "Adiaŭ {name}"
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "{name} is gone ..."
|
msgid "{name} is gone ..."
|
||||||
msgstr ""
|
msgstr "{name} malaperis…"
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Whoops ... {name} is gone."
|
msgid "Whoops ... {name} is gone."
|
||||||
msgstr ""
|
msgstr "Hups… {name} malaperis…"
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "{name} missed!"
|
msgid "{name} missed!"
|
||||||
msgstr ""
|
msgstr "{name} mankis!"
|
||||||
|
|
||||||
msgid "Missed!"
|
msgid "Missed!"
|
||||||
msgstr ""
|
msgstr "Maltrafis!"
|
||||||
|
|
||||||
msgid "Good friends are a blessing!"
|
msgid "Good friends are a blessing!"
|
||||||
msgstr ""
|
msgstr "Bonaj amikoj estas beno!"
|
||||||
|
|
||||||
msgid "I love my friends!"
|
msgid "I love my friends!"
|
||||||
msgstr ""
|
msgstr "Mi amas miajn amikojn!"
|
||||||
|
|
||||||
msgid "Nobody wants to play with me ..."
|
msgid "Nobody wants to play with me ..."
|
||||||
msgstr ""
|
msgstr "Neniu volas ludi kun mi..."
|
||||||
|
|
||||||
msgid "I feel so alone ..."
|
msgid "I feel so alone ..."
|
||||||
msgstr ""
|
msgstr "Mi estas tiel sola..."
|
||||||
|
|
||||||
msgid "Where's everybody?!"
|
msgid "Where's everybody?!"
|
||||||
msgstr ""
|
msgstr "KIE ĈIUJ ESTAS?!"
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Napping for {secs}s ..."
|
msgid "Napping for {secs}s ..."
|
||||||
msgstr ""
|
msgstr "Dormeto por {sec}j…"
|
||||||
|
|
||||||
msgid "Zzzzz"
|
msgid "Zzzzz"
|
||||||
msgstr ""
|
msgstr "Zzzzz"
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "ZzzZzzz ({secs}s)"
|
msgid "ZzzZzzz ({secs}s)"
|
||||||
msgstr ""
|
msgstr "ZzzZzzz ({sec}j)"
|
||||||
|
|
||||||
msgid "Good night."
|
msgid "Good night."
|
||||||
msgstr ""
|
msgstr "Bonan nokton"
|
||||||
|
|
||||||
msgid "Zzz"
|
msgid "Zzz"
|
||||||
msgstr ""
|
msgstr "Zzz"
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Waiting for {secs}s ..."
|
msgid "Waiting for {secs}s ..."
|
||||||
msgstr ""
|
msgstr "Atendas {sec}j…"
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Looking around ({secs}s)"
|
msgid "Looking around ({secs}s)"
|
||||||
msgstr ""
|
msgstr "Ĉirkaŭrigardante ({sec}j)"
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Hey {what} let's be friends!"
|
msgid "Hey {what} let's be friends!"
|
||||||
msgstr ""
|
msgstr "Hej {what}, ni estu amikoj!"
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Associating to {what}"
|
msgid "Associating to {what}"
|
||||||
msgstr ""
|
msgstr "asociante al {what}"
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Yo {what}!"
|
msgid "Yo {what}!"
|
||||||
msgstr ""
|
msgstr "Hej {what}!"
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Just decided that {mac} needs no WiFi!"
|
msgid "Just decided that {mac} needs no WiFi!"
|
||||||
msgstr ""
|
msgstr "Ĵus decidis, ke {mac} ne bezonas konekton!"
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Deauthenticating {mac}"
|
msgid "Deauthenticating {mac}"
|
||||||
msgstr ""
|
msgstr "Malaŭtentigi {mac}"
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Kickbanning {mac}!"
|
msgid "Kickbanning {mac}!"
|
||||||
msgstr ""
|
msgstr "Forigante {mac}!"
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Cool, we got {num} new handshake{plural}!"
|
msgid "Cool, we got {num} new handshake{plural}!"
|
||||||
msgstr ""
|
msgstr "Mirinda, ni havas {num} novajn manpremojn!"
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "You have {count} new message{plural}!"
|
msgid "You have {count} new message{plural}!"
|
||||||
msgstr ""
|
msgstr "Vi nun havas {num} novajn mesaĝojn"
|
||||||
|
|
||||||
msgid "Oops, something went wrong ... Rebooting ..."
|
msgid "Oops, something went wrong ... Rebooting ..."
|
||||||
msgstr ""
|
msgstr "Lo okazis... Rekomencante…"
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Uploading data to {to} ..."
|
msgid "Uploading data to {to} ..."
|
||||||
msgstr ""
|
msgstr "alŝuti datumojn al {to}…"
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Downloading from {name} ..."
|
msgid "Downloading from {name} ..."
|
||||||
msgstr ""
|
msgstr "Elŝutu de {name}…"
|
||||||
|
|
||||||
#, python-brace-format
|
#, fuzzy, python-brace-format
|
||||||
msgid "Kicked {num} stations\n"
|
msgid "Kicked {num} stations\n"
|
||||||
msgstr ""
|
msgstr "Forigita de {num} stacioj"
|
||||||
|
|
||||||
|
#, fuzzy
|
||||||
msgid "Made >999 new friends\n"
|
msgid "Made >999 new friends\n"
|
||||||
msgstr ""
|
msgstr "Faris pli ol 999 novajn amikojn!"
|
||||||
|
|
||||||
#, python-brace-format
|
#, fuzzy, python-brace-format
|
||||||
msgid "Made {num} new friends\n"
|
msgid "Made {num} new friends\n"
|
||||||
msgstr ""
|
msgstr "faris (nombro) novajn amikojn"
|
||||||
|
|
||||||
#, python-brace-format
|
#, fuzzy, python-brace-format
|
||||||
msgid "Got {num} handshakes\n"
|
msgid "Got {num} handshakes\n"
|
||||||
msgstr ""
|
msgstr "Ricevis {num} novajn manpremojn"
|
||||||
|
|
||||||
msgid "Met 1 peer"
|
msgid "Met 1 peer"
|
||||||
msgstr ""
|
msgstr "Renkontita unu kolego"
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Met {num} peers"
|
msgid "Met {num} peers"
|
||||||
msgstr ""
|
msgstr "renkontitajn {num} kolegojn"
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid ""
|
msgid ""
|
||||||
@ -237,21 +237,24 @@ 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 ""
|
||||||
|
"Mi pwning dum {duration} kaj piedbatis {deauthed} klientojn!Mi ankaŭ "
|
||||||
|
"renkontis {associated} novajn amikojn kaj manĝis {handshakes} manpremojn!"
|
||||||
|
"#pwnagotchi #pwnlog #pwnlife #hacktheplanet #skynet"
|
||||||
|
|
||||||
msgid "hours"
|
msgid "hours"
|
||||||
msgstr ""
|
msgstr "horror"
|
||||||
|
|
||||||
msgid "minutes"
|
msgid "minutes"
|
||||||
msgstr ""
|
msgstr "minutoj"
|
||||||
|
|
||||||
msgid "seconds"
|
msgid "seconds"
|
||||||
msgstr ""
|
msgstr "sekundoj"
|
||||||
|
|
||||||
msgid "hour"
|
msgid "hour"
|
||||||
msgstr ""
|
msgstr "horo"
|
||||||
|
|
||||||
msgid "minute"
|
msgid "minute"
|
||||||
msgstr ""
|
msgstr "minuto"
|
||||||
|
|
||||||
msgid "second"
|
msgid "second"
|
||||||
msgstr ""
|
msgstr "dua"
|
||||||
|
Binary file not shown.
@ -36,18 +36,18 @@ msgid "The neural network is ready."
|
|||||||
msgstr "La red neuronal está lista."
|
msgstr "La red neuronal está lista."
|
||||||
|
|
||||||
msgid "Generating keys, do not turn off ..."
|
msgid "Generating keys, do not turn off ..."
|
||||||
msgstr ""
|
msgstr "Generando llaves, no me apagues ..."
|
||||||
|
|
||||||
#, 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 "¡Oye, el canal {channel} está libre! Tu AP lo agradecerá."
|
msgstr "¡Oye, el canal {channel} está libre! Tu AP lo agradecerá."
|
||||||
|
|
||||||
msgid "Reading last session logs ..."
|
msgid "Reading last session logs ..."
|
||||||
msgstr ""
|
msgstr "Leyendo los logs de la última sesión ..."
|
||||||
|
|
||||||
#, 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 ""
|
msgstr "Leyendo {lines_so_far} líneas de log hasta ahora ..."
|
||||||
|
|
||||||
msgid "I'm bored ..."
|
msgid "I'm bored ..."
|
||||||
msgstr "Estoy aburrido ..."
|
msgstr "Estoy aburrido ..."
|
||||||
@ -74,7 +74,7 @@ msgid "Leave me alone ..."
|
|||||||
msgstr "Me siento tan solo ..."
|
msgstr "Me siento tan solo ..."
|
||||||
|
|
||||||
msgid "I'm mad at you!"
|
msgid "I'm mad at you!"
|
||||||
msgstr ""
|
msgstr "Toy re enojado con vos!"
|
||||||
|
|
||||||
msgid "I'm living the life!"
|
msgid "I'm living the life!"
|
||||||
msgstr "¡Estoy viviendo la vida!"
|
msgstr "¡Estoy viviendo la vida!"
|
||||||
@ -97,11 +97,11 @@ msgstr "¡Hola {name}! Encantado de conocerte."
|
|||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Yo {name}! Sup?"
|
msgid "Yo {name}! Sup?"
|
||||||
msgstr ""
|
msgstr "Que onda {name}!?"
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Hey {name} how are you doing?"
|
msgid "Hey {name} how are you doing?"
|
||||||
msgstr ""
|
msgstr "Eh!, ¿Que haces {name}?"
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Unit {name} is nearby!"
|
msgid "Unit {name} is nearby!"
|
||||||
@ -127,10 +127,10 @@ msgid "Missed!"
|
|||||||
msgstr "¡Perdido!"
|
msgstr "¡Perdido!"
|
||||||
|
|
||||||
msgid "Good friends are a blessing!"
|
msgid "Good friends are a blessing!"
|
||||||
msgstr ""
|
msgstr "Lxs buenxs amigxs son una masa!"
|
||||||
|
|
||||||
msgid "I love my friends!"
|
msgid "I love my friends!"
|
||||||
msgstr ""
|
msgstr "¡Amo a mis amigxs!"
|
||||||
|
|
||||||
msgid "Nobody wants to play with me ..."
|
msgid "Nobody wants to play with me ..."
|
||||||
msgstr "Nadie quiere jugar conmigo ..."
|
msgstr "Nadie quiere jugar conmigo ..."
|
||||||
@ -143,7 +143,7 @@ msgstr "¡¿Dónde está todo el mundo?!"
|
|||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Napping for {secs}s ..."
|
msgid "Napping for {secs}s ..."
|
||||||
msgstr "Descansando durante {secs}s ..."
|
msgstr "Descansando por {secs}s ..."
|
||||||
|
|
||||||
msgid "Zzzzz"
|
msgid "Zzzzz"
|
||||||
msgstr "Zzzzz"
|
msgstr "Zzzzz"
|
||||||
@ -203,7 +203,7 @@ msgstr "Oops, algo salió mal ... Reiniciando ..."
|
|||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Uploading data to {to} ..."
|
msgid "Uploading data to {to} ..."
|
||||||
msgstr ""
|
msgstr "Subiendo data a {to} ..."
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Downloading from {name} ..."
|
msgid "Downloading from {name} ..."
|
||||||
|
Binary file not shown.
@ -16,7 +16,7 @@ msgstr ""
|
|||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
|
||||||
msgid "ZzzzZZzzzzZzzz"
|
msgid "ZzzzZZzzzzZzzz"
|
||||||
msgstr ""
|
msgstr "ZzzzZZzzzzZzzz"
|
||||||
|
|
||||||
msgid "Hi, I'm Pwnagotchi! Starting ..."
|
msgid "Hi, I'm Pwnagotchi! Starting ..."
|
||||||
msgstr "Ciao! Piacere Pwnagotchi! Caricamento ..."
|
msgstr "Ciao! Piacere Pwnagotchi! Caricamento ..."
|
||||||
@ -25,7 +25,7 @@ msgid "New day, new hunt, new pwns!"
|
|||||||
msgstr "Nuovo giorno...nuovi handshakes!!!"
|
msgstr "Nuovo giorno...nuovi handshakes!!!"
|
||||||
|
|
||||||
msgid "Hack the Planet!"
|
msgid "Hack the Planet!"
|
||||||
msgstr ""
|
msgstr "Hack il Pianeta"
|
||||||
|
|
||||||
msgid "AI ready."
|
msgid "AI ready."
|
||||||
msgstr "IA pronta."
|
msgstr "IA pronta."
|
||||||
@ -34,18 +34,18 @@ msgid "The neural network is ready."
|
|||||||
msgstr "La rete neurale è pronta."
|
msgstr "La rete neurale è pronta."
|
||||||
|
|
||||||
msgid "Generating keys, do not turn off ..."
|
msgid "Generating keys, do not turn off ..."
|
||||||
msgstr ""
|
msgstr "Generazione di chiavi, non spegnere"
|
||||||
|
|
||||||
#, 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 "Hey, il canale {channel} è libero! Il tuo AP ringrazia."
|
msgstr "Hey, il canale {channel} è libero! Il tuo AP ringrazia."
|
||||||
|
|
||||||
msgid "Reading last session logs ..."
|
msgid "Reading last session logs ..."
|
||||||
msgstr ""
|
msgstr "Lettura dei log dell'ultima sessione ..."
|
||||||
|
|
||||||
#, 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 ""
|
msgstr "Leggi le righe di log {lines_so_far} finora ..."
|
||||||
|
|
||||||
msgid "I'm bored ..."
|
msgid "I'm bored ..."
|
||||||
msgstr "Che noia ..."
|
msgstr "Che noia ..."
|
||||||
@ -54,7 +54,7 @@ msgid "Let's go for a walk!"
|
|||||||
msgstr "Andiamo a fare una passeggiata!"
|
msgstr "Andiamo a fare una passeggiata!"
|
||||||
|
|
||||||
msgid "This is the best day of my life!"
|
msgid "This is the best day of my life!"
|
||||||
msgstr "Questo è il più bel giorno della mia vita!!!!"
|
msgstr "Questo e il miglior giorno della mia vita!!!!"
|
||||||
|
|
||||||
msgid "Shitty day :/"
|
msgid "Shitty day :/"
|
||||||
msgstr "Giorno di merda :/"
|
msgstr "Giorno di merda :/"
|
||||||
@ -72,22 +72,22 @@ msgid "Leave me alone ..."
|
|||||||
msgstr "Mi sento così solo..."
|
msgstr "Mi sento così solo..."
|
||||||
|
|
||||||
msgid "I'm mad at you!"
|
msgid "I'm mad at you!"
|
||||||
msgstr ""
|
msgstr "sono arabiata con te"
|
||||||
|
|
||||||
msgid "I'm living the life!"
|
msgid "I'm living the life!"
|
||||||
msgstr "Mi sento vivo!"
|
msgstr "sono viva la vita!"
|
||||||
|
|
||||||
msgid "I pwn therefore I am."
|
msgid "I pwn therefore I am."
|
||||||
msgstr "Pwn ergo sum."
|
msgstr "Pwn ergo sum."
|
||||||
|
|
||||||
msgid "So many networks!!!"
|
msgid "So many networks!!!"
|
||||||
msgstr "Qui è pieno di reti!"
|
msgstr "Qui pieno di reti!"
|
||||||
|
|
||||||
msgid "I'm having so much fun!"
|
msgid "I'm having so much fun!"
|
||||||
msgstr "Mi sto divertendo tantissimo!"
|
msgstr "Mi sto divertendo tantissimo!"
|
||||||
|
|
||||||
msgid "My crime is that of curiosity ..."
|
msgid "My crime is that of curiosity ..."
|
||||||
msgstr ""
|
msgstr "Il mio crimine ? quello della curiosit?"
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Hello {name}! Nice to meet you."
|
msgid "Hello {name}! Nice to meet you."
|
||||||
@ -95,15 +95,15 @@ msgstr "Ciao {name}! E' un piacere."
|
|||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Yo {name}! Sup?"
|
msgid "Yo {name}! Sup?"
|
||||||
msgstr ""
|
msgstr "Yo {name} Come va"
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Hey {name} how are you doing?"
|
msgid "Hey {name} how are you doing?"
|
||||||
msgstr ""
|
msgstr "Ehi {name} come stai?"
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Unit {name} is nearby!"
|
msgid "Unit {name} is nearby!"
|
||||||
msgstr "L'Unità {name} è vicina!"
|
msgstr "L'Unit {name} e vicina!"
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Uhm ... goodbye {name}"
|
msgid "Uhm ... goodbye {name}"
|
||||||
@ -111,30 +111,30 @@ msgstr "Uhm ... addio {name}, mi mancherai..."
|
|||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "{name} is gone ..."
|
msgid "{name} is gone ..."
|
||||||
msgstr "{name} se n'è andato ..."
|
msgstr "{name} se andato ..."
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Whoops ... {name} is gone."
|
msgid "Whoops ... {name} is gone."
|
||||||
msgstr "Whoops ...{name} se n'è andato."
|
msgstr "Whoops ...{name} se andato."
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "{name} missed!"
|
msgid "{name} missed!"
|
||||||
msgstr "{name} è scomparso..."
|
msgstr "{name} scomparso..."
|
||||||
|
|
||||||
msgid "Missed!"
|
msgid "Missed!"
|
||||||
msgstr "Ehi! Dove sei andato!?"
|
msgstr "Ehi! Dove sei andato!?"
|
||||||
|
|
||||||
msgid "Good friends are a blessing!"
|
msgid "Good friends are a blessing!"
|
||||||
msgstr ""
|
msgstr "Buoni amici sono una benedizione"
|
||||||
|
|
||||||
msgid "I love my friends!"
|
msgid "I love my friends!"
|
||||||
msgstr ""
|
msgstr "Amo i miei amici"
|
||||||
|
|
||||||
msgid "Nobody wants to play with me ..."
|
msgid "Nobody wants to play with me ..."
|
||||||
msgstr "Nessuno vuole giocare con me..."
|
msgstr "Nessuno vuole giocare con me..."
|
||||||
|
|
||||||
msgid "I feel so alone ..."
|
msgid "I feel so alone ..."
|
||||||
msgstr "Mi sento così solo..."
|
msgstr "Mi sento cos solo..."
|
||||||
|
|
||||||
msgid "Where's everybody?!"
|
msgid "Where's everybody?!"
|
||||||
msgstr "Dove sono tutti?!"
|
msgstr "Dove sono tutti?!"
|
||||||
@ -144,17 +144,17 @@ msgid "Napping for {secs}s ..."
|
|||||||
msgstr "Schiaccio un pisolino per {secs}s ..."
|
msgstr "Schiaccio un pisolino per {secs}s ..."
|
||||||
|
|
||||||
msgid "Zzzzz"
|
msgid "Zzzzz"
|
||||||
msgstr ""
|
msgstr "Zzzzz"
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "ZzzZzzz ({secs}s)"
|
msgid "ZzzZzzz ({secs}s)"
|
||||||
msgstr ""
|
msgstr "ZzzZzzz ({secs}s)"
|
||||||
|
|
||||||
msgid "Good night."
|
msgid "Good night."
|
||||||
msgstr ""
|
msgstr "Buona notte"
|
||||||
|
|
||||||
msgid "Zzz"
|
msgid "Zzz"
|
||||||
msgstr ""
|
msgstr "Zzz"
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Waiting for {secs}s ..."
|
msgid "Waiting for {secs}s ..."
|
||||||
@ -182,7 +182,7 @@ msgstr "Ho appena deciso che {mac} non necessita di WiFi!"
|
|||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Deauthenticating {mac}"
|
msgid "Deauthenticating {mac}"
|
||||||
msgstr ""
|
msgstr "Annullamento dell'autenticazione {mac}"
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Kickbanning {mac}!"
|
msgid "Kickbanning {mac}!"
|
||||||
@ -201,11 +201,11 @@ msgstr "Ops, qualcosa è andato storto ... Riavvio ..."
|
|||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Uploading data to {to} ..."
|
msgid "Uploading data to {to} ..."
|
||||||
msgstr ""
|
msgstr "Caricamento dei dati in {to}"
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Downloading from {name} ..."
|
msgid "Downloading from {name} ..."
|
||||||
msgstr ""
|
msgstr "Scaricamento da {name} ..."
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Kicked {num} stations\n"
|
msgid "Kicked {num} stations\n"
|
||||||
|
Binary file not shown.
@ -14,7 +14,7 @@ msgstr ""
|
|||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
"Language: Portuguese (Brazil)\n"
|
"Language: Portuguese (Brazil)\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=CHARSET\n"
|
"Content-Type: text/plain; charset=utf-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
|
||||||
msgid "ZzzzZZzzzzZzzz"
|
msgid "ZzzzZZzzzzZzzz"
|
||||||
|
@ -3,12 +3,12 @@
|
|||||||
# This file is distributed under the same license as the PACKAGE package.
|
# This file is distributed under the same license as the PACKAGE package.
|
||||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
# 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: 2023-11-17 15:46+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"
|
||||||
|
@ -28,7 +28,7 @@ def check(version, repo, native=True):
|
|||||||
resp = requests.get("https://api.github.com/repos/%s/releases/latest" % repo)
|
resp = requests.get("https://api.github.com/repos/%s/releases/latest" % repo)
|
||||||
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_arm64 = info['arch'].startswith('aarch')
|
is_armhf = info['arch'].startswith('arm')
|
||||||
|
|
||||||
local = version_to_tuple(info['current'])
|
local = version_to_tuple(info['current'])
|
||||||
remote = version_to_tuple(latest_ver)
|
remote = version_to_tuple(latest_ver)
|
||||||
@ -36,12 +36,12 @@ def check(version, repo, native=True):
|
|||||||
if not native:
|
if not native:
|
||||||
info['url'] = "https://github.com/%s/archive/%s.zip" % (repo, latest['tag_name'])
|
info['url'] = "https://github.com/%s/archive/%s.zip" % (repo, latest['tag_name'])
|
||||||
else:
|
else:
|
||||||
if is_arm64:
|
if is_armhf:
|
||||||
# check if this release is compatible with aarch64
|
# check if this release is compatible with armhf
|
||||||
for asset in latest['assets']:
|
for asset in latest['assets']:
|
||||||
download_url = asset['browser_download_url']
|
download_url = asset['browser_download_url']
|
||||||
if (download_url.endswith('.zip') and
|
if (download_url.endswith('.zip') and
|
||||||
(info['arch'] in download_url or (is_arm64 and 'aarch64' 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
|
||||||
|
|
||||||
@ -130,7 +130,7 @@ def install(display, update):
|
|||||||
source_path = "%s-%s" % (source_path, update['available'])
|
source_path = "%s-%s" % (source_path, update['available'])
|
||||||
|
|
||||||
# setup.py is going to install data files for us
|
# setup.py is going to install data files for us
|
||||||
os.system("cd %s && pip3 install . --no-cache-dir --break-system-packages" % source_path)
|
os.system("cd %s && pip3 install . --break-system-packages" % source_path)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
@ -189,7 +189,7 @@ class AutoUpdate(plugins.Plugin):
|
|||||||
to_check = [
|
to_check = [
|
||||||
('jayofelony/bettercap', parse_version('bettercap -version'), True, 'bettercap'),
|
('jayofelony/bettercap', parse_version('bettercap -version'), True, 'bettercap'),
|
||||||
('jayofelony/pwngrid', parse_version('pwngrid -version'), True, 'pwngrid-peer'),
|
('jayofelony/pwngrid', parse_version('pwngrid -version'), True, 'pwngrid-peer'),
|
||||||
('jayofelony/pwnagotchi-bookworm', pwnagotchi.__version__, False, 'pwnagotchi')
|
('jayofelony/pwnagotchi', pwnagotchi.__version__, False, 'pwnagotchi')
|
||||||
]
|
]
|
||||||
|
|
||||||
for repo, local_version, is_native, svc_name in to_check:
|
for repo, local_version, is_native, svc_name in to_check:
|
||||||
|
@ -11,6 +11,10 @@ from pwnagotchi import plugins
|
|||||||
import pwnagotchi.ui.faces as faces
|
import pwnagotchi.ui.faces as faces
|
||||||
from pwnagotchi.bettercap import Client
|
from pwnagotchi.bettercap import Client
|
||||||
|
|
||||||
|
from pwnagotchi.ui.components import Text
|
||||||
|
from pwnagotchi.ui.view import BLACK
|
||||||
|
import pwnagotchi.ui.fonts as fonts
|
||||||
|
|
||||||
|
|
||||||
class FixServices(plugins.Plugin):
|
class FixServices(plugins.Plugin):
|
||||||
__author__ = 'jayofelony'
|
__author__ = 'jayofelony'
|
||||||
@ -21,9 +25,6 @@ class FixServices(plugins.Plugin):
|
|||||||
__help__ = """
|
__help__ = """
|
||||||
Reload brcmfmac module when blindbug is detected, instead of rebooting. Adapted from WATCHDOG.
|
Reload brcmfmac module when blindbug is detected, instead of rebooting. Adapted from WATCHDOG.
|
||||||
"""
|
"""
|
||||||
__defaults__ = {
|
|
||||||
'enabled': True,
|
|
||||||
}
|
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.options = dict()
|
self.options = dict()
|
||||||
@ -34,23 +35,28 @@ class FixServices(plugins.Plugin):
|
|||||||
self.isReloadingMon = False
|
self.isReloadingMon = False
|
||||||
self.connection = None
|
self.connection = None
|
||||||
self.LASTTRY = 0
|
self.LASTTRY = 0
|
||||||
|
self._status = "--"
|
||||||
self._count = 0
|
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):
|
||||||
last_lines = self.get_last_lines('journalctl', ['-n10', '-k'], 10)
|
last_lines = ''.join(list(TextIOWrapper(subprocess.Popen(['journalctl', '-n10', '-k'],
|
||||||
|
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.info("[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.info("wlan0mon is up.")
|
logging.info("wlan0mon is up.")
|
||||||
|
self._status = "up"
|
||||||
|
|
||||||
if len(self.pattern.findall(last_lines)) >= 3:
|
if len(self.pattern.findall(last_lines)) >= 3:
|
||||||
|
self._status = "XX"
|
||||||
if hasattr(agent, 'view'):
|
if hasattr(agent, 'view'):
|
||||||
display = agent.view()
|
display = agent.view()
|
||||||
display.set('status', 'Blind-Bug detected. Restarting.')
|
display.set('status', 'Blind-Bug detected. Restarting.')
|
||||||
@ -63,10 +69,12 @@ class FixServices(plugins.Plugin):
|
|||||||
|
|
||||||
else:
|
else:
|
||||||
logging.info("[Fix_Services] Logs look good!")
|
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))
|
||||||
@ -95,23 +103,14 @@ class FixServices(plugins.Plugin):
|
|||||||
logging.error("[Fix_Services]SYSLOG wifi.recon flip fail: %s" % err)
|
logging.error("[Fix_Services]SYSLOG wifi.recon flip fail: %s" % err)
|
||||||
self._tryTurningItOffAndOnAgain(agent)
|
self._tryTurningItOffAndOnAgain(agent)
|
||||||
|
|
||||||
def get_last_lines(self, command, args, n):
|
|
||||||
try:
|
|
||||||
process = subprocess.Popen([command] + args, stdout=subprocess.PIPE)
|
|
||||||
output = TextIOWrapper(process.stdout)
|
|
||||||
lines = output.readlines()
|
|
||||||
last_n_lines = ''.join(lines[-n:])
|
|
||||||
return last_n_lines
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Error occurred: {e}")
|
|
||||||
return None
|
|
||||||
|
|
||||||
def on_epoch(self, agent, epoch, epoch_data):
|
def on_epoch(self, agent, epoch, epoch_data):
|
||||||
|
last_lines = ''.join(list(TextIOWrapper(subprocess.Popen(['journalctl', '-n10', '-k'],
|
||||||
last_lines = self.get_last_lines('journalctl', ['-n10', '-k'], 10)
|
stdout=subprocess.PIPE).stdout))[-10:])
|
||||||
other_last_lines = self.get_last_lines('journalctl', ['-n10'], 10)
|
other_last_lines = ''.join(list(TextIOWrapper(subprocess.Popen(['journalctl', '-n10'],
|
||||||
other_other_last_lines = self.get_last_lines('tail', ['-n10', '/home/pi/logs/pwnagotchi.log'], 10)
|
stdout=subprocess.PIPE).stdout))[-10:])
|
||||||
|
other_other_last_lines = ''.join(
|
||||||
|
list(TextIOWrapper(subprocess.Popen(['tail', '-n10', '/var/log/pwnagotchi.log'],
|
||||||
|
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:
|
||||||
@ -148,6 +147,7 @@ class FixServices(plugins.Plugin):
|
|||||||
logging.info("[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!")
|
||||||
@ -217,11 +217,12 @@ class FixServices(plugins.Plugin):
|
|||||||
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
|
||||||
|
|
||||||
@ -264,6 +265,7 @@ class FixServices(plugins.Plugin):
|
|||||||
|
|
||||||
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:
|
||||||
@ -276,14 +278,14 @@ class FixServices(plugins.Plugin):
|
|||||||
#
|
#
|
||||||
# Future: while "not fixed yet": blah blah blah. if "max_attemts", then reboot like the old days
|
# Future: while "not fixed yet": blah blah blah. if "max_attemts", then reboot like the old days
|
||||||
#
|
#
|
||||||
tries = 0
|
tries = 1
|
||||||
while tries < 3:
|
while tries < 3:
|
||||||
try:
|
try:
|
||||||
# unload the module
|
# unload the module
|
||||||
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})
|
||||||
time.sleep(1 + tries)
|
self._status = "ul"
|
||||||
|
|
||||||
# reload the module
|
# reload the module
|
||||||
try:
|
try:
|
||||||
@ -291,21 +293,21 @@ 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")
|
||||||
time.sleep(10 + 4 * tries) # give it some time for wlan device to stabilize, or whatever
|
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))
|
||||||
time.sleep(tries + 5)
|
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.info("[Fix_Services set wifi.interface wlan0mon] worked!")
|
logging.info("[Fix_Services set wifi.interface wlan0mon worked!")
|
||||||
|
self._status = ""
|
||||||
self._count = self._count + 1
|
self._count = self._count + 1
|
||||||
time.sleep(1)
|
|
||||||
# stop looping and get back to recon
|
# stop looping and get back to recon
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
@ -342,6 +344,7 @@ class FixServices(plugins.Plugin):
|
|||||||
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...")
|
||||||
@ -357,8 +360,10 @@ class FixServices(plugins.Plugin):
|
|||||||
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")
|
||||||
@ -373,9 +378,31 @@ class FixServices(plugins.Plugin):
|
|||||||
logging.error("[Fix_Services wifi.recon on] %s" % repr(err))
|
logging.error("[Fix_Services wifi.recon on] %s" % repr(err))
|
||||||
pwnagotchi.reboot()
|
pwnagotchi.reboot()
|
||||||
|
|
||||||
|
# called to setup the ui elements
|
||||||
|
def on_ui_setup(self, ui):
|
||||||
|
with ui._lock:
|
||||||
|
# add custom UI elements
|
||||||
|
if "position" in self.options:
|
||||||
|
pos = self.options['position'].split(',')
|
||||||
|
pos = [int(x.strip()) for x in pos]
|
||||||
|
else:
|
||||||
|
pos = (ui.width() / 2 + 35, ui.height() - 11)
|
||||||
|
|
||||||
|
logging.info("Got here")
|
||||||
|
ui.add_element('brcmfmac_status', Text(color=BLACK, value='--', position=pos, font=fonts.Small))
|
||||||
|
|
||||||
|
# called when the ui is updated
|
||||||
|
def on_ui_update(self, ui):
|
||||||
|
# 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):
|
||||||
with ui._lock:
|
with ui._lock:
|
||||||
try:
|
try:
|
||||||
|
ui.remove_element('brcmfmac_status')
|
||||||
logging.info("[Fix_Services] unloaded")
|
logging.info("[Fix_Services] unloaded")
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
logging.info("[Fix_Services] unload err %s " % repr(err))
|
logging.info("[Fix_Services] unload err %s " % repr(err))
|
||||||
|
@ -20,6 +20,7 @@ class GPS(plugins.Plugin):
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.running = False
|
self.running = False
|
||||||
self.coordinates = None
|
self.coordinates = None
|
||||||
|
self.options = dict()
|
||||||
|
|
||||||
def on_loaded(self):
|
def on_loaded(self):
|
||||||
logging.info(f"gps plugin loaded for {self.options['device']}")
|
logging.info(f"gps plugin loaded for {self.options['device']}")
|
||||||
|
@ -58,8 +58,9 @@ class Grid(plugins.Plugin):
|
|||||||
self.total_messages = 0
|
self.total_messages = 0
|
||||||
self.lock = Lock()
|
self.lock = Lock()
|
||||||
|
|
||||||
def is_excluded(self, what):
|
def is_excluded(self, what, agent):
|
||||||
for skip in self.options['exclude']:
|
config = agent.config()
|
||||||
|
for skip in config['main']['whitelist']:
|
||||||
skip = skip.lower()
|
skip = skip.lower()
|
||||||
what = what.lower()
|
what = what.lower()
|
||||||
if skip in what or skip.replace(':', '') in what:
|
if skip in what or skip.replace(':', '') in what:
|
||||||
@ -87,6 +88,7 @@ class Grid(plugins.Plugin):
|
|||||||
|
|
||||||
def check_handshakes(self, agent):
|
def check_handshakes(self, agent):
|
||||||
logging.debug("checking pcaps")
|
logging.debug("checking pcaps")
|
||||||
|
config = agent.config()
|
||||||
|
|
||||||
pcap_files = glob.glob(os.path.join(agent.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)
|
||||||
@ -98,19 +100,19 @@ class Grid(plugins.Plugin):
|
|||||||
if self.options['report']:
|
if self.options['report']:
|
||||||
logging.info("grid: %d new networks to report" % num_new)
|
logging.info("grid: %d new networks to report" % num_new)
|
||||||
logging.debug("self.options: %s" % self.options)
|
logging.debug("self.options: %s" % self.options)
|
||||||
logging.debug(" exclude: %s" % self.options['exclude'])
|
logging.debug(" exclude: %s" % config['main']['whitelist'])
|
||||||
|
|
||||||
for pcap_file in pcap_files:
|
for pcap_file in pcap_files:
|
||||||
net_id = os.path.basename(pcap_file).replace('.pcap', '')
|
net_id = os.path.basename(pcap_file).replace('.pcap', '')
|
||||||
if net_id not in reported:
|
if net_id not in reported:
|
||||||
if self.is_excluded(net_id):
|
if self.is_excluded(net_id, agent):
|
||||||
logging.debug("skipping %s due to exclusion filter" % pcap_file)
|
logging.debug("skipping %s due to exclusion filter" % pcap_file)
|
||||||
self.set_reported(reported, net_id)
|
self.set_reported(reported, net_id)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
essid, bssid = parse_pcap(pcap_file)
|
essid, bssid = parse_pcap(pcap_file)
|
||||||
if bssid:
|
if bssid:
|
||||||
if self.is_excluded(essid) or self.is_excluded(bssid):
|
if self.is_excluded(essid, agent) or self.is_excluded(bssid, agent):
|
||||||
logging.debug("not reporting %s due to exclusion filter" % pcap_file)
|
logging.debug("not reporting %s due to exclusion filter" % pcap_file)
|
||||||
self.set_reported(reported, net_id)
|
self.set_reported(reported, net_id)
|
||||||
else:
|
else:
|
||||||
|
@ -144,6 +144,9 @@ class MemTemp(plugins.Plugin):
|
|||||||
elif ui.is_waveshare2in7():
|
elif ui.is_waveshare2in7():
|
||||||
h_pos = (192, 138)
|
h_pos = (192, 138)
|
||||||
v_pos = (211, 122)
|
v_pos = (211, 122)
|
||||||
|
elif ui.is_waveshare1in54V2():
|
||||||
|
h_pos = (53, 77)
|
||||||
|
v_pos = (154, 65)
|
||||||
else:
|
else:
|
||||||
h_pos = (155, 76)
|
h_pos = (155, 76)
|
||||||
v_pos = (175, 61)
|
v_pos = (175, 61)
|
||||||
|
@ -117,7 +117,6 @@ class NetPos(plugins.Plugin):
|
|||||||
except OSError as os_e:
|
except OSError as os_e:
|
||||||
logging.error("NET-POS: %s", os_e)
|
logging.error("NET-POS: %s", os_e)
|
||||||
|
|
||||||
|
|
||||||
def _get_netpos(self, agent):
|
def _get_netpos(self, agent):
|
||||||
aps = agent.get_access_points()
|
aps = agent.get_access_points()
|
||||||
netpos = dict()
|
netpos = dict()
|
||||||
|
@ -25,6 +25,7 @@ class OnlineHashCrack(plugins.Plugin):
|
|||||||
self.report = StatusFile('/root/.ohc_uploads', data_format='json')
|
self.report = StatusFile('/root/.ohc_uploads', data_format='json')
|
||||||
self.skip = list()
|
self.skip = list()
|
||||||
self.lock = Lock()
|
self.lock = Lock()
|
||||||
|
self.options = dict()
|
||||||
|
|
||||||
def on_loaded(self):
|
def on_loaded(self):
|
||||||
"""
|
"""
|
||||||
@ -34,13 +35,9 @@ class OnlineHashCrack(plugins.Plugin):
|
|||||||
logging.error("OHC: Email isn't set. Can't upload to onlinehashcrack.com")
|
logging.error("OHC: Email isn't set. Can't upload to onlinehashcrack.com")
|
||||||
return
|
return
|
||||||
|
|
||||||
if 'whitelist' not in self.options:
|
|
||||||
self.options['whitelist'] = list()
|
|
||||||
|
|
||||||
self.ready = True
|
self.ready = True
|
||||||
logging.info("OHC: OnlineHashCrack plugin loaded.")
|
logging.info("OHC: OnlineHashCrack plugin loaded.")
|
||||||
|
|
||||||
|
|
||||||
def _upload_to_ohc(self, path, timeout=30):
|
def _upload_to_ohc(self, path, timeout=30):
|
||||||
"""
|
"""
|
||||||
Uploads the file to onlinehashcrack.com
|
Uploads the file to onlinehashcrack.com
|
||||||
@ -78,7 +75,6 @@ class OnlineHashCrack(plugins.Plugin):
|
|||||||
except OSError as os_e:
|
except OSError as os_e:
|
||||||
raise os_e
|
raise os_e
|
||||||
|
|
||||||
|
|
||||||
def on_webhook(self, path, request):
|
def on_webhook(self, path, request):
|
||||||
import requests
|
import requests
|
||||||
from flask import redirect
|
from flask import redirect
|
||||||
@ -87,7 +83,6 @@ class OnlineHashCrack(plugins.Plugin):
|
|||||||
r = s.post('https://www.onlinehashcrack.com/dashboard', data={'emailTasks': self.options['email'], 'submit': ''})
|
r = s.post('https://www.onlinehashcrack.com/dashboard', data={'emailTasks': self.options['email'], 'submit': ''})
|
||||||
return redirect(r.url, code=302)
|
return redirect(r.url, code=302)
|
||||||
|
|
||||||
|
|
||||||
def on_internet_available(self, agent):
|
def on_internet_available(self, agent):
|
||||||
"""
|
"""
|
||||||
Called in manual mode when there's internet connectivity
|
Called in manual mode when there's internet connectivity
|
||||||
@ -105,7 +100,7 @@ class OnlineHashCrack(plugins.Plugin):
|
|||||||
handshake_paths = [os.path.join(handshake_dir, filename) for filename in handshake_filenames if
|
handshake_paths = [os.path.join(handshake_dir, filename) for filename in handshake_filenames if
|
||||||
filename.endswith('.pcap')]
|
filename.endswith('.pcap')]
|
||||||
# pull out whitelisted APs
|
# pull out whitelisted APs
|
||||||
handshake_paths = remove_whitelisted(handshake_paths, self.options['whitelist'])
|
handshake_paths = remove_whitelisted(handshake_paths, config['main']['whitelist'])
|
||||||
handshake_new = set(handshake_paths) - set(reported) - set(self.skip)
|
handshake_new = set(handshake_paths) - set(reported) - set(self.skip)
|
||||||
if handshake_new:
|
if handshake_new:
|
||||||
logging.info("OHC: Internet connectivity detected. Uploading new handshakes to onlinehashcrack.com")
|
logging.info("OHC: Internet connectivity detected. Uploading new handshakes to onlinehashcrack.com")
|
||||||
|
@ -15,6 +15,9 @@ class PawGPS(plugins.Plugin):
|
|||||||
__license__ = 'GPL3'
|
__license__ = 'GPL3'
|
||||||
__description__ = 'Saves GPS coordinates whenever an handshake is captured. The GPS data is get from PAW on android.'
|
__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):
|
def on_loaded(self):
|
||||||
logging.info("[paw-gps] plugin loaded")
|
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):
|
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):
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
|
import sys
|
||||||
|
|
||||||
import pwnagotchi.plugins as plugins
|
import pwnagotchi.plugins as plugins
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
import datetime
|
|
||||||
from flask import Response
|
from flask import Response
|
||||||
from functools import lru_cache
|
from functools import lru_cache
|
||||||
from dateutil.parser import parse
|
from dateutil.parser import parse
|
||||||
@ -22,6 +23,7 @@ from dateutil.parser import parse
|
|||||||
|
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
||||||
class Webgpsmap(plugins.Plugin):
|
class Webgpsmap(plugins.Plugin):
|
||||||
__author__ = 'https://github.com/xenDE and https://github.com/dadav'
|
__author__ = 'https://github.com/xenDE and https://github.com/dadav'
|
||||||
__version__ = '1.4.0'
|
__version__ = '1.4.0'
|
||||||
@ -103,7 +105,7 @@ class Webgpsmap(plugins.Plugin):
|
|||||||
response_status = 200
|
response_status = 200
|
||||||
response_mimetype = "application/xhtml+xml"
|
response_mimetype = "application/xhtml+xml"
|
||||||
response_header_contenttype = 'text/html'
|
response_header_contenttype = 'text/html'
|
||||||
response_header_contentdisposition = 'attachment; filename=webgpsmap.html';
|
response_header_contentdisposition = 'attachment; filename=webgpsmap.html'
|
||||||
except Exception as error:
|
except Exception as error:
|
||||||
logging.error(f"[webgpsmap] on_webhook offlinemap: error: {error}")
|
logging.error(f"[webgpsmap] on_webhook offlinemap: error: {error}")
|
||||||
return
|
return
|
||||||
@ -149,7 +151,6 @@ class Webgpsmap(plugins.Plugin):
|
|||||||
def _get_pos_from_file(self, path):
|
def _get_pos_from_file(self, path):
|
||||||
return PositionFile(path)
|
return PositionFile(path)
|
||||||
|
|
||||||
|
|
||||||
def load_gps_from_dir(self, gpsdir, newest_only=False):
|
def load_gps_from_dir(self, gpsdir, newest_only=False):
|
||||||
"""
|
"""
|
||||||
Parses the gps-data from disk
|
Parses the gps-data from disk
|
||||||
@ -160,13 +161,9 @@ class Webgpsmap(plugins.Plugin):
|
|||||||
|
|
||||||
logging.info(f"[webgpsmap] scanning {handshake_dir}")
|
logging.info(f"[webgpsmap] scanning {handshake_dir}")
|
||||||
|
|
||||||
|
|
||||||
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)
|
all_pcap_files = [os.path.join(handshake_dir, filename) for filename in all_files if filename.endswith('.pcap')]
|
||||||
for filename in all_files
|
|
||||||
if 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"
|
||||||
@ -300,7 +297,6 @@ class PositionFile:
|
|||||||
return parsed_ssid.groups()[0]
|
return parsed_ssid.groups()[0]
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def json(self):
|
def json(self):
|
||||||
"""
|
"""
|
||||||
returns the parsed json
|
returns the parsed json
|
||||||
|
@ -52,10 +52,10 @@ def _transform_wigle_entry(gps_data, pcap_data, plugin_version):
|
|||||||
"""
|
"""
|
||||||
dummy = StringIO()
|
dummy = StringIO()
|
||||||
# write kismet header
|
# write kismet header
|
||||||
dummy.write(f"WigleWifi-1.4,appRelease={plugin_version},model=pwnagotchi,release={__pwnagotchi_version__},"
|
dummy.write(f"WigleWifi-1.6,appRelease={plugin_version},model=pwnagotchi,release={__pwnagotchi_version__},"
|
||||||
f"device={pwnagotchi.name()},display=kismet,board=RaspberryPi,brand=pwnagotchi\n")
|
f"device={pwnagotchi.name()},display=kismet,board=RaspberryPi,brand=pwnagotchi,star=Sol,body=3,subBody=0\n")
|
||||||
dummy.write("MAC,SSID,AuthMode,FirstSeen,Channel,RSSI,CurrentLatitude,"
|
dummy.write(
|
||||||
"CurrentLongitude,AltitudeMeters,AccuracyMeters,Type\n")
|
"MAC,SSID,AuthMode,FirstSeen,Channel,RSSI,CurrentLatitude,CurrentLongitude,AltitudeMeters,AccuracyMeters,Type\n")
|
||||||
|
|
||||||
writer = csv.writer(dummy, delimiter=",", quoting=csv.QUOTE_NONE, escapechar="\\")
|
writer = csv.writer(dummy, delimiter=",", quoting=csv.QUOTE_NONE, escapechar="\\")
|
||||||
writer.writerow([
|
writer.writerow([
|
||||||
@ -89,7 +89,7 @@ def _send_to_wigle(lines, api_key, donate=True, timeout=30):
|
|||||||
headers = {'Authorization': f"Basic {api_key}",
|
headers = {'Authorization': f"Basic {api_key}",
|
||||||
'Accept': 'application/json'}
|
'Accept': 'application/json'}
|
||||||
data = {'donate': 'on' if donate else 'false'}
|
data = {'donate': 'on' if donate else 'false'}
|
||||||
payload = {'file': (pwnagotchi.name()+".csv", dummy), 'type': 'multipart/form-data'}
|
payload = {'file': (pwnagotchi.name() + ".csv", dummy, 'multipart/form-data', {'Expires': '0'})}
|
||||||
try:
|
try:
|
||||||
res = requests.post('https://api.wigle.net/api/v2/file/upload',
|
res = requests.post('https://api.wigle.net/api/v2/file/upload',
|
||||||
data=data,
|
data=data,
|
||||||
@ -104,10 +104,10 @@ def _send_to_wigle(lines, api_key, donate=True, timeout=30):
|
|||||||
|
|
||||||
|
|
||||||
class Wigle(plugins.Plugin):
|
class Wigle(plugins.Plugin):
|
||||||
__author__ = 'Dadav and fixed by Jayofelony'
|
__author__ = "Dadav and updated by Jayofelony"
|
||||||
__version__ = '3.0.0'
|
__version__ = "3.0.1"
|
||||||
__license__ = 'GPL3'
|
__license__ = "GPL3"
|
||||||
__description__ = 'This plugin automatically uploads collected wifis to wigle.net'
|
__description__ = "This plugin automatically uploads collected WiFi to wigle.net"
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.ready = False
|
self.ready = False
|
||||||
@ -121,18 +121,15 @@ class Wigle(plugins.Plugin):
|
|||||||
logging.debug("WIGLE: api_key isn't set. Can't upload to wigle.net")
|
logging.debug("WIGLE: api_key isn't set. Can't upload to wigle.net")
|
||||||
return
|
return
|
||||||
|
|
||||||
if not 'whitelist' in self.options:
|
if 'donate' not in self.options:
|
||||||
self.options['whitelist'] = list()
|
self.options['donate'] = False
|
||||||
|
|
||||||
if not 'donate' in self.options:
|
|
||||||
self.options['donate'] = True
|
|
||||||
|
|
||||||
self.ready = True
|
self.ready = True
|
||||||
logging.info("WIGLE: ready")
|
logging.info("WIGLE: ready")
|
||||||
|
|
||||||
def on_internet_available(self, agent):
|
def on_internet_available(self, agent):
|
||||||
"""
|
"""
|
||||||
Called in manual mode when there's internet connectivity
|
Called when there's internet connectivity
|
||||||
"""
|
"""
|
||||||
if not self.ready or self.lock.locked():
|
if not self.ready or self.lock.locked():
|
||||||
return
|
return
|
||||||
@ -146,9 +143,9 @@ class Wigle(plugins.Plugin):
|
|||||||
all_files = os.listdir(handshake_dir)
|
all_files = os.listdir(handshake_dir)
|
||||||
all_gps_files = [os.path.join(handshake_dir, filename)
|
all_gps_files = [os.path.join(handshake_dir, filename)
|
||||||
for filename in all_files
|
for filename in all_files
|
||||||
if filename.endswith('.gps.json') or filename.endswith('.paw-gps.json') or filename.endswith('.geo.json')]
|
if filename.endswith('.gps.json') or filename.endswith('.geo.json')]
|
||||||
|
|
||||||
all_gps_files = remove_whitelisted(all_gps_files, self.options['whitelist'])
|
all_gps_files = remove_whitelisted(all_gps_files, config['main']['whitelist'])
|
||||||
new_gps_files = set(all_gps_files) - set(reported) - set(self.skip)
|
new_gps_files = set(all_gps_files) - set(reported) - set(self.skip)
|
||||||
if new_gps_files:
|
if new_gps_files:
|
||||||
logging.info("WIGLE: Internet connectivity detected. Uploading new handshakes to wigle.net")
|
logging.info("WIGLE: Internet connectivity detected. Uploading new handshakes to wigle.net")
|
||||||
@ -157,8 +154,6 @@ class Wigle(plugins.Plugin):
|
|||||||
for gps_file in new_gps_files:
|
for gps_file in new_gps_files:
|
||||||
if gps_file.endswith('.gps.json'):
|
if gps_file.endswith('.gps.json'):
|
||||||
pcap_filename = gps_file.replace('.gps.json', '.pcap')
|
pcap_filename = gps_file.replace('.gps.json', '.pcap')
|
||||||
if gps_file.endswith('.paw-gps.json'):
|
|
||||||
pcap_filename = gps_file.replace('.paw-gps.json', '.pcap')
|
|
||||||
if gps_file.endswith('.geo.json'):
|
if gps_file.endswith('.geo.json'):
|
||||||
pcap_filename = gps_file.replace('.geo.json', '.pcap')
|
pcap_filename = gps_file.replace('.geo.json', '.pcap')
|
||||||
if not os.path.exists(pcap_filename):
|
if not os.path.exists(pcap_filename):
|
||||||
|
@ -76,9 +76,6 @@ class WpaSec(plugins.Plugin):
|
|||||||
logging.error("WPA_SEC: API-URL isn't set. Can't upload, no endpoint configured.")
|
logging.error("WPA_SEC: API-URL isn't set. Can't upload, no endpoint configured.")
|
||||||
return
|
return
|
||||||
|
|
||||||
if 'whitelist' not in self.options:
|
|
||||||
self.options['whitelist'] = list()
|
|
||||||
|
|
||||||
self.ready = True
|
self.ready = True
|
||||||
logging.info("WPA_SEC: plugin loaded")
|
logging.info("WPA_SEC: plugin loaded")
|
||||||
|
|
||||||
@ -101,9 +98,8 @@ class WpaSec(plugins.Plugin):
|
|||||||
reported = self.report.data_field_or('reported', default=list())
|
reported = self.report.data_field_or('reported', default=list())
|
||||||
handshake_dir = config['bettercap']['handshakes']
|
handshake_dir = config['bettercap']['handshakes']
|
||||||
handshake_filenames = os.listdir(handshake_dir)
|
handshake_filenames = os.listdir(handshake_dir)
|
||||||
handshake_paths = [os.path.join(handshake_dir, filename) for filename in handshake_filenames if
|
handshake_paths = [os.path.join(handshake_dir, filename) for filename in handshake_filenames if filename.endswith('.pcap')]
|
||||||
filename.endswith('.pcap')]
|
handshake_paths = remove_whitelisted(handshake_paths, config['main']['whitelist'])
|
||||||
handshake_paths = remove_whitelisted(handshake_paths, self.options['whitelist'])
|
|
||||||
handshake_new = set(handshake_paths) - set(reported) - set(self.skip)
|
handshake_new = set(handshake_paths) - set(reported) - set(self.skip)
|
||||||
|
|
||||||
if handshake_new:
|
if handshake_new:
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from PIL import Image
|
from PIL import Image, ImageOps
|
||||||
from textwrap import TextWrapper
|
from textwrap import TextWrapper
|
||||||
|
|
||||||
|
|
||||||
@ -40,21 +40,37 @@ class FilledRect(Widget):
|
|||||||
|
|
||||||
|
|
||||||
class Text(Widget):
|
class Text(Widget):
|
||||||
def __init__(self, value="", position=(0, 0), font=None, color=0, wrap=False, max_length=0):
|
def __init__(self, value="", position=(0, 0), font=None, color=0, wrap=False, max_length=0, png=False):
|
||||||
super().__init__(position, color)
|
super().__init__(position, color)
|
||||||
self.value = value
|
self.value = value
|
||||||
self.font = font
|
self.font = font
|
||||||
self.wrap = wrap
|
self.wrap = wrap
|
||||||
self.max_length = max_length
|
self.max_length = max_length
|
||||||
self.wrapper = TextWrapper(width=self.max_length, replace_whitespace=False) if wrap else None
|
self.wrapper = TextWrapper(width=self.max_length, replace_whitespace=False) if wrap else None
|
||||||
|
self.png = png
|
||||||
|
|
||||||
def draw(self, canvas, drawer):
|
def draw(self, canvas, drawer):
|
||||||
if self.value is not None:
|
if self.value is not None:
|
||||||
if self.wrap:
|
if not self.png:
|
||||||
text = '\n'.join(self.wrapper.wrap(self.value))
|
if self.wrap:
|
||||||
|
text = '\n'.join(self.wrapper.wrap(self.value))
|
||||||
|
else:
|
||||||
|
text = self.value
|
||||||
|
drawer.text(self.xy, text, font=self.font, fill=self.color)
|
||||||
else:
|
else:
|
||||||
text = self.value
|
self.image = Image.open(self.value)
|
||||||
drawer.text(self.xy, text, font=self.font, fill=self.color)
|
self.image = self.image.convert('RGBA')
|
||||||
|
self.pixels = self.image.load()
|
||||||
|
for y in range(self.image.size[1]):
|
||||||
|
for x in range(self.image.size[0]):
|
||||||
|
if self.pixels[x,y][3] < 255: # check alpha
|
||||||
|
self.pixels[x,y] = (255, 255, 255, 255)
|
||||||
|
if self.color == 255:
|
||||||
|
self._image = ImageOps.colorize(self.image.convert('L'), black = "white", white = "black")
|
||||||
|
else:
|
||||||
|
self._image = self.image
|
||||||
|
self.image = self._image.convert('1')
|
||||||
|
canvas.paste(self.image, self.xy)
|
||||||
|
|
||||||
|
|
||||||
class LabeledValue(Widget):
|
class LabeledValue(Widget):
|
||||||
|
@ -23,7 +23,9 @@ DEBUG = '(#__#)'
|
|||||||
UPLOAD = '(1__0)'
|
UPLOAD = '(1__0)'
|
||||||
UPLOAD1 = '(1__1)'
|
UPLOAD1 = '(1__1)'
|
||||||
UPLOAD2 = '(0__1)'
|
UPLOAD2 = '(0__1)'
|
||||||
|
PNG = False
|
||||||
|
POSITION_X = 0
|
||||||
|
POSITION_Y = 40
|
||||||
|
|
||||||
def load_from_config(config):
|
def load_from_config(config):
|
||||||
for face_name, face_value in config.items():
|
for face_name, face_value in config.items():
|
||||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -12,6 +12,7 @@ from . import config
|
|||||||
from . import LCD_1in44
|
from . import LCD_1in44
|
||||||
from PIL import ImageOps
|
from PIL import ImageOps
|
||||||
|
|
||||||
|
|
||||||
class EPD(object):
|
class EPD(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.width = 128
|
self.width = 128
|
||||||
@ -24,9 +25,8 @@ class EPD(object):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def clear(self):
|
def clear(self):
|
||||||
#self.LCD.LCD_Clear()
|
self.LCD.LCD_Clear()
|
||||||
pass
|
|
||||||
|
|
||||||
def display(self, image):
|
def display(self, image):
|
||||||
rgb_im = ImageOps.colorize(image.convert("L"), black ="green", white ="black")
|
rgb_im = ImageOps.colorize(image.convert("L"), black="green", white="black")
|
||||||
self.LCD.LCD_ShowImage(rgb_im, 0, 0)
|
self.LCD.LCD_ShowImage(rgb_im, 0, 0)
|
||||||
|
@ -264,11 +264,11 @@ class EPD:
|
|||||||
def display(self, image):
|
def display(self, image):
|
||||||
if (image == None):
|
if (image == None):
|
||||||
return
|
return
|
||||||
# Width = (self.width % 8 == 0)? (self.width / 8 ): (self.width / 8 + 1)
|
# Width = (self.width % 8 == 0)? (self.width // 8 ): (self.width // 8 + 1)
|
||||||
if (self.width % 8 == 0):
|
if (self.width % 8 == 0):
|
||||||
Width = self.width / 8
|
Width = self.width // 8
|
||||||
else:
|
else:
|
||||||
Width = self.width / 8 + 1
|
Width = self.width // 8 + 1
|
||||||
|
|
||||||
self.send_command(0x10)
|
self.send_command(0x10)
|
||||||
for j in range(0, self.height):
|
for j in range(0, self.height):
|
||||||
@ -282,11 +282,11 @@ class EPD:
|
|||||||
self.TurnOnDisplay()
|
self.TurnOnDisplay()
|
||||||
|
|
||||||
def Clear(self):
|
def Clear(self):
|
||||||
# Width = (self.width % 8 == 0)? (self.width / 8 ): (self.width / 8 + 1)
|
# Width = (self.width % 8 == 0)? (self.width // 8 ): (self.width // 8 + 1)
|
||||||
if (self.width % 8 == 0):
|
if (self.width % 8 == 0):
|
||||||
Width = self.width / 8
|
Width = self.width // 8
|
||||||
else:
|
else:
|
||||||
Width = self.width / 8 + 1
|
Width = self.width // 8 + 1
|
||||||
|
|
||||||
Height = self.height
|
Height = self.height
|
||||||
|
|
||||||
@ -313,11 +313,11 @@ class EPD:
|
|||||||
self.send_data(127) # y-end
|
self.send_data(127) # y-end
|
||||||
self.send_data(0x00)
|
self.send_data(0x00)
|
||||||
|
|
||||||
# Width = (self.width % 8 == 0)? (self.width / 8 ): (self.width / 8 + 1)
|
# Width = (self.width % 8 == 0)? (self.width // 8 ): (self.width // 8 + 1)
|
||||||
if (self.width % 8 == 0):
|
if (self.width % 8 == 0):
|
||||||
Width = self.width / 8
|
Width = self.width // 8
|
||||||
else:
|
else:
|
||||||
Width = self.width / 8 + 1
|
Width = self.width // 8 + 1
|
||||||
|
|
||||||
Height = self.height
|
Height = self.height
|
||||||
# send data
|
# send data
|
||||||
|
242
pwnagotchi/ui/hw/libs/waveshare/v2in23g/epd2in13g.py
Normal file
242
pwnagotchi/ui/hw/libs/waveshare/v2in23g/epd2in13g.py
Normal file
@ -0,0 +1,242 @@
|
|||||||
|
# *****************************************************************************
|
||||||
|
# * | File : epd2in13g.py
|
||||||
|
# * | Author : Waveshare team
|
||||||
|
# * | Function : Electronic paper driver
|
||||||
|
# * | Info :
|
||||||
|
# *----------------
|
||||||
|
# * | This version: V1.0
|
||||||
|
# * | Date : 2023-05-29
|
||||||
|
# # | Info : python demo
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# ******************************************************************************/
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
# of this software and associated documnetation files (the "Software"), to deal
|
||||||
|
# in the Software without restriction, including without limitation the rights
|
||||||
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
# copies of the Software, and to permit persons to whom the Software is
|
||||||
|
# furished to do so, subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be included in
|
||||||
|
# all copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
# THE SOFTWARE.
|
||||||
|
#
|
||||||
|
|
||||||
|
import logging
|
||||||
|
from .. import epdconfig
|
||||||
|
|
||||||
|
import PIL
|
||||||
|
from PIL import Image
|
||||||
|
import io
|
||||||
|
|
||||||
|
# Display resolution
|
||||||
|
EPD_WIDTH = 122
|
||||||
|
EPD_HEIGHT = 250
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class EPD:
|
||||||
|
def __init__(self):
|
||||||
|
self.reset_pin = epdconfig.RST_PIN
|
||||||
|
self.dc_pin = epdconfig.DC_PIN
|
||||||
|
self.busy_pin = epdconfig.BUSY_PIN
|
||||||
|
self.cs_pin = epdconfig.CS_PIN
|
||||||
|
self.width = EPD_WIDTH
|
||||||
|
self.height = EPD_HEIGHT
|
||||||
|
self.BLACK = 0x000000 # 00 BGR
|
||||||
|
self.WHITE = 0xffffff # 01
|
||||||
|
self.YELLOW = 0x00ffff # 10
|
||||||
|
self.RED = 0x0000ff # 11
|
||||||
|
self.Gate_BITS = EPD_HEIGHT
|
||||||
|
if self.width < 128:
|
||||||
|
self.Source_BITS = 128
|
||||||
|
else:
|
||||||
|
self.Source_BITS = self.width
|
||||||
|
|
||||||
|
# Hardware reset
|
||||||
|
def reset(self):
|
||||||
|
epdconfig.digital_write(self.reset_pin, 1)
|
||||||
|
epdconfig.delay_ms(200)
|
||||||
|
epdconfig.digital_write(self.reset_pin, 0) # module reset
|
||||||
|
epdconfig.delay_ms(2)
|
||||||
|
epdconfig.digital_write(self.reset_pin, 1)
|
||||||
|
epdconfig.delay_ms(200)
|
||||||
|
|
||||||
|
def send_command(self, command):
|
||||||
|
epdconfig.digital_write(self.dc_pin, 0)
|
||||||
|
epdconfig.digital_write(self.cs_pin, 0)
|
||||||
|
epdconfig.spi_writebyte([command])
|
||||||
|
epdconfig.digital_write(self.cs_pin, 1)
|
||||||
|
|
||||||
|
def send_data(self, data):
|
||||||
|
epdconfig.digital_write(self.dc_pin, 1)
|
||||||
|
epdconfig.digital_write(self.cs_pin, 0)
|
||||||
|
epdconfig.spi_writebyte([data])
|
||||||
|
epdconfig.digital_write(self.cs_pin, 1)
|
||||||
|
|
||||||
|
def ReadBusy(self):
|
||||||
|
logger.debug("e-Paper busy H")
|
||||||
|
epdconfig.delay_ms(100)
|
||||||
|
while (epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy
|
||||||
|
epdconfig.delay_ms(5)
|
||||||
|
logger.debug("e-Paper busy release")
|
||||||
|
|
||||||
|
def SetWindow(self):
|
||||||
|
self.send_command(0x61) # SET_RAM_X_ADDRESS_START_END_POSITION
|
||||||
|
# x point must be the multiple of 8 or the last 3 bits will be ignored
|
||||||
|
self.send_data(self.Source_BITS / 256)
|
||||||
|
self.send_data(self.Source_BITS % 256)
|
||||||
|
self.send_data(self.Gate_BITS / 256)
|
||||||
|
self.send_data(self.Gate_BITS % 256)
|
||||||
|
|
||||||
|
def TurnOnDisplay(self):
|
||||||
|
self.send_command(0x12) # DISPLAY_REFRESH
|
||||||
|
self.send_data(0X00)
|
||||||
|
self.ReadBusy()
|
||||||
|
|
||||||
|
def init(self):
|
||||||
|
if (epdconfig.module_init() != 0):
|
||||||
|
return -1
|
||||||
|
# EPD hardware init start
|
||||||
|
|
||||||
|
self.reset()
|
||||||
|
|
||||||
|
self.ReadBusy()
|
||||||
|
self.send_command(0x4D)
|
||||||
|
self.send_data(0x78)
|
||||||
|
|
||||||
|
self.send_command(0x00)
|
||||||
|
self.send_data(0x0F)
|
||||||
|
self.send_data(0x29)
|
||||||
|
|
||||||
|
self.send_command(0x01)
|
||||||
|
self.send_data(0x07)
|
||||||
|
self.send_data(0x00)
|
||||||
|
|
||||||
|
self.send_command(0x03)
|
||||||
|
self.send_data(0x10)
|
||||||
|
self.send_data(0x54)
|
||||||
|
self.send_data(0x44)
|
||||||
|
|
||||||
|
self.send_command(0x06)
|
||||||
|
self.send_data(0x05)
|
||||||
|
self.send_data(0x00)
|
||||||
|
self.send_data(0x3F)
|
||||||
|
self.send_data(0x0A)
|
||||||
|
self.send_data(0x25)
|
||||||
|
self.send_data(0x12)
|
||||||
|
self.send_data(0x1A)
|
||||||
|
|
||||||
|
self.send_command(0x50)
|
||||||
|
self.send_data(0x37)
|
||||||
|
|
||||||
|
self.send_command(0x60)
|
||||||
|
self.send_data(0x02)
|
||||||
|
self.send_data(0x02)
|
||||||
|
|
||||||
|
self.SetWindow()
|
||||||
|
|
||||||
|
self.send_command(0xE7)
|
||||||
|
self.send_data(0x1C)
|
||||||
|
|
||||||
|
self.send_command(0xE3)
|
||||||
|
self.send_data(0x22)
|
||||||
|
|
||||||
|
self.send_command(0xB4)
|
||||||
|
self.send_data(0xD0)
|
||||||
|
self.send_command(0xB5)
|
||||||
|
self.send_data(0x03)
|
||||||
|
|
||||||
|
self.send_command(0xE9)
|
||||||
|
self.send_data(0x01)
|
||||||
|
|
||||||
|
self.send_command(0x30)
|
||||||
|
self.send_data(0x08)
|
||||||
|
|
||||||
|
self.send_command(0x04)
|
||||||
|
self.ReadBusy()
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def getbuffer(self, image):
|
||||||
|
# Create a pallette with the 4 colors supported by the panel
|
||||||
|
pal_image = Image.new("P", (1, 1))
|
||||||
|
pal_image.putpalette((0, 0, 0, 255, 255, 255, 255, 255, 0, 255, 0, 0) + (0, 0, 0) * 252)
|
||||||
|
|
||||||
|
# Check if we need to rotate the image
|
||||||
|
imwidth, imheight = image.size
|
||||||
|
if (imwidth == self.width and imheight == self.height):
|
||||||
|
image_temp = image
|
||||||
|
elif (imwidth == self.height and imheight == self.width):
|
||||||
|
image_temp = image.rotate(90, expand=True)
|
||||||
|
else:
|
||||||
|
logger.warning(
|
||||||
|
"Invalid image dimensions: %d x %d, expected %d x %d" % (imwidth, imheight, self.width, self.height))
|
||||||
|
|
||||||
|
# Convert the soruce image to the 4 colors, dithering if needed
|
||||||
|
image_4color = image_temp.convert("RGB").quantize(palette=pal_image)
|
||||||
|
buf_4color = bytearray(image_4color.tobytes('raw'))
|
||||||
|
|
||||||
|
# into a single byte to transfer to the panel
|
||||||
|
if self.width % 4 == 0:
|
||||||
|
Width = self.width // 4
|
||||||
|
else:
|
||||||
|
Width = self.width // 4 + 1
|
||||||
|
Height = self.height
|
||||||
|
buf = [0x00] * int(Width * Height)
|
||||||
|
idx = 0
|
||||||
|
for j in range(0, Height):
|
||||||
|
for i in range(0, Width):
|
||||||
|
if i == Width - 1:
|
||||||
|
buf[i + j * Width] = (buf_4color[idx] << 6) + (buf_4color[idx + 1] << 4)
|
||||||
|
idx = idx + 2
|
||||||
|
else:
|
||||||
|
buf[i + j * Width] = (buf_4color[idx] << 6) + (buf_4color[idx + 1] << 4) + (
|
||||||
|
buf_4color[idx + 2] << 2) + buf_4color[idx + 3]
|
||||||
|
idx = idx + 4
|
||||||
|
return buf
|
||||||
|
|
||||||
|
def display(self, image):
|
||||||
|
if self.width % 4 == 0:
|
||||||
|
Width = self.width // 4
|
||||||
|
else:
|
||||||
|
Width = self.width // 4 + 1
|
||||||
|
Height = self.height
|
||||||
|
|
||||||
|
self.send_command(0x10)
|
||||||
|
for j in range(0, Height):
|
||||||
|
for i in range(0, self.Source_BITS // 4):
|
||||||
|
if i < 31:
|
||||||
|
self.send_data(image[i + j * Width])
|
||||||
|
else:
|
||||||
|
self.send_data(0x00)
|
||||||
|
|
||||||
|
self.TurnOnDisplay()
|
||||||
|
|
||||||
|
def Clear(self, color=0x55):
|
||||||
|
Width = self.Source_BITS // 4
|
||||||
|
Height = self.height
|
||||||
|
|
||||||
|
self.send_command(0x10)
|
||||||
|
for j in range(0, Height):
|
||||||
|
for i in range(0, Width):
|
||||||
|
self.send_data(color)
|
||||||
|
self.TurnOnDisplay()
|
||||||
|
|
||||||
|
def sleep(self):
|
||||||
|
self.send_command(0x02) # POWER_OFF
|
||||||
|
self.ReadBusy()
|
||||||
|
epdconfig.delay_ms(100)
|
||||||
|
|
||||||
|
self.send_command(0x07) # DEEP_SLEEP
|
||||||
|
self.send_data(0XA5)
|
||||||
|
|
||||||
|
epdconfig.delay_ms(2000)
|
||||||
|
epdconfig.module_exit()
|
||||||
|
### END OF FILE ###
|
@ -100,7 +100,7 @@ class EPD:
|
|||||||
|
|
||||||
def ReadBusy(self):
|
def ReadBusy(self):
|
||||||
logger.debug("e-Paper busy")
|
logger.debug("e-Paper busy")
|
||||||
while epdconfig.digital_read(self.busy_pin) == 1: # 1: idle, 0: busy
|
while (epdconfig.digital_read(self.busy_pin) == 1): # 1: idle, 0: busy
|
||||||
epdconfig.delay_ms(20)
|
epdconfig.delay_ms(20)
|
||||||
logger.debug("e-Paper busy release")
|
logger.debug("e-Paper busy release")
|
||||||
|
|
||||||
@ -134,7 +134,7 @@ class EPD:
|
|||||||
self.send_data(self.LUT_DATA_4Gray[i])
|
self.send_data(self.LUT_DATA_4Gray[i])
|
||||||
|
|
||||||
def init(self):
|
def init(self):
|
||||||
if epdconfig.module_init() != 0:
|
if (epdconfig.module_init() != 0):
|
||||||
return -1
|
return -1
|
||||||
|
|
||||||
# EPD hardware init start
|
# EPD hardware init start
|
||||||
@ -159,7 +159,7 @@ class EPD:
|
|||||||
return 0
|
return 0
|
||||||
|
|
||||||
def init_Fast(self):
|
def init_Fast(self):
|
||||||
if epdconfig.module_init() != 0:
|
if (epdconfig.module_init() != 0):
|
||||||
return -1
|
return -1
|
||||||
|
|
||||||
# EPD hardware init start
|
# EPD hardware init start
|
||||||
@ -204,12 +204,12 @@ class EPD:
|
|||||||
return 0
|
return 0
|
||||||
|
|
||||||
def Init_4Gray(self):
|
def Init_4Gray(self):
|
||||||
if epdconfig.module_init() != 0:
|
if (epdconfig.module_init() != 0):
|
||||||
return -1
|
return -1
|
||||||
self.reset()
|
self.reset()
|
||||||
|
|
||||||
self.send_command(0x12) # soft reset
|
self.send_command(0x12) # soft reset
|
||||||
self.ReadBusy()
|
self.ReadBusy();
|
||||||
|
|
||||||
self.send_command(0x74) # set analog block control
|
self.send_command(0x74) # set analog block control
|
||||||
self.send_data(0x54)
|
self.send_data(0x54)
|
||||||
@ -268,14 +268,14 @@ class EPD:
|
|||||||
imwidth, imheight = image_monocolor.size
|
imwidth, imheight = image_monocolor.size
|
||||||
pixels = image_monocolor.load()
|
pixels = image_monocolor.load()
|
||||||
# logger.debug("imwidth = %d, imheight = %d",imwidth,imheight)
|
# logger.debug("imwidth = %d, imheight = %d",imwidth,imheight)
|
||||||
if imwidth == self.width and imheight == self.height:
|
if (imwidth == self.width and imheight == self.height):
|
||||||
logger.debug("Vertical")
|
logger.debug("Vertical")
|
||||||
for y in range(imheight):
|
for y in range(imheight):
|
||||||
for x in range(imwidth):
|
for x in range(imwidth):
|
||||||
# Set the bits for the column of pixels at the current position.
|
# Set the bits for the column of pixels at the current position.
|
||||||
if pixels[x, y] == 0:
|
if pixels[x, y] == 0:
|
||||||
buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8))
|
buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8))
|
||||||
elif imwidth == self.height and imheight == self.width:
|
elif (imwidth == self.height and imheight == self.width):
|
||||||
logger.debug("Horizontal")
|
logger.debug("Horizontal")
|
||||||
for y in range(imheight):
|
for y in range(imheight):
|
||||||
for x in range(imwidth):
|
for x in range(imwidth):
|
||||||
@ -293,40 +293,40 @@ class EPD:
|
|||||||
pixels = image_monocolor.load()
|
pixels = image_monocolor.load()
|
||||||
i = 0
|
i = 0
|
||||||
# logger.debug("imwidth = %d, imheight = %d",imwidth,imheight)
|
# logger.debug("imwidth = %d, imheight = %d",imwidth,imheight)
|
||||||
if imwidth == self.width and imheight == self.height:
|
if (imwidth == self.width and imheight == self.height):
|
||||||
logger.debug("Vertical")
|
logger.debug("Vertical")
|
||||||
for y in range(imheight):
|
for y in range(imheight):
|
||||||
for x in range(imwidth):
|
for x in range(imwidth):
|
||||||
# Set the bits for the column of pixels at the current position.
|
# Set the bits for the column of pixels at the current position.
|
||||||
if pixels[x, y] == 0xC0:
|
if (pixels[x, y] == 0xC0):
|
||||||
pixels[x, y] = 0x80
|
pixels[x, y] = 0x80
|
||||||
elif pixels[x, y] == 0x80:
|
elif (pixels[x, y] == 0x80):
|
||||||
pixels[x, y] = 0x40
|
pixels[x, y] = 0x40
|
||||||
i = i + 1
|
i = i + 1
|
||||||
if i % 4 == 0:
|
if (i % 4 == 0):
|
||||||
buf[int((x + (y * self.width)) / 4)] = (
|
buf[int((x + (y * self.width)) / 4)] = (
|
||||||
(pixels[x - 3, y] & 0xc0) | (pixels[x - 2, y] & 0xc0) >> 2 | (
|
(pixels[x - 3, y] & 0xc0) | (pixels[x - 2, y] & 0xc0) >> 2 | (
|
||||||
pixels[x - 1, y] & 0xc0) >> 4 | (pixels[x, y] & 0xc0) >> 6)
|
pixels[x - 1, y] & 0xc0) >> 4 | (pixels[x, y] & 0xc0) >> 6)
|
||||||
|
|
||||||
elif imwidth == self.height and imheight == self.width:
|
elif (imwidth == self.height and imheight == self.width):
|
||||||
logger.debug("Horizontal")
|
logger.debug("Horizontal")
|
||||||
for x in range(imwidth):
|
for x in range(imwidth):
|
||||||
for y in range(imheight):
|
for y in range(imheight):
|
||||||
newx = y
|
newx = y
|
||||||
newy = self.height - x - 1
|
newy = self.height - x - 1
|
||||||
if pixels[x, y] == 0xC0:
|
if (pixels[x, y] == 0xC0):
|
||||||
pixels[x, y] = 0x80
|
pixels[x, y] = 0x80
|
||||||
elif pixels[x, y] == 0x80:
|
elif (pixels[x, y] == 0x80):
|
||||||
pixels[x, y] = 0x40
|
pixels[x, y] = 0x40
|
||||||
i = i + 1
|
i = i + 1
|
||||||
if i % 4 == 0:
|
if (i % 4 == 0):
|
||||||
buf[int((newx + (newy * self.width)) / 4)] = (
|
buf[int((newx + (newy * self.width)) / 4)] = (
|
||||||
(pixels[x, y - 3] & 0xc0) | (pixels[x, y - 2] & 0xc0) >> 2 | (
|
(pixels[x, y - 3] & 0xc0) | (pixels[x, y - 2] & 0xc0) >> 2 | (
|
||||||
pixels[x, y - 1] & 0xc0) >> 4 | (pixels[x, y] & 0xc0) >> 6)
|
pixels[x, y - 1] & 0xc0) >> 4 | (pixels[x, y] & 0xc0) >> 6)
|
||||||
return buf
|
return buf
|
||||||
|
|
||||||
def Clear(self):
|
def Clear(self):
|
||||||
if self.width % 8 == 0:
|
if (self.width % 8 == 0):
|
||||||
Width = self.width // 8
|
Width = self.width // 8
|
||||||
else:
|
else:
|
||||||
Width = self.width // 8 + 1
|
Width = self.width // 8 + 1
|
||||||
@ -338,7 +338,7 @@ class EPD:
|
|||||||
self.TurnOnDisplay()
|
self.TurnOnDisplay()
|
||||||
|
|
||||||
def display(self, image):
|
def display(self, image):
|
||||||
if self.width % 8 == 0:
|
if (self.width % 8 == 0):
|
||||||
Width = self.width // 8
|
Width = self.width // 8
|
||||||
else:
|
else:
|
||||||
Width = self.width // 8 + 1
|
Width = self.width // 8 + 1
|
||||||
@ -350,7 +350,7 @@ class EPD:
|
|||||||
self.TurnOnDisplay()
|
self.TurnOnDisplay()
|
||||||
|
|
||||||
def display_Fast(self, image):
|
def display_Fast(self, image):
|
||||||
if self.width % 8 == 0:
|
if (self.width % 8 == 0):
|
||||||
Width = self.width // 8
|
Width = self.width // 8
|
||||||
else:
|
else:
|
||||||
Width = self.width // 8 + 1
|
Width = self.width // 8 + 1
|
||||||
@ -362,7 +362,7 @@ class EPD:
|
|||||||
self.TurnOnDisplay_Fast()
|
self.TurnOnDisplay_Fast()
|
||||||
|
|
||||||
def display_Base(self, image):
|
def display_Base(self, image):
|
||||||
if self.width % 8 == 0:
|
if (self.width % 8 == 0):
|
||||||
Width = self.width // 8
|
Width = self.width // 8
|
||||||
else:
|
else:
|
||||||
Width = self.width // 8 + 1
|
Width = self.width // 8 + 1
|
||||||
@ -379,7 +379,7 @@ class EPD:
|
|||||||
self.TurnOnDisplay()
|
self.TurnOnDisplay()
|
||||||
|
|
||||||
def display_Base_color(self, color):
|
def display_Base_color(self, color):
|
||||||
if self.width % 8 == 0:
|
if (self.width % 8 == 0):
|
||||||
Width = self.width // 8
|
Width = self.width // 8
|
||||||
else:
|
else:
|
||||||
Width = self.width // 8 + 1
|
Width = self.width // 8 + 1
|
||||||
@ -396,7 +396,8 @@ class EPD:
|
|||||||
# self.TurnOnDisplay()
|
# self.TurnOnDisplay()
|
||||||
|
|
||||||
def display_Partial(self, Image, Xstart, Ystart, Xend, Yend):
|
def display_Partial(self, Image, Xstart, Ystart, Xend, Yend):
|
||||||
if (Xstart % 8 + Xend % 8 == 8 & Xstart % 8 > Xend % 8) | Xstart % 8 + Xend % 8 == 0 | (Xend - Xstart) % 8 == 0:
|
if ((Xstart % 8 + Xend % 8 == 8 & Xstart % 8 > Xend % 8) | Xstart % 8 + Xend % 8 == 0 | (
|
||||||
|
Xend - Xstart) % 8 == 0):
|
||||||
Xstart = Xstart // 8
|
Xstart = Xstart // 8
|
||||||
Xend = Xend // 8
|
Xend = Xend // 8
|
||||||
else:
|
else:
|
||||||
@ -439,23 +440,23 @@ class EPD:
|
|||||||
self.send_command(0x24) # Write Black and White image to RAM
|
self.send_command(0x24) # Write Black and White image to RAM
|
||||||
for j in range(Height):
|
for j in range(Height):
|
||||||
for i in range(Width):
|
for i in range(Width):
|
||||||
if (j > Ystart - 1) & (j < (Yend + 1)) & (i > Xstart - 1) & (i < (Xend + 1)):
|
if ((j > Ystart - 1) & (j < (Yend + 1)) & (i > Xstart - 1) & (i < (Xend + 1))):
|
||||||
self.send_data(Image[i + j * Width])
|
self.send_data(Image[i + j * Width])
|
||||||
self.TurnOnDisplay_Partial()
|
self.TurnOnDisplay_Partial()
|
||||||
|
|
||||||
def display_4Gray(self, image):
|
def display_4Gray(self, image):
|
||||||
self.send_command(0x24)
|
self.send_command(0x24)
|
||||||
for i in range(0, 5808): # 5808*4 46464
|
for i in range(0, 48000): # 5808*4 46464
|
||||||
temp3 = 0
|
temp3 = 0
|
||||||
for j in range(0, 2):
|
for j in range(0, 2):
|
||||||
temp1 = image[i * 2 + j]
|
temp1 = image[i * 2 + j]
|
||||||
for k in range(0, 2):
|
for k in range(0, 2):
|
||||||
temp2 = temp1 & 0xC0
|
temp2 = temp1 & 0xC0
|
||||||
if temp2 == 0xC0:
|
if (temp2 == 0xC0):
|
||||||
temp3 |= 0x00
|
temp3 |= 0x00
|
||||||
elif temp2 == 0x00:
|
elif (temp2 == 0x00):
|
||||||
temp3 |= 0x01
|
temp3 |= 0x01
|
||||||
elif temp2 == 0x80:
|
elif (temp2 == 0x80):
|
||||||
temp3 |= 0x01
|
temp3 |= 0x01
|
||||||
else: # 0x40
|
else: # 0x40
|
||||||
temp3 |= 0x00
|
temp3 |= 0x00
|
||||||
@ -463,31 +464,31 @@ class EPD:
|
|||||||
|
|
||||||
temp1 <<= 2
|
temp1 <<= 2
|
||||||
temp2 = temp1 & 0xC0
|
temp2 = temp1 & 0xC0
|
||||||
if temp2 == 0xC0:
|
if (temp2 == 0xC0):
|
||||||
temp3 |= 0x00
|
temp3 |= 0x00
|
||||||
elif temp2 == 0x00:
|
elif (temp2 == 0x00):
|
||||||
temp3 |= 0x01
|
temp3 |= 0x01
|
||||||
elif temp2 == 0x80:
|
elif (temp2 == 0x80):
|
||||||
temp3 |= 0x01
|
temp3 |= 0x01
|
||||||
else: # 0x40
|
else: # 0x40
|
||||||
temp3 |= 0x00
|
temp3 |= 0x00
|
||||||
if j != 1 or k != 1:
|
if (j != 1 or k != 1):
|
||||||
temp3 <<= 1
|
temp3 <<= 1
|
||||||
temp1 <<= 2
|
temp1 <<= 2
|
||||||
self.send_data(temp3)
|
self.send_data(temp3)
|
||||||
|
|
||||||
self.send_command(0x26)
|
self.send_command(0x26)
|
||||||
for i in range(0, 5808): # 5808*4 46464
|
for i in range(0, 48000): # 5808*4 46464
|
||||||
temp3 = 0
|
temp3 = 0
|
||||||
for j in range(0, 2):
|
for j in range(0, 2):
|
||||||
temp1 = image[i * 2 + j]
|
temp1 = image[i * 2 + j]
|
||||||
for k in range(0, 2):
|
for k in range(0, 2):
|
||||||
temp2 = temp1 & 0xC0
|
temp2 = temp1 & 0xC0
|
||||||
if temp2 == 0xC0:
|
if (temp2 == 0xC0):
|
||||||
temp3 |= 0x00
|
temp3 |= 0x00
|
||||||
elif temp2 == 0x00:
|
elif (temp2 == 0x00):
|
||||||
temp3 |= 0x01
|
temp3 |= 0x01
|
||||||
elif temp2 == 0x80:
|
elif (temp2 == 0x80):
|
||||||
temp3 |= 0x00
|
temp3 |= 0x00
|
||||||
else: # 0x40
|
else: # 0x40
|
||||||
temp3 |= 0x01
|
temp3 |= 0x01
|
||||||
@ -495,15 +496,15 @@ class EPD:
|
|||||||
|
|
||||||
temp1 <<= 2
|
temp1 <<= 2
|
||||||
temp2 = temp1 & 0xC0
|
temp2 = temp1 & 0xC0
|
||||||
if temp2 == 0xC0:
|
if (temp2 == 0xC0):
|
||||||
temp3 |= 0x00
|
temp3 |= 0x00
|
||||||
elif temp2 == 0x00:
|
elif (temp2 == 0x00):
|
||||||
temp3 |= 0x01
|
temp3 |= 0x01
|
||||||
elif temp2 == 0x80:
|
elif (temp2 == 0x80):
|
||||||
temp3 |= 0x00
|
temp3 |= 0x00
|
||||||
else: # 0x40
|
else: # 0x40
|
||||||
temp3 |= 0x01
|
temp3 |= 0x01
|
||||||
if j != 1 or k != 1:
|
if (j != 1 or k != 1):
|
||||||
temp3 <<= 1
|
temp3 <<= 1
|
||||||
temp1 <<= 2
|
temp1 <<= 2
|
||||||
self.send_data(temp3)
|
self.send_data(temp3)
|
||||||
@ -516,3 +517,5 @@ class EPD:
|
|||||||
|
|
||||||
epdconfig.delay_ms(2000)
|
epdconfig.delay_ms(2000)
|
||||||
epdconfig.module_exit()
|
epdconfig.module_exit()
|
||||||
|
|
||||||
|
### END OF FILE ###
|
@ -28,7 +28,7 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
from .. import epdconfig
|
from . import epdconfig
|
||||||
|
|
||||||
# Display resolution
|
# Display resolution
|
||||||
EPD_WIDTH = 128
|
EPD_WIDTH = 128
|
||||||
@ -227,7 +227,7 @@ class EPD:
|
|||||||
self.send_data((y >> 8) & 0xFF)
|
self.send_data((y >> 8) & 0xFF)
|
||||||
|
|
||||||
def init(self):
|
def init(self):
|
||||||
if epdconfig.module_init() != 0:
|
if (epdconfig.module_init() != 0):
|
||||||
return -1
|
return -1
|
||||||
# EPD hardware init start
|
# EPD hardware init start
|
||||||
self.reset()
|
self.reset()
|
||||||
|
@ -184,7 +184,7 @@ class EPD:
|
|||||||
|
|
||||||
self.send_command(0x01) # Driver output control
|
self.send_command(0x01) # Driver output control
|
||||||
self.send_data((self.height - 1) % 256)
|
self.send_data((self.height - 1) % 256)
|
||||||
self.send_data((self.height - 1) / 256)
|
self.send_data((self.height - 1) // 256)
|
||||||
self.send_data(0x00)
|
self.send_data(0x00)
|
||||||
|
|
||||||
self.send_command(0x11) # data entry mode
|
self.send_command(0x11) # data entry mode
|
||||||
|
@ -29,7 +29,7 @@
|
|||||||
|
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
from .. import epdconfig
|
from . import epdconfig
|
||||||
|
|
||||||
# Display resolution
|
# Display resolution
|
||||||
EPD_WIDTH = 800
|
EPD_WIDTH = 800
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user