Post

Replies

Boosts

Views

Activity

Reply to PKG Installer postinstall script not executing in Sonoma
After many tests I finally found a solution. Apparently the & at the end of the command is whats causing the issue in Sonoma. So I had to replace this command: /Applications/myapp.app/Contents/MacOS/myapp --load-system-extension & With this one: /usr/bin/open /Applications/myapp.app --args --load-system-extension And now it works perfectly! Im still curious about those better ways to activate an extension.
Topic: Code Signing SubTopic: General Tags:
May ’24
Reply to Crash when using String(cString:)
I found a solution similar to what Quinn suggested on Google's Project Santa's github. I just ported it to swift. Here it is: let deadline = DispatchTime(uptimeNanoseconds: cMsg.pointee.deadline) - .seconds(2) let handlerSemaphore = DispatchSemaphore(value: 0) handlerSemaphore.signal() let deadlineSemaphore = DispatchSemaphore(value: 0) queue.asyncAfter(deadline: deadline) { // Check if event handler has already responded if handlerSemaphore.wait(timeout: .now()) == .timedOut { // Event handler already responded so exit return } // Deadline met, event handler still processing, default to allow self.allowMessage(cMsg) deadlineSemaphore.signal() } queue.async { // Process auth event and respond handleAuthEvent(cMsg) // Check if deadline was met if handlerSemaphore.wait(timeout: .now()) == .timedOut { // Wait for deadline block to allow the message, then free deadlineSemaphore.wait() } self.freeMessage(cMsg) }
Topic: Programming Languages SubTopic: Swift Tags:
Aug ’22
Reply to Apps/files do not open when the result is cached with es_respond_flags_result
Hi, I am having the same issue that Uddalak described. But in my case this only happens when I attach Xcode's debugger to my system extension. As soon as I do it the entire system becomes unresponsive for 30 secs until my app is killed (no crash log). It doesn't matter if I use the 0x7fffffff or 0xffffffff flags or if I cache the response or not. In my event handler block I allow all events, like this: es_respond_flags_result(client, msg, 0x7fffffff, true)
Topic: Privacy & Security SubTopic: General Tags:
Aug ’22
Reply to Crash when using String(cString:)
As per Quinns request here is a more detailed version of my code static func handleProcessExec(_ msg: UnsafePointer<es_message_t>) {     let target = msg.pointee.event.exec.target.pointee guard msg.pointee.process.pointee.is_es_client == false else {      es_respond_auth_result(esClient.client!, msg, ES_AUTH_RESULT_ALLOW, true)       return     }     // copy message so it can be used on another thread     guard let copiedMessage = copyMessage(msg) else {       es_respond_auth_result(esClient.client!, msg, ES_AUTH_RESULT_DENY, false)       return     }     DispatchQueue.global().async { var process: ProcessDetails? = nil let deadline = DispatchTime(uptimeNanoseconds: copiedMessage.pointee.deadline - (2*NSEC_PER_SEC)) let semaphore = DispatchSemaphore(value: 0) // RuleResult contains what rule matched and if we should allow/deny var result = RuleResult() let timestamp = copiedMessage.pointee.time.tv_sec DispatchQueue.global().async { process = ProcessDetails(process: copiedMessage.pointee.event.exec.target.pointee) ESEventRulesRunner.runProcessExecRules(process: process!, &result) semaphore.signal()        }       _ = semaphore.wait(timeout: deadline)       es_respond_auth_result(esClient.client!, copiedMessage, result.verdict, false)       if result.verdict == ES_AUTH_RESULT_DENY {         // Log event to api let event = ESEvent(process: process!, timestamp: timestamp, ruleType: result.type)         Api.postEvent(event: event) }       freeMessage(copiedMessage)     }       }
Topic: Programming Languages SubTopic: Swift Tags:
Aug ’22
Reply to Crash when using String(cString:)
Ok I found out what is happening but I need help fixing it. Look at this code // Inside function that handles process exec messages     guard let copiedMessage = copyMessage(msg) else {       es_respond_auth_result(esClient.client!, msg, ES_AUTH_RESULT_ALLOW, false)        return     }     DispatchQueue.global().async { var process: ProcessDetails? = nil        let deadline = copiedMessage.pointee.deadline let semaphore = DispatchSemaphore(value: 0) DispatchQueue.global().async { // This may take some time          process = ProcessDetails(process: copiedMessage.pointee.event.exec.target.pointee) semaphore.signal()       }       _ = semaphore.wait(timeout: DispatchTime(uptimeNanoseconds: deadline - (2 * NSEC_PER_SEC)))       es_respond_auth_result(...)       freeMessage(copiedMessage)     } I am calling description inside my ProcessDetails class, the problem is that if the timeout of the semaphore runs out I will free the copiedMessage but the initialization of ProcessDetails is still going so that results in a crash. How could I kill that seconds thread before freeing the message? Or should I somehow set another semaphore and wait for that thread to finish (even though I already responded to the message) to free the message?
Topic: Programming Languages SubTopic: Swift Tags:
Aug ’22
Reply to XCode not marked as is_platform_binary
Quinn, as you said, for apps as big as XCode this can be very slow. I understand I can cache the result but if I am doing this in the context of an ES_EVENT_TYPE_AUTH_EXEC I would be blocking the first execution of Xcode for a long time before my analysis finishes. What would you do in my case?
Topic: Privacy & Security SubTopic: General Tags:
Nov ’21
Reply to How to handle System Extension willCompleteAfterReboot
Another question I had about the willCompleteAfterReboot is why is this called? I want to better understand this problem I am having about extensions needing a reboot to complete an upgrade. What is preventing the system from starting my extension without the need of a reboot? I am using a Network Extension, if I were to add Endpoint Security to my extension would something in the activation process change? I can't see antivirus software that uses this framework requiring a reboot for every update to their extension.
Jul ’21
Reply to Get all Domain names in macos ?
Would this work? Just accessing the NEAppProxyFlow and returning true without having to handle the flow // NEDNSProxyProvider override func handleNewFlow(_ flow: NEAppProxyFlow) -> Bool { NSLog("DNSProxyProvider: handleFlow") if let tcpFlow = flow as? NEAppProxyTCPFlow { let remoteHost = (tcpFlow.remoteEndpoint as! NWHostEndpoint).hostname let remotePort = (tcpFlow.remoteEndpoint as! NWHostEndpoint).port // Do whatever I want with this data } else if let udpFlow = flow as? NEAppProxyUDPFlow { let localHost = (udpFlow.localEndpoint as! NWHostEndpoint).hostname let localPort = (udpFlow.localEndpoint as! NWHostEndpoint).port // Do whatever I want with this data } return true }
Jun ’21
Reply to Process arguments from audit token
Ok so here is what I came up with. This works fine, the only thing I have yet to see is if it has any memory leaks. I hope this helps someone in the future :) If you have any suggestions to the func please let me know. Also, you should first convert the audit_token to pid using the function mentioned above. func getArgs(from pid: Int32) -> [NSString]? {   var arguments: [NSString] = []   var mib: [Int32] = [0, 0, 0]   var argsMax: Int = 0   mib[0] = CTL_KERN   mib[1] = KERN_ARGMAX   var size = MemoryLayout<Int>.stride(ofValue: argsMax)   if sysctl(&mib, 2, &argsMax, &size, nil, 0) == -1 {     return nil   }   let processArgs = UnsafeMutablePointer<CChar>.allocate(capacity: argsMax)   mib[0] = CTL_KERN   mib[1] = KERN_PROCARGS2   mib[2] = pid size = argsMax as size_t   // Get process arguments   if sysctl(&mib, 3, processArgs, &size, nil, 0) == -1 {     return nil   }   if size <= MemoryLayout<Int>.size {     return nil   }   var numberOfArgs: Int32 = 0   //Get number of args   memcpy(&numberOfArgs, processArgs, MemoryLayout.size(ofValue: numberOfArgs)) // Initialize the pointer to the start of args   var parser: UnsafeMutablePointer<CChar> = processArgs + MemoryLayout.size(ofValue: numberOfArgs) // Iterate until NULL terminated path   while parser < &processArgs[size] {     if 0x0 == parser.pointee {       // arrived ar argv[0]       break     }     parser += 1   }   // sanity check   if parser == &processArgs[size] {     return nil   }   while parser < &processArgs[size] {     if 0x0 != parser.pointee {       break     }     parser += 1   }   // sanity check   if parser == &processArgs[size] {     return nil   }   var argStart: UnsafeMutablePointer<CChar>? = parser   // Get all args   while parser < &processArgs[size] {     if parser.pointee == CChar(0) {       if nil != argStart {         let argument = NSString(utf8String: argStart!)         if argument != nil {           arguments.append(argument!)         }       }       argStart = parser + 1       if arguments.count == numberOfArgs {         break       }     }     parser += 1   }   // Is this free necessary?   free(processArgs)   return arguments }
Topic: Programming Languages SubTopic: Swift Tags:
Jun ’21
Reply to NEFilterSocketFlow remoteHostname property
Yes, I can get the remoteEndpoint populated guard let socketFlow = flow as? NEFilterSocketFlow,     let remoteEndpoint = socketFlow.remoteEndpoint as? NWHostEndpoint,     let localEndpoint = socketFlow.localEndpoint as? NWHostEndpoint else {      return nil }       if #available(macOS 11.0, *) { if let hostname = socketFlow.remoteHostname {      // Access hostname     } }
May ’21
Reply to PKG Installer postinstall script not executing in Sonoma
After many tests I finally found a solution. Apparently the & at the end of the command is whats causing the issue in Sonoma. So I had to replace this command: /Applications/myapp.app/Contents/MacOS/myapp --load-system-extension & With this one: /usr/bin/open /Applications/myapp.app --args --load-system-extension And now it works perfectly! Im still curious about those better ways to activate an extension.
Topic: Code Signing SubTopic: General Tags:
Replies
Boosts
Views
Activity
May ’24
Reply to Crash when using String(cString:)
I found a solution similar to what Quinn suggested on Google's Project Santa's github. I just ported it to swift. Here it is: let deadline = DispatchTime(uptimeNanoseconds: cMsg.pointee.deadline) - .seconds(2) let handlerSemaphore = DispatchSemaphore(value: 0) handlerSemaphore.signal() let deadlineSemaphore = DispatchSemaphore(value: 0) queue.asyncAfter(deadline: deadline) { // Check if event handler has already responded if handlerSemaphore.wait(timeout: .now()) == .timedOut { // Event handler already responded so exit return } // Deadline met, event handler still processing, default to allow self.allowMessage(cMsg) deadlineSemaphore.signal() } queue.async { // Process auth event and respond handleAuthEvent(cMsg) // Check if deadline was met if handlerSemaphore.wait(timeout: .now()) == .timedOut { // Wait for deadline block to allow the message, then free deadlineSemaphore.wait() } self.freeMessage(cMsg) }
Topic: Programming Languages SubTopic: Swift Tags:
Replies
Boosts
Views
Activity
Aug ’22
Reply to Apps/files do not open when the result is cached with es_respond_flags_result
Hi, I am having the same issue that Uddalak described. But in my case this only happens when I attach Xcode's debugger to my system extension. As soon as I do it the entire system becomes unresponsive for 30 secs until my app is killed (no crash log). It doesn't matter if I use the 0x7fffffff or 0xffffffff flags or if I cache the response or not. In my event handler block I allow all events, like this: es_respond_flags_result(client, msg, 0x7fffffff, true)
Topic: Privacy & Security SubTopic: General Tags:
Replies
Boosts
Views
Activity
Aug ’22
Reply to Crash when using String(cString:)
As per Quinns request here is a more detailed version of my code static func handleProcessExec(_ msg: UnsafePointer<es_message_t>) {     let target = msg.pointee.event.exec.target.pointee guard msg.pointee.process.pointee.is_es_client == false else {      es_respond_auth_result(esClient.client!, msg, ES_AUTH_RESULT_ALLOW, true)       return     }     // copy message so it can be used on another thread     guard let copiedMessage = copyMessage(msg) else {       es_respond_auth_result(esClient.client!, msg, ES_AUTH_RESULT_DENY, false)       return     }     DispatchQueue.global().async { var process: ProcessDetails? = nil let deadline = DispatchTime(uptimeNanoseconds: copiedMessage.pointee.deadline - (2*NSEC_PER_SEC)) let semaphore = DispatchSemaphore(value: 0) // RuleResult contains what rule matched and if we should allow/deny var result = RuleResult() let timestamp = copiedMessage.pointee.time.tv_sec DispatchQueue.global().async { process = ProcessDetails(process: copiedMessage.pointee.event.exec.target.pointee) ESEventRulesRunner.runProcessExecRules(process: process!, &result) semaphore.signal()        }       _ = semaphore.wait(timeout: deadline)       es_respond_auth_result(esClient.client!, copiedMessage, result.verdict, false)       if result.verdict == ES_AUTH_RESULT_DENY {         // Log event to api let event = ESEvent(process: process!, timestamp: timestamp, ruleType: result.type)         Api.postEvent(event: event) }       freeMessage(copiedMessage)     }       }
Topic: Programming Languages SubTopic: Swift Tags:
Replies
Boosts
Views
Activity
Aug ’22
Reply to Crash when using String(cString:)
Ok I found out what is happening but I need help fixing it. Look at this code // Inside function that handles process exec messages     guard let copiedMessage = copyMessage(msg) else {       es_respond_auth_result(esClient.client!, msg, ES_AUTH_RESULT_ALLOW, false)        return     }     DispatchQueue.global().async { var process: ProcessDetails? = nil        let deadline = copiedMessage.pointee.deadline let semaphore = DispatchSemaphore(value: 0) DispatchQueue.global().async { // This may take some time          process = ProcessDetails(process: copiedMessage.pointee.event.exec.target.pointee) semaphore.signal()       }       _ = semaphore.wait(timeout: DispatchTime(uptimeNanoseconds: deadline - (2 * NSEC_PER_SEC)))       es_respond_auth_result(...)       freeMessage(copiedMessage)     } I am calling description inside my ProcessDetails class, the problem is that if the timeout of the semaphore runs out I will free the copiedMessage but the initialization of ProcessDetails is still going so that results in a crash. How could I kill that seconds thread before freeing the message? Or should I somehow set another semaphore and wait for that thread to finish (even though I already responded to the message) to free the message?
Topic: Programming Languages SubTopic: Swift Tags:
Replies
Boosts
Views
Activity
Aug ’22
Reply to Playgrounds get stuck "Running..."
12-17-2021 Xcode Version 13.2 (13C90) MBP 13-inch,i7, 2017, macOS 11.6.1 I honestly can't believe how after all this years the bug is still there. It is extremely anoying to have to restart Xcode everytime I want to test something in playgrounds
Replies
Boosts
Views
Activity
Dec ’21
Reply to XCode not marked as is_platform_binary
Quinn, as you said, for apps as big as XCode this can be very slow. I understand I can cache the result but if I am doing this in the context of an ES_EVENT_TYPE_AUTH_EXEC I would be blocking the first execution of Xcode for a long time before my analysis finishes. What would you do in my case?
Topic: Privacy & Security SubTopic: General Tags:
Replies
Boosts
Views
Activity
Nov ’21
Reply to XPC Connection error
Turns out I was setting the exportedInterface instead of the remoteObjectInterface on the NSXPCConnection.
Topic: Programming Languages SubTopic: Swift Tags:
Replies
Boosts
Views
Activity
Jul ’21
Reply to How to handle System Extension willCompleteAfterReboot
Another question I had about the willCompleteAfterReboot is why is this called? I want to better understand this problem I am having about extensions needing a reboot to complete an upgrade. What is preventing the system from starting my extension without the need of a reboot? I am using a Network Extension, if I were to add Endpoint Security to my extension would something in the activation process change? I can't see antivirus software that uses this framework requiring a reboot for every update to their extension.
Replies
Boosts
Views
Activity
Jul ’21
Reply to How to handle System Extension willCompleteAfterReboot
Would manually deactivating my System Extension before installing the new version help? I was thinking about doing that in the preinstall script of my pkg installer by executing the container app with a uninstall parameter like this: /Applications/app/Contents/MacOS/app --unload-extension
Replies
Boosts
Views
Activity
Jun ’21
Reply to Get all Domain names in macos ?
Would this work? Just accessing the NEAppProxyFlow and returning true without having to handle the flow // NEDNSProxyProvider override func handleNewFlow(_ flow: NEAppProxyFlow) -> Bool { NSLog("DNSProxyProvider: handleFlow") if let tcpFlow = flow as? NEAppProxyTCPFlow { let remoteHost = (tcpFlow.remoteEndpoint as! NWHostEndpoint).hostname let remotePort = (tcpFlow.remoteEndpoint as! NWHostEndpoint).port // Do whatever I want with this data } else if let udpFlow = flow as? NEAppProxyUDPFlow { let localHost = (udpFlow.localEndpoint as! NWHostEndpoint).hostname let localPort = (udpFlow.localEndpoint as! NWHostEndpoint).port // Do whatever I want with this data } return true }
Replies
Boosts
Views
Activity
Jun ’21
Reply to Process arguments from audit token
Ok so here is what I came up with. This works fine, the only thing I have yet to see is if it has any memory leaks. I hope this helps someone in the future :) If you have any suggestions to the func please let me know. Also, you should first convert the audit_token to pid using the function mentioned above. func getArgs(from pid: Int32) -> [NSString]? {   var arguments: [NSString] = []   var mib: [Int32] = [0, 0, 0]   var argsMax: Int = 0   mib[0] = CTL_KERN   mib[1] = KERN_ARGMAX   var size = MemoryLayout<Int>.stride(ofValue: argsMax)   if sysctl(&mib, 2, &argsMax, &size, nil, 0) == -1 {     return nil   }   let processArgs = UnsafeMutablePointer<CChar>.allocate(capacity: argsMax)   mib[0] = CTL_KERN   mib[1] = KERN_PROCARGS2   mib[2] = pid size = argsMax as size_t   // Get process arguments   if sysctl(&mib, 3, processArgs, &size, nil, 0) == -1 {     return nil   }   if size <= MemoryLayout<Int>.size {     return nil   }   var numberOfArgs: Int32 = 0   //Get number of args   memcpy(&numberOfArgs, processArgs, MemoryLayout.size(ofValue: numberOfArgs)) // Initialize the pointer to the start of args   var parser: UnsafeMutablePointer<CChar> = processArgs + MemoryLayout.size(ofValue: numberOfArgs) // Iterate until NULL terminated path   while parser < &processArgs[size] {     if 0x0 == parser.pointee {       // arrived ar argv[0]       break     }     parser += 1   }   // sanity check   if parser == &processArgs[size] {     return nil   }   while parser < &processArgs[size] {     if 0x0 != parser.pointee {       break     }     parser += 1   }   // sanity check   if parser == &processArgs[size] {     return nil   }   var argStart: UnsafeMutablePointer<CChar>? = parser   // Get all args   while parser < &processArgs[size] {     if parser.pointee == CChar(0) {       if nil != argStart {         let argument = NSString(utf8String: argStart!)         if argument != nil {           arguments.append(argument!)         }       }       argStart = parser + 1       if arguments.count == numberOfArgs {         break       }     }     parser += 1   }   // Is this free necessary?   free(processArgs)   return arguments }
Topic: Programming Languages SubTopic: Swift Tags:
Replies
Boosts
Views
Activity
Jun ’21
Reply to NEFilterSocketFlow remoteHostname property
Yes, I can get the remoteEndpoint populated guard let socketFlow = flow as? NEFilterSocketFlow,     let remoteEndpoint = socketFlow.remoteEndpoint as? NWHostEndpoint,     let localEndpoint = socketFlow.localEndpoint as? NWHostEndpoint else {      return nil }       if #available(macOS 11.0, *) { if let hostname = socketFlow.remoteHostname {      // Access hostname     } }
Replies
Boosts
Views
Activity
May ’21
Reply to Intercept request headers with AppProxy ?
Thanks for the help! Lastly, do you guys have a sample project using NETransparentProxyProvider because I could not find a single repo on github using this api.
Replies
Boosts
Views
Activity
Apr ’21
Reply to Intercept request headers with AppProxy ?
create your outbound copier, and then open the flow with your inbound copier. So no matter the direction of the flow (inbound/outbound) I need to use both inboundCopier and outboundCopier correct?
Replies
Boosts
Views
Activity
Apr ’21