I buy a lot of DVDs and Blu Ray discs, but I very rarely watch them straight from the disc. As soon as I get a new one, it gets ripped to my MythTV backend, from where I can watch it at any time and on any screen in the house. Until recently, I’ve been happily doing this using MakeMKV and a Plextor USB Blu Ray drive, but unfortunately it seems that the film industry thinks the best way to reward me for buying so many is to brick my drive.
Blu Ray discs are encrypted, and to read one the player (or ripper) software has to pass a key to the drive. The drive then uses the key to decrypt the data. However, these decryption keys can be revoked, and once they’ve been revoked the drives will refuse to use them to decrypt any data, even data from old discs that they’ve been happily decoding in the past.
The drives get to know about the revocation because the Blu Ray discs that they play contain revocation lists. When you buy a new disc it may contain a newer version of the revocation list, and as soon as it’s inserted into the drive, the drive will update its internal revocation list… permanently!
This is what happened to my drive… I bought a copy of Robert Rodriguez’ Machete Kills, dropped it into my drive and clicked the button to rip it, only to get an error message telling me that the host key was invalid. This isn’t the first time this has happened, whoever it is that maintains MakeMKV does a good job of acquiring new keys, so usually it’s just a five minute job to download a new version, and then off we go again.
Unfortunately this time the new version needed a newer version of libc, which meant upgrading my MythTV backend, which meant upgrading all of the MythTV frontends in the house too, and that’s a whole lot of effort that I could really do without, and which made me wonder… maybe it might be easier to make the drive forget about the new revocation list. It must be stored in some non-volatile memory somewhere in the drive. All I needed to do was find it and somehow revert it back to the previous version, how hard could it be?
So… out came the screwdriver and the four little screws that were hiding under the drive’s feet, and off came the cover.
There’s surprisingly little to be found inside the drive. Other than the motors and mechanical stuff, there’s one main board with a handful of chips on it and a tiny satellite board that just holds two buttons and an LED.
The main board is populated on both sides and contains the following chips:
|Sunext||SC6300B3||Multi-format optical disc controller|
|Prolific||PL2571||USB to SATA bridge (datasheet)|
|PMC||Pm25LV512||512Kbit Flash (datasheet)|
|Macronix||25L8005||8Mbit Flash (datasheet)|
So, from the chips on the board there were four potential places where the host revocation list could be stored:
- In the 512Kbit flash
- In the 8Mbit flash
- In one of the two undocumented Texas Instruments chips
- In some storage internal to the main Sunext chip
With the Sunext chip and the Texas Instruments chips appearing to have no documentation available anywhere on the Internet, the flash chips were the obvious starting point.
On the board the smaller flash chip is right next to the main Sunext chip and the larger flash is right next to the USB to SATA bridge, but a look at the datasheet for the USB to SATA bridge showed that it only needs a tiny amount of flash to store the vendor IDs and other stuff needed for USB device enumeration. This was confirmed by following the traces on the board, with the traces from both chips going through vias to the other side of the board.
So now the 8Mbit flash was the prime candidate.
Dumping The Flash
The flash chips use an SPI serial interface, so I could use my BusPirate to talk to them, but to dump the contents of the flash you have to find a way to get exclusive access to the signals, you can’t just attach some probes while they’re in circuit and start reading because the CPU on the board will interfere and you won’t get any sensible results.
If I had a hot air soldering station the easiest option would probably have been to de-solder the flash chip, but I don’t, so after a half-hearted attempt at de-soldering the chip with an iron, I took the next easiest option and cut the traces to the CS (Chip Select), SI (Serial In), SO (Serial Out) and CLK (Clock) pins. As long as the traces are cut with a thin and sharp blade then they can be reinstated very easily with solder bridges.
With the flash chip isolated, I connected the BusPirate, started minicom and powered up the board. I set the BusPirate to SPI mode with all default settings except for voltage (normal rather than open collector) and speed (a conservative 250KHz), and tried a read request, which as can be seen below, successfully read the first few bytes from the chip:
SPI> [0x03 0x00 0x00 0x00 r:8] /CS ENABLED WRITE: 0x03 WRITE: 0x00 WRITE: 0x00 WRITE: 0x00 READ: 0x58 0xF0 0x9F 0xE5 0x7E 0x00 0xA0 0xE3
Next, I needed to dump the entire contents of the chip, but I couldn’t really do that through the BusPirate’s interactive serial console. A quick Google found flashrom, which is a utility for reading and writing flash that lists the BusPirate as a supported programming device. After downloading and building the flashrom source, one quick command got me the entire 1MB contents of the chip:
flashrom -p buspirate_spi:dev=/dev/ttyACM0,spispeed=1M -r test.dump
Finding The Revocation List
With the contents of the flash successfully extracted, I needed to find out what the revocation list might look like. Time for Google again… where the second result for “blu ray host revocation list format” was a handy looking PDF titled “Advanced Access Content System (AACS) – Introduction and Common Cryptographic Elements“. Then searching within the PDF for “host revocation” immediately found a Table of Contents entry for “Updating Host Revocation List in Non-volatile Memory of Drive” and a List of Tables entry for “Host Revocation List Record” and “Host Revocation List Entry”. Perfect!
From reading this document I discovered that the host revocation list is held within a Media Key Block (MKB), and that the MKB starts with a Type and Version Record, which starts with the bytes 0x10 0x00 0x00 0x00 0x0C. 0x10 is the record type identifier and 0x00 0x00 0x00 0x0C is the length of the record.
Searching the flash dump for this byte string found exactly one occurrence at offset 0×5000, and using the document to decode the following data revealed that, yes, this is the host revocation list!
The document also revealed that drives are required to store up to 32KB of data for the revocation list, and looking at the data in the flash I could see that the list was followed by all zeros until offset 0xD000, or exactly 32KB from the start of the revocation list. This last bit was important as it meant that there probably wasn’t a length stored somewhere else in the flash that would need to be updated, and that I could probably just write over the revocation list and make no other changes.
Replacing The Revocation List
Having found the bad revocation list, my next task was to work out what I could replace it with. Where could I get a good version of the revocation list that didn’t lock out the key that my copy of MakeMKV was using?
The bad version was delivered by the Machete Kills disc… older discs must also have revocation lists… lists that don’t revoke the key I’m using.
It turned out that MakeMKV made the next bit particularly easy, it keeps a copy of any new MKB version that it comes across:
mythtv$ ls -l .MakeMKV ... -rw-r--r-- 1 root root 15101 2013-12-02 18:52 MKB_v42_ALAN_PARTRIDGE_ALPHA_PAPA.tgz -rw-r--r-- 1 root root 14933 2014-06-28 22:47 MKB_v46_MACHETE_KILLS.tgz ...
Unpacking these files and running a diff on the contents revealed that the revocation lists were exactly the same length and only had he following differences:
- The version number had increased from 42 (0x2A) to 46 (0x2E)
- The last revocation list entry had changed
- The signature at the end had changed
So, I used the old version of the revocation list from the Alan Partridge disc to overwrite the data in the flash dump, resulting in the diff shown in the image above. I then used flashrom to write the changes back to the chip:
echo "5000:d000 hostrev" > layout flashrom -p buspirate_spi:dev=/dev/ttyACM0,spispeed=1M --layout=layout --image=hostrev -w test.dump
Putting It Back Together
With the changes written back and verified, I reinstated the cut tracks to reconnect the flash chip to the controller.
Using some Kapton tape as a solder mask, it just took a single wipe of the iron to reinstate each track. Or at least that’s what I thought, but after testing the continuity, screwing the drive back together and applying power… nothing.
Luckily it was just a bad bridge on one track, after taking it apart again and rechecking the continuity I was able to fix it and this time, when I put it back together, it lit up and wiggled the read head like it should.
As the screenshot above shows, it works!
From start to finish I think it took four or five hours, so definitely faster than upgrading all of my MythTV machines, and of course it was interesting, rather than mind numbingly boring.
It’s only a temporary fix, I now need to make sure I don’t put a new disc in the drive. I was hoping for a while that I’d be able to write protect the flash and block the drive from ever updating the revocation list again, it has a physical write protect pin, but unfortunately the pin only protects the status register, which needs configuring via SPI commands to write protect the main data.
Any write protection will need a software element, which will mean changing the firmware, and right now I don’t even know what architecture the controller uses, ARM?, MIPS?, no idea!
It’s good enough for now though, I can at least start to clear the backlog of older discs waiting to be ripped.