攻防世界-RE-CatFly


我们将文件拖入虚拟机中运行看到这样的效果

其中上方的数字是不停变化的,下面的次数也在不断的增长。我们猜测这两者是有关联的。

接下来我们进行反编译程序的分析。最上面的字符输出肯定是与printf函数有关,所以我们检索printf在main函数中的调用

 time(&timer);
  v13 = 1;
  v24 = 0LL;
  v23 = 0;
  v22 = 0;
  v12 = off_FA88;
  while ( v13 )
  {
    if ( dword_E104 )
      printf("\x1B[H");
    else
      printf("\x1B[u");
    for ( k = dword_E1EC; k < dword_E1F0; ++k )
    {
      for ( m = dword_E1F4; m < dword_E1F8; ++m )
      {
        if ( k <= 23 || k > 42 || m >= 0 )
        {
          if ( m >= 0 && (unsigned int)k <= 0x3F && m <= 63 )
          {
            v19 = off_FA20[v24][k][m];
            off_FA88 = sub_6314((unsigned int)v24, k, m, (__int64)v12);
          }
          else
          {
            v19 = 44;
          }
        }
        else
        {
          v18 = (2 - m) % 16 / 8;
          if ( ((v24 >> 1) & 1) != 0 )
            v18 = 1 - v18;
          s[128] = (__int64)",,>>&&&+++###==;;;,,";
          v19 = asc_BFE3[v18 - 23 + k];
          if ( !v19 )
            v19 = 44;
        }
        if ( v25 )
        {
          printf("%s", *((const char **)&unk_FCC0 + v19));
        }
        else if ( v19 == v22 || !*((_QWORD *)&unk_FCC0 + v19) )
        {
          printf("%s", off_FA88);
        }
        else
        {
          v22 = v19;
          printf("%s%s", *((const char **)&unk_FCC0 + v19), off_FA88);
        }
      }
      sub_65E2(1);
    }
    if ( dword_E100 )
    {
      time(&time1);
      v11 = difftime(time1, timer);
      v10 = sub_63FF((unsigned int)(int)v11);
      for ( n = (dword_E1FC - 29 - v10) / 2; n > 0; --n )
        putchar(32);
      dword_E1E8 += printf("\x1B[1;37mYou have nyaned for %d times!\x1B[J\x1B[0m", (unsigned int)++dword_108E0);
    }
    v22 = 0;
    ++v23;
    if ( dword_104C4 && v23 == dword_104C4 )
      sub_6471();
    if ( !off_FA20[++v24] )
      v24 = 0LL;
    usleep(1000 * v27);
  }
  return 0LL;
}

我们注意到这一部分,printf("%s", off_FA88);这里我们进一步跟进 off_FA88
看到了对它的赋值操作 off_FA88 = sub_6314((unsigned int)v24, k, m, (__int64)v12);
此时我们查看sub_6314的源代码

char *__fastcall sub_6314(__int64 a1, int a2, int a3, __int64 a4)
{
  if ( a2 != 18 )
    return (char *)a4;
  if ( a3 <= 4 || a3 > 54 )
    return (char *)a4;
  byte_104C9 = 32;
  dword_E120[a3 - 5] ^= sub_62B5();
  if ( (unsigned __int8)sub_62E3(dword_E120[a3 - 5]) )
    byte_104C8 = dword_E120[a3 - 5] & 0x7F;
  else
    byte_104C8 = 32;
  return &byte_104C8;
}

这里的a4实际上就是off_FA88的值,我们通过分析可以把这个函数理解为以下的函数

for (int i = 0; i < 50; ++i) {
    dword_E120[i] ^= sub_62B5();                   // 对数组的每个元素进行异或操作
    if ((unsigned __int8)sub_62E3(dword_E120[i]))  // 调用 sub_62E3 进行判断
        flag[i] = dword_E120[i] & 0x7F;            // 如果条件成立,设置 flag[i] 为 dword_E120[i] & 0x7F
    else
        flag[i] = 32;                              // 否则,设置 flag[i] 为 32(空格字符)
}

也就是说接下来我们需要去进一步跟进 sub_62B5sub_62E3的内容

__int64 sub_62B5()
{
  dword_E1E8 = 1103515245 * dword_E1E8 + 12345;
  return (dword_E1E8 >> 10) & 0x7FFF;
}

_BOOL8 __fastcall sub_62E3(char a1)
{
  return (a1 & 0x7Fu) <= 0x7E && (a1 & 0x7Fu) > 0x20;
}

这些都是做判断和计算用的函数,不用逆向,只需要直接调用即可
现在我们对flag的逆向加密逻辑完成了,我们需要去注意使用的参数,比如dword_E1E8 dword_E120,其中dword_E120可以直接提取出来,但是dword_E1E8 则需要计算得到,而且他在程序中还有自增操作

dword_E1E8 += printf("\x1B[1;37mYou have nyaned for %d times!\x1B[J\x1B[0m", (unsigned int)++dword_108E0);

每次他都会加上一个printf的返回值,由于调用printf返回是一件很麻烦的事情,所以我们手动计算它的返回值
综上我们可以写出以下解密脚本

#include<stdio.h>
#include<string.h>
#include<stdbool.h>

int flag[50];
//可以在IDA中得到
int dword_E1E8 = 0x1106;
int dword_E120[50] = { 0x27fb, 0x27a4, 0x464e, 0x0e36, 0x7b70, 0x5e7a, 0x1a4a, 0x45c1, 0x2bdf, 0x23bd, 0x3a15, 0x5b83, 0x1e15, 0x5367, 0x50b8, 0x20ca, 0x41f5, 0x57d1, 0x7750, 0x2adf, 0x11f8, 0x09bb, 0x5724, 0x7374, 0x3ce6, 0x646e, 0x010c, 0x6e10, 0x64f4, 0x3263, 0x3137, 0x00b8, 0x229c, 0x7bcd, 0x73bd, 0x480c, 0x14db, 0x68b9, 0x5c8a, 0x1b61, 0x6c59, 0x5707, 0x09e6, 0x1fb9, 0x2ad3, 0x76d4, 0x3113, 0x7c7e, 0x11e0, 0x6c70 };


bool __fastcall sub_62E3(char a1)
{
    return (a1 & 0x7Fu) <= 0x7E && (a1 & 0x7Fu) > 0x20;
}
__int64 sub_62B5()
{
    dword_E1E8 = 1103515245 * dword_E1E8 + 12345;
    return (dword_E1E8 >> 10) & 0x7FFF;
}
int charnum(unsigned int a){
    int c = 0;
    while(a != 0){
        a/=10;
        c+=1;
    }
    return c;
}

int main(){
    unsigned int dword_108E0 = 0;
    while (1) {
        char flag[50];
        for (int i = 0; (int)i < 50; ++i) {
            dword_E120[i] ^= sub_62B5();
            if ((unsigned __int8)sub_62E3(dword_E120[i]))
                flag[i] = dword_E120[i] & 0x7F;
            else
                flag[i] = 32;
        }
        if (!strncmp(flag, "CatCTF", 6)) {
            printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
            puts(flag);
            printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n %d" ,dword_108E0);
            break;
        }
        dword_108E0 += 1;
        dword_E1E8 += 41;
        dword_E1E8 += charnum(dword_108E0);
        if(dword_108E0%1000000==0)
            printf("%d\n",dword_108E0);
    }
    return 0;
}

太抽象了,这真的是难度一吗?

总结:
明确flag是哪个参数,不断跟进,逆向加密逻辑,拿到解密数据,不要放弃,保持耐心与冷静

posted @   Ylin07  阅读(8)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示