[网鼎杯 2020 青龙组]singal详细题解--VMP 直接逆向,angr模拟执行,ponce符号化

news2025/1/11 11:18:22

文章目录

  • 直接逆向
    • 提取opcode
    • 获取指令执行流
    • getflag
    • 注意
  • 使用Angr
  • 使用Ponce插件
    • 安装并配置Ponce
    • 具体操作
  • 参考资料

直接逆向

提取opcode

主函数并不复杂,关键内容在vm_opcode中,先提取出main函数中的opcode

unsigned int OpCode[114] = {
    0x0000000A, 0x00000004, 0x00000010, 0x00000008, 0x00000003, 0x00000005, 0x00000001, 0x00000004,
    0x00000020, 0x00000008, 0x00000005, 0x00000003, 0x00000001, 0x00000003, 0x00000002, 0x00000008,
    0x0000000B, 0x00000001, 0x0000000C, 0x00000008, 0x00000004, 0x00000004, 0x00000001, 0x00000005,
    0x00000003, 0x00000008, 0x00000003, 0x00000021, 0x00000001, 0x0000000B, 0x00000008, 0x0000000B,
    0x00000001, 0x00000004, 0x00000009, 0x00000008, 0x00000003, 0x00000020, 0x00000001, 0x00000002,
    0x00000051, 0x00000008, 0x00000004, 0x00000024, 0x00000001, 0x0000000C, 0x00000008, 0x0000000B,
    0x00000001, 0x00000005, 0x00000002, 0x00000008, 0x00000002, 0x00000025, 0x00000001, 0x00000002,
    0x00000036, 0x00000008, 0x00000004, 0x00000041, 0x00000001, 0x00000002, 0x00000020, 0x00000008,
    0x00000005, 0x00000001, 0x00000001, 0x00000005, 0x00000003, 0x00000008, 0x00000002, 0x00000025,
    0x00000001, 0x00000004, 0x00000009, 0x00000008, 0x00000003, 0x00000020, 0x00000001, 0x00000002,
    0x00000041, 0x00000008, 0x0000000C, 0x00000001, 0x00000007, 0x00000022, 0x00000007, 0x0000003F,
    0x00000007, 0x00000034, 0x00000007, 0x00000032, 0x00000007, 0x00000072, 0x00000007, 0x00000033,
    0x00000007, 0x00000018, 0x00000007, 0xFFFFFFA7, 0x00000007, 0x00000031, 0x00000007, 0xFFFFFFF1,
    0x00000007, 0x00000028, 0x00000007, 0xFFFFFF84, 0x00000007, 0xFFFFFFC1, 0x00000007, 0x0000001E,
    0x00000007, 0x0000007A
};

获取指令执行流

可以在每条指令处加上打印语句获取指令执行顺序,这个操作仅能大概看一下执行流,自动解密不能靠这个

在switch执行前打印i的值以获取opcode的执行流

注意case10的read要改成scanf,case7的比较可以改成赋值

int __cdecl vm_operad1(int* opcode, int len)
{
    int result; // eax
    unsigned char Str[100]; // [esp+13h] [ebp-E5h] BYREF
    unsigned char buffer[100]; // [esp+77h] [ebp-81h] BYREF
    unsigned char chr; // [esp+DBh] [ebp-1Dh]
    int z; // [esp+DCh] [ebp-1Ch]
    int x; // [esp+E0h] [ebp-18h]
    int j; // [esp+E4h] [ebp-14h]
    int y; // [esp+E8h] [ebp-10h]
    int i; // [esp+ECh] [ebp-Ch]

    i = 0;
    y = 0;
    j = 0;
    x = 0;
    z = 0;
    int count = 0;
    while (1)
    {
        result = i;
        if (i >= len)
            return result;
        printf("%d,", i);//打印程序执行流
        switch (opcode[i])
        {
           
        case 1:
            //printf("x=%d,y=%d\n", x, y);
            buffer[x] = chr;
            ++i;
            ++x;
            ++y;                                    // 只有这条语句修改了v8
            break;
        case 2:
            chr = opcode[i + 1] + Str[y];
            //printf("data[%d]=%d;\n",count++, opcode[i + 1]);
            i += 2;
            break;
        case 3:
            chr = Str[y] - LOBYTE(opcode[i + 1]);
            //printf("data[%d]=%d;\n", count++, opcode[i + 1]);

            i += 2;
            break;
        case 4:
            chr = opcode[i + 1] ^ Str[y];
            //printf("data[%d]=%d;\n", count++, opcode[i + 1]);

            i += 2;
            break;
        case 5:
            chr = opcode[i + 1] * Str[y];
            //printf("data[%d]=%d;\n", count++, opcode[i + 1]);

            i += 2;
            break;
        case 6:
            ++i;
            break;
        case 7:                                   // v7只在case7中使用到
            buffer[j] = opcode[i + 1];
            //printf("cmpdata[%d]=%d;\n", j, buffer[j]);
            //if (buffer[j] != opcode[i + 1])       // 出现了15次7,所以flag应该是15字符
            //{
            //    printf("what a shame...");
            //    exit(0);
            //}
            ++j;
            i += 2;
            break;
        case 8:
            //printf("z=%d\n", z);
            Str[z] = chr;
            ++i;
            ++z;
            break;
        case 10:                                  // 由于arr[0]==10,所以第一次循环执行的必定是read,所以第一次就要输入Str
            scanf("%15s", Str);                              // strlen=15
            ++i;                                    // 只执行一次
            break;
        case 11:
            chr = Str[y] - 1;
            ++i;
            break;
        case 12:
            chr = Str[y] + 1;
            ++i;
            break;
        default:
            continue;
        }
        
    }
}

最终可以得到一个op数组存储执行流

unsigned int op[76] = {0,1,3,4,6,7,9,10,12,13,15,16,17,18,19,20,22,23,25,26,28,29,30,31,32,33,35,36,38,39,41,42,44,45,46,47,48,49,51,52,54,55,57,58,60,61,63,64,66,67,69,70,72,73,75,76,78,79,81,82,83,84,86,88,90,92,94,96,98,100,102,104,106,108,110,112 };

也可以打印case7的比较操作来获取最终用于比较的数据

unsigned char data[15] = { 0X22, 0X3F, 0X34, 0X32, 0X72, 0X33, 0X18, 0XFFFFFFA7, 0X31, 0XFFFFFFF1, 0X28, 0XFFFFFF84, 0XFFFFFFC1, 0X1E, 0X7A };

不获取这个data数组也没问题,因为数据保存在opcode数组中,只要执行流正确就可以

getflag

将每条case的指令修改为逆指令,倒序执行指令流即可得到flag

注意这里的几个数组最好用unsigned char,用char会导致一些错误

int __cdecl vm_operad(int* opcode, int len)
{
    unsigned char data[15] = { 0X22, 0X3F, 0X34, 0X32, 0X72, 0X33, 0X18, 0XFFFFFFA7, 0X31, 0XFFFFFFF1, 0X28, 0XFFFFFF84, 0XFFFFFFC1, 0X1E, 0X7A };
    unsigned char flag[100] = { 0 }; // [esp+13h] [ebp-E5h] BYREF
    unsigned char buffer[15] = {0 }; // [esp+77h] [ebp-81h] BYREF
    unsigned char chr=0; // [esp+DBh] [ebp-1Dh]
    int z; // [esp+DCh] [ebp-1Ch]
    int x; // [esp+E0h] [ebp-18h]
    int j; // [esp+E4h] [ebp-14h]
    int y; // [esp+E8h] [ebp-10h]
    int i; // [esp+ECh] [ebp-Ch]
    i = 0;//反向操作
    y = 15;
    j = 15;
    x = 15;
    z = 15;
    int k = 75;
    int count = 0;
    while (k>=0)
    {
        i = op[k];
        switch (opcode[i])
        {
        case 1:
            //这里调换一下顺序就可以得到结果
            --x;
            --y;
            printf("x=%d,y=%d\n", x, y);
            chr= buffer[x];//先给chr中间变量赋值,后续处理后再赋给flag[y],所以y=15,--y的形式是正确位置
            
            //如果y=14,--y那么后续第一个赋值的位置是flag[13]而不是flag[14]
            
                                   // 只有这条语句修改了v8
            break;
        case 2:
            printf("using y=%d\n", y);
             flag[y]=chr- opcode[i + 1];
            break;
        case 3:
            printf("using y=%d\n", y);
             flag[y]= chr  + opcode[i + 1];
            break;
        case 4:
            printf("using y=%d\n", y);
             flag[y]=(chr^ opcode[i+ 1]);
            break;
        case 5:
            printf("using y=%d\n", y);
            flag[y] = chr / opcode[i + 1];

            break;
        case 6:
            break;
        case 7:                                   // v7只在case7中使用到
            buffer[--j] = opcode[i + 1];
            break;
        case 8:
            z--;
            printf("z=%d\n", z);
            chr= flag[z] ;//这里也要调换顺序
            
            break;
        case 10:                                  // 由于arr[0]==10,所以第一次循环执行的必定是read,所以第一次就要输入Str
            printf("read\n");
           // printf("%s", flag);
            break;
        case 11:
            printf("using y=%d\n", y);
             flag[y]= chr  + 1;
            break;
        case 12:
            printf("using y=%d\n", y);
            flag[y]=chr-1;
            break;
        default:
            continue;
        }
        k--;
    }
    printf("flag{%s}", flag);
}

注意

值得一提的是这里xyzj最好是赋15,每次使用前自减

如果是赋值14,使用后自减,中间变量chr先赋值后,xyzj等的值变成13

导致后续flag[y]赋值时从flag[13]开始而非flag[14]开始

总而言之需要防止使用错误的下标

请添加图片描述

使用Angr

首先创建python虚拟环境安装angr,防止依赖冲突

注意尽量在linux环境使用,windows会出一些bug

在这里插入图片描述

pip install angr 即可安装

在这里插入图片描述

脚本

要将signal.exe放到和脚本同一文件夹内

import angr
project = angr.Project('signal.exe') 	#创建项目,加载二进制文件
state = project.factory.entry_state()	#创建state
sim = project.factory.simgr(state)		#创建sim
sim.explore(find=0x40175e,avoid=0x4016e6) # 希望到达的和避免的分支
if sim.found:
    res = sim.found[0]
    res = res.posix.dumps(0)
    print("[+] Success! Solution is: {}".format(res.decode("utf-8")))

结果

在这里插入图片描述

使用Ponce插件

安装并配置Ponce

github项目地址Ponce

下载对应系统最新版压缩包,解压后根据ida版本找到对应文件夹

将文件夹中的Ponce.dll和Ponce64.dll复制到ida的plugins文件夹中即可
在这里插入图片描述

使用Ponce

先设置config
在这里插入图片描述

设置如下即可

在这里插入图片描述

在read中下断点(便于后续符号化str,防止执行其他操作改变了str)

请添加图片描述

在case7处下断点(后续逐步获取flag就是根据这里)

请添加图片描述

具体操作

动态调试,先随便输入一个长度为15的字符串

在read处断下后找到字符串保存地址(这里是61FBC3),右键符号化字符串

请添加图片描述
请添加图片描述

按F9,运行到第二个断点,查看流程图,对着jz指令右键 SMT Solver>Negate and Inject to reach

请添加图片描述

之后会输出答案(第一个)

请添加图片描述

按f9再次到jz指令处,使用相同的操作最终可以得到flag

请添加图片描述

参考资料

  1. 2020网鼎杯青龙组部分逆向题

  2. IDA插件Ponce初体验

  3. IDA插件Ponce的使用

  4. [网鼎杯 2020 青龙组]singal

  5. 2020网鼎杯青龙组部分逆向题

  6. IDA插件Ponce初体验

  7. IDA插件Ponce的使用

  8. [网鼎杯 2020 青龙组]singal

  9. [re]符号执行一把梭:2020网鼎杯青龙组re_signal_wp

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1002641.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Spring上下文模块ApplicationContextAware

博主介绍:✌全网粉丝3W+,全栈开发工程师,从事多年软件开发,在大厂呆过。持有软件中级、六级等证书。可提供微服务项目搭建与毕业项目实战,博主也曾写过优秀论文,查重率极低,在这方面有丰富的经验✌ 博主作品:《Java项目案例》主要基于SpringBoot+MyBatis/MyBatis-plus+…

《数字图像处理-OpenCV/Python》连载(6)基于Matplotlib显示图像

《数字图像处理-OpenCV/Python》连载(6)基于Matplotlib显示图像 本书京东优惠购书链接:https://item.jd.com/14098452.html 本书CSDN独家连载专栏:https://blog.csdn.net/youcans/category_12418787.html 第1章 图像的基本操作 …

软件系统验收测试需要注意的地方

验收测试 一、软件验收测试含义: 软件验收测试是指测试人员检验软件是否符合软件规格说明书和用户需求的测试活动。 验收测试是软件测试的最后一个环节,也是最为关键的一个要素。 它关系到软件开发公司的产品质量,也关系到需求方的产品能…

二分查找[整数二分]

引例 不知道你有没有玩过猜数字游戏,在0到100之间随机选取一个数,让你猜是几,比如这个数是67,如果你猜了50,就会提示你小了,那么你就会去51到100之间猜,你猜了75,就会提示你大了,你就会去51到74之间去猜,就这样一直猜,数字可取的区间越来越小,用不了多久就可以猜出.当你每次都…

SpringBoot如何实现热部署

热部署是软件开发中一个非常有用的功能,它允许我们在不重新启动整个应用的情况下,依旧能够使我们修改的代码生效。 现在Java Web 开发应该都是使用的 SpringBoot,那么本篇文章就来介绍SpringBoot 如何实现热部署? 1、热部署的优点…

如何提取视频中的音频?几个步骤轻松提取

在现今社交媒体的风靡下,许多人都会使用手机录制视频来记录生活中的美好瞬间。有时候,我们也会想要提取视频中的音频,例如将自己的演讲录音分发给听众。本文将会介绍如何在手机上提取视频中的音频以及需要注意的事项。 使用应用程序 首先&am…

Linux Debian12使用git将本地项目上传到码云(gitee)远程仓库

一、注册码云gitee账号 这个可以参考其他教程,本文不做介绍。 gitee官网:https://gitee.com/ 二、Linux Debian12安装git 如果Linux系统没有安装git,可以使用下面命令安装git sudo apt install git 三、gitee新建仓库 我这只做测试&…

实现分别在Linux、Docker、Kubernetes上安装部署Mysql、Redis、Nginx软件

目录 实现目的: Linux上一键安装Mysql、Nginx、Redis软件 一键安装Mysql脚本 一键安装Redis脚本 一键安装Nginx脚本 docker上安装部署Mysql、Nginx、Redis容器 Kubernetes上安装部署Mysql、Nginx、Redis的Pod和通过Service发布 创建Pod生成容器 使用Servic…

时间序列论文-聚类和异常检测(二)

同样摘自知乎的回答:https://www.zhihu.com/question/29507442/answer/1212624591?utm_id0 正巧之前做过时间序列 的异常检测项目,这里介绍几种尝试过的方法,也算是抛砖引玉 吧,欢迎大家讨论交流~ 背景与定义 时间序列异常 检测…

c++实现数据结构栈和队列

1、栈 头文件 #ifndef ZHAN_H #define ZHAN_H#define MAX 8 #include <iostream> using namespace std;class Shu {int datatype; //入栈的数据int *arr; //栈的数组int top; //记录栈顶元素的下标public://构造函数Shu();//析构函数~Shu();//判断空int stack_empty…

COSCon'23 Call for Speakers

一年一度的开源盛会&#xff0c;COSCon23 第八届中国开源年会&#xff0c;将于10月28~29日&#xff0c;在四川成都市高新区菁蓉汇召开&#xff01; The yearly open source event, COSCon23 8th Annual China Open Source Conference, will be taken place on 28th~29th Octobe…

【编码魔法师系列_构建型1.1】简单工厂模式(Static Factory)

学会设计模式&#xff0c;你就可以像拥有魔法一样&#xff0c;在开发过程中解决一些复杂的问题。设计模式是由经验丰富的开发者们&#xff08;GoF&#xff09;凝聚出来的最佳实践&#xff0c;可以提高代码的可读性、可维护性和可重用性&#xff0c;从而让我们的开发效率更高。通…

让照片动起来的软件,轻松制作照片动效

随着社交媒体的日益普及&#xff0c;我们对于照片的要求也越来越高。普通的照片已经不能满足我们的需求&#xff0c;我们希望照片更加生动有趣。照片动效便应运而生&#xff0c;它可以让照片动起来&#xff0c;吸引更多的注意力&#xff0c;让照片更加生动有趣。 照片动效制作起…

软件验收测试

1. 服务流程 验收测试 2. 服务内容 测试过程中&#xff0c;根据合同要求制定测试方案&#xff0c;验证工程项目是否满足用户需求&#xff0c;软件质量特性是否达到系统的要求。 3. 周期 10-15个工作日 4. 报告用途 可作为进行地方、省级、国家、部委项目的验收&#xff0…

Java-华为真题-预定酒店

需求&#xff1a; 放暑假了&#xff0c;小王决定到某旅游景点游玩&#xff0c;他在网上搜索到了各种价位的酒店&#xff08;长度为n的数组A&#xff09;&#xff0c;他的心理价位是x元&#xff0c;请帮他筛选出k个最接近x元的酒店&#xff08;n>k>0&#xff09;&#xff…

JavaScript中的Generator函数及其使用方式

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ Generator函数⭐ 创建Generator函数⭐ 调用Generator函数⭐ Generator函数的应用1. 异步编程2. 生成器&#xff08;Generator&#xff09; ⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 记得点击上方或者右侧…

L1-002 打印沙漏分数 20

L1-002 打印沙漏 分数 20 全屏浏览题目 切换布局 作者 陈越 单位 浙江大学 本题要求你写个程序把给定的符号打印成沙漏的形状。例如给定17个“*”&#xff0c;要求按下列格式打印 ************ *****所谓“沙漏形状”&#xff0c;是指每行输出奇数个符号&#xff1b;各行符…

23年11月PMP考试如何报名?如何备考?(含备考干货)

总分为4个步骤&#xff1a;英文报名--审核【抽中后快递材料审核】--中文报名--在线支付费用 一、PMP英文报名&#xff1a; 英文报名时间无限制&#xff0c;但有一年的有效期&#xff0c;所以大家尽量提前报名 二、审核&#xff1a; PMI网站对你英文报名的材料进行审核&…

Java——》synchronized锁升级

推荐链接&#xff1a; 总结——》【Java】 总结——》【Mysql】 总结——》【Redis】 总结——》【Kafka】 总结——》【Spring】 总结——》【SpringBoot】 总结——》【MyBatis、MyBatis-Plus】 总结——》【Linux】 总结——》【MongoD…

Win11共享文件夹怎么设置

当我们在使用Win11的过程中有时会因为一些操作需要共享文件夹&#xff0c;那么Win11系统该如何设置共享文件夹呢&#xff0c;下面小编就给大家详细介绍一下Win11设置共享文件夹的方法&#xff0c;有需要的小伙伴快来和小编一起看看吧。 Win11设置共享文件夹的方法&#xff1a;…