Exploring the Pippin ROM(s), part 3

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?)

3 thoughts on “Exploring the Pippin ROM(s), part 3”

  1. 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?

  2. 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!

  3. 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.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.