Initial commit

This commit is contained in:
JinGen Lim 2020-11-27 19:36:04 +08:00
parent 065c8b40eb
commit 448c9c809a
41 changed files with 2606 additions and 0 deletions

98
README.md Normal file
View File

@ -0,0 +1,98 @@
# UnlockECU
![Header Image](https://raw.githubusercontent.com/jglim/UnlockECU/main/docs/resources/header.png)
Free, open-source ECU seed-key unlocking tool.
## Getting started
Download and unarchive the application from the [Releases](https://github.com/jglim/UnlockECU/releases/) page, then run the main application `VisualUnlockECU.exe`.
Ensure that you have *.NET Desktop Runtime 5.0.0*. , available from [here](https://dotnet.microsoft.com/download/dotnet/5.0).
## License
MIT
Icon from [http://www.famfamfam.com/lab/icons/silk/](http://www.famfamfam.com/lab/icons/silk/)
Excluding the icon, this application **does not include or require copyrighted or proprietary files**. Security functions and definitions have been reverse-engineered and reimplemented.
*When interacting with this repository (PR, issues, comments), please avoid including copyrighted/proprietary files, as they will be removed without notice.*
## Features
- There is no need for additional files such as security DLLs. The application supports a set of security providers out of the box, and definitions are stored in `db.json`.
- Security functions are completely reverse engineered and re-implemented in C#.
- The project is unencumbered by proprietary binary blobs, and can be shared freely without legal issues.
## Demo
![https://raw.githubusercontent.com/jglim/UnlockECU/main/docs/resources/demo.mp4](https://raw.githubusercontent.com/jglim/UnlockECU/main/docs/resources/demo-thumb.png)
## Adding definitions
Definitions specify a seed-key function for a specific ECU and security level. The input seed's size, output key's length as well as the security provider must be specified. Some security providers require specific parameters to operate.
Here is an example of a definition:
```
{
"EcuName": "ME97",
"AccessLevel": 5,
"SeedLength": 2,
"KeyLength": 2,
"Provider": "PowertrainBoschContiSecurityAlgo2",
"Origin": "ME97_ME97_13_10_01",
"Parameters": [
{
"Key": "Table",
"Value": "37C1A8179AE3745B",
"DataType": "ByteArray"
},
{
"Key": "uwMasc",
"Value": "4108",
"DataType": "ByteArray"
}
]
}
```
Currently, these security providers are available:
- DaimlerStandardSecurityAlgo
- DaimlerStandardSecurityAlgoMod
- DaimlerStandardSecurityAlgoRefG
- DRVU_PROF
- EDIFF290
- EsLibEd25519
- ESPSecurityAlgoLevel1
- MarquardtSecurityAlgo
- OCM172
- PowertrainBoschContiSecurityAlgo1
- PowertrainBoschContiSecurityAlgo2
- PowertrainDelphiSecurityAlgo
- PowertrainSecurityAlgo
- PowertrainSecurityAlgo2
- PowertrainSecurityAlgoNFZ
- RBTM
- RDU222
- RVC222_MPC222_FCW246_LRR3
- SWSP177
The definitions file `db.json` should be found alongside the application's main binary.
## Notes
- If your diagnostics file has unlocking capabilities, usually your diagnostics client can already perform the unlocking without further aid. Check your client's available functions for phrases such as `Entriegeln` , `Zugriffberechtigung` , and `Unlock`.
- Generally, this application operates like most DLL-based seed-key generators. If you already have a DLL-based tool, this application does not offer much more (only includes a few modern targets such as `HU7`).
- Definitions are reverse-engineered from DLLs and SMR-D files. If the definition does not innately exist in those files, they will not be available here (e.g. high-level instrument cluster definitions).
- There are ECUs that share the same seed-key function. For example, `CRD3` and `CRD3S2` appear to share the same function as `CRD3NFZ`.
- The core of this project is a "portable" .NET 5 class library which can be reused on other platforms.
- As the security providers are now written in a high-level language, they can be better studied. For example, `DaimlerStandardSecurityAlgo` performs a XOR with its private key as a final step, which allows the private key to be recovered from a known seed and key.
- `DaimlerStandardSecurityAlgo` is usually used for firmware flashing, and might not unlock other capabilities such as variant-coding.
## Contributing
Contributions in adding security providers and definitions are welcome.

37
UnlockECU/UnlockECU.sln Normal file
View File

@ -0,0 +1,37 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30711.63
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnlockECU", "UnlockECU\UnlockECU.csproj", "{9DBD6CB7-68B3-4801-95B7-D760C49E2A83}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VisualUnlockECU", "VisualUnlockECU\VisualUnlockECU.csproj", "{FA2EF721-9B46-4EB9-8444-0ABAF6F5185A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnlockECUTests", "UnlockECUTests\UnlockECUTests.csproj", "{6252DB08-EFD2-4889-B97F-090726D9DC9C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{9DBD6CB7-68B3-4801-95B7-D760C49E2A83}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9DBD6CB7-68B3-4801-95B7-D760C49E2A83}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9DBD6CB7-68B3-4801-95B7-D760C49E2A83}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9DBD6CB7-68B3-4801-95B7-D760C49E2A83}.Release|Any CPU.Build.0 = Release|Any CPU
{FA2EF721-9B46-4EB9-8444-0ABAF6F5185A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FA2EF721-9B46-4EB9-8444-0ABAF6F5185A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FA2EF721-9B46-4EB9-8444-0ABAF6F5185A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FA2EF721-9B46-4EB9-8444-0ABAF6F5185A}.Release|Any CPU.Build.0 = Release|Any CPU
{6252DB08-EFD2-4889-B97F-090726D9DC9C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6252DB08-EFD2-4889-B97F-090726D9DC9C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6252DB08-EFD2-4889-B97F-090726D9DC9C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6252DB08-EFD2-4889-B97F-090726D9DC9C}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {CC1E2894-FC7F-474F-8A14-00529B7EF07F}
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,87 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace UnlockECU
{
/// <summary>
/// Utilities for bit and byte operations.
/// (Frequently copied-and-pasted across my projects)
/// </summary>
public class BitUtility
{
/// <summary>
/// Sets all values in an array of bytes to a specific value
/// </summary>
/// <param name="value">Value to set byte array to</param>
/// <param name="buf">Target byte array buffer</param>
public static void Memset(byte value, byte[] buf)
{
for (int i = 0; i < buf.Length; i++)
{
buf[i] = value;
}
}
// Internally used by BytesFromHex
private static byte[] StringToByteArrayFastest(string hex)
{
// see https://stackoverflow.com/questions/321370/how-can-i-convert-a-hex-string-to-a-byte-array
if (hex.Length % 2 == 1)
{
throw new Exception("The binary key cannot have an odd number of digits");
}
byte[] arr = new byte[hex.Length >> 1];
for (int i = 0; i < hex.Length >> 1; ++i)
{
arr[i] = (byte)((GetHexValue(hex[i << 1]) << 4) + (GetHexValue(hex[(i << 1) + 1])));
}
return arr;
}
// Internally used by StringToByteArrayFastest
private static int GetHexValue(char hex)
{
int val = (int)hex;
return val - (val < 58 ? 48 : 55);
}
/// <summary>
/// Converts an array of bytes into its hex-string equivalent
/// </summary>
/// <param name="inBytes">Input byte array</param>
/// <param name="spacedOut">Option to add spaces between individual bytes</param>
/// <returns>Hex-string based on the input byte array</returns>
public static string BytesToHex(byte[] inBytes, bool spacedOut = false)
{
return BitConverter.ToString(inBytes).Replace("-", spacedOut ? " " : "");
}
/// <summary>
/// Converts an array of bytes into a printable hex-string
/// </summary>
/// <param name="hexString">Input hex-string to convert into a byte array</param>
/// <returns>Byte array based on the input hex-string</returns>
public static byte[] BytesFromHex(string hexString)
{
return StringToByteArrayFastest(hexString.Replace(" ", ""));
}
/// <summary>
/// Resize a smaller array of bytes to a larger array. The padding bytes will be 0.
/// </summary>
/// <param name="inData">Input byte array</param>
/// <param name="finalSize">New size for the input array</param>
/// <returns>Resized byte array</returns>
public static byte[] PadBytes(byte[] inData, int finalSize)
{
if (inData.Length > finalSize)
{
return inData;
}
byte[] result = new byte[finalSize];
Buffer.BlockCopy(inData, 0, result, 0, inData.Length);
return result;
}
}
}

View File

@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace UnlockECU
{
public class Definition
{
public string EcuName { get; set; }
public int AccessLevel { get; set; }
public int SeedLength { get; set; }
public int KeyLength { get; set; }
public string Provider { get; set; }
public string Origin { get; set; }
public List<Parameter> Parameters { get; set; }
[System.Text.Json.Serialization.JsonIgnore]
public string ParamParent;
public override string ToString()
{
StringBuilder sb = new StringBuilder($"Name: {EcuName} ({Origin}), Level: {AccessLevel}, Seed Length: {SeedLength}, Key Length: {KeyLength}, Provider: {Provider}");
return sb.ToString();
/*
// uncomment and remove the return above to print verbose parameter data
foreach (Parameter row in Parameters)
{
sb.AppendLine();
sb.Append($"Parameter[{row.Key}] ({row.DataType}) : {row.Value}");
}
return sb.ToString();
*/
}
}
}

View File

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace UnlockECU
{
public class Parameter
{
public string Key { get; set; }
public string Value { get; set; }
public string DataType { get; set; }
[System.Text.Json.Serialization.JsonIgnore]
public int AccessLevel = -1;
}
}

View File

@ -0,0 +1,70 @@
using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
namespace UnlockECU
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("UnlockECU (Running as console application)");
string definitionJson = File.ReadAllText("db.json");
List<Definition> definitions = System.Text.Json.JsonSerializer.Deserialize<List<Definition>>(definitionJson);
List<SecurityProvider> providers = SecurityProvider.GetSecurityProviders();
ReverseKey(BitUtility.BytesFromHex("BA00D268 972D452D"), BitUtility.BytesFromHex("BE515D46")); // reverse key 0x3F9C71A5 (CRD3S2SEC9A)
Console.ReadKey();
}
static void ReverseKey(byte[] inSeed, byte[] outKeyBytes)
{
long kA = 1103515245L;
long kC = 12345L;
long seedA = BytesToInt(inSeed, Endian.Big, 0);
long seedB = BytesToInt(inSeed, Endian.Big, 4);
long outKey = BytesToInt(outKeyBytes, Endian.Big);
long intermediate1 = kA * seedA + kC;
long intermediate2 = kA * seedB + kC;
long xorA = intermediate1 ^ intermediate2;
long reverseCryptoKey = (xorA ^ outKey) & 0xFFFFFFFF; // reverse key 0x3F9C71A5 (CRD3S2SEC9A)
Console.WriteLine($"Reversed DSSA key: {reverseCryptoKey:X}");
}
public enum Endian
{
Big,
Little,
}
public static uint BytesToInt(byte[] inBytes, Endian endian, int offset = 0)
{
uint result = 0;
if (endian == Endian.Big)
{
result |= (uint)inBytes[offset++] << 24;
result |= (uint)inBytes[offset++] << 16;
result |= (uint)inBytes[offset++] << 8;
result |= (uint)inBytes[offset++] << 0;
}
else
{
result |= (uint)inBytes[offset++] << 0;
result |= (uint)inBytes[offset++] << 8;
result |= (uint)inBytes[offset++] << 16;
result |= (uint)inBytes[offset++] << 24;
}
return result;
}
}
}

View File

@ -0,0 +1,41 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace UnlockECU
{
/// <summary>
/// Simpler version of DaimlerStandardSecurityAlgo with custom kA, kC, and no blockB
/// </summary>
class DRVU_PROF : SecurityProvider
{
public override bool GenerateKey(byte[] inSeed, byte[] outKey, int accessLevel, List<Parameter> parameters)
{
byte[] cryptoKeyBytes = GetParameterBytearray(parameters, "KeyConst");
uint cryptoKey = BytesToInt(cryptoKeyBytes, Endian.Big);
long kA = 258028488L;
long kC = 1583629211L;
if ((inSeed.Length != 4) || (outKey.Length != 4))
{
return false;
}
long seedA = BytesToInt(inSeed, Endian.Big, 0);
long intermediate1 = kA * seedA + kC;
long seedKey = intermediate1 % cryptoKey;
IntToBytes((uint)seedKey, outKey, Endian.Big);
return true;
}
public override string GetProviderName()
{
return "DRVU_PROF";
}
}
}

View File

@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace UnlockECU
{
/// <summary>
/// Basic implementation DaimlerStandardSecurityAlgo, with hardcoded kA, kC constants for the intermediate transformation.
/// </summary>
class DaimlerStandardSecurityAlgo : SecurityProvider
{
public override bool GenerateKey(byte[] inSeed, byte[] outKey, int accessLevel, List<Parameter> parameters)
{
byte[] cryptoKeyBytes = GetParameterBytearray(parameters, "K");
uint cryptoKey = BytesToInt(cryptoKeyBytes, Endian.Big);
long kA = 1103515245L;
long kC = 12345L;
if ((inSeed.Length != 8) || (outKey.Length != 4))
{
return false;
}
long seedA = BytesToInt(inSeed, Endian.Big, 0);
long seedB = BytesToInt(inSeed, Endian.Big, 4);
long intermediate1 = kA * seedA + kC;
long intermediate2 = kA * seedB + kC;
long seedKey = intermediate1 ^ intermediate2 ^ cryptoKey;
IntToBytes((uint)seedKey, outKey, Endian.Big);
return true;
}
public override string GetProviderName()
{
return "DaimlerStandardSecurityAlgo";
}
}
}

View File

@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace UnlockECU
{
/// <summary>
/// Derivative of DaimlerStandardSecurityAlgo, with defined kA, kC constants for the intermediate transformation.
/// </summary>
class DaimlerStandardSecurityAlgoMod : SecurityProvider
{
public override bool GenerateKey(byte[] inSeed, byte[] outKey, int accessLevel, List<Parameter> parameters)
{
byte[] cryptoKeyBytes = GetParameterBytearray(parameters, "K");
uint cryptoKey = BytesToInt(cryptoKeyBytes, Endian.Big);
long kA = GetParameterLong(parameters, "kA");
long kC = GetParameterLong(parameters, "kC");
if ((inSeed.Length != 8) || (outKey.Length != 4))
{
return false;
}
long seedA = BytesToInt(inSeed, Endian.Big, 0);
long seedB = BytesToInt(inSeed, Endian.Big, 4);
long intermediate1 = kA * seedA + kC;
long intermediate2 = kA * seedB + kC;
long seedKey = intermediate1 ^ intermediate2 ^ cryptoKey;
IntToBytes((uint)seedKey, outKey, Endian.Big);
return true;
}
public override string GetProviderName()
{
return "DaimlerStandardSecurityAlgoMod";
}
}
}

View File

@ -0,0 +1,45 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace UnlockECU
{
/// <summary>
/// Derivative of DaimlerStandardSecurityAlgoMod, with 4 custom values (kA0, kA1, kC0, kC1) for the intermediate transformation.
/// </summary>
class DaimlerStandardSecurityAlgoRefG : SecurityProvider
{
public override bool GenerateKey(byte[] inSeed, byte[] outKey, int accessLevel, List<Parameter> parameters)
{
byte[] cryptoKeyBytes = GetParameterBytearray(parameters, "K_refG");
uint cryptoKey = BytesToInt(cryptoKeyBytes, Endian.Big);
long kA_0 = 3040238857L;
long kA_1 = 4126034881L;
long kC_0 = 2094854071L;
long kC_1 = 3555108353L;
if ((inSeed.Length != 8) || (outKey.Length != 4))
{
return false;
}
long seedA = BytesToInt(inSeed, Endian.Big, 0);
long seedB = BytesToInt(inSeed, Endian.Big, 4);
long intermediate1 = kA_0 * seedA + kC_0;
long intermediate2 = kA_1 * seedB + kC_1;
long seedKey = intermediate1 ^ intermediate2 ^ cryptoKey;
IntToBytes((uint)seedKey, outKey, Endian.Big);
return true;
}
public override string GetProviderName()
{
return "DaimlerStandardSecurityAlgoRefG";
}
}
}

View File

@ -0,0 +1,47 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace UnlockECU
{
/// <summary>
/// Simpler version of DaimlerStandardSecurityAlgo with custom kA, kC, and no blockB.
/// Similar to DRVU_PROF, with different initial parameter data types
/// </summary>
class EDIFF290 : SecurityProvider
{
public override bool GenerateKey(byte[] inSeed, byte[] outKey, int accessLevel, List<Parameter> parameters)
{
byte[] cryptoKeyBytes = GetParameterBytearray(parameters, "KeyK");
uint cryptoKey = BytesToInt(cryptoKeyBytes, Endian.Big);
long kA = GetParameterInteger(parameters, "kA");
long kC = GetParameterInteger(parameters, "kC");
if ((inSeed.Length != 8) || (outKey.Length != 4))
{
return false;
}
long seedA = BytesToInt(inSeed, Endian.Big, 0);
// kA * seedA + kC, but constrained to 32bits
long intermediate1 = kA * seedA;
intermediate1 &= 0xFFFFFFFF;
intermediate1 += kC;
intermediate1 &= 0xFFFFFFFF;
long seedKey = intermediate1 % cryptoKey;
IntToBytes((uint)seedKey, outKey, Endian.Big);
return true;
}
public override string GetProviderName()
{
return "EDIFF290";
}
}
}

View File

@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace UnlockECU
{
class ESPSecurityAlgoLevel1 : SecurityProvider
{
public override bool GenerateKey(byte[] inSeed, byte[] outKey, int accessLevel, List<Parameter> parameters)
{
if ((inSeed.Length != 2) || (outKey.Length != 2))
{
return false;
}
uint seedAsInt = inSeed[1] | ((uint)inSeed[0] << 8);
uint key = 4 * ((seedAsInt >> 3) ^ seedAsInt) ^ seedAsInt;
outKey[0] = (byte)(key >> 8);
outKey[1] = (byte)(key >> 0);
return true;
}
public override string GetProviderName()
{
return "ESPSecurityAlgoLevel1";
}
}
}

View File

@ -0,0 +1,38 @@
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Crypto.Signers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace UnlockECU
{
/// <summary>
/// Used for modern ECUs. Ed25519PH with an empty context.
/// </summary>
class EsLibEd25519 : SecurityProvider
{
public override bool GenerateKey(byte[] inSeed, byte[] outKey, int accessLevel, List<Parameter> parameters)
{
byte[] privateKeyBytes = GetParameterBytearray(parameters, "PrivateKey");
if ((inSeed.Length != 32) || (outKey.Length != 64))
{
return false;
}
Ed25519PrivateKeyParameters privateKey = new Ed25519PrivateKeyParameters(privateKeyBytes, 0);
Ed25519phSigner signer = new Ed25519phSigner(new byte[] { });
signer.Init(true, privateKey);
signer.BlockUpdate(inSeed, 0, inSeed.Length);
byte[] signature = signer.GenerateSignature();
Array.ConstrainedCopy(signature, 0, outKey, 0, signature.Length);
return true;
}
public override string GetProviderName()
{
return "EsLibEd25519";
}
}
}

View File

@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace UnlockECU
{
class MarquardtSecurityAlgo : SecurityProvider
{
public override bool GenerateKey(byte[] inSeed, byte[] outKey, int accessLevel, List<Parameter> parameters)
{
byte[] constMBytes = GetParameterBytearray(parameters, "const_M");
byte[] constCBytes = GetParameterBytearray(parameters, "const_C");
byte[] constABytes = GetParameterBytearray(parameters, "const_A");
uint constM = BytesToInt(constMBytes, Endian.Big); // 228
uint constC = BytesToInt(constCBytes, Endian.Big); // 236
uint constA = BytesToInt(constABytes, Endian.Big); // 232
uint inSeedAsInt = BytesToInt(inSeed, Endian.Big); // 232
if ((inSeed.Length != 4) || (outKey.Length != 4))
{
return false;
}
uint outKeyInt = 0;
unchecked
{
outKeyInt = constC + inSeedAsInt * constA % constM;
}
IntToBytes(outKeyInt, outKey, Endian.Big);
return true;
}
public override string GetProviderName()
{
return "MarquardtSecurityAlgo";
}
}
}

View File

@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace UnlockECU
{
/// <summary>
/// Passes the input seed as the output key (2 bytes)
/// </summary>
class OCM172 : SecurityProvider
{
public override bool GenerateKey(byte[] inSeed, byte[] outKey, int accessLevel, List<Parameter> parameters)
{
if ((inSeed.Length != 2) || (outKey.Length != 2))
{
return false;
}
outKey[0] = inSeed[0];
outKey[1] = inSeed[1];
return true;
}
public override string GetProviderName()
{
return "OCM172";
}
}
}

View File

@ -0,0 +1,43 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace UnlockECU
{
/// <summary>
/// Appears to be specific to MED97
/// </summary>
class PowertrainBoschContiSecurityAlgo1 : SecurityProvider
{
public override bool GenerateKey(byte[] inSeed, byte[] outKey, int accessLevel, List<Parameter> parameters)
{
byte[] ubTable = GetParameterBytearray(parameters, "ubTable");
byte[] Mask = GetParameterBytearray(parameters, "Mask");
if ((inSeed.Length != 2) || (outKey.Length != 2))
{
return false;
}
uint inSeedAsInt = inSeed[1] | ((uint)inSeed[0] << 8);
uint MaskAsInt = Mask[1] | ((uint)Mask[0] << 8);
uint swBit1 = (inSeedAsInt & MaskAsInt & 0x4000) >> 12;
uint swBit2 = (inSeedAsInt & MaskAsInt & 0x200) >> 8;
uint swBit3 = (inSeedAsInt & MaskAsInt & 0x100) >> 8;
uint keyAsInt = ubTable[swBit1 | swBit2 | swBit3] * inSeedAsInt;
outKey[0] = (byte)((keyAsInt >> 16) & 0xFF);
outKey[1] = (byte)((keyAsInt >> 8) & 0xFF);
return true;
}
public override string GetProviderName()
{
return "PowertrainBoschContiSecurityAlgo1";
}
}
}

View File

@ -0,0 +1,57 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace UnlockECU
{
/// <summary>
/// Appears to be specific to MED97, SIM271CNG906
/// </summary>
class PowertrainBoschContiSecurityAlgo2 : SecurityProvider
{
public override bool GenerateKey(byte[] inSeed, byte[] outKey, int accessLevel, List<Parameter> parameters)
{
byte[] Table = GetParameterBytearray(parameters, "Table");
byte[] uwMasc = GetParameterBytearray(parameters, "uwMasc");
if ((inSeed.Length != 2) || (outKey.Length != 2))
{
return false;
}
uint shiftIndexA = 1;
uint shiftIndexB = 1;
uint activatedBits = 0;
uint inSeedAsInt = inSeed[1] | ((uint)inSeed[0] << 8);
uint uwMascAsInt = uwMasc[1] | ((uint)uwMasc[0] << 8);
for (int i = 0; i < 16; i++)
{
if ((shiftIndexA & uwMascAsInt) > 0)
{
if ((shiftIndexA & inSeedAsInt) > 0)
{
activatedBits |= shiftIndexB;
}
shiftIndexB *= 2;
}
shiftIndexA *= 2;
}
uint keyAsInt = (Table[activatedBits] * inSeedAsInt) >> 8;
keyAsInt &= 0xFFFF;
outKey[0] = (byte)((keyAsInt >> 8) & 0xFF);
outKey[1] = (byte)((keyAsInt >> 0) & 0xFF);
return true;
}
public override string GetProviderName()
{
return "PowertrainBoschContiSecurityAlgo2";
}
}
}

View File

@ -0,0 +1,93 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace UnlockECU
{
/// <summary>
/// Similar to generic PowertrainSecurityAlgo with a fixed i and j table, specific to CRD2, CRD3, CRD3S2 family
/// </summary>
class PowertrainDelphiSecurityAlgo : SecurityProvider
{
public override bool GenerateKey(byte[] inSeed, byte[] outKey, int accessLevel, List<Parameter> parameters)
{
byte[][] dValueMatrix = new byte[][] {
GetParameterBytearray(parameters, "D_VALUE_0"),
GetParameterBytearray(parameters, "D_VALUE_1"),
GetParameterBytearray(parameters, "D_VALUE_2"),
GetParameterBytearray(parameters, "D_VALUE_3"),
GetParameterBytearray(parameters, "D_VALUE_4"),
GetParameterBytearray(parameters, "D_VALUE_5"),
GetParameterBytearray(parameters, "D_VALUE_6"),
GetParameterBytearray(parameters, "D_VALUE_7"),
};
byte[][] gValueMatrix = new byte[][] {
GetParameterBytearray(parameters, "G_VALUE_0"),
GetParameterBytearray(parameters, "G_VALUE_1"),
GetParameterBytearray(parameters, "G_VALUE_2"),
GetParameterBytearray(parameters, "G_VALUE_3"),
GetParameterBytearray(parameters, "G_VALUE_4"),
GetParameterBytearray(parameters, "G_VALUE_5"),
GetParameterBytearray(parameters, "G_VALUE_6"),
GetParameterBytearray(parameters, "G_VALUE_7"),
};
if ((inSeed.Length != 4) || (outKey.Length != 4))
{
return false;
}
byte[] workingSeed = new byte[] { inSeed[3], inSeed[2], inSeed[1], inSeed[0] };
byte y = (byte)(workingSeed[1] ^ workingSeed[0]);
int dBit2 = GetBit((byte)(workingSeed[1] ^ workingSeed[0]), 3);
int dBit1 = GetBit(workingSeed[2], 3);
int dBit0 = GetBit(workingSeed[3], 6);
uint dValue = CreateDValue(dBit2, dBit1, dBit0, dValueMatrix);
uint seedAsInt = BytesToInt(workingSeed, Endian.Little);
uint dXorIntermediate = seedAsInt ^ dValue;
int gBit0 = GetBit(workingSeed[1], 3);
int gBit1 = GetBit(y, 7);
int gBit2 = GetBit(GetByte(dXorIntermediate, 1), 2);
uint gValue = CreateDValue(gBit2, gBit1, gBit0, gValueMatrix);
uint seedKey = dXorIntermediate ^ gValue;
IntToBytes(seedKey, outKey, Endian.Big);
return true;
}
private uint CreateDValue(int bit2Enabled, int bit1Enabled, int bit0Enabled, byte[][] matrix)
{
uint i = 0;
byte j = 0;
if (bit0Enabled != 0)
{
j = SetBit(j, 0);
}
if (bit1Enabled != 0)
{
j = SetBit(j, 1);
}
if (bit2Enabled != 0)
{
j = SetBit(j, 2);
}
i = SetByte(i, matrix[j][3], 0);
i = SetByte(i, matrix[j][2], 1);
i = SetByte(i, matrix[j][1], 2);
i = SetByte(i, matrix[j][0], 3);
return i;
}
public override string GetProviderName()
{
return "PowertrainDelphiSecurityAlgo";
}
}
}

View File

@ -0,0 +1,125 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace UnlockECU
{
/// <summary>
/// Basic implementation PowertrainSecurityAlgo.
/// </summary>
class PowertrainSecurityAlgo : SecurityProvider
{
public override bool GenerateKey(byte[] inSeed, byte[] outKey, int accessLevel, List<Parameter> parameters)
{
byte[][] matrix = new byte[][]
{
new byte[]{ GetParameterByte(parameters, "X00"), GetParameterByte(parameters, "X01"), GetParameterByte(parameters, "X02"), GetParameterByte(parameters, "X03") },
new byte[]{ GetParameterByte(parameters, "X10"), GetParameterByte(parameters, "X11"), GetParameterByte(parameters, "X12"), GetParameterByte(parameters, "X13") },
new byte[]{ GetParameterByte(parameters, "X20"), GetParameterByte(parameters, "X21"), GetParameterByte(parameters, "X22"), GetParameterByte(parameters, "X23") },
new byte[]{ GetParameterByte(parameters, "X30"), GetParameterByte(parameters, "X31"), GetParameterByte(parameters, "X32"), GetParameterByte(parameters, "X33") },
new byte[]{ GetParameterByte(parameters, "X40"), GetParameterByte(parameters, "X41"), GetParameterByte(parameters, "X42"), GetParameterByte(parameters, "X43") },
new byte[]{ GetParameterByte(parameters, "X50"), GetParameterByte(parameters, "X51"), GetParameterByte(parameters, "X52"), GetParameterByte(parameters, "X53") },
new byte[]{ GetParameterByte(parameters, "X60"), GetParameterByte(parameters, "X61"), GetParameterByte(parameters, "X62"), GetParameterByte(parameters, "X63") },
new byte[]{ GetParameterByte(parameters, "X70"), GetParameterByte(parameters, "X71"), GetParameterByte(parameters, "X72"), GetParameterByte(parameters, "X73") },
};
int[] i = new int[]
{
GetParameterInteger(parameters, "i1"),
GetParameterInteger(parameters, "i2"),
GetParameterInteger(parameters, "i3"),
GetParameterInteger(parameters, "i4"),
GetParameterInteger(parameters, "i5"),
GetParameterInteger(parameters, "i6")
};
int[] j = new int[]
{
GetParameterInteger(parameters, "j1"),
GetParameterInteger(parameters, "j2"),
GetParameterInteger(parameters, "j3"),
GetParameterInteger(parameters, "j4"),
GetParameterInteger(parameters, "j5"),
GetParameterInteger(parameters, "j6")
};
if ((inSeed.Length != 4) || (outKey.Length != 4))
{
return false;
}
byte[] workingSeed = new byte[] { inSeed[3], inSeed[2], inSeed[1], inSeed[0] };
byte y = (byte)(workingSeed[i[0]] ^ workingSeed[i[1]]);
int dBit2 = GetBit(workingSeed[i[2]], j[0]);
int dBit1 = GetBit(workingSeed[i[3]], j[1]);
int dBit0 = GetBit(y, j[2]);
uint dValue = CreateDValue(dBit2, dBit1, dBit0, matrix);
uint seedAsInt = BytesToInt(workingSeed, Endian.Little);
uint dXorIntermediate = seedAsInt ^ dValue;
int gBit2 = GetBit(workingSeed[i[4]], j[3]);
int gBit1 = GetBit(y, j[4]);
int gBit0 = GetBit(GetByte(dXorIntermediate, i[5]), j[5]);
uint gValue = CreateGValue(gBit2, gBit1, gBit0, matrix);
uint seedKey = dXorIntermediate ^ gValue;
IntToBytes(seedKey, outKey, Endian.Big);
return true;
}
private uint CreateDValue(int bit2Enabled, int bit1Enabled, int bit0Enabled, byte[][] matrix)
{
uint i = 0;
byte j = 0;
if (bit0Enabled != 0)
{
j = SetBit(j, 0);
}
if (bit1Enabled != 0)
{
j = SetBit(j, 1);
}
if (bit2Enabled != 0)
{
j = SetBit(j, 2);
}
i = SetByte(i, matrix[j][3], 0);
i = SetByte(i, matrix[j][2], 1);
i = SetByte(i, matrix[j][1], 2);
i = SetByte(i, matrix[j][0], 3);
return i;
}
private uint CreateGValue(int bit2Enabled, int bit1Enabled, int bit0Enabled, byte[][] matrix)
{
uint i = 0;
byte j = 0;
if (bit0Enabled != 0)
{
j = SetBit(j, 0);
}
if (bit1Enabled != 0)
{
j = SetBit(j, 1);
}
if (bit2Enabled != 0)
{
j = SetBit(j, 2);
}
i = SetByte(i, matrix[j][2], 0);
i = SetByte(i, matrix[j][1], 1);
i = SetByte(i, matrix[j][0], 2);
i = SetByte(i, matrix[j][3], 3);
return i;
}
public override string GetProviderName()
{
return "PowertrainSecurityAlgo";
}
}
}

View File

@ -0,0 +1,126 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace UnlockECU
{
/// <summary>
/// Derivative of PowertrainSecurityAlgo, with a slightly different byte ordering when computing the g-value.
/// </summary>
class PowertrainSecurityAlgo2 : SecurityProvider
{
public override bool GenerateKey(byte[] inSeed, byte[] outKey, int accessLevel, List<Parameter> parameters)
{
byte[][] matrix = new byte[][]
{
new byte[]{ GetParameterByte(parameters, "XX00"), GetParameterByte(parameters, "XX01"), GetParameterByte(parameters, "XX02"), GetParameterByte(parameters, "XX03") },
new byte[]{ GetParameterByte(parameters, "XX10"), GetParameterByte(parameters, "XX11"), GetParameterByte(parameters, "XX12"), GetParameterByte(parameters, "XX13") },
new byte[]{ GetParameterByte(parameters, "XX20"), GetParameterByte(parameters, "XX21"), GetParameterByte(parameters, "XX22"), GetParameterByte(parameters, "XX23") },
new byte[]{ GetParameterByte(parameters, "XX30"), GetParameterByte(parameters, "XX31"), GetParameterByte(parameters, "XX32"), GetParameterByte(parameters, "XX33") },
new byte[]{ GetParameterByte(parameters, "XX40"), GetParameterByte(parameters, "XX41"), GetParameterByte(parameters, "XX42"), GetParameterByte(parameters, "XX43") },
new byte[]{ GetParameterByte(parameters, "XX50"), GetParameterByte(parameters, "XX51"), GetParameterByte(parameters, "XX52"), GetParameterByte(parameters, "XX53") },
new byte[]{ GetParameterByte(parameters, "XX60"), GetParameterByte(parameters, "XX61"), GetParameterByte(parameters, "XX62"), GetParameterByte(parameters, "XX63") },
new byte[]{ GetParameterByte(parameters, "XX70"), GetParameterByte(parameters, "XX71"), GetParameterByte(parameters, "XX72"), GetParameterByte(parameters, "XX73") },
};
int[] i = new int[]
{
GetParameterInteger(parameters, "ii1"),
GetParameterInteger(parameters, "ii2"),
GetParameterInteger(parameters, "ii3"),
GetParameterInteger(parameters, "ii4"),
GetParameterInteger(parameters, "ii5"),
GetParameterInteger(parameters, "ii6")
};
int[] j = new int[]
{
GetParameterInteger(parameters, "jj1"),
GetParameterInteger(parameters, "jj2"),
GetParameterInteger(parameters, "jj3"),
GetParameterInteger(parameters, "jj4"),
GetParameterInteger(parameters, "jj5"),
GetParameterInteger(parameters, "jj6")
};
if ((inSeed.Length != 4) || (outKey.Length != 4))
{
return false;
}
byte[] workingSeed = new byte[] { inSeed[3], inSeed[2], inSeed[1], inSeed[0] };
byte y = (byte)(workingSeed[i[0]] ^ workingSeed[i[1]]);
int dBit2 = GetBit(workingSeed[i[2]], j[0]);
int dBit1 = GetBit(workingSeed[i[3]], j[1]);
int dBit0 = GetBit(y, j[2]);
uint dValue = CreateDValue(dBit2, dBit1, dBit0, matrix);
uint seedAsInt = BytesToInt(workingSeed, Endian.Little);
uint dXorIntermediate = seedAsInt ^ dValue;
int gBit2 = GetBit(workingSeed[i[4]], j[3]);
int gBit1 = GetBit(y, j[4]);
int gBit0 = GetBit(GetByte(dXorIntermediate, i[5]), j[5]);
uint gValue = CreateGValue(gBit2, gBit1, gBit0, matrix);
uint seedKey = dXorIntermediate ^ gValue;
IntToBytes(seedKey, outKey, Endian.Big);
return true;
}
private uint CreateDValue(int bit2Enabled, int bit1Enabled, int bit0Enabled, byte[][] matrix)
{
uint i = 0;
byte j = 0;
if (bit0Enabled != 0)
{
j = SetBit(j, 0);
}
if (bit1Enabled != 0)
{
j = SetBit(j, 1);
}
if (bit2Enabled != 0)
{
j = SetBit(j, 2);
}
i = SetByte(i, matrix[j][3], 0);
i = SetByte(i, matrix[j][2], 1);
i = SetByte(i, matrix[j][1], 2);
i = SetByte(i, matrix[j][0], 3);
return i;
}
private uint CreateGValue(int bit2Enabled, int bit1Enabled, int bit0Enabled, byte[][] matrix)
{
uint i = 0;
byte j = 0;
if (bit0Enabled != 0)
{
j = SetBit(j, 0);
}
if (bit1Enabled != 0)
{
j = SetBit(j, 1);
}
if (bit2Enabled != 0)
{
j = SetBit(j, 2);
}
i = SetByte(i, matrix[j][0], 0);
i = SetByte(i, matrix[j][3], 1);
i = SetByte(i, matrix[j][2], 2);
i = SetByte(i, matrix[j][1], 3);
return i;
}
public override string GetProviderName()
{
return "PowertrainSecurityAlgo2";
}
}
}

View File

@ -0,0 +1,93 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace UnlockECU
{
/// <summary>
/// Similar to PowertrainDelphiSecurityAlgo
/// </summary>
class PowertrainSecurityAlgoNFZ : SecurityProvider
{
public override bool GenerateKey(byte[] inSeed, byte[] outKey, int accessLevel, List<Parameter> parameters)
{
byte[][] dValueMatrix = new byte[][] {
GetParameterBytearray(parameters, "D_VALUE_0"),
GetParameterBytearray(parameters, "D_VALUE_1"),
GetParameterBytearray(parameters, "D_VALUE_2"),
GetParameterBytearray(parameters, "D_VALUE_3"),
GetParameterBytearray(parameters, "D_VALUE_4"),
GetParameterBytearray(parameters, "D_VALUE_5"),
GetParameterBytearray(parameters, "D_VALUE_6"),
GetParameterBytearray(parameters, "D_VALUE_7"),
};
byte[][] gValueMatrix = new byte[][] {
GetParameterBytearray(parameters, "G_VALUE_0"),
GetParameterBytearray(parameters, "G_VALUE_1"),
GetParameterBytearray(parameters, "G_VALUE_2"),
GetParameterBytearray(parameters, "G_VALUE_3"),
GetParameterBytearray(parameters, "G_VALUE_4"),
GetParameterBytearray(parameters, "G_VALUE_5"),
GetParameterBytearray(parameters, "G_VALUE_6"),
GetParameterBytearray(parameters, "G_VALUE_7"),
};
if ((inSeed.Length != 4) || (outKey.Length != 4))
{
return false;
}
byte[] workingSeed = new byte[] { inSeed[3], inSeed[2], inSeed[1], inSeed[0] };
int dBit2 = GetBit((byte)(workingSeed[3] ^ workingSeed[1]), 1);
int dBit1 = GetBit(workingSeed[2], 4);
int dBit0 = GetBit(workingSeed[0], 6);
uint dValue = CreateDValue(dBit2, dBit1, dBit0, dValueMatrix);
uint seedAsInt = BytesToInt(workingSeed, Endian.Little);
uint dXorIntermediate = seedAsInt ^ dValue;
int gBit0 = GetBit(GetByte(dXorIntermediate, 3), 4);
int gBit1 = GetBit(workingSeed[0], 1);
int gBit2 = GetBit(workingSeed[2], 6);
uint gValue = CreateDValue(gBit2, gBit1, gBit0, gValueMatrix);
uint seedKey = dXorIntermediate ^ gValue;
IntToBytes(seedKey, outKey, Endian.Big);
return true;
}
private uint CreateDValue(int bit2Enabled, int bit1Enabled, int bit0Enabled, byte[][] matrix)
{
uint i = 0;
byte j = 0;
if (bit0Enabled != 0)
{
j = SetBit(j, 0);
}
if (bit1Enabled != 0)
{
j = SetBit(j, 1);
}
if (bit2Enabled != 0)
{
j = SetBit(j, 2);
}
i = SetByte(i, matrix[j][3], 0);
i = SetByte(i, matrix[j][2], 1);
i = SetByte(i, matrix[j][1], 2);
i = SetByte(i, matrix[j][0], 3);
return i;
}
public override string GetProviderName()
{
return "PowertrainSecurityAlgoNFZ";
}
}
}

View File

@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace UnlockECU
{
/// <summary>
/// Passes the input seed as the output key (4 bytes)
/// </summary>
class RBTM : SecurityProvider
{
public override bool GenerateKey(byte[] inSeed, byte[] outKey, int accessLevel, List<Parameter> parameters)
{
if ((inSeed.Length != 4) || (outKey.Length != 4))
{
return false;
}
outKey[0] = inSeed[0];
outKey[1] = inSeed[1];
outKey[2] = inSeed[2];
outKey[3] = inSeed[3];
return true;
}
public override string GetProviderName()
{
return "RBTM";
}
}
}

View File

@ -0,0 +1,41 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
#pragma warning disable CS0675 // Bitwise-or operator used on a sign-extended operand
namespace UnlockECU
{
/// <summary>
/// RDU222: seed |= A, ^= B, += C
/// </summary>
class RDU222 : SecurityProvider
{
public override bool GenerateKey(byte[] inSeed, byte[] outKey, int accessLevel, List<Parameter> parameters)
{
long paramA = BytesToInt(GetParameterBytearray(parameters, "a"), Endian.Big);
long paramB = BytesToInt(GetParameterBytearray(parameters, "b"), Endian.Big);
long paramC = BytesToInt(GetParameterBytearray(parameters, "c"), Endian.Big);
if ((inSeed.Length != 4) || (outKey.Length != 4))
{
return false;
}
long inSeedAsLong = BytesToInt(inSeed, Endian.Big);
inSeedAsLong |= paramA;
inSeedAsLong ^= paramB;
inSeedAsLong += paramC;
IntToBytes((uint)inSeedAsLong, outKey, Endian.Big);
return true;
}
public override string GetProviderName()
{
return "RDU222";
}
}
}

View File

@ -0,0 +1,43 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
#pragma warning disable CS0675 // Bitwise-or operator used on a sign-extended operand
namespace UnlockECU
{
/// <summary>
/// Similar to RDU222, with an extra XOR
/// </summary>
class RVC222_MPC222_FCW246_LRR3 : SecurityProvider
{
public override bool GenerateKey(byte[] inSeed, byte[] outKey, int accessLevel, List<Parameter> parameters)
{
long paramA = BytesToInt(GetParameterBytearray(parameters, "A"), Endian.Big);
long paramB = BytesToInt(GetParameterBytearray(parameters, "B"), Endian.Big);
long paramC = BytesToInt(GetParameterBytearray(parameters, "C"), Endian.Big);
if ((inSeed.Length != 4) || (outKey.Length != 4))
{
return false;
}
long inSeedAsLong = BytesToInt(inSeed, Endian.Big);
inSeedAsLong |= paramA;
inSeedAsLong ^= paramB;
inSeedAsLong += paramC;
inSeedAsLong ^= 0xFFFFFFFF;
IntToBytes((uint)inSeedAsLong, outKey, Endian.Big);
return true;
}
public override string GetProviderName()
{
return "RVC222_MPC222_FCW246_LRR3";
}
}
}

View File

@ -0,0 +1,43 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace UnlockECU
{
/// <summary>
/// SWSP177: behavior, param sizes don't match DLL
/// </summary>
class SWSP177 : SecurityProvider
{
public override bool GenerateKey(byte[] inSeed, byte[] outKey, int accessLevel, List<Parameter> parameters)
{
byte[] cryptoKeyBytes = GetParameterBytearray(parameters, "KeyConst");
uint cryptoKey = BytesToInt(cryptoKeyBytes, Endian.Big);
if ((inSeed.Length != 4) || (outKey.Length != 4))
{
return false;
}
ulong seedA = BytesToInt(inSeed, Endian.Big, 0);
for (int i = 0; i < 35; i++)
{
seedA = seedA >> 1 | (seedA & 1) * 0x80000000;
seedA ^= cryptoKey;
}
ulong seedKey = seedA & 0xFFFFFFFF;
IntToBytes((uint)seedKey, outKey, Endian.Big);
return true;
}
public override string GetProviderName()
{
return "SWSP177";
}
}
}

View File

@ -0,0 +1,174 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace UnlockECU
{
/// <summary>
/// Basic SecurityProvider to be inherited from. This class should not be directly initialized.
/// </summary>
public class SecurityProvider
{
public virtual string GetProviderName()
{
return "ProviderName was not initialized";
}
public virtual bool GenerateKey(byte[] inSeed, byte[] outKey, int accessLevel, List<Parameter> parameters)
{
throw new Exception("GenerateKey was not overridden");
}
public byte GetParameterByte(List<Parameter> parameters, string key)
{
foreach (Parameter row in parameters)
{
if ((row.Key == key) && (row.DataType == "Byte"))
{
return (byte)(int.Parse(row.Value, System.Globalization.NumberStyles.HexNumber));
}
}
throw new Exception($"Failed to fetch byte parameter for key: {key}");
}
public int GetParameterInteger(List<Parameter> parameters, string key)
{
foreach (Parameter row in parameters)
{
if ((row.Key == key) && (row.DataType == "Int32"))
{
return int.Parse(row.Value, System.Globalization.NumberStyles.HexNumber);
}
}
throw new Exception($"Failed to fetch Int32 parameter for key: {key}");
}
public long GetParameterLong(List<Parameter> parameters, string key)
{
foreach (Parameter row in parameters)
{
if ((row.Key == key) && (row.DataType == "Int64"))
{
return long.Parse(row.Value, System.Globalization.NumberStyles.HexNumber);
}
}
throw new Exception($"Failed to fetch Int64 parameter for key: {key}");
}
public byte[] GetParameterBytearray(List<Parameter> parameters, string key)
{
foreach (Parameter row in parameters)
{
if ((row.Key == key) && (row.DataType == "ByteArray"))
{
return BitUtility.BytesFromHex(row.Value);
}
}
throw new Exception($"Failed to fetch ByteArray parameter for key: {key}");
}
private static bool IsInitialized = false;
private static List<SecurityProvider> SecurityProviders = new List<SecurityProvider>();
public static List<SecurityProvider> GetSecurityProviders()
{
if (IsInitialized)
{
return SecurityProviders;
}
SecurityProviders = new List<SecurityProvider>();
System.Reflection.Assembly
.GetExecutingAssembly()
.GetTypes()
.Where(x => x.IsSubclassOf(typeof(SecurityProvider)))
.ToList()
.ForEach(x => SecurityProviders.Add((SecurityProvider)Activator.CreateInstance(x)));
IsInitialized = true;
return SecurityProviders;
}
public enum Endian
{
Big,
Little,
}
public uint BytesToInt(byte[] inBytes, Endian endian, int offset = 0)
{
uint result = 0;
if (endian == Endian.Big)
{
result |= (uint)inBytes[offset++] << 24;
result |= (uint)inBytes[offset++] << 16;
result |= (uint)inBytes[offset++] << 8;
result |= (uint)inBytes[offset++] << 0;
}
else
{
result |= (uint)inBytes[offset++] << 0;
result |= (uint)inBytes[offset++] << 8;
result |= (uint)inBytes[offset++] << 16;
result |= (uint)inBytes[offset++] << 24;
}
return result;
}
public void IntToBytes(uint inInt, byte[] outBytes, Endian endian)
{
if (endian == Endian.Big)
{
outBytes[0] = (byte)(inInt >> 24);
outBytes[1] = (byte)(inInt >> 16);
outBytes[2] = (byte)(inInt >> 8);
outBytes[3] = (byte)(inInt >> 0);
}
else
{
outBytes[3] = (byte)(inInt >> 24);
outBytes[2] = (byte)(inInt >> 16);
outBytes[1] = (byte)(inInt >> 8);
outBytes[0] = (byte)(inInt >> 0);
}
}
// WARNING: endian unaware:
public byte GetBit(byte inByte, int bitPosition)
{
if (bitPosition > 7)
{
throw new Exception("Attempted to shift beyond 8 bits in a byte");
}
return (byte)((inByte >> bitPosition) & 1);
}
public byte GetByte(uint inInt, int bytePosition)
{
if (bytePosition > 3)
{
throw new Exception("Attempted to shift beyond 4 bytes in an uint");
}
return (byte)(inInt >> (8 * bytePosition));
}
public byte SetBit(byte inByte, int bitPosition)
{
if (bitPosition > 7)
{
throw new Exception("Attempted to shift beyond 8 bits in a byte");
}
return inByte |= (byte)(1 << bitPosition);
}
public uint SetByte(uint inInt, byte byteToSet, int bytePosition)
{
if (bytePosition > 3)
{
throw new Exception("Attempted to shift beyond 4 bytes in an uint");
}
int bitPosition = 8 * bytePosition;
inInt &= ~(uint)(0xFF << bitPosition);
inInt |= (uint)(byteToSet << bitPosition);
return inInt;
}
}
}

View File

@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Library</OutputType>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<PlatformTarget>AnyCPU</PlatformTarget>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Portable.BouncyCastle" Version="1.8.8" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,227 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Security.Cryptography;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.IO;
namespace UnlockECUTests
{
/// <summary>
/// High level interface to access a Vector DLL
/// </summary>
public class DllContext
{
private IntPtr dllHandle = IntPtr.Zero;
public List<string> DllExports = new List<string>();
private Dictionary<string, IntPtr> dllAddressMappings = new Dictionary<string, IntPtr>();
public string SHA1Hash = "";
public string FileDescription = "";
public string FileName = "";
public string DLLPath = "";
public string ECUName = "";
public string Comment = "";
public bool KeyGenerationCapability = false;
public bool ModeSpecified = false;
public List<Tuple<uint, int, int>> AccessLevels = new List<Tuple<uint, int, int>>();
public DllContext(string filePath, bool runHash = true)
{
DLLPath = filePath;
if (!File.Exists(DLLPath))
{
Console.WriteLine($"{DLLPath}: File does not exist");
return;
}
FileName = Path.GetFileName(filePath);
// Compute and store the file hash
if (runHash)
{
using (var cryptoProvider = new SHA1CryptoServiceProvider())
{
SHA1Hash = BitConverter.ToString(cryptoProvider.ComputeHash(File.ReadAllBytes(filePath))).Replace("-", "");
}
}
// Get the module's exports
DllExports = UnmanagedUtility.GetExports(DLLPath);
if (DllExports.Count == 0)
{
Console.WriteLine($"{DLLPath}: No exports, possibly an invalid DLL");
return;
}
// Try to load the library into our process space
dllHandle = UnmanagedUtility.LoadLibrary(filePath);
if (dllHandle == IntPtr.Zero)
{
Console.WriteLine($"{DLLPath}: LoadLibrary failed");
return;
}
// Try to load addresses of all known exports
dllAddressMappings = new Dictionary<string, IntPtr>();
foreach (string knownExport in ExportDefinition.KnownExportedFunctions)
{
if (DllExports.Contains(knownExport))
{
dllAddressMappings.Add(knownExport, UnmanagedUtility.GetProcAddress(dllHandle, knownExport));
}
else
{
dllAddressMappings.Add(knownExport, IntPtr.Zero);
}
}
// Set capabilities
KeyGenerationCapability = DllExports.Contains("GenerateKeyEx") || DllExports.Contains("GenerateKeyExOpt");
ModeSpecified = DllExports.Contains("GetKeyLength") && DllExports.Contains("GetSeedLength") && DllExports.Contains("GetConfiguredAccessTypes");
// Store additional metadata
FileDescription = FileVersionInfo.GetVersionInfo(DLLPath).FileDescription;
LoadAdditionalDataFromDllCalls();
}
public void LoadAdditionalDataFromDllCalls()
{
ECUName = GetECUName();
Comment = GetComment();
if (!ModeSpecified)
{
return;
}
// Access level, key size, seed size
AccessLevels = new List<Tuple<uint, int, int>>();
foreach (uint accessLevel in GetConfiguredAccessTypes())
{
AccessLevels.Add(new Tuple<uint, int, int>(accessLevel, GetKeyLength(accessLevel), GetSeedLength(accessLevel)));
}
}
// Automatically selects and invokes the correct key generation function. Prefers the "opt" variant
public byte[] GenerateKeyAuto(uint securityLevel, byte[] seed)
{
if (DllExports.Contains("GenerateKeyExOpt"))
{
return GenerateKey(seed, securityLevel, true, out ExportDefinition.VKeyGenResultEx result);
}
else if (DllExports.Contains("GenerateKeyEx"))
{
return GenerateKey(seed, securityLevel, false, out ExportDefinition.VKeyGenResultEx result);
}
else
{
return new byte[] { };
}
}
public string GetECUName()
{
IntPtr procAddress = dllAddressMappings["GetECUName"];
if (procAddress == IntPtr.Zero)
{
return "(unavailable)";
}
var fn = (ExportDefinition.GetECUName)Marshal.GetDelegateForFunctionPointer(procAddress, typeof(ExportDefinition.GetECUName));
IntPtr resultPtr = fn();
return Marshal.PtrToStringAnsi(resultPtr);
}
public string GetComment()
{
IntPtr procAddress = dllAddressMappings["GetComment"];
if (procAddress == IntPtr.Zero)
{
return "(unavailable)";
}
var fn = (ExportDefinition.GetComment)Marshal.GetDelegateForFunctionPointer(procAddress, typeof(ExportDefinition.GetComment));
IntPtr resultPtr = fn();
return Marshal.PtrToStringAnsi(resultPtr);
}
public List<uint> GetConfiguredAccessTypes()
{
IntPtr procAddress = dllAddressMappings["GetConfiguredAccessTypes"];
if (procAddress == IntPtr.Zero)
{
return new List<uint>();
}
uint[] accessTypes = new uint[1000];
var fn = (ExportDefinition.GetConfiguredAccessTypes)Marshal.GetDelegateForFunctionPointer(procAddress, typeof(ExportDefinition.GetConfiguredAccessTypes));
int accessTypesCount = fn(accessTypes);
return accessTypes.Take(accessTypesCount).ToList();
}
public int GetSeedLength(uint securityLevel)
{
IntPtr procAddress = dllAddressMappings["GetSeedLength"];
if (procAddress == IntPtr.Zero)
{
return 0;
}
var fn = (ExportDefinition.GetSeedLength)Marshal.GetDelegateForFunctionPointer(procAddress, typeof(ExportDefinition.GetSeedLength));
return fn(securityLevel);
}
public int GetKeyLength(uint securityLevel)
{
IntPtr procAddress = dllAddressMappings["GetKeyLength"];
if (procAddress == IntPtr.Zero)
{
return 0;
}
var fn = (ExportDefinition.GetKeyLength)Marshal.GetDelegateForFunctionPointer(procAddress, typeof(ExportDefinition.GetKeyLength));
return fn(securityLevel);
}
private byte[] GenerateKey(byte[] seed, uint securityLevel, bool addOptionParameter, out ExportDefinition.VKeyGenResultEx returnError)
{
returnError = ExportDefinition.VKeyGenResultEx.UnknownError;
IntPtr procAddress = dllAddressMappings[addOptionParameter ? "GenerateKeyExOpt" : "GenerateKeyEx"];
if ((!KeyGenerationCapability) || procAddress == IntPtr.Zero)
{
return new byte[] { };
}
byte[] keyResult = new byte[0x1000];
uint actualkeySize;
int keygenResult = (int)returnError;
if (addOptionParameter)
{
var fn = (ExportDefinition.GenerateKeyExOpt)Marshal.GetDelegateForFunctionPointer(procAddress, typeof(ExportDefinition.GenerateKeyExOpt));
keygenResult = fn(seed, (uint)seed.Length, securityLevel, null, null, keyResult, (uint)keyResult.Length, out actualkeySize);
}
else
{
var fn = (ExportDefinition.GenerateKeyEx)Marshal.GetDelegateForFunctionPointer(procAddress, typeof(ExportDefinition.GenerateKeyEx));
keygenResult = fn(seed, (uint)seed.Length, securityLevel, null, keyResult, (uint)keyResult.Length, out actualkeySize);
}
returnError = (ExportDefinition.VKeyGenResultEx)keygenResult;
keyResult = keyResult.Take((int)actualkeySize).ToArray();
return keyResult;
}
public void UnloadLibrary()
{
// WARNING: the instance will no longer be able to access native functions after this is called
// This is a workaround if many DLLs have to be enumerated for their metadata -- Windows has a limit on the number of DLLs that can be loaded simultaneously
UnmanagedUtility.FreeLibrary(dllHandle);
}
~DllContext()
{
if (dllHandle != IntPtr.Zero)
{
UnmanagedUtility.FreeLibrary(dllHandle);
}
}
}
}

View File

@ -0,0 +1,46 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace UnlockECUTests
{
class ExportDefinition
{
public static string[] KnownExportedFunctions = new string[] { "GetECUName", "GetComment", "GetKeyLength", "GetSeedLength", "GetConfiguredAccessTypes", "GenerateKeyExOpt", "GenerateKeyEx" };
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate IntPtr GetECUName();
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate IntPtr GetComment();
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int GetKeyLength(uint iSecurityLevel);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int GetSeedLength(uint iSecurityLevel);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int GetConfiguredAccessTypes(uint[] iSecurityLevels);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int GenerateKeyExOpt(byte[] ipSeedArray, uint iSeedArraySize, uint iSecurityLevel, byte[] ipVariant, byte[] ipOptions, byte[] iopKeyArray, uint iMaxKeyArraySize, out uint oActualKeyArraySize);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int GenerateKeyEx(byte[] ipSeedArray, uint iSeedArraySize, uint iSecurityLevel, byte[] ipVariant, byte[] iopKeyArray, uint iMaxKeyArraySize, out uint oActualKeyArraySize);
public enum VKeyGenResultEx
{
OK = 0,
BufferTooSmall = 1,
SecurityLevelInvalid = 2,
VariantInvalid = 3,
UnknownError = 4
}
}
}

View File

@ -0,0 +1,126 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace UnlockECUTests
{
class UnmanagedUtility
{
/*
Symbol enumeration:
https://stackoverflow.com/questions/18249566/c-sharp-get-the-list-of-unmanaged-c-dll-exports
*/
[DllImport("dbghelp.dll", SetLastError = true, CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SymInitialize(IntPtr hProcess, string UserSearchPath, [MarshalAs(UnmanagedType.Bool)] bool fInvadeProcess);
[DllImport("dbghelp.dll", SetLastError = true, CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SymCleanup(IntPtr hProcess);
[DllImport("dbghelp.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern ulong SymLoadModuleEx(IntPtr hProcess, IntPtr hFile, string ImageName, string ModuleName, long BaseOfDll, int DllSize, IntPtr Data, int Flags);
[DllImport("dbghelp.dll", SetLastError = true, CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SymEnumerateSymbols64(IntPtr hProcess, ulong BaseOfDll, SymEnumerateSymbolsProc64 EnumSymbolsCallback, IntPtr UserContext);
public delegate bool SymEnumerateSymbolsProc64(string SymbolName, ulong SymbolAddress, uint SymbolSize, IntPtr UserContext);
/*
DLL invocation:
https://stackoverflow.com/questions/16518943/dllimport-or-loadlibrary-for-best-performance
*/
[DllImport("kernel32.dll")]
public static extern IntPtr LoadLibrary(string dllToLoad);
[DllImport("kernel32.dll")]
public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);
[DllImport("kernel32.dll")]
public static extern bool FreeLibrary(IntPtr hModule);
// Required for textbox placeholder string
public const int EM_SETCUEBANNER = 0x1501;
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern Int32 SendMessage(IntPtr hWnd, int msg, int wParam, [MarshalAs(UnmanagedType.LPWStr)] string lParam);
private static List<string> LibraryExports = new List<string>();
public static bool SymbolEnumeratedCallback(string name, ulong address, uint size, IntPtr context)
{
// Useful for debug:
// Console.WriteLine(name);
LibraryExports.Add(name);
return true;
}
private static bool EnumerateDllExports(string modulePath)
{
IntPtr hCurrentProcess = Process.GetCurrentProcess().Handle;
ulong dllBase;
// Initialize symbol handler with our own process handle
if (!SymInitialize(hCurrentProcess, null, false))
{
Console.WriteLine("SymInitialize function (dbghelp.h) failed");
return false;
}
// Load dll
dllBase = SymLoadModuleEx(hCurrentProcess, IntPtr.Zero, modulePath, null, 0, 0, IntPtr.Zero, 0);
if (dllBase == 0)
{
Console.Out.WriteLine($"Failed to load module: {modulePath}");
SymCleanup(hCurrentProcess);
return false;
}
// Clean up the results list before it gets populated
LibraryExports.Clear();
// Enumerate symbols. For every symbol, the callback method SymbolEnumeratedCallback is called.
if (SymEnumerateSymbols64(hCurrentProcess, dllBase, SymbolEnumeratedCallback, IntPtr.Zero) == false)
{
Console.Out.WriteLine($"Failed to enumerate symbols for library {modulePath}");
return false;
}
SymCleanup(hCurrentProcess);
return true;
}
public static List<string> GetExports(string modulePath)
{
if (EnumerateDllExports(modulePath))
{
return LibraryExports;
}
else
{
return new List<string>();
}
}
public static void DumpExportsToConsole(string modulePath)
{
List<string> exports = UnmanagedUtility.GetExports(modulePath);
Console.WriteLine($"Retrieving exports for {modulePath}");
foreach (string s in exports)
{
Console.WriteLine($"{modulePath}: {s}");
}
Console.WriteLine($"End of {modulePath} exports.");
}
}
}

View File

@ -0,0 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<PlatformTarget>x86</PlatformTarget>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.16.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\UnlockECU\UnlockECU.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,146 @@
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using UnlockECU;
namespace UnlockECUTests
{
public class Tests
{
[SetUp]
public void Setup()
{
}
[Test]
public void VerifyOutputWithOfficialDLLs()
{
string dbPath = $"{GetLibraryFolder()}db.json";
string definitionJson = File.ReadAllText(dbPath);
List<Definition> definitions = System.Text.Json.JsonSerializer.Deserialize<List<Definition>>(definitionJson);
List<SecurityProvider> providers = SecurityProvider.GetSecurityProviders();
QuickTest(definitions.Find(x => (x.EcuName == "CRD3S2SEC9A") && (x.AccessLevel == 9) && (x.Provider == "DaimlerStandardSecurityAlgo")), providers);
QuickTest(definitions.Find(x => (x.EcuName == "RBS222") && (x.AccessLevel == 11) && (x.Provider == "DaimlerStandardSecurityAlgoMod")), providers);
QuickTest(definitions.Find(x => (x.EcuName == "IC177") && (x.AccessLevel == 11) && (x.Provider == "DaimlerStandardSecurityAlgoRefG")), providers);
QuickTest(definitions.Find(x => (x.EcuName == "MED40") && (x.AccessLevel == 5) && (x.Provider == "PowertrainSecurityAlgo")), providers);
QuickTest(definitions.Find(x => (x.EcuName == "CR6NFZ") && (x.AccessLevel == 1) && (x.Provider == "PowertrainSecurityAlgo2")), providers);
QuickTest(definitions.Find(x => (x.EcuName == "DCDC223") && (x.AccessLevel == 17) && (x.Provider == "EsLibEd25519")), providers);
Assert.Pass();
}
static string GetLibraryFolder()
{
return $"{Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)}{Path.DirectorySeparatorChar}Library{Path.DirectorySeparatorChar}";
}
static void QuickTest(Definition definition, List<SecurityProvider> providers)
{
// Build in x86 to test, else the pinvokes will fail!
SecurityProvider provider = providers.Find(x => x.GetProviderName() == definition.Provider);
if (provider is null)
{
Console.WriteLine($"Could not find a security provider for {definition.EcuName} ({definition.Provider})");
Assert.Fail();
return;
}
string dllPath = $"{GetLibraryFolder()}{definition.Provider}_L{definition.AccessLevel}.dll";
Console.WriteLine(dllPath);
if (!File.Exists(dllPath))
{
Console.WriteLine($"Could not find the target security DLL to verify with. {definition.EcuName} ({definition.Provider})");
Assert.Fail();
return;
}
Console.WriteLine($"Running QuickTest for {definition.EcuName} ({definition.Provider})");
DllContext context = new DllContext(dllPath, false);
Tuple<uint, int, int> match = context.AccessLevels.Find(x => x.Item1 == definition.AccessLevel);
if (match is null)
{
Console.WriteLine($"DLL does not support access level {definition.AccessLevel}");
Assert.Fail();
return;
}
List<byte[]> TestInput = new List<byte[]>();
foreach (GeneratedByteType byteType in Enum.GetValues(typeof(GeneratedByteType)))
{
TestInput.Add(GenerateBytes(definition.SeedLength, byteType));
}
bool matchesAreValid = true;
foreach (byte[] testRow in TestInput)
{
byte[] outKey = new byte[definition.KeyLength];
if (provider.GenerateKey(testRow, outKey, definition.AccessLevel, definition.Parameters))
{
byte[] dllResult = context.GenerateKeyAuto((uint)definition.AccessLevel, testRow);
matchesAreValid &= outKey.SequenceEqual(dllResult);
Console.WriteLine($"In: {BitUtility.BytesToHex(testRow)} Out: {BitUtility.BytesToHex(outKey)} DLL: {BitUtility.BytesToHex(dllResult)}");
}
}
string testResult = matchesAreValid ? "Passed" : "Failed";
Console.WriteLine($"QuickTest {testResult}");
if (!matchesAreValid)
{
Assert.Fail();
}
}
enum GeneratedByteType
{
Zeroes,
Max,
Ascending,
Descending,
Random,
}
static byte[] GenerateBytes(int size, GeneratedByteType byteType)
{
byte[] output = new byte[size];
if (byteType == GeneratedByteType.Zeroes)
{
// do nothing
}
else if (byteType == GeneratedByteType.Max)
{
for (int i = 0; i < size; i++)
{
output[i] = 0xFF;
}
}
else if (byteType == GeneratedByteType.Ascending)
{
for (int i = 0; i < size; i++)
{
output[i] = (byte)i;
}
}
else if (byteType == GeneratedByteType.Descending)
{
for (int i = 0; i < size; i++)
{
output[i] = (byte)(size - 1 - i);
}
}
else if (byteType == GeneratedByteType.Random)
{
Random r = new Random();
r.NextBytes(output);
}
return output;
}
}
}

View File

@ -0,0 +1,154 @@

namespace VisualUnlockECU
{
partial class MainForm
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm));
this.dgvMain = new System.Windows.Forms.DataGridView();
this.groupBox1 = new System.Windows.Forms.GroupBox();
this.txtFilter = new System.Windows.Forms.TextBox();
this.groupBox2 = new System.Windows.Forms.GroupBox();
this.txtKeyValue = new System.Windows.Forms.TextBox();
this.txtSeedValue = new System.Windows.Forms.TextBox();
((System.ComponentModel.ISupportInitialize)(this.dgvMain)).BeginInit();
this.groupBox1.SuspendLayout();
this.groupBox2.SuspendLayout();
this.SuspendLayout();
//
// dgvMain
//
this.dgvMain.AllowUserToAddRows = false;
this.dgvMain.AllowUserToDeleteRows = false;
this.dgvMain.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.dgvMain.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
this.dgvMain.Location = new System.Drawing.Point(12, 12);
this.dgvMain.MultiSelect = false;
this.dgvMain.Name = "dgvMain";
this.dgvMain.ReadOnly = true;
this.dgvMain.RowTemplate.Height = 25;
this.dgvMain.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect;
this.dgvMain.Size = new System.Drawing.Size(1118, 404);
this.dgvMain.TabIndex = 0;
this.dgvMain.SelectionChanged += new System.EventHandler(this.dgvMain_SelectionChanged);
//
// groupBox1
//
this.groupBox1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.groupBox1.Controls.Add(this.txtFilter);
this.groupBox1.Location = new System.Drawing.Point(12, 422);
this.groupBox1.Name = "groupBox1";
this.groupBox1.Size = new System.Drawing.Size(1118, 56);
this.groupBox1.TabIndex = 1;
this.groupBox1.TabStop = false;
this.groupBox1.Text = "Filter";
//
// txtFilter
//
this.txtFilter.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.txtFilter.Location = new System.Drawing.Point(6, 22);
this.txtFilter.Name = "txtFilter";
this.txtFilter.PlaceholderText = "Filter module by name";
this.txtFilter.Size = new System.Drawing.Size(1106, 23);
this.txtFilter.TabIndex = 0;
this.txtFilter.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
this.txtFilter.TextChanged += new System.EventHandler(this.txtFilter_TextChanged);
//
// groupBox2
//
this.groupBox2.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.groupBox2.Controls.Add(this.txtKeyValue);
this.groupBox2.Controls.Add(this.txtSeedValue);
this.groupBox2.Location = new System.Drawing.Point(12, 484);
this.groupBox2.Name = "groupBox2";
this.groupBox2.Size = new System.Drawing.Size(1118, 80);
this.groupBox2.TabIndex = 2;
this.groupBox2.TabStop = false;
this.groupBox2.Text = "Key Generation";
//
// txtKeyValue
//
this.txtKeyValue.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.txtKeyValue.Location = new System.Drawing.Point(6, 48);
this.txtKeyValue.Name = "txtKeyValue";
this.txtKeyValue.PlaceholderText = "Output seed value (read-only)";
this.txtKeyValue.ReadOnly = true;
this.txtKeyValue.Size = new System.Drawing.Size(1106, 23);
this.txtKeyValue.TabIndex = 1;
this.txtKeyValue.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
//
// txtSeedValue
//
this.txtSeedValue.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.txtSeedValue.Location = new System.Drawing.Point(6, 22);
this.txtSeedValue.Name = "txtSeedValue";
this.txtSeedValue.PlaceholderText = "Enter seed value in hex (e.g. 00 11 22 33)";
this.txtSeedValue.Size = new System.Drawing.Size(1106, 23);
this.txtSeedValue.TabIndex = 0;
this.txtSeedValue.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
this.txtSeedValue.TextChanged += new System.EventHandler(this.txtSeedValue_TextChanged);
//
// MainForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(1142, 576);
this.Controls.Add(this.groupBox2);
this.Controls.Add(this.groupBox1);
this.Controls.Add(this.dgvMain);
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.Name = "MainForm";
this.Text = "UnlockECU";
this.Load += new System.EventHandler(this.MainForm_Load);
((System.ComponentModel.ISupportInitialize)(this.dgvMain)).EndInit();
this.groupBox1.ResumeLayout(false);
this.groupBox1.PerformLayout();
this.groupBox2.ResumeLayout(false);
this.groupBox2.PerformLayout();
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.DataGridView dgvMain;
private System.Windows.Forms.GroupBox groupBox1;
private System.Windows.Forms.TextBox txtFilter;
private System.Windows.Forms.GroupBox groupBox2;
private System.Windows.Forms.TextBox txtKeyValue;
private System.Windows.Forms.TextBox txtSeedValue;
}
}

View File

@ -0,0 +1,167 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using UnlockECU;
namespace VisualUnlockECU
{
public partial class MainForm : Form
{
List<Definition> Definitions;
public MainForm()
{
string definitionJson = File.ReadAllText("db.json");
Definitions = System.Text.Json.JsonSerializer.Deserialize<List<Definition>>(definitionJson);
InitializeComponent();
}
private void MainForm_Load(object sender, EventArgs e)
{
EnableDoubleBuffer(dgvMain, true);
UpdateGrid();
}
public static void EnableDoubleBuffer(DataGridView dgv, bool setting)
{
Type dgvType = dgv.GetType();
PropertyInfo pi = dgvType.GetProperty("DoubleBuffered", BindingFlags.Instance | BindingFlags.NonPublic);
pi.SetValue(dgv, setting, null);
}
private void UpdateGrid(string filter = "")
{
DataTable dt = new DataTable();
dt.Columns.Add("Index");
dt.Columns.Add("Name");
dt.Columns.Add("Origin");
dt.Columns.Add("Level");
dt.Columns.Add("Seed Size");
dt.Columns.Add("Key Size");
dt.Columns.Add("Security Provider");
List<SecurityProvider> providers = SecurityProvider.GetSecurityProviders();
for (int i = 0; i < Definitions.Count; i++)
{
Definition definition = Definitions[i];
if (providers.Find(x => x.GetProviderName() == definition.Provider) is null)
{
continue;
}
if (!definition.Origin.ToLower().Contains(filter.ToLower()))
{
continue;
}
dt.Rows.Add(new string[] {
i.ToString(),
definition.EcuName,
definition.Origin,
definition.AccessLevel.ToString(),
definition.SeedLength.ToString(),
definition.KeyLength.ToString(),
definition.Provider
});
}
dgvMain.DataSource = dt;
dgvMain.Columns[0].Visible = false;
dgvMain.Columns[1].AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells;
dgvMain.Columns[2].AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells;
dgvMain.Columns[3].AutoSizeMode = DataGridViewAutoSizeColumnMode.DisplayedCells;
dgvMain.Columns[4].AutoSizeMode = DataGridViewAutoSizeColumnMode.DisplayedCells;
dgvMain.Columns[5].AutoSizeMode = DataGridViewAutoSizeColumnMode.DisplayedCells;
dgvMain.Columns[6].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
}
private void txtFilter_TextChanged(object sender, EventArgs e)
{
UpdateGrid(txtFilter.Text);
}
private void txtSeedValue_TextChanged(object sender, EventArgs e)
{
TryRefreshKey();
}
private void dgvMain_SelectionChanged(object sender, EventArgs e)
{
TryRefreshKey();
}
public void TryRefreshKey()
{
bool validHex = true;
string cleanedText = txtSeedValue.Text.Replace(" ", "").Replace("\r", "").Replace("\n", "").Replace("\t", "").Replace("-", "").ToUpper();
if (cleanedText.Length % 2 != 0)
{
validHex = false;
}
if (!System.Text.RegularExpressions.Regex.IsMatch(cleanedText, @"\A\b[0-9a-fA-F]+\b\Z"))
{
validHex = false;
}
if (validHex)
{
byte[] seed = BitUtility.BytesFromHex(cleanedText);
txtSeedValue.BackColor = System.Drawing.SystemColors.Window;
TryGenerateKey(seed);
}
else
{
if (cleanedText.Length == 0)
{
TryGenerateKey(new byte[] { });
}
txtSeedValue.BackColor = System.Drawing.Color.LavenderBlush;
}
}
public void TryGenerateKey(byte[] inByte)
{
if (dgvMain.SelectedRows.Count != 1)
{
txtKeyValue.Text = "Please select a definition first";
return;
}
int selectedIndex = int.Parse(dgvMain.SelectedRows[0].Cells[0].Value.ToString());
Definition definition = Definitions[selectedIndex];
groupBox2.Text = $"Key Generation ({definition})";
if (definition.SeedLength != inByte.Length)
{
txtKeyValue.Text = $"Expecting a {definition.SeedLength}-byte seed. Current length is {inByte.Length}";
return;
}
SecurityProvider provider = SecurityProvider.GetSecurityProviders().Find(x => x.GetProviderName() == definition.Provider);
if (provider is null)
{
txtKeyValue.Text = $"Could not load security provider for {definition.Provider}";
return;
}
byte[] outKey = new byte[definition.KeyLength];
if (provider.GenerateKey(inByte, outKey, definition.AccessLevel, definition.Parameters))
{
txtKeyValue.Text = BitUtility.BytesToHex(outKey, true);
}
else
{
txtKeyValue.Text = $"Key generation was unsuccessful ({definition.Provider})";
return;
}
}
}
}

View File

@ -0,0 +1,85 @@
<root>
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Drawing.Common" name="System.Drawing.Common, Version=5.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51" />
<data name="$this.Icon" type="System.Drawing.Icon, System.Drawing.Common" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAQAQAAAAAAAAAAAAAAAAAAAAA
AAA+ncp7NpbR5TOQzOsyi8vtO5XChwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAQqzhzcTr9/9/4fb/n+b3/zKKye86kLqRAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAESw4//G9Pv/Q9bx/0jb9f+C4fX/MYjI8DmNt5gAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAABEsOPyu+/6/znR8f8oxe7/Ttz2/4Xi9/8yi8ruOYu1mwAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAARLDj//D8/v+w7vr/Q9j0/yjI7v9B1/T/ieL3/zKLy+04iLKjAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAESw45ZEsOP/RLDj/6vq+f9O2PP/K8nv/z3W8/+K4ff/MorK7jOD
uNwpfdb/LIXY/zaOwcsAAAAAAAAAAAAAAAAAAAAAAAAAAESw4//x/P7/u/H7/3vk9v8o0vD/N9T1/4Pg
9v8+qeP/oPP8/6n1/P8rgtf/NYu/zwAAAAAAAAAAAAAAAAAAAABEsOOWRLDj/0Wy4/92xer/rO76/znW
8v9N2/X/ZeT3/zzO8v8yye//he/7/yuB1/81iLvUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACRLDjlm/E
6v+A5ff/PdHx/13b9f9p3/b/UNfz/zTN7/+F7/v/KX/W/zSGutgAAAAAAAAAAAAAAAAAAAAAAAAAAESw
4//V9/z/ief4/37k9/9+5Pf/fuT3/4Ll9/9H1vL/OM7w/671/P8pfNb/AAAAAAAAAAAAAAAAAAAAAAAA
AABEsOP/vvL7/37k9/9+5Pf/geX3/5Tp+P+88fv/i9rz/0nd9f/B+P3/MJDa/wAAAAAAAAAAAAAAAAAA
AAAAAAAARLDj/974/P+N5/j/fuT3/5Tp+P+86fj/RLDj/0Ks4//u/P7/Mpjd/zmVyL8AAAAAAAAAAAAA
AAAAAAAAAAAAAESw45ZEsOP/zvX8/43n+P+h7Pn/RLDj/0Sw4///////OaHf/zmVyL8AAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAARLDjlkSw4//O9fz/nuv5/77y+//+////RLDj/0Kr35wAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABEsOOWRLDj/974/P/e+Pz/RLDj/0Sw45YAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAESw45ZEsOP/RLDj/0Sw45YAAAAAAAAAAAAA
AAAAAAAAB/8AAAP/AAAB/wAAAP8AAAB/AAAABwAAwAMAAMABAAD4AAAA+AAAAPgAAAD4AAAA+AEAAPwD
AAD+BwAA/w8AAA==
</value>
</data>
</root>

View File

@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace VisualUnlockECU
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.SetHighDpiMode(HighDpiMode.SystemAware);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm());
}
}
}

View File

@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net5.0-windows</TargetFramework>
<UseWindowsForms>true</UseWindowsForms>
<ApplicationIcon>key.ico</ApplicationIcon>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\UnlockECU\UnlockECU.csproj" />
</ItemGroup>
</Project>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 269 KiB

BIN
docs/resources/demo.mp4 Normal file

Binary file not shown.

BIN
docs/resources/header.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB