http://feelyou.info/hierarchyviewer_connect_phone/#more-306
今天想通过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(没有的去装一个)
约定当前使用的工作目录是/home/feelyou/hierarchyviewer
。
打开终端切换到工作目录,新建文件夹存放数据。通过usb连接上手机,执行:
1
2
3
|
mkdir
.
/
system
mkdir
.
/
system
/
framework
adb
pull
/
system
/
framework
/
.
/
system
/
framework
/
|
1
2
3
4
|
adb
shell
echo
$BOOTCLASSPATH
#将输出的内容复制出来,随意保存到一个文本文件里,后面要用到。
exit
|
这里要下载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文件夹。
使用文本编辑器打开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
|
保存。
将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
|
这一步我们要获取/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的内容了。
首先需要下载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
|
注意!关键步骤!在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文件。
执行完了是这样的显示:
还是在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
|
最后一步,将/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步。
成功重启后,用以下命令打开View Server:
adb shell service call window 1 i32 4939
用以下命令查看View Server是否打开:
adb shell service call window 3
返回的值若是Result: Parcel(00000000 00000001 '........')
,那就搞定了!