Ejemplo n.º 1
0
        public ulong[] VerifyDataHashTree(bool rehash = false)
        {
            ulong dataBlockCount = XvdMath.OffsetToPageNumber((ulong)_io.Stream.Length - UserDataOffset);
            var   invalidBlocks  = new List <ulong>();

            for (ulong i = 0; i < dataBlockCount; i++)
            {
                var hashEntryOffset = CalculateHashEntryOffsetForBlock(i, 0);
                _io.Stream.Position = (long)hashEntryOffset;

                byte[] oldhash = _io.Reader.ReadBytes((int)DataHashEntryLength);

                var dataToHashOffset = XvdMath.PageNumberToOffset(i) + UserDataOffset;
                _io.Stream.Position = (long)dataToHashOffset;

                byte[] data = _io.Reader.ReadBytes((int)PAGE_SIZE);
                byte[] hash = HashUtils.ComputeSha256(data);
                Array.Resize(ref hash, (int)DataHashEntryLength);

                if (hash.IsEqualTo(oldhash))
                {
                    continue;
                }

                invalidBlocks.Add(i);
                if (!rehash)
                {
                    continue;
                }
                _io.Stream.Position = (long)hashEntryOffset;
                _io.Writer.Write(hash);
            }

            return(invalidBlocks.ToArray());
        }
Ejemplo n.º 2
0
        public bool VirtualToLogicalDriveOffset(ulong virtualOffset, out ulong logicalOffset)
        {
            logicalOffset = 0;

            if (virtualOffset >= Header.DriveSize)
            {
                throw new InvalidOperationException(
                          $"Virtual offset 0x{virtualOffset:X} is outside drivedata length 0x{Header.DriveSize:X}");
            }
            if (Header.Type > XvdType.Dynamic)
            {
                throw new NotSupportedException($"Xvd type {Header.Type} is unhandled");
            }


            if (Header.Type == XvdType.Dynamic)
            {
                var dataStartOffset  = virtualOffset + XvdMath.PageNumberToOffset(Header.NumberOfMetadataPages);
                var pageNumber       = XvdMath.OffsetToPageNumber(dataStartOffset);
                var inBlockOffset    = XvdMath.InBlockOffset(dataStartOffset);
                var firstDynamicPage = XvdMath.QueryFirstDynamicPage(Header.NumberOfMetadataPages);

                if (pageNumber >= firstDynamicPage)
                {
                    var   firstDynamicPageBytes = XvdMath.PageNumberToOffset(firstDynamicPage);
                    var   blockNumber           = XvdMath.OffsetToBlockNumber(dataStartOffset - firstDynamicPageBytes);
                    ulong allocatedBlock        = ReadBat(blockNumber);
                    if (allocatedBlock == INVALID_SECTOR)
                    {
                        return(false);
                    }

                    dataStartOffset = XvdMath.PageNumberToOffset(allocatedBlock) + inBlockOffset;
                    pageNumber      = XvdMath.OffsetToPageNumber(dataStartOffset);
                }

                var dataBackingBlockNum = XvdMath.ComputeDataBackingPageNumber(Header.Type,
                                                                               HashTreeLevels,
                                                                               HashTreePageCount,
                                                                               pageNumber);
                logicalOffset  = XvdMath.PageNumberToOffset(dataBackingBlockNum);
                logicalOffset += XvdMath.InPageOffset(dataStartOffset);
                logicalOffset += XvdMath.PageNumberToOffset(Header.EmbeddedXvdPageCount);
                logicalOffset += Header.MutableDataLength;
                logicalOffset += XVD_HEADER_INCL_SIGNATURE_SIZE;
                logicalOffset += PAGE_SIZE;
            }
            else
            { // Xvd type fixed
                logicalOffset  = virtualOffset;
                logicalOffset += XvdMath.PageNumberToOffset(Header.EmbeddedXvdPageCount);
                logicalOffset += Header.MutableDataLength;
                logicalOffset += XvdMath.PageNumberToOffset(Header.NumberOfMetadataPages);
                logicalOffset += XVD_HEADER_INCL_SIGNATURE_SIZE;
                logicalOffset += PAGE_SIZE;
            }

            return(true);
        }
Ejemplo n.º 3
0
        byte[] ReadDynamic(int count)
        {
            int positionInBuffer = 0;
            int bytesRemaining   = count;

            byte[] destBuffer = new byte[count];

            while (positionInBuffer < count)
            {
                byte[] data = new byte[0];
                if (Position < StaticDataLength)
                {
                    // Read a chunk from non-dynamic area, next iteration will read dynamic data
                    int maxReadLength = (int)(StaticDataLength - Position);
                    int length        = bytesRemaining > maxReadLength ? maxReadLength : bytesRemaining;
                    data = InternalRead(length);
                }
                else
                {
                    // Lookup block allocation table for real data offset
                    var   targetVirtualOffset = (ulong)(Position - StaticDataLength);
                    ulong blockNumber         = XvdMath.OffsetToBlockNumber(targetVirtualOffset);
                    long  inBlockOffset       = (long)XvdMath.InBlockOffset(targetVirtualOffset);
                    int   maxReadLength       = (int)(XvdFile.BLOCK_SIZE - inBlockOffset);
                    int   length = bytesRemaining > maxReadLength ? maxReadLength : bytesRemaining;

                    var targetPage = _xvdFile.ReadBat(blockNumber);
                    if (targetPage == XvdFile.INVALID_SECTOR)
                    {
                        data = new byte[length];
                        // Advance stream position cause we are not actually reading data
                        Position += length;
                    }
                    else
                    {
                        long targetPhysicalOffset = DynamicBaseOffset
                                                    + (long)XvdMath.PageNumberToOffset(targetPage)
                                                    + inBlockOffset;

                        data = InternalReadAbsolute(targetPhysicalOffset, length);
                    }
                }

                Array.Copy(data, 0, destBuffer, positionInBuffer, data.Length);
                positionInBuffer += data.Length;
                bytesRemaining   -= data.Length;
            }

            return(destBuffer);
        }
Ejemplo n.º 4
0
        public string ToString(bool formatted)
        {
            var b = new StringBuilder();

            b.AppendLine("XvcUpdateSegment");
            b.AppendLine();

            string fmt = formatted ? "    " : "";

            b.AppendLineSpace(fmt + $"PageNum: 0x{PageNum:X} (@ 0x{XvdMath.PageNumberToOffset(PageNum)})");
            b.AppendLineSpace(fmt + $"Hash: 0x{Hash:X}");

            return(b.ToString());
        }
Ejemplo n.º 5
0
 ulong CalculateStaticDataLength()
 {
     if (Header.Type == XvdType.Dynamic)
     {
         var smallestPage = GetAllBATEntries().Min();
         return(XvdMath.PageNumberToOffset(smallestPage)
                - XvdMath.PageNumberToOffset(Header.DynamicHeaderPageCount)
                - XvdMath.PageNumberToOffset(Header.XvcInfoPageCount));
     }
     else if (Header.Type == XvdType.Fixed)
     {
         return(0);
     }
     else
     {
         throw new InvalidProgramException("Unsupported XvdType");
     }
 }
Ejemplo n.º 6
0
        public ulong CalculateHashEntryOffsetForBlock(ulong blockNum, uint hashLevel)
        {
            var hashBlock = XvdMath.CalculateHashBlockNumForBlockNum(Header.Type, HashTreeLevels, Header.NumberOfHashedPages, blockNum, hashLevel, out var entryNum);

            return(HashTreeOffset + XvdMath.PageNumberToOffset(hashBlock) + (entryNum * HASH_ENTRY_LENGTH));
        }
Ejemplo n.º 7
0
        public string ToString(bool formatted)
        {
            var b = new StringBuilder();

            string fmt = formatted ? "    " : "";

            b.AppendLine("XvdMiscInfo:");
            b.AppendLineSpace(fmt + $"Page Count: 0x{Header.NumberOfHashedPages:X}");
            b.AppendLineSpace(fmt + $"Embedded XVD Offset: 0x{EmbeddedXvdOffset:X}");
            b.AppendLineSpace(fmt + $"MDU Offset: 0x{MduOffset:X}");
            b.AppendLineSpace(fmt + $"HashTree Offset: 0x{HashTreeOffset:X}");
            b.AppendLineSpace(fmt + $"User Data Offset: 0x{UserDataOffset:X}");
            b.AppendLineSpace(fmt + $"XVC Data Offset: 0x{XvcInfoOffset:X}");
            b.AppendLineSpace(fmt + $"Dynamic Header Offset: 0x{DynamicHeaderOffset:X}");
            b.AppendLineSpace(fmt + $"Drive Data Offset: 0x{DriveDataOffset:X}");

            if (IsDataIntegrityEnabled)
            {
                b.AppendLineSpace(fmt + $"Hash Tree Page Count: 0x{HashTreePageCount:X}");
                b.AppendLineSpace(fmt + $"Hash Tree Levels: 0x{HashTreeLevels:X}");
                b.AppendLineSpace(fmt + $"Hash Tree Valid: {HashTreeValid}");

                if (!DisableDataHashChecking)
                {
                    b.AppendLineSpace(fmt + $"Data Hash Tree Valid: {DataHashTreeValid}");
                }
            }

            if (IsXvcFile)
            {
                b.AppendLineSpace(fmt + $"XVC Data Hash Valid: {XvcDataHashValid}");
            }

            b.AppendLine();
            b.Append(Header.ToString(formatted));

            if (IsXvcFile && XvcInfo.ContentID != null)
            {
                b.AppendLine();
                bool xvcKeyFound = GetXvcKey(0, out var decryptKey);
                if (xvcKeyFound)
                {
                    b.AppendLine($"Decrypt key for xvc keyslot 0: {decryptKey.ToHexString()}");
                    b.AppendLine("(key is wrong though until the obfuscation/encryption on it is figured out)");
                    b.AppendLine();
                }

                b.AppendLine(XvcInfo.ToString(formatted));
            }

            if (RegionHeaders != null)
            {
                for (int i = 0; i < RegionHeaders.Count; i++)
                {
                    b.AppendLine();
                    string presenceInfo = "";
                    if (RegionPresenceInfo != null && RegionPresenceInfo.Count > i)
                    {
                        var presenceFlags = RegionPresenceInfo[i];
                        presenceInfo  = " (";
                        presenceInfo += (presenceFlags.HasFlag(XvcRegionPresenceInfo.IsPresent) ? "present" : "not present") + ", ";
                        presenceInfo += presenceFlags.HasFlag(XvcRegionPresenceInfo.IsAvailable) ? "available" : "unavailable";
                        if (((int)presenceFlags & 0xF0) != 0)
                        {
                            presenceInfo += $", on disc {(int)presenceFlags >> 4}";
                        }
                        presenceInfo += ")";
                    }
                    b.AppendLine($"Region {i}{presenceInfo}");
                    b.Append(RegionHeaders[i].ToString(formatted));
                }
            }

            if (UpdateSegments != null && UpdateSegments.Count > 0)
            {
                // have to add segments to a seperate List so we can store the index of them...
                var segments = new List <Tuple <int, XvcUpdateSegment> >();
                for (int i = 0; i < UpdateSegments.Count; i++)
                {
                    if (UpdateSegments[i].Hash == 0)
                    {
                        break;
                    }
                    segments.Add(Tuple.Create(i, UpdateSegments[i]));
                }

                b.AppendLine();
                b.AppendLine("Update Segments:");
                b.AppendLine();
                b.AppendLine(segments.ToStringTable(
                                 new[] { "Id", "PageNum (Offset)", "Hash" },
                                 a => a.Item1, a => $"0x{a.Item2.PageNum:X} (0x{XvdMath.PageNumberToOffset(a.Item2.PageNum):X})", a => $"0x{a.Item2.Hash:X}"));
            }


            if (RegionSpecifiers != null && RegionSpecifiers.Count > 0)
            {
                // have to add specifiers to a seperate List so we can store the index of them...
                var specs = new List <Tuple <int, XvcRegionSpecifier> >();
                for (int i = 0; i < RegionSpecifiers.Count; i++)
                {
                    specs.Add(Tuple.Create(i, RegionSpecifiers[i]));
                }

                b.AppendLine();
                b.AppendLine("Region Specifiers:");
                b.AppendLine();
                b.AppendLine(specs.ToStringTable(
                                 new[] { "Id", "RegionId", "Key", "Value" },
                                 a => a.Item1, a => $"0x{a.Item2.RegionId:X}", a => a.Item2.Key, a => a.Item2.Value));
            }

            if (!IsEncrypted)
            {
                b.AppendLine();
                try
                {
                    b.Append(Filesystem.ToString(formatted));
                }
                catch (Exception e)
                {
                    b.AppendLine($"Failed to get XvdFilesystem info, error: {e}");
                }
            }
            else
            {
                b.AppendLine($"Cannot get XvdFilesystem from encrypted package");
            }

            return(b.ToString());
        }