2

I’ve created a Swift macOS app which uses SMJobBless to create a helper with escalated privileges. This works fine—the helper gets installed to /Library/Privileged Helper Tools and an accompanying LaunchDaemon gets created in /Library/LaunchDaemons. However, the helper is unable to start successfully. Instead, it crashes with an “Illegal instruction: 4” message.

I’ve prepared the helper to respond to XML connections by implementing the NSXPCListenerDelegate protocol. Here‘s my Helper main.swift code:

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()

The crash occurs on the last line, listener.resume().

I tried to launch the helper app manually from the command line (which is identical to what the LaunchDaemon does) and, again, it crashes with the above error message printed to stdout. I don’t have any more ideas on how to test this for the root cause. My implementation is more than rudimentary, following Apple’s guidlines for implementing XM services. Also, the various posts on SO regarding XML services haven’t helped me in resolving this issue. Has anyone of you tried to create a privileged helper in Swift successfully? BTW, the app is not sandboxed.

For the sake of completeness, here’s the code for the HelperTool class referenced in my HelperDelegate class above:

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)
        }
    }
}

And finally the HelperToolProtocol:

import Foundation

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

Thanks for any help!

4

1 回答 1

2

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

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

正如问题中所说,这会使助手在最后一行立即崩溃。

我直接从Apple 的 Creating XPC Services 文档中获取了这些内容。这是该NSXPCListener resume()函数的文档:

如果在service()对象上调用,此方法永远不会返回。因此,在设置任何所需的初始状态并配置侦听器本身之后,您应该将其作为 XPC 服务主函数中的最后一步调用。

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

let listener = NSXPCListener(machServiceName: "Privilege-Escalation-Sample.Helper")
listener.delegate = delegate
listener.resume()
RunLoop.current.run()
于 2017-09-15T08:17:01.937 回答