summaryrefslogtreecommitdiff
path: root/view/kernelcache/core/Utility.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'view/kernelcache/core/Utility.cpp')
-rw-r--r--view/kernelcache/core/Utility.cpp146
1 files changed, 130 insertions, 16 deletions
diff --git a/view/kernelcache/core/Utility.cpp b/view/kernelcache/core/Utility.cpp
index 515d62c7..d2788faf 100644
--- a/view/kernelcache/core/Utility.cpp
+++ b/view/kernelcache/core/Utility.cpp
@@ -4,22 +4,6 @@
using namespace BinaryNinja;
-BNSegmentFlag SegmentFlagsFromMachOProtections(int initProt, int maxProt)
-{
- uint32_t flags = 0;
- if (initProt & MACHO_VM_PROT_READ)
- flags |= SegmentReadable;
- if (initProt & MACHO_VM_PROT_WRITE)
- flags |= SegmentWritable;
- if (initProt & MACHO_VM_PROT_EXECUTE)
- flags |= SegmentExecutable;
- if (((initProt & MACHO_VM_PROT_WRITE) == 0) && ((maxProt & MACHO_VM_PROT_WRITE) == 0))
- flags |= SegmentDenyWrite;
- if (((initProt & MACHO_VM_PROT_EXECUTE) == 0) && ((maxProt & MACHO_VM_PROT_EXECUTE) == 0))
- flags |= SegmentDenyExecute;
- return static_cast<BNSegmentFlag>(flags);
-}
-
int64_t readSLEB128(const uint8_t*& current, const uint8_t* end)
{
uint8_t cur;
@@ -157,3 +141,133 @@ bool IsSameFolder(Ref<ProjectFolder> a, Ref<ProjectFolder> b)
return a->GetId() == b->GetId();
return false;
}
+
+namespace {
+
+// Protection combinations used in XNU. Named to match the conventions in arm_vm_init.c
+constexpr uint32_t PROT_RNX = SegmentReadable | SegmentContainsData | SegmentDenyWrite | SegmentDenyExecute;
+constexpr uint32_t PROT_ROX = SegmentReadable | SegmentExecutable | SegmentContainsCode | SegmentDenyWrite;
+constexpr uint32_t PROT_RWNX = SegmentReadable | SegmentWritable | SegmentContainsData | SegmentDenyExecute;
+
+struct XNUSegmentProtection {
+ std::string_view name;
+ uint32_t protection;
+};
+
+// Protections taken from arm_vm_prot_init at
+// https://github.com/apple-oss-distributions/xnu/blob/xnu-12377.1.9/osfmk/arm64/arm_vm_init.c
+constexpr std::array<XNUSegmentProtection, 22> s_initialSegmentProtections = {{
+ // Core XNU Kernel Segments
+ {"__TEXT", PROT_RNX},
+ {"__TEXT_EXEC", PROT_ROX},
+ {"__DATA_CONST", PROT_RWNX},
+ {"__DATA", PROT_RWNX},
+ {"__HIB", PROT_RWNX},
+ {"__BOOTDATA", PROT_RWNX},
+ {"__KLD", PROT_ROX},
+ {"__KLDDATA", PROT_RNX},
+ {"__LINKEDIT", PROT_RWNX},
+ {"__LAST", PROT_ROX},
+ {"__LASTDATA_CONST", PROT_RWNX},
+
+ // Prelinked Kext Segments
+ {"__PRELINK_TEXT", PROT_RWNX},
+ {"__PLK_DATA_CONST", PROT_RWNX},
+ {"__PLK_TEXT_EXEC", PROT_ROX},
+ {"__PRELINK_DATA", PROT_RWNX},
+ {"__PLK_LINKEDIT", PROT_RWNX},
+ {"__PRELINK_INFO", PROT_RWNX},
+ {"__PLK_LLVM_COV", PROT_RWNX},
+
+ // PPL (Page Protection Layer) Segments
+ {"__PPLTEXT", PROT_ROX},
+ {"__PPLTRAMP", PROT_ROX},
+ {"__PPLDATA_CONST", PROT_RNX},
+ {"__PPLDATA", PROT_RWNX},
+}};
+
+std::string FormatSegmentFlags(uint32_t flags)
+{
+ std::string perms;
+ perms += (flags & SegmentReadable) ? 'R' : '-';
+ perms += (flags & SegmentWritable) ? 'W' : '-';
+ perms += (flags & SegmentExecutable) ? 'X' : '-';
+
+ std::string type;
+ if (flags & SegmentContainsCode)
+ type = " [CODE]";
+ else if (flags & SegmentContainsData)
+ type = " [DATA]";
+
+ std::string denies;
+ if (flags & SegmentDenyWrite)
+ denies += 'W';
+ if (flags & SegmentDenyExecute)
+ denies += 'X';
+ if (!denies.empty())
+ denies = fmt::format(" (deny:{})", denies);
+
+ return fmt::format("{}{}{}", perms, type, denies);
+}
+
+// XNU maps certain segments with specific protections regardless of what is in the load command.
+uint32_t SegmentFlagsForKnownXNUSegment(std::string_view segmentName)
+{
+ for (const auto& entry : s_initialSegmentProtections)
+ {
+ if (segmentName == entry.name)
+ return entry.protection;
+ }
+ return 0;
+}
+
+uint32_t SegmentFlagsFromMachOProtections(int initProt, int maxProt)
+{
+ uint32_t flags = 0;
+ if (initProt & MACHO_VM_PROT_READ)
+ flags |= SegmentReadable;
+ if (initProt & MACHO_VM_PROT_WRITE)
+ flags |= SegmentWritable;
+ if (initProt & MACHO_VM_PROT_EXECUTE)
+ flags |= SegmentExecutable;
+ if ((initProt & MACHO_VM_PROT_WRITE) == 0 && (maxProt & MACHO_VM_PROT_WRITE) == 0)
+ flags |= SegmentDenyWrite;
+ if ((initProt & MACHO_VM_PROT_EXECUTE) == 0 && (maxProt & MACHO_VM_PROT_EXECUTE) == 0)
+ flags |= SegmentDenyExecute;
+ return static_cast<BNSegmentFlag>(flags);
+}
+
+} // unnamed namespace
+
+uint32_t SegmentFlagsForSegment(const segment_command_64& segment)
+{
+ std::string_view segmentName(segment.segname, std::find(segment.segname, std::end(segment.segname), '\0'));
+ uint32_t flagsFromLoadCommand = SegmentFlagsFromMachOProtections(segment.initprot, segment.maxprot);
+ if (uint32_t flagsFromKnownXNUSegment = SegmentFlagsForKnownXNUSegment(segmentName))
+ {
+ constexpr int MASK = ~(SegmentContainsData | SegmentContainsCode);
+ if ((flagsFromKnownXNUSegment & MASK) != (flagsFromLoadCommand & MASK))
+ LogDebugF("Overriding segment protections from load command ({}) with known segment protections {} for segment {} ({:#x} - {:#x})",
+ FormatSegmentFlags(flagsFromLoadCommand), FormatSegmentFlags(flagsFromKnownXNUSegment), segmentName,
+ segment.vmaddr, segment.vmaddr + segment.vmsize);
+ return flagsFromKnownXNUSegment;
+ }
+
+ return flagsFromLoadCommand;
+}
+
+uint32_t SectionSemanticsForSection(const section_64& section)
+{
+ std::string_view segmentName(section.segname, std::find(section.segname, std::end(section.segname), '\0'));
+ int flags = SegmentFlagsForKnownXNUSegment(segmentName);
+ if (!flags)
+ return 0;
+
+ if (flags & SegmentExecutable)
+ return ReadOnlyCodeSectionSemantics;
+
+ if (flags & SegmentWritable)
+ return ReadWriteDataSectionSemantics;
+
+ return ReadOnlyDataSectionSemantics;
+}