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 |  | 
| 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 |  | 
| 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} | 
