blob: a92899229d873c355b9083a74ee89fffa0b98523 [file] [log] [blame]
// Copyright 2013 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "syzygy/pehacker/operations/add_imports_operation.h"
#include "syzygy/block_graph/transform.h"
namespace pehacker {
namespace operations {
namespace {
using pe::transforms::ImportedModule;
static const char kFunctionName[] = "function_name";
static const char kMustNotExist[] = "must_not_exist";
static const char kOrdinal[] = "ordinal";
// A simple struct for representing content parsed from an 'add_import'
// operation.
struct ImportInfo {
static const int kUnusedOrdinal = -1;
std::string function_name; // Empty if ordinal is being used.
int ordinal; // -1 if name is being used.
bool must_not_exist;
};
// Parses a dictionary describing an import.
bool ParseImport(const base::DictionaryValue* import, ImportInfo* import_info) {
DCHECK_NE(reinterpret_cast<const base::DictionaryValue*>(NULL), import);
DCHECK_NE(reinterpret_cast<ImportInfo*>(NULL), import_info);
bool have_function_name = import->HasKey(kFunctionName);
bool have_ordinal = import->HasKey(kOrdinal);
if (have_function_name && have_ordinal) {
LOG(ERROR) << "Only one of \"" << kFunctionName << "\" or \""
<< kOrdinal << "\" may be defined in an import.";
return false;
}
std::string function_name;
if (have_function_name && !import->GetString(kFunctionName, &function_name)) {
LOG(ERROR) << "\"" << kFunctionName << "\" must be a string.";
return false;
}
int ordinal = -1;
if (have_ordinal && !import->GetInteger(kOrdinal, &ordinal)) {
LOG(ERROR) << "\"" << kOrdinal << "\" must be an integer.";
return false;
}
bool must_not_exist = false;
if (import->HasKey(kMustNotExist) &&
!import->GetBoolean(kMustNotExist, &must_not_exist)) {
LOG(ERROR) << "\"" << kMustNotExist << "\" must be a boolean.";
return false;
}
import_info->function_name = function_name;
import_info->ordinal = ordinal;
import_info->must_not_exist = must_not_exist;
return true;
}
} // namespace
const char AddImportsOperation::kName[] = "AddImportsOperation";
const char* AddImportsOperation::name() const {
return kName;
}
bool AddImportsOperation::Init(const TransformPolicyInterface* policy,
const base::DictionaryValue* operation) {
DCHECK_NE(reinterpret_cast<TransformPolicyInterface*>(NULL), policy);
DCHECK_NE(reinterpret_cast<base::DictionaryValue*>(NULL), operation);
const base::ListValue* modules = NULL;
if (!operation->GetList("modules", &modules)) {
LOG(ERROR) << "Operation \"add_imports\" must contain a list of "
<< "\"modules\".";
return false;
}
// Iterate over the modules to be imported.
for (size_t i = 0; i < modules->GetSize(); ++i) {
const base::DictionaryValue* module = NULL;
if (!modules->GetDictionary(i, &module)) {
LOG(ERROR) << "Each module must be a dictionary.";
return false;
}
std::string module_name;
if (!module->GetString("module_name", &module_name) ||
module_name.empty()) {
LOG(ERROR) << "Each module must contain a \"module_name\".";
return false;
}
const base::ListValue* imports = NULL;
if (!module->GetList("imports", &imports) || imports->empty()) {
LOG(ERROR) << "Each module must contain a list of \"imports\".";
return false;
}
bool must_not_exist = false;
if (module->HasKey(kMustNotExist) &&
!module->GetBoolean(kMustNotExist, &must_not_exist)) {
LOG(ERROR) << "\"" << kMustNotExist << "\" must be a boolean.";
return false;
}
// Get the imported module with this name. ImportModule objects aren't
// copyable so we have to go out of our way to build a map of them.
ImportedModuleMap::iterator mod_it =
imported_module_map_.find(module_name);
if (mod_it == imported_module_map_.end()) {
ImportedModule* imported_module = new ImportedModule(module_name);
imported_modules_.push_back(imported_module);
mod_it = imported_module_map_.insert(std::make_pair(
module_name, imported_module)).first;
}
// Iterate over the imports to be added from this module.
for (size_t j = 0; j < imports->GetSize(); ++j) {
const base::DictionaryValue* import = NULL;
if (!imports->GetDictionary(j, &import)) {
LOG(ERROR) << "Each import must be a dictionary.";
return false;
}
ImportInfo import_info = {};
if (!ParseImport(import, &import_info))
return false;
if (import_info.ordinal != ImportInfo::kUnusedOrdinal) {
// TODO(chrisha): Add support for imports by ordinal.
LOG(ERROR) << "Imports by ordinal are not currently supported.";
return false;
}
// TODO(chrisha): Add support for a kMustImport mode.
if (must_not_exist) {
LOG(WARNING) << "The directive \"" << kMustNotExist << "\" is not yet "
<< "supported.";
}
// Configure the import.
VLOG(1) << "Parsed import \"" << module_name << ":"
<< import_info.function_name << "\".";
mod_it->second->AddSymbol(
import_info.function_name, ImportedModule::kAlwaysImport);
}
}
// Configure the transform itself.
ImportedModuleMap::iterator it = imported_module_map_.begin();
for (; it != imported_module_map_.end(); ++it)
add_imports_tx_.AddModule(it->second);
return true;
}
bool AddImportsOperation::Apply(const TransformPolicyInterface* policy,
BlockGraph* block_graph,
BlockGraph::Block* header_block) {
DCHECK_NE(reinterpret_cast<TransformPolicyInterface*>(NULL), policy);
DCHECK_NE(reinterpret_cast<BlockGraph*>(NULL), block_graph);
DCHECK_NE(reinterpret_cast<BlockGraph::Block*>(NULL), header_block);
// We pass our call through the unittesting seam so that we don't have to
// actually run the transform on a decomposed image in our tests.
VLOG(1) << "Applying \"" << add_imports_tx_.name() << "\" transform.";
if (!ApplyTransform(&add_imports_tx_,
policy,
block_graph,
header_block)) {
return false;
}
return true;
}
bool AddImportsOperation::ApplyTransform(
block_graph::BlockGraphTransformInterface* tx,
const TransformPolicyInterface* policy,
BlockGraph* block_graph,
BlockGraph::Block* header_block) {
DCHECK_NE(reinterpret_cast<block_graph::BlockGraphTransformInterface*>(NULL),
tx);
DCHECK_NE(reinterpret_cast<TransformPolicyInterface*>(NULL), policy);
DCHECK_NE(reinterpret_cast<BlockGraph*>(NULL), block_graph);
DCHECK_NE(reinterpret_cast<BlockGraph::Block*>(NULL), header_block);
if (!block_graph::ApplyBlockGraphTransform(tx,
policy,
block_graph,
header_block)) {
return false;
}
return true;
}
} // namespace operations
} // namespace pehacker