TemporalParadox

拿到这道题目,我运行程序后发现报错

0

检查了一下我没缺少动态库就没管了,直接静态分析

0

主函数里面有两个分支,当程序在特定的时间点v58 > 1751990400 && v58 <= 1752052051运行时会输出query:这类东西,由t,r,cipher组成

0

另一个分支时,当不在时间段内就会要求用户输入然后进行md5加密和一个md5值进行比较(这里我对函数进行了重命名)你可以点进MD5那个函数看到

0

这是md5的特征

我们主要分析sub_140001963函数

0

0

0

这里有个if条件,当条件成立时输出的东西就是我们需要的明文

也就是&t=t&r=r&cipher=cipher的值

具体分析可以结合我写的注释进行代码理解

这里的salt值应该是可以直接调试出来的,但是我无法调试,就直接进行静态分析

0

这是salt生成的主要逻辑,以下是我编写的获取salt的脚本

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
#include <stdio.h>
#include<stdint.h>
#include<math.h>

int main()
{
//生成salt
uint32_t dword_14000B060[32] = { 0x000000CC, 0x000000B4, 0xFFFFFF94, 0xFFFFFF86, 0xFFFFFF9A, 0xFFFFFF8A,
0xFFFFFF9A, 0xFFFFFF8E, 0xFFE7AC2D, 0x000000A2, 0xFFFFFF9A,0x000000AE, 0xFFB70487, 0x000000D2, 0x000000CC,
0x000000DE, 0xFFFFFF96, 0x000000CC,0x000000CC, 0xFFFFE65F, 0xFFF7E40F, 0xFFFFFF86, 0x000000B4, 0xFFFFE65F,
0xFFFF1957, 0xFFFFFF94, 0xFFFFFF8C, 0xFFFFFF88, 0x000000C6, 0xFFFFFF98, 0xFFFFFF92, 0xFFFD4C05 };
char salt[33]{};
int v10 = 0;
for (int i = 0; i <= 31; ++i)
{
int32_t v9 = (int32_t)dword_14000B060[i];
if (v9 >= 0)
{
v10 = v9 / 3 + 48;
}
else
{
if (v9 >= -728)
v10 = ~v9;
else
v10 = (log(-v9) / 1.09861228866811 - 6.0 + 48.0);
}
salt[i] = v10;
}
salt[32] = '\0';
printf("%s", salt);
return 0;
}

这里的结果是tlkyeueq7fej8vtzitt26yl24kswrgm5,正好32字节

R和t以及cipher我是想着直接爆破得到

R和t这里的话没啥加密,主要是cipher生成这里有个ciphersub_14000184D函数,为了实现这一部分函数我花费了些时间,以下是函数逻辑

0

0

0

涉及到了两个换表

下面是我写的c语言代码整体实现sub_140001963这个关键函数

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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
#include<stdio.h>
#include<stdint.h>
#include<math.h>

uint32_t S_box[16] = { 0x0000000E, 0x00000004, 0x0000000D, 0x00000001, 0x00000002, 0x0000000F, 0x0000000B, 0x00000008, 0x00000003, 0x0000000A, 0x00000006, 0x0000000C, 0x00000005, 0x00000009,0x00000000, 0x00000007 };
uint32_t P_box[16] = { 0x00000001, 0x00000005, 0x00000009, 0x0000000D, 0x00000002, 0x00000006, 0x0000000A, 0x0000000E, 0x00000003, 0x00000007, 0x0000000B, 0x0000000F, 0x00000004, 0x00000008,0x0000000C, 0x00000010 };

uint32_t srange(uint32_t *dword_14000B040)
{
uint32_t v1;

v1 = (((*(dword_14000B040) << 13) ^ *(dword_14000B040)) >> 17) ^ (*(dword_14000B040) << 13) ^ *(dword_14000B040);
*(dword_14000B040) = (32 * v1) ^ v1;
return *(dword_14000B040) & 0x7FFFFFFF;
}



//cipher生成函数 ~~~~~~
uint32_t sub_1400016A0(uint32_t i, uint32_t r)
{
return (r << (4 * (i - 1)) >> 16);
}

uint32_t sub_1400016C1(uint32_t v12, uint32_t* S_box)
{
uint32_t result;
uint32_t i;

for (i = 0; i <= 3; ++i)
{
result = v12;
v12 = S_box[(v12 >> 12) & 0xF] | (16 *v12);
}
return result;

}

uint32_t sub_140001785(uint32_t v12, uint32_t* P_box)
{
uint32_t result;
uint32_t i;
uint32_t v6;

v6 = 0;
for (i = 0; i <= 15; ++i)
v6 |= ((v12 << (P_box[i] - 1)) & 0x8000) >> i;
result = v12;
v12 = v6;
return result;

}

uint32_t sub_1400017F7(uint32_t v12, uint32_t* S_box, uint32_t* P_box)
{
v12 = sub_1400016C1(v12, S_box);
return sub_140001785(v12,P_box);
}

uint32_t generate_cipher(uint32_t t, uint32_t r, uint32_t* S_box, uint32_t* P_box)
{
uint32_t v4;
uint32_t v5;
uint32_t v6;
uint32_t v7;
uint32_t v8;
uint32_t v9;
int v10;
uint32_t v12 = t;
for (int i = 1; i <= 3; ++i)
{
v4 = sub_1400016A0(i, r);
v5 = v12;
v12 ^= v4;
v12 = sub_1400017F7(v12, S_box, P_box);
}
v8 = sub_1400016A0(4, r);
v9 = v12;
v12 ^= v8;
for (uint32_t i = 0; i <= 3; ++i)
{
v12 = S_box[(v12 >> 12) & 0xF] | (16 *v12);
}
v10 = sub_1400016A0(5, r);
return v12 ^ v10;

}
// ~~~~~~


int main()
{
uint32_t y;
uint32_t x;
uint32_t b;
uint32_t a;
uint32_t r;
double v10 = 0x61;
double v12 = 0xb;
double value1, value2;


for (uint32_t t = 1751990400;t <= 1752052051;t++)
{
// 伪随机数生成
uint32_t dword = t;
uint32_t cnt = srange(&dword);
uint32_t i = 0;
while(i<cnt)
{
a = srange(&dword);
b = srange(&dword);
x = srange(&dword);
y = srange(&dword);
cnt = srange(&dword);
i++;
}
r = srange(&dword);

value1 = v10 * pow((a | x), 2.0);
value2 = pow((b | y), 2.0) * v12;
double diff = fabs(value1 - value2);
double denom = fmax(fabs(value1), fabs(value2));
int is_close = (denom == 0) ? (diff < 1e-9) : (diff / denom < 1e-9);
if (is_close)
{
int32_t cipher = generate_cipher(t, r, S_box, P_box);
printf("salt=tlkyeueq7fej8vtzitt26yl24kswrgm5");
printf("&t=%d", t);
printf("&r=%d", r);
printf("&cipher=%d", cipher);
printf("\n");
break;
}
else
printf("salt=tlkyeueq7fej8vtzitt26yl24kswrgm5&t=%d&r=%d&a=%d&b=%d&x=%d&y=%d\n", t, r, a, b, x, y);

}
return 0;
}

这边是可以正常输出在时间段内全部的数据,剩下就是进行一个比较了,这边我是因为不会写c语言的md5加密代码,我一直在找md5的头文件,一开始查到用的是openssl里面的头文件,然后我还专门下了一个openssl,但是下的最新版的运用后说vs2022已经不用了(安全问题),然后强制过掉警告又有新的报错,实在搞不下去了,我用AI直接帮我转化为python代码后进行一个md5值的比较

这边调教AI也花费了很多时间

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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
import math
from hashlib import md5, sha1

def gen(dword):
v1 = ((((dword << 13)&0xffffffff) ^ dword) >> 17) ^ ((dword << 13)&0xffffffff) ^ dword
dword = (((32 * v1)&0xffffffff) ^ v1) &0xffffffff
return dword, dword & 0x7FFFFFFF


S_BOX = [0x0000000E, 0x00000004, 0x0000000D, 0x00000001, 0x00000002, 0x0000000F, 0x0000000B, 0x00000008, 0x00000003, 0x0000000A, 0x00000006, 0x0000000C, 0x00000005, 0x00000009, 0x00000000, 0x00000007]

P_BOX = [0x00000001, 0x00000005, 0x00000009, 0x0000000D, 0x00000002, 0x00000006, 0x0000000A, 0x0000000E, 0x00000003, 0x00000007, 0x0000000B, 0x0000000F, 0x00000004, 0x00000008, 0x0000000C, 0x00000010]


def to_u32(n):
"""将一个数转换为32位无符号整数"""
return n & 0xFFFFFFFF


def to_s32(n):
"""将一个数转换为32位有符号整数"""
n = n & 0xFFFFFFFF
if n & 0x80000000:
return n - 0x100000000
return n


def s_box_transform(state, s_box_table):
"""
对应 C 函数 sub_7FF65E2B16C1 (S-盒替换)
"""
s = to_u32(state)
for _ in range(4):
# 提取高4位作为索引
index = (s >> 12) & 0xF
sbox_val = s_box_table[index]
# (16 * s) 等价于 (s << 4)
s = sbox_val | (s << 4)
return to_u32(s)


def p_box_transform(state, p_box_table):
"""
对应 C 函数 sub_7FF65E2B1785 (P-盒置换)
"""

s = to_u32(state)
new_state = 0
for i in range(16):
# 获取源比特的位置 (C数组是1-based, Python是0-based)
source_bit_pos = p_box_table[i] - 1
# 检查源比特是否为1
if (s >> source_bit_pos) & 1:
# 如果是1,则在目标位置i设置比特
new_state |= (1 << i)
return new_state


def round_function(state, s_box_table, p_box_table):
"""
对应 C 函数 sub_7FF65E2B17F7 (轮函数)
"""
state = s_box_transform(state, s_box_table)
state = p_box_transform(state, p_box_table)
return state


def generate_round_key(key, round_num):
"""
对应 C 函数 sub_7FF65E2B16A0 (轮密钥生成)
"""
key_u32 = to_u32(key)
shift_amount = 4 * (round_num - 1)
# C++ 代码中 (unsigned int) >> 是逻辑右移
shifted_key = key_u32 << shift_amount
return to_u32(shifted_key) >> 16


def encrypt_token(timestamp, r_key, s_box_table, p_box_table):
"""
对应 C 函数 sub_7FF65E2B184D (加密主函数)
"""
state = to_u32(timestamp)

# 循环 3 轮
for i in range(1, 4):
round_key = generate_round_key(r_key, i)
state ^= round_key
state = round_function(state, s_box_table, p_box_table)

# 循环后的第4步
round_key_4 = generate_round_key(r_key, 4)
state ^= round_key_4
state = s_box_transform(state, s_box_table)

# 最终返回前的第5步
round_key_5 = generate_round_key(r_key, 5)
final_state = state ^ round_key_5

return to_u32(final_state)


a = [0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476]
for t in range(1751990400, 1752052052):
dword = t
dword, ret = gen(dword)
cnt = ret
i = 0
while i < cnt:
dword, ret = gen(dword)
a = ret
dword, ret = gen(dword)
b = ret
dword, ret = gen(dword)
x = ret
dword, ret = gen(dword)
y = ret
dword, ret = gen(dword)
cnt = ret
i+=1
dword, ret = gen(dword)
r = ret
# pow(a | x, 2)
val1 = math.pow(float(to_s32(a) | to_s32(x)), 2.0)
# pow(b | y, 2)
val2 = math.pow(float(to_s32(b) | to_s32(y)), 2.0)

if math.isclose(0x61 * val1, 0xb * val2):
cipher = encrypt_token(
t, r,
S_BOX,
P_BOX
)
query = f"salt=tlkyeueq7fej8vtzitt26yl24kswrgm5&t={t}&r={r}&cipher={cipher}"
else:
query = f"salt=tlkyeueq7fej8vtzitt26yl24kswrgm5&t={t}&r={r}&a={a}&b={b}&x={x}&y={y}"
print(t, query)
if md5(query.encode()).hexdigest() == "8a2fc1e9e2830c37f8a7f51572a640aa":
print(sha1(query.encode()).hexdigest())
break

0

L3HCTF{5cbbe37231ca99bd009f7eb67f49a98caae2bb0f}

终焉之门

分析主函数代码

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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
__int64 sub_7FF6A04C1CF0()
{
int v0; // ebx
__m128i v1; // xmm6
unsigned int v2; // eax
unsigned int v3; // r13d
unsigned int v4; // eax
int v5; // eax
__int64 v6; // rdi
bool v7; // dl
char v8; // al
double v9; // xmm0_8
unsigned int v10; // eax
char *v12; // r15
unsigned int v13; // ebx
int v14; // eax
__int64 v15; // r9
int v16; // edx
int v17; // eax
int v18; // ecx
int v19; // eax
unsigned int v20; // [rsp+38h] [rbp-100h]
unsigned int v21; // [rsp+3Ch] [rbp-FCh]
unsigned int v22; // [rsp+40h] [rbp-F8h]
unsigned int v23; // [rsp+44h] [rbp-F4h]
unsigned int v24; // [rsp+48h] [rbp-F0h]
int v25; // [rsp+4Ch] [rbp-ECh]
__m128i v26; // [rsp+50h] [rbp-E8h] BYREF
int v27; // [rsp+6Ch] [rbp-CCh] BYREF
char Str[8]; // [rsp+70h] [rbp-C8h] BYREF
__int64 v29; // [rsp+78h] [rbp-C0h]
__int64 v30; // [rsp+80h] [rbp-B8h]
__int64 v31; // [rsp+88h] [rbp-B0h]
__int64 v32[7]; // [rsp+90h] [rbp-A8h] BYREF
__int64 v33[3]; // [rsp+C8h] [rbp-70h]

v0 = 0;
sub_7FF6A04BE370();
sub_7FF6A0453480(8256i64);
sub_7FF6A044F730(1280i64, 800i64, "Password Checker");
sub_7FF6A0451100(&v26, 0i64, &aVersion330Defi);
v1 = _mm_loadu_si128(&v26);
v2 = sub_7FF6A043E700(&aVersion430Core, 37305i64);
v20 = sub_7FF6A043EEE0(v2);
v21 = sub_7FF6A043EFF0(672i64, &opcodes, 35050i64);
v3 = sub_7FF6A043EFF0(128i64, &co_consts, 35050i64);
v22 = sub_7FF6A043EFF0(64i64, &cipher, 35050i64);
v23 = sub_7FF6A043EFF0(1024i64, &stack, 35050i64);
v4 = sub_7FF6A043EFF0(4i64, &out, 35050i64);
v33[0] = 0i64;
v24 = v4;
*Str = 0i64;
v29 = 0i64;
v30 = 0i64;
v31 = 0i64;
memset(v32, 0, sizeof(v32));
*(v33 + 5) = 0i64;
sub_7FF6A04531A0(60i64);
while ( !sub_7FF6A044CAC0() ) // 判断窗口是否关闭
{
v5 = get();
if ( v5 > 0 && v0 <= 99 )
{
v6 = v0 + 1;
do
{
Str[v6 - 1] = v5;
v0 = v6;
v5 = get();
v7 = v6++ <= 99;
}
while ( v7 && v5 > 0 );
}
v8 = sub_7FF6A04558E0(259i64); // 259:这是Backspace键的键码
if ( v0 > 0 && v8 )
Str[--v0] = 0;
if ( sub_7FF6A04558E0(257i64) && strlen(Str) == 40 && !strncmp(Str, "L3HCTF{", 7ui64) && HIBYTE(v32[0]) == 125 )
{
v25 = v0;
v12 = &Str[7];
v13 = 0;
do // 这个do-while循环是将输入的字符每两个一组加密拼接成16进制
{
v17 = *v12;
v18 = v12[1];
if ( v17 > 96 )
v14 = v17 - 87;
else
v14 = v17 - 48;
v19 = 16 * v14;
v15 = v13;
v16 = v18 - 48;
if ( v18 >= 97 )
v16 = v18 - 87;
v12 += 2;
v13 += 4;
v27 = v16 + v19;
sub_7FF6A043F0B0(v3, &v27, 4i64, v15); // 将用户加密输入存入co_consts前16元素
}
while ( v32 + 7 != v12 ); // 当v12移动到花括号部分的末尾就退出。
v0 = v25;
sub_7FF6A043C100(v20); // 下面这一块应该是加载计算着色器
sub_7FF6A043F180(v21, 0i64);
sub_7FF6A043F180(v3, 2i64);
sub_7FF6A043F180(v22, 3i64);
sub_7FF6A043F180(v23, 4i64);
sub_7FF6A043F180(v24, 5i64);
sub_7FF6A043EFE0(1i64, 1i64, 1i64);
sub_7FF6A043F140(v24, &out, 4i64, 0i64);
sub_7FF6A043C110();
}
sub_7FF6A044FC90();
v26 = v1;
sub_7FF6A0450650(&v26);
v9 = sub_7FF6A044E170();
v26 = v1;
*&v9 = v9;
v27 = LODWORD(v9);
v10 = sub_7FF6A0451440(&v26, "time");
v26 = v1;
sub_7FF6A0451460(&v26, v10, &v27, 0i64);
sub_7FF6A046B9D0(0, 0, 1280, 800, -1);
sub_7FF6A0450690();
printf(Str, 100, 200, 40, -16777216);
if ( out == 1 )
printf("success", 100, 300, 40, -13863680);
else
printf("wrong password", 100, 300, 40, -13162010);
printf("Type password and press [Enter] to check!", 100, 100, 20, -8224126);
printf("Press [Backspace] to delete characters.", 100, 130, 20, -8224126);
sub_7FF6A0455CE0();
}
sub_7FF6A044FAA0();
return 0i64;
}

可以根据进行的函数重命名和注释来理解,这里的大概意思就是将用户输入的32字节字符串加密拼接成16进制数据存入co_consts前16元素

我们看到

0

这里是著加密代码点进co_consts数组可以看到前16个元素都是留空的,给了后16个元素,这里我们依旧没有找到check函数,因为这道题目是把验证逻辑放进了OpenGL Compute Shader里,也就是aVersion430Core里面,点进去可以看到

0

0

很明显,我们需要找的check函数就是void main()

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
68
69
70
71
72
73
74
75
76
77
78
79
.data:00007FF6A04C3C2B db 'void main()',0Ah
.data:00007FF6A04C3C37 db '{',0Ah
.data:00007FF6A04C3C39 db ' if (gl_GlobalInvocationID.x > 0) return;',0Ah
.data:00007FF6A04C3C66 db 0Ah
.data:00007FF6A04C3C67 db ' uint ip = 0u;',0Ah
.data:00007FF6A04C3C79 db ' int sp = 0;',0Ah
.data:00007FF6A04C3C89 db ' verdict = -233;',0Ah
.data:00007FF6A04C3C9D db 0Ah
.data:00007FF6A04C3C9E db ' while (ip < uint(MaxInstructionCount))',0Ah
.data:00007FF6A04C3CC9 db ' {',0Ah
.data:00007FF6A04C3CCF db ' int opcode = opcodes[int(ip)];',0Ah
.data:00007FF6A04C3CF6 db ' int arg = opcodes[int(ip)+1];',0Ah
.data:00007FF6A04C3D1F db 0Ah
.data:00007FF6A04C3D20 db ' switch (opcode)',0Ah
.data:00007FF6A04C3D38 db ' {',0Ah
.data:00007FF6A04C3D42 db ' case 2:',0Ah
.data:00007FF6A04C3D56 db ' stack_data[sp++] = co_consts[arg];',0Ah
.data:00007FF6A04C3D89 db ' break;',0Ah
.data:00007FF6A04C3DA0 db ' case 7:',0Ah
.data:00007FF6A04C3DB4 db ' {',0Ah
.data:00007FF6A04C3DC2 db ' int b = stack_data[--sp];',0Ah
.data:00007FF6A04C3DEC db ' int a = stack_data[--sp];',0Ah
.data:00007FF6A04C3E16 db ' stack_data[sp++] = a + b;',0Ah
.data:00007FF6A04C3E40 db ' break;',0Ah
.data:00007FF6A04C3E57 db ' }',0Ah
.data:00007FF6A04C3E65 db ' case 8:',0Ah
.data:00007FF6A04C3E79 db ' {',0Ah
.data:00007FF6A04C3E87 db ' int a = stack_data[--sp];',0Ah
.data:00007FF6A04C3EB1 db ' int b = stack_data[--sp];',0Ah
.data:00007FF6A04C3EDB db ' stack_data[sp++] = a - b;',0Ah
.data:00007FF6A04C3F05 db ' break;',0Ah
.data:00007FF6A04C3F1C db ' }',0Ah
.data:00007FF6A04C3F2A db ' case 14:',0Ah
.data:00007FF6A04C3F3F db ' {',0Ah
.data:00007FF6A04C3F4D db ' int b = stack_data[--sp];',0Ah
.data:00007FF6A04C3F77 db ' int a = stack_data[--sp];',0Ah
.data:00007FF6A04C3FA1 db ' stack_data[sp++] = a ^ b;',0Ah
.data:00007FF6A04C3FCB db ' break;',0Ah
.data:00007FF6A04C3FE2 db ' }',0Ah
.data:00007FF6A04C3FF0 db 0Ah
.data:00007FF6A04C3FF1 db ' case 15:',0Ah
.data:00007FF6A04C4006 db ' {',0Ah
.data:00007FF6A04C4014 db ' int b = stack_data[--sp];',0Ah
.data:00007FF6A04C403E db ' int a = stack_data[--sp];',0Ah
.data:00007FF6A04C4068 db ' stack_data[sp++] = int(a == b);',0Ah
.data:00007FF6A04C4098 db ' break;',0Ah
.data:00007FF6A04C40AF db ' }',0Ah
.data:00007FF6A04C40BD db 0Ah
.data:00007FF6A04C40BE db ' case 16:',0Ah
.data:00007FF6A04C40D3 db ' {',0Ah
.data:00007FF6A04C40E1 db ' bool ok = true;',0Ah
.data:00007FF6A04C4101 db ' for (int i = 0; i < 16; i++)',0Ah
.data:00007FF6A04C412E db ' {',0Ah
.data:00007FF6A04C4140 db ' if (stack_data[i] != (cipher[i] - 20))',0Ah
.data:00007FF6A04C417B db ' { ',0Ah
.data:00007FF6A04C4192 db ' ok = false; ',0Ah
.data:00007FF6A04C41B7 db ' break; ',0Ah
.data:00007FF6A04C41D7 db ' }',0Ah
.data:00007FF6A04C41ED db ' }',0Ah
.data:00007FF6A04C41FF db ' verdict = ok ? 1 : -1;',0Ah
.data:00007FF6A04C4226 db ' return;',0Ah
.data:00007FF6A04C423E db ' }',0Ah
.data:00007FF6A04C424C db 0Ah
.data:00007FF6A04C424D db ' case 18:',0Ah
.data:00007FF6A04C4262 db ' {',0Ah
.data:00007FF6A04C4270 db ' int c = stack_data[--sp];',0Ah
.data:00007FF6A04C429A db ' if (c == 0) ip = uint(arg);',0Ah
.data:00007FF6A04C42C6 db ' break;',0Ah
.data:00007FF6A04C42DD db ' }',0Ah
.data:00007FF6A04C42EB db 0Ah
.data:00007FF6A04C42EC db ' default:',0Ah
.data:00007FF6A04C4301 db ' verdict = 500;',0Ah
.data:00007FF6A04C4320 db ' return;',0Ah
.data:00007FF6A04C4338 db ' }',0Ah
.data:00007FF6A04C4342 db 0Ah
.data:00007FF6A04C4343 db ' ip+=2;',0Ah
.data:00007FF6A04C4352 db ' }',0Ah
.data:00007FF6A04C4358 db ' verdict = 501;',0Ah
.data:00007FF6A04C436B db '}',0Ah,0

这是一个小型VM的实现,操作码在前面的主函数中给了就是opcode数组,我们直接编写代码将其模拟运行,进行输出就可以得到加密逻辑

以下是我写的VM运行脚本:

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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
#include <stdio.h>
#include <stdint.h>

int main()
{
int opcodes[] = {
2,0,2,1,2,0,14,0,2,16,8,0,2,2,2,1,14,0,2,17,8,0,2,3,2,2,14,0,2,18,7,0,
2,4,2,3,14,0,2,19,7,0,2,5,2,4,14,0,2,20,8,0,2,6,2,5,14,0,2,21,7,0,
2,7,2,6,14,0,2,22,7,0,2,8,2,7,14,0,2,23,7,0,2,9,2,8,14,0,2,24,7,0,
2,10,2,9,14,0,2,25,7,0,2,11,2,10,14,0,2,26,7,0,2,12,2,11,14,0,2,27,8,0,
2,13,2,12,14,0,2,28,8,0,2,14,2,13,14,0,2,29,7,0,2,15,2,14,14,0,2,30,8,0,
16,0,2,16,2,17,15,0,18,84,2,31,1,0,3,1
};

int co_consts[] = {
0xB0, 0xC8, 0xFA, 0x86, 0x6E, 0x8F, 0xAF, 0xBF,
0xC9, 0x64, 0xD7, 0xC3, 0xE3, 0xEF, 0x87, 0x00
};

int cipher[] = {
0xF3, 0x82, 0x06, 0x1FD, 0x150, 0x38, 0xB2, 0xDE,
0x15A, 0x197, 0x9C, 0x1D7, 0x6E, 0x28, 0x146, 0x97
};

int stack_data[256] = {};
int sp = 0;

for (int i = 0; i < 168; i += 2)
{
int opcode = opcodes[i];
int arg = opcodes[i + 1];
int sp0 = sp;

switch (opcode)
{
case 2:
{
int v = (arg < 16) ? co_consts[arg] : 0;
stack_data[sp++] = v;
printf("[IP=%d]\tstack_data[%d] = co_consts[%d] = %#x\n",
i / 2, sp - 1, arg, v);
break;
}
case 7:
{
int b = stack_data[--sp];
int a = stack_data[--sp];
stack_data[sp++] = a + b;
printf("[IP=%d]\tstack_data[%d] = a + b = stack_data[%d] + stack_data[%d] = %#x\n",
i / 2, sp - 1, sp0 - 2, sp0 - 1, a + b);
break;
}
case 8:
{
int a = stack_data[--sp];
int b = stack_data[--sp];
stack_data[sp++] = a - b;
printf("[IP=%d]\tstack_data[%d] = a - b = stack_data[%d] - stack_data[%d] = %#x\n",
i / 2, sp - 1, sp0 - 1, sp0 - 2, a - b);
break;
}
case 14:
{
int b = stack_data[--sp];
int a = stack_data[--sp];
stack_data[sp++] = a ^ b;
printf("[IP=%d]\tstack_data[%d] = a ^ b = stack_data[%d] ^ stack_data[%d] = %#x\n",
i / 2, sp - 1, sp0 - 2, sp0 - 1, a ^ b);
break;
}
case 15:
{
int b = stack_data[--sp];
int a = stack_data[--sp];
stack_data[sp++] = int(a == b);
printf("[IP=%d]\tstack_data[%d] = (a == b) = stack_data[%d] == stack_data[%d] = %#x\n",
i / 2, sp - 1, sp0 - 2, sp0 - 1, (a == b));
break;
}
case 16:
{
printf("[IP=%d]\t=== VERIFY cipher check ===\n", i / 2);
for (int j = 0; j < 16; j++)
{
printf(" stack[%2d]=0x%X vs cipher[%2d]-20=0x%X\n",
j, stack_data[j], j, cipher[j] - 20);
}
break;
}
case 18:
{
int c = stack_data[--sp];
printf("[IP=%d]\tJZ if top==0 jump to %d (top=%d)\n",
i / 2, arg, c);
if (c == 0)
{
i = arg * 2 - 2; // 跳转
}
break;
}
default:
{
printf("[IP=%d]\tUNKNOWN OPCODE %d, abort\n", i / 2, opcode);
return -1;
}
}
}

return 0;
}

得到运行逻辑:

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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
[IP=0]  stack_data[0] = co_consts[0] = 0xb0
[IP=1] stack_data[1] = co_consts[1] = 0xc8
[IP=2] stack_data[2] = co_consts[0] = 0xb0
[IP=3] stack_data[1] = a ^ b = stack_data[1] ^ stack_data[2] = 0x78
[IP=4] stack_data[2] = co_consts[16] = 0
[IP=5] stack_data[1] = a - b = stack_data[2] - stack_data[1] = 0xffffff88
[IP=6] stack_data[2] = co_consts[2] = 0xfa
[IP=7] stack_data[3] = co_consts[1] = 0xc8
[IP=8] stack_data[2] = a ^ b = stack_data[2] ^ stack_data[3] = 0x32
[IP=9] stack_data[3] = co_consts[17] = 0
[IP=10] stack_data[2] = a - b = stack_data[3] - stack_data[2] = 0xffffffce
[IP=11] stack_data[3] = co_consts[3] = 0x86
[IP=12] stack_data[4] = co_consts[2] = 0xfa
[IP=13] stack_data[3] = a ^ b = stack_data[3] ^ stack_data[4] = 0x7c
[IP=14] stack_data[4] = co_consts[18] = 0
[IP=15] stack_data[3] = a + b = stack_data[3] + stack_data[4] = 0x7c
[IP=16] stack_data[4] = co_consts[4] = 0x6e
[IP=17] stack_data[5] = co_consts[3] = 0x86
[IP=18] stack_data[4] = a ^ b = stack_data[4] ^ stack_data[5] = 0xe8
[IP=19] stack_data[5] = co_consts[19] = 0
[IP=20] stack_data[4] = a + b = stack_data[4] + stack_data[5] = 0xe8
[IP=21] stack_data[5] = co_consts[5] = 0x8f
[IP=22] stack_data[6] = co_consts[4] = 0x6e
[IP=23] stack_data[5] = a ^ b = stack_data[5] ^ stack_data[6] = 0xe1
[IP=24] stack_data[6] = co_consts[20] = 0
[IP=25] stack_data[5] = a - b = stack_data[6] - stack_data[5] = 0xffffff1f
[IP=26] stack_data[6] = co_consts[6] = 0xaf
[IP=27] stack_data[7] = co_consts[5] = 0x8f
[IP=28] stack_data[6] = a ^ b = stack_data[6] ^ stack_data[7] = 0x20
[IP=29] stack_data[7] = co_consts[21] = 0
[IP=30] stack_data[6] = a + b = stack_data[6] + stack_data[7] = 0x20
[IP=31] stack_data[7] = co_consts[7] = 0xbf
[IP=32] stack_data[8] = co_consts[6] = 0xaf
[IP=33] stack_data[7] = a ^ b = stack_data[7] ^ stack_data[8] = 0x10
[IP=34] stack_data[8] = co_consts[22] = 0
[IP=35] stack_data[7] = a + b = stack_data[7] + stack_data[8] = 0x10
[IP=36] stack_data[8] = co_consts[8] = 0xc9
[IP=37] stack_data[9] = co_consts[7] = 0xbf
[IP=38] stack_data[8] = a ^ b = stack_data[8] ^ stack_data[9] = 0x76
[IP=39] stack_data[9] = co_consts[23] = 0
[IP=40] stack_data[8] = a + b = stack_data[8] + stack_data[9] = 0x76
[IP=41] stack_data[9] = co_consts[9] = 0x64
[IP=42] stack_data[10] = co_consts[8] = 0xc9
[IP=43] stack_data[9] = a ^ b = stack_data[9] ^ stack_data[10] = 0xad
[IP=44] stack_data[10] = co_consts[24] = 0
[IP=45] stack_data[9] = a + b = stack_data[9] + stack_data[10] = 0xad
[IP=46] stack_data[10] = co_consts[10] = 0xd7
[IP=47] stack_data[11] = co_consts[9] = 0x64
[IP=48] stack_data[10] = a ^ b = stack_data[10] ^ stack_data[11] = 0xb3
[IP=49] stack_data[11] = co_consts[25] = 0
[IP=50] stack_data[10] = a + b = stack_data[10] + stack_data[11] = 0xb3
[IP=51] stack_data[11] = co_consts[11] = 0xc3
[IP=52] stack_data[12] = co_consts[10] = 0xd7
[IP=53] stack_data[11] = a ^ b = stack_data[11] ^ stack_data[12] = 0x14
[IP=54] stack_data[12] = co_consts[26] = 0
[IP=55] stack_data[11] = a + b = stack_data[11] + stack_data[12] = 0x14
[IP=56] stack_data[12] = co_consts[12] = 0xe3
[IP=57] stack_data[13] = co_consts[11] = 0xc3
[IP=58] stack_data[12] = a ^ b = stack_data[12] ^ stack_data[13] = 0x20
[IP=59] stack_data[13] = co_consts[27] = 0
[IP=60] stack_data[12] = a - b = stack_data[13] - stack_data[12] = 0xffffffe0
[IP=61] stack_data[13] = co_consts[13] = 0xef
[IP=62] stack_data[14] = co_consts[12] = 0xe3
[IP=63] stack_data[13] = a ^ b = stack_data[13] ^ stack_data[14] = 0xc
[IP=64] stack_data[14] = co_consts[28] = 0
[IP=65] stack_data[13] = a - b = stack_data[14] - stack_data[13] = 0xfffffff4
[IP=66] stack_data[14] = co_consts[14] = 0x87
[IP=67] stack_data[15] = co_consts[13] = 0xef
[IP=68] stack_data[14] = a ^ b = stack_data[14] ^ stack_data[15] = 0x68
[IP=69] stack_data[15] = co_consts[29] = 0
[IP=70] stack_data[14] = a + b = stack_data[14] + stack_data[15] = 0x68
[IP=71] stack_data[15] = co_consts[15] = 0
[IP=72] stack_data[16] = co_consts[14] = 0x87
[IP=73] stack_data[15] = a ^ b = stack_data[15] ^ stack_data[16] = 0x87
[IP=74] stack_data[16] = co_consts[30] = 0
[IP=75] stack_data[15] = a - b = stack_data[16] - stack_data[15] = 0xffffff79
[IP=76] === VERIFY cipher check ===
stack[ 0]=0xB0 vs cipher[ 0]-20=0xDF
stack[ 1]=0xFFFFFF88 vs cipher[ 1]-20=0x6E
stack[ 2]=0xFFFFFFCE vs cipher[ 2]-20=0xFFFFFFF2
stack[ 3]=0x7C vs cipher[ 3]-20=0x1E9
stack[ 4]=0xE8 vs cipher[ 4]-20=0x13C
stack[ 5]=0xFFFFFF1F vs cipher[ 5]-20=0x24
stack[ 6]=0x20 vs cipher[ 6]-20=0x9E
stack[ 7]=0x10 vs cipher[ 7]-20=0xCA
stack[ 8]=0x76 vs cipher[ 8]-20=0x146
stack[ 9]=0xAD vs cipher[ 9]-20=0x183
stack[10]=0xB3 vs cipher[10]-20=0x88
stack[11]=0x14 vs cipher[11]-20=0x1C3
stack[12]=0xFFFFFFE0 vs cipher[12]-20=0x5A
stack[13]=0xFFFFFFF4 vs cipher[13]-20=0x14
stack[14]=0x68 vs cipher[14]-20=0x132
stack[15]=0xFFFFFF79 vs cipher[15]-20=0x83
[IP=77] stack_data[16] = co_consts[16] = 0 //这下面一块没用
[IP=78] stack_data[17] = co_consts[17] = 0
[IP=79] stack_data[16] = (a == b) = stack_data[16] == stack_data[17] = 0x1
[IP=80] JZ if top==0 jump to 84 (top=1)
[IP=81] stack_data[16] = co_consts[31] = 0
[IP=82] UNKNOWN OPCODE 1, abort

这个VM只进行了异或和加减法

将用户输入加密后的前16个元素和固定的后16个元素进行混合加密后和密文cipher进行比较。现在我们有了cipher和co_consts的后十六字节,我们可以进行解密

解密分析

这里我们选择从 IP = 6进行分析,因为从IP = 0进行分析会很懵 别问我咋知道的

IP = 0这里进行的加密和IP = 6的加密一样,都是case 8

1
2
3
4
5
[IP=6]  stack_data[2] = co_consts[2] = 0xfa
[IP=7] stack_data[3] = co_consts[1] = 0xc8
[IP=8] stack_data[2] = a ^ b = stack_data[2] ^ stack_data[3] = 0x32
[IP=9] stack_data[3] = co_consts[17] = 0
[IP=10] stack_data[2] = a - b = stack_data[3] - stack_data[2] = 0xffffffce

最后的结果就是cipher[2]

[IP=10] stack_data[2] = a - b = stack_data[3] - stack_data[2] = 0xffffffce = cipher[2]

这里的[IP=7] stack_data[3] = co_consts[1]就是相当于

[IP=7] stack_data[3] = cipher[1]

也就是说这里先将用户输入的第二位与密文cipher[1]进行异或后与co_consts的固定值co_consts[17]向减

好那么上面的[IP=2] stack_data[2] = co_consts[0]就相当于[IP=2] stack_data[2] = co_consts[0] = cipher[0]

后面的加法也是一样的分析,也就是未对co_consts[0]进行操作

解密代码

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
#include <stdio.h>
#include <stdint.h>

int main() {
int CIPHER[16] = {
0xF3, 0x82, 0x06, 0x1FD, 0x150, 0x38, 0xB2, 0xDE,
0x15A, 0x197, 0x9C, 0x1D7, 0x6E, 0x28, 0x146, 0x97
};

uint8_t CONSTS[16] = {
0xB0, 0xC8, 0xFA, 0x86, 0x6E, 0x8F, 0xAF, 0xBF,
0xC9, 0x64, 0xD7, 0xC3, 0xE3, 0xEF, 0x87, 0x00
};

uint8_t op_pattern[15] = {
1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1
};

int targets[16];
for (int i = 0; i < 16; i++) {
targets[i] = CIPHER[i] - 20;
}

uint8_t inputs[16] = { 0 };

inputs[0] = (uint8_t)(targets[0]);

for (int i = 1; i < 16; i++) {
uint8_t prev_input = inputs[i - 1];
int target = targets[i];
uint8_t constant = CONSTS[i - 1];
uint8_t op_type = op_pattern[i - 1];

uint8_t flag;

if (op_type == 0) {
// ADD
flag = (uint8_t)((target - constant) & 0xff);
}
else if (op_type == 1) {
// SUB
flag = (uint8_t)((constant - target) & 0xff);
}

inputs[i] = flag ^ prev_input;
}

for (int i = 0; i < 16; i++) {
printf("%02x", inputs[i]);
}
printf("\n");

return 0;
}////df9d4ba41258574ccb7155b9d01f5c58

L3HCTF{df9d4ba41258574ccb7155b9d01f5c58}

easyvm(复现)

找到主函数

如果反编译是一大串函数的话就 ida 打开先设置Options->Compiler… [Compiler: Visual C++]

这边我进行一个重命名

cmp函数存放着32位的数组密文

很清晰了,我们进入VM实现函数发现有很多switch-case函数

这就是VM的handle步骤

我们找到主要操作码分类

算术运算

  • 加法 (0x10)
  • 减法 (0x11)
  • 乘法 (0x12)
  • 除法 (0x13)
  • 取模 (0x14)
  • 位移 (0x16左移, 0x17右移)
  • 异或 (0x18)

这边的话我们可以通过条件断点的方式进行查看该vm的具体加密流程,我们可以在add,sub,xor,shl,shr进行条件断点(乘除和取余的话一般是无法逆向恢复的)

给出idapython代码(参考SU的WP

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
import idc, idaapi
op1_val = idc.get_reg_value("EDX")
op2_val = idc.get_reg_value("ECX") & 0xFF
print(f"shl {hex(op1_val)}, {hex(op2_val)} = {hex((op1_val<<op2_val)&0xffffffff)}")

import idc, idaapi
op1_val = idc.get_reg_value("EDX")
op2_val = idc.get_reg_value("ECX") & 0xFF
print(f"shr {hex(op1_val)}, {hex(op2_val)} = {hex((op1_val>>op2_val)&0xffffffff)}")

import idc, idaapi
op1_val = idc.get_reg_value("EAX")
rbp_val = idc.get_reg_value("RBP")
mem_addr = rbp_val + 0x4C
op2_val = idc.get_wide_dword(mem_addr)
print(f"xor {hex(op1_val)}, {hex(op2_val)} = {hex((op1_val^op2_val)&0xffffffff)}")

import idc, idaapi
op1_val = idc.get_reg_value("EAX")
rbp_val = idc.get_reg_value("RBP")
mem_addr = rbp_val + 0x1C
op2_val = idc.get_wide_dword(mem_addr)
print(f"sub {hex(op1_val)}, {hex(op2_val)} = {hex((op1_val-op2_val)&0xffffffff)}")

import idc, idaapi
op1_val = idc.get_reg_value("EAX")
op2_val = idc.get_reg_value("EDX")
print(f"add {hex(op1_val)}, {hex(op2_val)} = {hex((op1_val+op2_val)&0xffffffff)}")

得到加密逻辑,取一段进行分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
shl 0x31313131, 0x3 = 0x89898988
add 0xa56babcd, 0x89898988 = 0x2ef53555
add 0x0, 0x31313131 = 0x31313131
add 0x0, 0x31313131 = 0x31313131
xor 0x31313131, 0x2ef53555 = 0x1fc40464
shr 0x31313131, 0x4 = 0x3131313
add 0xffffffff, 0x3131313 = 0x3131312
xor 0x1fc40464, 0x3131312 = 0x1cd71776
add 0x31313131, 0x1cd71776 = 0x4e0848a7
add 0x11223344, 0x0 = 0x11223344
shl 0x4e0848a7, 0x2 = 0x3821229c
add 0xffffffff, 0x3821229c = 0x3821229b
add 0x11223344, 0x4e0848a7 = 0x5f2a7beb
add 0xabcdef01, 0x5f2a7beb = 0xaf86aec
xor 0xaf86aec, 0x3821229b = 0x32d94877
shr 0x4e0848a7, 0x5 = 0x2704245
add 0xa56babcd, 0x2704245 = 0xa7dbee12
xor 0x32d94877, 0xa7dbee12 = 0x9502a665
add 0x31313131, 0x9502a665 = 0xc633d796
sub 0x40, 0x1 = 0x3f

这边就可以通过代码一行一行进行复原,能知道是一个魔改的xtea加密,有个点要注意一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
add 0x11223344, 0x376a9dbc = 0x488cd100   // 上一组sum值
shl 0x574756d1, 0x2 = 0x5d1d5b44
add 0xffffffff, 0x5d1d5b44 = 0x5d1d5b43
add 0x488cd100, 0x574756d1 = 0x9fd427d1
add 0xabcdef01, 0x9fd427d1 = 0x4ba216d2
xor 0x4ba216d2, 0x5d1d5b43 = 0x16bf4d91
shr 0x574756d1, 0x5 = 0x2ba3ab6
add 0xa56babcd, 0x2ba3ab6 = 0xa825e683
xor 0x16bf4d91, 0xa825e683 = 0xbe9aab12
add 0x1ca93e77, 0xbe9aab12 = 0xdb43e989
sub 0x1, 0x1 = 0x0
shl 0x31313131, 0x3 = 0x89898988
add 0xa56babcd, 0x89898988 = 0x2ef53555
add 0x488cd100, 0x31313131 = 0x79be0231
add 0x0, 0x79be0231 = 0x79be0231
xor 0x79be0231, 0x2ef53555 = 0x574b3764
shr 0x31313131, 0x4 = 0x3131313
add 0xffffffff, 0x3131313 = 0x3131312
xor 0x574b3764, 0x3131312 = 0x54582476
add 0x31313131, 0x54582476 = 0x858955a7
add 0x11223344, 0x488cd100 = 0x59af0444 //// 下一组sum值

下一组加密8字节用的sum值是上一组结束后的sum值

给出解密脚本

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
#include<stdio.h>
#include<stdint.h>
#define dalte 0x11223344



void encypt(uint32_t *v, uint32_t key[3])
{

uint32_t v0 = v[0], v1 = v[1];
uint32_t sum = 0;
for(int i = 0;i<64;i++)
{
uint32_t key[4] = { 0xa56babcd ,0xffffffff,0xabcdef01 };

v0 += ((v1 << 3) + key[0]) ^ (sum + v1+key[1]) ^ ((v1 >> 4) + key[2]);
sum += dalte;
v1 += ((v0 << 2) + key[2]) ^ (sum + v0 + key[3]) ^ ((v0 >> 5) + key[0]);
}
v[0] = v0;
v[1] = v1;
}

void decypt(uint32_t v[2], uint32_t* key, uint32_t i)
{
uint32_t v0 = v[0], v1 = v[1];
i += 1;
uint32_t sum = dalte * 64 * i;
for (int i = 0;i < 64;i++)
{
v1 -= ((v0 << 2) + key[2]) ^ (sum + v0 + key[3]) ^ ((v0 >> 5) + key[0]);
sum -= dalte;
v0 -= ((v1 << 3) + key[0]) ^ (sum + v1+key[1]) ^ ((v1 >> 4) + key[2]);
}
v[0] = v0;
v[1] = v1;

}

int main()
{
uint32_t enc[] = { 0x877A62A6,0x6A55F1F3,0xAE194847,0xB1E643E7,0xA94FE881,0x9BC8A28A,0xC4CFAA9F,0xF1A00CA1 };
uint32_t key[4] = { 0xa56babcd ,0x00,0xffffffff,0xabcdef01 };
uint32_t i = 0;
for(int i = 0;i<4;i++)
{
decypt(&enc[i * 2], key, i);
}
printf("%s", enc);

return 0;
}//9c50d10ba864bedfb37d7efa4e110bf2

L3HCTF{9c50d10ba864bedfb37d7efa4e110bf2}