Source:http://initwithfunk.com/blog/2013/05/31/breaking-bad-with-dtrace/

I’ve spent an unwise amount of time recently on a problem that arose when me and my Code Schoolcolleagues were developing the challenges for our upcoming course on MapKit. All of the challenges assume that the user has given the app permission to use it’s current location, and our KIF tests in the simulator wouldn’t work unless we could somehow programmatically confirm this alert box:

KIF tests run in the simulator and are built to a different target, so using private API’s isn’t a problem (KIF wouldn’t work unless it used a ton of private API’s). Jim Puls from Square (the makers of KIF) was helpful enough to chime in this thread to share how they do it at Square: by method swizzling the crap out of theCLLocationManager class, replacing startUpdatingLocationlocation, and locationServicesEnabled with their own versions so the user location mechanics would be wholesale replaced, thus getting around the permission alert box.

We followed their advice and ended up mocking out CLLocationManager (see how in this gist) and it worked well enough, but I was unsatisfied that we had to mock out the entire location machinery just to get rid of that permissions alert box. It wasn’t elegant code.

…perfection is finally attained not when there is no longer anything to add, but when there is no longer anything to take away…

Antoine de Saint-Exupéry english.stackexchange.com/38837/…

I decided to keep searching for a more elegant solution and I started at what I thought was the most obvious place: UIAlertView.

Searching for UIAlertView

If I could find the code where the offending UIAlertView is actually created then I would know which code to mock out and hopefully bypass permissions. To do this, I could have swizzled some UIAlertView init methods or added a symbolic breakpoint in Xcode, but I had recently come across Mark Dalymple’sexcellent three part series on DTrace (12, and 3), and this seemed like a good opportunity to use it for a worthy real-world problem.

I created a new blank iOS 6.0 project with a single MKMapView that wants to show the current user location (you can ⬇download the project here). I built and ran the app for the simulator to verify that it brought up the alert box and to create a binary that I would later use in Instruments as a target for a custom DTrace instrument.

Instruments is powered by DTrace and by using it it made a couple of things easier, like launching an app in the simulator as it attached DTrace probes and logged the results, as well as a cleaner interface for viewing call stack information. To create custom DTrace probes in Instruments, start by opening Instruments from Xcode:

And then create a new blank document:

Use ⌘B to build a new instrument, and you should come across this screen:

Which presents a nice interface for creating custom DTrace scripts. To find all methods (class or instance) called on UIAlertView, I ended up with this:

The part to pay attention to is this group of boxes here:

Next up I needed to the choose the target this instrument would run against

You have to pick the executable that was built for the simulator, which you can find in ~/Library/Application\ Support/iPhone\ Simulator/6.0/Applications/. In there you might see many directories with randomly generated names:

You’ll have to dig through the noise to find the executable you want:

Now all that’s left is to hit the record button and hope we can find out where that UIAlertView is being initialized:

As you can see, there were 3 UIAlertView method calls that all came from [UIWindow keyWindow], and one from a performSelector, but that doesn’t help us much. Let’s update that probe to record both the module (in this case the class name), and the name of the method

Now it’s a little easier to see what’s going on. It looks like [UIWindow keyWindow] calls private class method+_alertWindow on UIAlertView.

Could this be where our location permissions alert box is being created. Fortunately, our instrument’s probe automatically records the stack trace information for each of the events recorded. To view the stack trace information, we just need to select an event and open the right hand side drawer:

The stack trace of the _alertWindow calls are all the same, and lead to a dead end. They don’t seem to be related to location at all but instead are triggered on normal app startup routines. The only other hit we got,+_initializeSafeCategory, is related to Accessibility and not location.

This is a dead end.

Well, not exactly. Using DTrace I was able to eliminate the possibility that the UIAlertView was created inside the TestingCurrentLocation process, which means it has to be created in another process. So I knew that somehow, my TestingCurrentLocation process was communicating with another outside process which was responsible for location permissions. Apple doesn’t want to give app developers the chance of getting around the location permissions box for privacy reasons, so they would want to lock it down in another process.

The Mystery Process

So my next goal was to figure out what this mystery outside process was. I knew that my app process and the mystery process had to communicate, which led me to research the XPC Services API which “provides a lightweight mechanism for basic interprocess communication integrated with Grand Central Dispatch (GCD) and launchd” and is an integral part of App Sandboxing, so it seemed like an interested place to start. I tried logging all methods called on parts of the Objective-C API, like NSXPCConnection, andNSXPCInterface, but there was nothing there. The next most obvious thing to probe was the c API, specifically the xpc_connection_create function. Here is how I created the probe I to log all the calls toxpc_connection_create with the caller and the name of the service with which to connect:

As you can see, probing this function turned out to bear some real fruit. It found a call toxpc_connection_create that was creating a connection to something named com.apple.locationd.registrationand the call stack confirmed that this being triggered by setting the map to display the users location:

Running sudo ps aux | grep locationd showed that their was indeed a locationd process running in the simulator located at/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator6.0.sdk/usr/libexec/locationd(there was also one running at /usr/libexec/locationd which is the location daemon for Mac OS X.)

locationd

This gave me a new process to probe for information. But what should I look for? I knew that if a user allowed an app in the simulator to get their location, they wouldn’t be asked again even if the simulator was restarted (only by resetting the simulator would the location permissions alert box come up again). So I knew that there had to be somewhere on the filesystem where locationd was storing the permission authorizations for simulator apps. If I could find this file (most likely a preferences file), I could possibly inject authorization for an app before it ever ran, thus never displaying the alert box even on the apps first run in the simulator.

So how do I find what files the locationd process is opening and reading and writing to? Well, that’s where the syscall DTrace provider comes in, which allows you to probe all system calls (like openread,writechdir, etc.). This provider makes it really easy to find out which files a process is opening and writing to by probing the open and write functions. For example, here is a DTrace script for printing out all the files opened by the locationd process:

locationopens.d

1
2
3
4
5
syscall::open*:entry
/execname == "locationd"/
{
printf("locationd open %s\n", copyinstr(arg0));
}

This script will trace all system calls to open* (* is a wildcard), but only on processes with the name “locationd” (the /execname == "locationd"/ line defines a predicate for the probe). The code between the curly-braces is the action to perform on each match. Inside the curly-braces we are printing the first argument to open, which is the path of the file (to find out which arguments a system call takes, use man 2 syscall which in this case would be man 2 open). We have to use copyinstr to copy data from the process into the kernel’s address space (where DTrace runs).

Running this with dtrace -s locationopens.d (make sure you have root priviledges: run sudo -i to start a new shell in root) and then running our TestingCurrentLocation app in the simulator results in a couple hundred lines that look like this:

1
2
3
4
5
6
7
8
9
10
11
12
  0    927              open_nocancel:entry locationd open /System/Library/CoreServices/SystemVersion.bundle
0 141 open:entry locationd open /Users/eric/Library/Application Support/iPhone Simulator/6.0/Library/Preferences/.GlobalPreferences.plist
0 927 open_nocancel:entry locationd open /System/Library/CoreServices/SystemVersion.bundle/English.lproj
0 927 open_nocancel:entry locationd open /System/Library/CoreServices/SystemVersion.bundle/Base.lproj
0 141 open:entry locationd open /System/Library/CoreServices/SystemVersion.bundle/English.lproj/SystemVersion.strings
0 141 open:entry locationd open /System/Library/CoreServices/SystemVersion.bundle/English.lproj/SystemVersion.stringsdict

We can get rid of some of the noise by running dtrace with the -q option, like so:

1
2
3
4
5
6
7
8
9
$ dtrace -q -s locationopens.d
...snip a bunch of lines...
locationd open /Users/eric/Library/Application Support/iPhone Simulator/6.0/Library/Preferences/com.apple.locationd.plist
locationd open /System/Library/CoreServices/ServerVersion.plist
locationd open /System/Library/CoreServices/SystemVersion.plist
locationd open /System/Library/CoreServices/SystemVersion.bundle
locationd open /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator6.0.sdk/usr/libexec
locationd open /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator6.0.sdk/usr/libexec/locationd
...snip a bunch of lines...

Okay that’s a lot of file opens, and it would take awhile going through every single one looking for clues. What I really want to know is: what files does locationd write to when a user taps the “OK” button in the location alert box, and what data does it write? For that we are going to need another DTrace script:

locationwrites.d

1
2
3
4
5
syscall::write*:entry
/execname == "locationd"/
{
printf("locationd write %s\n\n%s\n\n", fds[arg0].fi_pathname, copyinstr(arg1));
}

Here we are tracing all calls to write* and printing out the path name using the first argument (arg0) which is a file descriptor (for more on using file descriptors in DTrace, read this tutorial). We are also printing out the second argument (arg1) which is a buffer of the contents written to the file. Now if we run the script just before we confirm we want to allow this app to use our current location, we will know where locationd is writing to save this information.

This results in just one trace match writing to a file at ??/locationd/clients.plist. If we run the locationopens.dscript again while grepping for clients.plist we can find the full path to this file:

1
2
$ dtrace -q -s locationopens.d | grep 'clients.plist'
locationd open /Users/eric/Library/Application Support/iPhone Simulator/6.0/Library/Caches/locationd/clients.plist

The solution

When I opened this file for the first time I had to find someone in the office to high-five. Here it was, the file that tells locationd, and thus apps in the simulator, whether or not the user has permitted use of location information. I could convert the clients.plist file into xml using plutil -convert xml1 clients.plist and then open it up and see what was inside:

clients.plist

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.codeschool.TestingCurrentLocation</key>
<dict>
<key>Authorized</key>
<true/>
<key>BundleId</key>
<string>com.codeschool.TestingCurrentLocation</string>
<key>Executable</key>
<string>/Users/eric/Library/Application Support/iPhone Simulator/6.0/Applications/D2700670-4F2D-4A4B-A881-695E9812CB86/TestingCurrentLocation.app/TestingCurrentLocation</string>
<key>LocationTimeStopped</key>
<real>391883613.42615497</real>
<key>Registered</key>
<string>/Users/eric/Library/Application Support/iPhone Simulator/6.0/Applications/D2700670-4F2D-4A4B-A881-695E9812CB86/TestingCurrentLocation.app/TestingCurrentLocation</string>
<key>Whitelisted</key>
<false/>
</dict>
</dict>
</plist>

Property List’s (.plist) are a strange format but not impossible to read. The real important bit seemed to be the Authorized key which was set to <true/>. I tried changing that to <false/> and rerunning the app in the simulator and my app no longer had access to location information and also didn’t bring up the location permission alert box. If I removed the entire <dict></dict> block representing my app, the location permission alert box would show back up the next time I ran the app in the simulator.

I now had full control over that location alert box. All I had to do was write over the clients.plist file with the xml already crafted in a way to give my app access to location information. For our upcoming MapKit course, we’re doing it inside our executor server (which is written in ruby), kind of like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
def write_out_location_plist_hack!
plist = %{
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.codeschool.#{@project.project_name}</key>
<dict>
<key>Authorized</key>
<true/>
<key>BundleId</key>
<string>com.codeschool.#{@project.project_name}</string>
</dict>
</dict>
</plist>
}.strip
clients_plist = File.join(
File.expand_path("~"),
"Library",
"Application Support",
"iPhone Simulator",
"6.0",
"Library",
"Caches",
"locationd",
"clients.plist"
)
File.open(clients_plist, "w+") do |file|
file.puts plist
end
end

The code above runs before the app is launched in the simulator, and it works perfectly. We were able to get rid of the nasty CLLocationManager mocking and I was finally able to move on to something more productive.

But trying to solve this problem did lead me to learn a lot more about DTrace. Before, DTrace just seemed like this magical thing used by superhero programmers. Now that I’ve used it so solve a real problem, it’s not so magical anymore, and I am already starting to think of other problems I could solve using it that I wouldn’t have been able to before. I hope that by writing about how I used DTrace to solve this problem, it will lead you to try it out the next time you are stuck on something similar. If you do, please let me know how you did by getting in touch with me on my twitter.

[转]Breaking Bad With DTrace的更多相关文章

  1. How can I protect derived classes from breaking when I change the internal parts of the base class?

    How can I protect derived classes from breaking when I change the internal parts of the base class? ...

  2. FreeBSD打开DTrace支持

    主要翻译自:https://wiki.freebsd.org/DTrace FreeBSD跟Linux发行版一个比较大的差异,就是提倡源码构建.因此这里提到比较多的编译开关设置.自2012年5月后,D ...

  3. 在Oracle Linux上使用DTrace的相关指导

    如果你使用的Oracle Linux,因为sun被Oracle收购后,Oracle Linux版本的DTrace可以直接在Oracle官网进行下载. 下载地址 http://www.oracle.co ...

  4. Adding DTrace Probes to PHP Extensions

      By cj on Dec 06, 2012 The powerful DTrace tracing facility has some PHP-specific probes that can b ...

  5. 动态追踪技术(中) - Dtrace、SystemTap、火焰图

    http://openresty.org/cn/presentations.html http://weibo.com/agentzh?is_all=1 http://openresty.org/po ...

  6. DTrace patch for Python 2.7.x and 3.x

    DTrace patch for Python 2.7.x and 3.x Última Actualización: 21 de septiembre de 2015 https://www.jce ...

  7. python 2016 大会 pyconsk ppt ---python dtrace

    https://github.com/pyconsk/2016-slides PyCon SK 2016 - March 2016 1DTrace and PythonJesús Cea Aviónj ...

  8. DTrace Probes in HotSpot VM----java

    http://docs.oracle.com/javase/6/docs/technotes/guides/vm/dtrace.html http://docs.oracle.com/javase/7 ...

  9. DTrace to Troubleshoot Java Native Memory Problems

    How to Use DTrace to Troubleshoot Java Native Memory Problems on Oracle Solaris 11 Hands-On Labs of ...

随机推荐

  1. background-position 具体的使用说明

    语法: background-position : length || length background-position : position || position 值: length  : ...

  2. 如何为ios酷我音乐盒下载导出的音乐文件(使用Java程序设计)

    这个工具已经准备第二版,读者了解编程软件,可以直接使用,请阅读和使用这个场地 http://blog.csdn.net/jzj1993/article/details/44459983 本文所涉及内容 ...

  3. Activity的LaunchMode情景思考

    此链接:http://blog.csdn.net/xiaodongrush/article/details/28597855 1. 有哪几种类型?分别有什么用? http://developer.an ...

  4. JBPM——MyEclipse开发环境的搭建

    第一次接触JBPM我不知道如何在工程中的应用.查了一些资料.大约在JBPM随着时代的发展有一定的了解.首先JBPM它是JBoss件平台的一个组成部分.是一个灵活的,易扩展的工作流管理系统,仅仅只是这个 ...

  5. Tomcat剖析(三):连接器(1)

    Tomcat剖析(三):连接器(1) 1. Tomcat剖析(一):一个简单的Web服务器 2. Tomcat剖析(二):一个简单的Servlet服务器 3. Tomcat剖析(三):连接器(1) 4 ...

  6. 【百度地图API】手机浏览器抓包工具及其使用方法

    原文:[百度地图API]手机浏览器抓包工具及其使用方法 摘要:为了测试地图API在手机浏览器上的性能,需要给手机浏览器设置代理.通过代理,我们可以在PC上获取到抓包数据.进而对性能做进一步分析. -- ...

  7. Msgbox消息框

    于"自"<软件开发工具>一本书有这样的制剂,让我回忆--程序员可以做,让用户做:它允许用户做的少,即使是那些谁需要做的程序设置.我们应该做的. 这不是宣言.该是践行. ...

  8. my97 日期控件

    官网:http://www.my97.net/   好多广告啊! 文档地址: http://www.mysuc.com/test/My97DatePicker/

  9. MVC 验证码

    验证码类 public class VerifyCode { public string checkCode = String.Empty; public byte[] BuildImg() { in ...

  10. c# 数据类型转换 as(C# 参考)

    as    运算符类似于强制转换操作.               但是,因此,如果转换是不可能的,as 返回 null 而不引发异常.  请看下面的示例: expression is type ? ...