Linus Henze 开源 Fugu15越狱 代码

by on under jekyll
4 minute read

Linus Henze开源 Fugu15越狱代码

我的博客  https://xlsn0w.github.io/

10月初,Linus Henze 展示了他在运行 iOS15.4.1的 iPhone 上的 Fugu15越狱,他用一种巧妙的新安装方法让我们想起了 JailbreakMe 的日子。

现在,它已经正式发布,并在 Henze 的 GitHub 页面上开源。

https://github.com/pinauten/Fugu15

现在 TrollStore 正式支持 Fugu15越狱应用程序,这意味着用户可以使用 TrollStore 在他们兼容的设备上加载和永久签署 Fugu15越狱应用程序。

以前,用户需要用自己的域名安装一个基于 web 的安装程序,或者将他们的设备连接到 Mac 上,用 Xcode 安装 Fugu15。

公众号Cydiapps 已经发文详细讲解Fugu15

源代码

//
//  Fugu15.swift
//  Fugu15KernelExploit
//
//  Created by Linus Henze.
//

import Foundation
import ProcessCommunication
import CBindings
import KernelPatchfinder

public enum Fugu15LaunchError: Error {
    case deviceNotSupported(reason: String)
    case posix_spawnFailed(result: Int32, errno: Int32)
    case noPongReceived
    case kexploitdFailed
    case canOnlyLaunchiDownloadAfterExploitRan
    case iDownloadLaunchFailed(reply: [String])
}

public enum Fugu15SupportsThisDevice {
    case yes
    case no(reason: String)
}

public struct Fugu15 {
    public static var comm: ProcessCommunication?
    public static var patchfinder = KernelPatchfinder.running
    
    static var kernelBase: UInt64 = 0
    static var kernelSlide: UInt64 = 0
    
    // Lock for requests to pciPwn
    static let requestLock     = NSLock()

    // Lock to notify exception handler to continue
    static let sendRequestLock = NSLock()

    // Lock to notify requestor to continue
    static let replyLock       = NSLock()

    // Request
    static var request:        UInt64 = 0
    static var requestAddrPid: UInt64 = 0
    static var requestSize:    UInt64 = 0
    static var requestBuf:     Data?

    // Reply
    static var replyStatus: UInt64 = 0
    static var replyResult: UInt64 = 0
    static var replyBuf:    Data?
    
    public static func supportsThisDevice() -> Fugu15SupportsThisDevice {
        // Test iOS version
        let osVersion = ProcessInfo.processInfo.operatingSystemVersion
        guard osVersion.majorVersion == 15,
              osVersion.minorVersion < 5 else {
            return .no(reason: "Fugu15 only supports iOS 15 - 15.4.1!")
        }
        
        // The exploits should support some 15.5 betas
        // Not implemented though
        /*if osVersion.minorVersion == 5 {
            var size = 1024
            let ptr  = UnsafeMutablePointer<UInt8>.allocate(capacity: size + 1)
            defer { ptr.deallocate() }
            
            let res = sysctlbyname("kern.osversion", ptr, &size, nil, 0)
            guard res == 0 else {
                return .no(reason: "Fugu15 failed to determine your OS version!")
            }
            
            ptr[size] = 0 // Ensure that the string is terminated
            
            let vStr = String(cString: ptr)
            
            let supported = ["19A5261w", "19A5281h", "19A5281j", "19A5297e"] // No idea if this is correct
            guard supported.contains(vStr) else {
                return .no(reason: "Fugu15 only supports iOS 15 - 15.4.1 (and some 15.5 betas)!")
            }
        }*/
        
        // Ensure device supports pointer authentication
        guard deviceSupports(cpuFeature: "PAuth") else {
            return .no(reason: "Fugu15 only supports PAC devices (iPhone XS and newer)!")
        }
        
        return .yes
    }
    
    public static func querySysctlBool(name: String) -> Bool {
        var size = 8
        let ptr  = UnsafeMutablePointer<UInt8>.allocate(capacity: size)
        defer { ptr.deallocate() }
        
        let res = sysctlbyname(name, ptr, &size, nil, 0)
        guard res == 0 else {
            return false
        }
        
        return ptr[0] != 0
    }
    
    public static func deviceSupports(cpuFeature: String) -> Bool {
        return querySysctlBool(name: "hw.optional.arm.FEAT_\(cpuFeature)")
    }
    
    /**
     * Launch kernel exploit. Requires path to oobPCI.
     *
     * - Parameter oobPCI: Path to the oobPCI executable
     * - Parameter logger: A function to log messages
     *
     * - Warning: This function blocks, do not call it on the main dispatch queue
     */
    public static func launchKernelExploit(oobPCI: URL, logger: @escaping (_ msg: String) -> Void) throws {
        switch supportsThisDevice() {
        case .yes:
            break
            
        case .no(reason: let reason):
            throw Fugu15LaunchError.deviceNotSupported(reason: reason)
        }
        
        // Create pipes to use for communication
        // We use control and log pipes
        let controlToChild = Pipe()
        let controlFromChild = Pipe()
        let logFromChild = Pipe()
        
        // We're entitled to do that ;)
        var attr: posix_spawnattr_t?
        posix_spawnattr_init(&attr)
        posix_spawnattr_set_persona_np(&attr, 99, 1)
        posix_spawnattr_set_persona_uid_np(&attr, 0)
        posix_spawnattr_set_persona_gid_np(&attr, 0)
        
        // Close unnecessary handles
        var fileActions: posix_spawn_file_actions_t?
        posix_spawn_file_actions_init(&fileActions)
        posix_spawn_file_actions_addclose(&fileActions, controlToChild.fileHandleForWriting.fileDescriptor)
        posix_spawn_file_actions_addclose(&fileActions, controlFromChild.fileHandleForReading.fileDescriptor)
        posix_spawn_file_actions_addclose(&fileActions, logFromChild.fileHandleForReading.fileDescriptor)
        
        var pid: pid_t = 0
        var argv: [UnsafeMutablePointer<CChar>?] = [
            strdup(CommandLine.arguments[0]),
            strdup("Fugu15_server"),
            strdup("\(controlToChild.fileHandleForReading.fileDescriptor)"),
            strdup("\(controlFromChild.fileHandleForWriting.fileDescriptor)"),
            strdup("\(logFromChild.fileHandleForWriting.fileDescriptor)"),
            nil
        ]
        
        defer {
            for arg in argv {
                free(arg)
            }
        }
        
        let result = posix_spawn(&pid, argv[0], &fileActions, &attr, &argv, environ)
        let err = errno
        guard result == 0 else {
            throw Fugu15LaunchError.posix_spawnFailed(result: result, errno: err)
        }
        
        try? controlToChild.fileHandleForReading.close()
        try? controlFromChild.fileHandleForWriting.close()
        try? logFromChild.fileHandleForWriting.close()
        
        DispatchQueue(label: "Fugu15-Logging").async {
            var buf = ""
            while true {
                do {
                    let data = try logFromChild.fileHandleForReading.read(upToCount: 1)
                    if data == nil || data?.count == 0 {
                        return
                    }
                    
                    if data.unsafelyUnwrapped[0] == 0xA /* newline */ {
                        logger(buf)
                        buf = ""
                    } else {
                        buf += String(data: data.unsafelyUnwrapped, encoding: .utf8) ?? ""
                    }
                } catch _ {
                    return
                }
            }
        }
        
        // Send ping
        let comm = ProcessCommunication(read: controlFromChild.fileHandleForReading, write: controlToChild.fileHandleForWriting)
        comm.sendCommand("ping")
        
        guard comm.receiveCommand() == ["pong"] else {
            throw Fugu15LaunchError.noPongReceived
        }
        
        comm.sendCommand("pwn", oobPCI.path)
        
        if comm.receiveCommand() != ["ok"] {
            throw Fugu15LaunchError.kexploitdFailed
        }
        
        comm.sendCommand("waitUntilDone")
        if comm.receiveCommand() != ["done"] {
            throw Fugu15LaunchError.kexploitdFailed
        }
        
        Self.comm = comm
    }
    
    public static func launch_iDownload() throws {
        guard let comm = Self.comm else {
            throw Fugu15LaunchError.canOnlyLaunchiDownloadAfterExploitRan
        }
        
        comm.sendCommand("launch_iDownload")
        let reply = comm.receiveCommand()
        if reply != ["done"] {
            throw Fugu15LaunchError.iDownloadLaunchFailed(reply: reply ?? [])
        }
    }
    
    /**
     * Call this method from your main function. Only returns if invoked without a Fugu15 command.
     */
    public static func mainHook() {
        if CommandLine.arguments.count > 1 {
            switch CommandLine.arguments[1] {
            case "Fugu15_server":
                let logOut = FileHandle(fileDescriptor: Int32(CommandLine.arguments[4])!, closeOnDealloc: true)
                Logger.logFileHandle = logOut
                
                guard let checkin = getDKCheckinData() else {
                    execv(Bundle.main.executablePath, CommandLine.unsafeArgv)
                    Logger.print("Failed to re-exec myself after failing DK checkin!")
                    fatalError("Failed to re-exec myself after failing DK checkin!")
                }
                
                serverMain(checkin: checkin)
            
            default:
                break
            }
        }
    }
}

XLsn0w, Blog