消息斷點(diǎn)在x64dbg中的應(yīng)用 by lantie@15PB

消息斷點(diǎn)在x64dbg中的應(yīng)用 by lantie@15PB

Contents

介紹

你曾試圖逆向一個(gè)應(yīng)用程序中特定的函數(shù),但是卻無法真正找到它嗎?比如,在點(diǎn)擊按鈕或者按鍵之后想找到正在調(diào)用的代碼的。在某些程序(Delphi、CBuilder、Visual Basic等)中,可以使用工具反編譯程序并在幾秒鐘內(nèi)定位到相應(yīng)事件/地址。但如果程序被加殼或是使用了反-反編譯技術(shù),亦或是程序不是事件驅(qū)動(dòng)的,在這種情況下,你可以使用類似方法很快定位到地址嗎?
本文介紹的方式是在程序運(yùn)行中使用消息斷點(diǎn)來定位特定函數(shù)。(好不好使,看你的了??)

使用Windows消息

讓我們來看一個(gè)簡單的CrackMe作為示例。其是一個(gè)簡單的exe,使用VC++編寫:

sample crackme

如果我們嘗試輸入一個(gè)文本并點(diǎn)擊按鈕Check!,會(huì)發(fā)現(xiàn)沒有任何反應(yīng),沒有文本信息,什么也沒有。這個(gè)時(shí)候,我們可以開動(dòng)腦筋,有創(chuàng)意地尋找其他替代方案來找到我們的序列號(hào)被處理的確切位置,也許成功,也許失敗。但是如果我告訴你,有一個(gè)更簡單的方法可以找到按鈕呢,就像Delphi、Visual Basic或者其他事件驅(qū)動(dòng)的語言?然我們一起來看看這種方法吧。

在x64dbg中加載和執(zhí)行文件后,我們輸入一些文本,然后點(diǎn)擊按鈕Check!,之后我們轉(zhuǎn)到句柄(Handles)選項(xiàng)卡,并刷新視圖以獲取調(diào)試程序的窗口列表。然后我們可以在列表中看到按鈕Check!,選擇按鈕Check!所在列然后右鍵單擊它,并選擇Message Breakpoint選項(xiàng)設(shè)置消息斷點(diǎn):

windows widget

到此會(huì)進(jìn)行消息斷點(diǎn)的設(shè)置,具體的配置是這樣的:

message bp dialog

以上設(shè)置是當(dāng)WM_LBUTTONUP(鼠標(biāo)左鍵點(diǎn)擊)消息被發(fā)送到我們的按鈕控件,程序就會(huì)停止。設(shè)置斷點(diǎn)之后,我們點(diǎn)擊CrackMe中的按鈕Check!,不久之后,我們進(jìn)入斷點(diǎn)。

到此,我們實(shí)現(xiàn)了我們想要的程序暫停。按鈕點(diǎn)擊后我們剛剛停止執(zhí)行,程序會(huì)停在user32.dll中,但我們的目的是在主模塊代碼中。 到達(dá)主模塊就像在我們的可執(zhí)行文件的代碼部分使用斷點(diǎn)一樣簡單。 我們可以使用運(yùn)行到用戶代碼選項(xiàng)(Ctrl + F9)。

code section breakpoint

當(dāng)嘗試恢復(fù)執(zhí)行時(shí),調(diào)試器將再次停止執(zhí)行,但這次正好在我們正在尋找的代碼中間。 在這種情況下,我們進(jìn)入了 DLGPROC 函數(shù)(負(fù)責(zé)處理發(fā)送到主窗口對(duì)話框中的每個(gè)窗口控件的消息的回調(diào))。

dialog function

事件驅(qū)動(dòng)程序設(shè)計(jì)

如果你是一個(gè)程序員,或者一直在做與編程語言和寫代碼相關(guān)的工作,你應(yīng)該知道所謂的事件驅(qū)動(dòng)編程的概念。事件驅(qū)動(dòng)的編程是一種編程范例,程序執(zhí)行流由事件決定;用戶動(dòng)作,鼠標(biāo)點(diǎn)擊,按鍵等。事件驅(qū)動(dòng)的應(yīng)用程序用于識(shí)別事件發(fā)生時(shí),然后使用適當(dāng)?shù)氖录幚磉^程進(jìn)行管理。一些編程語言使用事件驅(qū)動(dòng)編程,并給出一個(gè)IDE,它可以讓代碼自動(dòng)生成,并且可以選擇內(nèi)置的對(duì)象和控件,Visual Basic,Borland Delphi / CBuilder,C#,Java等等都是可以做到。(1)

Windows消息

即使程序員沒有使用上述語言之一,或者即使以非事件驅(qū)動(dòng)的方式使用它們,Microsoft Windows應(yīng)用程序也是事件驅(qū)動(dòng)的,這意味著您將要處理窗口消息。以下是MSDN的的說明:

系統(tǒng)將應(yīng)用程序的所有輸入傳遞到應(yīng)用程序中的各種窗口。 每個(gè)窗口都有一個(gè)稱為窗口過程的函數(shù),系統(tǒng)在窗口輸入時(shí)調(diào)用。 窗口過程處理輸入并將控制返回給系統(tǒng)。

系統(tǒng)使用一組四個(gè)參數(shù)向窗口過程發(fā)送消息:窗口句柄,消息標(biāo)識(shí)符和兩個(gè)稱為消息參數(shù)的值。 窗口句柄標(biāo)識(shí)消息所針對(duì)的窗口。 系統(tǒng)使用它來確定哪個(gè)窗口過程應(yīng)該接收消息。

消息標(biāo)識(shí)符是用于標(biāo)識(shí)消息目的的命名常量。 當(dāng)窗口過程接收到消息時(shí),它使用消息標(biāo)識(shí)符來確定如何處理消息。(2)

Windows窗口回調(diào)函數(shù)

根據(jù)上面的信息,攔截某個(gè)窗口的窗口消息,我們需要首先找到所需“控件”的窗口回調(diào)函數(shù)。 從包含窗口回調(diào)函數(shù)的應(yīng)用程序這樣做是很容易的,我們可以通過使用以下函數(shù)來定位:

LONG WINAPI GetWindowLong(
  _In_ HWND hWnd,
  _In_ int  nIndex
);

DWORD WINAPI GetClassLong(
  _In_ HWND hWnd,
  _In_ int  nIndex
);

nIndex = GWL_WNDPROC:指定獲取的信息是窗口回調(diào)函數(shù)的地址,或表示窗口回調(diào)函數(shù)地址的句柄。 您必須使用CallWindowProc函數(shù)調(diào)用窗口回調(diào)函數(shù)。

BOOL WINAPI GetClassInfo(
  _In_opt_ HINSTANCE  hInstance,
  _In_     LPCTSTR    lpClassName,
  _Out_    LPWNDCLASS lpWndClass
);

BOOL WINAPI GetClassInfoEx(
  _In_opt_ HINSTANCE    hinst,
  _In_     LPCTSTR      lpszClass,
  _Out_    LPWNDCLASSEX lpwcx
);

typedef struct tagWNDCLASS {
  UINT      style;
  WNDPROC   lpfnWndProc;
  ...
} WNDCLASS, *PWNDCLASS;

lpfnWndProc:指向窗口回調(diào)函數(shù)的指針。

有了以上函數(shù),獲取窗口類信息和窗口函數(shù)地址會(huì)非常簡單。但是Windows操作系統(tǒng)有一個(gè)限制:Microsoft Windows 不允許從外部應(yīng)用程序(如調(diào)試器)中獲取這些信息。 如果要使用上述函數(shù)之一獲取另一個(gè)進(jìn)程所擁有的給定窗口或控件的窗口回調(diào)函數(shù)地址,則最終會(huì)出現(xiàn)ACCESS_VIOLATION異常。在我們這個(gè)例子中x64dbg應(yīng)該是一樣的,無法獲取信息。但是x64dbg可以獲取的真實(shí)的地址,接下來我們看看x64dbg是怎么做的

獲取外部Windows窗口回調(diào)函數(shù)

在這點(diǎn)上,我們不清楚為什么會(huì)出現(xiàn)這種情況,以及是否是操作系統(tǒng)的bug。事實(shí)上它可以在以前的Windows操作系統(tǒng)版本中使用。這其中關(guān)鍵函數(shù)是這個(gè):

DWORD WINAPI GetClassLong(
  _In_ HWND hWnd,
  _In_ int  nIndex
);

在相應(yīng)地調(diào)用GetClassLong(ANSI / UNICODE)的正確功能版本之前,黑客依靠對(duì)給定窗口的字符集進(jìn)行測試。 x64dbg使用的代碼很簡單:

duint wndProc;
if(IsWindowUnicode(hWnd))
    wndProc = GetClassLongPtrW(hWnd, GCLP_WNDPROC);
else
    wndProc = GetClassLongPtrA(hWnd, GCLP_WNDPROC);

要編寫與32位和64位版本的Windows兼容的代碼,您必須使用GetClassLongPtr。 當(dāng)編譯32位Windows時(shí),GetClassLongPtr被定義為調(diào)用通常的GetClassLong函數(shù)。

截取消息

現(xiàn)在已經(jīng)定位到窗口回調(diào)函數(shù),任何消息都可以用適當(dāng)?shù)臈l件表達(dá)式攔截,但在此之前,讓我們檢查一下這個(gè)邏輯。 每次通過窗口回調(diào)函數(shù)處理的結(jié)構(gòu)如下所示:

typedef struct tagMSG {
  HWND   hwnd;
  UINT   message;
  WPARAM wParam;
  LPARAM lParam;
  DWORD  time;
  POINT  pt;
} MSG, *PMSG, *LPMSG;

正如我們可以看到結(jié)構(gòu)給我們提供了一些有用的信息,最重要的是hwndmessage。 根據(jù)這些字段,我們可以知道哪個(gè)特定的控件發(fā)送什么消息。 在進(jìn)一步介紹之前,讓我們看一個(gè)發(fā)送給給定Button控件的特定消息(WM_LBUTTONUP)的示例。

message breakpoint dialog

點(diǎn)擊OK按鈕后,我們會(huì)觸發(fā)斷點(diǎn),當(dāng)我們檢查堆棧參數(shù)時(shí),我們可以看到這樣的東西
breakpoint stack

第一個(gè)值可以看到的是對(duì)應(yīng)于我們的Button控件的句柄,第二個(gè)值對(duì)應(yīng)于消息WM_LBUTTONUP(0x202)。

WinProc 條件斷點(diǎn)

完成此功能的最后一件事是可能暫停應(yīng)用程序的時(shí)機(jī)只有在特定句柄(handles)和消息(messages)處理時(shí)。我們可以在幫助文檔中閱讀,x64dbg集成了一組非常好的強(qiáng)大的表達(dá)式來實(shí)現(xiàn)這一點(diǎn)。 如上圖所示,有三個(gè)選項(xiàng):

  1. Break on any window(在任何窗口中斷):使用此選項(xiàng),我們停止給定的消息,而不管窗口句柄。 為此,我們需要最簡單的表達(dá)式:
bpcnd WINPROC, "arg.get(1) == MESSAGE"
  1. Break on current window only(僅在當(dāng)前窗口中斷):此功能將為表達(dá)式添加一個(gè)附加條件,以便僅在涉及特定窗口的句柄時(shí)停止執(zhí)行,在這種情況下,表達(dá)式將為:
bpcnd WINPROC, "arg.get(1) == MESSAGE && arg.get(0) == HANDLE"
  1. Use TranslateMessage(使用TranslateMessage函數(shù)):有時(shí)winproc技術(shù)可能不是預(yù)期的結(jié)果,那這個(gè)時(shí)候程序可能使用的是TranslateMessage API 來截獲消息,而不是在窗口回調(diào)本身。
BOOL WINAPI TranslateMessage(
  _In_ const MSG *lpMsg
);

看到這個(gè)函數(shù)使用了我們之前看到的相同的MSG結(jié)構(gòu),因此根據(jù)操作系統(tǒng)平臺(tái),使用表達(dá)式會(huì)增加一些微小的變化:

ifdef _WIN64
bpcnd TranslateMessage, "4:[arg.get(0)+8] == MESSAGE"
bpcnd TranslateMessage, "4:[arg.get(0)+8] == MESSAGE && 4:[arg.get(0)] == HANDLE"
#else //x86
bpcnd TranslateMessage, "[arg.get(0)+4] == MESSAGE"
bpcnd TranslateMessage, "[arg.get(0)+4] == MESSAGE && [arg.get(0)] == HANDLE"
#endif //_WIN64

案例說明

如在這篇文章中看到的,消息斷點(diǎn)是x64dbg中非常方便和強(qiáng)大的功能,可以在許多場景中使用,我們可以盡可能的控制任何事件暫停調(diào)試。即使不是類似Delphi或Visual Basic一樣的事件驅(qū)動(dòng)程序,也可以為逆向者打開一扇門,帶來更多的思路進(jìn)行調(diào)試。如果想在MASM編寫的應(yīng)用程序的“Edit”控件中輸入字符時(shí)暫停執(zhí)行,只需要在控件上對(duì)消息WM_KEYUP設(shè)置消息斷點(diǎn)即可。按鈕點(diǎn)擊、顯示窗口等控制一樣,有一大堆消息可以使用消息斷點(diǎn)進(jìn)行調(diào)試分析。

最后的話

書寫這篇文章,是想更深入了解消息斷點(diǎn)功能和在多種場景下如何使用消息斷點(diǎn),到此結(jié)束,希望對(duì)你有幫助
原作者: ThunderCls
翻譯者: lantie@15PB
原文地址:https://x64dbg.com/blog/2017/07/07/messages-breakpoints-in-x64dbg.html

參考

  1. https://www.technologyuk.net/computing/software-development/event-driven-programming.shtml
  2. https://msdn.microsoft.com/en-us/library/windows/desktop/ms644927(v=vs.85).asp

注釋

幫助

官網(wǎng):www.15pb.com.cn,專注于信息安全教育,軟件逆向,二進(jìn)制安全,網(wǎng)絡(luò)安全

下載

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容