mirror of https://github.com/rusefi/UnlockECU.git
Initial commit
This commit is contained in:
parent
065c8b40eb
commit
448c9c809a
|
@ -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.
|
|
@ -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
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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>
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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>
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -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>
|
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 |
Binary file not shown.
Binary file not shown.
After Width: | Height: | Size: 47 KiB |
Loading…
Reference in New Issue