mirror of
https://github.com/Gezine/Luac0re.git
synced 2026-10-22 08:32:28 -05:00
357 lines
10 KiB
Lua
357 lines
10 KiB
Lua
local MAPPING_ADDR = 0x926100000
|
|
local SHADOW_MAPPING_ADDR = 0x920100000
|
|
|
|
local PROT_RW = 0x3
|
|
local PROT_RWX = 0x7
|
|
local MAP_SHARED_FIXED = 0x11
|
|
local MAP_ANON_PRIVATE_FIXED = 0x1012
|
|
|
|
local IPPROTO_IPV6 = 41
|
|
local IPV6_PKTINFO = 46
|
|
local AF_INET6 = 28
|
|
local SOCK_DGRAM = 2
|
|
local IPPROTO_UDP = 17
|
|
|
|
-- error handling
|
|
|
|
local function fail(msg)
|
|
ulog("[elf] ERROR: " .. msg)
|
|
send_notification(msg)
|
|
return nil
|
|
end
|
|
|
|
-- helpers
|
|
|
|
local function get_ofiles()
|
|
local p_fd = kread64(curproc + OFF.PROC_FD)
|
|
local tbl = kread64(p_fd + OFF.FILEDESC_OFILES)
|
|
return tbl + OFF.FDESCENTTBL_HDR
|
|
end
|
|
|
|
local function fd_to_fp(ofiles, fd)
|
|
return kread64(ofiles + fd * OFF.FILEDESCENT_SIZE)
|
|
end
|
|
|
|
local function read_procname(proc, comm_off)
|
|
local buf = malloc(32)
|
|
kread(buf, proc + comm_off, 32)
|
|
local name = ""
|
|
for i = 0, 31 do
|
|
local ch = read8(buf + i)
|
|
if ch == 0 then break end
|
|
if ch < 0x20 or ch > 0x7E then return "" end
|
|
name = name .. string.char(ch)
|
|
end
|
|
return name
|
|
end
|
|
|
|
-- proc offset discovery
|
|
|
|
local function find_proc_offsets()
|
|
local buf = malloc(0x1000)
|
|
kread(buf, curproc, 0x1000)
|
|
|
|
local sysent_off
|
|
for i = 0, 0x1000 - 8 do
|
|
if read64(buf + i) == 0x7FFFFFFFFFFFFFFF then
|
|
sysent_off = i - 0x10
|
|
break
|
|
end
|
|
end
|
|
if not sysent_off or sysent_off < 0 then
|
|
return fail("PROC_SYSENT not found")
|
|
end
|
|
|
|
local comm_off
|
|
for i = 0, 0x1000 - 6 do
|
|
if read8(buf + i) == 0xCE and read8(buf + i+1) == 0xFA
|
|
and read8(buf + i+2) == 0xEF and read8(buf + i+3) == 0xBE
|
|
and read8(buf + i+4) == 0xCC and read8(buf + i+5) == 0xBB then
|
|
comm_off = i + 8
|
|
break
|
|
end
|
|
end
|
|
if not comm_off then
|
|
return fail("PROC_COMM not found")
|
|
end
|
|
|
|
return sysent_off, comm_off
|
|
end
|
|
|
|
-- sysent swap
|
|
|
|
local function find_native_process(comm_off)
|
|
local names = { "SceGameLiveStreaming", "SceRedisServer", "SceSysAvControl" }
|
|
local p = kread64(gpu.data_base + OFF.ALLPROC)
|
|
|
|
while p ~= 0 and (p >> 48) == 0xFFFF do
|
|
local name = read_procname(p, comm_off)
|
|
for _, target in ipairs(names) do
|
|
if name == target then return p, name end
|
|
end
|
|
p = kread64(p)
|
|
end
|
|
return fail("no native PS5 process found")
|
|
end
|
|
|
|
local function swap_sysent(sysent_off, target_proc)
|
|
local our = kread64(curproc + sysent_off)
|
|
local their = kread64(target_proc + sysent_off)
|
|
|
|
local saved = {
|
|
sv_size = kread32(our),
|
|
sv_table = kread64(our + 0x8),
|
|
sysent = our,
|
|
}
|
|
|
|
kwrite32(our, kread32(their))
|
|
kwrite64(our + 0x8, kread64(their + 0x8))
|
|
return saved
|
|
end
|
|
|
|
local function restore_sysent(saved)
|
|
kwrite32(saved.sysent, saved.sv_size)
|
|
kwrite64(saved.sysent + 0x8, saved.sv_table)
|
|
end
|
|
|
|
-- ELF loader
|
|
|
|
local function load_elf(path)
|
|
local fd = syscall.open(path, 0, 0)
|
|
if fd < 0 then return nil end
|
|
|
|
local size = syscall.lseek(fd, 0, 2)
|
|
syscall.lseek(fd, 0, 0)
|
|
|
|
local store = malloc(size)
|
|
syscall.read(fd, store, size)
|
|
syscall.close(fd)
|
|
|
|
if read32(store) ~= 0x464C457F then return nil end
|
|
return store
|
|
end
|
|
|
|
local function map_elf(store)
|
|
local e_entry = read64(store + 0x18)
|
|
local e_phoff = read64(store + 0x20)
|
|
local e_shoff = read64(store + 0x28)
|
|
local e_phnum = read16(store + 0x38)
|
|
local e_shnum = read16(store + 0x3C)
|
|
local exec_start, exec_end = 0, 0
|
|
|
|
for i = 0, e_phnum - 1 do
|
|
local ph = store + e_phoff + i * 0x38
|
|
local p_type = read32(ph)
|
|
local p_flags = read32(ph + 4)
|
|
local p_off = read64(ph + 0x08)
|
|
local p_vaddr = read64(ph + 0x10)
|
|
local p_filesz = read64(ph + 0x20)
|
|
local p_memsz = read64(ph + 0x28)
|
|
|
|
if p_type == 1 then
|
|
local aligned = (p_memsz + 0x3FFF) & 0xFFFFC000
|
|
|
|
if (p_flags & 1) == 1 then
|
|
exec_start = p_vaddr
|
|
exec_end = p_vaddr + p_memsz
|
|
local eh = syscall.jitshm_create(0, aligned, PROT_RWX)
|
|
local wh = syscall.jitshm_alias(eh, PROT_RW)
|
|
syscall.mmap(SHADOW_MAPPING_ADDR, aligned, PROT_RW, MAP_SHARED_FIXED, wh, 0)
|
|
if p_filesz > 0 then memcpy(SHADOW_MAPPING_ADDR, store + p_off, p_filesz) end
|
|
if p_memsz > p_filesz then memset(SHADOW_MAPPING_ADDR + p_filesz, 0, p_memsz - p_filesz) end
|
|
syscall.mmap(MAPPING_ADDR + p_vaddr, aligned, PROT_RWX, MAP_SHARED_FIXED, eh, 0)
|
|
else
|
|
syscall.mmap(MAPPING_ADDR + p_vaddr, aligned, PROT_RW, MAP_ANON_PRIVATE_FIXED, 0xFFFFFFFF, 0)
|
|
if p_filesz > 0 then memcpy(MAPPING_ADDR + p_vaddr, store + p_off, p_filesz) end
|
|
end
|
|
end
|
|
end
|
|
|
|
local reloc_count = 0
|
|
for i = 0, e_shnum - 1 do
|
|
local sh = store + e_shoff + i * 0x40
|
|
if read32(sh + 4) == 4 then
|
|
local sh_off = read64(sh + 0x18)
|
|
local sh_size = read64(sh + 0x20)
|
|
for j = 0, (sh_size // 0x18) - 1 do
|
|
local r = store + sh_off + j * 0x18
|
|
local r_offset = read64(r)
|
|
local r_info = read64(r + 8)
|
|
local r_addend = read64(r + 0x10)
|
|
|
|
if (r_info & 0xFF) == 0x08 then
|
|
local dst = (r_offset >= exec_start and r_offset < exec_end)
|
|
and (SHADOW_MAPPING_ADDR + r_offset)
|
|
or (MAPPING_ADDR + r_offset)
|
|
write64(dst, MAPPING_ADDR + r_addend)
|
|
reloc_count = reloc_count + 1
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
return MAPPING_ADDR + e_entry, reloc_count
|
|
end
|
|
|
|
-- kernel r/w primitives for the loaded ELF
|
|
|
|
local function create_pipe()
|
|
local fPipe = func_wrap(dlsym(LIBKERNEL_HANDLE, "pipe"))
|
|
local buf = malloc(8)
|
|
|
|
write32(buf, 0); write32(buf + 4, 0)
|
|
local ret = fPipe(buf)
|
|
local rfd, wfd = read32(buf), read32(buf + 4)
|
|
if ret ~= 0 or rfd <= 0 or wfd <= 0 then
|
|
return fail("pipe() failed")
|
|
end
|
|
|
|
local fp = fd_to_fp(get_ofiles(), rfd)
|
|
local kpipe = kread64(fp)
|
|
if kpipe == 0 then
|
|
return fail("kpipe is NULL")
|
|
end
|
|
|
|
return rfd, wfd, kpipe
|
|
end
|
|
|
|
local function create_overlapped_sockets()
|
|
local ms = create_socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP)
|
|
local vs = create_socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP)
|
|
|
|
local mbuf = malloc(20)
|
|
for i = 0, 4 do write32(mbuf + i * 4, 0) end
|
|
syscall.setsockopt(ms, IPPROTO_IPV6, IPV6_PKTINFO, mbuf, 20)
|
|
|
|
local vbuf = malloc(20)
|
|
for i = 0, 4 do write32(vbuf + i * 4, 0) end
|
|
syscall.setsockopt(vs, IPPROTO_IPV6, IPV6_PKTINFO, vbuf, 20)
|
|
|
|
local ofiles = get_ofiles()
|
|
|
|
local mso = kread64(fd_to_fp(ofiles, ms))
|
|
local mpcb = kread64(mso + 0x18)
|
|
local mp = kread64(mpcb + OFF.INPCB_PKTOPTS)
|
|
|
|
local vso = kread64(fd_to_fp(ofiles, vs))
|
|
local vpcb = kread64(vso + 0x18)
|
|
local vp = kread64(vpcb + OFF.INPCB_PKTOPTS)
|
|
|
|
if mp == 0 or vp == 0 then
|
|
return fail("pktopts allocation failed")
|
|
end
|
|
|
|
kwrite32(mso, kread32(mso) + 0x100)
|
|
kwrite32(vso, kread32(vso) + 0x100)
|
|
|
|
kwrite64(mp + 0x10, vp + 0x10)
|
|
|
|
return ms, vs
|
|
end
|
|
|
|
|
|
function load_elfldr()
|
|
ulog("====== elf_loader ======")
|
|
|
|
syscall.resolve({
|
|
open = 0x5, lseek = 0x1DE, mmap = 0x1DD, munmap = 0x49,
|
|
jitshm_create = 0x215, jitshm_alias = 0x216,
|
|
})
|
|
|
|
local fPJ = func_wrap(dlsym(LIBKERNEL_HANDLE, "scePthreadJoin"))
|
|
|
|
local PROC_SYSENT, PROC_COMM = find_proc_offsets()
|
|
if not PROC_SYSENT then return end
|
|
|
|
local target, tname = find_native_process(PROC_COMM)
|
|
if not target then return end
|
|
ulog("target: " .. tname)
|
|
|
|
local saved = swap_sysent(PROC_SYSENT, target)
|
|
|
|
local title_id = get_title_id()
|
|
|
|
local ok, err = pcall(function()
|
|
local fw = tonumber(FW_VERSION)
|
|
|
|
if fw > 13.20 then
|
|
error("Unsupported firmware " .. FW_VERSION)
|
|
end
|
|
|
|
local elf
|
|
if fw > 10.01 then
|
|
elf = "elfldr_1320.elf"
|
|
else
|
|
elf = "elfldr_1001.elf"
|
|
end
|
|
|
|
local search = {
|
|
"/mnt/sandbox/" .. title_id .. "_000/savedata0/lua/elf_jb/" .. elf,
|
|
"/mnt/sandbox/" .. title_id .. "_001/savedata0/lua/elf_jb/" .. elf,
|
|
"/mnt/sandbox/" .. title_id .. "_002/savedata0/lua/elf_jb/" .. elf,
|
|
}
|
|
|
|
local store, path
|
|
for _, p in ipairs(search) do
|
|
store = load_elf(p)
|
|
if store then path = p; break end
|
|
end
|
|
if not store then fail("elfldr.elf not found"); return end
|
|
ulog("loaded " .. path)
|
|
|
|
local entry, nrelocs = map_elf(store)
|
|
ulog("entry=" .. to_hex(entry) .. " relocs=" .. nrelocs)
|
|
|
|
local prfd, pwfd, kpipe = create_pipe()
|
|
if not prfd then return end
|
|
local ofiles = get_ofiles()
|
|
local prfp = fd_to_fp(ofiles, prfd)
|
|
local pwfp = fd_to_fp(ofiles, pwfd)
|
|
kwrite32(prfp + 0x28, kread32(prfp + 0x28) + 0x100)
|
|
kwrite32(pwfp + 0x28, kread32(pwfp + 0x28) + 0x100)
|
|
|
|
local ms, vs = create_overlapped_sockets()
|
|
if not ms then return end
|
|
|
|
local rwpipe = malloc(8)
|
|
write32(rwpipe, prfd); write32(rwpipe + 4, pwfd)
|
|
|
|
local rwpair = malloc(8)
|
|
write32(rwpair, ms); write32(rwpair + 4, vs)
|
|
|
|
local payloadout = malloc(4)
|
|
write32(payloadout, 0)
|
|
|
|
local args = malloc(0x30)
|
|
write64(args + 0x00, dlsym(LIBKERNEL_HANDLE, "getpid"))
|
|
write64(args + 0x08, rwpipe)
|
|
write64(args + 0x10, rwpair)
|
|
write64(args + 0x18, kpipe)
|
|
write64(args + 0x20, gpu.data_base)
|
|
write64(args + 0x28, payloadout)
|
|
|
|
local th = malloc(8); write64(th, 0)
|
|
local at = malloc(0x100)
|
|
fAI(at); fAS(at, 0x80000)
|
|
local ret = fPC(th, at, entry, args, "elfldr")
|
|
fAD(at)
|
|
if ret ~= 0 then
|
|
fail("pthread_create failed: " .. to_hex(ret))
|
|
return
|
|
end
|
|
|
|
ulog("running...")
|
|
fPJ(read64(th), 0)
|
|
|
|
ulog("payloadout=" .. to_hex(read32(payloadout)))
|
|
end)
|
|
|
|
restore_sysent(saved)
|
|
ulog("sysent restored")
|
|
|
|
if not ok then
|
|
ulog("[elf] ERROR: " .. tostring(err))
|
|
send_notification("elf_loader: " .. tostring(err))
|
|
end
|
|
ulog("====== done ======")
|
|
end |