diff options
Diffstat (limited to 'src/burn/engine/section.cpp')
-rw-r--r-- | src/burn/engine/section.cpp | 399 |
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 | ||
15 | typedef 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 | |||
32 | static HRESULT VerifySectionMatchesMemoryPEHeader( | ||
33 | __in REFGUID pSection | ||
34 | ); | ||
35 | |||
36 | |||
37 | extern "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, §ionHeader, 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 | |||
247 | LExit: | ||
248 | ReleaseMem(pBurnSectionHeader); | ||
249 | |||
250 | return hr; | ||
251 | } | ||
252 | |||
253 | extern "C" void SectionUninitialize( | ||
254 | __out BURN_SECTION* pSection | ||
255 | ) | ||
256 | { | ||
257 | ReleaseMem(pSection->rgcbContainers); | ||
258 | memset(pSection, 0, sizeof(BURN_SECTION)); | ||
259 | } | ||
260 | |||
261 | extern "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 | |||
303 | LExit: | ||
304 | return hr; | ||
305 | } | ||
306 | |||
307 | HRESULT 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 | |||
397 | LExit: | ||
398 | return hr; | ||
399 | } | ||