How to Inspect the View Hierarchy of Third-Party Apps
I’m generally not a big fan of jailbreaks. Mostly this is because they’re used for piracy and all the hacks result in weird crashes that generally are impossible to reproduce. Still, I was quite excited about the recent iOS 7 jailbreak, since it enables us to attach the debugger to third-party apps and do a little bit of runtime analysis.
Why? Because it’s fun, and it can inspire you to solve things differently. Studying the view hierarchy of complex apps can be rather revealing and it’s interesting to see how others are solving similar problems. This was one thing that took many iterations to get right in PSPDFKit, our iOS PDF framework.
So, how does this work? It’s actually super simple.
Jailbreak your device of choice. I’ve used an iPad 4 here. Make sure it runs iOS 7.0.x. Both arm7(s) and arm64 devices will work now. Don’t jailbreak a device that’s used in production. Otherwise you lose a lot of security features as well. I only jailbreak a clean device and install some apps to inspect.
Open Cydia and install OpenSSH, nano, and Cydia Substrate (previously called MobileSubstrate).
Copy the Reveal library. Find out the device IP address via Settings and execute the following in your terminal (this assumes you have installed Reveal already):
scp -r /Applications/Reveal.app/Contents/SharedSupport/iOS-Libraries/Reveal.framework root@192.168.0.X:/System/Library/Frameworks
scp /Applications/Reveal.app/Contents/SharedSupport/iOS-Libraries/libReveal.dylib root@192.168.0.X:/Library/MobileSubstrate/DynamicLibraries
.
For Spark Inspector, you would usescp "/Applications/Spark Inspector.app/Contents/Resources/Frameworks/SparkInspector.dylib" root@192.168.0.X:/Library/MobileSubstrate/DynamicLibraries
Note: The default SSH password on iOS is ‘alpine.’SSH into the device and create following text file with
nano /Library/MobileSubstrate/DynamicLibraries/libReveal.plist
:{ Filter = { Bundles = ( "<App ID>" ); }; }
Previously, this worked with wildcard IDs, but this approach has problems with the updated Cydia Substrate. So simply add the App ID of the app you want to inspect, and then restart the app.Respring with
killall SpringBoard
or simply restart the device.
Done! Start your app of choice and select it in Reveal. (This should also work similary for SparkInspector.) Attaching via LLDB is a bit harder and I won’t go into details here since this could also be used to pirate apps. Google for ‘task_for_pid-allow,’ ‘debugserver,’ and ‘ldid’ if you want to try this.
Tweetbot. The action bar is simply part of the cell. And the “No Tweets Found” view is always below the UITableView. pic.twitter.com/Xp6OKlvaJh
— Peter Steinberger (@steipete) December 27, 2013
AppStore.app is super complex. SKUISoftwareSwooshCollectionViewCell. No wonder it took so long to become native. pic.twitter.com/hjFdImyqSP
— Peter Steinberger (@steipete) December 27, 2013
Chrome is basically a full-screen UIWebView with some controls on top. pic.twitter.com/CFfEM7j6vT
— Peter Steinberger (@steipete) December 27, 2013
Surprise. PS Express is all native, even the classes are well structured. “AdobeCleanBoldFontButton”. pic.twitter.com/YW1xMNPSP2
— Peter Steinberger (@steipete) December 27, 2013
Twitter. There’s still a small part of @lorenb in there. (ABCustomHitTestView, ABSubTabBar) (Also: T1 as namespace?) pic.twitter.com/R62JAY4DDQ
— Peter Steinberger (@steipete) December 27, 2013