前言

第三届磐石行动 2025 初赛

比赛时间:2025.8.6 9:00 - 2025.8.6 21:00

考点:花指令,Tea

经典jx+jnx型花指令,恢复的代码:

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
void __fastcall sub_11A9(unsigned int *a1, _DWORD *a2)
{
int i; // [rsp+18h] [rbp-28h]
unsigned int v3; // [rsp+1Ch] [rbp-24h]
unsigned int v4; // [rsp+20h] [rbp-20h]
int v5; // [rsp+24h] [rbp-1Ch]
unsigned int j; // [rsp+28h] [rbp-18h]

for ( i = 0; i <= 3; ++i )
{
v3 = a1[2 * i];
v4 = a1[2 * i + 1];
v5 = 0;
for ( j = 0; j <= 0x1F; ++j )
{
v5 -= 1988930350;
v3 += v5 ^ (v4 + v5) ^ (16 * v4 + *a2) ^ ((v4 >> 5) + a2[1]);
v4 += v5 ^ (v3 + v5) ^ (16 * v3 + a2[2]) ^ ((v3 >> 5) + a2[3]);
}
a1[2 * i] = v3;
a1[2 * i + 1] = v4;
}
}

__int64 __fastcall main(int a1, char **a2, char **a3)
{
__int64 v3; // rax
int i; // [rsp+4h] [rbp-6Ch]
_DWORD v6[4]; // [rsp+10h] [rbp-60h] BYREF
_DWORD v7[8]; // [rsp+20h] [rbp-50h]
_QWORD buf[6]; // [rsp+40h] [rbp-30h] BYREF

buf[5] = __readfsqword(0x28u);
v6[0] = 2;
v6[1] = 0;
v6[2] = 2;
v6[3] = 2;
memset(buf, 0, 32);
v7[0] = 1452940357;
v7[1] = -282301936;
v7[2] = -79426602;
v7[3] = 1469576221;
v7[4] = 1379922627;
v7[5] = 1211333849;
v7[6] = 907455533;
v7[7] = 112603437;
puts("Pls input flag");
read(0, buf, 0x20uLL);
sub_11A9((unsigned int *)buf, v6);
for ( i = 0; i <= 3; ++i )
{
if ( v7[i] != *(_DWORD *)(4LL * i + v3) || v7[2 * i + 1] != *(_DWORD *)(4 * (2 * i + 1LL) + v3) )
{
puts("ERROR");
_exit(0);
}
}
puts("Success");
return 0LL;
}

解密脚本

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

void tea_dec(unsigned int *data, unsigned int *key);

int main()
{

unsigned int key[4] = {2, 0, 2, 2};

unsigned int ciphertext[8] = {
0x569A1C45, 0xEF2C6A10,
0xFB440BD6, 0x5797F41D,
0x523FF2C3, 0x48337CD9,
0x3616AC2D, 0x06B6312D // 补全为32位
};

unsigned int plaintext[8];
memcpy(plaintext, ciphertext, sizeof(plaintext));

for (int i = 0; i < 4; i++)
{
tea_dec(&plaintext[2 * i], key);
}

plaintext[8] = 0;

printf("Decrypted flag: %s\n", (char *)plaintext);

return 0;
}

void tea_dec(unsigned int *data, unsigned int *key)
{
unsigned int v0 = data[0];
unsigned int v1 = data[1];
int sum;
const unsigned int delta = 0x768CAB2E;

sum = -32 * delta;

for (int j = 0; j <= 0x1F; j++)
{
v1 -= sum ^ (v0 + sum) ^ (16 * v0 + key[2]) ^ ((v0 >> 5) + key[3]);
v0 -= sum ^ (v1 + sum) ^ (16 * v1 + key[0]) ^ ((v1 >> 5) + key[1]);
sum += delta;
}

// 保存解密结果
data[0] = v0;
data[1] = v1;
}

flag{b3d06a66f8aa86e3e6390f615e389e55}

My-Key

考点:MFC,反调试,RC6

追踪IsDebuggerPresent的引用,绕过反调试

image-20250829212525187

注意到字符串 ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/ ,交叉引用找到检验flag的函数,追踪变量,找到关键函数

1
2
3
4
5
6
7
8
9
__int64 __fastcall sub_1400040A0(__int64 a1)
{
...
encrypt(v25, v22, v24, enc_flag); // FSZ36f3vU8s5, WcE4Bbm4kHYQsAcX, 114514
base64((__int64)b64enc_flag, (__int64)enc_flag);
v6 = compare(b64enc_flag, enc); // RKCTaz+fty1J2qsz4DI6t9bmMiLBxqFrpI70fU4IMemczIlM+z1IoVQobIt1MbXF
...
return v6;
}

动态调试一次,确定base64没有被魔改。

1
2
EA 45 A9 DF 64 2B D8 9C 72 2B 7B 45 80 F0 2E 93
6kWp32Qr2JxyK3tFgPAukw==
  1. 专注于分析加密函数,追踪数据流,确定加密在循环中。
  2. 看循环可以推测,这是个16Byte的分组密码。
  3. 调试发现异或的WcE4Bbm4kHYQsAcX一直在继承上一次的密文,所以是CBC模式中的IV。
  4. v33是长度176字节
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Hidden C++ exception states: #wind=7
__int64 __fastcall encrypt(_QWORD *a1, __int64 a2, __int64 a3, _QWORD *ciphertext)
{
...
for ( i = 0LL; ; i += 16LL )
{
length_padded = sub_1400049D0(plaintext);
if ( i >= length_padded )
break;
for ( j = 0; j < 16; ++j )
{
plainblock = (_BYTE *)sub_1400049A0(plaintext, j + i);
v37[j] = iv[j] ^ *plainblock;
}
block_encrypt(v37, cipherblock, v33);
v22 = *(_QWORD *)sub_140004A90((__int64)ciphertext, (__int64)v29);
sub_140004560((_DWORD)ciphertext, (unsigned int)&v30, v22, (unsigned int)cipherblock, (char)v35);
sub_140004660(cipherblock, v35, iv);
}
sub_140005150((__int64)plaintext);
return sub_140005150((__int64)key);
}

查看加密函数,可以判断是RC6加密。

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
unsigned int *__fastcall block_encrypt(unsigned int *a1, unsigned int *a2, _DWORD *a3)
{
unsigned int *result; // rax
int i; // [rsp+20h] [rbp-38h]
unsigned int v5; // [rsp+24h] [rbp-34h]
unsigned int v6; // [rsp+28h] [rbp-30h]
unsigned int v7; // [rsp+2Ch] [rbp-2Ch] BYREF
unsigned int v8; // [rsp+30h] [rbp-28h] BYREF
unsigned int v9; // [rsp+34h] [rbp-24h] BYREF
unsigned int v10; // [rsp+38h] [rbp-20h] BYREF

v9 = *a1;
v7 = a1[1];
v8 = a1[2];
v10 = a1[3];
v7 += *a3;
v10 += a3[1];
for ( i = 1; i <= 20; ++i )
{
v6 = _ROL4__((2 * v7 + 1) * v7, 5LL);
v5 = _ROL4__((2 * v10 + 1) * v10, 5LL);
v9 = a3[2 * i] + _ROL4__(v6 ^ v9, v5);
v8 = a3[2 * i + 1] + _ROL4__(v5 ^ v8, v6);
swap(&v9, &v7);
swap(&v7, &v8);
swap(&v8, &v10);
}
v9 += a3[42];
v8 += a3[43];
*a2 = v9;
a2[1] = v7;
a2[2] = v8;
result = a2;
a2[3] = v10;
return result;
}

现在可以动态调试提取176的拓展key,脚本解密

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

#define w 32 // word size in bits
#define r 20 // number of rounds
#define ROTL(x, y) (((x) << (y & (w-1))) | ((x) >> (w - (y & (w-1)))))
#define ROTR(x, y) (((x) >> (y & (w-1))) | ((x) << (w - (y & (w-1)))))

unsigned char S_char[] =
{
0xE0, 0xAA, 0x68, 0x73, 0x7D, 0xCD, 0x54, 0x72, 0xE2, 0xAA,
0xD4, 0xFA, 0x41, 0x0C, 0x03, 0x9C, 0x51, 0xCA, 0x72, 0x5D,
0xF4, 0x53, 0xCA, 0xAD, 0x25, 0xEF, 0x26, 0x13, 0x8F, 0x14,
0xC1, 0x48, 0x40, 0x26, 0x1C, 0x0D, 0x6D, 0x91, 0x32, 0x16,
0xF8, 0xFC, 0x4F, 0xB5, 0xF9, 0x5F, 0x2C, 0x97, 0xEC, 0x64,
0x34, 0x6B, 0xB3, 0xFD, 0xB4, 0x89, 0xBE, 0xA5, 0x2D, 0x51,
0x04, 0x37, 0x18, 0x85, 0xB3, 0x88, 0x0D, 0xB8, 0x52, 0x05,
0x8E, 0xCD, 0x8C, 0xD8, 0xB3, 0x4F, 0x74, 0x81, 0xA6, 0xE2,
0xDF, 0x35, 0x68, 0x40, 0xA5, 0x1A, 0x49, 0x53, 0x05, 0x7C,
0x44, 0x53, 0xFA, 0xCB, 0x4F, 0xDB, 0xD8, 0xDC, 0x04, 0x31,
0x22, 0xF9, 0xD6, 0xB9, 0x6E, 0x1F, 0x53, 0xE5, 0x4E, 0xB6,
0x30, 0xAB, 0xA0, 0x4B, 0x7B, 0xC8, 0x7E, 0xB1, 0x21, 0x98,
0xDC, 0xAA, 0xFB, 0xB0, 0xC2, 0x72, 0x39, 0xD8, 0x11, 0xFE,
0x81, 0x7C, 0xE0, 0x6E, 0xBC, 0x99, 0x68, 0x6A, 0xA1, 0xBA,
0xA9, 0xED, 0x8E, 0x15, 0x5B, 0x20, 0x58, 0x2A, 0xCC, 0xB1,
0x85, 0xC9, 0xE3, 0x0B, 0x21, 0xD7, 0x7B, 0xBF, 0x5B, 0x5D,
0xC2, 0x76, 0xEB, 0x64, 0xD8, 0xC8, 0xE3, 0x44, 0x5F, 0xC7,
0xDF, 0xD9, 0x8D, 0x23, 0x1C, 0x54
};
uint32_t *S = (uint32_t *)S_char; // Round keys

void rc6_decrypt(const uint32_t in[4], uint32_t out[4]) {
uint32_t A = in[0];
uint32_t B = in[1];
uint32_t C = in[2];
uint32_t D = in[3];

C = C - S[2*r+3];
A = A - S[2*r+2];

for (int i = r; i >= 1; i--) {
uint32_t temp = D;
D = C;
C = B;
B = A;
A = temp;

uint32_t u = ROTL(D * (2*D + 1), 5);
uint32_t t = ROTL(B * (2*B + 1), 5);
C = ROTR(C - S[2*i+1], t) ^ u;
A = ROTR(A - S[2*i], u) ^ t;
}

D = D - S[1];
B = B - S[0];

out[0] = A;
out[1] = B;
out[2] = C;
out[3] = D;
}

int main()
{
unsigned char plain[128] = {0};
unsigned char data[] =
{
0x44,0xa0,0x93,0x6b,0x3f,0x9f,0xb7,0x2d,
0x49,0xda,0xab,0x33,0xe0,0x32,0x3a,0xb7,
0xd6,0xe6,0x32,0x22,0xc1,0xc6,0xa1,0x6b,
0xa4,0x8e,0xf4,0x7d,0x4e,0x08,0x31,0xe9,
0x9c,0xcc,0x89,0x4c,0xfb,0x3d,0x48,0xa1,
0x54,0x28,0x6c,0x8b,0x75,0x31,0xb5,0xc5
};

unsigned char xor_key[] = "WcE4Bbm4kHYQsAcX";
unsigned int *Plain = (unsigned int *)plain;
unsigned int *Data = (unsigned int *)data;

rc6_decrypt(Data,Plain);
rc6_decrypt(Data+4,Plain+4);
rc6_decrypt(Data+8,Plain+8);


for (int j = 0; j < 16; j++)
{
plain[j] ^= xor_key[j];
}
for (int j = 16; j < 48; j++)
{
plain[j] ^= data[j - 16];
}
printf("%s\n",plain);
}

或者借用网站,再看前面的代码可以知道,前面是对密钥进行PKCS7填充。

image-20250829233735983

EasyRE

考点:反调试,RC4

反调试,前两处动调的时候把ZF位改为1

img

加密过程只有两段,魔改RC4+Xor,这个函数前面都跟加密没关系

img

解密脚本:

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

#define __ROL1__(x, n) (((x) << (n)) | ((x) >> (8 - (n))))
unsigned char flag[29] =
{
0x93, 0xF9, 0x8D, 0x92, 0x52, 0x57, 0xD9, 0x05, 0xC6, 0x0A,
0x50, 0xC7, 0xDB, 0x4F, 0xCB, 0xD8, 0x5D, 0xA6, 0xB9, 0x40,
0x95, 0x70, 0xE7, 0x9A, 0x37, 0x72, 0x4D, 0xEF, 0x57};

unsigned char box[256];
int main()
{
// 第二部分
for (int k = 28; k > 0; k--)
{
flag[k] ^= flag[k - 1] ^ 0x42;
}
flag[0] ^= 0x42;

for (int k = 0; k < 29; k++)
{
printf("%02X ", flag[k]);
}
printf("\n");

// 第一部分
int i, j;

for (int k = 0; k < 256; k++)
{
box[k] = k;
}

i = 0, j = 0;
for (int k = 0; k < 256; k++)
{
j = (int)(j + box[i] - 7 * (i / 7) + i + 4919) % 256;

unsigned char temp = box[i];
box[i] = box[j];
box[j] = temp;

i = (i + 1) % 256;
}

i = 0, j = 0;
for (int k = 0; k < 29; k++)
{
i = (i + 1) % 256;
if (i == 3 * (i / 3))
j = ((unsigned __int8)box[3 * i % 256] + j) % 256;
else
j = ((unsigned __int8)box[i] + j) % 256;

unsigned char temp = box[i];
box[i] = box[j];
box[j] = temp;

// flag[k] = __ROL1__(i * j % 16 + (flag[k] ^ box[(unsigned __int8)(box[i] + box[j])]), 3);
flag[k] = (__ROL1__(flag[k], 5) - i * j % 16) ^ box[(unsigned __int8)(box[i] + box[j])];
}

printf("%s", flag);
// flag{Th1s_1s_A_Fl4w3d_Crypt0}
}

flag{Th1s_1s_A_Fl4w3d_Crypt0}