mirror of
https://github.com/jayofelony/pwnagotchi.git
synced 2025-07-01 18:37:27 -04:00
Compare commits
718 Commits
Author | SHA1 | Date | |
---|---|---|---|
f9b2a76665 | |||
80279ca02b | |||
c7a06a4e43 | |||
9b969375b7 | |||
61eff05227 | |||
21151dd9ef | |||
cd3eb0963b | |||
a41a8a277f | |||
4b7a3bc138 | |||
6fa6ca8a67 | |||
6b5631373a | |||
de5cd8705f | |||
a317528aeb | |||
1ec6931740 | |||
45138c7fae | |||
dcd91268a1 | |||
27c8a113de | |||
9ea6728a26 | |||
af4e4d9a35 | |||
b2175cbcc4 | |||
28c61ae02c | |||
358d400d23 | |||
09aace1cf7 | |||
fc0c72f7df | |||
b13243466b | |||
bb34980179 | |||
ff0c1aa2f6 | |||
7f2b3c7106 | |||
3e5d802e8d | |||
3e5e2bfb30 | |||
aa4c352bbd | |||
578731e978 | |||
6d9131b4f6 | |||
4c826c90cb | |||
1b11383011 | |||
6e93616ed7 | |||
824cd7efef | |||
246e280201 | |||
4e6b513fc7 | |||
5dbdf1e8de | |||
b28a0eb606 | |||
97f7e71ad7 | |||
89b9eb0e46 | |||
d1f0b7bb08 | |||
7e26085016 | |||
772d844526 | |||
0377b396c9 | |||
3fc319b650 | |||
f34471e044 | |||
de7e9b1c6d | |||
868314e244 | |||
7a8cc36a38 | |||
a3dce7fcbd | |||
0472faa2bf | |||
ae2eb77c7b | |||
b30daddb80 | |||
f81a8a7b75 | |||
cb99e99e0e | |||
3c07d6860e | |||
2cd564137d | |||
bcd49f3b40 | |||
05f75b1605 | |||
f98350f8a8 | |||
29b1ec1ce9 | |||
7764e1dfdf | |||
35deab25f2 | |||
39d6a774e9 | |||
4d024afded | |||
bad8d458cc | |||
c2d231a3fb | |||
724e674134 | |||
a70772b312 | |||
4f07fce44b | |||
5c8e57fd83 | |||
9c0891b9c6 | |||
d543144d27 | |||
4f332c14f0 | |||
b95ec0dd0f | |||
09ecfeed66 | |||
de62008d2d | |||
df3666777b | |||
459cc8e54c | |||
d1c5cd5e5d | |||
d16fa0097c | |||
ffa80a5d28 | |||
11fe8a607c | |||
7dc0eb4591 | |||
9b64b67745 | |||
55ba9e0ee8 | |||
b016766e87 | |||
387e11a5c5 | |||
913c22193d | |||
8be4d40960 | |||
26fbedd52d | |||
79c590fb38 | |||
ef3696510b | |||
7b7bbc4aad | |||
b18546f30d | |||
4d1aa79bfd | |||
6145692db0 | |||
a97f45859a | |||
8c0b66edfb | |||
4dd7520d16 | |||
0700bb2c1a | |||
4639870dc5 | |||
3d6e920ec6 | |||
ec47242af3 | |||
71e405fa63 | |||
9911cdb5bb | |||
10c4e5a984 | |||
ee1d1dba9b | |||
e4b95700a5 | |||
0b987656b5 | |||
f36d4aea77 | |||
3ccfbbc940 | |||
f29c0e08df | |||
0c44b02765 | |||
39c38d130b | |||
81f71710b6 | |||
14ae34860f | |||
b3df38e921 | |||
ae108b402c | |||
9e08a6294c | |||
68d123c4ec | |||
ad4b6891a6 | |||
260e501628 | |||
ad9f800b13 | |||
83e9ee6318 | |||
5b20c0fb16 | |||
6a217f3684 | |||
5bbbcea2af | |||
07e7ec661c | |||
706ae3b152 | |||
5713613451 | |||
b6b5e92f6b | |||
85856b2698 | |||
bdc6afd0be | |||
a5679a8f6c | |||
6f16c65132 | |||
9217338a74 | |||
cafc8de564 | |||
1cf8faddda | |||
3c4cb155d3 | |||
6edd91f9b9 | |||
246996a50a | |||
5956eb22b5 | |||
b8ecc7a75f | |||
644d342daa | |||
a78042b0fd | |||
8edf72c8ef | |||
53e4b7f2ba | |||
f6f30632f8 | |||
a8563d8107 | |||
dab9b8c4bb | |||
4ade52925d | |||
cf1e8aa4fa | |||
2d02b8c2a0 | |||
12b386f74e | |||
fa2b8b97c4 | |||
a2fd70ee27 | |||
13bedbc834 | |||
c67915af00 | |||
131bfc4c1c | |||
d21c2cb30c | |||
90d1e33eb3 | |||
b9b5ae2fc8 | |||
8fba99fa08 | |||
c918b620a8 | |||
6c3e5229ea | |||
3486020e54 | |||
29ecad81cd | |||
489deb09d7 | |||
42d1f92e02 | |||
99f75bf5eb | |||
4918b9a921 | |||
f55a08609d | |||
4a2b8e71a3 | |||
4fb7752493 | |||
e9b4667ffc | |||
ccf491a5dc | |||
c47fb379cb | |||
1d216a959c | |||
7108edc2ad | |||
3bfa73087c | |||
fa154ee279 | |||
34700dd30b | |||
dda8f24904 | |||
89103257ea | |||
76045b3564 | |||
e04badc254 | |||
32cea62999 | |||
2b0824c8ab | |||
4a076bd229 | |||
d5ef7dc303 | |||
f21998214b | |||
d0f4796265 | |||
e582925a8f | |||
111f871ea2 | |||
4393854718 | |||
ccce288182 | |||
deefada29c | |||
28c1e112c8 | |||
a45fd4627b | |||
164755590f | |||
ae2c0e3db4 | |||
13515dfa98 | |||
af632abc63 | |||
4029b2ffa0 | |||
32d244ec6f | |||
79e141320c | |||
330110843c | |||
dc5b297a82 | |||
e8f23d654c | |||
aedbf6bbfb | |||
0b3fc98f1c | |||
7edaa9040e | |||
6b6f5923ee | |||
d6bd65333e | |||
f81f1cedfc | |||
f6fa4ef39e | |||
e2ac10babb | |||
bff310ea3e | |||
a53130321f | |||
730cdad7cd | |||
85746046e1 | |||
fbe8211a10 | |||
ea4f495ab8 | |||
afb840212d | |||
82b64baaed | |||
244d3c03f9 | |||
c440a73824 | |||
48b3d6dcb5 | |||
b9531c60a2 | |||
27aac52e1f | |||
77998cd214 | |||
03c33e1be3 | |||
4f63af63a8 | |||
6ceb27f837 | |||
5d5cde5409 | |||
2630068c7b | |||
de940d0e77 | |||
0f36a10567 | |||
65a93812ef | |||
022ebccf30 | |||
e5812e86c1 | |||
1ee940c798 | |||
e3f0da6193 | |||
f9efbb56cd | |||
89a589af72 | |||
a100933678 | |||
3584f3f551 | |||
3fdcebc90c | |||
e33bec1d31 | |||
de0a09e39d | |||
dbb83b4825 | |||
956c083985 | |||
62c324730c | |||
8dc6f78610 | |||
0542c002d0 | |||
702d463194 | |||
087fd9cf09 | |||
28335419f6 | |||
db444588dd | |||
de08804b7f | |||
9d6a7e9441 | |||
14480346fe | |||
7b6ad4e99a | |||
313edb1524 | |||
820f2cef59 | |||
c76d9f4909 | |||
69ff6cd03c | |||
09f6281666 | |||
2f1c216387 | |||
87d8beb9be | |||
e63be1ffd8 | |||
d5755103a0 | |||
2014c22262 | |||
5295692da9 | |||
b7eb86d55c | |||
81c3581138 | |||
7d8b66e1cb | |||
689d39c450 | |||
8eccd71c6b | |||
caf73700fd | |||
e9c12dde94 | |||
3f40c0b7b3 | |||
318e3480ce | |||
1614d1f94d | |||
4e1f08f6a8 | |||
cbc601e38b | |||
20bae7adaa | |||
ed62ac0b84 | |||
f0ecee0555 | |||
8e99ae419d | |||
88957059ef | |||
c42e8d0f5d | |||
5b5718c186 | |||
70f0dcb891 | |||
8f02c36d63 | |||
9941f093e9 | |||
de07226be1 | |||
6603e01005 | |||
e2fa3ca138 | |||
357ffc67e5 | |||
ea8fb5e533 | |||
31afd6d0ba | |||
b38274e652 | |||
518e8c219c | |||
4e07fbf1aa | |||
b3e81a95c8 | |||
050966215b | |||
fef442edbb | |||
1064936503 | |||
a762a7f763 | |||
8fd0e358a9 | |||
9a941c1d46 | |||
ead5a3baac | |||
ac2973889d | |||
8991dd6811 | |||
415e5cd551 | |||
47705ba1a2 | |||
7c7bbc770b | |||
ab83de4905 | |||
8f7741cd9e | |||
835886e6f6 | |||
6b2039a8e6 | |||
d53d0c7841 | |||
6c40998b51 | |||
a71a90ba3d | |||
b03f6f747b | |||
a8ba88c9cc | |||
f597bd6d29 | |||
a3f103ac06 | |||
e0a068e51d | |||
dc1b3c7635 | |||
3657859a73 | |||
33ff5a0bf8 | |||
666f65c640 | |||
84d45a0d45 | |||
06a4491008 | |||
5485f83ca6 | |||
8a242a707b | |||
8a3eacb5d2 | |||
ab541458fa | |||
ac345c2ee7 | |||
468cfd9f4f | |||
d6bc5c0e66 | |||
4905eb6b26 | |||
697a7778b1 | |||
9287283ee7 | |||
8013109ef7 | |||
f140fe1a2d | |||
8fe503c67d | |||
d81013412e | |||
3c701822bd | |||
2f5ddb492a | |||
9e3324221d | |||
b069b82984 | |||
cb7d965271 | |||
7abf9ff8da | |||
18fb956251 | |||
afb1d11cd8 | |||
627be80e6c | |||
c4c4d6c417 | |||
8dcae13ce9 | |||
1352e99774 | |||
2182d7c29d | |||
703c05a93b | |||
697cc5d88b | |||
4b04f9b7a5 | |||
c7b94a0707 | |||
5116bac2a7 | |||
51625e61f9 | |||
bf9a0a96c1 | |||
bd03f07aa8 | |||
3f13df8f20 | |||
ce0275f2ae | |||
b971f18f75 | |||
ea9d11d018 | |||
8a572f1b70 | |||
0c4f2a5093 | |||
7edd752664 | |||
cc550aa236 | |||
ff033d41d3 | |||
6d0a0d8d5f | |||
b33af167d4 | |||
9053762e71 | |||
26fef7dd99 | |||
5dd17291f7 | |||
bac79f3465 | |||
bb8dfe0244 | |||
1b16975031 | |||
dd2b559ebc | |||
93ba2a7f79 | |||
dd18072002 | |||
1eafbb841f | |||
aa817288ea | |||
43285eb2c0 | |||
4b29476c2f | |||
1a8ff930d9 | |||
8a65afdc8f | |||
6fcd1b0e23 | |||
e0b0f5d800 | |||
2243079bf6 | |||
e10eb31ed1 | |||
1c8114e444 | |||
91638a151f | |||
d4adaabcbd | |||
006cdb0fe3 | |||
29386fb945 | |||
4b4646d604 | |||
3fae6ec312 | |||
09a82aa0b4 | |||
541865a2eb | |||
928de2769d | |||
6987840da2 | |||
58de15ce2d | |||
b5ea3da619 | |||
6c68d4608f | |||
14a727954b | |||
aeada2ee6e | |||
ebb8fef3fc | |||
e531288369 | |||
2015b56c5d | |||
2497475057 | |||
0bdbbc23fd | |||
111787544f | |||
efa5d8b197 | |||
9e5fb49d7c | |||
cbbd7d5a6d | |||
90c5818123 | |||
eb76ef4c54 | |||
59e42daeb5 | |||
5b7014c68e | |||
42cc3136ee | |||
2a243870f7 | |||
8f043ff5ef | |||
255bbdbc08 | |||
6f57b66cf4 | |||
a4c25e9996 | |||
9495d55296 | |||
69e7503d67 | |||
25cb1f2175 | |||
54b7ce5d0d | |||
1f9afd541d | |||
87eae76a58 | |||
6691257036 | |||
2149c5dbdf | |||
77af772b4b | |||
14e4fc6d47 | |||
5be8580a59 | |||
f7a599ab8f | |||
5f907b236a | |||
bc92613700 | |||
501ec9ca2b | |||
e5e0180f3c | |||
ea60808700 | |||
a34db250b5 | |||
d29aca15a9 | |||
8531b89771 | |||
59d510d0e1 | |||
913b1a6e1d | |||
de2cdaa3c9 | |||
f2cf34a8b9 | |||
bbb46128fe | |||
46713b6e73 | |||
aa2b09fb21 | |||
9125e43b20 | |||
7e4d926b14 | |||
6417ef5a78 | |||
46c03063fe | |||
d5384d5a81 | |||
e800c66e57 | |||
e3a404cb39 | |||
6cb6aaeb81 | |||
5761dac073 | |||
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 | |||
0585fe75fe | |||
27e784a5d7 | |||
c02efb5224 | |||
a7634a2b4a | |||
ca4feb895e | |||
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 | |||
cca3e77d50 | |||
4fe603bf5e | |||
9a9ee70a78 | |||
7fc8838f76 | |||
94b2ff7047 | |||
8edb0fdaa4 | |||
b557768159 | |||
5a6967eb4d | |||
c0c35231cf | |||
88362b3354 | |||
e50cf04967 | |||
85a64a3914 | |||
63d0c20a3e | |||
044639a6d4 | |||
7fc2c6b25f | |||
c5d5d9bb14 | |||
129ae92447 | |||
2560692ee2 | |||
1424e0d510 | |||
2c9eaa436a | |||
0c5c6058a5 | |||
a3fb39d78c | |||
bac62bc4aa | |||
f7a5f2a554 | |||
6921fd14a3 | |||
0dbd611ed5 | |||
18b9093f70 | |||
26913016b9 | |||
0786b6757e | |||
0f4eda8039 | |||
1672a35f09 | |||
80e7e6c550 | |||
ca274036ab | |||
649e8a4222 | |||
63f0a761c1 | |||
8a64918b55 | |||
c27cc0ac24 | |||
c9bdce0ea3 | |||
6422371346 | |||
3c5a95e0b8 |
3
.github/FUNDING.yml
vendored
3
.github/FUNDING.yml
vendored
@ -1,4 +1,3 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
patreon: pwnagotchi_torch
|
||||
github: jayofelony
|
||||
github: jayofelony
|
36
.github/ISSUE_TEMPLATE.md
vendored
36
.github/ISSUE_TEMPLATE.md
vendored
@ -1,36 +0,0 @@
|
||||
<!--- Provide a general summary of the issue in the Title above -->
|
||||
|
||||
## Expected Behaviour
|
||||
<!--- If you're describing a bug, tell us what should happen -->
|
||||
<!--- If you're suggesting a change/improvement, tell us how it should work -->
|
||||
|
||||
## Current Behaviour
|
||||
<!--- If describing a bug, tell us what happens instead of the expected behavior -->
|
||||
<!--- If suggesting a change/improvement, explain the difference from current behavior -->
|
||||
|
||||
## Possible Solution
|
||||
<!--- Not obligatory, but suggest a fix/reason for the bug, -->
|
||||
<!--- or ideas how to implement the addition or change -->
|
||||
|
||||
## Steps to Reproduce (for bugs)
|
||||
<!--- Provide a link to a live example, or an unambiguous set of steps to -->
|
||||
<!--- reproduce this bug. Include code to reproduce, if relevant -->
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
4.
|
||||
|
||||
## Context
|
||||
<!--- How has this issue affected you? What are you trying to accomplish? -->
|
||||
<!--- Providing context helps us come up with a solution that is most useful in the real world -->
|
||||
|
||||
## Your Environment
|
||||
|
||||
- [ ] You're using the official images
|
||||
|
||||
- [ ] You're using a raspberry pi 0
|
||||
|
||||
- [ ] You're using a supported LCD
|
||||
|
||||
|
||||
<!--- Include as many relevant details about the environment you experienced the bug in -->
|
62
.github/ISSUE_TEMPLATE.yml
vendored
Normal file
62
.github/ISSUE_TEMPLATE.yml
vendored
Normal file
@ -0,0 +1,62 @@
|
||||
name: Bug Report
|
||||
description: File a bug report.
|
||||
title: "[Bug]: "
|
||||
labels: ["bug", "triage"]
|
||||
projects: ["pwnagotchi"]
|
||||
assignees:
|
||||
- jayofelony
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for taking the time to fill out this bug report!
|
||||
- type: input
|
||||
id: contact
|
||||
attributes:
|
||||
label: Contact Details
|
||||
description: How can we get in touch with you if we need more info?
|
||||
placeholder: ex. email@example.com
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
id: what-happened
|
||||
attributes:
|
||||
label: What happened?
|
||||
description: Also tell us, what did you expect to happen?
|
||||
placeholder: Tell us what you see!
|
||||
value: "A bug happened!"
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: version
|
||||
attributes:
|
||||
label: Version
|
||||
description: What version of our software are you running?
|
||||
options:
|
||||
- 2.9.4-2
|
||||
default: 0
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: 3rd-party-hardware
|
||||
attributes:
|
||||
label: 3rd Party Hardware
|
||||
description: Are you using any 3rd party hardware? By selecting "Yes", you agree that you have tested the issue without the 3rd party hardware. And acknowledge that the issue may be related to the 3rd party hardware, for which we cannot provide full support.
|
||||
options:
|
||||
- "Yes"
|
||||
- "No"
|
||||
default: 1
|
||||
- type: textarea
|
||||
id: logs
|
||||
attributes:
|
||||
label: Relevant log output
|
||||
description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
|
||||
render: shell
|
||||
- type: checkboxes
|
||||
id: terms
|
||||
attributes:
|
||||
label: Code of Conduct
|
||||
description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/jayofelony/pwnagotchi/blob/noai/CODE_OF_CONDUCT.md).
|
||||
options:
|
||||
- label: I agree to follow this project's Code of Conduct
|
||||
required: true
|
32
.github/ISSUE_TEMPLATE/bug_report.md
vendored
32
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@ -1,32 +0,0 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: "[BUG]"
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. ...
|
||||
2. ...
|
||||
3. ...
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Environment (please complete the following information):**
|
||||
- Pwnagotchi version
|
||||
- OS version
|
||||
- Type of hardware
|
||||
- Any additional hardware used
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
62
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
62
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
@ -0,0 +1,62 @@
|
||||
name: Bug Report
|
||||
description: File a bug report.
|
||||
title: "[Bug]: "
|
||||
labels: ["bug", "triage"]
|
||||
projects: ["pwnagotchi"]
|
||||
assignees:
|
||||
- jayofelony
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for taking the time to fill out this bug report!
|
||||
- type: input
|
||||
id: contact
|
||||
attributes:
|
||||
label: Contact Details
|
||||
description: How can we get in touch with you if we need more info?
|
||||
placeholder: ex. email@example.com
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
id: what-happened
|
||||
attributes:
|
||||
label: What happened?
|
||||
description: Also tell us, what did you expect to happen?
|
||||
placeholder: Tell us what you see!
|
||||
value: "A bug happened!"
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: version
|
||||
attributes:
|
||||
label: Version
|
||||
description: What version of our software are you running?
|
||||
options:
|
||||
- 2.9.4-2
|
||||
default: 0
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: 3rd-party-hardware
|
||||
attributes:
|
||||
label: 3rd Party Hardware
|
||||
description: Are you using any 3rd party hardware? By selecting "Yes", you agree that you have tested the issue without the 3rd party hardware. And acknowledge that the issue may be related to the 3rd party hardware, for which we cannot provide full support.
|
||||
options:
|
||||
- "Yes"
|
||||
- "No"
|
||||
default: 1
|
||||
- type: textarea
|
||||
id: logs
|
||||
attributes:
|
||||
label: Relevant log output
|
||||
description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
|
||||
render: shell
|
||||
- type: checkboxes
|
||||
id: terms
|
||||
attributes:
|
||||
label: Code of Conduct
|
||||
description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/jayofelony/pwnagotchi/blob/noai/CODE_OF_CONDUCT.md).
|
||||
options:
|
||||
- label: I agree to follow this project's Code of Conduct
|
||||
required: true
|
59
.github/PULL_REQUEST_TEMPLATE.yml
vendored
Normal file
59
.github/PULL_REQUEST_TEMPLATE.yml
vendored
Normal file
@ -0,0 +1,59 @@
|
||||
name: Pull Request Template
|
||||
description: Template for submitting pull requests.
|
||||
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
## Description
|
||||
Provide a detailed description of your changes here.
|
||||
|
||||
- type: textarea
|
||||
id: motivation_and_context
|
||||
attributes:
|
||||
label: Motivation and Context
|
||||
description: Why is this change necessary? What problem does it solve?
|
||||
placeholder: Add details here. Link to open issues if applicable.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: checkboxes
|
||||
id: issue_proposal
|
||||
attributes:
|
||||
label: Did you raise an issue for this change? ([required](https://github.com/evilsocket/pwnagotchi/blob/master/CONTRIBUTING.md))
|
||||
options:
|
||||
- label: "Yes, I raised an issue."
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: testing
|
||||
attributes:
|
||||
label: How has this been tested?
|
||||
description: Provide a detailed description of how your changes were tested.
|
||||
placeholder: Add details about your testing environment, the tests you ran, and the impact of your changes.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: checkboxes
|
||||
id: types_of_changes
|
||||
attributes:
|
||||
label: What types of changes does this Pull Request introduce?
|
||||
options:
|
||||
- label: "Bug fix (non-breaking change that fixes an issue)"
|
||||
- label: "New feature (non-breaking change that adds functionality)"
|
||||
- label: "Breaking change (fix or feature that affects existing functionality)"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: checkboxes
|
||||
id: checklist
|
||||
attributes:
|
||||
label: Verify the following
|
||||
options:
|
||||
- label: "My code follows the code style of this project."
|
||||
- label: "My change requires a change to the documentation."
|
||||
- label: "I have updated the documentation accordingly."
|
||||
- label: "I have read the [CONTRIBUTION](https://github.com/evilsocket/pwnagotchi/blob/master/CONTRIBUTING.md) guide."
|
||||
- label: "I have signed off my commits with `git commit -s`."
|
||||
validations:
|
||||
required: true
|
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
*.pyc
|
4
.idea/deployment.xml
generated
4
.idea/deployment.xml
generated
@ -1,4 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="PublishConfigData" filePermissions="493" folderPermissions="493" remoteFilesAllowedToDisappearOnAutoupload="false" />
|
||||
<component name="PublishConfigData" filePermissions="493" folderPermissions="493" remoteFilesAllowedToDisappearOnAutoupload="false" confirmBeforeUploading="false">
|
||||
<option name="confirmBeforeUploading" value="false" />
|
||||
</component>
|
||||
</project>
|
5
.idea/misc.xml
generated
5
.idea/misc.xml
generated
@ -3,5 +3,8 @@
|
||||
<component name="Black">
|
||||
<option name="sdkName" value="Python 3.11 (pwnagotchi)" />
|
||||
</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.12 (pwnagotchi)" project-jdk-type="Python SDK" />
|
||||
<component name="PythonCompatibilityInspectionAdvertiser">
|
||||
<option name="version" value="3" />
|
||||
</component>
|
||||
</project>
|
3
.idea/pwnagotchi.iml
generated
3
.idea/pwnagotchi.iml
generated
@ -2,9 +2,10 @@
|
||||
<module type="PYTHON_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<excludeFolder url="file://$MODULE_DIR$/.venv" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/venv" />
|
||||
</content>
|
||||
<orderEntry type="jdk" jdkName="Python 3.10 (pwnagotchi-torch-bookworm)" jdkType="Python SDK" />
|
||||
<orderEntry type="jdk" jdkName="Python 3.12 (pwnagotchi)" jdkType="Python SDK" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
<component name="PyDocumentationSettings">
|
||||
|
10
.idea/sshConfigs.xml
generated
Normal file
10
.idea/sshConfigs.xml
generated
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="SshConfigs">
|
||||
<configs>
|
||||
<sshConfig authType="PASSWORD" connectionConfig="{"serverAliveInterval":300}" id="8b69df7d-cec5-421f-8edf-53ed6233f6b6" port="22" customName="pwnagotchi" nameFormat="CUSTOM" useOpenSSHConfig="true">
|
||||
<option name="customName" value="pwnagotchi" />
|
||||
</sshConfig>
|
||||
</configs>
|
||||
</component>
|
||||
</project>
|
14
.idea/webServers.xml
generated
Normal file
14
.idea/webServers.xml
generated
Normal file
@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="WebServers">
|
||||
<option name="servers">
|
||||
<webServer id="cf2a1148-a103-4472-a782-7debdc6dabf9" name="pwnagotchi">
|
||||
<fileTransfer accessType="SFTP" port="22" sshConfigId="8b69df7d-cec5-421f-8edf-53ed6233f6b6" sshConfig="pwnagotchi">
|
||||
<advancedOptions>
|
||||
<advancedOptions dataProtectionLevel="Private" passiveMode="true" shareSSLContext="true" />
|
||||
</advancedOptions>
|
||||
</fileTransfer>
|
||||
</webServer>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
@ -1,9 +1,6 @@
|
||||
exclude *.pyc .DS_Store .gitignore MANIFEST.in
|
||||
include setup.py
|
||||
include distribute_setup.py
|
||||
exclude *.pyc .gitignore MANIFEST.in
|
||||
include README.md
|
||||
include LICENSE
|
||||
recursive-include bin *
|
||||
recursive-include pwnagotchi *.py
|
||||
recursive-include pwnagotchi *.yml
|
||||
recursive-include pwnagotchi *.*
|
||||
|
57
Makefile
57
Makefile
@ -1,32 +1,3 @@
|
||||
PACKER_VERSION := 1.10.0
|
||||
PWN_HOSTNAME := pwnagotchi
|
||||
PWN_VERSION := $(shell cut -d"'" -f2 < pwnagotchi/_version.py)
|
||||
|
||||
MACHINE_TYPE := $(shell uname -m)
|
||||
ifneq (,$(filter x86_64,$(MACHINE_TYPE)))
|
||||
GOARCH := amd64
|
||||
else ifneq (,$(filter i686,$(MACHINE_TYPE)))
|
||||
GOARCH := 386
|
||||
else ifneq (,$(filter arm64% aarch64%,$(MACHINE_TYPE)))
|
||||
GOARCH := arm64
|
||||
else ifneq (,$(filter arm%,$(MACHINE_TYPE)))
|
||||
GOARCH := arm
|
||||
else
|
||||
GOARCH := amd64
|
||||
$(warning Unable to detect CPU arch from machine type $(MACHINE_TYPE), assuming $(GOARCH))
|
||||
endif
|
||||
|
||||
# The Ansible part of the build can inadvertently change the active hostname of
|
||||
# the build machine while updating the permanent hostname of the build image.
|
||||
# If the unshare command is available, use it to create a separate namespace
|
||||
# so hostname changes won't affect the build machine.
|
||||
UNSHARE := $(shell command -v unshare)
|
||||
ifneq (,$(UNSHARE))
|
||||
UNSHARE := $(UNSHARE) --uts
|
||||
endif
|
||||
|
||||
all: clean image clean
|
||||
|
||||
update_langs:
|
||||
@for lang in pwnagotchi/locale/*/; do\
|
||||
echo "updating language: $$lang ..."; \
|
||||
@ -38,31 +9,3 @@ compile_langs:
|
||||
echo "compiling language: $$lang ..."; \
|
||||
./scripts/language.sh compile $$(basename $$lang); \
|
||||
done
|
||||
|
||||
PACKER := ~/pwnagotchi/packer
|
||||
PACKER_URL := https://releases.hashicorp.com/packer/$(PACKER_VERSION)/packer_$(PACKER_VERSION)_linux_$(GOARCH).zip
|
||||
$(PACKER):
|
||||
mkdir -p $(@D)
|
||||
curl -L "$(PACKER_URL)" -o $(PACKER).zip
|
||||
unzip $(PACKER).zip -d $(@D)
|
||||
rm $(PACKER).zip
|
||||
chmod +x $@
|
||||
|
||||
SDIST := dist/pwnagotchi-$(PWN_VERSION).tar.gz
|
||||
$(SDIST): setup.py pwnagotchi
|
||||
python3 setup.py sdist
|
||||
|
||||
# Building the image requires packer, but don't rebuild the image just because packer updated.
|
||||
pwnagotchi: | $(PACKER)
|
||||
|
||||
# If the packer or ansible files are updated, rebuild the image.
|
||||
pwnagotchi: $(SDIST) builder/pwnagotchi.json.pkr.hcl builder/raspberrypi64.yml $(shell find builder/data -type f)
|
||||
|
||||
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:
|
||||
- rm -rf build dist pwnagotchi.egg-info
|
||||
- rm -f $(PACKER)
|
||||
|
58
README.md
58
README.md
@ -1,58 +1,42 @@
|
||||
# Pwnagotchi-Torch
|
||||
<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 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~~!!.**
|
||||
# Pwnagotchi
|
||||
This is the main source for all forks:
|
||||
- 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.
|
||||
**For installation docs check out the [wiki](https://github.com/jayofelony/pwnagotchi/wiki)!**
|
||||
|
||||
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.6.7), 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)
|
||||
If you want to sponsor this project you can use GH Sponsor or cryptocurrency:
|
||||
|
||||
[GH Sponsor](https://github.com/sponsors/jayofelony)
|
||||
|
||||
Or send some ethereum: 0x33ceC4Abe80fDE460a924d596d4dE31Bc0767bb6
|
||||
|
||||
**Proudly partnering with [PiSugar](https://www.pisugar.com)!!**
|
||||
|
||||
---
|
||||
|
||||
[Pwnagotchi](https://pwnagotchi.ai/) is an [A2C](https://hackernoon.com/intuitive-rl-intro-to-advantage-actor-critic-a2c-4ff545978752)-based "AI" leveraging [bettercap](https://www.bettercap.org/) that learns from its surrounding Wi-Fi environment to maximize the crackable WPA key material it captures (either passively, or by performing authentication and association attacks). This material is collected as PCAP files containing any form of handshake supported by [hashcat](https://hashcat.net/hashcat/), including [PMKIDs](https://www.evilsocket.net/2019/02/13/Pwning-WiFi-networks-with-bettercap-and-the-PMKID-client-less-attack/),
|
||||
[Pwnagotchi](https://pwnagotchi.org/) is a Raspberry Pi leveraging [bettercap](https://www.bettercap.org/) that survives from its surrounding Wi-Fi environment to maximize the crackable WPA key material it captures (either passively, or by performing authentication and association attacks). This material is collected as PCAP files containing any form of handshake supported by [hashcat](https://hashcat.net/hashcat/), including [PMKIDs](https://www.evilsocket.net/2019/02/13/Pwning-WiFi-networks-with-bettercap-and-the-PMKID-client-less-attack/),
|
||||
full and half WPA handshakes.
|
||||
|
||||

|
||||
|
||||
Instead of merely playing [Super Mario or Atari games](https://becominghuman.ai/getting-mario-back-into-the-gym-setting-up-super-mario-bros-in-openais-gym-8e39a96c1e41?gi=c4b66c3d5ced) like most reinforcement learning-based "AI" *(yawn)*, Pwnagotchi tunes [its parameters](https://github.com/evilsocket/pwnagotchi/blob/master/pwnagotchi/defaults.toml) over time to **get better at pwning Wi-Fi things to** in the environments you expose it to.
|
||||
The "old" Pwnagotchi used to have AI to help it learn from its environment, but since then AI seemed to destabilize the Wi-Fi firmware. So I have chosen to remove the AI completely to give the Pwnagotchi more up-time and longer battery life when taking it on a walk.
|
||||
|
||||
More specifically, Pwnagotchi is using an [LSTM with MLP feature extractor](https://stable-baselines.readthedocs.io/en/master/modules/policies.html#stable_baselines.common.policies.MlpLstmPolicy) as its policy network for the [A2C agent](https://stable-baselines.readthedocs.io/en/master/modules/a2c.html). If you're unfamiliar with A2C, here is [a very good introductory explanation](https://hackernoon.com/intuitive-rl-intro-to-advantage-actor-critic-a2c-4ff545978752) (in comic form!) of the basic principles behind how Pwnagotchi learns. (You can read more about how Pwnagotchi learns in the [Usage](https://www.pwnagotchi.ai/usage/#training-the-ai) doc.)
|
||||
|
||||
**Keep in mind:** Unlike the usual RL simulations, Pwnagotchi learns over time. Time for a Pwnagotchi is measured in epochs; a single epoch can last from a few seconds to minutes, depending on how many access points and client stations are visible. Do not expect your Pwnagotchi to perform amazingly well at the very beginning, as it will be [exploring](https://hackernoon.com/intuitive-rl-intro-to-advantage-actor-critic-a2c-4ff545978752) several combinations of [key parameters](https://www.pwnagotchi.ai/usage/#training-the-ai) to determine ideal adjustments for pwning the particular environment you are exposing it to during its beginning epochs ... but ** listen to your Pwnagotchi when it tells you it's boring!** Bring it into novel Wi-Fi environments with you and have it observe new networks and capture new handshakes—and you'll see. :)
|
||||
|
||||
Multiple units within close physical proximity can "talk" to each other, advertising their presence to each other by broadcasting custom information elements using a parasite protocol I've built on top of the existing dot11 standard. Over time, two or more units trained together will learn to cooperate upon detecting each other's presence by dividing the available channels among them for optimal pwnage.
|
||||
Multiple units within close physical proximity can "talk" to each other, advertising their presence to each other by broadcasting custom information elements using a parasite protocol I've built on top of the existing dot11 standard.
|
||||
|
||||
## Documentation
|
||||
|
||||
https://www.pwnagotchi.ai
|
||||
https://github.com/jayofelony/pwnagotchi/wiki
|
||||
https://pwnagotchi.org
|
||||
|
||||
## Links
|
||||
|
||||
| | Official Links |
|
||||
|-----------|-------------------------------------------------------------|
|
||||
| Website | [pwnagotchi.ai](https://pwnagotchi.ai/) |
|
||||
| Forum | [community.pwnagotchi.ai](https://community.pwnagotchi.ai/) |
|
||||
| Slack | [pwnagotchi.slack.com](https://invite.pwnagotchi.ai/) |
|
||||
| Subreddit | [r/pwnagotchi](https://www.reddit.com/r/pwnagotchi/) |
|
||||
| Twitter | [@pwnagotchi](https://twitter.com/pwnagotchi) |
|
||||
| | Official Links |
|
||||
|-----------|----------------------------------------------------------|
|
||||
| Website | [pwnagotchi.org](https://pwnagotchi.org/) |
|
||||
| Chat | [discord](https://discord.gg/PGgnzFbz4M) |
|
||||
| Subreddit | [r/pwnagotchi](https://www.reddit.com/r/pwnagotchi/) |
|
||||
|
||||
## License
|
||||
|
||||
`pwnagotchi` is made with ♥ by [@evilsocket](https://twitter.com/evilsocket) and the [amazing dev team](https://github.com/evilsocket/pwnagotchi/graphs/contributors). It is released under the GPL3 license.
|
||||
`pwnagotchi` created by [@evilsocket](https://twitter.com/evilsocket) and updated by [us](https://github.com/jayofelony/pwnagotchi/graphs/contributors). It is released under the GPL3 license.
|
||||
|
226
bin/pwnagotchi
226
bin/pwnagotchi
@ -1,226 +0,0 @@
|
||||
#!/usr/bin/python3
|
||||
import logging
|
||||
import argparse
|
||||
import time
|
||||
import signal
|
||||
import sys
|
||||
import toml
|
||||
import requests
|
||||
import os
|
||||
|
||||
import pwnagotchi
|
||||
from pwnagotchi import utils
|
||||
from pwnagotchi.google import cmd as google_cmd
|
||||
from pwnagotchi.plugins import cmd as plugins_cmd
|
||||
from pwnagotchi import log
|
||||
from pwnagotchi import restart
|
||||
from pwnagotchi import fs
|
||||
from pwnagotchi.utils import DottedTomlEncoder, parse_version as version_to_tuple
|
||||
|
||||
|
||||
def pwnagotchi_cli():
|
||||
def do_clear(display):
|
||||
logging.info("clearing the display ...")
|
||||
display.clear()
|
||||
sys.exit(0)
|
||||
|
||||
def do_manual_mode(agent):
|
||||
logging.info("entering manual mode ...")
|
||||
|
||||
agent.mode = 'manual'
|
||||
agent.last_session.parse(agent.view(), args.skip_session)
|
||||
if not args.skip_session:
|
||||
logging.info(
|
||||
"the last session lasted %s (%d completed epochs, trained for %d), average reward:%s (min:%s max:%s)" % (
|
||||
agent.last_session.duration_human,
|
||||
agent.last_session.epochs,
|
||||
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()
|
||||
|
||||
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)
|
||||
|
||||
def add_parsers(parser):
|
||||
"""
|
||||
Adds the plugins and google subcommands
|
||||
"""
|
||||
subparsers = parser.add_subparsers()
|
||||
|
||||
# Add parsers from plugins_cmd
|
||||
plugins_cmd.add_parsers(subparsers)
|
||||
|
||||
# Add parsers from google_cmd
|
||||
google_cmd.add_parsers(subparsers)
|
||||
|
||||
parser = argparse.ArgumentParser(prog="pwnagotchi")
|
||||
# pwnagotchi --help
|
||||
parser.add_argument('-C', '--config', action='store', dest='config', default='/etc/pwnagotchi/default.toml',
|
||||
help='Main configuration file.')
|
||||
parser.add_argument('-U', '--user-config', action='store', dest='user_config', default='/etc/pwnagotchi/config.toml',
|
||||
help='If this file exists, configuration will be merged and this will override default values.')
|
||||
|
||||
parser.add_argument('--manual', dest="do_manual", action="store_true", default=False, help="Manual mode.")
|
||||
parser.add_argument('--skip-session', dest="skip_session", action="store_true", default=False,
|
||||
help="Skip last session parsing in manual mode.")
|
||||
|
||||
parser.add_argument('--clear', dest="do_clear", action="store_true", default=False,
|
||||
help="Clear the ePaper display and exit.")
|
||||
|
||||
parser.add_argument('--debug', dest="debug", action="store_true", default=False,
|
||||
help="Enable debug logs.")
|
||||
|
||||
parser.add_argument('--version', dest="version", action="store_true", default=False,
|
||||
help="Print the version.")
|
||||
|
||||
parser.add_argument('--print-config', dest="print_config", action="store_true", default=False,
|
||||
help="Print the configuration.")
|
||||
|
||||
# Jayofelony added these
|
||||
parser.add_argument('--check-update', dest="check_update", action="store_true", default=False,
|
||||
help="Check for updates on Pwnagotchi. And tells current version.")
|
||||
parser.add_argument('--donate', dest="donate", action="store_true", default=False,
|
||||
help="How to donate to this project.")
|
||||
|
||||
# pwnagotchi plugins --help
|
||||
add_parsers(parser)
|
||||
args = parser.parse_args()
|
||||
|
||||
if plugins_cmd.used_plugin_cmd(args):
|
||||
config = utils.load_config(args)
|
||||
log.setup_logging(args, config)
|
||||
rc = plugins_cmd.handle_cmd(args, config)
|
||||
sys.exit(rc)
|
||||
if google_cmd.used_google_cmd(args):
|
||||
config = utils.load_config(args)
|
||||
log.setup_logging(args, config)
|
||||
rc = google_cmd.handle_cmd(args)
|
||||
sys.exit(rc)
|
||||
|
||||
if args.version:
|
||||
print(pwnagotchi.__version__)
|
||||
sys.exit(0)
|
||||
|
||||
if args.donate:
|
||||
print("Donations can made @ https://www.patreon.com/pwnagotchi_torch \n\nBut only if you really want to!")
|
||||
sys.exit(0)
|
||||
|
||||
if args.check_update:
|
||||
resp = requests.get("https://api.github.com/repos/jayofelony/pwnagotchi-bookworm/releases/latest")
|
||||
latest = resp.json()
|
||||
latest_ver = latest['tag_name'].replace('v', '')
|
||||
|
||||
local = version_to_tuple(pwnagotchi.__version__)
|
||||
remote = version_to_tuple(latest_ver)
|
||||
if remote > local:
|
||||
user_input = input("There is a new version available! Update from v%s to v%s?\n[y(es)/n(o)]"
|
||||
% (pwnagotchi.__version__, latest_ver))
|
||||
# input validation
|
||||
if user_input.lower() in ('y', 'yes'):
|
||||
if os.path.exists('/root/.auto-update'):
|
||||
os.system("rm /root/.auto-update && systemctl restart pwnagotchi")
|
||||
os.system("systemctl restart pwnagotchi")
|
||||
print("Okay, give me a couple minutes. Just watch pwnlog while you wait.")
|
||||
print("Okay, guess not!")
|
||||
print("You are currently on the latest release, v%s." % pwnagotchi.__version__)
|
||||
sys.exit(0)
|
||||
|
||||
config = utils.load_config(args)
|
||||
|
||||
if args.print_config:
|
||||
print(toml.dumps(config, encoder=DottedTomlEncoder()))
|
||||
sys.exit(0)
|
||||
|
||||
from pwnagotchi.identity import KeyPair
|
||||
from pwnagotchi.agent import Agent
|
||||
from pwnagotchi.ui import fonts
|
||||
from pwnagotchi.ui.display import Display
|
||||
from pwnagotchi import grid
|
||||
from pwnagotchi import plugins
|
||||
|
||||
pwnagotchi.config = config
|
||||
fs.setup_mounts(config)
|
||||
log.setup_logging(args, config)
|
||||
fonts.init(config)
|
||||
|
||||
pwnagotchi.set_name(config['main']['name'])
|
||||
|
||||
plugins.load(config)
|
||||
|
||||
display = Display(config=config, state={'name': '%s>' % pwnagotchi.name()})
|
||||
|
||||
if args.do_clear:
|
||||
do_clear(display)
|
||||
sys.exit(0)
|
||||
|
||||
agent = Agent(view=display, config=config, keypair=KeyPair(view=display))
|
||||
|
||||
def usr1_handler(*unused):
|
||||
logging.info('Received USR1 singal. Restart process ...')
|
||||
restart("MANU" if args.do_manual else "AUTO")
|
||||
|
||||
signal.signal(signal.SIGUSR1, usr1_handler)
|
||||
|
||||
if args.do_manual:
|
||||
do_manual_mode(agent)
|
||||
else:
|
||||
do_auto_mode(agent)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pwnagotchi_cli()
|
@ -1,5 +0,0 @@
|
||||
[main]
|
||||
plugins=keyfile,ifupdown
|
||||
|
||||
[ifupdown]
|
||||
managed=true
|
@ -1,36 +0,0 @@
|
||||
_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
|
@ -1,26 +0,0 @@
|
||||
# /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
|
@ -1,2 +0,0 @@
|
||||
allow-hotplug eth0
|
||||
iface eth0 inet dhcp
|
@ -1,2 +0,0 @@
|
||||
auto lo
|
||||
iface lo inet loopback
|
@ -1,8 +0,0 @@
|
||||
allow-hotplug usb0
|
||||
iface usb0 inet static
|
||||
address 10.0.0.2
|
||||
netmask 255.255.255.0
|
||||
network 10.0.0.0
|
||||
broadcast 10.0.0.255
|
||||
gateway 10.0.0.1
|
||||
metric 101
|
@ -1,2 +0,0 @@
|
||||
allow-hotplug wlan0
|
||||
iface wlan0 inet static
|
@ -1,13 +0,0 @@
|
||||
[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
|
@ -1,19 +0,0 @@
|
||||
[Unit]
|
||||
Description=pwnagotchi Deep Reinforcement Learning instrumenting bettercap for WiFI pwning.
|
||||
Documentation=https://pwnagotchi.ai
|
||||
Wants=network.target
|
||||
After=pwngrid-peer.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
WorkingDirectory=~
|
||||
ExecStart=/usr/bin/pwnagotchi-launcher
|
||||
Restart=always
|
||||
RestartSec=30
|
||||
TasksMax=infinity
|
||||
LimitNPROC=infinity
|
||||
StandardOutput=null
|
||||
StandardError=null
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
@ -1,16 +0,0 @@
|
||||
[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 /home/pi/logs/pwngrid-peer.log -iface wlan0mon
|
||||
Restart=always
|
||||
RestartSec=30
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
@ -1,33 +0,0 @@
|
||||
#!/bin/sh
|
||||
_hostname=$(hostname)
|
||||
_version=$(cut -d"'" -f2 < /usr/local/lib/python3.9/dist-packages/pwnagotchi/_version.py)
|
||||
echo
|
||||
echo "(◕‿‿◕) $_hostname"
|
||||
echo
|
||||
echo " Hi! I'm a pwnagotchi $_version, please take good care of me!"
|
||||
echo " Here are some basic things you need to know to raise me properly!"
|
||||
echo
|
||||
echo " If you want to change my configuration, use /etc/pwnagotchi/config.toml"
|
||||
echo " All plugin config files are located in /etc/pwnagotchi/conf.d/"
|
||||
echo " Read the readme if you want to use gdrivesync plugin!!"
|
||||
echo
|
||||
echo " All the configuration options can be found on /etc/pwnagotchi/default.toml,"
|
||||
echo " but don't change this file because I will recreate it every time I'm restarted!"
|
||||
echo
|
||||
echo " I use oPwnGrid as my main API, you can check stats at https://opwngrid.xyz"
|
||||
echo
|
||||
echo " I'm managed by systemd. Here are some basic commands."
|
||||
echo
|
||||
echo " If you want to know what I'm doing, you can check my logs with the command"
|
||||
echo " - pwnlog"
|
||||
echo " - sudo pwnagotchi --version, to check the current version"
|
||||
echo " - sudo pwnagotchi --donate, to see how you can donate to this project"
|
||||
echo " - sudo pwnagotchi --check-update, to see if there is a new version available"
|
||||
echo
|
||||
echo " If you want to know if I'm running, you can use"
|
||||
echo " sudo systemctl status pwnagotchi"
|
||||
echo
|
||||
echo " You can restart me using"
|
||||
echo " pwnkill"
|
||||
echo
|
||||
echo " You learn more about me at https://pwnagotchi.ai/"
|
@ -1,15 +0,0 @@
|
||||
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
|
@ -1,28 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
source /usr/bin/pwnlib
|
||||
|
||||
# we need to decrypt something
|
||||
if is_crypted_mode; then
|
||||
while ! is_decrypted; do
|
||||
echo "Waiting for decryption..."
|
||||
sleep 1
|
||||
done
|
||||
fi
|
||||
|
||||
# check if wifi driver is bugged
|
||||
if ! check_brcm; then
|
||||
if ! reload_brcm; then
|
||||
echo "Could not reload wifi driver. Reboot"
|
||||
reboot
|
||||
fi
|
||||
sleep 10
|
||||
fi
|
||||
|
||||
# start 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
|
@ -1,148 +0,0 @@
|
||||
#!/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()
|
@ -1,2 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
sudo /usr/bin/tvservice -o
|
@ -1,2 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
sudo /usr/bin/tvservice -p
|
@ -1,3 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
source /usr/bin/pwnlib
|
||||
start_monitor_interface
|
@ -1,3 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
source /usr/bin/pwnlib
|
||||
stop_monitor_interface
|
@ -1,16 +0,0 @@
|
||||
#!/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
|
@ -1,199 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# well ... it blinks the led
|
||||
blink_led() {
|
||||
# shellcheck disable=SC2034
|
||||
for i in $(seq 1 "$1"); do
|
||||
echo 0 >/sys/class/leds/led0/brightness
|
||||
sleep 0.3
|
||||
echo 1 >/sys/class/leds/led0/brightness
|
||||
sleep 0.3
|
||||
done
|
||||
echo 0 >/sys/class/leds/led0/brightness
|
||||
sleep 0.3
|
||||
}
|
||||
|
||||
# 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
|
||||
sleep 3
|
||||
iw dev wlan0 set power_save off
|
||||
iw phy "$(iw phy | head -1 | cut -d" " -f2)" interface add wlan0mon type monitor
|
||||
sleep 2
|
||||
rfkill unblock all
|
||||
ifconfig wlan0 down
|
||||
ifconfig wlan0mon up
|
||||
iw dev wlan0mon set power_save off
|
||||
}
|
||||
|
||||
# stops mon0
|
||||
stop_monitor_interface() {
|
||||
ifconfig wlan0mon down && iw dev wlan0mon del
|
||||
reload_brcm
|
||||
ifconfig wlan0 up
|
||||
}
|
||||
|
||||
# returns 0 if the 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
|
||||
}
|
@ -1,101 +0,0 @@
|
||||
# This is not working quite yet
|
||||
# https://github.com/mkaczanowski/packer-builder-arm/pull/172
|
||||
packer {
|
||||
required_plugins {
|
||||
#arm = {
|
||||
# version = "~> 1"
|
||||
# source = "github.com/cdecoux/builder-arm"
|
||||
#}
|
||||
ansible = {
|
||||
source = "github.com/hashicorp/ansible"
|
||||
version = "~> 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-rpi-bookworm-${var.pwn_version}-arm64.img"
|
||||
qemu_binary_source_path = "/usr/bin/qemu-aarch64-static"
|
||||
qemu_binary_destination_path = "/usr/bin/qemu-aarch64-static"
|
||||
image_build_method = "resize"
|
||||
image_size = "9G"
|
||||
image_type = "dos"
|
||||
image_partitions {
|
||||
name = "boot"
|
||||
type = "c"
|
||||
start_sector = "8192"
|
||||
filesystem = "fat"
|
||||
size = "256M"
|
||||
mountpoint = "/boot/firmware"
|
||||
}
|
||||
image_partitions {
|
||||
name = "root"
|
||||
type = "83"
|
||||
start_sector = "532480"
|
||||
filesystem = "ext4"
|
||||
size = "0"
|
||||
mountpoint = "/"
|
||||
}
|
||||
}
|
||||
|
||||
# a build block invokes sources and runs provisioning steps on them. The
|
||||
# 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/usr/bin/bettercap-launcher",
|
||||
"data/usr/bin/hdmioff",
|
||||
"data/usr/bin/hdmion",
|
||||
"data/usr/bin/monstart",
|
||||
"data/usr/bin/monstop",
|
||||
"data/usr/bin/pwnagotchi-launcher",
|
||||
"data/usr/bin/pwnlib",
|
||||
]
|
||||
}
|
||||
provisioner "shell" {
|
||||
inline = ["chmod +x /usr/bin/*"]
|
||||
}
|
||||
|
||||
provisioner "file" {
|
||||
destination = "/etc/systemd/system/"
|
||||
sources = [
|
||||
"data/etc/systemd/system/bettercap.service",
|
||||
"data/etc/systemd/system/pwnagotchi.service",
|
||||
"data/etc/systemd/system/pwngrid-peer.service",
|
||||
]
|
||||
}
|
||||
|
||||
provisioner "file" {
|
||||
destination = "/etc/update-motd.d/01-motd"
|
||||
source = "data/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 = "raspberrypi64.yml"
|
||||
}
|
||||
}
|
@ -1,619 +0,0 @@
|
||||
---
|
||||
- hosts:
|
||||
- 127.0.0.1
|
||||
gather_facts: true
|
||||
become: true
|
||||
vars:
|
||||
kernel:
|
||||
min: "6.1"
|
||||
full: "6.1.0-rpi7-rpi-v8"
|
||||
pwnagotchi:
|
||||
hostname: "{{ lookup('env', 'PWN_HOSTNAME') | default('pwnagotchi', true) }}"
|
||||
version: "{{ lookup('env', 'PWN_VERSION') | default('pwnagotchi-torch', true) }}"
|
||||
system:
|
||||
boot_options:
|
||||
- "dtoverlay=dwc2"
|
||||
- "dtoverlay=spi1-3cs"
|
||||
- "dtparam=i2c1=on"
|
||||
- "dtparam=i2c_arm=on"
|
||||
- "dtparam=spi=on"
|
||||
- "gpu_mem=16"
|
||||
modules:
|
||||
- "i2c-dev"
|
||||
services:
|
||||
enable:
|
||||
- bettercap.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
|
||||
- avahi-daemon.service
|
||||
- avahi-daemon.socket
|
||||
- bluetooth.service
|
||||
- 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.zip"
|
||||
ui: "https://github.com/bettercap/ui/releases/download/v1.3.0/ui.zip"
|
||||
pwngrid:
|
||||
source: "https://github.com/jayofelony/pwngrid.git"
|
||||
url: "https://github.com/jayofelony/pwngrid/releases/download/v1.10.5/pwngrid-1.10.5-aarch64.zip"
|
||||
apt:
|
||||
downgrade:
|
||||
- libpcap-dev_1.9.1-4_arm64.deb
|
||||
- libpcap0.8-dbg_1.9.1-4_arm64.deb
|
||||
- libpcap0.8-dev_1.9.1-4_arm64.deb
|
||||
- libpcap0.8_1.9.1-4_arm64.deb
|
||||
hold:
|
||||
- libpcap-dev
|
||||
- libpcap0.8
|
||||
- libpcap0.8-dev
|
||||
- libpcap0.8-dbg
|
||||
remove:
|
||||
- avahi-daemon
|
||||
- nfs-common
|
||||
- triggerhappy
|
||||
- wpasupplicant
|
||||
install:
|
||||
- aircrack-ng
|
||||
- autoconf
|
||||
- bc
|
||||
- bison
|
||||
- bluez
|
||||
- build-essential
|
||||
- curl
|
||||
- dkms
|
||||
- dphys-swapfile
|
||||
- fbi
|
||||
- flex
|
||||
- fonts-dejavu
|
||||
- fonts-dejavu-core
|
||||
- fonts-dejavu-extra
|
||||
- fonts-freefont-ttf
|
||||
- g++
|
||||
- gawk
|
||||
- gcc-arm-none-eabi
|
||||
- git
|
||||
- libatlas-base-dev
|
||||
- libavcodec59
|
||||
- libavformat59
|
||||
- libblas-dev
|
||||
- libbluetooth-dev
|
||||
- libbz2-dev
|
||||
- libc-ares-dev
|
||||
- libc6-dev
|
||||
- libcap-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
|
||||
- libraspberrypi-bin
|
||||
- libraspberrypi-dev
|
||||
- libraspberrypi-doc
|
||||
- libraspberrypi0
|
||||
- libsqlite3-dev
|
||||
- libssl-dev
|
||||
- libswscale5
|
||||
- libtiff6
|
||||
- libtool
|
||||
- libusb-1.0-0-dev
|
||||
- lsof
|
||||
- make
|
||||
- python3-yaml
|
||||
- python3-dbus
|
||||
- python3-flask
|
||||
- python3-flask-cors
|
||||
- python3-flaskext.wtf
|
||||
- python3-gast
|
||||
- python3-pil
|
||||
- python3-pycryptodome
|
||||
- python3-requests
|
||||
- python3-scapy
|
||||
- python3-smbus2
|
||||
- python3-spidev
|
||||
- python3-tweepy
|
||||
- python3-werkzeug
|
||||
- firmware-atheros
|
||||
- firmware-brcm80211
|
||||
- firmware-libertas
|
||||
- firmware-misc-nonfree
|
||||
- firmware-realtek
|
||||
- python3-pip
|
||||
- python3-setuptools
|
||||
- python3-smbus
|
||||
- qpdf
|
||||
- raspberrypi-kernel-headers
|
||||
- rsync
|
||||
- screen
|
||||
- tcpdump
|
||||
- texinfo
|
||||
- time
|
||||
- tk-dev
|
||||
- unzip
|
||||
- vim
|
||||
- wget
|
||||
- wl
|
||||
- xxd
|
||||
- zlib1g-dev
|
||||
environment:
|
||||
ARCHFLAGS: "-arch aarch64"
|
||||
QEMU_UNAME: "{{ kernel.full }}"
|
||||
|
||||
tasks:
|
||||
- name: Create pi user
|
||||
copy:
|
||||
dest: /boot/firmware/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: disable sap plugin for bluetooth.service
|
||||
lineinfile:
|
||||
dest: /lib/systemd/system/bluetooth.service
|
||||
regexp: '^ExecStart=/usr/libexec/bluetooth/bluetoothd$'
|
||||
line: 'ExecStart=/usr/libexec/bluetooth/bluetoothd --noplugin=sap,a2dp'
|
||||
state: present
|
||||
|
||||
- name: configure dphys-swapfile
|
||||
lineinfile:
|
||||
path: /etc/dphys-swapfile
|
||||
regexp: "^CONF_SWAPSIZE=.*$"
|
||||
line: "CONF_SWAPSIZE=2048"
|
||||
|
||||
- name: install packages
|
||||
apt:
|
||||
name: "{{ packages.apt.install }}"
|
||||
state: present
|
||||
update_cache: yes
|
||||
install_recommends: false
|
||||
|
||||
###########################################
|
||||
#
|
||||
# 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)
|
||||
- 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 (bcm43455c0)
|
||||
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/
|
||||
|
||||
- name: install new firmware (bcm43455c0)
|
||||
copy:
|
||||
src: /usr/local/src/nexmon/patches/bcm43455c0/7_45_206/nexmon/brcmfmac43455-sdio.bin
|
||||
dest: /usr/lib/firmware/brcm/brcmfmac43455-sdio.bin
|
||||
follow: true
|
||||
|
||||
- name: make firmware patch (bcm43436b0)
|
||||
shell: "source ./setup_env.sh && cd /usr/local/src/nexmon/patches/bcm43436b0/9_88_4_65/nexmon/ && make"
|
||||
args:
|
||||
executable: /bin/bash
|
||||
chdir: /usr/local/src/nexmon/
|
||||
|
||||
- name: install new firmware (bcm43436b0)
|
||||
copy:
|
||||
src: /usr/local/src/nexmon/patches/bcm43436b0/9_88_4_65/nexmon/brcmfmac43436-sdio.bin
|
||||
dest: /usr/lib/firmware/brcm/brcmfmac43436-sdio.bin
|
||||
follow: true
|
||||
|
||||
- name: make firmware patch (bcm43430a1)
|
||||
shell: "source ./setup_env.sh && cd /usr/local/src/nexmon/patches/bcm43430a1/7_45_41_46/nexmon/ && make"
|
||||
args:
|
||||
executable: /bin/bash
|
||||
chdir: /usr/local/src/nexmon/
|
||||
|
||||
- name: install new firmware (bcm43430a1)
|
||||
copy:
|
||||
src: /usr/local/src/nexmon/patches/bcm43430a1/7_45_41_46/nexmon/brcmfmac43430-sdio.bin
|
||||
dest: /usr/lib/firmware/brcm/brcmfmac43430-sdio.bin
|
||||
follow: true
|
||||
|
||||
- name: copy 43430-sdio as 43436s-sdio for the special 43430/1 /2
|
||||
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/brcmfmac43430-sdio.raspberrypi,3-model-b.clm_blob
|
||||
- /usr/lib/firmware/brcm/brcmfmac43430b0-sdio.raspberrypi,model-zero-2-w.clm_blob
|
||||
- /usr/lib/firmware/brcm/brcmfmac43436-sdio.clm_blob
|
||||
- /usr/lib/firmware/brcm/brcmfmac43436-sdio.raspberrypi,model-zero-2-w.clm_blob
|
||||
- /usr/lib/firmware/brcm/brcmfmac43455-sdio.clm_blob
|
||||
|
||||
- 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"
|
||||
|
||||
- name: copy modified driver
|
||||
copy:
|
||||
src: "/usr/local/src/nexmon/patches/driver/brcmfmac_{{ kernel.min }}.y-nexmon/brcmfmac.ko"
|
||||
dest: "/usr/lib/modules/{{ kernel.full }}/kernel/drivers/net/wireless/broadcom/brcm80211/brcmfmac/brcmfmac.ko"
|
||||
|
||||
- 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
|
||||
- name: Delete nexmon content & directory
|
||||
file:
|
||||
state: absent
|
||||
path: /usr/local/src/nexmon/
|
||||
|
||||
- name: Create custom plugin directory
|
||||
file:
|
||||
path: /usr/local/share/pwnagotchi/custom-plugins/
|
||||
state: directory
|
||||
|
||||
- name: Create custom config directory
|
||||
file:
|
||||
path: /etc/pwnagotchi/conf.d/
|
||||
state: directory
|
||||
|
||||
- name: clone pwnagotchi repository
|
||||
git:
|
||||
repo: https://github.com/jayofelony/pwnagotchi-bookworm.git
|
||||
dest: /usr/local/src/pwnagotchi
|
||||
|
||||
- name: build pwnagotchi wheel
|
||||
command: "pip3 install . --no-cache-dir --break-system-packages"
|
||||
args:
|
||||
chdir: /usr/local/src/pwnagotchi
|
||||
|
||||
- name: remove pwnagotchi folder
|
||||
file:
|
||||
state: absent
|
||||
path: /usr/local/src/pwnagotchi
|
||||
|
||||
- name: create /usr/local/share/pwnagotchi/ folder
|
||||
file:
|
||||
path: /usr/local/share/pwnagotchi/
|
||||
state: directory
|
||||
|
||||
- name: Install go-1.21
|
||||
unarchive:
|
||||
src: https://go.dev/dl/go1.21.5.linux-arm64.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
|
||||
git:
|
||||
repo: "{{ packages.pwngrid.source }}"
|
||||
dest: /usr/local/src/pwngrid
|
||||
|
||||
- name: install pwngrid
|
||||
shell: "export GOPATH=$HOME/go && export PATH=/usr/local/go/bin:$PATH:$GOPATH/bin && go mod tidy && make && make install"
|
||||
args:
|
||||
executable: /bin/bash
|
||||
chdir: /usr/local/src/pwngrid
|
||||
|
||||
- name: remove pwngrid folder
|
||||
file:
|
||||
state: absent
|
||||
path: /usr/local/src/pwngrid
|
||||
|
||||
- name: download bettercap
|
||||
git:
|
||||
repo: "{{ packages.bettercap.source }}"
|
||||
dest: /usr/local/src/bettercap
|
||||
|
||||
- name: install bettercap 2.32.2
|
||||
shell: "export GOPATH=$HOME/go && export PATH=/usr/local/go/bin:$PATH:$GOPATH/bin && go mod tidy && make && make install"
|
||||
args:
|
||||
executable: /bin/bash
|
||||
chdir: /usr/local/src/bettercap
|
||||
|
||||
- name: remove bettercap folder
|
||||
file:
|
||||
state: absent
|
||||
path: /usr/local/src/bettercap
|
||||
|
||||
#- name: 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
|
||||
file:
|
||||
state: absent
|
||||
path: /etc/motd
|
||||
|
||||
- name: Delete motd 10-uname
|
||||
file:
|
||||
state: absent
|
||||
path: /etc/update-motd.d/10-uname
|
||||
|
||||
- name: enable ssh on boot
|
||||
file:
|
||||
path: /boot/firmware/ssh
|
||||
state: touch
|
||||
|
||||
- name: adjust /boot/config.txt
|
||||
lineinfile:
|
||||
dest: /boot/firmware/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/firmware/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 /home/pi/logs/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 }}"
|
||||
register: enabled
|
||||
|
||||
- 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
|
||||
register: removed
|
||||
|
||||
- name: clean apt cache
|
||||
apt:
|
||||
autoclean: true
|
||||
when: removed.changed
|
||||
|
||||
- name: remove pre-collected packages zip
|
||||
file:
|
||||
path: /root/go_pkgs.tgz
|
||||
state: absent
|
||||
|
||||
- name: remove /root/go folder
|
||||
file:
|
||||
state: absent
|
||||
path: /root/go
|
||||
|
||||
- name: remove /usr/local/go folder
|
||||
file:
|
||||
state: absent
|
||||
path: /usr/local/go
|
||||
|
||||
- name: remove pip cache
|
||||
file:
|
||||
state: absent
|
||||
path: /root/.cache/pip
|
||||
|
||||
- name: remove dependencies that are no longer required
|
||||
apt:
|
||||
autoremove: yes
|
||||
when: removed.changed
|
||||
|
||||
- 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
|
||||
when: enabled.changed
|
@ -9,7 +9,6 @@ _name = None
|
||||
config = None
|
||||
_cpu_stats = {}
|
||||
|
||||
|
||||
def set_name(new_name):
|
||||
if new_name is None:
|
||||
return
|
||||
|
@ -1 +1 @@
|
||||
__version__ = '2.6.9'
|
||||
__version__ = '2.9.5'
|
||||
|
@ -4,7 +4,9 @@ import os
|
||||
import re
|
||||
import logging
|
||||
import asyncio
|
||||
import _thread
|
||||
#import _thread
|
||||
import threading
|
||||
import subprocess
|
||||
|
||||
import pwnagotchi
|
||||
import pwnagotchi.utils as utils
|
||||
@ -14,24 +16,22 @@ from pwnagotchi.automata import Automata
|
||||
from pwnagotchi.log import LastSession
|
||||
from pwnagotchi.bettercap import Client
|
||||
from pwnagotchi.mesh.utils import AsyncAdvertiser
|
||||
from pwnagotchi.ai.train import AsyncTrainer
|
||||
|
||||
RECOVERY_DATA_FILE = '/root/.pwnagotchi-recovery'
|
||||
|
||||
|
||||
class Agent(Client, Automata, AsyncAdvertiser, AsyncTrainer):
|
||||
class Agent(Client, Automata, AsyncAdvertiser):
|
||||
def __init__(self, view, config, keypair):
|
||||
Client.__init__(self, config['bettercap']['hostname'],
|
||||
config['bettercap']['scheme'],
|
||||
config['bettercap']['port'],
|
||||
config['bettercap']['username'],
|
||||
config['bettercap']['password'])
|
||||
Client.__init__(self,
|
||||
"127.0.0.1" if "hostname" not in config['bettercap'] else config['bettercap']['hostname'],
|
||||
"http" if "scheme" not in config['bettercap'] else config['bettercap']['scheme'],
|
||||
8081 if "port" not in config['bettercap'] else config['bettercap']['port'],
|
||||
"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)
|
||||
AsyncAdvertiser.__init__(self, config, view, keypair)
|
||||
AsyncTrainer.__init__(self, config)
|
||||
|
||||
self._started_at = time.time()
|
||||
self._filter = None if not config['main']['filter'] else re.compile(config['main']['filter'])
|
||||
self._current_channel = 0
|
||||
self._tot_aps = 0
|
||||
self._aps_on_channel = 0
|
||||
@ -129,7 +129,6 @@ class Agent(Client, Automata, AsyncAdvertiser, AsyncTrainer):
|
||||
time.sleep(1)
|
||||
|
||||
def start(self):
|
||||
self.start_ai()
|
||||
self._wait_bettercap()
|
||||
self.setup_events()
|
||||
self.set_starting()
|
||||
@ -164,11 +163,6 @@ class Agent(Client, Automata, AsyncAdvertiser, AsyncTrainer):
|
||||
|
||||
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):
|
||||
self._access_points = aps
|
||||
plugins.on('wifi_update', self, aps)
|
||||
@ -184,13 +178,12 @@ class Agent(Client, Automata, AsyncAdvertiser, AsyncTrainer):
|
||||
for ap in s['wifi']['aps']:
|
||||
if ap['encryption'] == '' or ap['encryption'] == 'OPEN':
|
||||
continue
|
||||
elif ap['hostname'] not in whitelist \
|
||||
and ap['mac'].lower() not in whitelist \
|
||||
and ap['mac'][:8].lower() not in whitelist:
|
||||
if self._filter_included(ap):
|
||||
aps.append(ap)
|
||||
elif ap['hostname'] in whitelist or ap['mac'][:13].lower() in whitelist or ap['mac'].lower() in whitelist:
|
||||
continue
|
||||
else:
|
||||
aps.append(ap)
|
||||
except Exception as e:
|
||||
logging.exception("Error while getting acces points (%s)", e)
|
||||
logging.exception("Error while getting access points (%s)", e)
|
||||
|
||||
aps.sort(key=lambda ap: ap['channel'])
|
||||
return self.set_access_points(aps)
|
||||
@ -260,7 +253,7 @@ class Agent(Client, Automata, AsyncAdvertiser, AsyncTrainer):
|
||||
txt = '%d (%d)' % (len(self._handshakes), tot)
|
||||
|
||||
if self._last_pwnd is not None:
|
||||
txt += ' [%s]' % self._last_pwnd[:11] # So it doesn't overlap with fix_brcmfmac_plugin
|
||||
txt += ' [%s]' % self._last_pwnd
|
||||
|
||||
self._view.set('shakes', txt)
|
||||
|
||||
@ -275,6 +268,10 @@ class Agent(Client, Automata, AsyncAdvertiser, AsyncTrainer):
|
||||
self._save_recovery_data()
|
||||
pwnagotchi.reboot()
|
||||
|
||||
def _restart(self, mode='AUTO'):
|
||||
self._save_recovery_data()
|
||||
pwnagotchi.restart(mode)
|
||||
|
||||
def _save_recovery_data(self):
|
||||
logging.warning("writing recovery data to %s ...", RECOVERY_DATA_FILE)
|
||||
with open(RECOVERY_DATA_FILE, 'w') as fp:
|
||||
@ -306,7 +303,8 @@ class Agent(Client, Automata, AsyncAdvertiser, AsyncTrainer):
|
||||
raise
|
||||
|
||||
def start_session_fetcher(self):
|
||||
_thread.start_new_thread(self._fetch_stats, ())
|
||||
#_thread.start_new_thread(self._fetch_stats, ())
|
||||
threading.Thread(target=self._fetch_stats, args=(), name="Session Fetcher", daemon=True).start()
|
||||
|
||||
def _fetch_stats(self):
|
||||
while True:
|
||||
@ -389,7 +387,8 @@ class Agent(Client, Automata, AsyncAdvertiser, AsyncTrainer):
|
||||
|
||||
def start_event_polling(self):
|
||||
# start a thread and pass in the mainloop
|
||||
_thread.start_new_thread(self._event_poller, (asyncio.get_event_loop(),))
|
||||
#_thread.start_new_thread(self._event_poller, (asyncio.get_event_loop(),))
|
||||
threading.Thread(target=self._event_poller, args=(asyncio.get_event_loop(),), name="Event Polling", daemon=True).start()
|
||||
|
||||
def is_module_running(self, module):
|
||||
s = self.session()
|
||||
@ -427,7 +426,6 @@ class Agent(Client, Automata, AsyncAdvertiser, AsyncTrainer):
|
||||
if self.is_stale():
|
||||
logging.debug("recon is stale, skipping assoc(%s)", ap['mac'])
|
||||
return
|
||||
|
||||
if throttle == -1 and "throttle_a" in self._config['personality']:
|
||||
throttle = self._config['personality']['throttle_a']
|
||||
|
||||
|
@ -1,67 +0,0 @@
|
||||
import os
|
||||
import time
|
||||
import logging
|
||||
|
||||
# https://stackoverflow.com/questions/40426502/is-there-a-way-to-suppress-the-messages-tensorflow-prints/40426709
|
||||
# os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' # or any {'0', '1', '2'}
|
||||
|
||||
|
||||
def load(config, agent, epoch, from_disk=True):
|
||||
config = config['ai']
|
||||
if not config['enabled']:
|
||||
logging.info("ai disabled")
|
||||
return False
|
||||
|
||||
try:
|
||||
begin = time.time()
|
||||
|
||||
logging.info("[AI] bootstrapping dependencies ...")
|
||||
|
||||
start = time.time()
|
||||
SB_BACKEND = "stable_baselines3"
|
||||
|
||||
from stable_baselines3 import A2C
|
||||
logging.debug("[AI] A2C imported in %.2fs" % (time.time() - start))
|
||||
|
||||
start = time.time()
|
||||
from stable_baselines3.a2c import MlpPolicy
|
||||
logging.debug("[AI] MlpPolicy imported in %.2fs" % (time.time() - start))
|
||||
SB_A2C_POLICY = MlpPolicy
|
||||
|
||||
start = time.time()
|
||||
from stable_baselines3.common.vec_env import DummyVecEnv
|
||||
logging.debug("[AI] DummyVecEnv imported in %.2fs" % (time.time() - start))
|
||||
|
||||
start = time.time()
|
||||
import pwnagotchi.ai.gym as wrappers
|
||||
logging.debug("[AI] gym wrapper imported in %.2fs" % (time.time() - start))
|
||||
|
||||
env = wrappers.Environment(agent, epoch)
|
||||
env = DummyVecEnv([lambda: env])
|
||||
|
||||
logging.info("[AI] creating model ...")
|
||||
|
||||
start = time.time()
|
||||
a2c = A2C(SB_A2C_POLICY, env, **config['params'])
|
||||
logging.debug("[AI] A2C created in %.2fs" % (time.time() - start))
|
||||
|
||||
if from_disk and os.path.exists(config['path']):
|
||||
logging.info("[AI] loading %s ..." % config['path'])
|
||||
start = time.time()
|
||||
a2c.load(config['path'], env)
|
||||
logging.debug("[AI] A2C loaded in %.2fs" % (time.time() - start))
|
||||
else:
|
||||
logging.info("[AI] model created:")
|
||||
for key, value in config['params'].items():
|
||||
logging.info(" %s: %s" % (key, value))
|
||||
|
||||
logging.debug("[AI] total loading time is %.2fs" % (time.time() - begin))
|
||||
|
||||
return a2c
|
||||
except Exception as e:
|
||||
logging.exception("[AI] error while starting AI (%s)", e)
|
||||
logging.info("[AI] Deleting brain and restarting.")
|
||||
os.system("rm /root/brain.nn && service pwnagotchi restart")
|
||||
|
||||
logging.warning("[AI] AI not loaded!")
|
||||
return False
|
@ -1,61 +0,0 @@
|
||||
import numpy as np
|
||||
|
||||
import pwnagotchi.mesh.wifi as wifi
|
||||
|
||||
MAX_EPOCH_DURATION = 1024
|
||||
|
||||
|
||||
histogram_size = wifi.NumChannels
|
||||
|
||||
shape = (1,
|
||||
# aps per channel
|
||||
histogram_size +
|
||||
# clients per channel
|
||||
histogram_size +
|
||||
# peers per channel
|
||||
histogram_size +
|
||||
# duration
|
||||
1 +
|
||||
# inactive
|
||||
1 +
|
||||
# active
|
||||
1 +
|
||||
# missed
|
||||
1 +
|
||||
# hops
|
||||
1 +
|
||||
# deauths
|
||||
1 +
|
||||
# assocs
|
||||
1 +
|
||||
# handshakes
|
||||
1)
|
||||
|
||||
|
||||
def featurize(state, step):
|
||||
tot_epochs = step + 1e-10
|
||||
tot_interactions = (state['num_deauths'] + state['num_associations']) + 1e-10
|
||||
return np.concatenate((
|
||||
# aps per channel
|
||||
state['aps_histogram'],
|
||||
# clients per channel
|
||||
state['sta_histogram'],
|
||||
# peers per channel
|
||||
state['peers_histogram'],
|
||||
# duration
|
||||
[np.clip(state['duration_secs'] / MAX_EPOCH_DURATION, 0.0, 1.0)],
|
||||
# inactive
|
||||
[state['inactive_for_epochs'] / tot_epochs],
|
||||
# active
|
||||
[state['active_for_epochs'] / tot_epochs],
|
||||
# missed
|
||||
[state['missed_interactions'] / tot_interactions],
|
||||
# hops
|
||||
[state['num_hops'] / wifi.NumChannels],
|
||||
# deauths
|
||||
[state['num_deauths'] / tot_interactions],
|
||||
# assocs
|
||||
[state['num_associations'] / tot_interactions],
|
||||
# handshakes
|
||||
[state['num_handshakes'] / tot_interactions],
|
||||
))
|
@ -1,148 +0,0 @@
|
||||
import logging
|
||||
import gym
|
||||
from gym import spaces
|
||||
import numpy as np
|
||||
|
||||
import pwnagotchi.ai.featurizer as featurizer
|
||||
import pwnagotchi.ai.reward as reward
|
||||
from pwnagotchi.ai.parameter import Parameter
|
||||
|
||||
|
||||
class Environment(gym.Env):
|
||||
render_mode = "human"
|
||||
metadata = {'render_modes': ['human']}
|
||||
params = [
|
||||
Parameter('min_rssi', min_value=-200, max_value=-50),
|
||||
Parameter('ap_ttl', min_value=30, max_value=600),
|
||||
Parameter('sta_ttl', min_value=60, max_value=300),
|
||||
|
||||
Parameter('recon_time', min_value=5, max_value=60),
|
||||
Parameter('max_inactive_scale', min_value=3, max_value=10),
|
||||
Parameter('recon_inactive_multiplier', min_value=1, max_value=3),
|
||||
Parameter('hop_recon_time', min_value=5, max_value=60),
|
||||
Parameter('min_recon_time', min_value=1, max_value=30),
|
||||
Parameter('max_interactions', min_value=1, max_value=25),
|
||||
Parameter('max_misses_for_recon', min_value=3, max_value=10),
|
||||
Parameter('excited_num_epochs', min_value=5, max_value=30),
|
||||
Parameter('bored_num_epochs', min_value=5, max_value=30),
|
||||
Parameter('sad_num_epochs', min_value=5, max_value=30),
|
||||
]
|
||||
|
||||
def __init__(self, agent, epoch):
|
||||
super(Environment, self).__init__()
|
||||
self._agent = agent
|
||||
self._epoch = epoch
|
||||
self._epoch_num = 0
|
||||
self._last_render = None
|
||||
|
||||
channels = agent.supported_channels()
|
||||
|
||||
Environment.params += [
|
||||
Parameter('_channel_%d' % ch, min_value=0, max_value=1, meta=ch + 1) for ch in
|
||||
range(featurizer.histogram_size) if ch + 1 in channels
|
||||
]
|
||||
|
||||
self.last = {
|
||||
'reward': 0.0,
|
||||
'observation': None,
|
||||
'policy': None,
|
||||
'params': {},
|
||||
'state': None,
|
||||
'state_v': None
|
||||
}
|
||||
|
||||
self.action_space = spaces.MultiDiscrete([p.space_size() for p in Environment.params if p.trainable])
|
||||
self.observation_space = spaces.Box(low=0, high=1, shape=featurizer.shape, dtype=np.float32)
|
||||
self.reward_range = reward.range
|
||||
|
||||
@staticmethod
|
||||
def policy_size():
|
||||
return len(list(p for p in Environment.params if p.trainable))
|
||||
|
||||
@staticmethod
|
||||
def policy_to_params(policy):
|
||||
num = len(policy)
|
||||
params = {}
|
||||
|
||||
assert len(Environment.params) == num
|
||||
|
||||
channels = []
|
||||
|
||||
for i in range(num):
|
||||
param = Environment.params[i]
|
||||
|
||||
if '_channel' not in param.name:
|
||||
params[param.name] = param.to_param_value(policy[i])
|
||||
else:
|
||||
has_chan = param.to_param_value(policy[i])
|
||||
# print("%s policy:%s bool:%s" % (param.name, policy[i], has_chan))
|
||||
chan = param.meta
|
||||
if has_chan:
|
||||
channels.append(chan)
|
||||
|
||||
params['channels'] = channels
|
||||
|
||||
return params
|
||||
|
||||
def _next_epoch(self):
|
||||
logging.debug("[ai] waiting for epoch to finish ...")
|
||||
return self._epoch.wait_for_epoch_data()
|
||||
|
||||
def _apply_policy(self, policy):
|
||||
new_params = Environment.policy_to_params(policy)
|
||||
self.last['policy'] = policy
|
||||
self.last['params'] = new_params
|
||||
self._agent.on_ai_policy(new_params)
|
||||
|
||||
def step(self, policy):
|
||||
# create the parameters from the policy and update
|
||||
# them in the algorithm
|
||||
self._apply_policy(policy)
|
||||
self._epoch_num += 1
|
||||
|
||||
# wait for the algorithm to run with the new parameters
|
||||
state = self._next_epoch()
|
||||
|
||||
self.last['reward'] = state['reward']
|
||||
self.last['state'] = state
|
||||
self.last['state_v'] = featurizer.featurize(state, self._epoch_num)
|
||||
|
||||
self._agent.on_ai_step()
|
||||
|
||||
return self.last['state_v'], self.last['reward'], not self._agent.is_training(), {}
|
||||
|
||||
def reset(self):
|
||||
# logging.info("[ai] resetting environment ...")
|
||||
self._epoch_num = 0
|
||||
state = self._next_epoch()
|
||||
self.last['state'] = state
|
||||
self.last['state_v'] = featurizer.featurize(state, 1)
|
||||
return self.last['state_v']
|
||||
|
||||
def _render_histogram(self, hist):
|
||||
for ch in range(featurizer.histogram_size):
|
||||
if hist[ch]:
|
||||
logging.info(" CH %d: %s" % (ch + 1, hist[ch]))
|
||||
|
||||
def render(self, mode='human', close=False, force=False):
|
||||
# when using a vectorialized environment, render gets called twice
|
||||
# avoid rendering the same data
|
||||
if self._last_render == self._epoch_num:
|
||||
return
|
||||
|
||||
if not self._agent.is_training() and not force:
|
||||
return
|
||||
|
||||
self._last_render = self._epoch_num
|
||||
|
||||
logging.info("[AI] --- training epoch %d/%d ---" % (self._epoch_num, self._agent.training_epochs()))
|
||||
logging.info("[AI] REWARD: %f" % self.last['reward'])
|
||||
|
||||
logging.debug(
|
||||
"[AI] policy: %s" % ', '.join("%s:%s" % (name, value) for name, value in self.last['params'].items()))
|
||||
|
||||
logging.info("[AI] observation:")
|
||||
for name, value in self.last['state'].items():
|
||||
if 'histogram' in name:
|
||||
logging.info(" %s" % name.replace('_histogram', ''))
|
||||
self._render_histogram(value)
|
@ -1,30 +0,0 @@
|
||||
from gym import spaces
|
||||
|
||||
|
||||
class Parameter(object):
|
||||
def __init__(self, name, value=0.0, min_value=0, max_value=2, meta=None, trainable=True):
|
||||
self.name = name
|
||||
self.trainable = trainable
|
||||
self.meta = meta
|
||||
self.value = value
|
||||
self.min_value = min_value
|
||||
self.max_value = max_value + 1
|
||||
|
||||
# gymnasium.space.Discrete is within [0, 1, 2, ..., n-1]
|
||||
if self.min_value < 0:
|
||||
self.scale_factor = abs(self.min_value)
|
||||
elif self.min_value > 0:
|
||||
self.scale_factor = -self.min_value
|
||||
else:
|
||||
self.scale_factor = 0
|
||||
|
||||
def space_size(self):
|
||||
return self.max_value + self.scale_factor
|
||||
|
||||
def space(self):
|
||||
return spaces.Discrete(self.max_value + self.scale_factor)
|
||||
|
||||
def to_param_value(self, policy_v):
|
||||
self.value = policy_v - self.scale_factor
|
||||
assert self.min_value <= self.value <= self.max_value
|
||||
return int(self.value)
|
@ -1,27 +1,28 @@
|
||||
import pwnagotchi.mesh.wifi as wifi
|
||||
|
||||
range = (-.7, 1.02)
|
||||
fuck_zero = 1e-20
|
||||
range: tuple[float, float] = (-.7, 1.02)
|
||||
fuck_zero: float = 1e-20
|
||||
|
||||
|
||||
class RewardFunction(object):
|
||||
def __call__(self, epoch_n, state):
|
||||
tot_epochs = epoch_n + fuck_zero
|
||||
tot_interactions = max(state['num_deauths'] + state['num_associations'], state['num_handshakes']) + fuck_zero
|
||||
tot_channels = wifi.NumChannels
|
||||
def __call__(self, epoch_n: float, state: dict[str, float]) -> float:
|
||||
|
||||
h = state['num_handshakes'] / tot_interactions
|
||||
a = .2 * (state['active_for_epochs'] / tot_epochs)
|
||||
c = .1 * (state['num_hops'] / tot_channels)
|
||||
tot_epochs: float = epoch_n + fuck_zero
|
||||
tot_interactions: float = max(state['num_deauths'] + state['num_associations'], state['num_handshakes']) + fuck_zero
|
||||
tot_channels: int = wifi.NumChannels
|
||||
|
||||
b = -.3 * (state['blind_for_epochs'] / tot_epochs)
|
||||
m = -.3 * (state['missed_interactions'] / tot_interactions)
|
||||
i = -.2 * (state['inactive_for_epochs'] / tot_epochs)
|
||||
h: float = state['num_handshakes'] / tot_interactions
|
||||
a: float = .2 * (state['active_for_epochs'] / tot_epochs)
|
||||
c: float = .1 * (state['num_hops'] / tot_channels)
|
||||
|
||||
b: float = -.3 * (state['blind_for_epochs'] / tot_epochs)
|
||||
m: float = -.3 * (state['missed_interactions'] / tot_interactions)
|
||||
i: float = -.2 * (state['inactive_for_epochs'] / tot_epochs)
|
||||
|
||||
# include emotions if state >= 5 epochs
|
||||
_sad = state['sad_for_epochs'] if state['sad_for_epochs'] >= 5 else 0
|
||||
_bored = state['bored_for_epochs'] if state['bored_for_epochs'] >= 5 else 0
|
||||
s = -.2 * (_sad / tot_epochs)
|
||||
l = -.1 * (_bored / tot_epochs)
|
||||
_sad: float = state['sad_for_epochs'] if state['sad_for_epochs'] >= 5 else 0
|
||||
_bored: float = state['bored_for_epochs'] if state['bored_for_epochs'] >= 5 else 0
|
||||
s: float = -.2 * (_sad / tot_epochs)
|
||||
l: float = -.1 * (_bored / tot_epochs)
|
||||
|
||||
return h + a + c + b + i + m + s + l
|
||||
|
@ -1,197 +0,0 @@
|
||||
import _thread
|
||||
import threading
|
||||
import time
|
||||
import random
|
||||
import os
|
||||
import json
|
||||
import logging
|
||||
|
||||
import pwnagotchi.plugins as plugins
|
||||
import pwnagotchi.ai as ai
|
||||
|
||||
|
||||
class Stats(object):
|
||||
def __init__(self, path, events_receiver):
|
||||
self._lock = threading.Lock()
|
||||
self._receiver = events_receiver
|
||||
|
||||
self.path = path
|
||||
self.born_at = time.time()
|
||||
# total epochs lived (trained + just eval)
|
||||
self.epochs_lived = 0
|
||||
# total training epochs
|
||||
self.epochs_trained = 0
|
||||
|
||||
self.worst_reward = 0.0
|
||||
self.best_reward = 0.0
|
||||
|
||||
self.load()
|
||||
|
||||
def on_epoch(self, data, training):
|
||||
best_r = False
|
||||
worst_r = False
|
||||
with self._lock:
|
||||
reward = data['reward']
|
||||
if reward < self.worst_reward:
|
||||
self.worst_reward = reward
|
||||
worst_r = True
|
||||
|
||||
elif reward > self.best_reward:
|
||||
best_r = True
|
||||
self.best_reward = reward
|
||||
|
||||
self.epochs_lived += 1
|
||||
if training:
|
||||
self.epochs_trained += 1
|
||||
|
||||
self.save()
|
||||
|
||||
if best_r:
|
||||
self._receiver.on_ai_best_reward(reward)
|
||||
elif worst_r:
|
||||
self._receiver.on_ai_worst_reward(reward)
|
||||
|
||||
def load(self):
|
||||
with self._lock:
|
||||
if os.path.exists(self.path) and os.path.getsize(self.path) > 0:
|
||||
logging.info("[AI] loading %s" % self.path)
|
||||
with open(self.path, 'rt') as fp:
|
||||
obj = json.load(fp)
|
||||
|
||||
self.born_at = obj['born_at']
|
||||
self.epochs_lived, self.epochs_trained = obj['epochs_lived'], obj['epochs_trained']
|
||||
self.best_reward, self.worst_reward = obj['rewards']['best'], obj['rewards']['worst']
|
||||
|
||||
def save(self):
|
||||
with self._lock:
|
||||
logging.info("[AI] saving %s" % self.path)
|
||||
|
||||
data = json.dumps({
|
||||
'born_at': self.born_at,
|
||||
'epochs_lived': self.epochs_lived,
|
||||
'epochs_trained': self.epochs_trained,
|
||||
'rewards': {
|
||||
'best': self.best_reward,
|
||||
'worst': self.worst_reward
|
||||
}
|
||||
})
|
||||
|
||||
temp = "%s.tmp" % self.path
|
||||
back = "%s.bak" % self.path
|
||||
with open(temp, 'wt') as fp:
|
||||
fp.write(data)
|
||||
|
||||
if os.path.isfile(self.path):
|
||||
os.replace(self.path, back)
|
||||
os.replace(temp, self.path)
|
||||
|
||||
|
||||
class AsyncTrainer(object):
|
||||
def __init__(self, config):
|
||||
self._config = config
|
||||
self._model = None
|
||||
self._is_training = False
|
||||
self._training_epochs = 0
|
||||
self._nn_path = self._config['ai']['path']
|
||||
self._stats = Stats("%s.json" % os.path.splitext(self._nn_path)[0], self)
|
||||
|
||||
def set_training(self, training, for_epochs=0):
|
||||
self._is_training = training
|
||||
self._training_epochs = for_epochs
|
||||
|
||||
if training:
|
||||
plugins.on('ai_training_start', self, for_epochs)
|
||||
else:
|
||||
plugins.on('ai_training_end', self)
|
||||
|
||||
def is_training(self):
|
||||
return self._is_training
|
||||
|
||||
def training_epochs(self):
|
||||
return self._training_epochs
|
||||
|
||||
def start_ai(self):
|
||||
_thread.start_new_thread(self._ai_worker, ())
|
||||
|
||||
def _save_ai(self):
|
||||
logging.info("[AI] saving model to %s ..." % self._nn_path)
|
||||
temp = "%s.tmp" % self._nn_path
|
||||
self._model.save(temp)
|
||||
os.replace(temp, self._nn_path)
|
||||
|
||||
def on_ai_step(self):
|
||||
self._model.env.render()
|
||||
|
||||
if self._is_training:
|
||||
self._save_ai()
|
||||
|
||||
self._stats.on_epoch(self._epoch.data(), self._is_training)
|
||||
|
||||
def on_ai_training_step(self, _locals, _globals):
|
||||
self._model.env.render()
|
||||
plugins.on('ai_training_step', self, _locals, _globals)
|
||||
|
||||
def on_ai_policy(self, new_params):
|
||||
plugins.on('ai_policy', self, new_params)
|
||||
logging.info("[AI] setting new policy:")
|
||||
for name, value in new_params.items():
|
||||
if name in self._config['personality']:
|
||||
curr_value = self._config['personality'][name]
|
||||
if curr_value != value:
|
||||
logging.info("[AI] ! %s: %s -> %s" % (name, curr_value, value))
|
||||
self._config['personality'][name] = value
|
||||
else:
|
||||
logging.error("[AI] param %s not in personality configuration!" % name)
|
||||
|
||||
self.run('set wifi.ap.ttl %d' % self._config['personality']['ap_ttl'])
|
||||
self.run('set wifi.sta.ttl %d' % self._config['personality']['sta_ttl'])
|
||||
self.run('set wifi.rssi.min %d' % self._config['personality']['min_rssi'])
|
||||
|
||||
def on_ai_ready(self):
|
||||
self._view.on_ai_ready()
|
||||
plugins.on('ai_ready', self)
|
||||
|
||||
def on_ai_best_reward(self, r):
|
||||
logging.info("[AI] best reward so far: %s" % r)
|
||||
self._view.on_motivated(r)
|
||||
plugins.on('ai_best_reward', self, r)
|
||||
|
||||
def on_ai_worst_reward(self, r):
|
||||
logging.info("[AI] worst reward so far: %s" % r)
|
||||
self._view.on_demotivated(r)
|
||||
plugins.on('ai_worst_reward', self, r)
|
||||
|
||||
def _ai_worker(self):
|
||||
self._model = ai.load(self._config, self, self._epoch)
|
||||
|
||||
if self._model:
|
||||
self.on_ai_ready()
|
||||
|
||||
epochs_per_episode = self._config['ai']['epochs_per_episode']
|
||||
|
||||
obs = None
|
||||
while True:
|
||||
self._model.env.render()
|
||||
# enter in training mode?
|
||||
if random.random() > self._config['ai']['laziness']:
|
||||
logging.info("[AI] learning for %d epochs ..." % epochs_per_episode)
|
||||
try:
|
||||
self.set_training(True, epochs_per_episode)
|
||||
# back up brain file before starting new training set
|
||||
if os.path.isfile(self._nn_path):
|
||||
back = "%s.bak" % self._nn_path
|
||||
os.replace(self._nn_path, back)
|
||||
self._view.set("mode", " AI")
|
||||
self._model.learn(total_timesteps=epochs_per_episode, callback=self.on_ai_training_step)
|
||||
except Exception as e:
|
||||
logging.exception("[AI] error while training (%s)", e)
|
||||
finally:
|
||||
self.set_training(False)
|
||||
obs = self._model.env.reset()
|
||||
# init the first time
|
||||
elif obs is None:
|
||||
obs = self._model.env.reset()
|
||||
|
||||
# run the inference
|
||||
action, _ = self._model.predict(obs)
|
||||
obs, _, _, _ = self._model.env.step(action)
|
@ -1,16 +0,0 @@
|
||||
import numpy as np
|
||||
|
||||
|
||||
def normalize(v, min_v, max_v):
|
||||
return (v - min_v) / (max_v - min_v)
|
||||
|
||||
|
||||
def as_batches(x, y, batch_size, shuffle=True):
|
||||
x_size = len(x)
|
||||
assert x_size == len(y)
|
||||
|
||||
indices = np.random.permutation(x_size) if shuffle else None
|
||||
|
||||
for offset in range(0, x_size - batch_size + 1, batch_size):
|
||||
excerpt = indices[offset:offset + batch_size] if shuffle else slice(offset, offset + batch_size)
|
||||
yield x[excerpt], y[excerpt]
|
@ -2,6 +2,7 @@ import logging
|
||||
|
||||
import pwnagotchi.plugins as plugins
|
||||
from pwnagotchi.ai.epoch import Epoch
|
||||
import os
|
||||
|
||||
|
||||
# basic mood system
|
||||
@ -136,8 +137,7 @@ class Automata(object):
|
||||
self.set_grateful()
|
||||
|
||||
plugins.on('epoch', self, self._epoch.epoch - 1, self._epoch.data())
|
||||
|
||||
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)
|
||||
self._reboot()
|
||||
logging.critical("%d epochs without visible access points -> restarting ...", self._epoch.blind_for)
|
||||
self._restart()
|
||||
self._epoch.blind_for = 0
|
||||
|
@ -100,7 +100,6 @@ class Client(object):
|
||||
await asyncio.sleep(sleep_time)
|
||||
continue
|
||||
except OSError:
|
||||
sleep_time = min_sleep + max_sleep * random.random()
|
||||
logging.warning('connection to the bettercap endpoint failed...')
|
||||
pwnagotchi.restart("AUTO")
|
||||
|
||||
|
343
pwnagotchi/cli.py
Normal file
343
pwnagotchi/cli.py
Normal file
@ -0,0 +1,343 @@
|
||||
import logging
|
||||
import argparse
|
||||
import time
|
||||
import signal
|
||||
import sys
|
||||
import toml
|
||||
import requests
|
||||
import os
|
||||
import re
|
||||
|
||||
import pwnagotchi
|
||||
from pwnagotchi import utils
|
||||
from pwnagotchi.google import cmd as google_cmd
|
||||
from pwnagotchi.plugins import cmd as plugins_cmd
|
||||
from pwnagotchi import log
|
||||
from pwnagotchi import fs
|
||||
from pwnagotchi.utils import DottedTomlEncoder, parse_version as version_to_tuple
|
||||
|
||||
|
||||
def pwnagotchi_cli():
|
||||
def do_clear(display):
|
||||
logging.info("clearing the display ...")
|
||||
display.clear()
|
||||
sys.exit(0)
|
||||
|
||||
def do_manual_mode(agent):
|
||||
logging.info("entering manual mode ...")
|
||||
|
||||
agent.mode = 'manual'
|
||||
agent.last_session.parse(agent.view(), args.skip_session)
|
||||
if not args.skip_session:
|
||||
logging.info(
|
||||
"the last session lasted %s (%d completed epochs, trained for %d), average reward:%s (min:%s max:%s)" % (
|
||||
agent.last_session.duration_human,
|
||||
agent.last_session.epochs,
|
||||
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.last_session.parse(agent.view(), args.skip_session) # show stats in 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:
|
||||
time.sleep(1)
|
||||
agent.set_channel(ch)
|
||||
|
||||
if not agent.is_stale() and agent.any_activity():
|
||||
logging.info("%d access points on channel %d" % (len(aps), ch))
|
||||
|
||||
# for each ap on this channel
|
||||
for ap in aps:
|
||||
# send an association frame in order to get for a PMKID
|
||||
agent.associate(ap)
|
||||
# deauth all client stations in order to get a full handshake
|
||||
for sta in ap['clients']:
|
||||
agent.deauth(ap, sta)
|
||||
time.sleep(1) # delay to not trigger nexmon firmware bugs
|
||||
|
||||
# An interesting effect of this:
|
||||
#
|
||||
# From Pwnagotchi's perspective, the more new access points
|
||||
# and / or client stations nearby, the longer one epoch of
|
||||
# its relative time will take ... basically, in Pwnagotchi's universe,
|
||||
# Wi-Fi electromagnetic fields affect time like gravitational fields
|
||||
# affect ours ... neat ^_^
|
||||
agent.next_epoch()
|
||||
|
||||
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)
|
||||
|
||||
def add_parsers(parser):
|
||||
"""
|
||||
Adds the plugins and google subcommands
|
||||
"""
|
||||
subparsers = parser.add_subparsers()
|
||||
|
||||
# Add parsers from plugins_cmd
|
||||
plugins_cmd.add_parsers(subparsers)
|
||||
|
||||
# Add parsers from google_cmd
|
||||
google_cmd.add_parsers(subparsers)
|
||||
|
||||
parser = argparse.ArgumentParser(prog="pwnagotchi")
|
||||
# pwnagotchi --help
|
||||
parser.add_argument('-C', '--config', action='store', dest='config', default='/etc/pwnagotchi/default.toml',
|
||||
help='Main configuration file.')
|
||||
parser.add_argument('-U', '--user-config', action='store', dest='user_config', default='/etc/pwnagotchi/config.toml',
|
||||
help='If this file exists, configuration will be merged and this will override default values.')
|
||||
|
||||
parser.add_argument('--manual', dest="do_manual", action="store_true", default=False, help="Manual mode.")
|
||||
parser.add_argument('--skip-session', dest="skip_session", action="store_true", default=False,
|
||||
help="Skip last session parsing in manual mode.")
|
||||
|
||||
parser.add_argument('--clear', dest="do_clear", action="store_true", default=False,
|
||||
help="Clear the ePaper display and exit.")
|
||||
|
||||
parser.add_argument('--debug', dest="debug", action="store_true", default=False,
|
||||
help="Enable debug logs.")
|
||||
|
||||
parser.add_argument('--version', dest="version", action="store_true", default=False,
|
||||
help="Print the version.")
|
||||
|
||||
parser.add_argument('--print-config', dest="print_config", action="store_true", default=False,
|
||||
help="Print the configuration.")
|
||||
|
||||
# Jayofelony added these
|
||||
parser.add_argument('--wizard', dest="wizard", action="store_true", default=False,
|
||||
help="Interactive installation of your personal configuration.")
|
||||
parser.add_argument('--check-update', dest="check_update", action="store_true", default=False,
|
||||
help="Check for updates on Pwnagotchi. And tells current version.")
|
||||
parser.add_argument('--donate', dest="donate", action="store_true", default=False,
|
||||
help="How to donate to this project.")
|
||||
|
||||
# pwnagotchi plugins --help
|
||||
add_parsers(parser)
|
||||
args = parser.parse_args()
|
||||
|
||||
if plugins_cmd.used_plugin_cmd(args):
|
||||
config = utils.load_config(args)
|
||||
log.setup_logging(args, config)
|
||||
rc = plugins_cmd.handle_cmd(args, config)
|
||||
sys.exit(rc)
|
||||
if google_cmd.used_google_cmd(args):
|
||||
config = utils.load_config(args)
|
||||
log.setup_logging(args, config)
|
||||
rc = google_cmd.handle_cmd(args)
|
||||
sys.exit(rc)
|
||||
|
||||
if args.version:
|
||||
print(pwnagotchi.__version__)
|
||||
sys.exit(0)
|
||||
|
||||
if args.wizard:
|
||||
def is_valid_hostname(hostname):
|
||||
if len(hostname) > 255:
|
||||
return False
|
||||
if hostname[-1] == ".":
|
||||
hostname = hostname[:-1] # strip exactly one dot from the right, if present
|
||||
allowed = re.compile("(?!-)[A-Z\d-]{1,63}(?<!-)$", re.IGNORECASE)
|
||||
return all(allowed.match(x) for x in hostname.split("."))
|
||||
|
||||
pwn_restore = input("Do you want to restore the previous configuration?\n\n"
|
||||
"[Y/N]: ")
|
||||
if pwn_restore in ('y', 'yes'):
|
||||
os.system("cp -f /etc/pwnagotchi/config.toml.bak /etc/pwnagotchi/config.toml")
|
||||
print("Your previous configuration is restored, and I will restart in 5 seconds.")
|
||||
time.sleep(5)
|
||||
os.system("service pwnagotchi restart")
|
||||
else:
|
||||
pwn_check = input("This will create a new configuration file and overwrite your current backup, are you sure?\n\n"
|
||||
"[Y/N]: ")
|
||||
if pwn_check.lower() in ('y', 'yes'):
|
||||
os.system("mv -f /etc/pwnagotchi/config.toml /etc/pwnagotchi/config.toml.bak")
|
||||
with open("/etc/pwnagotchi/config.toml", "a+") as f:
|
||||
f.write("# Do not edit this file if you do not know what you are doing!!!\n\n")
|
||||
# Set pwnagotchi name
|
||||
print("Welcome to the interactive installation of your personal Pwnagotchi configuration!\n"
|
||||
"My name is Jayofelony, how may I call you?\n\n")
|
||||
pwn_name = input("Pwnagotchi name (no spaces): ")
|
||||
if pwn_name == "":
|
||||
pwn_name = "Pwnagotchi"
|
||||
print("I shall go by Pwnagotchi from now on!")
|
||||
pwn_name = f"main.name = \"{pwn_name}\"\n"
|
||||
f.write(pwn_name)
|
||||
else:
|
||||
if is_valid_hostname(pwn_name):
|
||||
print(f"I shall go by {pwn_name} from now on!")
|
||||
pwn_name = f"main.name = \"{pwn_name}\"\n"
|
||||
f.write(pwn_name)
|
||||
else:
|
||||
print("You have chosen an invalid name. Please start over.")
|
||||
exit()
|
||||
pwn_whitelist = input("How many networks do you want to whitelist? "
|
||||
"We will also ask a MAC for each network?\n"
|
||||
"Each SSID and BSSID count as 1 network. \n\n"
|
||||
"Be sure to use digits as your answer.\n\n"
|
||||
"Amount of networks: ")
|
||||
if int(pwn_whitelist) > 0:
|
||||
f.write("main.whitelist = [\n")
|
||||
for x in range(int(pwn_whitelist)):
|
||||
ssid = input("SSID (Name): ")
|
||||
bssid = input("BSSID (MAC): ")
|
||||
f.write(f"\t\"{ssid}\",\n")
|
||||
if bssid != "":
|
||||
f.write(f"\t\"{bssid}\",\n")
|
||||
f.write("]\n")
|
||||
# set bluetooth tether
|
||||
pwn_bluetooth = input("Do you want to enable BT-Tether?\n\n"
|
||||
"[Y/N] ")
|
||||
if pwn_bluetooth.lower() in ('y', 'yes'):
|
||||
f.write("main.plugins.bt-tether.enabled = true\n\n")
|
||||
pwn_bluetooth_phone_name = input("What name uses your phone, check settings?\n\n")
|
||||
if pwn_bluetooth_phone_name != "":
|
||||
f.write(f"main.plugins.bt-tether.phone-name = \"{pwn_bluetooth_phone_name}\"\n")
|
||||
pwn_bluetooth_device = input("What device do you use? android or ios?\n\n"
|
||||
"Device: ")
|
||||
if pwn_bluetooth_device != "":
|
||||
if pwn_bluetooth_device != "android" and pwn_bluetooth_device != "ios":
|
||||
print("You have chosen an invalid device. Please start over.")
|
||||
exit()
|
||||
f.write(f"main.plugins.bt-tether.phone = \"{pwn_bluetooth_device.lower()}\"\n")
|
||||
if pwn_bluetooth_device == "android":
|
||||
f.write("main.plugins.bt-tether.ip = \"192.168.44.44\"\n")
|
||||
elif pwn_bluetooth_device == "ios":
|
||||
f.write("main.plugins.bt-tether.ip = \"172.20.10.6\"\n")
|
||||
pwn_bluetooth_mac = input("What is the bluetooth MAC of your device?\n\n"
|
||||
"MAC: ")
|
||||
if pwn_bluetooth_mac != "":
|
||||
f.write(f"main.plugins.bt-tether.mac = \"{pwn_bluetooth_mac}\"\n")
|
||||
# set up display settings
|
||||
pwn_display_enabled = input("Do you want to enable a display?\n\n"
|
||||
"[Y/N]: ")
|
||||
if pwn_display_enabled.lower() in ('y', 'yes'):
|
||||
f.write("ui.display.enabled = true\n")
|
||||
pwn_display_type = input("What display do you use?\n\n"
|
||||
"Be sure to check for the correct display type @ \n"
|
||||
"https://github.com/jayofelony/pwnagotchi/blob/master/pwnagotchi/utils.py#L240-L501\n\n"
|
||||
"Display type: ")
|
||||
if pwn_display_type != "":
|
||||
f.write(f"ui.display.type = \"{pwn_display_type}\"\n")
|
||||
pwn_display_invert = input("Do you want to invert the display colors?\n"
|
||||
"N = Black background\n"
|
||||
"Y = White background\n\n"
|
||||
"[Y/N]: ")
|
||||
if pwn_display_invert.lower() in ('y', 'yes'):
|
||||
f.write("ui.invert = true\n")
|
||||
f.close()
|
||||
if pwn_bluetooth.lower() in ('y', 'yes'):
|
||||
if pwn_bluetooth_device.lower == "android":
|
||||
print("To visit the webui when connected with your phone, visit: http://192.168.44.44:8080\n"
|
||||
"Be sure to run `sudo bluetoothctl` to set-up the bluetooth connection for the first time. And read the wiki step 4.\n"
|
||||
"Your configuration is done, and I will restart in 5 seconds.")
|
||||
|
||||
elif pwn_bluetooth_device.lower == "ios":
|
||||
print("To visit the webui when connected with your phone, visit: http://172.20.10.6:8080\n"
|
||||
"Your configuration is done, and I will restart in 5 seconds.")
|
||||
else:
|
||||
print("Your configuration is done, and I will restart in 5 seconds.")
|
||||
time.sleep(5)
|
||||
os.system("service pwnagotchi restart")
|
||||
else:
|
||||
print("Ok, doing nothing.")
|
||||
sys.exit(0)
|
||||
|
||||
if args.donate:
|
||||
print("Donations can be made @ \n "
|
||||
"https://github.com/sponsors/jayofelony \n\n"
|
||||
"But only if you really want to!")
|
||||
sys.exit(0)
|
||||
|
||||
if args.check_update:
|
||||
resp = requests.get("https://api.github.com/repos/jayofelony/pwnagotchi/releases/latest")
|
||||
latest = resp.json()
|
||||
latest_ver = latest['tag_name'].replace('v', '')
|
||||
|
||||
local = version_to_tuple(pwnagotchi.__version__)
|
||||
remote = version_to_tuple(latest_ver)
|
||||
if remote > local:
|
||||
user_input = input("There is a new version available! Update from v%s to v%s?\n[Y/N] " % (pwnagotchi.__version__, latest_ver))
|
||||
# input validation
|
||||
if user_input.lower() in ('y', 'yes'):
|
||||
if os.path.exists('/root/.auto-update'):
|
||||
os.system("rm /root/.auto-update && 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.")
|
||||
elif user_input.lower() in ('n', 'no'): # using this elif for readability
|
||||
print("Okay, guess not!")
|
||||
else:
|
||||
print("You are currently on the latest release, v%s." % pwnagotchi.__version__)
|
||||
sys.exit(0)
|
||||
|
||||
config = utils.load_config(args)
|
||||
|
||||
if args.print_config:
|
||||
print(toml.dumps(config, encoder=DottedTomlEncoder()))
|
||||
sys.exit(0)
|
||||
|
||||
from pwnagotchi.identity import KeyPair
|
||||
from pwnagotchi.agent import Agent
|
||||
from pwnagotchi.ui import fonts
|
||||
from pwnagotchi.ui.display import Display
|
||||
from pwnagotchi import grid
|
||||
from pwnagotchi import plugins
|
||||
|
||||
pwnagotchi.config = config
|
||||
fs.setup_mounts(config)
|
||||
log.setup_logging(args, config)
|
||||
fonts.init(config)
|
||||
|
||||
pwnagotchi.set_name(config['main']['name'])
|
||||
|
||||
plugins.load(config)
|
||||
|
||||
display = Display(config=config, state={'name': '%s>' % pwnagotchi.name()})
|
||||
|
||||
if args.do_clear:
|
||||
do_clear(display)
|
||||
sys.exit(0)
|
||||
|
||||
agent = Agent(view=display, config=config, keypair=KeyPair(view=display))
|
||||
|
||||
def usr1_handler(*unused):
|
||||
logging.info('Received USR1 signal. Restart process ...')
|
||||
agent._restart("MANU" if args.do_manual else "AUTO")
|
||||
|
||||
signal.signal(signal.SIGUSR1, usr1_handler)
|
||||
|
||||
if args.do_manual:
|
||||
do_manual_mode(agent)
|
||||
else:
|
||||
do_auto_mode(agent)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pwnagotchi_cli()
|
@ -1,44 +1,33 @@
|
||||
main.name = "pwnagotchi"
|
||||
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.custom_plugin_repos = [
|
||||
"https://github.com/jayofelony/pwnagotchi-torch-plugins/archive/master.zip",
|
||||
"https://github.com/tisboyo/pwnagotchi-pisugar2-plugin/archive/master.zip",
|
||||
"https://github.com/nullm0ose/pwnagotchi-plugin-pisugar3/archive/master.zip",
|
||||
"https://github.com/Sniffleupagus/pwnagotchi_plugins/archive/master.zip",
|
||||
"https://github.com/NeonLightning/pwny/archive/master.zip",
|
||||
"https://github.com/marbasec/UPSLite_Plugin_1_3/archive/master.zip"
|
||||
"https://github.com/marbasec/UPSLite_Plugin_1_3/archive/master.zip",
|
||||
"https://github.com/wpa-2/Pwnagotchi-Plugins/archive/master.zip"
|
||||
]
|
||||
|
||||
main.custom_plugins = "/usr/local/share/pwnagotchi/custom-plugins/"
|
||||
|
||||
main.plugins.auto-update.enabled = true
|
||||
main.plugins.auto-update.install = true
|
||||
main.plugins.auto-tune.enabled = true
|
||||
|
||||
main.plugins.auto-update.enabled = false
|
||||
main.plugins.auto-update.install = false
|
||||
main.plugins.auto-update.interval = 1
|
||||
|
||||
main.plugins.bt-tether.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.mac = ""
|
||||
main.plugins.bt-tether.devices.android-phone.ip = "192.168.44.44"
|
||||
main.plugins.bt-tether.devices.android-phone.netmask = 24
|
||||
main.plugins.bt-tether.devices.android-phone.interval = 1
|
||||
main.plugins.bt-tether.devices.android-phone.scantime = 10
|
||||
main.plugins.bt-tether.devices.android-phone.max_tries = 10
|
||||
main.plugins.bt-tether.devices.android-phone.share_internet = false
|
||||
main.plugins.bt-tether.devices.android-phone.priority = 1
|
||||
|
||||
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.mac = ""
|
||||
main.plugins.bt-tether.devices.ios-phone.ip = "172.20.10.6"
|
||||
main.plugins.bt-tether.devices.ios-phone.netmask = 24
|
||||
main.plugins.bt-tether.devices.ios-phone.interval = 5
|
||||
main.plugins.bt-tether.devices.ios-phone.scantime = 20
|
||||
main.plugins.bt-tether.devices.ios-phone.max_tries = 0
|
||||
main.plugins.bt-tether.devices.ios-phone.share_internet = false
|
||||
main.plugins.bt-tether.devices.ios-phone.priority = 999
|
||||
main.plugins.bt-tether.phone-name = "" # name as shown on the phone i.e. "Pwnagotchi's Phone"
|
||||
main.plugins.bt-tether.mac = ""
|
||||
main.plugins.bt-tether.phone = "" # android or ios
|
||||
main.plugins.bt-tether.ip = "" # 192.168.44.2 android / 172.20.10.2 ios
|
||||
|
||||
main.plugins.fix_services.enabled = true
|
||||
|
||||
@ -53,11 +42,10 @@ main.plugins.gps.enabled = false
|
||||
main.plugins.gps.speed = 19200
|
||||
main.plugins.gps.device = "/dev/ttyUSB0" # for GPSD: "localhost:2947"
|
||||
|
||||
main.plugins.gps_listener.enabled = false
|
||||
|
||||
main.plugins.grid.enabled = true
|
||||
main.plugins.grid.report = true
|
||||
main.plugins.grid.exclude = [
|
||||
"YourHomeNetworkHere"
|
||||
]
|
||||
|
||||
main.plugins.logtail.enabled = false
|
||||
main.plugins.logtail.max-lines = 10000
|
||||
@ -66,23 +54,15 @@ main.plugins.memtemp.enabled = false
|
||||
main.plugins.memtemp.scale = "celsius"
|
||||
main.plugins.memtemp.orientation = "horizontal"
|
||||
|
||||
main.plugins.net-pos.enabled = false
|
||||
main.plugins.net-pos.api_key = "test"
|
||||
main.plugins.ohcapi.enabled = true
|
||||
main.plugins.ohcapi.api_key = "sk_your_api_key_here"
|
||||
main.plugins.ohcapi.receive_email = "yes"
|
||||
|
||||
main.plugins.onlinehashcrack.enabled = false
|
||||
main.plugins.onlinehashcrack.email = ""
|
||||
main.plugins.onlinehashcrack.dashboard = ""
|
||||
main.plugins.onlinehashcrack.single_files = false
|
||||
main.plugins.onlinehashcrack.whitelist = []
|
||||
main.plugins.pisugarx.enabled = false
|
||||
main.plugins.pisugarx.rotation = false
|
||||
main.plugins.pisugarx.default_display = "percentage"
|
||||
|
||||
main.plugins.paw-gps.enabled = false
|
||||
main.plugins.paw-gps.ip = "192.168.44.1:8080"
|
||||
|
||||
main.plugins.pisugar2.enabled = false
|
||||
main.plugins.pisugar2.shutdown = 5
|
||||
main.plugins.pisugar2.sync_rtc_on_boot = false
|
||||
|
||||
main.plugins.session-stats.enabled = true
|
||||
main.plugins.session-stats.enabled = false
|
||||
main.plugins.session-stats.save_directory = "/var/tmp/pwnagotchi/sessions/"
|
||||
|
||||
main.plugins.ups_hat_c.enabled = false
|
||||
@ -100,45 +80,25 @@ main.plugins.webgpsmap.enabled = false
|
||||
|
||||
main.plugins.wigle.enabled = false
|
||||
main.plugins.wigle.api_key = ""
|
||||
main.plugins.wigle.whitelist = []
|
||||
main.plugins.wigle.donate = true
|
||||
main.plugins.wigle.donate = false
|
||||
|
||||
main.plugins.wpa-sec.enabled = false
|
||||
main.plugins.wpa-sec.api_key = ""
|
||||
main.plugins.wpa-sec.api_url = "https://wpa-sec.stanev.org"
|
||||
main.plugins.wpa-sec.download_results = false
|
||||
main.plugins.wpa-sec.whitelist = []
|
||||
main.plugins.wpa-sec.show_pwd = false
|
||||
|
||||
main.iface = "wlan0mon"
|
||||
main.mon_start_cmd = "/usr/bin/monstart"
|
||||
main.mon_stop_cmd = "/usr/bin/monstop"
|
||||
main.mon_max_blind_epochs = 50
|
||||
main.mon_max_blind_epochs = 5
|
||||
main.no_restart = false
|
||||
main.whitelist = [
|
||||
"EXAMPLE_NETWORK",
|
||||
"ANOTHER_EXAMPLE_NETWORK",
|
||||
"fo:od:ba:be:fo:od",
|
||||
"fo:od:ba"
|
||||
]
|
||||
main.filter = ""
|
||||
|
||||
main.log.path = "/home/pi/logs/pwnagotchi.log"
|
||||
main.log.path = "/etc/pwnagotchi/log/pwnagotchi.log"
|
||||
main.log.path-debug = "/etc/pwnagotchi/log/pwnagotchi-debug.log"
|
||||
main.log.rotation.enabled = true
|
||||
main.log.rotation.size = "10M"
|
||||
|
||||
ai.enabled = true
|
||||
ai.path = "/root/brain.nn"
|
||||
ai.laziness = 0.1
|
||||
ai.epochs_per_episode = 50
|
||||
|
||||
ai.params.gamma = 0.99
|
||||
ai.params.n_steps = 1
|
||||
ai.params.vf_coef = 0.25
|
||||
ai.params.ent_coef = 0.01
|
||||
ai.params.max_grad_norm = 0.5
|
||||
ai.params.learning_rate = 0.001
|
||||
ai.params.verbose = 1
|
||||
|
||||
personality.advertise = true
|
||||
personality.deauth = true
|
||||
personality.associate = true
|
||||
@ -160,6 +120,8 @@ personality.bond_encounters_factor = 20000
|
||||
personality.throttle_a = 0.4
|
||||
personality.throttle_d = 0.9
|
||||
|
||||
ui.invert = false # false = black background, true = white background
|
||||
ui.cursor = true
|
||||
ui.fps = 0.0
|
||||
ui.font.name = "DejaVuSansMono" # for japanese: fonts-japanese-gothic
|
||||
ui.font.size_offset = 0 # will be added to the font size
|
||||
@ -189,11 +151,15 @@ ui.faces.debug = "(#__#)"
|
||||
ui.faces.upload = "(1__0)"
|
||||
ui.faces.upload1 = "(1__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.address = "::" # listening on both ipv4 and ipv6 - switch to 0.0.0.0 to listen on just ipv4
|
||||
ui.web.username = "changeme"
|
||||
ui.web.password = "changeme"
|
||||
ui.web.auth = false
|
||||
ui.web.username = "changeme" # if auth is true
|
||||
ui.web.password = "changeme" # if auth is true
|
||||
ui.web.origin = ""
|
||||
ui.web.port = 8080
|
||||
ui.web.on_frame = ""
|
||||
@ -202,12 +168,7 @@ ui.display.enabled = false
|
||||
ui.display.rotation = 180
|
||||
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 = "/home/pi/handshakes"
|
||||
bettercap.silence = [
|
||||
"ble.device.new",
|
||||
"ble.device.lost",
|
||||
@ -225,7 +186,7 @@ bettercap.silence = [
|
||||
|
||||
fs.memory.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.sync = 60
|
||||
fs.memory.mounts.log.zram = true
|
||||
|
@ -3,7 +3,8 @@ import re
|
||||
import tempfile
|
||||
import contextlib
|
||||
import shutil
|
||||
import _thread
|
||||
#import _thread
|
||||
import threading
|
||||
import logging
|
||||
|
||||
from time import sleep
|
||||
@ -85,7 +86,8 @@ def setup_mounts(config):
|
||||
if interval:
|
||||
logging.debug("[FS] Starting thread to sync %s (interval: %d)",
|
||||
options['mount'], interval)
|
||||
_thread.start_new_thread(m.daemonize, (interval,))
|
||||
threading.Thread(target=m.daemonize, args=(interval,),name="File Sys", daemon=True).start()
|
||||
#_thread.start_new_thread(m.daemonize, (interval,))
|
||||
else:
|
||||
logging.debug("[FS] Not syncing %s, because interval is 0",
|
||||
options['mount'])
|
||||
|
@ -13,7 +13,8 @@ def is_connected():
|
||||
try:
|
||||
# check DNS
|
||||
host = 'https://api.opwngrid.xyz/api/v1/uptime'
|
||||
r = requests.get(host, headers=None, timeout=(30.0, 60.0))
|
||||
headers = {'user-agent': f'pwnagotchi/{pwnagotchi.__version__}'}
|
||||
r = requests.get(host, headers=headers, timeout=(30.0, 60.0))
|
||||
if r.json().get('isUp'):
|
||||
return True
|
||||
except:
|
||||
@ -70,10 +71,9 @@ def update_data(last_session):
|
||||
enabled = [name for name, options in pwnagotchi.config['main']['plugins'].items() if
|
||||
'enabled' in options and options['enabled']]
|
||||
language = pwnagotchi.config['main']['lang']
|
||||
ai = pwnagotchi.config['ai']['enabled']
|
||||
|
||||
data = {
|
||||
'ai': ai,
|
||||
'ai': "No AI!",
|
||||
'session': {
|
||||
'duration': last_session.duration,
|
||||
'epochs': last_session.epochs,
|
||||
@ -87,12 +87,11 @@ def update_data(last_session):
|
||||
'peers': last_session.peers,
|
||||
},
|
||||
'uname': subprocess.getoutput("uname -a"),
|
||||
'brain': brain,
|
||||
'version': pwnagotchi.__version__,
|
||||
'build': "Pwnagotchi-Torch by Jayofelony",
|
||||
'build': "Pwnagotchi by Jayofelony",
|
||||
'plugins': enabled,
|
||||
'language': language,
|
||||
'bettercap': subprocess.getoutput("bettercap -version").split(".\n\n")[1],
|
||||
'bettercap': subprocess.getoutput("bettercap -version"),
|
||||
'opwngrid': subprocess.getoutput("pwngrid -version")
|
||||
}
|
||||
|
||||
|
@ -50,10 +50,6 @@ class KeyPair(object):
|
||||
with open(self.fingerprint_path, 'w+t') as fp:
|
||||
fp.write(self.fingerprint)
|
||||
|
||||
# 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 ...")
|
||||
@ -63,6 +59,9 @@ class KeyPair(object):
|
||||
except:
|
||||
pass
|
||||
|
||||
# no exception, keys loaded correctly.
|
||||
self._view.on_starting()
|
||||
return
|
||||
def sign(self, message):
|
||||
hasher = SHA256.new(message.encode("ascii"))
|
||||
signer = PKCS1_PSS.new(self.priv_key, saltLen=16)
|
||||
|
@ -3,12 +3,11 @@
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-11-16 21:10+0100\n"
|
||||
"POT-Creation-Date: 2024-12-16 20:46+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@ -29,10 +28,10 @@ msgstr ""
|
||||
msgid "Hack the Planet!"
|
||||
msgstr ""
|
||||
|
||||
msgid "AI ready."
|
||||
msgid "No more mister Wi-Fi!!"
|
||||
msgstr ""
|
||||
|
||||
msgid "The neural network is ready."
|
||||
msgid "Pretty fly 4 a Wi-Fi!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Generating keys, do not turn off ..."
|
||||
|
@ -3,12 +3,11 @@
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-11-16 21:10+0100\n"
|
||||
"POT-Creation-Date: 2024-12-16 20:46+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@ -29,10 +28,10 @@ msgstr ""
|
||||
msgid "Hack the Planet!"
|
||||
msgstr ""
|
||||
|
||||
msgid "AI ready."
|
||||
msgid "No more mister Wi-Fi!!"
|
||||
msgstr ""
|
||||
|
||||
msgid "The neural network is ready."
|
||||
msgid "Pretty fly 4 a Wi-Fi!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Generating keys, do not turn off ..."
|
||||
|
@ -3,12 +3,11 @@
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-11-16 21:10+0100\n"
|
||||
"POT-Creation-Date: 2024-12-16 20:46+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@ -29,10 +28,10 @@ msgstr ""
|
||||
msgid "Hack the Planet!"
|
||||
msgstr ""
|
||||
|
||||
msgid "AI ready."
|
||||
msgid "No more mister Wi-Fi!!"
|
||||
msgstr ""
|
||||
|
||||
msgid "The neural network is ready."
|
||||
msgid "Pretty fly 4 a Wi-Fi!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Generating keys, do not turn off ..."
|
||||
|
Binary file not shown.
@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: 1.5.3\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-11-16 21:51+0100\n"
|
||||
"POT-Creation-Date: 2024-12-16 20:46+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: MatthewNunu https://github.com/MatthewNunu\n"
|
||||
"Language-Team: \n"
|
||||
@ -28,11 +28,11 @@ msgstr "Nuwe dag, nuwe jag, nuwe pwns!"
|
||||
msgid "Hack the Planet!"
|
||||
msgstr "Hack die wêreld!"
|
||||
|
||||
msgid "AI ready."
|
||||
msgstr "AI gereed."
|
||||
msgid "No more mister Wi-Fi!!"
|
||||
msgstr ""
|
||||
|
||||
msgid "The neural network is ready."
|
||||
msgstr "Die neurale netwerk is gereed."
|
||||
msgid "Pretty fly 4 a Wi-Fi!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Generating keys, do not turn off ..."
|
||||
msgstr "Genereer wagwoord, moenie afskakel nie ..."
|
||||
@ -257,3 +257,9 @@ msgstr "minuut"
|
||||
|
||||
msgid "second"
|
||||
msgstr "tweede"
|
||||
|
||||
#~ msgid "AI ready."
|
||||
#~ msgstr "AI gereed."
|
||||
|
||||
#~ msgid "The neural network is ready."
|
||||
#~ msgstr "Die neurale netwerk is gereed."
|
||||
|
@ -3,12 +3,11 @@
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-11-16 21:10+0100\n"
|
||||
"POT-Creation-Date: 2024-12-16 20:46+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@ -29,10 +28,10 @@ msgstr ""
|
||||
msgid "Hack the Planet!"
|
||||
msgstr ""
|
||||
|
||||
msgid "AI ready."
|
||||
msgid "No more mister Wi-Fi!!"
|
||||
msgstr ""
|
||||
|
||||
msgid "The neural network is ready."
|
||||
msgid "Pretty fly 4 a Wi-Fi!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Generating keys, do not turn off ..."
|
||||
|
@ -3,12 +3,11 @@
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-11-16 21:10+0100\n"
|
||||
"POT-Creation-Date: 2024-12-16 20:46+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@ -29,10 +28,10 @@ msgstr ""
|
||||
msgid "Hack the Planet!"
|
||||
msgstr ""
|
||||
|
||||
msgid "AI ready."
|
||||
msgid "No more mister Wi-Fi!!"
|
||||
msgstr ""
|
||||
|
||||
msgid "The neural network is ready."
|
||||
msgid "Pretty fly 4 a Wi-Fi!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Generating keys, do not turn off ..."
|
||||
|
@ -3,12 +3,11 @@
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-11-16 21:10+0100\n"
|
||||
"POT-Creation-Date: 2024-12-16 20:46+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@ -29,10 +28,10 @@ msgstr ""
|
||||
msgid "Hack the Planet!"
|
||||
msgstr ""
|
||||
|
||||
msgid "AI ready."
|
||||
msgid "No more mister Wi-Fi!!"
|
||||
msgstr ""
|
||||
|
||||
msgid "The neural network is ready."
|
||||
msgid "Pretty fly 4 a Wi-Fi!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Generating keys, do not turn off ..."
|
||||
|
@ -3,12 +3,11 @@
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-11-16 21:10+0100\n"
|
||||
"POT-Creation-Date: 2024-12-16 20:46+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@ -29,10 +28,10 @@ msgstr ""
|
||||
msgid "Hack the Planet!"
|
||||
msgstr ""
|
||||
|
||||
msgid "AI ready."
|
||||
msgid "No more mister Wi-Fi!!"
|
||||
msgstr ""
|
||||
|
||||
msgid "The neural network is ready."
|
||||
msgid "Pretty fly 4 a Wi-Fi!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Generating keys, do not turn off ..."
|
||||
|
@ -3,12 +3,11 @@
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-11-16 21:10+0100\n"
|
||||
"POT-Creation-Date: 2024-12-16 20:46+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@ -29,10 +28,10 @@ msgstr ""
|
||||
msgid "Hack the Planet!"
|
||||
msgstr ""
|
||||
|
||||
msgid "AI ready."
|
||||
msgid "No more mister Wi-Fi!!"
|
||||
msgstr ""
|
||||
|
||||
msgid "The neural network is ready."
|
||||
msgid "Pretty fly 4 a Wi-Fi!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Generating keys, do not turn off ..."
|
||||
|
@ -3,12 +3,11 @@
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-11-16 21:10+0100\n"
|
||||
"POT-Creation-Date: 2024-12-16 20:46+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@ -29,10 +28,10 @@ msgstr ""
|
||||
msgid "Hack the Planet!"
|
||||
msgstr ""
|
||||
|
||||
msgid "AI ready."
|
||||
msgid "No more mister Wi-Fi!!"
|
||||
msgstr ""
|
||||
|
||||
msgid "The neural network is ready."
|
||||
msgid "Pretty fly 4 a Wi-Fi!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Generating keys, do not turn off ..."
|
||||
|
@ -3,12 +3,11 @@
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-11-16 21:10+0100\n"
|
||||
"POT-Creation-Date: 2024-12-16 20:46+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@ -29,10 +28,10 @@ msgstr ""
|
||||
msgid "Hack the Planet!"
|
||||
msgstr ""
|
||||
|
||||
msgid "AI ready."
|
||||
msgid "No more mister Wi-Fi!!"
|
||||
msgstr ""
|
||||
|
||||
msgid "The neural network is ready."
|
||||
msgid "Pretty fly 4 a Wi-Fi!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Generating keys, do not turn off ..."
|
||||
|
@ -3,12 +3,11 @@
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-11-16 21:10+0100\n"
|
||||
"POT-Creation-Date: 2024-12-16 20:46+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@ -29,10 +28,10 @@ msgstr ""
|
||||
msgid "Hack the Planet!"
|
||||
msgstr ""
|
||||
|
||||
msgid "AI ready."
|
||||
msgid "No more mister Wi-Fi!!"
|
||||
msgstr ""
|
||||
|
||||
msgid "The neural network is ready."
|
||||
msgid "Pretty fly 4 a Wi-Fi!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Generating keys, do not turn off ..."
|
||||
|
@ -3,12 +3,11 @@
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-11-16 21:10+0100\n"
|
||||
"POT-Creation-Date: 2024-12-16 20:46+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@ -29,10 +28,10 @@ msgstr ""
|
||||
msgid "Hack the Planet!"
|
||||
msgstr ""
|
||||
|
||||
msgid "AI ready."
|
||||
msgid "No more mister Wi-Fi!!"
|
||||
msgstr ""
|
||||
|
||||
msgid "The neural network is ready."
|
||||
msgid "Pretty fly 4 a Wi-Fi!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Generating keys, do not turn off ..."
|
||||
|
Binary file not shown.
@ -6,7 +6,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Pwnagotchi Belarusian translation v 0.0.1\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-11-16 21:51+0100\n"
|
||||
"POT-Creation-Date: 2024-12-16 20:46+0100\n"
|
||||
"PO-Revision-Date: \n"
|
||||
"Last-Translator: <https://github.com/andreifinski>\n"
|
||||
"Language-Team: \n"
|
||||
@ -33,11 +33,11 @@ msgstr "Новы дзень, новае паляванне, новыя ўзло
|
||||
msgid "Hack the Planet!"
|
||||
msgstr "Узламай гэту Планету!"
|
||||
|
||||
msgid "AI ready."
|
||||
msgstr "A.I. гатовы."
|
||||
msgid "No more mister Wi-Fi!!"
|
||||
msgstr ""
|
||||
|
||||
msgid "The neural network is ready."
|
||||
msgstr "Нейронная сетка гатова."
|
||||
msgid "Pretty fly 4 a Wi-Fi!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Generating keys, do not turn off ..."
|
||||
msgstr "Генерацыя ключоў, не выключайце..."
|
||||
@ -263,6 +263,12 @@ msgstr "хвіліну"
|
||||
msgid "second"
|
||||
msgstr ""
|
||||
|
||||
#~ msgid "AI ready."
|
||||
#~ msgstr "A.I. гатовы."
|
||||
|
||||
#~ msgid "The neural network is ready."
|
||||
#~ msgstr "Нейронная сетка гатова."
|
||||
|
||||
#, python-brace-format
|
||||
#~ msgid "Unit {name} is nearby! {name}"
|
||||
#~ msgstr "Мэта {name} побач!"
|
||||
|
Binary file not shown.
@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: 0.0.1\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-11-16 21:51+0100\n"
|
||||
"POT-Creation-Date: 2024-12-16 20:46+0100\n"
|
||||
"PO-Revision-Date: 2019-10-23 20:56+0200\n"
|
||||
"Last-Translator: Georgi Koemdzhiev <https://github.com/georgikoemdzhiev>\n"
|
||||
"Language-Team: \n"
|
||||
@ -28,11 +28,11 @@ msgstr "Нов ден, нов лов, нови pwns!"
|
||||
msgid "Hack the Planet!"
|
||||
msgstr "Хакни планетата!"
|
||||
|
||||
msgid "AI ready."
|
||||
msgstr "AI готов."
|
||||
msgid "No more mister Wi-Fi!!"
|
||||
msgstr ""
|
||||
|
||||
msgid "The neural network is ready."
|
||||
msgstr "Невронната мрежа е готова."
|
||||
msgid "Pretty fly 4 a Wi-Fi!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Generating keys, do not turn off ..."
|
||||
msgstr "Генериране на ключове, не изключвай ..."
|
||||
@ -258,3 +258,9 @@ msgstr "минута"
|
||||
|
||||
msgid "second"
|
||||
msgstr "секунда"
|
||||
|
||||
#~ msgid "AI ready."
|
||||
#~ msgstr "AI готов."
|
||||
|
||||
#~ msgid "The neural network is ready."
|
||||
#~ msgstr "Невронната мрежа е готова."
|
||||
|
@ -3,12 +3,11 @@
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-11-16 21:10+0100\n"
|
||||
"POT-Creation-Date: 2024-12-16 20:46+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@ -29,10 +28,10 @@ msgstr ""
|
||||
msgid "Hack the Planet!"
|
||||
msgstr ""
|
||||
|
||||
msgid "AI ready."
|
||||
msgid "No more mister Wi-Fi!!"
|
||||
msgstr ""
|
||||
|
||||
msgid "The neural network is ready."
|
||||
msgid "Pretty fly 4 a Wi-Fi!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Generating keys, do not turn off ..."
|
||||
|
@ -3,12 +3,11 @@
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-11-16 21:10+0100\n"
|
||||
"POT-Creation-Date: 2024-12-16 20:46+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@ -29,10 +28,10 @@ msgstr ""
|
||||
msgid "Hack the Planet!"
|
||||
msgstr ""
|
||||
|
||||
msgid "AI ready."
|
||||
msgid "No more mister Wi-Fi!!"
|
||||
msgstr ""
|
||||
|
||||
msgid "The neural network is ready."
|
||||
msgid "Pretty fly 4 a Wi-Fi!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Generating keys, do not turn off ..."
|
||||
|
@ -3,12 +3,11 @@
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-11-16 21:10+0100\n"
|
||||
"POT-Creation-Date: 2024-12-16 20:46+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@ -29,10 +28,10 @@ msgstr ""
|
||||
msgid "Hack the Planet!"
|
||||
msgstr ""
|
||||
|
||||
msgid "AI ready."
|
||||
msgid "No more mister Wi-Fi!!"
|
||||
msgstr ""
|
||||
|
||||
msgid "The neural network is ready."
|
||||
msgid "Pretty fly 4 a Wi-Fi!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Generating keys, do not turn off ..."
|
||||
|
@ -3,12 +3,11 @@
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-11-16 21:10+0100\n"
|
||||
"POT-Creation-Date: 2024-12-16 20:46+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@ -29,10 +28,10 @@ msgstr ""
|
||||
msgid "Hack the Planet!"
|
||||
msgstr ""
|
||||
|
||||
msgid "AI ready."
|
||||
msgid "No more mister Wi-Fi!!"
|
||||
msgstr ""
|
||||
|
||||
msgid "The neural network is ready."
|
||||
msgid "Pretty fly 4 a Wi-Fi!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Generating keys, do not turn off ..."
|
||||
|
@ -3,12 +3,11 @@
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-11-16 21:10+0100\n"
|
||||
"POT-Creation-Date: 2024-12-16 20:46+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@ -29,10 +28,10 @@ msgstr ""
|
||||
msgid "Hack the Planet!"
|
||||
msgstr ""
|
||||
|
||||
msgid "AI ready."
|
||||
msgid "No more mister Wi-Fi!!"
|
||||
msgstr ""
|
||||
|
||||
msgid "The neural network is ready."
|
||||
msgid "Pretty fly 4 a Wi-Fi!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Generating keys, do not turn off ..."
|
||||
|
@ -3,12 +3,11 @@
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-11-16 21:10+0100\n"
|
||||
"POT-Creation-Date: 2024-12-16 20:46+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@ -29,10 +28,10 @@ msgstr ""
|
||||
msgid "Hack the Planet!"
|
||||
msgstr ""
|
||||
|
||||
msgid "AI ready."
|
||||
msgid "No more mister Wi-Fi!!"
|
||||
msgstr ""
|
||||
|
||||
msgid "The neural network is ready."
|
||||
msgid "Pretty fly 4 a Wi-Fi!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Generating keys, do not turn off ..."
|
||||
|
@ -3,12 +3,11 @@
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-11-16 21:51+0100\n"
|
||||
"POT-Creation-Date: 2024-12-16 20:46+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@ -29,10 +28,10 @@ msgstr ""
|
||||
msgid "Hack the Planet!"
|
||||
msgstr ""
|
||||
|
||||
msgid "AI ready."
|
||||
msgid "No more mister Wi-Fi!!"
|
||||
msgstr ""
|
||||
|
||||
msgid "The neural network is ready."
|
||||
msgid "Pretty fly 4 a Wi-Fi!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Generating keys, do not turn off ..."
|
||||
|
@ -3,12 +3,11 @@
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-11-16 21:10+0100\n"
|
||||
"POT-Creation-Date: 2024-12-16 20:46+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@ -29,10 +28,10 @@ msgstr ""
|
||||
msgid "Hack the Planet!"
|
||||
msgstr ""
|
||||
|
||||
msgid "AI ready."
|
||||
msgid "No more mister Wi-Fi!!"
|
||||
msgstr ""
|
||||
|
||||
msgid "The neural network is ready."
|
||||
msgid "Pretty fly 4 a Wi-Fi!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Generating keys, do not turn off ..."
|
||||
|
@ -3,12 +3,11 @@
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-11-16 21:10+0100\n"
|
||||
"POT-Creation-Date: 2024-12-16 20:46+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@ -29,10 +28,10 @@ msgstr ""
|
||||
msgid "Hack the Planet!"
|
||||
msgstr ""
|
||||
|
||||
msgid "AI ready."
|
||||
msgid "No more mister Wi-Fi!!"
|
||||
msgstr ""
|
||||
|
||||
msgid "The neural network is ready."
|
||||
msgid "Pretty fly 4 a Wi-Fi!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Generating keys, do not turn off ..."
|
||||
|
@ -3,12 +3,11 @@
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-11-16 21:10+0100\n"
|
||||
"POT-Creation-Date: 2024-12-16 20:46+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@ -29,10 +28,10 @@ msgstr ""
|
||||
msgid "Hack the Planet!"
|
||||
msgstr ""
|
||||
|
||||
msgid "AI ready."
|
||||
msgid "No more mister Wi-Fi!!"
|
||||
msgstr ""
|
||||
|
||||
msgid "The neural network is ready."
|
||||
msgid "Pretty fly 4 a Wi-Fi!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Generating keys, do not turn off ..."
|
||||
|
@ -3,12 +3,11 @@
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-11-16 21:10+0100\n"
|
||||
"POT-Creation-Date: 2024-12-16 20:46+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@ -29,10 +28,10 @@ msgstr ""
|
||||
msgid "Hack the Planet!"
|
||||
msgstr ""
|
||||
|
||||
msgid "AI ready."
|
||||
msgid "No more mister Wi-Fi!!"
|
||||
msgstr ""
|
||||
|
||||
msgid "The neural network is ready."
|
||||
msgid "Pretty fly 4 a Wi-Fi!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Generating keys, do not turn off ..."
|
||||
|
Binary file not shown.
@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: 0.0.1\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-11-16 21:51+0100\n"
|
||||
"POT-Creation-Date: 2024-12-16 20:46+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: Czechball <czechball@users.noreply.github.com>\n"
|
||||
"Language-Team: pwnagotchi <33197631+dadav@users.noreply.github.com>\n"
|
||||
@ -28,11 +28,11 @@ msgstr "Nový den, nový lov, nové úlovky!"
|
||||
msgid "Hack the Planet!"
|
||||
msgstr "Hackni celou planetu!"
|
||||
|
||||
msgid "AI ready."
|
||||
msgstr "AI připraveno."
|
||||
msgid "No more mister Wi-Fi!!"
|
||||
msgstr ""
|
||||
|
||||
msgid "The neural network is ready."
|
||||
msgstr "Neuronová síť je připravena."
|
||||
msgid "Pretty fly 4 a Wi-Fi!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Generating keys, do not turn off ..."
|
||||
msgstr "Generování klíčů, nevypínej mě..."
|
||||
@ -257,3 +257,9 @@ msgstr "minuta"
|
||||
|
||||
msgid "second"
|
||||
msgstr "sekunda"
|
||||
|
||||
#~ msgid "AI ready."
|
||||
#~ msgstr "AI připraveno."
|
||||
|
||||
#~ msgid "The neural network is ready."
|
||||
#~ msgstr "Neuronová síť je připravena."
|
||||
|
@ -3,12 +3,11 @@
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-11-16 21:10+0100\n"
|
||||
"POT-Creation-Date: 2024-12-16 20:46+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@ -29,10 +28,10 @@ msgstr ""
|
||||
msgid "Hack the Planet!"
|
||||
msgstr ""
|
||||
|
||||
msgid "AI ready."
|
||||
msgid "No more mister Wi-Fi!!"
|
||||
msgstr ""
|
||||
|
||||
msgid "The neural network is ready."
|
||||
msgid "Pretty fly 4 a Wi-Fi!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Generating keys, do not turn off ..."
|
||||
|
@ -3,12 +3,11 @@
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-11-16 21:10+0100\n"
|
||||
"POT-Creation-Date: 2024-12-16 20:46+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@ -29,10 +28,10 @@ msgstr ""
|
||||
msgid "Hack the Planet!"
|
||||
msgstr ""
|
||||
|
||||
msgid "AI ready."
|
||||
msgid "No more mister Wi-Fi!!"
|
||||
msgstr ""
|
||||
|
||||
msgid "The neural network is ready."
|
||||
msgid "Pretty fly 4 a Wi-Fi!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Generating keys, do not turn off ..."
|
||||
|
@ -3,12 +3,11 @@
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-11-16 21:10+0100\n"
|
||||
"POT-Creation-Date: 2024-12-16 20:46+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@ -29,10 +28,10 @@ msgstr ""
|
||||
msgid "Hack the Planet!"
|
||||
msgstr ""
|
||||
|
||||
msgid "AI ready."
|
||||
msgid "No more mister Wi-Fi!!"
|
||||
msgstr ""
|
||||
|
||||
msgid "The neural network is ready."
|
||||
msgid "Pretty fly 4 a Wi-Fi!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Generating keys, do not turn off ..."
|
||||
|
Binary file not shown.
@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: 0.0.1\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-11-16 21:51+0100\n"
|
||||
"POT-Creation-Date: 2024-12-16 20:46+0100\n"
|
||||
"PO-Revision-Date: 2020-01-18 21:56+ZONE\n"
|
||||
"Last-Translator: Dennis Kjær Jensen <signout@signout.dk>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@ -28,11 +28,11 @@ msgstr "Ny dag, ny jagt, nye pwns!"
|
||||
msgid "Hack the Planet!"
|
||||
msgstr "Hack planeten!"
|
||||
|
||||
msgid "AI ready."
|
||||
msgstr "AI klar."
|
||||
msgid "No more mister Wi-Fi!!"
|
||||
msgstr ""
|
||||
|
||||
msgid "The neural network is ready."
|
||||
msgstr "Det neurale netværk er klart."
|
||||
msgid "Pretty fly 4 a Wi-Fi!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Generating keys, do not turn off ..."
|
||||
msgstr "Genererer nøgler, sluk ikke ..."
|
||||
@ -257,3 +257,9 @@ msgstr "minut"
|
||||
|
||||
msgid "second"
|
||||
msgstr "sekund"
|
||||
|
||||
#~ msgid "AI ready."
|
||||
#~ msgstr "AI klar."
|
||||
|
||||
#~ msgid "The neural network is ready."
|
||||
#~ msgstr "Det neurale netværk er klart."
|
||||
|
Binary file not shown.
@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: 0.0.1\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-11-16 21:51+0100\n"
|
||||
"POT-Creation-Date: 2024-12-16 20:46+0100\n"
|
||||
"PO-Revision-Date: 2019-09-29 14:00+0200\n"
|
||||
"Last-Translator: dadav <33197631+dadav@users.noreply.github.com>\n"
|
||||
"Language-Team: DE <33197631+dadav@users.noreply.github.com>\n"
|
||||
@ -28,11 +28,11 @@ msgstr "Neuer Tag, neue Jagd, neue Pwns!"
|
||||
msgid "Hack the Planet!"
|
||||
msgstr "Hack den Planeten!"
|
||||
|
||||
msgid "AI ready."
|
||||
msgstr "KI bereit."
|
||||
msgid "No more mister Wi-Fi!!"
|
||||
msgstr ""
|
||||
|
||||
msgid "The neural network is ready."
|
||||
msgstr "Das neurale Netz ist bereit."
|
||||
msgid "Pretty fly 4 a Wi-Fi!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Generating keys, do not turn off ..."
|
||||
msgstr "Generiere Schlüssel, nicht ausschalten..."
|
||||
@ -257,3 +257,9 @@ msgstr "Minute"
|
||||
|
||||
msgid "second"
|
||||
msgstr "Sekunde"
|
||||
|
||||
#~ msgid "AI ready."
|
||||
#~ msgstr "KI bereit."
|
||||
|
||||
#~ msgid "The neural network is ready."
|
||||
#~ msgstr "Das neurale Netz ist bereit."
|
||||
|
@ -3,12 +3,11 @@
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-11-16 21:10+0100\n"
|
||||
"POT-Creation-Date: 2024-12-16 20:46+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@ -29,10 +28,10 @@ msgstr ""
|
||||
msgid "Hack the Planet!"
|
||||
msgstr ""
|
||||
|
||||
msgid "AI ready."
|
||||
msgid "No more mister Wi-Fi!!"
|
||||
msgstr ""
|
||||
|
||||
msgid "The neural network is ready."
|
||||
msgid "Pretty fly 4 a Wi-Fi!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Generating keys, do not turn off ..."
|
||||
|
@ -3,12 +3,11 @@
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-11-16 21:10+0100\n"
|
||||
"POT-Creation-Date: 2024-12-16 20:46+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@ -29,10 +28,10 @@ msgstr ""
|
||||
msgid "Hack the Planet!"
|
||||
msgstr ""
|
||||
|
||||
msgid "AI ready."
|
||||
msgid "No more mister Wi-Fi!!"
|
||||
msgstr ""
|
||||
|
||||
msgid "The neural network is ready."
|
||||
msgid "Pretty fly 4 a Wi-Fi!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Generating keys, do not turn off ..."
|
||||
|
@ -3,12 +3,11 @@
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-11-16 21:10+0100\n"
|
||||
"POT-Creation-Date: 2024-12-16 20:46+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@ -29,10 +28,10 @@ msgstr ""
|
||||
msgid "Hack the Planet!"
|
||||
msgstr ""
|
||||
|
||||
msgid "AI ready."
|
||||
msgid "No more mister Wi-Fi!!"
|
||||
msgstr ""
|
||||
|
||||
msgid "The neural network is ready."
|
||||
msgid "Pretty fly 4 a Wi-Fi!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Generating keys, do not turn off ..."
|
||||
|
Binary file not shown.
@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: 0.0.1\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-11-16 21:51+0100\n"
|
||||
"POT-Creation-Date: 2024-12-16 20:46+0100\n"
|
||||
"PO-Revision-Date: 2019-10-03 08:00+0000\n"
|
||||
"Last-Translator: Periklis Fregkos <fregkos@gmail.com>\n"
|
||||
"Language-Team: pwnagotchi <33197631+dadav@users.noreply.github.com>\n"
|
||||
@ -29,11 +29,11 @@ msgstr "Νέα μέρα, νέο κυνήγι, νέα pwns!"
|
||||
msgid "Hack the Planet!"
|
||||
msgstr "Hackαρε τον πλανήτη!"
|
||||
|
||||
msgid "AI ready."
|
||||
msgstr "ΤΝ έτοιμη."
|
||||
msgid "No more mister Wi-Fi!!"
|
||||
msgstr ""
|
||||
|
||||
msgid "The neural network is ready."
|
||||
msgstr "Το νευρωνικό δίκτυο είναι έτοιμο."
|
||||
msgid "Pretty fly 4 a Wi-Fi!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Generating keys, do not turn off ..."
|
||||
msgstr ""
|
||||
@ -258,3 +258,9 @@ msgstr ""
|
||||
|
||||
msgid "second"
|
||||
msgstr ""
|
||||
|
||||
#~ msgid "AI ready."
|
||||
#~ msgstr "ΤΝ έτοιμη."
|
||||
|
||||
#~ msgid "The neural network is ready."
|
||||
#~ msgstr "Το νευρωνικό δίκτυο είναι έτοιμο."
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user