所需物料

  1. 树莓派(RPI-zero-w),记得得是w型号,不然无法扫描周围Wi-Fi
  2. waveshare_V3
  3. 移动电池(可选,用充电宝也行)
  4. 老式USB数据线
  5. 内存不少于16G的SD卡

软件准备

  • 下载发行版安装包

  • 下载项目开源代码

  • 下载烧录工具

操作步骤

  1. 烧录镜像到SD卡中,注意烧录好不要插入树莓派中,只拔出即可
  2. 在电脑编辑器打开下载好的源代码,并按照以下修改添加
# pwnagotchi/ui/display.py
import os
import logging
import threadingimport pwnagotchi.plugins as plugins
import pwnagotchi.ui.hw as hw
from pwnagotchi.ui.view import Viewclass Display(View):def __init__(self, config, state={}):super(Display, self).__init__(config, hw.display_for(config), state)config = config['ui']['display']self._enabled = config['enabled']self._rotation = config['rotation']self.init_display()self._canvas_next_event = threading.Event()self._canvas_next = Noneself._render_thread_instance = threading.Thread(target=self._render_thread,daemon=True)self._render_thread_instance.start()def is_inky(self):return self._implementation.name == 'inky'def is_papirus(self):return self._implementation.name == 'papirus'def is_waveshare_v1(self):return self._implementation.name == 'waveshare_1'def is_waveshare_v2(self):return self._implementation.name == 'waveshare_2'def is_waveshare_v3(self):return self._implementation.name == 'waveshare_3'def is_waveshare27inch(self):return self._implementation.name == 'waveshare27inch'def is_waveshare29inch(self):return self._implementation.name == 'waveshare29inch'def is_oledhat(self):return self._implementation.name == 'oledhat'def is_lcdhat(self):return self._implementation.name == 'lcdhat'def is_dfrobot_v1(self):return self._implementation.name == 'dfrobot_v1'def is_dfrobot_v2(self):return self._implementation.name == 'dfrobot_v2'def is_waveshare144lcd(self):return self._implementation.name == 'waveshare144lcd'def is_waveshare154inch(self):return self._implementation.name == 'waveshare154inch'def is_waveshare213d(self):return self._implementation.name == 'waveshare213d'def is_waveshare213bc(self):return self._implementation.name == 'waveshare213bc'def is_spotpear24inch(self):return self._implementation.name == 'spotpear24inch'def is_waveshare_any(self):return self.is_waveshare_v1() or self.is_waveshare_v2()def init_display(self):if self._enabled:self._implementation.initialize()plugins.on('display_setup', self._implementation)else:logging.warning("display module is disabled")self.on_render(self._on_view_rendered)def clear(self):self._implementation.clear()def image(self):img = Noneif self._canvas is not None:img = self._canvas if self._rotation == 0 else self._canvas.rotate(-self._rotation)return imgdef _render_thread(self):"""Used for non-blocking screen updating."""while True:self._canvas_next_event.wait()self._canvas_next_event.clear()self._implementation.render(self._canvas_next)def _on_view_rendered(self, img):try:if self._config['ui']['web']['on_frame'] != '':os.system(self._config['ui']['web']['on_frame'])except Exception as e:logging.error("%s" % e)if self._enabled:self._canvas = (img if self._rotation == 0 else img.rotate(self._rotation))if self._implementation is not None:self._canvas_next = self._canvasself._canvas_next_event.set()
# pwnagotchi/ui/hw/__init__.py
from pwnagotchi.ui.hw.inky import Inky
from pwnagotchi.ui.hw.papirus import Papirus
from pwnagotchi.ui.hw.oledhat import OledHat
from pwnagotchi.ui.hw.lcdhat import LcdHat
from pwnagotchi.ui.hw.dfrobot1 import DFRobotV1
from pwnagotchi.ui.hw.dfrobot2 import DFRobotV2
from pwnagotchi.ui.hw.waveshare1 import WaveshareV1
from pwnagotchi.ui.hw.waveshare2 import WaveshareV2
from pwnagotchi.ui.hw.waveshare3 import WaveshareV3
from pwnagotchi.ui.hw.waveshare27inch import Waveshare27inch
from pwnagotchi.ui.hw.waveshare29inch import Waveshare29inch
from pwnagotchi.ui.hw.waveshare144lcd import Waveshare144lcd
from pwnagotchi.ui.hw.waveshare154inch import Waveshare154inch
from pwnagotchi.ui.hw.waveshare213d import Waveshare213d
from pwnagotchi.ui.hw.waveshare213bc import Waveshare213bc
from pwnagotchi.ui.hw.spotpear24inch import Spotpear24inchdef display_for(config):# config has been normalized already in utils.load_configif config['ui']['display']['type'] == 'inky':return Inky(config)elif config['ui']['display']['type'] == 'papirus':return Papirus(config)if config['ui']['display']['type'] == 'oledhat':return OledHat(config)if config['ui']['display']['type'] == 'lcdhat':return LcdHat(config)if config['ui']['display']['type'] == 'dfrobot_1':return DFRobotV1(config)if config['ui']['display']['type'] == 'dfrobot_2':return DFRobotV2(config)elif config['ui']['display']['type'] == 'waveshare_1':return WaveshareV1(config)elif config['ui']['display']['type'] == 'waveshare_2':return WaveshareV2(config)elif config['ui']['display']['type'] == 'waveshare_3':return WaveshareV3(config)elif config['ui']['display']['type'] == 'waveshare27inch':return Waveshare27inch(config)elif config['ui']['display']['type'] == 'waveshare29inch':return Waveshare29inch(config)elif config['ui']['display']['type'] == 'waveshare144lcd':return Waveshare144lcd(config)elif config['ui']['display']['type'] == 'waveshare154inch':return Waveshare154inch(config)elif config['ui']['display']['type'] == 'waveshare213d':return Waveshare213d(config)elif config['ui']['display']['type'] == 'waveshare213bc':return Waveshare213bc(config)elif config['ui']['display']['type'] == 'spotpear24inch':return Spotpear24inch(config)
# pwnagotchi/ui/hw/libs/waveshare/v3/epd2in13_V3.pyimport logging
from . import epdconfig
import numpy as np# Display resolution
EPD_WIDTH       = 122
EPD_HEIGHT      = 250logger = logging.getLogger(__name__)class EPD:def __init__(self):self.reset_pin = epdconfig.RST_PINself.dc_pin = epdconfig.DC_PINself.busy_pin = epdconfig.BUSY_PINself.cs_pin = epdconfig.CS_PINself.width = EPD_WIDTHself.height = EPD_HEIGHTlut_partial_update= [0x0,0x40,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80,0x80,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x40,0x40,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x14,0x0,0x0,0x0,0x0,0x0,0x0,  0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x22,0x22,0x22,0x22,0x22,0x22,0x0,0x0,0x0,0x22,0x17,0x41,0x00,0x32,0x36,]lut_full_update = [ 0x80,0x4A,0x40,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x40,0x4A,0x80,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80,0x4A,0x40,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x40,0x4A,0x80,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xF,0x0,0x0,0x0,0x0,0x0,0x0,0xF,0x0,0x0,0xF,0x0,0x0,0x2,0xF,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x22,0x22,0x22,0x22,0x22,0x22,0x0,0x0,0x0,0x22,0x17,0x41,0x0,0x32,0x36,]'''function :Hardware resetparameter:'''def reset(self):epdconfig.digital_write(self.reset_pin, 1)epdconfig.delay_ms(20) epdconfig.digital_write(self.reset_pin, 0)epdconfig.delay_ms(2)epdconfig.digital_write(self.reset_pin, 1)epdconfig.delay_ms(20)   '''function :send commandparameter:command : Command register'''def send_command(self, command):epdconfig.digital_write(self.dc_pin, 0)epdconfig.digital_write(self.cs_pin, 0)epdconfig.spi_writebyte([command])epdconfig.digital_write(self.cs_pin, 1)'''function :send dataparameter:data : Write data'''def send_data(self, data):epdconfig.digital_write(self.dc_pin, 1)epdconfig.digital_write(self.cs_pin, 0)epdconfig.spi_writebyte([data])epdconfig.digital_write(self.cs_pin, 1)'''function :Wait until the busy_pin goes LOWparameter:'''def ReadBusy(self):logger.debug("e-Paper busy")while(epdconfig.digital_read(self.busy_pin) == 1):      # 0: idle, 1: busyepdconfig.delay_ms(10)  logger.debug("e-Paper busy release")'''function : Turn On Displayparameter:'''def TurnOnDisplay(self):self.send_command(0x22) # Display Update Controlself.send_data(0xC7)self.send_command(0x20) # Activate Display Update Sequenceself.ReadBusy()'''function : Turn On Display Partparameter:'''def TurnOnDisplayPart(self):self.send_command(0x22) # Display Update Controlself.send_data(0x0f)    # fast:0x0c, quality:0x0f, 0xcfself.send_command(0x20) # Activate Display Update Sequenceself.ReadBusy()'''function : Set lutparameter:lut : lut data'''    def Lut(self, lut):self.send_command(0x32)for i in range(0, 153):self.send_data(lut[i])self.ReadBusy()'''function : Send lut data and configurationparameter:lut : lut data '''def SetLut(self, lut):self.Lut(lut)self.send_command(0x3f)self.send_data(lut[153])self.send_command(0x03)     # gate voltageself.send_data(lut[154])self.send_command(0x04)     # source voltageself.send_data(lut[155])    # VSHself.send_data(lut[156])    # VSH2self.send_data(lut[157])    # VSLself.send_command(0x2c)     # VCOMself.send_data(lut[158])'''function : Setting the display windowparameter:xstart : X-axis starting positionystart : Y-axis starting positionxend : End position of X-axisyend : End position of Y-axis'''def SetWindow(self, x_start, y_start, x_end, y_end):self.send_command(0x44) # SET_RAM_X_ADDRESS_START_END_POSITION# x point must be the multiple of 8 or the last 3 bits will be ignoredself.send_data((x_start>>3) & 0xFF)self.send_data((x_end>>3) & 0xFF)self.send_command(0x45) # SET_RAM_Y_ADDRESS_START_END_POSITIONself.send_data(y_start & 0xFF)self.send_data((y_start >> 8) & 0xFF)self.send_data(y_end & 0xFF)self.send_data((y_end >> 8) & 0xFF)'''function : Set Cursorparameter:x : X-axis starting positiony : Y-axis starting position'''def SetCursor(self, x, y):self.send_command(0x4E) # SET_RAM_X_ADDRESS_COUNTER# x point must be the multiple of 8 or the last 3 bits will be ignoredself.send_data(x & 0xFF)self.send_command(0x4F) # SET_RAM_Y_ADDRESS_COUNTERself.send_data(y & 0xFF)self.send_data((y >> 8) & 0xFF)'''function : Initialize the e-Paper registerparameter:'''def init(self):if (epdconfig.module_init() != 0):return -1# EPD hardware init startself.reset()self.ReadBusy()self.send_command(0x12)  #SWRESETself.ReadBusy() self.send_command(0x01) #Driver output control      self.send_data(0xf9)self.send_data(0x00)self.send_data(0x00)self.send_command(0x11) #data entry mode       self.send_data(0x03)self.SetWindow(0, 0, self.width-1, self.height-1)self.SetCursor(0, 0)self.send_command(0x3c)self.send_data(0x05)self.send_command(0x21) #  Display update controlself.send_data(0x00)self.send_data(0x80)self.send_command(0x18)self.send_data(0x80)self.ReadBusy()self.SetLut(self.lut_full_update)return 0'''function : Display imagesparameter:image : Image data'''def getbuffer(self, image):img = imageimwidth, imheight = img.sizeif(imwidth == self.width and imheight == self.height):img = img.convert('1')elif(imwidth == self.height and imheight == self.width):# image has correct dimensions, but needs to be rotatedimg = img.rotate(90, expand=True).convert('1')else:logger.warning("Wrong image dimensions: must be " + str(self.width) + "x" + str(self.height))# return a blank bufferreturn [0x00] * (int(self.width/8) * self.height)buf = bytearray(img.tobytes('raw'))return buf'''function : Sends the image buffer in RAM to e-Paper and displaysparameter:image : Image data'''def display(self, image):if self.width%8 == 0:linewidth = int(self.width/8)else:linewidth = int(self.width/8) + 1self.send_command(0x24)for j in range(0, self.height):for i in range(0, linewidth):self.send_data(image[i + j * linewidth])   self.TurnOnDisplay()'''function : Sends the image buffer in RAM to e-Paper and partial refreshparameter:image : Image data'''def displayPartial(self, image):if self.width%8 == 0:linewidth = int(self.width/8)else:linewidth = int(self.width/8) + 1epdconfig.digital_write(self.reset_pin, 0)epdconfig.delay_ms(1)epdconfig.digital_write(self.reset_pin, 1)  self.SetLut(self.lut_partial_update)self.send_command(0x37)self.send_data(0x00)self.send_data(0x00)self.send_data(0x00)self.send_data(0x00)self.send_data(0x00)self.send_data(0x40)self.send_data(0x00)self.send_data(0x00)self.send_data(0x00)  self.send_data(0x00)self.send_command(0x3C) #BorderWavefromself.send_data(0x80)self.send_command(0x22) self.send_data(0xC0)self.send_command(0x20)self.ReadBusy()self.SetWindow(0, 0, self.width - 1, self.height - 1)self.SetCursor(0, 0)self.send_command(0x24) # WRITE_RAMfor j in range(0, self.height):for i in range(0, linewidth):self.send_data(image[i + j * linewidth])   self.TurnOnDisplayPart()'''function : Refresh a base imageparameter:image : Image data'''def displayPartBaseImage(self, image):if self.width%8 == 0:linewidth = int(self.width/8)else:linewidth = int(self.width/8) + 1self.send_command(0x24)for j in range(0, self.height):for i in range(0, linewidth):self.send_data(image[i + j * linewidth])   self.send_command(0x26)for j in range(0, self.height):for i in range(0, linewidth):self.send_data(image[i + j * linewidth])  self.TurnOnDisplay()'''function : Clear screenparameter:'''def Clear(self, color):if self.width%8 == 0:linewidth = int(self.width/8)else:linewidth = int(self.width/8) + 1# logger.debug(linewidth)self.send_command(0x24)for j in range(0, self.height):for i in range(0, linewidth):self.send_data(color)self.TurnOnDisplay()'''function : Enter sleep modeparameter:'''def sleep(self):self.send_command(0x10) #enter deep sleepself.send_data(0x01)epdconfig.delay_ms(2000)epdconfig.module_exit()### END OF FILE ###
# pwnagotchi/ui/hw/libs/waveshare/v3/epdconfig.pyimport os
import logging
import sys
import timeclass RaspberryPi:# Pin definitionRST_PIN         = 17DC_PIN          = 25CS_PIN          = 8BUSY_PIN        = 24def __init__(self):import spidevimport RPi.GPIOself.GPIO = RPi.GPIO# SPI device, bus = 0, device = 0self.SPI = spidev.SpiDev(0, 0)def digital_write(self, pin, value):self.GPIO.output(pin, value)def digital_read(self, pin):return self.GPIO.input(pin)def delay_ms(self, delaytime):time.sleep(delaytime / 1000.0)def spi_writebyte(self, data):self.SPI.writebytes(data)def module_init(self):self.GPIO.setmode(self.GPIO.BCM)self.GPIO.setwarnings(False)self.GPIO.setup(self.RST_PIN, self.GPIO.OUT)self.GPIO.setup(self.DC_PIN, self.GPIO.OUT)self.GPIO.setup(self.CS_PIN, self.GPIO.OUT)self.GPIO.setup(self.BUSY_PIN, self.GPIO.IN)self.SPI.max_speed_hz = 4000000self.SPI.mode = 0b00return 0def module_exit(self):logging.debug("spi end")self.SPI.close()logging.debug("close 5V, Module enters 0 power consumption ...")self.GPIO.output(self.RST_PIN, 0)self.GPIO.output(self.DC_PIN, 0)self.GPIO.cleanup()class JetsonNano:# Pin definitionRST_PIN         = 17DC_PIN          = 25CS_PIN          = 8BUSY_PIN        = 24def __init__(self):import ctypesfind_dirs = [os.path.dirname(os.path.realpath(__file__)),'/usr/local/lib','/usr/lib',]self.SPI = Nonefor find_dir in find_dirs:so_filename = os.path.join(find_dir, 'sysfs_software_spi.so')if os.path.exists(so_filename):self.SPI = ctypes.cdll.LoadLibrary(so_filename)breakif self.SPI is None:raise RuntimeError('Cannot find sysfs_software_spi.so')import Jetson.GPIOself.GPIO = Jetson.GPIOdef digital_write(self, pin, value):self.GPIO.output(pin, value)def digital_read(self, pin):return self.GPIO.input(self.BUSY_PIN)def delay_ms(self, delaytime):time.sleep(delaytime / 1000.0)def spi_writebyte(self, data):self.SPI.SYSFS_software_spi_transfer(data[0])def module_init(self):self.GPIO.setmode(self.GPIO.BCM)self.GPIO.setwarnings(False)self.GPIO.setup(self.RST_PIN, self.GPIO.OUT)self.GPIO.setup(self.DC_PIN, self.GPIO.OUT)self.GPIO.setup(self.CS_PIN, self.GPIO.OUT)self.GPIO.setup(self.BUSY_PIN, self.GPIO.IN)self.SPI.SYSFS_software_spi_begin()return 0def module_exit(self):logging.debug("spi end")self.SPI.SYSFS_software_spi_end()logging.debug("close 5V, Module enters 0 power consumption ...")self.GPIO.output(self.RST_PIN, 0)self.GPIO.output(self.DC_PIN, 0)self.GPIO.cleanup()if os.path.exists('/sys/bus/platform/drivers/gpiomem-bcm2835'):implementation = RaspberryPi()
else:implementation = JetsonNano()for func in [x for x in dir(implementation) if not x.startswith('_')]:setattr(sys.modules[__name__], func, getattr(implementation, func))### END OF FILE ###
# pwnagotchi/ui/hw/waveshare3.py
import loggingimport pwnagotchi.ui.fonts as fonts
from pwnagotchi.ui.hw.base import DisplayImplclass WaveshareV3(DisplayImpl):def __init__(self, config):super(WaveshareV3, self).__init__(config, 'waveshare_3')self._display = Nonedef layout(self):fonts.setup(10, 8, 10, 25, 25, 9)self._layout['width'] = 250self._layout['height'] = 122self._layout['face'] = (0, 40)self._layout['name'] = (5, 20)self._layout['channel'] = (0, 0)self._layout['aps'] = (28, 0)self._layout['uptime'] = (185, 0)self._layout['line1'] = [0, 14, 250, 14]self._layout['line2'] = [0, 108, 250, 108]self._layout['friend_face'] = (0, 92)self._layout['friend_name'] = (40, 94)self._layout['shakes'] = (0, 109)self._layout['mode'] = (225, 109)self._layout['status'] = {'pos': (125, 20),'font': fonts.status_font(fonts.Medium),'max': 20}return self._layoutdef initialize(self):logging.info("initializing waveshare v3 display")from pwnagotchi.ui.hw.libs.waveshare.v3.epd2in13_V3 import EPDself._display = EPD()self._display.init()self._display.Clear(0xFF)def render(self, canvas):buf = self._display.getbuffer(canvas)self._display.displayPartial(buf)def clear(self):#passself._display.Clear(0xFF)
# pwnagotchi/utils.pyimport logging
import glob
import os
import time
import subprocessimport json
import shutil
import toml
import sys
import refrom toml.encoder import TomlEncoder, _dump_str
from zipfile import ZipFile
from datetime import datetime
from enum import Enumclass DottedTomlEncoder(TomlEncoder):"""Dumps the toml into the dotted-key format"""def __init__(self, _dict=dict):super(DottedTomlEncoder, self).__init__(_dict)def dump_list(self, v):retval = "["# 1 line if its just 1 item; therefore no newlineif len(v) > 1:retval += "\n"for u in v:retval += " " + str(self.dump_value(u)) + ",\n"# 1 line if its just 1 item; remove newlineif len(v) <= 1:retval = retval.rstrip("\n")retval += "]"return retvaldef dump_sections(self, o, sup):retstr = ""pre = ""if sup:pre = sup + "."for section, value in o.items():section = str(section)qsection = sectionif not re.match(r'^[A-Za-z0-9_-]+$', section):qsection = _dump_str(section)if value is not None:if isinstance(value, dict):toadd, _ = self.dump_sections(value, pre + qsection)retstr += toadd# separte sectionsif not retstr.endswith('\n\n'):retstr += '\n'else:retstr += (pre + qsection + " = " +str(self.dump_value(value)) + '\n')return (retstr, self._dict())def parse_version(version):"""Converts a version str to tuple, so that versions can be compared"""return tuple(version.split('.'))def remove_whitelisted(list_of_handshakes, list_of_whitelisted_strings, valid_on_error=True):"""Removes a given list of whitelisted handshakes from a path list"""filtered = list()def normalize(name):"""Only allow alpha/nums"""return str.lower(''.join(c for c in name if c.isalnum()))for handshake in list_of_handshakes:try:normalized_handshake = normalize(os.path.basename(handshake).rstrip('.pcap'))for whitelist in list_of_whitelisted_strings:normalized_whitelist = normalize(whitelist)if normalized_whitelist in normalized_handshake:breakelse:filtered.append(handshake)except Exception:if valid_on_error:filtered.append(handshake)return filtereddef download_file(url, destination, chunk_size=128):import requestsresp = requests.get(url)resp.raise_for_status()with open(destination, 'wb') as fd:for chunk in resp.iter_content(chunk_size):fd.write(chunk)def unzip(file, destination, strip_dirs=0):os.makedirs(destination, exist_ok=True)with ZipFile(file, 'r') as zip:if strip_dirs:for info in zip.infolist():new_filename = info.filename.split('/', maxsplit=strip_dirs)[strip_dirs]if new_filename:info.filename = new_filenamezip.extract(info, destination)else:zip.extractall(destination)# https://stackoverflow.com/questions/823196/yaml-merge-in-python
def merge_config(user, default):if isinstance(user, dict) and isinstance(default, dict):for k, v in default.items():if k not in user:user[k] = velse:user[k] = merge_config(user[k], v)return userdef keys_to_str(data):if isinstance(data,list):converted_list = list()for item in data:if isinstance(item,list) or isinstance(item,dict):converted_list.append(keys_to_str(item))else:converted_list.append(item)return converted_listconverted_dict = dict()for key, value in data.items():if isinstance(value, list) or isinstance(value, dict):converted_dict[str(key)] = keys_to_str(value)else:converted_dict[str(key)] = valuereturn converted_dictdef save_config(config, target):with open(target, 'wt') as fp:fp.write(toml.dumps(config, encoder=DottedTomlEncoder()))return Truedef load_config(args):default_config_path = os.path.dirname(args.config)if not os.path.exists(default_config_path):os.makedirs(default_config_path)import pwnagotchiref_defaults_file = os.path.join(os.path.dirname(pwnagotchi.__file__), 'defaults.toml')ref_defaults_data = None# check for a config.yml file on /boot/for boot_conf in ['/boot/config.yml', '/boot/config.toml']:if os.path.exists(boot_conf):# logging not configured here yetprint("installing %s to %s ...", boot_conf, args.user_config)# https://stackoverflow.com/questions/42392600/oserror-errno-18-invalid-cross-device-linkshutil.move(boot_conf, args.user_config)break# check for an entire pwnagotchi folder on /boot/if os.path.isdir('/boot/pwnagotchi'):print("installing /boot/pwnagotchi to /etc/pwnagotchi ...")shutil.rmtree('/etc/pwnagotchi', ignore_errors=True)shutil.move('/boot/pwnagotchi', '/etc/')# if not config is found, copy the defaultsif not os.path.exists(args.config):print("copying %s to %s ..." % (ref_defaults_file, args.config))shutil.copy(ref_defaults_file, args.config)else:# check if the user messed with the defaultswith open(ref_defaults_file) as fp:ref_defaults_data = fp.read()with open(args.config) as fp:defaults_data = fp.read()if ref_defaults_data != defaults_data:print("!!! file in %s is different than release defaults, overwriting !!!" % args.config)shutil.copy(ref_defaults_file, args.config)# load the defaultswith open(args.config) as fp:config = toml.load(fp)# load the user configtry:user_config = None# migrateyaml_name = args.user_config.replace('.toml', '.yml')if not os.path.exists(args.user_config) and os.path.exists(yaml_name):# no toml found; convert yamllogging.info('Old yaml-config found. Converting to toml...')with open(args.user_config, 'w') as toml_file, open(yaml_name) as yaml_file:import yamluser_config = yaml.safe_load(yaml_file)# convert int/float keys to struser_config = keys_to_str(user_config)# convert to toml but use loaded yamltoml.dump(user_config, toml_file)elif os.path.exists(args.user_config):with open(args.user_config) as toml_file:user_config = toml.load(toml_file)if user_config:config = merge_config(user_config, config)except Exception as ex:logging.error("There was an error processing the configuration file:\n%s ",ex)sys.exit(1)# dropinsdropin = config['main']['confd']if dropin and os.path.isdir(dropin):dropin += '*.toml' if dropin.endswith('/') else '/*.toml' # only toml here; yaml is no morefor conf in glob.glob(dropin):with open(conf) as toml_file:additional_config = toml.load(toml_file)config = merge_config(additional_config, config)# the very first step is to normalize the display name so we don't need dozens of if/elif aroundif config['ui']['display']['type'] in ('inky', 'inkyphat'):config['ui']['display']['type'] = 'inky'elif config['ui']['display']['type'] in ('papirus', 'papi'):config['ui']['display']['type'] = 'papirus'elif config['ui']['display']['type'] in ('oledhat',):config['ui']['display']['type'] = 'oledhat'elif config['ui']['display']['type'] in ('ws_1', 'ws1', 'waveshare_1', 'waveshare1'):config['ui']['display']['type'] = 'waveshare_1'elif config['ui']['display']['type'] in ('ws_2', 'ws2', 'waveshare_2', 'waveshare2'):config['ui']['display']['type'] = 'waveshare_2'elif config['ui']['display']['type'] in ('ws_3', 'ws3', 'waveshare_3', 'waveshare3'):config['ui']['display']['type'] = 'waveshare_3'elif config['ui']['display']['type'] in ('ws_27inch', 'ws27inch', 'waveshare_27inch', 'waveshare27inch'):config['ui']['display']['type'] = 'waveshare27inch'elif config['ui']['display']['type'] in ('ws_29inch', 'ws29inch', 'waveshare_29inch', 'waveshare29inch'):config['ui']['display']['type'] = 'waveshare29inch'elif config['ui']['display']['type'] in ('lcdhat',):config['ui']['display']['type'] = 'lcdhat'elif config['ui']['display']['type'] in ('dfrobot_1', 'df1'):config['ui']['display']['type'] = 'dfrobot_1'elif config['ui']['display']['type'] in ('dfrobot_2', 'df2'):config['ui']['display']['type'] = 'dfrobot_2'elif config['ui']['display']['type'] in ('ws_154inch', 'ws154inch', 'waveshare_154inch', 'waveshare154inch'):config['ui']['display']['type'] = 'waveshare154inch'elif config['ui']['display']['type'] in ('waveshare144lcd', 'ws_144inch', 'ws144inch', 'waveshare_144inch', 'waveshare144inch'):config['ui']['display']['type'] = 'waveshare144lcd'elif config['ui']['display']['type'] in ('ws_213d', 'ws213d', 'waveshare_213d', 'waveshare213d'):config['ui']['display']['type'] = 'waveshare213d'elif config['ui']['display']['type'] in ('ws_213bc', 'ws213bc', 'waveshare_213bc', 'waveshare213bc'):config['ui']['display']['type'] = 'waveshare213bc'elif config['ui']['display']['type'] in ('spotpear24inch'):config['ui']['display']['type'] = 'spotpear24inch'else:print("unsupported display type %s" % config['ui']['display']['type'])sys.exit(1)return configdef secs_to_hhmmss(secs):mins, secs = divmod(secs, 60)hours, mins = divmod(mins, 60)return '%02d:%02d:%02d' % (hours, mins, secs)def total_unique_handshakes(path):expr = os.path.join(path, "*.pcap")return len(glob.glob(expr))def iface_channels(ifname):channels = []output = subprocess.getoutput("/sbin/iwlist %s freq" % ifname)for line in output.split("\n"):line = line.strip()if line.startswith("Channel "):channels.append(int(line.split()[1]))return channelsdef led(on=True):with open('/sys/class/leds/led0/brightness', 'w+t') as fp:fp.write("%d" % (0 if on is True else 1))def blink(times=1, delay=0.3):for _ in range(0, times):led(True)time.sleep(delay)led(False)time.sleep(delay)led(True)class WifiInfo(Enum):"""Fields you can extract from a pcap file"""BSSID = 0ESSID = 1ENCRYPTION = 2CHANNEL = 3RSSI = 4class FieldNotFoundError(Exception):passdef md5(fname):"""https://stackoverflow.com/questions/3431825/generating-an-md5-checksum-of-a-file"""import hashlibhash_md5 = hashlib.md5()with open(fname, "rb") as f:for chunk in iter(lambda: f.read(4096), b""):hash_md5.update(chunk)return hash_md5.hexdigest()def extract_from_pcap(path, fields):"""Search in pcap-file for specified informationpath: Path to pcap filefields: Array of fields that should be extractedIf a field is not found, FieldNotFoundError is raised"""results = dict()for field in fields:if not isinstance(field, WifiInfo):raise TypeError("Invalid field")subtypes = set()if field == WifiInfo.BSSID:from scapy.all import Dot11Beacon, Dot11ProbeResp, Dot11AssoReq, Dot11ReassoReq, Dot11, sniffsubtypes.add('beacon')bpf_filter = " or ".join([f"wlan type mgt subtype {subtype}" for subtype in subtypes])packets = sniff(offline=path, filter=bpf_filter)try:for packet in packets:if packet.haslayer(Dot11Beacon):if hasattr(packet[Dot11], 'addr3'):results[field] = packet[Dot11].addr3breakelse:  # magicraise FieldNotFoundError("Could not find field [BSSID]")except Exception:raise FieldNotFoundError("Could not find field [BSSID]")elif field == WifiInfo.ESSID:from scapy.all import Dot11Beacon, Dot11ReassoReq, Dot11AssoReq, Dot11, sniff, Dot11Eltsubtypes.add('beacon')subtypes.add('assoc-req')subtypes.add('reassoc-req')bpf_filter = " or ".join([f"wlan type mgt subtype {subtype}" for subtype in subtypes])packets = sniff(offline=path, filter=bpf_filter)try:for packet in packets:if packet.haslayer(Dot11Elt) and hasattr(packet[Dot11Elt], 'info'):results[field] = packet[Dot11Elt].info.decode('utf-8')breakelse:  # magicraise FieldNotFoundError("Could not find field [ESSID]")except Exception:raise FieldNotFoundError("Could not find field [ESSID]")elif field == WifiInfo.ENCRYPTION:from scapy.all import Dot11Beacon, sniffsubtypes.add('beacon')bpf_filter = " or ".join([f"wlan type mgt subtype {subtype}" for subtype in subtypes])packets = sniff(offline=path, filter=bpf_filter)try:for packet in packets:if packet.haslayer(Dot11Beacon) and hasattr(packet[Dot11Beacon], 'network_stats'):stats = packet[Dot11Beacon].network_stats()if 'crypto' in stats:results[field] = stats['crypto']  # set with encryption typesbreakelse:  # magicraise FieldNotFoundError("Could not find field [ENCRYPTION]")except Exception:raise FieldNotFoundError("Could not find field [ENCRYPTION]")elif field == WifiInfo.CHANNEL:from scapy.all import sniff, RadioTapfrom pwnagotchi.mesh.wifi import freq_to_channelpackets = sniff(offline=path, count=1)try:results[field] = freq_to_channel(packets[0][RadioTap].ChannelFrequency)except Exception:raise FieldNotFoundError("Could not find field [CHANNEL]")elif field == WifiInfo.RSSI:from scapy.all import sniff, RadioTapfrom pwnagotchi.mesh.wifi import freq_to_channelpackets = sniff(offline=path, count=1)try:results[field] = packets[0][RadioTap].dBm_AntSignalexcept Exception:raise FieldNotFoundError("Could not find field [RSSI]")return resultsclass StatusFile(object):def __init__(self, path, data_format='raw'):self._path = pathself._updated = Noneself._format = data_formatself.data = Noneif os.path.exists(path):self._updated = datetime.fromtimestamp(os.path.getmtime(path))with open(path) as fp:if data_format == 'json':self.data = json.load(fp)else:self.data = fp.read()def data_field_or(self, name, default=""):if self.data is not None and name in self.data:return self.data[name]return defaultdef newer_then_minutes(self, minutes):return self._updated is not None and ((datetime.now() - self._updated).seconds / 60) < minutesdef newer_then_hours(self, hours):return self._updated is not None and ((datetime.now() - self._updated).seconds / (60 * 60)) < hoursdef newer_then_days(self, days):return self._updated is not None and (datetime.now() - self._updated).days < daysdef update(self, data=None):from pwnagotchi.fs import ensure_writeself._updated = datetime.now()self.data = datawith ensure_write(self._path, 'w') as fp:if data is None:fp.write(str(self._updated))elif self._format == 'json':json.dump(self.data, fp)else:fp.write(data)
  1. 将修改好的工程中pwnagotchi文件夹修改为pwnagotchi_ss(防止导入boot后被自动删除),然后将烧录好的SD卡插入电脑中。

  2. pwnagotchi_ss导入到boot系统目录下,之后弹出SD卡。

  3. 用数据线连接树莓派和电脑,打开网络,按照以下数值配置,注意选择手动。

  1. 打开终端,输入ssh pi@10.0.0.2,密码是:raspberry
  2. 进入后输入sudo rm -rf /usr/local/lib/python3.7/dist-packages/pwnagotchi
  3. 输入cd /boot,然后输入sudo mv pwnagotchi_ss /usr/local/lib/python3.7/dist-packages
  4. 输入cd /usr/local/lib/python3.7/dist-packages
  5. 输入mv pwnagotchi_ss pwnagotchi
  6. 输入cd /boot
  7. 输入vim config.toml
  8. 点击键盘的i,进入编辑模式,输入
main.name = "pwnagotchi"
main.lang = "en"
main.whitelist = [
"348wifi","348wifi_5G"
]main.plugins.grid.enabled = true
main.plugins.grid.report = true
main.plugins.grid.exclude = ["YourHomeNetworkHere"
]ui.display.enabled = true
ui.display.type = "waveshare_3"
ui.display.color = "black"

main.whitelist是放Wi-Fi白名单的,防止自己被攻击

  1. 按键盘的ESC,然后输入:q!回车保存并退出
  2. 输入systemctl reboot,等待树莓派重新启动(1~2min)

效果展示

关于作者

喜欢动手做一些有意思的东西(虽然是个手残党…)

喜欢尝试,不怕丢脸

大家好,我是孙成,海南大学2020级智能科学与技术专业本科学生

博客地址:CSDNzhu主页

代码仓库:Gitee

Email:ac20311@163.com

WeChat: ac20311

参考资料

https://github.com/evilsocket/pwnagotchi

https://github.com/evilsocket/pwnagotchi/pull/1069

Pwnagotchi_waveshare_V3适配(海南大学)相关推荐

  1. 手动将jar包导入pom依赖,让jar包适配本地maven项目

    前言: Oracle对maven很久没有更新依赖,虽然19年更新了一版,但pom引入一直有错误. 我用的是oralce 12的依赖,虽然有jar包,但是依赖和pom没有适配,项目打包的时候还要去中央仓 ...

  2. ANSYS2020R2 Workbench汉化及高分屏适配

    操作系统:Windows10 软件版本:ANSYS2020R2 Workbench 硬件参数:27英寸4K屏幕 汉化 打开Workbench 2020 R2→Tools→Options→Appeara ...

  3. TVM适配NN编译Compiler缺陷

    TVM适配NN编译Compiler缺陷 内容纲要 前言 TVM针对VTA的编译流程 i. 自定义VTA架构:TVM的缺陷与性能瓶颈 TVM缺陷与瓶颈 i. 缺陷一:SRAM配置灵活性差 ii. 缺陷二 ...

  4. android 适配

    sw 适配 原理就不讲了,说说怎样使用方法 1  下载一个ScreenMatch的工具 File -setting -plugins  --搜索screenmatch --下载 下载之后再res -v ...

  5. 微信小程序填坑之路(三):布局适配方案(rpx、px、vw、vh)

    因为小程序是以微信为平台运行的,可以同时运行在android与ios的设备上,所以不可避免的会遇到布局适配问题,特别是在iphone5上,因为屏幕尺寸小的缘故,也是适配问题最多的机型,下面就简单介绍几 ...

  6. android 高通平台有前途吗,华为鸿蒙计划要适配高通平台了,可以告别安卓搭载鸿蒙OS了?...

    鸿蒙走出这一步是可以想象到的,看来华为打造这个系统希望的结果是万物皆可盘呀,所以一开始就提出了开源,也就意味着这次是高通,下次就可以是联发科,甚至更多的手机品牌也完全就可以搭载!早期我们一直在说国产手 ...

  7. 荣耀手机现在是鸿蒙,荣耀适配鸿蒙最新消息出现,华为不会让大家失望的

    荣耀适配鸿蒙最新消息出现,华为不会让大家失望的 2021-05-11 20:55:23 0点赞 0收藏 0评论 从现在的信息看华为的机型下个月就要开始大面积适配鸿蒙2.0操作系统了,因此很多人想知道荣 ...

  8. Android 10 vivo,更快更安全,vivo产品经理宣布:iQOO将首批适配Android 10正式版

    今天,谷歌一年一度的I/O 2019开发者大会如期举行.此次大会除了多款重磅新产品,新一代系统Android Q(10)beta版本的更多新功能也被揭开.除了支持5G网络.针对折叠屏这样的大屏设备进行 ...

  9. android o 全机型推送,氢OS(Android O)官方更新推送 一加两款机型完成适配

    原标题:氢OS(Android O)官方更新推送 一加两款机型完成适配 在如今智能手机硬件"横行"的今天,软件系统更新关注度下降,当然苹果的iOS系统除外.在今年8月谷歌正式发布了 ...

  10. 关于移动端rem适配

    var num = 1 / window.devicePixelRatio; var fontSize = document.documentElement.clientWidth / 10; doc ...

最新文章

  1. visual studio 汇编 创建 项目
  2. CentOS 7.6 搭建Gitlab教程
  3. c++学习笔记之基础篇
  4. day4作业(基本运算流程if for)
  5. (转载)程序员文史综合题目一(附答案)
  6. python安装哪个版本好啊_Python 的版本选择与安装细节
  7. 高通QCA9563详细资料全集-datasheet-原理图-PCB-HDK等资料免费下载
  8. Android 热补丁动态修复
  9. 使用lxml爬取豆瓣电影排行榜
  10. echart-pie
  11. 强劲大小核结构 三星将推八核处理器
  12. 慎投:这两本期刊被剔除SCI/SSCI, 11月WOS数据库已更新~
  13. 并行优化:OpenMP
  14. 计算机系统中软件的分类及各自的定义,计算机软件的定义和分类
  15. pythoncookie自动登录_Python使用cookie 免密登录了解一下
  16. SAP BAPI BAPI_PO_CREATE1创建采购订单
  17. AE导出JSON数据用CSS做前端交互---kalrry
  18. 结构体 5.火星人足球赛
  19. 【软件工程1916|W(福州大学)_助教博客】团队答辩助教问题记录
  20. 飞蛾与蝙蝠的玩命游戏(自然篇)

热门文章

  1. 自学WEB开发第一天:工欲善其事,必先利其器。基于VB语言,纠结于VS和VS code之间
  2. 英国政府收购SpaceX竞争对手背后,蕴藏着多大的野心?
  3. 在抖音找罗永浩干掉辣条
  4. table表格及属性
  5. 企业内IT部/信息部发展阶段和趋势(第一阶段)
  6. 笔记本电脑键盘的禁用与恢复【亲测有效】
  7. 解读微信第三方平台-代小程序开发
  8. 浅析Relaxed Ordering对PCIe系统稳定性的影响
  9. 恩尼格玛模拟器_用C语言编的恩格尼码模拟器
  10. 互联网项目经理的职业规划