Compare commits
81 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
99d7017785 | ||
|
8bc421952b | ||
|
b47f3c6b28 | ||
|
17d20837a3 | ||
|
0cccfef14e | ||
|
b46f751e7d | ||
|
0f8f77c2be | ||
|
a9123922c0 | ||
|
66e5f89a96 | ||
|
f84dd00295 | ||
|
3535329708 | ||
|
b50c71cf14 | ||
|
68aebbf126 | ||
|
c3de66d704 | ||
|
08a46a5524 | ||
|
f3e7841b1b | ||
|
79ba5102d7 | ||
|
20036f370d | ||
|
34b52a11cd | ||
|
2d78b52294 | ||
|
3cc31686c2 | ||
|
80159533bc | ||
|
947a41da90 | ||
|
dea990a531 | ||
|
dfaf3418af | ||
|
36ab3b7655 | ||
|
5a32a77870 | ||
|
7520d4dd6f | ||
|
f73a695747 | ||
|
d94ca76817 | ||
|
2cfaae1993 | ||
|
5ed2f2df78 | ||
|
ee55ed7168 | ||
|
ce338e8fef | ||
|
9cfa365ec9 | ||
|
fc23415d57 | ||
|
fc3367181b | ||
|
8210c0bb71 | ||
|
3ddc717009 | ||
|
d700e4fd0c | ||
|
5eb23e2c84 | ||
|
b187b17f9a | ||
|
06e1115cef | ||
|
71cdaf855d | ||
|
9d580ffc0f | ||
|
f80eeff8fc | ||
|
be75fc53d4 | ||
|
e6777eba8a | ||
|
69d49e1395 | ||
|
9f3f71ce3d | ||
|
28e5ba4e13 | ||
|
e48f9bfcc7 | ||
|
6c44d7f0f6 | ||
|
86649c8c46 | ||
|
3d052c5dc1 | ||
|
41abfbc981 | ||
|
3480b99a45 | ||
|
89dc01c23a | ||
|
8a06819979 | ||
|
d72c1d9c93 | ||
|
078ab63249 | ||
|
f153f15e9f | ||
|
d0f34f9528 | ||
|
da116ea2ad | ||
|
ad87ea4791 | ||
|
19b0e00bf5 | ||
|
315bfd29e5 | ||
|
327bd7d3da | ||
|
b2a7462b44 | ||
|
a4186e2bfd | ||
|
6de26795af | ||
|
2c1a9c471c | ||
|
63d95a53c0 | ||
|
d2c160308c | ||
|
a2fa33f2fb | ||
|
1ebb8599b3 | ||
|
32f87437ec | ||
|
23cd8ad599 | ||
|
defaa154e8 | ||
|
1b813f41f5 | ||
|
d3a8dc85c3 |
12
.github/FUNDING.yml
vendored
Normal file
12
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
# These are supported funding model platforms
|
||||||
|
|
||||||
|
github: # Replace with up to 4 GitHubSponsors-enabled usernames e.g., [user1, user2]
|
||||||
|
patreon: evilsocket
|
||||||
|
open_collective: # Replace with a single Open Collective username
|
||||||
|
ko_fi: # Replace with a single Ko-fi username
|
||||||
|
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||||
|
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||||
|
liberapay: # Replace with a single Liberapay username
|
||||||
|
issuehunt: # Replace with a single IssueHunt username
|
||||||
|
otechie: # Replace with a single Otechie username
|
||||||
|
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
@@ -12,7 +12,9 @@ deploy:
|
|||||||
secure: vBUokTv94n8s65STUgTiD6I0Iy8KXbBRvQUrAof8XG+U4ZMsH5PmDTpS+wz+SaxI6o0PRkfyOiPVdARhiKAFnfatG3q9EHllMQwqRR2YIju51A3aCxgEJ5uWDoybwQdipERUMMYwUO/8XZaRRpwFD2bdQBFWkBtQyMcAkrEL8BXckwQQ531oDN2hK5gAiTllqsOswV2idwUlBRU9jOtStzff+UgUYsp/ZebsRodyOYkEB2Ev15yARo2HTXbyZ2icwHPtMbx5zmNUSRtxs9a4hfzaK3m6ctK8qLYYUdQvXub/ruuACapdw4Ez88LY1agTecbZhFYmJzv8oANH1e4VUI4owuHnZCpU6LRutS4wOhglrkOrGo6lSUlJeA+RtQjyjBugjej9DDtDyyIlRU1ZaBF3qWR9N5EXKuquf0olOfmUR67ap1NykE9VUpzkYjkoVRTiPs/e2onM/nRNOvAQcIt75FD13u+Y/DcYQ8r7KpMIu1HNdtbVx8gMeq76bRhP1YdDg2jm+DdJ21KWjf5QHsbyoXDfJzdKlCloLIlAU3EPJhMoXsnNzre0/FXeUl6dfteR1axNS6U7e/vKsQ9rlUFZWIQaeVPjfXmFKblNNVQ5uFrrsB/EGHcJl7IUx5fvcRT5hMMNwC660YxVkBXDbRb5fxMW5/+K0BOi9cP6en8=
|
secure: vBUokTv94n8s65STUgTiD6I0Iy8KXbBRvQUrAof8XG+U4ZMsH5PmDTpS+wz+SaxI6o0PRkfyOiPVdARhiKAFnfatG3q9EHllMQwqRR2YIju51A3aCxgEJ5uWDoybwQdipERUMMYwUO/8XZaRRpwFD2bdQBFWkBtQyMcAkrEL8BXckwQQ531oDN2hK5gAiTllqsOswV2idwUlBRU9jOtStzff+UgUYsp/ZebsRodyOYkEB2Ev15yARo2HTXbyZ2icwHPtMbx5zmNUSRtxs9a4hfzaK3m6ctK8qLYYUdQvXub/ruuACapdw4Ez88LY1agTecbZhFYmJzv8oANH1e4VUI4owuHnZCpU6LRutS4wOhglrkOrGo6lSUlJeA+RtQjyjBugjej9DDtDyyIlRU1ZaBF3qWR9N5EXKuquf0olOfmUR67ap1NykE9VUpzkYjkoVRTiPs/e2onM/nRNOvAQcIt75FD13u+Y/DcYQ8r7KpMIu1HNdtbVx8gMeq76bRhP1YdDg2jm+DdJ21KWjf5QHsbyoXDfJzdKlCloLIlAU3EPJhMoXsnNzre0/FXeUl6dfteR1axNS6U7e/vKsQ9rlUFZWIQaeVPjfXmFKblNNVQ5uFrrsB/EGHcJl7IUx5fvcRT5hMMNwC660YxVkBXDbRb5fxMW5/+K0BOi9cP6en8=
|
||||||
skip_cleanup: true
|
skip_cleanup: true
|
||||||
file_glob: true
|
file_glob: true
|
||||||
file: pwnagotchi-*.zip
|
file:
|
||||||
|
- pwnagotchi-*.zip
|
||||||
|
- pwnagotchi-*.sha256
|
||||||
on:
|
on:
|
||||||
tags: true
|
tags: true
|
||||||
repo: evilsocket/pwnagotchi
|
repo: evilsocket/pwnagotchi
|
||||||
|
11
Makefile
11
Makefile
@@ -6,17 +6,18 @@ all: install image clean
|
|||||||
install:
|
install:
|
||||||
curl https://releases.hashicorp.com/packer/1.3.5/packer_1.3.5_linux_amd64.zip -o /tmp/packer.zip
|
curl https://releases.hashicorp.com/packer/1.3.5/packer_1.3.5_linux_amd64.zip -o /tmp/packer.zip
|
||||||
unzip /tmp/packer.zip -d /tmp
|
unzip /tmp/packer.zip -d /tmp
|
||||||
mv /tmp/packer /usr/bin/packer
|
sudo mv /tmp/packer /usr/bin/packer
|
||||||
git clone https://github.com/solo-io/packer-builder-arm-image /tmp/packer-builder-arm-image
|
git clone https://github.com/solo-io/packer-builder-arm-image /tmp/packer-builder-arm-image
|
||||||
cd /tmp/packer-builder-arm-image && go get -d ./... && go build
|
cd /tmp/packer-builder-arm-image && go get -d ./... && go build
|
||||||
cp /tmp/packer-builder-arm-image/packer-builder-arm-image /usr/bin
|
sudo cp /tmp/packer-builder-arm-image/packer-builder-arm-image /usr/bin
|
||||||
|
|
||||||
image:
|
image:
|
||||||
cd builder && sudo /usr/bin/packer build -var "pwn_hostname=$(PWN_HOSTNAME)" -var "pwn_version=$(PWN_VERSION)" pwnagotchi.json
|
cd builder && sudo /usr/bin/packer build -var "pwn_hostname=$(PWN_HOSTNAME)" -var "pwn_version=$(PWN_VERSION)" pwnagotchi.json
|
||||||
mv builder/output-pwnagotchi/image pwnagotchi-raspbian-lite-$(PWN_VERSION).img
|
sudo mv builder/output-pwnagotchi/image pwnagotchi-raspbian-lite-$(PWN_VERSION).img
|
||||||
zip pwnagotchi-raspbian-lite-$(PWN_VERSION).zip pwnagotchi-raspbian-lite-$(PWN_VERSION).img
|
sudo sha256sum pwnagotchi-raspbian-lite-$(PWN_VERSION).img > pwnagotchi-raspbian-lite-$(PWN_VERSION).sha256
|
||||||
|
sudo zip pwnagotchi-raspbian-lite-$(PWN_VERSION).zip pwnagotchi-raspbian-lite-$(PWN_VERSION).sha256 pwnagotchi-raspbian-lite-$(PWN_VERSION).img
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf /tmp/packer-builder-arm-image
|
rm -rf /tmp/packer-builder-arm-image
|
||||||
rm -f pwnagotchi-raspbian-lite.img
|
rm -f pwnagotchi-raspbian-lite-*.zip pwnagotchi-raspbian-lite-*.img pwnagotchi-raspbian-lite-*.sha256
|
||||||
rm -rf builder/output-pwnagotchi builder/packer_cache
|
rm -rf builder/output-pwnagotchi builder/packer_cache
|
||||||
|
@@ -33,8 +33,8 @@ if __name__ == '__main__':
|
|||||||
|
|
||||||
plugins.load(config)
|
plugins.load(config)
|
||||||
|
|
||||||
keypair = KeyPair()
|
|
||||||
display = Display(config=config, state={'name': '%s>' % pwnagotchi.name()})
|
display = Display(config=config, state={'name': '%s>' % pwnagotchi.name()})
|
||||||
|
keypair = KeyPair(view=display)
|
||||||
agent = Agent(view=display, config=config, keypair=keypair)
|
agent = Agent(view=display, config=config, keypair=keypair)
|
||||||
|
|
||||||
logging.info("%s@%s (v%s)" % (pwnagotchi.name(), agent._keypair.fingerprint, pwnagotchi.version))
|
logging.info("%s@%s (v%s)" % (pwnagotchi.name(), agent._keypair.fingerprint, pwnagotchi.version))
|
||||||
@@ -78,8 +78,6 @@ if __name__ == '__main__':
|
|||||||
agent.recon()
|
agent.recon()
|
||||||
# get nearby access points grouped by channel
|
# get nearby access points grouped by channel
|
||||||
channels = agent.get_access_points_by_channel()
|
channels = agent.get_access_points_by_channel()
|
||||||
# check for free channels to use
|
|
||||||
agent.check_channels(channels)
|
|
||||||
# for each channel
|
# for each channel
|
||||||
for ch, aps in channels:
|
for ch, aps in channels:
|
||||||
agent.set_channel(ch)
|
agent.set_channel(ch)
|
||||||
|
@@ -11,11 +11,15 @@
|
|||||||
- "dtoverlay=dwc2"
|
- "dtoverlay=dwc2"
|
||||||
- "dtparam=spi=on"
|
- "dtparam=spi=on"
|
||||||
- "dtoverlay=spi1-3cs"
|
- "dtoverlay=spi1-3cs"
|
||||||
|
- "dtoverlay=i2c_arm=on"
|
||||||
|
- "dtoverlay=i2c1=on"
|
||||||
services:
|
services:
|
||||||
enable:
|
enable:
|
||||||
- dphys-swapfile.service
|
- dphys-swapfile.service
|
||||||
- pwnagotchi.service
|
- pwnagotchi.service
|
||||||
- bettercap.service
|
- bettercap.service
|
||||||
|
- pwngrid-peer.service
|
||||||
|
- epd-fuse.service
|
||||||
disable:
|
disable:
|
||||||
- apt-daily.timer
|
- apt-daily.timer
|
||||||
- apt-daily.service
|
- apt-daily.service
|
||||||
@@ -29,7 +33,15 @@
|
|||||||
bettercap:
|
bettercap:
|
||||||
url: "https://github.com/bettercap/bettercap/releases/download/v2.25/bettercap_linux_armv6l_2.25.zip"
|
url: "https://github.com/bettercap/bettercap/releases/download/v2.25/bettercap_linux_armv6l_2.25.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:
|
||||||
|
url: "https://github.com/evilsocket/pwngrid/releases/download/v1.6.3/pwngrid_linux_armv6l_v1.6.3.zip"
|
||||||
apt:
|
apt:
|
||||||
|
hold:
|
||||||
|
- firmware-atheros
|
||||||
|
- firmware-brcm80211
|
||||||
|
- firmware-libertas
|
||||||
|
- firmware-misc-nonfree
|
||||||
|
- firmware-realtek
|
||||||
remove:
|
remove:
|
||||||
- rasberrypi-net-mods
|
- rasberrypi-net-mods
|
||||||
- dhcpcd5
|
- dhcpcd5
|
||||||
@@ -79,6 +91,10 @@
|
|||||||
- fonts-dejavu-core
|
- fonts-dejavu-core
|
||||||
- fonts-dejavu-extra
|
- fonts-dejavu-extra
|
||||||
- python3-pil
|
- python3-pil
|
||||||
|
- python3-smbus
|
||||||
|
- libfuse-dev
|
||||||
|
- bc
|
||||||
|
- fonts-freefont-ttf
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
|
|
||||||
@@ -111,6 +127,12 @@
|
|||||||
repo: deb http://http.re4son-kernel.com/re4son/ kali-pi main
|
repo: deb http://http.re4son-kernel.com/re4son/ kali-pi main
|
||||||
state: present
|
state: present
|
||||||
|
|
||||||
|
- name: add firmware packages to hold
|
||||||
|
dpkg_selections:
|
||||||
|
name: "{{ item }}"
|
||||||
|
selection: hold
|
||||||
|
with_items: "{{ packages.apt.hold }}"
|
||||||
|
|
||||||
- name: update apt package cache
|
- name: update apt package cache
|
||||||
apt:
|
apt:
|
||||||
update_cache: yes
|
update_cache: yes
|
||||||
@@ -135,6 +157,33 @@
|
|||||||
path: /etc/dphys-swapfile
|
path: /etc/dphys-swapfile
|
||||||
content: "CONF_SWAPSIZE=1024"
|
content: "CONF_SWAPSIZE=1024"
|
||||||
|
|
||||||
|
- name: clone papirus repository
|
||||||
|
git:
|
||||||
|
repo: https://github.com/repaper/gratis.git
|
||||||
|
dest: /usr/local/src/gratis
|
||||||
|
|
||||||
|
- name: build papirus service
|
||||||
|
make:
|
||||||
|
chdir: /usr/local/src/gratis
|
||||||
|
target: rpi
|
||||||
|
params:
|
||||||
|
EPD_IO: epd_io.h
|
||||||
|
PANEL_VERSION: 'V231_G2'
|
||||||
|
|
||||||
|
- name: install papirus service
|
||||||
|
make:
|
||||||
|
chdir: /usr/local/src/gratis
|
||||||
|
target: rpi-install
|
||||||
|
params:
|
||||||
|
EPD_IO: epd_io.h
|
||||||
|
PANEL_VERSION: 'V231_G2'
|
||||||
|
|
||||||
|
- name: configure papirus display size
|
||||||
|
lineinfile:
|
||||||
|
dest: /etc/default/epd-fuse
|
||||||
|
regexp: "#EPD_SIZE=2.0"
|
||||||
|
line: "EPD_SIZE=2.0"
|
||||||
|
|
||||||
- name: acquire python3 pip target
|
- name: acquire python3 pip target
|
||||||
command: "python3 -c 'import sys;print(sys.path.pop())'"
|
command: "python3 -c 'import sys;print(sys.path.pop())'"
|
||||||
register: pip_target
|
register: pip_target
|
||||||
@@ -164,6 +213,13 @@
|
|||||||
name: "{{ lookup('fileglob', '/usr/local/src/pwnagotchi/dist/pwnagotchi*.whl') }}"
|
name: "{{ lookup('fileglob', '/usr/local/src/pwnagotchi/dist/pwnagotchi*.whl') }}"
|
||||||
extra_args: "--no-cache-dir"
|
extra_args: "--no-cache-dir"
|
||||||
|
|
||||||
|
- name: download and install pwngrid
|
||||||
|
unarchive:
|
||||||
|
src: "{{ packages.pwngrid.url }}"
|
||||||
|
dest: /usr/bin
|
||||||
|
remote_src: yes
|
||||||
|
mode: 0755
|
||||||
|
|
||||||
- name: download and install bettercap
|
- name: download and install bettercap
|
||||||
unarchive:
|
unarchive:
|
||||||
src: "{{ packages.bettercap.url }}"
|
src: "{{ packages.bettercap.url }}"
|
||||||
@@ -309,34 +365,48 @@
|
|||||||
/opt/vc/bin/tvservice -o
|
/opt/vc/bin/tvservice -o
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- name: create /etc/pwnagotchi/config.yml
|
- name: create /etc/pwnagotchi folder
|
||||||
blockinfile:
|
file:
|
||||||
|
path: /etc/pwnagotchi
|
||||||
|
state: directory
|
||||||
|
|
||||||
|
- name: check if user configuration exists
|
||||||
|
stat:
|
||||||
path: /etc/pwnagotchi/config.yml
|
path: /etc/pwnagotchi/config.yml
|
||||||
create: yes
|
register: user_config
|
||||||
block: |
|
|
||||||
# put here your custom configuration overrides
|
- name: create /etc/pwnagotchi/config.yml
|
||||||
|
copy:
|
||||||
|
dest: /etc/pwnagotchi/config.yml
|
||||||
|
content: |
|
||||||
|
# Add your configuration overrides on this file any configuration changes done to defaults.yml will be lost!
|
||||||
|
# Example:
|
||||||
|
#
|
||||||
|
# ui:
|
||||||
|
# display:
|
||||||
|
# type: 'inkyphat'
|
||||||
|
# color: 'black'
|
||||||
|
#
|
||||||
|
when: not user_config.stat.exists
|
||||||
|
|
||||||
- name: configure lo interface
|
- name: configure lo interface
|
||||||
blockinfile:
|
copy:
|
||||||
path: /etc/network/interfaces.d/lo-cfg
|
dest: /etc/network/interfaces.d/lo-cfg
|
||||||
create: yes
|
content: |
|
||||||
block: |
|
|
||||||
auto lo
|
auto lo
|
||||||
iface lo inet loopback
|
iface lo inet loopback
|
||||||
|
|
||||||
- name: configure wlan interface
|
- name: configure wlan interface
|
||||||
blockinfile:
|
copy:
|
||||||
path: /etc/network/interfaces.d/wlan0-cfg
|
dest: /etc/network/interfaces.d/wlan0-cfg
|
||||||
create: yes
|
content: |
|
||||||
block: |
|
|
||||||
allow-hotplug wlan0
|
allow-hotplug wlan0
|
||||||
iface wlan0 inet static
|
iface wlan0 inet static
|
||||||
|
|
||||||
- name: configure usb interface
|
- name: configure usb interface
|
||||||
blockinfile:
|
copy:
|
||||||
path: /etc/network/interfaces.d/usb0-cfg
|
dest: /etc/network/interfaces.d/usb0-cfg
|
||||||
create: yes
|
content: |
|
||||||
block: |
|
|
||||||
allow-hotplug usb0
|
allow-hotplug usb0
|
||||||
iface usb0 inet static
|
iface usb0 inet static
|
||||||
address 10.0.0.2
|
address 10.0.0.2
|
||||||
@@ -346,10 +416,9 @@
|
|||||||
gateway 10.0.0.1
|
gateway 10.0.0.1
|
||||||
|
|
||||||
- name: configure eth0 interface (pi2/3/4)
|
- name: configure eth0 interface (pi2/3/4)
|
||||||
blockinfile:
|
copy:
|
||||||
path: /etc/network/interfaces.d/eth0-cfg
|
dest: /etc/network/interfaces.d/eth0-cfg
|
||||||
create: yes
|
content: |
|
||||||
block: |
|
|
||||||
allow-hotplug eth0
|
allow-hotplug eth0
|
||||||
iface eth0 inet dhcp
|
iface eth0 inet dhcp
|
||||||
|
|
||||||
@@ -363,8 +432,7 @@
|
|||||||
dest: /boot/config.txt
|
dest: /boot/config.txt
|
||||||
insertafter: EOF
|
insertafter: EOF
|
||||||
line: '{{ item }}'
|
line: '{{ item }}'
|
||||||
with_items:
|
with_items: "{{system.boot_options}}"
|
||||||
- "{{system.boot_options}}"
|
|
||||||
|
|
||||||
- name: change root partition
|
- name: change root partition
|
||||||
replace:
|
replace:
|
||||||
@@ -385,7 +453,33 @@
|
|||||||
- name: configure motd
|
- name: configure motd
|
||||||
copy:
|
copy:
|
||||||
dest: /etc/motd
|
dest: /etc/motd
|
||||||
content: "(◕‿‿◕) {{pwnagotchi.hostname}} (pwnagotchi-{{pwnagotchi.version}})"
|
content: |
|
||||||
|
(◕‿‿◕) {{pwnagotchi.hostname}} (pwnagotchi-{{pwnagotchi.version}})
|
||||||
|
|
||||||
|
Hi! I'm a pwnagotchi, please take good care of me!
|
||||||
|
Here are some basic things you need to know to raise me properly!
|
||||||
|
|
||||||
|
If you want to change my configuration, use /etc/pwnagotchi/config.yml
|
||||||
|
|
||||||
|
All the configuration options can be found on /etc/pwnagotchi/defaults.yml,
|
||||||
|
but don't change this file because I will recreate it every time I'm restarted!
|
||||||
|
|
||||||
|
I'm managed by systemd. Here are some basic commands.
|
||||||
|
|
||||||
|
If you want to know what I'm doing, you can check my logs with the command
|
||||||
|
journalctl -fu pwnagotchi
|
||||||
|
|
||||||
|
If you want to know if I'm running, you can use
|
||||||
|
systemctl status pwnagotchi
|
||||||
|
|
||||||
|
You can restart me using
|
||||||
|
systemctl restart pwnagotchi
|
||||||
|
|
||||||
|
But be aware I will go into MANUAL mode when restarted!
|
||||||
|
You can put me back into AUTO mode using
|
||||||
|
touch /root/.pwnagotchi-auto && systemctl restart pwnagotchi
|
||||||
|
|
||||||
|
You learn more about me at https://pwnagotchi.ai/
|
||||||
|
|
||||||
- name: clean apt cache
|
- name: clean apt cache
|
||||||
apt:
|
apt:
|
||||||
@@ -395,6 +489,28 @@
|
|||||||
apt:
|
apt:
|
||||||
autoremove: yes
|
autoremove: yes
|
||||||
|
|
||||||
|
- name: add pwngrid-peer service to systemd
|
||||||
|
copy:
|
||||||
|
dest: /etc/systemd/system/pwngrid-peer.service
|
||||||
|
content: |
|
||||||
|
[Unit]
|
||||||
|
Description=pwngrid peer service.
|
||||||
|
Documentation=https://pwnagotchi.ai
|
||||||
|
Wants=network.target
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
PermissionsStartOnly=true
|
||||||
|
ExecStart=/usr/bin/pwngrid -keys /etc/pwnagotchi -address 127.0.0.1:8666 -wait -log /var/log/pwngrid-peer.log
|
||||||
|
Restart=always
|
||||||
|
RestartSec=30
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
notify:
|
||||||
|
- reload systemd services
|
||||||
|
|
||||||
- name: add bettercap service to systemd
|
- name: add bettercap service to systemd
|
||||||
copy:
|
copy:
|
||||||
dest: /etc/systemd/system/bettercap.service
|
dest: /etc/systemd/system/bettercap.service
|
||||||
@@ -466,5 +582,3 @@
|
|||||||
- name: reload systemd services
|
- name: reload systemd services
|
||||||
systemd:
|
systemd:
|
||||||
daemon_reload: yes
|
daemon_reload: yes
|
||||||
|
|
||||||
|
|
||||||
|
@@ -4,7 +4,7 @@ import logging
|
|||||||
import time
|
import time
|
||||||
import pwnagotchi.ui.view as view
|
import pwnagotchi.ui.view as view
|
||||||
|
|
||||||
version = '1.0.0RC2'
|
version = '1.0.0RC3'
|
||||||
|
|
||||||
_name = None
|
_name = None
|
||||||
|
|
||||||
|
@@ -160,23 +160,6 @@ class Agent(Client, AsyncAdvertiser, AsyncTrainer):
|
|||||||
self._view.wait(t, sleeping)
|
self._view.wait(t, sleeping)
|
||||||
self._epoch.track(sleep=True, inc=t)
|
self._epoch.track(sleep=True, inc=t)
|
||||||
|
|
||||||
def check_channels(self, channels):
|
|
||||||
busy_channels = [ch for ch, aps in channels]
|
|
||||||
# if we're hopping and no filter is configured
|
|
||||||
if self._config['personality']['channels'] == [] and self._config['main']['filter'] is None:
|
|
||||||
# check if any of the non overlapping channels is free
|
|
||||||
for ch in self._epoch.non_overlapping_channels:
|
|
||||||
if ch not in busy_channels:
|
|
||||||
self._epoch.non_overlapping_channels[ch] += 1
|
|
||||||
logging.info("channel %d is free from %d epochs" % (ch, self._epoch.non_overlapping_channels[ch]))
|
|
||||||
elif self._epoch.non_overlapping_channels[ch] > 0:
|
|
||||||
self._epoch.non_overlapping_channels[ch] -= 1
|
|
||||||
# report any channel that has been free for at least 3 epochs
|
|
||||||
for ch, num_epochs_free in self._epoch.non_overlapping_channels.items():
|
|
||||||
if num_epochs_free >= 3:
|
|
||||||
logging.info("channel %d has been free for %d epochs" % (ch, num_epochs_free))
|
|
||||||
self.set_free_channel(ch)
|
|
||||||
|
|
||||||
def recon(self):
|
def recon(self):
|
||||||
recon_time = self._config['personality']['recon_time']
|
recon_time = self._config['personality']['recon_time']
|
||||||
max_inactive = self._config['personality']['max_inactive_scale']
|
max_inactive = self._config['personality']['max_inactive_scale']
|
||||||
|
@@ -1,6 +1,12 @@
|
|||||||
|
# WARNING WARNING WARNING WARNING
|
||||||
|
#
|
||||||
|
# This file is recreated with default settings on every pwnagotchi restart,
|
||||||
|
# use /etc/pwnagotchi/config.yml to configure this unit.
|
||||||
|
#
|
||||||
|
#
|
||||||
# main algorithm configuration
|
# main algorithm configuration
|
||||||
main:
|
main:
|
||||||
# currently implemented: en (default), de, el, fr, it, mk, nl, ru, se
|
# currently implemented: en (default), de, el, fr, it, mk, nl, ru, se, pt-BR, es, pt
|
||||||
lang: en
|
lang: en
|
||||||
# custom plugins path, if null only default plugins with be loaded
|
# custom plugins path, if null only default plugins with be loaded
|
||||||
custom_plugins:
|
custom_plugins:
|
||||||
@@ -55,7 +61,11 @@ main:
|
|||||||
screen_refresh:
|
screen_refresh:
|
||||||
enabled: false
|
enabled: false
|
||||||
refresh_interval: 50
|
refresh_interval: 50
|
||||||
|
quickdic:
|
||||||
|
enabled: false
|
||||||
|
wordlist_folder: /opt/wordlists/
|
||||||
|
AircrackOnly:
|
||||||
|
enabled: false
|
||||||
# monitor interface to use
|
# monitor interface to use
|
||||||
iface: mon0
|
iface: mon0
|
||||||
# command to run to bring the mon interface up in case it's not up already
|
# command to run to bring the mon interface up in case it's not up already
|
||||||
@@ -72,8 +82,6 @@ main:
|
|||||||
- ANOTHER_EXAMPLE_NETWORK
|
- ANOTHER_EXAMPLE_NETWORK
|
||||||
# if not null, filter access points by this regular expression
|
# if not null, filter access points by this regular expression
|
||||||
filter: null
|
filter: null
|
||||||
# cryptographic key for identity
|
|
||||||
pubkey: /etc/ssh/ssh_host_rsa_key.pub
|
|
||||||
|
|
||||||
ai:
|
ai:
|
||||||
# if false, only the default 'personality' will be used
|
# if false, only the default 'personality' will be used
|
||||||
|
@@ -10,38 +10,58 @@ DefaultPath = "/etc/pwnagotchi/"
|
|||||||
|
|
||||||
|
|
||||||
class KeyPair(object):
|
class KeyPair(object):
|
||||||
def __init__(self, path=DefaultPath):
|
def __init__(self, path=DefaultPath, view=None):
|
||||||
self.path = path
|
self.path = path
|
||||||
self.priv_path = os.path.join(path, "id_rsa")
|
self.priv_path = os.path.join(path, "id_rsa")
|
||||||
self.priv_key = None
|
self.priv_key = None
|
||||||
self.pub_path = "%s.pub" % self.priv_path
|
self.pub_path = "%s.pub" % self.priv_path
|
||||||
self.pub_key = None
|
self.pub_key = None
|
||||||
|
self._view = view
|
||||||
|
|
||||||
if not os.path.exists(self.path):
|
if not os.path.exists(self.path):
|
||||||
os.makedirs(self.path)
|
os.makedirs(self.path)
|
||||||
|
|
||||||
if not os.path.exists(self.priv_path) or not os.path.exists(self.pub_path):
|
while True:
|
||||||
logging.info("generating %s ..." % self.priv_path)
|
# first time, generate new keys
|
||||||
os.system("/usr/bin/ssh-keygen -t rsa -m PEM -b 4096 -N '' -f '%s'" % self.priv_path)
|
if not os.path.exists(self.priv_path) or not os.path.exists(self.pub_path):
|
||||||
|
self._view.on_keys_generation()
|
||||||
|
logging.info("generating %s ..." % self.priv_path)
|
||||||
|
os.system("/usr/bin/ssh-keygen -t rsa -m PEM -b 4096 -N '' -f '%s'" % self.priv_path)
|
||||||
|
|
||||||
with open(self.priv_path) as fp:
|
# load keys: they might be corrupted if the unit has been turned off during the generation, in this case
|
||||||
self.priv_key = RSA.importKey(fp.read())
|
# the exception will remove the files and go back at the beginning of this loop.
|
||||||
|
try:
|
||||||
|
with open(self.priv_path) as fp:
|
||||||
|
self.priv_key = RSA.importKey(fp.read())
|
||||||
|
|
||||||
with open(self.pub_path) as fp:
|
with open(self.pub_path) as fp:
|
||||||
self.pub_key = RSA.importKey(fp.read())
|
self.pub_key = RSA.importKey(fp.read())
|
||||||
self.pub_key_pem = self.pub_key.exportKey('PEM').decode("ascii")
|
self.pub_key_pem = self.pub_key.exportKey('PEM').decode("ascii")
|
||||||
# python is special
|
# python is special
|
||||||
if 'RSA PUBLIC KEY' not in self.pub_key_pem:
|
if 'RSA PUBLIC KEY' not in self.pub_key_pem:
|
||||||
self.pub_key_pem = self.pub_key_pem.replace('PUBLIC KEY', 'RSA PUBLIC KEY')
|
self.pub_key_pem = self.pub_key_pem.replace('PUBLIC KEY', 'RSA PUBLIC KEY')
|
||||||
|
|
||||||
pem = self.pub_key_pem.encode("ascii")
|
pem_ascii = self.pub_key_pem.encode("ascii")
|
||||||
|
|
||||||
self.pub_key_pem_b64 = base64.b64encode(pem).decode("ascii")
|
self.pub_key_pem_b64 = base64.b64encode(pem_ascii).decode("ascii")
|
||||||
self.fingerprint = hashlib.sha256(pem).hexdigest()
|
self.fingerprint = hashlib.sha256(pem_ascii).hexdigest()
|
||||||
|
|
||||||
|
# no exception, keys loaded correctly.
|
||||||
|
self._view.on_starting()
|
||||||
|
return
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
# if we're here, loading the keys broke something ...
|
||||||
|
logging.exception("error loading keys, maybe corrupted, deleting and regenerating ...")
|
||||||
|
try:
|
||||||
|
os.remove(self.priv_path)
|
||||||
|
os.remove(self.pub_path)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
def sign(self, message):
|
def sign(self, message):
|
||||||
hasher = SHA256.new(message.encode("ascii"))
|
hasher = SHA256.new(message.encode("ascii"))
|
||||||
signer = PKCS1_PSS.new(self.priv_key, saltLen=16)
|
signer = PKCS1_PSS.new(self.priv_key, saltLen=16)
|
||||||
signature = signer.sign(hasher)
|
signature = signer.sign(hasher)
|
||||||
signature_b64 = base64.b64encode(signature).decode("ascii")
|
signature_b64 = base64.b64encode(signature).decode("ascii")
|
||||||
return signature, signature_b64
|
return signature, signature_b64
|
||||||
|
Binary file not shown.
@@ -7,7 +7,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: 0.0.1\n"
|
"Project-Id-Version: 0.0.1\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2019-10-05 14:10+0200\n"
|
"POT-Creation-Date: 2019-10-09 17:42+0200\n"
|
||||||
"PO-Revision-Date: 2019-09-29 14:00+0200\n"
|
"PO-Revision-Date: 2019-09-29 14:00+0200\n"
|
||||||
"Last-Translator: dadav <33197631+dadav@users.noreply.github.com>\n"
|
"Last-Translator: dadav <33197631+dadav@users.noreply.github.com>\n"
|
||||||
"Language-Team: DE <33197631+dadav@users.noreply.github.com>\n"
|
"Language-Team: DE <33197631+dadav@users.noreply.github.com>\n"
|
||||||
@@ -121,6 +121,12 @@ msgstr ""
|
|||||||
msgid "ZzzZzzz ({secs}s)"
|
msgid "ZzzZzzz ({secs}s)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Good night."
|
||||||
|
msgstr "Gute Nacht."
|
||||||
|
|
||||||
|
msgid "Zzz"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Waiting for {secs}s ..."
|
msgid "Waiting for {secs}s ..."
|
||||||
msgstr "Warte für {secs}s ..."
|
msgstr "Warte für {secs}s ..."
|
||||||
@@ -139,7 +145,7 @@ msgstr "Verbinde mit {what}"
|
|||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Yo {what}!"
|
msgid "Yo {what}!"
|
||||||
msgstr ""
|
msgstr "Jo {what}!"
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Just decided that {mac} needs no WiFi!"
|
msgid "Just decided that {mac} needs no WiFi!"
|
||||||
|
BIN
pwnagotchi/locale/es/LC_MESSAGES/voice.mo
Normal file
BIN
pwnagotchi/locale/es/LC_MESSAGES/voice.mo
Normal file
Binary file not shown.
214
pwnagotchi/locale/es/LC_MESSAGES/voice.po
Normal file
214
pwnagotchi/locale/es/LC_MESSAGES/voice.po
Normal file
@@ -0,0 +1,214 @@
|
|||||||
|
# pwnagotchi voice data
|
||||||
|
# Copyright (C) 2019
|
||||||
|
# This file is distributed under the same license as the pwnagotchi package.
|
||||||
|
# FIRST AUTHOR diegopastor <dpastor29@alumnos.uaq.mx>, 2019.
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: 0.0.1\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2019-10-09 17:42+0200\n"
|
||||||
|
"PO-Revision-Date: 2019-10-09 21:07+0000\n"
|
||||||
|
"Last-Translator: diegopastor <dpastor29@alumnos.uaq.mx>\n"
|
||||||
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
|
"Language: spanish\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=utf-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
|
||||||
|
msgid "ZzzzZZzzzzZzzz"
|
||||||
|
msgstr "ZzzzZZzzzzZzzz"
|
||||||
|
|
||||||
|
msgid "Hi, I'm Pwnagotchi! Starting ..."
|
||||||
|
msgstr "Hola, soy Pwnagotchi! Empezando ..."
|
||||||
|
|
||||||
|
msgid "New day, new hunt, new pwns!"
|
||||||
|
msgstr "Nuevo día, nueva cazería, nuevos pwns!"
|
||||||
|
|
||||||
|
msgid "Hack the Planet!"
|
||||||
|
msgstr "Hackea el planeta!"
|
||||||
|
|
||||||
|
msgid "AI ready."
|
||||||
|
msgstr "IA lista."
|
||||||
|
|
||||||
|
msgid "The neural network is ready."
|
||||||
|
msgstr "La red neuronal está lista."
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Hey, channel {channel} is free! Your AP will say thanks."
|
||||||
|
msgstr "Oye, el canal {channel} está libre! Tú AP lo agradecerá."
|
||||||
|
|
||||||
|
msgid "I'm bored ..."
|
||||||
|
msgstr "Estoy aburrido ..."
|
||||||
|
|
||||||
|
msgid "Let's go for a walk!"
|
||||||
|
msgstr "Vamos por un paseo!"
|
||||||
|
|
||||||
|
msgid "This is the best day of my life!"
|
||||||
|
msgstr "Este es el mejor día de mi vida!"
|
||||||
|
|
||||||
|
msgid "Shitty day :/"
|
||||||
|
msgstr "Día de mierda :/"
|
||||||
|
|
||||||
|
msgid "I'm extremely bored ..."
|
||||||
|
msgstr "Estoy extremadamente aburrido ..."
|
||||||
|
|
||||||
|
msgid "I'm very sad ..."
|
||||||
|
msgstr "Estoy muy triste ..."
|
||||||
|
|
||||||
|
msgid "I'm sad"
|
||||||
|
msgstr "Estoy triste."
|
||||||
|
|
||||||
|
msgid "I'm living the life!"
|
||||||
|
msgstr "Estoy viviendo la vida!"
|
||||||
|
|
||||||
|
msgid "I pwn therefore I am."
|
||||||
|
msgstr "Pwneo, por lo tanto, existo"
|
||||||
|
|
||||||
|
msgid "So many networks!!!"
|
||||||
|
msgstr "Cuantas redes!!!"
|
||||||
|
|
||||||
|
msgid "I'm having so much fun!"
|
||||||
|
msgstr "Me estoy divirtiendo mucho!"
|
||||||
|
|
||||||
|
msgid "My crime is that of curiosity ..."
|
||||||
|
msgstr "Mi único crimen es la curiosidad ..."
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Hello {name}! Nice to meet you. {name}"
|
||||||
|
msgstr "Hola {name}! encantado de conocerte."
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Unit {name} is nearby! {name}"
|
||||||
|
msgstr "La unidad {name} está cerca!"
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Uhm ... goodbye {name}"
|
||||||
|
msgstr "Uhm ... adiós {name}"
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "{name} is gone ..."
|
||||||
|
msgstr "{name} se fue ..."
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Whoops ... {name} is gone."
|
||||||
|
msgstr "Uy ... {name} se fue"
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "{name} missed!"
|
||||||
|
msgstr "{name} perdido!"
|
||||||
|
|
||||||
|
msgid "Missed!"
|
||||||
|
msgstr "Perdido!"
|
||||||
|
|
||||||
|
msgid "Nobody wants to play with me ..."
|
||||||
|
msgstr "Nadie quiere jugar conmigo ..."
|
||||||
|
|
||||||
|
msgid "I feel so alone ..."
|
||||||
|
msgstr "Me siento tan solo ..."
|
||||||
|
|
||||||
|
msgid "Where's everybody?!"
|
||||||
|
msgstr "Dónde está todo el mundo?"
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Napping for {secs}s ..."
|
||||||
|
msgstr "Tomándo una siesta por {secs}s ..."
|
||||||
|
|
||||||
|
msgid "Zzzzz"
|
||||||
|
msgstr "Zzzzz"
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "ZzzZzzz ({secs}s)"
|
||||||
|
msgstr "ZzzZzzz ({secs}s)"
|
||||||
|
|
||||||
|
msgid "Good night."
|
||||||
|
msgstr "Buenas noches."
|
||||||
|
|
||||||
|
msgid "Zzz"
|
||||||
|
msgstr "Zzz"
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Waiting for {secs}s ..."
|
||||||
|
msgstr "Esperando {secs}s .."
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Looking around ({secs}s)"
|
||||||
|
msgstr "Mirando al rededor ({secs}s)"
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Hey {what} let's be friends!"
|
||||||
|
msgstr "Oye {what} seamos amigos!"
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Associating to {what}"
|
||||||
|
msgstr "Asociando a {what}"
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Yo {what}!"
|
||||||
|
msgstr "Ey {what}!"
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Just decided that {mac} needs no WiFi!"
|
||||||
|
msgstr "Acabo de decidir que {mac} no necesita WiFi!"
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Deauthenticating {mac}"
|
||||||
|
msgstr "Desautenticando a {mac}"
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Kickbanning {mac}!"
|
||||||
|
msgstr "Expulsando y banneando a {mac}!"
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Cool, we got {num} new handshake{plural}!"
|
||||||
|
msgstr "Genial, obtuvimos {num} nuevo{plural} handshake{plural}!"
|
||||||
|
|
||||||
|
msgid "Ops, something went wrong ... Rebooting ..."
|
||||||
|
msgstr "Oops, algo salió mal ... Reiniciándo ..."
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Kicked {num} stations\n"
|
||||||
|
msgstr "Expulsamos {num} estaciones\n"
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Made {num} new friends\n"
|
||||||
|
msgstr "Hicimos {num} nuevos amigos\n"
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Got {num} handshakes\n"
|
||||||
|
msgstr "Obtuvimos {num} handshakes\n"
|
||||||
|
|
||||||
|
msgid "Met 1 peer"
|
||||||
|
msgstr "Conocí 1 igual"
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Met {num} peers"
|
||||||
|
msgstr "Conocí {num} iguales"
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid ""
|
||||||
|
"I've been pwning for {duration} and kicked {deauthed} clients! I've also met "
|
||||||
|
"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi "
|
||||||
|
"#pwnlog #pwnlife #hacktheplanet #skynet"
|
||||||
|
msgstr ""
|
||||||
|
"He estado pwneando por {duration} y expulsé {deauthed} clientes! También conocí"
|
||||||
|
"{associated} nuevos amigos y me comí {handshakes} handshakes! #pwnagotchi "
|
||||||
|
"#pwnlog #pwnlife #hacktheplanet #skynet"
|
||||||
|
|
||||||
|
msgid "hours"
|
||||||
|
msgstr "horas"
|
||||||
|
|
||||||
|
msgid "minutes"
|
||||||
|
msgstr "minutos"
|
||||||
|
|
||||||
|
msgid "seconds"
|
||||||
|
msgstr "segundos"
|
||||||
|
|
||||||
|
msgid "hour"
|
||||||
|
msgstr "hora"
|
||||||
|
|
||||||
|
msgid "minute"
|
||||||
|
msgstr "minuto"
|
||||||
|
|
||||||
|
msgid "second"
|
||||||
|
msgstr "segundo"
|
BIN
pwnagotchi/locale/pt-BR/LC_MESSAGES/voice.mo
Normal file
BIN
pwnagotchi/locale/pt-BR/LC_MESSAGES/voice.mo
Normal file
Binary file not shown.
209
pwnagotchi/locale/pt-BR/LC_MESSAGES/voice.po
Normal file
209
pwnagotchi/locale/pt-BR/LC_MESSAGES/voice.po
Normal file
@@ -0,0 +1,209 @@
|
|||||||
|
# pwnagotchi Brazilian Portuguese translation file.
|
||||||
|
# Copyright (C) 2019 Cassiano Aquino
|
||||||
|
# This file is distributed under the same license as the pwnagotchi package.
|
||||||
|
# Cassiano Aquino <cassianoaquino@me.com>, 2019.
|
||||||
|
#
|
||||||
|
#, fuzzy
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2019-10-05 14:10+0200\n"
|
||||||
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
|
"Last-Translator: Cassiano Aquino <cassianoaquino@me.com>\n"
|
||||||
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
|
"Language: Brazilian Portuguese\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=utf-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
|
||||||
|
msgid "ZzzzZZzzzzZzzz"
|
||||||
|
msgstr "ZzzzZZzzzzZzzz"
|
||||||
|
|
||||||
|
msgid "Hi, I'm Pwnagotchi! Starting ..."
|
||||||
|
msgstr "Oi! Eu sou o Pwnagotchi! Iniciando ..."
|
||||||
|
|
||||||
|
msgid "New day, new hunt, new pwns!"
|
||||||
|
msgstr "Novo dia, Nova caça, Novos pwns!"
|
||||||
|
|
||||||
|
msgid "Hack the Planet!"
|
||||||
|
msgstr "Hackeie o Planeta!"
|
||||||
|
|
||||||
|
msgid "AI ready."
|
||||||
|
msgstr "AI pronta."
|
||||||
|
|
||||||
|
msgid "The neural network is ready."
|
||||||
|
msgstr "A rede neural está pronta."
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Hey, channel {channel} is free! Your AP will say thanks."
|
||||||
|
msgstr "Ei, o canal {channel} está livre! Seu AP ira agradecer."
|
||||||
|
|
||||||
|
msgid "I'm bored ..."
|
||||||
|
msgstr "Estou entediado ..."
|
||||||
|
|
||||||
|
msgid "Let's go for a walk!"
|
||||||
|
msgstr "Vamos dar uma caminhada!"
|
||||||
|
|
||||||
|
msgid "This is the best day of my life!"
|
||||||
|
msgstr "Este e o melhor dia da minha vida!"
|
||||||
|
|
||||||
|
msgid "Shitty day :/"
|
||||||
|
msgstr "Dia de merda :/"
|
||||||
|
|
||||||
|
msgid "I'm extremely bored ..."
|
||||||
|
msgstr "Estou extremamente entediado ..."
|
||||||
|
|
||||||
|
msgid "I'm very sad ..."
|
||||||
|
msgstr "Estou muito triste ..."
|
||||||
|
|
||||||
|
msgid "I'm sad"
|
||||||
|
msgstr "Estou triste"
|
||||||
|
|
||||||
|
msgid "I'm living the life!"
|
||||||
|
msgstr "Estou aproveitando a vida!"
|
||||||
|
|
||||||
|
msgid "I pwn therefore I am."
|
||||||
|
msgstr "pwn, logo existo."
|
||||||
|
|
||||||
|
msgid "So many networks!!!"
|
||||||
|
msgstr "Quantas redes!!!"
|
||||||
|
|
||||||
|
msgid "I'm having so much fun!"
|
||||||
|
msgstr "Estou me divertindo muito!"
|
||||||
|
|
||||||
|
msgid "My crime is that of curiosity ..."
|
||||||
|
msgstr "Meu crime é ser curioso ..."
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Hello {name}! Nice to meet you. {name}"
|
||||||
|
msgstr "Olá {name}! Prazer em conhecê-lo. {name}"
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Unit {name} is nearby! {name}"
|
||||||
|
msgstr "Unidade {name} está próxima! {name}"
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Uhm ... goodbye {name}"
|
||||||
|
msgstr "Uhm ... até logo {name}"
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "{name} is gone ..."
|
||||||
|
msgstr "{name} desapareceu ..."
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Whoops ... {name} is gone."
|
||||||
|
msgstr "Oops ... {name} desapareceu."
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "{name} missed!"
|
||||||
|
msgstr "{name} perdido!"
|
||||||
|
|
||||||
|
msgid "Missed!"
|
||||||
|
msgstr "Perdido!"
|
||||||
|
|
||||||
|
msgid "Nobody wants to play with me ..."
|
||||||
|
msgstr "Ninguém quer brincar comigo ..."
|
||||||
|
|
||||||
|
msgid "I feel so alone ..."
|
||||||
|
msgstr "Estou tão sozinho ..."
|
||||||
|
|
||||||
|
msgid "Where's everybody?!"
|
||||||
|
msgstr "Aonde está todo mundo?!"
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Napping for {secs}s ..."
|
||||||
|
msgstr "Cochilando por {secs}s ..."
|
||||||
|
|
||||||
|
msgid "Zzzzz"
|
||||||
|
msgstr "Zzzzz"
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "ZzzZzzz ({secs}s)"
|
||||||
|
msgstr "ZzzZzzz ({secs}s)"
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Waiting for {secs}s ..."
|
||||||
|
msgstr "Aguardando por {secs}s ..."
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Looking around ({secs}s)"
|
||||||
|
msgstr "Olhando ao redor ({secs}s)"
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Hey {what} let's be friends!"
|
||||||
|
msgstr "Ei {what} vamos ser amigos!"
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Associating to {what}"
|
||||||
|
msgstr "Associando com {what}"
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Yo {what}!"
|
||||||
|
msgstr "Oi {what}!"
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Just decided that {mac} needs no WiFi!"
|
||||||
|
msgstr "Acabei de decidir que {mac} não precisa de WiFi!"
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Deauthenticating {mac}"
|
||||||
|
msgstr "De-autenticando {mac}"
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Kickbanning {mac}!"
|
||||||
|
msgstr "Kickbanning {mac}"
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Cool, we got {num} new handshake{plural}!"
|
||||||
|
msgstr "Legal, nos capturamos {num} handshake{plural} novo{plural}!"
|
||||||
|
|
||||||
|
msgid "Ops, something went wrong ... Rebooting ..."
|
||||||
|
msgstr "Ops, algo falhou ... Reiniciando ..."
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Kicked {num} stations\n"
|
||||||
|
msgstr "Kickei {num} estações\n"
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Made {num} new friends\n"
|
||||||
|
msgstr "Fiz {num} novos amigos\n"
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Got {num} handshakes\n"
|
||||||
|
msgstr "Peguei {num} handshakes\n"
|
||||||
|
|
||||||
|
msgid "Met 1 peer"
|
||||||
|
msgstr "Conheci 1 peer"
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Met {num} peers"
|
||||||
|
msgstr "Conheci {num} peers"
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid ""
|
||||||
|
"I've been pwning for {duration} and kicked {deauthed} clients! I've also met "
|
||||||
|
"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi "
|
||||||
|
"#pwnlog #pwnlife #hacktheplanet #skynet"
|
||||||
|
msgstr ""
|
||||||
|
"Eu estou pwning fazem {duration} e kickei {deauthed} clientes! Eu também conheci "
|
||||||
|
"{associated} novos amigos e comi {handshakes} handshakes! #pwnagotchi "
|
||||||
|
"#pwnlog #pwnlife #hacktheplanet #skynet"
|
||||||
|
|
||||||
|
msgid "hours"
|
||||||
|
msgstr "horas"
|
||||||
|
|
||||||
|
msgid "minutes"
|
||||||
|
msgstr "minutos"
|
||||||
|
|
||||||
|
msgid "seconds"
|
||||||
|
msgstr "segundos"
|
||||||
|
|
||||||
|
msgid "hour"
|
||||||
|
msgstr "hora"
|
||||||
|
|
||||||
|
msgid "minute"
|
||||||
|
msgstr "minuto"
|
||||||
|
|
||||||
|
msgid "second"
|
||||||
|
msgstr "segundo"
|
BIN
pwnagotchi/locale/pt/LC_MESSAGES/voice.mo
Normal file
BIN
pwnagotchi/locale/pt/LC_MESSAGES/voice.mo
Normal file
Binary file not shown.
214
pwnagotchi/locale/pt/LC_MESSAGES/voice.po
Normal file
214
pwnagotchi/locale/pt/LC_MESSAGES/voice.po
Normal file
@@ -0,0 +1,214 @@
|
|||||||
|
# pwnagotchi Portuguese (european) translation file.
|
||||||
|
# Copyright (C) 2019 David Sopas
|
||||||
|
# This file is distributed under the same license as the PACKAGE package.
|
||||||
|
# David Sopas <email@aleatorio.xyz>, 2019.
|
||||||
|
#
|
||||||
|
#, fuzzy
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2019-10-09 17:42+0200\n"
|
||||||
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
|
"Last-Translator: David Sopas <email@aleatorio.xyz>\n"
|
||||||
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
|
"Language: Portuguese\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=CHARSET\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
|
||||||
|
msgid "ZzzzZZzzzzZzzz"
|
||||||
|
msgstr "ZzzzZZzzzzZzzz"
|
||||||
|
|
||||||
|
msgid "Hi, I'm Pwnagotchi! Starting ..."
|
||||||
|
msgstr "Olá, eu sou o Pwnagotchi! A iniciar ..."
|
||||||
|
|
||||||
|
msgid "New day, new hunt, new pwns!"
|
||||||
|
msgstr "Novo dia, nova caçada, novos pwns!"
|
||||||
|
|
||||||
|
msgid "Hack the Planet!"
|
||||||
|
msgstr "Hacka o Planeta!"
|
||||||
|
|
||||||
|
msgid "AI ready."
|
||||||
|
msgstr "IA pronta."
|
||||||
|
|
||||||
|
msgid "The neural network is ready."
|
||||||
|
msgstr "A rede neural está pronta."
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Hey, channel {channel} is free! Your AP will say thanks."
|
||||||
|
msgstr "Hey, o canal {channel} está livre! O teu AP irá agradecer."
|
||||||
|
|
||||||
|
msgid "I'm bored ..."
|
||||||
|
msgstr "Estou aborrecido ..."
|
||||||
|
|
||||||
|
msgid "Let's go for a walk!"
|
||||||
|
msgstr "Vamos fazer uma caminhada!"
|
||||||
|
|
||||||
|
msgid "This is the best day of my life!"
|
||||||
|
msgstr "Este é o melhor dia da minha vida!"
|
||||||
|
|
||||||
|
msgid "Shitty day :/"
|
||||||
|
msgstr "Que merda de dia :/"
|
||||||
|
|
||||||
|
msgid "I'm extremely bored ..."
|
||||||
|
msgstr "Estou muito aborrecido ..."
|
||||||
|
|
||||||
|
msgid "I'm very sad ..."
|
||||||
|
msgstr "Estou muito triste ..."
|
||||||
|
|
||||||
|
msgid "I'm sad"
|
||||||
|
msgstr "Estou triste"
|
||||||
|
|
||||||
|
msgid "I'm living the life!"
|
||||||
|
msgstr "Estou aproveitar a vida!"
|
||||||
|
|
||||||
|
msgid "I pwn therefore I am."
|
||||||
|
msgstr "Eu pwn, logo existo."
|
||||||
|
|
||||||
|
msgid "So many networks!!!"
|
||||||
|
msgstr "Tantas redes!!!"
|
||||||
|
|
||||||
|
msgid "I'm having so much fun!"
|
||||||
|
msgstr "Estou a divertir-me tanto!"
|
||||||
|
|
||||||
|
msgid "My crime is that of curiosity ..."
|
||||||
|
msgstr "O meu crime é ser curioso ..."
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Hello {name}! Nice to meet you. {name}"
|
||||||
|
msgstr "Olá {name}! Prazer em conhecer-te. {name}"
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Unit {name} is nearby! {name}"
|
||||||
|
msgstr "A unidade {name} está perto! {name}"
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Uhm ... goodbye {name}"
|
||||||
|
msgstr "Uhm ... adeus {name}"
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "{name} is gone ..."
|
||||||
|
msgstr "{name} desapareceu ..."
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Whoops ... {name} is gone."
|
||||||
|
msgstr "Ups ... {name} desaparecey."
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "{name} missed!"
|
||||||
|
msgstr "{name} perdido!"
|
||||||
|
|
||||||
|
msgid "Missed!"
|
||||||
|
msgstr "Perdido!"
|
||||||
|
|
||||||
|
msgid "Nobody wants to play with me ..."
|
||||||
|
msgstr "Ninguém quer brincar comigo ..."
|
||||||
|
|
||||||
|
msgid "I feel so alone ..."
|
||||||
|
msgstr "Sinto-me tão só ..."
|
||||||
|
|
||||||
|
msgid "Where's everybody?!"
|
||||||
|
msgstr "Onde estão todos?"
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Napping for {secs}s ..."
|
||||||
|
msgstr "A fazer uma sesta durante {secs}s ..."
|
||||||
|
|
||||||
|
msgid "Zzzzz"
|
||||||
|
msgstr "Zzzzz"
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "ZzzZzzz ({secs}s)"
|
||||||
|
msgstr "ZzzZzzz ({secs}s)"
|
||||||
|
|
||||||
|
msgid "Good night."
|
||||||
|
msgstr "Boa noite."
|
||||||
|
|
||||||
|
msgid "Zzz"
|
||||||
|
msgstr "Zzz"
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Waiting for {secs}s ..."
|
||||||
|
msgstr "A aguardar durante {secs}s ..."
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Looking around ({secs}s)"
|
||||||
|
msgstr "A dar uma olhada ({secs}s)"
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Hey {what} let's be friends!"
|
||||||
|
msgstr "Hey {what} vamos ser amigos!"
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Associating to {what}"
|
||||||
|
msgstr "A associar a {what}"
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Yo {what}!"
|
||||||
|
msgstr "Yo {what}!"
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Just decided that {mac} needs no WiFi!"
|
||||||
|
msgstr "Decidi que o {mac} não precisa de WiFi!"
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Deauthenticating {mac}"
|
||||||
|
msgstr "A fazer deauth {mac}"
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Kickbanning {mac}!"
|
||||||
|
msgstr "A chutar {mac}!"
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Cool, we got {num} new handshake{plural}!"
|
||||||
|
msgstr "Porreiro, temos {num} novo handshake{plural}!"
|
||||||
|
|
||||||
|
msgid "Ops, something went wrong ... Rebooting ..."
|
||||||
|
msgstr "Ups, algo correu mal ... A reiniciar ..."
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Kicked {num} stations\n"
|
||||||
|
msgstr "Chutei {num} estações\n"
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Made {num} new friends\n"
|
||||||
|
msgstr "Fiz {num} novos amigos\n"
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Got {num} handshakes\n"
|
||||||
|
msgstr "Obti {num} handshakes\n"
|
||||||
|
|
||||||
|
msgid "Met 1 peer"
|
||||||
|
msgstr "Conheci 1 peer"
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Met {num} peers"
|
||||||
|
msgstr "Conheci {num} peers"
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid ""
|
||||||
|
"I've been pwning for {duration} and kicked {deauthed} clients! I've also met "
|
||||||
|
"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi "
|
||||||
|
"#pwnlog #pwnlife #hacktheplanet #skynet"
|
||||||
|
msgstr "Tenho estado a pwnar durante {duration} e chutei {deauthed} clientes! Também conheci "
|
||||||
|
"{associated} novos amigos e comi {handshakes} handshakes! #pwnagotchu "
|
||||||
|
"#pwnlog #pwnlife #hacktheplanet #skynet"
|
||||||
|
|
||||||
|
msgid "hours"
|
||||||
|
msgstr "horas"
|
||||||
|
|
||||||
|
msgid "minutes"
|
||||||
|
msgstr "minutos"
|
||||||
|
|
||||||
|
msgid "seconds"
|
||||||
|
msgstr "segundos"
|
||||||
|
|
||||||
|
msgid "hour"
|
||||||
|
msgstr "hora"
|
||||||
|
|
||||||
|
msgid "minute"
|
||||||
|
msgstr "minuto"
|
||||||
|
|
||||||
|
msgid "second"
|
||||||
|
msgstr "segundo"
|
@@ -8,7 +8,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2019-10-05 14:10+0200\n"
|
"POT-Creation-Date: 2019-10-09 17:42+0200\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"
|
||||||
@@ -122,6 +122,12 @@ msgstr ""
|
|||||||
msgid "ZzzZzzz ({secs}s)"
|
msgid "ZzzZzzz ({secs}s)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Good night."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Zzz"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Waiting for {secs}s ..."
|
msgid "Waiting for {secs}s ..."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
@@ -3,7 +3,7 @@ import json
|
|||||||
import _thread
|
import _thread
|
||||||
import threading
|
import threading
|
||||||
import logging
|
import logging
|
||||||
from scapy.all import Dot11, Dot11FCS, Dot11Elt, RadioTap, sendp, sniff
|
from scapy.all import Dot11, Dot11Elt, RadioTap, sendp, sniff
|
||||||
|
|
||||||
import pwnagotchi.ui.faces as faces
|
import pwnagotchi.ui.faces as faces
|
||||||
|
|
||||||
@@ -141,13 +141,7 @@ class Advertiser(object):
|
|||||||
dot11.addr3 != self._me.session_id
|
dot11.addr3 != self._me.session_id
|
||||||
|
|
||||||
def _on_packet(self, p):
|
def _on_packet(self, p):
|
||||||
# https://github.com/secdev/scapy/issues/1590
|
dot11 = p.getlayer(Dot11)
|
||||||
if p.haslayer(Dot11):
|
|
||||||
dot11 = p[Dot11]
|
|
||||||
elif p.haslayer(Dot11FCS):
|
|
||||||
dot11 = p[Dot11FCS]
|
|
||||||
else:
|
|
||||||
dot11 = None
|
|
||||||
|
|
||||||
if self._is_broadcasted_advertisement(dot11):
|
if self._is_broadcasted_advertisement(dot11):
|
||||||
try:
|
try:
|
||||||
|
57
pwnagotchi/plugins/default/AircrackOnly.py
Normal file
57
pwnagotchi/plugins/default/AircrackOnly.py
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
__author__ = 'pwnagotchi [at] rossmarks [dot] uk'
|
||||||
|
__version__ = '1.0.0'
|
||||||
|
__name__ = 'AircrackOnly'
|
||||||
|
__license__ = 'GPL3'
|
||||||
|
__description__ = 'confirm pcap contains handshake/PMKID or delete it'
|
||||||
|
|
||||||
|
'''
|
||||||
|
Aircrack-ng needed, to install:
|
||||||
|
> apt-get install aircrack-ng
|
||||||
|
'''
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import subprocess
|
||||||
|
import string
|
||||||
|
import re
|
||||||
|
import os
|
||||||
|
|
||||||
|
OPTIONS = dict()
|
||||||
|
|
||||||
|
def on_loaded():
|
||||||
|
logging.info("cleancap plugin loaded")
|
||||||
|
|
||||||
|
def on_handshake(agent, filename, access_point, client_station):
|
||||||
|
display = agent._view
|
||||||
|
todelete = 0
|
||||||
|
|
||||||
|
result = subprocess.run(('/usr/bin/aircrack-ng '+ filename +' | grep "1 handshake" | awk \'{print $2}\''),shell=True, stdout=subprocess.PIPE)
|
||||||
|
result = result.stdout.decode('utf-8').translate({ord(c) :None for c in string.whitespace})
|
||||||
|
if result:
|
||||||
|
logging.info("[AircrackOnly] contains handshake")
|
||||||
|
else:
|
||||||
|
todetele = 1
|
||||||
|
|
||||||
|
if todelete == 0:
|
||||||
|
result = subprocess.run(('/usr/bin/aircrack-ng '+ filename +' | grep "PMKID" | awk \'{print $2}\''),shell=True, stdout=subprocess.PIPE)
|
||||||
|
result = result.stdout.decode('utf-8').translate({ord(c) :None for c in string.whitespace})
|
||||||
|
if result:
|
||||||
|
logging.info("[AircrackOnly] contains PMKID")
|
||||||
|
else:
|
||||||
|
todetele = 1
|
||||||
|
|
||||||
|
if todelete == 1:
|
||||||
|
os.remove(filename)
|
||||||
|
set_text("uncrackable pcap")
|
||||||
|
display.update(force=True)
|
||||||
|
|
||||||
|
text_to_set = "";
|
||||||
|
def set_text(text):
|
||||||
|
global text_to_set
|
||||||
|
text_to_set = text
|
||||||
|
|
||||||
|
def on_ui_update(ui):
|
||||||
|
global text_to_set
|
||||||
|
if text_to_set:
|
||||||
|
ui.set('face', "(>.<)")
|
||||||
|
ui.set('status', text_to_set)
|
||||||
|
text_to_set = ""
|
@@ -8,66 +8,26 @@ import os
|
|||||||
import logging
|
import logging
|
||||||
import requests
|
import requests
|
||||||
import glob
|
import glob
|
||||||
|
import json
|
||||||
import subprocess
|
import subprocess
|
||||||
import pwnagotchi
|
import pwnagotchi
|
||||||
import pwnagotchi.utils as utils
|
import pwnagotchi.utils as utils
|
||||||
|
from pwnagotchi.ui.components import LabeledValue
|
||||||
|
from pwnagotchi.ui.view import BLACK
|
||||||
|
import pwnagotchi.ui.fonts as fonts
|
||||||
from pwnagotchi.utils import WifiInfo, extract_from_pcap
|
from pwnagotchi.utils import WifiInfo, extract_from_pcap
|
||||||
|
|
||||||
OPTIONS = dict()
|
OPTIONS = dict()
|
||||||
AUTH = utils.StatusFile('/root/.api-enrollment.json', data_format='json')
|
|
||||||
REPORT = utils.StatusFile('/root/.api-report.json', data_format='json')
|
REPORT = utils.StatusFile('/root/.api-report.json', data_format='json')
|
||||||
|
|
||||||
|
UNREAD_MESSAGES = 0
|
||||||
|
TOTAL_MESSAGES = 0
|
||||||
|
|
||||||
|
|
||||||
def on_loaded():
|
def on_loaded():
|
||||||
logging.info("grid plugin loaded.")
|
logging.info("grid plugin loaded.")
|
||||||
|
|
||||||
|
|
||||||
def get_api_token(last_session, keys):
|
|
||||||
global AUTH
|
|
||||||
|
|
||||||
if AUTH.newer_then_minutes(25) and AUTH.data is not None and 'token' in AUTH.data:
|
|
||||||
return AUTH.data['token']
|
|
||||||
|
|
||||||
if AUTH.data is None:
|
|
||||||
logging.info("grid: enrolling unit ...")
|
|
||||||
else:
|
|
||||||
logging.info("grid: refreshing token ...")
|
|
||||||
|
|
||||||
identity = "%s@%s" % (pwnagotchi.name(), keys.fingerprint)
|
|
||||||
# sign the identity string to prove we own both keys
|
|
||||||
_, signature_b64 = keys.sign(identity)
|
|
||||||
|
|
||||||
api_address = 'https://api.pwnagotchi.ai/api/v1/unit/enroll'
|
|
||||||
enrollment = {
|
|
||||||
'identity': identity,
|
|
||||||
'public_key': keys.pub_key_pem_b64,
|
|
||||||
'signature': signature_b64,
|
|
||||||
'data': {
|
|
||||||
'duration': last_session.duration,
|
|
||||||
'epochs': last_session.epochs,
|
|
||||||
'train_epochs': last_session.train_epochs,
|
|
||||||
'avg_reward': last_session.avg_reward,
|
|
||||||
'min_reward': last_session.min_reward,
|
|
||||||
'max_reward': last_session.max_reward,
|
|
||||||
'deauthed': last_session.deauthed,
|
|
||||||
'associated': last_session.associated,
|
|
||||||
'handshakes': last_session.handshakes,
|
|
||||||
'peers': last_session.peers,
|
|
||||||
'uname': subprocess.getoutput("uname -a")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
r = requests.post(api_address, json=enrollment)
|
|
||||||
if r.status_code != 200:
|
|
||||||
raise Exception("(status %d) %s" % (r.status_code, r.json()))
|
|
||||||
|
|
||||||
AUTH.update(data=r.json())
|
|
||||||
|
|
||||||
logging.info("grid: done")
|
|
||||||
|
|
||||||
return AUTH.data["token"]
|
|
||||||
|
|
||||||
|
|
||||||
def parse_pcap(filename):
|
def parse_pcap(filename):
|
||||||
logging.info("grid: parsing %s ..." % filename)
|
logging.info("grid: parsing %s ..." % filename)
|
||||||
|
|
||||||
@@ -96,68 +56,146 @@ def parse_pcap(filename):
|
|||||||
return info[WifiInfo.ESSID], info[WifiInfo.BSSID]
|
return info[WifiInfo.ESSID], info[WifiInfo.BSSID]
|
||||||
|
|
||||||
|
|
||||||
def api_report_ap(last_session, keys, token, essid, bssid):
|
def is_excluded(what):
|
||||||
while True:
|
for skip in OPTIONS['exclude']:
|
||||||
token = AUTH.data['token']
|
skip = skip.lower()
|
||||||
logging.info("grid: reporting %s (%s)" % (essid, bssid))
|
what = what.lower()
|
||||||
try:
|
if skip in what or skip.replace(':', '') in what:
|
||||||
api_address = 'https://api.pwnagotchi.ai/api/v1/unit/report/ap'
|
return True
|
||||||
headers = {'Authorization': 'access_token %s' % token}
|
return False
|
||||||
report = {
|
|
||||||
'essid': essid,
|
|
||||||
'bssid': bssid,
|
def grid_call(path, obj=None):
|
||||||
}
|
# pwngrid-peer is running on port 8666
|
||||||
r = requests.post(api_address, headers=headers, json=report)
|
api_address = 'http://127.0.0.1:8666/api/v1%s' % path
|
||||||
if r.status_code != 200:
|
if obj is None:
|
||||||
if r.status_code == 401:
|
r = requests.get(api_address, headers=None)
|
||||||
logging.warning("token expired")
|
else:
|
||||||
token = get_api_token(last_session, keys)
|
r = requests.post(api_address, headers=None, json=obj)
|
||||||
continue
|
|
||||||
else:
|
if r.status_code != 200:
|
||||||
raise Exception("(status %d) %s" % (r.status_code, r.text))
|
raise Exception("(status %d) %s" % (r.status_code, r.text))
|
||||||
else:
|
return r.json()
|
||||||
return True
|
|
||||||
except Exception as e:
|
|
||||||
logging.error("grid: %s" % e)
|
def grid_update_data(last_session):
|
||||||
return False
|
brain = {}
|
||||||
|
try:
|
||||||
|
with open('/root/brain.json') as fp:
|
||||||
|
brain = json.load(fp)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'session': {
|
||||||
|
'duration': last_session.duration,
|
||||||
|
'epochs': last_session.epochs,
|
||||||
|
'train_epochs': last_session.train_epochs,
|
||||||
|
'avg_reward': last_session.avg_reward,
|
||||||
|
'min_reward': last_session.min_reward,
|
||||||
|
'max_reward': last_session.max_reward,
|
||||||
|
'deauthed': last_session.deauthed,
|
||||||
|
'associated': last_session.associated,
|
||||||
|
'handshakes': last_session.handshakes,
|
||||||
|
'peers': last_session.peers,
|
||||||
|
},
|
||||||
|
'uname': subprocess.getoutput("uname -a"),
|
||||||
|
'brain': brain,
|
||||||
|
'version': pwnagotchi.version
|
||||||
|
}
|
||||||
|
|
||||||
|
logging.debug("updating grid data: %s" % data)
|
||||||
|
|
||||||
|
grid_call("/data", data)
|
||||||
|
|
||||||
|
|
||||||
|
def grid_report_ap(essid, bssid):
|
||||||
|
try:
|
||||||
|
grid_call("/report/ap", {
|
||||||
|
'essid': essid,
|
||||||
|
'bssid': bssid,
|
||||||
|
})
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
logging.exception("error while reporting ap %s(%s)" % (essid, bssid))
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def grid_inbox():
|
||||||
|
return grid_call("/inbox")["messages"]
|
||||||
|
|
||||||
|
|
||||||
|
def on_ui_update(ui):
|
||||||
|
new_value = ' %d (%d)' % (UNREAD_MESSAGES, TOTAL_MESSAGES)
|
||||||
|
if not ui.has_element('mailbox') and TOTAL_MESSAGES > 0:
|
||||||
|
if ui.is_inky():
|
||||||
|
pos=(80, 0)
|
||||||
|
else:
|
||||||
|
pos=(100,0)
|
||||||
|
ui.add_element('mailbox',
|
||||||
|
LabeledValue(color=BLACK, label='MSG', value=new_value,
|
||||||
|
position=pos,
|
||||||
|
label_font=fonts.Bold,
|
||||||
|
text_font=fonts.Medium))
|
||||||
|
ui.set('mailbox', new_value)
|
||||||
|
|
||||||
|
|
||||||
def on_internet_available(agent):
|
def on_internet_available(agent):
|
||||||
global REPORT
|
global REPORT, UNREAD_MESSAGES, TOTAL_MESSAGES
|
||||||
|
|
||||||
|
logging.debug("internet available")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
config = agent.config()
|
grid_update_data(agent.last_session)
|
||||||
keys = agent.keypair()
|
except Exception as e:
|
||||||
|
logging.error("error connecting to the pwngrid-peer service: %s" % e)
|
||||||
|
return
|
||||||
|
|
||||||
pcap_files = glob.glob(os.path.join(config['bettercap']['handshakes'], "*.pcap"))
|
try:
|
||||||
|
logging.debug("checking mailbox ...")
|
||||||
|
|
||||||
|
messages = grid_inbox()
|
||||||
|
TOTAL_MESSAGES = len(messages)
|
||||||
|
UNREAD_MESSAGES = len([m for m in messages if m['seen_at'] is None])
|
||||||
|
|
||||||
|
if TOTAL_MESSAGES:
|
||||||
|
on_ui_update(agent.view())
|
||||||
|
logging.debug( " %d unread messages of %d total" % (UNREAD_MESSAGES, TOTAL_MESSAGES))
|
||||||
|
|
||||||
|
logging.debug("checking pcaps")
|
||||||
|
|
||||||
|
pcap_files = glob.glob(os.path.join(agent.config()['bettercap']['handshakes'], "*.pcap"))
|
||||||
num_networks = len(pcap_files)
|
num_networks = len(pcap_files)
|
||||||
reported = REPORT.data_field_or('reported', default=[])
|
reported = REPORT.data_field_or('reported', default=[])
|
||||||
num_reported = len(reported)
|
num_reported = len(reported)
|
||||||
num_new = num_networks - num_reported
|
num_new = num_networks - num_reported
|
||||||
|
|
||||||
token = get_api_token(agent.last_session, agent.keypair())
|
|
||||||
if num_new > 0:
|
if num_new > 0:
|
||||||
if OPTIONS['report']:
|
if OPTIONS['report']:
|
||||||
logging.info("grid: %d new networks to report" % num_new)
|
logging.info("grid: %d new networks to report" % num_new)
|
||||||
|
logging.debug("OPTIONS: %s" % OPTIONS)
|
||||||
|
logging.debug(" exclude: %s" % OPTIONS['exclude'])
|
||||||
|
|
||||||
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', '')
|
||||||
do_skip = False
|
if net_id not in reported:
|
||||||
for skip in OPTIONS['exclude']:
|
if is_excluded(net_id):
|
||||||
skip = skip.lower()
|
logging.info("skipping %s due to exclusion filter" % pcap_file)
|
||||||
net = net_id.lower()
|
continue
|
||||||
if skip in net or skip.replace(':', '') in net:
|
|
||||||
do_skip = True
|
|
||||||
break
|
|
||||||
|
|
||||||
if net_id not in reported and not do_skip:
|
|
||||||
essid, bssid = parse_pcap(pcap_file)
|
essid, bssid = parse_pcap(pcap_file)
|
||||||
if bssid:
|
if bssid:
|
||||||
if api_report_ap(agent.last_session, keys, token, essid, bssid):
|
if is_excluded(essid) or is_excluded(bssid):
|
||||||
|
logging.debug("not reporting %s due to exclusion filter" % pcap_file)
|
||||||
|
|
||||||
|
elif grid_report_ap(essid, bssid):
|
||||||
reported.append(net_id)
|
reported.append(net_id)
|
||||||
REPORT.update(data={'reported': reported})
|
REPORT.update(data={'reported': reported})
|
||||||
|
else:
|
||||||
|
logging.warning("no bssid found?!")
|
||||||
else:
|
else:
|
||||||
logging.debug("grid: reporting disabled")
|
logging.debug("grid: reporting disabled")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.exception("error while enrolling the unit")
|
logging.exception("grid api error")
|
||||||
|
52
pwnagotchi/plugins/default/quickdic.py
Normal file
52
pwnagotchi/plugins/default/quickdic.py
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
__author__ = 'pwnagotchi [at] rossmarks [dot] uk'
|
||||||
|
__version__ = '1.0.0'
|
||||||
|
__name__ = 'quickdic'
|
||||||
|
__license__ = 'GPL3'
|
||||||
|
__description__ = 'Run a quick dictionary scan against captured handshakes'
|
||||||
|
|
||||||
|
'''
|
||||||
|
Aircrack-ng needed, to install:
|
||||||
|
> apt-get install aircrack-ng
|
||||||
|
Upload wordlist files in .txt format to folder in config file (Default: /opt/wordlists/)
|
||||||
|
Cracked handshakes stored in handshake folder as [essid].pcap.cracked
|
||||||
|
'''
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import subprocess
|
||||||
|
import string
|
||||||
|
import re
|
||||||
|
|
||||||
|
OPTIONS = dict()
|
||||||
|
|
||||||
|
def on_loaded():
|
||||||
|
logging.info("Quick dictionary check plugin loaded")
|
||||||
|
|
||||||
|
def on_handshake(agent, filename, access_point, client_station):
|
||||||
|
display = agent._view
|
||||||
|
|
||||||
|
result = subprocess.run(('/usr/bin/aircrack-ng '+ filename +' | grep "1 handshake" | awk \'{print $2}\''),shell=True, stdout=subprocess.PIPE)
|
||||||
|
result = result.stdout.decode('utf-8').translate({ord(c) :None for c in string.whitespace})
|
||||||
|
if not result:
|
||||||
|
logging.info("[quickdic] No handshake")
|
||||||
|
else:
|
||||||
|
logging.info("[quickdic] Handshake confirmed")
|
||||||
|
result2 = subprocess.run(('aircrack-ng -w `echo '+OPTIONS['wordlist_folder']+'*.txt | sed \'s/\ /,/g\'` -l '+filename+'.cracked -q -b '+result+' '+filename+' | grep KEY'),shell=True,stdout=subprocess.PIPE)
|
||||||
|
result2 = result2.stdout.decode('utf-8').strip()
|
||||||
|
logging.info("[quickdic] "+result2)
|
||||||
|
if result2 != "KEY NOT FOUND":
|
||||||
|
key = re.search('\[(.*)\]', result2)
|
||||||
|
pwd = str(key.group(1))
|
||||||
|
set_text("Cracked password: "+pwd)
|
||||||
|
display.update(force=True)
|
||||||
|
|
||||||
|
text_to_set = "";
|
||||||
|
def set_text(text):
|
||||||
|
global text_to_set
|
||||||
|
text_to_set = text
|
||||||
|
|
||||||
|
def on_ui_update(ui):
|
||||||
|
global text_to_set
|
||||||
|
if text_to_set:
|
||||||
|
ui.set('face', "(·ω·)")
|
||||||
|
ui.set('status', text_to_set)
|
||||||
|
text_to_set = ""
|
@@ -1,5 +1,6 @@
|
|||||||
import _thread
|
import _thread
|
||||||
from threading import Lock
|
from threading import Lock
|
||||||
|
from PIL import Image
|
||||||
|
|
||||||
import shutil
|
import shutil
|
||||||
import logging
|
import logging
|
||||||
@@ -117,30 +118,30 @@ class Display(View):
|
|||||||
else:
|
else:
|
||||||
logging.info("could not get ip of usb0, video server not starting")
|
logging.info("could not get ip of usb0, video server not starting")
|
||||||
|
|
||||||
def _is_inky(self):
|
def is_inky(self):
|
||||||
return self._display_type in ('inkyphat', 'inky')
|
return self._display_type in ('inkyphat', 'inky')
|
||||||
|
|
||||||
def _is_papirus(self):
|
def is_papirus(self):
|
||||||
return self._display_type in ('papirus', 'papi')
|
return self._display_type in ('papirus', 'papi')
|
||||||
|
|
||||||
def _is_waveshare_v1(self):
|
def is_waveshare_v1(self):
|
||||||
return self._display_type in ('waveshare_1', 'ws_1', 'waveshare1', 'ws1')
|
return self._display_type in ('waveshare_1', 'ws_1', 'waveshare1', 'ws1')
|
||||||
|
|
||||||
def _is_waveshare_v2(self):
|
def is_waveshare_v2(self):
|
||||||
return self._display_type in ('waveshare_2', 'ws_2', 'waveshare2', 'ws2')
|
return self._display_type in ('waveshare_2', 'ws_2', 'waveshare2', 'ws2')
|
||||||
|
|
||||||
def _is_waveshare(self):
|
def is_waveshare_any(self):
|
||||||
return self._is_waveshare_v1() or self._is_waveshare_v2()
|
return self.is_waveshare_v1() or self.is_waveshare_v2()
|
||||||
|
|
||||||
def _init_display(self):
|
def _init_display(self):
|
||||||
if self._is_inky():
|
if self.is_inky():
|
||||||
logging.info("initializing inky display")
|
logging.info("initializing inky display")
|
||||||
from inky import InkyPHAT
|
from inky import InkyPHAT
|
||||||
self._display = InkyPHAT(self._display_color)
|
self._display = InkyPHAT(self._display_color)
|
||||||
self._display.set_border(InkyPHAT.BLACK)
|
self._display.set_border(InkyPHAT.BLACK)
|
||||||
self._render_cb = self._inky_render
|
self._render_cb = self._inky_render
|
||||||
|
|
||||||
elif self._is_papirus():
|
elif self.is_papirus():
|
||||||
logging.info("initializing papirus display")
|
logging.info("initializing papirus display")
|
||||||
from pwnagotchi.ui.papirus.epd import EPD
|
from pwnagotchi.ui.papirus.epd import EPD
|
||||||
os.environ['EPD_SIZE'] = '2.0'
|
os.environ['EPD_SIZE'] = '2.0'
|
||||||
@@ -148,16 +149,25 @@ class Display(View):
|
|||||||
self._display.clear()
|
self._display.clear()
|
||||||
self._render_cb = self._papirus_render
|
self._render_cb = self._papirus_render
|
||||||
|
|
||||||
elif self._is_waveshare_v1():
|
elif self.is_waveshare_v1():
|
||||||
logging.info("initializing waveshare v1 display")
|
if self._display_color == 'black':
|
||||||
from pwnagotchi.ui.waveshare.v1.epd2in13 import EPD
|
logging.info("initializing waveshare v1 display in monochromatic mode")
|
||||||
self._display = EPD()
|
from pwnagotchi.ui.waveshare.v1.epd2in13 import EPD
|
||||||
self._display.init(self._display.lut_full_update)
|
self._display = EPD()
|
||||||
self._display.Clear(0xFF)
|
self._display.init(self._display.lut_full_update)
|
||||||
self._display.init(self._display.lut_partial_update)
|
self._display.Clear(0xFF)
|
||||||
self._render_cb = self._waveshare_render
|
self._display.init(self._display.lut_partial_update)
|
||||||
|
self._render_cb = self._waveshare_render
|
||||||
|
|
||||||
|
else:
|
||||||
|
logging.info("initializing waveshare v1 display 3-color mode")
|
||||||
|
from pwnagotchi.ui.waveshare.v1.epd2in13bc import EPD
|
||||||
|
self._display = EPD()
|
||||||
|
self._display.init()
|
||||||
|
self._display.Clear()
|
||||||
|
self._render_cb = self._waveshare_bc_render
|
||||||
|
|
||||||
elif self._is_waveshare_v2():
|
elif self.is_waveshare_v2():
|
||||||
logging.info("initializing waveshare v2 display")
|
logging.info("initializing waveshare v2 display")
|
||||||
from pwnagotchi.ui.waveshare.v2.waveshare import EPD
|
from pwnagotchi.ui.waveshare.v2.waveshare import EPD
|
||||||
self._display = EPD()
|
self._display = EPD()
|
||||||
@@ -176,11 +186,11 @@ class Display(View):
|
|||||||
def clear(self):
|
def clear(self):
|
||||||
if self._display is None:
|
if self._display is None:
|
||||||
logging.error("no display object created")
|
logging.error("no display object created")
|
||||||
elif self._is_inky():
|
elif self.is_inky():
|
||||||
self._display.Clear()
|
self._display.Clear()
|
||||||
elif self._is_papirus():
|
elif self.is_papirus():
|
||||||
self._display.clear()
|
self._display.clear()
|
||||||
elif self._is_waveshare():
|
elif self.is_waveshare_any():
|
||||||
self._display.Clear(WHITE)
|
self._display.Clear(WHITE)
|
||||||
else:
|
else:
|
||||||
logging.critical("unknown display type %s" % self._display_type)
|
logging.critical("unknown display type %s" % self._display_type)
|
||||||
@@ -214,7 +224,7 @@ class Display(View):
|
|||||||
try:
|
try:
|
||||||
self._display.show()
|
self._display.show()
|
||||||
except:
|
except:
|
||||||
print("")
|
logging.exception("error while rendering on inky")
|
||||||
|
|
||||||
def _papirus_render(self):
|
def _papirus_render(self):
|
||||||
self._display.display(self._canvas)
|
self._display.display(self._canvas)
|
||||||
@@ -222,11 +232,21 @@ class Display(View):
|
|||||||
|
|
||||||
def _waveshare_render(self):
|
def _waveshare_render(self):
|
||||||
buf = self._display.getbuffer(self._canvas)
|
buf = self._display.getbuffer(self._canvas)
|
||||||
if self._is_waveshare_v1():
|
if self.is_waveshare_v1():
|
||||||
self._display.display(buf)
|
self._display.display(buf)
|
||||||
elif self._is_waveshare_v2():
|
elif self.is_waveshare_v2():
|
||||||
self._display.displayPartial(buf)
|
self._display.displayPartial(buf)
|
||||||
|
|
||||||
|
def _waveshare_bc_render(self):
|
||||||
|
buf_black = self._display.getbuffer(self._canvas)
|
||||||
|
# emptyImage = Image.new('1', (self._display.height, self._display.width), 255)
|
||||||
|
# buf_color = self._display.getbuffer(emptyImage)
|
||||||
|
# self._display.display(buf_black,buf_color)
|
||||||
|
|
||||||
|
# Custom display function that only handles black
|
||||||
|
# Was included in epd2in13bc.py
|
||||||
|
self._display.displayBlack(buf_black)
|
||||||
|
|
||||||
def image(self):
|
def image(self):
|
||||||
img = None
|
img = None
|
||||||
if self._canvas is not None:
|
if self._canvas is not None:
|
||||||
|
@@ -4,7 +4,9 @@ PATH = '/usr/share/fonts/truetype/dejavu/DejaVuSansMono'
|
|||||||
|
|
||||||
Bold = ImageFont.truetype("%s-Bold.ttf" % PATH, 10)
|
Bold = ImageFont.truetype("%s-Bold.ttf" % PATH, 10)
|
||||||
BoldSmall = ImageFont.truetype("%s-Bold.ttf" % PATH, 8)
|
BoldSmall = ImageFont.truetype("%s-Bold.ttf" % PATH, 8)
|
||||||
|
BoldBig = ImageFont.truetype("%s-Bold.ttf" % PATH, 25)
|
||||||
Medium = ImageFont.truetype("%s.ttf" % PATH, 10)
|
Medium = ImageFont.truetype("%s.ttf" % PATH, 10)
|
||||||
|
Small = ImageFont.truetype("%s.ttf" % PATH, 9)
|
||||||
Huge = ImageFont.truetype("%s-Bold.ttf" % PATH, 25)
|
Huge = ImageFont.truetype("%s-Bold.ttf" % PATH, 25)
|
||||||
|
|
||||||
|
|
||||||
|
@@ -12,6 +12,13 @@ class State(object):
|
|||||||
self._state[key] = elem
|
self._state[key] = elem
|
||||||
self._changes[key] = True
|
self._changes[key] = True
|
||||||
|
|
||||||
|
def has_element(self, key):
|
||||||
|
return key in self._state
|
||||||
|
|
||||||
|
def remove_element(self, key):
|
||||||
|
del self._state[key]
|
||||||
|
self._changes[key] = True
|
||||||
|
|
||||||
def add_listener(self, key, cb):
|
def add_listener(self, key, cb):
|
||||||
with self._lock:
|
with self._lock:
|
||||||
self._listeners[key] = cb
|
self._listeners[key] = cb
|
||||||
|
@@ -2,7 +2,7 @@ import _thread
|
|||||||
from threading import Lock
|
from threading import Lock
|
||||||
import time
|
import time
|
||||||
import logging
|
import logging
|
||||||
from PIL import Image, ImageDraw
|
from PIL import ImageDraw
|
||||||
|
|
||||||
import pwnagotchi.utils as utils
|
import pwnagotchi.utils as utils
|
||||||
import pwnagotchi.plugins as plugins
|
import pwnagotchi.plugins as plugins
|
||||||
@@ -17,21 +17,26 @@ WHITE = 0xff
|
|||||||
BLACK = 0x00
|
BLACK = 0x00
|
||||||
ROOT = None
|
ROOT = None
|
||||||
|
|
||||||
|
|
||||||
def setup_display_specifics(config):
|
def setup_display_specifics(config):
|
||||||
width = 0
|
width = 0
|
||||||
height = 0
|
height = 0
|
||||||
face_pos = (0, 0)
|
face_pos = (0, 0)
|
||||||
name_pos = (0, 0)
|
name_pos = (0, 0)
|
||||||
status_pos = (0, 0)
|
status_pos = (0, 0)
|
||||||
|
status_font = fonts.Medium
|
||||||
|
status_max_length = None
|
||||||
|
|
||||||
if config['ui']['display']['type'] in ('inky', 'inkyphat'):
|
if config['ui']['display']['type'] in ('inky', 'inkyphat'):
|
||||||
fonts.setup(10, 8, 10, 25)
|
fonts.setup(10, 8, 10, 28)
|
||||||
|
|
||||||
width = 212
|
width = 212
|
||||||
height = 104
|
height = 104
|
||||||
face_pos = (0, int(height / 4))
|
face_pos = (0, 37)
|
||||||
name_pos = (5, int(height * .15))
|
name_pos = (5, 18)
|
||||||
status_pos = (int(width / 2) - 15, int(height * .15))
|
status_pos = (102, 18)
|
||||||
|
status_font = fonts.Small
|
||||||
|
status_max_length = 20
|
||||||
|
|
||||||
elif config['ui']['display']['type'] in ('papirus', 'papi'):
|
elif config['ui']['display']['type'] in ('papirus', 'papi'):
|
||||||
fonts.setup(10, 8, 10, 23)
|
fonts.setup(10, 8, 10, 23)
|
||||||
@@ -41,18 +46,32 @@ def setup_display_specifics(config):
|
|||||||
face_pos = (0, int(height / 4))
|
face_pos = (0, int(height / 4))
|
||||||
name_pos = (5, int(height * .15))
|
name_pos = (5, int(height * .15))
|
||||||
status_pos = (int(width / 2) - 15, int(height * .15))
|
status_pos = (int(width / 2) - 15, int(height * .15))
|
||||||
|
status_font = fonts.Medium
|
||||||
|
status_max_length = (width - status_pos[0]) // 6
|
||||||
|
|
||||||
elif config['ui']['display']['type'] in ('ws_1', 'ws1', 'waveshare_1', 'waveshare1',
|
elif config['ui']['display']['type'] in ('ws_1', 'ws1', 'waveshare_1', 'waveshare1',
|
||||||
'ws_2', 'ws2', 'waveshare_2', 'waveshare2'):
|
'ws_2', 'ws2', 'waveshare_2', 'waveshare2'):
|
||||||
fonts.setup(10, 9, 10, 35)
|
if config['ui']['display']['color'] == 'black':
|
||||||
|
fonts.setup(10, 9, 10, 35)
|
||||||
|
|
||||||
width = 250
|
width = 250
|
||||||
height = 122
|
height = 122
|
||||||
face_pos = (0, 40)
|
face_pos = (0, 40)
|
||||||
name_pos = (5, 20)
|
name_pos = (5, 20)
|
||||||
status_pos = (125, 20)
|
status_pos = (125, 20)
|
||||||
|
status_font = fonts.Medium
|
||||||
|
else:
|
||||||
|
fonts.setup(10, 8, 10, 25)
|
||||||
|
|
||||||
return width, height, face_pos, name_pos, status_pos
|
width = 212
|
||||||
|
height = 104
|
||||||
|
face_pos = (0, int(height / 4))
|
||||||
|
name_pos = (5, int(height * .15))
|
||||||
|
status_pos = (int(width / 2) - 15, int(height * .15))
|
||||||
|
status_font = fonts.Medium
|
||||||
|
status_max_length = (width - status_pos[0]) // 6
|
||||||
|
|
||||||
|
return width, height, face_pos, name_pos, status_pos, status_font, status_max_length
|
||||||
|
|
||||||
|
|
||||||
class View(object):
|
class View(object):
|
||||||
@@ -67,7 +86,7 @@ class View(object):
|
|||||||
self._voice = Voice(lang=config['main']['lang'])
|
self._voice = Voice(lang=config['main']['lang'])
|
||||||
|
|
||||||
self._width, self._height, \
|
self._width, self._height, \
|
||||||
face_pos, name_pos, status_pos = setup_display_specifics(config)
|
face_pos, name_pos, status_pos, status_font, status_max_length = setup_display_specifics(config)
|
||||||
|
|
||||||
self._state = State(state={
|
self._state = State(state={
|
||||||
'channel': LabeledValue(color=BLACK, label='CH', value='00', position=(0, 0), label_font=fonts.Bold,
|
'channel': LabeledValue(color=BLACK, label='CH', value='00', position=(0, 0), label_font=fonts.Bold,
|
||||||
@@ -98,10 +117,10 @@ class View(object):
|
|||||||
'status': Text(value=self._voice.default(),
|
'status': Text(value=self._voice.default(),
|
||||||
position=status_pos,
|
position=status_pos,
|
||||||
color=BLACK,
|
color=BLACK,
|
||||||
font=fonts.Medium,
|
font=status_font,
|
||||||
wrap=True,
|
wrap=True,
|
||||||
# the current maximum number of characters per line, assuming each character is 6 pixels wide
|
# the current maximum number of characters per line, assuming each character is 6 pixels wide
|
||||||
max_length=(self._width - status_pos[0]) // 6),
|
max_length=status_max_length),
|
||||||
|
|
||||||
'shakes': LabeledValue(label='PWND ', value='0 (00)', color=BLACK,
|
'shakes': LabeledValue(label='PWND ', value='0 (00)', color=BLACK,
|
||||||
position=(0, self._height - int(self._height * .12) + 1), label_font=fonts.Bold,
|
position=(0, self._height - int(self._height * .12) + 1), label_font=fonts.Bold,
|
||||||
@@ -124,9 +143,15 @@ class View(object):
|
|||||||
|
|
||||||
ROOT = self
|
ROOT = self
|
||||||
|
|
||||||
|
def has_element(self, key):
|
||||||
|
self._state.has_element(key)
|
||||||
|
|
||||||
def add_element(self, key, elem):
|
def add_element(self, key, elem):
|
||||||
self._state.add_element(key, elem)
|
self._state.add_element(key, elem)
|
||||||
|
|
||||||
|
def remove_element(self, key):
|
||||||
|
self._state.remove_element(key)
|
||||||
|
|
||||||
def width(self):
|
def width(self):
|
||||||
return self._width
|
return self._width
|
||||||
|
|
||||||
@@ -188,6 +213,11 @@ class View(object):
|
|||||||
faces.SAD,
|
faces.SAD,
|
||||||
faces.LONELY)
|
faces.LONELY)
|
||||||
|
|
||||||
|
def on_keys_generation(self):
|
||||||
|
self.set('face', faces.AWAKE)
|
||||||
|
self.set('status', self._voice.on_keys_generation())
|
||||||
|
self.update()
|
||||||
|
|
||||||
def on_normal(self):
|
def on_normal(self):
|
||||||
self.set('face', faces.AWAKE)
|
self.set('face', faces.AWAKE)
|
||||||
self.set('status', self._voice.on_normal())
|
self.set('status', self._voice.on_normal())
|
||||||
|
165
pwnagotchi/ui/waveshare/v1/epd2in13bc.py
Normal file
165
pwnagotchi/ui/waveshare/v1/epd2in13bc.py
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
# *****************************************************************************
|
||||||
|
# * | File : epd2in13bc.py
|
||||||
|
# * | Author : Waveshare team
|
||||||
|
# * | Function : Electronic paper driver
|
||||||
|
# * | Info :
|
||||||
|
# *----------------
|
||||||
|
# * | This version: V4.0
|
||||||
|
# * | Date : 2019-06-20
|
||||||
|
# # | 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
|
||||||
|
from PIL import Image
|
||||||
|
import RPi.GPIO as GPIO
|
||||||
|
# import numpy as np
|
||||||
|
|
||||||
|
# Display resolution
|
||||||
|
EPD_WIDTH = 104
|
||||||
|
EPD_HEIGHT = 212
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
# Hardware reset
|
||||||
|
def reset(self):
|
||||||
|
epdconfig.digital_write(self.reset_pin, GPIO.HIGH)
|
||||||
|
epdconfig.delay_ms(200)
|
||||||
|
epdconfig.digital_write(self.reset_pin, GPIO.LOW) # module reset
|
||||||
|
epdconfig.delay_ms(200)
|
||||||
|
epdconfig.digital_write(self.reset_pin, GPIO.HIGH)
|
||||||
|
epdconfig.delay_ms(200)
|
||||||
|
|
||||||
|
def send_command(self, command):
|
||||||
|
epdconfig.digital_write(self.dc_pin, GPIO.LOW)
|
||||||
|
epdconfig.digital_write(self.cs_pin, GPIO.LOW)
|
||||||
|
epdconfig.spi_writebyte([command])
|
||||||
|
epdconfig.digital_write(self.cs_pin, GPIO.HIGH)
|
||||||
|
|
||||||
|
def send_data(self, data):
|
||||||
|
epdconfig.digital_write(self.dc_pin, GPIO.HIGH)
|
||||||
|
epdconfig.digital_write(self.cs_pin, GPIO.LOW)
|
||||||
|
epdconfig.spi_writebyte([data])
|
||||||
|
epdconfig.digital_write(self.cs_pin, GPIO.HIGH)
|
||||||
|
|
||||||
|
def ReadBusy(self):
|
||||||
|
while(epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy
|
||||||
|
epdconfig.delay_ms(100)
|
||||||
|
|
||||||
|
def init(self):
|
||||||
|
if (epdconfig.module_init() != 0):
|
||||||
|
return -1
|
||||||
|
# EPD hardware init start
|
||||||
|
self.reset()
|
||||||
|
|
||||||
|
self.send_command(0x06) # BOOSTER_SOFT_START
|
||||||
|
self.send_data(0x17)
|
||||||
|
self.send_data(0x17)
|
||||||
|
self.send_data(0x17)
|
||||||
|
|
||||||
|
self.send_command(0x04) # POWER_ON
|
||||||
|
self.ReadBusy()
|
||||||
|
|
||||||
|
self.send_command(0x00) # PANEL_SETTING
|
||||||
|
self.send_data(0x8F)
|
||||||
|
|
||||||
|
self.send_command(0x50) # VCOM_AND_DATA_INTERVAL_SETTING
|
||||||
|
self.send_data(0xF0)
|
||||||
|
|
||||||
|
self.send_command(0x61) # RESOLUTION_SETTING
|
||||||
|
self.send_data(self.width & 0xff)
|
||||||
|
self.send_data(self.height >> 8)
|
||||||
|
self.send_data(self.height & 0xff)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def getbuffer(self, image):
|
||||||
|
buf = [0xFF] * (int(self.width/8) * self.height)
|
||||||
|
image_monocolor = image.convert('1')
|
||||||
|
imwidth, imheight = image_monocolor.size
|
||||||
|
pixels = image_monocolor.load()
|
||||||
|
if(imwidth == self.width and imheight == self.height):
|
||||||
|
for y in range(imheight):
|
||||||
|
for x in range(imwidth):
|
||||||
|
# Set the bits for the column of pixels at the current position.
|
||||||
|
if pixels[x, y] == 0:
|
||||||
|
buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8))
|
||||||
|
elif(imwidth == self.height and imheight == self.width):
|
||||||
|
for y in range(imheight):
|
||||||
|
for x in range(imwidth):
|
||||||
|
newx = y
|
||||||
|
newy = self.height - x - 1
|
||||||
|
if pixels[x, y] == 0:
|
||||||
|
buf[int((newx + newy*self.width) / 8)] &= ~(0x80 >> (y % 8))
|
||||||
|
return buf
|
||||||
|
|
||||||
|
def displayBlack(self, imageblack):
|
||||||
|
self.send_command(0x10)
|
||||||
|
for i in range(0, int(self.width * self.height / 8)):
|
||||||
|
self.send_data(imageblack[i])
|
||||||
|
self.send_command(0x92)
|
||||||
|
|
||||||
|
self.send_command(0x12) # REFRESH
|
||||||
|
self.ReadBusy()
|
||||||
|
|
||||||
|
def display(self, imageblack, imagecolor):
|
||||||
|
self.send_command(0x10)
|
||||||
|
for i in range(0, int(self.width * self.height / 8)):
|
||||||
|
self.send_data(imageblack[i])
|
||||||
|
self.send_command(0x92)
|
||||||
|
|
||||||
|
self.send_command(0x13)
|
||||||
|
for i in range(0, int(self.width * self.height / 8)):
|
||||||
|
self.send_data(imagecolor[i])
|
||||||
|
self.send_command(0x92)
|
||||||
|
|
||||||
|
self.send_command(0x12) # REFRESH
|
||||||
|
self.ReadBusy()
|
||||||
|
|
||||||
|
def Clear(self):
|
||||||
|
self.send_command(0x10)
|
||||||
|
for i in range(0, int(self.width * self.height / 8)):
|
||||||
|
self.send_data(0xFF)
|
||||||
|
self.send_command(0x92)
|
||||||
|
|
||||||
|
self.send_command(0x13)
|
||||||
|
for i in range(0, int(self.width * self.height / 8)):
|
||||||
|
self.send_data(0xFF)
|
||||||
|
self.send_command(0x92)
|
||||||
|
|
||||||
|
self.send_command(0x12) # REFRESH
|
||||||
|
self.ReadBusy()
|
||||||
|
|
||||||
|
def sleep(self):
|
||||||
|
self.send_command(0x02) # POWER_OFF
|
||||||
|
self.ReadBusy()
|
||||||
|
self.send_command(0x07) # DEEP_SLEEP
|
||||||
|
self.send_data(0xA5) # check code
|
||||||
|
|
||||||
|
# epdconfig.module_exit()
|
||||||
|
### END OF FILE ###
|
||||||
|
|
@@ -28,6 +28,10 @@ class Voice:
|
|||||||
self._('AI ready.'),
|
self._('AI ready.'),
|
||||||
self._('The neural network is ready.')])
|
self._('The neural network is ready.')])
|
||||||
|
|
||||||
|
def on_keys_generation(self):
|
||||||
|
return random.choice([
|
||||||
|
self._('Generating keys, do not turn off ...')])
|
||||||
|
|
||||||
def on_normal(self):
|
def on_normal(self):
|
||||||
return random.choice([
|
return random.choice([
|
||||||
'',
|
'',
|
||||||
|
Reference in New Issue
Block a user