2. Focused Research (1)Creating a mac_apt Plugin (Part 1)

2.1 What is mac_apt?

Many plugins are implemented for mac_apt, allowing

Digital forensics is so well provided for on Windows thatanalysis of many key artifacts. But mac_apt is developed

free and open source tools alone are sufficient for analyzing

almost solely by its creator, Yogesh Khatri, who cannot bemost artifacts. Yet in the case of macOS—which, like

expected to provide support for all artifacts.

Windows, is widely used as a desktop OS—relatively few
commercial products, not to mention free and open source

If an artifact is unsupported, it would perhaps be common

tools, are available.

to submit an Issue on the mac_apt development repository
and wait for someone to volunteer to implement a plugin.

This probably reflects the relative OS market shares and

But if you have some understanding of the artifact’s dataneeds within the digital forensics market. But the last few

structure, you might also consider implementing the plugin

years have seen the release of open source forensic analysis

yourself, because as mentioned earlier, mac_apt wastools for macOS that implement just enough features to be

developed as a forensic analysis framework.

practically useful. In my case, I have been following a tool
called mac_apt*1 closely.

This has been a somewhat lengthy preamble, but I will now
go over the basics of creating plugins for the mac_apt

This tool was developed as a macOS forensic analysis

forensic analysis framework for macOS. There is no official

framework and can analyze a range of artifacts using overdocumentation on creating plugins, so what follows is based

40 plugins. It also implements its own APFS and HFS+ fileon the source code of plugins already implemented andsystem parsers, allowing direct analysis without the disk

what I have learned from creating plugins. While mac_apt isimage mounted. So to compare it with tools designed to

written in Python, I will not be explaining Python terminology

perform analysis on disk images mounted in the OS, mac_and the like here.

apt obviates the need to install file system drivers, and it
allows the analyst to perform analysis regardless of what

2.2 Important File Formats for macOS Forensics

OS they are running on the analysis machine. And in

Before we get into creating plugins, let’s look at file formats

addition to de facto standard disk image formats like RAW

that are often parsed in macOS forensics. macOS and its

and E01, it also supports relatively niche formats like AFF4

applications use property lists (plists) and SQLite to store

and SPARSEIMAGE. These are used by commercial forensic

settings and history data. So naturally, artifact files often

tools as disk image formats.

come in one of these formats (since forensic analysis often
involves analysis of settings and history).

Conversion tools can be used to convert disk images to any
number of formats, so analysis tools do not necessarily haveplist files are mainly used to store simple data like OS andto support a whole bunch of formats. But because the disk

application settings and history. They play a role like thatimages of computers these days often exceed severalof the Windows registry, but as they are created for eachhundred GB in size, converting disk images takes a lot ofapplication, they are found in various places on the filetime and disk space. So the ability to analyze disk images

system. Early plist files used an XML format, but a binary

without converting them is a plus for the analyst.

format is now the default. On the command line, plutil can*120macOS (& ios) Artifact Parsing Tool (https://github.com/ydkhatri/mac_apt).

© Internet Initiative Japan Inc.

May.2022

Vol.

54

2. Focused Research (1)be used to examine the contents of a plist file. Figure 1

in a range of applications including Chrome, and recently

shows an example of using plutil to display com.apple.

even in Windows some artifacts are saved in SQLite format.

dock.plist, which stores the settings for applications on

DB Browser for SQLite*2 is a convenient way to view thethe macOS Dock.

data. Figure 2 shows an example of using DB Browser
to read com.apple.LaunchServices.QuarantineEventsV2,

SQLite, like plist, is used to record settings and history,

which stores information related to the quarantining of

but it is also used to store slightly larger pieces of data

files downloaded via a Web browser.

such as blobs of sent and received data. It is also used

Figure 1: plist Example (com.apple.dock.plist)

Figure 2: SQLite Example (com.apple.LaunchServices.QuarantineEventsV2)*2

DB Browser for SQLite (https://sqlitebrowser.org/).

© Internet Initiative Japan Inc.

21

2.3 mac_apt Plugin Structure

2.3.2 Properties

2.3.1 Demo Plugin

Plugin properties are set near the beginning of the plugin

A demo plugin is provided in the mac_apt plugins folder(immediately after module imports) (Figure 3). Table 1

in a file called _demo_plugin.py. This plugin reads the

explains each of these properties.

file “/System/Library/CoreServices/SystemVersion.plist”,
gets the value in ProductVersion, displays it on screen,

Plugin authors can basically set these as they like, but

and saves the analysis results to a file.

__Plugin_Name needs to be unique as it is used to identify theplugin. __Plugin_Modes specifies what OS types (MACOS

It only performs a simple analysis, but it is just right foror IOS) the plugin supports. Note that the keyword

understanding how plugins are structured, so let’s use it“ARTIFACTONLY” can also appear in this property if you

as an example to see how plugins work in general.

want to support the analysis of exported artifact files.

Figure 3: Plugin Property Settings

Property name

Meaning

ExampleNotes__Plugin_Name

Plugin name

DEMOPLUGIN1

Must be all caps, cannot include spaces__Plugin_Friendly_Name

Friendly name of plugin

Demo Plugin 1

Not used within the program__Plugin_Version

Version

1.0

Not used within the program__Plugin_Description

Plugin description

Arbitrary string__Plugin_Author

AuthorJohn Smith

Not used within the program__Plugin_Author_EmailAuthor’s email

author@example.com

Not used within the program__Plugin_Modes

OSs supported

MACOS,IOS,ARTIFACTONLY__Plugin_ArtifactOnly_Usage

Usage info for mac_apt_artifact_only.py

Arbitrary string

Table 1: Meaning of Plugin Properties

22© Internet Initiative Japan Inc.

May.2022

Vol.

54

2. Focused Research (1)2.3.3 Entry Points■ Plugin_Start()

All plugins first call the Plugin_Start(), Plugin_Start_

Plugin_Start() (Figure 4) takes a mac_info object as anStandalone(), or Plugin_Start_Ios() function. Table 2 lists

argument. This object contains basic macOS information

the plugin entry points for different mac_apt commands.

(OS version, user list, etc.) obtained from the disk image
to be analyzed along with basic methods for accessing

The demo plugin implements two entry points, Plugin_

the files on the disk image.

Start() and Plugin_Start_Standalone(). And this is consistent
with the content of the __Plugin_Modes property (Plugin_

The demo plugin displays the name of the OS on which

Start_Ios() contains only a pass instruction, and IOS does

mac_apt is running and the macOS version for which the

not appear in __Plugin_Modes). Next, let’s look at what

analysis is being performed (lines 40–41). The function

happens at each entry point.

then sets the artifact file path, pulls the version number

mac_apt command
mac_apt.py
mac_apt_mounted_sys_data.py

Plugin entry point
Plugin_Start()

mac_apt_artifact_only.py

Plugin_Start_Standalone()

ios_apt.py

Plugin_Start_Ios()

Table 2: mac_apt Commands and Entry Point Called

Figure 4: The Plugin_Start() Function© Internet Initiative Japan Inc.

23

out of the artifact file using the Process_File() function,

could be undetermined. For example, if the artifact file isand displays this on screen (lines 44–46). It then exports

located within the user home directory tree, the file path

the artifact file from the disk image and saves it in a folder

will contain a user name and is thus not a fixed string.

with the same name as the plugin (line 49). Finally, it

In such cases, you need to use the methods provided byuses the WriteMe() function to save the analysis results

mac_apt to get a list of directories and files on the disk(line 52).

image and dynamically build artifact file paths.

So at the entry point, once the artifact file path has been

■ Plugin_Start_Standalone()set, the main tasks are to call a function that performs

Plugin_Start_Standalone() (Figure 5) takes as its first

analysis and a function that saves the analysis results

argument a list object of artifact files specified on the(see below for details of Process_File(), WriteMe()). Other

mac_apt_artifact_only.py command line, so this can beplugins used to perform actual analysis also follow thisiterated over to process the artifact files one after thesame procedure.

other (line 96).

The path of the artifact file analyzed by the demo plugin is

However, if the artifact is made up of multiple files, or if

a fixed string, but depending on the artifact, the file paththe settings result in the artifact file path(s) being

Figure 5: The Plugin_Start_Standalone() Function

24© Internet Initiative Japan Inc.

May.2022

Vol.

54

2. Focused Research (1)undetermined, you will again need to dynamically build

2.3.4 The Demo Plugin’s Other Functions

the artifact file paths. Since the artifact files are on the■ Process_File()

file system of the OS running mac_apt, you can use

A function that parses SystemVersion.plist (Figure 6).

Python’s standard os module to get the file list and so on.

Parses the artifact file passed in as the second argument
using the mac_info object’s ReadPlist() method (line 60).

In the demo plugin, if the file path ends with “SystemVersion.

If successful, calls the GetMacOsVersion() function,

plist”, the file is parsed by the ReadPlist() method in thedescribed below, to get the version number and returns

CommonFunctions module provided by mac_apt (lineit (lines 61–65).

100). If the file is successfully parsed, then after obtaining
the OS version, the function saves the analysis results

The mac_info object’s ReadPlist() method, like that in theusing the WriteMe() function (lines 101–106). As you canCommonFunction module, returns the result of parsing

see, Process_File() is not called here, but the procedure is

the plist as a dictionary object.

mostly the same as in Plugin_Start().
■ GetMacOsVersion()
Note that the result of parsing a plist file using the ReadPlist()

Gets the OS version from parsed plist data and returns it

method (second return value) is a dictionary object.

(Figure 7).

Figure 6: The Process_File() Function

Figure 7: The GetMacOsVersion() Function© Internet Initiative Japan Inc.

25

■ WriteMe()

The DataWriter object is used to save the analysis results

A function that saves the analysis results to a file (Figure 8).

in the location and file format specified on the mac_apt

col_info defines the columns used when writing thecommand line (line 82). The first argument holds information

analysis results (line 77). The definition is a list of tuple

such as the save folder, the second holds the table nameobjects. The first element of each tuple is the column(when saving in SQLite format), the third is the columns

name and the second is the column’s type. Common typedefinition, and the fourth is the artifact file path. But asvalues are “DataType.TEXT” and “DataType.INTEGER”.

the fourth argument is not used, you can simply passIn the demo plugin, the first column is named “Version

in an empty string. The DataWriter object’s WriteRow()

info” and contains text, the second is named “Major” andmethod is used to actually write the data.

contains integers.
As the comment on line 90 indicates, however, the above
The data variable holds the data (list object) to be saved

process can also be accomplished in a single line using(line 79). The length of this list must match the length of

the WriteList () function. A look through other plugins

the columns definition.

reveals that most of them use the WriteList() function.
The first argument is a string giving some details about

Figure 8: The WriteMe() Function

26© Internet Initiative Japan Inc.

May.2022

Vol.

54

2. Focused Research (1)the data, but this is only written to logs and is not saved

It also looks like analysis functions are often namedin the file. The sixth argument can simply be an empty“ProcessXxxx()” or “ParseXxxx()”. And “PrintAll()” is usedstring since, within the WriteList() function, it is used asconsistently as the name of the function that saves analysis

the fourth argument of a DataWriter object.

results (although the demo plugin uses WriteMe()). Thisfunction has three arguments. The first is a list object

2.3.5 Naming Rules for Functions etc.

holding the analysis results to be saved. The second is an

The entry point function names are fixed, but the plugin

object (mac_info.output_params) that holds settings such

author needs to decide on the names of the other functions

as the save destination and save file format. The third isthat perform analysis and save the analysis results. As

ultimately passed to the WriteList() function as its sixth

noted earlier, there is no documentation on how plugins

argument, and as such, it appears to be an empty string

should be written, but looking at existing plugins, mostin most cases.

seem to use Pascal case function names, and variablenames are in snake case. This is good to be aware if you
want to be consistent with other plugins.

© Internet Initiative Japan Inc.

27

2.4 Finding Artifacts Not Supported by mac_apt

application in the Dock with the path of a malicious

As I mentioned, the creator of mac_apt maintains it almost

program*3, I realized that mac_apt does not provide analysis

single-handedly, so there are unsupported artifacts. Soof “~/Library/Caches//Cache.db”. To check whether mac_when looking at other analysis tools or reading macOS

apt supports a given artifact, you can look through the list

security articles, you may notice artifacts that mac_aptof plugins or search the mac_apt source code for the namedoes not support.

of an artifact file.

For example, when reading an article about how an attacker

When I checked Cache.db on an actual machine, I foundcould, as a persistence method, replace the path of anthat it stores not only HTTP but also HTTPS traffic (Figure

Figure 9: The cfrul_cache_response Table*3

28

Are You Docking Kidding Me? (https://posts.specterops.io/are-you-docking-kidding-me-9aa79c24bdc1).

© Internet Initiative Japan Inc.

May.2022

Vol.

54

2. Focused Research (1)9). And not only that, it also stores the HTTP request

In the next issue of the IIR, I will discuss the data stored

method, HTTP status, HTTP headers, and response bodyin Cache.db in detail and the implementation of a plugin(Figures 10 and 11). Information like this can be very

for analyzing this artifact file.

useful when doing forensics, so it would be well worth
thinking about implementing a plugin for this.

Figure 10: The cfurl_cache_receiver_data Table

Figure 11: The cfurl_cache_blob_data Table

Minoru Kobayashi
Forensic Investigator, Office of Emergency Response and Clearinghouse for Security Information, Advanced Security Division, IIJ Mr.
Kobayashi is a member of IIJ-SECT, mainly dealing with digital forensics. He works to improve incident response capabilities and in-house
technical capabilities. He gives lectures and training sessions at security events both in Japan and abroad, including Black Hat, FIRST TC,
JSAC, and Security Camp events.

© Internet Initiative Japan Inc.

29