Open jnewb1 opened 1 year ago
Here's a seed/key exchange i captured:
message as hex | description |
---|---|
0227030000000000 | Seed request sent by diag tool |
10126703f8ee1609 | Seed First Frame, size 16, data 0x026703f8ee1609 (seed response, packet 0: f8ee1609) |
21ec9ab91c835806 | Seed Consecutive frame, packet 1: ec9ab91c835806 |
220fdebc3e060000 | Seed Consecutive frame, packet 2: fdebc3e06 |
FULL SEED | f8ee1609ec9ab91c8358060fdebc3e06 |
10122704aa21c011 | Key First Frame, size 16, data 0x022704aa21c011 (key response, packet 0: aa21c011) |
217cf3dcb6bc1f7a | Key Consecutive frame, packet 1: 7cf3dcb6bc1f7a |
227f90ee78ca0000 | Key Consecutive frame, packet 2: 7f90ee78ca |
FULL KEY | aa21c0117cf3dcb6bc1f7a7f90ee78ca |
0267040000000000 | Unlock success |
Hello and welcome, Justin,
I took a look at the library, and while I was not able to follow the calls, I have a hunch that it's an AES-based algo.
Here are some notes from looking at CMD_FhiCan, I hope they might come in helpful:
0x1000D8C0 : send channel message; describes what the client is writing to the ECU
For example, CMD_TwoPhasesOfCertification
calls external functions for unlock commands, then appears to send 31 01 02 00
in the function at 0x10029470. If this function also handles the 27 xx
messages, it would be a helpful debugging lead for working backwards to find the algo.
Possibly CMD_SecurityAccessAES_AB
@ 0x10007400, or CMD_SecurityAccess2018CY1
@ 0x10007990?
Expected seed length is 16, key length is 16 which matches captured frames.
Seems to handle 2702/2703, 2704/2705
Neither have direct xrefs, but they could be imported elsewhere or called indirectly through GetProcAddress
AES initialization is done at 0x1004A610, and both aes-based algos call this common function
If a breakpoint at the initialization is hit, the key can be likely plucked out from the memory.
There also appears to be a bunch of non-aes algos (CMD_SecurityAccess_Ocpt
, CMD_SecurityAccess
) that do not match your traces as they take a 4 byte seed and return a 4 byte key.
27 03 > seed request level 3 > 0000000000 67 03 >seed > f8ee1609 21 > ec9ab91c835806 22 > 0fdebc3e060000 27 04 > seed request level 4 > aa21c011 ...... 67 04 > 0000000000
Very similar to daimler
Thank you for looking, that is super helpful!
Possibly
CMD_SecurityAccessAES_AB
@ 0x10007400, orCMD_SecurityAccess2018CY1
@ 0x10007990? Expected seed length is 16, key length is 16 which matches captured frames. Seems to handle 2702/2703, 2704/2705 Neither have direct xrefs, but they could be imported elsewhere or called indirectly through GetProcAddress
I'll look for references to those functions. Whole SSM4 program has like 25 dlls, but I can try and find it
If a breakpoint at the initialization is hit, the key can be likely plucked out from the memory.
Do you think its a constant AES key for all cars, or does it somehow use the ECU address in a lookup table? I can hookup the debugger and try and capture that key, but I'm not super experienced in IDA.
There also appears to be a bunch of non-aes algos (
CMD_SecurityAccess_Ocpt
,CMD_SecurityAccess
) that do not match your traces as they take a 4 byte seed and return a 4 byte key.
Older Subarus have much shorter seed/keys, so that is most likely what that is for. This seed/key pair is from a 2018 crosstrek
0x019797 0x057028
The keys should be variant-specific. Both aes-based algos begin by initiating a 22 xx xx
ReadDataByIdentifier request (0x1000DF60), presumably to fetch the ECU variant. They will then fetch the variant-specific aes key via ApiTable::CVsmRsKeyTableAES
in VsmDatabase.dll.
(Before digging deeper into the key tables, it might be good to confirm if this is indeed the correct algo first.)
Got it w.r.t. older seed/keys, thanks! Incidentally, those also seem variant-specific, using ApiTable::CVsmRsKeyTable
this time.
@Feezex It does look very much like UDS
ECU << `27 03`
ECU >> `67 03 F8 EE 16 09 EC 9A B9 1C 83 58 06 0F DE BC 3E 06`
ECU << `27 04 AA 21 C0 11 7C F3 DC B6 BC 1F 7A 7F 90 EE 78 CA`
ECU >> `67 04`
looks like CMD_SecurityAccess2018CY1 is the one. have you used the trace recorder in ida? I'd like to be able to just record a full trace of the exchange and send it to you.
The keys should be variant-specific. Both aes-based algos begin by initiating a 22 xx xx ReadDataByIdentifier request (0x1000DF60), presumably to fetch the ECU variant. They will then fetch the variant-specific aes key via ApiTable::CVsmRsKeyTableAES in VsmDatabase.dll.
Lots of RDBI. Here is one of them that is long, and also has two different requests with different length responses (back to back)
0x0322f18200000000
RDBI REQUEST: 0x00f1
0x100d62f182000065
0x219a001f40203100
RDBI RESPONSE: 0x00f1 0x820000659a001f40203100 (11)
0x0322f19700000000
RDBI REQUEST: 0x00f1
0x102362f197457965
0x2153696768742053
0x22797374656d2020
0x2320202020202020
0x2420202020202020
0x2520000000000000
RDBI RESPONSE: 0x00f1 0x9745796553696768742053797374656d2020202020202020202020202020202020 (33)
Heres another one?
0x0322101e00000000
RDBI REQUEST: 0x0010
0x100962101e052500
0x2100026400000000
RDBI RESPONSE: 0x0010 0x1e052500000264 (7)
Here's a list of unique DID's that appear before the seed request:
0x0002 - 2 byte response
0x0020 - 2 byte response
0x0010 - 7 byte response
0x00ff - 5 byte response
0x00f1 - 33 or 11 byte response
0x0022, 0x0024, 0x0030, 0x0031, 0x0040 - 0x0044 - 5 byte response (probably just diagnostic data)
there is also an unanswered request for 0x0070?
The IDA version that I am running is rather dated, unfortunately I don't think trace recorder is going to work out.
Instead, can you place a breakpoint during the key initialization (see below) and go through the security access?
When the breakpoint is hit, read the ESI register which contains a pointer to the key. Read the process memory at the location specified by ESI; the first 16 bytes should be the raw, un-expanded aes key.
I haven't seen an aes IV around, if the vendor is using regular, unmodified aes, I would think that this is aes-ecb.
esi: 0x7692e7932f23a901568ddfa5ff580625
I haven't been able to get anything out of aes-ecb. There could be a few reasons,
testing aeskey 76 92 E7 93 2F 23 A9 01 56 8D DF A5 FF 58 06 25
enc 96 EC 1D AE 8C 2B BE 76 8D 41 FE 00 BE A2 05 5B
dec 50 94 43 D2 69 E9 E8 BF 34 78 EB 4D 5C 5B 0A AA
testing aeskey 25 06 58 FF A5 DF 8D 56 01 A9 23 2F 93 E7 92 76 (reversed)
enc 18 DD C1 E4 45 12 AE B7 85 82 9B 1A F7 B8 4A 98
dec C3 D8 41 38 D3 D1 9B 9B 67 5B 30 B0 0F CA 34 03
I ran it several times and got the same value every time, even after restarting the car. But I also get the same result when trying to query any other ECU on the car, so unless they all share the same key, that might be something else.
i wrote something to test a bunch of possible things that could be flipped, none of them worked though
from Crypto.Cipher import AES
seed_key_gen2 = [
(0xf8ee1609ec9ab91c8358060fdebc3e06,0xaa21c0117cf3dcb6bc1f7a7f90ee78ca),
(0x63c1fbe1248b0ab0d8a0fb8606e37b13,0x56da42fe1e9c6f936fda699ed253a2d7),
(0xd13dae85a592bb91672da8fe8315a1a3,0x8b5148b5ff3a00db72117760bc6e43c4),
(0xfeff4608b40e174f9fff21b3dc7fb46d,0xf5f7466d8e316f640e381fad5532ec14),
(0x02d3766aadc6621e12d50ddc2adc5038,0xdb2a108b77e977f8d7b0871a6eceb4b8),
(0xc01ef055314fb71fc0794b3440ec9577,0x0a6fe147394f74fc36655e2d99b1eb4a),
(0xecc763b6f9d31719eabcd5a6e3e1319e,0x88a2eb332caf13e138520424fbb46891),
(0x215c2719701b6de846e7461987134079,0x67b339ff36495fe6c8ab25efb82fb756),
]
seed_key_gen1 = [
(0x19797, 0x57028)
]
possible_secret_keys = [
0x7692e7932f23a901568ddfa5ff580625,
0x558bec8b4d0885c9750633c05dc20400,
0xd8d012189079455fe80dc30fb0c7f70c
]
possible_methods = [
AES.MODE_CBC,
AES.MODE_ECB,
AES.MODE_CFB,
AES.MODE_OFB,
AES.MODE_CTR,
#AES.MODE_OPENPGP,
AES.MODE_CCM,
AES.MODE_EAX,
#AES.MODE_SIV,
AES.MODE_GCM,
AES.MODE_OCB
]
def unlock_gen2(seed, secret_key, method, secret_key_byteorder, seed_byteorder, key_byteorder, seed_as_key=False, encrypt=False):
secret_key = secret_key.to_bytes(16, byteorder=secret_key_byteorder)
seed = seed.to_bytes(16, byteorder=seed_byteorder)
if seed_as_key:
cipher = AES.new(seed, method)
if encrypt:
key = cipher.encrypt(secret_key)
else:
key = cipher.decrypt(secret_key)
else:
cipher = AES.new(secret_key, method)
if encrypt:
key = cipher.encrypt(seed)
else:
key = cipher.decrypt(seed)
key = int.from_bytes(key, byteorder=key_byteorder)
return key
possible_byteorder = ["little", "big"]
booleans = [True, False]
max_error = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
best_error = max_error
best_set = None
if __name__ == "__main__":
for pair_to_use in range(len(seed_key_gen2)):
for method in possible_methods:
for secret_key in possible_secret_keys:
for seed_byteorder in possible_byteorder:
for secret_key_byteorder in possible_byteorder:
for key_byteorder in possible_byteorder:
for seed_as_key in booleans:
for encrypt in booleans:
seed = seed_key_gen2[pair_to_use][0]
real_key = seed_key_gen2[pair_to_use][1]
key = unlock_gen2(seed, secret_key, method, secret_key_byteorder, seed_byteorder, key_byteorder, seed_as_key, encrypt)
if key == real_key:
raise Exception("LFG!!")
error = abs(real_key - key)
if error < best_error:
best_error = error
best_set = (pair_to_use, method, secret_key, seed_byteorder, secret_key_byteorder, key_byteorder, seed_as_key, encrypt, key, real_key)
print("no bueno...")
print(f"{max_error:032x}")
print(f"{best_error:032x}")
print(best_set)
The aes looks like vanilla ECB. I've ripped the aes-specific code out from the dll into a single assembly file. aes.zip
This builds with masm32 V11 (masm32v11r):
c:\masm32\bin\ml /c /coff /Cp aes_dbg.asm
c:\masm32\bin\link /SUBSYSTEM:CONSOLE /LIBPATH:c:\masm32\lib aes_dbg.obj
>aes_dbg.exe
Cipher : 96EC1DAE8C2BBE768D41FE00BEA2055B
Plain : F8EE1609EC9AB91C8358060FDEBC3E06
Key : 7692E7932F23A901568DDFA5FF580625
The cipher 96EC1DAE8C2BBE768D41FE00BEA2055B
matches my aes-ecb encrypt test above. The ripped instructions are almost fully identical to those in the dll, down to the register level. I'm guessing that there might have been a hiccup during the key dumping process.
Could you perhaps try this instead:
I ran it a couple times:
ESI: 7692e7932f23a901568ddfa5ff580625
ECX: 8759e486dcf72a0659c7b07ba523bae9
EDI: 7692e7932f23a901568ddfa5ff5806251dfdd88532de71846453ae219b0ba804343f2a9106e15b1562b2f534f9b95d3066732e086092751d02208029fb99dd1980b2fa07e0208f1ae2000f331999d22a7e071fd39e2790c97c279ffa65be4dd0f0e46f9e6ec3ff5712e460ad775a2d7d0e3c906b60ff6f3c721b0f91054122ec0daf5e006d50313c1f4b3ead1a0a1c417133dda21c63ec9e0328d2331922ce72d4b89d76c8db71e8cbf3a3dbd2d16da9000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000
ESI: 7692e7932f23a901568ddfa5ff580625
ECX: 3cf9b9c8a1027379c84720dee7ded657
EDI: 7692e7932f23a901568ddfa5ff5806251dfdd88532de71846453ae219b0ba804343f2a9106e15b1562b2f534f9b95d3066732e086092751d02208029fb99dd1980b2fa07e0208f1ae2000f331999d22a7e071fd39e2790c97c279ffa65be4dd0f0e46f9e6ec3ff5712e460ad775a2d7d0e3c906b60ff6f3c721b0f91054122ec0daf5e006d50313c1f4b3ead1a0a1c417133dda21c63ec9e0328d2331922ce72d4b89d76c8db71e8cbf3a3dbd2d16da9000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000
ESI: 7692e7932f23a901568ddfa5ff580625
ECX: 4172e4fedcaa0ee05f4a7d3b5587dce3
EDI: 7692e7932f23a901568ddfa5ff5806251dfdd88532de71846453ae219b0ba804343f2a9106e15b1562b2f534f9b95d3066732e086092751d02208029fb99dd1980b2fa07e0208f1ae2000f331999d22a7e071fd39e2790c97c279ffa65be4dd0f0e46f9e6ec3ff5712e460ad775a2d7d0e3c906b60ff6f3c721b0f91054122ec0daf5e006d50313c1f4b3ead1a0a1c417133dda21c63ec9e0328d2331922ce72d4b89d76c8db71e8cbf3a3dbd2d16da9000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000
ESI: 7692e7932f23a901568ddfa5ff580625
ECX: e40df21a14d989053877f21361fd185c
EDI: 7692e7932f23a901568ddfa5ff5806251dfdd88532de71846453ae219b0ba804343f2a9106e15b1562b2f534f9b95d3066732e086092751d02208029fb99dd1980b2fa07e0208f1ae2000f331999d22a7e071fd39e2790c97c279ffa65be4dd0f0e46f9e6ec3ff5712e460ad775a2d7d0e3c906b60ff6f3c721b0f91054122ec0daf5e006d50313c1f4b3ead1a0a1c417133dda21c63ec9e0328d2331922ce72d4b89d76c8db71e8cbf3a3dbd2d16da9000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000
Perhaps you should also verify my methodology: (I tried with true and false for the use_dbg flag, both got same results)
Breakpoint at 77E1A: (push esi)
addr = get_reg_value("esi")
key = get_bytes(addr, 16, False)
print(f"ESI: {key.hex()}")
Breakpoint at 77E36: (call unk_5F4BA8C0)
addr = get_reg_value("ecx")
seed = get_bytes(addr, 16, False)
print(f"ECX: {seed.hex()}")
addr = get_reg_value("edi")
expanded_key = get_bytes(addr, 252, False)
print(f"EDI: {expanded_key.hex()}")
Aha! I found the key!
33e63ca0431153460c18f1064c70fe41
Turns out the authentication is done twice, the second time gets the correct key. Maybe the first request is to some other module in the car? We will probably have to figure that out.
def unlock_gen2(seed):
secret_key = 0x33e63ca0431153460c18f1064c70fe41
secret_key = secret_key.to_bytes(16, byteorder='big')
seed = seed.to_bytes(16, byteorder='big')
cipher = AES.new(secret_key, AES.MODE_ECB)
key = cipher.encrypt(seed)
return int.from_bytes(key, byteorder='big')
Your methodology looks good. ESI (aes key) and the first 16 bytes of EDI (expanded key) should point to different address and still remain identical, which is correct in your captures.
I ran those values with the ripped code from the DLL, and the expanded key fully matches your captures too, so at least that isn't a worry.
Here's a few more breakpoint suggestions to validate that the aes bits are working. The new additions include capturing the original seed response from the ecu, as well as the completed key response to the ecu.
Breakpoints, in order:
ESI
-> original key[16]. Should remain at 7692e7932f23a901568ddfa5ff580625
EDI
-> full key schedule[252]; ECX
-> seed component[16]. ECX
- 2 will show the entire message (e.g. 67 0X .. ..
)EDX
-> unlock message[18] (prefixed by 27 0X ..
)Right before I could post, I just saw your reply with the correct key. I'm still sharing the original content to confirm that your methodology is fine. Awesome to hear that you're on the right track!
Right before I could post, I just saw your reply with the correct key. I'm still sharing the original content to confirm that your methodology is fine. Awesome to hear that you're on the right track!
Thank you so much for the help! You are truly incredible.
I will do some more experimenting. I’m curious what the first unlock thing is for, and if that part is required to unlock this ECU.
if you are curious, this is for an open source driving assistant program called openpilot: https://github.com/commaai/openpilot
Glad that I could be of help. Good luck with your work on openpilot; that project does seem very fascinating.
On multiple unlocks, I've seen some ecus require a security access progression, like 27 09, 27 0A
then 27 0B, 27 0C
. If the messages are addressed to the same target, and use different levels, this could be a reason.
I'll find time to add a new subaru aes algo, along with a definition for 33e63ca0431153460c18f1064c70fe41
. Should you happen to figure out more keys e.g. from CVsmRsKeyTable
, I would be excited to hear more about it too.
I just verified that this algorithm does in fact unlock the ECU, and I am able to read the data that I was trying to read from the ECU! To support more than just my car, we will probably need to find more keys from the CVsmRsKeyTable
, so I will let you know when we stumble across those!
I've just added an AES provider in https://github.com/jglim/UnlockECU/commit/c4f693d6e73be3ca1480405c85398e28e8ed47ff , and also dumped the AES keys that I could find; the earlier AES key 7692E7932F23A901568DDFA5FF580625
is present as Subaru_2FEA/Subaru_2FE9. The dumping part is still quite preliminary and I might need a while more to remove the windows-specific CryptAPI bits.
As a follow up to the earlier post, this snippet decrypts the Subaru XML definitions without depending on MS CryptoAPI:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
namespace SubaruXmlDecrypt
{
class Program
{
static readonly string HelpText = "Specify/dragdrop a file or directory onto this application. The file(s) will be decrypted in-place (overwritten)";
static byte[] AesManagedDecrypt(byte[] cipher)
{
byte[] Key = {
0x46, 0x6A, 0x41, 0xAA, 0x5C, 0xE0, 0xFA, 0xF8, 0xF0, 0x2F, 0x15, 0x48, 0xEC, 0xE2, 0xC2, 0xCD,
0x1B, 0x5B, 0x01, 0x84, 0xA9, 0x3F, 0xF7, 0x62, 0xBF, 0x14, 0x65, 0xF8, 0x10, 0xD4, 0x8C, 0x9E,
};
byte[] IV = {
0xA8, 0xB0, 0xC8, 0xC9, 0x6F, 0x9B, 0xAF, 0xB8, 0xBE, 0xC2, 0xC2, 0xA0, 0x89, 0x85, 0xB4, 0x8C,
};
AesManaged aes = new AesManaged();
aes.Mode = CipherMode.CBC;
aes.IV = IV;
aes.Key = Key;
aes.Padding = PaddingMode.PKCS7;
return aes.CreateDecryptor().TransformFinalBlock(cipher, 0, cipher.Length);
}
static void Main(string[] args)
{
if (args.Length != 1)
{
Console.WriteLine($"Expecting one arg, received {args.Length}:");
foreach (var arg in args)
{
Console.WriteLine($"> {arg}");
}
PrintHelp();
}
else
{
string path = args[0];
DecryptFileOrDirectory(path);
}
Console.ReadKey();
}
static void PrintHelp()
{
Console.WriteLine(HelpText);
}
static void DecryptFileOrDirectory(string path)
{
if (Directory.Exists(path))
{
foreach (var file in Directory.GetFiles(path))
{
File.WriteAllBytes(file, AesManagedDecrypt(File.ReadAllBytes(file)));
Console.WriteLine($"Decrypted {file}");
}
Console.WriteLine($"Decrypting all files in {path}");
}
else if (File.Exists(path))
{
File.WriteAllBytes(path, AesManagedDecrypt(File.ReadAllBytes(path)));
Console.WriteLine($"Decrypted {path}");
}
else
{
PrintHelp();
}
}
}
}
The XML files are comprehensive and readable; apart from the AES keys, they also include the "regular" standard security algo key components:
There's also a bunch of "SpecialFunctionKey" credentials which doesn't seem related to ECU unlocking but could come in handy too:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<SpecialFunctionKeyTable Version="18.00a" Brand="SUBARU" EcuId="12022">
<SpecialFunctionKey Id="1" ReplaceId="F_GST" ReplaceType="2" />
<SpecialFunctionKey Id="2" ReplaceId="F_GST" ReplaceType="2" />
<SpecialFunctionKey Id="1" ScreenId="3" FunctionId="902" Keycode="$0000C048" Password="3689" />
<SpecialFunctionKey Id="2" ScreenId="3" FunctionId="902" Keycode="$0000C04A" />
</SpecialFunctionKeyTable>
The XML files are comprehensive and readable; apart from the AES keys, they also include the "regular" standard security algo key components:
What do you think the "Version" field is for? I was looking at trying to dump the ECU ROM, but no luck with the default key. I don't think its the security level, because that's included elsewhere in other ECU's.
<KeyTableAES Version="1" CryptKey="$33,$E6,$3C,$A0,$43,$11,$53,$46,$0C,$18,$F1,$06,$4C,$70,$FE,$41" />
<KeyTableAES Version="16" CryptKey="$A8,$08,$35,$0D,$2B,$AF,$20,$84,$F0,$A5,$C1,$07,$90,$A5,$D5,$06" />
<KeyTableAES Version="32" CryptKey="$29,$5A,$6E,$BA,$EC,$78,$D7,$74,$D8,$AA,$C1,$E0,$B3,$B5,$75,$0C" />
maybe a different AES mode or something?
I haven't seen any directly referenced "Version" strings yet, though I have a hunch that it might be related to the 22 xx xx
request that is sent directly before key computation. (CMD_FhiCan.dll, 10007A3B
)
If you're feeling adventurous, there's a bunch of AES keys in https://github.com/jglim/UnlockECU/blob/main/UnlockECU/db.json under "Provider": "SubaruSecurityAccess2018CY1"
. There are about 33 unique entries, so it might be feasible to try them all and work backwards from there if there is a successful key.
469A20AB308D5CA64BCD5BBE535BD85F
E8CC52D5D8F20706424813126FA7ABDD
E40EAFE45074A0A0BE9917B0BF59F99B
9A711B32ACC0FF4089A72545416470C6
7D89DDE1C95A224ED723E04496F4C0AE
5C9F97ADE51DA6D0609D49BB05A417E9
66D5364A0D2D45C67E8DB120ED471438
37490E2C46C57F8CB22FEC48133965D6
AEA6D9296786430A22A084EBA4498EE3
0B4BB2772B2119F733FC8C3A7304F022
92BF3DA9EADC2EB50E1A7BC13983899D
AECD1B5CDDE980BFC6869AB2D3DAEA1A
00F17B760F53C30CBFFB54AD808F497A
76E19EDB3027B6C51C8E90CED15E59BA
8C15AC1E0EDFB84D8A1C1526CDFF0850
1615239AB78925A41F18E6AA07673581
0F84E1C95C8277A370E6CE71BFA6831D
9F18E548DCA83D6BAED30ECAAE2D0DDD
6A673120695FD8431286925F8601F955
8B1103CEFC35D915FDA0D869542854DC
1BF6344AA6256C70E45D8754CD653FB2
7692E7932F23A901568DDFA5FF580625
1B8E5CE1CA38AEB6B4AC07C62FC8D769
33E63CA0431153460C18F1064C70FE41
A808350D2BAF2084F0A5C10790A5D506
295A6EBAEC78D774D8AAC1E0B3B5750C
BCB8039CCDFCF5C82D4803E50F399F26
9978D5FA22B0C4E78B5D6451B2F7B7B5
35371DE1F774FC0123A8AE67A725D1BB
3C74A203B92DC74A0BC9D36489223F0A
7168633A55270B3BA4E873BBBC248EC6
18852C9509C8610F3E894CD8F23EB30D
0D816973C75E8B38B920061FCEDBCBAE
As for different AES modes, the algo in CMD_SecurityAccessAES_AB
seems to use similar AES parameters except it decrypts the seed (CMD_SecurityAccess2018CY1
encrypts the seed)
Also on a tangent, I grabbed the only copy of SSM4 that I could find online, which ships with a third-party "ssm4_loader.exe". The program seems to run fine without using the loader application as the activation dialog comes with a working skip button.
I'm still looking into a more permanent solution to this; If you happen to be using the same version of SSM4 (26.6.0), could you help read out these values:
(1) Product ID, as shown on the activation dialog
(2) The value at HKLM\SOFTWARE\WOW6432Node\SUBARU CORPORATION\SUBARU Select Monitor 4\Conf\Device1
If you happen to be using the same version of SSM4
I believe I have the same version, except the skip button is greyed out on mine. I'm pretty sure how ssm4_loader works is that it reads the memory of the program and enables the skip button, then clicks it.
Hello. Based on your information I decoded the XML files from SSM4 and was able to access some ECUs in my 2017 Subaru Forester. Also, using IDA, I was able to “extract” the Seed-Key algorithm used for some ECUs (in particular the METER or MFD).
I'm currently trying to access EyeSight cameras to read the firmware or Coding. I was able to obtain the firmware data that is used to update the EyeSight (from Subaru FlashWrite software), but the firmware is sent to EyeSight in encrypted form and EyeSight decrypts the firmware when it receives all the data.
I want to try to read the firmware from the EyeSight for its further modification. Maybe you have some experience in this direction or maybe you managed to read the firmware from the EyeSight?
Hey OmegaKZ,
I don't have the expertise to answer your question, but I'd like to suggest this thread where jnewb1 and Manevolent are also looking at Subaru-related encryption.
These are their respective repos:
Hello. Based on your information I decoded the XML files from SSM4 and was able to access some ECUs in my 2017 Subaru Forester. Also, using IDA, I was able to “extract” the Seed-Key algorithm used for some ECUs (in particular the METER or MFD).
I'm currently trying to access EyeSight cameras to read the firmware or Coding. I was able to obtain the firmware data that is used to update the EyeSight (from Subaru FlashWrite software), but the firmware is sent to EyeSight in encrypted form and EyeSight decrypts the firmware when it receives all the data.
I want to try to read the firmware from the EyeSight for its further modification. Maybe you have some experience in this direction or maybe you managed to read the firmware from the EyeSight?
You and me both! This is a tricky part to find, and I’ve been stuck on it for a while. If you find anything I would be more than happy to assist. I’ve spent a lot of time searching and this as of now doesn’t seem to be public information; I’m pretty sure we just have to figure this out.
What I know now is that the ECU among all other modules in the various busses are “spoken” to through the CAN gateway module. I have an ECU sitting on my desk right now, and I can confirm that if I power it up, it does not answer to any typical Subaru CAN commands. I’ve thought for some time that the gateway is the missing piece of the puzzle, as it may flash the modules behind it in clear text. But that’s just my current theory.
I have LOG of firmware Upload process in to cameras. In addition, I have Firmware in BIN and MOT format. As I see in Firmware FLASH LOG:
OUT: 00 00 07 87 27 61 IN: 00 00 07 8F 67 61 1A B8 0C 0D OUT: 00 00 07 87 27 62 E0 DE 81 AC IN: 00 00 07 8F 67 62
As I know this messages is SEED-KEY comands. Where: 00 00 07 87 - EyeSight address for Reqest 27 - Access Seed reqest 61 - Level of access.
Second message is ANSWER form EyeSight (00 00 07 8F 67 61 1A B8 0C 0D), Where: 00 00 07 8F - EyeSight adress for Answer 67 - Access Seed reqest 61 - Level of access. 1A B8 0C 0D - SEED
i attach the ZIP archive with LOG files and firmwares. 87501FJ034.zip
I have LOG of firmware Upload process in to cameras. In addition, I have Firmware in BIN and MOT format. As I see in Firmware FLASH LOG:
OUT: 00 00 07 87 27 61 IN: 00 00 07 8F 67 61 1A B8 0C 0D OUT: 00 00 07 87 27 62 E0 DE 81 AC IN: 00 00 07 8F 67 62
As I know this messages is SEED-KEY comands. Where: 00 00 07 87 - EyeSight address for Reqest 27 - Access Seed reqest 61 - Level of access.
Second message is ANSWER form EyeSight (00 00 07 8F 67 61 1A B8 0C 0D), Where: 00 00 07 8F - EyeSight adress for Answer 67 - Access Seed reqest 61 - Level of access. 1A B8 0C 0D - SEED
i attach the ZIP archive with LOG files and firmwares. 87501FJ034.zip
you think the binary is encrypted and eyesight decrypts it before flashing?
I have LOG of firmware Upload process in to cameras. In addition, I have Firmware in BIN and MOT format. As I see in Firmware FLASH LOG:
OUT: 00 00 07 87 27 61 IN: 00 00 07 8F 67 61 1A B8 0C 0D OUT: 00 00 07 87 27 62 E0 DE 81 AC IN: 00 00 07 8F 67 62
As I know this messages is SEED-KEY comands. Where: 00 00 07 87 - EyeSight address for Reqest 27 - Access Seed reqest 61 - Level of access.
Second message is ANSWER form EyeSight (00 00 07 8F 67 61 1A B8 0C 0D), Where: 00 00 07 8F - EyeSight adress for Answer 67 - Access Seed reqest 61 - Level of access. 1A B8 0C 0D - SEED
i attach the ZIP archive with LOG files and firmwares. 87501FJ034.zip
This is exactly what I am seeing in flashing logs for the ECU/ECM:
But here's the interesting part. I can't find any of the same 32-bit words in a flash dump I have for a 2022 WRX's ECM vs. the one you posted. I do know that the same 32-bit patterns show up in both your CPU 1 and 2 files, but they don't match any patterns in the 2022 WRX ECU, which I know uses the same pattern as the 2015-2021 WRX flashes. But, across the two BIN files you shared, I can find similarities between those two.
My guess is we are dealing with the same 32-bit cipher on-the-wire (CAN) that uses different keys for different modules? Or, the module in the 2014 model year was different and all the keys are always the same.
I have the same info for 2 more ECU flashes.
I think we need to not only identify what cipher it is (I have been hinted that it is a Feistel cipher), but also then what the keys are. My guess is that, since the OEM's PK2 files' .MOT's are already encrypted (to be specific, even after decrypting the PK2 file itself), the keys are not in SSM4. Unless anything in the SSM4 ecosystem of utilities has the ability to write to specific sections of an ECU's flash dynamically (i.e. key fob codes or something, not sure here), then we might not be able to disassemble SSM4 in order to obtain details behind the encipherment. In other words, Subaru, or one of its vendors/partners, is "encoding" the flash data to protect it even before packing it into a PK2 file.
I strongly suspect that either the CAN gateway module or the ECM itself read the flash commands and use this cipher to transform the data before persisting to flash, but I don't know how to get the canonical cleartext flash data from the ECM's in order to disassemble them and identify the cipher and/or keys. I know that, on the 2022 WRX (and very likely for the 2015-2021 MY's) they are using at least one Renesas chip on the face of the board with the appropriate flash size that matches the encrypted MOT/BIN files I've seen for flashing them. With a flash dump (that I am not experienced with) and the appropriate RX* architecture plug-ins on the right disassembler, I bet we could figure this out.
There is one more problem: the firmware sent by FlashWrite is not complite (not full firmware).
All firmwares are missing the initial part of bytes, something like a Bootloader (Flasher).
Most likely there is a small algorithm for decrypting data "on the fly" while loading new firmware.
A little later I will upload files of other firmwares and their logs.
You are correct immo and bootliader remains the same, its known practice for almost all ecus
сб, 16 груд. 2023 р. о 08:08 OmegaKZ @.***> пише:
There is one more problem: the firmware sent by FlashWrite is not complite (not full firmware). All firmwares are missing the initial part of bytes, something like a Bootloader (Flasher). Most likely there is a small algorithm for decrypting data "on the fly" while loading new firmware.
A little later I will upload files of other firmwares and their logs.
— Reply to this email directly, view it on GitHub https://github.com/jglim/UnlockECU/issues/25#issuecomment-1858734464, or unsubscribe https://github.com/notifications/unsubscribe-auth/ASCMGG35N72H7A6UGMFBI43YJU3G7AVCNFSM6AAAAAAYPSILXCVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMYTQNJYG4ZTINBWGQ . You are receiving this because you were mentioned.Message ID: @.***>
There is one more problem: the firmware sent by FlashWrite is not complite (not full firmware). All firmwares are missing the initial part of bytes, something like a Bootloader (Flasher). Most likely there is a small algorithm for decrypting data "on the fly" while loading new firmware.
A little later I will upload files of other firmwares and their logs.
I just invited you to a repo I have where I'm cataloging everything I can decrypt from PK2/PAK files. Perhaps there's something in that big library that presents the algorithm in cleartext somehow that I've missed
noticed a 0x34 "request download" as part of the flashing log, curious if you can request to download some or all of the firmware
31.07.2023 11:25:49:0209 OUT: 00 00 07 87 34 04 34 00 02 00 00 12 00 00
would have to figure out the seed/key part, you can try to use the private keys that we found in this thread
The CPU in Cameras the same as in Engine Control Module. Firmware process is the same, but chipper keys is different.
I can flash in my test cameras (from 2018 JP wrx) any of available in FlasWrite firmware.
I use my own driver that I wrote for myself. This driver allows to replace "on the fly" any data transmitted from/to the CAN bus.
The "Driver" it is a piece of software that is embedded between the original software and the original driver from the device (for example, an OpenPort) and replaces the data if necessary.
This way I can replace the Rom ID of the cameras with any one and FlasWrite thinks that it is updating the cameras that it expected to update.
noticed a 0x34 "request download" as part of the flashing log, curious if you can request to download some or all of the firmware
31.07.2023 11:25:49:0209 OUT: 00 00 07 87 34 04 34 00 02 00 00 12 00 00
I will try to "pause" flash process after seed/key from FlasWrite success, and try to send "command" fo request piece of flash from cameras.
87501AL001 - 2015 Subaru Legacy (2.5 and 3.6) 87501VA012 and 87501VA013 - Subaru WRX 2.0L CVT Limited VA013 it is updated version of VA012
Looks like it may still be enciphered as the pattern is still showing up in the new regions of flash. If we can decrypt it, it will be useful to have the bootloader as well though.
Looking at 87501FJ034 (CPU1), the values are 4-byte aligned and these blocks repeat across the file
bytes as-uint freq
9CFBFE52: (0x52FEFB9C) 71957
721F55CF: (0xCF551F72) 17497
50F59156: (0x5691F550) 6063
91134FA6: (0xA64F1391) 1413
1ED7CA1C: (0x1CCAD71E) 1356
3D9EBDA8: (0xA8BD9E3D) 1311
43711815: (0x15187143) 1266
E3342926: (0x262934E3) 1086
0E74769E: (0x9E76740E) 1077
A4B9A6AF: (0xAFA6B9A4) 1019
...
They are most likely operated as individual blocks of 4 bytes. No chaining as mentioned by Manevolent
I'm fairly sure that some of these would have been a bunch of 00000000
or FFFFFFFF
s, however they do not yield anything from a simple xor, so there's likely more nondestructive operations (e.g. rotate/transpose/sum/xors).
If I had to guess, we will not have access to the algo by design (unlike seed/key which we can reverse from the dlls); when the firmware images were built, they were encrypted on their servers prior to distribution, and the bootloader on the cpu receives and decrypts them. Between these two points, the firmware remains as an encrypted blob, so this is probably going to be a difficult endeavour.
If anyone has any tips, I have a de-lidded 2022 WRX ECU sitting on my desk which employs the same strategy (and even has the same exact patterns) as the 2015-2021 "VA" WRX, as I am sure many other ECUs do. I have access to the front side of the PCB with a Renesas chip showing itself off.
Renesas chip: R7F701216, mounted BGA or some other under-chip mount style. This chip reports 4MB flash, exactly how much I see getting reprogrammed in many ECU update files. Another chip: APIC-U05, mounted QFP Another chip: APIC-D18, mounted QFP I also see another chip, RAA252001, QFP
I just do not know much about dumping the flash manually; if there is a way, I am up for it.
There are 10 lines (2 rows, 5 spots per row) of solder on the board that traces seem to dead-end into, very near to the R7F7* chip, which appears to be a part of the RH850 family. I know nothing of Renesas chips, but this comes up: https://www.isystem.com/downloads/user-manuals/10pin-1_27mm-renesas-rh850-debug-adapter.html I wonder if what I am seeing is a development feature left on the PCB and could yield results. I have an oscilloscope and a way to power this ECU, I could perhaps see if the connection points on the PCB make sense in the 10-pin debug adapter?
That Renesas chip most likely uses a serial-like programming protocol, but the programming interface is typically locked by the vendor to prevent code readout.
There are some expensive third-party tools that claim to support the chip such as VVDI, but I am not sure if they are able to bypass the code readout. I vaguely remember seeing a teardown thread somewhere (twitter?) featuring some of these tools; the fancier ones even have fpgas to handle the glitching for you, assuming that the target is susceptible.
Fault injection attacks can also be done by hand, but it is a very involved process. This article covers a successful code readout bypass through fault injection attacks.
If the vendor has enabled the UDS "Read Memory By Address" service, it might also be possible to use that to read out the firmware. From my limited experience with MB ECUs, this service is available after the seed/key unlock during an extended session. With knowledge of the memory map, it is possible to create a full dump of the device, including regions that are normally not covered by the published images (e.g. early stage boot code)
i have tool to read rh850, not expensive, vvdi and all others most likely cant fully do their jubs. If vvdi are able to read mcu - it will kill it while writing. Write procedure is a bit special here and not a "line" per time as it been always. However if you read mcu - you wont get real data/code - all data inside is mixed by renesas compiller in such way - that only mcu knews correct byte order (real data map). Protection here is way high. I got experience working on ic907 with bga R7F701411 - so i can fully read mcu, modify data such as odometer/immo and write it back, successfully virginized couple used ic907 and installed them to cars- one thing helped me alot -that i can read real data by obd , since i knew what im looking for , search this bytes in mcu (one part can be at file head another in middle ). So if you even get mcu dump - it wont help you, unless some of ida or ghidra can analyze the code. Dont give up, i wish i have one of ecu's to read it out for you. My tool is not serial manufactured, id say - handmade.
Definitely some great leads presented here. Thanks everyone for filling in. I am fairly determined to get this thing cracked open as many have (Cobb, etc.); clearly it is possible. I will keep working on trying different things, starting with a memory dump.
What I can say up-front is I think this SID is supported on the ECU. Even without seed-key performed yet, it can be spoken with at "address" 0x7A2 and answers to my (likely malformed) read by memory request with 0x13/INVALID_MESSAGE_LEN_OR_FORMAT.
Another update: if you sent 0x23 0x44 [ 4 bytes ] [ 4 bytes ], ECU isn't happy as described above. However, if you send 0x23 0x41 [ 4 bytes ] [ 1 byte ] or 0x23 0x14 [ 1 byte ] [ 4 bytes ], ECU simply doesn't answer. So I am guessing that likely means I need seed/key and then maybe we'll see some results.
I made a bunch of progress in the code I am using to interact with the car over OBD2 over at https://github.com/atlas-tuning/utilities/blob/main/java/src/main/java/com/github/manevolent/atlas/Main.java, but I am out of time for today. I have the ECU's battery on a tender and will see how it responds at a later time.
For rh850 daimler used routine , where you must pass security levels to reach mcu data read/write access by UDS. Even more the data you will read will be decoded (not mixed as i told before) and here is good place to decompile. Its a massive problem for all who dont knew the right sequence and commands as they are vendor specific, MB smr protocol files dont even have such commands - but they avaliable in ecu if you knew them. Not all daimler rh850 units have routines to reach full memory access - so not all are possible to write, so can be read only - even this give a hope. Im not a Subaru expert, im quiet sure that mcu access here -done in same way. Level 61 must open "Read only" state to ecu, there can be one more security level to get full access
Here's what I was able to discover, with the sent/received frames shown:
7E0 0x1 SubaruStatus1Request code=0x0C 7E8 0x41 SubaruStatus1Response code=0x0C data=0000 Reading active diagnostic session... 7A2 0x22 UDSReadDataByIDRequest dids=11C8(Vehicle Manufacturer Specific) 7AA 0x62 UDSReadDataByIDResponse 11C8(Vehicle Manufacturer Specific) value=FF 7A2 0x22 UDSReadDataByIDRequest dids=F40C(OBD Data Identifier) 7AA 0x62 UDSReadDataByIDResponse F40C(OBD Data Identifier) value=0000 7A2 0x22 UDSReadDataByIDRequest dids=F182(Application Data Identification Data Identifier) 7AA 0x62 UDSReadDataByIDResponse F182(Application Data Identification Data Identifier) value=EE2CB17407 752 0x22 UDSReadDataByIDRequest dids=F186(Active Diagnostic Session Data Identifier) 75A 0x62 UDSReadDataByIDResponse F186(Active Diagnostic Session Data Identifier) value=01 763 0x22 UDSReadDataByIDRequest dids=F186(Active Diagnostic Session Data Identifier) 76B 0x62 UDSReadDataByIDResponse F186(Active Diagnostic Session Data Identifier) value=01 Entering diagnostic session with vehicle... 7DF 0x10 UDSDiagSessionControlRequest EXTENDED_SESSION 7E8 0x50 UDSDiagSessionControlResponse EXTENDED_SESSION data=(empty) 76B 0x50 UDSDiagSessionControlResponse EXTENDED_SESSION data=003201F4 75A 0x50 UDSDiagSessionControlResponse EXTENDED_SESSION data=003201F4 Unlocking CGW... 763 0x27 UDSSecurityAccessRequest seed=7 key=(empty) 76B 0x67 UDSSecurityAccessResponse seed=7 key=90AFB3A631CD80EC12B4F8731B24BBEC 763 0x27 UDSSecurityAccessRequest seed=8 key=E92EBAAF12ED1D27FC5FBBFFA027397B 76B 0x67 UDSSecurityAccessResponse seed=8 key=(empty) Starting routine... 763 0x31 UDSRoutineControlRequest func=START_ROUTINE routineId=2 data=00 76B 0x71 UDSRoutineControlResponse func=START_ROUTINE routineId=2 data=00 7DF 0x3e UDSTesterPresentRequest 80 7DF 0x85 UDSCommunicationControlRequest type=2 data=(empty) 76B 0xC5 UDSCommunicationControlResponse type=2 data=(empty) 75A 0xC5 UDSCommunicationControlResponse type=2 data=(empty) 7DF 0x3e UDSTesterPresentRequest 80 7DF 0x85 UDSCommunicationControlRequest type=3 data=01 Starting routine... 763 0x31 UDSRoutineControlRequest func=START_ROUTINE routineId=2 data=01 76B 0x71 UDSRoutineControlResponse func=START_ROUTINE routineId=2 data=01 Unlocking ECU... 7A2 0x27 UDSSecurityAccessRequest seed=1 key=(empty) 7AA 0x67 UDSSecurityAccessResponse seed=1 key=962E07664C16599CC8DBB16DDBF902BA 7A2 0x27 UDSSecurityAccessRequest seed=2 key=3B29B5BBB8704D2AF7EAB1D5C92E6491 7AA 0x67 UDSSecurityAccessResponse seed=2 key=(empty) Entering programming session... 7A2 0x10 UDSDiagSessionControlRequest PROGRAMMING_SESSION 7AA 0x50 UDSDiagSessionControlResponse PROGRAMMING_SESSION data=(empty) Starting routine... 7DF 0x3e UDSTesterPresentRequest 80 7A2 0x31 UDSRoutineControlRequest func=START_ROUTINE routineId=255 data=004400010000003F0000 Reading memory... 7DF 0x3e UDSTesterPresentRequest 80 Reading memory by address... 7A2 0x23 UDSReadMemoryByAddressRequest address=255 size=255 7AA 0x7F UDSNegativeResponse sid=31 reason=RESPONSE_PENDING 7AA 0x7F UDSNegativeResponse sid=31 reason=RESPONSE_PENDING 7AA 0x7F UDSNegativeResponse sid=31 reason=RESPONSE_PENDING 7AA 0x7F UDSNegativeResponse sid=31 reason=RESPONSE_PENDING 7AA 0x7F UDSNegativeResponse sid=31 reason=RESPONSE_PENDING 7AA 0x7F UDSNegativeResponse sid=31 reason=RESPONSE_PENDING 7AA 0x7F UDSNegativeResponse sid=31 reason=RESPONSE_PENDING 7AA 0x7F UDSNegativeResponse sid=31 reason=RESPONSE_PENDING 7AA 0x7F UDSNegativeResponse sid=31 reason=RESPONSE_PENDING 7AA 0x7F UDSNegativeResponse sid=31 reason=RESPONSE_PENDING 7AA 0x7F UDSNegativeResponse sid=31 reason=RESPONSE_PENDING 7AA 0x7F UDSNegativeResponse sid=31 reason=RESPONSE_PENDING 7AA 0x7F UDSNegativeResponse sid=31 reason=RESPONSE_PENDING 7AA 0x7F UDSNegativeResponse sid=31 reason=RESPONSE_PENDING 7AA 0x7F UDSNegativeResponse sid=31 reason=RESPONSE_PENDING 7AA 0x7F UDSNegativeResponse sid=31 reason=RESPONSE_PENDING 7AA 0x71 UDSRoutineControlResponse func=START_ROUTINE routineId=255 data=00 7AA 0x7F UDSNegativeResponse sid=23 reason=SERVICE_NOT_SUPPORTED
We result in two things,
Good thing I have extra ECUs!
@Feezex does this look similar to anything you've done/worked with?
so its same as daimler 2701 , 2702 and so on. level 61 will open mcu to read data, If you can get 61 you can try to use CaesarSuite to read data with Hex editor. If programmer you use to flash ecu can generate level 61 response then you can collect some key pairs to analyze. Or even more you can brute with that tool by simulating ecu responses , interrupt after got key and start over.
ECU Name 87501AN010
(Subaru 2020-2022 legacy/outback eyesight module. Probably also applicable to other subaru cars and ECUs.)
Source file SSM4 can unlock this ecu. There are two dlls in this zip file. CMD_TwoPhasesOfCertification within CMD_FhiCAan seems to be the entrypoint for the seed/key exchange. I have attached a debugger with IDA and I can step through the process, but not quite sure where to go from there. There appears to be a database lookup for each ECU.
VsmDataLib.zip
Additional context
I captured some seed/key exchanges on my CAN bus. They are 16 byte seed/key pairs using the extended CAN format. They could be byte or frame swapped, I'm not 100% percent sure, but I'll work on verifying that.
(0xf8ee1609ec9ab91c8358060fdebc3e06,0xaa21c0117cf3dcb6bc1f7a7f90ee78ca), (0x63c1fbe1248b0ab0d8a0fb8606e37b13,0x56da42fe1e9c6f936fda699ed253a2d7), (0xd13dae85a592bb91672da8fe8315a1a3,0x8b5148b5ff3a00db72117760bc6e43c4), (0xfeff4608b40e174f9fff21b3dc7fb46d,0xf5f7466d8e316f640e381fad5532ec14), (0x02d3766aadc6621e12d50ddc2adc5038,0xdb2a108b77e977f8d7b0871a6eceb4b8), (0xc01ef055314fb71fc0794b3440ec9577,0x0a6fe147394f74fc36655e2d99b1eb4a), (0xecc763b6f9d31719eabcd5a6e3e1319e,0x88a2eb332caf13e138520424fbb46891), (0x215c2719701b6de846e7461987134079,0x67b339ff36495fe6c8ab25efb82fb756)