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. 好好回答下 TCP 和 UDP 的区别!

    写了这么多篇关于 TCP 和 UDP 的文章,还没有好好聊过这两个协议的区别,这篇文章我们就来开诚布公的谈一谈. 关于 TCP 和 UDP ,想必大家都看过一张这样的图. 有一个小姑娘在对着瓶口慢慢的 ...

  2. redhat7 team bonding 双网卡绑定 主备 负载均衡

    team简介 team也被称为网络组,是将多个网卡聚合在一起,从而实现冗错和提高吞吐量.适用于redhat7.0以上版本,至多可支持8块网卡.team相对于之前的bonding技术,能提供更好的性能和 ...

  3. LeetCode买卖股票之一:基本套路(122)

    欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 关于<LeetCode买卖股票>系列 在L ...

  4. 解决Nginx SSL 代理 Tomcat 获取 Scheme 总是 Http 问题

    背景 公司之前用的是http,但是出于苹果app审核和服务器安全性问题,要改为https,我们公司用的是沃通的ssl,按照沃通的官方文档提供的步骤完成服务器的配置. 架构上使用了 Nginx +tom ...

  5. JS深入学习笔记 - 第三章.变量作用域与内存

    1.原始值和引用值 ECMScript变量包含两种不同类型是数据:原始值和引用值. 原始值:最简单的数据.有6中原始值:Undefined.Null.Boolean.Number.String和Sym ...

  6. 算法打卡|Day1 数组part01

    Day1 数组part01 今日任务:数组理论基础,704. 二分查找,27. 移除元素 目录 Day1 数组part01 今日任务:数组理论基础,704. 二分查找,27. 移除元素 Part1: ...

  7. #POWERBI_指标监控(第二部分,周期内下降天数及日期明细)

    在指标监控的第一部分文章中,我们已经讲了,如何用DAX去查询一段周期内连续下降或者上升指标. 需要复习的同学可以点击下方链接: https://www.cnblogs.com/simone331/p/ ...

  8. Spring-Boot-Starter 学习笔记(1)

    Spring-Boot-Starter 1. 准备配置类和 Bean 对象 Spring Boot 提供了两个注解: @Configuration:Spring 提供的配置类注解,作用在类上,代表整个 ...

  9. 15. 从零开始编写一个类nginx工具, 如果将nginx.conf转成yaml,toml,json会怎么样

    wmproxy wmproxy将用Rust实现http/https代理, socks5代理, 反向代理, 静态文件服务器,后续将实现websocket代理, 内外网穿透等, 会将实现过程分享出来, 感 ...

  10. 【Unity3D】资源管理

    1 前言 ​ Unity 中资源管理方案主要有 Resources.TextAsset.ScriptableObject .AssetDatabase.PlayerPrefs.Addressables ...