1. 程式人生 > >cocos2dx-Lua引擎遊戲指令碼及圖片資源解密與DUMP

cocos2dx-Lua引擎遊戲指令碼及圖片資源解密與DUMP

分析目標

下面分析的主要是少年三國志。

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-05 19:29:27.674 13191-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 = debug.traceback("", 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-05 19:29:27.679 13191-13215/? D/SUBSTRATEHOOK: [dumplua] luaL_loadbuffer name: upgrade.AntiAddictionLayer lua: LJ-
01-05 19:29:27.679 13191-13215/? D/SUBSTRATEHOOK: [dumplua] luaL_loadbuffer name: upgrade.ComSdkUtils lua: LJA
01-05 19:29:27.684 13191-13215/? D/SUBSTRATEHOOK: [dumplua] luaL_loadbuffer name: upgrade.config lua: LJ�    
01-05 19:29:27.684 13191-13215/? D/SUBSTRATEHOOK: [dumplua] luaL_loadbuffer name: upgrade.ConfigLayer lua: LJ]
01-05 19:29:27.684 13191-13215/? D/SUBSTRATEHOOK: [dumplua] luaL_loadbuffer name: upgrade.EffectNode_Upgrade lua: LJ�
01-05 19:29:27.684 13191-13215/? D/SUBSTRATEHOOK: [dumplua] luaL_loadbuffer name: upgrade.ErrMsgBox lua: LJP
01-05 19:29:27.684 13191-13215/? D/SUBSTRATEHOOK: [dumplua] luaL_loadbuffer name: upgrade.game lua: LJ�
01-05 19:29:27.684 13191-13215/? D/SUBSTRATEHOOK: [dumplua] luaL_loadbuffer name: upgrade.NativeCallUtils lua: LJ�
01-05 19:29:27.684 13191-13215/? D/SUBSTRATEHOOK: [dumplua] luaL_loadbuffer name: upgrade.NativeProxy lua: LJ�
01-05 19:29:27.684 13191-13215/? D/SUBSTRATEHOOK: [dumplua] luaL_loadbuffer name: upgrade.Patcher lua: LJ�
01-05 19:29:27.689 13191-13215/? D/SUBSTRATEHOOK: [dumplua] luaL_loadbuffer name: upgrade.SplashLayer lua: LJ-
01-05 19:29:27.689 13191-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::decryptXXTEAcocos2d::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 & 0xFu) <= 9 )
      *(_DWORD *)(v8 + 36) = v25 & 0xF;
    switch ( v15 )
    {
      case 1:
        v16 = 1067030938;
        break;
      case 2:
        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 )
    {
      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;
  }
  return 0;
}

其實看到這裡應該也是比較容易逆向分析出解密的演算法的,應該說比較簡單,可以直接寫一個指令碼來解密assets裡的資源。但是為了保證通用性,還是寫HOOK程式碼比較好。

本來分析以為最終都會呼叫_initWithWebpData、_initWithJpgData、_initWithBpgData、_initWithPngData、_initWithTiffData、_initWithRawData這些函式的,但是實際上分別HOOK後並沒有被攔截,所以最後還是HOOK了下cocos2d::extra::CCCrypto::decryptUF

static string g_strDataPath;
static int g_nCount = 1;

string getNextFilePath(const char *fileExt) {
    char buff[100] = {0};
    ++g_nCount;
    sprintf(buff, "%s/cache/%d%s", g_strDataPath.c_str(), g_nCount, fileExt);
    return buff;
}

bool saveFile(const void* addr, int len, const char *outFileName)
{
    bool bSuccess = false;
    FILE* file = fopen(outFileName, "wb+");
    if (file != NULL) {
        fwrite(addr, len, 1, file);
        fflush(file);
        fclose(file);
        bSuccess = true;
        chmod(outFileName, S_IRWXU | S_IRWXG | S_IRWXO);
    }else{
        LOGE("[%s] fopen failed, error: %s", __FUNCTION__, dlerror());
    }

    return bSuccess;
}

//hook decryptUF
int (*decryptUF_orig)(void *pInBuff, int len, int *n, int *poutlen, char *name) = NULL;
int decryptUF_mod(void *pInBuff, int len, int *n, int *poutlen, char *name) {
    int ret = decryptUF_orig(pInBuff, len, n, poutlen, name);
    saveFile(pInBuff, *poutlen, getNextFilePath(".png").c_str());
    return ret;
}

void hook() {
    //hook decryptUF
    void *decryptUF = dlsym(handle, "_ZN7cocos2d5extra8CCCrypto9decryptUFEPhiPiS3_");
    if ( decryptUF==NULL ) {
        LOGE("[dumplua] _ZN7cocos2d5extra8CCCrypto9decryptUFEPhiPiS3_ (decryptUF) not found!");
        LOGE("[dumplua] dlsym err: %s.", dlerror());
    }else{
        LOGD("[dumplua] _ZN7cocos2d5extra8CCCrypto9decryptUFEPhiPiS3_ (decryptUF) found!");
        MSHookFunction(decryptUF, (void *)&decryptUF_mod, (void **)&decryptUF_orig);
    }
}

我這裡圖方便把所有解密的資料都DUMP為/data/data/packagename/cache目錄下副檔名為PNG的檔案了,最後通過指令碼從手機中批量提取出解密後的檔案:

#coding:utf-8
import os

for i in range(1, 10000):
    cmd = 'adb pull /data/data/com.youzu.android.snsgz/cache/' + str(i) +'.png' + ' e:\\test'
    os.system(cmd)

image
其實通過上面的分析就可以知道,圖片資源的解密和Lua的解密都是呼叫了相同的函式,因此解密出的檔案不全是圖片,還有寫LuaJit的指令碼檔案,用十六進位制編輯器開啟就可以看到LJ開頭的魔法數字。

全民水滸

全民水滸這個比較簡單,資源直接沒加密處理,解壓縮APK檔案就可以在assets目錄下查看了。Lua指令碼可以通過HOOK函式luaL_loadbuffer獲得,而且可以看出只是對編譯的Lua指令碼做了簡單的加密,可以直接DUMP出來,相對少年三國志稍微弱了一些。

01-05 20:04:16.569 17729-17886/? D/SUBSTRATEHOOK: [dumplua] luaL_loadbuffer name: require "UpdateScene.lua" lua: require "UpdateScene.lua"
01-05 20:04:16.574 17729-17886/? D/SUBSTRATEHOOK: [dumplua] luaL_loadbuffer name: UpdateScene.lua lua: LuaQ
01-05 20:04:16.574 17729-17886/? D/SUBSTRATEHOOK: [dumplua] luaL_loadbuffer name: Modal.lua lua: LuaQ
01-05 20:04:16.574 17729-17886/? D/SUBSTRATEHOOK: [dumplua] luaL_loadbuffer name: UIDefine.lua lua: LuaQ
01-05 20:04:16.574 17729-17886/? D/SUBSTRATEHOOK: [dumplua] luaL_loadbuffer name: MessageBox.lua lua: LuaQ
01-05 20:04:16.579 17729-17886/? D/SUBSTRATEHOOK: [dumplua] luaL_loadbuffer name: SoundManager.lua lua: LuaQ
01-05 20:04:16.579 17729-17886/? D/SUBSTRATEHOOK: [dumplua] luaL_loadbuffer name: SoundConfig.lua lua: LuaQ

相關推薦

cocos2dx-Lua引擎遊戲指令碼圖片資源解密DUMP

分析目標 少年三國志,包名:com.youzu.android.snsgz 全民水滸,包名:com.tencent.Q108 下面分析的主要是少年三國志。 Lua指令碼解密與DUMP LuaJit IDA分析呼叫樹: AppDelegate:

cocos2dx 3.0 使用TexturePacker對圖片資源加密

       遊戲開發過程中要涉及到大量的圖片,使用TexturePacker可以把小圖合成大圖。這是我們使用最多的功能,但是TexturePacker還帶有對圖片加密的功能。之前還是對加密不慎瞭解,今天在網上搜了下然後自己親手寫了,所以寫下來分享下。 把圖片匯入到Text

cocos2dx lua轉luac的問題(32位64位不相容問題)

兩種思路: 1、LuaJit 轉luac,生成的bytecode,是按照32位生成的。在64位的機器上是不支援的。(新版本的LuaJit 已經支援了64位。可更新新版本的LuaJit, 用新版本LuaJit生成對應的luajit-mac) 2、原生的Lua5.1.4 生成

Java併發包原始碼學習系列:CLH同步佇列同步資源獲取釋放

[toc] ## 本篇學習目標 - 回顧CLH同步佇列的結構。 - 學習獨佔式資源獲取和釋放的流程。 ## CLH佇列的結構 我在[Java併發包原始碼學習系列:AbstractQueuedSynchronizer#同步佇列與Node節點](https://www.cnblogs.com/summer

Lua筆記:SpriteImageView型別替換新的圖片資源

------------------- Sprite及ImageView型別替換新的圖片資源 ---------------------- --Sprite型別 local img = cc

Java版AVG遊戲開發入門示例[3]——指令碼引擎的製作應用

 根據wikipedia的解釋:指令碼語言(Script language,scripting language,scripting programming language),是為了縮短傳統的編寫-編譯-連結-執行(edit-compile-link-run)過程而建立

Lua遊戲逆向破解方法介紹

初始 成對 參數 很多 技術 邏輯 重寫 源碼 sem Lua遊戲逆向及破解方法介紹 背景介紹 隨著手遊的發展,越來越多的Cocos-lua端遊開發者轉移到手遊平臺。Lua腳本編寫邏輯的手遊也是越來越多,如夢幻西遊、刀塔傳奇、開心消消樂、遊龍英雄、奇跡暖暖、疾風獵

Mac OSX 10.13 手動編譯Quick-cocos2dx Lua遊戲APK

bubuko 手動編譯 class mac osx post img mac os cos http Mac OSX 10.13 手動編譯Quick-cocos2dx Lua遊戲APK

使用python指令碼實現iOS圖片資源壓縮

最近公司有一個新的需求,要把程式碼進行瘦身,這篇部落格記錄下如何對圖片進行壓縮的。 原理: 寫一個指令碼,把圖片資料夾'.xcassets'的所有檔案遍歷出來,然後使用一個第三方的演算法把圖片壓縮後再替換回去 成果: 由於在該工程中的png圖片已經壓縮過了,這次只壓縮了jgp為字尾的圖片,可以看出,還

Cocos2d-x Lua引擎製作的遊戲程式碼加密

在Mac OS中的終端鍵入:cocos luacompile -h,可以獲得這個命令的幫助: usage: cocos luacompile [-h] [-v] [-s SRC_DIR_ARR] [-d DST_DIR] [-e]         &nbs

Android studio2.0在app中設定背景圖片新增圖片資源

我還處於摸索階段,也是在慢慢倒騰,持續更新,希望能幫助到有需要的人 首先將需要的圖片轉成png格式(png格式的圖片顏色過渡平滑且支援透明度),牆紙或啟動畫面的圖片資源儲存為jpg格式。 將圖片儲存到相應的工程之下,不要放錯了。 某工程/app/src/m

cocos2dx 圖片資源加密

圖片加密使用xxtea來加密,加密祕鑰自己定,思路就是自己使用程式碼首先將圖片加密,在程式中使用的時候,在載入圖片資源處再將資源解密 加密程式碼如下: 首先要載入標頭檔案 2、將圖片加密 bool jiamiImg(string inputFileName,string

圖片資源加密,Lua檔案加密

轉自:http://www.cnblogs.com/zisou/p/cocos2dxJQ-67.html 遊戲開發中常遇到資源保護的問題。 目前遊戲開發中常加密的檔案型別有:圖片,Lua檔案,音訊等檔案,而其實加密也是一把雙刃劍。 需要安全那就得耗費一定的資源去實

cocos2dx-lua在android上實現生成掃描二維碼

首先說明下,生成二維碼是用android原生的BitMatrix和Bitmap類來生成的,而掃描二維碼用到了google官方的zxing包(core.jar)。 這裡我把所有生成二維碼的程式碼和lua呼叫的掃描二維碼方法都放在了專案->frameworks->

cocos2dx引擎版本問題,由圖片導致android系統出現Unable to access asset data: -1 從而導致崩潰

這個問題是因為pkm圖片壓縮造成的,需要在生成pkm的時候新增引數 -slow 採用慢速生成,首先確定是哪張圖造成的,然後把這張圖採用慢速壓縮就ok了 其實修改一下texturepacker打包時候的引數有時候也能解決,但具有不確定性,而在壓縮pkm的時候採用慢速方式能夠完

學著做的第一個小遊戲 flappyBrid 飛翔的小鳥 有圖片資源

<!DOCTYPE html> <html> <head> <title>飛鳥</title> <style type="text/css"> /* 不同核心*/ @-webkit-keyfra

COCOS2DX-LUA 指令碼開發之六】利用Lua強轉函式解決使用CCNode報錯或無法正常使用以及簡單介紹 quick-cocos2d-x OpenQuick 兩款Lua免費開源框架

Him 的Cocos2dx-Lua群中有童鞋出現一個問題,問題是當他在Lua專案中利用Lua建立一個區域性變數CCSprite或者CCLayer等CCNode,然後在其他的函式中通過其索引取出之前建立過的CCSprite或CCLayer等,取出後進行設定設定透明、座標、縮放、

一個簡單的基於OpenGL的Lua遊戲引擎的例項

學習了幾天Lua,今天突然想為Lua寫一個簡單的遊戲引擎方便使用Lua單獨的開發遊戲,下面是一個基本的程式碼(很簡單), Lua的程式碼也測試通過了但還不完整,稍候再發上來程式碼如下:#define PENQ_LUAGAME #include <string.h>

kbengine服務端引擎+cocos2dx搭建網路遊戲

原始碼:https://github.com/cnsoft/kbengine_cocos2dx_demo 什麼是KBEngine? 一款開源的遊戲服務端引擎,使用簡單的約定協議就能夠使客戶端與服務端進行互動,使用KBEngine外掛能夠快速與(Unity3D, OGR

利用觀察者模式實現Cocos2DX-lua遊戲中的訊息管理系統

http://blog.csdn.net/operhero1990/article/details/48575487                遊戲中某些物件往往需要及時獲知其他特定物件狀體的改變。為降低類之間的耦合度,可以建立訊息管理系統,實現訊息的集中與分發。觀察者