-
Notifications
You must be signed in to change notification settings - Fork 55
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Subaru SSM4 2020-2022 #25
Comments
Here's a seed/key exchange i captured:
|
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, Possibly 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 ( |
27 03 > seed request level 3 > 0000000000 Very similar to daimler |
Thank you for looking, that is super helpful!
I'll look for references to those functions. Whole SSM4 program has like 25 dlls, but I can try and find it
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.
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 |
The keys should be variant-specific. Both aes-based algos begin by initiating a (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 @Feezex It does look very much like UDS
|
Lots of RDBI. Here is one of them that is long, and also has two different requests with different length responses (back to back)
Heres another one?
Here's a list of unique DID's that appear before the seed request:
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. |
I haven't been able to get anything out of aes-ecb. There could be a few reasons,
|
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
|
The aes looks like vanilla ECB. I've ripped the aes-specific code out from the dll into a single assembly file. This builds with masm32 V11 (masm32v11r):
The cipher Could you perhaps try this instead:
|
I ran it a couple times:
Perhaps you should also verify my methodology: Breakpoint at 77E1A: (push esi)
Breakpoint at 77E36: (call unk_5F4BA8C0)
|
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.
|
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:
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 I'll find time to add a new subaru aes algo, along with a definition for |
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 |
I've just added an AES provider in c4f693d , and also dumped the AES keys that I could find; the earlier AES key Line 12161 in c4f693d
|
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> |
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.
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 If you're feeling adventurous, there's a bunch of AES keys in https://github.com/jglim/UnlockECU/blob/main/UnlockECU/db.json under
As for different AES modes, the algo in |
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 |
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. I'm currently trying to access EyeSight cameras to read the firmware or Coding. I want to try to read the firmware from the EyeSight for its further modification. |
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: |
The ecu could be soft-bricked and potentially recoverable. On some MB ecus, a prerequisite for flashing is to send an erase command first, then start writing in the firmware. If the process is interrupted or if the firmware is modified, the ecu should remain in a failsafe boot mode that can still receive firmware over the bus. Here's a summarized transcript of a flash session on a specific MB ecu:
Btw I noticed that you were attempting the ReadMemoryByAddress while in a boot session, where it was rejected. The same is true for MB ecus -- ReadMemoryByAddress only works in a extended session and after the seed/key unlock is completed. |
Thank you both! You're probably spot on about the boot mode. Looking at that routine 255, 44 seems a lot like 4 bytes + 4 bytes indicator, and 00010000 looks an awful lot like the end of the bootloader, and 003F0000 looks right too at 4128768 bytes. I'll take care of recovering it in due time, but I do have the delidded ECU mounted and used SSM4 to pair it to the vehicle. It might be delidded, but the vehicle is fine and drives happily otherwise. I now have a third ECU on the way for more testing and a possible clone to keep stock on backups so I have one that isn't exposed to the elements I can always fall back to. I haven't actually tried more of these avenues yet; will get the OEM ECU taken care of first and then go from there. Very excited to get more traction on a real flash dump. I have a few options on the OEM; Cobb will flash it back for me, or I could play with the tooling a bit more. For this round, I'll let Cobb repair it, but in the future I likely will be doing it myself provided we make more progress. |
So I was able to determine that although 0x61 isn't available (yet?), there are more levels that are: Specifically, level 1 (which we know can flash). If you're in Level 1, 3 and 5 do not answer with seeds. No other seed requests return INVALID_MESSAGE_LEN_OR_FORMAT Unlocking ECU... For Level 3, the correct key is 469A20AB308D5CA64BCD5BBE535BD85F (thank you! got that from this repo). However, we get REQUEST_OUT_OF_RANGE for that Level when requesting memory. Level 3 cannot enter a programming session. Level 3 can enter an extended session where I tested reading memory and got the out of range response. For Level 5, the correct key is E8CC52D5D8F20706424813126FA7ABDD. Same issue with REQUEST_OUT_OF_RANGE. Level 5 cannot enter a programming session, but can enter an extended session (where read memory was tested). Also, if I enter a level, I am unable to access any other levels (including 0x61; I wasn't sure if I had to go to level 1, 3, or 5, and then jump to 61). Update: Level 3 does allow you to set the VIN. Level 1 does not. |
I think I am making progress!! I data-logged some tools I have, and I noticed they were setting dynamically defined data identifiers using func=2, or define by memory address. The data returned by these DIDs is not encrypted as far as I know. This gave me a great example to work from to create DIDs, and it turns out you can set each DID for up to exactly 0x64, or 100 bytes. The memory addresses were high, though, so I wanted to read at, say, 0x00000000. This gives you SECURITY_ACCESS_DENIED (getting somewhere...). Turns out Level #1 cannot give you access to this, but Level #3 (in my prior post) DOES. I think this means I can read all memory using the dynamically define data identifier service. REQUEST_OUT_OF_RANGE is returned if you ask for any more than 0x64 bytes back. I will put this code in a loop and report back with what I have. For now, the first several bytes in memory at offset 0x00000000 is: 8388062A0000000000000000006A00008388062A0000000000000000006A00001BE2062A0000000000000000006A00008388062A0000000000000000006A00008388 UPDATE: Here are some example cipher pairs (clear left, encrypted right) we can test with later as I am still dumping data: |
Here is the flash data in the clear for a 2022 USDM WRX MT (very recent OEM calibration) with bootloader included: |
Woah, nice! Wonder if we can reverse the fw encryption from that dump |
I think we can, I've been digging in Ghidra (using https://github.com/esaulenka/ghidra_v850) and have found a few interesting things:
That constant hits this on GH: https://github.com/morkt/GARbro/blob/master/ArcFormats/Escude/ArcBIN.cs Bunch of odd things in here, maybe some folks will know a little better but I'll keep digging |
Sick! I’ll have to try it on my eyesight module. I used the dynamic read by identifier before but hadn’t thought of reading the full memory with it, that’s super cool! |
Sweet and good luck to you! I had to use security level 3, and my define looked like this: 0x2C 0xF3 0x00 0x02 0x14 0xFF 0xFF 0xFF 0xFF 0x10 If it's the same, flash should start and be aligned at 0x00000000 and should at some point give you very consistent OUT_OF_RANGE errors, likely right around where we've seen programming files for that module end. There are some small regions in the middle of the flash that might report that error, too, but they're small on the engine module. |
I will try to read data from my cameras, using this way. Now i log SSM4 data monitor read process, and it wherry similar with this "Dynamically Define Data by Identifier" read algorithm.
|
That 0x1 looks like "define by identifier" sub function which you may not want, the function that got me access to memory was 0x2 in that position https://embetronicx.com/tutorials/automotive/uds-protocol/data-transmission-in-uds-protocol/ and I had to have Level 3 access |
I try to session with EyeSight and no luck: //TESTER TOOL PRESENT //START $03 SESSION //REQUEST $03 SEED //SEND $03 KEY //REQEST DID VALUE IN: 00 00 07 8F 7F 2C 12 I try to use 03 - Sub function instead 02 (Read by memory Address) and got NRC 13 (INVALID_MESSAGE_LEN_OR_FORMAT) |
Dang, okay. So I think this means we have to use the flash we have (or any other modules we can find), decrypt that flash, then figure out what key yours might be using and decrypt that flash, too. I am pretty sure we have the algorithm somewhere in the flash dump I sent, it's just a matter of digging it out. Also did you try entering an Extended Session? I had to do that, too, to get that to work |
I think no. How to do this? |
Try sending frame 0x10 0x03 after successful Level 3 seed/key where |
Turns out I was wrong; we don't need DIDs to read memory on the ECU, but you definitely can do it. What works is (again, level 3 security access within an extended session): What threw me off is that the address and size seem to be backwards from the definition in the 0x14 byte. |
I was try this, but no luck. I receve SERVICE_NOT_SUPPORTED. Here is full log of transmission:
|
I think I found the best candidate so far as to what the firmware encryption algorithm may be. I have spent the last 2 days digging, and digging, and I have found numerous candidates but have all led to dead ends so far. However, I think the function starting at offset 0x0000f26e is the best candidate for how the encryption works. Here is why I think that:
Does appear to be the "crossover" where the red/green lines switch over, if L0 and R0 are both 16 bit segments of param_1, and the last operation in a loop is to, indeed, cross the data over before looping again. K0..n would likely be the data referenced in RAM starting at 0xfef1dbbc. Also, note that only L0 (AKA param_1_copy >> 0x10) is the only side XOR'd, which matches the Wikipedia article as well. In practice, that would mean "uVar1" is the result of F. This makes sense, as in the disassembled code, 'F' (in regards to the Wikipedia article) does appear to be the result of K0..n's associated lookup. I'll keep trying to figure this function out, but if anyone else is interested you might want to have a peek in IDA/Ghidra as well! If we can figure the algorithm out, we will be able to decrypt any flashes. K0..n may only be so large; we can likely brute-force the remaining flashes such as EyeSight. |
As far as I understand, all this applies only to the engine ECU? It will not be possible to decrypt/encrypt the Camera (EyeSight) firmware using this algorithm, or there will be a completely different keys that we do not have. I also couldn’t read the firmware from the EyeSight using any of the methods (the cameras are blocked or I’m doing something wrong). |
I think you're right, because I've just finished reversing the algorithm (encrypt + decrypt), but even the keys that I found in the firmware don't match up with what is flashed to the vehicle. It is now up to us individually to find the 64-bit key pairs used for each control unit/module I suspect. At least we have the algorithm, now, though. Here is the prototype: https://github.com/atlas-tuning/utilities/blob/main/java/src/main/java/com/github/manevolent/atlas/ssm4/SubaruDITFlashEncryption.java Keep in mind what I have right now is very preliminary; the RH850 has some unusual byte endianness so there is a good chance it doesn't line up perfectly right now. It does produce the correct data with the ECU's routine being emulated/debugged with Ghidra, however. Here's what I've found out:
I strongly believe the flash data I've seen in this thread so far all traces back to the same algorithm that I have shared. This appears to be the "new" flash encryption algorithm for Subaru Renesas-based ECUs. Sadly, though, what this does mean is that unless you can find that 64-bit key, you're going to have a bad time. So, memory access is probably going to be an absolute must. |
Send me one of ecu's, ill read it fully. |
SSM don't have any interesting keys or protocols for flashing(programming) eyesight. I found key algorithm in SSM for diag session $03 for eyesight, IPC, MFD. We can try search Flash codes or something else in FlashWrite, but i think we don't found any interesting there. How will you planing to read EyeSight CPU? I think CPU is locked from external (ISP, UART) read. |
Correction: I have figured out how to decrypt flash for my ECU. The 64 bit I have is correct, I just had the operations backwards. Encryption = decryption, and vice-versa. Here is how I did it with my code I pushed and referenced earlier:
This means that WRX ECU's from 2015-2023 are now wide open for what I think is the first time in the open source community! Very exciting. Now, the real work can begin... I'd like to try decrypting your flash, next, @OmegaKZ by doing a bunch of different things with the data I have. I can't brute force a 64 bit key, but I'd at least like to try different variations/permutations of the key I have, perform a key-scan through my flash, etc. It most likely won't work, but I'll give it my best shot! Update: sadly I haven't been able to figure out the key for that module from what I have. Back to thinking we need the memory/flash from the device in order to decrypt that module. On the other hand, my code and related tests are complete so once we do have candidates to test with, we have a working program to test keys against. |
Hello All! With level 1, 0x34 accepted: 0x34 0x04 0x44 0x08 0xFA 0xC0 0x00 0x00 0x3D 0x3F 0x00 But 0x35 didn't accepted: 0x35 0x04 0x44 0x08 0xFA 0xC0 0x00 0x00 0x3D 0x3F 0x00 |
Started reading with 0x23, start address = 0x0. 7F 23 31 changed to 7F 23 33 from 0x08FAC000. 33 - Security Access Denied. Nothing was readed. So i think right offset the same - 0x08FAC000. But why 0x23 doesn't read it with all security levels 1, 3, 5? |
Got full ROM with OBK... No one is answered... |
Sorry about that, unfortunately I am not experienced enough in Subaru ECUs to offer any meaningful help. Thanks for pointing out the lead about OBK too, hopefully that helps someone else. FWIW If I had to guess based on my experience with MB, if testerpresent messages are not sent, the ECU will exit the session and revert to an unauthenticated state. If you've been sending testerpresent messages and this is still an issue, it's probably a Subaru-specific issue. |
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)
The text was updated successfully, but these errors were encountered: