用远程注入实现9屏winxp扫雷流畅 bad apple
终极目标和思考过程
终极目标:在不使用dll注入的情况下,使用python实现 9 连屏(3x3)的 流畅 badapple 播放。
思考:由于 windows 消息队列的处理能力有限,即便使用了前后两帧异或信息来减少点击量,使用 PostMessage 的时候也依然会卡,这就需要手动写帧补偿。而且当画面急剧变化时,4屏或9屏卡顿时间长达一两秒,这是不可接受的。所以就想绕过消息队列,直接给扫雷进程注入shellcode,调用它的点击右键时的响应函数。
以前有人做过winxp扫雷外挂并开源在github,使用这个代码作为辅助,进行了对扫雷的逆向工程。
注:本程序使用 python 实现,目标是不注入dll(因为注入dll甚至可以把帧写死在代码里,那就没意思了),也不使用多进程/多线程(因为 python 调用 windows API 的速度完全够用,这种项目里面代码不仅要高效,更要有艺术性)
试图通过直接更改内存来切换显示模式
github代码中有内存地址,所以就使用了 windows api 的 WriteProcessMemory 函数,结果搞崩了内存,后来在 ollydbg 中逆向才发现他是先改旗子后写内存的,也就是说这么做没用,不仅如此,(踩坑记录:)还阴差阳错地覆盖了内存中注册表存取函数控制的区域,导致注册表值异常,最后让扫雷在启动读取注册表的时候崩掉了,进不去UI了,删掉注册表才恢复正常。
动态注入 shellcode 手动触发右键点击旗子时扫雷自己调用的函数
通过OD逆向,发现,扫雷程序在接收到右键点击的时候,会触发地址为 0x0100374F 的函数。函数内容如下:
1 | |
找到这个函数的方式,是给内存下断点。前文找到了雷区的内存地址,在 OD 里面很容易能看出来雷区的样子:
| 地址 | data1 | data2 | data3 | data4 | ASCII (OEM - 多语言拉丁语1> |
|---|---|---|---|---|---|
| 01005340 | 10 10 10 10 | 10 10 10 10 | 10 10 10 0F | 0F 0F 0F 0F | |
| 01005350 | 0F 0F 0F 0F | 0F 0F 0F 0F | 0F 0F 0F 0F | 0F 0F 0F 0F | |
| 01005360 | 10 0F 0F 0F | 0F 0F 0F 0F | 0F 0F 10 0F | 0F 0F 0F 0F | |
| 01005370 | 0F 0F 0F 0F | 0F 0F 0F 0F | 0F 0F 0F 0F | 0F 0F 0F 0F | |
| 01005380 | 10 0E 0F 0F | 0F 0F 0F 8F | 0F 0F 10 0F | 0F 0F 0F 0F | Å |
| 01005390 | 0F 0F 0F 0F | 0F 0F 0F 0F | 0F 0F 0F 0F | 0F 0F 0F 0F | |
| 010053A0 | 10 8F 0F 0F | 0F 0F 8F 0F | 0F 0F 10 0F | 0F 0F 0F 0F | ÅÅ |
| 010053B0 | 0F 0F 0F 0F | 0F 0F 0F 0F | 0F 0F 0F 0F | 0F 0F 0F 0F | |
| 010053C0 | 10 0F 0F 0F | 0F 0F 0F 0F | 0F 0F 10 0F | 0F 0F 0F 0F | |
| 010053D0 | 0F 0F 0F 0F | 0F 0F 0F 0F | 0F 0F 0F 0F | 0F 0F 0F 0F | |
| 010053E0 | 10 0F 0F 0F | 0F 8F 0F 8F | 0F 0F 10 0F | 0F 0F 0F 0F | ÅÅ |
| 010053F0 | 0F 0F 0F 0F | 0F 0F 0F 0F | 0F 0F 0F 0F | 0F 0F 0F 0F | |
| 01005400 | 10 0F 0F 0F | 0F 0F 0F 0F | 0F 0F 10 0F | 0F 0F 0F 0F | |
| 01005410 | 0F 0F 0F 0F | 0F 0F 0F 0F | 0F 0F 0F 0F | 0F 0F 0F 0F | |
| 01005420 | 10 0F 0F 0F | 0F 8F 0F 0F | 8F 0F 10 0F | 0F 0F 0F 0F | ÅÅ |
| 01005430 | 0F 0F 0F 0F | 0F 0F 0F 0F | 0F 0F 0F 0F | 0F 0F 0F 0F | |
| 01005440 | 10 0F 8F 8F | 0F 0F 0F 0F | 0F 0F 10 0F | 0F 0F 0F 0F | ÅÅ |
| 01005450 | 0F 0F 0F 0F | 0F 0F 0F 0F | 0F 0F 0F 0F | 0F 0F 0F 0F | |
| 01005460 | 10 0F 0F 0F | 0F 0F 0F 0F | 8F 0F 10 0F | 0F 0F 0F 0F | Å |
| 01005470 | 0F 0F 0F 0F | 0F 0F 0F 0F | 0F 0F 0F 0F | 0F 0F 0F 0F | |
| 01005480 | 10 10 10 10 | 10 10 10 10 | 10 10 10 0F | 0F 0F 0F 0F |
其中8F是雷,以10开头的行,后面会记录这一行的方格信息,以10结尾。0F是空填充,以0F开头的行没用。
不断地右击插旗可以发现,旗子对应的记录值是0E。而且很容易就能找到某个格子对应的内存的位置。因此找一个格子,对它的内存地址下一个写入断点,会发现暂停在了上面的函数的第17行:
0100377F |. 8A06 MOV AL,BYTE PTR DS:[ESI]
这明显是个赋值指令。它所属的函数正如上文所言,地址是0x0100374F。直接在这个地址下断点,然后在外面触发右击事件,果然断在了这里。说明我们的猜想是正确的。BTW,左键单击则不会触发,它触发的是0x01003512处的函数。
查看CS寄存器,得到这段代码的段地址0x0023。所以在调用绝对地址跳转的时候,地址的构造是0x00230100374f,然后使用小端序写入机器码,即 413700012300。
那么接下来就开始向程序注入shellcode。
1 | |
至于手动构造机器码,则是另外一个故事了,此处不必多说。上面的过程无论是注入DLL还是远程调用,都是用得上的。那么接下来的过程只是用哪种艺术手法将其实现的问题。
如此这般就实现了远程注入。只要用python写一个合理的发送程序,实现定时播放、插帧补帧等细节,即可使用扫雷完美播放bad apple。
一个扫雷播放 bad apple 那只是一个扫雷播放 bad apple。
而9个扫雷同时播放 bad apple,它就不仅仅是9个扫雷同时播放 bad apple,
而是现代计算机理论、严肃活泼的黑客精神与小众亚文化的结合,是这个时代的缩影。