前言

从google找到的资料看,c++和lua交互的经典用法,都是c++调用lua脚本文件.
但是c++程序内,嵌入lua编译好的字节码缓冲区,也是一种用场.
下载的lua官方包里,没有例子程序了.
对于开源程序,自带丰富的例子,是很重要的事情. 虽然说”源码面前没有秘密”, 但实际情况心里还是万马奔腾,让人崩溃.
还是M$做的好,每个API都能在MSDN或WDK中找到可以run的例子.
找到了lua历史版本下载页: https://www.lua.org/versions.html

试验记录

自己编译出lua.exe

http://blog.csdn.net/lostspeed/article/details/52904410

写lua文件

假设文件为TaskBegin.lua, 里面只有一个c++注册的接口.

fnTaskBegin()

编译lua文件为目标文件

rem file : build_lua.cmd
mylua.exe -o TaskBegin.dat TaskBegin.lua
pause

将目标文件制作成数组

可以搞个小工具来做,我直接用WinHex将TaskBegin.dat拖进, 复制拷贝成c数组
这一步可以考虑用对称加密算法,将数组加密, 处理后,静态分析PE时, 就不知道这是lua的字节码了.

const BYTE g_AryLuaObj[132] = {
    0x1B, 0x4C, 0x75, 0x61, 0x53, 0x00, 0x19, 0x93, 0x0D, 0x0A, 0x1A, 0x0A, 0x04, 0x04, 0x04, 0x08, 
        0x08, 0x78, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x77, 
        0x40, 0x01, 0x0F, 0x40, 0x54, 0x61, 0x73, 0x6B, 0x42, 0x65, 0x67, 0x69, 0x6E, 0x2E, 0x6C, 0x75, 
        0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x03, 0x00, 0x00, 0x00, 
        0x06, 0x00, 0x40, 0x00, 0x24, 0x40, 0x80, 0x00, 0x26, 0x00, 0x80, 0x00, 0x01, 0x00, 0x00, 0x00, 
        0x04, 0x0C, 0x66, 0x6E, 0x54, 0x61, 0x73, 0x6B, 0x42, 0x65, 0x67, 0x69, 0x6E, 0x01, 0x00, 0x00, 
        0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 
        0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 
        0x5F, 0x45, 0x4E, 0x56
};

在程序中加入lua引擎

对于lua5.3.3分2步
* 包含src目录下的所有文件(.h, .c), 将.c的编译选项改成不支持预编译头文件
* 将.c中的main注释掉

在c++程序中调用嵌入的lua字节码数组

int __cdecl fnTaskBegin(lua_State* L) {
    TCHAR* pszBuf = NULL;
    size_t nLenBuf = 0;

    if (fnProcRegistr(&pszBuf, nLenBuf)) {
        fnProcRegOk(&pszBuf, nLenBuf);
    } else {
        fnProcRegFail(&pszBuf, nLenBuf);
    }

    fnClearBuf(&pszBuf, nLenBuf);

    lua_pushnumber(L, LUA_OK); // 压入返回值给.lua
    return LUA_YIELD; // ! 不能是 LUA_OK
}

void fnLuaProc() {
    lua_State* L = NULL;
    int iLuaErr = 0;
    // 创建lua新环境. lua_open被废弃了
    L = luaL_newstate();

    if (NULL != L) {
        luaL_openlibs(L); // 打开lua库
        // 注册宿主程序的接口函数给lua用
        lua_register(L, "fnTaskBegin", fnTaskBegin);
        // iLuaErr = luaL_dofile(L, LUA_FILE_NAME); // 载入lua文件
        iLuaErr = luaL_loadbuffer(L, (const char*)g_AryLuaObj, sizeof(g_AryLuaObj), NULL);

        if (LUA_OK != iLuaErr) {
            // printf("%s\r\n", luaL_checkstring(L, -1));
        } else {
            // 调用luaL_loadbuffer后, 要调用lua_pcall, 才能调用buffer中的lua接口
            // 这是文档中没有的, 机智啊:P
            lua_pcall(L, 0, LUA_MULTRET, 0);
        }

        lua_close(L); // 关闭lua环境
    }
}

总结

在业务逻辑的调用点上, 调用lua处理函数fnLuaProc(), 不熟悉lua或没有源码的逆向分析师只能去跟lua的实现, 将对抗强度转嫁给lua了.

可以考虑修改lua源码, 增加免查处理, 用来躲过扫描lua特征的工具. 让他不知道我用的lua版本和lua关键函数.
在没有这种工具样本的前提下,可以自己先修改一下lua源码, 提前做个准备.

Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐