GWCTF2019 babyvm

vm题算是逆向中比较难的一种题型了,就在这里详细的记录一下。

原理

程序运行时通过解释操作码(opcode)选择对应的函数(handle)执行。

vm_start

进行初始化工作。在这个函数里,规定了有几个寄存器,以及有几种不同的操作。
在这里插入图片描述
这样看不明显,创建结构体修复一下,结构体长这个样子

typedef struct
{
    unsigned long R0;      //寄存器
    unsigned long R1;    
    unsigned long R2;   
    unsigned long R4;
    unsigned char *rip;    //指向正在解释的opcode地址
    vm_opcode op_list[OPCODE_N];    //opcode列表,存放了所有的opcode及其对应的处理函数
}vm_cpu;
typedef struct
{
    unsigned long opcode;
    void (*handle)(void*);
}vm_opcode;

修复完的初始化函数在这里插入图片描述

vm_start

void vm_start(vm_cpu *cpu)
{
    
    cpu->eip = (unsigned char*)opcodes;   //这里不是在上面就初始化过了吗???
    while((*cpu->eip) != 0xf4)//如果opcode不为RET,就调用vm_dispatcher来解释执行
    {
        vm_dispatcher(*cpu->eip)
    }
}

在这里插入图片描述
如果rip指向的操作码为F4就返回。

vm_dispatcher

调度器,任务是根据opcode选择函数执行

void vm_dispatcher(vm_cpu *cpu)
{
    int i;
    for(i = 0; i < OPCODE_N; i++)
    {    
        if(*cpu->eip == cpu->op_list[i].opcode)
        {
            cpu->op_list[i].handle(cpu);
            break;
        }
    }
}

在这里插入图片描述
循环,找到opcode对应的函数。

参考链接:
系统学习vm虚拟机逆向_vmware 逆向-CSDN博客

实战

【GWCTF2019babyvm】

分析opcode

知道了原理之后,就可以分析题目加深理解了。
在这里插入图片描述
主要分析vm_ini函数,搞清楚opcode对应的操作
在这里插入图片描述

0xF1

可以把这个当作mov指令
在这里插入图片描述

v2 = (int *)(a1->_rip + 2);

v2指向当前指令往后偏移两个字节的位置。

a1->_rip += 6LL;

说明这条指令的大小为6字节。

可以看出这条指令可以将复制一些值到寄存器,也可以将寄存器的值复制过去。

拿第一组数据来看,a1->rip+1是0xE1,那么将input[*v2]的值存入寄存器R0,也就是R0=input[0]

0xF1, 0xE1, 0x00, 0x00, 0x00, 0x00
0xF2

在这里插入图片描述
R0=R0^R1,指令长度1字节。

0xF5

在这里插入图片描述
读取输入并判断长度。

0xF4

在这里插入图片描述
空操作,nop,作用是将rip+1。

0xF7

在这里插入图片描述
R0*=R3,指令长度为1。

0xF8

在这里插入图片描述
交换R0和R1的数值。

0xF6

在这里插入图片描述
R0=R2+2*R1+3*R0

翻译

#include<stdio.h>
void myswap(char*a,char*b);
int main()
{
    unsigned char opcode[575] = {
    0xF5, 0xF1, 0xE1, 0x00, 0x00, 0x00, 0x00, 0xF2, 0xF1, 0xE4, 0x20, 0x00, 0x00, 0x00, 0xF1, 0xE1, 
    0x01, 0x00, 0x00, 0x00, 0xF2, 0xF1, 0xE4, 0x21, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x02, 0x00, 0x00, 
    0x00, 0xF2, 0xF1, 0xE4, 0x22, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x03, 0x00, 0x00, 0x00, 0xF2, 0xF1, 
    0xE4, 0x23, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x04, 0x00, 0x00, 0x00, 0xF2, 0xF1, 0xE4, 0x24, 0x00, 
    0x00, 0x00, 0xF1, 0xE1, 0x05, 0x00, 0x00, 0x00, 0xF2, 0xF1, 0xE4, 0x25, 0x00, 0x00, 0x00, 0xF1, 
    0xE1, 0x06, 0x00, 0x00, 0x00, 0xF2, 0xF1, 0xE4, 0x26, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x07, 0x00, 
    0x00, 0x00, 0xF2, 0xF1, 0xE4, 0x27, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x08, 0x00, 0x00, 0x00, 0xF2, 
    0xF1, 0xE4, 0x28, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x09, 0x00, 0x00, 0x00, 0xF2, 0xF1, 0xE4, 0x29, 
    0x00, 0x00, 0x00, 0xF1, 0xE1, 0x0A, 0x00, 0x00, 0x00, 0xF2, 0xF1, 0xE4, 0x2A, 0x00, 0x00, 0x00, 
    0xF1, 0xE1, 0x0B, 0x00, 0x00, 0x00, 0xF2, 0xF1, 0xE4, 0x2B, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x0C, 
    0x00, 0x00, 0x00, 0xF2, 0xF1, 0xE4, 0x2C, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x0D, 0x00, 0x00, 0x00, 
    0xF2, 0xF1, 0xE4, 0x2D, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x0E, 0x00, 0x00, 0x00, 0xF2, 0xF1, 0xE4, 
    0x2E, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x0F, 0x00, 0x00, 0x00, 0xF2, 0xF1, 0xE4, 0x2F, 0x00, 0x00, 
    0x00, 0xF1, 0xE1, 0x10, 0x00, 0x00, 0x00, 0xF2, 0xF1, 0xE4, 0x30, 0x00, 0x00, 0x00, 0xF1, 0xE1, 
    0x11, 0x00, 0x00, 0x00, 0xF2, 0xF1, 0xE4, 0x31, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x12, 0x00, 0x00, 
    0x00, 0xF2, 0xF1, 0xE4, 0x32, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x13, 0x00, 0x00, 0x00, 0xF2, 0xF1, 
    0xE4, 0x33, 0x00, 0x00, 0x00, 0xF4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
    0xF5, 0xF1, 0xE1, 0x00, 0x00, 0x00, 0x00, 0xF1, 0xE2, 0x01, 0x00, 0x00, 0x00, 0xF2, 0xF1, 0xE4, 
    0x00, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x01, 0x00, 0x00, 0x00, 0xF1, 0xE2, 0x02, 0x00, 0x00, 0x00, 
    0xF2, 0xF1, 0xE4, 0x01, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x02, 0x00, 0x00, 0x00, 0xF1, 0xE2, 0x03, 
    0x00, 0x00, 0x00, 0xF2, 0xF1, 0xE4, 0x02, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x03, 0x00, 0x00, 0x00, 
    0xF1, 0xE2, 0x04, 0x00, 0x00, 0x00, 0xF2, 0xF1, 0xE4, 0x03, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x04, 
    0x00, 0x00, 0x00, 0xF1, 0xE2, 0x05, 0x00, 0x00, 0x00, 0xF2, 0xF1, 0xE4, 0x04, 0x00, 0x00, 0x00, 
    0xF1, 0xE1, 0x05, 0x00, 0x00, 0x00, 0xF1, 0xE2, 0x06, 0x00, 0x00, 0x00, 0xF2, 0xF1, 0xE4, 0x05, 
    0x00, 0x00, 0x00, 0xF1, 0xE1, 0x06, 0x00, 0x00, 0x00, 0xF1, 0xE2, 0x07, 0x00, 0x00, 0x00, 0xF1, 
    0xE3, 0x08, 0x00, 0x00, 0x00, 0xF1, 0xE5, 0x0C, 0x00, 0x00, 0x00, 0xF6, 0xF7, 0xF1, 0xE4, 0x06, 
    0x00, 0x00, 0x00, 0xF1, 0xE1, 0x07, 0x00, 0x00, 0x00, 0xF1, 0xE2, 0x08, 0x00, 0x00, 0x00, 0xF1, 
    0xE3, 0x09, 0x00, 0x00, 0x00, 0xF1, 0xE5, 0x0C, 0x00, 0x00, 0x00, 0xF6, 0xF7, 0xF1, 0xE4, 0x07, 
    0x00, 0x00, 0x00, 0xF1, 0xE1, 0x08, 0x00, 0x00, 0x00, 0xF1, 0xE2, 0x09, 0x00, 0x00, 0x00, 0xF1, 
    0xE3, 0x0A, 0x00, 0x00, 0x00, 0xF1, 0xE5, 0x0C, 0x00, 0x00, 0x00, 0xF6, 0xF7, 0xF1, 0xE4, 0x08, 
    0x00, 0x00, 0x00, 0xF1, 0xE1, 0x0D, 0x00, 0x00, 0x00, 0xF1, 0xE2, 0x13, 0x00, 0x00, 0x00, 0xF8, 
    0xF1, 0xE4, 0x0D, 0x00, 0x00, 0x00, 0xF1, 0xE7, 0x13, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x0E, 0x00, 
    0x00, 0x00, 0xF1, 0xE2, 0x12, 0x00, 0x00, 0x00, 0xF8, 0xF1, 0xE4, 0x0E, 0x00, 0x00, 0x00, 0xF1, 
    0xE7, 0x12, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x0F, 0x00, 0x00, 0x00, 0xF1, 0xE2, 0x11, 0x00, 0x00, 
    0x00, 0xF8, 0xF1, 0xE4, 0x0F, 0x00, 0x00, 0x00, 0xF1, 0xE7, 0x11, 0x00, 0x00, 0x00,0xf4};
    //翻译
    for (int i = 0; i < 575; )
    {
        if (opcode[i] == 0xf1) // mov
        {
            switch (opcode[i+1])
            {
            case 0xE1:
                //a1->R0 = *((char *)input + *v2);
                printf("R0=input[%d]\n",*(int*)&opcode[i+2]);
                break;
            case 0xE2:
                //a1->R1 = *((char *)input + *v2);
                printf("R1=input[%d]\n",*(int*)&opcode[i+2]);
                break;
            case 0xE3:
                //a1->R2 = *((char *)input + *v2);
                printf("R2=input[%d]\n",*(int*)&opcode[i+2]);
                break;
            case 0xE4:
                //*((_BYTE *)input + *v2) = a1->R0;
                printf("input[%d]=R0\n",*(int*)&opcode[i+2]);
                break;
            case 0xE5:
                //a1->R3 = *((char *)input + *v2);
                printf("R3=input[%d]\n",*(int*)&opcode[i+2]);
                break;
            case 0xE7:
                //*((_BYTE *)input + *v2) = a1->R1;
                printf("input[%d]=R1\n",*(int*)&opcode[i+2]);
                break;
            default:
                printf("mov wrong!!!!!\n");
                break;
            }
            i+=6;
        }
        else if (opcode[i] == 0xf2) // xor
        {
            printf("R0=R0^R1\n");
            i+=1;
        }
        else if (opcode[i] == 0xf5) // scanf
        {
            printf("please input:\n");
            i+=1;
        }
        else if (opcode[i] == 0xf4) // nop
        {
            printf("0xF4 nop\n");
            printf("\n");
            i+=1;
        }
        else if (opcode[i] == 0xf7) //*
        {
            printf("R0*=R3\n");
            i+=1;
        }
        else if (opcode[i] == 0xf8) // change
        {
            printf("change(R0,R1)\n");
            i+=1;
        }
        else if (opcode[i] == 0xf6) //
        {
            printf("R0=R2+2*R1+3*R0\n");
            i+=1;
        }
        else if(opcode[i]==0)
        {
            printf("nop\n");
            i++;
        }
    }
    printf("over!!");
    
    return 0;
}

得到了两段程序,第一个是简单的异或

please input:       R1=18
R0=input[0]        
R0=R0^R1       //input0^18
input[32]=R0      
R0=input[1]
R0=R0^R1
input[33]=R0
R0=input[2]
R0=R0^R1
input[34]=R0
R0=input[3]
R0=R0^R1
input[35]=R0
R0=input[4]
R0=R0^R1
input[36]=R0
R0=input[5]
R0=R0^R1
input[37]=R0
R0=input[6]
R0=R0^R1
input[38]=R0
R0=input[7]
R0=R0^R1
input[39]=R0
R0=input[8]
R0=R0^R1
input[40]=R0
R0=input[9]
R0=R0^R1
input[41]=R0
R0=input[10]
R0=R0^R1
input[42]=R0
R0=input[11]
R0=R0^R1
input[43]=R0
R0=input[12]
R0=R0^R1
input[44]=R0
R0=input[13]
R0=R0^R1
input[45]=R0
R0=input[14]
R0=R0^R1
input[46]=R0
R0=input[15]
R0=R0^R1
input[47]=R0
R0=input[16]
R0=R0^R1
input[48]=R0
R0=input[17]
R0=R0^R1
input[49]=R0
R0=input[18]
R0=R0^R1
input[50]=R0
R0=input[19]
R0=R0^R1
input[51]=R0
0xF4 nop
for(int i=0;i<21;i++)
{
    printf("%c",cpdata[i]^18);
}
//This_is_not_flag_233

第二段

please input:
R0=input[0]
R1=input[1]
R0=R0^R1
input[0]=R0

R0=input[1]
R1=input[2]
R0=R0^R1
input[1]=R0

R0=input[2]
R1=input[3]
R0=R0^R1
input[2]=R0

R0=input[3]
R1=input[4]
R0=R0^R1
input[3]=R0

R0=input[4]
R1=input[5]
R0=R0^R1
input[4]=R0

R0=input[5]
R1=input[6]
R0=R0^R1
input[5]=R0

R0=input[6]          //input[6]=
R1=input[7]
R2=input[8]
R3=input[12]
R0=R2+2*R1+3*R0
R0*=R3
input[6]=R0

R0=input[7]              //input[7]=
R1=input[8]
R2=input[9]
R3=input[12]
R0=R2+2*R1+3*R0
R0*=R3
input[7]=R0

R0=input[8]                  //input[8]=
R1=input[9]
R2=input[10]
R3=input[12]
R0=R2+2*R1+3*R0
R0*=R3
input[8]=R0

R0=input[13]      //置换  13  19
R1=input[19]
change(R0,R1)
input[13]=R0
input[19]=R1

R0=input[14]       //置换14 18
R1=input[18]
change(R0,R1)
input[14]=R0
input[18]=R1

R0=input[15]       //置换15 17
R1=input[17]
change(R0,R1)
input[15]=R0
input[17]=R1
0xF4 nop   

over!!

第二段的比较数据就在第一个比较数据的附近,在得到假flag之后,查看该处的数据
在这里插入图片描述
发现上面有一个可疑数据,查看它的交叉引用,可以跟进一个比较函数
在这里插入图片描述
但是比较奇怪的是,这个函数没有被调用过,之前做过actf的一道题,函数通过栈溢出覆盖了返回值,从而被调用,但是这一题就算输入了真正的flag,也不会调用这一处函数,所以感觉有点……,虽然有提示,虽然opcode有两个0xf5调用输入,但还是有些生硬了。

#include<stdio.h>
void myswap(char*a,char*b);
int main()
{
    for(int i=30;i<127;i++)
    {
        if(realdata[8]==(unsigned char)((realdata[10]+2*realdata[9]+3*i)*realdata[12]))
        {
            //printf("flag[8]==%c\n",i);
            realdata[8]=i;
        }
    }
    for(int i=30;i<127;i++)
    {
        if(realdata[7]==(unsigned char)((realdata[9]+2*realdata[8]+3*i)*realdata[12]))
        {
            //printf("flag[7]==%c\n",i);
            realdata[7]=i;
        }
    }
    for(int i=30;i<127;i++)
    {
        int a=(realdata[8]+2*realdata[7]+3*i)*realdata[12];
        if(realdata[6]==(unsigned char)((realdata[8]+2*realdata[7]+3*i)*realdata[12]))
        {
            //printf("flag[6]==%c\n",i);
            realdata[6]=i;
        }
    }
    myswap(&realdata[13],&realdata[19]);
    myswap(&realdata[14],&realdata[18]);
    myswap(&realdata[15],&realdata[17]);
    for(int i=0;i<20;i++)
    {
        printf("%c",realdata[i]);
    }
    
    return 0;
}

void myswap(char* a,char* b)
{
   char t=*a;
   *a=*b;
   *b=t;
}

//Y0u_hav3_r3v3rs3_1t!
  • 49
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
东南亚位于我国倡导推进的“一带一路”海陆交汇地带,作为当今全球发展最为迅速的地区之一,近年来区域内生产总值实现了显著且稳定的增长。根据东盟主要经济体公布的最新数据,印度尼西亚2023年国内生产总值(GDP)增长5.05%;越南2023年经济增长5.05%;马来西亚2023年经济增速为3.7%;泰国2023年经济增长1.9%;新加坡2023年经济增长1.1%;柬埔寨2023年经济增速预计为5.6%。 东盟国家在“一带一路”沿线国家中的总体GDP经济规模、贸易总额与国外直接投资均为最大,因此有着举足轻重的地位和作用。当前,东盟与中国已互相成为双方最大的交易伙伴。中国-东盟贸易总额已从2013年的443亿元增长至 2023年合计超逾6.4万亿元,占中国外贸总值的15.4%。在过去20余年中,东盟国家不断在全球多变的格局里面临挑战并寻求机遇。2023东盟国家主要经济体受到国内消费、国外投资、货币政策、旅游业复苏、和大宗商品出口价企稳等方面的提振,经济显现出稳步增长态势和强韧性的潜能。 本调研报告旨在深度挖掘东南亚市场的增长潜力与发展机会,分析东南亚市场竞争态势、销售模式、客户偏好、整体市场营商环境,为国内企业出海开展业务提供客观参考意见。 本文核心内容: 市场空间:全球行业市场空间、东南亚市场发展空间。 竞争态势:全球份额,东南亚市场企业份额。 销售模式:东南亚市场销售模式、本地代理商 客户情况:东南亚本地客户及偏好分析 营商环境:东南亚营商环境分析 本文纳入的企业包括国外及印尼本土企业,以及相关上下游企业等,部分名单 QYResearch是全球知名的大型咨询公司,行业涵盖各高科技行业产业链细分市场,横跨如半导体产业链(半导体设备及零部件、半导体材料、集成电路、制造、封测、分立器件、传感器、光电器件)、光伏产业链(设备、硅料/硅片、电池片、组件、辅料支架、逆变器、电站终端)、新能源汽车产业链(动力电池及材料、电驱电控、汽车半导体/电子、整车、充电桩)、通信产业链(通信系统设备、终端设备、电子元器件、射频前端、光模块、4G/5G/6G、宽带、IoT、数字经济、AI)、先进材料产业链(金属材料、高分子材料、陶瓷材料、纳米材料等)、机械制造产业链(数控机床、工程机械、电气机械、3C自动化、工业机器人、激光、工控、无人机)、食品药品、医疗器械、农业等。邮箱:market@qyresearch.com

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值