mirror of
https://github.com/Gezine/Luac0re.git
synced 2026-10-18 23:07:31 -05:00
436 lines
12 KiB
Lua
436 lines
12 KiB
Lua
function printf(fmt, ...)
|
|
print(string.format(fmt, ...))
|
|
end
|
|
|
|
function errorf(fmt, ...)
|
|
error(string.format(fmt, ...))
|
|
end
|
|
|
|
function to_hex(value)
|
|
return string.format("0x%016X", value & 0xFFFFFFFFFFFFFFFF)
|
|
end
|
|
|
|
function file_exists(name)
|
|
local f = io.open(name, "r")
|
|
if f ~= nil then
|
|
io.close(f)
|
|
return true
|
|
else
|
|
return false
|
|
end
|
|
end
|
|
|
|
function file_write(filename, data, mode)
|
|
local fd = io.open(filename, mode or "wb")
|
|
fd:write(data)
|
|
fd:close()
|
|
end
|
|
|
|
function file_read(filename, mode)
|
|
return io.open(filename, mode):read("*all")
|
|
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"
|
|
|
|
write32(notify_buffer + 0x0, 0) -- type
|
|
write32(notify_buffer + 0x28, 0) -- unk3
|
|
write32(notify_buffer + 0x2C, 1) -- use_icon_image_uri
|
|
write32(notify_buffer + 0x10, -1) -- target_id
|
|
write_buffer(notify_buffer + 0x2D, text) -- message
|
|
write_buffer(notify_buffer + 0x42D, icon_uri) -- uri
|
|
|
|
local dev_path = "/dev/notification0"
|
|
local fd = sceKernelOpen(dev_path, O_WRONLY, 0)
|
|
sceKernelWrite(fd, notify_buffer, notify_buffer_size)
|
|
sceKernelClose(fd)
|
|
end
|
|
|
|
function show_dialog(message)
|
|
sceMsgDialogTerminate()
|
|
sceMsgDialogInitialize()
|
|
|
|
local dialog_param_addr = DIALOG_SCRATCH
|
|
local msg_param_addr = DIALOG_SCRATCH + 0x88
|
|
|
|
-- Zero dialog_param buffer
|
|
memset(dialog_param_addr, 0, 0x87)
|
|
|
|
-- Zero msg_param buffer
|
|
memset(msg_param_addr, 0, 0x1F)
|
|
|
|
-- Calculate magic (32-bit address)
|
|
local magic = (0xC0D1A109 + dialog_param_addr) & 0xFFFFFFFF
|
|
|
|
-- Setup dialog_param structure
|
|
write64(dialog_param_addr + 0x00, 0x30) -- baseParam.size
|
|
write32(dialog_param_addr + 0x2C, magic) -- magic
|
|
write64(dialog_param_addr + 0x30, 0x88) -- size
|
|
write32(dialog_param_addr + 0x38, 1) -- mode
|
|
write64(dialog_param_addr + 0x40, msg_param_addr) -- msg_param pointer
|
|
write32(dialog_param_addr + 0x58, USER_ID) -- userId
|
|
|
|
-- Setup msg_param structure
|
|
write32(msg_param_addr + 0x00, 0)
|
|
write64(msg_param_addr + 0x08, message)
|
|
|
|
local msg_ret = sceMsgDialogOpen(dialog_param_addr)
|
|
|
|
if msg_ret ~= 0 then
|
|
send_notification("sceMsgDialogOpen failed")
|
|
end
|
|
end
|
|
|
|
function microsleep(val)
|
|
sceKernelUsleep(val // 1)
|
|
end
|
|
|
|
function sleep(val)
|
|
microsleep(val * 1000000)
|
|
end
|
|
|
|
function find_pattern(buffer, pattern)
|
|
local pattern_bytes = {}
|
|
local is_wildcard = {}
|
|
|
|
-- Parse pattern
|
|
for hex in pattern:gmatch("%S+") do
|
|
if hex == "?" then
|
|
pattern_bytes[#pattern_bytes + 1] = 0
|
|
is_wildcard[#is_wildcard + 1] = true
|
|
else
|
|
pattern_bytes[#pattern_bytes + 1] = tonumber(hex, 16)
|
|
is_wildcard[#is_wildcard + 1] = false
|
|
end
|
|
end
|
|
|
|
local pattern_len = #pattern_bytes
|
|
local buffer_len = #buffer
|
|
local matches = {}
|
|
|
|
-- Search byte-by-byte
|
|
for i = 1, buffer_len - pattern_len + 1 do
|
|
local match = true
|
|
for j = 1, pattern_len do
|
|
if not is_wildcard[j] then
|
|
if string.byte(buffer, i + j - 1) ~= pattern_bytes[j] then
|
|
match = false
|
|
break
|
|
end
|
|
end
|
|
end
|
|
if match then
|
|
matches[#matches + 1] = i
|
|
end
|
|
end
|
|
|
|
return matches
|
|
end
|
|
|
|
function get_error_string()
|
|
local error_addr = libc_error()
|
|
local errno = read64(error_addr)
|
|
local str_addr = libc_strerror(errno)
|
|
return "errno : " .. to_hex(errno) .. "\nerror : " .. read_null_terminated_string(str_addr)
|
|
end
|
|
|
|
function sysctlbyname(name, oldp, oldp_len, newp, newp_len)
|
|
local translate_name_mib = malloc(0x8)
|
|
local buf_size = 0x70
|
|
local mib = malloc(buf_size)
|
|
local size = malloc(0x8)
|
|
|
|
write32(translate_name_mib, 0)
|
|
write32(translate_name_mib + 4, 3)
|
|
write64(size, buf_size)
|
|
|
|
local name_len = #name
|
|
|
|
local ret = syscall.sysctl(translate_name_mib, 2, mib, size, name, name_len)
|
|
if ret == -1 then
|
|
error("failed to translate sysctl name to mib (" .. name .. ")")
|
|
end
|
|
|
|
local mib_len = read64(size) // 4
|
|
|
|
ret = syscall.sysctl(mib, mib_len, oldp, oldp_len, newp, newp_len)
|
|
if ret == -1 then
|
|
return false
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
function get_fwversion()
|
|
local buf = malloc(0x8)
|
|
local size = malloc(0x8)
|
|
write64(size, 0x8)
|
|
|
|
if sysctlbyname("kern.sdk_version", buf, size, 0, 0) then
|
|
local byte1 = read8(buf + 2) -- Minor version
|
|
local byte2 = read8(buf + 3) -- Major version
|
|
|
|
local version = string.format("%x.%02x", byte2, byte1)
|
|
return version
|
|
end
|
|
|
|
return nil
|
|
end
|
|
|
|
function get_current_ip()
|
|
-- Get interface count
|
|
local count = syscall.netgetiflist(0, 10)
|
|
if count < 0 then
|
|
return nil
|
|
end
|
|
|
|
-- Allocate buffer for interfaces
|
|
local iface_size = 0x1e0
|
|
local iface_buf = malloc(iface_size * count)
|
|
|
|
-- Get interface list
|
|
if syscall.netgetiflist(iface_buf, count) < 0 then
|
|
return nil
|
|
end
|
|
|
|
-- Parse interfaces
|
|
for i = 0, count - 1 do
|
|
local offset = i * iface_size
|
|
|
|
-- Read interface name (null-terminated string at offset 0)
|
|
local iface_name = ""
|
|
for j = 0, 15 do
|
|
local c = read8(iface_buf + offset + j)
|
|
if c == 0 then break end
|
|
iface_name = iface_name .. string.char(c)
|
|
end
|
|
|
|
-- Read IP address (4 bytes at offset 0x28)
|
|
local ip_offset = offset + 0x28
|
|
local ip1 = read8(iface_buf + ip_offset)
|
|
local ip2 = read8(iface_buf + ip_offset + 1)
|
|
local ip3 = read8(iface_buf + ip_offset + 2)
|
|
local ip4 = read8(iface_buf + ip_offset + 3)
|
|
local iface_ip = ip1 .. "." .. ip2 .. "." .. ip3 .. "." .. ip4
|
|
|
|
-- Check if this is eth0 or wlan0 with valid IP
|
|
if (iface_name == "eth0" or iface_name == "wlan0") and
|
|
iface_ip ~= "0.0.0.0" and iface_ip ~= "127.0.0.1" then
|
|
return iface_ip
|
|
end
|
|
end
|
|
|
|
return nil
|
|
end
|
|
|
|
function is_jailbroken()
|
|
local cur_uid = syscall.getuid()
|
|
local is_in_sandbox = syscall.is_in_sandbox()
|
|
if cur_uid == 0 and is_in_sandbox == 0 then
|
|
return true
|
|
else
|
|
-- Check if elfldr is running at 9021
|
|
local sockaddr_in = malloc(16)
|
|
local enable = malloc(4)
|
|
|
|
local sock_fd = create_socket(AF_INET, SOCK_STREAM, 0)
|
|
if sock_fd == -1 then
|
|
error("socket failed: " .. to_hex(sock_fd))
|
|
end
|
|
|
|
write32(enable, 1)
|
|
syscall.setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, enable, 4)
|
|
|
|
write8(sockaddr_in + 1, AF_INET)
|
|
write16(sockaddr_in + 2, 0x3D23) -- port 9021
|
|
write32(sockaddr_in + 4, 0x0100007F) -- 127.0.0.1
|
|
|
|
-- Try to connect to 127.0.0.1:9021
|
|
local ret = syscall.connect(sock_fd, sockaddr_in, 16)
|
|
|
|
syscall.close(sock_fd)
|
|
|
|
if ret == 0 then
|
|
return true
|
|
else
|
|
return false
|
|
end
|
|
end
|
|
end
|
|
|
|
function get_nidpath()
|
|
local path_buffer = malloc(0x255)
|
|
local len_ptr = malloc(8)
|
|
|
|
write64(len_ptr, 0x255)
|
|
|
|
local ret = syscall.randomized_path(0, path_buffer, len_ptr)
|
|
if ret == -1 then
|
|
error("randomized_path failed : " .. to_hex(ret))
|
|
end
|
|
|
|
return read_null_terminated_string(path_buffer)
|
|
end
|
|
|
|
function run_lua_buffer(code)
|
|
local func, err = load(code)
|
|
if not func then
|
|
send_notification("Lua load error:\n" .. err)
|
|
return
|
|
end
|
|
|
|
local status, result = xpcall(func, debug.traceback)
|
|
if not status then
|
|
show_dialog("Lua exec error:\n" .. result)
|
|
end
|
|
end
|
|
|
|
function run_lua_file(filename)
|
|
local func, err = loadfile(filename)
|
|
if not func then
|
|
send_notification("Lua load error:\n" .. err)
|
|
return
|
|
end
|
|
|
|
local status, result = xpcall(func, debug.traceback)
|
|
if not status then
|
|
show_dialog("Lua exec error:\n" .. result)
|
|
end
|
|
end
|
|
|
|
function create_socket(domain, type, protocol)
|
|
|
|
-- PS4 below firmware 14.00 or PS5 below firmware 8.00 or domain = 0: use syscall directly
|
|
if (PLATFORM == "PS4" and tonumber(FW_VERSION) < 14.00) or (PLATFORM == "PS5" and tonumber(FW_VERSION) < 8.00) or domain == AF_UNIX then
|
|
local fd = syscall.socket(domain, type, protocol)
|
|
if fd < 0 then
|
|
send_notification("create_socket error: " .. get_error_string())
|
|
return -1
|
|
end
|
|
return fd
|
|
end
|
|
|
|
-- PS4 firmware 14.00+, PS5 firmware 8.00+, non-zero domain: route through JIT socket path
|
|
local jit_fd = jit_syscall.socket(domain, type, protocol)
|
|
if jit_fd < 0 then
|
|
send_notification("create_socket error: " .. jit_get_error_string())
|
|
return -1
|
|
end
|
|
|
|
local fd = jit_send_recv_fd(jit_fd, NEW_JIT_SOCK, NEW_MAIN_SOCK)
|
|
if fd < 0 then
|
|
send_notification("create_socket error")
|
|
return -1
|
|
end
|
|
|
|
return fd
|
|
|
|
end
|
|
|
|
function write_string(dest, str)
|
|
write_buffer(dest, str)
|
|
write8(dest + #str, 0)
|
|
end
|
|
|
|
function hex_to_binary(hex)
|
|
return (hex:gsub('..', function(cc)
|
|
return string.char(tonumber(cc, 16))
|
|
end))
|
|
end
|
|
|
|
function bin_to_hex(str)
|
|
return (str:gsub('.', function(c)
|
|
return string.format('%02x', string.byte(c))
|
|
end))
|
|
end
|
|
|
|
function write_shellcode(dest, str)
|
|
local shellcode = hex_to_binary(str)
|
|
if #shellcode < 0x400000 then
|
|
write_buffer(SHELLCODE_SCRATCH, shellcode)
|
|
jit_memcpy(dest, SHELLCODE_SCRATCH, #shellcode)
|
|
else
|
|
error("shellcode is larger than 0x400000 bytes")
|
|
end
|
|
end
|
|
|
|
function write_shellcode_network(dest)
|
|
local maxsize = 0x400000
|
|
local sockaddr_in = malloc(16)
|
|
local enable = malloc(4)
|
|
|
|
local function htons(port)
|
|
return ((port << 8) | (port >> 8)) & 0xFFFF
|
|
end
|
|
|
|
local sock_fd = create_socket(AF_INET, SOCK_STREAM, 0)
|
|
if sock_fd < 0 then error("create_socket error") end
|
|
|
|
write32(enable, 1)
|
|
syscall.setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, enable, 4)
|
|
|
|
write8(sockaddr_in + 1, AF_INET)
|
|
write16(sockaddr_in + 2, htons(9027))
|
|
write32(sockaddr_in + 4, INADDR_ANY)
|
|
|
|
syscall.bind(sock_fd, sockaddr_in, 16)
|
|
syscall.listen(sock_fd, 1)
|
|
|
|
local client_fd = syscall.accept(sock_fd, sockaddr_in, enable)
|
|
if client_fd < 0 then
|
|
syscall.close(sock_fd)
|
|
error("accept() error")
|
|
end
|
|
|
|
local total_read = 0
|
|
while total_read < maxsize do
|
|
local n = syscall.read(client_fd, SHELLCODE_SCRATCH + total_read, maxsize - total_read)
|
|
if n == 0 then break end
|
|
if n < 0 then
|
|
syscall.close(client_fd)
|
|
syscall.close(sock_fd)
|
|
error("read() error")
|
|
end
|
|
total_read = total_read + n
|
|
end
|
|
|
|
syscall.close(client_fd)
|
|
syscall.close(sock_fd)
|
|
|
|
if total_read == 0 then error("no data received") end
|
|
|
|
jit_memcpy(dest, SHELLCODE_SCRATCH, total_read)
|
|
end
|
|
|
|
|
|
function get_title_id()
|
|
local pid = syscall.getpid()
|
|
|
|
if PLATFORM ~= "PS4" and PLATFORM ~= "PS5" then
|
|
error("Unknown PLATFORM: " .. tostring(PLATFORM))
|
|
end
|
|
|
|
local app_info_size
|
|
if PLATFORM == "PS4" then
|
|
app_info_size = 0x48
|
|
elseif PLATFORM == "PS5" then
|
|
app_info_size = 0x58
|
|
end
|
|
|
|
local mib = malloc(0x10)
|
|
write32(mib + 0x0, 1)
|
|
write32(mib + 0x4, 14)
|
|
write32(mib + 0x8, 35)
|
|
write32(mib + 0xC, pid)
|
|
local app_info = malloc(0x100)
|
|
local oldlen = malloc(0x8)
|
|
write64(oldlen, app_info_size)
|
|
local result = syscall.sysctl(mib, 4, app_info, oldlen, 0, 0)
|
|
if result ~= 0 then
|
|
error("sysctl error: " .. get_error_string())
|
|
end
|
|
return read_null_terminated_string(app_info + 0x10)
|
|
end |