给 ppSR 私服(ppsr.exe,Honkai: Star Rail 4.4.5X)游戏左下角水印打运行时补丁的工具:把默认的 ppSR / discord.gg/ppSR 水印替换成自定义的两行文字,支持整行配色和逐字配色。
不修改 ppsr.exe 文件本身 —— 每次启动服务器时自动注入,exe 保持原样。
有打包好的 ppsr_watermark_patcher.exe(配置 ppsr_watermark.json ,改它即可):
- 编辑
ppsr_watermark.json(两行水印文字 + 颜色) - 双击
ppsr_watermark_patcher.exe,UAC 弹窗点"是" - 服务器启动,游戏客户端连上来,左下角水印就是你的文字
配置必须与 exe 放在同一目录(exe 只认自己旁边的
ppsr_watermark.json,缺失会自动生成默认配置)。 命令行用法同 python 版:--attach/--dry-run。
# 1. 编辑配置(两行水印文字 + 颜色)
# 同目录下 ppsr_watermark.json
# 2. 用补丁器启动服务器(会弹出 UAC 提权确认,点"是")
python ppsr_watermark_patcher.py| 命令 | 说明 |
|---|---|
python ppsr_watermark_patcher.py |
启动 ppsr.exe + 打水印补丁(推荐日常使用) |
python ppsr_watermark_patcher.py --attach |
ppsr.exe 已在运行,只打补丁 |
python ppsr_watermark_patcher.py --dry-run |
离线自检:验证配置能否装入槽位,不启动服务器 |
需要管理员权限(ppsr.exe 本身要求提权,补丁器要写入它的进程内存)。 直接双击运行或从普通 CMD 运行都会自动弹 UAC 请求提权。
pip install pyinstaller
pyinstaller --onefile --console --name ppsr_watermark_patcher ppsr_watermark_patcher.py
# 产物在 dist\ppsr_watermark_patcher.exe,连同 ppsr_watermark.json 一起拷贝使用| 写法 | 效果 | 占用容量 |
|---|---|---|
"color": "#FFD700" |
整行统一颜色,一个 <color> 包裹 |
小(约 6 字节/字) |
"color": ["#FE9A9A", "#D990B0", ...] |
逐字着色,每字一个 <color> |
大(约 19 字节/字) |
水印 Lua 在 ppsr.dll 内存里是定长槽位,新内容(两行文字的富文本标记总字节数)不能超过:
| 槽位 | base64 长度 | 解码后上限 |
|---|---|---|
| Lua1(旧版水印) | 1732 字符 | 1299 字节 |
| Lua2(新版水印) | 2168 字符 | 1626 字节 |
超限会明确报错,提示缩短文字或减少逐字配色。参考容量:整行单色约 6 字节/字,两行合计可用 1000+ 字节,正常使用绰绰有余;逐字配色每字约 19 字节,两行合计约 80 字以内。
注:逐字配色时若与整行单色混用(line1 单色、line2 逐字),按各自写法分别计算。
- 结构:
ppsr.exe是定制版 PyInstaller onefile(载荷加密,无法直接改文件)。运行时解压到%TEMP%\onefile_<pid>_*,实际服务器本体是ppsr.dll—— Nuitka 编译的 Python(FreeSR 改版),子进程将其加载进内存。 - 水印机制:服务器在玩家每次心跳(
PlayerHeartBeatCsReq)时经ClientDownloadDataScNotify推送一段 base64 编码的 Lua 给游戏客户端,客户端 LuaShell 执行:beta_text():把UIRoot/AboveDialog/BetaHintDialog(Clone)移到左下角(-10, 10, 0),文字改为水印第一行;version_text():把VersionText改为水印第二行;mhy_text():隐藏官方提示文字。
- 补丁方式:水印 base64 以明文存在 ppsr.dll 的
.rdata字符串表中(Nuitka 条目:标记字节 + base64 + NUL 分隔)。补丁器按特征前缀bG9jYWwgZnVuY3Rpb24gYmV0YV90ZXh0(即local function beta_text)在内存中定位两个槽位,把新 Lua 重新 base64 编码后等长原位写回(长度不足用=填充,Python 的 base64 解码容忍尾部填充),写入后读回验证。
ppsr_watermark_patcher.py
├─ 1. 自提权(UAC)
├─ 2. 启动 ppsr.exe
├─ 3. 轮询找到加载了 ppsr.dll 的子进程(Toolhelp 快照)
├─ 4. 读取 .rdata → 扫描水印 base64 槽位(2 个)
├─ 5. 配置 → 生成新 Lua → base64 等长写回(WriteProcessMemory)
└─ 6. 读回解码验证,打印补丁后的水印文字
Q:提示"新水印 Lua 太大"?
缩短 line1/line2 的文字,或把逐字配色 color 数组改成整行单色写法。
Q:游戏里水印没变?
- 确认是用
ppsr_watermark_patcher.py启动的服务器(而不是直接运行ppsr.exe); - 确认补丁输出显示两个
[OK]且读回的文字是你配置的内容; - 游戏客户端需要重新连接(水印随心跳下发,连上后下一次心跳生效);
- 如果之前直接跑过
ppsr.exe,先结束所有 ppsr 进程再用补丁器启动。
Q:弹 UAC 提示是正常的吗? 正常。ppsr.exe 本身要求管理员权限,补丁器也需要同样的权限写入其进程内存。
Q:会修改 ppsr.exe 文件吗? 不会。只写入运行中进程的内存,exe 与 ppsr.dll 文件均保持原样;重启后补丁需要重新打(补丁器每次启动自动完成)。
Q:换了新版本/新客户端后还能用吗?
补丁器按特征前缀扫描定位,不依赖固定偏移,只要水印 Lua 结构不变(local function beta_text)就能工作。若 ppSR 大版本更新改变了水印实现,需重新适配。
Q:--dry-run 显示没有 ppsr.dll?
离线自检会读取 misc\_dump\ 下运行时提取的 ppsr.dll 样本。该目录是分析时留存的,被清理后 dry-run 只跳过自检;正常启动不受影响。
ppSR_REL_4.4.5X/
├── ppsr.exe # 服务器本体(不动它)
├── ppsr_watermark_patcher.py # 本补丁器
├── ppsr_watermark.json # 水印配置(改这里)
├── README.md # 本文档
└── misc/ # 服务器数据(hotfix.json、freesr-data.json、resources/ 等)
- 外层 exe:MSVC 壳(.text 仅 50KB)+ 14MB 高熵加密载荷,无标准 PyInstaller COOKIE/PYZ 魔数 → 定制 onefile。
- 运行时释放
%TEMP%\onefile_<pid>_*:ppsr.dll(33.8MB,Nuitka 编译,.text 21.5MB + .rdata 11.9MB)、python314.dll、__mypyc.pyd等。 - 进程模型:父进程 = 引导器(负责解压与清理),子进程 = 实际应用(加载 ppsr.dll)。杀掉父进程会让解压目录残留在 %TEMP%。
- 服务器 = FreeSR 改版:Flask dispatch(
/query_dispatch、/query_gateway、账号 API、/srtools)+ KCP 游戏服(game/handlers/*、game/services/*、msg/classes_pb2.py)。 - 启动时有密码校验("Enter the correct password:",key 存于
misc/persistent.json)。 - 水印 Lua 两段完整源码见分析记录(解码产物含
beta_text/version_text/mhy_text三个函数)。
{ // ppsr.exe 的路径(可绝对路径或相对本文件所在目录) "ppsr_exe": "E:\\HSRBetaPS\\ppSR_REL_4.4.5X\\ppsr.exe", // 第一行(主标)—— 替换原 "ppSR" "line1": { "text": "MY SERVER", // 水印文字,任意字符 "size": 18, // 字号(Unity 富文本) "color": "#FFD700" // 整行单色:一个 #RRGGBB }, // 第二行(副标)—— 替换原 "discord.gg/ppSR" "line2": { "text": "qq 12345678", "size": 12, "color": "#9C80D6" // 整行单色写法 // "color": ["#FE9A9A", "#D990B0", "#B586C7"] // 逐字配色写法: // // 颜色数组长度必须与 text 字符数一致 } }