zoukankan      html  css  js  c++  java
  • 浅谈网络游戏《天龙X部》的文件加密格式

    三月份时玩了某狐公司的网络游戏《天龙X部》,感觉还是蛮有意思的,遂研究了一下。
    这个游戏是利用开源游戏引擎OGRE进行开发的,看了一下目录里面的文件结构,主要的数
    据都放在Data目录下面。不过文件基本都是.AXP后缀的,每一个动辄几十兆,料想肯定是
    把游戏文件打包到一起并加密过的,GOOGLE未遂。开始用UE打开看了一下这个AXP文件,发
    现里面居然大部分都是明文的,开始以为只是把文件罗列在一起,不过仔细看了一下,发
    现每个文件都有一段间隔,前面还有一个数据头,而且文件与名字也无法对应。于是打开
    OD手动分析一下,主要过程其实比较简单,CreateFile函数下断,找到文件Buffer位置,
    再下内存访问断点即可来到关键代码区域。略过具体跟踪细节及文件校验部分不讲,文件
    格式主要分析如下:

    整个AXP文件可以分成四个部分:1.文件头 2.文件名索引 3.文件索引 4.文件数据


    1.文件头:
      整个文件头固定为0x28字节,其中第20个字节开始的一个整数乘以12代表了第三部分
    即文件索引部分的长度(因为每个索引有三个整数构成)
    2.文件名索引:
      整个文件名索引固定为0x60000字节,其中包含了每个压缩文件对应的文件索引位置

    3.文件索引:
      本部分长度由文件头相关数据决定,其中包含了每个压缩文件在.axp中的实际偏移位
    置及文件大小
    4.文件数据:
      本部分包含所有压缩文件的具体数据,每个文件之间用若干零填充。

    首先说说解压总体过程:比如我们要从A.axp中解压出一个叫file.txt的文件,那么先根据
    文件名file.txt到文件名索引中去找到对应的文件索引,然后再根据文件索引找到这个文
    件在axp文件中的位置和大小,最后把其解压出来。

    解压具体过程如下:
      将待解压的文件名转为小写(如果为英文字母),利用GetDisp(char* s,int v)函数
    计算相关数据,其中s代表文件名,v代表计算参数,分别计算GetDisp(fname,1),GetDisp
    (fname,2),
    GetDisp(fname,3),得到三个值a1,a2,a3。其中a3低位与在文件名索引中的位置有关,a3最
    高位及a1,a2用来进行校验,如果三个值不能同时满足要求,则将偏移位置顺移继续验证,
    具体细节懒得写了。
    以下为GetDisp函数具体内容,我直接将跟踪代码里面的汇编改造了一下拿出来用,其中s
    ucks为一个随机数数组,这里不列出来了。
    unsigned int TLBBUnpacker::GetDisp(char* s,int v)
    {
    __asm
    {
    push esi
    mov esi,s
    mov cl,byte ptr ds:[esi]
    test cl,cl
    mov eax,0x7FED7FED
    mov edx,0xEEEEEEEE
    je end
    push ebx
    push ebp
    push edi
    mov edi,v
    shl edi,0x8
    iter:
    add eax,edx
    imul edx,edx,0x21
    movsx ecx,cl
    lea ebx,dword ptr ds:[edi+ecx]
    mov ebp,dword ptr ds:[ebx*4+sucks]
    inc esi
    add edx,ecx
    mov cl,byte ptr ds:[esi]
    xor eax,ebp
    test cl,cl
    lea edx,dword ptr ds:[edx+eax+3]
    jnz iter
    pop edi
    pop ebp
    pop ebx
    end:
    pop esi
    }
    }
    这个为解压单个文件的函数GenerateFile,用到了QT作GUI,大家就当伪代码看吧。
    bool TLBBUnpacker::GenerateFile(QString name)
    {
    name=name.toLower();
    unsigned int a=GetDisp(name.toLocal8Bit().data(),3),a2=GetDisp(name.toLocal8B
    it().data(),2),a1=GetDisp(name.toLocal8Bit().data(),1),b,disp,length;
    a&=0x7FFF;
    while(!((b=((int*)buffer2)[a*3+2])&0x80000000)||a1!=((int*)buffer2)[a*3]||a2!
    =((int*)buffer2)[a*3+1])
    {
    a++;
    a&=0x7FFF;
    }
    b&=0x3FFFFFFF;
    disp=((int*)buffer3)[b*3];
    length=((int*)buffer3)[b*3+1];

    QFile pdata(this->package_name);
    if (pdata.open(QFile::ReadOnly)&&pdata.seek(disp)) {

    QString wdir=QDir::currentPath()+QDir::separator()+QFileInfo(pdata).fileName
    ()+QDir::separator();
    QDir dir;
    QDataStream pfin(&pdata);
    QFile file(wdir+name);
    QFileInfo info(file);
    dir.mkpath(info.absolutePath());
    if(file.open(QFile::WriteOnly))
    {
    char* pBuffer=new char[length];;
    QDataStream fout(&file);
    pfin.readRawData(pBuffer,length);
    fout.writeRawData(pBuffer,length);
    delete []pBuffer;
    return true;
    }
    }
    return false;
    }
    得到待解压文件在文件索引中的位置後就可以找出该文件在axp文件中的具体偏移量和文件
    大小了,然后直接fseek一下然后在弄出来就OK了。
    最后说一下,这个AXP压缩包本身就含有一个文件列表文件叫做(list),所以每次只要先解
    压缩这个文件,然后按照里面的文件列表来一一解压缩就OK了。


    以上就是文件大致格式,感觉还是比较简单的,也可以考虑在自己的项目中使用类似方法
    进行文件压缩。

    PS:本文仅供学习,本人不负任何责任。。。。
  • 相关阅读:
    垂死挣扎还是涅槃重生 -- Delphi XE5 公布会归来感想
    自考感悟,话谈备忘录模式
    [每日一题] OCP1z0-047 :2013-07-26 alter table set unused之后各种情况处理
    Java实现 蓝桥杯 算法提高 p1001
    Java实现 蓝桥杯 算法提高 拿糖果
    Java实现 蓝桥杯 算法提高 拿糖果
    Java实现 蓝桥杯 算法提高 求arccos值
    Java实现 蓝桥杯 算法提高 求arccos值
    Java实现 蓝桥杯 算法提高 因式分解
    Java实现 蓝桥杯 算法提高 因式分解
  • 原文地址:https://www.cnblogs.com/MaxWoods/p/1401125.html
Copyright © 2011-2022 走看看