public void Dev_Signed_XVC_Decrypt_Test() { const string dest = @"F:\XBone\XVDs\TestXVDs\xvd1_decrypted_temp"; const string fileToCompare = @"F:\XBone\XVDs\TestXVDs\xvd1_decrypted"; if (File.Exists(dest)) File.Delete(dest); File.Copy(@"F:\XBone\XVDs\TestXVDs\xvd1", dest); using (var file = new XvdFile(dest)) { Assert.IsTrue(file.Load()); Assert.IsTrue(file.Header.IsSignedWithRedKey); Assert.IsTrue(file.IsEncrypted); Assert.IsTrue(file.IsDataIntegrityEnabled); Assert.IsTrue(file.HashTreeValid); Assert.IsTrue(file.DataHashTreeValid); Assert.IsTrue(file.Decrypt()); Assert.IsFalse(file.IsEncrypted); int[] invalid = file.VerifyDataHashTree(); Assert.IsTrue(invalid.Length == 0); Assert.IsTrue(file.VerifyHashTree()); byte[] ntfsString = file.Read(0x87003, 4); byte[] expectedString = { 0x4E, 0x54, 0x46, 0x53 }; Assert.IsTrue(ntfsString.IsEqualTo(expectedString)); } byte[] generatedHash; using (FileStream stream = File.OpenRead(dest)) { generatedHash = new SHA256Managed().ComputeHash(stream); } File.Delete(dest); byte[] expectedHash; using (FileStream stream = File.OpenRead(fileToCompare)) { expectedHash = new SHA256Managed().ComputeHash(stream); } Assert.IsTrue(generatedHash.IsEqualTo(expectedHash)); }
public void Unsigned_XVD_Encrypt_Test() { const string dest = @"F:\XBone\XVDs\TestXVDs\xvd2_encrypted_temp"; const string fileToCompare = @"F:\XBone\XVDs\TestXVDs\xvd2"; if (File.Exists(dest)) { File.Delete(dest); } File.Copy(@"F:\XBone\XVDs\TestXVDs\xvd2_decrypted_orig_mod", dest); // modded with CIK used in xvd2 using (var file = new XvdFile(dest)) { Assert.True(file.Load()); /* * Assert.False(file.Header.IsSignedWithRedKey); */ Assert.False(file.IsEncrypted); Assert.False(file.IsDataIntegrityEnabled); // Assert.True(file.Encrypt()); Assert.True(file.IsEncrypted); // copy values from file being compared so the hashes match file.Header.PDUID = new byte[] { 0xEA, 0xC8, 0xE2, 0x82, 0x2F, 0x58, 0x32, 0x4F, 0x92, 0x29, 0xE1, 0xAB, 0x6E, 0x8F, 0x91, 0x63 }; using (FileStream stream = File.OpenRead(fileToCompare)) { stream.Position = 0; stream.Read(file.Header.Signature, 0, 0x200); } Assert.True(file.AddHashTree()); ulong[] invalid = file.VerifyDataHashTree(); Assert.True(invalid.Length == 0); Assert.True(file.VerifyHashTree()); } byte[] generatedHash; using (FileStream stream = File.OpenRead(dest)) { generatedHash = HashUtils.ComputeSha256(stream); } File.Delete(dest); byte[] expectedHash; using (FileStream stream = File.OpenRead(fileToCompare)) { expectedHash = HashUtils.ComputeSha256(stream); } Assert.True(generatedHash.IsEqualTo(expectedHash)); }
public void Dev_Signed_InvalidHashTree_Test() { using (var file = new XvdFile(@"F:\XBone\XVDs\TestXVDs\xvd1_brokehash")) { Assert.IsTrue(file.Load()); if (XvdFile.SignKeyLoaded) Assert.IsTrue(file.Header.IsSignedWithRedKey); Assert.IsTrue(file.IsEncrypted); Assert.IsTrue(file.IsDataIntegrityEnabled); Assert.IsFalse(file.HashTreeValid); } }
public void Dev_Signed_InvalidHashTree_Test() { using (var file = new XvdFile(@"F:\XBone\XVDs\TestXVDs\xvd1_brokehash")) { Assert.IsTrue(file.Load()); if (XvdFile.SignKeyLoaded) { Assert.IsTrue(file.Header.IsSignedWithRedKey); } Assert.IsTrue(file.IsEncrypted); Assert.IsTrue(file.IsDataIntegrityEnabled); Assert.IsFalse(file.HashTreeValid); } }
public void Unsigned_XVD_Decrypt_Test() { const string dest = @"F:\XBone\XVDs\TestXVDs\xvd2_decrypted_temp"; const string fileToCompare = @"F:\XBone\XVDs\TestXVDs\xvd2_decrypted"; if (File.Exists(dest)) { File.Delete(dest); } File.Copy(@"F:\XBone\XVDs\TestXVDs\xvd2", dest); using (var file = new XvdFile(dest)) { Assert.True(file.Load()); /* * Assert.True(file.Header.IsSignedWithRedKey); */ Assert.True(file.IsEncrypted); Assert.True(file.IsDataIntegrityEnabled); Assert.True(file.HashTreeValid); Assert.True(file.DataHashTreeValid); Assert.True(file.Decrypt()); Assert.False(file.IsEncrypted); ulong[] invalid = file.VerifyDataHashTree(); Assert.True(invalid.Length == 0); Assert.True(file.VerifyHashTree()); byte[] ntfsString = file.Read(0x75003, 4); byte[] expectedString = { 0x4E, 0x54, 0x46, 0x53 }; Assert.True(ntfsString.IsEqualTo(expectedString)); } byte[] generatedHash; using (FileStream stream = File.OpenRead(dest)) { generatedHash = HashUtils.ComputeSha256(stream); } File.Delete(dest); byte[] expectedHash; using (FileStream stream = File.OpenRead(fileToCompare)) { expectedHash = HashUtils.ComputeSha256(stream); } Assert.True(generatedHash.IsEqualTo(expectedHash)); }
public void Dev_Signed_ValidHash_Test() { using (var file = new XvdFile(@"F:\XBone\XVDs\TestXVDs\xvd1")) { Assert.True(file.Load()); /* * if(XvdFile.SignKeyLoaded) * Assert.True(file.Header.IsSignedWithRedKey); */ Assert.True(file.IsEncrypted); Assert.True(file.IsDataIntegrityEnabled); Assert.True(file.HashTreeValid); Assert.True(file.DataHashTreeValid); } }
public void XvdSign_Key_Extract() { var sdkVersions = new List <string> { "XDK_11785" }; var versionsTextFile = @"F:\Xbone\Research\xdk_versions.txt"; if (File.Exists(versionsTextFile)) { string[] sdkVersionArr = File.ReadAllLines(versionsTextFile); foreach (string ver in sdkVersionArr) { if (!sdkVersions.Contains(ver.ToUpper())) { sdkVersions.Add(ver.ToUpper()); } } } foreach (string ver in sdkVersions) { string path = Path.Combine(@"F:\Xbone\Research\", ver); string binPath = Path.Combine(path, "bin"); if (Directory.Exists(binPath)) { path = binPath; } if (!File.Exists(Path.Combine(path, "xvdsign.exe"))) { continue; } XvdFile.CikFileLoaded = false; XvdFile.OdkKeyLoaded = false; XvdFile.SignKeyLoaded = false; XvdFile.LoadKeysFromSdk(path); Assert.IsTrue(XvdFile.CikFileLoaded && XvdFile.OdkKeyLoaded && XvdFile.SignKeyLoaded); } XvdFile.CikFileLoaded = false; XvdFile.OdkKeyLoaded = false; XvdFile.SignKeyLoaded = false; }
public void Dev_Signed_XVC_Encrypt_Test() { const string dest = @"F:\XBone\XVDs\TestXVDs\xvd1_encrypted_temp"; const string fileToCompare = @"F:\XBone\XVDs\TestXVDs\xvd1"; if (File.Exists(dest)) { File.Delete(dest); } File.Copy(@"F:\XBone\XVDs\TestXVDs\xvd1_decrypted", dest); using (var file = new XvdFile(dest)) { Assert.IsTrue(file.Load()); Assert.IsFalse(file.IsEncrypted); Assert.IsTrue(file.IsDataIntegrityEnabled); Assert.IsTrue(file.HashTreeValid); Assert.IsTrue(file.DataHashTreeValid); Assert.IsTrue(file.Encrypt()); Assert.IsTrue(file.IsEncrypted); int[] invalid = file.VerifyDataHashTree(); Assert.IsTrue(invalid.Length == 0); Assert.IsTrue(file.VerifyHashTree()); } byte[] generatedHash; using (FileStream stream = File.OpenRead(dest)) { generatedHash = new SHA256Managed().ComputeHash(stream); } File.Delete(dest); byte[] expectedHash; using (FileStream stream = File.OpenRead(fileToCompare)) { expectedHash = new SHA256Managed().ComputeHash(stream); } Assert.IsTrue(generatedHash.IsEqualTo(expectedHash)); }
public void Unsigned_XVD_Encrypt_Test() { const string dest = @"F:\XBone\XVDs\TestXVDs\xvd2_encrypted_temp"; const string fileToCompare = @"F:\XBone\XVDs\TestXVDs\xvd2"; if (File.Exists(dest)) File.Delete(dest); File.Copy(@"F:\XBone\XVDs\TestXVDs\xvd2_decrypted_orig_mod", dest); // modded with CIK used in xvd2 using (var file = new XvdFile(dest)) { Assert.IsTrue(file.Load()); Assert.IsFalse(file.Header.IsSignedWithRedKey); Assert.IsFalse(file.IsEncrypted); Assert.IsFalse(file.IsDataIntegrityEnabled); Assert.IsTrue(file.Encrypt()); Assert.IsTrue(file.IsEncrypted); // copy values from file being compared so the hashes match file.Header.PDUID = new byte[] {0xEA, 0xC8, 0xE2, 0x82, 0x2F, 0x58, 0x32, 0x4F, 0x92, 0x29, 0xE1, 0xAB, 0x6E, 0x8F, 0x91, 0x63}; using (FileStream stream = File.OpenRead(fileToCompare)) { stream.Position = 0; stream.Read(file.Header.Signature, 0, 0x200); } Assert.IsTrue(file.AddHashTree()); int[] invalid = file.VerifyDataHashTree(); Assert.IsTrue(invalid.Length == 0); Assert.IsTrue(file.VerifyHashTree()); } byte[] generatedHash; using (FileStream stream = File.OpenRead(dest)) { generatedHash = new SHA256Managed().ComputeHash(stream); } File.Delete(dest); byte[] expectedHash; using (FileStream stream = File.OpenRead(fileToCompare)) { expectedHash = new SHA256Managed().ComputeHash(stream); } Assert.IsTrue(generatedHash.IsEqualTo(expectedHash)); }
static void Main(string[] args) { const string fmt = " "; var outputFile = String.Empty; var fileList = String.Empty; var folder = String.Empty; var exvdDest = String.Empty; var userDataDest = String.Empty; var vhdDest = String.Empty; var rawimageDest = String.Empty; var fsDest = String.Empty; var signKeyToUse = String.Empty; var odkToUse = OdkIndex.Invalid; var cikToUse = Guid.Empty; var signKeyFilepath = String.Empty; var odkFilepath = String.Empty; var cikFilepath = String.Empty; bool listKeys = false; string mountPoint = null; var mountPackage = false; var unmountPackage = false; var decryptPackage = false; var encryptPackage = false; var rehashPackage = false; var resignPackage = false; var addHashTree = false; var removeHashTree = false; var addMdu = false; var removeMdu = false; var printInfo = false; var writeInfo = false; var printHelp = false; var disableDataExtract = false; XvdFile.PrintProgressToConsole = true; XvdFile.DisableSaveAfterModification = true; var p = new OptionSet { { "h|?|help", v => printHelp = v != null }, { "i|info", v => printInfo = v != null }, { "wi|writeinfo", v => writeInfo = v != null }, { "o|output=", v => outputFile = v }, { "m|mount", v => mountPackage = v != null }, { "um|unmount", v => unmountPackage = v != null }, { "mp|mountpoint=", v => mountPoint = v }, { "lk|listkeys", v => listKeys = v != null }, { "signfile=", v => signKeyFilepath = v }, { "odkfile=", v => odkFilepath = v }, { "cikfile=", v => cikFilepath = v }, { "sk|signkey=", v => signKeyToUse = v }, { "odk|odkid=", v => { if (!DurangoKeys.GetOdkIndexFromString(v, out odkToUse)) { throw new OptionException("Invalid Odk Id", "odkid"); } } }, { "cik|cikguid=", v => { if (!Guid.TryParse(v, out cikToUse)) { throw new OptionException("Invalid CIK GUID", "cikguid"); } } }, { "nd|nodatahash", v => XvdFile.DisableDataHashChecking = v != null }, { "ne|noextract", v => disableDataExtract = v != null }, { "r|rehash", v => rehashPackage = v != null }, { "rs|resign", v => resignPackage = v != null }, { "eu|decrypt", v => decryptPackage = v != null }, { "ee|encrypt", v => encryptPackage = v != null }, { "hd|removehash|removehashtree", v => removeHashTree = v != null }, { "he|addhash|addhashtree", v => addHashTree = v != null }, { "md|removemdu", v => removeMdu = v != null }, { "ma|addmdu", v => addMdu = v != null }, { "xe|extractembedded=", v => exvdDest = v }, { "xu|extractuserdata=", v => userDataDest = v }, { "xv|extractvhd=", v => vhdDest = v }, { "xi|extractimage=", v => rawimageDest = v }, { "xf|extractfiles=", v => fsDest = v }, { "l|filelist=", v => fileList = v }, { "f|folder=", v => folder = v }, }; List <string> extraArgs; try { extraArgs = p.Parse(args); } catch (OptionException e) { Console.WriteLine($"Failed parsing parameter \'{e.OptionName}\': {e.Message}"); Console.WriteLine("Try 'xvdtool --help' for more information"); return; } Console.WriteLine($"XVDTool {AppVersion}: XVD file manipulator"); if (printHelp || (String.IsNullOrEmpty(fileList) && String.IsNullOrEmpty(folder) && !listKeys && extraArgs.Count <= 0)) { Console.WriteLine("Usage : xvdtool.exe [parameters] [filename]"); Console.WriteLine(); Console.WriteLine("Parameters:"); Console.WriteLine(fmt + "-h (-help) - print xvdtool usage"); Console.WriteLine(fmt + "-i (-info) - print info about package"); Console.WriteLine(fmt + "-wi (-writeinfo) - write info about package to [filename].txt"); Console.WriteLine(fmt + "-o (-output) <output-path> - specify output filename"); Console.WriteLine(); Console.WriteLine(fmt + "-m (-mount) - mount package"); Console.WriteLine(fmt + "-um (-unmount) - unmount package"); Console.WriteLine(fmt + "-mp (-mountpoint) - Mount point for package (e.g. \"X:\")"); Console.WriteLine(); Console.WriteLine(fmt + "-lk (-listkeys) - List known keys including their hashes / availability"); Console.WriteLine(); Console.WriteLine(fmt + "-signfile <path-to-file> - Path to xvd sign key (RSA)"); Console.WriteLine(fmt + "-odkfile <path-to-file> - Path to Offline Distribution key"); Console.WriteLine(fmt + "-cikfile <path-to-file> - Path to Content Instance key"); Console.WriteLine(); Console.WriteLine(fmt + "-sk (-signkey) <key-name> - Name of xvd sign key to use"); Console.WriteLine(fmt + "-odk (-odkid) <id> - Id of Offline Distribution key to use (uint)"); Console.WriteLine(fmt + "-cik (-cikguid) <GUID> - Guid of Content Instance key to use"); Console.WriteLine(); Console.WriteLine(fmt + "-nd (-nodatahash) - disable data hash checking, speeds up -l and -f"); Console.WriteLine(fmt + "-ne (-noextract) - disable data (embedded XVD/user data) extraction, speeds up -l and -f"); Console.WriteLine(); Console.WriteLine(fmt + "-eu (-decrypt) - decrypt output xvd"); Console.WriteLine(fmt + "-ee (-encrypt) - encrypt output xvd"); Console.WriteLine(fmt + fmt + "XVDs will have a new CIK generated (if CIK in XVD header is empty), which will be encrypted with the ODK and stored in the XVD header"); Console.WriteLine(); Console.WriteLine(fmt + "-hd (-removehash) - remove hash tree/data integrity from package"); Console.WriteLine(fmt + "-he (-addhash) - add hash tree/data integrity to package"); Console.WriteLine(); Console.WriteLine(fmt + "-md (-removemdu) - remove mutable data (MDU) from package"); Console.WriteLine(fmt + "-ma (-addmdu) - add mutable data (MDU) to package"); Console.WriteLine(); Console.WriteLine(fmt + "-r (-rehash) - fix data integrity hashes inside package"); Console.WriteLine(fmt + "-rs (-resign) - sign package using the private key from rsa3_key.bin"); Console.WriteLine(); Console.WriteLine(fmt + "-xe (-extractembedded) <output-file> - extract embedded XVD from package"); Console.WriteLine(fmt + "-xu (-extractuserdata) <output-file> - extract user data from package"); Console.WriteLine(fmt + "-xv (-extractvhd) <output-vhd> - extracts filesystem from XVD into a VHD file"); Console.WriteLine(fmt + "-xi (-extractimage) <output-file> - extract raw filesystem image"); Console.WriteLine(fmt + "-xf (-extractfiles) <output-folder> - extract files from XVD filesystem"); Console.WriteLine(); Console.WriteLine(fmt + "The next two commands will write info about each package found to [filename].txt"); Console.WriteLine(fmt + "also extracts embedded XVD and user data to [filename].exvd.bin / [filename].userdata.bin"); Console.WriteLine(fmt + "-l (-filelist) <path-to-file-list> - use each XVD specified in the list"); Console.WriteLine(fmt + "-f (-folder) <path-to-folder> - scan folder for XVD files"); Console.WriteLine(); Console.WriteLine(@"To mount a package in Windows you'll have to decrypt it and remove the hash tables & mutable data first (-eu -hd -md)"); return; } Console.WriteLine(); var fallbackCik = DurangoKeys.TestCIK; var fallbackSignKey = DurangoKeys.RedXvdPrivateKey; var localConfigDir = AppDirs.GetApplicationConfigDirectory(AppName); var executableDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); /* If necessary, create folders to store keys */ EnsureConfigDirectoryStructure(localConfigDir); EnsureConfigDirectoryStructure(executableDir); /* Load keys from global and local config directory */ DurangoKeys.LoadKeysRecursive(localConfigDir); DurangoKeys.LoadKeysRecursive(executableDir); /* Check key parameters */ if (signKeyFilepath != String.Empty) { if (!DurangoKeys.LoadKey(KeyType.XvdSigningKey, signKeyFilepath)) { Console.WriteLine($"Failed to load SignKey from {signKeyFilepath}"); return; } } if (odkFilepath != String.Empty) { if (!DurangoKeys.LoadKey(KeyType.Odk, odkFilepath)) { Console.WriteLine($"Failed to load ODK key from {odkFilepath}"); return; } } if (cikFilepath != String.Empty) { if (!DurangoKeys.LoadKey(KeyType.Cik, cikFilepath)) { Console.WriteLine($"Failed to load CIK from {cikFilepath}"); return; } } if (signKeyToUse == String.Empty) { Console.WriteLine($"No desired signkey provided, falling back to {fallbackSignKey}"); signKeyToUse = fallbackSignKey; } if (cikToUse == Guid.Empty) { Console.WriteLine($"No desired CIK provided, falling back to {fallbackCik}"); cikToUse = fallbackCik; } if (odkToUse == OdkIndex.Invalid) { Console.WriteLine("No desired or invalid ODK provided, will try to use ODK indicated by XVD header"); } else if (!DurangoKeys.IsOdkLoaded(odkToUse)) { Console.WriteLine($"Warning: ODK {odkToUse} could not be loaded!"); } else { Console.WriteLine($"Using ODK: {odkToUse}"); } Console.WriteLine(!DurangoKeys.IsSignkeyLoaded(signKeyToUse) ? "Warning: Signkey could not be loaded, you will be unable to resign XVD headers!" : $"Using Xvd Signkey: {signKeyToUse}"); Console.WriteLine(!DurangoKeys.IsCikLoaded(cikToUse) ? "Warning: CIK could not be loaded!" : $"Using CIK: {cikToUse}"); Console.WriteLine(); if (listKeys) { void PrintKnownKeys <T>(KeyType type, KeyValuePair <T, DurangoKeyEntry>[] keyCollection) { Console.WriteLine($"{type}:"); foreach (var keyKvp in keyCollection) { Console.WriteLine(fmt + $"{keyKvp.Key}: {keyKvp.Value}"); } } Console.WriteLine("Known Durango keys:"); PrintKnownKeys(KeyType.XvdSigningKey, DurangoKeys.GetAllXvdSigningKeys()); PrintKnownKeys(KeyType.Odk, DurangoKeys.GetAllODK()); PrintKnownKeys(KeyType.Cik, DurangoKeys.GetAllCIK()); return; } /* Write out xvd info / extract data from provided folderpath */ if (!String.IsNullOrEmpty(folder)) { IEnumerable <string> files = ScanFolderForXvds(folder, true); foreach (string filename in files) { var xvd = new XvdFile(filename); xvd.Load(); try { File.WriteAllText(filename + ".txt", xvd.ToString(true)); } // ReSharper disable once EmptyGeneralCatchClause catch { } if (!disableDataExtract) { if (xvd.Header.EmbeddedXVDLength > 0) { File.WriteAllBytes(filename + ".exvd.bin", xvd.ExtractEmbeddedXvd()); } if (xvd.Header.UserDataLength > 0 && !xvd.IsEncrypted) { File.WriteAllBytes(filename + ".userdata.bin", xvd.ExtractUserData()); } } xvd.Dispose(); } return; } /* Write out xvd info / extract data from provided filelist */ if (!String.IsNullOrEmpty(fileList)) { string[] files = File.ReadAllLines(fileList); foreach (string filename in files) { var xvd = new XvdFile(filename); xvd.Load(); File.WriteAllText(filename + ".txt", xvd.ToString(true)); if (!disableDataExtract) { if (xvd.Header.EmbeddedXVDLength > 0) { File.WriteAllBytes(filename + ".exvd.bin", xvd.ExtractEmbeddedXvd()); } if (xvd.Header.UserDataLength > 0 && !xvd.IsEncrypted) { File.WriteAllBytes(filename + ".userdata.bin", xvd.ExtractUserData()); } } xvd.Dispose(); } return; } /* Handle input xvd */ if (extraArgs.Count <= 0) { return; } string filePath = extraArgs[0]; if (!File.Exists(filePath)) { Console.WriteLine(@"Error: input file doesn't exist"); return; } if (!String.IsNullOrEmpty(outputFile)) { if (File.Exists(outputFile)) { Console.WriteLine(@"Error: output file already exists."); return; } Console.WriteLine("Copying original file to output destination..."); File.Copy(filePath, outputFile); filePath = outputFile; } if (mountPackage) { bool success = XvdMount.MountXvd(filePath, mountPoint); Console.WriteLine("Mounting {0} {1}", filePath, success ? "completed successfully" : "failed with error" ); return; } if (unmountPackage) { bool success = XvdMount.UnmountXvd(filePath); Console.WriteLine("Unmounting {0} {1}", filePath, success ? "completed successfully" : "failed with error" ); return; } Console.WriteLine($"Loading file from {filePath}..."); Console.WriteLine(); bool PerformActions(XvdFile file) { bool fileModified = false; if (printInfo || writeInfo) { string info = file.ToString(true); if (writeInfo) { File.WriteAllText(filePath + ".txt", info); Console.WriteLine($"Wrote package info to \"{filePath}.txt\""); } else { Console.WriteLine(info); } } if (addHashTree) { if (file.IsDataIntegrityEnabled) { Console.WriteLine("Warning: -addhashtree failed as package already has a hash tree."); } else { Console.WriteLine("Attempting to add hash tree to package..."); bool success = file.AddHashTree(); Console.WriteLine(success ? "Hash tree added successfully and header updated." : "Error: failed to extend package to make room for hash tree, is there enough disk space?"); if (!success) { return(fileModified); } fileModified = true; } } if (encryptPackage) { if (file.IsEncrypted) { Console.WriteLine("Warning: -encrypt failed as package is already encrypted"); } else { string keyToUse = "ODK"; if (file.IsXvcFile) { var cikKeys = DurangoKeys.GetAllCIK(); var chosenKey = cikKeys.Single(kvp => kvp.Key == cikToUse).Value; if (chosenKey == null) { Console.WriteLine( "Error: Invalid CIK key \"{encryptKeyId}\" specified, make sure said key exists!"); return(fileModified); } keyToUse = $"CIK:{cikToUse}"; } Console.WriteLine($"Encrypting package using \"{keyToUse}\" key..."); bool success = file.Encrypt(cikToUse); Console.WriteLine(success ? "Package encrypted successfully!" : "Error during encryption!"); if (!success) { return(fileModified); } fileModified = true; } } if (decryptPackage) { if (!file.IsEncrypted) { Console.WriteLine(@"Warning: -decrypt failed as package is already decrypted"); } else { if (file.IsXvcFile) { Console.WriteLine("Decrypting XVC..."); } else { string keyToUse = odkToUse != OdkIndex.Invalid ? odkToUse.ToString() : "<ODK indicated by XVD header>"; Console.WriteLine($"Decrypting XVD using \"{keyToUse}\" key..."); } bool success = file.Decrypt(); Console.WriteLine(success ? "Package decrypted successfully!" : "Error during decryption!"); if (!success) { return(fileModified); } fileModified = true; } } if (removeHashTree) { if (!file.IsDataIntegrityEnabled) { Console.WriteLine("Warning: -removehashtree failed as package doesn't have a hash tree."); } else { Console.WriteLine("Attempting to remove hash tree from package..."); bool success = file.RemoveHashTree(); Console.WriteLine(success ? "Hash tree removed successfully and header updated." : "Error: hash tree is larger than input package (???)"); if (!success) { return(fileModified); } fileModified = true; } } if (removeMdu) { if (file.Header.MutableDataPageCount <= 0) { Console.WriteLine("Warning: -removemdu failed as package doesn't have any mutable data."); } else { Console.WriteLine("Removing mutable data from package..."); bool success = file.RemoveMutableData(); Console.WriteLine(success ? "Mutable data removed successfully and header updated." : "Failed to remove mutable data?"); if (!success) { return(fileModified); } fileModified = true; } } if (addMdu) { if (file.Header.MutableDataPageCount > 0) { Console.WriteLine("Warning: -addmdu failed as package already has mutable data."); } else { Console.WriteLine("Adding mutable data to package..."); bool success = file.AddMutableData(); Console.WriteLine(success ? "Mutable data added successfully and header updated." : "Failed to add mutable data?"); if (!success) { return(fileModified); } fileModified = true; } } if (rehashPackage) { if (!file.IsDataIntegrityEnabled) { Console.WriteLine("Warning: -rehash failed as package doesn't have a hash tree."); } else { Console.WriteLine($"Old top hash block hash: {file.Header.TopHashBlockHash.ToHexString()}"); Console.WriteLine("Rehashing package..."); ulong[] fixedHashes = file.VerifyDataHashTree(true); bool success = file.CalculateHashTree(); if (success) { Console.WriteLine($"New top hash block hash: {file.Header.TopHashBlockHash.ToHexString()}"); } Console.WriteLine(success ? $"Successfully rehashed {fixedHashes.Length} invalid data hashes inside package." : "Error: there was a problem rehashing the package."); if (!success) { return(fileModified); } fileModified = true; } } if (resignPackage) { var key = DurangoKeys.GetSignkeyByName(signKeyToUse); bool success = file.Header.Resign(key.KeyData, "RSAFULLPRIVATEBLOB"); Console.WriteLine(success ? "Successfully resigned package." : "Error: there was a problem resigning the package."); if (!success) { return(fileModified); } fileModified = true; } if (!String.IsNullOrEmpty(exvdDest)) { byte[] exvd = file.ExtractEmbeddedXvd(); if (exvd == null || exvd.Length <= 0) { Console.WriteLine( "Warning: -extractembedded failed as package doesn't contain an embedded XVD."); } else { try { File.WriteAllBytes(exvdDest, exvd); Console.WriteLine( $"Extracted embedded XVD to \"{exvdDest}\" successfully (0x{exvd.Length:X} bytes)"); } catch { Console.WriteLine("Error: failed to extract embedded XVD."); } } } if (!String.IsNullOrEmpty(userDataDest)) { byte[] userData = file.ExtractUserData(); if (userData == null || userData.Length <= 0) { Console.WriteLine("Warning: -extractuserdata failed as package doesn't contain user data."); } else { try { File.WriteAllBytes(userDataDest, userData); Console.WriteLine( $"Extracted XVD user data to \"{userDataDest}\" successfully (0x{userData.Length:X} bytes)"); } catch { Console.WriteLine("Error: failed to extract XVD user data."); } } } if (!String.IsNullOrEmpty(vhdDest)) { if (file.IsEncrypted) { Console.WriteLine("Warning: -extractvhd failed as package is still encrypted."); } else { Console.WriteLine($"Extracting XVD filesystem to VHD file \"{vhdDest}\"..."); bool success = file.Filesystem.ConvertToVhd(vhdDest); Console.WriteLine(success ? "Wrote VHD successfully." : "Error: there was a problem extracting the filesystem from the XVD."); if (!success) { return(fileModified); } } } if (!String.IsNullOrEmpty(rawimageDest)) { if (file.IsEncrypted) { Console.WriteLine("Warning: -extractimage failed as package is still encrypted."); } else { Console.WriteLine($"Extracting raw filesystem image to file \"{rawimageDest}\"..."); bool success = file.Filesystem.ExtractFilesystemImage(rawimageDest, false); Console.WriteLine(success ? "Extracted raw image successfully." : "Error: there was a problem extracting raw image from the XVD."); if (!success) { return(fileModified); } } } if (!String.IsNullOrEmpty(fsDest)) { if (file.IsEncrypted) { Console.WriteLine("Warning: -extractfiles failed as package is still encrypted."); } else { Console.WriteLine($"Extracting XVD files to folder \"{fsDest}\"..."); bool success = file.Filesystem.ExtractFilesystem(fsDest); Console.WriteLine(success ? "Extracted files successfully." : "Error: there was a problem extracting the files from the XVD."); if (!success) { return(fileModified); } } } return(fileModified); } var xvdFile = new XvdFile(filePath) { OverrideOdk = odkToUse }; xvdFile.Load(); if (PerformActions(xvdFile)) // PerformActions returns true if file has been modified { xvdFile.Save(); } xvdFile.Dispose(); }
static void Main(string[] args) { const string fmt = " "; var outputFile = String.Empty; var fileList = String.Empty; var folder = String.Empty; var exvdDest = String.Empty; var userDataDest = String.Empty; var vhdDest = String.Empty; var decryptPackage = false; var encryptPackage = false; var encryptKeyId = 0; var rehashPackage = false; var resignPackage = false; var addHashTree = false; var removeHashTree = false; var printInfo = false; var writeInfo = false; var printHelp = false; var disableDataExtract = false; var p = new OptionSet { { "h|?|help", v => printHelp = v != null }, { "i|info", v => printInfo = v != null }, { "wi|writeinfo", v => writeInfo = v != null }, { "o|output=", v => outputFile = v }, { "nd|nodatahash", v => XvdFile.DisableDataHashChecking = v != null }, { "nn|nonatives", v => XvdFile.DisableNativeFunctions = v != null }, { "ne|noextract", v => disableDataExtract = v != null }, { "r|rehash", v => rehashPackage = v != null }, { "rs|resign", v => resignPackage = v != null }, { "eu|decrypt", v => decryptPackage = v != null }, { "ee|encrypt", v => { encryptPackage = v != null; if (!int.TryParse(v, out encryptKeyId)) { Console.WriteLine("Error: invalid keyid specified for -encrypt"); System.Diagnostics.Process.GetCurrentProcess().Kill(); } } }, { "hd|removehash|removehashtree", v => removeHashTree = v != null }, { "he|addhash|addhashtree", v => addHashTree = v != null }, { "xe|extractembedded=", v => exvdDest = v }, { "xu|extractuserdata=", v => userDataDest = v }, { "xv|extractvhd=", v => vhdDest = v }, { "l|filelist=", v => fileList = v }, { "f|folder=", v => folder = v }, }; var extraArgs = p.Parse(args); Console.WriteLine("xvdtool 0.4: XVD file manipulator"); XvdFile.LoadKeysFromDisk(); if (!XvdFile.SignKeyLoaded) { Console.WriteLine("Warning: rsa3_key.bin file not found and unable to retrieve key from SDK files, you will be unable to resign packages."); } if (!XvdFile.OdkKeyLoaded) { Console.WriteLine("Warning: odk_key.bin file not found and unable to retrieve key from SDK files, you will be unable to decrypt XVDs."); } if (!XvdFile.CikFileLoaded) { Console.WriteLine("Warning: cik_keys.bin file not found and unable to retrieve key from SDK files, you will be unable to decrypt XVCs."); } if (printHelp || (String.IsNullOrEmpty(fileList) && String.IsNullOrEmpty(folder) && extraArgs.Count <= 0)) { if (!XvdFile.CikFileLoaded || !XvdFile.OdkKeyLoaded || !XvdFile.SignKeyLoaded) { Console.WriteLine(); } Console.WriteLine("Usage : xvdtool.exe [parameters] [filename]"); Console.WriteLine(); Console.WriteLine("Parameters:"); Console.WriteLine(fmt + "-h (-help) - print xvdtool usage"); Console.WriteLine(fmt + "-i (-info) - print info about package"); Console.WriteLine(fmt + "-wi (-writeinfo) - write info about package to [filename].txt"); Console.WriteLine(fmt + "-o (-output) <output-path> - specify output filename"); Console.WriteLine(fmt + "-nd (-nodatahash) - disable data hash checking, speeds up -l and -f"); Console.WriteLine(fmt + "-ne (-noextract) - disable data (embedded XVD/user data) extraction, speeds up -l and -f"); Console.WriteLine(fmt + "-nn (-nonatives) - disable importing native windows functions (ncrypt etc)"); Console.WriteLine(fmt + fmt + "note that signature verification/resigning won't work with this!"); Console.WriteLine(); Console.WriteLine(fmt + "-eu (-decrypt) = decrypt output xvd"); Console.WriteLine(fmt + "-ee (-encrypt) [keyid] = encrypt output xvd"); Console.WriteLine(fmt + fmt + "(optional [keyid] param for XVCs to choose which key inside cik_keys.bin to use)"); Console.WriteLine(fmt + fmt + "XVDs will have a new CIK generated (if CIK in XVD header is empty), which will be encrypted with the odk_key.bin and stored in the XVD header"); Console.WriteLine(); Console.WriteLine(fmt + "-hd (-removehash) - remove hash tree/data integrity from package"); Console.WriteLine(fmt + "-he (-addhash) - add hash tree/data integrity to package"); Console.WriteLine(); Console.WriteLine(fmt + "-r (-rehash) - fix data integrity hashes inside package"); Console.WriteLine(fmt + "-rs (-resign) - sign package using the private key from rsa3_key.bin"); Console.WriteLine(); Console.WriteLine(fmt + "-xe (-extractembedded) <output-file> - extract embedded XVD from package"); Console.WriteLine(fmt + "-xu (-extractuserdata) <output-file> - extract user data from package"); Console.WriteLine(fmt + "-xv (-extractvhd) <output-vhd> - extracts filesystem from XVD into a VHD file, doesn't seem to work properly with XVC packages yet (also removes NTFS compression from output VHD so Windows can mount it, use -nn to disable)"); Console.WriteLine(); Console.WriteLine(fmt + "The next two commands will write info about each package found to [filename].txt"); Console.WriteLine(fmt + "also extracts embedded XVD and user data to [filename].exvd.bin / [filename].userdata.bin"); Console.WriteLine(fmt + "-l (-filelist) <path-to-file-list> - use each XVD specified in the list"); Console.WriteLine(fmt + "-f (-folder) <path-to-folder> - scan folder for XVD files"); Console.WriteLine(); Console.WriteLine(@"Note that to mount an XVD/XVC in Windows you'll have to decrypt it and remove the hash tables first (-eu -hd)"); return; } Console.WriteLine(); if (!String.IsNullOrEmpty(folder)) { IEnumerable <string> files = ScanFolderForXvds(folder, true); foreach (string filename in files) { var xvd = new XvdFile(filename); xvd.Load(); try { File.WriteAllText(filename + ".txt", xvd.ToString(true)); } // ReSharper disable once EmptyGeneralCatchClause catch { } if (!disableDataExtract) { if (xvd.Header.EmbeddedXVDLength > 0) { File.WriteAllBytes(filename + ".exvd.bin", xvd.ExtractEmbeddedXvd()); } if (xvd.Header.UserDataLength > 0 && !xvd.IsEncrypted) { File.WriteAllBytes(filename + ".userdata.bin", xvd.ExtractUserData()); } } xvd.Dispose(); } return; } if (!String.IsNullOrEmpty(fileList)) { string[] files = File.ReadAllLines(fileList); foreach (string filename in files) { var xvd = new XvdFile(filename); xvd.Load(); File.WriteAllText(filename + ".txt", xvd.ToString(true)); if (!disableDataExtract) { if (xvd.Header.EmbeddedXVDLength > 0) { File.WriteAllBytes(filename + ".exvd.bin", xvd.ExtractEmbeddedXvd()); } if (xvd.Header.UserDataLength > 0 && !xvd.IsEncrypted) { File.WriteAllBytes(filename + ".userdata.bin", xvd.ExtractUserData()); } } xvd.Dispose(); } return; } if (extraArgs.Count > 0) { string filePath = extraArgs[0]; if (!File.Exists(filePath)) { Console.WriteLine(@"Error: input file doesn't exist"); return; } if (!String.IsNullOrEmpty(outputFile)) { if (File.Exists(outputFile)) { Console.WriteLine(@"Error: output file already exists."); return; } File.Copy(filePath, outputFile); filePath = outputFile; } var file = new XvdFile(filePath); file.Load(); if (printInfo || writeInfo) { string info = file.ToString(true); if (writeInfo) { File.WriteAllText(filePath + ".txt", info); Console.WriteLine("Wrote package info to \"" + filePath + ".txt\""); return; } Console.WriteLine(info); return; } if (decryptPackage) { if (!file.IsEncrypted) { Console.WriteLine(@"Error: package already decrypted"); return; } string keyToUse = "TestODK"; if (file.IsXvcFile) { byte[] outputKey; keyToUse = file.GetXvcKey(0, out outputKey); if (String.IsNullOrEmpty(keyToUse)) { Console.WriteLine("Error: unable to find key for key GUID " + new Guid(file.XvcInfo.EncryptionKeyIds[0].KeyId)); return; } } Console.WriteLine("Decrypting package using \"" + keyToUse + "\" key..."); bool success = file.Decrypt(); Console.WriteLine(success ? "Package decrypted successfully!" : "Error during decryption!"); if (!success) { return; } } if (encryptPackage) { if (file.IsEncrypted) { Console.WriteLine("Error: package already encrypted"); return; } string keyToUse = "ODK"; if (file.IsXvcFile) { var keyGuids = XvdFile.CikKeys.Keys.ToList(); if (encryptKeyId < 0 || encryptKeyId >= keyGuids.Count) { Console.WriteLine( "Error: invalid key index \"" + encryptKeyId + "\" specified, make sure the index you provided exists inside cik_keys.bin!"); return; } keyToUse = keyGuids[encryptKeyId].ToString(); } Console.WriteLine("Encrypting package using \"" + keyToUse + "\" key..."); bool success = file.Encrypt(encryptKeyId); Console.WriteLine(success ? "Package encrypted successfully!" : "Error during encryption!"); if (!success) { return; } } if (removeHashTree) { if (!file.IsDataIntegrityEnabled) { Console.WriteLine("Error: cannot remove hash tree from package that hasn't got a hash tree."); return; } Console.WriteLine("Attempting to remove hash tree from package..."); bool success = file.RemoveHashTree() && file.Save(); Console.WriteLine(success ? "Hash tree removed successfully and header updated." : "Error: hash tree is larger than input package (???)"); return; } if (addHashTree) { if (file.IsDataIntegrityEnabled) { Console.WriteLine("Error: cannot add hash tree to package that already has a hash tree."); return; } Console.WriteLine("Attempting to add hash tree to package..."); bool success = file.AddHashTree() && file.Save(); Console.WriteLine(success ? "Hash tree added successfully and header updated." : "Error: failed to extend package to make room for hash tree, is there enough disk space?"); if (!success) { return; } } if (rehashPackage) { if (!file.IsDataIntegrityEnabled) { Console.WriteLine("Error: cannot rehash package that hasn't got a hash tree."); return; } Console.WriteLine("Old top hash block hash: " + file.Header.TopHashBlockHash.ToHexString()); Console.WriteLine("Rehashing package..."); int[] fixedHashes = file.VerifyDataHashTree(true); bool success = file.CalculateHashTree(); if (success) { Console.WriteLine("New top hash block hash: " + file.Header.TopHashBlockHash.ToHexString()); file.Save(); } Console.WriteLine(success ? "Successfully rehashed " + fixedHashes.Length + " invalid data hashes inside package." : "Error: there was a problem rehashing the package."); if (!success) { return; } } if (resignPackage) { if (!XvdFile.SignKeyLoaded) { Console.WriteLine("Error: rsa3_key.bin file was not found, unable to resign package without it."); return; } bool success = file.Header.ResignWithSignKey(); Console.WriteLine(success ? "Successfully resigned package." : "Error: there was a problem resigning the package."); if (!success) { return; } } if (!String.IsNullOrEmpty(exvdDest)) { byte[] exvd = file.ExtractEmbeddedXvd(); if (exvd == null || exvd.Length <= 0) { Console.WriteLine("Error: no embedded XVD to extract."); return; } try { File.WriteAllBytes(exvdDest, exvd); Console.WriteLine( "Extracted embedded XVD to \"" + exvdDest + "\" successfully (0x{0:X} bytes)", exvd.Length); } catch { Console.WriteLine("Error: failed to extract embedded XVD."); } } if (!String.IsNullOrEmpty(userDataDest)) { byte[] userData = file.ExtractUserData(); if (userData == null || userData.Length <= 0) { Console.WriteLine("Error: no user data to extract."); return; } try { File.WriteAllBytes(userDataDest, userData); Console.WriteLine( "Extracted XVD user data to \"" + userDataDest + "\" successfully (0x{0:X} bytes)", userData.Length); } catch { Console.WriteLine("Error: failed to extract XVD user data."); } } if (!String.IsNullOrEmpty(vhdDest)) { if (!file.IsEncrypted) { Console.WriteLine("Extracting XVD filesystem to VHD file \"" + vhdDest + "\"..."); bool success = file.ConvertToVhd(vhdDest); Console.WriteLine(success ? "Wrote VHD successfully." : "Error: there was a problem extracting the filesystem from the XVD."); if (!success) { return; } } else { Console.WriteLine("Error: can't convert encrypted package to VHD."); } } file.Dispose(); } }