【手游】童话大冒险 美术资源加密分析

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/BlueEffie/article/details/51037953

这是一款Q萌横版街机冒险闯关手游,使用的引擎是Cocos2dx-lua


0x00 先用WinHex看看游戏资源

所有的资源文件都被加密了,而且是同一种加密方式,加密后的文件以@gf@开头

经过逆向分析后知道 前8个字节是文件头,9~12字节是解压后的大小,13~16字节是压缩前的大小,17~20字节未知,数据从21字节开始也就是0xE8 0xE8……



0x01 在IDA中分析libcocos2dlua.so,发现加密处理的方法是在cocos2d::G33InPackageFileInfo::GetFileData中调用cocos2d::PreprocessFileData


0x02 在PreprocessFileData中先是通过cocos2d::DecryptData解密,在通过cocos2d::ZipUtils::G33Uncompress解压缩



0x03 先看看cocos2d::DecryptData中是如何解密的...这个程序猿木有用自定义的加密算法,而是用的RC4加密算法,在cocos2d::GenerateKey中找到Key就可以解密了


这个就是RC4算法的密钥



0x04 在看看cocos2d::ZipUtils::G33Uncompress中用的是什么压缩算法

我之前以为是ZIP压缩算法,但是解密后我发现文件在21字节处(数据段头部)是0x78 0x9C......,这个是zlib数据头(非固定 参考RFC 1951)



0x05 经过上面的分析我用C#写了个小工具,这里只给出关键方法

注意C# 版的zlib库 可在http://www.componentace.com/zlib_.NET.htm 下载

其他语言的zlib库 在http://www.zlib.net/ 自行寻找下载

//遍历查找文件
privatevoid FindFile(string dirPath) //参数dirPath为指定的目录 
{
    DirectoryInfo Dir = new DirectoryInfo(dirPath);
    try
    {
        //查找子目录 
        foreach (DirectoryInfo d in Dir.GetDirectories())
        {
            FindFile(Dir + "\\" + d.ToString());
        }

        //查找文件 
        foreach (FileInfo f in Dir.GetFiles("*.*"))
        {
            ReadSCFile(f);
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}

//读取加密文件
private void ReadSCFile(FileInfo f)
{
    using (FileStream inStream = new FileStream(f.FullName, FileMode.Open, FileAccess.ReadWrite))
    {
        byte[] ThdmxBytes = new byte[inStream.Length];
        inStream.Read(ThdmxBytes, 0, ThdmxBytes.Length);
        inStream.Close();

        if (IsThdmxFile(ThdmxBytes))
        {
            string outPath = Path.Combine(f.DirectoryName, Path.GetFileNameWithoutExtension(f.FullName) + f.Extension);
            byte[] rc4Bytes = DecryptRC4(ThdmxBytes);
            DecompressZlibFile(rc4Bytes, outPath);

        }
    }
}

//判断是否是Thdmx文件
private bool IsThdmxFile(byte[] bytes)
{
    byte[] Thdmx_Head = new byte[] { 0x40, 0x67, 0x66, 0x40 };

    if (bytes.Length > Thdmx_Head.Length)
    {
        int count = 0;
        for (int i = 0; i < Thdmx_Head.Length; i++)
        {
            if (bytes[i] == Thdmx_Head[i])
                count++;
        }

        if (count == Thdmx_Head.Length)
            return true;
        else
            return false;
    }
    else
    {
        return false;
    }
}

//解密文件
private byte[] DecryptRC4(byte[] ThdmxBytes)
{
    byte[] rc4Bytes = new byte[ThdmxBytes.Length - 20];
    Array.Copy(ThdmxBytes, 20, rc4Bytes, 0, rc4Bytes.Length);

    byte[] key = new byte[8] { 0x9, 0x15, 0x21, 0x2D, 0x39, 0x45, 0x51, 0x5D };

    return RC4Dncrypt(rc4Bytes, key);
}

//RC4初始化
private byte[] RC4Init(byte[] pass, int kLen)
{
    byte[] mBox = new byte[kLen];

    for (long i = 0; i < kLen; i++)
    {
        mBox[i] = (byte)i;
    }
    long j = 0;
    for (long i = 0; i < kLen; i++)
    {
        j = (j + mBox[i] + pass[i % pass.Length]) % kLen;
        byte temp = mBox[i];
        mBox[i] = mBox[j];
        mBox[j] = temp;
    }
    return mBox;
}

//RC4算法
private byte[] RC4Dncrypt(byte[] data, byte[] pass)
{
    if (data == null || pass == null) return null;
    byte[] output = new byte[data.Length];
    long i = 0;
    long j = 0;
    byte[] mBox = RC4Init(pass, 256);

    // 加密
    for (long offset = 0; offset < data.Length; offset++)
    {
        i = (i + 1) % mBox.Length;
        j = (j + mBox[i]) % mBox.Length;
        byte temp = mBox[i];
        mBox[i] = mBox[j];
        mBox[j] = temp;
        byte a = data[offset];
        byte b = mBox[(mBox[i] + mBox[j]) % mBox.Length];
        output[offset] = (byte)(a ^ b);
    }

    return output;
}


//解压zlib文件
private void DecompressZlibFile(byte[] bytes, string outFile)
{
    using (MemoryStream inStream = new MemoryStream(bytes))
    {
        using (FileStream outStream = new FileStream(outFile, FileMode.Create))
        {
            ZOutputStream outZStream = new ZOutputStream(outStream);
            try
            {
                CopyStream(inStream, outZStream);
            }
            catch(Exception ex)
            {
                
            }
        }
    }
}

private void CopyStream(Stream input, Stream output)
{
    byte[] buffer = new byte[2000];
    int len;
    while ((len = input.Read(buffer, 0, 2000)) > 0)
    {
        output.Write(buffer, 0, len);
    }
    output.Flush();
}

资源提取源码

链接:http://pan.baidu.com/s/1slvnIbz 密码:jwzn

猜你喜欢

转载自blog.csdn.net/BlueEffie/article/details/51037953