记一次棋牌游戏 cocos2d-x LUA脚本解密

1.如何查看luac的key和sign

    对一棋牌游戏反编译后,可以在其assets目录下面发现*.luac的脚本,打开下面的luac脚本,可以看到luac是加密的,如下图:

 记一次棋牌游戏 cocos2d-x LUA脚本解密_第1张图片      

   记一次棋牌游戏 cocos2d-x LUA脚本解密_第2张图片

    可以看到加密的luac文件的头几个字节,都是一个固定字符串值,而这个字符串值就是利用官方加解密Xxtea算法用的sign值,找到sign后,接下来找KEY,用IDA载入libcocos2dlua.so,搜索刚刚找到的固定字符串zhongrdhsakhfjasf,可以看到KEY位于其附近。

记一次棋牌游戏 cocos2d-x LUA脚本解密_第3张图片


2.xxtea加解密实现

    由于lua本身是开源的,其官方加解密实现方式很容易就能查找资料得到,根据得到key和sign就可以利用xxtea算法来对脚本进行解密,本人用c++实现了xxtea算法的解密过程,这个程序只需要三个参数:文件路径,sign, key即可解密,附上代码:

main.cpp

#include "stdafx.h"
#include "xxtea.h"
#include 


int main(int argc, char *argv[])
{
	if (argc <= 1)
	{
		printf("usage .\ConsoleApplication1   \n");
		return 0;
	}
	printf("filePath:%s sign:%s key:%s", argv[1], argv[2], argv[3]);
	std::string filePath = argv[1];
	std::string outFilePath = filePath;
	outFilePath.resize(filePath.rfind("\\")+ 1);
	size_t pos = filePath.rfind("\\");
	size_t pos2 = filePath.rfind(".");
	std::string temp = filePath.substr(pos + 1, pos2 - pos -1);
	outFilePath += "jiemi\\";
	outFilePath += temp;
	outFilePath += ".lua";
	int signLen = strlen(argv[2]);
	int keyLen = strlen(argv[3]);
	std::ifstream ifs(argv[1], std::ios::binary);
	unsigned char *content = NULL;
	int length = 0;
	if (ifs)
	{
		ifs.seekg(0, std::ios::end);
		length = ifs.tellg();
		ifs.seekg(0, std::ios::beg);
		content = new unsigned char[length];
		memset(content, 0, length);
		ifs.read((char* )content, length);
	}
	ifs.close();
	xxtea_long  retLen = 0;
	unsigned char * data = xxtea_decrypt(content + signLen, length - signLen, (unsigned char*)argv[3], keyLen, &retLen);
	if (data == NULL) 
	{
		printf("%s decrypt fail\n", argv[1]);
		return -1;
	}
	std::ofstream ofs(outFilePath.c_str(), std::ios::binary);
	if (ofs)
	{
		ofs.write((char*)data, retLen);
	}
	ofs.close();
	printf("%s->%s decrypt success\n", filePath.c_str(), outFilePath.c_str());
	delete[] content;
    return 0;
}
   xxtea.cpp
/***********************************************************************
 
 Copyright 2006-2009 Ma Bingyao
 Copyright 2013 Gao Chunhui, Liu Tao
 
 These sources is free software. Redistributions of source code must
 retain the above copyright notice. Redistributions in binary form
 must reproduce the above copyright notice. You can redistribute it
 freely. You can use it with any free or commercial software.
 
 These sources is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY. Without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
 github: https://github.com/liut/pecl-xxtea
 
 *************************************************************************/

#include "xxtea.h"
#include 
#include 

static void xxtea_long_encrypt(xxtea_long *v, xxtea_long len, xxtea_long *k)
{
    xxtea_long n = len - 1;
    xxtea_long z = v[n], y = v[0], p, q = 6 + 52 / (n + 1), sum = 0, e;
    if (n < 1) {
        return;
    }
    while (0 < q--) {
        sum += XXTEA_DELTA;
        e = sum >> 2 & 3;
        for (p = 0; p < n; p++) {
            y = v[p + 1];
            z = v[p] += XXTEA_MX;
        }
        y = v[0];
        z = v[n] += XXTEA_MX;
    }
}

static void xxtea_long_decrypt(xxtea_long *v, xxtea_long len, xxtea_long *k)
{
    xxtea_long n = len - 1;
    xxtea_long z = v[n], y = v[0], p, q = 6 + 52 / (n + 1), sum = q * XXTEA_DELTA, e;
    if (n < 1) {
        return;
    }
    while (sum != 0) {
        e = sum >> 2 & 3;
        for (p = n; p > 0; p--) {
            z = v[p - 1];
            y = v[p] -= XXTEA_MX;
        }
        z = v[n];
        y = v[0] -= XXTEA_MX;
        sum -= XXTEA_DELTA;
    }
}

static unsigned char *fix_key_length(unsigned char *key, xxtea_long key_len)
{
    unsigned char *tmp = (unsigned char *)malloc(16);
    memcpy(tmp, key, key_len);
    memset(tmp + key_len, '\0', 16 - key_len);
    return tmp;
}

static xxtea_long *xxtea_to_long_array(unsigned char *data, xxtea_long len, int include_length, xxtea_long *ret_len) {
    xxtea_long i, n, *result;
    
    n = len >> 2;
    n = (((len & 3) == 0) ? n : n + 1);
    if (include_length) {
        result = (xxtea_long *)malloc((n + 1) << 2);
        result[n] = len;
        *ret_len = n + 1;
    } else {
        result = (xxtea_long *)malloc(n << 2);
        *ret_len = n;
    }
    memset(result, 0, n << 2);
    for (i = 0; i < len; i++) {
        result[i >> 2] |= (xxtea_long)data[i] << ((i & 3) << 3);
    }
    
    return result;
}

static unsigned char *xxtea_to_byte_array(xxtea_long *data, xxtea_long len, int include_length, xxtea_long *ret_len) {
    xxtea_long i, n, m;
    unsigned char *result;
    
    n = len << 2;
    if (include_length) {
        m = data[len - 1];
        if ((m < n - 7) || (m > n - 4)) return NULL;
        n = m;
    }
    result = (unsigned char *)malloc(n + 1);
    for (i = 0; i < n; i++) {
        result[i] = (unsigned char)((data[i >> 2] >> ((i & 3) << 3)) & 0xff);
    }
    result[n] = '\0';
    *ret_len = n;
    
    return result;
}

static unsigned char *do_xxtea_encrypt(unsigned char *data, xxtea_long len, unsigned char *key, xxtea_long *ret_len) {
    unsigned char *result;
    xxtea_long *v, *k, v_len, k_len;
    
    v = xxtea_to_long_array(data, len, 1, &v_len);
    k = xxtea_to_long_array(key, 16, 0, &k_len);
    xxtea_long_encrypt(v, v_len, k);
    result = xxtea_to_byte_array(v, v_len, 0, ret_len);
    free(v);
    free(k);
    
    return result;
}

static unsigned char *do_xxtea_decrypt(unsigned char *data, xxtea_long len, unsigned char *key, xxtea_long *ret_len) {
    unsigned char *result;
    xxtea_long *v, *k, v_len, k_len;
    
    v = xxtea_to_long_array(data, len, 0, &v_len);
    k = xxtea_to_long_array(key, 16, 0, &k_len);
    xxtea_long_decrypt(v, v_len, k);
    result = xxtea_to_byte_array(v, v_len, 1, ret_len);
    free(v);
    free(k);
    
    return result;
}

unsigned char *xxtea_encrypt(unsigned char *data, xxtea_long data_len, unsigned char *key, xxtea_long key_len, xxtea_long *ret_length)
{
    unsigned char *result;
    
    *ret_length = 0;
    
    if (key_len < 16) {
        unsigned char *key2 = fix_key_length(key, key_len);
        result = do_xxtea_encrypt(data, data_len, key2, ret_length);
        free(key2);
    }
    else
    {
        result = do_xxtea_encrypt(data, data_len, key, ret_length);
    }
    
    return result;
}

unsigned char *xxtea_decrypt(unsigned char *data, xxtea_long data_len, unsigned char *key, xxtea_long key_len, xxtea_long *ret_length)
{
    unsigned char *result;
    
    *ret_length = 0;
    
    if (key_len < 16) {
        unsigned char *key2 = fix_key_length(key, key_len);
        result = do_xxtea_decrypt(data, data_len, key2, ret_length);
        free(key2);
    }
    else
    {
        result = do_xxtea_decrypt(data, data_len, key, ret_length);
    }
    
    return result;
}

/* }}} */
    XXTEA,又称Corrected Block TEA,是XTEA的升级版,设计者是Roger Needham, David Wheeler

加密过程:

    记一次棋牌游戏 cocos2d-x LUA脚本解密_第4张图片

    其核心是:XXTEA算法使用128bit的密钥对以32bit为单位的信息块进行加密。

3.非官方xxtea加解密爆破方案

    刚上述操作几乎无障碍的就可以轻易获得key和sign进行解密,所以比较的好的加固方案就是不使用官方加解密,并且对key字符串做混淆,那如果碰上这种加固方案,那如何进行爆破呢,答案是爆破libcocos2dlua.so里面的luaL_loadbuffer函数。Cocos引擎的lua加载器为cocos2dx_lua_loader,最终都是调用luaL_loadbuffer函数来加载,一般厂商会在这层上面对lua脚本进行解密,即是在luaL_loadbuffer函数获取buff参数可得到解密后的lua脚本。

  记一次棋牌游戏 cocos2d-x LUA脚本解密_第5张图片

    lua引擎加载lua脚本最底层是到lua_reader函数。该函数负责最底层的脚本buff遍历,因此在此处dump出来的lua脚本是最纯正的lua脚本,所有加密都已经被去除(修改lua opcode或者引擎逻辑除外)。不过这个点的获取不到足够的文件信息(文件名、buff index等),需要配合上层函数拼凑lua脚本。


参考链接:

    Lua游戏逆向及破解方法介绍: http://www.freebuf.com/articles/system/103388.html

    浅析android手游lua脚本的加密与解密: https://bbs.pediy.com/thread-216969.htm

    

你可能感兴趣的:(android逆向)