这是一款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