diff options
Diffstat (limited to 'src/WixToolset.Core.Burn/Bundles/BurnCommon.cs')
-rw-r--r-- | src/WixToolset.Core.Burn/Bundles/BurnCommon.cs | 378 |
1 files changed, 378 insertions, 0 deletions
diff --git a/src/WixToolset.Core.Burn/Bundles/BurnCommon.cs b/src/WixToolset.Core.Burn/Bundles/BurnCommon.cs new file mode 100644 index 00000000..0baa6094 --- /dev/null +++ b/src/WixToolset.Core.Burn/Bundles/BurnCommon.cs | |||
@@ -0,0 +1,378 @@ | |||
1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. | ||
2 | |||
3 | namespace WixToolset.Core.Burn.Bundles | ||
4 | { | ||
5 | using System; | ||
6 | using System.Diagnostics; | ||
7 | using System.IO; | ||
8 | using WixToolset.Data; | ||
9 | |||
10 | /// <summary> | ||
11 | /// Common functionality for Burn PE Writer & Reader for the WiX toolset. | ||
12 | /// </summary> | ||
13 | /// <remarks>This class encapsulates common functionality related to | ||
14 | /// bundled/chained setup packages.</remarks> | ||
15 | /// <example> | ||
16 | /// </example> | ||
17 | internal abstract class BurnCommon : IDisposable | ||
18 | { | ||
19 | public const string BurnNamespace = "http://wixtoolset.org/schemas/v4/2008/Burn"; | ||
20 | public const string BurnUXContainerEmbeddedIdFormat = "u{0}"; | ||
21 | public const string BurnUXContainerPayloadIdFormat = "p{0}"; | ||
22 | public const string BurnAttachedContainerEmbeddedIdFormat = "a{0}"; | ||
23 | |||
24 | // See WinNT.h for details about the PE format, including the | ||
25 | // structure and offsets for IMAGE_DOS_HEADER, IMAGE_NT_HEADERS32, | ||
26 | // IMAGE_FILE_HEADER, etc. | ||
27 | protected const UInt32 IMAGE_DOS_HEADER_SIZE = 64; | ||
28 | protected const UInt32 IMAGE_DOS_HEADER_OFFSET_MAGIC = 0; | ||
29 | protected const UInt32 IMAGE_DOS_HEADER_OFFSET_NTHEADER = 60; | ||
30 | |||
31 | protected const UInt32 IMAGE_NT_HEADER_SIZE = 24; // signature DWORD (4) + IMAGE_FILE_HEADER (20) | ||
32 | protected const UInt32 IMAGE_NT_HEADER_OFFSET_SIGNATURE = 0; | ||
33 | protected const UInt32 IMAGE_NT_HEADER_OFFSET_NUMBEROFSECTIONS = 6; | ||
34 | protected const UInt32 IMAGE_NT_HEADER_OFFSET_SIZEOFOPTIONALHEADER = 20; | ||
35 | |||
36 | protected const UInt32 IMAGE_OPTIONAL_OFFSET_CHECKSUM = 4 * 16; // checksum is 16 DWORDs into IMAGE_OPTIONAL_HEADER which is right after the IMAGE_NT_HEADER. | ||
37 | protected const UInt32 IMAGE_OPTIONAL_NEGATIVE_OFFSET_CERTIFICATETABLE = (IMAGE_DATA_DIRECTORY_SIZE * (IMAGE_NUMBEROF_DIRECTORY_ENTRIES - IMAGE_DIRECTORY_ENTRY_SECURITY)); | ||
38 | |||
39 | protected const UInt32 IMAGE_SECTION_HEADER_SIZE = 40; | ||
40 | protected const UInt32 IMAGE_SECTION_HEADER_OFFSET_NAME = 0; | ||
41 | protected const UInt32 IMAGE_SECTION_HEADER_OFFSET_VIRTUALSIZE = 8; | ||
42 | protected const UInt32 IMAGE_SECTION_HEADER_OFFSET_SIZEOFRAWDATA = 16; | ||
43 | protected const UInt32 IMAGE_SECTION_HEADER_OFFSET_POINTERTORAWDATA = 20; | ||
44 | |||
45 | protected const UInt32 IMAGE_DATA_DIRECTORY_SIZE = 8; // struct of two DWORDs. | ||
46 | protected const UInt32 IMAGE_DIRECTORY_ENTRY_SECURITY = 4; | ||
47 | protected const UInt32 IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16; | ||
48 | |||
49 | protected const UInt16 IMAGE_DOS_SIGNATURE = 0x5A4D; | ||
50 | protected const UInt32 IMAGE_NT_SIGNATURE = 0x00004550; | ||
51 | protected const UInt64 IMAGE_SECTION_WIXBURN_NAME = 0x6E7275627869772E; // ".wixburn", as a qword. | ||
52 | |||
53 | // The ".wixburn" section contains: | ||
54 | // 0- 3: magic number | ||
55 | // 4- 7: version | ||
56 | // 8-23: bundle GUID | ||
57 | // 24-27: engine (stub) size | ||
58 | // 28-31: original checksum | ||
59 | // 32-35: original signature offset | ||
60 | // 36-39: original signature size | ||
61 | // 40-43: container type (1 = CAB) | ||
62 | // 44-47: container count | ||
63 | // 48-51: byte count of manifest + UX container | ||
64 | // 52-55: byte count of attached container | ||
65 | protected const UInt32 BURN_SECTION_OFFSET_MAGIC = 0; | ||
66 | protected const UInt32 BURN_SECTION_OFFSET_VERSION = 4; | ||
67 | protected const UInt32 BURN_SECTION_OFFSET_BUNDLEGUID = 8; | ||
68 | protected const UInt32 BURN_SECTION_OFFSET_STUBSIZE = 24; | ||
69 | protected const UInt32 BURN_SECTION_OFFSET_ORIGINALCHECKSUM = 28; | ||
70 | protected const UInt32 BURN_SECTION_OFFSET_ORIGINALSIGNATUREOFFSET = 32; | ||
71 | protected const UInt32 BURN_SECTION_OFFSET_ORIGINALSIGNATURESIZE = 36; | ||
72 | protected const UInt32 BURN_SECTION_OFFSET_FORMAT = 40; | ||
73 | protected const UInt32 BURN_SECTION_OFFSET_COUNT = 44; | ||
74 | protected const UInt32 BURN_SECTION_OFFSET_UXSIZE = 48; | ||
75 | protected const UInt32 BURN_SECTION_OFFSET_ATTACHEDCONTAINERSIZE = 52; | ||
76 | protected const UInt32 BURN_SECTION_SIZE = BURN_SECTION_OFFSET_ATTACHEDCONTAINERSIZE + 4; // last field + sizeof(DWORD) | ||
77 | |||
78 | protected const UInt32 BURN_SECTION_MAGIC = 0x00f14300; | ||
79 | protected const UInt32 BURN_SECTION_VERSION = 0x00000002; | ||
80 | |||
81 | protected string fileExe; | ||
82 | protected UInt32 peOffset = UInt32.MaxValue; | ||
83 | protected UInt16 sections = UInt16.MaxValue; | ||
84 | protected UInt32 firstSectionOffset = UInt32.MaxValue; | ||
85 | protected UInt32 checksumOffset; | ||
86 | protected UInt32 certificateTableSignatureOffset; | ||
87 | protected UInt32 certificateTableSignatureSize; | ||
88 | protected UInt32 wixburnDataOffset = UInt32.MaxValue; | ||
89 | |||
90 | // TODO: does this enum exist in another form somewhere? | ||
91 | /// <summary> | ||
92 | /// The types of attached containers that BurnWriter supports. | ||
93 | /// </summary> | ||
94 | public enum Container | ||
95 | { | ||
96 | Nothing = 0, | ||
97 | UX, | ||
98 | Attached | ||
99 | } | ||
100 | |||
101 | /// <summary> | ||
102 | /// Creates a BurnCommon for re-writing a PE file. | ||
103 | /// </summary> | ||
104 | /// <param name="fileExe">File to modify in-place.</param> | ||
105 | /// <param name="bundleGuid">GUID for the bundle.</param> | ||
106 | public BurnCommon(string fileExe) | ||
107 | { | ||
108 | this.fileExe = fileExe; | ||
109 | } | ||
110 | |||
111 | public UInt32 Checksum { get; protected set; } | ||
112 | public UInt32 SignatureOffset { get; protected set; } | ||
113 | public UInt32 SignatureSize { get; protected set; } | ||
114 | public UInt32 Version { get; protected set; } | ||
115 | public UInt32 StubSize { get; protected set; } | ||
116 | public UInt32 OriginalChecksum { get; protected set; } | ||
117 | public UInt32 OriginalSignatureOffset { get; protected set; } | ||
118 | public UInt32 OriginalSignatureSize { get; protected set; } | ||
119 | public UInt32 EngineSize { get; protected set; } | ||
120 | public UInt32 ContainerCount { get; protected set; } | ||
121 | public UInt32 UXAddress { get; protected set; } | ||
122 | public UInt32 UXSize { get; protected set; } | ||
123 | public UInt32 AttachedContainerAddress { get; protected set; } | ||
124 | public UInt32 AttachedContainerSize { get; protected set; } | ||
125 | |||
126 | public void Dispose() | ||
127 | { | ||
128 | Dispose(true); | ||
129 | |||
130 | GC.SuppressFinalize(this); | ||
131 | } | ||
132 | |||
133 | /// <summary> | ||
134 | /// Copies one stream to another. | ||
135 | /// </summary> | ||
136 | /// <param name="input">Input stream.</param> | ||
137 | /// <param name="output">Output stream.</param> | ||
138 | /// <param name="size">Optional count of bytes to copy. 0 indicates whole input stream from current should be copied.</param> | ||
139 | protected static int CopyStream(Stream input, Stream output, int size) | ||
140 | { | ||
141 | byte[] bytes = new byte[4096]; | ||
142 | int total = 0; | ||
143 | int read = 0; | ||
144 | do | ||
145 | { | ||
146 | read = Math.Min(bytes.Length, size - total); | ||
147 | read = input.Read(bytes, 0, read); | ||
148 | if (0 == read) | ||
149 | { | ||
150 | break; | ||
151 | } | ||
152 | |||
153 | output.Write(bytes, 0, read); | ||
154 | total += read; | ||
155 | } while (0 == size || total < size); | ||
156 | |||
157 | return total; | ||
158 | } | ||
159 | |||
160 | /// <summary> | ||
161 | /// Initialize the common information about a Burn engine. | ||
162 | /// </summary> | ||
163 | /// <param name="reader">Binary reader open against a Burn engine.</param> | ||
164 | /// <returns>True if initialized.</returns> | ||
165 | protected bool Initialize(BinaryReader reader) | ||
166 | { | ||
167 | if (!GetWixburnSectionInfo(reader)) | ||
168 | { | ||
169 | return false; | ||
170 | } | ||
171 | |||
172 | reader.BaseStream.Seek(this.wixburnDataOffset, SeekOrigin.Begin); | ||
173 | byte[] bytes = reader.ReadBytes((int)BURN_SECTION_SIZE); | ||
174 | UInt32 uint32 = 0; | ||
175 | |||
176 | uint32 = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_MAGIC); | ||
177 | if (BURN_SECTION_MAGIC != uint32) | ||
178 | { | ||
179 | Messaging.Instance.OnMessage(WixErrors.InvalidBundle(this.fileExe)); | ||
180 | return false; | ||
181 | } | ||
182 | |||
183 | this.Version = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_VERSION); | ||
184 | if (BURN_SECTION_VERSION != this.Version) | ||
185 | { | ||
186 | Messaging.Instance.OnMessage(WixErrors.BundleTooNew(this.fileExe, this.Version)); | ||
187 | return false; | ||
188 | } | ||
189 | |||
190 | uint32 = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_FORMAT); // We only know how to deal with CABs right now | ||
191 | if (1 != uint32) | ||
192 | { | ||
193 | Messaging.Instance.OnMessage(WixErrors.InvalidBundle(this.fileExe)); | ||
194 | return false; | ||
195 | } | ||
196 | |||
197 | this.StubSize = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_STUBSIZE); | ||
198 | this.OriginalChecksum = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_ORIGINALCHECKSUM); | ||
199 | this.OriginalSignatureOffset = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_ORIGINALSIGNATUREOFFSET); | ||
200 | this.OriginalSignatureSize = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_ORIGINALSIGNATURESIZE); | ||
201 | |||
202 | this.ContainerCount = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_COUNT); | ||
203 | this.UXAddress = this.StubSize; | ||
204 | this.UXSize = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_UXSIZE); | ||
205 | |||
206 | // If there is an original signature use that to determine the engine size. | ||
207 | if (0 < this.OriginalSignatureOffset) | ||
208 | { | ||
209 | this.EngineSize = this.OriginalSignatureOffset + this.OriginalSignatureSize; | ||
210 | } | ||
211 | else if (0 < this.SignatureOffset && 2 > this.ContainerCount) // if there is a signature and no attached containers, use the current signature. | ||
212 | { | ||
213 | this.EngineSize = this.SignatureOffset + this.SignatureSize; | ||
214 | } | ||
215 | else // just use the stub and UX container as the size of the engine. | ||
216 | { | ||
217 | this.EngineSize = this.StubSize + this.UXSize; | ||
218 | } | ||
219 | |||
220 | this.AttachedContainerAddress = this.ContainerCount > 1 ? this.EngineSize : 0; | ||
221 | this.AttachedContainerSize = this.ContainerCount > 1 ? BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_ATTACHEDCONTAINERSIZE) : 0; | ||
222 | |||
223 | return true; | ||
224 | } | ||
225 | |||
226 | protected virtual void Dispose(bool disposing) | ||
227 | { | ||
228 | } | ||
229 | |||
230 | /// <summary> | ||
231 | /// Finds the ".wixburn" section in the current exe. | ||
232 | /// </summary> | ||
233 | /// <returns>true if the ".wixburn" section is successfully found; false otherwise</returns> | ||
234 | private bool GetWixburnSectionInfo(BinaryReader reader) | ||
235 | { | ||
236 | if (UInt32.MaxValue == this.wixburnDataOffset) | ||
237 | { | ||
238 | if (!EnsureNTHeader(reader)) | ||
239 | { | ||
240 | return false; | ||
241 | } | ||
242 | |||
243 | UInt32 wixburnSectionOffset = UInt32.MaxValue; | ||
244 | byte[] bytes = new byte[IMAGE_SECTION_HEADER_SIZE]; | ||
245 | |||
246 | reader.BaseStream.Seek(this.firstSectionOffset, SeekOrigin.Begin); | ||
247 | for (UInt16 sectionIndex = 0; sectionIndex < this.sections; ++sectionIndex) | ||
248 | { | ||
249 | reader.Read(bytes, 0, bytes.Length); | ||
250 | |||
251 | if (IMAGE_SECTION_WIXBURN_NAME == BurnCommon.ReadUInt64(bytes, IMAGE_SECTION_HEADER_OFFSET_NAME)) | ||
252 | { | ||
253 | wixburnSectionOffset = this.firstSectionOffset + (IMAGE_SECTION_HEADER_SIZE * sectionIndex); | ||
254 | break; | ||
255 | } | ||
256 | } | ||
257 | |||
258 | if (UInt32.MaxValue == wixburnSectionOffset) | ||
259 | { | ||
260 | Messaging.Instance.OnMessage(WixErrors.StubMissingWixburnSection(this.fileExe)); | ||
261 | return false; | ||
262 | } | ||
263 | |||
264 | // we need 56 bytes for the manifest header, which is always going to fit in | ||
265 | // the smallest alignment (512 bytes), but just to be paranoid... | ||
266 | if (BURN_SECTION_SIZE > BurnCommon.ReadUInt32(bytes, IMAGE_SECTION_HEADER_OFFSET_SIZEOFRAWDATA)) | ||
267 | { | ||
268 | Messaging.Instance.OnMessage(WixErrors.StubWixburnSectionTooSmall(this.fileExe)); | ||
269 | return false; | ||
270 | } | ||
271 | |||
272 | this.wixburnDataOffset = BurnCommon.ReadUInt32(bytes, IMAGE_SECTION_HEADER_OFFSET_POINTERTORAWDATA); | ||
273 | } | ||
274 | |||
275 | return true; | ||
276 | } | ||
277 | |||
278 | /// <summary> | ||
279 | /// Checks for a valid Windows PE signature (IMAGE_NT_SIGNATURE) in the current exe. | ||
280 | /// </summary> | ||
281 | /// <returns>true if the exe is a Windows executable; false otherwise</returns> | ||
282 | private bool EnsureNTHeader(BinaryReader reader) | ||
283 | { | ||
284 | if (UInt32.MaxValue == this.firstSectionOffset) | ||
285 | { | ||
286 | if (!EnsureDosHeader(reader)) | ||
287 | { | ||
288 | return false; | ||
289 | } | ||
290 | |||
291 | reader.BaseStream.Seek(this.peOffset, SeekOrigin.Begin); | ||
292 | byte[] bytes = reader.ReadBytes((int)IMAGE_NT_HEADER_SIZE); | ||
293 | |||
294 | // Verify the NT signature... | ||
295 | if (IMAGE_NT_SIGNATURE != BurnCommon.ReadUInt32(bytes, IMAGE_NT_HEADER_OFFSET_SIGNATURE)) | ||
296 | { | ||
297 | Messaging.Instance.OnMessage(WixErrors.InvalidStubExe(this.fileExe)); | ||
298 | return false; | ||
299 | } | ||
300 | |||
301 | ushort sizeOptionalHeader = BurnCommon.ReadUInt16(bytes, IMAGE_NT_HEADER_OFFSET_SIZEOFOPTIONALHEADER); | ||
302 | |||
303 | this.sections = BurnCommon.ReadUInt16(bytes, IMAGE_NT_HEADER_OFFSET_NUMBEROFSECTIONS); | ||
304 | this.firstSectionOffset = this.peOffset + IMAGE_NT_HEADER_SIZE + sizeOptionalHeader; | ||
305 | |||
306 | this.checksumOffset = this.peOffset + IMAGE_NT_HEADER_SIZE + IMAGE_OPTIONAL_OFFSET_CHECKSUM; | ||
307 | this.certificateTableSignatureOffset = this.peOffset + IMAGE_NT_HEADER_SIZE + sizeOptionalHeader - IMAGE_OPTIONAL_NEGATIVE_OFFSET_CERTIFICATETABLE; | ||
308 | this.certificateTableSignatureSize = this.certificateTableSignatureOffset + 4; // size is in the DWORD after the offset. | ||
309 | |||
310 | bytes = reader.ReadBytes(sizeOptionalHeader); | ||
311 | this.Checksum = BurnCommon.ReadUInt32(bytes, IMAGE_OPTIONAL_OFFSET_CHECKSUM); | ||
312 | this.SignatureOffset = BurnCommon.ReadUInt32(bytes, sizeOptionalHeader - IMAGE_OPTIONAL_NEGATIVE_OFFSET_CERTIFICATETABLE); | ||
313 | this.SignatureSize = BurnCommon.ReadUInt32(bytes, sizeOptionalHeader - IMAGE_OPTIONAL_NEGATIVE_OFFSET_CERTIFICATETABLE + 4); | ||
314 | } | ||
315 | |||
316 | return true; | ||
317 | } | ||
318 | |||
319 | /// <summary> | ||
320 | /// Checks for a valid DOS header in the current exe. | ||
321 | /// </summary> | ||
322 | /// <returns>true if the exe starts with a DOS stub; false otherwise</returns> | ||
323 | private bool EnsureDosHeader(BinaryReader reader) | ||
324 | { | ||
325 | if (UInt32.MaxValue == this.peOffset) | ||
326 | { | ||
327 | byte[] bytes = reader.ReadBytes((int)IMAGE_DOS_HEADER_SIZE); | ||
328 | |||
329 | // Verify the DOS 'MZ' signature. | ||
330 | if (IMAGE_DOS_SIGNATURE != BurnCommon.ReadUInt16(bytes, IMAGE_DOS_HEADER_OFFSET_MAGIC)) | ||
331 | { | ||
332 | Messaging.Instance.OnMessage(WixErrors.InvalidStubExe(this.fileExe)); | ||
333 | return false; | ||
334 | } | ||
335 | |||
336 | this.peOffset = BurnCommon.ReadUInt32(bytes, IMAGE_DOS_HEADER_OFFSET_NTHEADER); | ||
337 | } | ||
338 | |||
339 | return true; | ||
340 | } | ||
341 | |||
342 | /// <summary> | ||
343 | /// Reads a UInt16 value in little-endian format from an offset in an array of bytes. | ||
344 | /// </summary> | ||
345 | /// <param name="bytes">Array from which to read.</param> | ||
346 | /// <param name="offset">Beginning offset from which to read.</param> | ||
347 | /// <returns>value at offset</returns> | ||
348 | private static UInt16 ReadUInt16(byte[] bytes, UInt32 offset) | ||
349 | { | ||
350 | Debug.Assert(offset + 2 <= bytes.Length); | ||
351 | return (UInt16)(bytes[offset] + (bytes[offset + 1] << 8)); | ||
352 | } | ||
353 | |||
354 | /// <summary> | ||
355 | /// Reads a UInt32 value in little-endian format from an offset in an array of bytes. | ||
356 | /// </summary> | ||
357 | /// <param name="bytes">Array from which to read.</param> | ||
358 | /// <param name="offset">Beginning offset from which to read.</param> | ||
359 | /// <returns>value at offset</returns> | ||
360 | private static UInt32 ReadUInt32(byte[] bytes, UInt32 offset) | ||
361 | { | ||
362 | Debug.Assert(offset + 4 <= bytes.Length); | ||
363 | return (UInt32)(bytes[offset] + (bytes[offset + 1] << 8) + (bytes[offset + 2] << 16) + (bytes[offset + 3] << 24)); | ||
364 | } | ||
365 | |||
366 | /// <summary> | ||
367 | /// Reads a UInt64 value in little-endian format from an offset in an array of bytes. | ||
368 | /// </summary> | ||
369 | /// <param name="bytes">Array from which to read.</param> | ||
370 | /// <param name="offset">Beginning offset from which to read.</param> | ||
371 | /// <returns>value at offset</returns> | ||
372 | private static UInt64 ReadUInt64(byte[] bytes, UInt32 offset) | ||
373 | { | ||
374 | Debug.Assert(offset + 8 <= bytes.Length); | ||
375 | return BurnCommon.ReadUInt32(bytes, offset) + ((UInt64)(BurnCommon.ReadUInt32(bytes, offset + 4)) << 32); | ||
376 | } | ||
377 | } | ||
378 | } | ||