1 : // Copyright 2014 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 SwapImportApp class, which implements the command-line
16 : // "swapimport" tool.
17 :
18 : #include "syzygy/swapimport/swapimport_app.h"
19 :
20 : #include "base/files/file_util.h"
21 : #include "base/strings/string_util.h"
22 : #include "base/strings/utf_string_conversions.h"
23 : #include "syzygy/core/file_util.h"
24 : #include "syzygy/pe/pe_file.h"
25 : #include "syzygy/pe/pe_file_writer.h"
26 :
27 : namespace swapimport {
28 :
29 : namespace {
30 :
31 : static const char kUsageFormatStr[] = "Usage: %ls [options] IMPORT\n"
32 : " Required Options:\n"
33 : " --input-image=PATH Path of the input image.\n"
34 : " --output-image=PATH Path where the output image will be written.\n"
35 : " The generated image will still be paired to\n"
36 : " the original PDB file.\n"
37 : " --x64 Decompose a 64-bit binary rather than a\n"
38 : " 32-bit one.\n"
39 : " Options:\n"
40 : " --overwrite Allow output files to be overwritten.\n"
41 : " --verbose Log verbosely.\n"
42 : "\n";
43 :
44 : } // namespace
45 :
46 E : bool SwapImportApp::ParseCommandLine(const base::CommandLine* cmd_line) {
47 E : DCHECK_NE(reinterpret_cast<const base::CommandLine*>(NULL), cmd_line);
48 :
49 E : if (cmd_line->HasSwitch("help"))
50 E : return Usage(cmd_line, "");
51 :
52 E : if (cmd_line->HasSwitch("verbose")) {
53 E : logging::SetMinLogLevel(logging::LOG_VERBOSE);
54 E : VLOG(1) << "Parsed --verbose switch.";
55 E : } else {
56 E : logging::SetMinLogLevel(logging::LOG_ERROR);
57 : }
58 :
59 E : input_image_ = cmd_line->GetSwitchValuePath("input-image");
60 E : if (input_image_.empty()) {
61 E : LOG(ERROR) << "Must specify --input-image!";
62 E : return false;
63 : }
64 :
65 E : output_image_ = cmd_line->GetSwitchValuePath("output-image");
66 E : if (output_image_.empty()) {
67 E : LOG(ERROR) << "Must specify --output-image!";
68 E : return false;
69 : }
70 :
71 E : overwrite_ = cmd_line->HasSwitch("overwrite");
72 E : if (overwrite_)
73 E : VLOG(1) << "Parsed --overwrite switch.";
74 :
75 E : base::CommandLine::StringVector args = cmd_line->GetArgs();
76 E : if (args.size() != 1) {
77 E : LOG(ERROR) << "Expect exactly one import name.";
78 E : return false;
79 : }
80 E : import_name_ = base::WideToUTF8(args[0]);
81 :
82 E : x64_ = cmd_line->HasSwitch("x64");
83 E : if (x64_)
84 E : VLOG(1) << "Parsed --x64 switch.";
85 :
86 E : return true;
87 E : }
88 :
89 : template <typename PEFileType>
90 E : int SwapImportApp::SwapImports() {
91 : // Parse the input file as a PE image.
92 E : PEFileType pe_file;
93 E : if (!pe_file.Init(input_image_)) {
94 E : LOG(ERROR) << "Failed to parse image as a PE file: "
95 : << input_image_.value();
96 E : return 1;
97 : }
98 :
99 : // Read the entire input into memory.
100 E : VLOG(1) << "Reading \"" << input_image_.value() << "\" into memory.";
101 E : int64 image_size = 0;
102 E : if (!base::GetFileSize(input_image_, &image_size)) {
103 i : LOG(ERROR) << "Failed to get image size: " << input_image_.value();
104 i : return 1;
105 : }
106 E : std::vector<unsigned char> image(image_size, 0);
107 : if (!base::ReadFile(input_image_,
108 : reinterpret_cast<char*>(image.data()),
109 E : image_size)) {
110 i : LOG(ERROR) << "Failed to read image to memory: " << input_image_.value();
111 i : return 1;
112 : }
113 :
114 : // Keeps track of matched imports, and how many have been swapped.
115 E : size_t imports_swapped = 0;
116 E : size_t imports_matched = 0;
117 :
118 : // Look up the import directory.
119 E : LOG(INFO) << "Processing NT headers.";
120 : const IMAGE_DATA_DIRECTORY* data_dir =
121 : pe_file.nt_headers()->OptionalHeader.DataDirectory +
122 E : IMAGE_DIRECTORY_ENTRY_IMPORT;
123 E : if (data_dir->Size != 0) {
124 E : LOG(INFO) << "Processing imports.";
125 :
126 E : pe::PEFile::FileOffsetAddress import_offset;
127 : if (!pe_file.Translate(
128 : pe::PEFile::RelativeAddress(data_dir->VirtualAddress),
129 E : &import_offset)) {
130 i : LOG(ERROR) << "Failed to translate import directory address.";
131 i : return 1;
132 : }
133 :
134 : // Walk over the imports.
135 : IMAGE_IMPORT_DESCRIPTOR* imports_begin =
136 : reinterpret_cast<IMAGE_IMPORT_DESCRIPTOR*>(
137 E : image.data() + import_offset.value());
138 : IMAGE_IMPORT_DESCRIPTOR* imports_end =
139 : reinterpret_cast<IMAGE_IMPORT_DESCRIPTOR*>(
140 E : image.data() + import_offset.value() + data_dir->Size);
141 E : IMAGE_IMPORT_DESCRIPTOR* imports = imports_begin;
142 E : size_t import_index = 0;
143 E : while (imports < imports_end && imports->Characteristics != 0) {
144 : // Look up the import name.
145 E : pe::PEFile::FileOffsetAddress name_offset;
146 : if (!pe_file.Translate(
147 : pe::PEFile::RelativeAddress(imports->Name),
148 E : &name_offset)) {
149 i : LOG(ERROR) << "Failed to translate import name.";
150 i : return 1;
151 : }
152 :
153 : // Compare the import name.
154 : const char* name = reinterpret_cast<const char*>(
155 E : image.data() + name_offset.value());
156 E : VLOG(1) << "Processing import " << import_index << " \""
157 : << name << "\".";
158 E : if (base::CompareCaseInsensitiveASCII(import_name_.c_str(), name) == 0) {
159 E : VLOG(1) << "Import " << import_index << " matches import name.";
160 E : ++imports_matched;
161 :
162 E : if (import_index > imports_swapped) {
163 : // Do the actual swapping of the imports.
164 E : LOG(INFO) << "Swapping imports " << imports_swapped << " and "
165 : << import_index;
166 :
167 E : IMAGE_IMPORT_DESCRIPTOR temp_iid = *imports;
168 E : *imports = imports_begin[imports_swapped];
169 E : imports_begin[imports_swapped] = temp_iid;
170 :
171 E : ++imports_swapped;
172 : }
173 : }
174 :
175 E : ++imports;
176 E : ++import_index;
177 E : }
178 : }
179 :
180 : // We expect to have matched the specified import at least once.
181 E : if (imports_matched == 0) {
182 E : LOG(ERROR) << "Did not find an import matching \"" << import_name_ << "\".";
183 E : return 1;
184 : }
185 :
186 : // Write the actual output.
187 E : LOG(INFO) << "Writing output to \"" << output_image_.value() << "\".";
188 E : base::ScopedFILE output(base::OpenFile(output_image_, "wb"));
189 E : if (output.get() == NULL) {
190 i : LOG(ERROR) << "Failed to open \"" << output_image_.value() << "\" for "
191 : << "writing.";
192 i : return 1;
193 : }
194 : if (::fwrite(image.data(), sizeof(image[0]), image.size(), output.get()) !=
195 E : image.size()) {
196 i : LOG(ERROR) << "Failed to write output: " << output_image_.value();
197 i : return 1;
198 : }
199 E : output.reset();
200 :
201 : // Finalize the image by updating the checksum.
202 E : LOG(INFO) << "Updating output image checksum.";
203 E : if (!pe::PEFileWriter::UpdateFileChecksum(output_image_)) {
204 i : LOG(ERROR) << "Failed to update image checksum.";
205 i : return 1;
206 : }
207 :
208 E : return 0;
209 E : }
210 :
211 E : int SwapImportApp::Run() {
212 : // Check the input.
213 E : if (!base::PathExists(input_image_)) {
214 i : LOG(ERROR) << "Path does not exist: " << input_image_.value();
215 i : return 1;
216 : }
217 :
218 : // Check the output unless we're overwriting.
219 E : if (!overwrite_) {
220 E : if (base::PathExists(output_image_)) {
221 E : LOG(ERROR) << "Output path exists: " << output_image_.value();
222 E : LOG(ERROR) << "Did you mean to specify --overwrite?";
223 E : return 1;
224 : }
225 :
226 : core::FilePathCompareResult result = core::CompareFilePaths(
227 E : input_image_, output_image_);
228 E : if (result == core::kEquivalentFilePaths) {
229 i : LOG(ERROR) << "Output image path equivalent to input image path.";
230 i : return 1;
231 : }
232 : }
233 :
234 E : if (x64_) {
235 E : return SwapImports<pe::PEFile64>();
236 i : } else {
237 E : return SwapImports<pe::PEFile>();
238 : }
239 E : }
240 :
241 : bool SwapImportApp::Usage(const base::CommandLine* cmd_line,
242 E : const base::StringPiece& message) const {
243 E : if (!message.empty()) {
244 i : ::fwrite(message.data(), 1, message.length(), err());
245 i : ::fprintf(err(), "\n\n");
246 : }
247 :
248 : ::fprintf(err(),
249 : kUsageFormatStr,
250 E : cmd_line->GetProgram().BaseName().value().c_str());
251 :
252 E : return false;
253 E : }
254 :
255 : } // namespace swapimport
|