Beispiel #1
0
        /// <summary>This method acts as a factory. It recognizes the next chunk type
        /// and instanciate a sub class accordingly.</summary>
        /// <param name="buffer">Buffer to acquire data from.</param>
        /// <param name="offset">Offset within buffer to start reading. On return the
        /// offset is updated to reflect bytes consumption.</param>
        /// <param name="stringPool">An optional string pool that will be used to resolve
        /// some string reference. This parameter is required when handling an XML document
        /// content, it is optional for other contents.</param>
        /// <returns>An instance of the class that handle the chunk type.</returns>
        internal static ResourceChunkHeader Create(byte[] buffer, ref int offset,
            StringPool stringPool = null)
        {
            ChunkType chunkType = (ChunkType)Helpers.PeekUInt16(buffer, offset);

            switch (chunkType) {
                case ChunkType.Null:
                    return NullChunk.Create(buffer, ref offset);
                case ChunkType.StringPool:
                    return new StringPool(buffer, ref offset);
                case ChunkType.Table:
                    return new TableHeader(buffer, ref offset);
                case ChunkType.TablePackage:
                    return new Package(buffer, ref offset);
                case ChunkType.TableType:
                    if (null == stringPool) {
                        throw new ArgumentNullException("stringPool");
                    }
                    return new Type(buffer, ref offset, stringPool);
                case ChunkType.TableTypeSpec:
                    return new TypeSpecification(buffer, ref offset);
                case ChunkType.XmlElementEnd:
                case ChunkType.XmlElementStart:
                case ChunkType.XmlNamespaceEnd:
                case ChunkType.XmlNamespaceStart:
                    if (null == stringPool) {
                        throw new ArgumentNullException("stringPool");
                    }
                    return XmlTreeItem.Create(buffer, ref offset, chunkType, stringPool);
                case ChunkType.XmlResourceMap:
                    return new XmlResourceMap(buffer, ref offset);
                default:
                    throw new NotSupportedException("Unsupported chunk type : " + chunkType.ToString());
            }
        }
Beispiel #2
0
 internal XmlElementItem(byte[] buffer, ref int offset, StringPool stringPool,
     bool startElement)
     : base(buffer, ref offset, stringPool)
 {
     int baseOffset = offset;
     Namespace = stringPool.GetReferencedString(buffer, ref offset, true);
     Name = stringPool.GetReferencedString(buffer, ref offset, true);
     if (startElement) { base.MarkAsStarter(); }
     if (!startElement) { return; }
     // End elements don't have these values.
     ushort attributeStart = Helpers.ReadUInt16(buffer, ref offset);
     ushort attributeSize = Helpers.ReadUInt16(buffer, ref offset);
     ushort attributeCount = Helpers.ReadUInt16(buffer, ref offset);
     // The three next values are 1 based indexes in the attribute array. Their
     // value is 0 if the attribute doesn't exist.
     ushort idAttributeIndex = Helpers.ReadUInt16(buffer, ref offset);
     if (0 != idAttributeIndex) { int i = 1; }
     ushort classAttributeIndex = Helpers.ReadUInt16(buffer, ref offset);
     ushort styleAttributeIndex = Helpers.ReadUInt16(buffer, ref offset);
     // This computaion should not usually modify the current offset value.
     offset = baseOffset + attributeStart;
     List<XmlElementAttributeItem> attributes = new List<XmlElementAttributeItem>();
     for (int index = 0; index < attributeCount; index++) {
         attributes.Add(new XmlElementAttributeItem(buffer, ref offset, stringPool));
     }
     Attributes = attributes.ToArray();
     return;
 }
Beispiel #3
0
 protected XmlTreeItem(byte[] buffer, ref int offset, StringPool stringPool)
     : base(buffer, ref offset)
 {
     LineNumber = Helpers.ReadUInt32(buffer, ref offset);
     Comment = stringPool.GetReferencedString(buffer, ref offset, true);
     return;
 }
Beispiel #4
0
 internal Type(byte[] buffer, ref int offset, StringPool keyStrings)
     : base(buffer, ref offset)
 {
     int baseOffset = (int)(offset - ResourceChunkHeader.ChunkHeaderSize);
     Id = buffer[offset++];
     // Skip null bytes.
     offset += 3;
     // Number of uint32_t entry indices that follow.
     uint entryCount = Helpers.ReadUInt32(buffer, ref offset);
     // Offset from header where ResTable_entry data starts.
     uint entriesStart = Helpers.ReadUInt32(buffer, ref offset);
     Configuration = new ResourceTableConfiguration(buffer, ref offset);
     uint[] valuesOffset = new uint[(int)entryCount];
     for (int index = 0; index < entryCount; index++) {
         uint candidateOffset = Helpers.ReadUInt32(buffer, ref offset);
         valuesOffset[index] = (uint.MaxValue == candidateOffset)
             ? uint.MaxValue
             : (uint)(baseOffset + entriesStart + candidateOffset);
     }
     Resources = new Resource[entryCount];
     for(int index = 0; index < entryCount; index++) {
         if (uint.MaxValue == valuesOffset[index]) { continue; }
         offset = (int)valuesOffset[index];
         Resources[index] = new Resource(buffer, ref offset, keyStrings);
     }
     offset = (int)(baseOffset + base.Size);
     return;
 }
Beispiel #5
0
 internal XmlElementAttributeItem(byte[] buffer, ref int offset,
     StringPool stringPool)
 {
     Namespace = stringPool.GetReferencedString(buffer, ref offset, true);
     Name = stringPool.GetReferencedString(buffer, ref offset, true);
     RawValue = stringPool.GetReferencedString(buffer, ref offset, true);
     TypedValue = new ResourceValue(buffer, ref offset);
     return;
 }
Beispiel #6
0
 internal XmlNamespaceItem(byte[] buffer, ref int offset, StringPool stringPool,
     bool startElement)
     : base(buffer, ref offset, stringPool)
 {
     if (startElement) { base.MarkAsStarter(); }
     Prefix = stringPool.GetReferencedString(buffer, ref offset);
     Uri = stringPool.GetReferencedString(buffer, ref offset);
     return;
 }
Beispiel #7
0
 internal Resource(byte[] buffer, ref int offset, StringPool stringPool)
 {
     // Number of bytes in this structure.
     ushort size = Helpers.ReadUInt16(buffer, ref offset);
     ResourceFlags flags = (ResourceFlags)Helpers.ReadUInt16(buffer, ref offset);
     // Reference into ResTable_package::keyStrings identifying this entry.
     Name = stringPool.GetReferencedString(buffer, ref offset);
     Value = new ResourceValue(buffer, ref offset);
     return;
 }
Beispiel #8
0
        /// <summary>This method acts as a factory for sub-classes from the <see cref="XmlTreeItem"/>
        /// class.</summary>
        /// <param name="buffer">Buffer to get bytes from.</param>
        /// <param name="offset">Offset of the first unconsumed buffer byte. Will be updated on
        /// return to denote additional consumed bytes.</param>
        /// <param name="chunkType">The chunk type that has been detected.</param>
        /// <param name="stringPool">String pool from the compressed document.</param>
        /// <returns></returns>
        internal static XmlTreeItem Create(byte[] buffer, ref int offset, ChunkType chunkType,
            StringPool stringPool)
        {
            bool startElement = false;

            switch(chunkType) {
                case ChunkType.XmlNamespaceStart:
                    startElement = true;
                    goto case ChunkType.XmlNamespaceEnd;
                case ChunkType.XmlNamespaceEnd:
                    return new XmlNamespaceItem(buffer, ref offset, stringPool, startElement);
                case ChunkType.XmlElementStart:
                    startElement = true;
                    goto case ChunkType.XmlElementEnd;
                case ChunkType.XmlElementEnd:
                    return new XmlElementItem(buffer, ref offset, stringPool, startElement);
                default:
                    throw new ArgumentException();
            }
        }
Beispiel #9
0
 internal string GetStringRepresentation(StringPool stringPool, PackageResolverDelegate packageResolver)
 {
     switch (DataType)
     {
         case ResourceValueType.Boolean:
             return (0 == Data) ? "false" : "true";
         case ResourceValueType.Decimal:
             return Data.ToString();
         case ResourceValueType.Hexadecimal:
             return string.Format("0x{0}", Data);
         case ResourceValueType.Null:
             return null;
         case ResourceValueType.String:
             return stringPool[Data];
         case ResourceValueType.Reference:
             uint packageId = (Data & 0xFF000000) >> 24;
             uint index = (Data & 0x00FF0000) >> 16;
             uint entryIndex = Data & 0x0000FFFF;
             Package package = packageResolver(packageId);
             TypeSpecification specification = package.GetType((int)index);
             if (!specification.IsReferenceIndexValid(entryIndex)) {
                 throw new ApkFormatException();
             }
             // TODO : Should be more specific when several types exist under the
             // specification. Should filter relatively to a target configuration.
             Resource referencedResource = null;
             foreach (Type scannedType in specification.EnumerateTypes()) {
                 referencedResource = scannedType.Resources[(int)entryIndex];
                 if (null != referencedResource) { break; }
             }
             if (null == referencedResource) { throw new ApkFormatException(); }
             return referencedResource.Name;
         default:
             throw new CompressedFormatException(
                 "Resource value type not supported : " + DataType.ToString());
     }
 }
Beispiel #10
0
        internal Package(byte[] buffer, ref int offset)
            : base(buffer, ref offset)
        {
            int startOffset = offset - ResourceChunkHeader.ChunkHeaderSize;
            Id = Helpers.ReadUInt32(buffer, ref offset);
            byte[] nameBuffer = new byte[MaximumNameLength];
            int nameBytesCount = sizeof(ushort) * MaximumNameLength;
            int effectiveNameLength;
            for (effectiveNameLength = 0; effectiveNameLength < MaximumNameLength; effectiveNameLength++) {
                int unicodeCharOffset = sizeof(ushort) * effectiveNameLength;
                if ((0 == buffer[offset + unicodeCharOffset])
                    && (0 == buffer[offset + unicodeCharOffset + 1]))
                {
                    break;
                }
            }
            Name = UnicodeEncoding.Unicode.GetString(buffer, offset, effectiveNameLength * sizeof(ushort));
            offset += nameBytesCount;
            // Offset to a ResStringPool_header defining the resource
            // type symbol table.  If zero, this package is inheriting from
            // another base package (overriding specific values in it).
            uint typeStrings = Helpers.ReadUInt32(buffer, ref offset);
            // Last index into typeStrings that is for public use by others.
            uint lastPublicType = Helpers.ReadUInt32(buffer, ref offset);
            // Offset to a ResStringPool_header defining the resource
            // key symbol table.  If zero, this package is inheriting from
            // another base package (overriding specific values in it).
            uint keyStrings = Helpers.ReadUInt32(buffer, ref offset);
            // Last index into keyStrings that is for public use by others.
            uint lastPublicKey = Helpers.ReadUInt32(buffer, ref offset);

            // Go on with string pools
            offset = (int)(startOffset + typeStrings);
            ResourceChunkHeader chunk = ResourceChunkHeader.Create(buffer, ref offset);
            if (ChunkType.StringPool != chunk.Type) {
                throw new CompressedFormatException(
                    "Expecting a string pool, found a " + chunk.Type.ToString());
            }
            _typeNames = (StringPool)chunk;

            offset = (int)(startOffset + keyStrings);
            chunk = ResourceChunkHeader.Create(buffer, ref offset);
            if (ChunkType.StringPool != chunk.Type) {
                throw new CompressedFormatException(
                    "Expecting a string pool, found a " + chunk.Type.ToString());
            }
            _keyNames = (StringPool)chunk;
            int endOfPackageOffset = (int)(startOffset + base.Size);
            chunk = ResourceChunkHeader.Create(buffer, ref offset);
            _specifications = new List<TypeSpecification>();
            while (offset < endOfPackageOffset) {
                TypeSpecification specification = chunk as TypeSpecification;
                if (null == specification) {
                    throw new CompressedFormatException(
                        "Expecting a type specification, found a " + chunk.Type.ToString());
                }
                _specifications.Add(specification);
                while (offset < endOfPackageOffset) {
                    chunk = ResourceChunkHeader.Create(buffer, ref offset, _keyNames);
                    if (chunk is TypeSpecification) { break; }
                    specification.AddType((Type)chunk);
                }
            }
            return;
        }
Beispiel #11
0
        internal static XmlDocument GetDocument(FileStream from, PackageResolverDelegate packageResolver)
        {
            if (null == from) { throw new ArgumentNullException(); }
            if (!from.CanRead) { throw new ArgumentException("Readable stream required."); }
            if (0 != from.Position) { throw new ArgumentException("Ill positioned stream."); }
            byte[] buffer = new byte[(int)from.Length];

            if (buffer.Length != from.Read(buffer, 0, buffer.Length)) {
                throw new ApkFormatException();
            }
            int offset = 0;
            ResourceChunkHeader header = new ResourceChunkHeader(buffer, ref offset);
            if (ChunkType.Xml != header.Type) {
                throw new ApkFormatException("XML chunk was expected.");
            }
            XmlDocument result = new XmlDocument();
            XmlNamespaceManager namespaceManager = new XmlNamespaceManager(result.NameTable);
            StringPool stringPool = new StringPool(buffer, ref offset);
            ResourceChunkHeader scannedChunk = ResourceChunkHeader.Create(buffer, ref offset, stringPool);
            XmlResourceMap resourceMap = scannedChunk as XmlResourceMap;
            // The resource map is optional. Should we have found it we need to read one
            // more chunk.
            if (null != resourceMap) { scannedChunk = ResourceChunkHeader.Create(buffer, ref offset, stringPool); }
            XmlNamespaceItem namespaceItem = scannedChunk as XmlNamespaceItem;
            if ((null == namespaceItem) || !namespaceItem.Start) {
                throw new CompressedFormatException();
            }
            namespaceManager.AddNamespace(namespaceItem.Prefix, namespaceItem.Uri);
            Stack<XmlTreeItem> stackedItem = new Stack<XmlTreeItem>();
            stackedItem.Push(namespaceItem);
            XmlElementItem currentElementItem = null;
            XmlElement currentElement = null;
            while (offset < buffer.Length) {
                XmlTreeItem item = (XmlTreeItem)ResourceChunkHeader.Create(buffer, ref offset, stringPool);
                if (item.Start) {
                    stackedItem.Push(item);
                    namespaceItem = item as XmlNamespaceItem;
                    if (null != namespaceItem) {
                        namespaceManager.AddNamespace(namespaceItem.Prefix, namespaceItem.Uri);
                        continue;
                    }
                    currentElementItem = item as XmlElementItem;
                    if (null != currentElementItem) {
                        XmlElement newElement =
                            result.CreateElement(currentElementItem.Name, currentElementItem.Namespace);
                        if (null == currentElement) { result.AppendChild(newElement); }
                        else { currentElement.AppendChild(newElement); }
                        currentElement = newElement;
                        foreach (XmlElementItem.XmlElementAttributeItem scannedAttribute in
                            currentElementItem.Attributes)
                        {
                            XmlAttribute newAttribute =
                                result.CreateAttribute(scannedAttribute.Name, scannedAttribute.Namespace);
                            newAttribute.Value = scannedAttribute.GetStringRepresentation(stringPool, packageResolver);
                            currentElement.Attributes.Append(newAttribute);
                        }
                        continue;
                    }
                    throw new NotImplementedException();
                }
                else {
                    if (0 == stackedItem.Count) {
                        throw new CompressedFormatException("Unbalanced start/end XML elements");
                    }
                    XmlTreeItem poppedItem = stackedItem.Pop();
                    if (!item.StartEndMatch(poppedItem)) {
                        throw new CompressedFormatException("Start/end XML elements mismatch");
                    }
                    // Hem. Little bit loose.
                    if (null != currentElement) {
                        currentElement = (currentElement.ParentNode as XmlElement);
                    }
                }
                int i = 1;
            }
            return result;
        }
Beispiel #12
0
 internal string GetStringRepresentation(StringPool stringPool,
     PackageResolverDelegate packageResolver)
 {
     return RawValue ?? TypedValue.GetStringRepresentation(stringPool, packageResolver);
 }