'); }

2021羊城杯_EasyVM


分析

主函数

int __cdecl main()
{
  int v1; // [esp+18h] [ebp-10h]

  sub_80486BB();
  v1 = sub_8048F45();
  *(_DWORD *)(v1 + 32) = &unk_804B0C0;
  sub_80491C8();                                // 类似于xxtea,Smc自解密
  ((void (__cdecl *)(int))sub_80487A8)(v1);
  return 0;
}

依旧用了Smc自解密,这次是类似xxtea解密,思路一样,直接动调就能看到真正的加密函数内容

image-20210917213645563这里a[8]获取值,然后确定加密流程,我们需要找到a[8]的值都是从哪里”进货”的,在第一次比较开始之前,肯定会有一个赋值的操作,找到”进货点”,进而确定流程。

image-20210917214133673

既然有了“进货流程”,那就直接跟着走一遍,然后再具体分析过程中做了什么,自己人脑分析太费脑+费时,用脚本代替人脑分析:

其中有个坑点,就是在分析if(a2[8] == 0x80)内操作的时候, a2[sub_804875F(a2, 1)] = *(_DWORD *)(a2[8] + 2);是将四个字节赋值给了前面参数,所以我们在脚本中,需要注意一下这个点

a=[0xA1, 0xC1, 0x00, 0xB1, 0x77, 0xC2, 0x4A, 0x01, 0x00, 0x00,
  0xC1, 0x01, 0xB2, 0x77, 0xC2, 0x19, 0x01, 0x00, 0x00, 0xC1,
  0x02, 0xB4, 0x77, 0xC2, 0xDD, 0x01, 0x00, 0x00, 0xC1, 0x03,
  0xB3, 0x77, 0xC2, 0x0F, 0x01, 0x00, 0x00, 0xC1, 0x04, 0xB2,
  0x77, 0xC2, 0x1B, 0x01, 0x00, 0x00, 0xC1, 0x05, 0xB4, 0x77,
  0xC2, 0x89, 0x01, 0x00, 0x00, 0xC1, 0x06, 0xB1, 0x77, 0xC2,
  0x19, 0x01, 0x00, 0x00, 0xC1, 0x07, 0xB3, 0x77, 0xC2, 0x54,
  0x01, 0x00, 0x00, 0xC1, 0x08, 0xB1, 0x77, 0xC2, 0x4F, 0x01,
  0x00, 0x00, 0xC1, 0x09, 0xB1, 0x77, 0xC2, 0x4E, 0x01, 0x00,
  0x00, 0xC1, 0x0A, 0xB3, 0x77, 0xC2, 0x55, 0x01, 0x00, 0x00,
  0xC1, 0x0B, 0xB3, 0x77, 0xC2, 0x56, 0x01, 0x00, 0x00, 0xC1,
  0x0C, 0xB4, 0x77, 0xC2, 0x8E, 0x00, 0x00, 0x00, 0xC1, 0x0D,
  0xB2, 0x77, 0xC2, 0x49, 0x00, 0x00, 0x00, 0xC1, 0x0E, 0xB3,
  0x77, 0xC2, 0x0E, 0x01, 0x00, 0x00, 0xC1, 0x0F, 0xB1, 0x77,
  0xC2, 0x4B, 0x01, 0x00, 0x00, 0xC1, 0x10, 0xB3, 0x77, 0xC2,
  0x06, 0x01, 0x00, 0x00, 0xC1, 0x11, 0xB3, 0x77, 0xC2, 0x54,
  0x01, 0x00, 0x00, 0xC1, 0x12, 0xB2, 0x77, 0xC2, 0x1A, 0x00,
  0x00, 0x00, 0xC1, 0x13, 0xB1, 0x77, 0xC2, 0x42, 0x01, 0x00,
  0x00, 0xC1, 0x14, 0xB3, 0x77, 0xC2, 0x53, 0x01, 0x00, 0x00,
  0xC1, 0x15, 0xB1, 0x77, 0xC2, 0x1F, 0x01, 0x00, 0x00, 0xC1,
  0x16, 0xB3, 0x77, 0xC2, 0x52, 0x01, 0x00, 0x00, 0xC1, 0x17,
  0xB4, 0x77, 0xC2, 0xDB, 0x00, 0x00, 0x00, 0xC1, 0x18, 0xB1,
  0x77, 0xC2, 0x19, 0x01, 0x00, 0x00, 0xC1, 0x19, 0xB4, 0x77,
  0xC2, 0xD9, 0x00, 0x00, 0x00, 0xC1, 0x1A, 0xB1, 0x77, 0xC2,
  0x19, 0x01, 0x00, 0x00, 0xC1, 0x1B, 0xB3, 0x77, 0xC2, 0x55,
  0x01, 0x00, 0x00, 0xC1, 0x1C, 0xB2, 0x77, 0xC2, 0x19, 0x00,
  0x00, 0x00, 0xC1, 0x1D, 0xB3, 0x77, 0xC2, 0x00, 0x01, 0x00,
  0x00, 0xC1, 0x1E, 0xB1, 0x77, 0xC2, 0x4B, 0x01, 0x00, 0x00,
  0xC1, 0x1F, 0xB2, 0x77, 0xC2, 0x1E, 0x00, 0x00, 0x00, 0xC1,
  0x20, 0x80, 0x02, 0x18, 0x00, 0x00, 0x00, 0x23, 0x10, 0xC1,
  0x21, 0x80, 0x02, 0x10, 0x00, 0x00, 0x00, 0x23, 0xF7, 0xC1,
  0x22, 0x80, 0x02, 0x08, 0x00, 0x00, 0x00, 0x23, 0xF7, 0xC1,
  0x23, 0xF7, 0xFE, 0x80, 0x02, 0x05, 0x00, 0x00, 0x00, 0x22,
  0x77, 0x10, 0x80, 0x02, 0x07, 0x00, 0x00, 0x00, 0x23, 0x80,
  0x02, 0x23, 0x77, 0xF1, 0x98, 0x31, 0x77, 0x10, 0x80, 0x02,
  0x18, 0x00, 0x00, 0x00, 0x23, 0x80, 0x02, 0x20, 0xB9, 0xE4,
  0x35, 0x31, 0x77, 0x10, 0x80, 0x02, 0x12, 0x00, 0x00, 0x00,
  0x22, 0x77, 0xA0, 0xC1, 0x24, 0x80, 0x02, 0x18, 0x00, 0x00,
  0x00, 0x23, 0x10, 0xC1, 0x25, 0x80, 0x02, 0x10, 0x00, 0x00,
  0x00, 0x23, 0xF7, 0xC1, 0x26, 0x80, 0x02, 0x08, 0x00, 0x00,
  0x00, 0x23, 0xF7, 0xC1, 0x27, 0xF7, 0xFE, 0x32, 0x20, 0x43,
  0x33, 0x77, 0x80, 0x02, 0x11, 0x00, 0x00, 0x00, 0x22, 0x35,
  0x37, 0x38, 0x77, 0x80, 0x02, 0x0D, 0x00, 0x00, 0x00, 0x23,
  0x77, 0x38, 0x39, 0x10, 0x32, 0x20, 0x43, 0x33, 0x77, 0x80,
  0x02, 0x11, 0x00, 0x00, 0x00, 0x22, 0x35, 0x37, 0x38, 0x77,
  0x80, 0x02, 0x0D, 0x00, 0x00, 0x00, 0x23, 0x77, 0x38, 0x39,
  0xC7, 0xC1, 0x28, 0x80, 0x02, 0x18, 0x00, 0x00, 0x00, 0x23,
  0x10, 0xC1, 0x29, 0x80, 0x02, 0x10, 0x00, 0x00, 0x00, 0x23,
  0xF7, 0xC1, 0x2A, 0x80, 0x02, 0x08, 0x00, 0x00, 0x00, 0x23,
  0xF7, 0xC1, 0x2B, 0xF7, 0xFE, 0x32, 0x20, 0x43, 0x33, 0x77,
  0x80, 0x02, 0x11, 0x00, 0x00, 0x00, 0x22, 0x35, 0x37, 0x38,
  0x77, 0x80, 0x02, 0x0D, 0x00, 0x00, 0x00, 0x23, 0x77, 0x38,
  0x39, 0x10, 0x32, 0x20, 0x43, 0x33, 0x77, 0x80, 0x02, 0x11,
  0x00, 0x00, 0x00, 0x22, 0x35, 0x37, 0x38, 0x77, 0x80, 0x02,
  0x0D, 0x00, 0x00, 0x00, 0x23, 0x77, 0x38, 0x39, 0xC8, 0x99]
i=0
print("Start")
while True:
    #0x71,0x41,0x44,0x76,0x30,0x34,0xA4实际上都不用写,因为a数组中并没有这些数
    if(a[i]==0x71):
        print("a[6]-=4,a[6]=0x%x"%(a[i+1]))
        i+=5
    if(a[i]==0x41):
        print("a[1]+=a[2]")
        i+=1
    if(a[i]==0x42):
        print("a[1]-=a[4]")
        i+=1
    if(a[i]==0x43):
        print("a[1]*=a[3]")
        i+=1
    if(a[i]==0x37):
        print("a[1]=a[5]")
        i+=1
    if(a[i]==0x38):
        print("a[1]^=a[4]")
        i+=1
    if(a[i]==0x39):
        print("a[1]^=a[5]")
        i+=1
    if(a[i]==0x35):
        print("a[5]=a[1]")
        i+=1
    if(a[i]==0xF7):
        print("a[9]+=a[1]")
        i+=1
    if(a[i]==0x44):
        print("a[1]/=a[5]")
        i+=1
    if(a[i]==0x80):    #最大的坑点,如果没有注意到,直接努力白费(小端存储
        print("a[%d]=0X%x%x%x%x"%(a[i+1],a[i+5],a[i+4],a[i+3],a[i+2]))
        i+=6
    if(a[i]==0x77):
        print("a[1]^=a[9]")
        i+=1
    if(a[i]==0x53):
        print("putchar(a[3])")
        i+=2
    if(a[i]==0x22):
        print("a[1]>>=a[2]")
        i+=1
    if(a[i]==0x23):
        print("a[1]<<=a[2]")
        i+=1
    if(a[i]==0x99):
        print("break")
        break
    if(a[i]==0x76):
        print("a[3]=a[6],a[6]=0,a[6]+=4")
        i+=5
    if(a[i]==0x54):
        print("v2=a[3],v2=getchar()")
        i+=2
    if(a[i]==0x30):
        print("a[1]|=a[2]")
        i+=1
    if(a[i]==0x31):
        print("a[1]&=a[2]")
        i+=1
    if(a[i]==0x32):
        print("a[3]=0x%x"%(a[i+1]))
        i+=2
    if(a[i]==9):
        print("a[1]=0x6FEBF867")
        i+=1
    if(a[i]==0x10):
        print("a[9]=a[1]")
        i+=1
    if(a[i]==0x33):
        print("a[4]=a[1]")
        i+=1
    if(a[i]==0x34):
        print("a[2]=0x%x"%(a[i+1]))
        i+=2
    if(a[i]==0xFE):
        print("a[1]=a[9]")
        i+=1
    if(a[i]==0x11):
        print("a[1]=*******")
        i+=1
    if(a[i]==0xA0):
        print("if(a[1]!=0x6FEBF967)\n")
        i+=1
    if(a[i]==0xA1):
        print("read()")
        i+=1
    if(a[i]==0xB1):
        print("a[9]=byte0")
        i+=1
    if(a[i]==0xB2):
        print("a[9]=byte1")
        i+=1
    if(a[i]==0xA4):
        print("byte0[%d]=a[1]"%(a[i+1]))
        i+=4
    if(a[i]==0xB3):
        print("a[9]=byte2")
        i+=1
    if(a[i]==0xB4):
        print("a[9]=byte3")
        i+=1
    if(a[i]==0xC1):
        print("a[1]=flag[%d]"%(a[i+1]))
        i+=2
    if(a[i]==0xC7):
        print("if(byte_804B060!=a[1])\n")
        i+=1
    if(a[i]==0xC8):
        print("if(byte_804B064!=a[1])\n")
        i+=1
    if(a[i]==0xC2):
        print("if(0x%x!=a[1])\n"%(a[i+1]))
        i+=5

为了更好分析,在每次if后都加了一个换行

其中byte0~3:

image-20210917214813251

可以很明显的知道前32位都是进行一次简单的异或,但是异或是从一个数组里面取出的,而且每次异或的下标没有规律(也许是我找不出来),人为提取,获取前32位值:

xor=[0x7B,0x2F,0x37,0xE8]    #异或值,即上面的byte0~3
xor_number=[0,1,3,2,1,3,0,2,0,0,2,2,3,1,2,0,2,2,1,0,2,0,2,3,0,3,0,2,1,2,0,1]
cmp=[74,25,221,15,27,137,25,84,79,78,85,86,142,73,14,75,6,84,26,66,83,31,82,219,25,217,25,85,25,0,75,30]
for i in range(len(cmp)):
  print(chr(cmp[i]^xor[xor_number[i]]),end="")
#answer:16584abc45baff901c59dde3b1bb6701

后面部分进行位运算什么的,由于出现了位运算等容易丢失数据的操作,直接爆破。

一点一点的爆破,四个进行爆破就花了两分多钟,分别爆破获得内容:

我确定不了范围,就直接选择的(32,127),能确定范围可以减少很多时间

#flag[32]~flag[35]
for i in range(32,127):                    
  for j in range(32,127): 
    for k in range(32,127): 
      for l in range(32,127): 
        a=[0]*10
        a[1]=i
        a[2]=24
        a[1]<<=a[2]
        a[9]=a[1]
        a[1]=j
        a[2]=16
        a[1]<<=a[2]
        a[9]=(a[9]+a[1])&0xffffffff
        a[1]=k
        a[2]=8
        a[1]<<=a[2]
        a[9]=(a[9]+a[1])&0xffffffff
        a[1]=l
        a[9]+=a[1]
        a[1]=a[9]
        a[2]=5
        a[1]>>=a[2]
        a[1]^=a[9]
        a[9]=a[1]
        a[2]=7
        a[1]<<=a[2]
        a[2]=0x98f17723
        a[1]&=a[2]
        a[1]^=a[9]
        a[9]=a[1]
        a[2]=24
        a[1]<<=a[2]
        a[2]=0x35e4b920
        a[1]&=a[2]
        a[1]^=a[9]
        a[9]=a[1]
        a[2]=18
        a[1]>>=a[2]
        a[1]^=a[9]
        if(a[1]&0xffffffff==0x6FEBF967):    
          print(chr(i)+chr(j)+chr(k)+chr(l))
 #answer:a254

这里就不放后面爆破代码了,直接给结果

提示一下,一定要注意溢出问题,不然得不到后续答案的

#flag[36]~flag[39]:b06c
#flag[40]~flag[41]:dc23

拼接起来,加个外壳,获得flag:

SangFor{16584abc45baff901c59dde3b1bb6701a254b06cdc23}

  目录