首页
论坛
专栏
课程

[原创]用 Lua 简单还原 OpCode 顺序

2019-4-4 14:44 3940

[原创]用 Lua 简单还原 OpCode 顺序

2019-4-4 14:44
3940

前段时间外出游荡,遇上个大兄弟说能不能跑个 Lua 脚本把 Lua 里被修改的 OpCode 顺序弄出来,最近有空自己尝试一下

 

首先准备材料

  • 被修改 OpCode 顺序的 Lua 虚拟机程序一个,不限 SO/DLL/EXE;
  • 同版本的正常 Lua 虚拟机程序一个,不知道版本的同学可以从字符串里找,这里是 Lua 5.1.5;
  • 随便写一个能正常编译的 Lua 函数。

思路

  1. dump 出 Lua 虚拟机中的脚本字节码文件;
  2. 用正常的字节码去校对 OpCode 乱序的字节码。

一般情况下 *.luac 文件由 luac 程序生成,其中的数据是由 luaU_dump 函数产生,在 luac.c 文件中被调用,而 luaU_dump 的另一个入口在 lapi.clua_dump,被绑定到 Lua 的 string.dump 函数。
通过在 Lua 脚本中对需要 dump 的函数用 string.dump,可以得到对应的字节码。

 

分别在目标 Lua 和正常 Lua 虚拟机中运行以下这段代码,获得 OpCode 乱序后的 luac 文件和正常的 luac 文件:

-- lua script
function test()
    -- ...略
end
local data = string.dump(test)
local fp = io.open("test.luac","wb")
fp:write(data)
fp:close()

拿得到的两份 luac 文件比较下,提取出差异内容,差异的部分应该是 Instruction 中的 OpCode。

 

从 Lua 源码中的 lvm.c 文件的 luaV_execute 函数中得知,是通过 GET_OPCODE 宏获取到 OpCode,再执行对应的操作

// lvm.c
void luaV_execute (lua_State *L, int nexeccalls) {
    //......
    switch (GET_OPCODE(i)) {
      case OP_MOVE: {
        setobjs2s(L, ra, RB(i));
        continue;
      }
      case OP_LOADK: {
        setobj2s(L, ra, KBx(i));
        continue;
      }
    //......
}

GET_OPCODE 宏的定义在 lopcodes.h 文件中,相关代码如下:

// lopcodes.h
#define cast(t, exp)    ((t)(exp))
#define SIZE_OP        6
#define POS_OP        0
#define MASK1(n, p)    ((~((~(Instruction)0)<<n))<<p)
#define GET_OPCODE(i)    (cast(OpCode, ((i)>>POS_OP) & MASK1(SIZE_OP,0)))

简单来说,指令 & 0x3F 就可以得到 OpCode 的数值

 

依旧在目标 Lua 中执行,让它自己输出 OpCode 顺序,完整代码资源在附件

-- lua sciprt
-- ...
local data = string.dump(test)    -- dump
local new_op = {}
-- 用目标 lua 和正常 lua 的 dump 数据对比
for i = 1, #data do
    local by_ori = string.byte(ori_data,i)
    local by_new = string.byte(data,i)
    if by_ori ~= by_new then
        local op_name = ori_op_name[bit:_and(0x3F,by_ori) + 1]
        local op_idx = bit:_and(0x3F,by_new)
        new_op[op_name] = op_idx
    end
end

print("old \t new \t name")
for idx, op_name in pairs(ori_op_name) do
    local tmp = ''
    if new_op[op_name] ~= nil then
        tmp = new_op[op_name]
    end
    print((idx - 1) .. "\t" .. tmp .. "\t" .. op_name )
end

输出结果

 

对于手游中使用的 xlua/ulua/tolua,操作方法类似

 

下面是技术总结

  1. Lua 版本一定要相同
  2. 用于 dump 的函数,尽量覆盖所有的指令操作,否则只能提取出已有的指令

附件为用到的相关资源



[推荐]看雪企服平台,提供安全分析、定制项目开发、APP等级保护、渗透测试等安全服务!

最后于 2019-4-4 17:29 被Amun编辑 ,原因: 话题修正
上传的附件:
上一主题 下一主题
最新回复 (14)
bambooqj 2019-4-4 15:49
2
0
这帖子不错..很强.
lhb天羽 2019-4-4 16:39
3
0
mark
Tatsuy 2019-4-4 17:38
4
0
哇这个思路……
坚固的堡垒从内部被攻破
这样子获取对照很棒哦
Editor 2019-4-4 18:12
5
0
感谢分享~
芃杉 2019-4-4 18:56
6
0
mark 感谢分享
xxRea 2019-4-11 11:38
7
0
哇,厉害啊,大佬
Zkeleven 2019-4-11 20:04
8
0
学习了
落叶似秋 1 2019-4-15 09:55
9
0
前来支持师兄
天道xxxx 2019-4-16 03:54
10
0
厉害 但是不管用
天道xxxx 2019-4-16 05:01
11
0
读取不了。。
着迷留香 2019-4-17 10:43
12
0
落叶似秋 前来支持师兄
朋友,读了你的文章 ,有些小疑问,加个QQ,可以吗?3133916
keeslient 2019-4-21 15:02
13
0
感谢分享啊
Jmdebugger 2019-4-23 10:17
14
0
很棒
guotouck 2019-5-14 14:59
15
0
感觉没意义啊.. 前提哪里来的明文lua ?
看起来这是 拿 明文lua和解密lua 对比计算的  前提哪里来的明文lua?
最后于 2019-5-14 15:21 被guotouck编辑 ,原因:
游客
登录 | 注册 方可回帖
返回