NCTF_WcyVM

NCTF_WcyVM

分析

首先拿到程序,用IDA运行,F5静态分析,发现if判断其中有一个函数
进去,简单的描述一下:
1.定义了一些变量,其中某个数组的内容copy给了dest
2.while循环里面有个Switch-case结构,典型的VM题
3.Case100里面有个运算,决定上层函数的返回值,即程序运行的输出结果


思路

1.VM题我们需要对应分析,首先找到程序对应的Opcode,这里我们可以很轻易知道dest指向Opcode的值,可利用IDA对作为VM寄存器的变量重新命名,利用分析
2.最后将Opcode与Switch结构对应翻译
3.对Case100函数进行逆运算,从而得到正确的Flag

利用

第一步,翻译Switch-Case

1
#include<stdio.h>
2
int main()
3
{
4
		//虚拟机OPcode 
5
		FILE *fp;
6
		int n = 0,R0,R1,R2;
7
		int *N = &n;
8
		fp = fopen("Asm","w");
9
		unsigned char S[] =
10
		{
11
		0x8,0x1,0x0,0x8,0x3,0x46,0xe,0x15,0xa,0x1,
12
		0x9,0x2,0xb,0xa,0x1,0xa,0x2,0x9,0x1,0x11,
13
		0x1,0xd,0x1,0x3,0xf,0x8,0x8,0x1,0x0,0x8,
14
		0x3,0x47,0xe,0x46,0xa,0x1,0x1a,0x2,0x6,0x1d,
15
		0x1,0x4,0x14,0x2,0x1,0x19,0x1,0x2,0x1b,0x1,
16
		0x1,0x1d,0x1,0x6e,0x13,0x1,0x63,0x15,0x1,0x74,
17
		0x13,0x1,0x66,0x1c,0x2,0x1,0x9,0x1,0x11,0x1,
18
		0xd,0x1,0x3,0xf,0x22,0x64
19
		};
20
		int i = 0;//I表示EIP Location 
21
		while(i<75)
22
		{
23
			if(S[i]== 8)
24
			{
25
				fprintf(fp,"%d\tmov R%d %d\n",i,S[i+1]-1,S[i+2]);
26
				i+=3;
27
			}
28
			if(S[i] ==9)
29
			{
30
				fprintf(fp,"%d\tpop R%d\n",i,S[i+1]-1);
31
				i+=2;
32
			}
33
			if(S[i] == 10)
34
			{
35
				fprintf(fp,"%d\tpush R%d\n",i,S[i+1]-1);
36
				i+=2;
37
			}
38
			if(S[i] == 11)
39
			{
40
				fprintf(fp,"%d\tR0 = getchar()\n",i);
41
				i++;
42
			}
43
			if(S[i]==12)
44
			{
45
				fprintf(fp,"%d\tRO = putchar()\n",i);
46
				i++;
47
			}
48
			if(S[i] == 13)
49
			{
50
				fprintf(fp,"%d\tif R%d==R%d\n\t\tmov Rx 0x80\n",i,S[i+1]-1,S[i+2]-1);
51
				fprintf(fp,"\tif R%d <R%d\n\t\tmov Rx 0x40\n",S[i+1]-1,S[i+2]-1);
52
				fprintf(fp,"\tif R%d >R%d\n\t\tmov Rx 0x20\n",S[i+1]-1,S[i+2]-1);
53
				i+=3;
54
			}
55
			if(S[i] ==14)
56
			{
57
				fprintf(fp,"%d\tjmp %d\n",i,S[i+1]);
58
				i+=2;
59
			}
60
			if(S[i] == 15)
61
			{
62
				fprintf(fp,"%d\tand Rx 80h\n",i);
63
				fprintf(fp,"\ttest Rx Rx\n");
64
				fprintf(fp,"\tjnz %d\n",S[i+1]);
65
				i+=2;//此处由于我们通过上面的汇编,知道只有遍历完70个字符才会停止,所以一直都是jnz
66
			}
67
			if(S[i] == 16)
68
			{
69
				fprintf(fp,"%d\tand Rx 80h\n",i);
70
				fprintf(fp,"\ttest Rx Rx\n");
71
				fprintf(fp,"\tjz %d \n",S[i+1]);
72
				i+=2;//同上,但是此处没用到貌似
73
			}
74
			if(S[i] == 17)
75
			{
76
				fprintf(fp,"%d\tinc R%d\n",i,S[i+1]-1);
77
				i+=2;
78
			}
79
			if(S[i] == 18)
80
			{
81
				fprintf(fp,"%d\tdec R%d\n",i,S[i+1]-1);
82
				i+=2;
83
			}
84
			if(S[i] == 19)
85
			{
86
				fprintf(fp,"%d\tadd R%d %d\n",i,S[i+1]-1,S[i+2]);
87
				i+=3;
88
			}
89
			if(S[i] == 20)
90
			{
91
				fprintf(fp,"%d\tsub R%d R%d\n",i,S[i+1]-1,S[i+2]-1);
92
				i+=3;
93
94
			}
95
			if(S[i] == 21)
96
			{
97
				fprintf(fp,"%d\txor R%d %d\n",i,S[i+1]-1,S[i+2]);
98
				i+=3;
99
			}
100
			if(S[i] ==22)
101
			{
102
				fprintf(fp,"%d\tand R%d R%d\n",i,S[i+1]-1,S[i+2]-1);
103
				i+=3;
104
105
			}
106
			if(S[i] == 23)
107
			{
108
				fprintf(fp,"%d\tor R%d R%d\n",i,S[i+1]-1,S[i+2]-1);
109
				i+=3;
110
			}
111
			if(S[i] == 25)
112
			{
113
				fprintf(fp,"%d\tmov R%d R%d\n",i,S[i+1]-1,S[i+2]-1);
114
				i+=3;
115
			}
116
			if(S[i] == 26)
117
			{
118
				fprintf(fp,"%d\tlea R%d [R%d]\n",i,S[i+1]-1,S[i+2]-1);
119
				i+=3;
120
			}
121
			if(S[i] == 27)
122
			{
123
				fprintf(fp,"%d\tmov R%d [R%d]\n",i,S[i+1]-1,S[i+2]-1);
124
				i+=3;
125
			}
126
			if(S[i] == 28)
127
			{
128
				fprintf(fp,"%d\tmov [R%d] R%d\n",i,S[i+1]-1,S[i+2]-1);
129
				i+=3;
130
			}
131
			if(S[i] == 29)
132
			{
133
				fprintf(fp,"%d\tmul R%d %d\n",i,S[i+1]-1,S[i+2]);
134
				i+=3;
135
			}
136
			if(S[i]==100)
137
			{
138
				fprintf(fp,"%d\tRet\n",i);
139
				printf("-------Ret--------\n");
140
				fclose(fp); 
141
			}
142
		}
143
}

第二步,翻译生成的汇编语言为运算方法,这段汇编很简单,显而易见:
汇编段:

1
0	mov R0 0
2
3	mov R2 70
3
6	jmp 21
4
8	push R0
5
10	pop R1
6
12	R0 = getchar()
7
13	push R0
8
15	push R1
9
17	pop R0
10
19	inc R0
11
21	if R0==R2
12
		mov Rx 0x80
13
	if R0 <R2
14
		mov Rx 0x40
15
	if R0 >R2
16
		mov Rx 0x20
17
24	and Rx 80h
18
	test Rx Rx
19
	jnz 8
20
26	mov R0 0
21
29	mov R2 71
22
32	jmp 70
23
34	push R0
24
36	lea R1 [R5]
25
39	mul R0 4
26
42	sub R1 R0
27
45	mov R0 R1
28
48	mov R0 [R0]
29
51	mul R0 110
30
54	add R0 99
31
57	xor R0 116
32
60	add R0 102
33
63	mov [R1] R0
34
66	pop R0
35
68	inc R0
36
70	if R0==R2
37
		mov Rx 0x80
38
	if R0 <R2
39
		mov Rx 0x40
40
	if R0 >R2
41
		mov Rx 0x20
42
73	and Rx 80h
43
	test Rx Rx
44
	jnz 34
45
75	Ret
1
逆运算:(((P[i]-102)^116)-99)/110

第三步,进行逆运算

1
#include<stdio.h>
2
int main()
3
{
4
	int P[]={	
5
	0x36D3, 0x2AFF, 0x2ACB, 0x2B95, 0x2B95, 0x2B95, 0x169F, 0x186D,    
6
	0x18D7, 0x1611, 0x18D7, 0x2B95, 0x2C23, 0x2CA9, 0x1611, 0x1611,    
7
	0x18D7, 0x2AFF, 0x1849, 0x18FB, 0x2ACB, 0x2A71, 0x1735, 0x18D7,    
8
	0x1611, 0x2ACB, 0x15DD, 0x18D7, 0x2C23, 0x169F, 0x15DD, 0x2B95,    
9
	0x169F, 0x156B, 0x186D, 0x2AFF, 0x1611, 0x1611, 0x15DD, 0x2AFF,    
10
	0x2C23, 0x2ACB, 0x15DD, 0x15DD, 0x186D, 0x1849, 0x2B95, 0x156B,    
11
	0x1735, 0x18FB, 0x18FB, 0x2A71, 0x2AFF, 0x1735, 0x2C23, 0x15DD,    
12
	0x18D7, 0x2A71, 0x18D7, 0x18D7, 0x2C23, 0x2AFF, 0x156B, 0x2C23,    
13
	0x169F, 0x35AF, 0x2CA9, 0x32B5, 0x2AFF, 0x3039};
14
	for(int i=69;i>=0;i--)
15
		 printf("%c",(((P[i]-102)^116)-99)/110);
16
17
18
}

最后flag为:

1
nctf{3e1ce77b70e4cb9941d6800aec022c813d03e70a274ba96c722fed72783dddac}

下载:源代码 DeCrypto ASM

Contents
  1. 1. NCTF_WcyVM
    1. 1.1. 分析
    2. 1.2. 思路
    3. 1.3. 利用
|