简介
Smali是Android虚拟机的反汇编语言,dex文件反编译之后就是Smali代码。通过分析Smali文件能够更加了解代码的执行流程。平时用java编写的代码,当编译后,实际的执行顺序并不是java的代码顺序,这就是java所谓的“指令重排序”,在多线程中尤其需要注意。所以,有些bug在java层逻辑完全没有问题,但是通过分析Smali文件,可能看到不一样的逻辑。
语法
打开Smali文件,看一下内容。可以直接在Android Studio中打开,而且有语法高亮。
下面只讲解Smali类,方法,字段定义,像执行指令请自行百度,不再累述。

对应的java代码

-
类主体部分属性
.class <访问权限> [修饰关键字] <类名> .super <父类名> .source <源文件名> (混淆的dex反编译出来,可能没有这个属性)
-
字段
用.field来声明字段,格式:-
静态字段
.file <访问权限> static [修饰关键字] <字段名>:<字段类型>
-
实例字段
.file <访问权限> [修饰关键字] <字段名>:<字段类型>
-
-
方法
-
直接方法
.method <访问权限> [修饰关键字] <方法原型> <.locals> (局部变量个数) <.parameter> (方法参数) <.prologue> (代码开始处) <.line> (该处指令在原代码中的行号) <代码体> .end method
-
虚方法
和直接方法类似,只是.method是.virtual methods
-
-
接口
# interfaces .implements <接口名>
-
注解
#annotations .annotation [注解属性] <注解类名> [注解字段=值] .end annotation
smali动态调试
参考:smali
其实网上对于smali的动态调试已有介绍,但是当自己动手尝试的时候,还是会出现这样那样的问题,网上只是给你提供一个思路,而自己亲自尝试才是掌握真正的技术。
-
smalidea插件
要进行smali动态调试需要在Android Studio安装smalidea插件,不然根本无法在smali文件打断点和进行调试。插件地址 smalidea,里面包含了很多jar包,请自己仔细查找smalidea。
截屏2020-02-07下午10.27.24.png
安装就不多说了。 -
得到smali文件
使用apktool d testapp.apk命令反编译apk
截屏2020-02-07下午10.39.47.png
-
导入smali
Android Studio导入:File -> New -> import project...选择需要调试代码所在的smali文件夹,比如上图的smali文件夹,这个是主dex反编译生成的smali文件。
截屏2020-02-07下午10.43.42.png
如果安装好了smalidea插件,是可以在左边打断点的,否则不能。
-
动态调试
Android Studio安装在电脑上,而应用安装在手机上,使用Android Studio进行debug过的,应该知道,需要将电脑和手机建立通信链接,说白就是使用socket来进行电脑和手机直接的数据交换。- 以调试模式启动app
adb shell am start -D -n 包名/启动类全路径名
eg:adb shell am start -D -n net.pd.cn/net.pd.cn.guide.SplashActivity
然后应用启动,处在等待被调试的界面。
截屏2020-02-07下午11.06.25.png
- 以调试模式启动app
-
查看调试应用的进程id
adb shell ps | grep 包名
截屏2020-02-07下午10.55.05.png
-
pc和手机建立端口映射,并指定对应的调试进程。
用 adb forward tcp:port jdwp:<pid> 在PC上做端口映射, 然后在PC上通过端口连接手机。例如:adb forward tcp:8666 jdwp:13335关于forward,jdwp具体使用,可以网上查找资料。
-
查看已经映射成功的进程和端口
adb forward --list
-
创建远程调试
截屏2020-02-07下午11.02.30.png
这里的Port即为上面通过adb forward指定的端口。这个可以自己设定,只要没有被占用即可。
-
打断点,开始调试
截屏2020-02-07下午11.04.51.png
如果顺利,便会跳到调试断点的地方,但是,通过这个方法,始终在Run -> Debug smali的时候提示:
Error running 'smali': Unable to open debugger port (localhost:8700): java.net.SocketException "Connection reset"
解决办法,不使用adb forward命令做端口映射,使用DDMS。
最新版Android Studio已经到不到打开这个工具的入口了,那么我们可以在命令行打开,如果配置了环境变量可以直接执行:monitor。如果没有配,mac电脑可以到在/Users/pds/Library/Android/sdk/tools目录下执行。

我们可以直接使用8700端口。
其它
Dex、Smali、Class、Java、Jar之间的相互转换
工具 | 作用 | 举例 |
---|---|---|
javac | java----->class | javac Test.java |
dx | class----->dex | dx --dex --output=Test.dex Test.class |
baksmali | dex----->smali | java -jar baksmali.jar -o [输出文件夹] dex文件 |
smali | smali------>dex | java -jar smali.jar -o 目标dex文件 [smali文件夹] |
dex2jar | dex----->jar(class的压缩包) | d2j-dex2jar.bat dex文件 |
apktool | apk----->smali | apktool d testapp.apk |
遇到的问题
- 启动DDMS,提示8700端口占用
执行“lsof -i:端口号”命令查看占用进程。
执行“kill -9 端口号”结束该进程
重新启动DDMS -
DDMS不能显示调试端口
截屏2020-02-07下午11.21.05.png
同样,可能8600被占用。
- 以调试模式启动app提示不能启动Activity,报Permission Denial
Activity应该是主入口activity,加上“exported=true”也可以启动。
网友评论