前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android逆向入门篇--java层静态分析

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

原创
作者头像
迅达集团
修改2019-07-01 18:40:29
9980
修改2019-07-01 18:40:29
举报
文章被收录于专栏:工作专用1

[TOC]

概述

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

java层

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

工具

  • apktool
  • androidkiller
  • jeb
  • jadx
  • GDA
  • smali/baksmali
  • ....

破解流程

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

例子1,广告破解

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

  • 反编译
代码语言:javascript
复制
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找到入口 点

代码语言:javascript
复制
<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,这个类分析到这,不是什么重要的类,接着往下看

代码语言:javascript
复制
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)));
    }
代码语言:javascript
复制
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方法

代码语言:javascript
复制
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

我们必须保证不抛出异常

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

代码语言:javascript
复制
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类中的方法,因为是个乱码,实际上是调用了父类中继承的方法

代码语言:javascript
复制
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

代码语言:javascript
复制
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

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 概述
  • java层
    • 工具
      • 破解流程
        • 例子1,广告破解
      • CTF-CrakeMe流程
        • 例子2
        • 小结
      • 病毒分析
        • 分析流程
    • 小结
    • 参考
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档