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

2019-06-30 约 2033 字 预计阅读 5 分钟

声明:本文 【Android逆向入门篇–java层静态分析】 由作者 yong夜 于 2019-07-01 06:02:00 首发 先知社区 曾经 浏览数 1 次

感谢 yong夜 的辛苦付出!

[TOC]

概述

了解了编译、打包、签名、安装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开发一些基础知识有一定的掌握

参考

【1】批量注入栈跟踪小工具

【2】crakeme赛题 https://bbs.pediy.com/thread-251787-1.htm

关键词:[‘安全技术’, ‘移动安全’]


author

旭达网络

旭达网络技术博客,曾记录各种技术问题,一贴搞定.
本文采用知识共享署名 4.0 国际许可协议进行许可。

We notice you're using an adblocker. If you like our webite please keep us running by whitelisting this site in your ad blocker. We’re serving quality, related ads only. Thank you!

I've whitelisted your website.

Not now