Featured image of post Geekchallenge2025 Her/ez_Android

Geekchallenge2025 Her/ez_Android

Her

这题的核心是手动构建一个SEH链,自己触发异常,导致伪代码无法正确查看程序执行流

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
.text:00401DBD loc_401DBD:                             ; CODE XREF: sub_401D30+82j
.text:00401DBD                 mov     eax, [ebp+var_20]
.text:00401DC0                 cmp     eax, [ebp+var_8]
.text:00401DC3                 jnb     short loc_401E3E
.text:00401DC5                 mov     eax, [ebp+var_20]
.text:00401DC8                 mov     dword_40B1DC, eax
.text:00401DCD                 push    offset j_enc3
.text:00401DD2                 push    large dword ptr fs:0
.text:00401DD9                 mov     large fs:0, esp
.text:00401DE0                 push    offset j_enc2
.text:00401DE5                 push    large dword ptr fs:0
.text:00401DEC                 mov     large fs:0, esp
.text:00401DF3                 push    offset j_enc1
.text:00401DF8                 push    large dword ptr fs:0
.text:00401DFF                 mov     large fs:0, esp
.text:00401E06                 ud2

这里手动写了一个SEH链,通过ud2这个异常来触发异常处理。

这里有enc1–>enc2–>enc3

查看enc1的汇编可知

1
2
3
.text:00401F60                 push    offset j_enc2
.text:00401F65                 push    large dword ptr fs:0
.text:00401F6C                 mov     large fs:0, esp

调用enc1之后还会调用enc2

查看enc2

1
2
3
.text:004020DD                 push    offset j_enc3
.text:004020E2                 push    large dword ptr fs:0
.text:004020E9                 mov     large fs:0, esp

调用enc2之后还会调用enc3

enc3中无SEH链的构造

所有最终的加密链条是enc1–>enc2–>enc3–enc2–>enc3–>enc3

enc1中对数据的处理byte_40B2E0[dword_40B1DC] = ++dword_40B1D8 ^ Str[dword_40B1DC] ^ 0xAA;

enc2中对数据的处理byte_40B2E0[dword_40B1DC] += ++dword_40B1D8 % 0x100u + dword_40B1DC * dword_40B1DC;

enc3中队数据的处理byte_40B260[v5] = ((int)(unsigned __int8)byte_40B2E0[v5] » 5) | (8 * byte_40B2E0[v5]);

dword_40B1DC为下标,dword_40B1D8 为一个参与加密的中间量,byte_40B260是最终密文数组

唯一难搞的就是dword_40B1D8的变化,在enc1和enc2会有+1,在每次动用反调试时会有dword_40B1D8 ^= 0x1A373u,每次enc会有反调试,循环加密最开始也有一个反调试

已知是单字节加密,就可以通过前四位SYC{,来验证同构

同构如下

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

#define ANTI_DEBUG_KEY 0x1A373u  // sub_4010CD的异或值
uint32_t dword_40B1D8 = 0;       // 核心计数变量,初始为0
uint8_t byte_40B2E0[10] = {0};  // 加密中间缓冲区
uint8_t byte_40B260[10] = {0};  // 最终密文缓冲区


void sub_4010CD(void) {
    dword_40B1D8 ^= ANTI_DEBUG_KEY;
}

uint8_t encrypt_single_byte(uint8_t plain_byte, uint32_t dw) {
    // ==================== 1. enc1 ====================
    byte_40B2E0[dw] = ++dword_40B1D8 ^ plain_byte ^ 0xAA;

    // ==================== 2. 调用sub_4010CD() ====================
    sub_4010CD();

    // ==================== 3. enc2 ====================
    byte_40B2E0[dw] += ++dword_40B1D8 % 0x100u + (dw * dw);
    // 强制截断为8位无符号字节(匹配C语言unsigned __int8规则)
    byte_40B2E0[dw] &= 0xFF;

    // ==================== 4. 调用sub_4010CD() ====================
    sub_4010CD();

    // ==================== 5. enc3:仅++dword_40B1D8 ====================
    ++dword_40B1D8;

    // ==================== 6. 调用sub_4010CD() ====================
    sub_4010CD();

    // ==================== 7. enc3:移位运算 ====================
    byte_40B260[dw] = ((uint8_t)byte_40B2E0[dw] >> 5) | (8 * byte_40B2E0[dw]);
    byte_40B260[dw] &= 0xFF;  // 截断为8位

    // ==================== 8. 第二次enc2 ====================
    byte_40B2E0[dw] += ++dword_40B1D8 % 0x100u + (dw * dw);
    byte_40B2E0[dw] &= 0xFF;

    // ==================== 9. 调用sub_4010CD() ====================
    sub_4010CD();

    // ==================== 10. 第二次enc3:仅++dword_40B1D8 ====================
    ++dword_40B1D8;

    // ==================== 11. 调用sub_4010CD() ====================
    sub_4010CD();

    // ==================== 12. 第二次enc3:移位运算 ====================
    byte_40B260[dw] = ((uint8_t)byte_40B2E0[dw] >> 5) | (8 * byte_40B2E0[dw]);
    byte_40B260[dw] &= 0xFF;

    // ==================== 13. 第三次enc3:仅++dword_40B1D8 ====================
    ++dword_40B1D8;

    // ==================== 14. 调用sub_4010CD() ====================
    sub_4010CD();

    // ==================== 15. 第三次enc3:移位运算(最终密文) ====================
    byte_40B260[dw] = ((uint8_t)byte_40B2E0[dw] >> 5) | (8 * byte_40B2E0[dw]);
    byte_40B260[dw] &= 0xFF;

    // 返回最终密文
    return byte_40B260[dw];
}

// 测试:验证前4位明文 SYC{ → 目标密文 0x2D/0x4F/0x69/0x3D
int main() {
    // 前4位明文:S(0x53)、Y(0x59)、C(0x43)、{(0x7B)
    uint8_t plain_4[] = {0x53, 0x59, 0x43, 0x7B};
    // 目标密文(题目给出的前4位)
    uint8_t target_cipher_4[] = {0x2D, 0x4F, 0x69, 0x3D};
	sub_4010CD();
    printf("===== 单字节加密验证(SYC{ → 目标密文)=====\n");
    for (uint32_t dw = 0; dw < 4; dw++) {
        uint8_t cipher = encrypt_single_byte(plain_4[dw], dw);
        printf("下标%d | 明文:0x%02X(%c) | 加密结果:0x%02X | 目标密文:0x%02X | %s\n",
               dw, plain_4[dw], plain_4[dw], cipher, target_cipher_4[dw],
               (cipher == target_cipher_4[dw]) ? "? 匹配" : "? 不匹配");
    }

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

#define ANTI_DEBUG_KEY 0x1A373u

// 解密函数:根据密文字节数组和长度,恢复明文
void decrypt(const uint8_t* cipher, int len, uint8_t* plain) {
    uint32_t state = ANTI_DEBUG_KEY; // 初始状态,与加密前一致
    for (int dw = 0; dw < len; dw++) {
        // 记录中间状态 S1, S3, S8
        uint32_t S1, S3, S8;

        // 步骤1
        state += 1;
        S1 = state;

        // 步骤2
        state ^= ANTI_DEBUG_KEY;

        // 步骤3
        state += 1;
        S3 = state;

        // 步骤4
        state ^= ANTI_DEBUG_KEY;

        // 步骤5
        state += 1;

        // 步骤6
        state ^= ANTI_DEBUG_KEY;

        // 步骤7不改变 state

        // 步骤8
        state += 1;
        S8 = state;

        // 从密文恢复 b3(循环右移3位)
        uint8_t c = cipher[dw];
        uint8_t b3 = ((c >> 3) | ((c & 0x07) << 5)) & 0xFF;

        // 计算 b2
        uint8_t b2 = (b3 - (S8 & 0xFF) - (dw * dw)) & 0xFF;

        // 计算 b1
        uint8_t b1 = (b2 - (S3 & 0xFF) - (dw * dw)) & 0xFF;

        // 计算明文
        plain[dw] = (b1 ^ (S1 & 0xFF) ^ 0xAA) & 0xFF;

        // 继续完成当前字节的剩余状态更新(步骤9到步骤14)
        state ^= ANTI_DEBUG_KEY; // 步骤9
        state += 1;              // 步骤10
        state ^= ANTI_DEBUG_KEY; // 步骤11
        // 步骤12不改变 state
        state += 1;              // 步骤13
        state ^= ANTI_DEBUG_KEY; // 步骤14
        // 步骤15不改变 state
    }
}

int main() {
    // 已知密文(30字节)
    uint8_t cipher[31] = {
        0x2D, 0x4F, 0x69, 0x3D, 0x5F, 0x01, 0xBD, 0x9F,
        0xA4, 0x6D, 0x89, 0xAE, 0x2A, 0xEA, 0xD1, 0x9C,
        0x71, 0x6D, 0xE1, 0x1E, 0x38, 0x7E, 0x8C, 0x0A,
        0xCE, 0x6B, 0xE0, 0xF7, 0x36, 0x72, 0x99
    };

    uint8_t plain[30] = {0};

    decrypt(cipher, 31, plain);

    printf("解密结果(字符串):\n");
    for (int i = 0; i < 31; i++) {
        printf("%c", plain[i]);
    }
    printf("\n");

    printf("解密结果(十六进制):\n");
    for (int i = 0; i < 30; i++) {
        printf("0x%02X ", plain[i]);
    }
    printf("\n");

    return 0;
}

SYC{M@y_bE_m3et_HeR_1s_A_Err0R}

ez_Android

我在raw中找到三个flag文件

flag1:43464244374534323530413841423536

flag2:C2726AD56FC754D1

flag3:AEBCDE9B24029CA7

查看jadx,flag1是标准DES加密

 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
package com.example.syc;

import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;

/* loaded from: classes.dex */
public abstract class function1 {
    public static String function1(String input1) throws BadPaddingException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, InvalidKeyException, UnsupportedEncodingException {
        try {
            byte[] data = input1.getBytes("UTF-8");
            int len = data.length;
            int padLen = 8 - (len % 8);
            byte[] padded = new byte[len + padLen];
            System.arraycopy(data, 0, padded, 0, len);
            SecretKeySpec keySpec = new SecretKeySpec("12345678".getBytes("UTF-8"), "DES");
            Cipher cipher = Cipher.getInstance("DES/ECB/NoPadding");
            cipher.init(1, keySpec);
            byte[] encrypted = cipher.doFinal(padded);
            StringBuilder sb = new StringBuilder();
            for (byte b : encrypted) {
                sb.append(String.format("%02X", Byte.valueOf(b)));
            }
            return sb.toString();
        } catch (Exception e) {
            e.printStackTrace();
            return "";
        }
    }
}

在java层实现

flag1

flag2在libwrapper.so中

 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
void __cdecl load_real_so(char *a1)
{
  int *v1; // eax
  int *v2; // eax
  void *handle; // [esp+1Ch] [ebp-4Ch]
  int fd; // [esp+20h] [ebp-48h]
  void *ptr; // [esp+24h] [ebp-44h]

  ptr = malloc(encrypted_so_len);
  if ( ptr )
  {
    __memcpy_chk(ptr, &encrypted_so, encrypted_so_len, -1);
    decrypt_xor((int)ptr, encrypted_so_len, 170);
    fd = open(a1, 578, 448);
    if ( fd >= 0 )
    {
      if ( __write_chk(fd, ptr, (*(&off_20CC - 2))->d_tag, -1) == (*(&off_20CC - 2))->d_tag )
      {
        close(fd);
        free(ptr);
        handle = dlopen(a1, 0);
        if ( handle )
        {
          *(&off_20CC + 2759) = (Elf32_Dyn *)dlsym(handle, "Java_com_example_syc_function2_function2");
          if ( *(&off_20CC + 2759) )
          {
            __android_log_print(3, "wrapper", "Real SO loaded successfully!");
          }
          else
          {
            dlerror();
            __android_log_print(3, "wrapper", "dlsym error: %s");
            dlclose(handle);
          }
        }
        else
        {
          dlerror();
          __android_log_print(3, "wrapper", "dlopen error: %s");
        }
      }
      else
      {
        v2 = (int *)__errno();
        strerror(*v2);
        __android_log_print(3, "wrapper", "write failed: %s");
        close(fd);
        free(ptr);
      }
    }
    else
    {
      v1 = (int *)__errno();
      strerror(*v1);
      __android_log_print(3, "wrapper", "open tmp file failed: %s");
      free(ptr);
    }
  }
  else
  {
    __android_log_print(3, "wrapper", "malloc failed");
  }
}

发现这个程序其实是把encrypted_so这个数据和0xAA异或进行解密,才能显示正确的逻辑

通过脚本得到真正的.so

 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
__int64 __fastcall Java_com_example_syc_function2_function2(__int64 a1, __int64 a2, __int64 a3)
{
  int v3; // w0
  const char *v5; // [xsp+8h] [xbp-568h]
  int v6; // [xsp+24h] [xbp-54Ch]
  int i; // [xsp+34h] [xbp-53Ch]
  char v8[1024]; // [xsp+38h] [xbp-538h] BYREF
  __int64 v9; // [xsp+438h] [xbp-138h] BYREF
  int v10; // [xsp+440h] [xbp-130h]
  int v11; // [xsp+444h] [xbp-12Ch]
  const char *v12; // [xsp+448h] [xbp-128h]
  _BYTE v13[256]; // [xsp+450h] [xbp-120h] BYREF
  const char *v14; // [xsp+550h] [xbp-20h]
  __int64 v15; // [xsp+558h] [xbp-18h]
  __int64 v16; // [xsp+560h] [xbp-10h]
  __int64 v17; // [xsp+568h] [xbp-8h]

  v17 = a1;
  v16 = a2;
  v15 = a3;
  v14 = "mysecret";
  v12 = (const char *)(*(__int64 (__fastcall **)(__int64, __int64, _QWORD))(*(_QWORD *)a1 + 1352LL))(a1, a3, 0LL);
  v11 = strlen(v12);
  if ( v11 >= 8 )
    v6 = 8;
  else
    v6 = v11;
  v10 = v6;
  v9 = 0LL;
  v5 = v14;
  v3 = strlen(v14);
  rc4_init((__int64)v13, (__int64)v5, v3);
  rc4_crypt((__int64)v13, (__int64)v12, (__int64)&v9, v11);
  memset(v8, 0, sizeof(v8));
  for ( i = 0; i < v11; ++i )
    sprintf(&v8[2 * i], "%02X", *((unsigned __int8 *)&v9 + i));
  (*(void (__fastcall **)(__int64, __int64, const char *))(*(_QWORD *)v17 + 1360LL))(v17, v15, v12);
  return (*(__int64 (__fastcall **)(__int64, char *))(*(_QWORD *)v17 + 1336LL))(v17, v8);
}

发现是RC4,密钥是mysecret

flag2

flag3是一个魔改DES,主要魔改在generate_subkeys

  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
#include <stdio.h>  
#include <string.h>  
  
#define DES_BLOCK_SIZE 8 
#define KEY_SIZE 8        

static const unsigned char DES_KEY[8] = {0x13, 0x34, 0x57, 0x79, 0x9B, 0xBC, 0xDF, 0xF1};  
  
static const int IP[64] = {  
        58,50,42,34,26,18,10,2,60,52,44,36,28,20,12,4,  
        62,54,46,38,30,22,14,6,64,56,48,40,32,24,16,8,  
        57,49,41,33,25,17,9,1,59,51,43,35,27,19,11,3,  
        61,53,45, 37,29,21,13,5,63,55,47,39,31,23,15,7  
};  
  
static const int FP[64] = {  
        40,8,48,16,56,24,64,32,39,7,47,15,55,23,63,31,  
        38,6,46,14,54,22,62,30,37,5,45,13,53,21,61,29,  
        36,4,44,12,52,20,60,28,35,3,43,11,51,19,59,27,  
        34,2,42,10,50,18,58,26,33,1,41,9,49,17,57,25  
};  
  
static const int E[48] = {  
        32,1,2,3,4,5,4,5,6,7,8,9,8,9,10,11,12,13,12,13,  
        14,15,16,17,16,17,18,19,20,21,20,21,22,23,24,25,  
        24,25,26,27,28,29,28,29,30,31,32,1  
};  
  
static const int P[32] = {  
        16,7,20,21,29,12,28,17,1,15,23,26,5,18,31,10,  
        2,8,24,14,32,27,3,9,19,13,30,6,22,11,4,25  
};  
  
static const int PC1[56] = {  
        57,49,41,33,25,17,9,1,58,50,42,34,26,18,10,2,  
        59,51,43,35,27,19,11,3,60,52,44,36,63,55,47,39,  
        31,23,15,7,62,54,46,38,30,22,14,6,61,53,45,37,  
        29,21,13,5,28,20,12,4  
};  
  
static const int PC2[48] = {  
        14,17,11,24,1,5,3,28,15,6,21,10,23,19,12,4,  
        26,8,16,7,27,20,13,2,41,52,31,37,47,55,30,40,  
        51,45,33,48,44,49,39,56,34,53,46,42,50,36,29,32  
};  
  
static const int shifts[16] = {1,1,2,2,2,2,2,2,1,2,2,2,2,2,2,1};  
  
static const int S_BOX[8][4][16] = {  
        {{14,4,13,1,2,15,11,8,3,10,6,12,5,9,0,7},{0,15,7,4,14,2,13,1,10,6,12,11,9,5,3,8},{4,1,14,8,13,6,2,11,15,12,9,7,3,10,5,0},{15,12,8,2,4,9,1,7,5,11,3,14,10,0,6,13}},  
        {{15,1,8,14,6,11,3,4,9,7,2,13,12,0,5,10},{3,13,4,7,15,2,8,14,12,0,1,10,6,9,11,5},{0,14,7,11,10,4,13,1,5,8,12,6,9,3,2,15},{13,8,10,1,3,15,4,2,11,6,7,12,0,5,14,9}},  
        {{10,0,9,14,6,3,15,5,1,13,12,7,11,4,2,8},{13,7,0,9,3,4,6,10,2,8,5,14,12,11,15,1},{13,6,4,9,8,15,3,0,11,1,2,12,5,10,14,7},{1,10,13,0,6,9,8,7,4,15,14,3,11,5,2,12}},  
        {{7,13,14,3,0,6,9,10,1,2,8,5,11,12,4,15},{13,8,11,5,6,15,0,3,4,7,2,12,1,10,14,9},{10,6,9,0,12,11,7,13,15,1,3,14,5,2,8,4},{3,15,0,6,10,1,13,8,9,4,5,11,12,7,2,14}},  
        {{2,12,4,1,7,10,11,6,8,5,3,15,13,0,14,9},{14,11,2,12,4,7,13,1,5,0,15,10,3,9,8,6},{4,2,1,11,10,13,7,8,15,9,12,5,6,3,0,14},{11,8,12,7,1,14,2,13,6,15,0,9,10,4,5,3}},  
        {{12,1,10,15,9,2,6,8,0,13,3,4,14,7,5,11},{10,15,4,2,7,12,9,5,6,1,13,14,0,11,3,8},{9,14,15,5,2,8,12,3,7,0,4,10,1,13,11,6},{4,3,2,12,9,5,15,10,11,14,1,7,6,0,8,13}},  
        {{4,11,2,14,15,0,8,13,3,12,9,7,5,10,6,1},{13,0,11,7,4,9,1,10,14,3,5,12,2,15,8,6},{1,4,11,13,12,3,7,10,15,6,8,0,5,9,14,2},{6,11,13,8,1,4,10,7,9,5,0,15,14,2,3,12}},  
};  
  
void permute(const unsigned char *in, unsigned char *out, const int *table, int size) {  
    memset(out, 0, (size+7)/8);  
    for(int i=0;i<size;i++){  
        int pos=table[i]-1;  
        int byte_idx=pos/8;  
        int bit_idx=7-(pos%8);  
        unsigned char bit=(in[byte_idx]>>bit_idx)&0x01;  
        out[i/8]|=(bit<<(7-(i%8)));  
    }
}  
  
void left_shift(unsigned char *key,int shifts){  
    unsigned char temp=key[0]>>(8-shifts);  
    for(int i=0;i<3;i++) key[i]=(key[i]<<shifts)|(key[i+1]>>(8-shifts));  
    key[3]=(key[3]<<shifts)|temp;  
}  
  
void generate_subkeys(const unsigned char *key,unsigned char subkeys[16][6]){  
    unsigned char permuted_key[7]={0};  
    permute(key,permuted_key,PC1,56);  
    unsigned char C[4],D[4];  
    memcpy(C,permuted_key,4);  
    memcpy(D,permuted_key+3,4);  
    for(int i=0;i<16;i++){  
        left_shift(C,shifts[i]);  
        left_shift(D,shifts[i]);  
        unsigned char combined[7];  
        memcpy(combined,C,4);  
        memcpy(combined+3,D,4);  
        memset(subkeys[i],0,6);  
        permute(combined,subkeys[i],PC2,48);  
    }
}  
  
void feistel(unsigned char *R,const unsigned char *subkey){  
    unsigned char expanded[6]={0};  
    permute(R,expanded,E,48);  
    for(int i=0;i<6;i++) expanded[i]^=subkey[i];  
    unsigned char output[4]={0};  
    for(int i=0;i<8;i++){  
        int idx=i*6;  
        int row=((expanded[idx/8]>>(7-(idx%8)))&0x01)<<1;  
        row|=(expanded[(idx+5)/8]>>(7-((idx+5)%8)))&0x01;  
        int col=0;  
        for(int j=1;j<=4;j++){  
            col<<=1;  
            col|=(expanded[(idx+j)/8]>>(7-((idx+j)%8)))&0x01;  
        }
        int val=S_BOX[i][row][col];  
        output[i/2]|=(i%2==0)?(val<<4):val;  
    }
    unsigned char temp[4]={0};  
    permute(output,temp,P,32);  
    memcpy(R,temp,4);  
}  
  
void des_crypt(const unsigned char *key,const unsigned char *input,unsigned char *output,int encrypt){  
    unsigned char subkeys[16][6];  
    generate_subkeys(key,subkeys);  
    unsigned char block[8]={0};  
    permute(input,block,IP,64);  
    unsigned char L[4],R[4];  
    memcpy(L,block,4);  
    memcpy(R,block+4,4);  
    for(int i=0;i<16;i++){  
        unsigned char temp[4];  
        memcpy(temp,R,4);  
        int idx = encrypt? i: 15-i;  
        feistel(R,subkeys[idx]);  
        for(int j=0;j<4;j++) R[j]^=L[j];  
        memcpy(L,temp,4);  
    }
    unsigned char preoutput[8];  
    memcpy(preoutput,R,4);  
    memcpy(preoutput+4,L,4);  
    permute(preoutput,output,FP,64);  
}  
  
void des_encrypt(const unsigned char *key,const unsigned char *input,unsigned char *output){  
    des_crypt(key,input,output,1);  
}  
  
void des_decrypt(const unsigned char *key,const unsigned char *input,unsigned char *output){  
    des_crypt(key,input,output,0);  
}  
  
int main() {  
    const char *hex_ciphertext = "AEBCDE9B24029CA7";  
    unsigned char ciphertext[8] = {0};  
    unsigned char decrypted[8] = {0};  
  
    for (int i = 0; i < 8; i++) {  
        sscanf(hex_ciphertext + i * 2, "%2hhX", &ciphertext[i]);  
    }  
    des_decrypt(DES_KEY, ciphertext, decrypted);  
  
    printf("解密: %.8s\n", decrypted);  
  
    return 0;  
}

ly_F1ag!

SYC{Th1s_1s_Th3_R3@lly_F1ag!}