首页 > 代码库 > 带你开发一款给Apk中自动注入代码工具icodetools(开凿篇)

带你开发一款给Apk中自动注入代码工具icodetools(开凿篇)

一、前言

从这篇开始咋们开始一个全新的静态方式逆向工具icodetools的实现过程,这个也是我自己第一次写的个人觉得比较有用的小工具,特别是在静态方式逆向apk找关键点的时候,后续会分为三篇来详细介绍这个工具实现:

第一篇:开凿篇,简单介绍实现原理以及简单的初次方案实现简单的apk注入代码功能

第二篇:填坑篇,这一篇是在前一篇的基础上对工具的优化,可以应对市面上大部分的apk代码注入功能实现

第三篇:生产篇,这一篇是在前两篇的基础上利用这个工具来实际操刀如何进行快速定位应用的关键方法功能

还记得那一年咋们 静态方式破解应用,为了更好的追踪代码位置,手动反编译成smali文件,然后添加smali日志代码,在回编译,查看日志信息来定位关键点。那种操作现在回想是苦不堪言,操作非常复杂。那么就尝试想想如果有一个工具可以在自动为每个方法注入日志代码,这样就可以很快速的定位到我们想要的方法,所以本文就来介绍一下这个工具的实现原理,我将其命名为icodetools


二、实现方案

首先咋们来看一下具体实现原理吧,这个工具最终的形态应该是这样的,就是输入一个apk,然后在为apk中每个类的每个方法添加一段打印此方法的堆栈信息日志,然后在重新签名打包成新的apk。所以这个过程中我们最关键的地方就是如何把日志代码插入到已经编译好的apk中。我们可能想到的有两个方案:

第一个方案:首先利用apktool进行反编译成smali文件,然后解析smali文件找到每个方法的位置,添加指定smali日志代码。那么这里有一个很大的问题,就是如何分析smali文件,如何定位到每个方法?这个过程工作量就比较大了,所以这个方案就废弃了。

第二个方案:利用dex2jar获取到apk中的dex转化之后的jar文件,然后在解析jar文件获取到每个class文件,解析class文件进行方法的信息获取。这里会发现这个方案很靠谱,因为我们解析class文件比解析smali文件方便多了。而且在这个过程中会发现有一个更大的惊喜,就是dex2jar这个工具是开源的,其实内部实现原理就是解析dex文件格式,然后借助asm工具将其变成class文件的,这里又出现了一个非常重要的工具asm,这个工具下面会详细介绍。

说明:

1》这里非常感谢dex2jar的作者Claud大神的开源精神,这个工具的地址:https://github.com/pxb1988/dex2jar,是纯java代码,所以大家一定要先解读这个工具源码,内部实现原理自己研读,本文不会详细介绍的。

2》这里有的同学会想到,能不能直接解析dex文件格式,然后在找到每个类的每个方法添加日志信息,虽然我们在前面有详细介绍dex文件格式:Dex文件格式详解,但是如果想在每个类每个方法中添加日志代码这个工作量感觉比操作smali还要复杂,所以直接将其转化成class文件进行操作就非常方便了,因为我们有asm工具。


三、方案实现

上面已经探讨了方案了,在上面提到了很多次一个非常重要的工具就是asm,那么这个工具到底是干嘛的呢?这个工具非常有用,他的表现之处在JavaWeb开发中的Spring框架就有用到,可以动态的解析class文件,然后可以操作这个类,比如添加类成员,方法等等操作。所以下面不多解释了,直接用一个简单的案例来看看他的强大之处,我们实现给一个类添加一个成员字段,方法,给每个方法调用前添加一行代码。

第一、asm库基本使用

技术分享

这里借助ClassReader来读取一个类,使用ClassVisitor来进行类信息操作,ClassWriter将操作完之后的类写入文件中


第二、添加类成员字段

上面看到了一个主要的类ClassVistor,他主要就是用来操作一个类:

技术分享

这里看到的是继承了ClassAdapter,然后在构造方法中可以添加类成员信息包括字段,方法等,这里我们添加一个字段mJW,类型是String的。看看如何添加的:

技术分享

借助ClassVisitor的visitField方法就可以进行字段的添加了。然后我们运行程序之后,使用JD-GUI工具查看保存本地的class文件信息:

技术分享

看到了吧,这里成功的定义了一个字段mJW。


第三、为类的所有方法前添加代码

这里我们需要借助另外一个类了MethodVisitor了,他是用来操作方法信息的:

技术分享

在ClassAdapter的visitMethod回调方法中可以获取方法信息,然后进行操作:

技术分享

在visitCode回调方法中利用MethodVisitor开始添加代码:

技术分享

这里看到可以利用visitFieldInsn方法方法System类的静态变量out,然后在使用visitLdcInsn从常量池中获取值,最后在利用visitMethodInsn方法来调用PrintStream类的成员方法println来打印信息,运行程序之后继续借助JD-GUI来查看class文件:

技术分享

看到了,类中所有的方法前面都被加了一行打印代码。

注意:有的同学发现了,上面那个添加代码怎么得到呢?难道真的要记住那么多api去添加吗?其实不用那么复杂,Eclipse有一个插件Bytecode,可以把Java代码自动生成类似于上面的代码,咋们之后手动拷贝一下就好了!工具后面会说。


第四、为类添加成员方法

下面在来看最后一个操作吧,就是给类中添加一个成员方法,有了上面的操作之后我们应该知道,如果要操作类就要借助ClassVisitor类,要是操作方法就需要借助MethodVisitor类,那么这里为类添加成员方法,借助ClassVistor类来进行操作了,但是在操作之前咋们得先搞好一个工具Bytecode,这个是Eclipse插件,安装很简单:

技术分享

安装完成之后,可以打开视图栏:

技术分享

然后选择Java中的bytecode视图就可以了:

技术分享

下面咋们首先在一个类中编写好我们想要添加的方法代码,然后点击Bytecode视图卡片就可以看到对应的asm代码:

技术分享

看到了吧,这样操作是不是如此简单,我们可以把这段asm代码直接拷贝到AsmUtils中:

技术分享

然后在ClassVisitor的构造方法中直接调用这个方法就可以了:

技术分享

再次运行程序之后,使用JD-GUI查看类信息:

技术分享

成功的添加了printStackTrace方法了。就是如此简单。借助Eclipse的强大工具Bytecode就可以了,以后都不要在自己去调用asm库的api去手动编写了。这个工具太好了得记住它!


四、工具案例实践

上面就介绍完了如何操作一个类和方法,给类添加字段,方法,在每个方法之前添加一行代码。下面就得进入本文的主题了,如何给apk应用中每个类中的每个方法添加一行打印日志信息。在开始的时候我们有了方法,就是借助工具dex2jar源码,他内部也是解析dex格式,然后利用asm将其信息变成一个class文件的,那么我们只要在这个过程中得到ClassVisitor和MethodVisitor这两个对象,就可以尽情的干很多事了。所以第一步你一定要先看懂dex2jar的源码。这里我不会对源码进行详细分析了,用过dex2jar工具的都知道,找入口很简单,直接看他的一个d2j-dex2jar.bat脚本信息:

技术分享

Dex2jarCmd就是工具的入口类:

技术分享

然后分析代码,会发现处理的核心类是Dex2jar类:

技术分享

这里会看到可以拿到ClassVisitor类对象的,那么我就可以添加打印堆栈信息的方法了:

技术分享

这里需要注意的是打印消息用了Android的Log.d方法。然后在每个方法调用之前在调用这个打印方法:

技术分享

这里过滤依然过滤了构造方法和静态代码块方法。


其实我们就需要这么做就可以了,下面咋们就用一个简单的Apk来做一下实验,咋们把apk中的classes.dex文件解压出来,为了简单直接跑这个dex2jar工具,可以在Dex2jarCmd开始处构造一个简单的命令:

技术分享

输入的dex文件为指定目录的,输出的jar文件也是指定目录的:

技术分享

然后咋们就可以运行程序了。运行结束之后我们得到了一个classes.jar文件,可以使用JD-GUI进行查看:

技术分享

到这里我们就成功的,把apk中的classes.dex中的每个类每个方法添加了我们自己的打印消息的代码了,已经成功一大半了,下面为了验证效果,咋们还得把classes.jar转成dex,在弄到apk中运行看效果。这个过程就简单了,利用系统的dx命令,把classes.jar在变成classes.dex文件:

技术分享

然后在把这个classes.dex文件塞到apk中,不过这时候得重新签名了,可以借助jarsigner工具:

jarsigner -verbose -keystore cyy_game.keystore -storepass cyy1888 -signedjar signed.apk unsigned.apk cyy_game.keystore -digestalg SHA1 -sigalg MD5withRSA

然后咋们就可以安装apk,然后开始运行效果:

技术分享

看到效果了吧,表示成功的不要不要的。有点小兴奋,通过栈信息加上我们出发一个事件之后就可以定位这个事件的方法了。对于我们在使用hook的时候寻找hook点非常重要。


五、技术总结

到这里我们本文的知识点就算结束了,但是整个工具开发并没有结束,下面就来总结一下本文涉及到的知识点:

1、学会了利用asm库来操作类,实现添加字段,方法等操作。

2、了解到了Eclipse的一个强大插件Bytecode,可以快速得到java代码对应的asm代码

3、了解了dex2jar的实现原理,内部也是借助于asm来进行操作的

其实本文的这个自动注入代码工具绝大部分是借助了dex2jar这个工具的,我们只是在适当的地方添加了我们想要的信息代码。在操作类的时候只要有ClassVisitor对象就可以操作类,MethodVisitor对象就可以操作方法了。而在dex2jar中正好有这两个对象,所以我们实际要操作的内容并不复杂。当我们使用通过修改之后的dex2jar得到了classes.jar之后,在用dx工具将其还原成classes.dex文件,在放到apk中进行重签名验证。


六、遗留的问题

如果到这里有的同学觉得这个是挺简单的,那其实就错了,因为开始的时候我已经说了后面还有一个填坑篇文章,在那篇文章会详细说明在实际操作其他企业app会遇到哪些问题,我们需要继续优化这个工具,同时对这个工具最好能够做到一键化工作,所以我们需要解决这几个问题:

1、上面看到我们是为每个类添加了一个打印栈信息的方法,那么就有一个很大的问题,如果一个dex文件过大,包含的类很多,那么就是增加了很多方法,对于dx进行转化的时候会发现方法数超了。

2、在实际的apk操作过程中会发现在每个方法中插入日志之后,当你启动app的时候那日志几乎会被霸屏的,而且一个庞大的app内部调用的方法非常多,导致应用会出现无响应状态,所以咋们得弄个开关以及想在哪些方法中添加。

3、上面在看到在整个过程中,先把dex转成jar,然后再把jar转成dex,在放到apk中,在签名,整个过程我们都是手动操作的,显得非常的费劲,所以我们还得优化这个工作,做到真正意义上的一键化,输入一个apk,输入就是已经添加日志信息的apk就可以了。


所以后面一个填坑篇文章会有很多工作要做的,当坑都填完了,咋们就得实际生产拿一些app进行实战操刀了。最后在来看一下我总结的一张原理图吧(可以点击查看高清大图):

技术分享


严重说明:

===》本文介绍的是基础篇,可以看到我们是大致走通了整个流程,但是这个和最终的工具实现差的很多,后续还有很多问题需要解决,所以重点其实在下一篇中,那里会去解决在实际使用过程中遇到的各个问题。以及最重要的就是如何把所有的步骤连贯起来,一键化开发完善。一定要记得看下一篇内容,那里才是主战场!

===》一定要自己去github上下载dex2jar的源码,自己先调试走通程序,不然一切都是枉然,因为这个工具是纯Java编写的,代码不是很多,所以难度不大。


七、总结

对于这个自动插入代码的工具我个人觉得有需求采取这么干的,因为以前遭受过那种手动注入代码的痛苦,但是从本文的工具可以看到,对于一些加固app是无能无力的。而工具的理念来源是美团的一个热修复框架Robust的。关于源码的话等后面都介绍完了,我把我写的部分公开,但是你还是得先去看看dex2jar的源码。又开始了写这种文章,每次写完感觉自己身体被掏空了一样,多多点赞。打赏就在好不过了。


更多内容:点击这里

关注微信公众号,最新技术干货实时推送

技术分享

扫一扫加小编微信
添加时注明:“编码美丽”否则不予通过!
技术分享
技术分享

带你开发一款给Apk中自动注入代码工具icodetools(开凿篇)