半年前看书看得一个简单的加密算法,拖了好几个月 通过上周五和今天的努力 终于调试出效果 所以记录一下
加密算法
void HelloWorld::encipher( const unsigned long *const v,unsigned long *const w,const unsigned long *const k )
{
unsigned long y = v[0];
unsigned long z = v[1];
unsigned long sum = 0;
unsigned long deltas = 0x9E3779B9;
unsigned long n = 32;
while(n-- >0)
{
y += (z<<4^z>>5) + z^sum + k[sum&3];
sum += deltas;
z += (y<<4^y>>5) + y^sum + k[sum>>11 & 3];
}
w[0] = y;
w[1] = z;
}
解密算法
void HelloWorld::decipher( const unsigned long *const v,unsigned long *const w,const unsigned long *const k )
{
unsigned long y = v[0];
unsigned long z = v[1];
unsigned long sum = 0xC6EF3720;
unsigned long deltas = 0x9E3779B9;
unsigned long n = 32;
while(n– > 0)
{
z -= (y<<4^y>>5) + y^sum + k[sum >> 11 & 3];
sum -= deltas;
y -= (z<<4^z>>5) + z^sum + k[sum & 3];
}
w[0] = y;
w[1] = z;
}
加密调用
void HelloWorld::encode()
{
const int nchar = 2*sizeof(long);
const int kchar = 2*nchar;
std::string key = "xcsmxxxxxxxxxxxxxxxx";
std::string infile = "grossini.png";
std::string outfile = "111.txt";
while (key.size() < kchar)
{
key += "0";
}
std::ifstream inf(infile.c_str());
std::ofstream outf(outfile.c_str());
if(!inf || !outf)
CCLOG("bag file name");
const unsigned long *k = reinterpret_cast<const unsigned long*>(key.data());
unsigned long outptr[2];
char inbuf[nchar];
unsigned long* inptr = reinterpret_cast<unsigned long*>(inbuf);
int count = 0;
while(inf.get(inbuf[count]))
{
outf << std::hex;
if (++count == nchar)
{
encipher(inptr,outptr,k);
outf<<std::setw(8)<<std::setfill('0')<<outptr[0]<<' '
<<std::setw(8)<<std::setfill('0')<<outptr[1]<<' ';
//outf.write((char*)&outptr,sizeof(outptr))
count = 0 ;
}
}
if (count)
{
while (count != nchar)
{
inbuf[count++] = '\0';
encipher(inptr,outptr,k);
outf<<outptr[0]<<' '<<outptr[1]<<' ';
}
}
}
解密调用
void HelloWorld::decode()
{
const int nchar = 2*sizeof(long);
const int kchar = 2*nchar;
std::string key = "xcsmxxxxxxxxxxxxxxxx";
std::string infile = "111.txt";
std::string outfile = "222.txt";
while (key.size() < kchar)
{
key += "0";
}
std::ifstream inf(infile.c_str());
std::ofstream outf(outfile.c_str());
const unsigned long *k = reinterpret_cast<const unsigned long*>(key.data());
unsigned long inptr[2];
char outbuf[nchar+1];
outbuf[nchar] = '\0';
unsigned long* outptr = reinterpret_cast<unsigned long*>(outbuf);
inf.setf(std::ios_base::hex,std::ios_base::basefield);
while(inf>>inptr[0]>>inptr[1])
{
decipher(inptr,outptr,k);
outf<<outbuf;
}
}
主要原理是while循环读入字符到输入缓冲区inbuf中,每次将8个字符传递给encipher()进行加密。经过测试加密类似lua的文本是没问题的
不过现在这个东西还有点问题,图片的加密和解密有点问题 虽然可以加密出密文 但解密出来的图片打不开(变小了),查了下 可能是读信息的时候跳行了 导致加密不完整 解密不完整 查了关于c++ ifstream和ofsteam的一些资料后 我试过用 打开二进制文件的方法打开文件 用write和read进行写入和读取 这次加密出来的密文多了很多 应该没跳行了 但是解密出来的文件还是略小于源文件 还是打不开 不知道操作错了什么 希望有大神看到知道的话可以指导下 ifstream和ofstream怎么读取和写入图片信息
后来我发现别人加解密资源不用这么麻烦和复杂 综合了别人的东西一并记录
首先写个加密方法
bool PublicCommen::recode_getFileByName(string pFileName){
unsigned long nSize = 0;
unsigned char* pBuffer = CCFileUtils::sharedFileUtils()->getFileData(
pFileName.c_str(),
"rb",&nSize);
unsigned char* newBuf = new unsigned char[nSize];
int newblen = nSize;
if(pBuffer!=NULL&&nSize>0)
{
for (int i = 0; i<nSize; i++) {
newBuf[i]=pBuffer[i]+MD5;
}
string savepath = pFileName;
savepath = savepath.substr(0,savepath.length()-4);
savepath = savepath + "xx.X";
FILE *fp = fopen(savepath.c_str(), "wb+");
fwrite(newBuf, 1, newblen, fp);
fclose(fp);
CCLOG("save file ok. path = %s" ,savepath.c_str());
return true;
}
return false;
}
unsigned char* pBuffer = CCFileUtils::sharedFileUtils()->getFileData(pFileName.c_str(),”rb”,&nSize);
用cocos2dx的底层代码比我用那个ifstream和ofstream真是棒太多,获取了流信息后循环一个个字符加密改变 这个方法也可以放进你游戏中修改cocos2dx底层的CCFileUtils::fullPathForFilename获取全路径的方法中 这样就统一调用了
图片解密3.x版本修改在ccimage的initWithImageData
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;
//detecgt 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 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;
case Format::ATITC:
ret = initWithATITCData(unpackedData, unpackedLen);
break;
case Format::ATITC:"XX.X":
ret = initWithXX.XData(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
{
CCAssert(false, "unsupport image format!");
}
free(tgaData);
break;
}
}
if(unpackedData != data)
{
free(unpackedData);
}
} while (0);
return ret;
}
bool CCImage::initWithXX.XData()
{
bool bRet = false;
unsigned long nSize = 0;
unsigned char* pBuffer = CCFileUtils::sharedFileUtils()->getFileData(
CCFileUtils::sharedFileUtils()->fullPathForFilename(strPath).c_str(),
"rb",
&nSize);
for (int i= 0; i < nSize; i++) {
pBuffer[i] = pBuffer[i]-MD5;
}
pBuffer[nSize] = pBuffer[nSize]-1;
eImgFmt = kFmtPng;
if (pBuffer != NULL && nSize > 0)
{
bRet = initWithImageData(pBuffer, nSize, eImgFmt);
}
CC_SAFE_DELETE_ARRAY(pBuffer);
return bRet;
}
这个没上面那个复杂,但是思路还是很清晰的,lua文件也是类似需要找到解密的地方
:executeScriptFile找到实现在libs/lua/lua/lauxlib.c的luaL_loadfile 再找到lua读入脚本文件的方法lua_load
status = lua_load(L, getF, &lf, lua_tostring(L, -1));
再找到getF
static const char *getF (lua_State *L, void *ud, size_t *size) {
LoadF *lf = (LoadF *)ud;
(void)L;
if (lf->extraline) {
lf->extraline = 0;
*size = 1;
return "\n";
}
if (feof(lf->f)) return NULL;
memset(lf->buff, 0, sizeof(lf->buff));
//将文件读入LoadF结构体的buff内存, 并将实际读到的长度赋予size,
*size = fread(lf->buff, 1, sizeof(lf->buff), lf->f);
return (*size > 0) ? lf->buff : NULL;
}
经过加密的文件, 在fread之后把buff的数据进行解密即可, 就像这样:
static const char *getF (lua_State *L, void *ud, size_t *size) {
...
//将文件读入LoadF结构体的buff内存, 并将实际读到的长度赋予size,
*size = fread(lf->buff, 1, sizeof(lf->buff), lf->f);
//将buff内容解密
*size = aes256_decrypt(key, lf->buff,*size);
return (*size > 0) ? lf->buff : NULL;
}
需要注意的是
文件的大小大于LUAL_BUFFERSIZE时, 会分多次读入
LUAL_BUFFERSIZE的大小必须是加密块的整倍数
解密之后必须将size赋值为strlen. 例如块大小为32字节, 加密3B的文件内容123后文件大小还是为32B, 解密后buff内容为 0x30,0x31,0x32,0x00….0x00, 会出错
这些就是别人的一些思路,虽然清晰 但分开还是挺麻烦的 而我们这边的思路是写把这些解密的方法放在读取文件必经的方法里,也就是和CCFileUtils相关的地方,分别是CCFileUtilsAndroid,FileUtilsWin32的getdata,CCFileUtilsApple的getValueMapFromFile里
今天补充一下 我们的解密放在CCFileUtils相关的地方是比上面说的lua_load更前,所以lua_load的时候已经是明文了 这种方法比较好 改跟引擎相关的地方就行了 符合一般人的思维 不用跑到lua那里去改 毕竟不是人人都搞得懂lua的文件载入代码