1 : // Copyright 2012 Google Inc.
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/serialization.h"
16 :
17 : #include "base/bind.h"
18 : #include "base/file_util.h"
19 : #include "syzygy/block_graph/typed_block.h"
20 : #include "syzygy/pe/find.h"
21 : #include "syzygy/pe/image_layout.h"
22 : #include "syzygy/pe/metadata.h"
23 : #include "syzygy/pe/pe_file.h"
24 :
25 : namespace pe {
26 :
27 : namespace {
28 :
29 : using block_graph::BlockGraph;
30 : using block_graph::BlockGraphSerializer;
31 :
32 : // Used for versioning the serialized stream. Be sure to change this if
33 : // non-backwards compatible changes are made to the stream layout.
34 : static const uint32 kSerializedBlockGraphAndImageLayoutVersion = 0;
35 :
36 E : bool MetadataMatchesPEFile(const Metadata& metadata, const PEFile& pe_file) {
37 E : PEFile::Signature pe_signature;
38 E : pe_file.GetSignature(&pe_signature);
39 :
40 E : if (!metadata.IsConsistent(pe_signature))
41 i : return false;
42 :
43 E : return true;
44 E : }
45 :
46 E : bool FindPEFile(const Metadata& metadata, PEFile* pe_file) {
47 E : DCHECK(pe_file != NULL);
48 :
49 E : LOG(INFO) << "Searching for module to use in deserialization.";
50 :
51 : // We search for a PE file in the following sequence:
52 : // (1) If pe_file is already initialized, try to use it.
53 : // (2) Look for a PE file using the path stored in metadata.
54 : // (3) Search for a matching PE file in the already initialized pe_file
55 : // directory (if provided), and the metadata directory.
56 : // (4) Search for a matching PE file using a system wide search.
57 E : std::wstring search_path;
58 :
59 : // Approach 1: If we already have a PE file initialized, see if it matches the
60 : // signature of the one we serialized.
61 E : if (!pe_file->path().empty()) {
62 i : LOG(INFO) << "Attempting to use provided module in deserialization: "
63 : << pe_file->path().value();
64 :
65 i : if (MetadataMatchesPEFile(metadata, *pe_file))
66 i : return true;
67 :
68 : // Save the directory of the provided PE file in the search path.
69 i : search_path.append(pe_file->path().DirName().value());
70 i : search_path.append(L";");
71 i : LOG(WARNING) << "Metadata signature does not match provided module: "
72 : << pe_file->path().value();
73 : }
74 :
75 : // Approach 2: Try to use the path provided in the metadata itself.
76 E : FilePath metadata_path(metadata.module_signature().path);
77 E : LOG(INFO) << "Attempting to use metadata path in deserialization: "
78 : << metadata_path.value();
79 E : if (!file_util::PathExists(metadata_path) || !pe_file->Init(metadata_path)) {
80 i : LOG(WARNING) << "Unable to read module:" << metadata_path.value();
81 i : } else {
82 E : if (MetadataMatchesPEFile(metadata, *pe_file))
83 E : return true;
84 :
85 : // Append the directory to the search path if it exists.
86 i : FilePath dir = metadata_path.DirName();
87 i : if (file_util::DirectoryExists(dir))
88 i : search_path.append(metadata_path.DirName().value());
89 :
90 i : LOG(WARNING) << "Metadata signature does not match metadata module: "
91 : << metadata_path.value();
92 i : }
93 :
94 i : FilePath module_path;
95 :
96 : // Approach 3: Use an explicit search in the provided paths.
97 i : if (!search_path.empty()) {
98 i : LOG(INFO) << "Searching for module in provided paths: " << search_path;
99 : if (!FindModuleBySignature(metadata.module_signature(),
100 : search_path.c_str(),
101 i : &module_path)) {
102 i : LOG(WARNING) << "FindModuleBySignature failed.";
103 : }
104 : }
105 :
106 : // Approach 4: Do a system-wide search.
107 i : if (module_path.empty()) {
108 i : LOG(INFO) << "Searching for module using system paths.";
109 : if (!FindModuleBySignature(metadata.module_signature(),
110 i : &module_path)) {
111 i : LOG(ERROR) << "FindModuleBySignature failed.";
112 i : return false;
113 : }
114 : }
115 :
116 : // No module found in either of the above two searches?
117 i : if (module_path.empty()) {
118 i : LOG(ERROR) << "No module found in FindModuleBySignature.";
119 i : return false;
120 : }
121 :
122 : // If we get here, we've found a module. However, we don't just accept that
123 : // fact.
124 :
125 i : if (!pe_file->Init(module_path)) {
126 i : LOG(ERROR) << "Failed to read module: " << module_path.value();
127 i : return false;
128 : }
129 :
130 i : if (!MetadataMatchesPEFile(metadata, *pe_file)) {
131 i : LOG(ERROR) << "Metadata signature does not match found module: "
132 : << module_path.value();
133 i : return false;
134 : }
135 :
136 i : LOG(INFO) << "Found module with matching signature: " << module_path.value();
137 :
138 i : return true;
139 E : }
140 :
141 : // This callback is used to save the data in a block by simply savings its
142 : // address in the image-layout.
143 : bool SaveBlockData(const ImageLayout* image_layout,
144 : bool data_already_saved,
145 : const BlockGraph::Block& block,
146 E : core::OutArchive* out_archive) {
147 E : DCHECK(image_layout != NULL);
148 E : DCHECK(out_archive != NULL);
149 :
150 : // We're always in OUTPUT_NO_DATA mode, so either the data hasn't yet been
151 : // saved or there was no data to save.
152 E : DCHECK(block.data_size() == 0 || !data_already_saved);
153 :
154 E : core::RelativeAddress block_addr;
155 E : if (!image_layout->blocks.GetAddressOf(&block, &block_addr)) {
156 i : LOG(ERROR) << "Block with id " << block.id() << " not in image-layout.";
157 i : return false;
158 : }
159 :
160 : // Save the address of the block wrt to the provided image-layout. This will
161 : // be sufficient for us to lookup the block data in the PE file afterwards.
162 E : if (!out_archive->Save(block_addr)) {
163 i : LOG(ERROR) << "Unable to save address of block with id " << block.id()
164 : << ".";
165 i : return false;
166 : }
167 :
168 E : return true;
169 E : }
170 :
171 : // This callback is used to load the data in a block. It also simultaneously
172 : // constructs the image-layout.
173 : bool LoadBlockData(const PEFile* pe_file,
174 : ImageLayout* image_layout,
175 : bool need_to_set_data,
176 : size_t data_size,
177 : BlockGraph::Block* block,
178 E : core::InArchive* in_archive) {
179 E : DCHECK(pe_file != NULL);
180 E : DCHECK(image_layout != NULL);
181 E : DCHECK(block != NULL);
182 E : DCHECK(in_archive != NULL);
183 :
184 E : core::RelativeAddress block_addr;
185 E : if (!in_archive->Load(&block_addr)) {
186 i : LOG(ERROR) << "Unable to load address in image-layout of block with id "
187 : << block->id() << ".";
188 i : return false;
189 : }
190 :
191 : // Insert the block in the image layout.
192 E : if (!image_layout->blocks.InsertBlock(block_addr, block)) {
193 i : LOG(ERROR) << "Unable to insert block with id " << block->id() << " into "
194 : << "image-layout.";
195 i : return false;
196 : }
197 :
198 : // If we have no data in this block then there's no need to load any.
199 E : if (data_size == 0)
200 E : return true;
201 :
202 : // We're in OUTPUT_NO_DATA mode, so we should always be responsible for
203 : // setting the block data.
204 E : DCHECK(need_to_set_data);
205 E : DCHECK_EQ(0u, block->data_size());
206 E : DCHECK(block->data() == NULL);
207 :
208 E : const uint8* data = pe_file->GetImageData(block_addr, data_size);
209 E : if (data == NULL) {
210 i : LOG(ERROR) << "Unable to get data from PE file for block with id "
211 : << block->id() << ".";
212 i : return false;
213 : }
214 :
215 E : block->SetData(data, data_size);
216 :
217 E : return true;
218 E : }
219 :
220 : bool LoadBlockGraphAndImageLayout(
221 : const PEFile& pe_file,
222 : PEFile* pe_file_ptr,
223 : block_graph::BlockGraphSerializer::Attributes* attributes,
224 : ImageLayout* image_layout,
225 E : core::InArchive* in_archive) {
226 E : DCHECK(pe_file_ptr == NULL || pe_file_ptr == &pe_file);
227 E : DCHECK(image_layout != NULL);
228 E : DCHECK(in_archive != NULL);
229 :
230 E : BlockGraph* block_graph = image_layout->blocks.graph();
231 :
232 : // Load and check the stream version. This is where we could dispatch to
233 : // different handlers for old versions of the stream if we wish to maintain
234 : // backwards compatibility.
235 E : uint32 stream_version = 0;
236 E : if (!in_archive->Load(&stream_version)) {
237 i : LOG(ERROR) << "Unable to load serialized stream version.";
238 i : return false;
239 : }
240 E : if (stream_version != kSerializedBlockGraphAndImageLayoutVersion) {
241 E : LOG(ERROR) << "Invalid stream version " << stream_version << ", expected "
242 : << kSerializedBlockGraphAndImageLayoutVersion << ".";
243 E : return false;
244 : }
245 :
246 : // Load the metadata.
247 E : Metadata metadata;
248 E : if (!in_archive->Load(&metadata)) {
249 i : LOG(ERROR) << "Unable to load metadata.";
250 i : return false;
251 : }
252 :
253 E : if (pe_file_ptr != NULL) {
254 : // If we've been given a modifiable PE-file, then we can be more intelligent
255 : // about our search. This call logs verbosely on failure so we don't have
256 : // to.
257 E : if (!FindPEFile(metadata, pe_file_ptr))
258 i : return false;
259 E : } else {
260 E : if (!MetadataMatchesPEFile(metadata, pe_file)) {
261 i : LOG(ERROR) << "Provided PE file does not match signature in serialized "
262 : << "stream.";
263 i : return false;
264 : }
265 : }
266 :
267 : // Set up the serializer.
268 E : BlockGraphSerializer bgs;
269 : bgs.set_load_block_data_callback(
270 : base::Bind(&LoadBlockData,
271 : base::Unretained(&pe_file),
272 E : base::Unretained(image_layout)));
273 :
274 : // Now deserialize the block-graph. This will simultaneously deserialize the
275 : // image-layout address-space.
276 E : if (!bgs.Load(block_graph, in_archive)) {
277 i : LOG(ERROR) << "Unable to load block-graph.";
278 i : return false;
279 : }
280 :
281 : // Return the attributes if asked to.
282 E : if (attributes != NULL)
283 E : *attributes = bgs.attributes();
284 :
285 : // We can now recreate the rest of the image-layout from the block-graph.
286 : // Start by retrieving the DOS header block, which is always at the start of
287 : // the image.
288 : BlockGraph::Block* dos_header_block =
289 E : image_layout->blocks.GetBlockByAddress(core::RelativeAddress());
290 E : if (dos_header_block == NULL) {
291 i : LOG(ERROR) << "Unable to find DOS header in image-layout address-space.";
292 i : return false;
293 : }
294 :
295 : // Cast this as an IMAGE_DOS_HEADER.
296 E : block_graph::ConstTypedBlock<IMAGE_DOS_HEADER> dos_header;
297 E : if (!dos_header.Init(0, dos_header_block)) {
298 i : LOG(ERROR) << "Unable to cast DOS header block to IMAGE_DOS_HEADER.";
299 i : return false;
300 : }
301 :
302 : // Get the NT headers.
303 E : block_graph::ConstTypedBlock<IMAGE_NT_HEADERS> nt_headers;
304 E : if (!dos_header.Dereference(dos_header->e_lfanew, &nt_headers)) {
305 i : LOG(ERROR) << "Unable to dereference NT headers from DOS header.";
306 i : return false;
307 : }
308 :
309 : // Finally, use these headers to populate the section info vector of the
310 : // image-layout.
311 E : if (!CopyHeaderToImageLayout(nt_headers.block(), image_layout)) {
312 i : LOG(ERROR) << "Unable to copy NT headers to image-layout.";
313 i : return false;
314 : }
315 :
316 E : return true;
317 E : }
318 :
319 : } // namespace
320 :
321 : bool SaveBlockGraphAndImageLayout(
322 : const PEFile& pe_file,
323 : block_graph::BlockGraphSerializer::Attributes attributes,
324 : const ImageLayout& image_layout,
325 E : core::OutArchive* out_archive) {
326 E : DCHECK(out_archive != NULL);
327 :
328 E : const BlockGraph& block_graph = *image_layout.blocks.graph();
329 :
330 E : if (!out_archive->Save(kSerializedBlockGraphAndImageLayoutVersion)) {
331 i : LOG(ERROR) << "Unable to save serialized stream version.";
332 i : return false;
333 : }
334 :
335 : // Get the metadata for this module and the toolchain. This will
336 : // allow us to validate input files in other pieces of the toolchain.
337 E : Metadata metadata;
338 E : PEFile::Signature pe_file_signature;
339 E : pe_file.GetSignature(&pe_file_signature);
340 E : if (!metadata.Init(pe_file_signature)) {
341 i : LOG(ERROR) << "Unable to initialize metadata for PE file \""
342 : << pe_file.path().value() << "\".";
343 i : return false;
344 : }
345 :
346 : // Save the metadata.
347 E : if (!out_archive->Save(metadata)) {
348 i : LOG(ERROR) << "Unable to save metadata for PE file \""
349 : << pe_file.path().value() << "\".";
350 i : return false;
351 : }
352 :
353 : // Initialize the serializer. We don't save any of the data because it can all
354 : // be retrieved from the PE file.
355 E : BlockGraphSerializer bgs;
356 E : bgs.set_data_mode(BlockGraphSerializer::OUTPUT_NO_DATA);
357 E : bgs.set_attributes(attributes);
358 : bgs.set_save_block_data_callback(base::Bind(
359 : &SaveBlockData,
360 E : base::Unretained(&image_layout)));
361 :
362 : // Write the block-graph. This also simultaneously serializes the
363 : // address-space portion of the image-layout.
364 E : if (!bgs.Save(block_graph, out_archive)) {
365 i : LOG(ERROR) << "Unable to save block-graph.";
366 i : return false;
367 : }
368 :
369 E : return true;
370 E : }
371 :
372 : bool LoadBlockGraphAndImageLayout(
373 : const PEFile& pe_file,
374 : block_graph::BlockGraphSerializer::Attributes* attributes,
375 : ImageLayout* image_layout,
376 E : core::InArchive* in_archive) {
377 : if (!LoadBlockGraphAndImageLayout(pe_file, NULL, attributes,
378 E : image_layout, in_archive)) {
379 i : return false;
380 : }
381 :
382 E : return true;
383 E : }
384 :
385 : bool LoadBlockGraphAndImageLayout(
386 : PEFile* pe_file,
387 : block_graph::BlockGraphSerializer::Attributes* attributes,
388 : ImageLayout* image_layout,
389 E : core::InArchive* in_archive) {
390 E : DCHECK(pe_file != NULL);
391 E : DCHECK(image_layout != NULL);
392 E : DCHECK(in_archive != NULL);
393 :
394 : if (!LoadBlockGraphAndImageLayout(*pe_file, pe_file, attributes,
395 E : image_layout, in_archive)) {
396 E : return false;
397 : }
398 :
399 E : return true;
400 E : }
401 :
402 : } // namespace pe
|