浅尝CTF安卓逆向
前言
偶然接触到的一道简单CTF安卓逆向入门的题,感觉挺有意思的,故此开始学一学,看一看记录一下
一、题目描述
给出了一个安卓的apk,模拟器打开是这样的,试了一下,大概可能就是叫输入一个flag值正确的话返回right,错误的话则是wrong
二、解题过程
虽然以前没做过安卓逆向的题,但是打ctf耳濡目染也知道,逆向嘛,我要分析代码!先用jadx打开看看怎么个事儿。我们可以看到有这么多复杂的东西,不要紧张,这是简单的题目,我们找到函数的入口。即MainActivity中onCreate方法下的onClick点击事件。
来出来我们读一读
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() { // from class: com.test.easyapk1.MainActivity.1
@Override // android.view.View.OnClickListener
public void onClick(View v) {
String input = ((EditText) MainActivity.this.findViewById(R.id.editText)).getText().toString();
if (input.startsWith("flag{") && input.endsWith("}")) {
String input2 = input.substring(5, input.length() - 1);
String[] input_split = input2.split("_");
if (input_split.length == 4 && input2.length() == 26) {
long p1 = Long.parseLong(MainActivity.hexlong(input_split[0]), 16);
long p2 = Long.parseLong(MainActivity.hexlong(input_split[1]), 16);
long p3 = Long.parseLong(MainActivity.hexlong(input_split[2]), 16);
if ((p1 + p2) - p3 == 141187687 && p1 * p2 == 892043278776532616L && p3 - p2 == 956566027 && MainActivity.this.check2(input_split[3]) == 1) {
Toast.makeText(MainActivity.this, "right", 1).show();
return;
}
}
}
Toast.makeText(MainActivity.this, "wrong!", 1).show();
}
});
}
简单读下代码
- 获取输入:
获取 MainActivity 对象,并从中获取 EditText 控件的文本内容。
检查文本是否以 “flag{” 开头,并以 “}” 结束。 - 分割输入:
如果输入符合条件,则去除开头和结尾的标记,并按 _ 分割成数组 input_split。 - 验证长度:
检查分割后的数组长度是否为 4,并且整个字符串长度为 26。 - 数值验证:
将分割后的字符串转换为长整型数值 p1, p2, 和 p3。
o 检查数值是否满足以下条件:
(p1 + p2) - p3 == 141187687
p1 * p2 == 892043278776532616L
p3 - p2 == 956566027 - 本地方法验证:
调用本地方法 check2 对输入的第四部分进行验证。 - 显示结果:
如果所有条件都满足,则显示 “right” 的提示。
否则,显示 “wrong!” 的提示
那不就有思路了,就是解密四个数组,前三个的关系已经给我们了,是比较简单的数字关系。
直接写个python脚本轻松拿捏
a = 141187687
b = 892043278776532616
c = 956566027
p1 = a+c
p2 = b//p1
p3 = c+p2
print(long_to_bytes(p1))
print(long_to_bytes(p2))
print(long_to_bytes(p3))
得到前三个数组如下:
现在就是这第四个数组了,因为我之前是真没接触过,所以这里卡了很久,查资料才搞清楚原来本地方法check2原来是要去so文件里面找,先把apk文件后缀改为zip解压打开找到这个文件。
idea打开这个文件,我们找check2方法
不出所料应该就是这个Java_com_test_easyapk1_MainActivity_check2
,又读代码呗没啥好说的
我们主要找check2的判断逻辑,该函数通过 JNI 获取 Java 字符串的内容,并将其传递给 sub_E9F0
函数进行处理。然后,该函数比较处理后的字符串与目标字符串是否相同。他说传递给sub_E9F0
处理的我们就找E9F0
了。一阵好找,找到了。
继续我们来好好看看他是什么处理逻辑
我一看是一个类base64的加密方式,不过这里用的是特定的查找表 aZtsi01htnufVoe,(好家伙,自定表的base64嘛就是)接下来我们就需要找这个表和加密后的内容了,又是一阵好找。黄天不负有心人啊!最后找到了。
没啥好说的,下面都是easy的工作,CyberChef直接解一下
然后整合一下加上flag{},终于顺利拿下了说是
byte_strings = [b'Andr', b'0oid', b'isso', b'1nteResTing']
concatenated_bytes = b''.join(byte_strings)
final_string = concatenated_bytes.decode('utf-8')
flag_format = f"flag{{{final_string}}}"
print(flag_format)
总结
虽然做的过程小虐了我这个菜鸡一把,但是不得不说安卓逆向还是有趣哈。