Skip to content

Commit

Permalink
Merge pull request #51 from p-x9/feature/chaind-fixup-bind-rebase
Browse files Browse the repository at this point in the history
Dyld Chained Fix Up Pointer
  • Loading branch information
p-x9 authored Feb 21, 2024
2 parents 307fec4 + 0dbcad5 commit 0bde958
Show file tree
Hide file tree
Showing 17 changed files with 1,160 additions and 135 deletions.
76 changes: 76 additions & 0 deletions Sources/MachOKit/Extension/dyld_chained_ptr+.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
//
// dyld_chained_ptr+.swift
//
//
// Created by p-x9 on 2024/02/19.
//
//

import Foundation
import MachOKitC

extension dyld_chained_ptr_arm64e_rebase: CustomStringConvertible {
public var description: String {
"dyld_chained_ptr_arm64e_rebase(target: \(target), high8: \(high8), next: \(next), bind: \(bind), auth: \(auth))"
}
}

extension dyld_chained_ptr_arm64e_bind: CustomStringConvertible {
public var description: String {
"dyld_chained_ptr_arm64e_bind(ordinal: \(ordinal), zero: \(zero), addend: \(addend), next: \(next), bind: \(bind), auth: \(auth))"
}
}

extension dyld_chained_ptr_arm64e_auth_rebase: CustomStringConvertible {
public var description: String {
"dyld_chained_ptr_arm64e_auth_rebase(target: \(target), diversity: \(diversity), addrDiv: \(addrDiv), key: \(key), next: \(next), bind: \(bind), auth: \(auth))"
}
}

extension dyld_chained_ptr_arm64e_auth_bind: CustomStringConvertible {
public var description: String {
"dyld_chained_ptr_arm64e_auth_bind(ordinal: \(ordinal), zero: \(zero), diversity: \(diversity), addrDiv: \(addrDiv), key: \(key), next: \(next), bind: \(bind), auth: \(auth))"
}
}

extension dyld_chained_ptr_64_rebase: CustomStringConvertible {
public var description: String {
"dyld_chained_ptr_64_rebase(target: \(target), high8: \(high8), reserved: \(reserved), next: \(next), bind: \(bind))"
}
}

extension dyld_chained_ptr_64_bind: CustomStringConvertible {
public var description: String {
"dyld_chained_ptr_64_bind(ordinal: \(ordinal), addend: \(addend), reserved: \(reserved), next: \(next), bind: \(bind))"
}
}

extension dyld_chained_ptr_64_kernel_cache_rebase: CustomStringConvertible {
public var description: String {
"dyld_chained_ptr_64_kernel_cache_rebase(target: \(target), cacheLevel: \(cacheLevel), diversity: \(diversity), addrDiv: \(addrDiv), key: \(key), next: \(next), isAuth: \(isAuth))"
}
}

extension dyld_chained_ptr_32_rebase: CustomStringConvertible {
public var description: String {
"dyld_chained_ptr_32_rebase(target: \(target), next: \(next), bind: \(bind))"
}
}

extension dyld_chained_ptr_32_bind: CustomStringConvertible {
public var description: String {
"dyld_chained_ptr_32_bind(ordinal: \(ordinal), addend: \(addend), next: \(next), bind: \(bind))"
}
}

extension dyld_chained_ptr_32_cache_rebase: CustomStringConvertible {
public var description: String {
"dyld_chained_ptr_32_cache_rebase(target: \(target), next: \(next))"
}
}

extension dyld_chained_ptr_32_firmware_rebase: CustomStringConvertible {
public var description: String {
"dyld_chained_ptr_32_firmware_rebase(target: \(target), next: \(next))"
}
}
110 changes: 109 additions & 1 deletion Sources/MachOKit/LoadCommand/SegmentCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import Foundation

public protocol SegmentCommandProtocol: LoadCommandWrapper {
associatedtype SectionType: LayoutWrapper
associatedtype SectionType: SectionProtocol
var segmentName: String { get }
var virtualMemoryAddress: Int { get }
var virtualMemorySize: Int { get }
Expand All @@ -23,6 +23,8 @@ public protocol SegmentCommandProtocol: LoadCommandWrapper {
func endPtr(vmaddrSlide: Int) -> UnsafeRawPointer?
func sections(cmdsStart: UnsafeRawPointer) -> MemorySequence<SectionType>
func sections(in machO: MachOFile) -> DataSequence<SectionType>
func section(at offset: UInt, cmdsStart: UnsafeRawPointer) -> SectionType?
func section(at offset: UInt, in machO: MachOFile) -> SectionType?
}

extension SegmentCommandProtocol {
Expand Down Expand Up @@ -208,3 +210,109 @@ extension SegmentCommand64 {
)
}
}

extension SegmentCommand {
private func _section(
at offset: UInt,
segmentStart: UInt,
sections: any Sequence<Section>
) -> Section? {
sections.first(where: { section in
let sectionStart = UInt(section.layout.offset)
let size = UInt(section.layout.size)
if sectionStart <= segmentStart + offset &&
segmentStart + offset < sectionStart + size {
return true
} else {
return false
}
})
}

/// Section at the specified offset
/// - Parameters:
/// - offset: offset from start of segment
/// - cmdsStart: pointer at load commands start
/// - Returns: located section
public func section(
at offset: UInt,
cmdsStart: UnsafeRawPointer
) -> Section? {
let sections = sections(cmdsStart: cmdsStart)
return _section(
at: offset,
segmentStart: UInt(layout.vmaddr),
sections: sections
)
}

/// Section at the specified offset
/// - Parameters:
/// - offset: offset from start of segment
/// - machO: machO file
/// - Returns: located section
public func section(
at offset: UInt,
in machO: MachOFile
) -> Section? {
let sections = sections(in: machO)
return _section(
at: offset,
segmentStart: UInt(layout.fileoff),
sections: sections
)
}
}

extension SegmentCommand64 {
private func _section(
at offset: UInt,
segmentStart: UInt,
sections: any Sequence<Section64>
) -> Section64? {
sections.first(where: { section in
let sectionStart = UInt(section.layout.offset)
let size = UInt(section.layout.size)
if sectionStart <= segmentStart + offset &&
segmentStart + offset < sectionStart + size {
return true
} else {
return false
}
})
}

/// Section at the specified offset
/// - Parameters:
/// - offset: offset from start of segment
/// - cmdsStart: pointer at load commands start
/// - Returns: located section
public func section(
at offset: UInt,
cmdsStart: UnsafeRawPointer
) -> Section64? {
let sections = sections(cmdsStart: cmdsStart)
return _section(
at: offset,
segmentStart: UInt(layout.vmaddr),
sections: sections
)
}

/// Section at the specified offset
/// - Parameters:
/// - offset: offset from start of segment
/// - machO: machO file
/// - Returns: located section
public func section(
at offset: UInt,
in machO: MachOFile
) -> Section64? {
let sections = sections(in: machO)
return _section(
at: offset,
segmentStart: UInt(layout.fileoff),
sections: sections
)
}
}
156 changes: 151 additions & 5 deletions Sources/MachOKit/MachOFile+DyldChainedFixups.swift
Original file line number Diff line number Diff line change
Expand Up @@ -69,20 +69,22 @@ extension MachOFile.DyldChainedFixups: DyldChainedFixupsProtocol {
guard let basePtr = $0.baseAddress else { return [] }
let ptr = UnsafeRawPointer(basePtr)
.advanced(by: startsInImage.offset)
return offsets.map {
let layout = ptr.advanced(by: $0)
return offsets.enumerated().map { index, offset in
let layout = ptr.advanced(by: offset)
.assumingMemoryBound(to: DyldChainedStartsInSegment.Layout.self)
.pointee
let offset: Int = startsInImage.offset + $0
let offset: Int = startsInImage.offset + offset
let ret: DyldChainedStartsInSegment = .init(
layout: layout,
offset: offset
offset: offset,
segmentIndex: index
)
return isSwapped ? ret.swapped : ret
}
}
}

// xcrun dyld_info -fixup_chains "Path to Binary"
public func pages(
of startsInSegment: DyldChainedStartsInSegment?
) -> [DyldChainedPage] {
Expand All @@ -105,7 +107,7 @@ extension MachOFile.DyldChainedFixups: DyldChainedFixupsProtocol {
.map {
isSwapped ? $0.byteSwapped : $0
}
.map { .init(offset: $0) }
.enumerated().map { .init(offset: $1, index: $0) }
}
}

Expand Down Expand Up @@ -166,3 +168,147 @@ extension MachOFile.DyldChainedFixups: DyldChainedFixupsProtocol {
}
}
}

extension MachOFile.DyldChainedFixups {
// https://github.com/apple-oss-distributions/dyld/blob/d1a0f6869ece370913a3f749617e457f3b4cd7c4/common/MachOLoaded.cpp#L884
// xcrun dyld_info -fixup_chain_details "Path to Binary"
// xcrun dyld_info -fixups "Path to Binary"
public func pointers(
of startsInSegment: DyldChainedStartsInSegment,
in machO: MachOFile
) -> [DyldChainedFixupPointer] {
let pages = pages(of: startsInSegment)

let pagesData = machO.fileHandle.readData(
offset: numericCast(machO.headerStartOffset) + startsInSegment.segment_offset,
size: pages.count * numericCast(startsInSegment.page_size)
)

var pointers: [DyldChainedFixupPointer] = []

for (index, page) in pages.enumerated() {
var offsetInPage = page.offset

if page.isNone { continue }
if page.isMulti {
var overflowIndex = Int(offsetInPage & ~UInt16(DYLD_CHAINED_PTR_START_MULTI))
var chainEnd = false
while !chainEnd {
chainEnd = pages[overflowIndex].offset & UInt16(DYLD_CHAINED_PTR_START_LAST) != 0
offsetInPage = pages[overflowIndex].offset & ~UInt16(DYLD_CHAINED_PTR_START_LAST)
let pageContentStart: Int = index * numericCast(startsInSegment.page_size)
let chainOffset = pageContentStart + numericCast(offsetInPage)
walkChain(offset: chainOffset, data: pagesData, of: startsInSegment, in: machO, pointers: &pointers)
overflowIndex += 1
}
} else {
let pageContentStart: Int = index * numericCast(startsInSegment.page_size)
let chainOffset = pageContentStart + numericCast(offsetInPage)
walkChain(offset: chainOffset, data: pagesData, of: startsInSegment, in: machO, pointers: &pointers)
}
}

return pointers
}

private func walkChain(
offset: Int,
data: Data,
of startsInSegment: DyldChainedStartsInSegment,
in machO: MachOFile,
pointers: inout [DyldChainedFixupPointer]
) {
guard let pointerFormat = startsInSegment.pointerFormat else {
return
}
var offset = offset

let stride = pointerFormat.stride
var stop = false
var chainEnd = false

while !stop && !chainEnd {
data.withUnsafeBytes {
guard let baseAddress = $0.baseAddress else { return }
let ptr = baseAddress.advanced(by: offset)

var fixupInfo: DyldChainedFixupPointerInfo?

if pointerFormat.is64Bit {
let rawValue = ptr.load(as: UInt64.self)
switch pointerFormat {
case .arm64e, .arm64e_kernel, .arm64e_userland, .arm64e_firmware, .arm64e_userland24:
let content = DyldChainedFixupPointerInfo.ARM64E(rawValue: rawValue)
switch pointerFormat {
case .arm64e:
fixupInfo = .arm64e(content)
case .arm64e_kernel:
fixupInfo = .arm64e_kernel(content)
case .arm64e_userland:
fixupInfo = .arm64e_userland(content)
case .arm64e_firmware:
fixupInfo = .arm64e_firmware(content)
case .arm64e_userland24:
fixupInfo = .arm64e_userland24(content)
default: break
}

case ._64, ._64_offset:
let content = DyldChainedFixupPointerInfo.General64(rawValue: rawValue)
switch pointerFormat {
case ._64: fixupInfo = ._64(content)
case ._64_offset: fixupInfo = ._64_offset(content)
default: break
}

case ._64_kernel_cache, .x86_64_kernel_cache:
let content = DyldChainedFixupPointerInfo.General64Cache(rawValue: rawValue)
switch pointerFormat {
case ._64_kernel_cache:
fixupInfo = ._64_kernel_cache(content)
case .x86_64_kernel_cache:
fixupInfo = .x86_64_kernel_cache(content)
default: break
}

default:
// unknown format
stop = true
}


} else {
let rawValue = ptr.load(as: UInt32.self)
switch pointerFormat {
case ._32:
let content = DyldChainedFixupPointerInfo.General32(rawValue: rawValue)
fixupInfo = ._32(content)
case ._32_cache:
let content = DyldChainedFixupPointerInfo.General32Cache(rawValue: rawValue)
fixupInfo = ._32_cache(content)
case ._32_firmware:
let content = DyldChainedFixupPointerInfo.General32Firmware(rawValue: rawValue)
fixupInfo = ._32_firmware(content)
default:
// unknown format
stop = true
}
}

if let fixupInfo {
let pointerOffset = numericCast(startsInSegment.segment_offset) + offset

pointers.append(
DyldChainedFixupPointer(
offset: pointerOffset,
fixupInfo: fixupInfo
)
)

if fixupInfo.next == 0 { chainEnd = true }
else { offset += stride * fixupInfo.next }
}
}
}
}
}
Loading

0 comments on commit 0bde958

Please sign in to comment.