阿里、蚂蚁、晟腾、中科加禾精彩分享 AI 基础设施洞见,现购票可享受 9 折优惠 |AICon 了解详情
写点什么

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:031325

评论

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

华为云“网红”语言Python课程来啦!

华为云开发者联盟

Python

安卓软件开发教程!全世界都在问Android开发凉了吗?offer拿到手软

欢喜学安卓

android 程序员 面试 移动开发

2021版面试必问178条性能优化建议!(Java+JVM+Redis+MySQL等)

Java架构追梦

Java 架构 面试 性能优化 金三银四跳槽

使用Kong作为微服务网关

行者AI

kong

技术干货 | 趣谈哈希表优化:从规避 Hash 冲突到利⽤ Hash 冲突

百度开发者中心

算法 数据结构和算法

【文末彩蛋】数据仓库服务 GaussDB(DWS)单点性能案例集锦

华为云开发者联盟

sql 数据仓库 数据

魔改出一个 Encoder | Rust 学习笔记(一)

李大狗

区块链 rust 入门

DCache 分布式存储系统|K-K-Row 缓存模块的创建与使用

TARS基金会

MySQL 数据库 nosql 分布式存储 TARS

简述:一款优秀的缺陷管理系统有哪些功能特点!

优秀

缺陷管理系统

国产芯片WiFi物联网智能插座—项目简介

不脱发的程序猿

物联网 28天写作 二月春节不断更 WiFi物联网插座 智能插座

国产芯片WiFi物联网智能插座—电源功能设计

不脱发的程序猿

28天写作 二月春节不断更 智能插座 WiFi物联网智能插座 电源设计

Git教程--git merge命令

生之欢愉,时间同行

git 程序员 git merge

2021金三银四涨薪季,这些面试题都掌握了嘛?

ios 面试

添加小助理vx:mxzFAFAFA即可!!

比伯

Java 编程 架构 面试 计算机

研发效能的历史和未来

李小腾

研发效能 数据驱动

Centos7配置librdkafka运行时

happlyfox

Centos 7 28天写作 2月春节不断更

第一篇文章

棉花糖

技术干货 | 中间件技术在百度云原生测试中的应用实践

百度开发者中心

底层技术 #技术干货#

常见的初级排序算法,这次全搞懂

Silently9527

Java 排序算法

诊所数字化:连锁型诊所应用远程会诊做分级诊疗

boshi

数字化医疗 七日更 28天写作

日记 2021年2月25日(周四)

Changing Lin

2月春节不断更

优化软件测试成本的7个步骤

程序员阿沐

软件测试 自动化测试 测试工程师 黑盒测试 白盒测试

Oracle sqlldr快速导入和sqluldr2快速导出

阳光下、慵懒的熊

数据库

安卓天气app开发!2021年Android开发者跳槽指南,社招面试心得

欢喜学安卓

android 程序员 面试 移动开发

运维工程师小张的日记

XSKY星辰天合

话题讨论 | 在中国程序员工作是青春饭吗?

happlyfox

话题讨论 2月春节不断更 话题王者

LeetCode题解:718. 最长重复子数组,动态规划,JavaScript,详细注释

Lee Chen

算法 大前端 LeetCode

开工来面试了几十个人,一言难尽

yes

面试

到底什么是敏捷

Teobler

敏捷 敏捷开发 敏捷精髓 敏捷书籍

话题讨论 | 英语对IT从业人员重要吗?

happlyfox

IT 话题讨论 28天写作 2月春节不断更 话题王者

3分钟学会如何上手supervisor看门狗

happlyfox

Linux centos7 28天写作 2月春节不断更

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