Chapters

Hide chapters

Advanced Apple Debugging & Reverse Engineering

Third Edition · iOS 12 · Swift 4.2 · Xcode 10

Before You Begin

Section 0: 3 chapters
Show chapters Hide chapters

Section III: Low Level

Section 3: 7 chapters
Show chapters Hide chapters

Section IV: Custom LLDB Commands

Section 4: 8 chapters
Show chapters Hide chapters

4. Stopping in Code
Written by Derek Selander

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now

Whether you’re using Swift, Objective-C, C++, C, or an entirely different language in your technology stack, you’ll need to learn how to create breakpoints. It’s easy to click on the side panel in Xcode to create a breakpoint using the GUI, but the LLDB console can give you much more control over breakpoints.

In this chapter, you’re going to learn all about breakpoints and how to create them using LLDB.

Signals

For this chapter, you’ll be looking at a project I’ve supplied; it’s called Signals and you’ll find it in the resources bundle for this chapter.

Open up the Signals project using Xcode. Signals is a basic master-detail project themed as an American football app that displays some rather nerdily-named offensive play calls.

Internally, this project montors several Unix signals and displays them when the Signals program receives them.

Unix signals are a basic form of interprocess communication. For example, one of the signals, SIGSTOP, can be used to save the state and pause execution of a process, while its counterpart, SIGCONT, is sent to a program to resume execution. Both of these signals can be used by a debugger to pause and continue a program’s execution.

This is an interesting application on several fronts, because it not only explores Unix signal handling, but also highlights what happens when a controlling process (LLDB) handles the passing of Unix signals to the controlled process. By default, LLDB has custom actions for handling different signals. Some signals are not passed onto the controlled process while LLDB is attached.

In order to display a signal, you can either raise a Signal from within the application, or send a signal externally from a different application, like Terminal.

In addition, there’s a UISwitch that toggles the signal handling. When the switch is toggled, it calls a C function sigprocmask to disable or enable the signal handlers.

Finally, the Signal application has a Timeout bar button which raises the SIGSTOP signal from within the application, essentially “freezing” the program. However, if LLDB is attached to the Signals program (and by default it will be, when you build and run through Xcode), calling SIGSTOP will allow you to inspect the execution state with LLDB while in Xcode.

Select your iOS Simulator of choice while making sure the iOS Simulator is at least version iOS 12.0 or greater. Build and run the app. Once the project is running, navigate to the Xcode console and pause the debugger.

Resume Xcode and keep an eye on the Simulator. A new row will be added to the UITableView whenever the debugger stops then resumes execution. This is achieved by Signals monitoring the SIGSTOP Unix signal event and adding a row to the data model whenever it occurs. When a process is stopped, any new signals will not be immediately processed because the program is sort of, well, stopped.

Xcode breakpoints

Before you go off learning the cool, shiny breakpoints through the LLDB console, it’s worth covering what you can achieve through Xcode alone.

LLDB breakpoint syntax

Now that you’ve had a crash course in using the IDE debugging features of Xcode, it’s time to learn how to create breakpoints through the LLDB console. In order to create useful breakpoints, you need to learn how to query what you’re looking for.

(lldb) image lookup -n "-[UIViewController viewDidLoad]"
1 match found in /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/PrivateFrameworks/UIKitCore.framework/UIKitCore:
        Address: UIKitCore[0x0000000000ac813c] (UIKitCore.__TEXT.__text + 11295452)
        Summary: UIKitCore`-[UIViewController viewDidLoad]
(lldb) image lookup -rn test

Objective-C properties

Learning how to query loaded code is essential for learning how to create breakpoints on that code. Both Objective-C and Swift have specific property signatures when they’re created by the compiler, which results in different querying strategies when looking for code.

@interface TestClass : NSObject 
@property (nonatomic, strong) NSString *name; 
@end
-[TestClass name]
-[TestClass setName:]
(lldb) image lookup -n "-[TestClass name]"
1 match found in /Users/derekselander/Library/Developer/Xcode/DerivedData/Signals-atqcdyprrotlrvdanihoufkwzyqh/Build/Products/Debug-iphonesimulator/Signals.app/Signals:
        Address: Signals[0x0000000100002150] (Signals.__TEXT.__text + 0)
        Summary: Signals`-[TestClass name] at TestClass.h:28
(lldb) image lookup -n "-[TestClass setName:]"

Objective-C properties and dot notation

Something that is often misleading to entry level Objective-C (or Swift only) developers is the Objective-C dot notation syntax for properties.

TestClass *a = [[TestClass alloc] init];

// Both equivalent for setters
[a setName:@"hello, world"];
a.name = @"hello, world";

// Both equivalent for getters
NSString *b; 
b = [a name]; // b = @"hello, world"
b = a.name;   // b = @"hello, world"

Swift properties

The syntax for a property is much different in Swift. Take a look at the code in SwiftTestClass.swift which contains the following:

class SwiftTestClass: NSObject {
  var name: String!
}
(lldb) image lookup -rn Signals.SwiftTestClass.name.setter
1 match found in /Users/derekselander/Library/Developer/Xcode/DerivedData/Signals-atqcdyprrotlrvdanihoufkwzyqh/Build/Products/Debug-iphonesimulator/Signals.app/Signals:
        Address: Signals[0x000000010000bd50] (Signals.__TEXT.__text + 39936)
        Summary: Signals`Signals.SwiftTestClass.name.setter : Swift.Optional<Swift.String> at SwiftTestClass.swift:28
(lldb) b Signals.SwiftTestClass.name.setter : Swift.Optional<Swift.String>
(lldb) image lookup -rn Signals.SwiftTestClass.name

Finally… creating breakpoints

Now you know how to query the existence of functions and methods in your code, it’s time to start creating breakpoints on them.

(lldb) b -[UIViewController viewDidLoad]
Breakpoint 1: where = UIKitCore`-[UIViewController viewDidLoad], address = 0x0000000114a4a13c

Regex breakpoints and scope

Another extremely powerful command is the regular expression breakpoint, rbreak, which is an abbreviation for breakpoint set -r %1. You can quickly create many breakpoints using smart regular expressions to stop wherever you want.

(lldb) b Signals.SwiftTestClass.name.setter : Swift.Optional<Swift.String>
(lldb) rb SwiftTestClass.name.setter
(lldb) rb name\.setter
(lldb) rb '\-\[UIViewController\ ' 
(lldb) breakpoint delete
(lldb) rb '\-\[UIViewController(\(\w+\))?\ '
(lldb) rb . -f DetailViewController.swift
(lldb) rb . 
(lldb) rb . -s Commons 
(lldb) rb . -s UIKitCore
(lldb) breakpoint delete 
(lldb) rb . -s UIKitCore -o 1

Other cool breakpoint options

The -L option lets you filter by source language. So, if you wanted to only go after Swift code in the Commons module of the Signals application, you could do the following:

(lldb) breakpoint set -L swift -r . -s Commons
(lldb) breakpoint set -A -p "if let" 
(lldb) breakpoint set -p "if let" -f MasterViewController.swift -f DetailViewController.swift
(lldb) breakpoint set -p "if let" -s Signals -A
(lldb) breakpoint delete
(lldb) breakpoint set -n "-[UIViewController viewDidLoad]" -C "po $arg1" -G1 
(lldb) breakpoint write -f /tmp/br.json
(lldb) platform shell cat /tmp/br.json
(lldb) breakpoint delete
(lldb) breakpoint read -f /tmp/br.json

Modifying and removing breakpoints

Now that you have a basic understanding of how to create these breakpoints, you might be wondering how you can alter them. What if you found the object you were interested in and wanted to delete the breakpoint, or temporarily disable it? What if you need to modify the breakpoint to perform a specific action next time it triggers?

(lldb) b main
Breakpoint 1: 70 locations.
(lldb) breakpoint list 1
1: name = 'main', locations = 70, resolved = 70, hit count = 0
  1.1: where = Signals`main at AppDelegate.swift, address = 0x00000001098b1520, resolved, hit count = 0 
  1.2: where = Foundation`-[NSThread main], address = 0x0000000109bfa9e3, resolved, hit count = 0 
  1.3: where = Foundation`-[NSBlockOperation main], address = 0x0000000109c077d6, resolved, hit count = 0 
  1.4: where = Foundation`-[NSFilesystemItemRemoveOperation main], address = 0x0000000109c40e99, resolved, hit count = 0 
  1.5: where = Foundation`-[NSFilesystemItemMoveOperation main], address = 0x0000000109c419ee, resolved, hit count = 0 
  1.6: where = Foundation`-[NSInvocationOperation main], address = 0x0000000109c6aee4, resolved, hit count = 0 
  1.7: where = Foundation`-[NSDirectoryTraversalOperation main], address = 0x0000000109caefa6, resolved, hit count = 0 
  1.8: where = Foundation`-[NSOperation main], address = 0x0000000109cfd5e3, resolved, hit count = 0 
  1.9: where = Foundation`-[_NSFileAccessAsynchronousProcessAssertionOperation main], address = 0x0000000109d55ca9, resolved, hit count = 0 
  1.10: where = UIKit`-[_UIFocusFastScrollingTest main], address = 0x000000010b216598, resolved, hit count = 0 
  1.11: where = UIKit`-[UIStatusBarServerThread main], address = 0x000000010b651e97, resolved, hit count = 0 
  1.12: where = UIKit`-[_UIDocumentActivityDownloadOperation main], address = 0x000000010b74f718, resolved, hit count = 0 
(lldb) breakpoint list 1 -b 
(lldb) breakpoint list 
(lldb) breakpoint list 1 3 
(lldb) breakpoint list 1-3
(lldb) breakpoint delete 1
(lldb) breakpoint delete 1.1 

Where to go from here?

You’ve covered a lot in this chapter. Breakpoints are a big topic and mastering the art of quickly finding an item of interest is essential to becoming a debugging expert. You’ve also started exploring function searching using regular expressions. Now would be a great time to brush up on regular expression syntax, as you’ll be using lots of regular expressions in the rest of this book.

Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.
© 2024 Kodeco Inc.

You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now