写点什么

Lua 程序逆向之 Luac 文件格式分析(下)

  • 2019-11-29
  • 本文字数:5147 字

    阅读完需:约 17 分钟

Lua程序逆向之Luac文件格式分析(下)

点击 010 Editor 菜单 Templates->New Template,新建一个模板,会自动生成如下内容:


//------------------------------------------------//--- 010 Editor v8.0 Binary Template////      File: //   Authors: //   Version: //   Purpose: //  Category: // File Mask: //  ID Bytes: //   History: //------------------------------------------------
复制代码


File 是文件名,010 Editor 使用.bt 作为模柏树的后缀,这里取名为 luac.bt 即可。


Authors 是作者信息。


Version 是当前模板的版本,如果将最终的模板文件上传到 010 Editor 的官方模板仓库,010 Editor 会以此字段来判断模板文件的版本信息。


Purpose 是编写本模板的意图,内容上可以留空。


Category 是模板的分类,010 Editor 中自带了一些内置的分类,这里选择 Programming 分类。


File Mask 是文件扩展名掩码,表示当前模板支持处理哪种文件类型的数据,支持通配符,如果支持多种文件格式,可以将所有的文件扩展名写在一行,中间使用逗号分开,这里设置它的值为“*.luac, *.lua”。


ID Bytes 是文件开头的 Magic Number,用来通过文件的开头来判断是否为支持处理的文件,这里的取值为“1B 4c 75 61”。


History 中可以留空,也可以编写模板的更新历史信息。


最终,Luac.bt 的开头内容如下:


//------------------------------------------------//--- 010 Editor v8.0 Binary Template////      File: luac.bt//   Authors: fei_cong(346345565@qq.com)//   Version: 1.0//   Purpose: //  Category: Programming// File Mask: *.luac, *.lua//  ID Bytes: 1B 4c 75 61//   History: //      1.0   fei_cong: Initial version, support lua 5.2.//// License: This file is released into the public domain. People may //          use it for any purpose, commercial or otherwise. //------------------------------------------------
复制代码


010 Editor 模板与 C 语言一样,支持 C 语言的宏、数据类型、变量、函数、代码语句、控制流程等,还支持调用常见的 C 语言函数。


数据类型上,支持的非常丰富,官方列出 BS 的支持的数据类型如下:


- 8-Bit Signed Integer - char, byte, CHAR, BYTE- 8-Bit Unsigned Integer - uchar, ubyte, UCHAR, UBYTE- 16-Bit Signed Integer - short, int16, SHORT, INT16- 16-Bit Unsigned Integer - ushort, uint16, USHORT, UINT16, WORD- 32-Bit Signed Integer - int, int32, long, INT, INT32, LONG- 32-Bit Unsigned Integer - uint, uint32, ulong, UINT, UINT32, ULONG, DWORD- 64-Bit Signed Integer - int64, quad, QUAD, INT64, __int64- 64-Bit Unsigned Integer - uint64, uquad, UQUAD, UINT64, QWORD, __uint64- 32-Bit Floating Point Number - float, FLOAT - 64-Bit Floating Point Number - double, DOUBLE - 16-Bit Floating Point Number - hfloat, HFLOAT - Date Types - DOSDATE, DOSTIME, FILETIME, OLETIME, time_t (for more information on date types see Using the Inspector)
复制代码


在编写模板时,同一数据类型中列出的类型,使用上是一样,如下面的代码片断:


local int a;local int32 a;local long a;
复制代码


表示的都是一个 32 位的整型变量,这三种声明方式表达的含义是相同的。声明变量时,需要在前面跟上 local 关键字,如果没有跟上 local,则表明是在声明一个占位的数据字段。所谓占位的数据字段,指的 010 Editor 在解析模板中的变量时,会对占位的数据部分使用指定的数据类型进行解析,如下面的代码:


typedef struct {    GlobalHeader header;    Proto proto;} Luac;Luac luac;
复制代码


010 Editor 在解析这段代码时,会按照 Luac 中所有的占位数据字段信息解析当前的二进制文件。GlobalHeader 与 Proto 的声明也中如此,没有加上 local 的数据字段,都会被 010 Editor 解析并显示。


除了支持基本的 C 语言格式结构体 struct 外,010 Editor 模板语法还加入了一些特性,比如字段注释与格式、结构体压缩与处理函数。看如下的结构体信息:


typedef struct {    uint64 varname_size <format=hex>;    char varname[varname_size];    uint32 startpc <format=hex, comment="first point where variable is active">;    uint32 endpc <format=hex, comment="first point where variable is dead">;} LocVar <read = LocVarRead, optimize = false>;
复制代码


这是按照前面介绍的 LocVar 结构体信息,按照 010 Editor 模板语法处理过后的效果。为字段后添加 format 可以指定它的输出格式为十六进制 hex,默认是 10 进制;为字段后添加 comment 可以指定它的注释信息,这两个字段可以同时存在,在中间加入一个逗号即可;可以为结构体指定 read 来指定它的类型读取函数,也可以指定 write 来指定它的类型写入函数,read 与 write 有着自己的格式,如下所示:


string LocVarRead(LocVar &val) {    return val.varname;}
复制代码


所有的 read 与 write 返回值必须为 string,参数必须为要处理的结构体类型的引用。注意:010 Editor 模板语法不支持指针,但支持引用类型,但引用类型不能作为变量与函数的返回值,只能作为参数进行传递,在编写模板代码时需要注意。


除了以上的基础类型外,010 Editor 模板还支持字符串类型 string,这在 C 语言中是不存在的!它与 char[]代表的含义是相同的,而且它支持的操作比较多,如以下字符串相加等操作:


local string str = "world";local string str2 = "hello " + str + "!\n";
复制代码


010 Editor 模板中的宏有限制,并不能解析那些需要展开后替换符号的宏,只支持那些能够直接计算的宏。如下面的 BITRK 与 ISK 宏:


#define SIZE_B9#define BITRK(1 << (SIZE_B - 1))#define ISK(x)((x) & BITRK)
复制代码


前者可以直接解析并计算出来,010 Editor 模板就支持它,而对于 ISK 宏,并不能在展开时计算出它的值,因此,010 Editor 模板并不支持它。


010 Editor 模板支持 enum 枚举,与 C 语言中的枚举的差别是,在定义枚举时可以指定它的数据类型,这样的好处是可以在 010 Editor 模板中声明占位的枚举数据。如下所示是 Luac.bt 中用到的 LUA_DATATYPE 类型:


enum <uchar> LUA_DATATYPE {    LUA_TNIL=     0,    LUA_TBOOLEAN=  1,    LUA_TLIGHTUSERDATA =  2,    LUA_TNUMBER=     3,    LUA_TSTRING=     4,    LUA_TTABLE=     5,    LUA_TFUNCTION=     6,    LUA_TUSERDATA=     7,    LUA_TTHREAD=     8,    LUA_NUMTAGS     =    9,};
复制代码


010 Editor 模板中支持调用常见的 C 语言库函数,如 strlen()、strcat()、print()、sprintf()、strstr(),不同的是,函数名上有些差别,这些可调用的函数在 010 Editor 模板中首字母是大写的,因此,在调用时,它们分别是 Strlen()、Strcat()、Print()、Sprintf()、Strstr()。更多支持的字符串操作的函数可以查看 010 Editor 的帮助文档“String Functions”小节,除了“String Functions”外,还有“I/O Functions”、“Math Functions”、“Tool Functions”、“Interface Functions”等函数可供模板代码使用。


接下来看下代码结构部分,010 Editor 模板支持 C 语言中的 for/while/dowhile 等循环语句,这些语句可以用来组成到 010 Editor 模板的函数与代码块中。一点细微的差别是 010 Editor 模板的返回类型只能是上面介绍过的基础类型,不支持自定义类型与数组结构,这就给实际编写代码带来了一些麻烦,遇到这种函数场景时,就需要考虑更改代码的结构了。

编写 luac.bt 文件格式模板

了解了 010 Editor 模板语法后,就可以开始编写 Luac.bt 模板文件了。编写模板前,需要找好一个 Luac 文件,然后边写边测试,生成一个 Luac 文件很简单,可以编写好 hello.lua 后,执行下面的命令生成 hello.luac:


$ luac -o ./hello.luac ./hello.lua
复制代码


生成好 Luac 文件后,就是编写一个个结构体进行测试,这是纯体力活了。luadec 提供了一个 ChunkSpy52.lua,可以使用它打印 Luac 的文件格式内容,可以参考它的输出进行 Luac.bt 的编写工作,实际上我也是这么做的。


首先是 GlobalHeader,它的定义可以这样写:


typedef struct {    uint32 signature <format=hex>;   //".lua"    uchar version <format=hex>;    uchar format <comment = "format (0=official)">;    uchar endian <comment = "1 == LittleEndian; 0 == BigEndian">;    uchar size_int <comment = "sizeof(int)">;    uchar size_size_t <comment = "sizeof(size_t)">;    uchar size_Instruction <comment = "sizeof(Instruction)">;    uchar size_lua_Number <comment = "sizeof(lua_Number)">;    uchar lua_num_valid <comment = "Determine lua_Number whether it works or not, It's usually 0">;    if (version == 0x52) {        uchar luac_tail[0x6] <format=hex, comment = "data to catch conversion errors">;    }} GlobalHeader;
复制代码


这种定义的方式与前面介绍的 LocVar 一样,具体就不展开讨论了。下面主要讨论编写过程中遇到的问题与难点。


首先是输出与 ChunkSpy52.lua 一样的 function level,也就是函数的嵌套级别,定义结构体时可以传递参数,这一点是 C 语言不具备的,但这个功能非常实用,可以用来传递定义结构时的信息,如这里的 function level 就用到了该特性。这是 Protos 的定义:


typedef struct(string level) {    uint32 sizep <format=hex>;    local uint32 sz = sizep;    local uint32 i = 0;    local string s_level;    while (sz-- > 0) {        SPrintf(s_level, "%s_%d", level, i++);        Proto proto(s_level);    };} Protos <optimize=false>;
复制代码


为结构体加上一个 string 类型的 level 参数,初始时传值“0”,然后往下传递时,为传递的值累加一,这样就做到了 function level 的输出。


然后是 Constant 常量信息的获取,由于 TValue 支持多种数据的类型,因此在处理上需要分别进行处理,这里参考了 luadec 的实现,不过在细节上还是比较麻烦。luadec 使用 DecompileConstant()方法实现,它的代码片断如下:


···char* DecompileConstant(const Proto* f, int i) {    const TValue* o = &f->k[i];switch (ttype(o)) {case LUA_TBOOLEAN:return strdup(bvalue(o)?"true":"false");case LUA_TNIL:return strdup("nil");#if LUA_VERSION_NUM == 501 || LUA_VERSION_NUM == 502case LUA_TNUMBER:{char* ret = (char*)calloc(128, sizeof(char));sprintf(ret, LUA_NUMBER_FMT, nvalue(o));return ret;}case LUA_TSTRING:        return DecompileString(o);default:return strdup("Unknown_Type_Error");}}···
复制代码


bvalue 与 nvalue 是 Lua 提供的两个宏,这在编写模板时不能直接使用,需要自己实现,由于宏的嵌套较多,实际测试时编写了 C 语言代码展开它的实现,如 nvalue 展开后的实现为:


((((((o))->tt_) == ((3 | (1 << 4)))) ? ((lua_Number)(((((o)->value_).i)))) : (((o)->value_).n))));
复制代码


于是编写替换代码 number2str 函数,实现如下:


string number2str(TValue &o) {    local string ret;    local string fmt;    if (get_inst_sz() == 4) {        fmt = "(=%.7g)";    } else if (get_inst_sz() == 8) {        fmt = "(=%.14g)";    } else {        Warning("error inst size.\n");    }    local int tt = o.value_.val.tt_;    //Printf("tt:%x\n", tt);    local lua_Integer i = o.value_.i;    local lua_Number n = o.value_.n;    SPrintf(ret, "%.14g", ((tt == (3 | (1 << 4))) ? i : n));    return ret;}
复制代码


然后为 Constant 编写 read 方法 ConstantRead,代码片断如下:


string ConstantRead(Constant& constant) {    local string str;    switch (constant.const_type) {        case LUA_TBOOLEAN:        {            SPrintf(str, "%s", constant.bool_val ? "true" : "false");            return str;        }        case LUA_TNIL:        {            return "nil";        }        case LUA_TNUMBER:        {            return number2str(constant.num_val);        }        case LUA_TSTRING:        {            return "(=\"" + constant.str_val + "\")";        }        ......        default:            return "";    }}
复制代码


DecompileConstant 中调用的 DecompileString 方法,原实现比较麻烦,处理了非打印字符,这里简单的获取解析的字符串内容,然后直接返回了。


最后,所有的代码编写完成后,效果如图所示:



luac.bt 的完整实现可以在这里找到:https://github.com/feicong/lua_re


本文转载自公众号 360 云计算(ID:hulktalk)。


原文链接:


https://mp.weixin.qq.com/s/EXMqlYe6C8MEWsz063wHGA


2019-11-29 15:031596

评论

发布
暂无评论
发现更多内容

针对容器层的五种攻击手段

穿过生命散发芬芳

容器安全 三周年连更

汽油价格变动实时短信通知

DS小龙哥

三周年连更

手把手教会你|Sockets多用户-服务器数据库编程

TiAmo

服务器 socket通信 数据库编程

HTML和xml有哪些区别?

海拥(haiyong.site)

三周年连更

【web 开发】PHP 特殊的对象引用 "$this"(62)

迷彩

php 面向对象 this指针 三周年连更

新手如何学习挖漏洞?看这篇就够了【网络安全】

网络安全学海

网络安全 信息安全 渗透测试 WEB安全 漏洞挖掘

云原生底座之上,这些企业领跑行业的秘密

阿里巴巴云原生

阿里云 容器 微服务 云原生

史上最强升级!音乐制作软件Logic Pro中文特别版

Rose

Logic Pro Mac音乐软件下载 Logic Pro破解版

假期做了一项调研:大厂为啥都自研RPC?结果合乎情理!

冰河

程序员 RPC 架构师 技术提升 大厂招聘

2023-05-03:给你一棵 二叉树 的根节点 root ,树中有 n 个节点 每个节点都可以被分配一个从 1 到 n 且互不相同的值 另给你一个长度为 m 的数组 queries 你必须在树上执行

福大大架构师每日一题

Go 算法 福大大

Flink应用框架层

阿泽🧸

flink 三周年连更

xmind怎么导出为pdf?Xmind最全入门教程

Rose

Xmind 2022 XMind下载 思维导图软件

GitHub上“千金难求”的Spring Boot趣味实战全彩版手册,太干了

程序知音

Java spring 微服务 springboot Java进阶

一文看懂:StopWatch 源码解读

后台技术汇

三周年连更

与伙伴同行,Serverless 让创新触手可及

阿里巴巴云原生

阿里云 Serverless 云原生

如何解决Paragon NTFS for Mac安装分卷失败?

Rose

Paragon NTFS ntfs 安装分卷失败

Prometheus 瘦身第一步,使用 mimirtool 找到没用的 Prometheus 指标

巴辉特

Grafana Prometheus Mimir mimirtool

新来个技术总监:发现谁再用 delete 删数据直接开除!

Java你猿哥

Java MySQL ssm 存储 delete

阿里巴巴官方上线!号称国内2023最新Java八股文天花板(终极版)首次开源

程序员小毕

程序员 微服务 JVM java面试 Java八股文、

Django操作异步任务

乌龟哥哥

三周年连更

习惯了和AI聊天,感觉我更加社恐了......

FN0

人工智能 AI AIGC

硬核Prompt赏析:与Auto-GPT的“契约”

无人之路

ChatGPT Prompt

MATLAB实现航天相关的仿真

袁袁袁袁满

三周年连更

Java面试题1000+附答案大全(合适各级Java开发人员)

架构师之道

Java 面试

The Foundry Modo 16 16.1v3激活版 专业3D建模软件

Rose

3d建模 The Foundry Modo

Mac音乐制作软件推荐:Ableton Live 11 Suite中文版「win/Mac」

Rose

Ableton Live 11破解版 Ableton Live 11中文版 苹果软件下载

阿里巴巴爆款“Spring Cloud Alibaba 全彩笔记”正式发布

采菊东篱下

微服务

Redis Set 用了 2 种数据结构来存储,到现在才知道

Java你猿哥

Java ssm sets

Mac M1 安装SD不折腾版本

IT蜗壳-Tango

三周年连更

C++智能指针和内存管理:使用指南和技巧

小万哥

c++ 后端 开发 内存管理 智能指针

Lua程序逆向之Luac文件格式分析(下)_文化 & 方法_安全客资讯平台_InfoQ精选文章