为避免不必要的麻烦,附上本人调试的版本:度盘链接 c60K9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8Y4m8S2L8W2)9J5k6h3u0S2K9h3c8#2i4K6u0W2j5$3!0E0i4K6u0r3M7#2)9J5c8U0q4E0K9q4u0W2x3f1g2G2,密码 l4pm
=============================================================================================================
富甲天下3是一个三国题材的大富翁类游戏,里面有一个赌场小游戏,游戏中随机生成5个象棋棋子,不同排列组合的5个棋子对应不同倍数的奖励。
本次调试的目标是拿到5个棋子完全相同的10倍奖励“五子登科", 先上最终成果图。

切入点: 游戏中点击'保留'以将棋子翻转至反面,同时'保留'键会被重新绘制为'换棋'。我们以通过观察按键转化时的内存数据变化,找到调试入口。
由于数据的初始值与增减性无从知晓, 因此使用CE时只能采取模糊搜索。先从'unknown initial value'开始搜索, 按键后再勾选'changed value'继续搜索,中间可以穿插一些无关的操作后勾选'unchanged value'以期大幅度减少搜索范围。


反复几次后,我们观察到一个可疑的地址:0x00492444。当按键为'保留'时,其值为0,当按键为'换棋'时,其值为1.

当全部五个按键为'换棋'状态时,0x00492444到0x00492454的内存区间全部为小字节序1。当全部五个按键为'保留'状态时,相同的内存区间全部为0。由此可以推断,这段内存被用来表示棋子翻开的状态。接下来判断这个变量在进程空间中的位置,先看看会谁往这个地址里写东西。

根据之前观察到的现象,图中edx的作用很有可能是棋子的偏移量, 当edx = 0,1,2,3,4时,对应第1,2,3,4,5个棋子。因此,0x00492454位于进程数据段或BSS段,其地址不会随每次程序的装入而变更。CE已经提供了足够的信息,下一步使用OD开始调试。


如图所示,棋子在'换棋'状态时棋子上的字符是不显示的,跳转至'保留'状态后才会重新显示。我们可以利用这一特点,搞清楚棋子翻转显示字符时的依据,进而搞清楚棋子的属性。举个例子,棋子'士'在'换棋'状态时是白板,在'保留'状态时才被赋值'士',当我们这个赋值动作依据什么知道这个白板应该被赋值'士'的,我们再修改这个依据,就可以把这个棋子更改为任意棋子。先将棋子翻转至'换棋',然后在0x00492444下断。翻转棋子,在写0的时候果然被断下来了。


棋子被翻出了角度,然而并没有出现字符,同时我们观察到了一个大循环,目测循环结束之后整个棋子就会逐渐显现字符并最终被翻转至'保留'。

蛋疼的一幕出现了,翻转过程中无论棋子上本来是什么都会显示'兵',这个循环略过。最终,棋子完全翻转成’保留’并且被赋值了正确的字符。完成这个动作的函数位于0x00411790,我们进去看看。

进去后里面还有一个调用0x00404D10,真正的换图发生在这个调用之后,再跟进去。

看到一个系统调用bitblt。这个函数接受一个源HDC句柄,一个目标HDC句柄,并且把源HDC所属的图像资源粘贴至目标HDC句柄。很明显,这个源HDC就是写有'士'棋子的容器,这个目标HDC就是我们的白板棋子,我们看看它的来源。

[培训]科锐逆向工程师培训第53期2025年7月8日开班!