[转]Cocos2dlua手游 Lua解密与资源解密实战

news/2024/10/3 2:16:04

来自看雪:Cocos2dlua手游 Lua解密与资源解密实战

https://mp.weixin.qq.com/s/WeYxlXZvCPv_3nGgeKdunw

 

Cocos2dlua 逆向相关学习,略有删减,直接进入正文

APK大致如下:

.
├── assets
│   ├── res
│   │   ├── ani
│   │   │   └── logo
│   │   │       └── logo.png
│   ├── src
│   │   ├── main.lua
│   │   └── main.lua64
├── lib
│   ├── arm64-v8a
│   │   ├── libBugly.so
│   │   └── libcocos2dlua.so
│   └── armeabi-v7a
│       ├── libBugly.so
│       └── libcocos2dlua.so
└── stamp-cert-sha256

其中.lua64为标准LuaJit文件,而.lua的文件头有点奇怪。

 .lua文件头:abcd,再看资源文件.png,发现也是加密的:

 综上,我们需要实现的目标:1、解密.lua文件;2、解密.png文件。

这个不多介绍了窝,在理论篇中已经提到了。对于此样本的LuaJit的版本是2.1.0-Beta2,并且没有魔改Opcode。


Lua文件加载流程:想要解密.lua文件,了解coco2d-x加载Lua的流程必不可少。

Cocos2d-x环境搭建:参考官方Docs,搭建所需环境:

由于Android的应用层是从Activity开始的,也就是创建完一个Cocos2dx后src文件夹下的Java文件,其中主要看Activity创建时的操作。

package org.cocos2dx.lua;import android.os.Bundle;
import org.cocos2dx.lib.Cocos2dxActivity;public class AppActivity extends Cocos2dxActivity{@Overrideprotected void onCreate(Bundle savedInstanceState) {super.setEnableVirtualButton(false);super.onCreate(savedInstanceState);// Workaround in https://stackoverflow.com/questions/16283079/re-launch-of-activity-on-home-button-but-only-the-first-time/16447508if (!isTaskRoot()) {// Android launched another instance of the root activity into an existing task//  so just quietly finish and go away, dropping the user back into the activity//  at the top of the stack (ie: the last state of this task)// Don't need to finish it again since it's finished in super.onCreate .return;}// DO OTHER INITIALIZATION BELOW
 }
}

这个方法很简单,就只调用了父类Cocos2dxActivity的onCreate方法:

@Overrideprotected void onCreate(final Bundle savedInstanceState) {super.onCreate(savedInstanceState);// Workaround in https://stackoverflow.com/questions/16283079/re-launch-of-activity-on-home-button-but-only-the-first-time/16447508if (!isTaskRoot()) {// Android launched another instance of the root activity into an existing task//  so just quietly finish and go away, dropping the user back into the activity//  at the top of the stack (ie: the last state of this task)
            finish();Log.w(TAG, "[Workaround] Ignore the activity started from icon!");return;}this.hideVirtualButton();onLoadNativeLibraries();sContext = this;this.mHandler = new Cocos2dxHandler(this);Cocos2dxHelper.init(this);this.mGLContextAttrs = getGLContextAttrs();this.init();if (mVideoHelper == null) {mVideoHelper = new Cocos2dxVideoHelper(this, mFrameLayout);}if(mWebViewHelper == null){mWebViewHelper = new Cocos2dxWebViewHelper(mFrameLayout);}if(mEditBoxHelper == null){mEditBoxHelper = new Cocos2dxEditBoxHelper(mFrameLayout);}Window window = this.getWindow();window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);// Audio configurationthis.setVolumeControlStream(AudioManager.STREAM_MUSIC);}

在这个方法中主要看Cocos2dxActivity的初始化方法init,Cocos2dxHandler是工具辅助类,不是重点。

public Cocos2dxGLSurfaceView onCreateView() {Cocos2dxGLSurfaceView glSurfaceView = new Cocos2dxGLSurfaceView(this);//this line is need on some device if we specify an alpha bits// FIXME: is it needed? And it will cause afterimage.// if(this.mGLContextAttrs[3] > 0) glSurfaceView.getHolder().setFormat(PixelFormat.TRANSLUCENT);// use custom EGLConfigureChooserCocos2dxEGLConfigChooser chooser = new Cocos2dxEGLConfigChooser(this.mGLContextAttrs);glSurfaceView.setEGLConfigChooser(chooser);return glSurfaceView;}
// ......
public void init() {// FrameLayout 初始化窗口布局ViewGroup.LayoutParams framelayout_params =new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.MATCH_PARENT);mFrameLayout = new ResizeLayout(this);mFrameLayout.setLayoutParams(framelayout_params);// Cocos2dxEditText layout 初始化Cocos2dx的文本编辑布局ViewGroup.LayoutParams edittext_layout_params =new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.WRAP_CONTENT);Cocos2dxEditBox edittext = new Cocos2dxEditBox(this);edittext.setLayoutParams(edittext_layout_params);mFrameLayout.addView(edittext);// Cocos2dxGLSurfaceView 初始化Cocos2dx视图this.mGLSurfaceView = this.onCreateView();// ...add to FrameLayout 将Cocos2dxGLSurfaceView加入到当前的窗口布局中mFrameLayout.addView(this.mGLSurfaceView);// Switch to supported OpenGL (ARGB888) mode on emulator// this line dows not needed on new emulators and also it breaks stencil buffer//if (isAndroidEmulator()) // 在模拟器中切换支持OpenGL模式的渲染(ARGB888)//   this.mGLSurfaceView.setEGLConfigChooser(8, 8, 8, 8, 16, 0);// 设置Cocos2dx的渲染器this.mGLSurfaceView.setCocos2dxRenderer(new Cocos2dxRenderer());//设置Cocos2dx的文本编辑this.mGLSurfaceView.setCocos2dxEditText(edittext);// Set framelayout as the content view// 把显示布局(即Cocos2dx的视图)绑定到Activity上,建立显示窗口
        setContentView(mFrameLayout);}

首先在init中先看this.mGLSurfaceView = this.onCreateView();this.mGLSurfaceView是一个Cocos2dxGLSurfaceView类。 进入到Cocos2dxGLSurfaceView这个类,可以看到时继承于GLSurfaceView(可以把GLSurfaceView看成一个视图,里面有个方法设置了这个视图的渲染器,然后通过这个渲染器来进行画面的渲染)。
在Android中,GLSurfaceView是一个支持OpenGL的渲染视图,通过继承SurfaceView中的surface来渲染OpenGL。
并提供了以下特性(来源于网上):

  1. 管理一个surface,这个surface就是一块特殊的内存,能直接排版到android的视图view上
  2. 管理一个EGL display,它能让opengl把内容渲染到上述的surface上
  3. 用户自定义渲染器(render)
  4. 让渲染器在独立的线程里运作,和UI线程分离
  5. 支持按需渲染(on-demand)和连续渲染(continuous)
  6. 一些可选工具,如调试

重点是自定义的渲染器,也就是Cocos2dx引擎封装的渲染器。 进入Cocos2dxRenderer类,看到他是继承GLSurfaceView.Renderer接口,这个接口定义了三个方法:

  • onSurfaceCreated: 创建GLSurfaceView时被调用,只调用一次,做初始化工作
  • onSurfaceChanged: 当GLSurfaceView的几何体被改变时被调用
  • onDrawFrame: 绘制渲染GLSurfaceView


onSurfaceCreated:

@Overridepublic void onSurfaceCreated(final GL10 GL10, final EGLConfig EGLConfig) {Cocos2dxRenderer.nativeInit(this.mScreenWidth, this.mScreenHeight);this.mLastTickInNanoSeconds = System.nanoTime();mNativeInitCompleted = true;}

nativeInit是一个Native函数:

private static native void nativeInit(final int width, final int height);

具体实现在frameworks\cocos2d-x\cocos\platform\android\javaactivity-android.cpp中:

JNIEXPORT void Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeInit(JNIEnv*  env, jobject thiz, jint w, jint h)
{auto director = cocos2d::Director::getInstance();auto glview = director->getOpenGLView();if (!glview){glview = cocos2d::GLViewImpl::create("Android app");glview->setFrameSize(w, h);director->setOpenGLView(glview);cocos2d::Application::getInstance()->run();}else{cocos2d::Director::getInstance()->resetMatrixStack();cocos2d::EventCustom recreatedEvent(EVENT_RENDERER_RECREATED);director->getEventDispatcher()->dispatchEvent(&recreatedEvent);director->setGLDefaultValues();cocos2d::VolatileTextureMgr::reloadAllTextures();}cocos2d::network::_preloadJavaDownloaderClass();
}

重点是cocos2d::Application::getInstance()->run()。

int Application::run()
{// Initialize instance and cocos2d.if (! applicationDidFinishLaunching()){return 0;}return -1;
}

applicationDidFinishLaunching,没错这就是游戏逻辑的入口了:

bool AppDelegate::applicationDidFinishLaunching()
{// set default FPSDirector::getInstance()->setAnimationInterval(1.0 / 60.0f);// register lua module// 初始化 LuaEngine, 在 getInstance 中会初始化 LuaStack, LuaStack 初始化 Lua 环境相关auto engine = LuaEngine::getInstance();// 将 LuaEngine 添加到脚本引擎管理器 ScriptEngineManager 中ScriptEngineManager::getInstance()->setScriptEngine(engine);// 获取 Lua 环境lua_State* L = engine->getLuaStack()->getLuaState();// 注册额外的 C++ API 相关,比如 cocosstudio, spine, audio 相关
    lua_module_register(L);// 设置 cocos 自带的加密相关
    register_all_packages();// 在 LuaStack::executeScriptFile 执行脚本文件时,会通过 LuaStack::luaLoadBuffer 对文件进行解密LuaStack* stack = engine->getLuaStack();stack->setXXTEAKeyAndSign("2dxLua", strlen("2dxLua"), "XXTEA", strlen("XXTEA"));//register custom function//LuaStack* stack = engine->getLuaStack();//register_custom_function(stack->getLuaState());#if CC_64BITSFileUtils::getInstance()->addSearchPath("src/64bit");
#endifFileUtils::getInstance()->addSearchPath("src");FileUtils::getInstance()->addSearchPath("res");if (engine->executeScriptFile("main.lua")){return false;}return true;
}

LuaEngine::getInstance();这段代码实例化了LuaEngine。 下面分析LuaEngine初始化过程:

LuaEngine* LuaEngine::getInstance(void)
{if (!_defaultEngine){_defaultEngine = new (std::nothrow) LuaEngine();_defaultEngine->init();}return _defaultEngine;
}

接下来是_defaultEngine->init():

bool LuaEngine::init(void)
{_stack = LuaStack::create();_stack->retain();return true;
}

继续进入:

LuaStack *LuaStack::create()
{LuaStack *stack = new (std::nothrow) LuaStack();stack->init();stack->autorelease();return stack;
}

下面是stack->init():

bool LuaStack::init()
{// 初始化Lua环境并打开标准库_state = lua_open();luaL_openlibs(_state);toluafix_open(_state);// Register our version of the global "print" function// 注册全局函数print到lua中,它会覆盖lua库中的print方法const luaL_Reg global_functions [] = {{"print", lua_print},{"release_print",lua_release_print},{nullptr, nullptr}};// 注册全局变量luaL_register(_state, "_G", global_functions);// 注册cocos2d-x引擎的API到lua环境中
    g_luaType.clear();register_all_cocos2dx(_state);register_all_cocos2dx_backend(_state);register_all_cocos2dx_manual(_state);register_all_cocos2dx_module_manual(_state);register_all_cocos2dx_math_manual(_state);register_all_cocos2dx_shaders_manual(_state);register_all_cocos2dx_bytearray_manual(_state);tolua_luanode_open(_state);register_luanode_manual(_state);
#if CC_USE_PHYSICS// 导入使用的physics相关API
    register_all_cocos2dx_physics(_state);register_all_cocos2dx_physics_manual(_state);
#endif#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_MAC)// 导入ios下调用object-c相关API
    LuaObjcBridge::luaopen_luaoc(_state);
#endif#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)// 导入android下调用java相关API
    LuaJavaBridge::luaopen_luaj(_state);
#endifregister_all_cocos2dx_deprecated(_state);register_all_cocos2dx_manual_deprecated(_state);tolua_script_handler_mgr_open(_state);// add cocos2dx loader// 添加Lua的加载器,该方法将cocos2dx_lua_loader方法添加到Lua全局变量package下的loaders成员中// 当requires加载脚本时,Lua会使用package下的loaders中的加载器,即cocos2dx_lua_loader来加载// 设定cocos2dx_lua_loader,可以使得我们自定义设置搜索路径相关,且拓展实现对脚本的加密解密相关
    addLuaLoader(cocos2dx_lua_loader);return true;
}

重点在cocos2dx_lua_loader方法(这个方法在frameworks\cocos2d-x\cocos\scripting\lua-bindings\manual\Cocos2dxLuaLoader.cpp):

int cocos2dx_lua_loader(lua_State *L)
{// 后缀为luac和luastatic const std::string BYTECODE_FILE_EXT    = ".luac";static const std::string NOT_BYTECODE_FILE_EXT = ".lua";// require传入的要加载的文件名,例如:require("a.b") 查找文件为:a/b.luastd::string filename(luaL_checkstring(L, 1));size_t pos = filename.rfind(BYTECODE_FILE_EXT);// 去掉后缀名".luac"或“.lua”if (pos != std::string::npos && pos == filename.length() - BYTECODE_FILE_EXT.length())filename = filename.substr(0, pos);else{pos = filename.rfind(NOT_BYTECODE_FILE_EXT);if (pos != std::string::npos && pos == filename.length() - NOT_BYTECODE_FILE_EXT.length())filename = filename.substr(0, pos);}// 将 "." 替换为 "/"pos = filename.find_first_of('.');while (pos != std::string::npos){filename.replace(pos, 1, "/");pos = filename.find_first_of('.');}// search file in package.path
        Data chunk;std::string chunkName;FileUtils* utils = FileUtils::getInstance();// 获取 package.path 的变量lua_getglobal(L, "package");lua_getfield(L, -1, "path");// 通过 package.path 获取搜索路径相关,该路径为模版路径,格式类似于:// ?; ?.lua; c:\Users\?;  /usr/local/lua/lua/?/?.lua 以“;”作为分割符std::string searchpath(lua_tostring(L, -1));lua_pop(L, 1);size_t begin = 0;size_t next = searchpath.find_first_of(';', 0);// 遍历 package.path 中的所有路径,查找文件是否存在,若文件存在则通过 getDataFromFile 读取文件数据do{if (next == std::string::npos)next = searchpath.length();std::string prefix = searchpath.substr(begin, next-begin);if (prefix[0] == '.' && prefix[1] == '/')prefix = prefix.substr(2);pos = prefix.rfind(BYTECODE_FILE_EXT);if (pos != std::string::npos && pos == prefix.length() - BYTECODE_FILE_EXT.length()){prefix = prefix.substr(0, pos);}else{pos = prefix.rfind(NOT_BYTECODE_FILE_EXT);if (pos != std::string::npos && pos == prefix.length() - NOT_BYTECODE_FILE_EXT.length())prefix = prefix.substr(0, pos);}pos = prefix.find_first_of('?', 0);while (pos != std::string::npos){prefix.replace(pos, 1, filename);pos = prefix.find_first_of('?', pos + filename.length() + 1);}chunkName = prefix + BYTECODE_FILE_EXT;if (utils->isFileExist(chunkName)) // && !utils->isDirectoryExist(chunkName))
            {chunk = utils->getDataFromFile(chunkName);break;}else{chunkName = prefix + NOT_BYTECODE_FILE_EXT;if (utils->isFileExist(chunkName) ) //&& !utils->isDirectoryExist(chunkName))
                {chunk = utils->getDataFromFile(chunkName);break;}else{chunkName = prefix;if (utils->isFileExist(chunkName)) // && !utils->isDirectoryExist(chunkName))
                    {chunk = utils->getDataFromFile(chunkName);break;}}}// 指定搜素路径下不存在该文件, 下一个begin = next + 1;next = searchpath.find_first_of(';', begin);} while (begin < searchpath.length());// 判断文件内容是否获取成功if (chunk.getSize() > 0){// 加载文件LuaStack* stack = LuaEngine::getInstance()->getLuaStack();stack->luaLoadBuffer(L, reinterpret_cast<const char*>(chunk.getBytes()),static_cast<int>(chunk.getSize()), chunkName.c_str());}else{CCLOG("can not get file data of %s", chunkName.c_str());return 0;}return 1;}

通过此处的代码,可以了解到cocos2d-x是如何搜索指定的lua文件。 同时也会明白require为何可以使用 "." 来设定文件路径了,比如:

require "cocos.cocos2d.Cocos2d"
require "cocos.cocos2d.Cocos2dConstants"
require "cocos.cocos2d.functions"

再来看下stack->luaLoadBuffer的实现:

int LuaStack::luaLoadBuffer(lua_State *L, const char *chunk, int chunkSize, const char *chunkName)
{int r = 0;// 判断是否加密,若lua脚本加密,则解密后在加载脚本文件// luaL_loadbuffer 用于加载并编译Lua代码,并将其压入栈中if (_xxteaEnabled && strncmp(chunk, _xxteaSign, _xxteaSignLen) == 0){// decrypt XXTEAxxtea_long len = 0;unsigned char* result = xxtea_decrypt((unsigned char*)chunk + _xxteaSignLen,(xxtea_long)chunkSize - _xxteaSignLen,(unsigned char*)_xxteaKey,(xxtea_long)_xxteaKeyLen,&len);unsigned char* content = result;xxtea_long contentSize = len;skipBOM((const char*&)content, (int&)contentSize);r = luaL_loadbuffer(L, (char*)content, contentSize, chunkName);free(result);}else{skipBOM(chunk, chunkSize);r = luaL_loadbuffer(L, chunk, chunkSize, chunkName);}// 判定内容是否存在错误
#if defined(COCOS2D_DEBUG) && COCOS2D_DEBUG > 0if (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);}}
#endifreturn r;
}

至此,Lua文件的加载流程结束。 下面是Lua文件主要加载流程图

 了解了Coco2d-x Lua文件基本的加载流程,可以帮助我们很快的定位关键方法。

快速定位:由于所有的Lua文件加载必定经过cocos2d::LuaStack::luaLoadBuffer,所以可以直接定位,进行回溯。

 定位至cocos2dx_lua_loader:

 在luaLoadBuffer前调用了decodeLuaData,怀疑这就是解密Lua的关键方法。

下面是伪代码:

_BYTE *__fastcall decodeLuaData(cocos2d::Data *a1, int *size)
{_BYTE *v3; // r0_BYTE *v4; // r5int v6; // r0int v7; // r2int v8; // r3bool v9; // ccunsigned int v10; // r1_BYTE *v11; // r12_BYTE *v12; // r0char v13; // t1_BYTE *v14; // r0char v15[4]; // [sp+0h] [bp-18h]
 v3 = (_BYTE *)cocos2d::Data::getBytes(a1);v4 = v3;if ( *size > 8 && *v3 == 'a' && v3[1] == 'b' && v3[2] == 'c' && v3[3] == 'd' ){v6 = _hexToDecimal(v3 + 4, 4);v7 = *size;v8 = 0;v9 = *size <= 8;v15[3] = v6;v15[2] = (unsigned __int16)(v6 - 2048) >> 8;v10 = (unsigned int)(v6 - 2048) >> 24;v15[1] = (unsigned int)(v6 - 2048) >> 16;v15[0] = (unsigned int)(v6 - 2048) >> 24;if ( !v9 ){v11 = v4 - 1;v12 = v4 + 7;while ( 1 ){v13 = *++v12;++v8;*++v11 = v13 ^ v10;v7 = *size;if ( *size <= v8 + 8 )break;LOBYTE(v10) = v15[v8 & 3];}}v14 = &v4[v7 - 8];*v14 = 0;v14[1] = 0;v14[2] = 0;v14[3] = 0;v14[4] = 0;v14[5] = 0;v14[6] = 0;v14[7] = 0;*size -= 8;}return v4;
}

此方法首先获取文件数据,而后判断文件头,文件头正是abcd。 可以肯定这就是解密.lua的算法。如果不确定,我们可以使用Frida进行Hook验证猜想,我这里就不Hook了。

算法还原:要实现算法的还原,我们就要依据伪代码或是汇编代码翻译成可用代码,可以发现IDA所反汇编的伪代码中包含了一些特定的宏,例如:LOBYTE
 要了解这些宏的用途,我们可以参考ida_root/plugins/def.h
我这里使用了Python:

import struct
from loguru import loggerdef _hexToDecimal(data, offset):if offset <= 0:return 0return struct.unpack(">I", data[:offset])[0]def LOBYTE(d):return d & 0xffdef decodeLuaData(data, size):v4 = bytearray(data)v15 = [0] * 4if size > 8 and data[0] == ord('a') and data[1] == ord('b') and data[2] == ord('c') and data[3] == ord('d'):logger.info("find special exts")v6 = _hexToDecimal(data[4:], 4)v15[3] = v6v15[2] = (v6 - 2048) >> 8v10 = (v6 - 2048) >> 24v15[1] = (v6 - 2048) >> 16v15[0] = (v6 - 2048) >> 24if size > 8:i = 0while 1:v13 = v4[8 + i]v4[i] = v13 ^ v10i += 1if size <= i + 8:breakv10 = LOBYTE(v15[i & 3])size -= 8return v4[:-8]

解密后,发现还是LuaJit,那么直接类同.lua64反汇编即可。

 回到Lua文件加载流程分析时提到的LuaStack::init()方法中,调用了:register_all_cocos2dx

TOLUA_API int register_all_cocos2dx(lua_State* tolua_S)
{tolua_open(tolua_S);tolua_module(tolua_S,"cc",0);tolua_beginmodule(tolua_S,"cc");lua_register_cocos2dx_Ref(tolua_S);lua_register_cocos2dx_Material(tolua_S);lua_register_cocos2dx_Console(tolua_S);lua_register_cocos2dx_Node(tolua_S);lua_register_cocos2dx_Scene(tolua_S);lua_register_cocos2dx_TransitionScene(tolua_S);lua_register_cocos2dx_TransitionEaseScene(tolua_S);lua_register_cocos2dx_TransitionMoveInL(tolua_S);lua_register_cocos2dx_TransitionMoveInB(tolua_S);lua_register_cocos2dx_AtlasNode(tolua_S);lua_register_cocos2dx_TileMapAtlas(tolua_S);lua_register_cocos2dx_TransitionMoveInT(tolua_S);lua_register_cocos2dx_TMXTilesetInfo(tolua_S);lua_register_cocos2dx_TransitionMoveInR(tolua_S);lua_register_cocos2dx_Action(tolua_S);lua_register_cocos2dx_FiniteTimeAction(tolua_S);lua_register_cocos2dx_ActionInstant(tolua_S);lua_register_cocos2dx_Hide(tolua_S);lua_register_cocos2dx_ParticleSystem(tolua_S);lua_register_cocos2dx_ParticleSystemQuad(tolua_S);lua_register_cocos2dx_ParticleSpiral(tolua_S);lua_register_cocos2dx_GridBase(tolua_S);lua_register_cocos2dx_AnimationCache(tolua_S);lua_register_cocos2dx_ActionInterval(tolua_S);lua_register_cocos2dx_ActionCamera(tolua_S);lua_register_cocos2dx_ProgressFromTo(tolua_S);lua_register_cocos2dx_MoveBy(tolua_S);lua_register_cocos2dx_MoveTo(tolua_S);lua_register_cocos2dx_JumpBy(tolua_S);lua_register_cocos2dx_EventListener(tolua_S);lua_register_cocos2dx_EventListenerKeyboard(tolua_S);lua_register_cocos2dx_EventListenerMouse(tolua_S);lua_register_cocos2dx_TransitionRotoZoom(tolua_S);lua_register_cocos2dx_Event(tolua_S);lua_register_cocos2dx_EventController(tolua_S);lua_register_cocos2dx_Director(tolua_S);lua_register_cocos2dx_Scheduler(tolua_S);lua_register_cocos2dx_ActionEase(tolua_S);lua_register_cocos2dx_EaseElastic(tolua_S);lua_register_cocos2dx_EaseElasticOut(tolua_S);lua_register_cocos2dx_EaseQuadraticActionInOut(tolua_S);lua_register_cocos2dx_EaseBackOut(tolua_S);lua_register_cocos2dx_Texture2D(tolua_S);lua_register_cocos2dx_TransitionSceneOriented(tolua_S);lua_register_cocos2dx_TransitionFlipX(tolua_S);lua_register_cocos2dx_CameraBackgroundBrush(tolua_S);lua_register_cocos2dx_CameraBackgroundDepthBrush(tolua_S);lua_register_cocos2dx_CameraBackgroundColorBrush(tolua_S);lua_register_cocos2dx_GridAction(tolua_S);lua_register_cocos2dx_TiledGrid3DAction(tolua_S);lua_register_cocos2dx_FadeOutTRTiles(tolua_S);lua_register_cocos2dx_FadeOutUpTiles(tolua_S);lua_register_cocos2dx_FadeOutDownTiles(tolua_S);lua_register_cocos2dx_StopGrid(tolua_S);lua_register_cocos2dx_Technique(tolua_S);lua_register_cocos2dx_SkewTo(tolua_S);lua_register_cocos2dx_SkewBy(tolua_S);lua_register_cocos2dx_EaseQuadraticActionOut(tolua_S);lua_register_cocos2dx_TransitionProgress(tolua_S);lua_register_cocos2dx_TransitionProgressVertical(tolua_S);lua_register_cocos2dx_Layer(tolua_S);lua_register_cocos2dx_TMXTiledMap(tolua_S);lua_register_cocos2dx_Grid3DAction(tolua_S);lua_register_cocos2dx_BaseLight(tolua_S);lua_register_cocos2dx_SpotLight(tolua_S);lua_register_cocos2dx_FadeTo(tolua_S);lua_register_cocos2dx_FadeIn(tolua_S);lua_register_cocos2dx_DirectionLight(tolua_S);lua_register_cocos2dx_EventListenerCustom(tolua_S);lua_register_cocos2dx_FlipX3D(tolua_S);lua_register_cocos2dx_FlipY3D(tolua_S);lua_register_cocos2dx_EaseSineInOut(tolua_S);lua_register_cocos2dx_TransitionFlipAngular(tolua_S);lua_register_cocos2dx_EaseElasticInOut(tolua_S);lua_register_cocos2dx_EaseBounce(tolua_S);lua_register_cocos2dx_Show(tolua_S);lua_register_cocos2dx_FadeOut(tolua_S);lua_register_cocos2dx_CallFunc(tolua_S);lua_register_cocos2dx_EventMouse(tolua_S);lua_register_cocos2dx_GLView(tolua_S);lua_register_cocos2dx_EaseBezierAction(tolua_S);lua_register_cocos2dx_ParticleFireworks(tolua_S);lua_register_cocos2dx_MenuItem(tolua_S);lua_register_cocos2dx_MenuItemSprite(tolua_S);lua_register_cocos2dx_MenuItemImage(tolua_S);lua_register_cocos2dx_AutoPolygon(tolua_S);lua_register_cocos2dx_ParticleSmoke(tolua_S);lua_register_cocos2dx_TransitionZoomFlipAngular(tolua_S);lua_register_cocos2dx_EaseRateAction(tolua_S);lua_register_cocos2dx_EaseIn(tolua_S);lua_register_cocos2dx_EaseExponentialInOut(tolua_S);lua_register_cocos2dx_CardinalSplineTo(tolua_S);lua_register_cocos2dx_CatmullRomTo(tolua_S);lua_register_cocos2dx_Waves3D(tolua_S);lua_register_cocos2dx_EaseExponentialOut(tolua_S);lua_register_cocos2dx_Label(tolua_S);lua_register_cocos2dx_Application(tolua_S);lua_register_cocos2dx_DelayTime(tolua_S);lua_register_cocos2dx_LabelAtlas(tolua_S);lua_register_cocos2dx_EaseCircleActionOut(tolua_S);lua_register_cocos2dx_SpriteBatchNode(tolua_S);lua_register_cocos2dx_TMXLayer(tolua_S);lua_register_cocos2dx_AsyncTaskPool(tolua_S);lua_register_cocos2dx_ParticleSnow(tolua_S);lua_register_cocos2dx_EaseElasticIn(tolua_S);lua_register_cocos2dx_EaseCircleActionInOut(tolua_S);lua_register_cocos2dx_TransitionFadeTR(tolua_S);lua_register_cocos2dx_EaseQuarticActionOut(tolua_S);lua_register_cocos2dx_EventAcceleration(tolua_S);lua_register_cocos2dx_EaseCubicActionIn(tolua_S);lua_register_cocos2dx_TextureCache(tolua_S);lua_register_cocos2dx_ActionTween(tolua_S);lua_register_cocos2dx_TransitionFadeDown(tolua_S);lua_register_cocos2dx_ParticleSun(tolua_S);lua_register_cocos2dx_TransitionProgressHorizontal(tolua_S);lua_register_cocos2dx_ParticleFire(tolua_S);lua_register_cocos2dx_FlipX(tolua_S);lua_register_cocos2dx_FlipY(tolua_S);lua_register_cocos2dx_EventKeyboard(tolua_S);lua_register_cocos2dx_TransitionSplitCols(tolua_S);lua_register_cocos2dx_Timer(tolua_S);lua_register_cocos2dx_RepeatForever(tolua_S);lua_register_cocos2dx_Place(tolua_S);lua_register_cocos2dx_EventListenerAcceleration(tolua_S);lua_register_cocos2dx_TiledGrid3D(tolua_S);lua_register_cocos2dx_EaseBounceOut(tolua_S);lua_register_cocos2dx_RenderTexture(tolua_S);lua_register_cocos2dx_TintBy(tolua_S);lua_register_cocos2dx_TransitionShrinkGrow(tolua_S);lua_register_cocos2dx_ClippingNode(tolua_S);lua_register_cocos2dx_ActionFloat(tolua_S);lua_register_cocos2dx_ParticleFlower(tolua_S);lua_register_cocos2dx_EaseCircleActionIn(tolua_S);lua_register_cocos2dx_Image(tolua_S); // 加载图片资源
    lua_register_cocos2dx_LayerMultiplex(tolua_S);lua_register_cocos2dx_Blink(tolua_S);lua_register_cocos2dx_ShaderCache(tolua_S);lua_register_cocos2dx_JumpTo(tolua_S);lua_register_cocos2dx_ParticleExplosion(tolua_S);lua_register_cocos2dx_TransitionJumpZoom(tolua_S);lua_register_cocos2dx_Pass(tolua_S);lua_register_cocos2dx_Touch(tolua_S);lua_register_cocos2dx_CardinalSplineBy(tolua_S);lua_register_cocos2dx_CatmullRomBy(tolua_S);lua_register_cocos2dx_NodeGrid(tolua_S);lua_register_cocos2dx_TMXLayerInfo(tolua_S);lua_register_cocos2dx_EaseSineIn(tolua_S);lua_register_cocos2dx_EaseBounceIn(tolua_S);lua_register_cocos2dx_Camera(tolua_S);lua_register_cocos2dx_TMXObjectGroup(tolua_S);lua_register_cocos2dx_FastTMXTiledMap(tolua_S);lua_register_cocos2dx_ParticleGalaxy(tolua_S);lua_register_cocos2dx_Twirl(tolua_S);lua_register_cocos2dx_MenuItemLabel(tolua_S);lua_register_cocos2dx_EaseQuinticActionIn(tolua_S);lua_register_cocos2dx_LayerColor(tolua_S);lua_register_cocos2dx_FadeOutBLTiles(tolua_S);lua_register_cocos2dx_LayerGradient(tolua_S);lua_register_cocos2dx_EventListenerTouchAllAtOnce(tolua_S);lua_register_cocos2dx_GLViewImpl(tolua_S);lua_register_cocos2dx_ToggleVisibility(tolua_S);lua_register_cocos2dx_Repeat(tolua_S);lua_register_cocos2dx_TransitionFlipY(tolua_S);lua_register_cocos2dx_TurnOffTiles(tolua_S);lua_register_cocos2dx_TintTo(tolua_S);lua_register_cocos2dx_EaseBackInOut(tolua_S);lua_register_cocos2dx_TransitionFadeBL(tolua_S);lua_register_cocos2dx_TargetedAction(tolua_S);lua_register_cocos2dx_DrawNode(tolua_S);lua_register_cocos2dx_TransitionTurnOffTiles(tolua_S);lua_register_cocos2dx_RotateTo(tolua_S);lua_register_cocos2dx_TransitionSplitRows(tolua_S);lua_register_cocos2dx_Device(tolua_S);lua_register_cocos2dx_TransitionProgressRadialCCW(tolua_S);lua_register_cocos2dx_ScaleTo(tolua_S);lua_register_cocos2dx_TransitionPageTurn(tolua_S);lua_register_cocos2dx_RenderState(tolua_S);lua_register_cocos2dx_Properties(tolua_S);lua_register_cocos2dx_BezierBy(tolua_S);lua_register_cocos2dx_BezierTo(tolua_S);lua_register_cocos2dx_ParticleMeteor(tolua_S);lua_register_cocos2dx_SpriteFrame(tolua_S);lua_register_cocos2dx_Liquid(tolua_S);lua_register_cocos2dx_UserDefault(tolua_S);lua_register_cocos2dx_FastTMXLayer(tolua_S);lua_register_cocos2dx_TransitionZoomFlipX(tolua_S);lua_register_cocos2dx_EventFocus(tolua_S);lua_register_cocos2dx_TransitionFade(tolua_S);lua_register_cocos2dx_EaseQuinticActionInOut(tolua_S);lua_register_cocos2dx_SpriteFrameCache(tolua_S);lua_register_cocos2dx_PointLight(tolua_S);lua_register_cocos2dx_TransitionCrossFade(tolua_S);lua_register_cocos2dx_Ripple3D(tolua_S);lua_register_cocos2dx_Lens3D(tolua_S);lua_register_cocos2dx_EventListenerFocus(tolua_S);lua_register_cocos2dx_Spawn(tolua_S);lua_register_cocos2dx_EaseQuarticActionInOut(tolua_S);lua_register_cocos2dx_ShakyTiles3D(tolua_S);lua_register_cocos2dx_PageTurn3D(tolua_S);lua_register_cocos2dx_PolygonInfo(tolua_S);lua_register_cocos2dx_TransitionSlideInL(tolua_S);lua_register_cocos2dx_TransitionSlideInT(tolua_S);lua_register_cocos2dx_Grid3D(tolua_S);lua_register_cocos2dx_EventListenerController(tolua_S);lua_register_cocos2dx_TransitionProgressInOut(tolua_S);lua_register_cocos2dx_EaseCubicActionInOut(tolua_S);lua_register_cocos2dx_ParticleData(tolua_S);lua_register_cocos2dx_EaseBackIn(tolua_S);lua_register_cocos2dx_SplitRows(tolua_S);lua_register_cocos2dx_Follow(tolua_S);lua_register_cocos2dx_Animate(tolua_S);lua_register_cocos2dx_ShuffleTiles(tolua_S);lua_register_cocos2dx_CameraBackgroundSkyBoxBrush(tolua_S);lua_register_cocos2dx_ProgressTimer(tolua_S);lua_register_cocos2dx_EaseQuarticActionIn(tolua_S);lua_register_cocos2dx_Menu(tolua_S);lua_register_cocos2dx_EaseInOut(tolua_S);lua_register_cocos2dx_TransitionZoomFlipY(tolua_S);lua_register_cocos2dx_ScaleBy(tolua_S);lua_register_cocos2dx_EventTouch(tolua_S);lua_register_cocos2dx_Animation(tolua_S);lua_register_cocos2dx_TMXMapInfo(tolua_S);lua_register_cocos2dx_EaseExponentialIn(tolua_S);lua_register_cocos2dx_ReuseGrid(tolua_S);lua_register_cocos2dx_EaseQuinticActionOut(tolua_S);lua_register_cocos2dx_EventDispatcher(tolua_S);lua_register_cocos2dx_MenuItemAtlasFont(tolua_S);lua_register_cocos2dx_ActionManager(tolua_S);lua_register_cocos2dx_OrbitCamera(tolua_S);lua_register_cocos2dx_ClippingRectangleNode(tolua_S);lua_register_cocos2dx_EventCustom(tolua_S);lua_register_cocos2dx_ParticleBatchNode(tolua_S);lua_register_cocos2dx_Component(tolua_S);lua_register_cocos2dx_EaseCubicActionOut(tolua_S);lua_register_cocos2dx_EventListenerTouchOneByOne(tolua_S);lua_register_cocos2dx_Renderer(tolua_S);lua_register_cocos2dx_ParticleRain(tolua_S);lua_register_cocos2dx_Waves(tolua_S);lua_register_cocos2dx_ComponentLua(tolua_S);lua_register_cocos2dx_MotionStreak3D(tolua_S);lua_register_cocos2dx_EaseOut(tolua_S);lua_register_cocos2dx_MenuItemFont(tolua_S);lua_register_cocos2dx_TransitionFadeUp(tolua_S);lua_register_cocos2dx_LayerRadialGradient(tolua_S);lua_register_cocos2dx_EaseSineOut(tolua_S);lua_register_cocos2dx_JumpTiles3D(tolua_S);lua_register_cocos2dx_MenuItemToggle(tolua_S);lua_register_cocos2dx_RemoveSelf(tolua_S);lua_register_cocos2dx_SplitCols(tolua_S);lua_register_cocos2dx_ProtectedNode(tolua_S);lua_register_cocos2dx_MotionStreak(tolua_S);lua_register_cocos2dx_RotateBy(tolua_S);lua_register_cocos2dx_FileUtils(tolua_S);lua_register_cocos2dx_Sprite(tolua_S);lua_register_cocos2dx_ProgressTo(tolua_S);lua_register_cocos2dx_TransitionProgressOutIn(tolua_S);lua_register_cocos2dx_AnimationFrame(tolua_S);lua_register_cocos2dx_Sequence(tolua_S);lua_register_cocos2dx_Shaky3D(tolua_S);lua_register_cocos2dx_TransitionProgressRadialCW(tolua_S);lua_register_cocos2dx_EaseBounceInOut(tolua_S);lua_register_cocos2dx_TransitionSlideInR(tolua_S);lua_register_cocos2dx_AmbientLight(tolua_S);lua_register_cocos2dx_ParallaxNode(tolua_S);lua_register_cocos2dx_EaseQuadraticActionIn(tolua_S);lua_register_cocos2dx_WavesTiles3D(tolua_S);lua_register_cocos2dx_TransitionSlideInB(tolua_S);lua_register_cocos2dx_Speed(tolua_S);lua_register_cocos2dx_ShatteredTiles3D(tolua_S);tolua_endmodule(tolua_S);return 1;
}

在大量注册函数中寻找到关键方法lua_register_cocos2dx_Image:

int lua_register_cocos2dx_Image(lua_State* tolua_S)
{tolua_usertype(tolua_S,"cc.Image");tolua_cclass(tolua_S,"Image","cc.Image","cc.Ref",nullptr);tolua_beginmodule(tolua_S,"Image");tolua_function(tolua_S,"new",lua_cocos2dx_Image_constructor);tolua_function(tolua_S,"hasPremultipliedAlpha",lua_cocos2dx_Image_hasPremultipliedAlpha);tolua_function(tolua_S,"reversePremultipliedAlpha",lua_cocos2dx_Image_reversePremultipliedAlpha);tolua_function(tolua_S,"isCompressed",lua_cocos2dx_Image_isCompressed);tolua_function(tolua_S,"hasAlpha",lua_cocos2dx_Image_hasAlpha);tolua_function(tolua_S,"getPixelFormat",lua_cocos2dx_Image_getPixelFormat);tolua_function(tolua_S,"getHeight",lua_cocos2dx_Image_getHeight);tolua_function(tolua_S,"premultiplyAlpha",lua_cocos2dx_Image_premultiplyAlpha);tolua_function(tolua_S,"initWithImageFile",lua_cocos2dx_Image_initWithImageFile); // 初始化图片文件tolua_function(tolua_S,"getWidth",lua_cocos2dx_Image_getWidth);tolua_function(tolua_S,"getBitPerPixel",lua_cocos2dx_Image_getBitPerPixel);tolua_function(tolua_S,"getFileType",lua_cocos2dx_Image_getFileType);tolua_function(tolua_S,"getFilePath",lua_cocos2dx_Image_getFilePath);tolua_function(tolua_S,"getNumberOfMipmaps",lua_cocos2dx_Image_getNumberOfMipmaps);tolua_function(tolua_S,"saveToFile",lua_cocos2dx_Image_saveToFile);tolua_function(tolua_S,"setPVRImagesHavePremultipliedAlpha", lua_cocos2dx_Image_setPVRImagesHavePremultipliedAlpha);tolua_function(tolua_S,"setPNGPremultipliedAlphaEnabled", lua_cocos2dx_Image_setPNGPremultipliedAlphaEnabled);tolua_endmodule(tolua_S);std::string typeName = typeid(cocos2d::Image).name();g_luaType[typeName] = "cc.Image";g_typeCast["Image"] = "cc.Image";return 1;
}

lua_cocos2dx_Image_initWithImageFile加载了我们的图片文件。

int lua_cocos2dx_Image_initWithImageFile(lua_State* tolua_S)
{int argc = 0;cocos2d::Image* cobj = nullptr;bool ok  = true;#if COCOS2D_DEBUG >= 1tolua_Error tolua_err;
#endif#if COCOS2D_DEBUG >= 1if (!tolua_isusertype(tolua_S,1,"cc.Image",0,&tolua_err)) goto tolua_lerror;
#endifcobj = (cocos2d::Image*)tolua_tousertype(tolua_S,1,0);#if COCOS2D_DEBUG >= 1if (!cobj){tolua_error(tolua_S,"invalid 'cobj' in function 'lua_cocos2dx_Image_initWithImageFile'", nullptr);return 0;}
#endifargc = lua_gettop(tolua_S)-1;if (argc == 1){std::string arg0;ok &= luaval_to_std_string(tolua_S, 2,&arg0, "cc.Image:initWithImageFile");if(!ok){tolua_error(tolua_S,"invalid arguments in function 'lua_cocos2dx_Image_initWithImageFile'", nullptr);return 0;}bool ret = cobj->initWithImageFile(arg0);tolua_pushboolean(tolua_S,(bool)ret);return 1;}luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "cc.Image:initWithImageFile",argc, 1);return 0;#if COCOS2D_DEBUG >= 1tolua_lerror:tolua_error(tolua_S,"#ferror in function 'lua_cocos2dx_Image_initWithImageFile'.",&tolua_err);
#endifreturn 0;
}

下面看cobj->initWithImageFile:

bool Image::initWithImageFile(const std::string& path)
{bool ret = false;_filePath = FileUtils::getInstance()->fullPathForFilename(path);Data data = FileUtils::getInstance()->getDataFromFile(_filePath); // 获取文件数据if (!data.isNull()){ret = initWithImageData(data.getBytes(), data.getSize()); // 解码/解密 处理文件数据
    }return ret;
}

下面是图片数据格式解码方法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;//detect and unzip the compress fileif (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::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;default:{// load and detect image formattImageTGA* tgaData = tgaLoadBuffer(unpackedData, unpackedLen);if (tgaData != nullptr && tgaData->status == TGA_OK){ret = initWithTGAData(tgaData);}else{CCLOG("cocos2d: unsupported image format!");}free(tgaData);break;}}if(unpackedData != data){free(unpackedData);}} while (0);return ret;
}

图片资源的加载到这里就结束了。

 

快速定位:根据调用流程,我们可以直接定位方法cocos2d::Image::initWithImageData。

向上可以追溯到cocos2d::Image::initWithImageFile:

 看到了可疑函数cocos2d::Image::decodePngData

cocos2d *__fastcall cocos2d::Image::decodePngData(cocos2d::Image *this, cocos2d::Data *a2)
{_BYTE *v4; // r7int v5; // r5signed int v6; // r0bool v7; // ccint v8; // r4void *v9; // r0int v10; // r2int v12; // r6int v13; // r4int v14; // r9int v15; // r2int v16; // r0int v17; // r3_BYTE *v18; // r5unsigned int *v19; // r2int v20; // r3int v21; // [sp+8h] [bp-28h] BYREF
 v4 = (_BYTE *)cocos2d::Data::getBytes(a2);v5 = cocos2d::Data::getSize(a2);sub_FBDCA8(&v21, (int *)this + 47);v6 = sub_FBC854((int)&v21, ".png", 0, 4u);v7 = v6 <= -1;if ( v6 != -1 )v7 = v5 <= 24;v8 = !v7;v9 = (void *)(v21 - 12);if ( (int *)(v21 - 12) != &dword_12F6978 ){if ( &pthread_create ){v19 = (unsigned int *)(v21 - 4);__dmb(0xFu);dov20 = __ldrex(v19);while ( __strex(v20 - 1, v19) );__dmb(0xFu);}else{v20 = *(_DWORD *)(v21 - 4);*(_DWORD *)(v21 - 4) = v20 - 1;}if ( v20 <= 0 )operator delete(v9);}if ( v8 && memcmp(&unk_11CBE0C, v4, 8u) )     // PNG Header
  {v12 = v5 - 4;v13 = v5 - 8;v14 = cocos2d::hexToDecimal((cocos2d *)&v4[v5 - 4], (unsigned __int8 *)&byte_4, v10);v16 = cocos2d::hexToDecimal((cocos2d *)&v4[v5 - 8], (unsigned __int8 *)&byte_4, v15);if ( v5 >= v16 )cocos2d::decodePng(v4, (unsigned __int8 *)(v14 - 2048), v16, v17);v18 = &v4[v5];*(v18 - 1) = 130;*(v18 - 2) = 96;*(v18 - 3) = 66;v4[v12] = 174;*(v18 - 5) = 68;*(v18 - 6) = 78;*(v18 - 7) = 69;v4[v13] = 73;}return (cocos2d *)v4;
}

明显是一个算法函数,看方法cocos2d::decodePng:

_BYTE *__fastcall cocos2d::decodePng(_BYTE *this, unsigned __int8 *a2, int a3, int a4)
{char v4; // r12unsigned int v5; // r1int v6; // r12char v7[4]; // [sp+0h] [bp-10h]
 v7[1] = BYTE2(a2);v4 = BYTE1(a2);v7[3] = (char)a2;v5 = (unsigned int)a2 >> 24;v7[2] = v4;v6 = 0;v7[0] = v5;if ( a3 > 0 ){while ( 1 ){++v6;*this++ ^= v5;if ( v6 == a3 )break;LOBYTE(v5) = v7[v6 % 4];}}return this;
}

这里也可以尝试Frida Hook验证猜想,同样的,我就不Hook了。算法还原

def _hexToDecimal(data, offset):if offset <= 0:return 0return struct.unpack(">I", data[:offset])[0]def LOBYTE(d):return d & 0xffdef _decodePng(data, magic, rounds):dthis = bytearray(data)v7 = [0] * 4a2 = struct.pack("<i", magic)v7[1] = a2[2]v7[3] = a2[0]v7[0] = v5 = magic >> 24v7[2] = a2[1]i = 0if rounds > 0:while 1:i += 1dthis[i-1] ^= v5if i == rounds:breakv5 = LOBYTE(v7[i % 4])return dthisdef decodePng(data, size):v14 = _hexToDecimal(data[-4:], 4)v16 = _hexToDecimal(data[-8:], 4)if size >= v16:ret = _decodePng(data, v14 - 2048, v16)ret[-1] = 130ret[-2] = 96ret[-3] = 66ret[-4] = 174ret[-5] = 68ret[-6] = 78ret[-7] = 69ret[-8] = 73return ret

解密结果:

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ryyt.cn/news/29277.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

Flink架构与原理

Flink集群运行剖析 Flink 运行时由两种类型的进程组成:一个 JobManager 和一个或者多个 TaskManager。 Client:Client是提交作业的客户端,虽然不是运行时和作业执行时的一部分,但它负责准备和提交作业到JobManager,它可以运行在任何机器上,只要与JobManager环境连通即可。…

Nexpose v6.6.250 for Linux Windows - 漏洞扫描

Nexpose v6.6.250 for Linux & Windows - 漏洞扫描Nexpose v6.6.250 for Linux & Windows - 漏洞扫描 Rapid7 Vulnerability Management, Release May 02, 2024 请访问原文链接:Nexpose v6.6.250 for Linux & Windows - 漏洞扫描,查看最新版。原创作品,转载请保…

Metasploit Pro 4.22.3-2024050201 (Linux, Windows) - 专业渗透测试框架

Metasploit Pro 4.22.3-2024050201 (Linux, Windows) - 专业渗透测试框架Metasploit Pro 4.22.3-2024050201 (Linux, Windows) - 专业渗透测试框架 Rapid7 Penetration testing, Release May 03, 2024 请访问原文链接:Metasploit Pro 4.22.3-2024050201 (Linux, Windows) - 专…

[转]vscode必备插件,美化、炫酷、实用-留着防丢

vscode必备插件,美化、炫酷、实用-留着防丢 - 知乎 (zhihu.com) https://zhuanlan.zhihu.com/p/112016680本篇文章只推荐看起来不错的插件,并不详细介绍插件的使用方法,插件的具体使用方法可以单独对其进行百度搜索。当然,有啥问题也可以在下面评论,但我觉得可以百度的地方…

应急响应web1

应急响应的过程 目的:分析攻击时间、攻击操作、攻击结果、安全修复等并给出合理的解决方案。 保护阶段:直接断网,保护现场,看是否能够恢复数据; 分析阶段:对入侵过程进行分析,常见的方法为指纹库搜索、日志时间分析、后门追查分析、漏洞检查分析等; 复现阶段:还原攻击…

FTP主动模式和被动模式(3)NAT对FTP的影响 - NAT ALG

NAT对FTP的影响 NAT环境下FTP存在的问题 FTP主动模式 FTP服务器在外部网络 在FTP主动模式下,如果网络中存在NAT,且FTP客户端在NAT内部网络中,那么FTP数据连接会出现下面的问题,如图:内部网络中的FTP客户端和外部网络中的FTP服务器端通过NAT地址转换是可以正常建立控制连接…

python读写json文件

1. 新建json文件打开记事本,重命名为.json后缀 使用的样例如下,注意看json文件格式:{"server":{"host": "example.com","port": 443,"protocol": "https"},"authentication":{"username":…