disassembler_win32.cc 14 KB

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