/// <summary> /// Initialize the common information about a Burn engine. /// </summary> /// <param name="reader">Binary reader open against a Burn engine.</param> /// <returns>True if initialized.</returns> protected bool Initialize(BinaryReader reader) { if (!this.GetWixburnSectionInfo(reader)) { return(false); } reader.BaseStream.Seek(this.wixburnDataOffset, SeekOrigin.Begin); byte[] bytes = reader.ReadBytes((int)BURN_SECTION_SIZE); UInt32 uint32 = 0; uint32 = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_MAGIC); if (BURN_SECTION_MAGIC != uint32) { this.messaging.Write(ErrorMessages.InvalidBundle(this.fileExe)); return(false); } this.Version = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_VERSION); if (BURN_SECTION_VERSION != this.Version) { this.messaging.Write(ErrorMessages.BundleTooNew(this.fileExe, this.Version)); return(false); } uint32 = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_FORMAT); // We only know how to deal with CABs right now if (1 != uint32) { this.messaging.Write(ErrorMessages.InvalidBundle(this.fileExe)); return(false); } this.StubSize = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_STUBSIZE); this.OriginalChecksum = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_ORIGINALCHECKSUM); this.OriginalSignatureOffset = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_ORIGINALSIGNATUREOFFSET); this.OriginalSignatureSize = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_ORIGINALSIGNATURESIZE); this.ContainerCount = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_COUNT); this.UXAddress = this.StubSize; this.UXSize = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_UXSIZE); // If there is an original signature use that to determine the engine size. if (0 < this.OriginalSignatureOffset) { this.EngineSize = this.OriginalSignatureOffset + this.OriginalSignatureSize; } else if (0 < this.SignatureOffset && 2 > this.ContainerCount) // if there is a signature and no attached containers, use the current signature. { this.EngineSize = this.SignatureOffset + this.SignatureSize; } else // just use the stub and UX container as the size of the engine. { this.EngineSize = this.StubSize + this.UXSize; } this.AttachedContainerAddress = this.ContainerCount > 1 ? this.EngineSize : 0; this.AttachedContainerSize = this.ContainerCount > 1 ? BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_ATTACHEDCONTAINERSIZE) : 0; return(true); }
/// <summary> /// Checks for a valid DOS header in the current exe. /// </summary> /// <returns>true if the exe starts with a DOS stub; false otherwise</returns> private bool EnsureDosHeader(BinaryReader reader) { if (UInt32.MaxValue == this.peOffset) { byte[] bytes = reader.ReadBytes((int)IMAGE_DOS_HEADER_SIZE); // Verify the DOS 'MZ' signature. if (IMAGE_DOS_SIGNATURE != BurnCommon.ReadUInt16(bytes, IMAGE_DOS_HEADER_OFFSET_MAGIC)) { this.messaging.Write(ErrorMessages.InvalidStubExe(this.fileExe)); return(false); } this.peOffset = BurnCommon.ReadUInt32(bytes, IMAGE_DOS_HEADER_OFFSET_NTHEADER); } return(true); }
/// <summary> /// Appends a container to the exe and updates the ".wixburn" section data to point to it. /// </summary> /// <param name="containerStream">File stream to append to the current exe.</param> /// <param name="containerSize">Size of the container.</param> /// <param name="burnSectionOffsetSize">Offset of size field for this container in ".wixburn" section data.</param> /// <param name="burnSectionCount">Number of Burn sections.</param> /// <returns>true if the container data is successfully appended; false otherwise</returns> private bool AppendContainer(Stream containerStream, UInt32 containerSize, UInt32 burnSectionOffsetSize, UInt32 burnSectionCount) { if (this.invalidBundle) { return(false); } // Update the ".wixburn" section data this.WriteToBurnSectionOffset(BURN_SECTION_OFFSET_COUNT, burnSectionCount); this.WriteToBurnSectionOffset(burnSectionOffsetSize, containerSize); // Append the container to the end of the existing bits. this.binaryWriter.BaseStream.Seek(0, SeekOrigin.End); BurnCommon.CopyStream(containerStream, this.binaryWriter.BaseStream, (int)containerSize); this.binaryWriter.BaseStream.Flush(); return(true); }
/// <summary> /// Finds the ".wixburn" section in the current exe. /// </summary> /// <returns>true if the ".wixburn" section is successfully found; false otherwise</returns> private bool GetWixburnSectionInfo(BinaryReader reader) { if (UInt32.MaxValue == this.wixburnDataOffset) { if (!this.EnsureNTHeader(reader)) { return(false); } UInt32 wixburnSectionOffset = UInt32.MaxValue; byte[] bytes = new byte[IMAGE_SECTION_HEADER_SIZE]; reader.BaseStream.Seek(this.firstSectionOffset, SeekOrigin.Begin); for (UInt16 sectionIndex = 0; sectionIndex < this.sections; ++sectionIndex) { reader.Read(bytes, 0, bytes.Length); if (IMAGE_SECTION_WIXBURN_NAME == BurnCommon.ReadUInt64(bytes, IMAGE_SECTION_HEADER_OFFSET_NAME)) { wixburnSectionOffset = this.firstSectionOffset + (IMAGE_SECTION_HEADER_SIZE * sectionIndex); break; } } if (UInt32.MaxValue == wixburnSectionOffset) { this.messaging.Write(ErrorMessages.StubMissingWixburnSection(this.fileExe)); return(false); } // we need 56 bytes for the manifest header, which is always going to fit in // the smallest alignment (512 bytes), but just to be paranoid... if (BURN_SECTION_SIZE > BurnCommon.ReadUInt32(bytes, IMAGE_SECTION_HEADER_OFFSET_SIZEOFRAWDATA)) { this.messaging.Write(ErrorMessages.StubWixburnSectionTooSmall(this.fileExe)); return(false); } this.wixburnDataOffset = BurnCommon.ReadUInt32(bytes, IMAGE_SECTION_HEADER_OFFSET_POINTERTORAWDATA); } return(true); }
/// <summary> /// Gets the attached container from the exe and extracts its contents to the output directory. /// </summary> /// <param name="outputDirectory">Directory to write extracted files to.</param> /// <returns>True if successful, false otherwise</returns> public bool ExtractAttachedContainer(string outputDirectory, string tempDirectory) { // No attached container to extract if (this.AttachedContainerAddress == 0 || this.AttachedContainerSize == 0) { return(false); } if (this.invalidBundle) { return(false); } Directory.CreateDirectory(outputDirectory); string tempCabPath = Path.Combine(tempDirectory, "attached.cab"); this.binaryReader.BaseStream.Seek(this.AttachedContainerAddress, SeekOrigin.Begin); using (Stream tempCab = File.Open(tempCabPath, FileMode.Create, FileAccess.Write)) { BurnCommon.CopyStream(this.binaryReader.BaseStream, tempCab, (int)this.AttachedContainerSize); } var cabinet = new Cabinet(tempCabPath); cabinet.Extract(outputDirectory); foreach (DictionaryEntry entry in this.attachedContainerPayloadNames) { string sourcePath = Path.Combine(outputDirectory, (string)entry.Key); string destinationPath = Path.Combine(outputDirectory, (string)entry.Value); Directory.CreateDirectory(Path.GetDirectoryName(destinationPath)); File.Delete(destinationPath); File.Move(sourcePath, destinationPath); } return(true); }
/// <summary> /// Checks for a valid Windows PE signature (IMAGE_NT_SIGNATURE) in the current exe. /// </summary> /// <returns>true if the exe is a Windows executable; false otherwise</returns> private bool EnsureNTHeader(BinaryReader reader) { if (UInt32.MaxValue == this.firstSectionOffset) { if (!this.EnsureDosHeader(reader)) { return(false); } reader.BaseStream.Seek(this.peOffset, SeekOrigin.Begin); byte[] bytes = reader.ReadBytes((int)IMAGE_NT_HEADER_SIZE); // Verify the NT signature... if (IMAGE_NT_SIGNATURE != BurnCommon.ReadUInt32(bytes, IMAGE_NT_HEADER_OFFSET_SIGNATURE)) { this.messaging.Write(ErrorMessages.InvalidStubExe(this.fileExe)); return(false); } ushort sizeOptionalHeader = BurnCommon.ReadUInt16(bytes, IMAGE_NT_HEADER_OFFSET_SIZEOFOPTIONALHEADER); this.sections = BurnCommon.ReadUInt16(bytes, IMAGE_NT_HEADER_OFFSET_NUMBEROFSECTIONS); this.firstSectionOffset = this.peOffset + IMAGE_NT_HEADER_SIZE + sizeOptionalHeader; this.checksumOffset = this.peOffset + IMAGE_NT_HEADER_SIZE + IMAGE_OPTIONAL_OFFSET_CHECKSUM; this.certificateTableSignatureOffset = this.peOffset + IMAGE_NT_HEADER_SIZE + sizeOptionalHeader - IMAGE_OPTIONAL_NEGATIVE_OFFSET_CERTIFICATETABLE; this.certificateTableSignatureSize = this.certificateTableSignatureOffset + 4; // size is in the DWORD after the offset. bytes = reader.ReadBytes(sizeOptionalHeader); this.Checksum = BurnCommon.ReadUInt32(bytes, IMAGE_OPTIONAL_OFFSET_CHECKSUM); this.SignatureOffset = BurnCommon.ReadUInt32(bytes, sizeOptionalHeader - IMAGE_OPTIONAL_NEGATIVE_OFFSET_CERTIFICATETABLE); this.SignatureSize = BurnCommon.ReadUInt32(bytes, sizeOptionalHeader - IMAGE_OPTIONAL_NEGATIVE_OFFSET_CERTIFICATETABLE + 4); } return(true); }
/// <summary> /// Reads a UInt64 value in little-endian format from an offset in an array of bytes. /// </summary> /// <param name="bytes">Array from which to read.</param> /// <param name="offset">Beginning offset from which to read.</param> /// <returns>value at offset</returns> internal static UInt64 ReadUInt64(byte[] bytes, UInt32 offset) { Debug.Assert(offset + 8 <= bytes.Length); return(BurnCommon.ReadUInt32(bytes, offset) + ((UInt64)BurnCommon.ReadUInt32(bytes, offset + 4) << 32)); }
/// <summary> /// Reads a UInt32 value in little-endian format from an offset in an array of bytes. /// </summary> /// <param name="bytes">Array from which to read.</param> /// <param name="offset">Beginning offset from which to read.</param> /// <returns>value at offset</returns> internal static UInt32 ReadUInt32(byte[] bytes, UInt32 offset) { Debug.Assert(offset + 4 <= bytes.Length); return(BurnCommon.ReadUInt16(bytes, offset) + ((UInt32)BurnCommon.ReadUInt16(bytes, offset + 2) << 16)); }
/// <summary> /// Gets the UX container from the exe and extracts its contents to the output directory. /// </summary> /// <param name="outputDirectory">Directory to write extracted files to.</param> /// <returns>True if successful, false otherwise</returns> public bool ExtractUXContainer(string outputDirectory, string tempDirectory) { // No UX container to extract if (this.UXAddress == 0 || this.UXSize == 0) { return(false); } if (this.invalidBundle) { return(false); } Directory.CreateDirectory(outputDirectory); string tempCabPath = Path.Combine(tempDirectory, "ux.cab"); string manifestOriginalPath = Path.Combine(outputDirectory, "0"); string manifestPath = Path.Combine(outputDirectory, "manifest.xml"); this.binaryReader.BaseStream.Seek(this.UXAddress, SeekOrigin.Begin); using (Stream tempCab = File.Open(tempCabPath, FileMode.Create, FileAccess.Write)) { BurnCommon.CopyStream(this.binaryReader.BaseStream, tempCab, (int)this.UXSize); } var cabinet = new Cabinet(tempCabPath); cabinet.Extract(outputDirectory); Directory.CreateDirectory(Path.GetDirectoryName(manifestPath)); File.Delete(manifestPath); File.Move(manifestOriginalPath, manifestPath); XmlDocument document = new XmlDocument(); document.Load(manifestPath); XmlNamespaceManager namespaceManager = new XmlNamespaceManager(document.NameTable); namespaceManager.AddNamespace("burn", BurnCommon.BurnNamespace); XmlNodeList uxPayloads = document.SelectNodes("/burn:BurnManifest/burn:UX/burn:Payload", namespaceManager); XmlNodeList payloads = document.SelectNodes("/burn:BurnManifest/burn:Payload", namespaceManager); foreach (XmlNode uxPayload in uxPayloads) { XmlNode sourcePathNode = uxPayload.Attributes.GetNamedItem("SourcePath"); XmlNode filePathNode = uxPayload.Attributes.GetNamedItem("FilePath"); string sourcePath = Path.Combine(outputDirectory, sourcePathNode.Value); string destinationPath = Path.Combine(outputDirectory, filePathNode.Value); Directory.CreateDirectory(Path.GetDirectoryName(destinationPath)); File.Delete(destinationPath); File.Move(sourcePath, destinationPath); } foreach (XmlNode payload in payloads) { XmlNode sourcePathNode = payload.Attributes.GetNamedItem("SourcePath"); XmlNode filePathNode = payload.Attributes.GetNamedItem("FilePath"); XmlNode packagingNode = payload.Attributes.GetNamedItem("Packaging"); string sourcePath = sourcePathNode.Value; string destinationPath = filePathNode.Value; string packaging = packagingNode.Value; if (packaging.Equals("embedded", StringComparison.OrdinalIgnoreCase)) { this.attachedContainerPayloadNames.Add(new DictionaryEntry(sourcePath, destinationPath)); } } return(true); }