1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "components/zucchini/zucchini_tools.h"
6
7 #include <stddef.h>
8 #include <stdint.h>
9
10 #include <algorithm>
11 #include <memory>
12 #include <ostream>
13 #include <utility>
14
15 #include "base/bind.h"
16 #include "base/check_op.h"
17 #include "base/strings/stringprintf.h"
18 #include "components/zucchini/disassembler.h"
19 #include "components/zucchini/element_detection.h"
20 #include "components/zucchini/ensemble_matcher.h"
21 #include "components/zucchini/heuristic_ensemble_matcher.h"
22 #include "components/zucchini/imposed_ensemble_matcher.h"
23 #include "components/zucchini/io_utils.h"
24
25 namespace zucchini {
26
ReadReferences(ConstBufferView image,bool do_dump,std::ostream & out)27 status::Code ReadReferences(ConstBufferView image,
28 bool do_dump,
29 std::ostream& out) {
30 std::unique_ptr<Disassembler> disasm = MakeDisassemblerWithoutFallback(image);
31 if (!disasm) {
32 out << "Input file not recognized as executable." << std::endl;
33 return status::kStatusInvalidOldImage;
34 }
35
36 std::vector<offset_t> targets;
37 for (const auto& group : disasm->MakeReferenceGroups()) {
38 targets.clear();
39 auto refs = group.GetReader(disasm.get());
40 for (auto ref = refs->GetNext(); ref.has_value(); ref = refs->GetNext())
41 targets.push_back(ref->target);
42
43 size_t num_locations = targets.size();
44 std::sort(targets.begin(), targets.end());
45 targets.erase(std::unique(targets.begin(), targets.end()), targets.end());
46 size_t num_targets = targets.size();
47
48 out << "Type " << int(group.type_tag().value())
49 << ": Pool=" << static_cast<uint32_t>(group.pool_tag().value())
50 << ", width=" << group.width() << ", #locations=" << num_locations
51 << ", #targets=" << num_targets;
52 if (num_targets > 0) {
53 double ratio = static_cast<double>(num_locations) / num_targets;
54 out << " (ratio=" << base::StringPrintf("%.4f", ratio) << ")";
55 }
56 out << std::endl;
57
58 if (do_dump) {
59 refs = group.GetReader(disasm.get());
60
61 for (auto ref = refs->GetNext(); ref; ref = refs->GetNext()) {
62 out << " " << AsHex<8>(ref->location) << " " << AsHex<8>(ref->target)
63 << std::endl;
64 }
65 }
66 }
67
68 return status::kStatusSuccess;
69 }
70
DetectAll(ConstBufferView image,std::ostream & out,std::vector<ConstBufferView> * sub_image_list)71 status::Code DetectAll(ConstBufferView image,
72 std::ostream& out,
73 std::vector<ConstBufferView>* sub_image_list) {
74 DCHECK_NE(sub_image_list, nullptr);
75 sub_image_list->clear();
76
77 const size_t size = image.size();
78 size_t last_out_pos = 0;
79 size_t total_bytes_found = 0;
80
81 auto print_range = [&out](size_t pos, size_t size, const std::string& msg) {
82 out << "-- " << AsHex<8, size_t>(pos) << " +" << AsHex<8, size_t>(size)
83 << ": " << msg << std::endl;
84 };
85
86 ElementFinder finder(image,
87 base::BindRepeating(DetectElementFromDisassembler));
88 for (auto element = finder.GetNext(); element.has_value();
89 element = finder.GetNext()) {
90 ConstBufferView sub_image = image[element->region()];
91 sub_image_list->push_back(sub_image);
92 size_t pos = sub_image.begin() - image.begin();
93 size_t prog_size = sub_image.size();
94 if (last_out_pos < pos)
95 print_range(last_out_pos, pos - last_out_pos, "?");
96 auto disasm = MakeDisassemblerOfType(sub_image, element->exe_type);
97 print_range(pos, prog_size, disasm->GetExeTypeString());
98 total_bytes_found += prog_size;
99 last_out_pos = pos + prog_size;
100 }
101 if (last_out_pos < size)
102 print_range(last_out_pos, size - last_out_pos, "?");
103 out << std::endl;
104
105 // Print summary, using decimal instead of hexadecimal.
106 out << "Detected " << total_bytes_found << "/" << size << " bytes => ";
107 double percent = total_bytes_found * 100.0 / size;
108 out << base::StringPrintf("%.2f", percent) << "%." << std::endl;
109
110 return status::kStatusSuccess;
111 }
112
MatchAll(ConstBufferView old_image,ConstBufferView new_image,std::string imposed_matches,std::ostream & out)113 status::Code MatchAll(ConstBufferView old_image,
114 ConstBufferView new_image,
115 std::string imposed_matches,
116 std::ostream& out) {
117 std::unique_ptr<EnsembleMatcher> matcher;
118 if (imposed_matches.empty()) {
119 matcher = std::make_unique<HeuristicEnsembleMatcher>(&out);
120 } else {
121 matcher =
122 std::make_unique<ImposedEnsembleMatcher>(std::move(imposed_matches));
123 }
124 if (!matcher->RunMatch(old_image, new_image)) {
125 out << "RunMatch() failed.";
126 return status::kStatusFatal;
127 }
128 out << "Found " << matcher->matches().size() << " nontrivial matches and "
129 << matcher->num_identical() << " identical matches." << std::endl
130 << "To impose the same matches by command line, use: " << std::endl
131 << " -impose=";
132 PrefixSep sep(",");
133 for (const ElementMatch& match : matcher->matches())
134 out << sep << match.ToString();
135 out << std::endl;
136
137 return status::kStatusSuccess;
138 }
139
140 } // namespace zucchini
141