第 1 部分,共 3 部分
如果你对逆向工程很认真——忘掉培训师和作弊引擎吧。
好的逆向工程师首先应该了解操作系统、核心API函数、程序的一般结构(什么是运行循环、windows结构、事件处理例程)、文件格式(PE)。Petzold 的经典著作《Programming Windows》可以提供帮助(www.amazon.com/exec/obidos/ISBN=157231995X)以及在线 MSDN。
首先,您应该考虑在哪里可以调用雷区初始化例程。我想到了以下几点:
- 当你启动游戏时
- 当你点击开心的脸
- 当你点击 Game->New 或按 F2
- 当你改变关卡难度时
我决定查看 F2 加速器命令。
要查找加速器处理代码,您需要查找窗口消息处理过程 (WndProc)。它可以通过 CreateWindowEx 和 RegisterClass 调用来追踪。
读书:
打开 IDA,Imports 窗口,找到“CreateWindow*”,跳转到它并使用“Jump xref to operand (X)”命令查看它的调用位置。应该只有一个电话。
现在在上面查找RegisterClass 函数和它的参数WndClass.lpfnWndProc。在我的例子中,我已经命名了函数 mainWndProc。
.text:0100225D mov [ebp+WndClass.lpfnWndProc], offset mainWndProc
.text:01002264 mov [ebp+WndClass.cbClsExtra], edi
.text:01002267 mov [ebp+WndClass.cbWndExtra], edi
.text:0100226A mov [ebp+WndClass.hInstance], ecx
.text:0100226D mov [ebp+WndClass.hIcon], eax
.text:01002292 call ds:RegisterClassW
在函数名称上按 Enter(使用“N”将其重命名为更好的名称)
现在看看
.text:01001BCF mov edx, [ebp+Msg]
这是消息 id,在按下 F2 按钮的情况下应该包含 WM_COMMAND 值。您将找到它与 111h 相比的位置。这可以通过在 IDA 中跟踪 edx 或通过在 WinDbg 中设置条件断点并在游戏中按 F2 来完成。
无论哪种方式都会导致类似
.text:01001D5B sub eax, 111h
.text:01001D60 jz short loc_1001DBC
右键单击 111h 并使用“符号常量”->“使用标准符号常量”,输入 WM_ 并回车。你现在应该有
.text:01001D5B sub eax, WM_COMMAND
.text:01001D60 jz short loc_1001DBC
这是找出消息 id 值的简单方法。
要了解加速器处理,请查看:
对于一个单一的答案,这是相当多的文字。如果你有兴趣,我可以再写几篇文章。长话短说雷区存储为字节数组 [24x36],0x0F 表示未使用字节(播放较小的字段),0x10 - 空字段,0x80 - 我的。
第 2 部分,共 3 部分
好的,让我们继续使用 F2 按钮。
根据按下 F2 按钮时使用键盘加速器wndProc 函数
... 接收 WM_COMMAND 或 WM_SYSCOMMAND 消息。wParam 参数的低位字包含加速器的标识符。
好了,我们已经找到WM_COMMAND在哪里处理了,但是如何确定对应的wParam参数值呢?这就是资源黑客发挥作用的地方。用二进制文件喂它,它会向你展示一切。就像我的加速器表一样。
替代文字 http://files.getdropbox.com/u/1478671/2009-07-29_161532.jpg
您可以在这里看到,F2 按钮对应于 wParam 中的 510。
现在让我们回到处理 WM_COMMAND 的代码。它将 wParam 与不同的常数进行比较。
.text:01001DBC HandleWM_COMMAND: ; CODE XREF: mainWndProc+197j
.text:01001DBC movzx eax, word ptr [ebp+wParam]
.text:01001DC0 mov ecx, 210h
.text:01001DC5 cmp eax, ecx
.text:01001DC7 jg loc_1001EDC
.text:01001DC7
.text:01001DCD jz loc_1001ED2
.text:01001DCD
.text:01001DD3 cmp eax, 1FEh
.text:01001DD8 jz loc_1001EC8
使用上下文菜单或“H”键盘快捷键显示十进制值,您可以看到我们的跳转
.text:01001DBC HandleWM_COMMAND: ; CODE XREF: mainWndProc+197j
.text:01001DBC movzx eax, word ptr [ebp+wParam]
.text:01001DC0 mov ecx, 528
.text:01001DC5 cmp eax, ecx
.text:01001DC7 jg loc_1001EDC
.text:01001DC7
.text:01001DCD jz loc_1001ED2
.text:01001DCD
.text:01001DD3 cmp eax, 510
.text:01001DD8 jz loc_1001EC8 ; here is our jump
它导致代码块调用一些 proc 并退出 wndProc。
.text:01001EC8 loc_1001EC8: ; CODE XREF: mainWndProc+20Fj
.text:01001EC8 call sub_100367A ; startNewGame ?
.text:01001EC8
.text:01001ECD jmp callDefAndExit ; default
那是启动新游戏的功能吗?在最后一部分中找出答案!敬请关注。
第 3 部分,共 3 部分
让我们看一下该函数的第一部分
.text:0100367A sub_100367A proc near ; CODE XREF: sub_100140C+CAp
.text:0100367A ; sub_1001B49+33j ...
.text:0100367A mov eax, dword_10056AC
.text:0100367F mov ecx, uValue
.text:01003685 push ebx
.text:01003686 push esi
.text:01003687 push edi
.text:01003688 xor edi, edi
.text:0100368A cmp eax, dword_1005334
.text:01003690 mov dword_1005164, edi
.text:01003696 jnz short loc_10036A4
.text:01003696
.text:01003698 cmp ecx, dword_1005338
.text:0100369E jnz short loc_10036A4
有两个值(dword_10056AC,uValue)读入寄存器 eax 和 ecx,并与另外两个值(dword_1005164,dword_1005338)进行比较。
使用 WinDBG ('bp 01003696'; on break 'p eax; p ecx') 查看实际值——它们对我来说似乎是雷区尺寸。使用自定义雷区大小显示第一对是新维度和第二个当前维度。让我们设置新名称。
.text:0100367A startNewGame proc near ; CODE XREF: handleButtonPress+CAp
.text:0100367A ; sub_1001B49+33j ...
.text:0100367A mov eax, newMineFieldWidth
.text:0100367F mov ecx, newMineFieldHeight
.text:01003685 push ebx
.text:01003686 push esi
.text:01003687 push edi
.text:01003688 xor edi, edi
.text:0100368A cmp eax, currentMineFieldWidth
.text:01003690 mov dword_1005164, edi
.text:01003696 jnz short loc_10036A4
.text:01003696
.text:01003698 cmp ecx, currentMineFieldHeight
.text:0100369E jnz short loc_10036A4
稍后新值覆盖当前值并调用子程序
.text:010036A7 mov currentMineFieldWidth, eax
.text:010036AC mov currentMineFieldHeight, ecx
.text:010036B2 call sub_1002ED5
当我看到它
.text:01002ED5 sub_1002ED5 proc near ; CODE XREF: sub_1002B14:loc_1002B1Ep
.text:01002ED5 ; sub_100367A+38p
.text:01002ED5 mov eax, 360h
.text:01002ED5
.text:01002EDA
.text:01002EDA loc_1002EDA: ; CODE XREF: sub_1002ED5+Dj
.text:01002EDA dec eax
.text:01002EDB mov byte ptr dword_1005340[eax], 0Fh
.text:01002EE2 jnz short loc_1002EDA
我完全确定我找到了雷区阵列。使用 0xF 初始化 360h 字节长度数组 (dword_1005340 ) 的周期原因。
为什么 360h = 864?下面有一些提示,该行占用 32 个字节,并且 864 可以除以 32,因此数组可以容纳 27*32 个单元格(尽管 UI 允许最大 24*30 字段,但数组周围有一个字节填充用于边框)。
以下代码生成雷区顶部和底部边界(0x10 字节)。我希望你能在混乱中看到循环迭代;)我不得不用纸和笔
.text:01002EE4 mov ecx, currentMineFieldWidth
.text:01002EEA mov edx, currentMineFieldHeight
.text:01002EF0 lea eax, [ecx+2]
.text:01002EF3 test eax, eax
.text:01002EF5 push esi
.text:01002EF6 jz short loc_1002F11 ;
.text:01002EF6
.text:01002EF8 mov esi, edx
.text:01002EFA shl esi, 5
.text:01002EFD lea esi, dword_1005360[esi]
.text:01002EFD
.text:01002F03 draws top and bottom borders
.text:01002F03
.text:01002F03 loc_1002F03: ; CODE XREF: sub_1002ED5+3Aj
.text:01002F03 dec eax
.text:01002F04 mov byte ptr MineField?[eax], 10h ; top border
.text:01002F0B mov byte ptr [esi+eax], 10h ; bottom border
.text:01002F0F jnz short loc_1002F03
.text:01002F0F
.text:01002F11
.text:01002F11 loc_1002F11: ; CODE XREF: sub_1002ED5+21j
.text:01002F11 lea esi, [edx+2]
.text:01002F14 test esi, esi
.text:01002F16 jz short loc_1002F39
其余子程序绘制左右边界
.text:01002F18 mov eax, esi
.text:01002F1A shl eax, 5
.text:01002F1D lea edx, MineField?[eax]
.text:01002F23 lea eax, (MineField?+1)[eax+ecx]
.text:01002F23
.text:01002F2A
.text:01002F2A loc_1002F2A: ; CODE XREF: sub_1002ED5+62j
.text:01002F2A sub edx, 20h
.text:01002F2D sub eax, 20h
.text:01002F30 dec esi
.text:01002F31 mov byte ptr [edx], 10h
.text:01002F34 mov byte ptr [eax], 10h
.text:01002F37 jnz short loc_1002F2A
.text:01002F37
.text:01002F39
.text:01002F39 loc_1002F39: ; CODE XREF: sub_1002ED5+41j
.text:01002F39 pop esi
.text:01002F3A retn
巧妙地使用 WinDBG 命令可以为您提供酷炫的雷区转储(自定义大小 9x9)。检查边界!
0:000> db /c 20 01005340 L360
01005340 10 10 10 10 10 10 10 10-10 10 10 0f 0f 0f 0f 0f 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 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f ................................
01005380 10 0f 0f 0f 0f 0f 0f 0f-0f 0f 10 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f ................................
010053a0 10 0f 0f 0f 0f 0f 0f 0f-0f 0f 10 0f 0f 0f 0f 0f 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 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f ................................
010053e0 10 0f 0f 0f 0f 0f 0f 0f-0f 0f 10 0f 0f 0f 0f 0f 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 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f ................................
01005420 10 0f 0f 0f 0f 0f 0f 0f-0f 0f 10 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f ................................
01005440 10 0f 0f 0f 0f 0f 0f 0f-0f 0f 10 0f 0f 0f 0f 0f 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-0f 0f 10 0f 0f 0f 0f 0f 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 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f ................................
010054a0 0f 0f 0f 0f 0f 0f 0f 0f-0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f ................................
010054c0 0f 0f 0f 0f 0f 0f 0f 0f-0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f ................................
010054e0 0f 0f 0f 0f 0f 0f 0f 0f-0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f ................................
嗯,看来我需要另一个帖子才能结束这个话题