95 lines
3.0 KiB
Python
95 lines
3.0 KiB
Python
|
#!/usr/bin/en python
|
||
|
|
||
|
from __future__ import print_function
|
||
|
|
||
|
# LoRa PHYdecoder - parse LoRa PHY decoded by gr-lora
|
||
|
# Copyright (C) 2020 Sebastien Dudek (@FlUxIuS) @Penthertz
|
||
|
# Code base on LoRa Craft project @PentHertz from commit a5f0a9d65c5ddc584035d5f45b52763e3a03a55f
|
||
|
|
||
|
|
||
|
from layers import LoRa
|
||
|
from scapy.layers.inet import UDP
|
||
|
from scapy.sendrecv import sniff
|
||
|
#from scapy.packet import bind_layers, Ether
|
||
|
from scapy.all import *
|
||
|
from scapy.utils import wrpcap
|
||
|
from scapy.utils import rdpcap
|
||
|
from lutil.crypto import *
|
||
|
import argparse
|
||
|
from lutil.fonts import *
|
||
|
import code
|
||
|
|
||
|
# default keys
|
||
|
cur_NwSKey = "2B7E151628AED2A6ABF7158809CF4F3C"
|
||
|
cur_AppSKey = "2B7E151628AED2A6ABF7158809CF4F3C"
|
||
|
|
||
|
lpkt = b"" # last pkt
|
||
|
|
||
|
savepcap = None
|
||
|
|
||
|
def savePCAP(pkt):
|
||
|
global savepcap
|
||
|
wrpcap(savepcap, pkt, append=True)
|
||
|
|
||
|
def decodePHY(pkt):
|
||
|
global lpkt, savepcap
|
||
|
if pkt != lpkt:
|
||
|
lpkt = pkt
|
||
|
decoded = LoRa(pkt[UDP].load)
|
||
|
direction = 1
|
||
|
print ()
|
||
|
if decoded.MType & 0b001 == 0b1:
|
||
|
direction = 0
|
||
|
print ("<"+"-"*30)
|
||
|
if decoded.MType & 0b001 == 0b0:
|
||
|
print ("-"*30+">")
|
||
|
print (repr(decoded))
|
||
|
if savepcap is not None:
|
||
|
savePCAP(pkt)
|
||
|
|
||
|
if cur_AppSKey is not None:
|
||
|
print (Fore.WHITE+Style.BRIGHT + "Deciphered Payload: ", decryptFRMPayload_1_0(binascii.unhexlify(cur_AppSKey), bytes(decoded), direction=direction), Style.RESET_ALL)
|
||
|
|
||
|
|
||
|
def filterpkt(pkt, port):
|
||
|
if pkt.haslayer(UDP):
|
||
|
if pkt[UDP].dport == port and pkt != lpkt:
|
||
|
return True
|
||
|
return False
|
||
|
|
||
|
if __name__ == "__main__":
|
||
|
parser = argparse.ArgumentParser(
|
||
|
description='Monitor and decode MAC PHY packets.')
|
||
|
parser.add_argument('-p', '--port', dest='port', default=40868,
|
||
|
help='TAP PORT to listen on (default: UDP 40868)')
|
||
|
parser.add_argument('-i', '--iface', dest='iface', default='lo',
|
||
|
help='Interface to monitor (default: local)')
|
||
|
parser.add_argument('-v', '--version', dest='version', default='1.1',
|
||
|
help='LoRaWAN version (1.1 by default)')
|
||
|
parser.add_argument('-o', '--output', dest='output', default=None,
|
||
|
help='PCAP output filename')
|
||
|
parser.add_argument('-c', '--intercative', dest='interact', action='store_true',
|
||
|
help='Interactive mode')
|
||
|
parser.add_argument('-n', '--NwSKey', dest='netskey', default=None,
|
||
|
help='NwSKey')
|
||
|
parser.add_argument('-a', '--AppSKey', dest='appskey', default=None,
|
||
|
help='AppSKey')
|
||
|
|
||
|
args = parser.parse_args()
|
||
|
iface = args.iface
|
||
|
port = int(args.port)
|
||
|
|
||
|
cur_NwSKey = args.netskey
|
||
|
cur_AppSKey = args.appskey
|
||
|
|
||
|
LoRa.version = args.version # setup LoRaWAN version
|
||
|
savepcap = args.output
|
||
|
|
||
|
if args.interact is True:
|
||
|
bind_layers(UDP, LoRa)
|
||
|
code.interact(local=locals())
|
||
|
else:
|
||
|
sniff(prn=decodePHY,
|
||
|
lfilter=lambda pkt: filterpkt(pkt, port),
|
||
|
iface=iface)
|