Permalink
Cannot retrieve contributors at this time
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Fugu14/arm/shared/KernelExploit/Sources/KernelExploit/MemoryAccess.swift
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
353 lines (294 sloc)
14 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // | |
| // MemoryAccess.swift | |
| // KernelExploit | |
| // | |
| // Created by Linus Henze. | |
| // Copyright © 2021 Linus Henze. All rights reserved. | |
| // | |
| import Foundation | |
| import JailbreakUtils | |
| import Darwin | |
| import CoreFoundation | |
| import libkernrw | |
| public enum MemoryAccessError: Error { | |
| case failedToInitialize | |
| case attemptedToReadMoreThanAPage | |
| case attemptedToWriteMoreThanAPage | |
| case failedToReadKernelData | |
| case failedToWriteKernelData | |
| case failedToWriteIntoOurBuffer | |
| case failedToReadIntoDMABuffer | |
| case failedToTranslate(address: UInt64, table: String, entry: UInt64) | |
| case failedToCopyFromRemote | |
| case failedToCopyToRemote | |
| case failedToFindSomeProc | |
| } | |
| public class MemoryAccess { | |
| public static var kernelFromDisk:Data! | |
| private static var setupDone = false | |
| //private static var uc: PWNUC? | |
| //private let uc: PWNUC | |
| //public private(set) var kernelPhysBase: UInt64! | |
| public private(set) var kernelVirtBase: UInt64! | |
| public private(set) var unslidVirtBase: UInt64 = 0xFFFFFFF007004000 | |
| public var kernelSlide: UInt64 { | |
| return kernelVirtBase - unslidVirtBase | |
| } | |
| public private(set) var offsets: Offsets.KernelOffsetsEntry! | |
| public private(set) var thisProc: UInt64 = 0 | |
| public private(set) var kernelProc: UInt64 = 0 | |
| public private(set) var launchdProc: UInt64 = 0 | |
| var ttbr1: UInt64! | |
| public static func setup() throws { | |
| print("about to request kernRw") | |
| guard requestKernRw() == 0 else { | |
| throw MemoryAccessError.failedToInitialize | |
| } | |
| print("requested kernRw") | |
| setupDone = true | |
| /* | |
| guard let checkin = getDKCheckinData() else { | |
| throw MemoryAccessError.failedToInitialize | |
| } | |
| defer { mach_port_destroy(mach_task_self_, checkin.token) } | |
| uc = try dkLaunchExploit(token: checkin.token, tag: checkin.tag) | |
| setupDone = true | |
| */ | |
| } | |
| public static func setupUsingRawPorts(dkSvPort: mach_port_t, ucPort: mach_port_t, physMemDesc: mach_port_t, dmaPort: mach_port_t, dmaDesc: mach_port_t, mapAddr: UInt, remoteTask: mach_port_t) { | |
| // Will be unable to get any class due to ioPort being zero | |
| //gDK = DriverKit(svPort: dkSvPort, ioPort: 0) | |
| //let dma = PWNDMA(port: dmaPort, dmaMemDesc: dmaDesc, mapAddr: mapAddr, otherTask: remoteTask) | |
| //uc = PWNUC(port: ucPort, dmaCmd: dma, memDesc: physMemDesc) | |
| setupDone = true | |
| } | |
| public init(noLog: Bool = false) throws { | |
| let physMemBase: UInt64 = 0x800000000 | |
| if !Self.setupDone { | |
| try Self.setup() | |
| } | |
| //uc = Self.uc.unsafelyUnwrapped | |
| // Setup stuff | |
| var cur: UInt64 = 0 | |
| while cur < physMemBase + 0x2000000 { | |
| if true { | |
| // kernelPhysBase = physMemBase + cur | |
| kernelVirtBase = 0 // try r64(phys: kernelPhysBase + 0x38) | |
| guard kernRW_getKernelBase(&kernelVirtBase) == 0 else { | |
| fatalError("kernRW_getKernelBase can't find kernel") | |
| } | |
| print("kernel virt base is \(String(format: "%lx", kernelVirtBase))") | |
| // Code below should actually be in offsets.swift... | |
| // Read kernel MachO header | |
| // zhuowei: hack: without physical mapping this is way too slow (~2min), just read a precopied macho | |
| // let kernel = try readBytes(virt: kernelVirtBase, count: 0x4000) | |
| let kernelFromDisk = try Data(contentsOf: URL(fileURLWithPath: "/usr/local/zhuowei/kernelcache_iphone12ios141.macho")) | |
| MemoryAccess.kernelFromDisk = kernelFromDisk | |
| var kernel = Data(count: kernelFromDisk.count + 0x100000) | |
| kernel[0..<kernelFromDisk.count] = kernelFromDisk | |
| // Slide the kernel ourselves | |
| // Read as MachO | |
| let kernelMachO = try MachO(fromData: kernel) | |
| let slide = kernelVirtBase - unslidVirtBase | |
| rebaseKernelFromDisk(kernelFromDisk: &kernel, kernelMachO: kernelMachO, slide: slide) | |
| // Iterate over all the segments to find __TEXT_EXEC,__text and __DATA,__data | |
| var textExec: (Data, UInt64)! | |
| var dataSect: (Data, UInt64)! | |
| var cStrSect: (Data, UInt64)! | |
| var cnstSect: (Data, UInt64)! | |
| var pplText: (Data, UInt64)! | |
| var start: UInt64! | |
| for cmd in kernelMachO.cmds { | |
| print("cmd \(cmd)") | |
| if let seg = cmd as? Segment64LoadCommand { | |
| print("cmd \(seg.name)") | |
| if seg.name == "__TEXT_EXEC" { | |
| for sect in seg.sections { | |
| if sect.section == "__text" { | |
| // textExec = (try readBytes(virt: kernelVirtBase + UInt64(sect.offset), count: sect.size), sect.address) | |
| textExec = (kernel.subdata(in: Int(sect.offset)..<Int(sect.offset) + Int(sect.size)), sect.address + slide) | |
| } | |
| } | |
| } else if seg.name == "__DATA" { | |
| for sect in seg.sections { | |
| if sect.section == "__data" { | |
| // dataSect = (try readBytes(virt: kernelVirtBase + UInt64(sect.offset), count: sect.size), sect.address) | |
| dataSect = (kernel.subdata(in: Int(sect.offset)..<Int(sect.offset) + Int(sect.size)), sect.address + slide) | |
| } | |
| } | |
| } else if seg.name == "__TEXT" { | |
| for sect in seg.sections { | |
| print("sect \(sect.section)") | |
| if sect.section == "__cstring" { | |
| // cStrSect = (try readBytes(virt: kernelVirtBase + UInt64(sect.offset), count: sect.size), sect.address) | |
| cStrSect = (kernel.subdata(in: Int(sect.offset)..<Int(sect.offset) + Int(sect.size)), sect.address + slide) | |
| } | |
| } | |
| } else if seg.name == "__DATA_CONST" { | |
| for sect in seg.sections { | |
| if sect.section == "__const" { | |
| // cnstSect = (try readBytes(virt: kernelVirtBase + UInt64(sect.offset), count: sect.size), sect.address) | |
| cnstSect = (kernel.subdata(in: Int(sect.offset)..<Int(sect.offset) + Int(sect.size)), sect.address + slide) | |
| } | |
| } | |
| } else if seg.name == "__PPLTEXT" { | |
| for sect in seg.sections { | |
| if sect.section == "__text" { | |
| // pplText = (try readBytes(virt: kernelVirtBase + UInt64(sect.offset), count: sect.size), sect.address) | |
| pplText = (kernel.subdata(in: Int(sect.offset)..<Int(sect.offset) + Int(sect.size)), sect.address + slide) | |
| } | |
| } | |
| } | |
| } else if let c = cmd as? OpaqueLoadCommand { | |
| if c.type == .Unknown(0x5) { | |
| start = c.data.getGeneric(type: UInt64.self, offset: 0x108) + slide | |
| } | |
| } | |
| } | |
| // Why throw exceptions when you can just call fatalError??? | |
| guard textExec != nil else { | |
| fatalError("Failed to find __TEXT_EXEC,__text!") | |
| } | |
| guard dataSect != nil else { | |
| fatalError("Failed to find __DATA,__data!") | |
| } | |
| guard cStrSect != nil else { | |
| fatalError("Failed to find __TEXT,__cstring!") | |
| } | |
| guard cnstSect != nil else { | |
| fatalError("Failed to find __DATA_CONST,__const!") | |
| } | |
| guard pplText != nil else { | |
| fatalError("Failed to find __PPLTEXT,__text!") | |
| } | |
| guard start != nil else { | |
| fatalError("Failed to find start function!") | |
| } | |
| offsets = try Offsets.findOffsetsForThisDevice(textExec: textExec, dataSect: dataSect, cStrSect: cStrSect, cnstSect: cnstSect, pplText: pplText, start: start, slide: slide, noLog: noLog) | |
| // ttbr1 = try r64(virt: offsets.pagetable + slide) | |
| // Find some procs | |
| let myPID = getpid() | |
| var curProc = try r64(virt: offsets.allProcAddr + kernelSlide) | |
| while curProc != 0 { | |
| let pid = try r32(virt: curProc + offsets.procStruct.pidOffset) | |
| if pid == myPID { | |
| thisProc = curProc | |
| } else if pid == 0 { | |
| kernelProc = curProc | |
| } else if pid == 1 { | |
| launchdProc = curProc | |
| } | |
| if thisProc != 0 && kernelProc != 0 && launchdProc != 0 { | |
| break | |
| } | |
| curProc = try r64(virt: curProc + offsets.procStruct.nextOffset) | |
| } | |
| guard thisProc != 0 && kernelProc != 0 && launchdProc != 0 else { | |
| throw MemoryAccessError.failedToFindSomeProc | |
| } | |
| offsets = try Offsets.findThreadNextOffset(mem: self, thisProc: thisProc, noLog: noLog) | |
| return | |
| } | |
| cur += 0x4000 | |
| } | |
| fatalError("Couldn't find kernel!") | |
| } | |
| private func isKernel(address: UInt64) -> Bool { | |
| /* | |
| guard (try? r32(phys: address)) == 0xFEEDFACF else { | |
| return false | |
| } | |
| guard (try? r32(phys: address+0x4)) == 0x100000C else { | |
| return false | |
| } | |
| guard (try? r32(phys: address+0x8)) == 0xC0000002 else { | |
| return false | |
| } | |
| return true | |
| */ | |
| return false | |
| } | |
| /* | |
| public func unsafeMapAddress(phys: UInt64, size: UInt64) throws -> UInt64 { | |
| let desc = try uc.getPhysMemDesc(forAddress: phys, size: size) | |
| return try mapDescriptor(desc) | |
| } | |
| */ | |
| /** | |
| * Read some bytes (virtual address). | |
| */ | |
| public func readBytes(virt: UInt64, count: UInt64) throws -> Data { | |
| // print("reading data: \(String(format: "%lx %lx", virt, count))") | |
| var data = Data(count: Int(count)) | |
| try data.withUnsafeMutableBytes { dataBytes in | |
| guard kernRW_readbuf(virt, dataBytes.baseAddress, dataBytes.count) == 0 else { | |
| throw MemoryAccessError.failedToReadKernelData | |
| } | |
| } | |
| return data | |
| } | |
| /** | |
| * Write some bytes (virtual address). | |
| */ | |
| public func writeBytes(virt: UInt64, data: Data) throws { | |
| try data.withUnsafeBytes { dataBytes in | |
| guard kernRW_writebuf(virt, dataBytes.baseAddress, dataBytes.count) == 0 else { | |
| throw MemoryAccessError.failedToWriteKernelData | |
| } | |
| } | |
| } | |
| public func r64(virt: UInt64) throws -> UInt64 { | |
| return try readBytes(virt: virt, count: 8).getGeneric(type: UInt64.self) | |
| } | |
| public func r32(virt: UInt64) throws -> UInt32 { | |
| return try readBytes(virt: virt, count: 4).getGeneric(type: UInt32.self) | |
| } | |
| public func r16(virt: UInt64) throws -> UInt16 { | |
| return try readBytes(virt: virt, count: 2).getGeneric(type: UInt16.self) | |
| } | |
| public func r8(virt: UInt64) throws -> UInt8 { | |
| return try readBytes(virt: virt, count: 1).getGeneric(type: UInt8.self) | |
| } | |
| public func w64(virt: UInt64, data: UInt64) throws { | |
| let dat = Data(fromObject: data) | |
| try writeBytes(virt: virt, data: dat) | |
| } | |
| public func w32(virt: UInt64, data: UInt32) throws { | |
| let dat = Data(fromObject: data) | |
| try writeBytes(virt: virt, data: dat) | |
| } | |
| public func w16(virt: UInt64, data: UInt16) throws { | |
| let dat = Data(fromObject: data) | |
| try writeBytes(virt: virt, data: dat) | |
| } | |
| public func w8(virt: UInt64, data: UInt8) throws { | |
| let dat = Data(fromObject: data) | |
| try writeBytes(virt: virt, data: dat) | |
| } | |
| public func rStr(virt: UInt64) throws -> String { | |
| var ctr: UInt64 = 0 | |
| var res = "" | |
| while true { | |
| let b = try r8(virt: virt + ctr) | |
| if b == 0 { | |
| return res | |
| } | |
| res += String(format: "%c", b) | |
| ctr += 1 | |
| } | |
| } | |
| public func stripPAC(fromPtr ptr: UInt64) -> UInt64 { | |
| if ((ptr >> 55) & 1) == 1 { | |
| // Kernel pointer | |
| return ptr | 0xFFFFFF8000000000 | |
| } | |
| return ptr // Do nothing, for now | |
| } | |
| public func rPtr(virt: UInt64) throws -> UInt64 { | |
| return stripPAC(fromPtr: try r64(virt: virt)) | |
| } | |
| public func getKrwData() -> (dkSvPort: mach_port_t, ucPort: mach_port_t, physMemDesc: mach_port_t, dmaPort: mach_port_t, dmaDesc: mach_port_t, mapAddr: UInt) { | |
| //return (dkSvPort: gDK.serverPort, ucPort: Self.uc!.uextPort, physMemDesc: Self.uc!.entireMemDesc as! mach_port_t, dmaPort: Self.uc!.dmaCmd.uextPort, dmaDesc: Self.uc!.dmaCmd.memDesc as! mach_port_t, mapAddr: Self.uc!.dmaCmd.mapAddr) | |
| return (dkSvPort: 0, ucPort: 0, physMemDesc: 0, dmaPort: 0, dmaDesc: 0, mapAddr: 0) | |
| } | |
| } |