从CE入手,研究女神异闻录5皇家版(P5R)的暴击机制
geleisisisi
2023年02月22日 15:13

前言

最近想要研究下P5R的暴击机制,翻遍全网都没找到统一的说法,可谓是众说纷纭一团乱麻。所以我就想做个研究暴击的专题,但是苦于不会解包。

不过尺有所短寸有所长,最近正好在研究P5R的修改,那干脆用CE,从汇编入手,用笨办法一点点实验出来好了。

本文的主旨意在分享一些寻找切入口的思路,希望能起到抛砖引玉的效果。对汇编不感兴趣的同学可以直接搜索并跳转到“省流”部分,查看结论。


研究问题如下

  1. 暴击的辅助buff:反叛(Rebellion)、革命(Revolution)、勇气步伐(Brave Step)、斗志营养品(Hustle S),具体暴率为多少,是否可以叠加?

  2. 暴击的被动buff:建言(Apt Pupil)、仪式护手-饰品(Gauntlet)、枪弹狂热(Trigger Happy),高压电流(Fortified Moxy),逆境的觉悟(Adverse Resolve),具体暴率为多少,是否可以叠加?

  3. 敌我双方运气对暴击率的影响。

通过以上三点的研究,最终推导出整个暴击率的公式是如何计算的。


研究的过程(以下的地址基于steam1.03版本,所有测试基于Challenge模式


1.显示暴击率的切入口

首先我们需要梳理代码逻辑,找到显示暴击率的切入口。

从常理上说,每当使用可以触发暴击的技能时,程序应该会读取储存是否存在暴击buff的地址数值,如果存在暴击buff,不管是加法还是乘法,总之会使得暴击率上升。

所以储存暴击buff的地址就是一个很好的切入点。

在这里感谢下那位写CT表的外国大佬,节省了我很多时间。

以Mona为测试对象,已知Mona的暴击率buff地址是[P5R.exe+29E940E]。

对该地址下断,查找什么访问了该地址,然后使用一次奇迹拳,发现只有一个地方读取了该地址的值:

代码块
HTML
自动换行
复制代码
P5R.exe+E14BB9 - 44 0FB6 4F 2E         - movzx r9d,byte ptr [rdi+2E]
复制成功

跟过去,发现这里代码是读取了地址,然后进行一些计算和判断(我没仔细看,但用脚趾头想都知道一定是读取暴击buff数据啊,暴击buff是否存在等操作),直到:

代码块
HTML
自动换行
复制代码
P5R.exe+E14BFE - 03 F0                 - add esi,eax
复制成功

这里是一个非常重要的关键点(简称关键点1),因为它基本上确定了,暴击的辅助buff是加法。

接着往下走,直到:

代码块
HTML
自动换行
复制代码
P5R.exe+E14C13 - B8 3C000000           - mov eax,0000003C { 60 }
P5R.exe+E14C18 - 3B F0                 - cmp esi,eax
P5R.exe+E14C1A - 0F4F F0               - cmovg esi,eax
复制成功

这里同样是个很重要的关键点,具体的汇编逻辑就是,计算后的暴击率如果大于60那么取60,如果小于60那么不变。说明暴击率的上限不是网传的70或者67,而是60。

继续往下走,直到:

代码块
HTML
自动换行
复制代码
P5R.exe+E14CD3 - 44 3B C6              - cmp r8d,esi
P5R.exe+E14CD6 - 7D 47                 - jnl P5R.exe+E14D1F
复制成功

中间一群shr mul的运算看的我脑仁疼,我也没有认真去追。

但是我猜啊,如果我是程序员,肯定Random一个100以内的随机数,用该随机数和暴击率比大小,最终决定是否暴击。

所以如果想要写百分百暴击的脚本,直接把jnl nop掉即可成功,即:

代码块
HTML
自动换行
复制代码
P5R.exe+E14CD6:
db 90 90
复制成功

至此,先总结下代码逻辑:

代码块
HTML
自动换行
复制代码
P5R.exe+E14BB9 - 44 0FB6 4F 2E         - movzx r9d,byte ptr [rdi+2E]
//读取暴击buff地址
P5R.exe+E14BFE - 03 F0                 - add esi,eax
//暴击率=暴击基础几率+buff附带的暴击几率
P5R.exe+E14C1A - 0F4F F0               - cmovg esi,eax
//暴击率上限为60
P5R.exe+E14CD3 - 44 3B C6              - cmp r8d,esi
//暴击率和随机数比大小决定是否成功
复制成功

那么关键点1(P5R.exe+E14BFE)

代码块
HTML
自动换行
复制代码
P5R.exe+E14BFE - 03 F0                 - add esi,eax
//暴击率=暴击基础几率+buff附带的暴击几率
复制成功

则是一个很好的切入口,因为可以通过它得知暴击基础几率和buff附带的暴击几率分别为多少。


2.记录数据

找到了显示几率的切入口,那么接下来就是实验记录数据了

先以Mona奇迹拳为例子,反复攻击同一小怪。得出如下数据(0x表示16进制,即0xA=10):

代码块
HTML
自动换行
复制代码
无辅助buff:
0x55+0
反叛(Rebellion):
0x55+0x7
反叛(Rebellion)、勇气步伐(Brave Step)、斗志营养品(Hustle S):
0x55+0x7
反叛(Rebellion)、革命(Revolution)、勇气步伐(Brave Step)、斗志营养品(Hustle S):
0x55+0x16
复制成功

从上述数据可以得出结论1:

  • 反叛(Rebellion)、勇气步伐(Brave Step)、斗志营养品(Hustle S)不可叠加,且暴击率+=7

  • 革命(Revolution)可与上述buff叠加,且暴击率+=15


接下来以Mona至高魔弹为例子,反复攻击同一小怪。得出如下数据:

代码块
HTML
自动换行
复制代码
无辅助buff、无饰品:
0x11+0
无辅助buff、仪式护手-饰品(Gauntlet):
0x22+0
无辅助buff、仪式护手-饰品(Gauntlet)、建言(Apt Pupil):
0x22+0
复制成功

从上述数据可以得出结论2:

  • 仪式护手-饰品(Gauntlet)、建言(Apt Pupil)不可叠加,且暴击率=基础暴击率x2


继续:

代码块
HTML
自动换行
复制代码
无辅助buff、无饰品、枪弹狂热(Trigger Happy):
0x11+0x19
无辅助buff、无饰品、枪弹狂热(Trigger Happy)、建言(Apt Pupil):
0x22+0x19
无饰品、枪弹狂热(Trigger Happy)、建言(Apt Pupil)、反叛(Rebellion)、革命(Revolution):
0x22+0x2F
复制成功

从上述数据可以得出结论3:

  • 枪弹狂热(Trigger Happy)与其余所有buff都可叠加,且暴击率+=25


逆境觉悟和高压电流我就偷懒直接上结论了

至此,我们就可以总结出目前得到的结论了:

  • 暴击率=基础暴击率+buff附带的暴击率,且上限为60。

  • 基础暴击率指的是技能自带的暴击率,外加双方运差的计算,最终得出的几率。

  • 如果带有建言(Apt Pupil)或者仪式护手-饰品(Gauntlet),那么基础暴击率会翻倍。两者择其一即可,不可叠加。

  • 其余所有的暴击算法都是在建言计算后,进行加法,具体为:

    • 枪弹狂热(Trigger Happy):+25

    • 反叛(Rebellion)、勇气步伐(Brave Step)、斗志营养品(Hustle S):+7,且三者互相不可叠加

    • 革命(Revolution):+15

    • 高压电流(Fortified Moxy):+15

    • 逆境的觉悟(Adverse Resolve):+20


3.运差对基础暴击率的影响

当下我们已经得到了buff附带的暴击几率,以及它们之间是否叠加的结论,只剩下研究敌我双方的运气对于暴击率的影响了。

先准备一只Mona,属性如下:

力=20 魔=40 耐=60 速=80 运=50

然后对同一小怪使用奇迹拳,已知该技能命中=75,暴击率=45。

已知Mona敏的地址是[P5R.exe+29E9444],对该地址查找访问,获取读取地址

代码块
HTML
自动换行
复制代码
P5R.exe+E2EE5A - 0FB6 5C 08 1C         - movzx ebx,byte ptr [rax+rcx+1C]
复制成功

及其Return Address

代码块
HTML
自动换行
复制代码
P5R.exe+E40CFA       - 40000000,00000000,41B0D500,00000000,...
P5R.exe+E1B282       - 40E2A6C0,00000000,00000000,00000000,...
P5R.exe+E1BBA8       - 00000000,00000000,000004D4,029DD700,...
P5R.exe+E1C77C       - 000004D4,00000002,000004D4,029DD780,...
P5R.exe+E14A7F       - 00000064,000000CE,00000000,029DD780,...
P5R.exe+E338FB       - 029DD780,FB896ACC,000000CE,00000000,...
P5R.exe+75436A       - 00000000,FB896810,000000CE,00000018,...
P5R.exe+73DCBF       - FB896030,3503FD20,379E9810,00000000,...
P5R.exe+718010       - 00000000,379E9810,00000001,00000000,...
P5R.exe+24E0A0       - 00000000,00000000,00000000,00000000,...
复制成功

分别对返回地址进行追踪,发现

代码块
HTML
自动换行
复制代码
P5R.exe+E40CF5 - E8 26E1FEFF           - call P5R.exe+E2EE20
复制成功

返回rax=0x32=50=己方运

代码块
HTML
自动换行
复制代码
P5R.exe+E1B27D - E8 2E5A0200           - call P5R.exe+E40CB0
复制成功

返回rax=0x32=50=己方运

代码块
HTML
自动换行
复制代码
P5R.exe+E1BBA3 - E8 B8F4FFFF           - call P5R.exe+E1B060
P5R.exe+E1BBA8 - BA 04000000           - mov edx,00000004 { 4 }
P5R.exe+E1BBAD - 0F28 F8               - movaps xmm7,xmm0
P5R.exe+E1BBB0 - 8D 4A 0A              - lea ecx,[rdx+0A]
复制成功

返回xmm0=(float)50.00=己方运,储存在xmm7

不必跳出函数,继续跟踪此段代码

代码块
HTML
自动换行
复制代码
P5R.exe+E1BBB3 - E8 A8F4FFFF           - call P5R.exe+E1B060
P5R.exe+E1BBB8 - 33 D2                 - xor edx,edx
P5R.exe+E1BBBA - 0F28 F0               - movaps xmm6,xmm0
P5R.exe+E1BBBD - 8D 4A 14              - lea ecx,[rdx+14]
复制成功

返回xmm0=(float)36.00=猜测为敌方运,储存在xmm6

代码块
HTML
自动换行
复制代码
P5R.exe+E1BBC0 - E8 9BF4FFFF           - call P5R.exe+E1B060
复制成功

返回xmm0=(float)45.00=猜测为技能暴击率,储存在xmm0

然后往下走

代码块
HTML
自动换行
复制代码
P5R.exe+E1BBC5 - F3 0F58 3D C76CD200   - addss xmm7,[P5R.exe+1B42894] { (50.00) }
P5R.exe+E1BBCD - F3 0F58 35 BF6CD200   - addss xmm6,[P5R.exe+1B42894] { (50.00) }
P5R.exe+E1BBD5 - 66 83 0D 2F86C401 01  - or word ptr [P5R.exe+2A6420C],01 { (0),1 }
P5R.exe+E1BBDD - F3 0F5E FE            - divss xmm7,xmm6
P5R.exe+E1BBE1 - F3 0F59 C7            - mulss xmm0,xmm7
P5R.exe+E1BBE5 - 66 83 FB 02           - cmp bx,02 { 2 }
P5R.exe+E1BBE9 - 75 08                 - jne P5R.exe+E1BBF3
P5R.exe+E1BBEB - F3 0F59 05 9158D200   - mulss xmm0,[P5R.exe+1B41484] { (0.60) }
P5R.exe+E1BBF3 - 0F28 74 24 30         - movaps xmm6,[rsp+30]
P5R.exe+E1BBF8 - 0F28 7C 24 20         - movaps xmm7,[rsp+20]
P5R.exe+E1BBFD - F3 0F2C C0            - cvttss2si eax,xmm0
P5R.exe+E1BC01 - 89 05 F985C401        - mov [P5R.exe+2A64200],eax { (79) }
P5R.exe+E1BC07 - 48 83 C4 40           - add rsp,40 { 64 }
P5R.exe+E1BC0B - 5B                    - pop rbx
P5R.exe+E1BC0C - C3                    - ret 
复制成功

这段代码已经是明示了

(%E5%B7%B1%E6%96%B9%E8%BF%90%2B50)%2F(%E6%95%8C%E6%96%B9%E8%BF%90%2B50)*%E6%8A%80%E8%83%BD%E6%9A%B4%E5%87%BB%E7%8E%87%3D(50%2B50)%2F(36%2B50)*45%3D52.32

向下取整得基础暴击率=52


研究的结论(省流)

  • 最终暴击率=基础暴击率(小数点向下取整)+buff附带的暴击率,且上限为60。

  • 如果带有建言(Apt Pupil)或者仪式护手-饰品(Gauntlet),那么基础暴击率会翻倍。两者择其一即可,不可叠加。

  • 其余所有的暴击算法都是在建言计算完成后,进行加法,具体为:

    • 枪弹狂热(Trigger Happy):+25

    • 反叛(Rebellion)、勇气步伐(Brave Step)、斗志营养品(Hustle S):+7,且三者互相不可叠加

    • 革命(Revolution):+15

    • 高压电流(Fortified Moxy):+15

    • 逆境的觉悟(Adverse Resolve):+20


举例1:

    义经:运=80

    拉雯妲:运=75

    用义经使用八艘跳,那么最大化暴击率的方式是:

    带建言+逆境的觉悟(配合松的生命力)+反叛+革命

    基础暴击率=(80+50)/(75+50)*5=5

    最终暴击率=5*2+20+7+15=52

举例2:

    Mona:运=99

    拉雯妲:运=75

    那么Mona对拉雯妲用奇迹拳,

    基础暴击率=(99+50)/(75+50)*45=53

    也就是说99运的Mona只需要一个反叛暴击buff(+7)即可拉满暴击率,前提是得命中