最近突然对大航海又来了兴趣,故捡起了AFK 10年的大航海时代online。顺便也研究一下汇编、自动化脚本,提高技术知识。
前期工作
10年前就阅读过遍历人物思路,以及写过遍历周围人物代码。10年前的思路如下:
qingye在以前发过《几个关于对象的地址》,用这地址可得到时TAB选择对象时(NPC、玩家或场景)的代码(ID)。最近学习OD,参考zard110用商品ID得到商品名的思路及OD的使用方法,经过几天的跟踪解析,终于搞清了算法,由NPC或玩家的ID得到了名字。具体算法如下:
1、先给出个基址:addrbase=&H00af89ac
2、读取基址的内存值记为A
3、对象ID除于16,取整数,记为C
4、C除于[基址+4]的内存值,取余数,记为B
5、[A+B*4]得到addr
6、[addr]<>ID的话,再取[addr+8]的值作为addr,再继续这一步计算,直到[addr]=ID
7、[[[addr+4]+3c]+0]的值就是TAB选择对象的名字
因为游戏内存结构一般不会变化,可以从现在版本里,其他好查找的内存地址入手,推测出现在的基址是:0x0119E630。但是把脚本里的基址改了以后,脚本无法运行了。看来是10年来游戏的内部结构变化还是很大,需要重新找算法。
首先观察一下0119E630这个地址的数据
F0 | F4 | F8 | FC | |
0119E630 | 10437FC0 | 00000011 | 0000000E | 17D4B8E4 |
经过一些操作比如换场景、进出港口,可以观察出来:每次换场景,除了0119E634不变,其他3个地址的值先变0,再变有数值。其中0119E638是场景的对象数量,0119E63C每次对象数量变化,也会跟着变化。
正式开始
从基址倒推
使用CheatEngine,在0119E630下“找出是什么访问了地址”,之后CE会立刻弹出来信息框,显示有好几条地址不停的在访问0119E630。
由于之前看到人数是实时变化的,那么程序应该无限循环读取基址。所以我们看看计数最多的2条0597066和0599BD8。
00597066这个地址的代码,上面是好几条跳转指令,而且还有xmm0这种浮点数运算指令,从经验上来说,不太可能。因为基址是一个指向其他内存地址的数值,对象ID也肯定是正整数,即便基址需要经过运算,那也一定是正整数的运行,因为其他内存地址也一定是正整数。
00599BD8这个地址,代码位于函数开头,直接读取基址数值,然后判断跳转,看起来比较像。为什么呢?因为基址之所以是“基址”,就是基址是需要再经过二次运算,才能找到对象ID之类的数据,那么基址的运算一般是位于函数最开头部分的。
用x64dbg跟踪
基址代码段解析
现在换x64dbg进一步跟踪。直接CTRL+G跳转到00599BD8,下断点。这个代码段不多,但我水平不高,也耗费了不少时间分析。这里我就直接贴分析结果了。
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 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | 00599BD0 | 55 | push ebp | 根据活动对象基址计算活动对象内存指针的函数 00599BD1 | 8BEC | mov ebp,esp | 00599BD3 | 53 | push ebx | 00599BD4 | 56 | push esi | 00599BD5 | 57 | push edi | 00599BD6 | 8BF9 | mov edi,ecx | 00599BD8 | 8B5F 04 | mov ebx,dword ptr ds:[edi+0x4] | 00599BDB | 85DB | test ebx,ebx | 00599BDD | 74 6B | je gvo.599C4A | 00599BDF | 8B75 08 | mov esi,dword ptr ss:[ebp+0x8] | 读取堆栈的堆栈,也就是上次计算出来的下一个节点地址,也就是当前的节点地址 00599BE2 | 8B36 | mov esi,dword ptr ds:[esi] | 00599BE4 | 85F6 | test esi,esi | 00599BE6 | 74 67 | je gvo.599C4F | 00599BE8 | 83FE FF | cmp esi,0xFFFFFFFF | 00599BEB | 75 1B | jne gvo.599C08 | 00599BED | 8B57 08 | mov edx,dword ptr ds:[edi+0x8] | [edi+0x8]=11 固定值 00599BF0 | 33C9 | xor ecx,ecx | 计数器置0 00599BF2 | 85D2 | test edx,edx | 00599BF4 | 74 12 | je gvo.599C08 | 00599BF6 | 8BC3 | mov eax,ebx | 00599BF8 | 8B30 | mov esi,dword ptr ds:[eax] | 循环读取eax=eax+4;esi=eax,直到esi值!=0 00599BFA | 85F6 | test esi,esi | 00599BFC | 75 0A | jne gvo.599C08 | 00599BFE | 41 | inc ecx | 00599BFF | 83C0 04 | add eax,0x4 | 00599C02 | 3BCA | cmp ecx,edx | 00599C04 | 72 F2 | jb gvo.599BF8 | 00599C06 | EB 4C | jmp gvo.599C54 | 00599C08 | 8B4E 08 | mov ecx,dword ptr ds:[esi+0x8] | 下一个节点的地址 00599C0B | 85C9 | test ecx,ecx | 00599C0D | 75 20 | jne gvo.599C2F | 00599C0F | 8B7F 08 | mov edi,dword ptr ds:[edi+0x8] | 00599C12 | 8B46 0C | mov eax,dword ptr ds:[esi+0xC] | 00599C15 | 33D2 | xor edx,edx | 00599C17 | F7F7 | div edi | 00599C19 | 42 | inc edx | 00599C1A | 3BD7 | cmp edx,edi | 00599C1C | 73 11 | jae gvo.599C2F | 00599C1E | 8D0493 | lea eax,dword ptr ds:[ebx+edx*4] | 当前[0119e630]再往后edx(从0开始算)个,直到有数据 00599C21 | 8B08 | mov ecx,dword ptr ds:[eax] | 00599C23 | 85C9 | test ecx,ecx | 00599C25 | 75 08 | jne gvo.599C2F | 00599C27 | 42 | inc edx | 00599C28 | 83C0 04 | add eax,0x4 | 00599C2B | 3BD7 | cmp edx,edi | 00599C2D | 72 F2 | jb gvo.599C21 | 00599C2F | 8B45 08 | mov eax,dword ptr ss:[ebp+0x8] | 00599C32 | 5F | pop edi | 00599C33 | 8908 | mov dword ptr ds:[eax],ecx | 链表下一个节点地址放到0019F8F4 00599C35 | 8B0E | mov ecx,dword ptr ds:[esi] | 00599C37 | 8B45 0C | mov eax,dword ptr ss:[ebp+0xC] | 00599C3A | 8908 | mov dword ptr ds:[eax],ecx | ecx:L"\n" 00599C3C | 8B4E 04 | mov ecx,dword ptr ds:[esi+0x4] | 00599C3F | 8B45 10 | mov eax,dword ptr ss:[ebp+0x10] | 00599C42 | 5E | pop esi | 00599C43 | 8908 | mov dword ptr ds:[eax],ecx | 上层的EBP+8赋值 00599C45 | 5B | pop ebx | 00599C46 | 5D | pop ebp | 00599C47 | C2 0C00 | ret 0xC | 00599C4A | E8 30D97800 | call gvo.D2757F | 00599C4F | E8 2BD97800 | call gvo.D2757F | 00599C54 | E8 26D97800 | call gvo.D2757F | |
代码用python翻译一下大概意思如下:
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 | # sub599BD0部分 ecx = 0x0119E62C edi = ecx ebx=[0119E630] if ebx !=0: esi = [[ebp+8]] or 0xFFFFFFFF # 从上次循环读取esi的值,或者当第一次循环时esi=FFFFFFF if esi !=0: if esi == 0xFFFFFFFF: edx = [0119E634] # 循环次数 =11 ecx = 0x0 # 计数器 if edx !=0: eax = ebx while ecx < edx: # 循环,读取[0119E630]之后首个有数据的地址。也即链表首个节点的内存地址 esi = [eax] if esi == 0: ecx += 1 eax = eax + 0x4 else: break # 此时ESI是链表当前节点的内存地址。[ESI]=当前对象ID。[ESI+8]=链表下一个节点 ecx = [esi+0x8] # 如果ecx==0也就是链表下一个节点=0,则进一步处理 if ecx ==0: edi = [edi+0x8] # 固定值11 eax = [esi+0xC] # 地址计算所需的数据 edx = 0 # 计数器 eax = eax / edi edx = eax mod edi # 余数放edx 是个<0X11的数 其实就是[119e630]往后读了多少次 edx += 1 if edx < edi: eax = [ebx+edx*4] # 当前[0119e630]再往后edx(从0开始算)个,直到有数据 while edx < edi: ecx = [eax] if ecx ==0: edx += 1 eax = eax +0x4 # 链表下一个节点地址ecx放到0019F8F4 # eax = dword ptr ss:[ebp+8]=[0019F8E0]=0019F8F4 ecx = [esi] # 这里是对象ID # ecx = 放到堆栈0019F904 ecx = [esi+4] # 这里是对象数据地址指针 # ecx = 放到堆栈0019F8F8 |
这个函数是根据基址计算第一个数据内存地址、或者根据当前数据地址计算下一个数据内存地址的函数。代码主要分两部分,if esi == 0xFFFFFFFF部分是第一次循环时,esi=FFFFFFF,再读取[0119E630]之后首个有数据的地址。也即链表首个节点的内存地址;或者读取上次循环的值。
之后再ecx = [esi+0x8]读取下一个数据的值。如果[esi+0x8] == 0,那再进行一下除法和余数什么的运算,读当前[0119e630]再往后edx(从0开始算)个,直到有数据。
再由上层代码来控制循环结构,追踪后发现是固定循环[0119E634]次。
也就是说,程序可能按照不同对象的属性分类,一共有17条链表,每条链表里面有不定数量的节点,每个节点就是一个活动对象。
链表数据结构
用CE打开[0119E630]的内存区域看看。
这明显是一个链表数据。第一个4字节是当前对象ID,第二个4字节是对象详细数据的内存指针,里面有坐标、海拔、中文名之类的、第三个4字节是下一个链表节点的内存地址。第四个4字节是当下一个链表节点数值为0时,用来重新计算链表节点内存地址的(eax = [ebx+edx*4])。
对象详细数据分析
这一块不详细写了,用x64dbg跟着一步步F7/F8就走到了。
直接上图,005E14A0就是处理人物详细数据的函数。ESI的16F7DF08就是当前要处理的人物数据结构的起始内存地址。
起始内存地址的值始终为00EE0C00(不知道为什么)。之后是对象ID,[+2C]是中文名指针,下面是坐标。其他的数据没研究出来有什么名堂。
程序自己处理中文名的流程也是非常复杂严谨:
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 | 005DFDA5是否有中文名的判断流程 if [0119E66C+4] !=0 if [[0119E66C+4]+1C] == 0xB eax = 0 if eax == 0 if [119e030+36] != 30 ecx = esi = 0119E030 eax = [ecx+510] eax = eax and 0x1 if eax == 1 ecx = esi = 0119E030 eax = [ecx+510] eax = eax shr 0x1 eax = eax and 0x1 if eax ==1 eax = [esi] =[119e030] # 004F89A5 | FF50 0C | call dword ptr ds:[eax+0xC] | eax = [ecx+0x4] = [119e030+4] if eax != 0 al = [esi+5D1] = [119e030+5D1] al = not al movzx eax,al eax = eax shr 1 eax = eax and 1 if eax != 0 中文名 = [esi+2c] |
总结
最开始代码尝试用按键精灵写,但自从学会python后,回过头再用按键精灵,发现按键精灵不仅程序响应慢、功能极少、文档缺失,最关键由于航海是UTF16编码的,按键精灵各种进制转换、编码转换浪费了我一大半编写时间。所以以后我换python写脚本了。
顺便推荐两个python库,写脚本必备:pywinauto后台键鼠库,pymem读写内存库。这两个库基本都是对win32api的再封装,有了这两个库,连大漠都不需要了。
遍历思路
航海程序自己遍历有非常多的限制和分支,我开始模仿航海程序写脚本,头都大了。后来我发现我可以暴力一点:直接从对象基址0119E630读取链表地址,然后暴力读取每一条链表不就行了吗?至于中文名,再暴力读取每一个节点的中文名地址,如果地址数值>0,那不就肯定是有中文名吗?
当然这样无法区分NPC、玩家、以及书台这种可互动的对象。但那没关系,我们只要知道附近的情况已作出判断就好了。
读人名代码
从我python脚本里复制出来的,我已修改,直接能用。
效果如图(战列舰里斯本人就是多!)
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 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 | import pymem PID = 1234 # 你需要自己获取指定航海的PID pm = pymem.Pymem() pm.open_process_from_id(PID) def readaddr(pymem_instance, address: int, data_type: str = None, offset_list: list = None): # 如果有偏移地址,则循环读取偏移,得出最终要读的内存地址。没有偏移地址,最终地址=基址+地址 if offset_list is not None: try: len(offset_list) except TypeError: return None else: if len(offset_list) > 0 and type(offset_list) == list: offset_num = 1 for offset in offset_list: address = pymem_instance.read_int(address) + offset offset_num += 1 else: return None if data_type == "unicode": result = "" _temp_address = address while True: _temp = pymem_instance.read_bytes(_temp_address, 2) if _temp == b'\x00\x00': break else: try: _temp = _temp.decode('utf-16') except UnicodeDecodeError: _temp = ascii(_temp) finally: result += _temp _temp_address += 2 elif data_type == "float": result = pymem_instance.read_float(address) elif data_type == "hex": result = hex(pymem_instance.read_int(address)).upper() else: result = pymem_instance.read_int(address) return result def enum_liveobj() -> tuple: """ 枚举附近的活动对象 :return: tuple 枚举出来的附近所有活动对象,包括没名字的、非人类、人类等; 活动对象数量; 人类对象数量; """ liveobj_list = [] found_num = 0 human_num = 0 baseaddr = 0x0119E630 for i in range(readaddr(pm, baseaddr + 4)): # +4是算法内置的循环次数 nodeaddr = readaddr(pm, readaddr(pm, baseaddr) + i * 4) if nodeaddr == 0: # [baseaddr]为0,下一个循环 continue temp_list = [] while True: ID = readaddr(pm, nodeaddr) data = readaddr(pm, nodeaddr + 4) name = readaddr(pm, nodeaddr + 4, data_type='unicode', offset_list=[0x2c, 0]) cur_pos_x = readaddr(pm, nodeaddr + 4, data_type='float', offset_list=[0x13c]) cur_pos_y = readaddr(pm, nodeaddr + 4, data_type='float', offset_list=[0x144]) cur_altitude = readaddr(pm, nodeaddr + 4, data_type='float', offset_list=[0x140]) dst_pos_x = readaddr(pm, nodeaddr + 4, data_type='float', offset_list=[0x15c]) dst_pos_y = readaddr(pm, nodeaddr + 4, data_type='float', offset_list=[0x164]) dst_altitude = readaddr(pm, nodeaddr + 4, data_type='float', offset_list=[0x160]) next = readaddr(pm, nodeaddr + 8) if len(name) > 0: temp_list.append( {'ID': ID, 'name': name, '当前坐标X': cur_pos_x, '当前坐标Y': cur_pos_y, '目的坐标X': dst_pos_x, '目的坐标Y': dst_pos_y, '当前海拔': cur_altitude, '目的海拔': dst_altitude, } ) human_num += 1 found_num += 1 if next == 0: liveobj_list.append({i: temp_list}) break else: nodeaddr = readaddr(pm, nodeaddr + 8) return liveobj_list, found_num, human_num liveobj_list, found_num, human_num = enum_liveobj() print(liveobj_list) print(f'一共 {found_num} 个活动对象,{human_num} 个人类') |
文章评论
最近在研究航海找call,下了bp WSASend断点gvo没断下来 0.0
博主能否出个找从码头进城里的call教程呢
@小鱼干 AD9CF0就是码头进城CALL了。航海用的不是WSAsend,你用bp ws2_32.send就可以断下来。不过有很多心跳包你得过滤
@wking 感谢博主
@wking 00AD9CF0 | 0FB607 | movzx eax,byte ptr ds:[edi] |
不对耶
@菜菜 AD9CF0是游戏版本12006的地址,现在游戏升级到12007版本了,对应地址是ADC840
请问博主,除了ctypes win32api模块调用call之外,如果用pymem调用的话,有什么好的办法么?
在查询了pymem的资料后,发现有pm.assemble,但是测试发现不可用
@小鱼干 pymem调用汇编代码不好使。我用的 这个
请教下找货舱总量的基址。
我用CE查找1460(角色只开着一条克莱蒙特),搜索出地址0017873C,应该不是基址。奇怪的是,尝试其他进程,0017873C也能查得出货舱总量。尝试着跟进去,发现好长一串都找不到对应的值。
请问博主有什么好方法么?
@小鱼干 基址121D544,物资数量 (水粮木炮) +E74,交易物品数量+E78,最大仓位+E7C。这个基址是对话框基址,游戏里所有可交互对话框都是这个基址
大佬,你关于大航海的文章好少,最近正在学找call,能否抽空写一些指导指导,像寻路、使用技能、进们、对话
@keo 是少。本身就是业余爱好,技术也不高。而且找CALL属于核心技术了,也不好公开,太影响游戏平衡
不好公开,那私底下指导行不
@keo 单独有CALLL没用。有CALL以后,还需要写出来整套的控制流程代码,CALL才有作用。那这样就已经是一个完整的外挂了。
王老板,加一下微信,微信号是邮箱名
@Keo 不加了吧,我也没研究多深入啊
感谢博主答疑。blog有时候https访问不了,我还以为是我网络问题,换http就能访问了
想请教下按船只-舰队下“载货”里商品的基址。
我尝试用CE搜索商品的数量(数值类型:群组),得到几个地址
00E44290 一直会断下来
00AD7773 是数组的形式,可是往上跟找不到对应的。
121E544附近的地址也看了遍,也没有类似的。
请问这个要怎么读呢?
@小鱼干 https现在开启了。之前懒的申请SSL证书。
你是要读取所有载货的数据吧?交易品名称、数量什么的。这个我还没有具体研究。但从编程角度看,载货界面是一个对话框,对话框内又分几个子对话框,包括水粮木炮、船只样子、载货列表、仓位信息。要读取载货列表的数据,需要读取对话框(121E544)信息→子对话框载货列表信息→遍历列表才可以。不是靠光知道121E544就可以的。
这个哪天我有空了,仔细研究一下,再写篇文章吧。
你可以留真实的邮箱地址,别人看不见,我回复了会有邮件通知。
子对话框是不是121e544+c啊?我看鼠标在窗口里你描述的那些不同位置自动,这里都会变化
@Keo 是的。+8=鼠标按住地址,+C=鼠标当前位置地址,+10=当前焦点地址,+3C及后面几个地址是窗口大小位置
那我没法后台向卖货的窗口中的商品发点击消息,是不是因为我是对着游戏窗口句柄发消息,而实际上需要子对话框的句柄发才行啊?这个子对话框的句柄在内存里能找到吗?
@Keo 不是句柄的问题,是鼠标模拟的问题,或者说windows驱动的问题。如果你用大漠插件,会有好几种绑定模式,你得一个一个测试,哪种模式能用。游戏内的对话框都是3D引擎生成的,无法用句柄访问。
但我给这个游戏窗口的其他地方发鼠标消息就都能点到,对子窗口内的位置发就不行,一直没搞懂原理。之前就怀疑是不是这个点击得发到子窗口上才行。
@Keo 试了一下按键精灵+大漠,确实如此。可能需要收费大漠才可以实现
大佬,受你影响,我也想改用python来写了,不过你推荐的 pymem和 pywinauto两个库,我装完后发现在百度上找不到相应的文档可以了解他们封装了些啥。你这有链接分享一下么?
@Keo 百度肯定没有的,说明文档在https://pywinauto.readthedocs.io,https://pymem.readthedocs.io
投降!!跟隨您的文章嘗試了這些天,依然沒能弄懂。
N年前其他大大發出的精靈腳本《遍歷周圍xxx》《人物持有商品》...等的文章現在我都沒能再找著了。
請問大大您有備份這些大大的文章嗎?我需要再找來看看,看能否幫我提昇思維。或者您願意提供您目前使用中的相關查找人物持有商品代碼給予參考呢?
對於基址的部份,我目前還沒碰到大改版,不確定基址如何確定,我目前只是使用ce所能搜出的最前端的地址,並手動加減其他地址來做為基址使用@@,請問能否說說您在沒有大改版情況下,如何定位遊戲機址嗎?而目前今年的版本基址是否能提供參考?
感謝。
@winzxc 之前的文章已经没有了。因为游戏自己的算法改了,所以之前的文章已经没有用处了。如果算法没改,我也没必要重新分析啊。基址之前我就回答过你了:基址本身是没法用CE找的,因为他是一个固定值,没有变化CE是搜不出来的。需要用调用基址的代码逆向查找基址,我文章里就用了当前场景人物数量这个简单的数据逆向查找。
對不起,wking大大您誤會我的問題。
1.首先,我說明我目前自己設定的基址來源(我知道這個方法是錯誤的,但我沒有其他方式能定位出基址,因此採用一個錯誤的方法來暫定一個通用的內址)。也就是說,我沒辦法拿我自己定位的基址來協助我依照您的文章步驟來完整操作下去,才會詢問是否能針對遊戲機址定位方法做個教學。
2.我知道之前的各種高手文章應該都消失了,會提出詢問是否有之前高手們的文章網址,也是最後的希望。緊接著,根據您的文章內容,我也知道之前的算法已經無效,但畢竟我不是計算機本科,因此我不會懂這些編程的步驟,我只能依靠以前我依賴過的文章內的步驟,再次一步一步將記憶找回並協助我重新編寫。
3.本篇您的文章中,開頭提到”0119E630”這個基址,我並無法確定這個地址的由來過程,因此才會請您是否能詳述該基址的定址方式,否則我也無法再依靠您的文章將您所想教學的內容步驟走完,而文章最後您提到:(直接从对象基址0119E630读取链表地址),這裡我也完全跟不上,也是因為我現在的版本這個基址應該也變動了,且就算基址還是一樣,我還是不知道如何找出該地址。
我主要是完全不懂計算機編程的玩家,機械本科當年也不過念過自動化一科,因此,過往當我依照各位高手的文章學習並依照步驟操作時,總是會碰到某某基址該如何定位(高手們大都文章中也沒說明),我只能期待在遊戲版本與文章版本一樣下,讓我能使用文章內的基址去讓我依照文章步驟繼續下去,一旦版本不同,我就走不下了。
我知道我的提問,有時候會看起來像是在討要這些關鍵,但這不是我的本意義,我也是想要完全學會各位高手想教學的內容,但往往搜索到文章時,大都版本不同了,這點我是也很無力。
我也知道我的提問,大都時候看起來很無理頭,主要也是我非熟悉編程的人員,在學習過程幾乎都跳躍式(這點我也很無力,因為高手門的文章大都會簡略某些步驟,但對我來說,該步驟我只要沒辦法猜出來,我又斷掉學習前進的機會了。)
這陣子,在跑遊戲的陸戰內容,想做個面板來顯示陸戰人物的狀態。
請問您知道人物的武器攻擊距離?或是人物戰鬥的攻擊距離?這類的內址嗎?
最後,依然感謝您耐心回復。謝謝。
對不起,嘗試了好幾個星期,依然無法成功單從ce內直接搜出疲勞度的基址。
這幾天嘗試改搜索船隻耐久,也是無法搜出。
您對於”船隻耐久”、”人物疲勞度”這兩個基址,如果能單從ce直接搜出的話,能否說說這兩個基址的搜索方法呢?謝謝。
再請教一個問題,我記得以前在ce 5.x版或是更早之前,可以直接在ce內手動增加地址時,使用指針的方式來設定。
但現在ce7.x版怎麼我設定為指針時,其地址欄內統一變成原設定地址內的數值在加上偏移量???怎麼不是由設定地址在去加減偏移量?
@winzxc 没有用过你说的表示方法。CE的指针是多行表示法,每行只需要填偏移量就可以?
之前你10月15日问的问题,我真的无法回答你。汇编本来就是最难的编程语言,你在不懂普通编程的情况下,想追踪游戏,真的太难了。
再請問一個問題,關於疲勞度的搜索,您之前提到如果疲勞度為28,則應該是搜索280。
但在此前提下,我到目前依然無法成功使用ce成功搜索並定位出正確地只,找到的都是無關數值及內址。
請問您是靠分析出疲勞度的內址?還是一樣由ce內直接搜索出地址的呢?
@winzxc 我刚才试了一下,饅頭说的是对的,疲劳度在内存里是三位数表示法,我之前说的搜不到。
然后用两种方法可以方便的找到疲劳地址。
方法一:按经验,疲劳度和行动力地址应该是相近的。为什么可以这么说?因为游戏里行动力疲劳度是在一起显示的,那从编程的角度,行动力疲劳度这几个变量,编程时一般都是一起定义的变量。行动力地址好找,找到行动力地址后,CE浏览行动力地址内存,切换显示类型为4字节DEC。然后让疲劳度变化的同时观察内存,很明显的就可以找到疲劳度内存11B9190。
方法二:如果说方法一靠经验,那先让游戏的疲劳度为0,再用CE搜索0(精确数值4字节)。无粮无水去海上飘,天数+1后,疲劳度增加,CE搜索“增加的数值”。在天数再次增加之前,CE继续搜索“未变动的数值”。等天数增加了,CE搜索“增加的数值”,在天数再次增加之前,CE继续搜索“未变动的数值”。如此反复,我搜到第3天,CE就搜出来疲劳度内存了。最后吃个料理确认一下地址无误即可。
@winzxc
分享一下疲勞度的問題。
GVO顯示的疲勞值是2位數,在內址顯示的是3位數
我印象轉換的方式是最後一位四捨五入(貌似)/10
比如說GVO疲勞度是28,在內址的數值應為275~284
希望對你有幫助~
@饅頭
感謝。
@wking
感謝。
我目前好奇的地方在於,以我以前的經驗,使用的是您說的方法二即可以定位出來。但這次我在套用您的多開解除方式之後的bin檔案,我就好幾個以前我能輕鬆定位出來的數值,這次我都無法能準確找出來。
除了疲勞度之外,船隻耐久這個基址我也無法找出來,不是定位出來的位址顯示在其他記憶體區塊內,就是在該bin檔的記憶體區間內完全都沒有。
我現在是想不透為什麼會這樣。
兩位提供的疲勞度搜尋定位方式,我現在再搜看看。
我現在搜了一會疲勞度的基址,似乎還是一樣,就算以3位數下去搜索,且使用區間範圍搜索,目前定位出來的內旨在ce中持續觀察也並沒有跟隨遊戲人物的疲勞度變動@@。
而我對照原始的bin檔案內我目前所常用的bin檔內的記憶體位址,我發現會有飄移問題。而且幾個段落之間的飄移量又不一樣大小。這讓我很頭痛。
舉例來說,我直接搜索原始的bin檔案,遊戲內的tab選擇後的目標數字id的基址與人物行動力的基址其間距數值。
對照,我正在使用的可直接超過3開的bin檔案,其兩個基址間的間距大小並不同。
因此,當wking大大上面提示的各種基址,我實際上也都無法直接參照最為我是否操作正確的依據。
舉例來說,wking大大說的疲勞度基址:11B9190
我直接放入我的ce中,該基址顯示確是0,且不會變動。@@
這部份之所以困擾我如此嚴重,是因為早些年,我就算手動產生能超過3開的bin檔案後,其ce內所搜索出來的各種基址我記得原始檔與自改檔都是一樣的。
但這次在使用ce7.x版後,確第一次出現這種兩個檔案之間的基址會無法互相參照,且重點的基址間的間距大小居然不一樣。
而我嘗試拿3.5x版本前來搜索基址,發現似乎也一樣的問題,且我在3.7x的ce內搜索不到的基址,再3.5x的ce內也搜索不到。
我重頭思考起,我有在懷疑,當初製作能多開的bin檔案時,是否使用的編成軟件不同,因此造成我現在能搜索並定位出來的基址與各位大大們不一樣。
但,理論上這樣應該是無法正確進行遊戲才對,怎麼進行遊戲時,確又不曾出現報錯。
@winzxc 跟CE版本没关系,是否多开档案也没关系,和你操作系统有关系。你可以用虚拟机装个XP系统,在XP系统里运行航海和CE试试
更正,我上方提到的”ce 3.7x版本”,應該是”ce 7.4版本”
終於查找到了我用的bin檔案的疲勞度基址:11B4170
我想應該跟大大你們的疲勞度基址不同?
@winzxc 你这个地址是12011版本的主程序疲劳度地址。你怎么会用这么老旧的主程序?
@wking,我也不知道為什麼???我在找個時間直接重新做一次多開的bin檔案看看。但遊戲能進行這也是讓我很訝異!!!!
再請教個問題,有什麼方法能搜索出正在使用中的三個技能的基址嗎?(顯示在右下方地圖上的三個技能資訊)
@winzxc 跟搜疲劳度原理一样,把使用中的技能视为一个未知数。没使用时是0,使用时未知,不停来回切换搜索
@wking
好的,我試試看,感謝。
and 我看了一下我遊戲資料夾內的bin檔案日期是9/15號最後更新,請問大大們您的這個檔案也是這個最後更新日期嗎?
@winzxc 肯定不是啊,12016版本前两周才更新的。
@wking
這就奇怪了,今天我看原始啟動檔有更新,但沒看到該檔案的更新提示,而我進入遊戲資料夾內此"GVOnline.bin"檔案日期是9/19(上面提問打的日期我給打錯了@@),因此您的此檔案修改日期是否也是9/19日?
另外,上線搜索了快40分鐘,關於海上運行的三個技能相關內址搜索,依然都是不相關的部份,請問您有我這個版本這三個執行中技能的相關內址的資料嗎?我想直接輸入看看其變化,看看能否找出為什麼我無法輕易搜索並定位出來。或是目前最新版本的相關內址資料也行,我用最新版本跑看看都是顯示什麼變化。
@winzxc 你不要看修改日期,看程序属性里的版本信息。
技能地址我没找过,好像是需要多层偏移
@wking
好的,感谢。
那能否给我几个有用ce搜索出來的地址給我做核對?
版本号码的基址我这次也没有能搜索定位出来,也不知道怎么确定用来跑游戏的档案是否为最新版本。
还是说不管用哪个档案进行游戏,其登入前,在输入帐号密码前的那个讯息是正确对用开启游戏档案的版本号码?
@winzxc 你运行GVOnline.exe,右上角就是版本信息,再用gvoline.bin做多开。或者你直接下载我做好的多开也行。
@wking
好的,感謝。
我還是希望能自己手動,畢竟還是想要能學會這始終無法學明白的逆向工程技術。
@wking
想請教x64dbg的實際追蹤的執行步驟
我在x64dbg載入遊戲執行檔案後,該如何實時追蹤運行中的檔案?
我嚇了F9執行快速鍵後,X64dbg追蹤到遊戲畫面跳出後就失去追蹤畫面,x64dbg畫面不在顯示運行位置。
我記得早些年嘗試用軟件繞過np的時候(不確定是不是x64dbg,但印象中是類似的軟體畫面),下過F9後,就算遊戲畫面開始出現,其追蹤並不會失效,軟件會跳到該執行的位置。
也常試過F7、F8跟自動的F7、F8,這些步進指令,但都是在遊戲畫面出現後就不再顯示運行位置。
請問,我錯哪了?或是正確的追蹤步驟應該是如何?
謝。
@winzxc 步骤没有错,x64dbg下断点,游戏触发断点,x64dbg就达到运行位置了。我强烈建议你先看看youtube上的x64dbg初级教程。
@wking
感謝。
這也是我碰到的嚴重問題之一,我這邊已經很少有在碰這類軟件的網路資源分享人,我能搜到的yt上相關影片,目前都是用一個很簡單的hollo.......的解密檔案做教學,但我實際跟隨操作用在遊戲的操作上則是完全沒幫助。
而您那邊的教學文章,我看下來大都卡在缺乏詳細步驟(也就是略過太多實際細節,以您說的下斷點來舉例,是單純下斷點?(我試過x64dbg完全沒有任何動靜,下指令的斷點?如何判斷垓下什麼指令或關鍵字來作為斷點,目前我看過的文章都沒有提到如何判斷,這裡在舉個例子,我嘗試依照某個教學,在載入遊戲啟動檔案後,進入”符號”內尋找關鍵模組,然後....因為文章主人沒有說明為什麼會以他說的那個關鍵字來做為下斷點的判斷,我在進入”符號”這頁面後,又卡死了,我實在不知道目前看到的”符號”內那些資訊哪些事能用來作為下斷點的判斷使用,也常試過把”符號”頁面內的那些關鍵字符作為下斷點的判斷指令,也完全沒有幫助(沒有任何動靜)。
我現在碰到最嚴重的問題在於,如果不下斷點,為什麼在啟動遊戲後,當遊戲視窗出現後x64dbg會停止任何訊息更新(也就是原本遊戲視窗還沒否有出現前,還會有高量符號一步一步提示運行到何處,遊是視窗出現後就沒了???〉我記得以前用國外的那套軟件,在繞過np後,當遊戲視窗出現後該軟件的步進追蹤依然會顯示運行到何處,現在我也忘了當年用的軟件怎麼找回來,在只能用x64dbg的情況下,我實在卡死在遊戲視窗出現後,x64dbg就不再提示運行步進位置。)
@winzxc 跟软件没关系,这只是是否开启入口断点的区别。olly和x64dbg都有这个功能。我觉得你在谷歌需要用简体字搜索。破解这块大陆研究的比较多,港台好像少。
補充一個yt的問題。
我在yt改了新的大數據推薦後,我就很難在刷到我想搜尋的重要資訊了。
這也是我現在很頭痛的狀況之一。
我現在要在yt搜索列上打上想要查找的關鍵之前,得要先上google搜索到有yt網址且是我需要的網頁後,並要打開最少3個不同且有我需要的相關影片後,才有辦法在yt搜索列上透過搜索關鍵字詞才會出現我要的相關影片,但只要那一次頁面內沒有我有用的影片,我再來就不會再有機會輕易搜索出我需要的訊息影片。
對了,目前已我搜索到yt上尚有幫助的是up主”赵庆明”所製作的教學影片,但也還沒看到對我有幫助的部份。
根據該up的教學內容,當年應該是用ollyDbg這一套,但既然您用的是x64dbg做教學,我也就先繼續嘗試使用x64dbg。
行动力,+10为疲劳度内存,+4为最大行动力
疲劳度,看到的数值是内存数值大约除10(2字节),疲劳度28内存值280