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

查看 102|回复 9
作者:WXjzc   

高级题会不了一点儿

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


2817142-20240224173339638-2038123072.png (11.52 KB, 下载次数: 0)
下载附件
2024-2-25 10:32 上传



2817142-20240224173339769-1802703311.png (22.59 KB, 下载次数: 0)
下载附件
2024-2-25 10:33 上传

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


2817142-20240224173339643-1980481857.png (22.84 KB, 下载次数: 0)
下载附件
2024-2-25 10:32 上传

结果为flag{happy_new_year_2024}
初级二


2817142-20240224173339739-1019444826.png (41.47 KB, 下载次数: 0)
下载附件
2024-2-25 10:33 上传

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


2817142-20240224173339830-345057923.png (103.93 KB, 下载次数: 0)
下载附件
2024-2-25 10:33 上传

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


2817142-20240224173339725-618221395.png (41.73 KB, 下载次数: 0)
下载附件
2024-2-25 10:32 上传

所以直接上objection强制启动


2817142-20240224173339728-1531292322.png (70 KB, 下载次数: 0)
下载附件
2024-2-25 10:32 上传

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


2817142-20240224173339732-619894180.png (503.19 KB, 下载次数: 0)
下载附件
2024-2-25 10:33 上传

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


2817142-20240224173339688-1204657636.png (9.31 KB, 下载次数: 0)
下载附件
2024-2-25 10:32 上传

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


2817142-20240224173339675-1970195012.png (48.3 KB, 下载次数: 0)
下载附件
2024-2-25 10:32 上传

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


2817142-20240224173339800-589732217.png (60.32 KB, 下载次数: 0)
下载附件
2024-2-25 10:33 上传

明显的有问题


2817142-20240224173339790-589945863.png (21.07 KB, 下载次数: 0)
下载附件
2024-2-25 10:33 上传

回来发现做了修复


2817142-20240224173339650-655045531.png (16.93 KB, 下载次数: 0)
下载附件
2024-2-25 10:32 上传

由于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 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 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 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 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  0) {
            int i8 = 0;
            for (int i9 = 0; i9  0) {
            int i10 = 0;
            for (int i11 = 0; i11  0) {
            int i12 = 0;
            for (int i13 = 0; i13  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 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  127) {
            int i3 = byteBuffer.get() & 255;
            i2 = (i2 & 127) | ((i3 & 127)  127) {
                int i4 = byteBuffer.get() & 255;
                i2 |= (i4 & 127)  127) {
                    int i5 = byteBuffer.get() & 255;
                    i2 |= (i5 & 127)  127) {
                        i2 |= (byteBuffer.get() & 255)
上面这些都是摘除了android相关类的,可以正常用
其中fix需要3个int参数,是从资源中读取的


2817142-20240224173339616-1199007412.png (12.89 KB, 下载次数: 0)
下载附件
2024-2-25 10:32 上传

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


2817142-20240224173339730-1054711706.png (15.93 KB, 下载次数: 0)
下载附件
2024-2-25 10:33 上传



2817142-20240224173339778-2130295715.png (95.77 KB, 下载次数: 0)
下载附件
2024-2-25 10:33 上传

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


2817142-20240224173339904-171794742.png (77.33 KB, 下载次数: 0)
下载附件
2024-2-25 10:33 上传

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


2817142-20240224173339666-722136125.png (16.2 KB, 下载次数: 0)
下载附件
2024-2-25 10:32 上传

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


2817142-20240224173339788-2098695967.png (16.53 KB, 下载次数: 0)
下载附件
2024-2-25 10:33 上传

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


2817142-20240224173339743-599089465.png (39.12 KB, 下载次数: 0)
下载附件
2024-2-25 10:33 上传

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


2817142-20240224173339764-167346121.png (22.69 KB, 下载次数: 0)
下载附件
2024-2-25 10:33 上传

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 (23.67 KB, 下载次数: 0)
下载附件
2024-2-25 10:33 上传

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


2817142-20240224173339656-1242227329.png (47.19 KB, 下载次数: 0)
下载附件
2024-2-25 10:32 上传

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


2817142-20240224173339711-380000494.png (27.37 KB, 下载次数: 0)
下载附件
2024-2-25 10:32 上传



2817142-20240224173339771-962726443.png (27.89 KB, 下载次数: 0)
下载附件
2024-2-25 10:33 上传

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


2817142-20240224173339603-1975504139.png (18.01 KB, 下载次数: 0)
下载附件
2024-2-25 10:32 上传

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 (68.23 KB, 下载次数: 0)
下载附件
2024-2-25 10:32 上传

下载次数, 下载附件

WXjzc
OP
  

新人帖,给个精华,期待大佬后续分享更多佳作!
yeahn   

传了个重复的图,不知道删哪张,就不删了,请忽略文末的附件
西枫游戏   

大佬牛逼
ljz137649   

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

可以啊,学到不少东西!
weekylin   

感谢楼主,真的学习到了
beyondchampion   

小白前来学习了
jishufanzi   

感谢分享!!!
ljz137649   

学习了,感谢分享!
您需要登录后才可以回帖 登录 | 立即注册

返回顶部