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 : #include "syzygy/genfilter/genfilter_app.h"
16 :
17 : #include "base/files/file_util.h"
18 : #include "base/strings/string_util.h"
19 : #include "base/strings/stringprintf.h"
20 : #include "syzygy/genfilter/filter_compiler.h"
21 : #include "syzygy/pe/image_filter.h"
22 :
23 : namespace genfilter {
24 :
25 : namespace {
26 :
27 : typedef pe::ImageFilter ImageFilter;
28 :
29 : const char kUsageFormatStr[] =
30 : "Usage: %ls --action=<action> [options] [inputs ...]\n"
31 : "\n"
32 : " A tool for generating filters to be used in instrumenting a binary.\n"
33 : " Inputs may be specified using wildcards.\n"
34 : "\n"
35 : "Required parameters:\n"
36 : " --action=<action>\n"
37 : " The action to be performed. Must be one of 'compiler', 'intersect',\n"
38 : " 'invert', 'subtract' or 'union.\n"
39 : "\n"
40 : "Optional parameters:\n"
41 : " --output-file=<path>\n"
42 : " The path of the output file to produce. If none is specified this\n"
43 : " will go to stdout. If the output file already exists it will not be\n"
44 : " overwritten unless '--overwrite' is specified.\n"
45 : " --overwrite\n"
46 : " Indicates that the tool may safely overwrite existing files.\n"
47 : " --pretty-print\n"
48 : " If specified then the JSON encoded filter will be pretty printed.\n"
49 : "\n"
50 : "Actions:\n"
51 : " compile Compiles the rules in the filter description input files\n"
52 : " and produces a JSON encoded filter as output.\n"
53 : " intersect Calculates the union of the inputs, which must all be JSON\n"
54 : " encoded filters for the same module.\n"
55 : " invert Inverts the given JSON encoded filter. Only one input\n"
56 : " should be provided.\n"
57 : " subtract Calculates the set difference of the inputs, subtracting\n"
58 : " subsequent inputs from the first. All inputs must be JSON\n"
59 : " encoded filters for the same module.\n"
60 : " union Calculates the union of the input filters, which must all\n"
61 : " be JSON encoded filters for the same module.\n"
62 : "\n"
63 : "Parameters for 'compile' action:\n"
64 : " --input-image=<path> [REQUIRED]\n"
65 : " The path of the module for which the filter is being generated.\n"
66 : " --input-pdb=<path> [OPTIONAL]\n"
67 : " The path of the PDB corresponding to the input module. If not\n"
68 : " specified this will be searched for.\n";
69 :
70 : // Applies the given action to a set of filters. Assumes that all of the filters
71 : // are already verified as belonging to the same module.
72 : void ApplyBinarySetAction(GenFilterApp::Action action,
73 : const ImageFilter& in1,
74 : const ImageFilter& in2,
75 E : ImageFilter* out) {
76 E : DCHECK(out != NULL);
77 :
78 E : switch (action) {
79 : case GenFilterApp::kIntersect:
80 E : return in1.filter.Intersect(in2.filter, &out->filter);
81 : case GenFilterApp::kSubtract:
82 E : return in1.filter.Subtract(in2.filter, &out->filter);
83 : case GenFilterApp::kUnion:
84 E : return in1.filter.Union(in2.filter, &out->filter);
85 : default:
86 i : NOTREACHED() << "Not a binary set action.";
87 : }
88 E : }
89 :
90 : bool OutputFilter(bool pretty_print,
91 : const base::FilePath& path,
92 : const ImageFilter& filter,
93 E : FILE* default_file) {
94 : // Open the output file. If none was specified we default to default_file.
95 E : std::wstring dest(L"stdout");
96 E : FILE* file = default_file;
97 E : base::ScopedFILE scoped_file;
98 E : if (!path.empty()) {
99 E : dest = base::StringPrintf(L"\"%ls\"", path.value().c_str());
100 E : scoped_file.reset(base::OpenFile(path, "wb"));
101 E : file = scoped_file.get();
102 E : if (file == NULL) {
103 i : LOG(ERROR) << "Unable to open for writing: " << path.value();
104 i : return false;
105 : }
106 : }
107 :
108 E : LOG(INFO) << "Writing filter to " << dest << ".";
109 E : if (!filter.SaveToJSON(pretty_print, file)) {
110 i : LOG(ERROR) << "Failed to write filter to " << dest << ".";
111 i : return false;
112 : }
113 :
114 E : return true;
115 E : }
116 :
117 : } // namespace
118 :
119 E : bool GenFilterApp::ParseCommandLine(const base::CommandLine* command_line) {
120 E : if (!command_line->HasSwitch("action")) {
121 E : PrintUsage(command_line, "You must specify an action.");
122 E : return false;
123 : }
124 :
125 : // Get a list of all input files.
126 E : base::CommandLine::StringVector args = command_line->GetArgs();
127 E : if (args.empty()) {
128 E : PrintUsage(command_line, "You must provide at least one input file.");
129 E : return false;
130 : }
131 E : for (size_t i = 0; i < args.size(); ++i) {
132 E : if (!AppendMatchingPaths(base::FilePath(args[i]), &inputs_)) {
133 : PrintUsage(command_line,
134 : base::StringPrintf("No files matching '%ws'.",
135 i : args[i].c_str()));
136 i : return false;
137 : }
138 E : }
139 :
140 : // Parse the optional parameters.
141 E : output_file_ = command_line->GetSwitchValuePath("output-file");
142 E : pretty_print_ = command_line->HasSwitch("pretty-print");
143 E : overwrite_ = command_line->HasSwitch("overwrite");
144 :
145 : // Parse the action and any action-specific options.
146 E : size_t min_inputs = 1;
147 E : size_t max_inputs = SIZE_MAX;
148 E : std::string action = command_line->GetSwitchValueASCII("action");
149 E : if (base::LowerCaseEqualsASCII(action, "compile")) {
150 E : action_ = kCompile;
151 :
152 : // In compile mode we need an input image.
153 E : input_image_ = command_line->GetSwitchValuePath("input-image");
154 E : if (input_image_.empty()) {
155 : PrintUsage(command_line,
156 i : "Must specify '--input-image' when action is 'compile'.");
157 i : return false;
158 : }
159 E : input_pdb_ = command_line->GetSwitchValuePath("input-pdb");
160 E : } else if (base::LowerCaseEqualsASCII(action, "intersect")) {
161 E : action_ = kIntersect;
162 E : min_inputs = 2;
163 E : } else if (base::LowerCaseEqualsASCII(action, "invert")) {
164 E : action_ = kInvert;
165 E : max_inputs = 1;
166 E : } else if (base::LowerCaseEqualsASCII(action, "subtract")) {
167 E : action_ = kSubtract;
168 E : min_inputs = 2;
169 E : } else if (base::LowerCaseEqualsASCII(action, "union")) {
170 E : action_ = kUnion;
171 E : min_inputs = 2;
172 E : } else {
173 : PrintUsage(command_line,
174 i : base::StringPrintf("Unknown action: %s.", action.c_str()));
175 i : return false;
176 : }
177 :
178 : // Ensure we have the right number of inputs for the action.
179 E : if (inputs_.size() < min_inputs) {
180 : PrintUsage(command_line,
181 : base::StringPrintf("Expect at least %d inputs for action '%s'.",
182 E : min_inputs, action.c_str()));
183 E : return false;
184 : }
185 E : if (inputs_.size() > max_inputs) {
186 : PrintUsage(command_line,
187 : base::StringPrintf(
188 : "Expect no more than %d inputs for action '%s'.",
189 E : max_inputs, action.c_str()));
190 E : return false;
191 : }
192 :
193 E : return true;
194 E : }
195 :
196 E : int GenFilterApp::Run() {
197 : // Double check the output doesn't already exist early on, so we can prevent
198 : // doing work if it does.
199 : if (!output_file_.empty() &&
200 : base::PathExists(output_file_) &&
201 E : !overwrite_) {
202 E : LOG(ERROR) << "Output file \"" << output_file_.value()
203 : << "\" already exists.";
204 E : return 1;
205 : }
206 :
207 : // Run the appropriate action.
208 E : if (action_ == kCompile) {
209 E : if (!RunCompileAction())
210 E : return 1;
211 E : } else {
212 E : if (!RunSetAction())
213 E : return 1;
214 : }
215 :
216 E : return 0;
217 E : }
218 :
219 : void GenFilterApp::PrintUsage(const base::CommandLine* command_line,
220 E : const base::StringPiece& message) const {
221 E : if (!message.empty()) {
222 E : ::fwrite(message.data(), 1, message.length(), out());
223 E : ::fprintf(out(), "\n\n");
224 : }
225 :
226 : ::fprintf(out(),
227 : kUsageFormatStr,
228 E : command_line->GetProgram().BaseName().value().c_str());
229 E : }
230 :
231 E : bool GenFilterApp::RunCompileAction() {
232 E : FilterCompiler filter_compiler;
233 :
234 E : if (!filter_compiler.Init(input_image_, input_pdb_))
235 i : return false;
236 :
237 E : for (size_t i = 0; i < inputs_.size(); ++i) {
238 E : LOG(INFO) << "Parsing filter description file \"" << inputs_[i].value()
239 : << "\".";
240 E : if (!filter_compiler.ParseFilterDescriptionFile(inputs_[i]))
241 E : return false;
242 E : }
243 :
244 E : LOG(INFO) << "Compiling filter.";
245 E : ImageFilter filter;
246 E : if (!filter_compiler.Compile(&filter))
247 i : return false;
248 :
249 E : if (!OutputFilter(pretty_print_, output_file_, filter, out()))
250 i : return false;
251 :
252 E : return true;
253 E : }
254 :
255 E : bool GenFilterApp::RunSetAction() {
256 : // At this point we're handling set operations on JSON encoded filters. Load
257 : // them and make sure they're for the same module.
258 E : std::vector<ImageFilter> filters(inputs_.size());
259 E : for (size_t i = 0; i < inputs_.size(); ++i) {
260 E : if (!filters[i].LoadFromJSON(inputs_[i])) {
261 i : LOG(ERROR) << "Failed to load filter \"" << inputs_[i].value() << "\".";
262 i : return false;
263 : }
264 :
265 : // If this is a second or subsequent filter ensure it's for the same module
266 : // as the first one.
267 E : if (i > 0 && !filters[0].signature.IsConsistent(filters[i].signature)) {
268 E : LOG(ERROR) << "Filter \"" << inputs_[i].value() << "\" is not consistent "
269 : << "with filter \"" << inputs_[0].value() << "\".";
270 E : return false;
271 : }
272 E : }
273 :
274 : // Handle the 'invert' action. This is a unary set operator.
275 E : if (action_ == kInvert) {
276 : // Invert in place.
277 E : filters[0].filter.Invert(&filters[0].filter);
278 E : } else {
279 : // Otherwise we're a binary set operator. We do the work in place in the
280 : // place of the first filter.
281 E : for (size_t i = 1; i < inputs_.size(); ++i)
282 E : ApplyBinarySetAction(action_, filters[0], filters[i], &filters[0]);
283 : }
284 :
285 E : if (!OutputFilter(pretty_print_, output_file_, filters[0], out()))
286 i : return false;
287 :
288 E : return true;
289 E : }
290 :
291 : } // namespace genfilter
|