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