windows 訊息斷點

乘舟凉發表於2024-11-05

windows 訊息迴圈

以下是一個簡單的處理按鈕點選的示例:

#include <windows.h>

#define BUTTON_ID 1 // 定義按鈕ID

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
    case WM_CREATE:
    {
        // 建立一個按鈕
        HWND hButton = CreateWindow(
            L"BUTTON",          // 按鈕類名
            L"Click Me",        // 按鈕標題
            WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON,  // 樣式
            50,                 // x 座標
            50,                 // y 座標
            100,                // 寬度
            50,                 // 高度
            hwnd,               // 父視窗控制代碼
            (HMENU)BUTTON_ID,   // 按鈕ID
            (HINSTANCE)GetWindowLongPtr(hwnd, GWLP_HINSTANCE),
            NULL);              // 引數
        break;
    }
    case WM_COMMAND:
        if (LOWORD(wParam) == BUTTON_ID && HIWORD(wParam) == BN_CLICKED)
        {
            MessageBox(hwnd, L"Button clicked!", L"Notification", MB_OK);
        }
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }
    return 0;
}

int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow)
{
    const wchar_t CLASS_NAME[] = L"Sample Window Class";

    WNDCLASS wc = {};
    wc.lpfnWndProc = WindowProc;
    wc.hInstance = hInstance;
    wc.lpszClassName = CLASS_NAME;

    RegisterClass(&wc);

    HWND hwnd = CreateWindowEx(
        0,
        CLASS_NAME,
        L"Button Click Example",
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, 300, 200,
        NULL, NULL, hInstance, NULL);

    ShowWindow(hwnd, nCmdShow);

    MSG msg = {};
    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return 0;
}

下面是訊息迴圈的程式碼

    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

當訊息佇列沒有作業系統發來的訊息時,GetMessage會阻塞等待,當訊息佇列有訊息時,GetMessage會將訊息賦值到msg,並且呼叫DispatchMessage分發到各個視窗的訊息處理函式

下面是訊息處理函式註冊的程式碼

    WNDCLASS wc = {};
    wc.lpfnWndProc = WindowProc;
    wc.hInstance = hInstance;
    wc.lpszClassName = CLASS_NAME;

    RegisterClass(&wc);

透過SetWindowLong可以針對不同視窗設定訊息處理函式

SetWindowLongA(hWnd,GWL_WNDPROC,WindowProc);

透過GetWindowLong 可以獲取該視窗的訊息處理函式

 LONG_PTR wndProc = GetWindowLongPtr(hwnd, GWLP_WNDPROC);

SetWindowLongGetWindowLong僅限當前程序使用,其它程序呼叫是會有許可權不足的錯誤

x64dbg設定訊息斷點

載入程式後,執行到視窗彈出來,選擇控制代碼選項卡,右鍵點選重新整理
a30d73cbcdf920e942a7b93d1703157d

d04a3cc3a9c030063d2565a0357bac33
有很多關鍵資訊,其中最關鍵的還是視窗過程,這個就是訊息處理函式

在某個視窗右鍵點選訊息斷點
4b0ba06f8360a4b03c78f9e258f9af8a

彈出訊息斷點視窗
9d7c6dcbdc05fe4128f9d7af4f2aa29d
有很多訊息型別,下面我列舉一下點選事件的常見訊息

WM_LBUTTONDOWN //使用者按下滑鼠左鍵時傳送的訊息。
WM_LBUTTONUP //使用者釋放滑鼠左鍵時傳送的訊息
WM_COMMAND //當按鈕被點選時,最常見的訊息型別。訊息攜帶控制元件 ID 和通知碼,通常與按鈕互動事件相關。

其中WM_COMMAND一般用的最多,因為它會攜帶被點選的控制元件ID。

x64dbg訊息斷點的缺陷

x64dbg的控制代碼視窗只能獲取呼叫RegisterClass時的視窗過程,不能獲取SetWindowLong設定的視窗過程,所以要準確獲取正確的視窗過程,最好函式寫個外掛使用注入hook技術呼叫GetWindowLong獲取視窗過程

相關文章