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

2023-11-06

直接逆向

提取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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

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

  • 用 range() 以相反的顺序打印列表?

    如何生成以下列表range 在Python中 9 8 7 6 5 4 3 2 1 0 Use reversed 函数 高效 因为range实施 reversed reversed range 10 这更有意义 Update list cas
  • 反转 DOMNodeList 中项目的顺序

    你好 我正在制作 RSS 阅读器并且正在使用 DOM 现在我卡住了 试图反转 DOMNodeList 中项目的顺序 我可以用 2 个周期来完成 一个周期将其作为数组 另一个周期用于rsort 有没有办法反转 DOMNodeList 中的顺序
  • 通过 180 度翻转滚动列表反转滚动方向

    我正在构建一个离子 角度应用程序 并且我想在滚动输入上反转滚动方向 在这里你可以找到我的情况的一个例子 jsfiddle 示例 在上面的示例中 我将滚动列表翻转了 180 度 并将滚动列表内的 div 翻转了 180 度 我这样做是为了使消
  • 如何逆序读取文件?

    如何使用python以相反的顺序读取文件 我想从最后一行到第一行读取一个文件 以生成器形式编写的正确 高效的答案 import os def reverse readline filename buf size 8192 A generat
  • C++ 使用 for 循环反向打印字符串

    我有一个程序 使用 for 循环打印出字符串的字符 它还必须反向打印相同的字符 这就是我遇到问题的地方 有人可以帮我弄清楚为什么第二个 for 循环没有执行吗 int main string myAnimal cout lt lt Plea
  • 使用按位移位反转数字

    我正在尝试找到一种方法来反转数字without 将其转换为字符串以求长度 反转字符串并解析回来 运行单独的循环来计算长度 我目前正在这样做 public static int getReverse int num int revnum 0
  • Delphi 字节逆序

    我一直在尝试编写一个函数 它接受两个指针 一个输入和一个输出 并以相反的顺序将输入中的字节写入输出 到目前为止我还没能让它正常工作 procedure ReverseBytes Source Dest Pointer Size Intege
  • php通过序列号进行页面导航

    任何人都可以帮助这个 php 页面导航脚本打开计算正常序列号吗 在这个脚本中有一个名为 page id 的变量 我希望这个变量按0 1 2 3 4 5等顺序存储真实的页面链接
  • SQL查询输出的逆序

    我有一个无法解决的问题 我使用 PHP 通过我的数据库运行以下命令 strQuery select from LastResult ORDER BY Date DESC LIMIT 10 结果一切正常 符合预期 但是 然后我必须将它们输入折
  • 反转数组顺序

    我正在尝试反转 java 中数组的顺序 在 O n 内使用最少的内存来完成此操作的最有效方法是什么 不需要用代码回答 伪代码就可以了 这是我的思考过程 create a new temp array I think this is a wa
  • Django 跨站反向 URL

    可能是简单的问题 我只是错过了一些东西 但我没有想法 我有 Django 项目 为多个具有不同特性的站点提供服务sessions py并且完全不同ROOT URLCONFs 一个站点处理用户注册 身份验证和配置文件设置 其他站点 在另一个域
  • 反向重用 CSS 动画(通过重置状态?)

    我在 CSS 中使用了两个关键帧动画 一个从左向右移动 另一个使用完全相同的值 但方向相反 keyframes moveLeft from transform translate3d 50px 0 0 to transform transl
  • 在 Javascript 中反转数字而不使其成为字符串[重复]

    这个问题在这里已经有答案了 谁能告诉我我的代码哪里出错了 我正在尝试反转数字而不将其更改为字符串 我一直在搜索谷歌并浏览了之前提出的有关该主题的问题 从我可以看到我的代码反映了其他答案 我只能找到不使用 to string 方法的 Java
  • 在Java中反转数组[重复]

    这个问题在这里已经有答案了 如果我有一个像这样的数组 1 4 9 16 9 7 4 9 11 反转数组使其看起来像这样的最佳方法是什么 11 9 4 7 9 16 9 4 1 我有下面的代码 但我觉得有点乏味 public int reve
  • 通过 ArrayList 进行反向迭代会出现 IndexOutOfBoundsException

    当我反向迭代 ArrayList 时 我收到 IndexOutOfBoundsException 我尝试进行前向迭代 没有问题 我期望并知道列表中有五个元素 代码如下 Collection rtns absRtnMap values Lis
  • 如何在python3.2中以相反的顺序读取文件而不将整个文件读取到内存? [复制]

    这个问题在这里已经有答案了 我正在使用 python3 2 解析大小为 1 到 10GB 的日志文件 需要搜索具有特定正则表达式 某种时间戳 的行 并且我想找到最后一次出现的情况 我尝试过使用 for line in reversed li
  • 如何获取 Django Flatpages 模板的反向 url

    如何获取 Django Flatpages 模板的反向 url 我更喜欢以下解决方案 需要 Django gt 1 0 settings py INSTALLED APPS django contrib flatpages urls py
  • javascript中的(多维)数组中的反向条目

    我正在使用 leaflet js 在 openstreetmap 上显示一些多边形 我有一个外部数据资源 它为我提供了多边形的坐标 不幸的是 这个数组的坐标顺序错误 例子 我得到 10 5254913 52 2734311 10 52588
  • 在 django.core.urlresolvers reverse() 调用中包含查询字符串

    我正在尝试反转命名 URL 并在其中包含查询字符串 基本上我修改了登录功能 我想发送 next in it 这就是我现在正在做的事情 reverse name next reverse redirect 这是我想做的 reverse nam
  • 使用谷歌地图 API iOS 进行反向地理编码

    我正在使用以下代码进行反向地理编码 void locationManager CLLocationManager manager didUpdateToLocation CLLocation newLocation fromLocation

随机推荐

  • cookie session总结

    Cookie是由服务器创建 然后通过响应发送给客户端的一个键值对 客户端会保存Cookie 并会标注出Cookie的来源 哪个服务器的Cookie Cookie规范 Cookie通过请求头和响应头在服务器与客户端之间传输 Cookie大小上
  • 踩坑:git或gitee之上传超过100M文件

    直接说 如果你是免费用户 g远程仓库是gitee 那么对不起 你没法上传超过100M的大文件 不支持git fls 只有企业项目 才支持 如果你的远程仓库是git 那么借助git fls就可以了 至于怎么使用 网上一大堆博客 我就不浪费篇幅
  • hive 使用 jndi 数据源时已经在 Tomcat 中配置好 但是在 java 代码中获取数据源就会报错

    这个是异常信息 javax naming NoInitialContextException Need to specify class name in environment or system property or as an app
  • kafka系统的架构

    系统的架构 主题topic和分区partition topic Kafka中存储数据的逻辑分类 你可以理解为数据库中 表 的概念 比如 将app端日志 微信小程序端日志 业务库订单表数据分别放入不同的topic partition分区 提升
  • 数值分析 第一章:绪论

    第一章 绪论 1 2误差基础知识 1 2 1误差来源 1 2 2误差度量 1 2 3初值误差传播 1 3 舍入误差分析及数值稳定性 1 2误差基础知识 1 2 1误差来源 数学模型与实际问题的差异称为模型误差 数学模型中常常还包含有一些参数
  • 一起学SF框架系列附-Springframework源码学习总结

    学习过程 学习Springframework6 0 8 前后将近4个月终于结束了 学习主要内容如图 红框 本次学习主要针对核心模块 Beans Context Core SpEL 完全独立于框架的 没深入学习 AOP 以SF应用的初始化过程
  • nginx开启gzip压缩功能遇到的坑

    nginx开启gzip压缩功能一大堆 网上大多数配置如下 server listen 8080 proxy http version 1 1 gzip on gzip min length 1k gzip buffers 4 16k gzi
  • tf.reduce_sum tensorflow维度上的操作

    tensorflow中有很多在维度上的操作 本例以常用的tf reduce sum进行说明 官方给的api reduce sum input tensor axis None keep dims False name None reduct
  • 闲谈IPv6-IPv6地址的scope到底是什么?

    周日 大早上六点多和疯子去菜市场买了菜 顺便打了一壶糯米烧酒 回来把我的正则安哥哄睡了之后 继续思考IPv6的细节 一台主机启动后 每一块网卡都会自动生成一个fe80打头的 链路本地地址 这个地址在Linux上你删都删不掉 不信你试试 在W
  • Notepad++找回未保存的文件(缓存)

    Notepad 找回未保存的文件 缓存 就吃晚饭的功夫 电脑重启了 然鹅我在Notepad 里面写的东西还没保存 当场石化 还好挽救回来了 以后一定要记得Ctrl S 参考链接 Notepad 找回自动保存缓存内容的文件
  • 小米画报的壁纸怎么保存_小米怎么保存不生虫?掌握方法,安心存放随时吃,方法简单很实用...

    小米在古时被称为 粟 它营养丰富 味道清香 是传统的健康食品 在北方 小米粥配鸡蛋 红糖历来都是补充营养 滋补身体的佳品 在过去 小米是作为主食食用的 现在我们一般会用小米熬粥来调剂饮食 不会天天食用 那么 我们平时如何储存才能让小米干净卫
  • 对象的内存布局

    Hotspot虚拟机中 对象在内存中存储的布局可以分为三块区域 对象头 Header 实例数据 Instance Data 对齐填充 Padding 对象头 比如hash码 对象所属的年代 对象锁 锁状态标识 偏向锁线程ID 偏向时间 数组
  • wireshark display reference: https://www.wireshark.org/docs/dfref/

    2019独角兽企业重金招聘Python工程师标准 gt gt gt Display Filter Reference Wireshark s most powerful feature is its vast array of displa
  • Kruise Rollout:基于 Lua 脚本的可扩展流量调度方案

    前言 Kruise Rollout 1 是 OpenKruise 社区开源的渐进式交付框架 Kruise Rollout 支持配合流量和实例灰度的金丝雀发布 蓝绿发布 A B Testing 发布 以及发布过程能够基于 Prometheus
  • unable to access ‘https://gitee.com/XXX.git/‘: Failed to connect to 127.0.0.1port 7890: Connection r

    问题 在gitee拉取代码时 出现 unable to access https gitee com XXX git Failed to connect to 127 0 0 1port 7890 Connection refused的错误
  • 第五章 Vue组件化

    5 1 组件的概念 组件 component 是 Vue js 最强大的功能之一 Vue 中的组件化开发就是把网页的重复代码抽取出来 封装成一个个可复用的视图组件 然后将这些视图组件拼接到一块就构成了一个完整的系统 这种方式非常灵活 可以极
  • 知识蒸馏综述: 知识的类型

    GiantPandCV引言 简单总结一篇综述 Knowledge Distillation A Survey 中的内容 提取关键部分以及感兴趣部分进行汇总 这篇是知识蒸馏综述的第一篇 主要内容为知识蒸馏中知识的分类 包括基于响应的知识 基于
  • 太太太好用了!12款论文润色神器,SCI、EI论文写作必看

    SCI EI等期刊 会议论文现在大都需要英文写作 而非英语母语作者在写作上往往出现词不达意 描述模糊 句式混乱 累赘拖沓等现象 但是 期刊并不会因为作者母语不是英语就降低对语言的要求 并且审稿人很可能会因为语言的问题而低估了科学发现本身的意
  • python ddt模块

    python数据驱动模块ddt 一 安装 pip install ddt 二 使用 参考文章 https www cnblogs com miniren p 7099187 html 1 传入一个参数 import unittest fro
  • [网鼎杯 2020 青龙组]singal详细题解--VMP 直接逆向,angr模拟执行,ponce符号化

    文章目录 直接逆向 提取opcode 获取指令执行流 getflag 注意 使用Angr 使用Ponce插件 安装并配置Ponce 具体操作 参考资料 直接逆向 提取opcode 主函数并不复杂 关键内容在vm opcode中 先提取出ma