Swift 特权助手(XPC 侦听器)因非法指令错误而崩溃

Swift Privileged Helper (XPC Listener) Crashing with Illegal Instruction Error

我创建了一个 Swift macOS 应用程序,它使用 SMJobBless 创建一个具有升级权限的助手。这工作正常——帮助程序安装到 /Library/Privileged Helper Tools 并在 /Library/LaunchDaemons 中创建一个伴随的 LaunchDaemon。但是,助手无法成功启动。相反,它会崩溃并显示“非法指令:4”消息。

我已经准备好助手通过实施 NSXPCListenerDelegate 协议来响应 XML 连接。这是我的助手 main.swift 代码:

import Foundation

class HelperDelegate: NSObject, NSXPCListenerDelegate {    
    func listener(_ listener: NSXPCListener, shouldAcceptNewConnection newConnection: NSXPCConnection) -> Bool {        
    newConnection.exportedInterface = NSXPCInterface(with: HelperToolProtocol.self)
        newConnection.exportedObject = HelperTool()
        newConnection.resume()
        return true
    }    
}

let delegate = HelperDelegate()
let listener = NSXPCListener.service()
listener.delegate = delegate
listener.resume()

崩溃发生在最后一行,listener.resume()

我尝试从命令行手动启动助手应用程序(这与 LaunchDaemon 所做的相同),但它再次崩溃,并将上述错误消息打印到标准输出。关于如何测试这个问题的根本原因,我没有更多的想法。根据 Apple’s guidlines for implementing XM services,我的实现不仅仅是简陋的。此外,SO 上关于 XML 服务的各种帖子并没有帮助我解决这个问题。你们中有人尝试过在 Swift 中成功创建特权助手吗?顺便说一句,该应用程序未被沙盒化。

为了完整起见,这里是我上面 HelperDelegate class 中引用的 HelperTool class 的代码:

import Foundation

class HelperTool: NSObject, HelperToolProtocol {
    func getVersion(withReply reply: (NSData?) -> ()) {
        let version = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString" as String) as? String ?? "<unknown version>"
        let build = Bundle.main.object(forInfoDictionaryKey: kCFBundleVersionKey as String) as? String ?? "<unknown build>"
        if let d = "v\(version) (\(build))".data(using: .utf8, allowLossyConversion: false) {
            reply(d as NSData)
        }
    }
}

最后 HelperToolProtocol:

import Foundation

@objc(HelperToolProtocol) protocol HelperToolProtocol {
    func getVersion(withReply: (NSData?) -> ())
}

感谢您的帮助!

经过几天的测试,我终于找到了一个解决方案,可以让我的 XPC 助手正确启动并响应任何消息。问题出在当前读取

main.swift 模块的最后三行
let listener = NSXPCListener.service()
listener.delegate = delegate
listener.resume()

正如问题中所提出的那样,这会使助手在最后一行立即崩溃。

我直接从 Apple’s Creating XPC Services documentation 中摘录了这些台词。这是 NSXPCListener resume() 函数的文档:

If called on the service() object, this method never returns. Therefore, you should call it as the last step inside the XPC service's main function after setting up any desired initial state and configuring the listener itself.

解决方案是 不调用 NSXPCListener.service() 单例对象 而是使用 init(machServiceName:) 初始化程序实例化一个新的 NSXPCListener 对象与主应用程序的 XPC 连接上使用的相同 Mach 服务名称。由于在这种情况下 resume() 会立即恢复——从而终止助手——你必须将它放在当前的 运行 循环中才能使其 运行 不确定。这是新的工作代码:

let listener = NSXPCListener(machServiceName: "Privilege-Escalation-Sample.Helper")
listener.delegate = delegate
listener.resume()
RunLoop.current.run()