public static bool SecurityAccess(KW2000Dialog kwp2000, byte accessMode) { const byte identificationOption = 0x94; var responseMsg = kwp2000.SendReceive(Service.readEcuIdentification, new byte[] { identificationOption }); if (responseMsg.Body[0] != identificationOption) { throw new InvalidOperationException($"Received unexpected identificationOption: {responseMsg.Body[0]:X2}"); } Logger.WriteLine(Utils.DumpAscii(responseMsg.Body.Skip(1))); const int maxTries = 16; for (var i = 0; i < maxTries; i++) { responseMsg = kwp2000.SendReceive(Service.securityAccess, new byte[] { accessMode }); if (responseMsg.Body[0] != accessMode) { throw new InvalidOperationException($"Received unexpected accessMode: {responseMsg.Body[0]:X2}"); } var seedBytes = responseMsg.Body.Skip(1).ToArray(); var seed = (uint)( (seedBytes[0] << 24) | (seedBytes[1] << 16) | (seedBytes[2] << 8) | seedBytes[3]); var key = CalcRB8Key(seed); try { responseMsg = kwp2000.SendReceive(Service.securityAccess, new[] { (byte)(accessMode + 1), (byte)((key >> 24) & 0xFF), (byte)((key >> 16) & 0xFF), (byte)((key >> 8) & 0xFF), (byte)(key & 0xFF) }); Logger.WriteLine("Success!!!"); return(true); } catch (NegativeResponseException) { if (i < (maxTries - 1)) { Logger.WriteLine("Trying again."); } } } return(false); }
public void DumpEeprom(string filename) { Logger.WriteLine("Sending wakeup message"); var kwpVersion = _kwpCommon.FastInit((byte)_controllerAddress); var kwp2000 = new KW2000Dialog(_kwpCommon, (byte)_controllerAddress); Kwp2000Message resp; // Need to decrease these timing parameters to get the ECU to wake up. kwp2000.P3 = 0; kwp2000.P4 = 0; kwp2000.SendMessage(DiagnosticService.startCommunication, Array.Empty <byte>()); kwp2000.P3 = 6; try { resp = kwp2000.SendReceive(DiagnosticService.testerPresent, Array.Empty <byte>()); } catch (InvalidOperationException) { // Ignore "Unexpected DestAddress: 00" } kwp2000.P3 = 55; resp = kwp2000.SendReceive(DiagnosticService.startDiagnosticSession, new byte[] { 0x85 }); const byte accMod = 0x41; resp = kwp2000.SendReceive(DiagnosticService.securityAccess, new byte[] { accMod }); // ECU normally doesn't require seed/key authentication the first time it wakes up in // KWP2000 mode so sending an empty key is sufficient. var buf = new List <byte> { accMod + 1 }; if (!Enumerable.SequenceEqual(resp.Body, new byte[] { accMod, 0x00, 0x00 })) { // Normally we'll only get here if we wake up the ECU and it's already in KWP2000 mode, // which can happen if a previous download attempt did not complete. In that case we // need to calculate and send back a real key. var seedBuf = resp.Body.Skip(1).Take(4).ToArray(); var keyBuf = LVL41Auth(0x508DA647, 0x3800000, seedBuf); buf.AddRange(keyBuf); } resp = kwp2000.SendReceive(DiagnosticService.securityAccess, buf.ToArray()); var loader = Edc15VM.GetLoader(); var len = loader.Length; // Ask the ECU to accept our loader and store it in RAM resp = kwp2000.SendReceive(DiagnosticService.requestDownload, new byte[] { 0x40, 0xE0, 0x00, // Load address 0x40E000 0x00, // Not compressed, not encrypted (byte)(len >> 16), (byte)(len >> 8), (byte)(len & 0xFF) // Length }, excludeAddresses: true); // Break the loader into blocks and send each one var maxBlockLen = resp.Body[0]; var s = new MemoryStream(loader); while (true) { Thread.Sleep(5); var blockBytes = new byte[maxBlockLen]; var readCount = s.Read(blockBytes, 0, maxBlockLen - 1); if (readCount == 0) { break; } resp = kwp2000.SendReceive( DiagnosticService.transferData, blockBytes.Take(readCount).ToArray(), excludeAddresses: true); } // Ask the ECU to execute our loader kwp2000.SendMessage( DiagnosticService.startRoutineByLocalIdentifier, new byte[] { 0x02 }, excludeAddresses: true); resp = kwp2000.ReceiveMessage(); // Custom loader command to send all 512 bytes of the EEPROM kwp2000.SendMessage( (DiagnosticService)0xA6, Array.Empty <byte>(), excludeAddresses: true); resp = kwp2000.ReceiveMessage(); if (!resp.IsPositiveResponse(DiagnosticService.transferData)) { throw new InvalidOperationException($"Dump EEPROM failed."); } var eeprom = new List <byte>(); byte b; for (int i = 0; i < 512; i++) { b = _kwpCommon.Interface.ReadByte(); eeprom.Add(b); } var dumpFileName = filename ?? $"EDC15_EEPROM.bin"; File.WriteAllBytes(dumpFileName, eeprom.ToArray()); Logger.WriteLine($"Saved EEPROM to {dumpFileName}"); resp = kwp2000.ReceiveMessage(); // Custom loader command to reboot the ECU to return it to normal operation. kwp2000.SendMessage( (DiagnosticService)0xA2, Array.Empty <byte>(), excludeAddresses: true); resp = kwp2000.ReceiveMessage(); b = _kwpCommon.Interface.ReadByte(); if (b == 0x55) { Logger.WriteLine($"Reboot successful!"); } }