import os
import serial
import sys
import time
from ..core import FacedancerApp
from ..backends.MAXUSBApp import MAXUSBApp
from ..logging import log
[docs]
class GoodfetMaxUSBApp(MAXUSBApp):
app_name = "MAXUSB"
app_num = 0x40
[docs]
@classmethod
def appropriate_for_environment(cls, backend_name):
"""
Determines if the current environment seems appropriate
for using the GoodFET::MaxUSB backend.
"""
# Check: Only proceed if the user has specified "goodfet" as
# the backend name, we don't want a false positive because the
# user has another USB serial device connected.
if backend_name is None or backend_name != "goodfet":
return False
# See if there's a connected GoodFET.
try:
gf = GoodFETSerialPort()
gf.close()
return True
except ImportError:
log.info("Skipping GoodFET-based devices, as pyserial isn't installed.")
return False
except:
return False
def __init__(self, device=None, verbose=0, quirks=None):
if device is None:
serial = GoodFETSerialPort()
device = Facedancer(serial, verbose=verbose)
FacedancerApp.__init__(self, device, verbose)
self.connected_device = None
self.enable()
if verbose > 0:
rev = self.read_register(self.reg_revision)
print(self.app_name, "revision", rev)
# set duplex and negative INT level (from GoodFEDMAXUSB.py)
self.write_register(self.reg_pin_control,
self.full_duplex | self.interrupt_level)
[docs]
def init_commands(self):
self.read_register_cmd = FacedancerCommand(self.app_num, 0x00, b'')
self.write_register_cmd = FacedancerCommand(self.app_num, 0x00, b'')
self.enable_app_cmd = FacedancerCommand(self.app_num, 0x10, b'')
self.ack_cmd = FacedancerCommand(self.app_num, 0x00, b'\x01')
[docs]
def enable(self):
for i in range(3):
self.device.writecmd(self.enable_app_cmd)
self.device.readcmd()
if self.verbose > 0:
print(self.app_name, "enabled")
[docs]
def ack_status_stage(self, blocking=False):
if self.verbose > 5:
print(self.app_name, "sending ack!")
self.device.writecmd(self.ack_cmd)
self.device.readcmd()
[docs]
def read_register(self, reg_num, ack=False):
if self.verbose > 1:
print(self.app_name, "reading register 0x%02x" % reg_num)
self.read_register_cmd.data = bytearray([ reg_num << 3, 0 ])
if ack:
self.read_register_cmd.data[0] |= 1
self.device.writecmd(self.read_register_cmd)
resp = self.device.readcmd()
if self.verbose > 2:
print(self.app_name, "read register 0x%02x has value 0x%02x" %
(reg_num, resp.data[1]))
return resp.data[1]
[docs]
def write_register(self, reg_num, value, ack=False):
if self.verbose > 2:
print(self.app_name, "writing register 0x%02x with value 0x%02x" %
(reg_num, value))
self.write_register_cmd.data = bytearray([ (reg_num << 3) | 2, value ])
if ack:
self.write_register_cmd.data[0] |= 1
self.device.writecmd(self.write_register_cmd)
self.device.readcmd()
[docs]
def read_bytes(self, reg, n):
if self.verbose > 2:
print(self.app_name, "reading", n, "bytes from register", reg)
data = bytes([ (reg << 3) ] + ([0] * n))
cmd = FacedancerCommand(self.app_num, 0x00, data)
self.device.writecmd(cmd)
resp = self.device.readcmd()
if self.verbose > 3:
print(self.app_name, "read", len(resp.data) - 1, "bytes from register", reg)
return resp.data[1:]
[docs]
def write_bytes(self, reg, data):
data = bytes([ (reg << 3) | 3 ]) + data
cmd = FacedancerCommand(self.app_num, 0x00, data)
self.device.writecmd(cmd)
self.device.readcmd() # null response
if self.verbose > 3:
print(self.app_name, "wrote", len(data) - 1, "bytes to register", reg)
[docs]
class Facedancer:
def __init__(self, serialport, verbose=0):
self.serialport = serialport
self.verbose = verbose
self.reset()
self.monitor_app = GoodFETMonitorApp(self, verbose=self.verbose)
self.monitor_app.announce_connected()
[docs]
def halt(self):
self.serialport.setRTS(1)
self.serialport.setDTR(1)
[docs]
def reset(self):
if self.verbose > 1:
print("Facedancer resetting...")
self.halt()
self.serialport.setDTR(0)
c = self.readcmd()
if self.verbose > 0:
print("Facedancer reset")
[docs]
def read(self, n):
"""Read raw bytes."""
b = self.serialport.read(n)
if self.verbose > 3:
print("Facedancer received", len(b), "bytes;",
self.serialport.inWaiting(), "bytes remaining")
if self.verbose > 2:
print("Facedancer Rx:", MAXUSBApp.bytes_as_hex(b))
return b
[docs]
def readcmd(self):
"""Read a single command."""
b = self.read(4)
app = b[0]
verb = b[1]
n = b[2] + (b[3] << 8)
if n > 0:
data = self.read(n)
else:
data = b''
if len(data) != n:
raise ValueError('Facedancer expected ' + str(n) \
+ ' bytes but received only ' + str(len(data)))
cmd = FacedancerCommand(app, verb, data)
if self.verbose > 1:
print("Facedancer Rx command:", cmd)
return cmd
[docs]
def write(self, b):
"""Write raw bytes."""
if self.verbose > 2:
print("Facedancer Tx:", MAXUSBApp.bytes_as_hex(b))
self.serialport.write(b)
[docs]
def writecmd(self, c):
"""Write a single command."""
self.write(c.as_bytestring())
if self.verbose > 1:
print("Facedancer Tx command:", c)
[docs]
class FacedancerCommand:
def __init__(self, app=None, verb=None, data=None):
self.app = app
self.verb = verb
self.data = data
def __str__(self):
s = "app 0x%02x, verb 0x%02x, len %d" % (self.app, self.verb,
len(self.data))
if len(self.data) > 0:
s += ", data " + MAXUSBApp.bytes_as_hex(self.data)
return s
[docs]
def long_string(self):
s = "app: " + str(self.app) + "\n" \
+ "verb: " + str(self.verb) + "\n" \
+ "len: " + str(len(self.data))
if len(self.data) > 0:
try:
s += "\n" + self.data.decode("utf-8")
except UnicodeDecodeError:
s += "\n" + MAXUSBApp.bytes_as_hex(self.data)
return s
[docs]
def as_bytestring(self):
n = len(self.data)
b = bytearray(n + 4)
b[0] = self.app
b[1] = self.verb
b[2] = n & 0xff
b[3] = n >> 8
b[4:] = self.data
return b
[docs]
class GoodFETMonitorApp(FacedancerApp):
app_name = "GoodFET monitor"
app_num = 0x00
[docs]
def read_byte(self, addr):
d = [ addr & 0xff, addr >> 8 ]
cmd = FacedancerCommand(self.app_num, 2, d)
self.device.writecmd(cmd)
resp = self.device.readcmd()
return resp.data[0]
[docs]
def get_infostring(self):
return bytes([ self.read_byte(0xff0), self.read_byte(0xff1) ])
[docs]
def get_clocking(self):
return bytes([ self.read_byte(0x57), self.read_byte(0x56) ])
[docs]
def print_info(self):
infostring = self.get_infostring()
clocking = self.get_clocking()
print("MCU", MAXUSBApp.bytes_as_hex(infostring, delim=""))
print("clocked at", MAXUSBApp.bytes_as_hex(clocking, delim=""))
[docs]
def list_apps(self):
cmd = FacedancerCommand(self.app_num, 0x82, b'\x01')
self.device.writecmd(cmd)
resp = self.device.readcmd()
print("build date:", resp.data.decode("utf-8"))
print("firmware apps:")
while True:
resp = self.device.readcmd()
if len(resp.data) == 0:
break
print(resp.data.decode("utf-8"))
[docs]
def echo(self, s):
b = bytes(s, encoding="utf-8")
cmd = FacedancerCommand(self.app_num, 0x81, b)
self.device.writecmd(cmd)
resp = self.device.readcmd()
return resp.data == b
[docs]
def announce_connected(self):
cmd = FacedancerCommand(self.app_num, 0xb1, b'')
self.device.writecmd(cmd)
resp = self.device.readcmd()
[docs]
def GoodFETSerialPort(**kwargs):
"Return a Serial port using default values possibly overriden by caller"
port = os.environ.get('GOODFET') or "/dev/ttyUSB0"
args = dict(port=port, baudrate=115200,
parity=serial.PARITY_NONE, timeout=2)
args.update(kwargs)
return serial.Serial(**args)