对网页游戏《卧龙吟》的分析4-----加密xml文件mns_zh_CN.dat的解密过程

       上一篇粗略的过了一遍数据请求流的过程。

     1.发送请求  AddRequest()  在这里面构造数据流请求的路径

      2.请求数据LoadStream()

      3.数据解析 ProcessStreamData()

      数据解析ProcessStreamData() 通过AbstractXmlDataReader.as里面的ReadStream()函数来解析。我们重点分析这个函数的走向

     

 public override function ReadStream(stream:flash.net.URLStream, arg2:uqee.core.entity.StreamRequestInfo):String
        {
			var bt:* = new flash.utils.ByteArray();
			//stream.readBytes(bt,0,stream.length);
			//trace("长度:"+bt.length);
			//trace(bt);
			var loc1:*=null;
			var loc2:*=0;
			var loc3:*=null;
			var loc4:*=0;
			var loc5:*=null;
			var loc6:*=null;
			var loc7:*=null;
			var loc8:*=null;
			if (!arg2.IsConfusion) /*如果是未加密的数据就直接读取并存储*/
			{
				loc1 = new flash.utils.ByteArray();
				stream.readBytes(loc1, 0, loc2);
				loc1.position = 0;
				this.Store(arg2.FileName, new XML(loc1));
				return null;
			}
			/*处理加密的xml数据*/
			while (stream.bytesAvailable > 0) 
			{
				if ((loc2 = stream.readInt()) != 0) 
				{
					loc7 = new flash.utils.ByteArray();
					stream.readBytes(loc7, 0, loc2);
					loc7.uncompress();
					loc7.position = 0;
					loc3 = loc7.readMultiByte(loc7.length, "gbk");
					loc2 = stream.readInt();
				}
				else
				{		
					loc3 = arg2.FileName.replace("_" + uqee.core.global.GlobalVariables.Lang, "");
					loc2 = stream.bytesAvailable;
				}
				
				if(false)
				{
					
				}
				else
				{												
					loc4 =16;	
					loc8 = arg2.Url.replace("_" + uqee.core.global.GlobalVariables.Lang, "").split("/");
					loc4 = loc8[(loc8.length - 1)].indexOf(".");
				}			
				
				loc5 = this.ReadCompressStream(stream,loc2,loc4);	
				
				this.Store(loc3,loc5);
			}	
			
			return null
        }

       可以看到这个函数的流程

      根据Isconfusion判断是否加密。这里是mns_zh_CN.dat 文件 它是一个加密的文件 文件大小5163 

     以下是文件开始的部分十六进制数据视图

00000000h: 00 00 00 00 2B B0 00 16 E4 34 00 00 00 00 00 00 ; ....+?.?......
00000010h: 1E 0F CB 87 11 D8 CE 66 91 0F 83 1E CA FD 7B 33 ; ..藝.匚f??数{3
00000020h: D4 7F E9 B7 DA 28 31 76 25 66 20 4D 2A 09 6D 89 ; ?榉?1v%f M*.m?
00000030h: BA 5D 89 1B 90 49 4A 8D B8 2C 4E 0C 52 31 21 80 ; 篯?怚J嵏,N.R1!€
00000040h: 8A 78 53 5F A6 BB 89 D2 00 99 BA 96 80 04 4E 77 ; 妜S_壱.櫤杸.Nw
00000050h: 54 46 94 CE F1 F3 30 B8 6A 1B E6 B0 1C 7C 2A 9E ; TF斘耋0竕.姘.|*?
00000060h: 88 52 D7 24 60 9E 45 20 33 D6 BB 20 F4 73 15 92 ; 圧?`濫 3只 魋.?
00000070h: D3 99 D3 38 F8 47 3A 1E 8E 0A 18 C4 E5 99 6C 56 ; 訖?鳪:.?.腻檒V
00000080h: A7 C3 78 03 14 54 7C 32 05 F4 64 15 00 A9 D1 7E ; x..T|2.鬱..┭~
00000090h: 98 9A 09 6E 78 A3 7A 84 8F 7E FE 88 4B BD D6 8F ; 槡.nx剰~K街?
000000a0h: 6F 41 45 21 73 D5 24 C9 1B 4A E5 F9 9C 40 48 2E ; oAE!s??J妁淍H.
000000b0h: A2 2E F0 D1 05 23 F1 AA E5 B9 09 C3 06 FF 8F 6E ; ?鹧.#癃骞.?弉
000000c0h: 9E D0 64 88 FB DE 05 1E 3C 33 E2 8C DC 4A 3E 09 ; 炐d堺?.<3鈱躂>.
000000d0h: 5D D6 CF 01 5C 3B 0B AF B2 4B 27 69 C0 52 F2 ED ; ]窒.\;.K'i繰蝽
000000e0h: 35 4F F3 B1 69 EB 97 8F 09 CE 9B 9A B8 F6 4F B1 ; 5O蟊i霔?螞毟鯫?
000000f0h: 9C 4A 11 37 84 54 F6 13 EC 79 66 C4 1A 0A B8 44 ; 淛.7凾?靬f?.窪
00000100h: E1 E2 E0 E9 DD 22 BB 41 82 80 BD 9B 3E BC 87 62 ; 徕嚅?籄個經>紘b
00000110h: B1 68 15 D8 54 CA 47 50 27 B4 8C 40 8B 3A 2A 47 ; 県.豑蔊P'磳@?*G
00000120h: AE 33 E7 AD B5 6F 93 8B 0C 4A 33 21 47 95 03 0D ; ?绛祇搵.J3!G?.
00000130h: A7 40 F1 D5 44 1E D2 BE 26 7E B4 55 D5 23 BA 8A ; 裾D.揖&~碪?簥
00000140h: BD 34 51 AB D7 06 8C 24 84 DF 09 7E 1B 2C 5B 75 ; ?Q.?勥.~.,[u

      这个函数为了调试方便我有所修改。所以简单的说一下原来的解密流程。

首先读取头4个字节,都是0  此时 文件的availablebyte 5159字节  文件名中的语言字符要被替换为空 也就是说mns_zh_CN.dat被替换为 mns.dat

然后通过indexOf(".")函数查找文件名mns.dat分割点号的位置 这里是3  这些数值在进行解密的时候都会用到

这个dat文件采用了LZMA加密方式,文件中加入了杂数据用来扰乱视线。所以在进行LZMA标准解密的时候需要去掉这些杂数据。


在ReadCompressStream()函数中它仅仅是设置了 LZMA 解密的properties,这是5个字节的数组 内容是固定的,

var loc1:*;
            (loc1 = new flash.utils.ByteArray()).writeByte(93);
            loc1.writeByte(0);
            loc1.writeByte(0);
            loc1.writeByte(128);
            loc1.writeByte(0);

也就是 5D  00 00 80 00    这5个字节内容写入到要解密的数据首部

接下来就是从mns.dat 的 5159个字节中读取有效的数据


protected function ReadConfusionStream(arg1:flash.net.URLStream, arg2:int, arg3:flash.utils.ByteArray, arg4:int):void
        {
							
            var loc1:*=10;
            var loc2:*;
            var loc3:*;
            if ((loc3 = (loc2 = arg4 % loc1 + arg4 / loc1 + loc1) % (loc1 * loc1)) == 0) 
            {
                loc3 = 24;
            }			
			
            var loc4:*=arg3.length;
            if ((arg4 = arg4 % loc3) <= 0) 
            {
                arg4 = loc3;
            }
            arg1.readBytes(arg3, loc4, arg4);
            var loc5:*=new flash.utils.ByteArray();
            arg1.readBytes(loc5, 0, arg4);
            arg1.readBytes(arg3, arg4 + loc4, arg2 - arg4 * 2 - (loc3 - arg4));
            if (loc3 > arg4) 
            {
                arg1.readBytes(loc5, 0, loc3 - arg4);
            }
            arg3.position = 0;
            return;
        }

这个arg1就是要解密的 5159 个字符

arg2 的值是5159 不解释

arg3是存放标准的带解密的LZMA数据 

arg4的值是3 ,也就是之前mns.dat文件分割点号的位置


它首先根据arg4进行一系列的运算得出一个loc3 这个值是作为 arg4的后备值,只有在 if ((arg4 = arg4 % loc3) <= 0)  的情况下才会有作用,暂时可以忽略。

这时候开始读数据 


第一次读取3个字节

从要解密的5159个字节的数据中读取3个字节到arg3中,也就是arg4的值,读取的数据放在properties值的后面

所以这时候 arg3的值就是8个字节

5D  00 00 80 00 2B B0 00

第二次读取3个字节 

它会将3个废弃的字节读取到loc5中

也就是说 16 E4 34 这三个字节是无效的。

在文件最后还有无效的字节,无效的字节数为 loc3 - arg4 = 10 个


所以它接下来要读取的有效字节数为   5159- 3*2 - 10 = 5143 个


那么最终要交给LZMA 解密的数据长度是 


数据头的5个字节properties

3字节的有效数据 2B B0 00

5143个有效数据 ...

共计 5151 个字节


其中舍弃的数据有3字节+末尾的10字节


此时已经将所有有效数据读取到arg3中了。接下来要做的就是通过标准LZMA解密就OK了。很奇怪的是用反汇编出来的工程里面的LZMA解密代码解密出现问题,解密出来的文件长度是对的,但解密出来的数据全都是m 后来找了几个版本的LZMA解密代码,c++的,as的都解密成功。为什么工程里面的无法解密,这真是让人费解,希望有大神能给个解答。


解密出来的数据长度是45099

文件部分内容如下

<?xml version="1.0" encoding="utf-8"?><Modules fcmKey="P%2BViyZLtO^gRT2Huxqx#5VygbflvZx$8mFpX61VWvd;ivPu~XjL`CD7FrIe8=0" sid="P%2BViyZLtO^gRT2Huxqx#5Vygbfl$8m" uuid="P%2BViyZLtO^gRT2Huxqx#5VygbflvZx$8mFpX61VWvd;ivPu~XjL`CD7FrIe8=0">
<Module name="Common">
<Dll label="公共组件" name="LyingDragon_Common" version="1.4.3.2"/>
<StaticData label="静态数据" name="KeyWord_zh_CN" version="1.4.2.1"/>
    </Module>
<Module name="BeforeLoad">
<Dll label="基础组件" name="LyingDragon_Base" version="1.4.3.2"/>
<Text label="文本" name="Text_zh_CN" version="1.4.3.4"/>
        <Image label="图标" name="qt">
            <File name="lvup.swf"/>
            <File name="QT046.swf"/>
            <File name="QT047.swf"/>
            <File name="QT048.swf"/>
            <File name="QT2.0.swf"/>
        </Image>
        <Image label="图标" name="CoreIcon">




mns_zh_CN.dat 成功解密


这里附上我测试成功的 LZMA 解密代码

参数就是要解密的LZMA加密的数据(数据格式必须如下注释所说properties 5个字节,数据长度标记8字节) 

返回值就是解密后的数据 

//解码方式  0-4 为5个字节的properties  5-12  8个字节为解压后的数据长度
		public static function decode(src:ByteArray) : ByteArray			
		{
			
			if (src == null || src.length < 16)				
			{				
				return null;				
			}
			
			var inStream:InputStream = new InputStream(src);
			
			var propertiesSize:int = 5;
			
			var properties:Vector.<uint> = new Vector.<uint>(propertiesSize);
			
			if (inStream.readVOL(properties, 0, propertiesSize) != propertiesSize)
				
				return null;
			
			var decoder:Decoder = new Decoder();
			
			if (!decoder.SetDecoderProperties(properties))
				
				return null;
			
			var outSize:uint = 0;
			
			for (var i:int = 0; i < 8; i++)
				
			{
				
				var v:int = inStream.read();
				
				if (v < 0)
					
					return null;
				
				outSize |= (v << (8 * i));
				
			}
			
			var ret:ByteArray = new ByteArray();
			
			ret.length = outSize;
			
			var outStream:OutputStream = new OutputStream(ret);
			
			if (!decoder.Code(inStream, outStream, outSize))
				
				return null;
			
			inStream.close();
			
			outStream.close();
			
			ret.position =0;
			
			return ret;
			
		}









/********************Witch_soya****************************/

/***********************2014-4-27*************************/


     

    

     

你可能感兴趣的:(对网页游戏《卧龙吟》的分析4-----加密xml文件mns_zh_CN.dat的解密过程)