sec2023安卓初赛复现

安装运行后检测绕过

adb安装,打开发现:

image-20240326145930896

楽,估计是检测到了什么。

猜基本都是什么frida、idaserver什么的,先进shell重命名一下:

image-20240326150345827

再开发现还是会,个人觉得是这个android_server64(ida远程调试服务器)被检测到了,删除后发现能够正常运行,不过我自己把ida_server放在子文件夹下没有被抓到(

image-20240326150610784

算是先过了一开始的检测,能够运行了。

用jadx对包体进行分析

image-20240326150858235

发现是使用Unity3D

  • 包名:com.com.sec2023.rocketmouse.mouse
  • 入口点:com.unity3d.player.UnityPlayerActivity

不过此前自己是没有逆向过Unity制作的安卓游戏的,上来直接挑战这个,楽

解压看so

image-20240326151240788

Unity的游戏都有着libil2cpp.so,github上有个开源的项目Il2CppDumper可以获取实现获取符号表

解压进入目录中,拿到libil2cpp.soglobal-metadata.datlibil2cpp.so: \lib\armeabi-v7a\libil2cpp.so global-metadata.dat: \assets\bin\Data\Managed\Metadata\global-metadata.dat

我去,着IL2CppDumper还有调用选择文件的API,不用自己手写文件路径,好评。

不过和复现的WP一样是失败了:

image-20240326152711549

跑010模板也对的上,应该是没有加密的:

image-20240326152920430

至于libil2cpp.so,反编译也是依托:

image-20240326153239591

符号表除了一些API之外,剩下的就是纯乱,但是运行的时候不可能会这样的,准备使用frida把它自解密后的数据dump下来

dump数据

在开启frida_server之后,又出现一样的heck detect的提示,盲猜是检测了frida端口,关掉进程之后又能运行,尝试换端口启动

./frs16 -l 0.0.0.0:27041

能跑起来来了

根据多个师傅的WP,dump内存的方法多种多样,复现就是为了学习,全部做一遍:

gg修改器 dump

起点:

image-20240326161127735

终点

image-20240326161044469

再用IL2CppDump看看:

好!

能从exe目录下得到

image-20240326205619243

但是dump下来的文件没有导入导出函数的符号

而 il2cpp 符号的脚本又是针对于 dump 文件的

所以修复一下dump的文件头之类的

.....修不懂。。

挖个坑,自己PE没看完呢


Fallw1nd师傅那个dump下来怪怪的,接着这边了

修复dump数据

首先是先将每个program_table的每一个p_offset改成p_vaddr

同时也将每一个p_filesz改成p_memsz(因为原先是进行了加固,现在这个是在内存中dump下来的,现在对应的段已经解压到内存的对应地址)

image-20240403235731355

然后注意最后一个表,这个表的结尾地址就是SECTION_HEADER的开头,所以SECTION_HEADER本来就应该是0x13BC000+63352 = 0x13CB778,所以填到文件的地址

image-20240404000021360

修改完按 program_table 按一下 F5 就会重新分析了,也可以看到一片空白的 section_header:

image-20240404000107492

直接将原版的复制到dump下来的(Ctrl+shift+C Ctrl+shift+V)

再 F5 刷新

刷新后,发现section header table乱码,和旁边的完全不同

image-20240409213702661

这部分的值是根据header的e_shtrndx的值,去寻找section header对应的块,再去寻找对应的符号:

image-20240409213901334

这里的值是26,指的是section_table_element[26]

ection_header_table->section_table_element[26]s_offset的值决定了section的名称将从1199370h去索引

image-20240409214010117

就是分析的data块,section的所有名称都在这个地方,把原来的复制过去就行,再F5刷新一下就行

image-20240409214135598

但是模板还是没分析出来,接下来就要修复section的偏移:

节(section) 的位置和大小由节头表(secion_header_table)中这两个成员决定

成员名称 含义
s_addr 如果此 section 需要映射到进程空间,此成员指定映射的起始地址;如不需映射,此值为 0
s_offset 此 section 相对于文件开头的字节偏移量.如果 section 类型为 SHT_NOBITS,表明该 section 在文件中不占空间,这时 sh_offset 没什么用

修正 节(section) 的偏移有两条规则

  • 如果s_addr为0,无需修改s_offset
  • 如果s_addr不为0,则将s_addr的值复制给s_offset

修正完成后,按下F5重新运行模板ELF.bt,可以发现section的名称已经恢复,同时也有了dynamic_symbol_table

image-20240409214517506

然后就可以丢到ida分析了

Frida hook dlopen

看懂了Fallw1nd师傅的脚本。。至福

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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
function WriteMemToFile(addr, size, file_path) {
Java.perform(function() {
// var prefix = '/data/data/com.com.sec2023.rocketmouse.mouse/files/'
var prefix = '/storage/emulated/0/dump/'

var mkdir = Module.findExportByName('libc.so', 'mkdir');
var chmod = Module.findExportByName('libc.so', 'chmod');
var fopen = Module.findExportByName('libc.so', 'fopen');
var fwrite = Module.findExportByName('libc.so', 'fwrite');
var fclose = Module.findExportByName('libc.so', 'fclose');

var call_mkdir = new NativeFunction(mkdir, 'int', ['pointer', 'int']);
var call_chmod = new NativeFunction(chmod, 'int', ['pointer', 'int']);
var call_fopen =
new NativeFunction(fopen, 'pointer', ['pointer', 'pointer']);
var call_fwrite =
new NativeFunction(fwrite, 'int', ['pointer', 'int', 'int', 'pointer']);
var call_fclose = new NativeFunction(fclose, 'int', ['pointer']);

call_mkdir(Memory.allocUtf8String(prefix), 0x1FF);
call_chmod(Memory.allocUtf8String(prefix), 0x1FF);
var fp = call_fopen(
Memory.allocUtf8String(prefix + file_path),
Memory.allocUtf8String('wb'));
if (call_fwrite(addr, 1, size, fp)) {
console.log('[+] Write file success, file path: ' + prefix + file_path);
} else {
console.log('[x] Write file failed');
}

call_fclose(fp);
});
}

function HookLibWithCallback(name, callback) {
var dlopen = Module.findExportByName('libdl.so', 'dlopen');
var detach_listener = Interceptor.attach(dlopen, {
onEnter: function(args) {
var cur = args[0].readCString();
console.log('[+] dlopen called, name: ' + cur);
if (cur.indexOf(name) != -1) {
this.hook = true;
}
},
onLeave: function() {
if (this.hook) {
console.log('[+] Hook Lib success, name:', name);
callback();
detach_listener.detach();
}
}
});
}

function LogModule(module) {
console.log('Module name: ' + module.name);
console.log('Module base: ' + module.base);
console.log('Module size: ' + module.size);
}

function TraverseModules(mode, {name = '', name_array = []}) {
if (mode == 'all') {
var modules = Process.enumerateModules();
for (var i = 0; i < modules.length; i++) {
var module = modules[i];
// LogModule(module);
}
return modules;
} else if (mode == 'single') {
var module = Process.getModuleByName(name);
LogModule(module);
return module;
} else if (mode == 'multiple') {
var modules = Process.enumerateModules();
var target_modules = [];
for (var i = 0; i < modules.length; i++) {
var module = modules[i];
if (name_array.indexOf(module.name) != -1) {
LogModule(module);
target_modules.push(module);
}
}
return target_modules;
}
}

function DumpIL2CPP() {
var libil2cpp = TraverseModules('single', {name: 'libil2cpp.so'});
WriteMemToFile(libil2cpp.base, libil2cpp.size, 'libil2cpp.so');
}

function main() {
HookLibWithCallback('libil2cpp.so', DumpIL2CPP);
}

main();

根据自己的情况把dump的地址改了,不知道为什么我这边有root权限但是搞不到里面的文件,只能这样子保存然后dump下来

怪哦,这个好像dump的不多。

ida分析dump的libil2cpp

将 dump 文件载入 ida 之后,最好是 Rebase 一下,因为是运行态文件,可能有些内存值已经被重定位,因此 Rebase 之后可能得到更多符号, Rebase 的值就是 dump 文件的载入地址,文件名上就有,比如我的是 0x7495598000