Loading

hgame2023 vm

vm逆向

hgame2023 vm

简单翻阅一下发现,sub_140001000里面是vm_init,sub_1400010B0是主要的vm部分

查看vm_init部分,发现只知道cpu结构的大小以及初始值和24-32字节的结构,前24字节的结构未知,暂时还无法构建cpu的结构,需要更多的信息。

分析下面两图的函数可以得知,opcode总共有8个,byte_140005360的取值很多是0-7,故byte_140005360为opcode数组。看result类型可知opcode为4字节。(unsigned int *)(a1+24)在操作这个这个数组,因此a1+24为4字节的eip。sub_1400010B0中返回a1+32这个一字节的数据,他应该是某种标志位,某个标志寄存器。在sub_140001940中result为8字节,因此handle为8字节。在分析中,要确定cpu数组a1中各个成员的大小,以便创建cpu结构。

取v2 = opcode[a1[6] + 1]; 印证了a1[6]即a1+24为eip,此处为eip+1

此处出现了a1[7],而且a1[7]先自增再存入内容

下一个指令是a1[7]--,而且是先减后用,搭配上数组140005D40仅有上下两图中的指令操作,得出a1[7]应为esp,操作的数组为栈空间。

定义各种运算

此处涉及到a1+32的功能,根据a1和a1+4处的内容为a1+32号位置赋值,相等为0不等为1。联想到cmp指令操作的寄存器,猜测a1+32应该是ZF标志位。

至此,可以猜测cpu的结构。前24字节为6个通用寄存器,每个4字节。还有4字节eip,4字节esp和1字节ZF。创建结构体

typedef struct
{
    unsigned int R[6];      
    unsigned int eip;
    unsigned int esp;
    char          zf; 
    vm_opcode op_list[OPCODE_N];    
}vm_cpu;
typedef struct
{
    unsigned int opcode;
    void (*handle)(void*);
}vm_opcode;

修改类型让其更易读

可看到指令对应的函数已经被修复

修复后的指令功能识别

其他指令分析同理。写脚本打印一下执行流程。

#include<stdio.h>
#include <stdlib.h>

int main(){
    unsigned char opcode[]={
        0, 3, 2, 0, 3, 0, 2, 3, 0, 0, 0, 0, 0, 2, 1, 0, 0, 3, 2, 0x32, 3, 0, 2, 3, 0, 0, 0, 0,
        3, 0, 1, 0, 0, 3, 2, 0x64, 3, 0, 2, 3, 0, 0, 0, 0, 3, 3, 1, 0, 0, 3, 0, 8, 0, 2, 2, 1, 
        3, 4, 1, 0, 3, 5, 2, 0, 3, 0, 1, 2, 0, 2, 0, 1, 1, 0, 0, 3, 0, 1, 3, 0, 3, 0, 0, 2, 0, 
        3, 0, 3, 1, 0x28, 4, 6, 0x5f, 5, 0, 0, 3, 3, 0, 2, 1, 0, 3, 2, 0x96, 3, 0, 2, 3, 0, 0,
        0, 0, 4, 7, 0x88, 0, 3, 0, 1, 3, 0, 3, 0, 0, 2, 0, 3, 0, 3, 1, 0x28, 4, 7, 0x63, 0xff, 0xff};
    unsigned int r[6]={0, 0, 0, 0, 0, 0};
    int zf=0;
    unsigned int esp=0,eip=0;
    unsigned int input_and_mem[]={
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x9B,0x0A8,0x2, 0xBC,0x0AC,0x9C,0x0CE,
        0x0FA,0x2, 0xB9,0x0FF,0x3A,0x74,0x48,0x19,0x69,0x0E8,0x3, 0xCB,0x0C9,0x0FF,0x0FC,0x80,0x0D6,0x8D,
        0x0D7,0x72,0x0, 0xA7,0x1D,0x3D,0x99,0x88,0x99,0x0BF,0x0E8,0x96,0x2E,0x5D,0x57,0x0, 0, 0, 0, 0, 0,
         0, 0, 0, 0, 0xC9,0x0A9,0x0BD,0x8B,0x17,0x0C2,0x6E,0x0F8,0x0F5,0x6E,0x63,0x63,0x0D5,0x46,0x5D,0x16,
         0x98,0x38,0x30,0x73,0x38,0x0C1,0x5E,0x0ED,0x0B0,0x29,0x5A,0x18,0x40,0x0A7,0x0FD,0x0A,0x1E,0x78,0x8B,
         0x62,0x0DB,0x0F,0x8F,0x9C,0x0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x4800,0x0F100,0x4000,0x2100,0x3501,0x6400,
         0x7801,0x0F900,0x1801,0x5200,0x2500,0x5D01,0x4700,0x0FD00,0x6901,0x5C00,0x0AF01,0x0B200,0x0EC01,0x5201,
         0x4F01,0x1A01,0x5000,0x8501,0x0CD00,0x2300,0x0F800,0x0C00,0x0CF00,0x3D01,0x4501,0x8200,0x0D201,0x2901,
         0x0D501,0x601,0x0A201,0x0DE00,0x0A601,0x0CA01,0x0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
    unsigned int stack[80]={0};
    int v2;
    FILE *fp = fopen("log.txt", "a+");
    while(opcode[eip]!=0xff){
        switch (opcode[eip])
        {
        case 0:
            v2=opcode[eip+1];
            if(v2){
                switch (v2)
                {
                case 1:
                    input_and_mem[r[2]]=r[0];
                    fprintf(fp,"input_and_mem[%d]=%d\n",r[2],r[0]);
                    break;
                case 2:
                    *(r+opcode[eip+2])=*(r+opcode[eip+3]);
                    fprintf(fp,"r[%d]=r[%d]\n",opcode[eip+2],opcode[eip+3]);
                    break;
                case 3:
                    *(r+opcode[eip+2])=opcode[eip+3];
                    fprintf(fp,"r[%d]=%d\n",opcode[eip+2],opcode[eip+3]);
                    break;

                default:
                    break;
                }
            }else{
                r[0]=input_and_mem[r[2]];
                fprintf(fp,"r[0]=input_and_mem[%d] %d\n",r[2],r[0]);
            }
            eip=eip+4;
            break;

        case 1:
            v2=opcode[eip+1];
            if(v2){
                switch (v2)
                {
                case 1:
                    stack[++esp]=r[0];
                    fprintf(fp,"stack[%d]=r[0] %d\n",esp,r[0]);
                    break;
                case 2:
                    stack[++esp]=r[2];
                    fprintf(fp,"stack[%d]=r[2] %d\n",esp ,r[2]);
                    break;
                case 3:
                    stack[++esp]=r[3];
                    fprintf(fp,"stack[%d]=r[3] %d\n",esp ,r[3]);
                    break;
                
                }
            }else{
                stack[++esp]=r[0];
                fprintf(fp,"stack[%d]=r[0] %d\n",esp ,r[0]  );
            }
            eip=eip+2;
            break;
        case 2:
            v2=opcode[eip+1];
            if(v2){
                switch (v2)
                {
                case 1:
                    r[1]=stack[esp--];
                    fprintf(fp,"r[1]=stack[%d] %d\n",esp+1,r[1]);
                    break;
                case 2:
                    r[2]=stack[esp--];
                    fprintf(fp,"r[2]=stack[%d] %d\n",esp+1,r[2]);
                    break;
                case 3:
                    r[3]=stack[esp--];
                    fprintf(fp,"r[3]=stack[%d] %d\n",esp+1 ,r[3]);
                    break;
                }
            }else{
                r[0]=stack[esp--];
                fprintf(fp,"r[0]=stack[%d] %d\n",esp+1 ,r[0]);
            }
            eip=eip+2;
            break;
        case 3:
            switch (opcode[eip+1])
            {
            case 0:
                *(r+opcode[eip+2])+=*(r+opcode[eip+3]);
                fprintf(fp,"r[%d]=r[%d]+r[%d] %d\n",opcode[eip+2],opcode[eip+2],opcode[eip+3],*(r+opcode[eip+2]));
                break;
            case 1:
                *(r+opcode[eip+2])-=*(r+opcode[eip+3]);
                fprintf(fp,"r[%d]=r[%d]-r[%d] %d\n",opcode[eip+2],opcode[eip+2],opcode[eip+3] ,*(r+opcode[eip+2]));
                break;
            case 2:
                *(r+opcode[eip+2])*=*(r+opcode[eip+3]);
                fprintf(fp,"r[%d]=r[%d]*r[%d] %d\n",opcode[eip+2],opcode[eip+2],opcode[eip+3] ,*(r+opcode[eip+2]));
                break;
            case 3:
                *(r+opcode[eip+2])^=*(r+opcode[eip+3]);
                fprintf(fp,"r[%d]=r[%d]^r[%d] %d\n",opcode[eip+2],opcode[eip+2],opcode[eip+3] ,*(r+opcode[eip+2]) );
                break;
            case 4:
                *(r+opcode[eip+2])<<=*(r+opcode[eip+3]);
                fprintf(fp,"r[%d]=r[%d]<<r[%d] %d\n",opcode[eip+2],opcode[eip+2],opcode[eip+3],*(r+opcode[eip+2]));
                *(r+opcode[eip+2])&=0xff00u;
                fprintf(fp,"r[%d]=r[%d]&0xff00u %d\n",opcode[eip+2],opcode[eip+2],*(r+opcode[eip+2]));
                break;
            case 5:
                *(r+opcode[eip+2])=*(r+opcode[eip+2]) >> *(r+opcode[eip+3]);
                fprintf(fp,"r[%d]=r[%d]>>r[%d] %d\n",opcode[eip+2],opcode[eip+2],opcode[eip+3],*(r+opcode[eip+2]));
                break;
            default:
                break;
            }
            eip=eip+4;
            break;
        case 4:
            if(r[0]==r[1]){
                zf=0;

                fprintf(fp,"cmp r0 r1 r0==r1 zf=0\n");
            }else{
                zf=1;
                fprintf(fp,"cmp r0 r1 r0!=r1 zf=1\n");
            }

            eip=eip+1;
            break;    
        case 5:
            eip=opcode[eip+1];
            fprintf(fp,"jmp %d\n",eip);
            
            break;
        case 6:
            if(zf){
                eip=eip+2;
                fprintf(fp,"jnz %d\n",eip);
            }else{
                eip=opcode[eip+1];
                fprintf(fp,"jnz %d\n",eip);
            }
            break;
        case 7:
            if(zf){
                eip=opcode[eip+1];
                fprintf(fp,"jz %d\n",eip);
            }else{
                eip=eip+2;
                fprintf(fp,"jz %d\n",eip);
            }
            break;
        default:
            break;
        }
    }
    return 0;
}

编写代码时一定仔细检查

log:

r[2]=0
r[2]=r[2]+r[3] 0
r[0]=input_and_mem[0] 0
r[1]=r[0]
r[2]=50
r[2]=r[2]+r[3] 50
r[0]=input_and_mem[50] 155
r[1]=r[1]+r[0] 155
r[2]=100
r[2]=r[2]+r[3] 100
r[0]=input_and_mem[100] 201
r[1]=r[1]^r[0] 82
r[0]=8
r[2]=r[1]
r[1]=r[1]<<r[0] 20992
r[1]=r[1]&0xff00u 20992
r[2]=r[2]>>r[0] 0
r[1]=r[1]+r[2] 20992
r[0]=r[1]
stack[1]=r[0] 20992
r[0]=1
r[3]=r[3]+r[0] 1
r[0]=r[3]
r[1]=40
cmp r0 r1 r0!=r1 zf=1
jnz 93
jmp 0


r[2]=0
r[2]=r[2]+r[3] 1
r[0]=input_and_mem[1] 0
r[1]=r[0]
r[2]=50
r[2]=r[2]+r[3] 51
r[0]=input_and_mem[51] 168
r[1]=r[1]+r[0] 168
r[2]=100
r[2]=r[2]+r[3] 101
r[0]=input_and_mem[101] 169
r[1]=r[1]^r[0] 1
r[0]=8
r[2]=r[1]
r[1]=r[1]<<r[0] 256
r[1]=r[1]&0xff00u 256
r[2]=r[2]>>r[0] 0
r[1]=r[1]+r[2] 256
r[0]=r[1]
stack[2]=r[0] 256
r[0]=1
r[3]=r[3]+r[0] 2
r[0]=r[3]
r[1]=40
cmp r0 r1 r0!=r1 zf=1
jnz 93
jmp 0


....

r[2]=0
r[2]=r[2]+r[3] 37
r[0]=input_and_mem[37] 0
r[1]=r[0]
r[2]=50
r[2]=r[2]+r[3] 87
r[0]=input_and_mem[87] 46
r[1]=r[1]+r[0] 46
r[2]=100
r[2]=r[2]+r[3] 137
r[0]=input_and_mem[137] 15
r[1]=r[1]^r[0] 33
r[0]=8
r[2]=r[1]
r[1]=r[1]<<r[0] 8448
r[1]=r[1]&0xff00u 8448
r[2]=r[2]>>r[0] 0
r[1]=r[1]+r[2] 8448
r[0]=r[1]
stack[38]=r[0] 8448
r[0]=1
r[3]=r[3]+r[0] 38
r[0]=r[3]
r[1]=40
cmp r0 r1 r0!=r1 zf=1
jnz 93
jmp 0

r[2]=0
r[2]=r[2]+r[3] 38
r[0]=input_and_mem[38] 0
r[1]=r[0]
r[2]=50
r[2]=r[2]+r[3] 88
r[0]=input_and_mem[88] 93
r[1]=r[1]+r[0] 93
r[2]=100
r[2]=r[2]+r[3] 138
r[0]=input_and_mem[138] 143
r[1]=r[1]^r[0] 210
r[0]=8
r[2]=r[1]
r[1]=r[1]<<r[0] 53760
r[1]=r[1]&0xff00u 53760
r[2]=r[2]>>r[0] 0
r[1]=r[1]+r[2] 53760
r[0]=r[1]
stack[39]=r[0] 53760
r[0]=1
r[3]=r[3]+r[0] 39
r[0]=r[3]
r[1]=40
cmp r0 r1 r0!=r1 zf=1
jnz 93
jmp 0

r[2]=0
r[2]=r[2]+r[3] 39
r[0]=input_and_mem[39] 0
r[1]=r[0]
r[2]=50
r[2]=r[2]+r[3] 89
r[0]=input_and_mem[89] 87
r[1]=r[1]+r[0] 87
r[2]=100
r[2]=r[2]+r[3] 139
r[0]=input_and_mem[139] 156
r[1]=r[1]^r[0] 203
r[0]=8
r[2]=r[1]
r[1]=r[1]<<r[0] 51968
r[1]=r[1]&0xff00u 51968
r[2]=r[2]>>r[0] 0
r[1]=r[1]+r[2] 51968
r[0]=r[1]
stack[40]=r[0] 51968
r[0]=1
r[3]=r[3]+r[0] 40
r[0]=r[3]
r[1]=40
cmp r0 r1 r0==r1 zf=0
jnz 95

r[3]=0
r[1]=stack[40] 51968
r[2]=150
r[2]=r[2]+r[3] 150
r[0]=input_and_mem[150] 18432
cmp r0 r1 r0!=r1 zf=1
jz 136

分析可以发现,虽然步骤很多,但是通过jmp 0 指令分隔可分成40个部分,也就是40轮循环,每轮循环都类似。以第一轮为例

r[2]=0
r[2]=r[2]+r[3] 0
r[0]=input_and_mem[0] 0	
r[1]=r[0]					//r1=input_and_mem[0]
r[2]=50
r[2]=r[2]+r[3] 50
r[0]=input_and_mem[50] 155	//
r[1]=r[1]+r[0] 155			//r1=r1+input_and_mem[50]
r[2]=100
r[2]=r[2]+r[3] 100
r[0]=input_and_mem[100] 201
r[1]=r[1]^r[0] 82			//r1=r1^input_and_mem[100]
r[0]=8
r[2]=r[1]					//r2=r1
r[1]=r[1]<<r[0] 20992		//r1=r1<<8
r[1]=r[1]&0xff00u 20992		//r1=r1&0xff
r[2]=r[2]>>r[0] 0			//r2=r2>>8
r[1]=r[1]+r[2] 20992		//r1=r1+r2  也就是交换高低字节
r[0]=r[1]					//r0=r1
stack[1]=r[0] 20992			//push r0
r[0]=1
r[3]=r[3]+r[0] 1
r[0]=r[3]
r[1]=40
cmp r0 r1 r0!=r1 zf=1
jnz 93
jmp 0

也就是input_and_mem里前40位是输入,50-90位是加运算的数据,100-140位是异或的数据。在最后一次跳转中使用pop弹出了栈顶的数据,和input_and_mem里的150号数据进行比较,可以猜测150-190为加密后的数据,栈中的数据为加密后的数据,依次弹出和密文比较。还可推断是逐个比较,只要不正确即终止运行,因此此处只比较了一次。代码示意和脚本如下。

push (((input_and_mem[i]+input_and_mem[i+50])^input_and_mem[i+100])<<8)&0xff00u+(((input_and_mem[i]+input_and_mem[i+50])^input_and_mem[i+100])>>8)
#include<stdio.h>
#include <stdlib.h>
int main(){
    unsigned int cipher[40]=
    {
        0x4800,0x0F100,0x4000,0x2100,0x3501,0x6400,0x7801,0x0F900,0x1801,0x5200,0x2500,0x5D01,
        0x4700,0x0FD00,0x6901,0x5C00,0x0AF01,0x0B200,0x0EC01,0x5201,0x4F01,0x1A01,0x5000,0x8501,
        0x0CD00,0x2300,0x0F800,0x0C00,0x0CF00,0x3D01,0x4501,0x8200,0x0D201,0x2901,0x0D501,0x601,
        0x0A201,0x0DE00,0x0A601,0x0CA01
    };
    unsigned int key_add[40]={
        0x9B,0x0A8,0x2, 0xBC,0x0AC,0x9C,0x0CE,
        0x0FA,0x2, 0xB9,0x0FF,0x3A,0x74,0x48,0x19,0x69,0x0E8,0x3, 0xCB,0x0C9,0x0FF,0x0FC,0x80,0x0D6,0x8D,
        0x0D7,0x72,0x0, 0xA7,0x1D,0x3D,0x99,0x88,0x99,0x0BF,0x0E8,0x96,0x2E,0x5D,0x57
        
    };
    unsigned int key_xor[40]={
        0xC9,0x0A9,0x0BD,0x8B,0x17,0x0C2,0x6E,0x0F8,0x0F5,0x6E,0x63,0x63,0x0D5,0x46,0x5D,0x16,
        0x98,0x38,0x30,0x73,0x38,0x0C1,0x5E,0x0ED,0x0B0,0x29,0x5A,0x18,0x40,0x0A7,0x0FD,0x0A,0x1E,0x78,0x8B,
        0x62,0x0DB,0x0F,0x8F,0x9C
        
    };
    unsigned int temp;
    for(int i=0;i<40;i++){
        temp=cipher[39-i];
        temp=((temp<<8)&0xff00u)+(temp>>8);
        temp=temp^key_xor[i];
        temp=temp-key_add[i];
        printf("%c",temp);
    }
    return 0;
}
//hgame{y0ur_rever5e_sk1ll_i5_very_g0od!!}

参考:

  1. https://mp.weixin.qq.com/s/aVPlvHkb6ohZ5K_zXVhs0w

  2. cmp指令 - 知乎 (zhihu.com)

posted @ 2024-05-03 10:28  yuhury  阅读(6)  评论(0编辑  收藏  举报