【ZCTF】easy reverse 詳解

0x01  前言
    團隊逆向牛的解題思路,分享出來~



0x02  內容

0. 樣本
bbcdd1f7-9983-4bf4-9fde-7f77a6b947b4.dll

1. 靜態分析
使用IDAPro逆向分析樣本,樣本較小,得到方法列表:

調用關係:

PS: 開始受調用關係誤導,分析DLL入口函數調用的幾個方法,浪費了時間。

查看內部字符串:

根據yes!定位到方法 get_string

get_string: 調用關係圖,有解密過程調用。

使用IDAPro F5功能,生成僞碼:

大致過程:
1) 獲取unk_6E282000處存儲的0x19長字節流
2) 用戶輸入字符串
3) 使用encrypt_str加密用戶輸入字符串
4) 加密結果與1)處0x19字節相同,則輸入的串爲可能的Flag

encrypt_str僞碼:
大致過程:
1) v4基本上爲固定值0,原因自己看
2) 輸入串長度大於2, 並且只有偶數個字節參與運算, v7爲終止哨兵,其值爲 a1+2*result,  a1爲串首地址,result爲串長度的1/2取整。
3) v6從首地址開始,每循環一次,跳2個字節。

encrypt僞碼:
        大致過程:
1) 僞碼中參數列表,重點看a3參數,從encrypt_str調用處可看出,a3爲指向用戶輸入串中的某個位置,調用一次,前進2
2) 重點看:
此處取了 *a3 *(a3 + 1)的值

此處回寫了 *a3 *(a3 + 1)的值
這塊是暴力破解的關鍵。

        PS 可以看出,加密過程是通過一系列複雜的運算來實現,一般算法很難從結果逆推出原始串,先從暴力破解入手。

2. 動態分析
樣本爲DLL,導出方法get_string,需要編寫Load程序。

爲簡化調試過程,Load程序模擬get_string流程,不需scanf控制檯輸入,主要代碼如下:
typedef int(*encrypt_str)(uint8_t*, uint32_t, int*);
auto h = LoadLibrary("bbcdd1f7-9983-4bf4-9fde-7f77a6b947b4.dll");
encrypt_str f = (encrypt_str)((uint8_t*)h + 0x67C + 0xC00);
        int v1 = 0xEFBEADDE;
        char testStr[] = "0123456789abcdefgh";
        f((uint8_t *)testStr, strlen(testStr)+1, &v1);


注意:v1的值問題,IDA翻譯的僞碼未識別出v1的類型,從彙編可以看出,v14字節Int0xEFBEADDE對應僞碼中 -34.-83,-66.-17

OD調試分析步驟:
1) 使用OD啓動編譯完的程序
2) bp encrypt_str 下斷點
OD斷在

查看寄存器信息:

可知 0073FDD0處存儲待加密的字符串,OD數據跟隨:

單步步過一次encrypt調用後,數據區:

前兩個字節數據變化,基本肯定,加密方法每次處理2字節數據,且加密後數據長度不發生變化。

PS上述過程原理上只能得出每次只生成兩字節加密數據,不能證明加密過程也只有2字節參與,證明方法:可以在數據區按字節下內存讀斷點,看在encrypt過程中,是否只命中兩個字節的讀斷點。


3. 破解方法
1) 取到dll中加密後的串,即get_stringunk_6E2820000x19 Bytes數據。有效數據長度24
2) 每兩字節一組,窮舉0x0-0xFF,共256*256種可能,調用encrypt,結果與unk_6E282000對應位置數據比較,一致時找到2Bytes
3) 重複過程2) 12次。
4) 由於算法中,每兩字節一組獨立運行,暴力枚舉量從: 256^24 縮小到 256^2*12 = 786432,運算時間10秒以內。

核心代碼如下:
      
uint8_t str[25] = { 0 };[/align]        for (int n = 0; n < 25; n+=2)
      {
              for (int i = 0; i <= 0xFF; i++)
                      for (int j = 0; j <= 0xFF; j++)
                      {
                              uint8_t *data = new uint8_t[25];
                              memcpy(data, str, 25);
                              *(data + n) = (uint8_t)i;
                              *(data + n + 1) = (uint8_t)j;
                              f(data, 25, &v1);
                              if (data[n] == pstr[n] && data[n+1] == pstr[n+1]) {
                                      str[n] = i;
                                      str[n + 1] = j;
                                      break;
                              }
                      }
      }
      printf("%s\n", str);
      f(str, 25, &v1);
      if (memcmp(str, pstr, 25) == 0)
      {
              printf("It's OK!\n");
      }


  結果:
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章