红色警戒2修改器原理百科(六)

    先说一件事情,昨天我在继续OD调试游戏的时候,运行错了版本,然后OD的备注、标签和断点通通没有了。喜大普奔微笑上天关上了一扇门,然后我开了一扇窗户——抛弃之前的枷锁,重新出发,目前来看可能是找到了判断地图触发器的整个过程,待进一步测试;还找到了立即胜利的源头。

    让我们书接上回。

(十三)枚举地图玩家——隐藏在背后的黑暗势力

    如果细心的你在进行上一步的疯狂的掠夺的时候,发现了没人占有的建筑物的pOwner不是NULL,你就可能发现一片新大陆。是谁拥有了这个建筑物,却还能显示没有归属。

    前一篇最后一个图,转移所属函数(call [ebx+378])前面有这么备注的一个CALL:玩家ID获取数据基址(其实这个描述不准确),不知是否注意到了?当时调试时,传入ECX=0,EAX返回玩家数据地址,然后粗略的看了看里面的循环,就做了个这样的标签。毕竟能返回玩家数据地址的函数,需要一个参数,那八成就是玩家ID,因为咱就是本地第一个玩家,就是0号位置咯。联网对战时,才会变成其他位置,单机只能在0号。。。

    既然有这么一个ID,就来找找ID在哪个偏移处。用CE的分析数据/结构功能,对比分析地图上所有玩家数据(通过电脑建筑的pOwner获得电脑玩家数据地址,我测试时就两个玩家,我用美国,对手是简单的苏联),同时尝试发现一些其他显而易见的数据,只发现偏移+30处比较明显,我是0,电脑是1。基本就确定(玩家数据地址——[game.exe+635DB4])+30偏移就是玩家ID。然后就是一堆不明显的数据,一大片0。

    然而,当我因某些原因又去看了下那个所谓的CALL<玩家ID获取数据基址>,却发现没有一条指令读取了+30偏移,Amazing!然后仔细跟踪发现一个莫名其妙的循环次数,地图上有2个玩家,却循环了4次。尝试3个玩家,结果循环5次。而且和参数ECX比较的是玩家数据+34偏移处的一个地址的+B8偏移处的数据,文字表述比较复杂。C语言描述就是:if (CurrentPlayer.onePointer->oneValue == ECX)。

jz

    这个oneValue,它永远和玩家ID相同的吗?冥思苦想一番,这个指针指向的是什么结构?最终从地图编辑器获得灵感,地图上的单位归属可以设置给不同国家,还有平(即无归属)和治安(一个神秘的组织),这就解释了为什么循环次数永远多两次:平和治安在你看不到的地方,作为特殊玩家一直存在。也解释了单位看起来没有归属,实际上还是有个归属者。玩家数据+34偏移是所属势力对象指针,所属势力对象结构的+B8偏移是势力编号。那么这个CALL的准确备注应该是:势力编号获取数据基址。

gs

    现在才算进入正题了,来分析下这个函数(004ED760),就能找到枚举全图玩家的必要条件了:地图玩家数(包括平和治安)(game.exe+6322A8),地图玩家数组(game.exe+63229C)。然后你就能知道他们有多少钱,有几个步兵,有几个建筑等等等等,这些偏移比较好找。

    号外,好像是在一个苏军任务上发现,玩家ID终于不是0了,因为剧情是强行登陆建立基地,我是侵略者眨眼还有最后一关,俘获总统任务地图上有13个玩家(还有两个隐藏的更深的势力叫GDI和Nod)——我眼瞎,玩得时候没看到。。

(十四)立即胜利——来打我啊~

    如果对游戏编程有些许了解的话,一个简单的小游戏的大概框架简单描述如下:

int main()
{
    init();   //程序初始化,防止重复启动等
    setGameOption();
    startGame();
    cleanup();
    return 0;
}

void startGame()
{
    initGame();  //初始化数据,加载资源
    while(true)
    {
        doSomething();
        draw();
        if (gameIsOver())
            break;
    }
}

    由此,只要找到游戏的主循环就很容易找到游戏判断结束的地方。游戏的所有操作都在doSomething()里,对于具体游戏而言,当然会有好多层调用,在游戏的一个操作断下后,一直返回上层,就可以来到主循环了。

    就以建造单位时金钱减少为例,应该是这个地方:

jq

然后查看调用堆栈:

dz

game.<ModuleEntryPoint>+0DB就是调用程序main()函数的地方,前面都是编译器附加的初始化代码;

继续往上看,game.00690883:

d1c

看到字符串“Main_Game”,估计00690883就对应上面所说的startGame()函数。

继续往上, game.00481DBA:

zxh

    这段比较长,因为实际情况不可能像上面那么一个简化的模型那么简单。图中“game loop finished”是OD自己分析出来的程序内部字符串,另外我添加的“//”开头的注释也让这段程序的流程变得很清晰。注意红警和上述游戏模型的异同!

进入0053F840:

    通常游戏中会设置一个逻辑变量来标识是否结束,当结束条件满足时,被设置成TRUE,所以可以注意mov [xxxxxxxx],1

    从上图看出,al=1游戏就结束了,可以从函数尾向上分析al如何变成1的;

    也可以把类似“[A35DB1]”这样的固定的内存地址都添加到CT表中,观察游戏结束时是怎样的状态,然后依次修改;

    ……等等其他方法。

最后给出,其实就是上面的提到的,立即胜利=1(字节,game.exe+635DB1)。

To be continued…

署名许可转载请注明来源,http://www.cnblogs.com/viewll/p/4773802.html

posted @ 2015-08-31 20:42  vIewll  阅读(2135)  评论(0编辑  收藏  举报