CTF-RE-WcyVM

题源:nctf2018

逆向+虚拟机鬼畜题,对萌新来说是非常好的一道虚拟机入门题

如果缺少虚拟机相关知识先看一下这篇文章:https://www.52pojie.cn/forum.php?mod=viewthread&tid=713219

首先我们用LINUX执行这个文件,提示要我们输入字符,随便输几个之后输入文本结束符号。我们发现程序没办法继续运行下去了。于是扔进ida开始调试。

进入main函数,我们发现系统在输出提示字符串之后进入了一个函数,点进这个函数进去看,发现是一大串赋值和switch语句

根据刚才这篇文章中的提示,很明显这是一个虚拟机结构。因此我们第一步先找出如图所示的几个变量,并且搞懂它们的含义。如下图所示

如果怕自己判断错误这些变量,或者看得不是很懂的,可以先动态调试一下,然后再分析。

然后对switch语句进行分析。容易知道每一个case语句都对应了一个handler。因此我们逐条进行分析。如果分析起来比较困难的话可以选择伪代码按下tab键显示汇编语言,也能大致理解虚拟过程的汇编代码。我们就可以写个程序还原出虚拟机的执行过程:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int EIP[]={
0x8,0x1,0x0,0x8,0x3,0x46,0xe,0x15,0xa,0x1,
0x9,0x2,0xb,0xa,0x1,0xa,0x2,0x9,0x1,0x11,
0x1,0xd,0x1,0x3,0xf,0x8,0x8,0x1,0x0,0x8,0x3,
0x47,0xe,0x46,0xa,0x1,0x1a,0x2,0x6,0x1d,
0x1,0x4,0x14,0x2,0x1,0x19,0x1,0x2,0x1b,
0x1,0x1,0x1d,0x1,0x6e,0x13,0x1,0x63,0x15,
0x1,0x74,0x13,0x1,0x66,0x1c,0x2,0x1,0x9,
0x1,0x11,0x1,0xd,0x1,0x3,0xf,0x22,0x64
};
int main(){
	int i=0;
	while(i<80){
		if(EIP[i]==8){
			printf("%d mov R%d %d\n",i,EIP[i+1]-1,EIP[i+2]);
			i+=3;
		}
		if(EIP[i]==9){
			printf("%d pop R%d\n",i,EIP[i+1]-1);
			i+=2;
		}
		if(EIP[i]==10){
			printf("%d push R%d\n",i,EIP[i+1]-1);
			i+=2;
		}
		if(EIP[i]==11){
			printf("%d R0=getchar()\n",i);
			i++; 
		}
		if(EIP[i]==12){
			printf("%d R0=puchar()\n",i);
			i++;
		}
		if(EIP[i]==13){
			printf("%d cmp R%d R%d\n",i,EIP[i+1]-1,EIP[i+2]-1);
			printf("  jnz %d\n",i+3);
			printf("  mov a, 80\n");
			i+=3;
		}
		if(EIP[i]==14){
			printf("%d jmp %d\n",i,EIP[i+1]);
			i+=2;
		}
		if(EIP[i]==15){
			printf("%d and a, 80\n",i);
			printf("   test a a\n");
			printf("  jnz %d\n",EIP[i+1]);
			i+=2;
		}
		if(EIP[i]==16){
			printf("%d and a, 80\n",i);
			printf("  test a a\n");
			printf("  jz %d\n",EIP[i+1]);
			i+=2;
		}
		if(EIP[i]==17){
			printf("%d inc R%d\n",i,EIP[i+1]-1);
        	i+=2; 
		}
		if(EIP[i]==18){
			printf("%d inc R%d\n",i,EIP[i+1]-1);
        	i+=2;
		}
		if(EIP[i]==19){
			printf("%d add R%d, %d\n",i,EIP[i+1]-1,EIP[i+2]);
        	i+=3;
		}
    	if(EIP[i]==20){
    		printf("%d sub R%d, R%d\n",i,EIP[i+1]-1,EIP[i+2]-1);
        	i+=3;
		}
    	if(EIP[i]==21){
    		printf("%d xor R%d, %d\n",i,EIP[i+1]-1,EIP[i+2]);
        	i+=3;
		}
    	if(EIP[i]==22){
    		printf("%d and R%d, R%d\n",i,EIP[i+1]-1,EIP[i+2]-1);
        	i+=3;
		}
    	if(EIP[i]==23){
        	printf("%d or R%d, R%d\n",i,EIP[i+1]-1,EIP[i+2]-1);
        	i+=3;
        }
    	if(EIP[i]==25){
        	printf("%d mov R%d, R%d\n",i,EIP[i+1]-1,EIP[i+2]-1);
        	i+=3;
        }
   		if(EIP[i]==26){
        	printf("%d mov R%d, R%d\n",i,EIP[i+1]-1,EIP[i+2]-1);
        	i+=3;
        }
    	if(EIP[i]==27){
        	printf("%d mov R%d, [R%d]\n",i,EIP[i+1]-1,EIP[i+2]-1);
        	i+=3;
        }
    	if(EIP[i]==28){
        	printf("%d mov [R%d], R%d\n",i,EIP[i+1]-1,EIP[i+2]-1);
        	i+=3;
        }
    	if(EIP[i]==29){
        	printf("%d mul R%d, %d\n",i,EIP[i+1]-1,EIP[i+2]);
        	i+=3;
        }
    }
}

程序运行之后得到下面的东西:

0   mov R0 0
3   mov R2 70
6   jmp 21
8   push R0
10 pop R1
12 R0=getchar()
13 push R0
15 push R1
17 pop R0
19 inc R0
21 cmp R0,R2
     jnz 24
     mov a, 80
24 and a, 80
     test a a
     jnz 8
26 mov R0 0
29 mov R2 71
32 jmp 70
34 push R0
36 mov R1, 5
39 mul R0, 4
42 sub R1, R0
45 mov R0, R1
48 mov R0, [R0]
51 mul R0, 110
54 add R0, 99
57 xor R0, 116
60 add R0, 102
63 mov [R1], R0
66 pop R0
68 inc R0
70 cmp R0 R2
     jnz 73
     mov a, 80
73 and a, 80
     test a a
     jnz 34

这就是被虚拟机保护的那一串关键代码。翻译起来比较简单。大致就是输入一个长度为70的字符串s。然后对于s[i],加密过程为((s[i]*110)+99)^116+102,得到的东西在switch中case100的情况进行比较。找到比较的字节内容为dword_6020A0,然后逆序还原即可

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
int s[]={
	0x36D3, 0x2AFF, 0x2ACB, 0x2B95, 0x2B95, 0x2B95, 0x169F, 0x186D,
    0x18D7, 0x1611, 0x18D7, 0x2B95, 0x2C23, 0x2CA9, 0x1611, 0x1611,
    0x18D7, 0x2AFF, 0x1849, 0x18FB, 0x2ACB, 0x2A71, 0x1735, 0x18D7,
    0x1611, 0x2ACB, 0x15DD, 0x18D7, 0x2C23, 0x169F, 0x15DD, 0x2B95,
    0x169F, 0x156B, 0x186D, 0x2AFF, 0x1611, 0x1611, 0x15DD, 0x2AFF,
    0x2C23, 0x2ACB, 0x15DD, 0x15DD, 0x186D, 0x1849, 0x2B95, 0x156B,
    0x1735, 0x18FB, 0x18FB, 0x2A71, 0x2AFF, 0x1735, 0x2C23, 0x15DD,
    0x18D7, 0x2A71, 0x18D7, 0x18D7, 0x2C23, 0x2AFF, 0x156B, 0x2C23,
    0x169F, 0x35AF, 0x2CA9, 0x32B5, 0x2AFF, 0x3039
};
int c;
int main(){
	for(int i=69;i>=0;i--){
		c=(((s[i]-102)^116)-99)/110;
		putchar((char)c);
	}

}

得到flag nctf{3e1ce77b70e4cb9941d6800aec022c813d03e70a274ba96c722fed72783dddac}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值