1 : // Copyright 2012 Google Inc. All Rights Reserved.
2 : //
3 : // Licensed under the Apache License, Version 2.0 (the "License");
4 : // you may not use this file except in compliance with the License.
5 : // You may obtain a copy of the License at
6 : //
7 : // http://www.apache.org/licenses/LICENSE-2.0
8 : //
9 : // Unless required by applicable law or agreed to in writing, software
10 : // distributed under the License is distributed on an "AS IS" BASIS,
11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : // See the License for the specific language governing permissions and
13 : // limitations under the License.
14 :
15 : #include "syzygy/pe/pe_utils.h"
16 :
17 : #include "base/string_util.h"
18 : #include "syzygy/block_graph/typed_block.h"
19 : #include "syzygy/pe/dos_stub.h"
20 :
21 : namespace pe {
22 :
23 : using block_graph::BlockGraph;
24 : using block_graph::ConstTypedBlock;
25 : using block_graph::TypedBlock;
26 : using core::RelativeAddress;
27 :
28 : namespace {
29 :
30 : // A simple struct that can be used to let us access strings using TypedBlock.
31 : struct StringStruct {
32 : const char string[1];
33 : };
34 :
35 : typedef TypedBlock<IMAGE_DOS_HEADER> DosHeader;
36 : typedef TypedBlock<IMAGE_IMPORT_DESCRIPTOR> ImageImportDescriptor;
37 : typedef TypedBlock<IMAGE_NT_HEADERS> NtHeaders;
38 : typedef TypedBlock<StringStruct> String;
39 :
40 : template <typename BlockPtr>
41 : BlockPtr UncheckedGetNtHeadersBlockFromDosHeaderBlock(
42 E : BlockPtr dos_header_block) {
43 E : BlockGraph::Reference ref;
44 : if (!dos_header_block->GetReference(offsetof(IMAGE_DOS_HEADER, e_lfanew),
45 E : &ref)) {
46 : // No NT headers reference.
47 E : return NULL;
48 : }
49 :
50 : if (ref.offset() != 0 ||
51 : ref.type() != BlockGraph::RELATIVE_REF ||
52 E : ref.size() != sizeof(RelativeAddress)) {
53 : // The reference is of incorrect type.
54 E : return NULL;
55 : }
56 :
57 E : return ref.referenced();
58 E : }
59 :
60 : template <typename BlockPtr>
61 : BlockPtr CheckedGetNtHeadersBlockFromDosHeaderBlock(
62 E : BlockPtr dos_header_block) {
63 E : DCHECK(IsValidDosHeaderBlock(dos_header_block));
64 :
65 : BlockPtr nt_headers_block =
66 E : UncheckedGetNtHeadersBlockFromDosHeaderBlock(dos_header_block);
67 : if (nt_headers_block == NULL ||
68 E : !IsValidNtHeadersBlock(nt_headers_block)) {
69 i : return NULL;
70 : }
71 :
72 E : return nt_headers_block;
73 E : }
74 :
75 : } // namespace
76 :
77 : const char kCodeSectionName[] = ".text";
78 : const char kReadOnlyDataSectionName[] = ".rdata";
79 : const char kReadWriteDataSectionName[] = ".data";
80 : const char kRelocSectionName[] = ".reloc";
81 : const char kResourceSectionName[] = ".rsrc";
82 : const char kTlsSectionName[] = ".tls";
83 :
84 : const DWORD kCodeCharacteristics =
85 : IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_EXECUTE;
86 : const DWORD kReadOnlyDataCharacteristics =
87 : IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ;
88 : const DWORD kReadWriteDataCharacteristics =
89 : IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE;
90 : const DWORD kRelocCharacteristics =
91 : IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_DISCARDABLE |
92 : IMAGE_SCN_MEM_READ;
93 :
94 E : bool IsValidDosHeaderBlock(const BlockGraph::Block* dos_header_block) {
95 E : ConstTypedBlock<IMAGE_DOS_HEADER> dos_header;
96 :
97 E : if (!dos_header.Init(0, dos_header_block)) {
98 : // Too small or no data.
99 E : return false;
100 : }
101 :
102 E : if (dos_header->e_magic != IMAGE_DOS_SIGNATURE) {
103 : // Wrong signature.
104 E : return false;
105 : }
106 :
107 : // The "DOS file size" is encoded in a rather wonky manner.
108 : // - e_cp is the number of "pages" in the file, but
109 : // - e_cblp is the number of bytes used on the last page.
110 E : size_t dos_file_size = 512 * dos_header->e_cp;
111 E : if (dos_header->e_cblp != 0) {
112 : // Let's not go below zero size.
113 E : if (dos_file_size < 512)
114 E : return false;
115 :
116 E : dos_file_size -= 512;
117 E : dos_file_size += dos_header->e_cblp;
118 : }
119 : // The VC linker yields a DOS header with a size that's larger than
120 : // the DOS header and the NT headers combined. I wonder if anyone cares
121 : // about these sizes anymore.
122 E : if (dos_file_size < dos_header_block->size())
123 E : return false;
124 :
125 : // Check the paragraph size of the header.
126 E : if (dos_header->e_cparhdr * 16 < sizeof(IMAGE_DOS_HEADER))
127 E : return false;
128 :
129 : // Retrieve the NT headers.
130 : const BlockGraph::Block* nt_headers =
131 E : UncheckedGetNtHeadersBlockFromDosHeaderBlock(dos_header_block);
132 E : if (nt_headers == NULL) {
133 : // No DOS header reference.
134 E : return false;
135 : }
136 :
137 E : return true;
138 E : }
139 :
140 E : bool IsValidNtHeadersBlock(const BlockGraph::Block* nt_headers_block) {
141 : // Check the signatures.
142 E : ConstTypedBlock<IMAGE_NT_HEADERS> nt_headers;
143 :
144 E : if (!nt_headers.Init(0, nt_headers_block)) {
145 : // Short or no data.
146 i : return false;
147 : }
148 :
149 E : if (nt_headers->Signature!= IMAGE_NT_SIGNATURE) {
150 : // Wrong signature.
151 E : return false;
152 : }
153 : if (nt_headers->FileHeader.SizeOfOptionalHeader !=
154 E : sizeof(IMAGE_OPTIONAL_HEADER)) {
155 : // Wrong optional header size.
156 E : return false;
157 : }
158 E : if (nt_headers->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC) {
159 : // Wrong magic for optional header.
160 E : return false;
161 : }
162 :
163 : // Calculate the minimum size for the NT headers and the section header.
164 : size_t header_size = sizeof(IMAGE_NT_HEADERS) +
165 E : sizeof(IMAGE_SECTION_HEADER) * nt_headers->FileHeader.NumberOfSections;
166 :
167 : if (nt_headers_block->size() < header_size ||
168 E : nt_headers_block->data_size() < header_size) {
169 : // The block's size isn't large enough for the section headers.
170 i : return false;
171 : }
172 :
173 E : return true;
174 E : }
175 :
176 : const BlockGraph::Block* GetNtHeadersBlockFromDosHeaderBlock(
177 E : const BlockGraph::Block* dos_header_block) {
178 E : return CheckedGetNtHeadersBlockFromDosHeaderBlock(dos_header_block);
179 E : }
180 :
181 : BlockGraph::Block* GetNtHeadersBlockFromDosHeaderBlock(
182 E : BlockGraph::Block* dos_header_block) {
183 E : return CheckedGetNtHeadersBlockFromDosHeaderBlock(dos_header_block);
184 E : }
185 :
186 E : bool UpdateDosHeader(BlockGraph::Block* dos_header_block) {
187 E : DCHECK(dos_header_block != NULL);
188 :
189 : // The DOS header has to be a multiple of 16 bytes for historic reasons.
190 : size_t dos_header_size = common::AlignUp(
191 E : sizeof(IMAGE_DOS_HEADER) + pe::kDosStubSize, 16);
192 :
193 : // If the new header block is shorter than it was, go ahead and
194 : // trim the source ranges to match the new, shorter size.
195 E : if (dos_header_block->size() > dos_header_size) {
196 : BlockGraph::Block::DataRange range(
197 E : dos_header_size, dos_header_block->size() - dos_header_size);
198 E : dos_header_block->source_ranges().RemoveMappedRange(range);
199 : }
200 :
201 E : dos_header_block->ResizeData(dos_header_size);
202 E : dos_header_block->set_size(dos_header_size);
203 E : DCHECK_EQ(dos_header_size, dos_header_block->size());
204 E : DCHECK_EQ(dos_header_size, dos_header_block->data_size());
205 :
206 E : TypedBlock<IMAGE_DOS_HEADER> dos_header;
207 E : if (!dos_header.InitWithSize(0, dos_header_size, dos_header_block)) {
208 i : LOG(ERROR) << "Unable to cast IMAGE_DOS_HEADER.";
209 i : return false;
210 : }
211 :
212 : // Wipe the DOS header and fill in the stub.
213 E : memset(dos_header.Get(), 0, sizeof(IMAGE_DOS_HEADER));
214 E : memcpy(dos_header.Get() + 1, pe::kDosStub, pe::kDosStubSize);
215 :
216 E : dos_header->e_magic = IMAGE_DOS_SIGNATURE;
217 : // Calculate the number of bytes used on the last DOS executable "page".
218 E : dos_header->e_cblp = dos_header_size % 512;
219 : // Calculate the number of pages used by the DOS executable.
220 E : dos_header->e_cp = dos_header_size / 512;
221 : // Count the last page if we didn't have an even multiple
222 E : if (dos_header->e_cblp != 0)
223 E : dos_header->e_cp++;
224 :
225 : // Header length in "paragraphs".
226 E : dos_header->e_cparhdr = sizeof(*dos_header) / 16;
227 :
228 : // Set this to max allowed, just because.
229 E : dos_header->e_maxalloc = 0xFFFF;
230 :
231 : // Location of relocs - our header has zero relocs, but we set this anyway.
232 E : dos_header->e_lfarlc = sizeof(*dos_header);
233 :
234 E : DCHECK(IsValidDosHeaderBlock(dos_header_block));
235 :
236 E : return true;
237 E : }
238 :
239 : namespace {
240 :
241 : enum EntryPointTypeEnum { kExeEntryPoint, kDllEntryPoint };
242 :
243 : bool GetImageEntryPoint(BlockGraph::Block* dos_header_block,
244 : EntryPointTypeEnum desired_entry_point_type,
245 E : EntryPoint* entry_point) {
246 E : DCHECK(dos_header_block != NULL);
247 E : DCHECK(entry_point != NULL);
248 :
249 E : *entry_point = EntryPoint(static_cast<BlockGraph::Block*>(NULL), 0);
250 :
251 : BlockGraph::Block* nt_headers_block =
252 E : pe::GetNtHeadersBlockFromDosHeaderBlock(dos_header_block);
253 :
254 E : TypedBlock<IMAGE_NT_HEADERS> nt_headers;
255 E : if (nt_headers_block == NULL || !nt_headers.Init(0, nt_headers_block)) {
256 i : LOG(ERROR) << "Unable to retrieve NT Headers.";
257 i : return false;
258 : }
259 :
260 E : EntryPointTypeEnum entry_point_type = kExeEntryPoint;
261 E : if ((nt_headers->FileHeader.Characteristics & IMAGE_FILE_DLL) != 0)
262 E : entry_point_type = kDllEntryPoint;
263 :
264 E : if (entry_point_type != desired_entry_point_type)
265 E : return true;
266 :
267 E : BlockGraph::Reference entry_point_ref;
268 : bool found = nt_headers.block()->GetReference(
269 : offsetof(IMAGE_NT_HEADERS, OptionalHeader.AddressOfEntryPoint),
270 E : &entry_point_ref);
271 :
272 E : if (!found && entry_point_type == kExeEntryPoint) {
273 E : LOG(ERROR) << "Malformed PE Headers: No entry point found for executable.";
274 E : return false;
275 : }
276 :
277 E : if (found) {
278 : *entry_point = EntryPoint(entry_point_ref.referenced(),
279 E : entry_point_ref.offset());
280 : }
281 :
282 E : return true;
283 E : }
284 :
285 : } // namespace
286 :
287 : bool GetExeEntryPoint(BlockGraph::Block* dos_header_block,
288 E : EntryPoint* entry_point) {
289 E : return GetImageEntryPoint(dos_header_block, kExeEntryPoint, entry_point);
290 E : }
291 :
292 : bool GetDllEntryPoint(BlockGraph::Block* dos_header_block,
293 E : EntryPoint* entry_point) {
294 E : return GetImageEntryPoint(dos_header_block, kDllEntryPoint, entry_point);
295 E : }
296 :
297 : bool GetTlsInitializers(BlockGraph::Block* dos_header_block,
298 E : EntryPointSet* entry_points) {
299 E : DCHECK(dos_header_block != NULL);
300 E : DCHECK(entry_points != NULL);
301 :
302 : BlockGraph::Block* nt_headers_block =
303 E : pe::GetNtHeadersBlockFromDosHeaderBlock(dos_header_block);
304 :
305 E : TypedBlock<IMAGE_NT_HEADERS> nt_headers;
306 E : if (nt_headers_block == NULL || !nt_headers.Init(0, nt_headers_block)) {
307 i : LOG(ERROR) << "Unable to retrieve NT Headers.";
308 i : return false;
309 : }
310 :
311 : // If the module has no TLS directory then there are no TLS initializers
312 : // and hence nothing to do.
313 : const IMAGE_DATA_DIRECTORY& data_dir =
314 E : nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS];
315 E : if (data_dir.Size == 0 || !nt_headers.HasReference(data_dir.VirtualAddress)) {
316 E : return true;
317 : }
318 :
319 : // Find the TLS directory.
320 E : TypedBlock<IMAGE_TLS_DIRECTORY> tls_dir;
321 E : if (!nt_headers.Dereference(data_dir.VirtualAddress, &tls_dir)) {
322 i : LOG(ERROR) << "Failed to cast TLS directory.";
323 i : return false;
324 : }
325 :
326 : // Get the TLS initializer callbacks. We manually lookup the reference
327 : // because it is an indirect reference, which can't be dereferenced by
328 : // TypedBlock.
329 : typedef BlockGraph::Block::ReferenceMap ReferenceMap;
330 : ReferenceMap::const_iterator callback_ref =
331 : tls_dir.block()->references().find(
332 E : tls_dir.OffsetOf(tls_dir->AddressOfCallBacks));
333 E : if (callback_ref == tls_dir.block()->references().end()) {
334 i : LOG(ERROR) << "Failed to locate TLS initializers.";
335 i : return false;
336 : }
337 :
338 : // Note each of the TLS entry points.
339 E : const BlockGraph::Block* callbacks_block = callback_ref->second.referenced();
340 E : const ReferenceMap& ref_map = callbacks_block->references();
341 E : ReferenceMap::const_iterator iter = ref_map.begin();
342 E : for (; iter != ref_map.end(); ++iter) {
343 E : const BlockGraph::Reference& ref = iter->second;
344 E : DCHECK(ref.size() == sizeof(core::AbsoluteAddress));
345 : entry_points->insert(
346 E : std::make_pair(ref.referenced(), ref.offset()));
347 E : }
348 :
349 E : return true;
350 E : }
351 :
352 : bool HasImportEntry(block_graph::BlockGraph::Block* header_block,
353 : const base::StringPiece& dll_name,
354 E : bool* has_import_entry) {
355 E : DCHECK(header_block != NULL);
356 E : DCHECK(dll_name != NULL);
357 E : DCHECK(!dll_name.empty());
358 E : DCHECK(has_import_entry != NULL);
359 :
360 E : *has_import_entry = false;
361 :
362 E : DosHeader dos_header;
363 E : NtHeaders nt_headers;
364 : if (!dos_header.Init(0, header_block) ||
365 E : !dos_header.Dereference(dos_header->e_lfanew, &nt_headers)) {
366 i : LOG(ERROR) << "Unable to cast image headers.";
367 i : return false;
368 : }
369 :
370 : BlockGraph::Block* image_import_descriptor_block;
371 : IMAGE_DATA_DIRECTORY* import_directory =
372 E : nt_headers->OptionalHeader.DataDirectory + IMAGE_DIRECTORY_ENTRY_IMPORT;
373 E : DCHECK(nt_headers.HasReference(import_directory->VirtualAddress));
374 :
375 E : ImageImportDescriptor image_import_descriptor;
376 : if (!nt_headers.Dereference(import_directory->VirtualAddress,
377 E : &image_import_descriptor)) {
378 : // This could happen if the image import descriptor array is empty, and
379 : // terminated by a *partial* null entry. However, we've not yet seen that.
380 i : LOG(ERROR) << "Failed to dereference Image Import Descriptor Array.";
381 i : return false;
382 : }
383 :
384 E : image_import_descriptor_block = image_import_descriptor.block();
385 :
386 E : ImageImportDescriptor iida;
387 E : if (!iida.Init(0, image_import_descriptor_block)) {
388 i : LOG(ERROR) << "Unable to cast Image Import Descriptor.";
389 i : return false;
390 : }
391 :
392 : // The array is NULL terminated with a potentially incomplete descriptor so
393 : // we can't use ElementCount - 1.
394 E : DCHECK_GT(image_import_descriptor_block->size(), 0U);
395 : size_t descriptor_count =
396 : (common::AlignUp(image_import_descriptor_block->size(),
397 : sizeof(IMAGE_IMPORT_DESCRIPTOR)) /
398 E : sizeof(IMAGE_IMPORT_DESCRIPTOR)) - 1;
399 :
400 E : for (size_t iida_index = 0; iida_index < descriptor_count; ++iida_index) {
401 E : String ref_dll_name;
402 E : if (!iida.Dereference(iida[iida_index].Name, &ref_dll_name)) {
403 i : LOG(ERROR) << "Unable to dereference DLL name.";
404 i : return false;
405 : }
406 :
407 E : size_t max_len = ref_dll_name.ElementCount();
408 : if (base::strncasecmp(ref_dll_name->string, dll_name.data(),
409 E : max_len) == 0) {
410 E : *has_import_entry = true;
411 E : break;
412 : }
413 E : }
414 :
415 E : return true;
416 E : }
417 :
418 : } // namespace pe
|