/*Code by Mrdude - what this program does....
First: Open package3 and search for the loader offsets + size.
Second: Dump the loader stored inside package3.
Third: Get the sha256 value of the dumped loader.
Fourth: Use hactool to decrypt the dumped loader.
Fifth: Find the offset for this hex - 01c0be121f00016b - in the decrypted loader.
Sixth: Create the ips patch.
*/
string loaderpath; //create variable for package3 file path location.
string shaValue; //create a variable to store the sha256 value.
int patch_offset; //create a variable to store the ips patch offset.
public Form1()
{
InitializeComponent();
richTextBox1.Text += "This page will create a loader patch for Atmosphère NX" + "\n\n" +
"Click load and select your package3 file.";
}
private void button1_Click(object sender, EventArgs e)
{
load();
}
public void load()
{
try
{
OpenFileDialog openFileDialog = new OpenFileDialog();
openFileDialog.Filter = "package3|package3|fusee-secondary|fusee-secondary.bin|All Files (*.*)|*.*";
if (openFileDialog.ShowDialog() == DialogResult.OK)
{
loaderpath = openFileDialog.FileName;
bool myfile = loaderpath.Contains("package3") || loaderpath.Contains("fusee-secondary");
if (myfile)
{
richTextBox1.Clear();
richTextBox1.Text += "Selected file: " + loaderpath;
FindBytes();
}
else
{
MessageBox.Show("File not supported.");
}
}
}
catch (Exception error)
{
MessageBox.Show("Error is: " + error.Message);
}
}
private void Form1_DragDrop(object sender, DragEventArgs e)
{
try
{
var files = (string[])e.Data.GetData(DataFormats.FileDrop);
loaderpath = Path.GetFullPath(files[0]);
bool myfile = loaderpath.Contains("package3") || loaderpath.Contains("fusee-secondary");
if (myfile)
{
//loaderpath = loaderpath.Replace(@"\", @"\\");
FindBytes();
}
else
{
MessageBox.Show("File not supported.");
}
}
catch (Exception error)
{
MessageBox.Show("Error is: " + error.Message);
}
}
private void FindBytes()
{
try
{
int i;
int j;
byte[] ByteBuffer = File.ReadAllBytes(loaderpath);
//byte[] StringBytes = new byte[6] { 0x4C, 0x6F, 0x61, 0x64, 0x65, 0x72 }; --this is the same as "Loader" (hex 4C ascii value is 76 same as L)
byte[] StringBytes = Encoding.UTF8.GetBytes("oader"); //new atmosphere uses uppercase L so skip looking for that to avoid problems
for (i = 0; i <= (ByteBuffer.Length - StringBytes.Length); i++)
{
if (ByteBuffer[i] == StringBytes[0])
{
for (j = 1; j < StringBytes.Length && ByteBuffer[i + j] == StringBytes[j]; j++) ;
if (j == StringBytes.Length)
break;
}
}
int startpos = (i - 17); // where the ascii text "oader" is found (minus 17 bytes)
int sizeloc = (startpos + 4); //size of the loader is found 4 bytes from the startpos
var location = new byte[4];
var size = new byte[4];
//MessageBox.Show(i.ToString()); //only used for debugging.
//get loader start location
using (BinaryReader reader = new BinaryReader(new FileStream(loaderpath, FileMode.Open)))
{
reader.BaseStream.Seek(startpos, SeekOrigin.Begin);
reader.Read(location, 0, 4);
}
//get the size of the loader
using (BinaryReader reader = new BinaryReader(new FileStream(loaderpath, FileMode.Open)))
{
reader.BaseStream.Seek(sizeloc, SeekOrigin.Begin);
reader.Read(size, 0, 4);
}
//convert bytes to int:
var loadersize = BitConverter.ToUInt32(size, 0);
var startloc = BitConverter.ToUInt32(location, 0);
var end = (loadersize + startloc);
//call code to dump the loader here now we know the size and start and end locations.
using (var output = File.Create("dumped_loader")) //set path of dumped_loader
using (var input = File.OpenRead(loaderpath)) //open package3 so we can dump the loader
{
// if there's timing issues with the dumped loader not being created fast enough - put this in a new thread.
AppendChunk(output, input, startloc, end); //dump the loader
}
//revese location bytes
Array.Reverse(location);
string location_hex = BitConverter.ToString(location);
//reverse end location bytes
byte[] endlocation = BitConverter.GetBytes(end); //convert end int location to byte array
//MessageBox.Show(String.Join("",endlocation.Select(x => x.ToString("X2"))));
Array.Reverse(endlocation);
//Not used - but can mod byte array manually....for changing instructions.
// UInt32 revloc = ReverseBytes(end);
richTextBox1.Text += "\n" + "Loader size: " + loadersize.ToString() + " bytes";
richTextBox1.Text += "\n" + "Loader start offset: 0x" + location_hex.Replace("-", "");
richTextBox1.Text += "\n" + "Loader end offset: 0x" + String.Join("", endlocation.Select(x => x.ToString("X2")));
//loader should be dumped now - so let's get the sha256 value;
Sha256();
//Now we can decrypt the dumped loader...
Decrypt_loader();
//now try to find the patch location in the decypted loader
FindloaderPatch();
//write ips patch
WriteIPS();
//remove decrypted loader now.
if (checkBox_decrypted.Checked)
{
File.Delete("dec-loader.bin");
richTextBox1.Text += "\n" + "Decrypted loader cleaned up";
}
richTextBox1.Text += "\n" + "Finished!";
}
catch (Exception error)
{
MessageBox.Show("Error is: " + error.Message);
}
}
private void makedirs()
{
try
{
if (!Directory.Exists("atmosphere"))
{
Directory.CreateDirectory("atmosphere");
}
if (!Directory.Exists("atmosphere\\kip_patches"))
{
Directory.CreateDirectory("atmosphere\\kip_patches");
}
if (!Directory.Exists("atmosphere\\kip_patches\\loader_patches"))
{
Directory.CreateDirectory("atmosphere\\kip_patches\\loader_patches");
richTextBox1.Text += "\n" + "Patch directories created";
}
}
catch (Exception error)
{
MessageBox.Show("Error is: " + error.Message);
}
}
private void WriteIPS()
{
try
{
//create Atmosphere ips directory if it doesn't exist.
makedirs();
using (var stream = File.Open("atmosphere\\kip_patches\\loader_patches\\" + shaValue + ".ips", FileMode.Create))
{
using (var writer = new BinaryWriter(stream, Encoding.UTF8, false))
{
byte[] StartBytes = new byte[] { 0x50, 0x41, 0x54, 0x43, 0x48, 0x00 };
byte[] EndBytes = new byte[] { 0x00, 0x01, 0x00, 0x45, 0x4F, 0x46 };
byte[] Middlebytes = BitConverter.GetBytes(patch_offset);
writer.Write(StartBytes);
//stupid code for testing = also reverse bytes
if (patch_offset > 0 && patch_offset <= 255)
{
writer.Write(Middlebytes[0]);
}
if (patch_offset > 255 && patch_offset <= 65535)
{
writer.Write(Middlebytes[1]);
writer.Write(Middlebytes[0]);
}
if (patch_offset > 65535 && patch_offset <= 16777215)
{
writer.Write(Middlebytes[2]);
writer.Write(Middlebytes[1]);
writer.Write(Middlebytes[0]);
}
if (patch_offset > 16777215)
{
writer.Write(Middlebytes[3]);
writer.Write(Middlebytes[2]);
writer.Write(Middlebytes[1]);
writer.Write(Middlebytes[0]);
}
//end of stupid code
writer.Write(EndBytes);
richTextBox1.Text += "\n" + "IPS patch written";
}
}
}
catch (Exception error)
{
MessageBox.Show("Error is: " + error.Message);
}
}
private void FindloaderPatch()
{
try
{
int x;
int y;
byte[] ByteBuffer = File.ReadAllBytes("dec-loader.bin");
byte[] StringBytes = new byte[8] { 0x01, 0xC0, 0xBE, 0x12, 0x1F, 0x00, 0x01, 0x6B };
for (x = 0; x <= (ByteBuffer.Length - StringBytes.Length); x++)
{
if (ByteBuffer[x] == StringBytes[0])
{
for (y = 1; y < StringBytes.Length && ByteBuffer[x + y] == StringBytes[y]; y++) ;
if (y == StringBytes.Length)
break;
}
}
patch_offset = (x + 6);
string hex = patch_offset.ToString("X");
richTextBox1.Text += "\n" + "Patch location: " + hex;
//label_patch.Text = patchloc.ToString();
}
catch (Exception error)
{
MessageBox.Show("Error is: " + error.Message);
}
}
private void Decrypt_loader()
{
try
{
Process process = new Process();
process.StartInfo.FileName = "tools\\hactool.exe";
process.StartInfo.Arguments = " --intype=kip1 --uncompressed=dec-loader.bin dumped_loader";
process.StartInfo.UseShellExecute = false;
process.StartInfo.CreateNoWindow = true;
process.Start();
process.WaitForExit(); //make sure hactool has completed the decryption.
richTextBox1.Text += "\n" + "Loader decrypted";
//we may as well remove the dumped loader here as it's not needed anymore
if (checkBox_extracted.Checked)
{
File.Delete("dumped_loader");
richTextBox1.Text += "\n" + "Extracted loader cleaned up";
}
}
catch (Exception error)
{
MessageBox.Show("Error is: " + error.Message);
}
}
private void Sha256()
{
try
{
using (SHA256 mySHA256 = SHA256.Create())
{
using (BinaryReader read = new BinaryReader(new FileStream("dumped_loader", FileMode.Open)))
{
read.BaseStream.Position = 0;
byte[] hashValue = mySHA256.ComputeHash(read.BaseStream);
shaValue = String.Join("", hashValue.Select(x => x.ToString("X2")));
richTextBox1.Text += "\n" + "Sha256: " + shaValue;
//note - we should name our ips patch with the shaValue from above.
}
}
}
catch (Exception error)
{
MessageBox.Show("Error is: " + error.Message);
}
}
private static void AppendChunk(Stream output, Stream input, long start, long end)
{
long xsize = end - start;
byte[] buffer = new byte[1 * 1024]; // Copy 1K at a time
input.Position = start;
while (xsize > 0)
{
int bytesRead = input.Read(buffer, 0, (int)Math.Min(xsize, buffer.Length));
if (bytesRead <= 0)
{
throw new EndOfStreamException("Not enough data");
}
output.Write(buffer, 0, bytesRead);
xsize -= bytesRead;
}
}