BUUCTF笔记之Reverse(Java及android部分)

先把这最简单的拿下,将来或许会继续研究reverse

1.easyre

拖进winhex,直接搜flag:

2.Java逆向解密

下载下来一个字节码文件,拖进jdgui得到反编译的源码:

import java.util.ArrayList;
import java.util.Scanner;


public class Reverse
{
  public static void main(String[] args) {
    Scanner s = new Scanner(System.in);
    System.out.println("Please input the flag );
    String str = s.next();
    System.out.println("Your input is );
    System.out.println(str);
    char[] stringArr = str.toCharArray();
    Encrypt(stringArr);
  }
  
  public static void Encrypt(char[] arr) {
    ArrayList<Integer> Resultlist = new ArrayList<>();
    
    for (int i = 0; i < arr.length; i++) {
      int result = arr[i] + 64 ^ 0x20;
      Resultlist.add(Integer.valueOf(result));
    } 
    int[] KEY = { 180, 136, 137, 147, 191, 137, 147, 191, 148, 136, 133, 191, 134, 140, 129, 135, 191, 65 };
    ArrayList<Integer> KEYList = new ArrayList<>();
    for (int j = 0; j < KEY.length; j++) {
      KEYList.add(Integer.valueOf(KEY[j]));
    }
    System.out.println("Result:");
    if (Resultlist.equals(KEYList)) {
      System.out.println("Congratulations);
    } else {
      System.err.println("Error);
    } 
  }
}

看了一下是一道送分题,payload:

int[] KEY = { 180, 136, 137, 147, 191, 137, 147, 191, 148, 136, 133, 191, 134, 140, 129, 135, 191, 65 };
ArrayList<Integer> keys = new ArrayList<Integer>(KEY.length);
String s = "";
for(int i=0;i<KEY.length;i++) {
    s+=(char)(KEY[i]-64^0x20);
}
System.out.println(s);

得到flag:

3.[SWPU2019]Android1

现在的android题真是有毒啊。

直接上IDA里面F5之后的代码了:

int __cdecl Java_com_example_ndktest2_MainActivity_Encrypt(_JNIEnv *a1)
{
  char *v1; // eax
  char *v2; // eax
  char *v4; // [esp+24h] [ebp-B8h]
  char v5[13]; // [esp+2Dh] [ebp-AFh] BYREF
  char dest[6]; // [esp+3Ah] [ebp-A2h] BYREF
  char v7[128]; // [esp+40h] [ebp-9Ch] BYREF
  char v8[28]; // [esp+C0h] [ebp-1Ch] BYREF

  strcpy(dest, "flag{");
  strcpy(&v5[7], "wllm");
  strcpy(v8, "welcome");
  strcpy(v5, "newbee");
  base64_encode(v5, v7);
  v4 = strcat(dest, &v5[7]);
  v1 = strcat(v8, v5);
  v2 = strcat(v4, v1);
  return _JNIEnv::NewStringUTF(a1, v2);
}

分析一波:先把"flag{"赋值给dest

v5有13个字节,在strcpy(&v5[7], "wllm");结束后v5在内存里是这样的:

_ _ _ _ _ _ _ w l l m _ _

然后把"welcome"赋值给v8

然后把"newbee"赋值给v5

所以这时v5是这样的

n e w b e e _ w l l m _ _

然后对v5进行一次base64操作得到:v7="bmV3YmVld2xsbQ=="

接下来就是拼接:

v4="flag{wllm"

v1="welcomenewbeewllm"

v2="flag{wllmwelcomenewbeewllm"

然后失败了。。。。。

试一下frida hook函数:

写一个js脚本劫持java的equals函数:

console.log("Script loaded successfully ");
Java.perform(function x() {//hook思路3:系统打桩,监控系统api equals方法的调用,每调用一次equals方法就打印出一次equals对比的值
    var String = Java.use('java.lang.String')//定位到要hook的类名
    String.equals.implementation = function (arg1) {//equals就是我们要hook的函数
    console.log("your input : " + this.toString());
    console.log("I'm the real key : " + arg1);//打印出来真实的解锁码
    var ret = this.equals(arg1);
    return ret;//返回值
    }
});

然后在攻击机上执行命令frida -U -f com.example.ndktest2 -l fuck.js:

然后输入%resume让程序开始执行。

然后在验证框里面随便输入,equals方法被调用时就被劫持了,打印出来NDK返回的真实flag:

flag{YouaretheB3ST}。

4.[SWPU2019]Android2

这题也是神坑,到现在都没做出来,先把进展记录一下。

这题下载下来的APK并不是真的,真正的APK在假的APK的res文件夹里面。

把APK改成ZIP之后解压得到

把app2.txt改成apk之后得到真正的APK。拖进jadx分析一波MainActivity:

package com.example.thousandyearsago;

import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    /* renamed from: a */
    final long f32a = 1000000000;
    private Handler handler;

    public native String StringFromJNI();

    public native String stringFromJNI();

    static {
        System.loadLibrary("native-lib");
    }

    /* access modifiers changed from: protected */
    @Override // androidx.core.app.ComponentActivity, androidx.appcompat.app.AppCompatActivity, androidx.fragment.app.FragmentActivity
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(C0272R.layout.activity_main);
        this.handler = new Handler();
        ((Button) findViewById(C0272R.C0274id.button)).setOnClickListener(this);
        this.handler.postDelayed(new Inner(), 1000000000);
    }

    public void onClick(View v) {
        if (v.getId() == C0272R.C0274id.button) {
            Toast.makeText(this, "好像卡死了啊!!!", 0).show();
        }
    }

    class Inner implements Runnable {
        Inner() {
        }

        public void run() {
            if (1000000000 <= 1000000) {
                MainActivity mainActivity = MainActivity.this;
                Toast.makeText(mainActivity, mainActivity.StringFromJNI(), 1).show();
            }
        }
    }
}

不出意外又是native。这题的MainActivity在启动之后监听按钮,点击则弹出“好像卡死了啊”,同时延时277个小时(100万秒)启动一个线程,这个线程只做一件事,如果10亿小于100万则得到flag。这是一个假条件,现在需要想办法把延时277小时启动给改掉,同时让线程里的代码能够执行。

 5.[SCTF2019]Strange apk

这题下载下来先看Manifest.xml配置文件找程序入口:

demo包下,但是反编译出来的包里并没有demo包,只有hello:

这里就又有一个新知识点了:考虑APP动态释放文件的可能性(参考PC下的压缩壳)。

因此考虑使用反射大师把运行时的dex文件dump下来分析。

其他writeup都是说安装Xposed和反射大师把实际运行的dex文件dump下来分析,但是我实际操作下来发现windows操作系统下,只用模拟器的情况下根本没有任何操作性。

为了安装反射大师,我尝试了逍遥、雷电、mumu、夜神的各个版本的安卓,全是坑,根本用不成。我装这些模拟器之后,处理器型号统统是x86,在系统版本为7.1时,官方安装器和神盾装的Xposed框架无一例外会导致模拟器重启卡死在99%然后崩溃。在系统版本为5.1时,反射大师也正常装进去了,但是在导出dex文件的时候提示没有写权限,推测是安卓5.0和7.0挂载磁盘的方式不一样导致的,这个根本没法解决。

搞了半天还是回归神器frida吧:

frida-ps -U查看目标进程的PID为3742,所以下一步直接frida-dexdump -p 3742

把dex文件dump下来,这不比什么反射大师香吗?!!!:

拖进jadx分析:

package sctf.demo.myapplication;

import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

public class s extends AppCompatActivity {
    /* access modifiers changed from: protected */
    @Override // android.support.v7.app.AppCompatActivity, android.support.v4.app.SupportActivity, android.support.v4.app.FragmentActivity
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.textView);
        final EditText ed = (EditText) findViewById(R.id.editText);
        ((Button) findViewById(R.id.button)).setOnClickListener(new View.OnClickListener() {
            /* class sctf.demo.myapplication.s.AnonymousClass1 */

            public void onClick(View v) {
                String s1 = BuildConfig.FLAVOR;
                String s2 = BuildConfig.FLAVOR;
                int i = 0;
                String s = ed.getText().toString();
                if (s.length() == 30) {
                    while (i < 12) {
                        s1 = s1 + s.charAt(i);
                        i++;
                    }
                    String s12 = f.sctf(s1);
                    while (i < 30) {
                        s2 = s2 + s.charAt(i);
                        i++;
                    }
                    if (s12.equals("c2N0ZntXM2xjMG1l")) {
                        Intent intent = new Intent();
                        intent.putExtra("data_return", s2);
                        s.this.setResult(-1, intent);
                        s.this.finish();
                        return;
                    }
                    Toast.makeText(s.this.getApplicationContext(), "something wrong", 1).show();
                    return;
                }
                Toast.makeText(s.this.getApplicationContext(), "something wrong", 1).show();
            }
        });
    }
}

这段代码也不难,flag长度为30,前12位进行base64生成,值为c2N0ZntXM2xjMG1l,解码得sctf{W3lc0me,后18位使用 Intent.putExtra()方法传了出去:

intent是Android程序中各组件之间进行交互的一种重要方式,一般被用来启动活动、启动服务以及发送广播等;intent在启动Activity的时候可以传递数据,比如说给另一个Activity传递数据,那么活动与活动之间是怎样进行数据传递的呢?
这时候就需要用到putExtra()方法。intent中提供一系列的putExtra()方法的重载,可以把想要传递的数据暂存在intent中,当另一个活动启动后,再把这些数据从intent缓存中取出即可。
putExtra("A", B)方法中,AB为键值对,第一个参数为键名,第二个参数为键对应的值,这个值才是真正要传递的数据。

搜索键“data_return”得到s2被传到了这里:

看一下encode方法:

public static String encode(String str, String key) {
        int s = str.length();
        int c = key.length();
        StringBuilder t = new StringBuilder();
        for (int f = 0; f < s; f++) {
            t.append(str.charAt(f));
            t.append(key.charAt(f / c));
        }
        return t.toString();
    }

这里的key是这么来的:

MessageDigest md = MessageDigest.getInstance("MD5");
md.update("syclover".getBytes());
key = new BigInteger(1, md.digest()).toString(16);

自己运行一下得到key=8bfc8af07bca146c937f283b8ec768d4:

encode方法就是对stf的每一位进行遍历,然后在中间穿插8:

所以把~8t808_8A8n848r808i8d8-8w808r8l8d8}8里面偶数位的8去掉就行:

~t0_An4r0id-w0rld}

得到佛莱格:flag{W3lc0me~t0_An4r0id-w0rld}

这道题的启示是,不能迷信查壳工具,我开始先用查壳工具没查出壳来,以为没有壳,被耍得团团转。实际上进来先找配置文件看程序入口,如果入口的类和反编译出来的不一样,那十有八九就是有壳,直接frida-dexdump把运行中释放出来的dex文件dump下来分析。

6.简单注册器

把apk拖进jadx,flag直接就在java层直接给出:

int flag = 1;
        if (flag == 1) {
            char[] x = "dd2940c04462b4dd7c450528835cca15".toCharArray();
            x[2] = (char) ((x[2] + x[3]) - 50);
            x[4] = (char) ((x[2] + x[5]) - 48);
            x[30] = (char) ((x[31] + x[9]) - 48);
            x[14] = (char) ((x[27] + x[28]) - 97);
            for (int i = 0; i < 16; i++) {
                char a = x[31 - i];
                x[31 - i] = x[i];
                x[i] = a;
            }
            System.out.println(String.valueOf(x));

运行拿flag

7.findit

具体同上。代码:

final char[] a = {'T', 'h', 'i', 's', 'I', 's', 'T', 'h', 'e', 'F', 'l', 'a', 'g', 'H', 'o', 'm', 'e'};
        final char[] b = {'p', 'v', 'k', 'q', '{', 'm', '1', '6', '4', '6', '7', '5', '2', '6', '2', '0', '3', '3', 'l', '4', 'm', '4', '9', 'l', 'n', 'p', '7', 'p', '9', 'm', 'n', 'k', '2', '8', 'k', '7', '5', '}'};
		char[] x = new char[17];
        char[] y = new char[38];
        for (int i = 0; i < 17; i++) {
            if ((a[i] < 'I' && a[i] >= 'A') || (a[i] < 'i' && a[i] >= 'a')) {
                x[i] = (char) (a[i] + 18);
            } else if ((a[i] < 'A' || a[i] > 'Z') && (a[i] < 'a' || a[i] > 'z')) {
                x[i] = a[i];
            } else {
                x[i] = (char) (a[i] - '\b');
            }
        }
            for (int i2 = 0; i2 < 38; i2++) {
                if ((b[i2] < 'A' || b[i2] > 'Z') && (b[i2] < 'a' || b[i2] > 'z')) {
                    y[i2] = b[i2];
                } else {
                    y[i2] = (char) (b[i2] + 16);
                    if ((y[i2] > 'Z' && y[i2] < 'a') || y[i2] >= 'z') {
                        y[i2] = (char) (y[i2] - 26);
                    }
                }
            }
            System.out.println(String.valueOf(y));

运行得flag

8.rsa

openssl解析公钥

然后yafu分解大数。

然后生成私钥文件:

用私钥文件解密,得到flag:

ag{decrypt_256}


版权声明:本文为shuaicenglou3032原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。