五、添加日志打印过滤规则从上面可以看到我们完成了所有的一键化操作,但是可惜的是被那些日志霸屏了,完全懵逼的状态。所以这里我还得解决一个问题,就是给这个日志加一些过滤规则,能够很好的控制日志,让他受我控制。这个问题其实和上面的工具没多大关系了,因为在前面我们知道,那个打印方法已经被弄出来放到了JWUtils这个类中,而这个类是工具需要编译然后插入到dex中的,所以咋们就可以直接修改JWUtils中的打印日志的方法即可。
下面就是我写的JWUtils类,内部已经有了一些打印信息的过滤规则,主要包括:控制日志的总开关,需要打印日志的方法名,返回类型,参数类型,类名等规则。这个规则是一个字符串内容:
-s 1 -m JW -r int -p [int,java.lang.String] -c JWUtils
-s:表示日志总开关
-m:表示需要打印日志的方法名称
-r:表示方法返回的类型
-p:表示方法的参数类型,多个类型直接用分号隔开
-c:表示需要添加日志的类名
然后我把这行字符串内容保存到/data/local/tmp/log.txt文件中,为什么要保存到这里呢?有的同学想保存到SD卡中,但是假如有的应用没有声明SD读写权限,那怎么办?我们最终的JWUtils类是被插入到应用中的。所以就想到了系统有一个不需要权限有没有沙盒权限限制的目录:/data/local/tmp/。
下面简要的说一下这个过滤规则吧:
首先我们可以在一个方法中获取当前方法的堆栈信息,所以就可以获取到当前方法名和类名了:
StackTraceElement[] stackElements = new Throwable().getStackTrace();
因为我们想要获取到被插入打印代码的方法信息,所以这里只要获取数组的第二个元素即可,第一个元素其实是JWUtils.printStackTrace方法信息了。有了这个元素之后直接调用它的两个方法就可以获取到当前方法名称和类名称了,这个也就可以做到方法名和类型的过滤规则了。
而对于上面只能简单的获取到指定的方法名和类型,却获取不到对应方法的签名信息,比如参数类型,返回值类型等。所以这里得费点事,就是需要通过上面获取到的类名然后用Class.forName方法获取到对应类的Class对象,然后在获取该类所有的方法信息:
然后用一个全局数组进行保存,在结合上面的方法名去遍历这个数组,就可以获取到指定方法名对应的方法信息了:
这样就可以做到方法的返回值类型,参数类型的过滤规则了,但是有同学会发现这里有一个问题,假如一个类中有重构方法,也就是方法名相同的但是方法签名不一样,这里因为只是通过检测方法名来获取到method的信息的。所以对于同一个类中重构方法是没有过滤效果的。
最后就是一个日志总开关了,所以最终的过滤规则如下:
有了这个规则之后,咋们再次操作一下,把这个JWUtils类放到icode_tools目录中,然后再次跑一下icode_tools批处理,安装即可,这时候我们先设置一个过滤规则,直接使用命令就进行操作了:
我开始的时候把日志关闭,然后在打开,在关闭看一下效果,我们使用echo命令写入一条规则关闭日志:
看到了吧,这里我们通过总开关可以控制日志输出了,下面再来一条实际的过滤规则,就是通过包名,方法名,返回类型等规则操作一下:-s 1 -m a -c com.meelive.ingkee.v1.core.a.a
这里我们通过方法名和类型进行过滤限制,打印之后的结果都是指定类名的方法日志了。
上面操作的是某直播软件,为了有说服力,我得用微信在尝试一下哈:
看到了,此刻我们加上规则之后,打开微信顿时觉得爽多了,日志不多,慢慢操作查看具体方法,从微信的日志看,这个版本已经开始使用热修复框架Tinker了。后面得赶快出一篇分析Tinker框架的文章了。
最后在来看一下QQ的日志:
注意:此过滤规则可以自己定义的,因为所有的打印消息逻辑都在JWUtils类中,上面说到这个类是开放出来的,也就是可以自己随便定义这个类的信息!
六、问题总结好了,到这里我们已经解决了在前一篇文章中遇到的三个问题,也是填完了工具的坑,下面来总结一下这三个问题:
第一个问题:方法数超了问题,因为我们给每个类都添加了一个打印堆栈信息的方法,所以如果一个dex中包含很多类的话,那么就会额外增加很多方法,在使用dx工具进行转化的时候出现了方法数超的问题。
解决方案:可以把打印堆栈信息的方法抽取到一个工具类中,然后把这个类插入到dex中,这里采用的方案是通过dex2jar工具转化dex成jar文件,然后在编译需要插入的类文件,把编译之后的类文件直接塞入到jar文件中,最后在原先的dex基础上每个类中的每个方法只需要调用JWUtils.printStackTrace方法即可。
第二个问题:一键化完善工作,这个是因为我们在前面文章中操作的时候发现从抽离apk中的dex文件到最终重签名apk文件有很多步骤,但是都是人工操作的,非常繁琐,所以可以把这些步骤进行整合一步到位。
解决方案:先从apk中解压出dex文件和签名文件==》利用aapt命令删除apk中的签名文件==》添加代码到dex中==》编译工具类得到class文件,塞入到jar文件中==》使用dx命令转化成dex文件==》使用aapt命令覆盖apk中旧的dex文件==》使用jarsigner对apk重新签名。
然后把工程导出一个可执行的jar包,这里为了后续扩展,就提供了执行的入口参数:
1》工具运行的当前目录
2》需要处理的源apk文件路径
3》aapt命令的路径
4》打印信息的tag,默认是:jw
5》打印信息的类名,默认是:cn.wjdiankong.jw.utils.JWUtils
6》打印信息的方法名:默认是:printStackTrace
然后我们还需要提供一个批处理icodetools.bat文件,主要执行的命令是:
java -jar libs\icode_tools.jar %~dp0 src.apk %aapt_path% jw cn.wjdiankong.jw.utils.JWUtils printStackTrace
第三个问题:优化打印日志信息规则,这个是因为当我们使用一键化工具生成apk安装运行之后发现打印日志太多导致霸屏,而且应用本身还会出现了ANR问题。所以得想个办法控制打印日志输出。
解决方案:加一些日志输出过滤规则,首先咋们得有一个日志总开关,然后是可以指定需要打印方法所属的类名,方法名,以及方法的返回类型,参数类型等过滤规则。
需要注意的是,这个过滤规则都是工具使用者可以自己实现的,就在JWUtils类中,代码可以自行修改,如果不想修改,默认的我已经加了这些规则:
-s 1 -m JW -r int -p [int,java.lang.String] -c JWUtils
-s:表示日志总开关
-m:表示需要打印日志的方法名称
-r:表示方法返回的类型
-p:表示方法的参数类型,多个类型直接用分号隔开
-c:表示需要添加日志的类名
我们可以通过echo命令给/data/local/tmp/log.txt文件输入规则来控制日志输出。
七、增长经验值
1、了解到一种往dex文件中插入一个类文件的方法
先把dex转成jar文件,然后解压jar文件,复制对应的类文件到解压目录下,然后在使用jar命令进行class文件重新打包成jar文件即可。最后在使用dx命令转成dex文件。
2、了解到如何在一个方法中得到该方法的签名信息。
通过堆栈信息获取到该方法的名称和所在的类名,然后在使用Class.forName方法通过类名得到类对象,然后在使用反射获取到该类对应的所有方法签名信息,然后通过之前的方法名进行检索获取到对应方法的签名信息。
3、javac,jarsiginer,aapt命令的常见用法。
八、工具使用说明1、当前目录的需要操作的apk文件名称默认是src.apk文件,如果想修改apk名称,可以手动的修改icodetools.bat中的apk文件名
2、在icodetools.bat中可以指定当前日志的tag,默认值是jw
3、当前目录下还有一个JWUtils.java这个java文件,这个类中有一些打印方法,可以根据自己的需求定义一些方法,但是定义的方法必须有要求:
1》必须是static类型
2》方法只允许有一个参数是String类型的,而这个参数就是打印的日志tag
3》方法名称可以随意指定,但是必须在icodetools.bat中保持一致
所以最终的方法模板为: public static XXX YYY(String tag)
这个类的名称可以变动,但必须和icodetools.bat中保持一致
4、当前目录下的libs目录中是工具依赖的jar包,不可以随便修改
5、当前目录下的JWUtils.java文件名和包名都不可变动
6、cyy_game.keystore签名文件名不可进行修改
7、如果想自己再次签名,可以使用unsigned.apk文件操作,signed.apk是使用了cyy_game.keystore文件签名
8、在icodetools.bat中需要手动设置aapt命令的路径
9、工具运行前必须配置JAVA_HOME环境变量
10、现阶段只支持JDK1.7以及以下版本编译器,不支持1.8以及以上的
注意:工具目录下有两个脚本,一个是icodetools_1.0.bat,一个是icodetools_2.0.bat,这两个工具主要是因为为了兼容更多的apk,默认最好先采用icodetools_1.0.bat工具进行尝试,失败了可以在使用icodetools_2.0.bat工具,如果都失败了那就要反馈问题给我了!
九、工具使用常见问题第一个问题:Uncaught translation error: com.android.dx.cf.code.SimException
这样的错误是因为工具版本不兼容问题,可以尝试使用另外一个版本操作。如果icodetools_1.0.bat操作有问题具使用icodetools_2.0.bat工具操作,如果icodetools_2.0.bat工具操作有问题就是用icodetools_1.0.bat工具进行操作。如果两个工具都有问题那就是真的有问题了,记得给我反馈!
第二个问题:拷贝文件失败:java.io.FileNotFoundException: JWUtils.class
这个错误主要是因为工具内部会采用javac编译JWUtils类文件,这里应该是编译失败了,大部分原因是因为JWUtils这个类文件的编码格式和语法格式导致的,所以解决版本,可以自己使用javac命令进行编译看看具体是哪里编译出了问题。
第三个问题:bad class file magic (cafebabe) or version (0034.0000)
这个问题是因为javac这个命令是1.7以上版本的,也就是JDK版本。但是此工具现在只支持JDK1.8版本以下的。所以这里需要设置JDK版本。
第四个问题:成功注入代码安装之后发现无日志信息
这个问题可能有两个原因:第一个是需要检查日志的tag是否正确,主要通过查看icodetools.bat文件中的执行命令,第二个原因是因为默认情况下开始日志开关是关闭的,所以我们还得手动打开,首先得去/data/local/tmp目录下,然后使用echo "-s 1" >log.txt,打开日志即可。
当然还有其他问题,所以我希望大家在使用的过程中遇到问题以及一些优化建议都可以提给我,我会尽快修复!
工具下载地址:https://github.com/fourbrother/icodetools
声明:有人认为有了这个工具迫不及待的手痒想立马下载尝试,但是我想说这还没有结束,因为后面一篇文章才是重点,任何一个工具都需要发挥其作用才是个好工具,所以下一篇文章就会带大家用这个工具来逆向一些app!
后期优化:现阶段此工具支持Windows系统,后续会增加Mac和Linux系统,现阶段只支持apk根目录下的dex文件,不支持其他目录下的dex文件处理,所以对于有些apk此工具处理过程中会出现错误!
十、总结解决了这三个问题之后,咋们的工具才算是比较完整的能够用于生产的工具了,但是因为是本人业余时间编写的,所以我相信这个工具肯定还有一些漏洞,以及需要优化改善的地方,所以我先将此工具的第一版本给出,非常欢迎大家一起使用,如果在使用的过程中发现有一些问题,一定要记得给我留言,我会立即修复和改善,我相信一个好的工具是靠大家一起贡献的。问题反馈可以在我微信私聊我或者是在微信公众号留言都是可以的,我会第一时间回复,先拜谢各位使用者了。写了这篇文章之后并没有结束,因为后面还有一篇文章会详细介绍这个工具的实际使用,如何用它来解决我们的实际问题,比如寻找hook点。文章算是写完了,精力却全没了,感觉自己身体被掏空了一样,感谢大家多多点赞,要是有打赏就在好不过了!