Last week, I found the ‘rvpr’ 0 resource in the Pippin 1.0 ROM and the role it appears to play in the Pippin’s startup process. I noted that there are no ‘rvpr’ resources in the 1.2 or 1.3 ROMs, but after digging a little deeper I discovered that is only half true: the contents of ‘rvpr’ 0 are in fact present in both the 1.2 and 1.3 ROMs. But since there’s no entry for it in either ROM’s resource map, a call to _GetResource won’t find it. If 1.2 executes ‘rvpr’ 0 to perform the auth check, then the loading code I found last week must therefore be different in that version.
‘rvpr’ 0 itself appears to be a bit obfuscated. There are many subroutines contained within—if the Link / Unlk / Rts pattern is used as a heuristic, I counted over 250 of them. However, I suspect that much of this code is unused and/or intentional red herrings.
I quickly skimmed the code for an overall first impression before stepping through it in an editor. Some of the aforementioned subroutines are duplicates for some reason:
108A 4E56 0000 Link A6, #$0
108E 2F2E 0008 Move.L $8(A6), -(A7)
1092 206E 0008 MoveA.L $8(A6), A0
1096 2068 0004 MoveA.L $4(A0), A0
109A 2050 MoveA.L (A0), A0
109C 4E90 Jsr (A0)
109E 4E5E Unlk A6
10A0 4E75 Rts
10F0 4E56 0000 Link A6, #$0
10F4 2F2E 0008 Move.L $8(A6), -(A7)
10F8 206E 0008 MoveA.L $8(A6), A0
10FC 2068 0004 MoveA.L $4(A0), A0
1100 2050 MoveA.L (A0), A0
1102 4E90 Jsr (A0)
1104 4E5E Unlk A6
1106 4E75 Rts
1172 4E56 0000 Link A6, #$0
1176 2F2E 0008 Move.L $8(A6), -(A7)
117A 206E 0008 MoveA.L $8(A6), A0
117E 2068 0004 MoveA.L $4(A0), A0
1182 2050 MoveA.L (A0), A0
1184 4E90 Jsr (A0)
1186 4E5E Unlk A6
1188 4E75 Rts
27E8 4E56 0000 Link A6, #$0
27EC 206E 0008 MoveA.L $8(A6), A0
27F0 226E 000C MoveA.L $C(A6), A1
27F4 2290 Move.L (A0), (A1)
27F6 7000 MoveQ.L #$0, D0
27F8 4E5E Unlk A6
27FA 4E75 Rts
2AB0 4E56 0000 Link A6, #$0
2AB4 206E 0008 MoveA.L $8(A6), A0
2AB8 226E 000C MoveA.L $C(A6), A1
2ABC 2290 Move.L (A0), (A1)
2ABE 7000 MoveQ.L #$0, D0
2AC0 4E5E Unlk A6
2AC2 4E75 Rts
… while others are mostly duplicates with one or two extra instructions:
1C64 4E56 0000 Link A6, #$0
1C68 206E 0008 MoveA.L $8(A6), A0
1C6C 20AE 000C Move.L $C(A6), (A0)
1C70 216E 0010 0004 Move.L $10(A6), $4(A0)
1C76 216E 0014 0008 Move.L $14(A6), $8(A0)
1C7C 216E 0018 000C Move.L $18(A6), $C(A0)
1C82 216E 001C 0010 Move.L $1C(A6), $10(A0)
1C88 216E 0020 0014 Move.L $20(A6), $14(A0)
1C8E 216E 0024 0018 Move.L $24(A6), $18(A0)
1C94 4E5E Unlk A6
1C96 4E75 Rts
27FC 4E56 0000 Link A6, #$0
2800 206E 0008 MoveA.L $8(A6), A0
2804 20AE 000C Move.L $C(A6), (A0)
2808 216E 0010 0004 Move.L $10(A6), $4(A0)
280E 216E 0014 0008 Move.L $14(A6), $8(A0)
2814 216E 0018 000C Move.L $18(A6), $C(A0)
281A 216E 001C 0010 Move.L $1C(A6), $10(A0)
2820 4E5E Unlk A6
2822 4E75 Rts
2824 4E56 0000 Link A6, #$0
2828 206E 0008 MoveA.L $8(A6), A0
282C 20AE 000C Move.L $C(A6), (A0)
2830 216E 0010 0004 Move.L $10(A6), $4(A0)
2836 216E 0014 0008 Move.L $14(A6), $8(A0)
283C 216E 0018 000C Move.L $18(A6), $C(A0)
2842 216E 001C 0010 Move.L $1C(A6), $10(A0)
2848 216E 0020 0014 Move.L $20(A6), $14(A0)
284E 4E5E Unlk A6
2850 4E75 Rts
Once I sat down and walked through the code from the very top, though, things started to become a little clearer, although I’m not finished analyzing this code yet by any stretch. Recall from last week that the Pippin loads ‘rvpr’ 0 from ROM and then copies it into a block of memory allocated on the system heap. ‘rvpr’ 0 starts by getting its own address in the heap and applying an offset to it:
104 41FA FEFA Lea.L @start, A0
108 D1FC 0001 06A2 AddA.L #$106A2, A0
10E 2008 Move.L A0, D0
110 A055 _StripAddress
112 C18C Exg.L D0, A4
Next, it gets its address again, but without the offset:
14 41FA FFEA Lea.L @start, A0
18 2008 Move.L A0, D0
1A A055 _StripAddress
Then, it calls a subroutine that reads and writes to some data located at the offset -$7C60 from the address calculated in the first step, effectively placing it at $8A42 from the start of its memory block. It subtracts the longword found here (initially zero) from the unmodified start address, and if the result is zero, returns without doing anything else. But if it’s not zero, as would be the case when first running this code, things get interesting. It checks to see if _HWPriv is implemented and if so, sets a Boolean to true at offset $8A46. Then it passes its address + $8A47 to yet another subroutine. Finally, it sets $8A42 to the unmodified start address (effectively short-circuiting future calls), checks the Boolean at $8A46 and if it’s true, flushes the instruction cache by calling _HWPriv with selector 1 in register D0.
Hmmm. Why would it need to explicitly flush the instruction cache? The answer to that question lies in the subroutine that gets passed @start + $8A47. I haven’t fully wrapped my head around it yet, but from reading the code there it looks like offset $8A47 of ‘rvpr’ 0 looks to be a compressed list of offset locations, used to patch ‘rvpr’ 0 in place. A-ha! Now it’s clear why ‘rvpr’ 0 is copied to the system heap, and abundantly clear why the instruction cache needs to be flushed after this subroutine returns: it’s self-modifying code.
(P.S. Josh Juran graciously pointed out that the Metrowerks runtime used by e.g. CodeWarrior performs this same in-place relocation of code resources at runtime. Side question: What’s the possibility the Pippin’s auth check was written with CodeWarrior?)
Tricky one! Minute nitpick here — inline MacsBug symbols like the “T_memcmp” above apply to the preceding code, not to the following code.
What would you say about patching the ‘rvpr’ *into* a ROM that works with SheepShaver, so that you can test it out on some disk images (or disc images?) without burning them?
While I’m not that much into disassemblies and assembly listings, each and every post about the Pippin rom sure is exciting to read. Keep up the good work!
I tried to compare 2 Pippin Authentication files with each other. They are rather weird.
The first 4 bytes seem to be the length of the entire file. The 4 bytes at offset 0x4C-0x4F appear to be the number of 128-bit blocks after it (minus one block maybe? The numbers almost add up).
A few 128-bit patterns seem to be repeated in the files. 0DFBE8AA4C20B52E1B8BF3CB6CBDF193 is one of them. The authentication file for the Navigator CD (from Japan) has tens of kilobytes of 37CA147665AA8F971D9A5E36D9D78270 repeated over and over and over. I suspect that the auth file is encrypted in ECB mode using a constant key. Identical 128-bit plaintext blocks will always encrypt to the same ciphertext block, across all files and regardless of offset within the file.
The auth file is confusing me a lot, but it does not look like a simple hash signed with RSA. The file would only be a few kb long, not over 50. I’m not sure what to make of it. Hopefully the parsing code is complicated with lots of bugs in it.