自学内容网 自学内容网

CTF-RE 从0到N:Chacha20逆向实战 2024 强网杯青少年专项赛 EnterGame WP (END)

只想解题的看最后就好了,前面是算法分析
Chacha20
c语言是如何利用逻辑运算符拆分变量和合并的

通过百度网盘分享的文件:EnterGame_9acdc7c33f85832082adc6a4e...
链接:https://pan.baidu.com/s/182SRj2Xemo63PCoaLNUsRQ?pwd=1111 
提取码:1111 
--来自百度网盘超级会员V2的分享

step1 主函数

int __fastcall main(int argc, const char **argv, const char **envp)

  unsigned int v3; // eax
  _WORD v5[20]; // [rsp+8h] [rbp-138h] BYREF
  _QWORD s2[2]; // [rsp+30h] [rbp-110h] BYREF
  _QWORD v7[4]; // [rsp+40h] [rbp-100h] BYREF
  char s[112]; // [rsp+60h] [rbp-E0h] BYREF
  _BYTE s1[104]; // [rsp+D0h] [rbp-70h] BYREF
  unsigned __int64 v10; // [rsp+138h] [rbp-8h]

  v10 = __readfsqword(0x28u);
  puts("Welcome to the Youth Cybersecurity Challenge!");
  s2[0] = 0x7A2B7587D3AA135ELL;
  s2[1] = 0xD21D7E49A304161BLL;
  qmemcpy(v7, "k]X@^DcYHQ", 10);
  *(_QWORD *)((char *)&v7[1] + 2) = 0x82AD5855585E540DLL;
  *(_QWORD *)((char *)&v7[2] + 2) = 0xC1CE5D58ABE7DCAFLL;
  puts("Please enter the password to start the game:");
  fgets(s, 100, _bss_start);
  s[strcspn(s, "\n")] = 0;
  HIBYTE(v5[18]) = 0;
  v5[19] = 0;
  strcpy((char *)v5, "01234567Youth Strengthens the Nation");
  v3 = strlen(s);
  chacha20_encrypt(&v5[4], v5, s, s1, v3);
  if ( !memcmp(s1, s2, 0x2AuLL) )
  {
    puts("Password correct, you may start the game.");
    puts("With my strength, I secure the cyber frontier!");
  }
  else
  {
    puts("Incorrect password, cannot start the game.");
  }
  return 0;
}

直接定位到比较函数,在此处动态调试抽取s2的值

  if ( !memcmp(s1, s2, 0x2AuLL) )
  {
    puts("Password correct, you may start the game.");
    puts("With my strength, I secure the cyber frontier!");
  }
  else
  {
    puts("Incorrect password, cannot start the game.");
  }

建立数组使用插件dump数组备用
请添加图片描述

unsigned char data[42] = {
    0x5E, 0x13, 0xAA, 0xD3, 0x87, 0x75, 0x2B, 0x7A, 0x1B, 0x16, 0x04, 0xA3, 0x49, 0x7E, 0x1D, 0xD2, 
    0x6B, 0x5D, 0x58, 0x40, 0x5E, 0x44, 0x63, 0x59, 0x48, 0x51, 0x0D, 0x54, 0x5E, 0x58, 0x55, 0x58, 
    0xAD, 0x82, 0xAF, 0xDC, 0xE7, 0xAB, 0x58, 0x5D, 0xCE, 0xC1
};

step2 分析chacha20_encrypt参数

unsigned __int64 __fastcall chacha20_encrypt(int *a1, int *a2, __int64 a3, __int64 a4, int a5)
{
  int i; // [rsp+38h] [rbp-98h]
  int j; // [rsp+3Ch] [rbp-94h]
  int v11[12]; // [rsp+40h] [rbp-90h] BYREF
  int v12; // [rsp+70h] [rbp-60h]
  int v13; // [rsp+74h] [rbp-5Ch]
  int v14; // [rsp+78h] [rbp-58h]
  int v15; // [rsp+7Ch] [rbp-54h]
  char v16[72]; // [rsp+80h] [rbp-50h] BYREF
  unsigned __int64 v17; // [rsp+C8h] [rbp-8h]

  v17 = __readfsqword(0x28u);
  qmemcpy(v11, "expand 32-byte k", 16);
  v11[4] = *a1;
  v11[5] = a1[1];
  v11[6] = a1[2];
  v11[7] = a1[3];
  v11[8] = a1[4];
  v11[9] = a1[5];
  v11[10] = a1[6];
  v11[11] = a1[7];
  v12 = 0;
  v13 = 0;
  v14 = *a2;
  v15 = a2[1];
  for ( i = 0; i < a5; i += 64 )
  {
    chacha20_block(v16, v11);
    ++v12;
    for ( j = 0; j <= 63 && a5 > i + j; ++j )
      *(_BYTE *)(i + j + a4) = v16[j] ^ *(_BYTE *)(i + j + a3);
  }
  return v17 - __readfsqword(0x28u);
}

无视换位直接动态调试在此句断下

  for ( i = 0; i < a5; i += 64 )
  {
    chacha20_block(v16, v11);
    ++v12;
    for ( j = 0; j <= 63 && a5 > i + j; ++j )
      *(_BYTE *)(i + j + a4) = v16[j] ^ *(_BYTE *)(i + j + a3);
  }

化简,因为i + j + a4 等价于 a4[i + j]

  for ( i = 0; i < a5; i += 64 )
  {
    chacha20_block(v16, v11);
    ++v12;
    for ( j = 0; j <= 63 && a5 > i + j; ++j )
      a4[i + j] = v16[j] ^ a3[i+j];
  }

对比标准函数

    while (in_len > 0) {
        // 生成一个加密块
        chacha20_block((uint32_t *)block, state);
        state[12]++;  // 每次加密后递增计数器

        size_t block_size = (in_len < 64) ? in_len : 64;  // 计算当前块的大小
        for (i = 0; i < block_size; i++) {
            out[i] = in[i] ^ block[i];  // 将输入数据与加密块异或得到密文
        }

step3 _block()分析

unsigned __int64 __fastcall chacha20_block(__int64 a1, const void *a2)
{
  int i; // [rsp+18h] [rbp-58h]
  int j; // [rsp+1Ch] [rbp-54h]
  unsigned int dest; // [rsp+20h] [rbp-50h] BYREF
  unsigned int v6; // [rsp+24h] [rbp-4Ch]
  unsigned int v7; // [rsp+28h] [rbp-48h]
  unsigned int v8; // [rsp+2Ch] [rbp-44h]
  unsigned int v9; // [rsp+30h] [rbp-40h]
  unsigned int v10; // [rsp+34h] [rbp-3Ch]
  unsigned int v11; // [rsp+38h] [rbp-38h]
  unsigned int v12; // [rsp+3Ch] [rbp-34h]
  unsigned int v13; // [rsp+40h] [rbp-30h]
  unsigned int v14; // [rsp+44h] [rbp-2Ch]
  unsigned int v15; // [rsp+48h] [rbp-28h]
  unsigned int v16; // [rsp+4Ch] [rbp-24h]
  unsigned int v17; // [rsp+50h] [rbp-20h]
  unsigned int v18; // [rsp+54h] [rbp-1Ch]
  unsigned int v19; // [rsp+58h] [rbp-18h]
  unsigned int v20; // [rsp+5Ch] [rbp-14h]
  unsigned __int64 v21; // [rsp+68h] [rbp-8h]

  v21 = __readfsqword(0x28u);
  memcpy(&dest, a2, 0x40uLL);
  for ( i = 0; i <= 9; ++i )
  {
    dest += v9;
    v17 = (dest << 16) ^ v17 | HIWORD(dest) ^ v17;
    v13 += v17;
    v9 = (v13 << 12) ^ v9 | (v13 >> 20) ^ v9;
    dest += v9;
    v17 = (dest << 8) ^ v17 | HIBYTE(dest) ^ v17;
    v13 += v17;
    v9 = (v13 << 7) ^ v9 | (v13 >> 25) ^ v9;
    v6 += v10;
    v18 = (v6 << 16) ^ v18 | HIWORD(v6) ^ v18;
    v14 += v18;
    v10 = (v14 << 12) ^ v10 | (v14 >> 20) ^ v10;
    v6 += v10;
    v18 = (v6 << 8) ^ v18 | HIBYTE(v6) ^ v18;
    v14 += v18;
    v10 = (v14 << 7) ^ v10 | (v14 >> 25) ^ v10;
    v7 += v11;
    v19 = (v7 << 16) ^ v19 | HIWORD(v7) ^ v19;
    v15 += v19;
    v11 = (v15 << 12) ^ v11 | (v15 >> 20) ^ v11;
    v7 += v11;
    v19 = (v7 << 8) ^ v19 | HIBYTE(v7) ^ v19;
    v15 += v19;
    v11 = (v15 << 7) ^ v11 | (v15 >> 25) ^ v11;
    v8 += v12;
    v20 = (v8 << 16) ^ v20 | HIWORD(v8) ^ v20;
    v16 += v20;
    v12 = (v16 << 12) ^ v12 | (v16 >> 20) ^ v12;
    v8 += v12;
    v20 = (v8 << 8) ^ v20 | HIBYTE(v8) ^ v20;
    v16 += v20;
    v12 = (v16 << 7) ^ v12 | (v16 >> 25) ^ v12;
    dest += v10;
    v20 = (dest << 16) ^ v20 | HIWORD(dest) ^ v20;
    v15 += v20;
    v10 = (v15 << 12) ^ v10 | (v15 >> 20) ^ v10;
    dest += v10;
    v20 = (dest << 8) ^ v20 | HIBYTE(dest) ^ v20;
    v15 += v20;
    v10 = (v15 << 7) ^ v10 | (v15 >> 25) ^ v10;
    v6 += v11;
    v17 = (v6 << 16) ^ v17 | HIWORD(v6) ^ v17;
    v16 += v17;
    v11 = (v16 << 12) ^ v11 | (v16 >> 20) ^ v11;
    v6 += v11;
    v17 = (v6 << 8) ^ v17 | HIBYTE(v6) ^ v17;
    v16 += v17;
    v11 = (v16 << 7) ^ v11 | (v16 >> 25) ^ v11;
    v7 += v12;
    v18 = (v7 << 16) ^ v18 | HIWORD(v7) ^ v18;
    v13 += v18;
    v12 = (v13 << 12) ^ v12 | (v13 >> 20) ^ v12;
    v7 += v12;
    v18 = (v7 << 8) ^ v18 | HIBYTE(v7) ^ v18;
    v13 += v18;
    v12 = (v13 << 7) ^ v12 | (v13 >> 25) ^ v12;
    v8 += v9;
    v19 = (v8 << 16) ^ v19 | HIWORD(v8) ^ v19;
    v14 += v19;
    v9 = (v14 << 12) ^ v9 | (v14 >> 20) ^ v9;
    v8 += v9;
    v19 = (v8 << 8) ^ v19 | HIBYTE(v8) ^ v19;
    v14 += v19;
    v9 = (v14 << 7) ^ v9 | (v14 >> 25) ^ v9;
  }
  for ( j = 0; j <= 15; ++j )
    *(4LL * j + a1) = *(&dest + j) + *(a2 + j);
  return v21 - __readfsqword(0x28u);
}

将其与标准Chacha20代码对比

void chacha20_block(uint32_t output[16], const uint32_t input[16]) {
    int i;
    uint32_t x[16];  // 创建一个32位的数组用于存储状态
    memcpy(x, input, sizeof(x));  // 将输入状态复制到数组x

    // 进行20轮加密操作,每轮执行四分之一轮操作
    for (i = 0; i < 10; i++) {
        // 奇数轮
        QR(x[0], x[4], x[8], x[12]);
        QR(x[1], x[5], x[9], x[13]);
        QR(x[2], x[6], x[10], x[14]);
        QR(x[3], x[7], x[11], x[15]);

        // 偶数轮
        QR(x[0], x[5], x[10], x[15]);
        QR(x[1], x[6], x[11], x[12]);
        QR(x[2], x[7], x[8], x[13]);
        QR(x[3], x[4], x[9], x[14]);
    }

    // 将加密结果与原始输入状态相加,输出最终结果
    for (i = 0; i < 16; ++i) {
        output[i] = x[i] + input[i];
    }
}

继续对比

#define QR(a, b, c, d) ( \
    a += b, d ^= a, d = ROTL(d, 16), \
    c += d, b ^= c, b = ROTL(b, 12), \
    a += b, d ^= a, d = ROTL(d, 8), \
    c += d, b ^= c, b = ROTL(b, 7))

此加密函数外层循环包含重复的此块8次,与此对应分析

        QR(x[0], x[4], x[8], x[12]);
        QR(x[1], x[5], x[9], x[13]);
        QR(x[2], x[6], x[10], x[14]);
        QR(x[3], x[7], x[11], x[15]);

        // 偶数轮
        QR(x[0], x[5], x[10], x[15]);
        QR(x[1], x[6], x[11], x[12]);
        QR(x[2], x[7], x[8], x[13]);
        QR(x[3], x[4], x[9], x[14]);
    dest += v9;
    v17 = (dest << 16) ^ v17 | HIWORD(dest) ^ v17;
    v13 += v17;
    v9 = (v13 << 12) ^ v9 | (v13 >> 20) ^ v9;
    dest += v9;
    v17 = (dest << 8) ^ v17 | HIBYTE(dest) ^ v17;
    v13 += v17;
    v9 = (v13 << 7) ^ v9 | (v13 >> 25) ^ v9;
    v6 += v10;

退化ida的HIWORD

dest += v9;
v17 = (dest << 16) ^ v17 | (dest >> 16) ^ v17;
v13 += v17;
v9 = (v13 << 12) ^ v9 | (v13 >> 20) ^ v9;
dest += v9;
v17 = (dest << 8) ^ v17 | (dest >> 24) ^ v17;
v13 += v17;
v9 = (v13 << 7) ^ v9 | (v13 >> 25) ^ v9;
v6 += v10;

分析

  1. 变量映射:

    • 我们来对比两个代码片段中的变量:
      • a 对应 dest
      • b 对应 v9
      • c 对应 v13
      • d 对应 v17
  2. 操作分析:

    • 第一轮:
      • 第一个代码片段:

        1. a += b
        2. d ^= a
        3. d = ROTL(d, 16)
        4. c += d
        5. b ^= c
        6. b = ROTL(b, 12)
        7. a += b
        8. d ^= a
        9. d = ROTL(d, 8)
        10. c += d
        11. b ^= c
        12. b = ROTL(b, 7)
      • 第二个代码片段:

        1. dest += v9 (对应 a += b
        2. v17 = (dest << 16) ^ v17 | (dest >> 16) ^ v17 (对应 d ^= ad = ROTL(d, 16)
        3. v13 += v17 (对应 c += d
        4. v9 = (v13 << 12) ^ v9 | (v13 >> 20) ^ v9 (对应 b ^= cb = ROTL(b, 12)
        5. dest += v9 (对应 a += b
        6. v17 = (dest << 8) ^ v17 | (dest >> 24) ^ v17 (对应 d ^= ad = ROTL(d, 8)
        7. v13 += v17 (对应 c += d
        8. v9 = (v13 << 7) ^ v9 | (v13 >> 25) ^ v9 (对应 b ^= cb = ROTL(b, 7)

step4 重新化简代码

unsigned __int64 __fastcall chacha20_encrypt(int *a1, int *a2, __int64 a3, __int64 a4, int a5)
{
  int i; // [rsp+38h] [rbp-98h]
  int j; // [rsp+3Ch] [rbp-94h]
  int v11[12]; // [rsp+40h] [rbp-90h] BYREF
  int v12; // [rsp+70h] [rbp-60h]
  int v13; // [rsp+74h] [rbp-5Ch]
  int v14; // [rsp+78h] [rbp-58h]
  int v15; // [rsp+7Ch] [rbp-54h]
  char v16[72]; // [rsp+80h] [rbp-50h] BYREF
  unsigned __int64 v17; // [rsp+C8h] [rbp-8h]

  v17 = __readfsqword(0x28u);
  qmemcpy(v11, "expand 32-byte k", 16);
  v11[4] = *a1;
  v11[5] = a1[1];
  v11[6] = a1[2];
  v11[7] = a1[3];
  v11[8] = a1[4];
  v11[9] = a1[5];
  v11[10] = a1[6];
  v11[11] = a1[7];
  v12 = 0;
  v13 = 0;
  v14 = *a2;
  v15 = a2[1];
  for ( i = 0; i < a5; i += 64 )
  {
    chacha20_block(v16, v11);
    ++v12;
    for ( j = 0; j <= 63 && a5 > i + j; ++j )
      *(_BYTE *)(i + j + a4) = v16[j] ^ *(_BYTE *)(i + j + a3);
  }
  return v17 - __readfsqword(0x28u);
}

1.根据Chacha20加密代码v11state[16]重定义v11int state[16]
2.根据Chacha20加密代码v12重命名为block
3.根据Chacha20加密代码a4=>char out[64],a3=>in[64]

unsigned __int64 __fastcall chacha20_encrypt(int *a1, int *a2, char (*in)[64], char (*out)[64], int a5)
{
  int i; // [rsp+38h] [rbp-98h]
  int j; // [rsp+3Ch] [rbp-94h]
  int state[16]; // [rsp+40h] [rbp-90h] BYREF
  _BYTE block[72]; // [rsp+80h] [rbp-50h] BYREF
  unsigned __int64 v13; // [rsp+C8h] [rbp-8h]

  v13 = __readfsqword(0x28u);
  qmemcpy(state, "expand 32-byte k", 16);
  state[4] = *a1;
  state[5] = a1[1];
  state[6] = a1[2];
  state[7] = a1[3];
  state[8] = a1[4];
  state[9] = a1[5];
  state[10] = a1[6];
  state[11] = a1[7];
  state[12] = 0;
  state[13] = 0;
  state[14] = *a2;
  state[15] = a2[1];
  for ( i = 0; i < a5; i += 64 )
  {
    chacha20_block(block, state);
    ++state[12];
    for ( j = 0; j <= 63 && a5 > i + j; ++j )
      (*out)[i + j] = block[j] ^ (*in)[i + j];
  }
  return v13 - __readfsqword(0x28u);
}

我们能看到此时ida代码已经趋近与标准代码

try get flag

#include <stdint.h>  // 包含标准整数类型
#include <string.h>  // 包含内存操作函数,如 memcpy
#include <cstdio>    // 包含标准输入输出函数,如 putchar


// 宏定义:循环左移(左移操作后,右边溢出的部分重新回到左边)
#define ROTL(a, b) (((a) << (b)) | ((a) >> (32 - (b))))

// 宏定义:Chacha20的四分之一轮(Quarter Round)操作
#define QR(a, b, c, d) ( \
    a += b, d ^= a, d = ROTL(d, 16), \
    c += d, b ^= c, b = ROTL(b, 12), \
    a += b, d ^= a, d = ROTL(d, 8), \
    c += d, b ^= c, b = ROTL(b, 7))

// ChaCha20加密算法中的块函数
void chacha20_block(uint32_t output[16], const uint32_t input[16]) {
    int i;
    uint32_t x[16];  // 创建一个32位的数组用于存储状态
    memcpy(x, input, sizeof(x));  // 将输入状态复制到数组x

    // 进行20轮加密操作,每轮执行四分之一轮操作
    for (i = 0; i < 10; i++) {
        // 奇数轮
        QR(x[0], x[4], x[8], x[12]);
        QR(x[1], x[5], x[9], x[13]);
        QR(x[2], x[6], x[10], x[14]);
        QR(x[3], x[7], x[11], x[15]);

        // 偶数轮
        QR(x[0], x[5], x[10], x[15]);
        QR(x[1], x[6], x[11], x[12]);
        QR(x[2], x[7], x[8], x[13]);
        QR(x[3], x[4], x[9], x[14]);
    }

    // 将加密结果与原始输入状态相加,输出最终结果
    for (i = 0; i < 16; ++i) {
        output[i] = x[i] + input[i];
    }
}

// ChaCha20加密函数
void chacha20_encrypt(uint8_t *out, const uint8_t *in, size_t in_len) {
    /*
     * 从 chacha20_block(block, state); 断点抽取
     */
    uint32_t state[16] = {
            0x61707865, 0x3320646E, 0x79622D32, 0x6B206574, 0x74756F59, 0x74532068, 0x676E6572, 0x6E656874,
            0x68742073, 0x614E2065, 0x6E6F6974, 0x00000000, 0x00000000, 0x00000000, 0x33323130, 0x37363534
    };

    uint8_t block[64];  // 存储每次生成的64字节的加密块
    size_t i, j;
    while (in_len > 0) {
        // 生成一个加密块
        chacha20_block((uint32_t *)block, state);
        state[12]++;  // 每次加密后递增计数器

        size_t block_size = (in_len < 64) ? in_len : 64;  // 计算当前块的大小
        for (i = 0; i < block_size; i++) {
            out[i] = in[i] ^ block[i];  // 将输入数据与加密块异或得到密文
        }

        // 更新剩余输入数据的长度和指针
        in_len -= block_size;
        in += block_size;
        out += block_size;
    }
}

int main() {
    /*
     * 从 if ( !memcmp(s1, s2, 0x2AuLL) ) 断点抽取
     */
    uint8_t ciphertext[42] = {
                0x5E, 0x13, 0xAA, 0xD3, 0x87, 0x75, 0x2B, 0x7A, 0x1B, 0x16, 0x04, 0xA3, 0x49, 0x7E, 0x1D, 0xD2,
                0x6B, 0x5D, 0x58, 0x40, 0x5E, 0x44, 0x63, 0x59, 0x48, 0x51, 0x0D, 0x54, 0x5E, 0x58, 0x55, 0x58,
                0xAD, 0x82, 0xAF, 0xDC, 0xE7, 0xAB, 0x58, 0x5D, 0xCE, 0xC1
    };  // 用于存储加密后的密文
    uint8_t decrypted[64];  // 用于存储解密后的数据

    // 执行解密操作(加密是对称的,解密过程与加密相同)
    chacha20_encrypt(decrypted, ciphertext, sizeof(ciphertext));

    // 解密后的数据应当与原始明文相同
    for (int i = 0; i < sizeof(decrypted); i++) {
        putchar(decrypted[i]);  // 输出解密后的字符
    }

    return 0;
}

不知道为什么不对,直接暴力解题,尝试抽取chacha20_block(block, state);block和密文暴力解题

a = [0x38, 0x7F, 0xCB, 0xB4, 0xFC, 0x46, 0x13, 0x4F, 0x22, 0x27, 0x31, 0xC2, 0x2D, 0x53, 0x25, 0xB4, 0x58, 0x6F, 0x75, 0x74, 0x67, 0x20, 0x53, 0x74, 0x71, 0x65, 0x6E, 0x67, 0x73, 0x68, 0x65, 0x6E, 0x9A, 0xE4, 0x9E, 0xB8, 0x86, 0xCF, 0x69, 0x3F, 0xAA, 0xBC, 0x94, 0x90, 0x84, 0xDD, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x2F, 0x31, 0x32, 0x33, 0x33, 0x35, 0x36, 0x37]
b = [0x5E, 0x13, 0xAA, 0xD3, 0x87, 0x75, 0x2B, 0x7A, 0x1B, 0x16, 0x04, 0xA3, 0x49, 0x7E, 0x1D, 0xD2, 0x6B, 0x5D, 0x58, 0x40, 0x5E, 0x44, 0x63, 0x59, 0x48, 0x51, 0x0D, 0x54, 0x5E, 0x58, 0x55, 0x58, 0xAD, 0x82, 0xAF, 0xDC, 0xE7, 0xAB, 0x58, 0x5D, 0xCE, 0xC1]
flag = ''
for i in range(len(b)):
    flag += chr(a[i] ^ b[i])
    print(flag)

…直接出了,我想的太复杂了,不应该想这么复杂的


原文地址:https://blog.csdn.net/weixin_59166557/article/details/144053555

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!