直接IDA分析,将表0x403040拷贝到opcode_table中拷贝了456个字节,将opcode_table传入vm_operad中,并传入一个114,可以发现456时114的4倍,因此114就是opcode以int大小索引的长度
opcode表:[10,4,16,8,3,5,1,4,32,8,5,3,1,3,2,8,11,1,12,8,4,4,1,5,3,8,3,33,1,11,8,11,1,4,9,8,3,32,1,2,81,8,4,36,1,12,8,11,1,5,2,8,2,37,1,2,54,8,4,65,1,2,32,8,5,1,1,5,3,8,2,37,1,4,9,8,3,32,1,2,65,8,12,1,7,34,7,63,7,52,7,50,7,114,7,51,7,24,7,167,7,49,7,241,7,40,7,132,7,193,7,30,7,122]
进入vm_operad分析,可以发现从op表中取值进行不同的操作
这里有几个case还是比较好分析的,case10进入read函数跟进后发现就是输入,且输入的长度必须时15个,最终的值时放在v3中,可以确定v3数组就是保存就是input
case7也比较好分析,即使判断v4[v8]与opcode中的值进行判断,如果不满足则失败,这里通过对v8这个索引进行搜索,发现就这里一处带引用了,因此v4从一开始便要与opcode中的相等。
下面打印出opcode表,这里可以明显的看到从7开始的地方后面就是与v4比对的值,并且刚好15个
opcode表:[10,4,16,8,3,5,1,4,32,8,5,3,1,3,2,8,11,1,12,8,4,4,1,5,3,8,3,33,1,11,8,11,1,4,9,8,3,32,1,2,81,8,4,36,1,12,8,11,1,5,2,8,2,37,1,2,54,8,4,65,1,2,32,8,5,1,1,5,3,8,2,37,1,4,9,8,3,32,1,2,65,8,12,1,7,34,7,63,7,52,7,50,7,114,7,51,7,24,7,167,7,49,7,241,7,40,7,132,7,193,7,30,7,122]
有些操作数后面跟的数据而非运算数值
v4 需要满足:[34,63,52,50,114,51,24,167,49,241,40,132,193,30,122]
下面就有几个问题要分析:
通过分析case 1可以发现v4[v7]=v5,v4的产生是由v5赋值而来,v7初始化为0,其他并无赋值,由此可知,case1控着这v4从开始的生成,可以从op表中发现1就是有15个,该case中的v9,v5则可以判断处分别为:input的运算取值索引,v5运算结果。
接下来就要还原v3的计算流程的,根据opcode分析,这里可以知道case 2,3,4,5都是进行的op_code_table_index + 1取值op_code_table_index += 2迭代,因此这几个操作数后面跟的一定时运算的数值并非操作数
通过分析可以根据比较,写出脚本自动还原,(逆过程+逆运算)
public class Main { public static void main(String[] args) { char re[] = {34,63,52,50,114,51,24,167,49,241,40,132,193,30,122}; // int opcode[] = { //原始code // 4,16,8,3,5,1, // 4,32,8,5,3,1, // 3,2,8,11,1, // 12,8,4,4,1, // 5,3,8,3,33,1, // 11,8,11,1, // 4,9,8,3,32,1, // 2,81,8,4,36,1, // 12,8,11,1, // 5,2,8,2,37,1, // 2,54,8,4,65,1, // 2,32,8,5,1,1, // 5,3,8,2,37,1, // 4,9,8,3,32,1, // 2,65,8,12,1 // }; int opcode[] = { //调整code顺序 3,5,8,4,16,1, 5,3,8,4,32,1, 11,8,3,2,1, 4,4,8,12,1, 3,33,8,5,3,1, 11,8,11,1, 3,32,8,4,9,1, 4,36,8,2,81,1, 11,8,12,1, 2,37,8,5,2,1, 4,65,8,2,54,1, 5,1,8,2,32,1, 2,37,8,5,3,1, 3,32,8,4,9,1, 12,8,2,65,1 }; char tmp = 0; int tmp_index = 0; int opcode_index = 0; int re_index = 0; char flag[] = new char[15]; int flag_index = 0; while(opcode_index < 83){ switch(opcode[opcode_index]){ case 1: flag[flag_index] = tmp; ++opcode_index; ++flag_index; ++re_index; break; case 2: tmp = (char) (re[re_index] - (char)(opcode[opcode_index + 1]& 0xff)); opcode_index += 2; break; case 3: tmp = (char) (re[re_index] + (opcode[opcode_index + 1]& 0xff )); opcode_index += 2; break; case 4: tmp = (char) ((opcode[opcode_index + 1] & 0xff) ^ re[re_index]); opcode_index += 2; break; case 5: tmp = (char) (re[re_index] / (opcode[opcode_index + 1] & 0xff)); opcode_index += 2; break; case 6: ++opcode_index; break; case 8: re[tmp_index] = tmp; ++opcode_index; ++tmp_index; break; case 11: tmp = (char) (re[re_index] + 1); ++opcode_index; break; case 12: tmp = (char) (re[re_index] - 1); ++opcode_index; break; default: continue; } } System.out.println(new String(flag)); //757515121f3d478 } }
[培训]科锐逆向工程师培训班38期--远程教学预课班将于 2020年5月28日 正式开班!
最后于 4天前 被Craft_A编辑 ,原因: