首页 > 编程语言 >CatFly【汇编代码还原】

CatFly【汇编代码还原】

时间:2023-12-22 14:26:16浏览次数:40  
标签:CatFly sub 汇编 int E1E8 flag 还原 printf dword

CatFly【难度:1】

题目界面

下载附件,发现是dll文件,放到linux中运行一下,运行界面如图所示:

从上图中可以看到两处字符串,上面的字符串不断滚动,下方字符串在次数上不断累加,猜测上方字符串与flag相关。

静态调试

  • 打开IDA,找到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(1LL);
        }
        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;
    

    因为flag可能与屏幕上滚动的字符串有关,所以此处需要格外关注printf函数。从上述关键代码中,可以看到off_FA88最可能与flag有关,因为其他的printf函数打印的基本是一个确定的值。

  • 查看目标的交叉引用路径

    确定了off_FA88后,开始查找与其相关的函数。

    • 第一处是469行中的对off_FA88的赋值操作

      off_FA88的值是sub_6314()函数的返回值。

    • 接下来进入sub_6314()函数

      函数伪代码如下图所示:

      其功能为:当a2!=18,a3≤4||a3>54时off_FA88不变(v12的值就是off_FA88),所以此处相当于是k=18(一次)且m∈(4,53)时会执行sub_6314函数,即循环执行50次。该函数的返回值是byte_104C8的地址,byte_104C8的值与dword_E120[a3-5],dword_E120[a3-5]的值又与sub_62B5()函数的返回值有关。这里还有个sub_62E3作为逻辑判断的条件。将此处逻辑进行还原,还原代码如下:

      for (int i = 0; i < 50; i++) {
           dword_E120[i] ^= sub_62B5();
       }
      
    • 查看sub_62B5()函数

      函数伪代码如下图所示:

      可以看到该函数的返回值与dword_E1E8相关,查看dword_E1E8的交叉引用,可以看到只在main函数中有一次自增操作:

    • 总结整个流程

  • 还原关键汇编代码

    此处使用大佬wp给的代码进行分析,基本都给了解析:

    #include<stdio.h>
    #include<string.h>
    //在ida里面点开dword_E1E8会发现初始值为0x1106
    int dword_E1E8 = 0x1106;
    //同理,ida里也能看到dword_E120的初始值
    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 };
    //原封不动抄下来就好了
    int sub_62B5()
    {
        dword_E1E8 = 1103515245 * dword_E1E8 + 12345;
        return (dword_E1E8 >> 10) & 0x7FFF;
    }
    //这块是拿来算输出数字n需要多少个字符的。
    //因为main函数中的dword_E1E8需要接收printf函数的返回值
    //而printf函数的返回值是打印的字符长度
    int llog(int n) {
        int a = 0;
        while (n /= 10)a++;
        return a;
    }
    //这个函数我没有特别放出来,反正这里也是照抄的
    int sub_62E3(char a1)
    {
        int result; // rax
    
        if ((a1 & 0x7Fu) <= 0x7E)
            result = (a1 & 0x7Fu) > 0x20;
        else
            result = 0LL;
        return result;
    }
    
    int main() {
        //count代表那个不停自增的dword_108E0
        int count = 0;
        while (1) {
            for (int i = 0; i < 50; i++) {
                dword_E120[i] ^= sub_62B5();
            }
            count++;
            //计算printf的返回值,更改dword_E1E8,每10倍增加一个位数
            dword_E1E8 += 42 + llog(count);
            if (count % 1000000 == 0) {
                printf("Count:%d\n", count);
            }
            //flag代表off_FA88
            unsigned char flag[51] = { 0 };
            for (int i = 0; i < 50; i++) {
                //根据出题人所说,出题时循环次数为705980581,但是线性同余随机数算法出现了循环导致在100427942就出现了flag,若只考虑数组的最低字节,能在100001958得到flag
                // Loop: 100427942
                // if((dword_E120[i] & 0xff00)){
                //     break;
                // }
                // Loop: 100001958
                if (!sub_62E3(dword_E120[i])) {
                    break;
                }
                flag[i] = dword_E120[i] & 0xff;
            }
            if (memcmp("CatCTF", flag, 6) == 0) {
                puts(flag);
                printf("Count:%d\n", count);
                break;
            }
        }
    }
    
  • 运行代码,得到flag

标签:CatFly,sub,汇编,int,E1E8,flag,还原,printf,dword
From: https://www.cnblogs.com/yuanyy/p/17921456.html

相关文章

  • clion,rustrover,gdb,lldb设置调试汇编语法格式
    通过修改GDB的配置来改变显示的汇编代码的格式在用户目录(C:\Users\你的用户名)下创建一个.gdbinit文件,然后在该文件中添加以下内容:setdisassembly-flavorintel这行命令会将GDB的汇编指令格式设置为Intel格式如果你在调试过程中使用的是LLDB,你可以在.lldbinit文件中添加以下......
  • C和汇编混合编译
    有时候在写C语言项目的时候会需要用到汇编代码直接操作寄存器、栈之类更加底层的东西,所以在这里写一下C和汇编混合编程的几种方法(Windows和Linux需要分开讨论)WindowsVisualStudioWindows下常用的开发环境是VisualStudio,对于x86来说vs默认支持内联汇编,直接通过__asm关键词即可......
  • 使用汇编和反汇编引擎写一个x86任意地址hook
    最简单的Hook刚开始学的时候,用的hook都是最基础的5字节hook,也不会使用hook框架,hook流程如下:构建一个jmp指令跳转到你的函数(函数需定义为裸函数)保存被hook地址的至少5字节机器码,然后写入构建的jmp指令接着在你的代码里做你想要的操作以内联汇编的形式执行被hook地址5字节机......
  • 【反汇编3】基本数据类型的表现形式
    参考书籍,《C++反汇编与逆向分析技术揭秘》。 这次主要研究各种数据在计算机里怎么存的,又要涉及补码、科学计数法等基础内容。这些课程计算机专业的都会学,但作为程序员未必有直观的体验,比如java或python程序员,他们不用自己管理内存,也就根本不会接触到这类内容,例如inti=-1;对......
  • 通过反汇编理解GCC优化以及inline函数的功能
    在linux环境写下以下C代码:首先不加优化选项去编译:gcc-ginline_func_test.c-oinline_func_test之后用objdump-S反汇编可见:可见,即使f1是inline函数,还是和f2一样被调用了六次。之后加入优化选项去编译gcc-O1-ginline_func_test.c-oinline_func_test这一次,f2依然被......
  • 《环太平洋》流浪者机甲3D模型特效还原
    在线工具推荐:3D数字孪生场景编辑器 - GLTF/GLB材质纹理编辑器 - 3D模型在线转换 - Three.jsAI自动纹理开发包 - YOLO虚幻合成数据生成器 - 三维模型预览图生成器 - 3D模型语义搜索引擎法线贴图在3D建模中扮演着重要的角色,它通过模拟表面的微小细节和凹凸不平的部分......
  • linux还原uid
    原系统中磁盘的/dev/sda4挂载到了/home路径下。重装系统并创建之前的用户之后,该用户对自己家目录竟然没有权限。原因是用户的uid前后不一致。可通过还原uid解决。#查看自己现在的uid1005idhgdai#查看自己家目录所属的uid1001ls-ld/home/hgdai#查看当前系统中谁用了......
  • 汇编-条件循环
      loop循环指令       ......
  • 汇编-JGE有符号大于等于则跳转
      .386.modelflat,stdcalloptioncasemap:none.stack4096IncludeIrvine32.incincludelibIrvine32.libExitProcessPROTO,dwExitCode:DWORD.data.codemainPROCmoveax,+100cmpeax,+10jgelabel0;有符号大于......
  • 汇编-JNGE有符号小于则跳转
     .386.modelflat,stdcalloptioncasemap:none.stack4096IncludeIrvine32.incincludelibIrvine32.libExitProcessPROTO,dwExitCode:DWORD.data.codemainPROCmoveax,+10cmpeax,+100jngelabel0;有符号小于则......