开发者社区> 问答> 正文

【eoeAndroid精华推荐】Android逆向之巧妙利用asm修改方法得到需要的信息


做逆向(当然了,不一定是好事,关键是看用的人)的时候,经常发现别人为了防止你修改这个修改那个而各种加密加密再加密,加到最后自己都蒙了,如果顺着杆子网上摸索,太过于麻烦,何不换一个思路,在他最后的那个方法中做修改,输出其传入的参数,也就是直接修改他的.class文件呢?

这次遇到的情况是这样:有一个对外公开的sdk,这个sdk是配合动态库so工作的,而且呢,这个sdk中还有一个assets文件夹,里面有一些资源以及一个XXX.jar和YYY.jar(就这么定义吧),最终他会发送一些消息,我的目的是,得到这个消息.
先说说我的逆向步骤吧:

第一步 解压这个sdk看看


比如这个sdk是一个java的sdk,叫做AAA.jar,解压开来发现是assets: XXX.jar YYY.jarres:一些资源src:基本的对外接口
显然,第一反应是这个XXX.jar和YYY.jar有奇怪,可能是动态加载的或者其他什么,解压看看吧,然后发现,都解不开.
用LINUX命令查看,发现前者其实是一个so文件,后者未知.

第二步 研究下这个AAA.jar


老规矩,jdgui先看看他有什么内容,发现都是一些拷贝以及加密解密的代码,果然,那个XXX.jar
其实是它动态拷贝进去的一个动态链接库,里面的一些native方法还是拷贝以及加密解密(针对那个莫名其妙的YYY.jar)

第三步 研究下这个YYY.jar


从上面的步骤能猜出来,这个YYY.jar其实是真实逻辑所在的地方.
第一反应:它是一个dex文件,可以被动态加载用dex2jar试了一下,告诉我,这不是一个dex或者odex文件继续看发现这个YYY.jar是用native方法(也就是那个XXX.jar伪装的so文件中的方法)解密之后写入文件的,解密之后的确是一个jar文件(这个jar应该是用dx --dex生成的jar,里面是一个MF文件以及一个dex),这种jar文件,根据以前的逆向手段来看,他是一个来动态加载的文件.进入手机内部存储查看果然在/data/data/下的对应目录发现了这个YYY.jar以及在动态加载的时候会解压出来的YYY.dex.

第四步 用dex2jar把上述YYY.dex生成对应可读的jar包(假定其名字为realSDK.jar)


生成jar包之后,就可以开始我们今天的话题了.
不管愿意不愿意,最起码要针对这个最终可读的他的全部真实逻辑所在的jar包的内容,浏览一遍.这个需要一些经验来查找你需要的信息,现在来说我遇到的问题:经过非常复杂的函数调用,最终我能锁定我所需要的信息在ABC类中的sendMessage(msg)中(就是这个msg参数),但是因为前面的各种初始化逻辑太多,中间生成的一些变量比较难以抓取到,怎么能最快速的得到这个msg呢?

下面是本文的主题


思考了半天,最终决定用ASM来改造这个ABC.class文件(对于老手而言,或许比较简单吧:))

首先,新建一个java工程,将这个jar包以及asm所需要的包导入


我的是asm-3.2.jar和asm-commons-3.2.jar

然后,新建两个类,一个是ClassAdapter的子类(AddLogClassAdapter),一个是AdviceAdapter的子类(MyAdviceAdapter)


AddLogClassAdapter.java如下
package com.wei.tian;

import org.objectweb.asm.ClassAdapter;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;

public class AddLogClassAdapter extends ClassAdapter{

    public AddLogClassAdapter(ClassVisitor arg0) {
        super(arg0);
    }

     public MethodVisitor visitMethod(final int access, final String name,
                final String desc, final String signature, final String[] exceptions) {
                MethodVisitor mv = cv.visitMethod(access, name, desc, signature,exceptions);
                if (mv != null) {
                    if (name.equals("sendMessage")) {
                        return new MyAdviceAdapter(mv,access,name,desc);
                    }
                }
                return mv;
            }
}

MyAdviceAdapter.java如下
package com.wei.tian;

import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.AdviceAdapter;
public class MyAdviceAdapter extends AdviceAdapter{

protected MyAdviceAdapter(MethodVisitor mv, int access, String name, String desc) {
        super(mv,access,name,desc);
    }


    @Override
        protected void onMethodEnter() {
            super.onMethodEnter();
            visitFieldInsn(GETSTATIC,"java/lang/System","out","Ljava/io/PrintStream;");
             loadArg(0);
             visitMethodInsn(INVOKEVIRTUAL,"java/io/PrintStream","println",
                "(Ljava/lang/String;)V");
        }
}
然后写一个可执行程序Test.java,main里面有以下代码即可
try{
                     ClassReader cr = new ClassReader(ABC.class.getName());
                     ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
                     ClassAdapter classAdapter = new AddLogClassAdapter(cw);
                     cr.accept(classAdapter, ClassReader.SKIP_DEBUG);
                     byte[] data = cw.toByteArray();
                     File file = new File("ABC.class");
                     FileOutputStream fout = new FileOutputStream(file);
                     fout.write(data);
                     fout.close();
                    }catch(Exception e){
                        e.printStackTrace();
                    }

做一下简短说明吧,AddLogClassAdapter里面负责进行适配,适配你需要修改的方法,这里我们针对这个sendMessage方法进行拦截,拦截的具体规则,就是MyAdviceAdapter里面的逻辑了.这个里面有一个Override的方法onMethodEnter,它会在方法进入的时候调用到,这里我将其参数的第一个载入到当前栈中,并且调用System.out.println(String)方法,他会将msg输出.修改过的ABC.class文件会写入到工程的主目录下,F5刷新一下工程就会发现了
以前的ABC.class中的sendMessage方法如果是以下内容:
private void sendMessage(String msg){
  以前的逻辑
}
修改之后的ABC.class中的sendMessage代码相当于
private void sendMessage(String msg){
  System.out.println(msg);
  以前的逻辑
}
最后一步,怎么用这个ABC.class呢?很简单,将realSDK.jar解压,找到其中的ABC.class然后删掉,用我们刚生成的ABC.class替换掉,然后(假定这个realSDK解压后第一个目录是com)用命令 jar cvf newSDK.jar com/* 生成心的newSDK.jar文件,将这个newSDK.jar文件导入到一个新建的Android工程中,就能够在调用这个sendMessage的时候,输出我们需要看到的内容了。


本信息来源:eoe Android社区 作者ID:fortianwei
原帖地址:http://my.eoe.cn/fortianwei/archive/20909.html



展开
收起
eoe 2014-01-06 14:20:55 14233 0
6 条回答
写回答
取消 提交回答
  • Re【eoeAndroid精华推荐】Android逆向之巧妙利用asm修改方法得到需要的信息
    恩,看不懂

    -------------------------

    Re【eoeAndroid精华推荐】Android逆向之巧妙利用asm修改方法得到需要的信息
    我也看不懂!
    2014-02-02 21:10:19
    赞同 展开评论 打赏
  • Re【eoeAndroid精华推荐】Android逆向之巧妙利用asm修改方法得到需要的信息
    高手厉害
    2014-01-09 09:37:08
    赞同 展开评论 打赏
  • 移动云后续推出和eoe社区联营的安卓开发干货分享,欢迎各位喜欢安卓开发的、即将开始安卓开发的、或者正在进行安卓开发的小盆友们积极回帖、分享哈
    2014-01-08 12:12:42
    赞同 展开评论 打赏
  • java 表示看不懂
    纯支持了
    2014-01-07 11:35:45
    赞同 展开评论 打赏
  • 您的帖子很精彩!希望很快能再分享您的下一帖!
    2014-01-06 16:17:19
    赞同 展开评论 打赏
  • 支持很不错学习了
    2014-01-06 15:02:06
    赞同 展开评论 打赏
滑动查看更多
问答排行榜
最热
最新

相关电子书

更多
58同城Android客户端Walle框架演进与实践之路 立即下载
Android组件化实现 立即下载
蚂蚁聚宝Android秒级编译——Freeline 立即下载

相关实验场景

更多