buuoj[ACTF_Junior_2020]Splendid_MineCraft WriteUp

Luyao發表於2020-10-26

Splendid_MineCraft

題目標題就已經暗示這題是SMC了(self-modifying code)。

工具:exeinfo,x32dbg和IDA7.0

先丟進exeinfo裡檢視相關資訊:

 

 用IDA開啟:

根據可以字串“Wrong!\n”直接鎖定sub_401080為main函式。

int sub_401080()
{
  char *v0; // eax
  char *v1; // eax
  char *v2; // ST28_4
  signed int i; // [esp+14h] [ebp-54h]
  int v5; // [esp+20h] [ebp-48h]
  char Str1; // [esp+24h] [ebp-44h]
  char v7; // [esp+3Dh] [ebp-2Bh]
  int v8; // [esp+44h] [ebp-24h]
  __int16 v9; // [esp+48h] [ebp-20h]
  char v10[4]; // [esp+4Ch] [ebp-1Ch]
  __int16 v11; // [esp+50h] [ebp-18h]
  int v12; // [esp+54h] [ebp-14h]
  __int16 v13; // [esp+58h] [ebp-10h]
  int v14; // [esp+5Ch] [ebp-Ch]
  __int16 v15; // [esp+60h] [ebp-8h]

  sub_401020((const char *)&unk_404118, (unsigned int)"Welcome to ACTF_Splendid_MineCraft!");
  sub_401050((const char *)&unk_40411C, (unsigned int)&Str1);
  if ( strlen(&Str1) == 26 )
  {
    if ( !strncmp(&Str1, "ACTF{", 5u) && v7 == 125 )
    {
      v7 = 0;
      v0 = strtok(&Str1, "_");
      v12 = *(_DWORD *)(v0 + 5);
      v13 = *(_WORD *)(v0 + 9);
      v14 = *(_DWORD *)(v0 + 5);
      v15 = *(_WORD *)(v0 + 9);
      v1 = strtok(0, "_");
      v8 = *(_DWORD *)v1;
      v9 = *((_WORD *)v1 + 2);
      v2 = strtok(0, "_");
      *(_DWORD *)v10 = *(_DWORD *)v2;
      v11 = *((_WORD *)v2 + 2);
      dword_403354 = (int)dword_4051D8;
      if ( ((int (__cdecl *)(int *))dword_4051D8[0])(&v12) )
      {
        v5 = SBYTE2(v14) ^ SHIBYTE(v15) ^ (char)v14 ^ SHIBYTE(v14) ^ SBYTE1(v14) ^ (char)v15;
        for ( i = 256; i < 496; ++i )
          byte_405018[i] ^= v5;
        JUMPOUT(__CS__, &byte_405018[256]);
      }
      sub_401020("Wrong\n");
    }
    else
    {
      sub_401020("Wrong\n");
    }
  }
  else
  {
    sub_401020("Wrong\n");
  }
  return 0;
}

由三個strtok函式可知,flag{}裡的內容應該是被_分成了三個部分,根據輸入長度猜測每個部分應該是6位元組長(這三個部分我們分別稱為flag_sec1,flag_sec2和flag_sec3)。

flag_sec1

因為是SMC基本上確定是要動態除錯的,所以大致瞭解一下main函式的結構,就丟進x32dbg裡開始除錯。

先搜尋字串,直接進入main函式:

 到call CB1050時除錯會卡住,說明程式此時正在等待輸入,於是我們可以得到:

先隨便輸入一個flag:ACTF{123456_ABCDEF_abcdef}

我們向下瀏覽會發現有三個strtok()函式,如圖

正好與IDA反編譯的結果相吻合,所以這三個函式後面應該是對flag內容的比較。

在最後一個strtok()函式後面打個斷點,直接跳過中間的內容,然後進行單步除錯,直到進入這個call。

 進入call後會發現很多奇怪的指令,這才進入到我們這篇WP真正的主題:SMC

 

 一步一步F7除錯,會發現隨著如下迴圈的進行,奇怪的彙編指令也會被改變:

 跳過SMC迴圈繼續除錯,會發現又有一個大迴圈:

 經過一遍又一遍的除錯,會發現裡面的三條關鍵指令:

異或和求和的過程中並沒有用到我們的輸入,結果都儲存到ss:[ebp+eax-20]裡,直接執行完follow in dump:

 這就是上面迴圈得到的結果。

閱讀迴圈可知:00CB5332的cmp edx,ecx就是比較使用者輸入和flag的過程。

flag_sec1 = yOu0y*

flag_sec2

 這段也是SMC,修改後面jmp EAX裡面的程式碼。

重新除錯,輸入flag_sec1正確的字串繼續除錯。沒啥好說的,知道除錯到jmp eax:

 一進去就遇到個迴圈,但是這個迴圈非常詭異,因為中間有多餘程式碼,其實不斷的除錯就會發現,中間的多餘程式碼是會被修改的!這其實就是SMC,但是本該是資料卻被x32dbg識別成了程式碼而已,通過

右鍵->Analysis->Treat from section as byte就可以將其轉化為資料了,如下圖:

這個迴圈只是把使用者輸入的flag_sec2儲存到了這個區域裡(ABCDEF對應著41-46)。

後面有個迴圈:

 (一邊F7一邊檢視dump視窗。不好意思沒看出來有啥用,估計是煙霧彈)

其實就是EAX[flag_sec2[i]^(0x83+i)] == EAX[EDI+166]

flag_sec2 = knowo3

flag_sec3

題目裡直接明文strcmp,

flag_sec3 = 5mcsM<

 

綜上:flag{yOu0y*_knowo3_5mcsM<}