Added libwiisharp components

common key now from libwiisharp
This commit is contained in:
givememystuffplease 2010-07-06 18:45:55 +00:00
parent f4d321b377
commit c67e219ac3
14 changed files with 5984 additions and 44 deletions

View File

@ -0,0 +1,494 @@
/* This file is part of libWiiSharp
* Copyright (C) 2009 Leathl
*
* libWiiSharp is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* libWiiSharp is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
using System;
using System.IO;
using System.Security.Cryptography;
namespace libWiiSharp
{
public class CertificateChain : IDisposable
{
private const string certCaHash = "5B7D3EE28706AD8DA2CBD5A6B75C15D0F9B6F318";
private const string certCpHash = "6824D6DA4C25184F0D6DAF6EDB9C0FC57522A41C";
private const string certXsHash = "09787045037121477824BC6A3E5E076156573F8A";
private SHA1 sha = SHA1.Create();
private bool[] certsComplete = new bool[3];
private byte[] certCa = new byte[0x400];
private byte[] certCp = new byte[0x300];
private byte[] certXs = new byte[0x300];
/// <summary>
/// If false, the Certificate Chain is not complete (i.e. at least one certificate is missing).
/// </summary>
public bool CertsComplete { get { return (certsComplete[0] && certsComplete[1] && certsComplete[2]); } }
#region IDisposable Members
private bool isDisposed = false;
~CertificateChain()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing && !isDisposed)
{
sha.Clear();
sha = null;
certsComplete = null;
certCa = null;
certCp = null;
certXs = null;
}
isDisposed = true;
}
#endregion
#region Public Functions
/// <summary>
/// Loads a cert file.
/// </summary>
/// <param name="pathToCert"></param>
/// <returns></returns>
public static CertificateChain Load(string pathToCert)
{
return Load(File.ReadAllBytes(pathToCert));
}
/// <summary>
/// Loads a cert file.
/// </summary>
/// <param name="certFile"></param>
/// <returns></returns>
public static CertificateChain Load(byte[] certFile)
{
CertificateChain c = new CertificateChain();
MemoryStream ms = new MemoryStream(certFile);
try { c.parseCert(ms); }
catch { ms.Dispose(); throw; }
ms.Dispose();
return c;
}
/// <summary>
/// Loads a cert file.
/// </summary>
/// <param name="cert"></param>
/// <returns></returns>
public static CertificateChain Load(Stream cert)
{
CertificateChain c = new CertificateChain();
c.parseCert(cert);
return c;
}
/// <summary>
/// Grabs certificates from Ticket and Tmd.
/// Ticket and Tmd must contain certs! (They do when they're downloaded from NUS!)
/// </summary>
/// <param name="pathToTik"></param>
/// <param name="pathToTmd"></param>
/// <returns></returns>
public static CertificateChain FromTikTmd(string pathToTik, string pathToTmd)
{
return FromTikTmd(File.ReadAllBytes(pathToTik), File.ReadAllBytes(pathToTmd));
}
/// <summary>
/// Grabs certificates from Ticket and Tmd.
/// Ticket and Tmd must contain certs! (They do when they're downloaded from NUS!)
/// </summary>
/// <param name="tikFile"></param>
/// <param name="tmdFile"></param>
/// <returns></returns>
public static CertificateChain FromTikTmd(byte[] tikFile, byte[] tmdFile)
{
CertificateChain c = new CertificateChain();
MemoryStream ms = new MemoryStream(tikFile);
try { c.grabFromTik(ms); }
catch { ms.Dispose(); throw; }
ms = new MemoryStream(tmdFile);
try { c.grabFromTmd(ms); }
catch { ms.Dispose(); throw; }
ms.Dispose();
if (!c.CertsComplete) throw new Exception("Couldn't locate all certs!");
return c;
}
/// <summary>
/// Grabs certificates from Ticket and Tmd.
/// Ticket and Tmd must contain certs! (They do when they're downloaded from NUS!)
/// </summary>
/// <param name="tik"></param>
/// <param name="tmd"></param>
/// <returns></returns>
public static CertificateChain FromTikTmd(Stream tik, Stream tmd)
{
CertificateChain c = new CertificateChain();
c.grabFromTik(tik);
c.grabFromTmd(tmd);
return c;
}
/// <summary>
/// Loads a cert file.
/// </summary>
/// <param name="pathToCert"></param>
public void LoadFile(string pathToCert)
{
LoadFile(File.ReadAllBytes(pathToCert));
}
/// <summary>
/// Loads a cert file.
/// </summary>
/// <param name="certFile"></param>
public void LoadFile(byte[] certFile)
{
MemoryStream ms = new MemoryStream(certFile);
try { parseCert(ms); }
catch { ms.Dispose(); throw; }
ms.Dispose();
}
/// <summary>
/// Loads a cert file.
/// </summary>
/// <param name="cert"></param>
public void LoadFile(Stream cert)
{
parseCert(cert);
}
/// <summary>
/// Grabs certificates from Ticket and Tmd.
/// Ticket and Tmd must contain certs! (They do when they're downloaded from NUS!)
/// </summary>
/// <param name="pathToTik"></param>
/// <param name="pathToTmd"></param>
/// <returns></returns>
public void LoadFromTikTmd(string pathToTik, string pathToTmd)
{
LoadFromTikTmd(File.ReadAllBytes(pathToTik), File.ReadAllBytes(pathToTmd));
}
/// <summary>
/// Grabs certificates from Ticket and Tmd.
/// Ticket and Tmd must contain certs! (They do when they're downloaded from NUS!)
/// </summary>
/// <param name="tikFile"></param>
/// <param name="tmdFile"></param>
public void LoadFromTikTmd(byte[] tikFile, byte[] tmdFile)
{
MemoryStream ms = new MemoryStream(tikFile);
try { grabFromTik(ms); }
catch { ms.Dispose(); throw; }
ms = new MemoryStream(tmdFile);
try { grabFromTmd(ms); }
catch { ms.Dispose(); throw; }
ms.Dispose();
if (!CertsComplete) throw new Exception("Couldn't locate all certs!");
}
/// <summary>
/// Grabs certificates from Ticket and Tmd.
/// Ticket and Tmd must contain certs! (They do when they're downloaded from NUS!)
/// </summary>
/// <param name="tik"></param>
/// <param name="tmd"></param>
public void LoadFromTikTmd(Stream tik, Stream tmd)
{
grabFromTik(tik);
grabFromTmd(tmd);
}
/// <summary>
/// Saves the Certificate Chain.
/// </summary>
/// <param name="savePath"></param>
public void Save(string savePath)
{
if (File.Exists(savePath)) File.Delete(savePath);
using (FileStream fs = new FileStream(savePath, FileMode.Create))
writeToStream(fs);
}
/// <summary>
/// Returns the Certificate Chain as a memory stream.
/// </summary>
/// <returns></returns>
public MemoryStream ToMemoryStream()
{
MemoryStream ms = new MemoryStream();
try { writeToStream(ms); }
catch { ms.Dispose(); throw; }
return ms;
}
/// <summary>
/// Returns the Certificate Chain as a byte array.
/// </summary>
/// <returns></returns>
public byte[] ToByteArray()
{
MemoryStream ms = new MemoryStream();
try { writeToStream(ms); }
catch { ms.Dispose(); throw; }
byte[] res = ms.ToArray();
ms.Dispose();
return res;
}
#endregion
#region Private Functions
private void writeToStream(Stream writeStream)
{
fireDebug("Writing Certificate Chain...");
if (!CertsComplete)
{ fireDebug(" Certificate Chain incomplete..."); throw new Exception("At least one certificate is missing!"); }
writeStream.Seek(0, SeekOrigin.Begin);
fireDebug(" Writing Certificate CA... (Offset: 0x{0})", writeStream.Position.ToString("x8"));
writeStream.Write(certCa, 0, certCa.Length);
fireDebug(" Writing Certificate CP... (Offset: 0x{0})", writeStream.Position.ToString("x8"));
writeStream.Write(certCp, 0, certCp.Length);
fireDebug(" Writing Certificate XS... (Offset: 0x{0})", writeStream.Position.ToString("x8"));
writeStream.Write(certXs, 0, certXs.Length);
fireDebug("Writing Certificate Chain Finished...");
}
private void parseCert(Stream certFile)
{
fireDebug("Parsing Certificate Chain...");
int off = 0;
for (int i = 0; i < 3; i++)
{
fireDebug(" Scanning at Offset 0x{0}:", off.ToString("x8"));
try
{
certFile.Seek(off, SeekOrigin.Begin);
byte[] temp = new byte[0x400];
certFile.Read(temp, 0, temp.Length);
fireDebug(" Checking for Certificate CA...");
if (isCertCa(temp) && !certsComplete[1])
{ fireDebug(" Certificate CA detected..."); certCa = temp; certsComplete[1] = true; off += 0x400; continue; }
fireDebug(" Checking for Certificate CP...");
if (isCertCp(temp) && !certsComplete[2])
{ fireDebug(" Certificate CP detected..."); Array.Resize(ref temp, 0x300); certCp = temp; certsComplete[2] = true; off += 0x300; continue; }
fireDebug(" Checking for Certificate XS...");
if (isCertXs(temp) && !certsComplete[0])
{ fireDebug(" Certificate XS detected..."); Array.Resize(ref temp, 0x300); certXs = temp; certsComplete[0] = true; off += 0x300; continue; }
}
catch (Exception ex) { fireDebug("Error: {0}", ex.Message); }
off += 0x300;
}
if (!CertsComplete)
{ fireDebug(" Couldn't locate all Certificates..."); throw new Exception("Couldn't locate all certs!"); }
fireDebug("Parsing Certificate Chain Finished...");
}
private void grabFromTik(Stream tik)
{
fireDebug("Scanning Ticket for Certificates...");
int off = 676;
for (int i = 0; i < 3; i++)
{
fireDebug(" Scanning at Offset 0x{0}:", off.ToString("x8"));
try
{
tik.Seek(off, SeekOrigin.Begin);
byte[] temp = new byte[0x400];
tik.Read(temp, 0, temp.Length);
fireDebug(" Checking for Certificate CA...");
if (isCertCa(temp) && !certsComplete[1])
{ fireDebug(" Certificate CA detected..."); certCa = temp; certsComplete[1] = true; off += 0x400; continue; }
fireDebug(" Checking for Certificate CP...");
if (isCertCp(temp) && !certsComplete[2])
{ fireDebug(" Certificate CP detected..."); Array.Resize(ref temp, 0x300); certCp = temp; certsComplete[2] = true; off += 0x300; continue; }
fireDebug(" Checking for Certificate XS...");
if (isCertXs(temp) && !certsComplete[0])
{ fireDebug(" Certificate XS detected..."); Array.Resize(ref temp, 0x300); certXs = temp; certsComplete[0] = true; off += 0x300; continue; }
}
catch { }
off += 0x300;
}
fireDebug("Scanning Ticket for Certificates Finished...");
}
private void grabFromTmd(Stream tmd)
{
fireDebug("Scanning TMD for Certificates...");
byte[] temp = new byte[2];
tmd.Seek(478, SeekOrigin.Begin);
tmd.Read(temp, 0, 2);
int numContents = Shared.Swap(BitConverter.ToUInt16(temp, 0));
int off = 484 + numContents * 36;
for (int i = 0; i < 3; i++)
{
fireDebug(" Scanning at Offset 0x{0}:", off.ToString("x8"));
try
{
tmd.Seek(off, SeekOrigin.Begin);
temp = new byte[0x400];
tmd.Read(temp, 0, temp.Length);
fireDebug(" Checking for Certificate CA...");
if (isCertCa(temp) && !certsComplete[1])
{ fireDebug(" Certificate CA detected..."); certCa = temp; certsComplete[1] = true; off += 0x400; continue; }
fireDebug(" Checking for Certificate CP...");
if (isCertCp(temp) && !certsComplete[2])
{ fireDebug(" Certificate CP detected..."); Array.Resize(ref temp, 0x300); certCp = temp; certsComplete[2] = true; off += 0x300; continue; }
fireDebug(" Checking for Certificate XS...");
if (isCertXs(temp) && !certsComplete[0])
{ fireDebug(" Certificate XS detected..."); Array.Resize(ref temp, 0x300); certXs = temp; certsComplete[0] = true; off += 0x300; continue; }
}
catch { }
off += 0x300;
}
fireDebug("Scanning TMD for Certificates Finished...");
}
private bool isCertXs(byte[] part)
{
if (part.Length < 0x300) return false;
else if (part.Length > 0x300) Array.Resize(ref part, 0x300);
if (part[0x184] == 'X' && part[0x185] == 'S')
{
byte[] newHash = sha.ComputeHash(part);
byte[] oldHash = Shared.HexStringToByteArray(certXsHash);
if (Shared.CompareByteArrays(newHash, oldHash)) return true;
}
return false;
}
private bool isCertCa(byte[] part)
{
if (part.Length < 0x400) return false;
else if (part.Length > 0x400) Array.Resize(ref part, 0x400);
if (part[0x284] == 'C' && part[0x285] == 'A')
{
byte[] newHash = sha.ComputeHash(part);
byte[] oldHash = Shared.HexStringToByteArray(certCaHash);
if (Shared.CompareByteArrays(newHash, oldHash)) return true;
}
return false;
}
private bool isCertCp(byte[] part)
{
if (part.Length < 0x300) return false;
else if (part.Length > 0x300) Array.Resize(ref part, 0x300);
if (part[0x184] == 'C' && part[0x185] == 'P')
{
byte[] newHash = sha.ComputeHash(part);
byte[] oldHash = Shared.HexStringToByteArray(certCpHash);
if (Shared.CompareByteArrays(newHash, oldHash)) return true;
}
return false;
}
#endregion
#region Events
/// <summary>
/// Fires debugging messages. You may write them into a log file or log textbox.
/// </summary>
public event EventHandler<MessageEventArgs> Debug;
private void fireDebug(string debugMessage, params object[] args)
{
EventHandler<MessageEventArgs> debug = Debug;
if (debug != null)
debug(new object(), new MessageEventArgs(string.Format(debugMessage, args)));
}
#endregion
}
}

View File

@ -0,0 +1,35 @@
/* This file is part of libWiiSharp
* Copyright (C) 2009 Leathl
*
* libWiiSharp is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* libWiiSharp is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace libWiiSharp
{
public class CommonKey
{
private static string standardKey = "ebe42a225e8593e448d9c5457381aaf7";
private static string koreanKey = "63b82bb4f4614e2e13f2fefbba4c9b7e";
public static byte[] GetStandardKey()
{
return Shared.HexStringToByteArray(standardKey);
}
public static byte[] GetKoreanKey()
{
return Shared.HexStringToByteArray(koreanKey);
}
}
}

View File

@ -0,0 +1,44 @@
/* This file is part of libWiiSharp
* Copyright (C) 2009 Leathl
*
* libWiiSharp is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* libWiiSharp is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using System.Text;
namespace libWiiSharp
{
internal struct ContentIndices : IComparable
{
private int index;
private int contentIndex;
public int Index { get { return index; } }
public int ContentIndex { get { return contentIndex; } }
public ContentIndices(int index, int contentIndex)
{
this.index = index;
this.contentIndex = contentIndex;
}
public int CompareTo(object obj)
{
if (obj is ContentIndices) return contentIndex.CompareTo(((ContentIndices)obj).contentIndex);
else throw new ArgumentException();
}
}
}

View File

@ -63,10 +63,6 @@
this.toolStripSeparator3 = new System.Windows.Forms.ToolStripSeparator();
this.proxySettingsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripSeparator6 = new System.Windows.Forms.ToolStripSeparator();
//this.getCommonKeyMenuItem = new System.Windows.Forms.ToolStripMenuItem();
//this.commonKeykeybinToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
//this.koreanKeykkeybinToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripSeparator5 = new System.Windows.Forms.ToolStripSeparator();
this.aboutNUSDToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.proxyBox = new System.Windows.Forms.GroupBox();
this.label13 = new System.Windows.Forms.Label();
@ -339,11 +335,9 @@
this.toolStripSeparator3,
this.proxySettingsToolStripMenuItem,
this.toolStripSeparator6,
//this.getCommonKeyMenuItem,
this.toolStripSeparator5,
this.aboutNUSDToolStripMenuItem});
this.extrasStrip.Name = "extrasStrip";
this.extrasStrip.Size = new System.Drawing.Size(183, 110);
this.extrasStrip.Size = new System.Drawing.Size(183, 82);
//
// loadInfoFromTMDToolStripMenuItem
//
@ -371,35 +365,6 @@
this.toolStripSeparator6.Name = "toolStripSeparator6";
this.toolStripSeparator6.Size = new System.Drawing.Size(179, 6);
//
// getCommonKeyMenuItem
//
/*this.getCommonKeyMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.commonKeykeybinToolStripMenuItem,
this.koreanKeykkeybinToolStripMenuItem});
this.getCommonKeyMenuItem.Image = global::NUS_Downloader.Properties.Resources.key;
this.getCommonKeyMenuItem.Name = "getCommonKeyMenuItem";
this.getCommonKeyMenuItem.Size = new System.Drawing.Size(182, 22);
this.getCommonKeyMenuItem.Text = "Retrieve Key";
//
// commonKeykeybinToolStripMenuItem
//
this.commonKeykeybinToolStripMenuItem.Name = "commonKeykeybinToolStripMenuItem";
this.commonKeykeybinToolStripMenuItem.Size = new System.Drawing.Size(196, 22);
this.commonKeykeybinToolStripMenuItem.Text = "Common Key (key.bin)";
this.commonKeykeybinToolStripMenuItem.Click += new System.EventHandler(this.commonKeykeybinToolStripMenuItem_Click);
//
// koreanKeykkeybinToolStripMenuItem
//
this.koreanKeykkeybinToolStripMenuItem.Name = "koreanKeykkeybinToolStripMenuItem";
this.koreanKeykkeybinToolStripMenuItem.Size = new System.Drawing.Size(196, 22);
this.koreanKeykkeybinToolStripMenuItem.Text = "Korean Key (kkey.bin)";
this.koreanKeykkeybinToolStripMenuItem.Click += new System.EventHandler(this.koreanKeykkeybinToolStripMenuItem_Click);*/
//
// toolStripSeparator5
//
this.toolStripSeparator5.Name = "toolStripSeparator5";
this.toolStripSeparator5.Size = new System.Drawing.Size(179, 6);
//
// aboutNUSDToolStripMenuItem
//
this.aboutNUSDToolStripMenuItem.Image = global::NUS_Downloader.Properties.Resources.information;
@ -916,7 +881,6 @@
private System.Windows.Forms.ToolStripMenuItem PALMassUpdate;
private System.Windows.Forms.ToolStripMenuItem NTSCMassUpdate;
private System.Windows.Forms.ToolStripMenuItem KoreaMassUpdate;
private System.Windows.Forms.ToolStripSeparator toolStripSeparator5;
private System.Windows.Forms.ToolStripMenuItem aboutNUSDToolStripMenuItem;
private System.Windows.Forms.CheckBox checkBox1;
private System.Windows.Forms.Button SaveProxyPwdPermanentBtn;

View File

@ -114,6 +114,7 @@ namespace NUS_Downloader
// Scripts Thread
private BackgroundWorker scriptsWorker;
/*
// Common Key hash
private static byte[] wii_commonkey = new byte[16]
{
@ -124,7 +125,7 @@ namespace NUS_Downloader
{
0x63, 0xb8, 0x2b, 0xb4, 0xf4, 0x61, 0x4e, 0x2e, 0x13, 0xf2, 0xfe, 0xfb, 0xba, 0x4c, 0x9b, 0x7e
};
*/
/*
public struct WADHeader
{
@ -262,7 +263,7 @@ namespace NUS_Downloader
{
WriteStatus("Korean Common Key detected.");
}*/
WriteStatus("Common keys are compiled in, key.bin/kkey.bin not read.");
//WriteStatus("Common keys are compiled in, key.bin/kkey.bin not read.");
// Check for DSi common key bin file...
if (NUSDFileExists("dsikey.bin") == true)
@ -1011,15 +1012,15 @@ namespace NUS_Downloader
if (cetkbuf[0x01F1] == 0x01)
{
WriteStatus("Key Type: Korean");
keyBytes = LoadCommonKey("kkey.bin");
keyBytes = libWiiSharp.CommonKey.GetKoreanKey();
}
else
{
WriteStatus("Key Type: Standard");
if (wiimode)
keyBytes = LoadCommonKey("key.bin");
keyBytes = libWiiSharp.CommonKey.GetStandardKey();
else
keyBytes = LoadCommonKey("dsikey.bin");
keyBytes = File.ReadAllBytes(Path.Combine(CURRENT_DIR, "dsikey.bin"));
}
initCrypt(iv, keyBytes);
@ -1530,6 +1531,7 @@ namespace NUS_Downloader
return sha.ComputeHash(data);
}
/*
/// <summary>
/// Loads the common key from disc.
/// </summary>
@ -1552,8 +1554,9 @@ namespace NUS_Downloader
}
else
return null;
}
}*/
/*
/// <summary>
/// Writes/overwrites the common key onto disc.
/// </summary>
@ -1579,7 +1582,7 @@ namespace NUS_Downloader
WriteStatus(String.Format("Error: Couldn't write {0}: {1}", keyfile, e.Message));
}
return false;
}
}*/
private void button4_Click(object sender, EventArgs e)
{

747
NUS Downloader/Headers.cs Normal file
View File

@ -0,0 +1,747 @@
/* This file is part of libWiiSharp
* Copyright (C) 2009 Leathl
*
* libWiiSharp is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* libWiiSharp is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
using System;
using System.IO;
using System.Security.Cryptography;
namespace libWiiSharp
{
public class Headers
{
private static uint imd5Magic = 0x494d4435;
private static uint imetMagic = 0x494d4554;
/// <summary>
/// Convert HeaderType to int to get it's Length.
/// </summary>
public enum HeaderType
{
None = 0,
/// <summary>
/// Used in opening.bnr
/// </summary>
ShortIMET = 1536,
/// <summary>
/// Used in 00000000.app
/// </summary>
IMET = 1600,
/// <summary>
/// Used in banner.bin / icon.bin
/// </summary>
IMD5 = 32,
}
#region Public Functions
/// <summary>
/// Checks a file for Headers.
/// </summary>
/// <param name="pathToFile"></param>
/// <returns></returns>
public static HeaderType DetectHeader(string pathToFile)
{
return DetectHeader(File.ReadAllBytes(pathToFile));
}
/// <summary>
/// Checks the byte array for Headers.
/// </summary>
/// <param name="file"></param>
/// <returns></returns>
public static HeaderType DetectHeader(byte[] file)
{
if (file.Length > 68)
if (Shared.Swap(BitConverter.ToUInt32(file, 64)) == imetMagic)
return HeaderType.ShortIMET;
if (file.Length > 132)
if (Shared.Swap(BitConverter.ToUInt32(file, 128)) == imetMagic)
return HeaderType.IMET;
if (file.Length > 4)
if (Shared.Swap(BitConverter.ToUInt32(file, 0)) == imd5Magic)
return HeaderType.IMD5;
return HeaderType.None;
}
/// <summary>
/// Checks the stream for Headers.
/// </summary>
/// <param name="file"></param>
/// <returns></returns>
public static HeaderType DetectHeader(Stream file)
{
byte[] tmp = new byte[4];
if (file.Length > 68)
{
file.Seek(64, SeekOrigin.Begin);
file.Read(tmp, 0, tmp.Length);
if (Shared.Swap(BitConverter.ToUInt32(tmp, 0)) == imetMagic)
return HeaderType.ShortIMET;
}
if (file.Length > 132)
{
file.Seek(128, SeekOrigin.Begin);
file.Read(tmp, 0, tmp.Length);
if (Shared.Swap(BitConverter.ToUInt32(tmp, 0)) == imetMagic)
return HeaderType.IMET;
}
if (file.Length > 4)
{
file.Seek(0, SeekOrigin.Begin);
file.Read(tmp, 0, tmp.Length);
if (Shared.Swap(BitConverter.ToUInt32(tmp, 0)) == imd5Magic)
return HeaderType.IMD5;
}
return HeaderType.None;
}
#endregion
public class IMET
{
private bool hashesMatch = true;
private bool isShortImet = false;
private byte[] additionalPadding = new byte[64];
private byte[] padding = new byte[64];
private uint imetMagic = 0x494d4554;
private uint sizeOfHeader = 0x00000600; //Without additionalPadding
private uint unknown = 0x00000003;
private uint iconSize;
private uint bannerSize;
private uint soundSize;
private uint flags = 0x00000000;
private byte[] japaneseTitle = new byte[84];
private byte[] englishTitle = new byte[84];
private byte[] germanTitle = new byte[84];
private byte[] frenchTitle = new byte[84];
private byte[] spanishTitle = new byte[84];
private byte[] italianTitle = new byte[84];
private byte[] dutchTitle = new byte[84];
private byte[] unknownTitle1 = new byte[84];
private byte[] unknownTitle2 = new byte[84];
private byte[] koreanTitle = new byte[84];
private byte[] padding2 = new byte[588];
private byte[] hash = new byte[16];
/// <summary>
/// Short IMET has a padding of 64 bytes at the beginning while Long IMET has 128.
/// </summary>
public bool IsShortIMET { get { return isShortImet; } set { isShortImet = value; } }
/// <summary>
/// The size of uncompressed icon.bin
/// </summary>
public uint IconSize { get { return iconSize; } set { iconSize = value; } }
/// <summary>
/// The size of uncompressed banner.bin
/// </summary>
public uint BannerSize { get { return bannerSize; } set { bannerSize = value; } }
/// <summary>
/// The size of uncompressed sound.bin
/// </summary>
public uint SoundSize { get { return soundSize; } set { soundSize = value; } }
/// <summary>
/// The japanese Title.
/// </summary>
public string JapaneseTitle { get { return returnTitleAsString(japaneseTitle); } set { setTitleFromString(value, 0); } }
/// <summary>
/// The english Title.
/// </summary>
public string EnglishTitle { get { return returnTitleAsString(englishTitle); } set { setTitleFromString(value, 1); } }
/// <summary>
/// The german Title.
/// </summary>
public string GermanTitle { get { return returnTitleAsString(germanTitle); } set { setTitleFromString(value, 2); } }
/// <summary>
/// The french Title.
/// </summary>
public string FrenchTitle { get { return returnTitleAsString(frenchTitle); } set { setTitleFromString(value, 3); } }
/// <summary>
/// The spanish Title.
/// </summary>
public string SpanishTitle { get { return returnTitleAsString(spanishTitle); } set { setTitleFromString(value, 4); } }
/// <summary>
/// The italian Title.
/// </summary>
public string ItalianTitle { get { return returnTitleAsString(italianTitle); } set { setTitleFromString(value, 5); } }
/// <summary>
/// The dutch Title.
/// </summary>
public string DutchTitle { get { return returnTitleAsString(dutchTitle); } set { setTitleFromString(value, 6); } }
/// <summary>
/// The korean Title.
/// </summary>
public string KoreanTitle { get { return returnTitleAsString(koreanTitle); } set { setTitleFromString(value, 7); } }
/// <summary>
/// All Titles as a string array.
/// </summary>
public string[] AllTitles { get { return new string[] { JapaneseTitle, EnglishTitle, GermanTitle, FrenchTitle, SpanishTitle, ItalianTitle, DutchTitle, KoreanTitle }; } }
/// <summary>
/// When parsing an IMET header, this value will turn false if the hash stored in the header doesn't match the headers hash.
/// </summary>
public bool HashesMatch { get { return hashesMatch; } }
#region Public Functions
/// <summary>
/// Loads the IMET Header of a file.
/// </summary>
/// <param name="pathToFile"></param>
/// <returns></returns>
public static IMET Load(string pathToFile)
{
return Load(File.ReadAllBytes(pathToFile));
}
/// <summary>
/// Loads the IMET Header of a byte array.
/// </summary>
/// <param name="fileOrHeader"></param>
/// <returns></returns>
public static IMET Load(byte[] fileOrHeader)
{
HeaderType type = DetectHeader(fileOrHeader);
if (type != HeaderType.IMET && type != HeaderType.ShortIMET)
throw new Exception("No IMET Header found!");
IMET s = new IMET();
if (type == HeaderType.ShortIMET) s.isShortImet = true;
MemoryStream ms = new MemoryStream(fileOrHeader);
try { s.parseHeader(ms); }
catch { ms.Dispose(); throw; }
ms.Dispose();
return s;
}
/// <summary>
/// Loads the IMET Header of a stream.
/// </summary>
/// <param name="fileOrHeader"></param>
/// <returns></returns>
public static IMET Load(Stream fileOrHeader)
{
HeaderType type = DetectHeader(fileOrHeader);
if (type != HeaderType.IMET && type != HeaderType.ShortIMET)
throw new Exception("No IMET Header found!");
IMET s = new IMET();
if (type == HeaderType.ShortIMET) s.isShortImet = true;
s.parseHeader(fileOrHeader);
return s;
}
/// <summary>
/// Creates a new IMET Header.
/// </summary>
/// <param name="isShortImet"></param>
/// <param name="iconSize"></param>
/// <param name="bannerSize"></param>
/// <param name="soundSize"></param>
/// <param name="titles"></param>
/// <returns></returns>
public static IMET Create(bool isShortImet, int iconSize, int bannerSize, int soundSize, params string[] titles)
{
IMET s = new IMET();
s.isShortImet = isShortImet;
for (int i = 0; i < titles.Length; i++)
s.setTitleFromString(titles[i], i);
for (int i = titles.Length; i < 8; i++)
s.setTitleFromString((titles.Length > 1) ? titles[1] : titles[0], i);
s.iconSize = (uint)iconSize;
s.bannerSize = (uint)bannerSize;
s.soundSize = (uint)soundSize;
return s;
}
/// <summary>
/// Removes the IMET Header of a file.
/// </summary>
/// <param name="pathToFile"></param>
public static void RemoveHeader(string pathToFile)
{
byte[] fileWithoutHeader = RemoveHeader(File.ReadAllBytes(pathToFile));
File.Delete(pathToFile);
File.WriteAllBytes(pathToFile, fileWithoutHeader);
}
/// <summary>
/// Removes the IMET Header of a byte array.
/// </summary>
/// <param name="file"></param>
/// <returns></returns>
public static byte[] RemoveHeader(byte[] file)
{
HeaderType type = DetectHeader(file);
if (type != HeaderType.IMET && type != HeaderType.ShortIMET)
throw new Exception("No IMET Header found!");
byte[] fileWithoutHeader = new byte[file.Length - (int)type];
Array.Copy(file, (int)type, fileWithoutHeader, 0, fileWithoutHeader.Length);
return fileWithoutHeader;
}
/// <summary>
/// Sets all title to the given string.
/// </summary>
/// <param name="newTitle"></param>
public void SetAllTitles(string newTitle)
{
for (int i = 0; i < 10; i++)
setTitleFromString(newTitle, i);
}
/// <summary>
/// Returns the Header as a memory stream.
/// </summary>
/// <returns></returns>
public MemoryStream ToMemoryStream()
{
MemoryStream ms = new MemoryStream();
try { writeToStream(ms); }
catch { ms.Dispose(); throw; }
return ms;
}
/// <summary>
/// Returns the Header as a byte array.
/// </summary>
/// <returns></returns>
public byte[] ToByteArray()
{
return ToMemoryStream().ToArray();
}
/// <summary>
/// Writes the Header to the given stream.
/// </summary>
/// <param name="writeStream"></param>
public void Write(Stream writeStream)
{
writeToStream(writeStream);
}
/// <summary>
/// Changes the Titles.
/// </summary>
/// <param name="newTitles"></param>
public void ChangeTitles(params string[] newTitles)
{
for (int i = 0; i < newTitles.Length; i++)
setTitleFromString(newTitles[i], i);
for (int i = newTitles.Length; i < 8; i++)
setTitleFromString((newTitles.Length > 1) ? newTitles[1] : newTitles[0], i);
}
/// <summary>
/// Returns a string array with the Titles.
/// </summary>
/// <returns></returns>
public string[] GetTitles()
{
return new string[] { JapaneseTitle, EnglishTitle, GermanTitle, FrenchTitle, SpanishTitle, ItalianTitle, DutchTitle, KoreanTitle };
}
#endregion
#region Private Functions
private void writeToStream(Stream writeStream)
{
writeStream.Seek(0, SeekOrigin.Begin);
if (!isShortImet) writeStream.Write(additionalPadding, 0, additionalPadding.Length);
writeStream.Write(padding, 0, padding.Length);
writeStream.Write(BitConverter.GetBytes(Shared.Swap(imetMagic)), 0, 4);
writeStream.Write(BitConverter.GetBytes(Shared.Swap(sizeOfHeader)), 0, 4);
writeStream.Write(BitConverter.GetBytes(Shared.Swap(unknown)), 0, 4);
writeStream.Write(BitConverter.GetBytes(Shared.Swap(iconSize)), 0, 4);
writeStream.Write(BitConverter.GetBytes(Shared.Swap(bannerSize)), 0, 4);
writeStream.Write(BitConverter.GetBytes(Shared.Swap(soundSize)), 0, 4);
writeStream.Write(BitConverter.GetBytes(Shared.Swap(flags)), 0, 4);
writeStream.Write(japaneseTitle, 0, japaneseTitle.Length);
writeStream.Write(englishTitle, 0, englishTitle.Length);
writeStream.Write(germanTitle, 0, germanTitle.Length);
writeStream.Write(frenchTitle, 0, frenchTitle.Length);
writeStream.Write(spanishTitle, 0, spanishTitle.Length);
writeStream.Write(italianTitle, 0, italianTitle.Length);
writeStream.Write(dutchTitle, 0, dutchTitle.Length);
writeStream.Write(unknownTitle1, 0, unknownTitle1.Length);
writeStream.Write(unknownTitle2, 0, unknownTitle2.Length);
writeStream.Write(koreanTitle, 0, koreanTitle.Length);
writeStream.Write(padding2, 0, padding2.Length);
int hashPos = (int)writeStream.Position;
hash = new byte[16];
writeStream.Write(hash, 0, hash.Length);
byte[] toHash = new byte[writeStream.Position];
writeStream.Seek(0, SeekOrigin.Begin);
writeStream.Read(toHash, 0, toHash.Length);
computeHash(toHash, isShortImet ? 0 : 0x40);
writeStream.Seek(hashPos, SeekOrigin.Begin);
writeStream.Write(hash, 0, hash.Length);
}
private void computeHash(byte[] headerBytes, int hashPos)
{
MD5 md5 = MD5.Create();
hash = md5.ComputeHash(headerBytes, hashPos, 0x600);
md5.Clear();
}
private void parseHeader(Stream headerStream)
{
headerStream.Seek(0, SeekOrigin.Begin);
byte[] tmp = new byte[4];
if (!isShortImet) headerStream.Read(additionalPadding, 0, additionalPadding.Length);
headerStream.Read(padding, 0, padding.Length);
headerStream.Read(tmp, 0, 4);
if (Shared.Swap(BitConverter.ToUInt32(tmp, 0)) != imetMagic)
throw new Exception("Invalid Magic!");
headerStream.Read(tmp, 0, 4);
if (Shared.Swap(BitConverter.ToUInt32(tmp, 0)) != sizeOfHeader)
throw new Exception("Invalid Header Size!");
headerStream.Read(tmp, 0, 4);
unknown = Shared.Swap(BitConverter.ToUInt32(tmp, 0));
headerStream.Read(tmp, 0, 4);
iconSize = Shared.Swap(BitConverter.ToUInt32(tmp, 0));
headerStream.Read(tmp, 0, 4);
bannerSize = Shared.Swap(BitConverter.ToUInt32(tmp, 0));
headerStream.Read(tmp, 0, 4);
soundSize = Shared.Swap(BitConverter.ToUInt32(tmp, 0));
headerStream.Read(tmp, 0, 4);
flags = Shared.Swap(BitConverter.ToUInt32(tmp, 0));
headerStream.Read(japaneseTitle, 0, japaneseTitle.Length);
headerStream.Read(englishTitle, 0, englishTitle.Length);
headerStream.Read(germanTitle, 0, germanTitle.Length);
headerStream.Read(frenchTitle, 0, frenchTitle.Length);
headerStream.Read(spanishTitle, 0, spanishTitle.Length);
headerStream.Read(italianTitle, 0, italianTitle.Length);
headerStream.Read(dutchTitle, 0, dutchTitle.Length);
headerStream.Read(unknownTitle1, 0, unknownTitle1.Length);
headerStream.Read(unknownTitle2, 0, unknownTitle2.Length);
headerStream.Read(koreanTitle, 0, koreanTitle.Length);
headerStream.Read(padding2, 0, padding2.Length);
headerStream.Read(hash, 0, hash.Length);
headerStream.Seek(-16, SeekOrigin.Current);
headerStream.Write(new byte[16], 0, 16);
byte[] temp = new byte[headerStream.Length];
headerStream.Seek(0, SeekOrigin.Begin);
headerStream.Read(temp, 0, temp.Length);
MD5 m = MD5.Create();
byte[] newHash = m.ComputeHash(temp, (isShortImet) ? 0 : 0x40, 0x600);
m.Clear();
hashesMatch = Shared.CompareByteArrays(newHash, hash);
}
private string returnTitleAsString(byte[] title)
{
string tempStr = string.Empty;
for (int i = 0; i < 84; i += 2)
{
char tempChar = BitConverter.ToChar(new byte[] { title[i + 1], title[i] }, 0);
if (tempChar != 0x00) tempStr += tempChar;
}
return tempStr;
}
private void setTitleFromString(string title, int titleIndex)
{
byte[] tempArray = new byte[84];
for (int i = 0; i < title.Length; i++)
{
byte[] tempBytes = BitConverter.GetBytes(title[i]);
tempArray[i * 2 + 1] = tempBytes[0];
tempArray[i * 2] = tempBytes[1];
}
switch (titleIndex)
{
case 0:
japaneseTitle = tempArray;
break;
case 1:
englishTitle = tempArray;
break;
case 2:
germanTitle = tempArray;
break;
case 3:
frenchTitle = tempArray;
break;
case 4:
spanishTitle = tempArray;
break;
case 5:
italianTitle = tempArray;
break;
case 6:
dutchTitle = tempArray;
break;
case 7:
koreanTitle = tempArray;
break;
}
}
#endregion
}
public class IMD5
{
private uint imd5Magic = 0x494d4435;
private uint fileSize;
private byte[] padding = new byte[8];
private byte[] hash = new byte[16];
/// <summary>
/// The size of the file without the IMD5 Header.
/// </summary>
public uint FileSize { get { return fileSize; } }
/// <summary>
/// The hash of the file without the IMD5 Header.
/// </summary>
public byte[] Hash { get { return hash; } }
private IMD5() { }
#region Public Functions
/// <summary>
/// Loads the IMD5 Header of a file.
/// </summary>
/// <param name="pathToFile"></param>
/// <returns></returns>
public static IMD5 Load(string pathToFile)
{
return Load(File.ReadAllBytes(pathToFile));
}
/// <summary>
/// Loads the IMD5 Header of a byte array.
/// </summary>
/// <param name="fileOrHeader"></param>
/// <returns></returns>
public static IMD5 Load(byte[] fileOrHeader)
{
HeaderType type = DetectHeader(fileOrHeader);
if (type != HeaderType.IMD5)
throw new Exception("No IMD5 Header found!");
IMD5 h = new IMD5();
MemoryStream ms = new MemoryStream(fileOrHeader);
try { h.parseHeader(ms); }
catch { ms.Dispose(); throw; }
ms.Dispose();
return h;
}
/// <summary>
/// Loads the IMD5 Header of a stream.
/// </summary>
/// <param name="fileOrHeader"></param>
/// <returns></returns>
public static IMD5 Load(Stream fileOrHeader)
{
HeaderType type = DetectHeader(fileOrHeader);
if (type != HeaderType.IMD5)
throw new Exception("No IMD5 Header found!");
IMD5 h = new IMD5();
h.parseHeader(fileOrHeader);
return h;
}
/// <summary>
/// Creates a new IMD5 Header.
/// </summary>
/// <param name="file"></param>
/// <returns></returns>
public static IMD5 Create(byte[] file)
{
IMD5 h = new IMD5();
h.fileSize = (uint)file.Length;
h.computeHash(file);
return h;
}
/// <summary>
/// Adds an IMD5 Header to a file.
/// </summary>
/// <param name="pathToFile"></param>
public static void AddHeader(string pathToFile)
{
byte[] fileWithHeader = AddHeader(File.ReadAllBytes(pathToFile));
File.Delete(pathToFile);
using (FileStream fs = new FileStream(pathToFile, FileMode.Create))
fs.Write(fileWithHeader, 0, fileWithHeader.Length);
}
/// <summary>
/// Adds an IMD5 Header to a byte array.
/// </summary>
/// <param name="file"></param>
/// <returns></returns>
public static byte[] AddHeader(byte[] file)
{
IMD5 h = IMD5.Create(file);
MemoryStream ms = new MemoryStream();
h.writeToStream(ms);
ms.Write(file, 0, file.Length);
byte[] res = ms.ToArray();
ms.Dispose();
return res;
}
/// <summary>
/// Removes the IMD5 Header of a file.
/// </summary>
/// <param name="pathToFile"></param>
public static void RemoveHeader(string pathToFile)
{
byte[] fileWithoutHeader = RemoveHeader(File.ReadAllBytes(pathToFile));
File.Delete(pathToFile);
using (FileStream fs = new FileStream(pathToFile, FileMode.Create))
fs.Write(fileWithoutHeader, 0, fileWithoutHeader.Length);
}
/// <summary>
/// Removes the IMD5 Header of a byte array.
/// </summary>
/// <param name="file"></param>
/// <returns></returns>
public static byte[] RemoveHeader(byte[] file)
{
MemoryStream ms = new MemoryStream();
ms.Write(file, 32, file.Length - 32);
byte[] ret = ms.ToArray();
ms.Dispose();
return ret;
}
/// <summary>
/// Returns the IMD5 Header as a memory stream.
/// </summary>
/// <returns></returns>
public MemoryStream ToMemoryStream()
{
MemoryStream ms = new MemoryStream();
try { writeToStream(ms); }
catch { ms.Dispose(); throw; }
return ms;
}
/// <summary>
/// Returns the IMD5 Header as a byte array.
/// </summary>
/// <returns></returns>
public byte[] ToByteArray()
{
return ToMemoryStream().ToArray();
}
/// <summary>
/// Writes the IMD5 Header to the given stream.
/// </summary>
/// <param name="writeStream"></param>
public void Write(Stream writeStream)
{
writeToStream(writeStream);
}
#endregion
#region Private Functions
private void writeToStream(Stream writeStream)
{
writeStream.Seek(0, SeekOrigin.Begin);
writeStream.Write(BitConverter.GetBytes(Shared.Swap(imd5Magic)), 0, 4);
writeStream.Write(BitConverter.GetBytes(Shared.Swap(fileSize)), 0, 4);
writeStream.Write(padding, 0, padding.Length);
writeStream.Write(hash, 0, hash.Length);
}
private void computeHash(byte[] bytesToHash)
{
MD5 md5 = MD5.Create();
hash = md5.ComputeHash(bytesToHash);
md5.Clear();
}
private void parseHeader(Stream headerStream)
{
headerStream.Seek(0, SeekOrigin.Begin);
byte[] tmp = new byte[4];
headerStream.Read(tmp, 0, 4);
if (Shared.Swap(BitConverter.ToUInt32(tmp, 0)) != imd5Magic)
throw new Exception("Invalid Magic!");
headerStream.Read(tmp, 0, 4);
fileSize = Shared.Swap(BitConverter.ToUInt32(tmp, 0));
headerStream.Read(padding, 0, padding.Length);
headerStream.Read(hash, 0, hash.Length);
}
#endregion
}
}
}

View File

@ -0,0 +1,313 @@
/* This file is part of libWiiSharp
* Copyright (C) 2009 Leathl
*
* libWiiSharp is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* libWiiSharp is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
//Patches based on Dop-Mii - Thanks to Arikado and Lunatik
using System;
using System.ComponentModel;
using System.Text;
namespace libWiiSharp
{
/// <summary>
/// An IOS patcher which can patch fakesigning, es_identify and nand permissions.
/// </summary>
public class IosPatcher
{
private WAD wadFile;
private int esIndex = -1;
#region Public Functions
/// <summary>
/// Loads an IOS wad to patch the es module.
/// </summary>
/// <param name="iosWad"></param>
public void LoadIOS(ref WAD iosWad)
{
if ((iosWad.TitleID >> 32) != 1 ||
(iosWad.TitleID & 0xFFFFFFFF) < 3 ||
(iosWad.TitleID & 0xFFFFFFFF) > 255)
throw new Exception("Only IOS WADs can be patched!");
wadFile = iosWad;
getEsIndex();
}
/// <summary>
/// Patches fakesigning.
/// Returns the number of applied patches.
/// </summary>
/// <returns></returns>
public int PatchFakeSigning()
{
if (esIndex < 0) return -1;
return patchFakeSigning(ref wadFile.Contents[esIndex]);
}
/// <summary>
/// Patches es_identify.
/// Returns the number of applied patches.
/// </summary>
/// <returns></returns>
public int PatchEsIdentify()
{
if (esIndex < 0) return -1;
return patchEsIdentify(ref wadFile.Contents[esIndex]);
}
/// <summary>
/// Patches nand permissions.
/// Returns the number of applied patches.
/// </summary>
/// <returns></returns>
public int PatchNandPermissions()
{
if (esIndex < 0) return -1;
return patchNandPermissions(ref wadFile.Contents[esIndex]);
}
/// <summary>
/// Patches fakesigning, es_identify and nand permissions.
/// Returns the number of applied patches.
/// </summary>
/// <returns></returns>
public int PatchAll()
{
if (esIndex < 0) return -1;
return patchAll(ref wadFile.Contents[esIndex]);
}
/// <summary>
/// Patches fakesigning.
/// Returns the number of applied patches.
/// </summary>
/// <returns></returns>
public int PatchFakeSigning(ref byte[] esModule)
{
return patchFakeSigning(ref esModule);
}
/// <summary>
/// Patches es_identify.
/// Returns the number of applied patches.
/// </summary>
/// <returns></returns>
public int PatchEsIdentify(ref byte[] esModule)
{
return patchEsIdentify(ref esModule);
}
/// <summary>
/// Patches nand permissions.
/// Returns the number of applied patches.
/// </summary>
/// <returns></returns>
public int PatchNandPermissions(ref byte[] esModule)
{
return patchNandPermissions(ref esModule);
}
/// <summary>
/// Patches fakesigning, es_identify and nand permissions.
/// Returns the number of applied patches.
/// </summary>
/// <returns></returns>
public int PatchAll(ref byte[] esModule)
{
return patchAll(ref esModule);
}
#endregion
#region Private Functions
private int patchFakeSigning(ref byte[] esModule)
{
fireDebug("Patching Fakesigning...");
int patchCount = 0;
byte[] oldHashCheck = { 0x20, 0x07, 0x23, 0xA2 };
byte[] newHashCheck = { 0x20, 0x07, 0x4B, 0x0B };
for (int i = 0; i < esModule.Length - 4; i++)
{
fireProgress((i + 1) * 100 / esModule.Length);
if (Shared.CompareByteArrays(esModule, i, oldHashCheck, 0, 4) ||
Shared.CompareByteArrays(esModule, i, newHashCheck, 0, 4))
{
fireDebug(" Patching at Offset: 0x{0}", i.ToString("x8").ToUpper());
esModule[i + 1] = 0x00;
i += 4;
patchCount++;
}
}
fireDebug("Patching Fakesigning Finished... (Patches applied: {0})", patchCount);
return patchCount;
}
private int patchEsIdentify(ref byte[] esModule)
{
fireDebug("Patching ES_Identify...");
int patchCount = 0;
byte[] identifyCheck = { 0x28, 0x03, 0xD1, 0x23 };
for (int i = 0; i < esModule.Length - 4; i++)
{
fireProgress((i + 1) * 100 / esModule.Length);
if (Shared.CompareByteArrays(esModule, i, identifyCheck, 0, 4))
{
fireDebug(" Patching at Offset: 0x{0}", i.ToString("x8").ToUpper());
esModule[i + 2] = 0x00;
esModule[i + 3] = 0x00;
i += 4;
patchCount++;
}
}
fireDebug("Patching ES_Identify Finished... (Patches applied: {0})", patchCount);
return patchCount;
}
private int patchNandPermissions(ref byte[] esModule)
{
fireDebug("Patching NAND Permissions...");
int patchCount = 0;
byte[] permissionTable = { 0x42, 0x8B, 0xD0, 0x01, 0x25, 0x66 };
for (int i = 0; i < esModule.Length - 6; i++)
{
fireProgress((i + 1) * 100 / esModule.Length);
if (Shared.CompareByteArrays(esModule, i, permissionTable, 0, 6))
{
fireDebug(" Patching at Offset: 0x{0}", i.ToString("x8").ToUpper());
esModule[i + 2] = 0xE0;
i += 6;
patchCount++;
}
}
fireDebug("Patching NAND Permissions Finished... (Patches applied: {0})", patchCount);
return patchCount;
}
private int patchAll(ref byte[] esModule)
{
fireDebug("Patching Fakesigning, ES_Identify and NAND Permissions...");
int patchCount = 0;
byte[] oldHashCheck = { 0x20, 0x07, 0x23, 0xA2 };
byte[] newHashCheck = { 0x20, 0x07, 0x4B, 0x0B };
byte[] identifyCheck = { 0x28, 0x03, 0xD1, 0x23 };
byte[] permissionTable = { 0x42, 0x8B, 0xD0, 0x01, 0x25, 0x66 };
for (int i = 0; i < esModule.Length - 6; i++)
{
fireProgress((i + 1) * 100 / esModule.Length);
if (Shared.CompareByteArrays(esModule, i, oldHashCheck, 0, 4) ||
Shared.CompareByteArrays(esModule, i, newHashCheck, 0, 4))
{
fireDebug(" Patching Fakesigning at Offset: 0x{0}", i.ToString("x8").ToUpper());
esModule[i + 1] = 0x00;
i += 4;
patchCount++;
}
else if (Shared.CompareByteArrays(esModule, i, identifyCheck, 0, 4))
{
fireDebug(" Patching ES_Identify at Offset: 0x{0}", i.ToString("x8").ToUpper());
esModule[i + 2] = 0x00;
esModule[i + 3] = 0x00;
i += 4;
patchCount++;
}
else if (Shared.CompareByteArrays(esModule, i, permissionTable, 0, 6))
{
fireDebug(" Patching NAND Permissions at Offset: 0x{0}", i.ToString("x8").ToUpper());
esModule[i + 2] = 0xE0;
i += 6;
patchCount++;
}
}
fireDebug("Patching Fakesigning, ES_Identify and NAND Permissions Finished... (Patches applied: {0})", patchCount);
return patchCount;
}
private void getEsIndex()
{
fireDebug("Scanning for ES Module...");
string iosTag = "$IOSVersion:";
for (int i = wadFile.NumOfContents - 1; i >= 0; i--)
{
fireDebug(" Scanning Content #{0} of {1}...", i + 1, wadFile.NumOfContents);
fireProgress((i + 1) * 100 / wadFile.NumOfContents);
for (int j = 0; j < wadFile.Contents[i].Length - 64; j++)
{
if (ASCIIEncoding.ASCII.GetString(wadFile.Contents[i], j, 12) == iosTag)
{
int curIndex = j + 12;
while (wadFile.Contents[i][curIndex] == ' ') { curIndex++; }
if (ASCIIEncoding.ASCII.GetString(wadFile.Contents[i], curIndex, 3) == "ES:")
{
fireDebug(" -> ES Module found!");
fireDebug("Scanning for ES Module Finished...");
esIndex = i;
fireProgress(100);
return;
}
}
}
}
fireDebug(@"/!\/!\/!\ ES Module wasn't found! /!\/!\/!\");
throw new Exception("ES module wasn't found!");
}
#endregion
#region Events
/// <summary>
/// Fires the Progress of various operations
/// </summary>
public event EventHandler<ProgressChangedEventArgs> Progress;
/// <summary>
/// Fires debugging messages. You may write them into a log file or log textbox.
/// </summary>
public event EventHandler<MessageEventArgs> Debug;
private void fireDebug(string debugMessage, params object[] args)
{
EventHandler<MessageEventArgs> debug = Debug;
if (debug != null)
debug(new object(), new MessageEventArgs(string.Format(debugMessage, args)));
}
private void fireProgress(int progressPercentage)
{
EventHandler<ProgressChangedEventArgs> progress = Progress;
if (progress != null)
progress(new object(), new ProgressChangedEventArgs(progressPercentage, string.Empty));
}
#endregion
}
}

400
NUS Downloader/Lz77.cs Normal file
View File

@ -0,0 +1,400 @@
/* This file is part of libWiiSharp
* Copyright (C) 2009 Leathl
*
* libWiiSharp is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* libWiiSharp is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
//Lz77 (de-)compression algorithm based on gbalzss by Andre Perrot (Thanks!)
using System;
using System.IO;
namespace libWiiSharp
{
public class Lz77
{
private static uint lz77Magic = 0x4c5a3737;
private const int N = 4096;
private const int F = 18;
private const int threshold = 2;
private int[] leftSon = new int[N + 1];
private int[] rightSon = new int[N + 257];
private int[] dad = new int[N + 1];
private ushort[] textBuffer = new ushort[N + 17];
private int matchPosition = 0, matchLength = 0;
/// <summary>
/// Lz77 Magic.
/// </summary>
public static uint Lz77Magic { get { return lz77Magic; } }
#region Public Functions
/// <summary>
/// Checks whether a file is Lz77 compressed or not.
/// </summary>
/// <param name="file"></param>
/// <returns></returns>
public static bool IsLz77Compressed(string file)
{
return IsLz77Compressed(File.ReadAllBytes(file));
}
/// <summary>
/// Checks whether a file is Lz77 compressed or not.
/// </summary>
/// <param name="file"></param>
/// <returns></returns>
public static bool IsLz77Compressed(byte[] file)
{
Headers.HeaderType h = Headers.DetectHeader(file);
return (Shared.Swap(BitConverter.ToUInt32(file, (int)h)) == lz77Magic) ;
}
/// <summary>
/// Checks whether a file is Lz77 compressed or not.
/// </summary>
/// <param name="file"></param>
/// <returns></returns>
public static bool IsLz77Compressed(Stream file)
{
Headers.HeaderType h = Headers.DetectHeader(file);
byte[] temp = new byte[4];
file.Seek((long)h, SeekOrigin.Begin);
file.Read(temp, 0, temp.Length);
return (Shared.Swap(BitConverter.ToUInt32(temp, 0)) == lz77Magic);
}
/// <summary>
/// Compresses a file using the Lz77 algorithm.
/// </summary>
/// <param name="inFile"></param>
/// <param name="outFile"></param>
public void Compress(string inFile, string outFile)
{
Stream compressedFile;
using (FileStream fsIn = new FileStream(inFile, FileMode.Open))
compressedFile = compress(fsIn);
byte[] output = new byte[compressedFile.Length];
compressedFile.Read(output, 0, output.Length);
if (File.Exists(outFile)) File.Delete(outFile);
using (FileStream fs = new FileStream(outFile, FileMode.Create))
fs.Write(output, 0, output.Length);
}
/// <summary>
/// Compresses the byte array using the Lz77 algorithm.
/// </summary>
/// <param name="file"></param>
/// <returns></returns>
public byte[] Compress(byte[] file)
{
return ((MemoryStream)compress(new MemoryStream(file))).ToArray();
}
/// <summary>
/// Compresses the stream using the Lz77 algorithm.
/// </summary>
/// <param name="file"></param>
/// <returns></returns>
public Stream Compress(Stream file)
{
return compress(file);
}
/// <summary>
/// Decompresses a file using the Lz77 algorithm.
/// </summary>
/// <param name="inFile"></param>
/// <param name="outFile"></param>
public void Decompress(string inFile, string outFile)
{
Stream compressedFile;
using (FileStream fsIn = new FileStream(inFile, FileMode.Open))
compressedFile = decompress(fsIn);
byte[] output = new byte[compressedFile.Length];
compressedFile.Read(output, 0, output.Length);
if (File.Exists(outFile)) File.Delete(outFile);
using (FileStream fs = new FileStream(outFile, FileMode.Create))
fs.Write(output, 0, output.Length);
}
/// <summary>
/// Decompresses the byte array using the Lz77 algorithm.
/// </summary>
/// <param name="file"></param>
/// <returns></returns>
public byte[] Decompress(byte[] file)
{
return ((MemoryStream)decompress(new MemoryStream(file))).ToArray();
}
public Stream Decompress(Stream file)
{
return decompress(file);
}
#endregion
#region Private Functions
private Stream decompress(Stream inFile)
{
if (!Lz77.IsLz77Compressed(inFile)) return inFile;
inFile.Seek(0, SeekOrigin.Begin);
int i, j, k, r, c, z;
uint flags, decompressedSize, currentSize = 0;
Headers.HeaderType h = Headers.DetectHeader(inFile);
byte[] temp = new byte[8];
inFile.Seek((int)h, SeekOrigin.Begin);
inFile.Read(temp, 0, 8);
if (Shared.Swap(BitConverter.ToUInt32(temp, 0)) != lz77Magic)
{ inFile.Dispose(); throw new Exception("Invaild Magic!"); }
if (temp[4] != 0x10)
{ inFile.Dispose(); throw new Exception("Unsupported Compression Type!"); }
decompressedSize = (BitConverter.ToUInt32(temp, 4)) >> 8;
for (i = 0; i < N - F; i++) textBuffer[i] = 0xdf;
r = N - F; flags = 7; z = 7;
MemoryStream outFile = new MemoryStream();
while (true)
{
flags <<= 1;
z++;
if (z == 8)
{
if ((c = inFile.ReadByte()) == -1) break;
flags = (uint)c;
z = 0;
}
if ((flags & 0x80) == 0)
{
if ((c = inFile.ReadByte()) == inFile.Length - 1) break;
if (currentSize < decompressedSize) outFile.WriteByte((byte)c);
textBuffer[r++] = (byte)c;
r &= (N - 1);
currentSize++;
}
else
{
if ((i = inFile.ReadByte()) == -1) break;
if ((j = inFile.ReadByte()) == -1) break;
j = j | ((i << 8) & 0xf00);
i = ((i >> 4) & 0x0f) + threshold;
for (k = 0; k <= i; k++)
{
c = textBuffer[(r - j - 1) & (N - 1)];
if (currentSize < decompressedSize) outFile.WriteByte((byte)c); textBuffer[r++] = (byte)c; r &= (N - 1); currentSize++;
}
}
}
return outFile;
}
private Stream compress(Stream inFile)
{
if (Lz77.IsLz77Compressed(inFile)) return inFile;
inFile.Seek(0, SeekOrigin.Begin);
int textSize = 0;
int codeSize = 0;
int i, c, r, s, length, lastMatchLength, codeBufferPointer, mask;
int[] codeBuffer = new int[17];
uint fileSize = ((Convert.ToUInt32(inFile.Length)) << 8) + 0x10;
MemoryStream outFile = new MemoryStream();
outFile.Write(BitConverter.GetBytes(Shared.Swap(lz77Magic)), 0, 4);
outFile.Write(BitConverter.GetBytes(fileSize), 0, 4);
InitTree();
codeBuffer[0] = 0;
codeBufferPointer = 1;
mask = 0x80;
s = 0;
r = N - F;
for (i = s; i < r; i++) textBuffer[i] = 0xffff;
for (length = 0; length < F && (c = (int)inFile.ReadByte()) != -1; length++)
textBuffer[r + length] = (ushort)c;
if ((textSize = length) == 0) return inFile;
for (i = 1; i <= F; i++) InsertNode(r - i);
InsertNode(r);
do
{
if (matchLength > length) matchLength = length;
if (matchLength <= threshold)
{
matchLength = 1;
codeBuffer[codeBufferPointer++] = textBuffer[r];
}
else
{
codeBuffer[0] |= mask;
codeBuffer[codeBufferPointer++] = (char)
(((r - matchPosition - 1) >> 8) & 0x0f) |
((matchLength - (threshold + 1)) << 4);
codeBuffer[codeBufferPointer++] = (char)((r - matchPosition - 1) & 0xff);
}
if ((mask >>= 1) == 0)
{
for (i = 0; i < codeBufferPointer; i++)
outFile.WriteByte((byte)codeBuffer[i]);
codeSize += codeBufferPointer;
codeBuffer[0] = 0; codeBufferPointer = 1;
mask = 0x80;
}
lastMatchLength = matchLength;
for (i = 0; i < lastMatchLength && (c = (int)inFile.ReadByte()) != -1; i++)
{
DeleteNode(s);
textBuffer[s] = (ushort)c;
if (s < F - 1) textBuffer[s + N] = (ushort)c;
s = (s + 1) & (N - 1); r = (r + 1) & (N - 1);
InsertNode(r);
}
while (i++ < lastMatchLength)
{
DeleteNode(s);
s = (s + 1) & (N - 1); r = (r + 1) & (N - 1);
if (--length != 0) InsertNode(r);
}
} while (length > 0);
if (codeBufferPointer > 1)
{
for (i = 0; i < codeBufferPointer; i++) outFile.WriteByte((byte)codeBuffer[i]);
codeSize += codeBufferPointer;
}
if (codeSize % 4 != 0)
for (i = 0; i < 4 - (codeSize % 4); i++)
outFile.WriteByte(0x00);
return outFile;
}
private void InitTree()
{
int i;
for (i = N + 1; i <= N + 256; i++) rightSon[i] = N;
for (i = 0; i < N; i++) dad[i] = N;
}
private void InsertNode(int r)
{
int i, p, cmp;
cmp = 1;
p = N + 1 + (textBuffer[r] == 0xffff ? 0 : textBuffer[r]);
rightSon[r] = leftSon[r] = N; matchLength = 0;
for (; ; )
{
if (cmp >= 0)
{
if (rightSon[p] != N) p = rightSon[p];
else { rightSon[p] = r; dad[r] = p; return; }
}
else
{
if (leftSon[p] != N) p = leftSon[p];
else { leftSon[p] = r; dad[r] = p; return; }
}
for (i = 1; i < F; i++)
if ((cmp = textBuffer[r + i] - textBuffer[p + i]) != 0) break;
if (i > matchLength)
{
matchPosition = p;
if ((matchLength = i) >= F) break;
}
}
dad[r] = dad[p]; leftSon[r] = leftSon[p]; rightSon[r] = rightSon[p];
dad[leftSon[p]] = r; dad[rightSon[p]] = r;
if (rightSon[dad[p]] == p) rightSon[dad[p]] = r;
else leftSon[dad[p]] = r;
dad[p] = N;
}
private void DeleteNode(int p)
{
int q;
if (dad[p] == N) return;
if (rightSon[p] == N) q = leftSon[p];
else if (leftSon[p] == N) q = rightSon[p];
else
{
q = leftSon[p];
if (rightSon[q] != N)
{
do { q = rightSon[q]; } while (rightSon[q] != N);
rightSon[dad[q]] = leftSon[q]; dad[leftSon[q]] = dad[q];
leftSon[q] = leftSon[p]; dad[leftSon[p]] = q;
}
rightSon[q] = rightSon[p]; dad[rightSon[p]] = q;
}
dad[q] = dad[p];
if (rightSon[dad[p]] == p) rightSon[dad[p]] = q;
else leftSon[dad[p]] = q;
dad[p] = N;
}
#endregion
}
}

View File

@ -68,7 +68,10 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="CertificateChain.cs" />
<Compile Include="CommonKey.cs" />
<Compile Include="COMTypes.cs" />
<Compile Include="ContentIndices.cs" />
<Compile Include="Form1.cs">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<SubType>Form</SubType>
@ -77,6 +80,9 @@
<DependentUpon>Form1.cs</DependentUpon>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Compile>
<Compile Include="Headers.cs" />
<Compile Include="IosPatcher.cs" />
<Compile Include="Lz77.cs" />
<Compile Include="NusClient.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
@ -108,6 +114,11 @@
<DependentUpon>Settings.settings</DependentUpon>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
</Compile>
<Compile Include="Shared.cs" />
<Compile Include="Ticket.cs" />
<Compile Include="TMD.cs" />
<Compile Include="U8.cs" />
<Compile Include="WAD.cs" />
<Compile Include="WADPacker.cs" />
<Compile Include="WaterMarkTextBox.cs">
<SubType>Component</SubType>

272
NUS Downloader/Shared.cs Normal file
View File

@ -0,0 +1,272 @@
/* This file is part of libWiiSharp
* Copyright (C) 2009 Leathl
*
* libWiiSharp is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* libWiiSharp is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using System.Net;
namespace libWiiSharp
{
public static class Shared
{
/// <summary>
/// Merges two string arrays into one without double entries.
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <returns></returns>
public static string[] MergeStringArrays(string[] a, string[] b)
{
List<string> sList = new List<string>(a);
foreach (string currentString in b)
if (!sList.Contains(currentString)) sList.Add(currentString);
sList.Sort();
return sList.ToArray();
}
/// <summary>
/// Compares two byte arrays.
/// </summary>
/// <param name="first"></param>
/// <param name="firstIndex"></param>
/// <param name="second"></param>
/// <param name="secondIndex"></param>
/// <param name="length"></param>
/// <returns></returns>
public static bool CompareByteArrays(byte[] first, int firstIndex, byte[] second, int secondIndex, int length)
{
if (first.Length < length || second.Length < length) return false;
for (int i = 0; i < length; i++)
if (first[firstIndex + i] != second[secondIndex + i]) return false;
return true;
}
/// <summary>
/// Compares two byte arrays.
/// </summary>
/// <param name="first"></param>
/// <param name="second"></param>
/// <returns></returns>
public static bool CompareByteArrays(byte[] first, byte[] second)
{
if (first.Length != second.Length) return false;
else
for (int i = 0; i < first.Length; i++)
if (first[i] != second[i]) return false;
return true;
}
/// <summary>
/// Turns a byte array into a string, default separator is a space.
/// </summary>
/// <param name="byteArray"></param>
/// <param name="separator"></param>
/// <returns></returns>
public static string ByteArrayToString(byte[] byteArray, char separator = ' ')
{
string res = string.Empty;
foreach (byte b in byteArray)
res += b.ToString("x2").ToUpper() + separator;
return res.Remove(res.Length - 1);
}
/// <summary>
/// Turns a hex string into a byte array.
/// </summary>
/// <param name="hexString"></param>
/// <returns></returns>
public static byte[] HexStringToByteArray(string hexString)
{
byte[] ba = new byte[hexString.Length / 2];
for (int i = 0; i < hexString.Length / 2; i++)
ba[i] = byte.Parse(hexString.Substring(i * 2, 2), System.Globalization.NumberStyles.HexNumber);
return ba;
}
/// <summary>
/// Counts how often the given char exists in the given string.
/// </summary>
/// <param name="theString"></param>
/// <param name="theChar"></param>
/// <returns></returns>
public static int CountCharsInString(string theString, char theChar)
{
int count = 0;
foreach (char thisChar in theString)
if (thisChar == theChar)
count++;
return count;
}
/// <summary>
/// Pads the given value to a multiple of the given padding value, default padding value is 64.
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static long AddPadding(long value)
{
return AddPadding(value, 64);
}
/// <summary>
/// Pads the given value to a multiple of the given padding value, default padding value is 64.
/// </summary>
/// <param name="value"></param>
/// <param name="padding"></param>
/// <returns></returns>
public static long AddPadding(long value, int padding)
{
if (value % padding != 0)
{
value = value + (padding - (value % padding));
}
return value;
}
/// <summary>
/// Pads the given value to a multiple of the given padding value, default padding value is 64.
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static int AddPadding(int value)
{
return AddPadding(value, 64);
}
/// <summary>
/// Pads the given value to a multiple of the given padding value, default padding value is 64.
/// </summary>
/// <param name="value"></param>
/// <param name="padding"></param>
/// <returns></returns>
public static int AddPadding(int value, int padding)
{
if (value % padding != 0)
{
value = value + (padding - (value % padding));
}
return value;
}
/// <summary>
/// Swaps endianness.
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static ushort Swap(ushort value)
{
return (ushort)IPAddress.HostToNetworkOrder((short)value);
}
/// <summary>
/// Swaps endianness.
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static uint Swap(uint value)
{
return (uint)IPAddress.HostToNetworkOrder((int)value);
}
/// <summary>
/// Swaps endianness
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static ulong Swap(ulong value)
{
return (ulong)IPAddress.HostToNetworkOrder((long)value);
}
/// <summary>
/// Turns a ushort array into a byte array.
/// </summary>
/// <param name="array"></param>
/// <returns></returns>
public static byte[] UShortArrayToByteArray(ushort[] array)
{
List<byte> results = new List<byte>();
foreach (ushort value in array)
{
byte[] converted = BitConverter.GetBytes(value);
results.AddRange(converted);
}
return results.ToArray();
}
/// <summary>
/// Turns a uint array into a byte array.
/// </summary>
/// <param name="array"></param>
/// <returns></returns>
public static byte[] UIntArrayToByteArray(uint[] array)
{
List<byte> results = new List<byte>();
foreach (uint value in array)
{
byte[] converted = BitConverter.GetBytes(value);
results.AddRange(converted);
}
return results.ToArray();
}
/// <summary>
/// Turns a byte array into a uint array.
/// </summary>
/// <param name="array"></param>
/// <returns></returns>
public static uint[] ByteArrayToUIntArray(byte[] array)
{
UInt32[] converted = new UInt32[array.Length / 4];
int j = 0;
for (int i = 0; i < array.Length; i += 4)
converted[j++] = BitConverter.ToUInt32(array, i);
return converted;
}
/// <summary>
/// Turns a byte array into a ushort array.
/// </summary>
/// <param name="array"></param>
/// <returns></returns>
public static ushort[] ByteArrayToUShortArray(byte[] array)
{
ushort[] converted = new ushort[array.Length / 2];
int j = 0;
for (int i = 0; i < array.Length; i += 2)
converted[j++] = BitConverter.ToUInt16(array, i);
return converted;
}
}
}

659
NUS Downloader/TMD.cs Normal file
View File

@ -0,0 +1,659 @@
/* This file is part of libWiiSharp
* Copyright (C) 2009 Leathl
*
* libWiiSharp is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* libWiiSharp is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography;
namespace libWiiSharp
{
public enum ContentType : ushort
{
Normal = 0x0001,
DLC = 0x4001, //Seen this in a DLC wad...
Shared = 0x8001,
}
public enum Region : ushort
{
Japan = 0,
USA = 1,
Europe = 2,
Free = 3,
}
public class TMD : IDisposable
{
private bool fakeSign = false;
private uint signatureExponent = 0x00010001;
private byte[] signature = new byte[256];
private byte[] padding = new byte[60];
private byte[] issuer = new byte[64];
private byte version;
private byte caCrlVersion;
private byte signerCrlVersion;
private byte paddingByte;
private ulong startupIos;
private ulong titleId;
private uint titleType;
private ushort groupId;
private ushort padding2;
private ushort region;
private byte[] reserved = new byte[58];
private uint accessRights;
private ushort titleVersion;
private ushort numOfContents;
private ushort bootIndex;
private ushort padding3;
private List<TMD_Content> contents;
/// <summary>
/// The region of the title.
/// </summary>
public Region Region { get { return (Region)region; } set { region = (ushort)value; } }
/// <summary>
/// The IOS the title is launched with.
/// </summary>
public ulong StartupIOS { get { return startupIos; } set { startupIos = value; } }
/// <summary>
/// The Title ID.
/// </summary>
public ulong TitleID { get { return titleId; } set { titleId = value; } }
/// <summary>
/// The Title Version.
/// </summary>
public ushort TitleVersion { get { return titleVersion; } set { titleVersion = value; } }
/// <summary>
/// The Number of Contents.
/// </summary>
public ushort NumOfContents { get { return numOfContents; } }
/// <summary>
/// The boot index. Represents the index of the nand loader.
/// </summary>
public ushort BootIndex { get { return bootIndex; } set { if (value <= numOfContents) bootIndex = value; } }
/// <summary>
/// The content descriptions in the TMD.
/// </summary>
public TMD_Content[] Contents { get { return contents.ToArray(); } set { contents = new List<TMD_Content>(value); numOfContents = (ushort)value.Length; } }
/// <summary>
/// If true, the TMD will be fakesigned while saving.
/// </summary>
public bool FakeSign { get { return fakeSign; } set { fakeSign = value; } }
#region IDisposable Members
private bool isDisposed = false;
~TMD()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing && !isDisposed)
{
signature = null;
padding = null;
issuer = null;
reserved = null;
contents.Clear();
contents = null;
}
isDisposed = true;
}
#endregion
#region Public Functions
/// <summary>
/// Loads a tmd file.
/// </summary>
/// <param name="pathToTmd"></param>
/// <returns></returns>
public static TMD Load(string pathToTmd)
{
return Load(File.ReadAllBytes(pathToTmd));
}
/// <summary>
/// Loads a tmd file.
/// </summary>
/// <param name="tmdFile"></param>
/// <returns></returns>
public static TMD Load(byte[] tmdFile)
{
TMD t = new TMD();
MemoryStream ms = new MemoryStream(tmdFile);
try { t.parseTmd(ms); }
catch { ms.Dispose(); throw; }
ms.Dispose();
return t;
}
/// <summary>
/// Loads a tmd file.
/// </summary>
/// <param name="tmd"></param>
/// <returns></returns>
public static TMD Load(Stream tmd)
{
TMD t = new TMD();
t.parseTmd(tmd);
return t;
}
/// <summary>
/// Loads a tmd file.
/// </summary>
/// <param name="pathToTmd"></param>
public void LoadFile(string pathToTmd)
{
LoadFile(File.ReadAllBytes(pathToTmd));
}
/// <summary>
/// Loads a tmd file.
/// </summary>
/// <param name="tmdFile"></param>
public void LoadFile(byte[] tmdFile)
{
MemoryStream ms = new MemoryStream(tmdFile);
try { parseTmd(ms); }
catch { ms.Dispose(); throw; }
ms.Dispose();
}
/// <summary>
/// Loads a tmd file.
/// </summary>
/// <param name="tmd"></param>
public void LoadFile(Stream tmd)
{
parseTmd(tmd);
}
/// <summary>
/// Saves the TMD.
/// </summary>
/// <param name="savePath"></param>
public void Save(string savePath)
{
Save(savePath, false);
}
/// <summary>
/// Saves the TMD. If fakeSign is true, the Ticket will be fakesigned.
/// </summary>
/// <param name="savePath"></param>
/// <param name="fakeSign"></param>
public void Save(string savePath, bool fakeSign)
{
if (fakeSign) this.fakeSign = true;
if (File.Exists(savePath)) File.Delete(savePath);
using (FileStream fs = new FileStream(savePath, FileMode.Create))
writeToStream(fs);
}
/// <summary>
/// Returns the TMD as a memory stream.
/// </summary>
/// <returns></returns>
public MemoryStream ToMemoryStream()
{
return ToMemoryStream(false);
}
/// <summary>
/// Returns the TMD as a memory stream. If fakeSign is true, the Ticket will be fakesigned.
/// </summary>
/// <param name="fakeSign"></param>
/// <returns></returns>
public MemoryStream ToMemoryStream(bool fakeSign)
{
if (fakeSign) this.fakeSign = true;
MemoryStream ms = new MemoryStream();
try { writeToStream(ms); }
catch { ms.Dispose(); throw; }
return ms;
}
/// <summary>
/// Returns the TMD as a byte array.
/// </summary>
/// <returns></returns>
public byte[] ToByteArray()
{
return ToByteArray(false);
}
/// <summary>
/// Returns the TMD as a byte array. If fakeSign is true, the Ticket will be fakesigned.
/// </summary>
/// <param name="fakeSign"></param>
/// <returns></returns>
public byte[] ToByteArray(bool fakeSign)
{
if (fakeSign) this.fakeSign = true;
MemoryStream ms = new MemoryStream();
try { writeToStream(ms); }
catch { ms.Dispose(); throw; }
byte[] res = ms.ToArray();
ms.Dispose();
return res;
}
/// <summary>
/// Updates the content entries.
/// </summary>
/// <param name="contentDir"></param>
/// <param name="namedContentId">True if you use the content ID as name (e.g. 0000008a.app).
/// False if you use the index as name (e.g. 00000000.app)</param>
public void UpdateContents(string contentDir)
{
bool namedContentId = true;
for (int i = 0; i < contents.Count; i++)
if (!File.Exists(contentDir + Path.DirectorySeparatorChar + contents[i].ContentID.ToString("x8") + ".app"))
{ namedContentId = false; break; }
if (!namedContentId)
for (int i = 0; i < contents.Count; i++)
if (!File.Exists(contentDir + Path.DirectorySeparatorChar + contents[i].ContentID.ToString("x8") + ".app"))
throw new Exception("Couldn't find all content files!");
byte[][] conts = new byte[contents.Count][];
for (int i = 0; i < contents.Count; i++)
{
string file = contentDir + Path.DirectorySeparatorChar + ((namedContentId) ? contents[i].ContentID.ToString("x8") : contents[i].Index.ToString("x8")) + ".app";
conts[i] = File.ReadAllBytes(file);
}
updateContents(conts);
}
/// <summary>
/// Updates the content entries.
/// </summary>
/// <param name="contentDir"></param>
/// <param name="namedContentId">True if you use the content ID as name (e.g. 0000008a.app).
/// False if you use the index as name (e.g. 00000000.app)</param>
public void UpdateContents(byte[][] contents)
{
updateContents(contents);
}
/// <summary>
/// Returns the Upper Title ID as a string.
/// </summary>
/// <returns></returns>
public string GetUpperTitleID()
{
byte[] titleBytes = BitConverter.GetBytes(Shared.Swap((uint)titleId));
return new string(new char[] { (char)titleBytes[0], (char)titleBytes[1], (char)titleBytes[2], (char)titleBytes[3] });
}
/// <summary>
/// The Number of memory blocks the content will take.
/// </summary>
/// <returns></returns>
public string GetNandBlocks()
{
return calculateNandBlocks();
}
/// <summary>
/// Adds a TMD content.
/// </summary>
/// <param name="content"></param>
public void AddContent(TMD_Content content)
{
contents.Add(content);
numOfContents = (ushort)contents.Count;
}
/// <summary>
/// Removes the content with the given index.
/// </summary>
/// <param name="contentIndex"></param>
public void RemoveContent(int contentIndex)
{
for (int i = 0; i < numOfContents; i++)
if (contents[i].Index == contentIndex)
{ contents.RemoveAt(i); break; }
numOfContents = (ushort)contents.Count;
}
/// <summary>
/// Removes the content with the given ID.
/// </summary>
/// <param name="contentId"></param>
public void RemoveContentByID(int contentId)
{
for (int i = 0; i < numOfContents; i++)
if (contents[i].ContentID == contentId)
{ contents.RemoveAt(i); break; }
numOfContents = (ushort)contents.Count;
}
#endregion
#region Private Functions
private void writeToStream(Stream writeStream)
{
fireDebug("Writing TMD...");
if (fakeSign)
{ fireDebug(" Clearing Signature..."); signature = new byte[256]; } //Clear Signature if we fake Sign
MemoryStream ms = new MemoryStream();
ms.Seek(0, SeekOrigin.Begin);
fireDebug(" Writing Signature Exponent... (Offset: 0x{0})", ms.Position.ToString("x8").ToUpper());
ms.Write(BitConverter.GetBytes(Shared.Swap(signatureExponent)), 0, 4);
fireDebug(" Writing Signature... (Offset: 0x{0})", ms.Position.ToString("x8").ToUpper());
ms.Write(signature, 0, signature.Length);
fireDebug(" Writing Padding... (Offset: 0x{0})", ms.Position.ToString("x8").ToUpper());
ms.Write(padding, 0, padding.Length);
fireDebug(" Writing Issuer... (Offset: 0x{0})", ms.Position.ToString("x8").ToUpper());
ms.Write(issuer, 0, issuer.Length);
fireDebug(" Writing Version... (Offset: 0x{0})", ms.Position.ToString("x8").ToUpper());
ms.WriteByte(version);
fireDebug(" Writing CA Crl Version... (Offset: 0x{0})", ms.Position.ToString("x8").ToUpper());
ms.WriteByte(caCrlVersion);
fireDebug(" Writing Signer Crl Version... (Offset: 0x{0})", ms.Position.ToString("x8").ToUpper());
ms.WriteByte(signerCrlVersion);
fireDebug(" Writing Padding Byte... (Offset: 0x{0})", ms.Position.ToString("x8").ToUpper());
ms.WriteByte(paddingByte);
fireDebug(" Writing Startup IOS... (Offset: 0x{0})", ms.Position.ToString("x8").ToUpper());
ms.Write(BitConverter.GetBytes(Shared.Swap(startupIos)), 0, 8);
fireDebug(" Writing Title ID... (Offset: 0x{0})", ms.Position.ToString("x8").ToUpper());
ms.Write(BitConverter.GetBytes(Shared.Swap(titleId)), 0, 8);
fireDebug(" Writing Title Type... (Offset: 0x{0})", ms.Position.ToString("x8").ToUpper());
ms.Write(BitConverter.GetBytes(Shared.Swap(titleType)), 0, 4);
fireDebug(" Writing Group ID... (Offset: 0x{0})", ms.Position.ToString("x8").ToUpper());
ms.Write(BitConverter.GetBytes(Shared.Swap(groupId)), 0, 2);
fireDebug(" Writing Padding2... (Offset: 0x{0})", ms.Position.ToString("x8").ToUpper());
ms.Write(BitConverter.GetBytes(Shared.Swap(padding2)), 0, 2);
fireDebug(" Writing Region... (Offset: 0x{0})", ms.Position.ToString("x8").ToUpper());
ms.Write(BitConverter.GetBytes(Shared.Swap(region)), 0, 2);
fireDebug(" Writing Reserved... (Offset: 0x{0})", ms.Position.ToString("x8").ToUpper());
ms.Write(reserved, 0, reserved.Length);
fireDebug(" Writing Access Rights... (Offset: 0x{0})", ms.Position.ToString("x8").ToUpper());
ms.Write(BitConverter.GetBytes(Shared.Swap(accessRights)), 0, 4);
fireDebug(" Writing Title Version... (Offset: 0x{0})", ms.Position.ToString("x8").ToUpper());
ms.Write(BitConverter.GetBytes(Shared.Swap(titleVersion)), 0, 2);
fireDebug(" Writing NumOfContents... (Offset: 0x{0})", ms.Position.ToString("x8").ToUpper());
ms.Write(BitConverter.GetBytes(Shared.Swap(numOfContents)), 0, 2);
fireDebug(" Writing Boot Index... (Offset: 0x{0})", ms.Position.ToString("x8").ToUpper());
ms.Write(BitConverter.GetBytes(Shared.Swap(bootIndex)), 0, 2);
fireDebug(" Writing Padding3... (Offset: 0x{0})", ms.Position.ToString("x8").ToUpper());
ms.Write(BitConverter.GetBytes(Shared.Swap(padding3)), 0, 2);
//Write Contents
List<ContentIndices> contentList = new List<ContentIndices>();
for (int i = 0; i < contents.Count; i++)
contentList.Add(new ContentIndices(i, contents[i].Index));
contentList.Sort();
for (int i = 0; i < contentList.Count; i++)
{
fireDebug(" Writing Content #{1} of {2}... (Offset: 0x{0})", ms.Position.ToString("x8").ToUpper().ToUpper(), i + 1, numOfContents);
ms.Write(BitConverter.GetBytes(Shared.Swap(contents[contentList[i].Index].ContentID)), 0, 4);
ms.Write(BitConverter.GetBytes(Shared.Swap(contents[contentList[i].Index].Index)), 0, 2);
ms.Write(BitConverter.GetBytes(Shared.Swap((ushort)contents[contentList[i].Index].Type)), 0, 2);
ms.Write(BitConverter.GetBytes(Shared.Swap(contents[contentList[i].Index].Size)), 0, 8);
ms.Write(contents[contentList[i].Index].Hash, 0, contents[contentList[i].Index].Hash.Length);
}
//fake Sign
byte[] tmd = ms.ToArray();
ms.Dispose();
if (fakeSign)
{
fireDebug(" Fakesigning TMD...");
byte[] hash = new byte[20];
SHA1 s = SHA1.Create();
for (ushort i = 0; i < 0xFFFF; i++)
{
byte[] bytes = BitConverter.GetBytes(i);
tmd[482] = bytes[1]; tmd[483] = bytes[0];
hash = s.ComputeHash(tmd);
if (hash[0] == 0x00)
{ fireDebug(" -> Signed ({0})", i); break; } //Win! It's signed...
if (i == 0xFFFF - 1)
{ fireDebug(" -> Signing Failed..."); throw new Exception("Fakesigning failed..."); }
}
s.Clear();
}
writeStream.Seek(0, SeekOrigin.Begin);
writeStream.Write(tmd, 0, tmd.Length);
fireDebug("Writing TMD Finished...");
}
private void updateContents(byte[][] conts)
{
SHA1 s = SHA1.Create();
for (int i = 0; i < contents.Count; i++)
{
contents[i].Size = (ulong)conts[i].Length;
contents[i].Hash = s.ComputeHash(conts[i]);
}
s.Clear();
}
private void parseTmd(Stream tmdFile)
{
fireDebug("Pasing TMD...");
tmdFile.Seek(0, SeekOrigin.Begin);
byte[] temp = new byte[8];
fireDebug(" Reading Signature Exponent... (Offset: 0x{0})", tmdFile.Position.ToString("x8").ToUpper());
tmdFile.Read(temp, 0, 4);
signatureExponent = Shared.Swap(BitConverter.ToUInt32(temp, 0));
fireDebug(" Reading Signature... (Offset: 0x{0})", tmdFile.Position.ToString("x8").ToUpper());
tmdFile.Read(signature, 0, signature.Length);
fireDebug(" Reading Padding... (Offset: 0x{0})", tmdFile.Position.ToString("x8").ToUpper());
tmdFile.Read(padding, 0, padding.Length);
fireDebug(" Reading Issuer... (Offset: 0x{0})", tmdFile.Position.ToString("x8").ToUpper());
tmdFile.Read(issuer, 0, issuer.Length);
fireDebug(" Reading Version... (Offset: 0x{0})", tmdFile.Position.ToString("x8").ToUpper());
fireDebug(" Reading CA Crl Version... (Offset: 0x{0})", tmdFile.Position.ToString("x8").ToUpper());
fireDebug(" Reading Signer Crl Version... (Offset: 0x{0})", tmdFile.Position.ToString("x8").ToUpper());
fireDebug(" Reading Padding Byte... (Offset: 0x{0})", tmdFile.Position.ToString("x8").ToUpper());
tmdFile.Read(temp, 0, 4);
version = temp[0];
caCrlVersion = temp[1];
signerCrlVersion = temp[2];
paddingByte = temp[3];
fireDebug(" Reading Startup IOS... (Offset: 0x{0})", tmdFile.Position.ToString("x8").ToUpper());
tmdFile.Read(temp, 0, 8);
startupIos = Shared.Swap(BitConverter.ToUInt64(temp, 0));
fireDebug(" Reading Title ID... (Offset: 0x{0})", tmdFile.Position.ToString("x8").ToUpper());
tmdFile.Read(temp, 0, 8);
titleId = Shared.Swap(BitConverter.ToUInt64(temp, 0));
fireDebug(" Reading Title Type... (Offset: 0x{0})", tmdFile.Position.ToString("x8").ToUpper());
tmdFile.Read(temp, 0, 4);
titleType = Shared.Swap(BitConverter.ToUInt32(temp, 0));
fireDebug(" Reading Group ID... (Offset: 0x{0})", tmdFile.Position.ToString("x8").ToUpper());
tmdFile.Read(temp, 0, 2);
groupId = Shared.Swap(BitConverter.ToUInt16(temp, 0));
fireDebug(" Reading Padding2... (Offset: 0x{0})", tmdFile.Position.ToString("x8").ToUpper());
tmdFile.Read(temp, 0, 2);
padding2 = Shared.Swap(BitConverter.ToUInt16(temp, 0));
fireDebug(" Reading Region... (Offset: 0x{0})", tmdFile.Position.ToString("x8").ToUpper());
tmdFile.Read(temp, 0, 2);
region = Shared.Swap(BitConverter.ToUInt16(temp, 0));
fireDebug(" Reading Reserved... (Offset: 0x{0})", tmdFile.Position.ToString("x8").ToUpper());
tmdFile.Read(reserved, 0, reserved.Length);
fireDebug(" Reading Access Rights... (Offset: 0x{0})", tmdFile.Position.ToString("x8").ToUpper());
tmdFile.Read(temp, 0, 4);
accessRights = Shared.Swap(BitConverter.ToUInt32(temp, 0));
fireDebug(" Reading Title Version... (Offset: 0x{0})", tmdFile.Position.ToString("x8").ToUpper());
fireDebug(" Reading NumOfContents... (Offset: 0x{0})", tmdFile.Position.ToString("x8").ToUpper());
fireDebug(" Reading Boot Index... (Offset: 0x{0})", tmdFile.Position.ToString("x8").ToUpper());
fireDebug(" Reading Padding3... (Offset: 0x{0})", tmdFile.Position.ToString("x8").ToUpper());
tmdFile.Read(temp, 0, 8);
titleVersion = Shared.Swap(BitConverter.ToUInt16(temp, 0));
numOfContents = Shared.Swap(BitConverter.ToUInt16(temp, 2));
bootIndex = Shared.Swap(BitConverter.ToUInt16(temp, 4));
padding3 = Shared.Swap(BitConverter.ToUInt16(temp, 6));
contents = new List<TMD_Content>();
//Read Contents
for (int i = 0; i < numOfContents; i++)
{
fireDebug(" Reading Content #{0} of {1}... (Offset: 0x{2})", i + 1, numOfContents, tmdFile.Position.ToString("x8").ToUpper().ToUpper());
TMD_Content tempContent = new TMD_Content();
tempContent.Hash = new byte[20];
tmdFile.Read(temp, 0, 8);
tempContent.ContentID = Shared.Swap(BitConverter.ToUInt32(temp, 0));
tempContent.Index = Shared.Swap(BitConverter.ToUInt16(temp, 4));
tempContent.Type = (ContentType)Shared.Swap(BitConverter.ToUInt16(temp, 6));
tmdFile.Read(temp, 0, 8);
tempContent.Size = Shared.Swap(BitConverter.ToUInt64(temp, 0));
tmdFile.Read(tempContent.Hash, 0, tempContent.Hash.Length);
contents.Add(tempContent);
}
fireDebug("Pasing TMD Finished...");
}
private string calculateNandBlocks()
{
int nandSizeMin = 0;
int nandSizeMax = 0;
for (int i = 0; i < numOfContents; i++)
{
nandSizeMax += (int)contents[i].Size;
if (contents[i].Type == ContentType.Normal) nandSizeMin += (int)contents[i].Size;
}
int blocksMin = (int)Math.Ceiling((double)((double)nandSizeMin / (128 * 1024)));
int blocksMax = (int)Math.Ceiling((double)((double)nandSizeMax / (128 * 1024)));
if (blocksMin == blocksMax) return blocksMax.ToString();
else return string.Format("{0} - {1}", blocksMin, blocksMax);
}
#endregion
#region Events
/// <summary>
/// Fires debugging messages. You may write them into a log file or log textbox.
/// </summary>
public event EventHandler<MessageEventArgs> Debug;
private void fireDebug(string debugMessage, params object[] args)
{
EventHandler<MessageEventArgs> debug = Debug;
if (debug != null)
debug(new object(), new MessageEventArgs(string.Format(debugMessage, args)));
}
#endregion
}
public class TMD_Content
{
private uint contentId;
private ushort index;
private ushort type;
private ulong size;
private byte[] hash = new byte[20];
public uint ContentID { get { return contentId; } set { contentId = value; } }
public ushort Index { get { return index; } set { index = value; } }
public ContentType Type { get { return (ContentType)type; } set { type = (ushort)value; } }
public ulong Size { get { return size; } set { size = value; } }
public byte[] Hash { get { return hash; } set { hash = value; } }
}
}

609
NUS Downloader/Ticket.cs Normal file
View File

@ -0,0 +1,609 @@
/* This file is part of libWiiSharp
* Copyright (C) 2009 Leathl
*
* libWiiSharp is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* libWiiSharp is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
using System;
using System.IO;
using System.Security.Cryptography;
namespace libWiiSharp
{
public enum CommonKeyType : byte
{
Standard = 0x00,
Korean = 0x01,
}
public class Ticket : IDisposable
{
private byte newKeyIndex = (byte)CommonKeyType.Standard;
private byte[] decryptedTitleKey = new byte[16];
private bool fakeSign = false;
private bool titleKeyChanged = false;
private byte[] newEncryptedTitleKey = new byte[0];
private bool reDecrypt = false;
private uint signatureExponent = 0x00010001;
private byte[] signature = new byte[256];
private byte[] padding = new byte[60];
private byte[] issuer = new byte[64];
private byte[] unknown = new byte[63];
private byte[] encryptedTitleKey = new byte[16];
private byte unknown2;
private ulong ticketId;
private uint consoleId;
private ulong titleId;
private ushort unknown3 = 0xFFFF;
private ushort numOfDlc;
private ulong unknown4;
private byte padding2;
private byte commonKeyIndex = (byte)CommonKeyType.Standard;
private byte[] unknown5 = new byte[48];
private byte[] unknown6 = new byte[32]; //0xFF
private ushort padding3;
private uint enableTimeLimit;
private uint timeLimit;
private byte[] padding4 = new byte[88];
/// <summary>
/// The Title Key the WADs content is encrypted with.
/// </summary>
public byte[] TitleKey { get { return decryptedTitleKey; } set { decryptedTitleKey = value; titleKeyChanged = true; reDecrypt = false; } }
/// <summary>
/// Defines which Common Key is used (Standard / Korean).
/// </summary>
public CommonKeyType CommonKeyIndex { get { return (CommonKeyType)newKeyIndex; } set { newKeyIndex = (byte)value; } }
/// <summary>
/// The Ticket ID.
/// </summary>
public ulong TicketID { get { return ticketId; } set { ticketId = value; } }
/// <summary>
/// The Console ID.
/// </summary>
public uint ConsoleID { get { return consoleId; } set { consoleId = value; } }
/// <summary>
/// The Title ID.
/// </summary>
public ulong TitleID { get { return titleId; } set { titleId = value; if (reDecrypt) reDecryptTitleKey(); } }
/// <summary>
/// Number of DLC.
/// </summary>
public ushort NumOfDLC { get { return numOfDlc; } set { numOfDlc = value; } }
/// <summary>
/// If true, the Ticket will be fakesigned while saving.
/// </summary>
public bool FakeSign { get { return fakeSign; } set { fakeSign = value; } }
/// <summary>
/// True if the Title Key was changed.
/// </summary>
public bool TitleKeyChanged { get { return titleKeyChanged; } }
#region IDisposable Members
private bool isDisposed = false;
~Ticket()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing && !isDisposed)
{
decryptedTitleKey = null;
newEncryptedTitleKey = null;
signature = null;
padding = null;
issuer = null;
unknown = null;
encryptedTitleKey = null;
unknown5 = null;
unknown6 = null;
padding4 = null;
}
isDisposed = true;
}
#endregion
#region Public Functions
/// <summary>
/// Loads a tik file.
/// </summary>
/// <param name="pathToTicket"></param>
/// <returns></returns>
public static Ticket Load(string pathToTicket)
{
return Load(File.ReadAllBytes(pathToTicket));
}
/// <summary>
/// Loads a tik file.
/// </summary>
/// <param name="ticket"></param>
/// <returns></returns>
public static Ticket Load(byte[] ticket)
{
Ticket tik = new Ticket();
MemoryStream ms = new MemoryStream(ticket);
try { tik.parseTicket(ms); }
catch { ms.Dispose(); throw; }
ms.Dispose();
return tik;
}
/// <summary>
/// Loads a tik file.
/// </summary>
/// <param name="ticket"></param>
/// <returns></returns>
public static Ticket Load(Stream ticket)
{
Ticket tik = new Ticket();
tik.parseTicket(ticket);
return tik;
}
/// <summary>
/// Loads a tik file.
/// </summary>
/// <param name="pathToTicket"></param>
public void LoadFile(string pathToTicket)
{
LoadFile(File.ReadAllBytes(pathToTicket));
}
/// <summary>
/// Loads a tik file.
/// </summary>
/// <param name="ticket"></param>
public void LoadFile(byte[] ticket)
{
MemoryStream ms = new MemoryStream(ticket);
try { parseTicket(ms); }
catch { ms.Dispose(); throw; }
ms.Dispose();
}
/// <summary>
/// Loads a tik file.
/// </summary>
/// <param name="ticket"></param>
public void LoadFile(Stream ticket)
{
parseTicket(ticket);
}
/// <summary>
/// Saves the Ticket.
/// </summary>
/// <param name="savePath"></param>
public void Save(string savePath)
{
Save(savePath, false);
}
/// <summary>
/// Saves the Ticket. If fakeSign is true, the Ticket will be fakesigned.
/// </summary>
/// <param name="savePath"></param>
/// <param name="fakeSign"></param>
public void Save(string savePath, bool fakeSign)
{
if (fakeSign) this.fakeSign = true;
if (File.Exists(savePath)) File.Delete(savePath);
using (FileStream fs = new FileStream(savePath, FileMode.Create))
writeToStream(fs);
}
/// <summary>
/// Returns the Ticket as a memory stream.
/// </summary>
/// <returns></returns>
public MemoryStream ToMemoryStream()
{
return ToMemoryStream(false);
}
/// <summary>
/// Returns the Ticket as a memory stream. If fakeSign is true, the Ticket will be fakesigned.
/// </summary>
/// <param name="fakeSign"></param>
/// <returns></returns>
public MemoryStream ToMemoryStream(bool fakeSign)
{
if (fakeSign) this.fakeSign = true;
MemoryStream ms = new MemoryStream();
try { writeToStream(ms); }
catch { ms.Dispose(); throw; }
return ms;
}
/// <summary>
/// Returns the Ticket as a byte array.
/// </summary>
/// <returns></returns>
public byte[] ToByteArray()
{
return ToByteArray(false);
}
/// <summary>
/// Returns the Ticket as a byte array. If fakeSign is true, the Ticket will be fakesigned.
/// </summary>
/// <param name="fakeSign"></param>
/// <returns></returns>
public byte[] ToByteArray(bool fakeSign)
{
if (fakeSign) this.fakeSign = true;
MemoryStream ms = new MemoryStream();
try { writeToStream(ms); }
catch { ms.Dispose(); throw; }
byte[] res = ms.ToArray();
ms.Dispose();
return res;
}
/// <summary>
/// This will set a new encrypted Title Key (i.e. the one that you can "read" in the Ticket).
/// </summary>
/// <param name="newTitleKey"></param>
public void SetTitleKey(string newTitleKey)
{
SetTitleKey(newTitleKey.ToCharArray());
}
/// <summary>
/// This will set a new encrypted Title Key (i.e. the one that you can "read" in the Ticket).
/// </summary>
/// <param name="newTitleKey"></param>
public void SetTitleKey(char[] newTitleKey)
{
if (newTitleKey.Length != 16)
throw new Exception("The title key must be 16 characters long!");
for (int i = 0; i < 16; i++)
encryptedTitleKey[i] = (byte)newTitleKey[i];
decryptTitleKey();
titleKeyChanged = true;
reDecrypt = true;
newEncryptedTitleKey = encryptedTitleKey;
}
/// <summary>
/// This will set a new encrypted Title Key (i.e. the one that you can "read" in the Ticket).
/// </summary>
/// <param name="newTitleKey"></param>
public void SetTitleKey(byte[] newTitleKey)
{
if (newTitleKey.Length != 16)
throw new Exception("The title key must be 16 characters long!");
encryptedTitleKey = newTitleKey;
decryptTitleKey();
titleKeyChanged = true;
reDecrypt = true;
newEncryptedTitleKey = newTitleKey;
}
/// <summary>
/// Returns the Upper Title ID as a string.
/// </summary>
/// <returns></returns>
public string GetUpperTitleID()
{
byte[] titleBytes = BitConverter.GetBytes(Shared.Swap((uint)titleId));
return new string(new char[] { (char)titleBytes[0], (char)titleBytes[1], (char)titleBytes[2], (char)titleBytes[3] });
}
#endregion
#region Private Functions
private void writeToStream(Stream writeStream)
{
fireDebug("Writing Ticket...");
fireDebug(" Encrypting Title Key...");
encryptTitleKey();
fireDebug(" -> Decrypted Title Key: {0}", Shared.ByteArrayToString(decryptedTitleKey));
fireDebug(" -> Encrypted Title Key: {0}", Shared.ByteArrayToString(encryptedTitleKey));
if (fakeSign)
{ fireDebug(" Clearing Signature..."); signature = new byte[256]; } //Clear Signature if we fake Sign
MemoryStream ms = new MemoryStream();
ms.Seek(0, SeekOrigin.Begin);
fireDebug(" Writing Signature Exponent... (Offset: 0x{0})", ms.Position.ToString("x8").ToUpper());
ms.Write(BitConverter.GetBytes(Shared.Swap(signatureExponent)), 0, 4);
fireDebug(" Writing Signature... (Offset: 0x{0})", ms.Position.ToString("x8").ToUpper());
ms.Write(signature, 0, signature.Length);
fireDebug(" Writing Padding... (Offset: 0x{0})", ms.Position.ToString("x8").ToUpper());
ms.Write(padding, 0, padding.Length);
fireDebug(" Writing Issuer... (Offset: 0x{0})", ms.Position.ToString("x8").ToUpper());
ms.Write(issuer, 0, issuer.Length);
fireDebug(" Writing Unknown... (Offset: 0x{0})", ms.Position.ToString("x8").ToUpper());
ms.Write(unknown, 0, unknown.Length);
fireDebug(" Writing Title Key... (Offset: 0x{0})", ms.Position.ToString("x8").ToUpper());
ms.Write(encryptedTitleKey, 0, encryptedTitleKey.Length);
fireDebug(" Writing Unknown2... (Offset: 0x{0})", ms.Position.ToString("x8").ToUpper());
ms.WriteByte(unknown2);
fireDebug(" Writing Ticket ID... (Offset: 0x{0})", ms.Position.ToString("x8").ToUpper());
ms.Write(BitConverter.GetBytes(Shared.Swap(ticketId)), 0, 8);
fireDebug(" Writing Console ID... (Offset: 0x{0})", ms.Position.ToString("x8").ToUpper());
ms.Write(BitConverter.GetBytes(Shared.Swap(consoleId)), 0, 4);
fireDebug(" Writing Title ID... (Offset: 0x{0})", ms.Position.ToString("x8").ToUpper());
ms.Write(BitConverter.GetBytes(Shared.Swap(titleId)), 0, 8);
fireDebug(" Writing Unknwon3... (Offset: 0x{0})", ms.Position.ToString("x8").ToUpper());
ms.Write(BitConverter.GetBytes(Shared.Swap(unknown3)), 0, 2);
fireDebug(" Writing NumOfDLC... (Offset: 0x{0})", ms.Position.ToString("x8").ToUpper());
ms.Write(BitConverter.GetBytes(Shared.Swap(numOfDlc)), 0, 2);
fireDebug(" Writing Unknwon4... (Offset: 0x{0})", ms.Position.ToString("x8").ToUpper());
ms.Write(BitConverter.GetBytes(Shared.Swap(unknown4)), 0, 8);
fireDebug(" Writing Padding2... (Offset: 0x{0})", ms.Position.ToString("x8").ToUpper());
ms.WriteByte(padding2);
fireDebug(" Writing Common Key Index... (Offset: 0x{0})", ms.Position.ToString("x8").ToUpper());
ms.WriteByte(commonKeyIndex);
fireDebug(" Writing Unknown5... (Offset: 0x{0})", ms.Position.ToString("x8").ToUpper());
ms.Write(unknown5, 0, unknown5.Length);
fireDebug(" Writing Unknown6... (Offset: 0x{0})", ms.Position.ToString("x8").ToUpper());
ms.Write(unknown6, 0, unknown6.Length);
fireDebug(" Writing Padding3... (Offset: 0x{0})", ms.Position.ToString("x8").ToUpper());
ms.Write(BitConverter.GetBytes(Shared.Swap(padding3)), 0, 2);
fireDebug(" Writing Enable Time Limit... (Offset: 0x{0})", ms.Position.ToString("x8").ToUpper());
ms.Write(BitConverter.GetBytes(Shared.Swap(enableTimeLimit)), 0, 4);
fireDebug(" Writing Time Limit... (Offset: 0x{0})", ms.Position.ToString("x8").ToUpper());
ms.Write(BitConverter.GetBytes(Shared.Swap(timeLimit)), 0, 4);
fireDebug(" Writing Padding4... (Offset: 0x{0})", ms.Position.ToString("x8").ToUpper());
ms.Write(padding4, 0, padding4.Length);
byte[] tik = ms.ToArray();
ms.Dispose();
//fake Sign
if (fakeSign)
{
fireDebug(" Fakesigning Ticket...");
byte[] hash = new byte[20];
SHA1 s = SHA1.Create();
for (ushort i = 0; i < 0xFFFF; i++)
{
byte[] bytes = BitConverter.GetBytes(i);
tik[498] = bytes[1]; tik[499] = bytes[0];
hash = s.ComputeHash(tik);
if (hash[0] == 0x00)
{ fireDebug(" -> Signed ({0})", i); break; } //Win! It's signed...
if (i == 0xFFFF - 1)
{ fireDebug(" -> Signing Failed..."); throw new Exception("Fakesigning failed..."); }
}
s.Clear();
}
writeStream.Seek(0, SeekOrigin.Begin);
writeStream.Write(tik, 0, tik.Length);
fireDebug("Writing Ticket Finished...");
}
private void parseTicket(Stream ticketFile)
{
fireDebug("Parsing Ticket...");
ticketFile.Seek(0, SeekOrigin.Begin);
byte[] temp = new byte[8];
fireDebug(" Reading Signature Exponent... (Offset: 0x{0})", ticketFile.Position.ToString("x8").ToUpper());
ticketFile.Read(temp, 0, 4);
signatureExponent = Shared.Swap(BitConverter.ToUInt32(temp, 0));
fireDebug(" Reading Signature... (Offset: 0x{0})", ticketFile.Position.ToString("x8").ToUpper());
ticketFile.Read(signature, 0, signature.Length);
fireDebug(" Reading Padding... (Offset: 0x{0})", ticketFile.Position.ToString("x8").ToUpper());
ticketFile.Read(padding, 0, padding.Length);
fireDebug(" Reading Issuer... (Offset: 0x{0})", ticketFile.Position.ToString("x8").ToUpper());
ticketFile.Read(issuer, 0, issuer.Length);
fireDebug(" Reading Unknown... (Offset: 0x{0})", ticketFile.Position.ToString("x8").ToUpper());
ticketFile.Read(unknown, 0, unknown.Length);
fireDebug(" Reading Title Key... (Offset: 0x{0})", ticketFile.Position.ToString("x8").ToUpper());
ticketFile.Read(encryptedTitleKey, 0, encryptedTitleKey.Length);
fireDebug(" Reading Unknown2... (Offset: 0x{0})", ticketFile.Position.ToString("x8").ToUpper());
unknown2 = (byte)ticketFile.ReadByte();
fireDebug(" Reading Ticket ID.. (Offset: 0x{0})", ticketFile.Position.ToString("x8").ToUpper());
ticketFile.Read(temp, 0, 8);
ticketId = Shared.Swap(BitConverter.ToUInt64(temp, 0));
fireDebug(" Reading Console ID... (Offset: 0x{0})", ticketFile.Position.ToString("x8").ToUpper());
ticketFile.Read(temp, 0, 4);
consoleId = Shared.Swap(BitConverter.ToUInt32(temp, 0));
fireDebug(" Reading Title ID... (Offset: 0x{0})", ticketFile.Position.ToString("x8").ToUpper());
ticketFile.Read(temp, 0, 8);
titleId = Shared.Swap(BitConverter.ToUInt64(temp, 0));
fireDebug(" Reading Unknown3... (Offset: 0x{0})", ticketFile.Position.ToString("x8").ToUpper());
fireDebug(" Reading NumOfDLC... (Offset: 0x{0})", ticketFile.Position.ToString("x8").ToUpper());
ticketFile.Read(temp, 0, 4);
unknown3 = Shared.Swap(BitConverter.ToUInt16(temp, 0));
numOfDlc = Shared.Swap(BitConverter.ToUInt16(temp, 2));
fireDebug(" Reading Unknown4... (Offset: 0x{0})", ticketFile.Position.ToString("x8").ToUpper());
ticketFile.Read(temp, 0, 8);
unknown4 = Shared.Swap(BitConverter.ToUInt64(temp, 0));
fireDebug(" Reading Padding2... (Offset: 0x{0})", ticketFile.Position.ToString("x8").ToUpper());
padding2 = (byte)ticketFile.ReadByte();
fireDebug(" Reading Common Key Index... (Offset: 0x{0})", ticketFile.Position.ToString("x8").ToUpper());
commonKeyIndex = (byte)ticketFile.ReadByte();
newKeyIndex = commonKeyIndex;
fireDebug(" Reading Unknown5... (Offset: 0x{0})", ticketFile.Position.ToString("x8").ToUpper());
ticketFile.Read(unknown5, 0, unknown5.Length);
fireDebug(" Reading Unknown6... (Offset: 0x{0})", ticketFile.Position.ToString("x8").ToUpper());
ticketFile.Read(unknown6, 0, unknown6.Length);
fireDebug(" Reading Padding3... (Offset: 0x{0})", ticketFile.Position.ToString("x8").ToUpper());
ticketFile.Read(temp, 0, 2);
padding3 = Shared.Swap(BitConverter.ToUInt16(temp, 0));
fireDebug(" Reading Enable Time Limit... (Offset: 0x{0})", ticketFile.Position.ToString("x8").ToUpper());
fireDebug(" Reading Time Limit... (Offset: 0x{0})", ticketFile.Position.ToString("x8").ToUpper());
ticketFile.Read(temp, 0, 8);
enableTimeLimit = Shared.Swap(BitConverter.ToUInt32(temp, 0));
timeLimit = Shared.Swap(BitConverter.ToUInt32(temp, 4));
fireDebug(" Reading Padding4... (Offset: 0x{0})", ticketFile.Position.ToString("x8").ToUpper());
ticketFile.Read(padding4, 0, padding4.Length);
fireDebug(" Decrypting Title Key...");
decryptTitleKey();
fireDebug(" -> Encrypted Title Key: {0}", Shared.ByteArrayToString(encryptedTitleKey));
fireDebug(" -> Decrypted Title Key: {0}", Shared.ByteArrayToString(decryptedTitleKey));
fireDebug("Parsing Ticket Finished...");
}
private void decryptTitleKey()
{
byte[] ckey = (commonKeyIndex == 0x01) ? CommonKey.GetKoreanKey() : CommonKey.GetStandardKey();
byte[] iv = BitConverter.GetBytes(Shared.Swap(titleId));
Array.Resize(ref iv, 16);
RijndaelManaged rm = new RijndaelManaged();
rm.Mode = CipherMode.CBC;
rm.Padding = PaddingMode.None;
rm.KeySize = 128;
rm.BlockSize = 128;
rm.Key = ckey;
rm.IV = iv;
ICryptoTransform decryptor = rm.CreateDecryptor();
MemoryStream ms = new MemoryStream(encryptedTitleKey);
CryptoStream cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read);
cs.Read(decryptedTitleKey, 0, decryptedTitleKey.Length);
cs.Dispose();
ms.Dispose();
decryptor.Dispose();
rm.Clear();
}
private void encryptTitleKey()
{
commonKeyIndex = newKeyIndex;
byte[] ckey = (commonKeyIndex == 0x01) ? CommonKey.GetKoreanKey() : CommonKey.GetStandardKey();
byte[] iv = BitConverter.GetBytes(Shared.Swap(titleId));
Array.Resize(ref iv, 16);
RijndaelManaged rm = new RijndaelManaged();
rm.Mode = CipherMode.CBC;
rm.Padding = PaddingMode.None;
rm.KeySize = 128;
rm.BlockSize = 128;
rm.Key = ckey;
rm.IV = iv;
ICryptoTransform encryptor = rm.CreateEncryptor();
MemoryStream ms = new MemoryStream(decryptedTitleKey);
CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Read);
cs.Read(encryptedTitleKey, 0, encryptedTitleKey.Length);
cs.Dispose();
ms.Dispose();
encryptor.Dispose();
rm.Clear();
}
private void reDecryptTitleKey()
{
encryptedTitleKey = newEncryptedTitleKey;
decryptTitleKey();
}
#endregion
#region Events
/// <summary>
/// Fires debugging messages. You may write them into a log file or log textbox.
/// </summary>
public event EventHandler<MessageEventArgs> Debug;
private void fireDebug(string debugMessage, params object[] args)
{
EventHandler<MessageEventArgs> debug = Debug;
if (debug != null)
debug(new object(), new MessageEventArgs(string.Format(debugMessage, args)));
}
#endregion
}
}

1120
NUS Downloader/U8.cs Normal file

File diff suppressed because it is too large Load Diff

1269
NUS Downloader/WAD.cs Normal file

File diff suppressed because it is too large Load Diff