How to hook Android Native methods with Frida (Noob Friendly)
2020-05-15 09:02:09 Author: xz.aliyun.com(查看原文) 阅读量:568 收藏

原文地址:https://erev0s.com/blog/how-hook-android-native-methods-frida-noob-friendly/

上一篇文章中,我们以Android应用程序为例,并假设我们想要使用C/C++替换它的部分实现。在本文中,我们将使用该应用程序,并且尝试hook我们用C语言编写的Jniint函数。

如果你正在寻找frida的代码片段,那么你可能会对这篇文章感兴趣!

本篇教程是面向新手的,目的是向大家介绍使用Frida来hook方法,尤其是hook native方法。我们将从只有apk开始介绍,一步步介绍整个过程。如果想下载我们上一篇文章中编写的应用程序apk,请点击此链接
在本教程中,我们要处理Java方法和C函数,我将交换使用这些术语,因为它们基本上意味着同一件事!

首先,观察apk是否有正在加载的共享库。一个简单的验证方法是提取apk的内容。不要忘记apk只是实际应用程序的打包,因此可以提取apk内容,就像解压压缩文件一样。选择你喜欢的解压程序并解压apk。

提取后的apk结构应该类似于下面

├── AndroidManifest.xml
├── classes2.dex
├── classes.dex
├── lib
│   ├── arm64-v8a
│   │   └── libnative-lib.so
│   ├── armeabi-v7a
│   │   └── libnative-lib.so
│   ├── x86
│   │   └── libnative-lib.so
│   └── x86_64
│       └── libnative-lib.so
├── [...more here...]

lib目录中,我们可以看到不同架构的库的编译版本。选择适合我们的设备的那个,在我的例子里是x86。我们需要分析这个共享库来查看其包含的函数。我们只需要使用如下命令 nm --demangle --dynamic libnative-lib.so。结果如下所示:

$ nm --demangle --dynamic libnative-lib.so 
00002000 A __bss_start
         U __cxa_atexit
         U __cxa_finalize
00002000 A _edata
00002000 A _end
00000630 T Java_com_erev0s_jniapp_MainActivity_Jniint
000005d0 T Jniint
         U rand
         U srand
         U __stack_chk_fail
         U time

首先引起注意的是这两个函数的存在,即Java_com_erev0s_jniapp_MainActivity_Jniint和Jniint。如果你想知道为什么这里有两个函数,可以在我们以前的文章中查看这部分。 我们的目标是改变apk的执行流程,以使Jniint返回我们定义的值。

有两种方法可以做到这一点:

  1. 我们在Java层hook,意味着我们拦截了Java对JNI的调用,因此根本不需要处理C代码。
  2. 我们深入C语言中Jniint的实现,并在那里进行调整。

通常第一个方法比较容易实现,但是有时候第二个操作也很方便。这完全取决于应用程序在做什么以及你要实现的目标。我们将同时尝试这两种方法,并对步骤进行说明。

Setting up our testing Environment

在本教程和我对Android的常规安全测试中,我喜欢使用Genymotion。这是一个非常不错的模拟器,非常轻巧,还可以将其集成到Android Studio中。在选择设备/模拟器作为测试环境中很重要的一点是拥有root访问权限。好吧,root权限不是hook所必须的(有其他替代方法),只不过本文中我们使用的是这种方式。如果使用Genymotion,那么默认情况下模拟器中具有root访问权限。

Installing Frida on your computer

这个步骤非常简单,只需要安装Python并运行两个命令。第一个是pip install frida-tools,用于安装我们要使用的基本工具,第二个是pip install frida,它将安装python bindings,在使用Frida的过程中你会发现他们很有用。

Run the frida-server on the device

首先,下载frida-server的最新版本,可以在这里找到。只需找到frida-server,选择设备的android架构即可。Genymotion要下载x86的。下载后,只需解压并将输出重命名为容易记住的名称,例如frida-server。现在,我们要做的就是将文件推送到设备中并运行。为了将文件移动到设备中,我们需要adb的帮助。如果路径中没有adb,可以在这里下载然后提取它,然后要么在提取的文件夹中运行,这样可以直接访问adb,要么将其添加到路径中。这里可以找到有关如何操作的说明。

我们假设你拥有可运行的adb,并且已将设备连接到笔记本电脑或启动了模拟器。接下来需要运行命令adb push path/to/your/frida-server/tmp,将frida-server文件从计算机移至设备的/tmp路径中。

最后一步,在设备内运行frida-server。为此,我们运行adb shell以获取设备中的shell,然后切换至/tmp。然后使用chmod +x frida-server命令使文件可执行,最后执行./frida-server。一定要让终端保持打开。

为了验证一切正常,需要打开另一个终端并输入frida-ps -U。如果得到一长串的进程,那么一切正常可以继续进行,否则需要再次阅读本节并严格按照步骤进行操作。

另外,如果你是遵循上一篇文章的说明,也不要忘记将已下载或构建的apk安装到设备上。有几种方法可以完成此操作,其中一种方法是使用adb, 运行adb install nameOfApk.apk

Hooking with Frida using the Java api

我们想要hook的是Jniint,那么现在看看要如何操作。请在这里也看下Frida的Java API,因为我们将会用到它,它可以帮助你更好地理解我们在做什么。

首先,通过点击应用程序图标在设备内部启动应用程序。如果点击按钮,应该会弹出不同的数字,类似于这里所显示的。

我们现在要创建一个javascript文件,Frida将用它来hook我们想要的函数(Jniint)。文件内容如下:

Java.perform(function () {
  // we create a javascript wrapper for MainActivity
  var Activity = Java.use('com.erev0s.jniapp.MainActivity');
  // replace the Jniint implementation
  Activity.Jniint.implementation = function () {
    // console.log is used to report information back to us
    console.log("Inside Jniint now...");
    // return this number of our choice
    return 80085
  };
});

这段代码相对容易理解,首先为MainActivity类创建一个封装,然后替换Jniint实现,它是MainActivity类中的一个方法。然后将文件保存为myhook.js。在设备中打开应用程序时,我们需要在javascript文件所在的路径打开终端。

使用的命令是:frida -U -l myhook.js com.erev0s.jniapp

-U标志告诉frida我们使用的是USB设备,-l是要用的javascript文件,最后是要查找的应用程序。注意,此命令要求应用程序已经在设备上运行,因为它不会自动启动程序。如果你想自动启动应用程序,可以使用命令frida -U -l hookNative.js -f com.erev0s.jniapp --no-pause-f标志将启动指定的应用程序,而--no-pause将会在启动后启动应用程序的主线程。

无论使用哪个命令,结果都应该相同,最终都会出现类似于以下内容:

如你所见,按下按钮后的结果已经更改为我们在javascript代码中定义的值。就是这样!我们通过Frida的Java API,已经成功地改变了Jniint的实现。

Hook with Frida directly the Native C implementation!

当我们需要处理C / C ++中的函数并且仅仅改变返回的结果不满足需求时,此方法特别有用。比如说有一个加密函数,它有一些我们想找到的特殊参数(也许是密钥)。我们按照与之前完全相同的设置,唯一的变化是javascript文件myhook.js的内容。内容将变为:

Interceptor.attach(Module.getExportByName('libnative-lib.so', 'Jniint'), {
    onEnter: function(args) {
    },
    onLeave: function(retval) {
      // simply replace the value to be returned with 0
      retval.replace(0);
    }
});

我们现在使用的api是这个,它会自动地在libnative-lib.so中搜索Jniint函数,libnative-lib.so是先前从apk中提取文件时所看到的应用程序中库的名字。retval.replace(0)被调用来替换返回值。

在用与以前相同的方式启动Frida时,你会发现一个error!不用担心,这是预料之内的,你可以看到我们正在尝试hook Jniint,但是由于未按下按钮,函数还未被加载!因此frida提示找不到导出函数 'Jniint'。按一下按钮,现在Jniint已经被调用,所以应该已经导出,我们唯一要做的就是再次保存JavaScript,这样frida将会自动重新加载它,现在如果再次按下按钮,将得到以下信息:

Conclusion

在本文中,我们详细介绍了两种hook native 方法并更改其返回值的办法。我们解释了如何搭建环境并安装所有必需的工具。如果你想在frida上投入更多时间,请确保你在这里查看了API的官方页面,因为你会找到几乎所有需要的参考资料。建议你逐步进行,因为在与高级应用程序打交道时,事情可能会变得很复杂。希望您喜欢这篇文章,并从中学到了一些东西。如有任何问题或评论,请随时与我联系,我将尽力回答。


文章来源: http://xz.aliyun.com/t/7729
如有侵权请联系:admin#unsafe.sh