[ Mugen ] - L灵梦 1.81 使用技术、攻击流程 和 代码解析

L灵梦 0.78我曾经已经讲解过了。

http://v-madoka.lofter.com/post/2fdf1b_6e8511f


今次,我心血来潮,研究了一下 L灵梦 1.81。

绝大部分都能理解,但是“汇编部分”的最后几句我无法理解,幸好 遥远氏 帮助了我,我才勉强看明白。

话不多说,现在就开始正题。

-------------------------------------------------------------------------------------

-------------------- [ 使用技术 ] -------------------

首先,我们来看看 L灵梦 1.81 使用的技术。

(以下 在说法上 ,用 “L 1.81 或者 L灵梦” 代替 “L灵梦 1.81”)

图解:

图中,可以清晰的看出,L 1.81使用的技术 可以分为两大块。

一块是 “汇编部分”,另一块是“亲捏造部分


而,除了“亲捏造部分”内的 刷值外,其余都需要配合使用 %n 来进行 操作处理。

所以 %n 只是 一种操作手段,而并非攻击手段! 

故,我把 %n 放在 “使用技术 或 攻击技术”的 前面,以表示 “通过 %n 来 实现该技术”

没放 %n 的 说明 没使用 %n。


汇编部分”使用了 :

①,%n 自身time 赋予 (即:通过%n手段,给L 1.81 自身的 time 赋值。。以下略)

②,%n 自身hitpausetime 赋予 


亲捏造部分”使用了

①,本体亲捏造 (这里通过 null刷值完成)

②,%n 攻击手段 :

1、%n Player显示 相关操作 (让L 1.81 以外的 对手 都不显示,和P消去 很像)

2、%n 强制胜利 (让L 1.81胜利)

3、%n 报幕 K.O


L灵梦 1.81 用到的技术,我认为就这么些了。

需要注意的是: 

①,“汇编部分”并没有 攻击。攻击部分在 “亲捏造部分

②,虽然说L灵梦进行了攻击,但并没有对敌方的数据进行操作,而是对Mugen的内存进行操作。player显示相关操作也不过是对Mugen的显示机制进行了处理,并没有对敌方的内存数据进行修改。

③,L灵梦1.81 对 电脑无害,对Mugen无害,对其他人物无害。

(这点是为了防止被 部分人误会。不加上这点 可能会出事。)
 


------------------------------------------------------------------

----------- [ 文件 和 文件内重要状态号] -------------------

下面,我来介绍一下 L灵梦 1.81 用到的文件,以及文件内的状态号


L灵梦 1.81 ,上面那些技术 用到的文件,就只有 2个:

-2,-3state.cns 和 null1.cns


顾名思义,-2,-3state.cns 用来 操作处理的,而  null1.cns用来刷值用的。

如下图:



在-2,-3state.cns 内,重要的状态号 为:

statedef -2

statedef 3

statedef 5900


在null1.cns内,则是:

statedef 1

statedef 2

-----------------

下面我来介绍一下状态号:


~ ↓ 汇编部分 ↓~

[statedef -2] --- 主要为 “汇编部分”的汇编代码。目的和作用是:为 “亲捏造”做准备


~ ↓ 过渡部分 ↓~

[statedef 5900] --- P数判断用 (1-6P 自杀、7-12P 攻击),判断后 转入相应状态

[statedef 1] --- 1-6P时 使用的自杀状态


~ ↓ 亲捏造部分 ↓~

[statedef 2] --- 亲捏造 刷值用(7-12P时)(上面所说的:“本体亲捏造”)

[statedef 3] --- 亲捏造 操作用 (实际攻击)(上面所说的:“3个攻击手段”)


好了,状态号就说这么多了。

思路应该很清晰了吧。


--------------------------------------------------------

------------------[ 攻击流程 ] -----------------------

下面来说说,L灵梦 1.81 的攻击 流程:

图解



这里,只说 第一局的攻击流程,因为第二局基本上思路相同。

我这里分为 4个 步骤。

上图已经讲的非常清除了。不过可能图太长,所以我下面打个文字版。虽说是文字版,我可能也会添油加醋,加点东西进去。因为图片限制,很多话都没打上去。

所以,建议各位看完 图解后,再看一下文字版,理清一下思路。

------

①,statedef -2 下,通过%n将“汇编部分”写入内存,并读取内存上的汇编代码。
读取后,L灵梦 1.81本身 的 time 和 hitpausetime 都被刷成 16(十进制)。即:
time = 16
hitpausetime = 16
显然,最关键的是 hitpausetime 被刷成 16 了。

这是为接下来的 “亲捏造” 做准备

思路:

1,此处讲的 [statedef -2]这个状态号

2,“汇编部分”的操作是 :使得 L 1.81的time 和 hitpausetime 有值

3,“汇编部分”的目的是 :为接下来的“亲捏造”做准备

4,步骤① =“准备与 前提


---------

②,由于Mugen会在对战最开始 会默认读取 5900这个状态。
故 在[statedef 5900] 下 进行 P数判断
1-6P 时:
转入 [statedef 1] 将 alive值  刷成0(即自杀)
7-12P 时:
转入 [statedef 2] 进行 亲捏造 的重定向的刷值工作

思路:

1,此处讲的 [statedef 5900]这个状态号

2,[statedef 5900]  进行P数判断,根据P数 选择自杀 还是攻击敌方。

3,步骤② = “命运  抉择


---------

③,1-6P的自杀这里不讨论,着重讨论7-12P的攻击。
转入 [statedef 2] 刷值成功后,会转入 [statedef 3]进行亲捏造操作处理。
与此同时,(以下不懂没关系)
捏造后的假亲(parent) 位置在 4B5B40处。而 假亲的teamside (parent,teamside) 的位置,刚好在 4B5B4C处。由于4B5B4C处的值为 主程序地址指针 ,故parent,teamside处的值也为主程序地址指针。(指针 =  数值)

思路:

1,此处讲的 [statedef 2] 这个状态号

2,[statedef 2] 内进行着 亲捏造的刷值,即上面所说的 “本体亲捏造

3,步骤③ = “捏造 与 原理


---------

④,转入的statedef 3中后
由于parent,teamside处的值为 主程序地址指针。故
1,首先 将parent,teamside处的值赋值给sysvar(0)。(保存地址)
2,通过%n手段,实现:
Player显示相关操作(敌方不显示,L灵梦才能一个人坐中间)
强制胜利
强制报幕K.O

虽然攻击手段只有这3个,但是因为对战最开始就执行了,所以杀伤力巨大。

思路:

1,此处讲的 [statedef 3] 这个状态号

2,[statedef 3]内,将主程序地址的指针 赋值给 sysvar(0),目的是保存地址,为后面%n操作提供基础。

3,提供基础后,可以开始攻击了。采用%n的写值手段

4,步骤④ = “赋值 与 攻击


-------------------------------------------------------


好了,上面的“热身运动”准备结束,下面开始真正的代码分析了。


-------------------------------------------------------

============================================================

=================  【 代码解析 】 ======================

-----------------------------------------

------  [ 汇编部分 ] ---------------------

-----------------------------------------

  


在-2,-3state.cns文件中,[statedef -2]下

通过 %n 的写法手段,将汇编代码,写入内存。位置为 4B404A

条件均为:trigger1 = !sysvar(0)

sysvar(0)将会在后面被赋值,其数值为 主程序地址指针

没赋值之前,才会调用 4B404A处的代码。

由于这里是为了理解代码含义,故不暂时不考虑条件,就当条件不存在

之前这种写法等相关问题,我在 L 灵梦0.78的文章里已经说过了,我这里就不再啰嗦了。

汇编部分”到这里结束:

[state ]
type = DisplayToClipboard
text="%1021c%c%c%cJ@K"
params = 108,10,10,10
ignorehitpause = 1
persistent = 256
trigger1 = !sysvar(0)


写入内存(4B404A处)的代码为:

004B404A - push eax
004B404B - mov eax,[004B5B4C]                     ; 主程序地址
004B4050 - mov eax,[eax+0000B754]              ; 自己人物信息的起始地址
004B4056 - mov ss:[eax+00000DFC],00000010      ; Time
004B4061 - mov ss:[eax+00000E1A],00000010      ; Hitpausetime (一开始的赋值,为亲捏造做准备)
004B406C - pop eax
004B406D - add esp,14 ; 这里及以下 为修复ESP
004B4070 - pop esi
004B4071 - add esp,00000400
004B4077 - mov [esp],004713F3
004B407E - ret

---------------------

--------(解析)-----------

------------------

先看这段:

004B404A - push eax
004B404B - mov eax,[004B5B4C]                     ; 主程序地址
004B4050 - mov eax,[eax+0000B754]              ; 自己人物信息的起始地址
004B4056 - mov ss:[eax+00000DFC],00000010      ; Time
004B4061 - mov ss:[eax+00000E1A],00000010      ; Hitpausetime (一开始的刷值,为亲捏造做准备)
004B406C - pop eax

------

解释:

push eax 和 pop eax 表示 入栈 和 出栈。不理解用途的,可以当作不存在。


004B404B - mov eax,[004B5B4C]                     ; 主程序地址赋值给eax
004B4050 - mov eax,[eax+0000B754]              

; 通过 主程序地址 + B754 找到 自己人物信息的起始地址

(如果自己是1P,就+B754 后值 为 1P 人物信息的起始地址;如果自己是2P,就+B758找到2P的人物信息起始地址。以此类推)


004B4056 - mov ss:[eax+00000DFC],00000010      ; Time
004B4061 - mov ss:[eax+00000E1A],00000010      ; Hitpausetime (一开始的刷值,为亲捏造做准备)

这段就是我说的,将 给L灵梦 1.81自身的 time  和 hitpausetime 赋值 为 16 (十六进制为 :10)

人物信息起始位置内存表告诉我们为  ”-3067“

DFC的 十进制为 3580

3580 +(-3067) = 513

也就是 距离 (-3067)处 3580个单位的地方 为 ( 513)处,此处为 time

同理,+E1A处 为hitpausetime。

这是“汇编部分”的主要功能。

---------------------------------------

而这段:

004B406D - add esp,14 ; 这里及以下 为修复ESP
004B4070 - pop esi
004B4071 - add esp,00000400
004B4077 - mov [esp],004713F3
004B407E - ret

一开始我没懂,问了遥远氏,他的回复可以概括为

这段汇编代码可以转变为:

mov [esp+418],4713F3

jmp 4131E1


我们先不急着解释L的代码,先看看  4131E1这个位置是什么。


代码:

004131E1 - add esp,14
004131E4 - pop esi
004131E5 - add esp,00000400
004131EB - ret

我们发现,这段ESP的操作代码竟然跟 L灵梦 1.81 里 的代码一样!

按照遥远氏的解释,正常代码读到 4131E1处,但L灵梦1.81 把代码读到了4B404A处。

由于,最后需要对ESP进行处理,遥远氏采取的 返回原处(4131E1处)修复,

即,使用了 jmp 4131E1

而,drab氏的L灵梦 1.81 使用 直接把4131E1处的ESP操作代码,复制过来,进行操作。

所以,L灵梦 1.81 内的ESP的操作代码才难以理解。


最后L 1.81内还有一句 :

004B4077 - mov [esp],004713F3

这句含义就等同于遥远氏所说的:

mov [esp+418],4713F3

这是修复性质的代码。


那为何是 ESP后面 + 418处呢?

原来,L灵梦 1.81 内的复制的(4131E1处)代码 对 ESP的操作是这样的:

add esp,14             --------  ESP + 14
pop esi                   --------  ESP + 4
add esp,00000400  -------   ESP + 400

总计 ESP + 418

ESP + 418 该处需要被修复成 4713F3

-----

所以说,学习过遥远氏 %f教学贴的人,可以这么理解,L 1.81的代码相当于:

mov [esp+418],4713F3     -------修复代码

jmp 4131E1                         -------可类比:“返回主线程”

可以 把 4131E1 看作 主线程的 原值。jmp过去,就相当于 返回的 主线程。

-----------------

-----------------

[state ]
type = DisplayToClipboard
text="%1021c%c%c%cJ@K"
params = 108,10,10,10
ignorehitpause = 1
persistent = 256
trigger1 = !sysvar(0)

最后这段代码是什么意思呢? 

其实就是覆盖 ESP。和 %f中,调用%f一样,是为了运行上面写入内存的汇编代码用的。

和%f稍微不同的是,“覆盖ESP”采用的是溢出方式

对于 text="xxx"来说,xxx的内容,超过1024个字节后,就是ESP的值。

而 %1021c%c%c%c 刚好就是 1021 + 1 + 1+ 1 = 1024个字节

后面这个 J@K 是 ASCII码,翻译成 16进制刚好是 4A404B

倒过来看就是 4B404A

没错,就是 L灵梦 1.81 汇编代码写值的内存位置。

这种溢出方式,感兴趣的人可以研究一下。

------------

----(总结)-------


汇编部分”综上所述:

总而言之,这段代码可以当 增加hitpausetime 用。

直接复制过来就能用,用了能能增加hitpausetime,然后有了hitpausetime,可以随便刷值了。

这就不需要 攻击或者当身 来获取hitpausetime了。

放入一个状态中,作为直接获取hitpausetime的方法,是不是很方便?(这句话当我没说)


汇编部分的作用(讲了n次了)

为后面亲捏造 刷值 做准备。


-----------------------------------------

------  [ 过渡部分 ] ---------------------

-----------------------------------------

过渡部分不是重点,我这里还是稍微提一下

两个状态 ,一个 [statedef 5900],一个[statedef 1]

[statedef 5900]
...

...
[state ]
type = SelfState
trigger1 = 1
value = 1+(palno > 6)  
persistent = 256
ignorehitpause = 1

上面的代码

value = 1+(palno > 6)   如果 1-6P,转入 [statedef 1](自杀状态),如果7-12P则转入 [statedef 2]亲捏造刷值用 状态。


[statedef 1]  ; 自杀状态号

....
[state 553]    ; Alive
type=null
trigger1=1
ignorehitpause = 1
[state 554]
type=null
trigger1=1
ignorehitpause = 1
[state 555]
type=null
trigger1=1
ignorehitpause = 1
[state 556]
type=null
trigger1=1
ignorehitpause = 1
[state ]
type = changestate
trigger1 = hitpausetime
trigger1 = alive     ; 没死亡,继续刷
value = stateno
persistent = 256
ignorehitpause = 1
[state ]
type = changestate
trigger1 = !alive    ; 死亡了,转入 statedef 2
value = 2+!!RoundsExisted   ; 第一局值为2。第二局值为 3
persistent = 256
ignorehitpause = 1


懂512 bug的,我想不用我多解释。


-----------------------------------------

------  [ 亲捏造部分 ] ---------------------

----------------------------------------- 

为2个状态号,一个[statedef 2](刷值用),一个[statedef 3](攻击用)

-----

------ 亲捏造 刷值用  ------------------

--------------------------------------

[statedef 2]

...(这里面,以防万一,需要 对hitpausetime处,再刷点数值,防止到时候hitpausetime不够了,具体请自己拆L灵梦 1.81)

...

; parent_id    把亲ID先刷干净,防止捏造过程中出错。
[state 6689]
type=null
trigger1=1
ignorehitpause=1
[state 6690]
type=null
trigger1=1
ignorehitpause=1
[state 6691]
type=null
trigger1=1
ignorehitpause=1
[state 6692]
type=null
trigger1=1
ignorehitpause=1


;parent         这里 捏造的假亲 起始点在 4B5B40 ,则 相应人物信息内的 teamside处 即 4B5B4C
[state 6693]   ; 40  注意,persistent 值要减 1
type=null
trigger1=1
persistent=65
ignorehitpause=1
[state 6694] ; 5B
type=null
trigger1=1
persistent=92
ignorehitpause=1
[state 6695] ; 4B
type=null
trigger1=1
persistent=76
ignorehitpause=1
[state 6696] ; 00
type=null
trigger1=1

......

.....

[state 6700]
type=null
trigger1=1
[state 6701]  ; 转入 亲捏造操作用 状态号
type=changestate
trigger1=1
value=3
persistent=256
ignorehitpause=1


----(解析)-------

正如,我上面写的注释一样。

①,首先,需要把hitpausetime再刷点数值,防止不够。

②,parent_id 处 (6689 - 6692)应该把ID均刷成0,防止出问题

③,parent 处(6693-6696)这里,刷值成多少,那么它的亲的“人物初始信息位置”,就被认为是从哪里开始。

比如,L灵梦 1.81 这里刷值成 4B5B40 。那么,捏造的假亲 的 开始位置,被认为是4B5B40

也就是说,刷值后,L灵梦会认为:它的亲(parent)的数据是从 4B5B40开始。

假如,我把此处数值刷成 4B4000,那么它会认为自己的亲的数据是从 4B4000开始。


这就是 “亲捏造” 的 原理。

“亲捏造”顾名思义,就是 亲(parent)的捏造,就是捏造亲。

捏造出来的 亲,是假的。只是“人物它会认为 这个假的亲,是自己的真正的亲。”

但我们要知道,这个假的亲,是我们人为捏造的。


亲捏造,相当于Mugen内部的 变量,用来记录 内存地址用的。

亲捏造并不可怕,也不深刻,仅仅是名字感觉很高端而已,实际上只是一个记录用东西。

亲捏造内 分为“本体亲捏造” 和 “helper型亲捏造(通常意义上的 普通亲捏造)”


具体原理这里不多说了,以后有机会再讲一讲吧(我感觉我说过了。怎么记不起来了,囧)

---

------

言归正题,L 1.81的 parent(亲)被刷成了4B5B40后,查看512bug 内存表

-3055~-3054        : teamside

上面说过,由于普通读取代码,触发器都是从 1开始,不超过512

如果1为标准点的话,人物信息初始位置 就是 -3067

这里的 teamside:-3055

-3055- (-3067) = 12  --- 十六进制为 :C

结合之前刷的 亲 为 4B5B40

那么 亲的 teamside也就是:parent,teamside处的值为 4B5B4C处的值

(4B5B4C = 4B5B40 + C)

这里有一点要注意,写法一定是 重定向形式的写法,即用英文状态下的 逗号","连接.

如:parent,temaside


这里的 4B5B4C内的值,即为Mugen主程序地址的值。

4B5B4C这个位置,是几乎所有Mugen隔离相关的根基。请牢记。之前我在L灵梦 0.78的文章中,也讲过了。这里不再敖述。


刷值结束后。伴随着 

[state 6701]  ; 转入 亲捏造操作用 状态号
type=changestate
trigger1=1
value=3
persistent=256
ignorehitpause=1


-----------------

L灵梦 1.81 的 亲捏造刷值之旅也告一段落。她开始了亲捏造攻击之旅


-----------------

----------- 亲捏造 操作用 ------------------

---------------------------------------

[statedef 3]

[state ]  ; 第一次赋值时
type = varset
trigger1 =!sysvar(0)
sysvar(0) = parent,teamside ; 第一次赋值时,该位置即为4B5B4C处的值。即主程序指针值
ignorehitpause = 1


; Player显示 相关操作

[state ]     ; Player显示相关操作
type = DisplayToClipboard
trigger1 = sysvar(0) && (teammode = single || teammode = turns)
text = "%*d%n%n%d"
params = teamside,1,sysvar(0)+47440,sysvar(0)+47444       ; Player显示相关操作
ignorehitpause = 1
[state ]     ; Player显示相关操作
type = DisplayToClipboard
trigger1 = sysvar(0) && (teammode = simul) && numpartner
text = "%*d%n%n%d"
params = (playerid(id - PlayerIDExist(id - 1)),teamside = teamside) * PlayerIDExist(id - 1) * 2 + teamside,1,sysvar(0)+47440,sysvar(0)+47444
ignorehitpause = 1

; %n 强制胜利 相关操作
[state ]      ; 低P 自杀。送对面胜利
type = DisplayToClipboard
trigger1 = sysvar(0)
trigger1 = palno < 7
text = "%*d%n%d"
params = !(teamside-1)+1,1,sysvar(0)+48180   ; BC34 --- P侧胜利  (1是1P侧,2是2P侧)。自己如果是1P侧,就2P侧胜利。反之1P侧胜利
ignorehitpause = 1
[state ]       ; 高P 强制胜利
type = DisplayToClipboard
trigger1 = sysvar(0)
trigger1 = palno > 6
text = "%*d%n%d"
params = teamside,1,sysvar(0)+48180   ; BC34 --- P侧胜利  (1是1P侧,2是2P侧)。自己如果是1P侧,就1P侧胜利。反之2P侧胜利
ignorehitpause = 1


; %n 强制报幕 K.O  相关操作
[state ]     ; 强制报幕 K.0
type = DisplayToClipboard
trigger1 = sysvar(0)
text = " %n"
params = sysvar(0)+48184     ; BC38 --- 强制报幕 K.0  (1:K.O 、2:Double K.O、3:TimeOver) 这里是 1
ignorehitpause = 1


; 调试用,作者方便观看。没实际攻击功能
[state ]  
type = displayToClipboard
trigger1 = 1
text = "%d %d %d %d %d"
params = var(58),var(59),teammode=single,teammode=simul,teammode=turns
ignorehitpause = 1

-----------------------

注释写的非常清楚了

sysvar(0) = parent,teamside 。也就是说 sysvar(0)以后就是 Mugen主程序地址了

那么

sysvar(0)+47440 和 sysvar(0)+47444       ; Player显示相关操作


sysvar(0)+48180   ; BC34 --- P侧胜利  (1是1P侧,2是2P侧)。根据条件来决定,L 1.81是输,还是赢。如果是 1-6P,则算输。7-12P 则算赢。


sysvar(0)+48184     ; BC38 --- 强制报幕 K.0  (1:K.O 、2:Double K.O、3:TimeOver) 这里是 1。


注意:以上均是通过 %n 的 手段 来实现的。

----------

故,综上所述,亲捏造 操作用 状态号(statedef 3)总共 做了 4件事:

①,给 sysvar(0) 赋值为 主程序地址值 ,目的是 方便下面%n 使用;

②,%n Player显示相关操作。这就是为什么 敌方消失,L灵梦坐中间的原因;

③,%n 强制胜利。根据条件来决定,是自己胜利(攻击),还是敌方胜利(自杀)。

④,%n 强制报幕K.O。更快速的 K.O,快速进入下一局,同时也防止敌方“阻止报幕出现K.O”


②、③、④均为 攻击手段



--------------------------------------------------

====================================

====================================



好了。L灵梦 1.81 的 使用技术,攻击流程 和 代码解析到此全部结束了。

欢迎提出意见和建议。


~ Fin ~


////////////////

/  Author  : yui (百度贴吧id :拓扑变换的唯)

/  Date : 2016.01.20

////////////////



 
评论
热度(2)
 
回到顶部