Home iOS & Swift Books Advanced Apple Debugging & Reverse Engineering

10
Regex Commands Written by Derek Selander

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

You can unlock the rest of this book, and our entire catalogue of books and videos, with a raywenderlich.com Professional subscription.

In the previous chapter, you learned about the command alias command as well as how to persist commands through the lldbinit file. Unfortunately, command alias has some limitations.

An alias created this way will work great if you’re trying to execute a static command, but usually you’d want to feed input into a command in order to get some useful output.

Where command alias falls short is it essentially replaces the alias with the actual command. What if you wanted to have input supplied into the middle of a command, such as a command to get the class of a given object instance, providing the object as an input?

Fortunately, there is an elegant solution to supplying input into a custom LLDB command using a command regex.

command regex

The LLDB command command regex acts much like command alias, except you can provide a regular expression for input which will be parsed and applied to the action part of the command.

command regex takes an input syntax that looks similar to the following:

s/<regex>/<subst>/

This is a normal regular expression. It starts with ’s/’, which specifies a stream editor input to use the substitute command. The <regex> part is the bit that specifies what should be replaced. The <subst> part says what to replace it with.

Note: This syntax is derived from the sed Terminal command. This is important to know, because if you’re experimenting using advanced patterns, you can check the man pages of sed to see what’s possible within the substitute formatting syntax.

Time to look at a concrete example. Open up the Signals Xcode project. Build and run, then pause the application in the debugger. Once the LLDB console is up and ready to receive input, enter the following command in LLDB:

(lldb) command regex rlook 's/(.+)/image lookup -rn %1/'

This command you’ve entered will make your image regex searches much easier. You’ve created a new command called rlook. This new command takes everything after the rlook and prefixes it with image lookup -rn . It does this through a regex with a single matcher (the parentheses) which matches on one or more characters, and replaces the whole thing with image lookup -rn %1. The %1 specifies the contents of the matcher.

So, for example, if you enter this:

rlook FOO

LLDB will actually execute the following:

image lookup -rn FOO

Now, instead of having to type the soul-crushingly long image lookup -rn, you can just type rlook!

But wait, it gets better. Provided there are no conflicts with the characters rl, you can simply use that instead. You can specify any command, be it built-in or your own, by using any prefix which is not shared with another command.

This means you can easily search for methods like viewDidLoad using a much more convenient amount of typing. Try it out now:

(lldb) rl viewDidLoad 

This will produce all the viewDidLoad implementations across all modules in the current executable. Try limiting it to only code in the Signals app:

(lldb) rl viewDidLoad Signals

Now that you’re satisfied with the command, add the following line of code to your ~/.lldbinit file:

command regex rlook 's/(.+)/image lookup -rn %1/'

Note: The best way to implement a regex command is to use LLDB while a program is running. This lets you iterate on the command regex (by redeclaring it if you’re not happy with it) and test it out without having to relaunch LLDB.

Once you’re happy with the command, add it to your ~/.lldbinit file so it will be available every time LLDB starts up. Now the rlook command will be available to you from here on out, resulting in no more painful typing of the full image lookup -rn command. Yay!

Executing complex logic

Time to take the command regex up a level. You can actually use this command to execute multiple commands for a single alias. While LLDB is still paused, implement this new command:

(lldb) command regex -- tv 's/(.+)/expression -l objc -O -- @import QuartzCore; [%1 setHidden:!(BOOL)[%1 isHidden]]; (void)[CATransaction flush];/'
(lldb) tv [[[UIApp keyWindow] rootViewController] view]

command regex -- tv 's/(.+)/expression -l objc -O -- @import QuartzCore; [%1 setHidden:!(BOOL)[%1 isHidden]]; (void)[CATransaction flush];/'

Chaining regex inputs

There’s a reason why that weird stream editor input was chosen for using this command: this format lets you easily specify multiple actions for the same command. When given multiple commands, the regex will try to match each input. If the input matches, that particular <subst> is applied to the command. If the input doesn’t match for a particular stream, it’ll go to the next command and see if the regex can match that input.

(lldb) command regex getcls 's/(([0-9]|\$|\@|\[).*)/cpo [%1 class]/'
(lldb) getcls @"hello world"
__NSCFString

(lldb) getcls @[@"hello world"]
__NSSingleObjectArrayI

(lldb) getcls [UIDevice currentDevice]
UIDevice

(lldb) cpo [UIDevice currentDevice]
<UIDevice: 0x60800002b520>

(lldb) getcls 0x60800002b520
UIDevice
(lldb) getcls self
error: Command contents 'self' failed to match any regular expression in the 'getcls' regex command.
(lldb) command regex getcls 's/(([0-9]|\$|\@|\[).*)/cpo [%1 class]/' 's/(.+)/expression -l swift -O -- type(of: %1)/'
(lldb) getcls self
(lldb) getcls self .title 

Supplying multiple parameters

The final party trick you’ll explore in command regex is supplying multiple parameters to command regex. However, before you do that, revisit the first command:

(lldb) command regex rlook 's/(.+)/image lookup -rn %1/'
(lldb) ex -l objc -O -- [[UIApplication shared] statusBar]
(lldb) ex -l swift -O -- UIApplication.shared.perform(NSSelectorFromString("statusBar"))
(lldb) command regex swiftperfsel 's/(.+)\s+(\w+)/expression -l swift -O -- %1.perform(NSSelectorFromString("%2"))/'
(lldb) swiftperfsel UIApplication.shared statusBar
▿ Optional<Unmanaged<AnyObject>>
  ▿ some : Unmanaged<AnyObject>
    - _value : <UIStatusBar_Modern: 0x7fa86dc05820; frame = (0 0; 375 44); autoresize = W+BM; layer = <CALayer: 0x6000018e5000>>

Where to go from here?

Go back to the regex commands you’ve created in this chapter and add syntax and help help documentation.

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.

Have feedback to share about the online reading experience? If you have feedback about the UI, UX, highlighting, or other features of our online readers, you can send them to the design team with the form below:

© 2021 Razeware LLC

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 raywenderlich.com Professional subscription.

Unlock Now

To highlight or take notes, you’ll need to own this book in a subscription or purchased by itself.