首页
社区
课程
招聘
[原创]2022腾讯游戏安全安卓客户端初赛wp
发表于: 2022-4-28 11:48 23462

[原创]2022腾讯游戏安全安卓客户端初赛wp

2022-4-28 11:48
23462

这个外挂是一个典型的andlua程序,核心特征就是主要逻辑都在lua里面,外挂启动就会执行lua,由于lua被andlua官方加密了,所以外挂需要在内存中经过lua虚拟机加载函数时解密再运行,先判断了是否占用27042 和23946端口,以及/data/local/tmp下有没有re.frida.sever文件,进行反调试,然后申请root权限,创建一个随机文件名的空文件,同时将assert文件夹下的aes文件,进行rc4解密,解密后的文件字节流,写入前面创建的空文件中,并用root权限启动了这个随机文件名的文件进程,这个进程根据偏移修改了游戏数据,实现了飞天和人物移动加速的两个外挂功能。

image-20220418052603077

首先先查看外挂的结构

image-20220418054405607

发现是一个andlua程序,同时lua被加密了,用010打开是这样的,所有的lua都是类型这种结构

image-20220418054451999

assert文件夹下面有个aes文件不太一样,里面和lua的格式不太一样,怀疑是外挂会解密后,新进一个进程,但是ps -A 并没有看到叫aes的进程

image-20220418054605098

andlua的核心在lua里面,所以需要先把lua解密出来,这里我绕了一些弯路,这里是还没看出来是andlua官方加密,自己去逆了很久的lua的虚拟机

image-20220418054915339

image-20220418054927268

image-20220418054934546

在这里跟了很久,发现了解密的地方,同时为了避免麻烦,我hook j_lua_load这个函数,这里就出现了问题了,dump出来的是luas,用unluac反编译后,符号名出现丢失,代码逻辑是正常,但是没有符号可以说就是绝路,dump下来,是这样的

image-20220418055219123

继续逆虚拟机,找了很久,似乎没法通过frida和ida dump的方式,将符号问题搞定,这里我搜了一些关于andlua的解密方法,在b站上找到了https://www.bilibili.com/video/BV1W5411o7H3?spm_id_from=333.999.0.0,这个andlua的官方加解密,然后发现视频中lua 加密的跟这题lua 加密后的很像,又搜了一下andlua官方加密里面是有base64的,之前逆它解密方法的时候,也看到了,所以就开始用视频的方式dump出luac,用视频给的工具包,下载到本地。

image-20220418055532239

在/storage/emulated/0/ExaGear按照视频,将本地的 ExaGear文件夹复制过去

image-20220418060219899

,然后将外挂中需要解密的lua,都放入src文件夹中

image-20220418060234475

最后退出,运行ExaGear,点击luatools,自动解密开始

image-20220418060321282

在dst文件夹下,生成解密好的luac文件

image-20220418060351071

在用unlua53这个app,将这些luac反编译回lua,可以看到反编译后效果非常好,符号名也都有,很清晰

image-20220418060530763

这里main.lua文件是主要逻辑,可以看到进程在启动的时候,会检测23946和27042的端口,也就是ida和frida的常用默认端口,还检测了/data/local/tmp下有无re.frida.server相关的文件

image-20220418060902791

同时又创建了一个随机进程名的空白文件,并用shell命令运行了这个文件

image-20220418060942551

难怪之前的找aes什么的进程,是找不到的,名字都是随机的,同时解密assert/aes文件,看了下解密方法是rc4,密钥也是明文zyp

image-20220418061100011

image-20220418061148068

rc4没魔改,我直接在本地解密aes文件

因为外挂运行了这个文件,所以这个文件肯定是很重要的,解密后的文件发现是一个elf文件,用ida打开,有很明显的加壳,就两个区段,是很不正常,搜了下字符串,原来是upx的壳子

image-20220418061717535

从github上下了一份upx的代码,看看能不能upx -d直接脱了

image-20220418061959358

很舒服,是可以直接拖了的,哈哈,然后就可以用ida愉快的分析了,就是ida打开后也没符号,而且进程运行之后就死掉了,应该是出题人防止被附加吧,为了方便调试,我将调用kill和exit的地方都nop掉了

image-20220418062207468

但是还是闪退,这和upx的脱壳有关系了,每次upx -d的程序基本都运行的有点问题,可能是目前解压缩壳的问题,不过对我分析影响不是非常大,由于没符号,先从入口开始调试libc_init, 找到了这里,提一句,我是调试分析完再写的wp,里面的函数经过分析后确定功能之后被我重命名了。

image-20220418062345879

继续跟到这里

image-20220418062502831

这里基本确实是主函数,这里两个函数,猜测分别是两个功能的实现,后面的分析也验证了我的想法,一个一个的去跟,里面有比较严重的混淆,f5不了,慢慢审汇编,多用f4、f7、f8、f9,先跟mmain函数

image-20220418062939314

前面一大团字符串解密,直接跳到后面去看

image-20220418063032141

到这里的流程静态很难看,本地测试了下外挂直接运行,也是有效果的,也就是独立于游戏的,直接远程启动外挂进程就好了

image-20220418113021870

跟进去

image-20220418113449349

发现这里是获取fps游戏的pid,查看下参数,通过这个pidof来获取的

image-20220418113528391

到这里拿到pid

image-20220418113617575

继续f7

image-20220418113642635

每次调试这里都会跑飞,我只能在下一句汇编上下断才行,继续下一个函数

image-20220418113726386

查看参数为libUE4.so,应该是找到so基地址的

image-20220418113738900

跟进去看看,跟平常获取libue4.so基地址差不多

image-20220418113918529

查看参数

image-20220418113930887

这里是打开/proc/pid/maps,然后将每一行都和libUE4.so字符串做对比

image-20220418114019139

如果查询到了,再将地址拿出并转换

image-20220418114045516

image-20220418114141950

退出来,进入下一个函数

image-20220418114234478

这里发现是将libue4的基地址加上了0x4924570

[libue4.so_base+0x4924570]

跟进去,最终跟到一个syscall

image-20220418114450093

syscall的调用号是0x178,也就是读取,将libue4的基地址+0x4924570所对应的四个字节取出来了,继续下一个函数

image-20220418114703727

将取出来的数字+0x20,又经过相同的函数,和上面一样的分析,读取了4个字节到本地了

yk1=[libue4.so_base+0x4924570]

ykquestion=[yk1]+0x20

继续分析下一个函数

image-20220418115114364

同样将上面取出的字节加上了0x70,再次进行读取

yk2=[ykquestion]+0x70

yk3=[yk2]

继续跟进下一个函数

image-20220418115948729

发现这里是用libue4.so的基地址+0x4877034,然后依旧是同样的函数利用syscall,读取了4个字节

jf=libue4.so_base+0x4877034

jf1=[jf]

继续分析下一个函数,这里是一个远程读写的api,读取的jf1地址的0x78个字节到本地

image-20220418120335444

读取的数据

image-20220418120603001

发现是三个地址,这三个地址和后面的分析有关,继续看

image-20220418120719931

读取了yk3地址的4个字节到本地

yk4=[yk3]

image-20220418120944331

继续yk4+0x10的地址,取出4个字节

yk5=yk4+0x10

yk6=[yk5]

继续跟

image-20220418121052880

发现这里是一个大循环

image-20220418121429092

应该是遍历actor数组的,同时发现后面有拿到每个actor的name,并且对比

总结下这里在干什么,并举例

通过strstr来对比取出的字符串是否为FirstPersonC

image-20220418122100436

经过我的调试,发现这里actor数组都是固定的,index为0x23时,就找到了,调试了很多次都是一样的,也为我后续写外挂提供了很大便利

在index为0x23时,这时加的数值为0x90,字符串对比成功

image-20220418123641754

image-20220418123712537

pk4=[yk3+0x90]

字符串对比成功后,进入下一个逻辑

image-20220418123753316

找到人物的偏移

总结下,然后这里运行完,发现可以加速了,但是无法飞天

这里我开始用ue4dumper,将符号dump下来查看究竟是修改了什么

image-20220418150128360

经过这个文件的对比的查找,上面的那份偏移可以这么改

往 人物的MaxWalkSpeed这里地址写入了0x460ca000,增加了最大移动速度,可以越跑越快

继续飞天功能的分析,上面的函数无法在找出写数据的操作了,说明在别的函数里面,发现是在这个函数里面

image-20220418124237656

跟进去,因为分析过了一遍,我根据功能重命名了一下

image-20220418124418142

跟进

image-20220418124710012

先fopen了 /proc/filesystem , 本地测试了是这个东西,

image-20220417230246829

然后它在找这个

image-20220417230314471

在我上图倒数第二行,找到之后设置了为1 ,又fopen了这个文件

image-20220417230726716

image-20220417230805442

很多挂载的东西,但是还是想找这个

image-20220417230905994

最终拼接拿到了这个

image-20220417231200552

往这个文件里写入了0x30 ,应该是开启selinux的意思,这个函数分析完毕,继续下一个

image-20220418124822062

这里和移动加速的地方差不多,就是获取fps游戏进程的pid

image-20220418124903004

跟进去看看

image-20220417231851347

但是这里是附加的意思,让游戏成为自己的子进程

image-20220417232036242

这里阻塞游戏,ok,ok这个函数分析结束,下一个

image-20220418125226259

跟进去,发现拿出ro.build.version ,查看安卓的版本,为了后面的查找libc的路径有关系,不同安卓版本会不一样,分析下一个函数

image-20220418125331709

外挂先获取自己的libc的mprotect的指针,然后进去拿到fps的libc基地址

image-20220417234626421

通过寄存器知道了这个函数拿fps进程的libc和外挂进程的目的,是拿到外挂的libc的mprotect的偏移,加上fps的libc基地址拿到fps的libc中mprotect的函数地址,方便后面的ptrace调用

image-20220418005957738

分析下一个函数

image-20220418125737114

跟进去,这里libue4.so基地址+0x1e00000+0x1d6000作为参数传入

image-20220418125815567

image-20220418125827525

伪代码也很清晰,可以看到这里是ptrace利用getregs在保存寄存器,也就是保护上下文

继续跟下去,这里有四个ptrace

image-20220418010902785

伪代码挺清晰的

image-20220418010851977

这里是修改了libue4.so基地址+0x1e00000+0x1d6000所在页的读写权限,利用mprotect函数。

继续下一个函数分析,这里libue4.so基地址+0x1e00000+0x1d6000+0x428为参数

image-20220418130334885

这里是关键函数,跟进去

image-20220418130427964

这里ptrace参数为5,是写入数据的意思,在libue4.so基地址+0x1e00000+0x1d6000+0x428这个地址上写入了0xe3a00801这四个字节

再次运行游戏,按音量键,发现飞天了,说明这里就是飞天的关键

继续分析下一个函数

image-20220418130609603

跟进去

image-20220418130619075

查看伪代码,原来是退出ptrace,全部函数分析完毕!

总结一下飞天的实现,通过ptrace让游戏成为外挂进程的子进程,然后通过先拿出外挂进程libc中mprotect函数地址,算出mprotect的偏移,再拿到游戏的libc基地址,算出游戏进程的mprotect,再利用ptrace操纵寄存器的效果,调用mprotect函数,修改想修改内存的读写权限,然后再通过ptrace 写入数据,最后退出ptrace,进程运行结束

这里通过反编译libUE4.so,找到了这里

RT _ZNK27UCharacterMovementComponent11GetGravityZEv
.text:01FD640C _ZNK27UCharacterMovementComponent11GetGravityZEv

image-20220418145055572

很明显是这里修改了跳跃高度,每跳一次,高度直接太高,相当于飞天了。

这里我是用frida脚本实现了外挂功能,首先游戏并没有任何保护,所以可以选择远程读写或者注入,但是有些奇怪的这个外挂的两个功能,飞天是mprotect 页然后修改数据,但是人物移动速度是直接修改的,远程读写需要考虑权限,注入需要考虑修改的时机,基于两者我都想要,所以还是选择了frida来实现,通过之前得到的偏移,frida本身也提供内存权限的修改, 游戏启动后,attach方式启动,可以实现两个外挂的功能,飞天利用了人物actor在数组中下标一直不变,所以我这里不需要去遍历数组再通过字符串对比的方式找到人物,根据我之前记录人物的下标,就可以直接拿到人物actor了。

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
# coding=utf-8
 
 
 
DEFAULT_KEY = ""
 
 
def rc4(data, key=DEFAULT_KEY, skip=1024):
    x = 0
    box = range(256)
 
    x = 0
    for i in range(256):
        x = (x + box[i] + ord(key[i % len(key)])) % 256
        tmp = box[i]
        tmp2 = box[x]
        box[i] = box[x]
        box[x] = tmp
 
    x = 0
    y = 0
    out = []
    if skip > 0:
        for i in range(skip):
            x = (x + 1) % 256
            y = (y + box[x]) % 256
            box[x], box[y] = box[y], box[x]
 
    for char in data:
        x = (x + 1) % 256
        y = (y + box[x]) % 256
        box[x], box[y] = box[y], box[x]
        k = box[(box[x] + box[y]) % 256]
        out.append(chr(ord(char) ^ k))
 
    return out
 
 
if __name__ == '__main__':
    # handle input file or stream
    import sys
 
    tt = open("aes", "rb")
    ww = tt.read()
    decrypt_files=rc4(ww, "zyp", 0)
    decttt=open("dectt","wb")
    decttt.write("".join(decrypt_files))
# coding=utf-8
 
 
 
DEFAULT_KEY = ""
 
 
def rc4(data, key=DEFAULT_KEY, skip=1024):
    x = 0
    box = range(256)
 
    x = 0
    for i in range(256):
        x = (x + box[i] + ord(key[i % len(key)])) % 256
        tmp = box[i]
        tmp2 = box[x]
        box[i] = box[x]
        box[x] = tmp
 
    x = 0
    y = 0
    out = []
    if skip > 0:
        for i in range(skip):
            x = (x + 1) % 256
            y = (y + box[x]) % 256
            box[x], box[y] = box[y], box[x]
 
    for char in data:
        x = (x + 1) % 256
        y = (y + box[x]) % 256
        box[x], box[y] = box[y], box[x]
        k = box[(box[x] + box[y]) % 256]
        out.append(chr(ord(char) ^ k))

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 2022-4-28 11:49 被YenKoc编辑 ,原因:
收藏
免费 10
支持
分享
最新回复 (10)
雪    币: 3443
活跃值: (14163)
能力值: ( LV9,RANK:230 )
在线值:
发帖
回帖
粉丝
2
YK大佬太强了
2022-4-28 12:00
1
雪    币: 2677
活跃值: (5390)
能力值: ( LV10,RANK:177 )
在线值:
发帖
回帖
粉丝
3
珍惜Any YK大佬太强了
2022-4-28 12:28
0
雪    币: 4116
活跃值: (1034)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
4
YK大佬太强了
2022-4-28 14:13
0
雪    币: 8787
活跃值: (5783)
能力值: ( LV13,RANK:296 )
在线值:
发帖
回帖
粉丝
5
mark
2022-4-28 15:06
0
雪    币: 1507
活跃值: (853)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
希望作者用lua演示简单的外挂。最好最简单的apk作为目标,引大家入门
2022-4-28 15:15
0
雪    币: 8447
活跃值: (5041)
能力值: ( LV4,RANK:45 )
在线值:
发帖
回帖
粉丝
7
YK大佬太强了
2022-4-28 16:21
0
雪    币: 2134
活跃值: (3911)
能力值: ( LV4,RANK:55 )
在线值:
发帖
回帖
粉丝
8
mark
2022-4-28 17:43
0
雪    币: 403
活跃值: (172)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
YK大佬太强了
2022-4-29 01:47
0
雪    币: 2832
活跃值: (1430)
能力值: ( LV4,RANK:45 )
在线值:
发帖
回帖
粉丝
10
YK大佬太强了
2022-4-29 09:35
0
雪    币: 44
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
11
分析到改移动速度这里就研究不下去了
2022-5-12 11:37
0
游客
登录 | 注册 方可回帖
返回
//