Exploring the Pippin ROM(s), part 2

I’ve spent the last couple of evenings taking a closer look at the Pippin 1.0 ROM—specifically the boot process—trying to determine precisely how it verifies that a provided boot disc is in fact signed properly before passing it off to get loaded.

The earliest parts of the Pippin ROM are not much different from the late Quadra “universal” ROMs, which kind of makes sense given how close the Pippin is to the first couple generations of Power Macs. It deviates in a few places by writing to some areas of high memory for reasons I haven’t yet deduced, but is otherwise pretty straightforward compared to a real Mac—in accordance with being derived from a “universal” ROM, it retains the checks for various 68K processors and their capabilities, even despite only having the 68LC040 emulator underneath.

Where things start to get interesting is after the ROM initializes the SCSI Manager. It then looks for an ‘iNiT’ 1 resource (note the capitalization) and executes it, followed by an ‘iNiT’ resource named “Install XFS.” I haven’t yet dug into these segments to see what is happening here, but somehow I don’t think the latter block is installing drivers for a popular filesystem… 😉

Elliot Nunn pointed out to me that the boot process is part of the Start Manager and hadn’t changed much in the years leading up to the Pippin’s release. He also kindly suggested that I specifically search for FindStartupDevice.

So I did that.

I found a few interesting things.

FindStartupDevice pretty much follows the same steps as a real Mac… until we find valid boot blocks. Then it runs this little snippet of code:

1592   303C FFDC        Move      #-36, D0
1596   322A 0008        Move      dqRefNum(A2), D1
159A   B240             Cmp       D0, D1
159C   66C0             BNE.B     @TryAgain

-36 is the refNum for the internal CD-ROM drive. What this code does is check to see if our current drive queue entry is using the .AppleCD driver with the internal drive. If it’s not, it loops back to search for other potential boot volumes. Looks a bit like a hotfix and/or conditionally compiled to me (Why didn’t they just refactor the code so that it only searches the CD-ROM drive? Hey, I wasn’t there…), but essentially this means definitively that a 1.0 Pippin will not fully boot from any device other than its internal optical drive.

After this code is where things start to heat up. Take a look:

159E   2F3C FFFF FFFF   Move.L    #-1, -(A7)
15A4   4EBA 0B0A        Jsr       @mysterySub1          ; hmmm...
15A8   588F             AddQ.L    #4, A7
15AA   4EBA 1C84        Jsr       @mysterySub2          ; HMMM...
15AE   4A40             Tst       D0
15B0   6738             BEQ.B     @GotIt                ; success! boot!
15B2   303C FFDC        Move      #-36, D0
15B6   B06A 0008        Cmp       dqRefNum(A2), D0
15BA   66A2             BNE.B     @TryAgain
15BC   4FEF FFCE        Lea.L     -ioQElSize(A7), A7
15C0   204F             MoveA.L   A7, A0
15C2   317C FFDC 0018   Move      #-36, ioRefNum(A0)    ; .AppleCD
15C8   4268 0016        Clr       ioVRefNum(A0)
15CC   42A8 0012        Clr.L     ioNamePtr(A0)
15D0   317C 0007 001A   Move      #7, csCode(A0)        ; eject the disc
15D6   A004             _Control
15D8   3028 0010        Move      ioResult(A0), D0
15DC   4FEF 0032        Lea.L     ioQElSize(A7), A7
15E0   3F3C 0002        Move      #2, -(A7)             ; ShutDwnStart
15E4   A895             _ShutDown                       ; restart the Pippin
15E6   6000 FF76        Bra       @TryAgain
@GotIt
15EA   4A78 08D0        Tst       (CrsrState)
15EE   6B02             BMI.B     @ShowHappyMac
15F0   A852             _HideCursor
@ShowHappyMac

Immediately before making the decision to advance to the “Happy Mac” state (such as it is on the Pippin), this block of code passes -1 on the stack to a mystery subroutine. Then, it calls a second mystery subroutine, the result of which, if zero, indicates the Pippin is free and clear to boot from that volume (provided it’s the CD-ROM drive—again with that check!). If the check fails, the disc is ejected and the Pippin restarts.

So, let’s start with mysterySub1. mysterySub1 calls down to $20B0, where this happens:

20B0   60FF 000C AB0E    Bra.L     @mysterySub3

Hmmm. OK. So where does that take us? We end up in a short subroutine that loads a ‘nint’ 43 resource, then through a series of calls to _CodeFragmentDispatch we jump into InitAnimation. A-ha! ‘nint’ 43 starts with the string “Joy!peffpwpc” indicating that it’s PPC code, and a list of symbols at its end suggests it draws the “tray loading” animation using a loop of _DrawPicture and associated Color QuickDraw calls. Neat.

But mysterySub2 is where things get really juicy. Check it out:

3230   48E7 3030         MoveM.L   D2-D3/A2-A3, -(A7)
3234   2078 0DDC         MoveA.L   (BootGlobPtr), A0
3238   41E8 FF7E         Lea.L     -$82(A0), A0
323C   20B8 020C         Move.L    (Time), (A0)
3240   594F              SubQ      #4, A7
3242   2F3C 7276 7072    Move.L    #'rvpr', -(A7)       ; 'rvpr' resource
3248   4267              Clr       -(A7)                ; ID 0
324A   A9A0              _GetResource
324C   221F              Move.L    (A7)+, D1
324E   6700 0050         BEQ       @fail
3252   2041              MoveA.L   D1, A0
3254   2648              MoveA.L   A0, A3               ; A3 = handle to loaded 'rvpr' resource
3256   594F              SubQ      #4, A7
3258   2F08              Move.L    A0, -(A7)
325A   A9A5              _SizeRsrc
325C   201F              Move.L    (A7)+, D0
325E   2600              Move.L    D0, D3               ; D3 = size of loaded 'rvpr' resource
3260   A71E              _NewPtrSysClear
3262   6600 003C         BNE       @fail
3266   2F08              Move.L    A0, -(A7)
3268   2003              Move.L    D3, D0
326A   2248              MoveA.L   A0, A1
326C   204B              MoveA.L   A3, A0
326E   2050              MoveA.L   (A0), A0
3270   A02E              _BlockMove                     ; copy 'rvpr' resource into new ptr
3272   2F0B              Move.L    A3, -(A7)
3274   A9A3              _ReleaseResource
3276   2657              MoveA.L   (A7), A3             ; A3 -> our 'rvpr' resource data
3278   554F              SubQ      #2, A7
327A   3F2A 0008         Move      dqRefNum(A2), -(A7)
327E   3F2A 0006         Move      dqDrive(A2), -(A7)
3282   41FA FF5C         Lea.L     someData, A0
3286   2F08              Move.L    A0, -(A7)
3288   41FA FF46         Lea.L     someOtherData, A0
328C   2F10              Move.L    (A0), -(A7)
328E   4E93              Jsr       (A3)
3290   301F              Move      (A7)+, D0
3292   205F              MoveA.L   (A7)+, A0
3294   3F00              Move      D0, -(A7)
3296   A01F              _DisposePtr
3298   301F              Move      (A7)+, D0
@exit
329A   4CDF 0C0C         MoveM.L   (A7)+, D2-D3/A2-A3
329E   4E75              Rts
@fail
32A0   303C FFFF         Move      #-1, D0
32A4   60F4              Bra.B     @exit

That’s a bit to take in, but here’s a summary. We load ‘rvpr’ 0, then copy it into a new block of memory within the system heap. Then we pass the current DCE refNum, drive, a pointer to four longs (the first of which having the value $4B, or 75), and then a pointer to a much larger data block immediately preceding this subroutine to our local copy of ‘rvpr’ 0. It returns a 16-bit result code on the stack, which we save before disposing of our local copy of ‘rvpr’ 0, then we return. From examining what FindStartupDevice does earlier, the result of ‘rvpr’ 0 must be zero in order for the Pippin to complete the startup process.

So what’s in ‘rvpr’ 0?

'rvpr' 0 in 0xED
Anything stand out to you?

GetVolAuthFileInfo
CleanseInputChunk
VerifyDigestInfo
VerifySignature
CreateDigest
CompareDigest
RangedRand
CleanseVCB
GetVAFileInfoGivenMDB
and… InitRSAAlgorithmChooser

That smells like paydirt to me, at least in the 1.0 ROM. The best part is that it’s written in 68K, my reading comprehension skills of which are much better than that of PowerPC assembly. Curiously, there are no ‘rvpr’ resources in ROMs 1.2 or 1.3, even though 1.2 also does the auth check. I’m interested in discovering what replaces it in 1.2, but for now I will continue to investigate 1.0’s implementation. Stay tuned. 🙂

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

  1. Sure looks promising! Can you find those strings *anywhere* in later ROM versions?

    There don’t seem to be any obvious holes in this integrity check. But could it be possible for LoadSCSIDrivers to run some code from disk before the check?

    Failing that, have you got any thoughts about how to “take over” an existing signed Pippin disk without altering its digest? For example, is it possible to sneak a disk image into an existing file, along with some code to “switch boot” that image?

    I eagerly await the next episode!

    1. Those strings are present in ROMs 1.2 and 1.3. It looks like the data is there even though there are no entries for it in either ROM’s resource map. If this code is called in 1.2 then perhaps its location in ROM is hardcoded elsewhere.

      I spent some time recently going over how the Pippin boots from SCSI. I have a post in progress that should be going up over the weekend. 🙂

  2. If you replace the first part of the ‘rvpr’ with the following code, would it cause the check to succeed? I think it would work, but I am not sure I used the right stack offsets.
    movea.l (sp), $000C(sp)
    adda.l $000C, sp
    clr.w $0004(sp)
    rts

    1. I suppose my question is better worded as “If the _BlockMove trap were to ‘accidentally’ copy some totally different string of instructions instead of the ‘rvpr’ resource, what would you want that string of instructions to be?”. I have already written code that will probably do this, but I am not sure what the payload should be.

Leave a Reply to Daniel Cancel 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.