其他
cocos2d逆向入门和某捕鱼游戏分析
看雪论坛作者ID:挤蹭菌衣
1
2
applicationDidFinishLaunching函数
bool AppDelegate::applicationDidFinishLaunching()
{
// set default FPS
Director::getInstance()->setAnimationInterval(1.0 / 60.0f);//设置fps刷新率
// register lua module
auto engine = LuaEngine::getInstance();//创建lua虚拟机引擎
ScriptEngineManager::getInstance()->setScriptEngine(engine);//设置脚本引擎为lua引擎
lua_State* L = engine->getLuaStack()->getLuaState();//创建lua虚拟机环境lua_State
lua_module_register(L); //分配网络,控制台,ui界面等相关联的寄存器
register_all_packages();
LuaStack* stack = engine->getLuaStack();
stack->setXXTEAKeyAndSign("2dxLua", strlen("2dxLua"), "XXTEA", strlen("XXTEA"));
//这一步很关键,获取栈结构并调用setXXTEAKeyAndSign设置加密算法为xxtea,sign为XXTEA,KEY为2dxlua,很多游戏lua脚本都用的默认的sign和key
//如果没有用默认的,ida打开libxxxluaxxx.so直接搜索applicationDidFinishLaunching导出函数也基本都能直接找到
//register custom function
//LuaStack* stack = engine->getLuaStack();
//register_custom_function(stack->getLuaState());
#if CC_64BITS
FileUtils::getInstance()->addSearchPath("src/64bit");
#endif
FileUtils::getInstance()->addSearchPath("src"); //lua源码在src文件夹,资源在res文件夹
FileUtils::getInstance()->addSearchPath("res");
if (engine->executeScriptFile("main.lua")) //直接通过lua引擎调用main.lua进入lua的世界
{
return false;
}
return true;
}
LuaStack::executeScriptFile
int LuaStack::executeScriptFile(const char* filename)
{
CCAssert(filename, "CCLuaStack::executeScriptFile() - invalid filename");
std::string buf(filename);
//
// remove .lua or .luac
//
size_t pos = buf.rfind(BYTECODE_FILE_EXT);//BYTECODE_FILE_EXT就是lua字节码,NOT_BYTECODE_FILE_EXT就是lua脚本源码
//static const std::string BYTECODE_FILE_EXT = ".luac";
if (pos != std::string::npos)
{
buf = buf.substr(0, pos);//截取前缀
}
else
{
pos = buf.rfind(NOT_BYTECODE_FILE_EXT);
if (pos == buf.length() - NOT_BYTECODE_FILE_EXT.length())
{
buf = buf.substr(0, pos);
}
}
FileUtils *utils = FileUtils::getInstance();
//
// 1. check .luac suffix
// 2. check .lua suffix
//
std::string tmpfilename = buf + BYTECODE_FILE_EXT;
if (utils->isFileExist(tmpfilename))
{
buf = tmpfilename;
}
else
{
tmpfilename = buf + NOT_BYTECODE_FILE_EXT;
if (utils->isFileExist(tmpfilename))
{
buf = tmpfilename;
}
}
std::string fullPath = utils->fullPathForFilename(buf);//获取绝对路径
Data data = utils->getDataFromFile(fullPath);//通过getDataFromFile读取lua文件到data
int rn = 0;
if (!data.isNull())
{
if (luaLoadBuffer(_state, (const char*)data.getBytes(), (int)data.getSize(), fullPath.c_str()) == 0)//通过luaLoadBuffer加载data
{
rn = executeFunction(0);
}
}
return rn;
}
LuaStack::luaLoadBuffer
int LuaStack::luaLoadBuffer(lua_State *L, const char *chunk, int chunkSize, const char *chunkName)
{
int r = 0;
if (_xxteaEnabled && strncmp(chunk, _xxteaSign, _xxteaSignLen) == 0)//这里判断是否开启xxtea加密,如果开启就需要解密
{
// decrypt XXTEA
xxtea_long len = 0;
unsigned char* result = xxtea_decrypt((unsigned char*)chunk + _xxteaSignLen,
(xxtea_long)chunkSize - _xxteaSignLen,
(unsigned char*)_xxteaKey,
(xxtea_long)_xxteaKeyLen,
&len);//调用xxtea_decrypt解密脚本,这个函数在cocos2d-x\external\xxtea\xxtea.cpp里,加解密都在这个cpp里
unsigned char* content = result;
xxtea_long contentSize = len;
skipBOM((const char*&)content, (int&)contentSize);//忽略utf8的bom
r = luaL_loadbuffer(L, (char*)content, contentSize, chunkName);//无论是否加密,解密后都会调用luaL_loadbuffer函数,所以直接hook这个函数把(char*)content这个字符dump出来就是解密过的lua脚本了
free(result);
}
else
{
skipBOM(chunk, chunkSize);
r = luaL_loadbuffer(L, chunk, chunkSize, chunkName);//这个返回值r会反映加载失败的类型,在下面的switch中打印出来
}
#if defined(COCOS2D_DEBUG) && COCOS2D_DEBUG > 0
if (r)
{
switch (r)
{
case LUA_ERRSYNTAX:
CCLOG("[LUA ERROR] load \"%s\", error: syntax error during pre-compilation.", chunkName);
break;
case LUA_ERRMEM:
CCLOG("[LUA ERROR] load \"%s\", error: memory allocation error.", chunkName);
break;
case LUA_ERRFILE:
CCLOG("[LUA ERROR] load \"%s\", error: cannot open/read file.", chunkName);
break;
default:
CCLOG("[LUA ERROR] load \"%s\", error: unknown.", chunkName);
}
}
#endif
return r;
}
3
总结
AppDelegate::applicationDidFinishLaunching
{
setXXTEAKeyAndSign
executeScriptFile
{
getDataFromFile
luaLoadBuffer
{
xxtea_decrypt
luaL_loadbuffer
{
luajit
}
}
executeFunction
}
}
4
int __fastcall AppDelegate::applicationDidFinishLaunching(AppDelegate *this)`
`{`
//`前面是一堆初始化啥的没用的`
....
`(**(v7 + 1) + 116))(*(v7 + 1), "ZX_01RdsF~@!R8", 14, "ZX_CS@56#D~d@dud", 16);`
`if ( (*(*v7 + 28))(v7, "base/src/main.lua") )`
`return 0;`
`v26 = GetMCKernel();`
`if ( !v26 )`
`return 0;`
`v27 = *(this + 145);
if ( v27 )
v27 += 580;
v28 = (*(*v26 + 20))(v26, v27);`
`v29 = *(cocos2d::Director::getInstance(v28) + 152);`
`v32[0] = AppDelegate::GlobalUpdate;`
`v32[1] = -8;`
`cocos2d::Scheduler::schedule(v29, AppDelegate::GlobalUpdate, -8, this + 4, 0.0, 0xFFFFFFFE, 0.0, 0);`
`return v25;`
`}`
unsigned char* result = xxtea_decrypt((unsigned char*)chunk + _xxteaSignLen,
(xxtea_long)chunkSize - _xxteaSignLen,
(unsigned char*)_xxteaKey,
(xxtea_long)_xxteaKeyLen,
&len);
unsigned char *xxtea_decrypt(unsigned char *data, xxtea_long data_len, unsigned char *key, xxtea_long key_len, xxtea_long *ret_length)
{
unsigned char *result;
*ret_length = 0;
if (key_len < 16) {
unsigned char *key2 = fix_key_length(key, key_len);
result = do_xxtea_decrypt(data, data_len, key2, ret_length);
free(key2);
}
else
{
result = do_xxtea_decrypt(data, data_len, key, ret_length);
}
return result;
}
import xxtea
import os
orig_path=“”//初始路径
new_path=“”//存储路径
xxtea_sign=“”
xxtea_key=“”
orig_file = open(orig_path, "rb")
encrypt_bytes = orig_file.read()
orig_file.close()
decrypt_bytes = xxtea.decrypt(encrypt_bytes[len(xxtea_sign):], xxtea_key)
new_file = open(new_path, "wb")
new_file.write(decrypt_bytes)
new_file.close()
function BulletLayer:on_bullet_crash_fish(bullet_obj, t_fish_list)
local scene = self:get_scene()
local master_id = bullet_obj:get_master_id()
local bullet_uid = bullet_obj:get_bullet_uid()
local self_player_id = self:get_self_player_id()
local num = #t_fish_list
if(num >= 2) then
local fish_layer = self:get_fish_layer()
table.sort(t_fish_list, function(uid1, uid2)
local fish_obj1 = fish_layer:get_fish_by_uid(uid1);local fish_obj2 = fish_layer:get_fish_by_uid(uid2)
local zorder1 = fish_obj1:getLocalZOrder();local zorder2 = fish_obj2:getLocalZOrder()
if(zorder1 > zorder2) then return true end
end)
end
local fish_uid = t_fish_list[1]
if(master_id == self_player_id) then
scene:on_self_bullet_crash_fish(master_id, bullet_uid, fish_uid)
--scene:on_self_bullet_crash_fish_test(master_id, bullet_uid, fish_uid)
end
scene:on_bullet_crash_fish(master_id, bullet_uid, t_fish_list)
end
function AnimationLayer:play_catch_fish_earn_money_self(fish_gold, fish_odds)
local earn_money_node = self:reuse_new_earn_money_view_node('zhuanqianle_buyu', self, 0)
local pos = cc.p(sizeVisble.width/2, sizeVisble.height/2)
local one_add_gold = math.floor(fish_gold/12)
local min = self:get_earn_money_one_add_gold_min()
if(one_add_gold < min) then one_add_gold = min end
earn_money_node:setPosition(pos); earn_money_node:set_gain_gold(fish_gold)
earn_money_node:set_one_add_gold(one_add_gold); earn_money_node:set_cur_gold(0)
local delay_time = cc.DelayTime:create(4)
local call_back = cc.CallFunc:create(handler(self, self.call_back_reuse_obj))
local seq = cc.Sequence:create(delay_time, call_back);
earn_money_node:setScale(1)
earn_money_node:runAction(seq);
earn_money_node:play_ani()
end
function AnimationLayer:on_recv_player_catch_fish(player_id, fish_uid, fish_gold)
local fish_layer = self:get_fish_layer()
local fish_obj = fish_layer:get_fish_by_uid(fish_uid)
if(fish_obj == nil) then return end
local data_center = self:get_data_center()
local fish_id = fish_obj:get_fish_id()
local fish_info = data_center:get_fish_info(fish_id)
if(fish_info == nil) then return end
local fish_type = fish_info.type
if(fish_type == GameDefine.fish_type.wang) then
local other_fish_obj_list = self:get_other_fish_yi_wang_da_jin_list(fish_obj, GameDefine.fish_type.wang)
local num = #other_fish_obj_list
if(num > 0) then
local fish_odds = self:get_catch_fish_odds(fish_obj, other_fish_obj_list)
self:on_player_catch_fish_yi_wang_da_jin(player_id, fish_obj, fish_gold, fish_odds, other_fish_obj_list)
end
self:on_player_catch_fish_drop_fish_gold(player_id, fish_obj, 0, 0)
return
end
if(fish_type == GameDefine.fish_type.bomb) then
play_drop_gold = 0
local other_fish_list = self:get_bomb_fish_effect_fish_list()
local num = #other_fish_list
if(num > 0) then
local fish_odds = self:get_catch_fish_odds(fish_obj, other_fish_list)
self:on_player_catch_fish_bomb(player_id, fish_obj, fish_gold, fish_odds, other_fish_list)
end
self:on_player_catch_fish_drop_fish_gold(player_id, fish_obj, 0, 0)
return
end
local fish_odds = fish_info.mulriple_max;local fish_name = fish_info.name
self:on_player_cath_fish_da_zhuan_pan(player_id, fish_type, fish_gold, fish_name)
self:on_player_catch_fish_drop_fish_gold(player_id, fish_obj, fish_gold, fish_odds)
end
{
room_style= 3,
cannon_id= 109,
odds_min= 7,
odds_max= 10,
level= 3,
res= "pao3_buyu",
bullet= "zd9",
net= "yuwang3_buyu",
time= 200,
sound= 109
}
{
id= 302,
name= "60倍组合鱼",
packet= "4lian_buyu",
crash_model= 101,
mulriple_min= 60,
mulriple_max= 60,
type= 101,
zoder= 21,
die_sound= "fish14_1",
die_type= 4,
copy_num= 4
},
{
id= 402,
name= "大章鱼",
packet= "yu22_buyu",
crash_model= 16,
mulriple_min= 300,
mulriple_max= 300,
type= 2,
zoder= 30,
bron_sound= "f_wb_3",
die_sound= "fish33_1",
die_type= 5,
copy_num= 4
},
5
如何实现cocos2d反逆向,我的一些不成熟想法,由浅到深分层如下:
5、关键代码加入ollvm再编译或者直接vm掉,这样需要先去掉ollvm混淆或者分析vm。
参考文章:
看雪ID:挤蹭菌衣
https://bbs.pediy.com/user-home-877073.htm
*本文由看雪论坛 挤蹭菌衣 原创,转载请注明来自看雪社区
# 往期推荐
球分享
球点赞
球在看
点击“阅读原文”,了解更多!