diff --git a/FAQ.md b/FAQ.md index d422fc06..2ddcaa82 100644 --- a/FAQ.md +++ b/FAQ.md @@ -102,3 +102,10 @@ a serial sync with some hosts. - Appears as: `zbid` lists the device as 'v2' - Cause: expected behavior, as from the software side only v1 is different than v2-v4, and thus it doesn't see a difference - Fix: N/A + +#### Cannot identify hardware +- In case of problems identifying the hardware try to run the following command (present in [GoodFET](https://github.com/travisgoodspeed/goodfet/blob/master/firmware/apps/radios/ccspi.c)) before any killerbee command + +- `~/goodfet/client$ sudo ./goodfet.monitor listapps full` + + diff --git a/README.md b/README.md index 88b6f7a3..03a5ae1c 100644 --- a/README.md +++ b/README.md @@ -61,8 +61,13 @@ The install will detect and prompt you for what is needed. On Ubuntu systems, you can install the needed dependencies with the following commands: ``` -# apt-get install python-gtk2 python-cairo python-usb python-crypto python-serial python-dev libgcrypt-dev -# python3 setup.py install +# apt-get install python-usb python-crypto python-serial python-dev libgcrypt-dev +``` + +On Mac OS, you can install the dependencies with the following commands +``` +# brew install libusb libgcrypt +# pip3 install pyusb scapy ``` The python-dev and libgcrypt are required for the Scapy Extension Patch. @@ -82,7 +87,7 @@ KillerBee uses the standard Python 'setup.py' installation file, once dependenci Install KillerBee with the following command: ``` -# python setup.py install +# python3 setup.py install ``` DIRECTORIES diff --git a/killerbee/GoodFET.py b/killerbee/GoodFET.py index 65326682..849935cd 100644 --- a/killerbee/GoodFET.py +++ b/killerbee/GoodFET.py @@ -305,6 +305,12 @@ def bslResetZ1(self, invokeBSL=0): By now only BSL mode is accessed. @type invokeBSL: Integer @param invokeBSL: 1 for a complete sequence, or 0 to only access RST/NMI pin + Applies BSL entry sequence on RST/NMI and TEST/VPP pins + Parameters: + invokeBSL = 1: complete sequence + invokeBSL = 0: only RST/NMI pin accessed + + By now only BSL mode is accessed ''' #if DEBUG > 1: sys.stderr.write("* bslReset(invokeBSL=%s)\n" % invokeBSL) if invokeBSL: @@ -372,7 +378,6 @@ def picROMfastclock(self, masterout): return self.serialport.getCTS() def telosBReset(self, invokeBSL=0): - '''Helper function for support of the TelosB platform.''' # "BSL entry sequence at dedicated JTAG pins" # rst !s0: 0 0 0 0 1 1 # tck !s1: 1 0 1 0 0 1 @@ -615,7 +620,7 @@ def setBaud(self,baud): self.serialport.write(bytearray([0x00, 0x80, 1, baud])) print("Changed host baud.") - self.serialport.baudrate = rates[baud]; + self.serialport.setBaudrate(rates[baud]); time.sleep(1); self.serialport.flushInput() self.serialport.flushOutput() @@ -627,7 +632,7 @@ def readbyte(self): def findbaud(self): for r in self.baudrates: print("\nTrying %i" % r); - self.serialport.baudrate = r; + self.serialport.setBaudrate(r); #time.sleep(1); self.serialport.flushInput() self.serialport.flushOutput() diff --git a/killerbee/GoodFETCCSPI.py b/killerbee/GoodFETCCSPI.py index 1e849528..e21d3ce2 100644 --- a/killerbee/GoodFETCCSPI.py +++ b/killerbee/GoodFETCCSPI.py @@ -20,7 +20,8 @@ def setup(self): #Set up the radio for ZigBee self.strobe(0x01); #SXOSCON - self.strobe(0x02); #SCAL + self.strobe(0x01); #SXOSCON ##YL: idk why but needed to enable ram poking + self.strobe(0x02); #SCAL self.poke(0x11, 0x0AC2 & (~0x0800)); #MDMCTRL0, promiscuous self.poke(0x12, 0x0500); #MDMCTRL1 self.poke(0x1C, 0x007F); #IOCFG0 @@ -77,12 +78,11 @@ def peek(self,reg,bytes=2): bytes=2; self.writecmd(self.CCSPIAPP,0x02,len(data),data); - try: - toret = (self.data[2] + (self.data[1] << 8)) - except Exception as e: - print("issue in peeking for a register") - print(e) - toret = self.data[1] << 8 + + toret=( + self.data[2]+(self.data[1]<<8) + ); + return toret; def poke(self,reg,val,bytes=2): """Write a CCSPI Register.""" @@ -199,6 +199,20 @@ def pokeram(self,adr,data): return; lastpacket=list(range(0,0xff)); + + def RF_txrxpacket(self,packet,timeout=1): + data="\0"; + self.data=data; + packet = [timeout&0xff, timeout>>8] + packet + + self.writecmd(self.CCSPIAPP,0x86,len(packet),packet); + buffer=self.data; + self.lastpacket=buffer; + if(len(buffer)==0): + return None; + + return buffer; + def RF_rxpacket(self): """Get a packet from the radio. Returns None if none is waiting.""" diff --git a/killerbee/GoodFETatmel128.py b/killerbee/GoodFETatmel128.py index bcc0bb53..bd13e02c 100644 --- a/killerbee/GoodFETatmel128.py +++ b/killerbee/GoodFETatmel128.py @@ -95,7 +95,7 @@ def pyserInit(self, port, timeout, attemptlimit): break if self.verbose: print("Connected after %02i attempts." % attempts); - self.serialport.timeout = 12; + self.serialport.setTimeout(12); def serClose(self): self.connected = 0 diff --git a/killerbee/__init__.py b/killerbee/__init__.py index cacbd51c..2c95c69e 100644 --- a/killerbee/__init__.py +++ b/killerbee/__init__.py @@ -25,22 +25,6 @@ from .config import * #to get DEV_ENABLE_* variables # Utility Functions -def getKillerBee(channel: int, page: int=0) -> KillerBee: - ''' - Returns an instance of a KillerBee device, setup on the given channel/page. - Error handling for KillerBee creation and setting of the channel is wrapped - and will raise an Exception(). - @return: A KillerBee instance initialized to the given channel/page. - ''' - kb: KillerBee = KillerBee() - if kb is None: - raise Exception("Failed to create a KillerBee instance.") - try: - kb.set_channel(channel, page) - except Exception as e: - raise Exception('Error: Failed to set channel to %d/%d' % (channel, page), e) - return kb - def show_dev(vendor: str=None, product: str=None, gps: str=None, include: str=None) -> None: ''' A basic function to output the device listing. @@ -158,10 +142,10 @@ def __init__(self, device: str=None, datasource: str=None, gps: str=None) -> Non self.driver = TELOSB(self.dev) elif gfccspi and subtype == 1: from .dev_apimote import APIMOTE - self.driver = APIMOTE(self.dev, revision=1) + self.driver = APIMOTE(self.dev) elif gfccspi and subtype == 2: from .dev_apimote import APIMOTE - self.driver = APIMOTE(self.dev, revision=2) + self.driver = APIMOTE(self.dev) else: raise KBInterfaceError("KillerBee doesn't know how to interact with serial device at '%s'." % self.dev) # Otherwise unrecognized device string type was provided: @@ -412,7 +396,7 @@ def inject(self, packet: str, channel: Optional[int]=None, count: int=1, delay: return self.driver.inject(packet, channel, count, delay, page) - def pnext(self, timeout: int=100) -> Optional[Dict[str, Union[int, bool, str]]]: + def pnext(self, timeout: int=100) -> Optional[Dict[Union[int, str], Any]]: ''' Returns packet data as a string, else None. @type timeout: Integer diff --git a/killerbee/daintree.py b/killerbee/daintree.py index 55ddc87c..5a9a5b1d 100644 --- a/killerbee/daintree.py +++ b/killerbee/daintree.py @@ -104,8 +104,8 @@ def pnext(self) -> Optional[list[Any]]: try: while(1): - record: List[bytes] = self._fh.readline().split(' ') - if record[0][0] == "#": + record: list[bytes] = self._fh.readline().split(b' ') + if record[0] == b"#": continue else: break diff --git a/killerbee/dev_apimote.py b/killerbee/dev_apimote.py index 889658a3..7abaafc1 100755 --- a/killerbee/dev_apimote.py +++ b/killerbee/dev_apimote.py @@ -22,34 +22,29 @@ from .kbutils import KBCapabilities, makeFCS from .GoodFETCCSPI import GoodFETCCSPI -# Default revision of the ApiMote. This is liable to change at any time -# as new ApiMote versions are released. Automatic recognition would be nice. -DEFAULT_REVISION: int = 2 - CC2420_REG_SYNC: int = 0x14 class APIMOTE: - def __init__(self, dev: str, revision: int=DEFAULT_REVISION) -> None: + + def __init__(self, dev: str) -> None: ''' Instantiates the KillerBee class for the ApiMote platform running GoodFET firmware. @type dev: String @param dev: Serial device identifier (ex /dev/ttyUSB0) - @type revision: Integer - @param revision: The revision number for the ApiMote, which is used by - the called GoodFET libraries to properly communicate with - and configure the hardware. @return: None @rtype: None ''' + self.packet_queue: Optional[bytes] = None + self.packet_queue_rssi: Optional[int] = None + self._channel: Optional[int] = None self._page: int = 0 self.handle: Optional[Any] = None self.dev: str = dev - self.__revision_num: int = revision # Set enviroment variables for GoodFET code to use - os.environ["platform"] = "apimote{}".format(self.__revision_num) - os.environ["board"] = "apimote{}".format(self.__revision_num) + os.environ["platform"] = "apimote2" + os.environ["board"] = "apimote2" self.handle = GoodFETCCSPI() self.handle.serInit(port=self.dev) self.handle.setup() @@ -94,7 +89,7 @@ def get_dev_info(self) -> list[Union[str, Any]]: @rtype: List @return: List of 3 strings identifying device. ''' - return [self.dev, "GoodFET Apimote v{}".format(self.__revision_num), ""] + return [self.dev, "GoodFET Apimote v2", ""] # KillerBee expects the driver to implement this function def sniffer_on(self, channel: Optional[int]=None, page: int=0) -> None: @@ -205,26 +200,54 @@ def pnext(self, timeout: int=100) -> Any: if self.__stream_open == False: self.sniffer_on() + + if self.packet_queue is None: + packet: Optional[Any] = None + start = datetime.utcnow() + + while (packet is None and + (start + timedelta(microseconds=timeout) > datetime.utcnow())): + packet = self.handle.RF_rxpacket() + rssi = self.handle.RF_getrssi() #TODO calibrate + + if packet is None: + return None + + if ord(packet[0])+1 < len(packet): + self.packet_queue = packet[ord(packet[0])+1+1:] + self.packet_queue_rssi = rssi + + frame = packet[1:ord(packet[0])+1] + else: + packet = self.packet_queue + self.packet_queue = None + frame = packet + + if self.packet_queue_rssi is None: + rssi = None + else: + rssi = self.packet_queue_rssi - packet: Optional[bytes] = None - start: datetime = datetime.utcnow() - - while (packet is None and (start + timedelta(microseconds=timeout) > datetime.utcnow())): - packet = self.handle.RF_rxpacket() - rssi: int = self.handle.RF_getrssi() #TODO calibrate - - if packet is None: - return None - - frame: bytes = packet[1:] validcrc: bool = False if frame[-2:] == makeFCS(frame[:-2]): validcrc = True #Return in a nicer dictionary format, so we don't have to reference by number indicies. #Note that 0,1,2 indicies inserted twice for backwards compatibility. - result: Dict[Union[int, str], Any] = {0:frame, 1:validcrc, 2:rssi, 'bytes':frame, 'validcrc':validcrc, 'rssi':rssi, 'location':None} - result['dbm'] = rssi - 45 #TODO tune specifically to the Apimote platform (does ext antenna need to different?) + result: Dict[Union[int, str], Any] = { + 0: frame, + 1: validcrc, + 2: rssi, + 'bytes': frame, + 'validcrc': validcrc, + 'rssi': rssi, + 'location': None + } + result['datetime'] = datetime.utcnow() + if rssi is None: + result['dbm'] = None + else: + result['dbm'] = rssi - 45 #TODO tune specifically to the Apimote platform (does ext antenna need to different?) return result def ping(self, da: Any, panid: Any, sa: Any, channel: Optional[int]=None, page: int=0) -> None: diff --git a/killerbee/kbutils.py b/killerbee/kbutils.py index d55912c8..11191e51 100644 --- a/killerbee/kbutils.py +++ b/killerbee/kbutils.py @@ -363,7 +363,7 @@ def isgoodfetccspi(serialdev: str) -> Tuple[bool, Optional[int]]: os.environ["board"] = "apimote2" #set enviroment variable for GoodFET code to use gf = GoodFETCCSPI() try: - gf.serInit(port=serialdev, attemptlimit=2) + gf.serInit(port=serialdev, attemptlimit=30) #gf.setup() except serial.serialutil.SerialException as e: raise KBInterfaceError("Serial issue in kbutils.isgoodfetccspi: %s." % e) diff --git a/killerbee/scapy_extensions.py b/killerbee/scapy_extensions.py index b62ecc84..af5039f4 100644 --- a/killerbee/scapy_extensions.py +++ b/killerbee/scapy_extensions.py @@ -9,18 +9,17 @@ setattr(conf, 'killerbee_page', DEFAULT_KB_PAGE) setattr(conf, 'killerbee_device', DEFAULT_KB_DEVICE) setattr(conf, 'killerbee_nkey', None) +from scapy import plist from scapy.base_classes import SetGen # type: ignore from scapy.packet import Gen, Raw # type: ignore from scapy.all import Dot15d4 # type: ignore from scapy.all import Dot15d4FCS # type: ignore -from scapy.all import plist # type: ignore from scapy.all import PcapWriter # type: ignore from scapy.all import ZigbeeSecurityHeader # type: ignore from scapy.all import ZigbeeNWK # type: ignore from scapy.all import ZigbeeAppDataPayload # type: ignore from scapy.all import ZigbeeNWKCommandPayload # type: ignore from scapy.all import Packet # type: ignore -from scapy.all import Layer # type: ignore # This line will allow KillerBee's pcap reader to overwrite scapy's reader that is imported on the # above line, per suggestion from cutaway at https://code.google.com/p/killerbee/issues/detail?id=28: @@ -593,7 +592,7 @@ def kbgetmiclen(seclevel: int) -> int: return lengths[seclevel] @conf.commands.register -def kbgetpanid(packet: Gen) -> Tuple[Optional[bytes], Optional[Layer]]: +def kbgetpanid(packet: Gen) -> Tuple[Optional[bytes], Optional[Any]]: """Returns the pan id and which layer it was found in or None, None""" for layer in packet.layers(): for field in packet[layer].fields: diff --git a/tools/zbfind b/scripts/zbfind similarity index 100% rename from tools/zbfind rename to scripts/zbfind diff --git a/setup.py b/setup.py index 87e4f010..6e895821 100644 --- a/setup.py +++ b/setup.py @@ -14,19 +14,6 @@ apt_get_pkgs = [] pip_pkgs = [] -# We have made gtk, cairo, scapy-com into optional libraries -try: - import gtk -except ImportError: - warn.append("gtk") - apt_get_pkgs.append("python-gtk2") - -try: - import cairo -except ImportError: - warn.append("cairo") - apt_get_pkgs.append("python-cairo") - # Ensure we have either pyUSB 0.x or pyUSB 1.x, but we now # prefer pyUSB 1.x moving forward. Support for 0.x may be deprecated. try: @@ -80,7 +67,7 @@ ) setup(name = 'killerbee', - version = '3.0.0-beta.0', + version = '3.0.0-beta.1', description = 'ZigBee and IEEE 802.15.4 Attack Framework and Tools', author = 'Joshua Wright, Ryan Speers', author_email = 'jwright@willhackforsushi.com, ryan@riverloopsecurity.com', @@ -88,7 +75,7 @@ packages = ['killerbee'], scripts = ['tools/zbdump', 'tools/zbgoodfind', 'tools/zbid', 'tools/zbreplay', 'tools/zbconvert', 'tools/zbdsniff', 'tools/zbstumbler', 'tools/zbassocflood', - 'tools/zbfind', 'tools/zbscapy', 'tools/zbwireshark', 'tools/zbkey', + 'tools/zbscapy', 'tools/zbwireshark', 'tools/zbkey', 'tools/zbwardrive', 'tools/zbopenear', 'tools/zbfakebeacon', 'tools/zborphannotify', 'tools/zbpanidconflictflood', 'tools/zbrealign', 'tools/zbcat', 'tools/zbjammer', 'tools/kbbootloader'], diff --git a/tests/fixtures/test_dt_dump.dcf b/tests/fixtures/test_dt_dump.dcf deleted file mode 100644 index 65b77064..00000000 --- a/tests/fixtures/test_dt_dump.dcf +++ /dev/null @@ -1,2 +0,0 @@ -#Format=4 -# SNA v3.0.0.7 SUS:2021615 ACT:067341 diff --git a/tests/test_apimote_driver.py b/tests/test_apimote_driver.py index bc5e65d7..1ff61452 100644 --- a/tests/test_apimote_driver.py +++ b/tests/test_apimote_driver.py @@ -84,7 +84,7 @@ def test_sniffer_on_off(self): driver.close() self.assertTrue(True) - def test_set_channe(self): + def test_set_channel(self): driver=APIMOTE(os.environ['APIMOTE_DEVSTRING']) self.assertRaises(Exception, driver.set_channel, 1) diff --git a/tests/test_killerbee_core.py b/tests/test_killerbee_core.py index afff53de..322f3966 100644 --- a/tests/test_killerbee_core.py +++ b/tests/test_killerbee_core.py @@ -7,11 +7,6 @@ from killerbee.dev_apimote import APIMOTE class TestKillerbeeCore(unittest.TestCase): - def test_getkillerbee(self): - kb = getKillerBee(15) - self.assertIsNotNone(kb) - self.assertEqual(15, kb.channel) - def test_show_dev(self): show_dev() self.assertTrue(True)