aboutsummaryrefslogtreecommitdiff
path: root/src/WixToolset.Core.Burn/Bundles/BurnCommon.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/WixToolset.Core.Burn/Bundles/BurnCommon.cs')
-rw-r--r--src/WixToolset.Core.Burn/Bundles/BurnCommon.cs378
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
3namespace 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}