1 : // Copyright 2012 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 : #include "syzygy/relink/relink_app.h"
16 :
17 : #include "base/string_number_conversions.h"
18 : #include "syzygy/block_graph/orderers/original_orderer.h"
19 : #include "syzygy/block_graph/orderers/random_orderer.h"
20 : #include "syzygy/block_graph/transforms/fuzzing_transform.h"
21 : #include "syzygy/pe/pe_relinker.h"
22 : #include "syzygy/pe/transforms/explode_basic_blocks_transform.h"
23 : #include "syzygy/reorder/orderers/explicit_orderer.h"
24 : #include "syzygy/reorder/transforms/basic_block_layout_transform.h"
25 :
26 : namespace relink {
27 :
28 : namespace {
29 :
30 : const char kUsageFormatStr[] =
31 : "Usage: %ls [options]\n"
32 : " Required Options:\n"
33 : " --input-image=<path> The input image file to relink.\n"
34 : " --output-image=<path> Output path for the rewritten image file.\n"
35 : " Options:\n"
36 : " --basic-blocks Reorder at the basic-block level. At present,\n"
37 : " this is only supported for random reorderings.\n"
38 : " --code-alignment=<integer>\n"
39 : " Force a minimal alignment for code blocks.\n"
40 : " Default value is 1.\n"
41 : " --compress-pdb If --no-augment-pdb is specified, causes the\n"
42 : " augmented PDB stream to be compressed.\n"
43 : " --exclude-bb-padding When randomly reordering basic blocks, exclude\n"
44 : " padding and unreachable code from the relinked\n"
45 : " output binary.\n"
46 : " --input-pdb=<path> The PDB file associated with the input DLL.\n"
47 : " Default is inferred from input-image.\n"
48 : " --no-augment-pdb Indicates that the relinker should not augment\n"
49 : " the PDB with roundtrip decomposition info.\n"
50 : " --no-metadata Prevents the relinker from adding metadata\n"
51 : " to the output DLL.\n"
52 : " --no-strip-strings Causes strings to be output in the augmented\n"
53 : " PDB stream. The default is to omit these to\n"
54 : " make smaller PDBs.\n"
55 : " --old-decomposer Use the old decomposer.\n"
56 : " --order-file=<path> Reorder based on a JSON ordering file.\n"
57 : " --output-pdb=<path> Output path for the rewritten PDB file.\n"
58 : " Default is inferred from output-image.\n"
59 : " --overwrite Allow output files to be overwritten.\n"
60 : " --padding=<integer> Add bytes of padding between blocks.\n"
61 : " --verbose Log verbosely.\n"
62 : "\n"
63 : " Testing Options:\n"
64 : " --fuzz Fuzz the binary.\n"
65 : " --seed=<integer> Randomly reorder based on the given seed.\n"
66 : "\n"
67 : " Deprecated Options:\n"
68 : " --input-dll=<path> Aliased to --input-image.\n"
69 : " --output-dll=<path> Aliased to --output-image.\n"
70 : "\n"
71 : " Notes:\n"
72 : " * The --seed and --order-file options are mutually exclusive\n"
73 : " * If --order-file is specified, --input-image is optional.\n"
74 : " * The --compress-pdb and --no-strip-strings options are only\n"
75 : " effective if --no-augment-pdb is not specified.\n"
76 : " * The --exclude-bb-padding option is only effective if\n"
77 : " --basic-blocks is specified.\n";
78 :
79 E : bool ParseUInt32(const std::wstring& value_str, uint32* out_value) {
80 E : DCHECK(out_value != NULL);
81 : unsigned temp;
82 E : if (!base::StringToUint(value_str, &temp))
83 i : return false;
84 E : *out_value = temp;
85 E : return true;
86 E : }
87 :
88 : void GuessPdbPath(const base::FilePath& module_path, base::FilePath* pdb_path) {
89 : DCHECK(pdb_path != NULL);
90 : *pdb_path = module_path.ReplaceExtension(L"pdb");
91 : }
92 :
93 : } // namespace
94 :
95 E : bool RelinkApp::ParseCommandLine(const CommandLine* cmd_line) {
96 E : if (cmd_line->HasSwitch("help"))
97 E : return Usage(cmd_line, "");
98 :
99 E : input_image_path_ = AbsolutePath(cmd_line->GetSwitchValuePath("input-image"));
100 E : if (cmd_line->HasSwitch("input-dll")) {
101 E : if (input_image_path_.empty()) {
102 E : LOG(WARNING) << "Using deprecated switch --input-dll.";
103 : input_image_path_ =
104 E : AbsolutePath(cmd_line->GetSwitchValuePath("input-dll"));
105 E : } else {
106 : return Usage(cmd_line,
107 E : "Can't specify both --input-dll and --input-image.");
108 : }
109 : }
110 E : input_pdb_path_ = AbsolutePath(cmd_line->GetSwitchValuePath("input-pdb"));
111 :
112 E : output_image_path_ = cmd_line->GetSwitchValuePath("output-image");
113 E : if (cmd_line->HasSwitch("output-dll")) {
114 E : if (output_image_path_.empty()) {
115 E : LOG(WARNING) << "Using deprecated switch --output-dll.";
116 : output_image_path_ =
117 E : AbsolutePath(cmd_line->GetSwitchValuePath("output-dll"));
118 E : } else {
119 : return Usage(cmd_line,
120 E : "Can't specify both --output-dll and --output-image.");
121 : }
122 : }
123 :
124 E : output_pdb_path_ = cmd_line->GetSwitchValuePath("output-pdb");
125 E : order_file_path_ = AbsolutePath(cmd_line->GetSwitchValuePath("order-file"));
126 E : no_augment_pdb_ = cmd_line->HasSwitch("no-augment-pdb");
127 E : compress_pdb_ = cmd_line->HasSwitch("compress-pdb");
128 E : no_strip_strings_ = cmd_line->HasSwitch("no-strip-strings");
129 E : output_metadata_ = !cmd_line->HasSwitch("no-metadata");
130 E : overwrite_ = cmd_line->HasSwitch("overwrite");
131 E : basic_blocks_ = cmd_line->HasSwitch("basic-blocks");
132 E : exclude_bb_padding_ = cmd_line->HasSwitch("exclude-bb-padding");
133 E : fuzz_ = cmd_line->HasSwitch("fuzz");
134 E : old_decomposer_ = cmd_line->HasSwitch("old-decomposer");
135 :
136 : // The --output-image argument is required.
137 E : if (output_image_path_.empty()) {
138 E : return Usage(cmd_line, "You must specify --output-image.");
139 : }
140 :
141 : // Ensure that we have an input-image, either explicitly specified, or to be
142 : // taken from an order file.
143 E : if (input_image_path_.empty() && order_file_path_.empty()) {
144 : return Usage(
145 : cmd_line,
146 E : "You must specify --input-image if --order-file is not given.");
147 : }
148 :
149 : // Parse the random seed, if given. Note that the --seed and --order-file
150 : // arguments are mutually exclusive.
151 E : if (cmd_line->HasSwitch("seed")) {
152 E : if (cmd_line->HasSwitch("order-file")) {
153 : return Usage(cmd_line,
154 i : "The seed and order-file arguments are mutually exclusive.");
155 : }
156 E : std::wstring seed_str(cmd_line->GetSwitchValueNative("seed"));
157 E : if (!ParseUInt32(seed_str, &seed_))
158 i : return Usage(cmd_line, "Invalid seed value.");
159 E : }
160 :
161 : // Parse the padding argument.
162 E : if (cmd_line->HasSwitch("padding")) {
163 E : std::wstring padding_str(cmd_line->GetSwitchValueNative("padding"));
164 E : if (!ParseUInt32(padding_str, &padding_))
165 i : return Usage(cmd_line, "Invalid padding value.");
166 E : }
167 :
168 : // Parse the code alignment argument.
169 E : if (cmd_line->HasSwitch("code-alignment")) {
170 E : std::wstring align_str(cmd_line->GetSwitchValueNative("code-alignment"));
171 E : if (!ParseUInt32(align_str, &code_alignment_))
172 i : return Usage(cmd_line, "Invalid code-alignment value.");
173 E : if (code_alignment_ == 0)
174 i : return Usage(cmd_line, "Code-alignment value cannot be zero.");
175 E : }
176 :
177 E : return true;
178 E : }
179 :
180 E : bool RelinkApp::SetUp() {
181 E : if (input_image_path_.empty()) {
182 E : DCHECK(!order_file_path_.empty());
183 : if (!reorder::Reorderer::Order::GetOriginalModulePath(order_file_path_,
184 E : &input_image_path_)) {
185 E : LOG(ERROR) << "Unable to infer input-image.";
186 E : return false;
187 : }
188 :
189 i : LOG(INFO) << "Inferring input DLL path from order file: "
190 : << input_image_path_.value();
191 : }
192 :
193 E : DCHECK(!input_image_path_.empty());
194 E : DCHECK(!output_image_path_.empty());
195 E : DCHECK(order_file_path_.empty() || seed_ == 0);
196 :
197 E : return true;
198 E : }
199 :
200 E : int RelinkApp::Run() {
201 E : pe::PETransformPolicy policy;
202 E : pe::PERelinker relinker(&policy);
203 E : relinker.set_input_path(input_image_path_);
204 E : relinker.set_input_pdb_path(input_pdb_path_);
205 E : relinker.set_output_path(output_image_path_);
206 E : relinker.set_output_pdb_path(output_pdb_path_);
207 E : relinker.set_padding(padding_);
208 E : relinker.set_code_alignment(code_alignment_);
209 E : relinker.set_add_metadata(output_metadata_);
210 E : relinker.set_allow_overwrite(overwrite_);
211 E : relinker.set_augment_pdb(!no_augment_pdb_);
212 E : relinker.set_compress_pdb(compress_pdb_);
213 E : relinker.set_strip_strings(!no_strip_strings_);
214 E : relinker.set_use_old_decomposer(old_decomposer_);
215 :
216 : // Initialize the relinker. This does the decomposition, etc.
217 E : if (!relinker.Init()) {
218 i : LOG(ERROR) << "Failed to initialize relinker.";
219 i : return 1;
220 : }
221 :
222 : // Transforms that may be used.
223 E : scoped_ptr<pe::transforms::ExplodeBasicBlocksTransform> bb_explode;
224 E : scoped_ptr<reorder::transforms::BasicBlockLayoutTransform> bb_layout;
225 E : scoped_ptr<block_graph::transforms::FuzzingTransform> fuzzing_transform;
226 :
227 : // Orderers that may be used.
228 E : reorder::Reorderer::Order order;
229 E : scoped_ptr<block_graph::orderers::OriginalOrderer> orig_orderer;
230 E : scoped_ptr<block_graph::BlockGraphOrdererInterface> orderer;
231 :
232 : // If fuzzing is enabled, add it to the relinker.
233 E : if (fuzz_) {
234 E : fuzzing_transform.reset(new block_graph::transforms::FuzzingTransform);
235 E : CHECK(relinker.AppendTransform(fuzzing_transform.get()));
236 : }
237 :
238 : // If an order file is provided we are performing an explicit ordering.
239 E : if (!order_file_path_.empty()) {
240 : if (!order.LoadFromJSON(relinker.input_pe_file(),
241 : relinker.input_image_layout(),
242 E : order_file_path_)) {
243 i : LOG(ERROR) << "Failed to load order file: " << order_file_path_.value();
244 i : return 1;
245 : }
246 :
247 : // Allocate a BB layout transform. This applies the basic block portion of
248 : // the order specification. It will modify it in place so that it is ready
249 : // to be used by the ExplicitOrderer to finish the job.
250 E : bb_layout.reset(new reorder::transforms::BasicBlockLayoutTransform(&order));
251 E : CHECK(relinker.AppendTransform(bb_layout.get()));
252 :
253 : // Append an OriginalOrderer to the relinker. We do this so that the
254 : // original order is preserved entirely for sections that are not
255 : // fully specified by the order file, and therefore not ordered by the
256 : // ExplicitOrderer.
257 E : orig_orderer.reset(new block_graph::orderers::OriginalOrderer());
258 E : CHECK(relinker.AppendOrderer(orig_orderer.get()));
259 :
260 : // Allocate an explicit orderer.
261 E : orderer.reset(new reorder::orderers::ExplicitOrderer(&order));
262 E : } else {
263 : // No order file was provided, so we're doing a random ordering.
264 :
265 : // If we've been asked to go down to the basic block level, then we want to
266 : // use an explode basic blocks transform so that we randomize the entire
267 : // image at the BB level.
268 E : if (basic_blocks_) {
269 E : bb_explode.reset(new pe::transforms::ExplodeBasicBlocksTransform());
270 E : bb_explode->set_exclude_padding(exclude_bb_padding_);
271 E : CHECK(relinker.AppendTransform(bb_explode.get()));
272 : }
273 :
274 : // Allocate the random block orderer.
275 E : orderer.reset(new block_graph::orderers::RandomOrderer(true, seed_));
276 : }
277 :
278 : // Append the orderer to the relinker.
279 E : CHECK(relinker.AppendOrderer(orderer.get()));
280 :
281 : // Perform the actual relink.
282 E : if (!relinker.Relink()) {
283 i : LOG(ERROR) << "Unable to relink input image.";
284 i : return 1;
285 : }
286 :
287 E : return 0;
288 E : }
289 :
290 : bool RelinkApp::Usage(const CommandLine* cmd_line,
291 E : const base::StringPiece& message) const {
292 E : if (!message.empty()) {
293 E : ::fwrite(message.data(), 1, message.length(), err());
294 E : ::fprintf(err(), "\n\n");
295 : }
296 :
297 : ::fprintf(err(),
298 : kUsageFormatStr,
299 E : cmd_line->GetProgram().BaseName().value().c_str());
300 :
301 E : return false;
302 E : }
303 :
304 : } // namespace relink
|