Example #1
0
        private static long GetOffsetOfRefAt(BinaryReader reader, ref BinaryPlistTrailer trailer)
        {
            // *trailer contents are trusted, even for overflows -- was checked when the trailer was parsed;
            // this pointer arithmetic and the multiplication was also already done once and checked,
            // and the offsetTable was already validated.
            long pos = reader.BaseStream.Position;
            long objectsFirstBytePos = 8;

            if (pos < objectsFirstBytePos || trailer.offsetTableOffset - trailer.objectRefSize < pos)
            {
                throw new IndexOutOfRangeException("Object offset is out of stream");
            }
            var  bytes  = reader.ReadBEBytes(trailer.objectRefSize);
            long refnum = bytes.ToInt64();

            if (trailer.numObjects <= refnum)
            {
                throw new IndexOutOfRangeException("Object number is out of range");
            }

            pos = reader.BaseStream.Position;
            long offPos = trailer.offsetTableOffset + refnum * trailer.offsetIntSize;

            reader.BaseStream.Seek(offPos, SeekOrigin.Begin);
            bytes = reader.ReadBEBytes(trailer.offsetIntSize);
            long off = bytes.ToInt64();

            reader.BaseStream.Seek(pos, SeekOrigin.Begin);
            return(off);
        }
Example #2
0
        /// <summary>
        /// Write a property list to a stream, in binary format
        /// </summary>
        /// <param name="plist">plist is the property list to write (one of the basic property list types)</param>
        /// <param name="stream">stream is the destination of the property list</param>
        /// <returns></returns>
        public static long BinaryPlistWrite(object plist, Stream stream)
        {
            if (stream == null)
            {
                throw new ArgumentNullException("stream");
            }
            var objlist   = new List <object>();
            var objtable  = new Dictionary <object, int>(new DataComparer());
            var pathtable = new Dictionary <int, string>();

            FlattenPlist(plist, objlist, objtable, pathtable, "/plist[0.0]");

            int cnt     = objlist.Count;
            var offsets = new long[cnt];

            var writer = new BinaryWriter(stream);

            writer.Write(Encoding.ASCII.GetBytes("bplist00"));             // header

            BinaryPlistTrailer trailer = new BinaryPlistTrailer
            {
                sortVersion   = 0,
                numObjects    = cnt,
                topObject     = 0,             // true for this implementation
                objectRefSize = (byte)((long)cnt).BytesCount()
            };

            for (int idx = 0; idx < cnt; idx++)
            {
                offsets[idx] = stream.Position;
                object obj  = objlist[idx];
                string path = "/plist[0.0]";
                int    refnum;
                if (objtable.TryGetValue(obj, out refnum))
                {
                    path = pathtable[refnum];
                }
                else if (idx > 0)
                {
                    path = String.Format("/plist[{0}]", idx);
                }
                writer.WriteObject(obj, path, objtable, trailer.objectRefSize);
            }

            trailer.offsetTableOffset = stream.Position;
            trailer.offsetIntSize     = (byte)trailer.offsetTableOffset.BytesCount();

            for (int idx = 0; idx < cnt; idx++)
            {
                long swapped = offsets[idx];
                writer.WriteBE(swapped.ToByteArray(trailer.offsetIntSize));
            }

            writer.WriteTrailer(trailer);
            return(stream.Position);
        }
Example #3
0
        internal static object ReadObject(this BinaryReader reader, string path)
        {
            var trailer = new BinaryPlistTrailer
            {
                offsetIntSize = 0,
                objectRefSize = 0
            };

            return(reader.ReadObject(ref trailer, null, path));
        }
Example #4
0
 internal static void WriteTrailer(this BinaryWriter writer, BinaryPlistTrailer trailer)
 {
     try
     {
         writer.Write(new byte[] { 0, 0, 0, 0, 0 });                 //unused[5]
         writer.Write(trailer.sortVersion);
         writer.Write(trailer.offsetIntSize);
         writer.Write(trailer.objectRefSize);
         writer.WriteBE(BitConverter.GetBytes(trailer.numObjects));
         writer.WriteBE(BitConverter.GetBytes(trailer.topObject));
         writer.WriteBE(BitConverter.GetBytes(trailer.offsetTableOffset));
     }
     catch (Exception ex)
     {
         throw new BinWriterException("trailer", typeof(BinaryPlistTrailer), ex);
     }
 }
Example #5
0
 internal static BinaryPlistTrailer ReadTrailer(this BinaryReader reader)
 {
     try
     {
         var trailer = new BinaryPlistTrailer();
         // In Leopard, the unused bytes in the trailer must be 0 or the parse will fail
         // This check is not present in Tiger and earlier or after Leopard
         reader.ReadBytes(5);                 //unused[5]
         trailer.sortVersion       = reader.ReadByte();
         trailer.offsetIntSize     = reader.ReadByte();
         trailer.objectRefSize     = reader.ReadByte();
         trailer.numObjects        = BitConverter.ToInt64(reader.ReadBEBytes(sizeof(Int64)), 0);
         trailer.topObject         = BitConverter.ToInt64(reader.ReadBEBytes(sizeof(Int64)), 0);
         trailer.offsetTableOffset = BitConverter.ToInt64(reader.ReadBEBytes(sizeof(Int64)), 0);
         return(trailer);
     }
     catch (Exception ex)
     {
         throw new BinReaderException("trailer", BinaryPlistMarker.Data, ex);
     }
 }
Example #6
0
        private static object BinaryPlistCreateObject(Stream stream, ref BinaryPlistTrailer trailer, string version)
        {
            var reader = new BinaryReader(stream);

            return(reader.ReadObject(ref trailer, new Dictionary <long, object>(), String.Format("/plist[{0}]", version)));
        }
Example #7
0
        private static bool BinaryPlistGetTopLevelInfo(Stream stream, out long offset, out BinaryPlistTrailer trailer, out string version)
        {
            if (stream == null)
            {
                throw new ArgumentNullException("stream");
            }
            if (stream.Length < BinaryPlistTrailer.Size + 8 + 1)
            {
                throw new ArgumentOutOfRangeException("stream");
            }
            // Tiger and earlier will parse "bplist00"
            // Leopard will parse "bplist00" or "bplist01"
            // SnowLeopard will parse "bplist0?" where ? is any one character
            var reader = new BinaryReader(stream);

            stream.Seek(0, SeekOrigin.Begin);
            string header = Encoding.ASCII.GetString(reader.ReadBytes(8));

            if (!header.StartsWith("bplist"))
            {
                throw new NotSupportedException("Invalid header");
            }
            version = String.Format("{0}.{1}", header[6], header[7]);
            if (header[6] != '0')
            {
                offset  = 0;
                trailer = new BinaryPlistTrailer();
                return(false);
            }
            stream.Seek(-BinaryPlistTrailer.Size, SeekOrigin.End);
            trailer = reader.ReadTrailer();

            // Don't overflow on the number of objects or offset of the table
            // The trailer must point to a value before itself in the data.
            if (stream.Length < trailer.numObjects ||
                stream.Length < trailer.offsetTableOffset ||
                stream.Length - BinaryPlistTrailer.Size <= trailer.offsetTableOffset)
            {
                throw new IndexOutOfRangeException("Trailer is out of stream");
            }

            //  Must be a minimum of 1 object
            if (trailer.numObjects < 1)
            {
                throw new InvalidDataException("Objects not found");
            }

            // The ref to the top object must be a value in the range of 1 to the total number of objects
            if (trailer.numObjects <= trailer.topObject)
            {
                throw new IndexOutOfRangeException("Invalid top object ref");
            }

            // The offset table must be after at least 9 bytes of other data ('bplist??' + 1 byte of object table data).
            if (trailer.offsetTableOffset < 9)
            {
                throw new InvalidDataException("Invalid offset table offset");
            }

            // Minimum of 1 byte for the size of integers and references in the data
            if (trailer.offsetIntSize < 1)
            {
                throw new InvalidDataException("Invalid size of integers");
            }
            if (trailer.objectRefSize < 1)
            {
                throw new InvalidDataException("Invalid size of references");
            }

            // The total size of the offset table (number of objects * size of each int in the table) must not overflow
            long offsetIntSize   = trailer.offsetIntSize;
            long offsetTableSize = trailer.numObjects * offsetIntSize;

            if (stream.Length < offsetTableSize)
            {
                throw new IndexOutOfRangeException("Offset table is out of stream");
            }

            // The offset table must have at least 1 entry
            if (offsetTableSize < 1)
            {
                throw new InvalidDataException("Offsets not found");
            }

            // Make sure the size of the offset table and data sections do not overflow
            long objectDataSize = trailer.offsetTableOffset - 8;
            long tmpSum         = 8 + objectDataSize + offsetTableSize + BinaryPlistTrailer.Size;

            // The total size of the data should be equal to the sum of offsetTableOffset + sizeof(trailer)
            if (stream.Length != tmpSum)
            {
                throw new InvalidDataException("Invalid total data size");
            }

            // The object refs must be the right size to point into the offset table. That is, if the count of objects is 260, but only 1 byte is used to store references (max value 255), something is wrong.
            if (trailer.objectRefSize < 8 && (1L << (8 * trailer.objectRefSize)) <= trailer.numObjects)
            {
                throw new InvalidDataException("Invalid object refs size");
            }

            // The integers used for pointers in the offset table must be able to reach as far as the start of the offset table.
            if (trailer.offsetIntSize < 8 && (1L << (8 * trailer.offsetIntSize)) <= trailer.offsetTableOffset)
            {
                throw new InvalidDataException("Invalid offset table size");
            }

            long offsetsFirstBytePos = trailer.offsetTableOffset;
            long offsetsLastBytePos  = offsetsFirstBytePos + offsetTableSize - 1;

            stream.Seek(trailer.offsetTableOffset, SeekOrigin.Begin);
            long maxOffset = trailer.offsetTableOffset - 1;

            for (int idx = 0; idx < trailer.numObjects; idx++)
            {
                long off = reader.ReadBEBytes(trailer.offsetIntSize).ToInt64();
                if (maxOffset < off)
                {
                    throw new IndexOutOfRangeException("Invalid offset #" + idx);
                }
            }

            stream.Seek(trailer.offsetTableOffset + trailer.topObject * trailer.offsetIntSize, SeekOrigin.Begin);
            offset = reader.ReadBEBytes(trailer.offsetIntSize).ToInt64();
            if (offset < 8 || trailer.offsetTableOffset <= offset)
            {
                throw new IndexOutOfRangeException("Invalid top object offset");
            }

            stream.Seek(offset, SeekOrigin.Begin);
            return(true);
        }
Example #8
0
        internal static object ReadObject(this BinaryReader reader, ref BinaryPlistTrailer trailer, IDictionary <long, object> objects, string path)
        {
            bool   v15 = trailer.offsetIntSize == 0 || trailer.objectRefSize == 0;
            object plist;
            long   startOffset = reader.BaseStream.Position;

            if (objects != null && objects.TryGetValue(startOffset, out plist))
            {
                return(plist);
            }

            byte marker = reader.ReadByte();
            var  type   = (BinaryPlistMarker)(marker & 0xf0);

            try
            {
                switch (type)
                {
                case BinaryPlistMarker.Null:
                    type = (BinaryPlistMarker)marker;
                    switch (type)
                    {
                    case BinaryPlistMarker.Null:
                        return(null);

                    case BinaryPlistMarker.False:
                        return(false);

                    case BinaryPlistMarker.True:
                        return(true);

                    case BinaryPlistMarker.URL:
                        if (v15)
                        {
                            return(reader.ReadURL());
                        }
                        break;

                    case BinaryPlistMarker.BaseURL:
                        if (v15)
                        {
                            return(reader.ReadBaseURL());
                        }
                        break;

                    case BinaryPlistMarker.UUID:
                        if (v15)
                        {
                            return(reader.ReadUUID());
                        }
                        break;

                    case BinaryPlistMarker.Fill:
                        break;
                    }
                    throw new InvalidDataException("Unknown marker " + marker);

                case BinaryPlistMarker.Int:
                    if (marker == ((byte)BinaryPlistMarker.Int | 4))
                    {
                        plist = reader.ReadBEInt128();
                    }
                    else
                    {
                        plist = reader.ReadBEInt(marker);
                    }
                    // these are always immutable
                    if (objects != null)
                    {
                        objects[startOffset] = plist;
                    }
                    return(plist);

                case BinaryPlistMarker.Real:
                    switch (marker & 0x0f)
                    {
                    case 2:
                        plist = reader.ReadBEFloat();
                        break;

                    case 3:
                        plist = reader.ReadBEDouble();
                        break;

                    default:
                        throw new InvalidDataException("Unknown marker " + marker);
                    }

                    // these are always immutable
                    if (objects != null)
                    {
                        objects[startOffset] = plist;
                    }
                    return(plist);

                case (BinaryPlistMarker)((int)BinaryPlistMarker.Date & 0xf0):
                    type = (BinaryPlistMarker)marker;
                    if (type != BinaryPlistMarker.Date)
                    {
                        throw new InvalidDataException("Unknown marker " + marker);
                    }
                    plist = reader.ReadBEDateTime();
                    // these are always immutable
                    if (objects != null)
                    {
                        objects[startOffset] = plist;
                    }
                    return(plist);

                case BinaryPlistMarker.Data:
                    plist = reader.ReadData(marker);
                    if (objects != null)                     //&& mutabilityOption != kCFPropertyListMutableContainersAndLeaves
                    {
                        objects[startOffset] = plist;
                    }
                    return(plist);

                case BinaryPlistMarker.ASCIIString:
                    plist = reader.ReadASCIIString(marker);
                    if (objects != null)                     //&& mutabilityOption != kCFPropertyListMutableContainersAndLeaves
                    {
                        objects[startOffset] = plist;
                    }
                    return(plist);

                case BinaryPlistMarker.Unicode16String:
                    plist = reader.ReadUnicodeString(marker);
                    if (objects != null)                     //&& mutabilityOption != kCFPropertyListMutableContainersAndLeaves
                    {
                        objects[startOffset] = plist;
                    }
                    return(plist);

                case BinaryPlistMarker.UID:
                    if (v15)
                    {
                        throw new InvalidDataException("Unknown marker " + marker);
                    }
                    plist = reader.ReadBEUID(marker);
                    // these are always immutable
                    if (objects != null)
                    {
                        objects[startOffset] = plist;
                    }
                    return(plist);

                case BinaryPlistMarker.Dict:
                    plist = reader.ReadBEDict(marker, ref trailer, objects, path);
                    if (objects != null)                     //&& mutabilityOption == kCFPropertyListImmutable
                    {
                        objects[startOffset] = plist;
                    }
                    return(plist);

                case BinaryPlistMarker.Array:
                    plist = reader.ReadBEArray(marker, ref trailer, objects, path);
                    if (objects != null)                     //&& mutabilityOption == kCFPropertyListImmutable
                    {
                        objects[startOffset] = plist;
                    }
                    return(plist);

                case BinaryPlistMarker.Set:
                    if (!v15)
                    {
                        throw new InvalidDataException("Unknown marker " + marker);
                    }
                    plist = reader.ReadBESet(marker, ref trailer, objects, path);
                    if (objects != null)                     //&& mutabilityOption == kCFPropertyListImmutable
                    {
                        objects[startOffset] = plist;
                    }
                    return(plist);

                default:
                    throw new InvalidDataException("Unknown marker " + marker);
                }
            }
            catch (BinReaderException)
            {
                throw;
            }
            catch (Exception ex)
            {
                throw new BinReaderException(path, type, ex);
            }
            finally
            {
                Trace(path);
            }
        }
Example #9
0
        internal static ISet <object> ReadBESet(this BinaryReader reader, byte marker, ref BinaryPlistTrailer trailer, IDictionary <long, object> objects, string path)
        {
            var values = ReadArray(reader, marker, ref trailer, objects, (list, idx) => String.Format("{0}/set[{1}]", path, idx), new HashSet <long>());

            return(new Set(values));
        }
Example #10
0
        internal static object[] ReadBEArray(this BinaryReader reader, byte marker, ref BinaryPlistTrailer trailer, IDictionary <long, object> objects, string path)
        {
            var values = ReadArray(reader, marker, ref trailer, objects, (list, idx) => String.Format("{0}/array[{1}]", path, idx));

            return(values);
        }
Example #11
0
        internal static IDictionary <string, object> ReadBEDict(this BinaryReader reader, byte marker, ref BinaryPlistTrailer trailer, IDictionary <long, object> objects, string path)
        {
            var keysValues = ReadArray(reader, marker, ref trailer, objects, (list, idx) =>
                                       idx * 2 >= list.Length ? String.Format("{0}/dict[{1}]", path, list[idx - list.Length / 2]) : String.Format("{0}/dict.key[{1}]", path, idx));

            return(new Dict(keysValues));
        }
Example #12
0
        private static object[] ReadArray(BinaryReader reader, byte marker, ref BinaryPlistTrailer trailer, IDictionary <long, object> objects, Func <object[], int, string> child, ISet <long> set = null)
        {
            bool v15 = trailer.offsetIntSize == 0 || trailer.objectRefSize == 0;
            int  cnt = marker & 0x0f;

            if (0xf == cnt)
            {
                cnt = (int)reader.ReadIntProperty();
            }
            bool isDict = (marker & 0xf0) == (int)BinaryPlistMarker.Dict;
            int  kcnt   = cnt;

            if (isDict)
            {
                cnt = cnt * 2;
            }
            var list = new object[cnt];

            for (int idx = 0; idx < cnt; idx++)
            {
                string path = child(list, idx);
                if (!v15)
                {
                    long startOffset;
                    try
                    {
                        startOffset = GetOffsetOfRefAt(reader, ref trailer);
                        // databytes is trusted to be at least datalen bytes long
                        // *trailer contents are trusted, even for overflows -- was checked when the trailer was parsed
                        long objectsRangeStart = 8, objectsRangeEnd = trailer.offsetTableOffset - 1;
                        if (startOffset < objectsRangeStart || objectsRangeEnd < startOffset)
                        {
                            throw new IndexOutOfRangeException("Offset is out of stream");
                        }
                        // at any one invocation of this function, set should contain the offsets in the "path" down to this object
                        if (set != null)
                        {
                            if (set.Contains(startOffset))
                            {
                                throw new InvalidDataException("Not unique set");
                            }
                            set.Add(startOffset);
                        }
                    }
                    catch (Exception ex)
                    {
                        throw new BinReaderException(path, "ObjRef", ex);
                    }
                    finally
                    {
                        Trace(path + "#");
                    }

                    long pos = reader.BaseStream.Position;
                    reader.BaseStream.Seek(startOffset, SeekOrigin.Begin);
                    list[idx] = reader.ReadObject(ref trailer, objects, path);
                    reader.BaseStream.Seek(pos, SeekOrigin.Begin);
                }
                else
                {
                    list[idx] = reader.ReadObject(ref trailer, objects, path);
                }
            }
            return(list);
        }