xref: /aosp_15_r20/external/zucchini/disassembler_win32.cc (revision a03ca8b91e029cd15055c20c78c2e087c84792e4)
1*a03ca8b9SKrzysztof Kosiński // Copyright 2017 The Chromium Authors. All rights reserved.
2*a03ca8b9SKrzysztof Kosiński // Use of this source code is governed by a BSD-style license that can be
3*a03ca8b9SKrzysztof Kosiński // found in the LICENSE file.
4*a03ca8b9SKrzysztof Kosiński 
5*a03ca8b9SKrzysztof Kosiński #include "components/zucchini/disassembler_win32.h"
6*a03ca8b9SKrzysztof Kosiński 
7*a03ca8b9SKrzysztof Kosiński #include <stddef.h>
8*a03ca8b9SKrzysztof Kosiński 
9*a03ca8b9SKrzysztof Kosiński #include <algorithm>
10*a03ca8b9SKrzysztof Kosiński 
11*a03ca8b9SKrzysztof Kosiński #include "base/logging.h"
12*a03ca8b9SKrzysztof Kosiński #include "base/numerics/safe_conversions.h"
13*a03ca8b9SKrzysztof Kosiński #include "components/zucchini/abs32_utils.h"
14*a03ca8b9SKrzysztof Kosiński #include "components/zucchini/algorithm.h"
15*a03ca8b9SKrzysztof Kosiński #include "components/zucchini/buffer_source.h"
16*a03ca8b9SKrzysztof Kosiński #include "components/zucchini/rel32_finder.h"
17*a03ca8b9SKrzysztof Kosiński #include "components/zucchini/rel32_utils.h"
18*a03ca8b9SKrzysztof Kosiński #include "components/zucchini/reloc_win32.h"
19*a03ca8b9SKrzysztof Kosiński 
20*a03ca8b9SKrzysztof Kosiński namespace zucchini {
21*a03ca8b9SKrzysztof Kosiński 
22*a03ca8b9SKrzysztof Kosiński namespace {
23*a03ca8b9SKrzysztof Kosiński 
24*a03ca8b9SKrzysztof Kosiński // Decides whether |image| points to a Win32 PE file. If this is a possibility,
25*a03ca8b9SKrzysztof Kosiński // assigns |source| to enable further parsing, and returns true. Otherwise
26*a03ca8b9SKrzysztof Kosiński // leaves |source| at an undefined state and returns false.
ReadWin32Header(ConstBufferView image,BufferSource * source)27*a03ca8b9SKrzysztof Kosiński bool ReadWin32Header(ConstBufferView image, BufferSource* source) {
28*a03ca8b9SKrzysztof Kosiński   *source = BufferSource(image);
29*a03ca8b9SKrzysztof Kosiński 
30*a03ca8b9SKrzysztof Kosiński   // Check "MZ" magic of DOS header.
31*a03ca8b9SKrzysztof Kosiński   if (!source->CheckNextBytes({'M', 'Z'}))
32*a03ca8b9SKrzysztof Kosiński     return false;
33*a03ca8b9SKrzysztof Kosiński 
34*a03ca8b9SKrzysztof Kosiński   const auto* dos_header = source->GetPointer<pe::ImageDOSHeader>();
35*a03ca8b9SKrzysztof Kosiński   // For |e_lfanew|, reject on misalignment or overlap with DOS header.
36*a03ca8b9SKrzysztof Kosiński   if (!dos_header || (dos_header->e_lfanew & 7) != 0 ||
37*a03ca8b9SKrzysztof Kosiński       dos_header->e_lfanew < 0U + sizeof(pe::ImageDOSHeader)) {
38*a03ca8b9SKrzysztof Kosiński     return false;
39*a03ca8b9SKrzysztof Kosiński   }
40*a03ca8b9SKrzysztof Kosiński   // Offset to PE header is in DOS header.
41*a03ca8b9SKrzysztof Kosiński   *source = std::move(BufferSource(image).Skip(dos_header->e_lfanew));
42*a03ca8b9SKrzysztof Kosiński   // Check 'PE\0\0' magic from PE header.
43*a03ca8b9SKrzysztof Kosiński   if (!source->ConsumeBytes({'P', 'E', 0, 0}))
44*a03ca8b9SKrzysztof Kosiński     return false;
45*a03ca8b9SKrzysztof Kosiński 
46*a03ca8b9SKrzysztof Kosiński   return true;
47*a03ca8b9SKrzysztof Kosiński }
48*a03ca8b9SKrzysztof Kosiński 
49*a03ca8b9SKrzysztof Kosiński template <class TRAITS>
ReadDataDirectory(const typename TRAITS::ImageOptionalHeader * optional_header,size_t index)50*a03ca8b9SKrzysztof Kosiński const pe::ImageDataDirectory* ReadDataDirectory(
51*a03ca8b9SKrzysztof Kosiński     const typename TRAITS::ImageOptionalHeader* optional_header,
52*a03ca8b9SKrzysztof Kosiński     size_t index) {
53*a03ca8b9SKrzysztof Kosiński   if (index >= optional_header->number_of_rva_and_sizes)
54*a03ca8b9SKrzysztof Kosiński     return nullptr;
55*a03ca8b9SKrzysztof Kosiński   return &optional_header->data_directory[index];
56*a03ca8b9SKrzysztof Kosiński }
57*a03ca8b9SKrzysztof Kosiński 
58*a03ca8b9SKrzysztof Kosiński // Decides whether |section| (assumed value) is a section that contains code.
59*a03ca8b9SKrzysztof Kosiński template <class TRAITS>
IsWin32CodeSection(const pe::ImageSectionHeader & section)60*a03ca8b9SKrzysztof Kosiński bool IsWin32CodeSection(const pe::ImageSectionHeader& section) {
61*a03ca8b9SKrzysztof Kosiński   return (section.characteristics & kCodeCharacteristics) ==
62*a03ca8b9SKrzysztof Kosiński          kCodeCharacteristics;
63*a03ca8b9SKrzysztof Kosiński }
64*a03ca8b9SKrzysztof Kosiński 
65*a03ca8b9SKrzysztof Kosiński }  // namespace
66*a03ca8b9SKrzysztof Kosiński 
67*a03ca8b9SKrzysztof Kosiński /******** Win32X86Traits ********/
68*a03ca8b9SKrzysztof Kosiński 
69*a03ca8b9SKrzysztof Kosiński // static
70*a03ca8b9SKrzysztof Kosiński constexpr Bitness Win32X86Traits::kBitness;
71*a03ca8b9SKrzysztof Kosiński constexpr ExecutableType Win32X86Traits::kExeType;
72*a03ca8b9SKrzysztof Kosiński const char Win32X86Traits::kExeTypeString[] = "Windows PE x86";
73*a03ca8b9SKrzysztof Kosiński 
74*a03ca8b9SKrzysztof Kosiński /******** Win32X64Traits ********/
75*a03ca8b9SKrzysztof Kosiński 
76*a03ca8b9SKrzysztof Kosiński // static
77*a03ca8b9SKrzysztof Kosiński constexpr Bitness Win32X64Traits::kBitness;
78*a03ca8b9SKrzysztof Kosiński constexpr ExecutableType Win32X64Traits::kExeType;
79*a03ca8b9SKrzysztof Kosiński const char Win32X64Traits::kExeTypeString[] = "Windows PE x64";
80*a03ca8b9SKrzysztof Kosiński 
81*a03ca8b9SKrzysztof Kosiński /******** DisassemblerWin32 ********/
82*a03ca8b9SKrzysztof Kosiński 
83*a03ca8b9SKrzysztof Kosiński // static.
84*a03ca8b9SKrzysztof Kosiński template <class TRAITS>
QuickDetect(ConstBufferView image)85*a03ca8b9SKrzysztof Kosiński bool DisassemblerWin32<TRAITS>::QuickDetect(ConstBufferView image) {
86*a03ca8b9SKrzysztof Kosiński   BufferSource source;
87*a03ca8b9SKrzysztof Kosiński   return ReadWin32Header(image, &source);
88*a03ca8b9SKrzysztof Kosiński }
89*a03ca8b9SKrzysztof Kosiński 
90*a03ca8b9SKrzysztof Kosiński // |num_equivalence_iterations_| = 2 for reloc -> abs32.
91*a03ca8b9SKrzysztof Kosiński template <class TRAITS>
DisassemblerWin32()92*a03ca8b9SKrzysztof Kosiński DisassemblerWin32<TRAITS>::DisassemblerWin32() : Disassembler(2) {}
93*a03ca8b9SKrzysztof Kosiński 
94*a03ca8b9SKrzysztof Kosiński template <class TRAITS>
95*a03ca8b9SKrzysztof Kosiński DisassemblerWin32<TRAITS>::~DisassemblerWin32() = default;
96*a03ca8b9SKrzysztof Kosiński 
97*a03ca8b9SKrzysztof Kosiński template <class TRAITS>
GetExeType() const98*a03ca8b9SKrzysztof Kosiński ExecutableType DisassemblerWin32<TRAITS>::GetExeType() const {
99*a03ca8b9SKrzysztof Kosiński   return Traits::kExeType;
100*a03ca8b9SKrzysztof Kosiński }
101*a03ca8b9SKrzysztof Kosiński 
102*a03ca8b9SKrzysztof Kosiński template <class TRAITS>
GetExeTypeString() const103*a03ca8b9SKrzysztof Kosiński std::string DisassemblerWin32<TRAITS>::GetExeTypeString() const {
104*a03ca8b9SKrzysztof Kosiński   return Traits::kExeTypeString;
105*a03ca8b9SKrzysztof Kosiński }
106*a03ca8b9SKrzysztof Kosiński 
107*a03ca8b9SKrzysztof Kosiński template <class TRAITS>
MakeReferenceGroups() const108*a03ca8b9SKrzysztof Kosiński std::vector<ReferenceGroup> DisassemblerWin32<TRAITS>::MakeReferenceGroups()
109*a03ca8b9SKrzysztof Kosiński     const {
110*a03ca8b9SKrzysztof Kosiński   return {
111*a03ca8b9SKrzysztof Kosiński       {ReferenceTypeTraits{2, TypeTag(kReloc), PoolTag(kReloc)},
112*a03ca8b9SKrzysztof Kosiński        &DisassemblerWin32::MakeReadRelocs, &DisassemblerWin32::MakeWriteRelocs},
113*a03ca8b9SKrzysztof Kosiński       {ReferenceTypeTraits{Traits::kVAWidth, TypeTag(kAbs32), PoolTag(kAbs32)},
114*a03ca8b9SKrzysztof Kosiński        &DisassemblerWin32::MakeReadAbs32, &DisassemblerWin32::MakeWriteAbs32},
115*a03ca8b9SKrzysztof Kosiński       {ReferenceTypeTraits{4, TypeTag(kRel32), PoolTag(kRel32)},
116*a03ca8b9SKrzysztof Kosiński        &DisassemblerWin32::MakeReadRel32, &DisassemblerWin32::MakeWriteRel32},
117*a03ca8b9SKrzysztof Kosiński   };
118*a03ca8b9SKrzysztof Kosiński }
119*a03ca8b9SKrzysztof Kosiński 
120*a03ca8b9SKrzysztof Kosiński template <class TRAITS>
MakeReadRelocs(offset_t lo,offset_t hi)121*a03ca8b9SKrzysztof Kosiński std::unique_ptr<ReferenceReader> DisassemblerWin32<TRAITS>::MakeReadRelocs(
122*a03ca8b9SKrzysztof Kosiński     offset_t lo,
123*a03ca8b9SKrzysztof Kosiński     offset_t hi) {
124*a03ca8b9SKrzysztof Kosiński   if (!ParseAndStoreRelocBlocks())
125*a03ca8b9SKrzysztof Kosiński     return std::make_unique<EmptyReferenceReader>();
126*a03ca8b9SKrzysztof Kosiński 
127*a03ca8b9SKrzysztof Kosiński   RelocRvaReaderWin32 reloc_rva_reader(image_, reloc_region_,
128*a03ca8b9SKrzysztof Kosiński                                        reloc_block_offsets_, lo, hi);
129*a03ca8b9SKrzysztof Kosiński   CHECK_GE(image_.size(), Traits::kVAWidth);
130*a03ca8b9SKrzysztof Kosiński   offset_t offset_bound =
131*a03ca8b9SKrzysztof Kosiński       base::checked_cast<offset_t>(image_.size() - Traits::kVAWidth + 1);
132*a03ca8b9SKrzysztof Kosiński   return std::make_unique<RelocReaderWin32>(std::move(reloc_rva_reader),
133*a03ca8b9SKrzysztof Kosiński                                             Traits::kRelocType, offset_bound,
134*a03ca8b9SKrzysztof Kosiński                                             translator_);
135*a03ca8b9SKrzysztof Kosiński }
136*a03ca8b9SKrzysztof Kosiński 
137*a03ca8b9SKrzysztof Kosiński template <class TRAITS>
MakeReadAbs32(offset_t lo,offset_t hi)138*a03ca8b9SKrzysztof Kosiński std::unique_ptr<ReferenceReader> DisassemblerWin32<TRAITS>::MakeReadAbs32(
139*a03ca8b9SKrzysztof Kosiński     offset_t lo,
140*a03ca8b9SKrzysztof Kosiński     offset_t hi) {
141*a03ca8b9SKrzysztof Kosiński   ParseAndStoreAbs32();
142*a03ca8b9SKrzysztof Kosiński   Abs32RvaExtractorWin32 abs_rva_extractor(
143*a03ca8b9SKrzysztof Kosiński       image_, {Traits::kBitness, image_base_}, abs32_locations_, lo, hi);
144*a03ca8b9SKrzysztof Kosiński   return std::make_unique<Abs32ReaderWin32>(std::move(abs_rva_extractor),
145*a03ca8b9SKrzysztof Kosiński                                             translator_);
146*a03ca8b9SKrzysztof Kosiński }
147*a03ca8b9SKrzysztof Kosiński 
148*a03ca8b9SKrzysztof Kosiński template <class TRAITS>
MakeReadRel32(offset_t lo,offset_t hi)149*a03ca8b9SKrzysztof Kosiński std::unique_ptr<ReferenceReader> DisassemblerWin32<TRAITS>::MakeReadRel32(
150*a03ca8b9SKrzysztof Kosiński     offset_t lo,
151*a03ca8b9SKrzysztof Kosiński     offset_t hi) {
152*a03ca8b9SKrzysztof Kosiński   ParseAndStoreRel32();
153*a03ca8b9SKrzysztof Kosiński   return std::make_unique<Rel32ReaderX86>(image_, lo, hi, &rel32_locations_,
154*a03ca8b9SKrzysztof Kosiński                                           translator_);
155*a03ca8b9SKrzysztof Kosiński }
156*a03ca8b9SKrzysztof Kosiński 
157*a03ca8b9SKrzysztof Kosiński template <class TRAITS>
MakeWriteRelocs(MutableBufferView image)158*a03ca8b9SKrzysztof Kosiński std::unique_ptr<ReferenceWriter> DisassemblerWin32<TRAITS>::MakeWriteRelocs(
159*a03ca8b9SKrzysztof Kosiński     MutableBufferView image) {
160*a03ca8b9SKrzysztof Kosiński   if (!ParseAndStoreRelocBlocks())
161*a03ca8b9SKrzysztof Kosiński     return std::make_unique<EmptyReferenceWriter>();
162*a03ca8b9SKrzysztof Kosiński 
163*a03ca8b9SKrzysztof Kosiński   return std::make_unique<RelocWriterWin32>(Traits::kRelocType, image,
164*a03ca8b9SKrzysztof Kosiński                                             reloc_region_, reloc_block_offsets_,
165*a03ca8b9SKrzysztof Kosiński                                             translator_);
166*a03ca8b9SKrzysztof Kosiński }
167*a03ca8b9SKrzysztof Kosiński 
168*a03ca8b9SKrzysztof Kosiński template <class TRAITS>
MakeWriteAbs32(MutableBufferView image)169*a03ca8b9SKrzysztof Kosiński std::unique_ptr<ReferenceWriter> DisassemblerWin32<TRAITS>::MakeWriteAbs32(
170*a03ca8b9SKrzysztof Kosiński     MutableBufferView image) {
171*a03ca8b9SKrzysztof Kosiński   return std::make_unique<Abs32WriterWin32>(
172*a03ca8b9SKrzysztof Kosiński       image, AbsoluteAddress(Traits::kBitness, image_base_), translator_);
173*a03ca8b9SKrzysztof Kosiński }
174*a03ca8b9SKrzysztof Kosiński 
175*a03ca8b9SKrzysztof Kosiński template <class TRAITS>
MakeWriteRel32(MutableBufferView image)176*a03ca8b9SKrzysztof Kosiński std::unique_ptr<ReferenceWriter> DisassemblerWin32<TRAITS>::MakeWriteRel32(
177*a03ca8b9SKrzysztof Kosiński     MutableBufferView image) {
178*a03ca8b9SKrzysztof Kosiński   return std::make_unique<Rel32WriterX86>(image, translator_);
179*a03ca8b9SKrzysztof Kosiński }
180*a03ca8b9SKrzysztof Kosiński 
181*a03ca8b9SKrzysztof Kosiński template <class TRAITS>
Parse(ConstBufferView image)182*a03ca8b9SKrzysztof Kosiński bool DisassemblerWin32<TRAITS>::Parse(ConstBufferView image) {
183*a03ca8b9SKrzysztof Kosiński   image_ = image;
184*a03ca8b9SKrzysztof Kosiński   return ParseHeader();
185*a03ca8b9SKrzysztof Kosiński }
186*a03ca8b9SKrzysztof Kosiński 
187*a03ca8b9SKrzysztof Kosiński template <class TRAITS>
ParseHeader()188*a03ca8b9SKrzysztof Kosiński bool DisassemblerWin32<TRAITS>::ParseHeader() {
189*a03ca8b9SKrzysztof Kosiński   BufferSource source;
190*a03ca8b9SKrzysztof Kosiński 
191*a03ca8b9SKrzysztof Kosiński   if (!ReadWin32Header(image_, &source))
192*a03ca8b9SKrzysztof Kosiński     return false;
193*a03ca8b9SKrzysztof Kosiński 
194*a03ca8b9SKrzysztof Kosiński   constexpr size_t kDataDirBase =
195*a03ca8b9SKrzysztof Kosiński       offsetof(typename Traits::ImageOptionalHeader, data_directory);
196*a03ca8b9SKrzysztof Kosiński   auto* coff_header = source.GetPointer<pe::ImageFileHeader>();
197*a03ca8b9SKrzysztof Kosiński   if (!coff_header || coff_header->size_of_optional_header < kDataDirBase)
198*a03ca8b9SKrzysztof Kosiński     return false;
199*a03ca8b9SKrzysztof Kosiński 
200*a03ca8b9SKrzysztof Kosiński   // |number_of_rva_and_sizes < kImageNumberOfDirectoryEntries| is possible. So
201*a03ca8b9SKrzysztof Kosiński   // in theory, GetPointer() on ImageOptionalHeader can reach EOF for a tiny PE
202*a03ca8b9SKrzysztof Kosiński   // file, causing false rejection. However, this should not occur for practical
203*a03ca8b9SKrzysztof Kosiński   // cases; and rejection is okay for corner cases (e.g., from a fuzzer).
204*a03ca8b9SKrzysztof Kosiński   auto* optional_header =
205*a03ca8b9SKrzysztof Kosiński       source.GetPointer<typename Traits::ImageOptionalHeader>();
206*a03ca8b9SKrzysztof Kosiński   if (!optional_header || optional_header->magic != Traits::kMagic)
207*a03ca8b9SKrzysztof Kosiński     return false;
208*a03ca8b9SKrzysztof Kosiński 
209*a03ca8b9SKrzysztof Kosiński   // Check |optional_header->number_of_rva_and_sizes|.
210*a03ca8b9SKrzysztof Kosiński   const size_t data_dir_size =
211*a03ca8b9SKrzysztof Kosiński       coff_header->size_of_optional_header - kDataDirBase;
212*a03ca8b9SKrzysztof Kosiński   const size_t num_data_dir = data_dir_size / sizeof(pe::ImageDataDirectory);
213*a03ca8b9SKrzysztof Kosiński   if (num_data_dir != optional_header->number_of_rva_and_sizes ||
214*a03ca8b9SKrzysztof Kosiński       num_data_dir * sizeof(pe::ImageDataDirectory) != data_dir_size ||
215*a03ca8b9SKrzysztof Kosiński       num_data_dir > pe::kImageNumberOfDirectoryEntries) {
216*a03ca8b9SKrzysztof Kosiński     return false;
217*a03ca8b9SKrzysztof Kosiński   }
218*a03ca8b9SKrzysztof Kosiński 
219*a03ca8b9SKrzysztof Kosiński   base_relocation_table_ = ReadDataDirectory<Traits>(
220*a03ca8b9SKrzysztof Kosiński       optional_header, pe::kIndexOfBaseRelocationTable);
221*a03ca8b9SKrzysztof Kosiński   if (!base_relocation_table_)
222*a03ca8b9SKrzysztof Kosiński     return false;
223*a03ca8b9SKrzysztof Kosiński 
224*a03ca8b9SKrzysztof Kosiński   image_base_ = optional_header->image_base;
225*a03ca8b9SKrzysztof Kosiński 
226*a03ca8b9SKrzysztof Kosiński   // |optional_header->size_of_image| is the size of the image when loaded into
227*a03ca8b9SKrzysztof Kosiński   // memory, and not the actual size on disk.
228*a03ca8b9SKrzysztof Kosiński   rva_t rva_bound = optional_header->size_of_image;
229*a03ca8b9SKrzysztof Kosiński   if (rva_bound >= kRvaBound)
230*a03ca8b9SKrzysztof Kosiński     return false;
231*a03ca8b9SKrzysztof Kosiński 
232*a03ca8b9SKrzysztof Kosiński   // An exclusive upper bound of all offsets used in the image. This gets
233*a03ca8b9SKrzysztof Kosiński   // updated as sections get visited.
234*a03ca8b9SKrzysztof Kosiński   offset_t offset_bound =
235*a03ca8b9SKrzysztof Kosiński       base::checked_cast<offset_t>(source.begin() - image_.begin());
236*a03ca8b9SKrzysztof Kosiński 
237*a03ca8b9SKrzysztof Kosiński   // Extract |sections_|.
238*a03ca8b9SKrzysztof Kosiński   size_t sections_count = coff_header->number_of_sections;
239*a03ca8b9SKrzysztof Kosiński   auto* sections_array =
240*a03ca8b9SKrzysztof Kosiński       source.GetArray<pe::ImageSectionHeader>(sections_count);
241*a03ca8b9SKrzysztof Kosiński   if (!sections_array)
242*a03ca8b9SKrzysztof Kosiński     return false;
243*a03ca8b9SKrzysztof Kosiński   sections_.assign(sections_array, sections_array + sections_count);
244*a03ca8b9SKrzysztof Kosiński 
245*a03ca8b9SKrzysztof Kosiński   // Prepare |units| for offset-RVA translation.
246*a03ca8b9SKrzysztof Kosiński   std::vector<AddressTranslator::Unit> units;
247*a03ca8b9SKrzysztof Kosiński   units.reserve(sections_count);
248*a03ca8b9SKrzysztof Kosiński 
249*a03ca8b9SKrzysztof Kosiński   // Visit each section, validate, and add address translation data to |units|.
250*a03ca8b9SKrzysztof Kosiński   bool has_text_section = false;
251*a03ca8b9SKrzysztof Kosiński   decltype(pe::ImageSectionHeader::virtual_address) prev_virtual_address = 0;
252*a03ca8b9SKrzysztof Kosiński   for (size_t i = 0; i < sections_count; ++i) {
253*a03ca8b9SKrzysztof Kosiński     const pe::ImageSectionHeader& section = sections_[i];
254*a03ca8b9SKrzysztof Kosiński     // Apply strict checks on section bounds.
255*a03ca8b9SKrzysztof Kosiński     if (!image_.covers(
256*a03ca8b9SKrzysztof Kosiński             {section.file_offset_of_raw_data, section.size_of_raw_data})) {
257*a03ca8b9SKrzysztof Kosiński       return false;
258*a03ca8b9SKrzysztof Kosiński     }
259*a03ca8b9SKrzysztof Kosiński     if (!RangeIsBounded(section.virtual_address, section.virtual_size,
260*a03ca8b9SKrzysztof Kosiński                         rva_bound)) {
261*a03ca8b9SKrzysztof Kosiński       return false;
262*a03ca8b9SKrzysztof Kosiński     }
263*a03ca8b9SKrzysztof Kosiński 
264*a03ca8b9SKrzysztof Kosiński     // PE sections should be sorted by RVAs. For robustness, we don't rely on
265*a03ca8b9SKrzysztof Kosiński     // this, so even if unsorted we don't care. Output warning though.
266*a03ca8b9SKrzysztof Kosiński     if (prev_virtual_address > section.virtual_address)
267*a03ca8b9SKrzysztof Kosiński       LOG(WARNING) << "RVA anomaly found for Section " << i;
268*a03ca8b9SKrzysztof Kosiński     prev_virtual_address = section.virtual_address;
269*a03ca8b9SKrzysztof Kosiński 
270*a03ca8b9SKrzysztof Kosiński     // Add |section| data for offset-RVA translation.
271*a03ca8b9SKrzysztof Kosiński     units.push_back({section.file_offset_of_raw_data, section.size_of_raw_data,
272*a03ca8b9SKrzysztof Kosiński                      section.virtual_address, section.virtual_size});
273*a03ca8b9SKrzysztof Kosiński 
274*a03ca8b9SKrzysztof Kosiński     offset_t end_offset =
275*a03ca8b9SKrzysztof Kosiński         section.file_offset_of_raw_data + section.size_of_raw_data;
276*a03ca8b9SKrzysztof Kosiński     offset_bound = std::max(end_offset, offset_bound);
277*a03ca8b9SKrzysztof Kosiński     if (IsWin32CodeSection<Traits>(section))
278*a03ca8b9SKrzysztof Kosiński       has_text_section = true;
279*a03ca8b9SKrzysztof Kosiński   }
280*a03ca8b9SKrzysztof Kosiński 
281*a03ca8b9SKrzysztof Kosiński   if (offset_bound > image_.size())
282*a03ca8b9SKrzysztof Kosiński     return false;
283*a03ca8b9SKrzysztof Kosiński   if (!has_text_section)
284*a03ca8b9SKrzysztof Kosiński     return false;
285*a03ca8b9SKrzysztof Kosiński 
286*a03ca8b9SKrzysztof Kosiński   // Initialize |translator_| for offset-RVA translations. Any inconsistency
287*a03ca8b9SKrzysztof Kosiński   // (e.g., 2 offsets correspond to the same RVA) would invalidate the PE file.
288*a03ca8b9SKrzysztof Kosiński   if (translator_.Initialize(std::move(units)) != AddressTranslator::kSuccess)
289*a03ca8b9SKrzysztof Kosiński     return false;
290*a03ca8b9SKrzysztof Kosiński 
291*a03ca8b9SKrzysztof Kosiński   // Resize |image_| to include only contents claimed by sections. Note that
292*a03ca8b9SKrzysztof Kosiński   // this may miss digital signatures at end of PE files, but for patching this
293*a03ca8b9SKrzysztof Kosiński   // is of minor concern.
294*a03ca8b9SKrzysztof Kosiński   image_.shrink(offset_bound);
295*a03ca8b9SKrzysztof Kosiński 
296*a03ca8b9SKrzysztof Kosiński   return true;
297*a03ca8b9SKrzysztof Kosiński }
298*a03ca8b9SKrzysztof Kosiński 
299*a03ca8b9SKrzysztof Kosiński template <class TRAITS>
ParseAndStoreRelocBlocks()300*a03ca8b9SKrzysztof Kosiński bool DisassemblerWin32<TRAITS>::ParseAndStoreRelocBlocks() {
301*a03ca8b9SKrzysztof Kosiński   if (has_parsed_relocs_)
302*a03ca8b9SKrzysztof Kosiński     return reloc_region_.lo() != kInvalidOffset;
303*a03ca8b9SKrzysztof Kosiński 
304*a03ca8b9SKrzysztof Kosiński   has_parsed_relocs_ = true;
305*a03ca8b9SKrzysztof Kosiński   DCHECK(reloc_block_offsets_.empty());
306*a03ca8b9SKrzysztof Kosiński 
307*a03ca8b9SKrzysztof Kosiński   offset_t relocs_offset =
308*a03ca8b9SKrzysztof Kosiński       translator_.RvaToOffset(base_relocation_table_->virtual_address);
309*a03ca8b9SKrzysztof Kosiński   size_t relocs_size = base_relocation_table_->size;
310*a03ca8b9SKrzysztof Kosiński   const BufferRegion temp_reloc_region = {relocs_offset, relocs_size};
311*a03ca8b9SKrzysztof Kosiński 
312*a03ca8b9SKrzysztof Kosiński   // Reject bogus relocs. It's possible to have no reloc, so this is non-fatal!
313*a03ca8b9SKrzysztof Kosiński   if (relocs_offset == kInvalidOffset || !image_.covers(temp_reloc_region))
314*a03ca8b9SKrzysztof Kosiński     return false;
315*a03ca8b9SKrzysztof Kosiński 
316*a03ca8b9SKrzysztof Kosiński   // Precompute offsets of all reloc blocks.
317*a03ca8b9SKrzysztof Kosiński   if (!RelocRvaReaderWin32::FindRelocBlocks(image_, temp_reloc_region,
318*a03ca8b9SKrzysztof Kosiński                                             &reloc_block_offsets_)) {
319*a03ca8b9SKrzysztof Kosiński     return false;
320*a03ca8b9SKrzysztof Kosiński   }
321*a03ca8b9SKrzysztof Kosiński   // Reassign |reloc_region_| only on success.
322*a03ca8b9SKrzysztof Kosiński   reloc_region_ = temp_reloc_region;
323*a03ca8b9SKrzysztof Kosiński   return true;
324*a03ca8b9SKrzysztof Kosiński }
325*a03ca8b9SKrzysztof Kosiński 
326*a03ca8b9SKrzysztof Kosiński template <class TRAITS>
ParseAndStoreAbs32()327*a03ca8b9SKrzysztof Kosiński bool DisassemblerWin32<TRAITS>::ParseAndStoreAbs32() {
328*a03ca8b9SKrzysztof Kosiński   if (has_parsed_abs32_)
329*a03ca8b9SKrzysztof Kosiński     return true;
330*a03ca8b9SKrzysztof Kosiński   has_parsed_abs32_ = true;
331*a03ca8b9SKrzysztof Kosiński 
332*a03ca8b9SKrzysztof Kosiński   // Read reloc targets as preliminary abs32 locations.
333*a03ca8b9SKrzysztof Kosiński   std::unique_ptr<ReferenceReader> relocs = MakeReadRelocs(0, offset_t(size()));
334*a03ca8b9SKrzysztof Kosiński   for (auto ref = relocs->GetNext(); ref.has_value(); ref = relocs->GetNext())
335*a03ca8b9SKrzysztof Kosiński     abs32_locations_.push_back(ref->target);
336*a03ca8b9SKrzysztof Kosiński 
337*a03ca8b9SKrzysztof Kosiński   std::sort(abs32_locations_.begin(), abs32_locations_.end());
338*a03ca8b9SKrzysztof Kosiński 
339*a03ca8b9SKrzysztof Kosiński   // Abs32 references must have targets translatable to offsets. Remove those
340*a03ca8b9SKrzysztof Kosiński   // that are unable to do so.
341*a03ca8b9SKrzysztof Kosiński   size_t num_untranslatable = RemoveUntranslatableAbs32(
342*a03ca8b9SKrzysztof Kosiński       image_, {Traits::kBitness, image_base_}, translator_, &abs32_locations_);
343*a03ca8b9SKrzysztof Kosiński   LOG_IF(WARNING, num_untranslatable) << "Removed " << num_untranslatable
344*a03ca8b9SKrzysztof Kosiński                                       << " untranslatable abs32 references.";
345*a03ca8b9SKrzysztof Kosiński 
346*a03ca8b9SKrzysztof Kosiński   // Abs32 reference bodies must not overlap. If found, simply remove them.
347*a03ca8b9SKrzysztof Kosiński   size_t num_overlapping =
348*a03ca8b9SKrzysztof Kosiński       RemoveOverlappingAbs32Locations(Traits::kVAWidth, &abs32_locations_);
349*a03ca8b9SKrzysztof Kosiński   LOG_IF(WARNING, num_overlapping)
350*a03ca8b9SKrzysztof Kosiński       << "Removed " << num_overlapping
351*a03ca8b9SKrzysztof Kosiński       << " abs32 references with overlapping bodies.";
352*a03ca8b9SKrzysztof Kosiński 
353*a03ca8b9SKrzysztof Kosiński   abs32_locations_.shrink_to_fit();
354*a03ca8b9SKrzysztof Kosiński   return true;
355*a03ca8b9SKrzysztof Kosiński }
356*a03ca8b9SKrzysztof Kosiński 
357*a03ca8b9SKrzysztof Kosiński template <class TRAITS>
ParseAndStoreRel32()358*a03ca8b9SKrzysztof Kosiński bool DisassemblerWin32<TRAITS>::ParseAndStoreRel32() {
359*a03ca8b9SKrzysztof Kosiński   if (has_parsed_rel32_)
360*a03ca8b9SKrzysztof Kosiński     return true;
361*a03ca8b9SKrzysztof Kosiński   has_parsed_rel32_ = true;
362*a03ca8b9SKrzysztof Kosiński 
363*a03ca8b9SKrzysztof Kosiński   ParseAndStoreAbs32();
364*a03ca8b9SKrzysztof Kosiński 
365*a03ca8b9SKrzysztof Kosiński   AddressTranslator::RvaToOffsetCache target_rva_checker(translator_);
366*a03ca8b9SKrzysztof Kosiński 
367*a03ca8b9SKrzysztof Kosiński   for (const pe::ImageSectionHeader& section : sections_) {
368*a03ca8b9SKrzysztof Kosiński     if (!IsWin32CodeSection<Traits>(section))
369*a03ca8b9SKrzysztof Kosiński       continue;
370*a03ca8b9SKrzysztof Kosiński 
371*a03ca8b9SKrzysztof Kosiński     rva_t start_rva = section.virtual_address;
372*a03ca8b9SKrzysztof Kosiński     rva_t end_rva = start_rva + section.virtual_size;
373*a03ca8b9SKrzysztof Kosiński 
374*a03ca8b9SKrzysztof Kosiński     // |virtual_size < size_of_raw_data| is possible. In this case, disassembly
375*a03ca8b9SKrzysztof Kosiński     // should not proceed beyond |virtual_size|, so rel32 location RVAs remain
376*a03ca8b9SKrzysztof Kosiński     // translatable to file offsets.
377*a03ca8b9SKrzysztof Kosiński     uint32_t size_to_use =
378*a03ca8b9SKrzysztof Kosiński         std::min(section.virtual_size, section.size_of_raw_data);
379*a03ca8b9SKrzysztof Kosiński     ConstBufferView region =
380*a03ca8b9SKrzysztof Kosiński         image_[{section.file_offset_of_raw_data, size_to_use}];
381*a03ca8b9SKrzysztof Kosiński     Abs32GapFinder gap_finder(image_, region, abs32_locations_,
382*a03ca8b9SKrzysztof Kosiński                               Traits::kVAWidth);
383*a03ca8b9SKrzysztof Kosiński     typename Traits::RelFinder rel_finder(image_, translator_);
384*a03ca8b9SKrzysztof Kosiński     // Iterate over gaps between abs32 references, to avoid collision.
385*a03ca8b9SKrzysztof Kosiński     while (gap_finder.FindNext()) {
386*a03ca8b9SKrzysztof Kosiński       rel_finder.SetRegion(gap_finder.GetGap());
387*a03ca8b9SKrzysztof Kosiński       // Heuristically detect rel32 references, store if valid.
388*a03ca8b9SKrzysztof Kosiński       while (rel_finder.FindNext()) {
389*a03ca8b9SKrzysztof Kosiński         auto rel32 = rel_finder.GetRel32();
390*a03ca8b9SKrzysztof Kosiński         if (target_rva_checker.IsValid(rel32.target_rva) &&
391*a03ca8b9SKrzysztof Kosiński             (rel32.can_point_outside_section ||
392*a03ca8b9SKrzysztof Kosiński              (start_rva <= rel32.target_rva && rel32.target_rva < end_rva))) {
393*a03ca8b9SKrzysztof Kosiński           rel_finder.Accept();
394*a03ca8b9SKrzysztof Kosiński           rel32_locations_.push_back(rel32.location);
395*a03ca8b9SKrzysztof Kosiński         }
396*a03ca8b9SKrzysztof Kosiński       }
397*a03ca8b9SKrzysztof Kosiński     }
398*a03ca8b9SKrzysztof Kosiński   }
399*a03ca8b9SKrzysztof Kosiński   rel32_locations_.shrink_to_fit();
400*a03ca8b9SKrzysztof Kosiński   // |sections_| entries are usually sorted by offset, but there's no guarantee.
401*a03ca8b9SKrzysztof Kosiński   // So sort explicitly, to be sure.
402*a03ca8b9SKrzysztof Kosiński   std::sort(rel32_locations_.begin(), rel32_locations_.end());
403*a03ca8b9SKrzysztof Kosiński   return true;
404*a03ca8b9SKrzysztof Kosiński }
405*a03ca8b9SKrzysztof Kosiński 
406*a03ca8b9SKrzysztof Kosiński // Explicit instantiation for supported classes.
407*a03ca8b9SKrzysztof Kosiński template class DisassemblerWin32<Win32X86Traits>;
408*a03ca8b9SKrzysztof Kosiński template class DisassemblerWin32<Win32X64Traits>;
409*a03ca8b9SKrzysztof Kosiński 
410*a03ca8b9SKrzysztof Kosiński }  // namespace zucchini
411