This section tells you a few things you need to know before you get started, such as what you’ll need for hardware and software, where to find the project files for this book, and more.
This section will cover the basics of using LLDB, Apple’s software debugger. You’ll explore an application named Signals, an Objective-C/Swift application that illustrates how Unix signals can be processed within an application. You’ll learn some strategies to find and create Swift syntax-style breakpoints as well as Objective-C style breakpoints. By the end of this section, you’ll be able to wield the debugger to perform most of the basic tasks needed for debugging, as well as create your own simple custom commands.
In this chapter, you’re going to get acquainted with LLDB and investigate the process of introspecting and debugging a program. You’ll start off by introspecting a program you didn’t even write — Xcode!
Just like any respectable developer tool, LLDB ships with a healthy amount of documentation. Knowing how to navigate through this documentation — including some of the more obscure command flags — is essential to mastering LLDB.
Now that you’ve learned about the two most essential commands, help and apropos, it’s time to investigate all the ways LLDB can attach itself to a process.
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.
Learn how to query and manipulate the software you’re debugging. In this chapter, you’ll learn about the **`expression`** command, which allows you to query variables and execute arbitrary code.
You’ve learned how to create breakpoints, how to print and modify values, as well as how to execute code while paused in the debugger. But so far you’ve been left high and dry on how to move around in the debugger and inspect data beyond the immediate. In this chapter, you’ll learn how to move the debugger in and out of code while `lldb` has suspended a program.
It’s time to explore one of the best tools for finding code of interest through the powers of lldb. In this chapter, you’ll take a deep dive into the image command.
Learn an attractive alternative to creating breakpoints by monitoring reads or writes to memory.
In this chapter, you’ll learn how to create simple, custom commands and then persist them for using every time you launch `lldb`.
In this chapter, you will learn how to create commands that can take inputs.
Knowing what the computer is doing with all those 1s and 0s underneath your code is an excellent skill to have when digging for useful information about a program. This section will set you up with the theory you’ll need for the remainder of this book in order to create complex debugging scripts — and introduce you to the basic theory behind reverse-engineering code.
Now you’ve gained a basic understanding of how to maneuver around the debugger, it’s time to take a step down the executable Jenga tower and explore the 1s and 0s that make up your source code. This section will focus on the low-level aspects of debugging.
In this chapter, you’ll explore how a program executes. You’ll look at a special register used to tell the processor where it should read the next instruction from, as well as how different sizes and groupings of memory can produce very different results.
What does being “passed on the stack” mean exactly? It’s time to take a deeper dive into what happens when a function is called from an assembly standpoint by exploring some “stack related” registers as well as the contents in the stack.
With a foundation of assembler theory solidly below you, it’s time to explore other aspects of how programs work. This section is an eclectic grab-bag of weird and fun studies into reverse engineering, seldom-used APIs and debugging strategies.
In this chapter, you’ll learn about a special API that enables debugging or even disables other processes from debugging the calling process.
Shared libraries are essential for any program to run. This chapter focuses on the compilation and linking process, highlighting how to write code that uses public and private APIs.
It’s time to learn about the complementary skills of developing with these frameworks. In this chapter, you’re going to learn about methods and strategies to “hook” into Swift and C code as well as execute methods you wouldn’t normally have access to.
The file format used for a compiled program running on any Apple hardware. This chapter discusses how to read this information.
Now that you’ve learned the theory, it’s time to have some fun. Learn how to search for curse words in Apple frameworks and cheat at gambling games.
This chapter will give you a basic overview of how code signing works by having you pick apart an iOS application and learn how to re-sign and install it onto your iOS device.
You’ve learned the basic LLDB commands, the assembly that goes into code and the miscellaneous low-level concepts that make a program…well, a program.
It’s time to put that knowledge together to create some very powerful and complex debugging scripts. As you will soon see, you’re only limited by your skill and imagination — and finding the correct class (or header file) to do your debugging bidding.
LLDB ships with an integrated Python module that allows you to access most parts of the debugger through Python. This lets you leverage all the power of Python (and its modules) to help uncover whatever dark secrets vex you.
Next up in the tradeoff between convenience and complexity is LLDB’s script bridging. With script bridging, you can do nearly anything you like. Script bridging is a Python interface LLDB uses to help extend the debugger to accomplish your wildest debugging dreams.
You need a methodical way to figure out what went wrong in your LLDB script so you don’t pull your hair out. In this chapter, you’ll explore how to inspect your LLDB Python scripts using the Python pdb module, which is used for debugging Python scripts.
You’ve learned the essentials of working with LLDB’s Python module, as well as how to correct any errors using Python’s PDB debugging module. Now you’ll explore the main players within the lldb Python module for a good overview of the main parts. In this chapter, you’ll add some arguments to this script and deal with some annoying edge cases, such handling commands differently between Objective-C and Swift.
When you’re creating a custom debugging command, you’ll often want to slightly tweak functionality based upon options or arguments supplied to your command. A custom LLDB command that can do a job only one way is a boring one-trick pony. In this chapter, you’ll explore how to pass optional parameters (aka options) as well as arguments (parameters which are expected) to your custom command to alter functionality or logic in your custom LLDB scripts.
So far, when evaluating JIT code (i.e. Objective-C, Swift, C, etc. code that’s executed through your Python script), you’ve used a small set of APIs to evaluate the code. It’s time to talk about a new class in the lldb Python module, SBValue, and how it can simplify the parsing of JIT code output.
For the rest of the chapters in this section, you’ll focus on Python scripts. As alluded to in the previous chapter, the image lookup -rn command is on its way out. When you finish this chapter, you’ll have a new script named “lookup” which queries in a much cleaner way.
When LLDB comes up against a stripped executable (an executable devoid of DWARF
debugging information), LLDB won’t have the symbol information to give you the stack trace. Instead, LLDB will generate a synthetic name for a method it recognizes as a method, but doesn’t know what to call it. In this chapter, you’ll build an LLDB script that will resymbolicate stripped Objective-C functions in a stack trace.
For the final chapter in this section, you’ll go through the same steps I myself took to understand how the MallocStackLogging environment variable is used to get the stack trace when an object is created. From there, you’ll create a custom LLDB command which gives you the stack trace of when an object was allocated or deallocated in memory — even after the stack trace is long gone from the debugger.
What? Youve never heard of DTrace?! It is AWESOME! DTrace is a tool that lets you explore code in dynamic & static ways.
You can create DTrace probes to be compiled into your code (static), or you can inspect any code that is already compiled and running (dynamic). DTrace is a versatile tool: it can be a profiler, an analyzer, a debugger or anything you want.
I often will use DTrace to cast a wide-reaching net over code I want to explore, when I have no clue where I should start.
You’ll explore a very small section of what DTrace is capable of doing by tracing Objective-C code in already compiled applications. Using DTrace to observe iOS frameworks (like UIKit) can give you an incredible insight into how the authors designed their code.
This chapter will act as a grab-bag of more DTrace fundamentals, destructive actions (yay!), as well as how to use DTrace with Swift. In this chapter, you’ll learn additional ways DTrace can profile code, as well as how to augment existing code without laying a finger on the actual executable itself.