一个旧的Android CTF,主要分析一下反解算法是怎么写的
1. 用jadx查看入口点
看这段代码我们需要关注两个方法:
- getSecret()是一个native层的函数,这个函数对编辑框的内容进行了一定的处理之后返回了ans
- check()方法校验,通过校验才能得到正确的Flag
先分析下chek()是怎么实现的,等下再去看native层。
2. 查看check()方法
这段代码的主要作用是从数据库中取出secret值(用hello的MD5值作为name对应的secret值)与我们输入的值(经过getSecret()方法处理)进行比较相等就返回true。
分析完check()方法之后,现在我们可知:
- 输入编辑框的值经过getSecret()的运算之后 = ‘kEvKc|roAkNADgGExUeq’就可以通过校验
那接下来我们去分析一下getSecret()这个方法。
3. 分析getSecret()
用ida打开so文件之后,通过命名惯例用java定位到函数位置,再按F5修改以下参数类型方便分析
先初步分析这段代码来猜测里面的逻辑(因为不知道参数的内容,所以猜测不一定正确)我们依旧可以知道几点
- 代码通过接收我们输入的字符串,将其和内置的base_string进行一些运算得到一个新的字符串,最后将新的字符串返回
- 因为最后返回的新字符串是动态生成的,所以我们无法通过简单的hook去获取到正确的flag
- 这样子的话我们只能尝试一下看,能不能通过分析它的算法和入参写出它的反解算法
- 我们要详细分析算法写反解脚本的话,需要通过动态调试的方法来确定它的入参和返回值,另外静态反编译出来的伪代码可能也不准确不能支持我们去精确的写出反解算法
4. 用ida动态附加调试:attach过程略过
4.1. attach程序之后,找到我们的getSecret()方法下断点段下来
在函数开始的地方下断点,在手机上点击_TRY IT!_按钮之后我们可以中断在这里,这时候我们可以看到它的base_string字符串
_F8_继续往下跟,到获取dest[]的地方
分析后可以发现数组的内容是从0~0x44的dw数组。
4.2. 接下来开始分析for循环
- 第一个for循环
- 循环的次数根据组的长度获得
- 循环取出dest[i]中的值 + 0x10,然后再写回去
- 第二个嵌套for循环
- 外层循环的次数是我们输入的字符串的长度
- 内存循环的次数是base_string字符串的长度
- 这个嵌套循环是将我们输入的字符逐个和base_string中的字符对比,相等的话就跳出本轮内层循环
- 用循环次数去dest[]中取出对应的值->再用这个取出的值去base_string中取出对应下标的字符->然后用这个字符替换掉我们输入的inputStr[外层循环]的字符
最后一个循环
- 最后一个循环时将我们前面循环生成的新字符串再次循环进行^操作,然后放回原位、
5. 然后这个加密我们就全部分析完成了,修改一下我们前面猜错的伪代码注释
算法分析完成
接下来我们就可以写这个算法的反解脚本了,写反解脚本可以从最后一个循环往前推过去。
结果字符串循环对字符进行与运算,然后再取模128确保结果不大于ASCII码的最大值->然后将计算完成的字符逐个在base_string里面找到字符对应的索引值(即dest[k]的值)->如果这个索引值小于16(dest[]中的最小值0x10)表示它对应的索引值在base_string的前16位->就把下标值加上base_string的长度->最后打印字符的时候还要把索引值([dest[i]])减去0x10
1
2
3
4
5
6
7
8
|
static_str
=
'0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!{|}~'
now
=
'kEvKc|roAkNADgGExUeq'
for
i, each
in
enumerate
(now):
tmp
=
chr
((
ord
(each)^i)
%
128
)
index
=
static_str.find(tmp)
if
index <
16
:
index
=
index
+
len
(static_str)
print
(static_str[index
-
16
], end
=
'')
|
over~
更多【CTF简单算法反解】相关视频教程:www.yxfzedu.com