首页 > 其他分享 >ChallengeMobile

ChallengeMobile

时间:2024-06-03 11:24:20浏览次数:20  
标签:iArr int i3 i2 i6 length ChallengeMobile

解题思路


获取到输入的字符串保存到s,调用Jformat方法对s进行验证,返回true则代表输入字符串正确反之错误。

Jformat方法分析:

首先看到使用了LoadData加载了”ming“给了a方法,a方法的返回值赋值给了arr_b。
接着判断SDK_INT是否小于29:
意思就是判断Android版本是否小于10,如果不是则
使用InMemoryDexClassLoader从内存中动态加载DEX,加载方法在"challengemobile"文件中,这个是SO文件。

根据inMemoryDexClassLoader0的结果动态加载com.example.challengemobile下的Checker类。猜测是加载后的DEX文件。
method0是class0通过getMethod获取到Checker类下的"isflag"方法

如果以上操作都完成了则进入到if判断这里
很明显,取s字符串前五个判断是否是:
flag{,去最后一个判断是否是:}
如果都满足则继续进入if里:
首先获取s除去前五个字符的字符串赋值给了s1
通过反射机制调用了method0类的isflag方法,把s1字符串除去最后一个字符作为参数传递给了isflag方法。其实就是把flga{}括号中的字符串作为参数给了isflag方法。
根据isflag方法的返回值走return代码
如果返回了null则直接返回false,否则返回boolean0的布尔值。

其实说白了就是:
输入字符串--->isflag方法执行--->返回null可能代表isfhlag方法执行异常--->放回false则代码输入字符串不满足isflag方法的判断,true则证明输入的字符串满足了判断。同时就是正确的flag了

a方法分析

native方法,在"challengemobile"so文件中
解包APK在lib目录下使用IDA分析so文件

分析不难发现使用”ming“文件的数据去异或genrand_int32()函数的返回值,赋值给v10,v10的赋值的v8,那么可以说明这个循环执行完毕之后v8的数据就是动态加载的DEX文件了。这里有两个方法dump到v8的值:
1:动调SO文件,执行完循环查看V8的值;
2:使用Frida直接HOOK a方法的返回值;
我使用Frida去拿到DEX,因为比较快。

 let MainActivity = Java.use("com.example.challengemobile.MainActivity");
        MainActivity["a"].implementation = function (bArr) {
            // console.log(`MainActivity.a is called: bArr=${bArr}`);
            let result = this["a"](bArr);
            // console.log(`MainActivity.a result = ${result}`);
            // 将 result 转换为十六进制字符串
            let hexResult = Array.from(result).map(num => {
                // 对每个数值进行十六进制转换,并确保其为两个字符
                // (例如,0xA 会变成 0x0A)
                return ('0' + (num & 0xFF).toString(16)).slice(-2);
            }).join(' '); // 以空格分隔每个十六进制数

            console.log(`MainActivity.a result (hex): ${hexResult}`);
            return result;
        };

HOOK脚本可以直接在jadx右键复制,我这里改了输出结果为十六进制。

保存十六进制数据到txt中,使用010打开导入十六进制数据另存为dex文件
使用JADX打开dex文件分析:

可以看到就是Chekcer类

isflag方法,str就是{}中的数据,调用encryptToBase64String方法传入str和getKey方法的返回值。

getKey方法

也是个native方法,调用了example so文件

通过getKey方法定位到这里发现是使用了Java_example_Checker_getKey方法把v4复制给了v5,接着调用AES_ECB_decrypt方法对v5进行解密,其实就是v4经过AES_ECB_decrypt解密后就是key了,这个so文件是通过另一个so文件调用的,可能IDA无法调试。
还是使用Ffrida来获取getKey的返回值

Java.perform(function () {
        Java.enumerateClassLoaders({
            onMatch: function (loader) {
                try {
                    if (loader.findClass("com.example.challengemobile.Checker")) {
                        Java.classFactory.loader = loader;
                        // console.log(loader);
                    }
                } catch (error) {

                }
            }, onComplete: function () {

            }
        });
        let Checker = Java.use("com.example.challengemobile.Checker");
        console.log("Key--->  " + Checker["getKey"]())
    }

使用enumerateClassLoaders遍历所有类,找到Checker类,在使用use来获取到Checker类下的所有对象,通过Checker来主动调用getKey方法获取到返回值。
``Key---> oM51I504n137gp2~
获取到key后继续分析

encryptToBase64String方法

调用encrypt方法,str与str2作为参数传递过去

encrypt方法

调用另一个encrypt方法并把str与str2的字节数组作为参数传递过去

encrypt方法(第二个)

如果bArr长度为0则直接返回bArr的值,否则调用
toByteArray(encrypt(toIntArray(bArr, true), toIntArray(fixKey(bArr2), $assertionsDisabled)), $assertionsDisabled);
这一串方法调用,按顺序分析

encrypt方法(第三个)

很经典的一个XXTEA加密。
iArr为输入字符串的整数数组。
iArr2为key的整数数组。

那么不难理解:
toIntArray方法就是把字节数组转为对应的整数数组。
toByteArray方法把整数数组转为字节数组。

所有加密执行完之后回到encryptToBase64String方法

也就是说把XXTEA加密后的结果进行了Base64加密。所以
Ckh/PFCSS/i4kMVw1lswyghOZbIg+W5SymREHNcRg721Tm9w就是密文了

分析到这里就可以去解密了,没有魔改的XXTEA加密,有了Key和密文,直接解密后在进行Base64解密就是flag了。
使用java实现

package CTF.ISCC.ChallengeMobile;

import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Base64;

public class Exp {
    private static int MX(int i, int i2, int i3, int i4, int i5, int[] iArr) {
        return (((i3 >>> 5) ^ (i2 << 2)) + ((i2 >>> 3) ^ (i3 << 4))) ^ ((i ^ i2) + (iArr[(i4 & 3) ^ i5] ^ i3));
    }
    private static int[] toIntArray(byte[] bArr, boolean z) {
        int[] iArr;
        int length = (bArr.length & 3) == 0 ? bArr.length >>> 2 : (bArr.length >>> 2) + 1;
        if (z) {
            iArr = new int[length + 1];
            iArr[length] = bArr.length;
        } else {
            iArr = new int[length];
        }
        int length2 = bArr.length;
        for (int i = 0; i < length2; i++) {
            int i2 = i >>> 2;
            iArr[i2] = iArr[i2] | ((bArr[i] & 255) << ((i & 3) << 3));
        }
        return iArr;
    }
    private static byte[] fixKey(byte[] bArr) {
        if (bArr.length != 16) {
            byte[] bArr2 = new byte[16];
            if (bArr.length < 16) {
                System.arraycopy(bArr, 0, bArr2, 0, bArr.length);
            } else {
                System.arraycopy(bArr, 0, bArr2, 0, 16);
            }
            return bArr2;
        }
        return bArr;
    }
    private static byte[] toByteArray(int[] iArr, boolean z) {
        int i;
        int length = iArr.length << 2;
        if (z) {
            i = iArr[iArr.length - 1];
            int i2 = length - 4;
            if (i < i2 - 3 || i > i2) {
                return null;
            }
        } else {
            i = length;
        }
        byte[] bArr = new byte[i];
        for (int i3 = 0; i3 < i; i3++) {
            bArr[i3] = (byte) (iArr[i3 >>> 2] >>> ((i3 & 3) << 3));
        }
        return bArr;
    }

    private static int[] encrypt(int[] iArr, int[] iArr2) {
        int length = iArr.length - 1;
        if (length >= 1) {
            int i = (52 / (length + 1)) + 6;
            int i2 = iArr[length];
            int i3 = 0;
            while (true) {
                int i4 = i - 1;
                if (i <= 0) {
                    break;
                }
                i3 -= 1640531527;
                int i5 = (i3 >>> 2) & 3;
                int i6 = 0;
                while (i6 < length) {
                    i2 = iArr[i6] + MX(i3, iArr[i6 + 1], i2, i6, i5, iArr2);
                    iArr[i6] = i2;
                    i6++;
                }
                i2 = iArr[length] + MX(i3, iArr[0], i2, i6, i5, iArr2);
                iArr[length] = i2;
                i = i4;
            }
        }
        return iArr;
    }

    /**
     *
     * @param iArr
     * @param iArr2
     * @return
     */
    private static int[] decrypt(int[] iArr, int[] iArr2) {
        int length = iArr.length - 1;
        if (length >= 1) {
            int i = (52 / (length + 1)) + 6;
            int i3 = 0;
            for (int j = 0; j < i; ++j)
                i3 -= 0x61c88647;

            for (int j = 0; j < i; ++j) {
                int i6 = length;
                int i2 = iArr[length - 1];
                int i5 = (i3 >>> 2) & 3;
                iArr[length] = iArr[length] - MX(i3, iArr[0], i2, i6, i5, iArr2);
                while (i6 > 0) {
                    --i6;
                    i2 = iArr[i6 - 1 < 0 ? length : i6 - 1];
                    iArr[i6] = iArr[i6] - MX(i3, iArr[i6 + 1], i2, i6, i5, iArr2);
                }
                i3 += 0x61c88647;
            }
        }
        return iArr;
    }

    public static void main(String[] args) {
        int[] decBase64 = toIntArray(Base64.getDecoder().decode("Ckh/PFCSS/i4kMVw1lswyghOZbIg+W5SymREHNcRg721Tm9w"),false);
//        System.out.println(Arrays.toString(decBase64));

        int[] decKey = toIntArray(fixKey("oM51I504n137gp2~".getBytes()),true);
//        System.out.println(Arrays.toString(decKey));

        byte[] flag = toByteArray(decrypt(decBase64, decKey), true);
//        System.out.println(Arrays.toString(flag));

        assert  flag != null;
        String str = new String(flag,StandardCharsets.UTF_8);
        System.out.println("flag{" + str + "}");

    }
}

flag:
flag{ZVDK$8m|/;&6L6#zYJa3?Ming%a[Qt->}

标签:iArr,int,i3,i2,i6,length,ChallengeMobile
From: https://www.cnblogs.com/GaGaWord/p/18228409

相关文章