Skip to content

Instantly share code, notes, and snippets.

@AcK77

AcK77/dumpXCI.js Secret

Created March 22, 2018 00:50
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save AcK77/56b86469faef238157a0e02cb962ee30 to your computer and use it in GitHub Desktop.
Save AcK77/56b86469faef238157a0e02cb962ee30 to your computer and use it in GitHub Desktop.
/*
Provide with the courtesy of the mob.
Ac_K. (Thanks to SciresM)
----------------
- If mount partition gamecard failed try again (and again...).
- Partition: 0 - Header/Update | 1 - Normal/Secure.
- At the end, just merge all *.xci files on the SD Card to get a full *.xci dump.
Windows: copy /b TitleID-0.xci + TitleID-1.xci.0 + TitleID-1.xci.1 TitleID.xci
Linux: cat TitleID-0.xci TitleID-1.xci.0 TitleID-1.xci.1 > TitleID.xci
- You can verify your dump with Hactool.
*/
sc.getFSPPR = function () {
if (sc.closed_pr !== undefined) {
return;
}
sc.enableTurbo();
var i = 0;
var FspPr_Handle = null;
while (true) {
sc.ipcMsg(2).setType(5).sendTo('pm:shell');
var FspPr = sc.getService('fsp-pr');
if(FspPr.isOk) {
FspPr_Handle = FspPr.getValue();
break;
}
i++;
}
utils.log('Got fsp-pr handle after ' + i + ' iterations: ');
utils.log('fsp-pr handle: 0x' + FspPr_Handle.toString(16));
sc.svcCloseHandle(FspPr_Handle).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_Handle) => {
var pid = sc.getService('fsp-srv', (FspSrv_Handle) => {
utils.log("got fspsrv");
sc.ipcMsg(1).sendPid().data(0).sendTo(FspSrv_Handle).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_Handle).assertOk().show();
sc.ipcMsg(1).data(pid).sendTo(FspPr_Handle).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_Handle).assertOk().show();
sc.free(buf);
sc.free(buf2);
});
dumpIStorage = function(IStorage_Handle, SDCard_Handle, File_Path, isExFat) {
if (isExFat == undefined) isExFat = false;
sc.withHandle(IStorage_Handle, () => {
var size = sc.ipcMsg(4).sendTo(IStorage_Handle).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) && !isExFat) {
out_path = File_Path + '.0';
IFileSystemCreateFile(SDCard_Handle, out_path, two_gigs);
} else {
IFileSystemCreateFile(SDCard_Handle, out_path, size);
}
var IFile_Handle = IFileSystemOpenFile(SDCard_Handle, 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(IStorage_Handle).assertOk();
IFileSystemWriteBufferToFile(IFile_Handle, 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 && !isExFat) {
sc.ipcMsg(2).sendTo(IFile_Handle).assertOk(); // flush
sc.svcCloseHandle(IFile_Handle);
file_num++;
var new_path = File_Path + '.' + file_num;
if (size[1] > offset[1] || size[0] > two_gigs + offset[0]) {
IFileSystemCreateFile(SDCard_Handle, new_path, two_gigs);
} else {
IFileSystemCreateFile(SDCard_Handle, new_path, size[0] - offset[0]);
}
IFile_Handle = IFileSystemOpenFile(SDCard_Handle, new_path);
ofs_in_file = 0;
}
}
sc.ipcMsg(2).sendTo(IFile_Handle).assertOk();
sc.svcCloseHandle(IFile_Handle).assertOk();
});
};
IFileSystemCreateFile = function(IFileSystem_Handle, 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(IFileSystem_Handle);
utils.log('Create '+path+' (size '+size.toString(16)+'): ');
res.show();
};
IFileSystemWriteBufferToFile = function(IFile_Handle, offset, buf, sz) {
sc.ipcMsg(1).aDescriptor(buf, sz, 1).data([0,0], utils.pad64(offset), utils.trunc32(sz)).sendTo(IFile_Handle).show().assertOk();
};
IFileSystemOpenFile = function(IFileSystem_Handle, path) {
var pbuf = utils.str2ab(path);
utils.log('Open '+path+': ');
return sc.ipcMsg(8).datau32(3).xDescriptor(pbuf, pbuf.byteLength, 0).sendTo(IFileSystem_Handle).show().asResult().map((r) => r.movedHandles[0]).getValue();
};
buf2hex = function(buffer) { // buffer is an ArrayBuffer
return Array.prototype.map.call(new Uint8Array(buffer), x => ('00' + x.toString(16)).slice(-2)).reverse().join('');
}
dumpXCI = function (SDCard_Handle, FspSrv_Handle) {
sc.getService('ncm', (Ncm_Handle) => {
sc.ipcMsg(11).data(2).sendTo(Ncm_Handle).assertOk(); //OpenIContentMetaDatabase
var IContentMetaDatabase = sc.ipcMsg(5).data(2).sendTo(Ncm_Handle).assertOk(); //GetIContentMetaDatabase
sc.withHandle(IContentMetaDatabase.movedHandles[0], (IContentMetaDatabase_Handle) => {
var TitleId = new ArrayBuffer(0x8);
sc.ipcMsg(7).data(0).bDescriptor(TitleId, 24, 0).sendTo(IContentMetaDatabase_Handle).assertOk(); //ListApplicationa
TitleId = buf2hex(TitleId);
utils.log("Got GameCardTitleId: " + TitleId);
var OpenDeviceOperator = sc.ipcMsg(400).sendTo(FspSrv_Handle).assertOk(); //OpenDeviceOperator
sc.withHandle(OpenDeviceOperator.movedHandles[0], (OpenDeviceOperator_handle) => {
utils.log('Got OpenDeviceOperator handle: 0x'+ OpenDeviceOperator_handle.toString(16));
var GetGameCardHandle = sc.ipcMsg(202).sendTo(OpenDeviceOperator_handle).assertOk(); //GetGameCardHandle
GetGameCardHandle = GetGameCardHandle.data[0];
utils.log('Got GetGameCardHandle handle: 0x'+ GetGameCardHandle.toString(16));
for (var i = 0; i < 2; i++) { // Dump partitions 0 & 1
var IStorage = sc.ipcMsg(30).datau32(GetGameCardHandle, i).sendTo(FspSrv_Handle).asResult(); //OpenGameCardStorage
if (IStorage.isOk) {
IStorage = IStorage.getValue();
sc.withHandle(IStorage.movedHandles[0], (IStorage_Handle) => {
dumpIStorage(IStorage_Handle, SDCard_Handle, '/' + TitleId + '-' + i + '.xci', false);
});
} else {
utils.log('Failed to mount partition '+i);
}
}
});
});
});
};
sc.getService('fsp-srv', (FspSrv_Handle) => {
utils.log('Using fsp-srv handle: 0x' + FspSrv_Handle.toString(16));
sc.ipcMsg(1).sendPid().datau64(0).sendTo(FspSrv_Handle).assertOk();
utils.log('Initialized fsp-srv');
try {
var IFileSystem = sc.ipcMsg(18).sendTo(FspSrv_Handle).assertOk();
} catch(e) {
throw new Error('Failed to open SD card. Is it inserted?');
}
utils.log('SdCard Mounted');
IFileSystem.withHandles((r, m, c) => {
var SDCard_Handle = m[0];
dumpXCI(SDCard_Handle, FspSrv_Handle);
});
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment