吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 6025|回复: 55
收起左侧

[Android CTF] 吾爱破解2024红包题安卓部分wp

  [复制链接]
WXjzc 发表于 2024-2-25 10:51
本帖最后由 WXjzc 于 2024-2-25 11:15 编辑

高级题会不了一点儿

初级一

小猫游戏,改一下判断
2817142-20240224173339638-2038123072.png
2817142-20240224173339769-1802703311.png
t.LOSE的值改为win,然后将case i.LOSE的代码段删掉,重新签名安装即可<br />游戏结束会播放原神启动,播完会输出flag
2817142-20240224173339643-1980481857.png
结果为flag{happy_new_year_2024}

初级二

2817142-20240224173339739-1019444826.png
flag是跟着签名走的,所以没法重新编译
2817142-20240224173339830-345057923.png
看代码可以看到是出金启动FlagActivity
2817142-20240224173339725-618221395.png
所以直接上objection强制启动
2817142-20240224173339728-1531292322.png

中级

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

拿flag

反编译能得知assets目录下还有一个dex,这个dex的checksum校验不通过,需要在jadx里忽略校验
2817142-20240224173339688-1204657636.png
将assets目录下的dex拷贝的应用数据目录的app_data文件夹下,加载并且反射调用com.zj.wuaipojie2024_2.C.isValidate
2817142-20240224173339675-1970195012.png
再次反射,调用com.zj.wuaipojie2024_2.A.d
2817142-20240224173339800-589732217.png
明显的有问题
2817142-20240224173339790-589945863.png
回来发现做了修复
2817142-20240224173339650-655045531.png
由于1.dex的checksum校验不通过,所以系统不会加载这个dex,也就是说不会触发这个修复方法,因此可以将这个修复的代码摘出来,单独进行修复,涉及到3个类
Main类,将fix和read从com.zj.wuaipojie2024_2.C中拿出来

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")[i2][2] = i3;
            byte[] encodeClassData = D.encodeClassData(classData);
            byteBuffer.position(intValue);
            byteBuffer.put(encodeClassData);
            byteBuffer.position(32);
            byte[] bArr = new byte[byteBuffer.capacity() - 32];
            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.available()];
                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

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)[0];
        int i3 = Utils.fromULEB128(byteBuffer)[0];
        int i4 = Utils.fromULEB128(byteBuffer)[0];
        int i5 = Utils.fromULEB128(byteBuffer)[0];
        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[0];
                } else {
                    i6 += encode_field[0];
                }
                int[] iArr2 = new int[2];
                iArr2[0] = i6;
                iArr2[1] = encode_field[1];
                iArr[i7] = 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[0];
                } else {
                    i8 += encode_field2[0];
                }
                int[] iArr4 = new int[2];
                iArr4[0] = i8;
                iArr4[1] = encode_field2[1];
                iArr3[i9] = 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[0];
                } else {
                    i10 += encode_method[0];
                }
                int[] iArr6 = new int[3];
                iArr6[0] = i10;
                iArr6[1] = encode_method[1];
                iArr6[2] = encode_method[2];
                iArr5[i11] = 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[0];
                } else {
                    i12 += encode_method2[0];
                }
                int[] iArr8 = new int[3];
                iArr8[0] = i12;
                iArr8[1] = encode_method2[1];
                iArr8[2] = encode_method2[2];
                iArr7[i13] = 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)[0], Utils.fromULEB128(byteBuffer)[0]};
    }

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

    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[0];
            }
        }
        if (iArr2.length > 0) {
            int i2 = 0;
            for (int[] iArr6 : iArr2) {
                merge = merge(merge, decode_field(iArr6, i2));
                i2 = iArr6[0];
            }
        }
        if (iArr3.length > 0) {
            int i3 = 0;
            for (int[] iArr7 : iArr3) {
                merge = merge(merge, decode_method(iArr7, i3));
                i3 = iArr7[0];
            }
        }
        if (iArr4.length > 0) {
            int i4 = 0;
            for (int[] iArr8 : iArr4) {
                merge = merge(merge, decode_method(iArr8, i4));
                i4 = iArr8[0];
            }
        }
        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()];
        allocate.position(0);
        allocate.get(bArr4);
        return bArr4;
    }

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

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

com.zj.wuaipojie2024_2.Utils

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[0];
            int i5 = calculateVar[1];
            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参数,是从资源中读取的
2817142-20240224173339616-1199007412.png
资源里有两个,说明要修复两次,但是这个程序里只会修复一次,所以我在main里修复两次,得到修复后的dex,校验通过
2817142-20240224173339730-1054711706.png
2817142-20240224173339778-2130295715.png
这时候可以看到方法的全貌了,根据这个逻辑,显然密码是048531267
2817142-20240224173339904-171794742.png
看到B.d,也就是说密码是md5(getSha1("048531267uid".getBytes())),我的uid是2148984
2817142-20240224173339666-722136125.png
从代码里将md5和getSha1拿出来运行,得到结果
2817142-20240224173339788-2098695967.png

解锁

现在已经拿到了flag,但是解锁还过不去,有什么办法可以成功解锁呢?我的做法是使用frida,虽然这个cm在so里做了frida检测, 但我发现只要用attach模式就可以避免被杀,spawn模式一杀一个准,虽然可以hook检测函数,但是代码量就上来了,没有必要<br />首先用logcat来看日志,需要1.dex能通过校验,那么办法就是先将修复好的dex放到对应目录下
2817142-20240224173339743-599089465.png
但是每次解锁,应用都会重新复制1.dex,所以这里是第一个hook点,hookFileOutputStream的构造方法,将参数改为其他文件路径,同时还可以hookcheckPassword来查看输入的密码和返回值
2817142-20240224173339764-167346121.png

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
第二次解锁的时候触发了
2817142-20240224173339735-1349983931.png
这时候可以用修复好的dex替换掉1.dex
2817142-20240224173339656-1242227329.png
后面在应用中修复时,需要加载decode.dex,而且应用只修复1次,用的偏移是A_offset,还有个B_offset没用,所以我们需要单独修复一下这个情况,然后将修复完的dex放到目录下,改名decode.dex,配置权限777,让应用完成剩下的修复
2817142-20240224173339711-380000494.png
2817142-20240224173339771-962726443.png
同时需要注意,之前hook了FileOutputStream的构造函数,这里修复的时候同样用到了,所以要对2.dex做一次判断
2817142-20240224173339603-1975504139.png

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做一次覆盖就可以了)
2817142-20240224173339680-836605612.png

2817142-20240224173339800-589732217 (1).png

免费评分

参与人数 12吾爱币 +10 热心值 +12 收起 理由
noname96 + 1 + 1 谢谢@Thanks!
Wp123456 + 1 + 1 赞一个
240721 + 1 + 1 我很赞同!
macdym + 1 + 1 谢谢@Thanks!
sidesand + 1 热心回复!
ycwlhc + 1 + 1 我很赞同!
Skyline016 + 1 + 1 用心讨论,共获提升!
AcrossOver + 1 用心讨论,共获提升!
Fxizenta + 1 + 1 谢谢@Thanks!
西枫游戏 + 1 + 1 谢谢@Thanks!
yeahn + 1 + 1 我很赞同!
唐小样儿 + 1 + 1 我很赞同!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

正己 发表于 2024-2-28 12:52
新人帖,给个精华,期待大佬后续分享更多佳作!
 楼主| WXjzc 发表于 2024-2-25 11:23
传了个重复的图,不知道删哪张,就不删了,请忽略文末的附件
yeahn 发表于 2024-2-25 14:00
西枫游戏 发表于 2024-2-25 14:38
看到了 觉得很牛逼,我还得多多学习了。
ljz137649 发表于 2024-3-4 11:18
可以啊,学到不少东西!
ml14551 发表于 2024-3-5 11:56
感谢楼主,真的学习到了
weekylin 发表于 2024-3-8 12:51
小白前来学习了
beyondchampion 发表于 2024-3-8 14:01
感谢分享!!!
jishufanzi 发表于 2024-3-8 18:02
学习了,感谢分享!
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-12-14 00:25

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表