wiifuse_server+net has been a valuable ally so far, and I also have to thank Segher for releasing zelda-cksum in his git tree.
I've found a few things so far:
- It looks like the bytes 0x2b5..0x2b8 are what end up in the program counter after the overflow, making the start of the exploit code be at address 0x80451ca0
- It looks like the instruction starting at 0x2c0 is part of the exploit code, although the code as far back as 0x1d0 looks like it might be safe to execute. In any case, modifying the bytes at 0x2c0 to 'lwz r1,0(0)' cause the game to crash instead of starting the twilight hack.
- The exploit code is after the end of the NUL-terminated string, so there's no apparent need for the zero-avoidance tricks that are the focus of many ppc-exploit documents.
Before I looked at the disassembly, I had in mind what the exploit might look like; from a glance at this code, it seems that the twiizers team has done something rather more complicated. Basically, I had imagined that the exploit would amount to code implementing
int x = ISFS_Open("/title/.../boot.bin")
ISFS_Read(x, boot_base, boot_size);
boot_base();
(assuming something like ISFS is in Zelda as a recognizable API). This might
assemble to as few as a dozen instructions. However, there are quite a few
more instructions there -- over 500 bytes worth of stuff that is superficially
valid code. I've taken a look at the disassembly and written some notes:
2b4: 33 80 45 1c addic r28,r0,17692
2b8: a0 53 53 53 lhz r2,21331(r19)
2bc: 53 53 53 53 rlwimi. r19,r26,10,13,9
2c0: 3c 20 80 80 lis r1,-32640
2c4: 38 00 00 00 li r0,0
2c8: 94 01 ff c0 stwu r0,-64(r1)
(0x807fffc0) = 0
2cc: 7c 68 02 a6 mflr r3
r3 <= lr
2d0: 48 00 00 40 b _t1
video_clear: // sets the i2cReg and then sets 640*576*2 bytes of memory to in R3
2d4: 3d 20 cd 80 lis r9,-12928
2d8: 61 29 00 c0 ori r9,r9,192
r9 <= 0xcd8000c0 // i2cReg + 48 -- SCL
2dc: 80 09 00 00 lwz r0,0(r9)
r0 <= *r9
2e0: 7c 00 04 ac sync
2e4: 68 00 00 20 xori r0,r0,32 r0 <= r0 ^ 32
2e8: 90 09 00 00 stw r0,0(r9)
*r9 <= r0 // toggle SCL?
2ec: 7c 00 06 ac eieio
2f0: 3c 00 00 02 lis r0,2
2f4: 3d 20 c0 f0 lis r9,-16144 r9 <= 0xc0f00000
2f8: 60 00 d0 00 ori r0,r0,53248 r0 <= 0x2d000
video_loop:
2fc: 34 00 ff ff addic. r0,r0,-1 r0 <= r0 - 1
300: 90 69 00 00 stw r3,0(r9) *r9 <= r3
304: 39 29 00 04 addi r9,r9,4 r9 <= r9+4
308: 40 a2 ff f4 bne- 0x2fc if(r0) goto loop
30c: 4e 80 00 20 blr
_t1: sploit(r3 = void *lr) {
310: 7c 08 02 a6 mflr r0
r0 <= lr
314: 94 21 ff e0 stwu r1,-32(r1)
(0x87fffa0) = 0x87fffc0
318: 3d 20 80 00 lis r9,-32768
r9 = 0x80000000
31c: bf 41 00 08 stmw r26,8(r1)
store regs r26..r31 starting at 0x87fffc8
320: 61 29 00 03 ori r9,r9,3
r9 = 0x80000003
324: 90 01 00 24 stw r0,36(r1)
(0x87fffc4) = lr
328: 88 09 00 00 lbz r0,0(r9)
r0 = (0x80000003) -- region code of game ID?
32c: 2f 80 00 45 cmpwi cr7,r0,69
330: 41 9e 00 10 beq- cr7,0x340 region 'E'
334: 2f 80 00 50 cmpwi cr7,r0,80
338: 41 9e 00 40 beq- cr7,0x378 region 'P'
33c: 48 00 00 38 b 0x374 unknown region
region_e:
340: 6c 60 80 45 xoris r0,r3,32837
344: 2f 80 19 e0 cmpwi cr7,r0,6624 r3 (old lr) == 0x804519e0
348: 40 9e 00 18 bne- cr7,0x360 flavor 1
flavor_2:
34c: 3d 20 80 36 lis r9,-32714
350: 3b 49 c1 48 addi r26,r9,-16056 r26 = 0x8036c148
354: 3d 20 80 36 lis r9,-32714
358: 3b 89 c9 88 addi r28,r9,-13944 r28 = 0x8036c988
35c: 48 00 00 2c b 0x388 goto common
flavor_1:
360: 3d 20 80 37 lis r9,-32713
364: 3b 49 17 10 addi r26,r9,5904 r26 = 0x80371710
368: 3d 20 80 37 lis r9,-32713
36c: 3b 89 1f 50 addi r28,r9,8016 r28 = 0x80371f50
370: 48 00 00 18 b 0x388 goto common
die:
374: 48 00 00 00 b 0x374 loop forever
region_p:
378: 3d 20 80 36 lis r9,-32714
37c: 3b 49 c5 78 addi r26,r9,-14984 r26 = 0x8036c578
380: 3d 20 80 36 lis r9,-32714
384: 3b 89 cd b8 addi r28,r9,-12872 r28 = 0x8036cdb8
common:
388: 3c 60 26 6a lis r3,9834
38c: 3f a0 80 45 lis r29,-32699
390: 60 63 26 c0 ori r3,r3,9920 r3 = 0x266a26c0
394: 3b bd 1e c0 addi r29,r29,7872 r29 = 0x80451ec0
398: 4b ff ff 3d bl 0x2d4
call video_clear -- clear to maroon
39c: 3c 60 80 45 lis r3,-32699
3a0: 7f 89 03 a6 mtctr r28 ctr <= r28 (second region/flavor dependent function)
3a4: 7f a4 eb 78 mr r4,r29 r4 <= r29
3a8: 38 a0 00 01 li r5,1 r5 <= 1
3ac: 38 63 1f c0 addi r3,r3,8128 r3 <= 0x80451fc0 // filename pointer
3b0: 3f c0 40 80 lis r30,16512
3b4: 3f e0 90 00 lis r31,-28672
3b8: 3f 60 c0 80 lis r27,-16256
3bc: 63 de 40 80 ori r30,r30,16512 r30 <= 0x3f184080
3c0: 63 ff 00 20 ori r31,r31,32 r31 <= 0x90000020
3c4: 63 7b c0 80 ori r27,r27,49280 r27 <= COLOR_SILVER
3c8: 4e 80 04 21 bctrl call df1 // open loader.bin?
df1(r3="loader.bin",
r4=handle? (after code, 256 bytes before loader.bin),
r5=1 (read in ISFS_Open))
3cc: 3c 60 71 40 lis r3,28992
3d0: 60 63 71 8a ori r3,r3,29066 r3 <= 0x7140718a
3d4: 3b 80 00 00 li r28,0 r28 <= 0
3d8: 4b ff fe fd bl 0x2d4 call video_clear // clear to OLIVE
read_loop:
3dc: 7f c3 f3 78 mr r3,r30 r3 <= r30
3e0: 4b ff fe f5 bl 0x2d4 call video_clear // clear to ????
// Cache buster -- clear cache for physical range about to be read
3e4: 39 3f 10 1f addi r9,r31,4127 r9 <= 0x4000101f
3e8: 55 29 00 34 rlwinm r9,r9,0,0,26 r9 <= r9 & ~0x1f
3ec: 57 e0 00 34 rlwinm r0,r31,0,0,26 r0 <= r31 & ~0x1f
3f0: 7d 20 48 50 subf r9,r0,r9 r9 <= byte count r9 .. r31
3f4: 55 29 d9 7e rlwinm r9,r9,27,5,31 r9 <= r9 >> 5
3f8: 39 29 00 01 addi r9,r9,1 r9 <= r9 + 1 // size in 0x20 blocks, rounded up
3fc: 7d 29 03 a6 mtctr r9 and move to counter register
400: 7f e9 fb 78 mr r9,r31 r9 <= 0x40000020
404: 48 00 00 0c b 0x410
loop2:
408: 7c 00 48 ac dcbf r0,r9
40c: 39 29 00 20 addi r9,r9,32
410: 42 00 ff f8 bdnz+ 0x408
414: 7c 00 04 ac sync
418: 38 a0 10 00 li r5,4096
41c: 7f e4 fb 78 mr r4,r31
420: 7f 49 03 a6 mtctr r26
424: 7f a3 eb 78 mr r3,r29
428: 3b ff 10 00 addi r31,r31,4096
42c: 4e 80 04 21 bctrl r3 <= read(handle?, 0x50000020, 4096) // read block?
430: 3d 3e 14 ab addis r9,r30,5291 r9 = r30 + (5291<<16) /* 0x14ab0000 */
434: 7f 9c 1a 14 add r28,r28,r3 r28 += r3
438: 38 69 14 95 addi r3,r9,5269 r3 = r9 + 5269
43c: 4b ff fe 99 bl 0x2d4 clear to a color dependant on return value?
440: 3d 3e 02 00 addis r9,r30,512 r9 = r30 + 0x2000200
444: 3b c9 02 00 addi r30,r9,512 r30 = r9 + 0x2000200
448: 7f 9e d8 00 cmpw cr7,r30,r27
// did the compiler come up with this f***ed up loop termination
// or is there a human to blame?
44c: 40 9e ff 90 bne+ cr7,0x3dc if(r27 != r30) goto read_loop // 64 loops?
450: 3c 60 c3 99 lis r3,-15463
454: 60 63 c3 6a ori r3,r3,50026
458: 4b ff fe 7d bl 0x2d4 clear to SKYBLUE
45c: 3d 3c 90 00 addis r9,r28,-28672 .. and at this point boredom set in
460: 39 29 00 3f addi r9,r9,63 .. because it looks like we're home free
464: 3d 60 90 00 lis r11,-28672
468: 55 29 00 34 rlwinm r9,r9,0,0,26
46c: 61 6b 00 20 ori r11,r11,32 r11 = 0x90000020
470: 3d 29 70 00 addis r9,r9,28672
474: 39 29 ff e0 addi r9,r9,-32
478: 55 29 d9 7e rlwinm r9,r9,27,5,31
47c: 39 29 00 01 addi r9,r9,1
480: 7d 29 03 a6 mtctr r9 r9 = how much to clear, in cache lines
484: 48 00 00 10 b 0x494
loop3: // flush data cache
488: 7c 00 58 6c dcbst r0,r11
48c: 7c 00 5f ac icbi r0,r11
490: 39 6b 00 20 addi r11,r11,32
494: 42 00 ff f4 bdnz+ 0x488
498: 7c 00 04 ac sync
49c: 4c 00 01 2c isync
4a0: 3c 00 90 00 lis r0,-28672
4a4: 39 20 01 23 li r9,291
4a8: 60 00 00 20 ori r0,r0,32
4ac: 7d 23 4b 78 mr r3,r9
4b0: 7c 09 03 a6 mtctr r0
4b4: 4e 80 04 21 bctrl call 0x90000020 (r3=291) -- loader.bin?
4b8: 3c 60 4c 54 lis r3,19540 returned from loader.bin? unpossible!
4bc: 60 63 4c ff ori r3,r3,19711
4c0: 4b ff fe 15 bl 0x2d4 clear to RED
4c4: 48 00 00 00 b 0x4c4 ... and die
OK, a bit more than a dozen instructions, but not far off from what I thought.
There's code to set the screen color and some kind of I/O as each step of the
load occurs. There's code to switch between the different code versions
supported (E0, E2, P). And there's a lot of cache-related code that I would
never have figured out on my own.
Entry first conceived on 9 June 2008, 22:29 UTC, last modified on 15 January 2012, 3:46 UTC
Website Copyright © 2004-2024 Jeff Epler