如果周围的窗口似乎是一个真正的窗口,但单个按钮不是,我会假设
- 周围的窗口显示位图,或执行其他任何需要显示单个按钮的操作
- 周围的窗口对鼠标点击做出反应
- 并且,周围的窗口通过查看鼠标单击时的鼠标坐标来确定使用哪个“按钮”。
所以,我们要做的是,在调试器中启动程序,并使用鼠标点击时触发的断点。问题是,窗口过程被多次调用,包含所有类型的消息,而您想中断其中的一些(例如,WM_LBUTTONDOWN),而不是大多数其他消息(例如,WM_PAINT)。
- Ollydbg 可以直接做到这一点——放置一个根据窗口消息触发的条件断点。
- 如果,无论出于何种原因,您无法使用该功能,您需要反汇编(并部分理解)窗口过程。它将接收堆栈上的 4 个参数,其中第二个是窗口消息(请参阅使用窗口过程)。窗口过程将在某个时候将此参数与众所周知的窗口消息 ID 的值进行比较。您可能对
WM_LBUTTONDOWN( 0x0201) 或其他鼠标输入事件之一感兴趣。检查代码在收到这些“有趣”消息之一时分支到的位置,并在该分支上放置一个断点。
放置合适的断点后,在调试器中运行您的应用程序,单击鼠标,并希望调试器真正命中断点。如果没有,请检查您犯了哪个错误。
单步执行您的代码。它可能会从堆栈中获取参数 4,它对鼠标的 X 和 Y 坐标进行编码,并将这些参数与某些东西进行比较 - 很可能,有一个定义矩形和跳转地址的结构数组,以及一个循环检查每个入口。
一旦你找到了,你就完成了。
听起来太容易了?这可能是因为,根据您的框架,实际的比较可能被深深地埋在原始源代码中一堆 C++ 类的东西中,并且可能有各种调用,其中一些是间接调用(在 C++ 类的情况下)方法),直到到达“有趣”的位置。
有时使用 Ollydbg 更容易解决这个问题。在断点后单步执行源代码,使用“step over”来跳过函数调用。每当您看到函数调用中发生了有趣的事情时,请记下地址。重新启动你的程序,运行到你的断点,运行到你记下的地址,这次“步入”函数。这将允许你把手指放在有趣的代码比较快,而跨过所有的strcmp()和malloc()和WriteALotOfStuffToSomeFileIfDebuggingIsOn()功能。
有时使用 IDA 更容易。从您的窗口过程生成一个调用树,关注前 3-5 个级别。在您的代码中搜索出现的幻数,0x0201如上所示。检查其中一个是否CMP XX, 0x0201在您的调用树中的其中一个函数中。这可能正是您正在寻找的职位。
制作一个屏幕截图,并将其粘贴到某个图像查看器程序中(我喜欢Irfanview这样)。获取框架窗口内按钮的像素坐标。在数据部分中搜索那些被类似数据表包围的值。这可能是您的坐标/跳转表。如果你发现了一些东西,在它上面放置一个硬件断点以找出该内存何时被访问,或者使用 IDA 交叉引用列表来获取代码中的相应地址。