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

Heads up... You're reading this book for free, with parts of this chapter shown beyond this point as scrambled text.

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/ 
[  1] 002B0442-3D59-3159-BA10-1C0A77859C6A 0x000000011e7c8000 /usr/lib/dyld 
[  2] E991FA37-F8F9-39BB-B278-3ACF4712A994 0x000000010e817000 /Applications/ 

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/ 

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:

  1. 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.
  2. Following the UUID is the load address (0x000000010eb0c000). This identifies where the Foundation module is loaded into the Signals executable’s process space.
  3. 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.”

dispatch_once(&onceToken, ^{
  sharedSignalHandler = [[UnixSignalHandler alloc] initPrivate];

(lldb) frame info
frame #0: 0x000000010f9b45a0 Commons`__34+[UnixSignalHandler sharedHandler]_block_invoke(.block_descriptor=0x000000010f9ba200) at UnixSignalHandler.m:72
(lldb) image lookup -rn _block_invoke
(lldb) image lookup -rn _block_invoke Signals
(lldb) image lookup -rn _block_invoke Commons
(lldb) rb appendSignal.*_block_invoke -s Commons
pkill -SIGIO Signals
__38-[UnixSignalHandler appendSignal:sig:]_block_invoke
__38-[UnixSignalHandler appendSignal:sig:]_block_invoke_2
(lldb) frame variable
(__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)>
(__block_literal_5 *)  = 0x0000608000275e80
(int) sig = 23
(siginfo_t *) siginfo = 0x00007fff587525e8
(UnixSignalHandler *) self = 0x000061800007d440
(UnixSignal *) unixSignal = 0x000000010bd9eebe
(lldb) image lookup -t  __block_literal_5
Best match found in /Users/derekselander/Library/Developer/Xcode/DerivedData/Signals-efqxsbqzgzcqqvhjgzgeabtwfufy/Build/Products/Debug-iphonesimulator/
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;
(lldb) frame variable
(lldb) po ((__block_literal_5 *)0x0000618000070200)
<__NSMallocBlock__: 0x0000618000070200>
(lldb) p/x ((__block_literal_5 *)0x0000618000070200)->__FuncPtr
(void (*)()) $1 = 0x000000010756d8a0 (Commons`__38-[UnixSignalHandler appendSignal:sig:]_block_invoke_2 at UnixSignalHandler.m:123)
(lldb) image lookup -a 0x000000010756d8a0
(lldb) po ((__block_literal_5 *)0x0000618000070200)->sig
[(NSMutableArray *)self.signals addObject:unixSignal];
(lldb) p *(__block_literal_5 *)0x0000618000070200

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
<__NSMallocBlock__: 0x618000070200>
(lldb) image lookup -rn __NSMallocBlock__
(lldb) po [__NSMallocBlock__ superclass]
(lldb) image lookup -rn __NSMallocBlock
(lldb) po [__NSMallocBlock superclass]
(lldb) image lookup -rn 'NSBlock\ '
Address: CoreFoundation[0x000000000018fd80] (CoreFoundation.__TEXT.__text + 1629760)
        Summary: CoreFoundation`-[NSBlock invoke]    
(lldb) po id $block = (id)0x0000618000070200
(lldb) po [$block retain]
(lldb) po [$block invoke]
Appending new signal: SIGIO

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.

(lldb) image lookup -rn (?i)\ _\w+description\]
(lldb) image lookup -rn NSObject\(IvarDescription\)
(lldb) po [[UIApplication sharedApplication] _ivarDescription]
(lldb) image lookup -rn '\[UIStatusBar\ set'
(lldb) po (BOOL)[[UIStatusBar class] isSubclassOfClass:[UIView class]]
(lldb) po [[UIApplication sharedApplication] statusBar]
<UIStatusBar_Modern: 0x7fdcf3c0f090; frame = (0 0; 375 44); autoresize = W+BM; layer = <CALayer: 0x60c000036640>>
(lldb) po [0x7fdcf3c0f090 setBackgroundColor:[UIColor purpleColor]]
(lldb) breakpoint delete

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.

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 reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a Kodeco Personal Plan.

Unlock now