首个开源TJS2反编译器:Furikiri

Furikiri: The first decompiler for TJS2 language

Ulysses Wu
Schicksal
wdwxy12345@gmail.com

Abstract

TJS2是开源文字类游戏引擎吉里吉里(kirikiri)系列使用的脚本语言。在吉里吉里2及之后的版本(krkr2/krkrZ)中,原本仅为解释型语言的TJS升级为可以解释执行、也可以编译为字节码(TJS2 bytecode)并通过TJS虚拟机执行的编译型语言TJS2。目前,部分文字类游戏如[2]已经开始采用TJS2编译技术。现有工作[3]仅能实现字节码的反汇编,然而通过汇编码来了解脚本逻辑是困难且低效的。本文提出世界上首个公开的TJS2字节码反编译工具Furikiri[1],它可将TJS2字节码直接反编译为TJS2脚本语言。测试结果表明本工具能够由字节码生成简单的TJS2语句,包括赋值、分支、函数调用等。

 

Introduction

吉里吉里是一款开源的文字类游戏引擎,许多著名的文字类游戏均采用该引擎[4]。该引擎中用于实现程序逻辑的脚本语言为TJS。在吉里吉里引擎的较新版本(krkr2/krkrZ)中,采用了TJS语言的新版本TJS2。

TJS2是一门主要参考JavaScript实现的、面向对象的脚本语言[5]。该语言设计中的关键字与运算符众多(这导致吉里吉里引擎对TJS2的实现不完整,有些运算符几乎无人使用),字节码设计中的指令也多达128种(与之相对比,.NET的中间语言MSIL的指令仅有97种)。TJS2 VM是一种基于寄存器的虚拟机(区别于基于栈的虚拟机,如.NET CLR以及JVM),寄存器的数量在设计上是无限的。TJS2可以解释执行,也可以编译为二进制形式的TJS2字节码文件。进行编译的好处包括占用空间小、执行效率高、编译时语法检查、代码逻辑不易被观察和修改等。

现有工作,包括吉里吉里引擎代码中自带的tjs2disassmbler,仅能从TJS2字节码生成TJS2汇编码,即显示每条TJS2字节码的指令(Op Code)和操作数(Operand)。表1为一段TJS2代码及其对应的汇编码:

表1 TJS2代码与汇编码(汇编码由Furikiri生成)

function TestLoop(p3)
{
	while(p3 < 0)
	{
	   p3++;
	}
	
	do
	{
	   p3++;
	}
	while(p3 < 10);
	
	for(var i = 0; i < 10; i+=2)
	{
		i++;
	}
	
	if(p3 > 4396)
	{
		p3+=1;
	}
	else
	{
		p3-=1;
	}
	p3 = -p3;
	
	return p3;
}

(function) TestLoop 0x0219021A
00000000	const %1, *0 // *0 = (int)0
00000003	clt %-3, %1
00000006	jnf 00000012 // goto inc %-3
00000008	inc %-3
00000010	jmp 00000000 // goto const %1, *0
00000012	inc %-3
00000014	const %1, *1 // *1 = (int)10
00000017	clt %-3, %1
00000020	jf 00000012 // goto inc %-3
00000022	const %1, *0 // *0 = (int)0
00000025	cp %-4, %1
00000028	const %1, *1 // *1 = (int)10
00000031	clt %-4, %1
00000034	jnf 00000046 // goto const %1, *3
00000036	inc %-4
00000038	const %1, *2 // *2 = (int)2
00000041	add %-4, %1
00000044	jmp 00000028 // goto const %1, *1
00000046	const %1, *3 // *3 = (int)4396
00000049	cgt %-3, %1
00000052	jnf 00000062 // goto const %1, *4
00000054	const %1, *4 // *4 = (int)1
00000057	add %-3, %1
00000060	jmp 00000068 // goto cp %1, %-3
00000062	const %1, *4 // *4 = (int)1
00000065	sub %-3, %1
00000068	cp %1, %-3
00000071	chs %1
00000073	cp %-3, %1
00000076	srv %0
00000078	ret

由表1可知,仅从汇编码分析程序逻辑,是非常困难且低效的,需要随时记录和分析寄存器中的数据。并且,通过修改字节码来修改代码中的逻辑可能会非常复杂,尤其是在修改分支和循环逻辑(if、for等)时。因此,实现一个能够将字节码直接转换为TJS2代码的反编译器是非常有必要的。基于此,本文提出了Furikiri[1],它是世界上首个公开的、开源的TJS2反编译器,兼具反汇编功能,基于C#实现。

Architecture

Furikiri的设计主要参考了[5](TJS2设计规范)[6](反编译基本技术)[7](C#实现的反编译器设计)。

Furikiri采用自主实现的反汇编和反编译引擎,因此无需继承吉里吉里引擎的授权协议。Furikiri同时具有反汇编和反编译的功能。

Furikiri的运行流程是:

  • 读取TJS2字节码文件
  • 反汇编得到指令(Instruction)的集合
  • 检测并生成基本块(Block)
  • 计算支配树(Dominator)
  • 检测并生成循环(Loop)
  • RegMemberPass:检测类成员(Member)
  • ExpressionPass:生成表达式(Expression)的抽象语法树(AST)
  • ControlFlowPass:生成控制流结构
  • StatementCollectPass:收集生成的语句(Statement)
  • 输出代码文本(TjsWriter)

详细原理,将在后续文章中逐步介绍。

Evaluation

我们使用吉里吉里2引擎的示例工程中自带的初始化脚本Initialize.tjs进行测试,该脚本位于krkr\data\system路径中。我们使用TJS2编译器[8]对该脚本进行编译得到字节码,并使用Furikiri对该字节码进行反编译。图1显示了反编译结果与源代码的对比。

图1 TJS2源代码与反编译代码对比(反编译代码由Furikiri生成)

通过图1可知,Furikiri能够有效地对这一部分代码进行反编译,并准确地反编译出了函数调用、for循环、赋值语句、if分支、System和Storages标准库调用、break和delete关键字等,证明了Furikiri具有一定的实用性。

Future Works

尽管Furikiri当前支持大部分简单语句的反编译,但仍有许多TJS2 VM指令的分析未被实现,且Furikiri尚未完整支持数据流分析、表达式传播、if条件合并和条件反转等反编译器技术,导致在条件的反编译上会出现一些不正确或不完整结果。这些问题仍在改进中。

此外,未来版本的Furikiri可能也会实现对TJS2汇编码的汇编功能,以便于直接对汇编码进行修改。

Conclusions

本文提出了Furikiri——首个公开的、开源的TJS2反编译器。它能够读取TJS2字节码,并将其反编译为TJS2代码。该工具仍在不断完善中。本工具的提出和开源,不仅有助于对基于吉里吉里引擎的游戏进行分析与修改,也有助于参考实现对其他语言的反编译。如果您精通相关技术或有兴趣改善此工具,欢迎与我交流。

 

References

  1. Furikiri, UlyssesWu, github, https://github.com/UlyssesWu/Furikiri
  2. Nekopara Extra, NekoWorks, VNDB, https://vndb.org/v22020
  3. Literature Review: Kirikiri related projects, Project AZUSA, github, https://github.com/Project-AZUSA/KirikiriSharp/wiki/Related-Projects
  4. 吉里吉里:知名作品, 萌娘百科编辑者, 萌娘百科, https://zh.moegirl.org/%E5%90%89%E9%87%8C%E5%90%89%E9%87%8C#.E7.9F.A5.E5.90.8D.E4.BD.9C.E5.93.81
  5. TJS2参考手册, Key Fans Club, hydrozoa, http://hydrozoa.felisworks.com/doc/tjs2doc/contents/
  6. RecStudio Decompiler Design, Giampiero Caprino, Backer Street Software, https://www.backerstreet.com/decompiler/introduction.htm
  7. Telerik JustDecompile Engine, Telerik, github, https://github.com/telerik/JustDecompileEngine
  8. Tjs2Compiler, XmoeProject, github, https://github.com/xmoeproject/tjs2Compiler

添加评论

Loading