3.16---网鼎杯2020青龙组----signal----vm逆向

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 一.vm逆向
  • 二. wp

一.vm逆向

  • 这里的虚拟机指的是一种解释执行系统或者模拟器
  • 将程序的代码转换自定义的操作码(opcode),然后在程序执行时再通过解释这些操作码,选择对应的函数执行,从而实现程序原有的功能。

二. wp

  • 查壳,32位无壳
  • 载入IDA
  • 找到main函数
    3.16---网鼎杯2020青龙组----signal----vm逆向_第1张图片
  • 逻辑简单,
  • qmemcpy就是IDA中的memcpy函数,即从内存中取值进行赋值,找到403040处,找到456字节的数据,shift+E提取出来
  • 4字节为一个整数,v4是int型
  • 整理一下得到:
  • v4=[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]
  • 然后就是vm_operad函数
  • 传入v4与114两个参数
  • 跟进
int __cdecl vm_operad(int *a1, int _114)
{
  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 >= _114 )
      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;
    }
  }
}

  • 关注case10,发现v4[v9]等于10时,读入字符串,且根据read函数发现字符串长度为15
  • 关注case7,str字符串与v4下一位进行比较,且必须相等,也就是说输入的字符串进行一系列操作数后,即加密后,与v4中7后的数据相等
  • 我们可以写个脚本提取出v4中7后的数据
str=[]
a1=[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, 255, 255, 255, 7, 49, 7, 241, 255, 255, 255, 7, 40, 7, 132, 255, 255, 255, 7, 193, 255, 255, 255, 7, 30, 7, 122]
for i in range(0,len(a1)):
          if a1[i]==7:
                    str.append(a1[i+1])

print(str)

  • 得到***[34, 63, 52, 50, 114, 51, 24, 167, 49, 241, 40, 132, 193, 30, 122]***
  • v4数组7前面的应该是操作数,而7后面的就是比较字符串,即加密后的str,那么我们只需要进行逆向操作即可
  • 下为python脚本
v4=[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, 255, 255, 255, 7, 49, 7, 241, 255, 255, 255, 7, 40, 7, 132, 255, 255, 255, 7, 193, 255, 255, 255, 7, 30, 7, 122]
a1 = [34, 63, 52, 50, 114, 51, 24, 167, 49, 241, 40, 132, 193, 30, 122]
a1.reverse()
v4.reverse()

v9 = 0
us = 0
v5 = 0
flag = []
for i in range(0, len(v4)):
    if i == len(v4) - 1:
        flag.append(us)

    elif v4[i] == 1 and v4[i - 1] != 1:
        v5 = a1[v9]
        v9 += 1
        flag.append(us)

    elif v4[i] == 2:
        if (v4[i + 1] != 3 and v4[i + 1] != 4 and v4[i + 1] != 5):
            us = v5 - v4[i - 1]

    elif v4[i] == 3:
        if (v4[i + 1] != 2 and v4[i + 1] != 4 and v4[i + 1] != 5):
            us = v5 + v4[i - 1]  

    elif v4[i] == 4:
        if (v4[i + 1] != 3 and v4[i + 1] != 2 and v4[i + 1] != 5):
            us = v5 ^ v4[i - 1]

    elif v4[i] == 5:
        if (v4[i + 1] != 3 and v4[i + 1] != 4 and v4[i + 1] != 2):
            us = int(v5 / v4[i - 1])
    elif v4[i] == 8:
        v5 = us

    elif v4[i] == 11:
        us = v5 + 1

    elif v4[i] == 12:
        us = v5 - 1
flag.reverse()
out = ''
for j in flag:
    out += chr(j)
print(out)

  • 解得flag{757515121f3d478}
  • 或者可以正向爆破
#include 
#include 
 
int main(void)
{
	int key[] = {34, 63, 52, 50, 114, 51, 24, 167, 49, 241, 40, 132, 193, 30, 122};
	int a1[] ={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, 255, 255, 255, 7, 49, 7, 241, 255, 255, 255, 7, 40, 7, 132, 255, 255, 255, 7, 193, 255, 255, 255, 7, 30, 7, 122};
	int v9 = 0;	
	int v4 = 0; 	
	int i, j;
	int flag;
	int index = 0;
	
	for ( i = 0; i < 15; i++, index++)	
	{
		for ( j = 48; j < 123; j++)
		{	
			flag = j;	
			v9 = index;
			
			while ( a1[v9] != 1)
  			{
				switch ( a1[v9] )
    			{
      				case 2:
        				v4 = a1[v9 + 1] + flag;
        				v9 += 2;
        				break;
      				case 3:
        				v4 = flag - a1[v9 + 1];
        				v9 += 2;
        				break;
      				case 4:
        				v4 = a1[v9 + 1] ^ flag;
        				v9 += 2;
        				break;
      				case 5:
        				v4 = a1[v9 + 1] * flag;
        				v9 += 2;
        				break;
      				case 6:
        				++v9;
        				break;
      				case 8:
        				flag = v4;
        				++v9;
        				break;
      				case 11:
       					v4 = flag - 1;
        				++v9;
        				break;
      				case 12:
        				v4 = flag + 1;
        				++v9;
        				break;
      				default:
        				continue;
    			}	
			} 
			if ( v4 == key[i])			
    		{
    			index = v9;			
    			printf("%c", j);
    			break;				
			}
		}
 	}
  	
  	return 0;
}
  • 同样解得flag{757515121f3d478}
  • 其实,个人认为正向爆破更舒服,更直观
  • 这里的switch代码较小,故可以逆向,如果代码很长,就只能正向爆破了

你可能感兴趣的:(学习)