macOS Daemonology

Volodymyr Alkenso Vashurkin
8 min readDec 21, 2019

--

What do you see and what is the truth?

Note: this articles uses as example macOS as a system with open access to the file system, processes, etc. At the same time, all other devices of the Apple family (running by iOS, watchOS, …) has same internals.

How do we see the operating system from the ordinary user?

Like a simple set of windowed (UI) applications with colorful designs and attractive animations.

Moreover, it is obvious that for the work of all of these applications and the entire system, there must be something behind?
Daemons inside!

Daemons?

In everyday speech you can hear the term “demon,” which is incorrect, but used spread.

Remember: Daemon != Demon.

According to historical information, daemon is a kind of creature designed by the gods to perform various kinds of work that the gods themselves do not want to do.

This terminology was first used in the so-called Maxwell’s Daemon.

Who are all these daemons and what they’re doing?

  • OS support
  • monitoring, misc servers (smb, http, ftp etc.), task managers, security checks
  • running in the background
  • (usually) are deprived of any GUI

Daemon itself in the *OS operating system family is represented by a config (.plist) with a description of the daemon + executable file.

Configuration file .plist describes and defines the behavior of the daemon.

  • Label: daemon identifier.
    - com.example.mydaemond
  • ProgramArguments: command line arguments used to start the daemon
    - /bin/rm, -rf, “/tmp/trashfiles”
    - /Library/Application Support /CoolApp /com.coolapp.monitord

There is also a fairly large number of other arguments, such as

  • KeepAlive
  • RunAtLoad
  • WorkingDirectory
  • RootDirectory
  • StartInterval
  • StartCalendarInterval
  • WatchPaths

Note: you can read more about these and other configuration file attributes in the launchd: man launchd.plist manual.

You can also look at the official Apple article: Daemons and Services Programming Guide

launchd — the very first daemon

In *OS systems there is a “special” daemon named launchd

launchd is an analogue of the “init” UNIX-like systems.
launchd always has pid 1 (pid 0 is a Kernel itself)

The main tasks of launchd are:

  • initialize the system
  • start / restart processes
  • daemons support
  • XPC support

An interesting fact is that launchd has a personal Twitter: @launchderp.

*OS Daemonology in detail

Apple suggests classifying all daemons into the following categories:

XPC Service

Perhaps the XPC Service is the most recommended and documented type of daemons in macOS.

Apple recommends using the XPC Service in applications. They can even be integrated into Frameworks.

Key facts about the XPC Service

  • AppStore-friendly
  • Special target in Xcode
  • Runs as current user
  • GUI-less
  • Stateless (may be killed by launchd on IDLE)
  • Relates to concrete application
  • Lives no longer than parent application
  • May be installed within sandboxed app summary,

XPC Service allows you to take part of the logic code into a separate process, while providing out of the box a convenient mechanism for interaction between processes (XPC).

XPC Service is a fairly limited process, strongly associated with its parent.

For example, if two different applications integrate the same XPC Service, then for each application a separate and independent instance of the service will be launched.

Important note about XPC Service:

  • cannot be root
  • cannot use any GUI features (windows, notifications, …)
  • can be non-sandboxed even if main app is (not AppStore case, of’course)

UserAgent

A fairly popular kind of daemon is UserAgent.

UserAgent is a standalone background process launched from the current user.

Usually there is 1 UserAgent process for each specific user of the system (for each login session).

Key facts about UserAgent

  • Independent application
  • Runs as current user
  • May have state
  • May show GUI (windows, notifications, …)

UserAgent is convenient to use for putting all the application business logic, state and functionality into it, leaving only the UI in the main application.

Using UserAgent, you can also coordinate the work of various application component processes.

Note: You can install UserAgent in the system only from NOT sandboxed application.

Classic daemon

Daemon as a daemon (in the classic naming convention) is a system-global process run as root.

Daemon exists alone for the entire system, and can also start at the time of loading the OS and before the user login.

Daemons are usually used in cases where one or more features are needed:

  • performing privileged operations (as root)
  • storing a global state regardless of login sessions
  • coordinating UserAgents and apps
  • installations of KEXT

Key facts about Daemon

  • Independent application
  • Runs as root
  • May have state
  • May start on OS boot
  • Singleton
  • GUI-less

Note: Apple does NOT recommend the use of daemons, offering to replace them with UserAgent or, in extreme cases, with PrivilegedHelper.

PrivilegedHelper

PrivilegedHelper can be called a “legal daemon”.

Apple says that if you really can’t go without root, then use PrivilegedHelper instead of the usual scripts-as-root.

PrivilegedHelper looks like a usual root Daemon, but there are some specifics:

  • must be installed using API function SMJobBless
  • when installed, it is copied by the system to /Library/PrivilegedHelperTools

Why copying daemon into “/Library/PrivilegedHelperTools” may be a problem?
Because that means that any dynamic linking with own libraries and frameworks will break off, because the process does not know where to look for them.

There are several ways to resolve the issue:

  1. Static libraries
  2. Runtime link

Key facts about PrivilegedHelper

Note: PrivilegedHelper can NOT be installed from Sandboxed applications.

LoginItem

LoginItem is an AppStore-compatible (and ‘restricted’) version of UserAgent.

Generally it is a regular UserAgent, which is installed using the legal API provided by Apple.

Key facts about LoginItem

  • ‘Restricted’ User Agent
  • Installed via API function SMLoginItemSetEnabled
  • May be installed within sandboxed app
  • Automatically starts when user logs in
  • Usage guide

Note: This is NOT the LoginItem that can be seen in the Settings.

Where daemons live?

As mentioned earlier, daemon is a pair of config file and the executable program.

Placing executable file itself doesn’t matter at all because this is just the path in the Program / ProgramArguments section of config.

Configuration files are hosted by

╔════════════════════════════╦══════════════════════════════════╗
║ Path ║ Example ║
╠════════════════════════════╬══════════════════════════════════╣
║ Daemons: ║ /Library/LaunchDaemons ║
║ /Library/LaunchDaemons ║ /System/Library/LaunchDaemons ║
╠════════════════════════════╬══════════════════════════════════╣
║ Global (all-users) Agents: ║ /Library/LaunchAgents ║
║ /Library/LaunchAgents ║ /System/Library/LaunchAgents ║
╠════════════════════════════╬══════════════════════════════════╣
║ User-specific Agents: ║ /Users/JohnDoe/LaunchAgents ║
║ ~/Library/LaunchAgents ║ /Users/JaneDoe/LaunchAgents ║
║ ║ ... ║
║ ║ ~/LaunchAgents ║
╚════════════════════════════╩══════════════════════════════════╝

The difference between global UserAgent and user-specific is that the agents from the “global” will be associated with all users of the OS.

At the same time, ‘user-specific’ agents are associated only with a specific user.

Note: root privileges are required to put an agent in the “global”.

Daemon benefits

We have reviewed main types of daemons in the *OS family.

Frequently asked question is “Why do I need XPC Service if I can just start another thread?”.

There are the following advantages of using daemons

EXC_BAD_ACCESS, “crash-resistance”

Of course, we all write perfect code. Of course, it never crashes and does not upset our users.

Let’s make an assumption that somewhere we missed a defect leading to the crash of the application.

In the case of the “classic” monolithic approach to the application, the user loses everything at once in case of a crash.

But taking out parts of the code into individual services the worst thing is the loss of only one feature (and showing a non-fatal error).

Of course, this approach complicates the architecture of your application. But what is a little complexity over huge benefits like crash-safety?

Security

Placing your code in daemons greatly complicates the life of attackers and reverse engineers who for some reason want to figure out how your code works.

The code that is in the framework is extremely easy to use and learn through dynamic analysis (in simple words — via debug).

The code that is integrated into the daemon is much harder to use and exploit. At least until system protection has not fallen and attacker does not have root privileges.

Limited usage of root

Moving the code requiring root into a separate daemon leads to the principle “Use right features/rights only when they are really needed”.

This principle also follows the ideology of Apple, which says that: “Use Kernel modules only when you cannot do it via the privileged process. Use privileged processes only for those operations that really require elevated privileges.”

By isolating the minimum functionality into root daemons, we protect not only our applications, but also the entire system and user data for case if a small hole will be found in our daemon.

Personal daemon

We’ve looked at many different kinds of daemons.

Now it’s time to look at a simple diagram that allows to select a daemon specifically for your needs.

Choosing between XPC Service, UserAgent and LoginItem I suggest to be guided by the following considerations and practical experience.

XPC Service

  • AppStore-friendly
  • No GUI
  • Stateless
  • Always bundle
  • May be embedded into frameworks

LoginItem

  • AppStore-friendly
  • May have GUI
  • Always bundle
  • Native installation API
  • SMLoginItemSetEnabled
  • Pain in the ass when upgrade / reinstall
  • Non-obvious way to communicate
  • Non-documented launch / relaunch behavior

UserAgent

  • May have GUI
  • Bundle or executable
  • Flexible launchd.plist
  • Installation routines via ‘launchctl’ tool
  • Installation requires non-sandboxed app

Control your daemons — launchctl

XPC Services, LoginItems, PrivilegedHelpers will (should) work out of the box.

But to get the full power of UserAgent or Daemon we have to work with system tools.

macOS provides the launchctl

  • get a list of all registered daemons
  • load / unload the daemon
  • enable / disable the daemon

Examples

List agents for current user: launchctl list

Load agent: launchctl bootstrap gui / 501 /Library/LaunchAgents/com.company.launchagent.plist

Unload agent: launchctl bootout gui / 501 /Library/LaunchAgents/com.company.launchagent.plist

where <user’s UID> is user id (uid_t)

List daemons: sudo launchctl list

Load daemon: sudo launchctl bootstrap system /Library/LaunchDaemons/com.company. launchdaemon.plist

Unload daemon: sudo launchctl bootout system /Library/LaunchDaemons/com.company.launchdaemon.plist

More complete documentation on launchctl can be found at:

It’s just the beginning it’s not the end ©

Initially, I planned to write just this article: about the types of daemons in *OS + description of XPC as an interprocess communication mechanism and the second about low-level XPC implementation.

However, as usual, there was more things to talk about than expected.
Therefore, I wrote the whole book dedicated to macOS Daemons and XPC.
The title is ‘macOS Daemonology’ https://link.springer.com/book/10.1007/978-1-4842-7277-0

Subscribe and follow interesting articles and code sample:

--

--