* Target analysis. Find instructions that can be reached by a * control-flow-transfer, including returns. This can be a "safe" * overapproximation, even if the general case is undecidable. */
| 96 | * overapproximation, even if the general case is undecidable. |
| 97 | */ |
| 98 | void targetAnalysis(Binary *B) |
| 99 | { |
| 100 | // Step (1): Basic checks |
| 101 | if (B->targets != nullptr || !option_OCFR) |
| 102 | return; |
| 103 | switch (B->mode) |
| 104 | { |
| 105 | case MODE_ELF_EXE: case MODE_ELF_DSO: |
| 106 | break; |
| 107 | default: |
| 108 | warning("target analysis for Windows PE binaries is " |
| 109 | "not-yet-implemented; `-OCFR' will be ignored"); |
| 110 | option_OCFR = false; |
| 111 | return; // Windows PE is NYI |
| 112 | } |
| 113 | bool cet = false; |
| 114 | if (B->elf.features == nullptr || |
| 115 | (*B->elf.features & GNU_PROPERTY_X86_FEATURE_1_IBT) == 0 || |
| 116 | (*B->elf.features & GNU_PROPERTY_X86_FEATURE_1_SHSTK) == 0) |
| 117 | cet = true; |
| 118 | bool pic = B->pic; |
| 119 | const uint8_t *data = B->original.bytes; |
| 120 | const Elf64_Phdr *phdrs = (Elf64_Phdr *)(data + B->elf.ehdr->e_phoff); |
| 121 | size_t phnum = B->elf.ehdr->e_phnum; |
| 122 | const Elf64_Phdr *phdr_dynamic = nullptr; |
| 123 | for (unsigned i = 0; i < phnum; i++) |
| 124 | { |
| 125 | const Elf64_Phdr *phdr = phdrs + i; |
| 126 | if (phdr->p_type == PT_DYNAMIC) |
| 127 | phdr_dynamic = phdr; |
| 128 | if (phdr->p_type != PT_LOAD || (phdr->p_flags & PF_X) == 0) |
| 129 | continue; |
| 130 | if ((phdr->p_flags & PF_W) != 0) |
| 131 | { |
| 132 | warning("target analysis does not support writable code segments"); |
| 133 | return; // Not read-only |
| 134 | } |
| 135 | } |
| 136 | |
| 137 | // Step (2): Create the target map: |
| 138 | void *ptr = mmap(nullptr, (B->size + PAGE_SIZE) / 8, |
| 139 | PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, |
| 140 | -1, 0); |
| 141 | if (ptr == MAP_FAILED) |
| 142 | error("failed to allocate target map: %s", strerror(errno)); |
| 143 | uint8_t *targets = (uint8_t *)ptr; |
| 144 | B->targets = targets; |
| 145 | |
| 146 | // Step (3): Find all direct jump targets. |
| 147 | // |
| 148 | // Note: This is a basic overapproximation that assumes *all* executable |
| 149 | // byte patterns resembling direct calls/jumps *are* direct |
| 150 | // calls/jumps. This analysis does not assume the binary can be |
| 151 | // disassembled, and safely handles data-in-code, etc. |
| 152 | // |
| 153 | std::set<intptr_t> tables; |
| 154 | for (unsigned i = 0; i < phnum; i++) |
| 155 | { |
no test coverage detected