| 想成为一名破解者吗? Part II: 十六进制编辑器 (译者按: Part II介绍的有两部分,其一是资源文件的常见形式,另一部分是在探究一个未知的资源格式时最可靠的伴——十六进制编辑器.在这部分的翻译开始前,译者希望能为缺少编程与计算机相关知识的读者先做些背景资料介绍.同时,在翻译过程中也会适当加入一些内容. 译者先前也写了点资源格式相关的文字,灯穂奇譚的文件格式与以ef - the first tale. / Trial Version中资源文件的加密的解析简单介绍几个工具中的[准备工作1]部分) (译者按: 背景知识介绍: 首先,数字的进制问题. 所谓"进制问题",小学生也应该知道"逢X进一"的就是X进制,所以也不需要多说...这里特地提到,是因为计算机所使用的进制与我们日常生活中习惯的十进制不同.这里要说的,是二进制和十六进制. 二进制,逢二进一,每位可以是0-1两个状态. 十六进制,逢十六进一,每位可以是0-9,A-F共十六个状态.下面十六进制数字都使用0x为前缀表示.没有0x前缀的则是普通的十进制数字. 不同进制间的换算就不用说了吧...不过2的补码(2's complement)需要说说. 计算机里常使用所谓"2的补码"的形式来表达带符号的二进制数字.比起单独消耗掉一位来记录正负使0有两个,2的补码完整的利用了整个n位二进制数[-2^(n-1),2^(n-1)-1]范围内的所有数字.其值的计算方式是: -2^(n-1)*最高位+2^(n-2)&*次高位+...+2^0*最低位. 非负的2的补码的二进制数与直接从十进制换算到二进制的一样,不过首位必须要为0. 负的2的补码的二进制数是通过"取补"(complement)操作完成的.先得到数字的绝对值的原码,也就是直接从十进制换算到二进制的数;然后对每一位都"取反"(NOT),0变为1,1变为0,这样就得到了"反码";将反码加1,得到"补码". 对2的补码形式表达的二进制数,加法直接进行;减法转换为加上减数的补码;补码的计算就是"取反加一",其作用就是取得原数的相反数(绝对值相等,符号相反). 当前主流的计算机使用的电子元件都只能支持两个状态间的切换,"开"或者"关".因而使用二进制数字来描述计算机上储存的数据非常合适.但是二进制数字写起来还是显示起来都很冗长,为了方便起见,使用2的4次幂=16为底的十六进制来作为二进制数据的简写表达形式.所以,在查看二进制文件时,一般使用的是十六进制编辑器.高级语言像是C++和Java里一般也只允许以八进制或者十六进制作为简写形式来表达二进制数据. 用十六进制来表达二进制数字的方法: 将原二进制数字以4位为一组,分别换算为十六进制的对应数字作为一个十六进制位.最靠左的一组二进制数字不足四位的在前面补零.这样,一个字节是8个二进制位,用十六进制表达就是2个十六进制位. 然后,"文件"相关. 在计算机上,一个"文件"是在次级存储器(如磁盘,磁片,磁带,光盘等)上储存数据所使用的基本结构.在面向对象程序设计里,文件是一种用于描述次级存储器与内存之间数据传输的软件对象.本段的讨论范围并不涉及"管道文件""设备文件"之类的特殊文件,请注意区别.普通的文件,可以分为文本文件和二进制文件两种.其中, 文本文件是能被人直接阅读的文件.一个文本文件里的数据由一串字符所组成.字符都是按照一定的文字编码所表示的,例如ASCII或者UNICODE.可以直接以文本编辑器打开并阅读/编辑.举例的话,一个整数12345,保存到文本文件之后,就变成了"1""2""3""4""5"这五个字符. 二进制文件是文本文件以外的文件.这种文件更加简洁高效,但是其数据通常是不满足文字编码的一串0和1,不能直接被人阅读.举例的话,同样是整数12345,保存到二进制文件之后,就变成了0x00 0x00 0x30 0x39这4个字节的二进制数据. Archive,意思是存档,档案文件.本文内将其称为"归档".这个名词应该并不陌生,因为电脑的日常使用中也经常会用到rar,zip,tar,tar.gz,7z等格式的归档文件.游戏多数都不会将其使用的资源直接分散放在游戏的安装目录下,而会将他们"归档"到归档文件里.这样就能够使文件结构更加清晰,而且也可以在一定程度上节省空间,一是归档的时候可能有压缩,二是大量小文件浪费空间的问题被解决了.因而,通常情况下,要提取资源,首要破解目标就是游戏所采用归档的格式. 游戏有可能使用同样的归档文件格式对其所有类型的资源进行归档,也可能对不同类型的资源采取不同的归档格式,也有可能有选择性的归档.即使放入了归档文件,其中的资源也有可能被有选择性的压缩或加密过. 另外,还得提一下字节顺序的问题. 当一种数据需要多于一个字节来表达时,就牵涉到字节的顺序问题.可以认为从前向后读,首先读到的是最高位的,称为big-endian序;也可以认为最后读到的是最高位的,称为little-endian序.例如说,如果要读入一个32位的整型数,读入的数据是0x61 0x62 0x63 0x64的话,按big-endian读是0x61626364,而按little-endian读是0x64636261.同样的数据如果解释为ASCII编码的字符的话,无论采用什么字节顺序读都是"abcd"(也就是0x61 0x62 0x63 0x64对应的ASCII字符),因为ASCII字符只占一个字节(8位),而字节序只影响同一数据内的字节排列顺序而不影响数据间的顺序或者字节内位的顺序. 在Mac的PowerPC上,数据一般以big-endian顺序储存.而在我们常用的x86兼容的PC机上,数据一般以little-endian顺序储存.请留意. 呵呵,正篇开始前似乎废话太多呢.好了,开始翻译了.) 那么让我们开始吧.今天我们会看看一个简单的范例归档格式,作为讨论一个"标准的"游戏数据文件的各部分的跳板.把这种模板记在心里,尝试理解游戏数据文件里看似无规律的字节时就会轻松不少. 这次我们作为范例的游戏是Cross+Channel(体验版的下载链接在该页末尾).这个游戏已经有一个翻译计划正在进行中.喜欢的话下载一份体验版,然后我们来看看. (译者按: 嗯,原文里提到的翻译计划自然是英文化的翻译计划.现在其实也有中文化计划正在进行中.到时候或许就能见到成果了吧 ^ ^) 游戏安装后,安装目录下除了可执行文件之类以外,我们还能看到几个文件:bgm.pd, cg.pd, script.pd, se.pd, 还有voice.pd.这非常典型: 多数游戏不会让每个音频文件和图像文件独立放在外面,而会把它们集合起来放在几个归档文件里,然后游戏引擎就可以随机访问它们了.把原始的独立文件从归档文件中分离提取出来是攻破游戏的第一步. 现在就该启动破解者最喜欢的工具,十六进制编辑器.这是一类相对简单的工具软件,只是显示文件的原始字节及对应地址...常见的高级功能包括将数据解释为常见数据形式(整数或者浮点数,之类),比较文件,还有搜索特定数据模式等.我个人习惯用Mac上的工具,HexEdit.如果你有喜欢的Windows或者Linux上的十六进制编辑器的话,可以在回复的时候提及,让其他读者也留意一下. (更新: 至今提及的推荐的工具,在Windows上有WinHex,HView,XVI32,Hex Workshop,和文本/十六进制混合编辑器UltraEdit-32.Unix系的操作系统上有HexCurse.谢谢!) (译者按: 十六进制编辑器最基本的显示部分有两个: 一是以十六进制方式显示的原始的字节数据,每一个字节表示为两个十六进制位,并且每个字节之间会稍微分开一点;二是于前者相对应的以ASCII(或其他编码)方式解释的数据.有了对应的这个解释部分,我们就可以轻松的看出是否存在有明文存在的文本/标记. 译者想重点介绍一下WinHex.它是不但可以打开一般文件,还可以像打开文件一样打开正在运行中的进程的内存,同时还有别的一些方便的工具,像是计算器,十进制与十六进制转换器,磁盘编辑器等... 例如说,在Data Interpreter窗口里,可以直接读出光标当前位置之后(包括当前位置)的8位,16位和32位数据(以little-endian字节序解释)的十进制值,非常方便.) 那这些后缀为pd的文件看起来是什么样的呢? 下图是cg.pd的开头部分,我们可以肯定的猜测这个文件存有游戏的图像文件. 好极了,来看看: 可以辨认的文件名! (如bgcc0000e.png) 如果你看到的是类似这样的东西,而不是一些看似随机的字节,你应该庆幸.虽然你可能还不知道这些数据到底代表着什么,很明显能看到继续解释这些数据的前路. 为了更好的解释我们看到的数据是什么,是时候来简单介绍一下典型的游戏归档的内容了. - 1. 文件头 文件头里包含的是关于整个文件的一般信息.并不是必须的.不过如果在一个归档文件的开头就看到一串不像是文件名的字符串的话,多半就是有文件头的了. -- 1. 特征标记(signature) 通常一个归档文件都会以某种特征标记字符串开头,好让程序能辨认出归档的格式和版本.你可以通过这个标记来确认当前处理的文件是否属于正确的类型. -- 2. 索引位置 大多数情况下,紧接着特征标记就会是归档内的内容索引了.不过有时候索引实际上位于归档的末尾,毕竟归档打包程序要等到归档内的内容都处理完了才会知道索引有多大(例如说内容索引本身就被压缩过的情况).如果是那样的话,会有一个指向索引所在位置的指针. - 2.内容索引 归档内容的索引是你需要掌握的重要结构,因为你要通过它才能知道如何提取出归档里的文件. -- 1. 索引大小 索引一般会以一个表示大小的值开始.很多时候这个值就是归档所包含的文件数量,也可能是索引所占的字节数.不过索引大小并不一定存在,因为有时候内容索引会一直延续直到遇到一个特殊的结束记录(例如说,包含负的文件大小或者空的文件名的记录,等等). -- 2. 文件记录的列表 接下来会是归档内所包含的每个独立文件的记录的列表.这可以是定长或者变长的数据结构,取决于文件名是如何处理的.有时候还会有表示目录树装结构的路径层次结构.每个记录有含有一定数量的标准信息: 1. 文件名/文件路径: 可能是以0x00结尾的字符串,若不是也可能明确给出了字符串的长度.信不信由你,文件名其实不是必需的.我至少遇到过一个例子,只保存了文件名的哈希值. 2. 起始位置: 这会是一个相对某个位置的偏移量,通常是一个32位的整数.这"某个位置"可以是归档文件的开始,可以是内容索引的开始,或者有时候是"文件区"的开始(就是说,归档内第一个文件的起始地址,也可以说是内容索引的结束之后). 3. 文件大小: 文件的在归档内所占的空间大小,或者是文件的原始大小,通常是32位的整数.当文件在归档内所占空间与其原始大小相等时,文件大小要么有一个,要么干脆就省略掉了,因为可以从下一个文件的起始位置来计算出文件的大小;不相等时,通常说明文件被压缩过.则压缩后所占空间与原始大小都需要在内容索引里记录下来. 4. 标志位: 标明文件是否被压缩过,有的话用了何种算法;或者是否被加密过,有的话是否有相应的密钥或者初始值等. 5. 校验和(checksum): 为确保数据的完整性,有时候会记录下文件的校验和.这对破解者来说可能有点烦,因为这意味着修改归档的内容后我们还得把校验算法也跟出来,才能计算出修改过的新数据的正确校验和(不然想办法禁止掉可执行文件里的校验检查也可以). 注意: 有时候这些信息可能会分散在不同位置.例如说,文件的起始位置与文件名放在了索引的记录里,而文件大小和相关的一些标志位却在那个起始位置给出,紧接着的就是相应的原始文件. - 3.原始文件 原始文件的数据基本上就是头尾相接的放置在归档文件里了.这些数据有可能被压缩过也有可能被加密过.游戏引擎可以通过索引快速的定位到这些数据,因而可以任意使用它需要的文件数据. 好吧,了解了这个标准模板后,让我们来看看它能如何解释cg.pd里的数据.最开始的PackOnly看起来像是个特征标记.接下来是一串0x00字节,直到我们来到地址0x40,在一串可辨认的ASCII字符串之前有这么一组数据: 0x21 0x02 0x00 0x00 0x00 0x00 0x00 0x00. 这会是内容索引的大小吗? 说起来,我们应该如何解释这几个字节呢? 我们有几种选择: ·随机的标志位. 这里可以看成3个位被置位(set)了: 第一个字节中的0x20和0x01,以及第二个字节中的0x02.(注意这里最好转换到二进制观察每一位的值,是置位(set)还是清零(clear).例如0x21=00100001=0x20 AND 0x01.)这么解释算是有点道理,不过暂时没什么价值.看看其他可能吧. ·little-endian整数. 这里,0x21是最低字节,0x02是次低字节,依此类推.所以这个整数的实际数值是0x0000000000000221,十进制就是545.这可能是一个合理的索引大小值. ·big-endian整数. 按这种字节序的话,0x21是最高字节,0x02是次高字节,依此类推,整数值是0x21020000,也就是十进制的553,779,200(这已经是忽略了后面那4个0x00了).这个数据比较不合理,因为整个归档文件才不到500MB.所以如果想以big-endian方式来解释的话,得换个长度,例如说这可能是个16位的整数: 0x2102= 8450,这个值或许有可能,例如说是索引数组的字节数之类. 让我们来具体看看.从归档文件的开头开始看下来,似乎文件名是在0x48,0xD8,0x168,0x1F8,0x288等位置开始的.也就是说每个文件名的开始到下个文件名的开始之间有0x90 = 144字节.最后一个文件名(TCYM0005c.png)是在0x13248开始的,说明大概有(0x13248-0x00048)/0x90 + 1 = 545个文件记录. 你看到了什么了么? 没错,就是545! 以little-endian方式来解释0x21 0x02 0x00 0x00 0x00 0x00 0x00 0x00应该正确的告诉了我们在内容索引里有多少条记录.而且更重要的是,现在我们就可以专注于那些144字节的数据,确信这些就是一个个的索引记录.而且,我们知道这个归档青睐little-endian字节序,也可能使用8字节(64位)的整数. 那就让我们来看看这些索引记录.不过(这里有个小技巧了)不要看第一个记录.很多数据在第一个记录里都有可能是零,我们就看不出数据的意义了.来看看第二条记录吧,在地址0xD8到0x167: 我们看到了一个文件名,一堆零,和看起来像是8字节的little-endian整数.回忆一下典型游戏归档的模板...我们在寻找的是文件大小和位置的信息,或许还有一些标志位之类.那堆0x00可能会是标志位,不过现在还没办法确定. 眼下先假设那些零是文件名所属的数据结构的一部分: 这样就刚好给文件名分配了128个字节,是一个(懒惰的?)程序员会做的合理的事情.剩下的信息是两个整数,0x002420FA和0x0008370E.暂时还不知道这些是什么数据...还是先多看几个记录吧.内容索引的头几条记录里对应位置的数据是什么样的呢? File 1 0x00240048 0x000020B2 File 2 0x002420FA 0x0008370E File 3 0x002C5808 0x00002FA6 File 4 0x002C870E 0x00063B8A File 5 0x0032C338 0x0006A7CB 现在这些数据有点看头了.第一列数据总是越来越大,更重要的是它们总是以第二列的值增大! 这正是经典的位置+文件大小的进行. 如果我们的假设是正确的,那么第一个文件,bgcc0000e.png,应该有0x000020B2 = 8370字节这么长,而且应该在归档内的地址0x00240048附近开始.我们不能完全肯定,因为不知道这个偏移量是相对归档文件开头还是特征标记后还是哪里,而且这个值有点诡异,因为我们知道内容索引是在0x000132D7结束的,还记得吗? 总之先到那个地址去看看吧,因为文件的顺序可能被打乱了 爽! 我们猜得完全正确,地址0x00240048正是一个PNG文件的开头.无压缩,无加密.事实上,要是我们从这个地址开始把接下来的8370字节复制粘贴到一个新文件里(当然也是在十六进制编辑器里),然后用图像浏览器打开的话,我们就得到了...一张640×480的空白白色图. 呃.无论如何,游戏也是需要纯白图的,至少这图的尺寸没错.那么为保险起见,再试试下一张图片.从地址0x002420FA开始复制出0x0008370E个字节,我们得到的是: 好耶! 胜利是属于我们的! 现在让我们来总结一下.我们在这个阶段,认为.PD格式包括: 特征标记字符串"PackOnly" 56字节的0x00 8字节little-endian的文件数量值 多个144字节的索引记录,每个包括: 128字节的文件名,是以0x00表示结束的字符串 8字节little-endian的文件位置(从归档文件开头算起的偏移量) 8字节little-endian的文件大小 最后是原始文件数据,无压缩无加密,正好在索引里给出的位置上开始. 那么,内容索引之后到第一个原始文件之间的这段空白(全是0x00)该如何解释呢? 其实,那个地址,0x00240048看起来很可疑...一个索引记录是在0x48,也就是说有0x240000字节的空间可用于放置索引记录.每个记录144字节的话,就能装下16384个记录.也就是2^14.所以让我觉得这很像是一个(懒惰的?)程序员会做的事: 留下足够多的空间给大量的文件用就算了. 那这段空白是否必要呢? 说不定我们在重新打包归档的时候可以把这段空白清除掉,省下那么几兆空间.要不然我们把数据移动超过1个字节程序也会崩溃...我们只能等后面实践的时候才知道了. 好了,下次就让我们把获取到的知识转换成实际的程序代码吧.当然,我们会遇到些障碍,嘿嘿 (译者按: Part II结束.这个part所讲解的例子是一个非常简单,无加密无压缩的归档文件的格式分析. 简单说来,如果被分析的归档文件比较典型且无加密无压缩,那么就按照典型的游戏归档的形式,找到内容索引后,猜测索引内每个数值的意义,并且到归档内猜测的位置寻找原始文件. 译者的经验是,如果一开始就以脚本文件为目标的话,过程会比较痛苦.因为脚本文件经常是纯文本文件或者一些特制的格式,不一定有明确的起始标示,不便于确定是否正确定位到了原始文件的位置.所以,可以尝试对估计含有图像的或者音频的归档文件下手,就像本篇的例子以CG归档为破解对象. 为什么要针对图象,音频和视频下手呢? 因为业界在许多时候都会使用标准的格式来储存图像,音频和视频文件. 下面列举几种常见的文件格式,以[格式名]: [特征标识串]表示 图像: BMP: 0x41 0x4D (BM) PNG: 0x89 0x50 0x4E 0x47 0x0D 0x0A 0x1A 0x0A (.PNG....) GIF: 0x47 0x49 0x46 0x38 0x37 0x61 (GIF87a) 或 0x47 0x49 0x46 0x38 0x39 0x61 (GIF89a) JPEG: 0xFF 0xD8 0xFF 0xE0 0xxx 0xxx 0x4A 0x46 ( . ...JF) 0x49 0x46 0x00 (IF.) 音频: OGG: 0x4F 0x67 0x67 0x53 ("OggS") WAV: 0x52 0x49 0x46 0x46 0xxx 0xxx 0xxx 0xxx (RIFF....) 0x57 0x41 0x56 0x45 0x66 0x6D 0x74 0x20 (WAVEfmt ) 视频: MPEG: 0x00 0x00 0x01 0xBx AVI: 0x52 0x49 0x46 0x46 0xxx 0xxx 0xxx 0xxx (RIFF....) 0x41 0x56 0x49 0x20 0x4C 0x49 0x53 0x54 (AVI LIST) 在找到这些特征标识串后,我们就能轻松确定 1)是否存在某类型文件 2)文件的起始位置 从而可以与文件头里的信息进行对比,判断数据的意义,然后推广到游戏中同格式的其他归档的处理(例如含有脚本的归档) 更多更详细的文件特征标识串,可以在这里查询: http://www.garykessler.net/library/file_sigs.html Part II里举的例子"太过典型",让我们来看看可能发生什么简单的变化吧.同样是无加密无压缩的归档,灯穂奇譚里的AOD格式的归档虽然也有内容索引,但却被分成了一段段.详细请看灯穂奇譚的文件格式. 另外,区里另一篇帖子的例子更加有趣.ONE ~輝く季節へ FullVoice 汉化实战篇,其中的归档与其索引是分开在不同文件里的.再次提醒我们索引的必要性以及可能需要变通的地方. 不得不注意到,并不是所有游戏都会把资源都放入归档内的. 举个例子来说,Visual Art's旗下制作组所使用的RealLive,会使用GAMEEXE.INI文件来配置是否使用归档.其中相关的一段: Quote:#FOLDNAME.TXT = "DAT" = 1 : "SEEN.TXT" #FOLDNAME.DAT = "DAT" = 0 : "DAT.PAK" #FOLDNAME.ANM = "ANM" = 0 : "ANM.PAK" #FOLDNAME.ARD = "ARD" = 0 : "ARD.PAK" #FOLDNAME.HIK = "HIK" = 0 : "HIK.PAK" #FOLDNAME.PDT = "PDT" = 0 : "PDT.PAK" #FOLDNAME.G00 = "G00" = 0 : "G00.PAK" #FOLDNAME.M00 = "M00" = 0 : "M00.PAK" #FOLDNAME.WAV = "WAV" = 0 : "WAV.PAK" #FOLDNAME.BGM = "BGM" = 0 : "BGM.PAK" #FOLDNAME.KOE = "KOE" = 1 : "" #FOLDNAME.MOV = "MOV" = 0 : "MOV.PAK" #FOLDNAME.GAN = "GAN" = 0 : "GAN.PAK" 中间的数字就是说明是否使用归档的,是的话值为1,否的话值为0.这个还要与后面的文件名相配合,即使前面的值为1,假如后面的文件名为空串的话,也不使用归档,而是直接把一个个资源文件独立放置在安装目录下.上面这段引用自智代After的GAMEEXE.INI,可以看到只有脚本资源被放进了归档里,文件是SEEN.TXT.把这个归档文件拆开,就能看到里面实际上是许多小文件,SEEN0628.TXT到SEEN9072.TXT.而这些小文件又是编译过的脚本. 就算是放进了归档里的资源,要分析其内容索引也不总是这么轻松.假如说有数据被加密或压缩过,情况就会相对复杂一些. 也举个简单的例子吧.呵呵这个可是运气/RP大爆发的例子... 在はるのあしおと的web_trial里,归档文件的后缀是paz,都被简单加密过.要使用上面的经验来处理加密过的归档显然不实际,至少也得先解决解密问题.怎么办呢? 由于业界经常在音频格式上选择使用Ogg Vorbis,而且在游戏的安装目录下发现了"ogg.dll"和"vorbis.dll"这两个用于处理Ogg Vorbis文件的程序.所以我们大胆猜测bgm.paz里包含的背景音乐文件是采用Ogg Vorbis格式的,并由其入手. 用十六进制编辑器打开bgm.paz,发现里面都是些无法识别的数据.从头到尾浏览过之后没有发现形似文件头或者内容索引的东西.因此猜测文件被加密过. 上面说明文件的特征标识串时提到了,Ogg格式的文件是以0x4F 0x67 0x67 0x53 ("OggS")开头的,注意到中间有连续两个字节的值是一样的.如果运气好,文件只是被简单的加密过的话,那么加密后的密文里这两个字节的对应位置的值也应该是一样的;同理,拥有相同值的连续两个字节,其之前和之后的字节的值应该不同.根据这条线索,让我们来找找文件中有符合这个特征的地方. 很快我们就注意到了这个地方.红色标记出来的部分就是我们找到的一组符合线索的数据,0xB1 0x99 0x99 0xAD.先看看0x67可以如何对应到0x99上.还记得本篇开始时背景知识里提到的2的补码吗?把0x67和0x99分别展开,可以看到它们正是符合互为补码的关系.将头尾两个字节也对比一下,发现符合相同的关系.于是可以大胆猜测,整个文件都是以取补码的方式简单加密过的(虽然这个猜测不完全对...嘿嘿不过还是很RP吧...). 于是把想法实践一下,发现简单解密处理处理过后的文件已经相当的可读了.这样就可以继续按照典型的游戏归档文件的处理方法来处理了. 注意到,这里bgm.paz的头4个字节我并没有做取补处理.原因见以ef - the first tale. / Trial Version中资源文件的加密的解析简单介绍几个工具中的[准备工作1]部分所写的内容,这里就不重复写了. 虽然通过简单的观察就能破解出归档格式是很RP的事,不过游戏厂商在决定使用什么加密/压缩方式时本来也很RP -- XD 简单观察法适用于无加密无压缩,或者只做了简单加密的归档.简单的加密方法有: 1)加上或减去一个常量; 2)取反(NOT)或者取补(complement/negate); 3)与常量做简单的异或(XOR).如果是上述的3种情况,那么加密后的密文里字节与字节间的相等/不等关系会得以维持,因而可以让简单观察法顺利进行. 再举个例子的话...嗯,CIRCUS虽然一直不怎么用归档,不过它的游戏的脚本文件(后缀MES)对文本部分加了密,用的就是加上常量的方式,所以很容易由观察得出. ) 打击蛮大的,继续转吧写的蛮累的。。。。。讲究看吧 |