cocos2dx-Lua引擎游戏脚本及图⽚资源解密与DUMP
分析⽬标
,包名:uzu.android.snsgz
,包名:t.Q108
下⾯分析的主要是少年三国志。
Lua脚本解密与DUMP
LuaJit IDA分析调⽤树:
1. AppDelegate::applicationDidFinishLaunching(AppDelegate *__hidden this) EXPORT
_ZN11AppDelegate29applicationDidFinishLaunchingEv
2. cocos2d::CCLuaEngine::defaultEngine(cocos2d::CCLuaEngine *__hidden this) EXPORT
_ZN7cocos2d11CCLuaEngine13defaultEngineEv
3. cocos2d::CCLuaEngine::init(cocos2d::CCLuaEngine *__hidden this)
EXPORT _ZN7cocos2d11CCLuaEngine4initEv
4. cocos2d::CCLuaStack::create(cocos2d::CCLuaStack *__hidden this)
EXPORT _ZN7cocos2d10CCLuaStack6createEv
5. cocos2d::CCLuaStack::init(cocos2d::CCLuaStack *__hidden this)
EXPORT _ZN7cocos2d10CCLuaStack4initEv
6. cocos2dx_lua_loader
7. cocos2d::CCLuaStack::lua_loadbuffer(lua_State , char const, int, char const*)
EXPORT ZN7cocos2d10CCLuaStack14lua_loadbufferEP9lua_StatePKciS4
cocos2d::CCLuaStack::lua_loadbuffer先调⽤以下函数解密: cocos2d::extra::CCCrypto::decryptUF(uchar ,int,int ,int *) EXPORT ZN7cocos2d5extra8CCCrypto9decryptUFEPhiPiS3
最后再调⽤:luaL_loadbuffer
因此可以直接对luaL_loadbuffer进⾏HOOK,进⽽DUMP出Lua脚本,⽹上搜索函数声明:
int luaL_loadbuffer (lua_State *L, const char *buff, size_t sz, const char *name);
进⽽实现HOOK代码:
//orig function copy
int (*luaL_loadbuffer_orig)(void *L, const char *buff, int size, const char *name) = NULL;
//local function
int luaL_loadbuffer_mod(void *L, const char *buff, int size, const char *name) {
LOGD("[dumplua] luaL_loadbuffer name: %s lua: %s", name, buff);
return luaL_loadbuffer_orig(L, buff, size, name);
}
void hook() {
LOGD("[dumplua] hook begin");
void *handle = dlopen("libgame.so", RTLD_NOW);
if (handle == NULL) {
LOGE("[dumplua]dlopen err: %s.", dlerror());
return;
}else{
LOGD("[dumplua] libgame.so dlopen OK!");
}
void *pluaL_loadbuffer = dlsym(handle, "luaL_loadbuffer");
if (pluaL_loadbuffer == NULL){
LOGE("[dumplua] lua_loadbuffer not found!");
LOGE("[dumplua] dlsym err: %s.", dlerror());
}else{
LOGD("[dumplua] luaL_loadbuffer found!");
MSHookFunction(pluaL_loadbuffer, (void *)&luaL_loadbuffer_mod, (void **)&luaL_loadbuffer_orig);
}
}
运⾏后拦截到的输出信息:
01-0519:29:27.67413191-13215/? D/SUBSTRATEHOOK: [dumplua] luaL_loadbuffer name: assets/scripts/main.lua lua:
function__G__TRACKBACK__(errorMessage)
print("----------------------------------------")
print("LUA ERROR: " .. tostring(errorMessage) .. "\n")
local traceback = aceback("", 2)
print(traceback)
print("----------------------------------------")
--只有G_Report初始过后才会对错误⽇志做处理
if G_Report ~= nil then
G_Report:onTrackBack(errorMessage, traceback)
end
if SHOW_EXCEPTION_TIP and uf_notifyLayer ~= nil then
uf_notifyLayer:getDebugNode():removeChildByTag(10000)
local text = tostring(errorMessage)
require("upgrade.ErrMsgBox").showErrorMsgBox(text)
end
end
function traceMem(desc)
if desc == nil then
desc = "memory:"
end
if CCLuaObjcBridge then
local callStaticMethod = CCLuaObjcBridge.callStaticMethod
local ok, ret = callStaticMethod("NativeProxy", "getUsedMemory", nil)
if ok then
pri
01-0519:29:27.67913191-13215/? D/SUBSTRATEHOOK: [dumplua] luaL_loadbuffer name: upgrade.AntiAddictionLayer lua: LJ-
01-0519:29:27.67913191-13215/? D/SUBSTRATEHOOK: [dumplua] luaL_loadbuffer name: upgrade.ComSdkUtils lua: LJA
01-0519:29:27.68413191-13215/? D/SUBSTRATEHOOK: [dumplua] luaL_loadbuffer name: fig lua: LJ�
01-0519:29:27.68413191-13215/? D/SUBSTRATEHOOK: [dumplua] luaL_loadbuffer name: upgrade.ConfigLayer lua: LJ]
01-0519:29:27.68413191-13215/? D/SUBSTRATEHOOK: [dumplua] luaL_loadbuffer name: upgrade.EffectNode_Upgrade lua: LJ�
01-0519:29:27.68413191-13215/? D/SUBSTRATEHOOK: [dumplua] luaL_loadbuffer name: upgrade.ErrMsgBox lua: LJP
01-0519:29:27.68413191-13215/? D/SUBSTRATEHOOK: [dumplua] luaL_loadbuffer name: upgrade.game lua: LJ�
01-0519:29:27.68413191-13215/? D/SUBSTRATEHOOK: [dumplua] luaL_loadbuffer name: upgrade.NativeCallUtils lua: LJ�
01-0519:29:27.68413191-13215/? D/SUBSTRATEHOOK: [dumplua] luaL_loadbuffer name: upgrade.NativeProxy lua: LJ�
01-0519:29:27.68413191-13215/? D/SUBSTRATEHOOK: [dumplua] luaL_loadbuffer name: upgrade.Patcher lua: LJ�
01-0519:29:27.68913191-13215/? D/SUBSTRATEHOOK: [dumplua] luaL_loadbuffer name: upgrade.SplashLayer lua: LJ-
01-0519:29:27.68913191-13215/? D/SUBSTRATEHOOK: [dumplua] luaL_loadbuffer name: upgrade.upgrade lua: LJ6
可见,有些Lua脚本是源码形式,有些是LuaJit编译的,可以改写以上代码把脚本DUMP到⽂件中再进⼀步分析,此处略。
资源解密与DUMP
主要函数: cocos2d::CCImage::initWithImageFile调⽤ cocos2d::CCImage::initWithImageData
但是IDA分析发现initWithImageData会调⽤cocos2d::extra::CCCrypto::decryptXXTEA和cocos2d::extra::CCCrypto::decryptUF进⾏解密,最后再加载图⽚资源。以下是initWithImageData部分代码:
if ( s )
{
v12 = (unsigned __int8 *)strlen(s);
v13 = (void *)cocos2d::extra::CCCrypto::decryptXXTEA(v9, v11, (int)s, v12, (int)v24, v22);
v14 = v13;
if ( v13 )
v9 = (cocos2d::extra::CCCrypto *)v13;
goto LABEL_28;
}
v14 = 0;
if ( (signed int)a3 > 3 && *(_BYTE *)this == 85 && *((_BYTE *)this + 1) == 70 )
{
v25 = 0;
cocos2d::extra::CCCrypto::decryptUF(this, (int)a3, (int)&v25, v24, v21);
v15 = v25 >> 4;
if ( (v25 & 0xF u) <= 9 )
*(_DWORD *)(v8 + 36) = v25 & 0xF;
switch ( v15 )
{
case1:
v16 = 1067030938;
break;
case2:
v16 = 1068708659;
也即会调⽤cocos2d::extra::CCCrypto::decryptXXTEA和cocos2d::extra::CCCrypto::decryptUF进⾏解密操作。我们看下cocos2d::extra::CCCrypto::decryptUF这个函数,通过IDA的F5插件,并不断修改变量名可以获得⼀个⽐较清晰的C代码。手机游戏源码论坛
int __fastcall cocos2d::extra::CCCrypto::decryptUF(cocos2d::extra::CCCrypto *pInBuff, int nlen, int a3, int *pOutLen, int *name)
{
cocos2d::extra::CCCrypto *pInBuff2; // r5@1
int *pOutLen2; // r7@1
int v7; // r3@4
int v8; // r6@5
int v9; // r1@6
int result; // r0@9
int v11; // r4@10
int v12; // r6@12
int v13; // r6@15
signed int v14; // r0@19
int v15; // [sp+0h] [bp-28h]@5
int v16; // [sp+0h] [bp-28h]@10
int v17; // [sp+4h] [bp-24h]@5
pInBuff2 = pInBuff;
pOutLen2 = pOutLen;
if ( nlen <= 3 )
{
v14 = 1;
return -v14;
}
if ( *(_BYTE *)pInBuff != 'U' || *((_BYTE *)pInBuff + 1) != 'F' )
{
v14 = 2;
return -v14;
}
*(_DWORD *)a3 = *((_BYTE *)pInBuff + 2);
v7 = *((_BYTE *)pInBuff + 3);
if ( v7 == 1 )
{
v15 = nlen - 5;
v17 = *((_BYTE *)pInBuff + 4);
v8 = 0;
while ( v8 < v15 )
while ( v8 < v15 )
{
v9 = (v8++ + v17) % 0x21;
*(_BYTE *)pInBuff2 = *((_BYTE *)pInBuff2 + 5) ^ byte_6D192C[v9];
pInBuff2 = (cocos2d::extra::CCCrypto *)((char *)pInBuff2 + 1);
}
*pOutLen2 = v15;
}
else
{
result = 0;
if ( v7 != 2 )
return result;
v11 = 0;
v16 = *((_BYTE *)pInBuff2 + 4);
do
{
*((_BYTE *)pInBuff2 + v11) = *((_BYTE *)pInBuff2 + nlen + v11 - 5) ^ byte_6D192C[(v11 + v16) % 33 + 33];
++v11;
}
while ( v11 != 5 );
v12 = nlen - 10;
if ( nlen - 10 > 95 )
v12 = 95;
v13 = v12 + 4;
while ( v13 >= v11 )
{
*((_BYTE *)pInBuff2 + v11) ^= byte_6D192C[(v11 + v16) % 33 + 33];
++v11;
}
*pOutLen2 = nlen - 5;
}
return0;
}
其实看到这⾥应该也是⽐较容易逆向分析出解密的算法的,应该说⽐较简单,可以直接写⼀个脚本来解密assets⾥的资源。但是为了保证通⽤性,还是写HOOK代码⽐较好。
本来分析以为最终都会调⽤_initWithWebpData、_initWithJpgData、_initWithBpgData、_initWithPngData、_initWithTiffData、
_initWithRawData这些函数的,但是实际上分别HOOK后并没有被拦截,所以最后还是HOOK了下
cocos2d::extra::CCCrypto::decryptUF。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。