Files
etaHEN/Source Code/daemon/source/main.cpp
Adam Furtenbach 0a4bfbe558 use of custom kstuff (#58)
* reverted libmprotect.h, couldn't make a viable build with recent ps5 sdk's
* added feature to load kstuff from /data/kstuff.elf before using embedded
* added menu feature to download latest kstuff from echostretch
* made download_file() more generic
2025-09-22 14:11:47 -04:00

512 lines
15 KiB
C++

/* Copyright (C) 2025 etaHEN / LightningMods
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 files
#include <cstdint>
#include <stdio.h>
#include <stdarg.h>
#include <stddef.h>
#include <string>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
#include <poll.h>
// System includes
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/sysctl.h>
#include <sys/_pthreadtypes.h>
#include <sys/signal.h>
#include <netinet/in.h>
// Project includes
#include "../../include/backtrace.hpp"
#include "globalconf.hpp"
#include "launcher.hpp"
#include "ipc.hpp"
#define MSG_NOSIGNAL 0x20000 /* do not generate SIGPIPE on EOF. */
pthread_t cheat_thr = nullptr;
#define PAD_BUTTON_OPTIONS 0x00000008
// Structure definitions
typedef struct {
unsigned int size;
uint32_t userId;
} SceShellUIUtilLaunchByUriParam;
typedef struct {
int32_t type; // 0x00
int32_t req_id; // 0x04
int32_t priority; // 0x08
int32_t msg_id; // 0x0C
int32_t target_id; // 0x10
int32_t user_id; // 0x14
int32_t unk1; // 0x18
int32_t unk2; // 0x1C
int32_t app_id; // 0x20
int32_t error_num; // 0x24
int32_t unk3; // 0x28
char use_icon_image_uri; // 0x2C
char message[1024]; // 0x2D
char uri[1024]; // 0x42D
char unkstr[1024]; // 0x82D
} OrbisNotificationRequest; // Size = 0xC30
// External C declarations
extern "C" {
int sceKernelSendNotificationRequest(int32_t device,
OrbisNotificationRequest *req,
size_t size, int32_t blocking);
int sceSystemServiceNavigateToGoHome(void);
int sceUserServiceGetUserName(const int userId, char *userName, const size_t size);
uint64_t sceKernelGetProcessTime();
int sceSystemServiceGetAppId(const char *title_id);
int scePadSetProcessPrivilege(int priv);
pid_t elfldr_spawn(const char* cwd, int stdio, uint8_t* elf, const char* name);
int sceUserServiceGetForegroundUser(int *userId);
int sceLncUtilLaunchApp(const char *tid, const char *argv[], LncAppParam *param);
uint32_t _sceApplicationGetAppId(int pid, uint32_t *appId);
uint32_t sceLncUtilKillApp(uint32_t appId);
int sceSysmoduleLoadModuleInternal(int id);
int sceNetCtlInit();
int sceUserServiceInitialize(const int *);
int sceKernelLoadStartModule(const char *name, size_t argc, const void *argv,
uint32_t flags, void *unknown, int *result);
int sceKernelDlsym(uint32_t lib, const char *name, void **fun);
int sceShellUIUtilInitialize(void);
int scePadClose(int handle);
int sceShellUIUtilLaunchByUri(const char *uri, SceShellUIUtilLaunchByUriParam *Param);
int sceSystemStateMgrEnterStandby(void);
int sceKernelMprotect(void *addr, size_t len, int prot);
ssize_t _read(int, void *, size_t);
int sceKernelGetProcessName(int pid, char *name);
void free(void *);
int sceShellCoreUtilRequestEjectDevice(const char *path);
// PayloadAPI definitions
#include <ps5/payload.h>
// External data
extern uint8_t kstuff_start[];
extern const unsigned int kstuff_size;
extern uint8_t ps5debug_start[];
extern const unsigned int ps5debug_size;
}
// Global variables
uint64_t p_syscall = 0;
char _end[1] = {};
struct daemon_settings global_conf;
int fd = -1;
pthread_t klog_srv = nullptr;
static constexpr auto DEFAULT_PRIORITY = 256;
uintptr_t kernel_base = 0;
// Function declarations
void etaHEN_log(const char *fmt, ...);
void notify(bool show_watermark, const char *text, ...);
bool touch_file(const char *destfile);
int launchApp(const char *titleId);
int get_ip_address(char *ip_address);
bool sceKernelIsTestKit();
int ItemzLaunchByUri(const char *uri);
bool enable_toolbox();
void sig_handler(int signo);
uint8_t *get_kstuff_address(bool *need_cleanup);
bool is_elf_header(uint8_t *data);
bool if_exists(const char *path);
void *fifo_and_dumper_thread(void *args);
void *Play_time_thread(void *args) noexcept;
void patch_checker();
int elfldr_raise_privileges(pid_t pid);
extern void makenewapp();
// External function declarations
extern void *start_ftp(void *);
extern void *IPC_loop(void *);
extern bool is_handler_enabled;
// Whitelist for PSIDs
const char *whitelisted_psids[] = {
"b345df7d4c77618d40f19a90e438ad87",
"ab535275b7196e7e7d43f4f9e7806724",
"d376c7780b960e5182d326ba3aa2d7a3",
"a8d89ad976b5cb912837ad29b0cc4610",
"177e09480b40816a1caca5151565daa5"
};
// Function implementations
void etaHEN_log(const char *fmt, ...) {
char msg[0x1000];
va_list args;
va_start(args, fmt);
__builtin_vsnprintf(msg, sizeof(msg), fmt, args);
va_end(args);
// Append newline at the end
size_t msg_len = strlen(msg);
if (msg_len < sizeof(msg) - 1) {
msg[msg_len] = '\n';
msg[msg_len + 1] = '\0';
} else {
msg[sizeof(msg) - 2] = '\n';
msg[sizeof(msg) - 1] = '\0';
}
printf("[etaHEN]: %s", msg); // msg already includes a newline
int fd = open("/data/etaHEN/etaHEN.log", O_WRONLY | O_CREAT | O_APPEND, 0777);
if (fd < 0) {
return;
}
write(fd, msg, strlen(msg));
close(fd);
}
bool touch_file(const char *destfile) {
static constexpr int FLAGS = 0777;
int fd = open(destfile, O_WRONLY | O_CREAT | O_TRUNC, FLAGS);
if (fd > 0) {
close(fd);
return true;
}
return false;
}
int launchApp(const char *titleId) {
int id = 0;
uint32_t res = sceUserServiceGetForegroundUser(&id);
if (res != 0) {
printf("sceUserServiceGetForegroundUser failed: 0x%x", res);
return res;
}
etaHEN_log("[LA] user id %u", id);
// the thread will clean this up
Flag flag = Flag_None;
LncAppParam param{sizeof(LncAppParam), id, 0, 0, flag};
puts("calling sceLncUtilLaunchApp");
int err = sceLncUtilLaunchApp(titleId, nullptr, &param);
etaHEN_log("sceLncUtilLaunchApp returned 0x%x", (uint32_t)err);
if (err >= 0) {
return err;
}
switch ((uint32_t)err) {
case SCE_LNC_UTIL_ERROR_ALREADY_RUNNING:
etaHEN_log("app %s is already running", titleId);
break;
case SCE_LNC_ERROR_APP_NOT_FOUND:
etaHEN_log("app %s not found", titleId);
notify(true, "app %s not found", titleId);
break;
default:
etaHEN_log("[LA] unknown error 0x%x", (uint32_t)err);
// notify(true, "unknown error 0x%llx", (uint32_t)err);
break;
}
return err;
}
void sig_handler(int signo) {
if(!is_handler_enabled){
etaHEN_log("Signal handler is disabled, ignoring signal %d", signo);
return;
}
notify(true,
"etaHEN has crashed ...\n\nPlease send /data/etaHEN/etaHEN_crash.log "
"to the PKG-Zone discord: https://discord.gg/BduZHudWGj");
etaHEN_log("main etaHEN has crashed ...");
printBacktraceForCrash();
exit(1);
}
bool sceKernelIsTestKit() {
uint8_t s_PsId[16] = {0};
size_t v2 = 16;
if (sysctlbyname("machdep.openpsid_for_sys", &s_PsId, &v2, 0, 0) < 0) {
printf("sceKernelGetOpenPsIdForSystem failed\n");
return true;
}
char psid_buf[255] = {0};
for (int i = 0; i < 16; i++) {
snprintf(psid_buf + strlen(psid_buf), 255 - strlen(psid_buf), "%02x",
s_PsId[i]);
}
for (int i = 0; i < sizeof(whitelisted_psids) / sizeof(whitelisted_psids[0]);
i++) {
if (strcmp(psid_buf, whitelisted_psids[i]) == 0) {
return false; // report not testkit if is whitelisted
}
}
return if_exists("/system/priv/lib/libSceDeci5Ttyp.sprx");
}
int ItemzLaunchByUri(const char *uri) {
if (!uri)
return -1;
SceShellUIUtilLaunchByUriParam Param;
Param.size = sizeof(SceShellUIUtilLaunchByUriParam);
sceShellUIUtilInitialize();
sceUserServiceGetForegroundUser((int *)&Param.userId);
return sceShellUIUtilLaunchByUri(uri, &Param);
}
bool cmd_enable_toolbox();
void LoadSettings();
int main() {
char buz[255];
pthread_t fifo_thr = nullptr;
pthread_t pt_thr = nullptr;
pthread_t msg_thr = nullptr;
sceNetCtlInit();
sceUserServiceInitialize(&DEFAULT_PRIORITY);
puts("daemon entered");
OrbisKernelSwVersion sys_ver;
sceKernelGetProsperoSystemSwVersion(&sys_ver);
int fw_ver = (sys_ver.version >> 16);
// Set up signal handlers
struct sigaction new_SIG_action;
new_SIG_action.sa_handler = sig_handler;
sigemptyset(&new_SIG_action.sa_mask);
new_SIG_action.sa_flags = 0;
for (int i = 0; i < 12; i++)
sigaction(i, &new_SIG_action, NULL);
unlink("/data/etaHEN/etaHEN.log");
unlink("/data/etaHEN/etaHEN_crash.log");
payload_args_t *args = payload_get_args();
kernel_base = args->kdata_base_addr;
etaHEN_log("=========== starting etaHEN (0x%X) ... ===========", fw_ver);
bool has_hv_bypass = (sceKernelMprotect(&buz[0], 100, 0x7) == 0);
bool is_lite = if_exists("/system_tmp/lite_mode");
bool toolbox_only = (fw_ver >= 0x900);
bool no_ps5debug = (fw_ver >= 0x800);
LoadSettings();
#if 0
// Check if running on a test kit
if (sceKernelIsTestKit()) {
etaHEN_log("no NO NO");
return -1;
raise(SIGSEGV);
}
#endif
// Start threads
get_ip_address(&buz[0]);
pthread_create(&fifo_thr, nullptr, fifo_and_dumper_thread, nullptr);
pthread_create(&pt_thr, nullptr, Play_time_thread, nullptr);
pthread_create(&msg_thr, nullptr, IPC_loop, nullptr);
etaHEN_log("is toolbox only: %s | ver: %x", toolbox_only ? "Yes" : "No", sys_ver.version);
sleep(15);
// Load kstuff if needed
bool dont_load_kstuff = (if_exists("/mnt/usb0/no_kstuff") || if_exists("/data/etaHEN/no_kstuff"));
if (dont_load_kstuff) {
notify(true, "kstuff loading disabled via file, homebrew and FPKGs will be disabled");
etaHEN_log("kstuff loading disabled in config.ini or no_kstuff file found");
}
if (!dont_load_kstuff && !has_hv_bypass && !is_lite && !toolbox_only) {
notify(true, "Loading kstuff ...");
bool cleanup_kstuff;
uint8_t *kstuff_address = get_kstuff_address(&cleanup_kstuff);
if (elfldr_spawn("/", STDOUT_FILENO, kstuff_address, "kstuff")) {
int wait = 0;
bool kstuff_not_loaded = false;
sleep(1);
while ((kstuff_not_loaded = sceKernelMprotect(&buz[0], 100, 0x7) < 0)) {
if(wait++ > 10){
notify(true, "Failed to load kstuff, kstuff will be unavailable");
break;
}
sleep(1);
}
if(!kstuff_not_loaded)
etaHEN_log("kstuff loaded");
}
else {
notify(true, "Failed to load kstuff, kstuff will be unavailable");
}
if (cleanup_kstuff) {
free(kstuff_address);
}
}
sleep(10);
// Initialize toolbox if needed
if (global_conf.toolbox_auto_start) {
cmd_enable_toolbox();
}
else if (!global_conf.toolbox_auto_start) {
notify(true, "the etaHEN Toolbox auto start is disabled in the config.ini\n\n"
"If you want to re-enable the toolbox go to ItemzFlow's settings menu");
}
// Load PS5Debug if needed
if (global_conf.PS5Debug && !no_ps5debug && !has_hv_bypass && !is_lite) {
if (!elfldr_spawn("/", STDOUT_FILENO, ps5debug_start, "ps5debug"))
notify(true, "Failed to load PS5Debug");
}
// Display IP and service info
std::string dpi_url = "Direct Package Installer V2: http://" + std::string(buz) + ":12800";
notify(true,
"etaHEN 2.3b by LM (ko-fi.com/lightningmods)\n\nAIO HEN\n\nCurrent IP: %s\n\nFTP Port: "
"1337\nKlog Port: 9081\n%s",
buz, global_conf.DPIv2 ? dpi_url.c_str() : "");
etaHEN_log("StartUp thread created!! - welcome to etaHEN");
// Launch the appropriate app based on configuration
const char *URI = nullptr;
switch (global_conf.start_opt) {
case HOME_MENU: {
URI = "pshomeui:navigateToHome?bootCondition=psButton";
break;
}
case TOOLBOX: {
if (global_conf.toolbox_auto_start)
URI = "pssettings:play?mode=settings&function=debug_settings";
else
URI = "pshomeui:navigateToHome?bootCondition=psButton";
break;
}
case SETTINGS: {
URI = "pssettings:play?mode=settings";
break;
}
case ITEMZFLOW: {
launchApp("ITEM00001");
break;
}
default:
etaHEN_log("unknown opt %d", global_conf.start_opt);
break;
}
if (URI && !is_lite)
etaHEN_log("ret %d", ItemzLaunchByUri(URI));
if(global_conf.auto_eject_disc){
sceShellCoreUtilRequestEjectDevice("/dev/cd0");
}
unlink("/system_tmp/lite_mode");
// Main loop to keep the process running
while (true) {
pthread_join(msg_thr, NULL);
pthread_create(&msg_thr, nullptr, IPC_loop, nullptr);
sleep(1);
}
puts("main thread ended");
return 0;
}
uint8_t *get_kstuff_address(bool *require_cleanup) {
const char *path = "/data/kstuff.elf";
long offset = 0;
off_t size;
uint8_t *address;
int fd;
if (!if_exists(path)) {
goto embedded_kstuff;
}
fd = open(path, O_RDONLY);
if (fd <= 0) {
goto embedded_kstuff;
}
size = lseek(fd, 0, SEEK_END);
address = (uint8_t*) malloc(size);
if (!address) {
goto close_fd;
}
lseek(fd, 0, SEEK_SET);
while (offset != size) {
int n = read(fd, address + offset, size - offset);
if (n <= 0)
{
goto free_mem;
}
offset += n;
}
if(!is_elf_header(address)) {
notify(true, "Kstuff '%s' doesn't have ELF header.", path);
goto free_mem;
}
*require_cleanup = true;
notify(true, "Loading kstuff from: %s", path);
return address;
free_mem:
free(address);
close_fd:
close(fd);
embedded_kstuff:
*require_cleanup = false;
return kstuff_start;
}
bool is_elf_header(uint8_t *data)
{
uint8_t header[] = {0x7f, 'E', 'L', 'F'};
return !memcmp(data, header, 4);
}