xref: /aosp_15_r20/external/zucchini/zucchini_tools.cc (revision a03ca8b91e029cd15055c20c78c2e087c84792e4)
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