Proc Info is a open-source, user-mode, library for macOS. It provides simple interface to retrieve detailed information about running processes, plus allows one to asynchronously monitor process creation & exit events.
Love this library or want to support it? Check out my patreon page :)
To use the Proc Info library:
- add the Proc Info library (
libprocInfo.a
) and Apple's OpenBSM library (libbsm.tbd
) to your Xcode Project - import the Proc Info library header file (
procInfo.h
) - instantiate a
Proc Info
object - a) to retrieve information about a running process invoke the
init:
method
b) to enumerate existing processes invoke thecurrentProcesses
method
c) to monitor process events, declare a callback block and invoke thestart:
method
...or just download the demo project, to take it for a spin!
#import "procInfo.h"
//init proc info object
ProcInfo* procInfo = [[ProcInfo alloc] init];
//dump process info for process 1337
NSLog(@"process: %@", [[Process alloc] init:1337]);
//dump process info for all processes
for(Process* process in [procInfo currentProcesses])
NSLog(@"new process: %@", process);
//block for process events
ProcessCallbackBlock block = ^(Process* process)
{
if(process.type != EVENT_EXIT)
NSLog(@"process start: %@\n", process);
else
NSLog(@"process exit: %d\n", process.pid);
};
//start monitoring
// ->block will be invoke upon process events!
[processInfo start:block];
The Proc Info library provides an interface to:
- retrieve information about arbitrary processes (by pid)
- retrieve information about all running processes
- monitor for process start & exit events
The library is already used in various Objective-See's tools that:
- need to track process creation events (e.g. RansomWhere? BlockBlock, etc)
- classify running processes (based on their cryptographic signatures)
Moreover, it is an important component of tools designed to facilitate Mac malware analysis (e.g. OSX/FruitFly), and vulnerability hunting (e.g. Installers/Updaters).
As detailed in the 'Quick Start' section, to use Proc Info in your Xcode project perform the following steps.
1. Add the Proc Info library to your Xcode project:
To add the Proc Info library to your Xcode project simply drag and drop the library (libprocInfo.a
), into the File Navigator. In the resulting 'file add' popup, make sure the 'Copy items if needed' option is selected:
2. Add Apple's OpenBSM library to your Xcode project:
Select your project in Xcode, then click on the 'Build Phases' tab. In the 'Link Binary With Libraries' section, click the '+' to bring up the prompt for adding frameworks and libraries. Type bsm
in the search box. Then select libbsm.tbd
and click 'Add':
3. Add the 'Proc Info' library header file to your project:
In Xcode click, 'File' then 'Add Files to ' Browse to the location of the procInfo.h
header file, and click 'Add.' Note, you can also add the procInfo.h
file into your project simply by dragging and dropping it into Xcode.
Your Xcode project should now look something like this:
Hit Product->Build to compile and link your project. Assuming it cleanly builds, now it's time to start writing code to unlock the power of the Proc Info library :)
Before getting into coding specifics, its important to understand the Process
and Binary
objects. A Process
object represents a instance of a running process. Looking at the header file procInfo.h
one can see it contains information about the process such as:
- pid
- ppid
- user
- ancestors
- arguments
Each Process
object also has an associated Binary
object that represents the on-disk image of the main executable which backs the process. The 'Binary' object contains information such as:
- name
- full path
- file attributes
- signing information
The signing information includes:
- signing status
- signing authorities
- whether its an Apple binary
- from the app store
With this information, one can use the library to answer questions such as:
- "What are all running processes on my system that don't belong to Apple proper?"
- "What are the arguments of the process that was just spawned?"
- "Is that process from the Mac App Store?"
- ...and many more!
Retrieving Information about an Arbitrary Process:
Via the Proc Info library one can create Process
objects for arbitrary processes. Simply invoke the Process
init:
method with the process identifier (pid_t
) of a running process:
//init process obj
Process* process = [[Process alloc] init:1337];
This will create a Process
object for the specified process, that will contain the aforementioned process characteristics (ancestors, arguments, Binary
object, etc). You can directly access this (e.g. process.binary.isApple
). Here, we simply dump it to stdout
via NSLog()
:
//dump process info
NSLog(@"process: %@", process);
//output
process:
pid: 1337
path: /Applications/Calculator.app/Contents/MacOS/Calculator
user: 501
args: (
"/Applications/Calculator.app/Contents/MacOS/Calculator"
)
ancestors: (
557,
554,
353,
1
)
binary:
name: Calculator
path: /Applications/Calculator.app/Contents/MacOS/Calculator
attributes: {
NSFileCreationDate = "2017-03-23 00:27:11 +0000";
NSFileExtensionHidden = 0;
NSFileGroupOwnerAccountID = 0;
NSFileGroupOwnerAccountName = wheel;
NSFileHFSCreatorCode = 0;
NSFileHFSTypeCode = 0;
NSFileModificationDate = "2017-03-23 00:27:11 +0000";
NSFileOwnerAccountID = 0;
NSFileOwnerAccountName = root;
NSFilePosixPermissions = 493;
NSFileReferenceCount = 1;
NSFileSize = 199520;
NSFileSystemFileNumber = 92435925;
NSFileSystemNumber = 16777220;
NSFileType = NSFileTypeRegular;
}
signing info: {
signatureStatus = 0;
signedByApple = 1;
signingAuthorities = (
"Software Signing",
"Apple Code Signing Certification Authority",
"Apple Root CA"
);
} (isApple: 1 / isAppStore: 0)
Retrieving Information about all Running Processes:
The Proc Info library can also provide information about all running processes via the ProcInfo
object's currentProcesses
method. This method returns an array of Process
objects; one for each running process:
//enum all existing procs
for(Process* process in [processInfo currentProcesses])
{
//query/examine each process...
//dump process info
NSLog(@"process: %@", process);
}
Note that this method may take a few seconds to execute, as generating and verifying the cryptographic signing information for all processes is somewhat time/CPU consuming. As such, it is recommended that you invoke this logic (i.e. the currentProcesses
method) on a background thread.
Monitoring Process Start and Exit Events:
One of the most powerful features of the Proc Info library is its ability to asynchronously monitor for process events such as creation (exec
, spawn
, fork
) and exit. When running with root privileges (recommended!) it uses Apple BSM auditing events to such process events. (For background on BSM auditing see "OpenBSM auditd on OS X: these are the logs you are looking for".
In order to begin monitoring for such events first declare a block to pass to the library. This block's type should be ProcessCallbackBlock
which has been typedef'd in the procInfo.h
header file as:
typedef void (^ProcessCallbackBlock)(Process*);
From this typedef, one can see this block will invoked with a pointer to a Process
object, which is (as expected) represents the process which triggered the event. After creating a callback block, simply invoke the Proc Info start:
method, passing in the block. This will kickoff asynchronous process monitoring. Now, anytime a process creation or exit event occurs, the code in the block will be automatically invoked!
One can be query returned Process
object for the event type (EVENT_FORK
, EVENT_EXIT
, etc). If the event was not an exit event, the Process
object will be fully instantiated, thus containing information such as ancestors, arguments, Binary
object, etc. Note that for EVENT_EXIT
events, the Process
object will only contain the process identifier (pid
) and the processes exit code.
Below is some example code that will call into the Proc Info library to asynchronously monitor for process events. Once the library detects such events, it will automatically invoke the passed in block
which here, just examines the event type and then dumps information about the process to stdout
:
//define block
// ->automatically invoked upon process events
ProcessCallbackBlock block = ^(Process* process)
{
//process start event
// ->fork, spawn, exec, etc.
if(process.type != EVENT_EXIT)
{
//print
NSLog(@"process start: %@\n", process);
}
//process exit event
else
{
//print
// ->only pid
NSLog(@"process exit: %d\n", process.pid);
}
};
//start monitoring
// ->pass in block for events
[processInfo start:block];
//run loop
// ->as don't want to exit
[[NSRunLoop currentRunLoop] run];
Executing this code, and starting a process such as Calculator.app
results in the following output:
# ./procInfoExample:
process start:
pid: 1337
path: /Applications/Calculator.app/Contents/MacOS/Calculator
user: 501
args: (
"/Applications/Calculator.app/Contents/MacOS/Calculator"
)
ancestors: (
557,
554,
353,
1
)
binary: name: Calculator
path: /Applications/Calculator.app/Contents/MacOS/Calculator
attributes: {
NSFileCreationDate = "2017-03-23 00:27:11 +0000";
NSFileExtensionHidden = 0;
NSFileGroupOwnerAccountID = 0;
NSFileGroupOwnerAccountName = wheel;
NSFileHFSCreatorCode = 0;
NSFileHFSTypeCode = 0;
NSFileModificationDate = "2017-03-23 00:27:11 +0000";
NSFileOwnerAccountID = 0;
NSFileOwnerAccountName = root;
NSFilePosixPermissions = 493;
NSFileReferenceCount = 1;
NSFileSize = 199520;
NSFileSystemFileNumber = 92435925;
NSFileSystemNumber = 16777220;
NSFileType = NSFileTypeRegular;
}
signing info: {
signatureStatus = 0;
signedByApple = 1;
signingAuthorities = (
"Software Signing",
"Apple Code Signing Certification Authority",
"Apple Root CA"
);
} (isApple: 1 / isAppStore: 0)
2017-08-07 08:49:02.199 procInfoExample[10393:3296896] process exit: 1337
It should be noted that if the Proc Info library is not running with root privileges, or is executed on an older version of macOS (pre 10.12.4) it will only monitor for application events (i.e. not terminal nor background processes). This is because in order to safely monitor for audit events, root and recent version of macOS is required.
Mahalo: This library is supported by the following patrons!
- Lance Gaines
- Khalil Sehnaoui
- Nando Mendonca
- Bill Smartt
- Martin OConnell
- David Sulpy
- Shain Singh
- Chad Collins
- Harry Hoffman
- Keelian Wardle
- Christopher Giffard
- Conrad Rushing
- soreq
- Stuart Ashenbrenner
- trifero
- Peter Sinclair
- Ming
Want to add your support? Check out my patreon page :)