7.
Image
Written by Derek Selander
By now, you have a solid foundation in debugging. You can find and attach to processes of interest, efficiently create regular expression breakpoints to cover a wide range of culprits, navigate the stack frame and tweak variables using the expression command.
However, 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.
The image command is an alias for the target modules subcommand. The image command specializes in querying information about modules; that is, the code loaded and executed in a process. Modules can comprise many things, including the main executable, frameworks, or plugins. However, the majority of these modules typically come in the form of dynamic libraries. Examples of dynamic libraries include UIKit for iOS or AppKit for macOS.
The image command is great for querying information about any private frameworks and its classes or methods not publicly disclosed in these header files.
Wait… modules?
You’ll continue using the Signals project. Fire up the project, build on the iPhone X Simulator and run.
Pause the debugger and type the following into the LLDB console:
(lldb) image list
This command will list all the modules currently loaded. You’ll see a lot!
The start of the list should look something like the following:
[ 0] 1E1B0254-4F55-3985-92E4-B2B6916AD424 0x000000010e7e7000 /Users/derekselander/Library/Developer/Xcode/DerivedData/Signals-atjgadijglwyppbagqpvyvftavcw/Build/Products/Debug-iphonesimulator/Signals.app/Signals
[ 1] 002B0442-3D59-3159-BA10-1C0A77859C6A 0x000000011e7c8000 /usr/lib/dyld
[ 2] E991FA37-F8F9-39BB-B278-3ACF4712A994 0x000000010e817000 /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/usr/lib/dyld_sim
The first module is the app’s main binary, Signals. The second and third modules pertain to the dynamic link editors (dyld). These to modules allow your program to load dynamic libraries into memory as well as the main executable in your process.
But there’s a lot more in this list! You can filter out just those of interest to you. Type the following into LLDB:
(lldb) image list Foundation
The output will look similar to the following:
[ 0] D153C8B2-743C-36E2-84CD-C476A5D33C72 0x000000010eb0c000 /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/Frameworks/Foundation.framework/Foundation
This is a useful way to find out information about just the module or modules you want.
Let’s explore this output. There’s a few interesting bits in there:
- The module’s UUID is printed out first (
D153C8B2-743C-36E2-84CD-C476A5D33C72). The UUID is important for hunting down symbolic information and uniquely identifies the version of the Foundation module. - Following the UUID is the load address (
0x000000010eb0c000). This identifies where the Foundation module is loaded into theSignalsexecutable’s process space. - Finally, you have the full path to where the module is located on disk.
Let’s take a deeper dive into another common module, UIKit. Type the following into LLDB:
(lldb) image dump symtab UIKitCore -s address
This will dump all the symbol table information available for UIKitCore. It’s more output than you can shake a stick at! This command sorts the output by the address in which the functions are implemented in the private UIKitCore module thanks to the -s address argument.
There’s a lot of useful information in there, but you can’t go reading all that, now can you? You need a way to effectively query the UIKitCore module with a flexible way to search for code of interest.
The image lookup command is perfect for filtering out all the data. Type the following into LLDB:
(lldb) image lookup -n "-[UIViewController viewDidLoad]"
This will dump out information relating just to UIViewController’s viewDidLoad instance method. You’ll see the name of the symbol relating to this method, and also where the code for that method is implemented inside the UIKitCore framework. This is good and all, but typing this is a little tedious and this can only dump out very specific instances.
This is where regular expressions come into play. The -r option will let you do a regular expression query. Type the following into LLDB:
(lldb) image lookup -rn UIViewController
Not only will this dump out all UIViewController methods, it’ll also spit out results like UIViewControllerBuiltinTransitionViewAnimator since it contains the name UIViewController. You can be smart with the regular expression query to only spit out UIViewController methods. Type the following into LLDB:
(lldb) image lookup -rn '\[UIViewController\ '
Alternatively, you can use the \s meta character to indicate a space so you don’t have to escape an actual space and surround it in quotes. The following expression is equivalent:
(lldb) image lookup -rn \[UIViewController\s
This is good, but what about categories? They come in the form of UIViewController(CategoryName). Search for all UIViewController categories.
(lldb) image lookup -rn '\[UIViewController\(\w+\)\ '
This is starting to get complicated. The backslash at the beginning says you want the literal character for “[”, then UIViewController.
Finally, the literal character of “(” then one or more alphanumeric or underscore characters (denoted by \w+), then “)”, followed by a space.
Working knowledge of regular expressions will help you to creatively query any public or private code in any of the modules loaded into your binary.
Not only does this print out both public and private code, this will also give you hints to the methods the UIViewController class overrides from its parent classes.
Hunting for code
Regardless of whether you’re hunting for public or private code, sometimes it’s just interesting trying to figure out how the compiler created the function name for a particular method. You briefly used the image lookup command above to find UIViewController methods. You also used it to hunt for how Swift property setters and getters are named in Chapter 4, “Stopping in Code.”
However, there are many more cases where knowing how code is generated will give you a better understanding of where and how to create breakpoints for code you’re interested in. One particularly interesting example to explore is the method signature for Objective-C’s blocks.
So what’s the best way to search for a method signature for an Objective-C block? Since you don’t have any clue on where to start searching for how blocks are named, a good way to start is by putting a breakpoint inside a block and then inspecting from there.
Open UnixSignalHandler.m, then find the singleton method sharedHandler. Within the function, look for the following code:
dispatch_once(&onceToken, ^{
sharedSignalHandler = [[UnixSignalHandler alloc] initPrivate];
});
Put a breakpoint using the Xcode GUI in the line beginning with sharedSignalHandler.
Then build and run. Xcode will now pause on the line of code you just set a breakpoint on. Check out the top stack frame in the debugging window.
You can find the name of the function you’re in using Xcode’s GUI. In the Debug Navigator you’ll see your stack trace and you can look at frame 0. That’s a little hard to copy and paste (well, impossible, actually). Instead, type the following into LLDB:
(lldb) frame info
You’ll get output similar to the following:
frame #0: 0x000000010f9b45a0 Commons`__34+[UnixSignalHandler sharedHandler]_block_invoke(.block_descriptor=0x000000010f9ba200) at UnixSignalHandler.m:72
As you can see, the full function name is __34+[UnixSignalHandler sharedHandler]_block_invoke.
There’s an interesting portion to the function name, _block_invoke. This might be the pattern you need to help uniquely identify blocks in Objective-C. Type the following into LLDB:
(lldb) image lookup -rn _block_invoke
This will do a regular expression search for the word _block_invoke. It will treat everything before and after the phrase as a wildcard.
But wait! You accidentally printed out all the Objective-C blocks loaded into the program. This search included anything from UIKit, Foundation, iPhoneSimulator SDK, etc. You should limit your search to only search for the Signals module.
Type the following into LLDB:
(lldb) image lookup -rn _block_invoke Signals
Nothing is printed out. What gives? Open the right Xcode panel and click on the File Inspector. Alternatively, press ⌘ + Option + 1 if you have the default Xcode keymap.
If you look to where UnixSignalHandler.m is compiled, you’ll see it’s actually compiled into the Commons framework. So, redo that search and look for Objective-C blocks in the Commons module. Type the following into LLDB:
(lldb) image lookup -rn _block_invoke Commons
Finally, you’ll get some output!
You’ll now see all the Objective-C blocks that you’ve searched for in the Commons framework.
Now, let’s create a breakpoint to stop on a subset of these blocks you’ve found. Type the following into LLDB:
(lldb) rb appendSignal.*_block_invoke -s Commons
Note: There is a subtle difference between searching for code in a module versus breaking in code for a module. Take the above commands as an example. When you wanted to search for all blocks in the Commons framework, you used
image lookup -rn _block_invoke Commons. When you wanted to make breakpoints for blocks in the Commons framework, you usedrb appendSignal.*block_invoke -s Commons. Take note of the-sargument vs the space.
The idea is this breakpoint will hit on any block within the appendSignal method.
Resume the program by clicking the play button or typing continue into LLDB. Jump over to Terminal and type the following:
pkill -SIGIO Signals
The signal you sent the program will be processed. However, before the signal gets visually updated to the tableview, your regex breakpoint will get hit.
The first breakpoint you will hit will be in:
__38-[UnixSignalHandler appendSignal:sig:]_block_invoke
Go past this by continuing the debugger.
Next you’ll hit a breakpoint in:
__38-[UnixSignalHandler appendSignal:sig:]_block_invoke_2
There’s an interesting item to note about this function name compared to the first; notice the number 2 in the method name. The compiler uses a base of <FUNCTION_NAME>_block_invoke for blocks defined within the function called <FUNCTION_NAME>. However, when there’s more than one block in the function, a number is appended to the end to denote this.
As you learned in the previous chapter, the frame variable command will print all known local variable instances to a particular function. Execute that command now to see the reference found in this particular block.
Type the following into LLDB:
(lldb) frame variable
The output will look similar to the following:
(__block_literal_5 *) = 0x0000608000275e80
(int) sig = <read memory from 0x41 failed (0 of 4 bytes read)>
(siginfo_t *) siginfo = <read memory from 0x39 failed (0 of 8 bytes read)>
(UnixSignalHandler *const) self = <read memory from 0x31 failed (0 of 8 bytes read)>
Those read memory failures don’t look good! Step over once, either using the Xcode GUI or by typing next in LLDB. Next, execute frame variable again in LLDB. This time you’ll see something similar to the following:
(__block_literal_5 *) = 0x0000608000275e80
(int) sig = 23
(siginfo_t *) siginfo = 0x00007fff587525e8
(UnixSignalHandler *) self = 0x000061800007d440
(UnixSignal *) unixSignal = 0x000000010bd9eebe
You needed to step over one statement, so the block executed some initial logic to setup the function, also known as the function prologue. The function prologue is a topic related to assembly, which you’ll learn about in Section II.
This is actually quite interesting. First you see an object which references the block that’s being invoked. In this case it’s the type __block_literal_5. Then there are the sig and siginfo parameters that were passed into the Objective-C method where this block is invoked from. How did these get passed into the block?
Well, when a block is created, the compiler is smart enough to figure out what parameters are being used by it. It then creates a function that takes these as parameters. When the block is invoked, it’s this function that is called, with the relevant parameters passed in.
Type the following into LLDB:
(lldb) image lookup -t __block_literal_5
You’ll get something similar to the following:
Best match found in /Users/derekselander/Library/Developer/Xcode/DerivedData/Signals-efqxsbqzgzcqqvhjgzgeabtwfufy/Build/Products/Debug-iphonesimulator/Signals.app/Frameworks/Commons.framework/Commons:
id = {0x100000cba}, name = "__block_literal_5", byte-size = 52, decl = UnixSignalHandler.m:123, compiler_type = "struct __block_literal_5 {
void *__isa;
int __flags;
int __reserved;
void (*__FuncPtr)();
__block_descriptor_withcopydispose *__descriptor;
UnixSignalHandler *const self;
siginfo_t *siginfo;
int sig;
}"
This is the object that defines the block! Neat!
As you can see, this is almost as good as a header file for telling you how to navigate the memory in the block. Provided you cast the reference in memory to the type __block_literal_5, you can easily print out all the variables referenced by the block.
Start by getting the stack frame’s variable information again by typing the following:
(lldb) frame variable
Next, find the address of the __block_literal_5 object and print it out like so:
(lldb) po ((__block_literal_5 *)0x0000618000070200)
You should see something similar to the following:
<__NSMallocBlock__: 0x0000618000070200>
If you don’t, make sure the address you’re casting to a __block_literal_5 is the address of your block as it will differ each time the project is run.
Note: Bug alert in lldb-900.0.57 where LLDB will incorrectly dereference the
__block_literal_5pointer when executing theframe variablecommand. This means that the pointer output of(__block_literal_5 *)will give the class NSMallocBlock instead of the instance of NSMallocBlock. If you are getting the class description instead of an instance description, you can get around this by either referencing theRDIregister immediately at the start of the function, or obtain the instance of the__NSMallocBlock__viax/gx '$rbp - 32'if you are further into the function.
Now you can query the members of the __block_literal_5 struct. Type the following into LLDB:
(lldb) p/x ((__block_literal_5 *)0x0000618000070200)->__FuncPtr
This will dump the location of the function pointer for the block. The output will look like the following:
(void (*)()) $1 = 0x000000010756d8a0 (Commons`__38-[UnixSignalHandler appendSignal:sig:]_block_invoke_2 at UnixSignalHandler.m:123)
The function pointer for the block points to the function which is run when the block is invoked. It’s the same address that is being executed right now! You can confirm this by typing the following, replacing the address with the address of your function pointer printed in the command you last executed:
(lldb) image lookup -a 0x000000010756d8a0
This uses the -a (address) option of image lookup to find out which symbol a given address relates to.
Jumping back to the block struct’s members, you can also print out all the parameters passed to the block as well. Type the following, again replacing the address with the address of your block:
(lldb) po ((__block_literal_5 *)0x0000618000070200)->sig
This will output the signal number that was sent in as a parameter to the block’s parent function.
There is also a reference to the UnixSignalHandler in a member of the struct called self. Why is that? Take a look at the block and hunt for this line of code:
[(NSMutableArray *)self.signals addObject:unixSignal];
It’s the reference to self the block captured, and uses to find the offset of where the signals array is. So the block needs to know what self is. Pretty cool, eh?
By the way, you can dump out the full struct with the p command and dereferencing the pointer like so:
(lldb) p *(__block_literal_5 *)0x0000618000070200
Using the image dump symfile command in combination with the module is a great way to learn how a certain unknown data type works. It’s also a great tool to understand how the compiler generates code for your sources.
Additionally, you can inspect how blocks hold references to pointers outside the block — a very useful tool when debugging memory retain cycle problems.
Snooping around
OK, you’ve discovered how to inspect a private class’s instance variables in a static manner, but that block memory address is too tantalizing to be left alone. Try printing it out and exploring it using dynamic analysis. Type the following, replacing the address with the address of your block:
po 0x0000618000070200
LLDB will dump out a class indicating it’s an Objective-C class.
<__NSMallocBlock__: 0x618000070200>
This is interesting. The class is __NSMallocBlock__. Now that you’ve learned how to dump methods for both private and public classes, it’s time to explore what methods __NSMallocBlock__ implements. In LLDB, type:
(lldb) image lookup -rn __NSMallocBlock__
Nothing. Hmm. This means __NSMallocBlock__ doesn’t override any methods implemented by its super class. Type the following in LLDB to figure out the parent class of __NSMallocBlock__.
(lldb) po [__NSMallocBlock__ superclass]
This will produce a similarly named class named __NSMallocBlock — notice the lack of trailing underscores. What can you find out about this class? Does this class implement or override any methods? Type the following into LLDB:
(lldb) image lookup -rn __NSMallocBlock
The methods dumped by this command seems to indicate that __NSMallocBlock is responsible for memory management, since it implements methods like retain and release. What is the parent class of __NSMallocBlock? Type the following into LLDB:
(lldb) po [__NSMallocBlock superclass]
You’ll get another class named NSBlock. What about this class? Does it implement any methods? Type the following into LLDB:
(lldb) image lookup -rn 'NSBlock\ '
Notice the backslash and space at the end. This ensures there are no other classes that will match this query — remember, without it, a different class could be returned that contains the name NSBlock. A few more methods will be spat out. One of them, invoke, looks incredibly interesting:
Address: CoreFoundation[0x000000000018fd80] (CoreFoundation.__TEXT.__text + 1629760)
Summary: CoreFoundation`-[NSBlock invoke]
You’re now going to try to invoke this method on the block. However, you don’t want the block to disappear when the references that are retaining this block release their control, thus lowering the retainCount, and potentially deallocating the block.
There’s a simple way to hold onto this block — just retain it! Type the following into LLDB, replacing the address with the address of your block:
(lldb) po id $block = (id)0x0000618000070200
(lldb) po [$block retain]
(lldb) po [$block invoke]
For the final line, you’ll see the following output:
Appending new signal: SIGIO
nil
This shows you the block has been invoked again! Pretty neat!
It only worked because everything was already set up in the right way for the block to be invoked, since you’re currently paused right at the start of the block.
This type of methodology for exploring both public and private classes, and then exploring what methods they implement, is a great way to learn what goes on underneath the covers of a program. You’ll later use the same process of discovery for methods and then analyze the assembly these methods execute, giving you a very close approximation of the source code of the original method.
Private debugging methods
The image lookup command does a beautiful job of searching for private methods as well the public methods you’ve seen throughout your Apple development career.
However, there are some hidden methods which are quite useful when debugging your own code.
For example, a method beginning with _ usually denotes itself as being a private (and potentially important!) method.
Let’s try to search for any Objective-C methods in all of the modules that begin with the underscore character and contain the word “description” in it.
Build and run the project again. When your breakpoint in sharedHandler is hit, type the following into LLDB:
(lldb) image lookup -rn (?i)\ _\w+description\]
This regular expression is a bit complex so let’s break it down.
The expression searches for a space (\ ) followed by an underscore (_). Next, the expression searches for one or more alphanumeric or underscore characters (\w+) followed by the word description, followed by the ] character.
The beginning of the regular expression has an interesting set of characters, (?i). This states you want this to be a case insensitive search.
This regular expression has backslashes prepending characters. This means you want the literal character, instead of its regular expression meaning. It’s called “escaping”. For example, in a regular expression, the ] character has meaning, so to match the literal “]” character, you need to use \].
The exception to this in the regular expression above is the \w character. This is a special search item returning an alphanumeric character or an underscore (i.e. _, a-z, A-Z, 0-9).
If you had the deer in the headlights expression when reading this line of code, it’s strongly recommended to carefully scan https://docs.python.org/2/library/re.html to brush up on your regular expression queries; it’s only going to get more complicated from here on out.
Carefully scan through the output of image lookup. It’s often this tedious scanning that gives you the best answers, so please make sure you go through all the output.
You’ll notice a slew of interesting methods belonging to an NSObject category named IvarDescription belonging in UIKit.
Redo the search so only contents in this category get printed out. Type the following into LLDB:
(lldb) image lookup -rn NSObject\(IvarDescription\)
The console will dump out all the methods this category implements. Of the group of methods, there are a couple very interesting methods that stand out:
_ivarDescription
_propertyDescription
_methodDescription
_shortMethodDescription
Since this category is on NSObject, any subclass of NSObject can use these methods. This is pretty much everything, of course!
Execute the _ivarDescription on the UIApplication Objective-C class. Type the following into LLDB:
(lldb) po [[UIApplication sharedApplication] _ivarDescription]
You’ll get a slew of output since UIApplication holds many instance variables behind the scenes. Scan carefully and find something that interests you. Don’t come back to reading this until you find something of interest. This is important.
After carefully scanning the output, you can see a reference to the private class UIStatusBar. Which Objective-C setter methods does UIStatusBar have, I hear you ask? Let’s find out! Type the following into LLDB:
(lldb) image lookup -rn '\[UIStatusBar\ set'
This dumps all the setter methods available to UIStatusBar. In addition to the declared and overriden methods available in UIStatusBar, you have access to all the methods available to its parent class. Check to see if the UIStatusBar is a subclass of the UIView class
(lldb) po (BOOL)[[UIStatusBar class] isSubclassOfClass:[UIView class]]
Alternatively, you can repeatedly use the superclass method to jump up the class hierarchy. As you can see, it looks like this class is a subclass of UIView, so the backgroundColor property is available to you in this class. Let’s play with it.
First, type the following into LLDB:
(lldb) po [[UIApplication sharedApplication] statusBar]
You’ll see something similar to the following:
<UIStatusBar_Modern: 0x7fdcf3c0f090; frame = (0 0; 375 44); autoresize = W+BM; layer = <CALayer: 0x60c000036640>>
This prints out the UIStatusBar instance for your app. Next, using the address of the status bar, type the following into LLDB:
(lldb) po [0x7fdcf3c0f090 setBackgroundColor:[UIColor purpleColor]]
In LLDB, remove any of the previous breakpoints you created.
(lldb) breakpoint delete
Continue the app and see the beauty you’ve unleashed upon the world through your fingertips!
Not the prettiest of apps now, but at least you’ve managed to inspect a private method and used it to do something fun!
Where to go from here?
As a challenge, try figuring out a pattern using image lookup to find all Swift closures within the Signals module. Once you do that, create a breakpoint on every Swift closure within the Signals module. If that’s too easy, try looking at code that can stop on didSet/willSet property helpers, or do/try/catch blocks.
Also, try looking for more private methods hidden away in Foundation and UIKit. Have fun!
Need another challenge?
Using the private UIKitCore NSObject category method _shortMethodDescription as well as your image lookup -rn command, search for the class that’s responsible for displaying time in the upper left corner of the status bar and change it to something more amusing. Drill into subviews and see if you can find it using the tools given so far.