今天想通過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
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的辦法為:
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連接上手機,執行:
1
2
3
|
mkdir ./system
mkdir ./system/framework
adb pull /system/framework/ ./system/framework/
|
2.獲取bootclasspath
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,將這兩個文件下載到工作目錄。
然后在終端執行:
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的文件,后面要用到。
1
2
|
java -jar smali-2.0.3.jar ./out -o classes.dex
zip feelyou_services_hacked.jar ./classes.dex
|
6.獲取/system掛載信息
這一步我們要獲取/system掛載信息,並獲取寫入權限,因為后面要復制東西進來
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掛載分區):
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文件夾中
1
2
|
adb push ./feelyou_services_hacked.jar /data/local/tmp
adb push ./dexopt-wrapper /data/local/tmp
|
給dexopt-wrapper運行權限
1
2
3
|
adb shell
su
chmod 777 /data/local/tmp/dexopt-wrapper
|
8.生成odex文件
注意!關鍵步驟!在adb shell中cd到/data/local/tmp文件夾下,運行:
1
|
./dexopt-wrapper ./feelyou_services_hacked.jar ./feelyou_services_hacked.odex [這里替換成之前獲取到的BOOTCLASSPATH路徑,但是注意!刪除其中的":/system/framework/services.jar",當然,不包括中括號]
|
比如最后我的是這樣:
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,執行:
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。
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 '........')
,那就搞定了!