How to extract file .luac extention [Rich and Famous]

Extraction and unpacking of game archives and compression, encryption, obfuscation, decoding of unknown files
Maboyz59
Posts: 13
Joined: Sat Jul 07, 2018 8:12 am

How to extract file .luac extention [Rich and Famous]

Post by Maboyz59 »

Hi Aluigi,

Please help me, How do I extract the luac file? I have tried using the zt_qq_fo.bms script but failed. Files and screenshots attached
https://drive.google.com/file/d/12cw6JBPMn1BvHetgAtYKLl28ZixdFQAV/view?usp=drivesdk
aluigi
Site Admin
Posts: 12984
Joined: Wed Jul 30, 2014 9:32 pm

Re: How to extract file .luac extention

Post by aluigi »

What's the name of the game?
That file is obfuscated, no idea how.
Maboyz59
Posts: 13
Joined: Sat Jul 07, 2018 8:12 am

Re: How to extract file .luac extention

Post by Maboyz59 »

aluigi wrote:What's the name of the game?
That file is obfuscated, no idea how.


name the game is "rich and famous"
atom0s
Posts: 250
Joined: Sat Dec 27, 2014 8:49 pm

Re: How to extract file .luac extention [Rich and Famous]

Post by atom0s »

Here's a small rundown of how to decrypt the files of this game, as well as a "guide" of how to deal with Cocos2 games. (This game uses the Cocos2 engine.)

The encryption information is located inside of the libcocos2dlua.so in this case. Other games are in the same/similar named module.
First, you want to locate where the Lua engine is being created and initialized. The easiest way to find this is in a tool like IDA or Ghidra, look for the following kinds of functions:
- AppDelegate::applicationDidFinishLaunching
- cocos2d::ScriptEngineManager::setScriptEngine
- register_<misc_name_here>_module

Follow references to those and you should eventually find a function that looks something like this:

Code: Select all

__int64 sub_5A1498()
{
  cocos2d::ScriptEngineManager *v0; // x0
  cocos2d::ScriptEngineProtocol *v1; // x20
  cocos2d::ScriptEngineManager *v2; // x0
  __int64 v3; // x19
  __int64 v4; // x21
  unsigned int v5; // w24
  __int64 v6; // x0
  int v8; // [xsp+40h] [xbp-50h]
  int v9; // [xsp+44h] [xbp-4Ch]
  int v10; // [xsp+48h] [xbp-48h]
  int v11; // [xsp+4Ch] [xbp-44h]
  int v12; // [xsp+50h] [xbp-40h]
  int v13; // [xsp+54h] [xbp-3Ch]
  int v14; // [xsp+58h] [xbp-38h]
  int v15; // [xsp+5Ch] [xbp-34h]
  int v16; // [xsp+60h] [xbp-30h]
  int v17; // [xsp+68h] [xbp-28h]
  __int16 v18; // [xsp+6Ch] [xbp-24h]
  char v19; // [xsp+6Eh] [xbp-22h]
  __int64 v20; // [xsp+70h] [xbp-20h]
  __int64 v21; // [xsp+78h] [xbp-18h]
  char v22; // [xsp+80h] [xbp-10h]

  v0 = (cocos2d::ScriptEngineManager *)cocos2d::LuaEngine::getInstance(_stack_chk_guard);
  v1 = v0;
  v2 = (cocos2d::ScriptEngineManager *)cocos2d::ScriptEngineManager::getInstance(v0);
  cocos2d::ScriptEngineManager::setScriptEngine(v2, v1);
  v3 = *(_QWORD *)(*((_QWORD *)v1 + 1) + 40LL);
  register_cocosdenshion_module(*(_QWORD *)(*((_QWORD *)v1 + 1) + 40LL));
  register_network_module(v3);
  register_cocosbuilder_module(v3);
  register_cocostudio_module(v3);
  register_ui_moudle(v3);
  register_extension_module(v3);
  register_spine_module(v3);
  register_cocos3d_module(v3);
  register_audioengine_module(v3);
  register_all_laky_actUpdate(v3);
  register_physics3d_module(v3);
  register_navmesh_module(v3);
  luaopen_pb(v3);
  v8 = 0;
  v11 = 8;
  v13 = 1;
  v14 = 2;
  v9 = 6;
  v15 = 3;
  v20 = 0xA9D3B6D3988A8BD1LL;
  v21 = 0xDECEDC95A2DBD9CDLL;
  v16 = 6;
  v22 = 0;
  v10 = 0;
  v12 = 0;
  Cutecode((char *)&v20, &v8);
  v17 = 0xA5B0ACAD;
  v18 = 0xB0AAu;
  v19 = 0;
  Cutecode((char *)&v17, &v8);
  v4 = *((_QWORD *)v1 + 1);
  v5 = strlen(&v20);
  v6 = strlen(&v17);
  (*(void (__fastcall **)(__int64, __int64 *, _QWORD, int *, __int64))(*(_QWORD *)v4 + 232LL))(v4, &v20, v5, &v17, v6);
  (*(void (__fastcall **)(cocos2d::ScriptEngineProtocol *, const char *))(*(_QWORD *)v1 + 104LL))(v1, "main.lua");
  return _stack_chk_guard;
}


This is where the Lua engine is being initialized and prepared. This is also were the Lua file encryption is setup. Not all games use the same setup in this function so it'll be up to you to determine the layout of the function for your game.

In this case, the game uses the default XXTEA encryption setup built into Cocos2 which is setup using:

Code: Select all

virtual void    setXXTEAKeyAndSign (const char *key, int keyLen, const char *sign, int signLen)


The key is the actual encryption/decryption key used for the file data, the sign is just a pattern added to the start of the file to let the engine know if that file should be decrypted using the set key/sign information.

For this game, that data is encrypted itself as well to prevent reading that data easily using those 'cuteCode' calls. Those are set up like this:

Code: Select all

char CutecodeChar(char c, int32_t key)
{
    return ~(c ^ key);
}

void Cutecode(char* str, int32_t* key)
{
    const auto len = strlen(str);
    for (auto x = 0; x < len; x++)
        *(str + x) = CutecodeChar(*(str + x), key[x % 5]);
}


The first block before the first Cutecode call is the 'Cutecode' key and the data to decrypt:

Code: Select all

  v8 = 0;
  v11 = 8;
  v13 = 1;
  v14 = 2;
  v9 = 6;
  v15 = 3;
  v20 = 0xA9D3B6D3988A8BD1LL;
  v21 = 0xDECEDC95A2DBD9CDLL;
  v16 = 6;
  v22 = 0;
  v10 = 0;
  v12 = 0;
  Cutecode((char *)&v20, &v8);


IDA and Ghidra both output this data in the wrong order so be cautious of trusting the output given. Instead the key and data should be laid out like this:

Code: Select all

    int32_t cuteKey[5]{0, 6, 0, 8, 0};
    char cuteStr[]{0xD1, 0x8B, 0x8A, 0x98, 0xD3, 0xB6, 0xD3, 0xA9, 0xCD, 0xD9, 0xDB, 0xA2, 0x95, 0xDC, 0xCE, 0xDE};
    Cutecode(cuteStr, cuteKey);


This will give us the XXTEA key of:

Code: Select all

.ruo,I*V:&$[j+1!


The second block can be decoded to get the sign, but that doesn't really matter but here is that:

Code: Select all

    char cuteSign[]{0xAD, 0xAC, 0xB0, 0xA5, 0xAA, 0xB0, 0x00};
    Cutecode(cuteSign, cuteKey);


Which gives back the sign: RUORUO

You can now decrypt this using any means of XXTEA, stepping over the 6 byte signature (RUORUO) at the top of the file. For the example file you gave, decrypted it is:

Code: Select all



cc.FileUtils:getInstance():addSearchPath('res/LuaScript/')



if cc.exports then
    cc.exports.ccslog = function(...) end
else
    ccslog = function(...) end
end


local luaExtend = {}

local function getChildInSubNodes(nodeTable, key)
    if #nodeTable == 0 then
        return nil
    end
    local child = nil
    local subNodeTable = {}
    for _, v in ipairs(nodeTable) do
        child = v:getChildByName(key)
        if (child) then
            return child
        end
    end
    for _, v in ipairs(nodeTable) do
        local subNodes = v:getChildren()
        if #subNodes ~= 0 then
            for _, v1 in ipairs(subNodes) do
                table.insert(subNodeTable, v1)
            end
        end
    end
    return getChildInSubNodes(subNodeTable, key)
end

luaExtend.__index = function(table, key)
local root = table.root
local child = root[key]
    if child then
        return child
    end

    child = root:getChildByName(key)
    if child then
        root[key] = child
        return child
    end

    child = getChildInSubNodes(root:getChildren(), key)
    if child then root[key] = child end
    return child
end


return luaExtend
aluigi
Site Admin
Posts: 12984
Joined: Wed Jul 30, 2014 9:32 pm

Re: How to extract file .luac extention [Rich and Famous]

Post by aluigi »

Very good, just made a script with the additional cutecode stuff:
http://aluigi.org/bms/cocos2dx_xxtea_cutecode.bms
atom0s
Posts: 250
Joined: Sat Dec 27, 2014 8:49 pm

Re: How to extract file .luac extention [Rich and Famous]

Post by atom0s »

Better off labeling the script specifically for this game. The CuteCode stuff and the keys used won't be the same for other games.
aluigi
Site Admin
Posts: 12984
Joined: Wed Jul 30, 2014 9:32 pm

Re: How to extract file .luac extention [Rich and Famous]

Post by aluigi »

Oh ok. The title of the script is "Rich and Famous cocos2 setXXTEAKeyAndSign/cutecode lua decrypt" which is ok, but the script filename is already done.