天天看點

buu練題記錄7-[網鼎杯 2020 青龍組]singal

0x00 查殼

首先拿到的是一個exe檔案,是以先查殼,然後看看是32位還是64位的程式

buu練題記錄7-[網鼎杯 2020 青龍組]singal

32位的程式,無殼。進入IDA分析:

0x01 IDA分析

buu練題記錄7-[網鼎杯 2020 青龍組]singal

很容易就找到mian函數,邏輯簡單,就一個函數。直接進入函數分析:

int __cdecl vm_operad(int *a1, int a2)
{
  int result; // eax
  char v3[100]; // [esp+13h] [ebp-E5h]
  char v4[100]; // [esp+77h] [ebp-81h]
  char v5; // [esp+DBh] [ebp-1Dh]
  int v6; // [esp+DCh] [ebp-1Ch]
  int v7; // [esp+E0h] [ebp-18h]
  int v8; // [esp+E4h] [ebp-14h]
  int v9; // [esp+E8h] [ebp-10h]
  int v10; // [esp+ECh] [ebp-Ch]

  v10 = 0;
  v9 = 0;
  v8 = 0;
  v7 = 0;
  v6 = 0;
  while ( 1 )
  {
    result = v10;
    if ( v10 >= a2 )
      return result;
    switch ( a1[v10] )
    {
      case 1:                                   // 調用到1的時候就完成一個v4值得産生
        v4[v7] = v5;
        ++v10;
        ++v7;
        ++v9;
        break;
      case 2:
        v5 = a1[v10 + 1] + v3[v9];              // a1目前值得下一個值與v3目前值的和
        v10 += 2;
        break;
      case 3:
        v5 = v3[v9] - LOBYTE(a1[v10 + 1]);      // v3的目前值減去a1下一個值
        v10 += 2;
        break;
      case 4:
        v5 = a1[v10 + 1] ^ v3[v9];              // 異或
        v10 += 2;
        break;
      case 5:
        v5 = a1[v10 + 1] * v3[v9];              // 相乘
        v10 += 2;                               // 2,3,4,5下的運算,均為v10 += 2,既他們下一個的數的值即使在運算步驟的取值範圍内,也不作為運算步驟
        break;
      case 6:
        ++v10;
        break;
      case 7:
        if ( v4[v8] != a1[v10 + 1] )            // 之前産生的v4的值和a1中為7的值的下一個值比較
        {
          printf("what a shame...");
          exit(0);                              // 不同則停止程式
        }
        ++v8;
        v10 += 2;
        break;
      case 8:
        v3[v6] = v5;                            // 将目前運算的暫時的結果覆寫到目前的v3進行接來下的運算,直到調用1,産生新的v4,取下一個v3進入運算
        ++v10;
        ++v6;
        break;
      case 10:
        read(v3);                               // 寫入v3,既将input傳入v3
        ++v10;
        break;
      case 11:
        v5 = v3[v9] - 1;                        // 減1
        ++v10;
        break;
      case 12:
        v5 = v3[v9] + 1;                        // 加1
        ++v10;
        break;
      default:                                  // 大于12的值不作為運算方法,但參與其他的的運算的調用。
        continue;
    }
  }
}
           

通過分析read函數可知,輸入的字元長度為15個

buu練題記錄7-[網鼎杯 2020 青龍組]singal

在main函數中可知,vm_operad函數的a1是傳入的v4,而v4則是cpy的dword_403040。是以我們直接檢視dword_403040的值:

buu練題記錄7-[網鼎杯 2020 青龍組]singal

将資料提取出來然後處理一下:

10,				//輸入
4,16,8,3,5,1,	 //第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,	 //第8個數
12,8,11,1,
5,2,8,2,37,1,	
2,54,8,4,65,1,
2,32,8,5,1,1,	//第一個1它前面是5,是以它沒有被調用,後面的1才産生了一個v4
5,3,8,2,37,1,
4,9,8,3,32,1,
2,65,8,12,1,	//第15個數
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  //15次效驗
           

0x02 exp

exp1

起初是寫了一個爆破的exp,因為當時沒有弄的太明白:

#include<stdio.h>
#include<string.h>
#include<windows.h>

char 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,0};
unsigned char a2[] = {34,63,52,50,114,51,24,-89,49,-15,40,-124,-63,30,122,0};
int v10 = 0,v8 = 0,v7 = 0,v9 = 0,v6 = 0;
unsigned char v5,flag;

int crypt(int F,int S){
	v10 = S;
	while(1){
		switch ( a1[v10] ){
			case 0:
				exit(0);
			case 1:
		        return v5;
		    case 2:
		        v5 = a1[v10 + 1] + F;
		        v10 += 2;
		        break;
		    case 3:
		        v5 = F - (a1[v10 + 1]);
		        v10 += 2;
		        break;
		    case 4:
		        v5 = a1[v10 + 1] ^ F;
		        v10 += 2;
		        break;
		    case 5:
		        v5 = a1[v10 + 1] * F;
		        v10 += 2;
		        break;
		    case 6:
		        ++v10;
		        break;
		    case 7:
		        v10 += 2;
		        break;
		    case 8:
		        F = v5;
		        ++v10;
		        break;
		    case 10:
		        ++v10;
		        break;
		    case 11:
		        v5 = F - 1;
		        ++v10;
		        break;
		    case 12:
		        v5 = F + 1;
		        ++v10;
		        break;
		    default:
		        continue;
		}
	}
}

int main(){
	int V10 = 0 ,i = 0;
	while(1){	//硬爆破,唉,就是玩!
		for(flag = 1;flag<=122;flag++){
			int Crypt = crypt(flag,V10);
			if(Crypt == a2[i]){
				printf("%c",flag);
				i++;
				for(V10;a1[V10] != 1;V10++);
					V10++;			//比對一個值進入下一個值的操作數段開頭
				if(a1[V10] == 1)	//兩個1的地方,往後跳一下
					V10++;
			}
		}
	}
}
           

exp2

邊寫筆記邊把自己給弄明白了,同時也是為了更好的了解這個題,是以這裡又寫了一個逆算法和過程的exp:

#include<stdio.h>
#include<string.h>

char 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,0};
unsigned char a2[] = {34,63,52,50,114,51,24,-89,49,-15,40,-124,-63,30,122,0};
int v10 = strlen(a1) - 1,v8 = 14,v7 = 0,v9 = 15,v6 = 0;
unsigned char v5 = 0,flag[16];

int main(){
	memset(flag,0,16);
	while(v10 >= 0){
		if(a1[v10-1] <= 5 && a1[v10-1] >= 2){	//這裡是跳過非操作數,選出操作步驟 
			if(a1[v10-2] <= 5 && a1[v10-2] >= 2);
			else
				v10 --;
		}
		switch ( a1[v10] ){
			case 1:
		        v5 = a2[v8];
		        v9--;
		        v8--;
		        v10--;
		        break;
		    case 2:
		        flag[v9] = v5 - a1[v10 + 1];
		        v10--;
		        break;
		    case 3:
		        flag[v9] = v5 + (a1[v10 + 1]);
		        v10--;
		        break;
		    case 4:
		        flag[v9] = a1[v10 + 1] ^ v5;
		        v10--;
		        break;
		    case 5:
		        flag[v9] = v5 / a1[v10 + 1];
		        v10--;
		        break;
		    case 6:
		        --v10;
		        break;
		    case 8:
		        v5 = flag[v9];
		        --v10;
		        break;
		    case 10:
		        printf("%s",flag);
		        --v10;
		        break;
		    case 11:
		        flag[v9] = v5 + 1;
		        --v10;
		        break;
		    case 12:
		        flag[v9] = v5 - 1;
		        --v10;
		        break;
		    default:
		    	v10 -= 1;
		        break;
		}
	}
}