汉化GMSV第三篇(关于等级修改的问题)
-
这篇文章是作为前两篇文章的边角以及为即将发布的家族修复做个前站,虽说如此,我想这对一些新人来说还是十分必要的。这篇文章一共分两部分,第一部分就是上解决一篇文章所遗留下来的函数替换以及修改问题。第二部分是关于家族修复的部分信息,因为不只是修复家族,还要修复很多东西,所以把一些基础的知识抛开来与下一篇帖子分开。
最近有人msn我问关于等级修改的问题,其实我觉得这个是个非常明朗的问题,我说它明朗是因为它有一个好处,那就是没有于其他的函数交叉混杂,属于最低层的执行函数,所以这次我也就以如何修改人物的等级经验公式来说明如何修改和替换函数。
载入IDA,找到函数CHAR_GetLevelExp,获取原函数如下:
.text:08074098 ; =============== S U B R O U T I N E =======================================
.text:08074098 ; Attributes: bp-based frame
.text:08074098 public CHAR_GetLevelExp
.text:08074098 CHAR_GetLevelExp proc near ; CODE XREF: CHAR_LevelUpCheck+42 p
.text:08074098 ; CHAR_make_CP_String+517 p ...
.text:08074098 arg_0= dword ptr 8
.text:08074098 push ebp
.text:08074099 mov ebp, esp
.text:0807409B mov edx, [ebp+arg_0]
.text:0807409E cmp edx, 78h
.text:080740A1 jbe short loc_80740C0
.text:080740A3 cmp edx, 82h
.text:080740A9 jg short loc_80740B8
.text:080740AB mov eax, edx
.text:080740AD imul eax, edx
.text:080740B0 imul eax, edx
.text:080740B3 imul eax, edx
.text:080740B6 locret_80740B6: ; CODE XREF: CHAR_GetLevelExp+25 j
.text:080740B6 ; CHAR_GetLevelExp+2F j
.text:080740B6 leave
.text:080740B7 retn
.text:080740B8 ; ---------------------------------------------------------------------------
.text:080740B8 loc_80740B8: ; CODE XREF: CHAR_GetLevelExp+11 j
.text:080740B8 mov eax, 0FFFFFFFFh
.text:080740BD jmp short locret_80740B6
.text:080740BD ; ---------------------------------------------------------------------------
.text:080740BF align 10h
.text:080740C0 loc_80740C0: ; CODE XREF: CHAR_GetLevelExp+9 j
.text:080740C0 mov eax, dword ptr LevelUpTbl[edx*4]
.text:080740C7 jmp short locret_80740B6
.text:080740C7 CHAR_GetLevelExp endp
这个函数的原理我们已经在相关的帖子里了解到了,先是做一个比较,如果这个比较大于78h(等级),则按照一个固定的公式进行计算,如果小于等于78h,则在某表格中查询相应经验数值。
具体的分析和注解我就不加了,以前的帖子里面有,我们现在假设要把经验变为(下一等级)的三次方再乘以10,那么我们可以用自己的办法来解决,首先我们要获得下一等级,然后进行最高等级对比,如果到了最高等级呢,就把它变成一大堆负号(表示已经到了上限了),如果没有到达上限呢就执行我们的公式,无论哪种情况最终把结果返回,整个程序设计完成。比如代码可以这样字写:
.text:08074098 ; =============== S U B R O U T I N E =======================================
.text:08074098 ; Attributes: bp-based frame
.text:08074098 public CHAR_GetLevelExp
.text:08074098 CHAR_GetLevelExp proc near ; CODE XREF: CHAR_LevelUpCheck+42 p
.text:08074098 ; CHAR_make_CP_String+517 p ...
.text:08074098 arg_0 = dword ptr 8
.text:08074098 push ebp
.text:08074099 mov ebp, esp
.text:0807409B mov edx, [ebp+arg_0]
.text:0807409E cmp edx, 7Eh
.text:080740A1 jg short loc_80740B0 7F 0D //转到最高等级显示
.text:080740A3 mov eax, edx 89 D0 //取出下一等级
.text:080740A5 imul eax, edx 0F AF C2 //二次方
.text:080740A8 imul eax, edx 0F AF C2 //三次访
.text:080740AB imul eax, 0Ah 6B C0 0A //乘以倍数
.text:080740AE locret_80740AE:
.text:080740AE leave C9 //结束
.text:080740AF retn C3 //返回
.text:080740B0 ; ---------------------------------------------------------------------------
.text:080740B0 loc_80740B0:
.text:080740B0 mov eax, 0FFFFFFFFh B8 FF FF FF FF //显示-号
.text:080740B5 jmp short locret_80740AE EB F7 //转到结束离开
.text:080740B5 CHAR_GetLevelExp endp
把后面的代码替换到相应位置,剩余位置清零就可以了。写到这里我想大家都会问后面的十六进制代码都是怎么得来的呢,怎么就修改了原函数呢?呵呵,其实十六进制代码很简单一个是通过原来的代码复制修改,0F AF C2,6B C0 0A 都是这样得来的;另一种方式是计算得出来的,EB F7 ,7F 0D 这些就是计算得出来的,怎么计算呢。。。留给你动动脑筋,要不这都成了傻瓜教程了,动脑动手,就能改出更好的gmsv来。
第一部分完结。下面是第二部分
在这一部分里我们主要讲的是数据库的相关知识,高手可以忽略此部分了,哈哈
说到家族修复是个很老的故事了,大家都知道很多私服里已经可以使用这个系统,但是由于某些代码的失误,是我们的一些人无法享用到这个本该属于魔力的一个组成部分------家族。对gmsv的研究我可是新手,算起来不过一个星期,可是我却从中得到一些启示,其实没有别人说的那么难,关键就是你的耐性还心细,这些才是让你感到难的。
我对数据库的了解也不是很多,也就是7到8个小时吧,呵呵。。。刚刚看了三个多小时mysql帮助,顺便把地址也抄来送给大家,感觉里面博大精深,我却是真的一知半解直迷糊哦,废话少说,地址送上来:http://dev.mysql.com/doc/refman/5.1/zh/index.html
这个是5.1的,但是5.1新的太高级的功能咱们也用不到,貌似也不怎么好用,呵呵。从别人话里行间似乎大家都似懂非懂的觉得:哦,得改动数据库了。我也是这么认为的所以才不得不去欣赏下mysql的废话连篇的帮助文件,其实好东西很多,但是我们用得到的确真的很少,以下就举几个例子:
1、原字段修改
mysql>ALTER TABLE table_name CHANGE old_field_name new_field_name field_type;
2、PRIMARY KEY(主键索引)
mysql>ALTER TABLE `table_name` ADD PRIMARY KEY ( `column` )
3、UNIQUE(唯一索引)
mysql>ALTER TABLE `table_name` ADD UNIQUE (`column` )
4、INDEX(普通索引)
mysql>ALTER TABLE `table_name` ADD INDEX index_name ( `column` )
5、FULLTEXT(全文索引)
mysql>ALTER TABLE `table_name` ADD FULLTEXT ( `column` )
6、多列索引
mysql>ALTER TABLE `table_name` ADD INDEX index_name ( `column1`, `column2`, `column3` )
以上这几个加红的语句估计会用得多一些,后面三个是新的功能,我们可以忽略不计了,呵呵(蠢蛋,忽略不计还写上来)。下面我们从一个实际的例子来说明这些语句的运用和调试。
对于家族的测试不知道大家有没有五开试验过,说真的,不成功不可怕,不去尝试才是最可怕的,我去了试验了几次,然后还上瘾了,然后才有现在gmsv系列的文章。我之所以这么说是因为我试验后进入mysql,输入select * from tbl_guild;返回值是空的。打开guild.log发现了这么句话:
GUILD: guildID=30002 CdKey=sanat registnumber=3 name=Sun action=breedingRoomQueryError (8/6/9 11:58:8)
这句话很轻松的就理解了,错误原因breedingRoomQueryError!IDA重新载入Gmsv,在IDA-View界面搜索文本breedingRoomQueryError,我们很快的就找到它的根源getbreedingRoom函数,我们目前还不知道是什么原因产生的breedingRoomQueryError,但是有一点可以肯定,这个程序产生了这个错误,我们就要仔细的分析它。我们在西研究了下测试方案,决定试验下数据库进行到了哪一步对报错的上面的两句话进行了修改那原来得"DELETE FROM ...."修改成"SELECT * FROM...",这样改的目的是为了保存数据不被错误的产生而删除。修改后效果如下:
修改完成后重新进入服务器,五开建家族,当然是失败的,结束服务器查看GUILD.LOG记录,和以前一样。进入mysql,输入select * from tbl_guild;,我们看到了什么,对,我们看到了记录的输入,我们曾经创建了家族,但是因为某一个错误导致删除了,什么错误呢?我此我们开始研究这段代码的上层如图:
我们发现了两个大筐筐,由于篇幅的原因,我们今天就粗略的来分析下第一个框框,从里面的sql语句来看我猜测它是用来向tbl_guildMonster添加记录的,于是我就在mysql里面输入:select * from tbl_guildMonster;结果为空,貌似找到了病症了,让我们来粗略的分析下它的五脏六腑,我们假设初始esp=A:
A-04H push 0 //参数5入栈
A-08H push dword ptr [edi+24h] //参数4入栈
A-0CH push [ebp+var_420] //参数3入栈
A-10H push ebx //参数2入栈
A-14H push dword ptr [edi] //参数1入栈
A-18H push offset aInsertIntoTb_5 ; "INSERT INTO tbl_guildMonster SET guildI"...
//执行语句入栈
A-1CH push 400h ; maxlen //猜测:是sql参数
lea edx, [ebp+s]
A-20H push edx ; s
call _snprintf
A-08H add esp, 18h
lea eax, [ebp+s]
A-0CH push eax
A-10H push [ebp+arg_0]
inc ebx //roomNumber增加
call _mysql_query //执行
A-00H add esp, 10h //堆栈指针返回初始位置
cmp ebx, 2
jle short loc_8168726 //如果记录不足继续添加
由此我们可以看到,tbl_guildMonster这个表格是用来存储家族宠物的,而且一个家族可以拥有3个宠物,也就三个宠物空间roomnumber={0,1,2}可以作证。从这段程序中我们分析出,在不断地记录添加中,没有什么疑点,但是tbl_guildMonster没有记录而且breedingRoomQueryError错误产生,我们可以尝试把INSERT INTO语句换成其他的带有操作的语句和表格,很快拟就获得你想要的答案了。当然这只是个例子,如果说抛砖引玉的话,这也只是块泥坯,其它的还有待你慢慢的捉摸呢
-
分析的清楚明白,看懂了
-
好乱啊,呵呵,很好的教材