吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 1797|回复: 15
收起左侧

[原创] Nobody(大多数)游戏修改学习笔记--第三弹

  [复制链接]
huchen 发表于 2026-6-16 10:09
本帖最后由 huchen 于 2026-6-22 01:11 编辑

2026/6/12更新

上一帖直达:Nobody(大多数)游戏修改学习笔记--第二弹

完整代码:已于2026/6/22更新
main.rar (6.92 KB, 下载次数: 0)


(txt文件传不了,那就传压缩包吧)

更新目录

  1. 体质、智力、敏捷、心智等级修改
  2. 好感度
  3. 时间与流速

!!!(求助:目前游戏速度的倍率还是没有解决,如果有思路或方法的大佬们麻烦请告知我,要崩溃了T_T)!!!

属性等级只要想通了就非常简单,他就非常简单,原本以为他每次升级都会重置那个进度条,结果什么都搜索不到,看到风大大的修改器说需要6000左右才能升满,我怀疑会不会要加上前面的,然后就成功了,成功找到,追踪过程中发现就是PlayerConf类的+0x30、+0x34、+0x38、+0x3C偏移,这不就简单多了吗,然后就可以编写代码了,想的是编辑,并不是无限

好感度的话我想的是开始按照常规来搜索,由于有多个人物的好感度所以我认为单独去编辑每个人物的好感度有点麻烦和困难,因此就在要加上好感度的地方hook

;PlayerDataSystem.SetNPCPersionShip

GameAssembly.dll+37E7A1 | 0F85 C5000000                 jne GameAssembly.dll+37E86C

GameAssembly.dll+37E7A7 | 01 69 20                                add [rcx+20], ebp        ;此为改写好感度的代码

GameAssembly.dll+37E7AA | 48 8B 47 10                        mov rax, [rdi+10]

GameAssembly.dll+37E7AE | 48 85 C0                                test rax, rax

GameAssembly.dll+37E7B1 | 0F84 A5020000                        je GameAssembly.dll+37EA5C

GameAssembly.dll+37E7B7 | 48 8B 80 30020000                mov rax, [rax+00000230]

接下来是代码实现

static BYTE SetNPCersionShipOrigCode[7];
LPVOID HookSetNPCPersionShip(HANDLE hprocess, LPVOID base_address)
{
        static UINT64 set_npc_persion_ship_offset = 0x37E7A7; // 替换为实际偏移
        LPVOID hook_addr = (LPVOID)((uintptr_t)base_address + set_npc_persion_ship_offset);

        DWORD oldProtect;

        LPVOID desired = (LPVOID)(((uintptr_t)hook_addr - 0x10000000) & ~0xFFFF);  // 向下约 256MB 对齐
        MyHook = VirtualAllocEx(hprocess, desired, 0x1000, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
        if (MyHook == NULL)
        {
                printf("分配内存失败\n");
                return NULL;
        }

        BYTE hook_code[17] = {

                0xBD, 0x22, 0x05, 0x00, 0x00,        // mov ebp, 0x522
                0x01, 0x69, 0x20,                                // add [rcx+0x20], ebp
                0x48, 0x8B, 0x47, 0x10,                        // mov rax,[rdi+10]
        };
        CalcJMPOffset((LPVOID)((uintptr_t)MyHook + 12),   // JMP 指令所在远程地址
                (LPVOID)((uintptr_t)hook_addr + 5), // 跳回目标远程地址
                hook_code, 12);

        if (!WriteProcessMemory(hprocess, MyHook, hook_code, sizeof(hook_code), NULL))
        {
                printf("写入Hook代码失败\n");
                return NULL;
        }

        //修改内存保护以允许写入
        if (!VirtualProtectEx(hprocess, (LPVOID)hook_addr, 5,
                PAGE_EXECUTE_READWRITE, &oldProtect)) {
                printf("VirtualProtectEx 失败,错误码: %d\n", GetLastError());
                VirtualFreeEx(hprocess, MyHook, 0, MEM_RELEASE);
                return NULL;
        }
        //备份原始代码
        if (!ReadProcessMemory(hprocess, hook_addr, SetNPCersionShipOrigCode, sizeof(SetNPCersionShipOrigCode), NULL))
        {
                printf("读取原始代码失败\n");
                return NULL;
        }

        BYTE nopcode[5] = { 0x90, 0x90, 0x90, 0x90, 0x90 }; // nop指令
        BYTE jmpcode[5] = { 0xE9, 0, 0, 0, 0 }; // jmp指令
        //计算相对跳转地址
        uintptr_t offset = (uintptr_t)MyHook - (uintptr_t)hook_addr - 5;
        *(int32_t*)(jmpcode + 1) = offset;
        if (!WriteProcessMemory(hprocess, hook_addr, jmpcode, sizeof(jmpcode), NULL))
        {
                printf("写入Hook代码失败\n");
                return NULL;
        }
        if (sizeof(SetNPCersionShipOrigCode) / sizeof(BYTE) > 5)
        {
                if (!WriteProcessMemory(hprocess, (LPVOID)((uintptr_t)hook_addr + 5), nopcode, sizeof(SetNPCersionShipOrigCode) / sizeof(BYTE) - 5, NULL))
                {
                        printf("写入NOP代码失败\n");
                        return NULL;
                }
        }
        VirtualProtectEx(hprocess, hook_addr, sizeof(SetNPCersionShipOrigCode), oldProtect, &oldProtect);

        return hook_addr;
}

我觉得只要理解了以前的代码,这个代码就不难理解,只是这里hook的代码只有3个,所以需要向下要,所以就把后面代码也用上了,因此就超过了jmp所需的5字节,所以需要将多余字节给nop掉

接下来就是卸载hook了,也是一样的逻辑

BOOL UnHookSetNPCPersionShip(HANDLE hprocess, LPVOID hook_addr)
{
        if (!MyHook) {
                printf("尚未安装 Hook\n");
                return FALSE;
        }
        // 1. 修改保护为可写
        DWORD oldProtect;
        if (!VirtualProtectEx(hprocess, (LPVOID)hook_addr, 7,
                PAGE_EXECUTE_READWRITE, &oldProtect)) {
                printf("取消 Hook 失败:VirtualProtectEx 错误 %d\n", GetLastError());
                return FALSE;
        }
        // 2. 写回原始 7 字节
        if (!WriteProcessMemory(hprocess, (LPVOID)hook_addr, SetNPCersionShipOrigCode, 7, NULL)) {
                printf("取消 Hook 失败:写入原始代码错误\n");
                // 尝试恢复保护
                VirtualProtectEx(hprocess, (LPVOID)hook_addr, 7, oldProtect, NULL);
                return FALSE;
        }
        // 3. 恢复原保护
        DWORD temp;
        VirtualProtectEx(hprocess, (LPVOID)hook_addr, 7, oldProtect, &temp);
        // 4. 释放 shellcode 内存
        VirtualFreeEx(hprocess, MyHook, 0, MEM_RELEASE);
        MyHook = NULL;
        printf("Hook 卸载成功\n");
        return TRUE;
}

这是一种hook方法,还有一种就是全局都修改成满好感度,这样的多半要人物的控制中心或者面板来操控,所以在搜索栏输入character,可以看到

image-20260614163720230.png

篮筐就是人物的面板了

在这个类里你会看到UpdatePageDate函数,这个函数就有那些人物的好感度,我怎么知道的,首先可以先在CE找一个好感度的地址,然后选择查询访问该地址,随后在人物面板切换到你对应的那一页,随后就会出现一条访问记录

image-20260614172602251.png

选中的这个就是我们想要的函数

随后就是hook代码了,这个有了先前的经验就很简单了

BYTE OriginalNPCUpdatePageDateCode[5];
LPVOID HookNPCUpdtaePageDate(HANDLE hprocess, LPVOID base_address)
{
        static UINT64 npc_updatepagedate_offset = 0x6994CE;
        LPVOID hook_addr = (LPVOID)((uintptr_t)base_address + npc_updatepagedate_offset);
        DWORD oldProtect;
        LPVOID desired = (LPVOID)(((uintptr_t)hook_addr - 0x10000000) & ~0xFFFF);  // 向下约 256MB 对齐
        MyHook = VirtualAllocEx(hprocess, desired, 0x1000, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
        if (MyHook == NULL)
        {
                printf("分配内存失败\n");
                return NULL;
        }

        BYTE hook_code[17] = {
                0xC7, 0x46, 0x20, 0x22, 0x05, 0x00, 0x00,                // mov [rsi+0x20], 0x522
                0x8B, 0x5E, 0x20,                                                                // mov ebx,[rsi+20]
                0x33, 0xC9                                                                                // xor ecx, ecx
        };

        CalcJMPOffset((LPVOID)((uintptr_t)MyHook + 12),   // JMP 指令所在远程地址
                (LPVOID)((uintptr_t)hook_addr + 5), // 跳回目标远程地址
                hook_code, 12);

        if (!WriteProcessMemory(hprocess, MyHook, hook_code, sizeof(hook_code), NULL))
        {
                printf("写入Hook代码失败\n");
                return NULL;
        }

        //修改内存保护以允许写入
        if (!VirtualProtectEx(hprocess, (LPVOID)hook_addr, 5,
                PAGE_EXECUTE_READWRITE, &oldProtect)) {
                printf("VirtualProtectEx 失败,错误码: %d\n", GetLastError());
                VirtualFreeEx(hprocess, MyHook, 0, MEM_RELEASE);
                return NULL;
        }
        //备份原始代码
        if (!ReadProcessMemory(hprocess, hook_addr, OriginalNPCUpdatePageDateCode, sizeof(OriginalNPCUpdatePageDateCode), NULL))
        {
                printf("读取原始代码失败\n");
                return NULL;
        }
        BYTE nopcode[5] = { 0x90, 0x90, 0x90, 0x90, 0x90 }; // nop指令
        BYTE jmpcode[5] = { 0xE9, 0, 0, 0, 0 }; // jmp指令
        //计算相对跳转地址
        uintptr_t offset = (uintptr_t)MyHook - (uintptr_t)hook_addr - 5;
        *(int32_t*)(jmpcode + 1) = offset;
        if (!WriteProcessMemory(hprocess, hook_addr, jmpcode, sizeof(jmpcode), NULL))
        {
                printf("写入jmp代码失败\n");
                return NULL;
        }
        if (sizeof(OriginalNPCUpdatePageDateCode) / sizeof(BYTE) > 5)
        {
                if (!WriteProcessMemory(hprocess, (LPVOID)((uintptr_t)hook_addr + 5), nopcode, sizeof(OriginalNPCUpdatePageDateCode) / sizeof(BYTE) - 5, NULL))
                {
                        printf("写入NOP代码失败\n");
                        return NULL;
                }
        }
        VirtualProtectEx(hprocess, hook_addr, sizeof(OriginalNPCUpdatePageDateCode), oldProtect, &oldProtect);

        return hook_addr;
}

接下来是卸载hook代码

BOOL UnHookNPCUpdatePageDate(HANDLE hprocess, LPVOID hook_addr)
{
        if (!MyHook) {
                printf("尚未安装 Hook\n");
                return FALSE;
        }
        // 1. 修改保护为可写
        DWORD oldProtect;
        if (!VirtualProtectEx(hprocess, (LPVOID)hook_addr, 5,
                PAGE_EXECUTE_READWRITE, &oldProtect)) {
                printf("取消 Hook 失败:VirtualProtectEx 错误 %d\n", GetLastError());
                return FALSE;
        }
        // 2. 写回原始 5 字节
        if (!WriteProcessMemory(hprocess, (LPVOID)hook_addr, OriginalNPCUpdatePageDateCode, 5, NULL)) {
                printf("取消 Hook 失败:写入原始代码错误\n");
                // 尝试恢复保护
                VirtualProtectEx(hprocess, (LPVOID)hook_addr, 5, oldProtect, NULL);
                return FALSE;
        }
        // 3. 恢复原保护
        DWORD temp;
        VirtualProtectEx(hprocess, (LPVOID)hook_addr, 5, oldProtect, &temp);
        // 4. 释放 shellcode 内存
        VirtualFreeEx(hprocess, MyHook, 0, MEM_RELEASE);
        MyHook = NULL;
        printf("Hook 卸载成功\n");
        return TRUE;
}

效果图

image-20260614204345665.png

接下来就是时间相关的修改了,这里我主要修改了三个地方,增加、减少一小时和控制时间流速

对于时间,我肯定想的是time,于是去搜索time发现

image-20260616002032811.png

并且发现字段0x10偏移是PlayerConf类,于是找到准确地址,往下看发现在PlayerConf里的0x80和0x84偏移是秒和小时的字段,于是分别去查找写入地址,秒的结果是

image-20260616002505531.png

并且往上翻是FrameWork.GameTimeManager.TimeCallback函数里的所以这就是控制流速的代码,更改3C(60)的值,从而改变流速

PS:现实中的1秒到2秒左右,游戏时间的分钟会加一,因此可以说现实的一秒钟就是游戏的一分钟,而一分钟又等于60秒,所以这里3C也是60秒的意思

然后是小时的结果

image-20260616002720384.png

往上跟踪是MainBottomDetail.UpdateTime函数里的,原本的想法是修改r14的值发现不一会就修正了,于是向上跟踪发现

image-20260616003201003.png

这里的代码就是用秒数来计算hour的,问了GPT说这里的效果就是除以3600,这里只是编译器的优化,提升计算性能可改变的,称为用“乘法近似替代除法”,感兴趣的可以搜搜,这里不过多解释,然后我修改这里的ebx发现也不行。

于是搜索这个函数,发现需要两个参数

image-20260616003653249.png

一个是秒数,一个是下午标志,然后我的想法就是更改这个秒数,调试到这个参数的位置

image-20260616004245617.png

然后就是hook代码了

这里我要说明一下,由于这是UpdateTime函数,说明这个函数调用是有周期的,所以我在函数中加了sleep函数,来停止3秒进行修改小时的,其它的部分跟以前的大差不差,很好理解,我就不贴了,具体代码请见完整代码

2026/6/15更新

更新目录

  1. 速度倍率的修改
  2. 工作熟练度

声明:该速率修改只在可以奔跑的状态下生效,即饱腹度要高,至少不会低到锁奔跑状态,至于被锁了还能加速的修改,我目前还在研究,如果有思路和方法的大佬还请告知一二。(依旧求助T_T)

在之前的修改中,一直找不到速度倍率,在今天想了想,在CheckSpeedBuff函数中我已经找到了相关速度的加成计算,不应该不会出现速度啊,结果我自己亲自去跟踪,才明白,还是不能太相信GPT

image-20260620074506174.png

这部分就是计算速度的代码,我这里修改的是+76A443地址,修改xmm2里的值,就会让速度快起来,然后在上方,我感觉像一个初始速度,赋给了xmm7寄存器

image-20260620074842545.png

这里固定为0.04,所以这里也很容易找到指针链,然后我尝试按照倍率去修改这个初始值,发现效果并不是很明显,所以还是决定修改xmm2

接下来是工作熟练度的修改

这个就很容易找到了,只不过需要废些时间,他的等级阶梯跟技能点是一样的,是一个总量,然后分成15、20、25、30……共10个等级,从30开始不变,算下来总共才240就就可以满级了

找到地址后,查询写入地址,找到代码,往上翻发现是PlayerDtaSystem.AddSmallGameExp函数

image-20260621202756868.png

发现这个偏移就是存放熟练度的地址,这里的rbx*4就是不同的工作所优化的动态偏移,比如当搬砖时,这里的rbx为0,当小区保安时为2,当物流分拣时为6,这个可以自行去了解,可以演变成自编辑熟练度,不过对于修改器来说,变成满级比编辑数值来的方便也合理

然后我的想法是修改这里的xmm0寄存器,然后就能每次工作完后直接加满

;修改代码(hook代码)
mov ecx,100
movd xmm0,ecx
cvtdq2ps xmm0,xmm0

跳转到上面的代码,然后在跳回来,具体代码见完整代码

然后我又研究了下,这个代码是有参数,而第一个参数是那个工作,第二个参数是加多少经验值,第三个参数是否为自动工作

image-20260622001754114.png

image-20260622002002980.png

这是通过自动工作定位到的代码,为什么还有多的rcx的运算,因为在Uinty游戏中,他会默认往里传他这个类的地址,并且是第一个传的,所以有些空参数的函数在调用前也会mov rcx,xxx等

因此这里也可以多r8d动手脚,但是这里只针对自动,所以并不是全覆盖。

然后我也看了手动的,咋一看有点懵,仔细研究下发现mov没有用,rcx在前面就已经赋予好了,要修改的话还是得改r8d

image-20260622001849512.png

因此还是决定修改函数内部

具体的看代码部分吧

免费评分

参与人数 2吾爱币 +1 热心值 +2 收起 理由
社会主義パンク + 1 + 1 我很赞同!
52pojieplayer + 1 谢谢@Thanks!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

pwdobwq 发表于 2026-6-19 12:16
huchen 发表于 2026-6-18 13:50
我这边主要做的是技术分享,后续的话可以考虑

游戏下架前搞了离线,还没玩,只是想休闲体验下制作组所谓短期赚钱渠道都是什么,并不想玩所谓生存挑战,期待你的作品
xiete 发表于 2026-6-16 14:18
gofeel2024 发表于 2026-6-16 15:49
 楼主| huchen 发表于 2026-6-16 18:22
gofeel2024 发表于 2026-6-16 15:49
楼主 有资源吗?新版本很容易闪退

是游戏还是修改程序
willgoon 发表于 2026-6-17 09:02
这是什么游戏?
社会主義パンク 发表于 2026-6-17 11:05
CE的变速器不能用吗
gofeel2024 发表于 2026-6-17 11:31
huchen 发表于 2026-6-16 18:22
是游戏还是修改程序

游戏,嘿嘿,我以前有个1.0几的不怎么崩
 楼主| huchen 发表于 2026-6-17 11:50

CE的变速是加快整个游戏的时间,速度加快的同时,时间也被加快,不是我想要的
 楼主| huchen 发表于 2026-6-17 11:51
gofeel2024 发表于 2026-6-17 11:31
游戏,嘿嘿,我以前有个1.0几的不怎么崩

可能我的程序优化不是怎么好,哈哈
 楼主| huchen 发表于 2026-6-17 21:59
gofeel2024 发表于 2026-6-17 11:31
游戏,嘿嘿,我以前有个1.0几的不怎么崩

你要资源的话你看我第一个帖子里的评论区里有,我发过链接解压密码是1234
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-7-10 19:18

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表