rel32_finder.cc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  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/rel32_finder.h"
  5. #include <algorithm>
  6. #include "base/numerics/safe_conversions.h"
  7. namespace zucchini {
  8. /******** Abs32GapFinder ********/
  9. Abs32GapFinder::Abs32GapFinder(ConstBufferView image,
  10. ConstBufferView region,
  11. const std::deque<offset_t>& abs32_locations,
  12. size_t abs32_width)
  13. : base_(image.begin()),
  14. region_end_(region.end()),
  15. abs32_end_(abs32_locations.end()),
  16. abs32_width_(abs32_width) {
  17. DCHECK_GT(abs32_width, size_t(0));
  18. DCHECK_GE(region.begin(), image.begin());
  19. DCHECK_LE(region.end(), image.end());
  20. const offset_t begin_offset =
  21. base::checked_cast<offset_t>(region.begin() - image.begin());
  22. // Find the first |abs32_cur_| with |*abs32_cur_ >= begin_offset|.
  23. abs32_cur_ = std::lower_bound(abs32_locations.begin(), abs32_locations.end(),
  24. begin_offset);
  25. // Find lower boundary, accounting for the possibility that |abs32_cur_[-1]|
  26. // may straddle across |region.begin()|.
  27. cur_lo_ = region.begin();
  28. if (abs32_cur_ > abs32_locations.begin())
  29. cur_lo_ = std::max(cur_lo_, image.begin() + abs32_cur_[-1] + abs32_width_);
  30. }
  31. Abs32GapFinder::~Abs32GapFinder() = default;
  32. bool Abs32GapFinder::FindNext() {
  33. // Iterate over |[abs32_cur_, abs32_end_)| and emit segments.
  34. while (abs32_cur_ != abs32_end_ && base_ + *abs32_cur_ < region_end_) {
  35. ConstBufferView::const_iterator hi = base_ + *abs32_cur_;
  36. gap_ = ConstBufferView::FromRange(cur_lo_, hi);
  37. cur_lo_ = hi + abs32_width_;
  38. ++abs32_cur_;
  39. if (!gap_.empty())
  40. return true;
  41. }
  42. // Emit final segment.
  43. if (cur_lo_ < region_end_) {
  44. gap_ = ConstBufferView::FromRange(cur_lo_, region_end_);
  45. cur_lo_ = region_end_;
  46. return true;
  47. }
  48. return false;
  49. }
  50. /******** Rel32Finder ********/
  51. Rel32Finder::Rel32Finder(ConstBufferView image,
  52. const AddressTranslator& translator)
  53. : image_(image), offset_to_rva_(translator) {}
  54. Rel32Finder::~Rel32Finder() = default;
  55. void Rel32Finder::SetRegion(ConstBufferView region) {
  56. region_ = region;
  57. accept_it_ = region.begin();
  58. }
  59. bool Rel32Finder::FindNext() {
  60. NextIterators next_iters = Scan(region_);
  61. if (next_iters.reject == nullptr) {
  62. region_.seek(region_.end());
  63. return false;
  64. }
  65. region_.seek(next_iters.reject);
  66. accept_it_ = next_iters.accept;
  67. DCHECK_GE(accept_it_, region_.begin());
  68. DCHECK_LE(accept_it_, region_.end());
  69. return true;
  70. }
  71. void Rel32Finder::Accept() {
  72. region_.seek(accept_it_);
  73. }
  74. /******** Rel32FinderIntel ********/
  75. Rel32Finder::NextIterators Rel32FinderIntel::SetResult(
  76. ConstBufferView::const_iterator cursor,
  77. uint32_t opcode_size,
  78. bool can_point_outside_section) {
  79. offset_t location =
  80. base::checked_cast<offset_t>((cursor + opcode_size) - image_.begin());
  81. rva_t location_rva = offset_to_rva_.Convert(location);
  82. DCHECK_NE(location_rva, kInvalidRva);
  83. rva_t target_rva = location_rva + 4 + image_.read<uint32_t>(location);
  84. rel32_ = {location, target_rva, can_point_outside_section};
  85. return {cursor + 1, cursor + (opcode_size + 4)};
  86. }
  87. /******** Rel32FinderX86 ********/
  88. Rel32Finder::NextIterators Rel32FinderX86::Scan(ConstBufferView region) {
  89. ConstBufferView::const_iterator cursor = region.begin();
  90. while (cursor < region.end()) {
  91. // Heuristic rel32 detection by looking for opcodes that use them.
  92. if (cursor + 5 <= region.end()) {
  93. if (cursor[0] == 0xE8 || cursor[0] == 0xE9) // JMP rel32; CALL rel32
  94. return SetResult(cursor, 1, false);
  95. }
  96. if (cursor + 6 <= region.end()) {
  97. if (cursor[0] == 0x0F && (cursor[1] & 0xF0) == 0x80) // Jcc long form
  98. return SetResult(cursor, 2, false);
  99. }
  100. ++cursor;
  101. }
  102. return {nullptr, nullptr};
  103. }
  104. /******** Rel32FinderX64 ********/
  105. Rel32Finder::NextIterators Rel32FinderX64::Scan(ConstBufferView region) {
  106. ConstBufferView::const_iterator cursor = region.begin();
  107. while (cursor < region.end()) {
  108. // Heuristic rel32 detection by looking for opcodes that use them.
  109. if (cursor + 5 <= region.end()) {
  110. if (cursor[0] == 0xE8 || cursor[0] == 0xE9) // JMP rel32; CALL rel32
  111. return SetResult(cursor, 1, false);
  112. }
  113. if (cursor + 6 <= region.end()) {
  114. if (cursor[0] == 0x0F && (cursor[1] & 0xF0) == 0x80) { // Jcc long form
  115. return SetResult(cursor, 2, false);
  116. } else if ((cursor[0] == 0xFF &&
  117. (cursor[1] == 0x15 || cursor[1] == 0x25)) ||
  118. ((cursor[0] == 0x89 || cursor[0] == 0x8B ||
  119. cursor[0] == 0x8D) &&
  120. (cursor[1] & 0xC7) == 0x05)) {
  121. // 6-byte instructions:
  122. // [2-byte opcode] [disp32]:
  123. // Opcode
  124. // FF 15: CALL QWORD PTR [rip+disp32]
  125. // FF 25: JMP QWORD PTR [rip+disp32]
  126. //
  127. // [1-byte opcode] [ModR/M] [disp32]:
  128. // Opcode
  129. // 89: MOV DWORD PTR [rip+disp32],reg
  130. // 8B: MOV reg,DWORD PTR [rip+disp32]
  131. // 8D: LEA reg,[rip+disp32]
  132. // ModR/M : MMRRRMMM
  133. // MM = 00 & MMM = 101 => rip+disp32
  134. // RRR: selects reg operand from [eax|ecx|...|edi]
  135. return SetResult(cursor, 2, true);
  136. }
  137. }
  138. ++cursor;
  139. }
  140. return {nullptr, nullptr};
  141. }
  142. /******** Rel32FinderArm ********/
  143. template <typename ADDR_TYPE>
  144. Rel32FinderArm<ADDR_TYPE>::Rel32FinderArm(ConstBufferView image,
  145. const AddressTranslator& translator)
  146. : Rel32Finder(image, translator) {}
  147. template <typename ADDR_TYPE>
  148. Rel32FinderArm<ADDR_TYPE>::~Rel32FinderArm() = default;
  149. template <typename ADDR_TYPE>
  150. Rel32Finder::NextIterators Rel32FinderArm<ADDR_TYPE>::SetResult(
  151. Result&& result,
  152. ConstBufferView::const_iterator cursor,
  153. int instr_size) {
  154. rel32_ = result;
  155. return {cursor + instr_size, cursor + instr_size};
  156. }
  157. // SetResult() for end of scan.
  158. template <typename ADDR_TYPE>
  159. Rel32Finder::NextIterators Rel32FinderArm<ADDR_TYPE>::SetEmptyResult() {
  160. rel32_ = {kInvalidOffset, kInvalidOffset, ADDR_TYPE::ADDR_NONE};
  161. return {nullptr, nullptr};
  162. }
  163. /******** Rel32FinderAArch32 ********/
  164. Rel32FinderAArch32::Rel32FinderAArch32(ConstBufferView image,
  165. const AddressTranslator& translator,
  166. bool is_thumb2)
  167. : Rel32FinderArm(image, translator), is_thumb2_(is_thumb2) {}
  168. Rel32FinderAArch32::~Rel32FinderAArch32() = default;
  169. Rel32Finder::NextIterators Rel32FinderAArch32::ScanA32(ConstBufferView region) {
  170. // Guard against alignment potentially causing |cursor > region.end()|.
  171. if (region.size() < 4)
  172. return SetEmptyResult();
  173. ConstBufferView::const_iterator cursor = region.begin();
  174. cursor += IncrementForAlignCeil4(cursor - image_.begin());
  175. for (; region.end() - cursor >= 4; cursor += 4) {
  176. offset_t offset = base::checked_cast<offset_t>(cursor - image_.begin());
  177. AArch32Rel32Translator translator;
  178. rva_t instr_rva = offset_to_rva_.Convert(offset);
  179. uint32_t code32 = translator.FetchArmCode32(image_, offset);
  180. rva_t target_rva = kInvalidRva;
  181. if (translator.ReadA24(instr_rva, code32, &target_rva)) {
  182. return SetResult({offset, target_rva, AArch32Rel32Translator::ADDR_A24},
  183. cursor, 4);
  184. }
  185. }
  186. return SetEmptyResult();
  187. }
  188. Rel32Finder::NextIterators Rel32FinderAArch32::ScanT32(ConstBufferView region) {
  189. // Guard against alignment potentially causing |cursor > region.end()|.
  190. if (region.size() < 2)
  191. return SetEmptyResult();
  192. ConstBufferView::const_iterator cursor = region.begin();
  193. cursor += IncrementForAlignCeil2(cursor - image_.begin());
  194. while (region.end() - cursor >= 2) {
  195. offset_t offset = base::checked_cast<offset_t>(cursor - image_.begin());
  196. AArch32Rel32Translator translator;
  197. AArch32Rel32Translator::AddrType type = AArch32Rel32Translator::ADDR_NONE;
  198. rva_t instr_rva = offset_to_rva_.Convert(offset);
  199. uint16_t code16 = translator.FetchThumb2Code16(image_, offset);
  200. int instr_size = GetThumb2InstructionSize(code16);
  201. rva_t target_rva = kInvalidRva;
  202. if (instr_size == 2) { // 16-bit THUMB2 instruction.
  203. if (translator.ReadT8(instr_rva, code16, &target_rva))
  204. type = AArch32Rel32Translator::ADDR_T8;
  205. else if (translator.ReadT11(instr_rva, code16, &target_rva))
  206. type = AArch32Rel32Translator::ADDR_T11;
  207. } else { // |instr_size == 4|: 32-bit THUMB2 instruction.
  208. if (region.end() - cursor >= 4) {
  209. uint32_t code32 = translator.FetchThumb2Code32(image_, offset);
  210. if (translator.ReadT20(instr_rva, code32, &target_rva))
  211. type = AArch32Rel32Translator::ADDR_T20;
  212. else if (translator.ReadT24(instr_rva, code32, &target_rva))
  213. type = AArch32Rel32Translator::ADDR_T24;
  214. }
  215. }
  216. if (type != AArch32Rel32Translator::ADDR_NONE)
  217. return SetResult({offset, target_rva, type}, cursor, instr_size);
  218. cursor += instr_size;
  219. }
  220. return SetEmptyResult();
  221. }
  222. Rel32Finder::NextIterators Rel32FinderAArch32::Scan(ConstBufferView region) {
  223. return is_thumb2_ ? ScanT32(region) : ScanA32(region);
  224. }
  225. /******** Rel32FinderAArch64 ********/
  226. Rel32FinderAArch64::Rel32FinderAArch64(ConstBufferView image,
  227. const AddressTranslator& translator)
  228. : Rel32FinderArm(image, translator) {}
  229. Rel32FinderAArch64::~Rel32FinderAArch64() = default;
  230. Rel32Finder::NextIterators Rel32FinderAArch64::Scan(ConstBufferView region) {
  231. // Guard against alignment potentially causing |cursor > region.end()|.
  232. if (region.size() < 4)
  233. return SetEmptyResult();
  234. ConstBufferView::const_iterator cursor = region.begin();
  235. cursor += IncrementForAlignCeil4(cursor - image_.begin());
  236. for (; region.end() - cursor >= 4; cursor += 4) {
  237. offset_t offset = base::checked_cast<offset_t>(cursor - image_.begin());
  238. // For simplicity we assume RVA fits within 32-bits.
  239. AArch64Rel32Translator translator;
  240. AArch64Rel32Translator::AddrType type = AArch64Rel32Translator::ADDR_NONE;
  241. rva_t instr_rva = offset_to_rva_.Convert(offset);
  242. uint32_t code32 = translator.FetchCode32(image_, offset);
  243. rva_t target_rva = kInvalidRva;
  244. if (translator.ReadImmd14(instr_rva, code32, &target_rva)) {
  245. type = AArch64Rel32Translator::ADDR_IMMD14;
  246. } else if (translator.ReadImmd19(instr_rva, code32, &target_rva)) {
  247. type = AArch64Rel32Translator::ADDR_IMMD19;
  248. } else if (translator.ReadImmd26(instr_rva, code32, &target_rva)) {
  249. type = AArch64Rel32Translator::ADDR_IMMD26;
  250. }
  251. if (type != AArch64Rel32Translator::ADDR_NONE)
  252. return SetResult({offset, target_rva, type}, cursor, 4);
  253. }
  254. return SetEmptyResult();
  255. }
  256. } // namespace zucchini