第四十五课 win32 esp寻址_定位回调函数
自己vs编译的exe入口函数好像和课程视频哩的vc6不一样,没办法跟着视频走,可以用课件里给的作业exe勉强跟着视频学,前面的都差不多,课件下载地址:https://www.bcdaren.com/video/videoPlay/330318515062181888
1.win32应用程序入口识别
没加过壳的程序好找,直接打开后往下找就行了
入口程序的特征有四个参数,看看往下看看哪里有push了四个参数且call了kernel32.GetModuleHandleA函数,其返回值eax也push进去了
但是这也不能确定就是四个参数,因为windows编程默认使用内平栈,选中按回车键跟进去看看ret是不是10,十六进制10代表十进制的16个字节,刚好就是四个参数,
但是还是不能确定,因为有可能是fastcall,剩下两个参数用寄存器传参进去,所以我们进函数继续确认,
可以看到被使用的ECX和EAX都是在本函数内被赋值的,不是从外面就赋值好被使用的,说明不是fastcall
2.ESP寻址
进入一个函数,看到函数最开始的地方没有和其他的函数一样有许多开辟空间各种操作,只有一个SUB ESP,54
,这种开辟空间的方式比较方便
如果现在把断点下在这一行,那么现在堆栈里就只有四个值,分别是返回地址和三个参数
比如 想找返回地址的时候直接ESP+54
即可,找第一个参数则是ESP+58
但是寻址比较麻烦,因为下面如果有一个push,找返回地址就需要变成ESP+58
总结:
3.窗口回调函数的定位
回想上节课有一个地方wndclass.lpfnWndProc定义了回调函数的名字,然后又使用RegisterClass()注册了这个回调函数,所以我们现在需要去找RegisterClass()
可以看到在调用RegisterClass函数时push了eax进去,说明eax是传递进去的参数,RegisterClass(&wndclass);
这是注册窗口类的代码,可以看出eax就是这个&wndclass的结构体指针,我们进堆栈看看这个结构体指针,根据wndclass结构体的结构可以知道第二个就是回调函数的地址
4.具体事件处理的定位(条件断点)
断点下到回调函数地址第一行
由于这个地方是一直在循环很多个消息id的,所以断点在这会触发很多个事件,所以我们需要给断点设置个条件,让他只关注点击左键时
所以我们点右键点空格他都会执行,不会断,当我们点左键时他就断点了
断点后直接再执行几行代码就到了点击左键时的代码了
第四十六课 win32 子窗口_消息处理函数
1.按钮是什么
HINSTANCE hAppInstance;
void CreateButton(HWND hwnd)
{
HWND hwndPushButton;
HWND hwndCheckBox;
HWND hwndRadio;
hwndPushButton = CreateWindow (
TEXT("button"),
TEXT("普通按钮"),
//WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_DEFPUSHBUTTON,
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_DEFPUSHBUTTON,
10, 10,
80, 20,
hwnd,
(HMENU)1001, //子窗口ID
hAppInstance,
NULL);
hwndCheckBox = CreateWindow (
TEXT("button"),
TEXT("复选框"),
//WS_CHILD | WS_VISIBLE | BS_CHECKBOX | BS_AUTOCHECKBOX,
WS_CHILD | WS_VISIBLE | BS_CHECKBOX |BS_AUTOCHECKBOX ,
10, 40,
80, 20,
hwnd,
(HMENU)1002, //子窗口ID
hAppInstance,
NULL);
hwndRadio = CreateWindow (
TEXT("button"),
TEXT("单选按钮"),
//WS_CHILD | WS_VISIBLE | BS_RADIOBUTTON | BS_AUTORADIOBUTTON,
WS_CHILD | WS_VISIBLE | BS_RADIOBUTTON ,
10, 70,
80, 20,
hwnd,
(HMENU)1003, //子窗口ID
hAppInstance,
NULL);
}
总结:
- 在入口函数调用时应写在父窗口后面
2.按钮事件的处理
按钮的WNDCLASS类不是我们定义的,我们可以用下面方法查看系统预定义的WNDCLASS都包含什么样的信息
//查看单个属性
TCHAR szBuffer[0x20];
GetClassName(hwndPushButton,szBuffer,0x20);
//放到结构体里查看
WNDCLASS wc;
GetClassInfo(hAppInstance,szBuffer,&wc);
OutputDebugStringF("-->%s\n",wc.lpszClassName);
OutputDebugStringF("-->%x\n",wc.lpfnWndProc);
首先我们理解一个东西,我们在父窗口的回调函数写一个左键点击就会打印坐标的功能,那么我们点击按钮他则不会打印,所以我们可以初步分析子窗口和父窗口的回调函数是分开的且按钮的回调函数是系统写好的
然后我们可以在父窗口的回调函数种加上这段代码
case WM_COMMAND:
{
switch(LOWORD(wParam))
{
case 1001:
MessageBox(hwnd,"Hello Button 1","Demo",MB_OK);
return 0;
case 1002:
MessageBox(hwnd,"Hello Button 2","Demo",MB_OK);
return 0;
case 1003:
MessageBox(hwnd,"Hello Button 3","Demo",MB_OK);
return 0;
}
return DefWindowProc(hwnd,uMsg,wParam,lParam);
}
现在我们可以知道了按钮是怎么处理消息的
他会先执行系统提供的WinProc,然后从左键类型WM_LBUTTONDOWN转换WM_COMMAND类型再调给父窗口的WinProc(粗体字是重点)
所以子窗口和父窗口的信息处理函数最后都会转到父窗口的
3.消息堆栈
回调函数的结构
LRESULT CALLBACK WindowProc(
IN HWND hwnd,
IN UINT uMsg, //消息代码,比如WM_COMMAND是0x111
IN WPARAM wParam,
IN LPARAM lParam
);
回调函数的堆栈
4.按钮事件处理逻辑定位
跟之前具体消息事件的定位一样,只是判断条件改一下[esp+0x8] == WM_COMMAND&&[ESP+0XC] == 按钮id
第四十七课 win32 资源文件_消息断点
1.资源文件_DialogBox创建对话框
滴水课程里的vc6和我用的vs2019差距有点大,很多功能找不到,查了一下好像都需要mfc,暂且先跳过一些东西
a.在vs的项目右键添加资源
b.通过DialogBox创建对话框
c.在入口函数前面声明或者定义我们的DialogProc函数
#include<windows.h>
#include "resource.h"
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 IDOK:
MessageBox(NULL, TEXT("IDC_BUTTON_OK"), TEXT("OK"), MB_OK);
return TRUE;
case IDCLOSE:
MessageBox(NULL, TEXT("IDC_BUTTON_OUT"), TEXT("OUT"), MB_OK);
EndDialog(hwndDlg, 0);
return TRUE;
}
break;
}
return FALSE;
}
int CALLBACK WinMain(
_In_ HINSTANCE hInstance,
_In_ HINSTANCE hPrevInstance,
_In_ LPSTR lpCmdLine,
_In_ int nCmdShow
)
{
DialogBox(hInstance, MAKEINTRESOURCE(IDD_DIALOG1), NULL, DialogProc);
}
2.获取文本框内容
a.获取文本框句柄(通用的)HWND hEditUser = GetDlgItem(对话框句柄,文本框ID);
b.通过文本框句柄获取里面的内容TCHAR szUserBuff[0x50];
GetWindowText(文本框句柄,szUserBuff,0x50);
3.定位对话框回调函数(消息断点)
消息断点其实就是系统帮我们下的条件断点**下面逆向案例的背景就是:如果一个对话框比较复杂,很难直接和之前一样通过入口找到回调函数,那么我们就通过按钮系统定义的函数,去找到我们的回调函数,因为系统定义的函数最终也会调用我们父窗口的回调函数(上节课有讲到)**
通过消息断点的逆向过程
所以我们直接在od运行一遍程序
这时按w这里就可以看到很多句柄,很多7开头的函数地址就是系统的函数,
这里选(鼠标左键按下)201还是(鼠标左键松开)202,我们可以运行程序,点登录按钮不松开,如果还没触发事件,那就不是201,松开鼠标左键触发事件,则可以确定是202
下好断点后,我们按一下登录按钮,就运行到断点处了
最笨的方法就是一步步跟下去,但是这太麻烦,有个小技巧,我们到m这里在代码段下一个内存断点,只要访问到这块内存就会断
成功断到回调函数的地方,但是我们要知道其他复杂一点的程序这时候不一定就会运行到回调函数的地址,因为不一定访问这块内存的时候就是来访问父窗口的回调函数,因为还没点击按钮之前说不定有其他消息,比如这次消息,下面的堆栈第三个是135,可以看到不是我们想要找的消息处理函数
所以我们可以通过看此时回调函数的堆栈,看第一个参数是返回地址,但二个参数是句柄,第三个参数是消息代码,如果看起来不是,直接一直F8走回7开头地址的系统函数,然后再F9走到下次断点的地方,因为这里是消息循环,所以下次断点可能是我们想要的消息处理函数,就这样循环知道看到堆栈的第三个数是111,也就是WM_COMMAND的消息代码,可以确定这次找到的消息循环是我们要的消息处理函数