以光翼战姬1为例,探索MED引擎汉化方法

2020-06-27  本文已影响0人  ssynn

1.引擎介绍

MED引擎是魔改自DXLib的一个Gal游戏引擎,继承了DXLib引擎的一部分特点,但是引擎本身做了很多修改,比如文本包的加密,字体缓存的生成方式等(其实DXLib本身是支持中文和韩文的,但是MED的开发者很好心的进行了阉割,为此还重写了文字缓冲的写入和读取方式,我可真是谢谢你啊(●'◡'●))


2.游戏汉化过程

主要流程

  1. 文本解包解密
  2. 文本替换
  3. 文本封包
  4. 引擎汉化

1.文本解包解密

解密方式我在https://www.jianshu.com/p/f2c5cf9a9e4e聊过了,这里不再赘述,主要谈谈拆包。
md_scr.med的文件格式为:

ENTRY(不定长)

看了GARbro的源代码,它在拆包的时候会忽略ENTRY内的未知数据,所以想要汉化文本必须自己实现拆包和封包函数,代码我放最后了。
简单说明下,调用unpack函数传入md_scr.med 和保存位置的路径,拆解完毕后函数会将一个name_list.json放到脚本所在目录的intermediate_file文件夹内,之后封包要用,同时,程序会尝试解密文本,如果解密成功程序会显示密钥。记得将密钥复制到类的key属性,方便以后回封


2.文本替换

拆解出来的文本并没有文件扩展名,二进制打开后结构如下:

Header结构

我并没有研究出如何一步定位出字符串的位置,但是unk1指向的位置非常接近字符串所在的位置,所以从unk1开始扫描可以找到文件内所有的字符串。

文件内的字符串并没有用偏移和长度记录位置,只和顺序有关,所以替换的时候只要保证顺序不出错就可以了。


3.文本封包

封包时必须保证文件的顺序和源文件内的一致,所以需要上面提到的name_list.json来控制文件顺序。详细自己看代码中的repack函数吧。


4.引擎汉化

最最最麻烦的步骤!!!x32dbg用的不熟的可以放弃了!!!这一步折腾了我好几天,这里我以光翼战姬1的exe为例子说明。

首先的游戏文本的生成方式:

  1. exe检测文件夹内是否存在_FONTSET.MED文件,如果存在直接将字体数据读入内存。
  2. 如果不存在字体文件则调用createfontindirect创建字体,调用GetGlyphOutline创建字体点阵,将点阵写入内存,之后保存到_FONTSET.MED文件内,给下次打开游戏时使用。
  3. 在显示文字时在缓冲区直接读取字体的点阵,显示到屏幕上。

有问题的地方

  1. 在生成字体点阵时只会生成0x8140-0xB9FC 和 0xE040-0xF0FC的文字点阵,每个文字点阵占0xA80字节,这远远小于汉字GBK编码的范围,所以只修改createfontindirect不能完成文字汉化。
  2. 内存在开点阵缓冲区时只开了0x2370*0xA80的大小,存不下中文编码的字体。
  3. 引擎在存储和读取字体点阵时跳过了0xA000-0xE000的范围,所以需要修改计算文字点阵偏移的函数,才能让引擎正常读取汉字编码点阵。

exe修改步骤 光翼战姬1为例

  1. 修改CreateFontIndirectA的charset。首先删除文件夹内的_FONTSET.MED文件,给CreateFontIndirectA和CreateFont都下断点,F9运行,主要过程为:先断到CreateFontIndirectA大概3次然后断到CreateFont大概两次,继续F9,之后又会断到CreateFontIndirectA这时点运行到返回 再F8单步回到引擎的代码,可以看到引擎调用CreateFontIndirectA的代码
004B2EF1 | B0 86                      | mov al,86                                                   | 将这里改为mov al,0x86 最后一个字节用nop填充
004B2EF3 | 90                         | nop                                                         |
004B2EF4 | 8845 D7                    | mov byte ptr ss:[ebp-29],al                                 | 写入charset
004B2EF7 | 8D45 BC                    | lea eax,dword ptr ss:[ebp-44]                               |
004B2EFA | 8B55 FC                    | mov edx,dword ptr ss:[ebp-4]                                |
004B2EFD | 83C2 1B                    | add edx,1B                                                  |
004B2F00 | E8 738BFEFF                | call exstia1.49BA78                                         |
004B2F05 | 8B45 BC                    | mov eax,dword ptr ss:[ebp-44]                               |
004B2F08 | BA DC2F4B00                | mov edx,exstia1.4B2FDC                                      | 4B2FDC:"Default"
004B2F0D | E8 865BFDFF                | call exstia1.488A98                                         |
004B2F12 | 85C0                       | test eax,eax                                                |
004B2F14 | 75 1A                      | jne exstia1.4B2F30                                          |
004B2F16 | 8D45 B8                    | lea eax,dword ptr ss:[ebp-48]                               |
004B2F19 | BA DF7E6E00                | mov edx,exstia1.6E7EDF                                      | 6E7EDF:"\rMS Sans Serif"
004B2F1E | E8 558BFEFF                | call exstia1.49BA78                                         |
004B2F23 | 8B55 B8                    | mov edx,dword ptr ss:[ebp-48]                               |
004B2F26 | 8D45 DC                    | lea eax,dword ptr ss:[ebp-24]                               |
004B2F29 | E8 A266FDFF                | call exstia1.4895D0                                         |
004B2F2E | EB 19                      | jmp exstia1.4B2F49                                          |
004B2F30 | 8D45 B4                    | lea eax,dword ptr ss:[ebp-4C]                               |
004B2F33 | 8B55 FC                    | mov edx,dword ptr ss:[ebp-4]                                |
004B2F36 | 83C2 1B                    | add edx,1B                                                  |
004B2F39 | E8 3A8BFEFF                | call exstia1.49BA78                                         |
004B2F3E | 8B55 B4                    | mov edx,dword ptr ss:[ebp-4C]                               |
004B2F41 | 8D45 DC                    | lea eax,dword ptr ss:[ebp-24]                               |
004B2F44 | E8 8766FDFF                | call exstia1.4895D0                                         |
004B2F49 | C645 DA 00                 | mov byte ptr ss:[ebp-26],0                                  |
004B2F4D | C645 D8 00                 | mov byte ptr ss:[ebp-28],0                                  |
004B2F51 | C645 D9 00                 | mov byte ptr ss:[ebp-27],0                                  |
004B2F55 | 8BC3                       | mov eax,ebx                                                 |
004B2F57 | E8 C4010000                | call exstia1.4B3120                                         |
004B2F5C | FEC8                       | dec al                                                      |
004B2F5E | 74 06                      | je exstia1.4B2F66                                           |
004B2F60 | FEC8                       | dec al                                                      |
004B2F62 | 74 08                      | je exstia1.4B2F6C                                           |
004B2F64 | EB 0C                      | jmp exstia1.4B2F72                                          |
004B2F66 | C645 DB 02                 | mov byte ptr ss:[ebp-25],2                                  |
004B2F6A | EB 0A                      | jmp exstia1.4B2F76                                          |
004B2F6C | C645 DB 01                 | mov byte ptr ss:[ebp-25],1                                  |
004B2F70 | EB 04                      | jmp exstia1.4B2F76                                          |
004B2F72 | C645 DB 00                 | mov byte ptr ss:[ebp-25],0                                  |
004B2F76 | 8D45 C0                    | lea eax,dword ptr ss:[ebp-40]                               |
004B2F79 | 50                         | push eax                                                    | LOGFONT参数
004B2F7A | E8 31C12100                | call <JMP.&CreateFontIndirectA>                             | 调用CreateFontIndirectA

就按上面写的改就行了

  1. 修改缓冲区开辟内存的大小。引擎开辟内存长度的计算方式为0x2370*0xA80只需要把0x2370改为0x6000就够GBK编码的汉字使用了。修改时只需要搜索特征值 70 23 00 00就可以搜到所有开辟内存的函数段
00406092 | EB 65                      | jmp exstia1.4060F9                                          |
00406094 | 690D 647A7500 70230000     | imul ecx,dword ptr ds:[757A64],2370                         | 2370改为6000
0040609E | A1 647A7500                | mov eax,dword ptr ds:[757A64]                               |
004060A3 | C1E0 06                    | shl eax,6                                                   |
004060A6 | 8D0440                     | lea eax,dword ptr ds:[eax+eax*2]                            |
004060A9 | 03C8                       | add ecx,eax                                                 |
004060AB | 51                         | push ecx                                                    |
004060AC | E8 93650D00                | call exstia1.4DC644                                         |
004060B1 | 59                         | pop ecx                                                     |
004060B2 | A3 847A7500                | mov dword ptr ds:[757A84],eax                               |
004060B7 | FFB5 3CFEFFFF              | push dword ptr ss:[ebp-1C4]                                 |
004060BD | 6915 647A7500 70230000     | imul edx,dword ptr ds:[757A64],2370                         | 2370改为6000
004060C7 | 8B0D 647A7500              | mov ecx,dword ptr ds:[757A64]                               |
004060CD | C1E1 06                    | shl ecx,6                                                   |

应该要改前5处,改的时候只改命令里的立即数,后面搜索出来奇怪的命令不要去改!!

  1. 修改存取文字点阵的函数。一共有四处

第一处:读取对话文字的点阵,偏移地址的计算函数为:

if ( _wchar >= 0xE0 )                         // 全角字符
  {
    if ( _wchar < 0x8140
      || _wchar >= 0xF000
      || _wchar >= 0xA000 && _wchar < 0xE040
      || (signed int)(unsigned __int8)_wchar < 0x40
      || (signed int)(unsigned __int8)_wchar > 0xFC )
    {
      return wchar;
    }
    if ( _wchar >= 0xA000 )
      wchar_l = (_wchar >> 8) - 0xC1;
    else
      wchar_l = (_wchar >> 8) - 0x81;
    _ptr = (char *)ptr + 0xC0 * dword_757A64 + ((unsigned __int8)_wchar + 0xBD * wchar_l - 0x40) * dword_757A64;
  }
  else                                          // 半角字符
  {
    _ptr = (char *)ptr + (_wchar - 0x20) * dword_757A64;// 757A64 = 0xA80 = 0x20*0xA0*2
  }

得出当编码大于0xA000时文字点阵偏移的计算公式为

_wchar = b'\xa1\xb8'
offset = 0xC0 * 0xA80 + (_wchar[1] - 0x40 + 0xBD * (_wchar[0]-0xC1) ) * 0xA80 + 0x134E0004

其中_wchar为GBK编码的汉字
0x134E0004为点阵缓冲区的基址,每次运行都不一样

修改目标:

_wchar = b'\xa1\xb8'
offset = 0xC0 * 0xA80 + (_wchar[1] - 0x40 + 0xBF * (_wchar[0]-0x81) ) * 0xA80 + 0x134E0004

这里的代码修改后就像下面的样子

0042F839 | F76D F0                    | imul dword ptr ss:[ebp-10]                                  |
0042F83C | 0305 847A7500              | add eax,dword ptr ds:[757A84]                               | 基址
0042F842 | 8945 E0                    | mov dword ptr ss:[ebp-20],eax                               |
0042F845 | E9 C4000000                | jmp exstia1.42F90E                                          |
0042F84A | 817D FC 40810000           | cmp dword ptr ss:[ebp-4],8140                               |
0042F851 | 0F8C 75010000              | jl exstia1.42F9CC                                           |
0042F857 | 817D FC 00FE0000           | cmp dword ptr ss:[ebp-4],FE00                               | 改成FE00
0042F85E | 0F8D 68010000              | jge exstia1.42F9CC                                          |
0042F864 | 817D FC 00FE0000           | cmp dword ptr ss:[ebp-4],FE00                               | 改成FE00
0042F86B | 7C 0D                      | jl exstia1.42F87A                                           |
0042F86D | 817D FC 40FE0000           | cmp dword ptr ss:[ebp-4],FE40                               | 改成FE00
0042F874 | 0F8C 52010000              | jl exstia1.42F9CC                                           |
0042F87A | 8B55 FC                    | mov edx,dword ptr ss:[ebp-4]                                |
0042F87D | 81E2 FF000000              | and edx,FF                                                  |
0042F883 | 8955 F0                    | mov dword ptr ss:[ebp-10],edx                               |
0042F886 | 837D F0 40                 | cmp dword ptr ss:[ebp-10],40                                | 40:'@'
0042F88A | 0F8C 3C010000              | jl exstia1.42F9CC                                           |
0042F890 | 817D F0 FE000000           | cmp dword ptr ss:[ebp-10],FE                                | 0xFC -> 0xFE
0042F897 | 0F8F 2F010000              | jg exstia1.42F9CC                                           |
0042F89D | 817D FC 00A00000           | cmp dword ptr ss:[ebp-4],A000                               |
0042F8A4 | 7D 25                      | jge exstia1.42F8CB                                          |
0042F8A6 | 8B4D FC                    | mov ecx,dword ptr ss:[ebp-4]                                |
0042F8A9 | C1F9 08                    | sar ecx,8                                                   |
0042F8AC | 81C1 7FFFFFFF              | add ecx,FFFFFF7F                                            |
0042F8B2 | 69C1 BF000000              | imul eax,ecx,BF                                             | *0xBD -> *0xBF
0042F8B8 | 8B55 FC                    | mov edx,dword ptr ss:[ebp-4]                                |
0042F8BB | 81E2 FF000000              | and edx,FF                                                  |
0042F8C1 | 03C2                       | add eax,edx                                                 |
0042F8C3 | 83C0 C0                    | add eax,FFFFFFC0                                            |
0042F8C6 | 8945 F0                    | mov dword ptr ss:[ebp-10],eax                               |
0042F8C9 | EB 23                      | jmp exstia1.42F8EE                                          |
0042F8CB | 8B4D FC                    | mov ecx,dword ptr ss:[ebp-4]                                |
0042F8CE | C1F9 08                    | sar ecx,8                                                   |
0042F8D1 | 81C1 7FFFFFFF              | add ecx,FFFFFF7F                                            | -0xC1 -> -0x81
0042F8D7 | 69C1 BF000000              | imul eax,ecx,BF                                             | *0xBD -> *0xBF
0042F8DD | 8B55 FC                    | mov edx,dword ptr ss:[ebp-4]                                |

第二处:读取人名、log、对话框点阵
原理和上面一样,我直接给改好的汇编

00430ACA | 7C 09                      | jl exstia1.430AD5                                           |
00430ACC | 817D FC 00FE0000           | cmp dword ptr ss:[ebp-4],FE00                               | 改成FE00
00430AD3 | 7C 07                      | jl exstia1.430ADC                                           |
00430AD5 | B0 01                      | mov al,1                                                    |
00430AD7 | E9 6F010000                | jmp exstia1.430C4B                                          |
00430ADC | 817D FC 00FE0000           | cmp dword ptr ss:[ebp-4],FE00                               | 改成FE00
00430AE3 | 7C 10                      | jl exstia1.430AF5                                           |
00430AE5 | 817D FC 40FE0000           | cmp dword ptr ss:[ebp-4],FE40                               | 改成FE40
00430AEC | 7D 07                      | jge exstia1.430AF5                                          |
00430AEE | B0 01                      | mov al,1                                                    |
00430AF0 | E9 56010000                | jmp exstia1.430C4B                                          |
00430AF5 | 8B55 FC                    | mov edx,dword ptr ss:[ebp-4]                                |
00430AF8 | 81E2 FF000000              | and edx,FF                                                  |
00430AFE | 8955 F0                    | mov dword ptr ss:[ebp-10],edx                               |
00430B01 | 837D F0 40                 | cmp dword ptr ss:[ebp-10],40                                | 40:'@'
00430B05 | 7C 09                      | jl exstia1.430B10                                           |
00430B07 | 817D F0 FE000000           | cmp dword ptr ss:[ebp-10],FE                                | 改成FE
00430B0E | 7E 07                      | jle exstia1.430B17                                          |
00430B10 | B0 01                      | mov al,1                                                    |
00430B12 | E9 34010000                | jmp exstia1.430C4B                                          |
00430B17 | 817D FC 00A00000           | cmp dword ptr ss:[ebp-4],A000                               |
00430B1E | 7D 24                      | jge exstia1.430B44                                          |
00430B20 | 8B55 FC                    | mov edx,dword ptr ss:[ebp-4]                                |
00430B23 | C1FA 08                    | sar edx,8                                                   |
00430B26 | 81C2 7FFFFFFF              | add edx,FFFFFF7F                                            |
00430B2C | 69CA BF000000              | imul ecx,edx,BF                                             | 改成BF
00430B32 | 8B45 FC                    | mov eax,dword ptr ss:[ebp-4]                                |
00430B35 | 25 FF000000                | and eax,FF                                                  |
00430B3A | 03C8                       | add ecx,eax                                                 |
00430B3C | 83C1 C0                    | add ecx,FFFFFFC0                                            |
00430B3F | 894D F0                    | mov dword ptr ss:[ebp-10],ecx                               |
00430B42 | EB 22                      | jmp exstia1.430B66                                          |
00430B44 | 8B55 FC                    | mov edx,dword ptr ss:[ebp-4]                                |
00430B47 | C1FA 08                    | sar edx,8                                                   |
00430B4A | 81C2 7FFFFFFF              | add edx,FFFFFF7F                                            | 改成FFFFFF7F
00430B50 | 69CA BF000000              | imul ecx,edx,BF                                             |
00430B56 | 8B45 FC                    | mov eax,dword ptr ss:[ebp-4]                                | 改成BF

第三次:存储字体点阵的函数

00432065 | 81EA 81000000              | sub edx,81                                                  |
0043206B | 69CA BF000000              | imul ecx,edx,BF                                             | 改成BF
00432071 | 8B45 C8                    | mov eax,dword ptr ss:[ebp-38]                               |
00432074 | 25 FF000000                | and eax,FF                                                  |
00432079 | 03C8                       | add ecx,eax                                                 |
0043207B | 83E9 40                    | sub ecx,40                                                  |
0043207E | 894D 8C                    | mov dword ptr ss:[ebp-74],ecx                               |
00432081 | EB 25                      | jmp exstia1.4320A8                                          |
00432083 | 8B55 C8                    | mov edx,dword ptr ss:[ebp-38]                               |
00432086 | C1EA 08                    | shr edx,8                                                   |
00432089 | 81EA 81000000              | sub edx,81                                                  | 改成81
0043208F | 90                         | nop                                                         |
00432090 | 90                         | nop                                                         |
00432091 | 90                         | nop                                                         |
00432092 | 69CA BF000000              | imul ecx,edx,BF                                             | 改成BF
00432098 | 8B45 C8                    | mov eax,dword ptr ss:[ebp-38]                               |
0043209B | 25 FF000000                | and eax,FF                                                  |

第四处:让生成文字点阵的范围包含0xA000-0xE040的编码,同时第二个字节的上限改为FE

004317DB | C745 BC 81000000           | mov dword ptr ss:[ebp-44],81                                |
004317E2 | 817D BC A0000000           | cmp dword ptr ss:[ebp-44],A0                                | 生成文字缓冲
004317E9 | 75 07                      | jne exstia1.4317F2                                          |
004317EB | C745 BC A0000000           | mov dword ptr ss:[ebp-44],A0                                | E0改为A0
004317F2 | C745 B8 40000000           | mov dword ptr ss:[ebp-48],40                                | 40:'@'
004317F9 | 837D B8 7F                 | cmp dword ptr ss:[ebp-48],7F                                |
004317FD | 74 10                      | je exstia1.43180F                                           |
004317FF | 8B45 BC                    | mov eax,dword ptr ss:[ebp-44]                               |
00431802 | C1E0 08                    | shl eax,8                                                   |
00431805 | 0345 B8                    | add eax,dword ptr ss:[ebp-48]                               |
00431808 | 33D2                       | xor edx,edx                                                 |
0043180A | E8 79010000                | call exstia1.431988                                         |
0043180F | FF45 B8                    | inc dword ptr ss:[ebp-48]                                   |
00431812 | 817D B8 FE000000           | cmp dword ptr ss:[ebp-48],FE                                | 第二个字节的上限改为FE
00431819 | 7E DE                      | jle exstia1.4317F9                                          |
0043181B | FF45 BC                    | inc dword ptr ss:[ebp-44]                                   |
0043181E | 817D BC F8000000           | cmp dword ptr ss:[ebp-44],F8                                | 第一个字节的上限改为F8
00431825 | 7C BB                      | jl exstia1.4317E2                                           |
00431827 | 8A0D 5C7A7500              | mov cl,byte ptr ds:[757A5C]                                 |
0043182D | 888D 2CFFFFFF              | mov byte ptr ss:[ebp-D4],cl                                 |
00431833 | A0 607A7500                | mov al,byte ptr ds:[757A60]                                 |

最后是Python脚本

class MED():
    key = b'\x9b\xd0\xcf\xd0\x9b\x88\x8d\x8c\x97\x9f\xd0\xcf\x94\x8b\x8d\x8c\x9b\x8e\x97\x8d'
    def decrypt(_data: bytes, _key=None):
        if _key:
            MED.key = _key
        _data = bytearray(_data)

        for i in range(0x10, len(_data)):
            _data[i] = (_data[i]-MED.key[(i-0x10) % len(MED.key)]) & 0xff

        return _data

    def encrypt(_data: bytes):
        _data = bytearray(_data)

        for i in range(0x10, len(_data)):
            _data[i] = (_data[i]+MED.key[(i-0x10) % len(MED.key)]) & 0xff

        return _data

    def extract_med(name=None):
        '''
        从input文件夹内的脚本文件抽取文本,放入intermediate_file/jp_all.txt
        如果游戏可以自定义姓名,则需要将文本中的$0进行替换,使用时只需要传入主人公的名字就可以了
        $0 主人公名字
        '''
        def _has_jp(line: str) -> bool:
            '''
            如果含有日文文字(除日文标点)则认为传入文字含有日文, 返回true
            '''
            for ch in line:
                if ('\u0800' <= ch and ch <= '\u9fa5') or ('\uff01'<=ch<='\uff5e'):
                    return True
            return False

        file_all = os.listdir('input')
        ans = []
        for f in file_all:
            _data = open_file_b(f'input/{f}')
            # _data = MED.decrypt(_data)
            _offset = int.from_bytes(_data[4:8], byteorder='little') + 0x10
            _str = _data[_offset:]
            _buff = b''
            for i in _str:
                if i:
                    _buff += to_bytes(i, 1)
                else:
                    try:
                        _buff = _buff.decode('cp932')
                        if _has_jp(_buff) and _buff[0] not in ';#':
                            if name:
                                _buff = _buff.replace('$0', name)
                            ans.append(_buff)
                    except Exception as e:
                        print(e)
                        print(f, _buff)
                    _buff = b''
        save_file('intermediate_file/jp_all.txt', '\n'.join(ans))

    def output(name=None):
        '''
        替换原文件中的文本,将替换后的结果放入output文件夹
        使用前需要利用jp_all.txt生成翻译字典,并将字典翻译放入intermediate_file文件夹,字典的格式为
        {
            'Japanese':'chinese',
            'Japanese':'chinese',
            ...
        }
        '''
        def _has_jp(line: str) -> bool:
            '''
            如果含有日文文字(除日文标点)则认为传入文字含有日文, 返回true
            '''
            for ch in line:
                if ('\u0800' <= ch and ch <= '\u9fa5') or ('\uff01'<=ch<='\uff5e'):
                    return True
            return False

        file_all = os.listdir('input')
        jp_chs = open_json('intermediate_file/jp_chs.json')
        cnt = 0
        failed = []
        for f in file_all:
            _data = open_file_b(f'input/{f}')
            _data = bytearray(_data)
            _offset = int.from_bytes(_data[4:8], byteorder='little') + 0x10
            _str = _data[_offset:]
            _buff = b''
            while _offset < len(_data):
                if _data[_offset]:
                    _buff += _data[_offset:_offset+1]
                else:
                    try:
                        _str = _buff.decode('cp932')
                        # print(_str)
                        # if len(failed) >5:
                        #     return
                        if not _has_jp(_str) or _str[0] in ';#':
                            _offset += 1
                            continue
                        if name:
                            _str = _str.replace('$0', name)
                        if _str in jp_chs and jp_chs[_str]:
                            _offset -= len(_buff)
                            _new_bytes = jp_chs[_str].encode('gb2312', errors='ignore')
                            _data[_offset:_offset+len(_buff)] = _new_bytes
                            _offset += len(_new_bytes)
                            cnt += 1
                        else:
                            failed.append(_str)
                    except Exception as e:
                        print('失败')
                        print(f, _buff)
                        print(e)
                        print()
                    finally:
                        _buff = b''
                _offset += 1
            _data[:4] = to_bytes(len(_data)-0x10, 4)
            save_file_b(f'output/{f}', _data)
        save_file('intermediate_file/failed.txt', '\n'.join(failed))
        print('替换:', cnt)
        print('失败:', len(failed))

    def unpack(path='md_scr.med', output='input'):
        def remove_dumplicate_str(key):
            for i in range(2, len(key)):
                cnt = int(len(key) / i + 1)
                tmp = key[:i]
                ans = bytearray(tmp)
                tmp *= cnt
                tmp = tmp[:len(key)]
                # print(tmp, len(tmp))
                if tmp == key:
                    # print(ans)
                    return ans
            return None
        data = open_file_b(path)
        entry_length = from_bytes(data[4:6])
        entry_count = from_bytes(data[6:8])
        name_list = []
        if not os.path.exists(output):
            os.mkdir(output)
        for i in range(entry_count):
            entry = data[16+i*entry_length:16+(i+1)*entry_length]
            offset = from_bytes(entry[-4:])
            length = from_bytes(entry[-8:-4])
            unk = from_bytes(entry[-12:-8])
            name = ''
            for i in entry:
                if not i:
                    break
                else:
                    name+=chr(i)
            
            _file_data = data[offset:offset+length]
            file_name = f'{name}_{unk}'
            save_file_b(f'{output}/{file_name}', _file_data)
            # print(file_name, offset, length)
            name_list.append(file_name)
        key = None
        file_all = os.listdir(output)
        for f in file_all:
            if f[:5] == '_VIEW':
                _view = open_file_b(f'{output}/{f}')
                _raw = _view[0x10:0x28]
                _base = b'\x00\x23\x52\x55\x4C\x45\x5F\x56\x49\x45\x57\x45\x52\x00\x3A\x56\x49\x45\x57\x5F\x30\x00\x7B\x00'
                key = []
                for i in range(24):
                    key.append(_raw[i] - _base[i])
                break
        if key:
            print((key))
            key = bytearray(map(lambda x:x&0xff,key))
            key = remove_dumplicate_str(key)
            print(key, len(key))
            for f in file_all:
                _data = open_file_b(f'{output}/{f}')
                _data = MED.decrypt(_data, key)
                save_file_b(f'{output}/{f}', _data)
            if not os.path.exists('intermediate_file'):
                os.mkdir('intermediate_file')
            save_json(f'intermediate_file/name_list.json', name_list)
        else:
            print('无法解密')

    def repack(path='output'):
        '''
        调用此函数先需要先将解包时产生的密钥填入key属性
        '''
        name_list = open_json(f'intermediate_file/name_list.json')
        # name_list = os.listdir(path)
        entry_length = 0x17
        header = b'MDE0\x17\x00'
        header += to_bytes(len(name_list), 2) + b'\x00' * 8
        entry_all = []
        file_data = []
        offset = 0x10 + len(name_list)*entry_length
        
        for f in name_list:
            
            _p = len(f)-1
            while f[_p] != '_':
                _p-=1
            name = f[:_p].encode() 
            name += b'\x00'*(entry_length-len(name)-12)
            unk = int(f[_p+1:])
            unk = to_bytes(unk, 4)
            _file_data = open_file_b(f'{path}/{f}')
            _file_data = MED.encrypt(_file_data)
            entry = name + unk + to_bytes(len(_file_data), 4) + to_bytes(offset, 4)
            entry_all.append(entry)
            file_data.append(_file_data)
            offset += len(_file_data)
        
        save_file_b('md_scr.med.chs', header + b''.join(entry_all) + b''.join(file_data))


我的妈呀累死我了

上一篇下一篇

猜你喜欢

热点阅读