https://tieba.baidu.com/p/1716039664?pid=21729612845&cid=0#21729612845
这个贴跟大家一起探讨什么是场景,其包含哪些东西,是如何构建的,是如何作用的,怎样找到场景的构建函数组,如何修改它们
1、什么是场景,其是如何构建的?
什么是场景呢?说得通俗些,场景就是一个个的瓶子,里面可以装很多东西,瓶子外面就是真空,任何东西都无法存在。而这个瓶子的构建,先有一个生产工人(场景的初始化主控函数),这个工人根据图纸(*.map文件)来确定瓶子的形状(地图的大小、基本地貌),根据设计方案(bnt文件)来确定瓶子里采用哪些装饰(树木、河流、房屋、命令控件等)和把这些装饰放哪个位置(bnt文件中相应的坐标),根据与购瓶者签的合同(初始化函数)确定瓶子里放哪些人(场景中的npc),和这些人的门牌号(ont文件中配置的坐标、事件id等),同时也不能闭关锁国,也得在瓶子上开几个门(场景切换点),并根据设计方案(scn文件)确定每个门开在哪里(bnt文件中的坐标),这样就可以和其它邻居交流串门了,如此,一个微型王国:场景就诞生了。当然,为了区分瓶子的大小,里面东西的好坏,会对每个瓶子进行等级划分,等级越高的瓶子,里面的东西就越好,工资就越高
2、如何通过反汇编寻找场景的函数组群?
每一个场景的功能实现,不是由一个函数来完成,而是由一组函数来实现,但是,只要找到其中一个函数,就可以通过对该函数调用找到整个函数组。根据第一节内容,很容易联想到,既然初始化函数访问了*.scn.、bnt等文件,是不是可以通过scn文件的文件名来通过字符串搜索找到相应场景的初始化函数呢?没错,就是这样!下面以多人模式下的赵村为例来说明。
先反汇编easyrpg116.dll,搜索字符串“net_zhaocun”,也就是scene文件夹下赵村scn文件的文件名,我使用的工具是ida pro,如图:
找到后双击它,就来到了存放该字符串的data段的地址
右边的 DATA XREF: sub_10140990+20o就是引用该字符串的函数,也就是网络赵村的初始化函数,双击这个 DATA XREF: sub_10140990+20o就进入了赵村的初始化函数了。这里之所以肯定 10140990就是 初始化函数是因为只有该函数引用了字符串net_zhaocun,这一点你可以通过使用交叉参考菜单验证,如果是多个函数都引用了同一个变量名,那就需要一个一个的找了。
好了,看到上图中右上角的“DATA XREF: .rdata:101E8F60”了吗?这就是call赵村初始化函数的地方,也就是赵村场景函数组在rdata处的地址,双击它,看看函数组是啥样子的,
光标停留的地方(也就是第二行)就是初始化函数,每个场景都是如此,第七行的rdata:101E8F74 dd offset sub_10140D70就是轮回刷新函数
现在,我们重新返回初始化函数,看看这里面到底有些啥。
text:10140998 pushoffset aSfNetXRoomX ; "秦殇/NET/赵村/ROOM/赵村",这个好理解,场景的全称索引,也就是瓶子对外自报家门:劳资叫xxx,以后只要提到劳资(其它场景引用赵村),都要用xxx这个名字,text:101409A4 push offset asc_1022EEF4 ; "赵村"这个也好理解,瓶子的瓶牌嘛,叫人一看就知道是哪个瓶子(你从其它场景进入赵村时鼠标指向切换点显示的名称),push offset aNet_zhaocun ; "net_zhaocun"不解释了,咱就是通过他找来的, push offset aG_type ; "G_Type",这个东西就是瓶子的类型,也就是有的瓶子里面允许人打架(pk场景),有的里面不允许打架,你这个瓶子到底允许不?0B就是赵村的场景类型。
G_WEA_BigRain"和"G_WEA_SmallRain"没啥大用,就是瓶子里的天气特效,push offset aLevel ; "Level",哎呦,这可是个好东西,这决定了瓶子的好坏呢(也就是场景等级),上面的1就是赵村这个瓶子的等级。
下面是赵村这个瓶子的4扇门和3大金刚木、矿、宝箱登场了
这里由于ida汉化插件的 问题,没有显示出黄色部分的字符串内容,你双击它可以发现,他们是阳周城的全称索引:“秦殇/NET/阳周城/ROOM/阳周城”,还记得刚才赵村这个瓶子是怎样自报家门的吧?它上面的“10140A07 push 2”是什么呢,到底是什么呢?没错,就是场景id,也就是告诉阳周城的初始化函数,我这4扇门的号码可都是2,你可别搞错了啊!当然阳周城的初始化函数也很敬业,只要是从 这4个门里过来串门的人,先从阳周城的scn文件中查找“前场景名称=2”的记录,找到该记录配置的坐标,然后把过来的人一律放到这个坐标位置上,明白scn文件中的前场景名称的含义了吧?下面的“text:101409FB push offset aYzh ; "yzh"”,这个有点麻烦,他是每扇门的开门密码,就像四十大盗中一喊芝麻开门,宝藏大门就打开了一样,只要你一喊“yzh”(在bnt文件中通过命令控件携带,你用的gm命令也是如此),第一扇门就打开了,你就进入阳周城了,一喊yzh1,第二扇就开了。。。。,
“push offset unk_10299F10,push offset unk_10299F38和push offset unk_10299EE4”对应的分别是树、矿和宝箱的全称索引,也就是初始化函数在定义瓶子里面的这三种东西,上面的7、8、7分别是这三种东西的数量,至于这三种东西每个放在哪里,还记得第一节说的bnt文件的功能吗?没错,就是根据里面的数据进行配置,也就是说,如果你想增加某个场景中的矿堆的数量,单纯的改初始化函数中的push xx是不行的,还必须在bnt文件中添加随机矿控件才行,否则虽然初始化函数增加了矿堆,但因在bnt文件中找不到相应的配置数据,就会把它放到坐标0,0处,那样有也等于没有,还有,你发现了吗?定义这三种东西的时候没有使用等级参数,这就是为什么第一次总摸到垃圾的原因,因为第一次是初始化函数创建的,而不是轮回刷新函数创建的。
好了,现在该往瓶子里放买房住的人了,包括npc和怪物,当然在赵村这个瓶子里只有怪,没有npc。
其中push 65,是网络秦军士兵A刀盾的前场景id,这里的65转换成10进制就是101,初始化的时候就会到scn文件中找“前场景名称=101”的记录,并把其定义的坐标赋予这几个怪,push 1,是怪物等级参数,也就是会生成1级的刀盾兵,push 2,是数量参数,也就是生成2个刀盾兵,另外,scn文件中一个id的配置文件可以不唯一,比如,如果你在赵村的scn文件中配置3个“前场景名称=101”的记录,对应于A、B、C三个点,那生成的这2个怪,可能2个都出现在三个点中的任何一个点,也可能任意2个点各一个,当然对于场景切换点,也可以定义多个,比如你在阳周城定义三个“前场景名称=2”的记录,那当你从赵村进入阳周,就有可能出现在这三个点的任何一个的位置上,也就是说你进入阳周城的出生点是随机的了。好了,初始化函数就这些了,下面咱们再一起去看看赵村的轮回刷新函数,到赵村的函数组所在的rdata地址处,双击第7行的sub_10140D70
这样就来到了赵村的轮回刷新函数
为什么叫轮回刷新函数你?每个场景都有一个时钟计数器,轮回就是每个一个计数周期就扫描一次,所谓刷新就是每次扫描,只要条件满足就刷新。10140D70 mov eax, dword_106A0C84,把赵村计数器的数值存放在内存地址(106a0c84处)送入寄存器EAX(不明白没关系,你就把EAX理解成一个装东西的盒子好了,执行这条指令后,盒子里面装的是赵村的时钟计数器的数值),“10140D78 cdq”,EDX(理解成另一个盒子)清零,为什么要清零?因为后面要用它装重要的东西,“10140D79 mov ecx, 0Ah”把数值0A(十进制数10)送人寄存器ECX(呵呵,第三个盒子),“10140D7E idiv ecx”,EAX除以ECX商放入EAX,余数放入EDX,这时EAX这个盒子里是啥?对是赵村的时钟计数器的数值,这里假设转换成10进制数后是6987507,ECX呢?是10,6987507除以10的余数就是7,并把7放入EDX这个盒子里(明白前面为什么要把它清零了吧?),“ test edx, edx.和 jnz short loc_10140D95”,判断EDX中的数值是否等于0,如果不等于0,就不执行后面的语句,直接去执行10140D95处的语句,也就是该类宝箱被跳过了,如果等于0,就继续执行,去刷新宝箱,当然这里EDX中是7,不为0,就不会刷新,也就是对于该种宝箱,每隔10个计数期才会刷新一次,下面是另一种宝箱、树、矿和怪的刷新,原理都一样,怪的创建和初始化函数创建的原理一样,自此,轮回刷新函数也分析完了,场景函数组还有条件触发函数(比如你进入某个场景有限制条件,如特等等级以上、拥有特定物品等)等,有兴趣的可以自己摸索.