Skip to content

Latest commit

 

History

History
98 lines (70 loc) · 5.01 KB

1. Sire 自定义包的原理.md

File metadata and controls

98 lines (70 loc) · 5.01 KB

第一弹:Sire 自定义包的原理

首先明确下 Sire 自定义包的概念:

  • 文件名后缀为 scp,放入 sire 的 customize 目录下生效,同名的 scpv 文件为对应的参数配置文件
  • scp文件内容一般为 xml 文本,也有一些二进制文件(可能加密过? TODO: 需找大佬确认)
  • sire 的工作原理本质是对 SAN11PK 进程的内存中的机械码和数据进行修改,所以自定义包也是基于这一点,可以看到大佬们的自定义包中的 xml 文件都是描述对内存中的数据进行修改的
  • 包中最重要的部分是 <Codes> 标签,包含了一系列修改内存的操作<Code>, <Code>中包含了代码的地址<Address>、启用该功能时的机器码EnableCode和不启用该功能时的机器码DisableCode(大部分情况下为原机器码)。

下面是一个简单的显示武将真实忠诚度包 显示真实忠诚.scp的例子:

<?xml version="1.0" encoding="UTF-8"?>
<CustomModifyPackage>
	<PackageName>显示真实忠诚</PackageName>
	<PackageAuthor>龙哥</PackageAuthor>
	<PackageDiscription>显示超过100的忠诚的真实数值</PackageDiscription>
	<CustomModifyItems>
		<CustomModifyItem>
			<Caption>开启</Caption>
			<Enabled>true</Enabled>
			<Codes>
				<Code>
					<Description>代码</Description>
					<Address>004C8A9B</Address><!--长度:5-->
					<EnableCode>B8 64 00 00 00</EnableCode>
					<DisableCode>BE 64 00 00 00</DisableCode>
				</Code>
			</Codes>
		</CustomModifyItem>
	</CustomModifyItems>
</CustomModifyPackage>

(疑问:观察到每个<Address>后面都有一个长度的注释,实测为非必需,猜测自定包文件是由某编辑工具生成? TODO: 需找大佬确认)

做的操作是将地址为 004C8A9B 处的机械码 BE 64 00 00 00 用 B8 64 00 00 00 替换,

内存资料全局搜索 004C8A9B 可以搜到:

[命令编号为23:返回武将忠诚度]
004C8A8B - 0f b6 b7 ac 00 00 00       - movzx esi,byte ptr [edi+000000ac]  esi = 忠诚度
004C8A92 - 83 fe 64                   - cmp esi,64
004C8A95 - 0f 8c e4 03 00 00          - jl 004c8e7f
004C8A9B - be 64 00 00 00             - mov esi,00000064
004C8AA0 - 8b c6                      - mov eax,esi                        返回武将忠诚度
004C8AA2 - 5e                         - pop esi
004C8AA3 - 5b                         - pop ebx
004C8AA4 - 5f                         - pop edi
004C8AA5 - c3                         - ret

这段汇编代码主要处理的是返回某个武将的忠诚度。我们可以逐行分析它的功能:

  1. 004C8A8B - 0f b6 b7 ac 00 00 00 - movzx esi, byte ptr [edi+000000ac]

    • movzx esi, byte ptr [edi+000000ac]:这个指令从edi寄存器指向的地址偏移0xAC处读取一个字节,并将其零扩展后存储到esi寄存器中。这里的edi为武将指针,esi存放的是武将的忠诚度。
  2. 004C8A92 - 83 fe 64 - cmp esi, 64

    • cmp esi, 64:将esi中的值(忠诚度)与十六进制的64(即十进制的100)进行比较。
  3. 004C8A95 - 0f 8c e4 03 00 00 - jl 004c8e7f

    • jl 004c8e7f:如果esi中的值小于100(即忠诚度小于100),则跳转到地址004c8e7fjl是“jump if less”(如果小于则跳转)的缩写。
  4. 004C8A9B - be 64 00 00 00 - mov esi, 00000064

    • mov esi, 00000064:将十六进制的64(即十进制的100)赋值给esi寄存器。如果前面的比较没有跳转,说明忠诚度不小于100,这里将忠诚度设为100。
  5. 004C8AA0 - 8b c6 - mov eax, esi

    • mov eax, esi:将esi寄存器中的值(此时的忠诚度值)复制到eax寄存器中。eax寄存器通常用于函数的返回值,所以这里是将忠诚度值准备好作为返回值。
  6. 004C8AA2 - 5e - pop esi

    • pop esi:从栈顶弹出一个值到esi寄存器,恢复之前保存的esi值。
  7. 004C8AA3 - 5b - pop ebx

    • pop ebx:从栈顶弹出一个值到ebx寄存器,恢复之前保存的ebx值。
  8. 004C8AA4 - 5f - pop edi

    • pop edi:从栈顶弹出一个值到edi寄存器,恢复之前保存的edi值。
  9. 004C8AA5 - c3 - ret

    • ret:返回调用函数。这条指令会从栈顶弹出返回地址,并跳转到这个地址,恢复到调用该函数的地方继续执行。

这段代码的功能是读取一个武将的忠诚度,并确保返回的忠诚度不会超过100。如果忠诚度超过100,则将其限制在100以内,然后将这个值返回给调用者。

而我们做的修改是将地址 004C8A9B 处的代码改为:

B8 64 00 00 00  ; mov eax, 00000064

这条指令的含义是:将十六进制的 64 (十进制 100) 赋值给 eax 寄存器,而esi的值还是原来的,然后下一条将esi值加载给eax返回,就达到了返回真实忠诚度的作用了。

(PS: 这里实际使得武将忠诚度上限不再是100了,感觉对其他忠诚度的操作如登用武将会有影响,不是很推荐)