【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 的類型,從匯編可以看出, v1 4 字節 Int 0xEFBEADDE 對應偽碼中 -34.-83,-66.-17

 

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

 

查看寄存器信息:

 

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

 

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

 

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

 

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


3. 破解方法
1) 取到dll 中加密后的串,即 get_string unk_6E282000 0x19 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");
      }

 

 

   結果:
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 的類型,從匯編可以看出, v1 4 字節 Int 0xEFBEADDE 對應偽碼中 -34.-83,-66.-17

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

查看寄存器信息:

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

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

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

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


3. 破解方法
1) 取到dll 中加密后的串,即 get_string unk_6E282000 0x19 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");
      }

 


結果:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM