mirror of
https://github.com/etaHEN/etaHEN.git
synced 2026-01-12 19:25:33 +08:00
etaHEN 2.4B etaHEN 2.4B Change log - Updated to support the latest PS5 Payload SDK - Fixed etaHEN and Cheats support for 8.40-10.01 - Added a Game Overlay menu to show CPU/GPU Temp and utilization, Local IP Address and other future states - Added a Kstuff menu for options like downloading the latest kstuff from github, turning off kstufff autoload and more - Added a Custom Background Package Installer for installing PKGs from internal storage from any directory (Requires DPIv2 enabled for 5.50+) - DPIv2 can now download local files via url example http://192.xxx.xxx.xxx:12800/data/etaHEN/etaHEN.log - Improved Cheats support, cheats with or without 0 sections are now supported - Added Fix by TheFlow to Improve 2.xx PS4 PKG speeds - Replaced the donation links in the etaHEN credits menu with ones to github sponsers - Removed the non-whitelist app jailbreak option and moved it to an optional Legacy CMD Server option in the etaHEN Settings off by default - Game Decryptor has been updated for the Itemzflow Dumper - Updated the Plugin loader System - The Payload SDK ELFLDR is now REQUIRED for etaHEN to load - Replaced HTTP2 with Curl for better compatibility - Added timeout for ShellUI to receive a response (will stop it from freezing if no response is given) small fix
840 lines
18 KiB
C
840 lines
18 KiB
C
/* Copyright (C) 2024 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/>. */
|
|
|
|
#include <elf.h>
|
|
#include <fcntl.h>
|
|
#include <netinet/in.h>
|
|
#include <signal.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#include <sys/mman.h>
|
|
#include <sys/ptrace.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/syscall.h>
|
|
#include <sys/sysctl.h>
|
|
|
|
#include <sys/un.h>
|
|
#include <sys/user.h>
|
|
#include <sys/wait.h>
|
|
|
|
#include <ps5/kernel.h>
|
|
|
|
#include "elfldr.h"
|
|
#include "log.h"
|
|
#include "pt.h"
|
|
|
|
|
|
#ifndef IPV6_2292PKTOPTIONS
|
|
#define IPV6_2292PKTOPTIONS 25
|
|
#endif
|
|
|
|
|
|
/**
|
|
* Convenient macros.
|
|
**/
|
|
#define ROUND_PG(x) (((x) + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1))
|
|
#define TRUNC_PG(x) ((x) & ~(PAGE_SIZE - 1))
|
|
#define PFLAGS(x) ((((x) & PF_R) ? PROT_READ : 0) | \
|
|
(((x) & PF_W) ? PROT_WRITE : 0) | \
|
|
(((x) & PF_X) ? PROT_EXEC : 0))
|
|
|
|
|
|
/**
|
|
* Context structure for the ELF loader.
|
|
**/
|
|
typedef struct elfldr_ctx {
|
|
uint8_t* elf;
|
|
pid_t pid;
|
|
|
|
intptr_t base_addr;
|
|
size_t base_size;
|
|
void* base_mirror;
|
|
} elfldr_ctx_t;
|
|
|
|
|
|
/**
|
|
* Absolute path to the SceSpZeroConf eboot.
|
|
**/
|
|
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) {
|
|
intptr_t* loc = ctx->base_mirror + rela->r_offset;
|
|
intptr_t val = ctx->base_addr + rela->r_addend;
|
|
|
|
*loc = val;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* Parse a PT_LOAD program header.
|
|
**/
|
|
static int
|
|
data_load(elfldr_ctx_t *ctx, Elf64_Phdr *phdr) {
|
|
void* data = ctx->base_mirror + phdr->p_vaddr;
|
|
|
|
if(!phdr->p_memsz) {
|
|
return 0;
|
|
}
|
|
|
|
if(!phdr->p_filesz) {
|
|
return 0;
|
|
}
|
|
|
|
memcpy(data, ctx->elf+phdr->p_offset, phdr->p_filesz);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
elfldr_sanity_check(uint8_t *elf, size_t elf_size) {
|
|
Elf64_Ehdr *ehdr = (Elf64_Ehdr*)elf;
|
|
Elf64_Phdr *phdr;
|
|
|
|
if(elf_size < sizeof(Elf64_Ehdr) ||
|
|
elf_size < sizeof(Elf64_Phdr) + ehdr->e_phoff ||
|
|
elf_size < sizeof(Elf64_Shdr) + ehdr->e_shoff) {
|
|
return -1;
|
|
}
|
|
|
|
if(ehdr->e_ident[0] != 0x7f || ehdr->e_ident[1] != 'E' ||
|
|
ehdr->e_ident[2] != 'L' || ehdr->e_ident[3] != 'F') {
|
|
return -1;
|
|
}
|
|
|
|
phdr = (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;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* 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_ctx_t ctx = {.elf = elf, .pid=pid};
|
|
|
|
size_t min_vaddr = -1;
|
|
size_t max_vaddr = 0;
|
|
|
|
int error = 0;
|
|
|
|
// Compute size of virtual memory region.
|
|
for(int i=0; i<ehdr->e_phnum; i++) {
|
|
if(phdr[i].p_vaddr < min_vaddr) {
|
|
min_vaddr = phdr[i].p_vaddr;
|
|
}
|
|
|
|
if(max_vaddr < phdr[i].p_vaddr + phdr[i].p_memsz) {
|
|
max_vaddr = phdr[i].p_vaddr + phdr[i].p_memsz;
|
|
}
|
|
}
|
|
|
|
min_vaddr = TRUNC_PG(min_vaddr);
|
|
max_vaddr = ROUND_PG(max_vaddr);
|
|
ctx.base_size = max_vaddr - min_vaddr;
|
|
|
|
int flags = MAP_PRIVATE | MAP_ANONYMOUS;
|
|
int prot = PROT_READ | PROT_WRITE;
|
|
if(ehdr->e_type == ET_DYN) {
|
|
ctx.base_addr = 0;
|
|
} else if(ehdr->e_type == ET_EXEC) {
|
|
ctx.base_addr = min_vaddr;
|
|
flags |= MAP_FIXED;
|
|
} else {
|
|
LOG_PUTS("elfldr_load: ELF type not supported");
|
|
return 0;
|
|
}
|
|
|
|
if(!(ctx.base_mirror=malloc(ctx.base_size))) {
|
|
LOG_PERROR("malloc");
|
|
return 0;
|
|
}
|
|
|
|
// Reserve an address space of sufficient size.
|
|
if((ctx.base_addr=pt_mmap(pid, ctx.base_addr, ctx.base_size, prot,
|
|
flags, -1, 0)) == -1) {
|
|
LOG_PT_PERROR(pid, "pt_mmap");
|
|
free(ctx.base_mirror);
|
|
return 0;
|
|
}
|
|
|
|
// Parse program headers.
|
|
for(int i=0; i<ehdr->e_phnum && !error; i++) {
|
|
switch(phdr[i].p_type) {
|
|
case PT_LOAD:
|
|
error = data_load(&ctx, &phdr[i]);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Apply relocations.
|
|
for(int i=0; i<ehdr->e_shnum && !error; i++) {
|
|
if(shdr[i].sh_type != SHT_RELA) {
|
|
continue;
|
|
}
|
|
|
|
Elf64_Rela* rela = (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:
|
|
error = r_relative(&ctx, &rela[j]);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(pt_copyin(ctx.pid, ctx.base_mirror, ctx.base_addr, ctx.base_size)) {
|
|
LOG_PERROR("pt_copyin");
|
|
error = 1;
|
|
}
|
|
|
|
// Set protection bits on mapped segments.
|
|
for(int i=0; i<ehdr->e_phnum && !error; i++) {
|
|
if(phdr[i].p_type != PT_LOAD || phdr[i].p_memsz == 0) {
|
|
continue;
|
|
}
|
|
|
|
if(phdr[i].p_flags & PF_X) {
|
|
if(kernel_mprotect(pid, ctx.base_addr + phdr[i].p_vaddr,
|
|
ROUND_PG(phdr[i].p_memsz),
|
|
PFLAGS(phdr[i].p_flags))) {
|
|
LOG_PERROR("kernel_mprotect");
|
|
}
|
|
} else {
|
|
if(pt_mprotect(pid, ctx.base_addr + phdr[i].p_vaddr,
|
|
ROUND_PG(phdr[i].p_memsz),
|
|
PFLAGS(phdr[i].p_flags))) {
|
|
LOG_PT_PERROR(pid, "pt_mprotect");
|
|
error = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(pt_msync(pid, ctx.base_addr, ctx.base_size, MS_SYNC)) {
|
|
LOG_PT_PERROR(pid, "pt_msync");
|
|
error = 1;
|
|
}
|
|
|
|
free(ctx.base_mirror);
|
|
|
|
if(error) {
|
|
pt_munmap(pid, ctx.base_addr, ctx.base_size);
|
|
return 0;
|
|
}
|
|
|
|
return ctx.base_addr + ehdr->e_entry;
|
|
}
|
|
|
|
|
|
/**
|
|
* Create payload args in the address space of the process with the given pid.
|
|
**/
|
|
static intptr_t
|
|
elfldr_payload_args(pid_t pid) {
|
|
int victim_sock;
|
|
int master_sock;
|
|
intptr_t buf;
|
|
int pipe0;
|
|
int pipe1;
|
|
|
|
if((buf=pt_mmap(pid, 0, PAGE_SIZE, PROT_READ | PROT_WRITE,
|
|
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)) == -1) {
|
|
LOG_PT_PERROR(pid, "pt_mmap");
|
|
return 0;
|
|
}
|
|
|
|
if((master_sock=pt_socket(pid, AF_INET6, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
|
|
LOG_PT_PERROR(pid, "pt_socket");
|
|
return 0;
|
|
}
|
|
|
|
pt_setint(pid, buf+0x00, 20);
|
|
pt_setint(pid, buf+0x04, IPPROTO_IPV6);
|
|
pt_setint(pid, buf+0x08, IPV6_TCLASS);
|
|
pt_setint(pid, buf+0x0c, 0);
|
|
pt_setint(pid, buf+0x10, 0);
|
|
pt_setint(pid, buf+0x14, 0);
|
|
if(pt_setsockopt(pid, master_sock, IPPROTO_IPV6, IPV6_2292PKTOPTIONS, buf, 24)) {
|
|
LOG_PT_PERROR(pid, "pt_setsockopt");
|
|
return 0;
|
|
}
|
|
|
|
if((victim_sock=pt_socket(pid, AF_INET6, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
|
|
LOG_PT_PERROR(pid, "pt_socket");
|
|
return 0;
|
|
}
|
|
|
|
pt_setint(pid, buf+0x00, 0);
|
|
pt_setint(pid, buf+0x04, 0);
|
|
pt_setint(pid, buf+0x08, 0);
|
|
pt_setint(pid, buf+0x0c, 0);
|
|
pt_setint(pid, buf+0x10, 0);
|
|
if(pt_setsockopt(pid, victim_sock, IPPROTO_IPV6, IPV6_PKTINFO, buf, 20)) {
|
|
LOG_PT_PERROR(pid, "pt_setsockopt");
|
|
return 0;
|
|
}
|
|
|
|
if(kernel_overlap_sockets(pid, master_sock, victim_sock)) {
|
|
LOG_PUTS("kernel_overlap_sockets failed");
|
|
return 0;
|
|
}
|
|
|
|
if(pt_pipe(pid, buf)) {
|
|
LOG_PT_PERROR(pid, "pt_pipe");
|
|
return 0;
|
|
}
|
|
pipe0 = pt_getint(pid, buf);
|
|
pipe1 = pt_getint(pid, buf+4);
|
|
|
|
intptr_t args = buf;
|
|
intptr_t rwpipe = buf + 0x100;
|
|
intptr_t rwpair = buf + 0x200;
|
|
intptr_t kpipe_addr = kernel_get_proc_file(pid, pipe0);
|
|
intptr_t payloadout = buf + 0x300;
|
|
intptr_t getpid = pt_resolve(pid, "HoLVWNanBBc");
|
|
|
|
pt_setlong(pid, args + 0x00, getpid);
|
|
pt_setlong(pid, args + 0x08, rwpipe);
|
|
pt_setlong(pid, args + 0x10, rwpair);
|
|
pt_setlong(pid, args + 0x18, kpipe_addr);
|
|
pt_setlong(pid, args + 0x20, KERNEL_ADDRESS_DATA_BASE);
|
|
pt_setlong(pid, args + 0x28, payloadout);
|
|
pt_setint(pid, rwpipe + 0, pipe0);
|
|
pt_setint(pid, rwpipe + 4, pipe1);
|
|
pt_setint(pid, rwpair + 0, master_sock);
|
|
pt_setint(pid, rwpair + 4, victim_sock);
|
|
pt_setint(pid, payloadout, 0);
|
|
|
|
return args;
|
|
}
|
|
|
|
|
|
/**
|
|
* Prepare registers of a process for execution of an ELF.
|
|
**/
|
|
static int
|
|
elfldr_prepare_exec(pid_t pid, uint8_t *elf) {
|
|
intptr_t entry;
|
|
intptr_t args;
|
|
struct reg r;
|
|
|
|
if(pt_getregs(pid, &r)) {
|
|
LOG_PERROR("pt_getregs");
|
|
return -1;
|
|
}
|
|
|
|
if(!(entry=elfldr_load(pid, elf))) {
|
|
LOG_PUTS("elfldr_load failed");
|
|
return -1;
|
|
}
|
|
|
|
if(!(args=elfldr_payload_args(pid))) {
|
|
LOG_PUTS("elfldr_payload_args failed");
|
|
return -1;
|
|
}
|
|
|
|
r.r_rsp &= ~0xfl;
|
|
pt_setlong(pid, r.r_rsp-8, r.r_rip);
|
|
r.r_rsp -= 8;
|
|
r.r_rip = entry;
|
|
r.r_rdi = args;
|
|
|
|
if(pt_setregs(pid, &r)) {
|
|
LOG_PERROR("pt_setregs");
|
|
pt_detach(pid, SIGKILL);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* Set the name of a process.
|
|
**/
|
|
static int
|
|
elfldr_set_procname(pid_t pid, const char* name) {
|
|
intptr_t buf;
|
|
|
|
if((buf=pt_mmap(pid, 0, PAGE_SIZE, PROT_READ | PROT_WRITE,
|
|
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)) == -1) {
|
|
LOG_PT_PERROR(pid, "pt_mmap");
|
|
return -1;
|
|
}
|
|
|
|
pt_copyin(pid, name, buf, strlen(name)+1);
|
|
pt_syscall(pid, SYS_thr_set_name, -1, buf);
|
|
pt_msync(pid, buf, PAGE_SIZE, MS_SYNC);
|
|
pt_munmap(pid, buf, PAGE_SIZE);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* Escape jail and raise privileges.
|
|
**/
|
|
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;
|
|
}
|
|
|
|
|
|
/**
|
|
* Execute an ELF inside the process with the given pid.
|
|
**/
|
|
int
|
|
elfldr_exec(pid_t pid, int stdio, uint8_t* elf) {
|
|
uint8_t caps[16];
|
|
intptr_t jaildir;
|
|
intptr_t rootdir;
|
|
uint64_t authid;
|
|
int error = 0;
|
|
|
|
// backup privileges
|
|
jaildir = kernel_get_proc_jaildir(pid);
|
|
if(!(rootdir=kernel_get_proc_rootdir(pid))) {
|
|
LOG_PUTS("kernel_get_proc_rootdir failed");
|
|
pt_detach(pid, 0);
|
|
return -1;
|
|
}
|
|
if(kernel_get_ucred_caps(pid, caps)) {
|
|
LOG_PUTS("kernel_get_ucred_caps failed");
|
|
pt_detach(pid, 0);
|
|
return -1;
|
|
}
|
|
if(!(authid=kernel_get_ucred_authid(pid))) {
|
|
LOG_PUTS("kernel_get_ucred_authid failed");
|
|
pt_detach(pid, 0);
|
|
return -1;
|
|
}
|
|
|
|
if(elfldr_raise_privileges(pid)) {
|
|
LOG_PUTS("Unable to raise privileges");
|
|
pt_detach(pid, 0);
|
|
return -1;
|
|
}
|
|
|
|
if(stdio > 0) {
|
|
stdio = pt_rdup(pid, getpid(), stdio);
|
|
|
|
pt_close(pid, STDERR_FILENO);
|
|
pt_close(pid, STDOUT_FILENO);
|
|
pt_close(pid, STDIN_FILENO);
|
|
|
|
pt_dup2(pid, stdio, STDIN_FILENO);
|
|
pt_dup2(pid, stdio, STDOUT_FILENO);
|
|
pt_dup2(pid, stdio, STDERR_FILENO);
|
|
|
|
pt_close(pid, stdio);
|
|
}
|
|
|
|
if(elfldr_prepare_exec(pid, elf)) {
|
|
error = -1;
|
|
}
|
|
|
|
// restore privileges
|
|
if(kernel_set_proc_jaildir(pid, jaildir)) {
|
|
LOG_PUTS("kernel_set_proc_jaildir failed");
|
|
error = -1;
|
|
}
|
|
if(kernel_set_proc_rootdir(pid, rootdir)) {
|
|
LOG_PUTS("kernel_set_proc_rootdir failed");
|
|
error = -1;
|
|
}
|
|
|
|
if(kernel_set_ucred_caps(pid, caps)) {
|
|
LOG_PUTS("kernel_set_ucred_caps failed");
|
|
error = -1;
|
|
}
|
|
if(kernel_set_ucred_authid(pid, authid)) {
|
|
LOG_PUTS("kernel_set_ucred_authid failed");
|
|
error = -1;
|
|
}
|
|
|
|
if(pt_detach(pid, 0)) {
|
|
LOG_PERROR("pt_detach");
|
|
error = -1;
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
/**
|
|
* Set the heap size for libc.
|
|
**/
|
|
static int
|
|
elfldr_set_heap_size(pid_t pid, ssize_t size) {
|
|
intptr_t sceLibcHeapSize;
|
|
intptr_t sceLibcParam;
|
|
intptr_t sceProcParam;
|
|
intptr_t Need_sceLibc;
|
|
|
|
if(!(sceProcParam=pt_sceKernelGetProcParam(pid))) {
|
|
LOG_PT_PERROR(pid, "pt_sceKernelGetProcParam");
|
|
return -1;
|
|
}
|
|
|
|
if(pt_copyout(pid, sceProcParam+56, &sceLibcParam,
|
|
sizeof(sceLibcParam))) {
|
|
LOG_PERROR("pt_copyout");
|
|
return -1;
|
|
}
|
|
|
|
if(pt_copyout(pid, sceLibcParam+16, &sceLibcHeapSize,
|
|
sizeof(sceLibcHeapSize))) {
|
|
LOG_PERROR("pt_copyout");
|
|
return -1;
|
|
}
|
|
|
|
if(pt_setlong(pid, sceLibcHeapSize, size)) {
|
|
LOG_PERROR("pt_setlong");
|
|
return -1;
|
|
}
|
|
|
|
if(size != -1) {
|
|
return 0;
|
|
}
|
|
|
|
if(pt_copyout(pid, sceLibcParam+72, &Need_sceLibc,
|
|
sizeof(Need_sceLibc))) {
|
|
LOG_PERROR("pt_copyout");
|
|
return -1;
|
|
}
|
|
|
|
return pt_setlong(pid, sceLibcParam+32, Need_sceLibc);
|
|
}
|
|
|
|
|
|
static int
|
|
sys_budget_set(long budget) {
|
|
return __syscall(0x23b, budget);
|
|
}
|
|
|
|
|
|
static int
|
|
elfldr_rfork_entry(void* progname) {
|
|
char* const argv[] = {(char*)progname, 0};
|
|
|
|
if(sys_budget_set(0)) {
|
|
klog_perror("sys_budget_set");
|
|
return -1;
|
|
}
|
|
if(open("/dev/deci_stdin", O_RDONLY) < 0) {
|
|
klog_perror("open");
|
|
return -1;
|
|
}
|
|
if(open("/dev/deci_stdout", O_WRONLY) < 0) {
|
|
klog_perror("open");
|
|
return -1;
|
|
}
|
|
if(open("/dev/deci_stderr", O_WRONLY) < 0) {
|
|
klog_perror("open");
|
|
return -1;
|
|
}
|
|
|
|
if(ptrace(PT_TRACE_ME, 0, 0, 0)) {
|
|
klog_perror("ptrace");
|
|
return -1;
|
|
}
|
|
|
|
execve(SceSpZeroConf, argv, 0);
|
|
klog_perror("execve");
|
|
return -1;
|
|
}
|
|
|
|
|
|
/**
|
|
* Execute an ELF inside a new process.
|
|
**/
|
|
|
|
/**
|
|
* Fint the pid of a process with the given name.
|
|
**/
|
|
pid_t
|
|
elfldr_find_pid(const char* name) {
|
|
int mib[4] = {1, 14, 8, 0};
|
|
pid_t mypid = getpid();
|
|
pid_t pid = -1;
|
|
size_t buf_size;
|
|
uint8_t *buf;
|
|
|
|
if(sysctl(mib, 4, 0, &buf_size, 0, 0)) {
|
|
LOG_PERROR("sysctl");
|
|
return -1;
|
|
}
|
|
|
|
if(!(buf=malloc(buf_size))) {
|
|
LOG_PERROR("malloc");
|
|
return -1;
|
|
}
|
|
|
|
if(sysctl(mib, 4, buf, &buf_size, 0, 0)) {
|
|
LOG_PERROR("sysctl");
|
|
free(buf);
|
|
return -1;
|
|
}
|
|
|
|
for(uint8_t *ptr=buf; ptr<(buf+buf_size);) {
|
|
int ki_structsize = *(int*)ptr;
|
|
pid_t ki_pid = *(pid_t*)&ptr[72];
|
|
char *ki_tdname = (char*)&ptr[447];
|
|
|
|
ptr += ki_structsize;
|
|
if(!strcmp(name, ki_tdname) && ki_pid != mypid) {
|
|
pid = ki_pid;
|
|
}
|
|
}
|
|
|
|
free(buf);
|
|
|
|
return pid;
|
|
}
|
|
|
|
|
|
/**
|
|
* Read an ELF from a given socket.
|
|
**/
|
|
int
|
|
elfldr_read(int fd, uint8_t** elf, size_t* elf_size) {
|
|
Elf64_Shdr *shdr;
|
|
Elf64_Ehdr ehdr;
|
|
uint8_t* buf;
|
|
uint8_t* bak;
|
|
size_t size;
|
|
off_t shend;
|
|
size_t rem;
|
|
|
|
if(recv(fd, &ehdr, sizeof(ehdr), MSG_WAITALL) != sizeof(ehdr)) {
|
|
return -1;
|
|
}
|
|
|
|
if(ehdr.e_ident[0] != 0x7f || ehdr.e_ident[1] != 'E' ||
|
|
ehdr.e_ident[2] != 'L' || ehdr.e_ident[3] != 'F') {
|
|
errno = ENOEXEC;
|
|
return -1;
|
|
}
|
|
|
|
size = ehdr.e_shoff + ehdr.e_shnum * sizeof(Elf64_Ehdr);
|
|
if(!(buf=malloc(size))) {
|
|
return -1;
|
|
}
|
|
|
|
memcpy(buf, &ehdr, sizeof(ehdr));
|
|
rem = size - sizeof(ehdr);
|
|
if(recv(fd, buf + sizeof(ehdr), rem, MSG_WAITALL) != rem) {
|
|
free(buf);
|
|
return -1;
|
|
}
|
|
|
|
shend = 0;
|
|
shdr = (Elf64_Shdr*)(buf + ehdr.e_shoff);
|
|
for(int i=0; i<ehdr.e_shnum; i++) {
|
|
if(shdr[i].sh_type == SHT_NOBITS) {
|
|
continue;
|
|
}
|
|
size_t end = shdr[i].sh_offset + shdr[i].sh_size;
|
|
if(end > shend) {
|
|
shend = end;
|
|
}
|
|
}
|
|
|
|
// sections appear before section headers
|
|
if(shend <= size) {
|
|
*elf = buf;
|
|
*elf_size = size;
|
|
return 0;
|
|
}
|
|
|
|
bak = buf;
|
|
if(!(buf=realloc(buf, shend))) {
|
|
free(bak);
|
|
return -1;
|
|
}
|
|
|
|
rem = shend - size;
|
|
if(recv(fd, buf + size, rem, MSG_WAITALL) != rem) {
|
|
free(buf);
|
|
return -1;
|
|
}
|
|
|
|
*elf = buf;
|
|
*elf_size = shend;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* Execute an ELF inside a new process.
|
|
**/
|
|
pid_t
|
|
elfldr_spawn(const char* cwd, int stdio, uint8_t* elf, const char* name) {
|
|
|
|
uint8_t int3instr = 0xcc;
|
|
struct kevent evt;
|
|
intptr_t brkpoint;
|
|
uint8_t orginstr;
|
|
void *stack;
|
|
pid_t pid;
|
|
int kq;
|
|
|
|
if((kq=kqueue()) < 0) {
|
|
LOG_PERROR("kqueue");
|
|
return -1;
|
|
}
|
|
|
|
if(!(stack=malloc(PAGE_SIZE))) {
|
|
LOG_PERROR("malloc");
|
|
close(kq);
|
|
return -1;
|
|
}
|
|
|
|
if((pid=rfork_thread(RFPROC | RFCFDG | RFMEM, stack+PAGE_SIZE-8,
|
|
elfldr_rfork_entry, (void*)name)) < 0) {
|
|
LOG_PERROR("rfork_thread");
|
|
free(stack);
|
|
close(kq);
|
|
return -1;
|
|
}
|
|
|
|
EV_SET(&evt, pid, EVFILT_PROC, EV_ADD, NOTE_EXEC, 0, 0);
|
|
if(kevent(kq, &evt, 1, &evt, 1, 0) < 0) {
|
|
LOG_PERROR("kevent");
|
|
free(stack);
|
|
close(kq);
|
|
return -1;
|
|
}
|
|
|
|
if(waitpid(pid, 0, 0) < 0) {
|
|
LOG_PERROR("waitpid");
|
|
free(stack);
|
|
close(kq);
|
|
return -1;
|
|
}
|
|
|
|
free(stack);
|
|
close(kq);
|
|
|
|
// 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)) {
|
|
LOG_PT_PERROR(pid, "sys_dynlib_process_needed_and_relocate");
|
|
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))) {
|
|
LOG_PUTS("kernel_dynlib_entry_addr failed");
|
|
pt_detach(pid, SIGKILL);
|
|
return -1;
|
|
}
|
|
brkpoint += 58;// offset to invocation of main()
|
|
|
|
if(kernel_mprotect(pid, brkpoint, PAGE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC)) {
|
|
LOG_PUTS("kernel_mprotect failed");
|
|
pt_detach(pid, SIGKILL);
|
|
return -1;
|
|
}
|
|
|
|
if(pt_copyout(pid, brkpoint, &orginstr, sizeof(orginstr))) {
|
|
LOG_PERROR("pt_copyout");
|
|
pt_detach(pid, SIGKILL);
|
|
return -1;
|
|
}
|
|
if(pt_copyin(pid, &int3instr, brkpoint, sizeof(int3instr))) {
|
|
LOG_PERROR("pt_copyin");
|
|
pt_detach(pid, SIGKILL);
|
|
return -1;
|
|
}
|
|
|
|
// Continue execution until we hit the breakpoint, then remove it.
|
|
if(pt_continue(pid, SIGCONT)) {
|
|
LOG_PERROR("pt_continue");
|
|
pt_detach(pid, SIGKILL);
|
|
return -1;
|
|
}
|
|
if(waitpid(pid, 0, 0) == -1) {
|
|
LOG_PERROR("waitpid");
|
|
pt_detach(pid, SIGKILL);
|
|
return -1;
|
|
}
|
|
if(pt_copyin(pid, &orginstr, brkpoint, sizeof(orginstr))) {
|
|
LOG_PERROR("pt_copyin");
|
|
pt_detach(pid, SIGKILL);
|
|
return -1;
|
|
}
|
|
|
|
// Execute the ELF
|
|
elfldr_set_procname(pid, name);
|
|
if(elfldr_exec(pid, stdio, elf)) {
|
|
kill(pid, SIGKILL);
|
|
return -1;
|
|
}
|
|
|
|
return pid;
|
|
}
|
|
|
|
|
|
|