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)
重點看:


這塊是暴力破解的關鍵。
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 內容
此處取了 *a3
和
*(a3 + 1)
的值
此處回寫了 *a3
和 *(a3 + 1)
的值
結果:
團隊逆向牛的解題思路,分享出來~
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)
重點看:


這塊是暴力破解的關鍵。
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"); }
結果:
