Skip to content

Commit

Permalink
add experimental api for payload elf loader
Browse files Browse the repository at this point in the history
  • Loading branch information
john-tornblom committed Oct 8, 2024
1 parent 5f277a2 commit 4cc9e0a
Show file tree
Hide file tree
Showing 9 changed files with 470 additions and 37 deletions.
23 changes: 23 additions & 0 deletions assets/elfldr.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ELF Launcher</title>
<link rel="stylesheet" href="xterm.css">
</head>
<body>
<script src="xterm.js"></script>

<form action="/elfldr" method="POST" enctype="multipart/form-data">
<label for="elf">ELF:</label>
<input type="file" id="elf" name="elf" required><br>

<label for="args">Args:</label>
<input type="text" id="args" name="args"><br>

<input type="submit" value="Deploy">
</form>
</body>
</html>

45 changes: 45 additions & 0 deletions host_tools/ps5-elfldr
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#!/usr/bin/env bash
# Copyright (C) 2024 John Törnblom
#
# This file 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 of the License, 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/>

PS5_HOST=


while getopts "h:" opt; do
case ${opt} in
h) PS5_HOST=$OPTARG;;
esac
done
shift $((OPTIND -1))

if [[ -z $PS5_HOST || -z $1 ]]; then
echo "Usage: $(basename $0) -h HOST PAYLOAD [ARGS...]" >&2
exit 1
fi

PAYLOAD_PATH=$1
PAYLOAD_NAME=$(basename "$PAYLOAD_PATH")
shift

PAYLOAD_ARGS=${PAYLOAD_NAME// /\\ }
for ARG in "$@"; do
PAYLOAD_ARGS="${PAYLOAD_ARGS} ${ARG// /\\ }"
done

curl --form "elf=@${PAYLOAD_PATH}" \
--url-query pipe="1" \
--url-query args="$PAYLOAD_ARGS" \
http://$PS5_HOST:8080/elfldr
21 changes: 21 additions & 0 deletions src/pc/sys.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ along with this program; see the file COPYING. If not, see

#include <stdio.h>
#include <unistd.h>
#include <stdint.h>
#include <stdlib.h>

#include <sys/stat.h>


int
Expand Down Expand Up @@ -45,3 +49,20 @@ sys_launch_homebrew(const char* cwd, const char* path, const char* args,

return fileno(pf);
}


int
sys_launch_payload(const char* cwd, uint8_t* elf, size_t elf_size,
const char* args, const char* env) {
char filename[] = "/tmp/elfXXXXXX";
mktemp(filename);

FILE *f = fopen(filename, "wx");
fwrite(elf, elf_size, 1, f);
fclose(f);

chmod(filename, 0777);

return sys_launch_homebrew(cwd, filename, args, env);
}

120 changes: 120 additions & 0 deletions src/ps5/elfldr.c
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,15 @@ typedef struct elfldr_ctx {
} elfldr_ctx_t;


/**
* Absolute path to the SceSpZeroConf eboot.
**/
static const char* SceSpZeroConf = "/system/vsh/app/NPXS40112/eboot.bin";


int sceKernelSpawn(int *pid, int dbg, const char *path, char *root, char** argv);


/**
* Parse a R_X86_64_RELATIVE relocatable.
**/
Expand Down Expand Up @@ -376,6 +385,32 @@ elfldr_payload_args(pid_t pid) {
}


int
elfldr_raise_privileges(pid_t pid) {
static const uint8_t caps[16] = {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff};
intptr_t vnode;

if(!(vnode=kernel_get_root_vnode())) {
return -1;
}
if(kernel_set_proc_rootdir(pid, vnode)) {
return -1;
}
if(kernel_set_proc_jaildir(pid, 0)) {
return -1;
}
if(kernel_set_ucred_uid(pid, 0)) {
return -1;
}
if(kernel_set_ucred_caps(pid, caps)) {
return -1;
}

return 0;
}


/**
* Prepare registers of a process for execution of an ELF.
**/
Expand Down Expand Up @@ -644,3 +679,88 @@ elfldr_exec(pid_t pid, uint8_t* elf) {
return 0;
}


/**
* Execute an ELF inside a new process.
**/
pid_t
elfldr_spawn(const char* cwd, int stdio, uint8_t* elf, char** argv,
char** envp) {
uint8_t int3instr = 0xcc;
intptr_t brkpoint;
uint8_t orginstr;
pid_t pid = -1;

if(sceKernelSpawn(&pid, 1, SceSpZeroConf, 0, argv)) {
perror("sceKernelSpawn");
return -1;
}

elfldr_raise_privileges(pid);

// The proc is now in the STOP state, with the instruction pointer pointing
// at the libkernel entry. Let the kernel assign process parameters accessed
// via sceKernelGetProcParam()
if(pt_syscall(pid, 599)) {
puts("sys_dynlib_process_needed_and_relocate failed");
pt_detach(pid, SIGKILL);
return -1;
}

// Allow libc to allocate arbitrary amount of memory.
elfldr_set_heap_size(pid, -1);

//Insert a breakpoint at the eboot entry.
if(!(brkpoint=kernel_dynlib_entry_addr(pid, 0))) {
puts("kernel_dynlib_entry_addr failed");
pt_detach(pid, SIGKILL);
return -1;
}
brkpoint += 58;// offset to invocation of main()
if(mdbg_copyout(pid, brkpoint, &orginstr, sizeof(orginstr))) {
perror("mdbg_copyout");
pt_detach(pid, SIGKILL);
return -1;
}
if(mdbg_copyin(pid, &int3instr, brkpoint, sizeof(int3instr))) {
perror("mdbg_copyin");
pt_detach(pid, SIGKILL);
return -1;
}

// Continue execution until we hit the breakpoint, then remove it.
if(pt_continue(pid, SIGCONT)) {
perror("pt_continue");
pt_detach(pid, SIGKILL);
return -1;
}
if(waitpid(pid, 0, 0) == -1) {
perror("waitpid");
pt_detach(pid, SIGKILL);
return -1;
}
if(mdbg_copyin(pid, &orginstr, brkpoint, sizeof(orginstr))) {
perror("mdbg_copyin");
pt_detach(pid, SIGKILL);
return -1;
}

if(argv[0]) {
elfldr_set_procname(pid, argv[0]);
} else {
elfldr_set_procname(pid, "payload");
}

elfldr_set_environ(pid, envp);
elfldr_set_cwd(pid, cwd);
elfldr_set_stdio(pid, stdio);

// Execute the ELF
if(elfldr_exec(pid, elf)) {
kill(pid, SIGKILL);
return -1;
}

return pid;
}

14 changes: 14 additions & 0 deletions src/ps5/elfldr.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,20 @@ along with this program; see the file COPYING. If not, see

#include <unistd.h>


/**
* Escape jail and raise privileges.
**/
int elfldr_raise_privileges(pid_t pid);


/**
* Execute an ELF inside a new process.
**/
int elfldr_spawn(const char* cwd, int stdio, uint8_t* elf, char** argv,
char** envp);


/**
* Execute an ELF inside the process with the given pid.
**/
Expand Down
3 changes: 1 addition & 2 deletions src/ps5/hbldr.c
Original file line number Diff line number Diff line change
Expand Up @@ -472,8 +472,7 @@ hbldr_launch(const char*cwd, const char* path, int stdio, char** argv,
return -1;
}

kernel_set_proc_rootdir(pid, kernel_get_root_vnode());
kernel_set_proc_jaildir(pid, 0);
elfldr_raise_privileges(pid);

if(bigapp_replace(pid, elf, path, stdio, cwd, envp) < 0) {
if(pt_detach(pid, SIGKILL)) {
Expand Down
62 changes: 61 additions & 1 deletion src/ps5/sys.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ along with this program; see the file COPYING. If not, see
#include <sys/sysctl.h>
#include <sys/syscall.h>

#include "elfldr.h"
#include "hbldr.h"
#include "pt.h"
#include "sys.h"
Expand Down Expand Up @@ -171,6 +172,10 @@ sys_launch_homebrew(const char* cwd, const char* path, const char* args,
int fds[2];
pid_t pid;

if(!cwd) {
cwd = "/";
}

if(!args) {
args = "";
}
Expand All @@ -179,7 +184,7 @@ sys_launch_homebrew(const char* cwd, const char* path, const char* args,
env = "";
}

printf("launch homebrew: %s %s %s\n", env, path, args);
printf("launch homebrew: CWD=%s %s %s %s\n", cwd, env, path, args);

if(socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == -1) {
perror("socketpair");
Expand Down Expand Up @@ -214,6 +219,61 @@ sys_launch_homebrew(const char* cwd, const char* path, const char* args,
}


int
sys_launch_payload(const char* cwd, uint8_t* elf, size_t elf_size,
const char* args, const char* env) {
char* argv[255];
char* envp[255];
int optval = 1;
int fds[2];
pid_t pid;

if(!cwd) {
cwd = "/";
}

if(!args) {
args = "";
}

if(!env) {
env = "";
}

printf("launch payload: CWD=%s %s %s\n", cwd, env, args);

if(socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == -1) {
perror("socketpair");
return 1;
}

if(setsockopt(fds[1], SOL_SOCKET, SO_NOSIGPIPE, &optval, sizeof(optval)) < 0) {
perror("setsockopt");
close(fds[0]);
close(fds[1]);
return -11;
}

args_split(args, argv, 255);
args_split(env, envp, 255);
pid = elfldr_spawn(cwd, fds[1], elf, argv, envp);

for(int i=0; argv[i]; i++) {
free(argv[i]);
}
for(int i=0; envp[i]; i++) {
free(envp[i]);
}

close(fds[1]);
if(pid < 0) {
close(fds[0]);
return -1;
}

return fds[0];
}

int
sys_launch_title(const char* title_id, const char* args) {
app_launch_ctx_t ctx = {0};
Expand Down
6 changes: 5 additions & 1 deletion src/sys.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@ along with this program; see the file COPYING. If not, see

#pragma once

#include <stdint.h>


int sys_launch_title(const char* title_id, const char* args);
int sys_launch_homebrew(const char* cwd, const char* path, const char* args,
const char* env);

int sys_launch_payload(const char* cwd, uint8_t* elf, size_t elf_size,
const char* argv, const char* env);
Loading

0 comments on commit 4cc9e0a

Please sign in to comment.