3.11 support!

This commit is contained in:
CTurt 2020-06-29 02:13:44 +01:00
parent 4f7406c061
commit 0ba251a261
31 changed files with 506 additions and 5 deletions

BIN
3.11EU.iso Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

19
3.11EU/build.sh Normal file
View file

@ -0,0 +1,19 @@
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=0x01522944 -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
echo "Done. Insert fullpayload.bin into VIDEO_TS.IFO at offset 0x2ba4"

62
3.11EU/crt0.S Normal file
View file

@ -0,0 +1,62 @@
.set noreorder # If we're writing assembly, why would we want this?
.section .text.startup
.equ getBufferInternal, 0x2952f0
.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
.space (_start + 0x2fe2 - 0x2ba4) - .
fpIndex:
.byte 0x89
.byte 0xc6
.space (_start + 0x3000 - 0x2ba4) - .
.incbin "payload.bin"

BIN
3.11EU/fullpayload.bin Normal file

Binary file not shown.

BIN
3.11EU/fullpayload.elf Normal file

Binary file not shown.

BIN
3.11EU/payload.bin Normal file

Binary file not shown.

141
3.11EU/payload.c Normal file
View 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))0x258a28)
#define getDiscData ((void (*)(unsigned int s, void *d))0x258b98)
#define SifIopReset ((int (*)(char *, int))0x20e7d8)
#define SifIopSync ((int (*)(void))0x20e958)
#define SifInitRpc ((void (*)(int))0x208d80)
#define SifExitRpc ((void (*)(void))0x208f20)
#define getBufferInternal ((int (*)(void *filename, int type, int currentSector, void *dest, unsigned int sectorsRemaining, int curReadPos))0x2952f0)
#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
3.11EU/payload.elf Normal file

Binary file not shown.

19
PAYLOAD_3.11/build.sh Normal file
View file

@ -0,0 +1,19 @@
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=0x01522944 -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
echo "Done. Insert fullpayload.bin into VIDEO_TS.IFO at offset 0x2ba4"

62
PAYLOAD_3.11/crt0.S Normal file
View file

@ -0,0 +1,62 @@
.set noreorder # If we're writing assembly, why would we want this?
.section .text.startup
.equ getBufferInternal, 0x2952f0
.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
.space (_start + 0x2fe2 - 0x2ba4) - .
fpIndex:
.byte 0x89
.byte 0xc6
.space (_start + 0x3000 - 0x2ba4) - .
.incbin "payload.bin"

Binary file not shown.

Binary file not shown.

BIN
PAYLOAD_3.11/payload.bin Normal file

Binary file not shown.

141
PAYLOAD_3.11/payload.c Normal file
View 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))0x258a28)
#define getDiscData ((void (*)(unsigned int s, void *d))0x258b98)
#define SifIopReset ((int (*)(char *, int))0x20e7d8)
#define SifIopSync ((int (*)(void))0x20e958)
#define SifInitRpc ((void (*)(int))0x208d80)
#define SifExitRpc ((void (*)(void))0x208f20)
#define getBufferInternal ((int (*)(void *filename, int type, int currentSector, void *dest, unsigned int sectorsRemaining, int curReadPos))0x2952f0)
#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
PAYLOAD_3.11/payload.elf Normal file

Binary file not shown.

View file

@ -6,17 +6,24 @@ For technical details please refer to my [blog post](https://cturt.github.io/fre
## Step 1: Identify your DVD Player Version ## Step 1: Identify your DVD Player Version
Boot your PlayStation 2 without any disc inserted, and press Triangle to identify which DVD Player version your console has. Boot your PlayStation 2 without any disc inserted, and press Triangle to identify which DVD Player version your console has.
For initial release only DVD Player version 3.10E is supported (as that's the console I have) - UPDATE: someone tested 3.10E exploit on 3.10U and it worked, so I guess region doesn't matter. In the future I may look at other firmware versions, and hopefully over time other developers from the scene will also contribute support for additional DVD Player versions. Don't bother trying 3.10 on a different firmware, it won't work... Not sure if region matters or not, but so far I only tested E and U so J and others aren't guaranteed.
Pre-built ISO files for supported DVD Players containing just uLaunchELF are provided in this repository for ease of use (which can be used to boot homebrew over USB storage), such as `3.10E.iso`. If you intend to make your own image containing additional homebrew / modified initial loader, please read on. Currently only support:
3.10 (E or U)
3.11 (E or U)
Check back here later for more support. Hopefully over time other developers from the scene will also contribute support for additional DVD Player versions. Don't bother trying on a not supported firmware, it won't work...
Pre-built ISO files for supported DVD Players containing just uLaunchELF are provided in this repository for ease of use (which can be used to boot homebrew over USB storage), such as `3.10EU.iso`. If you intend to make your own image containing additional homebrew / modified initial loader, please read on.
## Step 2: Copy your homebrew ## Step 2: Copy your homebrew
Once you've identified your console's DVD Player version, copy all of the homebrew you would like to include on the disc into that directory (EG: `3.10E/`). Once you've identified your console's DVD Player version, copy all of the homebrew you would like to include on the disc into that directory (EG: `3.10EU/`).
## Step 3: Make an image ## Step 3: Make an image
Once you've placed all the homebrew files you'd like into the directory, generate a UDF image of the directory. The easiest way is probably to install `genisoimage` and run the following (where `exploit.iso` is the output and `3.10E` is the directory containing `VIDEO_TS` and any homebrew): Once you've placed all the homebrew files you'd like into the directory, generate a UDF image of the directory. The easiest way is probably to install `genisoimage` and run the following (where `exploit.iso` is the output and `3.10EU` is the directory containing `VIDEO_TS` and any homebrew):
genisoimage -udf -o exploit.iso 3.10E genisoimage -udf -o exploit.iso 3.10EU
## Step 4: Test and burn ## Step 4: Test and burn
I would recommend you test in PCSX2 first, but since [PCSX2 doesn't support loading the DVD Player](https://github.com/PCSX2/pcsx2/issues/1981), you have to decrypt and repack it yourself, which is beyond the scope of this README. With that said, if you aren't touching anything in `VIDEO_TS`, there shouldn't really be any reason for the exploit to fail. I would recommend you test in PCSX2 first, but since [PCSX2 doesn't support loading the DVD Player](https://github.com/PCSX2/pcsx2/issues/1981), you have to decrypt and repack it yourself, which is beyond the scope of this README. With that said, if you aren't touching anything in `VIDEO_TS`, there shouldn't really be any reason for the exploit to fail.

50
porting notes.txt Normal file
View file

@ -0,0 +1,50 @@
3.11
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
0x01522944 looks pretty good, it's at 0x3E4A74, so index would be (0x3E4A74-0x003b3050)/4 = 0xC689
// a backup incase that didn't work would be 0x14431114 (at 0x3B8164)
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!