这一道题目也确实写了好几个小时,也看了很多师傅们的博客,但感觉都不是很详细。在这里我给出我的详细思路供大家参考,希望对各位师傅们入门vm逆向有帮助,如有不足,希望各位师傅们包容!
一、查壳
使用exiinfo PE进行查壳:
发现是32位,Gcc编译,无壳,拖入ida32进行反汇编;
二、主函数分析
进入后按F5反汇编后,如下三个函数:
1、_main函数,点进入后发现是初始化;
2、qmemcpy将unk_403040处的数据写到v4处,写入0x1C8长度的数据;
单击byte_403040后,点击edit-array-OK后即可出现如下形式(仅便于观察,不改也可以)
我们可以先导出数据,以便后续操作;左上角菜单 edit—export_data后如下:
此后点击Export后,即可导出,可在同一目录查看txt文件.
3、vm_operad函数也就是我们主要分析的函数了,很明显v4,也就是上面的byte_403040是我们的操作码;
接下来我们进入vm_operad函数进行查看;
三、vm_operad函数分析:
代码如下:
int __cdecl vm_operad(int *a1, int a2) { int result; // eax char Str[200]; // [esp+13h] [ebp-E5h] BYREF char v4; // [esp+DBh] [ebp-1Dh] int v5; // [esp+DCh] [ebp-1Ch] int v6; // [esp+E0h] [ebp-18h] int v7; // [esp+E4h] [ebp-14h] int v8; // [esp+E8h] [ebp-10h] int v9; // [esp+ECh] [ebp-Ch] v9 = 0; v8 = 0; v7 = 0; v6 = 0; v5 = 0; while ( 1 ) { result = v9; if ( v9 >= a2 ) return result; switch ( a1[v9] ) { case 1: Str[v6 + 100] = v4; ++v9; ++v6; ++v8; break; case 2: v4 = a1[v9 + 1] + Str[v8]; v9 += 2; break; case 3: v4 = Str[v8] - LOBYTE(a1[v9 + 1]); v9 += 2; break; case 4: v4 = a1[v9 + 1] ^ Str[v8]; v9 += 2; break; case 5: v4 = a1[v9 + 1] * Str[v8]; v9 += 2; break; case 6: ++v9; break; case 7: if ( Str[v7 + 100] != a1[v9 + 1] ) { printf("what a shame..."); exit(0); } ++v7; v9 += 2; break; case 8: Str[v5] = v4; ++v9; ++v5; break; case 10: read(Str); ++v9; break; case 11: v4 = Str[v8] - 1; ++v9; break; case 12: v4 = Str[v8] + 1; ++v9; break; default: continue; } } }在这里需要注意几个点:
(1)这里的a1是int类型的指针(从函数名参数类型可得),但是我们的byte_403040是单字节,所以我们需要把它4个字节一个单位,小端转变为int类型;
(2)我们的操作数(就是case 1~12)中的中间变量v4是char类型的,所以逆向的时候需要再操作后进行 & 0xff 进行取低位,如果我们逆向中使用的中间变量是int类型的,反而会出现负数(因为我就卡到这里了)。
1、(1)接着我们分析函数,先看case 10,点进去后发现是获取输入,存放在str数组中,长度为15:
size_t __cdecl read(char *Str) { size_t result; // eax printf("string:"); scanf("%s", Str); result = strlen(Str); if ( result != 15 ) { puts("WRONG!\n"); exit(0); } return result; }(2)case 1 时是将v4写入我们的缓存,也就是Str[v6 + 100]的地方(此数组的前100作为临时运行区,此后的作为缓存,即输出区)
(3)case 2,3,4,5后·:对v4赋值str[v8]和操作数的运算(a1[v9+1],我们此次遇见2,3,4,5的是指令,即a1[v9],那么紧接的就是我们的操作数,至于为什么?是因为我们的v9+=2了),后面v9+=2(就是等于PC计数器加2,PC计数器指向下一个执行的指令,所以那么中间隔过去的就是我们的操作数,即a1[v9+1])
(4)case 6,11,12同上类似,不过这里的操作数是1,所以我们的v9(也就是PC计数器)只增加1;
(5)case 7,是 Str[v7 + 100] 与 a1[v9 + 1] 的比较,如果相等,通过;所以这里的a1[v9 + 1]就是我们的密文cipher了,我们可以待会再处理完机器码后再去得到;
(6)case 8,是改变我们的输入str[ v8 ]为中间值,很明显,这是一个重要的改变,待会我会通过正向分析来解密如何处理;
2、好了,分析那么久,逻辑我们还是不知道,不过在这之前我们先把机器码写脚本跑出来:
vm_operad=[10, 0, 0, 0, 4, 0, 0, 0, 16, 0, 0, 0, 8, 0, 0, 0, 3, 0, 0, 0, 5, 0, 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, 32, 0, 0, 0, 8, 0, 0, 0, 5, 0, 0, 0, 3, 0, 0, 0, 1, 0, 0, 0, 3, 0, 0, 0, 2, 0, 0, 0, 8, 0, 0, 0, 11, 0, 0, 0, 1, 0, 0, 0, 12, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 1, 0, 0, 0, 5, 0, 0, 0, 3, 0, 0, 0, 8, 0, 0, 0, 3, 0, 0, 0, 33, 0, 0, 0, 1, 0, 0, 0, 11, 0, 0, 0, 8, 0, 0, 0, 11, 0, 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, 9, 0, 0, 0, 8, 0, 0, 0, 3, 0, 0, 0, 32, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 81, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 36, 0, 0, 0, 1, 0, 0, 0, 12, 0, 0, 0, 8, 0, 0, 0, 11, 0, 0, 0, 1, 0, 0, 0, 5, 0, 0, 0, 2, 0, 0, 0, 8, 0, 0, 0, 2, 0, 0, 0, 37, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 54, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 65, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 32, 0, 0, 0, 8, 0, 0, 0, 5, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 5, 0, 0, 0, 3, 0, 0, 0, 8, 0, 0, 0, 2, 0, 0, 0, 37, 0, 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, 9, 0, 0, 0, 8, 0, 0, 0, 3, 0, 0, 0, 32, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 65, 0, 0, 0, 8, 0, 0, 0, 12, 0, 0, 0, 1, 0, 0, 0, 7, 0, 0, 0, 34, 0, 0, 0, 7, 0, 0, 0, 63, 0, 0, 0, 7, 0, 0, 0, 52, 0, 0, 0, 7, 0, 0, 0, 50, 0, 0, 0, 7, 0, 0, 0, 114, 0, 0, 0, 7, 0, 0, 0, 51, 0, 0, 0, 7, 0, 0, 0, 24, 0, 0, 0, 7, 0, 0, 0, 167, 255, 255, 255, 7, 0, 0, 0, 49, 0, 0, 0, 7, 0, 0, 0, 241, 255, 255, 255, 7, 0, 0, 0, 40, 0, 0, 0, 7, 0, 0, 0, 132, 255, 255, 255, 7, 0, 0, 0, 193, 255, 255, 255, 7, 0, 0, 0, 30, 0, 0, 0, 7, 0, 0, 0, 122, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] vm_t=[] for i in range(0,len(vm_operad),4): b = bytes(vm_operad[i:i + 4]) vm_t.append(int.from_bytes(b, byteorder='little', signed=True)) #小端4字节转化为int #print(vm_t)跑出来结果如下:
[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, -89, 7, 49, 7, -15, 7, 40, 7, -124, 7, -63, 7, 30, 7, 122, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
(在此处,如果有人看到另一位师傅的博客说1是16个,7是15个,不要疑惑,因为其中一个1是操作数,而不是指令本身,所以指令1还是15个)
分析后可以发现10(输入)后,紧接着是一个8,一个1,这是个重要的地方;
接着发现7都是在后面,每个7后面一个操作数,这就是cipher;我们跑出cipher的值:
vm=[] for each in vm_t: if each != 0: vm.append(each) cipher=list() for i in range(len(vm)): if vm[i]==7: cipher.append(vm[i+1])结果:cipher=[34, 63, 52, 50, 114, 51, 24, -89, 49, -15, 40, -124, -63, 30, 122]
3.接着我们分析运行逻辑,就那我们操作码的取一部分分析(以为都是8,1的循环),上面的红色部分:
————case 4, v4 = a1[v9 + 1] ^ Str[v8];
v9 += 2;
改变v[ 4 ],操作数就是 16,str[ v8 ]是我们的第一个输入;
————-——case 8. Str[v5] = v4;
++v9;
++v5;
把我们的第一个输入改成v4,然后v5加1,打算改变下一个输入;
——————case 3,5
就是对v4的赋值,自行分析
——————case 1
Str[v6 + 100] = v4;
++v9;
++v6;
++v8;
把v4写入缓存,等待最后和cipher[ 0 ]比较;
到这里我们的逻辑很清晰了,每一个8 ,1是一个循环,我们逆向的思路也很清晰,对操作码逆序后,遇到1后,他的下一个运算操作(就是2,3,4,5,6,11,12)cipher【 i ]就是v4,我们进行逆运算,得到str[v8] 存到temp。但是这个【v8】不是我们的输入,还记得case 8 吗?改变了我们的str【8】,所以我们继续运行,遇到8后,他的下一个运算操作,我们逆运算后,得到str[8]就是我们的输入;接着就是循环了。
这里我的方法是设定两个bool变量sign2和sign,标志遇到1和8;代码如下:
k是cipher的下标,code_index是我们逆序后的指令下标(在下一部分给出)
flag=[] temp=0 sign2=False sign=False k=0 cipher.reverse() for i in code_index: if i<len(vm): match vm[i]: case 1: sign2 = True case 2: if sign2: temp=(cipher[k]-vm[i+1]) & 0xff sign2=False k += 1 if sign: flag.append((temp-vm[i+1]) & 0xff) sign=False case 3: if sign2: temp = (cipher[k] + vm[i + 1] & 0xff) & 0xff sign2 = False k += 1 if sign: flag.append((temp+vm[i + 1] & 0xff) & 0xff) sign=False case 4: if sign2: temp = (cipher[k] ^ vm[i + 1]) & 0xff sign2 = False k += 1 if sign: flag.append((temp^vm[i + 1]) & 0xff) sign=False case 5: if sign2: temp=(cipher[k]//vm[i + 1]) & 0xff sign2 = False k += 1 if sign: flag.append((temp//vm[i + 1]) & 0xff) sign=False case 8: sign=True case 11: if sign2: temp=(cipher[k]+1) & 0xff sign2 = False k += 1 if sign: flag.append((temp+1) & 0xff) sign=False case 12: if sign2: temp=(cipher[k]-1) & 0xff sign2 = False k += 1 if sign: flag.append((temp-1) & 0xff) sign=False case _: continue flag.reverse() f="" for each in flag: f+=chr(each) print(f"flag{{{f}}}")4、我们需要解决我们执行的指令都是哪些?不然怎么逆序呢,这里我给出的方法是正向执行一边,记录下标即可;代码如下:
code_index=list() ################################## #在这里给出ip实际执行的指令 i=0 while i<len(vm): match vm[i]: case 1: code_index.append(i) i+=1 case 2: code_index.append(i) i+=2 case 3: code_index.append(i) i+=2 case 4: code_index.append(i) i+=2 case 5: code_index.append(i) i+=2 case 6: code_index.append(i) i+=1 case 7: code_index.append(i) i+=2 case 8: code_index.append(i) i+=1 case 10: code_index.append(i) i+=1 case 11: code_index.append(i) i+=1 case 12: code_index.append(i) i+=1 case _: i+=1 continue code_index.reverse() ##################################四、结语
最后给出完整代码:
vm_operad=[10, 0, 0, 0, 4, 0, 0, 0, 16, 0, 0, 0, 8, 0, 0, 0, 3, 0, 0, 0, 5, 0, 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, 32, 0, 0, 0, 8, 0, 0, 0, 5, 0, 0, 0, 3, 0, 0, 0, 1, 0, 0, 0, 3, 0, 0, 0, 2, 0, 0, 0, 8, 0, 0, 0, 11, 0, 0, 0, 1, 0, 0, 0, 12, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 1, 0, 0, 0, 5, 0, 0, 0, 3, 0, 0, 0, 8, 0, 0, 0, 3, 0, 0, 0, 33, 0, 0, 0, 1, 0, 0, 0, 11, 0, 0, 0, 8, 0, 0, 0, 11, 0, 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, 9, 0, 0, 0, 8, 0, 0, 0, 3, 0, 0, 0, 32, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 81, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 36, 0, 0, 0, 1, 0, 0, 0, 12, 0, 0, 0, 8, 0, 0, 0, 11, 0, 0, 0, 1, 0, 0, 0, 5, 0, 0, 0, 2, 0, 0, 0, 8, 0, 0, 0, 2, 0, 0, 0, 37, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 54, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 65, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 32, 0, 0, 0, 8, 0, 0, 0, 5, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 5, 0, 0, 0, 3, 0, 0, 0, 8, 0, 0, 0, 2, 0, 0, 0, 37, 0, 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, 9, 0, 0, 0, 8, 0, 0, 0, 3, 0, 0, 0, 32, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 65, 0, 0, 0, 8, 0, 0, 0, 12, 0, 0, 0, 1, 0, 0, 0, 7, 0, 0, 0, 34, 0, 0, 0, 7, 0, 0, 0, 63, 0, 0, 0, 7, 0, 0, 0, 52, 0, 0, 0, 7, 0, 0, 0, 50, 0, 0, 0, 7, 0, 0, 0, 114, 0, 0, 0, 7, 0, 0, 0, 51, 0, 0, 0, 7, 0, 0, 0, 24, 0, 0, 0, 7, 0, 0, 0, 167, 255, 255, 255, 7, 0, 0, 0, 49, 0, 0, 0, 7, 0, 0, 0, 241, 255, 255, 255, 7, 0, 0, 0, 40, 0, 0, 0, 7, 0, 0, 0, 132, 255, 255, 255, 7, 0, 0, 0, 193, 255, 255, 255, 7, 0, 0, 0, 30, 0, 0, 0, 7, 0, 0, 0, 122, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] vm_t=[] for i in range(0,len(vm_operad),4): b = bytes(vm_operad[i:i + 4]) vm_t.append(int.from_bytes(b, byteorder='little', signed=True)) #小端4字节转化为int #print(vm_t) ##################################### vm=[] for each in vm_t: if each != 0: vm.append(each) cipher=list() for i in range(len(vm)): if vm[i]==7: cipher.append(vm[i+1]) print(cipher) code_index=list() ################################## #在这里给出ip实际执行的指令 i=0 while i<len(vm): match vm[i]: case 1: code_index.append(i) i+=1 case 2: code_index.append(i) i+=2 case 3: code_index.append(i) i+=2 case 4: code_index.append(i) i+=2 case 5: code_index.append(i) i+=2 case 6: code_index.append(i) i+=1 case 7: code_index.append(i) i+=2 case 8: code_index.append(i) i+=1 case 10: code_index.append(i) i+=1 case 11: code_index.append(i) i+=1 case 12: code_index.append(i) i+=1 case _: i+=1 continue code_index.reverse() ################################## flag=[] temp=0 sign2=False sign=False k=0 cipher.reverse() for i in code_index: if i<len(vm): match vm[i]: case 1: sign2 = True case 2: if sign2: temp=(cipher[k]-vm[i+1]) & 0xff sign2=False k += 1 if sign: flag.append((temp-vm[i+1]) & 0xff) sign=False case 3: if sign2: temp = (cipher[k] + vm[i + 1] & 0xff) & 0xff sign2 = False k += 1 if sign: flag.append((temp+vm[i + 1] & 0xff) & 0xff) sign=False case 4: if sign2: temp = (cipher[k] ^ vm[i + 1]) & 0xff sign2 = False k += 1 if sign: flag.append((temp^vm[i + 1]) & 0xff) sign=False case 5: if sign2: temp=(cipher[k]//vm[i + 1]) & 0xff sign2 = False k += 1 if sign: flag.append((temp//vm[i + 1]) & 0xff) sign=False case 8: sign=True case 11: if sign2: temp=(cipher[k]+1) & 0xff sign2 = False k += 1 if sign: flag.append((temp+1) & 0xff) sign=False case 12: if sign2: temp=(cipher[k]-1) & 0xff sign2 = False k += 1 if sign: flag.append((temp-1) & 0xff) sign=False case _: continue flag.reverse() f="" for each in flag: f+=chr(each) print(f"flag{{{f}}}")运行后结果 flag{757515121f3d478}
我已经很详细地说了我的思路,希望能帮到各位师傅,谢谢!