美文网首页逆向 & 外挂Android
Android逆向入门流程

Android逆向入门流程

作者: 普通的程序员 | 来源:发表于2019-01-29 16:13 被阅读840次

    0.写在前面

    本文是笔者自学笔记,以破解某目标apk的方式进行学习,中间辅以原理性知识,方便面试需求。
    参考文章的原文链接会附在相应流程位置,方便阅读学习。

    逆向分析流程.jpg

    1.获取目标apk

    第一步是拿到目标安装包文件,这一步很简单,可以在主流的第三方市场内获取,但是主流的第三方市场都要求应用加固,为了之后方便,笔者常常去app的官网下载,或者去一些小市场下载,运气好的话能下载到他们的未加固版本。

    2.确定逆向目标

    这个就看各自需求,比如有破解内购,分析反逆向逻辑,crackme题目,查看竞品实现方式,确定好目标再下手。

    3.拖动目标文件到集成工具

    直接拖apk到集成工具,如改之理,比如jadx,这里是为了检查有没有加固。
    附上一些必要的开发工具下载地址
    https://www.jianshu.com/p/b33eb2f5efa0

    4.加固

    这里判断很好判断,形如有StubShell的包,都是被加固了的。
    那接下来就是要脱壳了。

    4.1加固原理

    脱壳之前,了解壳是什么(加固原理)。
    https://www.jianshu.com/p/cec7ef861ace

    加壳
    https://mp.weixin.qq.com/s/KELi6e6x4-svGP6Ef6r4jQ

    壳的加载
    https://mp.weixin.qq.com/s/AYZ7k75IlKLDbAIJZ2BelA

    so的加载
    https://mp.weixin.qq.com/s/BhGxnJrRnrYAEWJ7zDZKmQ

    还可能涉及到,so指令膨胀(加花),指令抽取等防护。

    4.2脱壳

    接下来才是脱壳
    脱壳还可能涉及到指令修复

    壳的指令修复
    https://mp.weixin.qq.com/s/-vrDvp3rbTKNIX-pAlamVw

    4.2.1手脱

    手脱的麻烦点就在于过各种检测,过完所有的检测最终就是找到dex的内存地址,最后运行代码进行dump

    链接是收集各种手工脱壳的帖子
    https://www.jianshu.com/p/3f6bcf364197

    4.2.2机脱

    实际上就是各种脱壳机,原理就是
    无论什么壳,最终都是要加载到内存中,等apk已经加载到内存后,脱壳机把dex dump出来就行了,也就省去了手工脱壳的麻烦。

    这里的原理可参考
    Android中apk加固完善篇之内存加载dex方案实现原理(不落地方式加载)
    http://www.520monkey.com/archives/629

    原理主要讨论了两个问题
    如果解密后有一个未加密的apk做中间产物,那就是落地方式,这个方式已经被淘汰了,因为不安全。
    如果不落地则可能出现加载两次到内存的效率问题。

    这里不深研,只提供一个脱壳机,fdex2
    看雪原帖(也说了原理)
    https://bbs.pediy.com/thread-224105.htm
    52破解帖
    https://www.52pojie.cn/forum.php?mod=viewthread&tid=758726&fromguid=hot
    推荐另一个机脱dumpDex
    https://github.com/WrBug/dumpDex

    核心代码,有空可以看
    package com.ppma.xposed;
    
    import java.io.File;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.OutputStream;
    import java.lang.reflect.Method;
    
    import de.robv.android.xposed.IXposedHookLoadPackage;
    import de.robv.android.xposed.XC_MethodHook;
    import de.robv.android.xposed.XSharedPreferences;
    import de.robv.android.xposed.XposedBridge;
    import de.robv.android.xposed.XposedHelpers;
    import de.robv.android.xposed.callbacks.XC_LoadPackage;
    
    public class MainHook implements IXposedHookLoadPackage {
    
        XSharedPreferences xsp;
        Class Dex;
        Method Dex_getBytes;
        Method getDex;
        String packagename;
    
    
        public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) throws Throwable {
            xsp = new XSharedPreferences("com.ppma.appinfo", "User");
            xsp.makeWorldReadable();
            xsp.reload();
            initRefect();
            packagename = xsp.getString("packagename", null);
            XposedBridge.log("设定包名:"+packagename);
            if ((!lpparam.packageName.equals(packagename))||packagename==null) {
                XposedBridge.log("当前程序包名与设定不一致或者包名为空");
                return;
            }
            XposedBridge.log("目标包名:"+lpparam.packageName);
            String str = "java.lang.ClassLoader";
            String str2 = "loadClass";
    
            XposedHelpers.findAndHookMethod(str, lpparam.classLoader, str2, String.class, Boolean.TYPE, new XC_MethodHook() {
                protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                    super.afterHookedMethod(param);
                    Class cls = (Class) param.getResult();
                    if (cls == null) {
                        //XposedBridge.log("cls == null");
                        return;
                    }
                    String name = cls.getName();
                    XposedBridge.log("当前类名:" + name);
                    byte[] bArr = (byte[]) Dex_getBytes.invoke(getDex.invoke(cls, new Object[0]), new Object[0]);
                    if (bArr == null) {
                        XposedBridge.log("数据为空:返回");
                        return;
                    }
                    XposedBridge.log("开始写数据");
                    String dex_path = "/data/data/" + packagename + "/" + packagename + "_" + bArr.length + ".dex";
                    XposedBridge.log(dex_path);
                    File file = new File(dex_path);
                    if (file.exists()) return;
                    writeByte(bArr, file.getAbsolutePath());
                }
                } );
        }
    
        public void initRefect() {
            try {
                Dex = Class.forName("com.android.dex.Dex");
                Dex_getBytes = Dex.getDeclaredMethod("getBytes", new Class[0]);
                getDex = Class.forName("java.lang.Class").getDeclaredMethod("getDex", new Class[0]);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            }
    
        }
    
        public  void writeByte(byte[] bArr, String str) {
            try {
                OutputStream outputStream = new FileOutputStream(str);
                outputStream.write(bArr);
                outputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
                XposedBridge.log("文件写出失败");
            }
        }
    }
    

    4.3得到.dex文件

    dex文件是什么
    java,dex,smali,关系/区别又是什么
    https://www.jianshu.com/p/fb9aec070c0a
    延展阅读
    dex直接得到smali,插入log代码再回编译dex
    https://blog.csdn.net/jiangwei0910410003/article/details/79820040

    涉及到两个语句

    java -jar baksmali.jar -o classes classes.dex 其中classes是反编译dex之后的smali文件夹目录
    
    java -jar smali.jar classes -o classes.dex 其中classes是反编译的smali目录,classes.dex是回编译之后的dex
    

    5.集成工具分析

    其实就是可以把刚才拿到的.dex拖进来分析了

    6.减少混淆干扰

    apk在打包时会被混淆,文件名被替换成影响无意义的字母甚至汉字。
    jdax通过以下设置来减少混淆干扰
    https://segmentfault.com/a/1190000012180752

    发散:proguard的源码阅读
    https://www.jianshu.com/p/734424a14eff

    7.定位目标

    借助第三方工具
    比如adb工具
    https://blog.csdn.net/halibobo1998/article/details/50623929

    或者有一部已经root的机器(可以减少很多麻烦),使用layoutInspector去查布局找id

    简单的就下一个第三方app 当前activity

    还可以通过搜索界面里的特殊字串来定目标文件。
    因为资源字串无论怎么混淆,反编译出来的public.xml一定有对应的int值

    8.分析业务逻辑

    9.确定逆向方法

    比如想通过xposed框架去hook某个结果,还是直接nop掉某个返回值。

    10.使用apktool进行反编译

    执行代码

    apktool d -f 目标apk路径
    

    这里可能遇到壳利用apktool漏洞对其进行的干扰
    http://www.520monkey.com/archives/808

    11.得到.smali

    smali语法
    https://www.jianshu.com/p/54e893ae28ea
    空类的smali结构
    https://www.jianshu.com/p/758bccaaa0a6

    12.源码调试

    静态调试
    https://www.jianshu.com/p/4359598a2c9b

    动态调试
    https://www.jianshu.com/p/90f495191a6a

    动态调试so
    https://www.jianshu.com/p/5617220cbb02

    13.回编译 app

    执行命令

    apktool b -d 资源路径 -o 输出.apk
    

    可能会遇到回编译失败的情况,比如低版本的apktool不认识高Android版本的属性,可以选择升级apktool或者删除该属性值。

    14.签名

    执行命令

    apksigner sign --ks testKey.jks --ks-key-alias testkey --ks-pass pass:123456 --key-pass pass:123456 --out output_sign.apk source.apk
    

    参考

    Android逆向分析笔记
    https://lichao890427.github.io/wiki/android%20reverse%20engineering/#%E6%A6%82%E8%BF%B0

    adb指令
    https://www.jianshu.com/p/85373d89bc81

    bugly出的一篇反调总结
    大部分方法都写过,少部分如文件节点,inotify,so hash检测可以加以了解(就是我没写过)
    https://mp.weixin.qq.com/s/uvrkAvbfWuDYf7SWX_dJBA

    todo

    还有smali,baksmali没写上去,还有教我兄弟学逆向,卧槽还有好多。。

    相关文章

      网友评论

        本文标题:Android逆向入门流程

        本文链接:https://www.haomeiwen.com/subject/mboajqtx.html