|
4 | 4 |
|
5 | 5 | using namespace BinaryNinja; |
6 | 6 |
|
7 | | -BNSegmentFlag SegmentFlagsFromMachOProtections(int initProt, int maxProt) |
8 | | -{ |
9 | | - uint32_t flags = 0; |
10 | | - if (initProt & MACHO_VM_PROT_READ) |
11 | | - flags |= SegmentReadable; |
12 | | - if (initProt & MACHO_VM_PROT_WRITE) |
13 | | - flags |= SegmentWritable; |
14 | | - if (initProt & MACHO_VM_PROT_EXECUTE) |
15 | | - flags |= SegmentExecutable; |
16 | | - if (((initProt & MACHO_VM_PROT_WRITE) == 0) && ((maxProt & MACHO_VM_PROT_WRITE) == 0)) |
17 | | - flags |= SegmentDenyWrite; |
18 | | - if (((initProt & MACHO_VM_PROT_EXECUTE) == 0) && ((maxProt & MACHO_VM_PROT_EXECUTE) == 0)) |
19 | | - flags |= SegmentDenyExecute; |
20 | | - return static_cast<BNSegmentFlag>(flags); |
21 | | -} |
22 | | - |
23 | 7 | int64_t readSLEB128(const uint8_t*& current, const uint8_t* end) |
24 | 8 | { |
25 | 9 | uint8_t cur; |
@@ -157,3 +141,133 @@ bool IsSameFolder(Ref<ProjectFolder> a, Ref<ProjectFolder> b) |
157 | 141 | return a->GetId() == b->GetId(); |
158 | 142 | return false; |
159 | 143 | } |
| 144 | + |
| 145 | +namespace { |
| 146 | + |
| 147 | +// Protection combinations used in XNU. Named to match the conventions in arm_vm_init.c |
| 148 | +constexpr uint32_t PROT_RNX = SegmentReadable | SegmentContainsData | SegmentDenyWrite | SegmentDenyExecute; |
| 149 | +constexpr uint32_t PROT_ROX = SegmentReadable | SegmentExecutable | SegmentContainsCode | SegmentDenyWrite; |
| 150 | +constexpr uint32_t PROT_RWNX = SegmentReadable | SegmentWritable | SegmentContainsData | SegmentDenyExecute; |
| 151 | + |
| 152 | +struct XNUSegmentProtection { |
| 153 | + std::string_view name; |
| 154 | + uint32_t protection; |
| 155 | +}; |
| 156 | + |
| 157 | +// Protections taken from arm_vm_prot_init at |
| 158 | +// https://github.com/apple-oss-distributions/xnu/blob/xnu-12377.1.9/osfmk/arm64/arm_vm_init.c |
| 159 | +constexpr std::array<XNUSegmentProtection, 22> s_initialSegmentProtections = {{ |
| 160 | + // Core XNU Kernel Segments |
| 161 | + {"__TEXT", PROT_RNX}, |
| 162 | + {"__TEXT_EXEC", PROT_ROX}, |
| 163 | + {"__DATA_CONST", PROT_RWNX}, |
| 164 | + {"__DATA", PROT_RWNX}, |
| 165 | + {"__HIB", PROT_RWNX}, |
| 166 | + {"__BOOTDATA", PROT_RWNX}, |
| 167 | + {"__KLD", PROT_ROX}, |
| 168 | + {"__KLDDATA", PROT_RNX}, |
| 169 | + {"__LINKEDIT", PROT_RWNX}, |
| 170 | + {"__LAST", PROT_ROX}, |
| 171 | + {"__LASTDATA_CONST", PROT_RWNX}, |
| 172 | + |
| 173 | + // Prelinked Kext Segments |
| 174 | + {"__PRELINK_TEXT", PROT_RWNX}, |
| 175 | + {"__PLK_DATA_CONST", PROT_RWNX}, |
| 176 | + {"__PLK_TEXT_EXEC", PROT_ROX}, |
| 177 | + {"__PRELINK_DATA", PROT_RWNX}, |
| 178 | + {"__PLK_LINKEDIT", PROT_RWNX}, |
| 179 | + {"__PRELINK_INFO", PROT_RWNX}, |
| 180 | + {"__PLK_LLVM_COV", PROT_RWNX}, |
| 181 | + |
| 182 | + // PPL (Page Protection Layer) Segments |
| 183 | + {"__PPLTEXT", PROT_ROX}, |
| 184 | + {"__PPLTRAMP", PROT_ROX}, |
| 185 | + {"__PPLDATA_CONST", PROT_RNX}, |
| 186 | + {"__PPLDATA", PROT_RWNX}, |
| 187 | +}}; |
| 188 | + |
| 189 | +std::string FormatSegmentFlags(uint32_t flags) |
| 190 | +{ |
| 191 | + std::string perms; |
| 192 | + perms += (flags & SegmentReadable) ? 'R' : '-'; |
| 193 | + perms += (flags & SegmentWritable) ? 'W' : '-'; |
| 194 | + perms += (flags & SegmentExecutable) ? 'X' : '-'; |
| 195 | + |
| 196 | + std::string type; |
| 197 | + if (flags & SegmentContainsCode) |
| 198 | + type = " [CODE]"; |
| 199 | + else if (flags & SegmentContainsData) |
| 200 | + type = " [DATA]"; |
| 201 | + |
| 202 | + std::string denies; |
| 203 | + if (flags & SegmentDenyWrite) |
| 204 | + denies += 'W'; |
| 205 | + if (flags & SegmentDenyExecute) |
| 206 | + denies += 'X'; |
| 207 | + if (!denies.empty()) |
| 208 | + denies = fmt::format(" (deny:{})", denies); |
| 209 | + |
| 210 | + return fmt::format("{}{}{}", perms, type, denies); |
| 211 | +} |
| 212 | + |
| 213 | +// XNU maps certain segments with specific protections regardless of what is in the load command. |
| 214 | +uint32_t SegmentFlagsForKnownXNUSegment(std::string_view segmentName) |
| 215 | +{ |
| 216 | + for (const auto& entry : s_initialSegmentProtections) |
| 217 | + { |
| 218 | + if (segmentName == entry.name) |
| 219 | + return entry.protection; |
| 220 | + } |
| 221 | + return 0; |
| 222 | +} |
| 223 | + |
| 224 | +uint32_t SegmentFlagsFromMachOProtections(int initProt, int maxProt) |
| 225 | +{ |
| 226 | + uint32_t flags = 0; |
| 227 | + if (initProt & MACHO_VM_PROT_READ) |
| 228 | + flags |= SegmentReadable; |
| 229 | + if (initProt & MACHO_VM_PROT_WRITE) |
| 230 | + flags |= SegmentWritable; |
| 231 | + if (initProt & MACHO_VM_PROT_EXECUTE) |
| 232 | + flags |= SegmentExecutable; |
| 233 | + if ((initProt & MACHO_VM_PROT_WRITE) == 0 && (maxProt & MACHO_VM_PROT_WRITE) == 0) |
| 234 | + flags |= SegmentDenyWrite; |
| 235 | + if ((initProt & MACHO_VM_PROT_EXECUTE) == 0 && (maxProt & MACHO_VM_PROT_EXECUTE) == 0) |
| 236 | + flags |= SegmentDenyExecute; |
| 237 | + return static_cast<BNSegmentFlag>(flags); |
| 238 | +} |
| 239 | + |
| 240 | +} // unnamed namespace |
| 241 | + |
| 242 | +uint32_t SegmentFlagsForSegment(const segment_command_64& segment) |
| 243 | +{ |
| 244 | + std::string_view segmentName(segment.segname, std::find(segment.segname, std::end(segment.segname), '\0')); |
| 245 | + uint32_t flagsFromLoadCommand = SegmentFlagsFromMachOProtections(segment.initprot, segment.maxprot); |
| 246 | + if (uint32_t flagsFromKnownXNUSegment = SegmentFlagsForKnownXNUSegment(segmentName)) |
| 247 | + { |
| 248 | + constexpr int MASK = ~(SegmentContainsData | SegmentContainsCode); |
| 249 | + if ((flagsFromKnownXNUSegment & MASK) != (flagsFromLoadCommand & MASK)) |
| 250 | + LogDebugF("Overriding segment protections from load command ({}) with known segment protections {} for segment {} ({:#x} - {:#x})", |
| 251 | + FormatSegmentFlags(flagsFromLoadCommand), FormatSegmentFlags(flagsFromKnownXNUSegment), segmentName, |
| 252 | + segment.vmaddr, segment.vmaddr + segment.vmsize); |
| 253 | + return flagsFromKnownXNUSegment; |
| 254 | + } |
| 255 | + |
| 256 | + return flagsFromLoadCommand; |
| 257 | +} |
| 258 | + |
| 259 | +uint32_t SectionSemanticsForSection(const section_64& section) |
| 260 | +{ |
| 261 | + std::string_view segmentName(section.segname, std::find(section.segname, std::end(section.segname), '\0')); |
| 262 | + int flags = SegmentFlagsForKnownXNUSegment(segmentName); |
| 263 | + if (!flags) |
| 264 | + return 0; |
| 265 | + |
| 266 | + if (flags & SegmentExecutable) |
| 267 | + return ReadOnlyCodeSectionSemantics; |
| 268 | + |
| 269 | + if (flags & SegmentWritable) |
| 270 | + return ReadWriteDataSectionSemantics; |
| 271 | + |
| 272 | + return ReadOnlyDataSectionSemantics; |
| 273 | +} |
0 commit comments