Lua简介

Lua(发音: /ˈluːə/,葡萄牙语“月亮”)是一个简洁、轻量、可扩展的脚本语言。Lua有着相对简单的C语言API而很容易嵌入应用中[3]。很多应用程序使用Lua作为自己的嵌入式脚本语言,以此来实现可配置性、可扩展性。 Lua是一种多重编程范型的程序设计语言:它只提供了很小的一个特性集合来满足不同编程范型的需要,而不是为某种特定的编程范型提供繁杂的特性支持。例如,Lua并不提供继承这个特性,但是你可以用元表格来模拟它。诸如命名空间、类这些概念都没有在语言基本特性中实现,但是我们可以用表格结构(Lua唯一提供的复杂数据结构)轻易模拟。正是提供了这些基本的元特性,我们可以任意的对语言进行自需的改造。 Lua实现了少量的高级特征比如头等函数、垃圾回收、闭包、正当尾调用、类型转换(于运行时间在字符串和数值之间自动转换)、协程(协作多任务)和动态模块装载。 (其实这是我抄的,具体我也不是很了解,这里附一个链接Wiki # Andirod逆向中的Lua 前几个星期做题的时候就碰到了,但是没怎么学,今天抽空了解了一下,以后又补充内容再写。 (借用学长的一句话,lua题都这样,擦不多都是公式hh) 这里用两个题来理解一下 ## 2024BuildCTF–新?Android路 用jdax-gui打开apk文件,可以看到一堆乱七八糟的东西。。。

main函数

看到主程序,发现了肯定是lua,很快啊,我直接解包so文件反编译,进入luaL_loadbufferx函数查看

.so文件

说实话,有点幽默·,我确实没看懂hhhhh 后来才知道这是一个现成的加壳方式脱壳链接 这样就可以反编译了啊哈哈哈哈·~ main.lua but.lua crc3264.lua 由于crc3264的opcode应该是被改了,所以…我还是看汇编吧(也是分析的不太对,但是不影响) ### main函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
function L1_2(A0_3)
local L1_3, L2_3, L3_3, L4_3, L5_3
L1_3 = L0_2
L2_3 = L1_3.crc3264
L2_3 = L2_3.encode
L3_3 = A0_3
L2_3 = L2_3(L3_3)

L1_3.key = "BuiIdCTF"
L3_3 = L1_3.but
L4_3 = L1_3.key
L5_3 = L2_3
L3_3 = L3_3(L4_3, L5_3)
L2_3 = L3_3

L3_3 = L1_3.crc3264
L3_3 = L3_3.encode
L4_3 = L2_3
L3_3 = L3_3(L4_3)
L2_3 = L3_3

if L2_3 == "L8xeG92a+mrlqa8Bp54fxTgAe7IJue5HTZx+bM6eBxJr0ukR6oQnRg==" then
return true
else
return false
end
end
首先看一下main函数中的check函数,简单阅读可以知: 1.先调用 crc3264.encode: crc3264.encode 第一次调用时接收了用户的输入(A0_3),即从 L2_3 = L2_3(L3_3) 的部分。 这个调用通过 crc3264.encode(A0_3),将用户输入的字符串转换为经过一个特定算法编码的输出。

2.然后连接 key,并进入but函数: 定义了一个字符串 key = “BuiIdCTF”。 接着,将 key 与第一步中 crc3264.encode 的输出结果 L2_3 组合起来。 通过 L1_3.but(L4_3, L5_3) 实现的

3.再次调用 crc3264.encode: 将组合后的字符串再次传入 crc3264.encode 进行第二次编码。

最终通过 if L2_3 == “L8xeG92a+mrlqa8Bp54fxTgAe7IJue5HTZx+bM6eBxJr0ukR6oQnRg==” then 判断编码结果是否与预定义的字符串是否一致。

but函数

下面分析but函数,这个很简单,交给gpt我们就可以知道这是一个rc4算法,并且没有任何魔改

crc3264函数

其实根据对比的字符串我们可以大概猜出来,这应该是一个base64加密

base字符串

在2000多行我们可以看到这个base64的编码表

下面就很简单了,知道逻辑之后一把梭

解密

DASCTF 2024金秋十月–ezAndroid

和上一道一样,也是先jdax-gui反编译,由于Main函数长得和上一道几乎一模一样,我就不贴照片了 然后,也是一样的步骤,啪的一下,我直接解包so文件反编译,进入luaL_loadbufferx函数查看

so文件

这个加密逻辑就可以看懂了,还是没这么幽默~ 解密脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def decrypt_lua(encrypted_data, byte_count):
decrypted_data = bytearray(byte_count)
decrypted_data[0] = encrypted_data[0]
v12 = 0
for i in range(1, byte_count):
v12 += byte_count
offset = v12 % 255
decrypted_data[i] = encrypted_data[i] ^ offset
return decrypted_data


with open("main.lua", "rb") as f:
encrypted_data = f.read()
byte_count = len(encrypted_data)
decrypted_data = decrypt_lua(encrypted_data, byte_count)
with open("decrypted_main.lua", "wb") as f:
f.write(decrypted_data)
print("解密完成,已保存为 decrypted_main.lua")
经过分析就可以知道,这个主要加密逻辑是在pz.lua里面 别的我不细说,对我们做题没啥用 pz.lua 好,下面就到恶心的函数分析环节: 1.填充函数 function L0_1(A0_2),该函数接收一个字符串 A0_2,计算其长度,如果长度不是8的倍数,则在字符串末尾添加零字符(\000)来填充到8的倍数。 2.位操作函数 function L0_1(A0_2, A1_2, A2_2, A3_2),该函数将四个数字合并成一个32位整数。 还有剩下的几个函数,粗略看一下作用:将输入字符串 A0_2 按 8 字节分块,并将每块转换为一组数字。等等 从这几个初始处理(?也许算是)函数,以及数字特征,加上自己的直觉,我么可以知道,这应该是一个魔改的tea算法 仔细看一下,(或者用一下GPT呢~),我们可以定位到核心函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
function L0_1(A0_2, A1_2)
local L2_2, L3_2, L4_2, L5_2, L6_2, L7_2, L8_2, L9_2, L10_2, L11_2, L12_2, L13_2, L14_2
L2_2 = A0_2[1]
L3_2 = A0_2[2]
L4_2 = 0
L5_2 = "(114514+114514)*((1+1)*4514+((1+1)*4*51-4+11-4*5+14))+(114514+(114*514+(114*51*4+((1+1)*4*514+(11*(45-1)/4)))))"
L6_2 = load
L7_2 = "return "
L8_2 = L5_2
L7_2 = L7_2 .. L8_2
L6_2 = L6_2(L7_2)
L6_2 = L6_2()
L7_2 = "-11 + 45 * 1 + 4"
L8_2 = load
L9_2 = "return "
L10_2 = L7_2
L9_2 = L9_2 .. L10_2
L8_2 = L8_2(L9_2)
L8_2 = L8_2()
L9_2 = 1
L10_2 = L8_2
L11_2 = 1
for L12_2 = L9_2, L10_2, L11_2 do
L13_2 = L4_2 + L6_2
L4_2 = L13_2 & 4294967295
L13_2 = L3_2 << 4
L14_2 = L3_2 >> 5
L13_2 = L13_2 ~ L14_2
L13_2 = L13_2 + L3_2
L14_2 = L4_2 & 3
L14_2 = L14_2 + 1
L14_2 = A1_2[L14_2]
L14_2 = L4_2 + L14_2
L13_2 = L13_2 ~ L14_2
L13_2 = L2_2 + L13_2
L2_2 = L13_2 & 4294967295
L13_2 = L2_2 << 4
L14_2 = L2_2 >> 5
L13_2 = L13_2 ~ L14_2
L13_2 = L13_2 + L2_2
L14_2 = L4_2 >> 11
L14_2 = L14_2 & 3
L14_2 = L14_2 + 1
L14_2 = A1_2[L14_2]
L14_2 = L4_2 + L14_2
L13_2 = L13_2 ~ L14_2
L13_2 = L3_2 + L13_2
L3_2 = L13_2 & 4294967295
end
L2_2 = L2_2 ~ 14.0
L3_2 = L3_2 ~ 17
L9_2 = {}
L10_2 = L2_2
L11_2 = L3_2
L9_2[1] = L10_2
L9_2[2] = L11_2
return L9_2
end
写出大概的c语言代码,大概是这个意思:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdint.h>
void Fun(uint32_t v[2], uint32_t k[4]) {
uint32_t v0 = v[0], v1 = v[1];
uint32_t sum = 0;
uint32_t delta = 0x80d6732b; // 给定的delta值

for (int i = 0; i < 38; i++) { // 给定的轮数
sum += delta;
sum &= 0xffffffff; // 保持sum为32位

uint32_t k0 = k[sum & 3];
uint32_t k1 = k[(sum >> 11) & 3];

// 根据Lua代码更新v0和v1
v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + k0);
v0 &= 0xffffffff; // 保持v0为32位

v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + k1);
v1 &= 0xffffffff; // 保持v1为32位
}

v[0] = v0 ^ 0x000e; // 最后的异或操作
v[1] = v1 ^ 0x0011; // 最后的异或操作
}
tea初见端倪,下面就是寻找一下几个主要数据

轮数:

1
L7_2 = "-11 + 45 * 1 + 4"
delta:
1
L5_2 = "(114514+114514)*((1+1)*4514+((1+1)*4*51-4+11-4*5+14))+(114514+(114*514+(114*51*4+((1+1)*4*514+(11*(45-1)/4)))))"
key:
1
2
3
4
5
6
7
8
9
10
11
12
L0_1 = "114 * 51 + 4 - 1 + 145 + 14"
...-- 省略若干
L2_1 = "114 * 51 * 4 + (1145 * 14 + (1 * -(1 - 4) * 514 - 11 + 45 - 1 - 4))"
...-- 省略若干
L4_1 = "(114514 + 114514) * (11451 + 4 + (1 + 14 * 51 * 4 + (1 * 14 * (5 + 1) + 4))) +(114514 + (114 * 514 + (11 * 4514 + (-1145 * (1 - 4) + 1 * 14 + 5 * 14))))"
...-- 省略若干
L6_1 = "(114514 + 114514) * (114 * (51 + 4) + (1 + 1 + 4 * 5 * 14 + (11 / (45 - 1) * 4))) +(114514 + ((1 + 1) * 4514 + 114 * 5 * 1 * 4 + 1 * 14 - 5 + 14))"
...-- 省略若干
L8_1[1] = L9_1
L8_1[2] = L10_1
L8_1[3] = L11_1
L8_1[4] = L12_1
加密数据:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
L2_1 = "return (114514 + 114514) * ((1 + 1) * 451 * 4 + 114 + 51 - 4 + 11 * -4 + 51 - 4) + (114 * 51 * 4 + ((1 + 1) * 45 * 14 - 11 + 45 * 1 + 4))"
...-- 省略若干
L3_1 = "return (114514 + 114514) * (1 * -(1 - 4) * 514 + 114 - 51 - 4) + (114514 + (114 * 51 * 4 + (1 + 14514 + ((1 + 1) * 45 * 14 + 11 - 4 + 5 * 14))))"
...-- 省略若干
L4_1 = "return (114514 + 114514) * (11451 + 4 + (11 * (4 + 5) * 14 + 1 + 14 - 5 + 1 + 4)) + (114514 + (114 * 514 + (11451 * 4 + ((1 + 1) * 4 * 51 * 4 + 1 - 14 + 5 + 14))))"
...-- 省略若干
L5_1 = "return (114514 + 114514) * (1145 * (1 + 4) - 11 + 4 + 5 + 14) + (114514 + (1 + 14514 + (11 * -45 * (1 - 4) + 11 - 4 + 5 / 1 - 4)))"
...-- 省略若干
L6_1 = "return (114514 + 114514) * ((1 + 1) * 4514 + 1 + 145 * 14 + 11 - 4 * 5 + 14) + 114 * 514 + 1 + 14514 + 1145 - 14"
...-- 省略若干
L7_1 = "return (114514 + 114514) * (114 * (51 - 4) + (1 + 1 * 4 * 5 * (1 + 4))) + (11 * (451 - 4) + 1 - 14 + 51 - 4)"
...-- 省略若干
L8_1 = "return (114514 + 114514) * (1 + 14514 + (1 - 14 * -(5 + 1) * 4 + 11 * -4 + 51 - 4)) + 114 * 514 + 114 * 5 * 14 - 11 + 45 * 14 + 11 - 4 + 5 / 1 - 4"
...-- 省略若干
L9_1 = "return (114514 + 114514) * (114 * 51 + 4 + 114 + 5 + 1 + 4) + (114514 + (114 * 51 * 4 + (1145 * 14 + (114 * -5 * (1 - 4) + 11 * 4 + 5 + 1 - 4))))"
...-- 省略若干
L10_1 = "return (114514 + 114514) * (11451 + 4 + (11 * (45 + 1) * 4 + 11 * -4 + 51 - 4)) + (114514 + (114 * 51 * 4 + (11451 + 4 + (114 * (5 + 1) * 4 + 11 + 4 * 5 / 1 - 4))))"
...-- 省略若干
L11_1 = "return (114514 + 114514) * (11451 + 4 + (11 * 4 * (51 - 4) + 114 - 5 * 14)) + 11451 * 4 + 11 * 4 * 5 * 14 + 11 * 4 + 5 * 14"
...-- 省略若干
L0_1[1] = L1_1
L0_1[2] = L2_1
L0_1[3] = L3_1
L0_1[4] = L4_1
L0_1[5] = L5_1
L0_1[6] = L6_1
L0_1[7] = L7_1
L0_1[8] = L8_1
L0_1[9] = L9_1
L0_1[10] = L10_1
L0_1[11] = L11_1
L0_1[12] = L12_1
L0_1[13] = L13_1

ok,处理完毕,完美的tea

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <stdio.h>
#include <stdint.h>
#include <string.h>

void xteaDec (uint32_t* v, uint32_t* k) {
unsigned int i;
uint32_t v0=v[0]^14, v1=v[1]^17, delta=0x80d6732b, sum=delta*38;
for (i = 1; i <= 38; i++) {
v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + k[(sum>>11) & 3]);
v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + k[sum & 3]);
sum -= delta;
}
v[0]=v0; v[1]=v1;
}

int main() {
uint32_t k[4]={0x1758,0x9f99,0xc496fceb,0x59769f99};
uint32_t v5[]={0x337e585a,0x15dd57ba,0xaf831968,0x4e53071e,0x970a61e2,0x4a859683,0xcacaa4ff,0x511fd23c,0xb80dba9c,0xb93534c2};
for (int i = 0; i < 10; i+=2) {
uint32_t* v = &v5[i];
xteaDec(v,k);
}
for (int i = 0; i < 10; i++) {
printf("%02x ",v5[i]);
}
printf("解密后的字符串: DASCTF{%s}\n", (char*)v5);
return 0;
// DASCTF{e5a7e55-1-54e1763fd-c-7b1a7d1f6-72-e}
}

最后,贴一个大佬博客,里面有详细的lua解法,虽然我没太看懂…~ 大佬博客