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【汇编代码还原】的更多相关文章

  1. PC逆向之代码还原技术,第五讲汇编中乘法的代码还原

    目录 PC逆向之代码还原技术,第五讲汇编中乘法的代码还原 一丶简介乘法指令 1.乘法指令 2.代码还原注意问题 二丶乘法的汇编代码产生的格式 1.高级代码观看 2.乘法的汇编代码还原. 三丶乘法总结 ...

  2. PC逆向之代码还原技术,第六讲汇编中除法代码还原以及原理第二讲,被除数是正数 除数非2的幂

    目录 一丶简介 二丶代码还原讲解 1.被除数无符号 除数非2的幂 2.被除数无符号 除数为特例7 三丶代码还原总结 一丶简介 上一篇博客说的除2的幂. 如果被除数是有符号的,那么会进行调整,并使用位操 ...

  3. PC逆向之代码还原技术,第四讲汇编中减法的代码还原

    目录 PC逆向之代码还原技术,第四讲汇编中减法的代码还原 一丶汇编简介 二丶高级代码对应汇编观看. 1.代码还原解析: 三丶根据高级代码IDA反汇编的完整代码 四丶知识总结 PC逆向之代码还原技术,第 ...

  4. PC逆向之代码还原技术,第三讲汇编中加法的代码还原

    目录 PC逆向之代码还原技术,第三讲汇编中加法的代码还原 一丶汇编简介 二丶高级代码对应汇编观看. 1.代码还原解析: 总结 PC逆向之代码还原技术,第三讲汇编中加法的代码还原 一丶汇编简介 在讲解加 ...

  5. linux内核分析作业:以一简单C程序为例,分析汇编代码理解计算机如何工作

    一.实验 使用gcc –S –o main.s main.c -m32 命令编译成汇编代码,如下代码中的数字请自行修改以防与他人雷同 int g(int x) { return x + 3; } in ...

  6. GCC 嵌入汇编代码

    The format of basic inline assembly is very much straight forward. Its basic form is 基本汇编嵌入格式如下: asm ...

  7. PC逆向之代码还原技术,第一讲基本数据类型在内存中的表现形式.浮点,指针寻址公式

    目录 代码还原技术 一丶简介代码还原 二丶代码还原中的数据类型表现形式 1.整数类型 2.无符号整数 3.有符号整数 4.浮点数数据类型 5.浮点编码 4.Double类型解析. 三丶浮点汇编 1.浮 ...

  8. RPC远程过程调用学习之路(一):用最原始代码还原PRC框架

    RPC: Remote Procedure Call 远程过程调用,即业务的具体实现不是在自己系统中,需要从其他系统中进行调用实现,所以在系统间进行数据交互时经常使用. rpc的实现方式有很多,可以通 ...

  9. linux内核分析作业4:使用库函数API和C代码中嵌入汇编代码两种方式使用同一个系统调用

    系统调用:库函数封装了系统调用,通过库函数和系统调用打交道 用户态:低级别执行状态,代码的掌控范围会受到限制. 内核态:高执行级别,代码可移植性特权指令,访问任意物理地址 为什么划分级别:如果全部特权 ...

  10. 简单C程序生成的汇编代码分析

    首先给出完整的C代码: int g(int x) { ; } int f(int x) { return g(x); } int main(void) { )+; } 使用命令:gcc –S –o h ...

随机推荐

  1. Verilog实现定点乘法器

    实验目的 理解定点乘法的不同实现算法的原理,掌握基本实现算法. 熟悉并运用 Verilog 语言进行电路设计. 为后续设计 CPU 的实验打下基础. 实验内容 定点乘法器有多种实现,实验要求实现迭代乘 ...

  2. 基于间隔密度的概念漂移检测算法mdm-DDM

    概念漂移 ​ 概念漂移是数据流挖掘领域中一个重要的研究点.传统的机器学习算法在操作时通常假设数据是静态的,其数据分布不会随着时间发生变化.然而对于真实的数据流来说,由于数据流天生的时间性,到达的数据的 ...

  3. containerd镜像拉取配置

    背景: 公司要求部署最一套新版的k8s系统来部署生产应用,说实话很头疼.因为k8s自1.23版本之后就用不docker作为容器的默认运行时了,而是采用的containerd,这就带来了一系列的问题.没 ...

  4. 基于AvaSpe 2048测定物体的光谱曲线

      本文介绍基于AvaSpec-ULS2048x64光纤光谱仪测定植被.土壤等地物高光谱曲线的方法.   AvaSpec是由荷兰著名的光纤光谱仪器与系统开发公司Avantes制造的系列高性能光谱仪,广 ...

  5. 解决软件安装无法自定义文件夹,自动安装在C盘 (Windows系统)

    其实就是软链接的简单应用 1.软件已经自动安装 2.完全退出当前软件 3.通过软件图标的属性找到其实际的安装目录 4.进入该软件的安装目录 5.将该软件整个剪切(你没有看错)到指定文件夹(自定义的安装 ...

  6. 安信可开发环境构建-基于Ai-WB2系列 和 Ai-M61 或 Ai-M62 (环境上下文切换)

    首先,对于Ai-WB2系列环境的构建官方文档已经讲的非常明白了,这里不做阐述如下链接所示https://blog.csdn.net/Boantong_/article/details/12848091 ...

  7. windows开发环境备份,再也不怕重装系统了

    每次重装系统后,都要重新安装软件,配置环境变量,极为繁琐.故作环境环境变量备份,常用软件恢复记录,前提是你的软件要安装在非系统盘,D/E盘等 软件安装在非系统盘 开发软件安装在非系统盘,建好目录.重装 ...

  8. Gitlab仓库代码更新时Jenkins自动构建

    环境说明 1.Jenkins和gitlab已经都已经安装完毕 2.Jenkins能连接到gitlab获取项目并能手动创建项目 3.Jenkins和gitlab能相互访问的到(gitlab要能连接到Je ...

  9. camerabin error:"Internal data stream error,使用QT打开MIPI摄像头

    使用QT自带的QCamera打开MIPI摄像头 遇到:camerabin error:"Internal data stream error 降低分辨率为640*480 TRANSLATE ...

  10. Spring/SpringBoot中的声明式事务和编程式事务源码、区别、优缺点、适用场景、实战

    一.前言 在现代软件开发中,事务处理是必不可少的一部分.当多个操作需要作为一个整体来执行时,事务可以确保数据的完整性和一致性,并避免出现异常和错误情况.在SpringBoot框架中,我们可以使用声明式 ...