Win32 ESP寻址 _ 定位回调函数

本文详细解释了Win32程序中WinMain函数的四个参数含义,包括句柄、命令行参数和窗口显示方式,以及如何通过ESP寻址和窗口回调函数定位。还介绍了如何通过IAT表确定函数名称和消息处理的定位技巧。
摘要由CSDN通过智能技术生成

1.Win32程序入口识别

WinMain函数的四个参数:
int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)

//这里许多我们没见过的类型,其实都是系统定义的宏,通过F12跳转我们可以发现
//其实都是一些常见的类型宏定义出来的
//甚至有很多类型相同,但是宏定义的名称不同

第一个参数 :hInstance 表示该程序当前运行的实例的句柄,这是一个数值。当程序在 Windows 下运行时,它唯一标识运行中的实例(注意,只有运行中的程序实例,才有实例句柄)。一个应用程序可以运行多个实例,每运行一个实例,系统都会给该实例分配一个句柄值,并通过 hInstance 参数传递给 WinMain 函数。

第二个参数: hPrevInstance 表示当前实例的前一个实例的句柄。通过查看 MSDN 我们可以知道,在 Win32 环境下,这个参数总是 NULL ,即在 Win32 环境下,这个参数不再起作用。

第三个参数 :lpCmdLine 是一个以空终止的字符串,指定传递给应用程序的命令行参数。 例如:在 D 盘下有一个 sunxin.txt 文件,当我们用鼠标双击这个文件时将启动记事本程序( notepad.exe ),此时系统会将 D:/sunxin.txt 作为命令行参数传递给记事本程序的 WinMain 函数,记事本程序在得到这个文件的全路径名后,就在窗口中显示该文件的内容。要在 VC++ 开发环境中向应用程序传递参数,可以单击菜单 【 Project 】→【 Settings 】,选择“ Debug ” 选项卡,在“ Program arguments ”编辑框中输入你想传递给应用程序的参数。

第四个参数: nCmdShow 指定程序的窗口应该如何显示,例如最大化、最小化、隐藏等。这个参数的值由该程序的调用者所指定,在调用ShowWindow()时可以使用到该值。

我们通过windows+R键,输入cmd打开控制台,输入D:后回车,再输入文件名+需要传递的参数abcdef,此时我们可以在DebugView上观察到WinMain函数接受到的参数就是abcdefg

 win32程序入口识别:

首先我们用DTDbug所在文件夹,打开udd文件夹,清空里面的缓存,再打开exe程序

我们首先进行分析,WinMain函数有四个参数,其中第一个参数是该程序的句柄

我们发现,在此处调用了一个函数,通过函数名我们大概可以猜到是获取句柄的一个函数,我们通过查文档可知:

If the function succeeds, the return value is a handle to the specified module.

如果函数成功,返回值是指定模块的句柄。

If the function fails, the return value is NULL. To get extended error information, call GetLastError.

如果函数失败,返回值为NULL。要获取扩展的错误信息,请调用GetLastError。

并且我们发现,call下一指令是push EAX,因此我们可以知道,GetModuleHandle函数的返回值通过寄存器传参给了EAX

push EAX后,call了一个函数,因此我们可以推测这就是WinMain函数的入口,我们Enter进入,查看平栈可以知道有几个参数

这里为什么DTDbug可以知道调用函数名字?

因为导入表的存在,此时call的地址就是IAT表

我们判断参数的个数并不能完全相信call前面push了几个参数,需要到函数内部观察平栈时恢复了几个,我们发现,该函数平栈时return 10,加上该函数是 stdcall,参数是从右至左进行压栈的。

我们可以断定这个call就是调用WinMain函数的入口

2.ESP寻址的特点

ESP寻址:可直接sub ESP来提升堆栈,ESP+提升的大小就可直接访问到参数

问题:在每一次push后,ESP的位置都会发生变化,因此,想要通过ESP来寻找到参数,需要计算ESP到参数的距离。

可通过双击OD右下角的地址处可以得到偏移量

3.窗口回调函数的定位

思路:

WinMain函数的流程是我们首先创建了一个WNDCLASS结构体的对象,并且定义了属性

然后再通过RegisterWindow函数对窗口类进行注册

wndclass对象中有一个成员存储了回调函数的名称,并且该对象的地址在RegisterWindow函数中作为参数进行传递

因此我们想要找到回调函数就要先找到RegisterWindow函数的参数

我们在push ECX处下一个断点,然后运行来到断点处,通过右上角我们可以得到ECX的值,我们鼠标右键跟进堆栈

此时我们可以发现,堆栈中存储的就是wndclass对象的10个成员

typedef struct tagWNDCLASSA {
    UINT        style;
    WNDPROC     lpfnWndProc;
    int         cbClsExtra;
    int         cbWndExtra;
    HINSTANCE   hInstance;
    HICON       hIcon;
    HCURSOR     hCursor;
    HBRUSH      hbrBackground;
    LPCSTR      lpszMenuName;
    LPCSTR      lpszClassName;
} WNDCLASSA, *PWNDCLASSA, NEAR *NPWNDCLASSA, FAR *LPWNDCLASSA;

第二个成员就是我们要找的回调函数的地址,我们Enter跟进,这里就是回调函数的入口位置

4.具体消息处理的定位

我们前面已经定位到了回调函数的内部,下面我们要定位到回调函数对于具体消息处理功能的位置

首先我们在回调函数头部下一个断点,清楚其余的断点

例如,我们需要定位对于鼠标左键消息处理的位置:

鼠标左键消息类型名是:WM_LBUTTONDOWN

设置条件[esp+8]==WM_LBUTTONDOWN

注意:这里为什么认定为是[esp+8]

因为我们定义的回调函数

	LRESULT CALLBACK WindowProc(
								IN HWND hwnd,
								IN UINT uMsg,
								IN WPARAM wParam,
								IN LPARAM lParam)
	{

他的类型是stdcall,参数压栈是从右至左进行压栈

因此在【esp+4】==hwnd

           【esp+8】==uMsg

           【esp+c】==wParam

           【esp+10】==IParam

并且,虽然是ESP寻址,但是我们的条件是定在刚调用函数时,参数已经压栈,并且ESP还未进行提升堆栈的时候

设置完条件后,我们就可以点击运行,此时会跳出窗口,除了跟鼠标左键意外的消息,此时堆栈都不会发生变化

我们鼠标左键后,窗口消失,我们F8逐步运行来到第一个call的位置就是具体消息处理的位置了

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值