放出旗帜! —扫雷

从今天起两周,将举行第一次“低级别与安全性庆祝”。 这次活动是由包特组织的,其目的是吸引更多的女性进入低层和安全领域。

教授逆向工程讲习班给了我惊人而艰巨的任务。 当我开始计划研讨会的最后一次会议时,我不知道该怎么办。 我一直在寻找可以在我的工作室中使用的有趣挑战。

我的一位亲爱的朋友,Aviad Carmel,是一位超级技能的反向器,也是我的反向工程导师。 他建议我对Minesweeper进行黑客攻击,以便在Minesweeper启动时,所有地雷都标记有标志

我花了一个星期,数十小时的时间,完成了许多Windows API Google搜索以及完成许多断点的工作,但我终于做到了。

我非常激动,不得不发布一些推文,当我收到许多喜欢,评论,转推和GitHub链接请求时,这被证明是一件很棒的事情。 我什至认识了一个非常有才华的年轻研究员,他(也很奇怪)巧合了Minesweeper,并发表了自己的文章!

我的策略是这样的:

  • 查找在右键单击时绘制标记的代码(我们将此函数称为draw_flag)
  • 查找绘制木板的代码(让我们将此函数称为draw_board)
  • 更改函数draw_board ,以便每当遇到一个正方形时-使用适当的参数调用draw_flag

这很乏味,但最终我找到了在右键单击时在正方形上绘制标志的线。 当用F8越过这条线时,标志立即出现在板上。

根据MSDN,此行上的函数“ BitBlt”“将与像素矩形相对应的颜色数据从指定的源设备上下文到目标设备上下文进行位块传输”。
H ?! 对我来说这是希腊 但是我猜想该函数将像素从源复制到目标,并且源设备上下文是我关心的参数。

hSrcDC参数的值是使用存储在EDX寄存器中的偏移量从位于1005A20的数组中获取的。 绘制标志时,此偏移量等于0x0E (请参见上面的屏幕快照,并注意行1002676和EDX寄存器)。

我的下一个猜测是,此BitBlt函数不仅用于绘制标志,而且还用于绘制地雷或空白方块。 我使用IDA的外部参照(交叉引用)功能来检测BitBlt在其他地方的使用位置:

我转到了代码中调用BitBlt的第二个位置。 令我惊喜的是,该呼叫出现在一个循环中的一个块中。 这强化了我的假设,即使用此功能绘制初始板。

这次,通过使用存储在EAX中的偏移量访问相同的hdcSrc数组来设置hSrcDC的值。 检查EAX的价值,我可以这样说:

  EAX = *(EBX + ESI)和1F 

0x1F是文字,但是EBX和ESI是什么? 回顾之前的两个模块,我看到EBX是内存中的固定位置(1005360),而ESI是循环变量。 我检查了内存中的地址1005360,发现看起来很像雷场:

我注意到两件事:

  • 每对0x10都相距9个字节。 因此,0x10必须是板上的行的分隔符。
  • 正好有十个0x8F,这表明0x8F是代表地雷的值。 剩下的0x0F代表一个空的正方形。

通过在10026E9行上与0x1F进行“与”运算(请参阅IDA屏幕截图),0x0F和0x8F最终都为0x0F,但是我希望它们为0x0E,还记得吗? 😉

此AND指令过于严格,这是我要实现的目标的关键。 我需要使此AND指令更加灵活,并考虑到当前处理的平方的值。 我要实现的逻辑是:

 如果board_location [square_position] == 0x8F: 
draw_flag
其他:
draw_empty_square

现有的AND指令占用3个字节的操作码。 我的逻辑是超出3个字节。 我需要一个密码保护程序来进行救援。

我在可执行文件的代码部分中搜索了一个空位,该空位有足够的空字节,可以用自己的代码替换。 使用十六进制编辑器和一个不错的在线汇编器,我添加了与以下x86指令序列相对应的操作码:

1004A60 CMP AL, 8F 
1004A62 JNZ SHORT patched_minesweeper.01004A66
1004A64 MOV AL, 0E
1004A66 PUSH DWORD PTR DS:[EAX*4+1005A20]
1004A6D JMP patched_minesweeper.010026F3

请注意此汇编代码如何从上面实现伪代码:

  1. 将AL与0x8F(最小值)进行比较。
  2. 如果不是地雷,则继续原始代码,即绘制一个空的正方形。
  3. 如果地雷,则将0x8F替换为0x0E(绘制标志所需的值)。

为了运行新代码,我需要使用原始代码中的JMP指令。 但是,即使JMP操作码也占用了3个以上的字节,这意味着我不仅必须覆盖AND指令,还必须覆盖其后的指令。 我用NOP填充了剩余的字节,并将覆盖的指令添加到了我的补丁中(请参见上面的1004A66行)。

因此,原始代码被修改为如下所示:

 010026E9 JMP patched_minesweeper.01004A60 
010026EE NOP
010026EF NOP
010026F0 NOP
010026F1 NOP
010026F2 NOP

扫雷艇现已打补丁,用旗帜标记地雷。 结束。


现在有趣的部分。

当我与Aviad交流以分享我的解决方案时,他告诉我我走了漫长而曲折的道路。 显然,有一个黑客比我做的要优雅得多,效率更高。 如果您想尝试一下,请这样做。

这里有2条提示:

  1. 这是单线的。
    即,您只能更改一条线以使游戏以所有已开采方块上的标志开始。
  2. 无需更改雷场的打印方式,而是更改雷场本身 。 如果您是Minesweeper程序员,将如何创建它?

当我打开Minesweeper的补丁版本时,我想执行健全性检查。 我单击了一个标志,并期望游戏结束(因为标志标记了地雷)。 那没有发生,只有在单击第二个标志之后游戏才结束。 为什么?

非常欢迎您与我联系,以获取问题,注释,建议等。

祝你好运,并感谢您阅读🙂