OD-困难重重的追踪消息断点
我是菜鳥,高手勿噴,謝謝,
最近逆向某捕魚游戲,由于需求需要逆向他的封包數(shù)據(jù),bp recv加給內(nèi)存下斷點(diǎn),找到解密消息的地方(收包子線程里面),我要的是是怎們處理里封包,而不只是解密過程,接著向下追查,發(fā)現(xiàn)游戲先是把解密好的數(shù)據(jù)放在模塊內(nèi)存里面,然后給主線程(主窗口)發(fā)消息,告訴他,收到網(wǎng)絡(luò)消息并給你解密好了,趕緊處理吧,發(fā)消息是使用的postmessage,窗口句柄參數(shù)是主窗口,消息ID是0x4FF,以前看過郁金香的視頻教程,好像可以用SPY++找到主窗口的窗口消息處理函數(shù),在哪里下消息斷點(diǎn),一下就可以攔截了,無奈自己當(dāng)時(shí)沒有記錄筆記,又找不到看的哪個(gè)視頻,自己動手找窗口處理函數(shù),找了半天還是不行,找不到哪個(gè)函數(shù),只能百度了“OD消息斷點(diǎn)”。大概收獲如下:
http://blog.csdn.net/liujiayu2/article/details/51789282
按照教程,依然沒有找到處理窗口消息的地方,哎,怪我太笨,,
無奈我只自己寫個(gè)工程,簡單逆向?qū)W習(xí)下,工程代碼如下(只列舉自己寫的,很簡單):
#define MSG_TEST 0x4FF ON_MESSAGE(MSG_TEST, &Ctest2Dlg::msg) #include "ClogFile.h" LRESULT Ctest2Dlg::msg(WPARAM w, LPARAM l) {//輸出字符串l gConsoleLog.WriteLogInfo("%s",(char*)l);return 0; }DWORD WINAPI func(LPVOID lpThreadParameter) { char *p = new char[20]; ZeroMemory(p, 20); strcpy(p,"1234567890"); ::PostMessage(AfxGetApp()->GetMainWnd()->m_hWnd, MSG_TEST, 0, (LPARAM)p);return 0; }void Ctest2Dlg::OnBnClickedButton1() { // TODO: 在ú此?添?加ó控?件t通?知a處鋦理え?程ì序ò代洙?碼? CreateThread(NULL, 0, func, NULL, 0, NULL); }按照上面的教程依然沒有找到我自己寫的msg(WPARAM?w, LPARAM?l)函數(shù)體,于是我進(jìn)下了一下小研究,
找到gConsoleLog.WriteLogInfo("%s",(char*)l);這句的反匯編代碼,下斷點(diǎn),點(diǎn)擊下按鈕,讓程序跑到斷點(diǎn)出,看調(diào)用堆棧,如下:
?
可以看到WinMain函數(shù)也在其中,這不是我們點(diǎn)擊按按鈕調(diào)用的,我們看到user32.DispatchMessageA
和MSG(0x4FF)結(jié)構(gòu)體,最終調(diào)用到了我們的msg(WPARAM?w, LPARAM?l)函數(shù)體,我決定親自試一下,看看能不能調(diào)用到。
1.在DispatchMessageA上下條件斷點(diǎn),[[esp+4]+4] == 0x4FF ?(消息ID)
2.點(diǎn)擊按鈕,程序成功在DispatchMessageA處斷下
3.F8單步走,走到user32.74E07BC5處的call,F7跟進(jìn)去,因?yàn)槎褩J沁@么指示的
4.一直跟著堆棧走確實(shí)最終走到了msg(WPARAM?w, LPARAM?l)函數(shù)體
走到是走到了,這是一路走來真不容易,每個(gè)call里面都有幾十個(gè)call,必須在指定的call跟進(jìn)去,不能走過了,也不能早走,但是看看這些,全是在系統(tǒng)代碼區(qū),最終一走到用戶代碼區(qū)就正好到了我們想要的函數(shù)體,肯定有簡便的方法吧,根據(jù)我們前面看到的文章(雖然沒找到函數(shù)體,依然學(xué)到了東西)打開OD的內(nèi)存窗口,找到可執(zhí)行程序test2的代碼段,.text
?
右鍵F2下斷點(diǎn),這個(gè)斷點(diǎn)表示,一旦程序運(yùn)行了用戶層代碼,立即斷下,并自動清除斷點(diǎn)。
我們按照這個(gè)方法試下test2程序,
1.在DispatchMessageA上下條件斷點(diǎn),[[esp+4]+4] == 0x4FF ?(消息ID)
2.點(diǎn)擊按鈕,程序成功在DispatchMessageA處斷下
3.test2.text下F2斷點(diǎn),
4.F9運(yùn)行程序
理論上來講應(yīng)該是直接就到了我們的msg(WPARAM?w, LPARAM?l)函數(shù)體,可惜,沒到,到了下邊這個(gè)地方
?
瞬間心里難受,竟然有這種幺蛾子,怎么辦?
冷靜過后,我們思考下,到了這里時(shí)候,我們真正想到的地方msg(WPARAM?w, LPARAM?l)函數(shù)體,到過還是沒到過?再來一變,這次在步驟3上面加上,在我們最終目的地加上斷點(diǎn),執(zhí)行步驟4之后發(fā)現(xiàn)直接走到了上面截圖的地方,還沒到我們最后的目的地,那就接著往下走走唄,F8單步運(yùn)行,發(fā)現(xiàn)程序運(yùn)行到了系統(tǒng)領(lǐng)空,那就繼續(xù)給test2.text下F2斷點(diǎn),F9運(yùn)行程序,到了下面,
?
依然沒有經(jīng)過最終目的地,沒事我們沖上面的步驟,經(jīng)過再兩次的重復(fù),突然柳暗花明到了我們最終目的地,這是什么情況,我現(xiàn)在還是不清除,但是可以確定的是這種方式確實(shí)可以找到,我們最終的目的地,
?
回想下我們看到的那幾篇文章,發(fā)現(xiàn)他下的是TranslateMessage斷點(diǎn),我下的是DispatchMessageA斷點(diǎn),那咱就試試這個(gè)TranslateMessage斷點(diǎn)法,經(jīng)過測試,也可以找到最終的消息響應(yīng)函數(shù)msg(WPARAM?w, LPARAM?l)?,歷程和DispatchMessageA斷點(diǎn)一樣的。
有了上面的實(shí)戰(zhàn)經(jīng)驗(yàn)我們就開始弄弄我們的游戲,為方便描述,我重新寫這個(gè)具體步驟:
?
1.在DispatchMessageA上下條件斷點(diǎn),[[esp+4]+4] == 0x4FF ?(消息ID)
2.點(diǎn)擊按鈕,程序成功在DispatchMessageA處斷下
3.test2.text下F2斷點(diǎn),
4.F9運(yùn)行程序
5.程序斷下,觀看程序斷的地方是不是最終想要的地方,是就結(jié)束,不是就執(zhí)行F8,程序會再進(jìn)入系統(tǒng)領(lǐng)空,
6.重復(fù)執(zhí)行步驟3、4、5直到我們找到最終的處理消息的地方
?
肯能你會問了,你怎么知道到達(dá)用戶端代碼是不是我們最終想要的呢?說下最終的特征:
?
?
上面截圖是我們的測試工程,
特點(diǎn)1,進(jìn)入的是一個(gè)完整的函數(shù)的頭,不可能是函數(shù)體的半中間,更不可能是jmp指令
特點(diǎn)2,函數(shù)體有兩個(gè)參數(shù),其實(shí)就是postmessage的WPARAM?w和LPARAM?l,在TranslateMessage的時(shí)候就要注意這兩個(gè)參數(shù)的值,鋼筋函數(shù)體時(shí),看下堆棧的第二個(gè)和第三只,應(yīng)該就是兩個(gè)參數(shù)(第一個(gè)參數(shù)是call產(chǎn)生的,表示要返回的地方)
經(jīng)過測試,哎,一直沒到我們想要的地方,并且進(jìn)入了一個(gè)死循環(huán),研究發(fā)現(xiàn)這個(gè)循環(huán)是
收包->解密->派遣??->收包->解密->派遣 ->收包->解密->派遣 這樣死循環(huán),怎么辦?知道我把這篇文章命名為困難重重的原因了吧,,
?
我有點(diǎn)小不服氣,以為運(yùn)氣原因,連續(xù)試了幾次,每次,都是這樣,我有陸續(xù)換了幾個(gè)OD,都是這樣,沒辦法只能想為什么會如此了。因?yàn)槲覀兪窍聰帱c(diǎn)再加F9的找,這樣可能會導(dǎo)致操作系統(tǒng)切換線程,切過去我們再單步走,走不出死循環(huán)了,差不多就是這個(gè)意思。
?
廢話不大多說了,直接說我想到的解決方案:
我們發(fā)現(xiàn)系統(tǒng)領(lǐng)空就是兩個(gè)dll的切換和調(diào)用,user32和mfc100.同一個(gè)程序連續(xù)啟動兩次,把兩個(gè)連接庫加載到內(nèi)存中的地址可能會變(變前16位,后16位不會變),這個(gè)我們知道的,和應(yīng)用程序一樣。我們拿我寫的那個(gè)測試程序做多次實(shí)驗(yàn),發(fā)現(xiàn)從TranslateMessage調(diào)用到最終的消息響應(yīng)函數(shù),調(diào)用堆棧很相似,(相同是調(diào)用堆棧地址的后16位,不同的是前16位),這就說明其實(shí)執(zhí)行代碼和流程固定不變的。這是時(shí)可以猜測即使不同的程序從TranslateMessage調(diào)用到最終的消息響應(yīng)函數(shù),執(zhí)行代碼和流程也應(yīng)該是固定不變的。那如果是這樣我們直接找到最后誰調(diào)用我們響應(yīng)函數(shù)在哪里下斷點(diǎn)就好了,就比如第一個(gè)圖的mfc100.dll 502CF274這個(gè)地址,我們在這個(gè)地方下斷點(diǎn),端下來再直接F8就到了,
按照上面的思路,按照下面的步驟,就可以找到最終消息響應(yīng)函數(shù)了,
1.在TranslateMessage上下條件斷點(diǎn),[[esp+4]+4] == 0x4FF ?(消息ID)
2.找到最終調(diào)用test項(xiàng)目的響應(yīng)函數(shù)的地方,下斷點(diǎn)
3.F9,直接到步驟2斷點(diǎn)的地方,
4.F8,到用戶區(qū)的消息響應(yīng)代碼
?
如何在游戲中找到最終調(diào)用處理消息call的位置?
1.看測試項(xiàng)目中調(diào)用最后處理消息call的代碼,如下(來自mfc100.dll的代碼):
5833F590 ???51 ?????????????push ecx
5833F591 ???50 ?????????????push eax
5833F592 ???8BCF ???????????mov ecx,edi
5833F594 ???FFD6 ???????????call esi ????????????????????????????????; test2.Ctest2Dlg::msg
2.把我們的有游戲加載上來,直接在mfc100.dll找“所有命令序列”,把上面的匯編指令貼進(jìn)去,正好只有一個(gè),也不用排除了,
?
?
注意:
上面的步驟,也執(zhí)行了F9,為什么沒有切換線程?應(yīng)該是同線程運(yùn)行比線程切換快的多的原因吧。
?
??
?
總結(jié)
以上是生活随笔為你收集整理的OD-困难重重的追踪消息断点的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 10.OD-强制在OEP前加载dll
- 下一篇: 0.IDA-基本的反汇编算法