首页
社区
课程
招聘
[原创]Google CTF 2020 资格赛 reverse_android
发表于: 2021-2-5 17:07 10040

[原创]Google CTF 2020 资格赛 reverse_android

2021-2-5 17:07
10040

这是2020年Google CTF资格赛中的一道安卓reverse题,纯java,没有so。虽然很简单,但因为是我第一次做题,所以并不顺利。在此给大家分享一下我的思路,也算是一次总结吧。

首先使用特征函数定位——OnClick

使用jadx反编译apk(无壳无混淆),搜索OnClick,定位到了验证的位置

验证部分所在文件的完整代码如下:

"验证成功"代表验证成功,两处地方有"验证成功",我们先分析上面一个"验证成功"附近的代码。

代码如下:

代码解读:
该部分代码根据作用分为两部分,第一部分生成Key,第二部分验证Key

第一部分
首先构造一个StringBuilder来存放Key,接着new了一个Object对象数组并初始化,循环取数组中的元素强转为Character类型,并调用charValue()方法得到字符添加到Key中。

第一部分会出现java.lang.ClassCastException异常,java.lang.Integer类不能转换为java.lang.Character类,因为他们并不存在子父类关系,所以此处是一个障眼法。

通过代码发现,出现的异常的代码被try-catch结构包裹了起来,所以出现的异常会被捕获,所以我们继续看catch部分的代码

代码如下:

代码解读:

第一部分:flag初步处理

首先得到的信息是flag的长度必须为48,紧接着是for循环,每次对flag中的4个字符进行如下处理后,放到this.f2[i]

补充一个信息:
this.f2ActivityC0000的构造函数中被new了12个元素;this.f0class被初始化,一共12个元素;this.f1被初始化为0

所以对flag初步处理后,我们得到了12个long型数据

第二部分:flag进一步处理

代码中用到的递归函数R.m0(a,b)的代码如下:

首先是把上面flag初步处理后得到的数据this.f2中的一个元素(this.f1作为索引)和4294967296L一起放入递归函数R.m0(a,b)中进行处理,返回一个long型数组,并取其中的第一个元素,接着把这第一个元素做一系列的算术运算,将最终得到的运算结果和this.f0class中的元素(this.f1作为索引)进行对比,如果不相等则验证失败。如果验证成功,则继续第三部分

第三部分:判断flag是否验证完毕

第二部分中的验证,每次只验证1个元素(即验证flag的4个字符),所以需要循环12次才能验证完毕

this.f1第二部分作为数组索引,同时在这里也作为循环判断条件,this.f2.length是12,如果循环次数<12,则主动抛出异常,但没有捕获异常,到这里程序就崩了。站在程序设计者的角度来看,抛出异常之后,应该捕获异常,然后继续验证flag。所以我觉得异常应该是被捕获并处理的了,只是可能反编译出来的Java代码有问题,所以直接看smali代码

这是抛出异常throw new RuntimeException();附近的代码,我们发现,异常是被catch_11捕获了的

catch_11处,我们发现又直接跳转到goto_205

我们发现goto_205处的代码正是第一部分:flag初步处理处的代码,所以循环成立,flag的循环验证通过异常来进行循环的

通过上面的分析,可以得出flag的验证流程图如下:

通过分析可知,我们的flag是由12组,每组4个字符组成的。每次循环取其中的4个字符通过一系列的运算,最终得到一个结果并与数组{40999019, 2789358025L, 656272715, 18374979, 3237618335L, 1762529471, 685548119, 382114257, 1436905469, 2126016673, 3318315423L, 797150821}中的元素做比较,一共12次循环。

我采用暴力枚举的方式算出flag,算法如下:

之所以采用ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!?\_{}作为迭代数,是因为这些是flag中常见的字符

最终得到的flag为CTF{y0u_c4n_k3ep_y0u?_m4gic_1_h4Ue_laser_b3ams!},说一个小技巧,adb shell input text "CTF{y0u_c4n_k3ep_y0u?_m4gic_1_h4Ue_laser_b3ams!}"可以快速输入字符到编辑框

Google CTF 2020: Android

apk见附件

 
 
 
 
package com.google.ctf.sandbox;
 
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
 
/* renamed from: com.google.ctf.sandbox.ő  reason: contains not printable characters */
public class ActivityC0000 extends Activity {
 
    /* renamed from: class  reason: not valid java name */
    long[] f0class;
 
    /* renamed from: ő  reason: contains not printable characters */
    int f1;
 
    /* renamed from: ő  reason: contains not printable characters and collision with other field name */
    long[] f2;
 
    public ActivityC0000() {
        try {
            this.f0class = new long[]{40999019, 2789358025L, 656272715, 18374979, 3237618335L, 1762529471, 685548119, 382114257, 1436905469, 2126016673, 3318315423L, 797150821};
            this.f2 = new long[12];
            this.f1 = 0;
        } catch (I unused) {
        }
    }
 
    /* access modifiers changed from: protected */
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        final EditText editText = (EditText) findViewById(R.id.editText);
        final TextView textView = (TextView) findViewById(R.id.textView);
        ((Button) findViewById(R.id.button)).setOnClickListener(new View.OnClickListener() {
            /* class com.google.ctf.sandbox.ActivityC0000.AnonymousClass1 */
 
            public void onClick(View v) {
                ActivityC0000.this.f1 = 0;
                try {
                    StringBuilder keyString = new StringBuilder();
                    for (Object chr : new Object[]{65, 112, 112, 97, 114, 101, 110, 116, 108, 121, 32, 116, 104, 105, 115, 32, 105, 115, 32, 110, 111, 116, 32, 116, 104, 101, 32, 102, 108, 97, 103, 46, 32, 87, 104, 97, 116, 39, 115, 32, 103, 111, 105, 110, 103, 32, 111, 110, 63}) {
                        keyString.append(((Character) chr).charValue());
                    }
                    if (editText.getText().toString().equals(keyString.toString())) {
                        textView.setText("验证成功");
                    } else {
                        textView.setText("验证失败");
                    }
                } catch (J | Error | Exception unused) {
                    String flagString = editText.getText().toString();
                    if (flagString.length() != 48) {
                        textView.setText("验证失败");
                        return;
                    }
                    for (int i = 0; i < flagString.length() / 4; i++) {
                        ActivityC0000.this.f2[i] = (long) (flagString.charAt((i * 4) + 3) << 24);
                        long[] jArr = ActivityC0000.this.f2;
                        jArr[i] = jArr[i] | ((long) (flagString.charAt((i * 4) + 2) << 16));
                        long[] jArr2 = ActivityC0000.this.f2;
                        jArr2[i] = jArr2[i] | ((long) (flagString.charAt((i * 4) + 1) << '\b'));
                        long[] jArr3 = ActivityC0000.this.f2;
                        jArr3[i] = jArr3[i] | ((long) flagString.charAt(i * 4));
                    }
                    ActivityC0000 r6 = ActivityC0000.this;
                    if (((R.m0(ActivityC0000.this.f2[ActivityC0000.this.f1], 4294967296L)[0] % 4294967296L) + 4294967296L) % 4294967296L != ActivityC0000.this.f0class[ActivityC0000.this.f1]) {
                        textView.setText("验证失败");
                        return;
                    }
                    ActivityC0000.this.f1++;
                    if (ActivityC0000.this.f1 >= ActivityC0000.this.f2.length) {
                        textView.setText("验证成功");
                        return;
                    }
                    throw new RuntimeException();
                }
            }
        });
    }
}
package com.google.ctf.sandbox;
 
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
 
/* renamed from: com.google.ctf.sandbox.ő  reason: contains not printable characters */
public class ActivityC0000 extends Activity {
 
    /* renamed from: class  reason: not valid java name */
    long[] f0class;
 
    /* renamed from: ő  reason: contains not printable characters */
    int f1;
 
    /* renamed from: ő  reason: contains not printable characters and collision with other field name */
    long[] f2;
 
    public ActivityC0000() {
        try {
            this.f0class = new long[]{40999019, 2789358025L, 656272715, 18374979, 3237618335L, 1762529471, 685548119, 382114257, 1436905469, 2126016673, 3318315423L, 797150821};
            this.f2 = new long[12];
            this.f1 = 0;
        } catch (I unused) {
        }
    }
 
    /* access modifiers changed from: protected */
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        final EditText editText = (EditText) findViewById(R.id.editText);
        final TextView textView = (TextView) findViewById(R.id.textView);
        ((Button) findViewById(R.id.button)).setOnClickListener(new View.OnClickListener() {
            /* class com.google.ctf.sandbox.ActivityC0000.AnonymousClass1 */
 
            public void onClick(View v) {
                ActivityC0000.this.f1 = 0;
                try {
                    StringBuilder keyString = new StringBuilder();
                    for (Object chr : new Object[]{65, 112, 112, 97, 114, 101, 110, 116, 108, 121, 32, 116, 104, 105, 115, 32, 105, 115, 32, 110, 111, 116, 32, 116, 104, 101, 32, 102, 108, 97, 103, 46, 32, 87, 104, 97, 116, 39, 115, 32, 103, 111, 105, 110, 103, 32, 111, 110, 63}) {
                        keyString.append(((Character) chr).charValue());
                    }
                    if (editText.getText().toString().equals(keyString.toString())) {
                        textView.setText("验证成功");
                    } else {
                        textView.setText("验证失败");
                    }
                } catch (J | Error | Exception unused) {
                    String flagString = editText.getText().toString();
                    if (flagString.length() != 48) {
                        textView.setText("验证失败");
                        return;
                    }
                    for (int i = 0; i < flagString.length() / 4; i++) {
                        ActivityC0000.this.f2[i] = (long) (flagString.charAt((i * 4) + 3) << 24);
                        long[] jArr = ActivityC0000.this.f2;
                        jArr[i] = jArr[i] | ((long) (flagString.charAt((i * 4) + 2) << 16));
                        long[] jArr2 = ActivityC0000.this.f2;
                        jArr2[i] = jArr2[i] | ((long) (flagString.charAt((i * 4) + 1) << '\b'));
                        long[] jArr3 = ActivityC0000.this.f2;
                        jArr3[i] = jArr3[i] | ((long) flagString.charAt(i * 4));
                    }
                    ActivityC0000 r6 = ActivityC0000.this;
                    if (((R.m0(ActivityC0000.this.f2[ActivityC0000.this.f1], 4294967296L)[0] % 4294967296L) + 4294967296L) % 4294967296L != ActivityC0000.this.f0class[ActivityC0000.this.f1]) {
                        textView.setText("验证失败");
                        return;
                    }
                    ActivityC0000.this.f1++;
                    if (ActivityC0000.this.f1 >= ActivityC0000.this.f2.length) {
                        textView.setText("验证成功");
                        return;
                    }
                    throw new RuntimeException();
                }
            }
        });
    }
}
 
try {
     StringBuilder keyString = new StringBuilder();
     for (Object chr : new Object[]{65, 112, 112, 97, 114, 101, 110, 116, 108, 121, 32, 116, 104, 105, 115, 32, 105, 115, 32, 110, 111, 116, 32, 116, 104, 101, 32, 102, 108, 97, 103, 46, 32, 87, 104, 97, 116, 39, 115, 32, 103, 111, 105, 110, 103, 32, 111, 110, 63}) {
         keyString.append(((Character) chr).charValue());
     }
     if (editText.getText().toString().equals(keyString.toString())) {
         textView.setText("验证成功");
     } else {
         textView.setText("验证失败");
     }
 } catch (J | Error | Exception unused) {
     ......
     }
try {
     StringBuilder keyString = new StringBuilder();
     for (Object chr : new Object[]{65, 112, 112, 97, 114, 101, 110, 116, 108, 121, 32, 116, 104, 105, 115, 32, 105, 115, 32, 110, 111, 116, 32, 116, 104, 101, 32, 102, 108, 97, 103, 46, 32, 87, 104, 97, 116, 39, 115, 32, 103, 111, 105, 110, 103, 32, 111, 110, 63}) {
         keyString.append(((Character) chr).charValue());
     }
     if (editText.getText().toString().equals(keyString.toString())) {
         textView.setText("验证成功");
     } else {
         textView.setText("验证失败");
     }
 } catch (J | Error | Exception unused) {
     ......
     }
 
 
 
 
String flagString = editText.getText().toString();
  if (flagString.length() != 48) {
      textView.setText("验证失败");
      return;
  }
  for (int i = 0; i < flagString.length() / 4; i++) {
      ActivityC0000.this.f2[i] = (long) (flagString.charAt((i * 4) + 3) << 24);
      long[] jArr = ActivityC0000.this.f2;
      jArr[i] = jArr[i] | ((long) (flagString.charAt((i * 4) + 2) << 16));
      long[] jArr2 = ActivityC0000.this.f2;
      jArr2[i] = jArr2[i] | ((long) (flagString.charAt((i * 4) + 1) << '\b'));
      long[] jArr3 = ActivityC0000.this.f2;
      jArr3[i] = jArr3[i] | ((long) flagString.charAt(i * 4));
  }
  ActivityC0000 r6 = ActivityC0000.this;
  if (((R.m0(ActivityC0000.this.f2[ActivityC0000.this.f1], 4294967296L)[0] % 4294967296L) + 4294967296L) % 4294967296L != ActivityC0000.this.f0class[ActivityC0000.this.f1]) {
      textView.setText("验证失败");
      return;
  }
  ActivityC0000.this.f1++;
  if (ActivityC0000.this.f1 >= ActivityC0000.this.f2.length) {
      textView.setText("验证成功");
      return;
  }
  throw new RuntimeException();
String flagString = editText.getText().toString();
  if (flagString.length() != 48) {
      textView.setText("验证失败");
      return;
  }
  for (int i = 0; i < flagString.length() / 4; i++) {
      ActivityC0000.this.f2[i] = (long) (flagString.charAt((i * 4) + 3) << 24);
      long[] jArr = ActivityC0000.this.f2;
      jArr[i] = jArr[i] | ((long) (flagString.charAt((i * 4) + 2) << 16));
      long[] jArr2 = ActivityC0000.this.f2;
      jArr2[i] = jArr2[i] | ((long) (flagString.charAt((i * 4) + 1) << '\b'));
      long[] jArr3 = ActivityC0000.this.f2;
      jArr3[i] = jArr3[i] | ((long) flagString.charAt(i * 4));
  }
  ActivityC0000 r6 = ActivityC0000.this;
  if (((R.m0(ActivityC0000.this.f2[ActivityC0000.this.f1], 4294967296L)[0] % 4294967296L) + 4294967296L) % 4294967296L != ActivityC0000.this.f0class[ActivityC0000.this.f1]) {
      textView.setText("验证失败");
      return;
  }
  ActivityC0000.this.f1++;

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

最后于 2021-2-5 17:31 被genliese编辑 ,原因:
上传的附件:
收藏
免费 3
支持
分享
最新回复 (7)
雪    币: 503
活跃值: (2204)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
2
学习了 感谢
2021-2-22 13:38
0
雪    币: 329
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
3
大佬,你这个分析框架是啥?
2021-2-26 10:26
0
雪    币: 566
活跃值: (231)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
4
思源欲涩 大佬,你这个分析框架是啥?
不懂分析框架,但是如果你的意思是分析工具的话,用的应该是Jadx吧。
2021-2-27 10:12
0
雪    币: 2479
活跃值: (3099)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
5
思源欲涩 大佬,你这个分析框架是啥?
没有分析框架,分析工具就是jadx
2021-2-27 11:28
0
雪    币: 219
活跃值: (56)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
6

感谢分享。所以我也分享一点我知道的东西~
最后暴力破解这段,实际上是一个取模反元素的算法。意思大概是已知互质的两个数 a 和 n 的值,求一个 x 满足 a*x % n == 1。这个算法在这里对于 a 和 x 是完全对称的。
所以最后暴力破解这块,可以改成图中的样子。

不过话说回来,一般在讲算法的时候,上面取模范元素式子写作已知互质的两个数 a,b 求 x, y 满足 ax + by == 1, 这里的 b 就是前面式子的 n ,然后这个算法叫做扩展欧几里得算法,在 RSA 里有涉及到。

最后于 2021-3-5 23:41 被UndeadFu编辑 ,原因:
2021-3-5 23:35
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
7
jadx 是啥版本的 怎么我的直接jadx 打开 和 jeb3 打开 去反编译附件的apk 都是失败的
2021-3-6 09:49
0
雪    币: 2479
活跃值: (3099)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
8
r0-涛 jadx 是啥版本的 怎么我的直接jadx 打开 和 jeb3 打开 去反编译附件的apk 都是失败的
1.2.0
2021-3-6 11:05
0
游客
登录 | 注册 方可回帖
返回
//