← 回到文章列表

FFxivARR SqPack 拆包记录 4 (Final)

这章应该算是SqPack的最终拆解记录了 在之前的几篇文章中已经说了 如何定位到各种文件块 并且解析文件头以获取文件大小 以下需要做到的仅仅是提取文件而已 按照上一章的内容来说 我们分析到的各个文件都是有一个 0x10长度的文件头 记录了文件的大小 那么我们就从文…

这章应该算是SqPack的最终拆解记录了
在之前的几篇文章中已经说了
如何定位到各种文件块
并且解析文件头以获取文件大小
以下需要做到的仅仅是提取文件而已

按照上一章的内容来说
我们分析到的各个文件都是有一个
0x10长度的文件头
记录了文件的大小
那么我们就从文件头之后
开始转储文件内容 按照解压前的大小
顺带这里讲一个小段子吧
因为之前从文件头分析出文件压缩过
但是不清楚是否压缩以及加密
就找了个朋友发了几段内容给他
在即将发出第二段内容的时候
这个朋友就回我是Zlib压缩了
说这么明显的尾部特征…
看来我真的是鸡丁~
言归正传
既然知道只是Zlib加密的话
获取到文件内容之后
并且知道文件压缩前后大小
那么直接调用Zlib解压就好
顺带说明下
这里提取的文件块内容
的的确确是Zlib压缩产物
不过不带Zlib的标准头和AB32尾巴
所以需要自己加上头尾
然后交给Zlib解压导出文件即可
老样子 代码如下:

    private static void outputFile(RandomAccessFile randomAccessFile,long addr) throws Exception{
        //進行壓縮前後文件長度獲取
        int[] size = getFileLength(randomAccessFile,addr);
        System.out.println("文件大小爲:"+Arrays.toString(size));
//  TODO之後再寫有問題的處理塊方法 先跳過
if(size[0]/size[1]<250){
        //將指針偏移到數據開始部分
        randomAccessFile.seek(addr+0x10);
        //開始讀取到壹個臨時數組
        byte[] tmp = new byte[size[0]];
        for(int i=0;i<(int)size[0];i++){
            tmp[i] = randomAccessFile.readByte();
        }
        //准備好頭和尾
        byte header[] = {(byte) 0x58,(byte) 0x85};
        byte footer[] = HexUtils.HexString2Bytes(Long.toHexString(adler32(tmp,(int)size[0])));
        //組合數組
        byte[] hexBytes = new byte[header.length+tmp.length+footer.length];
        System.arraycopy(header, 0, hexBytes, 0, header.length);
        System.arraycopy(tmp, 0, hexBytes, header.length , tmp.length);
        System.arraycopy(footer, 0, hexBytes, header.length+tmp.length , footer.length);
        //解壓
        byte[] uncompr=uncompress(hexBytes,(int)size[1]);
        //提取解壓之後的文件頭說明 設置爲文件的擴展名
        byte[] extension = new byte[8];
        System.arraycopy(uncompr, 0, extension, 0, 8);
        //輸出文件
        File fileOut = new File("D:\\sqdat\\"+Long.toHexString(addr)+".dat");
        FileOutputStream fos = new FileOutputStream(fileOut);
        fos.write(uncompr);
        fos.flush();
        fos.close();
}
    }
    //Mark工具
    private static int adler32(byte[] inB,int size){
        final int a32mod=65521;
        int s1=1,s2=0;
        for(int i=0;i<size;i++){
            int b = inB[i];
            s1 = (s1 + b )%a32mod;
            s2 = (s2 + s1)%a32mod;
        }
        return (int)((s2<<16)+s1);
    }
    //Zlib解壓工具
    private static byte[] uncompress(byte[] data,int length){
          int err;
          int uncomprLen = length;
          byte[] uncompr = new byte[uncomprLen];
          ZStream d_stream = new ZStream();
          err = d_stream.inflateInit();
          CHECK_ERR(d_stream, err, "inflateInit");
          d_stream.next_in = data;
          d_stream.next_in_index = 0;
          d_stream.next_out = uncompr;
          d_stream.next_out_index = 0;
          while (d_stream.total_out < uncomprLen
            && d_stream.total_in < uncomprLen) {
           d_stream.avail_in = d_stream.avail_out = 1;
           err = d_stream.inflate(JZlib.Z_NO_FLUSH);
           if (err == JZlib.Z_STREAM_END) {
            break;
           }
           CHECK_ERR(d_stream, err, "inflate");
          }  
          err = d_stream.inflateEnd();
          CHECK_ERR(d_stream, err, "inflateEnd");
          byte[] unzipfile = new byte[(int) d_stream.total_out];
          System.arraycopy(uncompr, 0, unzipfile, 0, unzipfile.length);
          return unzipfile;
     }
    //Zlib解壓檢錯工具
     static void CHECK_ERR(ZStream z, int err, String msg) {
            if(err!=JZlib.Z_OK){
              if(z.msg!=null) System.out.print(z.msg+" "); 
              System.out.println(msg+" error: "+err); 
              System.exit(1);
            }
     }

至于运行之后的内容
大家去找输出文件夹吧
顺带说下文本文件是使用UTF-8编码的喔~
最后给出Zlib的jar包: [媒体不可用] 以上 转载请说明出处喔~

在这里找找

输入关键词,找一篇过去的记录。