您的位置:首页 > 其它

Unable to get view server protocol version from device

2013-10-14 20:05 645 查看
The HierarchyViewer is
an Android SDK tool that gives developers the ability to introspect on all aspects of an application’s layout at runtime. The tool can be extremely useful for developers when debugging the view state of an application across a realm of devices. Unfortunately,
the Android SDK has limited this tool to “devices
running a developer version of the Android system”. I personally had no idea what Android meant by this statement, so we spent some time investigating the implementation of the ViewServer in Android OS and fortunately found that the HierarchyViewer
can be enabled with any Android device as long as it has root access.





Android HierarchyViewer in Tree Mode on the Galaxy SII

So how does it actually work? The HierarchyViewer utilizes a service running on the device called ViewServer located in
frameworks/base/services/java/com/android/server/ViewServer.java


When launched, ViewServer opens up a socket on local port 4939 and receives commands from a client (usually HierarchyViewer) to dump the current view state of the device. The ViewServer dispatches these calls via binder to the ViewRoot class, which serializes
the view state and transmits it to the client over the socket. The WindowManagerService manages the ViewServer and provides a hook (via binder service call) to spawn the service.

A client can only launch the ViewServer if the device’s properties ro.debuggable=1 and ro.secure=0, and the client contains manifest.dump permission (in the usual case, HierarchyView, as the client, has these permissions via adb). Although Google Engineer Romain
Guy has been very adamant that the ViewServer is not enabled for production devices, we’ve found a pretty easy way to bypass its restrictions if you have root. The portion of the code dictating the restrictions can be found in line
5501 ofWindowManagerService.java and
is shown below.

 

One can go about bypassing these restrictions in two ways: patch the WindowManagerService to allow anyone to spawn the server _or_ modify the ro.debuggable and ro.secure properties in the default.prop file. Because of the potential negative ramifications of
modifying the ro.debuggable and ro.secure properties in Android (not to mention the fact that the default.prop file is part of the ramdisk, which makes editing it a bitch), we decided to patch the WindowManagerService. The patch is minimally invasive and provides
a good intro into the power of patching the Android framework.

In order to perform this patch you will need the smali/baksmali tools,dextopt-wrapper,
and access to a rooted device with BusyBoxinstalled.
If you have no idea what these tools are or how they work, you may want to do
some reading first. We’ve only tested this on the HTC One S and a 4.0.3 Galaxy S II, so if you brick your device we’ll feel bad but can’t really do anything about it (hey, maybe we’ll send you a t-shirt).

Step 1 – Backup the phone’s /system/framework/ on your machine
$ mkdir ./system
$ mkdir ./system/framework
$ adb pull /system/framework/ ./system/framework/


Important: Make sure the directory structure matches the device’s for step 3.

Step 2 – Grab the bootclasspath from the device
$ adb shell
# echo $BOOTCLASSPATH


Copy this path.

Step 3 – Disassemble (baksmali) the services odex
$ baksmali -x -a 14 -c <copied bootclasspath> ./system/framework/services.odex


-x = odex

-a = api level 14

-c = classes (loaded from the bootclasspath, separated by colon)

If you’ve done this correctly you will now see a directory called ‘out’,otherwise
verify you’ve pulled the jars and bootclasspath correctly.

Step 4 – In your favorite text editor, open up WindowManagerService.smali
$ emacs out/com/android/server/wm/WindowManagerService.smali


Search for isSystemSecure(). This is the method they use (only for ViewServer) to verify the system is not secure. We happened to find this
on line 4815. Note: In 4.0 they moved WindowManagerService into the wm directory, it may just be in out/com/android/server/

Step 5 – Patch the return value of isSystemSecure() to always return false

We can see from above that the v0 register contains the return value of this method. Let’s add a simple
const/4 v0, 0x0


in the line 41 above
return v0 to ensure this method will always return false, giving the WMS the go ahead to spawn view server. Easy enough, right?

Step 6 – Reassemble into Dex
$ smali ./out -o classes.dex


-o = output file (classes.dex is the default name for apks in Android)

Step 7 – Zip dex
$ zip services_hacked.jar ./classes.dex


Step 8 – Remount /system
$ adb remount


OR
$ adb shell
# mount -o remount,rw -t yaffs2 /dev/block/mtdblock3 /system


Step 9 – Push dexopt-wrapper and services_hacked.jar onto /data/local/tmp
$ adb push ./services_hacked.jar /data/local/tmp
$ adb push ./dexopt-wrapper /data/local/tmp


Step 10 – Optimize the services_hacked.jar into services_hacked.odex
$ adb shell
# cd /data/local/tmp
# chmod 777 ./dexopt-wrapper


Important: We need to feed in the bootclasspath into dexopt, but because we have modified a member of that class path, we need to make sure
we don’t include it. To do this, copy bootclasspath (via method in step 2) and omit the “:/system/framework/services.jar”. Forgetting this step will likely result in a boot loop.
# ./dexopt-wrapper ./services_hacked.jar ./services_hacked.odex <copied bootclasspath excluding ":/system/framework/services.jar">


Our final command looked something like
./dexopt-wrapper ./services_hacked.jar ./services_hacked.odex /system/framework/core.jar:/system/framework/core-junit.jar:/system/framework/bouncycastle.jar:/system/framework/ext.jar:/system/framework/framework.jar:/system/framework/framework2.jar:/system/framework/android.policy.jar:/system/framework/apache-xml.jar:/system/framework/filterfw.jar:/system/framework/sechardware.jar:/system/framework/wimax.jar


A great way to check that this command has worked is to see that the /system/framework/services.odex and the services_hacked.odex are nearly
identical in size. The two files shouldn’t differ by more than 1KB.

Step 11 – Copy in the signature

Each odex is signed with a 20 byte signature located at a 52 byte offset within the odex. Let’s trick the OS into thinking our hacked odex
matches the system signature by copying it from the original
# busybox dd if=/system/framework/services.odex of=/data/local/tmp/services_hacked.odex bs=1 count=20 skip=52 seek=52 conv=notrunc


if = input file

of = output file

bs = block size (1 byte)

count = number of blocks

skip = input file offset

seek = output file offset

conv=notrunc – don’t truncate the output file.

Step 12 – Replace the original odex with the hacked version
# dd if=/data/local/tmp/services_hacked.odex of=/system/framework/services.odex


Step 13 – Let the device reset

The previous step should result in a software reset, and it may be worthwhile to logcat as this loads. If you see the Dalvik logs stating “some
deps went away” or “not all deps represented” then you’ve definitely screwed up step 10. These errors are a result of passing in the wrong bootclasspath to dexopt-wrapper. If this causes a bootloop, see step 16.

Step 14 – Launch ViewServer

WindowManagerService provides a (heavily undocumented) hook into spawning ViewServer. To start the server on port 4939,
$ adb shell service call window 1 i32 4939


To stop the server
$ adb shell service call window 2


To check if the server is running
$ adb shell service call window 3


If ViewServer is running, you should see
Result: Parcel(00000000 00000001   '........')


Step 15 – Launch HierarchyViewer
$ ./sdk/tools/hierarchyviewer


You will now see your rooted phone available in the HierarchyViewer. Double click on a layout to introspect on that view tree.

Step 16 (if you get stuck in a bootloop)

Don’t panic! Simply push the backed up services.odex to system/framework to revert to your previous state.
$ adb push ./system/framework/services.odex /system/framework/services.odex


Note, you should make sure you don’t reboot when if you get a bootloop. In some cases the OS will not be at a state at which your SU binary
is functional (I’ve seen it segfault many times). If you don’t have root your only option is to reflash the device via fastboot or other bootloader.

Although this API is not meant to be public, it’s extremely useful to developers when debugging their UI. It’s a shame you have to jump through so many hoops to obtain this functionality. I hope to see Android keep the ViewServer in further builds and provide
a means of using the HierarchyViewer on production phones as well.

(As an aside: we refered to HierarchyViewer as the “usual client” of ViewServer above. Other clients are certainly possible. One issue with HierarchyViewer is it’s pretty damn slow – we’ve been working on our own optimized client over here at Apkudo, which
will be the subject of a future blog!)

Happy Hacking,

David Teitelbaum

VP of Engineering
@davtbaum
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: