抽象RC4

  1. 抽象RC4

抽象RC4

一道很逆天的题目…

ida直接分析

int __fastcall main(int argc, const char **argv, const char **envp)
{
  unsigned int Seed; // eax
  int v5[2]; // [rsp+2Ch] [rbp-14h] BYREF
  unsigned int n0x2F; // [rsp+34h] [rbp-Ch]
  int n3; // [rsp+38h] [rbp-8h]
  int n15; // [rsp+3Ch] [rbp-4h]

  _main(argc, argv, envp);
  n15 = 15;
  Seed = time(0i64);
  srand(Seed);
  puts("You want my treasure? You can have it all if you want it. Go find it! I hid the treasure chest in the program");
  puts("Number of remaining steps: 15 | 1 to 3 steps per round | The first one to get to the KEY\n");
  v5[1] = 0;
  while ( n15 > 0 )
  {
    printf("Remaining steps: [%d]\n", (unsigned int)n15);
    do
    {
      printf("Number of steps (1-3): ");
      while ( scanf("%d", v5) != 1 )
      {
        printf("Input error, please re-enter: ");
        while ( getchar() != 10 )
          ;
      }
      if ( v5[0] > 0 && v5[0] <= 3 )
      {
        if ( n15 < v5[0] )
          printf(aNotEnoughSteps, (unsigned int)n15);
      }
      else
      {
        puts(aMustTake13Step);
      }
    }
    while ( v5[0] <= 0 || v5[0] > 3 || n15 < v5[0] );
    n15 -= v5[0];
    printf("* Player walks %d steps, remaining %d\n\n", (unsigned int)v5[0], (unsigned int)n15);
    if ( n15 <= 0 )
    {
      full_decrypt();
      puts("This is your KEY. You deserve it ! !");
      for ( n0x2F = 0; n0x2F <= 0x2F; ++n0x2F )
        printf("%02X", real_key[n0x2F]);
      break;
    }
    n3 = calculate_best_move((unsigned int)n15);
    if ( n3 <= 0 || n3 > 3 )
      n3 = 1;
    if ( n3 > n15 )
      n3 = n15;
    n15 -= n3;
    printf("* The AI takes %d steps, remaining %d\n\n", (unsigned int)n3, (unsigned int)n15);
    if ( n15 <= 0 )
      puts("This treasure does not belong to you. Go back, Boy");
  }
  printf("\nGame over ! ");
  system("pause");
  return 0;
}

就是一个类似于之前TPCTF2025的一道题,”搬石头”,可以直接和”AI”对弈,第一步走”3”,然后后面走的每一步都是(4 - x),x为上一回合中”AI”的步数,然后直接拿到key

image-20250426221230130

一开始拿到key不知道有什么用,继续分析程序。在函数窗口看到有rc4_init,rc4_next

image-20250426221328189

继续分析

rc4_init

__int64 __fastcall rc4_init(__int64 a1, __int64 a2, unsigned __int64 a3)
{
  __int64 result; // rax
  char v4; // [rsp+2h] [rbp-Eh]
  int n255_1; // [rsp+4h] [rbp-Ch]
  int v6; // [rsp+8h] [rbp-8h]
  int n255; // [rsp+Ch] [rbp-4h]

  for ( n255 = 0; n255 <= 255; ++n255 )
    *(_BYTE *)(a1 + n255) = n255;
  v6 = 0;
  for ( n255_1 = 0; n255_1 <= 255; ++n255_1 )
  {
    v6 = ((*(_BYTE *)(n255_1 % a3 + a2) ^ 0x37) + *(unsigned __int8 *)(a1 + n255_1) + v6) % 256;
    v4 = *(_BYTE *)(a1 + n255_1);
    *(_BYTE *)(a1 + n255_1) = *(_BYTE *)(a1 + v6);
    *(_BYTE *)(a1 + v6) = v4;
  }
  *(_DWORD *)(a1 + 260) = 0;
  result = a1;
  *(_DWORD *)(a1 + 256) = *(_DWORD *)(a1 + 260);
  return result;
}

rc4_next

__int64 __fastcall rc4_next(__int64 a1)
{
  unsigned __int8 v2; // [rsp+Eh] [rbp-2h]
  char v3; // [rsp+Fh] [rbp-1h]

  *(_DWORD *)(a1 + 256) = (*(_DWORD *)(a1 + 256) + 1) % 256;
  *(_DWORD *)(a1 + 260) = (*(_DWORD *)(a1 + 260) + *(unsigned __int8 *)(a1 + *(int *)(a1 + 256))) % 256;
  v3 = *(_BYTE *)(a1 + *(int *)(a1 + 256));
  *(_BYTE *)(a1 + *(int *)(a1 + 256)) = *(_BYTE *)(a1 + *(int *)(a1 + 260));
  *(_BYTE *)(a1 + *(int *)(a1 + 260)) = v3;
  v2 = *(_BYTE *)(a1 + (unsigned __int8)(*(_BYTE *)(a1 + *(int *)(a1 + 256)) + *(_BYTE *)(a1 + *(int *)(a1 + 260))));
  return (16 * v2) | (unsigned int)(v2 >> 4);
}

但是这两函数不被任何函数调用,但是我们在分析full_decrypt()函数时,在查看encrypted_key数组数据时看到一串可疑的数据

image-20250426221535815

于是就尝试将这串数据作为密文,程序输出的key作为密钥按照程序中rc4的逻辑进行解密,这道题抽象的地方就在于,明明在main函数中看到输出key的格式化是十六进制形式,但是真正在解密的时候却是字符串,逆天…

由于程序的rc4进行了魔改,直接dump下伪代码进行解密

#include <stdio.h>

#define _BYTE unsigned char
#define _DWORD unsigned int
#define _QWORD unsigned long

unsigned char data[48] = {
    0xBB, 0xCA, 0x12, 0x14, 0xD0, 0xF1, 0x99, 0xA7, 0x91, 0x48, 0xC3, 0x28, 0x73, 0xAD, 0xB7, 0x75,
    0x8C, 0x89, 0xCD, 0xDD, 0x2D, 0x50, 0x5D, 0x7F, 0x95, 0xB1, 0xA4, 0x9D, 0x09, 0x43, 0xE1, 0xD2,
    0xE9, 0x66, 0xEA, 0x18, 0x98, 0xC6, 0xCC, 0x02, 0x39, 0x18
};

__int64 __fastcall rc4_init(__int64 a1, __int64 a2, unsigned __int64 a3)
{
    __int64 result; // rax
    char v4; // [rsp+2h] [rbp-Eh]
    int j; // [rsp+4h] [rbp-Ch]
    int v6; // [rsp+8h] [rbp-8h]
    int i; // [rsp+Ch] [rbp-4h]

    for (i = 0; i <= 255; ++i)
        *(_BYTE*)(a1 + i) = i;
    v6 = 0;
    for (j = 0; j <= 255; ++j)
    {
        v6 = ((*(_BYTE*)(j % a3 + a2) ^ 0x37) + *(unsigned __int8*)(a1 + j) + v6) % 256;
        v4 = *(_BYTE*)(a1 + j);
        *(_BYTE*)(a1 + j) = *(_BYTE*)(a1 + v6);
        
        *(_BYTE*)(a1 + v6) = v4;
        //printf("%d,",*(_BYTE*)(a1 + j));
    }

    *(_DWORD*)(a1 + 260) = 0;
    result = a1;
    *(_DWORD*)(a1 + 256) = *(_DWORD*)(a1 + 260);
    return result;
}

__int64 __fastcall rc4_next(__int64 a1)
{
    unsigned __int8 v2; // [rsp+Eh] [rbp-2h]
    char v3; // [rsp+Fh] [rbp-1h]

    *(_DWORD*)(a1 + 256) = (*(_DWORD*)(a1 + 256) + 1) % 256;
    *(_DWORD*)(a1 + 260) = (*(_DWORD*)(a1 + 260) + *(unsigned __int8*)(a1 + *(int*)(a1 + 256))) % 256;
    v3 = *(_BYTE*)(a1 + *(int*)(a1 + 256));
    *(_BYTE*)(a1 + *(int*)(a1 + 256)) = *(_BYTE*)(a1 + *(int*)(a1 + 260));
    *(_BYTE*)(a1 + *(int*)(a1 + 260)) = v3;
    v2 = *(_BYTE*)(a1 + (unsigned __int8)(*(_BYTE*)(a1 + *(int*)(a1 + 256)) + *(_BYTE*)(a1 + *(int*)(a1 + 260))));
    return (16 * v2) | (unsigned int)(v2 >> 4);
}

unsigned char key[0x200];

int main(){
    unsigned char real_key[] = "EC3700DFCD4F364EC54B19C5E7E26DEF6A25087C4FCDF4F8507A40A9019E3B48BD70129D0141A5B8F089F280F4BE6CCD";
    rc4_init((long long)key, (long long)real_key, sizeof(real_key)-1);

    for (int i = 0; i < 42; i++) {
        data[i] ^= rc4_next((long long)key);
    }
    printf("%s", data);
    return 0;
}

flag{8263b6c6-094d-4bd8-bbc2-b63ab34e8db7}


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 1621925986@qq.com

💰

×

Help us with donation