Android逆向入门篇--java层静态分析

isnull   ·   发表于 2019-07-01 11:23:49   ·   漏洞文章

概述

了解了编译、打包、签名、安装apk文件后,正式开始逆向的基础,静态分析

java层

apk包内的dex文件是dalvik虚拟机可识别的可执行文件,我们主要也是对dex文件进行逆向,分析其代码逻辑、更改其逻辑做一些分析、破解之类的行为

工具

  • apktool

  • androidkiller

  • jeb
  • jadx
  • GDA
  • smali/baksmali
  • ....
  • 破解流程

    1. 反编译apk
    2. 定位关键代码
    3. 功能分析
    4. smali修改
    5. 重打包、签名、安装

    例子1,广告破解

    这里我们用一个去广告的例子,简单的过一下流程

  • 反编译
  • java -jar apktool.jar d xxx.apk -o out


    定位关键代码的方法很多,这里我用的是一个开源的小工具,原理是注入+栈追踪,我们可以先用字符串大法先随意拼接以下ad字符串大致定位一下,可能会在哪个文件夹中,挑选出下面包含类所在文件夹批量注入日志打印方法


    python3 inject_log.py -r .\out\smali\com\youdo

  • 由于我们的目的是去广告,所以我们需要打开一个视频,查看日志中,广告开始播放时调用了哪些方法,这里我们就这样不断的去缩小我们过滤的范围,这里不做太多的赘述,最终确定到parsead方法,我们在返回的地方加一条指令const/4 v0, 0x0,让返回始终为null

    const/4 v0, 0x0
    return-object v0
  • CTF-CrakeMe流程

    首先针对不同逆向,我们需要清楚逆向的目的,在crakeme赛题中,我们主要目的是找到flag,具体需要我们找到打印输出的地方,在其周围找到判断逻辑,然后就是最主要的一部分逆向算法,算出flag

    1.反编译

    2.定位关键代码

    3.逆向算法

    例子2

    反编译后,查看AndroidManifest.xml找到入口 点

    <activity android:label="@h/a" android:name="ctf.bobbydylan.M">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN"/>
                    <category android:name="android.intent.category.LAUNCHER"/>
                </intent-filter>


    进入这个Activity类中,我们根据执行逻辑,如果有static{}和构造方法,我们可以看一眼里面除了初始化是否还有别的东西,这里并没有这两个调用,我们直接去onCreate方法中,这个Acitivity创建时调用的方法

    分析代码:主要有个开启服务的方法,进入这个P服务类,扫一眼生命周期中用到的方法,并没有值得留意的东西,直接看onStartCommand方法,服务启动时执行的方法,这里是循环播放res/raw目录下的音频文件bodylan,这个类分析到这,不是什么重要的类,接着往下看

    public void onCreate(Bundle bundle) {
            super.onCreate(bundle);
            setContentView(R.layout.main);
            startService(new Intent(this, P.class));
            ((Button) findViewById(R.id.button)).setOnClickListener(new a(this, (TextView) findViewById(R.id.et)));
        }
    public int onStartCommand(Intent intent, int i, int i2) {
            try {
                if (this.a == null) {
                    this.a = MediaPlayer.create(this, R.raw.bobdylan);
                    this.a.start();
                    this.a.setLooping(true);
                }
            } catch (Exception e) {
            }
            return super.onStartCommand(intent, i, i2);
        }


    按钮控件设置监听方法,在Jadx-Gui中右键->Find Usage找到方法定义的地方,可以从中文字符看出我们找到了主要逻辑,我们的目的就是打印出正确二字,从这个逻辑中可以看出,我们必须保证this.b.check不能抛出异常才行,下面才是crakeme真正开始的地方,分析这个check方法

    a(M m, TextView textView) {
            this.b = m;
            this.a = textView;
        }
        public void onClick(View view) {
            try {
                this.b.check(this.a.getText().toString());
                new Builder(this.b).setMessage("正确").setNeutralButton("OK", null).create().show();
            } catch (Exception e) {
                new Builder(this.b).setMessage("错误").setNeutralButton("OK", null).create().show();
            }
        }


    check

    我们必须保证不抛出异常

    满足下面注释的两个条件即可

    public void check(String str) {
            int i = 0;
            //条件1:输入字符串长度必须等于16
            if (str.length() != 16) {
                throw new RuntimeException();
            }
            String str2 = "";
            try {
                    str2 = getKey();
            } catch (Exception e) {
                str2 = getKey();
                System.arraycopy(str2, 0, str, 5, 5);
            }
            int[] iArr = new int[16];
            iArr[0] = 0;
            iArr[12] = 14;
            iArr[10] = 7;
            iArr[14] = 15;
            iArr[15] = 42;
            try {
                iArr[1] = 3;
                iArr[5] = 5;
                System.out.println();
            } catch (Exception e2) {
                iArr[5] = 37;
                iArr[1] = 85;
            }
            iArr[6] = 15;
            iArr[2] = 13;
            iArr[3] = 19;
            iArr[11] = 68;
            iArr[4] = 85;
            iArr[13] = 5;
            iArr[9] = 7;
            iArr[7] = 78;
            iArr[8] = 22;
            //条件2:iArr数组&255必须和后面这串计算相等
            while (i < str.length()) {
                if ((iArr[i] & 255) != ((str.charAt(i) ^ str2.charAt(i % str2.length())) & 255)) {
                    throw new RuntimeException();
                }
                i++;
            }
        }
     public String getKey() {
            return "bobbydylan";
        }


    根据面的分析写出第一版算不出结果的解密脚本,然后我用androidkiller打开这个apk文件,看汇编代码发现这个getKey不是调用M类中的方法,因为是个乱码,实际上是调用了父类中继承的方法

    iArr = [0, 3, 13, 19, 85, 5, 15, 78, 22, 7, 7, 68, 14, 5, 15, 42]
    chArr = ['b', 'o', 'b', 'b', 'y', 'd', 'y', 'l', 'a', 'n']
    for i in iArr:
        number = 0
        while(True):
            if iArr[i] == number ^ ord(chArr[i%len('bobbydylan')]):
                print(chr(number))
                if i == len(iArr):
                    print("end")
                    exit(0)
                break
            number += 1


    解密算法,结果blow,in the winD

    iArr = [0, 3, 13, 19, 85, 5, 15, 78, 22, 7, 7, 68, 14, 5, 15, 42]
    chArr = ['b', 'o', 'b', 'd', 'y', 'l', 'a', 'n']
    for i in range(len(iArr)):
        number = 0
        while(True):
            if iArr[i] == number ^ ord(chArr[i%len('bobdylan')]):
                print(chr(number))
                if i == len(iArr):
                    print("end")
                    exit(0)
                break
            number += 1


    小结

    【1】当java层代码不能给我们正确答案是,可以阅读smali代码

    病毒分析

    根据这几篇文章的逆向流程来分析病毒即可

    分析流程

    1. 大致阅览安装包内有哪些文件。资源目录raw、lib等
    2. 看AndroidManifest.xml文件,看有哪些注册组件,主要看server组件和receiver组件,详细分析组件行为
    3. 从入口类触发看逻辑
    4. 汇总

    【1】zoopark https://www.freebuf.com/articles/system/184286.html

    【2】锁屏病毒 https://www.freebuf.com/articles/others-articles/199515.html

    小结

    上面从具体逆向的一些分支的实现中来了解一下java层的具体实现,难度其实没那么大,但是需要对Android开发一些基础知识有一定的掌握


    转自先知社区

    打赏我,让我更有动力~

    0 条回复   |  直到 2019-7-1 | 1074 次浏览
    登录后才可发表内容
    返回顶部 投诉反馈

    © 2016 - 2024 掌控者 All Rights Reserved.