kkndmmx 发表于 2020-3-25 23:44

RGSS301(金庸5) 内存数据分析

我游戏已经翻版了,修改器应该不会在更新,抛砖引玉,希望大能们能写出更完善的修改器

用rgss3写的 引用dll为rgss301.dll的游戏程序,应该都遵循下面几个特点:
数据基本都是4字节存放的,除了浮点,二进制数据等

1.[["RGSS301.dll"+0025A2AC]+10]存放所有类型变量的映射表。   
读取方式:+4==名称的存放地址,+8==映射值, +0x10下一个地址,为0是最后一个,+0x14上一个地址,为0是第一个
在rmxp编辑器的脚本中定义的类型,变量,都在这里,字符串都不重复,有唯一的映射值
2.["RGSS301.dll"+002AC044]存放所有的对象数据(映射表中以$开头的类型),+4是列表的数量,+0xc是列表
遍历列表中的每个4字节地址,+0==+4==映射值,+8存放数据的基址,+c下一个数据,一直+c直到为0时结束此4字节的循环。
3.通过以上2步,就能得到所有你想要的数据基址。 比如$game_variables为游戏中的变量 $game_switches为游戏中的开关
[[[基址]]+4]到数据类型
01?1:对象数据 ?为0时,+8长度,+c是值,+0x10是类型结构,以映射值存放,具体读法和第一步差不多, 不为0时代表长度。+8开始是值(金庸5一般+8就是要读取的值的地址)
类型结构数据读取:
+8,双数时/2为长度,+0是映射值,+10是下一个数据
       单数时+4 +8值要一样,长度=(值-1)/2。+c是值的地址+0映射值,+4为0,+8映射值 +c为1 以此类推
       存储的值是数组时,不用读取类型结构,+c +10值为6
0401:double
05?1?0:字符串 问号的地方为长度,参考以下代码
uint firstbit = (len & 0x000f0000) >> 4*4;
uint secondbit= (len & 0x0000f000) >> 3*4;
if(secondbit%4!=0)
   {
         return -1;
      }
      return firstbit * 4 + secondbit / 4;

-1时+8长度,+c值的地址
非-1是+8是值的起点

07?1:数组
+8==+c=长度
+0x10为值存放地址,进去就是数组列表

0c?1:二进制
其他的类型对照存档文件分析下就知道了,不一一例举

值存放形式: 0==false, 2==true,4==nil 其他的单数值减一除2, 其他的双数值为地址



举例:
["RGSS301.dll"+0025A2AC]=02252ff0
=022530d0
=CB 57 85 7B 60 98 0F 02(名称) 6D 01 00 00(映射值) 00 00 00 00 10 31 25 02(下一个地址) 00 00 00 00(上一个地址,0是第一个地址)
60 98 0F 02(名称)= 05 08 D0 00 00 00 00 00 00 00 00 00 00 00 00 00 (空值)
10 31 25 02(下一个地址)=D1 28 C2 39 4C 98 0F 02 6F 01 00 00 00 00 00 00 50 31 25 02 D0 30 25 02
4C 98 0F 02(名称)=05 C8 D1 00 00 00 00 00 3C 49 46 55 4E 43 3E(<IFUNC>) 00
...
...
递归得到$game_player 映射值22315
通过第二步,递归得到映射值为22315的地址为91bb198    2B 57 00 00(映射值) 2B 57 00 00 00 6F FE 08(基址)
==090eb7c8
=14086b14
=01 01 00 00(类型)18 E7 19 14 31 00 00 00(长度) D8 42 F4 09(值存放的地址) 30 74 63 11(结构)
先读结构数据:
0@damage_pop
1@damage
2@critical
3@is_battle
4@duration_battle
5@id
6@x
7@y
8@real_x
9@real_y
10@tile_id
11@get_rect
12@character_name
....
25@move_speed
.....
.....
48@encounter_count


可以看到,2暴击,6 7是坐标信息,25是移动速度,12角色名字等等

再读值:00 00 00 00 01 00 00 00 01 00 00 00 00 00 00 00 01 00 00 00 01 00 00 00 47 00 00 00 43 00 00 00 01 23 00 00
每4字节就是对应的值,
@damage_pop=false   
@damage=0
@critical=0
...
...
@x=35
@y=33
...
...
@move_speed=80 68 08 14 是地址,读进去=04 01(数据类型) 00 00 84 6F 0F 02 CD CC CC CC CC CC 12 40(双精度类型值 4.70)

其他的数据都是这个读法,在我那个修改器下面有个测试用程序,可以得到我认为有用的一些基址
运行结果:
得到$game_map
得到$game_player
得到$game_variables
得到$game_actors
得到$game_switches
得到$game_skills
得到$game_party
得到映射7/7

$game_variables 22443
$game_switches23051
$game_actors    23003
$game_skills    23411
$game_map       22299
$game_player    22315
$game_party   23555
//类型与映射值

$game_switches:91c0ff8,offset:3,
$game_player:91bb198,offset:4,
$game_actors:91c09b8,offset:22,
$game_skills:91c50b8,offset:28,
$game_party:91c5e98,offset:38,
$game_map:91964a8,offset:55,
$game_variables:91bbbd8,offset:65,

//类型,基址,"RGSS301.dll"+002AC044的一系列4字节偏移,读进4字节的+c的偏移



4。穿墙
我穿墙很暴力,可能会影响其他数据。
原理是分析了下Tilesets.rxdata,其中"@passages"的二进制数据,0是可以通过,15是不能通过,下面代码就是下内存访问断点找到的。
RGSS301.dll+14874 - movsx ecx,word ptr
RGSS301.dll+14878 - push ecx当ecx==0F时不允许通过,ecx==0时允许通过,其他的我没管,直接注入当等于0F时改成0
RGSS301.dll+14879 - call RGSS301.dll+14AE0


5.增加物品的call
直接改内存你能改数量,但是不能增加物品,以下call可以
10027AC4 - 8B 50 04            - mov edx,
10027AC7 - 8B 00               - mov eax,
10027AC9 - 52                  - push edx //数量
10027ACA - 50                  - push eax//物品ID
10027ACB - 56                  - push esi//物品基址
10027ACC - FF D7               - call edi//edi==10096BA0

5.增加技能
直接修改内存可以改变技能,但不能增加技能,以下是call
10027AA4 - 56                  - push esi//基址
10027AA5 - 50                  - push eax//==技能ID注意这里是个地址,地址中存放ID
10027AA6 - 52                  - push edx// 1
10027AA7 - FF D7                - call edi   //1008D2C0
10027AA9 - 83 C4 0C         - add esp,0C
10027AAC - 5B                  - pop ebx
10027AAD - 5F                  - pop edi
10027AAE - C3                  - ret



6.地图事件代码
放在$game_map中的@events, 具体结构自己看看就清楚了,很简单,
这里只分析@code,至于@parameters 基本看到就能懂,很多参数我也没分析,对于攻略来说没帮助
太多,直接贴代码
switch (codeNumber)
            {
                case 0:
                  return calcIndent(indent) + "!";
                case 101:
                  return calcIndent(indent) + "对话抬头:" + parameters;
                case 401:

                  if (ep.EventCommand.Last().Replace("\t", "").StartsWith("内容"))
                  {
                        string s401 = ep.EventCommand.Last() + parameters;
                        ep.EventCommand.RemoveAt(ep.EventCommand.Count - 1);
                        return s401;
                  }
                  else
                  {
                        return calcIndent(indent) + "内容:" + parameters;
                  }
                case 102:
                  return calcIndent(indent) + "选择:" + parameters;
                case 103: //数值输入的处理
                  return calcIndent(indent) + "接受输入:变量 [" + parameters + wb.Variables)] + "]," + parameters + "位";
                case 402:
                  return calcIndent(indent) + "选项:" + parameters + "的场景";
                case 104:
                  return calcIndent(indent) + "更改选择:显示位置(0上1中2下):" + parameters + " 窗口显示 0显1布显 " + parameters;
                case 105:
                  return calcIndent(indent) + "输入信息存储到:变量[" + parameters + wb.Variables)] + "]";
                case 106:
                  return calcIndent(indent) + "等待" + parameters + "帧";
                case 108:
                  return calcIndent(indent) + "注释:" + parameters;
                case 408:
                  string s408 = ep.EventCommand.Last() + parameters;
                  ep.EventCommand.RemoveAt(ep.EventCommand.Count - 1);
                  return s408;
                case 111://if语句
                  return calcIndent(indent) + code111(parameters);
                case 112: //循环
                  return calcIndent(indent) + "循环";
                case 113: //循环
                  return calcIndent(indent) + "中断循环";
                case 115:
                  return calcIndent(indent) + "中断事件处理";
                case 116:
                  return calcIndent(indent) + "暂时消除事件";
                case 117:
                  return calcIndent(indent) + "公共事件:[" + parameters + wb.CommentEvent)] + "]";
                case 118:
                  return calcIndent(indent) + "标签:" + parameters;
                case 119:
                  return calcIndent(indent) + "跳转到标签:" + parameters;
                case 121://开关赋值
                  return calcIndent(indent) + code121(parameters);
                case 122: //变量赋值
                  return calcIndent(indent) + code122(parameters);
                case 123: //独立开关
                  return calcIndent(indent) + "独立开关" + parameters + ":" + (parameters == "0" ? "on" : "off");
                case 124://计时器
                  return calcIndent(indent) + code124(parameters);
                case 125: //金钱赋值
                  return calcIndent(indent) + code125(parameters);
                case 126: //物品
                  return calcIndent(indent) + code126(parameters);
                case 127: //武器
                  return calcIndent(indent) + code127(parameters);
                case 128: //防具
                  return calcIndent(indent) + code128(parameters);
                case 129: //替换队员
                  string s129 = "替换队员:";
                  s129 += parameters == "1" ? "主角" : "[" + parameters + wb.Actors)] + "]";
                  s129 += parameters == "0" ? "加入" : "离开";
                  s129 += parameters == "0" ? "" : "初始化";
                  return calcIndent(indent) + s129;
                case 131:
                  return calcIndent(indent) + "更改战斗外观";
                case 132:
                  return calcIndent(indent) + "更改战斗BGM";
                case 133:
                  return calcIndent(indent) + "更改战斗结束 ME ";
                case 134:
                  return calcIndent(indent) + "更改禁止存档 ";
                case 135:
                  return calcIndent(indent) + "更改禁止菜单 ";
                case 136:
                  return calcIndent(indent) + " 更改禁止遇敌 ";
                case 201: //场景移动
                  return calcIndent(indent) + code201(parameters);
                case 202: //设置事件位置
                  return calcIndent(indent) + "人物移动";
                case 203: //画面卷动
                  return calcIndent(indent) + "画面卷动";
                case 204://更改地图设置
                  return calcIndent(indent) + "更改地图设置";
                case 205:// 更改雾的色调
                  return calcIndent(indent) + "更改雾的色调";
                case 206: //更改雾的不透明度
                  return calcIndent(indent) + "更改雾的不透明度";
                case 207: //更改透明状态
                  return calcIndent(indent) + "显示动画";

                case 208: //显示动画
                  return calcIndent(indent) + "显示动画";
                case 209: //事件移动

                  return calcIndent(indent) + "事件移动";
                case 210: //事件移动

                  return calcIndent(indent) + "等待移动结束";
                case 231:
                  return calcIndent(indent) + "显示图片";
                case 232:
                  return calcIndent(indent) + "移动图片";
                case 233:
                  return calcIndent(indent) + "旋转图片";
                case 234: //更改图片色调
                  return calcIndent(indent) + "更改图片色调";
                case 235:
                  return calcIndent(indent) + "图片消失";
                case 221://准备渐变
                  return calcIndent(indent) + "准备渐变";
                case 222: //执行渐变
                  return calcIndent(indent) + "执行渐变";
                case 223: //更改画面色调
                  return calcIndent(indent) + "更改画面色调";
                case 224://画面闪烁
                  return calcIndent(indent) + "画面闪烁";
                case 225://画面振动
                  return calcIndent(indent) + "画面振动";
                case 236: //设置天候
                  return calcIndent(indent) + "设置天候";
                case 241://更改色调
                  return calcIndent(indent) + "更改色调";
                case 242: //bgm淡出
                  return calcIndent(indent) + "bgm淡出";
                case 245: //演奏bgs
                  return calcIndent(indent) + "演奏bgs";
                case 246: //bgs淡出
                  return calcIndent(indent) + "bgs淡出";
                case 247: //记忆bgm/bgs
                  return calcIndent(indent) + "记忆bgm";
                case 248://还原bgm/bgs
                  return calcIndent(indent) + "还原bgm";
                case 249://演奏me
                  return calcIndent(indent) + "演奏me";
                case 250://演奏se
                  return calcIndent(indent) + "演奏se";
                case 251://停止se
                  return "停止se";
                case 301:
                  //战斗
                  return "战斗";
                case 302:

                  return calcIndent(indent) + "开商店";
                case 303:
                  return calcIndent(indent) + "输入更改角色[" + parameters + wb.Actors)] + "]的名字(最多" + parameters + "个字)";


                case 311://增减血量
                case 312: //增加sp
                case 315://增加exp
                  return calcIndent(indent) + code311(parameters, codeNumber);
                case 313:
                  string s313 = "更改状态 ";
                  if (parameters == "0") s313 += "全部同伴";
                  else if (parameters == "1") s313 += "主角";
                  else s313 += "角色[" + parameters + wb.Actors)] + "]";

                  s313 += parameters == "0" ? "附加" : "解除";
                  s313 += "[" + parameters + wb.States)] + "]";
                  return calcIndent(indent) + s313;
                case 314:
                  string s = "完全恢复 ";
                  if (parameters == "0") s += "全部同伴";
                  else if (parameters == "1") s += "主角";
                  else s += "角色[" + parameters + wb.Actors)] + "]";


                  return calcIndent(indent) + s;

                case 316: //等级增减

                  return calcIndent(indent) + code316(parameters);

                case 317: //改变4维

                  return calcIndent(indent) + code317(parameters);
                case 318: //特技
                  return calcIndent(indent) + code318(parameters);
                case 319: //改变装备

                  return calcIndent(indent) + code319(parameters);
                case 320:
                  if (ep.EventCommand.Count > 0 && ep.EventCommand.Last().Replace("\t", "").StartsWith("更改名字"))
                  {
                        string s320 = ep.EventCommand.Last() + parameters;
                        ep.EventCommand.RemoveAt(ep.EventCommand.Count - 1);
                        return s320;
                  }
                  else
                  {
                        return calcIndent(indent) + "更改名字:" + parameters;
                  }
                case 321: //更改角色职业;
                  return calcIndent(indent) + "更改角色职业";
                case 322:
                  return calcIndent(indent) + "更换角色图片" + "[" + parameters + wb.Actors)] + "]";
                case 337:
                  return calcIndent(indent) + "显示动画";
                case 338:
                  return calcIndent(indent) + "伤害处理";
                case 339:
                  return calcIndent(indent) + "强制行动 ";
                case 340:
                  return calcIndent(indent) + "战斗中断 ";
                case 351:
                  return calcIndent(indent) + "呼叫菜单画面";
                case 352:
                  return calcIndent(indent) + "呼叫存档画面";
                case 353:
                  return calcIndent(indent) + "游戏结束";
                case 354:
                  return calcIndent(indent) + "返回标题画面";
                case 355:
                  return calcIndent(indent) + "脚本:" + parameters;
                case 655:
                  string s655 = ep.EventCommand.Last() + parameters;
                  ep.EventCommand.RemoveAt(ep.EventCommand.Count - 1);
                  return s655;
                case 403:
                  return calcIndent(indent) + "选择取消的场景";
                case 404:
                  return calcIndent(indent) + "分歧结束";
                case 411:
                  return calcIndent(indent) + "除此以外场合";
                case 412:
                  return calcIndent(indent) + "分歧结束";
                case 413:
                  return calcIndent(indent) + "回到循环";
                case 509: //设置移动下一行
                  return calcIndent(indent) + "设置方位事件";
                case 605: //商店后续
                  return calcIndent(indent) + "商店";

                default:
                  return "";
            }








1107348661@qq.c 发表于 2020-6-26 19:24

:sleepy:我是谁我在那儿

G.Omniscient.D 发表于 2020-9-26 21:24

我是谁?我在哪?发生了什么?
页: [1]
查看完整版本: RGSS301(金庸5) 内存数据分析