1,加密,采用blowfish或其他
2,自定是32个字符的混淆code
3,对文件做blowfish加密,入口文件加密前将混淆code按约定格式(自定义的文件头或文件尾部)写入到文件
4,遍历资源目录,对每个文件做md5混淆,混淆原始串=“相对路径”+“文件名”+混淆code,
文件改名并且移动到资源目录根目录,清除原始目录
入口文件除外,因为入口文件也混淆的话就只能把混淆code写入到加密程序中,不方便频繁修改,留个入口文件就能在程序运行最开始的地方读取到混淆code
5,引擎c++代码层修改文件检索,CCFileUtils::fullPathForFilename,获取md5混淆后的文件名,混淆原始串=“相对路径”+“文件名”+混淆code
6,拿到目标文件名后,blowfish对文件数据解码读取文件
因为混淆用的md5,混淆码是写在加密后的入口文件内的,所以破解出文件名就只能解开入口文件,但入口文件的加密过的,加密解密以及加密code是在c++层面做的,只通过资源或者脚本文件不可能破解出来,除非有能力把C++代码反编译出来.
是否有其他更牛逼的手段破解不知道,但总归现在这种方式已经将破解代价提到很高了,目的已经达到
脚本范例
require 'optparse'
require 'digest/md5'
require 'fileutils'
require './utils'
jobs = 1
OptionParser.new do |cfg|
cfg.banner = "Usage:"
cfg.on('-j', '--jobs num', 'jobs num') { |v| jobs = v.to_i }
end.parse!
dir = ARGV.shift()
# dir = 'E:/WorkSpace/Kapai/Games/xjlqiming/build/data'
raise if not jobs >= 1
raise if not dir
puts "加密目录:"+dir
#获取混淆串
key = ARGV.shift
#输出目录
out = ARGV.shift
if out
puts dir,out
u_copy_underDir dir,out
dir = out
end
dir = u_UTF8 dir
dir = u_win2Utf dir
files = Dir.glob %Q[#{dir}/**/*]
files.delete_if do |f|
name_slice = f.split "data/"
name = f.sub name_slice[0]+"data/",""
name.length == 32 and not name.index('.')
end
files.delete_if do |f|
not File.file? f or
f.end_with? ".plist" or
f.end_with? ".ttf" or
f.end_with? ".mp3" or
f.end_with? ".caf" or
f.end_with? ".wav" or
f.end_with? "config/Config.xml" or
f.end_with? "config/ServerConfig.xml"
end
job_files = []
(0...jobs).each { |i| job_files[i] = [] }
raise if not job_files.length() == jobs
files.each_index do |i|
job_files[i%jobs].push files[i]
end
job_threads = []
(0...jobs).each do |i|
job_threads[i] = Thread.start(i) do |i|
job_files[i].each() do |f|
print "\n加密文件#{f} "
cmd = %Q[#{File.dirname(__FILE__)}/encryption.exe e "#{f}"]
if key and key.length == 32 then cmd = cmd + " #{key}" end
puts cmd
if not system cmd then
raise "ERROR:"+cmd
end
end
end
job_threads[i].abort_on_exception = true
end
(0...jobs).each do |i|
job_threads[i].join
end
puts "开始混淆......................#{key}"
sleep 3
if key and key.length == 32 then
job_threads = []
(0...jobs).each do |i|
job_threads[i] = Thread.start(i) do |i|
job_files[i].each() do |f|
next if f =~ /script\/main.lua/
#只保留相对路径
name_slice = f.split "data/"
name = f.sub name_slice[0]+"data/",""
raise "name 为空" unless name
#跳过已经混淆的文件
next if name.length == 32 and not name.index('.')
#添加混淆串
print "文件混淆#{name}"
name = name + key if key
md5 = Digest::MD5.new
md5_name = md5.hexdigest name
raise "生成混淆代码失败" unless md5_name
#避免文件占用,先打开一下尝试状态
File.open(f) do |ff| end
FileUtils.mv f,"#{dir}/#{md5_name}",:verbose => true,:force => true
end
end
job_threads[i].abort_on_exception = true
end
(0...jobs).each do |i|
job_threads[i].join
end
end
#删除空目录
sleep 3
puts "清除空目录.................."
files = Dir.glob %Q[#{dir}/**/*]
files.each_index do |index|
f = files[index]
if File.directory? f then
file_count = 0
arr = Dir.glob %Q[#{f}/**/*]
arr.each_index do |i|
file_count = file_count + 1 unless File.directory? arr[i]
end
FileUtils.rm_r f,:verbose => true,:force => true if file_count == 0
end
end
引擎文件检索修改示例
std::string CCFileUtils::fullPathForFilename(const char* pszFileName)
{
CCAssert(pszFileName != NULL, "CCFileUtils: Invalid path");
std::string strFileName = pszFileName;
if (strFileName == "cc_2x2_white_image" || strFileName == "cc_fps_images"){
return pszFileName;
}
if (isAbsolutePath(pszFileName))
{
//CCLOG("Return absolute path( %s ) directly.", pszFileName);
return pszFileName;
}
else
{
int index = strFileName.find("script/logo.lua");
if ( strFileName.find(".plist") == -1
&& strFileName.find(".ttf") == -1
&& strFileName.find(".mp3") == -1
&& strFileName.find(".caf") == -1
&& strFileName.find(".wav") == -1
&& strFileName.find("script/main.lua") == -1)
{
if (m_data_key != NULL && m_data_key->size() > 0)
{
string a;
for (int i = 0; i < m_data_key->size(); ++i){
a = a + (char)((*m_data_key)[i]);
}
strFileName = strFileName + a;
strFileName = Md5Encode(strFileName);
}
}
}
// Already Cached ?
std::map<std::string, std::string>::iterator cacheIter = m_fullPathCache.find(pszFileName);
if (cacheIter != m_fullPathCache.end())
{
//CCLOG("Return full path from cache: %s", cacheIter->second.c_str());
return cacheIter->second;
}
// Get the new file name.
std::string newFilename = getNewFilename(strFileName.c_str());
string fullpath = "";
for (std::vector<std::string>::iterator searchPathsIter = m_searchPathArray.begin();
searchPathsIter != m_searchPathArray.end(); ++searchPathsIter) {
for (std::vector<std::string>::iterator resOrderIter = m_searchResolutionsOrderArray.begin();
resOrderIter != m_searchResolutionsOrderArray.end(); ++resOrderIter) {
//CCLOG("\n\nSEARCHING: %s, %s, %s", newFilename.c_str(), resOrderIter->c_str(), searchPathsIter->c_str());
fullpath = this->getPathForFilename(newFilename, *resOrderIter, *searchPathsIter);
if (fullpath.length() > 0)
{
// Using the filename passed in as key.
m_fullPathCache.insert(std::pair<std::string, std::string>(pszFileName, fullpath));
//CCLOG("Returning path: %s", fullpath.c_str());
return fullpath;
}
}
}
CCLOG("cocos2d: fullPathForFilename: No file found at %s Possible missing file.", pszFileName);
// The file wasn't found, return the file name passed in.
return pszFileName;
}
blowfish加密解密
Blowfish *bl = new Blowfish();
dataBuffer key = {
我是敏感数据,不能泄漏 };
dataBuffer default_head_info = {
我是敏感数据,不能泄漏
};
dataBuffer default_data_key;
if (argc >= 4){
string code = argv[3];
default_data_key.resize(head_datakey_size);
for (int i = 0; i < head_datakey_size; i++){
default_data_key[i] = code.at(i);
}
}
bl->SetKey(&key[0], key.size());
dataBuffer src_data, dest_data;
getFileData(src_data, fileName);
if (src_data.size() == 0){
return -1;
}
unsigned int size = src_data.size();
dest_data.resize(size);
unsigned int len = 0;
bool is_file_crypted = parseBFFileInfo(&src_data[0], size, &len);
if (method == "e"){
if (is_file_crypted){
return 0;
}
unsigned int off_head_size = head_info_size + head_datakey_size;
dataBuffer head(off_head_size, 0), head_encrypted(off_head_size, 0),data_key(head_datakey_size,0);
if (default_data_key.size() == head_datakey_size){
copy(default_head_info.begin(), default_head_info.end(), head.begin());
copy(default_data_key.begin(), default_data_key.end(), data_key.begin());
copy(data_key.begin(), data_key.end(), head.begin() + head_info_size);
bl->Encrypt(&head_encrypted[0], &head[0], head.size());
bl->SetKey(&data_key[0], data_key.size());
}
else
{
off_head_size = 0;
}
int s1 = dest_data.size();
make_64bit_align(dest_data);
bl->Encrypt(&dest_data[0], &src_data[0], dest_data.size(), s1);
FILE* file = fopen(fileName.c_str(), "wb");
if (!file)
{
return -1;
}
if (off_head_size == head_info_size + head_datakey_size){
fwrite(&head_encrypted[0], sizeof(dataBuffer::value_type), head_encrypted.size(), file);
}
fwrite(&dest_data[0], sizeof(dataBuffer::value_type), dest_data.size(), file);
fwrite(&size, sizeof(size), 1, file);
fwrite(&c_bf_magic, sizeof(c_bf_magic), 1, file);
fclose(file);
}
if (method == "d"){
if (!is_file_crypted){
return 0;
}
dataBuffer head_encrypted(src_data.begin(), src_data.begin() + head_info_size + head_datakey_size), head(head_info_size + head_datakey_size);
bl->Decrypt(&head[0], &head_encrypted[0], head_info_size + head_datakey_size);
dataBuffer head_info(head.begin(), head.begin() + head_info_size), data_key(head.begin() + head_info_size, head.begin() + head_info_size + head_datakey_size);
if (head_info == default_head_info){
bl->SetKey(&data_key[0], data_key.size());
dest_data.resize(size - c_bf_info_size - head_info_size - head_datakey_size);
bl->Decrypt(&dest_data[0], &src_data[0] + head_info_size + head_datakey_size, dest_data.size());
}
else{
bl->Decrypt(&dest_data[0], &src_data[0], size - c_bf_info_size);
}
FILE* file = fopen(fileName.c_str(), "wb");
if (!file)
{
return -1;
}
fwrite(&dest_data[0], sizeof(dataBuffer::value_type), len, file);
fclose(file);
}