深入理解win32(四)

 

前言

在上一节中我们对子窗口和在od中如何定位消息处理函数进行了探究,在这一节里面我们来看一下资源文件和如何在od中定位对话框的回调函数。

 

资源文件创建对话框

何为资源文件?看一下msdn的解释

术语 “资源 文件”可以指多种文件类型,例如:
程序的资源脚本 (.rc) 文件。
资源模板 (.rct) 文件。
作为独立文件存在的单个资源。 此类型包括从 .rc 文件引用的位图、图标或游标文件。
由开发环境生成的头文件。 此类型 Resource.h 包括 .rc 文件中引用的 。
在其他文件类型(如 .exe、.dll 和 .res 文件)中找到的资源称为 资源。
可以在项目中使用资源文件和资源。 还可以处理不是当前项目一部分或在项目开发环境外部创建的Visual Studio。 例如,可以:
使用嵌套的条件包含资源文件。
更新现有资源或将其转换为Visual C++。
从当前资源文件中导入图形资源或向其导出图形资源。
包含不能由开发环境修改的共享或只读标识符(符号)。
在可执行文件 (.exe) 文件包含不需要编辑 (或不应编辑) 的资源,例如多个项目之间的共享资源。
包含开发环境不支持的资源类型。

我们在前面几节中创建了一个窗口,并在窗口里面添加了一系列的按钮,今天我们就使用到资源文件来实现创建窗口的操作。

这里我们首先创建一个资源文件

手动添加resource.h头文件

右键在rc文件处加入Dialog并点击新建

可以看到创建成功

右键修改对话框的属性

F7编译后会在resource.h头文件中定义一个dialog

然后就可以用DialogBox()生成dialog

GetProcAddress

FARPROC GetProcAddress(
  HMODULE hModule,    // handle to DLL module
  LPCSTR lpProcName   // function name
);
  • hModule[in] Handle to the DLL module that contains the function or variable. The LoadLibrary or GetModuleHandle function returns this handle.
  • lpProcName[in] Pointer to a null-terminated string containing the function or variable name, or the function’s ordinal value. If this parameter is an ordinal value, it must be in the low-order word; the high-order word must be zero.

GetProcAddress的作用是通过hModule到导出表里找到dll,然后通过名字得到dll地址

char szDllBuffer = "kernel32.dll";
char szFunctionName = "Function";

GetProcAddress(szDllBuffer,szFunctionName);

但是我们知道在导入表里有两种导入方式,一是名称导入,二是序号导入,那么如果是序号导入的情况下我们就需要把序号强转为char*类型后再去查询,即用指针去查询

char szDllBuffer = "kernel32.dll";
char szFunctionName = "Function"; //序号为11

GetProcAddress(szDllBuffer,(char*)11);

Dialogbox

INT_PTR DialogBox(
  HINSTANCE hInstance,  // handle to module
  LPCTSTR lpTemplate,   // dialog box template
  HWND hWndParent,      // handle to owner window
  DLGPROC lpDialogFunc  // dialog box procedure
);
  • hInstance[in] Handle to the module whose executable file contains the dialog box template.
  • lpTemplate[in] Specifies the dialog box template. This parameter is either the pointer to a null-terminated character string that specifies the name of the dialog box template or an integer value that specifies the resource identifier of the dialog box template. If the parameter specifies a resource identifier, its high-order word must be zero and its low-order word must contain the identifier. You can use the MAKEINTRESOURCE macro to create this value.
  • hWndParent[in] Handle to the window that owns the dialog box.
  • lpDialogFunc[in] Pointer to the dialog box procedure. For more information about the dialog box procedure, see DialogProc.

第一个参数hInstance相当于ImageBase,第二个参数lpTemplate就是dialog需要的模板,这里就是我们之前自己手动生成的dialog,第三个参数hWndParent为父进程的句柄,如果没有父进程的话就写NULL,第四个参数lpDialogFunc就是当前窗口的消息处理函数

消息处理函数代码如下:

BOOL CALLBACK DialogProc(                                    
                         HWND hwndDlg,  // handle to dialog box            
                         UINT uMsg,     // message            
                         WPARAM wParam, // first message parameter            
                         LPARAM lParam  // second message parameter            
                         )            
{                                    

    switch(uMsg)                                
    {                                
    case  WM_INITDIALOG :                                

        MessageBox(NULL,TEXT("WM_INITDIALOG"),TEXT("INIT"),MB_OK);                            

        return TRUE ;                            

    case  WM_COMMAND :                                

        switch (LOWORD (wParam))                            
        {                            
        case   IDC_BUTTON_OK :                            

            MessageBox(NULL,TEXT("IDC_BUTTON_OK"),TEXT("OK"),MB_OK);                        

            return TRUE;                        

        case   IDC_BUTTON_ERROR:                            

            MessageBox(NULL,TEXT("IDC_BUTTON_ERROR"),TEXT("ERROR"),MB_OK);                        

            EndDialog(hwndDlg, 0);                        

            return TRUE;                        
        }                            
        break ;                            
    }                                    

    return FALSE ;                                
}

这里我直接使用IDD_DIALOG_MAIN这个定义好了的宏发现报错,报错原因是不能够把int类型转为char*类型。在windows里面是不允许直接使用数字去查询的,跟上面所提到的GetProcAddress()函数一样,这里也需要使用指针去查询

这里可以使用(char*) IDD_DIALOG_MAIN进行强转,但是这里msdn里给我们定义了一个宏方便我们进行转型

You can use the MAKEINTRESOURCE macro to create this value.

那么这里就可以使用MAKEINTRESOURCE (IDD_DIALOG_MAIN)即可

即可弹出窗口

使用资源文件的话可以简化为两个步骤:创建窗口和提供消息处理函数

 

添加按钮&文本框

双击资源文件,添加两个按钮,然后点击下方两个按钮即可调整为两个完全相同的按钮

右键点击属性进行修改

回到resource.h发现又多了两个定义

这里加一下两个按钮的消息处理函数

F7编译运行看一下效果

继续再生成两个文本框,分别为UserNamePassWord,左边的为静态即写死了的文本框,右边的为动态即可编辑的对话框

这里我想把UserName框和PassWord框里面的字符取出来打印,就需要以下两个步骤

  • 获取文本框句柄
  • 获取文本框内容

这里获取文本框句柄可以使用如下语句

HWND hEditUser = GetDlgItem(hDlg,IDC_EDIT_USER);

GetDlgItem

HWND GetDlgItem(
  HWND hDlg,       // handle to dialog box
  int nIDDlgItem   // control identifier
);
  • hDlg[in] Handle to the dialog box that contains the control.
  • nIDDlgItem[in] Specifies the identifier of the control to be retrieved.

然后再就是获取文本框的内容

TCHAR szUserBuff[0x50];    

GetWindowText(hEditUser,szUserBuff,0x50);

GetWindowText

int GetWindowText(
  HWND hWnd,        // handle to window or control
  LPTSTR lpString,  // text buffer
  int nMaxCount     // maximum number of characters to copy
);
  • hWnd[in] Handle to the window or control containing the text.
  • lpString[out] Pointer to the buffer that will receive the text.
  • nMaxCount[in] Specifies the maximum number of characters to copy to the buffer, including the NULL character. If the text exceeds this limit, it is truncated.

GetWindowText处下一个断点

这里在UserNamePassWord处分别填上

点击OK,看一下szUserBuff

F10单步往下跟,发现已经存到了缓冲区里面

szPassBuff缓冲区也同理

 

定位对话框回调函数

打开od进入入口函数,找到DialogBoxParamA,就是我们之前写的那个dialog对话框的处理函数

ctrl+g跟到回调函数

下断点添加判断条件

点击OK暂停下来

 

消息断点

这里因为我们自己写的这个程序很简单,很容易找到消息处理函数,但是如果逆向其他的程序的时候界面窗口很多的情况下就很难找到消息处理函数,所以这时候我们就需要用到消息断点

这里我们先运行一下

找到W按钮这个地方

右键下一个ClsProc的消息断点

这里因为我们要跟子窗口的这个消息处理函数,我们知道子窗口的消息处理函数调用了父窗口的WM_COMMAND,所以这个地方我们需要找的是鼠标左键进行的操作。这里可以看到鼠标左键是有两个操作的,一个是WM_LBUTTONDOWN,一个是WM_LBUTTONUP,那么这两个有什么区别呢

WM_LBUTTONDOWN就是当你鼠标在按下去的那一刻但是没有松开鼠标的时候,如果有消息发生就设置WM_LBUTTONDOWN,如果在鼠标松开的那一刻有消息发生就设置WM_LBUTTONUP,所以这里就需要使用到202的消息处理函数

这里再到断点的地方去看一下

当我点击OK这里就已经断点断下来了

但是他这里只是断到了内存里系统的这个函数上,还不够,还要继续往下找

到内存资源图里看一下,看到了pe结构

这里也就是说如果你要使用button这个结构,pe文件使用的时候必须要调用我们刚才断点所在的函数,所以这里继续右键在.text处设置内存访问断点

设置断点后f9,发现已经跳到了401000,但是这里注意一下,这个函数并不能百分之百确定就是我们点击按钮的时候调用的消息处理函数,所以这里还需要判断一下

这里看一下我们现在跟到的这个函数的esp+8的位置,即消息类型,我们知道如果是正确的调用函数,esp+8的位置是WM_COMMAND,对应的编号为111,而这里的编号为135,明显就不是我们找的那个函数

那么这里继续F8单步往下跟,可以看到这里有一个retn语句

retn语句执行之后又返回到了内存中的地址

这时候再F9运行又回到了401000但是这时候的消息编号已经是WM_COMMAND

(完)