植物大战僵尸吧 关注:555,456贴子:5,045,193

图片之下——PVZ程序内存中的数据地址列表(PC v1.0.0.1051限定)

取消只看楼主收藏回复



IP属地:江苏1楼2014-01-31 23:52回复
    如果说PAK控制了程序可能显示在屏幕上的东西,显然只有程序中的可执行代码决定了如何使用这些PAK,并且还不止于此。而代码所使用的数据,代表了游戏的状态。读取内存中的数据是挂机的一次重大突破,促成了24炮的诞生,而修改内存中的数据是实现修改器的重要方式。
    试过修改pvz的人应该会发现,pvz的数据地址(如阳光地址)是动态决定的——显然存在基址和偏移,但这里的偏移不是一级,常常是三级。要想在多次游戏中都能以同一方式找到数据的地址并不容易,也正是由于偏移较多,要想表示出数据的地址,就需要牺牲可读性了。
    本贴希望作为http://tieba.baidu.com/f?kz=1014349785的后继,记录下pvz中的数据,无论看起来有用与否,或许将来会发现“无用”基址的别样用途


    IP属地:江苏2楼2014-01-31 23:54
    回复
      格式说明
      一切数据地址以6A9EC0为基址,每向右一层为一级偏移,沿纵向的线条竖直向上可找到对应的偏移值。“—”右侧为对应数据的最后一级偏移。
      本帖的地址都用16进制数表示,不使用任何前缀,其余数据一般使用⑩进制,请注意区分。


      IP属地:江苏3楼2014-01-31 23:54
      收起回复
        数据类型说明
        众所周知,一切数据在内存中都以一串二进制数字的形式存储,8个二进制位是一个字节。但数字所占的字节数不会存储,此外占相同字节数的二进制数也有不同的解释方式。数据的字节数和解释方式可以从操作数据的指令推断。根据常见的占字节数和解释方式,可以总结出常见的(标量)类型。
        对于地址以外的类型,本帖会根据观察到的行为,在//之后的括号里标出具体的类型。以下是各个类型所占的字节数和解释。
        ·(未标出):4字节,解释为有符号的整数。
        ·Byte:1字节,通常是布尔值(Boolean),根据值为0或值不为0两种情况,将采取不同的行为。
        ·Float:4字节,(单精度)浮点数,表示一个可能带有小数部分的数字。
        ·String[n]:(n为数字)字符串,所占位数为n,表示一串字符。
        二进制数如何转换为不同的内容,这里不会解释。只要知道使用程序读写内存时,对于不同类型的数据可能需要不同的函数,例如按键精灵提供了Read32Bit用以读取4字节(32位)整数,ReadSingle用以读取单精度浮点数,ReadString读取字符串。
        对于直接使用ReadProcessMemory/WriteProcessMemory系统调用的编程党,也要注意使用不同类型的变量来接收数据(例如用int32_t变量接收4字节整数)。另外也需要指定正确的数据长度。
        到目前为止的观察发现,在常见的数据类型中,程序没有使用双精度浮点数和2字节数(No Double, 2bytes,简称ND2)。
        程序没有使用双精度浮点数和2字节数,简称ND2,可以作为一条公理,至于你信不信,反正我是信了。


        IP属地:江苏4楼2014-01-31 23:54
        收起回复
          │├—94//最多时僵尸数(注意僵尸属性在序列中的排列顺序与其他对象属性不同)
          │├—98//可容纳的最大僵尸数{1024}(?)
          │├—9C//内存中下一个未被占用的僵尸位置
          │├—A0//当前僵尸数
          │├┬AC//植物对象序列,+14C下一个
          ││├—8//植物X坐标
          ││├—C//植物Y坐标
          ││├—18//(Byte)为0时隐形
          ││├—1C//植物行数(0-5)
          ││├—20//与图像遮盖关系有关
          ││├—24//植物特性种类
          ││├—28//植物列数(0-8)
          ││├—3C//植物状态(?)
          ││├—40//当前血值
          ││├—44//血值上限
          ││├—48//是否开火(1为开火)
          ││├—4C//植物消失倒计时(?)
          ││├—50//灰烬植物、寒冰菇、三叶草生效倒计时
          ││├—54//各种倒计时
          ││├—58//发射子弹/产生物品倒计时,为0时开始子弹发射动作/产生物品
          ││├—5C//固定子弹发射/产生物品时间
          ││├—90//子弹发射动作倒计时,为1时子弹产生,为0时植物恢复为子弹发射前的状态
          ││├—B0//眨眼倒计时(为0时眨眼)
          ││├—B8//植物亮度(倒计时,为0时不发亮)
          ││├—130//阳光蘑菇长大倒计时,蘑菇睡醒倒计时
          ││├—141//(Byte)不为0时植物消失
          ││├—142//(Byte)不为0时植物为被压扁状态
          ││├—143//(Byte)不为0时植物为睡着状态
          ││└—145//(Byte)为0时不发亮,否则发亮(如当铲子移到这个植物上时,咖啡豆移到睡觉的蘑菇上时)
          │├—B0//最多时使用的植物数
          │├—B4//可容纳的最大植物数{1024}(?)
          │├—B8//内存中下一个未被占用的植物位置
          │├—BC//当前植物数


          IP属地:江苏6楼2014-01-31 23:55
          收起回复
            │├┬C8//子弹对象序列,+94下一个
            ││├—8//子弹X坐标(图像位置,+30的整数部分)
            ││├—C//子弹Y坐标(图像位置,+34与+38的和的整数部分)
            ││├—18//(Byte)为0时隐形
            ││├—1C//子弹所在行数
            ││├—20//与图像遮盖关系有关
            ││├—30//(Float)子弹X坐标(影子图像位置,用以计算图像位置)
            ││├—34//(Float)子弹Y坐标(用以计算图像位置)
            ││├—38//(Float)子弹Y坐标改变的值(用以计算图像位置)(子弹图像Y坐标为该处的值与+34处的值的和)
            ││├—3C//(Float)子弹X坐标(+30)每cs增加的数值
            ││├—40//(Float)子弹Y坐标(+34、+4C)每cs增加的数值
            ││├—44//(Float)子弹Y坐标(+38)每cs增加的数值
            ││├—48//(Float)+44每cs增加的数值
            ││├—4C//(Float)影子Y坐标
            ││├—50//(Byte)不为0时子弹消失
            ││├—58//运动方式?
            ││├—5C//子弹类型
            ││└—60//当前为该子弹被发射后的第多少cs
            │├—CC//最多时子弹数(?)
            │├—D0//可容纳的最多子弹数(?)
            │├—D8//当前子弹数量


            IP属地:江苏7楼2014-01-31 23:55
            收起回复
              │├┬E4//掉落物品对象序列,+D8下一个
              ││├—8//物品X坐标增加量(图像位置)
              ││├—C//物品Y坐标增加量(图像位置)
              ││├—10//物品判定宽度
              ││├—14//物品判定高度
              ││├—18//(Byte)为0时隐形
              ││├—20//与图像遮盖关系有关
              ││├—24//(Float)物品X坐标
              ││├—28//(Float)物品Y坐标
              ││├—34//(Float)物品大小
              ││├—38//(Byte)不为0时消失
              ││├—48//物品将要移动到的Y坐标
              ││├—4C//物品已存在时间,影响一些物品的明暗变化
              ││├—50//(Byte)不为0时为被收集状态
              ││├—58//物品类型
              ││└—68//植物类型(植物卡牌)
              │├—F4//场上物品数量


              IP属地:江苏9楼2014-01-31 23:56
              回复
                │├┬138//鼠标相关对象属性
                ││├—8//X坐标(相对+768+5558减少25)
                ││├—C//Y坐标(相对+768+555C减少25)
                ││├—18//鼠标是否在游戏界面内(0为不在,1为在)
                ││├—24//鼠标上的植物对应的卡槽序号
                ││├—28//鼠标上的卡牌序号
                ││├—2C//模仿者模仿内容
                ││└—30//类型


                IP属地:江苏11楼2014-01-31 23:57
                回复
                  │├┬140//白字(实际上还有红字等)对象属性
                  ││├—4//(String[132])白字内容
                  ││├—88//消失倒计时
                  ││└—8C//白字类型(0-17)


                  IP属地:江苏12楼2014-01-31 23:57
                  回复
                    │├┬144//卡槽,+28以前为整个卡槽的属性,以后为每格的属性,+50下一格
                    ││├—8//卡槽X坐标
                    ││├—C//卡槽Y坐标
                    ││├—10//卡槽横向判定范围
                    ││├—18//(Byte)为0时卡槽隐形
                    ││├—24//卡槽格数
                    ││├—30//卡槽中的卡牌的X坐标
                    ││├—34//卡槽中的卡牌的Y坐标
                    ││├—38//卡牌判定宽度
                    ││├—3C//卡牌判定高度
                    ││├—40//(Byte)为0时隐形
                    ││├—4C//已冷却时间
                    ││├—50//总冷却时间
                    ││├—54//卡槽序号(这是第几格)
                    ││├—58//传送带中滑动倒计时,卡牌实际坐标为这里与+30的值的和
                    ││├—5C//卡槽内容
                    ││├—60//模仿者模仿内容
                    ││├—64//Slot Machine中老虎机停止倒计时,为0时该格停止变化
                    ││├—68//Slot Machine中该格接下来转出的内容
                    ││├—70//(Byte)为0时为冷却中(或被鼠标点中但未被种植),否则为可用
                    ││└—71//(Bool)为0时为鼠标点中但未被种植
                    │├┬148//右上角的的Menu按钮
                    ││├—8//X坐标
                    ││├—C//Y坐标
                    ││├—14//文字纵向位置偏移
                    ││└—84//(String[15])按钮内容
                    │├┬14C//LS的Start Onslaught按钮
                    ││├—8//X坐标
                    ││├—C//Y坐标
                    ││├—14//文字纵向位置偏移
                    ││├—18//(Byte)鼠标是否悬停在按钮上,0为否
                    ││├—19//(Byte)鼠标是否按下按钮,0为否
                    ││└—84//(String[15])按钮内容


                    IP属地:江苏13楼2014-01-31 23:57
                    回复
                      │├—164//(Byte)不为0时游戏暂停
                      │├—168~23C//场景每格类型
                      │├—554~5C0//雾的浓度(每4字节只用首个字节,另3个字节值为0)
                      │├—5D8~5EC//每行出怪类型
                      │├—60C~620//每行冰道最左坐标(最右坐标为800)
                      │├—624~638//每行冰道消失倒计时,小于10时冰道有逐渐变浅的过程
                      │├—554C//场景类型
                      │├—5550//(冒险模式)当前关卡
                      │├—5558//鼠标X坐标
                      │├—555C//鼠标Y坐标
                      │├—5560//阳光
                      │├—557C//已刷新波数(当前选卡)
                      │├—559C//下一波僵尸刷新时间(倒计时)
                      │└—561C//出怪种子码
                      ├—7F8//当前模式(同存档编号)
                      └—7FC//当前游戏状态


                      IP属地:江苏14楼2014-01-31 23:57
                      收起回复



                        IP属地:江苏15楼2014-01-31 23:58
                        收起回复
                          示例:如何阅读列表中的地址(以“僵尸相关倒计时”为例)
                          令 [ X ] 表示地址 X 存储的数据,其中 X 可能是一个16进制数或一个表达式。对于按键精灵而言, [ X ] 代表的实际代码是 Plugin.Memory.Read32Bit(Plugin.Window.Find(0, "Plants vs. Zombies"), X)。

                          如图,“僵尸相关倒计时”所在行已被选中。
                          由最左侧的竖线(已标为红色)向上,可读到基址6A9EC0(16进制)。
                          可以在纸上写下
                          6A9EC0
                          由左数第二条竖线(橙色)向上,可读到第一级偏移768(16进制)。
                          在纸上继续写下+768,现在纸上的内容为
                          6A9EC0 +768
                          继续向右,由左数第三条竖线(已标为绿色)向上,可读到第二级偏移90(16进制)。
                          在纸上继续写下+90,现在纸上的内容为
                          6A9EC0 +768 +90
                          “—”的右侧为最后一级偏移,即68(仍然是咲夜进制)。
                          在纸上继续写下+68,现在纸上的内容为
                          6A9EC0 +768 +90 +68
                          个人有时装B,会直接把这个式子(加上c++使用的16进制前缀0x)作为所需的地址。这个式子看起来像是普通的数学表达式,但很容易想到,如果这个式子只是普通的数学表达式,为何不直接写出最后的结果,而要弄出三级偏移?显然它不是普通的数学表达式。
                          上面的式子的更准确形式是(注意表达式中的数都是16进制)
                          [[[[6A9EC0] +768] +90] +68]
                          [ ] 的意义见本楼顶部。
                          具体到按键精灵,可以在给数字加上表示16进制的前缀后,直接把[?]替换为Plugin.Memory.Read32Bit(Plugin.Window.Find("Plants vs. Zombies", 0), ?),假如缩进合理的话,大概还能看(误
                          (截图时使用的编辑器不是按键精灵,事实上是Notepad++的自定义高亮。)
                          通过引入中间变量,可以得到对一些人而言可读性更强的代码

                          当然也可以有其他更好的写法


                          IP属地:江苏40楼2014-02-03 20:25
                          回复
                            练习:获得“下一波僵尸刷新时间倒计时”的地址
                            “下一波僵尸刷新时间倒计时”所在行为:
                            │├—559C//下一波僵尸刷新时间(倒计时)
                            仍然可以由最左侧的竖线向上读到基址6A9EC0,由左数第二条竖线向上读到第一级偏移768,在“—”的右侧读到最后一级偏移559C。不过由于pvz使用了科学的“结构化编程”,所有数据的基址都是6A9EC0,几乎所有与当前关相关的数据的第一级偏移都是768,至少在本帖中都是这样。因此可以快速得出所需地址为
                            6A9EC0 +768 +559C
                            或更准确的形式
                            [[[6A9EC0] +768] +559C]
                            列表中有时使用了“+768 +559C”这样只写出最后两级偏移或“+90”这样只写出最后一级偏移的写法


                            IP属地:江苏41楼2014-02-03 20:25
                            回复
                              question:
                              1.[[[[6A9EC0] +768] +90] +68]只是一个地址,而僵尸可能有上千个,如何读到所有僵尸的相关倒计时?
                              2.这个“相关倒计时”到底是什么含义?如何应用到实际当中?
                              唔,楼下会发一个实际运用的例子——在我吃完饭以后。在此之前,可以看看http://tieba.baidu.com/p/709879550,以及学习按键精灵文件操作的相关函数


                              IP属地:江苏42楼2014-02-03 20:25
                              回复