
HexTree.io
HexTree 与 谷歌合作推出的安卓 APP 安全课程,希望让更多安全研究者来挖安卓 APP 的漏洞,拿来学一学安卓吧。要开始这一系列教程,首先要有 Android Studio,从这里下载:
https://developer.android.com/studio

https://app.hextree.io/map/android

新建一个 Empty Views Activity 项目

名字随意起,创建好项目后找到右侧的 Device Manager 添加一个安卓虚拟机,用来跑我们的程序,点击加号按钮可以添加虚拟机

可以选择不同的安卓版本,随便选一个直接下一步即可

你的所有安卓虚拟机都在 Device Manager 中,点击启动按钮可以运行虚拟机,在上面的下拉菜单中可以选择使用哪个虚拟机运行 APP,选择好后点击旁边的启动按钮就会自动编译 APP 并在虚拟机上运行了

当然因为我们创建的是一个空的 APP 所以打开之后是默认的 Hello World 字样

来到代码部分,接下来尝试修改一下 APP 显示的内容,首先来观察一下代码,MainActivity.java 内容如下:
package com.example.myapplication1;
import android.os.Bundle;
import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
publicclass MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_main);
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
return insets;
});
}
}
Android 应用程序的入口通常是由系统启动的一个 Activity 类,如 MainActivity;onCreate() 方法在 Activity 第一次被创建时由系统调用
setContentView(R.layout.activity_main); 设置 Activity 的内容视图为 activity_main.xml 布局文件,这个文件在 res 文件夹的 layout 目录下,定义了用户界面的结构与组件,可以通过修改这个文件修改用户界面

想要操作布局中的一个文本,可以先给他指定一个 id,然后在代码中调用这个 id 显示变量等信息

TextView homeText = findViewById(R.id.my_text);
homeText.setText("Hello Yichen!");
在实际攻击中可能会需要一个按钮,当点击按钮时发起攻击,如果想添加按钮,直接在 activity_main.xml 选中 Button 往下拖到组件树中即可

修改右侧的约束宽度,直接拖拽到某个你认为合适的位置

可以为按钮添加点击监听 setOnClickListener ,当点击时进行一些操作,例如在 logcat 中输出字符串
Button homeButton = findViewById(R.id.my_button);
homeButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.i("HEXTREE", "Welcome from the Button");
}
});
还可以添加一个变量,例如用来记录点击次数,当变量达到一个值时,做一些操作:代码中定义了一个 Intent,intent 是意图的意思,这段代码表示当 click 点击次数到 10 次后,打开一个链接,这时候安卓系统会找到可以满足我们这个意图的 APP 来打开这个链接
Button homeButton = findViewById(R.id.my_button);
homeButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.i("HEXTREE", "Welcome from the Button");
clicknum++;
homeText.setText(String.format("You clicked %d", clicknum));
if(clicknum==10){
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://www.yuque.com/hxfqg9"));
startActivity(browserIntent);
}
}
});
但是安卓系统怎么知道哪个应用可以用来打开什么呢?原因就是在每个应用的 AndroidManifest.xml 中有一个叫 intent-filter 的标签定义,例如 Chrome APP 的 AndroidManifest.xml 中可以找到当想要访问 https 时,会打开:
org.chromium.chrome.browser.document.ChromeLauncherActivity

如何自己编写一个 APP 实现类似上面的效果呢?首先创建一个新的 activity,我起名为 Security

在 AndroidManifest.xml 设置其 exported 为 true,这样才能让别的 APP 访问到,然后加上 intent-fliter 用来标识可以接收哪些意图,此处我们为 Security 这个 Activity 添加了一个 intent-filter,使其能够接收 SEND 类型的 Intent,当在安卓系统中使用“发送到”这个功能时就可以看到我们的 APP 啦
<activity
android:name=".Security"
android:exported="true" >
<intent-filter>
<action android:name="android.intent.action.SEND" />
<data android:mimeType="text/plain"/>
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
在 Security 对应的布局文件 activity_security.xml 中,添加一个文本框,设置其 id 为 debug_text 并调整位置,在 Securtiy.java 的代码中使用 getIntent 函数获取数据,然后提取出来具体的 Intent 数据,将其设置为文本框中的内容
Intent receivedIntent = getIntent();
String sharedText = receivedIntent.getStringExtra(Intent.EXTRA_TEXT);
if(sharedText!=null){
TextView debug_text = findViewById(R.id.debug_text);
debug_text.setText("Shared: "+sharedText);
}
可以看到 share 过来的数据

使用 Build -> Build AppBundle(s) / APK(s) -> Build APK(s) 可以编译为一个 apk 文件,编译好的 app 可以在 output 文件夹找到


通过 jadx 这个开源工具可以很方便的将 APP 反编译,下载完后将压缩包解压,应该会得到一个 jadx-gui.exe 双击打开,将我们自己写的 app 拖进去,会进行自动的反编译,然后你就可以通过左侧的目录看到所有的信息了,比如打开 Security 这个 activity 可以看到没有经过混淆/加壳的 APP 跟看源码没啥区别

在 Android Studio 中也可以给某一行下断点,然后点击菜单栏的 debug 按钮即可进行调试

当运行到这一行时就会断住,然后你就可以找到这个变量,右键 -> Set Value 修改变量的值

hextreeio 在 github 上分享了一个 app 源码,下载下来后使用 Android Studio 打开

阅读代码可知,在 MainActivity 中点击按钮超过 9999 次就会调用 ChallengeActivity,因此可以通过下断点,修改点击数量跳过去,来到 ChallengeActivity 后会发现有很多按钮,通过阅读源码可知,只有点击 button9 会打开 FlagActivity 其他的都会退回到 MainActivity,因此点击 button9

在 FlagActivity 阅读源码得知,需要调节滚动条到 42 的位置,然后就可以获得 flag 啦

当然这只是常规思路,源码都在你手上,你直接调用函数把 flag 打印出来就好了呀,在 FlagActivity 中找到解密 flag 相关的代码复制到 MainActivity,直接通过 text.setText(decryptFlag()); 即可将解密结果显示在界面上
