WXjzc 发表于 2024-2-25 10:51

吾爱破解2024红包题安卓部分wp

本帖最后由 WXjzc 于 2024-2-25 11:15 编辑


> 高级题会不了一点儿

# 初级一
小猫游戏,改一下判断


将`t.LOSE`的值改为win,然后将`case i.LOSE`的代码段删掉,重新签名安装即可<br />游戏结束会播放原神启动,播完会输出flag

结果为`flag{happy_new_year_2024}`
# 初级二

flag是跟着签名走的,所以没法重新编译

看代码可以看到是出金启动`FlagActivity`

所以直接上objection强制启动

# 中级
实际是`网信柏鹭杯2021不重要的图形`的变种(不能说一模一样,只能说完全一致),不过这题可以很简单的得到密码,而原题的密码需要爆破16长度数组的md5,不过正如题目名一样,重点不在解锁,不解锁也可以

## 拿flag
反编译能得知assets目录下还有一个dex,这个dex的checksum校验不通过,需要在jadx里忽略校验

将assets目录下的dex拷贝的应用数据目录的`app_data`文件夹下,加载并且反射调用`com.zj.wuaipojie2024_2.C.isValidate`

再次反射,调用`com.zj.wuaipojie2024_2.A.d`

明显的有问题

回来发现做了修复

由于`1.dex`的checksum校验不通过,所以系统不会加载这个dex,也就是说不会触发这个修复方法,因此可以将这个修复的代码摘出来,单独进行修复,涉及到3个类
Main类,将fix和read从`com.zj.wuaipojie2024_2.C`中拿出来
```java
package org.example;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.lang.String;
import java.lang.Integer;
import java.nio.ByteBuffer;
import java.util.HashMap;

public class Main {

    private static File fix(ByteBuffer byteBuffer, int i, int i2, int i3) throws Exception {
      try {
            int intValue = D.getClassDefData(byteBuffer, i).get("class_data_off").intValue();
            HashMap<String, int[][]> classData = D.getClassData(byteBuffer, intValue);
            classData.get("direct_methods") = i3;
            byte[] encodeClassData = D.encodeClassData(classData);
            byteBuffer.position(intValue);
            byteBuffer.put(encodeClassData);
            byteBuffer.position(32);
            byte[] bArr = new byte;
            byteBuffer.get(bArr);
            byte[] sha1 = Utils.getSha1(bArr);
            byteBuffer.position(12);
            byteBuffer.put(sha1);
            int checksum = Utils.checksum(byteBuffer);
            byteBuffer.position(8);
            byteBuffer.putInt(Integer.reverseBytes(checksum));
            byte[] array = byteBuffer.array();
            File file = new File("D:\\Tools\\workspace\\ctf\\52\\", "decode2.dex");
            FileOutputStream fileOutputStream = new FileOutputStream(file);
            fileOutputStream.write(array);
            fileOutputStream.close();
            return file;
      } catch (Exception e) {
            e.printStackTrace();
            return null;
      }
    }

    private static ByteBuffer read() {
      try {
            File file = new File("D:\\Tools\\workspace\\ctf\\52", "classes.dex");
            if (file.exists()) {
                FileInputStream fileInputStream = new FileInputStream(file);
                byte[] bArr = new byte;
                fileInputStream.read(bArr);
                ByteBuffer wrap = ByteBuffer.wrap(bArr);
                fileInputStream.close();
                return wrap;
            }
            return null;
      } catch (Exception unused) {
            return null;
      }
    }

    public static void main(String[] args) throws Exception{
      fix(read(),0,3,7908);
      // fix(read(),1,1,8108);
    }
}
```
`com.zj.wuaipojie2024_2.D`类
```java
package org.example;

import java.lang.reflect.Array;
import java.nio.ByteBuffer;
import java.util.HashMap;

/* loaded from: assets/classes.dex */
public class D {
    public static HashMap<String, Integer> getClassDefData(ByteBuffer byteBuffer, int i) {
      if (byteBuffer == null) {
            throw new IllegalArgumentException("Buffer cannot be null");
      }
      byteBuffer.position(100);
      byteBuffer.position((i * 32) + Integer.reverseBytes(byteBuffer.getInt()));
      HashMap<String, Integer> hashMap = new HashMap<>();
      int reverseBytes = Integer.reverseBytes(byteBuffer.getInt());
      int reverseBytes2 = Integer.reverseBytes(byteBuffer.getInt());
      int reverseBytes3 = Integer.reverseBytes(byteBuffer.getInt());
      int reverseBytes4 = Integer.reverseBytes(byteBuffer.getInt());
      int reverseBytes5 = Integer.reverseBytes(byteBuffer.getInt());
      int reverseBytes6 = Integer.reverseBytes(byteBuffer.getInt());
      int reverseBytes7 = Integer.reverseBytes(byteBuffer.getInt());
      int reverseBytes8 = Integer.reverseBytes(byteBuffer.getInt());
      hashMap.put("class_idx", Integer.valueOf(reverseBytes));
      hashMap.put("access_flag", Integer.valueOf(reverseBytes2));
      hashMap.put("superclass_idx", Integer.valueOf(reverseBytes3));
      hashMap.put("interfaces_off", Integer.valueOf(reverseBytes4));
      hashMap.put("source_file_idx", Integer.valueOf(reverseBytes5));
      hashMap.put("annotation_off", Integer.valueOf(reverseBytes6));
      hashMap.put("class_data_off", Integer.valueOf(reverseBytes7));
      hashMap.put("static_values_off", Integer.valueOf(reverseBytes8));
      return hashMap;
    }

    public static HashMap<String, int[][]> getClassData(ByteBuffer byteBuffer, int i) {
      byteBuffer.position(i);
      int i2 = Utils.fromULEB128(byteBuffer);
      int i3 = Utils.fromULEB128(byteBuffer);
      int i4 = Utils.fromULEB128(byteBuffer);
      int i5 = Utils.fromULEB128(byteBuffer);
      int[][] iArr = (int[][]) Array.newInstance(Integer.TYPE, i2, 2);
      if (i2 > 0) {
            int i6 = 0;
            for (int i7 = 0; i7 < i2; i7++) {
                int[] encode_field = encode_field(byteBuffer);
                if (i7 == 0) {
                  i6 = encode_field;
                } else {
                  i6 += encode_field;
                }
                int[] iArr2 = new int;
                iArr2 = i6;
                iArr2 = encode_field;
                iArr = iArr2;
            }
      }
      int[][] iArr3 = (int[][]) Array.newInstance(Integer.TYPE, i3, 2);
      if (i3 > 0) {
            int i8 = 0;
            for (int i9 = 0; i9 < i3; i9++) {
                int[] encode_field2 = encode_field(byteBuffer);
                if (i9 == 0) {
                  i8 = encode_field2;
                } else {
                  i8 += encode_field2;
                }
                int[] iArr4 = new int;
                iArr4 = i8;
                iArr4 = encode_field2;
                iArr3 = iArr4;
            }
      }
      int[][] iArr5 = (int[][]) Array.newInstance(Integer.TYPE, i4, 3);
      if (i4 > 0) {
            int i10 = 0;
            for (int i11 = 0; i11 < i4; i11++) {
                int[] encode_method = encode_method(byteBuffer);
                if (i11 == 0) {
                  i10 = encode_method;
                } else {
                  i10 += encode_method;
                }
                int[] iArr6 = new int;
                iArr6 = i10;
                iArr6 = encode_method;
                iArr6 = encode_method;
                iArr5 = iArr6;
            }
      }
      int[][] iArr7 = (int[][]) Array.newInstance(Integer.TYPE, i5, 3);
      if (i5 > 0) {
            int i12 = 0;
            for (int i13 = 0; i13 < i3; i13++) {
                int[] encode_method2 = encode_method(byteBuffer);
                if (i13 == 0) {
                  i12 = encode_method2;
                } else {
                  i12 += encode_method2;
                }
                int[] iArr8 = new int;
                iArr8 = i12;
                iArr8 = encode_method2;
                iArr8 = encode_method2;
                iArr7 = iArr8;
            }
      }
      HashMap<String, int[][]> hashMap = new HashMap<>();
      hashMap.put("static_fields", iArr);
      hashMap.put("instance_fields", iArr3);
      hashMap.put("direct_methods", iArr5);
      hashMap.put("virtual_methods", iArr7);
      return hashMap;
    }

    private static int[] encode_field(ByteBuffer byteBuffer) {
      return new int[]{Utils.fromULEB128(byteBuffer), Utils.fromULEB128(byteBuffer)};
    }

    private static int[] encode_method(ByteBuffer byteBuffer) {
      return new int[]{Utils.fromULEB128(byteBuffer), Utils.fromULEB128(byteBuffer), Utils.fromULEB128(byteBuffer)};
    }

    public static byte[] encodeClassData(HashMap<String, int[][]> hashMap) {
      int[][] iArr = hashMap.get("static_fields");
      int[][] iArr2 = hashMap.get("instance_fields");
      int[][] iArr3 = hashMap.get("direct_methods");
      int[][] iArr4 = hashMap.get("virtual_methods");
      byte[] merge = merge(Utils.toULEB128(iArr.length), Utils.toULEB128(iArr2.length), Utils.toULEB128(iArr3.length), Utils.toULEB128(iArr4.length));
      if (iArr.length > 0) {
            int i = 0;
            for (int[] iArr5 : iArr) {
                merge = merge(merge, decode_field(iArr5, i));
                i = iArr5;
            }
      }
      if (iArr2.length > 0) {
            int i2 = 0;
            for (int[] iArr6 : iArr2) {
                merge = merge(merge, decode_field(iArr6, i2));
                i2 = iArr6;
            }
      }
      if (iArr3.length > 0) {
            int i3 = 0;
            for (int[] iArr7 : iArr3) {
                merge = merge(merge, decode_method(iArr7, i3));
                i3 = iArr7;
            }
      }
      if (iArr4.length > 0) {
            int i4 = 0;
            for (int[] iArr8 : iArr4) {
                merge = merge(merge, decode_method(iArr8, i4));
                i4 = iArr8;
            }
      }
      return merge;
    }

    private static byte[] merge(byte[]... bArr) {
      int i = 0;
      for (byte[] bArr2 : bArr) {
            i += bArr2.length;
      }
      ByteBuffer allocate = ByteBuffer.allocate(i);
      for (byte[] bArr3 : bArr) {
            allocate.put(bArr3);
      }
      byte[] bArr4 = new byte;
      allocate.position(0);
      allocate.get(bArr4);
      return bArr4;
    }

    private static byte[] decode_field(int[] iArr, int i) {
      return merge(Utils.toULEB128(iArr - i), Utils.toULEB128(iArr));
    }

    private static byte[] decode_method(int[] iArr, int i) {
      return merge(Utils.toULEB128(iArr - i), Utils.toULEB128(iArr), Utils.toULEB128(iArr));
    }
}
```
`com.zj.wuaipojie2024_2.Utils`类
```java
package org.example;

import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;

/* loaded from: assets/classes.dex */
public class Utils {
    public static final String SHA1 = "SHA1";

    public static byte[] toULEB128(int i) {
      int i2 = i >> 28;
      if (i2 > 0) {
            return new byte[]{(byte) ((i & 127) | 128), (byte) (((i >> 7) & 127) | 128), (byte) (((i >> 14) & 127) | 128), (byte) (((i >> 21) & 127) | 128), (byte) (i2 & 15)};
      }
      int i3 = i >> 21;
      if (i3 > 0) {
            return new byte[]{(byte) ((i & 127) | 128), (byte) (((i >> 7) & 127) | 128), (byte) (((i >> 14) & 127) | 128), (byte) (i3 & 127)};
      }
      int i4 = i >> 14;
      if (i4 > 0) {
            return new byte[]{(byte) ((i & 127) | 128), (byte) (((i >> 7) & 127) | 128), (byte) (i4 & 127)};
      }
      int i5 = i >> 7;
      return i5 > 0 ? new byte[]{(byte) ((i & 127) | 128), (byte) (i5 & 127)} : new byte[]{(byte) (i & 127)};
    }

    public static byte[] getSha1(byte[] bArr) {
      try {
            return MessageDigest.getInstance("SHA").digest(bArr);
      } catch (Exception unused) {
            return null;
      }
    }

    public static String md5(byte[] bArr) {
      try {
            String bigInteger = new BigInteger(1, MessageDigest.getInstance("md5").digest(bArr)).toString(16);
            for (int i = 0; i < 32 - bigInteger.length(); i++) {
                bigInteger = "0" + bigInteger;
            }
            return bigInteger;
      } catch (NoSuchAlgorithmException unused) {
            throw new RuntimeException("ops!!");
      }
    }

    public static int checksum(ByteBuffer byteBuffer) {
      byteBuffer.position(12);
      int capacity = byteBuffer.capacity();
      int i = 1;
      int i2 = 0;
      boolean z = false;
      while (byteBuffer.position() < capacity) {
            ArrayList arrayList = new ArrayList();
            int i3 = 0;
            while (true) {
                if (i3 < 1024) {
                  arrayList.add(Integer.valueOf(byteBuffer.get() & 255));
                  if (byteBuffer.position() == byteBuffer.limit()) {
                        z = true;
                        break;
                  }
                  i3++;
                } else {
                  break;
                }
            }
            int[] calculateVar = calculateVar(arrayList, i, i2);
            int i4 = calculateVar;
            int i5 = calculateVar;
            if (z) {
                return (i5 << 16) + i4;
            }
            i2 = i5;
            i = i4;
      }
      return 0;
    }

    private static int[] calculateVar(ArrayList<Integer> arrayList, int i, int i2) {
      int i3 = 0;
      while (i3 < arrayList.size()) {
            int intValue = (arrayList.get(i3).intValue() + i) % 65521;
            i2 = (i2 + intValue) % 65521;
            i3++;
            i = intValue;
      }
      return new int[]{i, i2};
    }

    public static int[] fromULEB128(ByteBuffer byteBuffer) {
      int i;
      int i2 = byteBuffer.get() & 255;
      if (i2 > 127) {
            int i3 = byteBuffer.get() & 255;
            i2 = (i2 & 127) | ((i3 & 127) << 7);
            if (i3 > 127) {
                int i4 = byteBuffer.get() & 255;
                i2 |= (i4 & 127) << 14;
                if (i4 > 127) {
                  int i5 = byteBuffer.get() & 255;
                  i2 |= (i5 & 127) << 21;
                  if (i5 > 127) {
                        i2 |= (byteBuffer.get() & 255) << 28;
                        i = 5;
                  } else {
                        i = 4;
                  }
                } else {
                  i = 3;
                }
            } else {
                i = 2;
            }
      } else {
            i = 1;
      }
      return new int[]{i2, i};
    }
}
```
上面这些都是摘除了android相关类的,可以正常用
其中fix需要3个int参数,是从资源中读取的

资源里有两个,说明要修复两次,但是这个程序里只会修复一次,所以我在main里修复两次,得到修复后的dex,校验通过


这时候可以看到方法的全貌了,根据这个逻辑,显然密码是`048531267`

看到`B.d`,也就是说密码是`md5(getSha1("048531267uid".getBytes()))`,我的uid是`2148984`

从代码里将md5和getSha1拿出来运行,得到结果

## 解锁
现在已经拿到了flag,但是解锁还过不去,有什么办法可以成功解锁呢?我的做法是使用frida,虽然这个cm在so里做了frida检测, 但我发现只要用attach模式就可以避免被杀,spawn模式一杀一个准,虽然可以hook检测函数,但是代码量就上来了,没有必要<br />首先用logcat来看日志,需要1.dex能通过校验,那么办法就是先将修复好的dex放到对应目录下

但是每次解锁,应用都会重新复制1.dex,所以这里是第一个hook点,hook`FileOutputStream`的构造方法,将参数改为其他文件路径,同时还可以hook`checkPassword`来查看输入的密码和返回值


```javascript
Java.perform(function () {
    let MainActivity = Java.use("com.zj.wuaipojie2024_2.MainActivity");
    MainActivity["checkPassword"].implementation = function (str) {
      console.log(`MainActivity.checkPassword is called: str=${str}`);
      let result = this["checkPassword"](str);
      var outStream = Java.use('java.io.FileOutputStream')
      outStream["$init"].overload('java.io.File').implementation = function (arg) {
            console.log(arg)
            var ret = this.$init('/data/user/0/com.zj.wuaipojie2024_2/app_data/0.dex') }
      }
      console.log(`MainActivity.checkPassword result=${result}`);
      return result;
    };

});
```
这样就不会重复写入`1.dex`,将修复好的dex复制过去并重命名, 权限开成`777`
第二次解锁的时候触发了

这时候可以用修复好的dex替换掉`1.dex`

后面在应用中修复时,需要加载`decode.dex`,而且应用只修复1次,用的偏移是`A_offset`,还有个`B_offset`没用,所以我们需要单独修复一下这个情况,然后将修复完的dex放到目录下,改名`decode.dex`,配置权限`777`,让应用完成剩下的修复


同时需要注意,之前hook了`FileOutputStream`的构造函数,这里修复的时候同样用到了,所以要对`2.dex`做一次判断


```javascript
Java.perform(function () {
    let MainActivity = Java.use("com.zj.wuaipojie2024_2.MainActivity");
MainActivity["checkPassword"].implementation = function (str) {
    console.log(`MainActivity.checkPassword is called: str=${str}`);
    let result = this["checkPassword"](str);
    var outStream = Java.use('java.io.FileOutputStream')
    outStream["$init"].overload('java.io.File').implementation = function(arg){
      console.log(arg)
      if(arg == '/data/user/0/com.zj.wuaipojie2024_2/app_data/2.dex'){
            console.log('2.dex')
            var ret = this.$init(arg)
      }
      else{var ret = this.$init('/data/user/0/com.zj.wuaipojie2024_2/app_data/0.dex')}
    }
    console.log(`MainActivity.checkPassword result=${result}`);
    return result;
};
   
});
```
使用密码成功解锁(如果不成功,可能是在重新hook时,首次没有触发`FileOutputStrem`,导致`1.dex`被重写,这时候重新对`1.dex`做一次覆盖就可以了)

正己 发表于 2024-2-28 12:52

新人帖,给个精华,期待大佬后续分享更多佳作!{:301_997:}

WXjzc 发表于 2024-2-25 11:23

传了个重复的图,不知道删哪张,就不删了,请忽略文末的附件:rggrg

yeahn 发表于 2024-2-25 14:00

大佬牛逼{:1_927:}

西枫游戏 发表于 2024-2-25 14:38

看到了 觉得很牛逼,我还得多多学习了。

ljz137649 发表于 2024-3-4 11:18

可以啊,学到不少东西!

ml14551 发表于 2024-3-5 11:56

感谢楼主,真的学习到了:lol

weekylin 发表于 2024-3-8 12:51

小白前来学习了

beyondchampion 发表于 2024-3-8 14:01

感谢分享!!!

jishufanzi 发表于 2024-3-8 18:02

{:1_893:}学习了,感谢分享!
页: [1] 2 3 4 5 6
查看完整版本: 吾爱破解2024红包题安卓部分wp