【CTF&WriteUP&Re】2022强网杯 ”Reverse“ WriteUp
2022强网杯 ”Reverse“ WP
Reverse
1.【game】
考点
- Android逆向分析, Android网络请求,Web数据
解法
-
下载程序解压,发现是"ab"结尾的文件推测是安卓备份文件,用010Editor打开看一下,发现有明显的标识 ”ANDROID BACKUP“ 。
-
直接用android-backup-extractor提取出数据
-
提取之后发现有两个文件其中一个是apk文件直接用Jeb打开apk安装包
-
打开Manifest清单文件找到启动Act直接双击,tab键转成Java代码
-
根据Android生命周期可以知道onCreate是第一个启动的函数,直接找onCreate函数
-
经过分析发现在App启动的时候会自动登录,如果登陆失败则会跳转到LoginAct,否则跳转到MainAct
-
本想跟进autoLogin查看是如何登录的,结果发现了关键字眼函数”getFlag“
-
那就先分析getFlag,发现需要传入三个参数然后通过发包获取Flag。根据后面的Api.class发现远程文件名
-
文件名搞到手了,剩下的是远程服务器地址了。直接暴力搜索http,找到了远程地址
-
根据
getFlag
可知,需要获取Flag就需要传入三个参数,分别是:- code
- account
- username
-
尝试随便填看看服务器返回的结果发现不允许。
-
发现App可以注册,直接注册一个账号再尝试发现不行
-
猜测可能需要用到管理员的账号才可以,尝试获取管理员账号。打开App发现有个排行榜。有个名为 “admin” 的账号积分刚好是9999。根据Game页面中的提示 ”获得超过9999分能获得Flag哦!“
-
推测应该是管理员用户名,但是getFlag需要三个参数。剩下两个参数。从刚刚的Api中可以找到一个
ScoreBoard.php
的请求文件并且还是GET方法。直接用Postman请求查看是否有数据在排行榜上没显示出来。 -
发现有好几个admin, 根据排行榜显示的数据。找到排行榜best(最佳)是9999的,发现其code是”123125“
-
第二个参数到手,第三个参数一样的方法去其他Api中看看是否能获取到。经过分析发现有个
AddFriend
只需要传入一个参数code
即可。尝试用Postman对其发包。获取到两个参数分别是- account
- username
-
然而两个参数都加密了。
-
分析发现在登录和注册的时候实在本地进行了数据的加密。双击到
OooOoo0
-
发现有解密操作,但再跟进
OooooOOo
发现其是native函数。但不影响操作 -
直接把apk的dex2jar后需要的class保留其余全部删了。新建一个AndroidStudio工程直接把jar和so文件导入AS调用函数解密。
-
编译后发现无法解密
-
直接上动态调试,用工具把apk中Manifest文件中的debuggable改成true。
-
再用Apktiool对apk反汇编成smali
1 | java -jar apktool.jar d FilePath |
-
反编译后用AndroidStudio打开,定位到关键地方下断点
-
adb调试模式启动apk
-
AS对其附加
-
-
待断点断下后,把刚刚AddFriend返回的两个数据分别复制
-
按F2修改p0寄存器,按F9运行断在了String实例化后
-
根据smali指令
return-object v0
得知实例化后数据放在了v0寄存器。v0寄存器就是我们需要的数据直接复制备用
-
根据同样的方法得出username,其实不需要解密username因为刚刚的排行榜就已经显示了
-
经过一系列的操作后得到以下数据:
- code: 123125
- account: &Od987$2sPa?>l<k^j
- username: admin
-
直接发包到
GetFlag.php
得到Flag
2. 【GameMaster】
考点
- C#程序的逆向,PE文件结构,程序逻辑分析
解法
-
用IDA打开发现是.NET架构的程序,直接使用dnSpy打开程序。找到
Main
函数。发现读取了一个文件 -
读取文件字节流存放到了Program.memory全局变量里面
-
直接点击Program,使用搜索功能全词匹配模式搜索memory,发现有两个地方使用了该变量
-
将memory的数据异或解密存放回memory中
-
将数据进行解密放到Program.m中
-
将m中的数据反序列化加载
-
根据代码中的AchivePoint推断出执行顺序,直接上c#写代码对文件进行解密
1 | // See https://aka.ms/new-console-template for more information |
-
用010Editor查看一下文件发现有一些字符串像是可执行文件,尝试搜索MZ头看看是否是PE文件。发现确实是PE文件。把MZ头前面的数据删去保存
-
使用dnSpy打开刚刚的文件,发现在T1中存在Flag字眼。推测这里就是Flag
-
经过分析发现,首先是使用
Check1
对三个变量num,num2,num3进行编码加密写入到array2中,再用first的字节流和array2的字节流进行比对 -
那么这里可以直接用z3得出三个变量分别是:
- array[0] = 156324965
- array[1] = 868387187
- array[2] = 3131229747
1 | import z3 |
-
从代码中很明显发现把刚刚解密出来的三个变量经过ParseKey转换成密钥对数据进行解密
-
计算Key
1 | v = [156324965, 868387187, 3131229747] |
- 解密数据
1 |
|
3. 【find_basic】
考点
- 类似虚拟保护的switch混淆执行逻辑分析
解法
-
用IDA直接打开程序,来到start中跳转到main。
-
发现调用了很多子程序,随便进去一个发现使用了switch结构混淆了执行的过程
-
main函数领空下个断点不断F8直到程序打印出 “basic secret”
-
发现在调用了
sub_565D84FA
后程序进入了阻塞状态,因此判断该函数执行完就开始获取用户输入的数据。发现程序会发出一个时钟信号,直接忽略
-
随便输入数据之后,程序继续断下来。断下来后打开IDA的指令跟踪功能。
-
在下一个子程序后面下断点,直接F9运行到断点
-
将跟踪记录导出,对其进行溯源分析
-
发现使用了
_rand
产生了一个随机数后逻辑与0xFF,不断地比较。如果小于就自增一在继续判断,不断地循环直到和产生的随机数相等再推出循环。经过分析该循环体内没有需要的数据。直接修改 随机数产生后的与逻辑,patch与逻辑成异或清空数据后trace再次运行程序。 -
但发现这样的随机不止一个,直接定位到
rand
函数的领空 -
打开交叉引用
-
分别对两个地方的调用进行断点,把 "break"去掉勾选。“condition”中填入eax = 0。
-
这样做保证每次随机数产生后都被置0,记得要在函数调用后断点否则无效
-
分析函数,将用户输入的数据压入堆栈中
-
在数据的地方下个断点
-
分析整理,根据trace发现用sub指令和数据进行了比对
-
往后一样的操作分析,使用z3即可算出所求flag
1 | import z3 |