看雪CTF 2017 比賽進行至第十一題
截止至今天中午12點,第十題破解人數為 8 人!
防守方 hsadkhk 依然位居首位~,第十一題作者衝進第三位。
此題過後,風間仁依然處於第一位,前十名排名基本保持不變。
比賽進入尾聲,僅剩 4 題。
期待ing......
接下來我們來回顧一下第十一題
看看 看雪評委和出題者是怎麼說的ヾ(๑╹◡╹)ノ"。
該題運用多種反除錯手段,並採用了修改過的DES演算法來進行防護。首先為主程式加了一個殼,然後用自己實現的函式隱藏api呼叫;判斷當前程式是否被除錯,如果是則跳入錯誤流程;透過父程式除錯子程式以及除0異常的方式來接管程式流程。繞過種種反除錯,識別出經過變形後的DES演算法後,就可以破解此題。
TobeBetter,科銳學員,目前學完x86逆向,希望在看雪論壇這個技術氛圍濃厚的平臺,可以向各位前輩學習知識和經驗。
參賽題目
Windows平臺CrackMe
題目答案
75A29C09E180217C048420956C15DA309FF2B69170
詳細的題目設計
1. 為程式寫了一個簡單的殼,對程式進行簡單的異或加密(加了殼之後程式會報毒,水平有限暫時無法解決)。若對程式進行dump,會丟失藏在檔案中但沒有讀入記憶體的DES解密函式,使序列號無法正常解密
2. 使用自己實現LoadLibrary將kernel32匯入到記憶體中,並使用自己實現的GetProcAddress透過已加密的函式名獲取函式地址,隱藏api呼叫
3. 程式入口處判斷了是否處於除錯狀態,包括檢查PEB的BeingDebugged、堆標誌位和父程式名是否為explore.exe或cmd.exe
1) 如果不為除錯狀態,則以除錯方式開啟程式本身,建立子程式
2) 如果處於除錯狀態,則顯示CrackMe對話方塊
4. 透過SetUnhandledExceptionFilter設定了異常篩選器
5. 當使用者點選確定按鈕時,會觸發除0異常。如果本程式是透過父程式以除錯模式開啟,則該異常會被父程式捕獲;否則,會被異常篩選器給捕獲(正常情況不會來這裡)
6. 程式本身沒有正常呼叫驗證序列號的流程。當父程式捕捉到除0異常之後(程式第一次點選確認按鈕時),會對錯誤的驗證序列號的函式進行hook,從而跳轉到正確的流程。
7. 驗證序列號流程
程式中提供前96位元組的DES解密演算法明文(記為fuc1)。驗證前會將pe檔案中已的DES解密演算法密文(記為fuc2)讀入記憶體中。
驗證碼的前10個字元組成4位元組和1位元組的兩個秘鑰,這兩個秘鑰對fuc2進行簡單移位異或操作解密並與fuc1的前96位元組比較,驗證成功則繼續。
驗證碼的後32字元組成16位元組密文,透過程式中提供的秘鑰呼叫fuc1對密文進行解密,驗證解密出的明文與程式中的明文進行比較,字串相同則為提示成功。
破解思路
1. 對程式進行脫殼dump。可以透過修改自己載入的kernel32的地址為系統kernel32的地址,使用OD除錯時即可觀察到程式呼叫api情況
2. 在WinMain中透過修改全域性的除錯狀態標誌位使得程式能夠在除錯狀態下建立子程式(預設只能透過explore.exe和cmd.exe開啟的程式才會建立子程式)
3. 在處理除錯事件的函式中下斷點,找到程式的hook點和hook的內容,直接對程式對應位置修改機器碼。此時還原了程式正確的流程,點選確認按鈕就可以正常驗證序列號
4.
解密des函式的兩個秘鑰(共5個位元組)可以透過窮舉法獲取,異或演算法較為簡單,很容易就能還原出來,透過解密密文和程式中的前96位元組明文對比可以確認秘鑰是否正確。需要注意的是,des函式需要在原檔案中提取。這裡獲得的兩個秘鑰轉為十六進位制字元,得到序列號前10個字元。透過測試,對於i5-4210h
CPU單執行緒需要跑20分鐘
5. 識別出變形的des演算法(在加密前和解密後需要對偶數位進行取反,即異或0x55),根據程式中提供的16位明文和秘鑰進行加密,得到16位密文,對密文轉為十六進位制字元,得到序列號的後32個字元。此時組成的48位序列號即為最終答案
使用工具:IDA6.8(反彙編分析) notepad(記錄) vc6.0(解碼) OD(除錯)
步 驟:
一、程式碼自解密
IDA載入看入口點和匯入表,沒有發現任何有用的東西,考慮該程式已經加密處理。
二、識別真正流程
繼續OD動態跟蹤,在此處發現DialogBoxParamW,有過MFC程式設計經驗的朋友應該不陌生,說明我們已經找到關鍵點了(其實不然)。
三、第一個關鍵演算法
實際除錯時可以在4016AF處將JE用NOP代替,將401A43處修改為jmp 402303,即可繞過,直接除錯主程式。
另外,此題動態記憶體執行程式碼較多,建議關鍵位置下好“硬體執行”斷點,便於除錯。
考慮對第9、10位的數字進行列舉,動態除錯找到待解碼和比較的資料,編寫程式碼如下。
#include
unsigned m_DE0000[] = { 0x83F08EA7,0x3F0FBA29,0xE747E97C,0x93D03647,0xEC72CD2C,0x93C0BA2E,0x90A578A3,0x2A40BA2F,\
0xDB3FF233,0x9031FB09,0xD1477258,0x905E3DAC,0xAB817C35,0x6BD43434,0xC49E84E4,0x83B426AF,\
0x51C0BA3A,0x280080B8,0x93BE3FF3,0x8E36BA3B,0xE9C0BA3C,0x93C0BA29,0x93C0B2C5,0x1680CD3F};
unsigned m_4340B0[] = { 0x1070EC81,0x55530000,0xBC8B5756,0x00108424,0xBBF63300,0x00000001,0x0725C68B,0x79800000,\
0xC8834805,0x07B140F8,0xC68BC82A,0x07E28399,0xF8C1C203,0x38148A03,0xD322FAD2,0x10349488,\
0x46000002,0x7C40FE83,0x0002BDCF,0x05BA0000,0xBE000000,0x00000014,0x000008B9,0x8DC03300};
int main()
{
unsigned xx = 0;
unsigned yy,zz1,zz2;
for(xx=0;xx<=0xff;xx++)
{
yy = 0x1010101*xx;
zz1=(m_DE0000[0]+yy)^m_4340B0[0];
zz2=(m_DE0000[1]+yy)^m_4340B0[1];
if(zz1 == (zz2-1))
{
printf("%02X\n",xx);
printf("%08X\n",zz1);
}
}
return 0;
}
四、第二個關鍵演算法
還是sub_402300中,剛解碼的函式對輸入註冊碼第10位以後的字串進行處理,處理結果與“!HelloHaniella!”比較,返回值與sub_402240的返回值比較。
五、結論
組合兩個關鍵演算法的結果,得到註冊碼“75A29C09E180217C048420956C15DA309FF2B69170”,輸入程式,結果如下。
當然,由於作者沒有考慮到結尾的0x00,導致結尾加上(0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00)解密後的結果“FAB17827375A8685”後依然正確,如下。理論上講有無數個即“75A29C09E180217C048420956C15DA309FF2B69170(FAB17827375A8685迴圈)”。
詳細的解析,戳:http://bbs.pediy.com/thread-218701.htm
最後感謝 WiFi 萬能鑰匙安全應急響應中心的贊助支援,
接下來的比賽大家一定要使出洪荒之力哦!↖(^ω^)↗
比心 ❤
上海連尚網路科技有限公司成立於
2013 年,是一家專注於提供免費上網和內容服務的移動網際網路企業。連尚網路自主研發的核心產品 WiFi
萬能鑰匙,以分享經濟的模式,透過雲端計算和大資料技術,利用熱點主人分享的閒置WiFi資源,為使用者提供免費、穩定、安全的上網服務,以幫助更多的人上網,找到屬於他們的機會,改變自己的命運。