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