1 : // Copyright 2013 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 : // Defines the PEHackerApp class, which implements the command-line
16 : // "pehacker" tool.
17 :
18 : #include "syzygy/pehacker/pehacker_app.h"
19 :
20 : #include "base/file_util.h"
21 : #include "base/stringprintf.h"
22 : #include "base/utf_string_conversions.h"
23 : #include "base/json/json_reader.h"
24 : #include "base/strings/string_split.h"
25 : #include "syzygy/block_graph/orderers/original_orderer.h"
26 : #include "syzygy/pdb/pdb_reader.h"
27 : #include "syzygy/pdb/pdb_writer.h"
28 : #include "syzygy/pe/decomposer.h"
29 : #include "syzygy/pe/pe_file_writer.h"
30 : #include "syzygy/pe/pe_relinker_util.h"
31 : #include "syzygy/pehacker/operation.h"
32 : #include "syzygy/pehacker/variables.h"
33 : #include "syzygy/pehacker/operations/add_imports_operation.h"
34 :
35 : namespace pehacker {
36 :
37 : namespace {
38 :
39 : using block_graph::BlockGraph;
40 :
41 : static const char kUsageFormatStr[] = "Usage: %ls [options]\n"
42 : " Required Options:\n"
43 : " --config-file=<path> Path to the configuration file to be used.\n"
44 : " Options:\n"
45 : " -Dvar=val Defines variable 'var' with value 'val'.\n"
46 : " Variable names defined on the command-line\n"
47 : " will be normalized to all lowercase. Values\n"
48 : " will be parsed as JSON.\n"
49 : " --overwrite Allow output files to be overwritten.\n"
50 : " --verbose Log verbosely.\n"
51 : "\n";
52 :
53 : // Gets the value under key |name| in |dictionary|, performing variable
54 : // expansion using |variables|, and finally converting it to a normalized path
55 : // in |path|. If |optional| this will return true if the key doesn't exist
56 : // and leave |path| unchanged. Returns true on success, false otherwise.
57 : bool GetFilePath(bool optional,
58 : const base::DictionaryValue& dictionary,
59 : const base::DictionaryValue& variables,
60 : const std::string& name,
61 E : base::FilePath* path) {
62 E : DCHECK_NE(reinterpret_cast<base::FilePath*>(NULL), path);
63 :
64 : const base::Value* value;
65 E : if (!dictionary.Get(name, &value)) {
66 E : if (optional)
67 E : return true;
68 :
69 i : LOG(ERROR) << "Dictionary does not contain key \"" << name << "\".";
70 i : return false;
71 : }
72 :
73 E : std::string s;
74 E : if (!ConvertVariableToString(*value, &s))
75 i : return false;
76 :
77 E : if (!ExpandVariables(variables, s, &s))
78 i : return false;
79 :
80 E : *path = base::FilePath(base::UTF8ToWide(s)).NormalizePathSeparators();
81 E : VLOG(1) << "Parsed \"" << name << "\" as \"" << path->value() << "\".";
82 E : return true;
83 E : }
84 :
85 E : void RemovePaddingBlocks(BlockGraph* block_graph) {
86 E : DCHECK_NE(reinterpret_cast<BlockGraph*>(NULL), block_graph);
87 E : BlockGraph::BlockMap::iterator it = block_graph->blocks_mutable().begin();
88 E : while (it != block_graph->blocks_mutable().end()) {
89 E : BlockGraph::BlockMap::iterator it_next = it;
90 E : ++it_next;
91 :
92 E : BlockGraph::Block* block = &it->second;
93 E : if (block->attributes() & BlockGraph::PADDING_BLOCK)
94 E : block_graph->RemoveBlock(block);
95 :
96 E : it = it_next;
97 E : }
98 E : }
99 :
100 : } // namespace
101 :
102 i : bool PEHackerApp::ImageId::operator<(const ImageId& rhs) const {
103 i : if (input_module.value() < rhs.input_module.value())
104 i : return true;
105 i : if (input_module.value() > rhs.input_module.value())
106 i : return false;
107 i : return output_module.value() < rhs.output_module.value();
108 i : }
109 :
110 E : bool PEHackerApp::ParseCommandLine(const CommandLine* cmd_line) {
111 E : DCHECK_NE(reinterpret_cast<const CommandLine*>(NULL), cmd_line);
112 :
113 E : if (cmd_line->HasSwitch("help"))
114 E : return Usage(cmd_line, "");
115 :
116 E : if (cmd_line->HasSwitch("verbose")) {
117 i : logging::SetMinLogLevel(logging::LOG_VERBOSE);
118 i : VLOG(1) << "Parsed --verbose switch.";
119 : }
120 :
121 : config_file_ = cmd_line->GetSwitchValuePath("config-file").
122 E : NormalizePathSeparators();
123 E : if (config_file_.empty()) {
124 E : LOG(ERROR) << "Must specify --config-file!";
125 E : return false;
126 : }
127 :
128 E : overwrite_ = cmd_line->HasSwitch("overwrite");
129 E : if (overwrite_) {
130 E : VLOG(1) << "Parsed --overwrite switch.";
131 : }
132 :
133 : // Set built-in variables.
134 E : if (!SetBuiltInVariables())
135 i : return false;
136 :
137 : // Parse any variables defined as arguments.
138 E : VLOG(1) << "Parsing command-line variables.";
139 E : const CommandLine::SwitchMap& switches = cmd_line->GetSwitches();
140 E : CommandLine::SwitchMap::const_iterator it = switches.begin();
141 E : for (; it != switches.end(); ++it) {
142 E : if (it->first[0] != 'd')
143 E : continue;
144 E : const std::wstring wname(it->first.begin() + 1, it->first.end());
145 E : std::string name = base::WideToUTF8(wname);
146 E : std::string value = base::WideToUTF8(it->second);
147 E : if (!ParseVariable(name, value, &variables_))
148 E : return false;
149 E : }
150 :
151 E : return true;
152 E : }
153 :
154 E : int PEHackerApp::Run() {
155 E : if (!LoadAndValidateConfigurationFile())
156 i : return 1;
157 :
158 E : if (!ProcessConfigurationFile(false))
159 i : return 1;
160 :
161 E : if (!WriteImages())
162 i : return 1;
163 :
164 E : return 0;
165 E : }
166 :
167 : bool PEHackerApp::Usage(const CommandLine* cmd_line,
168 E : const base::StringPiece& message) const {
169 E : if (!message.empty()) {
170 i : ::fwrite(message.data(), 1, message.length(), err());
171 i : ::fprintf(err(), "\n\n");
172 : }
173 :
174 : ::fprintf(err(),
175 : kUsageFormatStr,
176 E : cmd_line->GetProgram().BaseName().value().c_str());
177 :
178 E : return false;
179 E : }
180 :
181 E : bool PEHackerApp::SetBuiltInVariables() {
182 E : VLOG(1) << "Setting built-in variables.";
183 E : std::wstring wroot = config_file_.DirName().value();
184 E : std::string root = base::WideToUTF8(wroot);
185 E : variables_.Set("ROOT", new base::StringValue(root));
186 E : return true;
187 E : }
188 :
189 E : bool PEHackerApp::LoadAndValidateConfigurationFile() {
190 : // Parse the configuration file.
191 E : if (!ParseConfigFile())
192 E : return false;
193 :
194 : // Build the variables dictionary.
195 E : if (!UpdateVariablesFromConfig())
196 i : return false;
197 :
198 : // If we're logging verbosely then dump the variables for debugging.
199 E : if (logging::LOG_VERBOSE >= logging::GetMinLogLevel()) {
200 i : base::DictionaryValue::Iterator it(variables_);
201 i : for (; !it.IsAtEnd(); it.Advance()) {
202 i : std::string value;
203 i : ConvertVariableToJson(it.value(), &value);
204 i : VLOG(1) << "Have variable \"" << it.key() << "\" with value "
205 : << value << ".";
206 i : }
207 : }
208 :
209 : // Process the configuration in dry-run mode. This doesn't do any work, but
210 : // validates that the configuration makes sense and can be run.
211 E : if (!ProcessConfigurationFile(true))
212 E : return false;
213 :
214 E : return true;
215 E : }
216 :
217 E : bool PEHackerApp::ParseConfigFile() {
218 E : LOG(INFO) << "Loading configuration file \"" << config_file_.value()
219 : << "\".";
220 :
221 E : VLOG(1) << "Reading configuration file from disk.";
222 E : std::string json;
223 E : if (!file_util::ReadFileToString(config_file_, &json)) {
224 E : LOG(ERROR) << "Unable to read configuration file \""
225 : << config_file_.value() << "\".";
226 E : return false;
227 : }
228 :
229 E : VLOG(1) << "Parsing configuration file contents.";
230 E : scoped_ptr<base::Value> config;
231 E : int error_code = 0;
232 E : std::string error_message;
233 : config.reset(base::JSONReader::ReadAndReturnError(
234 : json,
235 : base::JSON_ALLOW_TRAILING_COMMAS,
236 : &error_code,
237 E : &error_message));
238 E : if (config.get() == NULL) {
239 E : LOG(ERROR) << "Failed to parse configuration file: "
240 : << error_message << "(" << error_code << ").";
241 E : return false;
242 : }
243 :
244 : // Ensure the configuration is a dictionary, and transfer ownership to
245 : // config_ if it is.
246 E : base::DictionaryValue* dict = NULL;
247 E : if (!config->GetAsDictionary(&dict)) {
248 E : LOG(ERROR) << "Configuration must be a dictionary.";
249 E : return false;
250 : }
251 E : config_.reset(dict);
252 E : config.release();
253 :
254 E : return true;
255 E : }
256 :
257 E : bool PEHackerApp::UpdateVariablesFromConfig() {
258 E : base::Value* value = NULL;
259 E : if (!config_->Get("variables", &value))
260 E : return true;
261 :
262 E : base::DictionaryValue* variables = NULL;
263 E : if (!value->GetAsDictionary(&variables)) {
264 i : LOG(ERROR) << "Expect a dictionary for \"variables\".";
265 i : return false;
266 : }
267 :
268 E : VLOG(1) << "Merging configuration variables with command-line variables.";
269 E : if (!MergeVariables(*variables, &variables_))
270 i : return false;
271 E : return true;
272 E : }
273 :
274 E : bool PEHackerApp::ProcessConfigurationFile(bool dry_run) {
275 E : if (dry_run) {
276 E : VLOG(1) << "Validating configuration file.";
277 : }
278 :
279 E : base::ListValue* targets = NULL;
280 E : if (!config_->GetList("targets", &targets)) {
281 i : LOG(ERROR) << "Configuration must contain a \"targets\" list.";
282 i : return false;
283 : }
284 :
285 E : if (!ProcessTargets(dry_run, targets))
286 E : return false;
287 :
288 E : return true;
289 E : }
290 :
291 E : bool PEHackerApp::ProcessTargets(bool dry_run, base::ListValue* targets) {
292 E : DCHECK_NE(reinterpret_cast<base::ListValue*>(NULL), targets);
293 :
294 E : if (targets->GetSize() == 0) {
295 i : LOG(ERROR) << "No targets to process.";
296 i : return false;
297 : }
298 :
299 : // Process the targets in order.
300 E : for (size_t i = 0; i < targets->GetSize(); ++i) {
301 E : base::DictionaryValue* target = NULL;
302 E : if (!targets->GetDictionary(i, &target)) {
303 i : LOG(ERROR) << "Each target must be a dictionary.";
304 i : return false;
305 : }
306 :
307 E : if (!ProcessTarget(dry_run, target))
308 E : return false;
309 E : }
310 :
311 E : return true;
312 E : }
313 :
314 E : bool PEHackerApp::ProcessTarget(bool dry_run, base::DictionaryValue* target) {
315 E : DCHECK_NE(reinterpret_cast<base::DictionaryValue*>(NULL), target);
316 :
317 E : base::FilePath input_module;
318 E : base::FilePath output_module;
319 E : base::FilePath input_pdb;
320 E : base::FilePath output_pdb;
321 E : bool opt = false;
322 E : if (!GetFilePath(opt, *target, variables_, "input_module", &input_module))
323 i : return false;
324 E : if (!GetFilePath(opt, *target, variables_, "output_module", &output_module))
325 i : return false;
326 E : opt = true;
327 E : if (!GetFilePath(opt, *target, variables_, "input_pdb", &input_pdb))
328 i : return false;
329 E : if (!GetFilePath(opt, *target, variables_, "output_pdb", &output_pdb))
330 i : return false;
331 :
332 E : base::ListValue* operations = NULL;
333 E : if (!target->GetList("operations", &operations)) {
334 i : LOG(ERROR) << "Each target must specify an \"operations\" list.";
335 i : return false;
336 : }
337 :
338 : // Validate and infer module-related paths.
339 : if (!pe::ValidateAndInferPaths(
340 E : input_module, output_module, overwrite_, &input_pdb, &output_pdb)) {
341 E : return false;
342 : }
343 :
344 E : ImageInfo* image_info = NULL;
345 E : if (!dry_run) {
346 : // Get the decomposed image.
347 : image_info = GetImageInfo(
348 E : input_module, output_module, input_pdb, output_pdb);
349 E : if (image_info == NULL)
350 i : return false;
351 : }
352 :
353 E : VLOG(1) << "Processing operations for module \"" << input_module.value()
354 : << "\".";
355 E : if (!ProcessOperations(dry_run, operations, image_info))
356 i : return false;
357 :
358 E : return true;
359 E : }
360 :
361 : bool PEHackerApp::ProcessOperations(bool dry_run,
362 : base::ListValue* operations,
363 E : ImageInfo* image_info) {
364 E : DCHECK_NE(reinterpret_cast<base::ListValue*>(NULL), operations);
365 E : if (!dry_run)
366 E : DCHECK_NE(reinterpret_cast<ImageInfo*>(NULL), image_info);
367 :
368 E : for (size_t i = 0; i < operations->GetSize(); ++i) {
369 E : base::DictionaryValue* operation = NULL;
370 E : if (!operations->GetDictionary(i, &operation)) {
371 i : LOG(ERROR) << "Each operation must be a dictionary.";
372 i : return false;
373 : }
374 :
375 E : if (!ProcessOperation(dry_run, operation, image_info))
376 i : return false;
377 E : }
378 :
379 E : return true;
380 E : }
381 :
382 : bool PEHackerApp::ProcessOperation(bool dry_run,
383 : base::DictionaryValue* operation,
384 E : ImageInfo* image_info) {
385 E : DCHECK_NE(reinterpret_cast<base::DictionaryValue*>(NULL), operation);
386 E : if (!dry_run)
387 E : DCHECK_NE(reinterpret_cast<ImageInfo*>(NULL), image_info);
388 :
389 E : std::string type;
390 E : if (!operation->GetString("type", &type)) {
391 i : LOG(ERROR) << "Each operation must specify a \"type\".";
392 i : return false;
393 : }
394 :
395 : // Dispatch to the appropriate operation implementation.
396 E : scoped_ptr<OperationInterface> operation_impl;
397 E : if (type == "none") {
398 : // The 'none' operation is always defined, and does nothing. This is
399 : // mainly there for simple unittesting of configuration files.
400 E : return true;
401 i : } else if (type == "add_imports") {
402 i : operation_impl.reset(new operations::AddImportsOperation());
403 i : } else {
404 i : LOG(ERROR) << "Unrecognized operation type \"" << type << "\".";
405 i : return false;
406 : }
407 :
408 : // Initialize the operation.
409 i : DCHECK_NE(reinterpret_cast<OperationInterface*>(NULL), operation_impl.get());
410 i : if (!operation_impl->Init(&policy_, operation)) {
411 i : LOG(ERROR) << "Failed to initialize \"" << operation_impl->name()
412 : << "\".";
413 i : return false;
414 : }
415 :
416 : // If not in a dry-run then apply the operation.
417 i : if (!dry_run) {
418 i : LOG(INFO) << "Applying operation \"" << type << "\" to \""
419 : << image_info->input_module.value() << "\".";
420 : if (!operation_impl->Apply(&policy_,
421 : &image_info->block_graph,
422 i : image_info->header_block)) {
423 i : LOG(ERROR) << "Failed to apply \"" << operation_impl->name() << "\".";
424 i : return false;
425 : }
426 : }
427 :
428 i : return true;
429 E : }
430 :
431 : PEHackerApp::ImageInfo* PEHackerApp::GetImageInfo(
432 : const base::FilePath& input_module,
433 : const base::FilePath& output_module,
434 : const base::FilePath& input_pdb,
435 E : const base::FilePath& output_pdb) {
436 E : DCHECK(!input_module.empty());
437 E : DCHECK(!output_module.empty());
438 E : DCHECK(!input_pdb.empty());
439 E : DCHECK(!output_pdb.empty());
440 :
441 : // Return the existing module if it exists.
442 E : ImageId image_id = { input_module, output_module };
443 E : ImageInfoMap::iterator it = image_info_map_.find(image_id);
444 E : if (it != image_info_map_.end())
445 i : return it->second;
446 :
447 : // Initialize a new ImageInfo struct.
448 E : scoped_ptr<ImageInfo> image_info(new ImageInfo());
449 E : image_info->input_module = input_module;
450 E : image_info->output_module = output_module;
451 E : image_info->input_pdb = input_pdb;
452 E : image_info->output_pdb = output_pdb;
453 E : if (!image_info->pe_file.Init(input_module)) {
454 i : LOG(ERROR) << "Failed to read image: " << input_module.value();
455 i : return NULL;
456 : }
457 :
458 : // Decompose the image.
459 E : pe::ImageLayout image_layout(&image_info->block_graph);
460 E : pe::Decomposer decomposer(image_info->pe_file);
461 E : if (!decomposer.Decompose(&image_layout)) {
462 i : LOG(ERROR) << "Failed to decompose image: " << input_module.value();
463 i : return NULL;
464 : }
465 :
466 : // Lookup the header block.
467 : image_info->header_block = image_layout.blocks.GetBlockByAddress(
468 E : BlockGraph::RelativeAddress(0));
469 : DCHECK_NE(reinterpret_cast<BlockGraph::Block*>(NULL),
470 E : image_info->header_block);
471 :
472 : // Remove padding blocks. No need to carry these through the pipeline.
473 E : VLOG(1) << "Removing padding blocks.";
474 E : RemovePaddingBlocks(&image_info->block_graph);
475 :
476 : // Get the input range to use in generating OMAP information. This is required
477 : // when finalizing the PDB.
478 E : pe::GetOmapRange(image_layout.sections, &image_info->input_omap_range);
479 :
480 : // Decomposition was successful. Add it to the map, transfer the image info to
481 : // the scoped array and return it.
482 E : it = image_info_map_.insert(std::make_pair(image_id, image_info.get())).first;
483 E : image_infos_.push_back(image_info.release());
484 E : return it->second;
485 E : }
486 :
487 E : bool PEHackerApp::WriteImages() {
488 E : ImageInfoMap::iterator it = image_info_map_.begin();
489 E : for (; it != image_info_map_.end(); ++it) {
490 E : ImageInfo* image_info = it->second;
491 :
492 E : LOG(INFO) << "Finalizing and writing image \""
493 : << image_info->output_module.value() << "\".";
494 :
495 : // Create a GUID for the output PDB.
496 E : GUID pdb_guid = {};
497 E : if (FAILED(::CoCreateGuid(&pdb_guid))) {
498 i : LOG(ERROR) << "Failed to create new GUID for output PDB.";
499 i : return false;
500 : }
501 :
502 : // Finalize the block-graph.
503 E : VLOG(1) << "Finalizing the block-graph.";
504 : if (!pe::FinalizeBlockGraph(image_info->input_module,
505 : image_info->output_module,
506 : pdb_guid,
507 : true,
508 : &policy_,
509 : &image_info->block_graph,
510 E : image_info->header_block)) {
511 i : return false;
512 : }
513 :
514 : // Build the ordered block-graph.
515 : block_graph::OrderedBlockGraph ordered_block_graph(
516 E : &image_info->block_graph);
517 E : block_graph::orderers::OriginalOrderer orderer;
518 E : VLOG(1) << "Ordering the block-graph.";
519 : if (!orderer.OrderBlockGraph(&ordered_block_graph,
520 E : image_info->header_block)) {
521 i : return false;
522 : }
523 :
524 : // Finalize the ordered block-graph.
525 E : VLOG(1) << "Finalizing the ordered block-graph.";
526 : if (!pe::FinalizeOrderedBlockGraph(&ordered_block_graph,
527 E : image_info->header_block)) {
528 i : return false;
529 : }
530 :
531 : // Build the image layout.
532 E : pe::ImageLayout image_layout(&image_info->block_graph);
533 E : VLOG(1) << "Building the image layout.";
534 : if (!pe::BuildImageLayout(0, 1, ordered_block_graph,
535 E : image_info->header_block, &image_layout)) {
536 i : return false;
537 : }
538 :
539 : // Write the image.
540 E : pe::PEFileWriter pe_writer(image_layout);
541 E : VLOG(1) << "Writing image to disk.";
542 E : if (!pe_writer.WriteImage(image_info->output_module))
543 i : return false;
544 :
545 E : LOG(INFO) << "Finalizing and writing PDB file \""
546 : << image_info->output_pdb.value() << "\".";
547 :
548 : // Parse the original PDB.
549 E : pdb::PdbFile pdb_file;
550 E : pdb::PdbReader pdb_reader;
551 E : VLOG(1) << "Reading original PDB.";
552 E : if (!pdb_reader.Read(image_info->input_pdb, &pdb_file))
553 i : return false;
554 :
555 : // Finalize the PDB to reflect the transformed image.
556 E : VLOG(1) << "Finalizing PDB.";
557 : if (!pe::FinalizePdbFile(image_info->input_module,
558 : image_info->output_module,
559 : image_info->input_omap_range,
560 : image_layout,
561 : pdb_guid,
562 : false,
563 : false,
564 : false,
565 E : &pdb_file)) {
566 i : return false;
567 : }
568 :
569 : // Write the PDB.
570 E : pdb::PdbWriter pdb_writer;
571 E : VLOG(1) << "Writing transformed PDB.";
572 E : if (!pdb_writer.Write(image_info->output_pdb, pdb_file))
573 i : return false;
574 E : }
575 :
576 E : return true;
577 E : }
578 :
579 : } // namespace pehacker
|