移动端脱壳分析
GitHub - noxke/TencentGameClientOpenCourse: 腾讯游戏客户端公开课2023 腾讯菁英班
DUMP脱壳
拿到附件先file一下
arm平台的ELF文件,因此运行和调试使用手机进行(不需要root)
ida32打开CrackMe2
,进行静态分析
ida只识别出了几个函数,一眼壳,Shift+F12查看字符串,upx加壳
下载upx尝试使用upx -d
脱壳
脱不下来,猜测upx特征被隐藏了,因此进行手动脱壳
先将CrackMe2
push到手机,运行测试
程序在等待输入状态,此时upx已经完成解压缩,直接dump内存
启动idaserver,ida连接idaserver并附加调试
进程在read系统调用断下,根据LR寄存器定位返回地址,下断点,F9恢复进程运行
程序中随意输入字符串1234567890
后回车,进程在断点出断下
使用Ctrl+F7运行到返回,直到PC离开libc.so内存空间,函数返回情况如下
1 |
|
PC离开libc.so内存空间后,返回到debug005段中,因此该段为upx解压之后的可执行段,使用OllyDump插件dump该段内存,选择debug005段
OllyDump自动识别了入口点0x5358
,以及存在关联的段,所有参数保持默认,dump为Crackme2_dump.so
静态分析
使用ida32打开Crackme2_dump.so
,自动识别出程序入口点start
和主函数main
在main函数中,显然0x6E2C处为输出提示字符串,Shift+F12查看字符串,定位到如下可能有用的字符串
1 |
|
反调试
第一个可疑字符串cat /proc/net/tcp |grep :5D8A
查看当前的TCP连接情况,0x5D8A
转换为十进制为23946
,idaserver的默认调试端口,因此此处为检查端口的反调试,查看交叉引用定位到反调试代码
定位到反调试函数的地址为0x53F4
,命名为undebug1
第二个可疑字符串/proc/%d/status
,查看进程的状态,根据后续的字符串TracerPid
猜测该处是检查进程的TracerPid
判断进程是否被调试,查看交叉引用定位到反调试代码
定位到反调试函数地址为0x059D4
,命名为undebug2
第三个字符串undebug
,是检测到调试器时的输出字符串,查看交叉定位输出该字符串的位置
定位到函数地址为0x5AD0
,命名为undebug
,该函数调用undebug1
和undebug2
检测是否被调试,由于undebuge1
和undebug2
中均没有使程序终止的代码,因此检查调试的入口点为undebug
输入校验
注意到字符串DMD2vxKYDLvezuriqND2DhP3BJfdtuWWrxe9pq==
,典型的base64字符串,尝试解码但发现乱码,猜测解码后的数据为二进制数据或base64码表被替换
True Answer
应该是输入字符串正确时的输出,根据其交叉引用定位到输出该字符串的位置
定位到0x7B50
函数处,将其命名位check
两个分支分别输出Wrong Answer
和True Answer
,查看该函数的反编译代码
1 |
|
注意到该if内有明显的base64编码特征
每次从a2中取出三个字符,依次存放在v49, v50, v51中
1 |
|
对第一个字符取高6位作为索引查表,然后由sub_8430
处理
1 |
|
对第一个字符取低两位,第二个字符取高4位,组合为6位,作为索引查表,然后由sub_8430
处理
1 |
|
对第二个字符取低4位,第三个字符取高两位,组合为6位,作为索引查表,然后由sub_8430
处理
1 |
|
对第三个字符取低6位,作为索引查表,然后由sub_8430
处理
1 |
|
后续不足3个字符的一次此处省略分析,分析发现存在两个关键点,一是base64编码的码表(char *)dword_48230
,二是sub_8430
函数
base64编码结束的地址为0x7D84
0x48230
储存的是码表的地址,未在dump的文件中,后续动态调试时分析
0x8430
处函数较为复杂,但是由于其输入简单,因此考虑在动态调试时分析
上述代码对参数字符串a2使用base64编码,分析check
函数剩余代码
1 |
|
在check
函数开始时,a1[0], a1[1], a1[2]
均被设为0,但a1作为sub_8430
参数,多次参入base64编码的过程,且在最后的判断中,多个值从a1
处读取,猜测a1
处可能储存base64编码的结果
1 |
|
上述代码进行字符串比较,关键判断
动态调试分析
根据上述动态调试分析,获得以下需要断点的位置
getc返回地址
debug005+0x9E64
getc处为Thumb模式,需要使用Alt+g将Thumb标准设为1进行解析
undebug反调试函数
debug005+0x5AD0
check函数
debug005+0x7B50
base64编码结束位置
debug005+0x7D84
需要重点分析的内存区域有:
check函数参数寄存器
R0
check函数参数寄存器
R3
base64编码码表
debug005+0x48230
由于dump得到的elf文件中部分地址存储的值错误,不能之间进行运行调试,因此继续对CrackMe2
进行附加调试
按照dump过程进行附加调试,进程断下后对上述地址下断点恢复运行,随意输入字符串1234567890
getc返回位置断下
取消断点,F9继续运行,undebug函数断下
手动将PC设到LR寄存器指向的返回地址处
F9继续运行
check函数断下,寄存器如下
记录R0寄存器的值0xFFBAA758
查看R1地址处(a2参数),很明显为输入的字符串
R2的值(a3参数)为0xA
,很明显为输入字符串的长度
查看R3地址处(a4参数),0x21
查看base64编码码表debug005+0x48230
注意到该码表与标准base64码表不同,标准base64大写字母在前,小写字母在后,而此处相反,因此经过编码后的字符串大小写与标准base64编码相反
1 |
|
F9运行到对输入字符串base64编码完成处debug005+0x7D84
,查看之前记录的R0中地址处内容0xFFBAA758
第一个值为0x21
,根据前面的分析,可能是待比较的数据长度,第二个值为0x10
,可能为输入字符串编码后的长度,第三个值为地址0xF67450A0
,查看该地址处内存
地址处的值为输入字符串编码后的b64字符串mtiZndu2nZG5ma==
,与上述测试相同,该地址-0x20
处为另一个b64字符串vgvUy2vUDeDHBwvtzwn1CML0Eq==
,猜测为待比较的字符串,使用base64解码测试
1 |
|
解码得到字符串TencentGameSecurity
,猜测可能为flag
ida继续调试,运行到第一个判断分枝
此时R2为0x1C
,vgvUy2vUDeDHBwvtzwn1CML0Eq==
字符串的长度
R0为0x10
,mtiZndu2nZG5ma==
字符串的长度
当两个字符串长度不相等时,跳转到输出Wrong Answer
,手动修改R0寄存器为0x1C
,跳过该判断
运行到第二个分枝,仍然手动跳过
运行到循环进行比较的位置,比较R1和R3处字符串是否相等
查看寄存器发现R1指向字符串mtiZndu2nZG5ma==
,但R3未指向另一字符串,猜测在手动跳转时可能导致寄存器改变错误
中断进程,对上述得到的疑似flagTencentGameSecurity
进行测试
Trun Answer
,说明确实是正确的flag
静态分析时得到一个使用base64解码乱码的字符串
1 |
|
猜测其编码码表被替换,对其重新解码
1 |
|
解码出来的字符串是vgvUy2vUDeDHBwvtzwn1CML0Eq==
,再解码一次就是flagTencentGameSecurity
upx -d脱壳
经过调试解压缩过程,发现该壳明显就是原版的upx,无法直接upx -d
说明修改了,特征字段,编译一个测试文件进行加壳对照分析
使用010editor打开CrackMe2
和对照文件,两个文件都为3个节区,理论上每个节区都存在UPX!
关键字,搜索关键字UPX!
发现CrackMe2
只搜索到3个UPX!
关键字,而对照文件有4个,对照文件中4个UPX!
第一个分布在文件开始位置不远处,第二个位于中间位置,后两个位于文件末尾,并且后两个距离很近,而CrackMe2
末尾处只有两个UPX!
关键字
定位到文件末尾
将CrackMe2
对应位置处修改为UPX!
保存文件使用upx -d
脱壳
脱壳成功,使用ida打开脱壳文件分析
程序的函数以及代码都能够正常分析,但是字符串未正常解析,无法快速定位,根据之前的分析可以知道字符串是在程序运行时解码生成的
尝试运行脱壳后的文件
能够正常运行,输入flagTencentGameSecurity
后能够正确判断