通过分析APK目录结构和Lua脚本加密,使用IDA找到解密函数,编写Python脚本解密Lua脚本,反编译后找到核心代码,最终通过XTEA加密解密得到flag。
AI 摘要

拿到APK之后,别急着安装运行,先看看它的目录结构。

image-20241014181306510

点进assess看到里全是lua脚本,猜测是需要加载的,发现被加密了

lua脚本需要加载,而在加载之前肯定是要先解密的,所以只要找到解密函数,然后就可以把解密后的lua脚本dump出来。或者写出解密脚本解出来

把apk拖入JADX,看java伪代码源码,查找加载lua脚本的位置

image-20241014182704140

猜测拿到File对象应该是需要载入的,然后进一步搜索这个so的native方法:

image-20241014182410457

使用IDA打开libluajava.so,经过查找找到函数luaL_loadbufferx

luaL_loadbufferx的第二个参数是加密的字节数组,第三个参数是大小,第四个参数是lua文件位置。 程序在这个函数中加载加密lua脚本,其中对脚本进行了解密操作。 根据第四个参数我们可以区分目前加载的lua脚本名称,从而选择性地dump (即在函数开头下断点,查看第四个参数内容)
luaL_loadbufferx函数伪代码如下:

image-20241014182912451

这里也给出解密函数:

def key(t):
    t1 = t
    t2 = (0xFFFFFFFF80808081 * t1) >> 32
    t3 = (t2 + t1) >> 7
    t4 = 0
    if t2 + t1 < 0:
        t4 = 1
    tf = (t + t3 + t4) & 0xFF
    return tf

def decrypt(s):
    out = bytearray(len(s))
    out[0] = 27
    t = 0
    for i in range(1, len(s)):
        t += len(s)
        out[i] = s[i] ^ key(t)
    return bytes(out)

解密出来的脚本:

image-20241014183317591

这里也给出一个用Unidbg来Hook的方法(挺久之前看的了)

使用Unidbg模拟Android环境Hook Androlua

通过unluac来反编译,先查看main.lua

image-20241014183419300

可以发现里面没有核心代码,而是先跳转到了root.lua里 再看root.lua里面有个onclick可以发现就是check按钮的点击事件

image-20241012161819696 但root里并没有这个函数,不过可以发现在这之前import了pz.lua 最后在pz里发现了函数A和加密函数

image-20241012161855957可以发现这是一个魔改的XTEA加密,编写脚本即可

exp:

function decipher(num_rounds, v, key)
local v0 = v[1] ~ 14
local v1 = v[2] ~ 17
local delta = 2161537835
local sum = (delta * num_rounds) & 0xFFFFFFFF
for i = 1, num_rounds do
v1 = (v1 - ((((v0 << 4) ~ (v0 >> 5)) + v0) ~ (sum + key[((sum >> 11) & 3) + 1]))) & 0xFFFFFFFF
v0 = (v0 - ((((v1 << 4) ~ (v1 >> 5)) + v1) ~ (sum + key[(sum & 3) + 1]))) & 0xFFFFFFFF
sum = (sum - delta) & 0xFFFFFFFF
end
v[1] = v0
v[2] = v1
end

local v = {863918170, 366827450, 2944604520, 1314064158, 2534040034, 1250268803, 3402278143, 1361039932, 3087907484, 3107271874}
local key = {5976, 40857, 3298229483, 1500946329}
local num_rounds = 38

for i = 0, 4 do
local idx = i * 2 + 1
local v_sub = {v[idx], v[idx + 1]}
decipher(num_rounds, v_sub, key)
v[idx] = v_sub[1]
v[idx + 1] = v_sub[2]
end
for i = 1, 10 do
v[i] = ((v[i] >> 24) | ((v[i] >> 8) & 0xFF00) | ((v[i] << 8) & 0xFF0000) | (v[i] << 24)) & 0xFFFFFFFF
end

local function uint32_to_bytes(u)
local b1 = u & 0xFF
local b2 = (u >> 8) & 0xFF
local b3 = (u >> 16) & 0xFF
local b4 = (u >> 24) & 0xFF
return string.char(b1, b2, b3, b4)
end

local result = ""
for i = 1, 10 do
result = result .. uint32_to_bytes(v[i])
end

print("flag{"..result.."}")--flag{7a5e-55e45-1671e-df3b7-cd7a1-6f1e-27fc}