跳至主要内容

LitCTF2026_WP

Reverse

  • Re题的考点基本在题目中都说了,非常的贴心,爱了爱了

lit_xor_chain

  • 丢到IDA中分析一下
    2bb2b475 3def 44e1 b41f 2d6d8621cb69
  • 发现就是异或后加一个数
  • 用赛博厨子即可
    406363c0 3d6e 4634 9a2b 9732096245ab 1

lit_b64_alphabet

  • 改了字母表

  • 依旧赛博厨子
    a7c2ebac 9c68 4096 ace1 a24c3a040138 1

lit_tea_standard

  • 考点题目说的很明确了

  • 搓个脚本即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import struct

def tea_decrypt(v: bytes, k: bytes) -> bytes:
v0, v1 = struct.unpack('>II', v)
k0, k1, k2, k3 = struct.unpack('>IIII', k)
delta = 0x9E3779B9
_sum = (delta * 32) & 0xFFFFFFFF
for _ in range(32):
v1 = (v1 - (((v0 << 4) + k2) ^ (v0 + _sum) ^ ((v0 >> 5) + k3))) & 0xFFFFFFFF
v0 = (v0 - (((v1 << 4) + k0) ^ (v1 + _sum) ^ ((v1 >> 5) + k1))) & 0xFFFFFFFF
_sum = (_sum - delta) & 0xFFFFFFFF
return struct.pack('>II', v0, v1)

# 从 IDA 提取的密钥 (k0, k1, k2, k3)
key = struct.pack('>IIII', 0x21524111, 0x35014542, 0x4FF4E200, 0x5EE31054)

# 从 g_cipher (0x140012040) 读取的 32 字节密文
cipher = bytes([
0xed, 0xef, 0x21, 0xfe, 0xb7, 0x9b, 0x3c, 0xb0,
0x1e, 0x93, 0x72, 0xe2, 0x02, 0x3e, 0x29, 0xbc,
0x36, 0xf7, 0x0c, 0x92, 0x2e, 0x5a, 0xae, 0x46,
0x44, 0xfa, 0x45, 0x25, 0x1a, 0xe5, 0x8c, 0x87
])

# TEA 每次处理 8 字节,32 字节分 4 块解密
plaintext = b""
for i in range(0, len(cipher), 8):
plaintext += tea_decrypt(cipher[i:i+8], key)

# 去掉 PKCS7 填充
pad_len = plaintext[-1]
plaintext = plaintext[:-pad_len]

print(f"Flag: {plaintext.decode()}")

lit_xtea_tweak

  • 题目啥都告诉你了

  • 搓脚本就ok了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import struct

def xtea_decrypt(v: bytes, k: bytes) -> bytes:
v0, v1 = struct.unpack('<II', v)
k0, k1, k2, k3 = struct.unpack('<IIII', k)
delta, mask = 0x21524111, 0xFFFFFFFF
_sum = (-delta * 32) & mask
keys = (k0, k1, k2, k3)
for _ in range(32):
v1 = (v1 - ((_sum + keys[(_sum >> 11) & 3]) ^ (v0 + ((v0 << 4) ^ (v0 >> 5))))) & mask
_sum = (_sum + delta) & mask
v0 = (v0 - ((_sum + keys[_sum & 3]) ^ (v1 + ((v1 << 4) ^ (v1 >> 5))))) & mask
return struct.pack('<II', v0, v1)

key = struct.pack('<IIII', 0x11111111, 0x22222222, 0x33333333, 0x44444444)
cipher = bytes([
0xE3, 0xEE, 0x1E, 0xE7, 0xD3, 0xA7, 0x96, 0x6F,
0xC6, 0xA7, 0xB9, 0xE1, 0xB9, 0x4E, 0x67, 0x86,
0x5F, 0x03, 0x04, 0xA6, 0xDB, 0xBB, 0xB9, 0x40,
0x56, 0x3A, 0xF7, 0x9E, 0xEE, 0x64, 0xD4, 0x06,
])

plaintext = b""
for i in range(0, len(cipher), 8):
plaintext += xtea_decrypt(cipher[i:i+8], key)

pad_len = plaintext[-1]
if 1 <= pad_len <= 8:
plaintext = plaintext[:-pad_len]

print(f"Flag: {plaintext.decode()}")

lit_rc4_variant

  • 魔改rc4

  • 魔改的点也告诉你了

  • 冷知识,rc4加密和解密是一样的

  • 所以把ida的伪c复制过来,简单改一下就可以用了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#include <stdio.h>
#include <stdint.h>
#include <string.h>

int main(){
uint8_t g_key[12] = {
0x6C, 0x69, 0x74, 0x5F, 0x72, 0x63, 0x34, 0x5F, 0x6B, 0x65, 0x79, 0x21
};
uint8_t g_cipher[29] = {
0x7B, 0x3D, 0x38, 0x77, 0x4E, 0x72, 0x42, 0x7D, 0x45, 0x37, 0x76, 0x0F, 0x53, 0x53, 0x4F, 0x66,
0x37, 0x17, 0x75, 0x37, 0x5F, 0x49, 0x58, 0x72, 0x74, 0x7F, 0x79, 0x1F, 0x3A
};
int v3; // ebx
uint8_t *v5; // rcx
uint8_t *v6; // r8
uint8_t *v7; // r11
uint8_t *v8; // r10
int i; // edx
int v10; // edx
char v11; // r9
unsigned int v12; // edx
char v13; // kr00_1
uint8_t *Buf1_1; // r8
int v15; // r11d
int v16; // r10d
uint8_t v17; // cl
uint8_t v18[64]; // [rsp+20h] [rbp-498h] BYREF
uint8_t v19[64]; // [rsp+60h] [rbp-458h] BYREF
uint8_t Buf1[29]; // [rsp+A0h] [rbp-418h] BYREF
char v21; // [rsp+BDh] [rbp-3FBh] BYREF
char Str[16]; // [rsp+2A0h] [rbp-218h] BYRE
v5 = v18;
v6 = v19;
v7 = v19;
v8 = v18;
for ( i = 0; i != 64; ++i )
{
*v8 = i;
*v7 = g_key[i % 12];
++v8;
++v7;
}
v10 = 0;
for(int i = 0;i < 64;i++){
v11 = *v5;
v13 = v10 + *v5 + *v6;
v12 = (v10 + (uint8_t)*v5 + (uint8_t)*v6) >> 31;
v10 = (((v12 >> 26) + v13) & 0x3F) - (v12 >> 26);
*v5 = v18[v10];
v18[v10] = v11;
++v6;
++v5;
}
memcpy(Buf1, g_cipher, 29);
Buf1_1 = Buf1;
v15 = 0;
v16 = 0;
for(int i = 0;i < 29;i++){
v16 = (v16 + 1) % 64;
v17 = v18[v16];
v15 = (v15 + v17) % 64;
v18[v16] = v18[v15];
v18[v15] = v17;
*Buf1_1++ ^= v18[(v18[v16] + v17) & 0x3F] + v18[v16];
}
printf("Flag:%s\n",Buf1);
}
  • 后记:

  • 经过了学姐的点播,发现了更快的方法

  • 把加密之前的输入数据改成对比数据

  • 然后打个断点运行就可以拿到flag了
    b02a8dfe 2ebf 4ead 943f abaeb1dd3cfc

  • 原理就是因为rc4是加解密流程一样,即便简单魔改(这里严谨下,不知道所有魔改是不是都是加解密流程一样)

  • 因为对比密文一般都是不可输入的16进制数字,所以只能在输入后patch成对比密文

  • 这样就可以不在乎rc4怎么魔改了,还可以不写脚本

  • 这个办法给到一个夯,我怎么没想到,还是没有逆向思维哇

  • hmmm,还是太笨了0.0

关于本文

由 GuQing 撰写,采用 CC BY-NC 4.0 许可协议。