wking's blog

  • 文章分类
    • 日常随笔
    • IT技术
    • 系统封装
    • 大航海时代
  • 关于博主
GOD'S IN HIS HEAVEN, ALL'S RIGHT WITH THE WORLD.
  1. 首页
  2. IT技术
  3. 大航海时代online
  4. 正文

大航海时代online 对话框内存数据分析

2021-12-30 3181点热度 13人点赞 10条评论

文章深入详细分析大航海时代online对话框信息内存数据。

这里所说的对话框,包括一切在游戏内弹出的不可交互式和可交互式窗口,包括人物信息、技能列表、船只信息、交易所买卖界面、睿智之书,甚至账号密码输入框、退出游戏确定提示,都统称为对话框。

先上一张成果图:

CE找核心对话框代码

首先,已知台服v12010版本的对话框基址是121E544。打开CE,添加地址121E544。

从游戏逻辑来说,在游戏内做打开对话框动作,游戏是先生成对话框内容,再读取对话框基址显示对话框内容。反应到CE上,就是在没有对话框的时候,对话框基址是000000,打开对话框后,对话框基址有数值。生成对话框的过程很复杂,所以我们跟踪读取对话框过程。因此在121E544上右键选“什么访问了这个地址”。

然后在游戏里反复打开、关闭对话框,仔细观察CE窗口。通过观察,发现D24B27和D24B3C这两个地址,只有在打开对话框的时候计数会变。而这两个地址是一个代码段的。因此从D24B27入手,继续跟踪。

X64DBG跟踪

x64dbg载入游戏并登录游戏,直接跳转到D24B27代码段,在代码段开头下断。然后游戏内按F1快捷键打开人物信息对话框。x64dbg成功断下来,F8单步到D24B27行:

此时[121E544]地址数值是0,ecx=[121E544],再判断ecx是否为0,跳到了D24B38,读esp+4的值到eax,再[121E544]=eax,之后直接ret了。所以这个函数就是确定 [121E544] 的值,再到上一级行数里继续跟踪。

F8返回到D21D0C,如下图:

下面有3个CALL,怎么确定是哪一个?现在程序已经有了[121E544]的数值,接下来的CALL要用到这个数值。而C语言一般是ecx或堆栈来传递值。所以继续F8单步,观察下面CALL时的ecx和堆栈。并且要循环对话框,在汇编里就是有一个向上跳转的标志。

经过观察,00D00D21D48 call <gvo12010.sub_D25D10>这个CALL非常像,ecx=[121E544],且有2个向上跳转。

由于已知在CE里可以读出来鼠标位置对话框的地址[121E544+C],再反过来跟踪验证D25D10,可以确定这就是遍历对话框地址函数。

经过研究,程序逻辑是:

  1. 通过ecx传入[121E544]的值
  2. D25D10行,[ecx+1C]是ecx的子对话框的基址。判断子对话框数值是否为0。不为0继续。
  3. D25D39行,[esi+24]=1继续,=0跳到D25D5A
  4. D25D46行,CALL EBX作用未知,结果eax=1
  5. D25D4C行,判断[esi+1c],=1表示当前层级的当前对话框还有子对话框,再CALL D25D30通过递归实现继续分解。=0没有子对话框,跳到D25D5A。这句之后,ecx=子对话框基址,esi=当前对话框基址。
  6. D25D5A行,[ESI+14]是当前层级的当前对话框的下一个对话框基址。这句相当于循环里的变量迭代。
  7. D25D61行,判断当前对话框是否是最后一个,不是则跳到D25D43继续循环

python遍历代码

仿照程序逻辑和汇编代码,我写了一个python遍历对话框代码。实测可用,但你没办法直接复制使用,你需要把gvo.read_addr改成读取航海内存的代码。或者根据思路自己写个按键精灵的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
    def enum_dialog(_addr, _list=None) -> list:
        """
        枚举当前打开的对话框信息
 
        Parameters
        ----------
        _addr: int
            传入的对话框基址
        _list: list
            返回的列表。由于可能多次嵌套调用,需要用参数保存返回值
 
        Returns
        -------
        Returns: list
            枚举的列表
        -------
        """
        if _list is None:
            _list = []
        _addr = gvo.read_addr(_addr + 0x1C)
        while _addr:
            temp_dict = {}
            if gvo.read_addr(_addr + 0x24) == 1:
                # 读取当前对话框基础信息
                temp_dict['addr'] = hex(_addr)
                temp_dict['id'] = hex(gvo.read_addr(_addr + 0x4))
                temp_dict['pos_x'] = gvo.read_addr(_addr + 0x34)
                temp_dict['pos_y'] = gvo.read_addr(_addr + 0x38)
                temp_dict['width'] = gvo.read_addr(_addr + 0x4C)
                temp_dict['height'] = gvo.read_addr(_addr + 0x50)
                _list.append(temp_dict)
 
                # [_addr + 0x1C] 是否有当前对话框的子对话框数据,=0没数据,=1有数据
                subaddr = gvo.read_addr(_addr + 0x1C)
                if subaddr:
                    temp_list = enum_dialog(subaddr, _list)
                    _list.append(temp_list)
 
            # [_addr + 0x14] 是否有当前对话框的同级下一个对话框数据,=0没数据,=1有数据
            _addr = gvo.read_addr(_addr + 0x14)
        return _list
 
 
    dialog_base = 0x121E544
    print("base_addr = " + hex(dialog_base).upper())
    a = enum_dialog(dialog_base)
    print(a)

实际利用数据

id

代码效果如下图,每一个对话框都有addr、id、坐标及其他数据。比较重要的是id。id中2ee?开头的都是按钮。也可以根据ID判断脚本是否打开了正确的对话框。

坐标

坐标是相对于对话框的坐标。比如下图id=2ee0的坐标,是返回按钮左上角,相对于船只信息对话框左上角的坐标值(650,380),按钮宽56,高24。在游戏分辨率较大或者缩放了DPI时,可根据对话框相对坐标调整鼠标点击位置。

数值

可以根据子对话框基址+偏移获取所需数据。比如载货界面,船只的货仓状态。

载货界面右下角货仓数据,也是一个小的对话框。地址是06633E08,物资=06633E08+8C,交易品=06633E08+90,仓位=06633E08+94

其他利用,还未研究。

本作品采用 知识共享署名-非商业性使用 4.0 国际许可协议 进行许可
标签: 内存 大航海时代
最后更新:2021-12-30

wking

不管博客型博主

点赞
< 上一篇
下一篇 >

文章评论

  • Keo

    通过这个从船的窗口进去,就能找到当前耐久上限了吧?之前一直没找到这个

    2022-01-04
    回复
    • wking

      @Keo 船的耐久有个单独的静态地址吧?你从船只信息里找,当对话框不打开的时候,看不到耐久的。

      2022-01-04
      回复
  • Keo

    这篇文章能否再补充点怎么找到打开窗口call,以及找到窗口基址的思路 :biggrin:

    2022-01-11
    回复
    • wking

      @Keo 窗口CALL就麻烦了,我也在学习。找窗口基址的思路是有个内存地址,窗口关闭=0,打开=1。从这里逆推出来基址

      2022-01-12
      回复
  • ct

    感谢提供思路,最近刚好在研究大航海,希望博主多写点这些内容,让我们小白多学习学习

    2022-04-06
    回复
  • 笨蛋一枚

    大航海时代 Online 版本 12.030

    找出了对话方块的基底位置为 0x01720EE4,也成功地读取出人物情报跟载货的交易品资讯

    可是在读取持有物品一览时,目前只找到了几个资讯
    持有物品数量 ( 偏移值 658 ) / 持有物品数量最大值 ( 偏移值 614 )
    船长袋收藏数 ( 偏移值 6F4) / 船长袋收藏数最大值 ( 偏移值 61C )
    档案夹收藏数 ( 偏移值 6D8) / 档案夹收藏数最大值 ( 偏移值 618 )

    接着要读取物品一览的资讯时,使用了物品的 ID、属性内容 ( 耐久度 )等已知讯息下去搜索,怎么找都找不出相符合的记忆体纪录。

    最后用了消耗物品的数量以未知初始值跟增减的方式下去找,发现记忆体实际内容最为可能的纪录是,消耗物品数量x 256 ( 1 = 0x0100,200 = 0xC800 ),也买了好几种消耗用品进行研究,发现+0x14 是下一种消耗用品的数量,但进去该位置浏览记忆体时,发现数量跟我研究出来的相符合,可是又找不到消耗物品的ID。

    请问要如何找出物品一览的详细资讯,还是有什么文章或者思路可以参考的,感恩!

    另外请问在周围人物算法分析中,是要如何确定基底位置?

    2024-11-28
    回复
    • wking

      @笨蛋一枚 不知道你有没有学过c语言的数组?持有物品可以看做一个数组。当然实际上,他更像是c++语言的STL标准模板库的map容器,或者python语言的字典。键(key)就是物品ID,值(value)是游戏自定义的结构体。所以,你需要根据物品ID来找,不能用耐久度找。物品的说明、耐久度这些都是存储在其他地方的数据,不在持有物品里。

      你想要找物品的详细资讯,就需要先理解游戏的对话框,学会分析持有物品子对话框,然后学会分析持有物品数组,最后分析物品值(value)的游戏自定义结构体。

      2024-11-28
      回复
    • wking

      @笨蛋一枚 周围人物算法找基址,最快捷方便的方法是你去一个偏僻的NPC房间,数一数房间内NPC加上你自己角色,一共有几个人,比如是3个人。用CE搜索数值3。然后控制另外一个角色,进入这个房间,CE搜索4。控制另一个角色离开房间,CE搜索3。反复搜索几次,就可以搜到一个静态地址。这个地址前面4字节就是基址。

      2024-11-28
      回复
  • 笨蛋一枚

    昨天是在商店买陆战用品,今天搜索别的消耗物品,数量又不对了,头大。

    2024-11-28
    回复
  • 笨蛋一枚

    感恩!谢谢指导,我再研究看看。

    2024-11-29
    回复
  • razz evil exclaim smile redface biggrin eek confused idea lol mad twisted rolleyes wink cool arrow neutral cry mrgreen drooling persevering
    取消回复

    目录
    • CE找核心对话框代码
    • X64DBG跟踪
    • python遍历代码
    • 实际利用数据
      • id
      • 坐标
      • 数值
      • 其他利用,还未研究。
    标签聚合
    大航海时代 wordpress 一支红杏 C++ win10 linux R6300V2 OneNote

    COPYRIGHT © 2024 wkings.blog. ALL RIGHTS RESERVED.

    Theme Kratos Made By Seaton Jiang