ESP寻址
ESP作为堆栈指针寄存器,存储栈顶指针指向的地址。但是当出现堆栈传参之类的操作时,栈顶发生了变化,就需要先对栈顶指针进行修正后再用ESP进行寻址,比较麻烦
EBP寻址
EBP指向的是栈底,如果栈顶发生变化,也只会改变ESP的值。这时我们就可以通过EBP来寻址更为方便
回调函数的定位
下面有一个简单的窗口程序:
#include "stdafx.h"
#include "Tools.h"
HINSTANCE hAppInstance;
LRESULT CALLBACK WindowProc(
IN HWND hwnd,
IN UINT uMsg,
IN WPARAM wParam,
IN LPARAM lParam
);
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
// TODO: Place code here.
hAppInstance = hInstance;
//窗口的类名
PSTR className = "My First Window";
// 创建窗口类的对象
WNDCLASS wndclass = {0}; //一定要先将所有值赋值
wndclass.hbrBackground = (HBRUSH)COLOR_MENU; //窗口的背景色
wndclass.lpfnWndProc = WindowProc; //窗口过程函数
wndclass.lpszClassName = className; //窗口类的名字
wndclass.hInstance = hInstance; //定义窗口类的应用程序的实例句柄
// 注册窗口类
// 参加MSDN文档RegisterClass->Parameters:
// You must fill the structure with the appropriate class attributes
// before passing it to the function.
RegisterClass(&wndclass);
// 创建窗口
HWND hwnd = CreateWindow(
className, //类名
"我的第一个窗口", //窗口标题
WS_OVERLAPPEDWINDOW, //窗口外观样式
10, //相对于父窗口的X坐标
10, //相对于父窗口的Y坐标
600, //窗口的宽度
300, //窗口的高度
NULL, //父窗口句柄,为NULL
NULL, //菜单句柄,为NULL
hInstance, //当前应用程序的句柄
NULL); //附加数据一般为NULL
if(hwnd == NULL) //是否创建成功
return 0;
// 显示窗口
ShowWindow(hwnd, SW_SHOW);
// 更新窗口
UpdateWindow(hwnd);
// 消息循环
MSG msg;
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
LRESULT CALLBACK WindowProc(
IN HWND hwnd,
IN UINT uMsg,
IN WPARAM wParam,
IN LPARAM lParam
)
{
switch(uMsg)
{
//窗口消息
case WM_CREATE:
{
DbgPrintf("WM_CREATE %d %d\n",wParam,lParam);
CREATESTRUCT* createst = (CREATESTRUCT*)lParam;
DbgPrintf("CREATESTRUCT %s\n",createst->lpszClass);
break;
}
case WM_MOVE:
{
DbgPrintf("WM_MOVE %d %d\n",wParam,lParam);
POINTS points = MAKEPOINTS(lParam);
DbgPrintf("X Y %d %d\n",points.x,points.y);
break;
}
case WM_SIZE:
{
DbgPrintf("WM_SIZE %d %d\n",wParam,lParam);
int newWidth = (int)(short) LOWORD(lParam);
int newHeight = (int)(short) HIWORD(lParam);
DbgPrintf("WM_SIZE %d %d\n",newWidth,newHeight);
break;
}
case WM_DESTROY:
{
DbgPrintf("WM_DESTROY %d %d\n",wParam,lParam);
PostQuitMessage(0);
return 0;
break;
}
//键盘消息
case WM_KEYUP:
{
DbgPrintf("WM_KEYUP %d %d\n",wParam,lParam);
break;
}
case WM_KEYDOWN:
{
DbgPrintf("WM_KEYDOWN %d %d\n",wParam,lParam);
break;
}
//鼠标消息
case WM_LBUTTONDOWN:
{
DbgPrintf("WM_LBUTTONDOWN %d %d\n",wParam,lParam);
POINTS points = MAKEPOINTS(lParam);
DbgPrintf("WM_LBUTTONDOWN %d %d\n",points.x,points.y);
break;
}
default:
return DefWindowProc(hwnd,uMsg,wParam,lParam);
}
return 0;
}
这个窗口程序一共有六步:创建窗口类、注册窗口类、创建窗口、显示窗口、消息循环以及回调函数。在创建窗口类的时候会初始化窗口结构体的一些值,其中就包括了lpfnWndProc。lpfnWndProc储存了回调函数的地址,在而注册窗口类时用到的函数RegisterClass()会将这个窗口类的信息拿去注册。也就是说,只要找到RegisterClass()函数,再从它注册的内容中找到lpfnWndProc指向的地址,就能定位到这个回调函数
F8下来找到函数RegisterClass()
RegisterClass()函数只有一个参数,我们有理由猜测004010BC处的PUSH EDX语句就是传递的这个参数
然后我们单步到这个位置,查看堆栈
我们发现现在堆栈里面有连续的十个0,而刚好WNDCLASS类有十个成员,与我们的猜想有些印证。所以接下来我们锁定堆栈,单步到call,我们发现这四个mov指令刚好传进来4个参数,和我们初始化wndclass的成员数量一样
我们查文档,发现第二个参数就是lpfnWndClass
所以我们就找到了回调函数的地址
具体事件的消息定位
假设我们捕获鼠标左键的消息,我们先反汇编跟到上面这个回调函数的地方
由于消息是一直都在循环的,打开一个窗口就会出现好几十个事件,如果断在这里,会一直触发事件,不停地在这里下断点,因此我们在这里下一个条件断点
而由于MSG结构体里面的第二个成员就是消息ID,所以我们把条件加在[esp+8]处(假设只捕获鼠标左键的消息)
所有内容均参考滴水逆向三期