Cocos2dlua手游 Lua解密与资源解密实战

[复制链接]
查看1793 | 回复2 | 2025-7-13 14:12:31 | 显示全部楼层 |阅读模式

立即注册,突破游客限制,与技术大牛交流切磋,畅享海量资源,让你的技术之路不再孤单!

您需要 登录 才可以下载或查看,没有账号?立即注册

×
样本树
大致结构如下
截图202507131403262607.png
其中.lua64为标准LuaJit文件

而.lua的文件头有点奇怪
截图202507131403421716.png

.lua文件头:abcd

再看资源文件.png,发现也是加密的
截图202507131403562276.png

综上,我们需要实现的目标
  • 解密.lua文件
  • 解密.png文件
LuaJit 反汇编
这个不多介绍了窝,在理论篇中已经提到了。对于此样本的LuaJit的版本是2.1.0-Beta2,并且没有魔改Opcode
Lua文件加载流程
想要解密.lua文件,了解coco2d-x加载Lua的流程必不可少
Cocos2d-x环境搭建
参考官方Docs,搭建所需环境
截图202507131404194224.png
从Android Activity揭秘cocos2d-x的世界
由于Android的应用层是从Activity开始的,也就是创建完一个Cocos2dx后src文件夹下的Java文件,其中主要看Activity创建时的操作
截图202507131404441825.png
这个方法很简单,就只调用了父类Cocos2dxActivity的onCreate方法
  1. @Override
  2.     protected void onCreate(final Bundle savedInstanceState) {
  3.         super.onCreate(savedInstanceState);
  4.         // Workaround in https://stackoverflow.com/questions/16283079/re-launch-of-activity-on-home-button-but-only-the-first-time/16447508
  5.         if (!isTaskRoot()) {
  6.             // Android launched another instance of the root activity into an existing task
  7.             //  so just quietly finish and go away, dropping the user back into the activity
  8.             //  at the top of the stack (ie: the last state of this task)
  9.             finish();
  10.             Log.w(TAG, "[Workaround] Ignore the activity started from icon!");
  11.             return;
  12.         }
  13.         this.hideVirtualButton();
  14.         onLoadNativeLibraries();
  15.         sContext = this;
  16.         this.mHandler = new Cocos2dxHandler(this);
  17.         Cocos2dxHelper.init(this);
  18.         this.mGLContextAttrs = getGLContextAttrs();
  19.         this.init();
  20.         if (mVideoHelper == null) {
  21.             mVideoHelper = new Cocos2dxVideoHelper(this, mFrameLayout);
  22.         }
  23.         if(mWebViewHelper == null){
  24.             mWebViewHelper = new Cocos2dxWebViewHelper(mFrameLayout);
  25.         }
  26.         if(mEditBoxHelper == null){
  27.             mEditBoxHelper = new Cocos2dxEditBoxHelper(mFrameLayout);
  28.         }
  29.         Window window = this.getWindow();
  30.         window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
  31.         // Audio configuration
  32.         this.setVolumeControlStream(AudioManager.STREAM_MUSIC);
  33.     }
复制代码
在这个方法中主要看Cocos2dxActivity的初始化方法init,Cocos2dxHandler是工具辅助类,不是重点。
  1. public Cocos2dxGLSurfaceView onCreateView() {
  2.         Cocos2dxGLSurfaceView glSurfaceView = new Cocos2dxGLSurfaceView(this);
  3.         //this line is need on some device if we specify an alpha bits
  4.         // FIXME: is it needed? And it will cause afterimage.
  5.         // if(this.mGLContextAttrs[3] > 0) glSurfaceView.getHolder().setFormat(PixelFormat.TRANSLUCENT);
  6.         // use custom EGLConfigureChooser
  7.         Cocos2dxEGLConfigChooser chooser = new Cocos2dxEGLConfigChooser(this.mGLContextAttrs);
  8.         glSurfaceView.setEGLConfigChooser(chooser);
  9.         return glSurfaceView;
  10.     }
  11. // ......
  12. public void init() {
  13.         // FrameLayout 初始化窗口布局
  14.         ViewGroup.LayoutParams framelayout_params =
  15.             new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
  16.                                        ViewGroup.LayoutParams.MATCH_PARENT);
  17.         mFrameLayout = new ResizeLayout(this);
  18.         mFrameLayout.setLayoutParams(framelayout_params);
  19.         // Cocos2dxEditText layout 初始化Cocos2dx的文本编辑布局
  20.         ViewGroup.LayoutParams edittext_layout_params =
  21.             new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
  22.                                        ViewGroup.LayoutParams.WRAP_CONTENT);
  23.         Cocos2dxEditBox edittext = new Cocos2dxEditBox(this);
  24.         edittext.setLayoutParams(edittext_layout_params);
  25.         mFrameLayout.addView(edittext);
  26.         // Cocos2dxGLSurfaceView 初始化Cocos2dx视图
  27.         this.mGLSurfaceView = this.onCreateView();
  28.         // ...add to FrameLayout 将Cocos2dxGLSurfaceView加入到当前的窗口布局中
  29.         mFrameLayout.addView(this.mGLSurfaceView);
  30.         // Switch to supported OpenGL (ARGB888) mode on emulator
  31.         // this line dows not needed on new emulators and also it breaks stencil buffer
  32.         //if (isAndroidEmulator()) // 在模拟器中切换支持OpenGL模式的渲染(ARGB888)
  33.         //   this.mGLSurfaceView.setEGLConfigChooser(8, 8, 8, 8, 16, 0);
  34.         // 设置Cocos2dx的渲染器
  35.         this.mGLSurfaceView.setCocos2dxRenderer(new Cocos2dxRenderer());
  36.         //设置Cocos2dx的文本编辑
  37.         this.mGLSurfaceView.setCocos2dxEditText(edittext);
  38.         // Set framelayout as the content view
  39.         // 把显示布局(即Cocos2dx的视图)绑定到Activity上,建立显示窗口
  40.         setContentView(mFrameLayout);
  41.     }
复制代码
首先在init中先看this.mGLSurfaceView = this.onCreateView();this.mGLSurfaceView是一个Cocos2dxGLSurfaceView类

进入到Cocos2dxGLSurfaceView这个类,可以看到时继承于GLSurfaceView(可以把GLSurfaceView看成一个视图,里面有个方法设置了这个视图的渲染器,然后通过这个渲染器来进行画面的渲染)
在Android中,GLSurfaceView是一个支持OpenGL的渲染视图,通过继承SurfaceView中的surface来渲染OpenGL
并提供了以下特性(来源于网上)
  • 管理一个surface,这个surface就是一块特殊的内存,能直接排版到android的视图view上
  • 管理一个EGL display,它能让opengl把内容渲染到上述的surface上
  • 用户自定义渲染器(render)
  • 让渲染器在独立的线程里运作,和UI线程分离
  • 支持按需渲染(on-demand)和连续渲染(continuous)
  • 一些可选工具,如调试
重点是自定义的渲染器,也就是Cocos2dx引擎封装的渲染器

进入Cocos2dxRenderer类,看到他是继承GLSurfaceView.Renderer接口,这个接口定义了三个方法:
  • onSurfaceCreated: 创建GLSurfaceView时被调用,只调用一次,做初始化工作
  • onSurfaceChanged: 当GLSurfaceView的几何体被改变时被调用
  • onDrawFrame: 绘制渲染GLSurfaceView
onSurfaceCreated
  1. @Override
  2.     public void onSurfaceCreated(final GL10 GL10, final EGLConfig EGLConfig) {
  3.         Cocos2dxRenderer.nativeInit(this.mScreenWidth, this.mScreenHeight);
  4.         this.mLastTickInNanoSeconds = System.nanoTime();
  5.         mNativeInitCompleted = true;
  6.     }
复制代码
nativeInit是一个Native函数
  1. private static native void nativeInit(final int width, final int height);
复制代码
具体实现在frameworks\cocos2d-x\cocos\platform\android\javaactivity-android.cpp中
  1. JNIEXPORT void Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeInit(JNIEnv*  env, jobject thiz, jint w, jint h)
  2. {
  3.     auto director = cocos2d::Director::getInstance();
  4.     auto glview = director->getOpenGLView();
  5.     if (!glview)
  6.     {
  7.         glview = cocos2d::GLViewImpl::create("Android app");
  8.         glview->setFrameSize(w, h);
  9.         director->setOpenGLView(glview);
  10.         cocos2d::Application::getInstance()->run();
  11.     }
  12.     else
  13.     {
  14.         cocos2d::Director::getInstance()->resetMatrixStack();
  15.         cocos2d::EventCustom recreatedEvent(EVENT_RENDERER_RECREATED);
  16.         director->getEventDispatcher()->dispatchEvent(&recreatedEvent);
  17.         director->setGLDefaultValues();
  18.         cocos2d::VolatileTextureMgr::reloadAllTextures();
  19.     }
  20.     cocos2d::network::_preloadJavaDownloaderClass();
  21. }
复制代码
重点是cocos2d::Application::getInstance()->run()
  1. int Application::run()
  2. {
  3.     // Initialize instance and cocos2d.
  4.     if (! applicationDidFinishLaunching())
  5.     {
  6.         return 0;
  7.     }
  8.     return -1;
  9. }
复制代码
applicationDidFinishLaunching,没错这就是游戏逻辑的入口了
  1. bool AppDelegate::applicationDidFinishLaunching()
  2. {
  3.     // set default FPS
  4.     Director::getInstance()->setAnimationInterval(1.0 / 60.0f);
  5.     // register lua module
  6.       // 初始化 LuaEngine, 在 getInstance 中会初始化 LuaStack, LuaStack 初始化 Lua 环境相关
  7.     auto engine = LuaEngine::getInstance();
  8.     // 将 LuaEngine 添加到脚本引擎管理器 ScriptEngineManager 中
  9.     ScriptEngineManager::getInstance()->setScriptEngine(engine);
  10.     // 获取 Lua 环境
  11.     lua_State* L = engine->getLuaStack()->getLuaState();
  12.     // 注册额外的 C++ API 相关,比如 cocosstudio, spine, audio 相关
  13.     lua_module_register(L);
  14.     // 设置 cocos 自带的加密相关
  15.     register_all_packages();
  16.     // 在 LuaStack::executeScriptFile 执行脚本文件时,会通过 LuaStack::luaLoadBuffer 对文件进行解密
  17.     LuaStack* stack = engine->getLuaStack();
  18.     stack->setXXTEAKeyAndSign("2dxLua", strlen("2dxLua"), "XXTEA", strlen("XXTEA"));
  19.     //register custom function
  20.     //LuaStack* stack = engine->getLuaStack();
  21.     //register_custom_function(stack->getLuaState());
  22. #if CC_64BITS
  23.     FileUtils::getInstance()->addSearchPath("src/64bit");
  24. #endif
  25.     FileUtils::getInstance()->addSearchPath("src");
  26.     FileUtils::getInstance()->addSearchPath("res");
  27.     if (engine->executeScriptFile("main.lua"))
  28.     {
  29.         return false;
  30.     }
  31.     return true;
  32. }
复制代码
LuaEngine::getInstance();这段代码实例化了LuaEngine
下面分析LuaEngine初始化过程
  1. LuaEngine* LuaEngine::getInstance(void)
  2. {
  3.     if (!_defaultEngine)
  4.     {
  5.         _defaultEngine = new (std::nothrow) LuaEngine();
  6.         _defaultEngine->init();
  7.     }
  8.     return _defaultEngine;
  9. }
复制代码
接下来是_defaultEngine->init();
  1. bool LuaEngine::init(void)
  2. {
  3.     _stack = LuaStack::create();
  4.     _stack->retain();
  5.     return true;
  6. }
复制代码
继续进入
  1. LuaStack *LuaStack::create()
  2. {
  3.     LuaStack *stack = new (std::nothrow) LuaStack();
  4.     stack->init();
  5.     stack->autorelease();
  6.     return stack;
  7. }
复制代码
下面是stack->init()
  1. bool LuaStack::init()
  2. {
  3.     // 初始化Lua环境并打开标准库
  4.     _state = lua_open();
  5.     luaL_openlibs(_state);
  6.     toluafix_open(_state);
  7.     // Register our version of the global "print" function
  8.     // 注册全局函数print到lua中,它会覆盖lua库中的print方法
  9.     const luaL_Reg global_functions [] = {
  10.         {"print", lua_print},
  11.         {"release_print",lua_release_print},
  12.         {nullptr, nullptr}
  13.     };
  14.     // 注册全局变量
  15.     luaL_register(_state, "_G", global_functions);
  16.     // 注册cocos2d-x引擎的API到lua环境中
  17.     g_luaType.clear();
  18.     register_all_cocos2dx(_state);
  19.     register_all_cocos2dx_backend(_state);
  20.     register_all_cocos2dx_manual(_state);
  21.     register_all_cocos2dx_module_manual(_state);
  22.     register_all_cocos2dx_math_manual(_state);
  23.     register_all_cocos2dx_shaders_manual(_state);
  24.     register_all_cocos2dx_bytearray_manual(_state);
  25.     tolua_luanode_open(_state);
  26.     register_luanode_manual(_state);
  27. #if CC_USE_PHYSICS
  28.     // 导入使用的physics相关API
  29.     register_all_cocos2dx_physics(_state);
  30.     register_all_cocos2dx_physics_manual(_state);
  31. #endif
  32. #if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_MAC)
  33.     // 导入ios下调用object-c相关API
  34.     LuaObjcBridge::luaopen_luaoc(_state);
  35. #endif
  36. #if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
  37.     // 导入android下调用java相关API
  38.     LuaJavaBridge::luaopen_luaj(_state);
  39. #endif
  40.     register_all_cocos2dx_deprecated(_state);
  41.     register_all_cocos2dx_manual_deprecated(_state);
  42.     tolua_script_handler_mgr_open(_state);
  43.     // add cocos2dx loader
  44.     // 添加Lua的加载器,该方法将cocos2dx_lua_loader方法添加到Lua全局变量package下的loaders成员中
  45.    // 当requires加载脚本时,Lua会使用package下的loaders中的加载器,即cocos2dx_lua_loader来加载
  46.     // 设定cocos2dx_lua_loader,可以使得我们自定义设置搜索路径相关,且拓展实现对脚本的加密解密相关
  47.     addLuaLoader(cocos2dx_lua_loader);
  48.     return true;
  49. }
复制代码
重点在cocos2dx_lua_loader方法(这个方法在frameworks\cocos2d-x\cocos\scripting\lua-bindings\manual\Cocos2dxLuaLoader.cpp)
  1. int cocos2dx_lua_loader(lua_State *L)
  2.     {
  3.         // 后缀为luac和lua
  4.         static const std::string BYTECODE_FILE_EXT    = ".luac";
  5.         static const std::string NOT_BYTECODE_FILE_EXT = ".lua";
  6.         // require传入的要加载的文件名,例如:require("a.b") 查找文件为:a/b.lua
  7.         std::string filename(luaL_checkstring(L, 1));
  8.         size_t pos = filename.rfind(BYTECODE_FILE_EXT);
  9.          // 去掉后缀名".luac"或“.lua”
  10.         if (pos != std::string::npos && pos == filename.length() - BYTECODE_FILE_EXT.length())
  11.             filename = filename.substr(0, pos);
  12.         else
  13.         {
  14.             pos = filename.rfind(NOT_BYTECODE_FILE_EXT);
  15.             if (pos != std::string::npos && pos == filename.length() - NOT_BYTECODE_FILE_EXT.length())
  16.                 filename = filename.substr(0, pos);
  17.         }
  18.         // 将 "." 替换为 "/"
  19.         pos = filename.find_first_of('.');
  20.         while (pos != std::string::npos)
  21.         {
  22.             filename.replace(pos, 1, "/");
  23.             pos = filename.find_first_of('.');
  24.         }
  25.         // search file in package.path
  26.         Data chunk;
  27.         std::string chunkName;
  28.         FileUtils* utils = FileUtils::getInstance();
  29.         // 获取 package.path 的变量
  30.         lua_getglobal(L, "package");
  31.         lua_getfield(L, -1, "path");
  32.         // 通过 package.path 获取搜索路径相关,该路径为模版路径,格式类似于:
  33.         // ?; ?.lua; c:\Users\?;  /usr/local/lua/lua/?/?.lua 以“;”作为分割符
  34.         std::string searchpath(lua_tostring(L, -1));
  35.         lua_pop(L, 1);
  36.         size_t begin = 0;
  37.         size_t next = searchpath.find_first_of(';', 0);
  38.         // 遍历 package.path 中的所有路径,查找文件是否存在,若文件存在则通过 getDataFromFile 读取文件数据
  39.         do
  40.         {
  41.             if (next == std::string::npos)
  42.                 next = searchpath.length();
  43.             std::string prefix = searchpath.substr(begin, next-begin);
  44.             if (prefix[0] == '.' && prefix[1] == '/')
  45.                 prefix = prefix.substr(2);
  46.             pos = prefix.rfind(BYTECODE_FILE_EXT);
  47.             if (pos != std::string::npos && pos == prefix.length() - BYTECODE_FILE_EXT.length())
  48.             {
  49.                 prefix = prefix.substr(0, pos);
  50.             }
  51.             else
  52.             {
  53.                 pos = prefix.rfind(NOT_BYTECODE_FILE_EXT);
  54.                 if (pos != std::string::npos && pos == prefix.length() - NOT_BYTECODE_FILE_EXT.length())
  55.                     prefix = prefix.substr(0, pos);
  56.             }
  57.             pos = prefix.find_first_of('?', 0);
  58.             while (pos != std::string::npos)
  59.             {
  60.                 prefix.replace(pos, 1, filename);
  61.                 pos = prefix.find_first_of('?', pos + filename.length() + 1);
  62.             }
  63.             chunkName = prefix + BYTECODE_FILE_EXT;
  64.             if (utils->isFileExist(chunkName)) // && !utils->isDirectoryExist(chunkName))
  65.             {
  66.                 chunk = utils->getDataFromFile(chunkName);
  67.                 break;
  68.             }
  69.             else
  70.             {
  71.                 chunkName = prefix + NOT_BYTECODE_FILE_EXT;
  72.                 if (utils->isFileExist(chunkName) ) //&& !utils->isDirectoryExist(chunkName))
  73.                 {
  74.                     chunk = utils->getDataFromFile(chunkName);
  75.                     break;
  76.                 }
  77.                 else
  78.                 {
  79.                     chunkName = prefix;
  80.                     if (utils->isFileExist(chunkName)) // && !utils->isDirectoryExist(chunkName))
  81.                     {
  82.                         chunk = utils->getDataFromFile(chunkName);
  83.                         break;
  84.                     }
  85.                 }
  86.             }
  87.             // 指定搜素路径下不存在该文件, 下一个
  88.             begin = next + 1;
  89.             next = searchpath.find_first_of(';', begin);
  90.         } while (begin < searchpath.length());
  91.         // 判断文件内容是否获取成功
  92.         if (chunk.getSize() > 0)
  93.         {
  94.             // 加载文件
  95.             LuaStack* stack = LuaEngine::getInstance()->getLuaStack();
  96.             stack->luaLoadBuffer(L, reinterpret_cast<const char*>(chunk.getBytes()),
  97.                                  static_cast<int>(chunk.getSize()), chunkName.c_str());
  98.         }
  99.         else
  100.         {
  101.             CCLOG("can not get file data of %s", chunkName.c_str());
  102.             return 0;
  103.         }
  104.         return 1;
  105.     }
复制代码
通过此处的代码,可以了解到cocos2d-x是如何搜索指定的lua文件
同时也会明白require为何可以使用 "." 来设定文件路径了,比如:
  1. require "cocos.cocos2d.Cocos2d"
  2. require "cocos.cocos2d.Cocos2dConstants"
  3. require "cocos.cocos2d.functions"
复制代码
再来看下stack->luaLoadBuffer的实现:
  1. int LuaStack::luaLoadBuffer(lua_State *L, const char *chunk, int chunkSize, const char *chunkName)
  2. {
  3.     int r = 0;
  4.     // 判断是否加密,若lua脚本加密,则解密后在加载脚本文件
  5.     // luaL_loadbuffer 用于加载并编译Lua代码,并将其压入栈中
  6.     if (_xxteaEnabled && strncmp(chunk, _xxteaSign, _xxteaSignLen) == 0)
  7.     {
  8.         // decrypt XXTEA
  9.         xxtea_long len = 0;
  10.         unsigned char* result = xxtea_decrypt((unsigned char*)chunk + _xxteaSignLen,
  11.                                               (xxtea_long)chunkSize - _xxteaSignLen,
  12.                                               (unsigned char*)_xxteaKey,
  13.                                               (xxtea_long)_xxteaKeyLen,
  14.                                               &len);
  15.         unsigned char* content = result;
  16.         xxtea_long contentSize = len;
  17.         skipBOM((const char*&)content, (int&)contentSize);
  18.         r = luaL_loadbuffer(L, (char*)content, contentSize, chunkName);
  19.         free(result);
  20.     }
  21.     else
  22.     {
  23.         skipBOM(chunk, chunkSize);
  24.         r = luaL_loadbuffer(L, chunk, chunkSize, chunkName);
  25.     }
  26. // 判定内容是否存在错误
  27. #if defined(COCOS2D_DEBUG) && COCOS2D_DEBUG > 0
  28.     if (r)
  29.     {
  30.         switch (r)
  31.         {
  32.             case LUA_ERRSYNTAX:
  33.                 // 语法错误
  34.                 CCLOG("[LUA ERROR] load "%s", error: syntax error during pre-compilation.", chunkName);
  35.                 break;
  36.             case LUA_ERRMEM:
  37.                 // 内存分配错误
  38.                 CCLOG("[LUA ERROR] load "%s", error: memory allocation error.", chunkName);
  39.                 break;
  40.             case LUA_ERRFILE:
  41.                  // 文件错误
  42.                 CCLOG("[LUA ERROR] load "%s", error: cannot open/read file.", chunkName);
  43.                 break;
  44.             default:
  45.                 // 未知错误
  46.                 CCLOG("[LUA ERROR] load "%s", error: unknown.", chunkName);
  47.         }
  48.     }
  49. #endif
  50.     return r;
  51. }
复制代码
至此,Lua文件的加载流程结束

下面是Lua文件主要加载流程图
截图202507131408301175.png
Lua解密之自定义文件头
了解了Coco2d-x Lua文件基本的加载流程,可以帮助我们很快的定位关键方法
快速定位
由于所有的Lua文件加载必定经过cocos2d:uaStack::luaLoadBuffer,所以可以直接定位,进行回溯

定位至cocos2dx_lua_loader
截图202507131408482818.png
在luaLoadBuffer前调用了decodeLuaData,怀疑这就是解密Lua的关键方法

下面是伪代码
  1. _BYTE *__fastcall decodeLuaData(cocos2d::Data *a1, int *size)
  2. {
  3.   _BYTE *v3; // r0
  4.   _BYTE *v4; // r5
  5.   int v6; // r0
  6.   int v7; // r2
  7.   int v8; // r3
  8.   bool v9; // cc
  9.   unsigned int v10; // r1
  10.   _BYTE *v11; // r12
  11.   _BYTE *v12; // r0
  12.   char v13; // t1
  13.   _BYTE *v14; // r0
  14.   char v15[4]; // [sp+0h] [bp-18h]
  15.   v3 = (_BYTE *)cocos2d::Data::getBytes(a1);
  16.   v4 = v3;
  17.   if ( *size > 8 && *v3 == 'a' && v3[1] == 'b' && v3[2] == 'c' && v3[3] == 'd' )
  18.   {
  19.     v6 = _hexToDecimal(v3 + 4, 4);
  20.     v7 = *size;
  21.     v8 = 0;
  22.     v9 = *size <= 8;
  23.     v15[3] = v6;
  24.     v15[2] = (unsigned __int16)(v6 - 2048) >> 8;
  25.     v10 = (unsigned int)(v6 - 2048) >> 24;
  26.     v15[1] = (unsigned int)(v6 - 2048) >> 16;
  27.     v15[0] = (unsigned int)(v6 - 2048) >> 24;
  28.     if ( !v9 )
  29.     {
  30.       v11 = v4 - 1;
  31.       v12 = v4 + 7;
  32.       while ( 1 )
  33.       {
  34.         v13 = *++v12;
  35.         ++v8;
  36.         *++v11 = v13 ^ v10;
  37.         v7 = *size;
  38.         if ( *size <= v8 + 8 )
  39.           break;
  40.         LOBYTE(v10) = v15[v8 & 3];
  41.       }
  42.     }
  43.     v14 = &v4[v7 - 8];
  44.     *v14 = 0;
  45.     v14[1] = 0;
  46.     v14[2] = 0;
  47.     v14[3] = 0;
  48.     v14[4] = 0;
  49.     v14[5] = 0;
  50.     v14[6] = 0;
  51.     v14[7] = 0;
  52.     *size -= 8;
  53.   }
  54.   return v4;
  55. }
复制代码
此方法首先获取文件数据,而后判断文件头,文件头正是abcd

可以肯定这就是解密.lua的算法

如果不确定,我们可以使用Frida进行Hook验证猜想

我这里就不Hook了
算法还原
要实现算法的还原,我们就要依据伪代码或是汇编代码翻译成可用代码,可以发现IDA所反汇编的伪代码中包含了一些特定的宏,例如:LOBYTE

要了解这些宏的用途,我们可以参考ida_root/plugins/def.h

我这里使用了Python
  1. import struct
  2. from loguru import logger
  3. def _hexToDecimal(data, offset):
  4.     if offset <= 0:
  5.         return 0
  6.     return struct.unpack(">I", data[:offset])[0]
  7. def LOBYTE(d):
  8.     return d & 0xff
  9. def decodeLuaData(data, size):
  10.     v4 = bytearray(data)
  11.     v15 = [0] * 4
  12.     if size > 8 and data[0] == ord('a') and data[1] == ord('b') and data[2] == ord('c') and data[3] == ord('d'):
  13.         logger.info("find special exts")
  14.         v6 = _hexToDecimal(data[4:], 4)
  15.         v15[3] = v6
  16.         v15[2] = (v6 - 2048) >> 8
  17.         v10 = (v6 - 2048) >> 24
  18.         v15[1] = (v6 - 2048) >> 16
  19.         v15[0] = (v6 - 2048) >> 24
  20.         if size > 8:
  21.             i = 0
  22.             while 1:
  23.                 v13 = v4[8 + i]
  24.                 v4[i] = v13 ^ v10
  25.                 i += 1
  26.                 if size <= i + 8:
  27.                     break
  28.                 v10 = LOBYTE(v15[i & 3])
  29.         size -= 8
  30.         return v4[:-8]
复制代码
解密后,发现还是LuaJit,那么直接类同.lua64反汇编即可
截图202507131409363547.png
Lua资源加载流程
回到Lua文件加载流程分析时提到的LuaStack::init()方法中,调用了register_all_cocos2dx
  1. TOLUA_API int register_all_cocos2dx(lua_State* tolua_S)
  2. {
  3.     tolua_open(tolua_S);
  4.     tolua_module(tolua_S,"cc",0);
  5.     tolua_beginmodule(tolua_S,"cc");
  6.     lua_register_cocos2dx_Ref(tolua_S);
  7.     lua_register_cocos2dx_Material(tolua_S);
  8.     lua_register_cocos2dx_Console(tolua_S);
  9.     lua_register_cocos2dx_Node(tolua_S);
  10.     lua_register_cocos2dx_Scene(tolua_S);
  11.     lua_register_cocos2dx_TransitionScene(tolua_S);
  12.     lua_register_cocos2dx_TransitionEaseScene(tolua_S);
  13.     lua_register_cocos2dx_TransitionMoveInL(tolua_S);
  14.     lua_register_cocos2dx_TransitionMoveInB(tolua_S);
  15.     lua_register_cocos2dx_AtlasNode(tolua_S);
  16.     lua_register_cocos2dx_TileMapAtlas(tolua_S);
  17.     lua_register_cocos2dx_TransitionMoveInT(tolua_S);
  18.     lua_register_cocos2dx_TMXTilesetInfo(tolua_S);
  19.     lua_register_cocos2dx_TransitionMoveInR(tolua_S);
  20.     lua_register_cocos2dx_Action(tolua_S);
  21.     lua_register_cocos2dx_FiniteTimeAction(tolua_S);
  22.     lua_register_cocos2dx_ActionInstant(tolua_S);
  23.     lua_register_cocos2dx_Hide(tolua_S);
  24.     lua_register_cocos2dx_ParticleSystem(tolua_S);
  25.     lua_register_cocos2dx_ParticleSystemQuad(tolua_S);
  26.     lua_register_cocos2dx_ParticleSpiral(tolua_S);
  27.     lua_register_cocos2dx_GridBase(tolua_S);
  28.     lua_register_cocos2dx_AnimationCache(tolua_S);
  29.     lua_register_cocos2dx_ActionInterval(tolua_S);
  30.     lua_register_cocos2dx_ActionCamera(tolua_S);
  31.     lua_register_cocos2dx_ProgressFromTo(tolua_S);
  32.     lua_register_cocos2dx_MoveBy(tolua_S);
  33.     lua_register_cocos2dx_MoveTo(tolua_S);
  34.     lua_register_cocos2dx_JumpBy(tolua_S);
  35.     lua_register_cocos2dx_EventListener(tolua_S);
  36.     lua_register_cocos2dx_EventListenerKeyboard(tolua_S);
  37.     lua_register_cocos2dx_EventListenerMouse(tolua_S);
  38.     lua_register_cocos2dx_TransitionRotoZoom(tolua_S);
  39.     lua_register_cocos2dx_Event(tolua_S);
  40.     lua_register_cocos2dx_EventController(tolua_S);
  41.     lua_register_cocos2dx_Director(tolua_S);
  42.     lua_register_cocos2dx_Scheduler(tolua_S);
  43.     lua_register_cocos2dx_ActionEase(tolua_S);
  44.     lua_register_cocos2dx_EaseElastic(tolua_S);
  45.     lua_register_cocos2dx_EaseElasticOut(tolua_S);
  46.     lua_register_cocos2dx_EaseQuadraticActionInOut(tolua_S);
  47.     lua_register_cocos2dx_EaseBackOut(tolua_S);
  48.     lua_register_cocos2dx_Texture2D(tolua_S);
  49.     lua_register_cocos2dx_TransitionSceneOriented(tolua_S);
  50.     lua_register_cocos2dx_TransitionFlipX(tolua_S);
  51.     lua_register_cocos2dx_CameraBackgroundBrush(tolua_S);
  52.     lua_register_cocos2dx_CameraBackgroundDepthBrush(tolua_S);
  53.     lua_register_cocos2dx_CameraBackgroundColorBrush(tolua_S);
  54.     lua_register_cocos2dx_GridAction(tolua_S);
  55.     lua_register_cocos2dx_TiledGrid3DAction(tolua_S);
  56.     lua_register_cocos2dx_FadeOutTRTiles(tolua_S);
  57.     lua_register_cocos2dx_FadeOutUpTiles(tolua_S);
  58.     lua_register_cocos2dx_FadeOutDownTiles(tolua_S);
  59.     lua_register_cocos2dx_StopGrid(tolua_S);
  60.     lua_register_cocos2dx_Technique(tolua_S);
  61.     lua_register_cocos2dx_SkewTo(tolua_S);
  62.     lua_register_cocos2dx_SkewBy(tolua_S);
  63.     lua_register_cocos2dx_EaseQuadraticActionOut(tolua_S);
  64.     lua_register_cocos2dx_TransitionProgress(tolua_S);
  65.     lua_register_cocos2dx_TransitionProgressVertical(tolua_S);
  66.     lua_register_cocos2dx_Layer(tolua_S);
  67.     lua_register_cocos2dx_TMXTiledMap(tolua_S);
  68.     lua_register_cocos2dx_Grid3DAction(tolua_S);
  69.     lua_register_cocos2dx_BaseLight(tolua_S);
  70.     lua_register_cocos2dx_SpotLight(tolua_S);
  71.     lua_register_cocos2dx_FadeTo(tolua_S);
  72.     lua_register_cocos2dx_FadeIn(tolua_S);
  73.     lua_register_cocos2dx_DirectionLight(tolua_S);
  74.     lua_register_cocos2dx_EventListenerCustom(tolua_S);
  75.     lua_register_cocos2dx_FlipX3D(tolua_S);
  76.     lua_register_cocos2dx_FlipY3D(tolua_S);
  77.     lua_register_cocos2dx_EaseSineInOut(tolua_S);
  78.     lua_register_cocos2dx_TransitionFlipAngular(tolua_S);
  79.     lua_register_cocos2dx_EaseElasticInOut(tolua_S);
  80.     lua_register_cocos2dx_EaseBounce(tolua_S);
  81.     lua_register_cocos2dx_Show(tolua_S);
  82.     lua_register_cocos2dx_FadeOut(tolua_S);
  83.     lua_register_cocos2dx_CallFunc(tolua_S);
  84.     lua_register_cocos2dx_EventMouse(tolua_S);
  85.     lua_register_cocos2dx_GLView(tolua_S);
  86.     lua_register_cocos2dx_EaseBezierAction(tolua_S);
  87.     lua_register_cocos2dx_ParticleFireworks(tolua_S);
  88.     lua_register_cocos2dx_MenuItem(tolua_S);
  89.     lua_register_cocos2dx_MenuItemSprite(tolua_S);
  90.     lua_register_cocos2dx_MenuItemImage(tolua_S);
  91.     lua_register_cocos2dx_AutoPolygon(tolua_S);
  92.     lua_register_cocos2dx_ParticleSmoke(tolua_S);
  93.     lua_register_cocos2dx_TransitionZoomFlipAngular(tolua_S);
  94.     lua_register_cocos2dx_EaseRateAction(tolua_S);
  95.     lua_register_cocos2dx_EaseIn(tolua_S);
  96.     lua_register_cocos2dx_EaseExponentialInOut(tolua_S);
  97.     lua_register_cocos2dx_CardinalSplineTo(tolua_S);
  98.     lua_register_cocos2dx_CatmullRomTo(tolua_S);
  99.     lua_register_cocos2dx_Waves3D(tolua_S);
  100.     lua_register_cocos2dx_EaseExponentialOut(tolua_S);
  101.     lua_register_cocos2dx_Label(tolua_S);
  102.     lua_register_cocos2dx_Application(tolua_S);
  103.     lua_register_cocos2dx_DelayTime(tolua_S);
  104.     lua_register_cocos2dx_LabelAtlas(tolua_S);
  105.     lua_register_cocos2dx_EaseCircleActionOut(tolua_S);
  106.     lua_register_cocos2dx_SpriteBatchNode(tolua_S);
  107.     lua_register_cocos2dx_TMXLayer(tolua_S);
  108.     lua_register_cocos2dx_AsyncTaskPool(tolua_S);
  109.     lua_register_cocos2dx_ParticleSnow(tolua_S);
  110.     lua_register_cocos2dx_EaseElasticIn(tolua_S);
  111.     lua_register_cocos2dx_EaseCircleActionInOut(tolua_S);
  112.     lua_register_cocos2dx_TransitionFadeTR(tolua_S);
  113.     lua_register_cocos2dx_EaseQuarticActionOut(tolua_S);
  114.     lua_register_cocos2dx_EventAcceleration(tolua_S);
  115.     lua_register_cocos2dx_EaseCubicActionIn(tolua_S);
  116.     lua_register_cocos2dx_TextureCache(tolua_S);
  117.     lua_register_cocos2dx_ActionTween(tolua_S);
  118.     lua_register_cocos2dx_TransitionFadeDown(tolua_S);
  119.     lua_register_cocos2dx_ParticleSun(tolua_S);
  120.     lua_register_cocos2dx_TransitionProgressHorizontal(tolua_S);
  121.     lua_register_cocos2dx_ParticleFire(tolua_S);
  122.     lua_register_cocos2dx_FlipX(tolua_S);
  123.     lua_register_cocos2dx_FlipY(tolua_S);
  124.     lua_register_cocos2dx_EventKeyboard(tolua_S);
  125.     lua_register_cocos2dx_TransitionSplitCols(tolua_S);
  126.     lua_register_cocos2dx_Timer(tolua_S);
  127.     lua_register_cocos2dx_RepeatForever(tolua_S);
  128.     lua_register_cocos2dx_Place(tolua_S);
  129.     lua_register_cocos2dx_EventListenerAcceleration(tolua_S);
  130.     lua_register_cocos2dx_TiledGrid3D(tolua_S);
  131.     lua_register_cocos2dx_EaseBounceOut(tolua_S);
  132.     lua_register_cocos2dx_RenderTexture(tolua_S);
  133.     lua_register_cocos2dx_TintBy(tolua_S);
  134.     lua_register_cocos2dx_TransitionShrinkGrow(tolua_S);
  135.     lua_register_cocos2dx_ClippingNode(tolua_S);
  136.     lua_register_cocos2dx_ActionFloat(tolua_S);
  137.     lua_register_cocos2dx_ParticleFlower(tolua_S);
  138.     lua_register_cocos2dx_EaseCircleActionIn(tolua_S);
  139.     lua_register_cocos2dx_Image(tolua_S); // 加载图片资源
  140.     lua_register_cocos2dx_LayerMultiplex(tolua_S);
  141.     lua_register_cocos2dx_Blink(tolua_S);
  142.     lua_register_cocos2dx_ShaderCache(tolua_S);
  143.     lua_register_cocos2dx_JumpTo(tolua_S);
  144.     lua_register_cocos2dx_ParticleExplosion(tolua_S);
  145.     lua_register_cocos2dx_TransitionJumpZoom(tolua_S);
  146.     lua_register_cocos2dx_Pass(tolua_S);
  147.     lua_register_cocos2dx_Touch(tolua_S);
  148.     lua_register_cocos2dx_CardinalSplineBy(tolua_S);
  149.     lua_register_cocos2dx_CatmullRomBy(tolua_S);
  150.     lua_register_cocos2dx_NodeGrid(tolua_S);
  151.     lua_register_cocos2dx_TMXLayerInfo(tolua_S);
  152.     lua_register_cocos2dx_EaseSineIn(tolua_S);
  153.     lua_register_cocos2dx_EaseBounceIn(tolua_S);
  154.     lua_register_cocos2dx_Camera(tolua_S);
  155.     lua_register_cocos2dx_TMXObjectGroup(tolua_S);
  156.     lua_register_cocos2dx_FastTMXTiledMap(tolua_S);
  157.     lua_register_cocos2dx_ParticleGalaxy(tolua_S);
  158.     lua_register_cocos2dx_Twirl(tolua_S);
  159.     lua_register_cocos2dx_MenuItemLabel(tolua_S);
  160.     lua_register_cocos2dx_EaseQuinticActionIn(tolua_S);
  161.     lua_register_cocos2dx_LayerColor(tolua_S);
  162.     lua_register_cocos2dx_FadeOutBLTiles(tolua_S);
  163.     lua_register_cocos2dx_LayerGradient(tolua_S);
  164.     lua_register_cocos2dx_EventListenerTouchAllAtOnce(tolua_S);
  165.     lua_register_cocos2dx_GLViewImpl(tolua_S);
  166.     lua_register_cocos2dx_ToggleVisibility(tolua_S);
  167.     lua_register_cocos2dx_Repeat(tolua_S);
  168.     lua_register_cocos2dx_TransitionFlipY(tolua_S);
  169.     lua_register_cocos2dx_TurnOffTiles(tolua_S);
  170.     lua_register_cocos2dx_TintTo(tolua_S);
  171.     lua_register_cocos2dx_EaseBackInOut(tolua_S);
  172.     lua_register_cocos2dx_TransitionFadeBL(tolua_S);
  173.     lua_register_cocos2dx_TargetedAction(tolua_S);
  174.     lua_register_cocos2dx_DrawNode(tolua_S);
  175.     lua_register_cocos2dx_TransitionTurnOffTiles(tolua_S);
  176.     lua_register_cocos2dx_RotateTo(tolua_S);
  177.     lua_register_cocos2dx_TransitionSplitRows(tolua_S);
  178.     lua_register_cocos2dx_Device(tolua_S);
  179.     lua_register_cocos2dx_TransitionProgressRadialCCW(tolua_S);
  180.     lua_register_cocos2dx_ScaleTo(tolua_S);
  181.     lua_register_cocos2dx_TransitionPageTurn(tolua_S);
  182.     lua_register_cocos2dx_RenderState(tolua_S);
  183.     lua_register_cocos2dx_Properties(tolua_S);
  184.     lua_register_cocos2dx_BezierBy(tolua_S);
  185.     lua_register_cocos2dx_BezierTo(tolua_S);
  186.     lua_register_cocos2dx_ParticleMeteor(tolua_S);
  187.     lua_register_cocos2dx_SpriteFrame(tolua_S);
  188.     lua_register_cocos2dx_Liquid(tolua_S);
  189.     lua_register_cocos2dx_UserDefault(tolua_S);
  190.     lua_register_cocos2dx_FastTMXLayer(tolua_S);
  191.     lua_register_cocos2dx_TransitionZoomFlipX(tolua_S);
  192.     lua_register_cocos2dx_EventFocus(tolua_S);
  193.     lua_register_cocos2dx_TransitionFade(tolua_S);
  194.     lua_register_cocos2dx_EaseQuinticActionInOut(tolua_S);
  195.     lua_register_cocos2dx_SpriteFrameCache(tolua_S);
  196.     lua_register_cocos2dx_PointLight(tolua_S);
  197.     lua_register_cocos2dx_TransitionCrossFade(tolua_S);
  198.     lua_register_cocos2dx_Ripple3D(tolua_S);
  199.     lua_register_cocos2dx_Lens3D(tolua_S);
  200.     lua_register_cocos2dx_EventListenerFocus(tolua_S);
  201.     lua_register_cocos2dx_Spawn(tolua_S);
  202.     lua_register_cocos2dx_EaseQuarticActionInOut(tolua_S);
  203.     lua_register_cocos2dx_ShakyTiles3D(tolua_S);
  204.     lua_register_cocos2dx_PageTurn3D(tolua_S);
  205.     lua_register_cocos2dx_PolygonInfo(tolua_S);
  206.     lua_register_cocos2dx_TransitionSlideInL(tolua_S);
  207.     lua_register_cocos2dx_TransitionSlideInT(tolua_S);
  208.     lua_register_cocos2dx_Grid3D(tolua_S);
  209.     lua_register_cocos2dx_EventListenerController(tolua_S);
  210.     lua_register_cocos2dx_TransitionProgressInOut(tolua_S);
  211.     lua_register_cocos2dx_EaseCubicActionInOut(tolua_S);
  212.     lua_register_cocos2dx_ParticleData(tolua_S);
  213.     lua_register_cocos2dx_EaseBackIn(tolua_S);
  214.     lua_register_cocos2dx_SplitRows(tolua_S);
  215.     lua_register_cocos2dx_Follow(tolua_S);
  216.     lua_register_cocos2dx_Animate(tolua_S);
  217.     lua_register_cocos2dx_ShuffleTiles(tolua_S);
  218.     lua_register_cocos2dx_CameraBackgroundSkyBoxBrush(tolua_S);
  219.     lua_register_cocos2dx_ProgressTimer(tolua_S);
  220.     lua_register_cocos2dx_EaseQuarticActionIn(tolua_S);
  221.     lua_register_cocos2dx_Menu(tolua_S);
  222.     lua_register_cocos2dx_EaseInOut(tolua_S);
  223.     lua_register_cocos2dx_TransitionZoomFlipY(tolua_S);
  224.     lua_register_cocos2dx_ScaleBy(tolua_S);
  225.     lua_register_cocos2dx_EventTouch(tolua_S);
  226.     lua_register_cocos2dx_Animation(tolua_S);
  227.     lua_register_cocos2dx_TMXMapInfo(tolua_S);
  228.     lua_register_cocos2dx_EaseExponentialIn(tolua_S);
  229.     lua_register_cocos2dx_ReuseGrid(tolua_S);
  230.     lua_register_cocos2dx_EaseQuinticActionOut(tolua_S);
  231.     lua_register_cocos2dx_EventDispatcher(tolua_S);
  232.     lua_register_cocos2dx_MenuItemAtlasFont(tolua_S);
  233.     lua_register_cocos2dx_ActionManager(tolua_S);
  234.     lua_register_cocos2dx_OrbitCamera(tolua_S);
  235.     lua_register_cocos2dx_ClippingRectangleNode(tolua_S);
  236.     lua_register_cocos2dx_EventCustom(tolua_S);
  237.     lua_register_cocos2dx_ParticleBatchNode(tolua_S);
  238.     lua_register_cocos2dx_Component(tolua_S);
  239.     lua_register_cocos2dx_EaseCubicActionOut(tolua_S);
  240.     lua_register_cocos2dx_EventListenerTouchOneByOne(tolua_S);
  241.     lua_register_cocos2dx_Renderer(tolua_S);
  242.     lua_register_cocos2dx_ParticleRain(tolua_S);
  243.     lua_register_cocos2dx_Waves(tolua_S);
  244.     lua_register_cocos2dx_ComponentLua(tolua_S);
  245.     lua_register_cocos2dx_MotionStreak3D(tolua_S);
  246.     lua_register_cocos2dx_EaseOut(tolua_S);
  247.     lua_register_cocos2dx_MenuItemFont(tolua_S);
  248.     lua_register_cocos2dx_TransitionFadeUp(tolua_S);
  249.     lua_register_cocos2dx_LayerRadialGradient(tolua_S);
  250.     lua_register_cocos2dx_EaseSineOut(tolua_S);
  251.     lua_register_cocos2dx_JumpTiles3D(tolua_S);
  252.     lua_register_cocos2dx_MenuItemToggle(tolua_S);
  253.     lua_register_cocos2dx_RemoveSelf(tolua_S);
  254.     lua_register_cocos2dx_SplitCols(tolua_S);
  255.     lua_register_cocos2dx_ProtectedNode(tolua_S);
  256.     lua_register_cocos2dx_MotionStreak(tolua_S);
  257.     lua_register_cocos2dx_RotateBy(tolua_S);
  258.     lua_register_cocos2dx_FileUtils(tolua_S);
  259.     lua_register_cocos2dx_Sprite(tolua_S);
  260.     lua_register_cocos2dx_ProgressTo(tolua_S);
  261.     lua_register_cocos2dx_TransitionProgressOutIn(tolua_S);
  262.     lua_register_cocos2dx_AnimationFrame(tolua_S);
  263.     lua_register_cocos2dx_Sequence(tolua_S);
  264.     lua_register_cocos2dx_Shaky3D(tolua_S);
  265.     lua_register_cocos2dx_TransitionProgressRadialCW(tolua_S);
  266.     lua_register_cocos2dx_EaseBounceInOut(tolua_S);
  267.     lua_register_cocos2dx_TransitionSlideInR(tolua_S);
  268.     lua_register_cocos2dx_AmbientLight(tolua_S);
  269.     lua_register_cocos2dx_ParallaxNode(tolua_S);
  270.     lua_register_cocos2dx_EaseQuadraticActionIn(tolua_S);
  271.     lua_register_cocos2dx_WavesTiles3D(tolua_S);
  272.     lua_register_cocos2dx_TransitionSlideInB(tolua_S);
  273.     lua_register_cocos2dx_Speed(tolua_S);
  274.     lua_register_cocos2dx_ShatteredTiles3D(tolua_S);
  275.     tolua_endmodule(tolua_S);
  276.     return 1;
  277. }
复制代码
在大量注册函数中寻找到关键方法lua_register_cocos2dx_Image
  1. int lua_register_cocos2dx_Image(lua_State* tolua_S)
  2. {
  3.     tolua_usertype(tolua_S,"cc.Image");
  4.     tolua_cclass(tolua_S,"Image","cc.Image","cc.Ref",nullptr);
  5.     tolua_beginmodule(tolua_S,"Image");
  6.         tolua_function(tolua_S,"new",lua_cocos2dx_Image_constructor);
  7.         tolua_function(tolua_S,"hasPremultipliedAlpha",lua_cocos2dx_Image_hasPremultipliedAlpha);
  8.         tolua_function(tolua_S,"reversePremultipliedAlpha",lua_cocos2dx_Image_reversePremultipliedAlpha);
  9.         tolua_function(tolua_S,"isCompressed",lua_cocos2dx_Image_isCompressed);
  10.         tolua_function(tolua_S,"hasAlpha",lua_cocos2dx_Image_hasAlpha);
  11.         tolua_function(tolua_S,"getPixelFormat",lua_cocos2dx_Image_getPixelFormat);
  12.         tolua_function(tolua_S,"getHeight",lua_cocos2dx_Image_getHeight);
  13.         tolua_function(tolua_S,"premultiplyAlpha",lua_cocos2dx_Image_premultiplyAlpha);
  14.         tolua_function(tolua_S,"initWithImageFile",lua_cocos2dx_Image_initWithImageFile); // 初始化图片文件
  15.         tolua_function(tolua_S,"getWidth",lua_cocos2dx_Image_getWidth);
  16.         tolua_function(tolua_S,"getBitPerPixel",lua_cocos2dx_Image_getBitPerPixel);
  17.         tolua_function(tolua_S,"getFileType",lua_cocos2dx_Image_getFileType);
  18.         tolua_function(tolua_S,"getFilePath",lua_cocos2dx_Image_getFilePath);
  19.         tolua_function(tolua_S,"getNumberOfMipmaps",lua_cocos2dx_Image_getNumberOfMipmaps);
  20.         tolua_function(tolua_S,"saveToFile",lua_cocos2dx_Image_saveToFile);
  21.         tolua_function(tolua_S,"setPVRImagesHavePremultipliedAlpha", lua_cocos2dx_Image_setPVRImagesHavePremultipliedAlpha);
  22.         tolua_function(tolua_S,"setPNGPremultipliedAlphaEnabled", lua_cocos2dx_Image_setPNGPremultipliedAlphaEnabled);
  23.     tolua_endmodule(tolua_S);
  24.     std::string typeName = typeid(cocos2d::Image).name();
  25.     g_luaType[typeName] = "cc.Image";
  26.     g_typeCast["Image"] = "cc.Image";
  27.     return 1;
  28. }
复制代码
lua_cocos2dx_Image_initWithImageFile加载了我们的图片文件
  1. int lua_cocos2dx_Image_initWithImageFile(lua_State* tolua_S)
  2. {
  3.     int argc = 0;
  4.     cocos2d::Image* cobj = nullptr;
  5.     bool ok  = true;
  6. #if COCOS2D_DEBUG >= 1
  7.     tolua_Error tolua_err;
  8. #endif
  9. #if COCOS2D_DEBUG >= 1
  10.     if (!tolua_isusertype(tolua_S,1,"cc.Image",0,&tolua_err)) goto tolua_lerror;
  11. #endif
  12.     cobj = (cocos2d::Image*)tolua_tousertype(tolua_S,1,0);
  13. #if COCOS2D_DEBUG >= 1
  14.     if (!cobj)
  15.     {
  16.         tolua_error(tolua_S,"invalid 'cobj' in function 'lua_cocos2dx_Image_initWithImageFile'", nullptr);
  17.         return 0;
  18.     }
  19. #endif
  20.     argc = lua_gettop(tolua_S)-1;
  21.     if (argc == 1)
  22.     {
  23.         std::string arg0;
  24.         ok &= luaval_to_std_string(tolua_S, 2,&arg0, "cc.Image:initWithImageFile");
  25.         if(!ok)
  26.         {
  27.             tolua_error(tolua_S,"invalid arguments in function 'lua_cocos2dx_Image_initWithImageFile'", nullptr);
  28.             return 0;
  29.         }
  30.         bool ret = cobj->initWithImageFile(arg0);
  31.         tolua_pushboolean(tolua_S,(bool)ret);
  32.         return 1;
  33.     }
  34.     luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "cc.Image:initWithImageFile",argc, 1);
  35.     return 0;
  36. #if COCOS2D_DEBUG >= 1
  37.     tolua_lerror:
  38.     tolua_error(tolua_S,"#ferror in function 'lua_cocos2dx_Image_initWithImageFile'.",&tolua_err);
  39. #endif
  40.     return 0;
  41. }
复制代码
下面看cobj->initWithImageFile
  1. bool Image::initWithImageFile(const std::string& path)
  2. {
  3.     bool ret = false;
  4.     _filePath = FileUtils::getInstance()->fullPathForFilename(path);
  5.     Data data = FileUtils::getInstance()->getDataFromFile(_filePath); // 获取文件数据
  6.     if (!data.isNull())
  7.     {
  8.         ret = initWithImageData(data.getBytes(), data.getSize()); // 解码/解密 处理文件数据
  9.     }
  10.     return ret;
  11. }
复制代码
下面是图片数据格式解码方法initWithImageData
  1. bool Image::initWithImageData(const unsigned char * data, ssize_t dataLen)
  2. {
  3.     bool ret = false;
  4.     do
  5.     {
  6.         CC_BREAK_IF(! data || dataLen <= 0);
  7.         unsigned char* unpackedData = nullptr;
  8.         ssize_t unpackedLen = 0;
  9.         //detect and unzip the compress file
  10.         if (ZipUtils::isCCZBuffer(data, dataLen))
  11.         {
  12.             unpackedLen = ZipUtils::inflateCCZBuffer(data, dataLen, &unpackedData);
  13.         }
  14.         else if (ZipUtils::isGZipBuffer(data, dataLen))
  15.         {
  16.             unpackedLen = ZipUtils::inflateMemory(const_cast<unsigned char*>(data), dataLen, &unpackedData);
  17.         }
  18.         else
  19.         {
  20.             unpackedData = const_cast<unsigned char*>(data);
  21.             unpackedLen = dataLen;
  22.         }
  23.         _fileType = detectFormat(unpackedData, unpackedLen);
  24.         switch (_fileType)
  25.         {
  26.         case Format::PNG:
  27.             ret = initWithPngData(unpackedData, unpackedLen);
  28.             break;
  29.         case Format::JPG:
  30.             ret = initWithJpgData(unpackedData, unpackedLen);
  31.             break;
  32.         case Format::WEBP:
  33.             ret = initWithWebpData(unpackedData, unpackedLen);
  34.             break;
  35.         case Format::PVR:
  36.             ret = initWithPVRData(unpackedData, unpackedLen);
  37.             break;
  38.         case Format::ETC:
  39.             ret = initWithETCData(unpackedData, unpackedLen);
  40.             break;
  41.         case Format::S3TC:
  42.             ret = initWithS3TCData(unpackedData, unpackedLen);
  43.             break;
  44.         case Format::ATITC:
  45.             ret = initWithATITCData(unpackedData, unpackedLen);
  46.             break;
  47.         default:
  48.             {
  49.                 // load and detect image format
  50.                 tImageTGA* tgaData = tgaLoadBuffer(unpackedData, unpackedLen);
  51.                 if (tgaData != nullptr && tgaData->status == TGA_OK)
  52.                 {
  53.                     ret = initWithTGAData(tgaData);
  54.                 }
  55.                 else
  56.                 {
  57.                     CCLOG("cocos2d: unsupported image format!");
  58.                 }
  59.                 free(tgaData);
  60.                 break;
  61.             }
  62.         }
  63.         if(unpackedData != data)
  64.         {
  65.             free(unpackedData);
  66.         }
  67.     } while (0);
  68.     return ret;
  69. }
复制代码
图片资源的加载到这里就结束了
截图202507131411014194.png
图片资源解密快速定位
根据调用流程,我们可以直接定位方法cocos2d::Image::initWithImageData

向上可以追溯到cocos2d::Image::initWithImageFile
截图202507131411164671.png
看到了可疑函数cocos2d::Image::decodePngData
  1. cocos2d *__fastcall cocos2d::Image::decodePngData(cocos2d::Image *this, cocos2d::Data *a2)
  2. {
  3.   _BYTE *v4; // r7
  4.   int v5; // r5
  5.   signed int v6; // r0
  6.   bool v7; // cc
  7.   int v8; // r4
  8.   void *v9; // r0
  9.   int v10; // r2
  10.   int v12; // r6
  11.   int v13; // r4
  12.   int v14; // r9
  13.   int v15; // r2
  14.   int v16; // r0
  15.   int v17; // r3
  16.   _BYTE *v18; // r5
  17.   unsigned int *v19; // r2
  18.   int v20; // r3
  19.   int v21; // [sp+8h] [bp-28h] BYREF
  20.   v4 = (_BYTE *)cocos2d::Data::getBytes(a2);
  21.   v5 = cocos2d::Data::getSize(a2);
  22.   sub_FBDCA8(&v21, (int *)this + 47);
  23.   v6 = sub_FBC854((int)&v21, ".png", 0, 4u);
  24.   v7 = v6 <= -1;
  25.   if ( v6 != -1 )
  26.     v7 = v5 <= 24;
  27.   v8 = !v7;
  28.   v9 = (void *)(v21 - 12);
  29.   if ( (int *)(v21 - 12) != &dword_12F6978 )
  30.   {
  31.     if ( &pthread_create )
  32.     {
  33.       v19 = (unsigned int *)(v21 - 4);
  34.       __dmb(0xFu);
  35.       do
  36.         v20 = __ldrex(v19);
  37.       while ( __strex(v20 - 1, v19) );
  38.       __dmb(0xFu);
  39.     }
  40.     else
  41.     {
  42.       v20 = *(_DWORD *)(v21 - 4);
  43.       *(_DWORD *)(v21 - 4) = v20 - 1;
  44.     }
  45.     if ( v20 <= 0 )
  46.       operator delete(v9);
  47.   }
  48.   if ( v8 && memcmp(&unk_11CBE0C, v4, 8u) )     // PNG Header
  49.   {
  50.     v12 = v5 - 4;
  51.     v13 = v5 - 8;
  52.     v14 = cocos2d::hexToDecimal((cocos2d *)&v4[v5 - 4], (unsigned __int8 *)&byte_4, v10);
  53.     v16 = cocos2d::hexToDecimal((cocos2d *)&v4[v5 - 8], (unsigned __int8 *)&byte_4, v15);
  54.     if ( v5 >= v16 )
  55.       cocos2d::decodePng(v4, (unsigned __int8 *)(v14 - 2048), v16, v17);
  56.     v18 = &v4[v5];
  57.     *(v18 - 1) = 130;
  58.     *(v18 - 2) = 96;
  59.     *(v18 - 3) = 66;
  60.     v4[v12] = 174;
  61.     *(v18 - 5) = 68;
  62.     *(v18 - 6) = 78;
  63.     *(v18 - 7) = 69;
  64.     v4[v13] = 73;
  65.   }
  66.   return (cocos2d *)v4;
  67. }
复制代码
明显是一个算法函数,看方法cocos2d::decodePng
  1. _BYTE *__fastcall cocos2d::decodePng(_BYTE *this, unsigned __int8 *a2, int a3, int a4)
  2. {
  3.   char v4; // r12
  4.   unsigned int v5; // r1
  5.   int v6; // r12
  6.   char v7[4]; // [sp+0h] [bp-10h]
  7.   v7[1] = BYTE2(a2);
  8.   v4 = BYTE1(a2);
  9.   v7[3] = (char)a2;
  10.   v5 = (unsigned int)a2 >> 24;
  11.   v7[2] = v4;
  12.   v6 = 0;
  13.   v7[0] = v5;
  14.   if ( a3 > 0 )
  15.   {
  16.     while ( 1 )
  17.     {
  18.       ++v6;
  19.       *this++ ^= v5;
  20.       if ( v6 == a3 )
  21.         break;
  22.       LOBYTE(v5) = v7[v6 % 4];
  23.     }
  24.   }
  25.   return this;
  26. }
复制代码
这里也可以尝试Frida Hook验证猜想

同样的,我就不Hook了
算法还原


  1. def _hexToDecimal(data, offset):
  2.     if offset <= 0:
  3.         return 0
  4.     return struct.unpack(">I", data[:offset])[0]
  5. def LOBYTE(d):
  6.     return d & 0xff
  7. def _decodePng(data, magic, rounds):
  8.     dthis = bytearray(data)
  9.     v7 = [0] * 4
  10.     a2 = struct.pack("<i", magic)
  11.     v7[1] = a2[2]
  12.     v7[3] = a2[0]
  13.     v7[0] = v5 = magic >> 24
  14.     v7[2] = a2[1]
  15.     i = 0
  16.     if rounds > 0:
  17.         while 1:
  18.             i += 1
  19.             dthis[i-1] ^= v5
  20.             if i == rounds:
  21.                 break
  22.             v5 = LOBYTE(v7[i % 4])
  23.     return dthis
  24. def decodePng(data, size):
  25.     v14 = _hexToDecimal(data[-4:], 4)
  26.     v16 = _hexToDecimal(data[-8:], 4)
  27.     if size >= v16:
  28.         ret = _decodePng(data, v14 - 2048, v16)
  29.         ret[-1] = 130
  30.         ret[-2] = 96
  31.         ret[-3] = 66
  32.         ret[-4] = 174
  33.         ret[-5] = 68
  34.         ret[-6] = 78
  35.         ret[-7] = 69
  36.         ret[-8] = 73
  37.     return ret
复制代码
解密结果
截图202507131412172871.png |

总结
写不出总结啦,大概最大的感想就是:对照源码逆向好爽!!!




这个人还没有签名
661784 | 2025-7-16 18:41:23 来自手机 | 显示全部楼层
看看
这个人还没有签名
hhwsljb | 2026-2-8 19:59:41 | 显示全部楼层
感谢提供教程。
这个人还没有签名
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则