From c7a63ca28c31659608db0846d586429eb56c7eed Mon Sep 17 00:00:00 2001 From: Taylor Date: Wed, 23 Jun 2021 17:40:03 -0400 Subject: [PATCH 1/6] implemented goodfet and apimote changes, added mac install instructions, moved zbfind, removed gtk + cairo dependencies --- FAQ.md | 7 +++++++ README.md | 11 ++++++++--- killerbee/GoodFET.py | 11 ++++++++--- killerbee/GoodFETCCSPI.py | 29 ++++++++++++++++++++++------- killerbee/GoodFETatmel128.py | 2 +- killerbee/dev_apimote.py | 31 +++++++++++++++++++++++-------- killerbee/kbutils.py | 2 +- {tools => scripts}/zbfind | 0 setup.py | 15 +-------------- 9 files changed, 71 insertions(+), 37 deletions(-) rename {tools => scripts}/zbfind (100%) 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 a22f6de6..f2c826c6 100644 --- a/killerbee/GoodFET.py +++ b/killerbee/GoodFET.py @@ -300,6 +300,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: @@ -367,7 +373,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 @@ -610,7 +615,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() @@ -622,7 +627,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..f0928b36 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,12 @@ 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=( + ord(self.data[2])+ + (ord(self.data[1])<<8) + ); + return toret; def poke(self,reg,val,bytes=2): """Write a CCSPI Register.""" @@ -199,6 +200,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/dev_apimote.py b/killerbee/dev_apimote.py index 8b327c4c..7210cb38 100755 --- a/killerbee/dev_apimote.py +++ b/killerbee/dev_apimote.py @@ -32,6 +32,8 @@ CC2420_REG_SYNC: int = 0x14 class APIMOTE: + packet_queue: Optional[list[Any]]: None + def __init__(self, dev: str, revision: int=DEFAULT_REVISION) -> None: ''' Instantiates the KillerBee class for the ApiMote platform running GoodFET firmware. @@ -213,17 +215,30 @@ def pnext(self, timeout: int=100) -> Any: if self.__stream_open == False: self.sniffer_on() - packet: Optional[bytes] = None - start: datetime = datetime.utcnow() + if self.packet_queue is not None: + packet = self.packet_queue + self.packet_queue = None + frame = packet + rssi = self.packet_queue_rssi + + else: + packet = 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 - 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 - 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] - frame: bytes = packet[1:] validcrc: bool = False if frame[-2:] == makeFCS(frame[:-2]): validcrc = True diff --git a/killerbee/kbutils.py b/killerbee/kbutils.py index 18db8f21..023b838d 100644 --- a/killerbee/kbutils.py +++ b/killerbee/kbutils.py @@ -369,7 +369,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/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..f3c9a282 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', From cd6d40e04a275c9c940823f9e6c99fb9295e0785 Mon Sep 17 00:00:00 2001 From: Taylor Date: Thu, 24 Jun 2021 09:12:28 -0400 Subject: [PATCH 2/6] fixed some typing --- killerbee/__init__.py | 2 +- killerbee/dev_apimote.py | 47 +++++++++++++++++++++++++++------------- 2 files changed, 33 insertions(+), 16 deletions(-) diff --git a/killerbee/__init__.py b/killerbee/__init__.py index cacbd51c..96922137 100644 --- a/killerbee/__init__.py +++ b/killerbee/__init__.py @@ -412,7 +412,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/dev_apimote.py b/killerbee/dev_apimote.py index 7210cb38..6ec628f9 100755 --- a/killerbee/dev_apimote.py +++ b/killerbee/dev_apimote.py @@ -32,7 +32,6 @@ CC2420_REG_SYNC: int = 0x14 class APIMOTE: - packet_queue: Optional[list[Any]]: None def __init__(self, dev: str, revision: int=DEFAULT_REVISION) -> None: ''' @@ -46,6 +45,9 @@ def __init__(self, dev: str, revision: int=DEFAULT_REVISION) -> None: @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 @@ -214,19 +216,13 @@ def pnext(self, timeout: int=100) -> Any: if self.__stream_open == False: self.sniffer_on() - - if self.packet_queue is not None: - packet = self.packet_queue - self.packet_queue = None - frame = packet - rssi = self.packet_queue_rssi - - else: - packet = None - + + if self.packet_queue is None: + packet: Optional[Any] = None start = datetime.utcnow() - while (packet is None and (start + timedelta(microseconds=timeout) > 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 @@ -235,18 +231,39 @@ def pnext(self, timeout: int=100) -> Any: if ord(packet[0])+1 < len(packet): self.packet_queue = packet[ord(packet[0])+1+1:] - self.packet_queue.rssi = rssi + 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 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: From 5c39c0d5110c5fa3417ece9f7b858793fc1fe653 Mon Sep 17 00:00:00 2001 From: Taylor Date: Fri, 25 Jun 2021 10:39:01 -0400 Subject: [PATCH 3/6] fixing things from tests --- killerbee/__init__.py | 20 ++------------------ killerbee/dev_apimote.py | 17 ++++------------- killerbee/scapy_extensions.py | 2 +- setup.py | 2 +- tests/test_killerbee_core.py | 5 ----- 5 files changed, 8 insertions(+), 38 deletions(-) diff --git a/killerbee/__init__.py b/killerbee/__init__.py index 96922137..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: diff --git a/killerbee/dev_apimote.py b/killerbee/dev_apimote.py index 6ec628f9..3380e1a8 100755 --- a/killerbee/dev_apimote.py +++ b/killerbee/dev_apimote.py @@ -25,23 +25,15 @@ 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 ''' @@ -53,10 +45,9 @@ def __init__(self, dev: str, revision: int=DEFAULT_REVISION) -> None: 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() @@ -101,7 +92,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: diff --git a/killerbee/scapy_extensions.py b/killerbee/scapy_extensions.py index 7cb8132e..7ccbe569 100644 --- a/killerbee/scapy_extensions.py +++ b/killerbee/scapy_extensions.py @@ -12,11 +12,11 @@ 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 diff --git a/setup.py b/setup.py index f3c9a282..6e895821 100644 --- a/setup.py +++ b/setup.py @@ -75,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/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) From 624143e1ea17ce82a1b623ec660de5d988fd2075 Mon Sep 17 00:00:00 2001 From: Taylor Date: Fri, 25 Jun 2021 10:49:02 -0400 Subject: [PATCH 4/6] missed letter --- tests/test_apimote_driver.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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) From d98b95bf315e3d1b5a27c31231632a30927170ef Mon Sep 17 00:00:00 2001 From: Taylor Date: Fri, 25 Jun 2021 10:53:19 -0400 Subject: [PATCH 5/6] remove ord --- killerbee/GoodFETCCSPI.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/killerbee/GoodFETCCSPI.py b/killerbee/GoodFETCCSPI.py index f0928b36..e21d3ce2 100644 --- a/killerbee/GoodFETCCSPI.py +++ b/killerbee/GoodFETCCSPI.py @@ -80,8 +80,7 @@ def peek(self,reg,bytes=2): self.writecmd(self.CCSPIAPP,0x02,len(data),data); toret=( - ord(self.data[2])+ - (ord(self.data[1])<<8) + self.data[2]+(self.data[1]<<8) ); return toret; From f68130f6078ce3d19c5396747db9c839163c274b Mon Sep 17 00:00:00 2001 From: Taylor Date: Fri, 25 Jun 2021 10:56:45 -0400 Subject: [PATCH 6/6] fixed some type mismatches --- killerbee/daintree.py | 4 ++-- killerbee/scapy_extensions.py | 3 +-- tests/fixtures/test_dt_dump.dcf | 2 -- 3 files changed, 3 insertions(+), 6 deletions(-) delete mode 100644 tests/fixtures/test_dt_dump.dcf diff --git a/killerbee/daintree.py b/killerbee/daintree.py index 6c65a9d1..b3d05012 100644 --- a/killerbee/daintree.py +++ b/killerbee/daintree.py @@ -106,8 +106,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/scapy_extensions.py b/killerbee/scapy_extensions.py index 7ccbe569..2c606cf8 100644 --- a/killerbee/scapy_extensions.py +++ b/killerbee/scapy_extensions.py @@ -23,7 +23,6 @@ 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: @@ -596,7 +595,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/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