在之前一篇文章中,我们介绍了如何在安卓app中添加C/C++代码。本文将以上文的app为示例,hook其中用C编写的Jniint函数。
这篇文章中有一些你可能感兴趣的Frida代码示例。
本文适合刚入门的新手,目的是介绍如何使用Frida hook方法,尤其是native方法。我们将详细介绍整个流程中的每个步骤。如果你想下载本文的APK示例,链接在此。
在本教程中,我们会处理Java方法和C函数。我不会刻意区分这些术语,因为它们意思是一样的。
第一步是检测APK是否包含共享库。简单的方法是解压APK。别忘了APK是实际app的打包,所以可以像解压压缩包一样提取APK内容。选择你常用的解压程序解压APK。
解压后的文件结构应该类似于:
lib文件夹下,我们看到了针对不同架构的库的编译版本。选择适配设备的,我们选的是x86。我们需要分析该共享对象内部的函数,命令为nm --demangle --dynamic libnative-lib.so。结果如下:
我们首先注意到了两个函数:Java_com_erev0s_jniapp_MainActivity_Jniint和Jniint。如果你想了解关于这两个函数的更多信息,请阅读另一篇文章的相关部分。我们要做的是改变APK的执行流程,让Jniint返回我们自定义的值。
方法有二:
一般来说,方法一比较容易,但是有时候方法二操作会更方便。这完全取决于app本身和你的目的。两种方法我们都会尝试,并且对其步骤做评价。
本教程中所使用的,以及我在进行常规安卓安全测试中所使用的,都是Genymotion。Genymotion是一个非常不错的模拟器,非常轻巧,也可集成到Android Studio中。在测试环境中,挑选的设备/模拟器一定要有root访问权限。其实root对于执行hook而言也不是必须的(有其他替代方法),但本文中的教程需要root权限。Genymotion默认情况下具有root访问权限。
此步骤非常简单,只需Python环境并且运行两个命令。第一个命令是pip install frida-tools,用以安装基本组件,第二个命令是pip install frida,用以安装一些在使用Frida过程中有用的Python bindings(Python bindings的作用是允许用Python调用其他语言的方法)。
首先下载最新版本的frida-server,链接在此。搜索frida-server并选择设备对应的安卓架构,Genymotion对应的是x86。下载完成后,解压并重命名一个好记的名字,比如frida-server。接下来将其传至设备上并运行。往设备上传文件需要adb。如果目录下没有adb,你需要下载adb。你可以在adb目录下运行,也可以将目录路径添加至环境变量。部分教程在此。
现在adb就绪,并且已经将设备连接至电脑,或者配置好了模拟器。执行命令adb push path/to/your/frida-server /tmp。该命令会将电脑上的frida-server文件传到设备的/tmp目录下。
最后,在设备上运行frida-server。步骤是:先打开adb shell,进入/tmp目录,然后执行chmod +x frida-server使其可执行,最后执行./frida-server,并且不要关闭命令行窗口。
验证正确配置和操作的方式是,打开另一个命令行窗口,执行frida-ps -U。如果看到结果是一个很长的进程列表,那就说明没问题,否则重新阅读本小节并严格按步骤执行。
如果你是按照上一篇文章的步骤执行的,别忘了安装从此处下载APK样本,或者自己编译APK。有多种安装APK的方法,其中之一是运行adb install nameOfApk.apk。
我们要hook的方法是Jniint,在此之前,请先在此查阅Frida的Java API,会对你理解后续的操作有帮助。
首先,点击app图标启动app。点击按钮后会弹出一个数字,如下所示:
我们接下来创建一个给Frida使用的JavaScript文件,来hook我们想hook的函数(Jniint)。JavaScript文件内容如下:
代码的意思直接明了,我们首先给MainActivity类创建了一个包装对象,然后替换MainActivity类中的Jniint。保存文件,命名为myhook.js。
在设备中打开app时,我们需要在JavaScript文件的目录下打开命令行终端。
命令是frida -U -l myhook.js com.erev0s.jniapp。
-U表示通过USB连接设备,-l表示使用JavaScript文件,最后是指定hook的app。注意此命令要求app已经在设备上运行,它不会自动启动。如果想自动启动app,则需使用命令frida -U -l hookNative.js -f com.erev0s.jniapp --no-pause。-f会启动指定的app,--no-pause会在app启动后执行主线程。
不论用何种方式,结果是一样的,你会看到类似如下的界面:
如你所见,按下按钮后,结果已更改为我们在JavaScript代码中定义的值。至此,我们已经使用Frida的Java API成功更改了Jniint的返回值。
在我们需要处理C/C++的函数并且仅更改返回结果无法满足需求的情况下,此方法特别有用。比如找出一个加密函数中的特殊参数(也许是密钥)。步骤与上文一致,唯一的变动是JavaScript文件myhook.js的内容有变化:
此处用的API介绍见该链接,它会在libnative-lib.so中自动搜寻Jniint函数,libnative-lib.so即为我们之前解压APK时看到的库文件的名字。调用retval.replace(0)以替换返回值。
以与之前相同的方式执行Frida时,会看到报错!不用担心,这是预期内的,因为我们正在尝试hook Jniint,但是由于还未按下按钮,该函数尚未被加载!因此,Frida告诉我们找不到"Jniint"。按下按钮,调用Jniint,然后就可以了。我们唯一要做的就是再次保存我们的JavaScript文件,Frida将自动重新加载之,现在如果再次按下按钮,我们会得到以下信息:
在本文中,我们详细介绍了两种hook native方法并更改其返回值的方法,并介绍了如何配置环境以及安装所有必需的工具。如果你想深入研究Frida,请查阅官方API文档,因为你会找到几乎所有你所需的内容。建议在一开始练习的时候节奏不要太快,因为可能会遇到一些复杂情况。希望你喜欢这篇文章,并能从中学到一些东西。如有任何问题或评论,请随时与我联系,我将尽力回答。
原文链接:https://erev0s.com/blog/how-hook-android-native-methods-frida-noob-friendly/
[注意]APP应用上架合规检测服务,协助应用顺利上架!