在分析安卓APK的过程中,Java伪代码因为语法更接近常见的编程语言,读起来比较直观,很适合用来快速搞清楚程序的整体业务逻辑。但如果想进一步核对寄存器里到底放了什么值、指令之间的跳转是怎么实现的,或者方法之间又是如何调用的,光看伪代码往往还不够,这时候就需要翻开更接近底层Dalvik字节码的Smali代码来对照着看。
一、JEB Decompiler怎么查看Smali代码
在开始查看Smali之前,一般先把APK正常地加载进JEB里面。如果碰到的APK文件体积比较大,或者经过了比较严重的加固和混淆,软件在第一次解析时可能会花上一些时间,不要刚把文件打开,界面还没完全反应过来,就急着去搜索类名,这样反而容易因为索引没建完而找不到东西。
1、打开APK文件
启动JEB之后,点一下菜单里的【File】→【Open】,然后选中自己要分析的那个APK文件。等加载全部完成以后,在左侧那个叫Project Explorer的导航区域里面,把APK下面跟DEX或者Bytecode相关的那些节点一层层展开来。JEB的项目界面本身支持在反汇编、反编译出来的源码,还有那些资源文件当中去搜索文本内容,所以如果不想手工去点树形目录,也可以借助软件自带的快速搜索功能来定位到某一个具体的类或者方法上,这样会省下不少时间。
2、定位目标类
在左侧那棵树形结构中,顺着DEX的包名一层一层点下去,就能找到目标类的名字,也可以直接使用【Ctrl】加【Q】或者【Ctrl】加【F】这样的快捷键,在搜索框里把类名、方法名、某一段特定的字符串甚至包名给敲进去,让JEB帮你去找。按照官方手册里的说明,JEB的这个快速搜索功能,是能够在反汇编、反编译源码以及资源文件等文本内容里面,比较快地帮你定位到需要的信息的,用熟了以后,就算遇到被混淆得一塌糊涂的包名,也能比用眼睛一层层翻快上不少。
3、打开Smali视图
在找到的目标类上面双击一下,把它打开,这时候通常就能看到它的字节码,或者已经被反编译好的代码了。要是当前显示出来的是一段Java伪代码,那就需要去界面上找一下跟视图或者标签页相关的按钮,在里面把展现形式从Java切换到Dalvik Bytecode、Disassembly或者Smali上去。不同版本下这个切换入口的具体位置可能会有一点差别,但不管它放在哪里,核心的思路都是回到DEX那个代码单元的下面,去查看这个类在字节码层面的真实形态。
二、JEB Decompiler Smali代码修改后怎么保存
在JEB里动手把Smali的指令改过之后,能不能直接点一个按钮就把它存成一个可以装到手机上去运行的APK文件,这要取决于你当前使用的JEB版本、手里有什么样的授权功能,还有你修改的时候到底采用的是哪种方式。不能简单地觉得在软件里点了一下保存项目,就等于已经把那个APK文件给改好了,这两步之间往往还隔着不少操作,心里得有个清晰的区分。
1、先保存JEB项目
如果你现在的修改还只是停留在分析层面,比如只是给变量或者方法改了一个更好懂的名字、给某条指令加上了一行注释,或者做了一些跳转和导航上的标记,那么用一下菜单里的【File】→【Save】,或者快捷保存按钮,把这些分析进度给留下来就可以了。按照官方手册里的说法,这种快速保存的作用主要是把你当前项目里的分析状态给存下来,方便下一次再打开同一个项目的时候接着往下分析,不用再从头翻起,省掉很多重复工作。
2、确认修改是否写入了字节码
如果你的操作已经是在Smali视图里直接动手改过指令了,那就要专门去确认一下,你所做的这些改动,到底有没有真正地写进DEX那个代码单元里面去。因为有的时候,你看到的那个Smali视图只是软件在分析阶段给出的一个输出结果,看上去是可以编辑的,但它不一定就代表最终那个可执行文件。改完之后一个比较稳妥的做法是,把当前这个方法关掉,再重新双击点开一次,看看刚才敲进去的新指令还在不在,有没有被还原回去,这样心里会更有底。
3、重新打包和签名
安卓应用只要被改动过,原来带着的那个签名基本上就会失效,即使你在Smali里的逻辑改得完全正确,如果最后没有重新给他签名,或者签名的方案跟手机系统要求的不一致,在安装的时候就很可能直接报错,连装都装不上去。所以在准备正式到设备上去验证以前,应该用合适的工具把APK重新打一个包,再把签名给签好,然后放到测试设备上去安装运行,这样跑出来的结果,才能代表你这一轮修改之后的真实效果。
三、JEB Smali修改后为什么没有生效
有时候会碰到这样一种情况:明明已经在Smali里面改过了指令,可是最后程序跑起来的表现跟修改之前还是一模一样,好像什么都没有发生过。遇到这类情形,最常见到的几个原因,通常是改错了DEX文件、选错了要修改的类名、保存的其实只是JEB项目里的分析状态而没有真正动到APK本体,或者手机上正在运行的依旧是修改之前那个旧的安装包,新的包没有被正确替换上。
1、检查是否有多个DEX
很多APK里面并不只有一个DEX文件,除了默认常见的classes.dex之外,还可能会存在classes2.dex、classes3.dex这样的文件,被拆分过的大型应用尤其容易出现这种情况。你想改的那个目标类,它不一定就是放在你最先打开的那一个DEX里面。所以修改之前和修改之后,都有必要反复去核对一下包名、类的完整名称,还有方法的签名,在所有DEX中是不是都保持了完全一致,避免只在其中一个DEX里做了修改,而程序实际调用的却是另一个DEX里的旧版本。
2、检查安装包是否更新
等你已经重新打包并且装到手机上之后,也要专门去确认一下,手机上现在装着的确实是你刚刚生成的那个新版本。很多时候,因为旧的应用程序没有被卸载干净,或者新旧版本之间的版本号、签名信息纠缠在了一起,系统可能并没有真正用新包去覆盖掉旧包,导致运行的时候还是老的代码在起作用,这个细节是很容易被忽略过去的。
3、保留原始APK和修改记录
最后,在动手修改Smali之前,养成一个好习惯:提前把最原始的APK做一个备份存好,同时把你这一次改动的类名叫什么、方法名叫什么,在哪个偏移位置动了手,还有为什么要这样改,全部用文字记下来。有了这些记录在后面,万一程序跑起来直接崩溃了,也可以根据记录快速地退回到修改前的状态,再一条一条地去排查,看看究竟是哪一步的操作带来了问题,不至于手忙脚乱。
总结
在JEB Decompiler里面去查看Smali代码,以及修改完之后应该怎么去保存,整体的操作思路大致是这样:先把APK文件正常打开,在DEX或者字节码的那个层级下面找到目标类,接着再把视图切换到Smali或者Dalvik反汇编的那种形式,去一条一条地分析指令的细节。
