Esempio n. 1
1
        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));
        }
Esempio n. 2
0
        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));
        }
Esempio n. 3
0
 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);
     }
 }
Esempio n. 4
0
 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);
     }
 }
Esempio n. 5
0
        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));
        }
Esempio n. 6
0
        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);
            }
        }
Esempio n. 7
0
        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;
        }
Esempio n. 8
0
        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));
        }
Esempio n. 9
0
        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));
        }
Esempio n. 10
0
        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();
        }
Esempio n. 11
0
        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();
            }
        }