24win32编程基础——入口函数、ESP寻址和回调函数的定位
实验代码如下:
// 20C++Win32CreateWind.cpp : 定义应用程序的入口点。
//
#include "stdafx.h"
#include "20C++Win32CreateWind.h"
#include "OutPut.h"
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
int APIENTRY _tWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
//不用系统以及准备好的窗口类,我们自己创建1个窗口类
TCHAR className[] = TEXT("My Windows");
//WNDCLASS wndclas;小bug
WNDCLASS wndclas={0}; //这个用之前必须先初始化。
wndclas.lpszClassName = className; //别人用的时候,根据这个类名使用
wndclas.hbrBackground = HBRUSH(COLOR_GRAYTEXT); //窗口背景
wndclas.lpfnWndProc =WndProc ; //回调函数
wndclas.hInstance = hInstance; //应用程序句柄,哪个应用程序创建的这个窗口类,因为1个应用程序可能有很多个窗口。因此创建窗口的时候要指明窗口属于哪个应用程序
//注册,告诉操作系统有这么1个窗体类长这样,别的地方可以使用了。
RegisterClass(&wndclas);
//创建
HWND hwnd = CreateWindow(
className, //用我自己定义的窗体类,不用windows里面已经创建好的窗口类
TEXT("窗口的标题"), //窗口标题
WS_OVERLAPPEDWINDOW, //窗口外观样式
10,20, //坐标
300,600, //大小
NULL, //父窗口句柄,第一个窗口,目前没有父窗口
NULL, //菜单句柄,不带菜单栏
hInstance, //当前应用程序的句柄,窗口创建之后属于哪个应用程序
NULL); //附加数据
//显示窗口
ShowWindow(hwnd,SW_SHOW);
//消息循环
MSG msg;
int bRet = 0;
while( (bRet = GetMessage( &msg, hwnd, 0, 0 )) != 0)//不停的取消息
{
if (bRet == -1)
{
// handle the error and possibly exit
OutputDebugStringF("%d\n",GetLastError());
}
else
{
TranslateMessage(&msg); //翻译成更具体的消息
DispatchMessage(&msg); //再传递给操作系统,然后操作系统自动调用回调函数。
}
}
return 0;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
//OutputDebugStringF("%d\n",message);测试消息
switch(message){
case WM_CREATE:
{
DbgPrintf("WM_CREATE:%d\n",message);
CREATESTRUCT* cs = (CREATESTRUCT*)lParam;
DbgPrintf("ClassName:%s\n",cs->lpszClass);
OutputDebugStringQ(L"ClassName:%s\n",cs->lpszClass);
return 0; //处理完消息之后,要返回0,等于告诉系统已经处理过了。
}
case WM_DESTROY:
{
PostQuitMessage(0);
return 0;
}
case WM_KEYDOWN:
{
DbgPrintf("WM_KEYDOWN:%d,%d,%d\n",message,wParam,lParam);
return 0;
}
case WM_KEYUP:
{
DbgPrintf("WM_KEYUP:%d,%d,%d\n",message,wParam,lParam);
return 0;
}
}
//我不关系的消息,操作系统自己去处理
return DefWindowProc(hWnd, message, wParam, lParam);
}
1、关于入口函数的查找?
要找入口函数,首先得属性入口函数:比如有几个参数,参数的特征是什么
int APIENTRY _tWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
例如win32的入口函数:
1、4个参数
2、特征:第一个参数是加载地址ImageBase,第二个参数永远为0

反汇编的的我们这里看到4个参数(需要进函数内部看,确认一样,因为有寄存器传参的可能,因此这里不一定)
如何确认参数个数:
1、进入函数内部,查看是否存在寄存器传参数的可能 参考,不是一定
2、返回值retn n 参考,不是一定
进入这个函数内部查看一下:

查看是否存在:对寄存器初始化或者赋值等操作的过程,如果没有对寄存器赋值或者初始化等操作,而是在代码中直接拿来使用寄存器(参数直接通过寄存器传递过来),就说明可能是寄存器传参。

retn 0x10(疑似4个参数)
最终确认参数个数。
本程序也是ebp寻址,而不是esp寻址。
esp寻址的特点是,程序开头直接升栈,sub esp,0x64等,而程序中获取参数或者局部变量,直接使用的esp进行寻址,而不用push ebp等操作。
回调函数如何定位,我们知道回调函数是在RegisterClass中注册的,我们只有找到RegisterClass或者RegisterClass附件就能找到回调函数。
先定位到RegisterClass
然后根据这个参数确定WNDCLASS这个结构体

然后观察第二个参数的值,即为回调函数的地址:0x401160
然后我们右键---->在反汇编窗口中跟随,定位到这个地址。

到这一步之后,思考1个问题,如何找到我们关心的消息处理函数呢?
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
//OutputDebugStringF("%d\n",message);测试都有哪些消息会过来,发现很多消息都会被传递过来
switch(message){
case WM_CREATE:
{
DbgPrintf("WM_CREATE:%d\n",message);
CREATESTRUCT* cs = (CREATESTRUCT*)lParam;
DbgPrintf("ClassName:%s\n",cs->lpszClass);
OutputDebugStringQ(L"ClassName:%s\n",cs->lpszClass);
return 0; //处理完消息之后,要返回0,等于告诉系统已经处理过了。
}
case WM_DESTROY:
{
PostQuitMessage(0);
return 0;
}
case WM_KEYDOWN:
{
DbgPrintf("WM_KEYDOWN:%d,%d,%d\n",message,wParam,lParam);
return 0;
}
case WM_KEYUP:
{
DbgPrintf("WM_KEYUP:%d,%d,%d\n",message,wParam,lParam);
return 0;
}
}
//我不关系的消息,操作系统自己去处理
return DefWindowProc(hWnd, message, wParam, lParam);
}
因此我们使用
OutputDebugStringF("%d\n",message)测试发现,这个回调函数会接收太多的消息了。
那我们想找到想要的处理消息的位置呢,比如鼠标按下的时候的消息处理函数?
我们观察LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
再根据我们前边学的堆栈图和参数入栈顺序:在堆栈中[esp+8]存的就是消息的ID
因此可以设置条件断点

这样就断下来了。


浙公网安备 33010602011771号