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