天天看點

CocosCreator圖檔資源加密解密

文章轉載自:http://www.cnblogs.com/pixs-union/p/6226337.html

主要處理png圖檔,其他格式圖檔也是一樣的原理。閱讀前可以簡略了解一下png格式圖檔的Data trunck。

     首先使用python腳本去掉png的PNG SIG(8 bytes) 以及末尾的PNGIEND(12 bytes)。然後圖檔剩餘資料的每一個位元組和秘鑰字元串的每一個字元做不進位加(按位異或,解密的原理就是 a ^ b ^ b = a)。通過改寫cpp工程裡的 Image::initWithImageData(const unsigned char * data, ssize_t dataLen),來進行還原圖檔資料,也就是解密,過程就是對每一位加密後的資料按秘鑰做異或運算還原資料,然後加上PNGSIG和PNGIEND。

加密腳本:encode.py(這個腳本會生成加密後的圖檔替換原始圖檔)

# -*- coding:UTF-8 -*-
#該腳本用于加密png圖檔 使用python3以上版本解釋執行
__author__ = "ChenGuanzhou"

import os
import time
CUR_DIR = os.getcwd();
print("cur_dir:",CUR_DIR)
#CUR_DIR = 'C:\\Users\\chenguanzhou\\Desktop'
_KEY = 'jiaozi2013' #指定加密秘鑰,英文
_ENCRYSIG = 'jiaozhi'
_PNGSIG = bytes([0x89,0x50,0x4e,0x47,0x0d,0x0a,0x1a,0x0a])
_PNGIEND = bytes([0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, 0xAE, 0x42, 0x60, 0x82])
 #擷取filesig是否是png
def isPNGSig(bytes_8):
    return bytes_8 == _PNGSIG

def isPNG(absPath):#判斷是否是PNG圖檔
    """
    :param absPath: 檔案的絕對路徑
    :return: {Bool}
    """
    isFile = os.path.isfile(absPath)
    hasPNGSig = False
    fileExt = os.path.splitext(absPath)[1]
    isPngExt = (fileExt == ".png" or fileExt == ".PNG")
    if isFile and isPngExt:
        with open(absPath,"rb") as file:
            hasPNGSig = isPNGSig(file.read(8)[:8])
    return isFile and isPngExt and hasPNGSig

def preProcessPng(pngData):#預處理圖檔資料
    """
    剪掉png的signature(8bytes),IEND(12Bytes)
    :param pngData:
    :return:
    """
    assert type(pngData) == bytes
    lostHeadData = pngData[8:]
    iendData = lostHeadData[-12:]
    if iendData == _PNGIEND:#防止Png已經進行過外部軟體的壓縮,丢掉了IEND
        return lostHeadData[:-12]
    else:
        return lostHeadData

def encryption(fileData,key):#加密操作 ascii占一個位元組
    """
    加密png資料
    :param fileData:{bytes}預處理後的png資料
    :param key:{str}秘鑰
    :return:{bytes}加密後的資料
    """
    assert type(key) is str
    k = bytes(key,"utf8")
    klen= len(k)
    kindex = 0
    fileData = bytearray(fileData)
    for i,v in enumerate(fileData):
        if kindex >= klen:
            kindex = 0
        fileData[i] = v ^ k[kindex]#加密
        kindex = kindex + 1
    return bytes(_ENCRYSIG,"utf8") + fileData

#處理圖檔
def processPNG(filePath):
    global filenum
    fileData = None
    with open(filePath,'rb') as file:
        fileData = encryption(preProcessPng(file.read()),_KEY)
    os.remove(filePath)
    with open(filePath,'wb') as file: #覆寫新檔案
        file.write(fileData)
    filenum = filenum + 1



def traverseDir(absDir):#周遊目前目錄以及遞歸的子目錄,找到所有的png圖檔
    """
    :param absDir: 要周遊的路徑
    :return: None
    """
    assert (os.path.isdir(absDir) and os.path.isabs(absDir))
    dirName = absDir
    for fileName in os.listdir(absDir):
        absFileName = os.path.join(dirName,fileName)
        print("isPNG:",absFileName)
        if os.path.isdir(absFileName):#遞歸查找檔案夾
            print("absFileName:",absFileName)
            traverseDir(absFileName)
        elif isPNG(absFileName):
            print("isPNG:",absFileName)
            processPNG(absFileName)
        else:
            pass


 #------------------- 主函數-------------------------#
start_clock = time.clock()
filenum = 0
#traverseDir(os.path.join(CUR_DIR,"png2"))
traverseDir(CUR_DIR)
end_clock = time.clock()
time = (end_clock - start_clock)*1000
print("encrypt %d Png Pictures"%filenum)
print("use time %fms"%time)
           

解密的cpp檔案

1.新增圖檔類型

enum class Format
{
    //! JPEG
    JPG,
    //! PNG
    PNG,
    //! ENCRYPTEDPNG,下面這行是新加的
    ENCRYPTEDPNG, //加密後的Png圖檔
    //! TIFF
    TIFF,
    //! WebP
    WEBP,
    //! PVR
    PVR,
    //! ETC
    ETC,
    //! S3TC
    S3TC,
    //! ATITC
//    ATITC,
    //! TGA
    TGA,
    //! Raw Data
    RAW_DATA,
    //! Unknown format
    UNKNOWN
};
           

2.新增Image::isEncryptedPng方法:

bool Image::isEncryptedPng(const unsigned char *data, ssize_t dataLen)
{
    if (dataLen <= 7 || memcmp("jiaozhi", data, 7) != 0) {
        return false;
    }
    return true;
}
           

3.修改檢測圖檔類型的Image::detectFormat方法:

Image::Format Image::detectFormat(const unsigned char * data, ssize_t dataLen)
{
    if (isPng(data, dataLen))
    {
        return Format::PNG;
    }
    //新增判斷
    else if (isEncryptedPng(data, dataLen)) {
        return Format::ENCRYPTEDPNG;
    }
    else if (isJpg(data, dataLen))
    {
        return Format::JPG;
    }
    else if (isTiff(data, dataLen))
    {
        return Format::TIFF;
    }
    else if (isWebp(data, dataLen))
    {
        return Format::WEBP;
    }
    else if (isPvr(data, dataLen))
    {
        return Format::PVR;
    }
    else if (isEtc(data, dataLen))
    {
        return Format::ETC;
    }
    else if (isS3TC(data, dataLen))
    {
        return Format::S3TC;
    }
    else
    {
        return Format::UNKNOWN;
    }
}
           

4.修改Image::initWithImageData,進行解密png

bool Image::initWithImageData(const unsigned char * data, ssize_t dataLen)
{
    bool ret = false;

    do
    {
        CC_BREAK_IF(! data || dataLen <= 0);

        unsigned char* unpackedData = nullptr;
        ssize_t unpackedLen = 0;

        //detect and unzip the compress file
        if (ZipUtils::isCCZBuffer(data, dataLen))
        {
            unpackedLen = ZipUtils::inflateCCZBuffer(data, dataLen, &unpackedData);
        }
        else if (ZipUtils::isGZipBuffer(data, dataLen))
        {
            unpackedLen = ZipUtils::inflateMemory(const_cast<unsigned char*>(data), dataLen, &unpackedData);
        }
        else
        {
            unpackedData = const_cast<unsigned char*>(data);
            unpackedLen = dataLen;
        }

        _fileType = detectFormat(unpackedData, unpackedLen);

        switch (_fileType)
        {
            //這個case塊是增加的内容
            case Format::ENCRYPTEDPNG:
            {
                unsigned char* copyData = new unsigned char[unpackedLen + 13]; // 8 + 12 - 7
                memcpy(copyData + 8, unpackedData + 7, unpackedLen - 7);
                deEncryptPng(&copyData, "jiaozi2013", unpackedLen + 13);
                ret = initWithPngData(copyData, unpackedLen + 13);
                delete [] copyData;
            }
            break;
        case Format::PNG:
            ret = initWithPngData(unpackedData, unpackedLen);
            break;
        case Format::JPG:
            ret = initWithJpgData(unpackedData, unpackedLen);
            break;
        case Format::TIFF:
            ret = initWithTiffData(unpackedData, unpackedLen);
            break;
        case Format::WEBP:
            ret = initWithWebpData(unpackedData, unpackedLen);
            break;
        case Format::PVR:
            ret = initWithPVRData(unpackedData, unpackedLen);
            break;
        case Format::ETC:
            ret = initWithETCData(unpackedData, unpackedLen);
            break;
        case Format::S3TC:
            ret = initWithS3TCData(unpackedData, unpackedLen);
            break;
        default:
            {
                // load and detect image format
                tImageTGA* tgaData = tgaLoadBuffer(unpackedData, unpackedLen);

                if (tgaData != nullptr && tgaData->status == TGA_OK)
                {
                    ret = initWithTGAData(tgaData);
                }
                else
                {
                    CCLOG("Image: unsupported image format!");
                }

                free(tgaData);
                break;
            }
        }

        if(unpackedData != data)
        {
            free(unpackedData);
        }
    } while (0);

    return ret;
}
           

5.新增Image::deEncryptPng做解密:

void Image::deEncryptPng(unsigned char **copyData, const char *key, ssize_t dataLen) {
    static const unsigned char PNG_SIGNATURE[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a };
    static const unsigned char PNG_IEND[] = { 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, 0xAE, 0x42, 0x60, 0x82 };
    unsigned char* data = *copyData;
    memcpy(data, PNG_SIGNATURE, 8);
    memcpy(data + (dataLen - 12), PNG_IEND, 12);
    unsigned char* destart = data + 8;
    unsigned char* de_end = data + dataLen - 13;
    ssize_t keyLen = strlen(key);
    ssize_t keyIndex = 0;
    for(; destart <= de_end; destart++, keyIndex++) {
        if (keyIndex >= keyLen)
            keyIndex = 0;
        *destart ^= key[keyIndex];
    }
}
           

最後加一句:python大法好。

繼續閱讀