(2025-03-03) The Graphene Saga: part 3 -------------------------------------- The spring has come, and guess who's back. Guess **what** is back almost a year since the last time. I had to have a lot of patience to wait until this moment, and it's not even the biggest thing to come yet. Right now, as far as I'm concerned, IMEI editing in Google Pixels from 6 to 9 Pro is a fully solved thing. Of course, you have to have it rooted but I don't deal with carrier-locked devices in general, there are other people who are more interested in that. Regardless, there now is a totally irrefutable proof that GrapheneOS developers were absolutely wrong in their statements about IMEI editing in Pixels and in general. I don't know why they said that in the first place, either because of their own stupidity or due to the fear of being threatened a legal action, but the proof is out there now, and in my case, it even is shaped into a fully autonomous FOSS tool that runs on any rooted Pixels themselves starting from the model 6 and above. I've called it lexipwn ([1]). It's a portmanteu of the reversed word "pixel" and "pwn". It consists of two parts, both written in Go: lexipwn-cli and lexipwn-gui. You can use lexipwn-cli independently in the console like Termux or ADB (as long as you have root access), but it also gets built inside the lexipwn-gui APK file and is unpacked and called as root by the GUI part when you perform any saving or loading action. The GUI part is handled by the Fyne ([2]) framework which might not be an ideal choice for everyone but is the easiest way to get started with Android GUI without having to write a single line of Java or XML. Also, since all targets are Pixels that share the same architecture, we can easily instruct Fyne to only build the application for "android/arm64" and save a ton of space by eliminating unused binary code. So, as you can see, lexipwn offers a lot of fields to edit, and also eases the IMEI and MAC address randomization. Of course, everything is done via editing the devinfo partition, which had been covered before. But what's different this time and why does the IMEI editing finally work as intended? Well, let's recap where the research had stopped a year ago. Quoting my last post on the topic directly: > The main subject, as you might have seen in LuxDocs, is now stalled at the stage of finding where the IMEI SHA checksums are stored. Because the IMEIs themselves are stored in the devinfo partition in the plain ASCII form (although the partition itself is binary), and this partition, contrary to my expectations, really controls everything over the EFS. Of course, if either IMEI doesn't match its checksum, the device reports both of them as 000000000000000 to both the OS userspace and the network. And I could partially do this search in the offline mode as I dumped the modem firmware image along with everything EFS-related while I still had the root access. But, of course, I should have dumped everything I could. And then yes, I had to move to an unrooted Graphene on my Pixel 6 and have been using it as my main Android ever since, having no way to continue this research. The truth is, I had no idea how close to the solution I had been for all this time. But then, in December 2024, a guy who was interested in my research contacted me. As a result, after a short exchange of ideas, I bought myself a Pixel 7a, rooted it and resumed digging. Yes, I had been right about the place where the checksums were stored, the /mnt/vendor/persist/modem/cpsha file, but how was its content generated? Well, the algorithm still is a black box inside the modem firmware but it can be triggered with an external command, and I had also seen that command before: AT+GOOGGETIMEISHA. It just didn't work for me. Why? And that's when that guy gave me a tip that unraveled everything else: for this AT command to work, the phone needs to be booted into the factory mode. What's factory mode, you may ask? Well, that's essentially a value for another field in the devinfo partition, "bootmode". I don't know other values for this field besides "normal" and "factory", and these values can also be set via the "fastboot oem set_config bootmode" command. When editing the field manually and not via fastboot, you also have to change its field type to DIUS if it was initially set to DIFR (lexipwn-gui does this automatically btw). Anyway, when you set this field to "factory" and reboot, it indicates that the phone has been booted into the factory mode by showing a big red word "Factory" instead of the very first boot logo, and I'm not sure whether it affects anything else but it unlocks certain modem interactions including the AT+GOOGGETIMEISHA command, which now returns the hexadecimal value to be replaced in the /mnt/vendor/persist/modem/cpsha file, which is calculated from the current devinfo fields. So, again, the devinfo partition is the primary source of truth for EFS here, not vice versa. And this is where everything worked: after fixing the CPSHA value, regardless of whether or not the "bootmode" field is changed backed to "normal", the new IMEI numbers are shown both to the device and the network after rebooting. This was a real breakthrough, but I'm not going to stop there. Since I have a performant enough hardware to build Android locally now, I am planning on creating a custom rooted (or at least userdebug) GrapheneOS build with lexipwn, a fingerprint spoofer and several other privacy-protecting tools preinstalled. If going the userdebug route, I'll also have to change the way lexipwn-cli is called inside the GUI part, but that is the least of my worries for now. The primary testbed is still going to be the Pixel 7a, but I'm pretty sure similar builds will work on every device from the Tensor/Exynos family. This is pretty much going to be my magnum opus of the year, unless, of course, GrapheneOS devs themselves realize their past mistakes, embrace the truth and finally incorporate IMEI, MAC, other devinfo and fingerprint editing into their mainline builds. --- Luxferre --- [1]: https://codeberg.org/luxferre/lexipwn [2]: https://fyne.io