ida attach 到进程上,然后把数据改为代码形式然后 F5 反汇编。 主体逻辑如下:
int __usercall sub_1711BF@<eax>(int a1@<edi>, int a2@<esi>)
{
unsigned int v3;
_DWORD *v4;
_DWORD *v5;
unsigned int v6;
unsigned int i;
signed int v8;
unsigned int v9;
int v10;
int v11;
unsigned int v12;
bool v13;
int v14;
unsigned int v15;
__m128i *v16;
__m128i v17;
__m128i v18;
__m128i v19;
__m128i v20;
__m128i v21;
__m128i v22;
__m128i v23;
__m128i v24;
int v25;
unsigned int v26;
int v27;
void (__cdecl *v28)(int (*)(int, ...), __int64 *);
_BYTE *v29;
int v32;
_DWORD *v33;
unsigned int v34;
int v35;
unsigned int v36;
unsigned int v37;
unsigned int *v38;
int v39;
unsigned int v40;
_BYTE *v41;
unsigned int vlp;
int key[4];
char input[356];
__int64 v45;
int v46;
__int16 v47;
char v48;
unsigned int v49;
int v50;
__debugbreak();
v49 = (unsigned int)&v50 ^ dword_174004;
memset((int)input, 0, 353);
printf((int)"please input the shellcode:\n");
scanf("%352s", input);
if ( &input[strlen(input) + 1] - &input[1] != 352 )
{
printf((int)"shellcode length error\n");
LABEL_3:
(*(&loc_173098 + 1))("pause");
return sub_17166E((char)v33);
}
v40 = 352;
v41 = base64(352u, input, (int *)&v40);
if ( (v40 & 3) != 0 )
v3 = 4 * (v40 >> 2) + 4;
else
v3 = v40;
v4 = (_DWORD *)ucrtbase_malloc(v3);
v5 = v4;
v33 = v4;
if ( !v4 )
return sub_17166E((char)v33);
memset((int)v4, 0, v3);
v6 = v40;
if ( v40 )
{
if ( v41 && v3 >= v40 )
{
memcpy((int)v5, (int)v41, v40);
goto LABEL_17;
}
memset((int)v5, 0, v3);
if ( v41 )
{
if ( v3 >= v6 )
goto LABEL_17;
*(_DWORD *)sub_1A398C() = '"';
}
else
{
*(_DWORD *)sub_1A398C() = '\x16';
}
sub_254ACA();
}
LABEL_17:
for ( i = 0; i < v3; ++i )
*((_BYTE *)v5 + i) = __ROL1__(*((_BYTE *)v5 + i), 3);
v8 = v3 >> 2;
key[0] = 116;
key[1] = 111;
v38 = &v5[v8 - 1];
v9 = v5[v8 - 1];
v10 = v8 - 1;
v35 = 52 / v8 + 6;
v11 = 0;
key[2] = 114;
key[3] = 97;
v37 = v8;
vlp = v9;
v34 = v8 - 1;
do
{
v12 = 0;
v39 = v11 - 0x61C88647;
v36 = ((unsigned int)(v11 - 0x61C88647) >> 2) & 3;
if ( v10 )
{
do
{
v5[v12] += ((v39 ^ v5[v12 + 1]) + (vlp ^ key[v36 ^ v12 & 3])) ^ (((16 * vlp) ^ (v5[v12 + 1] >> 3))
+ ((v9 >> 6) ^ (4 * v5[v12 + 1])));
v9 = v5[v12++];
vlp = v9;
}
while ( v12 < v34 );
}
v10 = v34;
*v38 += ((v39 ^ *v5) + (vlp ^ key[v36 ^ v12 & 3])) ^ (((16 * vlp) ^ (*v5 >> 3)) + ((v9 >> 6) ^ (4 * *v5)));
v13 = v35-- == 1;
v9 = *v38;
v11 -= 0x61C88647;
vlp = *v38;
}
while ( !v13 );
v14 = 0;
v15 = 0;
if ( !v37 )
goto LABEL_38;
if ( v37 >= 8 )
{
v16 = (__m128i *)v5;
v17 = 0i64;
v18 = 0i64;
do
{
v19 = *v16;
v15 += 8;
v16 += 2;
v20 = _mm_cmpeq_epi32(*(__m128i *)((char *)&v16[-2] + (char *)dword_173310 - (char *)v5), v19);
v17 = _mm_or_si128(_mm_and_si128(_mm_add_epi32((__m128i)xmmword_173420, v17), v20), _mm_andnot_si128(v20, v17));
v21 = _mm_cmpeq_epi32(*(__m128i *)((char *)&v16[-2] + &unk_173320 - (_UNKNOWN *)v5), v16[-1]);
v22 = _mm_or_si128(_mm_and_si128(_mm_add_epi32((__m128i)xmmword_173420, v18), v21), _mm_andnot_si128(v21, v18));
v18 = v22;
}
while ( v15 < (v37 & 0xFFFFFFF8) );
v5 = v33;
v23 = _mm_add_epi32(v22, v17);
v24 = _mm_add_epi32(v23, _mm_srli_si128(v23, 8));
v14 = _mm_cvtsi128_si32(_mm_add_epi32(v24, _mm_srli_si128(v24, 4)));
}
for ( ; v15 < v37; v14 = v25 )
{
v25 = v14 + 1;
if ( dword_173310[v15] != v5[v15] )
v25 = v14;
++v15;
}
if ( v14 != 66 )
{
LABEL_38:
printf((int)"wrong shellcode\n");
goto LABEL_39;
}
printf((int)"now let's execute the shellcode\n");
printf((int)&off_1731A8);
v46 = 0;
v47 = 0;
v45 = 0i64;
v48 = 0;
scanf("%14s", &v45);
if ( strlen((const char *)&v45) != 14 )
{
printf((int)"length error\n");
LABEL_39:
ucrtbase_free(v41);
ucrtbase_free(v5);
(*(&loc_173098 + 1))("pause");
return sub_17166E((char)v33);
}
v26 = v40;
v27 = ((int (__fastcall *)(int, char *, _DWORD, unsigned int, int, int, int, int))((char *)&loc_2D39B6 + 2))(
14,
(char *)&v45 + 1,
0,
v40,
4096,
64,
a1,
a2);
v28 = (void (__cdecl *)(int (*)(int, ...), __int64 *))v27;
if ( v27 )
{
v32 = v26;
v29 = v41;
memcpy(v27, (int)v41, v32);
v28(printf, &v45);
ucrtbase_free(v29);
ucrtbase_free(v5);
sub_1D8CC6(v28);
printf((int)&off_1731D4);
goto LABEL_3;
}
return sub_17166E(v35);
}
检查完读入长度后首先对输入进行 base64 解码: 之后把数据每一字节都循环左移 3 位: 之后就是一个 XXTEA 加密:
v8 = v3 >> 2;
key[0] = 116;
key[1] = 111;
v38 = &v5[v8 - 1];
v9 = v5[v8 - 1];
v10 = v8 - 1;
v35 = 52 / v8 + 6;
v11 = 0;
key[2] = 114;
key[3] = 97;
v37 = v8;
vlp = v9;
v34 = v8 - 1;
do
{
v12 = 0;
v39 = v11 - 0x61C88647;
v36 = ((unsigned int)(v11 - 0x61C88647) >> 2) & 3;
if ( v10 )
{
do
{
v5[v12] += ((v39 ^ v5[v12 + 1]) + (vlp ^ key[v36 ^ v12 & 3])) ^ (((16 * vlp) ^ (v5[v12 + 1] >> 3))
+ ((v9 >> 6) ^ (4 * v5[v12 + 1])));
v9 = v5[v12++];
vlp = v9;
}
while ( v12 < v34 );
}
v10 = v34;
*v38 += ((v39 ^ *v5) + (vlp ^ key[v36 ^ v12 & 3])) ^ (((16 * vlp) ^ (*v5 >> 3)) + ((v9 >> 6) ^ (4 * *v5)));
v13 = v35-- == 1;
v9 = *v38;
v11 -= 0x61C88647;
vlp = *v38;
}
while ( !v13 );
需要注意的是这里的 XXTEA 是魔改过的,共有 3 处修改:
delta 由 0x9E3779B9 改为了 0x61C88647 v11 += delta; 这里变成了 v11 -= delta; v9 >> 5 改为了 v9 >> 6
后面有一个比较: 对应如下数据:
根据以上分析不难写出逆向脚本:
#include <bits/stdc++.h>
using namespace std;
#define ROR3(x) (((x)>>3) | (((x)&7) << 5))
typedef unsigned int i32;
typedef unsigned char i8;
struct XXTEA {
const i32 delta = 0x61C88647;
inline void decryptData(vector<i32> &v, vector<i32> k) {
while (k.size() < 4)k.push_back(0);
i32 t = 6 + 52 / v.size();
for (i32 i = 0, sum = -delta * t; i < t; i++, sum += delta)
for (int p = (int) v.size() - 1; p >= 0; p--) {
int lp = p - 1, rp = p + 1;
if (lp < 0)lp += (int) v.size();
if (rp >= v.size())rp -= (int) v.size();
v[p] -= (((v[lp] >> 6 ^ v[rp] << 2) + (v[rp] >> 3 ^ v[lp] << 4)) ^
((sum ^ v[rp]) + (k[(p & 3) ^ (sum >> 2 & 3)] ^ v[lp])));
}
}
inline vector<i8> decryptString(vector<i32> &v, vector<i32> k) {
assert(v.size() % 2 == 0);
while (k.size() < 4)k.push_back(0);
decryptData(v, k);
return intToString(v);
}
inline vector<i8> intToString(vector<i32> &v) {
vector<i8> s;
for (int i = 0; i < v.size(); i++) {
s.push_back(i8(v[i] & 0xFF));
s.push_back(i8(v[i] >> 8 & 0xFF));
s.push_back(i8(v[i] >> 16 & 0xFF));
s.push_back(i8(v[i] >> 24 & 0xFF));
}
return s;
}
} T;
struct Base64 {
string table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
string encode(vector<i8> s) {
string res;
int n = (int) s.size();
s.push_back('\x00');
for (int i = 0; i < n; i += 3) {
res.push_back(table[s[i] >> 2]);
res.push_back(table[((s[i] & 3) << 4) | (s[i + 1] >> 4)]);
if (i + 1 < n)res.push_back(table[((s[i + 1] & 15) << 2) | (s[i + 2] >> 6)]);
if (i + 2 < n)res.push_back(table[s[i + 2] & ((1 << 6) - 1)]);
}
while (res.size() & 3)res.push_back('=');
return res;
}
} B;
int main() {
vector<unsigned int> v = {0x4B6B89A1, 0x74C15453, 0x4092A06E, 0x429B0C07, 0x40281E84, 0x8B5B44C9, 0x66FEB37B,
0x3C77A603, 0x79C5892D, 0x0D7ADA97, 0x1D51AA56, 0x02D4D703, 0x4FA526BA, 0x32FAD64A,
0x0C0F6091, 0x562B7593, 0xDB9ADD67, 0x76165563, 0xA5F79315, 0x3AEB991D, 0x1AB721D4,
0xAACD9D2C, 0x825C2B27, 0x76A7761A, 0xB4005F18, 0x117F3763, 0x512CC540, 0xC594A16F,
0xD0E24F8C, 0x9CA3E2E9, 0x0A9CC2D5, 0x4629E61D, 0x637129E3, 0xCA4E8AD7, 0xF5DFAF71,
0x474E68AB, 0x542FBC3A, 0xD6741617, 0xAD0DBBE5, 0x62F7BBE3, 0xC8D68C07, 0x880E950E,
0xF80F25BA, 0x767A264C, 0x9A7CE014, 0x5C8BC9EE, 0x5D9EF7D4, 0xB999ACDE, 0xB2EC8E13,
0xEE68232D, 0x927C5FCE, 0xC9E3A85D, 0xAC74B56B, 0x42B6E712, 0xCD2898DA, 0xFCF11C58,
0xF57075EE, 0x5076E678, 0xD4D66A35, 0x95105AB9, 0x1BB04403, 0xB240B959, 0x7B4E261A,
0x23D129D8, 0xF5E752CD, 0x4EA78F70};
vector<unsigned int> k = {116, 111, 114, 97};
vector<i8> t = T.decryptString(v, k);
for (int i = 0; i < t.size(); i++)t[i] = ROR3(t[i]);
for (int i = 0; i < t.size(); i++)
cout << hex << "0x" << int(t[i]) << ",\n"[i == (int) t.size() - 1];
string s = B.encode(t);
cout << s << "\n";
return 0;
}
解得 shellcode 为:
0x60,0xfc,0x68,0x4c,0x77,0x26,0x7,0x33,0xd2,0x64,0x8b,0x52,0x30,0x8b,0x52,0xc,0x8b,0x52,0x14,0x8b,0x72,0x28,0xf,0xb7,0x4a,0x26,0x33,0xff,0x33,0xc0,0xac,0x3c,0x61,0x7c,0x2,0x2c,0x20,0xc1,0xcf,0xd,0x3,0xf8,0xe2,0xf0,0x52,0x57,0x8b,0x52,0x10,0x8b,0x42,0x3c,0x3,0xc2,0x8b,0x40,0x78,0x85,0xc0,0xf,0x84,0xbe,0x0,0x0,0x0,0x3,0xc2,0x50,0x8b,0x48,0x18,0x8b,0x58,0x20,0x3,0xda,0x83,0xf9,0x0,0xf,0x84,0xa9,0x0,0x0,0x0,0x49,0x8b,0x34,0x8b,0x3,0xf2,0x33,0xff,0x33,0xc0,0xac,0xc1,0xcf,0xd,0x3,0xf8,0x3a,0xc4,0x75,0xf4,0x3,0x7c,0x24,0x4,0x3b,0x7c,0x24,0xc,0x75,0xd9,0x33,0xff,0x33,0xc9,0x83,0xc2,0x50,0xf,0xb6,0x4,0xa,0xc1,0xcf,0xd,0x3,0xf8,0x41,0x83,0xf9,0xe,0x75,0xf1,0xc1,0xcf,0xd,0x57,0x33,0xff,0x33,0xc9,0x8b,0x54,0x24,0x3c,0x52,0xf,0xb6,0x1c,0xe,0xb8,0x67,0x66,0x66,0x66,0xf7,0xeb,0xd1,0xfa,0x8b,0xc2,0xc1,0xe8,0x1f,0x3,0xc2,0x8d,0x4,0x80,0x2b,0xd8,0x5a,0xf,0xb6,0x4,0xa,0x2b,0xc3,0xc1,0xcf,0xd,0x3,0xf8,0x41,0x83,0xf9,0xe,0x75,0xd4,0xc1,0xcf,0xd,0x3b,0x3c,0x24,0x74,0x16,0x68,0x25,0x73,0x0,0x0,0x8b,0xc4,0x68,0x6e,0x6f,0x0,0x0,0x54,0x50,0x8b,0x5c,0x24,0x48,0xff,0xd3,0xeb,0x14,0x68,0x25,0x73,0x0,0x0,0x8b,0xc4,0x68,0x79,0x65,0x73,0x0,0x54,0x50,0x8b,0x5c,0x24,0x48,0xff,0xd3,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x61,0xc3,0x58,0x5f,0x5a,0x8b,0x12,0xe9,0xb,0xff,0xff,0xff
base64 编码后的 shellcode 输入为:
YPxoTHcmBzPSZItSMItSDItSFItyKA+3SiYz/zPArDxhfAIsIMHPDQP44vBSV4tSEItCPAPCi0B4hcAPhL4AAAADwlCLSBiLWCAD2oP5AA+EqQAAAEmLNIsD8jP/M8Cswc8NA/g6xHX0A3wkBDt8JAx12TP/M8mDwlAPtgQKwc8NA/hBg/kOdfHBzw1XM/8zyYtUJDxSD7YcDrhnZmZm9+vR+ovCwegfA8KNBIAr2FoPtgQKK8PBzw0D+EGD+Q511MHPDTs8JHQWaCVzAACLxGhubwAAVFCLXCRI/9PrFGglcwAAi8RoeWVzAFRQi1wkSP/TWFhYWFhYWFhYYcNYX1qLEukL
将 shellcode 运行起来然后 ida 动调 编译命令:
gcc shellcode.c -o shellcode.exe -g -m32
#include <windows.h>
#include <string.h>
unsigned char shellcode[] = {0x60, 0xfc, 0x68, 0x4c, 0x77, 0x26, 0x7, 0x33, 0xd2, 0x64, 0x8b, 0x52, 0x30, 0x8b, 0x52,
0xc, 0x8b, 0x52, 0x14, 0x8b, 0x72, 0x28, 0xf, 0xb7, 0x4a, 0x26, 0x33, 0xff, 0x33, 0xc0,
0xac, 0x3c, 0x61, 0x7c, 0x2, 0x2c, 0x20, 0xc1, 0xcf, 0xd, 0x3, 0xf8, 0xe2, 0xf0, 0x52,
0x57, 0x8b, 0x52, 0x10, 0x8b, 0x42, 0x3c, 0x3, 0xc2, 0x8b, 0x40, 0x78, 0x85, 0xc0, 0xf,
0x84, 0xbe, 0, 0, 0, 0x3, 0xc2, 0x50, 0x8b, 0x48, 0x18, 0x8b, 0x58, 0x20, 0x3, 0xda, 0x83,
0xf9, 0, 0xf, 0x84, 0xa9, 0, 0, 0, 0x49, 0x8b, 0x34, 0x8b, 0x3, 0xf2, 0x33, 0xff, 0x33,
0xc0, 0xac, 0xc1, 0xcf, 0xd, 0x3, 0xf8, 0x3a, 0xc4, 0x75, 0xf4, 0x3, 0x7c, 0x24, 0x4, 0x3b,
0x7c, 0x24, 0xc, 0x75, 0xd9, 0x33, 0xff, 0x33, 0xc9, 0x83, 0xc2, 0x50, 0xf, 0xb6, 0x4, 0xa,
0xc1, 0xcf, 0xd, 0x3, 0xf8, 0x41, 0x83, 0xf9, 0xe, 0x75, 0xf1, 0xc1, 0xcf, 0xd, 0x57, 0x33,
0xff, 0x33, 0xc9, 0x8b, 0x54, 0x24, 0x3c, 0x52, 0xf, 0xb6, 0x1c, 0xe, 0xb8, 0x67, 0x66,
0x66, 0x66, 0xf7, 0xeb, 0xd1, 0xfa, 0x8b, 0xc2, 0xc1, 0xe8, 0x1f, 0x3, 0xc2, 0x8d, 0x4,
0x80, 0x2b, 0xd8, 0x5a, 0xf, 0xb6, 0x4, 0xa, 0x2b, 0xc3, 0xc1, 0xcf, 0xd, 0x3, 0xf8, 0x41,
0x83, 0xf9, 0xe, 0x75, 0xd4, 0xc1, 0xcf, 0xd, 0x3b, 0x3c, 0x24, 0x74, 0x16, 0x68, 0x25,
0x73, 0, 0, 0x8b, 0xc4, 0x68, 0x6e, 0x6f, 0, 0, 0x54, 0x50, 0x8b, 0x5c, 0x24, 0x48, 0xff,
0xd3, 0xeb, 0x14, 0x68, 0x25, 0x73, 0, 0, 0x8b, 0xc4, 0x68, 0x79, 0x65, 0x73, 0, 0x54,
0x50, 0x8b, 0x5c, 0x24, 0x48, 0xff, 0xd3, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58,
0x58, 0x61, 0xc3, 0x58, 0x5f, 0x5a, 0x8b, 0x12, 0xe9, 0xb, 0xff, 0xff, 0xff};
typedef void(__stdcall *CODE)();
int main() {
PVOID p = NULL;
if ((p = VirtualAlloc(NULL, sizeof(shellcode), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE)) == NULL)
MessageBoxA(NULL, "alloc error", "tips", MB_OK);
if (!(memcpy(p, shellcode, sizeof(shellcode))))
MessageBoxA(NULL, "write error", "tips", MB_OK);
CODE code = (CODE) p;
code();
return 0;
}
shellcode 反编译代码为:
void __usercall shellcode(int eax0@<eax>, int edx0@<edx>, int a3@<ecx>, void (__cdecl *a1)(int *, int *), char *flag)
{
_LIST_ENTRY *i;
_LIST_ENTRY *Flink;
int Blink_high;
int v8;
int Flink_low;
_LIST_ENTRY *v10;
int v11;
int v12;
char *v13;
unsigned __int8 *v14;
int v15;
int v16;
int v17;
int v18;
_LIST_ENTRY *v19;
int v20;
int j;
int v22;
int v23;
int v24;
int v25;
int v26;
_LIST_ENTRY *v27;
int v28;
int v29;
int v30;
int v31;
void **v32;
void *retaddr;
v32 = &retaddr;
v31 = a3;
v30 = edx0;
v29 = eax0;
v28 = 119961420;
for ( i = NtCurrentPeb()->Ldr->InMemoryOrderModuleList.Flink; ; i = v27->Flink )
{
Flink = i[5].Flink;
Blink_high = HIWORD(i[4].Blink);
v8 = 0;
do
{
Flink_low = LOBYTE(Flink->Flink);
Flink = (_LIST_ENTRY *)((char *)Flink + 1);
if ( (char)Flink_low >= 97 )
LOBYTE(Flink_low) = Flink_low - 32;
v8 = Flink_low + __ROR4__(v8, 13);
--Blink_high;
}
while ( Blink_high );
v27 = i;
v26 = v8;
v10 = i[2].Flink;
v11 = *(int *)((char *)&v10[15].Flink + (unsigned int)v10[7].Blink);
if ( v11 )
break;
LABEL_20:
;
}
v25 = (int)v10 + v11;
v12 = *(int *)((char *)&v10[3].Flink + v11);
v13 = (char *)v10 + *(unsigned int *)((char *)&v10[4].Flink + v11);
do
{
if ( !v12 )
goto LABEL_20;
--v12;
v14 = (unsigned __int8 *)v10 + *(_DWORD *)&v13[4 * v12];
v15 = 0;
do
{
v16 = *v14++;
v15 = v16 + __ROR4__(v15, 13);
}
while ( (_BYTE)v16 != BYTE1(v16) );
}
while ( v26 + v15 != v28 );
v17 = 0;
v18 = 0;
v19 = v10 + 10;
do
v17 = *((unsigned __int8 *)&v19->Flink + v18++) + __ROR4__(v17, 13);
while ( v18 != 14 );
v24 = __ROR4__(v17, 13);
v20 = 0;
for ( j = 0; j != 14; ++j )
{
v23 = (int)flag;
v20 = (unsigned __int8)flag[j] - v14[j] % 5 + __ROR4__(v20, 13);
}
v23 = 29477;
if ( __ROR4__(v20, 13) == v24 )
v22 = 7562617;
else
v22 = 28526;
a1(&v23, &v22);
}
前面部分是初始化数据,获取加密所用的字符串。 关键代码为:
v17 = 0;
v18 = 0;
v19 = v10 + 10;
do
v17 = *((unsigned __int8 *)&v19->Flink + v18++) + __ROR4__(v17, 13);
while ( v18 != 14 );
v24 = __ROR4__(v17, 13);
v20 = 0;
for ( j = 0; j != 14; ++j )
{
v23 = (int)flag;
v20 = (unsigned __int8)flag[j] - v14[j] % 5 + __ROR4__(v20, 13);
}
v23 = 29477;
if ( __ROR4__(v20, 13) == v24 )
v22 = 7562617;
else
v22 = 28526;
可以看出,这里分别对 &v19->Flink 的前 14 个字符做一个哈希运算,结果为 v24 ; 之后对 flag[j] - v14[j] % 5 前 14 个结果做一个哈希运算,结果为 __ROR4__(v20, 13) 。 最后比较这两个哈希的结果,盲猜如果相等则通过检验。 ida 动态调试: 定位到 &v19->Flink (即下面的 &v17->Flink ) 分析汇编 定位到对应的字符串 定位到 v14 (单跑 shellcode ,执行到下一条语句会段错误,不过还是能够找到字符串) 如果两个序列相等,则哈希值相等,因此我们不妨认为两个序列相等,则 flag[i] = &v19->Flink[i] + v14[i] % 5
#include <bits/stdc++.h>
using namespace std;
int main() {
string a = "is program cannot be run in DOS mode.";
string b = "LoadLibraryExA";
for (int i = 0; i < 14; i++)
cout << char(a[i] + b[i] % 5);
return 0;
}
求解得 flag 为
jt"psojvcq!gan
通过检验
与前面的 shellcode 拼接后求 md5 得 flag 为 HFCTF{2b794e95022f2fe46106c21bbf57a755} 。
|