MASTG-TOOL-0001: Android 版 Frida
Frida支持通过Java API与Android Java运行时进行交互。您将能够hook和调用进程及其原生库中的Java和native函数。您的JavaScript代码段可以完全访问内存,例如,读取和/或写入任何结构化数据。
以下是Frida API提供的一些任务,这些任务与Android相关或专有
- 实例化Java对象并调用静态和非静态类方法(Java API)。
- 替换Java方法实现(Java API)。
- 通过扫描Java堆枚举特定类的实时实例(Java API)。
- 扫描进程内存中出现的字符串(Memory API)。
- 拦截本机函数调用,以便在函数入口和出口处运行您自己的代码(Interceptor API)。
请记住,在Android上,您还可以从安装Frida时提供的内置工具中受益,包括Frida CLI (frida
)、frida-ps
、frida-ls-devices
和frida-trace
等等。
Frida经常与Xposed进行比较,但是这种比较远非公平,因为这两个框架的设计目标不同。理解这一点很重要,因为作为应用安全测试人员,您可以知道在什么情况下使用哪个框架
- Frida是独立的,您只需要从目标Android设备上的已知位置运行frida-server二进制文件(请参阅下面的“安装Frida”)。这意味着,与Xposed相比,它没有深度安装在目标操作系统中。
- 逆向一个应用程序是一个迭代的过程。作为前一点的结果,您在测试时获得了更短的反馈循环,因为您不需要(软)重启来应用或简单地更新您的hook。因此,在实现更永久的hook时,您可能更喜欢使用Xposed。
- 您可以在进程运行期间的任何时候动态注入和更新您的Frida JavaScript代码(类似于iOS上的Cycript)。这样,您可以通过让Frida生成您的应用程序来执行所谓的早期检测,或者您可以选择附加到您可能已置于某种状态的正在运行的应用程序。
- Frida能够处理Java和native代码(JNI),允许您修改它们。不幸的是,这是Xposed的一个限制,它缺乏对native代码的支持。
请注意,截至2019年初,Xposed尚不能在Android 9(API级别28)上运行。
在Android上安装Frida¶
为了在您的Android设备上设置Frida
- 如果您的设备没有root,您也可以使用Frida,请参考 在非root设备上进行动态分析。
- 如果您有一个root的设备,只需按照官方说明操作,或按照下面的提示操作。
除非另有说明,否则我们假设这里有一个root的设备。从Frida发布页面下载frida-server二进制文件。确保您为您的Android设备或模拟器的架构下载正确的frida-server二进制文件:x86、x86_64、arm或arm64。确保服务器版本(至少主要版本号)与您本地Frida安装的版本匹配。PyPI通常安装最新版本的Frida。如果您不确定安装了哪个版本,您可以使用Frida命令行工具进行检查
frida --version
或者您可以运行以下命令来自动检测Frida版本并下载正确的frida-server二进制文件
wget https://github.com/frida/frida/releases/download/$(frida --version)/frida-server-$(frida --version)-android-arm.xz
将frida-server复制到设备并运行它
adb push frida-server /data/local/tmp/
adb shell "chmod 755 /data/local/tmp/frida-server"
adb shell "su -c /data/local/tmp/frida-server &"
在Android上使用Frida¶
随着frida-server的运行,您现在应该能够使用以下命令获取正在运行的进程的列表(使用-U
选项指示Frida使用连接的USB设备或模拟器)
$ frida-ps -U
PID Name
----- --------------------------------------------------------------
276 adbd
956 android.process.media
198 bridgemgrd
30692 com.android.chrome
30774 com.android.chrome:privileged_process0
30747 com.android.chrome:sandboxed
30834 com.android.chrome:sandboxed
3059 com.android.nfc
1526 com.android.phone
17104 com.android.settings
1302 com.android.systemui
(...)
或者使用-Uai
标志组合限制列表以获取当前安装在连接的USB设备(-U
)上的所有应用程序(-a
)(-i
)
$ frida-ps -Uai
PID Name Identifier
----- ---------------------------------------- ------------------------------
766 Android System android
30692 Chrome com.android.chrome
3520 Contacts Storage com.android.providers.contacts
- Uncrackable1 sg.vantagepoint.uncrackable1
- drozer Agent com.mwr.dz
这将显示所有应用程序的名称和标识符,如果它们当前正在运行,它还将显示它们的PID。在列表中搜索您的应用程序并记下PID或其名称/标识符。从现在开始,您将通过使用其中一个来引用您的应用程序。建议使用标识符,因为PID在每次运行应用程序时都会更改。例如,让我们以com.android.chrome
为例。您现在可以在所有Frida工具上使用此字符串,例如,在Frida CLI、frida-trace或Python脚本中。
使用frida-trace追踪Native库¶
要追踪特定的(低级)库调用,您可以使用frida-trace
命令行工具
frida-trace -U com.android.chrome -i "open"
这会在__handlers__/libc.so/open.js
中生成一个小的JavaScript,Frida将其注入到进程中。该脚本跟踪libc.so
中对open
函数的所有调用。您可以根据您的需求使用Frida JavaScript API修改生成的脚本。
不幸的是,目前尚不支持跟踪Java类的高级方法(但将来可能会支持)。
Frida CLI和Java API¶
使用Frida CLI工具(frida
)以交互方式使用Frida。它hook到一个进程,并为您提供一个Frida API的命令行界面。
frida -U com.android.chrome
使用-l
选项,您还可以使用Frida CLI加载脚本,例如,加载myscript.js
frida -U -l myscript.js com.android.chrome
Frida还提供了一个Java API,这对于处理Android应用程序特别有用。它允许您直接使用Java类和对象。这是一个覆盖Activity类的onResume
函数的脚本
Java.perform(function () {
var Activity = Java.use("android.app.Activity");
Activity.onResume.implementation = function () {
console.log("[*] onResume() got called!");
this.onResume();
};
});
上面的脚本调用Java.perform
以确保您的代码在Java VM的上下文中执行。它通过Java.use
实例化android.app.Activity
类的包装器,并覆盖onResume
函数。新的onResume
函数实现在控制台上打印一个通知,并通过在应用程序中每次恢复活动时调用this.onResume
来调用原始的onResume
方法。
jadx可以通过其图形代码浏览器生成Frida代码段。要使用此功能,请使用jadx-gui
打开APK或DEX,浏览到目标方法,右键单击方法名称,然后选择“Copy as frida snippet (f)”。例如,使用MASTG Android UnCrackable L1
上述步骤将以下输出放在剪贴板中,然后您可以将其粘贴到JavaScript文件中并将其馈送到frida -U -l
。
let a = Java.use("sg.vantagepoint.a.a");
a["a"].implementation = function (bArr, bArr2) {
console.log('a is called' + ', ' + 'bArr: ' + bArr + ', ' + 'bArr2: ' + bArr2);
let ret = this.a(bArr, bArr2);
console.log('a ret value is ' + ret);
return ret;
};
上面的代码hook了sg.vantagepoint.a.a
类中的a
方法,并记录其输入参数和返回值。
Frida还允许您搜索和处理堆上的实例化对象。以下脚本搜索android.view.View
对象的实例并调用它们的toString
方法。结果将打印到控制台
setImmediate(function() {
console.log("[*] Starting script");
Java.perform(function () {
Java.choose("android.view.View", {
"onMatch":function(instance){
console.log("[*] Instance found: " + instance.toString());
},
"onComplete":function() {
console.log("[*] Finished heap search")
}
});
});
});
输出如下所示
[*] Starting script
[*] Instance found: android.view.View{7ccea78 G.ED..... ......ID 0,0-0,0 #7f0c01fc app:id/action_bar_black_background}
[*] Instance found: android.view.View{2809551 V.ED..... ........ 0,1731-0,1731 #7f0c01ff app:id/menu_anchor_stub}
[*] Instance found: android.view.View{be471b6 G.ED..... ......I. 0,0-0,0 #7f0c01f5 app:id/location_bar_verbose_status_separator}
[*] Instance found: android.view.View{3ae0eb7 V.ED..... ........ 0,0-1080,63 #102002f android:id/statusBarBackground}
[*] Finished heap search
您还可以使用Java的反射功能。要列出android.view.View
类的公共方法,您可以在Frida中为此类创建一个包装器,并从包装器的class
属性中调用getMethods
Java.perform(function () {
var view = Java.use("android.view.View");
var methods = view.class.getMethods();
for(var i = 0; i < methods.length; i++) {
console.log(methods[i].toString());
}
});
这将在终端上打印一个非常长的方法列表
public boolean android.view.View.canResolveLayoutDirection()
public boolean android.view.View.canResolveTextAlignment()
public boolean android.view.View.canResolveTextDirection()
public boolean android.view.View.canScrollHorizontally(int)
public boolean android.view.View.canScrollVertically(int)
public final void android.view.View.cancelDragAndDrop()
public void android.view.View.cancelLongPress()
public final void android.view.View.cancelPendingInputEvents()
...