2020XNUCA Reverse 部分Writeup

只做了三道RE,还好队友解了一道PWN,最终成功苟进了决赛圈,决赛拿了第九,不亏

babyarm

简单题,给了个假的check,向下看可以发现是xxtea加密算法

xxtea decrypt,密钥 {2,2,3,4},明文128bits,加密轮次9轮,重复加密16次

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
// [+] Dump 0x230B4 - 0x230F4 (64 bytes) :
// "\x13\xF0\x61\xB0\x7E\x56\xC8\xB3\xC7\xA3\x52\x99\x3F\x2D\x1C\x45\x67\x22\xE3\x3E\x3E\x2B\xE2\xE3\x50\xA2\xE5\x43\xD0\x8E\xB2\x59\xDC\x49\x86\x0F\x83\xD0\xF4\x9B\x10\x81\x57\x8A\x4F\xEC\x04\x86\x7F\xA2\xB5\x2E\xF3\xDD\x17\x12\x53\xB2\xC9\x93\x43\x8E\x7F\xDC"

// [+] Dump 0x230B4 - 0x230F4 (64 bytes) :
// [0x13, 0xF0, 0x61, 0xB0, 0x7E, 0x56, 0xC8, 0xB3, 0xC7, 0xA3, 0x52, 0x99, 0x3F, 0x2D, 0x1C, 0x45, 0x67, 0x22, 0xE3, 0x3E, 0x3E, 0x2B, 0xE2, 0xE3, 0x50, 0xA2, 0xE5, 0x43, 0xD0, 0x8E, 0xB2, 0x59, 0xDC, 0x49, 0x86, 0x0F, 0x83, 0xD0, 0xF4, 0x9B, 0x10, 0x81, 0x57, 0x8A, 0x4F, 0xEC, 0x04, 0x86, 0x7F, 0xA2, 0xB5, 0x2E, 0xF3, 0xDD, 0x17, 0x12, 0x53, 0xB2, 0xC9, 0x93, 0x43, 0x8E, 0x7F, 0xDC]


#include <stdbool.h>
#include <stdio.h>
#define MX \
  ((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4) ^ (sum ^ y) + (k[p & 3 ^ e] ^ z))
bool btea(unsigned int* v, int n, unsigned int* k) {
  unsigned int z = v[n - 1], y = v[0], sum = 0, e, DELTA = 0x9e3779b9;
  unsigned int p, q;
  if (n > 1) { /* Coding Part */
    q = 6 + 52 / n;
    while (q-- > 0) {
      sum += DELTA;
      e = (sum >> 2) & 3;
      for (p = 0; p < n - 1; p++)
        y = v[p + 1], z = v[p] += MX;
      y = v[0];
      z = v[n - 1] += MX;
    }
    return 0;
  } else if (n < -1) { /* Decoding Part */
    n = -n;
    q = 6 + 52 / n;
    sum = q * DELTA;
    while (sum != 0) {
      e = (sum >> 2) & 3;
      for (p = n - 1; p > 0; p--)
        z = v[p - 1], y = v[p] -= MX;
      z = v[n - 1];
      y = v[0] -= MX;
      sum -= DELTA;
    }
    return 0;
  }
  return 1;
}
// flag{this_is_not_flagthis_is_not_flagthis_is_not_flagthis_is_no}
int main(int argc, char const* argv[]) {
  // test
  unsigned int v[16] = {0xB061F013, 0xB3C8567E, 0x9952A3C7, 0x451C2D3F, 0x3EE32267, 0xE3E22B3E, 0x43E5A250, 0x59B28ED0, 
    0x0F8649DC, 0x9BF4D083, 0x8A578110, 0x8604EC4F, 0x2EB5A27F, 0x1217DDF3, 0x93C9B253, 0xDC7F8E43}, key[4] = {2,2,3,4};
  
  for (size_t i = 0; i < 16; i++)
  {
      btea(v, -16, key);
  }
  for (size_t i = 0; i < 16; i++)
  {
      printf("%x,",v[i]);
  }
  puts("");
  puts(v);
    
  return 0;
}

flag{D0cab07Fa45A2b6Ff7bAE6AaBbA5F5dcb3C9fDCE5afeF54DAC88A5093A}

Exception

这道题有点意思,最终也只有7队解了,主要是将几个部分揉成一团,再加上阴间反调试还有基本都是密码学,逆得是真的累,全靠静态硬刚。

这里程序通过异常处理调用真正的加密函数来进行反调试,导致patch非常麻烦

1
2
3
4
5
6
7
8
9
10
11
.text:00401C6B ;   __try { // __except at loc_401C7B
.text:00401C6B ; __try { // __except at loc_401D2B
.text:00401C6B mov [ebp+ms_exc.registration.TryLevel], 1
.text:00401C72 int 3 ; Trap to Debugger
.text:00401C73 jmp short loc_401CED
.text:00401C75 ; ---------------------------------------------------------------------------
.text:00401C75
.text:00401C75 loc_401C75: ; DATA XREF: .rdata:stru_404500↓o
.text:00401C75 ; __except filter // owned by 401C6B
.text:00401C75 call sub_4019D0
.text:00401C7A retn

flag有4part

part1:

魔改AES,s-box被魔改

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

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

其他无变化,key和密文如下,解密即可

1
2
3
uint8_t key[] = {35,1,0,3,99,5,143,7,8,9,199,11,239,13,0,15,238,17,222,19,16,21,142,23,126,25,16,27,46,29,30,31};

uint8_t in[] = {0x4D, 0x91, 0x0F, 0xD0, 0x04, 0xF1, 0xBA, 0x64, 0x59, 0xC9, 0xDA, 0xB3, 0x05, 0x99, 0x54, 0xDB};

得到part1 pLeA5e_nEveR_UnD

part2:魔改base32,编码表为 0x20-0x51,直接解即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import base64
b32table = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'
def b32decode(src, table):

    if len(table) != 32:
        return 'Table length error'
    
    try:
        dest = base64.b32decode(src.translate(str.maketrans(table, b32table)).encode())
    except Exception as e:
        return e

    try:
        dest = dest.decode()
    except UnicodeDecodeError as identifier:
        pass
   
    return dest

table = '\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20!"#$%&\'()*+,-./0123'
cipher = '\x22\x24\x2e\x1a\x2e\x20\x1f\x28\x1a\x29\x26\x29\x18\x2d\x1c\x34'

print(b32decode(cipher, table)[::-1])

part2:eRe5t1m4t

part3:魔改RC6,初始化了子密钥,直接解密即可

1
// __uint32_t keys[] = {0xb7e15163,0x5618cb1c,0xf45044d5,0x9287be8e,0x30bf3847,0xcef6b200,0x6d2e2bb9,0xb65a572,0xa99d1f2b,0x47d498e4,0xe60c129d,0x84438c56,0x227b060f,0xc0b27fc8,0x5ee9f981,0xfd21733a,0x9b58ecf3,0x399066ac,0xd7c7e065,0x75ff5a1e,0x1436d3d7,0xb26e4d90,0x50a5c749,0xeedd4102,0x8d14babb,0x2b4c3474,0xc983ae2d,0x67bb27e6,0x5f2a19f,0xa42a1b58,0x42619511,0xe0990eca,0x7ed08883,0x1d08023c,0xbb3f7bf5,0x5976f5ae,0xf7ae6f67,0x95e5e920,0x341d62d9,0xd254dc92,0x708c564b,0xec3d004,0xacfb49bd,0x4b32c376};

随手一搜,http://www.codeforge.com/read/65331/JG.TXT__html

直接get key

unsigned char key[] = {0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef,0xfe,0xdc,0xba,0x98,0x76,0x54,0x32,0x10};

结合密文解密即可

unsigned char cipher[] ={0xa5,0xf9,0x6e,0x68,0x22,0x3a,0x40,0xc9,0x7d,0xf2,0x38,0xa5,0x8b,0xc4,0xc7,0xa5};

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

typedef unsigned int WORD;           /* Should be 32-bit = 4 bytes        */
#define w 32                         /* word size in bits                 */
#define r 20                         /* based on security estimates       */
#define bytes (w / 8)                /* bytes per word                    */
#define c ((b + bytes - 1) / bytes)  /* key in words, rounded up          */
#define R24 (2 * r + 4)              /* length of array S                 */
#define lgw 5                        /* log2(w) -- wussed out             */
WORD S[R24 - 1];                     /* Key schedule                      */
WORD P = 0xb7e15163, Q = 0x9e3779b9; /* magic constants                   */
/* Rotation operators. x must be unsigned, to get logical right shift     */
#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)))))
// __uint32_t keys[] = {0xb7e15163,0x5618cb1c,0xf45044d5,0x9287be8e,0x30bf3847,0xcef6b200,0x6d2e2bb9,0xb65a572,0xa99d1f2b,0x47d498e4,0xe60c129d,0x84438c56,0x227b060f,0xc0b27fc8,0x5ee9f981,0xfd21733a,0x9b58ecf3,0x399066ac,0xd7c7e065,0x75ff5a1e,0x1436d3d7,0xb26e4d90,0x50a5c749,0xeedd4102,0x8d14babb,0x2b4c3474,0xc983ae2d,0x67bb27e6,0x5f2a19f,0xa42a1b58,0x42619511,0xe0990eca,0x7ed08883,0x1d08023c,0xbb3f7bf5,0x5976f5ae,0xf7ae6f67,0x95e5e920,0x341d62d9,0xd254dc92,0x708c564b,0xec3d004,0xacfb49bd,0x4b32c376};
    
void rc6_keygen(unsigned char *K, int b)
{
    int i, j, s, v;
    WORD L[(32 + bytes - 1) / bytes]; /* Big enough for max b */
    WORD A, B;
    L[c - 1] = 0;
    for (i = b - 1; i >= 0; i--)
        L[i / bytes] = (L[i / bytes] << 8) + K[i];
    S[0] = P;
    for (i = 1; i <= 2 * r + 3; i++)
        S[i] = S[i - 1] + Q;
    
    A = B = i = j = 0;
    v = R24;
    if (c > v)
        v = c;
    v *= 3;
    for (s = 1; s <= v; s++)
    {
        A = S[i] = ROTL(S[i] + A + B, 3);
        B = L[j] = ROTL(L[j] + A + B, A + B);
        i = (i + 1) % R24;
        j = (j + 1) % c;
    }
}


void rc6_encrypt(unsigned char *plain, unsigned char *cipher)
{
    WORD pt[4], ct[4];
    for (int i = 0; i < 4; i++)
    {
        pt[i] = plain[4 * i] + (plain[4 * i + 1] << 8) + (plain[4 * i + 2] << 16) + (plain[4 * i + 3] << 24);
    }
    WORD A, B, C, D, t, u, x;
    A = pt[0];
    B = pt[1];
    C = pt[2];
    D = pt[3];
    B += S[0];
    D += S[1];
    for (int i = 2; i <= 2 * r; i += 2)
    {
        t = ROTL(B * (2 * B + 1), lgw);
        u = ROTL(D * (2 * D + 1), lgw);
        A = ROTL(A ^ t, u) + S[i];
        C = ROTL(C ^ u, t) + S[i + 1];
        x = A;
        A = B;
        B = C;
        C = D;
        D = x;
    }
    A += S[2 * r + 2];
    C += S[2 * r + 3];
    ct[0] = A;
    ct[1] = B;
    ct[2] = C;
    ct[3] = D;
    for (int i = 0; i < 4; i++)
    {
        cipher[4 * i] = ct[i] & 0xFF;
        cipher[4 * i + 1] = (ct[i] >> 8) & 0xFF;
        cipher[4 * i + 2] = (ct[i] >> 16) & 0xFF;
        cipher[4 * i + 3] = (ct[i] >> 24) & 0xFF;
    }
}
void rc6_decrypt(unsigned char *cipher, unsigned char *plain)
{
    WORD pt[4], ct[4];
    for (int i = 0; i < 4; i++)
    {
        ct[i] = cipher[4 * i] + (cipher[4 * i + 1] << 8) + (cipher[4 * i + 2] << 16) + (cipher[4 * i + 3] << 24);
    }
    WORD A, B, C, D, t, u, x;
    A = ct[0];
    B = ct[1];
    C = ct[2];
    D = ct[3];
    C -= S[2 * r + 3];
    A -= S[2 * r + 2];
    for (int i = 2 * r; i >= 2; i -= 2)
    {
        x = D;
        D = C;
        C = B;
        B = A;
        A = x;
        u = ROTL(D * (2 * D + 1), lgw);
        t = ROTL(B * (2 * B + 1), lgw);
        C = ROTR(C - S[i + 1], t) ^ u;
        A = ROTR(A - S[i], u) ^ t;
    }
    D -= S[1];
    B -= S[0];
    pt[0] = A;
    pt[1] = B;
    pt[2] = C;
    pt[3] = D;
    for (int i = 0; i < 4; i++)
    {
        plain[4 * i] = pt[i] & 0xFF;
        plain[4 * i + 1] = (pt[i] >> 8) & 0xFF;
        plain[4 * i + 2] = (pt[i] >> 16) & 0xFF;
        plain[4 * i + 3] = (pt[i] >> 24) & 0xFF;
    }
}

int main(int argc, char const *argv[])
{
    unsigned char key[] = {0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef,0xfe,0xdc,0xba,0x98,0x76,0x54,0x32,0x10};
    unsigned char cipher[] ={0xa5,0xf9,0x6e,0x68,0x22,0x3a,0x40,0xc9,0x7d,0xf2,0x38,0xa5,0x8b,0xc4,0xc7,0xa5};
    
    unsigned char plain[16];
    rc6_keygen(key,16);
    
    rc6_decrypt(cipher,plain);
    for (size_t i = 0; i < 16; i++)
    {
        printf("%x ",plain[i]);
    }
    puts(plain);
    
    return 0;
}

// e_7he_heArt_Of_a

part3: e_7he_heArt_Of_a

part4:明文比对一个字节,剩下八个字节简单xor

1
2
3
4
# >>> hex(0x461F6348^0x2b2b2b2b)[2:].decode('hex')[::-1]
# 'cH4m'
# >>> hex(0x451B625B^0x2b2b2b2b)[2:].decode('hex')[::-1]
# 'pI0n'

最终得到flag

pLeA5e_nEveR_UnDeRe5t1m4te_7he_heArt_Of_a_cH4mpI0n

MFC

简单mfc逆向,坑多了点,od大法好

提交按钮disable,先patch使其enable先

flag分为两个part,第一个part是smc(好像有反调试)od dump内存,patch回源文件重载进ida

1
((void (__fastcall *)(char *, int *, unsigned int))byte_11C5188)(&v10, &v12, a2);

如下所示,只有两个函数sub_59E070、sub_875231修改了flag,第一个显然是rc4算法(sbox已初始化完,不管),第二个函数显然是base64换表(+35)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int __usercall sub_59E070@<eax>(int a1@<xmm0>, int a2, int a3)
{
sub_571BAF((int)&unk_90903F);
v9 = 0;
v8 = 0;
for ( i = 0; i < a3; ++i )
{
v9 = (v9 + 1) % 256;
v8 = (v8 + *((unsigned __int8 *)&dword_875088 + v9)) % 256;
v5 = *((_BYTE *)&dword_875088 + v9);
*((_BYTE *)&dword_875088 + v9) = *((_BYTE *)&dword_875088 + v8);
*((_BYTE *)&dword_875088 + v8) = v5;
v3 = i + a2;
v4 = i + a2;
*(_BYTE *)(i + a2) ^= *((_BYTE *)&dword_875088
+ (*((unsigned __int8 *)&dword_875088 + v8) + *((unsigned __int8 *)&dword_875088 + v9)) % 256);
}
return sub_571D80(v4, v3, 1, i, a1);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void __usercall sub_875231(int a1@<edx>, _BYTE *a2@<ecx>, int a3@<ebp>)
{
if ( v7 != 2 )
{
do
{
v11 = *(_BYTE *)(a1 + v9);
v8 = (v11 >> 2) & 0x3F;
v10 = 16 * (v11 & 3);
*v6 = v8 + 35;
v5 = *(_BYTE *)(*(_DWORD *)(a3 - 4) + v9 + 1);
v4 = *(_DWORD *)(a3 - 4);
v6[1] = ((v5 >> 4) & 0xF | v10) + 35;
v13 = *(_BYTE *)(v4 + v9 + 2);
v9 += 3;
v6[2] = ((v13 >> 6) & 3 | 4 * (v5 & 0xF)) + 35;
v6[3] = (v13 & 0x3F) + 35;
v6 += 4;
a1 = *(_DWORD *)(a3 - 4);
}
while ( v9 < v3 );
v7 = *(_DWORD *)(a3 + 8);
}

直接od动态调试即可,这里根据常量”B=.NI;&3JBZ;$;?(I72’0&4GLZDS2V6&%AF.!5#+J[F^”的长度为44可以推测出flag_part1长度为33,然而这两个加密函数只对flag的前半部分进行加密,所以flag长度应该为66,od开调

在加密函数入口点断下,修改内存为

1
'************************************************************************'
1
2
.text:00EF11F0                 push    eax
.text:00EF11F1 call sub_EBDD48

在check函数断下,查看内存,得到两个值

1
2
3
4
.text:00EE95F4                 push    offset aBNi3jbzI7204gl ; "B=.NI;&3JBZ;$;?(I72'0&4GLZDS2V6&%AF.!5#"...
.text:00EE95F9 lea eax, [ebp+var_7C]
.text:00EE95FC push eax
.text:00EE95FD call sub_EBD3F2

一个是我们的输入经过前面两个函数加密编码后的结果

=C-?@RIQ@F&-/P?8@a6?G4@O==D3CZ>,VI.:F/67B^7,:?N

另外一个是校验的常量

$HM%/NEX,79PBN\C/BQLVSW,*/‘8T#UMC4%EG@@@,.%5

根据RC4加密算法的原理,有 p1 ^ c1 = p2 ^ c2 (len(p1) == len(p2)),可以推出

c2 = p1 ^ c1 ^ p2(len(p2) > len(p1))

ps:p1、p2为明文,c1、c2为密文

再结合我们的明文

1
'************************************'
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
import base64
from Crypto.Util.number import long_to_bytes,bytes_to_long
b64table = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
table = "#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`ab"

def b64decode(src, table):
    if len(table) != 64:
        return 'Table length error'
    try:
        dest = base64.b64decode(
        src.translate(str.maketrans(table, b64table)).encode())
    except Exception as e:
        return e
    try:
        dest = dest.decode()
    except UnicodeDecodeError as identifier:
        pass
    return dest

dest = "=C-?@RIQ@F&-/P?8@a6?G4@O==D3CZ>,VI.:F\/67B^7,:?N"
tmp0 = b'*********************************************'
tmp1 = "$HM%/NEX,79PBN\C/BQLVSW,*/'8T#UMC4%EG@@@,.%5"
a1 = b64decode(dest,table)
a2 = b64decode(tmp1,table)

print(long_to_bytes(bytes_to_long(a1[:33])^bytes_to_long(a2[:33])^bytes_to_long(tmp0[:33])))

三者xor即可得到flag_part1 Fr4nk1y_MfC_l5_t0O_ComPIeX_4nd_dl

flag_part2就比较简单了,显然就是一个普通的tea加密算法,delta的值被魔改为0x2433B95A

1
2
3
4
5
6
7
8
9
10
11
do
{
v9 += 0x2433B95A;
v6 += (v9 + v8) ^ (v21 + 16 * v8) ^ (v14 + (v8 >> 5));
v10 = (v9 + v6) ^ (v19 + 16 * v6) ^ (v20 + (v6 >> 5));
v14 = v18;
v15 = v10 + v8;
--v7;
v8 = v15;
}
while ( v7 );

part2这里先校验了第一个字符是否为 ‘f’,剩下32个字节以8个字节为一组进行一次tea加密,每次加密密钥都不一样。然后就是算出加密后的密文,直接用key解密即可。

密文有4组,其中一组明文给出,另外三组是一个三元一次方程(这里涉及到整数溢出)

1
2
3
4
// (v17 - v14) == 0x3F66B755B4490579
// (v14 + v18) == 0x162F924623D2CAE0
// (v18 - v17) == 0x7C3C71F1B295D77F
// v14<v17<v18(无符号比较)

直接算得到的结果如下,显然不满足 v14<v17<v18

1
2
3
// v18 = 0x68e95dc6c558d3ec
// v17 = 0xecacebd512c2fc6d
// v14 = 0xad46347f5e79f6f4

所以这里 v14 + v18应该是溢出了,真正的方程应该为

1
2
3
// (v17 - v14) == 0x3F66B755B4490579
// (v14 + v18) == 0x1162F924623D2CAE0
// (v18 - v17) == 0x7C3C71F1B295D77F

解出三个值

1
2
3
// 2d46347f5e79f6f4
// 6cacebd512c2fc6d
// e8e95dc6c558d3ec

然后直接tea解密得到part2

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
#include <stdio.h>
#include <string.h>
void dec(unsigned int *ll,unsigned int *rr, unsigned int* key) {
  unsigned int l = *ll, r = *rr, sum = 0, delta = 0x2433B95A;
  sum = delta *32;
  // printf("%x,%x,%x,%x\n", key[0],key[1],key[2],key[3]);
  for (size_t i = 0; i < 32; i++) {
    r -= ((l << 4) + key[2]) ^ (l + sum) ^ ((l >> 5) + key[3]);
    l -= ((r << 4) + key[0]) ^ (r + sum) ^ ((r >> 5) + key[1]);
    sum -= delta;
  }
  *ll = l;
  *rr = r;
  printf("'%x','%x',", *ll,*rr);
}

void enc(unsigned int *ll,unsigned int *rr, unsigned int* key) {
  // printf("%x,%x\n", *ll,*rr);
  // printf("%x,%x,%x,%x\n", key[0],key[1],key[2],key[3]);
  unsigned int l = *ll, r = *rr, sum = 0, delta = 0x2433B95A;
  for (size_t i = 0; i < 32; i++) {
    sum += delta;
    l += ((r << 4) + key[0]) ^ (r + sum) ^ ((r >> 5) + key[1]);
    r += ((l << 4) + key[2]) ^ (l + sum) ^ ((l >> 5) + key[3]);
  }
  *ll = l;
  *rr = r;
  printf("0x%x,0x%x,\n", *ll,*rr);
}

unsigned int key[]={0x0D9610D02,0x2AADA57D,0x0A37537F1,0x0C29E3913,0x0D5942CE8,0x608CCE66,0x6D593422,0x21E5D6F2,0x0ED3A9235,0x9DAD62C4,0x3856641B,0x71F75B9D,0x0DCDEDAE8,0x0EAD2D1A0,0x0BAC4F564,0x0DA4772AC};
int main(int argc, char const *argv[])
{

  int ttt[] = {0x2d46347f,0x5e79f6f4,0xDF3634AE,0x2F9970FF,0x6cacebd5,0x12c2fc6d,0xe8e95dc6,0xc558d3ec};
  for (size_t i = 0; i <4; i++)
  {
    dec(ttt+2*i,ttt+1+2*i,key+4*i);
  }
  
  return 0;
}

将得到的结果按小端序 decode即可

flag_part2:ff1cUlt_foR_THe_r0Ok1E_t0_REver5e

拼接得到flag

Fr4nk1y_MfC_l5_t0O_ComPIeX_4nd_dlff1cUlt_foR_THe_r0Ok1E_t0_REver5e

0%