Skip to content

Commit 448c9c8

Browse files
committed
Initial commit
1 parent 065c8b4 commit 448c9c8

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+2606
-0
lines changed

README.md

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
# UnlockECU
2+
3+
![Header Image](https://raw.githubusercontent.com/jglim/UnlockECU/main/docs/resources/header.png)
4+
5+
Free, open-source ECU seed-key unlocking tool.
6+
7+
## Getting started
8+
9+
Download and unarchive the application from the [Releases](https://github.com/jglim/UnlockECU/releases/) page, then run the main application `VisualUnlockECU.exe`.
10+
11+
Ensure that you have *.NET Desktop Runtime 5.0.0*. , available from [here](https://dotnet.microsoft.com/download/dotnet/5.0).
12+
13+
## License
14+
15+
MIT
16+
17+
Icon from [http://www.famfamfam.com/lab/icons/silk/](http://www.famfamfam.com/lab/icons/silk/)
18+
19+
Excluding the icon, this application **does not include or require copyrighted or proprietary files**. Security functions and definitions have been reverse-engineered and reimplemented.
20+
21+
*When interacting with this repository (PR, issues, comments), please avoid including copyrighted/proprietary files, as they will be removed without notice.*
22+
23+
## Features
24+
25+
- 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`.
26+
- Security functions are completely reverse engineered and re-implemented in C#.
27+
- The project is unencumbered by proprietary binary blobs, and can be shared freely without legal issues.
28+
29+
## Demo
30+
31+
![https://raw.githubusercontent.com/jglim/UnlockECU/main/docs/resources/demo.mp4](https://raw.githubusercontent.com/jglim/UnlockECU/main/docs/resources/demo-thumb.png)
32+
33+
## Adding definitions
34+
35+
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.
36+
37+
Here is an example of a definition:
38+
39+
```
40+
{
41+
"EcuName": "ME97",
42+
"AccessLevel": 5,
43+
"SeedLength": 2,
44+
"KeyLength": 2,
45+
"Provider": "PowertrainBoschContiSecurityAlgo2",
46+
"Origin": "ME97_ME97_13_10_01",
47+
"Parameters": [
48+
{
49+
"Key": "Table",
50+
"Value": "37C1A8179AE3745B",
51+
"DataType": "ByteArray"
52+
},
53+
{
54+
"Key": "uwMasc",
55+
"Value": "4108",
56+
"DataType": "ByteArray"
57+
}
58+
]
59+
}
60+
```
61+
62+
Currently, these security providers are available:
63+
64+
- DaimlerStandardSecurityAlgo
65+
- DaimlerStandardSecurityAlgoMod
66+
- DaimlerStandardSecurityAlgoRefG
67+
- DRVU_PROF
68+
- EDIFF290
69+
- EsLibEd25519
70+
- ESPSecurityAlgoLevel1
71+
- MarquardtSecurityAlgo
72+
- OCM172
73+
- PowertrainBoschContiSecurityAlgo1
74+
- PowertrainBoschContiSecurityAlgo2
75+
- PowertrainDelphiSecurityAlgo
76+
- PowertrainSecurityAlgo
77+
- PowertrainSecurityAlgo2
78+
- PowertrainSecurityAlgoNFZ
79+
- RBTM
80+
- RDU222
81+
- RVC222_MPC222_FCW246_LRR3
82+
- SWSP177
83+
84+
The definitions file `db.json` should be found alongside the application's main binary.
85+
86+
## Notes
87+
88+
- 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`.
89+
- 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`).
90+
- 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).
91+
- There are ECUs that share the same seed-key function. For example, `CRD3` and `CRD3S2` appear to share the same function as `CRD3NFZ`.
92+
- The core of this project is a "portable" .NET 5 class library which can be reused on other platforms.
93+
- 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.
94+
- `DaimlerStandardSecurityAlgo` is usually used for firmware flashing, and might not unlock other capabilities such as variant-coding.
95+
96+
## Contributing
97+
98+
Contributions in adding security providers and definitions are welcome.

UnlockECU/UnlockECU.sln

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio Version 16
4+
VisualStudioVersion = 16.0.30711.63
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnlockECU", "UnlockECU\UnlockECU.csproj", "{9DBD6CB7-68B3-4801-95B7-D760C49E2A83}"
7+
EndProject
8+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VisualUnlockECU", "VisualUnlockECU\VisualUnlockECU.csproj", "{FA2EF721-9B46-4EB9-8444-0ABAF6F5185A}"
9+
EndProject
10+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnlockECUTests", "UnlockECUTests\UnlockECUTests.csproj", "{6252DB08-EFD2-4889-B97F-090726D9DC9C}"
11+
EndProject
12+
Global
13+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
14+
Debug|Any CPU = Debug|Any CPU
15+
Release|Any CPU = Release|Any CPU
16+
EndGlobalSection
17+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
18+
{9DBD6CB7-68B3-4801-95B7-D760C49E2A83}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
19+
{9DBD6CB7-68B3-4801-95B7-D760C49E2A83}.Debug|Any CPU.Build.0 = Debug|Any CPU
20+
{9DBD6CB7-68B3-4801-95B7-D760C49E2A83}.Release|Any CPU.ActiveCfg = Release|Any CPU
21+
{9DBD6CB7-68B3-4801-95B7-D760C49E2A83}.Release|Any CPU.Build.0 = Release|Any CPU
22+
{FA2EF721-9B46-4EB9-8444-0ABAF6F5185A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
23+
{FA2EF721-9B46-4EB9-8444-0ABAF6F5185A}.Debug|Any CPU.Build.0 = Debug|Any CPU
24+
{FA2EF721-9B46-4EB9-8444-0ABAF6F5185A}.Release|Any CPU.ActiveCfg = Release|Any CPU
25+
{FA2EF721-9B46-4EB9-8444-0ABAF6F5185A}.Release|Any CPU.Build.0 = Release|Any CPU
26+
{6252DB08-EFD2-4889-B97F-090726D9DC9C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
27+
{6252DB08-EFD2-4889-B97F-090726D9DC9C}.Debug|Any CPU.Build.0 = Debug|Any CPU
28+
{6252DB08-EFD2-4889-B97F-090726D9DC9C}.Release|Any CPU.ActiveCfg = Release|Any CPU
29+
{6252DB08-EFD2-4889-B97F-090726D9DC9C}.Release|Any CPU.Build.0 = Release|Any CPU
30+
EndGlobalSection
31+
GlobalSection(SolutionProperties) = preSolution
32+
HideSolutionNode = FALSE
33+
EndGlobalSection
34+
GlobalSection(ExtensibilityGlobals) = postSolution
35+
SolutionGuid = {CC1E2894-FC7F-474F-8A14-00529B7EF07F}
36+
EndGlobalSection
37+
EndGlobal

UnlockECU/UnlockECU/BitUtility.cs

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
7+
namespace UnlockECU
8+
{
9+
/// <summary>
10+
/// Utilities for bit and byte operations.
11+
/// (Frequently copied-and-pasted across my projects)
12+
/// </summary>
13+
public class BitUtility
14+
{
15+
/// <summary>
16+
/// Sets all values in an array of bytes to a specific value
17+
/// </summary>
18+
/// <param name="value">Value to set byte array to</param>
19+
/// <param name="buf">Target byte array buffer</param>
20+
public static void Memset(byte value, byte[] buf)
21+
{
22+
for (int i = 0; i < buf.Length; i++)
23+
{
24+
buf[i] = value;
25+
}
26+
}
27+
// Internally used by BytesFromHex
28+
private static byte[] StringToByteArrayFastest(string hex)
29+
{
30+
// see https://stackoverflow.com/questions/321370/how-can-i-convert-a-hex-string-to-a-byte-array
31+
if (hex.Length % 2 == 1)
32+
{
33+
throw new Exception("The binary key cannot have an odd number of digits");
34+
}
35+
byte[] arr = new byte[hex.Length >> 1];
36+
for (int i = 0; i < hex.Length >> 1; ++i)
37+
{
38+
arr[i] = (byte)((GetHexValue(hex[i << 1]) << 4) + (GetHexValue(hex[(i << 1) + 1])));
39+
}
40+
return arr;
41+
}
42+
// Internally used by StringToByteArrayFastest
43+
private static int GetHexValue(char hex)
44+
{
45+
int val = (int)hex;
46+
return val - (val < 58 ? 48 : 55);
47+
}
48+
/// <summary>
49+
/// Converts an array of bytes into its hex-string equivalent
50+
/// </summary>
51+
/// <param name="inBytes">Input byte array</param>
52+
/// <param name="spacedOut">Option to add spaces between individual bytes</param>
53+
/// <returns>Hex-string based on the input byte array</returns>
54+
public static string BytesToHex(byte[] inBytes, bool spacedOut = false)
55+
{
56+
return BitConverter.ToString(inBytes).Replace("-", spacedOut ? " " : "");
57+
}
58+
59+
/// <summary>
60+
/// Converts an array of bytes into a printable hex-string
61+
/// </summary>
62+
/// <param name="hexString">Input hex-string to convert into a byte array</param>
63+
/// <returns>Byte array based on the input hex-string</returns>
64+
public static byte[] BytesFromHex(string hexString)
65+
{
66+
return StringToByteArrayFastest(hexString.Replace(" ", ""));
67+
}
68+
69+
/// <summary>
70+
/// Resize a smaller array of bytes to a larger array. The padding bytes will be 0.
71+
/// </summary>
72+
/// <param name="inData">Input byte array</param>
73+
/// <param name="finalSize">New size for the input array</param>
74+
/// <returns>Resized byte array</returns>
75+
public static byte[] PadBytes(byte[] inData, int finalSize)
76+
{
77+
if (inData.Length > finalSize)
78+
{
79+
return inData;
80+
}
81+
byte[] result = new byte[finalSize];
82+
Buffer.BlockCopy(inData, 0, result, 0, inData.Length);
83+
return result;
84+
}
85+
86+
}
87+
}

UnlockECU/UnlockECU/Definition.cs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
7+
namespace UnlockECU
8+
{
9+
public class Definition
10+
{
11+
public string EcuName { get; set; }
12+
public int AccessLevel { get; set; }
13+
public int SeedLength { get; set; }
14+
public int KeyLength { get; set; }
15+
public string Provider { get; set; }
16+
public string Origin { get; set; }
17+
public List<Parameter> Parameters { get; set; }
18+
19+
[System.Text.Json.Serialization.JsonIgnore]
20+
public string ParamParent;
21+
22+
public override string ToString()
23+
{
24+
StringBuilder sb = new StringBuilder($"Name: {EcuName} ({Origin}), Level: {AccessLevel}, Seed Length: {SeedLength}, Key Length: {KeyLength}, Provider: {Provider}");
25+
return sb.ToString();
26+
/*
27+
// uncomment and remove the return above to print verbose parameter data
28+
foreach (Parameter row in Parameters)
29+
{
30+
sb.AppendLine();
31+
sb.Append($"Parameter[{row.Key}] ({row.DataType}) : {row.Value}");
32+
}
33+
return sb.ToString();
34+
*/
35+
}
36+
37+
}
38+
}

UnlockECU/UnlockECU/Parameter.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
7+
namespace UnlockECU
8+
{
9+
public class Parameter
10+
{
11+
public string Key { get; set; }
12+
public string Value { get; set; }
13+
public string DataType { get; set; }
14+
[System.Text.Json.Serialization.JsonIgnore]
15+
public int AccessLevel = -1;
16+
}
17+
}

UnlockECU/UnlockECU/Program.cs

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
using System;
2+
using System.IO;
3+
using System.Collections.Generic;
4+
using System.Linq;
5+
6+
namespace UnlockECU
7+
{
8+
class Program
9+
{
10+
11+
static void Main(string[] args)
12+
{
13+
Console.WriteLine("UnlockECU (Running as console application)");
14+
15+
string definitionJson = File.ReadAllText("db.json");
16+
17+
List<Definition> definitions = System.Text.Json.JsonSerializer.Deserialize<List<Definition>>(definitionJson);
18+
List<SecurityProvider> providers = SecurityProvider.GetSecurityProviders();
19+
20+
ReverseKey(BitUtility.BytesFromHex("BA00D268 972D452D"), BitUtility.BytesFromHex("BE515D46")); // reverse key 0x3F9C71A5 (CRD3S2SEC9A)
21+
22+
Console.ReadKey();
23+
}
24+
25+
static void ReverseKey(byte[] inSeed, byte[] outKeyBytes)
26+
{
27+
long kA = 1103515245L;
28+
long kC = 12345L;
29+
30+
long seedA = BytesToInt(inSeed, Endian.Big, 0);
31+
long seedB = BytesToInt(inSeed, Endian.Big, 4);
32+
33+
long outKey = BytesToInt(outKeyBytes, Endian.Big);
34+
35+
long intermediate1 = kA * seedA + kC;
36+
long intermediate2 = kA * seedB + kC;
37+
38+
long xorA = intermediate1 ^ intermediate2;
39+
40+
long reverseCryptoKey = (xorA ^ outKey) & 0xFFFFFFFF; // reverse key 0x3F9C71A5 (CRD3S2SEC9A)
41+
42+
Console.WriteLine($"Reversed DSSA key: {reverseCryptoKey:X}");
43+
}
44+
45+
public enum Endian
46+
{
47+
Big,
48+
Little,
49+
}
50+
public static uint BytesToInt(byte[] inBytes, Endian endian, int offset = 0)
51+
{
52+
uint result = 0;
53+
if (endian == Endian.Big)
54+
{
55+
result |= (uint)inBytes[offset++] << 24;
56+
result |= (uint)inBytes[offset++] << 16;
57+
result |= (uint)inBytes[offset++] << 8;
58+
result |= (uint)inBytes[offset++] << 0;
59+
}
60+
else
61+
{
62+
result |= (uint)inBytes[offset++] << 0;
63+
result |= (uint)inBytes[offset++] << 8;
64+
result |= (uint)inBytes[offset++] << 16;
65+
result |= (uint)inBytes[offset++] << 24;
66+
}
67+
return result;
68+
}
69+
}
70+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
7+
namespace UnlockECU
8+
{
9+
/// <summary>
10+
/// Simpler version of DaimlerStandardSecurityAlgo with custom kA, kC, and no blockB
11+
/// </summary>
12+
class DRVU_PROF : SecurityProvider
13+
{
14+
public override bool GenerateKey(byte[] inSeed, byte[] outKey, int accessLevel, List<Parameter> parameters)
15+
{
16+
byte[] cryptoKeyBytes = GetParameterBytearray(parameters, "KeyConst");
17+
uint cryptoKey = BytesToInt(cryptoKeyBytes, Endian.Big);
18+
19+
long kA = 258028488L;
20+
long kC = 1583629211L;
21+
22+
if ((inSeed.Length != 4) || (outKey.Length != 4))
23+
{
24+
return false;
25+
}
26+
27+
long seedA = BytesToInt(inSeed, Endian.Big, 0);
28+
29+
long intermediate1 = kA * seedA + kC;
30+
long seedKey = intermediate1 % cryptoKey;
31+
32+
IntToBytes((uint)seedKey, outKey, Endian.Big);
33+
return true;
34+
}
35+
36+
public override string GetProviderName()
37+
{
38+
return "DRVU_PROF";
39+
}
40+
}
41+
}

0 commit comments

Comments
 (0)