关于逆向Windows窗口程序

一、窗口程序

窗口程序也就是非终端程序,带有图形化界面窗口的程序。

这类程序的起始函数一般为WinMain函数,start函数中会调用该函数。

关于WinMain函数的定义如下:

int WINAPI WinMain(
   _In_ HINSTANCE hInstance,
   _In_opt_ HINSTANCE hPrevInstance,//已弃用
   _In_ LPSTR     lpCmdLine,
   _In_ int       nCmdShow
);

二、创建一个窗口程序的过程

步骤可以分为五步:

  1. 定义一个窗口类;
  2. 注册该窗口类;
  3. 创建窗口;
  4. 显示窗口;
  5. 创建消息循环。

1.定义窗口类

在定义窗口类的过程中,设置窗口的各项属性,其中对于我们逆向工作最值得注意的是其中的窗口过程函数设置。

窗口类定义示例:

复制代码
WNDCLASSEX wcex;

wcex.cbSize         = sizeof(WNDCLASSEX);
wcex.style          = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc    = WndProc; //窗口过程函数设置
wcex.cbClsExtra     = 0;
wcex.cbWndExtra     = 0;
wcex.hInstance      = hInstance;
wcex.hIcon          = LoadIcon(wcex.hInstance, IDI_APPLICATION);
wcex.hCursor        = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName   = NULL;
wcex.lpszClassName  = szWindowClass;
wcex.hIconSm        = LoadIcon(wcex.hInstance, IDI_APPLICATION);
复制代码

其中wcex.lpfnWndProc就是定义窗口过程函数,窗口中接收到的消息都通过这个函数去决定如何去处理,所以我们需要重点关注它。

窗口过程函数的定义如下:

LRESULT CALLBACK WndProc(
   _In_ HWND   hWnd,
   _In_ UINT   message,
   _In_ WPARAM wParam,
   _In_ LPARAM lParam
);

窗口过程函数示例:

复制代码
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
    case WM_SIZE:
        {
            int width = LOWORD(lParam);  // Macro to get the low-order word.
            int height = HIWORD(lParam); // Macro to get the high-order word.
            // Respond to the message:
            OnSize(hwnd, (UINT)wParam, width, height);
        }
        break;
    }
}
void OnSize(HWND hwnd, UINT flag, int width, int height)
{
    // Handle resizing
}
复制代码

2.注册定义好的窗口类

我们通过RegisterClassEx(&wcex)来将定义好的窗口类向操作系统注册。tip:在调试过程中,我们也可以对该函数下断点,再查看分析参数指向内存地址来定位到窗口过程函数的地址。

3.创建窗口

 使用 CreateWindowEx 函数将窗口实例化,这里是通过实例句柄将这个窗口和窗口类关联到一起的,实例句柄是当前代码模块的句柄(该程序实例或dll的模块),窗口是在不同实例中是可以重名的(窗口它们都是通过实例区分的)。

复制代码
HWND hWnd = CreateWindowEx(
WS_EX_OVERLAPPEDWINDOW,
   szWindowClass,
   szTitle,
   WS_OVERLAPPEDWINDOW,
   CW_USEDEFAULT, CW_USEDEFAULT,
   500, 100,
   NULL,
   NULL,
   hInstance,
   NULL
);
复制代码

4.将窗口显示出来

接下来就是通过showWindow函数来显示窗口。

示例如下:

ShowWindow(hWnd,
   nCmdShow);
UpdateWindow(hWnd);

5.定义消息循环

这个过程是一个套路,没什么变化。简单来说,就是定义一个循环,从消息队列中取消息,然后翻译消息(消息结构体中的部分字段需要解码),最后分发消息给窗口过程函数处理。

GetMessage函数用于查看消息队列中是否有消息并取出。

TranslateMessage函数用于将虚拟码消息转换为字符消息。

DispatchMessage函数用于将消息分发到窗口过程中函数。

注意,消息队列有两个,一个是系统消息队列,另一个应用程序自己的消息队列(每一个应用程序都有自己的消息队列)。消息产生后是先到系统消息队列,然后分发到应用程序消息队列里等待被处理。

消息循环示例如下:

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

return (int) msg.wParam;

消息循环工作原理如下图所示:

 

 三、逆向时经验

逆向窗口程序时,我们要从WinMain函数入手,找到窗口类定义过程中关于窗口过程函数的赋值部分,追踪到窗口过程函数中去分析,分析其中每一条消息对应的处理过程,从而还原出程序功能逻辑。

窗口过程函数并不难找到,我们只需要在 RegisterClass函数调用前查看(WNDCLASSA *)lpWndClass对象,该对象成员中有窗口过程函数。

麻烦的是控件的消息处理函数,MFC框架通过消息映射表来绑定消息和消息处理函数的关系。

开发者使用宏“DECLARE_MESSAGE_MAP” 和 “END_MESSAGE_MAP”来定义消息映射表。

示例:

BEGIN_MESSAGE_MAP(CHelloDlg, CDialogEx)
    ON_WM_SYSCOMMAND()
    ON_WM_PAINT()
    ON_WM_QUERYDRAGICON()
    ON_BN_CLICKED(IDC_BUTTON1, &CHelloDlg::OnBnClickedButton1)    
END_MESSAGE_MAP()

对应使用的结构体为AFX_MSGMAP,定义如下:

struct AFX_MSGMAP
{
    const AFX_MSGMAP* (PASCAL* pfnGetBaseMap)();    // 父类消息映射表
    const AFX_MSGMAP_ENTRY* lpEntries;              // 自身消息映射表
};

AFX_MSGMAP_ENTRY结构体定义如下:

复制代码
struct AFX_MSGMAP_ENTRY
{
    UINT nMessage;   // windows message
    UINT nCode;      // control code or WM_NOTIFY code
    UINT nID;        // control ID (or 0 for windows messages)
    UINT nLastID;    // used for entries specifying a range of control id's
    UINT_PTR nSig;   // signature type (action) or pointer to message #
    AFX_PMSG pfn;    // routine to call (or special value)
};
复制代码

识别AFX_MSGMAP_ENTRY结构体的思路有:

内存搜索该结构体

我们可以通过Resource Hacker查看控件ID和消息ID,也就是nID和nCode。根据它们对应的十六进制数据在内存中搜索。

 

posted @   An2i  阅读(350)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
点击右上角即可分享
微信分享提示