BUUCTF Android逆向合集

记录一下在BUUCTF Mobile方向中做过的题。

1. 相册(四大组件)

Android四大组件之一——Service

启动M2服务,注册内容。读取短信收件箱中以消息序号降序的第一条消息序号(猜测是最新的)、手机号、短信内容,并且监听短信数据库变化。

Content Provider

短信操作

BroadCast Receiver详解

AlarmManager的setRepeating的用法-闹钟/定时器

android 4.4 设置默认短信 和来电短信拒接

设置本应用为默认短信应用,向通讯录里的所有号码发送钓鱼短信。

用ContentProvider获取通讯录联系人

TelephonyManager

向某个号码(病毒开发者)发送本机设备id短信,表示这台手机已安装了本应用。

将本机的所有短信记录发送到某个邮箱当中。

回到C1.class看MailTask,将本机的所有联系人备注及手机号发送到某个邮箱中。

继续看check(),获取设备管理权限,加载WebView页面。

Android设备管理器DevicePolicyManager的使用和理解

但这个WebView页面现在已经无了。分析到这里,发现flag就是病毒开发者邮箱。

2. PixelShooter

这个是Assembly-CSharp.dll类的unity3D游戏。将APK直接用jeb打开,在Assembly-CSharp.dll中搜索关键字符串即可。

具体如何分析一个unity3D游戏再说吧…

3. Strange apk(加固APK的执行流程)

查看sctf.demo.myapplication.c发现继承了Application类,并重写了Application.attachBaseContext()Application.onCreate()方法。

Android app的启动流程

对于整体加密壳,它在运行时解密,在Application.attchBaseContext()Application.onCreate()中必须要完成对加密Dex的动态加载和解密,得到源Dex,才能保证源APK的正常运行。

将解密函数拷贝下来,运行一下得到解密Dex。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

public class datong{
public static byte[] __(String fileName) throws IOException {
InputStream in = new FileInputStream(fileName);
int lenght = in.available();
byte[] buffer = new byte[lenght];
in.read(buffer);
in.close();
return buffer;
}

private static byte[] _0_(byte[] srcdata) {
for (int i = 0; i < srcdata.length; i++) {
srcdata[i] = (byte) ("syclover".charAt(i % "syclover".length()) ^ srcdata[i]); // 对data进行解密操作
}

return srcdata;
}

private static void _(byte[] apkdata) throws IOException {
byte[] apkdata2 = _0_(apkdata); // 解密Data
File file = new File("srcdata.apk");
try {
FileOutputStream localFileOutputStream = new FileOutputStream(file);
localFileOutputStream.write(apkdata2);
localFileOutputStream.close();
} catch (IOException localIOException) {
throw new RuntimeException(localIOException);
}
}

public static void main(String[] args) throws IOException{
byte[] bytes = __("data");
_(bytes);
}
}

分析脱壳后的APK:

已知flag一部分为sctf{W3lc0me,剩下那部分与key进行加密。根据encode()函数编写decode()函数。

所以完整flag为sctf{W3lc0me~t0_An4r0id-w0rld}

4. app-debug(TEA加密)

java层,简单。

So,在.init_array()中做了TracerPid反调试。

再看关键的check()函数:

看wp说这是TEA加密,属于分组加密算法。想要逆向,就必须要知道其DELTA值与key,以及加密后的密文。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
from ctypes import * 
def encrypt(v,k):
v0=c_uint32(v[0])
v1=c_uint32(v[1])
sum1=c_uint32(0)
#delta=0x9e3779b9
delta = 0x458BCD42
for i in range(32):
sum1.value+=delta
v0.value+=((v1.value<<4)+k[0])^(v1.value+sum1.value)^((v1.value>>5)+k[1])
v1.value+=((v0.value<<4)+k[2])^(v0.value+sum1.value)^((v0.value>>5)+k[3])
return v0.value,v1.value

def decrypt(v,k):
v0=c_uint32(v[0])
v1=c_uint32(v[1])
#delta=0x9e3779b9
delta = 0x458BCD42
sum1=c_uint32(delta*32)
for i in range(32):
v1.value-=((v0.value<<4)+k[2])^(v0.value+sum1.value)^((v0.value>>5)+k[3])
v0.value-=((v1.value<<4)+k[0])^(v1.value+sum1.value)^((v1.value>>5)+k[1])
sum1.value-=delta
return v0.value,v1.value

if __name__=='__main__':
a=[0xF5A98FF3,0xA21873A3]
k=[9,3,2,1]
print("加密前数据:",a)
res=encrypt(a,k)
print("加密后的数据:",res)
res=decrypt(res,k)
print("解密后数据:",res)

发现并不是可输入字符串,说明在解密过程中出错了。由于delta值固定,所以猜测是key的问题。查看交叉调用,发现在.init_array()中不仅做了反调试操作,还改变了key值。

又由于每个数都是小端存储,所以正确input应该是GKcTFg0,回到Java层看到提示,对其进行MD5加密为77bca47fe645ca1bd1ac93733171c9c4

1
flag{77bca47fe645ca1bd1ac93733171c9c4}

5. [FlareOn2]Android

查看Native层函数,如果v7能整除v14,则v16[j]++。

sorry看不懂。

6. [WMCTF2020]easy_apk

查看Java层:

Android 关于ApplicationInfo flags

查看So层,.init_array()做了一个Frida检测:

Native层的stringFromJNI()函数的作用是设置字符串“check”。进入JNI_OnLoad()

我们发现Class类名被加密了,查看String窗口其实很多字符串都被加密了,都是用这个函数加密。由于本人实在看不懂,这道题GG