Windows初级题通过调试获取flag,Android初级题通过Base64解码和XXTEA解密获取flag,Android中级题通过动态调试和Hook获取密钥流,与密文异或得到flag。
AI 摘要
Windows初级题
先判断flag长度为27
搜索Success,在附近下断点,输入全1,直接调试即可得到flag
fl@g{52pOj1E_2025#Fighting}
Android初级题
拖入JADX
在FoldFragment2类中看到三个Base64编码的字符串:
"2hyWtSLN69+QWLHQ"
"hjyaQ8jNSdp+mZic7Kdtyw=="
"cYoiUd2BfEDc/V9e4LdciBz9Mzwzs3yr0kgrLA=="
在TO类可以看到db方法,即Base64解码后应用XXTEA解密,密钥为my-xxtea-secret填充至16字节
粘贴到IDE里运行即可
运行后可以知道这三个字符串分别对应为:
flag{
xnkl2025!}
快去寻找flag吧!
所以flag为
flag{xnkl2025!}
EXP:
import java.util.Base64;
import java.nio.charset.StandardCharsets;
public class Main {
private static final String YYLX = "my-xxtea-secret";
public static final byte[] de(byte[] data, byte[] key) {
return toByteArray(de(toIntArray(data, false), toIntArray(fK(key), false)), true);
}
private static int[] de(int[] iArr, int[] iArr2) {
int length = iArr.length;
int i = length - 1;
if (i < 1) {
return iArr;
}
int i2 = iArr[0];
for (int i3 = ((52 / length) + 6) * (-1640531527); i3 != 0; i3 -= -1640531527) {
int i4 = (i3 >>> 2) & 3;
for (int i5 = i; i5 > 0; i5--) {
int i6 = iArr[i5 - 1];
i2 = iArr[i5] - (((i2 ^ i3) + (i6 ^ iArr2[(i5 & 3) ^ i4])) ^ (((i6 >>> 5) ^ (i2 << 2)) + ((i2 >>> 3) ^ (i6 << 4))));
iArr[i5] = i2;
}
int i7 = iArr[i];
i2 = iArr[0] - (((i2 ^ i3) + (iArr2[i4] ^ i7)) ^ (((i7 >>> 5) ^ (i2 << 2)) + ((i2 >>> 3) ^ (i7 << 4))));
iArr[0] = i2;
}
return iArr;
}
private static int[] toIntArray(byte[] bArr, boolean z) {
int length = (bArr.length + 3) / 4;
int[] iArr = new int[length + (z ? 1 : 0)];
int length2 = bArr.length;
for (int i = 0; i < length2; i++) {
int i2 = i / 4;
iArr[i2] = iArr[i2] | ((bArr[i] & 0xFF) << ((i % 4) * 8));
}
if (z) {
iArr[length] = bArr.length;
}
return iArr;
}
private static byte[] toByteArray(int[] iArr, boolean z) {
int length = iArr.length * 4;
if (z) {
length = iArr[iArr.length - 1];
}
byte[] bArr = new byte[length];
for (int i = 0; i < length; i++) {
bArr[i] = (byte) ((iArr[i / 4] >> ((i % 4) * 8)) & 255);
}
return bArr;
}
private static byte[] fK(byte[] bArr) {
byte[] bArr2 = new byte[16];
System.arraycopy(bArr, 0, bArr2, 0, Math.min(bArr.length, 16));
return bArr2;
}
public static void main(String[] args) {
String v1 = "2hyWtSLN69+QWLHQ";
String v2 = "hjyaQ8jNSdp+mZic7Kdtyw==";
byte[] decode1 = Base64.getDecoder().decode(v1);
byte[] decode2 = Base64.getDecoder().decode(v2);
byte[] bytes = YYLX.getBytes(StandardCharsets.UTF_8);
byte[] flag1 = de(decode1,bytes);
byte[] flag2 = de(decode2,bytes);
String flag1_ = new String(flag1, StandardCharsets.UTF_8);
String flag2_ = new String(flag2, StandardCharsets.UTF_8);
System.out.println(flag1_ + flag2_);//flag{xnkl2025!}
}
}
Android中级题
拖入JADX
可以看到check逻辑在so
拖入ida查看so
搜索java没有,那就是动态注册了
进去可以发现有一些检测调试的
但没细看,只关注了检测的结果导致v15是a
还是ao
v15
的地址不能确定, 直接用Unidbg断在调用v15
的那里即可
emulator.attach().addBreakPoint(md.base+0xe8f44); // 0xe8f44为指令BLR X22的偏移量, 断下后看X22寄存器减掉基址就行
最后可以发现调用函数为ao (0xe9f60) (后面发现是没过掉检测所以走的ao,实际上正确应该走的a)
// a2就是输入的字符串, a3为19, result为最后结果
void __fastcall ao(__int128 *a1, const char *a2, __int64 a3, _QWORD *result)
{
__int64 i; // x23
__int128 v8; // [xsp+0h] [xbp-20h] BYREF
__int64 v9; // [xsp+18h] [xbp-8h]
v9 = *(_QWORD *)(_ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2)) + 40); // 没用
v8 = *a1; // 传入a1为定值, 故v8也为定值
if ( a3 )
{
for ( i = 0LL; i != a3; ++i ) // 循环19次, 侧面说明Flag长度为19
{
if ( (i & 0xF) == 0 )
sub_E9954(&v8); // 这里的这个函数其实不用跟进去看...... (和输入啥的都没关系, v8一开始就是定值, v8更新的次数也固定)
*((_BYTE *)result + i) = *(_BYTE *)((unsigned __int64)&v8 | i & 0xF) ^ a2[i]; // 实际上做的事情 ("^"左侧其实都是确定的值, 不随输入的改变而改变)
}
}
}
sub_E9954就是白盒AES,所以总得来说加密过程其实只是一个异或
用unidbg输入0x13个字符串1,HOOK出扩展密钥流
emulator.getBackend().hook_add_new(new CodeHook() {
@Override
public void hook(Backend backend, long address, int size, Object user) {
if (address == module.base + 0xe9fb8) { // 0xe9fb8就是刚才说的异或指令的偏移
// 输出 W8, chr(a2[i])
System.out.printf("0x%1$x, %2$c", emulator.getBackend().reg_read(Unicorn.UC_ARM64_REG_W8).intValue(), (char) emulator.getBackend().reg_read(Unicorn.UC_ARM64_REG_W9).intValue());
System.out.println();
}
}
@Override
public void onAttach(UnHook unHook) {}
@Override
public void detach() {}
}, module.base, module.base + module.size, null);
[0x2e, 0x4b, 0xee, 0xc8, 0xe0, 0x95, 0x88, 0x47, 0xb0, 0x72, 0x1b, 0x68, 0x40, 0xd0, 0xa, 0x84, 0x27, 0xaf, 0xf3]
再和密文异或得到flag
key = bytes(bytes.fromhex('2e4beec8e0958847b0721b6840d00a8427aff3'))
enc = bytes(bytes.fromhex('48278faf9bf8ec729807720c6be23ab64259f7')
flag = bytes([a^b for a, b in zip(key, enc)])
print(flag)
后面的题因为电脑突然坏了 没时间看了😭(一修修了一星期)