9 Commits
9 changed files with 141 additions and 43 deletions

No files matched your search

+4 -13
View File
@@ -28,34 +28,25 @@ LDADD := -lSceSsl -lSceHttp
all: elfldr-ps5.elf
socksrv_elf.c: socksrv.elf
bootstrap_elf.c: bootstrap.elf
bootstrap.o: socksrv_elf.c
main.o: bootstrap_elf.c
bootstrap.o: socksrv.elf
main.o: bootstrap.elf
bootstrap.elf: bootstrap.o elfldr.o pt.o notify.o
$(CC) -o $@ $^
$(STRIP) $@
bootstrap_elf.c: bootstrap.elf
xxd -i $^ > $@
socksrv.elf: socksrv.o selfldr.o elfldr.o pt.o notify.o uri.o
$(CC) -o $@ $^ $(LDADD)
$(STRIP) $@
socksrv_elf.c: socksrv.elf
xxd -i $^ > $@
elfldr-ps5.elf: main.o elfldr.o pt.o notify.o
$(CC) -o $@ $^
$(STRIP) $@
clean:
rm -f bootstrap_elf.c socksrv_elf.c *.o *.elf
rm -f *.o *.elf
test: elfldr-ps5.elf
$(PS5_DEPLOY) -h $(PS5_HOST) -p $(PS5_PORT) $^
.INTERMEDIATE: socksrv_elf.c socksrv.elf bootstrap_elf.c bootstrap.elf
.INTERMEDIATE: socksrv.elf bootstrap.elf
+3
View File
@@ -30,6 +30,9 @@ john@localhost:~$ echo "$PS5_PAYLOAD" | nc -q0 $PS5_HOST $PS5_PORT
john@localhost:~$
john@localhost:~$ export PS5_PAYLOAD=https://github.com/ps5-payload-dev/ftpsrv/releases/latest/download/ftpsrv-ps5.elf
john@localhost:~$ echo "$PS5_PAYLOAD" | nc -q0 $PS5_HOST $PS5_PORT
john@localhost:~$
john@localhost:~$ export PS5_PAYLOAD=https://github.com/ps5-payload-dev/ftpsrv/releases/latest/download/ftpsrv-ps5.elf
john@localhost:~$ curl "http://$PS5_HOST:$PS5_PORT?uri=$PS5_PAYLOAD"
```
## Building
+32
View File
@@ -0,0 +1,32 @@
/* Copyright (C) 2026 John Törnblom
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 3, or (at your option) any
later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; see the file COPYING. If not, see
<http://www.gnu.org/licenses/>. */
#pragma once
#define INCASSET(name, file) \
__asm__(".section .rodata\n" \
".global " #name "\n" \
".global " #name "_end\n" \
".global " #name "_size\n" \
".align 16\n" \
#name ":\n" \
".incbin \"" file "\"\n" \
#name "_end:\n" \
#name "_size:\n" \
".quad " #name "_end - " #name "\n" \
".previous\n"); \
extern const unsigned char name[]; \
extern const unsigned long name##_size;
+4 -4
View File
@@ -19,13 +19,13 @@ along with this program; see the file COPYING. If not, see
#include <ps5/kernel.h>
#include "asset.h"
#include "elfldr.h"
#include "log.h"
#include "notify.h"
#include "pt.h"
#include "socksrv_elf.c"
INCASSET(socksrv_elf, "socksrv.elf");
/**
@@ -48,7 +48,7 @@ main() {
notify("Spawning elfldr.elf...");
LOG_PUTS("Spawning elfldr.elf...");
if(elfldr_sanity_check(socksrv_elf, socksrv_elf_len)) {
if(elfldr_sanity_check(socksrv_elf, socksrv_elf_size)) {
LOG_PUTS("socksrv.elf is corrupted");
return -1;
}
@@ -73,7 +73,7 @@ main() {
ret = -1;
} else {
signal(SIGCHLD, SIG_IGN);
ret = elfldr_spawn(-1, argv, socksrv_elf, socksrv_elf_len);
ret = elfldr_spawn(-1, argv, socksrv_elf, socksrv_elf_size);
}
// restore my privileges
+16 -16
View File
@@ -59,8 +59,8 @@ along with this program; see the file COPYING. If not, see
* Context structure for the ELF loader.
**/
typedef struct elfldr_ctx {
uint8_t* elf;
pid_t pid;
const uint8_t* elf;
pid_t pid;
intptr_t base_addr;
size_t base_size;
@@ -78,7 +78,7 @@ static const char* SceSpZeroConf = "/system/vsh/app/NPXS40112/eboot.bin";
* Parse a R_X86_64_RELATIVE relocatable.
**/
static int
r_relative(elfldr_ctx_t *ctx, Elf64_Rela* rela) {
r_relative(elfldr_ctx_t *ctx, const Elf64_Rela* rela) {
intptr_t* loc = ctx->base_mirror + rela->r_offset;
intptr_t val = ctx->base_addr + rela->r_addend;
@@ -92,7 +92,7 @@ r_relative(elfldr_ctx_t *ctx, Elf64_Rela* rela) {
* Parse a PT_LOAD program header.
**/
static int
data_load(elfldr_ctx_t *ctx, Elf64_Phdr *phdr) {
data_load(elfldr_ctx_t *ctx, const Elf64_Phdr *phdr) {
void* data = ctx->base_mirror + phdr->p_vaddr;
if(!phdr->p_memsz) {
@@ -110,9 +110,9 @@ data_load(elfldr_ctx_t *ctx, Elf64_Phdr *phdr) {
int
elfldr_sanity_check(uint8_t *elf, size_t elf_size) {
Elf64_Ehdr *ehdr = (Elf64_Ehdr*)elf;
Elf64_Phdr *phdr;
elfldr_sanity_check(const uint8_t *elf, size_t elf_size) {
const Elf64_Ehdr *ehdr = (const Elf64_Ehdr*)elf;
const Elf64_Phdr *phdr;
if(elf_size < sizeof(Elf64_Ehdr) ||
elf_size < sizeof(Elf64_Phdr) + ehdr->e_phoff ||
@@ -125,7 +125,7 @@ elfldr_sanity_check(uint8_t *elf, size_t elf_size) {
return -1;
}
phdr = (Elf64_Phdr*)(elf + ehdr->e_phoff);
phdr = (const Elf64_Phdr*)(elf + ehdr->e_phoff);
for(int i=0; i<ehdr->e_phnum; i++) {
if(phdr[i].p_offset + phdr[i].p_filesz > elf_size) {
return -1;
@@ -140,10 +140,10 @@ elfldr_sanity_check(uint8_t *elf, size_t elf_size) {
* Load an ELF into the address space of a process with the given pid.
**/
static intptr_t
elfldr_load(pid_t pid, uint8_t *elf) {
Elf64_Ehdr *ehdr = (Elf64_Ehdr*)elf;
Elf64_Phdr *phdr = (Elf64_Phdr*)(elf + ehdr->e_phoff);
Elf64_Shdr *shdr = (Elf64_Shdr*)(elf + ehdr->e_shoff);
elfldr_load(pid_t pid, const uint8_t *elf) {
const Elf64_Ehdr *ehdr = (const Elf64_Ehdr*)elf;
const Elf64_Phdr *phdr = (const Elf64_Phdr*)(elf + ehdr->e_phoff);
const Elf64_Shdr *shdr = (const Elf64_Shdr*)(elf + ehdr->e_shoff);
elfldr_ctx_t ctx = {.elf = elf, .pid=pid};
@@ -207,7 +207,7 @@ elfldr_load(pid_t pid, uint8_t *elf) {
continue;
}
Elf64_Rela* rela = (Elf64_Rela*)(elf + shdr[i].sh_offset);
const Elf64_Rela* rela = (const Elf64_Rela*)(elf + shdr[i].sh_offset);
for(int j=0; j<shdr[i].sh_size/sizeof(Elf64_Rela); j++) {
switch(rela[j].r_info & 0xffffffffl) {
case R_X86_64_RELATIVE:
@@ -348,7 +348,7 @@ elfldr_payload_args(pid_t pid) {
* Prepare registers of a process for execution of an ELF.
**/
static int
elfldr_prepare_exec(pid_t pid, uint8_t *elf) {
elfldr_prepare_exec(pid_t pid, const uint8_t *elf) {
intptr_t entry;
intptr_t args;
struct reg r;
@@ -437,7 +437,7 @@ elfldr_raise_privileges(pid_t pid) {
* Execute an ELF inside the process with the given pid.
**/
int
elfldr_exec(pid_t pid, int stdio, uint8_t* elf) {
elfldr_exec(pid_t pid, int stdio, const uint8_t* elf) {
uint8_t caps[16];
intptr_t jaildir;
intptr_t rootdir;
@@ -600,7 +600,7 @@ elfldr_rfork_entry(void* argv) {
* Execute an ELF inside a new process.
**/
pid_t
elfldr_spawn(int stdio, char* const argv[], uint8_t* elf, size_t payload_size) {
elfldr_spawn(int stdio, char* const argv[], const uint8_t* elf, size_t payload_size) {
uint8_t int3instr = 0xcc;
struct kevent evt;
intptr_t brkpoint;
+4 -3
View File
@@ -28,13 +28,14 @@ pid_t elfldr_find_pid(const char* name);
/**
* Spawn a new process that executes the given ELF file.
**/
pid_t elfldr_spawn(int stdio, char* const argv[], uint8_t *elf, size_t elf_size);
pid_t elfldr_spawn(int stdio, char* const argv[], const uint8_t *elf,
size_t elf_size);
/**
* Execute an ELF file in a given process.
**/
int elfldr_exec(pid_t pid, int stdio, uint8_t* elf);
int elfldr_exec(pid_t pid, int stdio, const uint8_t* elf);
/**
@@ -43,7 +44,7 @@ int elfldr_exec(pid_t pid, int stdio, uint8_t* elf);
int elfldr_read(int fd, uint8_t** elf, size_t* elf_size);
int elfldr_sanity_check(uint8_t *elf, size_t elf_size);
int elfldr_sanity_check(const uint8_t *elf, size_t elf_size);
int elfldr_raise_privileges(pid_t pid);
+3 -2
View File
@@ -16,12 +16,13 @@ along with this program; see the file COPYING. If not, see
#include <ps5/kernel.h>
#include "asset.h"
#include "elfldr.h"
#include "log.h"
#include "notify.h"
#include "pt.h"
#include "bootstrap_elf.c"
INCASSET(bootstrap_elf, "bootstrap.elf");
/**
@@ -40,7 +41,7 @@ main() {
notify("Bootstrapping elfldr.elf...");
LOG_PUTS("Bootstrapping elfldr.elf...");
if(elfldr_sanity_check(bootstrap_elf, bootstrap_elf_len)) {
if(elfldr_sanity_check(bootstrap_elf, bootstrap_elf_size)) {
LOG_PUTS("bootstrap.elf is corrupted");
return -1;
}
+70 -4
View File
@@ -39,10 +39,11 @@ along with this program; see the file COPYING. If not, see
* Magic number that socket input starts with (little endian).
**/
#define PAYLOAD_MAGIC_ELF 0x464C457F // ELF payload
#define PAYLOAD_MAGIC_FILE 0x656C6966 // file:// URI
#define PAYLOAD_MAGIC_HTTP 0x70747468 // http:// or https:// URI
#define PAYLOAD_MAGIC_URI_FILE 0x656C6966 // file:// URI
#define PAYLOAD_MAGIC_URI_HTTP 0x70747468 // http:// or https:// URI
#define PAYLOAD_MAGIC_PS4_SELF 0x1D3D154F // PS4 SELF payload
#define PAYLOAD_MAGIC_PS5_SELF 0xEEF51454 // PS5 SELF payload
#define PAYLOAD_MAGIC_HTTP_GET 0x20544547 // HTTP GET request
/**
@@ -175,6 +176,53 @@ payload_readuri(int fd, char* uri, size_t size) {
}
/**
* Read a HTTP (GET) request.
**/
static int
payload_readhttp(int fd, char* uri, size_t size) {
const char *headers = ("HTTP/1.1 200 OK\r\n"
"Content-Type: text/plain; charset=utf-8\r\n"
"Access-Control-Allow-Origin: *\r\n"
"Connection: close\r\n"
"\r\n");
char buf[PATH_MAX+255] = {0};
char* param_uri;
ssize_t n;
char* p;
if((n=read(fd, buf, sizeof(buf)-1)) < 0) {
return -1;
}
if(memcmp(buf, "GET ", 4)) {
return -1;
}
if(!(p=strchr(buf+4, ' '))) {
return -1;
}
if(strstr(buf, "user-agent: rnps-action-cards")) {
return -1;
}
*p = 0;
if((param_uri=uri_get_param(buf+4, "uri"))) {
strncpy(uri, param_uri, size);
free(param_uri);
} else {
snprintf(uri, size, "file:/%s", buf+4);
}
write(fd, headers, strlen(headers));
return 0;
}
/**
* Process connection input.
**/
@@ -186,6 +234,7 @@ on_connection(int fd) {
size_t len = 0;
int optval = 1;
char* args = "";
char* pipe = "";
int magic = 0;
if(setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &optval, sizeof(optval)) < 0) {
@@ -199,12 +248,18 @@ on_connection(int fd) {
return;
}
if(magic == PAYLOAD_MAGIC_FILE || magic == PAYLOAD_MAGIC_HTTP) {
if(magic == PAYLOAD_MAGIC_URI_FILE || magic == PAYLOAD_MAGIC_URI_HTTP) {
if(payload_readuri(fd, uri, PATH_MAX) || uri_get_content(uri, &buf, &len)) {
LOG_PERROR("read_uri");
LOG_PERROR("payload_readuri");
write(fd, "[elfldr.elf] Error reading URI payload\n\r\0", 41);
}
} else if(magic == PAYLOAD_MAGIC_HTTP_GET) {
if(payload_readhttp(fd, uri, PATH_MAX) || uri_get_content(uri, &buf, &len)) {
LOG_PERROR("payload_readhttp");
write(fd, "[elfldr.elf] Error reading HTTP payload\n\r\0", 42);
}
} else if(magic == PAYLOAD_MAGIC_ELF) {
if(elfldr_read(fd, &buf, &len)) {
LOG_PERROR("elfldr_read");
@@ -227,10 +282,21 @@ on_connection(int fd) {
if(!(args=uri_get_param(uri, "args"))) {
args = strdup("");
}
if(!(pipe=uri_get_param(uri, "pipe"))) {
pipe = strdup("1");
}
if(!strcmp(pipe, "0") ||
!strcasecmp(pipe, "false") ||
!strcasecmp(pipe, "no")) {
fd = -1;
}
if(payload_spawn(filename, args, fd, buf, len) < 0) {
write(fd, "[elfldr.elf] Error spawning payload\n\r\0", 38);
}
free(filename);
free(pipe);
free(args);
free(buf);
}
+5 -1
View File
@@ -336,9 +336,13 @@ uri_get_path(const char* uri) {
char*
uri_get_filename(const char* uri) {
size_t len = strlen(uri);
const char* begin = 0;
const char* end = 0;
size_t len;
if(!(len=strlen(uri))) {
return 0;
}
end = uri + len;
for(int i=0; i<len; i++) {