Mini L-CTF 2025 WP
s1gn1n
ida打开发现存在花指令

在这里我一开始犯了一个错误我把jz和jnz进行nop没有nop C7
解释下为什么C7也要nop,因为把前面的跳转nop留下单独一条C7,CPU会继续往下读取字节,形成一个完整的指令。后面的正常的代码会当作 C7 指令的操作数去解码/执行 → 指令流被破坏、程序会异常或崩溃。
int __cdecl sub_4014D0(int a1)
{
int v2; // [esp+8h] [ebp-68h]
int v3; // [esp+Ch] [ebp-64h]
unsigned int j; // [esp+10h] [ebp-60h]
int v5; // [esp+18h] [ebp-58h]
unsigned int i; // [esp+20h] [ebp-50h]
int v7; // [esp+24h] [ebp-4Ch] BYREF
unsigned int v8; // [esp+28h] [ebp-48h] BYREF
char v9[64]; // [esp+2Ch] [ebp-44h] BYREF
memset(v9, 0, sizeof(v9));
v7 = 0;
v2 = sub_4012E0(a1);
sub_401470(v2, v9, &v7); // 中序遍历
v5 = sub_401100(v9, &v9[strlen(v9) + 1] - &v9[1], &v8);// base64
for ( i = v8 - 1; i; --i )
{
*(i + v5) ^= *(i + v5 - 1);
*(i + v5) ^= byte_404060[i];
}
v3 = -28;
for ( j = 0; j < v8; ++j )
v3 = v3 + *(j + v5) - 1;
return v3;
}
for ( i = v8 - 1; i; --i )
{
*(i + v5) ^= *(i + v5 - 1);
*(i + v5) ^= byte_404060[i];
}
得到x[i]=x[i]⊕x[i−1]⊕k[i]
其中:
- x[i] 表示处理后的字节
- k[i]=byte_404060[i]
v3 = -28;
for (j = 0; j < v8; ++j)
v3 = v3 + *(j + v5) - 1;
得到

所有项展开得到

因为循环没有处理第一项它的形式是
x[0]⊕k[0]
所以最终的求和形式为:

因为它把v3当做求和值来进行返回,我们要让函数不进行jnz跳转,所以当v3的值为0时才不会跳转

补充:
jnz为0继续执行下一条指令,不为0执行跳转
因为Sum(x[i]^x[i-1]^k[i])+x[0]^k[0]==0 可以逆推出v9
exp
k = [
88, 105, 123, 6, 30, 56, 44, 32, 4, 15,
1, 7, 49, 107, 8, 14, 122, 10, 114, 114,
38, 55, 111, 73, 33, 22, 17, 47, 26, 13,
60, 31, 43, 50, 26, 52, 55, 127, 3, 68,
22, 14, 1, 40, 30, 104, 100, 35, 23, 9,
61, 100, 106, 105, 99, 24, 24, 10, 21, 112
]
x = [0] * len(k)
x[0] = k[0]
for i in range(1, len(k)):
x[i] = x[i-1] ^ k[i]
print("还原出来的 v9 数值序列:")
print(x)
# 尝试转成 ASCIIprint("对应的 ASCII 字符串:")
print("".join(chr(c) if 32 <= c < 127 else '.' for c in x))
X1JLRjFfbmlkZ197MG5GaV9pQGVycnRMfTNzM21ucmlDZ2VubkV2X1RJRXM=
进行base64解密得到
RKF1_nidg3s3mnriCgennEv_TIEs
利用加密前后字符位置的映射,还原目标正确位置
flag1 = []
flag2 = "_RKF1_nidg_{0nFi_i@errtL}3s3mnriCgennEv_TIEs"
s1 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr"
s2 = "fPgHhQiDjRkIlSmBnToJpUqErVKWAXLYFZMaCbNcGdOe"
for i in range(44):
c = s1[i]
index = s2.index(c)
flag1.append(flag2[index])
print("".join(flag1))
# miniLCTF{esrevER_gnir33nignE_Is_K1nd_0F_@rt}
x96re
v9 = 0;
v10 = 0;
v11 = 0;
v12 = 0;
v13 = 0;
v14 = 0;
v15 = 0;
v16 = 0;
v17 = 0;
v18 = 0;
v19 = 0;
v20 = 0;
v21 = 0;
v22 = 0;
v6[0] = 0x35323032;
v6[1] = 0x696E696D;
v6[2] = 0x6674636C;
v6[3] = 0x21212121; // 2025minilctf!!!!
*s = 0;
v24 = 0;
v25 = 0;
v26 = 0;
v27 = 0;
v28 = 0;
v29 = 0;
v30 = 0;
v31 = 0;
v32 = 0;
v33 = 0;
v34 = 0;
v35 = 0;
v7[0] = 0xDCBEE7D4;
v7[1] = 0x78FB2439;
v7[2] = 0xC06E8000;
v7[3] = 0xD3C34A2C;
v7[4] = 0xF53837D5;
v7[5] = 0xA9C8D88D;
v7[6] = 0x20CBDAE5;
v7[7] = 0x2551D478;
puts("please input flag:");
if ( !fgets(s, 50, stdin) )
goto LABEL_7;
v5 = strlen(s);
if ( v5 )
{
if ( s[v5 - 1] == 10 )
s[--v5] = 0;
}
if ( v5 == 32 )
{
LABEL_7:
init();
whathappened(); // xor 0x4c
encode_fun(0x20u, v6, s, v8); // sm4
for ( i = 0; i <= 0x1Fu; ++i )
{
if ( *(v8 + i) != *(v7 + i) )
{
printf("try again~~~");
return 1;
}
}
printf("congratulation!!!");
return 0;
}
else
{
printf("Invalid length!got %zu\n", v5);
return 1;
}
}
分析代码知道whathappened()先进行了运算,encode_fun()后面进行了sm4

动调拿到whathappened()函数的内容,进行与0x4c的异或,正常的范围为0-31但是索引计算公式是 (counter - 1),实际被异或的索引范围是:-1-30所以最后两个字节不进行异或。


flag为3ac159d665b4ccfb25c0927c1a23edb3

浙公网安备 33010602011771号