Skip to main content

Investigate an Application with an Objective-C Frontend

This article will walk you through investigating an application with an Objective-C frontend using Ocular.

Sample Application

For the examples to follow, we will be working with the Mach-O Browser application, whose repository you can clone via GitHub.

// clone the project
git clone

// navigate into the working directory and build
cd macho-browser

// build
xcodebuild OTHER_CFLAGS=-flto OTHER_LDFLAGS=-flto CODE_SIGN_IDENTITY="-" DEVELOPMENT_TEAM="" -configuration Debug

// navigate into the folder with the build files
cd "Build/Mach-O"

Once you've successfully built macho-browser, you can transform it to a Code Property Graph (CPG):

/Users/<username>/.shiftleft/ocular/ --output=/tmp/ *.o

If you're not using the default install location for Ocular, you may need to change /.shiftleft/ocular to the appropriate path.

Once you have the CPG, start Ocular:

sl ocular

When prompted, load the CPG you generated:

ocular> importCpg("/tmp/")

Run workspace to ensure that your CPG has been loaded. If loaded is true, you're ready to proceed with your investigative queries.

ocular> workspace
res0: Workspace =
| name | overlays | loaded|
| | semanticcpg(l),dataflow(l) | true |

Investigative Queries

The following examples will walk you through the basics of querying the CPG to gather information about your application.

  • Identify functions with more than 4 parameters: cpg.method.where(_.parameter.size > 4).l

  • Sort methods by the number of callers and exclude the first 1000 results: val methodList = => (x.start.callIn.size,

  • Filter for methods that have callers: methodList.filter(_._1>0).sorted

  • Filter for methods that do not have callers: methodList.filter(_._1==0)

  • Get all of the hard-coded literals in the code: cpg.literal.code.l

  • Get all types and associated properties/members in the code: val typesList = { t => (, }.l

  • Identify the call site to malloc where the first argument contains an arithmetic expression:"malloc").filter(_.argument (1).arithmetics ).l

Querying for Method-Specific Information

Let's say that you're interested in a class called LoadCommand and how it is used. You can declare variables containing the class name for easy reuse:

val className="LoadCommand"

You can pick a class and observe its methods:

The result will look something like the following:

result: List[String] = List(
"+[LoadCommand loadCommandWithData:offset:]",
"-[LoadCommand .cxx_destruct]",

You can get a list of all Objective-C classes (the convention is that class names start with an uppercase later):"[A-Z][A-Za-z]*").name.p.sorted

res11: List[String] = List(

You can find where a method is called:

cpg.method.fullNameExact("-[LoadCommand initWithData:offset:]").caller.fullName.p

result: List[String] = List("+[LoadCommand loadCommandWithData:offset:]")

Get subclasses in it's type hierarchy:

result: List[String] = List("SymbolTableLoadCommand", "SegmentLoadCommand")

Get superclasses in its type hierarchy:"LoadCommand")

result: List[String] = List("NSObject")

Get all of the NSObject subclasses:"NSObject")

result: List[String] = List(

Example: Protecting Against the Billion Laughs Attack

iOS offers you two SDK options for parsing XML: NSXMLParser and libxml2.

If you're using NSXMLParser, you should enable the shouldResolveExternalEntities property to protect yourself against the Billion Laughs Attack:

// Verify that the NSXMLParser has been imported and shouldResolveExternalEntities has been enabled".*NSXMLParser.*")".*shouldResolveExternalEntities.*").l

// [OR] Verify that, if shouldResolveExternalEntities is set, it is associated to the type NSXMLParser".*shouldResolveExternalEntities.*").where(_.astParentFullName.contains("NSXMLParser")).l