腾讯游戏安全2023
初赛
参考资料
https://bbs.kanxue.com/thread-278648.htm
https://bbs.kanxue.com/homepage-888558.htm
https://bbs.kanxue.com/homepage-thread-831526-1.htm
https://bbs.kanxue.com/homepage-thread-887676-1.htm
腾讯游戏安全竞赛官网下载 下载链接
分析题目
apk
通过jadx反编译来获取apk信息,查看AndroidManifest.xml获取包名:com.com.sec2023.rocketmouse.mouse
解压apk,在lib目录发现libil2cpp.so
文件
使用Il2CppDumper获取符号信息
Il2CppDumper
直接运行Il2CppDumper.exe并依次选择il2cpp的可执行文件和global-metadata.dat文件,然后根据提示输入相应信息。
程序运行完成后将在当前运行目录下生成输出文件
1 | Il2CppDumper.exe <executable-file> <global-metadata> <output-directory> |
被加密了
IDA分析so
这个so加密了
利用frida进行sodump
so在运行时是解密状态的,利用frida进行sodump来得到解密状态的so。
先用frida获取so地址测试有没有frida检测
1 | setImmediate(function (){ |
被检测了
修改frida特征
- 修改frida-server的名字
- 修改frida-server的默认端口
注入成功了!!
dumpso
接下来就开是编写sodump的代码了
1 | function dump_so(soName){ |
然后把dump下来的so给pull出来
移动位置并修改权限
pull
再次使用Il2CppDumper获取符号信息
生成如下文件
修复dump下来的so
对于dump下来的so文件,所有的段(segment)
和节(section)
的偏移都是在虚拟空间中的偏移(即映射到进程空间的虚拟地址偏移),但静态分析工具分析so时所使用的偏移仍然为在实际文件中的偏移(即相对于文件开头的字节偏移量),错误的偏移导致静态分析工具如IDA等无法分析dump下来的so
所以我们需要将segment
和section
在实际文件中的偏移替换为在虚拟空间中的偏移,这些偏移由program header table
和secion header table
内的成员指出
使用010 Editor将libil2cpp.so
和libil2cpp.so_0x78dec12000_0x13cc000.so
打开进行对比修复
修正段偏移
段的位置和大小由程序头中的字段决定
在libil2cpp.so_0x78dec12000_0x13cc000.so
中将program header table
中的每一个element
的p_vaddr复制到p_offset
,p_memsz
复制到p_filesz
修正节表的偏移
节表的位置在最后一个段之后
在libil2cpp.so_0x78dec12000_0x13cc000.so
中,section_header_table
的文件偏移为0x11AB778
,需要将其改为在虚拟空间的偏移
找到程序头表的最后一个段,节表的位置就是内存中的偏移(p_vaddr
)加上内存中的长度(p_memsz
)
节表在虚拟空间的偏移为:0x13CB778 = 0x13BC000 + 0xF778
节表偏移的字段是文件头中e_shoff
修正后,按下F5
刷新,重新运行模板,结果如下
补充section的内容
修正节表的偏移后,发现节表的区域没有内容,需要从libil2cpp.so
中复制节表的内容到libil2cpp.so_0x78dec12000_0x13cc000.so
中,重新运行模板
恢复节的名称
修复了节的内容之后,节的名称依旧是乱码
每个节的名字在.shstrtab
节中保存着,首先我们要到文件头中的e_shtrndx
字段找到.shstrtab
的索引,其字段值是0x1A
,也就是说,.shstrtab
的索引是26
。.shstrtab
节头中的s_offset
字段是section名称的偏移,其值为0x1199370
右键0x1199370
可以跳转到这个地址处,发现其内容都是乱码
回到libil2cpp.so
中,发现同样的地方是完整的字符串
将libil2cpp.so
中的section的符号名称复制到libil2cpp.so_0x78dec12000_0x13cc000.so
中,F5
刷新,重新运行模板
修正节的偏移
节的位置由两个字段决定,s_addr
和 s_offset
字段 | 含义 |
---|---|
s_addr | 如果此 section 需要映射到进程空间,此成员指定映射的起始地址;如不需映射,此值为 0 |
s_offset | 此 section 相对于文件开头的字节偏移量.如果 section 类型为 SHT_NOBITS,表明该 section 在文件中不占空间,这时 sh_offset 没什么用 |
修正节的偏移的规则:
- 如果s_addr为0,无需修改s_offset
- 如果s_addr不为0,则将s_addr的值复制给s_offset
修改完成好,按下F5
,发现有了dynamic_symbol_table
,至此,修复完成
IDA恢复符号
将libil2cpp.so_0x78dec12000_0x13cc000.so
拖入IDA中进行分析,待分析完成后,点击编辑
->Segments
->Rebase program
,重新定位基址为0x78dec12000
,这样可以分析出更多的符号
分析完成后,点击File->Script file...
运行il2cppdumper中的ida_with_struct_py3.py,需要注意的这个脚本需要运行两次,第一次选择script.json,il2cpp.h第二次选择stringliteral.json,il2cpp.h(分析时间可能很久)
获取flag
flag获取条件:当金币数量超过1000,会出现flag
有一个思路,我们知道金币的英文是coin,到前面使用Il2CppDumper得到的文件dump.cs文件里搜索coin,发现coin
跟进到IDA中,有发现,这里有一个1000,猜想到金币数量1000
切换为汇编语言,找到发现cmp指令以及值1000,这里利用frida把1000改成0,那么在游戏中随便捡一个金币就可以获取flag了
利用frida进行patch
1 | function flag(){ |
运行游戏,frida注入
寻找OK按钮调用的函数
可以使用frida-il2cppDumper,用法就是直接frida注入_agent.js
1 | frida -H 192.168.0.106:8000 -f com.com.sec2023.rocketmouse.mouse -l _agent.js |
在IDA中分析调用的函数SmallKeyboard__oO0oOo0
,明显看出是生成TOKEN的地方SmallKeyboard
类被调用了很多次,在dump.cs里面搜索发现,此处定义了KeyType不同的值对应的含义,那么这个EnterKey就是OK按钮了
回到IDA,再去搜索SmallKeyboard,找到SmallKeyboard__iI1Ii(SmallKeyboard_o *this, SmallKeyboard_iII1i_o *info, const MethodInfo *method)这个函数,这是与KeyType有关的函数,显而易见,我们需要重点分析的是KeyType == 2的情况
使用frida
来hook System_Convert__ToUInt64_519142037956
的返回值来验证猜想
1 | function hook_input(){ |
输入123456,按下OK按钮,frida的hook结果如下,猜测正确。