C++程序反编译笔记(15) 扫雷雷数代码识别与修正

分析主绘图函数
主绘图函数sub_1002AC3就是简单调用了其他函数而已, 并且, 绘图函数只是根据数据显示图像, 基本上不会导致程序崩溃. 因此, 可以采用依次注释掉某个函数, 观察界面的方法来分析主绘图函数.
以下是分析出来的结果, 注释掉对应的函数后, 界面上的某个区域就不再绘制, 看不到了.


重构绘制地雷数的主函数
代码重构是反编译过程中很重要的操作, 它可以让代码变得易懂.
以下是绘制地雷数的函数的原始代码.

修正该函数用到了以下重构方法:
(1) 重命名. 已知标识符(函数, 变量等)的作用, 那么给它一个有意义的名字. 此处重命名了函数, 变量hdca.
(2) 常量替换. 变量v3赋值后保持不变, 那么可以将赋值后的v3当作常量. 变量v4由v3赋值, 赋值后也不变, 因此可以使用v3代替v4, 从而减少一个变量.
(3) 首次使用时定义. 一个变量, 在第一次使用它的时候才定义它, 而不是将所有变量都放在函数开头定义, 这样有利于分析变量的作用, 也可以减少函数的代码函数.
以下是修正后的代码


分析调用函数
分析sub_1002752, 一个变量的意义需要通过分析它的赋值和取值的地方来得出. 重构后的sub_1002752如下. 可以看到第3个参数是一个数组的索引, 因此我给它起名index. 从而可以得出DrawMineCount函数的v2, v3都与数组索引有关.


数组赋值循环条件错误
要得出v2, v3的具体含义还需要知道那个数组dword_1005A60的含义. 首先观察它的定义

接着, 观察它的引用(给它赋值和取值的地方). 使用Visual Studio的搜索功能就可以看到所有引用它的地方了. 很幸运, 只有一处给它赋值, 只有上面提到的那一处使用了它的值. 观察给它赋值的地方

显然, 这里使用了循环来给数组dword_1005A60赋值. 但是, 这里循环的条件错了. 在exe文件中, dword_1005A60和dword_1005A90是连续排列的. 而Visual Studio中, 它们却是两个变量, 虽然是连着定义, 但是C/C++标准并没有保证连续定义的变量会连续存放. 所以, 这个循环很可能是死循环.
根据这两个变量的后缀地址 1005A90 和 1005A60, 可以得出:
1005A90 - 1005A60 = 0x30,
0x30 ÷ 4 = 12
所以, dword_1005A60确实是12个4字节的数组, 至于是不是int类型还不知道. 因而, 把这个循环的条件改成指定数量的形式才是正确的. 如下

v9 可以删掉了.

顺便修复另一个数组循环条件错误
往上看了一个, 还有一个数组循环条件错误. 修正后如下


修正成果
再写下去就太长了, 看一下本次的成果吧.

本次主要是重构了绘制左上地雷数的代码, 及修正了两个数组循环错误. 第一个数组循环错误似乎对界面没什么影响(可能是巧合, 两个变量正好连续存放); 但是第二个数组循环错误就让界面变得比较好看了, 雷区的"乱码"变成了红旗.