For the complete documentation index, see llms.txt. This page is also available as Markdown.

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 https://github.com/dcsch/macho-browser.git

// 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 Browser.build/Debug/Mach-O Browser.build/Objects-normal/x86_64/"

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

/Users/<username>/.shiftleft/ocular/llvm2cpg.sh --output=/tmp/macho.cpg.bin.zip *.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:

When prompted, load the CPG you generated:

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

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 = cpg.method.map(x => (x.start.callIn.size, x.name)).l.sorted.reverse.take(1000)

  • 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 = cpg.typeDecl.map { t => (t.name, t.start.member.name.l) }.l

  • Identify the call site to malloc where the first argument contains an arithmetic expression: cpg.call("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:

You can pick a class and observe its methods:

The result will look something like the following:

You can get a list of all Objective-C classes (the convention is that class names start with an uppercase later):

You can find where a method is called:

Get subclasses in it's type hierarchy:

Get superclasses in its type hierarchy:

Get all of the NSObject subclasses:

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:

Last updated

Was this helpful?