diff --git a/scapy/contrib/automotive/gm/__init__.py b/scapy/contrib/automotive/gm/__init__.py new file mode 100644 index 000000000..f43f0b7c1 --- /dev/null +++ b/scapy/contrib/automotive/gm/__init__.py @@ -0,0 +1,9 @@ +# This file is part of Scapy +# See http://www.secdev.org/projects/scapy for more informations +# Copyright (C) Nils Weiss +# This program is published under a GPLv2 license + +""" +Package of contrib automotive gm specific modules +that have to be loaded explicitly. +""" diff --git a/scapy/contrib/automotive/gm/gmlan.py b/scapy/contrib/automotive/gm/gmlan.py new file mode 100644 index 000000000..794f6a03d --- /dev/null +++ b/scapy/contrib/automotive/gm/gmlan.py @@ -0,0 +1,627 @@ +#! /usr/bin/env python + +# This file is part of Scapy +# See http://www.secdev.org/projects/scapy for more informations +# Copyright (C) Nils Weiss +# Copyright (C) Enrico Pozzobon +# This program is published under a GPLv2 license + + +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 +from scapy.config import conf +from scapy.error import warning + + +""" +GMLAN +""" + +conf.contribs['GMLAN'] = {'GMLAN_ECU_AddressingScheme': None} + + +class GMLAN(Packet): + @staticmethod + def determine_len(x): + if conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] is None: + warning("Define conf.GMLAN_ECU_AddressingScheme! " + "Assign either 2,3 or 4") + if conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] \ + not in [2, 3, 4]: + warning("Define conf.GMLAN_ECU_AddressingScheme! " + "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): + """DEV: true if self is an answer from other""" + if other.__class__ == self.__class__: + return (other.service + 0x40) == self.service or \ + (self.service == 0x7f and + (self.requestServiceId == other.service)) + return 0 + + def hashret(self): + if self.service == 0x7f: + return struct.pack('B', self.requestServiceId) + return struct.pack('B', self.service & ~0x40) + + +# ########################IDO################################### +class GMLAN_IDO(Packet): + subfunctions = { + 0x02: 'disableAllDTCs', + 0x03: 'enableDTCsDuringDevCntrl', + 0x04: 'wakeUpLinks'} + name = 'InitiateDiagnosticOperation' + fields_desc = [ + ByteEnumField('subfunction', 0, subfunctions) + ] + + +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) + ] + + +bind_layers(GMLAN, GMLAN_RFRD, service=0x12) + + +class GMLAN_RFRDPR(Packet): + name = 'ReadFailureRecordDataPositiveResponse' + fields_desc = [ + ByteEnumField('subfunction', 0, GMLAN_RFRD.subfunctions) + ] + + +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) + ] + + +bind_layers(GMLAN, GMLAN_RDBI, service=0x1A) + + +class GMLAN_RDBIPR(Packet): + name = 'ReadDataByIdentifierPositiveResponse' + fields_desc = [ + XByteEnumField('dataIdentifier', 0, GMLAN_RDBI.dataIdentifiers), + ] + + +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)) + ] + + +bind_layers(GMLAN, GMLAN_RDBPI, service=0x22) + + +class GMLAN_RDBPIPR(Packet): + name = 'ReadDataByParameterIdentifierPositiveResponse' + fields_desc = [ + XShortEnumField('parameterIdentifier', 0, GMLAN_RDBPI.dataIdentifiers), + ] + + +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(StrField('request_DPIDs', b''), + lambda pkt: pkt.subfunction > 0x0) + ] + + +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), + ] + + +bind_layers(GMLAN, GMLAN_RMBA, service=0x23) + + +class GMLAN_RMBAPR(Packet): + name = 'ReadMemoryByAddressPositiveResponse' + fields_desc = [ + StrField('dataRecord', None, fmt="B") + ] + + +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) + ] + + +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), + ] + + +bind_layers(GMLAN, GMLAN_SAPR, service=0x67) + + +# ########################DDM################################### +class GMLAN_DDM(Packet): + name = 'DynamicallyDefineMessage' + fields_desc = [ + XByteField('DPIDIdentifier', 0), + StrField('PIDData', b'\x00\x00') + ] + + +bind_layers(GMLAN, GMLAN_DDM, service=0x2C) + + +class GMLAN_DDMPR(Packet): + name = 'DynamicallyDefineMessagePositiveResponse' + fields_desc = [ + XByteField('DPIDIdentifier', 0) + ] + + +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), + ] + + +bind_layers(GMLAN, GMLAN_DPBA, service=0x2D) + + +class GMLAN_DPBAPR(Packet): + name = 'DefinePIDByAddressPositiveResponse' + fields_desc = [ + XShortField('parameterIdentifier', 0), + ] + + +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)), + ] + + +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) + ] + + +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') + ] + + +bind_layers(GMLAN, GMLAN_WDBI, service=0x3B) + + +class GMLAN_WDBIPR(Packet): + name = 'WriteDataByIdentifierPositiveResponse' + fields_desc = [ + XByteEnumField('dataIdentifier', 0, GMLAN_RDBI.dataIdentifiers) + ] + + +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), + ] + + +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), + ] + + +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) + ] + + +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) + ] + + +bind_layers(GMLAN, GMLAN_NR, service=0x7f) diff --git a/scapy/contrib/automotive/gm/gmlan.uts b/scapy/contrib/automotive/gm/gmlan.uts new file mode 100644 index 000000000..510ff95e7 --- /dev/null +++ b/scapy/contrib/automotive/gm/gmlan.uts @@ -0,0 +1,367 @@ +# 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 +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 + += Craft Packet with ECU AddressingScheme2 +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 + += Craft Packet with ECU AddressingScheme3 +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 + += Craft Packet with ECU AddressingScheme4 +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 == b'deadbeef' + += Craft Packet for GMLAN_RDBPKTI2 +x = GMLAN(b'\xAA\x00') +x.service == 0xAA +x.subfunction == 0 + += 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() + diff --git a/setup.py b/setup.py index 934e59343..da48f727e 100755 --- a/setup.py +++ b/setup.py @@ -64,6 +64,7 @@ setup( 'scapy/contrib', 'scapy/contrib/automotive', 'scapy/contrib/automotive/bmw', + 'scapy/contrib/automotive/gm', 'scapy/layers', 'scapy/layers/tls', 'scapy/layers/tls/crypto',