src/x86_decode.hpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #ifndef DETOURMODKIT_X86_DECODE_HPP | ||
| 2 | #define DETOURMODKIT_X86_DECODE_HPP | ||
| 3 | |||
| 4 | #include "DetourModKit/memory.hpp" | ||
| 5 | |||
| 6 | #include <cstdint> | ||
| 7 | #include <cstring> | ||
| 8 | #include <optional> | ||
| 9 | |||
| 10 | namespace DetourModKit::detail | ||
| 11 | { | ||
| 12 | [[nodiscard]] inline std::optional<std::uintptr_t> | ||
| 13 | 7 | decode_e9_rel32(std::uintptr_t address) noexcept | |
| 14 | { | ||
| 15 |
2/2✓ Branch 3 → 4 taken 1 time.
✓ Branch 3 → 5 taken 6 times.
|
7 | if (!Memory::is_readable(reinterpret_cast<const void *>(address), 5)) |
| 16 | { | ||
| 17 | 1 | return std::nullopt; | |
| 18 | } | ||
| 19 | 6 | const auto *bytes = reinterpret_cast<const std::uint8_t *>(address); | |
| 20 |
2/2✓ Branch 5 → 6 taken 1 time.
✓ Branch 5 → 7 taken 5 times.
|
6 | if (bytes[0] != 0xE9) |
| 21 | { | ||
| 22 | 1 | return std::nullopt; | |
| 23 | } | ||
| 24 | 5 | std::int32_t disp = 0; | |
| 25 | 5 | std::memcpy(&disp, bytes + 1, sizeof(disp)); | |
| 26 | 5 | return static_cast<std::uintptr_t>( | |
| 27 | 5 | static_cast<std::int64_t>(address) + 5 + disp); | |
| 28 | } | ||
| 29 | |||
| 30 | [[nodiscard]] inline std::optional<std::uintptr_t> | ||
| 31 | 4 | decode_eb_rel8(std::uintptr_t address) noexcept | |
| 32 | { | ||
| 33 |
2/2✓ Branch 3 → 4 taken 1 time.
✓ Branch 3 → 5 taken 3 times.
|
4 | if (!Memory::is_readable(reinterpret_cast<const void *>(address), 2)) |
| 34 | { | ||
| 35 | 1 | return std::nullopt; | |
| 36 | } | ||
| 37 | 3 | const auto *bytes = reinterpret_cast<const std::uint8_t *>(address); | |
| 38 |
2/2✓ Branch 5 → 6 taken 1 time.
✓ Branch 5 → 7 taken 2 times.
|
3 | if (bytes[0] != 0xEB) |
| 39 | { | ||
| 40 | 1 | return std::nullopt; | |
| 41 | } | ||
| 42 | 2 | const auto disp = static_cast<std::int8_t>(bytes[1]); | |
| 43 | 2 | return static_cast<std::uintptr_t>( | |
| 44 | 2 | static_cast<std::int64_t>(address) + 2 + disp); | |
| 45 | } | ||
| 46 | |||
| 47 | // FF 25 disp32 on x86-64 is RIP-relative: the 32-bit signed displacement | ||
| 48 | // is added to the address of the next instruction. On x86 (32-bit) the | ||
| 49 | // same encoding is absolute, which this decoder does not handle. | ||
| 50 | [[nodiscard]] inline std::optional<std::uintptr_t> | ||
| 51 | 5 | decode_ff25_indirect(std::uintptr_t address) noexcept | |
| 52 | { | ||
| 53 | static_assert(sizeof(void *) == 8, | ||
| 54 | "decode_ff25_indirect assumes x86-64 RIP-relative semantics"); | ||
| 55 |
2/2✓ Branch 3 → 4 taken 1 time.
✓ Branch 3 → 5 taken 4 times.
|
5 | if (!Memory::is_readable(reinterpret_cast<const void *>(address), 6)) |
| 56 | { | ||
| 57 | 1 | return std::nullopt; | |
| 58 | } | ||
| 59 | 4 | const auto *bytes = reinterpret_cast<const std::uint8_t *>(address); | |
| 60 |
4/4✓ Branch 5 → 6 taken 3 times.
✓ Branch 5 → 7 taken 1 time.
✓ Branch 6 → 7 taken 1 time.
✓ Branch 6 → 8 taken 2 times.
|
4 | if (bytes[0] != 0xFF || bytes[1] != 0x25) |
| 61 | { | ||
| 62 | 2 | return std::nullopt; | |
| 63 | } | ||
| 64 | 2 | std::int32_t disp = 0; | |
| 65 | 2 | std::memcpy(&disp, bytes + 2, sizeof(disp)); | |
| 66 | 2 | const auto slot_addr = static_cast<std::uintptr_t>( | |
| 67 | 2 | static_cast<std::int64_t>(address) + 6 + disp); | |
| 68 |
2/2✓ Branch 9 → 10 taken 1 time.
✓ Branch 9 → 11 taken 1 time.
|
2 | if (!Memory::is_readable(reinterpret_cast<const void *>(slot_addr), sizeof(std::uintptr_t))) |
| 69 | { | ||
| 70 | 1 | return std::nullopt; | |
| 71 | } | ||
| 72 | 1 | std::uintptr_t indirect_destination = 0; | |
| 73 | 1 | std::memcpy(&indirect_destination, | |
| 74 | reinterpret_cast<const void *>(slot_addr), | ||
| 75 | sizeof(indirect_destination)); | ||
| 76 | 1 | return indirect_destination; | |
| 77 | } | ||
| 78 | } // namespace DetourModKit::detail | ||
| 79 | |||
| 80 | #endif // DETOURMODKIT_X86_DECODE_HPP | ||
| 81 |