今天想通过HierarchyViewer分析一下Android应用的布局,但是发现无法连接上真机,错误如下:
1 2 3 4 5 |
[hierarchyviewer]Unable to get view server version from device 00856cd5d08d2409 [hierarchyviewer]Unable to get view server protocol version from device 00856cd5d08d2409 [ViewServerDevice]Unable to debug device: lge-nexus_4-00856cd5d08d2409 [hierarchyviewer]Missing forwarded port for 00856cd5d08d2409 [hierarchyviewer]Unable to get the focused window from device 00856cd5d08d2409 |
原理
Android系统出于安全考虑,Hierarchy Viewer只能连接开发版手机或模拟器,我们普通的商业手机是无法连上的(老版本的Hierarchy Viewer可以),这一限制在frameworks/base/services/java/com/android/server/wm/WindowManageService.java
Java
1 2 3 4 5 6 7 8 9 10 |
public boolean startViewServer(int port) { if (isSystemSecure()) { return false; } if (!checkCallingPermission(Manifest.permission.DUMP, "startViewServer")) { return false; } //…… } |
我们要做的就是,修改并替换掉这个文件,使其通过判断。
检验一台手机是否开启了View Server的办法为:
Shell
1 |
adb shell service call window 3 |
若返回值是:Result: Parcel(00000000 00000000 ‘........‘)
说明View Server处于关闭状态
若返回值是:Result: Parcel(00000000 00000001 ‘........‘)
说明View Server处于开启状态
若是一台可以打开View Server的手机(Android开发版手机 、模拟器or 按照本帖步骤给系统打补丁的手机),我们可以使用以下命令打开View Server:adb shell service call window 1 i32 4939
使用以下命令关闭View Server:adb shell service call window 2 i32 4939
听说小米手机可以直接打开,如果你的是小米手机,可以试一下。
下面开始是解决方案,使用本方法的前提是:
- 手机已root
- 手机安装了BusyBox(没有的去装一个)
1.拷贝数据
约定当前使用的工作目录是/home/feelyou/hierarchyviewer
。
打开终端切换到工作目录,新建文件夹存放数据。通过usb连接上手机,执行:
Shell
1 2 3 |
mkdir ./system mkdir ./system/framework adb pull /system/framework/ ./system/framework/ |
2.获取bootclasspath
Shell
1 2 3 4 |
adb shell echo $BOOTCLASSPATH #将输出的内容复制出来,随意保存到一个文本文件里,后面要用到。 exit |
3.反编译odex文件
这里要下载2个小工具,官方地址是https://bitbucket.org/JesusFreke/smali/downloads,下载最新版的smali-xxx.jar和baksmali-xxx.jar,比如我这里下载的是smali-2.0.3.jar和baksmali-2.0.3.jar,将这两个文件下载到工作目录。
然后在终端执行:
Shell
1 |
java -jar baksmali-2.0.3.jar -a 19 -x ./system/framework/services.odex -d ./system/framework/ |
注意,-a 后面的参数19,是你的手机当前的版本API Level,不知道的自己查一下。我的Nexus 4 是4.2.2,所以是19。执行成功了之后,在当前目录会有个out文件夹。
4.修改smail文件
使用文本编辑器打开out/com/android/server/wm/WindowManagerService.smali
文件,搜索isSystemSecure(),第一个找到的目标,应该就是我们要的,这段代码如下(不用细看,我写这么多只是为了让你找到这个方法):
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 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
.method private isSystemSecure()Z .registers 4 .prologue .line 6164 const-string v0, "1" const-string v1, "ro.secure" const-string v2, "1" invoke-static {v1, v2}, Landroid/os/SystemProperties;->get(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; move-result-object v1 invoke-virtual {v0, v1}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z move-result v0 if-eqz v0, :cond_22 const-string v0, "0" const-string v1, "ro.debuggable" const-string v2, "0" invoke-static {v1, v2}, Landroid/os/SystemProperties;->get(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; move-result-object v1 invoke-virtual {v0, v1}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z move-result v0 if-eqz v0, :cond_22 const/4 v0, 0x1 :goto_21 return v0 :cond_22 const/4 v0, 0x0 goto :goto_21 .end method |
这里注意,我们要在第41~42行之间,也就是:goto_21
和return v0
之间加入const/4 v0, 0x0
,使他变成
1 2 3 4 5 6 7 8 9 |
:goto_21 const/4 v0, 0x0 return v0 :cond_22 const/4 v0, 0x0 goto :goto_21 .end method |
保存。
5.重新编译成dex文件
将out文件夹的内容编译并压缩。然后我们会得到一个叫做feelyou_services_hacked.jar的文件,后面要用到。
Shell
1 2 |
java -jar smali-2.0.3.jar ./out -o classes.dex zip feelyou_services_hacked.jar ./classes.dex |
6.获取/system挂载信息
这一步我们要获取/system挂载信息,并获取写入权限,因为后面要复制东西进来
Shell
1 2 3 |
adb shell su mount |
然后出来一堆东西,查找一下哪个分区挂载了/system,例如我的是/dev/block/platform/msm_sdcc.1/by-name/system
:
接着,输入以下命令重新挂载/system,并更改/system权限(请将/dev/block/platform/msm_sdcc.1/by-name/system
替换成你的/system挂载分区):
Shell
1 2 |
mount -o rw,remount -t yaffs2 /dev/block/platform/msm_sdcc.1/by-name/system chmod -R 777 /system |
这样我们就可以修改/system的内容了。
7.复制所需文件到手机
首先需要下载dexopt-wrapper,连接为https://dl.dropboxusercontent.com/u/5055823/dexopt-wrapper(英文原文章的连接已经失效),下载后依然放到当前工作目录。
将feelyou_services_hacked.jar和dexopt-wrapper复制到手机的/data/local/tmp文件夹中
Shell
1 2 |
adb push ./feelyou_services_hacked.jar /data/local/tmp adb push ./dexopt-wrapper /data/local/tmp |
给dexopt-wrapper运行权限
Shell
1 2 3 |
adb shell su chmod 777 /data/local/tmp/dexopt-wrapper |
8.生成odex文件
注意!关键步骤!在adb shell中cd到/data/local/tmp文件夹下,运行:
Shell
1 |
./dexopt-wrapper ./feelyou_services_hacked.jar ./feelyou_services_hacked.odex [这里替换成之前获取到的BOOTCLASSPATH路径,但是注意!删除其中的":/system/framework/services.jar",当然,不包括中括号] |
比如最后我的是这样:
Shell
1 |
./dexopt-wrapper ./feelyou_services_hacked.jar ./feelyou_services_hacked.odex /system/framework/core.jar:/system/framework/conscrypt.jar:/system/framework/okhttp.jar:/system/framework/core-junit.jar:/system/framework/bouncycastle.jar:/system/framework/ext.jar:/system/framework/framework.jar:/system/framework/framework2.jar:/system/framework/telephony-common.jar:/system/framework/voip-common.jar:/system/framework/mms-common.jar:/system/framework/android.policy.jar:/system/framework/apache-xml.jar:/system/framework/webviewchromium.jar |
这样就生成了一个feelyou_services_hacked.odex文件,等下我们要用它来替换系统原有的odex文件。
执行完了是这样的显示:
9.给生成的odex文件签名
还是在adb shell,su,执行:
Shell
1 |
busybox dd if=/system/framework/services.odex of=/data/local/tmp/feelyou_services_hacked.odex bs=1 count=20 skip=52 seek=52 conv=notrunc |
10.替换系统odex
最后一步,将/system/framework里的services.odex替换成我们自己制作的feelyou_services_hacked.odex。
Shell
1 |
dd if=/data/local/tmp/feelyou_services_hacked.odex of=/system/framework/services.odex |
替换完成后手机会立刻重启。如果执行这一步,这个时候提示是只读,说明/system没有获取到写入权限,请重复第6步。
11.打开服务
成功重启后,用以下命令打开View Server:adb shell service call window 1 i32 4939
用以下命令查看View Server是否打开:adb shell service call window 3
返回的值若是Result: Parcel(00000000 00000001 ‘........‘)
,那就搞定了!