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

因此可以设置条件断点

 这样就断下来了。

 

posted @ 2023-10-23 18:15  一日学一日功  阅读(192)  评论(0)    收藏  举报