C++程序反编译笔记(18) 拆分函数
如果无法找到突破口, 那么就从界面入手.
程序总是要输入和输出的, 这里说的界面是指显示输出的界面, 可以是控制台, 窗口, 文本文件等.
一般程序总是调用系统API来制作界面的, 操作系统屏蔽掉了显卡显示器等的操作接口.
而操作系统的API的参数和返回值都是可以查到的, 这就有利于反编译时分析代码, 从已知的推断未知的.

消息处理
Windows操作系统的界面是基于消息处理的, 因此, 要了解操作界面后会调用什么代码, 就需要分析窗口的消息处理函数.

从上图中的 lpfnWndProc 字段可以知道扫雷主窗口的消息处理函数.

拆分函数
为什么要拆分函数? 因为很大的函数看起来十分头疼, 我认为一个漂亮的函数应该在50行左右.
MainWndProc_1001BC9 就是一个比较大的函数, 我们需要把它按照不同的消息来拆分. 比较好的做法是将它改成一个仅有一个switch语句的函数, 而且每个case分支只是简单的调用具体的消息函数, 这样我们看起来就一目了然了.
本来它是这样的


里面有很多if, switch 和 goto, 看起来还行, 不是特别乱. 拆分后是这样的

一般是On + 消息名来给某个消息命名处理函数, 这样找起来比较方便.
可以使用 TortoiseGit 将我上传到gitee的项目切换到 21 这个版本来对比拆分前后的区别.


菜单资源
我并没有从鼠标消息入手, 而是从菜单消息入手. 原因是直接看鼠标消息的处理代码, 肯定会遇到很多不知道具体含义的变量, 从菜单入手有一个好处就是菜单项是有文本的, 我们可以根据文本直接对应的处理代码做了什么.
菜单也是一种资源, 我们可以从Visual Studio的资源视图看到菜单的消息.

比如, 点击"开局"菜单, 那么对应的代码肯定是游戏开始要做的事了. 而初级, 中级, 高级, 自定义这四个菜单就更好理解了, 设置游戏级别(难度), 那么我们就可以知道表示游戏级别的变量是哪个了.
菜单点击对应的消息是WM_COMMAND, 这里我用OnCommand函数来处理该消息.

再资源视图选中某个菜单项, 在属性窗口就可以看到它的ID了, 我们需要根据ID来找到它的处理代码.

WM_COMMAND消息处理中的WPARAM参数的低2个字节就是菜单项ID. 具体的处理代码就下篇再说了.