public void Apply(ExeFS exefs, List <Tuple <string, uint> > symbols) { long patchAddr = Address.GetAddress(exefs, symbols); byte[] buffer = new byte[0]; switch (PatchType) { case PatchTypeEnum.ptr: buffer = BitConverter.GetBytes(((MemAddress)Data).GetAddress(exefs, symbols)); break; case PatchTypeEnum.bl: long blHookAddr = ((MemAddress)Data).GetAddress(exefs, symbols); buffer = ArmUtils.Armv8Encode_bl((uint)(blHookAddr - patchAddr)); break; case PatchTypeEnum.b: long bHookAddr = ((MemAddress)Data).GetAddress(exefs, symbols); buffer = ArmUtils.Armv8Encode_b((uint)(bHookAddr - patchAddr)); break; case PatchTypeEnum.data: buffer = (byte[])Data; break; default: throw new Exception($"Invalid Patch Type : {PatchType}"); } exefs.WriteExeFs(patchAddr, buffer); }
static void AddSdk(ExeFS exefs, Nso custom) { Nso sdk = exefs.GetNso(NsoTypeEnum.sdk); exefs.SetNso(sdk, NsoTypeEnum.subsdk0); exefs.SetNso(custom, NsoTypeEnum.sdk); }
private void ExtractExeFS(Stream output, NCCHRegion region, Keyslot secondaryKeyslot, bool close = true) { if (secondaryKeyslot == Keyslot.NCCH) { //if the secondary keyslot is also the original 0x2C NCCH Keyslot, don't bother decrypting anything in parts, and just shove the entire ExeFS through a CryptoStream Tools.CryptFileStreamPart(this.NCCHMemoryMappedFile, output, new AesCtrCryptoTransform(this.Cryptor.NormalKey[(int)Keyslot.NCCH], region.CTR), region.Offset, region.Size, close); return; } //here we go, i don't like this part byte[] header = new byte[0x200]; using (MemoryMappedViewStream headerViewStream = this.NCCHMemoryMappedFile.CreateViewStream(region.Offset, 0x200)) { header = Tools.CryptBytes(headerViewStream.ReadBytes(0x200), new AesCtrCryptoTransform(this.Cryptor.NormalKey[0x2C], region.CTR)); } output.Write(header); //create dummy ExeFS class instance to figure out the locations of each file in the ExeFS ExeFS exefs = new ExeFS(header); //write decrypted header to output file foreach (ExeFSEntry entry in exefs.Entries) { byte[] CTR = (region.CTRInt + (entry.Offset / 16)).ToCTRBytes(); AesCtrCryptoTransform transform = (NormalCryptoExeFSFiles.Contains(entry.Name)) ? new AesCtrCryptoTransform(this.Cryptor.NormalKey[(int)Keyslot.NCCH], CTR) : new AesCtrCryptoTransform(this.Cryptor.NormalKey[(int)secondaryKeyslot], CTR); using (MemoryMappedViewStream fileViewStream = this.NCCHMemoryMappedFile.CreateViewStream(region.Offset + entry.Offset, entry.Size)) { CryptoStream cs = new CryptoStream(output, transform, CryptoStreamMode.Write); output.Seek(entry.Offset, SeekOrigin.Begin); fileViewStream.CopyTo(cs); cs.FlushFinalBlock(); } } //sneaky way to make it gm9-like if (!(output.GetType() == typeof(MemoryMappedViewStream))) { output.SetLength(Tools.RoundUp(output.Length, 0x200)); } if (close) { output.Dispose(); } }
static void Main(string[] args) { if (args.Length != 5) { Console.WriteLine($"Invalid Arg Nb ({args.Length})"); ShowInfo(); return; } string indir = args[0]; string subsdkPath = args[1]; string outdir = args[2]; string lstFile = args[3]; string configFile = args[4]; Console.WriteLine($"Current dir : {Environment.CurrentDirectory}"); if (!Directory.Exists(indir) || !File.Exists(subsdkPath) || !Directory.Exists(outdir) || !File.Exists(lstFile) || !File.Exists(configFile)) { Console.WriteLine($"Invalid Args :"); foreach (var arg in args) { Console.WriteLine(arg); } ShowInfo(); return; } ExeFS exefs = new ExeFS(indir); Nso custom = new Nso(subsdkPath); //allocate 0x100 bytes at the end of .bss for the module_object AllocModObj(custom); AddSdk(exefs, custom); ApplyPatches(exefs, lstFile, configFile); Console.WriteLine("Exporting..."); exefs.ExportAsDir(outdir); Console.WriteLine("Done!"); }
public long GetAddress(ExeFS exefs, List <Tuple <string, uint> > symbols) { if (IsSymbol) { foreach (var sym in symbols) { if (sym.Item1 == Symbol) { return(exefs.GetNsoAddress(NsoTypeEnum.sdk) + sym.Item2); } } throw new PatchConfigException($"Could not find symbol : {Symbol}"); } else { NsoTypeEnum type = NsoType; if (type == NsoTypeEnum.sdk) { type = NsoTypeEnum.subsdk0; } return(exefs.GetNsoAddress(type) + Offset); } }
static void ApplyPatches(ExeFS exefs, string lstFile, string configfile) { var patches = PatchConfig.GetHooks(configfile); var symbols = ListingFile.GetSymbols(lstFile); foreach (var patch in patches) { Console.WriteLine($"{patch}"); patch.Apply(exefs, symbols); } return; /* we don't need this anymore * * Console.WriteLine("Patching NPDM..."); * //patch ndpm to be able to access sd card * using (var ms = new MemoryStream(exefs.Npdm)) * { * BinaryReader br = new BinaryReader(ms); * BinaryWriter bw = new BinaryWriter(ms); * br.BaseStream.Position = 0x70; * var aci0Off = br.ReadUInt32(); //ACI0 offset * br.BaseStream.Position = 0x78; * var acidOff = br.ReadUInt32(); //ACID offset * * br.BaseStream.Position = aci0Off+0x20; //FS Access Header offset * var fsAccessHeaderOff = aci0Off + br.ReadUInt32(); * br.BaseStream.Position = acidOff + 0x220; //FS Access Control offset * var fsAccessControlOff = acidOff + br.ReadUInt32(); * * bw.BaseStream.Position = fsAccessHeaderOff + 4; * bw.Write(0x4000000000a92131); //full fs access * bw.BaseStream.Position = fsAccessControlOff + 4; * bw.Write(0x4000000000a92131); //full fs access * } */ }
public NCCH(Stream input) { _stream = input; using (var br = new BinaryReaderX(input, true)) { //NCCH Header ncchHeader = br.ReadStruct <Header>(); if (ncchHeader.ncchSize != input.Length / mediaUnitSize) { throw new Exception("Size in header is not the same as the file."); } //ExtHeader if (ncchHeader.exHeaderSize != 0) { var exHeaderOffset = br.BaseStream.Position; exHeader = br.ReadStruct <ExHeader>(); Files.Add(new ArchiveFileInfo { State = ArchiveFileState.Archived, FileName = "ExHeader.bin", FileData = new SubStream(br.BaseStream, exHeaderOffset, ncchHeader.exHeaderSize * 2) }); } //Plain Region if (ncchHeader.plainRegionOffset != 0 && ncchHeader.plainRegionSize != 0) { br.BaseStream.Position = ncchHeader.plainRegionOffset * mediaUnitSize; plainRegion = br.ReadBytes(ncchHeader.plainRegionSize * mediaUnitSize); Files.Add(new ArchiveFileInfo { State = ArchiveFileState.Archived, FileName = "PlainRegion.bin", FileData = new SubStream(br.BaseStream, ncchHeader.plainRegionOffset * mediaUnitSize, ncchHeader.plainRegionSize * mediaUnitSize) }); } //Logo Region if (ncchHeader.logoRegionOffset != 0 && ncchHeader.logoRegionSize != 0) { br.BaseStream.Position = ncchHeader.logoRegionOffset * mediaUnitSize; logoRegion = br.ReadBytes(ncchHeader.logoRegionSize * mediaUnitSize); Files.Add(new ArchiveFileInfo { State = ArchiveFileState.Archived, FileName = "Logo.icn", FileData = new SubStream(br.BaseStream, ncchHeader.logoRegionOffset * mediaUnitSize, ncchHeader.logoRegionSize * mediaUnitSize) }); } //ExeFS int exeFSHeaderSize = 0x200; if (ncchHeader.exeFSOffset != 0 && ncchHeader.exeFSSize != 0) { br.BaseStream.Position = ncchHeader.exeFSOffset * mediaUnitSize; exeFS = new ExeFS(br.BaseStream); foreach (var exeFsFile in exeFS.fileHeader) { if (exeFsFile.offset == 0 && exeFsFile.size == 0) { break; } Files.Add(new ExeFSFileInfo { State = ArchiveFileState.Archived, FileName = "ExeFS\\" + exeFsFile.name, FileData = new SubStream(br.BaseStream, ncchHeader.exeFSOffset * mediaUnitSize + exeFSHeaderSize + exeFsFile.offset, exeFsFile.size), compressed = exeFsFile.name == ".code" && (exHeader.sci.flag & 0x1) == 1 }); } } //RomFS if (ncchHeader.romFSOffset != 0 && ncchHeader.romFSSize != 0) { br.BaseStream.Position = ncchHeader.romFSOffset * mediaUnitSize; romFS = new RomFS(br.BaseStream); foreach (var romFSFile in romFS.files) { Files.Add(new ArchiveFileInfo { State = ArchiveFileState.Archived, FileName = "RomFS" + romFSFile.fileName, FileData = new SubStream(br.BaseStream, romFSFile.fileOffset, romFSFile.fileSize) }); } } } }