高级题会不了一点儿
初级一
小猫游戏,改一下判断
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 上传