|
3 | 3 | #pragma once |
4 | 4 |
|
5 | 5 | /// @file BinaryEditor.hpp |
6 | | -/// @brief Abstract interface for format-specific binary mutation. |
| 6 | +/// @brief Variant-based type-erased editor and factory for binary mutation. |
7 | 7 | /// |
8 | | -/// Each platform (ELF, PE, Mach-O) implements this interface. |
9 | | -/// The Loader orchestrator calls these methods without knowing the format. |
| 8 | +/// Each platform editor (ELFEditor, PEEditor, MachOEditor) inherits from |
| 9 | +/// EditorBase<Derived> (CRTP). At the factory boundary, runtime format |
| 10 | +/// selection is done via std::variant<ELFEditor, PEEditor, MachOEditor>. |
10 | 11 | /// |
11 | | -/// Source: D13§D1-D3 (binary patching / loader). |
| 12 | +/// Source: D13 D1-D3 (binary patching / loader). |
12 | 13 |
|
13 | 14 | #include <LoaderTypes.hpp> |
14 | 15 | #include <diagnostic_collector.hpp> |
15 | 16 |
|
| 17 | +#include <ELFEditor.hpp> |
| 18 | +#include <MachOEditor.hpp> |
| 19 | +#include <PEEditor.hpp> |
| 20 | + |
16 | 21 | #include <tl/expected.hpp> |
17 | 22 |
|
18 | | -#include <cstdint> |
19 | | -#include <memory> |
20 | | -#include <string> |
21 | | -#include <string_view> |
22 | | -#include <vector> |
| 23 | +#include <variant> |
23 | 24 |
|
24 | 25 | namespace VMPilot::Loader { |
25 | 26 |
|
26 | | -class BinaryEditor { |
27 | | -public: |
28 | | - virtual ~BinaryEditor() = default; |
29 | | - |
30 | | - // --- Query (read-only, no mutation) --- |
31 | | - |
32 | | - /// Get .text section VA and size. |
33 | | - [[nodiscard]] virtual TextSectionInfo text_section() const noexcept = 0; |
34 | | - |
35 | | - /// Compute the VA where the next segment would be placed, without |
36 | | - /// actually creating it. Enables build_payload() to resolve all |
37 | | - /// displacements before injection. |
38 | | - [[nodiscard]] virtual uint64_t |
39 | | - next_segment_va(uint64_t alignment) const noexcept = 0; |
40 | | - |
41 | | - /// Check if the binary enforces Control-Flow Integrity. |
42 | | - /// ELF: .note.gnu.property with GNU_PROPERTY_X86_FEATURE_1_IBT |
43 | | - /// or GNU_PROPERTY_AARCH64_FEATURE_1_BTI |
44 | | - /// PE: IMAGE_DLLCHARACTERISTICS_CET_COMPAT in DllCharacteristics |
45 | | - /// MachO: Always false for now (Apple controls BTI via code signing) |
46 | | - /// Loader uses this to emit diagnostics confirming stubs have landing pads. |
47 | | - [[nodiscard]] virtual bool cfi_enforced() const noexcept = 0; |
48 | | - |
49 | | - /// Find usable gaps (NOP sleds, INT3/BRK padding, alignment fill) |
50 | | - /// in .text that are at least `min_size` bytes. |
51 | | - /// Returned sorted by size descending (largest first). |
52 | | - /// |
53 | | - /// @note RESERVED — not wired to patch(). Current implementations have |
54 | | - /// known limitations (false positives on embedded data, no cross- |
55 | | - /// reference checks). See GitHub issue #9 for the rewrite plan. |
56 | | - [[nodiscard]] virtual std::vector<TextGap> |
57 | | - find_text_gaps(std::size_t min_size) const noexcept = 0; |
58 | | - |
59 | | - // --- Mutate --- |
60 | | - |
61 | | - /// Create a new loadable segment/section with the given data. |
62 | | - /// This is the ONLY injection method currently used by patch(). |
63 | | - [[nodiscard]] virtual tl::expected<NewSegmentInfo, Common::DiagnosticCode> |
64 | | - add_segment(std::string_view name, const std::vector<uint8_t>& data, |
65 | | - uint64_t alignment, |
66 | | - Common::DiagnosticCollector& diag) noexcept = 0; |
67 | | - |
68 | | - /// Extend the .text section/segment to accommodate additional data. |
69 | | - /// Returns the VA where data was placed (at the end of .text, aligned). |
70 | | - /// |
71 | | - /// @warning RESERVED — not wired to patch(). All three platform |
72 | | - /// implementations currently perform binary shifting, which |
73 | | - /// corrupts metadata (linkedit offsets, PE headers, section VAs). |
74 | | - /// Must be rewritten as slack-space-only before use. |
75 | | - /// See GitHub issue #9. |
76 | | - [[nodiscard]] virtual tl::expected<NewSegmentInfo, Common::DiagnosticCode> |
77 | | - extend_text(const std::vector<uint8_t>& data, |
78 | | - uint64_t alignment, |
79 | | - Common::DiagnosticCollector& diag) noexcept = 0; |
80 | | - |
81 | | - /// Overwrite bytes in .text at the given VA. |
82 | | - [[nodiscard]] virtual tl::expected<void, Common::DiagnosticCode> |
83 | | - overwrite_text(uint64_t va, const uint8_t* data, size_t len, |
84 | | - Common::DiagnosticCollector& diag) noexcept = 0; |
85 | | - |
86 | | - /// Add a runtime library dependency (LC_LOAD_DYLIB / DT_NEEDED / import). |
87 | | - [[nodiscard]] virtual tl::expected<void, Common::DiagnosticCode> |
88 | | - add_runtime_dep(std::string_view install_name, |
89 | | - Common::DiagnosticCollector& diag) noexcept = 0; |
90 | | - |
91 | | - /// Invalidate old code signature. Called before save(). |
92 | | - /// Mach-O: zero LC_CODE_SIGNATURE. PE: zero Certificate Table. ELF: no-op. |
93 | | - virtual void invalidate_signature() noexcept = 0; |
94 | | - |
95 | | - // --- Persist --- |
96 | | - |
97 | | - [[nodiscard]] virtual tl::expected<void, Common::DiagnosticCode> |
98 | | - save(const std::string& path, |
99 | | - Common::DiagnosticCollector& diag) noexcept = 0; |
100 | | -}; |
| 27 | +/// Runtime-polymorphic editor handle. std::visit dispatches to the |
| 28 | +/// concrete CRTP editor without vtable indirection. |
| 29 | +using AnyEditor = std::variant<ELFEditor, PEEditor, MachOEditor>; |
101 | 30 |
|
102 | | -/// Factory. Opens a binary file and returns the appropriate editor. |
103 | | -[[nodiscard]] tl::expected<std::unique_ptr<BinaryEditor>, Common::DiagnosticCode> |
| 31 | +/// Factory. Opens a binary file and returns the appropriate editor |
| 32 | +/// wrapped in the AnyEditor variant. |
| 33 | +[[nodiscard]] tl::expected<AnyEditor, Common::DiagnosticCode> |
104 | 34 | open_binary(const std::string& path, Common::FileFormat format, |
105 | 35 | Common::DiagnosticCollector& diag) noexcept; |
106 | 36 |
|
|
0 commit comments