mirror of
https://github.com/arg0WAK/sce-hell0.git
synced 2026-10-17 12:00:07 +02:00
313 lines
9.0 KiB
C
313 lines
9.0 KiB
C
/* Standard C Library Headers */
|
|
#include <stdarg.h>
|
|
#include <stdatomic.h>
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
/* Networking Headers */
|
|
#include <arpa/inet.h>
|
|
#include <netinet/in.h>
|
|
#include <sys/socket.h>
|
|
|
|
/* System Headers (POSIX) */
|
|
#include <signal.h>
|
|
#include <sys/syscall.h>
|
|
#include <sys/sysctl.h>
|
|
#include <unistd.h>
|
|
|
|
/* Threading Headers */
|
|
#include <pthread.h>
|
|
|
|
/* Third Party Headers */
|
|
#include <microhttpd.h>
|
|
|
|
/* Local Headers */
|
|
#include "build/payloads.inc"
|
|
|
|
/**
|
|
* @brief Configuration parameters
|
|
*/
|
|
|
|
#define ELFLDR_PORT 9021
|
|
#define KILL_DELAY_US 300000
|
|
#define KILL_SIGNAL SIGTERM
|
|
#define MAX_TASKS 10
|
|
#define POLL_INTERVAL_US 100000
|
|
#define PORT 11110
|
|
#define PROC_NAME "sce-hell0-orchestrator.elf"
|
|
#define RESTORE_TIMEOUT_POLLS 100
|
|
#define TARGET_PROC_NAME "SceShellUI"
|
|
|
|
/* Struct Definitions */
|
|
typedef struct {
|
|
char name[64];
|
|
int delay;
|
|
} task_t;
|
|
|
|
typedef struct {
|
|
char useless1[45];
|
|
char message[3075];
|
|
} notify_request_t;
|
|
|
|
/* External Function Declarations */
|
|
extern int sceKernelSendNotificationRequest(int device, void *request, size_t size, int unused);
|
|
extern int sceNetCtlInit(void);
|
|
extern int kernel_set_ucred_authid(int td, uint64_t auth_id);
|
|
|
|
/* Function Implementations */
|
|
static void notify(const char *fmt, ...) {
|
|
notify_request_t req;
|
|
va_list args;
|
|
memset(&req, 0, sizeof(req));
|
|
va_start(args, fmt);
|
|
vsnprintf(req.message, sizeof(req.message), fmt, args);
|
|
va_end(args);
|
|
sceKernelSendNotificationRequest(0, &req, sizeof(req), 0);
|
|
}
|
|
|
|
static pid_t find_pid_by_name(const char *target_name) {
|
|
int mib[4] = {1, 14, 8, 0};
|
|
pid_t mypid = getpid();
|
|
size_t size = 0;
|
|
if (sysctl(mib, 4, NULL, &size, NULL, 0)) return -1;
|
|
size += 16 * 1024;
|
|
uint8_t *buf = malloc(size);
|
|
if (!buf) return -1;
|
|
if (sysctl(mib, 4, buf, &size, NULL, 0)) {
|
|
free(buf);
|
|
return -1;
|
|
}
|
|
pid_t found = -1;
|
|
for (uint8_t *p = buf; p < buf + size;) {
|
|
int len = *(int *)p;
|
|
if (len < 448) break;
|
|
pid_t pid = *(pid_t *)&p[72];
|
|
const char *name = (const char *)&p[447];
|
|
if (pid != mypid && strcmp(name, target_name) == 0) {
|
|
found = pid;
|
|
break;
|
|
}
|
|
p += len;
|
|
}
|
|
free(buf);
|
|
return found;
|
|
}
|
|
|
|
static void kill_stale_instances(void) {
|
|
for (int i = 0; i < 5; i++) {
|
|
pid_t pid = find_pid_by_name(PROC_NAME);
|
|
if (pid <= 0) return;
|
|
kill(pid, SIGKILL);
|
|
usleep(500000);
|
|
}
|
|
}
|
|
|
|
static atomic_int keep_running = 1;
|
|
static atomic_int kill_pending = 0;
|
|
|
|
static task_t task_queue[MAX_TASKS];
|
|
static int task_count = 0;
|
|
static pthread_mutex_t task_lock = PTHREAD_MUTEX_INITIALIZER;
|
|
|
|
static void kill_shellui(void) {
|
|
pid_t pid = find_pid_by_name(TARGET_PROC_NAME);
|
|
if (pid <= 0) return;
|
|
|
|
kill(pid, KILL_SIGNAL);
|
|
|
|
for (int i = 0; i < RESTORE_TIMEOUT_POLLS; i++) {
|
|
if (find_pid_by_name(TARGET_PROC_NAME) != pid) break;
|
|
usleep(POLL_INTERVAL_US);
|
|
}
|
|
}
|
|
|
|
static bool wait_for_shellui_restore(void) {
|
|
for (int i = 0; i < RESTORE_TIMEOUT_POLLS; i++) {
|
|
if (find_pid_by_name(TARGET_PROC_NAME) > 0) return true;
|
|
usleep(POLL_INTERVAL_US);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static int send_payload(const unsigned char *payload, size_t size) {
|
|
int sock = socket(AF_INET, SOCK_STREAM, 0);
|
|
if (sock < 0) return -1;
|
|
struct sockaddr_in server;
|
|
memset(&server, 0, sizeof(server));
|
|
server.sin_family = AF_INET;
|
|
server.sin_port = htons(ELFLDR_PORT);
|
|
server.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
|
|
|
if (connect(sock, (struct sockaddr *)&server, sizeof(server)) < 0) {
|
|
close(sock);
|
|
return -1;
|
|
}
|
|
size_t sent = 0;
|
|
while (sent < size) {
|
|
ssize_t ret = send(sock, payload + sent, size - sent, 0);
|
|
if (ret <= 0) break;
|
|
sent += ret;
|
|
}
|
|
close(sock);
|
|
return (sent == size) ? 0 : -1;
|
|
}
|
|
|
|
static void *sequence_thread(void *arg) {
|
|
(void)arg;
|
|
|
|
task_t local[MAX_TASKS];
|
|
int n;
|
|
|
|
pthread_mutex_lock(&task_lock);
|
|
n = task_count;
|
|
memcpy(local, task_queue, n * sizeof(local[0]));
|
|
task_count = 0;
|
|
pthread_mutex_unlock(&task_lock);
|
|
|
|
usleep(KILL_DELAY_US);
|
|
kill_shellui();
|
|
notify("SceShellUI restarted, queue will start after it is restored");
|
|
|
|
if (!wait_for_shellui_restore()) {
|
|
notify("SceShellUI restore timed out, exiting");
|
|
usleep(500000);
|
|
atomic_store(&kill_pending, 0);
|
|
atomic_store(&keep_running, 0);
|
|
return NULL;
|
|
}
|
|
notify("SceShellUI restored, queue will start");
|
|
|
|
for (int i = 0; i < n; i++) {
|
|
if (local[i].delay > 0) {
|
|
sleep(local[i].delay);
|
|
}
|
|
|
|
const embedded_payload_t *p = NULL;
|
|
for (int j = 0; all_payloads[j].name != NULL; j++) {
|
|
if (strcmp(all_payloads[j].name, local[i].name) == 0) {
|
|
p = &all_payloads[j];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (p) {
|
|
notify("%s (%d) sending...", p->name, ELFLDR_PORT);
|
|
send_payload(p->data, p->size);
|
|
} else {
|
|
notify("FAIL: %s not found!", local[i].name);
|
|
}
|
|
}
|
|
notify("Queue finished, exiting");
|
|
usleep(500000);
|
|
atomic_store(&kill_pending, 0);
|
|
atomic_store(&keep_running, 0);
|
|
return NULL;
|
|
}
|
|
|
|
static enum MHD_Result reply(struct MHD_Connection *c, unsigned int status, const char *body) {
|
|
struct MHD_Response *r = MHD_create_response_from_buffer(
|
|
strlen(body), (void *)body, MHD_RESPMEM_PERSISTENT);
|
|
if (!r) return MHD_NO;
|
|
MHD_add_response_header(r, "Content-Type", "text/plain");
|
|
MHD_add_response_header(r, "Access-Control-Allow-Origin", "*");
|
|
MHD_add_response_header(r, "Cache-Control", "no-cache");
|
|
enum MHD_Result res = MHD_queue_response(c, status, r);
|
|
MHD_destroy_response(r);
|
|
return res;
|
|
}
|
|
|
|
static enum MHD_Result on_request(void *cls, struct MHD_Connection *c,
|
|
const char *url, const char *method,
|
|
const char *version, const char *upload_data,
|
|
size_t *upload_size, void **con_cls) {
|
|
if (*con_cls == NULL) {
|
|
*con_cls = (void *)1;
|
|
return MHD_YES;
|
|
}
|
|
if (strcmp(method, "OPTIONS") == 0) return reply(c, MHD_HTTP_OK, "OK");
|
|
|
|
if (strcmp(url, "/list_payloads") == 0) {
|
|
char list[2048] = "";
|
|
for (int i = 0; all_payloads[i].name != NULL; i++) {
|
|
strcat(list, all_payloads[i].name);
|
|
if (all_payloads[i+1].name != NULL) strcat(list, ",");
|
|
}
|
|
return reply(c, MHD_HTTP_OK, list);
|
|
}
|
|
|
|
if (strcmp(url, "/clear_tasks") == 0) {
|
|
pthread_mutex_lock(&task_lock);
|
|
task_count = 0;
|
|
pthread_mutex_unlock(&task_lock);
|
|
return reply(c, MHD_HTTP_OK, "Queue cleared");
|
|
}
|
|
|
|
if (strcmp(url, "/add_task") == 0) {
|
|
const char *name = MHD_lookup_connection_value(c, MHD_GET_ARGUMENT_KIND, "name");
|
|
const char *delay_str = MHD_lookup_connection_value(c, MHD_GET_ARGUMENT_KIND, "delay");
|
|
pthread_mutex_lock(&task_lock);
|
|
|
|
if (name && task_count < MAX_TASKS) {
|
|
strncpy(task_queue[task_count].name, name, 63);
|
|
task_queue[task_count].name[63] = '\0';
|
|
task_queue[task_count].delay = delay_str ? atoi(delay_str) : 0;
|
|
task_count++;
|
|
pthread_mutex_unlock(&task_lock);
|
|
return reply(c, MHD_HTTP_OK, "Task added");
|
|
}
|
|
pthread_mutex_unlock(&task_lock);
|
|
return reply(c, MHD_HTTP_BAD_REQUEST, "Task could not be added (Missing parameter or queue full)");
|
|
}
|
|
|
|
if (strcmp(url, "/trigger_sequence") == 0) {
|
|
if (atomic_exchange(&kill_pending, 1) == 0) {
|
|
pthread_t t;
|
|
if (pthread_create(&t, NULL, sequence_thread, NULL) == 0)
|
|
pthread_detach(t);
|
|
else
|
|
atomic_store(&kill_pending, 0);
|
|
}
|
|
return reply(c, MHD_HTTP_OK, "Sequence triggered");
|
|
}
|
|
|
|
if (strcmp(url, "/exit") == 0) {
|
|
atomic_store(&keep_running, 0);
|
|
return reply(c, MHD_HTTP_OK, "OK");
|
|
}
|
|
|
|
return reply(c, MHD_HTTP_NOT_FOUND, "404 Not Found\n");
|
|
}
|
|
|
|
int main(void) {
|
|
syscall(SYS_thr_set_name, -1, PROC_NAME);
|
|
kill_stale_instances();
|
|
if (sceNetCtlInit() != 0) notify("sceNetCtlInit failed");
|
|
kernel_set_ucred_authid(-1, 0x4801000000000013L);
|
|
|
|
signal(SIGPIPE, SIG_IGN);
|
|
signal(SIGHUP, SIG_IGN);
|
|
signal(SIGTERM, SIG_IGN);
|
|
|
|
struct sockaddr_in addr;
|
|
memset(&addr, 0, sizeof(addr));
|
|
addr.sin_family = AF_INET;
|
|
addr.sin_port = htons(PORT);
|
|
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
|
|
|
struct MHD_Daemon *daemon = NULL;
|
|
for (int i = 0; i < 5 && !daemon; i++) {
|
|
daemon = MHD_start_daemon(
|
|
MHD_USE_INTERNAL_POLLING_THREAD, PORT, NULL, NULL, &on_request,
|
|
NULL, MHD_OPTION_SOCK_ADDR, (struct sockaddr *)&addr,
|
|
MHD_OPTION_THREAD_POOL_SIZE, (unsigned int)2, MHD_OPTION_END);
|
|
if (!daemon) sleep(1);
|
|
}
|
|
|
|
if (!daemon) return EXIT_FAILURE;
|
|
while (atomic_load(&keep_running)) usleep(POLL_INTERVAL_US);
|
|
|
|
MHD_stop_daemon(daemon);
|
|
return EXIT_SUCCESS;
|
|
} |