Swift

Developer Mode System Extensions on macOS

System and Network Extensions are fairly easy programmatically. However, there is some nuance around building them. Much of this is in getting the correct entitlements – but also a little in troubleshooting.

To see (or set) those entitlements, look at the .entitlements file located in the root of an Xcode Project. That will be a plist with a few entries. In this one, we’ll see com.apple.developer.networking.networkextension so we’re working on a network extension.

com.apple.security.app-sandbox com.apple.security.application-groups $(TeamIdentifierPrefix)com.krypted.firewall com.apple.developer.networking.networkextension content-filter-provider

To add one, go to the General screen for the project, and locate the section for Frameworks, Libraries, and Embedded Content.

Then use the plus sign to add and provide the name of the framework to add.

That allows us to use network extensions (so we’ll add or see import NetworkExtension in the beginning of a swift file that uses it, as well as an import SystemExtension when using those. That import will allow us to use classes Apple provides for tasks that expose functionality such as NEFilterManager (https://developer.apple.com/documentation/networkextension/nefiltermanager). I won’t get into everything one might do with these, but once we get to doing the things, we invariably need to troubleshoot. And given that they have specific needs, the things I’m about to cover shouldn’t be done in production and are really just there for troubleshooting new extensions.

First, we can’t do much with SIP on so this is one of the very few cases where we’re going to disable it on a development machine. To do that, we’ll boot into Recovery Mode (using Command-R to boot) and run the following command.

csrutil disable

Now, let’s boot the machine and set

systemextensionsctl developer on

If it’s an arm and it’s a driver extension, we might then need to run the following:

sudo nvram boot-args=-arm64e_preview_abi

Now that we’re booted and able to interface with system extensions directly, we can use lldb. To enter the lldb interactive mode, simply run lldb:

sudo lldb

Now from the interactive mode, we can attach to a process to see what’s happening when it’s run:

process attach --pid 8766

In the above we’re attaching to a specific pid and could have used --waitfor following it if we wanted to attach to the next instance. But if we wanted to attach to an app we’d just be running lldb followed by the .app bundle URI. For example, if we’re putting test apps into /Compiled and want to attach to an app called myextension.app:

lldb /Compiled/myextension.app

We can then see any breakpoints as well (from that lldb interactive mode):

breakpoint list

So let’s say we invoke an extension in line 87 of file blocker.c we could create a breakpoint there:

breakpoint set --file blocker.c --line 87

This sets the breakpoint for each methods that implement that line, or its class, as well. Once the breakpoint is hit, use the thread verb followed by continue to proceed:

thread continue

This brings up threads. From within lldb use the thread verb followed by list to see the state the process is in:

thread list

Or use thread with backtrace to see those:

thread backtrace

Also checkout waypoint and frame in lldb.