Derek Selander Profile picture
Author of Advanced Apple Debugging & Reverse Engineering. Adds +12 to your LLDB skrillz

Oct 24, 2018, 25 tweets

In less than a week, v3 of the Advanced Apple Debugging book will be released! store.raywenderlich.com/products/advan…

Unfortunately, there's an annoying bug in Xcode 10's LLDB that crippled a couple chapters and prevented many LLDB scripts from executing. Since I should probably be a bit more social on the Tweeters during this time, here's a thread for the problem, find, and fix for this bug

Hopefully, this thread of debugging, reverse engineering, and extracting information out of a binary is a fun (hopefully?) read. If you're interested to learn more, the methodologies are discussed quite a bit in the book. So here we go....

Let's first discuss the problem:

In lldb-1000.11.37.1 (packaged with Xcode 10.0), LLDB will default to importing the MacOSK headers even if the target is an iOS Simulator or iOS app. This bug only appears (to my knowledge?) in a Terminal session. Let's attach LLDB to Safari

So far so good.... now execute a LLDB command to import the UIKit module

This fails because LLDB is looking in the wrong directory. Specifically it's looking at the path given by:

xcrun -sdk macosx -show-sdk-path

This is a minor inconvenience for most iOS devs who now can't call -[UIView frame] via LLDB, but it REALLY sucks for my dbg scripts

So the problem for me was I had a book that's coming out w/ a bunch of chapters that won't work due to this bug and a repo of scripts that are now broken with no specific time on when my radar would be addressed (I am just assuming it's Q1/Q2 2019 based upon past resolved radars)

So, how can I verify this is problem? I have no clue! The LLDB repo (available here github.com/llvm-mirror/ll…) is a BEAST. I have so much respect for the people who commit to that thing. When I am this clueless on where to start, I'll usually cast a wide net... using DTrace!

I first want to determine that LLDB is incorrectly referencing the MacOS SDK. To do that, there's a lovely script called opensnoop (originally authored by @brendangregg, also check out his DTrace book, it's amazing) that monitors the system calls of open* functions

This tweet compares Xcode 10's LLDB vs Xcode 9's LLDB:

sudo opensnoop -n lldb -t | grep "./Developer/Platforms" -A10

opensoop is told to monitor any processes named "lldb", print out the stack trace (-t), and only observe opens to the ./Developer dir

So we can definitely see Xcode 10.0's LLDB is opening the wrong SDK for some reason.

It's important to note that with Mojave, DTrace will only function if you disable SIP on your macOS.

Looking at the source code for LLDB, there's an enum that specifies the type of SDK to load called SDKType github.com/llvm-mirror/ll…
Since this is c++ code, we can use LLDB to set a breakpoint on a func that contains SDKType (Non stripped c++ functions will contain the param type)

You know it's going to be a "fun" day when you're using LLDB to debug LLDB

1. Sets up the lldb executable target
2. Set a regex dummy breakpoint on code that contains the phrase "SDKType" in the lldb process
3. Attach to an iOS simulator app (equivalent to "lldb -n MobileSMS")

LLDB has several ways to load code into the LLDB process

It can load Python code via the "command script" command (discussed extensively in the book) and can load executable (C/C++) code via the "plugin load" command. Perhaps we can augment existing code in LLDB

This means I am looking for a *SDKType* breakpoint that can get called later in the execution time since I need any injection code to be executed first. Maybe I could pull off a DYLD_INSERT_LIBRARIES var to augment some code, but I want this to be a bit more approachable...

Fortunately, check out what happens when I execute a "po @import UIKit" while that SDKType regex breakpoint enabled.

A breakpoint of interest gets tripped!

Look at the parameters passed into method: For this particular example, the RCX register will be the register holding the SDKType (assembly calling conventions are discussed in the book)

Cross referencing this value with the SDKType in the LLDB repo, we can see 0 is MacOSX

So what if we changed this value to 1 to get the expected iPhoneSimulator? Using LLDB, that's totally possible

Then continue the application. Hmm.... no errors now!

So that means if I can turn a 0 into a 1 in this method, all my headaches go away, my chapters will no longer be crippled, and my scripts will work

Let's get back to that breakpoint when executing that "po @import UIKit" method and check out how the SDKType parameter is resolved

After another setup execution of running LLDB to debug LLDB to debug the Simulator MobileSMS, that same breakpoint is tripped. Let's look at that stack trace again. See how "frame 0" has the SDKType param while "frame 1" has no parameter for the SDKType?

Inspecting the assembly on "frame 1" didn't produce anything (I'll spare you that dead end). Let's look at how this method is called

Jumping down to "frame 1", dump the disassembly leading up to calling the SDKType breakpoint function

Then navigating to where frame 0 is called

You see that instruction on 0x102ff0154? That is saying call the address at

*(long *)(rax + 0x150)

What does the rax register hold? It's c++ vtable for the class PlatformMacOSX. Offset 0x150 references a method AddClangCompilerOptions

Do you notice something fishy?

I sure didn't on my first pass! The reference to the stack trace and the *(long *)(rax + 0x150) are different methods! Let's inspect this new method

This sets RCX to 0 and does a tail call optimization so we don't see it in the stack trace! This is the problem method!

How can we resolve this? Well let's look at the memory in that RAX register...

It's both readable and writable! Meaning if we can find this address in memory, then we can overwrite it to a function we control!

This means that if we can find the desired function to go to, the problematic function to avoid, and the vtable for PlatformMacOSX, we can inject or own code to intercept this func. So without further ado here's github.com/DerekSelander/… which will let your override the SDK. Enjoy!

Share this Scrolly Tale with your friends.

A Scrolly Tale is a new way to read Twitter threads with a more visually immersive experience.
Discover more beautiful Scrolly Tales like this.

Keep scrolling