Contents

VCS Training RE

week 1/crackme1

0x01 Overview

Decompile bằng IDA32, ta thu được pseudo-code của hàm main() như sau

 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
int __cdecl main(int argc, const char **argv, const char **envp)
{
  char v4; // [esp+0h] [ebp-2CCh]
  char v5; // [esp+0h] [ebp-2CCh]
  char v6; // [esp+0h] [ebp-2CCh]
  char Format[160]; // [esp+8h] [ebp-2C4h] BYREF
  char input; // [esp+A8h] [ebp-224h] BYREF
  char v9[299]; // [esp+A9h] [ebp-223h] BYREF
  char v10[200]; // [esp+1D4h] [ebp-F8h] BYREF
  void *v11; // [esp+29Ch] [ebp-30h]
  int v12; // [esp+2A0h] [ebp-2Ch]
  int v13; // [esp+2A4h] [ebp-28h]
  char *v14; // [esp+2A8h] [ebp-24h]
  int v15; // [esp+2ACh] [ebp-20h]
  char *v16; // [esp+2B0h] [ebp-1Ch]
  int v17; // [esp+2B4h] [ebp-18h]
  int v18; // [esp+2B8h] [ebp-14h]
  char *p_input; // [esp+2BCh] [ebp-10h]
  char *addrInput; // [esp+2C0h] [ebp-Ch]
  int i; // [esp+2C8h] [ebp-4h]

  input = 0;
  memset(v9, 0, sizeof(v9));
  v12 = 335;
  memset(v10, 0, sizeof(v10));
  strcpy(
    Format,
    "Do you remember the good old days?! I don't know how about you,but I don't. Please help me to recover my memory, it'"
    "s password protected, and that's sad :(\n");
  v11 = &byte_864BE8;
  printf(Format, v4);
  printf("Enter password: ", v5);
  scanf("%300[^\n]s", (char)&input);
  addrInput = &input;
  v16 = v9;
  addrInput += strlen(addrInput);
  v15 = ++addrInput - v9;
  v17 = addrInput - v9;
  p_input = &input;
  v14 = v9;
  p_input += strlen(p_input);
  v13 = ++p_input - v9;
  if ( (unsigned int)(p_input - v9) >= 294 )
  {
    if ( checkInput(&input) )
    {
      v18 = v17 / 3;
      for ( i = 0; i < v17; ++i )
        v10[i % v18] ^= v9[i - 1];
      for ( i = 0; i < v12; ++i )
        byte_864020[i] ^= v10[i % v18];
      printf("\n\nCongratulation! Here is your memo :> \n\n", v6);
      printf("%s", (char)byte_864020);
    }
    else
    {
      printf("\nInvalid password\n", v6);
    }
    getchar();
    getchar();
    return 0;
  }
  else
  {
    printf("oh, no!", v6);
    return 0;
  }
}

Chương trình cho phép nhập vào tối đa 300 ký tự (không tính \n) và bắt đầu check input nếu độ dài ≥ 294. Ngược lại, chương trình sẽ in ra oh, no!

Nếu input của chúng ta hợp lệ, flag sẽ được in ra tại đoạn mã này.

1
2
3
4
5
6
7
v18 = v17 / 3;
for ( i = 0; i < v17; ++i )
  v10[i % v18] ^= v9[i - 1];
for ( i = 0; i < v12; ++i )
  byte_864020[i] ^= v10[i % v18];
printf("\n\nCongratulation! Here is your memo :> \n\n", v6);
printf("%s", (char)byte_864020);

0x02 Static Analysis

Chúng ta bắt đầu đi sâu hơn vào hàm checkInput() để phân tích chức năng của nó.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
char __cdecl checkInput(const char *input)
{
  int i; // [esp+14h] [ebp-10h]
  _DWORD *byteStr; // [esp+18h] [ebp-Ch]

  byteStr = &unk_404BE8;
  if ( (int)strlen(input) < 55 )
    return 0;
  for ( i = 0; i < 122; ++i )
  {
    if ( !(unsigned __int8)execCheck(*byteStr, &input[byteStr[1]], byteStr + 2) )
      return 0;
    byteStr += 3;
  }
  return 1;
}

Chương trình khởi tạo một byteStr, kiểm tra độ dài input một lần nữa và bắt đầu thực hiện biến đổi 122 rounds. Sau mỗi round, byteStr sẽ tăng thêm 3 đơn vị, mỗi DWORD có kích thước 4 byte. Vậy mỗi round cần dùng 12 byte → kích thước của byteStr sẽ là 12 * 122 = 1464 (bytes).

Trong mỗi round, hàm execCheck() sẽ được gọi với 3 đối số.

1
execCheck(*(_DWORD *)byteStr, (char *)&input[*((_DWORD *)byteStr + 1)], byteStr + 8)

Với kiểu dữ liệu DWORD trong IDA, mỗi đối số sẽ có kích thước 4 byte. Đây là một mẩu chuỗi byte nhỏ trong byteStr.

Để biết chính xác các đối số đó làm nhiệm vụ gì, chúng ta sẽ đi phân tích hàm execCheck().

 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
char __cdecl execCheck(int caseIndex, char *subInput, char *initString)
{
  char v4[4]; // [esp+0h] [ebp-20h] BYREF
  LPVOID v5; // [esp+4h] [ebp-1Ch]
  LPVOID v6; // [esp+8h] [ebp-18h]
  SIZE_T v7; // [esp+Ch] [ebp-14h]
  SIZE_T v8; // [esp+10h] [ebp-10h]
  LPVOID lpAddress; // [esp+14h] [ebp-Ch]
  SIZE_T dwSize; // [esp+18h] [ebp-8h]
  char v11; // [esp+1Fh] [ebp-1h]

  v8 = 221;
  v5 = sub_861000((int)&unk_864288, 221, 5);
  v7 = 278;
  v6 = sub_861000((int)&unk_864170, 278, 6);
  if ( !v5 || !v6 )
    return 0;
  switch ( caseIndex )
  {
    case 1:
      dwSize = 97;
      lpAddress = sub_861000((int)&unk_864B80, 97, 1);
      break;
    case 2:
      dwSize = 142;
      lpAddress = sub_861000((int)&unk_864AF0, 142, 2);
      break;
    case 3:
      dwSize = 1685;
      lpAddress = sub_861000((int)&unk_864458, 0x695, 3);
      break;
    case 4:
      dwSize = 235;
      lpAddress = sub_861000((int)&unk_864368, 0xEB, 4);
      break;
    default:
      return 0;
  }
  if ( !lpAddress )
    return 0;
  v11 = ((int (__cdecl *)(char *, char *, char *))lpAddress)(subInput, initString, v4);
  VirtualFree(lpAddress, dwSize, 0x8000u);
  VirtualFree(v5, v8, 0x8000u);
  VirtualFree(v6, v7, 0x8000u);
  return v11;
}

Sau một hồi quan sát, ta biết được 3 đối số đó lần lượt là:

  1. caseIndex: Có tổng cộng 4 caseIndex. Dưới đây là case 1, 1 và 2.
  2. subInput: Lấy ví dụ hình ảnh ở trên với case 2. Index của subInput là 88h, nghĩa là subInput sẽ được lấy từ input[0x88] đến hết.
  3. initString: Đây là chuỗi byte để so sánh kết quả của từng case.

Chúng ta thấy switch xử lý từng case một nhưng đều chung 1 format là

1
lpAddress = sub_861000((int)&unk_864B80, 97, 1);

Hàm sub_861000() thực hiện một số biến đổi và trả về cho ta là địa chỉ của hàm xử lý từng case đó tại biến lpAddress. Và đây là dòng lệnh khi hàm đó được gọi

1
v11 = ((int (__cdecl *)(char *, char *, char *))lpAddress)(subInput, initString, v4);

Nhiệm vụ của chúng ta là phải lấy được code hàm xử lý 4 case trên. Đặt breakpoint ngay tại dòng code trên, ấn F7 để đi vào trong hàm xử lý từng case. Dưới đây là một đoạn code của hàm xử lý case 1.

Ấn phím p để IDA tạo function, ấn F5 để xem mã giả của hàm xử lý.

Thực hiện tương tự cho các case còn lại. Lưu ý là qua từng round, do input ban đầu chúng ta nhập chưa chính xác, chương trình sẽ end khi gọi hàm execCheck().

Khi gặp câu lệnh jnz sẽ sửa lại giá trị cờ ZF từ 1 thành 0 là có thể đi tiếp vào round sau.

Bây giờ, chúng ta sẽ đi phân tích cụ thể chức năng của từng hàm.

checkCase1

Hàm checkCase1() sẽ XOR ký tự subInput[0] với 0x20 nếu mã ASCII của nó là chẵn và XOR với 0x52 nếu ngược lại.

1
2
3
4
5
6
7
8
9
bool __cdecl checkCase1(char *subInput, char *initString)
{
  char v2; // cl

  v2 = *subInput % 2;
  if ( !v2 && (*subInput ^ 0x20) == (unsigned __int8)*initString )
    return 1;
  return v2 == 1 && (*subInput ^ 0x52) == (unsigned __int8)*initString;
}

Để tìm được kí tự thỏa mãn checkCase1() khá đơn giản bằng cách brute-force.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
def checkCase1(block): 
    pos = block[4] + block[5] * 256
    k = block[8]
    fsRes = k ^ 0x20 
    seRes = k ^ 0x52 
    
    if (0x20 <= fsRes and fsRes <= 0x7f and fsRes % 2 == 0): 
        flag[pos] = fsRes
    else:
        flag[pos] = seRes

checkCase2

Hàm checkCase2() thực hiện biến đổi 2 kí tự subInput[0]subInput[1] qua 5 rounds.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
bool __cdecl checkCase2(char *subInput, char *initString)
{
  int i; // [esp+0h] [ebp-Ch]
  unsigned __int16 v4; // [esp+8h] [ebp-4h]

  v4 = subInput[1] | (unsigned __int16)(*subInput << 8);
  for ( i = 1; i <= 5; ++i )
    v4 = (((int)v4 >> (16 - i)) | (v4 << i)) ^ 0x1693;
  return *(unsigned __int16 *)initString == v4;
}

Chúng ta hoàn toàn brute force được để tìm được 2 kí tự thỏa mãn. Lưu ý là v4 chỉ là số 2 bytes, trong khi các phép biến đổi có thể làm v4 vượt qua khoảng giới hạn. Vì vậy sau mỗi lần brute-force, ta phải v4 &= 0xffff để nó luôn là số 2 bytes.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
def checkCase2(block):
    pos = block[4]+ block[5] * 256
    res = block[9] * 256 + block[8]
    for i in range(0, 0x7f):
        for j in range(0, 0x7f): 
            tmp = j | (i << 8)
            for k in range(1, 6, 1):
                tmp = (((tmp >> (16 - k)) | (tmp << k)) ^ 0x1693) & 0xffff
            if tmp == res: 
                flag[pos] = i
                flag[pos + 1] = j

checkCase3

Đối với checkCase3(), chương trình biến đổi 3 kí tự đầu của subInput và đi so sánh với initString. Lưu ý rằng, dưới đây là toàn bộ mã giả do IDA sinh ra. Nếu chú ý sẽ thấy đoạn mã hóa giống y hệt như đoạn code mình đã comment. Mình đoán IDA đã gặp lỗi gì đó trong phân tích/do mình đoán bừa nhưng không ảnh hưởng đến bài toán nên mình đã comment lại.

 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
bool __cdecl checkCase3(char *subInput, char *initString)
{
  char alphabet[68]; // [esp+0h] [ebp-64h] BYREF
  int v4; // [esp+44h] [ebp-20h]
  int v5; // [esp+48h] [ebp-1Ch]
  int v7; // [esp+50h] [ebp-14h]
  int i; // [esp+54h] [ebp-10h]
  int v9; // [esp+58h] [ebp-Ch]
  unsigned __int8 idx0; // [esp+5Ch] [ebp-8h]
  unsigned __int8 idx1; // [esp+5Dh] [ebp-7h]
  unsigned __int8 idx2; // [esp+5Eh] [ebp-6h]
  char idx3; // [esp+5Fh] [ebp-5h]
  char subInput0; // [esp+60h] [ebp-4h]
  char subInput1; // [esp+61h] [ebp-3h]
  char subInput2; // [esp+62h] [ebp-2h]

  v9 = 0;
  i = 0;
  v7 = 3;
  v5 = 0;
  v4 = 0;
  qmemcpy(alphabet, "ABDCEHGFIJKLUNOPYRTSMVWXQZajcdefohibkmlngpqrstuv4xzy8123w56709+0", 64);
  while ( v7-- )
  {
    *(&subInput0 + v9++) = *subInput++;
    if ( v9 == 3 )
    {
      idx0 = (subInput0 & 0xFC) >> 2;
      if ( alphabet[idx0] != (unsigned __int8)*initString )
        return 0;
      idx1 = ((subInput1 & 0xF0) >> 4) + 16 * (subInput0 & 3);
      if ( alphabet[idx1] != (unsigned __int8)initString[1] )
        return 0;
      idx2 = ((subInput2 & 0xC0) >> 6) + 4 * (subInput1 & 0xF);
      if ( alphabet[idx2] != (unsigned __int8)initString[2] )
        return 0;
      idx3 = subInput2 & 0x3F;
      if ( alphabet[subInput2 & 0x3F] != (unsigned __int8)initString[3] )
        return 0;
      v9 = 0;
    }
  }
  // if ( v9 <= 0 )
  //   return 1;
  // for ( i = v9; i < 3; ++i )
  //   *(&subInput0 + i) = 0;
  // idx0 = (subInput0 & 0xFC) >> 2;
  // if ( alphabet[idx0] != (unsigned __int8)*initString )
  //   return 0;
  // idx1 = ((subInput1 & 0xF0) >> 4) + 16 * (subInput0 & 3);
  // if ( alphabet[idx1] != (unsigned __int8)initString[1] )
  //   return 0;
  // idx2 = ((subInput2 & 0xC0) >> 6) + 4 * (subInput1 & 0xF);
  // if ( alphabet[idx2] != (unsigned __int8)initString[2] )
  //   return 0;
  // idx3 = subInput2 & 0x3F;
  // return alphabet[subInput2 & 0x3F] == (unsigned __int8)initString[3];
}

Chúng ta tiếp tục brute-force 3 kí tự để tìm ra đáp án.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
def checkCase3(block):
    pos = block[4]+ block[5] * 256
    alphabet = "ABDCEHGFIJKLUNOPYRTSMVWXQZajcdefohibkmlngpqrstuv4xzy8123w56709+0"
    
    for i in range(0, 0x7f):
        subInput0 = i 
        idx0 = (subInput0 & 0xFC) >> 2
        if (ord(alphabet[idx0]) == block[8]): 
            for j in range(0, 0x7f):
                subInput1 = j
                idx1 = ((subInput1 & 0xF0) >> 4) + 16 * (subInput0 & 3)
                if (ord(alphabet[idx1]) == block[9]):
                    for k in range(0, 0x7f):
                        subInput2 = k 
                        idx2 = ((subInput2 & 0xC0) >> 6) + 4 * (subInput1 & 0xF)
                        idx3 = subInput2 & 0x3F
                        if (ord(alphabet[idx2]) == block[10] and ord(alphabet[idx3]) == block[11]):
                            flag[pos] = i
                            flag[pos + 1] = j
                            flag[pos + 2] = k

checkCase4

Đây là mã giả của hàm checkCase4(). Ta thấy createTablecheckFlag là 2 con trỏ hàm. Vậy nên ta sẽ không thể double-click để xem source code. Lúc này sẽ phải debug, ấn F7 là sẽ nhảy được vào trong từng hàm và có thể xem được code của chúng.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
int __cdecl checkCase4(char *input, char *initString, int a3)
{
  char table[256]; // [esp+0h] [ebp-11Ch] BYREF
  int (__cdecl *checkFlag)(char *, char *, char *); // [esp+100h] [ebp-1Ch]
  void (__cdecl *createTable)(char *, char *); // [esp+104h] [ebp-18h]
  char copyInput[8]; // [esp+108h] [ebp-14h] BYREF
  char susanString[11]; // [esp+110h] [ebp-Ch] BYREF

  strcpy(susanString, "susan");
  createTable = *(void (__cdecl **)(char *, char *))(a3 + 4);
  checkFlag = *(int (__cdecl **)(char *, char *, char *))(a3 + 8);
  createTable(susanString, table);
  copyInput[0] = *input;
  copyInput[1] = input[1];
  copyInput[2] = input[2];
  copyInput[3] = input[3];
  copyInput[4] = 0;
  return checkFlag(table, copyInput, initString);
}

Hàm createTable() tạo cho chúng ta một bảng table có kích thước 256 bytes với giá trị các ô nằm trong khoảng 0 → 255. Lưu ý là giá trị các ô đã bị hoán đổi sau vòng for kia. Nhưng do hàm luôn nhận susanString làm đối số vì vậy giá trị của table sẽ luôn được cố định.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
int __cdecl createTable(const char *susanString, int *table)
{
  signed int v2; // kr00_4
  int v4; // [esp+Ch] [ebp-14h]
  int i; // [esp+10h] [ebp-10h]
  int j; // [esp+18h] [ebp-8h]
  char v7; // [esp+1Eh] [ebp-2h]

  v2 = strlen(susanString);
  v4 = 0;
  for ( i = 0; i < 256; ++i )
    *((_BYTE *)table + i) = i;
  for ( j = 0; j < 256; ++j )
  {
    v4 = (susanString[j % v2] + v4 + *((unsigned __int8 *)table + j)) % 256;
    v7 = *((_BYTE *)table + j);
    *((_BYTE *)table + j) = *((_BYTE *)table + v4);
    *((_BYTE *)table + v4) = v7;
  }
  return 0;
}

Sau khi có table ở trên, hàm checkFlag() bắt đầu đi thực hiện biến đổi và kiểm tra.

 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
char __cdecl checkFlag(char *table, const char *input, char *initString)
{
  unsigned int inputLength; // [esp+4h] [ebp-20h]
  unsigned int cnt; // [esp+10h] [ebp-14h]
  int idx2; // [esp+14h] [ebp-10h]
  int idx1; // [esp+1Ch] [ebp-8h]
  char tmp; // [esp+22h] [ebp-2h]

  idx1 = 0;
  idx2 = 0;
  cnt = 0;
  inputLength = strlen(input);
  while ( cnt < inputLength )
  {
    idx1 = (idx1 + 1) % 256;
    idx2 = (idx2 + table[idx1]) % 256;
    tmp = table[idx1];
    table[idx1] = table[idx2];
    table[idx2] = tmp;
    if ( initString[cnt] != (table[(table[idx2] + table[idx1]) % 256] ^ input[cnt]) )
      return 0;
    ++cnt;
  }
  return 1;
}

Ta dễ dàng viết script brute-force để lấy được kết quả

 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
def checkCase4(block):
    pos = block[4]+ block[5] * 256
    
    table = [0x73, 0xE9, 0x39, 0xD0, 0x98, 0xBB, 0xD6, 0x23, 0x16, 0x19, 
        0xFC, 0x7C, 0x0F, 0x32, 0x80, 0xB2, 0x9C, 0x57, 0x36, 0x9E, 
        0x91, 0x4D, 0xDF, 0x7A, 0x08, 0x42, 0x76, 0xA5, 0x11, 0xAD, 
        0x3E, 0xD2, 0x65, 0x4F, 0x71, 0x20, 0xA0, 0x28, 0xC3, 0x33, 
        0x4E, 0x6C, 0x79, 0x95, 0xAF, 0x6B, 0xC8, 0x70, 0xA2, 0x41, 
        0x92, 0xBA, 0x4B, 0xD1, 0xE3, 0xBC, 0x2B, 0xF4, 0x1C, 0x46, 
        0x78, 0xD9, 0xB6, 0x04, 0xED, 0x96, 0x68, 0x97, 0xF5, 0x09, 
        0x3A, 0x25, 0xEB, 0xBE, 0x49, 0xD8, 0x6D, 0xB5, 0x13, 0x7E, 
        0x00, 0x77, 0x6F, 0xB4, 0x0E, 0x1D, 0xB7, 0x2C, 0xCA, 0x7F, 
        0x3C, 0x5F, 0x7D, 0xA9, 0x88, 0xC4, 0xC0, 0x5E, 0x18, 0xCD, 
        0xE0, 0x0C, 0x62, 0x29, 0x54, 0x84, 0x07, 0x47, 0xC9, 0xF7, 
        0x2E, 0x06, 0xE2, 0x24, 0x83, 0xE4, 0x52, 0x15, 0x45, 0x43, 
        0xDA, 0x31, 0x82, 0x87, 0xB8, 0x14, 0xE7, 0xCF, 0xE5, 0x40, 
        0x1A, 0xDD, 0x9A, 0x35, 0x85, 0xF3, 0x63, 0xB1, 0xF0, 0x3D, 
        0x0D, 0xEA, 0x8B, 0xEE, 0x99, 0xAE, 0xA4, 0x51, 0xA8, 0x1E, 
        0x1B, 0xC5, 0x34, 0x4C, 0xFD, 0xFF, 0xEC, 0x37, 0x64, 0x75, 
        0x05, 0x01, 0x8C, 0x21, 0xA3, 0x60, 0x50, 0x6A, 0xB9, 0x5C, 
        0x53, 0xCE, 0x26, 0xC1, 0x3B, 0xF2, 0x3F, 0x66, 0xCC, 0x2F, 
        0xA1, 0x94, 0x56, 0x59, 0x4A, 0x9F, 0xD7, 0x89, 0x48, 0x5B, 
        0x12, 0x9D, 0x8F, 0x55, 0xD5, 0xBF, 0x5D, 0x2D, 0xF8, 0x1F, 
        0x30, 0x0B, 0x5A, 0x44, 0x67, 0x2A, 0x38, 0xF9, 0xF6, 0x6E, 
        0x7B, 0xEF, 0xE8, 0x8A, 0xDE, 0xC7, 0xF1, 0xA7, 0xCB, 0xDC, 
        0xD4, 0xD3, 0x27, 0xFE, 0x10, 0x02, 0xBD, 0x90, 0xFA, 0xE1, 
        0x69, 0xE6, 0x72, 0xAB, 0xAC, 0x22, 0x8E, 0x86, 0x9B, 0xFB, 
        0xA6, 0x17, 0xB3, 0x61, 0x74, 0xC6, 0xC2, 0x58, 0xB0, 0xAA, 
        0xDB, 0x93, 0x8D, 0x03, 0x0A, 0x81]
            
    idx1 = 0
    idx2 = 0
    for i in range(4):
        idx1 = (idx1 + 1) % 256
        idx2 = (idx2 + table[idx1]) % 256
        tmp = table[idx1]
        table[idx1] = table[idx2]
        table[idx2] = tmp

        for j in range(0, 0x7f):
            if ((table[(table[idx2] + table[idx1]) % 256] ^ j) == block[8 + i]):
                flag[pos + i] = j
                break

0x03 Final script

Sau khi hoàn thiện việc giải mã 4 hàm rồi, ta sẽ lấy toàn bộ chuỗi byte ban đầu, chia thành từng block và xử lý theo từng case một.

  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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
data = [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0xCC, 
    0xCC, 0xCC, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 
    0x48, 0xCC, 0xCC, 0xCC, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 
    0x00, 0x00, 0x3B, 0xCC, 0xCC, 0xCC, 0x02, 0x00, 0x00, 0x00, 
    0x88, 0x00, 0x00, 0x00, 0x81, 0x24, 0xCC, 0xCC, 0x03, 0x00, 
    0x00, 0x00, 0x6F, 0x00, 0x00, 0x00, 0x63, 0x33, 0x59, 0x6F, 
    0x02, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x01, 0x01, 
    0xCC, 0xCC, 0x02, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 
    0x35, 0xAF, 0xCC, 0xCC, 0x01, 0x00, 0x00, 0x00, 0xD0, 0x00, 
    0x00, 0x00, 0x33, 0xCC, 0xCC, 0xCC, 0x03, 0x00, 0x00, 0x00, 
    0x0F, 0x00, 0x00, 0x00, 0x64, 0x4D, 0x78, 0x76, 0x04, 0x00, 
    0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0xDD, 0x20, 0xB1, 0x1A, 
    0x01, 0x00, 0x00, 0x00, 0x06, 0x01, 0x00, 0x00, 0x0C, 0xCC, 
    0xCC, 0xCC, 0x03, 0x00, 0x00, 0x00, 0xE8, 0x00, 0x00, 0x00, 
    0x6A, 0x44, 0x42, 0x35, 0x02, 0x00, 0x00, 0x00, 0x1D, 0x00, 
    0x00, 0x00, 0xA6, 0x21, 0xCC, 0xCC, 0x02, 0x00, 0x00, 0x00, 
    0x1F, 0x00, 0x00, 0x00, 0xBE, 0x8A, 0xCC, 0xCC, 0x01, 0x00, 
    0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x4C, 0xCC, 0xCC, 0xCC, 
    0x02, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x26, 0x0E, 
    0xCC, 0xCC, 0x01, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 
    0x35, 0xCC, 0xCC, 0xCC, 0x01, 0x00, 0x00, 0x00, 0x5D, 0x00, 
    0x00, 0x00, 0x3B, 0xCC, 0xCC, 0xCC, 0x03, 0x00, 0x00, 0x00, 
    0x2B, 0x00, 0x00, 0x00, 0x6A, 0x45, 0x39, 0x75, 0x04, 0x00, 
    0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x88, 0x3F, 0xED, 0x0D, 
    0x03, 0x00, 0x00, 0x00, 0xEB, 0x00, 0x00, 0x00, 0x6A, 0x33, 
    0x56, 0x7A, 0x02, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 
    0x35, 0xAF, 0xCC, 0xCC, 0x02, 0x00, 0x00, 0x00, 0x30, 0x00, 
    0x00, 0x00, 0xAB, 0x2F, 0xCC, 0xCC, 0x02, 0x00, 0x00, 0x00, 
    0x08, 0x00, 0x00, 0x00, 0x81, 0x36, 0xCC, 0xCC, 0x03, 0x00, 
    0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x6A, 0x6C, 0x63, 0x75, 
    0x01, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x00, 0xCC, 
    0xCC, 0xCC, 0x02, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 
    0x25, 0x3C, 0xCC, 0xCC, 0x03, 0x00, 0x00, 0x00, 0x53, 0x00, 
    0x00, 0x00, 0x49, 0x46, 0x52, 0x67, 0x01, 0x00, 0x00, 0x00, 
    0x3E, 0x00, 0x00, 0x00, 0x21, 0xCC, 0xCC, 0xCC, 0x01, 0x00, 
    0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x54, 0xCC, 0xCC, 0xCC, 
    0x01, 0x00, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, 0x37, 0xCC, 
    0xCC, 0xCC, 0x01, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00, 
    0x74, 0xCC, 0xCC, 0xCC, 0x04, 0x00, 0x00, 0x00, 0x09, 0x01, 
    0x00, 0x00, 0xC0, 0x36, 0xFD, 0x13, 0x01, 0x00, 0x00, 0x00, 
    0x4A, 0x00, 0x00, 0x00, 0x00, 0xCC, 0xCC, 0xCC, 0x01, 0x00, 
    0x00, 0x00, 0x0D, 0x01, 0x00, 0x00, 0x52, 0xCC, 0xCC, 0xCC, 
    0x04, 0x00, 0x00, 0x00, 0x4D, 0x00, 0x00, 0x00, 0xC1, 0x3D, 
    0xBA, 0x43, 0x03, 0x00, 0x00, 0x00, 0x1C, 0x01, 0x00, 0x00, 
    0x64, 0x32, 0x77, 0x6F, 0x04, 0x00, 0x00, 0x00, 0x3A, 0x00, 
    0x00, 0x00, 0xCD, 0x73, 0xB0, 0x0C, 0x04, 0x00, 0x00, 0x00, 
    0x56, 0x00, 0x00, 0x00, 0xCD, 0x73, 0xB9, 0x06, 0x03, 0x00, 
    0x00, 0x00, 0x5A, 0x00, 0x00, 0x00, 0x51, 0x32, 0x6D, 0x79, 
    0x04, 0x00, 0x00, 0x00, 0x0F, 0x01, 0x00, 0x00, 0xCB, 0x36, 
    0xAE, 0x10, 0x01, 0x00, 0x00, 0x00, 0x5E, 0x00, 0x00, 0x00, 
    0x3D, 0xCC, 0xCC, 0xCC, 0x02, 0x00, 0x00, 0x00, 0x5F, 0x00, 
    0x00, 0x00, 0x01, 0x21, 0xCC, 0xCC, 0x04, 0x00, 0x00, 0x00, 
    0x68, 0x00, 0x00, 0x00, 0x88, 0x27, 0xB5, 0x06, 0x01, 0x00, 
    0x00, 0x00, 0xBA, 0x00, 0x00, 0x00, 0x44, 0xCC, 0xCC, 0xCC, 
    0x04, 0x00, 0x00, 0x00, 0x9A, 0x00, 0x00, 0x00, 0xDC, 0x3A, 
    0xBA, 0x06, 0x03, 0x00, 0x00, 0x00, 0x6C, 0x00, 0x00, 0x00, 
    0x49, 0x46, 0x4A, 0x6D, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 
    0x00, 0x00, 0x88, 0x62, 0xAE, 0x43, 0x04, 0x00, 0x00, 0x00, 
    0x72, 0x00, 0x00, 0x00, 0xC1, 0x20, 0xFD, 0x0E, 0x02, 0x00, 
    0x00, 0x00, 0xC7, 0x00, 0x00, 0x00, 0x3D, 0x86, 0xCC, 0xCC, 
    0x01, 0x00, 0x00, 0x00, 0x7A, 0x00, 0x00, 0x00, 0x2B, 0xCC, 
    0xCC, 0xCC, 0x04, 0x00, 0x00, 0x00, 0x7B, 0x00, 0x00, 0x00, 
    0x88, 0x27, 0xB8, 0x0D, 0x01, 0x00, 0x00, 0x00, 0xC6, 0x00, 
    0x00, 0x00, 0x0E, 0xCC, 0xCC, 0xCC, 0x01, 0x00, 0x00, 0x00, 
    0x83, 0x00, 0x00, 0x00, 0x2B, 0xCC, 0xCC, 0xCC, 0x01, 0x00, 
    0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0xCC, 0xCC, 0xCC, 
    0x04, 0x00, 0x00, 0x00, 0x93, 0x00, 0x00, 0x00, 0x88, 0x23, 
    0xBC, 0x13, 0x04, 0x00, 0x00, 0x00, 0xA9, 0x00, 0x00, 0x00, 
    0x88, 0x37, 0xB2, 0x43, 0x03, 0x00, 0x00, 0x00, 0x0C, 0x00, 
    0x00, 0x00, 0x5A, 0x47, 0x6D, 0x43, 0x04, 0x00, 0x00, 0x00, 
    0x8A, 0x00, 0x00, 0x00, 0xCE, 0x36, 0xBC, 0x11, 0x04, 0x00, 
    0x00, 0x00, 0x8E, 0x00, 0x00, 0x00, 0xDB, 0x73, 0xBC, 0x11, 
    0x04, 0x00, 0x00, 0x00, 0xAF, 0x00, 0x00, 0x00, 0xD1, 0x27, 
    0xB5, 0x0A, 0x02, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 
    0x25, 0xA5, 0xCC, 0xCC, 0x01, 0x00, 0x00, 0x00, 0x97, 0x00, 
    0x00, 0x00, 0x37, 0xCC, 0xCC, 0xCC, 0x02, 0x00, 0x00, 0x00, 
    0x98, 0x00, 0x00, 0x00, 0x01, 0x2F, 0xCC, 0xCC, 0x04, 0x00, 
    0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0xDC, 0x3C, 0xFD, 0x02, 
    0x01, 0x00, 0x00, 0x00, 0x9E, 0x00, 0x00, 0x00, 0x52, 0xCC, 
    0xCC, 0xCC, 0x04, 0x00, 0x00, 0x00, 0x9F, 0x00, 0x00, 0x00, 
    0xDB, 0x7D, 0xFD, 0x3A, 0x04, 0x00, 0x00, 0x00, 0x46, 0x00, 
    0x00, 0x00, 0xCB, 0x26, 0xB1, 0x17, 0x01, 0x00, 0x00, 0x00, 
    0xA7, 0x00, 0x00, 0x00, 0x33, 0xCC, 0xCC, 0xCC, 0x01, 0x00, 
    0x00, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x00, 0xCC, 0xCC, 0xCC, 
    0x01, 0x00, 0x00, 0x00, 0x87, 0x00, 0x00, 0x00, 0x48, 0xCC, 
    0xCC, 0xCC, 0x01, 0x00, 0x00, 0x00, 0xAD, 0x00, 0x00, 0x00, 
    0x33, 0xCC, 0xCC, 0xCC, 0x01, 0x00, 0x00, 0x00, 0xAE, 0x00, 
    0x00, 0x00, 0x4E, 0xCC, 0xCC, 0xCC, 0x04, 0x00, 0x00, 0x00, 
    0xC9, 0x00, 0x00, 0x00, 0xC7, 0x26, 0xFD, 0x00, 0x02, 0x00, 
    0x00, 0x00, 0xB3, 0x00, 0x00, 0x00, 0x22, 0xA1, 0xCC, 0xCC, 
    0x01, 0x00, 0x00, 0x00, 0xB5, 0x00, 0x00, 0x00, 0x00, 0xCC, 
    0xCC, 0xCC, 0x04, 0x00, 0x00, 0x00, 0xB6, 0x00, 0x00, 0x00, 
    0xD1, 0x3C, 0xA8, 0x43, 0x03, 0x00, 0x00, 0x00, 0x65, 0x00, 
    0x00, 0x00, 0x51, 0x33, 0x59, 0x73, 0x01, 0x00, 0x00, 0x00, 
    0xBB, 0x00, 0x00, 0x00, 0x37, 0xCC, 0xCC, 0xCC, 0x01, 0x00, 
    0x00, 0x00, 0xBC, 0x00, 0x00, 0x00, 0x31, 0xCC, 0xCC, 0xCC, 
    0x04, 0x00, 0x00, 0x00, 0xD2, 0x00, 0x00, 0x00, 0xDC, 0x73, 
    0xA9, 0x0C, 0x03, 0x00, 0x00, 0x00, 0xBF, 0x00, 0x00, 0x00, 
    0x5A, 0x54, 0x42, 0x38, 0x02, 0x00, 0x00, 0x00, 0xC2, 0x00, 
    0x00, 0x00, 0x81, 0x21, 0xCC, 0xCC, 0x01, 0x00, 0x00, 0x00, 
    0xDA, 0x00, 0x00, 0x00, 0x4E, 0xCC, 0xCC, 0xCC, 0x04, 0x00, 
    0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0xC9, 0x30, 0xB4, 0x17, 
    0x04, 0x00, 0x00, 0x00, 0x76, 0x00, 0x00, 0x00, 0xCD, 0x21, 
    0xB8, 0x0F, 0x04, 0x00, 0x00, 0x00, 0xFB, 0x00, 0x00, 0x00, 
    0xCD, 0x73, 0xAD, 0x11, 0x02, 0x00, 0x00, 0x00, 0xCD, 0x00, 
    0x00, 0x00, 0xA6, 0x26, 0xCC, 0xCC, 0x01, 0x00, 0x00, 0x00, 
    0xCF, 0x00, 0x00, 0x00, 0x00, 0xCC, 0xCC, 0xCC, 0x02, 0x00, 
    0x00, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x25, 0x2C, 0xCC, 0xCC, 
    0x01, 0x00, 0x00, 0x00, 0xD1, 0x00, 0x00, 0x00, 0x31, 0xCC, 
    0xCC, 0xCC, 0x02, 0x00, 0x00, 0x00, 0xBD, 0x00, 0x00, 0x00, 
    0xA3, 0x22, 0xCC, 0xCC, 0x03, 0x00, 0x00, 0x00, 0x13, 0x01, 
    0x00, 0x00, 0x49, 0x47, 0x6D, 0x79, 0x02, 0x00, 0x00, 0x00, 
    0xC4, 0x00, 0x00, 0x00, 0x26, 0xA4, 0xCC, 0xCC, 0x03, 0x00, 
    0x00, 0x00, 0xEF, 0x00, 0x00, 0x00, 0x6A, 0x47, 0x6D, 0x6C, 
    0x04, 0x00, 0x00, 0x00, 0xDC, 0x00, 0x00, 0x00, 0xCD, 0x73, 
    0xBC, 0x0D, 0x01, 0x00, 0x00, 0x00, 0xEE, 0x00, 0x00, 0x00, 
    0x00, 0xCC, 0xCC, 0xCC, 0x04, 0x00, 0x00, 0x00, 0xE4, 0x00, 
    0x00, 0x00, 0xC6, 0x27, 0xAF, 0x0C, 0x03, 0x00, 0x00, 0x00, 
    0x1A, 0x00, 0x00, 0x00, 0x5A, 0x31, 0x39, 0x55, 0x01, 0x00, 
    0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x35, 0xCC, 0xCC, 0xCC, 
    0x04, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0xCC, 0x73, 
    0xBE, 0x0C, 0x01, 0x00, 0x00, 0x00, 0xDB, 0x00, 0x00, 0x00, 
    0x35, 0xCC, 0xCC, 0xCC, 0x02, 0x00, 0x00, 0x00, 0xF2, 0x00, 
    0x00, 0x00, 0x8C, 0xA4, 0xCC, 0xCC, 0x04, 0x00, 0x00, 0x00, 
    0xF4, 0x00, 0x00, 0x00, 0x88, 0x32, 0xB3, 0x07, 0x02, 0x00, 
    0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0xA7, 0x39, 0xCC, 0xCC, 
    0x04, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x88, 0x37, 
    0xB4, 0x05, 0x01, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 
    0x3D, 0xCC, 0xCC, 0xCC, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 
    0x00, 0x00, 0x51, 0x32, 0x56, 0x6B, 0x03, 0x00, 0x00, 0x00, 
    0x03, 0x01, 0x00, 0x00, 0x64, 0x58, 0x4A, 0x6D, 0x04, 0x00, 
    0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0xC7, 0x1D, 0xBA, 0x3C, 
    0x02, 0x00, 0x00, 0x00, 0x07, 0x01, 0x00, 0x00, 0x2B, 0x06, 
    0xCC, 0xCC, 0x04, 0x00, 0x00, 0x00, 0xA3, 0x00, 0x00, 0x00, 
    0xC7, 0x26, 0xFD, 0x00, 0x01, 0x00, 0x00, 0x00, 0xA8, 0x00, 
    0x00, 0x00, 0x4E, 0xCC, 0xCC, 0xCC, 0x01, 0x00, 0x00, 0x00, 
    0x0E, 0x01, 0x00, 0x00, 0x3D, 0xCC, 0xCC, 0xCC, 0x03, 0x00, 
    0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0x49, 0x46, 0x52, 0x67, 
    0x04, 0x00, 0x00, 0x00, 0xD6, 0x00, 0x00, 0x00, 0x88, 0x30, 
    0xB5, 0x02, 0x02, 0x00, 0x00, 0x00, 0x16, 0x01, 0x00, 0x00, 
    0x25, 0x86, 0xCC, 0xCC, 0x04, 0x00, 0x00, 0x00, 0x18, 0x01, 
    0x00, 0x00, 0xDC, 0x20, 0xFD, 0x0C, 0x02, 0x00, 0x00, 0x00, 
    0x51, 0x00, 0x00, 0x00, 0xA8, 0xA2, 0xCC, 0xCC, 0x03, 0x00, 
    0x00, 0x00, 0x1F, 0x01, 0x00, 0x00, 0x63, 0x6C, 0x56, 0x33, 
    0x03, 0x00, 0x00, 0x00, 0x22, 0x01, 0x00, 0x00, 0x51, 0x58, 
    0x4A, 0x6B, 0x01, 0x00, 0x00, 0x00, 0x25, 0x01, 0x00, 0x00, 
    0x0E, 0xCC, 0xCC, 0xCC]

flag = [0] * 300 

def checkCase1(block): 
    pos = block[4] + block[5] * 256
    k = block[8]
    fsRes = k ^ 0x20 
    seRes = k ^ 0x52 
    
    if (0x20 <= fsRes and fsRes <= 0x7f and fsRes % 2 == 0): 
        flag[pos] = fsRes
    else:
        flag[pos] = seRes

def checkCase2(block):
    pos = block[4]+ block[5] * 256
    res = block[9] * 256 + block[8]
    for i in range(0, 0x7f):
        for j in range(0, 0x7f): 
            tmp = j | (i << 8)
            for k in range(1, 6, 1):
                tmp = (((tmp >> (16 - k)) | (tmp << k)) ^ 0x1693) & 0xffff
            if tmp == res: 
                flag[pos] = i
                flag[pos + 1] = j

def checkCase3(block):
    pos = block[4]+ block[5] * 256
    alphabet = "ABDCEHGFIJKLUNOPYRTSMVWXQZajcdefohibkmlngpqrstuv4xzy8123w56709+0"
    
    for i in range(0, 0x7f):
        subInput0 = i 
        idx0 = (subInput0 & 0xFC) >> 2
        if (ord(alphabet[idx0]) == block[8]): 
            for j in range(0, 0x7f):
                subInput1 = j
                idx1 = ((subInput1 & 0xF0) >> 4) + 16 * (subInput0 & 3)
                if (ord(alphabet[idx1]) == block[9]):
                    for k in range(0, 0x7f):
                        subInput2 = k 
                        idx2 = ((subInput2 & 0xC0) >> 6) + 4 * (subInput1 & 0xF)
                        idx3 = subInput2 & 0x3F
                        if (ord(alphabet[idx2]) == block[10] and ord(alphabet[idx3]) == block[11]):
                            flag[pos] = i
                            flag[pos + 1] = j
                            flag[pos + 2] = k
                          
def checkCase4(block):
    pos = block[4]+ block[5] * 256
    
    table = [0x73, 0xE9, 0x39, 0xD0, 0x98, 0xBB, 0xD6, 0x23, 0x16, 0x19, 
            0xFC, 0x7C, 0x0F, 0x32, 0x80, 0xB2, 0x9C, 0x57, 0x36, 0x9E, 
            0x91, 0x4D, 0xDF, 0x7A, 0x08, 0x42, 0x76, 0xA5, 0x11, 0xAD, 
            0x3E, 0xD2, 0x65, 0x4F, 0x71, 0x20, 0xA0, 0x28, 0xC3, 0x33, 
            0x4E, 0x6C, 0x79, 0x95, 0xAF, 0x6B, 0xC8, 0x70, 0xA2, 0x41, 
            0x92, 0xBA, 0x4B, 0xD1, 0xE3, 0xBC, 0x2B, 0xF4, 0x1C, 0x46, 
            0x78, 0xD9, 0xB6, 0x04, 0xED, 0x96, 0x68, 0x97, 0xF5, 0x09, 
            0x3A, 0x25, 0xEB, 0xBE, 0x49, 0xD8, 0x6D, 0xB5, 0x13, 0x7E, 
            0x00, 0x77, 0x6F, 0xB4, 0x0E, 0x1D, 0xB7, 0x2C, 0xCA, 0x7F, 
            0x3C, 0x5F, 0x7D, 0xA9, 0x88, 0xC4, 0xC0, 0x5E, 0x18, 0xCD, 
            0xE0, 0x0C, 0x62, 0x29, 0x54, 0x84, 0x07, 0x47, 0xC9, 0xF7, 
            0x2E, 0x06, 0xE2, 0x24, 0x83, 0xE4, 0x52, 0x15, 0x45, 0x43, 
            0xDA, 0x31, 0x82, 0x87, 0xB8, 0x14, 0xE7, 0xCF, 0xE5, 0x40, 
            0x1A, 0xDD, 0x9A, 0x35, 0x85, 0xF3, 0x63, 0xB1, 0xF0, 0x3D, 
            0x0D, 0xEA, 0x8B, 0xEE, 0x99, 0xAE, 0xA4, 0x51, 0xA8, 0x1E, 
            0x1B, 0xC5, 0x34, 0x4C, 0xFD, 0xFF, 0xEC, 0x37, 0x64, 0x75, 
            0x05, 0x01, 0x8C, 0x21, 0xA3, 0x60, 0x50, 0x6A, 0xB9, 0x5C, 
            0x53, 0xCE, 0x26, 0xC1, 0x3B, 0xF2, 0x3F, 0x66, 0xCC, 0x2F, 
            0xA1, 0x94, 0x56, 0x59, 0x4A, 0x9F, 0xD7, 0x89, 0x48, 0x5B, 
            0x12, 0x9D, 0x8F, 0x55, 0xD5, 0xBF, 0x5D, 0x2D, 0xF8, 0x1F, 
            0x30, 0x0B, 0x5A, 0x44, 0x67, 0x2A, 0x38, 0xF9, 0xF6, 0x6E, 
            0x7B, 0xEF, 0xE8, 0x8A, 0xDE, 0xC7, 0xF1, 0xA7, 0xCB, 0xDC, 
            0xD4, 0xD3, 0x27, 0xFE, 0x10, 0x02, 0xBD, 0x90, 0xFA, 0xE1, 
            0x69, 0xE6, 0x72, 0xAB, 0xAC, 0x22, 0x8E, 0x86, 0x9B, 0xFB, 
            0xA6, 0x17, 0xB3, 0x61, 0x74, 0xC6, 0xC2, 0x58, 0xB0, 0xAA, 
            0xDB, 0x93, 0x8D, 0x03, 0x0A, 0x81]
            
    idx1 = 0
    idx2 = 0
    for i in range(4):
        idx1 = (idx1 + 1) % 256
        idx2 = (idx2 + table[idx1]) % 256
        tmp = table[idx1]
        table[idx1] = table[idx2]
        table[idx2] = tmp

        for j in range(0, 0x7f):
            if ((table[(table[idx2] + table[idx1]) % 256] ^ j) == block[8 + i]):
                flag[pos + i] = j
                break

for i in range(0, len(data), 12):
    block = []
    for j in range(12):
        block.append(data[i + j])
    if (block[0] == 1): 
        checkCase1(block)
    elif (block[0] == 2):
        checkCase2(block)
    elif block[0] == 3:
        checkCase3(block)
    else: 
        checkCase4(block)

for i in range(len(flag)):
    print(chr(flag[i]), end="")

Kết quả message thu được là

1
ThiS 1s A rIdiCuLously l0ng_Lon9_l0ng_loNg_lOng strIng. The most difficult thing is the decision to act, the rest is merely tenacity. The fears are paper tigers. You can do anything you decide to do. You can act to change and control your life; and the procedure, the process is its own reward.

Chạy lại chương trình và nhập message trên, ta được flag là vcstraining{Aw3s0me_D4ta_tran5Form4t1oN_Kak4}.