PIC Burnout

From RECESSIM, A Reverse Engineering Community
Jump to navigation Jump to search

Abstract

Burnout is a low-cost method to bypass code protection and dump the OTP of PIC12C5XX, PIC12C67X, PIC12CE67X, PIC14000, PIC16C55X, PIC16C6XX, PIC16C7XX, and PIC16C9XX MCUs. It is non-invasive but does damage the memory contents in the readout process.

Burnout is possible due to two findings:

  1. Excessive programming at an elevated programming voltage causes a whole flash bit line to become stuck high.
  2. 'Scrambled' OTP readout is possible on devices that are supposed to only read 00. Some devices have scrambling always available.

For the development of Burnout, the PIC16LC63A as featured in the original Xbox was used[1]. Other OTP MCUs are assumed to have the same vulnerability[2]. PIC16C65A is also confirmed to have the same vulnerability.

Steps used to dump the PIC16LC63A:

  1. Assert the ICSP CLK pin (RB6 for PIC16C63) high when powering the VPP pin (to enter programming mode)
  2. Read out the whole OTP. It will be subject to scrambling due to code protection.
  3. Enter the config area and increment the address beyond the config words (to 0x2008 or beyond). I recommend going to address 0x20E0.
  4. Raise the programming voltage to ~14V. I found this is the voltage that has the highest programming current on my chip, but that should be determined for each chip individually.
  5. Program the value 0x7F using a custom program cycle that lasts 5ms. That's 50x longer than recommended.
  6. Repeatedly program the value. Perform readbacks to determine attack success. Bits that are programmed (set low) will eventually read high once again. This can take several tens of thousands of program cycles
  7. Repeat this burning process for 16 consecutive words.
  8. Increment the address to 0x2100 to check for success. This area is usually unwritable and reads back as 0. However, Burnout still causes these bit lines to read high so you will read 0x3F80 if the attack has worked.
  9. Power cycle or reset the ICSP to read out the scrambled OTP again. I recommend repeating step 8 and 9 a few times to make sure a good dump has been made.
  10. Reconstruct the original OTP data from the pre-Burnout and post-Burnout dumps.

Discovered by Prehistoricman

Background

Inspired by doom's talk at RE//verse

Prior to my work, there is only one published attack to dump OTP on PIC16C63. That's flylogic's laser method[3] which damages the gate that combines the redundant lock bits.

It's an impressive hack but expensive, precise, and physically challenging.

Many have tried glitching the PIC16C chips[4][5] to bypass code protection without luck, myself included. The most promising exploit I found involved dropping the supply voltage low during a read command, causing some bits of the word to change. These bit flips can be monitored with the scrambled readout and they leak some information about the real value of the word.

Scrambling

Data reconstruction is only possible if the scrambling method is known. For certain PIC MCUs, the scrambling is known and documented by Microchip[6]. For example, the PIC16C64 has an operation called SUM_XNOR7 where the upper 7 and lower 7 bits of the word are XORed together to produce a 7-bit result. If it's not documented, or the chip is not supposed to output scrambled data, then you will have to figure out the scrambling scheme.

In the case of the PIC16C63, I discovered a backdoor that allows scrambled data readout which is simply: drive RB6 high at programming mode entry. It does not have the same SUM_XNOR7 scrambling scheme. I used a sacrificial chip to figure it out:

  1. Program 14 words where each has one unique bit set
  2. Program another word to 0
  3. Program the rest of memory with random values for validation
  4. Enable code protection
  5. Read out the values

I found that the scheme in PIC16C63 also reduced the effective bits per word to 7 (from 14, the native word width). This describes the xor operations to produce each scrambled output bit:

i<0:13> are the input bits from OTP, o<0:13> are the output bits (scrambled)

o0 = 0
o1 = 0
o2 = 1 ^ i2 ^ i13
o3 = 1 ^ i3 ^ i12
o4 = 0
o5 = 0
o6 = 0
o7 = 1 ^ i1 ^ i7
o8 = 1 ^ i0 ^ i8
o9 = 1 ^ i4 ^ i9
o10 = 1 ^ i5 ^ i10
o11 = 1 ^ i6 ^ i11
o12 = 1 ^ i3 ^ i12
o13 = 1 ^ i2 ^ i13

If 7 of the 14 input bits are known, then the unknown 7 bits can be decoded in this scheme. Note that every active output bit is flipped by two input bits. So both 0x0000 and and 0x3FFF get scrambled to the same value: 0x3F8C.

Burnout

By design, the affected PIC chips are partially programmable even when code protection is enabled. Writing to the program OTP is restricted, but the config area is always programmable.

The ICSP (In-Circuit Serial Programming) Guide shows a memory map where 0x2000 - 0x2007 is the config area and all addresses beyond that are 'Reserved'. If you read 0x2008 and beyond, you will find there's actually a program flashed there. I suspect it's for production tests and there is a secret sequence of operations to execute it, but that is not the topic of this exploit. This program is stored in flash and can be overwritten too.

Beyond 0x2100, there is no memory or data except for when the PC wraps around to 0x2000. It reads back as 0.

Another thing to note is that for these chips, the programmer is in full control over the programming voltage, duration, and repetitions. We can also run ICSP commands during the programming operation. The programming interface is very low-level compared to modern chips, and this is why the config area is always programmable. To enable code protection, the algorithm from Microchip specifies 100 write cycles to the config word. If the config area was protected, we would not be able to complete those 100 cycles.

So, what can we do? A lot. These are some of my ideas:

  1. Reset glitch. The reset pin is shared with the programming voltage as well.
  2. Run the standard clock input while in programming mode, or before entering programming mode.
  3. Measure current during a read command. Possible side-channel information leak.
  4. Run ICSP commands while a word is being programmed.
  5. Check for undocumented ICSP commands

While exploring those, I had my winning idea: over-programming. To program a flash cell, a high voltage is applied that accelerates charges across the insulators around the floating gate in a process called hot carrier injection. It seemed plausible that I could burn out a flash cell by cranking up the voltage to break down that insulation and hopefully its value would go from 0 back to 1. I would use this against the config bits that enable code protection.

It didn't work. I believe the primary reason for that is there's a current limiting circuit on the programming voltage. Beyond around 15V, increasing the voltage only decreased the current further. The specified programming voltage is 13.25V. I tried extreme levels of over-voltage and found that above ~25V, a protection circuit kicks in and draws high current. Above 22V, bits not intended to be programmed were programmed anyway, indicating that some internal transistors are breaking down due to high voltage.

Another variable we can change is the programming duration because that's simply the time between ICSP commands for start programming and end programming. Microchip specify 100µs, so I gingerly tried 1ms, 10ms, 100ms, and found that the chip can survive those out-of-spec scenarios for a long time. Yet I was still not able to damage the code protection flash cells.

But surprisingly, the flash memory outside the config word does get damaged! I found that around VPP = 13.9V and 5ms program cycle duration was most effective in burning out bits and causing them to read back as 1 again. So that's cool, but how does it help us read out the program area which we can't program? It turns out that this bit burnout doesn't apply to the flash cell, but to the whole flash bit column! Now we can guarantee that every flash word in the column has this bit set, and it's simply a case of repeating the process with 7 bits per word for all word columns (there are 16 on PIC16C63A).

Another parameter to play with is temperature. I found that heating the chip (with a lighter) caused Burnout to succeed quicker, but there is also a tendency for bits to recover when the chip has cooled.

Typically, I can use Burnout to dump a chip in under 20 minutes. My programmer burns 7 bits at once, repeated 16 times to cover all columns. Some chips can burn in in under 30 seconds, making the whole process very fast. Some chips are more resilient and need over an hour plus heating.

Aftermath

PIC16LC63A RP2040 breadboard.jpg

I built the setup above to develop Burnout as well as to try out glitching. Once successful, I started soliciting PIC chips from the Xbox community for dumping.

PIC16LC63As from Xboxes.jpg

References