aboutsummaryrefslogtreecommitdiff
path: root/src/burn/engine/section.cpp
diff options
context:
space:
mode:
authorRob Mensching <rob@firegiant.com>2021-04-22 17:06:54 -0700
committerRob Mensching <rob@firegiant.com>2021-04-29 16:36:06 -0700
commitaf10c45d7b3a44af0b461a557847fe03263dcc10 (patch)
tree6a5c1532304782c36ffe4200b38f3afb76789a43 /src/burn/engine/section.cpp
parent9c2aed97299fb96aeee3f1471ce40225437aaecf (diff)
downloadwix-af10c45d7b3a44af0b461a557847fe03263dcc10.tar.gz
wix-af10c45d7b3a44af0b461a557847fe03263dcc10.tar.bz2
wix-af10c45d7b3a44af0b461a557847fe03263dcc10.zip
Move burn into burn
Diffstat (limited to 'src/burn/engine/section.cpp')
-rw-r--r--src/burn/engine/section.cpp399
1 files changed, 399 insertions, 0 deletions
diff --git a/src/burn/engine/section.cpp b/src/burn/engine/section.cpp
new file mode 100644
index 00000000..3720155c
--- /dev/null
+++ b/src/burn/engine/section.cpp
@@ -0,0 +1,399 @@
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#include "precomp.h"
4
5
6// constants
7
8// If these defaults ever change, be sure to update constants in burn\stub\StubSection.cpp as well.
9#define BURN_SECTION_NAME ".wixburn"
10#define BURN_SECTION_MAGIC 0x00f14300
11#define BURN_SECTION_VERSION 0x00000002
12#define MANIFEST_CABINET_TOKEN L"0"
13
14// structs
15typedef struct _BURN_SECTION_HEADER
16{
17 DWORD dwMagic;
18 DWORD dwVersion;
19
20 GUID guidBundleId;
21
22 DWORD dwStubSize;
23 DWORD dwOriginalChecksum;
24 DWORD dwOriginalSignatureOffset;
25 DWORD dwOriginalSignatureSize;
26
27 DWORD dwFormat;
28 DWORD cContainers;
29 DWORD rgcbContainers[1];
30} BURN_SECTION_HEADER;
31
32static HRESULT VerifySectionMatchesMemoryPEHeader(
33 __in REFGUID pSection
34 );
35
36
37extern "C" HRESULT SectionInitialize(
38 __in BURN_SECTION* pSection,
39 __in HANDLE hEngineFile,
40 __in HANDLE hSourceEngineFile
41 )
42{
43 HRESULT hr = S_OK;
44 DWORD cbRead = 0;
45 LARGE_INTEGER li = { };
46 LONGLONG llSize = 0;
47 IMAGE_DOS_HEADER dosHeader = { };
48 IMAGE_NT_HEADERS ntHeader = { };
49 DWORD dwChecksumOffset = 0;
50 DWORD dwCertificateTableOffset = 0;
51 DWORD dwSignatureOffset = 0;
52 DWORD cbSignature = 0;
53 IMAGE_SECTION_HEADER sectionHeader = { };
54 DWORD_PTR dwOriginalChecksumAndSignatureOffset = 0;
55 BURN_SECTION_HEADER* pBurnSectionHeader = NULL;
56
57 pSection->hEngineFile = hEngineFile;
58 ExitOnInvalidHandleWithLastError(pSection->hEngineFile, hr, "Failed to open handle to engine process path.");
59
60 pSection->hSourceEngineFile = INVALID_HANDLE_VALUE == hSourceEngineFile ? hEngineFile : hSourceEngineFile;
61
62 //
63 // First, make sure we have a valid DOS signature.
64 //
65 if (!::SetFilePointerEx(pSection->hEngineFile, li, NULL, FILE_BEGIN))
66 {
67 ExitWithLastError(hr, "Failed to seek to start of file.");
68 }
69
70 // read DOS header
71 if (!::ReadFile(pSection->hEngineFile, &dosHeader, sizeof(IMAGE_DOS_HEADER), &cbRead, NULL))
72 {
73 ExitWithLastError(hr, "Failed to read DOS header.");
74 }
75 else if (sizeof(IMAGE_DOS_HEADER) > cbRead || IMAGE_DOS_SIGNATURE != dosHeader.e_magic)
76 {
77 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
78 ExitOnRootFailure(hr, "Failed to find valid DOS image header in buffer.");
79 }
80
81 //
82 // Now, make sure we have a valid NT signature.
83 //
84
85 // seek to new header
86 li.QuadPart = dosHeader.e_lfanew;
87 if (!::SetFilePointerEx(pSection->hEngineFile, li, NULL, FILE_BEGIN))
88 {
89 ExitWithLastError(hr, "Failed to seek to NT header.");
90 }
91
92 // read NT header
93 if (!::ReadFile(pSection->hEngineFile, &ntHeader, sizeof(IMAGE_NT_HEADERS) - sizeof(IMAGE_OPTIONAL_HEADER), &cbRead, NULL))
94 {
95 ExitWithLastError(hr, "Failed to read NT header.");
96 }
97 else if ((sizeof(IMAGE_NT_HEADERS) - sizeof(IMAGE_OPTIONAL_HEADER)) > cbRead || IMAGE_NT_SIGNATURE != ntHeader.Signature)
98 {
99 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
100 ExitOnRootFailure(hr, "Failed to find valid NT image header in buffer.");
101 }
102
103 // Get the table offsets.
104 dwChecksumOffset = dosHeader.e_lfanew + sizeof(IMAGE_NT_HEADERS) - sizeof(IMAGE_OPTIONAL_HEADER) + (sizeof(DWORD) * 16);
105 dwCertificateTableOffset = dosHeader.e_lfanew + sizeof(IMAGE_NT_HEADERS) - (sizeof(IMAGE_DATA_DIRECTORY) * (IMAGE_NUMBEROF_DIRECTORY_ENTRIES - IMAGE_DIRECTORY_ENTRY_SECURITY));
106
107 // Seek into the certificate table to get the signature size.
108 li.QuadPart = dwCertificateTableOffset;
109 if (!::SetFilePointerEx(pSection->hEngineFile, li, NULL, FILE_BEGIN))
110 {
111 ExitWithLastError(hr, "Failed to seek to section info.");
112 }
113
114 if (!::ReadFile(pSection->hEngineFile, &dwSignatureOffset, sizeof(dwSignatureOffset), &cbRead, NULL))
115 {
116 ExitWithLastError(hr, "Failed to read signature offset.");
117 }
118
119 if (!::ReadFile(pSection->hEngineFile, &cbSignature, sizeof(cbSignature), &cbRead, NULL))
120 {
121 ExitWithLastError(hr, "Failed to read signature size.");
122 }
123
124 //
125 // Finally, get into the section table and look for the Burn section info.
126 //
127
128 // seek past optional headers
129 li.QuadPart = dosHeader.e_lfanew + sizeof(IMAGE_NT_HEADERS) - sizeof(IMAGE_OPTIONAL_HEADER) + ntHeader.FileHeader.SizeOfOptionalHeader;
130 if (!::SetFilePointerEx(pSection->hEngineFile, li, NULL, FILE_BEGIN))
131 {
132 ExitWithLastError(hr, "Failed to seek past optional headers.");
133 }
134
135 // read sections one by one until we find our section
136 for (DWORD i = 0; ; ++i)
137 {
138 // read section
139 if (!::ReadFile(pSection->hEngineFile, &sectionHeader, sizeof(IMAGE_SECTION_HEADER), &cbRead, NULL))
140 {
141 ExitWithLastError(hr, "Failed to read image section header, index: %u", i);
142 }
143 if (sizeof(IMAGE_SECTION_HEADER) > cbRead)
144 {
145 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
146 ExitOnRootFailure(hr, "Failed to read complete image section header, index: %u", i);
147 }
148
149 // compare header name
150 C_ASSERT(sizeof(sectionHeader.Name) == sizeof(BURN_SECTION_NAME) - 1);
151 if (0 == memcmp(sectionHeader.Name, BURN_SECTION_NAME, sizeof(sectionHeader.Name)))
152 {
153 break;
154 }
155
156 // fail if we hit the end
157 if (i + 1 >= ntHeader.FileHeader.NumberOfSections)
158 {
159 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
160 ExitOnRootFailure(hr, "Failed to find Burn section.");
161 }
162 }
163
164 //
165 // We've arrived at the section info.
166 //
167
168 // check size of section
169 if (sizeof(BURN_SECTION_HEADER) > sectionHeader.SizeOfRawData)
170 {
171 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
172 ExitOnRootFailure(hr, "Failed to read section info, data to short: %u", sectionHeader.SizeOfRawData);
173 }
174
175 // allocate buffer for section info
176 pBurnSectionHeader = (BURN_SECTION_HEADER*)MemAlloc(sectionHeader.SizeOfRawData, TRUE);
177 ExitOnNull(pBurnSectionHeader, hr, E_OUTOFMEMORY, "Failed to allocate buffer for section info.");
178
179 // seek to section info
180 li.QuadPart = sectionHeader.PointerToRawData;
181 if (!::SetFilePointerEx(pSection->hEngineFile, li, NULL, FILE_BEGIN))
182 {
183 ExitWithLastError(hr, "Failed to seek to section info.");
184 }
185
186 // Note the location of original checksum and signature information in the burn section header.
187 dwOriginalChecksumAndSignatureOffset = sectionHeader.PointerToRawData + (reinterpret_cast<LPBYTE>(&pBurnSectionHeader->dwOriginalChecksum) - reinterpret_cast<LPBYTE>(pBurnSectionHeader));
188
189 // read section info
190 if (!::ReadFile(pSection->hEngineFile, pBurnSectionHeader, sectionHeader.SizeOfRawData, &cbRead, NULL))
191 {
192 ExitWithLastError(hr, "Failed to read section info.");
193 }
194 else if (sectionHeader.SizeOfRawData > cbRead)
195 {
196 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
197 ExitOnRootFailure(hr, "Failed to read complete section info.");
198 }
199
200 // validate version of section info
201 if (BURN_SECTION_VERSION != pBurnSectionHeader->dwVersion)
202 {
203 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
204 ExitOnRootFailure(hr, "Failed to read section info, unsupported version: %08x", pBurnSectionHeader->dwVersion);
205 }
206
207 hr = FileSizeByHandle(pSection->hSourceEngineFile, &llSize);
208 ExitOnFailure(hr, "Failed to get total size of bundle.");
209
210 pSection->cbStub = pBurnSectionHeader->dwStubSize;
211
212 // If there is an original signature use that to determine the engine size.
213 if (pBurnSectionHeader->dwOriginalSignatureOffset)
214 {
215 pSection->cbEngineSize = pBurnSectionHeader->dwOriginalSignatureOffset + pBurnSectionHeader->dwOriginalSignatureSize;
216 }
217 else if (dwSignatureOffset) // if there is a signature, use it.
218 {
219 pSection->cbEngineSize = dwSignatureOffset + cbSignature;
220 }
221 else // just use the stub and UX container as the size of the engine.
222 {
223 pSection->cbEngineSize = pSection->cbStub + pBurnSectionHeader->rgcbContainers[0];
224 }
225
226 pSection->qwBundleSize = static_cast<DWORD64>(llSize);
227
228 pSection->dwChecksumOffset = dwChecksumOffset;
229 pSection->dwCertificateTableOffset = dwCertificateTableOffset;
230 pSection->dwOriginalChecksumAndSignatureOffset = dwOriginalChecksumAndSignatureOffset;
231
232 pSection->dwOriginalChecksum = pBurnSectionHeader->dwOriginalChecksum;
233 pSection->dwOriginalSignatureOffset = pBurnSectionHeader->dwOriginalSignatureOffset;
234 pSection->dwOriginalSignatureSize = pBurnSectionHeader->dwOriginalSignatureSize;
235
236 pSection->dwFormat = pBurnSectionHeader->dwFormat;
237 pSection->cContainers = pBurnSectionHeader->cContainers;
238 pSection->rgcbContainers = (DWORD*)MemAlloc(sizeof(DWORD) * pSection->cContainers, TRUE);
239 ExitOnNull(pSection->rgcbContainers, hr, E_OUTOFMEMORY, "Failed to allocate memory for container sizes.");
240
241 memcpy(pSection->rgcbContainers, pBurnSectionHeader->rgcbContainers, sizeof(DWORD) * pSection->cContainers);
242
243 // TODO: verify more than just the GUID.
244 hr = VerifySectionMatchesMemoryPEHeader(pBurnSectionHeader->guidBundleId);
245 ExitOnRootFailure(hr, "PE Header from file didn't match PE Header in memory.");
246
247LExit:
248 ReleaseMem(pBurnSectionHeader);
249
250 return hr;
251}
252
253extern "C" void SectionUninitialize(
254 __out BURN_SECTION* pSection
255 )
256{
257 ReleaseMem(pSection->rgcbContainers);
258 memset(pSection, 0, sizeof(BURN_SECTION));
259}
260
261extern "C" HRESULT SectionGetAttachedContainerInfo(
262 __in BURN_SECTION* pSection,
263 __in DWORD iContainerIndex,
264 __in DWORD dwExpectedType,
265 __out DWORD64* pqwOffset,
266 __out DWORD64* pqwSize,
267 __out BOOL* pfPresent
268 )
269{
270 HRESULT hr = S_OK;
271
272 // validate container info
273 if (iContainerIndex >= pSection->cContainers)
274 {
275 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
276 ExitOnRootFailure(hr, "Failed to find container info, too few elements: %u", pSection->cContainers);
277 }
278 else if (dwExpectedType != pSection->dwFormat)
279 {
280 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
281 ExitOnRootFailure(hr, "Unexpected container format.");
282 }
283
284 // If we are asking for the UX container, find it right after the stub.
285 if (0 == iContainerIndex)
286 {
287 *pqwOffset = pSection->cbStub;
288 }
289 else // attached containers start after the whole engine.
290 {
291 *pqwOffset = pSection->cbEngineSize;
292 for (DWORD i = 1; i < iContainerIndex; ++i)
293 {
294 *pqwOffset += pSection->rgcbContainers[i];
295 }
296 }
297
298 *pqwSize = pSection->rgcbContainers[iContainerIndex];
299 *pfPresent = (*pqwOffset + *pqwSize) <= pSection->qwBundleSize;
300
301 AssertSz(*pfPresent || pSection->qwBundleSize <= *pqwOffset, "An attached container should either be present or completely absent from the bundle. Found a case where the attached container is partially present which is wrong.");
302
303LExit:
304 return hr;
305}
306
307HRESULT VerifySectionMatchesMemoryPEHeader(
308 __in REFGUID pBundleId
309 )
310{
311 HRESULT hr = S_OK;
312 BYTE* pbPEHeader = NULL;
313 PIMAGE_DOS_HEADER pDosHeader = NULL;
314 PIMAGE_NT_HEADERS pNtHeader = NULL;
315 PIMAGE_SECTION_HEADER pSections = NULL;
316 PIMAGE_SECTION_HEADER pSectionHeader = NULL;
317 BURN_SECTION_HEADER* pBurnSectionHeader = NULL;
318
319 pbPEHeader = reinterpret_cast<BYTE*>(::GetModuleHandleW(NULL));
320 ExitOnNullWithLastError(pbPEHeader, hr, "Failed to get module handle to process.");
321
322 //
323 // First, make sure we have a valid DOS signature.
324 //
325
326 pDosHeader = reinterpret_cast<PIMAGE_DOS_HEADER>(pbPEHeader);
327 if (IMAGE_DOS_SIGNATURE != pDosHeader->e_magic)
328 {
329 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
330 ExitOnRootFailure(hr, "Failed to find valid DOS image header in buffer.");
331 }
332
333 //
334 // Now, make sure we have a valid NT signature.
335 //
336
337 pNtHeader = reinterpret_cast<PIMAGE_NT_HEADERS>(pbPEHeader + pDosHeader->e_lfanew);
338 if (IMAGE_NT_SIGNATURE != pNtHeader->Signature)
339 {
340 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
341 ExitOnRootFailure(hr, "Failed to find valid NT image header in buffer.");
342 }
343
344 //
345 // Finally, get into the section table and look for the Burn section info.
346 //
347
348 pSections = reinterpret_cast<PIMAGE_SECTION_HEADER>(pbPEHeader + pDosHeader->e_lfanew + sizeof(IMAGE_NT_HEADERS) - sizeof(IMAGE_OPTIONAL_HEADER) + pNtHeader->FileHeader.SizeOfOptionalHeader);
349
350 // Read sections one by one until we find our section.
351 for (DWORD i = 0; ; ++i)
352 {
353 pSectionHeader = pSections + i;
354
355 // Compare header name.
356 C_ASSERT(sizeof(pSectionHeader->Name) == sizeof(BURN_SECTION_NAME) - 1);
357 if (0 == memcmp(pSectionHeader->Name, BURN_SECTION_NAME, sizeof(pSectionHeader->Name)))
358 {
359 break;
360 }
361
362 // Fail if we hit the end.
363 if (i + 1 >= pNtHeader->FileHeader.NumberOfSections)
364 {
365 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
366 ExitOnRootFailure(hr, "Failed to find Burn section.");
367 }
368 }
369
370 //
371 // We've arrived at the section info.
372 //
373
374 // Check size of section.
375 if (sizeof(BURN_SECTION_HEADER) > pSectionHeader->SizeOfRawData)
376 {
377 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
378 ExitOnRootFailure(hr, "Failed to read section info, data to short: %u", pSectionHeader->SizeOfRawData);
379 }
380
381 // Get Burn section info.
382 pBurnSectionHeader = reinterpret_cast<BURN_SECTION_HEADER*>(pbPEHeader + pSectionHeader->VirtualAddress);
383
384 // Validate version of section info.
385 if (BURN_SECTION_VERSION != pBurnSectionHeader->dwVersion)
386 {
387 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
388 ExitOnRootFailure(hr, "Failed to read section info, unsupported version: %08x", pBurnSectionHeader->dwVersion);
389 }
390
391 if (!::IsEqualGUID(pBundleId, pBurnSectionHeader->guidBundleId))
392 {
393 hr = E_INVALIDDATA;
394 ExitOnRootFailure(hr, "Bundle guid didn't match the guid in the PE Header in memory.");
395 }
396
397LExit:
398 return hr;
399}