initial commit

todo: add documentation & wireshark dissector
This commit is contained in:
Matheus Eduardo Garbelini 2021-08-31 19:51:03 +08:00
parent 5a410587ed
commit 86890704fd
363 changed files with 164824 additions and 1 deletions

6
.gitignore vendored Normal file
View file

@ -0,0 +1,6 @@
firmware/.pio/
libs/__pycache__/
*__pycache__*

217
BTSnifferBREDR.py Executable file
View file

@ -0,0 +1,217 @@
#!/usr/bin/python3
import os
import sys
import _ctypes
import ctypes
import _thread
import colorama
import subprocess
import random
import signal
from threading import Thread
from time import sleep
from colorama import Fore, init
sys.path.insert(0, os.getcwd() + '/libs')
from scapy.layers.bluetooth import HCI_Hdr, BT_ACL_Hdr, HCI_PHDR_Hdr, BT_LMP, BT_Baseband
from scapy.utils import wrpcap, PcapWriter
from ESP32BTDriver import ESP32BTDriver
init(autoreset=True)
class SnifferBREDR:
TAG = 'Sniffer'
working_dir = None
packets = []
wireshark_process = None
fifo_file = '/tmp/fifocap.fifo'
pcap_filename = 'capture_bluetooth.pcap'
save_pcap = False
pcap_fifo_writer = None
pcap_writer = None
wireshark_started = False
driver = None # type: ESP32BTDriver
run_driver = True
serial_port = None
serial_baud = None
serial_thread = None
bridge_hci = True
bt_program = None
bt_program_thread = None
bt_program_run = True
bt_program_process = None
# BT Vars
tx_packets = 0
rx_packets = 0
remote_address = b'a8:96:75:25:c2:ac'
# Constructor
def __init__(self,
serial_port=None,
serial_baud=4000000,
start_wireshark=False,
save_pcap=True,
pcap_filename=None,
bridge_hci=True,
bt_program=None):
self.serial_port = serial_port
self.serial_baud = serial_baud
self.save_pcap = save_pcap
self.bridge_hci = bridge_hci
if pcap_filename:
self.pcap_filename = pcap_filename
if bt_program:
self.bt_program = bt_program
if start_wireshark:
try:
os.remove(self.fifo_file)
except:
pass
os.mkfifo(self.fifo_file)
try:
self.l('[!] Starting Wireshark...')
self.wireshark_process = subprocess.Popen(
['wireshark', '-k', '-i', self.fifo_file])
self.pcap_fifo_writer = PcapWriter(self.fifo_file, sync=True)
self.wireshark_started = True
except Exception as e:
self.error('Wireshark could not start: ' + str(e))
if save_pcap:
self.pcap_writer = PcapWriter(self.pcap_filename, sync=True)
if sys.platform == 'linux':
os.system('chmod o+rw ' + self.pcap_filename)
def signal_handler(self, signal, frame):
self.error('You pressed Ctrl+C - or killed me with -2')
exit(0)
# sys.exit(0)
# Logs
def l(self, msg):
print(Fore.YELLOW + '[' + self.TAG + '] ' + msg)
def error(self, msg):
print(Fore.RED + '[Error:' + self.TAG + '] ' + msg)
# Main functions
def start(self):
if self.bridge_hci or self.bt_program is None:
self.driver = ESP32BTDriver(self.serial_port, self.serial_baud)
self.driver.enable_sniffing(1)
self.driver.disable_poll_null(1)
print(Fore.GREEN + 'ESP32BT driver started on ' +
self.serial_port + '@' + str(self.serial_baud))
self.serial_thread = Thread(target=self.uart_rx_handler)
self.serial_thread.daemon = True
self.serial_thread.start()
if self.bt_program is not None:
self.bt_program_thread = Thread(target=self.bt_program_handler)
self.bt_program_thread.daemon = True
self.bt_program_thread.start()
@staticmethod
def skip_slashes(summary_text, idx):
return '/'.join(summary_text.split('/')[idx:])
def bt_program_handler(self):
if self.bridge_hci:
p_name = self.driver.serial_bridge_name
else:
p_name = self.serial_port
print('Starting ' + self.bt_program + ' -u ' + p_name)
process = subprocess.Popen([self.bt_program, '-u', p_name],
# stdin=subprocess.PIPE,
# stdout=subprocess.PIPE,
# stderr=subprocess.PIPE
)
self.bt_program_process = process
while self.bt_program_run:
sleep(1)
rc = process.poll()
return rc
def uart_rx_handler(self):
while self.run_driver:
# Receive packet from the NRF52 Dongle
data = self.driver.raw_receive()
if data:
# Decode Bluetooth Low Energy Data
pkt = BT_Baseband(data)
if pkt:
summary = pkt.summary()
direction = self.driver.direction
if direction == 1:
# print('R:' + summary)
self.log_rx(summary)
self.rx_packets += 1
elif direction == 0:
if BT_LMP in pkt:
# print('T:' + summary)
pkt = BT_ACL_Hdr(data)
self.log_tx(summary)
self.tx_packets += 1
# self.update_summary(self.tx_packets, self.rx_packets)
# Pipe/Save pcap
hci_pkt = HCI_PHDR_Hdr(
direction=direction) / HCI_Hdr(type=8) / pkt
if self.wireshark_started is True:
self.pcap_fifo_writer.write(hci_pkt)
if self.save_pcap is True:
self.pcap_writer.write(hci_pkt)
@staticmethod
def decode_address(addr):
return bytes.fromhex(''.join(addr.split(':')))
def log_tx(self, log_message):
print(Fore.CYAN + 'TX --> ' + log_message)
def log_rx(self, log_message):
print(Fore.GREEN + 'RX <-- ' + log_message)
def update_summary(self, tx_pkts, rx_pkts):
self.log_summary('TX packets: ' + str(tx_pkts))
self.log_summary('RX Packets: ' + str(rx_pkts))
self.log_summary('BT Clock: ' + str(self.driver.event_counter))
Sniffer = SnifferBREDR(serial_port='/dev/ttyUSB1',
serial_baud=4000000,
start_wireshark=False,
bt_program='./bin/spp_counter',
)
Sniffer.start()
try:
while True:
sleep(1)
except KeyboardInterrupt:
if Sniffer.save_pcap:
print(Fore.GREEN + 'Capture saved on capture_bluetooth.pcap')
if Sniffer.bt_program_process is not None:
Sniffer.bt_program_process.kill()
print(Fore.YELLOW + 'BT Program finished')

View file

@ -1 +1,2 @@
esp32_bluetooth_classic_sniffer
# ESP32 BR/EDR Active Sniffer

BIN
bin/libbtstack.so Executable file

Binary file not shown.

BIN
bin/sdp_bnep_query Executable file

Binary file not shown.

BIN
bin/sdp_general_query Executable file

Binary file not shown.

BIN
bin/sdp_rfcomm_query Executable file

Binary file not shown.

BIN
bin/spp_counter Executable file

Binary file not shown.

BIN
capture_bluetooth.pcap Normal file

Binary file not shown.

37
firmware/PlatformioScripts.py Executable file
View file

@ -0,0 +1,37 @@
Import("env")
import os
from firmware import reset_firmware
if os.path.isfile('BTPatcher.py'):
from BTPatcher import patch_esp32
def run_patch(target, source, env):
patch_esp32(str(target[0]))
env.AddPostAction("$BUILD_DIR/${PROGNAME}.elf", [run_patch])
def before_upload(target, source, env):
do_reset = env.GetProjectOption("reset_before_after_flash", default='false')
if 'true' in do_reset:
monitor_port = env.GetProjectOption("monitor_port", default=None)
if monitor_port:
try:
reset_firmware(monitor_port)
except Exception as err:
print(err)
def after_upload(target, source, env):
do_reset = env.GetProjectOption("reset_before_after_flash", default='false')
if 'true' in do_reset:
monitor_port = env.GetProjectOption("monitor_port", default=None)
if monitor_port:
try:
reset_firmware(monitor_port)
except Exception as err:
print(err)
env.AddPreAction("upload", [before_upload])
env.AddPostAction("upload", [after_upload])

BIN
firmware/bootloader.bin Normal file

Binary file not shown.

BIN
firmware/firmware.bin Normal file

Binary file not shown.

198
firmware/firmware.py Executable file
View file

@ -0,0 +1,198 @@
#!/usr/bin/env python3
import sys
import os
import subprocess
import shutil
import platform
from time import sleep
from pathlib import Path
from hashlib import sha1
from urllib.request import urlretrieve
from zipfile import ZipFile, ZIP_DEFLATED
args = sys.argv[1:]
system_name = platform.system()
is_linux = system_name == 'Linux' or system_name == 'Darwin'
script_path = sys.path[0]
pio_build_path = Path('.pio/build/esp32doit-devkit-v1-serial/')
def has_pio():
if is_linux:
find_cmd = 'which'
else:
find_cmd = 'where'
result = subprocess.run(
[find_cmd, 'pio'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
output = result.stdout.decode()
if len(output) > 0 and result.returncode == 0:
return output
return None
def is_source_project():
return os.path.isdir('src')
def get_platformio_version():
result = subprocess.run(
['pio', '--version'], stdout=subprocess.PIPE, stderr=subprocess.PIPE).stdout
return result.split(b' ')[-1].replace(b'\r', b'').replace(b'\n', b'')
def get_platformio_config_json():
return subprocess.run(['pio', 'project', 'config', '--json-output'],
stdout=subprocess.PIPE, stderr=subprocess.PIPE).stdout.replace(b'\r', b'').replace(b'\n', b'')
def generate_project_checksum():
print('Generating project.checksum')
# PIO Core version changes
checksum = sha1(get_platformio_version())
# Configuration file state
checksum.update(get_platformio_config_json())
# Write project checksum
with open(Path('.pio/build/project.checksum'), 'w') as out:
out.write(checksum.hexdigest())
def download_file(url, file_name):
if os.path.isfile(file_name):
return
def ProgressBar(block_num, block_size, total_size):
downloaded = block_num * block_size
if downloaded < total_size:
completed = round((downloaded * 100) / total_size, 2)
sys.stdout.write('\r' + str(completed) + '%')
sys.stdout.flush()
else:
print('\nDone')
print('Downloading ' + file_name + '...')
urlretrieve(url, file_name, reporthook=ProgressBar)
def reset_firmware(serial_port):
try:
import serial
except:
print("[ERROR] pyserial module not found, installing now via pip...")
os.system(sys.executable + ' -m pip install pyserial --upgrade')
os.sync()
# We should have pyserial here
import serial
ser = serial.Serial(serial_port, 115200, rtscts=False, dsrdtr=False)
ser.rts = True
ser.dtr = True
ser.dtr = False
ser.dtr = True
ser.close()
ser = None
print('Reset Done! EN pin toggled HIGH->LOW->HIGH')
def flash_firmware(serial_port):
os.makedirs(pio_build_path, exist_ok=True)
if not is_source_project():
generate_project_checksum()
shutil.copyfile('firmware.bin', pio_build_path / 'firmware.bin')
shutil.copyfile('bootloader.bin', pio_build_path / 'bootloader.bin')
shutil.copyfile('partitions.bin', pio_build_path / 'partitions.bin')
elif not os.path.isfile(pio_build_path / 'firmware.bin'):
print('[ERROR] Build project first. Example: ./firmware.py build')
exit(1)
print('Flashing firmware...')
os.system('pio run -e esp32doit-devkit-v1-serial -v -t nobuild -t upload --upload-port ' +
serial_port)
if __name__ == "__main__":
# Change working dir to script path
os.chdir(script_path)
enable_build = is_source_project()
home_path = str(Path.home())
if is_linux:
# Fix locale
os.environ['LC_ALL'] = 'C.UTF-8'
os.environ['LANG'] = 'C.UTF-8'
if not has_pio():
# Try adding platformio bin folder to path environment
if is_linux:
os.environ['PATH'] = home_path + \
'/.platformio/penv/bin/:' + os.environ['PATH']
elif platform.system() == 'Windows':
os.environ['Path'] = home_path + \
'\\.platformio\\penv\\Scripts;' + os.environ['Path']
# install platformio if not present on system
if not has_pio():
print('Platformio not found, installing now...')
download_file(
'https://raw.githubusercontent.com/platformio/platformio-core-installer/master/get-platformio.py',
'get-platformio.py')
os.system(sys.executable + ' get-platformio.py')
# Handle arguments
if len(args):
for i, arg in enumerate(args):
if 'build' in arg and enable_build:
print('Building firmware from source...')
os.system('pio run -e esp32doit-devkit-v1-serial')
os.makedirs('release', exist_ok=True)
shutil.copyfile(pio_build_path / 'firmware.bin',
Path('release/firmware.bin'))
shutil.copyfile(pio_build_path / 'bootloader.bin',
Path('release/bootloader.bin'))
shutil.copyfile(pio_build_path / 'partitions.bin',
Path('release/partitions.bin'))
shutil.copyfile('PlatformioScripts.py', Path(
'release/PlatformioScripts.py'))
shutil.copyfile('platformio.ini', Path(
'release/platformio.ini'))
shutil.copyfile('firmware.py', Path('release/firmware.py'))
# Create a ZipFile Object
with ZipFile(Path('release/esp32driver.zip'), 'w', ZIP_DEFLATED) as zipObj:
# Add multiple files to the zip
zipObj.write(Path('release/firmware.bin'))
zipObj.write(Path('release/bootloader.bin'))
zipObj.write(Path('release/partitions.bin'))
zipObj.write(Path('release/PlatformioScripts.py'))
zipObj.write(Path('release/platformio.ini'))
zipObj.write(Path('release/firmware.py'))
exit(0)
elif 'clean' in arg and enable_build:
os.system('pio run -v -t clean')
elif 'flash' in arg:
if len(args) < i + 2:
print(
'[ERROR] Missing serial port argument. Example: ./firmware.py flash /dev/ttyUSB0')
exit(1)
flash_firmware(args[i + 1])
exit(0)
elif 'reset' in arg:
if len(args) < i + 2:
print(
'[ERROR] Missing serial port argument. Example: ./firmware.py flash /dev/ttyUSB0')
exit(1)
reset_firmware(args[i + 1])
exit(0)
# Print usage
print('------ Usage help -------')
if enable_build:
print('./firmware.py build # Build firmware using platformio and distribute it to release folder')
print('./firmware.py clean # Clean firmware build files')
print('./firmware.py flash <port name> # Flash firmware using serial port')
print('./firmware.py reset <port name> # Reset firmware using serial port')

BIN
firmware/partitions.bin Normal file

Binary file not shown.

50
firmware/platformio.ini Normal file
View file

@ -0,0 +1,50 @@
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[platformio]
default_envs = esp32doit-devkit-v1-jtag
[env:esp32doit-devkit-v1-jtag]
platform = espressif32@3.0.0
board = esp32doit-devkit-v1
board_build.f_flash = 40000000L
framework = espidf
platform_packages =
tool-openocd-esp32@2.1000.20201202 ; Updated openocd for jtag debugging on wrover-kit
toolchain-xtensa32@2.80400.210211 ; Updated toolchain for esp32
framework-espidf@3.40001.200521 ; ESP-IDF 4.0.1
debug_tool = esp-prog
upload_protocol = esp-prog
debug_speed = 26000 ; Set JTAG to 26000Khz
upload_port = /dev/ttyUSB1
monitor_port = /dev/ttyUSB1
monitor_speed = 4000000
monitor_filters = colorize, esp32_exception_decoder
build_flags = -w
extra_scripts = post:PlatformioScripts.py
reset_before_after_flash = true
[env:esp32doit-devkit-v1-serial]
platform = espressif32@3.0.0
board = esp32doit-devkit-v1
framework = espidf
platform_packages =
toolchain-xtensa32@2.80400.210211 ; Updated toolchain for esp32
framework-espidf@3.40001.200521 ; ESP-IDF 4.0.1
upload_protocol = esptool
upload_port = /dev/ttyUSB1
monitor_port = /dev/ttyUSB1
monitor_speed = 4000000
monitor_filters = colorize, esp32_exception_decoder
build_flags = -w
upload_command = $PYTHONEXE $UPLOADER --chip esp32 --port $UPLOAD_PORT --baud 460800 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 40m --flash_size detect 0x1000 $BUILD_DIR/bootloader.bin 0x8000 $BUILD_DIR/partitions.bin 0x10000 $BUILD_DIR/firmware.bin
extra_scripts = post:PlatformioScripts.py
reset_before_after_flash = true

BIN
hci_dump.pklg Normal file

Binary file not shown.

164
libs/ESP32BTDriver.py Executable file
View file

@ -0,0 +1,164 @@
from binascii import hexlify
from colorama import Fore
import binascii
import os
import sys
import pty
import subprocess
import serial
import tty
import time
import ctypes
import serial.tools.list_ports
from threading import Thread
class ConnectionStatus(ctypes.LittleEndianStructure):
_fields_ = [
("clock", ctypes.c_uint32, 8*4),
("channel", ctypes.c_uint8, 8),
("ptt", ctypes.c_uint8, 1),
("role", ctypes.c_uint8, 1),
("custom_lmp", ctypes.c_uint8, 1),
("retry_flag", ctypes.c_uint8, 1),
("intercept_req", ctypes.c_uint8, 1),
("tx_encrypted", ctypes.c_uint8, 1),
("rx_encrypted", ctypes.c_uint8, 1),
("is_eir", ctypes.c_uint8, 1),
]
def getdict(self):
return dict((f, getattr(self, f)) for f, _, _ in self._fields_)
# USB Serial commands
ESP32_CMD_DATA_RX = b'\xA7'
ESP32_CMD_DATA_TX = b'\xBB'
ESP32_CMD_CHECKSUM_ERROR = b'\xA8'
ESP32_CMD_CONFIG_AUTO_EMPTY_PDU = b'\xA9'
ESP32_CMD_CONFIG_ACK = b'\xAA'
ESP32_CMD_CONFIG_LOG_TX = b'\xCC'
ESP32_CMD_LOG = b'\x7F'
ESP32_CMD_RESET = b'\x86'
ESP32_CMD_VERSION = b'\xEE'
ESP32_CMD_ENABLE_LMP_SNIFFING = b'\x81'
ESP32_CMD_DISABLE_POLL_NULL = b'\x89'
# HCI Codes to bridge
H4_NONE = b'\x00'
H4_CMD = b'\x01'
H4_ACL = b'\x02'
H4_SCO = b'\x03'
H4_EVT = b'\x04'
H4_EVT_HW_ERROR = b'\x10'
# Driver class
class ESP32BTDriver:
n_debug = False
n_log = False
logs_pcap = False
event_counter = 0
pcap_filename = None
pcap_tx_handover = False
sent_pkt = None
direction = None
version = None
status = None # type: ConnectionStatus
serial_bridge = None
serial_bridge_name = None
serial_hci_thread = None
# Constructor ------------------------------------
def __init__(self, port_name=None, baudrate=115200, debug=False, logs=True, logs_pcap=False, pcap_filename=None):
self.serial = serial.Serial(
port_name, baudrate, rtscts=0, xonxoff=0, timeout=1)
self.logs_pcap = logs_pcap
self.n_log = logs
self.n_debug = debug
self.get_version()
if self.n_debug:
print('ESP32: Instance started')
master, slave = pty.openpty()
self.serial_bridge_name = os.ttyname(slave)
self.serial_bridge = master
tty.setraw(master)
tty.setraw(slave)
print(Fore.GREEN + 'HCI Bridge started on ' + os.ttyname(slave))
self.serial_hci_thread = Thread(target=self.hci_handler)
self.serial_hci_thread.daemon = True
self.serial_hci_thread.start()
def close(self):
print('NRF52 Dongle closed')
# UART functions ---------------------------
def get_version(self):
self.serial.write(ESP32_CMD_VERSION)
version_string = self.serial.readline()
if version_string and len(version_string):
self.version = version_string.decode('utf-8').split('\n')[0]
print("[ESP32BT] Firmware version: " + self.version)
else:
raise Exception(
"Version not received. Make sure to flash ESP32BT firmware")
def enable_sniffing(self, val):
self.serial.write(ESP32_CMD_ENABLE_LMP_SNIFFING + bytearray([val]))
def reset_firmware(self, val):
self.serial.write(ESP32_CMD_RESET + bytearray([0x86, 0xAA]))
def disable_poll_null(self, val):
self.serial.write(ESP32_CMD_DISABLE_POLL_NULL + bytearray([val]))
self.serial.read(1)
def hci_handler(self):
while True:
c = os.read(self.serial_bridge, 1)
self.serial.write(c)
def raw_receive(self):
cmd = self.serial.read(1)
if cmd == H4_EVT:
opcode = self.serial.read(1)
length = self.serial.read(1)
payload = self.serial.read(ord(length))
os.write(self.serial_bridge, H4_EVT + opcode + length + payload)
elif cmd == H4_ACL:
opcode = self.serial.read(2)
length = self.serial.read(2)
payload = self.serial.read(length[0] | (length[1] << 8))
print(payload)
os.write(self.serial_bridge, H4_ACL + opcode + length + payload)
elif cmd == H4_CMD:
opcode = self.serial.read(2)
length = self.serial.read(1)
payload = self.serial.read(ord(length))
os.write(self.serial_bridge, H4_CMD + opcode + length + payload)
# Receive BT packets
elif cmd == ESP32_CMD_DATA_RX or cmd == ESP32_CMD_DATA_TX:
raw_sz = self.serial.read(2)
sz = raw_sz[0] | (raw_sz[1] << 8)
data = self.serial.read(sz)
checksum = self.serial.read(1)
# Check data checksum
if (checksum and (sum(data) & 0xFF) == ord(checksum)):
self.status = ConnectionStatus(*data[0:6])
if cmd == ESP32_CMD_DATA_RX:
self.direction = 1
elif cmd == ESP32_CMD_DATA_TX:
self.direction = 0
return data[6:]
return None

1
libs/scapy/VERSION Executable file
View file

@ -0,0 +1 @@
python2.dev0

119
libs/scapy/__init__.py Executable file
View file

@ -0,0 +1,119 @@
# This file is part of Scapy
# See http://www.secdev.org/projects/scapy for more information
# Copyright (C) Philippe Biondi <phil@secdev.org>
# This program is published under a GPLv2 license
"""
Scapy: create, send, sniff, dissect and manipulate network packets.
Usable either from an interactive console or as a Python library.
https://scapy.net
"""
import os
import re
import subprocess
from scapy.compat import AnyStr
_SCAPY_PKG_DIR = os.path.dirname(__file__)
def _version_from_git_describe():
# type: () -> AnyStr
"""
Read the version from ``git describe``. It returns the latest tag with an
optional suffix if the current directory is not exactly on the tag.
Example::
$ git describe --always
v2.3.2-346-g164a52c075c8
The tag prefix (``v``) and the git commit sha1 (``-g164a52c075c8``) are
removed if present.
If the current directory is not exactly on the tag, a ``.devN`` suffix is
appended where N is the number of commits made after the last tag.
Example::
>>> _version_from_git_describe()
'2.3.2.dev346'
:raises CalledProcessError: if git is unavailable
:return: Scapy's latest tag
"""
if not os.path.isdir(os.path.join(os.path.dirname(_SCAPY_PKG_DIR), '.git')): # noqa: E501
raise ValueError('not in scapy git repo')
def _git(cmd):
process = subprocess.Popen(
cmd.split(),
cwd=_SCAPY_PKG_DIR,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
out, err = process.communicate()
if process.returncode == 0:
return out.decode().strip()
else:
raise subprocess.CalledProcessError(process.returncode, err)
tag = _git("git describe --always")
if not tag.startswith("v"):
# Upstream was not fetched
commit = _git("git rev-list --tags --max-count=1")
tag = _git("git describe --tags --always --long %s" % commit)
match = re.match('^v?(.+?)-(\\d+)-g[a-f0-9]+$', tag)
if match:
# remove the 'v' prefix and add a '.devN' suffix
return '%s.dev%s' % (match.group(1), match.group(2))
else:
# just remove the 'v' prefix
return re.sub('^v', '', tag)
def _version():
# type: () -> str
"""Returns the Scapy version from multiple methods
:return: the Scapy version
"""
version_file = os.path.join(_SCAPY_PKG_DIR, 'VERSION')
try:
tag = _version_from_git_describe()
# successfully read the tag from git, write it in VERSION for
# installation and/or archive generation.
with open(version_file, 'w') as fdesc:
fdesc.write(tag)
return tag
except Exception:
# failed to read the tag from git, try to read it from a VERSION file
try:
with open(version_file, 'r') as fdsec:
tag = fdsec.read()
return tag
except Exception:
# Rely on git archive "export-subst" git attribute.
# See 'man gitattributes' for more details.
git_archive_id = '$Format:%h %d$'
sha1 = git_archive_id.strip().split()[0]
match = re.search('tag:(\\S+)', git_archive_id)
if match:
return "git-archive.dev" + match.group(1)
elif sha1:
return "git-archive.dev" + sha1
else:
return 'unknown.version'
VERSION = __version__ = _version()
_tmp = re.search(r"[0-9.]+", VERSION)
VERSION_MAIN = _tmp.group() if _tmp is not None else VERSION
if __name__ == "__main__":
from scapy.main import interact
interact()

15
libs/scapy/__main__.py Executable file
View file

@ -0,0 +1,15 @@
# This file is part of Scapy
# See http://www.secdev.org/projects/scapy for more information
# Copyright (C) Philippe Biondi <phil@secdev.org>
# This program is published under a GPLv2 license
"""
Scapy: create, send, sniff, dissect and manipulate network packets.
Usable either from an interactive console or as a Python library.
http://www.secdev.org/projects/scapy
"""
from scapy.main import interact
interact()

52
libs/scapy/all.py Executable file
View file

@ -0,0 +1,52 @@
# This file is part of Scapy
# See http://www.secdev.org/projects/scapy for more information
# Copyright (C) Philippe Biondi <phil@secdev.org>
# This program is published under a GPLv2 license
"""
Aggregate top level objects from all Scapy modules.
"""
from scapy.base_classes import *
from scapy.config import *
from scapy.dadict import *
from scapy.data import *
from scapy.error import *
from scapy.themes import *
from scapy.arch import *
from scapy.plist import *
from scapy.fields import *
from scapy.packet import *
from scapy.asn1fields import *
from scapy.asn1packet import *
from scapy.utils import *
from scapy.route import *
from scapy.sendrecv import *
from scapy.sessions import *
from scapy.supersocket import *
from scapy.volatile import *
from scapy.as_resolvers import *
from scapy.automaton import *
from scapy.autorun import *
from scapy.main import *
from scapy.consts import *
from scapy.compat import raw # noqa: F401
from scapy.layers.all import *
from scapy.asn1.asn1 import *
from scapy.asn1.ber import *
from scapy.asn1.mib import *
from scapy.pipetool import *
from scapy.scapypipes import *
if conf.ipv6_enabled: # noqa: F405
from scapy.utils6 import * # noqa: F401
from scapy.route6 import * # noqa: F401
from scapy.ansmachine import *

131
libs/scapy/ansmachine.py Executable file
View file

@ -0,0 +1,131 @@
# This file is part of Scapy
# See http://www.secdev.org/projects/scapy for more information
# Copyright (C) Philippe Biondi <phil@secdev.org>
# This program is published under a GPLv2 license
"""
Answering machines.
"""
########################
# Answering machines #
########################
from __future__ import absolute_import
from __future__ import print_function
from scapy.sendrecv import send, sniff
from scapy.config import conf
from scapy.error import log_interactive
import scapy.modules.six as six
class ReferenceAM(type):
def __new__(cls, name, bases, dct):
obj = super(ReferenceAM, cls).__new__(cls, name, bases, dct)
if obj.function_name:
globals()[obj.function_name] = lambda obj=obj, *args, **kargs: obj(*args, **kargs)() # noqa: E501
return obj
class AnsweringMachine(six.with_metaclass(ReferenceAM, object)):
function_name = ""
filter = None
sniff_options = {"store": 0}
sniff_options_list = ["store", "iface", "count", "promisc", "filter", "type", "prn", "stop_filter"] # noqa: E501
send_options = {"verbose": 0}
send_options_list = ["iface", "inter", "loop", "verbose"]
send_function = staticmethod(send)
def __init__(self, **kargs):
self.mode = 0
if self.filter:
kargs.setdefault("filter", self.filter)
kargs.setdefault("prn", self.reply)
self.optam1 = {}
self.optam2 = {}
self.optam0 = {}
doptsend, doptsniff = self.parse_all_options(1, kargs)
self.defoptsend = self.send_options.copy()
self.defoptsend.update(doptsend)
self.defoptsniff = self.sniff_options.copy()
self.defoptsniff.update(doptsniff)
self.optsend, self.optsniff = [{}, {}]
def __getattr__(self, attr):
for dct in [self.optam2, self.optam1]:
if attr in dct:
return dct[attr]
raise AttributeError(attr)
def __setattr__(self, attr, val):
mode = self.__dict__.get("mode", 0)
if mode == 0:
self.__dict__[attr] = val
else:
[self.optam1, self.optam2][mode - 1][attr] = val
def parse_options(self):
pass
def parse_all_options(self, mode, kargs):
sniffopt = {}
sendopt = {}
for k in list(kargs): # use list(): kargs is modified in the loop
if k in self.sniff_options_list:
sniffopt[k] = kargs[k]
if k in self.send_options_list:
sendopt[k] = kargs[k]
if k in self.sniff_options_list + self.send_options_list:
del kargs[k]
if mode != 2 or kargs:
if mode == 1:
self.optam0 = kargs
elif mode == 2 and kargs:
k = self.optam0.copy()
k.update(kargs)
self.parse_options(**k)
kargs = k
omode = self.__dict__.get("mode", 0)
self.__dict__["mode"] = mode
self.parse_options(**kargs)
self.__dict__["mode"] = omode
return sendopt, sniffopt
def is_request(self, req):
return 1
def make_reply(self, req):
return req
def send_reply(self, reply):
self.send_function(reply, **self.optsend)
def print_reply(self, req, reply):
print("%s ==> %s" % (req.summary(), reply.summary()))
def reply(self, pkt):
if not self.is_request(pkt):
return
reply = self.make_reply(pkt)
self.send_reply(reply)
if conf.verb >= 0:
self.print_reply(pkt, reply)
def run(self, *args, **kargs):
log_interactive.warning("run() method deprecated. The instance is now callable") # noqa: E501
self(*args, **kargs)
def __call__(self, *args, **kargs):
optsend, optsniff = self.parse_all_options(2, kargs)
self.optsend = self.defoptsend.copy()
self.optsend.update(optsend)
self.optsniff = self.defoptsniff.copy()
self.optsniff.update(optsniff)
try:
self.sniff()
except KeyboardInterrupt:
print("Interrupted by user")
def sniff(self):
sniff(**self.optsniff)

95
libs/scapy/arch/__init__.py Executable file
View file

@ -0,0 +1,95 @@
# This file is part of Scapy
# See http://www.secdev.org/projects/scapy for more information
# Copyright (C) Philippe Biondi <phil@secdev.org>
# This program is published under a GPLv2 license
"""
Operating system specific functionality.
"""
from __future__ import absolute_import
import socket
from scapy.consts import LINUX, SOLARIS, WINDOWS, BSD
from scapy.error import Scapy_Exception
from scapy.config import conf, _set_conf_sockets
from scapy.pton_ntop import inet_pton, inet_ntop
from scapy.data import ARPHDR_ETHER, ARPHDR_LOOPBACK, IPV6_ADDR_GLOBAL
from scapy.compat import orb
def str2mac(s):
return ("%02x:" * 6)[:-1] % tuple(orb(x) for x in s)
if not WINDOWS:
if not conf.use_pcap:
from scapy.arch.bpf.core import get_if_raw_addr
def get_if_addr(iff):
return inet_ntop(socket.AF_INET, get_if_raw_addr(iff))
def get_if_hwaddr(iff):
addrfamily, mac = get_if_raw_hwaddr(iff) # noqa: F405
if addrfamily in [ARPHDR_ETHER, ARPHDR_LOOPBACK]:
return str2mac(mac)
else:
raise Scapy_Exception("Unsupported address family (%i) for interface [%s]" % (addrfamily, iff)) # noqa: E501
# Next step is to import following architecture specific functions:
# def get_if_raw_hwaddr(iff)
# def get_if_raw_addr(iff):
# def get_if_list():
# def get_working_if():
# def attach_filter(s, filter, iface):
# def set_promisc(s,iff,val=1):
# def read_routes():
# def read_routes6():
# def get_if(iff,cmd):
# def get_if_index(iff):
if LINUX:
from scapy.arch.linux import * # noqa F403
elif BSD:
from scapy.arch.unix import read_routes, read_routes6, in6_getifaddr # noqa: F401, E501
from scapy.arch.bpf.core import * # noqa F403
if not conf.use_pcap:
# Native
from scapy.arch.bpf.supersocket import * # noqa F403
conf.use_bpf = True
elif SOLARIS:
from scapy.arch.solaris import * # noqa F403
elif WINDOWS:
from scapy.arch.windows import * # noqa F403
from scapy.arch.windows.native import * # noqa F403
if conf.iface is None:
conf.iface = conf.loopback_name
_set_conf_sockets() # Apply config
def get_if_addr6(iff):
"""
Returns the main global unicast address associated with provided
interface, in human readable form. If no global address is found,
None is returned.
"""
return next((x[0] for x in in6_getifaddr()
if x[2] == iff and x[1] == IPV6_ADDR_GLOBAL), None)
def get_if_raw_addr6(iff):
"""
Returns the main global unicast address associated with provided
interface, in network format. If no global address is found, None
is returned.
"""
ip6 = get_if_addr6(iff)
if ip6 is not None:
return inet_pton(socket.AF_INET6, ip6)
return None

View file

@ -0,0 +1,5 @@
# Guillaume Valadon <guillaume@valadon.net>
"""
Scapy BSD native support
"""

27
libs/scapy/arch/bpf/consts.py Executable file
View file

@ -0,0 +1,27 @@
# Guillaume Valadon <guillaume@valadon.net>
"""
Scapy BSD native support - constants
"""
from ctypes import sizeof
from scapy.libs.structures import bpf_program
from scapy.data import MTU
SIOCGIFFLAGS = 0xc0206911
BPF_BUFFER_LENGTH = MTU
# From net/bpf.h
BIOCIMMEDIATE = 0x80044270
BIOCGSTATS = 0x4008426f
BIOCPROMISC = 0x20004269
BIOCSETIF = 0x8020426c
BIOCSBLEN = 0xc0044266
BIOCGBLEN = 0x40044266
BIOCSETF = 0x80004267 | ((sizeof(bpf_program) & 0x1fff) << 16)
BIOCSDLT = 0x80044278
BIOCSHDRCMPLT = 0x80044275
BIOCGDLT = 0x4004426a
DLT_IEEE802_11_RADIO = 127

211
libs/scapy/arch/bpf/core.py Executable file
View file

@ -0,0 +1,211 @@
# Guillaume Valadon <guillaume@valadon.net>
"""
Scapy *BSD native support - core
"""
from __future__ import absolute_import
from ctypes import cdll, cast, pointer
from ctypes import c_int, c_ulong, c_char_p
from ctypes.util import find_library
import fcntl
import os
import re
import socket
import struct
import subprocess
from scapy.arch.bpf.consts import BIOCSETF, SIOCGIFFLAGS, BIOCSETIF
from scapy.arch.common import get_if, compile_filter
from scapy.compat import plain_str
from scapy.config import conf
from scapy.data import ARPHDR_LOOPBACK, ARPHDR_ETHER
from scapy.error import Scapy_Exception, warning
from scapy.modules.six.moves import range
# ctypes definitions
LIBC = cdll.LoadLibrary(find_library("libc"))
LIBC.ioctl.argtypes = [c_int, c_ulong, c_char_p]
LIBC.ioctl.restype = c_int
# Addresses manipulation functions
def get_if_raw_addr(ifname):
"""Returns the IPv4 address configured on 'ifname', packed with inet_pton.""" # noqa: E501
# Get ifconfig output
subproc = subprocess.Popen(
[conf.prog.ifconfig, ifname],
close_fds=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE
)
stdout, stderr = subproc.communicate()
if subproc.returncode:
warning("Failed to execute ifconfig: (%s)", plain_str(stderr))
return b"\0\0\0\0"
# Get IPv4 addresses
addresses = [
line for line in plain_str(stdout).splitlines()
if "inet " in line
]
if not addresses:
warning("No IPv4 address found on %s !", ifname)
return b"\0\0\0\0"
# Pack the first address
address = addresses[0].split(' ')[1]
if '/' in address: # NetBSD 8.0
address = address.split("/")[0]
return socket.inet_pton(socket.AF_INET, address)
def get_if_raw_hwaddr(ifname):
"""Returns the packed MAC address configured on 'ifname'."""
NULL_MAC_ADDRESS = b'\x00' * 6
# Handle the loopback interface separately
if ifname == conf.loopback_name:
return (ARPHDR_LOOPBACK, NULL_MAC_ADDRESS)
# Get ifconfig output
subproc = subprocess.Popen(
[conf.prog.ifconfig, ifname],
close_fds=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE
)
stdout, stderr = subproc.communicate()
if subproc.returncode:
raise Scapy_Exception("Failed to execute ifconfig: (%s)" %
(plain_str(stderr)))
# Get MAC addresses
addresses = [
line for line in plain_str(stdout).splitlines() if (
"ether" in line or "lladdr" in line or "address" in line
)
]
if not addresses:
raise Scapy_Exception("No MAC address found on %s !" % ifname)
# Pack and return the MAC address
mac = addresses[0].split(' ')[1]
mac = [chr(int(b, 16)) for b in mac.split(':')]
return (ARPHDR_ETHER, ''.join(mac))
# BPF specific functions
def get_dev_bpf():
"""Returns an opened BPF file object"""
# Get the first available BPF handle
for bpf in range(256):
try:
fd = os.open("/dev/bpf%i" % bpf, os.O_RDWR)
return (fd, bpf)
except OSError:
continue
raise Scapy_Exception("No /dev/bpf handle is available !")
def attach_filter(fd, bpf_filter, iface):
"""Attach a BPF filter to the BPF file descriptor"""
bp = compile_filter(bpf_filter, iface)
# Assign the BPF program to the interface
ret = LIBC.ioctl(c_int(fd), BIOCSETF, cast(pointer(bp), c_char_p))
if ret < 0:
raise Scapy_Exception("Can't attach the BPF filter !")
# Interface manipulation functions
def get_if_list():
"""Returns a list containing all network interfaces."""
# Get ifconfig output
subproc = subprocess.Popen(
[conf.prog.ifconfig],
close_fds=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE
)
stdout, stderr = subproc.communicate()
if subproc.returncode:
raise Scapy_Exception("Failed to execute ifconfig: (%s)" %
(plain_str(stderr)))
interfaces = [
line[:line.find(':')] for line in plain_str(stdout).splitlines()
if ": flags" in line.lower()
]
return interfaces
_IFNUM = re.compile(r"([0-9]*)([ab]?)$")
def get_working_ifaces():
"""
Returns an ordered list of interfaces that could be used with BPF.
Note: the order mimics pcap_findalldevs() behavior
"""
# Only root is allowed to perform the following ioctl() call
if os.getuid() != 0:
return []
# Test all network interfaces
interfaces = []
for ifname in get_if_list():
# Unlike pcap_findalldevs(), we do not care of loopback interfaces.
if ifname == conf.loopback_name:
continue
# Get interface flags
try:
result = get_if(ifname, SIOCGIFFLAGS)
except IOError:
warning("ioctl(SIOCGIFFLAGS) failed on %s !", ifname)
continue
# Convert flags
ifflags = struct.unpack("16xH14x", result)[0]
if ifflags & 0x1: # IFF_UP
# Get a BPF handle
fd = get_dev_bpf()[0]
if fd is None:
raise Scapy_Exception("No /dev/bpf are available !")
# Check if the interface can be used
try:
fcntl.ioctl(fd, BIOCSETIF, struct.pack("16s16x",
ifname.encode()))
except IOError:
pass
else:
ifnum, ifab = _IFNUM.search(ifname).groups()
interfaces.append((ifname, int(ifnum) if ifnum else -1, ifab))
finally:
# Close the file descriptor
os.close(fd)
# Sort to mimic pcap_findalldevs() order
interfaces.sort(key=lambda elt: (elt[1], elt[2], elt[0]))
return [iface[0] for iface in interfaces]
def get_working_if():
"""Returns the first interface than can be used with BPF"""
ifaces = get_working_ifaces()
if not ifaces:
# A better interface will be selected later using the routing table
return conf.loopback_name
return ifaces[0]

View file

@ -0,0 +1,423 @@
# Guillaume Valadon <guillaume@valadon.net>
"""
Scapy *BSD native support - BPF sockets
"""
from ctypes import c_long, sizeof
import errno
import fcntl
import os
import platform
from select import select
import struct
import time
from scapy.arch.bpf.core import get_dev_bpf, attach_filter
from scapy.arch.bpf.consts import BIOCGBLEN, BIOCGDLT, BIOCGSTATS, \
BIOCIMMEDIATE, BIOCPROMISC, BIOCSBLEN, BIOCSETIF, BIOCSHDRCMPLT, \
BPF_BUFFER_LENGTH, BIOCSDLT, DLT_IEEE802_11_RADIO
from scapy.config import conf
from scapy.consts import FREEBSD, NETBSD, DARWIN
from scapy.data import ETH_P_ALL
from scapy.error import Scapy_Exception, warning
from scapy.supersocket import SuperSocket
from scapy.compat import raw
if FREEBSD:
# On 32bit architectures long might be 32bit.
BPF_ALIGNMENT = sizeof(c_long)
elif NETBSD:
BPF_ALIGNMENT = 8 # sizeof(long)
else:
BPF_ALIGNMENT = 4 # sizeof(int32_t)
# SuperSockets definitions
class _L2bpfSocket(SuperSocket):
""""Generic Scapy BPF Super Socket"""
desc = "read/write packets using BPF"
nonblocking_socket = True
def __init__(self, iface=None, type=ETH_P_ALL, promisc=None, filter=None,
nofilter=0, monitor=False):
self.fd_flags = None
self.assigned_interface = None
# SuperSocket mandatory variables
if promisc is None:
self.promisc = conf.sniff_promisc
else:
self.promisc = promisc
if iface is None:
self.iface = conf.iface
else:
self.iface = iface
# Get the BPF handle
(self.ins, self.dev_bpf) = get_dev_bpf()
self.outs = self.ins
# Set the BPF buffer length
try:
fcntl.ioctl(self.ins, BIOCSBLEN, struct.pack('I', BPF_BUFFER_LENGTH)) # noqa: E501
except IOError:
raise Scapy_Exception("BIOCSBLEN failed on /dev/bpf%i" %
self.dev_bpf)
# Assign the network interface to the BPF handle
try:
fcntl.ioctl(self.ins, BIOCSETIF, struct.pack("16s16x", self.iface.encode())) # noqa: E501
except IOError:
raise Scapy_Exception("BIOCSETIF failed on %s" % self.iface)
self.assigned_interface = self.iface
# Set the interface into promiscuous
if self.promisc:
self.set_promisc(1)
# Set the interface to monitor mode
# Note: - trick from libpcap/pcap-bpf.c - monitor_mode()
# - it only works on OS X 10.5 and later
if DARWIN and monitor:
dlt_radiotap = struct.pack('I', DLT_IEEE802_11_RADIO)
try:
fcntl.ioctl(self.ins, BIOCSDLT, dlt_radiotap)
except IOError:
raise Scapy_Exception("Can't set %s into monitor mode!" %
self.iface)
# Don't block on read
try:
fcntl.ioctl(self.ins, BIOCIMMEDIATE, struct.pack('I', 1))
except IOError:
raise Scapy_Exception("BIOCIMMEDIATE failed on /dev/bpf%i" %
self.dev_bpf)
# Scapy will provide the link layer source address
# Otherwise, it is written by the kernel
try:
fcntl.ioctl(self.ins, BIOCSHDRCMPLT, struct.pack('i', 1))
except IOError:
raise Scapy_Exception("BIOCSHDRCMPLT failed on /dev/bpf%i" %
self.dev_bpf)
# Configure the BPF filter
if not nofilter:
if conf.except_filter:
if filter:
filter = "(%s) and not (%s)" % (filter, conf.except_filter)
else:
filter = "not (%s)" % conf.except_filter
if filter is not None:
try:
attach_filter(self.ins, filter, self.iface)
except ImportError as ex:
warning("Cannot set filter: %s" % ex)
# Set the guessed packet class
self.guessed_cls = self.guess_cls()
def set_promisc(self, value):
"""Set the interface in promiscuous mode"""
try:
fcntl.ioctl(self.ins, BIOCPROMISC, struct.pack('i', value))
except IOError:
raise Scapy_Exception("Cannot set promiscuous mode on interface "
"(%s)!" % self.iface)
def __del__(self):
"""Close the file descriptor on delete"""
# When the socket is deleted on Scapy exits, __del__ is
# sometimes called "too late", and self is None
if self is not None:
self.close()
def guess_cls(self):
"""Guess the packet class that must be used on the interface"""
# Get the data link type
try:
ret = fcntl.ioctl(self.ins, BIOCGDLT, struct.pack('I', 0))
ret = struct.unpack('I', ret)[0]
except IOError:
cls = conf.default_l2
warning("BIOCGDLT failed: unable to guess type. Using %s !",
cls.name)
return cls
# Retrieve the corresponding class
try:
return conf.l2types[ret]
except KeyError:
cls = conf.default_l2
warning("Unable to guess type (type %i). Using %s", ret, cls.name)
def set_nonblock(self, set_flag=True):
"""Set the non blocking flag on the socket"""
# Get the current flags
if self.fd_flags is None:
try:
self.fd_flags = fcntl.fcntl(self.ins, fcntl.F_GETFL)
except IOError:
warning("Cannot get flags on this file descriptor !")
return
# Set the non blocking flag
if set_flag:
new_fd_flags = self.fd_flags | os.O_NONBLOCK
else:
new_fd_flags = self.fd_flags & ~os.O_NONBLOCK
try:
fcntl.fcntl(self.ins, fcntl.F_SETFL, new_fd_flags)
self.fd_flags = new_fd_flags
except Exception:
warning("Can't set flags on this file descriptor !")
def get_stats(self):
"""Get received / dropped statistics"""
try:
ret = fcntl.ioctl(self.ins, BIOCGSTATS, struct.pack("2I", 0, 0))
return struct.unpack("2I", ret)
except IOError:
warning("Unable to get stats from BPF !")
return (None, None)
def get_blen(self):
"""Get the BPF buffer length"""
try:
ret = fcntl.ioctl(self.ins, BIOCGBLEN, struct.pack("I", 0))
return struct.unpack("I", ret)[0]
except IOError:
warning("Unable to get the BPF buffer length")
return
def fileno(self):
"""Get the underlying file descriptor"""
return self.ins
def close(self):
"""Close the Super Socket"""
if not self.closed and self.ins is not None:
os.close(self.ins)
self.closed = True
self.ins = None
def send(self, x):
"""Dummy send method"""
raise Exception(
"Can't send anything with %s" % self.__class__.__name__
)
def recv_raw(self, x=BPF_BUFFER_LENGTH):
"""Dummy recv method"""
raise Exception(
"Can't recv anything with %s" % self.__class__.__name__
)
@staticmethod
def select(sockets, remain=None):
"""This function is called during sendrecv() routine to select
the available sockets.
"""
# sockets, None (means use the socket's recv() )
return bpf_select(sockets, remain), None
class L2bpfListenSocket(_L2bpfSocket):
""""Scapy L2 BPF Listen Super Socket"""
def __init__(self, *args, **kwargs):
self.received_frames = []
super(L2bpfListenSocket, self).__init__(*args, **kwargs)
def buffered_frames(self):
"""Return the number of frames in the buffer"""
return len(self.received_frames)
def get_frame(self):
"""Get a frame or packet from the received list"""
if self.received_frames:
return self.received_frames.pop(0)
else:
return None, None, None
@staticmethod
def bpf_align(bh_h, bh_c):
"""Return the index to the end of the current packet"""
# from <net/bpf.h>
return ((bh_h + bh_c) + (BPF_ALIGNMENT - 1)) & ~(BPF_ALIGNMENT - 1)
def extract_frames(self, bpf_buffer):
"""Extract all frames from the buffer and stored them in the received list.""" # noqa: E501
# Ensure that the BPF buffer contains at least the header
len_bb = len(bpf_buffer)
if len_bb < 20: # Note: 20 == sizeof(struct bfp_hdr)
return
# Extract useful information from the BPF header
if FREEBSD:
# Unless we set BIOCSTSTAMP to something different than
# BPF_T_MICROTIME, we will get bpf_hdr on FreeBSD, which means
# that we'll get a struct timeval, which is time_t, suseconds_t.
# On i386 time_t is 32bit so the bh_tstamp will only be 8 bytes.
# We really want to set BIOCSTSTAMP to BPF_T_NANOTIME and be
# done with this and it always be 16?
if platform.machine() == "i386":
# struct bpf_hdr
bh_tstamp_offset = 8
else:
# struct bpf_hdr (64bit time_t) or struct bpf_xhdr
bh_tstamp_offset = 16
elif NETBSD:
# struct bpf_hdr or struct bpf_hdr32
bh_tstamp_offset = 16
else:
# struct bpf_hdr
bh_tstamp_offset = 8
# Parse the BPF header
bh_caplen = struct.unpack('I', bpf_buffer[bh_tstamp_offset:bh_tstamp_offset + 4])[0] # noqa: E501
next_offset = bh_tstamp_offset + 4
bh_datalen = struct.unpack('I', bpf_buffer[next_offset:next_offset + 4])[0] # noqa: E501
next_offset += 4
bh_hdrlen = struct.unpack('H', bpf_buffer[next_offset:next_offset + 2])[0] # noqa: E501
if bh_datalen == 0:
return
# Get and store the Scapy object
frame_str = bpf_buffer[bh_hdrlen:bh_hdrlen + bh_caplen]
self.received_frames.append(
(self.guessed_cls, frame_str, None)
)
# Extract the next frame
end = self.bpf_align(bh_hdrlen, bh_caplen)
if (len_bb - end) >= 20:
self.extract_frames(bpf_buffer[end:])
def recv_raw(self, x=BPF_BUFFER_LENGTH):
"""Receive a frame from the network"""
x = min(x, BPF_BUFFER_LENGTH)
if self.buffered_frames():
# Get a frame from the buffer
return self.get_frame()
# Get data from BPF
try:
bpf_buffer = os.read(self.ins, x)
except EnvironmentError as exc:
if exc.errno != errno.EAGAIN:
warning("BPF recv_raw()", exc_info=True)
return None, None, None
# Extract all frames from the BPF buffer
self.extract_frames(bpf_buffer)
return self.get_frame()
class L2bpfSocket(L2bpfListenSocket):
""""Scapy L2 BPF Super Socket"""
def send(self, x):
"""Send a frame"""
return os.write(self.outs, raw(x))
def nonblock_recv(self):
"""Non blocking receive"""
if self.buffered_frames():
# Get a frame from the buffer
return L2bpfListenSocket.recv(self)
# Set the non blocking flag, read from the socket, and unset the flag
self.set_nonblock(True)
pkt = L2bpfListenSocket.recv(self)
self.set_nonblock(False)
return pkt
class L3bpfSocket(L2bpfSocket):
def recv(self, x=BPF_BUFFER_LENGTH):
"""Receive on layer 3"""
r = SuperSocket.recv(self, x)
if r:
r.payload.time = r.time
return r.payload
return r
def send(self, pkt):
"""Send a packet"""
# Use the routing table to find the output interface
iff = pkt.route()[0]
if iff is None:
iff = conf.iface
# Assign the network interface to the BPF handle
if self.assigned_interface != iff:
try:
fcntl.ioctl(self.outs, BIOCSETIF, struct.pack("16s16x", iff.encode())) # noqa: E501
except IOError:
raise Scapy_Exception("BIOCSETIF failed on %s" % iff)
self.assigned_interface = iff
# Build the frame
frame = raw(self.guessed_cls() / pkt)
pkt.sent_time = time.time()
# Send the frame
L2bpfSocket.send(self, frame)
# Sockets manipulation functions
def isBPFSocket(obj):
"""Return True is obj is a BPF Super Socket"""
return isinstance(
obj,
(L2bpfListenSocket, L2bpfListenSocket, L3bpfSocket)
)
def bpf_select(fds_list, timeout=None):
"""A call to recv() can return several frames. This functions hides the fact
that some frames are read from the internal buffer."""
# Check file descriptors types
bpf_scks_buffered = list()
select_fds = list()
for tmp_fd in fds_list:
# Specific BPF sockets: get buffers status
if isBPFSocket(tmp_fd) and tmp_fd.buffered_frames():
bpf_scks_buffered.append(tmp_fd)
continue
# Regular file descriptors or empty BPF buffer
select_fds.append(tmp_fd)
if select_fds:
# Call select for sockets with empty buffers
if timeout is None:
timeout = 0.05
ready_list, _, _ = select(select_fds, [], [], timeout)
return bpf_scks_buffered + ready_list
else:
return bpf_scks_buffered

138
libs/scapy/arch/common.py Executable file
View file

@ -0,0 +1,138 @@
# This file is part of Scapy
# See http://www.secdev.org/projects/scapy for more information
# Copyright (C) Philippe Biondi <phil@secdev.org>
# This program is published under a GPLv2 license
"""
Functions common to different architectures
"""
import ctypes
import socket
import struct
import sys
import time
from scapy.consts import WINDOWS
from scapy.config import conf
from scapy.data import MTU, ARPHRD_TO_DLT
from scapy.error import Scapy_Exception
if not WINDOWS:
from fcntl import ioctl
# UTILS
def get_if(iff, cmd):
"""Ease SIOCGIF* ioctl calls"""
sck = socket.socket()
try:
return ioctl(sck, cmd, struct.pack("16s16x", iff.encode("utf8")))
finally:
sck.close()
def get_if_raw_hwaddr(iff):
"""Get the raw MAC address of a local interface.
This function uses SIOCGIFHWADDR calls, therefore only works
on some distros.
:param iff: the network interface name as a string
:returns: the corresponding raw MAC address
"""
from scapy.arch import SIOCGIFHWADDR
return struct.unpack("16xh6s8x", get_if(iff, SIOCGIFHWADDR))
# SOCKET UTILS
def _select_nonblock(sockets, remain=None):
"""This function is called during sendrecv() routine to select
the available sockets.
"""
# pcap sockets aren't selectable, so we return all of them
# and ask the selecting functions to use nonblock_recv instead of recv
def _sleep_nonblock_recv(self):
res = self.nonblock_recv()
if res is None:
time.sleep(conf.recv_poll_rate)
return res
# we enforce remain=None: don't wait.
return sockets, _sleep_nonblock_recv
# BPF HANDLERS
def compile_filter(filter_exp, iface=None, linktype=None,
promisc=False):
"""Asks libpcap to parse the filter, then build the matching
BPF bytecode.
:param iface: if provided, use the interface to compile
:param linktype: if provided, use the linktype to compile
"""
try:
from scapy.libs.winpcapy import (
PCAP_ERRBUF_SIZE,
pcap_open_live,
pcap_compile,
pcap_compile_nopcap,
pcap_close
)
from scapy.libs.structures import bpf_program
except OSError:
raise ImportError(
"libpcap is not available. Cannot compile filter !"
)
from ctypes import create_string_buffer
bpf = bpf_program()
bpf_filter = create_string_buffer(filter_exp.encode("utf8"))
if not linktype:
# Try to guess linktype to avoid root
if not iface:
if not conf.iface:
raise Scapy_Exception(
"Please provide an interface or linktype!"
)
if WINDOWS:
iface = conf.iface.pcap_name
else:
iface = conf.iface
# Try to guess linktype to avoid requiring root
try:
arphd = get_if_raw_hwaddr(iface)[0]
linktype = ARPHRD_TO_DLT.get(arphd)
except Exception:
# Failed to use linktype: use the interface
pass
if linktype is not None:
ret = pcap_compile_nopcap(
MTU, linktype, ctypes.byref(bpf), bpf_filter, 0, -1
)
elif iface:
err = create_string_buffer(PCAP_ERRBUF_SIZE)
iface = create_string_buffer(iface.encode("utf8"))
pcap = pcap_open_live(
iface, MTU, promisc, 0, err
)
error = bytes(bytearray(err)).strip(b"\x00")
if error:
raise OSError(error)
ret = pcap_compile(
pcap, ctypes.byref(bpf), bpf_filter, 0, -1
)
pcap_close(pcap)
if ret == -1:
raise Scapy_Exception(
"Failed to compile filter expression %s (%s)" % (filter_exp, ret)
)
if conf.use_pypy and sys.pypy_version_info <= (7, 3, 0):
# PyPy < 7.3.0 has a broken behavior
# https://bitbucket.org/pypy/pypy/issues/3114
return struct.pack(
'HL',
bpf.bf_len, ctypes.addressof(bpf.bf_insns.contents)
)
return bpf

642
libs/scapy/arch/linux.py Executable file
View file

@ -0,0 +1,642 @@
# This file is part of Scapy
# See http://www.secdev.org/projects/scapy for more information
# Copyright (C) Philippe Biondi <phil@secdev.org>
# This program is published under a GPLv2 license
"""
Linux specific functions.
"""
from __future__ import absolute_import
import array
from fcntl import ioctl
import os
from select import select
import socket
import struct
import time
import re
import subprocess
from scapy.compat import raw, plain_str
from scapy.consts import LINUX
import scapy.utils
import scapy.utils6
from scapy.packet import Packet, Padding
from scapy.config import conf
from scapy.data import MTU, ETH_P_ALL, SOL_PACKET, SO_ATTACH_FILTER, \
SO_TIMESTAMPNS
from scapy.supersocket import SuperSocket
from scapy.error import warning, Scapy_Exception, \
ScapyInvalidPlatformException, log_runtime
from scapy.arch.common import get_if, compile_filter
import scapy.modules.six as six
from scapy.modules.six.moves import range
from scapy.arch.common import get_if_raw_hwaddr # noqa: F401
# From bits/ioctls.h
SIOCGIFHWADDR = 0x8927 # Get hardware address
SIOCGIFADDR = 0x8915 # get PA address
SIOCGIFNETMASK = 0x891b # get network PA mask
SIOCGIFNAME = 0x8910 # get iface name
SIOCSIFLINK = 0x8911 # set iface channel
SIOCGIFCONF = 0x8912 # get iface list
SIOCGIFFLAGS = 0x8913 # get flags
SIOCSIFFLAGS = 0x8914 # set flags
SIOCGIFINDEX = 0x8933 # name -> if_index mapping
SIOCGIFCOUNT = 0x8938 # get number of devices
SIOCGSTAMP = 0x8906 # get packet timestamp (as a timeval)
# From if.h
IFF_UP = 0x1 # Interface is up.
IFF_BROADCAST = 0x2 # Broadcast address valid.
IFF_DEBUG = 0x4 # Turn on debugging.
IFF_LOOPBACK = 0x8 # Is a loopback net.
IFF_POINTOPOINT = 0x10 # Interface is point-to-point link.
IFF_NOTRAILERS = 0x20 # Avoid use of trailers.
IFF_RUNNING = 0x40 # Resources allocated.
IFF_NOARP = 0x80 # No address resolution protocol.
IFF_PROMISC = 0x100 # Receive all packets.
# From netpacket/packet.h
PACKET_ADD_MEMBERSHIP = 1
PACKET_DROP_MEMBERSHIP = 2
PACKET_RECV_OUTPUT = 3
PACKET_RX_RING = 5
PACKET_STATISTICS = 6
PACKET_MR_MULTICAST = 0
PACKET_MR_PROMISC = 1
PACKET_MR_ALLMULTI = 2
# From net/route.h
RTF_UP = 0x0001 # Route usable
RTF_REJECT = 0x0200
# From if_packet.h
PACKET_HOST = 0 # To us
PACKET_BROADCAST = 1 # To all
PACKET_MULTICAST = 2 # To group
PACKET_OTHERHOST = 3 # To someone else
PACKET_OUTGOING = 4 # Outgoing of any type
PACKET_LOOPBACK = 5 # MC/BRD frame looped back
PACKET_USER = 6 # To user space
PACKET_KERNEL = 7 # To kernel space
PACKET_AUXDATA = 8
PACKET_FASTROUTE = 6 # Fastrouted frame
# Unused, PACKET_FASTROUTE and PACKET_LOOPBACK are invisible to user space
# Utils
def get_if_raw_addr(iff):
try:
return get_if(iff, SIOCGIFADDR)[20:24]
except IOError:
return b"\0\0\0\0"
def get_if_list():
try:
f = open("/proc/net/dev", "rb")
except IOError:
try:
f.close()
except Exception:
pass
warning("Can't open /proc/net/dev !")
return []
lst = []
f.readline()
f.readline()
for line in f:
line = plain_str(line)
lst.append(line.split(":")[0].strip())
f.close()
return lst
def get_working_if():
"""
Return the name of the first network interfcace that is up.
"""
for i in get_if_list():
if i == conf.loopback_name:
continue
ifflags = struct.unpack("16xH14x", get_if(i, SIOCGIFFLAGS))[0]
if ifflags & IFF_UP:
return i
return conf.loopback_name
def attach_filter(sock, bpf_filter, iface):
"""
Compile bpf filter and attach it to a socket
:param sock: the python socket
:param bpf_filter: the bpf string filter to compile
:param iface: the interface used to compile
"""
bp = compile_filter(bpf_filter, iface)
sock.setsockopt(socket.SOL_SOCKET, SO_ATTACH_FILTER, bp)
def set_promisc(s, iff, val=1):
mreq = struct.pack("IHH8s", get_if_index(iff), PACKET_MR_PROMISC, 0, b"")
if val:
cmd = PACKET_ADD_MEMBERSHIP
else:
cmd = PACKET_DROP_MEMBERSHIP
s.setsockopt(SOL_PACKET, cmd, mreq)
def get_alias_address(iface_name, ip_mask, gw_str, metric):
"""
Get the correct source IP address of an interface alias
"""
# Detect the architecture
if scapy.consts.IS_64BITS:
offset, name_len = 16, 40
else:
offset, name_len = 32, 32
# Retrieve interfaces structures
sck = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
names = array.array('B', b'\0' * 4096)
ifreq = ioctl(sck.fileno(), SIOCGIFCONF,
struct.pack("iL", len(names), names.buffer_info()[0]))
# Extract interfaces names
out = struct.unpack("iL", ifreq)[0]
names = names.tobytes() if six.PY3 else names.tostring()
names = [names[i:i + offset].split(b'\0', 1)[0] for i in range(0, out, name_len)] # noqa: E501
# Look for the IP address
for ifname in names:
# Only look for a matching interface name
if not ifname.decode("utf8").startswith(iface_name):
continue
# Retrieve and convert addresses
ifreq = ioctl(sck, SIOCGIFADDR, struct.pack("16s16x", ifname))
ifaddr = struct.unpack(">I", ifreq[20:24])[0]
ifreq = ioctl(sck, SIOCGIFNETMASK, struct.pack("16s16x", ifname))
msk = struct.unpack(">I", ifreq[20:24])[0]
# Get the full interface name
ifname = plain_str(ifname)
if ':' in ifname:
ifname = ifname[:ifname.index(':')]
else:
continue
# Check if the source address is included in the network
if (ifaddr & msk) == ip_mask:
sck.close()
return (ifaddr & msk, msk, gw_str, ifname,
scapy.utils.ltoa(ifaddr), metric)
sck.close()
return
def read_routes():
try:
f = open("/proc/net/route", "rb")
except IOError:
warning("Can't open /proc/net/route !")
return []
routes = []
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
try:
ifreq = ioctl(s, SIOCGIFADDR, struct.pack("16s16x", conf.loopback_name.encode("utf8"))) # noqa: E501
addrfamily = struct.unpack("h", ifreq[16:18])[0]
if addrfamily == socket.AF_INET:
ifreq2 = ioctl(s, SIOCGIFNETMASK, struct.pack("16s16x", conf.loopback_name.encode("utf8"))) # noqa: E501
msk = socket.ntohl(struct.unpack("I", ifreq2[20:24])[0])
dst = socket.ntohl(struct.unpack("I", ifreq[20:24])[0]) & msk
ifaddr = scapy.utils.inet_ntoa(ifreq[20:24])
routes.append((dst, msk, "0.0.0.0", conf.loopback_name, ifaddr, 1)) # noqa: E501
else:
warning("Interface %s: unknown address family (%i)" % (conf.loopback_name, addrfamily)) # noqa: E501
except IOError as err:
if err.errno == 99:
warning("Interface %s: no address assigned" % conf.loopback_name) # noqa: E501
else:
warning("Interface %s: failed to get address config (%s)" % (conf.loopback_name, str(err))) # noqa: E501
for line in f.readlines()[1:]:
line = plain_str(line)
iff, dst, gw, flags, _, _, metric, msk, _, _, _ = line.split()
flags = int(flags, 16)
if flags & RTF_UP == 0:
continue
if flags & RTF_REJECT:
continue
try:
ifreq = ioctl(s, SIOCGIFADDR, struct.pack("16s16x", iff.encode("utf8"))) # noqa: E501
except IOError: # interface is present in routing tables but does not have any assigned IP # noqa: E501
ifaddr = "0.0.0.0"
ifaddr_int = 0
else:
addrfamily = struct.unpack("h", ifreq[16:18])[0]
if addrfamily == socket.AF_INET:
ifaddr = scapy.utils.inet_ntoa(ifreq[20:24])
ifaddr_int = struct.unpack("!I", ifreq[20:24])[0]
else:
warning("Interface %s: unknown address family (%i)", iff, addrfamily) # noqa: E501
continue
# Attempt to detect an interface alias based on addresses inconsistencies # noqa: E501
dst_int = socket.htonl(int(dst, 16)) & 0xffffffff
msk_int = socket.htonl(int(msk, 16)) & 0xffffffff
gw_str = scapy.utils.inet_ntoa(struct.pack("I", int(gw, 16)))
metric = int(metric)
if ifaddr_int & msk_int != dst_int:
tmp_route = get_alias_address(iff, dst_int, gw_str, metric)
if tmp_route:
routes.append(tmp_route)
else:
routes.append((dst_int, msk_int, gw_str, iff, ifaddr, metric))
else:
routes.append((dst_int, msk_int, gw_str, iff, ifaddr, metric))
f.close()
s.close()
return routes
############
# IPv6 #
############
def in6_getifaddr():
"""
Returns a list of 3-tuples of the form (addr, scope, iface) where
'addr' is the address of scope 'scope' associated to the interface
'iface'.
This is the list of all addresses of all interfaces available on
the system.
"""
ret = []
try:
fdesc = open("/proc/net/if_inet6", "rb")
except IOError:
return ret
for line in fdesc:
# addr, index, plen, scope, flags, ifname
tmp = plain_str(line).split()
addr = scapy.utils6.in6_ptop(
b':'.join(
struct.unpack('4s4s4s4s4s4s4s4s', tmp[0].encode())
).decode()
)
# (addr, scope, iface)
ret.append((addr, int(tmp[3], 16), tmp[5]))
fdesc.close()
return ret
def read_routes6():
try:
f = open("/proc/net/ipv6_route", "rb")
except IOError:
return []
# 1. destination network
# 2. destination prefix length
# 3. source network displayed
# 4. source prefix length
# 5. next hop
# 6. metric
# 7. reference counter (?!?)
# 8. use counter (?!?)
# 9. flags
# 10. device name
routes = []
def proc2r(p):
ret = struct.unpack('4s4s4s4s4s4s4s4s', p)
ret = b':'.join(ret).decode()
return scapy.utils6.in6_ptop(ret)
lifaddr = in6_getifaddr()
for line in f.readlines():
d, dp, _, _, nh, metric, rc, us, fl, dev = line.split()
metric = int(metric, 16)
fl = int(fl, 16)
dev = plain_str(dev)
if fl & RTF_UP == 0:
continue
if fl & RTF_REJECT:
continue
d = proc2r(d)
dp = int(dp, 16)
nh = proc2r(nh)
cset = [] # candidate set (possible source addresses)
if dev == conf.loopback_name:
if d == '::':
continue
cset = ['::1']
else:
devaddrs = (x for x in lifaddr if x[2] == dev)
cset = scapy.utils6.construct_source_candidate_set(d, dp, devaddrs)
if len(cset) != 0:
routes.append((d, dp, nh, dev, cset, metric))
f.close()
return routes
def get_if_index(iff):
return int(struct.unpack("I", get_if(iff, SIOCGIFINDEX)[16:20])[0])
if os.uname()[4] in ['x86_64', 'aarch64']:
def get_last_packet_timestamp(sock):
ts = ioctl(sock, SIOCGSTAMP, "1234567890123456")
s, us = struct.unpack("QQ", ts)
return s + us / 1000000.0
else:
def get_last_packet_timestamp(sock):
ts = ioctl(sock, SIOCGSTAMP, "12345678")
s, us = struct.unpack("II", ts)
return s + us / 1000000.0
def _flush_fd(fd):
if hasattr(fd, 'fileno'):
fd = fd.fileno()
while True:
r, w, e = select([fd], [], [], 0)
if r:
os.read(fd, MTU)
else:
break
def get_iface_mode(iface):
"""Return the interface mode.
params:
- iface: the iwconfig interface
"""
p = subprocess.Popen(["iwconfig", iface], stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
output, err = p.communicate()
match = re.search(br"mode:([a-zA-Z]*)", output.lower())
if match:
return plain_str(match.group(1))
return "unknown"
def set_iface_monitor(iface, monitor):
"""Sets the monitor mode (or remove it) from an interface.
params:
- iface: the iwconfig interface
- monitor: True if the interface should be set in monitor mode,
False if it should be in managed mode
"""
mode = get_iface_mode(iface)
if mode == "unknown":
warning("Could not parse iwconfig !")
current_monitor = mode == "monitor"
if monitor == current_monitor:
# Already correct
return True
s_mode = "monitor" if monitor else "managed"
def _check_call(commands):
p = subprocess.Popen(commands,
stderr=subprocess.PIPE,
stdout=subprocess.PIPE)
stdout, stderr = p.communicate()
if p.returncode != 0:
warning("%s failed !" % " ".join(commands))
return False
return True
if not _check_call(["ifconfig", iface, "down"]):
return False
if not _check_call(["iwconfig", iface, "mode", s_mode]):
return False
if not _check_call(["ifconfig", iface, "up"]):
return False
return True
class L2Socket(SuperSocket):
desc = "read/write packets at layer 2 using Linux PF_PACKET sockets"
def __init__(self, iface=None, type=ETH_P_ALL, promisc=None, filter=None,
nofilter=0, monitor=None):
self.iface = conf.iface if iface is None else iface
self.type = type
self.promisc = conf.sniff_promisc if promisc is None else promisc
if monitor is not None:
warning(
"The monitor argument is ineffective on native linux sockets."
" Use set_iface_monitor instead."
)
self.ins = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(type)) # noqa: E501
if not nofilter:
if conf.except_filter:
if filter:
filter = "(%s) and not (%s)" % (filter, conf.except_filter)
else:
filter = "not (%s)" % conf.except_filter
if filter is not None:
try:
attach_filter(self.ins, filter, iface)
except ImportError as ex:
warning("Cannot set filter: %s" % ex)
if self.promisc:
set_promisc(self.ins, self.iface)
self.ins.bind((self.iface, type))
_flush_fd(self.ins)
self.ins.setsockopt(
socket.SOL_SOCKET,
socket.SO_RCVBUF,
conf.bufsize
)
if not six.PY2:
# Receive Auxiliary Data (VLAN tags)
try:
self.ins.setsockopt(SOL_PACKET, PACKET_AUXDATA, 1)
self.ins.setsockopt(
socket.SOL_SOCKET,
SO_TIMESTAMPNS,
1
)
self.auxdata_available = True
except OSError:
# Note: Auxiliary Data is only supported since
# Linux 2.6.21
msg = "Your Linux Kernel does not support Auxiliary Data!"
log_runtime.info(msg)
if isinstance(self, L2ListenSocket):
self.outs = None
else:
self.outs = self.ins
self.outs.setsockopt(
socket.SOL_SOCKET,
socket.SO_SNDBUF,
conf.bufsize
)
sa_ll = self.ins.getsockname()
if sa_ll[3] in conf.l2types:
self.LL = conf.l2types[sa_ll[3]]
self.lvl = 2
elif sa_ll[1] in conf.l3types:
self.LL = conf.l3types[sa_ll[1]]
self.lvl = 3
else:
self.LL = conf.default_l2
self.lvl = 2
warning("Unable to guess type (interface=%s protocol=%#x family=%i). Using %s", sa_ll[0], sa_ll[1], sa_ll[3], self.LL.name) # noqa: E501
def close(self):
if self.closed:
return
try:
if self.promisc and self.ins:
set_promisc(self.ins, self.iface, 0)
except (AttributeError, OSError):
pass
SuperSocket.close(self)
def recv_raw(self, x=MTU):
"""Receives a packet, then returns a tuple containing (cls, pkt_data, time)""" # noqa: E501
pkt, sa_ll, ts = self._recv_raw(self.ins, x)
if self.outs and sa_ll[2] == socket.PACKET_OUTGOING:
return None, None, None
if ts is None:
ts = get_last_packet_timestamp(self.ins)
return self.LL, pkt, ts
def send(self, x):
try:
return SuperSocket.send(self, x)
except socket.error as msg:
if msg.errno == 22 and len(x) < conf.min_pkt_size:
padding = b"\x00" * (conf.min_pkt_size - len(x))
if isinstance(x, Packet):
return SuperSocket.send(self, x / Padding(load=padding))
else:
return SuperSocket.send(self, raw(x) + padding)
raise
class L2ListenSocket(L2Socket):
desc = "read packets at layer 2 using Linux PF_PACKET sockets. Also receives the packets going OUT" # noqa: E501
def send(self, x):
raise Scapy_Exception("Can't send anything with L2ListenSocket")
class L3PacketSocket(L2Socket):
desc = "read/write packets at layer 3 using Linux PF_PACKET sockets"
def recv(self, x=MTU):
pkt = SuperSocket.recv(self, x)
if pkt and self.lvl == 2:
pkt.payload.time = pkt.time
return pkt.payload
return pkt
def send(self, x):
iff = x.route()[0]
if iff is None:
iff = conf.iface
sdto = (iff, self.type)
self.outs.bind(sdto)
sn = self.outs.getsockname()
ll = lambda x: x
type_x = type(x)
if type_x in conf.l3types:
sdto = (iff, conf.l3types[type_x])
if sn[3] in conf.l2types:
ll = lambda x: conf.l2types[sn[3]]() / x
if self.lvl == 3 and type_x != self.LL:
warning("Incompatible L3 types detected using %s instead of %s !",
type_x, self.LL)
self.LL = type_x
sx = raw(ll(x))
x.sent_time = time.time()
try:
self.outs.sendto(sx, sdto)
except socket.error as msg:
if msg.errno == 22 and len(sx) < conf.min_pkt_size:
self.outs.send(sx + b"\x00" * (conf.min_pkt_size - len(sx)))
elif conf.auto_fragment and msg.errno == 90:
for p in x.fragment():
self.outs.sendto(raw(ll(p)), sdto)
else:
raise
class VEthPair(object):
"""
encapsulates a virtual Ethernet interface pair
"""
def __init__(self, iface_name, peer_name):
if not LINUX:
# ToDo: do we need a kernel version check here?
raise ScapyInvalidPlatformException(
'Virtual Ethernet interface pair only available on Linux'
)
self.ifaces = [iface_name, peer_name]
def iface(self):
return self.ifaces[0]
def peer(self):
return self.ifaces[1]
def setup(self):
"""
create veth pair links
:raises subprocess.CalledProcessError if operation fails
"""
subprocess.check_call(['ip', 'link', 'add', self.ifaces[0], 'type', 'veth', 'peer', 'name', self.ifaces[1]]) # noqa: E501
def destroy(self):
"""
remove veth pair links
:raises subprocess.CalledProcessError if operation fails
"""
subprocess.check_call(['ip', 'link', 'del', self.ifaces[0]])
def up(self):
"""
set veth pair links up
:raises subprocess.CalledProcessError if operation fails
"""
for idx in [0, 1]:
subprocess.check_call(["ip", "link", "set", self.ifaces[idx], "up"]) # noqa: E501
def down(self):
"""
set veth pair links down
:raises subprocess.CalledProcessError if operation fails
"""
for idx in [0, 1]:
subprocess.check_call(["ip", "link", "set", self.ifaces[idx], "down"]) # noqa: E501
def __enter__(self):
self.setup()
self.up()
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self.destroy()

394
libs/scapy/arch/pcapdnet.py Executable file
View file

@ -0,0 +1,394 @@
# This file is part of Scapy
# See http://www.secdev.org/projects/scapy for more information
# Copyright (C) Philippe Biondi <phil@secdev.org>
# This program is published under a GPLv2 license
"""
Packet sending and receiving libpcap/WinPcap.
"""
import os
import platform
import socket
import struct
import time
from scapy.automaton import SelectableObject
from scapy.arch.common import _select_nonblock
from scapy.compat import raw, plain_str
from scapy.config import conf
from scapy.consts import WINDOWS
from scapy.data import MTU, ETH_P_ALL
from scapy.pton_ntop import inet_ntop
from scapy.supersocket import SuperSocket
from scapy.error import Scapy_Exception, log_loading, warning
import scapy.consts
if not scapy.consts.WINDOWS:
from fcntl import ioctl
############
# COMMON #
############
# From BSD net/bpf.h
# BIOCIMMEDIATE = 0x80044270
BIOCIMMEDIATE = -2147204496
class _L2pcapdnetSocket(SuperSocket, SelectableObject):
nonblocking_socket = True
def __init__(self):
SelectableObject.__init__(self)
self.cls = None
def check_recv(self):
return True
def recv_raw(self, x=MTU):
"""Receives a packet, then returns a tuple containing (cls, pkt_data, time)""" # noqa: E501
if self.cls is None:
ll = self.ins.datalink()
if ll in conf.l2types:
self.cls = conf.l2types[ll]
else:
self.cls = conf.default_l2
warning(
"Unable to guess datalink type "
"(interface=%s linktype=%i). Using %s",
self.iface, ll, self.cls.name
)
ts, pkt = self.ins.next()
if pkt is None:
return None, None, None
return self.cls, pkt, ts
def nonblock_recv(self):
"""Receives and dissect a packet in non-blocking mode.
Note: on Windows, this won't do anything."""
self.ins.setnonblock(1)
p = self.recv(MTU)
self.ins.setnonblock(0)
return p
@staticmethod
def select(sockets, remain=None):
return _select_nonblock(sockets, remain=None)
##########
# PCAP #
##########
if conf.use_pcap:
if WINDOWS:
# Windows specific
NPCAP_PATH = os.environ["WINDIR"] + "\\System32\\Npcap"
from scapy.libs.winpcapy import pcap_setmintocopy
else:
from scapy.libs.winpcapy import pcap_get_selectable_fd
from ctypes import POINTER, byref, create_string_buffer, c_ubyte, cast
# Part of the Winpcapy integration was inspired by phaethon/scapy
# but he destroyed the commit history, so there is no link to that
try:
from scapy.libs.winpcapy import PCAP_ERRBUF_SIZE, pcap_if_t, \
sockaddr_in, sockaddr_in6, pcap_findalldevs, pcap_freealldevs, \
pcap_lib_version, pcap_close, \
pcap_open_live, pcap_pkthdr, \
pcap_next_ex, pcap_datalink, \
pcap_compile, pcap_setfilter, pcap_setnonblock, pcap_sendpacket, \
bpf_program
def load_winpcapy():
"""This functions calls libpcap ``pcap_findalldevs`` function,
and extracts and parse all the data scapy will need
to build the Interface List.
The date will be stored in ``conf.cache_iflist``, or accessible
with ``get_if_list()``
"""
err = create_string_buffer(PCAP_ERRBUF_SIZE)
devs = POINTER(pcap_if_t)()
if_list = {}
if pcap_findalldevs(byref(devs), err) < 0:
return
try:
p = devs
# Iterate through the different interfaces
while p:
name = plain_str(p.contents.name) # GUID
description = plain_str(p.contents.description) # NAME
flags = p.contents.flags # FLAGS
ips = []
a = p.contents.addresses
while a:
# IPv4 address
family = a.contents.addr.contents.sa_family
ap = a.contents.addr
if family == socket.AF_INET:
val = cast(ap, POINTER(sockaddr_in))
val = val.contents.sin_addr[:]
elif family == socket.AF_INET6:
val = cast(ap, POINTER(sockaddr_in6))
val = val.contents.sin6_addr[:]
else:
# Unknown address family
# (AF_LINK isn't a thing on Windows)
a = a.contents.next
continue
addr = inet_ntop(family, bytes(bytearray(val)))
if addr != "0.0.0.0":
ips.append(addr)
a = a.contents.next
if_list[name] = (description, ips, flags)
p = p.contents.next
conf.cache_iflist = if_list
except Exception:
raise
finally:
pcap_freealldevs(devs)
except OSError:
conf.use_pcap = False
if WINDOWS:
if conf.interactive:
log_loading.critical(
"Npcap/Winpcap is not installed ! See "
"https://scapy.readthedocs.io/en/latest/installation.html#windows" # noqa: E501
)
else:
if conf.interactive:
log_loading.critical(
"Libpcap is not installed!"
)
else:
if WINDOWS:
# Detect Pcap version: check for Npcap
version = pcap_lib_version()
if b"winpcap" in version.lower():
if os.path.exists(NPCAP_PATH + "\\wpcap.dll"):
warning("Winpcap is installed over Npcap. "
"Will use Winpcap (see 'Winpcap/Npcap conflicts' "
"in Scapy's docs)")
elif platform.release() != "XP":
warning("WinPcap is now deprecated (not maintained). "
"Please use Npcap instead")
elif b"npcap" in version.lower():
conf.use_npcap = True
conf.loopback_name = conf.loopback_name = "Npcap Loopback Adapter" # noqa: E501
if conf.use_pcap:
def get_if_list():
"""Returns all pcap names"""
if not conf.cache_iflist:
load_winpcapy()
return list(conf.cache_iflist)
class _PcapWrapper_libpcap: # noqa: F811
"""Wrapper for the libpcap calls"""
def __init__(self, device, snaplen, promisc, to_ms, monitor=None):
self.errbuf = create_string_buffer(PCAP_ERRBUF_SIZE)
self.iface = create_string_buffer(device.encode("utf8"))
self.dtl = None
if monitor:
if WINDOWS and not conf.use_npcap:
raise OSError("On Windows, this feature requires NPcap !")
# Npcap-only functions
from scapy.libs.winpcapy import pcap_create, \
pcap_set_snaplen, pcap_set_promisc, \
pcap_set_timeout, pcap_set_rfmon, pcap_activate
self.pcap = pcap_create(self.iface, self.errbuf)
pcap_set_snaplen(self.pcap, snaplen)
pcap_set_promisc(self.pcap, promisc)
pcap_set_timeout(self.pcap, to_ms)
if pcap_set_rfmon(self.pcap, 1) != 0:
warning("Could not set monitor mode")
if pcap_activate(self.pcap) != 0:
raise OSError("Could not activate the pcap handler")
else:
self.pcap = pcap_open_live(self.iface,
snaplen, promisc, to_ms,
self.errbuf)
error = bytes(bytearray(self.errbuf)).strip(b"\x00")
if error:
raise OSError(error)
if WINDOWS:
# Winpcap/Npcap exclusive: make every packet to be instantly
# returned, and not buffered within Winpcap/Npcap
pcap_setmintocopy(self.pcap, 0)
self.header = POINTER(pcap_pkthdr)()
self.pkt_data = POINTER(c_ubyte)()
self.bpf_program = bpf_program()
def next(self):
"""
Returns the next packet as the tuple
(timestamp, raw_packet)
"""
c = pcap_next_ex(
self.pcap,
byref(self.header),
byref(self.pkt_data)
)
if not c > 0:
return None, None
ts = self.header.contents.ts.tv_sec + float(self.header.contents.ts.tv_usec) / 1e6 # noqa: E501
pkt = bytes(bytearray(self.pkt_data[:self.header.contents.len]))
return ts, pkt
__next__ = next
def datalink(self):
"""Wrapper around pcap_datalink"""
if self.dtl is None:
self.dtl = pcap_datalink(self.pcap)
return self.dtl
def fileno(self):
if WINDOWS:
log_loading.error("Cannot get selectable PCAP fd on Windows")
return -1
else:
# This does not exist under Windows
return pcap_get_selectable_fd(self.pcap)
def setfilter(self, f):
filter_exp = create_string_buffer(f.encode("utf8"))
if pcap_compile(self.pcap, byref(self.bpf_program), filter_exp, 0, -1) == -1: # noqa: E501
log_loading.error("Could not compile filter expression %s", f)
return False
else:
if pcap_setfilter(self.pcap, byref(self.bpf_program)) == -1:
log_loading.error("Could not install filter %s", f)
return False
return True
def setnonblock(self, i):
pcap_setnonblock(self.pcap, i, self.errbuf)
def send(self, x):
pcap_sendpacket(self.pcap, x, len(x))
def close(self):
pcap_close(self.pcap)
open_pcap = _PcapWrapper_libpcap
# pcap sockets
class L2pcapListenSocket(_L2pcapdnetSocket):
desc = "read packets at layer 2 using libpcap"
def __init__(self, iface=None, type=ETH_P_ALL, promisc=None, filter=None, monitor=None): # noqa: E501
super(L2pcapListenSocket, self).__init__()
self.type = type
self.outs = None
self.iface = iface
if iface is None:
iface = conf.iface
if promisc is None:
promisc = conf.sniff_promisc
self.promisc = promisc
# Note: Timeout with Winpcap/Npcap
# The 4th argument of open_pcap corresponds to timeout. In an ideal world, we would # noqa: E501
# set it to 0 ==> blocking pcap_next_ex.
# However, the way it is handled is very poor, and result in a jerky packet stream. # noqa: E501
# To fix this, we set 100 and the implementation under windows is slightly different, as # noqa: E501
# everything is always received as non-blocking
self.ins = open_pcap(iface, MTU, self.promisc, 100,
monitor=monitor)
try:
ioctl(self.ins.fileno(), BIOCIMMEDIATE, struct.pack("I", 1))
except Exception:
pass
if type == ETH_P_ALL: # Do not apply any filter if Ethernet type is given # noqa: E501
if conf.except_filter:
if filter:
filter = "(%s) and not (%s)" % (filter, conf.except_filter) # noqa: E501
else:
filter = "not (%s)" % conf.except_filter
if filter:
self.ins.setfilter(filter)
def send(self, x):
raise Scapy_Exception("Can't send anything with L2pcapListenSocket") # noqa: E501
class L2pcapSocket(_L2pcapdnetSocket):
desc = "read/write packets at layer 2 using only libpcap"
def __init__(self, iface=None, type=ETH_P_ALL, promisc=None, filter=None, nofilter=0, # noqa: E501
monitor=None):
super(L2pcapSocket, self).__init__()
if iface is None:
iface = conf.iface
self.iface = iface
if promisc is None:
promisc = 0
self.promisc = promisc
# See L2pcapListenSocket for infos about this line
self.ins = open_pcap(iface, MTU, self.promisc, 100,
monitor=monitor)
self.outs = self.ins
try:
ioctl(self.ins.fileno(), BIOCIMMEDIATE, struct.pack("I", 1))
except Exception:
pass
if nofilter:
if type != ETH_P_ALL: # PF_PACKET stuff. Need to emulate this for pcap # noqa: E501
filter = "ether proto %i" % type
else:
filter = None
else:
if conf.except_filter:
if filter:
filter = "(%s) and not (%s)" % (filter, conf.except_filter) # noqa: E501
else:
filter = "not (%s)" % conf.except_filter
if type != ETH_P_ALL: # PF_PACKET stuff. Need to emulate this for pcap # noqa: E501
if filter:
filter = "(ether proto %i) and (%s)" % (type, filter)
else:
filter = "ether proto %i" % type
if filter:
self.ins.setfilter(filter)
def send(self, x):
sx = raw(x)
try:
x.sent_time = time.time()
except AttributeError:
pass
self.outs.send(sx)
class L3pcapSocket(L2pcapSocket):
desc = "read/write packets at layer 3 using only libpcap"
def recv(self, x=MTU):
r = L2pcapSocket.recv(self, x)
if r:
r.payload.time = r.time
return r.payload
return r
def send(self, x):
# Makes send detects when it should add Loopback(), Dot11... instead of Ether() # noqa: E501
ll = self.ins.datalink()
if ll in conf.l2types:
cls = conf.l2types[ll]
else:
cls = conf.default_l2
warning("Unable to guess datalink type (interface=%s linktype=%i). Using %s", self.iface, ll, cls.name) # noqa: E501
sx = raw(cls() / x)
try:
x.sent_time = time.time()
except AttributeError:
pass
self.outs.send(sx)
else:
# No libpcap installed
get_if_list = lambda: []
if WINDOWS:
NPCAP_PATH = ""

35
libs/scapy/arch/solaris.py Executable file
View file

@ -0,0 +1,35 @@
# This file is part of Scapy
# See http://www.secdev.org/projects/scapy for more information
# Copyright (C) Philippe Biondi <phil@secdev.org>
# This program is published under a GPLv2 license
"""
Customization for the Solaris operation system.
"""
import socket
from scapy.config import conf
conf.use_pcap = True
# IPPROTO_GRE is missing on Solaris
socket.IPPROTO_GRE = 47
# From sys/sockio.h and net/if.h
SIOCGIFHWADDR = 0xc02069b9 # Get hardware address
from scapy.arch.pcapdnet import * # noqa: F401, F403
from scapy.arch.unix import * # noqa: F401, F403
from scapy.arch.common import get_if_raw_hwaddr # noqa: F401, F403
def get_working_if():
"""Return an interface that works"""
try:
# return the interface associated with the route with smallest
# mask (route by default if it exists)
iface = min(conf.route.routes, key=lambda x: x[1])[3]
except ValueError:
# no route
iface = conf.loopback_name
return iface

355
libs/scapy/arch/unix.py Executable file
View file

@ -0,0 +1,355 @@
# This file is part of Scapy
# See http://www.secdev.org/projects/scapy for more information
# Copyright (C) Philippe Biondi <phil@secdev.org>
# This program is published under a GPLv2 license
"""
Common customizations for all Unix-like operating systems other than Linux
"""
import os
import socket
import scapy.config
import scapy.utils
from scapy.arch import get_if_addr
from scapy.config import conf
from scapy.consts import FREEBSD, NETBSD, OPENBSD, SOLARIS
from scapy.error import warning, log_interactive
from scapy.pton_ntop import inet_pton
from scapy.utils6 import in6_getscope, construct_source_candidate_set
from scapy.utils6 import in6_isvalid, in6_ismlladdr, in6_ismnladdr
##################
# Routes stuff #
##################
def _guess_iface_name(netif):
"""
We attempt to guess the name of interfaces that are truncated from the
output of ifconfig -l.
If there is only one possible candidate matching the interface name then we
return it.
If there are none or more, then we return None.
"""
with os.popen('%s -l' % conf.prog.ifconfig) as fdesc:
ifaces = fdesc.readline().strip().split(' ')
matches = [iface for iface in ifaces if iface.startswith(netif)]
if len(matches) == 1:
return matches[0]
return None
def read_routes():
"""Return a list of IPv4 routes than can be used by Scapy.
This function parses netstat.
"""
if SOLARIS:
f = os.popen("netstat -rvn -f inet")
elif FREEBSD:
f = os.popen("netstat -rnW") # -W to handle long interface names
else:
f = os.popen("netstat -rn -f inet")
ok = 0
mtu_present = False
prio_present = False
refs_present = False
use_present = False
routes = []
pending_if = []
for line in f.readlines():
if not line:
break
line = line.strip().lower()
if line.find("----") >= 0: # a separation line
continue
if not ok:
if line.find("destination") >= 0:
ok = 1
mtu_present = "mtu" in line
prio_present = "prio" in line
refs_present = "ref" in line # There is no s on Solaris
use_present = "use" in line
continue
if not line:
break
rt = line.split()
if SOLARIS:
dest, netmask, gw, netif = rt[:4]
flg = rt[4 + mtu_present + refs_present]
else:
dest, gw, flg = rt[:3]
locked = OPENBSD and rt[6] == "l"
offset = mtu_present + prio_present + refs_present + locked
offset += use_present
netif = rt[3 + offset]
if flg.find("lc") >= 0:
continue
elif dest == "default":
dest = 0
netmask = 0
elif SOLARIS:
dest = scapy.utils.atol(dest)
netmask = scapy.utils.atol(netmask)
else:
if "/" in dest:
dest, netmask = dest.split("/")
netmask = scapy.utils.itom(int(netmask))
else:
netmask = scapy.utils.itom((dest.count(".") + 1) * 8)
dest += ".0" * (3 - dest.count("."))
dest = scapy.utils.atol(dest)
# XXX: TODO: add metrics for unix.py (use -e option on netstat)
metric = 1
if "g" not in flg:
gw = '0.0.0.0'
if netif is not None:
try:
ifaddr = get_if_addr(netif)
routes.append((dest, netmask, gw, netif, ifaddr, metric))
except OSError as exc:
if exc.message == 'Device not configured':
# This means the interface name is probably truncated by
# netstat -nr. We attempt to guess it's name and if not we
# ignore it.
guessed_netif = _guess_iface_name(netif)
if guessed_netif is not None:
ifaddr = get_if_addr(guessed_netif)
routes.append((dest, netmask, gw, guessed_netif, ifaddr, metric)) # noqa: E501
else:
warning("Could not guess partial interface name: %s", netif) # noqa: E501
else:
raise
else:
pending_if.append((dest, netmask, gw))
f.close()
# On Solaris, netstat does not provide output interfaces for some routes
# We need to parse completely the routing table to route their gw and
# know their output interface
for dest, netmask, gw in pending_if:
gw_l = scapy.utils.atol(gw)
max_rtmask, gw_if, gw_if_addr, = 0, None, None
for rtdst, rtmask, _, rtif, rtaddr in routes[:]:
if gw_l & rtmask == rtdst:
if rtmask >= max_rtmask:
max_rtmask = rtmask
gw_if = rtif
gw_if_addr = rtaddr
# XXX: TODO add metrics
metric = 1
if gw_if:
routes.append((dest, netmask, gw, gw_if, gw_if_addr, metric))
else:
warning("Did not find output interface to reach gateway %s", gw)
return routes
############
# IPv6 #
############
def _in6_getifaddr(ifname):
"""
Returns a list of IPv6 addresses configured on the interface ifname.
"""
# Get the output of ifconfig
try:
f = os.popen("%s %s" % (conf.prog.ifconfig, ifname))
except OSError:
log_interactive.warning("Failed to execute ifconfig.")
return []
# Iterate over lines and extract IPv6 addresses
ret = []
for line in f:
if "inet6" in line:
addr = line.rstrip().split(None, 2)[1] # The second element is the IPv6 address # noqa: E501
else:
continue
if '%' in line: # Remove the interface identifier if present
addr = addr.split("%", 1)[0]
# Check if it is a valid IPv6 address
try:
inet_pton(socket.AF_INET6, addr)
except (socket.error, ValueError):
continue
# Get the scope and keep the address
scope = in6_getscope(addr)
ret.append((addr, scope, ifname))
f.close()
return ret
def in6_getifaddr():
"""
Returns a list of 3-tuples of the form (addr, scope, iface) where
'addr' is the address of scope 'scope' associated to the interface
'iface'.
This is the list of all addresses of all interfaces available on
the system.
"""
# List all network interfaces
if OPENBSD or SOLARIS:
if SOLARIS:
cmd = "%s -a6"
else:
cmd = "%s"
try:
f = os.popen(cmd % conf.prog.ifconfig)
except OSError:
log_interactive.warning("Failed to execute ifconfig.")
return []
# Get the list of network interfaces
splitted_line = []
for l in f:
if "flags" in l:
iface = l.split()[0].rstrip(':')
splitted_line.append(iface)
else: # FreeBSD, NetBSD or Darwin
try:
f = os.popen("%s -l" % conf.prog.ifconfig)
except OSError:
log_interactive.warning("Failed to execute ifconfig.")
return []
# Get the list of network interfaces
splitted_line = f.readline().rstrip().split()
ret = []
for i in splitted_line:
ret += _in6_getifaddr(i)
f.close()
return ret
def read_routes6():
"""Return a list of IPv6 routes than can be used by Scapy.
This function parses netstat.
"""
# Call netstat to retrieve IPv6 routes
fd_netstat = os.popen("netstat -rn -f inet6")
# List interfaces IPv6 addresses
lifaddr = in6_getifaddr()
if not lifaddr:
fd_netstat.close()
return []
# Routes header information
got_header = False
mtu_present = False
prio_present = False
# Parse the routes
routes = []
for line in fd_netstat.readlines():
# Parse the routes header and try to identify extra columns
if not got_header:
if "Destination" == line[:11]:
got_header = True
mtu_present = "Mtu" in line
prio_present = "Prio" in line
continue
# Parse a route entry according to the operating system
splitted_line = line.split()
if OPENBSD or NETBSD:
index = 5 + mtu_present + prio_present
if len(splitted_line) < index:
warning("Not enough columns in route entry !")
continue
destination, next_hop, flags = splitted_line[:3]
dev = splitted_line[index]
else:
# FREEBSD or DARWIN
if len(splitted_line) < 4:
warning("Not enough columns in route entry !")
continue
destination, next_hop, flags, dev = splitted_line[:4]
# XXX: TODO: add metrics for unix.py (use -e option on netstat)
metric = 1
# Check flags
if "U" not in flags: # usable route
continue
if "R" in flags: # Host or net unreachable
continue
if "m" in flags: # multicast address
# Note: multicast routing is handled in Route6.route()
continue
# Replace link with the default route in next_hop
if "link" in next_hop:
next_hop = "::"
# Default prefix length
destination_plen = 128
# Extract network interface from the zone id
if '%' in destination:
destination, dev = destination.split('%')
if '/' in dev:
# Example: fe80::%lo0/64 ; dev = "lo0/64"
dev, destination_plen = dev.split('/')
if '%' in next_hop:
next_hop, dev = next_hop.split('%')
# Ensure that the next hop is a valid IPv6 address
if not in6_isvalid(next_hop):
# Note: the 'Gateway' column might contain a MAC address
next_hop = "::"
# Modify parsed routing entries
# Note: these rules are OS specific and may evolve over time
if destination == "default":
destination, destination_plen = "::", 0
elif '/' in destination:
# Example: fe80::/10
destination, destination_plen = destination.split('/')
if '/' in dev:
# Example: ff02::%lo0/32 ; dev = "lo0/32"
dev, destination_plen = dev.split('/')
# Check route entries parameters consistency
if not in6_isvalid(destination):
warning("Invalid destination IPv6 address in route entry !")
continue
try:
destination_plen = int(destination_plen)
except Exception:
warning("Invalid IPv6 prefix length in route entry !")
continue
if in6_ismlladdr(destination) or in6_ismnladdr(destination):
# Note: multicast routing is handled in Route6.route()
continue
if conf.loopback_name in dev:
# Handle ::1 separately
cset = ["::1"]
next_hop = "::"
else:
# Get possible IPv6 source addresses
devaddrs = (x for x in lifaddr if x[2] == dev)
cset = construct_source_candidate_set(destination, destination_plen, devaddrs) # noqa: E501
if len(cset):
routes.append((destination, destination_plen, next_hop, dev, cset, metric)) # noqa: E501
fd_netstat.close()
return routes

File diff suppressed because it is too large Load diff

219
libs/scapy/arch/windows/native.py Executable file
View file

@ -0,0 +1,219 @@
# This file is part of Scapy
# See http://www.secdev.org/projects/scapy for more information
# Copyright (C) Philippe Biondi <phil@secdev.org>
# Copyright (C) Gabriel Potter <gabriel@potter.fr>
# This program is published under a GPLv2 license
"""
Native Microsoft Windows sockets (L3 only)
## Notice: ICMP packets
DISCLAIMER: Please use Npcap/Winpcap to send/receive ICMP. It is going to work.
Below is some additional information, mainly implemented in a testing purpose.
When in native mode, everything goes through the Windows kernel.
This firstly requires that the Firewall is open. Be sure it allows ICMPv4/6
packets in and out.
Windows may drop packets that it finds wrong. for instance, answers to
ICMP packets with id=0 or seq=0 may be dropped. It means that sent packets
should (most of the time) be perfectly built.
A perfectly built ICMP req packet on Windows means that its id is 1, its
checksum (IP and ICMP) are correctly built, but also that its seq number is
in the "allowed range".
In fact, every time an ICMP packet is sent on Windows, a global sequence
number is increased, which is only reset at boot time. The seq number of the
received ICMP packet must be in the range [current, current + 3] to be valid,
and received by the socket. The current number is quite hard to get, thus we
provide in this module the get_actual_icmp_seq() function.
Example:
>>> conf.use_pcap = False
>>> a = conf.L3socket()
# This will (most likely) work:
>>> current = get_current_icmp_seq()
>>> a.sr(IP(dst="www.google.com", ttl=128)/ICMP(id=1, seq=current))
# This won't:
>>> a.sr(IP(dst="www.google.com", ttl=128)/ICMP())
PS: on computers where the firewall isn't open, Windows temporarily opens it
when using the `ping` util from cmd.exe. One can first call a ping on cmd,
then do custom calls through the socket using get_current_icmp_seq(). See
the tests (windows.uts) for an example.
"""
import io
import os
import socket
import subprocess
import time
from scapy.automaton import SelectableObject
from scapy.arch.common import _select_nonblock
from scapy.arch.windows.structures import GetIcmpStatistics
from scapy.compat import raw
from scapy.config import conf
from scapy.data import MTU
from scapy.error import Scapy_Exception, warning
from scapy.supersocket import SuperSocket
# Watch out for import loops (inet...)
class L3WinSocket(SuperSocket, SelectableObject):
desc = "a native Layer 3 (IPv4) raw socket under Windows"
nonblocking_socket = True
__slots__ = ["promisc", "cls", "ipv6", "proto"]
def __init__(self, iface=None, proto=socket.IPPROTO_IP,
ttl=128, ipv6=False, promisc=True, **kwargs):
from scapy.layers.inet import IP
from scapy.layers.inet6 import IPv6
for kwarg in kwargs:
warning("Dropping unsupported option: %s" % kwarg)
af = socket.AF_INET6 if ipv6 else socket.AF_INET
self.proto = proto
if ipv6:
from scapy.arch import get_if_addr6
self.host_ip6 = get_if_addr6(conf.iface) or "::1"
if proto == socket.IPPROTO_IP:
# We'll restrict ourselves to UDP, as TCP isn't bindable
# on AF_INET6
self.proto = socket.IPPROTO_UDP
# On Windows, with promisc=False, you won't get much
self.ipv6 = ipv6
self.cls = IPv6 if ipv6 else IP
self.promisc = promisc
# Notes:
# - IPPROTO_RAW only works to send packets.
# - IPPROTO_IPV6 exists in MSDN docs, but using it will result in
# no packets being received. Same for its options (IPV6_HDRINCL...)
# However, using IPPROTO_IP with AF_INET6 will still receive
# the IPv6 packets
try:
self.ins = socket.socket(af,
socket.SOCK_RAW,
self.proto)
self.outs = socket.socket(af,
socket.SOCK_RAW,
socket.IPPROTO_RAW)
except OSError as e:
if e.errno == 10013:
raise OSError("Windows native L3 Raw sockets are only "
"usable as administrator ! "
"Install Winpcap/Npcap to workaround !")
raise
self.ins.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.outs.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.ins.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 2**30)
self.outs.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 2**30)
# IOCTL Include IP headers
self.ins.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
self.outs.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
# set TTL
self.ins.setsockopt(socket.IPPROTO_IP, socket.IP_TTL, ttl)
self.outs.setsockopt(socket.IPPROTO_IP, socket.IP_TTL, ttl)
# Bind on all ports
iface = iface or conf.iface
host = iface.ip if iface.ip else socket.gethostname()
self.ins.bind((host, 0))
self.ins.setblocking(False)
# Get as much data as possible: reduce what is cropped
if ipv6:
try: # Not all Windows versions
self.ins.setsockopt(socket.IPPROTO_IPV6,
socket.IPV6_RECVTCLASS, 1)
self.ins.setsockopt(socket.IPPROTO_IPV6,
socket.IPV6_HOPLIMIT, 1)
except (OSError, socket.error):
pass
else:
try: # Not Windows XP
self.ins.setsockopt(socket.IPPROTO_IP,
socket.IP_RECVDSTADDR, 1)
except (OSError, socket.error):
pass
try: # Windows 10+ recent builds only
self.ins.setsockopt(socket.IPPROTO_IP, socket.IP_RECVTTL, 1)
except (OSError, socket.error):
pass
if promisc:
# IOCTL Receive all packets
self.ins.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON)
def send(self, x):
data = raw(x)
if self.cls not in x:
raise Scapy_Exception("L3WinSocket can only send IP/IPv6 packets !"
" Install Npcap/Winpcap to send more")
dst_ip = str(x[self.cls].dst)
self.outs.sendto(data, (dst_ip, 0))
def nonblock_recv(self, x=MTU):
return self.recv()
# https://docs.microsoft.com/en-us/windows/desktop/winsock/tcp-ip-raw-sockets-2 # noqa: E501
# - For IPv4 (address family of AF_INET), an application receives the IP
# header at the front of each received datagram regardless of the
# IP_HDRINCL socket option.
# - For IPv6 (address family of AF_INET6), an application receives
# everything after the last IPv6 header in each received datagram
# regardless of the IPV6_HDRINCL socket option. The application does
# not receive any IPv6 headers using a raw socket.
def recv_raw(self, x=MTU):
try:
data, address = self.ins.recvfrom(x)
except io.BlockingIOError:
return None, None, None
from scapy.layers.inet import IP
from scapy.layers.inet6 import IPv6
if self.ipv6:
# AF_INET6 does not return the IPv6 header. Let's build it
# (host, port, flowinfo, scopeid)
host, _, flowinfo, _ = address
header = raw(IPv6(src=host,
dst=self.host_ip6,
fl=flowinfo,
nh=self.proto, # fixed for AF_INET6
plen=len(data)))
return IPv6, header + data, time.time()
else:
return IP, data, time.time()
def check_recv(self):
return True
def close(self):
if not self.closed and self.promisc:
self.ins.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF)
super(L3WinSocket, self).close()
@staticmethod
def select(sockets, remain=None):
return _select_nonblock(sockets, remain=remain)
class L3WinSocket6(L3WinSocket):
desc = "a native Layer 3 (IPv6) raw socket under Windows"
def __init__(self, **kwargs):
super(L3WinSocket6, self).__init__(ipv6=True, **kwargs)
def open_icmp_firewall(host):
"""Temporarily open the ICMP firewall. Tricks Windows into allowing
ICMP packets for a short period of time (~ 1 minute)"""
# We call ping with a timeout of 1ms: will return instantly
with open(os.devnull, 'wb') as DEVNULL:
return subprocess.Popen("ping -4 -w 1 -n 1 %s" % host,
shell=True,
stdout=DEVNULL,
stderr=DEVNULL).wait()
def get_current_icmp_seq():
"""See help(scapy.arch.windows.native) for more information.
Returns the current ICMP seq number."""
return GetIcmpStatistics()['stats']['icmpOutStats']['dwEchos']

View file

@ -0,0 +1,585 @@
# This file is part of Scapy
# See http://www.secdev.org/projects/scapy for more information
# Copyright (C) Philippe Biondi <phil@secdev.org>
# Copyright (C) Gabriel Potter <gabriel@potter.fr>
# This program is published under a GPLv2 license
# flake8: noqa E266
# (We keep comment boxes, it's then one-line comments)
"""
C API calls to Windows DLLs
"""
import ctypes
import ctypes.wintypes
from ctypes import Structure, POINTER, byref, create_string_buffer, WINFUNCTYPE
from scapy.config import conf
from scapy.consts import WINDOWS_XP
ANY_SIZE = 65500 # FIXME quite inefficient :/
AF_UNSPEC = 0
NO_ERROR = 0x0
CHAR = ctypes.c_char
DWORD = ctypes.wintypes.DWORD
BOOL = ctypes.wintypes.BOOL
BOOLEAN = ctypes.wintypes.BOOLEAN
ULONG = ctypes.wintypes.ULONG
ULONGLONG = ctypes.c_ulonglong
HANDLE = ctypes.wintypes.HANDLE
LPWSTR = ctypes.wintypes.LPWSTR
VOID = ctypes.c_void_p
INT = ctypes.c_int
UINT = ctypes.wintypes.UINT
UINT8 = ctypes.c_uint8
UINT16 = ctypes.c_uint16
UINT32 = ctypes.c_uint32
UINT64 = ctypes.c_uint64
BYTE = ctypes.c_byte
UCHAR = UBYTE = ctypes.c_ubyte
SHORT = ctypes.c_short
USHORT = ctypes.c_ushort
# UTILS
def _resolve_list(list_obj):
current = list_obj
_list = []
while current and hasattr(current, "contents"):
_list.append(_struct_to_dict(current.contents))
current = current.contents.next
return _list
def _struct_to_dict(struct_obj):
results = {}
for fname, ctype in struct_obj.__class__._fields_:
val = getattr(struct_obj, fname)
if fname == "next":
# Already covered by the trick below
continue
if issubclass(ctype, (Structure, ctypes.Union)):
results[fname] = _struct_to_dict(val)
elif val and hasattr(val, "contents"):
# Let's resolve recursive pointers
if hasattr(val.contents, "next"):
results[fname] = _resolve_list(val)
else:
results[fname] = val
else:
results[fname] = val
return results
##############################
####### WinAPI handles #######
##############################
_winapi_SetConsoleTitle = ctypes.windll.kernel32.SetConsoleTitleW
_winapi_SetConsoleTitle.restype = BOOL
_winapi_SetConsoleTitle.argtypes = [LPWSTR]
def _windows_title(title=None):
"""Updates the terminal title with the default one or with `title`
if provided."""
if conf.interactive:
_winapi_SetConsoleTitle(title or "Scapy v{}".format(conf.version))
SC_HANDLE = HANDLE
class SERVICE_STATUS(Structure):
"""https://docs.microsoft.com/en-us/windows/desktop/api/winsvc/ns-winsvc-_service_status""" # noqa: E501
_fields_ = [("dwServiceType", DWORD),
("dwCurrentState", DWORD),
("dwControlsAccepted", DWORD),
("dwWin32ExitCode", DWORD),
("dwServiceSpecificExitCode", DWORD),
("dwCheckPoint", DWORD),
("dwWaitHint", DWORD)]
OpenServiceW = ctypes.windll.Advapi32.OpenServiceW
OpenServiceW.restype = SC_HANDLE
OpenServiceW.argtypes = [SC_HANDLE, LPWSTR, DWORD]
CloseServiceHandle = ctypes.windll.Advapi32.CloseServiceHandle
CloseServiceHandle.restype = BOOL
CloseServiceHandle.argtypes = [SC_HANDLE]
OpenSCManagerW = ctypes.windll.Advapi32.OpenSCManagerW
OpenSCManagerW.restype = SC_HANDLE
OpenSCManagerW.argtypes = [LPWSTR, LPWSTR, DWORD]
QueryServiceStatus = ctypes.windll.Advapi32.QueryServiceStatus
QueryServiceStatus.restype = BOOL
QueryServiceStatus.argtypes = [SC_HANDLE, POINTER(SERVICE_STATUS)]
def get_service_status(service):
"""Returns content of QueryServiceStatus for a service"""
SERVICE_QUERY_STATUS = 0x0004
schSCManager = OpenSCManagerW(
None, # Local machine
None, # SERVICES_ACTIVE_DATABASE
SERVICE_QUERY_STATUS
)
service = OpenServiceW(
schSCManager,
service,
SERVICE_QUERY_STATUS
)
status = SERVICE_STATUS()
QueryServiceStatus(
service,
status
)
result = _struct_to_dict(status)
CloseServiceHandle(service)
CloseServiceHandle(schSCManager)
return result
##############################
###### Define IPHLPAPI ######
##############################
iphlpapi = ctypes.windll.iphlpapi
##############################
########### Common ###########
##############################
class in_addr(Structure):
_fields_ = [("byte", UBYTE * 4)]
class in6_addr(Structure):
_fields_ = [("byte", UBYTE * 16)]
class sockaddr_in(Structure):
_fields_ = [("sin_family", SHORT),
("sin_port", USHORT),
("sin_addr", in_addr),
("sin_zero", 8 * CHAR)]
class sockaddr_in6(Structure):
_fields_ = [("sin6_family", SHORT),
("sin6_port", USHORT),
("sin6_flowinfo", ULONG),
("sin6_addr", in6_addr),
("sin6_scope_id", ULONG)]
class SOCKADDR_INET(ctypes.Union):
_fields_ = [("Ipv4", sockaddr_in),
("Ipv6", sockaddr_in6),
("si_family", USHORT)]
##############################
######### ICMP stats #########
##############################
class MIBICMPSTATS(Structure):
_fields_ = [("dwMsgs", DWORD),
("dwErrors", DWORD),
("dwDestUnreachs", DWORD),
("dwTimeExcds", DWORD),
("dwParmProbs", DWORD),
("dwSrcQuenchs", DWORD),
("dwRedirects", DWORD),
("dwEchos", DWORD),
("dwEchoReps", DWORD),
("dwTimestamps", DWORD),
("dwTimestampReps", DWORD),
("dwAddrMasks", DWORD),
("dwAddrMaskReps", DWORD)]
class MIBICMPINFO(Structure):
_fields_ = [("icmpInStats", MIBICMPSTATS),
("icmpOutStats", MIBICMPSTATS)]
class MIB_ICMP(Structure):
_fields_ = [("stats", MIBICMPINFO)]
PMIB_ICMP = POINTER(MIB_ICMP)
# Func
_GetIcmpStatistics = WINFUNCTYPE(ULONG, PMIB_ICMP)(
('GetIcmpStatistics', iphlpapi))
def GetIcmpStatistics():
"""Return all Windows ICMP stats from iphlpapi"""
statistics = MIB_ICMP()
_GetIcmpStatistics(byref(statistics))
results = _struct_to_dict(statistics)
del(statistics)
return results
##############################
##### Adapters Addresses #####
##############################
# Our GetAdaptersAddresses implementation is inspired by
# @sphaero 's gist: https://gist.github.com/sphaero/f9da6ebb9a7a6f679157
# published under a MPL 2.0 License (GPLv2 compatible)
# from iptypes.h
MAX_ADAPTER_ADDRESS_LENGTH = 8
MAX_DHCPV6_DUID_LENGTH = 130
GAA_FLAG_INCLUDE_PREFIX = ULONG(0x0010)
# for now, just use void * for pointers to unused structures
PIP_ADAPTER_WINS_SERVER_ADDRESS_LH = VOID
PIP_ADAPTER_GATEWAY_ADDRESS_LH = VOID
PIP_ADAPTER_DNS_SUFFIX = VOID
IF_OPER_STATUS = UINT
IF_LUID = UINT64
NET_IF_COMPARTMENT_ID = UINT32
GUID = BYTE * 16
NET_IF_NETWORK_GUID = GUID
NET_IF_CONNECTION_TYPE = UINT # enum
TUNNEL_TYPE = UINT # enum
class SOCKET_ADDRESS(ctypes.Structure):
_fields_ = [('address', POINTER(SOCKADDR_INET)),
('length', INT)]
class _IP_ADAPTER_ADDRESSES_METRIC(Structure):
_fields_ = [('length', ULONG),
('interface_index', DWORD)]
class IP_ADAPTER_UNICAST_ADDRESS(Structure):
pass
PIP_ADAPTER_UNICAST_ADDRESS = POINTER(IP_ADAPTER_UNICAST_ADDRESS)
if WINDOWS_XP:
IP_ADAPTER_UNICAST_ADDRESS._fields_ = [
("length", ULONG),
("flags", DWORD),
("next", PIP_ADAPTER_UNICAST_ADDRESS),
("address", SOCKET_ADDRESS),
("prefix_origin", INT),
("suffix_origin", INT),
("dad_state", INT),
("valid_lifetime", ULONG),
("preferred_lifetime", ULONG),
("lease_lifetime", ULONG),
]
else:
IP_ADAPTER_UNICAST_ADDRESS._fields_ = [
("length", ULONG),
("flags", DWORD),
("next", PIP_ADAPTER_UNICAST_ADDRESS),
("address", SOCKET_ADDRESS),
("prefix_origin", INT),
("suffix_origin", INT),
("dad_state", INT),
("valid_lifetime", ULONG),
("preferred_lifetime", ULONG),
("lease_lifetime", ULONG),
("on_link_prefix_length", UBYTE)
]
class IP_ADAPTER_ANYCAST_ADDRESS(Structure):
pass
PIP_ADAPTER_ANYCAST_ADDRESS = POINTER(IP_ADAPTER_ANYCAST_ADDRESS)
IP_ADAPTER_ANYCAST_ADDRESS._fields_ = [
("length", ULONG),
("flags", DWORD),
("next", PIP_ADAPTER_ANYCAST_ADDRESS),
("address", SOCKET_ADDRESS),
]
class IP_ADAPTER_MULTICAST_ADDRESS(Structure):
pass
PIP_ADAPTER_MULTICAST_ADDRESS = POINTER(IP_ADAPTER_MULTICAST_ADDRESS)
IP_ADAPTER_MULTICAST_ADDRESS._fields_ = [
("length", ULONG),
("flags", DWORD),
("next", PIP_ADAPTER_MULTICAST_ADDRESS),
("address", SOCKET_ADDRESS),
]
class IP_ADAPTER_DNS_SERVER_ADDRESS(Structure):
pass
PIP_ADAPTER_DNS_SERVER_ADDRESS = POINTER(IP_ADAPTER_DNS_SERVER_ADDRESS)
IP_ADAPTER_DNS_SERVER_ADDRESS._fields_ = [
("length", ULONG),
("flags", DWORD),
("next", PIP_ADAPTER_DNS_SERVER_ADDRESS),
("address", SOCKET_ADDRESS),
]
class IP_ADAPTER_PREFIX(Structure):
pass
PIP_ADAPTER_PREFIX = ctypes.POINTER(IP_ADAPTER_PREFIX)
IP_ADAPTER_PREFIX._fields_ = [
("alignment", ULONGLONG),
("next", PIP_ADAPTER_PREFIX),
("address", SOCKET_ADDRESS),
("prefix_length", ULONG)
]
class IP_ADAPTER_ADDRESSES(Structure):
pass
LP_IP_ADAPTER_ADDRESSES = POINTER(IP_ADAPTER_ADDRESSES)
if WINDOWS_XP:
IP_ADAPTER_ADDRESSES._fields_ = [
('length', ULONG),
('interface_index', DWORD),
('next', LP_IP_ADAPTER_ADDRESSES),
('adapter_name', ctypes.c_char_p),
('first_unicast_address', PIP_ADAPTER_UNICAST_ADDRESS),
('first_anycast_address', PIP_ADAPTER_ANYCAST_ADDRESS),
('first_multicast_address', PIP_ADAPTER_MULTICAST_ADDRESS),
('first_dns_server_address', PIP_ADAPTER_DNS_SERVER_ADDRESS),
('dns_suffix', ctypes.c_wchar_p),
('description', ctypes.c_wchar_p),
('friendly_name', ctypes.c_wchar_p),
('physical_address', BYTE * MAX_ADAPTER_ADDRESS_LENGTH),
('physical_address_length', ULONG),
('flags', ULONG),
('mtu', ULONG),
('interface_type', DWORD),
('oper_status', IF_OPER_STATUS),
('ipv6_interface_index', DWORD),
('zone_indices', ULONG * 16),
('first_prefix', PIP_ADAPTER_PREFIX),
]
else:
IP_ADAPTER_ADDRESSES._fields_ = [
('length', ULONG),
('interface_index', DWORD),
('next', LP_IP_ADAPTER_ADDRESSES),
('adapter_name', ctypes.c_char_p),
('first_unicast_address', PIP_ADAPTER_UNICAST_ADDRESS),
('first_anycast_address', PIP_ADAPTER_ANYCAST_ADDRESS),
('first_multicast_address', PIP_ADAPTER_MULTICAST_ADDRESS),
('first_dns_server_address', PIP_ADAPTER_DNS_SERVER_ADDRESS),
('dns_suffix', ctypes.c_wchar_p),
('description', ctypes.c_wchar_p),
('friendly_name', ctypes.c_wchar_p),
('physical_address', BYTE * MAX_ADAPTER_ADDRESS_LENGTH),
('physical_address_length', ULONG),
('flags', ULONG),
('mtu', ULONG),
('interface_type', DWORD),
('oper_status', IF_OPER_STATUS),
('ipv6_interface_index', DWORD),
('zone_indices', ULONG * 16),
('first_prefix', PIP_ADAPTER_PREFIX),
('transmit_link_speed', ULONGLONG),
('receive_link_speed', ULONGLONG),
('first_wins_server_address', PIP_ADAPTER_WINS_SERVER_ADDRESS_LH),
('first_gateway_address', PIP_ADAPTER_GATEWAY_ADDRESS_LH),
('ipv4_metric', ULONG),
('ipv6_metric', ULONG),
('luid', IF_LUID),
('dhcpv4_server', SOCKET_ADDRESS),
('compartment_id', NET_IF_COMPARTMENT_ID),
('network_guid', NET_IF_NETWORK_GUID),
('connection_type', NET_IF_CONNECTION_TYPE),
('tunnel_type', TUNNEL_TYPE),
('dhcpv6_server', SOCKET_ADDRESS),
('dhcpv6_client_duid', BYTE * MAX_DHCPV6_DUID_LENGTH),
('dhcpv6_client_duid_length', ULONG),
('dhcpv6_iaid', ULONG),
('first_dns_suffix', PIP_ADAPTER_DNS_SUFFIX)]
# Func
_GetAdaptersAddresses = WINFUNCTYPE(ULONG, ULONG, ULONG,
POINTER(VOID),
LP_IP_ADAPTER_ADDRESSES,
POINTER(ULONG))(
('GetAdaptersAddresses', iphlpapi))
def GetAdaptersAddresses(AF=AF_UNSPEC):
"""Return all Windows Adapters addresses from iphlpapi"""
# We get the size first
size = ULONG()
flags = GAA_FLAG_INCLUDE_PREFIX
res = _GetAdaptersAddresses(AF, flags,
None, None,
byref(size))
if res != 0x6f: # BUFFER OVERFLOW -> populate size
raise RuntimeError("Error getting structure length (%d)" % res)
# Now let's build our buffer
pointer_type = POINTER(IP_ADAPTER_ADDRESSES)
buffer = create_string_buffer(size.value)
AdapterAddresses = ctypes.cast(buffer, pointer_type)
# And call GetAdaptersAddresses
res = _GetAdaptersAddresses(AF, flags,
None, AdapterAddresses,
byref(size))
if res != NO_ERROR:
raise RuntimeError("Error retrieving table (%d)" % res)
results = _resolve_list(AdapterAddresses)
del(AdapterAddresses)
return results
##############################
####### Routing tables #######
##############################
### V1 ###
class MIB_IPFORWARDROW(Structure):
_fields_ = [('ForwardDest', DWORD),
('ForwardMask', DWORD),
('ForwardPolicy', DWORD),
('ForwardNextHop', DWORD),
('ForwardIfIndex', DWORD),
('ForwardType', DWORD),
('ForwardProto', DWORD),
('ForwardAge', DWORD),
('ForwardNextHopAS', DWORD),
('ForwardMetric1', DWORD),
('ForwardMetric2', DWORD),
('ForwardMetric3', DWORD),
('ForwardMetric4', DWORD),
('ForwardMetric5', DWORD)]
class MIB_IPFORWARDTABLE(Structure):
_fields_ = [('NumEntries', DWORD),
('Table', MIB_IPFORWARDROW * ANY_SIZE)]
PMIB_IPFORWARDTABLE = POINTER(MIB_IPFORWARDTABLE)
# Func
_GetIpForwardTable = WINFUNCTYPE(DWORD,
PMIB_IPFORWARDTABLE, POINTER(ULONG), BOOL)(
('GetIpForwardTable', iphlpapi))
def GetIpForwardTable():
"""Return all Windows routes (IPv4 only) from iphlpapi"""
# We get the size first
size = ULONG()
res = _GetIpForwardTable(None, byref(size), False)
if res != 0x7a: # ERROR_INSUFFICIENT_BUFFER -> populate size
raise RuntimeError("Error getting structure length (%d)" % res)
# Now let's build our buffer
pointer_type = PMIB_IPFORWARDTABLE
buffer = create_string_buffer(size.value)
pIpForwardTable = ctypes.cast(buffer, pointer_type)
# And call GetAdaptersAddresses
res = _GetIpForwardTable(pIpForwardTable, byref(size), True)
if res != NO_ERROR:
raise RuntimeError("Error retrieving table (%d)" % res)
results = []
for i in range(pIpForwardTable.contents.NumEntries):
results.append(_struct_to_dict(pIpForwardTable.contents.Table[i]))
del(pIpForwardTable)
return results
### V2 ###
NET_IFINDEX = ULONG
NL_ROUTE_PROTOCOL = INT
NL_ROUTE_ORIGIN = INT
class NET_LUID(Structure):
_fields_ = [("Value", ULONGLONG)]
class IP_ADDRESS_PREFIX(Structure):
_fields_ = [("Prefix", SOCKADDR_INET),
("PrefixLength", UINT8)]
class MIB_IPFORWARD_ROW2(Structure):
_fields_ = [("InterfaceLuid", NET_LUID),
("InterfaceIndex", NET_IFINDEX),
("DestinationPrefix", IP_ADDRESS_PREFIX),
("NextHop", SOCKADDR_INET),
("SitePrefixLength", UCHAR),
("ValidLifetime", ULONG),
("PreferredLifetime", ULONG),
("Metric", ULONG),
("Protocol", NL_ROUTE_PROTOCOL),
("Loopback", BOOLEAN),
("AutoconfigureAddress", BOOLEAN),
("Publish", BOOLEAN),
("Immortal", BOOLEAN),
("Age", ULONG),
("Origin", NL_ROUTE_ORIGIN)]
class MIB_IPFORWARD_TABLE2(Structure):
_fields_ = [("NumEntries", ULONG),
("Table", MIB_IPFORWARD_ROW2 * ANY_SIZE)]
PMIB_IPFORWARD_TABLE2 = POINTER(MIB_IPFORWARD_TABLE2)
# Func
if not WINDOWS_XP:
# GetIpForwardTable2 does not exist under Windows XP
_GetIpForwardTable2 = WINFUNCTYPE(
ULONG, USHORT,
POINTER(PMIB_IPFORWARD_TABLE2))(
('GetIpForwardTable2', iphlpapi)
)
_FreeMibTable = WINFUNCTYPE(None, PMIB_IPFORWARD_TABLE2)(
('FreeMibTable', iphlpapi)
)
def GetIpForwardTable2(AF=AF_UNSPEC):
"""Return all Windows routes (IPv4/IPv6) from iphlpapi"""
if WINDOWS_XP:
raise OSError("Not available on Windows XP !")
table = PMIB_IPFORWARD_TABLE2()
res = _GetIpForwardTable2(AF, byref(table))
if res != NO_ERROR:
raise RuntimeError("Error retrieving table (%d)" % res)
results = []
for i in range(table.contents.NumEntries):
results.append(_struct_to_dict(table.contents.Table[i]))
_FreeMibTable(table)
return results

143
libs/scapy/as_resolvers.py Executable file
View file

@ -0,0 +1,143 @@
# This file is part of Scapy
# See http://www.secdev.org/projects/scapy for more information
# Copyright (C) Philippe Biondi <phil@secdev.org>
# This program is published under a GPLv2 license
"""
Resolve Autonomous Systems (AS).
"""
from __future__ import absolute_import
import socket
from scapy.config import conf
from scapy.compat import plain_str
class AS_resolver:
server = None
options = "-k"
def __init__(self, server=None, port=43, options=None):
if server is not None:
self.server = server
self.port = port
if options is not None:
self.options = options
def _start(self):
self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.s.connect((self.server, self.port))
if self.options:
self.s.send(self.options.encode("utf8") + b"\n")
self.s.recv(8192)
def _stop(self):
self.s.close()
def _parse_whois(self, txt):
asn, desc = None, b""
for line in txt.splitlines():
if not asn and line.startswith(b"origin:"):
asn = plain_str(line[7:].strip())
if line.startswith(b"descr:"):
if desc:
desc += b"\n"
desc += line[6:].strip()
if asn is not None and desc:
break
return asn, plain_str(desc.strip())
def _resolve_one(self, ip):
self.s.send(("%s\n" % ip).encode("utf8"))
x = b""
while not (b"%" in x or b"source" in x):
x += self.s.recv(8192)
asn, desc = self._parse_whois(x)
return ip, asn, desc
def resolve(self, *ips):
self._start()
ret = []
for ip in ips:
ip, asn, desc = self._resolve_one(ip)
if asn is not None:
ret.append((ip, asn, desc))
self._stop()
return ret
class AS_resolver_riswhois(AS_resolver):
server = "riswhois.ripe.net"
options = "-k -M -1"
class AS_resolver_radb(AS_resolver):
server = "whois.ra.net"
options = "-k -M"
class AS_resolver_cymru(AS_resolver):
server = "whois.cymru.com"
options = None
def resolve(self, *ips):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((self.server, self.port))
s.send(
b"begin\r\n" +
b"\r\n".join(ip.encode() for ip in ips) +
b"\r\nend\r\n"
)
r = b""
while True:
line = s.recv(8192)
if line == b"":
break
r += line
s.close()
return self.parse(r)
def parse(self, data):
"""Parse bulk cymru data"""
ASNlist = []
for line in data.splitlines()[1:]:
line = plain_str(line)
if "|" not in line:
continue
asn, ip, desc = [elt.strip() for elt in line.split('|')]
if asn == "NA":
continue
asn = "AS%s" % asn
ASNlist.append((ip, asn, desc))
return ASNlist
class AS_resolver_multi(AS_resolver):
resolvers_list = (AS_resolver_riswhois(), AS_resolver_radb(),
AS_resolver_cymru())
resolvers_list = resolvers_list[1:]
def __init__(self, *reslist):
AS_resolver.__init__(self)
if reslist:
self.resolvers_list = reslist
def resolve(self, *ips):
todo = ips
ret = []
for ASres in self.resolvers_list:
try:
res = ASres.resolve(*todo)
except socket.error:
continue
todo = [ip for ip in todo if ip not in [r[0] for r in res]]
ret += res
if not todo:
break
return ret
conf.AS_resolver = AS_resolver_multi()

8
libs/scapy/asn1/__init__.py Executable file
View file

@ -0,0 +1,8 @@
# This file is part of Scapy
# See http://www.secdev.org/projects/scapy for more information
# Copyright (C) Philippe Biondi <phil@secdev.org>
# This program is published under a GPLv2 license
"""
Package holding ASN.1 related modules.
"""

517
libs/scapy/asn1/asn1.py Executable file
View file

@ -0,0 +1,517 @@
# This file is part of Scapy
# See http://www.secdev.org/projects/scapy for more information
# Copyright (C) Philippe Biondi <phil@secdev.org>
# Modified by Maxence Tury <maxence.tury@ssi.gouv.fr>
# This program is published under a GPLv2 license
"""
ASN.1 (Abstract Syntax Notation One)
"""
from __future__ import absolute_import
from __future__ import print_function
import random
from datetime import datetime
from scapy.config import conf
from scapy.error import Scapy_Exception, warning
from scapy.volatile import RandField, RandIP, GeneralizedTime
from scapy.utils import Enum_metaclass, EnumElement, binrepr
from scapy.compat import plain_str, chb, orb
import scapy.modules.six as six
from scapy.modules.six.moves import range
class RandASN1Object(RandField):
def __init__(self, objlist=None):
self.objlist = [
x._asn1_obj
for x in six.itervalues(ASN1_Class_UNIVERSAL.__rdict__)
if hasattr(x, "_asn1_obj")
] if objlist is None else objlist
self.chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" # noqa: E501
def _fix(self, n=0):
o = random.choice(self.objlist)
if issubclass(o, ASN1_INTEGER):
return o(int(random.gauss(0, 1000)))
elif issubclass(o, ASN1_IPADDRESS):
z = RandIP()._fix()
return o(z)
elif issubclass(o, ASN1_GENERALIZED_TIME) or issubclass(o, ASN1_UTC_TIME): # noqa: E501
z = GeneralizedTime()._fix()
return o(z)
elif issubclass(o, ASN1_STRING):
z = int(random.expovariate(0.05) + 1)
return o("".join(random.choice(self.chars) for _ in range(z)))
elif issubclass(o, ASN1_SEQUENCE) and (n < 10):
z = int(random.expovariate(0.08) + 1)
return o([self.__class__(objlist=self.objlist)._fix(n + 1)
for _ in range(z)])
return ASN1_INTEGER(int(random.gauss(0, 1000)))
##############
# ASN1 #
##############
class ASN1_Error(Scapy_Exception):
pass
class ASN1_Encoding_Error(ASN1_Error):
pass
class ASN1_Decoding_Error(ASN1_Error):
pass
class ASN1_BadTag_Decoding_Error(ASN1_Decoding_Error):
pass
class ASN1Codec(EnumElement):
def register_stem(cls, stem):
cls._stem = stem
def dec(cls, s, context=None):
return cls._stem.dec(s, context=context)
def safedec(cls, s, context=None):
return cls._stem.safedec(s, context=context)
def get_stem(cls):
return cls.stem
class ASN1_Codecs_metaclass(Enum_metaclass):
element_class = ASN1Codec
class ASN1_Codecs(six.with_metaclass(ASN1_Codecs_metaclass)):
BER = 1
DER = 2
PER = 3
CER = 4
LWER = 5
BACnet = 6
OER = 7
SER = 8
XER = 9
class ASN1Tag(EnumElement):
def __init__(self, key, value, context=None, codec=None):
EnumElement.__init__(self, key, value)
self._context = context
if codec is None:
codec = {}
self._codec = codec
def clone(self): # not a real deep copy. self.codec is shared
return self.__class__(self._key, self._value, self._context, self._codec) # noqa: E501
def register_asn1_object(self, asn1obj):
self._asn1_obj = asn1obj
def asn1_object(self, val):
if hasattr(self, "_asn1_obj"):
return self._asn1_obj(val)
raise ASN1_Error("%r does not have any assigned ASN1 object" % self)
def register(self, codecnum, codec):
self._codec[codecnum] = codec
def get_codec(self, codec):
try:
c = self._codec[codec]
except KeyError:
raise ASN1_Error("Codec %r not found for tag %r" % (codec, self))
return c
class ASN1_Class_metaclass(Enum_metaclass):
element_class = ASN1Tag
def __new__(cls, name, bases, dct): # XXX factorise a bit with Enum_metaclass.__new__() # noqa: E501
for b in bases:
for k, v in six.iteritems(b.__dict__):
if k not in dct and isinstance(v, ASN1Tag):
dct[k] = v.clone()
rdict = {}
for k, v in six.iteritems(dct):
if isinstance(v, int):
v = ASN1Tag(k, v)
dct[k] = v
rdict[v] = v
elif isinstance(v, ASN1Tag):
rdict[v] = v
dct["__rdict__"] = rdict
cls = type.__new__(cls, name, bases, dct)
for v in six.itervalues(cls.__dict__):
if isinstance(v, ASN1Tag):
v.context = cls # overwrite ASN1Tag contexts, even cloned ones
return cls
class ASN1_Class(six.with_metaclass(ASN1_Class_metaclass)):
pass
class ASN1_Class_UNIVERSAL(ASN1_Class):
name = "UNIVERSAL"
ERROR = -3
RAW = -2
NONE = -1
ANY = 0
BOOLEAN = 1
INTEGER = 2
BIT_STRING = 3
STRING = 4
NULL = 5
OID = 6
OBJECT_DESCRIPTOR = 7
EXTERNAL = 8
REAL = 9
ENUMERATED = 10
EMBEDDED_PDF = 11
UTF8_STRING = 12
RELATIVE_OID = 13
SEQUENCE = 16 | 0x20 # constructed encoding
SET = 17 | 0x20 # constructed encoding
NUMERIC_STRING = 18
PRINTABLE_STRING = 19
T61_STRING = 20 # aka TELETEX_STRING
VIDEOTEX_STRING = 21
IA5_STRING = 22
UTC_TIME = 23
GENERALIZED_TIME = 24
GRAPHIC_STRING = 25
ISO646_STRING = 26 # aka VISIBLE_STRING
GENERAL_STRING = 27
UNIVERSAL_STRING = 28
CHAR_STRING = 29
BMP_STRING = 30
IPADDRESS = 0 | 0x40 # application-specific encoding
COUNTER32 = 1 | 0x40 # application-specific encoding
GAUGE32 = 2 | 0x40 # application-specific encoding
TIME_TICKS = 3 | 0x40 # application-specific encoding
class ASN1_Object_metaclass(type):
def __new__(cls, name, bases, dct):
c = super(ASN1_Object_metaclass, cls).__new__(cls, name, bases, dct)
try:
c.tag.register_asn1_object(c)
except Exception:
warning("Error registering %r for %r" % (c.tag, c.codec))
return c
class ASN1_Object(six.with_metaclass(ASN1_Object_metaclass)):
tag = ASN1_Class_UNIVERSAL.ANY
def __init__(self, val):
self.val = val
def enc(self, codec):
return self.tag.get_codec(codec).enc(self.val)
def __repr__(self):
return "<%s[%r]>" % (self.__dict__.get("name", self.__class__.__name__), self.val) # noqa: E501
def __str__(self):
return self.enc(conf.ASN1_default_codec)
def __bytes__(self):
return self.enc(conf.ASN1_default_codec)
def strshow(self, lvl=0):
return (" " * lvl) + repr(self) + "\n"
def show(self, lvl=0):
print(self.strshow(lvl))
def __eq__(self, other):
return self.val == other
def __lt__(self, other):
return self.val < other
def __le__(self, other):
return self.val <= other
def __gt__(self, other):
return self.val > other
def __ge__(self, other):
return self.val >= other
def __ne__(self, other):
return self.val != other
#######################
# ASN1 objects #
#######################
# on the whole, we order the classes by ASN1_Class_UNIVERSAL tag value
class ASN1_DECODING_ERROR(ASN1_Object):
tag = ASN1_Class_UNIVERSAL.ERROR
def __init__(self, val, exc=None):
ASN1_Object.__init__(self, val)
self.exc = exc
def __repr__(self):
return "<%s[%r]{{%r}}>" % (self.__dict__.get("name", self.__class__.__name__), # noqa: E501
self.val, self.exc.args[0])
def enc(self, codec):
if isinstance(self.val, ASN1_Object):
return self.val.enc(codec)
return self.val
class ASN1_force(ASN1_Object):
tag = ASN1_Class_UNIVERSAL.RAW
def enc(self, codec):
if isinstance(self.val, ASN1_Object):
return self.val.enc(codec)
return self.val
class ASN1_BADTAG(ASN1_force):
pass
class ASN1_INTEGER(ASN1_Object):
tag = ASN1_Class_UNIVERSAL.INTEGER
def __repr__(self):
h = hex(self.val)
if h[-1] == "L":
h = h[:-1]
# cut at 22 because with leading '0x', x509 serials should be < 23
if len(h) > 22:
h = h[:12] + "..." + h[-10:]
r = repr(self.val)
if len(r) > 20:
r = r[:10] + "..." + r[-10:]
return h + " <%s[%s]>" % (self.__dict__.get("name", self.__class__.__name__), r) # noqa: E501
class ASN1_BOOLEAN(ASN1_INTEGER):
tag = ASN1_Class_UNIVERSAL.BOOLEAN
# BER: 0 means False, anything else means True
def __repr__(self):
return '%s %s' % (not (self.val == 0), ASN1_Object.__repr__(self))
class ASN1_BIT_STRING(ASN1_Object):
"""
ASN1_BIT_STRING values are bit strings like "011101".
A zero-bit padded readable string is provided nonetheless,
which is stored in val_readable
"""
tag = ASN1_Class_UNIVERSAL.BIT_STRING
def __init__(self, val, readable=False):
if not readable:
self.val = val
else:
self.val_readable = val
def __setattr__(self, name, value):
if name == "val_readable":
if isinstance(value, (str, bytes)):
val = "".join(binrepr(orb(x)).zfill(8) for x in value)
else:
warning("Invalid val: should be bytes")
val = "<invalid val_readable>"
object.__setattr__(self, "val", val)
object.__setattr__(self, name, value)
object.__setattr__(self, "unused_bits", 0)
elif name == "val":
value = plain_str(value)
if isinstance(value, str):
if any(c for c in value if c not in ["0", "1"]):
warning("Invalid operation: 'val' is not a valid bit string.") # noqa: E501
return
else:
if len(value) % 8 == 0:
unused_bits = 0
else:
unused_bits = 8 - (len(value) % 8)
padded_value = value + ("0" * unused_bits)
bytes_arr = zip(*[iter(padded_value)] * 8)
val_readable = b"".join(chb(int("".join(x), 2)) for x in bytes_arr) # noqa: E501
else:
warning("Invalid val: should be str")
val_readable = b"<invalid val>"
unused_bits = 0
object.__setattr__(self, "val_readable", val_readable)
object.__setattr__(self, name, value)
object.__setattr__(self, "unused_bits", unused_bits)
elif name == "unused_bits":
warning("Invalid operation: unused_bits rewriting "
"is not supported.")
else:
object.__setattr__(self, name, value)
def __repr__(self):
s = self.val_readable
if len(s) > 16:
s = s[:10] + b"..." + s[-10:]
v = self.val
if len(v) > 20:
v = v[:10] + "..." + v[-10:]
return "<%s[%s]=%s (%d unused bit%s)>" % (
self.__dict__.get("name", self.__class__.__name__),
v,
s,
self.unused_bits,
"s" if self.unused_bits > 1 else ""
)
class ASN1_STRING(ASN1_Object):
tag = ASN1_Class_UNIVERSAL.STRING
class ASN1_NULL(ASN1_Object):
tag = ASN1_Class_UNIVERSAL.NULL
def __repr__(self):
return ASN1_Object.__repr__(self)
class ASN1_OID(ASN1_Object):
tag = ASN1_Class_UNIVERSAL.OID
def __init__(self, val):
val = plain_str(val)
val = conf.mib._oid(val)
ASN1_Object.__init__(self, val)
self.oidname = conf.mib._oidname(val)
def __repr__(self):
return "<%s[%r]>" % (self.__dict__.get("name", self.__class__.__name__), self.oidname) # noqa: E501
class ASN1_ENUMERATED(ASN1_INTEGER):
tag = ASN1_Class_UNIVERSAL.ENUMERATED
class ASN1_UTF8_STRING(ASN1_STRING):
tag = ASN1_Class_UNIVERSAL.UTF8_STRING
class ASN1_NUMERIC_STRING(ASN1_STRING):
tag = ASN1_Class_UNIVERSAL.NUMERIC_STRING
class ASN1_PRINTABLE_STRING(ASN1_STRING):
tag = ASN1_Class_UNIVERSAL.PRINTABLE_STRING
class ASN1_T61_STRING(ASN1_STRING):
tag = ASN1_Class_UNIVERSAL.T61_STRING
class ASN1_VIDEOTEX_STRING(ASN1_STRING):
tag = ASN1_Class_UNIVERSAL.VIDEOTEX_STRING
class ASN1_IA5_STRING(ASN1_STRING):
tag = ASN1_Class_UNIVERSAL.IA5_STRING
class ASN1_UTC_TIME(ASN1_STRING):
tag = ASN1_Class_UNIVERSAL.UTC_TIME
def __init__(self, val):
ASN1_STRING.__init__(self, val)
def __setattr__(self, name, value):
if isinstance(value, bytes):
value = plain_str(value)
if name == "val":
pretty_time = None
if isinstance(self, ASN1_GENERALIZED_TIME):
_len = 15
self._format = "%Y%m%d%H%M%S"
else:
_len = 13
self._format = "%y%m%d%H%M%S"
_nam = self.tag._asn1_obj.__name__[4:].lower()
if (isinstance(value, str) and
len(value) == _len and value[-1] == "Z"):
dt = datetime.strptime(value[:-1], self._format)
pretty_time = dt.strftime("%b %d %H:%M:%S %Y GMT")
else:
pretty_time = "%s [invalid %s]" % (value, _nam)
ASN1_STRING.__setattr__(self, "pretty_time", pretty_time)
ASN1_STRING.__setattr__(self, name, value)
elif name == "pretty_time":
print("Invalid operation: pretty_time rewriting is not supported.")
else:
ASN1_STRING.__setattr__(self, name, value)
def __repr__(self):
return "%s %s" % (self.pretty_time, ASN1_STRING.__repr__(self))
class ASN1_GENERALIZED_TIME(ASN1_UTC_TIME):
tag = ASN1_Class_UNIVERSAL.GENERALIZED_TIME
class ASN1_ISO646_STRING(ASN1_STRING):
tag = ASN1_Class_UNIVERSAL.ISO646_STRING
class ASN1_UNIVERSAL_STRING(ASN1_STRING):
tag = ASN1_Class_UNIVERSAL.UNIVERSAL_STRING
class ASN1_BMP_STRING(ASN1_STRING):
tag = ASN1_Class_UNIVERSAL.BMP_STRING
class ASN1_SEQUENCE(ASN1_Object):
tag = ASN1_Class_UNIVERSAL.SEQUENCE
def strshow(self, lvl=0):
s = (" " * lvl) + ("# %s:" % self.__class__.__name__) + "\n"
for o in self.val:
s += o.strshow(lvl=lvl + 1)
return s
class ASN1_SET(ASN1_SEQUENCE):
tag = ASN1_Class_UNIVERSAL.SET
class ASN1_IPADDRESS(ASN1_STRING):
tag = ASN1_Class_UNIVERSAL.IPADDRESS
class ASN1_COUNTER32(ASN1_INTEGER):
tag = ASN1_Class_UNIVERSAL.COUNTER32
class ASN1_GAUGE32(ASN1_INTEGER):
tag = ASN1_Class_UNIVERSAL.GAUGE32
class ASN1_TIME_TICKS(ASN1_INTEGER):
tag = ASN1_Class_UNIVERSAL.TIME_TICKS
conf.ASN1_default_codec = ASN1_Codecs.BER

565
libs/scapy/asn1/ber.py Executable file
View file

@ -0,0 +1,565 @@
# This file is part of Scapy
# See http://www.secdev.org/projects/scapy for more information
# Copyright (C) Philippe Biondi <phil@secdev.org>
# Modified by Maxence Tury <maxence.tury@ssi.gouv.fr>
# Acknowledgment: Ralph Broenink
# This program is published under a GPLv2 license
"""
Basic Encoding Rules (BER) for ASN.1
"""
from __future__ import absolute_import
from scapy.error import warning
from scapy.compat import chb, orb, bytes_encode
from scapy.utils import binrepr, inet_aton, inet_ntoa
from scapy.asn1.asn1 import ASN1_Decoding_Error, ASN1_Encoding_Error, \
ASN1_BadTag_Decoding_Error, ASN1_Codecs, ASN1_Class_UNIVERSAL, \
ASN1_Error, ASN1_DECODING_ERROR, ASN1_BADTAG
from scapy.modules import six
##################
# BER encoding #
##################
# [ BER tools ] #
class BER_Exception(Exception):
pass
class BER_Encoding_Error(ASN1_Encoding_Error):
def __init__(self, msg, encoded=None, remaining=None):
Exception.__init__(self, msg)
self.remaining = remaining
self.encoded = encoded
def __str__(self):
s = Exception.__str__(self)
if isinstance(self.encoded, BERcodec_Object):
s += "\n### Already encoded ###\n%s" % self.encoded.strshow()
else:
s += "\n### Already encoded ###\n%r" % self.encoded
s += "\n### Remaining ###\n%r" % self.remaining
return s
class BER_Decoding_Error(ASN1_Decoding_Error):
def __init__(self, msg, decoded=None, remaining=None):
Exception.__init__(self, msg)
self.remaining = remaining
self.decoded = decoded
def __str__(self):
s = Exception.__str__(self)
if isinstance(self.decoded, BERcodec_Object):
s += "\n### Already decoded ###\n%s" % self.decoded.strshow()
else:
s += "\n### Already decoded ###\n%r" % self.decoded
s += "\n### Remaining ###\n%r" % self.remaining
return s
class BER_BadTag_Decoding_Error(BER_Decoding_Error,
ASN1_BadTag_Decoding_Error):
pass
def BER_len_enc(ll, size=0):
if ll <= 127 and size == 0:
return chb(ll)
s = b""
while ll or size > 0:
s = chb(ll & 0xff) + s
ll >>= 8
size -= 1
if len(s) > 127:
raise BER_Exception(
"BER_len_enc: Length too long (%i) to be encoded [%r]" %
(len(s), s)
)
return chb(len(s) | 0x80) + s
def BER_len_dec(s):
tmp_len = orb(s[0])
if not tmp_len & 0x80:
return tmp_len, s[1:]
tmp_len &= 0x7f
if len(s) <= tmp_len:
raise BER_Decoding_Error(
"BER_len_dec: Got %i bytes while expecting %i" %
(len(s) - 1, tmp_len),
remaining=s
)
ll = 0
for c in s[1:tmp_len + 1]:
ll <<= 8
ll |= orb(c)
return ll, s[tmp_len + 1:]
def BER_num_enc(ll, size=1):
x = []
while ll or size > 0:
x.insert(0, ll & 0x7f)
if len(x) > 1:
x[0] |= 0x80
ll >>= 7
size -= 1
return b"".join(chb(k) for k in x)
def BER_num_dec(s, cls_id=0):
if len(s) == 0:
raise BER_Decoding_Error("BER_num_dec: got empty string", remaining=s)
x = cls_id
for i, c in enumerate(s):
c = orb(c)
x <<= 7
x |= c & 0x7f
if not c & 0x80:
break
if c & 0x80:
raise BER_Decoding_Error("BER_num_dec: unfinished number description",
remaining=s)
return x, s[i + 1:]
def BER_id_dec(s):
# This returns the tag ALONG WITH THE PADDED CLASS+CONSTRUCTIVE INFO.
# Let's recall that bits 8-7 from the first byte of the tag encode
# the class information, while bit 6 means primitive or constructive.
#
# For instance, with low-tag-number b'\x81', class would be 0b10
# ('context-specific') and tag 0x01, but we return 0x81 as a whole.
# For b'\xff\x22', class would be 0b11 ('private'), constructed, then
# padding, then tag 0x22, but we return (0xff>>5)*128^1 + 0x22*128^0.
# Why the 5-bit-shifting? Because it provides an unequivocal encoding
# on base 128 (note that 0xff would equal 1*128^1 + 127*128^0...),
# as we know that bits 5 to 1 are fixed to 1 anyway.
#
# As long as there is no class differentiation, we have to keep this info
# encoded in scapy's tag in order to reuse it for packet building.
# Note that tags thus may have to be hard-coded with their extended
# information, e.g. a SEQUENCE from asn1.py has a direct tag 0x20|16.
x = orb(s[0])
if x & 0x1f != 0x1f:
# low-tag-number
return x, s[1:]
else:
# high-tag-number
return BER_num_dec(s[1:], cls_id=x >> 5)
def BER_id_enc(n):
if n < 256:
# low-tag-number
return chb(n)
else:
# high-tag-number
s = BER_num_enc(n)
tag = orb(s[0]) # first byte, as an int
tag &= 0x07 # reset every bit from 8 to 4
tag <<= 5 # move back the info bits on top
tag |= 0x1f # pad with 1s every bit from 5 to 1
return chb(tag) + s[1:]
# The functions below provide implicit and explicit tagging support.
def BER_tagging_dec(s, hidden_tag=None, implicit_tag=None,
explicit_tag=None, safe=False):
# We output the 'real_tag' if it is different from the (im|ex)plicit_tag.
real_tag = None
if len(s) > 0:
err_msg = "BER_tagging_dec: observed tag does not match expected tag"
if implicit_tag is not None:
ber_id, s = BER_id_dec(s)
if ber_id != implicit_tag:
if not safe:
raise BER_Decoding_Error(err_msg, remaining=s)
else:
real_tag = ber_id
s = chb(hash(hidden_tag)) + s
elif explicit_tag is not None:
ber_id, s = BER_id_dec(s)
if ber_id != explicit_tag:
if not safe:
raise BER_Decoding_Error(err_msg, remaining=s)
else:
real_tag = ber_id
l, s = BER_len_dec(s)
return real_tag, s
def BER_tagging_enc(s, implicit_tag=None, explicit_tag=None):
if len(s) > 0:
if implicit_tag is not None:
s = BER_id_enc(implicit_tag) + s[1:]
elif explicit_tag is not None:
s = BER_id_enc(explicit_tag) + BER_len_enc(len(s)) + s
return s
# [ BER classes ] #
class BERcodec_metaclass(type):
def __new__(cls, name, bases, dct):
c = super(BERcodec_metaclass, cls).__new__(cls, name, bases, dct)
try:
c.tag.register(c.codec, c)
except Exception:
warning("Error registering %r for %r" % (c.tag, c.codec))
return c
class BERcodec_Object(six.with_metaclass(BERcodec_metaclass)):
codec = ASN1_Codecs.BER
tag = ASN1_Class_UNIVERSAL.ANY
@classmethod
def asn1_object(cls, val):
return cls.tag.asn1_object(val)
@classmethod
def check_string(cls, s):
if not s:
raise BER_Decoding_Error(
"%s: Got empty object while expecting tag %r" %
(cls.__name__, cls.tag), remaining=s
)
@classmethod
def check_type(cls, s):
cls.check_string(s)
tag, remainder = BER_id_dec(s)
if not isinstance(tag, int) or cls.tag != tag:
raise BER_BadTag_Decoding_Error(
"%s: Got tag [%i/%#x] while expecting %r" %
(cls.__name__, tag, tag, cls.tag), remaining=s
)
return remainder
@classmethod
def check_type_get_len(cls, s):
s2 = cls.check_type(s)
if not s2:
raise BER_Decoding_Error("%s: No bytes while expecting a length" %
cls.__name__, remaining=s)
return BER_len_dec(s2)
@classmethod
def check_type_check_len(cls, s):
l, s3 = cls.check_type_get_len(s)
if len(s3) < l:
raise BER_Decoding_Error("%s: Got %i bytes while expecting %i" %
(cls.__name__, len(s3), l), remaining=s)
return l, s3[:l], s3[l:]
@classmethod
def do_dec(cls, s, context=None, safe=False):
if context is None:
context = cls.tag.context
cls.check_string(s)
p, remainder = BER_id_dec(s)
if p not in context:
t = s
if len(t) > 18:
t = t[:15] + b"..."
raise BER_Decoding_Error("Unknown prefix [%02x] for [%r]" %
(p, t), remaining=s)
codec = context[p].get_codec(ASN1_Codecs.BER)
if codec == BERcodec_Object:
# Value type defined as Unknown
l, s = BER_num_dec(remainder)
return ASN1_BADTAG(s[:l]), s[l:]
return codec.dec(s, context, safe)
@classmethod
def dec(cls, s, context=None, safe=False):
if not safe:
return cls.do_dec(s, context, safe)
try:
return cls.do_dec(s, context, safe)
except BER_BadTag_Decoding_Error as e:
o, remain = BERcodec_Object.dec(e.remaining, context, safe)
return ASN1_BADTAG(o), remain
except BER_Decoding_Error as e:
return ASN1_DECODING_ERROR(s, exc=e), ""
except ASN1_Error as e:
return ASN1_DECODING_ERROR(s, exc=e), ""
@classmethod
def safedec(cls, s, context=None):
return cls.dec(s, context, safe=True)
@classmethod
def enc(cls, s):
if isinstance(s, six.string_types + (bytes,)):
return BERcodec_STRING.enc(s)
else:
return BERcodec_INTEGER.enc(int(s))
ASN1_Codecs.BER.register_stem(BERcodec_Object)
##########################
# BERcodec objects #
##########################
class BERcodec_INTEGER(BERcodec_Object):
tag = ASN1_Class_UNIVERSAL.INTEGER
@classmethod
def enc(cls, i):
s = []
while True:
s.append(i & 0xff)
if -127 <= i < 0:
break
if 128 <= i <= 255:
s.append(0)
i >>= 8
if not i:
break
s = [chb(hash(c)) for c in s]
s.append(BER_len_enc(len(s)))
s.append(chb(hash(cls.tag)))
s.reverse()
return b"".join(s)
@classmethod
def do_dec(cls, s, context=None, safe=False):
l, s, t = cls.check_type_check_len(s)
x = 0
if s:
if orb(s[0]) & 0x80: # negative int
x = -1
for c in s:
x <<= 8
x |= orb(c)
return cls.asn1_object(x), t
class BERcodec_BOOLEAN(BERcodec_INTEGER):
tag = ASN1_Class_UNIVERSAL.BOOLEAN
class BERcodec_BIT_STRING(BERcodec_Object):
tag = ASN1_Class_UNIVERSAL.BIT_STRING
@classmethod
def do_dec(cls, s, context=None, safe=False):
# /!\ the unused_bits information is lost after this decoding
l, s, t = cls.check_type_check_len(s)
if len(s) > 0:
unused_bits = orb(s[0])
if safe and unused_bits > 7:
raise BER_Decoding_Error(
"BERcodec_BIT_STRING: too many unused_bits advertised",
remaining=s
)
s = "".join(binrepr(orb(x)).zfill(8) for x in s[1:])
if unused_bits > 0:
s = s[:-unused_bits]
return cls.tag.asn1_object(s), t
else:
raise BER_Decoding_Error(
"BERcodec_BIT_STRING found no content "
"(not even unused_bits byte)",
remaining=s
)
@classmethod
def enc(cls, s):
# /!\ this is DER encoding (bit strings are only zero-bit padded)
s = bytes_encode(s)
if len(s) % 8 == 0:
unused_bits = 0
else:
unused_bits = 8 - len(s) % 8
s += b"0" * unused_bits
s = b"".join(chb(int(b"".join(chb(y) for y in x), 2))
for x in zip(*[iter(s)] * 8))
s = chb(unused_bits) + s
return chb(hash(cls.tag)) + BER_len_enc(len(s)) + s
class BERcodec_STRING(BERcodec_Object):
tag = ASN1_Class_UNIVERSAL.STRING
@classmethod
def enc(cls, s):
s = bytes_encode(s)
# Be sure we are encoding bytes
return chb(hash(cls.tag)) + BER_len_enc(len(s)) + s
@classmethod
def do_dec(cls, s, context=None, safe=False):
l, s, t = cls.check_type_check_len(s)
return cls.tag.asn1_object(s), t
class BERcodec_NULL(BERcodec_INTEGER):
tag = ASN1_Class_UNIVERSAL.NULL
@classmethod
def enc(cls, i):
if i == 0:
return chb(hash(cls.tag)) + b"\0"
else:
return super(cls, cls).enc(i)
class BERcodec_OID(BERcodec_Object):
tag = ASN1_Class_UNIVERSAL.OID
@classmethod
def enc(cls, oid):
oid = bytes_encode(oid)
if oid:
lst = [int(x) for x in oid.strip(b".").split(b".")]
else:
lst = list()
if len(lst) >= 2:
lst[1] += 40 * lst[0]
del(lst[0])
s = b"".join(BER_num_enc(k) for k in lst)
return chb(hash(cls.tag)) + BER_len_enc(len(s)) + s
@classmethod
def do_dec(cls, s, context=None, safe=False):
l, s, t = cls.check_type_check_len(s)
lst = []
while s:
l, s = BER_num_dec(s)
lst.append(l)
if (len(lst) > 0):
lst.insert(0, lst[0] // 40)
lst[1] %= 40
return (
cls.asn1_object(b".".join(str(k).encode('ascii') for k in lst)),
t,
)
class BERcodec_ENUMERATED(BERcodec_INTEGER):
tag = ASN1_Class_UNIVERSAL.ENUMERATED
class BERcodec_UTF8_STRING(BERcodec_STRING):
tag = ASN1_Class_UNIVERSAL.UTF8_STRING
class BERcodec_NUMERIC_STRING(BERcodec_STRING):
tag = ASN1_Class_UNIVERSAL.NUMERIC_STRING
class BERcodec_PRINTABLE_STRING(BERcodec_STRING):
tag = ASN1_Class_UNIVERSAL.PRINTABLE_STRING
class BERcodec_T61_STRING(BERcodec_STRING):
tag = ASN1_Class_UNIVERSAL.T61_STRING
class BERcodec_VIDEOTEX_STRING(BERcodec_STRING):
tag = ASN1_Class_UNIVERSAL.VIDEOTEX_STRING
class BERcodec_IA5_STRING(BERcodec_STRING):
tag = ASN1_Class_UNIVERSAL.IA5_STRING
class BERcodec_UTC_TIME(BERcodec_STRING):
tag = ASN1_Class_UNIVERSAL.UTC_TIME
class BERcodec_GENERALIZED_TIME(BERcodec_STRING):
tag = ASN1_Class_UNIVERSAL.GENERALIZED_TIME
class BERcodec_ISO646_STRING(BERcodec_STRING):
tag = ASN1_Class_UNIVERSAL.ISO646_STRING
class BERcodec_UNIVERSAL_STRING(BERcodec_STRING):
tag = ASN1_Class_UNIVERSAL.UNIVERSAL_STRING
class BERcodec_BMP_STRING(BERcodec_STRING):
tag = ASN1_Class_UNIVERSAL.BMP_STRING
class BERcodec_SEQUENCE(BERcodec_Object):
tag = ASN1_Class_UNIVERSAL.SEQUENCE
@classmethod
def enc(cls, ll):
if not isinstance(ll, bytes):
ll = b"".join(x.enc(cls.codec) for x in ll)
return chb(hash(cls.tag)) + BER_len_enc(len(ll)) + ll
@classmethod
def do_dec(cls, s, context=None, safe=False):
if context is None:
context = cls.tag.context
ll, st = cls.check_type_get_len(s) # we may have len(s) < ll
s, t = st[:ll], st[ll:]
obj = []
while s:
try:
o, s = BERcodec_Object.dec(s, context, safe)
except BER_Decoding_Error as err:
err.remaining += t
if err.decoded is not None:
obj.append(err.decoded)
err.decoded = obj
raise
obj.append(o)
if len(st) < ll:
raise BER_Decoding_Error("Not enough bytes to decode sequence",
decoded=obj)
return cls.asn1_object(obj), t
class BERcodec_SET(BERcodec_SEQUENCE):
tag = ASN1_Class_UNIVERSAL.SET
class BERcodec_IPADDRESS(BERcodec_STRING):
tag = ASN1_Class_UNIVERSAL.IPADDRESS
@classmethod
def enc(cls, ipaddr_ascii):
try:
s = inet_aton(ipaddr_ascii)
except Exception:
raise BER_Encoding_Error("IPv4 address could not be encoded")
return chb(hash(cls.tag)) + BER_len_enc(len(s)) + s
@classmethod
def do_dec(cls, s, context=None, safe=False):
l, s, t = cls.check_type_check_len(s)
try:
ipaddr_ascii = inet_ntoa(s)
except Exception:
raise BER_Decoding_Error("IP address could not be decoded",
remaining=s)
return cls.asn1_object(ipaddr_ascii), t
class BERcodec_COUNTER32(BERcodec_INTEGER):
tag = ASN1_Class_UNIVERSAL.COUNTER32
class BERcodec_GAUGE32(BERcodec_INTEGER):
tag = ASN1_Class_UNIVERSAL.GAUGE32
class BERcodec_TIME_TICKS(BERcodec_INTEGER):
tag = ASN1_Class_UNIVERSAL.TIME_TICKS

623
libs/scapy/asn1/mib.py Executable file
View file

@ -0,0 +1,623 @@
# This file is part of Scapy
# See http://www.secdev.org/projects/scapy for more information
# Copyright (C) Philippe Biondi <phil@secdev.org>
# Modified by Maxence Tury <maxence.tury@ssi.gouv.fr>
# This program is published under a GPLv2 license
"""
Management Information Base (MIB) parsing
"""
from __future__ import absolute_import
import re
from glob import glob
from scapy.dadict import DADict, fixname
from scapy.config import conf
from scapy.utils import do_graph
import scapy.modules.six as six
from scapy.compat import plain_str
#################
# MIB parsing #
#################
_mib_re_integer = re.compile(r"^[0-9]+$")
_mib_re_both = re.compile(r"^([a-zA-Z_][a-zA-Z0-9_-]*)\(([0-9]+)\)$")
_mib_re_oiddecl = re.compile(r"$\s*([a-zA-Z0-9_-]+)\s+OBJECT([^:\{\}]|\{[^:]+\})+::=\s*\{([^\}]+)\}", re.M) # noqa: E501
_mib_re_strings = re.compile(r'"[^"]*"')
_mib_re_comments = re.compile(r'--.*(\r|\n)')
class MIBDict(DADict):
def fixname(self, val):
# We overwrite DADict fixname method as we want to keep - in names
return val
def _findroot(self, x):
"""Internal MIBDict function used to find a partial OID"""
if x.startswith("."):
x = x[1:]
if not x.endswith("."):
x += "."
max = 0
root = "."
root_key = ""
for k in six.iterkeys(self):
if x.startswith(k + "."):
if max < len(k):
max = len(k)
root = self[k]
root_key = k
return root, root_key, x[max:-1]
def _oidname(self, x):
"""Deduce the OID name from its OID ID"""
root, _, remainder = self._findroot(x)
return root + remainder
def _oid(self, x):
"""Parse the OID id/OID generator, and return real OID"""
xl = x.strip(".").split(".")
p = len(xl) - 1
while p >= 0 and _mib_re_integer.match(xl[p]):
p -= 1
if p != 0 or xl[p] not in six.itervalues(self.__dict__):
return x
xl[p] = next(k for k, v in six.iteritems(self.__dict__) if v == xl[p])
return ".".join(xl[p:])
def _make_graph(self, other_keys=None, **kargs):
if other_keys is None:
other_keys = []
nodes = [(self[key], key) for key in self.iterkeys()]
oids = set(self.iterkeys())
for k in other_keys:
if k not in oids:
nodes.append(self.oidname(k), k)
s = 'digraph "mib" {\n\trankdir=LR;\n\n'
for k, o in nodes:
s += '\t"%s" [ label="%s" ];\n' % (o, k)
s += "\n"
for k, o in nodes:
parent, parent_key, remainder = self._findroot(o[:-1])
remainder = remainder[1:] + o[-1]
if parent != ".":
parent = parent_key
s += '\t"%s" -> "%s" [label="%s"];\n' % (parent, o, remainder)
s += "}\n"
do_graph(s, **kargs)
def _mib_register(ident, value, the_mib, unresolved):
"""Internal function used to register an OID and its name in a MIBDict"""
if ident in the_mib or ident in unresolved:
return ident in the_mib
resval = []
not_resolved = 0
for v in value:
if _mib_re_integer.match(v):
resval.append(v)
else:
v = fixname(plain_str(v))
if v not in the_mib:
not_resolved = 1
if v in the_mib:
v = the_mib[v]
elif v in unresolved:
v = unresolved[v]
if isinstance(v, list):
resval += v
else:
resval.append(v)
if not_resolved:
unresolved[ident] = resval
return False
else:
the_mib[ident] = resval
keys = list(unresolved)
i = 0
while i < len(keys):
k = keys[i]
if _mib_register(k, unresolved[k], the_mib, {}):
del(unresolved[k])
del(keys[i])
i = 0
else:
i += 1
return True
def load_mib(filenames):
"""Load the conf.mib dict from a list of filenames"""
the_mib = {'iso': ['1']}
unresolved = {}
for k in six.iterkeys(conf.mib):
_mib_register(conf.mib[k], k.split("."), the_mib, unresolved)
if isinstance(filenames, (str, bytes)):
filenames = [filenames]
for fnames in filenames:
for fname in glob(fnames):
with open(fname) as f:
text = f.read()
cleantext = " ".join(_mib_re_strings.split(" ".join(_mib_re_comments.split(text)))) # noqa: E501
for m in _mib_re_oiddecl.finditer(cleantext):
gr = m.groups()
ident, oid = gr[0], gr[-1]
ident = fixname(ident)
oid = oid.split()
for i, elt in enumerate(oid):
m = _mib_re_both.match(elt)
if m:
oid[i] = m.groups()[1]
_mib_register(ident, oid, the_mib, unresolved)
newmib = MIBDict(_name="MIB")
for oid, key in six.iteritems(the_mib):
newmib[".".join(key)] = oid
for oid, key in six.iteritems(unresolved):
newmib[".".join(key)] = oid
conf.mib = newmib
####################
# OID references #
####################
# pkcs1 #
pkcs1_oids = {
"1.2.840.113549.1.1.1": "rsaEncryption",
"1.2.840.113549.1.1.2": "md2WithRSAEncryption",
"1.2.840.113549.1.1.3": "md4WithRSAEncryption",
"1.2.840.113549.1.1.4": "md5WithRSAEncryption",
"1.2.840.113549.1.1.5": "sha1-with-rsa-signature",
"1.2.840.113549.1.1.6": "rsaOAEPEncryptionSET",
"1.2.840.113549.1.1.7": "id-RSAES-OAEP",
"1.2.840.113549.1.1.8": "id-mgf1",
"1.2.840.113549.1.1.9": "id-pSpecified",
"1.2.840.113549.1.1.10": "rsassa-pss",
"1.2.840.113549.1.1.11": "sha256WithRSAEncryption",
"1.2.840.113549.1.1.12": "sha384WithRSAEncryption",
"1.2.840.113549.1.1.13": "sha512WithRSAEncryption",
"1.2.840.113549.1.1.14": "sha224WithRSAEncryption"
}
# secsig oiw #
secsig_oids = {
"1.3.14.3.2.26": "sha1"
}
# pkcs9 #
pkcs9_oids = {
"1.2.840.113549.1.9.0": "modules",
"1.2.840.113549.1.9.1": "emailAddress",
"1.2.840.113549.1.9.2": "unstructuredName",
"1.2.840.113549.1.9.3": "contentType",
"1.2.840.113549.1.9.4": "messageDigest",
"1.2.840.113549.1.9.5": "signing-time",
"1.2.840.113549.1.9.6": "countersignature",
"1.2.840.113549.1.9.7": "challengePassword",
"1.2.840.113549.1.9.8": "unstructuredAddress",
"1.2.840.113549.1.9.9": "extendedCertificateAttributes",
"1.2.840.113549.1.9.13": "signingDescription",
"1.2.840.113549.1.9.14": "extensionRequest",
"1.2.840.113549.1.9.15": "smimeCapabilities",
"1.2.840.113549.1.9.16": "smime",
"1.2.840.113549.1.9.17": "pgpKeyID",
"1.2.840.113549.1.9.20": "friendlyName",
"1.2.840.113549.1.9.21": "localKeyID",
"1.2.840.113549.1.9.22": "certTypes",
"1.2.840.113549.1.9.23": "crlTypes",
"1.2.840.113549.1.9.24": "pkcs-9-oc",
"1.2.840.113549.1.9.25": "pkcs-9-at",
"1.2.840.113549.1.9.26": "pkcs-9-sx",
"1.2.840.113549.1.9.27": "pkcs-9-mr",
"1.2.840.113549.1.9.52": "id-aa-CMSAlgorithmProtection"
}
# x509 #
attributeType_oids = {
"2.5.4.0": "objectClass",
"2.5.4.1": "aliasedEntryName",
"2.5.4.2": "knowledgeInformation",
"2.5.4.3": "commonName",
"2.5.4.4": "surname",
"2.5.4.5": "serialNumber",
"2.5.4.6": "countryName",
"2.5.4.7": "localityName",
"2.5.4.8": "stateOrProvinceName",
"2.5.4.9": "streetAddress",
"2.5.4.10": "organizationName",
"2.5.4.11": "organizationUnitName",
"2.5.4.12": "title",
"2.5.4.13": "description",
"2.5.4.14": "searchGuide",
"2.5.4.15": "businessCategory",
"2.5.4.16": "postalAddress",
"2.5.4.17": "postalCode",
"2.5.4.18": "postOfficeBox",
"2.5.4.19": "physicalDeliveryOfficeName",
"2.5.4.20": "telephoneNumber",
"2.5.4.21": "telexNumber",
"2.5.4.22": "teletexTerminalIdentifier",
"2.5.4.23": "facsimileTelephoneNumber",
"2.5.4.24": "x121Address",
"2.5.4.25": "internationalISDNNumber",
"2.5.4.26": "registeredAddress",
"2.5.4.27": "destinationIndicator",
"2.5.4.28": "preferredDeliveryMethod",
"2.5.4.29": "presentationAddress",
"2.5.4.30": "supportedApplicationContext",
"2.5.4.31": "member",
"2.5.4.32": "owner",
"2.5.4.33": "roleOccupant",
"2.5.4.34": "seeAlso",
"2.5.4.35": "userPassword",
"2.5.4.36": "userCertificate",
"2.5.4.37": "cACertificate",
"2.5.4.38": "authorityRevocationList",
"2.5.4.39": "certificateRevocationList",
"2.5.4.40": "crossCertificatePair",
"2.5.4.41": "name",
"2.5.4.42": "givenName",
"2.5.4.43": "initials",
"2.5.4.44": "generationQualifier",
"2.5.4.45": "uniqueIdentifier",
"2.5.4.46": "dnQualifier",
"2.5.4.47": "enhancedSearchGuide",
"2.5.4.48": "protocolInformation",
"2.5.4.49": "distinguishedName",
"2.5.4.50": "uniqueMember",
"2.5.4.51": "houseIdentifier",
"2.5.4.52": "supportedAlgorithms",
"2.5.4.53": "deltaRevocationList",
"2.5.4.54": "dmdName",
"2.5.4.55": "clearance",
"2.5.4.56": "defaultDirQop",
"2.5.4.57": "attributeIntegrityInfo",
"2.5.4.58": "attributeCertificate",
"2.5.4.59": "attributeCertificateRevocationList",
"2.5.4.60": "confKeyInfo",
"2.5.4.61": "aACertificate",
"2.5.4.62": "attributeDescriptorCertificate",
"2.5.4.63": "attributeAuthorityRevocationList",
"2.5.4.64": "family-information",
"2.5.4.65": "pseudonym",
"2.5.4.66": "communicationsService",
"2.5.4.67": "communicationsNetwork",
"2.5.4.68": "certificationPracticeStmt",
"2.5.4.69": "certificatePolicy",
"2.5.4.70": "pkiPath",
"2.5.4.71": "privPolicy",
"2.5.4.72": "role",
"2.5.4.73": "delegationPath",
"2.5.4.74": "protPrivPolicy",
"2.5.4.75": "xMLPrivilegeInfo",
"2.5.4.76": "xmlPrivPolicy",
"2.5.4.77": "uuidpair",
"2.5.4.78": "tagOid",
"2.5.4.79": "uiiFormat",
"2.5.4.80": "uiiInUrh",
"2.5.4.81": "contentUrl",
"2.5.4.82": "permission",
"2.5.4.83": "uri",
"2.5.4.84": "pwdAttribute",
"2.5.4.85": "userPwd",
"2.5.4.86": "urn",
"2.5.4.87": "url",
"2.5.4.88": "utmCoordinates",
"2.5.4.89": "urnC",
"2.5.4.90": "uii",
"2.5.4.91": "epc",
"2.5.4.92": "tagAfi",
"2.5.4.93": "epcFormat",
"2.5.4.94": "epcInUrn",
"2.5.4.95": "ldapUrl",
"2.5.4.96": "ldapUrl",
"2.5.4.97": "organizationIdentifier"
}
certificateExtension_oids = {
"2.5.29.1": "authorityKeyIdentifier",
"2.5.29.2": "keyAttributes",
"2.5.29.3": "certificatePolicies",
"2.5.29.4": "keyUsageRestriction",
"2.5.29.5": "policyMapping",
"2.5.29.6": "subtreesConstraint",
"2.5.29.7": "subjectAltName",
"2.5.29.8": "issuerAltName",
"2.5.29.9": "subjectDirectoryAttributes",
"2.5.29.10": "basicConstraints",
"2.5.29.14": "subjectKeyIdentifier",
"2.5.29.15": "keyUsage",
"2.5.29.16": "privateKeyUsagePeriod",
"2.5.29.17": "subjectAltName",
"2.5.29.18": "issuerAltName",
"2.5.29.19": "basicConstraints",
"2.5.29.20": "cRLNumber",
"2.5.29.21": "reasonCode",
"2.5.29.22": "expirationDate",
"2.5.29.23": "instructionCode",
"2.5.29.24": "invalidityDate",
"2.5.29.25": "cRLDistributionPoints",
"2.5.29.26": "issuingDistributionPoint",
"2.5.29.27": "deltaCRLIndicator",
"2.5.29.28": "issuingDistributionPoint",
"2.5.29.29": "certificateIssuer",
"2.5.29.30": "nameConstraints",
"2.5.29.31": "cRLDistributionPoints",
"2.5.29.32": "certificatePolicies",
"2.5.29.33": "policyMappings",
"2.5.29.34": "policyConstraints",
"2.5.29.35": "authorityKeyIdentifier",
"2.5.29.36": "policyConstraints",
"2.5.29.37": "extKeyUsage",
"2.5.29.38": "authorityAttributeIdentifier",
"2.5.29.39": "roleSpecCertIdentifier",
"2.5.29.40": "cRLStreamIdentifier",
"2.5.29.41": "basicAttConstraints",
"2.5.29.42": "delegatedNameConstraints",
"2.5.29.43": "timeSpecification",
"2.5.29.44": "cRLScope",
"2.5.29.45": "statusReferrals",
"2.5.29.46": "freshestCRL",
"2.5.29.47": "orderedList",
"2.5.29.48": "attributeDescriptor",
"2.5.29.49": "userNotice",
"2.5.29.50": "sOAIdentifier",
"2.5.29.51": "baseUpdateTime",
"2.5.29.52": "acceptableCertPolicies",
"2.5.29.53": "deltaInfo",
"2.5.29.54": "inhibitAnyPolicy",
"2.5.29.55": "targetInformation",
"2.5.29.56": "noRevAvail",
"2.5.29.57": "acceptablePrivilegePolicies",
"2.5.29.58": "id-ce-toBeRevoked",
"2.5.29.59": "id-ce-RevokedGroups",
"2.5.29.60": "id-ce-expiredCertsOnCRL",
"2.5.29.61": "indirectIssuer",
"2.5.29.62": "id-ce-noAssertion",
"2.5.29.63": "id-ce-aAissuingDistributionPoint",
"2.5.29.64": "id-ce-issuedOnBehaIFOF",
"2.5.29.65": "id-ce-singleUse",
"2.5.29.66": "id-ce-groupAC",
"2.5.29.67": "id-ce-allowedAttAss",
"2.5.29.68": "id-ce-attributeMappings",
"2.5.29.69": "id-ce-holderNameConstraints"
}
certExt_oids = {
"2.16.840.1.113730.1.1": "cert-type",
"2.16.840.1.113730.1.2": "base-url",
"2.16.840.1.113730.1.3": "revocation-url",
"2.16.840.1.113730.1.4": "ca-revocation-url",
"2.16.840.1.113730.1.5": "ca-crl-url",
"2.16.840.1.113730.1.6": "ca-cert-url",
"2.16.840.1.113730.1.7": "renewal-url",
"2.16.840.1.113730.1.8": "ca-policy-url",
"2.16.840.1.113730.1.9": "homepage-url",
"2.16.840.1.113730.1.10": "entity-logo",
"2.16.840.1.113730.1.11": "user-picture",
"2.16.840.1.113730.1.12": "ssl-server-name",
"2.16.840.1.113730.1.13": "comment",
"2.16.840.1.113730.1.14": "lost-password-url",
"2.16.840.1.113730.1.15": "cert-renewal-time",
"2.16.840.1.113730.1.16": "aia",
"2.16.840.1.113730.1.17": "cert-scope-of-use",
}
certPkixPe_oids = {
"1.3.6.1.5.5.7.1.1": "authorityInfoAccess",
"1.3.6.1.5.5.7.1.2": "biometricInfo",
"1.3.6.1.5.5.7.1.3": "qcStatements",
"1.3.6.1.5.5.7.1.4": "auditIdentity",
"1.3.6.1.5.5.7.1.6": "aaControls",
"1.3.6.1.5.5.7.1.10": "proxying",
"1.3.6.1.5.5.7.1.11": "subjectInfoAccess"
}
certPkixQt_oids = {
"1.3.6.1.5.5.7.2.1": "cps",
"1.3.6.1.5.5.7.2.2": "unotice"
}
certPkixKp_oids = {
"1.3.6.1.5.5.7.3.1": "serverAuth",
"1.3.6.1.5.5.7.3.2": "clientAuth",
"1.3.6.1.5.5.7.3.3": "codeSigning",
"1.3.6.1.5.5.7.3.4": "emailProtection",
"1.3.6.1.5.5.7.3.5": "ipsecEndSystem",
"1.3.6.1.5.5.7.3.6": "ipsecTunnel",
"1.3.6.1.5.5.7.3.7": "ipsecUser",
"1.3.6.1.5.5.7.3.8": "timeStamping",
"1.3.6.1.5.5.7.3.9": "ocspSigning",
"1.3.6.1.5.5.7.3.10": "dvcs",
"1.3.6.1.5.5.7.3.21": "secureShellClient",
"1.3.6.1.5.5.7.3.22": "secureShellServer"
}
certPkixAd_oids = {
"1.3.6.1.5.5.7.48.1": "ocsp",
"1.3.6.1.5.5.7.48.2": "caIssuers",
"1.3.6.1.5.5.7.48.3": "timestamping",
"1.3.6.1.5.5.7.48.4": "id-ad-dvcs",
"1.3.6.1.5.5.7.48.5": "id-ad-caRepository",
"1.3.6.1.5.5.7.48.6": "id-pkix-ocsp-archive-cutoff",
"1.3.6.1.5.5.7.48.7": "id-pkix-ocsp-service-locator",
"1.3.6.1.5.5.7.48.12": "id-ad-cmc",
"1.3.6.1.5.5.7.48.1.1": "basic-response"
}
# ansi-x962 #
x962KeyType_oids = {
"1.2.840.10045.1.1": "prime-field",
"1.2.840.10045.1.2": "characteristic-two-field",
"1.2.840.10045.2.1": "ecPublicKey",
}
x962Signature_oids = {
"1.2.840.10045.4.1": "ecdsa-with-SHA1",
"1.2.840.10045.4.2": "ecdsa-with-Recommended",
"1.2.840.10045.4.3.1": "ecdsa-with-SHA224",
"1.2.840.10045.4.3.2": "ecdsa-with-SHA256",
"1.2.840.10045.4.3.3": "ecdsa-with-SHA384",
"1.2.840.10045.4.3.4": "ecdsa-with-SHA512"
}
# elliptic curves #
ansiX962Curve_oids = {
"1.2.840.10045.3.1.1": "prime192v1",
"1.2.840.10045.3.1.2": "prime192v2",
"1.2.840.10045.3.1.3": "prime192v3",
"1.2.840.10045.3.1.4": "prime239v1",
"1.2.840.10045.3.1.5": "prime239v2",
"1.2.840.10045.3.1.6": "prime239v3",
"1.2.840.10045.3.1.7": "prime256v1"
}
certicomCurve_oids = {
"1.3.132.0.1": "ansit163k1",
"1.3.132.0.2": "ansit163r1",
"1.3.132.0.3": "ansit239k1",
"1.3.132.0.4": "sect113r1",
"1.3.132.0.5": "sect113r2",
"1.3.132.0.6": "secp112r1",
"1.3.132.0.7": "secp112r2",
"1.3.132.0.8": "ansip160r1",
"1.3.132.0.9": "ansip160k1",
"1.3.132.0.10": "ansip256k1",
"1.3.132.0.15": "ansit163r2",
"1.3.132.0.16": "ansit283k1",
"1.3.132.0.17": "ansit283r1",
"1.3.132.0.22": "sect131r1",
"1.3.132.0.24": "ansit193r1",
"1.3.132.0.25": "ansit193r2",
"1.3.132.0.26": "ansit233k1",
"1.3.132.0.27": "ansit233r1",
"1.3.132.0.28": "secp128r1",
"1.3.132.0.29": "secp128r2",
"1.3.132.0.30": "ansip160r2",
"1.3.132.0.31": "ansip192k1",
"1.3.132.0.32": "ansip224k1",
"1.3.132.0.33": "ansip224r1",
"1.3.132.0.34": "ansip384r1",
"1.3.132.0.35": "ansip521r1",
"1.3.132.0.36": "ansit409k1",
"1.3.132.0.37": "ansit409r1",
"1.3.132.0.38": "ansit571k1",
"1.3.132.0.39": "ansit571r1"
}
# policies #
certPolicy_oids = {
"anyPolicy": "2.5.29.32.0"
}
# from Chromium source code (ev_root_ca_metadata.cc)
evPolicy_oids = {
'1.2.392.200091.100.721.1': 'EV Security Communication RootCA1',
'1.2.616.1.113527.2.5.1.1': 'EV Certum Trusted Network CA',
'1.3.159.1.17.1': 'EV Actualis Authentication Root CA',
'1.3.6.1.4.1.13177.10.1.3.10': 'EV Autoridad de Certificacion Firmaprofesional CIF A62634068', # noqa: E501
'1.3.6.1.4.1.14370.1.6': 'EV GeoTrust Primary Certification Authority',
'1.3.6.1.4.1.14777.6.1.1': 'EV Izenpe.com roots Business',
'1.3.6.1.4.1.14777.6.1.2': 'EV Izenpe.com roots Government',
'1.3.6.1.4.1.17326.10.14.2.1.2': 'EV AC Camerfirma S.A. Chambers of Commerce Root - 2008', # noqa: E501
'1.3.6.1.4.1.17326.10.14.2.2.2': 'EV AC Camerfirma S.A. Chambers of Commerce Root - 2008', # noqa: E501
'1.3.6.1.4.1.17326.10.8.12.1.2': 'EV AC Camerfirma S.A. Global Chambersign Root - 2008', # noqa: E501
'1.3.6.1.4.1.17326.10.8.12.2.2': 'EV AC Camerfirma S.A. Global Chambersign Root - 2008', # noqa: E501
'1.3.6.1.4.1.22234.2.5.2.3.1': 'EV CertPlus Class 2 Primary CA (KEYNECTIS)', # noqa: E501
'1.3.6.1.4.1.23223.1.1.1': 'EV StartCom Certification Authority',
'1.3.6.1.4.1.29836.1.10': 'EV China Internet Network Information Center EV Certificates Root', # noqa: E501
'1.3.6.1.4.1.311.60.2.1.1': 'jurisdictionOfIncorporationLocalityName',
'1.3.6.1.4.1.311.60.2.1.2': 'jurisdictionOfIncorporationStateOrProvinceName', # noqa: E501
'1.3.6.1.4.1.311.60.2.1.3': 'jurisdictionOfIncorporationCountryName',
'1.3.6.1.4.1.34697.2.1': 'EV AffirmTrust Commercial',
'1.3.6.1.4.1.34697.2.2': 'EV AffirmTrust Networking',
'1.3.6.1.4.1.34697.2.3': 'EV AffirmTrust Premium',
'1.3.6.1.4.1.34697.2.4': 'EV AffirmTrust Premium ECC',
'1.3.6.1.4.1.36305.2': 'EV Certificate Authority of WoSign',
'1.3.6.1.4.1.40869.1.1.22.3': 'EV TWCA Roots',
'1.3.6.1.4.1.4146.1.1': 'EV GlobalSign Root CAs',
'1.3.6.1.4.1.4788.2.202.1': 'EV D-TRUST Root Class 3 CA 2 EV 2009',
'1.3.6.1.4.1.6334.1.100.1': 'EV Cybertrust Global Root',
'1.3.6.1.4.1.6449.1.2.1.5.1': 'EV USERTrust Certification Authorities',
'1.3.6.1.4.1.781.1.2.1.8.1': 'EV Network Solutions Certificate Authority',
'1.3.6.1.4.1.782.1.2.1.8.1': 'EV AddTrust External CA Root',
'1.3.6.1.4.1.7879.13.24.1': 'EV T-Telessec GlobalRoot Class 3',
'1.3.6.1.4.1.8024.0.2.100.1.2': 'EV QuoVadis Roots',
'2.16.528.1.1003.1.2.7': 'EV Staat der Nederlanden EV Root CA',
'2.16.578.1.26.1.3.3': 'EV Buypass Class 3',
'2.16.756.1.83.21.0': 'EV Swisscom Root EV CA 2',
'2.16.756.1.89.1.2.1.1': 'EV SwissSign Gold CA - G2',
'2.16.792.3.0.4.1.1.4': 'EV E-Tugra Certification Authority',
'2.16.840.1.113733.1.7.23.6': 'EV VeriSign Certification Authorities',
'2.16.840.1.113733.1.7.48.1': 'EV thawte CAs',
'2.16.840.1.114028.10.1.2': 'EV Entrust Certification Authority',
'2.16.840.1.114171.500.9': 'EV Wells Fargo WellsSecure Public Root Certification Authority', # noqa: E501
'2.16.840.1.114404.1.1.2.4.1': 'EV XRamp Global Certification Authority',
'2.16.840.1.114412.2.1': 'EV DigiCert High Assurance EV Root CA',
'2.16.840.1.114413.1.7.23.3': 'EV ValiCert Class 2 Policy Validation Authority', # noqa: E501
'2.16.840.1.114414.1.7.23.3': 'EV Starfield Certificate Authority',
'2.16.840.1.114414.1.7.24.3': 'EV Starfield Service Certificate Authority' # noqa: E501
}
x509_oids_sets = [
pkcs1_oids,
secsig_oids,
pkcs9_oids,
attributeType_oids,
certificateExtension_oids,
certExt_oids,
certPkixPe_oids,
certPkixQt_oids,
certPkixKp_oids,
certPkixAd_oids,
certPolicy_oids,
evPolicy_oids,
x962KeyType_oids,
x962Signature_oids,
ansiX962Curve_oids,
certicomCurve_oids
]
x509_oids = {}
for oids_set in x509_oids_sets:
x509_oids.update(oids_set)
conf.mib = MIBDict(_name="MIB", **x509_oids)
#########################
# Hash mapping helper #
#########################
# This dict enables static access to string references to the hash functions
# of some algorithms from pkcs1_oids and x962Signature_oids.
hash_by_oid = {
"1.2.840.113549.1.1.2": "md2",
"1.2.840.113549.1.1.3": "md4",
"1.2.840.113549.1.1.4": "md5",
"1.2.840.113549.1.1.5": "sha1",
"1.2.840.113549.1.1.11": "sha256",
"1.2.840.113549.1.1.12": "sha384",
"1.2.840.113549.1.1.13": "sha512",
"1.2.840.113549.1.1.14": "sha224",
"1.2.840.10045.4.1": "sha1",
"1.2.840.10045.4.3.1": "sha224",
"1.2.840.10045.4.3.2": "sha256",
"1.2.840.10045.4.3.3": "sha384",
"1.2.840.10045.4.3.4": "sha512"
}

665
libs/scapy/asn1fields.py Executable file
View file

@ -0,0 +1,665 @@
# This file is part of Scapy
# See http://www.secdev.org/projects/scapy for more information
# Copyright (C) Philippe Biondi <phil@secdev.org>
# Enhanced by Maxence Tury <maxence.tury@ssi.gouv.fr>
# This program is published under a GPLv2 license
"""
Classes that implement ASN.1 data structures.
"""
from __future__ import absolute_import
from scapy.asn1.asn1 import ASN1_Class_UNIVERSAL, ASN1_NULL, ASN1_Error, \
ASN1_Object, ASN1_INTEGER
from scapy.asn1.ber import BER_tagging_dec, BER_Decoding_Error, BER_id_dec, \
BER_tagging_enc
from scapy.volatile import RandInt, RandChoice, RandNum, RandString, RandOID, \
GeneralizedTime
from scapy.compat import orb, raw
from scapy.base_classes import BasePacket
from scapy.utils import binrepr
from scapy import packet
from functools import reduce
import scapy.modules.six as six
from scapy.modules.six.moves import range
class ASN1F_badsequence(Exception):
pass
class ASN1F_element(object):
pass
##########################
# Basic ASN1 Field #
##########################
class ASN1F_field(ASN1F_element):
holds_packets = 0
islist = 0
ASN1_tag = ASN1_Class_UNIVERSAL.ANY
context = ASN1_Class_UNIVERSAL
def __init__(self, name, default, context=None,
implicit_tag=None, explicit_tag=None,
flexible_tag=False):
self.context = context
self.name = name
if default is None:
self.default = None
elif isinstance(default, ASN1_NULL):
self.default = default
else:
self.default = self.ASN1_tag.asn1_object(default)
self.flexible_tag = flexible_tag
if (implicit_tag is not None) and (explicit_tag is not None):
err_msg = "field cannot be both implicitly and explicitly tagged"
raise ASN1_Error(err_msg)
self.implicit_tag = implicit_tag
self.explicit_tag = explicit_tag
# network_tag gets useful for ASN1F_CHOICE
self.network_tag = implicit_tag or explicit_tag or self.ASN1_tag
def i2repr(self, pkt, x):
return repr(x)
def i2h(self, pkt, x):
return x
def any2i(self, pkt, x):
return x
def m2i(self, pkt, s):
"""
The good thing about safedec is that it may still decode ASN1
even if there is a mismatch between the expected tag (self.ASN1_tag)
and the actual tag; the decoded ASN1 object will simply be put
into an ASN1_BADTAG object. However, safedec prevents the raising of
exceptions needed for ASN1F_optional processing.
Thus we use 'flexible_tag', which should be False with ASN1F_optional.
Regarding other fields, we might need to know whether encoding went
as expected or not. Noticeably, input methods from cert.py expect
certain exceptions to be raised. Hence default flexible_tag is False.
"""
diff_tag, s = BER_tagging_dec(s, hidden_tag=self.ASN1_tag,
implicit_tag=self.implicit_tag,
explicit_tag=self.explicit_tag,
safe=self.flexible_tag)
if diff_tag is not None:
# this implies that flexible_tag was True
if self.implicit_tag is not None:
self.implicit_tag = diff_tag
elif self.explicit_tag is not None:
self.explicit_tag = diff_tag
codec = self.ASN1_tag.get_codec(pkt.ASN1_codec)
if self.flexible_tag:
return codec.safedec(s, context=self.context)
else:
return codec.dec(s, context=self.context)
def i2m(self, pkt, x):
if x is None:
return b""
if isinstance(x, ASN1_Object):
if (self.ASN1_tag == ASN1_Class_UNIVERSAL.ANY or
x.tag == ASN1_Class_UNIVERSAL.RAW or
x.tag == ASN1_Class_UNIVERSAL.ERROR or
self.ASN1_tag == x.tag):
s = x.enc(pkt.ASN1_codec)
else:
raise ASN1_Error("Encoding Error: got %r instead of an %r for field [%s]" % (x, self.ASN1_tag, self.name)) # noqa: E501
else:
s = self.ASN1_tag.get_codec(pkt.ASN1_codec).enc(x)
return BER_tagging_enc(s, implicit_tag=self.implicit_tag,
explicit_tag=self.explicit_tag)
def extract_packet(self, cls, s):
if len(s) > 0:
try:
c = cls(s)
except ASN1F_badsequence:
c = packet.Raw(s)
cpad = c.getlayer(packet.Raw)
s = b""
if cpad is not None:
s = cpad.load
del(cpad.underlayer.payload)
return c, s
else:
return None, s
def build(self, pkt):
return self.i2m(pkt, getattr(pkt, self.name))
def dissect(self, pkt, s):
v, s = self.m2i(pkt, s)
self.set_val(pkt, v)
return s
def do_copy(self, x):
if hasattr(x, "copy"):
return x.copy()
if isinstance(x, list):
x = x[:]
for i in range(len(x)):
if isinstance(x[i], BasePacket):
x[i] = x[i].copy()
return x
def set_val(self, pkt, val):
setattr(pkt, self.name, val)
def is_empty(self, pkt):
return getattr(pkt, self.name) is None
def get_fields_list(self):
return [self]
def __str__(self):
return repr(self)
def randval(self):
return RandInt()
############################
# Simple ASN1 Fields #
############################
class ASN1F_BOOLEAN(ASN1F_field):
ASN1_tag = ASN1_Class_UNIVERSAL.BOOLEAN
def randval(self):
return RandChoice(True, False)
class ASN1F_INTEGER(ASN1F_field):
ASN1_tag = ASN1_Class_UNIVERSAL.INTEGER
def randval(self):
return RandNum(-2**64, 2**64 - 1)
class ASN1F_enum_INTEGER(ASN1F_INTEGER):
def __init__(self, name, default, enum, context=None,
implicit_tag=None, explicit_tag=None):
ASN1F_INTEGER.__init__(self, name, default, context=context,
implicit_tag=implicit_tag,
explicit_tag=explicit_tag)
i2s = self.i2s = {}
s2i = self.s2i = {}
if isinstance(enum, list):
keys = range(len(enum))
else:
keys = list(enum)
if any(isinstance(x, six.string_types) for x in keys):
i2s, s2i = s2i, i2s
for k in keys:
i2s[k] = enum[k]
s2i[enum[k]] = k
def i2m(self, pkt, s):
if isinstance(s, str):
s = self.s2i.get(s)
return super(ASN1F_enum_INTEGER, self).i2m(pkt, s)
def i2repr(self, pkt, x):
if x is not None and isinstance(x, ASN1_INTEGER):
r = self.i2s.get(x.val)
if r:
return "'%s' %s" % (r, repr(x))
return repr(x)
class ASN1F_BIT_STRING(ASN1F_field):
ASN1_tag = ASN1_Class_UNIVERSAL.BIT_STRING
def __init__(self, name, default, default_readable=True, context=None,
implicit_tag=None, explicit_tag=None):
if default is not None and default_readable:
default = b"".join(binrepr(orb(x)).zfill(8).encode("utf8") for x in default) # noqa: E501
ASN1F_field.__init__(self, name, default, context=context,
implicit_tag=implicit_tag,
explicit_tag=explicit_tag)
def randval(self):
return RandString(RandNum(0, 1000))
class ASN1F_STRING(ASN1F_field):
ASN1_tag = ASN1_Class_UNIVERSAL.STRING
def randval(self):
return RandString(RandNum(0, 1000))
class ASN1F_NULL(ASN1F_INTEGER):
ASN1_tag = ASN1_Class_UNIVERSAL.NULL
class ASN1F_OID(ASN1F_field):
ASN1_tag = ASN1_Class_UNIVERSAL.OID
def randval(self):
return RandOID()
class ASN1F_ENUMERATED(ASN1F_enum_INTEGER):
ASN1_tag = ASN1_Class_UNIVERSAL.ENUMERATED
class ASN1F_UTF8_STRING(ASN1F_STRING):
ASN1_tag = ASN1_Class_UNIVERSAL.UTF8_STRING
class ASN1F_NUMERIC_STRING(ASN1F_STRING):
ASN1_tag = ASN1_Class_UNIVERSAL.NUMERIC_STRING
class ASN1F_PRINTABLE_STRING(ASN1F_STRING):
ASN1_tag = ASN1_Class_UNIVERSAL.PRINTABLE_STRING
class ASN1F_T61_STRING(ASN1F_STRING):
ASN1_tag = ASN1_Class_UNIVERSAL.T61_STRING
class ASN1F_VIDEOTEX_STRING(ASN1F_STRING):
ASN1_tag = ASN1_Class_UNIVERSAL.VIDEOTEX_STRING
class ASN1F_IA5_STRING(ASN1F_STRING):
ASN1_tag = ASN1_Class_UNIVERSAL.IA5_STRING
class ASN1F_UTC_TIME(ASN1F_STRING):
ASN1_tag = ASN1_Class_UNIVERSAL.UTC_TIME
def randval(self):
return GeneralizedTime()
class ASN1F_GENERALIZED_TIME(ASN1F_STRING):
ASN1_tag = ASN1_Class_UNIVERSAL.GENERALIZED_TIME
def randval(self):
return GeneralizedTime()
class ASN1F_ISO646_STRING(ASN1F_STRING):
ASN1_tag = ASN1_Class_UNIVERSAL.ISO646_STRING
class ASN1F_UNIVERSAL_STRING(ASN1F_STRING):
ASN1_tag = ASN1_Class_UNIVERSAL.UNIVERSAL_STRING
class ASN1F_BMP_STRING(ASN1F_STRING):
ASN1_tag = ASN1_Class_UNIVERSAL.BMP_STRING
class ASN1F_SEQUENCE(ASN1F_field):
# Here is how you could decode a SEQUENCE
# with an unknown, private high-tag prefix :
# class PrivSeq(ASN1_Packet):
# ASN1_codec = ASN1_Codecs.BER
# ASN1_root = ASN1F_SEQUENCE(
# <asn1 field #0>,
# ...
# <asn1 field #N>,
# explicit_tag=0,
# flexible_tag=True)
# Because we use flexible_tag, the value of the explicit_tag does not matter. # noqa: E501
ASN1_tag = ASN1_Class_UNIVERSAL.SEQUENCE
holds_packets = 1
def __init__(self, *seq, **kwargs):
name = "dummy_seq_name"
default = [field.default for field in seq]
for kwarg in ["context", "implicit_tag",
"explicit_tag", "flexible_tag"]:
setattr(self, kwarg, kwargs.get(kwarg))
ASN1F_field.__init__(self, name, default, context=self.context,
implicit_tag=self.implicit_tag,
explicit_tag=self.explicit_tag,
flexible_tag=self.flexible_tag)
self.seq = seq
self.islist = len(seq) > 1
def __repr__(self):
return "<%s%r>" % (self.__class__.__name__, self.seq)
def is_empty(self, pkt):
return all(f.is_empty(pkt) for f in self.seq)
def get_fields_list(self):
return reduce(lambda x, y: x + y.get_fields_list(), self.seq, [])
def m2i(self, pkt, s):
"""
ASN1F_SEQUENCE behaves transparently, with nested ASN1_objects being
dissected one by one. Because we use obj.dissect (see loop below)
instead of obj.m2i (as we trust dissect to do the appropriate set_vals)
we do not directly retrieve the list of nested objects.
Thus m2i returns an empty list (along with the proper remainder).
It is discarded by dissect() and should not be missed elsewhere.
"""
diff_tag, s = BER_tagging_dec(s, hidden_tag=self.ASN1_tag,
implicit_tag=self.implicit_tag,
explicit_tag=self.explicit_tag,
safe=self.flexible_tag)
if diff_tag is not None:
if self.implicit_tag is not None:
self.implicit_tag = diff_tag
elif self.explicit_tag is not None:
self.explicit_tag = diff_tag
codec = self.ASN1_tag.get_codec(pkt.ASN1_codec)
i, s, remain = codec.check_type_check_len(s)
if len(s) == 0:
for obj in self.seq:
obj.set_val(pkt, None)
else:
for obj in self.seq:
try:
s = obj.dissect(pkt, s)
except ASN1F_badsequence:
break
if len(s) > 0:
raise BER_Decoding_Error("unexpected remainder", remaining=s)
return [], remain
def dissect(self, pkt, s):
_, x = self.m2i(pkt, s)
return x
def build(self, pkt):
s = reduce(lambda x, y: x + y.build(pkt), self.seq, b"")
return self.i2m(pkt, s)
class ASN1F_SET(ASN1F_SEQUENCE):
ASN1_tag = ASN1_Class_UNIVERSAL.SET
class ASN1F_SEQUENCE_OF(ASN1F_field):
ASN1_tag = ASN1_Class_UNIVERSAL.SEQUENCE
holds_packets = 1
islist = 1
def __init__(self, name, default, cls, context=None,
implicit_tag=None, explicit_tag=None):
self.cls = cls
ASN1F_field.__init__(self, name, None, context=context,
implicit_tag=implicit_tag, explicit_tag=explicit_tag) # noqa: E501
self.default = default
def is_empty(self, pkt):
return ASN1F_field.is_empty(self, pkt)
def m2i(self, pkt, s):
diff_tag, s = BER_tagging_dec(s, hidden_tag=self.ASN1_tag,
implicit_tag=self.implicit_tag,
explicit_tag=self.explicit_tag,
safe=self.flexible_tag)
if diff_tag is not None:
if self.implicit_tag is not None:
self.implicit_tag = diff_tag
elif self.explicit_tag is not None:
self.explicit_tag = diff_tag
codec = self.ASN1_tag.get_codec(pkt.ASN1_codec)
i, s, remain = codec.check_type_check_len(s)
lst = []
while s:
c, s = self.extract_packet(self.cls, s)
lst.append(c)
if len(s) > 0:
raise BER_Decoding_Error("unexpected remainder", remaining=s)
return lst, remain
def build(self, pkt):
val = getattr(pkt, self.name)
if isinstance(val, ASN1_Object) and val.tag == ASN1_Class_UNIVERSAL.RAW: # noqa: E501
s = val
elif val is None:
s = b""
else:
s = b"".join(raw(i) for i in val)
return self.i2m(pkt, s)
def randval(self):
return packet.fuzz(self.cls())
def __repr__(self):
return "<%s %s>" % (self.__class__.__name__, self.name)
class ASN1F_SET_OF(ASN1F_SEQUENCE_OF):
ASN1_tag = ASN1_Class_UNIVERSAL.SET
class ASN1F_IPADDRESS(ASN1F_STRING):
ASN1_tag = ASN1_Class_UNIVERSAL.IPADDRESS
class ASN1F_TIME_TICKS(ASN1F_INTEGER):
ASN1_tag = ASN1_Class_UNIVERSAL.TIME_TICKS
#############################
# Complex ASN1 Fields #
#############################
class ASN1F_optional(ASN1F_element):
def __init__(self, field):
field.flexible_tag = False
self._field = field
def __getattr__(self, attr):
return getattr(self._field, attr)
def m2i(self, pkt, s):
try:
return self._field.m2i(pkt, s)
except (ASN1_Error, ASN1F_badsequence, BER_Decoding_Error):
# ASN1_Error may be raised by ASN1F_CHOICE
return None, s
def dissect(self, pkt, s):
try:
return self._field.dissect(pkt, s)
except (ASN1_Error, ASN1F_badsequence, BER_Decoding_Error):
self._field.set_val(pkt, None)
return s
def build(self, pkt):
if self._field.is_empty(pkt):
return b""
return self._field.build(pkt)
def any2i(self, pkt, x):
return self._field.any2i(pkt, x)
def i2repr(self, pkt, x):
return self._field.i2repr(pkt, x)
class ASN1F_CHOICE(ASN1F_field):
"""
Multiple types are allowed: ASN1_Packet, ASN1F_field and ASN1F_PACKET(),
See layers/x509.py for examples.
Other ASN1F_field instances than ASN1F_PACKET instances must not be used.
"""
holds_packets = 1
ASN1_tag = ASN1_Class_UNIVERSAL.ANY
def __init__(self, name, default, *args, **kwargs):
if "implicit_tag" in kwargs:
err_msg = "ASN1F_CHOICE has been called with an implicit_tag"
raise ASN1_Error(err_msg)
self.implicit_tag = None
for kwarg in ["context", "explicit_tag"]:
setattr(self, kwarg, kwargs.get(kwarg))
ASN1F_field.__init__(self, name, None, context=self.context,
explicit_tag=self.explicit_tag)
self.default = default
self.current_choice = None
self.choices = {}
self.pktchoices = {}
for p in args:
if hasattr(p, "ASN1_root"): # should be ASN1_Packet
if hasattr(p.ASN1_root, "choices"):
for k, v in six.iteritems(p.ASN1_root.choices):
self.choices[k] = v # ASN1F_CHOICE recursion
else:
self.choices[p.ASN1_root.network_tag] = p
elif hasattr(p, "ASN1_tag"):
if isinstance(p, type): # should be ASN1F_field class
self.choices[p.ASN1_tag] = p
else: # should be ASN1F_PACKET instance
self.choices[p.network_tag] = p
self.pktchoices[hash(p.cls)] = (p.implicit_tag, p.explicit_tag) # noqa: E501
else:
raise ASN1_Error("ASN1F_CHOICE: no tag found for one field")
def m2i(self, pkt, s):
"""
First we have to retrieve the appropriate choice.
Then we extract the field/packet, according to this choice.
"""
if len(s) == 0:
raise ASN1_Error("ASN1F_CHOICE: got empty string")
_, s = BER_tagging_dec(s, hidden_tag=self.ASN1_tag,
explicit_tag=self.explicit_tag)
tag, _ = BER_id_dec(s)
if tag not in self.choices:
if self.flexible_tag:
choice = ASN1F_field
else:
raise ASN1_Error("ASN1F_CHOICE: unexpected field")
else:
choice = self.choices[tag]
if hasattr(choice, "ASN1_root"):
# we don't want to import ASN1_Packet in this module...
return self.extract_packet(choice, s)
elif isinstance(choice, type):
# XXX find a way not to instantiate the ASN1F_field
return choice(self.name, b"").m2i(pkt, s)
else:
# XXX check properly if this is an ASN1F_PACKET
return choice.m2i(pkt, s)
def i2m(self, pkt, x):
if x is None:
s = b""
else:
s = raw(x)
if hash(type(x)) in self.pktchoices:
imp, exp = self.pktchoices[hash(type(x))]
s = BER_tagging_enc(s, implicit_tag=imp,
explicit_tag=exp)
return BER_tagging_enc(s, explicit_tag=self.explicit_tag)
def randval(self):
randchoices = []
for p in six.itervalues(self.choices):
if hasattr(p, "ASN1_root"): # should be ASN1_Packet class
randchoices.append(packet.fuzz(p()))
elif hasattr(p, "ASN1_tag"):
if isinstance(p, type): # should be (basic) ASN1F_field class # noqa: E501
randchoices.append(p("dummy", None).randval())
else: # should be ASN1F_PACKET instance
randchoices.append(p.randval())
return RandChoice(*randchoices)
class ASN1F_PACKET(ASN1F_field):
holds_packets = 1
def __init__(self, name, default, cls, context=None,
implicit_tag=None, explicit_tag=None):
self.cls = cls
ASN1F_field.__init__(self, name, None, context=context,
implicit_tag=implicit_tag, explicit_tag=explicit_tag) # noqa: E501
if cls.ASN1_root.ASN1_tag == ASN1_Class_UNIVERSAL.SEQUENCE:
if implicit_tag is None and explicit_tag is None:
self.network_tag = 16 | 0x20
self.default = default
def m2i(self, pkt, s):
diff_tag, s = BER_tagging_dec(s, hidden_tag=self.cls.ASN1_root.ASN1_tag, # noqa: E501
implicit_tag=self.implicit_tag,
explicit_tag=self.explicit_tag,
safe=self.flexible_tag)
if diff_tag is not None:
if self.implicit_tag is not None:
self.implicit_tag = diff_tag
elif self.explicit_tag is not None:
self.explicit_tag = diff_tag
p, s = self.extract_packet(self.cls, s)
return p, s
def i2m(self, pkt, x):
if x is None:
s = b""
else:
s = raw(x)
return BER_tagging_enc(s, implicit_tag=self.implicit_tag,
explicit_tag=self.explicit_tag)
def randval(self):
return packet.fuzz(self.cls())
class ASN1F_BIT_STRING_ENCAPS(ASN1F_BIT_STRING):
"""
We may emulate simple string encapsulation with explicit_tag=0x04,
but we need a specific class for bit strings because of unused bits, etc.
"""
holds_packets = 1
def __init__(self, name, default, cls, context=None,
implicit_tag=None, explicit_tag=None):
self.cls = cls
ASN1F_BIT_STRING.__init__(self, name, None, context=context,
implicit_tag=implicit_tag,
explicit_tag=explicit_tag)
self.default = default
def m2i(self, pkt, s):
bit_string, remain = ASN1F_BIT_STRING.m2i(self, pkt, s)
if len(bit_string.val) % 8 != 0:
raise BER_Decoding_Error("wrong bit string", remaining=s)
p, s = self.extract_packet(self.cls, bit_string.val_readable)
if len(s) > 0:
raise BER_Decoding_Error("unexpected remainder", remaining=s)
return p, remain
def i2m(self, pkt, x):
s = b"" if x is None else raw(x)
s = b"".join(binrepr(orb(x)).zfill(8).encode("utf8") for x in s)
return ASN1F_BIT_STRING.i2m(self, pkt, s)
class ASN1F_FLAGS(ASN1F_BIT_STRING):
def __init__(self, name, default, mapping, context=None,
implicit_tag=None, explicit_tag=None):
self.mapping = mapping
ASN1F_BIT_STRING.__init__(self, name, default,
default_readable=False,
context=context,
implicit_tag=implicit_tag,
explicit_tag=explicit_tag)
def get_flags(self, pkt):
fbytes = getattr(pkt, self.name).val
return [self.mapping[i] for i, positional in enumerate(fbytes)
if positional == '1' and i < len(self.mapping)]
def i2repr(self, pkt, x):
if x is not None:
pretty_s = ", ".join(self.get_flags(pkt))
return pretty_s + " " + repr(x)
return repr(x)

35
libs/scapy/asn1packet.py Executable file
View file

@ -0,0 +1,35 @@
# This file is part of Scapy
# See http://www.secdev.org/projects/scapy for more information
# Copyright (C) Philippe Biondi <phil@secdev.org>
# This program is published under a GPLv2 license
"""
ASN.1 Packet
Packet holding data in Abstract Syntax Notation (ASN.1).
"""
from __future__ import absolute_import
from scapy.base_classes import Packet_metaclass
from scapy.packet import Packet
import scapy.modules.six as six
class ASN1Packet_metaclass(Packet_metaclass):
def __new__(cls, name, bases, dct):
if dct["ASN1_root"] is not None:
dct["fields_desc"] = dct["ASN1_root"].get_fields_list()
return super(ASN1Packet_metaclass, cls).__new__(cls, name, bases, dct)
class ASN1_Packet(six.with_metaclass(ASN1Packet_metaclass, Packet)):
ASN1_root = None
ASN1_codec = None
def self_build(self):
if self.raw_packet_cache is not None:
return self.raw_packet_cache
return self.ASN1_root.build(self)
def do_dissect(self, x):
return self.ASN1_root.dissect(self, x)

1030
libs/scapy/automaton.py Executable file

File diff suppressed because it is too large Load diff

203
libs/scapy/autorun.py Executable file
View file

@ -0,0 +1,203 @@
# This file is part of Scapy
# See http://www.secdev.org/projects/scapy for more information
# Copyright (C) Philippe Biondi <phil@secdev.org>
# This program is published under a GPLv2 license
"""
Run commands when the Scapy interpreter starts.
"""
from __future__ import print_function
import code
import sys
import importlib
from scapy.config import conf
from scapy.themes import NoTheme, DefaultTheme, HTMLTheme2, LatexTheme2
from scapy.error import Scapy_Exception
from scapy.utils import tex_escape
import scapy.modules.six as six
#########################
# Autorun stuff #
#########################
class StopAutorun(Scapy_Exception):
code_run = ""
class ScapyAutorunInterpreter(code.InteractiveInterpreter):
def __init__(self, *args, **kargs):
code.InteractiveInterpreter.__init__(self, *args, **kargs)
self.error = 0
def showsyntaxerror(self, *args, **kargs):
self.error = 1
return code.InteractiveInterpreter.showsyntaxerror(self, *args, **kargs) # noqa: E501
def showtraceback(self, *args, **kargs):
self.error = 1
exc_type, exc_value, exc_tb = sys.exc_info()
if isinstance(exc_value, StopAutorun):
raise exc_value
return code.InteractiveInterpreter.showtraceback(self, *args, **kargs)
def autorun_commands(cmds, my_globals=None, ignore_globals=None, verb=None):
sv = conf.verb
try:
try:
if my_globals is None:
my_globals = importlib.import_module(".all", "scapy").__dict__
if ignore_globals:
for ig in ignore_globals:
my_globals.pop(ig, None)
if verb is not None:
conf.verb = verb
interp = ScapyAutorunInterpreter(my_globals)
cmd = ""
cmds = cmds.splitlines()
cmds.append("") # ensure we finish multi-line commands
cmds.reverse()
six.moves.builtins.__dict__["_"] = None
while True:
if cmd:
sys.stderr.write(sys.__dict__.get("ps2", "... "))
else:
sys.stderr.write(str(sys.__dict__.get("ps1", sys.ps1)))
line = cmds.pop()
print(line)
cmd += "\n" + line
if interp.runsource(cmd):
continue
if interp.error:
return 0
cmd = ""
if len(cmds) <= 1:
break
except SystemExit:
pass
finally:
conf.verb = sv
return _ # noqa: F821
class StringWriter(object):
"""Util to mock sys.stdout and sys.stderr, and
store their output in a 's' var."""
def __init__(self, debug=None):
self.s = ""
self.debug = debug
def write(self, x):
if self.debug:
self.debug.write(x)
self.s += x
def flush(self):
if self.debug:
self.debug.flush()
def autorun_get_interactive_session(cmds, **kargs):
"""Create an interactive session and execute the
commands passed as "cmds" and return all output
:param cmds: a list of commands to run
:returns: (output, returned) contains both sys.stdout and sys.stderr logs
"""
sstdout, sstderr = sys.stdout, sys.stderr
sw = StringWriter()
try:
try:
sys.stdout = sys.stderr = sw
res = autorun_commands(cmds, **kargs)
except StopAutorun as e:
e.code_run = sw.s
raise
finally:
sys.stdout, sys.stderr = sstdout, sstderr
return sw.s, res
def autorun_get_interactive_live_session(cmds, **kargs):
"""Create an interactive session and execute the
commands passed as "cmds" and return all output
:param cmds: a list of commands to run
:returns: (output, returned) contains both sys.stdout and sys.stderr logs
"""
sstdout, sstderr = sys.stdout, sys.stderr
sw = StringWriter(debug=sstdout)
try:
try:
sys.stdout = sys.stderr = sw
res = autorun_commands(cmds, **kargs)
except StopAutorun as e:
e.code_run = sw.s
raise
finally:
sys.stdout, sys.stderr = sstdout, sstderr
return sw.s, res
def autorun_get_text_interactive_session(cmds, **kargs):
ct = conf.color_theme
try:
conf.color_theme = NoTheme()
s, res = autorun_get_interactive_session(cmds, **kargs)
finally:
conf.color_theme = ct
return s, res
def autorun_get_live_interactive_session(cmds, **kargs):
ct = conf.color_theme
try:
conf.color_theme = DefaultTheme()
s, res = autorun_get_interactive_live_session(cmds, **kargs)
finally:
conf.color_theme = ct
return s, res
def autorun_get_ansi_interactive_session(cmds, **kargs):
ct = conf.color_theme
try:
conf.color_theme = DefaultTheme()
s, res = autorun_get_interactive_session(cmds, **kargs)
finally:
conf.color_theme = ct
return s, res
def autorun_get_html_interactive_session(cmds, **kargs):
ct = conf.color_theme
to_html = lambda s: s.replace("<", "&lt;").replace(">", "&gt;").replace("#[#", "<").replace("#]#", ">") # noqa: E501
try:
try:
conf.color_theme = HTMLTheme2()
s, res = autorun_get_interactive_session(cmds, **kargs)
except StopAutorun as e:
e.code_run = to_html(e.code_run)
raise
finally:
conf.color_theme = ct
return to_html(s), res
def autorun_get_latex_interactive_session(cmds, **kargs):
ct = conf.color_theme
to_latex = lambda s: tex_escape(s).replace("@[@", "{").replace("@]@", "}").replace("@`@", "\\") # noqa: E501
try:
try:
conf.color_theme = LatexTheme2()
s, res = autorun_get_interactive_session(cmds, **kargs)
except StopAutorun as e:
e.code_run = to_latex(e.code_run)
raise
finally:
conf.color_theme = ct
return to_latex(s), res

360
libs/scapy/base_classes.py Executable file
View file

@ -0,0 +1,360 @@
# This file is part of Scapy
# See http://www.secdev.org/projects/scapy for more information
# Copyright (C) Philippe Biondi <phil@secdev.org>
# This program is published under a GPLv2 license
"""
Generators and packet meta classes.
"""
################
# Generators #
################
from __future__ import absolute_import
from functools import reduce
import operator
import os
import re
import random
import socket
import subprocess
import types
from scapy.consts import WINDOWS
from scapy.modules.six.moves import range
class Gen(object):
__slots__ = []
def __iter__(self):
return iter([])
def __iterlen__(self):
return sum(1 for _ in iter(self))
def _get_values(value):
"""Generate a range object from (start, stop[, step]) tuples, or
return value.
"""
if (isinstance(value, tuple) and (2 <= len(value) <= 3) and
all(hasattr(i, "__int__") for i in value)):
# We use values[1] + 1 as stop value for (x)range to maintain
# the behavior of using tuples as field `values`
return range(*((int(value[0]), int(value[1]) + 1) +
tuple(int(v) for v in value[2:])))
return value
class SetGen(Gen):
def __init__(self, values, _iterpacket=1):
self._iterpacket = _iterpacket
if isinstance(values, (list, BasePacketList)):
self.values = [_get_values(val) for val in values]
else:
self.values = [_get_values(values)]
def transf(self, element):
return element
def __iter__(self):
for i in self.values:
if (isinstance(i, Gen) and
(self._iterpacket or not isinstance(i, BasePacket))) or (
isinstance(i, (range, types.GeneratorType))):
for j in i:
yield j
else:
yield i
def __repr__(self):
return "<SetGen %r>" % self.values
class Net(Gen):
"""Generate a list of IPs from a network address or a name"""
name = "ip"
ip_regex = re.compile(r"^(\*|[0-2]?[0-9]?[0-9](-[0-2]?[0-9]?[0-9])?)\.(\*|[0-2]?[0-9]?[0-9](-[0-2]?[0-9]?[0-9])?)\.(\*|[0-2]?[0-9]?[0-9](-[0-2]?[0-9]?[0-9])?)\.(\*|[0-2]?[0-9]?[0-9](-[0-2]?[0-9]?[0-9])?)(/[0-3]?[0-9])?$") # noqa: E501
@staticmethod
def _parse_digit(a, netmask):
netmask = min(8, max(netmask, 0))
if a == "*":
a = (0, 256)
elif a.find("-") >= 0:
x, y = [int(d) for d in a.split('-')]
if x > y:
y = x
a = (x & (0xff << netmask), max(y, (x | (0xff >> (8 - netmask)))) + 1) # noqa: E501
else:
a = (int(a) & (0xff << netmask), (int(a) | (0xff >> (8 - netmask))) + 1) # noqa: E501
return a
@classmethod
def _parse_net(cls, net):
tmp = net.split('/') + ["32"]
if not cls.ip_regex.match(net):
tmp[0] = socket.gethostbyname(tmp[0])
netmask = int(tmp[1])
ret_list = [cls._parse_digit(x, y - netmask) for (x, y) in zip(tmp[0].split('.'), [8, 16, 24, 32])] # noqa: E501
return ret_list, netmask
def __init__(self, net):
self.repr = net
self.parsed, self.netmask = self._parse_net(net)
def __str__(self):
return next(self.__iter__(), None)
def __iter__(self):
for d in range(*self.parsed[3]):
for c in range(*self.parsed[2]):
for b in range(*self.parsed[1]):
for a in range(*self.parsed[0]):
yield "%i.%i.%i.%i" % (a, b, c, d)
def __iterlen__(self):
return reduce(operator.mul, ((y - x) for (x, y) in self.parsed), 1)
def choice(self):
return ".".join(str(random.randint(v[0], v[1] - 1)) for v in self.parsed) # noqa: E501
def __repr__(self):
return "Net(%r)" % self.repr
def __eq__(self, other):
if not other:
return False
if hasattr(other, "parsed"):
p2 = other.parsed
else:
p2, nm2 = self._parse_net(other)
return self.parsed == p2
def __ne__(self, other):
# Python 2.7 compat
return not self == other
__hash__ = None
def __contains__(self, other):
if hasattr(other, "parsed"):
p2 = other.parsed
else:
p2, nm2 = self._parse_net(other)
return all(a1 <= a2 and b1 >= b2 for (a1, b1), (a2, b2) in zip(self.parsed, p2)) # noqa: E501
def __rcontains__(self, other):
return self in self.__class__(other)
class OID(Gen):
name = "OID"
def __init__(self, oid):
self.oid = oid
self.cmpt = []
fmt = []
for i in oid.split("."):
if "-" in i:
fmt.append("%i")
self.cmpt.append(tuple(map(int, i.split("-"))))
else:
fmt.append(i)
self.fmt = ".".join(fmt)
def __repr__(self):
return "OID(%r)" % self.oid
def __iter__(self):
ii = [k[0] for k in self.cmpt]
while True:
yield self.fmt % tuple(ii)
i = 0
while True:
if i >= len(ii):
return
if ii[i] < self.cmpt[i][1]:
ii[i] += 1
break
else:
ii[i] = self.cmpt[i][0]
i += 1
def __iterlen__(self):
return reduce(operator.mul, (max(y - x, 0) + 1 for (x, y) in self.cmpt), 1) # noqa: E501
######################################
# Packet abstract and base classes #
######################################
class Packet_metaclass(type):
def __new__(cls, name, bases, dct):
if "fields_desc" in dct: # perform resolution of references to other packets # noqa: E501
current_fld = dct["fields_desc"]
resolved_fld = []
for f in current_fld:
if isinstance(f, Packet_metaclass): # reference to another fields_desc # noqa: E501
for f2 in f.fields_desc:
resolved_fld.append(f2)
else:
resolved_fld.append(f)
else: # look for a fields_desc in parent classes
resolved_fld = None
for b in bases:
if hasattr(b, "fields_desc"):
resolved_fld = b.fields_desc
break
if resolved_fld: # perform default value replacements
final_fld = []
for f in resolved_fld:
if f.name in dct:
f = f.copy()
f.default = dct[f.name]
del(dct[f.name])
final_fld.append(f)
dct["fields_desc"] = final_fld
dct.setdefault("__slots__", [])
for attr in ["name", "overload_fields"]:
try:
dct["_%s" % attr] = dct.pop(attr)
except KeyError:
pass
newcls = super(Packet_metaclass, cls).__new__(cls, name, bases, dct)
newcls.__all_slots__ = set(
attr
for cls in newcls.__mro__ if hasattr(cls, "__slots__")
for attr in cls.__slots__
)
newcls.aliastypes = [newcls] + getattr(newcls, "aliastypes", [])
if hasattr(newcls, "register_variant"):
newcls.register_variant()
for f in newcls.fields_desc:
if hasattr(f, "register_owner"):
f.register_owner(newcls)
if newcls.__name__[0] != "_":
from scapy import config
config.conf.layers.register(newcls)
return newcls
def __getattr__(self, attr):
for k in self.fields_desc:
if k.name == attr:
return k
raise AttributeError(attr)
def __call__(cls, *args, **kargs):
if "dispatch_hook" in cls.__dict__:
try:
cls = cls.dispatch_hook(*args, **kargs)
except Exception:
from scapy import config
if config.conf.debug_dissector:
raise
cls = config.conf.raw_layer
i = cls.__new__(cls, cls.__name__, cls.__bases__, cls.__dict__)
i.__init__(*args, **kargs)
return i
class Field_metaclass(type):
def __new__(cls, name, bases, dct):
dct.setdefault("__slots__", [])
newcls = super(Field_metaclass, cls).__new__(cls, name, bases, dct)
return newcls
class BasePacket(Gen):
__slots__ = []
#############################
# Packet list base class #
#############################
class BasePacketList(object):
__slots__ = []
class _CanvasDumpExtended(object):
def psdump(self, filename=None, **kargs):
"""
psdump(filename=None, layer_shift=0, rebuild=1)
Creates an EPS file describing a packet. If filename is not provided a
temporary file is created and gs is called.
:param filename: the file's filename
"""
from scapy.config import conf
from scapy.utils import get_temp_file, ContextManagerSubprocess
canvas = self.canvas_dump(**kargs)
if filename is None:
fname = get_temp_file(autoext=kargs.get("suffix", ".eps"))
canvas.writeEPSfile(fname)
if WINDOWS and conf.prog.psreader is None:
os.startfile(fname)
else:
with ContextManagerSubprocess(conf.prog.psreader):
subprocess.Popen([conf.prog.psreader, fname])
else:
canvas.writeEPSfile(filename)
print()
def pdfdump(self, filename=None, **kargs):
"""
pdfdump(filename=None, layer_shift=0, rebuild=1)
Creates a PDF file describing a packet. If filename is not provided a
temporary file is created and xpdf is called.
:param filename: the file's filename
"""
from scapy.config import conf
from scapy.utils import get_temp_file, ContextManagerSubprocess
canvas = self.canvas_dump(**kargs)
if filename is None:
fname = get_temp_file(autoext=kargs.get("suffix", ".pdf"))
canvas.writePDFfile(fname)
if WINDOWS and conf.prog.pdfreader is None:
os.startfile(fname)
else:
with ContextManagerSubprocess(conf.prog.pdfreader):
subprocess.Popen([conf.prog.pdfreader, fname])
else:
canvas.writePDFfile(filename)
print()
def svgdump(self, filename=None, **kargs):
"""
svgdump(filename=None, layer_shift=0, rebuild=1)
Creates an SVG file describing a packet. If filename is not provided a
temporary file is created and gs is called.
:param filename: the file's filename
"""
from scapy.config import conf
from scapy.utils import get_temp_file, ContextManagerSubprocess
canvas = self.canvas_dump(**kargs)
if filename is None:
fname = get_temp_file(autoext=kargs.get("suffix", ".svg"))
canvas.writeSVGfile(fname)
if WINDOWS and conf.prog.svgreader is None:
os.startfile(fname)
else:
with ContextManagerSubprocess(conf.prog.svgreader):
subprocess.Popen([conf.prog.svgreader, fname])
else:
canvas.writeSVGfile(filename)
print()

169
libs/scapy/compat.py Executable file
View file

@ -0,0 +1,169 @@
# This file is part of Scapy
# See http://www.secdev.org/projects/scapy for more information
# Copyright (C) Philippe Biondi <phil@secdev.org>
# Copyright (C) Gabriel Potter <gabriel@potter.fr>
# This program is published under a GPLv2 license
"""
Python 2 and 3 link classes.
"""
from __future__ import absolute_import
import base64
import binascii
import gzip
import struct
import sys
import scapy.modules.six as six
###########
# Python3 #
###########
def lambda_tuple_converter(func):
"""
Converts a Python 2 function as
lambda (x,y): x + y
In the Python 3 format:
lambda x,y : x + y
"""
if func is not None and func.__code__.co_argcount == 1:
return lambda *args: func(args[0] if len(args) == 1 else args)
else:
return func
if six.PY2:
bytes_encode = plain_str = str
chb = lambda x: x if isinstance(x, str) else chr(x)
orb = ord
def raw(x):
"""Builds a packet and returns its bytes representation.
This function is and always be cross-version compatible"""
if hasattr(x, "__bytes__"):
return x.__bytes__()
return bytes(x)
else:
def raw(x):
"""Builds a packet and returns its bytes representation.
This function is and always be cross-version compatible"""
return x.__bytes__()
def bytes_encode(x):
"""Ensure that the given object is bytes.
If the parameter is a packet, raw() should be preferred.
"""
if isinstance(x, str):
return x.encode()
return bytes(x)
if sys.version_info[0:2] <= (3, 4):
def plain_str(x):
"""Convert basic byte objects to str"""
if isinstance(x, bytes):
return x.decode(errors="ignore")
return str(x)
else:
# Python 3.5+
def plain_str(x):
"""Convert basic byte objects to str"""
if isinstance(x, bytes):
return x.decode(errors="backslashreplace")
return str(x)
def chb(x):
"""Same than chr() but encode as bytes."""
return struct.pack("!B", x)
def orb(x):
"""Return ord(x) when not already an int."""
if isinstance(x, int):
return x
return ord(x)
def bytes_hex(x):
"""Hexify a str or a bytes object"""
return binascii.b2a_hex(bytes_encode(x))
def hex_bytes(x):
"""De-hexify a str or a byte object"""
return binascii.a2b_hex(bytes_encode(x))
def base64_bytes(x):
"""Turn base64 into bytes"""
if six.PY2:
return base64.decodestring(x)
return base64.decodebytes(bytes_encode(x))
def bytes_base64(x):
"""Turn bytes into base64"""
if six.PY2:
return base64.encodestring(x).replace('\n', '')
return base64.encodebytes(bytes_encode(x)).replace(b'\n', b'')
if six.PY2:
from StringIO import StringIO
def gzip_decompress(x):
"""Decompress using gzip"""
with gzip.GzipFile(fileobj=StringIO(x), mode='rb') as fdesc:
return fdesc.read()
def gzip_compress(x):
"""Compress using gzip"""
buf = StringIO()
with gzip.GzipFile(fileobj=buf, mode='wb') as fdesc:
fdesc.write(x)
return buf.getvalue()
else:
gzip_decompress = gzip.decompress
gzip_compress = gzip.compress
# Typing compatibility
try:
# Only required if using mypy-lang for static typing
from typing import Optional, List, Union, Callable, Any, AnyStr, Tuple, \
Sized, Dict, Pattern, cast
except ImportError:
# Let's make some fake ones.
def cast(_type, obj):
return obj
class _FakeType(object):
# make the objects subscriptable indefinetly
def __getitem__(self, item):
return _FakeType()
Optional = _FakeType()
Union = _FakeType()
Callable = _FakeType()
List = _FakeType()
Dict = _FakeType()
Any = _FakeType()
AnyStr = _FakeType()
Tuple = _FakeType()
Pattern = _FakeType()
class Sized(object):
pass

707
libs/scapy/config.py Executable file
View file

@ -0,0 +1,707 @@
# This file is part of Scapy
# See http://www.secdev.org/projects/scapy for more information
# Copyright (C) Philippe Biondi <phil@secdev.org>
# This program is published under a GPLv2 license
"""
Implementation of the configuration object.
"""
from __future__ import absolute_import
from __future__ import print_function
import functools
import os
import re
import time
import socket
import sys
import atexit
from scapy import VERSION, base_classes
from scapy.consts import DARWIN, WINDOWS, LINUX, BSD, SOLARIS
from scapy.error import log_scapy, warning, ScapyInvalidPlatformException
from scapy.modules import six
from scapy.themes import NoTheme, apply_ipython_style
############
# Config #
############
class ConfClass(object):
def configure(self, cnf):
self.__dict__ = cnf.__dict__.copy()
def __repr__(self):
return str(self)
def __str__(self):
s = ""
keys = self.__class__.__dict__.copy()
keys.update(self.__dict__)
keys = sorted(keys)
for i in keys:
if i[0] != "_":
r = repr(getattr(self, i))
r = " ".join(r.split())
wlen = 76 - max(len(i), 10)
if len(r) > wlen:
r = r[:wlen - 3] + "..."
s += "%-10s = %s\n" % (i, r)
return s[:-1]
class Interceptor(object):
def __init__(self, name=None, default=None,
hook=None, args=None, kargs=None):
self.name = name
self.intname = "_intercepted_%s" % name
self.default = default
self.hook = hook
self.args = args if args is not None else []
self.kargs = kargs if kargs is not None else {}
def __get__(self, obj, typ=None):
if not hasattr(obj, self.intname):
setattr(obj, self.intname, self.default)
return getattr(obj, self.intname)
@staticmethod
def set_from_hook(obj, name, val):
int_name = "_intercepted_%s" % name
setattr(obj, int_name, val)
def __set__(self, obj, val):
setattr(obj, self.intname, val)
self.hook(self.name, val, *self.args, **self.kargs)
def _readonly(name):
default = Conf.__dict__[name].default
Interceptor.set_from_hook(conf, name, default)
raise ValueError("Read-only value !")
ReadOnlyAttribute = functools.partial(
Interceptor,
hook=(lambda name, *args, **kwargs: _readonly(name))
)
ReadOnlyAttribute.__doc__ = "Read-only class attribute"
class ProgPath(ConfClass):
universal_open = "open" if DARWIN else "xdg-open"
pdfreader = universal_open
psreader = universal_open
svgreader = universal_open
dot = "dot"
display = "display"
tcpdump = "tcpdump"
tcpreplay = "tcpreplay"
hexedit = "hexer"
tshark = "tshark"
wireshark = "wireshark"
ifconfig = "ifconfig"
class ConfigFieldList:
def __init__(self):
self.fields = set()
self.layers = set()
@staticmethod
def _is_field(f):
return hasattr(f, "owners")
def _recalc_layer_list(self):
self.layers = {owner for f in self.fields for owner in f.owners}
def add(self, *flds):
self.fields |= {f for f in flds if self._is_field(f)}
self._recalc_layer_list()
def remove(self, *flds):
self.fields -= set(flds)
self._recalc_layer_list()
def __contains__(self, elt):
if isinstance(elt, base_classes.Packet_metaclass):
return elt in self.layers
return elt in self.fields
def __repr__(self):
return "<%s [%s]>" % (self.__class__.__name__, " ".join(str(x) for x in self.fields)) # noqa: E501
class Emphasize(ConfigFieldList):
pass
class Resolve(ConfigFieldList):
pass
class Num2Layer:
def __init__(self):
self.num2layer = {}
self.layer2num = {}
def register(self, num, layer):
self.register_num2layer(num, layer)
self.register_layer2num(num, layer)
def register_num2layer(self, num, layer):
self.num2layer[num] = layer
def register_layer2num(self, num, layer):
self.layer2num[layer] = num
def __getitem__(self, item):
if isinstance(item, base_classes.Packet_metaclass):
return self.layer2num[item]
return self.num2layer[item]
def __contains__(self, item):
if isinstance(item, base_classes.Packet_metaclass):
return item in self.layer2num
return item in self.num2layer
def get(self, item, default=None):
return self[item] if item in self else default
def __repr__(self):
lst = []
for num, layer in six.iteritems(self.num2layer):
if layer in self.layer2num and self.layer2num[layer] == num:
dir = "<->"
else:
dir = " ->"
lst.append((num, "%#6x %s %-20s (%s)" % (num, dir, layer.__name__,
layer._name)))
for layer, num in six.iteritems(self.layer2num):
if num not in self.num2layer or self.num2layer[num] != layer:
lst.append((num, "%#6x <- %-20s (%s)" % (num, layer.__name__,
layer._name)))
lst.sort()
return "\n".join(y for x, y in lst)
class LayersList(list):
def __init__(self):
list.__init__(self)
self.ldict = {}
def __repr__(self):
return "\n".join("%-20s: %s" % (l.__name__, l.name) for l in self)
def register(self, layer):
self.append(layer)
if layer.__module__ not in self.ldict:
self.ldict[layer.__module__] = []
self.ldict[layer.__module__].append(layer)
def layers(self):
result = []
# This import may feel useless, but it is required for the eval below
import scapy # noqa: F401
for lay in self.ldict:
doc = eval(lay).__doc__
result.append((lay, doc.strip().split("\n")[0] if doc else lay))
return result
class CommandsList(list):
def __repr__(self):
s = []
for l in sorted(self, key=lambda x: x.__name__):
doc = l.__doc__.split("\n")[0] if l.__doc__ else "--"
s.append("%-20s: %s" % (l.__name__, doc))
return "\n".join(s)
def register(self, cmd):
self.append(cmd)
return cmd # return cmd so that method can be used as a decorator
def lsc():
"""Displays Scapy's default commands"""
print(repr(conf.commands))
class CacheInstance(dict, object):
__slots__ = ["timeout", "name", "_timetable", "__dict__"]
def __init__(self, name="noname", timeout=None):
self.timeout = timeout
self.name = name
self._timetable = {}
def flush(self):
self.__init__(name=self.name, timeout=self.timeout)
def __getitem__(self, item):
if item in self.__slots__:
return object.__getattribute__(self, item)
val = dict.__getitem__(self, item)
if self.timeout is not None:
t = self._timetable[item]
if time.time() - t > self.timeout:
raise KeyError(item)
return val
def get(self, item, default=None):
# overloading this method is needed to force the dict to go through
# the timetable check
try:
return self[item]
except KeyError:
return default
def __setitem__(self, item, v):
if item in self.__slots__:
return object.__setattr__(self, item, v)
self._timetable[item] = time.time()
dict.__setitem__(self, item, v)
def update(self, other):
for key, value in six.iteritems(other):
# We only update an element from `other` either if it does
# not exist in `self` or if the entry in `self` is older.
if key not in self or self._timetable[key] < other._timetable[key]:
dict.__setitem__(self, key, value)
self._timetable[key] = other._timetable[key]
def iteritems(self):
if self.timeout is None:
return six.iteritems(self.__dict__)
t0 = time.time()
return ((k, v) for (k, v) in six.iteritems(self.__dict__) if t0 - self._timetable[k] < self.timeout) # noqa: E501
def iterkeys(self):
if self.timeout is None:
return six.iterkeys(self.__dict__)
t0 = time.time()
return (k for k in six.iterkeys(self.__dict__) if t0 - self._timetable[k] < self.timeout) # noqa: E501
def __iter__(self):
return six.iterkeys(self.__dict__)
def itervalues(self):
if self.timeout is None:
return six.itervalues(self.__dict__)
t0 = time.time()
return (v for (k, v) in six.iteritems(self.__dict__) if t0 - self._timetable[k] < self.timeout) # noqa: E501
def items(self):
if self.timeout is None:
return dict.items(self)
t0 = time.time()
return [(k, v) for (k, v) in six.iteritems(self.__dict__) if t0 - self._timetable[k] < self.timeout] # noqa: E501
def keys(self):
if self.timeout is None:
return dict.keys(self)
t0 = time.time()
return [k for k in six.iterkeys(self.__dict__) if t0 - self._timetable[k] < self.timeout] # noqa: E501
def values(self):
if self.timeout is None:
return list(six.itervalues(self))
t0 = time.time()
return [v for (k, v) in six.iteritems(self.__dict__) if t0 - self._timetable[k] < self.timeout] # noqa: E501
def __len__(self):
if self.timeout is None:
return dict.__len__(self)
return len(self.keys())
def summary(self):
return "%s: %i valid items. Timeout=%rs" % (self.name, len(self), self.timeout) # noqa: E501
def __repr__(self):
s = []
if self:
mk = max(len(k) for k in six.iterkeys(self.__dict__))
fmt = "%%-%is %%s" % (mk + 1)
for item in six.iteritems(self.__dict__):
s.append(fmt % item)
return "\n".join(s)
class NetCache:
def __init__(self):
self._caches_list = []
def add_cache(self, cache):
self._caches_list.append(cache)
setattr(self, cache.name, cache)
def new_cache(self, name, timeout=None):
c = CacheInstance(name=name, timeout=timeout)
self.add_cache(c)
def __delattr__(self, attr):
raise AttributeError("Cannot delete attributes")
def update(self, other):
for co in other._caches_list:
if hasattr(self, co.name):
getattr(self, co.name).update(co)
else:
self.add_cache(co.copy())
def flush(self):
for c in self._caches_list:
c.flush()
def __repr__(self):
return "\n".join(c.summary() for c in self._caches_list)
def _version_checker(module, minver):
"""Checks that module has a higher version that minver.
params:
- module: a module to test
- minver: a tuple of versions
"""
# We could use LooseVersion, but distutils imports imp which is deprecated
version_regexp = r'[a-z]?((?:\d|\.)+\d+)(?:\.dev[0-9]+)?'
version_tags = re.match(version_regexp, module.__version__)
if not version_tags:
return False
version_tags = version_tags.group(1).split(".")
version_tags = tuple(int(x) for x in version_tags)
return version_tags >= minver
def isCryptographyValid():
"""
Check if the cryptography module >= 2.0.0 is present. This is the minimum
version for most usages in Scapy.
"""
try:
import cryptography
except ImportError:
return False
return _version_checker(cryptography, (2, 0, 0))
def isCryptographyAdvanced():
"""
Check if the cryptography module is present, and if it supports X25519,
ChaCha20Poly1305 and such.
Notes:
- cryptography >= 2.0 is required
- OpenSSL >= 1.1.0 is required
"""
try:
from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey # noqa: E501
X25519PrivateKey.generate()
except Exception:
return False
else:
return True
def isPyPy():
"""Returns either scapy is running under PyPy or not"""
try:
import __pypy__ # noqa: F401
return True
except ImportError:
return False
def _prompt_changer(attr, val):
"""Change the current prompt theme"""
try:
sys.ps1 = conf.color_theme.prompt(conf.prompt)
except Exception:
pass
try:
apply_ipython_style(get_ipython())
except NameError:
pass
def _set_conf_sockets():
"""Populate the conf.L2Socket and conf.L3Socket
according to the various use_* parameters
"""
from scapy.main import _load
if conf.use_bpf and not BSD:
Interceptor.set_from_hook(conf, "use_bpf", False)
raise ScapyInvalidPlatformException("BSD-like (OSX, *BSD...) only !")
if not conf.use_pcap and SOLARIS:
Interceptor.set_from_hook(conf, "use_pcap", True)
raise ScapyInvalidPlatformException(
"Scapy only supports libpcap on Solaris !"
)
# we are already in an Interceptor hook, use Interceptor.set_from_hook
if conf.use_pcap:
try:
from scapy.arch.pcapdnet import L2pcapListenSocket, L2pcapSocket, \
L3pcapSocket
except (OSError, ImportError):
warning("No libpcap provider available ! pcap won't be used")
Interceptor.set_from_hook(conf, "use_pcap", False)
else:
conf.L3socket = L3pcapSocket
conf.L3socket6 = functools.partial(L3pcapSocket, filter="ip6")
conf.L2socket = L2pcapSocket
conf.L2listen = L2pcapListenSocket
# Update globals
_load("scapy.arch.pcapdnet")
return
if conf.use_bpf:
from scapy.arch.bpf.supersocket import L2bpfListenSocket, \
L2bpfSocket, L3bpfSocket
conf.L3socket = L3bpfSocket
conf.L3socket6 = functools.partial(L3bpfSocket, filter="ip6")
conf.L2socket = L2bpfSocket
conf.L2listen = L2bpfListenSocket
# Update globals
_load("scapy.arch.bpf")
return
if LINUX:
from scapy.arch.linux import L3PacketSocket, L2Socket, L2ListenSocket
conf.L3socket = L3PacketSocket
conf.L3socket6 = functools.partial(L3PacketSocket, filter="ip6")
conf.L2socket = L2Socket
conf.L2listen = L2ListenSocket
# Update globals
_load("scapy.arch.linux")
return
if WINDOWS:
from scapy.arch.windows import _NotAvailableSocket
from scapy.arch.windows.native import L3WinSocket, L3WinSocket6
conf.L3socket = L3WinSocket
conf.L3socket6 = L3WinSocket6
conf.L2socket = _NotAvailableSocket
conf.L2listen = _NotAvailableSocket
# No need to update globals on Windows
return
from scapy.supersocket import L3RawSocket
from scapy.layers.inet6 import L3RawSocket6
conf.L3socket = L3RawSocket
conf.L3socket6 = L3RawSocket6
def _socket_changer(attr, val):
if not isinstance(val, bool):
raise TypeError("This argument should be a boolean")
dependencies = { # Things that will be turned off
"use_pcap": ["use_bpf"],
"use_bpf": ["use_pcap"],
}
restore = {k: getattr(conf, k) for k in dependencies}
del restore[attr] # This is handled directly by _set_conf_sockets
if val: # Only if True
for param in dependencies[attr]:
Interceptor.set_from_hook(conf, param, False)
try:
_set_conf_sockets()
except (ScapyInvalidPlatformException, ImportError) as e:
for key, value in restore.items():
Interceptor.set_from_hook(conf, key, value)
if isinstance(e, ScapyInvalidPlatformException):
raise
def _loglevel_changer(attr, val):
"""Handle a change of conf.logLevel"""
log_scapy.setLevel(val)
class Conf(ConfClass):
"""
This object contains the configuration of Scapy.
"""
version = ReadOnlyAttribute("version", VERSION)
session = "" #: filename where the session will be saved
interactive = False
#: can be "ipython", "python" or "auto". Default: Auto
interactive_shell = ""
#: if 1, prevents any unwanted packet to go out (ARP, DNS, ...)
stealth = "not implemented"
#: selects the default output interface for srp() and sendp().
iface = None
layers = LayersList()
commands = CommandsList()
ASN1_default_codec = None #: Codec used by default for ASN1 objects
AS_resolver = None #: choose the AS resolver class to use
dot15d4_protocol = None # Used in dot15d4.py
logLevel = Interceptor("logLevel", log_scapy.level, _loglevel_changer)
#: if 0, doesn't check that IPID matches between IP sent and
#: ICMP IP citation received
#: if 1, checks that they either are equal or byte swapped
#: equals (bug in some IP stacks)
#: if 2, strictly checks that they are equals
checkIPID = False
#: if 1, checks IP src in IP and ICMP IP citation match
#: (bug in some NAT stacks)
checkIPsrc = True
checkIPaddr = True
#: if True, checks that IP-in-IP layers match. If False, do
#: not check IP layers that encapsulates another IP layer
checkIPinIP = True
#: if 1, also check that TCP seq and ack match the
#: ones in ICMP citation
check_TCPerror_seqack = False
verb = 2 #: level of verbosity, from 0 (almost mute) to 3 (verbose)
prompt = Interceptor("prompt", ">>> ", _prompt_changer)
#: default mode for listening socket (to get answers if you
#: spoof on a lan)
promisc = True
sniff_promisc = 1 #: default mode for sniff()
raw_layer = None
raw_summary = False
default_l2 = None
l2types = Num2Layer()
l3types = Num2Layer()
L3socket = None
L3socket6 = None
L2socket = None
L2listen = None
BTsocket = None
USBsocket = None
min_pkt_size = 60
mib = None #: holds MIB direct access dictionary
bufsize = 2**16
#: history file
histfile = os.getenv('SCAPY_HISTFILE',
os.path.join(os.path.expanduser("~"),
".scapy_history"))
#: includes padding in disassembled packets
padding = 1
#: BPF filter for packets to ignore
except_filter = ""
#: bpf filter added to every sniffing socket to exclude traffic
#: from analysis
filter = ""
#: when 1, store received packet that are not matched into `debug.recv`
debug_match = False
#: When 1, print some TLS session secrets when they are computed.
debug_tls = False
wepkey = ""
cache_iflist = {}
#: holds the Scapy IPv4 routing table and provides methods to
#: manipulate it
route = None # Filed by route.py
#: holds the Scapy IPv6 routing table and provides methods to
#: manipulate it
route6 = None # Filed by route6.py
auto_fragment = True
#: raise exception when a packet dissector raises an exception
debug_dissector = False
color_theme = Interceptor("color_theme", NoTheme(), _prompt_changer)
#: how much time between warnings from the same place
warning_threshold = 5
prog = ProgPath()
#: holds list of fields for which resolution should be done
resolve = Resolve()
#: holds list of enum fields for which conversion to string
#: should NOT be done
noenum = Resolve()
emph = Emphasize()
#: read only attribute to show if PyPy is in use
use_pypy = ReadOnlyAttribute("use_pypy", isPyPy())
#: use libpcap integration or not. Changing this value will update
#: the conf.L[2/3] sockets
use_pcap = Interceptor(
"use_pcap",
os.getenv("SCAPY_USE_PCAPDNET", "").lower().startswith("y"),
_socket_changer
)
use_bpf = Interceptor("use_bpf", False, _socket_changer)
use_npcap = False
ipv6_enabled = socket.has_ipv6
#: path or list of paths where extensions are to be looked for
extensions_paths = "."
stats_classic_protocols = []
stats_dot11_protocols = []
temp_files = []
netcache = NetCache()
geoip_city = None
# can, tls, http are not loaded by default
load_layers = ['bluetooth', 'bluetooth4LE', 'dhcp', 'dhcp6', 'dns',
'dot11', 'dot15d4', 'eap', 'gprs', 'hsrp', 'inet',
'inet6', 'ipsec', 'ir', 'isakmp', 'l2', 'l2tp',
'llmnr', 'lltd', 'mgcp', 'mobileip', 'netbios',
'netflow', 'ntp', 'ppi', 'ppp', 'pptp', 'radius', 'rip',
'rtp', 'sctp', 'sixlowpan', 'skinny', 'smb', 'smb2', 'snmp',
'tftp', 'vrrp', 'vxlan', 'x509', 'zigbee']
#: a dict which can be used by contrib layers to store local
#: configuration
contribs = dict()
crypto_valid = isCryptographyValid()
crypto_valid_advanced = isCryptographyAdvanced()
fancy_prompt = True
auto_crop_tables = True
#: how often to check for new packets.
#: Defaults to 0.05s.
recv_poll_rate = 0.05
#: When True, raise exception if no dst MAC found otherwise broadcast.
#: Default is False.
raise_no_dst_mac = False
loopback_name = "lo" if LINUX else "lo0"
def __getattr__(self, attr):
# Those are loaded on runtime to avoid import loops
if attr == "manufdb":
from scapy.data import MANUFDB
return MANUFDB
if attr == "ethertypes":
from scapy.data import ETHER_TYPES
return ETHER_TYPES
if attr == "protocols":
from scapy.data import IP_PROTOS
return IP_PROTOS
if attr == "services_udp":
from scapy.data import UDP_SERVICES
return UDP_SERVICES
if attr == "services_tcp":
from scapy.data import TCP_SERVICES
return TCP_SERVICES
if attr == "iface6":
warning("conf.iface6 is deprecated in favor of conf.iface")
attr = "iface"
return object.__getattribute__(self, attr)
if not Conf.ipv6_enabled:
log_scapy.warning("IPv6 support disabled in Python. Cannot load Scapy IPv6 layers.") # noqa: E501
for m in ["inet6", "dhcp6"]:
if m in Conf.load_layers:
Conf.load_layers.remove(m)
conf = Conf()
def crypto_validator(func):
"""
This a decorator to be used for any method relying on the cryptography library. # noqa: E501
Its behaviour depends on the 'crypto_valid' attribute of the global 'conf'.
"""
def func_in(*args, **kwargs):
if not conf.crypto_valid:
raise ImportError("Cannot execute crypto-related method! "
"Please install python-cryptography v1.7 or later.") # noqa: E501
return func(*args, **kwargs)
return func_in
def scapy_delete_temp_files():
# type: () -> None
for f in conf.temp_files:
try:
os.unlink(f)
except Exception:
pass
del conf.temp_files[:]
atexit.register(scapy_delete_temp_files)

20
libs/scapy/consts.py Executable file
View file

@ -0,0 +1,20 @@
# This file is part of Scapy
# See http://www.secdev.org/projects/scapy for more information
# Copyright (C) Philippe Biondi <phil@secdev.org>
# This program is published under a GPLv2 license
from sys import platform, maxsize
import platform as platform_lib
LINUX = platform.startswith("linux")
OPENBSD = platform.startswith("openbsd")
FREEBSD = "freebsd" in platform
NETBSD = platform.startswith("netbsd")
DARWIN = platform.startswith("darwin")
SOLARIS = platform.startswith("sunos")
WINDOWS = platform.startswith("win32")
WINDOWS_XP = platform_lib.release() == "XP"
BSD = DARWIN or FREEBSD or OPENBSD or NETBSD
# See https://docs.python.org/3/library/platform.html#cross-platform
IS_64BITS = maxsize > 2**32
# LOOPBACK_NAME moved to conf.loopback_name

8
libs/scapy/contrib/__init__.py Executable file
View file

@ -0,0 +1,8 @@
# This file is part of Scapy
# See http://www.secdev.org/projects/scapy for more information
# Copyright (C) Philippe Biondi <phil@secdev.org>
# This program is published under a GPLv2 license
"""
Package of contrib modules that have to be loaded explicitly.
"""

82
libs/scapy/contrib/altbeacon.py Executable file
View file

@ -0,0 +1,82 @@
# -*- mode: python3; indent-tabs-mode: nil; tab-width: 4 -*-
# altbeacon.py - protocol handlers for AltBeacon
#
# This file is part of Scapy
# See http://www.secdev.org/projects/scapy for more information
# Copyright (C) Michael Farrell <micolous+git@gmail.com>
# This program is published under a GPLv2 (or later) license
#
# scapy.contrib.description = AltBeacon BLE proximity beacon
# scapy.contrib.status = loads
"""
scapy.contrib.altbeacon - AltBeacon Bluetooth LE proximity beacons.
The AltBeacon specification can be found at: https://github.com/AltBeacon/spec
"""
from scapy.fields import ByteField, ShortField, SignedByteField, \
StrFixedLenField
from scapy.layers.bluetooth import EIR_Hdr, EIR_Manufacturer_Specific_Data, \
UUIDField, LowEnergyBeaconHelper
from scapy.packet import Packet
# When building beacon frames, one should use their own manufacturer ID.
#
# However, most software (including the AltBeacon SDK) requires explicitly
# registering particular manufacturer IDs to listen to, and the only ID used is
# that of Radius Networks (the developer of the specification).
#
# To maximise compatibility, Scapy's implementation of
# LowEnergyBeaconHelper.build_eir (for constructing frames) uses Radius
# Networks' manufacturer ID.
#
# Scapy's implementation of AltBeacon **does not** require a specific
# manufacturer ID to detect AltBeacons - it uses
# EIR_Manufacturer_Specific_Data.register_magic_payload.
RADIUS_NETWORKS_MFG = 0x0118
class AltBeacon(Packet, LowEnergyBeaconHelper):
"""
AltBeacon broadcast frame type.
https://github.com/AltBeacon/spec
"""
name = "AltBeacon"
magic = b"\xBE\xAC"
fields_desc = [
StrFixedLenField("header", magic, len(magic)),
# The spec says this is 20 bytes, with >=16 bytes being an
# organisational unit-specific identifier. However, the Android library
# treats this as UUID + uint16 + uint16.
UUIDField("id1", None),
# Local identifier
ShortField("id2", None),
ShortField("id3", None),
SignedByteField("tx_power", None),
ByteField("mfg_reserved", None),
]
@classmethod
def magic_check(cls, payload):
"""
Checks if the given payload is for us (starts with our magic string).
"""
return payload.startswith(cls.magic)
def build_eir(self):
"""Builds a list of EIR messages to wrap this frame."""
# Note: Company ID is not required by spec, but most tools only look
# for manufacturer-specific data with Radius Networks' manufacturer ID.
return LowEnergyBeaconHelper.base_eir + [
EIR_Hdr() / EIR_Manufacturer_Specific_Data(
company_id=RADIUS_NETWORKS_MFG) / self
]
EIR_Manufacturer_Specific_Data.register_magic_payload(AltBeacon)

View file

@ -0,0 +1,94 @@
% AltBeacon unit tests
#
# Type the following command to launch start the tests:
# $ test/run_tests -P "load_contrib('altbeacon')" -t scapy/contrib/altbeacon.uts
#
# AltBeaconParser tests adapted from:
# https://github.com/AltBeacon/android-beacon-library/blob/master/lib/src/test/java/org/altbeacon/beacon/AltBeaconParserTest.java
+ AltBeacon tests
= Setup
def next_eir(p):
return EIR_Hdr(p[Padding])
= Presence check
AltBeacon
= AltBeaconParserTest.testRecognizeBeacon
d = hex_bytes('02011a1bff1801beac2f234454cf6d4a0fadf2f4911ba9ffa600010002c509')
p = EIR_Hdr(d)
# First is a flags header
assert EIR_Flags in p
# Then the AltBeacon
p = next_eir(p)
assert p[EIR_Manufacturer_Specific_Data].company_id == RADIUS_NETWORKS_MFG
assert p[AltBeacon].mfg_reserved == 9
= AltBeaconParserTest.testDetectsDaveMHardwareBeacon
d = hex_bytes('02011a1bff1801beac2f234454cf6d4a0fadf2f4911ba9ffa600050003be020e09526164426561636f6e20555342020a03000000000000000000000000')
p = EIR_Hdr(d)
# First is Flags
assert EIR_Flags in p
# Then the AltBeacon
p = next_eir(p)
assert p[EIR_Manufacturer_Specific_Data].company_id == RADIUS_NETWORKS_MFG
assert AltBeacon in p
# Then CompleteLocalName
p = next_eir(p)
assert p[EIR_CompleteLocalName].local_name == b'RadBeacon USB'
# Then TX_Power_Level
p = next_eir(p)
assert p[EIR_TX_Power_Level].level == 3
= AltBeaconParserTest.testParseWrongFormatReturnsNothing
d = hex_bytes('02011a1aff1801ffff2f234454cf6d4a0fadf2f4911ba9ffa600010002c509')
p = EIR_Hdr(d)
# First is Flags
assert EIR_Flags in p
# Then the EIR_Manufacturer_Specific_Data
p = next_eir(p)
assert p[EIR_Manufacturer_Specific_Data].company_id == RADIUS_NETWORKS_MFG
assert AltBeacon not in p
= AltBeaconParserTest.testParsesBeaconMissingDataField
d = hex_bytes('02011a1aff1801beac2f234454cf6d4a0fadf2f4911ba9ffa600010002c50000')
p = EIR_Hdr(d)
# First is Flags
assert EIR_Flags in p
# Then the EIR_Manufacturer_Specific_Data
p = next_eir(p)
assert p[EIR_Manufacturer_Specific_Data].company_id == RADIUS_NETWORKS_MFG
assert p[AltBeacon].id1 == uuid.UUID('2f234454-cf6d-4a0f-adf2-f4911ba9ffa6')
assert p[AltBeacon].id2 == 1
assert p[AltBeacon].id3 == 2
assert p[AltBeacon].tx_power == -59
= Build EIR
p = AltBeacon(
id1=uuid.UUID('2f234454-cf6d-4a0f-adf2-f4911ba9ffa6'),
id2=1,
id3=2,
tx_power=-59,
)
d = raw(p.build_eir()[-1])
assert d == hex_bytes('1bff1801beac2f234454cf6d4a0fadf2f4911ba9ffa600010002c500')

137
libs/scapy/contrib/aoe.py Executable file
View file

@ -0,0 +1,137 @@
# Copyright (C) 2018 antoine.torre <torreantoine1@gmail.com>
##
# This program is published under a GPLv2 license
# scapy.contrib.description = ATA Over Internet
# scapy.contrib.status = loads
from scapy.packet import Packet, bind_layers
from scapy.fields import FlagsField, XByteField, ByteField, XShortField, \
ShortField, StrLenField, BitField, BitEnumField, ByteEnumField, \
FieldLenField, PacketListField, FieldListField, MACField, PacketField, \
ConditionalField, XIntField
from scapy.layers.l2 import Ether
from scapy.data import ETHER_ANY
class IssueATACommand(Packet):
name = "Issue ATA Command"
fields_desc = [FlagsField("flags", 0, 8, "zezdzzaw"),
XByteField("err_feature", 0),
ByteField("sector_count", 1),
XByteField("cmd_status", 0xec),
XByteField("lba0", 0),
XByteField("lba1", 0),
XByteField("lba2", 0),
XByteField("lba3", 0),
XByteField("lba4", 0),
XByteField("lba5", 0),
XShortField("reserved", 0),
StrLenField("data", "",
length_from=lambda x: x.sector_count * 512)]
def extract_padding(self, s):
return "", s
class QueryConfigInformation(Packet):
name = "Query Config Information"
fields_desc = [ShortField("buffer_count", 0),
ShortField("firmware", 0),
ByteField("sector_count", 0),
BitField("aoe", 0, 4),
BitEnumField("ccmd", 0, 4, {0: "Read config string",
1: "Test config string",
2: "Test config string prefix",
3: "Set config string",
4: "Force set config string"}),
FieldLenField("config_length", None, length_of="config"),
StrLenField("config", None,
length_from=lambda x: x.config_length)]
def extract_padding(self, s):
return "", s
class Directive(Packet):
name = "Directive"
fields_desc = [ByteField("reserved", 0),
ByteEnumField("dcmd", 0,
{0: "No directive",
1: "Add mac address to mask list",
2: "Delete mac address from mask list"}),
MACField("mac_addr", ETHER_ANY)]
class MacMaskList(Packet):
name = "Mac Mask List"
fields_desc = [ByteField("reserved", 0),
ByteEnumField("mcmd", 0, {0: "Read Mac Mask List",
1: "Edit Mac Mask List"}),
ByteEnumField("merror", 0, {0: "",
1: "Unspecified error",
2: "Bad dcmd directive",
3: "Mask List Full"}),
FieldLenField("dir_count", None, count_of="directives"),
PacketListField("directives", None, Directive,
count_from=lambda pkt: pkt.dir_count)]
def extract_padding(self, s):
return "", s
class ReserveRelease(Packet):
name = "Reserve / Release"
fields_desc = [ByteEnumField("rcmd", 0, {0: "Read Reserve List",
1: "Set Reserve List",
2: "Force Set Reserve List"}),
FieldLenField("nb_mac", None, count_of="mac_addrs"),
FieldListField("mac_addrs", None, MACField("", ETHER_ANY),
count_from=lambda pkt: pkt.nb_mac)]
def extract_padding(self, s):
return "", s
class AOE(Packet):
name = "ATA over Ethernet"
fields_desc = [BitField("version", 1, 4),
FlagsField("flags", 0, 4, ["Response", "Error",
"r1", "r2"]),
ByteEnumField("error", 0, {1: "Unrecognized command code",
2: "Bad argument parameter",
3: "Device unavailable",
4: "Config string present",
5: "Unsupported exception",
6: "Target is reserved"}),
XShortField("major", 0xFFFF),
XByteField("minor", 0xFF),
ByteEnumField("cmd", 1, {0: "Issue ATA Command",
1: "Query Config Information",
2: "Mac Mask List",
3: "Reserve / Release"}),
XIntField("tag", 0),
ConditionalField(PacketField("i_ata_cmd", IssueATACommand(),
IssueATACommand),
lambda x: x.cmd == 0),
ConditionalField(PacketField("q_conf_info",
QueryConfigInformation(),
QueryConfigInformation),
lambda x: x.cmd == 1),
ConditionalField(PacketField("mac_m_list", MacMaskList(),
MacMaskList),
lambda x: x.cmd == 2),
ConditionalField(PacketField("res_rel", ReserveRelease(),
ReserveRelease),
lambda x: x.cmd == 3)]
def extract_padding(self, s):
return "", s
bind_layers(Ether, AOE, type=0x88A2)
bind_layers(AOE, IssueATACommand, cmd=0)
bind_layers(AOE, QueryConfigInformation, cmd=1)
bind_layers(AOE, MacMaskList, cmd=2)
bind_layers(AOE, ReserveRelease, cmd=3)

44
libs/scapy/contrib/aoe.uts Executable file
View file

@ -0,0 +1,44 @@
% Regression tests for aoe module
############
############
+ Basic tests
= Build - Check Ethertype
a = Ether(src="00:01:02:03:04:05")
b = AOE()
c = a / b
assert(c[Ether].type == 0x88a2)
= Build - Check default
p = AOE()
assert(hasattr(p, "q_conf_info"))
= Build - Check Issue ATA command
p = AOE()
p.cmd = 0
assert(hasattr(p, "i_ata_cmd"))
= Build - Check Query Config Information
p = AOE()
p.cmd = 1
assert(hasattr(p, "q_conf_info"))
= Build - Check Mac Mask List
p = AOE()
p.cmd = 2
assert(hasattr(p, "mac_m_list"))
= Build - Check ReserveRelease
p = AOE()
p.cmd = 3
assert(hasattr(p, "res_rel"))

View file

@ -0,0 +1,10 @@
# This file is part of Scapy
# See http://www.secdev.org/projects/scapy for more information
# Copyright (C) Nils Weiss <nils@we155.de>
# This program is published under a GPLv2 license
# scapy.contrib.status = skip
"""
Package of contrib automotive modules that have to be loaded explicitly.
"""

View file

@ -0,0 +1,11 @@
# This file is part of Scapy
# See http://www.secdev.org/projects/scapy for more information
# Copyright (C) Nils Weiss <nils@we155.de>
# This program is published under a GPLv2 license
# scapy.contrib.status = skip
"""
Package of contrib automotive bmw specific modules
that have to be loaded explicitly.
"""

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,95 @@
# This file is part of Scapy
# See http://www.secdev.org/projects/scapy for more information
# Copyright (C) Nils Weiss <nils@we155.de>
# This program is published under a GPLv2 license
# scapy.contrib.description = ENET - BMW diagnostic protocol over Ethernet
# scapy.contrib.status = loads
import struct
import socket
from scapy.packet import Packet, bind_layers, bind_bottom_up
from scapy.fields import IntField, ShortEnumField, XByteField
from scapy.layers.inet import TCP
from scapy.supersocket import StreamSocket
from scapy.contrib.automotive.uds import UDS
from scapy.contrib.isotp import ISOTP
from scapy.error import Scapy_Exception
from scapy.data import MTU
"""
BMW specific diagnostic over IP protocol implementation ENET
"""
# #########################ENET###################################
class ENET(Packet):
name = 'ENET'
fields_desc = [
IntField('length', None),
ShortEnumField('type', 1, {0x01: "message",
0x02: "echo"}),
XByteField('src', 0),
XByteField('dst', 0),
]
def hashret(self):
hdr_hash = struct.pack("B", self.src ^ self.dst)
pay_hash = self.payload.hashret()
return hdr_hash + pay_hash
def answers(self, other):
if other.__class__ == self.__class__:
return self.payload.answers(other.payload)
return 0
def extract_padding(self, s):
return s[:self.length - 2], s[self.length - 2:]
def post_build(self, pkt, pay):
"""
This will set the LenField 'length' to the correct value.
"""
if self.length is None:
pkt = struct.pack("!I", len(pay) + 2) + pkt[4:]
return pkt + pay
bind_bottom_up(TCP, ENET, sport=6801)
bind_bottom_up(TCP, ENET, dport=6801)
bind_layers(TCP, ENET, sport=6801, dport=6801)
bind_layers(ENET, UDS)
# ########################ENETSocket###################################
class ENETSocket(StreamSocket):
def __init__(self, ip='127.0.0.1', port=6801):
self.ip = ip
self.port = port
s = socket.socket()
s.connect((self.ip, self.port))
StreamSocket.__init__(self, s, ENET)
class ISOTP_ENETSocket(ENETSocket):
def __init__(self, src, dst, ip='127.0.0.1', port=6801, basecls=ISOTP):
super(ISOTP_ENETSocket, self).__init__(ip, port)
self.src = src
self.dst = dst
self.basecls = ENET
self.outputcls = basecls
def send(self, x):
if not isinstance(x, ISOTP):
raise Scapy_Exception("Please provide a packet class based on "
"ISOTP")
super(ISOTP_ENETSocket, self).send(
ENET(src=self.src, dst=self.dst) / x)
def recv(self, x=MTU):
pkt = super(ISOTP_ENETSocket, self).recv(x)
return self.outputcls(bytes(pkt[1]))

View file

@ -0,0 +1,69 @@
+ ENET Contrib tests
= Load Contrib Layer
load_contrib("automotive.bmw.enet")
= Basic Test 1
pkt = ENET(type=1, src=0xf4, dst=0x10)/Raw(b'\x11\x22\x33')
assert bytes(pkt) == b'\x00\x00\x00\x05\x00\x01\xf4\x10\x11"3'
= Basic Test 2
pkt = ENET(type=1, src=0xf4, dst=0x10)/Raw(b'\x11\x22\x33\x11\x11\x11\x11\x11')
assert bytes(pkt) == b'\x00\x00\x00\x0a\x00\x01\xf4\x10\x11"3\x11\x11\x11\x11\x11'
= Basic Dissect Test
pkt = ENET(b'\x00\x00\x00\x0a\x00\x01\xf4\x10\x11"3\x11\x11\x11\x11\x11')
assert pkt.length == 10
assert pkt.src == 0xf4
assert pkt.dst == 0x10
assert pkt.type == 1
assert pkt[1].service == 17
assert pkt[2].resetType == 34
= Build Test
pkt = ENET(src=0xf4, dst=0x10)/Raw(b"0" * 20)
assert bytes(pkt) == b'\x00\x00\x00\x16\x00\x01\xf4\x10' + b"0" * 20
= Dissect Test
pkt = ENET(b'\x00\x00\x00\x18\x00\x01\xf4\x10\x67\x01' + b"0" * 20)
assert pkt.length == 24
assert pkt.src == 0xf4
assert pkt.dst == 0x10
assert pkt.type == 1
assert pkt.securitySeed == b"0" * 20
assert len(pkt[1]) == pkt.length - 2
= Dissect Test with padding
pkt = ENET(b'\x00\x00\x00\x18\x00\x01\xf4\x10\x67\x01' + b"0" * 20 + b"p" * 100)
assert pkt.length == 24
assert pkt.src == 0xf4
assert pkt.dst == 0x10
assert pkt.type == 1
assert pkt.securitySeed == b"0" * 20
assert pkt.load == b'p' * 100
= Dissect Test to short packet
pkt = ENET(b'\x00\x00\x00\x18\x00\x01\xf4\x10\x67\x01' + b"0" * 19)
assert pkt.length == 24
assert pkt.src == 0xf4
assert pkt.dst == 0x10
assert pkt.type == 1
assert pkt.securitySeed == b"0" * 19
= Dissect Test very long packet
pkt = ENET(b'\x00\x0f\xff\x04\x00\x01\xf4\x10\x67\x01' + b"0" * 0xfff00)
assert pkt.length == 0xfff04
assert pkt.src == 0xf4
assert pkt.dst == 0x10
assert pkt.type == 1
assert pkt.securitySeed == b"0" * 0xfff00

View file

@ -0,0 +1,588 @@
# This file is part of Scapy
# See http://www.secdev.org/projects/scapy for more information
# Copyright (C) Nils Weiss <nils@we155.de>
# This program is published under a GPLv2 license
# scapy.contrib.description = CAN Calibration Protocol (CCP)
# scapy.contrib.status = loads
import struct
from scapy.packet import Packet, bind_layers, bind_bottom_up
from scapy.fields import XIntField, FlagsField, ByteEnumField, \
ThreeBytesField, XBitField, ShortField, IntField, XShortField, \
ByteField, XByteField, StrFixedLenField, LEShortField
from scapy.layers.can import CAN
class CCP(CAN):
name = 'CAN Calibration Protocol'
fields_desc = [
FlagsField('flags', 0, 3, ['error',
'remote_transmission_request',
'extended']),
XBitField('identifier', 0, 29),
ByteField('length', 8),
ThreeBytesField('reserved', 0),
]
def extract_padding(self, p):
return p, None
class CRO(Packet):
commands = {
0x01: "CONNECT",
0x1B: "GET_CCP_VERSION",
0x17: "EXCHANGE_ID",
0x12: "GET_SEED",
0x13: "UNLOCK",
0x02: "SET_MTA",
0x03: "DNLOAD",
0x23: "DNLOAD_6",
0x04: "UPLOAD",
0x0F: "SHORT_UP",
0x11: "SELECT_CAL_PAGE",
0x14: "GET_DAQ_SIZE",
0x15: "SET_DAQ_PTR",
0x16: "WRITE_DAQ",
0x06: "START_STOP",
0x07: "DISCONNECT",
0x0C: "SET_S_STATUS",
0x0D: "GET_S_STATUS",
0x0E: "BUILD_CHKSUM",
0x10: "CLEAR_MEMORY",
0x18: "PROGRAM",
0x22: "PROGRAM_6",
0x19: "MOVE",
0x05: "TEST",
0x09: "GET_ACTIVE_CAL_PAGE",
0x08: "START_STOP_ALL",
0x20: "DIAG_SERVICE",
0x21: "ACTION_SERVICE"
}
name = 'Command Receive Object'
fields_desc = [
ByteEnumField('cmd', 0x01, commands),
ByteField('ctr', 0)
]
def hashret(self):
return struct.pack('B', self.ctr)
# ##### CROs ######
class CONNECT(Packet):
fields_desc = [
LEShortField('station_address', 0),
StrFixedLenField('ccp_reserved', b'\xff' * 4, length=4),
]
bind_layers(CRO, CONNECT, cmd=0x01)
class GET_CCP_VERSION(Packet):
fields_desc = [
XByteField('main_protocol_version', 0),
XByteField('release_version', 0),
StrFixedLenField('ccp_reserved', b'\xff' * 4, length=4)
]
bind_layers(CRO, GET_CCP_VERSION, cmd=0x1B)
class EXCHANGE_ID(Packet):
fields_desc = [
StrFixedLenField('ccp_master_device_id', b'\x00' * 6, length=6)
]
bind_layers(CRO, EXCHANGE_ID, cmd=0x17)
class GET_SEED(Packet):
fields_desc = [
XByteField('resource', 0),
StrFixedLenField('ccp_reserved', b'\xff' * 5, length=5)
]
bind_layers(CRO, GET_SEED, cmd=0x12)
class UNLOCK(Packet):
fields_desc = [
StrFixedLenField('key', b'\x00' * 6, length=6)
]
bind_layers(CRO, UNLOCK, cmd=0x13)
class SET_MTA(Packet):
fields_desc = [
XByteField('mta_num', 0),
XByteField('address_extension', 0),
XIntField('address', 0),
]
bind_layers(CRO, SET_MTA, cmd=0x02)
class DNLOAD(Packet):
fields_desc = [
XByteField('size', 0),
StrFixedLenField('data', b'\x00' * 5, length=5)
]
bind_layers(CRO, DNLOAD, cmd=0x03)
class DNLOAD_6(Packet):
fields_desc = [
StrFixedLenField('data', b'\x00' * 6, length=6)
]
bind_layers(CRO, DNLOAD_6, cmd=0x23)
class UPLOAD(Packet):
fields_desc = [
XByteField('size', 0),
StrFixedLenField('ccp_reserved', b'\xff' * 5, length=5)
]
bind_layers(CRO, UPLOAD, cmd=0x04)
class SHORT_UP(Packet):
fields_desc = [
XByteField('size', 0),
XByteField('address_extension', 0),
XIntField('address', 0),
]
bind_layers(CRO, SHORT_UP, cmd=0x0F)
class SELECT_CAL_PAGE(Packet):
fields_desc = [
StrFixedLenField('ccp_reserved', b'\xff' * 6, length=6)
]
bind_layers(CRO, SELECT_CAL_PAGE, cmd=0x11)
class GET_DAQ_SIZE(Packet):
fields_desc = [
XByteField('DAQ_num', 0),
XByteField('ccp_reserved', 0),
XIntField('DTO_identifier', 0),
]
bind_layers(CRO, GET_DAQ_SIZE, cmd=0x14)
class SET_DAQ_PTR(Packet):
fields_desc = [
XByteField('DAQ_num', 0),
XByteField('ODT_num', 0),
XByteField('ODT_element', 0),
StrFixedLenField('ccp_reserved', b'\xff' * 3, length=3)
]
bind_layers(CRO, SET_DAQ_PTR, cmd=0x15)
class WRITE_DAQ(Packet):
fields_desc = [
XByteField('DAQ_size', 0),
XByteField('address_extension', 0),
XIntField('address', 0),
]
bind_layers(CRO, WRITE_DAQ, cmd=0x16)
class START_STOP(Packet):
fields_desc = [
XByteField('mode', 0),
XByteField('DAQ_num', 0),
XByteField('ODT_num', 0),
XByteField('event_channel', 0),
XShortField('transmission_rate', 0),
]
bind_layers(CRO, START_STOP, cmd=0x06)
class DISCONNECT(Packet):
fields_desc = [
ByteEnumField('type', 0, {0: "temporary", 1: "end_of_session"}),
StrFixedLenField('ccp_reserved0', b'\xff' * 1, length=1),
LEShortField('station_address', 0),
StrFixedLenField('ccp_reserved', b'\xff' * 2, length=2)
]
bind_layers(CRO, DISCONNECT, cmd=0x07)
class SET_S_STATUS(Packet):
name = "Set Session Status"
fields_desc = [
FlagsField("session_status", 0, 8, ["CAL", "DAQ", "RESUME", "RES0",
"RES1", "RES2", "STORE", "RUN"]),
StrFixedLenField('ccp_reserved', b'\xff' * 5, length=5)
]
bind_layers(CRO, SET_S_STATUS, cmd=0x0C)
class GET_S_STATUS(Packet):
fields_desc = [
StrFixedLenField('ccp_reserved', b'\xff' * 6, length=6)
]
bind_layers(CRO, GET_S_STATUS, cmd=0x0D)
class BUILD_CHKSUM(Packet):
fields_desc = [
IntField('size', 0),
StrFixedLenField('ccp_reserved', b'\xff' * 2, length=2)
]
bind_layers(CRO, BUILD_CHKSUM, cmd=0x0E)
class CLEAR_MEMORY(Packet):
fields_desc = [
IntField('size', 0),
StrFixedLenField('ccp_reserved', b'\xff' * 2, length=2)
]
bind_layers(CRO, CLEAR_MEMORY, cmd=0x10)
class PROGRAM(Packet):
fields_desc = [
XByteField('size', 0),
StrFixedLenField('data', b'\x00' * 0,
length_from=lambda pkt: pkt.size),
StrFixedLenField('ccp_reserved', b'\xff' * 5,
length_from=lambda pkt: 5 - pkt.size)
]
bind_layers(CRO, PROGRAM, cmd=0x18)
class PROGRAM_6(Packet):
fields_desc = [
StrFixedLenField('data', b'\x00' * 6, length=6)
]
bind_layers(CRO, PROGRAM_6, cmd=0x22)
class MOVE(Packet):
fields_desc = [
IntField('size', 0),
StrFixedLenField('ccp_reserved', b'\xff' * 2, length=2)
]
bind_layers(CRO, MOVE, cmd=0x19)
class TEST(Packet):
fields_desc = [
LEShortField('station_address', 0),
StrFixedLenField('ccp_reserved', b'\xff' * 4, length=4)
]
bind_layers(CRO, TEST, cmd=0x05)
class GET_ACTIVE_CAL_PAGE(Packet):
fields_desc = [
StrFixedLenField('ccp_reserved', b'\xff' * 6, length=6)
]
bind_layers(CRO, GET_ACTIVE_CAL_PAGE, cmd=0x09)
class START_STOP_ALL(Packet):
fields_desc = [
ByteEnumField('type', 0, {0: "stop", 1: "start"}),
StrFixedLenField('ccp_reserved', b'\xff' * 5, length=5)
]
bind_layers(CRO, START_STOP_ALL, cmd=0x08)
class DIAG_SERVICE(Packet):
fields_desc = [
ShortField('diag_service', 0),
StrFixedLenField('ccp_reserved', b'\xff' * 4, length=4)
]
bind_layers(CRO, DIAG_SERVICE, cmd=0x20)
class ACTION_SERVICE(Packet):
fields_desc = [
ShortField('action_service', 0),
StrFixedLenField('ccp_reserved', b'\xff' * 4, length=4)
]
bind_layers(CRO, ACTION_SERVICE, cmd=0x21)
# ##### DTOs ######
class DEFAULT_DTO(Packet):
fields_desc = [
StrFixedLenField('load', b'\xff' * 5, length=5),
]
class GET_CCP_VERSION_DTO(Packet):
fields_desc = [
XByteField('main_protocol_version', 0),
XByteField('release_version', 0),
StrFixedLenField('ccp_reserved', b'\x00' * 3, length=3)
]
class EXCHANGE_ID_DTO(Packet):
fields_desc = [
ByteField('slave_device_ID_length', 0),
ByteField('data_type_qualifier', 0),
ByteField('resource_availability_mask', 0),
ByteField('resource_protection_mask', 0),
StrFixedLenField('ccp_reserved', b'\xff' * 1, length=1),
]
class GET_SEED_DTO(Packet):
fields_desc = [
XByteField('protection_status', 0),
StrFixedLenField('seed', b'\x00' * 4, length=4)
]
class UNLOCK_DTO(Packet):
fields_desc = [
ByteField('privilege_status', 0),
StrFixedLenField('ccp_reserved', b'\xff' * 4, length=4),
]
class DNLOAD_DTO(Packet):
fields_desc = [
XByteField('MTA0_extension', 0),
XIntField('MTA0_address', 0)
]
class DNLOAD_6_DTO(Packet):
fields_desc = [
XByteField('MTA0_extension', 0),
XIntField('MTA0_address', 0)
]
class UPLOAD_DTO(Packet):
fields_desc = [
StrFixedLenField('data', b'\x00' * 5, length=5)
]
class SHORT_UP_DTO(Packet):
fields_desc = [
StrFixedLenField('data', b'\x00' * 5, length=5)
]
class GET_DAQ_SIZE_DTO(Packet):
fields_desc = [
XByteField('DAQ_list_size', 0),
XByteField('first_pid', 0),
StrFixedLenField('ccp_reserved', b'\xff' * 3, length=3)
]
class GET_S_STATUS_DTO(Packet):
fields_desc = [
FlagsField("session_status", 0, 8, ["CAL", "DAQ", "RESUME", "RES0",
"RES1", "RES2", "STORE", "RUN"]),
ByteField('information_qualifier', 0),
StrFixedLenField('information', b'\x00' * 3, length=3)
]
class BUILD_CHKSUM_DTO(Packet):
fields_desc = [
ByteField('checksum_size', 0),
StrFixedLenField('checksum_data', b'\x00' * 4,
length_from=lambda pkt: pkt.checksum_size),
StrFixedLenField('ccp_reserved', b'\xff' * 0,
length_from=lambda pkt: 4 - pkt.checksum_size)
]
class PROGRAM_DTO(Packet):
fields_desc = [
ByteField('MTA0_extension', 0),
XIntField('MTA0_address', 0)
]
class PROGRAM_6_DTO(Packet):
fields_desc = [
ByteField('MTA0_extension', 0),
XIntField('MTA0_address', 0)
]
class GET_ACTIVE_CAL_PAGE_DTO(Packet):
fields_desc = [
XByteField('address_extension', 0),
XIntField('address', 0)
]
class DIAG_SERVICE_DTO(Packet):
fields_desc = [
ByteField('data_length', 0),
ByteField('data_type', 0),
StrFixedLenField('ccp_reserved', b'\xff' * 3, length=3)
]
class ACTION_SERVICE_DTO(Packet):
fields_desc = [
ByteField('data_length', 0),
ByteField('data_type', 0),
StrFixedLenField('ccp_reserved', b'\xff' * 3, length=3)
]
class DTO(Packet):
__slots__ = Packet.__slots__ + ["payload_cls"]
return_codes = {
0x00: "acknowledge / no error",
0x01: "DAQ processor overload",
0x10: "command processor busy",
0x11: "DAQ processor busy",
0x12: "internal timeout",
0x18: "key request",
0x19: "session status request",
0x20: "cold start request",
0x21: "cal. data init. request",
0x22: "DAQ list init. request",
0x23: "code update request",
0x30: "unknown command",
0x31: "command syntax",
0x32: "parameter(s) out of range",
0x33: "access denied",
0x34: "overload",
0x35: "access locked",
0x36: "resource/function not available"
}
fields_desc = [
XByteField("packet_id", 0xff),
ByteEnumField('return_code', 0x00, return_codes),
ByteField('ctr', 0)
]
def __init__(self, *args, **kwargs):
self.payload_cls = DEFAULT_DTO
if "payload_cls" in kwargs:
self.payload_cls = kwargs["payload_cls"]
del kwargs["payload_cls"]
Packet.__init__(self, *args, **kwargs)
def guess_payload_class(self, payload):
return self.payload_cls
@staticmethod
def get_dto_cls(cmd):
try:
return {
0x03: DNLOAD_DTO,
0x04: UPLOAD_DTO,
0x09: GET_ACTIVE_CAL_PAGE_DTO,
0x0D: GET_S_STATUS_DTO,
0x0E: BUILD_CHKSUM_DTO,
0x0F: SHORT_UP_DTO,
0x12: GET_SEED_DTO,
0x13: UNLOCK_DTO,
0x14: GET_DAQ_SIZE_DTO,
0x17: EXCHANGE_ID_DTO,
0x18: PROGRAM_DTO,
0x1B: GET_CCP_VERSION_DTO,
0x20: DIAG_SERVICE_DTO,
0x21: ACTION_SERVICE_DTO,
0x22: PROGRAM_6_DTO,
0x23: DNLOAD_6_DTO
}[cmd]
except KeyError:
return DEFAULT_DTO
def answers(self, other):
"""In CCP, the payload of a DTO packet is dependent on the cmd field
of a corresponding CRO packet. Two packets correspond, if there
ctr field is equal. If answers detect the corresponding CRO, it will
interpret the payload of a DTO with the correct class. In CCP, there is
no other way, to determine the class of a DTO payload. Since answers is
called on sr and sr1, this modification of the original answers
implementation will give a better user experience. """
if not hasattr(other, "ctr"):
return 0
if self.ctr != other.ctr:
return 0
if not hasattr(other, "cmd"):
return 0
new_pl_cls = self.get_dto_cls(other.cmd)
if self.payload_cls != new_pl_cls and \
self.payload_cls == DEFAULT_DTO:
data = bytes(self.load)
self.remove_payload()
self.add_payload(new_pl_cls(data))
self.payload_cls = new_pl_cls
return 1
def hashret(self):
return struct.pack('B', self.ctr)
bind_bottom_up(CCP, DTO)

View file

@ -0,0 +1,998 @@
% Regression tests for the CCP layer
# More information at http://www.secdev.org/projects/UTscapy/
############
############
+ Basic operations
= Load module
load_contrib("automotive.ccp")
= Build CRO CONNECT
cro = CCP(identifier=0x700)/CRO(ctr=1)/CONNECT(station_address=0x02)
assert cro.identifier == 0x700
assert cro.length == 8
assert cro.flags == 0
assert cro.ctr == 1
assert cro.cmd == 1
assert cro.station_address == 0x02
assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x01\x01\x02\x00\xff\xff\xff\xff'
= Dissect DTO CONNECT
dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x01\xff\xff\xff\xff\xff')
assert dto.answers(cro)
assert dto.identifier == 0x700
assert dto.length == 8
assert dto.flags == 0
assert dto.ctr == 1
assert dto.load == b"\xff" * 5
= Build CRO EXCHANGE_ID
cro = CCP(identifier=0x700)/CRO(ctr=18)/EXCHANGE_ID(ccp_master_device_id=b'abcdef')
assert cro.identifier == 0x700
assert cro.length == 8
assert cro.flags == 0
assert cro.ctr == 18
assert cro.cmd == 0x17
assert cro.ccp_master_device_id == b"abcdef"
assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x17\x12abcdef'
= Dissect DTO EXCHANGE_ID
dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x12\x04\x02\x03\x03\xff')
assert dto.ctr == 18
assert dto.packet_id == 0xff
assert dto.return_code == 0
assert dto.load == b'\x04\x02\x03\x03\xff'
# answers will interpret payload
assert dto.answers(cro)
assert dto.ctr == 18
assert dto.packet_id == 0xff
assert dto.return_code == 0
assert hasattr(dto, "load") == False
assert dto.slave_device_ID_length == 4
assert dto.data_type_qualifier == 2
assert dto.resource_availability_mask == 3
assert dto.resource_protection_mask == 3
assert dto.ccp_reserved == b"\xff"
= Build CRO GET_SEED
cro = CCP(identifier=0x711)/CRO(ctr=19)/GET_SEED(resource=2)
assert cro.identifier == 0x711
assert cro.length == 8
assert cro.flags == 0
assert cro.ctr == 19
assert cro.cmd == 0x12
assert cro.resource == 2
assert cro.ccp_reserved == b"\xff" * 5
assert bytes(cro) == b'\x00\x00\x07\x11\x08\x00\x00\x00\x12\x13\x02\xff\xff\xff\xff\xff'
= Dissect DTO GET_SEED
dto = CCP(b'\x00\x00\x07\x11\x08\x00\x00\x00\xff\x00\x13\x01\x14\x15\x16\x17')
assert dto.ctr == 19
assert dto.packet_id == 0xff
assert dto.return_code == 0
assert dto.load == b'\x01\x14\x15\x16\x17'
# answers will interpret payload
assert dto.answers(cro)
assert dto.ctr == 19
assert dto.packet_id == 0xff
assert dto.return_code == 0
assert hasattr(dto, "load") == False
assert dto.protection_status == 0x1
assert dto.seed == b'\x14\x15\x16\x17'
= Build CRO UNLOCK
cro = CCP(identifier=0x711)/CRO(ctr=20)/UNLOCK(key=b"123456")
assert cro.identifier == 0x711
assert cro.length == 8
assert cro.flags == 0
assert cro.ctr == 20
assert cro.cmd == 0x13
assert cro.key == b"123456"
assert bytes(cro) == b'\x00\x00\x07\x11\x08\x00\x00\x00\x13\x14123456'
= Dissect DTO UNLOCK
dto = CCP(b'\x00\x00\x07\x11\x08\x00\x00\x00\xff\x00\x14\x02\xff\xff\xff\xff')
assert dto.ctr == 20
assert dto.packet_id == 0xff
assert dto.return_code == 0
assert dto.load == b'\x02\xff\xff\xff\xff'
# answers will interpret payload
assert dto.answers(cro)
assert dto.ctr == 20
assert dto.packet_id == 0xff
assert dto.return_code == 0
assert hasattr(dto, "load") == False
assert dto.privilege_status == 0x2
assert dto.ccp_reserved == b"\xff" * 4
= Build CRO SET_MTA
cro = CCP(identifier=0x711)/CRO(ctr=21)/SET_MTA(mta_num=0, address_extension=0x02, address=0x34002000)
assert cro.identifier == 0x711
assert cro.length == 8
assert cro.flags == 0
assert cro.ctr == 21
assert cro.cmd == 0x02
assert cro.mta_num == 0
assert cro.address_extension == 2
assert cro.address == 0x34002000
assert bytes(cro) == b'\x00\x00\x07\x11\x08\x00\x00\x00\x02\x15\x00\x02\x34\x00\x20\x00'
= Dissect DTO SET_MTA
dto = CCP(b'\x00\x00\x07\x11\x08\x00\x00\x00\xff\x00\x15\xff\xff\xff\xff\xff')
assert dto.ctr == 21
assert dto.packet_id == 0xff
assert dto.return_code == 0
assert dto.load == b'\xff\xff\xff\xff\xff'
# answers will interpret payload
assert dto.answers(cro)
assert dto.ctr == 21
assert dto.packet_id == 0xff
assert dto.return_code == 0
assert hasattr(dto, "load") == True
assert dto.load == b"\xff" * 5
= Build CRO DNLOAD
cro = CCP(identifier=0x700)/CRO(ctr=17)/DNLOAD(size=0x05, data=b'abcde')
assert cro.identifier == 0x700
assert cro.length == 8
assert cro.flags == 0
assert cro.ctr == 17
assert cro.cmd == 3
assert cro.size == 0x05
assert cro.data == b'abcde'
assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x03\x11\x05abcde'
= Dissect DTO DNLOAD
dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x11\x02\x34\x00\x20\x05')
assert dto.ctr == 17
assert dto.packet_id == 0xff
assert dto.return_code == 0
assert dto.load == b'\x024\x00 \x05'
# answers will interpret payload
assert dto.answers(cro)
assert dto.ctr == 17
assert dto.packet_id == 0xff
assert dto.return_code == 0
assert hasattr(dto, "load") == False
assert dto.MTA0_extension == 2
assert dto.MTA0_address == 0x34002005
= Build CRO DNLOAD_6
cro = CCP(identifier=0x700)/CRO(ctr=0x40)/DNLOAD_6(data=b'abcdef')
assert cro.identifier == 0x700
assert cro.length == 8
assert cro.flags == 0
assert cro.ctr == 0x40
assert cro.cmd == 0x23
assert cro.data == b'abcdef'
assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x23\x40abcdef'
= Dissect DTO DNLOAD_6
dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x40\x02\x34\x00\x20\x06')
assert dto.ctr == 64
assert dto.packet_id == 0xff
assert dto.return_code == 0
assert dto.load == b'\x024\x00 \x06'
# answers will interpret payload
assert dto.answers(cro)
assert dto.ctr == 64
assert dto.packet_id == 0xff
assert dto.return_code == 0
assert hasattr(dto, "load") == False
assert dto.MTA0_extension == 2
assert dto.MTA0_address == 0x34002006
= Build CRO UPLOAD
cro = CCP(identifier=0x700)/CRO(ctr=0x41)/UPLOAD(size=4)
assert cro.identifier == 0x700
assert cro.length == 8
assert cro.flags == 0
assert cro.ctr == 0x41
assert cro.cmd == 0x04
assert cro.size == 4
assert cro.ccp_reserved == b"\xff" * 5
assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x04\x41\x04\xff\xff\xff\xff\xff'
= Dissect DTO UPLOAD
dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x41\x10\x11\x12\x13\xff')
assert dto.ctr == 65
assert dto.packet_id == 0xff
assert dto.return_code == 0
assert dto.load == b'\x10\x11\x12\x13\xff'
# answers will interpret payload
assert dto.answers(cro)
assert dto.ctr == 65
assert dto.packet_id == 0xff
assert dto.return_code == 0
assert hasattr(dto, "load") == False
assert dto.data == b"\x10\x11\x12\x13\xff"
= Build CRO SHORT_UP
cro = CCP(identifier=0x700)/CRO(ctr=0x42)/SHORT_UP(size=4, address_extension=0, address=0x12345678)
assert cro.identifier == 0x700
assert cro.length == 8
assert cro.flags == 0
assert cro.ctr == 0x42
assert cro.cmd == 0x0f
assert cro.size == 4
assert cro.address == 0x12345678
assert cro.address_extension == 0
assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x0f\x42\x04\x00\x12\x34\x56\x78'
= Dissect DTO SHORT_UP
dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x42\x10\x11\x12\x13\xff')
assert dto.ctr == 66
assert dto.packet_id == 0xff
assert dto.return_code == 0
assert dto.load == b'\x10\x11\x12\x13\xff'
# answers will interpret payload
assert dto.answers(cro)
assert dto.ctr == 66
assert dto.packet_id == 0xff
assert dto.return_code == 0
assert hasattr(dto, "load") == False
assert dto.data == b"\x10\x11\x12\x13\xff"
= Build CRO SELECT_CAL_PAGE
cro = CCP(identifier=0x700)/CRO(ctr=0x43)/SELECT_CAL_PAGE()
assert cro.identifier == 0x700
assert cro.length == 8
assert cro.flags == 0
assert cro.ctr == 0x43
assert cro.cmd == 0x11
assert cro.ccp_reserved == b"\xff" * 6
assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x11\x43\xff\xff\xff\xff\xff\xff'
= Dissect DTO SELECT_CAL_PAGE
dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x43\xff\xff\xff\xff\xff')
assert dto.ctr == 67
assert dto.packet_id == 0xff
assert dto.return_code == 0
assert dto.load == b'\xff\xff\xff\xff\xff'
# answers will interpret payload
assert dto.answers(cro)
assert dto.ctr == 67
assert dto.packet_id == 0xff
assert dto.return_code == 0
assert hasattr(dto, "load") == True
assert dto.load == b"\xff\xff\xff\xff\xff"
= Build CRO GET_DAQ_SIZE
cro = CCP(identifier=0x700)/CRO(ctr=0x44)/GET_DAQ_SIZE(DAQ_num=0x03, DTO_identifier=0x1020304)
assert cro.identifier == 0x700
assert cro.length == 8
assert cro.flags == 0
assert cro.ctr == 0x44
assert cro.cmd == 0x14
assert cro.DAQ_num == 0x03
assert cro.ccp_reserved == 00
assert cro.DTO_identifier == 0x01020304
assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x14\x44\x03\x00\x01\x02\x03\x04'
= Dissect DTO GET_DAQ_SIZE
dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x44\x10\x08\xff\xff\xff')
assert dto.ctr == 68
assert dto.packet_id == 0xff
assert dto.return_code == 0
assert dto.load == b'\x10\x08\xff\xff\xff'
# answers will interpret payload
assert dto.answers(cro)
assert dto.ctr == 68
assert dto.packet_id == 0xff
assert dto.return_code == 0
assert hasattr(dto, "load") == False
assert dto.DAQ_list_size == 16
assert dto.first_pid == 8
assert dto.ccp_reserved == b"\xff\xff\xff"
= Build CRO SET_DAQ_PTR
cro = CCP(identifier=0x700)/CRO(ctr=0x45)/SET_DAQ_PTR(DAQ_num=3, ODT_num=5, ODT_element=2)
assert cro.identifier == 0x700
assert cro.length == 8
assert cro.flags == 0
assert cro.ctr == 0x45
assert cro.cmd == 0x15
assert cro.DAQ_num == 0x03
assert cro.ODT_num == 5
assert cro.ODT_element == 2
assert cro.ccp_reserved == b"\xff\xff\xff"
assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x15\x45\x03\x05\x02\xff\xff\xff'
= Dissect DTO SET_DAQ_PTR
dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x45\xff\xff\xff\xff\xff')
assert dto.ctr == 69
assert dto.packet_id == 0xff
assert dto.return_code == 0
assert dto.load == b'\xff\xff\xff\xff\xff'
# answers will interpret payload
assert dto.answers(cro)
assert dto.ctr == 69
assert dto.packet_id == 0xff
assert dto.return_code == 0
assert hasattr(dto, "load") == True
assert dto.load == b'\xff\xff\xff\xff\xff'
= Build CRO WRITE_DAQ
cro = CCP(identifier=0x700)/CRO(ctr=0x46)/WRITE_DAQ(DAQ_size=2, address_extension=1, address=0x2004200)
assert cro.identifier == 0x700
assert cro.length == 8
assert cro.flags == 0
assert cro.ctr == 0x46
assert cro.cmd == 0x16
assert cro.DAQ_size == 0x02
assert cro.address_extension == 1
assert cro.address == 0x2004200
assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x16\x46\x02\x01\x02\x00\x42\x00'
= Dissect DTO WRITE_DAQ
dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x46\xff\xff\xff\xff\xff')
assert dto.ctr == 70
assert dto.packet_id == 0xff
assert dto.return_code == 0
assert dto.load == b'\xff\xff\xff\xff\xff'
# answers will interpret payload
assert dto.answers(cro)
assert dto.ctr == 70
assert dto.packet_id == 0xff
assert dto.return_code == 0
assert hasattr(dto, "load") == True
assert dto.load == b'\xff\xff\xff\xff\xff'
= Build CRO START_STOP
cro = CCP(identifier=0x700)/CRO(ctr=0x47)/START_STOP(mode=1, DAQ_num=3, ODT_num=7, event_channel=2, transmission_rate=1)
assert cro.identifier == 0x700
assert cro.length == 8
assert cro.flags == 0
assert cro.ctr == 0x47
assert cro.cmd == 0x06
assert cro.mode == 0x01
assert cro.DAQ_num == 3
assert cro.event_channel == 2
assert cro.transmission_rate == 1
assert cro.ODT_num == 7
assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x06\x47\x01\x03\x07\x02\x00\x01'
= Dissect DTO START_STOP
dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x47\xff\xff\xff\xff\xff')
assert dto.ctr == 71
assert dto.packet_id == 0xff
assert dto.return_code == 0
assert dto.load == b'\xff\xff\xff\xff\xff'
# answers will interpret payload
assert dto.answers(cro)
assert dto.ctr == 71
assert dto.packet_id == 0xff
assert dto.return_code == 0
assert hasattr(dto, "load") == True
assert dto.load == b'\xff\xff\xff\xff\xff'
= Build CRO DISCONNECT
cro = CCP(identifier=0x700)/CRO(ctr=0x48)/DISCONNECT(type="temporary", station_address=0x208)
assert cro.identifier == 0x700
assert cro.length == 8
assert cro.flags == 0
assert cro.ctr == 0x48
assert cro.cmd == 0x07
assert cro.type == 0x00
assert cro.station_address == 0x208
assert cro.ccp_reserved0 == b"\xff"
assert cro.ccp_reserved == b"\xff" * 2
assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x07\x48\x00\xff\x08\x02\xff\xff'
= Dissect DTO DISCONNECT
dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x48\xff\xff\xff\xff\xff')
assert dto.ctr == 72
assert dto.packet_id == 0xff
assert dto.return_code == 0
assert dto.load == b'\xff\xff\xff\xff\xff'
# answers will interpret payload
assert dto.answers(cro)
assert dto.ctr == 72
assert dto.packet_id == 0xff
assert dto.return_code == 0
assert hasattr(dto, "load") == True
assert dto.load == b'\xff\xff\xff\xff\xff'
= Build CRO SET_S_STATUS
cro = CCP(identifier=0x700)/CRO(ctr=0x49)/SET_S_STATUS(session_status="RUN+CAL")
assert cro.identifier == 0x700
assert cro.length == 8
assert cro.flags == 0
assert cro.ctr == 0x49
assert cro.cmd == 0x0c
assert cro.session_status == 0x81
assert cro.ccp_reserved == b"\xff" * 5
assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x0c\x49\x81\xff\xff\xff\xff\xff'
= Dissect DTO SET_S_STATUS
dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x49\xff\xff\xff\xff\xff')
assert dto.ctr == 73
assert dto.packet_id == 0xff
assert dto.return_code == 0
assert dto.load == b'\xff\xff\xff\xff\xff'
# answers will interpret payload
assert dto.answers(cro)
assert dto.ctr == 73
assert dto.packet_id == 0xff
assert dto.return_code == 0
assert hasattr(dto, "load") == True
assert dto.load == b'\xff\xff\xff\xff\xff'
= Build CRO GET_S_STATUS
cro = CCP(identifier=0x700)/CRO(ctr=0x4a)/GET_S_STATUS()
assert cro.identifier == 0x700
assert cro.length == 8
assert cro.flags == 0
assert cro.ctr == 0x4a
assert cro.cmd == 0x0D
assert cro.ccp_reserved == b"\xff" * 6
assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x0d\x4a\xff\xff\xff\xff\xff\xff'
= Dissect DTO GET_S_STATUS
dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x4a\x81\xff\xff\xff\xff')
assert dto.ctr == 74
assert dto.packet_id == 0xff
assert dto.return_code == 0
assert dto.load == b'\x81\xff\xff\xff\xff'
# answers will interpret payload
assert dto.answers(cro)
assert dto.ctr == 74
assert dto.packet_id == 0xff
assert dto.return_code == 0
assert hasattr(dto, "load") == False
assert dto.session_status == 0x81
assert dto.information_qualifier == 0xff
assert dto.information == b"\xff" * 3
= Build CRO BUILD_CHKSUM
cro = CCP(identifier=0x700)/CRO(ctr=0x50)/BUILD_CHKSUM(size=0x8000)
assert cro.identifier == 0x700
assert cro.length == 8
assert cro.flags == 0
assert cro.ctr == 0x50
assert cro.cmd == 0x0e
assert cro.size == 0x8000
assert cro.ccp_reserved == b"\xff" * 2
assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x0e\x50\x00\x00\x80\x00\xff\xff'
= Dissect DTO BUILD_CHKSUM
dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x50\x02\x12\x34\xff\xff')
assert dto.ctr == 80
assert dto.packet_id == 0xff
assert dto.return_code == 0
assert dto.load == b'\x02\x12\x34\xff\xff'
# answers will interpret payload
assert dto.answers(cro)
assert dto.ctr == 80
assert dto.packet_id == 0xff
assert dto.return_code == 0
assert hasattr(dto, "load") == False
assert dto.checksum_size == 2
assert dto.checksum_data == b'\x12\x34'
assert dto.ccp_reserved == b'\xff\xff'
= Dissect DTO BUILD_CHKSUM2
dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x50\x04\x12\x34\x56\x78')
assert dto.ctr == 80
assert dto.packet_id == 0xff
assert dto.return_code == 0
assert dto.load == b'\x04\x12\x34\x56\x78'
# answers will interpret payload
assert dto.answers(cro)
assert dto.ctr == 80
assert dto.packet_id == 0xff
assert dto.return_code == 0
assert hasattr(dto, "load") == False
assert dto.checksum_size == 4
assert dto.checksum_data == b'\x12\x34\x56\x78'
assert dto.ccp_reserved == b''
= Build CRO CLEAR_MEMORY
cro = CCP(identifier=0x700)/CRO(ctr=0x51)/CLEAR_MEMORY(size=0x8000)
assert cro.identifier == 0x700
assert cro.length == 8
assert cro.flags == 0
assert cro.ctr == 0x51
assert cro.cmd == 0x10
assert cro.size == 0x8000
assert cro.ccp_reserved == b"\xff" * 2
assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x10\x51\x00\x00\x80\x00\xff\xff'
= Dissect DTO CLEAR_MEMORY
dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x51\xff\xff\xff\xff\xff')
assert dto.ctr == 81
assert dto.packet_id == 0xff
assert dto.return_code == 0
assert dto.load == b'\xff\xff\xff\xff\xff'
# answers will interpret payload
assert dto.answers(cro)
assert dto.ctr == 81
assert dto.packet_id == 0xff
assert dto.return_code == 0
assert hasattr(dto, "load") == True
assert dto.load == b'\xff\xff\xff\xff\xff'
= Build CRO PROGRAM
cro = CCP(identifier=0x700)/CRO(ctr=0x52)/PROGRAM(size=0x3, data=b"\x10\x11\x12")
assert cro.identifier == 0x700
assert cro.length == 8
assert cro.flags == 0
assert cro.ctr == 0x52
assert cro.cmd == 0x18
assert cro.size == 0x3
assert cro.data == b"\x10\x11\x12"
assert cro.ccp_reserved == b"\xff" * 5
assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x18\x52\x03\x10\x11\x12\xff\xff'
= Dissect DTO PROGRAM
dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x52\x02\x34\x00\x20\x03')
assert dto.ctr == 82
assert dto.packet_id == 0xff
assert dto.return_code == 0
assert dto.load == b'\x02\x34\x00\x20\x03'
# answers will interpret payload
assert dto.answers(cro)
assert dto.ctr == 82
assert dto.packet_id == 0xff
assert dto.return_code == 0
assert hasattr(dto, "load") == False
assert dto.MTA0_extension == 2
assert dto.MTA0_address == 0x34002003
= Build CRO PROGRAM_6
cro = CCP(identifier=0x700)/CRO(ctr=0x53)/PROGRAM_6(data=b"\x10\x11\x12\x10\x11\x12")
assert cro.identifier == 0x700
assert cro.length == 8
assert cro.flags == 0
assert cro.ctr == 0x53
assert cro.cmd == 0x22
assert cro.data == b"\x10\x11\x12\x10\x11\x12"
assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x22\x53\x10\x11\x12\x10\x11\x12'
= Dissect DTO PROGRAM_6
dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x53\x02\x34\x00\x20\x06')
assert dto.ctr == 83
assert dto.packet_id == 0xff
assert dto.return_code == 0
assert dto.load == b'\x02\x34\x00\x20\x06'
# answers will interpret payload
assert dto.answers(cro)
assert dto.ctr == 83
assert dto.packet_id == 0xff
assert dto.return_code == 0
assert hasattr(dto, "load") == False
assert dto.MTA0_extension == 2
assert dto.MTA0_address == 0x34002006
= Build CRO MOVE
cro = CCP(identifier=0x700)/CRO(ctr=0x54)/MOVE(size=0x8000)
assert cro.identifier == 0x700
assert cro.length == 8
assert cro.flags == 0
assert cro.ctr == 0x54
assert cro.cmd == 0x19
assert cro.size == 0x8000
assert cro.ccp_reserved == b'\xff\xff'
assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x19\x54\x00\x00\x80\x00\xff\xff'
= Dissect DTO MOVE
dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x54\xff\xff\xff\xff\xff')
assert dto.ctr == 84
assert dto.packet_id == 0xff
assert dto.return_code == 0
assert dto.load == b'\xff\xff\xff\xff\xff'
# answers will interpret payload
assert dto.answers(cro)
assert dto.ctr == 84
assert dto.packet_id == 0xff
assert dto.return_code == 0
assert hasattr(dto, "load") == True
= Build CRO DIAG_SERVICE
cro = CCP(identifier=0x700)/CRO(ctr=0x55)/DIAG_SERVICE(diag_service=0x8000)
assert cro.identifier == 0x700
assert cro.length == 8
assert cro.flags == 0
assert cro.ctr == 0x55
assert cro.cmd == 0x20
assert cro.diag_service == 0x8000
assert cro.ccp_reserved == b'\xff\xff\xff\xff'
assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x20\x55\x80\x00\xff\xff\xff\xff'
= Dissect DTO DIAG_SERVICE
dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x55\x20\x00\xff\xff\xff')
assert dto.ctr == 85
assert dto.packet_id == 0xff
assert dto.return_code == 0
assert dto.load == b'\x20\x00\xff\xff\xff'
# answers will interpret payload
assert dto.answers(cro)
assert dto.ctr == 85
assert dto.packet_id == 0xff
assert dto.return_code == 0
assert hasattr(dto, "load") == False
assert dto.data_length == 0x20
assert dto.data_type == 0x00
assert dto.ccp_reserved == b"\xff\xff\xff"
= Build CRO ACTION_SERVICE
cro = CCP(identifier=0x700)/CRO(ctr=0x56)/ACTION_SERVICE(action_service=0x8000)
assert cro.identifier == 0x700
assert cro.length == 8
assert cro.flags == 0
assert cro.ctr == 0x56
assert cro.cmd == 0x21
assert cro.action_service == 0x8000
assert cro.ccp_reserved == b'\xff\xff\xff\xff'
assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x21\x56\x80\x00\xff\xff\xff\xff'
= Dissect DTO ACTION_SERVICE
dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x56\x20\x00\xff\xff\xff')
assert dto.ctr == 86
assert dto.packet_id == 0xff
assert dto.return_code == 0
assert dto.load == b'\x20\x00\xff\xff\xff'
# answers will interpret payload
assert dto.answers(cro)
assert dto.ctr == 86
assert dto.packet_id == 0xff
assert dto.return_code == 0
assert hasattr(dto, "load") == False
assert dto.data_length == 0x20
assert dto.data_type == 0x00
assert dto.ccp_reserved == b"\xff\xff\xff"
= Build CRO TEST
cro = CCP(identifier=0x700)/CRO(ctr=0x60)/TEST(station_address=0x80)
assert cro.identifier == 0x700
assert cro.length == 8
assert cro.flags == 0
assert cro.ctr == 0x60
assert cro.cmd == 0x05
assert cro.station_address == 0x80
assert cro.ccp_reserved == b"\xff" * 4
assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x05\x60\x80\x00\xff\xff\xff\xff'
= Dissect DTO TEST
dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x60\xff\xff\xff\xff\xff')
assert dto.ctr == 96
assert dto.packet_id == 0xff
assert dto.return_code == 0
assert dto.load == b'\xff\xff\xff\xff\xff'
# answers will interpret payload
assert dto.answers(cro)
assert dto.ctr == 96
assert dto.packet_id == 0xff
assert dto.return_code == 0
assert hasattr(dto, "load") == True
assert dto.load == b'\xff\xff\xff\xff\xff'
= Build CRO START_STOP_ALL
cro = CCP(identifier=0x700)/CRO(ctr=0x61)/START_STOP_ALL(type="start")
assert cro.identifier == 0x700
assert cro.length == 8
assert cro.flags == 0
assert cro.ctr == 0x61
assert cro.cmd == 0x08
assert cro.type == 0x01
assert cro.ccp_reserved == b"\xff" * 5
assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x08\x61\x01\xff\xff\xff\xff\xff'
= Dissect DTO START_STOP_ALL
dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x61\xff\xff\xff\xff\xff')
assert dto.ctr == 97
assert dto.packet_id == 0xff
assert dto.return_code == 0
assert dto.load == b'\xff\xff\xff\xff\xff'
# answers will interpret payload
assert dto.answers(cro)
assert dto.ctr == 97
assert dto.packet_id == 0xff
assert dto.return_code == 0
assert hasattr(dto, "load") == True
assert dto.load == b'\xff\xff\xff\xff\xff'
= Build CRO GET_ACTIVE_CAL_PAGE
cro = CCP(identifier=0x700)/CRO(ctr=0x62)/GET_ACTIVE_CAL_PAGE()
assert cro.identifier == 0x700
assert cro.length == 8
assert cro.flags == 0
assert cro.ctr == 0x62
assert cro.cmd == 0x09
assert cro.ccp_reserved == b"\xff" * 6
assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x09\x62\xff\xff\xff\xff\xff\xff'
= Dissect DTO GET_ACTIVE_CAL_PAGE
dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x62\x01\x11\x44\x77\x22')
assert dto.ctr == 98
assert dto.packet_id == 0xff
assert dto.return_code == 0
assert dto.load == b'\x01\x11\x44\x77\x22'
# answers will interpret payload
assert dto.answers(cro)
assert dto.ctr == 98
assert dto.packet_id == 0xff
assert dto.return_code == 0
assert hasattr(dto, "load") == False
assert dto.address_extension == 1
assert dto.address == 0x11447722
= Build CRO GET_CCP_VERSION
cro = CCP(identifier=0x700)/CRO(ctr=0x63)/GET_CCP_VERSION(main_protocol_version=2, release_version=1)
assert cro.identifier == 0x700
assert cro.length == 8
assert cro.flags == 0
assert cro.ctr == 0x63
assert cro.cmd == 0x1b
assert cro.main_protocol_version == 2
assert cro.release_version == 1
assert cro.ccp_reserved == b"\xff" * 4
assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x1b\x63\x02\x01\xff\xff\xff\xff'
assert dto.hashret() != cro.hashret()
assert not dto.answers(cro)
= Dissect DTO GET_CCP_VERSION
dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x63\x02\x01\xff\xff\xff')
assert dto.ctr == 99
assert dto.packet_id == 0xff
assert dto.return_code == 0
assert dto.load == b'\x02\x01\xff\xff\xff'
# answers will interpret payload
assert dto.answers(cro)
assert dto.ctr == 99
assert dto.packet_id == 0xff
assert dto.return_code == 0
assert hasattr(dto, "load") == False
assert dto.main_protocol_version == 2
assert dto.release_version == 1
assert dto.ccp_reserved == b"\xff" * 3
assert dto.hashret() == cro.hashret()
+ Tests on a virtual CAN-Bus
~ needs_root linux
= Load modules
~ needs_root linux conf
import can
from subprocess import call
import time
conf.contribs['CANSocket'] = {'use-python-can': True}
load_contrib("cansocket")
iface0 = "vcan0"
= Initialize a virtual CAN interface
~ needs_root linux conf
if 0 != call("cansend %s 000#" % iface0, shell=True):
# vcan0 is not enabled
if 0 != call("sudo modprobe vcan", shell=True):
raise Exception("modprobe vcan failed")
if 0 != call("sudo ip link add name %s type vcan" % iface0, shell=True):
print("add %s failed: Maybe it was already up?" % iface0)
if 0 != call("sudo ip link set dev %s up" % iface0, shell=True):
raise Exception("could not bring up %s" % iface0)
if 0 != call("cansend %s 000#" % iface0, shell=True):
raise Exception("cansend doesn't work")
print("CAN should work now")
= CAN Socket sr1 with dto.ansers(cro) == True
~ needs_root linux
sock1 = CANSocket(iface=can.interface.Bus(bustype='socketcan', channel='vcan0', bitrate=250000), basecls=CCP)
sock2 = CANSocket(iface=can.interface.Bus(bustype='socketcan', channel='vcan0', bitrate=250000))
def ecu():
pkts = sock2.sniff(count=1, timeout=1)
if len(pkts) == 1:
cro = CRO(pkts[0].data)
assert cro.cmd == 0x22
assert cro.data == b"\x10\x11\x12\x10\x11\x12"
sock2.send(CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x53\x02\x34\x00\x20\x06'))
thread = threading.Thread(target=ecu)
thread.start()
time.sleep(0.1)
dto = sock1.sr1(CCP(identifier=0x700)/CRO(ctr=0x53)/PROGRAM_6(data=b"\x10\x11\x12\x10\x11\x12"), timeout=1)
thread.join()
assert dto.ctr == 83
assert dto.packet_id == 0xff
assert dto.return_code == 0
assert hasattr(dto, "load") == False
assert dto.MTA0_extension == 2
assert dto.MTA0_address == 0x34002006
sock1.close()
sock2.close()
= CAN Socket sr1 with dto.ansers(cro) == False
~ needs_root linux
sock1 = CANSocket(iface=can.interface.Bus(bustype='socketcan', channel='vcan0', bitrate=250000), basecls=CCP)
sock2 = CANSocket(iface=can.interface.Bus(bustype='socketcan', channel='vcan0', bitrate=250000))
def ecu():
pkts = sock2.sniff(count=1, timeout=1)
if len(pkts) == 1:
cro = CRO(pkts[0].data)
assert cro.cmd == 0x22
assert cro.data == b"\x10\x11\x12\x10\x11\x12"
sock2.send(CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x55\x02\x34\x00\x20\x06'))
thread = threading.Thread(target=ecu)
thread.start()
time.sleep(0.1)
gotTimeout = False
try:
dto = sock1.sr1(CCP(identifier=0x700)/CRO(ctr=0x54)/PROGRAM_6(data=b"\x10\x11\x12\x10\x11\x12"), timeout=1)
except CANSocketTimeoutElapsed as e:
gotTimeout = True
assert gotTimeout
thread.join()
sock1.close()
sock2.close()
= CAN Socket sr1 with error code
~ needs_root linux
sock1 = CANSocket(iface=can.interface.Bus(bustype='socketcan', channel='vcan0', bitrate=250000), basecls=CCP)
sock2 = CANSocket(iface=can.interface.Bus(bustype='socketcan', channel='vcan0', bitrate=250000))
def ecu():
pkts = sock2.sniff(count=1, timeout=1)
if len(pkts) == 1:
cro = CRO(pkts[0].data)
assert cro.cmd == 0x22
assert cro.data == b"\x10\x11\x12\x10\x11\x12"
sock2.send(CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x01\x55\xff\xff\xff\xff\xff'))
thread = threading.Thread(target=ecu)
thread.start()
time.sleep(0.1)
dto = sock1.sr1(CCP(identifier=0x700)/CRO(ctr=0x55)/PROGRAM_6(data=b"\x10\x11\x12\x10\x11\x12"), timeout=1)
thread.join()
assert dto.ctr == 85
assert dto.packet_id == 0xff
assert dto.return_code == 1
assert hasattr(dto, "load") == False
assert dto.MTA0_extension == 0xff
assert dto.MTA0_address == 0xffffffff
sock1.close()
sock2.close()

View file

@ -0,0 +1,343 @@
#! /usr/bin/env python
# This file is part of Scapy
# See http://www.secdev.org/projects/scapy for more information
# Copyright (C) Nils Weiss <nils@we155.de>
# This program is published under a GPLv2 license
# scapy.contrib.description = Helper class for tracking ECU states (ECU)
# scapy.contrib.status = loads
import time
import random
from collections import defaultdict
from scapy.packet import Raw, Packet
from scapy.plist import PacketList
from scapy.error import Scapy_Exception
from scapy.sessions import DefaultSession
from scapy.ansmachine import AnsweringMachine
__all__ = ["ECU", "ECUResponse", "ECUSession", "ECU_am"]
class ECU(object):
"""A ECU object can be used to
- track the states of an ECU.
- to log all modification to an ECU
- to extract supported responses of a real ECU
Usage:
>>> print("This ecu logs, tracks and creates supported responses")
>>> my_virtual_ecu = ECU()
>>> my_virtual_ecu.update(PacketList([...]))
>>> my_virtual_ecu.supported_responses
>>> print("Another ecu just tracks")
>>> my_tracking_ecu = ECU(logging=False, store_supported_responses=False) # noqa: E501
>>> my_tracking_ecu.update(PacketList([...]))
>>> print("Another ecu just logs all modifications to it")
>>> my_logging_ecu = ECU(verbose=False, store_supported_responses=False) # noqa: E501
>>> my_logging_ecu.update(PacketList([...]))
>>> my_logging_ecu.log
>>> print("Another ecu just creates supported responses")
>>> my_response_ecu = ECU(verbose=False, logging=False)
>>> my_response_ecu.update(PacketList([...]))
>>> my_response_ecu.supported_responses
"""
def __init__(self, init_session=None, init_security_level=None,
init_communication_control=None, logging=True, verbose=True,
store_supported_responses=True):
"""
Initialize an ECU object
:param init_session: An initial session
:param init_security_level: An initial security level
:param init_communication_control: An initial communication control
setting
:param logging: Turn logging on or off. Default is on.
:param verbose: Turn tracking on or off. Default is on.
:param store_supported_responses: Turn creation of supported responses
on or off. Default is on.
"""
self.current_session = init_session or 1
self.current_security_level = init_security_level or 0
self.communication_control = init_communication_control or 0
self.verbose = verbose
self.logging = logging
self.store_supported_responses = store_supported_responses
self.log = defaultdict(list)
self._supported_responses = list()
self._unanswered_packets = PacketList()
def reset(self):
self.current_session = 1
self.current_security_level = 0
self.communication_control = 0
def update(self, p):
if isinstance(p, PacketList):
for pkt in p:
self._update(pkt)
elif not isinstance(p, Packet):
raise Scapy_Exception("Provide a Packet object for an update")
else:
self._update(p)
def _update(self, pkt):
if self.verbose:
print(repr(self), repr(pkt))
if self.store_supported_responses:
self._update_supported_responses(pkt)
if self.logging:
self._update_log(pkt)
self._update_internal_state(pkt)
def _update_log(self, pkt):
for l in pkt.layers():
if hasattr(l, "get_log"):
log_key, log_value = l.get_log(pkt)
self.log[log_key].append((pkt.time, log_value))
def _update_internal_state(self, pkt):
for l in pkt.layers():
if hasattr(l, "modifies_ecu_state"):
l.modifies_ecu_state(pkt, self)
def _update_supported_responses(self, pkt):
self._unanswered_packets += PacketList([pkt])
answered, unanswered = self._unanswered_packets.sr()
for _, resp in answered:
ecu_resp = ECUResponse(session=self.current_session,
security_level=self.current_security_level,
responses=resp)
if ecu_resp not in self._supported_responses:
if self.verbose:
print("[+] ", repr(ecu_resp))
self._supported_responses.append(ecu_resp)
else:
if self.verbose:
print("[-] ", repr(ecu_resp))
self._unanswered_packets = unanswered
@property
def supported_responses(self):
# This sorts responses in the following order:
# 1. Positive responses first
# 2. Lower ServiceID first
# 3. Longer (more specific) responses first
self._supported_responses.sort(
key=lambda x: (x.responses[0].service == 0x7f,
x.responses[0].service,
0xffffffff - len(x.responses[0])))
return self._supported_responses
@property
def unanswered_packets(self):
return self._unanswered_packets
def __repr__(self):
return "ses: %03d sec: %03d cc: %d" % (self.current_session,
self.current_security_level,
self.communication_control)
class ECUSession(DefaultSession):
"""Tracks modification to an ECU 'on-the-flow'.
Usage:
>>> sniff(session=ECUSession)
"""
def __init__(self, *args, **kwargs):
DefaultSession.__init__(self, *args, **kwargs)
self.ecu = ECU(init_session=kwargs.pop("init_session", None),
init_security_level=kwargs.pop("init_security_level", None), # noqa: E501
init_communication_control=kwargs.pop("init_communication_control", None), # noqa: E501
logging=kwargs.pop("logging", True),
verbose=kwargs.pop("verbose", True),
store_supported_responses=kwargs.pop("store_supported_responses", True)) # noqa: E501
def on_packet_received(self, pkt):
if not pkt:
return
if isinstance(pkt, list):
for p in pkt:
ECUSession.on_packet_received(self, p)
return
self.ecu.update(pkt)
DefaultSession.on_packet_received(self, pkt)
class ECUResponse:
"""Encapsulates a response and the according ECU state.
A list of this objects can be used to configure a ECU Answering Machine.
This is useful, if you want to clone the behaviour of a real ECU on a bus.
Usage:
>>> print("Generates a ECUResponse which answers on UDS()/UDS_RDBI(identifiers=[2]) if ECU is in session 2 and has security_level 2") # noqa: E501
>>> ECUResponse(session=2, security_level=2, responses=UDS()/UDS_RDBIPR(dataIdentifier=2)/Raw(b"deadbeef1")) # noqa: E501
>>> print("Further examples")
>>> ECUResponse(session=range(3,5), security_level=[3,4], responses=UDS()/UDS_RDBIPR(dataIdentifier=3)/Raw(b"deadbeef2")) # noqa: E501
>>> ECUResponse(session=[5,6,7], security_level=range(5,7), responses=UDS()/UDS_RDBIPR(dataIdentifier=5)/Raw(b"deadbeef3")) # noqa: E501
>>> ECUResponse(session=lambda x: 8 < x <= 10, security_level=lambda x: x > 10, responses=UDS()/UDS_RDBIPR(dataIdentifier=9)/Raw(b"deadbeef4")) # noqa: E501
"""
def __init__(self, session=1, security_level=0,
responses=Raw(b"\x7f\x10"),
answers=None):
"""
Initialize an ECUResponse capsule
:param session: Defines the session in which this response is valid.
A integer, a callable or any iterable object can be
provided.
:param security_level: Defines the security_level in which this
response is valid. A integer, a callable or any
iterable object can be provided.
:param responses: A Packet or a list of Packet objects. By default the
last packet is asked if it answers a incoming packet.
This allows to send for example
`requestCorrectlyReceived-ResponsePending` packets.
:param answers: Optional argument to provide a custom answer here:
`lambda resp, req: return resp.answers(req)`
This allows the modification of a response depending
on a request. Custom SecurityAccess mechanisms can
be implemented in this way or generic NegativeResponse
messages which answers to everything can be realized
in this way.
"""
self.__session = session \
if hasattr(session, "__iter__") or callable(session) else [session]
self.__security_level = security_level \
if hasattr(security_level, "__iter__") or callable(security_level)\
else [security_level]
if isinstance(responses, PacketList):
self.responses = responses
elif isinstance(responses, Packet):
self.responses = PacketList([responses])
elif hasattr(responses, "__iter__"):
self.responses = PacketList(responses)
else:
self.responses = PacketList([responses])
self.__custom_answers = answers
def in_correct_session(self, current_session):
if callable(self.__session):
return self.__session(current_session)
else:
return current_session in self.__session
def has_security_access(self, current_security_level):
if callable(self.__security_level):
return self.__security_level(current_security_level)
else:
return current_security_level in self.__security_level
def answers(self, other):
if self.__custom_answers is not None:
return self.__custom_answers(self.responses[-1], other)
else:
return self.responses[-1].answers(other)
def __repr__(self):
return "session=%s, security_level=%s, responses=%s" % \
(self.__session, self.__security_level,
[resp.summary() for resp in self.responses])
def __eq__(self, other):
return \
self.__class__ == other.__class__ and \
self.__session == other.__session and \
self.__security_level == other.__security_level and \
len(self.responses) == len(other.responses) and \
all(bytes(x) == bytes(y) for x, y in zip(self.responses,
other.responses))
def __ne__(self, other):
# Python 2.7 compat
return not self == other
__hash__ = None
class ECU_am(AnsweringMachine):
"""AnsweringMachine which emulates the basic behaviour of a real world ECU.
Provide a list of ``ECUResponse`` objects to configure the behaviour of this
AnsweringMachine.
:param supported_responses: List of ``ECUResponse`` objects to define
the behaviour. The default response is
``generalReject``.
:param main_socket: Defines the object of the socket to send
and receive packets.
:param broadcast_socket: Defines the object of the broadcast socket.
Listen-only, responds with the main_socket.
`None` to disable broadcast capabilities.
:param basecls: Provide a basecls of the used protocol
Usage:
>>> resp = ECUResponse(session=range(0,255), security_level=0, responses=UDS() / UDS_NR(negativeResponseCode=0x7f, requestServiceId=0x10)) # noqa: E501
>>> sock = ISOTPSocket(can_iface, sid=0x700, did=0x600, basecls=UDS) # noqa: E501
>>> answering_machine = ECU_am(supported_responses=[resp], main_socket=sock, basecls=UDS) # noqa: E501
>>> sim = threading.Thread(target=answering_machine, kwargs={'count': 4, 'timeout':5}) # noqa: E501
>>> sim.start()
"""
function_name = "ECU_am"
sniff_options_list = ["store", "opened_socket", "count", "filter", "prn", "stop_filter", "timeout"] # noqa: E501
def parse_options(self, supported_responses=None,
main_socket=None, broadcast_socket=None, basecls=Raw,
timeout=None):
self.main_socket = main_socket
self.sockets = [self.main_socket]
if broadcast_socket is not None:
self.sockets.append(broadcast_socket)
self.ecu_state = ECU(logging=False, verbose=False,
store_supported_responses=False)
self.basecls = basecls
self.supported_responses = supported_responses
self.sniff_options["timeout"] = timeout
self.sniff_options["opened_socket"] = self.sockets
def is_request(self, req):
return req.__class__ == self.basecls
def print_reply(self, req, reply):
print("%s ==> %s" % (req.summary(), [res.summary() for res in reply]))
def make_reply(self, req):
if self.supported_responses is not None:
for resp in self.supported_responses:
if not isinstance(resp, ECUResponse):
raise Scapy_Exception("Unsupported type for response. "
"Please use `ECUResponse` objects. ")
if not resp.in_correct_session(self.ecu_state.current_session):
continue
if not resp.has_security_access(
self.ecu_state.current_security_level):
continue
if not resp.answers(req):
continue
for r in resp.responses:
for l in r.layers():
if hasattr(l, "modifies_ecu_state"):
l.modifies_ecu_state(r, self.ecu_state)
return resp.responses
return PacketList([self.basecls(b"\x7f" + bytes(req)[0:1] + b"\x10")])
def send_reply(self, reply):
for p in reply:
if len(reply) > 1:
time.sleep(random.uniform(0.01, 0.5))
self.main_socket.send(p)

View file

@ -0,0 +1,11 @@
# This file is part of Scapy
# See http://www.secdev.org/projects/scapy for more information
# Copyright (C) Nils Weiss <nils@we155.de>
# This program is published under a GPLv2 license
# scapy.contrib.status = skip
"""
Package of contrib automotive gm specific modules
that have to be loaded explicitly.
"""

View file

@ -0,0 +1,836 @@
# This file is part of Scapy
# See http://www.secdev.org/projects/scapy for more information
# Copyright (C) Nils Weiss <nils@we155.de>
# Copyright (C) Enrico Pozzobon <enrico.pozzobon@gmail.com>
# This program is published under a GPLv2 license
# scapy.contrib.description = General Motors Local Area Network (GMLAN)
# scapy.contrib.status = loads
import struct
from scapy.fields import ObservableDict, XByteEnumField, ByteEnumField, \
ConditionalField, XByteField, StrField, XShortEnumField, XShortField, \
X3BytesField, XIntField, ShortField, PacketField, PacketListField, \
FieldListField
from scapy.packet import Packet, bind_layers, NoPayload
from scapy.config import conf
from scapy.error import warning, log_loading
from scapy.contrib.isotp import ISOTP
"""
GMLAN
"""
try:
if conf.contribs['GMLAN']['treat-response-pending-as-answer']:
pass
except KeyError:
log_loading.info("Specify \"conf.contribs['GMLAN'] = "
"{'treat-response-pending-as-answer': True}\" to treat "
"a negative response 'RequestCorrectlyReceived-"
"ResponsePending' as answer of a request. \n"
"The default value is False.")
conf.contribs['GMLAN'] = {'treat-response-pending-as-answer': False}
conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = None
class GMLAN(ISOTP):
@staticmethod
def determine_len(x):
if conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] is None:
warning("Define conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme']! " # noqa: E501
"Assign either 2,3 or 4")
if conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] \
not in [2, 3, 4]:
warning("Define conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme']! " # noqa: E501
"Assign either 2,3 or 4")
return conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] == x
services = ObservableDict(
{0x04: 'ClearDiagnosticInformation',
0x10: 'InitiateDiagnosticOperation',
0x12: 'ReadFailureRecordData',
0x1a: 'ReadDataByIdentifier',
0x20: 'ReturnToNormalOperation',
0x22: 'ReadDataByParameterIdentifier',
0x23: 'ReadMemoryByAddress',
0x27: 'SecurityAccess',
0x28: 'DisableNormalCommunication',
0x2c: 'DynamicallyDefineMessage',
0x2d: 'DefinePIDByAddress',
0x34: 'RequestDownload',
0x36: 'TransferData',
0x3b: 'WriteDataByIdentifier',
0x3e: 'TesterPresent',
0x44: 'ClearDiagnosticInformationPositiveResponse',
0x50: 'InitiateDiagnosticOperationPositiveResponse',
0x52: 'ReadFailureRecordDataPositiveResponse',
0x5a: 'ReadDataByIdentifierPositiveResponse',
0x60: 'ReturnToNormalOperationPositiveResponse',
0x62: 'ReadDataByParameterIdentifierPositiveResponse',
0x63: 'ReadMemoryByAddressPositiveResponse',
0x67: 'SecurityAccessPositiveResponse',
0x68: 'DisableNormalCommunicationPositiveResponse',
0x6c: 'DynamicallyDefineMessagePositiveResponse',
0x6d: 'DefinePIDByAddressPositiveResponse',
0x74: 'RequestDownloadPositiveResponse',
0x76: 'TransferDataPositiveResponse',
0x7b: 'WriteDataByIdentifierPositiveResponse',
0x7e: 'TesterPresentPositiveResponse',
0x7f: 'NegativeResponse',
0xa2: 'ReportProgrammingState',
0xa5: 'ProgrammingMode',
0xa9: 'ReadDiagnosticInformation',
0xaa: 'ReadDataByPacketIdentifier',
0xae: 'DeviceControl',
0xe2: 'ReportProgrammingStatePositiveResponse',
0xe5: 'ProgrammingModePositiveResponse',
0xe9: 'ReadDiagnosticInformationPositiveResponse',
0xea: 'ReadDataByPacketIdentifierPositiveResponse',
0xee: 'DeviceControlPositiveResponse'})
name = 'General Motors Local Area Network'
fields_desc = [
XByteEnumField('service', 0, services)
]
def answers(self, other):
if other.__class__ != self.__class__:
return False
if self.service == 0x7f:
return self.payload.answers(other)
if self.service == (other.service + 0x40):
if isinstance(self.payload, NoPayload) or \
isinstance(other.payload, NoPayload):
return True
else:
return self.payload.answers(other.payload)
return False
def hashret(self):
if self.service == 0x7f:
return struct.pack('B', self.requestServiceId)
return struct.pack('B', self.service & ~0x40)
@staticmethod
def modifies_ecu_state(pkt, ecu):
if pkt.service == 0x50:
ecu.current_session = 3
elif pkt.service == 0x60:
ecu.current_session = 1
ecu.communication_control = 0
ecu.current_security_level = 0
elif pkt.service == 0x68:
ecu.communication_control = 1
elif pkt.service == 0xe5:
ecu.current_session = 2
# ########################IDO###################################
class GMLAN_IDO(Packet):
subfunctions = {
0x02: 'disableAllDTCs',
0x03: 'enableDTCsDuringDevCntrl',
0x04: 'wakeUpLinks'}
name = 'InitiateDiagnosticOperation'
fields_desc = [
ByteEnumField('subfunction', 0, subfunctions)
]
@staticmethod
def get_log(pkt):
return pkt.sprintf("%GMLAN.service%"), \
pkt.sprintf("%GMLAN_IDO.subfunction%")
bind_layers(GMLAN, GMLAN_IDO, service=0x10)
# ########################RFRD###################################
class GMLAN_DTC(Packet):
name = 'GMLAN DTC information'
fields_desc = [
XByteField('failureRecordNumber', 0),
XByteField('DTCHighByte', 0),
XByteField('DTCLowByte', 0),
XByteField('DTCFailureType', 0)
]
def extract_padding(self, p):
return "", p
class GMLAN_RFRD(Packet):
subfunctions = {
0x01: 'readFailureRecordIdentifiers',
0x02: 'readFailureRecordParameters'}
name = 'ReadFailureRecordData'
fields_desc = [
ByteEnumField('subfunction', 0, subfunctions),
ConditionalField(PacketField("dtc", b'', GMLAN_DTC),
lambda pkt: pkt.subfunction == 0x02)
]
@staticmethod
def get_log(pkt):
return pkt.sprintf("%GMLAN.service%"), \
pkt.sprintf("%GMLAN_RFRD.subfunction%")
bind_layers(GMLAN, GMLAN_RFRD, service=0x12)
class GMLAN_RFRDPR(Packet):
name = 'ReadFailureRecordDataPositiveResponse'
fields_desc = [
ByteEnumField('subfunction', 0, GMLAN_RFRD.subfunctions)
]
def answers(self, other):
return other.__class__ == GMLAN_RFRD and \
other.subfunction == self.subfunction
@staticmethod
def get_log(pkt):
return pkt.sprintf("%GMLAN.service%"), \
pkt.sprintf("%GMLAN_RFRDPR.subfunction%")
bind_layers(GMLAN, GMLAN_RFRDPR, service=0x52)
class GMLAN_RFRDPR_RFRI(Packet):
failureRecordDataStructureIdentifiers = {
0x00: "PID",
0x01: "DPID"
}
name = 'ReadFailureRecordDataPositiveResponse_readFailureRecordIdentifiers'
fields_desc = [
ByteEnumField('failureRecordDataStructureIdentifier', 0,
failureRecordDataStructureIdentifiers),
PacketListField("dtcs", [], GMLAN_DTC)
]
bind_layers(GMLAN_RFRDPR, GMLAN_RFRDPR_RFRI, subfunction=0x01)
class GMLAN_RFRDPR_RFRP(Packet):
name = 'ReadFailureRecordDataPositiveResponse_readFailureRecordParameters'
fields_desc = [
PacketField("dtc", b'', GMLAN_DTC)
]
bind_layers(GMLAN_RFRDPR, GMLAN_RFRDPR_RFRP, subfunction=0x02)
# ########################RDBI###################################
class GMLAN_RDBI(Packet):
dataIdentifiers = ObservableDict({
0x90: "$90: VehicleIdentificationNumber (VIN)",
0x92: "$92: SystemSupplierId (SYSSUPPID)",
0x97: "$97: SystemNameOrEngineType (SNOET)",
0x98: "$98: RepairShopCodeOrTesterSerialNumber (RSCOTSN)",
0x99: "$99: ProgrammingDate (PD)",
0x9a: "$9a: DiagnosticDataIdentifier (DDI)",
0x9b: "$9b: XmlConfigurationCompatibilityIdentifier (XMLCCID)",
0x9C: "$9C: XmlDataFilePartNumber (XMLDFPN)",
0x9D: "$9D: XmlDataFileAlphaCode (XMLDFAC)",
0x9F: "$9F: PreviousStoredRepairShopCodeOrTesterSerialNumbers "
"(PSRSCOTSN)",
0xA0: "$A0: manufacturers_enable_counter (MEC)",
0xA1: "$A1: ECUConfigurationOrCustomizationData (ECUCOCGD) 1",
0xA2: "$A2: ECUConfigurationOrCustomizationData (ECUCOCGD) 2",
0xA3: "$A3: ECUConfigurationOrCustomizationData (ECUCOCGD) 3",
0xA4: "$A4: ECUConfigurationOrCustomizationData (ECUCOCGD) 4",
0xA5: "$A5: ECUConfigurationOrCustomizationData (ECUCOCGD) 5",
0xA6: "$A6: ECUConfigurationOrCustomizationData (ECUCOCGD) 6",
0xA7: "$A7: ECUConfigurationOrCustomizationData (ECUCOCGD) 7",
0xA8: "$A8: ECUConfigurationOrCustomizationData (ECUCOCGD) 8",
0xB0: "$B0: ECUDiagnosticAddress (ECUADDR)",
0xB1: "$B1: ECUFunctionalSystemsAndVirtualDevices (ECUFSAVD)",
0xB2: "$B2: GM ManufacturingData (GMMD)",
0xB3: "$B3: Data Universal Numbering System Identification (DUNS)",
0xB4: "$B4: Manufacturing Traceability Characters (MTC)",
0xB5: "$B5: GM BroadcastCode (GMBC)",
0xB6: "$B6: GM Target Vehicle (GMTV)",
0xB7: "$B7: GM Software Usage Description (GMSUD)",
0xB8: "$B8: GM Bench Verification Information (GMBVI)",
0xB9: "$B9: Subnet_Config_List_HighSpeed (SCLHS)",
0xBA: "$BA: Subnet_Config_List_LowSpeed (SCLLS)",
0xBB: "$BB: Subnet_Config_List_MidSpeed (SCLMS)",
0xBC: "$BC: Subnet_Config_List_NonCan 1 (SCLNC 1)",
0xBD: "$BD: Subnet_Config_List_NonCan 2 (SCLNC 2)",
0xBE: "$BE: Subnet_Config_List_LIN (SCLLIN)",
0xBF: "$BF: Subnet_Config_List_GMLANChassisExpansionBus (SCLGCEB)",
0xC0: "$C0: BootSoftwarePartNumber (BSPN)",
0xC1: "$C1: SoftwareModuleIdentifier (SWMI) 01",
0xC2: "$C2: SoftwareModuleIdentifier (SWMI) 02",
0xC3: "$C3: SoftwareModuleIdentifier (SWMI) 03",
0xC4: "$C4: SoftwareModuleIdentifier (SWMI) 04",
0xC5: "$C5: SoftwareModuleIdentifier (SWMI) 05",
0xC6: "$C6: SoftwareModuleIdentifier (SWMI) 06",
0xC7: "$C7: SoftwareModuleIdentifier (SWMI) 07",
0xC8: "$C8: SoftwareModuleIdentifier (SWMI) 08",
0xC9: "$C9: SoftwareModuleIdentifier (SWMI) 09",
0xCA: "$CA: SoftwareModuleIdentifier (SWMI) 10",
0xCB: "$CB: EndModelPartNumber",
0xCC: "$CC: BaseModelPartNumber (BMPN)",
0xD0: "$D0: BootSoftwarePartNumberAlphaCode",
0xD1: "$D1: SoftwareModuleIdentifierAlphaCode (SWMIAC) 01",
0xD2: "$D2: SoftwareModuleIdentifierAlphaCode (SWMIAC) 02",
0xD3: "$D3: SoftwareModuleIdentifierAlphaCode (SWMIAC) 03",
0xD4: "$D4: SoftwareModuleIdentifierAlphaCode (SWMIAC) 04",
0xD5: "$D5: SoftwareModuleIdentifierAlphaCode (SWMIAC) 05",
0xD6: "$D6: SoftwareModuleIdentifierAlphaCode (SWMIAC) 06",
0xD7: "$D7: SoftwareModuleIdentifierAlphaCode (SWMIAC) 07",
0xD8: "$D8: SoftwareModuleIdentifierAlphaCode (SWMIAC) 08",
0xD9: "$D9: SoftwareModuleIdentifierAlphaCode (SWMIAC) 09",
0xDA: "$DA: SoftwareModuleIdentifierAlphaCode (SWMIAC) 10",
0xDB: "$DB: EndModelPartNumberAlphaCode",
0xDC: "$DC: BaseModelPartNumberAlphaCode",
0xDD: "$DD: SoftwareModuleIdentifierDataIdentifiers (SWMIDID)",
0xDE: "$DE: GMLANIdentificationData (GMLANID)",
0xDF: "$DF: ECUOdometerValue (ECUODO)",
0xE0: "$E0: VehicleLevelDataRecord (VLDR) 0",
0xE1: "$E1: VehicleLevelDataRecord (VLDR) 1",
0xE2: "$E2: VehicleLevelDataRecord (VLDR) 2",
0xE3: "$E3: VehicleLevelDataRecord (VLDR) 3",
0xE4: "$E4: VehicleLevelDataRecord (VLDR) 4",
0xE5: "$E5: VehicleLevelDataRecord (VLDR) 5",
0xE6: "$E6: VehicleLevelDataRecord (VLDR) 6",
0xE7: "$E7: VehicleLevelDataRecord (VLDR) 7",
0xE8: "$E8: Subnet_Config_List_GMLANPowertrainExpansionBus (SCLGPEB)",
0xE9: "$E9: Subnet_Config_List_GMLANFrontObjectExpansionBus "
"(SCLGFOEB)",
0xEA: "$EA: Subnet_Config_List_GMLANRearObjectExpansionBus (SCLGROEB)",
0xEB: "$EB: Subnet_Config_List_GMLANExpansionBus1 (SCLGEB1)",
0xEC: "$EC: Subnet_Config_List_GMLANExpansionBus2 (SCLGEB2)",
0xED: "$ED: Subnet_Config_List_GMLANExpansionBus3 (SCLGEB3)",
0xEE: "$EE: Subnet_Config_List_GMLANExpansionBus4 (SCLGEB4)",
0xEF: "$EF: Subnet_Config_List_GMLANExpansionBus5 (SCLGEB5)",
})
name = 'ReadDataByIdentifier'
fields_desc = [
XByteEnumField('dataIdentifier', 0, dataIdentifiers)
]
@staticmethod
def get_log(pkt):
return pkt.sprintf("%GMLAN.service%"), \
pkt.sprintf("%GMLAN_RDBI.dataIdentifier%")
bind_layers(GMLAN, GMLAN_RDBI, service=0x1A)
class GMLAN_RDBIPR(Packet):
name = 'ReadDataByIdentifierPositiveResponse'
fields_desc = [
XByteEnumField('dataIdentifier', 0, GMLAN_RDBI.dataIdentifiers),
]
@staticmethod
def get_log(pkt):
return pkt.sprintf("%GMLAN.service%"), \
(pkt.sprintf("%GMLAN_RDBIPR.dataIdentifier%"),
bytes(pkt[1].payload))
def answers(self, other):
return other.__class__ == GMLAN_RDBI and \
other.dataIdentifier == self.dataIdentifier
bind_layers(GMLAN, GMLAN_RDBIPR, service=0x5A)
# ########################RDBI###################################
class GMLAN_RDBPI(Packet):
dataIdentifiers = ObservableDict({
0x0005: "OBD_EngineCoolantTemperature",
0x000C: "OBD_EngineRPM",
0x001f: "OBD_TimeSinceEngineStart"
})
name = 'ReadDataByParameterIdentifier'
fields_desc = [
FieldListField("identifiers", [],
XShortEnumField('parameterIdentifier', 0,
dataIdentifiers))
]
@staticmethod
def get_log(pkt):
return pkt.sprintf("%GMLAN.service%"), \
pkt.sprintf("%GMLAN_RDBPI.identifiers%")
bind_layers(GMLAN, GMLAN_RDBPI, service=0x22)
class GMLAN_RDBPIPR(Packet):
name = 'ReadDataByParameterIdentifierPositiveResponse'
fields_desc = [
XShortEnumField('parameterIdentifier', 0, GMLAN_RDBPI.dataIdentifiers),
]
@staticmethod
def get_log(pkt):
return pkt.sprintf("%GMLAN.service%"), \
pkt.sprintf("%GMLAN_RDBPIPR.parameterIdentifier%")
def answers(self, other):
return other.__class__ == GMLAN_RDBPI and \
self.parameterIdentifier in other.identifiers
bind_layers(GMLAN, GMLAN_RDBPIPR, service=0x62)
# ########################RDBPKTI###################################
class GMLAN_RDBPKTI(Packet):
name = 'ReadDataByPacketIdentifier'
subfunctions = {
0x00: "stopSending",
0x01: "sendOneResponse",
0x02: "scheduleAtSlowRate",
0x03: "scheduleAtMediumRate",
0x04: "scheduleAtFastRate"
}
fields_desc = [
XByteEnumField('subfunction', 0, subfunctions),
ConditionalField(FieldListField('request_DPIDs', [],
XByteField("", 0)),
lambda pkt: pkt.subfunction > 0x0)
]
@staticmethod
def get_log(pkt):
return pkt.sprintf("%GMLAN.service%"), \
pkt.sprintf("%GMLAN_RDBPKTI.subfunction%")
bind_layers(GMLAN, GMLAN_RDBPKTI, service=0xAA)
# ########################RMBA###################################
class GMLAN_RMBA(Packet):
name = 'ReadMemoryByAddress'
fields_desc = [
ConditionalField(XShortField('memoryAddress', 0),
lambda pkt: GMLAN.determine_len(2)),
ConditionalField(X3BytesField('memoryAddress', 0),
lambda pkt: GMLAN.determine_len(3)),
ConditionalField(XIntField('memoryAddress', 0),
lambda pkt: GMLAN.determine_len(4)),
XShortField('memorySize', 0),
]
@staticmethod
def get_log(pkt):
return pkt.sprintf("%GMLAN.service%"), \
pkt.sprintf("%GMLAN_RMBA.memoryAddress%")
bind_layers(GMLAN, GMLAN_RMBA, service=0x23)
class GMLAN_RMBAPR(Packet):
name = 'ReadMemoryByAddressPositiveResponse'
fields_desc = [
ConditionalField(XShortField('memoryAddress', 0),
lambda pkt: GMLAN.determine_len(2)),
ConditionalField(X3BytesField('memoryAddress', 0),
lambda pkt: GMLAN.determine_len(3)),
ConditionalField(XIntField('memoryAddress', 0),
lambda pkt: GMLAN.determine_len(4)),
StrField('dataRecord', None, fmt="B")
]
def answers(self, other):
return other.__class__ == GMLAN_RMBA and \
other.memoryAddress == self.memoryAddress
@staticmethod
def get_log(pkt):
return pkt.sprintf("%GMLAN.service%"), \
(pkt.sprintf("%GMLAN_RMBAPR.memoryAddress%"), pkt.dataRecord)
bind_layers(GMLAN, GMLAN_RMBAPR, service=0x63)
# ########################SA###################################
class GMLAN_SA(Packet):
subfunctions = {
0: 'ReservedByDocument',
1: 'SPSrequestSeed',
2: 'SPSsendKey',
3: 'DevCtrlrequestSeed',
4: 'DevCtrlsendKey',
255: 'ReservedByDocument'}
for i in range(0x05, 0x0a + 1):
subfunctions[i] = 'ReservedByDocument'
for i in range(0x0b, 0xfa + 1):
subfunctions[i] = 'Reserved for vehicle manufacturer specific needs'
for i in range(0xfb, 0xfe + 1):
subfunctions[i] = 'Reserved for ECU or ' \
'system supplier manufacturing needs'
name = 'SecurityAccess'
fields_desc = [
ByteEnumField('subfunction', 0, subfunctions),
ConditionalField(XShortField('securityKey', B""),
lambda pkt: pkt.subfunction % 2 == 0)
]
@staticmethod
def get_log(pkt):
if pkt.subfunction % 2 == 1:
return pkt.sprintf("%GMLAN.service%"), \
(pkt.subfunction, None)
else:
return pkt.sprintf("%GMLAN.service%"), \
(pkt.subfunction, pkt.securityKey)
bind_layers(GMLAN, GMLAN_SA, service=0x27)
class GMLAN_SAPR(Packet):
name = 'SecurityAccessPositiveResponse'
fields_desc = [
ByteEnumField('subfunction', 0, GMLAN_SA.subfunctions),
ConditionalField(XShortField('securitySeed', B""),
lambda pkt: pkt.subfunction % 2 == 1),
]
def answers(self, other):
return other.__class__ == GMLAN_SA \
and other.subfunction == self.subfunction
@staticmethod
def get_log(pkt):
if pkt.subfunction % 2 == 0:
return pkt.sprintf("%GMLAN.service%"), \
(pkt.subfunction, None)
else:
return pkt.sprintf("%GMLAN.service%"), \
(pkt.subfunction, pkt.securitySeed)
@staticmethod
def modifies_ecu_state(pkt, ecu):
if pkt.subfunction % 2 == 0:
ecu.current_security_level = pkt.subfunction
bind_layers(GMLAN, GMLAN_SAPR, service=0x67)
# ########################DDM###################################
class GMLAN_DDM(Packet):
name = 'DynamicallyDefineMessage'
fields_desc = [
XByteField('DPIDIdentifier', 0),
StrField('PIDData', b'\x00\x00')
]
@staticmethod
def get_log(pkt):
return pkt.sprintf("%GMLAN.service%"), \
(pkt.sprintf("%GMLAN_DDM.DPIDIdentifier%"), pkt.PIDData)
bind_layers(GMLAN, GMLAN_DDM, service=0x2C)
class GMLAN_DDMPR(Packet):
name = 'DynamicallyDefineMessagePositiveResponse'
fields_desc = [
XByteField('DPIDIdentifier', 0)
]
@staticmethod
def get_log(pkt):
return pkt.sprintf("%GMLAN.service%"), \
pkt.sprintf("%GMLAN_DDMPR.DPIDIdentifier%")
def answers(self, other):
return other.__class__ == GMLAN_DDM \
and other.DPIDIdentifier == self.DPIDIdentifier
bind_layers(GMLAN, GMLAN_DDMPR, service=0x6C)
# ########################DPBA###################################
class GMLAN_DPBA(Packet):
name = 'DefinePIDByAddress'
fields_desc = [
XShortField('parameterIdentifier', 0),
ConditionalField(XShortField('memoryAddress', 0),
lambda pkt: GMLAN.determine_len(2)),
ConditionalField(X3BytesField('memoryAddress', 0),
lambda pkt: GMLAN.determine_len(3)),
ConditionalField(XIntField('memoryAddress', 0),
lambda pkt: GMLAN.determine_len(4)),
XByteField('memorySize', 0),
]
@staticmethod
def get_log(pkt):
return pkt.sprintf("%GMLAN.service%"), \
(pkt.parameterIdentifier, pkt.memoryAddress, pkt.memorySize)
bind_layers(GMLAN, GMLAN_DPBA, service=0x2D)
class GMLAN_DPBAPR(Packet):
name = 'DefinePIDByAddressPositiveResponse'
fields_desc = [
XShortField('parameterIdentifier', 0),
]
@staticmethod
def get_log(pkt):
return pkt.sprintf("%GMLAN.service%"), pkt.parameterIdentifier
def answers(self, other):
return other.__class__ == GMLAN_DPBA \
and other.parameterIdentifier == self.parameterIdentifier
bind_layers(GMLAN, GMLAN_DPBA, service=0x6D)
# ########################RD###################################
class GMLAN_RD(Packet):
name = 'RequestDownload'
fields_desc = [
XByteField('dataFormatIdentifier', 0),
ConditionalField(XShortField('memorySize', 0),
lambda pkt: GMLAN.determine_len(2)),
ConditionalField(X3BytesField('memorySize', 0),
lambda pkt: GMLAN.determine_len(3)),
ConditionalField(XIntField('memorySize', 0),
lambda pkt: GMLAN.determine_len(4)),
]
@staticmethod
def get_log(pkt):
return pkt.sprintf("%GMLAN.service%"), \
(pkt.dataFormatIdentifier, pkt.memorySize)
bind_layers(GMLAN, GMLAN_RD, service=0x34)
# ########################TD###################################
class GMLAN_TD(Packet):
subfunctions = {
0x00: "download",
0x80: "downloadAndExecuteOrExecute"
}
name = 'TransferData'
fields_desc = [
ByteEnumField('subfunction', 0, subfunctions),
ConditionalField(XShortField('startingAddress', 0),
lambda pkt: GMLAN.determine_len(2)),
ConditionalField(X3BytesField('startingAddress', 0),
lambda pkt: GMLAN.determine_len(3)),
ConditionalField(XIntField('startingAddress', 0),
lambda pkt: GMLAN.determine_len(4)),
StrField("dataRecord", None)
]
@staticmethod
def get_log(pkt):
return pkt.sprintf("%GMLAN.service%"), \
(pkt.sprintf("%GMLAN_TD.subfunction%"), pkt.startingAddress,
pkt.dataRecord)
bind_layers(GMLAN, GMLAN_TD, service=0x36)
# ########################WDBI###################################
class GMLAN_WDBI(Packet):
name = 'WriteDataByIdentifier'
fields_desc = [
XByteEnumField('dataIdentifier', 0, GMLAN_RDBI.dataIdentifiers),
StrField("dataRecord", b'\x00')
]
@staticmethod
def get_log(pkt):
return pkt.sprintf("%GMLAN.service%"), \
(pkt.sprintf("%GMLAN_WDBI.dataIdentifier%"), pkt.dataRecord)
bind_layers(GMLAN, GMLAN_WDBI, service=0x3B)
class GMLAN_WDBIPR(Packet):
name = 'WriteDataByIdentifierPositiveResponse'
fields_desc = [
XByteEnumField('dataIdentifier', 0, GMLAN_RDBI.dataIdentifiers)
]
@staticmethod
def get_log(pkt):
return pkt.sprintf("%GMLAN.service%"), \
pkt.sprintf("%GMLAN_WDBIPR.dataIdentifier%")
def answers(self, other):
return other.__class__ == GMLAN_WDBI \
and other.dataIdentifier == self.dataIdentifier
bind_layers(GMLAN, GMLAN_WDBIPR, service=0x7B)
# ########################RPSPR###################################
class GMLAN_RPSPR(Packet):
programmedStates = {
0x00: "fully programmed",
0x01: "no op s/w or cal data",
0x02: "op s/w present, cal data missing",
0x03: "s/w present, default or no start cal present",
0x50: "General Memory Fault",
0x51: "RAM Memory Fault",
0x52: "NVRAM Memory Fault",
0x53: "Boot Memory Failure",
0x54: "Flash Memory Failure",
0x55: "EEPROM Memory Failure",
}
name = 'ReportProgrammedStatePositiveResponse'
fields_desc = [
ByteEnumField('programmedState', 0, programmedStates),
]
@staticmethod
def get_log(pkt):
return pkt.sprintf("%GMLAN.service%"), \
pkt.sprintf("%GMLAN_RPSPR.programmedState%")
bind_layers(GMLAN, GMLAN_RPSPR, service=0xE2)
# ########################PM###################################
class GMLAN_PM(Packet):
subfunctions = {
0x01: "requestProgrammingMode",
0x02: "requestProgrammingMode_HighSpeed",
0x03: "enableProgrammingMode"
}
name = 'ProgrammingMode'
fields_desc = [
ByteEnumField('subfunction', 0, subfunctions),
]
@staticmethod
def get_log(pkt):
return pkt.sprintf("%GMLAN.service%"), \
pkt.sprintf("%GMLAN_PM.subfunction%")
bind_layers(GMLAN, GMLAN_PM, service=0xA5)
# ########################RDI###################################
class GMLAN_RDI(Packet):
subfunctions = {
0x80: 'readStatusOfDTCByDTCNumber',
0x81: 'readStatusOfDTCByStatusMask',
0x82: 'sendOnChangeDTCCount'
}
name = 'ReadDiagnosticInformation'
fields_desc = [
ByteEnumField('subfunction', 0, subfunctions)
]
@staticmethod
def get_log(pkt):
return pkt.sprintf("%GMLAN.service%"), \
pkt.sprintf("%GMLAN_RDI.subfunction%")
bind_layers(GMLAN, GMLAN_RDI, service=0xA9)
class GMLAN_RDI_BN(Packet):
name = 'ReadStatusOfDTCByDTCNumber'
fields_desc = [
XByteField('DTCHighByte', 0),
XByteField('DTCLowByte', 0),
XByteField('DTCFailureType', 0),
]
bind_layers(GMLAN_RDI, GMLAN_RDI_BN, subfunction=0x80)
class GMLAN_RDI_BM(Packet):
name = 'ReadStatusOfDTCByStatusMask'
fields_desc = [
XByteField('DTCStatusMask', 0),
]
bind_layers(GMLAN_RDI, GMLAN_RDI_BM, subfunction=0x81)
class GMLAN_RDI_BC(Packet):
name = 'SendOnChangeDTCCount'
fields_desc = [
XByteField('DTCStatusMask', 0),
]
bind_layers(GMLAN_RDI, GMLAN_RDI_BC, subfunction=0x82)
# TODO:This function receive single frame responses... (Implement GMLAN Socket)
# ########################NRC###################################
class GMLAN_NR(Packet):
negativeResponseCodes = {
0x11: 'ServiceNotSupported',
0x12: 'SubFunctionNotSupported',
0x22: 'ConditionsNotCorrectOrRequestSequenceError',
0x31: 'RequestOutOfRange',
0x35: 'InvalidKey',
0x36: 'ExceedNumberOfAttempts',
0x37: 'RequiredTimeDelayNotExpired',
0x78: 'RequestCorrectlyReceived-ResponsePending',
0x81: 'SchedulerFull',
0x83: 'VoltageOutOfRange',
0x85: 'GeneralProgrammingFailure',
0x89: 'DeviceTypeError',
0x99: 'ReadyForDownload-DTCStored',
0xe3: 'DeviceControlLimitsExceeded',
}
name = 'NegativeResponse'
fields_desc = [
XByteEnumField('requestServiceId', 0, GMLAN.services),
ByteEnumField('returnCode', 0, negativeResponseCodes),
ShortField('deviceControlLimitExceeded', 0)
]
@staticmethod
def get_log(pkt):
return pkt.sprintf("%GMLAN.service%"), \
(pkt.sprintf("%GMLAN_NR.requestServiceId%"),
pkt.sprintf("%GMLAN_NR.returnCode%"))
def answers(self, other):
return self.requestServiceId == other.service and \
(self.returnCode != 0x78 or
conf.contribs['GMLAN']['treat-response-pending-as-answer'])
bind_layers(GMLAN, GMLAN_NR, service=0x7f)

View file

@ -0,0 +1,403 @@
# gmlan unit tests
#
# Type the following command to launch start the tests:
# $ sudo bash test/run_tests -t test/gmlan.uts -F
% gmlan unit tests
+ Configuration of scapy
= Load gmlan layer
~ conf command
load_contrib('automotive.gm.gmlan')
+ Basic Packet Tests()
= Set GMLAN ECU AddressingScheme
conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 2
assert conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] == 2
= Craft Packet
x = GMLAN(b'\x52\x02\x01\x16\x71\x00\x00\x0c\xaa\xbb')
x.load == b'\x00\x0c\xaa\xbb'
x.service == 0x52
= Craft VIN Packet
x = GMLAN(b'\x5a\x90'+ raw("WOOOJBF35W1042000"))
x.load == b'WOOOJBF35W1042000'
x.dataIdentifier == 0x90
= Test Packet with ECU AddressingScheme2
x = GMLAN()/GMLAN_RMBA(b'\x11\x22\x44\x22')
x.memoryAddress == 0x1122
x.memorySize == 0x4422
= Test Packet GMLAN_RMBAPR with ECU AddressingScheme2
x = GMLAN()/GMLAN_RMBAPR(b'\x11\x22\x44\x22')
x.memoryAddress == 0x1122
x.dataRecord == b'\x44\x22'
= Craft Packet with ECU AddressingScheme2
x = GMLAN() / GMLAN_RMBA(b'\x11\x22\x44\x22')
y = GMLAN()/GMLAN_RMBA(memoryAddress=0x1122, memorySize=0x4422)
bytes(x) == bytes(y)
= Test Packet with ECU AddressingScheme3
conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 3
x = GMLAN()/GMLAN_RMBA(b'\x11\x22\x44\x22\x11')
x.memoryAddress == 0x112244
x.memorySize == 0x2211
= Test Packet GMLAN_RMBAPR with ECU AddressingScheme3
x = GMLAN()/GMLAN_RMBAPR(b'\x11\x22\x44\x22\x11')
x.memoryAddress == 0x112244
x.dataRecord == b'\x22\x11'
= Craft Packet with ECU AddressingScheme3
x = GMLAN() / GMLAN_RMBA(b'\x11\x22\x44\x22\x11')
y = GMLAN()/GMLAN_RMBA(memoryAddress=0x112244, memorySize=0x2211)
bytes(x) == bytes(y)
= Test Packet with ECU AddressingScheme4
conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 4
x = GMLAN()/GMLAN_RMBA(b'\x11\x22\x44\x22\x11\x00')
x.memoryAddress == 0x11224422
x.memorySize == 0x1100
= Test Packet GMLAN_RMBAPR with ECU AddressingScheme4
x = GMLAN()/GMLAN_RMBAPR(b'\x11\x22\x44\x22\x11\x00')
x.memoryAddress == 0x11224422
x.dataRecord == b'\x11\x00'
= Craft Packet with ECU AddressingScheme4
x = GMLAN() / GMLAN_RMBA(b'\x11\x22\x44\x22\x11\x00')
y = GMLAN()/GMLAN_RMBA(memoryAddress=0x11224422, memorySize=0x1100)
bytes(x) == bytes(y)
= Craft Packet for RequestDownload2
conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 2
x = GMLAN(b'\x34\x12\x08\x15')
x.service == 0x34
x.dataFormatIdentifier == 0x12
x.memorySize == 0x815
y = GMLAN()/GMLAN_RD(dataFormatIdentifier=0x12, memorySize=0x815)
bytes(y) == bytes(x)
= Craft Packet for RequestDownload3
conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 3
x = GMLAN(b'\x34\x12\x08\x15\x00')
x.service == 0x34
x.dataFormatIdentifier == 0x12
x.memorySize == 0x81500
y = GMLAN()/GMLAN_RD(dataFormatIdentifier=0x12, memorySize=0x81500)
bytes(y) == bytes(x)
= Craft Packet for RequestDownload4
conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 4
x = GMLAN(b'\x34\x12\x08\x15\x00\x11')
x.service == 0x34
x.dataFormatIdentifier == 0x12
x.memorySize == 0x8150011
= Craft Packet for RFRD1
x = GMLAN(b'\x12\x01')
x.service == 0x12
x.subfunction == 1
= Craft Packet for RFRD2
x = GMLAN(b'\x12\x02\x01\x02\x03\x04')
x.service == 0x12
x.subfunction == 2
x.dtc.failureRecordNumber == 1
x.dtc.DTCHighByte == 2
x.dtc.DTCLowByte == 3
x.dtc.DTCFailureType == 4
= Craft Packet for RFRDPR_RFRI
x = GMLAN(b'\x52\x01\x00\x01\x02\x03\x04')
x.service == 0x52
x.subfunction == 1
x.failureRecordDataStructureIdentifier == 0
x.dtcs[0].failureRecordNumber == 1
x.dtcs[0].DTCHighByte == 2
x.dtcs[0].DTCLowByte == 3
x.dtcs[0].DTCFailureType == 4
= Craft Packet for RFRDPR_RFRI
x = GMLAN(b'\x52\x01\x00\x01\x02\x03\x04\x01\x02\x03\x04\x01\x02\x03\x04\x01\x02\x03\x04')
x.service == 0x52
x.subfunction == 1
x.failureRecordDataStructureIdentifier == 0
x.dtcs[0].failureRecordNumber == 1
x.dtcs[0].DTCHighByte == 2
x.dtcs[0].DTCLowByte == 3
x.dtcs[0].DTCFailureType == 4
x.dtcs[1].failureRecordNumber == 1
x.dtcs[1].DTCHighByte == 2
x.dtcs[1].DTCLowByte == 3
x.dtcs[1].DTCFailureType == 4
x.dtcs[2].failureRecordNumber == 1
x.dtcs[2].DTCHighByte == 2
x.dtcs[2].DTCLowByte == 3
x.dtcs[2].DTCFailureType == 4
x.dtcs[3].failureRecordNumber == 1
x.dtcs[3].DTCHighByte == 2
x.dtcs[3].DTCLowByte == 3
x.dtcs[3].DTCFailureType == 4
= Craft Packet for RFRDPR_RFRP
x = GMLAN(b'\x52\x02\x01\x02\x03\x04deadbeef')
x.service == 0x52
x.subfunction == 2
x.dtc.failureRecordNumber == 1
x.dtc.DTCHighByte == 2
x.dtc.DTCLowByte == 3
x.dtc.DTCFailureType == 4
x.show()
x.load == b'deadbeef'
= Craft Packet for RDBI
x = GMLAN(b'\x1A\x11')
x.service == 0x1A
x.dataIdentifier == 0x11
= Craft Packet for RDBIPR
x = GMLAN(b'\x5A\x11deadbeef')
x.service == 0x5A
x.dataIdentifier == 0x11
x.load == b'deadbeef'
= Craft Packet for RDBPI
x = GMLAN(b'\x22\x11\x11\x22\x22\x33\x33\x44\x44\x55\x55\x66\x66\x77\x77\x88\x88\x99\x99')
x.service == 0x22
x.identifiers[0] == 0x1111
x.identifiers[1] == 0x2222
x.identifiers[2] == 0x3333
x.identifiers[3] == 0x4444
x.identifiers[4] == 0x5555
x.identifiers[5] == 0x6666
x.identifiers[6] == 0x7777
x.identifiers[7] == 0x8888
x.identifiers[8] == 0x9999
= Craft Packet for RDBPIPR
x = GMLAN(b'\x62\x00\x11deadbeef')
x.service == 0x62
x.parameterIdentifier == 0x11
x.load == b'deadbeef'
= Craft Packet for GMLAN_RDBPKTI1
x = GMLAN(b'\xAA\x01deadbeef')
x.service == 0xAA
x.subfunction == 0x01
x.request_DPIDs == [0x64, 0x65, 0x61, 0x64, 0x62, 0x65, 0x65, 0x66]
= Craft Packet for GMLAN_RDBPKTI3
x = GMLAN(b'\xAA\x02deadbeef')
x.service == 0xAA
x.subfunction == 0x02
x.request_DPIDs == [0x64, 0x65, 0x61, 0x64, 0x62, 0x65, 0x65, 0x66]
= Craft Packet for GMLAN_RDBPKTI4
x = GMLAN(b'\xAA\x03deadbeef')
x.service == 0xAA
x.subfunction == 0x03
x.request_DPIDs == [0x64, 0x65, 0x61, 0x64, 0x62, 0x65, 0x65, 0x66]
= Craft Packet for GMLAN_RDBPKTI2
x = GMLAN(b'\xAA\x00')
x.service == 0xAA
x.subfunction == 0
= Build GMLAN_RDBPKTI1
x = GMLAN()/GMLAN_RDBPKTI(subfunction=1, request_DPIDs=[0x64, 0x65])
assert b"\xaa\x01de" == bytes(x)
= Craft Packet for GMLAN_SA1
x = GMLAN(b'\x27\x01')
x.service == 0x27
x.subfunction == 1
= Craft Packet for GMLAN_SA2
x = GMLAN(b'\x27\x02\xde\xad')
x.service == 0x27
x.subfunction == 2
x.securityKey == 0xdead
= Craft Packet for GMLAN_SAPR1
x = GMLAN(b'\x67\x02')
x.service == 0x67
x.subfunction == 2
= Craft Packet for GMLAN_SAPR2
x = GMLAN(b'\x67\x01\xde\xad')
x.service == 0x67
x.subfunction == 1
x.securitySeed == 0xdead
= Craft Packet for GMLAN_DDM
x = GMLAN(b'\x2c\x02dead')
x.service == 0x2c
x.DPIDIdentifier == 2
x.PIDData == b'dead'
= Craft Packet for GMLAN_DDMPR
x = GMLAN(b'\x6c\x02dead')
x.service == 0x6c
x.DPIDIdentifier == 2
= Craft Packet for GMLAN_DPBA1
conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 2
x = GMLAN(b'\x2D\x02\x02\x11\x11\x33')
x.service == 0x2d
x.parameterIdentifier == 0x202
x.memoryAddress == 0x1111
x.memorySize == 0x33
= Craft Packet for GMLAN_DPBA2
conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 3
x = GMLAN(b'\x2D\x02\x02\x11\x11\x11\x33')
x.service == 0x2d
x.parameterIdentifier == 0x202
x.memoryAddress == 0x111111
x.memorySize == 0x33
= Craft Packet for GMLAN_DPBA3
conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 4
x = GMLAN(b'\x2D\x02\x02\x11\x11\x11\x11\x33')
x.service == 0x2d
x.parameterIdentifier == 0x202
x.memoryAddress == 0x11111111
x.memorySize == 0x33
= Craft Packet for GMLAN_DPBAPR
x = GMLAN(b'\x6D\x02\x02')
x.service == 0x6d
x.parameterIdentifier == 0x202
= Craft Packet for GMLAN_RD1
conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 2
x = GMLAN(b'\x34\x02\x11\x11')
x.service == 0x34
x.dataFormatIdentifier == 0x2
x.memorySize == 0x1111
= Craft Packet for GMLAN_RD2
conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 3
x = GMLAN(b'\x34\x02\x11\x11\x11')
x.service == 0x34
x.dataFormatIdentifier == 0x2
x.memorySize == 0x111111
= Craft Packet for GMLAN_RD3
conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 4
x = GMLAN(b'\x34\x02\x11\x11\x11\x11')
x.service == 0x34
x.dataFormatIdentifier == 0x2
x.memorySize == 0x11111111
= Craft Packet for GMLAN_TD1
conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 2
x = GMLAN(b'\x36\x02\x11\x11dead')
x.service == 0x36
x.subfunction == 0x2
x.startingAddress == 0x1111
x.dataRecord == b'dead'
= Craft Packet for GMLAN_TD2
conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 3
x = GMLAN(b'\x36\x02\x11\x11\x11dead')
x.service == 0x36
x.subfunction == 0x2
x.startingAddress == 0x111111
x.dataRecord == b'dead'
= Craft Packet for GMLAN_TD3
conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 4
x = GMLAN(b'\x36\x02\x11\x11\x11\x11dead')
x.service == 0x36
x.subfunction == 0x2
x.startingAddress == 0x11111111
x.dataRecord == b'dead'
= Craft Packet for WDBI
x = GMLAN(b'\x3b\x11deadbeef')
x.service == 0x3b
x.dataIdentifier == 0x11
x.dataRecord == b'deadbeef'
= Craft Packet for WDBIPR
x = GMLAN(b'\x7b\x11')
x.service == 0x7b
x.dataIdentifier == 0x11
= Craft Packet for RPSPR
x = GMLAN(b'\xe2\x11')
x.service == 0xe2
x.programmedState == 0x11
= Craft Packet for PM
x = GMLAN(b'\xA5\x11')
x.service == 0xA5
x.subfunction == 0x11
= Craft Packet for RDI
x = GMLAN(b'\xA9\x11')
x.service == 0xA9
x.subfunction == 0x11
= Craft Packet for RDI_BN
x = GMLAN(b'\xA9\x80\x11\x22\x33')
x.service == 0xA9
x.subfunction == 0x80
x.DTCHighByte == 0x11
x.DTCLowByte == 0x22
x.DTCFailureType == 0x33
= Craft Packet for RDI_BM1
x = GMLAN(b'\xA9\x81\x11')
x.service == 0xA9
x.subfunction == 0x81
x.DTCStatusMask == 0x11
= Craft Packet for RDI_BM2
x = GMLAN(b'\xA9\x82\x11')
x.service == 0xA9
x.subfunction == 0x82
x.DTCStatusMask == 0x11
= Craft Packet for NR
x = GMLAN(b'\x7f\x11\x00\x11\x22')
x.service == 0x7f
x.requestServiceId == 0x11
x.returnCode == 0
x.deviceControlLimitExceeded == 0x1122
= Check not answers
y = GMLAN(b'\x11deadbeef')
x = GMLAN(b'\x7f\x10\x00\x11\x22')
assert not x.answers(y)
= Check answers 1
y = GMLAN(b'\x10deadbeef')
x = GMLAN(b'\x7f\x10\x00\x11\x22')
assert x.answers(y)
= Check hashret 1
print(y.hashret())
print(x.hashret())
y.hashret() == x.hashret()
= Check answers 2
y = GMLAN()/GMLAN_SA(subfunction=1)
x = GMLAN()/GMLAN_SAPR()
assert x.answers(y)
= Check hashret 2
y.hashret() == x.hashret()

View file

@ -0,0 +1,339 @@
#! /usr/bin/env python
# This file is part of Scapy
# See http://www.secdev.org/projects/scapy for more information
# Copyright (C) Markus Schroetter <project.m.schroetter@gmail.com>
# Copyright (C) Nils Weiss <nils@we155.de>
# This program is published under a GPLv2 license
# scapy.contrib.description = GMLAN Utilities
# scapy.contrib.status = loads
import time
from scapy.contrib.automotive.gm.gmlan import GMLAN, GMLAN_SA, GMLAN_RD, \
GMLAN_TD, GMLAN_PM, GMLAN_RMBA
from scapy.config import conf
from scapy.contrib.isotp import ISOTPSocket
from scapy.error import warning, log_loading
from scapy.utils import PeriodicSenderThread
__all__ = ["GMLAN_TesterPresentSender", "GMLAN_InitDiagnostics",
"GMLAN_GetSecurityAccess", "GMLAN_RequestDownload",
"GMLAN_TransferData", "GMLAN_TransferPayload",
"GMLAN_ReadMemoryByAddress", "GMLAN_BroadcastSocket"]
log_loading.info("\"conf.contribs['GMLAN']"
"['treat-response-pending-as-answer']\" set to True). This "
"is required by the GMLAN-Utils module to operate "
"correctly.")
try:
conf.contribs['GMLAN']['treat-response-pending-as-answer'] = False
except KeyError:
conf.contribs['GMLAN'] = {'treat-response-pending-as-answer': False}
class GMLAN_TesterPresentSender(PeriodicSenderThread):
def __init__(self, sock, pkt=GMLAN(service="TesterPresent"), interval=2):
""" Thread to send TesterPresent messages packets periodically
Args:
sock: socket where packet is sent periodically
pkt: packet to send
interval: interval between two packets
"""
PeriodicSenderThread.__init__(self, sock, pkt, interval)
def _check_response(resp, verbose):
if resp is None:
if verbose:
print("Timeout.")
return False
if verbose:
resp.show()
return resp.sprintf("%GMLAN.service%") != "NegativeResponse"
def _send_and_check_response(sock, req, timeout, verbose):
if verbose:
print("Sending %s" % repr(req))
resp = sock.sr1(req, timeout=timeout, verbose=0)
return _check_response(resp, verbose)
def GMLAN_InitDiagnostics(sock, broadcastsocket=None, timeout=None,
verbose=None, retry=0):
"""Send messages to put an ECU into an diagnostic/programming state.
Args:
sock: socket to send the message on.
broadcast: socket for broadcasting. If provided some message will be
sent as broadcast. Recommended when used on a network with
several ECUs.
timeout: timeout for sending, receiving or sniffing packages.
verbose: set verbosity level
retry: number of retries in case of failure.
Returns true on success.
"""
if verbose is None:
verbose = conf.verb
retry = abs(retry)
while retry >= 0:
retry -= 1
# DisableNormalCommunication
p = GMLAN(service="DisableNormalCommunication")
if broadcastsocket is None:
if not _send_and_check_response(sock, p, timeout, verbose):
continue
else:
if verbose:
print("Sending %s as broadcast" % repr(p))
broadcastsocket.send(p)
time.sleep(0.05)
# ReportProgrammedState
p = GMLAN(service="ReportProgrammingState")
if not _send_and_check_response(sock, p, timeout, verbose):
continue
# ProgrammingMode requestProgramming
p = GMLAN() / GMLAN_PM(subfunction="requestProgrammingMode")
if not _send_and_check_response(sock, p, timeout, verbose):
continue
time.sleep(0.05)
# InitiateProgramming enableProgramming
# No response expected
p = GMLAN() / GMLAN_PM(subfunction="enableProgrammingMode")
if verbose:
print("Sending %s" % repr(p))
sock.send(p)
time.sleep(0.05)
return True
return False
def GMLAN_GetSecurityAccess(sock, keyFunction, level=1, timeout=None,
verbose=None, retry=0):
"""Authenticate on ECU. Implements Seey-Key procedure.
Args:
sock: socket to send the message on.
keyFunction: function implementing the key algorithm.
level: level of access
timeout: timeout for sending, receiving or sniffing packages.
verbose: set verbosity level
retry: number of retries in case of failure.
Returns true on success.
"""
if verbose is None:
verbose = conf.verb
retry = abs(retry)
if level % 2 == 0:
warning("Parameter Error: Level must be an odd number.")
return False
while retry >= 0:
retry -= 1
request = GMLAN() / GMLAN_SA(subfunction=level)
if verbose:
print("Requesting seed..")
resp = sock.sr1(request, timeout=timeout, verbose=0)
if not _check_response(resp, verbose):
if verbose:
print("Negative Response.")
continue
seed = resp.securitySeed
if seed == 0:
if verbose:
print("ECU security already unlocked. (seed is 0x0000)")
return True
keypkt = GMLAN() / GMLAN_SA(subfunction=level + 1,
securityKey=keyFunction(seed))
if verbose:
print("Responding with key..")
resp = sock.sr1(keypkt, timeout=timeout, verbose=0)
if resp is None:
if verbose:
print("Timeout.")
continue
if verbose:
resp.show()
if resp.sprintf("%GMLAN.service%") == "SecurityAccessPositiveResponse": # noqa: E501
if verbose:
print("SecurityAccess granted.")
return True
# Invalid Key
elif resp.sprintf("%GMLAN.service%") == "NegativeResponse" and \
resp.sprintf("%GMLAN.returnCode%") == "InvalidKey":
if verbose:
print("Key invalid")
continue
return False
def GMLAN_RequestDownload(sock, length, timeout=None, verbose=None, retry=0):
"""Send RequestDownload message.
Usually used before calling TransferData.
Args:
sock: socket to send the message on.
length: value for the message's parameter 'unCompressedMemorySize'.
timeout: timeout for sending, receiving or sniffing packages.
verbose: set verbosity level.
retry: number of retries in case of failure.
Returns true on success.
"""
if verbose is None:
verbose = conf.verb
retry = abs(retry)
while retry >= 0:
# RequestDownload
pkt = GMLAN() / GMLAN_RD(memorySize=length)
resp = sock.sr1(pkt, timeout=timeout, verbose=0)
if _check_response(resp, verbose):
return True
retry -= 1
if retry >= 0 and verbose:
print("Retrying..")
return False
def GMLAN_TransferData(sock, addr, payload, maxmsglen=None, timeout=None,
verbose=None, retry=0):
"""Send TransferData message.
Usually used after calling RequestDownload.
Args:
sock: socket to send the message on.
addr: destination memory address on the ECU.
payload: data to be sent.
maxmsglen: maximum length of a single iso-tp message. (default:
maximum length)
timeout: timeout for sending, receiving or sniffing packages.
verbose: set verbosity level.
retry: number of retries in case of failure.
Returns true on success.
"""
if verbose is None:
verbose = conf.verb
retry = abs(retry)
startretry = retry
scheme = conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme']
if addr < 0 or addr >= 2**(8 * scheme):
warning("Error: Invalid address " + hex(addr) + " for scheme " +
str(scheme))
return False
# max size of dataRecord according to gmlan protocol
if maxmsglen is None or maxmsglen <= 0 or maxmsglen > (4093 - scheme):
maxmsglen = (4093 - scheme)
for i in range(0, len(payload), maxmsglen):
retry = startretry
while True:
if len(payload[i:]) > maxmsglen:
transdata = payload[i:i + maxmsglen]
else:
transdata = payload[i:]
pkt = GMLAN() / GMLAN_TD(startingAddress=addr + i,
dataRecord=transdata)
resp = sock.sr1(pkt, timeout=timeout, verbose=0)
if _check_response(resp, verbose):
break
retry -= 1
if retry >= 0:
if verbose:
print("Retrying..")
else:
return False
return True
def GMLAN_TransferPayload(sock, addr, payload, maxmsglen=None, timeout=None,
verbose=None, retry=0):
"""Send data by using GMLAN services.
Args:
sock: socket to send the data on.
addr: destination memory address on the ECU.
payload: data to be sent.
maxmsglen: maximum length of a single iso-tp message. (default:
maximum length)
timeout: timeout for sending, receiving or sniffing packages.
verbose: set verbosity level.
retry: number of retries in case of failure.
Returns true on success.
"""
if not GMLAN_RequestDownload(sock, len(payload), timeout=timeout,
verbose=verbose, retry=retry):
return False
if not GMLAN_TransferData(sock, addr, payload, maxmsglen=maxmsglen,
timeout=timeout, verbose=verbose, retry=retry):
return False
return True
def GMLAN_ReadMemoryByAddress(sock, addr, length, timeout=None,
verbose=None, retry=0):
"""Read data from ECU memory.
Args:
sock: socket to send the data on.
addr: source memory address on the ECU.
length: bytes to read
timeout: timeout for sending, receiving or sniffing packages.
verbose: set verbosity level.
retry: number of retries in case of failure.
Returns the bytes read.
"""
if verbose is None:
verbose = conf.verb
retry = abs(retry)
scheme = conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme']
if addr < 0 or addr >= 2**(8 * scheme):
warning("Error: Invalid address " + hex(addr) + " for scheme " +
str(scheme))
return None
# max size of dataRecord according to gmlan protocol
if length <= 0 or length > (4094 - scheme):
warning("Error: Invalid length " + hex(length) + " for scheme " +
str(scheme) + ". Choose between 0x1 and " + hex(4094 - scheme))
return None
while retry >= 0:
# RequestDownload
pkt = GMLAN() / GMLAN_RMBA(memoryAddress=addr, memorySize=length)
resp = sock.sr1(pkt, timeout=timeout, verbose=0)
if _check_response(resp, verbose):
return resp.dataRecord
retry -= 1
if retry >= 0 and verbose:
print("Retrying..")
return None
def GMLAN_BroadcastSocket(interface):
"""Returns a GMLAN broadcast socket using interface."""
return ISOTPSocket(interface, sid=0x101, did=0x0, basecls=GMLAN,
extended_addr=0xfe)

View file

@ -0,0 +1,12 @@
# This file is part of Scapy
# See http://www.secdev.org/projects/scapy for more information
# Copyright (C) Andreas Korb <andreas.d.korb@gmail.com>
# Copyright (C) Nils Weiss <nils@we155.de>
# This program is published under a GPLv2 license
# scapy.contrib.status = skip
"""
Package of contrib automotive obd specific modules
that have to be loaded explicitly.
"""

View file

@ -0,0 +1,12 @@
# This file is part of Scapy
# See http://www.secdev.org/projects/scapy for more information
# Copyright (C) Andreas Korb <andreas.d.korb@gmail.com>
# Copyright (C) Nils Weiss <nils@we155.de>
# This program is published under a GPLv2 license
# scapy.contrib.status = skip
"""
Package of contrib automotive obd specific modules
that have to be loaded explicitly.
"""

View file

@ -0,0 +1,177 @@
# This file is part of Scapy
# See http://www.secdev.org/projects/scapy for more information
# Copyright (C) Andreas Korb <andreas.d.korb@gmail.com>
# Copyright (C) Nils Weiss <nils@we155.de>
# This program is published under a GPLv2 license
# scapy.contrib.status = skip
from scapy.fields import FieldLenField, FieldListField, StrFixedLenField, \
ByteField, ShortField, FlagsField, XByteField, PacketListField
from scapy.packet import Packet, bind_layers
from scapy.contrib.automotive.obd.packet import OBD_Packet
from scapy.contrib.automotive.obd.services import OBD_S09
# See https://en.wikipedia.org/wiki/OBD-II_PIDs#Service_09
# for further information
# IID = Information IDentification
class OBD_S09_PR_Record(Packet):
fields_desc = [
XByteField("iid", 0),
]
class OBD_S09_PR(Packet):
name = "Infotype IDs"
fields_desc = [
PacketListField("data_records", [], OBD_S09_PR_Record)
]
def answers(self, other):
return other.__class__ == OBD_S09 \
and all(r.iid in other.iid for r in self.data_records)
class OBD_IID00(OBD_Packet):
name = "IID_00_Service9SupportedInformationTypes"
fields_desc = [
FlagsField('supported_iids', 0, 32, [
'IID20',
'IID1F',
'IID1E',
'IID1D',
'IID1C',
'IID1B',
'IID1A',
'IID19',
'IID18',
'IID17',
'IID16',
'IID15',
'IID14',
'IID13',
'IID12',
'IID11',
'IID10',
'IID0F',
'IID0E',
'IID0D',
'IID0C',
'IID0B',
'IID0A',
'IID09',
'IID08',
'IID07',
'IID06',
'IID05',
'IID04',
'IID03',
'IID02',
'IID01'
])
]
class _OBD_IID_MessageCount(OBD_Packet):
fields_desc = [
ByteField('message_count', 0)
]
class OBD_IID01(_OBD_IID_MessageCount):
name = "IID_01_VinMessageCount"
class OBD_IID03(_OBD_IID_MessageCount):
name = "IID_03_CalibrationIdMessageCount"
class OBD_IID05(_OBD_IID_MessageCount):
name = "IID_05_CalibrationVerificationNumbersMessageCount"
class OBD_IID07(_OBD_IID_MessageCount):
name = "IID_07_InUsePerformanceTrackingMessageCount"
class OBD_IID09(_OBD_IID_MessageCount):
name = "IID_09_EcuNameMessageCount"
class OBD_IID02(OBD_Packet):
name = "IID_02_VehicleIdentificationNumber"
fields_desc = [
FieldLenField('count', None, count_of='vehicle_identification_numbers',
fmt='B'),
FieldListField('vehicle_identification_numbers', [],
StrFixedLenField('', b'', 17),
count_from=lambda pkt: pkt.count)
]
class OBD_IID04(OBD_Packet):
name = "IID_04_CalibrationId"
fields_desc = [
FieldLenField('count', None, count_of='calibration_identifications',
fmt='B'),
FieldListField('calibration_identifications', [],
StrFixedLenField('', b'', 16),
count_from=lambda pkt: pkt.count)
]
class OBD_IID06(OBD_Packet):
name = "IID_06_CalibrationVerificationNumbers"
fields_desc = [
FieldLenField('count', None,
count_of='calibration_verification_numbers', fmt='B'),
FieldListField('calibration_verification_numbers', [],
StrFixedLenField('', b'', 4),
count_from=lambda pkt: pkt.count)
]
class OBD_IID08(OBD_Packet):
name = "IID_08_InUsePerformanceTracking"
fields_desc = [
FieldLenField('count', None, count_of='data', fmt='B'),
FieldListField('data', [],
ShortField('', 0),
count_from=lambda pkt: pkt.count)
]
class OBD_IID0A(OBD_Packet):
name = "IID_0A_EcuName"
fields_desc = [
FieldLenField('count', None, count_of='ecu_names', fmt='B'),
FieldListField('ecu_names', [],
StrFixedLenField('', b'', 20),
count_from=lambda pkt: pkt.count)
]
class OBD_IID0B(OBD_Packet):
name = "IID_0B_InUsePerformanceTrackingForCompressionIgnitionVehicles"
fields_desc = [
FieldLenField('count', None, count_of='data', fmt='B'),
FieldListField('data', [],
ShortField('', 0),
count_from=lambda pkt: pkt.count)
]
bind_layers(OBD_S09_PR_Record, OBD_IID00, iid=0x00)
bind_layers(OBD_S09_PR_Record, OBD_IID01, iid=0x01)
bind_layers(OBD_S09_PR_Record, OBD_IID02, iid=0x02)
bind_layers(OBD_S09_PR_Record, OBD_IID03, iid=0x03)
bind_layers(OBD_S09_PR_Record, OBD_IID04, iid=0x04)
bind_layers(OBD_S09_PR_Record, OBD_IID05, iid=0x05)
bind_layers(OBD_S09_PR_Record, OBD_IID06, iid=0x06)
bind_layers(OBD_S09_PR_Record, OBD_IID07, iid=0x07)
bind_layers(OBD_S09_PR_Record, OBD_IID08, iid=0x08)
bind_layers(OBD_S09_PR_Record, OBD_IID09, iid=0x09)
bind_layers(OBD_S09_PR_Record, OBD_IID0A, iid=0x0A)
bind_layers(OBD_S09_PR_Record, OBD_IID0B, iid=0x0B)

View file

@ -0,0 +1,12 @@
# This file is part of Scapy
# See http://www.secdev.org/projects/scapy for more information
# Copyright (C) Andreas Korb <andreas.d.korb@gmail.com>
# Copyright (C) Nils Weiss <nils@we155.de>
# This program is published under a GPLv2 license
# scapy.contrib.status = skip
"""
Package of contrib automotive obd specific modules
that have to be loaded explicitly.
"""

View file

@ -0,0 +1,554 @@
# This file is part of Scapy
# See http://www.secdev.org/projects/scapy for more information
# Copyright (C) Andreas Korb <andreas.d.korb@gmail.com>
# Copyright (C) Nils Weiss <nils@we155.de>
# This program is published under a GPLv2 license
# scapy.contrib.status = skip
from scapy.fields import FlagsField, ScalingField, ByteEnumField, \
MultipleTypeField, ShortField, ShortEnumField, PacketListField
from scapy.packet import Packet, bind_layers
from scapy.contrib.automotive.obd.packet import OBD_Packet
from scapy.contrib.automotive.obd.services import OBD_S06
def _unit_and_scaling_fields(name):
return [
(ScalingField(name, 0, fmt='H'),
lambda pkt: pkt.unit_and_scaling_id == 0x1),
(ScalingField(name, 0, scaling=0.1, fmt='H'),
lambda pkt: pkt.unit_and_scaling_id == 0x2),
(ScalingField(name, 0, scaling=0.01, fmt='H'),
lambda pkt: pkt.unit_and_scaling_id == 0x3),
(ScalingField(name, 0, scaling=0.001, fmt='H'),
lambda pkt: pkt.unit_and_scaling_id == 0x4),
(ScalingField(name, 0, scaling=0.0000305, fmt='H'),
lambda pkt: pkt.unit_and_scaling_id == 0x5),
(ScalingField(name, 0, scaling=0.000305, fmt='H'),
lambda pkt: pkt.unit_and_scaling_id == 0x6),
(ScalingField(name, 0, scaling=0.25, unit="rpm", fmt='H'),
lambda pkt: pkt.unit_and_scaling_id == 0x7),
(ScalingField(name, 0, scaling=0.01, unit="km/h", fmt='H'),
lambda pkt: pkt.unit_and_scaling_id == 0x8),
(ScalingField(name, 0, unit="km/h", fmt='H'),
lambda pkt: pkt.unit_and_scaling_id == 0x9),
(ScalingField(name, 0, scaling=0.122, unit="mV", fmt='H'),
lambda pkt: pkt.unit_and_scaling_id == 0xA),
(ScalingField(name, 0, scaling=0.001, unit="V", fmt='H'),
lambda pkt: pkt.unit_and_scaling_id == 0xB),
(ScalingField(name, 0, scaling=0.01, unit="V", fmt='H'),
lambda pkt: pkt.unit_and_scaling_id == 0xC),
(ScalingField(name, 0, scaling=0.00390625, unit="mA", fmt='H'),
lambda pkt: pkt.unit_and_scaling_id == 0xD),
(ScalingField(name, 0, scaling=0.001, unit="A", fmt='H'),
lambda pkt: pkt.unit_and_scaling_id == 0xE),
(ScalingField(name, 0, scaling=0.01, unit="A", fmt='H'),
lambda pkt: pkt.unit_and_scaling_id == 0xF),
(ScalingField(name, 0, unit="ms", fmt='H'),
lambda pkt: pkt.unit_and_scaling_id == 0x10),
(ScalingField(name, 0, scaling=100, unit="ms", fmt='H'),
lambda pkt: pkt.unit_and_scaling_id == 0x11),
(ScalingField(name, 0, scaling=1, unit="s", fmt='H'),
lambda pkt: pkt.unit_and_scaling_id == 0x12),
(ScalingField(name, 0, scaling=1, unit="mOhm", fmt='H'),
lambda pkt: pkt.unit_and_scaling_id == 0x13),
(ScalingField(name, 0, scaling=1, unit="Ohm", fmt='H'),
lambda pkt: pkt.unit_and_scaling_id == 0x14),
(ScalingField(name, 0, scaling=1, unit="kOhm", fmt='H'),
lambda pkt: pkt.unit_and_scaling_id == 0x15),
(ScalingField(name, -40, scaling=0.1, unit="deg. C",
offset=-40, fmt='H'),
lambda pkt: pkt.unit_and_scaling_id == 0x16),
(ScalingField(name, 0, scaling=0.01, unit="kPa", fmt='H'),
lambda pkt: pkt.unit_and_scaling_id == 0x17),
(ScalingField(name, 0, scaling=0.0117, unit="kPa", fmt='H'),
lambda pkt: pkt.unit_and_scaling_id == 0x18),
(ScalingField(name, 0, scaling=0.079, unit="kPa", fmt='H'),
lambda pkt: pkt.unit_and_scaling_id == 0x19),
(ScalingField(name, 0, scaling=1, unit="kPa", fmt='H'),
lambda pkt: pkt.unit_and_scaling_id == 0x1A),
(ScalingField(name, 0, scaling=10, unit="kPa", fmt='H'),
lambda pkt: pkt.unit_and_scaling_id == 0x1B),
(ScalingField(name, 0, scaling=0.01, unit="deg.", fmt='H'),
lambda pkt: pkt.unit_and_scaling_id == 0x1C),
(ScalingField(name, 0, scaling=0.5, unit="deg.", fmt='H'),
lambda pkt: pkt.unit_and_scaling_id == 0x1D),
(ScalingField(name, 0, scaling=0.0000305, fmt='H'),
lambda pkt: pkt.unit_and_scaling_id == 0x1E),
(ScalingField(name, 0, scaling=0.05, fmt='H'),
lambda pkt: pkt.unit_and_scaling_id == 0x1F),
(ScalingField(name, 0, scaling=0.0039062, fmt='H'),
lambda pkt: pkt.unit_and_scaling_id == 0x20),
(ScalingField(name, 0, scaling=1, unit="mHz", fmt='H'),
lambda pkt: pkt.unit_and_scaling_id == 0x21),
(ScalingField(name, 0, scaling=1, unit="Hz", fmt='H'),
lambda pkt: pkt.unit_and_scaling_id == 0x22),
(ScalingField(name, 0, scaling=1, unit="KHz", fmt='H'),
lambda pkt: pkt.unit_and_scaling_id == 0x23),
(ScalingField(name, 0, scaling=1, unit="counts", fmt='H'),
lambda pkt: pkt.unit_and_scaling_id == 0x24),
(ScalingField(name, 0, scaling=1, unit="km", fmt='H'),
lambda pkt: pkt.unit_and_scaling_id == 0x25),
(ScalingField(name, 0, scaling=0.1, unit="mV/ms", fmt='H'),
lambda pkt: pkt.unit_and_scaling_id == 0x26),
(ScalingField(name, 0, scaling=0.01, unit="g/s", fmt='H'),
lambda pkt: pkt.unit_and_scaling_id == 0x27),
(ScalingField(name, 0, scaling=1, unit="g/s", fmt='H'),
lambda pkt: pkt.unit_and_scaling_id == 0x28),
(ScalingField(name, 0, scaling=0.25, unit="Pa/s", fmt='H'),
lambda pkt: pkt.unit_and_scaling_id == 0x29),
(ScalingField(name, 0, scaling=0.001, unit="kg/h", fmt='H'),
lambda pkt: pkt.unit_and_scaling_id == 0x2A),
(ScalingField(name, 0, scaling=1, unit="switches", fmt='H'),
lambda pkt: pkt.unit_and_scaling_id == 0x2B),
(ScalingField(name, 0, scaling=0.01, unit="g/cyl", fmt='H'),
lambda pkt: pkt.unit_and_scaling_id == 0x2C),
(ScalingField(name, 0, scaling=0.01, unit="mg/stroke", fmt='H'),
lambda pkt: pkt.unit_and_scaling_id == 0x2D),
(ShortEnumField(name, 0, {0: "false", 1: "true"}),
lambda pkt: pkt.unit_and_scaling_id == 0x2E),
(ScalingField(name, 0, scaling=0.01, unit="%", fmt='H'),
lambda pkt: pkt.unit_and_scaling_id == 0x2F),
(ScalingField(name, 0, scaling=0.001526, unit="%", fmt='H'),
lambda pkt: pkt.unit_and_scaling_id == 0x30),
(ScalingField(name, 0, scaling=0.001, unit="L", fmt='H'),
lambda pkt: pkt.unit_and_scaling_id == 0x31),
(ScalingField(name, 0, scaling=0.0000305, unit="inch", fmt='H'),
lambda pkt: pkt.unit_and_scaling_id == 0x32),
(ScalingField(name, 0, scaling=0.00024414, fmt='H'),
lambda pkt: pkt.unit_and_scaling_id == 0x33),
(ScalingField(name, 0, scaling=1, unit="min", fmt='H'),
lambda pkt: pkt.unit_and_scaling_id == 0x34),
(ScalingField(name, 0, scaling=10, unit="ms", fmt='H'),
lambda pkt: pkt.unit_and_scaling_id == 0x35),
(ScalingField(name, 0, scaling=0.01, unit="g", fmt='H'),
lambda pkt: pkt.unit_and_scaling_id == 0x36),
(ScalingField(name, 0, scaling=0.1, unit="g", fmt='H'),
lambda pkt: pkt.unit_and_scaling_id == 0x37),
(ScalingField(name, 0, scaling=1, unit="g", fmt='H'),
lambda pkt: pkt.unit_and_scaling_id == 0x38),
(ScalingField(name, 0, scaling=0.01, unit="%", offset=-327.68,
fmt='H'),
lambda pkt: pkt.unit_and_scaling_id == 0x39),
(ScalingField(name, 0, scaling=1, fmt='h'),
lambda pkt: pkt.unit_and_scaling_id == 0x81),
(ScalingField(name, 0, scaling=0.1, fmt='h'),
lambda pkt: pkt.unit_and_scaling_id == 0x82),
(ScalingField(name, 0, scaling=0.01, fmt='h'),
lambda pkt: pkt.unit_and_scaling_id == 0x83),
(ScalingField(name, 0, scaling=0.001, fmt='h'),
lambda pkt: pkt.unit_and_scaling_id == 0x84),
(ScalingField(name, 0, scaling=0.0000305, fmt='h'),
lambda pkt: pkt.unit_and_scaling_id == 0x85),
(ScalingField(name, 0, scaling=0.000305, fmt='h'),
lambda pkt: pkt.unit_and_scaling_id == 0x86),
(ScalingField(name, 0, scaling=0.122, unit="mV", fmt='h'),
lambda pkt: pkt.unit_and_scaling_id == 0x8A),
(ScalingField(name, 0, scaling=0.001, unit="V", fmt='h'),
lambda pkt: pkt.unit_and_scaling_id == 0x8B),
(ScalingField(name, 0, scaling=0.01, unit="V", fmt='h'),
lambda pkt: pkt.unit_and_scaling_id == 0x8C),
(ScalingField(name, 0, scaling=0.00390625, unit="mA", fmt='h'),
lambda pkt: pkt.unit_and_scaling_id == 0x8D),
(ScalingField(name, 0, scaling=0.001, unit="A", fmt='h'),
lambda pkt: pkt.unit_and_scaling_id == 0x8E),
(ScalingField(name, 0, scaling=1, unit="ms", fmt='h'),
lambda pkt: pkt.unit_and_scaling_id == 0x90),
(ScalingField(name, 0, scaling=0.1, unit="deg. C", fmt='h'),
lambda pkt: pkt.unit_and_scaling_id == 0x96),
(ScalingField(name, 0, scaling=0.01, unit="deg.", fmt='h'),
lambda pkt: pkt.unit_and_scaling_id == 0x9C),
(ScalingField(name, 0, scaling=0.5, unit="deg.", fmt='h'),
lambda pkt: pkt.unit_and_scaling_id == 0x9D),
(ScalingField(name, 0, scaling=1, unit="g/s", fmt='h'),
lambda pkt: pkt.unit_and_scaling_id == 0xA8),
(ScalingField(name, 0, scaling=0.25, unit="Pa/s", fmt='h'),
lambda pkt: pkt.unit_and_scaling_id == 0xA9),
(ScalingField(name, 0, scaling=0.01, unit="%", fmt='h'),
lambda pkt: pkt.unit_and_scaling_id == 0xAF),
(ScalingField(name, 0, scaling=0.003052, unit="%", fmt='h'),
lambda pkt: pkt.unit_and_scaling_id == 0xB0),
(ScalingField(name, 0, scaling=2, unit="mV/s", fmt='h'),
lambda pkt: pkt.unit_and_scaling_id == 0xB1),
(ScalingField(name, 0, scaling=0.001, unit="kPa", fmt='h'),
lambda pkt: pkt.unit_and_scaling_id == 0xFD),
(ScalingField(name, 0, scaling=0.25, unit="Pa", fmt='h'),
lambda pkt: pkt.unit_and_scaling_id == 0xFE)
]
def _mid_flags(basemid):
return [
'MID%02X' % (basemid + 0x20),
'MID%02X' % (basemid + 0x1F),
'MID%02X' % (basemid + 0x1E),
'MID%02X' % (basemid + 0x1D),
'MID%02X' % (basemid + 0x1C),
'MID%02X' % (basemid + 0x1B),
'MID%02X' % (basemid + 0x1A),
'MID%02X' % (basemid + 0x19),
'MID%02X' % (basemid + 0x18),
'MID%02X' % (basemid + 0x17),
'MID%02X' % (basemid + 0x16),
'MID%02X' % (basemid + 0x15),
'MID%02X' % (basemid + 0x14),
'MID%02X' % (basemid + 0x13),
'MID%02X' % (basemid + 0x12),
'MID%02X' % (basemid + 0x11),
'MID%02X' % (basemid + 0x10),
'MID%02X' % (basemid + 0x0F),
'MID%02X' % (basemid + 0x0E),
'MID%02X' % (basemid + 0x0D),
'MID%02X' % (basemid + 0x0C),
'MID%02X' % (basemid + 0x0B),
'MID%02X' % (basemid + 0x0A),
'MID%02X' % (basemid + 0x09),
'MID%02X' % (basemid + 0x08),
'MID%02X' % (basemid + 0x07),
'MID%02X' % (basemid + 0x06),
'MID%02X' % (basemid + 0x05),
'MID%02X' % (basemid + 0x04),
'MID%02X' % (basemid + 0x03),
'MID%02X' % (basemid + 0x02),
'MID%02X' % (basemid + 0x01)
]
class OBD_MIDXX(OBD_Packet):
standardized_test_ids = {
1: "TID_01_RichToLeanSensorThresholdVoltage",
2: "TID_02_LeanToRichSensorThresholdVoltage",
3: "TID_03_LowSensorVoltageForSwitchTimeCalculation",
4: "TID_04_HighSensorVoltageForSwitchTimeCalculation",
5: "TID_05_RichToLeanSensorSwitchTime",
6: "TID_06_LeanToRichSensorSwitchTime",
7: "TID_07_MinimumSensorVoltageForTestCycle",
8: "TID_08_MaximumSensorVoltageForTestCycle",
9: "TID_09_TimeBetweenSensorTransitions",
10: "TID_0A_SensorPeriod"}
unit_and_scaling_ids = {
0x01: "Raw Value",
0x02: "Raw Value",
0x03: "Raw Value",
0x04: "Raw Value",
0x05: "Raw Value",
0x06: "Raw Value",
0x07: "rotational frequency",
0x08: "Speed",
0x09: "Speed",
0x0A: "Voltage",
0x0B: "Voltage",
0x0C: "Voltage",
0x0D: "Current",
0x0E: "Current",
0x0F: "Current",
0x10: "Time",
0x11: "Time",
0x12: "Time",
0x13: "Resistance",
0x14: "Resistance",
0x15: "Resistance",
0x16: "Temperature",
0x17: "Pressure (Gauge)",
0x18: "Pressure (Air pressure)",
0x19: "Pressure (Fuel pressure)",
0x1A: "Pressure (Gauge)",
0x1B: "Pressure (Diesel pressure)",
0x1C: "Angle",
0x1D: "Angle",
0x1E: "Equivalence ratio (lambda)",
0x1F: "Air/Fuel ratio",
0x20: "Ratio",
0x21: "Frequency",
0x22: "Frequency",
0x23: "Frequency",
0x24: "Counts",
0x25: "Distance",
0x26: "Voltage per time",
0x27: "Mass per time",
0x28: "Mass per time",
0x29: "Pressure per time",
0x2A: "Mass per time",
0x2B: "Switches",
0x2C: "Mass per cylinder",
0x2D: "Mass per stroke",
0x2E: "True/False",
0x2F: "Percent",
0x30: "Percent",
0x31: "volume",
0x32: "length",
0x33: "Equivalence ratio (lambda)",
0x34: "Time",
0x35: "Time",
0x36: "Weight",
0x37: "Weight",
0x38: "Weight",
0x39: "Percent",
0x81: "Raw Value",
0x82: "Raw Value",
0x83: "Raw Value",
0x84: "Raw Value",
0x85: "Raw Value",
0x86: "Raw Value",
0x8A: "Voltage",
0x8B: "Voltage",
0x8C: "Voltage",
0x8D: "Current",
0x8E: "Current",
0x90: "Time",
0x96: "Temperature",
0x9C: "Angle",
0x9D: "Angle",
0xA8: "Mass per time",
0xA9: "Pressure per time",
0xAF: "Percent",
0xB0: "Percent",
0xB1: "Voltage per time",
0xFD: "Pressure",
0xFE: "Pressure"
}
name = "OBD MID data record"
fields_desc = [
ByteEnumField("standardized_test_id", 1, standardized_test_ids),
ByteEnumField("unit_and_scaling_id", 1, unit_and_scaling_ids),
MultipleTypeField(_unit_and_scaling_fields("test_value"),
ShortField("test_value", 0)),
MultipleTypeField(_unit_and_scaling_fields("min_limit"),
ShortField("min_limit", 0)),
MultipleTypeField(_unit_and_scaling_fields("max_limit"),
ShortField("max_limit", 0)),
]
class OBD_MID00(OBD_Packet):
fields_desc = [
FlagsField('supported_mids', 0, 32, _mid_flags(0x00)),
]
class OBD_MID20(OBD_Packet):
fields_desc = [
FlagsField('supported_mids', 0, 32, _mid_flags(0x20)),
]
class OBD_MID40(OBD_Packet):
fields_desc = [
FlagsField('supported_mids', 0, 32, _mid_flags(0x40)),
]
class OBD_MID60(OBD_Packet):
fields_desc = [
FlagsField('supported_mids', 0, 32, _mid_flags(0x60)),
]
class OBD_MID80(OBD_Packet):
fields_desc = [
FlagsField('supported_mids', 0, 32, _mid_flags(0x80)),
]
class OBD_MIDA0(OBD_Packet):
fields_desc = [
FlagsField('supported_mids', 0, 32, _mid_flags(0xA0)),
]
class OBD_S06_PR_Record(Packet):
on_board_monitoring_ids = {
0x00: "OBD Monitor IDs supported ($01 - $20)",
0x01: "Oxygen Sensor Monitor Bank 1 - Sensor 1",
0x02: "Oxygen Sensor Monitor Bank 1 - Sensor 2",
0x03: "Oxygen Sensor Monitor Bank 1 - Sensor 3",
0x04: "Oxygen Sensor Monitor Bank 1 - Sensor 4",
0x05: "Oxygen Sensor Monitor Bank 2 - Sensor 1",
0x06: "Oxygen Sensor Monitor Bank 2 - Sensor 2",
0x07: "Oxygen Sensor Monitor Bank 2 - Sensor 3",
0x08: "Oxygen Sensor Monitor Bank 2 - Sensor 4",
0x09: "Oxygen Sensor Monitor Bank 3 - Sensor 1",
0x0A: "Oxygen Sensor Monitor Bank 3 - Sensor 2",
0x0B: "Oxygen Sensor Monitor Bank 3 - Sensor 3",
0x0C: "Oxygen Sensor Monitor Bank 3 - Sensor 4",
0x0D: "Oxygen Sensor Monitor Bank 4 - Sensor 1",
0x0E: "Oxygen Sensor Monitor Bank 4 - Sensor 2",
0x0F: "Oxygen Sensor Monitor Bank 4 - Sensor 3",
0x10: "Oxygen Sensor Monitor Bank 4 - Sensor 4",
0x20: "OBD Monitor IDs supported ($21 - $40)",
0x21: "Catalyst Monitor Bank 1",
0x22: "Catalyst Monitor Bank 2",
0x23: "Catalyst Monitor Bank 3",
0x24: "Catalyst Monitor Bank 4",
0x32: "EGR Monitor Bank 2",
0x33: "EGR Monitor Bank 3",
0x34: "EGR Monitor Bank 4",
0x35: "VVT Monitor Bank 1",
0x36: "VVT Monitor Bank 2",
0x37: "VVT Monitor Bank 3",
0x38: "VVT Monitor Bank 4",
0x39: "EVAP Monitor (Cap Off / 0.150\")",
0x3A: "EVAP Monitor (0.090\")",
0x3B: "EVAP Monitor (0.040\")",
0x3C: "EVAP Monitor (0.020\")",
0x3D: "Purge Flow Monitor",
0x40: "OBD Monitor IDs supported ($41 - $60)",
0x41: "Oxygen Sensor Heater Monitor Bank 1 - Sensor 1",
0x42: "Oxygen Sensor Heater Monitor Bank 1 - Sensor 2",
0x43: "Oxygen Sensor Heater Monitor Bank 1 - Sensor 3",
0x44: "Oxygen Sensor Heater Monitor Bank 1 - Sensor 4",
0x45: "Oxygen Sensor Heater Monitor Bank 2 - Sensor 1",
0x46: "Oxygen Sensor Heater Monitor Bank 2 - Sensor 2",
0x47: "Oxygen Sensor Heater Monitor Bank 2 - Sensor 3",
0x48: "Oxygen Sensor Heater Monitor Bank 2 - Sensor 4",
0x49: "Oxygen Sensor Heater Monitor Bank 3 - Sensor 1",
0x4A: "Oxygen Sensor Heater Monitor Bank 3 - Sensor 2",
0x4B: "Oxygen Sensor Heater Monitor Bank 3 - Sensor 3",
0x4C: "Oxygen Sensor Heater Monitor Bank 3 - Sensor 4",
0x4D: "Oxygen Sensor Heater Monitor Bank 4 - Sensor 1",
0x4E: "Oxygen Sensor Heater Monitor Bank 4 - Sensor 2",
0x4F: "Oxygen Sensor Heater Monitor Bank 4 - Sensor 3",
0x50: "Oxygen Sensor Heater Monitor Bank 4 - Sensor 4",
0x60: "OBD Monitor IDs supported ($61 - $80)",
0x61: "Heated Catalyst Monitor Bank 1",
0x62: "Heated Catalyst Monitor Bank 2",
0x63: "Heated Catalyst Monitor Bank 3",
0x64: "Heated Catalyst Monitor Bank 4",
0x71: "Secondary Air Monitor 1",
0x72: "Secondary Air Monitor 2",
0x73: "Secondary Air Monitor 3",
0x74: "Secondary Air Monitor 4",
0x80: "OBD Monitor IDs supported ($81 - $A0)",
0x81: "Fuel System Monitor Bank 1",
0x82: "Fuel System Monitor Bank 2",
0x83: "Fuel System Monitor Bank 3",
0x84: "Fuel System Monitor Bank 4",
0x85: "Boost Pressure Control Monitor Bank 1",
0x86: "Boost Pressure Control Monitor Bank 2",
0x90: "NOx Adsorber Monitor Bank 1",
0x91: "NOx Adsorber Monitor Bank 2",
0x98: "NOx Catalyst Monitor Bank 1",
0x99: "NOx Catalyst Monitor Bank 2",
0xA0: "OBD Monitor IDs supported ($A1 - $C0)",
0xA1: "Misfire Monitor General Data",
0xA2: "Misfire Cylinder 1 Data",
0xA3: "Misfire Cylinder 2 Data",
0xA4: "Misfire Cylinder 3 Data",
0xA5: "Misfire Cylinder 4 Data",
0xA6: "Misfire Cylinder 5 Data",
0xA7: "Misfire Cylinder 6 Data",
0xA8: "Misfire Cylinder 7 Data",
0xA9: "Misfire Cylinder 8 Data",
0xAA: "Misfire Cylinder 9 Data",
0xAB: "Misfire Cylinder 10 Data",
0xAC: "Misfire Cylinder 11 Data",
0xAD: "Misfire Cylinder 12 Data",
0xB0: "PM Filter Monitor Bank 1",
0xB1: "PM Filter Monitor Bank 2"
}
name = "On-Board diagnostic monitoring ID"
fields_desc = [
ByteEnumField("mid", 0, on_board_monitoring_ids),
]
class OBD_S06_PR(Packet):
name = "On-Board monitoring IDs"
fields_desc = [
PacketListField("data_records", [], OBD_S06_PR_Record)
]
def answers(self, other):
return other.__class__ == OBD_S06 \
and all(r.mid in other.mid for r in self.data_records)
bind_layers(OBD_S06_PR_Record, OBD_MID00, mid=0x00)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x01)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x02)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x03)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x04)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x05)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x06)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x07)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x08)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x09)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x0A)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x0B)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x0C)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x0D)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x0E)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x0F)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x10)
bind_layers(OBD_S06_PR_Record, OBD_MID20, mid=0x20)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x21)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x22)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x23)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x24)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x32)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x33)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x34)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x35)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x36)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x37)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x38)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x39)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x3A)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x3B)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x3C)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x3D)
bind_layers(OBD_S06_PR_Record, OBD_MID40, mid=0x40)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x41)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x42)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x43)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x44)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x45)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x46)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x47)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x48)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x49)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x4A)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x4B)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x4C)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x4D)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x4E)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x4F)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x50)
bind_layers(OBD_S06_PR_Record, OBD_MID60, mid=0x60)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x61)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x62)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x63)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x64)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x71)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x72)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x73)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x74)
bind_layers(OBD_S06_PR_Record, OBD_MID80, mid=0x80)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x81)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x82)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x83)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x84)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x85)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x86)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x90)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x91)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x98)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x99)
bind_layers(OBD_S06_PR_Record, OBD_MIDA0, mid=0xA0)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0xA1)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0xA2)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0xA3)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0xA4)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0xA5)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0xA6)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0xA7)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0xA8)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0xA9)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0xAA)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0xAB)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0xAC)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0xAD)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0xB0)
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0xB1)

View file

@ -0,0 +1,105 @@
# This file is part of Scapy
# See http://www.secdev.org/projects/scapy for more information
# Copyright (C) Andreas Korb <andreas.d.korb@gmail.com>
# Copyright (C) Nils Weiss <nils@we155.de>
# This program is published under a GPLv2 license
# scapy.contrib.description = On Board Diagnostic Protocol (OBD-II)
# scapy.contrib.status = loads
import struct
from scapy.contrib.automotive.obd.iid.iids import *
from scapy.contrib.automotive.obd.mid.mids import *
from scapy.contrib.automotive.obd.pid.pids import *
from scapy.contrib.automotive.obd.tid.tids import *
from scapy.contrib.automotive.obd.services import *
from scapy.packet import bind_layers, NoPayload
from scapy.error import log_loading
from scapy.config import conf
from scapy.fields import XByteEnumField
from scapy.contrib.isotp import ISOTP
try:
if conf.contribs['OBD']['treat-response-pending-as-answer']:
pass
except KeyError:
log_loading.info("Specify \"conf.contribs['OBD'] = "
"{'treat-response-pending-as-answer': True}\" to treat "
"a negative response 'requestCorrectlyReceived-"
"ResponsePending' as answer of a request. \n"
"The default value is False.")
conf.contribs['OBD'] = {'treat-response-pending-as-answer': False}
class OBD(ISOTP):
services = {
0x01: 'CurrentPowertrainDiagnosticDataRequest',
0x02: 'PowertrainFreezeFrameDataRequest',
0x03: 'EmissionRelatedDiagnosticTroubleCodesRequest',
0x04: 'ClearResetDiagnosticTroubleCodesRequest',
0x05: 'OxygenSensorMonitoringTestResultsRequest',
0x06: 'OnBoardMonitoringTestResultsRequest',
0x07: 'PendingEmissionRelatedDiagnosticTroubleCodesRequest',
0x08: 'ControlOperationRequest',
0x09: 'VehicleInformationRequest',
0x0A: 'PermanentDiagnosticTroubleCodesRequest',
0x41: 'CurrentPowertrainDiagnosticDataResponse',
0x42: 'PowertrainFreezeFrameDataResponse',
0x43: 'EmissionRelatedDiagnosticTroubleCodesResponse',
0x44: 'ClearResetDiagnosticTroubleCodesResponse',
0x45: 'OxygenSensorMonitoringTestResultsResponse',
0x46: 'OnBoardMonitoringTestResultsResponse',
0x47: 'PendingEmissionRelatedDiagnosticTroubleCodesResponse',
0x48: 'ControlOperationResponse',
0x49: 'VehicleInformationResponse',
0x4A: 'PermanentDiagnosticTroubleCodesResponse',
0x7f: 'NegativeResponse'}
name = "On-board diagnostics"
fields_desc = [
XByteEnumField('service', 0, services)
]
def hashret(self):
if self.service == 0x7f:
return struct.pack('B', self.request_service_id & ~0x40)
return struct.pack('B', self.service & ~0x40)
def answers(self, other):
if other.__class__ != self.__class__:
return False
if self.service == 0x7f:
return self.payload.answers(other)
if self.service == (other.service + 0x40):
if isinstance(self.payload, NoPayload) or \
isinstance(other.payload, NoPayload):
return True
else:
return self.payload.answers(other.payload)
return False
# Service Bindings
bind_layers(OBD, OBD_S01, service=0x01)
bind_layers(OBD, OBD_S02, service=0x02)
bind_layers(OBD, OBD_S03, service=0x03)
bind_layers(OBD, OBD_S04, service=0x04)
bind_layers(OBD, OBD_S06, service=0x06)
bind_layers(OBD, OBD_S07, service=0x07)
bind_layers(OBD, OBD_S08, service=0x08)
bind_layers(OBD, OBD_S09, service=0x09)
bind_layers(OBD, OBD_S0A, service=0x0A)
bind_layers(OBD, OBD_S01_PR, service=0x41)
bind_layers(OBD, OBD_S02_PR, service=0x42)
bind_layers(OBD, OBD_S03_PR, service=0x43)
bind_layers(OBD, OBD_S04_PR, service=0x44)
bind_layers(OBD, OBD_S06_PR, service=0x46)
bind_layers(OBD, OBD_S07_PR, service=0x47)
bind_layers(OBD, OBD_S08_PR, service=0x48)
bind_layers(OBD, OBD_S09_PR, service=0x49)
bind_layers(OBD, OBD_S0A_PR, service=0x4A)
bind_layers(OBD, OBD_NR, service=0x7F)

View file

@ -0,0 +1,921 @@
% Regression tests for the OBD layer
# More information at http://www.secdev.org/projects/UTscapy/
############
############
+ Basic operations
= Load module
load_contrib("automotive.obd.obd")
= Check if positive response answers
req = OBD(b'\x01\x2f')
res = OBD(b'\x41\x2f\x1a')
assert res.answers(req)
= Check hashret
assert req.hashret() == res.hashret()
= Check if negative response answers
req = OBD(b'\x01\x2f')
res = OBD(b'\x7f\x01\x11')
assert res.answers(req)
= Check hashret
assert req.hashret() == res.hashret()
= Check hashret for Service 0x40
req = OBD(b'\x40')
res = OBD(b'\x7F\x40\x11')
assert req.hashret() == res.hashret()
= Check hashret for Service 0x51
req = OBD(b'\x51')
res = OBD(b'\x7F\x51\x11')
assert req.hashret() == res.hashret()
= Check dissecting a request for Service 01 PID 00
p = OBD(b'\x01\x00')
assert p.service == 0x01
assert p.pid[0] == 0x00
= Check dissecting a request for Service 01 PID 75
p = OBD(b'\x01\x75')
assert p.service == 0x01
assert p.pid[0] == 0x75
= Check dissecting a request for Service 01 PID 78
p = OBD(b'\x01\x78')
assert p.service == 0x01
assert p.pid[0] == 0x78
= Check dissecting a request for Service 01 PID 7F
p = OBD(b'\x01\x7F')
assert p.service == 0x01
assert p.pid[0] == 0x7F
= Check dissecting a request for Service 01 PID 89
p = OBD(b'\x01\x89')
assert p.service == 0x01
assert p.pid[0] == 0x89
= Check dissecting a request for Service 02 PID 00
p = OBD(b'\x02\x00\x01')
assert p.service == 0x02
assert p.requests[0].pid == 0x00
assert p.requests[0].frame_no == 0x01
= Check dissecting a request for Service 02 PID 75
p = OBD(b'\x02\x75\x01')
assert p.service == 0x02
assert p.requests[0].pid == 0x75
assert p.requests[0].frame_no == 0x01
= Check dissecting a request for Service 02 PID 78
p = OBD(b'\x02\x78\x01')
assert p.service == 0x02
assert p.requests[0].pid == 0x78
assert p.requests[0].frame_no == 0x01
= Check dissecting a request for Service 02 PID 7F
p = OBD(b'\x02\x7F\x01')
assert p.service == 0x02
assert p.requests[0].pid == 0x7F
assert p.requests[0].frame_no == 0x01
= Check dissecting a request for Service 02 PID 89
p = OBD(b'\x02\x89\x01')
assert p.service == 0x02
assert p.requests[0].pid == 0x89
assert p.requests[0].frame_no == 0x01
= Check dissecting a request for Service 03
p = OBD(b'\x03')
assert p.service == 0x03
= Check dissecting a request for Service 06
p = OBD(b'\x06\x01')
assert p.service == 0x06
assert p.mid[0] == 0x01
= Check dissecting a request for Service 06 MID 00
p = OBD(b'\x06\x00')
assert p.service == 0x06
assert p.mid[0] == 0x00
= Check dissecting a request for Service 06 MID 00,01,02,03,04
p = OBD(b'\x06\x00\x01\x02\x03\x04')
assert p.service == 0x06
assert p.mid[0] == 0x00
assert p.mid[1] == 0x01
assert p.mid[2] == 0x02
assert p.mid[3] == 0x03
assert p.mid[4] == 0x04
= Check dissecting a response for Service 06 MID 00
p = OBD(b'\x46\x00\x00\x00\x00\x00')
assert p.service == 0x46
assert p.data_records[0].mid == 0x00
assert p.data_records[0].supported_mids == ""
= Check dissecting a response for Service 06 MID 00 and MID 20
p = OBD(b'\x46\x00\x01\x02\x03\x04\x20\x01\x02\x03\x04')
assert p.service == 0x46
assert p.data_records[0].mid == 0x00
assert p.data_records[0].supported_mids == "MID1E+MID18+MID17+MID0F+MID08"
assert p.data_records[1].mid == 0x20
assert p.data_records[1].supported_mids == "MID3E+MID38+MID37+MID2F+MID28"
= Check dissecting a response for Service 06 MID 00, 20, 40, 60, 80, A0
p = OBD(b'\x46\x00\x01\x02\x03\x04\x20\x01\x02\x03\x04\x40\x01\x02\x03\x04\x60\x01\x02\x03\x04\x80\x01\x02\x03\x04\xA0\x01\x02\x03\x04')
assert p.service == 0x46
assert p.data_records[0].mid == 0x00
assert p.data_records[0].supported_mids == "MID1E+MID18+MID17+MID0F+MID08"
assert p.data_records[1].mid == 0x20
assert p.data_records[1].supported_mids == "MID3E+MID38+MID37+MID2F+MID28"
assert p.data_records[2].mid == 0x40
assert p.data_records[2].supported_mids == "MID5E+MID58+MID57+MID4F+MID48"
assert p.data_records[3].mid == 0x60
assert p.data_records[3].supported_mids == "MID7E+MID78+MID77+MID6F+MID68"
assert p.data_records[4].mid == 0x80
assert p.data_records[4].supported_mids == "MID9E+MID98+MID97+MID8F+MID88"
assert p.data_records[5].mid == 0xA0
assert p.data_records[5].supported_mids == "MIDBE+MIDB8+MIDB7+MIDAF+MIDA8"
assert len(p.data_records) == 6
= Check dissecting a response for Service 06 MID 01
p = OBD(b'\x46\x01\x01\x0A\x0B\xB0\x0B\xB0\x0B\xB0\x01\x05\x10\x00\x48\x00\x00\x00\x64\x01\x85\x24\x00\x96\x00\x4B\xFF\xFF')
assert p.service == 0x46
assert p.data_records[0].mid == 0x01
assert p.data_records[0].standardized_test_id == 1
assert p.data_records[0].unit_and_scaling_id == 10
assert p.data_records[0].test_value == 365.024
assert p.data_records[0].min_limit == 365.024
assert p.data_records[0].max_limit == 365.024
assert "Voltage" in p.data_records[0].__repr__()
assert "365.024 mV" in p.data_records[0].__repr__()
assert p.data_records[1].mid == 0x01
assert p.data_records[1].standardized_test_id == 5
assert p.data_records[1].unit_and_scaling_id == 16
assert p.data_records[1].test_value == 72
assert p.data_records[1].min_limit == 0
assert p.data_records[1].max_limit == 100
assert "Time" in p.data_records[1].__repr__()
assert "72 ms" in p.data_records[1].__repr__()
assert p.data_records[2].mid == 0x01
assert p.data_records[2].standardized_test_id == 0x85
assert p.data_records[2].unit_and_scaling_id == 0x24
assert p.data_records[2].test_value == 150
assert p.data_records[2].min_limit == 75
assert p.data_records[2].max_limit == 65535
assert "Counts" in p.data_records[2].__repr__()
assert "150 counts" in p.data_records[2].__repr__()
assert len(p.data_records) == 3
= Check dissecting a response for Service 06 MID 21
p = OBD(b'\x46\x21\x87\x2F\x00\x00\x00\x00\x00\x00')
p.show()
assert p.service == 0x46
assert p.data_records[0].mid == 0x21
assert p.data_records[0].standardized_test_id == 135
assert p.data_records[0].unit_and_scaling_id == 0x2F
assert p.data_records[0].test_value == 0
assert p.data_records[0].min_limit == 0
assert p.data_records[0].max_limit == 0
assert "Percent" in p.data_records[0].__repr__()
assert "0 %" in p.data_records[0].__repr__()
assert len(p.data_records) == 1
= Check dissecting a request for Service 09 IID 00
p = OBD(b'\x09\x00')
assert p.service == 0x09
assert p.iid[0] == 0x00
= Check dissecting a request for Service 09 IID 02
p = OBD(b'\x09\x02')
assert p.service == 0x09
assert p.iid[0] == 0x02
= Check dissecting a request for Service 09 IID 04
p = OBD(b'\x09\x04')
assert p.service == 0x09
assert p.iid[0] == 0x04
= Check dissecting a request for Service 09 IID 00 and IID 02 and IID 04
p = OBD(b'\x09\x00\x02\x04')
assert p.service == 0x09
assert p.iid[0] == 0x00
assert p.iid[1] == 0x02
assert p.iid[2] == 0x04
= Check dissecting a request for Service 09 IID 0A
p = OBD(b'\x09\x0A')
assert p.service == 0x09
assert p.iid[0] == 0x0A
= Check dissecting a response for Service 01 PID 75
p = OBD(b'\x41\x75\x0a\x00\x11\x22\x33\x44\x55')
assert p.service == 0x41
assert p.data_records[0].pid == 0x75
assert p.data_records[0].reserved == 0
assert p.data_records[0].turbo_a_turbine_outlet_temperature_supported == 1
assert p.data_records[0].turbo_a_turbine_inlet_temperature_supported == 0
assert p.data_records[0].turbo_a_compressor_outlet_temperature_supported == 1
assert p.data_records[0].turbo_a_compressor_inlet_temperature_supported == 0
assert p.data_records[0].turbocharger_a_compressor_inlet_temperature == 0x00-40
assert p.data_records[0].turbocharger_a_compressor_outlet_temperature == 0x11-40
assert p.data_records[0].turbocharger_a_turbine_inlet_temperature == \
round((0x2233 * 0.1) - 40, 3)
assert p.data_records[0].turbocharger_a_turbine_outlet_temperature == \
round((0x4455 * 0.1) - 40, 3)
= Check dissecting a response for Service 01 PID 00 and PID 20
p = OBD(b'\x41\x00\xBF\xBF\xA8\x91\x20\x80\x00\x00\x00')
assert p.service == 0x41
assert p.data_records[0].pid == 0
assert p.data_records[0].supported_pids == "PID20+PID1C+PID19+PID15+PID13+PID11+PID10+PID0F+PID0E+PID0D+PID0C+PID0B+PID09+PID08+PID07+PID06+PID05+PID04+PID03+PID01"
assert p.data_records[1].pid == 0x20
assert p.data_records[1].supported_pids == "PID21"
assert len(p.data_records) == 2
= Check dissecting a response for Service 01 PID 05,01,15,0C,03
p = OBD(b'\x41\x05\x6e\x01\x83\x33\xff\x63\x15\xa0\x78\x0c\x0a\x6b\x03\x02\x00')
p.show()
assert p.service == 0x41
assert p.data_records[0].pid == 5
assert p.data_records[0].data == 70.0
assert p.data_records[1].pid == 0x1
assert p.data_records[2].pid == 0x15
assert p.data_records[2].outputVoltage == 0.8
assert p.data_records[2].trim == -6.25
assert p.data_records[3].pid == 12
assert p.data_records[3].data == 666.75
assert p.data_records[4].pid == 3
assert p.data_records[4].fuel_system1 == 0x02
assert p.data_records[4].fuel_system2 == 0
assert len(p.data_records) == 5
p = OBD(b'\x41\x00\xBF\xBF\xA8\x91\x20\x80\x00\x00\x00')
p.show()
assert p.service == 0x41
assert p.data_records[0].pid == 0
assert p.data_records[0].supported_pids == "PID20+PID1C+PID19+PID15+PID13+PID11+PID10+PID0F+PID0E+PID0D+PID0C+PID0B+PID09+PID08+PID07+PID06+PID05+PID04+PID03+PID01"
assert p.data_records[1].pid == 0x20
assert p.data_records[1].supported_pids == "PID21"
assert len(p.data_records) == 2
= Check dissecting a response for Service 01 PID 78
p = OBD(b'\x41\x78ABCDEFGHI')
assert p.service == 0x41
assert p.data_records[0].pid == 0x78
assert p.data_records[0].reserved == 4
assert p.data_records[0].sensor1_supported == 1
assert p.data_records[0].sensor2_supported == 0
assert p.data_records[0].sensor3_supported == 0
assert p.data_records[0].sensor4_supported == 0
assert p.data_records[0].sensor1 == 1656.3
assert p.data_records[0].sensor2 == 1707.7
assert p.data_records[0].sensor3 == 1759.1
assert p.data_records[0].sensor4 == 1810.5
= Check dissecting a response for Service 01 PID 7F
p = OBD(b'\x41\x7F\x0a'
b'\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF'
b'\x01\x02\x03\x04\x05\x06\x07\x08'
b'\x00\x11\x22\x33\x44\x55\x66\x77')
assert p.service == 0x41
assert p.data_records[0].pid == 0x7F
assert p.data_records[0].reserved == 1
assert p.data_records[0].total_with_pto_active_supported == 0
assert p.data_records[0].total_idle_supported == 1
assert p.data_records[0].total_supported == 0
assert p.data_records[0].total == 0xFFFFFFFFFFFFFFFF
assert p.data_records[0].total_idle == 0x0102030405060708
assert p.data_records[0].total_with_pto_active == 0x0011223344556677
= Check dissecting a response for Service 01 PID 89
p = OBD(b'\x41\x89ABCDEFGHIKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOP')
assert p.service == 0x41
assert p.data_records[0].pid == 0x89
assert p.data_records[0].data == b'ABCDEFGHIKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOP'
= Check dissecting a response for Service 02 PID 75
p = OBD(b'\x42\x75\01\x0a\x00\x11\x22\x33\x44\x55')
assert p.service == 0x42
assert p.data_records[0].pid == 0x75
assert p.data_records[0].frame_no == 0x01
assert p.data_records[0].reserved == 0
assert p.data_records[0].turbo_a_turbine_outlet_temperature_supported == 1
assert p.data_records[0].turbo_a_turbine_inlet_temperature_supported == 0
assert p.data_records[0].turbo_a_compressor_outlet_temperature_supported == 1
assert p.data_records[0].turbo_a_compressor_inlet_temperature_supported == 0
assert p.data_records[0].turbocharger_a_compressor_inlet_temperature == 0x00 - 40
assert p.data_records[0].turbocharger_a_compressor_outlet_temperature == 0x11 - 40
assert p.data_records[0].turbocharger_a_turbine_inlet_temperature == \
round((0x2233 * 0.1) - 40, 3)
assert p.data_records[0].turbocharger_a_turbine_outlet_temperature == \
round((0x4455 * 0.1) - 40, 3)
= Check dissecting a response for Service 02 PID 78
p = OBD(b'\x42\x78\x05ABCDEFGHI')
assert p.service == 0x42
assert p.data_records[0].pid == 0x78
assert p.data_records[0].frame_no == 0x05
assert p.data_records[0].reserved == 4
assert p.data_records[0].sensor1_supported == 1
assert p.data_records[0].sensor2_supported == 0
assert p.data_records[0].sensor3_supported == 0
assert p.data_records[0].sensor4_supported == 0
assert p.data_records[0].sensor1 == 1656.3
assert p.data_records[0].sensor2 == 1707.7
assert p.data_records[0].sensor3 == 1759.1
assert p.data_records[0].sensor4 == 1810.5
= Check dissecting a response for Service 02 PID 7F
p = OBD(b'\x42\x7F\x01\x03'
b'\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF'
b'\x01\x02\x03\x04\x05\x06\x07\x08'
b'\x00\x11\x22\x33\x44\x55\x66\x77')
assert p.service == 0x42
assert p.data_records[0].pid == 0x7F
assert p.data_records[0].frame_no == 0x01
assert p.data_records[0].reserved == 0
assert p.data_records[0].total_with_pto_active_supported == 0
assert p.data_records[0].total_idle_supported == 1
assert p.data_records[0].total_supported == 1
assert p.data_records[0].total == 0xFFFFFFFFFFFFFFFF
assert p.data_records[0].total_idle == 0x0102030405060708
assert p.data_records[0].total_with_pto_active == 0x0011223344556677
= Check dissecting a response for Service 02 PID 89
p = OBD(b'\x42\x89\x01ABCDEFGHIKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOP')
assert p.service == 0x42
assert p.data_records[0].pid == 0x89
assert p.data_records[0].frame_no == 0x01
assert p.data_records[0].data == b'ABCDEFGHIKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOP'
= Check dissecting a response for Service 02 PID 0C, 05, 04
p = OBD(b'\x42\x0c\x00\x20\x80\x04\x00\x80\x05\x00\x28')
assert p.service == 0x42
assert p.data_records[0].pid == 0x0C
assert p.data_records[0].frame_no == 0x0
assert p.data_records[0].data == 2080
assert p.data_records[1].pid == 0x04
assert p.data_records[1].frame_no == 0x0
assert p.data_records[1].data == 50.196
assert p.data_records[2].pid == 0x05
assert p.data_records[2].frame_no == 0x0
assert p.data_records[2].data == 0.0
= Check dissecting a response for Service 03
p = OBD(b'\x43\x06\x01\x43\x01\x96\x02\x34\x02\xcd\x03\x57\x0a\x24')
assert p.service == 0x43
assert p.count == 6
assert bytes(p.dtcs[0]) == b'\x01\x43'
assert bytes(p.dtcs[1]) == b'\x01\x96'
assert bytes(p.dtcs[2]) == b'\x02\x34'
assert bytes(p.dtcs[3]) == b'\x02\xcd'
assert bytes(p.dtcs[4]) == b'\x03\x57'
assert bytes(p.dtcs[5]) == b'\x0a\x24'
= Check dissecting a response for Service 07
p = OBD(b'\x47\x06\x01\x43\x01\x96\x02\x34\x02\xcd\x03\x57\x0a\x24')
assert p.service == 0x47
assert p.count == 6
assert bytes(p.dtcs[0]) == b'\x01\x43'
assert bytes(p.dtcs[1]) == b'\x01\x96'
assert bytes(p.dtcs[2]) == b'\x02\x34'
assert bytes(p.dtcs[3]) == b'\x02\xcd'
assert bytes(p.dtcs[4]) == b'\x03\x57'
assert bytes(p.dtcs[5]) == b'\x0a\x24'
= Check dissecting a response for Service 08 Tid 00
p = OBD(b'\x48\x00ABCD')
assert p.service == 0x48
assert p.data_records[0].tid == 0x00
assert p.data_records[0].supported_tids == "TID1E+TID1A+TID18+TID17+TID12+TID0F+TID0A+TID08+TID02"
= Check dissecting a response for Service 08 Tid 01
p = OBD(b'\x48\x01\x00\x00"\xffd')
assert p.service == 0x48
assert p.data_records[0].tid == 0x01
assert p.data_records[0].data_a == 0.0
assert p.data_records[0].data_b == 0.0
assert p.data_records[0].data_c == 0.17
assert p.data_records[0].data_d == 1.275
assert p.data_records[0].data_e == 0.5
= Check dissecting a response for Service 08 Tid 05
p = OBD(b'\x48\x05\x00\x00\x2b\xff\x7d')
assert p.service == 0x48
assert p.data_records[0].tid == 0x05
assert p.data_records[0].data_a == 0.0
assert p.data_records[0].data_b == 0.0
assert p.data_records[0].data_c == 0.172
assert p.data_records[0].data_d == 1.02
assert p.data_records[0].data_e == 0.5
= Check dissecting a response for Service 08 Tid 09
p = OBD(b'\x48\x09\x00\x00\x04\x1a\x0c')
assert p.service == 0x48
assert p.data_records[0].tid == 0x09
assert p.data_records[0].data_a == 0.0
assert p.data_records[0].data_b == 0.0
assert p.data_records[0].data_c == 0.16
assert p.data_records[0].data_d == 1.04
assert p.data_records[0].data_e == 0.48
= Check dissecting a response for Service 09 IID 00
p = OBD(b'\x49\x00ABCD')
assert p.service == 0x49
assert p.data_records[0].iid == 0x00
assert p.data_records[0].supported_iids == "IID1E+IID1A+IID18+IID17+IID12+IID0F+IID0A+IID08+IID02"
= Check dissecting a response for Service 09 IID 02 with one VIN
p = OBD(b'\x49\x02\x01W0L000051T2123456')
assert p.service == 0x49
assert p.data_records[0].iid == 0x02
assert p.data_records[0].count == 0x01
assert p.data_records[0].vehicle_identification_numbers[0] == b'W0L000051T2123456'
= Check dissecting a response for Service 09 IID 02 with two VINs
p = OBD(b'\x49\x02\x02W0L000051T2123456W0L000051T2123456')
assert p.service == 0x49
assert p.data_records[0].iid == 0x02
assert p.data_records[0].count == 0x02
assert p.data_records[0].vehicle_identification_numbers[0] == b'W0L000051T2123456'
assert p.data_records[0].vehicle_identification_numbers[1] == b'W0L000051T2123456'
= Check dissecting a response for Service 09 IID 04 with one CID
p = OBD(b'\x49\x04\x01ABCDEFGHIJKLMNOP')
assert p.service == 0x49
assert p.data_records[0].iid == 0x04
assert p.data_records[0].count == 0x01
assert p.data_records[0].calibration_identifications[0] == b'ABCDEFGHIJKLMNOP'
= Check dissecting a response for Service 09 IID 04 with two CID
p = OBD(b'\x49\x04\x02ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP')
assert p.service == 0x49
assert p.data_records[0].iid == 0x04
assert p.data_records[0].count == 0x02
assert p.data_records[0].calibration_identifications[0] == b'ABCDEFGHIJKLMNOP'
assert p.data_records[0].calibration_identifications[1] == b'ABCDEFGHIJKLMNOP'
= Check dissecting a response for Service 09 IID 06
p = OBD(b'\x49\x06\x02ABCDEFGH')
assert p.service == 0x49
assert p.data_records[0].iid == 0x06
assert p.data_records[0].count == 0x02
assert p.data_records[0].calibration_verification_numbers[0] == b'ABCD'
assert p.data_records[0].calibration_verification_numbers[1] == b'EFGH'
= Check dissecting a response for Service 09 IID 08
p = OBD(b'\x49\x08\x09\x00\x01\x00\x02\x00\x03\x00\x04\x00\x05\x00\x06\x00\x07\x00\x08\xFF\xFF')
assert p.service == 0x49
assert p.data_records[0].iid == 0x08
assert p.data_records[0].count == 0x09
assert p.data_records[0].data[0] == 1
assert p.data_records[0].data[1] == 2
assert p.data_records[0].data[2] == 3
assert p.data_records[0].data[3] == 4
assert p.data_records[0].data[4] == 5
assert p.data_records[0].data[5] == 6
assert p.data_records[0].data[6] == 7
assert p.data_records[0].data[7] == 8
assert p.data_records[0].data[8] == 65535
= Check dissecting a response for Service 09 IID 0A
p = OBD(b'\x49\x0A\x01ECM\x00-Engine Control\x00')
assert p.service == 0x49
assert p.data_records[0].iid == 0x0A
assert p.data_records[0].count == 0x01
assert p.data_records[0].ecu_names[0] == b'ECM\x00-Engine Control\x00'
= Check dissecting a response for Service 09 IID 0B
p = OBD(b'\x49\x0B\x05\x00\x01\x00\x02\x00\x03\x00\x04\xFF\xFF')
assert p.service == 0x49
assert p.data_records[0].iid == 0x0B
assert p.data_records[0].count == 0x05
assert p.data_records[0].data[0] == 1
assert p.data_records[0].data[1] == 2
assert p.data_records[0].data[2] == 3
assert p.data_records[0].data[3] == 4
assert p.data_records[0].data[4] == 65535
= Check dissecting a response for Service 09 IID 02 and IID 04
p = OBD(b'\x49\x02\x01ABCDEFGHIJKLMNOPQ\x04\x01ABCDEFGHIJKLMNOP')
assert p.service == 0x49
assert p.data_records[0].iid == 0x02
assert p.data_records[0].count == 0x01
assert p.data_records[0].vehicle_identification_numbers[0] == b'ABCDEFGHIJKLMNOPQ'
assert p.data_records[1].iid == 0x04
assert p.data_records[1].count == 0x01
assert p.data_records[1].calibration_identifications[0] == b'ABCDEFGHIJKLMNOP'
b = bytes(p)
assert b[0:1] == b'\x49'
assert b[1:2] == b'\x02'
assert b[2:3] == b'\x01'
assert b[3:20] == b'ABCDEFGHIJKLMNOPQ'
assert b[20:21] == b'\x04'
assert b[21:22] == b'\x01'
assert b[22:] == b'ABCDEFGHIJKLMNOP'
= Check building a request for Service 01 PID 00
p = OBD()/OBD_S01(pid=0x00)
b = bytes(p)
assert b[0:1] == b'\x01'
assert b[1:2] == b'\x00'
= Check building a request for Service 01 PID 75
p = OBD()/OBD_S01(pid=0x75)
b = bytes(p)
assert b[0:1] == b'\x01'
assert b[1:2] == b'\x75'
= Check building a request for Service 01 PID 78
p = OBD()/OBD_S01(pid=0x78)
b = bytes(p)
assert b[0:1] == b'\x01'
assert b[1:2] == b'\x78'
= Check building a request for Service 01 PID 7F
p = OBD()/OBD_S01(pid=0x7F)
b = bytes(p)
assert b[0:1] == b'\x01'
assert b[1:2] == b'\x7F'
= Check building a request for Service 01 PID 89
p = OBD()/OBD_S01(pid=0x89)
b = bytes(p)
assert b[0:1] == b'\x01'
assert b[1:2] == b'\x89'
= Check building a request for Service 02 PID 00
p = OBD()/OBD_S02(requests=[OBD_S02_Record(pid=0x00, frame_no=0x01)])
b = bytes(p)
assert b[0:1] == b'\x02'
assert b[1:2] == b'\x00'
assert b[2:3] == b'\x01'
= Check building a request for Service 02 PID 75
p = OBD()/OBD_S02(requests=[OBD_S02_Record(pid=0x75, frame_no=0x01)])
b = bytes(p)
assert b[0:1] == b'\x02'
assert b[1:2] == b'\x75'
assert b[2:3] == b'\x01'
= Check building a request for Service 02 PID 78
p = OBD()/OBD_S02(requests=[OBD_S02_Record(pid=0x78, frame_no=0x01)])
b = bytes(p)
assert b[0:1] == b'\x02'
assert b[1:2] == b'\x78'
assert b[2:3] == b'\x01'
= Check building a request for Service 02 PID 7F
p = OBD()/OBD_S02(requests=[OBD_S02_Record(pid=0x7F, frame_no=0x01)])
b = bytes(p)
assert b[0:1] == b'\x02'
assert b[1:2] == b'\x7F'
assert b[2:3] == b'\x01'
= Check building a request for Service 02 PID 89
p = OBD()/OBD_S02(requests=[OBD_S02_Record(pid=0x89, frame_no=0x01)])
b = bytes(p)
assert b[0:1] == b'\x02'
assert b[1:2] == b'\x89'
assert b[2:3] == b'\x01'
= Check building a request for Service 03
p = OBD()/OBD_S03()
assert p.service == 0x03
= Check building a request for Service 02 PID 7F
p = OBD()/OBD_S02(requests=[OBD_S02_Record(pid=0x7F, frame_no=0x01)])
b = bytes(p)
assert b[0:1] == b'\x02'
assert b[1:2] == b'\x7F'
assert b[2:3] == b'\x01'
= Check building a request for Service 09 IID 00
p = OBD()/OBD_S09(iid=0x00)
b = bytes(p)
assert b[0:1] == b'\x09'
assert b[1:2] == b'\x00'
= Check building a request for Service 09 IID 02
p = OBD()/OBD_S09(iid=0x02)
b = bytes(p)
assert b[0:1] == b'\x09'
assert b[1:2] == b'\x02'
= Check building a request for Service 09 IID 04
p = OBD()/OBD_S09(iid=0x04)
b = bytes(p)
assert b[0:1] == b'\x09'
assert b[1:2] == b'\x04'
= Check building a request for Service 09 IID 00 and IID 02 and IID 04
p = OBD()/OBD_S09(iid=[0x00, 0x02, 0x04])
b = bytes(p)
assert b[0:1] == b'\x09'
assert b[1:2] == b'\x00'
assert b[2:3] == b'\x02'
assert b[3:4] == b'\x04'
= Check building a request for Service 09 IID 0A
p = OBD()/OBD_S09(iid=0x0A)
b = bytes(p)
assert b[0:1] == b'\x09'
assert b[1:2] == b'\x0A'
= Check building a response for Service 03
p = OBD()/OBD_S03_PR(dtcs=[OBD_DTC(), OBD_DTC(location='Powertrain', code1=1, code2=3, code3=0, code4=1)])
b = bytes(p)
assert b[0:1] == b'\x43'
assert b[1:2] == b'\x02'
assert b[2:4] == b'\x00\x00'
assert b[4:6] == b'\x13\x01'
= Check building a default response for Service 03
p = OBD()/OBD_S03_PR()
b = bytes(p)
assert len(p) == 2
assert b[0:1] == b'\x43'
assert b[1:2] == b'\x00'
assert p.dtcs == []
= Check building a response for Service 07
p = OBD()/OBD_S07_PR(dtcs=[OBD_DTC(location='Chassis', code1=0, code2=5, code3=1, code4=0)])
b = bytes(p)
assert b[0:1] == b'\x47'
assert b[1:2] == b'\x01'
assert b[2:4] == b'\x45\x10'
= Check building a default response for Service 07
p = OBD()/OBD_S07_PR()
b = bytes(p)
assert len(p) == 2
assert b[0:1] == b'\x47'
assert b[1:2] == b'\x00'
assert p.dtcs == []
= Check building a response for Service 0A
p = OBD()/OBD_S0A_PR(dtcs=[OBD_DTC(), OBD_DTC(location='Body', code1=1, code2=7, code3=8, code4=2), OBD_DTC()])
b = bytes(p)
assert b[0:1] == b'\x4A'
assert b[1:2] == b'\x03'
assert b[2:4] == b'\x00\x00'
assert b[4:6] == b'\x97\x82'
assert b[6:8] == b'\x00\x00'
= Check building a default response for Service 0A
p = OBD()/OBD_S0A_PR()
b = bytes(p)
assert len(p) == 2
assert b[0:1] == b'\x4A'
assert b[1:2] == b'\x00'
assert p.dtcs == []
= Check building a response for Service 09 IID 00
p = OBD(service=0x49)/OBD_S02_PR(data_records=OBD_S09_PR_Record()/OBD_IID00(b'ABCD'))
b = bytes(p)
assert b[0:1] == b'\x49'
assert b[1:2] == b'\x00'
assert b[2:] == b'ABCD'
= Check building a response for Service 09 IID 02 with one VIN
p = OBD(service=0x49)/OBD_S02_PR(data_records=OBD_S09_PR_Record()/OBD_IID02(vehicle_identification_numbers=b'W0L000051T2123456'))
b = bytes(p)
assert b[0:1] == b'\x49'
assert b[1:2] == b'\x02'
assert b[2:3] == b'\x01'
assert b[3:] == b'W0L000051T2123456'
= Check building a response for Service 09 IID 02 with two VINs
p = OBD(service=0x49)/OBD_S02_PR(data_records=OBD_S09_PR_Record()/OBD_IID02(vehicle_identification_numbers=[b'W0L000051T2123456', b'W0L000051T2123456']))
b = bytes(p)
assert b[0:1] == b'\x49'
assert b[1:2] == b'\x02'
assert b[2:3] == b'\x02'
assert b[3:20] == b'W0L000051T2123456'
assert b[20:] == b'W0L000051T2123456'
= Check building a response for Service 09 IID 04 with one CID
p = OBD(service=0x49)/OBD_S02_PR(data_records=OBD_S09_PR_Record()/OBD_IID04(calibration_identifications=b'ABCDEFGHIJKLMNOP'))
b = bytes(p)
assert b[0:1] == b'\x49'
assert b[1:2] == b'\x04'
assert b[2:3] == b'\x01'
assert b[3:] == b'ABCDEFGHIJKLMNOP'
= Check building a response for Service 09 IID 04 with two CID
p = OBD(service=0x49)/OBD_S02_PR(data_records=OBD_S09_PR_Record()/OBD_IID04(calibration_identifications=[b'ABCDEFGHIJKLMNOP', b'ABCDEFGHIJKLMNOP']))
b = bytes(p)
assert b[0:1] == b'\x49'
assert b[1:2] == b'\x04'
assert b[2:3] == b'\x02'
assert b[3:19] == b'ABCDEFGHIJKLMNOP'
assert b[19:] == b'ABCDEFGHIJKLMNOP'
= Check building a response for Service 09 IID 0A
p = OBD(service=0x49)/OBD_S02_PR(data_records=OBD_S09_PR_Record()/OBD_IID0A(ecu_names=b'ABCDEFGHIJKLMNOPQRST'))
b = bytes(p)
assert b[0:1] == b'\x49'
assert b[1:2] == b'\x0A'
assert b[2:3] == b'\x01'
assert b[3:] == b'ABCDEFGHIJKLMNOPQRST'
= Check building a response for Service 09 IID 02 and IID 04
p = OBD(service=0x49)/OBD_S02_PR(data_records=[
OBD_S09_PR_Record()/OBD_IID02(vehicle_identification_numbers=b'ABCDEFGHIJKLMNOPQ'),
OBD_S09_PR_Record()/OBD_IID04(calibration_identifications=b'ABCDEFGHIJKLMNOP')
])
b = bytes(p)
assert b[0:1] == b'\x49'
assert b[1:2] == b'\x02'
assert b[2:3] == b'\x01'
assert b[3:20] == b'ABCDEFGHIJKLMNOPQ'
assert b[20:21] == b'\x04'
assert b[21:22] == b'\x01'
assert b[22:] == b'ABCDEFGHIJKLMNOP'

View file

@ -0,0 +1,14 @@
# This file is part of Scapy
# See http://www.secdev.org/projects/scapy for more information
# Copyright (C) Andreas Korb <andreas.d.korb@gmail.com>
# Copyright (C) Nils Weiss <nils@we155.de>
# This program is published under a GPLv2 license
# scapy.contrib.status = skip
from scapy.packet import Packet
class OBD_Packet(Packet):
def extract_padding(self, s):
return '', s

View file

@ -0,0 +1,12 @@
# This file is part of Scapy
# See http://www.secdev.org/projects/scapy for more information
# Copyright (C) Andreas Korb <andreas.d.korb@gmail.com>
# Copyright (C) Nils Weiss <nils@we155.de>
# This program is published under a GPLv2 license
# scapy.contrib.status = skip
"""
Package of contrib automotive obd specific modules
that have to be loaded explicitly.
"""

View file

@ -0,0 +1,390 @@
# This file is part of Scapy
# See http://www.secdev.org/projects/scapy for more information
# Copyright (C) Andreas Korb <andreas.d.korb@gmail.com>
# Copyright (C) Nils Weiss <nils@we155.de>
# This program is published under a GPLv2 license
# scapy.contrib.status = skip
from scapy.packet import Packet, bind_layers
from scapy.fields import PacketListField
from scapy.contrib.automotive.obd.services import OBD_S01, OBD_S02
from scapy.contrib.automotive.obd.pid.pids_00_1F import *
from scapy.contrib.automotive.obd.pid.pids_20_3F import *
from scapy.contrib.automotive.obd.pid.pids_40_5F import *
from scapy.contrib.automotive.obd.pid.pids_60_7F import *
from scapy.contrib.automotive.obd.pid.pids_80_9F import *
from scapy.contrib.automotive.obd.pid.pids_A0_C0 import *
class OBD_S01_PR_Record(Packet):
fields_desc = [
XByteField("pid", 0),
]
class OBD_S01_PR(Packet):
name = "Parameter IDs"
fields_desc = [
PacketListField("data_records", [], OBD_S01_PR_Record)
]
def answers(self, other):
return other.__class__ == OBD_S01 \
and all(r.pid in other.pid for r in self.data_records)
class OBD_S02_PR_Record(Packet):
fields_desc = [
XByteField("pid", 0),
XByteField("frame_no", 0),
]
class OBD_S02_PR(Packet):
name = "Parameter IDs"
fields_desc = [
PacketListField("data_records", [], OBD_S02_PR_Record)
]
def answers(self, other):
return other.__class__ == OBD_S02 \
and all(r.pid in [o.pid for o in other.requests]
for r in self.data_records)
bind_layers(OBD_S01_PR_Record, OBD_PID00, pid=0x00)
bind_layers(OBD_S01_PR_Record, OBD_PID01, pid=0x01)
bind_layers(OBD_S01_PR_Record, OBD_PID02, pid=0x02)
bind_layers(OBD_S01_PR_Record, OBD_PID03, pid=0x03)
bind_layers(OBD_S01_PR_Record, OBD_PID04, pid=0x04)
bind_layers(OBD_S01_PR_Record, OBD_PID05, pid=0x05)
bind_layers(OBD_S01_PR_Record, OBD_PID06, pid=0x06)
bind_layers(OBD_S01_PR_Record, OBD_PID07, pid=0x07)
bind_layers(OBD_S01_PR_Record, OBD_PID08, pid=0x08)
bind_layers(OBD_S01_PR_Record, OBD_PID09, pid=0x09)
bind_layers(OBD_S01_PR_Record, OBD_PID0A, pid=0x0A)
bind_layers(OBD_S01_PR_Record, OBD_PID0B, pid=0x0B)
bind_layers(OBD_S01_PR_Record, OBD_PID0C, pid=0x0C)
bind_layers(OBD_S01_PR_Record, OBD_PID0D, pid=0x0D)
bind_layers(OBD_S01_PR_Record, OBD_PID0E, pid=0x0E)
bind_layers(OBD_S01_PR_Record, OBD_PID0F, pid=0x0F)
bind_layers(OBD_S01_PR_Record, OBD_PID10, pid=0x10)
bind_layers(OBD_S01_PR_Record, OBD_PID11, pid=0x11)
bind_layers(OBD_S01_PR_Record, OBD_PID12, pid=0x12)
bind_layers(OBD_S01_PR_Record, OBD_PID13, pid=0x13)
bind_layers(OBD_S01_PR_Record, OBD_PID14, pid=0x14)
bind_layers(OBD_S01_PR_Record, OBD_PID15, pid=0x15)
bind_layers(OBD_S01_PR_Record, OBD_PID16, pid=0x16)
bind_layers(OBD_S01_PR_Record, OBD_PID17, pid=0x17)
bind_layers(OBD_S01_PR_Record, OBD_PID18, pid=0x18)
bind_layers(OBD_S01_PR_Record, OBD_PID19, pid=0x19)
bind_layers(OBD_S01_PR_Record, OBD_PID1A, pid=0x1A)
bind_layers(OBD_S01_PR_Record, OBD_PID1B, pid=0x1B)
bind_layers(OBD_S01_PR_Record, OBD_PID1C, pid=0x1C)
bind_layers(OBD_S01_PR_Record, OBD_PID1D, pid=0x1D)
bind_layers(OBD_S01_PR_Record, OBD_PID1E, pid=0x1E)
bind_layers(OBD_S01_PR_Record, OBD_PID1F, pid=0x1F)
bind_layers(OBD_S01_PR_Record, OBD_PID20, pid=0x20)
bind_layers(OBD_S01_PR_Record, OBD_PID21, pid=0x21)
bind_layers(OBD_S01_PR_Record, OBD_PID22, pid=0x22)
bind_layers(OBD_S01_PR_Record, OBD_PID23, pid=0x23)
bind_layers(OBD_S01_PR_Record, OBD_PID24, pid=0x24)
bind_layers(OBD_S01_PR_Record, OBD_PID25, pid=0x25)
bind_layers(OBD_S01_PR_Record, OBD_PID26, pid=0x26)
bind_layers(OBD_S01_PR_Record, OBD_PID27, pid=0x27)
bind_layers(OBD_S01_PR_Record, OBD_PID28, pid=0x28)
bind_layers(OBD_S01_PR_Record, OBD_PID29, pid=0x29)
bind_layers(OBD_S01_PR_Record, OBD_PID2A, pid=0x2A)
bind_layers(OBD_S01_PR_Record, OBD_PID2B, pid=0x2B)
bind_layers(OBD_S01_PR_Record, OBD_PID2C, pid=0x2C)
bind_layers(OBD_S01_PR_Record, OBD_PID2D, pid=0x2D)
bind_layers(OBD_S01_PR_Record, OBD_PID2E, pid=0x2E)
bind_layers(OBD_S01_PR_Record, OBD_PID2F, pid=0x2F)
bind_layers(OBD_S01_PR_Record, OBD_PID30, pid=0x30)
bind_layers(OBD_S01_PR_Record, OBD_PID31, pid=0x31)
bind_layers(OBD_S01_PR_Record, OBD_PID32, pid=0x32)
bind_layers(OBD_S01_PR_Record, OBD_PID33, pid=0x33)
bind_layers(OBD_S01_PR_Record, OBD_PID34, pid=0x34)
bind_layers(OBD_S01_PR_Record, OBD_PID35, pid=0x35)
bind_layers(OBD_S01_PR_Record, OBD_PID36, pid=0x36)
bind_layers(OBD_S01_PR_Record, OBD_PID37, pid=0x37)
bind_layers(OBD_S01_PR_Record, OBD_PID38, pid=0x38)
bind_layers(OBD_S01_PR_Record, OBD_PID39, pid=0x39)
bind_layers(OBD_S01_PR_Record, OBD_PID3A, pid=0x3A)
bind_layers(OBD_S01_PR_Record, OBD_PID3B, pid=0x3B)
bind_layers(OBD_S01_PR_Record, OBD_PID3C, pid=0x3C)
bind_layers(OBD_S01_PR_Record, OBD_PID3D, pid=0x3D)
bind_layers(OBD_S01_PR_Record, OBD_PID3E, pid=0x3E)
bind_layers(OBD_S01_PR_Record, OBD_PID3F, pid=0x3F)
bind_layers(OBD_S01_PR_Record, OBD_PID40, pid=0x40)
bind_layers(OBD_S01_PR_Record, OBD_PID41, pid=0x41)
bind_layers(OBD_S01_PR_Record, OBD_PID42, pid=0x42)
bind_layers(OBD_S01_PR_Record, OBD_PID43, pid=0x43)
bind_layers(OBD_S01_PR_Record, OBD_PID44, pid=0x44)
bind_layers(OBD_S01_PR_Record, OBD_PID45, pid=0x45)
bind_layers(OBD_S01_PR_Record, OBD_PID46, pid=0x46)
bind_layers(OBD_S01_PR_Record, OBD_PID47, pid=0x47)
bind_layers(OBD_S01_PR_Record, OBD_PID48, pid=0x48)
bind_layers(OBD_S01_PR_Record, OBD_PID49, pid=0x49)
bind_layers(OBD_S01_PR_Record, OBD_PID4A, pid=0x4A)
bind_layers(OBD_S01_PR_Record, OBD_PID4B, pid=0x4B)
bind_layers(OBD_S01_PR_Record, OBD_PID4C, pid=0x4C)
bind_layers(OBD_S01_PR_Record, OBD_PID4D, pid=0x4D)
bind_layers(OBD_S01_PR_Record, OBD_PID4E, pid=0x4E)
bind_layers(OBD_S01_PR_Record, OBD_PID4F, pid=0x4F)
bind_layers(OBD_S01_PR_Record, OBD_PID50, pid=0x50)
bind_layers(OBD_S01_PR_Record, OBD_PID51, pid=0x51)
bind_layers(OBD_S01_PR_Record, OBD_PID52, pid=0x52)
bind_layers(OBD_S01_PR_Record, OBD_PID53, pid=0x53)
bind_layers(OBD_S01_PR_Record, OBD_PID54, pid=0x54)
bind_layers(OBD_S01_PR_Record, OBD_PID55, pid=0x55)
bind_layers(OBD_S01_PR_Record, OBD_PID56, pid=0x56)
bind_layers(OBD_S01_PR_Record, OBD_PID57, pid=0x57)
bind_layers(OBD_S01_PR_Record, OBD_PID58, pid=0x58)
bind_layers(OBD_S01_PR_Record, OBD_PID59, pid=0x59)
bind_layers(OBD_S01_PR_Record, OBD_PID5A, pid=0x5A)
bind_layers(OBD_S01_PR_Record, OBD_PID5B, pid=0x5B)
bind_layers(OBD_S01_PR_Record, OBD_PID5C, pid=0x5C)
bind_layers(OBD_S01_PR_Record, OBD_PID5D, pid=0x5D)
bind_layers(OBD_S01_PR_Record, OBD_PID5E, pid=0x5E)
bind_layers(OBD_S01_PR_Record, OBD_PID5F, pid=0x5F)
bind_layers(OBD_S01_PR_Record, OBD_PID60, pid=0x60)
bind_layers(OBD_S01_PR_Record, OBD_PID61, pid=0x61)
bind_layers(OBD_S01_PR_Record, OBD_PID62, pid=0x62)
bind_layers(OBD_S01_PR_Record, OBD_PID63, pid=0x63)
bind_layers(OBD_S01_PR_Record, OBD_PID64, pid=0x64)
bind_layers(OBD_S01_PR_Record, OBD_PID65, pid=0x65)
bind_layers(OBD_S01_PR_Record, OBD_PID66, pid=0x66)
bind_layers(OBD_S01_PR_Record, OBD_PID67, pid=0x67)
bind_layers(OBD_S01_PR_Record, OBD_PID68, pid=0x68)
bind_layers(OBD_S01_PR_Record, OBD_PID69, pid=0x69)
bind_layers(OBD_S01_PR_Record, OBD_PID6A, pid=0x6A)
bind_layers(OBD_S01_PR_Record, OBD_PID6B, pid=0x6B)
bind_layers(OBD_S01_PR_Record, OBD_PID6C, pid=0x6C)
bind_layers(OBD_S01_PR_Record, OBD_PID6D, pid=0x6D)
bind_layers(OBD_S01_PR_Record, OBD_PID6E, pid=0x6E)
bind_layers(OBD_S01_PR_Record, OBD_PID6F, pid=0x6F)
bind_layers(OBD_S01_PR_Record, OBD_PID70, pid=0x70)
bind_layers(OBD_S01_PR_Record, OBD_PID71, pid=0x71)
bind_layers(OBD_S01_PR_Record, OBD_PID72, pid=0x72)
bind_layers(OBD_S01_PR_Record, OBD_PID73, pid=0x73)
bind_layers(OBD_S01_PR_Record, OBD_PID74, pid=0x74)
bind_layers(OBD_S01_PR_Record, OBD_PID75, pid=0x75)
bind_layers(OBD_S01_PR_Record, OBD_PID76, pid=0x76)
bind_layers(OBD_S01_PR_Record, OBD_PID77, pid=0x77)
bind_layers(OBD_S01_PR_Record, OBD_PID78, pid=0x78)
bind_layers(OBD_S01_PR_Record, OBD_PID79, pid=0x79)
bind_layers(OBD_S01_PR_Record, OBD_PID7A, pid=0x7A)
bind_layers(OBD_S01_PR_Record, OBD_PID7B, pid=0x7B)
bind_layers(OBD_S01_PR_Record, OBD_PID7C, pid=0x7C)
bind_layers(OBD_S01_PR_Record, OBD_PID7D, pid=0x7D)
bind_layers(OBD_S01_PR_Record, OBD_PID7E, pid=0x7E)
bind_layers(OBD_S01_PR_Record, OBD_PID7F, pid=0x7F)
bind_layers(OBD_S01_PR_Record, OBD_PID80, pid=0x80)
bind_layers(OBD_S01_PR_Record, OBD_PID81, pid=0x81)
bind_layers(OBD_S01_PR_Record, OBD_PID82, pid=0x82)
bind_layers(OBD_S01_PR_Record, OBD_PID83, pid=0x83)
bind_layers(OBD_S01_PR_Record, OBD_PID84, pid=0x84)
bind_layers(OBD_S01_PR_Record, OBD_PID85, pid=0x85)
bind_layers(OBD_S01_PR_Record, OBD_PID86, pid=0x86)
bind_layers(OBD_S01_PR_Record, OBD_PID87, pid=0x87)
bind_layers(OBD_S01_PR_Record, OBD_PID88, pid=0x88)
bind_layers(OBD_S01_PR_Record, OBD_PID89, pid=0x89)
bind_layers(OBD_S01_PR_Record, OBD_PID8A, pid=0x8A)
bind_layers(OBD_S01_PR_Record, OBD_PID8B, pid=0x8B)
bind_layers(OBD_S01_PR_Record, OBD_PID8C, pid=0x8C)
bind_layers(OBD_S01_PR_Record, OBD_PID8D, pid=0x8D)
bind_layers(OBD_S01_PR_Record, OBD_PID8E, pid=0x8E)
bind_layers(OBD_S01_PR_Record, OBD_PID8F, pid=0x8F)
bind_layers(OBD_S01_PR_Record, OBD_PID90, pid=0x90)
bind_layers(OBD_S01_PR_Record, OBD_PID91, pid=0x91)
bind_layers(OBD_S01_PR_Record, OBD_PID92, pid=0x92)
bind_layers(OBD_S01_PR_Record, OBD_PID93, pid=0x93)
bind_layers(OBD_S01_PR_Record, OBD_PID94, pid=0x94)
bind_layers(OBD_S01_PR_Record, OBD_PID98, pid=0x98)
bind_layers(OBD_S01_PR_Record, OBD_PID99, pid=0x99)
bind_layers(OBD_S01_PR_Record, OBD_PID9A, pid=0x9A)
bind_layers(OBD_S01_PR_Record, OBD_PID9B, pid=0x9B)
bind_layers(OBD_S01_PR_Record, OBD_PID9C, pid=0x9C)
bind_layers(OBD_S01_PR_Record, OBD_PID9D, pid=0x9D)
bind_layers(OBD_S01_PR_Record, OBD_PID9E, pid=0x9E)
bind_layers(OBD_S01_PR_Record, OBD_PID9F, pid=0x9F)
bind_layers(OBD_S01_PR_Record, OBD_PIDA0, pid=0xA0)
bind_layers(OBD_S01_PR_Record, OBD_PIDA1, pid=0xA1)
bind_layers(OBD_S01_PR_Record, OBD_PIDA2, pid=0xA2)
bind_layers(OBD_S01_PR_Record, OBD_PIDA3, pid=0xA3)
bind_layers(OBD_S01_PR_Record, OBD_PIDA4, pid=0xA4)
bind_layers(OBD_S01_PR_Record, OBD_PIDA5, pid=0xA5)
bind_layers(OBD_S01_PR_Record, OBD_PIDA6, pid=0xA6)
bind_layers(OBD_S01_PR_Record, OBD_PIDC0, pid=0xC0)
# Service 2
bind_layers(OBD_S02_PR_Record, OBD_PID00, pid=0x00)
bind_layers(OBD_S02_PR_Record, OBD_PID01, pid=0x01)
bind_layers(OBD_S02_PR_Record, OBD_PID02, pid=0x02)
bind_layers(OBD_S02_PR_Record, OBD_PID03, pid=0x03)
bind_layers(OBD_S02_PR_Record, OBD_PID04, pid=0x04)
bind_layers(OBD_S02_PR_Record, OBD_PID05, pid=0x05)
bind_layers(OBD_S02_PR_Record, OBD_PID06, pid=0x06)
bind_layers(OBD_S02_PR_Record, OBD_PID07, pid=0x07)
bind_layers(OBD_S02_PR_Record, OBD_PID08, pid=0x08)
bind_layers(OBD_S02_PR_Record, OBD_PID09, pid=0x09)
bind_layers(OBD_S02_PR_Record, OBD_PID0A, pid=0x0A)
bind_layers(OBD_S02_PR_Record, OBD_PID0B, pid=0x0B)
bind_layers(OBD_S02_PR_Record, OBD_PID0C, pid=0x0C)
bind_layers(OBD_S02_PR_Record, OBD_PID0D, pid=0x0D)
bind_layers(OBD_S02_PR_Record, OBD_PID0E, pid=0x0E)
bind_layers(OBD_S02_PR_Record, OBD_PID0F, pid=0x0F)
bind_layers(OBD_S02_PR_Record, OBD_PID10, pid=0x10)
bind_layers(OBD_S02_PR_Record, OBD_PID11, pid=0x11)
bind_layers(OBD_S02_PR_Record, OBD_PID12, pid=0x12)
bind_layers(OBD_S02_PR_Record, OBD_PID13, pid=0x13)
bind_layers(OBD_S02_PR_Record, OBD_PID14, pid=0x14)
bind_layers(OBD_S02_PR_Record, OBD_PID15, pid=0x15)
bind_layers(OBD_S02_PR_Record, OBD_PID16, pid=0x16)
bind_layers(OBD_S02_PR_Record, OBD_PID17, pid=0x17)
bind_layers(OBD_S02_PR_Record, OBD_PID18, pid=0x18)
bind_layers(OBD_S02_PR_Record, OBD_PID19, pid=0x19)
bind_layers(OBD_S02_PR_Record, OBD_PID1A, pid=0x1A)
bind_layers(OBD_S02_PR_Record, OBD_PID1B, pid=0x1B)
bind_layers(OBD_S02_PR_Record, OBD_PID1C, pid=0x1C)
bind_layers(OBD_S02_PR_Record, OBD_PID1D, pid=0x1D)
bind_layers(OBD_S02_PR_Record, OBD_PID1E, pid=0x1E)
bind_layers(OBD_S02_PR_Record, OBD_PID1F, pid=0x1F)
bind_layers(OBD_S02_PR_Record, OBD_PID20, pid=0x20)
bind_layers(OBD_S02_PR_Record, OBD_PID21, pid=0x21)
bind_layers(OBD_S02_PR_Record, OBD_PID22, pid=0x22)
bind_layers(OBD_S02_PR_Record, OBD_PID23, pid=0x23)
bind_layers(OBD_S02_PR_Record, OBD_PID24, pid=0x24)
bind_layers(OBD_S02_PR_Record, OBD_PID25, pid=0x25)
bind_layers(OBD_S02_PR_Record, OBD_PID26, pid=0x26)
bind_layers(OBD_S02_PR_Record, OBD_PID27, pid=0x27)
bind_layers(OBD_S02_PR_Record, OBD_PID28, pid=0x28)
bind_layers(OBD_S02_PR_Record, OBD_PID29, pid=0x29)
bind_layers(OBD_S02_PR_Record, OBD_PID2A, pid=0x2A)
bind_layers(OBD_S02_PR_Record, OBD_PID2B, pid=0x2B)
bind_layers(OBD_S02_PR_Record, OBD_PID2C, pid=0x2C)
bind_layers(OBD_S02_PR_Record, OBD_PID2D, pid=0x2D)
bind_layers(OBD_S02_PR_Record, OBD_PID2E, pid=0x2E)
bind_layers(OBD_S02_PR_Record, OBD_PID2F, pid=0x2F)
bind_layers(OBD_S02_PR_Record, OBD_PID30, pid=0x30)
bind_layers(OBD_S02_PR_Record, OBD_PID31, pid=0x31)
bind_layers(OBD_S02_PR_Record, OBD_PID32, pid=0x32)
bind_layers(OBD_S02_PR_Record, OBD_PID33, pid=0x33)
bind_layers(OBD_S02_PR_Record, OBD_PID34, pid=0x34)
bind_layers(OBD_S02_PR_Record, OBD_PID35, pid=0x35)
bind_layers(OBD_S02_PR_Record, OBD_PID36, pid=0x36)
bind_layers(OBD_S02_PR_Record, OBD_PID37, pid=0x37)
bind_layers(OBD_S02_PR_Record, OBD_PID38, pid=0x38)
bind_layers(OBD_S02_PR_Record, OBD_PID39, pid=0x39)
bind_layers(OBD_S02_PR_Record, OBD_PID3A, pid=0x3A)
bind_layers(OBD_S02_PR_Record, OBD_PID3B, pid=0x3B)
bind_layers(OBD_S02_PR_Record, OBD_PID3C, pid=0x3C)
bind_layers(OBD_S02_PR_Record, OBD_PID3D, pid=0x3D)
bind_layers(OBD_S02_PR_Record, OBD_PID3E, pid=0x3E)
bind_layers(OBD_S02_PR_Record, OBD_PID3F, pid=0x3F)
bind_layers(OBD_S02_PR_Record, OBD_PID40, pid=0x40)
bind_layers(OBD_S02_PR_Record, OBD_PID41, pid=0x41)
bind_layers(OBD_S02_PR_Record, OBD_PID42, pid=0x42)
bind_layers(OBD_S02_PR_Record, OBD_PID43, pid=0x43)
bind_layers(OBD_S02_PR_Record, OBD_PID44, pid=0x44)
bind_layers(OBD_S02_PR_Record, OBD_PID45, pid=0x45)
bind_layers(OBD_S02_PR_Record, OBD_PID46, pid=0x46)
bind_layers(OBD_S02_PR_Record, OBD_PID47, pid=0x47)
bind_layers(OBD_S02_PR_Record, OBD_PID48, pid=0x48)
bind_layers(OBD_S02_PR_Record, OBD_PID49, pid=0x49)
bind_layers(OBD_S02_PR_Record, OBD_PID4A, pid=0x4A)
bind_layers(OBD_S02_PR_Record, OBD_PID4B, pid=0x4B)
bind_layers(OBD_S02_PR_Record, OBD_PID4C, pid=0x4C)
bind_layers(OBD_S02_PR_Record, OBD_PID4D, pid=0x4D)
bind_layers(OBD_S02_PR_Record, OBD_PID4E, pid=0x4E)
bind_layers(OBD_S02_PR_Record, OBD_PID4F, pid=0x4F)
bind_layers(OBD_S02_PR_Record, OBD_PID50, pid=0x50)
bind_layers(OBD_S02_PR_Record, OBD_PID51, pid=0x51)
bind_layers(OBD_S02_PR_Record, OBD_PID52, pid=0x52)
bind_layers(OBD_S02_PR_Record, OBD_PID53, pid=0x53)
bind_layers(OBD_S02_PR_Record, OBD_PID54, pid=0x54)
bind_layers(OBD_S02_PR_Record, OBD_PID55, pid=0x55)
bind_layers(OBD_S02_PR_Record, OBD_PID56, pid=0x56)
bind_layers(OBD_S02_PR_Record, OBD_PID57, pid=0x57)
bind_layers(OBD_S02_PR_Record, OBD_PID58, pid=0x58)
bind_layers(OBD_S02_PR_Record, OBD_PID59, pid=0x59)
bind_layers(OBD_S02_PR_Record, OBD_PID5A, pid=0x5A)
bind_layers(OBD_S02_PR_Record, OBD_PID5B, pid=0x5B)
bind_layers(OBD_S02_PR_Record, OBD_PID5C, pid=0x5C)
bind_layers(OBD_S02_PR_Record, OBD_PID5D, pid=0x5D)
bind_layers(OBD_S02_PR_Record, OBD_PID5E, pid=0x5E)
bind_layers(OBD_S02_PR_Record, OBD_PID5F, pid=0x5F)
bind_layers(OBD_S02_PR_Record, OBD_PID60, pid=0x60)
bind_layers(OBD_S02_PR_Record, OBD_PID61, pid=0x61)
bind_layers(OBD_S02_PR_Record, OBD_PID62, pid=0x62)
bind_layers(OBD_S02_PR_Record, OBD_PID63, pid=0x63)
bind_layers(OBD_S02_PR_Record, OBD_PID64, pid=0x64)
bind_layers(OBD_S02_PR_Record, OBD_PID65, pid=0x65)
bind_layers(OBD_S02_PR_Record, OBD_PID66, pid=0x66)
bind_layers(OBD_S02_PR_Record, OBD_PID67, pid=0x67)
bind_layers(OBD_S02_PR_Record, OBD_PID68, pid=0x68)
bind_layers(OBD_S02_PR_Record, OBD_PID69, pid=0x69)
bind_layers(OBD_S02_PR_Record, OBD_PID6A, pid=0x6A)
bind_layers(OBD_S02_PR_Record, OBD_PID6B, pid=0x6B)
bind_layers(OBD_S02_PR_Record, OBD_PID6C, pid=0x6C)
bind_layers(OBD_S02_PR_Record, OBD_PID6D, pid=0x6D)
bind_layers(OBD_S02_PR_Record, OBD_PID6E, pid=0x6E)
bind_layers(OBD_S02_PR_Record, OBD_PID6F, pid=0x6F)
bind_layers(OBD_S02_PR_Record, OBD_PID70, pid=0x70)
bind_layers(OBD_S02_PR_Record, OBD_PID71, pid=0x71)
bind_layers(OBD_S02_PR_Record, OBD_PID72, pid=0x72)
bind_layers(OBD_S02_PR_Record, OBD_PID73, pid=0x73)
bind_layers(OBD_S02_PR_Record, OBD_PID74, pid=0x74)
bind_layers(OBD_S02_PR_Record, OBD_PID75, pid=0x75)
bind_layers(OBD_S02_PR_Record, OBD_PID76, pid=0x76)
bind_layers(OBD_S02_PR_Record, OBD_PID77, pid=0x77)
bind_layers(OBD_S02_PR_Record, OBD_PID78, pid=0x78)
bind_layers(OBD_S02_PR_Record, OBD_PID79, pid=0x79)
bind_layers(OBD_S02_PR_Record, OBD_PID7A, pid=0x7A)
bind_layers(OBD_S02_PR_Record, OBD_PID7B, pid=0x7B)
bind_layers(OBD_S02_PR_Record, OBD_PID7C, pid=0x7C)
bind_layers(OBD_S02_PR_Record, OBD_PID7D, pid=0x7D)
bind_layers(OBD_S02_PR_Record, OBD_PID7E, pid=0x7E)
bind_layers(OBD_S02_PR_Record, OBD_PID7F, pid=0x7F)
bind_layers(OBD_S02_PR_Record, OBD_PID80, pid=0x80)
bind_layers(OBD_S02_PR_Record, OBD_PID81, pid=0x81)
bind_layers(OBD_S02_PR_Record, OBD_PID82, pid=0x82)
bind_layers(OBD_S02_PR_Record, OBD_PID83, pid=0x83)
bind_layers(OBD_S02_PR_Record, OBD_PID84, pid=0x84)
bind_layers(OBD_S02_PR_Record, OBD_PID85, pid=0x85)
bind_layers(OBD_S02_PR_Record, OBD_PID86, pid=0x86)
bind_layers(OBD_S02_PR_Record, OBD_PID87, pid=0x87)
bind_layers(OBD_S02_PR_Record, OBD_PID88, pid=0x88)
bind_layers(OBD_S02_PR_Record, OBD_PID89, pid=0x89)
bind_layers(OBD_S02_PR_Record, OBD_PID8A, pid=0x8A)
bind_layers(OBD_S02_PR_Record, OBD_PID8B, pid=0x8B)
bind_layers(OBD_S02_PR_Record, OBD_PID8C, pid=0x8C)
bind_layers(OBD_S02_PR_Record, OBD_PID8D, pid=0x8D)
bind_layers(OBD_S02_PR_Record, OBD_PID8E, pid=0x8E)
bind_layers(OBD_S02_PR_Record, OBD_PID8F, pid=0x8F)
bind_layers(OBD_S02_PR_Record, OBD_PID90, pid=0x90)
bind_layers(OBD_S02_PR_Record, OBD_PID91, pid=0x91)
bind_layers(OBD_S02_PR_Record, OBD_PID92, pid=0x92)
bind_layers(OBD_S02_PR_Record, OBD_PID93, pid=0x93)
bind_layers(OBD_S02_PR_Record, OBD_PID94, pid=0x94)
bind_layers(OBD_S02_PR_Record, OBD_PID98, pid=0x98)
bind_layers(OBD_S02_PR_Record, OBD_PID99, pid=0x99)
bind_layers(OBD_S02_PR_Record, OBD_PID9A, pid=0x9A)
bind_layers(OBD_S02_PR_Record, OBD_PID9B, pid=0x9B)
bind_layers(OBD_S02_PR_Record, OBD_PID9C, pid=0x9C)
bind_layers(OBD_S02_PR_Record, OBD_PID9D, pid=0x9D)
bind_layers(OBD_S02_PR_Record, OBD_PID9E, pid=0x9E)
bind_layers(OBD_S02_PR_Record, OBD_PID9F, pid=0x9F)
bind_layers(OBD_S02_PR_Record, OBD_PIDA0, pid=0xA0)
bind_layers(OBD_S02_PR_Record, OBD_PIDA1, pid=0xA1)
bind_layers(OBD_S02_PR_Record, OBD_PIDA2, pid=0xA2)
bind_layers(OBD_S02_PR_Record, OBD_PIDA3, pid=0xA3)
bind_layers(OBD_S02_PR_Record, OBD_PIDA4, pid=0xA4)
bind_layers(OBD_S02_PR_Record, OBD_PIDA5, pid=0xA5)
bind_layers(OBD_S02_PR_Record, OBD_PIDA6, pid=0xA6)
bind_layers(OBD_S02_PR_Record, OBD_PIDC0, pid=0xC0)

View file

@ -0,0 +1,378 @@
# This file is part of Scapy
# See http://www.secdev.org/projects/scapy for more information
# Copyright (C) Andreas Korb <andreas.d.korb@gmail.com>
# Copyright (C) Nils Weiss <nils@we155.de>
# This program is published under a GPLv2 license
# scapy.contrib.status = skip
from scapy.fields import BitEnumField, BitField, ScalingField, \
FlagsField, XByteEnumField, PacketField
from scapy.contrib.automotive.obd.packet import OBD_Packet
from scapy.contrib.automotive.obd.services import OBD_DTC
# See https://en.wikipedia.org/wiki/OBD-II_PIDs for further information
# PID = Parameter IDentification
class OBD_PID00(OBD_Packet):
name = "PID_00_PIDsSupported"
fields_desc = [
FlagsField('supported_pids', b'', 32, [
'PID20',
'PID1F',
'PID1E',
'PID1D',
'PID1C',
'PID1B',
'PID1A',
'PID19',
'PID18',
'PID17',
'PID16',
'PID15',
'PID14',
'PID13',
'PID12',
'PID11',
'PID10',
'PID0F',
'PID0E',
'PID0D',
'PID0C',
'PID0B',
'PID0A',
'PID09',
'PID08',
'PID07',
'PID06',
'PID05',
'PID04',
'PID03',
'PID02',
'PID01'
])
]
class OBD_PID01(OBD_Packet):
name = "PID_01_MonitorStatusSinceDtcsCleared"
onOff = {
0: 'off',
1: 'on'
}
fields_desc = [
BitEnumField('mil', 0, 1, onOff),
BitField('dtc_count', 0, 7),
BitField('reserved1', 0, 1),
FlagsField('continuous_tests_ready', 0, 3, [
'misfire',
'fuelSystem',
'components'
]),
BitField('reserved2', 0, 1),
FlagsField('continuous_tests_supported', 0, 3, [
'misfire',
'fuel_system',
'components'
]),
FlagsField('once_per_trip_tests_supported', 0, 8, [
'egr',
'oxygenSensorHeater',
'oxygenSensor',
'acSystemRefrigerant',
'secondaryAirSystem',
'evaporativeSystem',
'heatedCatalyst',
'catalyst'
]),
FlagsField('once_per_trip_tests_ready', 0, 8, [
'egr',
'oxygenSensorHeater',
'oxygenSensor',
'acSystemRefrigerant',
'secondaryAirSystem',
'evaporativeSystem',
'heatedCatalyst',
'catalyst'
])
]
class OBD_PID02(OBD_Packet):
name = "PID_02_FreezeDtc"
fields_desc = [
PacketField('dtc', b'', OBD_DTC)
]
class OBD_PID03(OBD_Packet):
name = "PID_03_FuelSystemStatus"
loopStates = {
0x00: 'OpenLoopInsufficientEngineTemperature',
0x02: 'ClosedLoop',
0x04: 'OpenLoopEngineLoadOrFuelCut',
0x08: 'OpenLoopDueSystemFailure',
0x10: 'ClosedLoopWithFault'
}
fields_desc = [
XByteEnumField('fuel_system1', 0, loopStates),
XByteEnumField('fuel_system2', 0, loopStates)
]
class OBD_PID04(OBD_Packet):
name = "PID_04_CalculatedEngineLoad"
fields_desc = [
ScalingField('data', 0, scaling=100 / 255., unit="%")
]
class OBD_PID05(OBD_Packet):
name = "PID_05_EngineCoolantTemperature"
fields_desc = [
ScalingField('data', 0, unit="deg. C", offset=-40.0)
]
class OBD_PID06(OBD_Packet):
name = "PID_06_ShortTermFuelTrimBank1"
fields_desc = [
ScalingField('data', 0, scaling=100 / 128.,
unit="%", offset=-100.0)
]
class OBD_PID07(OBD_Packet):
name = "PID_07_LongTermFuelTrimBank1"
fields_desc = [
ScalingField('data', 0, scaling=100 / 128.,
unit="%", offset=-100.0)
]
class OBD_PID08(OBD_Packet):
name = "PID_08_ShortTermFuelTrimBank2"
fields_desc = [
ScalingField('data', 0, scaling=100 / 128.,
unit="%", offset=-100.0)
]
class OBD_PID09(OBD_Packet):
name = "PID_09_LongTermFuelTrimBank2"
fields_desc = [
ScalingField('data', 0, scaling=100 / 128.,
unit="%", offset=-100.0)
]
class OBD_PID0A(OBD_Packet):
name = "PID_0A_FuelPressure"
fields_desc = [
ScalingField('data', 0, scaling=3, unit="kPa")
]
class OBD_PID0B(OBD_Packet):
name = "PID_0B_IntakeManifoldAbsolutePressure"
fields_desc = [
ScalingField('data', 0, unit="kPa")
]
class OBD_PID0C(OBD_Packet):
name = "PID_0C_EngineRpm"
fields_desc = [
ScalingField('data', 0, scaling=1 / 4., unit="min-1", fmt="H")
]
class OBD_PID0D(OBD_Packet):
name = "PID_0D_VehicleSpeed"
fields_desc = [
ScalingField('data', 0, unit="km/h")
]
class OBD_PID0E(OBD_Packet):
name = "PID_0E_TimingAdvance"
fields_desc = [
ScalingField('data', 0, scaling=1 / 2., unit="deg.", offset=-64.0)
]
class OBD_PID0F(OBD_Packet):
name = "PID_0F_IntakeAirTemperature"
fields_desc = [
ScalingField('data', 0, unit="deg. C", offset=-40.0)
]
class OBD_PID10(OBD_Packet):
name = "PID_10_MafAirFlowRate"
fields_desc = [
ScalingField('data', 0, scaling=1 / 100., unit="g/s")
]
class OBD_PID11(OBD_Packet):
name = "PID_11_ThrottlePosition"
fields_desc = [
ScalingField('data', 0, scaling=100 / 255., unit="%")
]
class OBD_PID12(OBD_Packet):
name = "PID_12_CommandedSecondaryAirStatus"
states = {
0x00: 'upstream',
0x02: 'downstreamCatalyticConverter',
0x04: 'outsideAtmosphereOrOff',
0x08: 'pumpCommanded'
}
fields_desc = [
XByteEnumField('data', 0, states)
]
class OBD_PID13(OBD_Packet):
name = "PID_13_OxygenSensorsPresent"
fields_desc = [
FlagsField('sensors_present', b'', 8, [
'Bank1Sensor1',
'Bank1Sensor2',
'Bank1Sensor3',
'Bank1Sensor4',
'Bank2Sensor1',
'Bank2Sensor2',
'Bank2Sensor3',
'Bank2Sensor4'
])
]
class _OBD_PID14_1B(OBD_Packet):
fields_desc = [
ScalingField('outputVoltage', 0, scaling=0.005, unit="V"),
ScalingField('trim', 0, scaling=100 / 128.,
unit="%", offset=-100)
]
class OBD_PID14(_OBD_PID14_1B):
name = "PID_14_OxygenSensor1"
class OBD_PID15(_OBD_PID14_1B):
name = "PID_15_OxygenSensor2"
class OBD_PID16(_OBD_PID14_1B):
name = "PID_16_OxygenSensor3"
class OBD_PID17(_OBD_PID14_1B):
name = "PID_17_OxygenSensor4"
class OBD_PID18(_OBD_PID14_1B):
name = "PID_18_OxygenSensor5"
class OBD_PID19(_OBD_PID14_1B):
name = "PID_19_OxygenSensor6"
class OBD_PID1A(_OBD_PID14_1B):
name = "PID_1A_OxygenSensor7"
class OBD_PID1B(_OBD_PID14_1B):
name = "PID_1B_OxygenSensor8"
class OBD_PID1C(OBD_Packet):
name = "PID_1C_ObdStandardsThisVehicleConformsTo"
obdStandards = {
0x01: 'OBD-II as defined by the CARB',
0x02: 'OBD as defined by the EPA',
0x03: 'OBD and OBD-II',
0x04: 'OBD-I',
0x05: 'Not OBD compliant',
0x06: 'EOBD (Europe)',
0x07: 'EOBD and OBD-II',
0x08: 'EOBD and OBD',
0x09: 'EOBD, OBD and OBD II',
0x0A: 'JOBD (Japan)',
0x0B: 'JOBD and OBD II',
0x0C: 'JOBD and EOBD',
0x0D: 'JOBD, EOBD, and OBD II',
0x0E: 'Reserved',
0x0F: 'Reserved',
0x10: 'Reserved',
0x11: 'Engine Manufacturer Diagnostics (EMD)',
0x12: 'Engine Manufacturer Diagnostics Enhanced (EMD+)',
0x13: 'Heavy Duty On-Board Diagnostics (Child/Partial) (HD OBD-C)',
0x14: 'Heavy Duty On-Board Diagnostics (HD OBD)',
0x15: 'World Wide Harmonized OBD (WWH OBD)',
0x16: 'Reserved',
0x17: 'Heavy Duty Euro OBD Stage I without NOx control (HD EOBD-I)',
0x18: 'Heavy Duty Euro OBD Stage I with NOx control (HD EOBD-I N)',
0x19: 'Heavy Duty Euro OBD Stage II without NOx control (HD EOBD-II)',
0x1A: 'Heavy Duty Euro OBD Stage II with NOx control (HD EOBD-II N)',
0x1B: 'Reserved',
0x1C: 'Brazil OBD Phase 1 (OBDBr-1)',
0x1D: 'Brazil OBD Phase 2 (OBDBr-2)',
0x1E: 'Korean OBD (KOBD)',
0x1F: 'India OBD I (IOBD I)',
0x20: 'India OBD II (IOBD II)',
0x21: 'Heavy Duty Euro OBD Stage VI (HD EOBD-IV)',
}
fields_desc = [
XByteEnumField('data', 0, obdStandards)
]
class OBD_PID1D(OBD_Packet):
name = "PID_1D_OxygenSensorsPresent"
fields_desc = [
FlagsField('sensors_present', 0, 8, [
'Bank1Sensor1',
'Bank1Sensor2',
'Bank2Sensor1',
'Bank2Sensor2',
'Bank3Sensor1',
'Bank3Sensor2',
'Bank4Sensor1',
'Bank4Sensor2'
])
]
class OBD_PID1E(OBD_Packet):
name = "PID_1E_AuxiliaryInputStatus"
fields_desc = [
BitField('reserved', 0, 7),
BitEnumField('pto_status', 0, 1, OBD_PID01.onOff)
]
class OBD_PID1F(OBD_Packet):
name = "PID_1F_RunTimeSinceEngineStart"
fields_desc = [
ScalingField('data', 0, unit="s", fmt="H")
]

View file

@ -0,0 +1,242 @@
# This file is part of Scapy
# See http://www.secdev.org/projects/scapy for more information
# Copyright (C) Andreas Korb <andreas.d.korb@gmail.com>
# Copyright (C) Nils Weiss <nils@we155.de>
# This program is published under a GPLv2 license
# scapy.contrib.status = skip
from scapy.fields import FlagsField, ScalingField
from scapy.contrib.automotive.obd.packet import OBD_Packet
# See https://en.wikipedia.org/wiki/OBD-II_PIDs for further information
# PID = Parameter IDentification
class OBD_PID20(OBD_Packet):
name = "PID_20_PIDsSupported"
fields_desc = [
FlagsField('supported_pids', 0, 32, [
'PID40',
'PID3F',
'PID3E',
'PID3D',
'PID3C',
'PID3B',
'PID3A',
'PID39',
'PID38',
'PID37',
'PID36',
'PID35',
'PID34',
'PID33',
'PID32',
'PID31',
'PID30',
'PID2F',
'PID2E',
'PID2D',
'PID2C',
'PID2B',
'PID2A',
'PID29',
'PID28',
'PID27',
'PID26',
'PID25',
'PID24',
'PID23',
'PID22',
'PID21'
])
]
class OBD_PID21(OBD_Packet):
name = "PID_21_DistanceTraveledWithMalfunctionIndicatorLampOn"
fields_desc = [
ScalingField('data', 0, unit="km", fmt="H")
]
class OBD_PID22(OBD_Packet):
name = "PID_22_FuelRailPressure"
fields_desc = [
ScalingField('data', 0, scaling=0.079, unit="kPa", fmt="H")
]
class OBD_PID23(OBD_Packet):
name = "PID_23_FuelRailGaugePressure"
fields_desc = [
ScalingField('data', 0, scaling=10, unit="kPa", fmt="H")
]
class _OBD_PID24_2B(OBD_Packet):
fields_desc = [
ScalingField('equivalence_ratio', 0, scaling=0.0000305, fmt="H"),
ScalingField('voltage', 0, scaling=0.000122, unit="V", fmt="H")
]
class OBD_PID24(_OBD_PID24_2B):
name = "PID_24_OxygenSensor1"
class OBD_PID25(_OBD_PID24_2B):
name = "PID_25_OxygenSensor2"
class OBD_PID26(_OBD_PID24_2B):
name = "PID_26_OxygenSensor3"
class OBD_PID27(_OBD_PID24_2B):
name = "PID_27_OxygenSensor4"
class OBD_PID28(_OBD_PID24_2B):
name = "PID_28_OxygenSensor5"
class OBD_PID29(_OBD_PID24_2B):
name = "PID_29_OxygenSensor6"
class OBD_PID2A(_OBD_PID24_2B):
name = "PID_2A_OxygenSensor7"
class OBD_PID2B(_OBD_PID24_2B):
name = "PID_2B_OxygenSensor8"
class OBD_PID2C(OBD_Packet):
name = "PID_2C_CommandedEgr"
fields_desc = [
ScalingField('data', 0, scaling=100 / 255., unit="%")
]
class OBD_PID2D(OBD_Packet):
name = "PID_2D_EgrError"
fields_desc = [
ScalingField('data', 0, scaling=100 / 128.,
unit="%", offset=-100.0)
]
class OBD_PID2E(OBD_Packet):
name = "PID_2E_CommandedEvaporativePurge"
fields_desc = [
ScalingField('data', 0, scaling=100 / 255., unit="%")
]
class OBD_PID2F(OBD_Packet):
name = "PID_2F_FuelTankLevelInput"
fields_desc = [
ScalingField('data', 0, scaling=100 / 255., unit="%")
]
class OBD_PID30(OBD_Packet):
name = "PID_30_WarmUpsSinceCodesCleared"
fields_desc = [
ScalingField('data', 0)
]
class OBD_PID31(OBD_Packet):
name = "PID_31_DistanceTraveledSinceCodesCleared"
fields_desc = [
ScalingField('data', 0, unit="km", fmt="H")
]
class OBD_PID32(OBD_Packet):
name = "PID_32_EvapSystemVaporPressure"
fields_desc = [
ScalingField('data', 0, scaling=0.25, unit="Pa", fmt="h")
]
class OBD_PID33(OBD_Packet):
name = "PID_33_AbsoluteBarometricPressure"
fields_desc = [
ScalingField('data', 0, unit="kPa")
]
class _OBD_PID34_3B(OBD_Packet):
fields_desc = [
ScalingField('equivalence_ratio', 0, scaling=0.0000305, fmt="H"),
ScalingField('current', 0, scaling=0.00390625, unit="mA", fmt="H")
]
class OBD_PID34(_OBD_PID34_3B):
name = "PID_34_OxygenSensor1"
class OBD_PID35(_OBD_PID34_3B):
name = "PID_35_OxygenSensor2"
class OBD_PID36(_OBD_PID34_3B):
name = "PID_36_OxygenSensor3"
class OBD_PID37(_OBD_PID34_3B):
name = "PID_37_OxygenSensor4"
class OBD_PID38(_OBD_PID34_3B):
name = "PID_38_OxygenSensor5"
class OBD_PID39(_OBD_PID34_3B):
name = "PID_39_OxygenSensor6"
class OBD_PID3A(_OBD_PID34_3B):
name = "PID_3A_OxygenSensor7"
class OBD_PID3B(_OBD_PID34_3B):
name = "PID_3B_OxygenSensor8"
class OBD_PID3C(OBD_Packet):
name = "PID_3C_CatalystTemperatureBank1Sensor1"
fields_desc = [
ScalingField('data', 0, scaling=0.1, unit="deg. C",
offset=-40.0, fmt="H")
]
class OBD_PID3D(OBD_Packet):
name = "PID_3D_CatalystTemperatureBank2Sensor1"
fields_desc = [
ScalingField('data', 0, scaling=0.1, unit="deg. C",
offset=-40.0, fmt="H")
]
class OBD_PID3E(OBD_Packet):
name = "PID_3E_CatalystTemperatureBank1Sensor2"
fields_desc = [
ScalingField('data', 0, scaling=0.1, unit="deg. C",
offset=-40.0, fmt="H")
]
class OBD_PID3F(OBD_Packet):
name = "PID_3F_CatalystTemperatureBank2Sensor2"
fields_desc = [
ScalingField('data', 0, scaling=0.1, unit="deg. C",
offset=-40.0, fmt="H")
]

View file

@ -0,0 +1,335 @@
# This file is part of Scapy
# See http://www.secdev.org/projects/scapy for more information
# Copyright (C) Andreas Korb <andreas.d.korb@gmail.com>
# Copyright (C) Nils Weiss <nils@we155.de>
# This program is published under a GPLv2 license
# scapy.contrib.status = skip
from scapy.fields import ByteEnumField, BitField, FlagsField, XByteField, \
ScalingField, ThreeBytesField
from scapy.contrib.automotive.obd.packet import OBD_Packet
# See https://en.wikipedia.org/wiki/OBD-II_PIDs for further information
# PID = Parameter IDentification
class OBD_PID40(OBD_Packet):
name = "PID_40_PIDsSupported"
fields_desc = [
FlagsField('supported_pids', 0, 32, [
'PID60',
'PID5F',
'PID5E',
'PID5D',
'PID5C',
'PID5B',
'PID5A',
'PID59',
'PID58',
'PID57',
'PID56',
'PID55',
'PID54',
'PID53',
'PID52',
'PID51',
'PID50',
'PID4F',
'PID4E',
'PID4D',
'PID4C',
'PID4B',
'PID4A',
'PID49',
'PID48',
'PID47',
'PID46',
'PID45',
'PID44',
'PID43',
'PID42',
'PID41'
])
]
class OBD_PID41(OBD_Packet):
name = "PID_41_MonitorStatusThisDriveCycle"
onOff = {
0: 'off',
1: 'on'
}
fields_desc = [
XByteField('reserved', 0),
BitField('reserved1', 0, 1),
FlagsField('continuous_tests_ready', 0, 3, [
'misfire',
'fuelSystem',
'components'
]),
BitField('reserved2', 0, 1),
FlagsField('continuous_tests_supported', 0, 3, [
'misfire',
'fuelSystem',
'components'
]),
FlagsField('once_per_trip_tests_supported', 0, 8, [
'egr',
'oxygenSensorHeater',
'oxygenSensor',
'acSystemRefrigerant',
'secondaryAirSystem',
'evaporativeSystem',
'heatedCatalyst',
'catalyst'
]),
FlagsField('once_per_trip_tests_ready', 0, 8, [
'egr',
'oxygenSensorHeater',
'oxygenSensor',
'acSystemRefrigerant',
'secondaryAirSystem',
'evaporativeSystem',
'heatedCatalyst',
'catalyst'
])
]
class OBD_PID42(OBD_Packet):
name = "PID_42_ControlModuleVoltage"
fields_desc = [
ScalingField('data', 0, scaling=0.001, unit="V", fmt="H")
]
class OBD_PID43(OBD_Packet):
name = "PID_43_AbsoluteLoadValue"
fields_desc = [
ScalingField('data', 0, scaling=100 / 255., unit="%", fmt="H")
]
class OBD_PID44(OBD_Packet):
name = "PID_44_FuelAirCommandedEquivalenceRatio"
fields_desc = [
ScalingField('data', 0, scaling=0.0000305, fmt="H")
]
class _OBD_PercentPacket(OBD_Packet):
fields_desc = [
ScalingField('data', 0, scaling=100 / 255., unit="%")
]
class OBD_PID45(_OBD_PercentPacket):
name = "PID_45_RelativeThrottlePosition"
class OBD_PID46(OBD_Packet):
name = "PID_46_AmbientAirTemperature"
fields_desc = [
ScalingField('data', 0, unit="deg. C", offset=-40.0)
]
class OBD_PID47(_OBD_PercentPacket):
name = "PID_47_AbsoluteThrottlePositionB"
class OBD_PID48(_OBD_PercentPacket):
name = "PID_48_AbsoluteThrottlePositionC"
class OBD_PID49(_OBD_PercentPacket):
name = "PID_49_AcceleratorPedalPositionD"
class OBD_PID4A(_OBD_PercentPacket):
name = "PID_4A_AcceleratorPedalPositionE"
class OBD_PID4B(_OBD_PercentPacket):
name = "PID_4B_AcceleratorPedalPositionF"
class OBD_PID4C(_OBD_PercentPacket):
name = "PID_4C_CommandedThrottleActuator"
class OBD_PID4D(OBD_Packet):
name = "PID_4D_TimeRunWithMilOn"
fields_desc = [
ScalingField('data', 0, unit="min", fmt="H")
]
class OBD_PID4E(OBD_Packet):
name = "PID_4E_TimeSinceTroubleCodesCleared"
fields_desc = [
ScalingField('data', 0, unit="min", fmt="H")
]
class OBD_PID4F(OBD_Packet):
name = "PID_4F_VariousMaxValues"
fields_desc = [
ScalingField('equivalence_ratio', 0),
ScalingField('sensor_voltage', 0, unit="V"),
ScalingField('sensor_current', 0, unit="mA"),
ScalingField('intake_manifold_absolute_pressure', 0,
scaling=10, unit="kPa")
]
class OBD_PID50(OBD_Packet):
name = "PID_50_MaximumValueForAirFlowRateFromMassAirFlowSensor"
fields_desc = [
ScalingField('data', 0, scaling=10, unit="g/s"),
ThreeBytesField('reserved', 0)
]
class OBD_PID51(OBD_Packet):
name = "PID_51_FuelType"
fuelTypes = {
0: 'Not available',
1: 'Gasoline',
2: 'Methanol',
3: 'Ethanol',
4: 'Diesel',
5: 'LPG',
6: 'CNG',
7: 'Propane',
8: 'Electric',
9: 'Bifuel running Gasoline',
10: 'Bifuel running Methanol',
11: 'Bifuel running Ethanol',
12: 'Bifuel running LPG',
13: 'Bifuel running CNG',
14: 'Bifuel running Propane',
15: 'Bifuel running Electricity',
16: 'Bifuel running electric and combustion engine',
17: 'Hybrid gasoline',
18: 'Hybrid Ethanol',
19: 'Hybrid Diesel',
20: 'Hybrid Electric',
21: 'Hybrid running electric and combustion engine',
22: 'Hybrid Regenerative',
23: 'Bifuel running diesel'}
fields_desc = [
ByteEnumField('data', 0, fuelTypes)
]
class OBD_PID52(_OBD_PercentPacket):
name = "PID_52_EthanolFuel"
class OBD_PID53(OBD_Packet):
name = "PID_53_AbsoluteEvapSystemVaporPressure"
fields_desc = [
ScalingField('data', 0, scaling=1 / 200., unit="kPa", fmt="H")
]
class OBD_PID54(OBD_Packet):
name = "PID_54_EvapSystemVaporPressure"
fields_desc = [
ScalingField('data', 0, unit="Pa", fmt="h")
]
class _OBD_SensorTrimPacket1(OBD_Packet):
fields_desc = [
ScalingField('bank1', 0, scaling=100 / 128.,
offset=-100, unit="%"),
ScalingField('bank3', 0, scaling=100 / 128.,
offset=-100, unit="%")
]
class _OBD_SensorTrimPacket2(OBD_Packet):
fields_desc = [
ScalingField('bank2', 0, scaling=100 / 128.,
offset=-100, unit="%"),
ScalingField('bank4', 0, scaling=100 / 128.,
offset=-100, unit="%")
]
class OBD_PID55(_OBD_SensorTrimPacket1):
name = "PID_55_ShortTermSecondaryOxygenSensorTrim"
class OBD_PID56(_OBD_SensorTrimPacket1):
name = "PID_56_LongTermSecondaryOxygenSensorTrim"
class OBD_PID57(_OBD_SensorTrimPacket2):
name = "PID_57_ShortTermSecondaryOxygenSensorTrim"
class OBD_PID58(_OBD_SensorTrimPacket2):
name = "PID_58_LongTermSecondaryOxygenSensorTrim"
class OBD_PID59(OBD_Packet):
name = "PID_59_FuelRailAbsolutePressure"
fields_desc = [
ScalingField('data', 0, scaling=10, unit="kPa", fmt="H")
]
class OBD_PID5A(_OBD_PercentPacket):
name = "PID_5A_RelativeAcceleratorPedalPosition"
class OBD_PID5B(_OBD_PercentPacket):
name = "PID_5B_HybridBatteryPackRemainingLife"
class OBD_PID5C(OBD_Packet):
name = "PID_5C_EngineOilTemperature"
fields_desc = [
ScalingField('data', 0, unit="deg. C", offset=-40.0)
]
class OBD_PID5D(OBD_Packet):
name = "PID_5D_FuelInjectionTiming"
fields_desc = [
ScalingField('data', 0, scaling=1 / 128., offset=-210,
unit="deg.", fmt="H")
]
class OBD_PID5E(OBD_Packet):
name = "PID_5E_EngineFuelRate"
fields_desc = [
ScalingField('data', 0, scaling=0.05, unit="L/h", fmt="H")
]
class OBD_PID5F(OBD_Packet):
name = "PID_5F_EmissionRequirementsToWhichVehicleIsDesigned"
emissionRequirementTypes = {
0xE: 'Heavy Duty Vehicles (EURO IV) B1',
0xF: 'Heavy Duty Vehicles (EURO V) B2',
0x10: 'Heavy Duty Vehicles (EURO EEV) C',
}
fields_desc = [
ByteEnumField('data', 0, emissionRequirementTypes)
]

View file

@ -0,0 +1,501 @@
# This file is part of Scapy
# See http://www.secdev.org/projects/scapy for more information
# Copyright (C) Andreas Korb <andreas.d.korb@gmail.com>
# Copyright (C) Nils Weiss <nils@we155.de>
# This program is published under a GPLv2 license
# scapy.contrib.status = skip
from scapy.fields import BitField, FlagsField, ScalingField
from scapy.contrib.automotive.obd.packet import OBD_Packet
# See https://en.wikipedia.org/wiki/OBD-II_PIDs for further information
# PID = Parameter IDentification
class OBD_PID60(OBD_Packet):
name = "PID_60_PIDsSupported"
fields_desc = [
FlagsField('supported_pids', 0, 32, [
'PID80',
'PID7F',
'PID7E',
'PID7D',
'PID7C',
'PID7B',
'PID7A',
'PID79',
'PID78',
'PID77',
'PID76',
'PID75',
'PID74',
'PID73',
'PID72',
'PID71',
'PID70',
'PID6F',
'PID6E',
'PID6D',
'PID6C',
'PID6B',
'PID6A',
'PID69',
'PID68',
'PID67',
'PID66',
'PID65',
'PID64',
'PID63',
'PID62',
'PID61'
])
]
class OBD_PID61(OBD_Packet):
name = "PID_61_DriverSDemandEnginePercentTorque"
fields_desc = [
ScalingField('data', 0, unit="%", offset=-125.0)
]
class OBD_PID62(OBD_Packet):
name = "PID_62_ActualEnginePercentTorque"
fields_desc = [
ScalingField('data', 0, unit="%", offset=-125.0)
]
class OBD_PID63(OBD_Packet):
name = "PID_63_EngineReferenceTorque"
fields_desc = [
ScalingField('data', 0, unit="Nm", fmt="H")
]
class OBD_PID64(OBD_Packet):
name = "PID_64_EnginePercentTorqueData"
fields_desc = [
ScalingField('at_point1', 0, unit="%", offset=-125.0),
ScalingField('at_point2', 0, unit="%", offset=-125.0),
ScalingField('at_point3', 0, unit="%", offset=-125.0),
ScalingField('at_point4', 0, unit="%", offset=-125.0),
ScalingField('at_point5', 0, unit="%", offset=-125.0)
]
class OBD_PID65(OBD_Packet):
name = "PID_65_AuxiliaryInputOutputSupported"
fields_desc = [
BitField('reserved1', 0, 4),
BitField('glow_plug_lamp_status_supported', 0, 1),
BitField('manual_trans_neutral_drive_status_supported', 0, 1),
BitField('auto_trans_neutral_drive_status_supported', 0, 1),
BitField('power_take_off_status_supported', 0, 1),
BitField('reserved2', 0, 4),
BitField('glow_plug_lamp_status', 0, 1),
BitField('manual_trans_neutral_drive_status', 0, 1),
BitField('auto_trans_neutral_drive_status', 0, 1),
BitField('power_take_off_status', 0, 1),
]
class OBD_PID66(OBD_Packet):
name = "PID_66_MassAirFlowSensor"
fields_desc = [
BitField('reserved', 0, 6),
BitField('sensor_b_supported', 0, 1),
BitField('sensor_a_supported', 0, 1),
ScalingField('sensor_a', 0, scaling=0.03125, unit="g/s", fmt="H"),
ScalingField('sensor_b', 0, scaling=0.03125, unit="g/s", fmt="H"),
]
class OBD_PID67(OBD_Packet):
name = "PID_67_EngineCoolantTemperature"
fields_desc = [
BitField('reserved', 0, 6),
BitField('sensor2_supported', 0, 1),
BitField('sensor1_supported', 0, 1),
ScalingField('sensor1', 0, unit="deg. C", offset=-40.0),
ScalingField('sensor2', 0, unit="deg. C", offset=-40.0)
]
class OBD_PID68(OBD_Packet):
name = "PID_68_IntakeAirTemperatureSensor"
fields_desc = [
BitField('reserved', 0, 2),
BitField('bank2_sensor3_supported', 0, 1),
BitField('bank2_sensor2_supported', 0, 1),
BitField('bank2_sensor1_supported', 0, 1),
BitField('bank1_sensor3_supported', 0, 1),
BitField('bank1_sensor2_supported', 0, 1),
BitField('bank1_sensor1_supported', 0, 1),
ScalingField('bank1_sensor1', 0, unit="deg. C", offset=-40),
ScalingField('bank1_sensor2', 0, unit="deg. C", offset=-40),
ScalingField('bank1_sensor3', 0, unit="deg. C", offset=-40),
ScalingField('bank2_sensor1', 0, unit="deg. C", offset=-40),
ScalingField('bank2_sensor2', 0, unit="deg. C", offset=-40),
ScalingField('bank2_sensor3', 0, unit="deg. C", offset=-40)
]
class OBD_PID69(OBD_Packet):
name = "PID_69_CommandedEgrAndEgrError"
fields_desc = [
BitField('reserved', 0, 2),
BitField('egr_b_error_supported', 0, 1),
BitField('actual_egr_b_duty_cycle_supported', 0, 1),
BitField('commanded_egr_b_duty_cycle_supported', 0, 1),
BitField('egr_a_error_supported', 0, 1),
BitField('actual_egr_a_duty_cycle_supported', 0, 1),
BitField('commanded_egr_a_duty_cycle_supported', 0, 1),
ScalingField('commanded_egr_a_duty_cycle', 0, scaling=100 / 255.,
unit="%"),
ScalingField('actual_egr_a_duty_cycle', 0, scaling=100 / 255.,
unit="%"),
ScalingField('egr_a_error', 0, scaling=100 / 128., unit="%",
offset=-100),
ScalingField('commanded_egr_b_duty_cycle', 0, scaling=100 / 255.,
unit="%"),
ScalingField('actual_egr_b_duty_cycle', 0, scaling=100 / 255.,
unit="%"),
ScalingField('egr_b_error', 0, scaling=100 / 128., unit="%",
offset=-100),
]
class OBD_PID6A(OBD_Packet):
name = "PID_6A_CommandedDieselIntakeAirFlowControl" \
"AndRelativeIntakeAirFlowPosition"
fields_desc = [
BitField('reserved', 0, 4),
BitField('relative_intake_air_flow_b_position_supported', 0, 1),
BitField('commanded_intake_air_flow_b_control_supported', 0, 1),
BitField('relative_intake_air_flow_a_position_supported', 0, 1),
BitField('commanded_intake_air_flow_a_control_supported', 0, 1),
ScalingField('commanded_intake_air_flow_a_control', 0,
scaling=100 / 255., unit="%"),
ScalingField('relative_intake_air_flow_a_position', 0,
scaling=100 / 255., unit="%"),
ScalingField('commanded_intake_air_flow_b_control', 0,
scaling=100 / 255., unit="%"),
ScalingField('relative_intake_air_flow_b_position', 0,
scaling=100 / 255., unit="%"),
]
class OBD_PID6B(OBD_Packet):
name = "PID_6B_ExhaustGasRecirculationTemperature"
fields_desc = [
BitField('reserved', 0, 4),
BitField('bank2_sensor2_supported', 0, 1),
BitField('bank2_sensor1_supported', 0, 1),
BitField('bank1_sensor2_supported', 0, 1),
BitField('bank1_sensor1_supported', 0, 1),
ScalingField('bank1_sensor1', 0, unit="deg. C", offset=-40),
ScalingField('bank1_sensor2', 0, unit="deg. C", offset=-40),
ScalingField('bank2_sensor1', 0, unit="deg. C", offset=-40),
ScalingField('bank2_sensor2', 0, unit="deg. C", offset=-40),
]
class OBD_PID6C(OBD_Packet):
name = "PID_6C_CommandedThrottleActuatorControlAndRelativeThrottlePosition"
fields_desc = [
BitField('reserved', 0, 4),
BitField('relative_throttle_b_position_supported', 0, 1),
BitField('commanded_throttle_actuator_b_control_supported', 0, 1),
BitField('relative_throttle_a_position_supported', 0, 1),
BitField('commanded_throttle_actuator_a_control_supported', 0, 1),
ScalingField('commanded_throttle_actuator_a_control', 0,
scaling=100 / 255., unit="%"),
ScalingField('relative_throttle_a_position', 0,
scaling=100 / 255., unit="%"),
ScalingField('commanded_throttle_actuator_b_control', 0,
scaling=100 / 255., unit="%"),
ScalingField('relative_throttle_b_position', 0,
scaling=100 / 255., unit="%"),
]
class OBD_PID6D(OBD_Packet):
name = "PID_6D_FuelPressureControlSystem"
fields_desc = [
BitField('reserved', 0, 5),
BitField('fuel_temperature_supported', 0, 1),
BitField('fuel_rail_pressure_supported', 0, 1),
BitField('commanded_fuel_rail_pressure_supported', 0, 1),
ScalingField('commanded_fuel_rail_pressure', 0, scaling=10, unit="kPa",
fmt='H'),
ScalingField('fuel_rail_pressure', 0, scaling=10, unit="kPa",
fmt='H'),
ScalingField('fuel_rail_temperature', 0, unit="deg. C", offset=-40)
]
class OBD_PID6E(OBD_Packet):
name = "PID_6E_InjectionPressureControlSystem"
fields_desc = [
BitField('reserved', 0, 6),
BitField('injection_control_pressure_supported', 0, 1),
BitField('commanded_injection_control_pressure_supported', 0, 1),
ScalingField('commanded_injection_control_pressure', 0, scaling=10,
unit="kPa", fmt='H'),
ScalingField('injection_control_pressure', 0, scaling=10,
unit="kPa", fmt='H'),
]
class OBD_PID6F(OBD_Packet):
name = "PID_6F_TurbochargerCompressorInletPressure"
fields_desc = [
BitField('reserved', 0, 6),
BitField('sensor_b_supported', 0, 1),
BitField('sensor_a_supported', 0, 1),
ScalingField('sensor_a', 0, unit="kPa"),
ScalingField('sensor_b', 0, unit="kPa"),
]
class OBD_PID70(OBD_Packet):
name = "PID_70_BoostPressureControl"
fields_desc = [
BitField('reserved', 0, 4),
BitField('boost_pressure_sensor_b_supported', 0, 1),
BitField('commanded_boost_pressure_b_supported', 0, 1),
BitField('boost_pressure_sensor_a_supported', 0, 1),
BitField('commanded_boost_pressure_a_supported', 0, 1),
ScalingField('commanded_boost_pressure_a', 0, scaling=0.03125,
unit="kPa", fmt='H'),
ScalingField('boost_pressure_sensor_a', 0, scaling=0.03125,
unit="kPa", fmt='H'),
ScalingField('commanded_boost_pressure_b', 0, scaling=0.03125,
unit="kPa", fmt='H'),
ScalingField('boost_pressure_sensor_b', 0, scaling=0.03125,
unit="kPa", fmt='H'),
]
class OBD_PID71(OBD_Packet):
name = "PID_71_VariableGeometryTurboControl"
fields_desc = [
BitField('reserved', 0, 4),
BitField('vgt_b_position_supported', 0, 1),
BitField('commanded_vgt_b_position_supported', 0, 1),
BitField('vgt_a_position_supported', 0, 1),
BitField('commanded_vgt_a_position_supported', 0, 1),
ScalingField('commanded_variable_geometry_turbo_a_position', 0,
scaling=100 / 255., unit="%"),
ScalingField('variable_geometry_turbo_a_position', 0,
scaling=100 / 255., unit="%"),
ScalingField('commanded_variable_geometry_turbo_b_position', 0,
scaling=100 / 255., unit="%"),
ScalingField('variable_geometry_turbo_b_position', 0,
scaling=100 / 255., unit="%"),
]
class OBD_PID72(OBD_Packet):
name = "PID_72_WastegateControl"
fields_desc = [
BitField('reserved', 0, 4),
BitField('wastegate_b_position_supported', 0, 1),
BitField('commanded_wastegate_b_position_supported', 0, 1),
BitField('wastegate_a_position_supported', 0, 1),
BitField('commanded_wastegate_a_position_supported', 0, 1),
ScalingField('commanded_wastegate_a_position', 0,
scaling=100 / 255., unit="%"),
ScalingField('wastegate_a_position', 0,
scaling=100 / 255., unit="%"),
ScalingField('commanded_wastegate_b_position', 0,
scaling=100 / 255., unit="%"),
ScalingField('wastegate_b_position', 0,
scaling=100 / 255., unit="%"),
]
class OBD_PID73(OBD_Packet):
name = "PID_73_ExhaustPressure"
fields_desc = [
BitField('reserved', 0, 6),
BitField('sensor_bank2_supported', 0, 1),
BitField('sensor_bank1_supported', 0, 1),
ScalingField('sensor_bank1', 0, scaling=0.01, unit="kPa", fmt='H'),
ScalingField('sensor_bank2', 0, scaling=0.01, unit="kPa", fmt='H'),
]
class OBD_PID74(OBD_Packet):
name = "PID_74_TurbochargerRpm"
fields_desc = [
BitField('reserved', 0, 6),
BitField('b_supported', 0, 1),
BitField('a_supported', 0, 1),
ScalingField('a_rpm', 0, unit="min-1", fmt='H'),
ScalingField('b_rpm', 0, unit="min-1", fmt='H'),
]
class OBD_PID75(OBD_Packet):
name = "PID_75_TurbochargerATemperature"
fields_desc = [
BitField('reserved', 0, 4),
BitField('turbo_a_turbine_outlet_temperature_supported', 0, 1),
BitField('turbo_a_turbine_inlet_temperature_supported', 0, 1),
BitField('turbo_a_compressor_outlet_temperature_supported', 0, 1),
BitField('turbo_a_compressor_inlet_temperature_supported', 0, 1),
ScalingField('turbocharger_a_compressor_inlet_temperature', 0,
unit="deg. C", offset=-40),
ScalingField('turbocharger_a_compressor_outlet_temperature', 0,
unit="deg. C", offset=-40),
ScalingField('turbocharger_a_turbine_inlet_temperature', 0,
unit="deg. C", offset=-40, fmt='H',
scaling=0.1),
ScalingField('turbocharger_a_turbine_outlet_temperature', 0,
unit="deg. C", offset=-40, fmt='H',
scaling=0.1),
]
class OBD_PID76(OBD_Packet):
name = "PID_76_TurbochargerBTemperature"
fields_desc = [
BitField('reserved', 0, 4),
BitField('turbo_a_turbine_outlet_temperature_supported', 0, 1),
BitField('turbo_a_turbine_inlet_temperature_supported', 0, 1),
BitField('turbo_a_compressor_outlet_temperature_supported', 0, 1),
BitField('turbo_a_compressor_inlet_temperature_supported', 0, 1),
ScalingField('turbocharger_a_compressor_inlet_temperature', 0,
unit="deg. C", offset=-40),
ScalingField('turbocharger_a_compressor_outlet_temperature', 0,
unit="deg. C", offset=-40),
ScalingField('turbocharger_a_turbine_inlet_temperature', 0,
unit="deg. C", offset=-40, fmt='H',
scaling=0.1),
ScalingField('turbocharger_a_turbine_outlet_temperature', 0,
unit="deg. C", offset=-40, fmt='H',
scaling=0.1),
]
class OBD_PID77(OBD_Packet):
name = "PID_77_ChargeAirCoolerTemperature"
fields_desc = [
BitField('reserved', 0, 4),
BitField('bank2_sensor2_supported', 0, 1),
BitField('bank2_sensor1_supported', 0, 1),
BitField('bank1_sensor2_supported', 0, 1),
BitField('bank1_sensor1_supported', 0, 1),
ScalingField('bank1_sensor1', 0, unit="deg. C", offset=-40),
ScalingField('bank1_sensor2', 0, unit="deg. C", offset=-40),
ScalingField('bank2_sensor1', 0, unit="deg. C", offset=-40),
ScalingField('bank2_sensor2', 0, unit="deg. C", offset=-40),
]
class _OBD_PID_ExhaustGasTemperatureBank(OBD_Packet):
fields_desc = [
BitField('reserved', 0, 4),
BitField('sensor4_supported', 0, 1),
BitField('sensor3_supported', 0, 1),
BitField('sensor2_supported', 0, 1),
BitField('sensor1_supported', 0, 1),
ScalingField('sensor1', 0, unit="deg. C", offset=-40,
scaling=0.1, fmt='H'),
ScalingField('sensor2', 0, unit="deg. C", offset=-40,
scaling=0.1, fmt='H'),
ScalingField('sensor3', 0, unit="deg. C", offset=-40,
scaling=0.1, fmt='H'),
ScalingField('sensor4', 0, unit="deg. C", offset=-40,
scaling=0.1, fmt='H'),
]
class OBD_PID78(_OBD_PID_ExhaustGasTemperatureBank):
name = "PID_78_ExhaustGasTemperatureBank1"
class OBD_PID79(_OBD_PID_ExhaustGasTemperatureBank):
name = "PID_79_ExhaustGasTemperatureBank2"
class _OBD_PID_DieselParticulateFilter(OBD_Packet):
fields_desc = [
BitField('reserved', 0, 5),
BitField('outlet_pressure_supported', 0, 1),
BitField('inlet_pressure_supported', 0, 1),
BitField('delta_pressure_supported', 0, 1),
ScalingField('delta_pressure', 0,
unit='kPa', offset=-327.68, scaling=0.01, fmt='H'),
ScalingField('particulate_filter', 0,
unit='kPa', scaling=0.01, fmt='H'),
ScalingField('outlet_pressure', 0,
unit='kPa', scaling=0.01, fmt='H'),
]
class OBD_PID7A(_OBD_PID_DieselParticulateFilter):
name = "PID_7A_DieselParticulateFilter1"
class OBD_PID7B(_OBD_PID_DieselParticulateFilter):
name = "PID_7B_DieselParticulateFilter2"
class OBD_PID7C(OBD_Packet):
name = "PID_7C_DieselParticulateFilterTemperature"
fields_desc = [
BitField('reserved', 0, 4),
BitField('bank2_outlet_temperature_supported', 0, 1),
BitField('bank2_inlet_temperature_supported', 0, 1),
BitField('bank1_outlet_temperature_supported', 0, 1),
BitField('bank1_inlet_temperature_supported', 0, 1),
ScalingField('bank1_inlet_temperature_sensor', 0,
unit="deg. C", offset=-40, scaling=0.1, fmt='H'),
ScalingField('bank1_outlet_temperature_sensor', 0,
unit="deg. C", offset=-40, scaling=0.1, fmt='H'),
ScalingField('bank2_inlet_temperature_sensor', 0,
unit="deg. C", offset=-40, scaling=0.1, fmt='H'),
ScalingField('bank2_outlet_temperature_sensor', 0,
unit="deg. C", offset=-40, scaling=0.1, fmt='H'),
]
class OBD_PID7D(OBD_Packet):
name = "PID_7D_NoxNteControlAreaStatus"
fields_desc = [
BitField('reserved', 0, 4),
BitField('nte_deficiency_for_nox_active_area', 0, 1),
BitField('inside_manufacturer_specific_nox_nte_carve_out_area', 0, 1),
BitField('outside', 0, 1),
BitField('inside', 0, 1),
]
class OBD_PID7E(OBD_Packet):
name = "PID_7E_PmNteControlAreaStatus"
fields_desc = [
BitField('reserved', 0, 4),
BitField('nte_deficiency_for_pm_active_area', 0, 1),
BitField('inside_manufacturer_specific_pm_nte_carve_out_area', 0, 1),
BitField('outside', 0, 1),
BitField('inside', 0, 1),
]
class OBD_PID7F(OBD_Packet):
name = "PID_7F_EngineRunTime"
fields_desc = [
BitField('reserved', 0, 5),
BitField('total_with_pto_active_supported', 0, 1),
BitField('total_idle_supported', 0, 1),
BitField('total_supported', 0, 1),
ScalingField('total', 0, unit='sec', fmt='Q'),
ScalingField('total_idle', 0, unit='sec', fmt='Q'),
ScalingField('total_with_pto_active', 0, unit='sec', fmt='Q'),
]

View file

@ -0,0 +1,287 @@
# This file is part of Scapy
# See http://www.secdev.org/projects/scapy for more information
# Copyright (C) Andreas Korb <andreas.d.korb@gmail.com>
# Copyright (C) Nils Weiss <nils@we155.de>
# This program is published under a GPLv2 license
# scapy.contrib.status = skip
from scapy.fields import StrFixedLenField, FlagsField, ScalingField, BitField
from scapy.contrib.automotive.obd.packet import OBD_Packet
# See https://en.wikipedia.org/wiki/OBD-II_PIDs for further information
# PID = Parameter IDentification
class OBD_PID80(OBD_Packet):
name = "PID_80_PIDsSupported"
fields_desc = [
FlagsField('supported_pids', 0, 32, [
'PIDA0',
'PID9F',
'PID9E',
'PID9D',
'PID9C',
'PID9B',
'PID9A',
'PID99',
'PID98',
'PID97',
'PID96',
'PID95',
'PID94',
'PID93',
'PID92',
'PID91',
'PID90',
'PID8F',
'PID8E',
'PID8D',
'PID8C',
'PID8B',
'PID8A',
'PID89',
'PID88',
'PID87',
'PID86',
'PID85',
'PID84',
'PID83',
'PID82',
'PID81'
])
]
class OBD_PID81(OBD_Packet):
name = "PID_81_EngineRunTimeForAuxiliaryEmissionsControlDevice"
fields_desc = [
BitField('reserved', 0, 3),
BitField('total_run_time_with_ei_aecd5_supported', 0, 1),
BitField('total_run_time_with_ei_aecd4_supported', 0, 1),
BitField('total_run_time_with_ei_aecd3_supported', 0, 1),
BitField('total_run_time_with_ei_aecd2_supported', 0, 1),
BitField('total_run_time_with_ei_aecd1_supported', 0, 1),
ScalingField('total_run_time_with_ei_aecd1', 0, unit='sec',
fmt='Q'),
ScalingField('total_run_time_with_ei_aecd2', 0, unit='sec',
fmt='Q'),
ScalingField('total_run_time_with_ei_aecd3', 0, unit='sec',
fmt='Q'),
ScalingField('total_run_time_with_ei_aecd4', 0, unit='sec',
fmt='Q'),
ScalingField('total_run_time_with_ei_aecd5', 0, unit='sec',
fmt='Q'),
]
class OBD_PID82(OBD_Packet):
name = "PID_82_EngineRunTimeForAuxiliaryEmissionsControlDevice"
fields_desc = [
BitField('reserved', 0, 3),
BitField('total_run_time_with_ei_aecd10_supported', 0, 1),
BitField('total_run_time_with_ei_aecd9_supported', 0, 1),
BitField('total_run_time_with_ei_aecd8_supported', 0, 1),
BitField('total_run_time_with_ei_aecd7_supported', 0, 1),
BitField('total_run_time_with_ei_aecd6_supported', 0, 1),
ScalingField('total_run_time_with_ei_aecd6', 0, unit='sec',
fmt='Q'),
ScalingField('total_run_time_with_ei_aecd7', 0, unit='sec',
fmt='Q'),
ScalingField('total_run_time_with_ei_aecd8', 0, unit='sec',
fmt='Q'),
ScalingField('total_run_time_with_ei_aecd9', 0, unit='sec',
fmt='Q'),
ScalingField('total_run_time_with_ei_aecd10', 0, unit='sec',
fmt='Q'),
]
class OBD_PID83(OBD_Packet):
name = "PID_83_NOxSensor"
fields_desc = [
BitField('reserved', 0, 6),
BitField('nox_sensor_concentration_bank2_sensor1_supported', 0, 1),
BitField('nox_sensor_concentration_bank1_sensor1_supported', 0, 1),
ScalingField('nox_sensor_concentration_bank1_sensor1', 0, unit='ppm',
fmt='H'),
ScalingField('nox_sensor_concentration_bank2_sensor1', 0, unit='ppm',
fmt='H'),
]
class OBD_PID84(OBD_Packet):
name = "PID_84_ManifoldSurfaceTemperature"
fields_desc = [
StrFixedLenField('data', b'', 1)
]
class OBD_PID85(OBD_Packet):
name = "PID_85_NoxReagentSystem"
fields_desc = [
StrFixedLenField('data', b'', 10)
]
class OBD_PID86(OBD_Packet):
name = "PID_86_ParticulateMatterSensor"
fields_desc = [
StrFixedLenField('data', b'', 5)
]
class OBD_PID87(OBD_Packet):
name = "PID_87_IntakeManifoldAbsolutePressure"
fields_desc = [
StrFixedLenField('data', b'', 5)
]
class OBD_PID88(OBD_Packet):
name = "PID_88_ScrInduceSystem"
fields_desc = [
StrFixedLenField('data', b'', 13)
]
class OBD_PID89(OBD_Packet):
# 11 - 15
name = "PID_89_RunTimeForAecd"
fields_desc = [
StrFixedLenField('data', b'', 41)
]
class OBD_PID8A(OBD_Packet):
# 16 - 20
name = "PID_8A_RunTimeForAecd"
fields_desc = [
StrFixedLenField('data', b'', 41)
]
class OBD_PID8B(OBD_Packet):
name = "PID_8B_DieselAftertreatment"
fields_desc = [
StrFixedLenField('data', b'', 7)
]
class OBD_PID8C(OBD_Packet):
name = "PID_8C_O2Sensor"
fields_desc = [
StrFixedLenField('data', b'', 16)
]
class OBD_PID8D(OBD_Packet):
name = "PID_8D_ThrottlePositionG"
fields_desc = [
StrFixedLenField('data', b'', 1)
]
class OBD_PID8E(OBD_Packet):
name = "PID_8E_EngineFrictionPercentTorque"
fields_desc = [
StrFixedLenField('data', b'', 1)
]
class OBD_PID8F(OBD_Packet):
name = "PID_8F_PmSensorBank1And2"
fields_desc = [
StrFixedLenField('data', b'', 5)
]
class OBD_PID90(OBD_Packet):
name = "PID_90_WwhObdVehicleObdSystemInformation"
fields_desc = [
StrFixedLenField('data', b'', 3)
]
class OBD_PID91(OBD_Packet):
name = "PID_91_WwhObdVehicleObdSystemInformation"
fields_desc = [
StrFixedLenField('data', b'', 5)
]
class OBD_PID92(OBD_Packet):
name = "PID_92_FuelSystemControl"
fields_desc = [
StrFixedLenField('data', b'', 2)
]
class OBD_PID93(OBD_Packet):
name = "PID_93_WwhObdVehicleObdCountersSupport"
fields_desc = [
StrFixedLenField('data', b'', 3)
]
class OBD_PID94(OBD_Packet):
name = "PID_94_NoxWarningAndInducementSystem"
fields_desc = [
StrFixedLenField('data', b'', 12)
]
class OBD_PID98(OBD_Packet):
name = "PID_98_ExhaustGasTemperatureSensor"
fields_desc = [
StrFixedLenField('data', b'', 9)
]
class OBD_PID99(OBD_Packet):
name = "PID_99_ExhaustGasTemperatureSensor"
fields_desc = [
StrFixedLenField('data', b'', 9)
]
class OBD_PID9A(OBD_Packet):
name = "PID_9A_HybridEvVehicleSystemDataBatteryVoltage"
fields_desc = [
StrFixedLenField('data', b'', 6)
]
class OBD_PID9B(OBD_Packet):
name = "PID_9B_DieselExhaustFluidSensorData"
fields_desc = [
StrFixedLenField('data', b'', 4)
]
class OBD_PID9C(OBD_Packet):
name = "PID_9C_O2SensorData"
fields_desc = [
StrFixedLenField('data', b'', 17)
]
class OBD_PID9D(OBD_Packet):
name = "PID_9D_EngineFuelRate"
fields_desc = [
StrFixedLenField('data', b'', 4)
]
class OBD_PID9E(OBD_Packet):
name = "PID_9E_EngineExhaustFlowRate"
fields_desc = [
StrFixedLenField('data', b'', 2)
]
class OBD_PID9F(OBD_Packet):
name = "PID_9F_FuelSystemPercentageUse"
fields_desc = [
StrFixedLenField('data', b'', 9)
]

View file

@ -0,0 +1,135 @@
# This file is part of Scapy
# See http://www.secdev.org/projects/scapy for more information
# Copyright (C) Andreas Korb <andreas.d.korb@gmail.com>
# Copyright (C) Nils Weiss <nils@we155.de>
# This program is published under a GPLv2 license
# scapy.contrib.status = skip
from scapy.fields import StrFixedLenField, FlagsField
from scapy.contrib.automotive.obd.packet import OBD_Packet
# See https://en.wikipedia.org/wiki/OBD-II_PIDs for further information
# PID = Parameter IDentification
class OBD_PIDA0(OBD_Packet):
name = "PID_A0_PIDsSupported"
fields_desc = [
FlagsField('supported_pids', 0, 32, [
'PIDC0',
'PIDBF',
'PIDBE',
'PIDBD',
'PIDBC',
'PIDBB',
'PIDBA',
'PIDB9',
'PIDB8',
'PIDB7',
'PIDB6',
'PIDB5',
'PIDB4',
'PIDB3',
'PIDB2',
'PIDB1',
'PIDB0',
'PIDAF',
'PIDAE',
'PIDAD',
'PIDAC',
'PIDAB',
'PIDAA',
'PIDA9',
'PIDA8',
'PIDA7',
'PIDA6',
'PIDA5',
'PIDA4',
'PIDA3',
'PIDA2',
'PIDA1'
])
]
class OBD_PIDA1(OBD_Packet):
name = "PID_A1_NoxSensorCorrectedData"
fields_desc = [
StrFixedLenField('data', b'', 9)
]
class OBD_PIDA2(OBD_Packet):
name = "PID_A2_CylinderFuelRate"
fields_desc = [
StrFixedLenField('data', b'', 2)
]
class OBD_PIDA3(OBD_Packet):
name = "PID_A3_EvapSystemVaporPressure"
fields_desc = [
StrFixedLenField('data', b'', 9)
]
class OBD_PIDA4(OBD_Packet):
name = "PID_A4_TransmissionActualGear"
fields_desc = [
StrFixedLenField('data', b'', 4)
]
class OBD_PIDA5(OBD_Packet):
name = "PID_A5_DieselExhaustFluidDosing"
fields_desc = [
StrFixedLenField('data', b'', 4)
]
class OBD_PIDA6(OBD_Packet):
name = "PID_A6_Odometer"
fields_desc = [
StrFixedLenField('data', b'', 4)
]
class OBD_PIDC0(OBD_Packet):
name = "PID_C0_PIDsSupported"
fields_desc = [
FlagsField('supported_pids', 0, 32, [
'PIDE0',
'PIDDF',
'PIDDE',
'PIDDD',
'PIDDC',
'PIDDB',
'PIDDA',
'PIDD9',
'PIDD8',
'PIDD7',
'PIDD6',
'PIDD5',
'PIDD4',
'PIDD3',
'PIDD2',
'PIDD1',
'PIDD0',
'PIDCF',
'PIDCE',
'PIDCD',
'PIDCC',
'PIDCB',
'PIDCA',
'PIDC9',
'PIDC8',
'PIDC7',
'PIDC6',
'PIDC5',
'PIDC4',
'PIDC3',
'PIDC2',
'PIDC1'
])
]

View file

@ -0,0 +1,231 @@
# This file is part of Scapy
# See http://www.secdev.org/projects/scapy for more information
# Copyright (C) Andreas Korb <andreas.korb@e-mundo.de>
# Copyright (C) Friedrich Feigel <friedrich.feigel@e-mundo.de>
# This program is published under a GPLv2 license
# scapy.contrib.description = OnBoardDiagnosticScanner
# scapy.contrib.status = loads
# XXX TODO This file contains illegal E501 issues D:
from scapy.compat import chb
from scapy.contrib.automotive.obd.obd import OBD, OBD_S03, OBD_S07, OBD_S0A, \
OBD_S01, OBD_S06, OBD_S08, OBD_S09
def _supported_id_numbers(socket, timeout, service_class, id_name, verbose):
""" Check which Parameter IDs are supported by the vehicle
Args:
socket: is the ISOTPSocket, over which the OBD-Services communicate.
the id 0x7df acts as a broadcast address for all obd-supporting ECUs.
timeout: only required for the OBD Simulator, since it might tell it
supports a PID, while it actually doesn't and won't respond to this PID.
If this happens with a real ECU, it is an implementation error.
service_class: specifies, which OBD-Service should be queried.
id_name: describes the car domain (e.g.: mid = IDs in Motor Domain).
verbose: specifies, whether the sr1()-method gives feedback or not.
This method sends a query message via a ISOTPSocket, which will be responded by the ECUs with
a message containing Bits, representing whether a PID is supported by the vehicle's protocol implementation or not.
The first Message has the PID 0x00 and contains 32 Bits, which indicate by their index and value, which PIDs are
supported.
If the PID 0x20 is supported, that means, there are more supported PIDs within the next 32 PIDs, which will result
in a new query message being sent, that contains the next 32 Bits.
There is a maximum of 256 possible PIDs.
The supported PIDs will be returned as set.
"""
supported_id_numbers = set()
supported_prop = 'supported_' + id_name + 's'
# ID 0x00 requests the first range of supported IDs in OBD
supported_ids_req = OBD() / service_class(b'\x00')
while supported_ids_req is not None:
resp = socket.sr1(supported_ids_req, timeout=timeout, verbose=verbose)
# If None, the device did not respond.
# Usually only occurs, if device is off.
if resp is None or resp.service == 0x7f:
break
supported_ids_req = None
all_supported_in_range = getattr(resp.data_records[0], supported_prop)
for supported in all_supported_in_range:
id_number = int(supported[-2:], 16)
supported_id_numbers.add(id_number)
# send a new query if the next PID range is supported
if id_number % 0x20 == 0:
supported_ids_req = OBD() / service_class(chb(id_number))
return supported_id_numbers
def _scan_id_service(socket, timeout, service_class, id_numbers, verbose):
""" Queries certain PIDs and stores their return value
Args:
socket: is the ISOTPSocket, over which the OBD-Services communicate.
the id 0x7df acts as a broadcast address for all obd-supporting ECUs.
timeout: only required for the OBD Simulator, since it might tell it
supports a PID, while it actually doesn't and won't respond to this PID.
If this happens with a real ECU, it is an implementation error.
service_class: specifies, which OBD-Service should be queried.
id_numbers: a set of PIDs, which should be queried by the method.
verbose: specifies, whether the sr1()-method gives feedback or not.
This method queries the specified id_numbers and stores their responses in a dictionary, which is then returned.
"""
data = dict()
for id_number in id_numbers:
id_byte = chb(id_number)
# assemble request packet
pkt = OBD() / service_class(id_byte)
resp = socket.sr1(pkt, timeout=timeout, verbose=verbose)
if resp is not None:
data[id_number] = bytes(resp)
return data
def _scan_dtc_service(socket, timeout, service_class, verbose):
""" Queries Diagnostic Trouble Code Parameters and stores their return value
Args:
socket: is the ISOTPSocket, over which the OBD-Services communicate.
the id 0x7df acts as a broadcast address for all obd-supporting ECUs.
timeout: only required for the OBD Simulator, since it might tell it
supports a PID, while it actually doesn't and won't respond to this PID.
If this happens with a real ECU, it is an implementation error.
service_class: specifies, which OBD-Service should be queried.
verbose: specifies, whether the sr1()-method gives feedback or not.
This method queries the specified Diagnostic Trouble Code Parameters and stores their responses in a dictionary,
which is then returned.
"""
req = OBD() / service_class()
resp = socket.sr1(req, timeout=timeout, verbose=verbose)
if resp is not None:
return bytes(resp)
def obd_scan(socket, timeout=0.1, supported_ids=False,
unsupported_ids=False, verbose=False):
""" Scans for all accessible information of each commonly used OBD service classes and prints the results
Args:
socket: is the ISOTPSocket, over which the OBD-Services communicate.
the id 0x7df acts as a broadcast address for all obd-supporting ECUs.
timeout: only required for the OBD Simulator, since it might tell it
supports a PID, while it actually doesn't and won't respond to this PID.
If this happens with a real ECU, it is an implementation error.
supported_ids: specifies, whether to check for supported Parameter IDs.
The OBD-Protocol offers querying, which PIDs the implemented ECUs support.
unsupported_ids: specifies, whether to check for unsupported or hidden Parameter IDs.
There is a possibility of PIDs answering, which are addressed directly, but which are
not listed in the supported query response. We call these PIDs unsupported PIDs, because
they are seemingly unsupported.
verbose: specifies, whether the sr1()-method gives feedback or not and turns.
This method queries the Diagnostic Trouble Code Parameters and if selected, supported and/or unsupported PIDS and
prints the results.
"""
dtc = dict()
supported = dict()
unsupported = dict()
if verbose:
print("\nStarting OBD-Scan...")
print("\nScanning Diagnostic Trouble Codes:")
# Emission-related DTCs
dtc[3] = _scan_dtc_service(socket, timeout, OBD_S03, verbose)
# Emission-related DTCs detected during current or last completed driving
# cycle
dtc[7] = _scan_dtc_service(socket, timeout, OBD_S07, verbose)
# Permanent DTCs
dtc[10] = _scan_dtc_service(socket, timeout, OBD_S0A, verbose)
print("Service 3:")
print(dtc[3])
print("Service 7:")
print(dtc[7])
print("Service 10:")
print(dtc[10])
if not supported_ids and not unsupported_ids:
return dtc
# Powertrain
supported_ids_s01 = _supported_id_numbers(
socket, timeout, OBD_S01, 'pid', verbose)
# On-board monitoring test results for non-continuously monitored systems
supported_ids_s06 = _supported_id_numbers(
socket, timeout, OBD_S06, 'mid', verbose)
# Control of on-board system, test or component
supported_ids_s08 = _supported_id_numbers(
socket, timeout, OBD_S08, 'tid', verbose)
# On-board monitoring test results for non-continuously monitored systems
supported_ids_s09 = _supported_id_numbers(
socket, timeout, OBD_S09, 'iid', verbose)
if supported_ids:
print("\nScanning supported Parameter IDs")
supported[1] = _scan_id_service(
socket, timeout, OBD_S01, supported_ids_s01, verbose)
supported[6] = _scan_id_service(
socket, timeout, OBD_S06, supported_ids_s06, verbose)
supported[8] = _scan_id_service(
socket, timeout, OBD_S08, supported_ids_s08, verbose)
supported[9] = _scan_id_service(
socket, timeout, OBD_S09, supported_ids_s09, verbose)
print("\nSupported PIDs of Service 1:")
print(supported[1])
print("Supported PIDs of Service 6:")
print(supported[6])
print("Supported PIDs of Service 8:")
print(supported[8])
print("Supported PIDs of Service 9:")
# this option will slow down the test a lot, since it tests for seemingly unsupported ids
# the chances of those actually responding will be small, so a lot of
# timeouts can be expected
if unsupported_ids:
# the complete id range is from 1 to 255
all_ids_set = set(range(1, 256))
# the unsupported id ranges are obtained by creating the compliment set
# excluding 0
unsupported_ids_s01 = all_ids_set - supported_ids_s01
unsupported_ids_s06 = all_ids_set - supported_ids_s06
unsupported_ids_s08 = all_ids_set - supported_ids_s08
unsupported_ids_s09 = all_ids_set - supported_ids_s09
print("\nScanning unsupported Parameter IDs")
if verbose:
print("This may take a while...")
unsupported[1] = _scan_id_service(
socket, timeout, OBD_S01, unsupported_ids_s01, verbose)
unsupported[6] = _scan_id_service(
socket, timeout, OBD_S06, unsupported_ids_s06, verbose)
unsupported[8] = _scan_id_service(
socket, timeout, OBD_S08, unsupported_ids_s08, verbose)
unsupported[9] = _scan_id_service(
socket, timeout, OBD_S09, unsupported_ids_s09, verbose)
print("\nUnsupported PIDs of Service 1:")
print(unsupported[1])
print("Unsupported PIDs of Service 6:")
print(unsupported[6])
print("unsupported PIDs of Service 8:")
print(unsupported[8])
print("Unsupported PIDs of Service 9:")
print(unsupported[9])
return dtc, supported, unsupported

View file

@ -0,0 +1,153 @@
# This file is part of Scapy
# See http://www.secdev.org/projects/scapy for more information
# Copyright (C) Andreas Korb <andreas.d.korb@gmail.com>
# Copyright (C) Nils Weiss <nils@we155.de>
# This program is published under a GPLv2 license
# scapy.contrib.status = skip
from scapy.fields import ByteField, XByteField, BitEnumField, \
PacketListField, XBitField, XByteEnumField, FieldListField, FieldLenField
from scapy.packet import Packet
from scapy.contrib.automotive.obd.packet import OBD_Packet
from scapy.config import conf
class OBD_DTC(OBD_Packet):
name = "DiagnosticTroubleCode"
locations = {
0b00: 'Powertrain',
0b01: 'Chassis',
0b10: 'Body',
0b11: 'Network',
}
fields_desc = [
BitEnumField('location', 0, 2, locations),
XBitField('code1', 0, 2),
XBitField('code2', 0, 4),
XBitField('code3', 0, 4),
XBitField('code4', 0, 4),
]
class OBD_NR(Packet):
name = "NegativeResponse"
responses = {
0x10: 'generalReject',
0x11: 'serviceNotSupported',
0x12: 'subFunctionNotSupported-InvalidFormat',
0x21: 'busy-RepeatRequest',
0x22: 'conditionsNotCorrectOrRequestSequenceError',
0x78: 'requestCorrectlyReceived-ResponsePending'
}
fields_desc = [
XByteField('request_service_id', 0),
XByteEnumField('response_code', 0, responses)
]
def answers(self, other):
return self.request_service_id == other.service and \
(self.response_code != 0x78 or
conf.contribs['OBD']['treat-response-pending-as-answer'])
class OBD_S01(Packet):
name = "S1_CurrentData"
fields_desc = [
FieldListField("pid", [], XByteField('', 0))
]
class OBD_S02_Record(OBD_Packet):
fields_desc = [
XByteField('pid', 0),
ByteField('frame_no', 0)
]
class OBD_S02(Packet):
name = "S2_FreezeFrameData"
fields_desc = [
PacketListField("requests", [], OBD_S02_Record)
]
class OBD_S03(Packet):
name = "S3_RequestDTCs"
class OBD_S03_PR(Packet):
name = "S3_ResponseDTCs"
fields_desc = [
FieldLenField('count', None, count_of='dtcs', fmt='B'),
PacketListField('dtcs', [], OBD_DTC, count_from=lambda pkt: pkt.count)
]
def answers(self, other):
return other.__class__ == OBD_S03
class OBD_S04(Packet):
name = "S4_ClearDTCs"
class OBD_S04_PR(Packet):
name = "S4_ClearDTCsPositiveResponse"
def answers(self, other):
return other.__class__ == OBD_S04
class OBD_S06(Packet):
name = "S6_OnBoardDiagnosticMonitoring"
fields_desc = [
FieldListField("mid", [], XByteField('', 0))
]
class OBD_S07(Packet):
name = "S7_RequestPendingDTCs"
class OBD_S07_PR(Packet):
name = "S7_ResponsePendingDTCs"
fields_desc = [
FieldLenField('count', None, count_of='dtcs', fmt='B'),
PacketListField('dtcs', [], OBD_DTC, count_from=lambda pkt: pkt.count)
]
def answers(self, other):
return other.__class__ == OBD_S07
class OBD_S08(Packet):
name = "S8_RequestControlOfSystem"
fields_desc = [
FieldListField("tid", [], XByteField('', 0))
]
class OBD_S09(Packet):
name = "S9_VehicleInformation"
fields_desc = [
FieldListField("iid", [], XByteField('', 0))
]
class OBD_S0A(Packet):
name = "S0A_RequestPermanentDTCs"
class OBD_S0A_PR(Packet):
name = "S0A_ResponsePermanentDTCs"
fields_desc = [
FieldLenField('count', None, count_of='dtcs', fmt='B'),
PacketListField('dtcs', [], OBD_DTC, count_from=lambda pkt: pkt.count)
]
def answers(self, other):
return other.__class__ == OBD_S0A

View file

@ -0,0 +1,12 @@
# This file is part of Scapy
# See http://www.secdev.org/projects/scapy for more information
# Copyright (C) Andreas Korb <andreas.d.korb@gmail.com>
# Copyright (C) Nils Weiss <nils@we155.de>
# This program is published under a GPLv2 license
# scapy.contrib.status = skip
"""
Package of contrib automotive obd specific modules
that have to be loaded explicitly.
"""

View file

@ -0,0 +1,153 @@
# This file is part of Scapy
# See http://www.secdev.org/projects/scapy for more information
# Copyright (C) Andreas Korb <andreas.d.korb@gmail.com>
# Copyright (C) Nils Weiss <nils@we155.de>
# This program is published under a GPLv2 license
# scapy.contrib.status = skip
from scapy.fields import FlagsField, ByteField, ScalingField, PacketListField
from scapy.packet import bind_layers, Packet
from scapy.contrib.automotive.obd.packet import OBD_Packet
from scapy.contrib.automotive.obd.services import OBD_S08
class _OBD_TID_Voltage(OBD_Packet):
fields_desc = [
ScalingField('data_a', 0, 0.005, "V"),
ScalingField('data_b', 0, 0.005, "V"),
ScalingField('data_c', 0, 0.005, "V"),
ScalingField('data_d', 0, 0.005, "V"),
ScalingField('data_e', 0, 0.005, "V"),
]
class _OBD_TID_Time(OBD_Packet):
fields_desc = [
ScalingField('data_a', 0, 0.004, "s"),
ScalingField('data_b', 0, 0.004, "s"),
ScalingField('data_c', 0, 0.004, "s"),
ScalingField('data_d', 0, 0.004, "s"),
ScalingField('data_e', 0, 0.004, "s"),
]
class _OBD_TID_Period(OBD_Packet):
fields_desc = [
ScalingField('data_a', 0, 0.04, "s"),
ScalingField('data_b', 0, 0.04, "s"),
ScalingField('data_c', 0, 0.04, "s"),
ScalingField('data_d', 0, 0.04, "s"),
ScalingField('data_e', 0, 0.04, "s"),
]
class OBD_TID00(OBD_Packet):
name = "TID_00_Service8SupportedTestIdentifiers"
fields_desc = [
FlagsField('supported_tids', 0, 32, [
'TID20',
'TID1F',
'TID1E',
'TID1D',
'TID1C',
'TID1B',
'TID1A',
'TID19',
'TID18',
'TID17',
'TID16',
'TID15',
'TID14',
'TID13',
'TID12',
'TID11',
'TID10',
'TID0F',
'TID0E',
'TID0D',
'TID0C',
'TID0B',
'TID0A',
'TID09',
'TID08',
'TID07',
'TID06',
'TID05',
'TID04',
'TID03',
'TID02',
'TID01'
])
]
class OBD_TID01(_OBD_TID_Voltage):
name = "TID_01_RichToLeanSensorThresholdVoltage"
class OBD_TID02(_OBD_TID_Voltage):
name = "TID_02_LeanToRichSensorThresholdVoltage"
class OBD_TID03(_OBD_TID_Voltage):
name = "TID_03_LowSensorVoltageForSwitchTimeCalculation"
class OBD_TID04(_OBD_TID_Voltage):
name = "TID_04_HighSensorVoltageForSwitchTimeCalculation"
class OBD_TID05(_OBD_TID_Time):
name = "TID_05_RichToLeanSensorSwitchTime"
class OBD_TID06(_OBD_TID_Time):
name = "TID_06_LeanToRichSensorSwitchTime"
class OBD_TID07(_OBD_TID_Voltage):
name = "TID_07_MinimumSensorVoltageForTestCycle"
class OBD_TID08(_OBD_TID_Voltage):
name = "TID_08_MaximumSensorVoltageForTestCycle"
class OBD_TID09(_OBD_TID_Period):
name = "TID_09_TimeBetweenSensorTransitions"
class OBD_TID0A(_OBD_TID_Period):
name = "TID_0A_SensorPeriod"
class OBD_S08_PR_Record(Packet):
name = "Control Operation ID"
fields_desc = [
ByteField("tid", 0),
]
class OBD_S08_PR(Packet):
name = "Control Operation IDs"
fields_desc = [
PacketListField("data_records", [], OBD_S08_PR_Record)
]
def answers(self, other):
return other.__class__ == OBD_S08 \
and all(r.tid in other.tid for r in self.data_records)
bind_layers(OBD_S08_PR_Record, OBD_TID00, tid=0x00)
bind_layers(OBD_S08_PR_Record, OBD_TID01, tid=0x01)
bind_layers(OBD_S08_PR_Record, OBD_TID02, tid=0x02)
bind_layers(OBD_S08_PR_Record, OBD_TID03, tid=0x03)
bind_layers(OBD_S08_PR_Record, OBD_TID04, tid=0x04)
bind_layers(OBD_S08_PR_Record, OBD_TID05, tid=0x05)
bind_layers(OBD_S08_PR_Record, OBD_TID06, tid=0x06)
bind_layers(OBD_S08_PR_Record, OBD_TID07, tid=0x07)
bind_layers(OBD_S08_PR_Record, OBD_TID08, tid=0x08)
bind_layers(OBD_S08_PR_Record, OBD_TID09, tid=0x09)
bind_layers(OBD_S08_PR_Record, OBD_TID0A, tid=0x0A)

View file

@ -0,0 +1,526 @@
# MIT License
# Copyright (c) 2018 Jose Amores
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
# This file is part of Scapy
# See http://www.secdev.org/projects/scapy for more information
# Copyright (C) Sebastian Baar <sebastian.baar@gmx.de>
# This program is published under a GPLv2 license
# scapy.contrib.description = Scalable service-Oriented MiddlewarE/IP (SOME/IP)
# scapy.contrib.status = loads
import ctypes
import collections
import struct
from scapy.layers.inet import TCP, UDP
from scapy.layers.inet6 import IP6Field
from scapy.compat import raw, orb
from scapy.config import conf
from scapy.modules.six.moves import range
from scapy.packet import Packet, Raw, bind_top_down, bind_bottom_up
from scapy.fields import XShortField, BitEnumField, ConditionalField, \
BitField, XBitField, IntField, XByteField, ByteEnumField, \
ShortField, X3BytesField, StrLenField, IPField, FieldLenField, \
PacketListField, XIntField
class SOMEIP(Packet):
""" SOME/IP Packet."""
PROTOCOL_VERSION = 0x01
INTERFACE_VERSION = 0x01
LEN_OFFSET = 0x08
LEN_OFFSET_TP = 0x0c
TYPE_REQUEST = 0x00
TYPE_REQUEST_NO_RET = 0x01
TYPE_NOTIFICATION = 0x02
TYPE_REQUEST_ACK = 0x40
TYPE_REQUEST_NORET_ACK = 0x41
TYPE_NOTIFICATION_ACK = 0x42
TYPE_RESPONSE = 0x80
TYPE_ERROR = 0x81
TYPE_RESPONSE_ACK = 0xc0
TYPE_ERROR_ACK = 0xc1
TYPE_TP_REQUEST = 0x20
TYPE_TP_REQUEST_NO_RET = 0x21
TYPE_TP_NOTIFICATION = 0x22
TYPE_TP_RESPONSE = 0x23
TYPE_TP_ERROR = 0x24
RET_E_OK = 0x00
RET_E_NOT_OK = 0x01
RET_E_UNKNOWN_SERVICE = 0x02
RET_E_UNKNOWN_METHOD = 0x03
RET_E_NOT_READY = 0x04
RET_E_NOT_REACHABLE = 0x05
RET_E_TIMEOUT = 0x06
RET_E_WRONG_PROTOCOL_V = 0x07
RET_E_WRONG_INTERFACE_V = 0x08
RET_E_MALFORMED_MSG = 0x09
RET_E_WRONG_MESSAGE_TYPE = 0x0a
_OVERALL_LEN_NOPAYLOAD = 16
name = "SOME/IP"
fields_desc = [
XShortField("srv_id", 0),
BitEnumField("sub_id", 0, 1, {0: "METHOD_ID", 1: "EVENT_ID"}),
ConditionalField(XBitField("method_id", 0, 15),
lambda pkt: pkt.sub_id == 0),
ConditionalField(XBitField("event_id", 0, 15),
lambda pkt: pkt.sub_id == 1),
IntField("len", None),
XShortField("client_id", 0),
XShortField("session_id", 0),
XByteField("proto_ver", PROTOCOL_VERSION),
XByteField("iface_ver", INTERFACE_VERSION),
ByteEnumField("msg_type", TYPE_REQUEST, {
TYPE_REQUEST: "REQUEST",
TYPE_REQUEST_NO_RET: "REQUEST_NO_RETURN",
TYPE_NOTIFICATION: "NOTIFICATION",
TYPE_REQUEST_ACK: "REQUEST_ACK",
TYPE_REQUEST_NORET_ACK: "REQUEST_NO_RETURN_ACK",
TYPE_NOTIFICATION_ACK: "NOTIFICATION_ACK",
TYPE_RESPONSE: "RESPONSE",
TYPE_ERROR: "ERROR",
TYPE_RESPONSE_ACK: "RESPONSE_ACK",
TYPE_ERROR_ACK: "ERROR_ACK",
TYPE_TP_REQUEST: "TP_REQUEST",
TYPE_TP_REQUEST_NO_RET: "TP_REQUEST_NO_RETURN",
TYPE_TP_NOTIFICATION: "TP_NOTIFICATION",
TYPE_TP_RESPONSE: "TP_RESPONSE",
TYPE_TP_ERROR: "TP_ERROR",
}),
ByteEnumField("retcode", 0, {
RET_E_OK: "E_OK",
RET_E_NOT_OK: "E_NOT_OK",
RET_E_UNKNOWN_SERVICE: "E_UNKNOWN_SERVICE",
RET_E_UNKNOWN_METHOD: "E_UNKNOWN_METHOD",
RET_E_NOT_READY: "E_NOT_READY",
RET_E_NOT_REACHABLE: "E_NOT_REACHABLE",
RET_E_TIMEOUT: "E_TIMEOUT",
RET_E_WRONG_PROTOCOL_V: "E_WRONG_PROTOCOL_VERSION",
RET_E_WRONG_INTERFACE_V: "E_WRONG_INTERFACE_VERSION",
RET_E_MALFORMED_MSG: "E_MALFORMED_MESSAGE",
RET_E_WRONG_MESSAGE_TYPE: "E_WRONG_MESSAGE_TYPE",
}),
ConditionalField(BitField("offset", 0, 28),
lambda pkt: SOMEIP._is_tp(pkt)),
ConditionalField(BitField("res", 0, 3),
lambda pkt: SOMEIP._is_tp(pkt)),
ConditionalField(BitField("more_seg", 0, 1),
lambda pkt: SOMEIP._is_tp(pkt))
]
def post_build(self, pkt, pay):
length = self.len
if length is None:
if SOMEIP._is_tp(self):
length = SOMEIP.LEN_OFFSET_TP + len(pay)
else:
length = SOMEIP.LEN_OFFSET + len(pay)
pkt = pkt[:4] + struct.pack("!I", length) + pkt[8:]
return pkt + pay
def answers(self, other):
if other.__class__ == self.__class__:
if self.msg_type in [SOMEIP.TYPE_REQUEST_NO_RET,
SOMEIP.TYPE_REQUEST_NORET_ACK,
SOMEIP.TYPE_NOTIFICATION,
SOMEIP.TYPE_TP_REQUEST_NO_RET,
SOMEIP.TYPE_TP_NOTIFICATION]:
return 0
return self.payload.answers(other.payload)
return 0
@staticmethod
def _is_tp(pkt):
"""Returns true if pkt is using SOMEIP-TP, else returns false."""
tp = [SOMEIP.TYPE_TP_REQUEST, SOMEIP.TYPE_TP_REQUEST_NO_RET,
SOMEIP.TYPE_TP_NOTIFICATION, SOMEIP.TYPE_TP_RESPONSE,
SOMEIP.TYPE_TP_ERROR]
if isinstance(pkt, Packet):
return pkt.msg_type in tp
else:
return pkt[15] in tp
def fragment(self, fragsize=1392):
"""Fragment SOME/IP-TP"""
fnb = 0
fl = self
lst = list()
while fl.underlayer is not None:
fnb += 1
fl = fl.underlayer
for p in fl:
s = raw(p[fnb].payload)
nb = (len(s) + fragsize) // fragsize
for i in range(nb):
q = p.copy()
del q[fnb].payload
q[fnb].len = SOMEIP.LEN_OFFSET_TP + \
len(s[i * fragsize:(i + 1) * fragsize])
q[fnb].more_seg = 1
if i == nb - 1:
q[fnb].more_seg = 0
q[fnb].offset += i * fragsize // 16
r = conf.raw_layer(load=s[i * fragsize:(i + 1) * fragsize])
r.overload_fields = p[fnb].payload.overload_fields.copy()
q.add_payload(r)
lst.append(q)
return lst
def _bind_someip_layers():
bind_top_down(UDP, SOMEIP, sport=30490, dport=30490)
for i in range(15):
bind_bottom_up(UDP, SOMEIP, sport=30490 + i)
bind_bottom_up(TCP, SOMEIP, sport=30490 + i)
bind_bottom_up(UDP, SOMEIP, dport=30490 + i)
bind_bottom_up(TCP, SOMEIP, dport=30490 + i)
_bind_someip_layers()
class _SDPacketBase(Packet):
""" base class to be used among all SD Packet definitions."""
def extract_padding(self, s):
return "", s
SDENTRY_TYPE_SRV_FINDSERVICE = 0x00
SDENTRY_TYPE_SRV_OFFERSERVICE = 0x01
SDENTRY_TYPE_SRV = (SDENTRY_TYPE_SRV_FINDSERVICE,
SDENTRY_TYPE_SRV_OFFERSERVICE)
SDENTRY_TYPE_EVTGRP_SUBSCRIBE = 0x06
SDENTRY_TYPE_EVTGRP_SUBSCRIBE_ACK = 0x07
SDENTRY_TYPE_EVTGRP = (SDENTRY_TYPE_EVTGRP_SUBSCRIBE,
SDENTRY_TYPE_EVTGRP_SUBSCRIBE_ACK)
SDENTRY_OVERALL_LEN = 16
def _MAKE_SDENTRY_COMMON_FIELDS_DESC(type):
return [
XByteField("type", type),
XByteField("index_1", 0),
XByteField("index_2", 0),
XBitField("n_opt_1", 0, 4),
XBitField("n_opt_2", 0, 4),
XShortField("srv_id", 0),
XShortField("inst_id", 0),
XByteField("major_ver", 0),
X3BytesField("ttl", 0)
]
class SDEntry_Service(_SDPacketBase):
name = "Service Entry"
fields_desc = _MAKE_SDENTRY_COMMON_FIELDS_DESC(
SDENTRY_TYPE_SRV_FINDSERVICE)
fields_desc += [
XIntField("minor_ver", 0)
]
class SDEntry_EventGroup(_SDPacketBase):
name = "Eventgroup Entry"
fields_desc = _MAKE_SDENTRY_COMMON_FIELDS_DESC(
SDENTRY_TYPE_EVTGRP_SUBSCRIBE)
fields_desc += [
XBitField("res", 0, 12),
XBitField("cnt", 0, 4),
XShortField("eventgroup_id", 0)
]
def _sdentry_class(payload, **kargs):
TYPE_PAYLOAD_I = 0
pl_type = orb(payload[TYPE_PAYLOAD_I])
cls = None
if pl_type in SDENTRY_TYPE_SRV:
cls = SDEntry_Service
elif pl_type in SDENTRY_TYPE_EVTGRP:
cls = SDEntry_EventGroup
return cls(payload, **kargs)
def _sdoption_class(payload, **kargs):
pl_type = orb(payload[2])
cls = {
SDOPTION_CFG_TYPE: SDOption_Config,
SDOPTION_LOADBALANCE_TYPE: SDOption_LoadBalance,
SDOPTION_IP4_ENDPOINT_TYPE: SDOption_IP4_EndPoint,
SDOPTION_IP4_MCAST_TYPE: SDOption_IP4_Multicast,
SDOPTION_IP4_SDENDPOINT_TYPE: SDOption_IP4_SD_EndPoint,
SDOPTION_IP6_ENDPOINT_TYPE: SDOption_IP6_EndPoint,
SDOPTION_IP6_MCAST_TYPE: SDOption_IP6_Multicast,
SDOPTION_IP6_SDENDPOINT_TYPE: SDOption_IP6_SD_EndPoint
}.get(pl_type, Raw)
return cls(payload, **kargs)
# SD Option
SDOPTION_CFG_TYPE = 0x01
SDOPTION_LOADBALANCE_TYPE = 0x02
SDOPTION_LOADBALANCE_LEN = 0x05
SDOPTION_IP4_ENDPOINT_TYPE = 0x04
SDOPTION_IP4_ENDPOINT_LEN = 0x0009
SDOPTION_IP4_MCAST_TYPE = 0x14
SDOPTION_IP4_MCAST_LEN = 0x0009
SDOPTION_IP4_SDENDPOINT_TYPE = 0x24
SDOPTION_IP4_SDENDPOINT_LEN = 0x0009
SDOPTION_IP6_ENDPOINT_TYPE = 0x06
SDOPTION_IP6_ENDPOINT_LEN = 0x0015
SDOPTION_IP6_MCAST_TYPE = 0x16
SDOPTION_IP6_MCAST_LEN = 0x0015
SDOPTION_IP6_SDENDPOINT_TYPE = 0x26
SDOPTION_IP6_SDENDPOINT_LEN = 0x0015
def _MAKE_COMMON_SDOPTION_FIELDS_DESC(type, length=None):
return [
ShortField("len", length),
XByteField("type", type),
XByteField("res_hdr", 0)
]
def _MAKE_COMMON_IP_SDOPTION_FIELDS_DESC():
return [
XByteField("res_tail", 0),
ByteEnumField("l4_proto", 0x11, {0x06: "TCP", 0x11: "UDP"}),
ShortField("port", 0)
]
class SDOption_Config(_SDPacketBase):
name = "Config Option"
fields_desc = _MAKE_COMMON_SDOPTION_FIELDS_DESC(SDOPTION_CFG_TYPE) + [
StrLenField("cfg_str", "\x00", length_from=lambda pkt: pkt.len - 1)
]
def post_build(self, pkt, pay):
if self.len is None:
length = len(self.cfg_str) + 1 # res_hdr field takes 1 byte
pkt = struct.pack("!H", length) + pkt[2:]
return pkt + pay
@staticmethod
def make_string(data):
# Build a valid null-terminated configuration string from a dict or a
# list with key-value pairs.
#
# Example:
# >>> SDOption_Config.make_string({ "hello": "world" })
# b'\x0bhello=world\x00'
#
# >>> SDOption_Config.make_string([
# ... ("x", "y"),
# ... ("abc", "def"),
# ... ("123", "456")
# ... ])
# b'\x03x=y\x07abc=def\x07123=456\x00'
if isinstance(data, dict):
data = data.items()
# combine entries
data = ("{}={}".format(k, v) for k, v in data)
# prepend length
data = ("{}{}".format(chr(len(v)), v) for v in data)
# concatenate
data = "".join(data)
data += "\x00"
return data.encode("utf8")
class SDOption_LoadBalance(_SDPacketBase):
name = "LoadBalance Option"
fields_desc = _MAKE_COMMON_SDOPTION_FIELDS_DESC(
SDOPTION_LOADBALANCE_TYPE, SDOPTION_LOADBALANCE_LEN)
fields_desc += [
ShortField("priority", 0),
ShortField("weight", 0)
]
class SDOption_IP4_EndPoint(_SDPacketBase):
name = "IP4 EndPoint Option"
fields_desc = _MAKE_COMMON_SDOPTION_FIELDS_DESC(
SDOPTION_IP4_ENDPOINT_TYPE, SDOPTION_IP4_ENDPOINT_LEN)
fields_desc += [
IPField("addr", "0.0.0.0"),
] + _MAKE_COMMON_IP_SDOPTION_FIELDS_DESC()
class SDOption_IP4_Multicast(_SDPacketBase):
name = "IP4 Multicast Option"
fields_desc = _MAKE_COMMON_SDOPTION_FIELDS_DESC(
SDOPTION_IP4_MCAST_TYPE, SDOPTION_IP4_MCAST_LEN)
fields_desc += [
IPField("addr", "0.0.0.0"),
] + _MAKE_COMMON_IP_SDOPTION_FIELDS_DESC()
class SDOption_IP4_SD_EndPoint(_SDPacketBase):
name = "IP4 SDEndPoint Option"
fields_desc = _MAKE_COMMON_SDOPTION_FIELDS_DESC(
SDOPTION_IP4_SDENDPOINT_TYPE, SDOPTION_IP4_SDENDPOINT_LEN)
fields_desc += [
IPField("addr", "0.0.0.0"),
] + _MAKE_COMMON_IP_SDOPTION_FIELDS_DESC()
class SDOption_IP6_EndPoint(_SDPacketBase):
name = "IP6 EndPoint Option"
fields_desc = _MAKE_COMMON_SDOPTION_FIELDS_DESC(
SDOPTION_IP6_ENDPOINT_TYPE, SDOPTION_IP6_ENDPOINT_LEN)
fields_desc += [
IP6Field("addr", "::"),
] + _MAKE_COMMON_IP_SDOPTION_FIELDS_DESC()
class SDOption_IP6_Multicast(_SDPacketBase):
name = "IP6 Multicast Option"
fields_desc = _MAKE_COMMON_SDOPTION_FIELDS_DESC(
SDOPTION_IP6_MCAST_TYPE, SDOPTION_IP6_MCAST_LEN)
fields_desc += [
IP6Field("addr", "::"),
] + _MAKE_COMMON_IP_SDOPTION_FIELDS_DESC()
class SDOption_IP6_SD_EndPoint(_SDPacketBase):
name = "IP6 SDEndPoint Option"
fields_desc = _MAKE_COMMON_SDOPTION_FIELDS_DESC(
SDOPTION_IP6_SDENDPOINT_TYPE, SDOPTION_IP6_SDENDPOINT_LEN)
fields_desc += [
IP6Field("addr", "::"),
] + _MAKE_COMMON_IP_SDOPTION_FIELDS_DESC()
##
# SD PACKAGE DEFINITION
##
class SD(_SDPacketBase):
"""
SD Packet
NOTE : when adding 'entries' or 'options', do not use list.append()
method but create a new list
e.g. : p = SD()
p.option_array = [SDOption_Config(),SDOption_IP6_EndPoint()]
"""
SOMEIP_MSGID_SRVID = 0xffff
SOMEIP_MSGID_SUBID = 0x1
SOMEIP_MSGID_EVENTID = 0x100
SOMEIP_CLIENT_ID = 0x0000
SOMEIP_MINIMUM_SESSION_ID = 0x0001
SOMEIP_PROTO_VER = 0x01
SOMEIP_IFACE_VER = 0x01
SOMEIP_MSG_TYPE = SOMEIP.TYPE_NOTIFICATION
SOMEIP_RETCODE = SOMEIP.RET_E_OK
_sdFlag = collections.namedtuple('Flag', 'mask offset')
FLAGSDEF = {
"REBOOT": _sdFlag(mask=0x80, offset=7),
"UNICAST": _sdFlag(mask=0x40, offset=6)
}
name = "SD"
fields_desc = [
XByteField("flags", 0),
X3BytesField("res", 0),
FieldLenField("len_entry_array", None,
length_of="entry_array", fmt="!I"),
PacketListField("entry_array", None, cls=_sdentry_class,
length_from=lambda pkt: pkt.len_entry_array),
FieldLenField("len_option_array", None,
length_of="option_array", fmt="!I"),
PacketListField("option_array", None, cls=_sdoption_class,
length_from=lambda pkt: pkt.len_option_array)
]
def get_flag(self, name):
name = name.upper()
if name in self.FLAGSDEF:
return ((self.flags & self.FLAGSDEF[name].mask) >>
self.FLAGSDEF[name].offset)
else:
return None
def set_flag(self, name, value):
name = name.upper()
if name in self.FLAGSDEF:
self.flags = (self.flags &
(ctypes.c_ubyte(~self.FLAGSDEF[name].mask).value)) \
| ((value & 0x01) << self.FLAGSDEF[name].offset)
def set_entryArray(self, entry_list):
if isinstance(entry_list, list):
self.entry_array = entry_list
else:
self.entry_array = [entry_list]
def set_optionArray(self, option_list):
if isinstance(option_list, list):
self.option_array = option_list
else:
self.option_array = [option_list]
bind_top_down(SOMEIP, SD,
srv_id=SD.SOMEIP_MSGID_SRVID,
sub_id=SD.SOMEIP_MSGID_SUBID,
client_id=SD.SOMEIP_CLIENT_ID,
session_id=SD.SOMEIP_MINIMUM_SESSION_ID,
event_id=SD.SOMEIP_MSGID_EVENTID,
proto_ver=SD.SOMEIP_PROTO_VER,
iface_ver=SD.SOMEIP_IFACE_VER,
msg_type=SD.SOMEIP_MSG_TYPE,
retcode=SD.SOMEIP_RETCODE)
bind_bottom_up(SOMEIP, SD,
srv_id=SD.SOMEIP_MSGID_SRVID,
sub_id=SD.SOMEIP_MSGID_SUBID,
event_id=SD.SOMEIP_MSGID_EVENTID,
proto_ver=SD.SOMEIP_PROTO_VER,
iface_ver=SD.SOMEIP_IFACE_VER,
msg_type=SD.SOMEIP_MSG_TYPE,
retcode=SD.SOMEIP_RETCODE)
# FIXME: Service Discovery messages shall be transported over UDP
# (TR_SOMEIP_00248)
# FIXME: The port 30490 (UDP and TCP as well) shall be only used for SOME/IP-SD
# and not used for applications communicating over SOME/IP
# (TR_SOMEIP_00020)

View file

@ -0,0 +1,257 @@
# MIT License
# Copyright (c) 2018 Jose Amores
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
# This file is part of Scapy
# See http://www.secdev.org/projects/scapy for more information
# Copyright (C) Sebastian Baar <sebastian.baar@gmx.de>
# This program is published under a GPLv2 license
##########
##########
+ Test MessageId
= Load module
load_contrib("automotive.someip")
import binascii
= Check MessageId with method_id
p = SOMEIP().msg_id
p.srv_id = 0x1111
p.method_id = 0x0222
p.event_id = 0x0333
p.sub_id = 0
assert(struct.unpack("!H", bytes(p)[:2])[0] == 0x1111)
assert((struct.unpack("!B", bytes(p)[2:3])[0] & 0x80) == 0x00)
assert((struct.unpack("!H", bytes(p)[2:4])[0] & ~0x8000) == 0x0222)
assert(bytes(p) == b"\x11\x11\x02\x22")
del(p)
= Dissect MessageId with method_id
p = SOMEIP(b'\x22\x22\x03\x33')
assert(p.msg_id.srv_id == 0x2222)
assert(p.msg_id.method_id == 0x0333)
assert(p.msg_id.sub_id == 0)
del(p)
= Build MessageId with event_id
p = SOMEIP().msg_id
p.srv_id = 0x1111
p.method_id = 0x0222
p.event_id = 0x0333
p.sub_id = 1
assert(struct.unpack("!H", bytes(p)[:2])[0] == 0x1111)
assert((struct.unpack("!B", bytes(p)[2:3])[0] & 0x80) == 0x80)
assert((struct.unpack("!H", bytes(p)[2:4])[0] & ~0x8000) == 0x0333)
assert(bytes(p) == b"\x11\x11\x83\x33")
del(p)
= Dissect MessageId with event_id
p = SOMEIP(b'\x33\x33\x82\x22')
assert(p.msg_id.srv_id == 0x3333)
assert(p.msg_id.event_id == 0x0222)
assert(p.msg_id.sub_id == 1)
del(p)
+ Test RequestId
= Request Id
p = SOMEIP().req_id
p.client_id = 0x1111
p.session_id = 0x2222
assert(struct.unpack("!H", bytes(p)[:2])[0] == 0x1111)
assert(struct.unpack("!H", bytes(p)[2:4])[0] == 0x2222)
assert(bytes(p) == b"\x11\x11\x22\x22")
del(p)
= Dissect RequestId
method_id = b'\x22\x22\x03\x33'
pktLen = b'\x11\x11\x11\x11'
reqId = b'\x22\x22\x33\x33'
p = SOMEIP(method_id + pktLen + reqId)
assert(p.req_id.client_id == 0x2222)
assert(p.req_id.session_id == 0x3333)
del(p)
+ Test SOMEIP
= Check SomeIp
p = SOMEIP()
pstr = binascii.hexlify(bytes(p))
binstr = binascii.hexlify(b"\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x01\x01\x00\x00")
assert(pstr == binstr)
p.payload = Raw(binascii.unhexlify("DEADBEEF"))
pstr = bytes(p)
binstr = b"\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x00\x00\x00\x01\x01\x00\x00\xde\xad\xbe\xef"
assert(pstr == binstr)
p.payload = Raw('')
pstr = bytes(p)
binstr = b"\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x01\x01\x00\x00"
assert(pstr == binstr)
del(p)
= Dissect SomeIP packet
p = SOMEIP(
b"\x11\x11\x81\x11\x00\x00\x00\x04\x33\x33\x44\x44\x02\x03\x04\x05")
assert(p.msg_id.srv_id == 0x1111)
assert(p.msg_id.event_id == 0x0111)
assert(p.req_id.client_id == 0x3333)
assert(p.req_id.session_id == 0x4444)
assert(p.proto_ver == 0x02)
assert(p.iface_ver == 0x03)
assert(p.msg_type == 0x04)
assert(p.retcode == 0x05)
del(p)
+ Test SOMEIP_SubPackages
= Check MessageId subpackage
p = SOMEIP()
p.msg_id.srv_id = 0x1111
p.msg_id.method_id = 0x0222
p.msg_id.event_id = 0x0333
p.msg_id.sub_id = 0
pstr = bytes(p)
binstr = b"\x11\x11\x02\x22\x00\x00\x00\x08\x00\x00\x00\x00\x01\x01\x00\x00"
assert(pstr == binstr)
p.msg_id.sub_id = 1
pstr = bytes(p)
binstr = b"\x11\x11\x83\x33\x00\x00\x00\x08\x00\x00\x00\x00\x01\x01\x00\x00"
assert(pstr == binstr)
del(p)
= Check RequestId subpackage
p = SOMEIP()
p.req_id.client_id = 0x1111
p.req_id.session_id = 0x2222
pstr = bytes(p)
binstr = b"\x00\x00\x00\x00\x00\x00\x00\x08\x11\x11\x22\x22\x01\x01\x00\x00"
assert(pstr == binstr)
+ Test SOMEIP_TP
= Check TP
p = SOMEIP()
p.msg_type = 0x20
pstr = bytes(p)
print(pstr)
binstr = b'\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x00\x00\x00\x01\x01\x20\x00\x00\x00\x00\x00'
assert(pstr == binstr)
p.more_seg = 1
pstr = bytes(p)
binstr = b'\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x00\x00\x00\x01\x01\x20\x00\x00\x00\x00\x01'
assert(pstr == binstr)
p.msg_type = 0x00
pstr = bytes(p)
binstr = b'\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x01\x01\x00\x00'
assert(pstr == binstr)
= Dissect TP
p = SOMEIP(b'\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x00\x00\x00\x01\x01\x21\x00\x00\x00\x00\x01')
assert(p.msg_type == 0x21)
assert(p.more_seg == 1)
assert(p.len == 12)
p.msg_type = 0x00
pstr = bytes(p)
binstr = b"\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x00\x00\x00\x01\x01\x00\x00"
assert(pstr == binstr)
+ Test SOMEIP-TP
= Build TP fragmented
p = SOMEIP()
p.msg_type = 0x20
p.add_payload(Raw("A"*1400))
f = p.fragment()
assert(f[0].len == 1404)
assert(f[1].len == 20)
assert(f[0].payload == Raw("A"*1392))
assert(f[1].payload == Raw("A"*8))
assert(f[0].more_seg == 1)
assert(f[1].more_seg == 0)

View file

@ -0,0 +1,350 @@
#! /usr/bin/env python
# MIT License
# Copyright (c) 2018 Jose Amores
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
# This file is part of Scapy
# See http://www.secdev.org/projects/scapy for more information
# Copyright (C) Sebastian Baar <sebastian.baar@gmx.de>
# This program is published under a GPLv2 license
# scapy.contrib.description = SOME/IP Service Discovery
# scapy.contrib.status = loads
import ctypes
import collections
import struct
from scapy.packet import Packet, Raw
from scapy.fields import ByteField, BitField, ShortField, \
X3BytesField, IntField, ByteEnumField, StrField, IPField, \
FieldLenField, PacketListField
from scapy.contrib.automotive.someip import SOMEIP
from scapy.layers.inet6 import IP6Field
from scapy.compat import orb
class _SDPacketBase(Packet):
""" base class to be used among all SD Packet definitions."""
# use this dictionary to set default values for desired fields (mostly on
# subclasses where not all fields are defined locally)
# - key : field_name, value : desired value
# - it will be used from 'init_fields' function, upon packet initialization
#
# example : _defaults =
# {'field_1_name':field_1_value,'field_2_name':field_2_value}
_defaults = {}
def _set_defaults(self):
for key in self._defaults:
try:
self.get_field(key)
except KeyError:
pass
else:
self.setfieldval(key, self._defaults[key])
def init_fields(self):
super(_SDPacketBase, self).init_fields()
self._set_defaults()
# SD ENTRY
# - Service
# - EventGroup
class _SDEntry(_SDPacketBase):
TYPE_FMT = ">B"
TYPE_PAYLOAD_I = 0
TYPE_SRV_FINDSERVICE = 0x00
TYPE_SRV_OFFERSERVICE = 0x01
TYPE_SRV = (TYPE_SRV_FINDSERVICE, TYPE_SRV_OFFERSERVICE)
TYPE_EVTGRP_SUBSCRIBE = 0x06
TYPE_EVTGRP_SUBSCRIBE_ACK = 0x07
TYPE_EVTGRP = (TYPE_EVTGRP_SUBSCRIBE, TYPE_EVTGRP_SUBSCRIBE_ACK)
OVERALL_LEN = 16
fields_desc = [
ByteField("type", 0),
ByteField("index_1", 0),
ByteField("index_2", 0),
BitField("n_opt_1", 0, 4),
BitField("n_opt_2", 0, 4),
ShortField("srv_id", 0),
ShortField("inst_id", 0),
ByteField("major_ver", 0),
X3BytesField("ttl", 0)
]
def guess_payload_class(self, payload):
pl_type = orb(payload[_SDEntry.TYPE_PAYLOAD_I])
if (pl_type in _SDEntry.TYPE_SRV):
return (SDEntry_Service)
elif (pl_type in _SDEntry.TYPE_EVTGRP):
return (SDEntry_EventGroup)
class SDEntry_Service(_SDEntry):
_defaults = {"type": _SDEntry.TYPE_SRV_FINDSERVICE}
name = "Service Entry"
fields_desc = [
_SDEntry,
IntField("minor_ver", 0)
]
class SDEntry_EventGroup(_SDEntry):
_defaults = {"type": _SDEntry.TYPE_EVTGRP_SUBSCRIBE}
name = "Eventgroup Entry"
fields_desc = [
_SDEntry,
BitField("res", 0, 12),
BitField("cnt", 0, 4),
ShortField("eventgroup_id", 0)
]
# SD Option
# - Configuration
# - LoadBalancing
# - IPv4 EndPoint
# - IPv6 EndPoint
# - IPv4 MultiCast
# - IPv6 MultiCast
# - IPv4 EndPoint
# - IPv6 EndPoint
class _SDOption(_SDPacketBase):
CFG_TYPE = 0x01
CFG_OVERALL_LEN = 4
LOADBALANCE_TYPE = 0x02
LOADBALANCE_LEN = 0x05
LOADBALANCE_OVERALL_LEN = 8
IP4_ENDPOINT_TYPE = 0x04
IP4_ENDPOINT_LEN = 0x0009
IP4_MCAST_TYPE = 0x14
IP4_MCAST_LEN = 0x0009
IP4_SDENDPOINT_TYPE = 0x24
IP4_SDENDPOINT_LEN = 0x0009
IP4_OVERALL_LEN = 12
IP6_ENDPOINT_TYPE = 0x06
IP6_ENDPOINT_LEN = 0x0015
IP6_MCAST_TYPE = 0x16
IP6_MCAST_LEN = 0x0015
IP6_SDENDPOINT_TYPE = 0x26
IP6_SDENDPOINT_LEN = 0x0015
IP6_OVERALL_LEN = 24
def guess_payload_class(self, payload):
pl_type = orb(payload[2])
return {
_SDOption.CFG_TYPE: SDOption_Config,
self.LOADBALANCE_TYPE: SDOption_LoadBalance,
self.IP4_ENDPOINT_TYPE: SDOption_IP4_EndPoint,
self.IP4_MCAST_TYPE: SDOption_IP4_Multicast,
self.IP4_SDENDPOINT_TYPE: SDOption_IP4_SD_EndPoint,
self.IP6_ENDPOINT_TYPE: SDOption_IP6_EndPoint,
self.IP6_MCAST_TYPE: SDOption_IP6_Multicast,
self.IP6_SDENDPOINT_TYPE: SDOption_IP6_SD_EndPoint
}.get(pl_type, Raw)
class _SDOption_Header(_SDOption):
fields_desc = [
ShortField("len", None),
ByteField("type", 0),
ByteField("res_hdr", 0)
]
class _SDOption_Tail(_SDOption):
fields_desc = [
ByteField("res_tail", 0),
ByteEnumField("l4_proto", 0x06, {0x06: "TCP", 0x11: "UDP"}),
ShortField("port", 0)
]
class _SDOption_IP4(_SDOption):
fields_desc = [
_SDOption_Header,
IPField("addr", "0.0.0.0"),
_SDOption_Tail
]
class _SDOption_IP6(_SDOption):
fields_desc = [
_SDOption_Header,
IP6Field("addr", "2001:cdba:0000:0000:0000:0000:3257:9652"),
_SDOption_Tail
]
class SDOption_Config(_SDOption):
LEN_OFFSET = 0x01
name = "Config Option"
_defaults = {'type': _SDOption.CFG_TYPE}
fields_desc = [
_SDOption_Header,
StrField("cfg_str", "")
]
def post_build(self, pkt, pay):
length = self.len
if (length is None):
length = len(self.cfg_str) + self.LEN_OFFSET
pkt = struct.pack("!H", length) + pkt[2:]
return (pkt + pay)
class SDOption_LoadBalance(_SDOption):
name = "LoadBalance Option"
_defaults = {'type': _SDOption.LOADBALANCE_TYPE,
'len': _SDOption.LOADBALANCE_LEN}
fields_desc = [
_SDOption_Header,
ShortField("priority", 0),
ShortField("weight", 0)
]
class SDOption_IP4_EndPoint(_SDOption_IP4):
name = "IP4 EndPoint Option"
_defaults = {'type': _SDOption.IP4_ENDPOINT_TYPE,
'len': _SDOption.IP4_ENDPOINT_LEN}
class SDOption_IP4_Multicast(_SDOption_IP4):
name = "IP4 Multicast Option"
_defaults = {'type': _SDOption.IP4_MCAST_TYPE,
'len': _SDOption.IP4_MCAST_LEN}
class SDOption_IP4_SD_EndPoint(_SDOption_IP4):
name = "IP4 SDEndPoint Option"
_defaults = {'type': _SDOption.IP4_SDENDPOINT_TYPE,
'len': _SDOption.IP4_SDENDPOINT_LEN}
class SDOption_IP6_EndPoint(_SDOption_IP6):
name = "IP6 EndPoint Option"
_defaults = {'type': _SDOption.IP6_ENDPOINT_TYPE,
'len': _SDOption.IP6_ENDPOINT_LEN}
class SDOption_IP6_Multicast(_SDOption_IP6):
name = "IP6 Multicast Option"
_defaults = {'type': _SDOption.IP6_MCAST_TYPE,
'len': _SDOption.IP6_MCAST_LEN}
class SDOption_IP6_SD_EndPoint(_SDOption_IP6):
name = "IP6 SDEndPoint Option"
_defaults = {'type': _SDOption.IP6_SDENDPOINT_TYPE,
'len': _SDOption.IP6_SDENDPOINT_LEN}
##
# SD PACKAGE DEFINITION
##
class SD(_SDPacketBase):
"""
SD Packet
NOTE : when adding 'entries' or 'options', do not use list.append()
method but create a new list
e.g. : p = SD()
p.option_array = [SDOption_Config(),SDOption_IP6_EndPoint()]
"""
SOMEIP_MSGID_SRVID = 0xffff
SOMEIP_MSGID_SUBID = 0x1
SOMEIP_MSGID_EVENTID = 0x100
SOMEIP_PROTO_VER = 0x01
SOMEIP_IFACE_VER = 0x01
SOMEIP_MSG_TYPE = SOMEIP.TYPE_NOTIFICATION
name = "SD"
_sdFlag = collections.namedtuple('Flag', 'mask offset')
FLAGSDEF = {
"REBOOT": _sdFlag(mask=0x80, offset=7),
"UNICAST": _sdFlag(mask=0x40, offset=6)
}
name = "SD"
fields_desc = [
ByteField("flags", 0),
X3BytesField("res", 0),
FieldLenField("len_entry_array", None,
length_of="entry_array", fmt="!I"),
PacketListField("entry_array", None, cls=_SDEntry,
length_from=lambda pkt: pkt.len_entry_array),
FieldLenField("len_option_array", None,
length_of="option_array", fmt="!I"),
PacketListField("option_array", None, cls=_SDOption,
length_from=lambda pkt: pkt.len_option_array)
]
def get_flag(self, name):
name = name.upper()
if (name in self.FLAGSDEF):
return ((self.flags & self.FLAGSDEF[name].mask) >>
self.FLAGSDEF[name].offset)
else:
return None
def set_flag(self, name, value):
name = name.upper()
if (name in self.FLAGSDEF):
self.flags = (self.flags &
(ctypes.c_ubyte(~self.FLAGSDEF[name].mask).value)) \
| ((value & 0x01) << self.FLAGSDEF[name].offset)
def set_entryArray(self, entry_list):
if (isinstance(entry_list, list)):
self.entry_array = entry_list
else:
self.entry_array = [entry_list]
def set_optionArray(self, option_list):
if (isinstance(option_list, list)):
self.option_array = option_list
else:
self.option_array = [option_list]
def get_someip(self, stacked=False):
p = SOMEIP()
p.msg_id.srv_id = SD.SOMEIP_MSGID_SRVID
p.msg_id.sub_id = SD.SOMEIP_MSGID_SUBID
p.msg_id.event_id = SD.SOMEIP_MSGID_EVENTID
p.proto_ver = SD.SOMEIP_PROTO_VER
p.iface_ver = SD.SOMEIP_IFACE_VER
p.msg_type = SD.SOMEIP_MSG_TYPE
if (stacked):
return (p / self)
else:
return (p)

View file

@ -0,0 +1,381 @@
# MIT License
# Copyright (c) 2018 Jose Amores
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
# This file is part of Scapy
# See http://www.secdev.org/projects/scapy for more information
# Copyright (C) Sebastian Baar <sebastian.baar@gmx.de>
# This program is published under a GPLv2 license
#########
#########
+ SD Entry Service
=load module
load_contrib("automotive.someip_sd")
= Check packet length
p = SDEntry_Service()
assert(len(bytes(p)) == SDEntry_Service.OVERALL_LEN)
= Check fields setting
p.type = SDEntry_Service.TYPE_SRV_OFFERSERVICE
p.index_1 = 0x11
p.index_2 = 0x22
p.srv_id = 0x3333
p.inst_id = 0x4444
p.major_ver = 0x55
p.ttl = 0x666666
p.minor_ver = 0xdeadbeef
p_str = bytes(p)
bin_str = b"\x01\x11\x22\x00\x33\x33\x44\x44\x55\x66\x66\x66\xde\xad\xbe\xef"
assert(p_str == bin_str)
= Check fields setting2
p = SDEntry_Service()
p.n_opt_1 = 0xf1
p.n_opt_2 = 0xf2
p_str = bytes(p)
bin_str = b"\x00\x00\x00\x12\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
assert(p_str == bin_str)
assert(len(p_str) == SDEntry_Service.OVERALL_LEN)
= Check payload guess
p_entry_srv = SDEntry_Service()
assert(SDEntry_Service().guess_payload_class(bytes(p_entry_srv)) ==
SDEntry_Service)
= Check SDEntry_Service
p = SDEntry_Service(
b"\x01\x22\x33\x00\x44\x44\x55\x55\x66\x77\x77\x77\xde\xad\xbe\xef")
assert(p.type == SDEntry_Service.TYPE_SRV_OFFERSERVICE)
assert(p.index_1 == 0x22)
assert(p.index_2 == 0x33)
assert(p.srv_id == 0x4444)
assert(p.inst_id == 0x5555)
assert(p.major_ver == 0x66)
assert(p.ttl == 0x777777)
assert(p.minor_ver == 0xdeadbeef)
+ SD Entry Eventgroup
= Check packet length
p = SDEntry_EventGroup()
assert(len(bytes(p)) == SDEntry_EventGroup.OVERALL_LEN)
= Check fields setting
p.index_1 = 0x11
p.index_2 = 0x22
p.srv_id = 0x3333
p.inst_id = 0x4444
p.major_ver = 0x55
p.ttl = 0x666666
p.cnt = 0x7
p.eventgroup_id = 0x8888
p_str = bytes(p)
bin_str = b"\x06\x11\x22\x00\x33\x33\x44\x44\x55\x66\x66\x66\x00\x07\x88\x88"
assert(p_str == bin_str)
= Check payload guess
p_entry_evtgrp = SDEntry_EventGroup()
assert(SDEntry_EventGroup().guess_payload_class(
bytes(p_entry_evtgrp)) == SDEntry_EventGroup)
+ SD Option Config
= Check pkg type
p = SDOption_Config()
assert(p.type == 0x01)
= Check length without payload
assert(len(bytes(p)) == 4)
= Check add payload and check length
import binascii
p.cfg_str = binascii.hexlify(b"5abc=x7def=1230")
assert(bytes(p) == b"\x00\x1f\x01\x00" +
binascii.hexlify(b"5abc=x7def=1230"))
= Check payload guess
assert(SDOption_Config().guess_payload_class(bytes(p)) == SDOption_Config)
= Check SDEntry_EventGroup
p = SDEntry_EventGroup(
b"\x06\x11\x22\x00\x33\x33\x44\x44\x55\x66\x66\x66\x00\x07\x88\x88")
assert(p.index_1 == 0x11)
assert(p.index_2 == 0x22)
assert(p.srv_id == 0x3333)
assert(p.inst_id == 0x4444)
assert(p.major_ver == 0x55)
assert(p.ttl == 0x666666)
assert(p.cnt == 0x7)
assert(p.eventgroup_id == 0x8888)
+ SD Option Load Balance
= Check pkg type & lengths(static and overall)
p = SDOption_LoadBalance()
assert(p.type == 0x02)
assert(p.len == 0x05)
assert(len(bytes(p)) == 8)
= Check payload guess
assert(SDOption_LoadBalance().guess_payload_class(
bytes(p)) == SDOption_LoadBalance)
= Check SDOption_LoadBalance
p = SDOption_LoadBalance(b'\x00\x05\x02\x01\x00\x02\x00\x03')
assert(p.type == 0x02)
assert(p.len == 0x05)
assert(p.res_hdr == 0x01)
assert(p.priority == 0x02)
assert(p.weight == 0x03)
+SD Option IP4 Endpoint
= Check pkg type & length
p = SDOption_IP4_EndPoint()
assert(p.type == 0x04)
assert(p.len == 0x0009)
= Check payload guess
assert(SDOption_IP4_EndPoint().guess_payload_class(
bytes(p)) == SDOption_IP4_EndPoint)
= Check SDOption_IP4_EndPoint
p = SDOption_IP4_EndPoint(b'\x00\x09\x04')
assert(p.type == 0x04)
assert(p.len == 0x0009)
+SD Option IP4 Multicast
= Check pkg type & length
p = SDOption_IP4_Multicast()
assert(p.type == 0x14)
assert(p.len == 0x0009)
= Payload guess
assert(SDOption_IP4_Multicast().guess_payload_class(
bytes(p)) == SDOption_IP4_Multicast)
= Check SDOption_IP4_Multicast
p = SDOption_IP4_Multicast(b'\x00\x09\x14')
assert(p.type == 0x14)
assert(p.len == 0x0009)
+SD OPTION IP4 SD EndPoint
= Check pkg type & length
p = SDOption_IP4_SD_EndPoint()
assert(p.type == 0x24)
assert(p.len == 0x0009)
= Check payload guess
assert(SDOption_IP4_SD_EndPoint().guess_payload_class(
bytes(p)) == SDOption_IP4_SD_EndPoint)
= Check SDOption_IP4_SD_EndPoint
p = SDOption_IP4_SD_EndPoint(b'\x00\x09\x24')
assert(p.type == 0x24)
assert(p.len == 0x0009)
+SD Option IP6 End Point
= Check pkg type & length
p = SDOption_IP6_EndPoint()
assert(p.type == 0x06)
assert(p.len == 0x0015)
= Check payload guess
assert(SDOption_IP6_EndPoint().guess_payload_class(
bytes(p)) == SDOption_IP6_EndPoint)
= Check SDOption_IP6_EndPoint
p = SDOption_IP6_EndPoint(b'\x00\x15\x06')
assert(p.type == 0x06)
assert(p.len == 0x0015)
+SD Option IP6 Multicast
= Check pkg type & length
p = SDOption_IP6_Multicast()
assert(p.type == 0x16)
assert(p.len == 0x0015)
= Check payload guess
assert(SDOption_IP6_Multicast().guess_payload_class(
bytes(p)) == SDOption_IP6_Multicast)
= Check SDOption_IP6_Multicast
p = SDOption_IP6_Multicast(b'\x00\x15\x16')
assert(p.type == 0x16)
assert(p.len == 0x0015)
+SD OPTION IP6 SD EndPoint
= Check pkg type & length
p = SDOption_IP6_SD_EndPoint()
assert(p.type == 0x26)
assert(p.len == 0x015)
= Check payload guess
assert(SDOption_IP6_SD_EndPoint().guess_payload_class(
bytes(p)) == SDOption_IP6_SD_EndPoint)
= Check SDOption_IP6_SD_EndPoint
p = SDOption_IP6_SD_EndPoint(b'\x00\x15\x26')
assert(p.type == 0x26)
assert(p.len == 0x0015)
+ SD Flags
= Check the flags
p = SD()
p.set_flag("REBOOT", 1)
assert(p.flags == 0x80)
p.set_flag("REBOOT", 0)
assert(p.flags == 0x00)
p.set_flag("UNICAST", 1)
assert(p.flags == 0x40)
p.set_flag("UNICAST", 0)
assert(p.flags == 0x00)
p.set_flag("REBOOT", 1)
p.set_flag("UNICAST", 1)
assert(p.flags == 0xc0)
+SD Get Someip Packet
= Check someip packet
p_sd = SD()
sd_len = bytes(p_sd)
p_someip = p_sd.get_someip()
assert(len(bytes(p_someip)) == SOMEIP._OVERALL_LEN_NOPAYLOAD)
p = p_sd.get_someip(stacked=True)
assert(len(bytes(p)) == SOMEIP._OVERALL_LEN_NOPAYLOAD + 12)
+ SD
= Check length of package without entries nor options
p = SD()
assert(len(bytes(p)) == 12)
= Check entries to array and size check
p.set_entryArray([SDEntry_Service(), SDEntry_EventGroup()])
assert(struct.unpack("!L", bytes(p)[4:8])[0] == 32)
p.set_entryArray([])
assert(struct.unpack("!L", bytes(p)[4:8])[0] == 0)
= Check Options to array and size check
p.set_optionArray([SDOption_IP4_EndPoint(), SDOption_IP4_EndPoint()])
assert(struct.unpack("!L", bytes(p)[8:12])[0] == 24)
p.set_optionArray([])
assert(struct.unpack("!L", bytes(p)[8:12])[0] == 0)
= Check Entries & Options to array and size check
p.set_entryArray([SDEntry_Service(), SDEntry_EventGroup()])
p.set_optionArray([SDOption_IP4_EndPoint(), SDOption_IP4_EndPoint()])
assert(struct.unpack("!L", bytes(p)[4:8])[0] == 32)
assert(struct.unpack("!L", bytes(p)[40:44])[0] == 24)

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,768 @@
% Regression tests for the UDS layer
# More information at http://www.secdev.org/projects/UTscapy/
############
############
+ Basic operations
= Load module
load_contrib("automotive.uds")
= Check if positive response answers
dsc = UDS(b'\x10')
dscpr = UDS(b'\x50')
assert dscpr.answers(dsc)
= Check hashret
dsc.hashret() == dscpr.hashret()
= Check if negative response answers
dsc = UDS(b'\x10')
neg = UDS(b'\x7f\x10')
assert neg.answers(dsc)
= CHECK hashret NEG
dsc.hashret() == neg.hashret()
= Check if negative response answers not
dsc = UDS(b'\x10')
neg = UDS(b'\x7f\x11')
assert not neg.answers(dsc)
= Check if positive response answers not
dsc = UDS(b'\x10')
somePacket = UDS(b'\x49')
assert not somePacket.answers(dsc)
= Check UDS_DSC
dsc = UDS(b'\x10\x01')
assert dsc.service == 0x10
assert dsc.diagnosticSessionType == 0x01
= Check UDS_DSC
dsc = UDS()/UDS_DSC(b'\x01')
assert dsc.service == 0x10
assert dsc.diagnosticSessionType == 0x01
= Check UDS_DSCPR
dscpr = UDS(b'\x50\x02beef')
assert dscpr.service == 0x50
assert dscpr.diagnosticSessionType == 0x02
= Check UDS_DSCPR
dscpr = UDS()/UDS_DSCPR(b'\x02beef')
assert dscpr.service == 0x50
assert dscpr.diagnosticSessionType == 0x02
assert dscpr.sessionParameterRecord == b"beef"
= Check UDS_ER
er = UDS(b'\x11\x01')
assert er.service == 0x11
assert er.resetType == 0x01
= Check UDS_ER
er = UDS()/UDS_ER(resetType="hardReset")
assert er.service == 0x11
assert er.resetType == 0x01
= Check UDS_ERPR
erpr = UDS(b'\x51\x01')
assert erpr.service == 0x51
assert erpr.resetType == 0x01
= Check UDS_ERPR
erpr = UDS(b'\x51\x04\x10')
assert erpr.service == 0x51
assert erpr.resetType == 0x04
assert erpr.powerDownTime == 0x10
= Check UDS_SA
sa = UDS(b'\x27\x00c0ffee')
assert sa.service == 0x27
assert sa.securityAccessType == 0x0
assert sa.securityKey == b'c0ffee'
= Check UDS_SA
sa = UDS(b'\x27\x01c0ffee')
assert sa.service == 0x27
assert sa.securityAccessType == 0x1
assert sa.securityAccessDataRecord == b'c0ffee'
= Check UDS_SAPR
sapr = UDS(b'\x67\x01c0ffee')
assert sapr.service == 0x67
assert sapr.securityAccessType == 0x1
assert sapr.securitySeed == b'c0ffee'
= Check UDS_SAPR
sapr = UDS(b'\x67\x00')
assert sapr.service == 0x67
assert sapr.securityAccessType == 0x0
= Check UDS_CC
cc = UDS(b'\x28\x01\xff')
assert cc.service == 0x28
assert cc.controlType == 0x1
assert cc.communicationType0 == 0x3
assert cc.communicationType1 == 0x3
assert cc.communicationType2 == 0xf
= Check UDS_CCPR
ccpr = UDS(b'\x68\x01')
assert ccpr.service == 0x68
assert ccpr.controlType == 0x1
= Check UDS_TP
tp = UDS(b'\x3E\x01')
assert tp.service == 0x3e
assert tp.subFunction == 0x1
= Check UDS_TPPR
tppr = UDS(b'\x7E\x01')
assert tppr.service == 0x7e
assert tppr.zeroSubFunction == 0x1
= Check UDS_ATP
atp = UDS(b'\x83\x01')
assert atp.service == 0x83
assert atp.timingParameterAccessType == 0x1
= Check UDS_ATP
atp = UDS(b'\x83\x04coffee')
assert atp.service == 0x83
assert atp.timingParameterAccessType == 0x4
assert atp.timingParameterRequestRecord == b'coffee'
= Check UDS_ATPPR
atppr = UDS(b'\xc3\x01')
assert atppr.service == 0xc3
assert atppr.timingParameterAccessType == 0x1
= Check UDS_ATPPR
atppr = UDS(b'\xc3\x03coffee')
assert atppr.service == 0xc3
assert atppr.timingParameterAccessType == 0x3
assert atppr.timingParameterResponseRecord == b'coffee'
= Check UDS_SDT
sdt = UDS(b'\x84coffee')
assert sdt.service == 0x84
assert sdt.securityDataRequestRecord == b'coffee'
= Check UDS_SDTPR
sdtpr = UDS(b'\xC4coffee')
assert sdtpr.service == 0xC4
assert sdtpr.securityDataResponseRecord == b'coffee'
= Check UDS_CDTCS
cdtcs = UDS(b'\x85\x00coffee')
assert cdtcs.service == 0x85
assert cdtcs.DTCSettingType == 0
assert cdtcs.DTCSettingControlOptionRecord == b'coffee'
= Check UDS_CDTCSPR
cdtcspr = UDS(b'\xC5\x00')
assert cdtcspr.service == 0xC5
assert cdtcspr.DTCSettingType == 0
= Check UDS_ROE
roe = UDS(b'\x86\x00\x10coffee')
assert roe.service == 0x86
assert roe.eventType == 0
assert roe.eventWindowTime == 16
assert roe.eventTypeRecord == b'coffee'
= Check UDS_ROEPR
roepr = UDS(b'\xC6\x00\x01\x10coffee')
assert roepr.service == 0xC6
assert roepr.eventType == 0
assert roepr.numberOfIdentifiedEvents == 1
assert roepr.eventWindowTime == 16
assert roepr.eventTypeRecord == b'coffee'
= Check UDS_LC
lc = UDS(b'\x87\x01\x02')
assert lc.service == 0x87
assert lc.linkControlType == 0x01
assert lc.baudrateIdentifier == 0x02
= Check UDS_LC
lc = UDS(b'\x87\x02\x02\x03\x04')
assert lc.service == 0x87
assert lc.linkControlType == 0x02
assert lc.baudrateHighByte == 0x02
assert lc.baudrateMiddleByte == 0x03
assert lc.baudrateLowByte == 0x04
= Check UDS_LCPR
lcpr = UDS(b'\xC7\x01')
assert lcpr.service == 0xC7
assert lcpr.linkControlType == 0x01
= Check UDS_RDBI
rdbi = UDS(b'\x22\x01\x02')
assert rdbi.service == 0x22
assert rdbi.identifiers[0] == 0x0102
= Build UDS_RDBI
rdbi = UDS()/UDS_RDBI(identifiers=[0x102])
assert rdbi.service == 0x22
assert rdbi.identifiers[0] == 0x0102
assert bytes(rdbi) == b'\x22\x01\x02'
= Check UDS_RDBI2
rdbi = UDS(b'\x22\x01\x02\x03\x04')
assert rdbi.service == 0x22
assert rdbi.identifiers[0] == 0x0102
assert rdbi.identifiers[1] == 0x0304
assert raw(rdbi) == b'\x22\x01\x02\x03\x04'
= Build UDS_RDBI2
rdbi = UDS()/UDS_RDBI(identifiers=[0x102, 0x304])
assert rdbi.service == 0x22
assert rdbi.identifiers[0] == 0x0102
assert rdbi.identifiers[1] == 0x0304
assert raw(rdbi) == b'\x22\x01\x02\x03\x04'
= Check UDS_RDBIPR
rdbipr = UDS(b'\x62\x01\x02dieselgate')
assert rdbipr.service == 0x62
assert rdbipr.dataIdentifier == 0x0102
assert rdbipr.load == b'dieselgate'
= Check UDS_RMBA
rmba = UDS(b'\x23\x11\x02\x02')
assert rmba.service == 0x23
assert rmba.memorySizeLen == 1
assert rmba.memoryAddressLen == 1
assert rmba.memoryAddress1 == 2
assert rmba.memorySize1 == 2
= Check UDS_RMBA
rmba = UDS(b'\x23\x22\x02\x02\x03\x03')
assert rmba.service == 0x23
assert rmba.memorySizeLen == 2
assert rmba.memoryAddressLen == 2
assert rmba.memoryAddress2 == 0x202
assert rmba.memorySize2 == 0x303
= Check UDS_RMBA
rmba = UDS(b'\x23\x33\x02\x02\x02\x03\x03\x03')
assert rmba.service == 0x23
assert rmba.memorySizeLen == 3
assert rmba.memoryAddressLen == 3
assert rmba.memoryAddress3 == 0x20202
assert rmba.memorySize3 == 0x30303
= Check UDS_RMBA
rmba = UDS(b'\x23\x44\x02\x02\x02\x02\x03\x03\x03\x03')
assert rmba.service == 0x23
assert rmba.memorySizeLen == 4
assert rmba.memoryAddressLen == 4
assert rmba.memoryAddress4 == 0x2020202
assert rmba.memorySize4 == 0x3030303
= Check UDS_RMBAPR
rmbapr = UDS(b'\x63muchData')
assert rmbapr.service == 0x63
assert rmbapr.dataRecord == b'muchData'
= Check UDS_RSDBI
rsdbi = UDS(b'\x24\x12\x34')
assert rsdbi.service == 0x24
assert rsdbi.dataIdentifier == 0x1234
= Check UDS_RSDBIPR
rsdbipr = UDS(b'\x64\x12\x34\xffmuchData')
assert rsdbipr.service == 0x64
assert rsdbipr.dataIdentifier == 0x1234
assert rsdbipr.scalingByte == 255
assert rsdbipr.dataRecord == b'muchData'
= Check UDS_RSDBPI
rsdbpi = UDS(b'\x2a\x12\x34coffee')
assert rsdbpi.service == 0x2a
assert rsdbpi.transmissionMode == 0x12
assert rsdbpi.periodicDataIdentifier == 0x34
assert rsdbpi.furtherPeriodicDataIdentifier == b'coffee'
= Check UDS_RSDBPIPR
rsdbpipr = UDS(b'\x6a\xff\x12\x34')
assert rsdbpipr.service == 0x6a
assert rsdbpipr.periodicDataIdentifier == 255
assert rsdbpipr.dataRecord == b'\x12\x34'
= Check UDS_DDDI
dddi = UDS(b'\x2c\x12coffee')
assert dddi.service == 0x2c
assert dddi.definitionMode == 0x12
assert dddi.dataRecord == b'coffee'
= Check UDS_DDDIPR
dddipr = UDS(b'\x6c\x12\x44\x55')
assert dddipr.service == 0x6c
assert dddipr.definitionMode == 0x12
assert dddipr.dynamicallyDefinedDataIdentifier == 0x4455
= Check UDS_WDBI
wdbi = UDS(b'\x2e\x01\x02dieselgate')
assert wdbi.service == 0x2e
assert wdbi.dataIdentifier == 0x0102
assert wdbi.load == b'dieselgate'
= Build UDS_WDBI
wdbi = UDS()/UDS_WDBI(dataIdentifier=0x0102)/Raw(load=b'dieselgate')
assert wdbi.service == 0x2e
assert wdbi.dataIdentifier == 0x0102
assert wdbi.load == b'dieselgate'
assert bytes(wdbi) == b'\x2e\x01\x02dieselgate'
= Check UDS_WDBIPR
wdbipr = UDS(b'\x6e\x01\x02')
assert wdbipr.service == 0x6e
assert wdbipr.dataIdentifier == 0x0102
= Check UDS_WMBA
wmba = UDS(b'\x3d\x11\x02\x02muchData')
assert wmba.service == 0x3d
assert wmba.memorySizeLen == 1
assert wmba.memoryAddressLen == 1
assert wmba.memoryAddress1 == 2
assert wmba.memorySize1 == 2
assert wmba.dataRecord == b'muchData'
= Check UDS_WMBA
wmba = UDS(b'\x3d\x22\x02\x02\x03\x03muchData')
assert wmba.service == 0x3d
assert wmba.memorySizeLen == 2
assert wmba.memoryAddressLen == 2
assert wmba.memoryAddress2 == 0x202
assert wmba.memorySize2 == 0x303
assert wmba.dataRecord == b'muchData'
= Check UDS_WMBA
wmba = UDS(b'\x3d\x33\x02\x02\x02\x03\x03\x03muchData')
assert wmba.service == 0x3d
assert wmba.memorySizeLen == 3
assert wmba.memoryAddressLen == 3
assert wmba.memoryAddress3 == 0x20202
assert wmba.memorySize3 == 0x30303
assert wmba.dataRecord == b'muchData'
= Check UDS_WMBA
wmba = UDS(b'\x3d\x44\x02\x02\x02\x02\x03\x03\x03\x03muchData')
assert wmba.service == 0x3d
assert wmba.memorySizeLen == 4
assert wmba.memoryAddressLen == 4
assert wmba.memoryAddress4 == 0x2020202
assert wmba.memorySize4 == 0x3030303
assert wmba.dataRecord == b'muchData'
= Check UDS_WMBAPR
wmbapr = UDS(b'\x7d\x11\x02\x02')
assert wmbapr.service == 0x7d
assert wmbapr.memorySizeLen == 1
assert wmbapr.memoryAddressLen == 1
assert wmbapr.memoryAddress1 == 2
assert wmbapr.memorySize1 == 2
= Check UDS_WMBAPR
wmbapr = UDS(b'\x7d\x22\x02\x02\x03\x03')
assert wmbapr.service == 0x7d
assert wmbapr.memorySizeLen == 2
assert wmbapr.memoryAddressLen == 2
assert wmbapr.memoryAddress2 == 0x202
assert wmbapr.memorySize2 == 0x303
= Check UDS_WMBAPR
wmbapr = UDS(b'\x7d\x33\x02\x02\x02\x03\x03\x03')
assert wmbapr.service == 0x7d
assert wmbapr.memorySizeLen == 3
assert wmbapr.memoryAddressLen == 3
assert wmbapr.memoryAddress3 == 0x20202
assert wmbapr.memorySize3 == 0x30303
= Check UDS_WMBAPR
wmbapr = UDS(b'\x7d\x44\x02\x02\x02\x02\x03\x03\x03\x03')
assert wmbapr.service == 0x7d
assert wmbapr.memorySizeLen == 4
assert wmbapr.memoryAddressLen == 4
assert wmbapr.memoryAddress4 == 0x2020202
assert wmbapr.memorySize4 == 0x3030303
= Check UDS_CDTCI
cdtci = UDS(b'\x14\x44\x02\x03')
assert cdtci.service == 0x14
assert cdtci.groupOfDTCHighByte == 0x44
assert cdtci.groupOfDTCMiddleByte == 0x02
assert cdtci.groupOfDTCLowByte == 0x3
= Check UDS_RDTCI
rdtci = UDS(b'\x19\x44')
assert rdtci.service == 0x19
assert rdtci.reportType == 0x44
= Check UDS_RDTCI
rdtci = UDS(b'\x19\x01\xff')
assert rdtci.service == 0x19
assert rdtci.reportType == 0x01
assert rdtci.DTCStatusMask == 0xff
= Check UDS_RDTCI
rdtci = UDS(b'\x19\x02\xff')
assert rdtci.service == 0x19
assert rdtci.reportType == 0x02
assert rdtci.DTCStatusMask == 0xff
= Check UDS_RDTCI
rdtci = UDS(b'\x19\x0f\xff')
assert rdtci.service == 0x19
assert rdtci.reportType == 0x0f
assert rdtci.DTCStatusMask == 0xff
= Check UDS_RDTCI
rdtci = UDS(b'\x19\x11\xff')
assert rdtci.service == 0x19
assert rdtci.reportType == 0x11
assert rdtci.DTCStatusMask == 0xff
= Check UDS_RDTCI
rdtci = UDS(b'\x19\x12\xff')
assert rdtci.service == 0x19
assert rdtci.reportType == 0x12
assert rdtci.DTCStatusMask == 0xff
= Check UDS_RDTCI
rdtci = UDS(b'\x19\x13\xff')
assert rdtci.service == 0x19
assert rdtci.reportType == 0x13
assert rdtci.DTCStatusMask == 0xff
= Check UDS_RDTCI
rdtci = UDS(b'\x19\x03\xff\xee\xdd\xaa')
assert rdtci.service == 0x19
assert rdtci.reportType == 0x03
assert rdtci.DTCHighByte == 0xff
assert rdtci.DTCMiddleByte == 0xee
assert rdtci.DTCLowByte == 0xdd
assert rdtci.DTCSnapshotRecordNumber == 0xaa
= Check UDS_RDTCI
rdtci = UDS(b'\x19\x04\xff\xee\xdd\xaa')
assert rdtci.service == 0x19
assert rdtci.reportType == 0x04
assert rdtci.DTCHighByte == 0xff
assert rdtci.DTCMiddleByte == 0xee
assert rdtci.DTCLowByte == 0xdd
assert rdtci.DTCSnapshotRecordNumber == 0xaa
= Check UDS_RDTCI
rdtci = UDS(b'\x19\x05\xaa')
assert rdtci.service == 0x19
assert rdtci.reportType == 0x05
assert rdtci.DTCSnapshotRecordNumber == 0xaa
= Check UDS_RDTCI
rdtci = UDS(b'\x19\x06\xff\xee\xdd\xaa')
assert rdtci.service == 0x19
assert rdtci.reportType == 0x06
assert rdtci.DTCHighByte == 0xff
assert rdtci.DTCMiddleByte == 0xee
assert rdtci.DTCLowByte == 0xdd
assert rdtci.DTCExtendedDataRecordNumber == 0xaa
= Check UDS_RDTCI
rdtci = UDS(b'\x19\x07\xaa\xbb')
assert rdtci.service == 0x19
assert rdtci.reportType == 0x07
assert rdtci.DTCSeverityMask == 0xaa
assert rdtci.DTCStatusMask == 0xbb
= Check UDS_RDTCI
rdtci = UDS(b'\x19\x08\xaa\xbb')
assert rdtci.service == 0x19
assert rdtci.reportType == 0x08
assert rdtci.DTCSeverityMask == 0xaa
assert rdtci.DTCStatusMask == 0xbb
= Check UDS_RDTCI
rdtci = UDS(b'\x19\x09\xff\xee\xdd')
assert rdtci.service == 0x19
assert rdtci.reportType == 0x09
assert rdtci.DTCHighByte == 0xff
assert rdtci.DTCMiddleByte == 0xee
assert rdtci.DTCLowByte == 0xdd
= Check UDS_RDTCI
rdtci = UDS(b'\x19\x10\xff\xee\xdd\xaa')
assert rdtci.service == 0x19
assert rdtci.reportType == 0x10
assert rdtci.DTCHighByte == 0xff
assert rdtci.DTCMiddleByte == 0xee
assert rdtci.DTCLowByte == 0xdd
assert rdtci.DTCExtendedDataRecordNumber == 0xaa
= Check UDS_RDTCIPR
rdtcipr = UDS(b'\x59\x01\xff\xee\xdd\xaa')
assert rdtcipr.service == 0x59
assert rdtcipr.reportType == 1
assert rdtcipr.DTCStatusAvailabilityMask == 0xff
assert rdtcipr.DTCFormatIdentifier == 0xee
assert rdtcipr.DTCCount == 0xddaa
= Check UDS_RDTCIPR
rdtcipr = UDS(b'\x59\x02\xff\xee\xdd\xaa')
assert rdtcipr.service == 0x59
assert rdtcipr.reportType == 2
assert rdtcipr.DTCStatusAvailabilityMask == 0xff
assert rdtcipr.DTCAndStatusRecord == b'\xee\xdd\xaa'
= Check UDS_RDTCIPR
rdtcipr = UDS(b'\x59\x03\xff\xee\xdd\xaa')
assert rdtcipr.service == 0x59
assert rdtcipr.reportType == 3
assert rdtcipr.dataRecord == b'\xff\xee\xdd\xaa'
= Check UDS_RC
rc = UDS(b'\x31\x03\xff\xee\xdd\xaa')
assert rc.service == 0x31
assert rc.routineControlType == 3
assert rc.routineIdentifier == 0xffee
assert rc.routineControlOptionRecord == b'\xdd\xaa'
= Check UDS_RCPR
rcpr = UDS(b'\x71\x03\xff\xee\xdd\xaa')
assert rcpr.service == 0x71
assert rcpr.routineControlType == 3
assert rcpr.routineIdentifier == 0xffee
assert rcpr.routineStatusRecord == b'\xdd\xaa'
= Check UDS_RD
rd = UDS(b'\x34\xaa\x11\x02\x02')
assert rd.service == 0x34
assert rd.dataFormatIdentifier == 0xaa
assert rd.memorySizeLen == 1
assert rd.memoryAddressLen == 1
assert rd.memoryAddress1 == 2
assert rd.memorySize1 == 2
= Check UDS_RD
rd = UDS(b'\x34\xaa\x22\x02\x02\x03\x03')
assert rd.service == 0x34
assert rd.dataFormatIdentifier == 0xaa
assert rd.memorySizeLen == 2
assert rd.memoryAddressLen == 2
assert rd.memoryAddress2 == 0x202
assert rd.memorySize2 == 0x303
= Check UDS_RD
rd = UDS(b'\x34\xaa\x33\x02\x02\x02\x03\x03\x03')
assert rd.service == 0x34
assert rd.dataFormatIdentifier == 0xaa
assert rd.memorySizeLen == 3
assert rd.memoryAddressLen == 3
assert rd.memoryAddress3 == 0x20202
assert rd.memorySize3 == 0x30303
= Check UDS_RD
rd = UDS(b'\x34\xaa\x44\x02\x02\x02\x02\x03\x03\x03\x03')
assert rd.service == 0x34
assert rd.dataFormatIdentifier == 0xaa
assert rd.memorySizeLen == 4
assert rd.memoryAddressLen == 4
assert rd.memoryAddress4 == 0x2020202
assert rd.memorySize4 == 0x3030303
= Check UDS_RDPR
rdpr = UDS(b'\x74\xaa\x44\x02\x02\x02\x02\x03\x03\x03\x03')
assert rdpr.service == 0x74
assert rdpr.routineControlType == 0xaa
assert rdpr.memorySizeLen == 4
assert rdpr.memoryAddressLen == 4
assert rdpr.maxNumberOfBlockLength == b'\x02\x02\x02\x02\x03\x03\x03\x03'
= Check UDS_RU
ru = UDS(b'\x35\xaa\x11\x02\x02')
assert ru.service == 0x35
assert ru.dataFormatIdentifier == 0xaa
assert ru.memorySizeLen == 1
assert ru.memoryAddressLen == 1
assert ru.memoryAddress1 == 2
assert ru.memorySize1 == 2
= Check UDS_RU
ru = UDS(b'\x35\xaa\x22\x02\x02\x03\x03')
assert ru.service == 0x35
assert ru.dataFormatIdentifier == 0xaa
assert ru.memorySizeLen == 2
assert ru.memoryAddressLen == 2
assert ru.memoryAddress2 == 0x202
assert ru.memorySize2 == 0x303
= Check UDS_RU
ru = UDS(b'\x35\xaa\x33\x02\x02\x02\x03\x03\x03')
assert ru.service == 0x35
assert ru.dataFormatIdentifier == 0xaa
assert ru.memorySizeLen == 3
assert ru.memoryAddressLen == 3
assert ru.memoryAddress3 == 0x20202
assert ru.memorySize3 == 0x30303
= Check UDS_RU
ru = UDS(b'\x35\xaa\x44\x02\x02\x02\x02\x03\x03\x03\x03')
assert ru.service == 0x35
assert ru.dataFormatIdentifier == 0xaa
assert ru.memorySizeLen == 4
assert ru.memoryAddressLen == 4
assert ru.memoryAddress4 == 0x2020202
assert ru.memorySize4 == 0x3030303
= Check UDS_RUPR
rupr = UDS(b'\x75\xaa\x44\x02\x02\x02\x02\x03\x03\x03\x03')
assert rupr.service == 0x75
assert rupr.routineControlType == 0xaa
assert rupr.memorySizeLen == 4
assert rupr.memoryAddressLen == 4
assert rupr.maxNumberOfBlockLength == b'\x02\x02\x02\x02\x03\x03\x03\x03'
= Check UDS_TD
td = UDS(b'\x36\xaapayload')
assert td.service == 0x36
assert td.blockSequenceCounter == 0xaa
assert td.transferRequestParameterRecord == b'payload'
= Check UDS_TDPR
tdpr = UDS(b'\x76\xaapayload')
assert tdpr.service == 0x76
assert tdpr.blockSequenceCounter == 0xaa
assert tdpr.transferResponseParameterRecord == b'payload'
= Check UDS_RTE
rte = UDS(b'\x37payload')
assert rte.service == 0x37
assert rte.transferRequestParameterRecord == b'payload'
= Check UDS_RTEPR
rtepr = UDS(b'\x77payload')
assert rtepr.service == 0x77
assert rtepr.transferResponseParameterRecord == b'payload'
= Check UDS_IOCBI
iocbi = UDS(b'\x2f\x23\x34\xffcoffee')
assert iocbi.service == 0x2f
assert iocbi.dataIdentifier == 0x2334
assert iocbi.controlOptionRecord == 255
assert iocbi.controlEnableMaskRecord == b'coffee'
= Check UDS_NRC
nrc = UDS(b'\x7f\x22\x33')
assert nrc.service == 0x7f
assert nrc.requestServiceId == 0x22
assert nrc.negativeResponseCode == 0x33

View file

@ -0,0 +1,11 @@
# This file is part of Scapy
# See http://www.secdev.org/projects/scapy for more information
# Copyright (C) Nils Weiss <nils@we155.de>
# This program is published under a GPLv2 license
# scapy.contrib.status = skip
"""
Package of contrib automotive bmw specific modules
that have to be loaded explicitly.
"""

File diff suppressed because it is too large Load diff

71
libs/scapy/contrib/avs.py Executable file
View file

@ -0,0 +1,71 @@
# This file is part of Scapy
# Scapy is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# any later version.
#
# Scapy is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Scapy. If not, see <http://www.gnu.org/licenses/>.
# scapy.contrib.description = AVS WLAN Monitor Header
# scapy.contrib.status = loads
from scapy.packet import Packet, bind_layers
from scapy.fields import IntEnumField, IntField, LongField, SignedIntField
from scapy.layers.dot11 import Dot11
from scapy.data import DLT_IEEE802_11_RADIO_AVS
from scapy.config import conf
AVSWLANPhyType = {0: "Unknown",
1: "FHSS 802.11 '97",
2: "DSSS 802.11 '97",
3: "IR Baseband",
4: "DSSS 802.11b",
5: "PBCC 802.11b",
6: "OFDM 802.11g",
7: "PBCC 802.11g",
8: "OFDM 802.11a"}
AVSWLANEncodingType = {0: "Unknown",
1: "CCK",
2: "PBCC",
3: "OFDM"}
AVSWLANSSIType = {0: "None",
1: "Normalized RSSI",
2: "dBm",
3: "Raw RSSI"}
AVSWLANPreambleType = {0: "Unknown",
1: "Short",
2: "Long"}
class AVSWLANHeader(Packet):
""" iwpriv eth1 set_prismhdr 1 """
name = "AVS WLAN Monitor Header"
fields_desc = [IntField("version", 1),
IntField("len", 64),
LongField("mactime", 0),
LongField("hosttime", 0),
IntEnumField("phytype", 0, AVSWLANPhyType),
IntField("channel", 0),
IntField("datarate", 0),
IntField("antenna", 0),
IntField("priority", 0),
IntEnumField("ssi_type", 0, AVSWLANSSIType),
SignedIntField("ssi_signal", 0),
SignedIntField("ssi_noise", 0),
IntEnumField("preamble", 0, AVSWLANPreambleType),
IntEnumField("encoding", 0, AVSWLANEncodingType),
]
conf.l2types.register(DLT_IEEE802_11_RADIO_AVS, AVSWLANHeader)
bind_layers(AVSWLANHeader, Dot11)

19
libs/scapy/contrib/avs.uts Executable file
View file

@ -0,0 +1,19 @@
% Regression tests for the avs module
+ Basic AVS test
= Default build, storage and dissection
pkt = AVSWLANHeader()/Dot11()/Dot11Auth()
_filepath = get_temp_file(autoext=".pcap")
wrpcap(_filepath, pkt)
pkt1 = rdpcap(_filepath)[0]
assert raw(pkt) == raw(pkt1)
assert AVSWLANHeader in pkt
assert Dot11 in pkt
assert Dot11Auth in pkt
try:
os.remove(_filepath)
except Exception:
pass

42
libs/scapy/contrib/bfd.py Executable file
View file

@ -0,0 +1,42 @@
# This file is part of Scapy
# See http://www.secdev.org/projects/scapy for more information
# Copyright (C) Parag Bhide
# This program is published under GPLv2 license
"""
BFD - Bidirectional Forwarding Detection - RFC 5880, 5881
"""
# scapy.contrib.description = BFD
# scapy.contrib.status = loads
from scapy.packet import Packet, bind_layers, bind_bottom_up
from scapy.fields import BitField, FlagsField, XByteField
from scapy.layers.inet import UDP
class BFD(Packet):
name = "BFD"
fields_desc = [
BitField("version", 1, 3),
BitField("diag", 0, 5),
BitField("sta", 3, 2),
FlagsField("flags", 0x00, 6, ['P', 'F', 'C', 'A', 'D', 'M']),
XByteField("detect_mult", 0x03),
XByteField("len", 24),
BitField("my_discriminator", 0x11111111, 32),
BitField("your_discriminator", 0x22222222, 32),
BitField("min_tx_interval", 1000000000, 32),
BitField("min_rx_interval", 1000000000, 32),
BitField("echo_rx_interval", 1000000000, 32)]
def mysummary(self):
return self.sprintf(
"BFD (my_disc=%BFD.my_discriminator%,"
"your_disc=%BFD.my_discriminator%)"
)
bind_bottom_up(UDP, BFD, dport=3784)
bind_bottom_up(UDP, BFD, sport=3784)
bind_layers(UDP, BFD, sport=3784, dport=3784)

2524
libs/scapy/contrib/bgp.py Executable file

File diff suppressed because it is too large Load diff

740
libs/scapy/contrib/bgp.uts Executable file
View file

@ -0,0 +1,740 @@
#################################### bgp.py ##################################
% Regression tests for the bgp module
# Default configuration : OLD speaker (see RFC 6793)
bgp_module_conf.use_2_bytes_asn = True
################################ BGPNLRI_IPv4 ################################
+ BGPNLRI_IPv4 class tests
= BGPNLRI_IPv4 - Instantiation
raw(BGPNLRI_IPv4()) == b'\x00'
= BGPNLRI_IPv4 - Instantiation with specific values (1)
raw(BGPNLRI_IPv4(prefix = '255.255.255.255/32')) == b' \xff\xff\xff\xff'
= BGPNLRI_IPv4 - Instantiation with specific values (2)
raw(BGPNLRI_IPv4(prefix = '0.0.0.0/0')) == b'\x00'
= BGPNLRI_IPv4 - Instantiation with specific values (3)
raw(BGPNLRI_IPv4(prefix = '192.0.2.0/24')) == b'\x18\xc0\x00\x02'
= BGPNLRI_IPv4 - Basic dissection
nlri = BGPNLRI_IPv4(b'\x00')
nlri.prefix == '0.0.0.0/0'
= BGPNLRI_IPv4 - Dissection with specific values
nlri = BGPNLRI_IPv4(b'\x18\xc0\x00\x02')
nlri.prefix == '192.0.2.0/24'
################################ BGPNLRI_IPv6 ################################
+ BGPNLRI_IPv6 class tests
= BGPNLRI_IPv6 - Instantiation
raw(BGPNLRI_IPv6()) == b'\x00'
= BGPNLRI_IPv6 - Instantiation with specific values (1)
raw(BGPNLRI_IPv6(prefix = '::/0')) == b'\x00'
= BGPNLRI_IPv6 - Instantiation with specific values (2)
raw(BGPNLRI_IPv6(prefix = '2001:db8::/32')) == b' \x01\r\xb8'
= BGPNLRI_IPv6 - Basic dissection
nlri = BGPNLRI_IPv6(b'\x00')
nlri.prefix == '::/0'
= BGPNLRI_IPv6 - Dissection with specific values
nlri = BGPNLRI_IPv6(b' \x01\r\xb8')
nlri.prefix == '2001:db8::/32'
#################################### BGP #####################################
+ BGP class tests
= BGP - Instantiation (Should be a KEEPALIVE)
m = BGP()
assert(raw(m) == b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x13\x04')
assert(m.type == BGP.KEEPALIVE_TYPE)
= BGP - Instantiation with specific values (1)
raw(BGP(type = 0)) == b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x13\x00'
= BGP - Instantiation with specific values (2)
raw(BGP(type = 1)) == b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x13\x01'
= BGP - Instantiation with specific values (3)
raw(BGP(type = 2)) == b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x13\x02'
= BGP - Instantiation with specific values (4)
raw(BGP(type = 3)) == b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x13\x03'
= BGP - Instantiation with specific values (5)
raw(BGP(type = 4)) == b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x13\x04'
= BGP - Instantiation with specific values (6)
raw(BGP(type = 5)) == b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x13\x05'
= BGP - Basic dissection
h = BGP(b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x13\x04')
assert(h.type == BGP.KEEPALIVE_TYPE)
assert(h.len == 19)
= BGP - Dissection with specific values
h = BGP(b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x13\x01')
assert(h.type == BGP.OPEN_TYPE)
assert(h.len == 19)
############################### BGPKeepAlive #################################
+ BGPKeepAlive class tests
= BGPKeepAlive - Instantiation (by default, should be a "generic" capability)
raw(BGPKeepAlive())
raw(BGPKeepAlive()) == b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x13\x04'
= BGPKeepAlive - Swallowing tests: combined BGPKeepAlive
o = BGPKeepAlive()
m=IP(src="12.0.0.1",dst="12.0.0.2")/TCP(dport=54321)/BGP(raw(o)*2)
m.show()
assert isinstance(m[BGPKeepAlive].payload, BGPKeepAlive)
assert m[BGPKeepAlive].payload.marker == 0xffffffffffffffffffffffffffffffff
############################### BGPCapability #################################
+ BGPCapability class tests
= BGPCapability - Instantiation (by default, should be a "generic" capability)
raw(BGPCapability())
raw(BGPCapability()) == b'\x00\x00'
= BGPCapability - Instantiation with specific values (1)
c = BGPCapability(code = 70)
assert(raw(c) == b'F\x00')
= BGPCapability - Check exception
from scapy.contrib.bgp import _BGPInvalidDataException
try:
BGPCapability("\x00")
False
except _BGPInvalidDataException:
True
= BGPCapability - Test haslayer()
assert BGPCapFourBytesASN().haslayer(BGPCapability)
assert BGPCapability in BGPCapFourBytesASN()
= BGPCapability - Test getlayer()
assert isinstance(BGPCapFourBytesASN().getlayer(BGPCapability), BGPCapFourBytesASN)
assert isinstance(BGPCapFourBytesASN()[BGPCapability], BGPCapFourBytesASN)
= BGPCapability - sessions (1)
p = IP()/TCP()/BGPCapability()
l = PacketList(p)
s = l.sessions() # Crashed on commit: e42ecdc54556c4852ca06b1a6da6c1ccbf3f522e
assert len(s) == 1
= BGPCapability - sessions (2)
p = IP()/UDP()/BGPCapability()
l = PacketList(p)
s = l.sessions() # Crashed on commit: e42ecdc54556c4852ca06b1a6da6c1ccbf3f522e
assert len(s) == 1
############################ BGPCapMultiprotocol ##############################
+ BGPCapMultiprotocol class tests
= BGPCapMultiprotocol - Inheritance
c = BGPCapMultiprotocol()
assert(isinstance(c, BGPCapability))
= BGPCapMultiprotocol - Instantiation
raw(BGPCapMultiprotocol()) == b'\x01\x04\x00\x00\x00\x00'
= BGPCapMultiprotocol - Instantiation with specific values (1)
raw(BGPCapMultiprotocol(afi = 1, safi = 1)) == b'\x01\x04\x00\x01\x00\x01'
= BGPCapMultiprotocol - Instantiation with specific values (2)
raw(BGPCapMultiprotocol(afi = 2, safi = 1)) == b'\x01\x04\x00\x02\x00\x01'
= BGPCapMultiprotocol - Dissection with specific values
c = BGPCapMultiprotocol(b'\x01\x04\x00\x02\x00\x01')
assert(c.code == 1)
assert(c.length == 4)
assert(c.afi == 2)
assert(c.reserved == 0)
assert(c.safi == 1)
############################### BGPCapORFBlock ###############################
+ BGPCapORFBlock class tests
= BGPCapORFBlock - Instantiation
raw(BGPCapORFBlock()) == b'\x00\x00\x00\x00\x00'
= BGPCapORFBlock - Instantiation with specific values (1)
raw(BGPCapORFBlock(afi = 1, safi = 1)) == b'\x00\x01\x00\x01\x00'
= BGPCapORFBlock - Instantiation with specific values (2)
raw(BGPCapORFBlock(afi = 2, safi = 1)) == b'\x00\x02\x00\x01\x00'
= BGPCapORFBlock - Basic dissection
c = BGPCapORFBlock(b'\x00\x00\x00\x00\x00')
c.afi == 0 and c.reserved == 0 and c.safi == 0 and c.orf_number == 0
= BGPCapORFBlock - Dissection with specific values
c = BGPCapORFBlock(b'\x00\x02\x00\x01\x00')
c.afi == 2 and c.reserved == 0 and c.safi == 1 and c.orf_number == 0
############################# BGPCapORFBlock.ORF ##############################
+ BGPCapORFBlock.ORF class tests
= BGPCapORFBlock.ORF - Instantiation
raw(BGPCapORFBlock.ORFTuple()) == b'\x00\x00'
= BGPCapORFBlock.ORF - Instantiation with specific values (1)
raw(BGPCapORFBlock.ORFTuple(orf_type = 64, send_receive = 3)) == b'@\x03'
= BGPCapORFBlock.ORF - Basic dissection
c = BGPCapORFBlock.ORFTuple(b'\x00\x00')
c.orf_type == 0 and c.send_receive == 0
= BGPCapORFBlock.ORF - Dissection with specific values
c = BGPCapORFBlock.ORFTuple(b'@\x03')
c.orf_type == 64 and c.send_receive == 3
################################# BGPCapORF ###################################
+ BGPCapORF class tests
= BGPCapORF - Inheritance
c = BGPCapORF()
assert(isinstance(c, BGPCapability))
= BGPCapORF - Instantiation
raw(BGPCapORF()) == b'\x03\x00'
= BGPCapORF - Instantiation with specific values (1)
raw(BGPCapORF(orf = [BGPCapORFBlock(afi = 1, safi = 1, entries = [BGPCapORFBlock.ORFTuple(orf_type = 64, send_receive = 3)])])) == b'\x03\x07\x00\x01\x00\x01\x01@\x03'
= BGPCapORF - Instantiation with specific values (2)
raw(BGPCapORF(orf = [BGPCapORFBlock(afi = 1, safi = 1, entries = [BGPCapORFBlock.ORFTuple(orf_type = 64, send_receive = 3)]), BGPCapORFBlock(afi = 2, safi = 1, entries = [BGPCapORFBlock.ORFTuple(orf_type = 64, send_receive = 3)])])) == b'\x03\x0e\x00\x01\x00\x01\x01@\x03\x00\x02\x00\x01\x01@\x03'
= BGPCapORF - Basic dissection
c = BGPCapORF(b'\x03\x00')
c.code == 3 and c.length == 0
= BGPCapORF - Dissection with specific values
c = BGPCapORF(orf = [BGPCapORFBlock(afi = 1, safi = 1, entries = [BGPCapORFBlock.ORFTuple(orf_type = 64, send_receive = 3)]), BGPCapORFBlock(afi = 2, safi = 1, entries = [BGPCapORFBlock.ORFTuple(orf_type = 64, send_receive = 3)])])
c.code == 3 and c.orf[0].afi == 1 and c.orf[0].safi == 1 and c.orf[0].entries[0].orf_type == 64 and c.orf[0].entries[0].send_receive == 3 and c.orf[1].afi == 2 and c.orf[1].safi == 1 and c.orf[1].entries[0].orf_type == 64 and c.orf[1].entries[0].send_receive == 3
= BGPCapORF - Dissection
p = BGPCapORF(b'\x03\x07\x00\x01\x00\x01\x01@\x03')
assert(len(p.orf) == 1)
####################### BGPCapGracefulRestart.GRTuple #########################
+ BGPCapGracefulRestart.GRTuple class tests
= BGPCapGracefulRestart.GRTuple - Instantiation
raw(BGPCapGracefulRestart.GRTuple()) == b'\x00\x00\x00\x00'
= BGPCapGracefulRestart.GRTuple - Instantiation with specific values
raw(BGPCapGracefulRestart.GRTuple(afi = 1, safi = 1, flags = 128)) == b'\x00\x01\x01\x80'
= BGPCapGracefulRestart.GRTuple - Basic dissection
c = BGPCapGracefulRestart.GRTuple(b'\x00\x00\x00\x00')
c.afi == 0 and c.safi == 0 and c.flags == 0
= BGPCapGracefulRestart.GRTuple - Dissection with specific values
c = BGPCapGracefulRestart.GRTuple(b'\x00\x01\x01\x80')
c.afi == 1 and c.safi == 1 and c.flags == 128
########################### BGPCapGracefulRestart #############################
+ BGPCapGracefulRestart class tests
= BGPCapGracefulRestart - Inheritance
c = BGPCapGracefulRestart()
assert(isinstance(c, BGPCapGracefulRestart))
= BGPCapGracefulRestart - Instantiation
raw(BGPCapGracefulRestart()) == b'@\x02\x00\x00'
= BGPCapGracefulRestart - Instantiation with specific values (1)
raw(BGPCapGracefulRestart(restart_time = 120, entries = [BGPCapGracefulRestart.GRTuple(afi = 1, safi = 1)])) == b'@\x06\x00x\x00\x01\x01\x00'
= BGPCapGracefulRestart - Instantiation with specific values (2)
raw(BGPCapGracefulRestart(restart_time = 120, entries = [BGPCapGracefulRestart.GRTuple(afi = 1, safi = 1)])) == b'@\x06\x00x\x00\x01\x01\x00'
= BGPCapGracefulRestart - Instantiation with specific values (3)
raw(BGPCapGracefulRestart(restart_time = 120, entries = [BGPCapGracefulRestart.GRTuple(afi = 1, safi = 1, flags = 128)])) == b'@\x06\x00x\x00\x01\x01\x80'
= BGPCapGracefulRestart - Instantiation with specific values (4)
raw(BGPCapGracefulRestart(restart_time = 120, restart_flags = 0x8, entries = [BGPCapGracefulRestart.GRTuple(afi = 1, safi = 1, flags = 128)])) == b'@\x06\x80x\x00\x01\x01\x80'
= BGPCapGracefulRestart - Basic dissection
c = BGPCapGracefulRestart(b'@\x02\x00\x00')
c.code == 64 and c.restart_flags == 0 and c.restart_time == 0
= BGPCapGracefulRestart - Dissection with specific values
c = BGPCapGracefulRestart(b'@\x06\x80x\x00\x01\x01\x80')
c.code == 64 and c.restart_time == 120 and c.restart_flags == 0x8 and c.entries[0].afi == 1 and c.entries[0].safi == 1 and c.entries[0].flags == 128
############################ BGPCapFourBytesASN ###############################
+ BGPCapFourBytesASN class tests
= BGPCapFourBytesASN - Inheritance
c = BGPCapFourBytesASN()
assert(isinstance(c, BGPCapFourBytesASN))
= BGPCapFourBytesASN - Instantiation
raw(BGPCapFourBytesASN()) == b'A\x04\x00\x00\x00\x00'
= BGPCapFourBytesASN - Instantiation with specific values (1)
raw(BGPCapFourBytesASN(asn = 6555555)) == b'A\x04\x00d\x07\xa3'
= BGPCapFourBytesASN - Instantiation with specific values (2)
raw(BGPCapFourBytesASN(asn = 4294967295)) == b'A\x04\xff\xff\xff\xff'
= BGPCapFourBytesASN - Basic dissection
c = BGPCapFourBytesASN(b'A\x04\x00\x00\x00\x00')
c.code == 65 and c.length == 4 and c.asn == 0
= BGPCapFourBytesASN - Dissection with specific values
c = BGPCapFourBytesASN(b'A\x04\xff\xff\xff\xff')
c.code == 65 and c.length == 4 and c.asn == 4294967295
####################### BGPAuthenticationInformation ##########################
+ BGPAuthenticationInformation class tests
= BGPAuthenticationInformation - Instantiation
raw(BGPAuthenticationInformation()) == b'\x00'
= BGPAuthenticationInformation - Basic dissection
c = BGPAuthenticationInformation(b'\x00')
c.authentication_code == 0 and c.authentication_data == None
################################# BGPOptParam #################################
+ BGPOptParam class tests
= BGPOptParam - Instantiation
raw(BGPOptParam()) == b'\x02\x00'
= BGPOptParam - Instantiation with specific values (1)
raw(BGPOptParam(param_type = 1)) == b'\x01\x00'
raw(BGPOptParam(param_type = 1, param_value = BGPAuthenticationInformation())) == b'\x01\x00'
= BGPOptParam - Instantiation with specific values (2)
raw(BGPOptParam(param_type = 2)) == b'\x02\x00'
= BGPOptParam - Instantiation with specific values (3)
raw(BGPOptParam(param_type = 2, param_value = BGPCapFourBytesASN(asn = 4294967295))) == b'\x02\x06A\x04\xff\xff\xff\xff'
= BGPOptParam - Instantiation with specific values (4)
raw(BGPOptParam(param_type = 2, param_value = BGPCapability(code = 127))) == b'\x02\x02\x7f\x00'
= BGPOptParam - Instantiation with specific values (5)
raw(BGPOptParam(param_type = 2, param_value = BGPCapability(code = 255))) == b'\x02\x02\xff\x00'
= BGPOptParam - Basic dissection
p = BGPOptParam(b'\x02\x00')
p.param_type == 2 and p.param_length == 0
= BGPOptParam - Dissection with specific values
p = BGPOptParam(b'\x02\x06A\x04\xff\xff\xff\xff')
p.param_type == 2 and p.param_length == 6 and p.param_value[0].code == 65 and p.param_value[0].length == 4 and p.param_value[0].asn == 4294967295
################################### BGPOpen ###################################
+ BGPOpen class tests
= BGPOpen - Instantiation
raw(BGPOpen()) == b'\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00'
= BGPOpen - Instantiation with specific values (1)
raw(BGPOpen(my_as = 64501, bgp_id = "192.0.2.1")) == b'\x04\xfb\xf5\x00\x00\xc0\x00\x02\x01\x00'
= BGPOpen - Instantiation with specific values (2)
opt = BGPOptParam(param_value = BGPCapMultiprotocol(afi = 1, safi = 1))
raw(BGPOpen(my_as = 64501, bgp_id = "192.0.2.1", opt_params = [opt])) == b'\x04\xfb\xf5\x00\x00\xc0\x00\x02\x01\x08\x02\x06\x01\x04\x00\x01\x00\x01'
= BGPOpen - Instantiation with specific values (3)
cap = BGPOptParam(param_value = BGPCapMultiprotocol(afi = 1, safi = 1))
capabilities = [cap]
cap = BGPOptParam(param_value = BGPCapability(code = 128))
capabilities.append(cap)
cap = BGPOptParam(param_value = BGPCapability(code = 2))
capabilities.append(cap)
cap = BGPOptParam(param_value = BGPCapGracefulRestart(restart_time = 120, entries = [BGPCapGracefulRestart.GRTuple(afi = 1, safi= 1, flags = 128)]))
capabilities.append(cap)
cap = BGPOptParam(param_value = BGPCapFourBytesASN(asn = 64503))
capabilities.append(cap)
raw(BGPOpen(my_as = 64503, bgp_id = "192.168.100.3", hold_time = 30, opt_params = capabilities)) == b'\x04\xfb\xf7\x00\x1e\xc0\xa8d\x03"\x02\x06\x01\x04\x00\x01\x00\x01\x02\x02\x80\x00\x02\x02\x02\x00\x02\x08@\x06\x00x\x00\x01\x01\x80\x02\x06A\x04\x00\x00\xfb\xf7'
= BGPOpen - Dissection with specific values (1)
m = BGP(b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00?\x01\x04\xfb\xf7\x00\x1e\xc0\xa8d\x03"\x02\x06\x01\x04\x00\x01\x00\x01\x02\x02\x80\x00\x02\x02\x02\x00\x02\x08@\x06\x00x\x00\x01\x01\x80\x02\x06A\x04\x00\x00\xfb\xf7')
assert(BGPHeader in m and BGPOpen in m)
assert(m.len == 63)
assert(m.type == BGP.OPEN_TYPE)
assert(m.version == 4)
assert(m.my_as == 64503)
assert(m.hold_time == 30)
assert(m.bgp_id == "192.168.100.3")
assert(m.opt_param_len == 34)
assert(isinstance(m.opt_params[0].param_value, BGPCapMultiprotocol))
assert(isinstance(m.opt_params[1].param_value, BGPCapability))
assert(isinstance(m.opt_params[2].param_value, BGPCapability))
assert(isinstance(m.opt_params[3].param_value, BGPCapGracefulRestart))
= BGPOpen - Dissection with specific values (2) (followed by a KEEPALIVE)
messages = b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00=\x01\x04\xfb\xf6\x00\xb4\xc0\xa8ze \x02\x06\x01\x04\x00\x01\x00\x01\x02\x06\x01\x04\x00\x02\x00\x01\x02\x02\x80\x00\x02\x02\x02\x00\x02\x06A\x04\x00\x00\xfb\xf6\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x13\x04'
m = BGP(messages)
raw(m) == b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00=\x01\x04\xfb\xf6\x00\xb4\xc0\xa8ze \x02\x06\x01\x04\x00\x01\x00\x01\x02\x06\x01\x04\x00\x02\x00\x01\x02\x02\x80\x00\x02\x02\x02\x00\x02\x06A\x04\x00\x00\xfb\xf6\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x13\x04'
= BGPOpen - Dissection with specific values (3)
m = BGP(b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x8f\x01\x04\xfd\xe8\x00\xb4\n\xff\xff\x01r\x02\x06\x01\x04\x00\x01\x00\x84\x02\x06\x01\x04\x00\x19\x00A\x02\x06\x01\x04\x00\x02\x00\x02\x02\x06\x01\x04\x00\x01\x00\x02\x02\x06\x01\x04\x00\x02\x00\x80\x02\x06\x01\x04\x00\x01\x00\x80\x02\x06\x01\x04\x00\x01\x00B\x02\x06\x01\x04\x00\x02\x00\x01\x02\x06\x01\x04\x00\x02\x00\x04\x02\x06\x01\x04\x00\x01\x00\x01\x02\x06\x01\x04\x00\x01\x00\x04\x02\x02\x80\x00\x02\x02\x02\x00\x02\x04@\x02\x80x\x02\x02F\x00\x02\x06A\x04\x00\x00\xfd\xe8')
assert(BGPHeader in m and BGPOpen in m)
= BGPOpen - Dissection with specific values (4)
m = BGP(b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x8f\x01\x04\xfd\xe8\x00\xb4\n\xff\xff\x02r\x02\x06\x01\x04\x00\x01\x00\x84\x02\x06\x01\x04\x00\x19\x00A\x02\x06\x01\x04\x00\x02\x00\x02\x02\x06\x01\x04\x00\x01\x00\x02\x02\x06\x01\x04\x00\x02\x00\x80\x02\x06\x01\x04\x00\x01\x00\x80\x02\x06\x01\x04\x00\x01\x00B\x02\x06\x01\x04\x00\x02\x00\x01\x02\x06\x01\x04\x00\x02\x00\x04\x02\x06\x01\x04\x00\x01\x00\x01\x02\x06\x01\x04\x00\x01\x00\x04\x02\x02\x80\x00\x02\x02\x02\x00\x02\x04@\x02\x00x\x02\x02F\x00\x02\x06A\x04\x00\x00\xfd\xe8')
assert(BGPHeader in m and BGPOpen in m)
################################# BGPPAOrigin #################################
+ BGPPAOrigin class tests
= BGPPAOrigin - Instantiation
raw(BGPPAOrigin()) == b'\x00'
= BGPPAOrigin - Instantiation with specific values
raw(BGPPAOrigin(origin = 1)) == b'\x01'
= BGPPAOrigin - Dissection
a = BGPPAOrigin(b'\x00')
a.origin == 0
################################ BGPPAASPath ##################################
+ BGPPAASPath class tests
= BGPPAASPath - Instantiation
raw(BGPPAASPath()) == b''
= BGPPAASPath - Instantiation with specific values (1)
raw(BGPPAASPath(segments = [BGPPAASPath.ASPathSegment(segment_type = 2, segment_value = [64496, 64497, 64498])])) == b'\x02\x03\xfb\xf0\xfb\xf1\xfb\xf2'
= BGPPAASPath - Instantiation with specific values (2)
raw(BGPPAASPath(segments = [BGPPAASPath.ASPathSegment(segment_type = 1, segment_value = [64496, 64497, 64498])])) == b'\x01\x03\xfb\xf0\xfb\xf1\xfb\xf2'
= BGPPAASPath - Instantiation with specific values (3)
raw(BGPPAASPath(segments = [BGPPAASPath.ASPathSegment(segment_type = 1, segment_value = [64496, 64497, 64498]), BGPPAASPath.ASPathSegment(segment_type = 2, segment_value = [64500, 64501, 64502, 64502, 64503])])) == b'\x01\x03\xfb\xf0\xfb\xf1\xfb\xf2\x02\x05\xfb\xf4\xfb\xf5\xfb\xf6\xfb\xf6\xfb\xf7'
= BGPPAASPath - Dissection (1)
a = BGPPAASPath(b'\x02\x03\xfb\xf0\xfb\xf1\xfb\xf2')
a.segments[0].segment_type == 2 and a.segments[0].segment_length == 3 and a.segments[0].segment_value == [64496, 64497, 64498]
= BGPPAASPath - Dissection (2)
a = BGPPAASPath(b'\x01\x03\xfb\xf0\xfb\xf1\xfb\xf2\x02\x05\xfb\xf4\xfb\xf5\xfb\xf6\xfb\xf6\xfb\xf7')
a.segments[0].segment_type == 1 and a.segments[0].segment_length == 3 and a.segments[0].segment_value == [64496, 64497, 64498] and a.segments[1].segment_type == 2 and a.segments[1].segment_length == 5 and a.segments[1].segment_value == [64500, 64501, 64502, 64502, 64503]
############################### BGPPANextHop ##################################
+ BGPPANextHop class tests
= BGPPANextHop - Instantiation
raw(BGPPANextHop()) == b'\x00\x00\x00\x00'
= BGPPANextHop - Instantiation with specific values
raw(BGPPANextHop(next_hop = "192.0.2.1")) == b'\xc0\x00\x02\x01'
= BGPPANextHop - Basic dissection
a = BGPPANextHop(b'\x00\x00\x00\x00')
a.next_hop == "0.0.0.0"
= BGPPANextHop - Dissection with specific values
a = BGPPANextHop(b'\xc0\x00\x02\x01')
a.next_hop == '192.0.2.1'
############################ BGPPAMultiExitDisc ##############################
+ BGPPAMultiExitDisc class tests
= BGPPAMultiExitDisc - Instantiation
raw(BGPPAMultiExitDisc()) == b'\x00\x00\x00\x00'
= BGPPAMultiExitDisc - Instantiation with specific values (1)
raw(BGPPAMultiExitDisc(med = 4)) == b'\x00\x00\x00\x04'
= BGPPAMultiExitDisc - Basic dissection
a = BGPPAMultiExitDisc(b'\x00\x00\x00\x00')
a.med == 0
############################## BGPPALocalPref ################################
+ BGPPALocalPref class tests
= BGPPALocalPref - Instantiation
raw(BGPPALocalPref()) == b'\x00\x00\x00\x00'
= BGPPALocalPref - Instantiation with specific values (1)
raw(BGPPALocalPref(local_pref = 110)) == b'\x00\x00\x00n'
= BGPPALocalPref - Basic dissection
a = BGPPALocalPref(b'\x00\x00\x00n')
a.local_pref == 110
############################## BGPPAAggregator ###############################
+ BGPPAAggregator class tests
= BGPPAAggregator - Instantiation
raw(BGPPAAggregator()) == b'\x00\x00\x00\x00\x00\x00'
= BGPPAAggregator - Instantiation with specific values (1)
raw(BGPPAAggregator(aggregator_asn = 64500, speaker_address = "192.0.2.1")) == b'\xfb\xf4\xc0\x00\x02\x01'
= BGPPAAggregator - Dissection
a = BGPPAAggregator(b'\xfb\xf4\xc0\x00\x02\x01')
a.aggregator_asn == 64500 and a.speaker_address == "192.0.2.1"
############################## BGPPACommunity ################################
+ BGPPACommunity class tests
= BGPPACommunity - Basic instantiation
raw(BGPPACommunity()) == b'\x00\x00\x00\x00'
= BGPPACommunity - Instantiation with specific value
raw(BGPPACommunity(community = 0xFFFFFF01)) == b'\xff\xff\xff\x01'
= BGPPACommunity - Dissection
a = BGPPACommunity(b'\xff\xff\xff\x01')
a.community == 0xFFFFFF01
############################ BGPPAOriginatorID ###############################
+ BGPPAOriginatorID class tests
= BGPPAOriginatorID - Basic instantiation
raw(BGPPAOriginatorID()) == b'\x00\x00\x00\x00'
= BGPPAOriginatorID - Instantiation with specific value
raw(BGPPAOriginatorID(originator_id = '192.0.2.1')) == b'\xc0\x00\x02\x01'
= BGPPAOriginatorID - Dissection
a = BGPPAOriginatorID(b'\xc0\x00\x02\x01')
a.originator_id == "192.0.2.1"
############################ BGPPAClusterList ################################
+ BGPPAClusterList class tests
= BGPPAClusterList - Basic instantiation
raw(BGPPAClusterList()) == b''
= BGPPAClusterList - Instantiation with specific values
raw(BGPPAClusterList(cluster_list = [150000, 165465465, 132132])) == b'\x00\x02I\xf0\t\xdc\xcdy\x00\x02\x04$'
= BGPPAClusterList - Dissection
a = BGPPAClusterList(b'\x00\x02I\xf0\t\xdc\xcdy\x00\x02\x04$')
a.cluster_list[0] == 150000 and a.cluster_list[1] == 165465465 and a.cluster_list[2] == 132132
########################### BGPPAMPReachNLRI ###############################
+ BGPPAMPReachNLRI class tests
= BGPPAMPReachNLRI - Instantiation
raw(BGPPAMPReachNLRI()) == b'\x00\x00\x00\x00\x00'
= BGPPAMPReachNLRI - Instantiation with specific values (1)
raw(BGPPAMPReachNLRI(afi=2, safi=1, nh_addr_len=16, nh_v6_addr = "2001:db8::2", nlri = [BGPNLRI_IPv6(prefix = "2001:db8:2::/64")])) == b'\x00\x02\x01\x10 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00@ \x01\r\xb8\x00\x02\x00\x00'
= BGPPAMPReachNLRI - Dissection (1)
a = BGPPAMPReachNLRI(b'\x00\x02\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xfe\x80\x00\x00\x00\x00\x00\x00\xc0\x02\x0b\xff\xfe~\x00\x00\x00@ \x01\r\xb8\x00\x02\x00\x02@ \x01\r\xb8\x00\x02\x00\x01@ \x01\r\xb8\x00\x02\x00\x00')
a.afi == 2 and a.safi == 1 and a.nh_addr_len == 32 and a.nh_v6_global == "2001:db8::2" and a.nh_v6_link_local == "fe80::c002:bff:fe7e:0" and a.reserved == 0 and a.nlri[0].prefix == "2001:db8:2:2::/64" and a.nlri[1].prefix == "2001:db8:2:1::/64" and a.nlri[2].prefix == "2001:db8:2::/64"
= BGPPAMPReachNLRI - Dissection (2)
a = BGPPAMPReachNLRI(b'\x00\x02\x01 \xfe\x80\x00\x00\x00\x00\x00\x00\xfa\xc0\x01\x00\x15\xde\x15\x81\xfe\x80\x00\x00\x00\x00\x00\x00\xfa\xc0\x01\x00\x15\xde\x15\x81\x00\x06\x04\x05\x08\x04\x10\x03`\x03\x80\x03\xa0\x03\xc0\x04\xe0\x05\xf0\x06\xf8\t\xfe\x00\x16 \x01<\x08-\x07.\x040\x10?\xfe\x10 \x02\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00`\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff@\x01\x00\x00\x00\x00\x00\x00\x00\x17 \x01\x00 \x01\x00\x000 \x01\x00\x02\x00\x00 \x01\r\xb8\x1c \x01\x00\x10\x07\xfc\n\xfe\x80\x08\xff\n\xfe\xc0\x03 \x03@\x08_`\x00d\xff\x9b\x00\x00\x00\x00\x00\x00\x00\x00\x08\x00\x08\x01\x07\x02')
a.afi == 2 and a.safi == 1 and a.nh_addr_len == 32 and a.nh_v6_global == "fe80::fac0:100:15de:1581" and a.nh_v6_link_local == "fe80::fac0:100:15de:1581" and a.reserved == 0 and a.nlri[0].prefix == "400::/6" and a.nlri[1].prefix == "800::/5" and raw(a.nlri[18]) == b'`\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff' and a.nlri[35].prefix == "200::/7"
############################# BGPPAMPUnreachNLRI #############################
+ BGPPAMPUnreachNLRI class tests
= BGPPAMPUnreachNLRI - Instantiation
raw(BGPPAMPUnreachNLRI()) == b'\x00\x00\x00'
= BGPPAMPUnreachNLRI - Instantiation with specific values (1)
raw(BGPPAMPUnreachNLRI(afi = 2, safi = 1)) == b'\x00\x02\x01'
= BGPPAMPUnreachNLRI - Instantiation with specific values (2)
raw(BGPPAMPUnreachNLRI(afi = 2, safi = 1, afi_safi_specific = BGPPAMPUnreachNLRI_IPv6(withdrawn_routes = [BGPNLRI_IPv6(prefix = "2001:db8:2::/64")]))) == b'\x00\x02\x01@ \x01\r\xb8\x00\x02\x00\x00'
= BGPPAMPUnreachNLRI - Dissection (1)
a = BGPPAMPUnreachNLRI(b'\x00\x02\x01')
a.afi == 2 and a.safi == 1
= BGPPAMPUnreachNLRI - Dissection (2)
a = BGPPAMPUnreachNLRI(b'\x00\x02\x01\x03`\x03\x80\x03\xa0\x03\xc0\x04\xe0\x05\xf0\x06\xf8\x10 \x02`\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff@\x01\x00\x00\x00\x00\x00\x00\x00\x17 \x01\x00 \x01\x00\x000 \x01\x00\x02\x00\x00 \x01\r\xb8\n\xfe\xc0\x07\xfc\n\xfe\x80\x1c \x01\x00\x10\x03 \x06\x04\x03@\x08_\x05\x08\x04\x10')
a.afi == 2 and a.safi == 1 and a.afi_safi_specific.withdrawn_routes[0].prefix == "6000::/3" and a.afi_safi_specific.withdrawn_routes[11].prefix == "2001::/32" and a.afi_safi_specific.withdrawn_routes[23].prefix == "1000::/4"
############################# BGPPAAS4Aggregator #############################
+ BGPPAAS4Aggregator class tests
= BGPPAAS4Aggregator - Instantiation
raw(BGPPAAS4Aggregator()) == b'\x00\x00\x00\x00\x00\x00\x00\x00'
= BGPPAAS4Aggregator - Instantiation with specific values
raw(BGPPAAS4Aggregator(aggregator_asn = 644566565, speaker_address = "192.0.2.1")) == b'&kN%\xc0\x00\x02\x01'
= BGPPAAS4Aggregator - Dissection
a = BGPPAAS4Aggregator(b'&kN%\xc0\x00\x02\x01')
a.aggregator_asn == 644566565 and a.speaker_address == "192.0.2.1"
################################ BGPPathAttr #################################
+ BGPPathAttr class tests
= BGPPathAttr - Instantiation
raw(BGPPathAttr()) == b'\x80\x00\x00'
= BGPPathAttr - Instantiation with specific values (1)
raw(BGPPathAttr(type_code = 1, attribute = BGPPAOrigin(origin = 0)))
= BGPPathAttr - Instantiation with specific values (2)
raw(BGPPathAttr(type_code = 2, attribute = BGPPAASPath(segments = [BGPPAASPath.ASPathSegment(segment_type = 2, segment_value = [64501, 64501, 64501])]))) == b'\x80\x02\x08\x02\x03\xfb\xf5\xfb\xf5\xfb\xf5'
= BGPPathAttr - Instantiation with specific values (3)
raw(BGPPathAttr(type_code = 14, attribute = BGPPAMPReachNLRI(afi = 2, safi = 1, nh_addr_len = 16, nh_v6_addr = "2001:db8::2", nlri = [BGPNLRI_IPv6(prefix = "2001:db8:2::/64")]))) == b'\x80\x0e\x1e\x00\x02\x01\x10 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00@ \x01\r\xb8\x00\x02\x00\x00'
= BGPPathAttr - Dissection (1)
a = BGPPathAttr(b'\x90\x0f\x00X\x00\x02\x01\x03`\x03\x80\x03\xa0\x03\xc0\x04\xe0\x05\xf0\x06\xf8\x10 \x02`\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff@\x01\x00\x00\x00\x00\x00\x00\x00\x17 \x01\x00 \x01\x00\x000 \x01\x00\x02\x00\x00 \x01\r\xb8\n\xfe\xc0\x07\xfc\n\xfe\x80\x1c \x01\x00\x10\x03 \x06\x04\x03@\x08_\x05\x08\x04\x10')
a.type_flags == 0x90 and a.type_code == 15 and a.attr_ext_len == 88 and a.attribute.afi == 2 and a.attribute.safi == 1 and a.attribute.afi_safi_specific.withdrawn_routes[0].prefix == "6000::/3" and a.attribute.afi_safi_specific.withdrawn_routes[1].prefix == "8000::/3" and a.attribute.afi_safi_specific.withdrawn_routes[2].prefix == "a000::/3" and a.attribute.afi_safi_specific.withdrawn_routes[3].prefix == "c000::/3" and a.attribute.afi_safi_specific.withdrawn_routes[4].prefix == "e000::/4" and a.attribute.afi_safi_specific.withdrawn_routes[5].prefix == "f000::/5" and a.attribute.afi_safi_specific.withdrawn_routes[23].prefix == "1000::/4"
= BGPPathAttr - advanced
b = BGPPathAttr(type_code=0x10, attribute=BGPPAExtComms(extended_communities=[
BGPPAExtCommunity(value=BGPPAExtCommTwoOctetASSpecific()),
BGPPAExtCommunity(value=BGPPAExtCommIPv4AddressSpecific()),
BGPPAExtCommunity(value=BGPPAExtCommFourOctetASSpecific()),
BGPPAExtCommunity(value=BGPPAExtCommOpaque()),
BGPPAExtCommunity(value=BGPPAExtCommTrafficMarking()),
BGPPAExtCommunity(value=BGPPAExtCommRedirectIPv4()),
BGPPAExtCommunity(value=BGPPAExtCommRedirectAS4Byte()),
]))
b = BGPPathAttr(raw(b))
cls_list = [x.value.__class__ for x in b.attribute.extended_communities]
assert cls_list == [BGPPAExtCommTwoOctetASSpecific, BGPPAExtCommIPv4AddressSpecific, BGPPAExtCommFourOctetASSpecific, BGPPAExtCommOpaque,
BGPPAExtCommTrafficMarking, BGPPAExtCommRedirectIPv4, BGPPAExtCommRedirectAS4Byte]
b.show()
################################# BGPUpdate ##################################
+ BGPUpdate class tests
= BGPUpdate - Instantiation
raw(BGPUpdate()) == b'\x00\x00\x00\x00'
= BGPUpdate - Dissection (1)
bgp_module_conf.use_2_bytes_asn = True
m = BGP(b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x000\x02\x00\x19\x18\xc0\xa8\x96\x18\x07\x07\x07\x18\xc63d\x18\xc0\xa8\x01\x19\x06\x06\x06\x00\x18\xc0\xa8\x1a\x00\x00')
assert(BGPHeader in m and BGPUpdate in m)
assert(m.withdrawn_routes_len == 25)
assert(m.withdrawn_routes[0].prefix == "192.168.150.0/24")
assert(m.withdrawn_routes[5].prefix == "192.168.26.0/24")
assert(m.path_attr_len == 0)
= BGPUpdate - Behave like a NEW speaker (RFC 6793) - Dissection (2)
bgp_module_conf.use_2_bytes_asn = False
m = BGP(b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00=\x02\x00\x00\x00"@\x01\x01\x00@\x02\x06\x02\x01\x00\x00\xfb\xfa@\x03\x04\xc0\xa8\x10\x06\x80\x04\x04\x00\x00\x00\x00\xc0\x08\x04\xff\xff\xff\x01\x18\xc0\xa8\x01')
assert(BGPHeader in m and BGPUpdate in m)
assert(m.path_attr[1].attribute.segments[0].segment_value == [64506])
assert(m.path_attr[4].attribute.community == 0xFFFFFF01)
assert(m.nlri[0].prefix == "192.168.1.0/24")
= BGPUpdate - Dissection (MP_REACH_NLRI)
m = BGP(b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\xd8\x02\x00\x00\x00\xc1@\x01\x01\x00@\x02\x06\x02\x01\x00\x00\xfb\xf6\x90\x0e\x00\xb0\x00\x02\x01 \xfe\x80\x00\x00\x00\x00\x00\x00\xfa\xc0\x01\x00\x15\xde\x15\x81\xfe\x80\x00\x00\x00\x00\x00\x00\xfa\xc0\x01\x00\x15\xde\x15\x81\x00\x06\x04\x05\x08\x04\x10\x03`\x03\x80\x03\xa0\x03\xc0\x04\xe0\x05\xf0\x06\xf8\t\xfe\x00\x16 \x01<\x08-\x07.\x040\x10?\xfe\x10 \x02\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00`\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff@\x01\x00\x00\x00\x00\x00\x00\x00\x17 \x01\x00 \x01\x00\x000 \x01\x00\x02\x00\x00 \x01\r\xb8\x1c \x01\x00\x10\x07\xfc\n\xfe\x80\x08\xff\n\xfe\xc0\x03 \x03@\x08_`\x00d\xff\x9b\x00\x00\x00\x00\x00\x00\x00\x00\x08\x00\x08\x01\x07\x02')
assert(BGPHeader in m and BGPUpdate in m)
assert(m.path_attr[2].attribute.afi == 2)
assert(m.path_attr[2].attribute.safi == 1)
assert(m.path_attr[2].attribute.nh_addr_len == 32)
assert(m.path_attr[2].attribute.nh_v6_global == "fe80::fac0:100:15de:1581")
assert(m.path_attr[2].attribute.nh_v6_link_local == "fe80::fac0:100:15de:1581")
assert(m.path_attr[2].attribute.nlri[0].prefix == "400::/6")
assert(m.nlri == [])
= BGPUpdate - Dissection (MP_UNREACH_NLRI)
m = BGP(b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00s\x02\x00\x00\x00\\\x90\x0f\x00X\x00\x02\x01\x03`\x03\x80\x03\xa0\x03\xc0\x04\xe0\x05\xf0\x06\xf8\x10 \x02`\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff@\x01\x00\x00\x00\x00\x00\x00\x00\x17 \x01\x00 \x01\x00\x000 \x01\x00\x02\x00\x00 \x01\r\xb8\n\xfe\xc0\x07\xfc\n\xfe\x80\x1c \x01\x00\x10\x03 \x06\x04\x03@\x08_\x05\x08\x04\x10')
assert(BGPHeader in m and BGPUpdate in m)
assert(m.path_attr[0].attribute.afi == 2)
assert(m.path_attr[0].attribute.safi == 1)
assert(m.path_attr[0].attribute.afi_safi_specific.withdrawn_routes[0].prefix == "6000::/3")
assert(m.nlri == [])
= BGPUpdate - with BGPHeader
p = BGP(raw(BGPHeader()/BGPUpdate()))
assert(BGPHeader in p and BGPUpdate in p)
########## BGPNotification Class ###################################
+ BGPNotification class tests
= BGPNotification - Instantiation
raw(BGPNotification()) == b'\x00\x00'
= BGPNotification - Dissection (Administratively Reset)
m = BGP(b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x15\x03\x06\x04')
m.type == BGP.NOTIFICATION_TYPE and m.error_code == 6 and m.error_subcode == 4
= BGPNotification - Dissection (Bad Peer AS)
m = BGP(b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x17\x03\x02\x02\x00\x00')
m.type == BGP.NOTIFICATION_TYPE and m.error_code == 2 and m.error_subcode == 2
= BGPNotification - Dissection (Attribute Flags Error)
m = BGP(b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x19\x03\x03\x04\x80\x01\x01\x00')
m.type == BGP.NOTIFICATION_TYPE and m.error_code == 3 and m.error_subcode == 4
########## BGPRouteRefresh Class ###################################
+ BGPRouteRefresh class tests
= BGPRouteRefresh - Instantiation
raw(BGPRouteRefresh()) == b'\x00\x01\x00\x01'
= BGPRouteRefresh - Instantiation with specific values
raw(BGPRouteRefresh(afi = 1, safi = 1)) == b'\x00\x01\x00\x01'
= BGPRouteRefresh - Dissection (1)
m = BGP(b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x17\x05\x00\x02\x00\x01')
m.type == BGP.ROUTEREFRESH_TYPE and m.len == 23 and m.afi == 2 and m.subtype == 0 and m.safi == 1
= BGPRouteRefresh - Dissection (2) - With ORFs
m = BGP(b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00.\x05\x00\x01\x00\x01\x01\x80\x00\x13 \x00\x00\x00\x05\x18\x18\x15\x01\x01\x00\x00\x00\x00\x00\n\x00 \x00')
assert(m.type == BGP.ROUTEREFRESH_TYPE)
assert(m.len == 46)
assert(m.afi == 1)
assert(m.subtype == 0)
assert(m.safi == 1)
assert(m.orf_data[0].when_to_refresh == 1)
assert(m.orf_data[0].orf_type == 128)
assert(m.orf_data[0].orf_len == 19)
assert(len(m.orf_data[0].entries) == 2)
assert(m.orf_data[0].entries[0].action == 0)
assert(m.orf_data[0].entries[0].match == 1)
assert(m.orf_data[0].entries[0].prefix.prefix == "1.1.0.0/21")
assert(m.orf_data[0].entries[1].action == 0)
assert(m.orf_data[0].entries[1].match == 0)
assert(m.orf_data[0].entries[1].prefix.prefix == "0.0.0.0/0")
########## BGPCapGeneric fuzz() ###################################
+ BGPCapGeneric fuzz()
= BGPCapGeneric fuzz()
for i in range(10):
assert isinstance(raw(fuzz(BGPCapGeneric())), bytes)

69
libs/scapy/contrib/bier.py Executable file
View file

@ -0,0 +1,69 @@
# This file is part of Scapy
# Scapy is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# any later version.
#
# Scapy is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Scapy. If not, see <http://www.gnu.org/licenses/>.
# scapy.contrib.description = Bit Index Explicit Replication (BIER)
# scapy.contrib.status = loads
from scapy.packet import Packet, bind_layers
from scapy.fields import BitEnumField, BitField, BitFieldLenField, ByteField, \
ShortField, StrLenField
from scapy.layers.inet import IP, UDP
from scapy.layers.inet6 import IPv6
class BIERLength:
BIER_LEN_64 = 0
BIER_LEN_128 = 1
BIER_LEN_256 = 2
BIER_LEN_512 = 3
BIER_LEN_1024 = 4
BIERnhcls = {1: "MPLS",
2: "MPLS",
4: "IPv4",
5: "IPv6"}
class BIFT(Packet):
name = "BIFT"
fields_desc = [BitField("bsl", BIERLength.BIER_LEN_256, 4),
BitField("sd", 0, 8),
BitField("set", 0, 8),
BitField("cos", 0, 3),
BitField("s", 1, 1),
ByteField("ttl", 0)]
class BIER(Packet):
name = "BIER"
fields_desc = [BitField("id", 5, 4),
BitField("version", 0, 4),
BitFieldLenField("length", BIERLength.BIER_LEN_256, 4,
length_of=lambda x:(x.BitString >> 8)),
BitField("entropy", 0, 20),
BitField("OAM", 0, 2),
BitField("RSV", 0, 2),
BitField("DSCP", 0, 6),
BitEnumField("Proto", 2, 6, BIERnhcls),
ShortField("BFRID", 0),
StrLenField("BitString",
"",
length_from=lambda x:(8 << x.length))]
bind_layers(BIER, IP, Proto=4)
bind_layers(BIER, IPv6, Proto=5)
bind_layers(UDP, BIFT, dport=8138)
bind_layers(BIFT, BIER)

22
libs/scapy/contrib/bier.uts Executable file
View file

@ -0,0 +1,22 @@
# BIER unit tests
#
# Type the following command to launch start the tests:
# $ test/run_tests -P "load_contrib('bier')" -P "load_contrib('mpls')" -t scapy/contrib/bier.uts
+ BIER tests
= BIER - build/dissection
from scapy.contrib.mpls import MPLS
p1 = MPLS()/BIER(length=BIERLength.BIER_LEN_256)/IP()/UDP()
assert(p1[MPLS].s == 1)
p2 = BIFT()/BIER(length=BIERLength.BIER_LEN_64)/IP()/UDP()
assert(p2[BIFT].s == 1)
p1[MPLS]
p1[BIER]
p1[IP]
p2[BIFT]
p2[BIER]
p2[IP]

Some files were not shown because too many files have changed in this diff Show more