小白的yu-ris引擎汉化教程

最近遇到一些游戏使用yu-ris引擎制作,估计不少同学和我一样在拆包汉化的过程中遇到了严重的问题如:

提取出的ybn文件都是乱码

解密后的ybn文件依然是一堆乱码

客户端显示的中文都是乱码

接下来我们一条一条来解决,为了避免图片失效,我尽量只使用文字

一、提取出的ybn文件都是乱码

大家应该都知道ybn是加密文件,而加密方法其实很简单就是拿加密表进行异或计算,算法也不难,首先我们要了解ybn文件的结构(以下所有单位均为字节),我们这里使用473版本的ybn文件,其他版本大同小异,可以使用010Editor查看

索引头固定32Byte,应该所有版本都一样:

0-4:固定为ystb,只要处理形如:yst0000x.ybn的文件,除此之外别动

4-8:文件版本

8-12: 函数数量

12-16:函数区字节数,每个函数4字节[byte,byte,byte,byte]->[未知,参数个数,0,0]

16-20:参数区字节数,每个参数12字节[long,long,long]->[未知,字符串长度,偏移地址]

20-24:字符串区字节数,没有固定长度

24-28:未知数据区大小

28-32:全零

函数区大小:函数数量*4

参数区大小:参数数量*12,参数数量并没有直接给出,可以自己算一下

未知区大小:我们不用管他

接下来就是对每个区域的数据分别进行异或操作,而常用的异或表有3种


BYTE dec_tbl0[4] = { 0x80, 0x80, 0x80, 0x80 };

BYTE dec_tbl1[4] = { 0x07, 0xb4, 0x02, 0x4a };

BYTE dec_tbl2[4] = { 0xd3, 0x6f, 0xac, 0x96 };

这些是我从crass源代码摘出来的,基本上最后一个是目前最常使用的版本

具体算法可以参考:损疾 给的教程,为了方便大家调试我用python3写了一个版本:


def create_head_ystb(data:bytes):

    header = {

        'magic_8*4':data[:4].decode(),

        'version_32':0,

        'data1_length_div_4_32':0,

        'data1_length_32':0,

        'data2_length_32':0,

        'data3_length_32':0,

        'data4_length_32':0,

        'reserved_32':0

    }

    if header['magic_8*4'] != 'YSTB':

        return None

    cnt = 1

    for key in header:

        if key == 'magic_8*4':

            continue

        header[key] = int.from_bytes(data[cnt*4:cnt*4+4],byteorder='little')

        cnt += 1

    return header

def decode_473(file_name):

    dec_tbl2 = [ 0xd3, 0x6f, 0xac, 0x96 ]

    with open(file_name, 'rb') as f:

        data = f.read()

    header = create_head_ystb(data)

    print(header)

    if not header or header['magic_8*4'] != 'YSTB':

        print('未知文件:', file_name)

        return None

    header_byte = data[:32]

    data = list(data)

    data1_pointer = 32

    for i in range(header['data1_length_32']):

        ch = data[data1_pointer+i]

        ch ^= dec_tbl2[i&3]

        data[data1_pointer+i] = ch.to_bytes(1, 'little')

    data2_pointer = 32 + header['data1_length_32']

    for i in range(header['data2_length_32']):

        ch = data[data2_pointer+i]

        ch ^= dec_tbl2[i&3]

        data[data2_pointer+i] = ch.to_bytes(1, 'little')

    data3_pointer = data2_pointer + header['data2_length_32']

    for i in range(header['data3_length_32']):

        ch = data[data3_pointer+i]

        ch ^= dec_tbl2[i&3]

        data[data3_pointer+i] = ch.to_bytes(1, 'little')

    data4_pointer = data3_pointer + header['data3_length_32']

    for i in range(header['data4_length_32']):

        ch = data[data4_pointer+i]

        ch ^= dec_tbl2[i&3]

        data[data4_pointer+i] = ch.to_bytes(1, 'little')

    data = header_byte + b''.join(data[32:])

return data

用法:传入需要解密的文件地址,然后函数返回解密后的二进制Bytes串,保存后就可以看到解密后的文件。

二、解密后的ybn文件依然是一堆乱码

我们打开解密完的ybn文件,发现里面依然是一堆乱码,但是同时一些日文也显示了出来,原因是ybn文件和krkr的ks文本文件完全不一样,ybn由以下过程产生:

1)yu-ris引擎读取data/scripts文件夹。

2)把文件夹交给system/ YSCom/ YSCom.exe打包编译成二进制文件。

3)把刚刚打包好的文件切割成许多ybn文件放入ysbin文件夹。

4)yu-ris执行ysbin文件夹内的ybn。

ybn文件本质上是被打碎的压缩包,所以不会像其他引擎的脚本文件一样直接就能看到脚本,而我到现在也没有找到可以把ybn文件还原成scripts文件夹的工具,因此汉化只能把ybn文件夹内可以看到的日文翻译成中文,替换的时候一定要注意参数的长度和地址要重新对齐,索引头的字符串区长度也要修改,(这个过程只能由程序来完成,所以需要一定的编程能力)中文使用的字符串一定是gb2312,不能Unicode,下面会讲为什么。

三、客户端显示的中文都是乱码

Yu-ris引擎只支持shift-jis编码,因此中文肯定都是乱码,一个解决方法是使用大佬汉化过的yu-ris但是我看到几乎所有的地址都失效了。我也下了汉化游戏的执行程序(女装山脉、EUPHORIA)但是在我电脑上都运行不了(估计能运行也不通用),唯一的办法就是按照:损疾 的那篇教程修改exe文件,核心步骤就两个:1)修正CharSet 0x86 。2)修改检查表,我说一下使用x32dbg定位这两个部分的要点

修正CharSet 0x86:

找到gdi32.CreateFontIndirectA 这个函数的引用位置,直接就是需要修改的目标位置,修改成如下形式


00437D14    8B15 3CC55A00  mov    edx, dword ptr [5AC53C]

00437D1A    8B3495 3CD87D00 mov    esi, dword ptr [edx*4+7DD83C]

00437D21    83C6 17        add    esi, 17

00437D24    C606 86        mov    byte ptr [esi], 86

00437D27    90              nop

00437D28    90              nop

00437D29    90              nop

00437D2A    90              nop

00437D2B    90              nop

00437D2C    90              nop

00437D2D    90              nop

00437D2E    90              nop

00437D2F    90              nop

00437D30    A1 3CC55A00    mov    eax, dword ptr [5AC53C]

00437D35    FF3485 3CD87D00 push    dword ptr [eax*4+7DD83C]

00437D3C    FF15 38805600  call    dword ptr [<&GDI32.CreateFontInd>; gdi32.CreateFontIndirectA

修改检查表:

这个找起来有点麻烦,折腾了我一个下午,主要技巧就是搜索ysbin\ysc.ybn 这个字符串(可能有好几个,随便点一个就行),跳转到引用位置,往下翻可以看到这样一堆代码


.text:0045B5F0                mov    dword_7A8E98, offset loc_44B710

.text:0045B5FA                mov    dword_7A8EA0, offset sub_445624

.text:0045B604                mov    dword_7A8E9C, offset sub_445624

.text:0045B60E                mov    dword_7A8EA4, offset sub_445624

.text:0045B618                mov    dword_7A8EAC, offset loc_44B7DC

.text:0045B622                mov    dword_7A8EB0, offset off_44CDE4

.text:0045B62C                mov    dword_7A8EB4, offset off_44CE0C

.text:0045B636                mov    dword_7A8EB8, offset loc_44CE2C

.text:0045B640                mov    dword_7A8EBC, offset sub_44DDE4

………

这是一个定义结构体的代码,翻倒最后的位置,从下往上找和 损疾 帖子里最后给出的一大串汇编代码一个一个对照,倒数10个以内可以找到找到负责访问检查表的函数,跳转到函数位置往下翻,找到如下位置:


0044A57C  >  0FB6741D 00  movzx  esi, byte ptr ss:[ebp+ebx]      ;  取要判断的字的一个字节

0044A581  .  0FB68E A0F058>movzx  ecx, byte ptr ds:[esi+58F0A0]

0044A588  .  0FBEF1        movsx  esi, cl                          ;  esi, 将会记录是否32h,31h

0044A58B  .  8B78 4C      mov    edi, dword ptr ds:[eax+4C]

其中58F0A0 就是检查表的位置,不同的版本数字不一样,在内存区跳转后看到:


005B20A0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................ 

005B20B0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................ 

005B20C0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................ 

005B20D0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................ 

005B20E0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................ 

005B20F0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................ 

005B2100  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................ 

005B2110  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................ 

005B2120  00 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01  ................ 

005B2130  01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01  ................ 

005B2140  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................ 

005B2150  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................ 

005B2160  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................ 

005B2170  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................ 

005B2180  01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01  ................ 

005B2190  01 01 01 01 01 01 01 01 01 01 01 01 01 00 00 00  ................ 

005B21A0  00 00 00 00 96 30 07 77 2C 61 0E EE BA 51 09 99  .....0.w,a.îºQ.. 

005B21B0  19 C4 6D 07 8F F4 6A 70 35 A5 63 E9 A3 95 64 9E  .Äm..ôjp5¥cé£.d.  

把两个01串中间的00填充成01就可以了,如果使用的是x32dbg右键->补丁->修补文件就能保存exe。

最后我使用venus社的游戏(466版本): さまぁ☆ソルト进行了验证,可以显示中文,但是所有的日文将变成乱码,同时只支持gb2312,不过修改上面的86h似乎可以实现其他编码的显示,我还没有尝试过。

参考资料:

[1] 损疾https://www.cnblogs.com/sunjicccc/archive/2013/01/02/2841956.html

[2] flandrehttps://flandre-scarlet.moe/blog/629/

[3] 官网http://yu-ris.net/

你可能感兴趣的:(小白的yu-ris引擎汉化教程)