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

posted @ 2025-09-25 11:47  XXSL  阅读(17)  评论(0)    收藏  举报