在未root的设备上使用frida

转自:https://bbs.pediy.com/thread-229970.htm

在这篇教程中我们可以学会怎么在未root设备上使用Frida gadget。
脚本和工具在这里:materials
作者Romain Thomas - @rh0main


 

最近几年,Frida已经成为这一行业进行hook的首选工具。它使用起来快速,灵活,且支持跨平台。

大部分时候在root过后的设备上使用Frida并没有什么限制,但某些场景中有些app可能会检测执行环境。

@ikoz在他的博文Using Frida on Android without root中提到了一种修改Dalvik字节码的方法,以实现在未root设备上使用Frida。在这篇教程中我们提出了一种不需要修改Dalvik字节码的新方法。(即classes.dex

Frida Gadget

在默认模式下,Frida需要在目的应用程序中注入一个代理以访问目标进行的内存空间。

在Android和Linux中这种注入使用到了ptrace。它通过附加或启动一个程序,然后注入对应的代理程序。在代理程序被注入后,它通过管道和服务器进行通信。

有些注入需要权限。比如,我们不能使用普通用户调用ptrace。为了解除这个限制,Frida提供了另一种模式,叫作“embedded”。在这一模式中,用户需要注入frida-gadget库。

这种注入需要:

  • 环境变量:LD_PRELOADDYLD_INSERT_LIBRARIES...
  • 使用dlopen
  • 在开源的目标中,使用linker链接frida-gadget.
  • ...

    要获取关于Frida gadget的更多信息,可以看官方文档:frida-gadget

Frida和LIEF

有一种不那么出名但非常古老的注入技术是通过修改ELF格式。Mayhem 在Phrack详细解释了这一技术的原理,而LIEF(译者注:LIEF,本文作者实现的一个库)提供了一种用户友好的API来实现。

 

简而言之,可执行文件格式包含了链接在可执行文件上的库。我们可以使用lddreadelf(Unix)列出这些库,或者是使用elf_reader.py(Linux, Windows, OSX):

1

2

3

4

5

6

7

$ python ./elf_reader.py -/bin/ls

 

== Dynamic entries ==

 

|Tag    | Value | Info        |

|NEEDED | 0x1   | libcap.so.2 |

|NEEDED | 0x80  | libc.so.6   |

在这里/bin/ls有两个依赖:

  • libcap.so.2
  • libc.so.6

当可执行文件载入的时候,载入器会遍历这些库,并把它们映射到进程到的内存空间中去,并在加载之后调用它的构造方法。

 

这一想法的原理是添加frida-agent.so作为APK的native库的依赖。

 

添加这个依赖的代码非常简单,像下面这样:

1

2

3

4

5

import lief

 

libnative = lief.parse("libnative.so")

libnative.add_library("libgadget.so"# Injection!

libnative.write("libgadget.so")

Telegram

为了解释这个进程,我们会注入frida gadget到Telegram这个应用中。Telegram是个好玩的目标,因为:

  • 它只包含一个native库,这样库就会早点被加载。
  • 它表明了LIEF修改LEF文件的可靠性
  • 这是个真实的app

关于环境,软件是Telegram的4.8.4-12207版本(2018.2.18),系统是在Android 6.0.1上,架构是Samsung Galaxy S6的AArch64架构。

注入LIEF

正如上面解释的,注入过程只需要在libtmessages.28.so上调用lief.ELF.Binary.add_library()

 

在注入之前,libtmessages.28.so与下列库相链接:

1

2

3

4

5

6

7

8

9

10

11

$ readelf -d ./libtmessages.28.so|grep NEEDED

  0x0000000000000001 (NEEDED) Shared library: [libjnigraphics.so]

  0x0000000000000001 (NEEDED) Shared library: [liblog.so]

  0x0000000000000001 (NEEDED) Shared library: [libz.so]

  0x0000000000000001 (NEEDED) Shared library: [libOpenSLES.so]

  0x0000000000000001 (NEEDED) Shared library: [libEGL.so]

  0x0000000000000001 (NEEDED) Shared library: [libGLESv2.so]

  0x0000000000000001 (NEEDED) Shared library: [libdl.so]

  0x0000000000000001 (NEEDED) Shared library: [libstdc++.so]

  0x0000000000000001 (NEEDED) Shared library: [libm.so]

  0x0000000000000001 (NEEDED) Shared library: [libc.so]

在执行了telegram.add_library("libgadget.so")这条语句后,在第一个位置上,我们有了一条新的依赖。

1

2

3

4

5

6

7

8

9

10

11

12

$ readelf -d ./libtmessages.28.so|grep NEEDED

  0x0000000000000001 (NEEDED) Shared library: [libgadget.so]

  0x0000000000000001 (NEEDED) Shared library: [libjnigraphics.so]

  0x0000000000000001 (NEEDED) Shared library: [liblog.so]

  0x0000000000000001 (NEEDED) Shared library: [libz.so]

  0x0000000000000001 (NEEDED) Shared library: [libOpenSLES.so]

  0x0000000000000001 (NEEDED) Shared library: [libEGL.so]

  0x0000000000000001 (NEEDED) Shared library: [libGLESv2.so]

  0x0000000000000001 (NEEDED) Shared library: [libdl.so]

  0x0000000000000001 (NEEDED) Shared library: [libstdc++.so]

  0x0000000000000001 (NEEDED) Shared library: [libm.so]

  0x0000000000000001 (NEEDED) Shared library: [libc.so]

配置Frida Gadget

根据文档我们可以知道,Frida Gadgets可以使用配置文件作为参数来进行交互。

  • Listing:交互和frida-server一样
  • Script:使用配置文件中指定的JS脚本进行交互
  • ScriptDirectory:和Script一样,但可以指定多个应用和多个脚本
    Listing交互方式需要android.permission.INTERNET权限。我们可以通过修改manifest文件添加这个权限。不过,如果我们使用的是Script这种交互方式就不需要这一权限。

Frida payload会定位到/data/local/tmp/myscript.js文件,gadget配置文件的配置如下:

1

2

3

4

5

6

7

{

  "interaction": {

    "type""script",

    "path""/data/local/tmp/myscript.js",

    "on_change""reload"

  }

}

使用配置配置文件必须遵循两个条件:

  1. 文件必须和gadget库同名(例如:libgadget.so 和 libgadget.conf
  2. 配置文件必须和gadget库位于同一目录中

第二个要求也就意味着在设备中安装之后,gadget库会会在/data/app/org.telegram.messenger-1/lib目录中寻找配置文件。

在安装app之后,当满足下述条件时,Android包管理器会从APK的lib/目录中复制文件:

  • 名字具有lib前缀
  • 名字具有.so后缀
  • 名字是gdbserver

Frida 实现这些要求的源码如下。因此我们只需要给libgadget.conf添加.so后缀就行了。

1

2

3

4

5

6

7

8

9

10

#if ANDROID

  if (!FileUtils.test (config_path, FileTest.EXISTS)) {

    var ext_index = config_path.last_index_of_char ('.');

      if (ext_index != -1) {

        config_path = config_path[0:ext_index] + ".config.so";

      else {

        config_path = config_path + ".config.so";

      }

  }

#endif

lib/gadget/gadget.vala

最终,新的Telegram的.apklib目录结构如下:

1

2

3

4

5

6

$ tree lib

.

└── arm64-v8a

    ├── libgadget.config.so

    ├── libgadget.so

    └── libtmessages.28.so

libtmessages.28.so链接到了libgadget.so

运行

在满足下述要求后:

  1. libtmessages.28.so的注入完成
  2. gadget库及其配置文件放在/lib/ABI目录下
  3. 应用程序重新签名

安装new.apk这个重新打包的APK,并把myscript.js放在/data/local/tmp目录下:

1

2

3

$ adb shell install new.apk

$ adb push myscript.js /data/local/tmp

$ adb shell chmod 777 /data/local/tmp/myscript.js

这篇教程中用到的Frida脚本myscript.js只实现了一个对Android log函数调用的功能:

1

2

3

4

5

6

7

8

'use strict';

 

console.log("Waiting for Java..");

 

Java.perform(function () {

  var Log = Java.use("android.util.Log");

  Log.v("frida-lief""Have fun!");

});

myscript.js

最后,我们就可以运行telegram程序并查看它的Android日志了。

1

2

3

4

$ adb logcat -"frida-lief:V"

--------- beginning of system

--------- beginning of main

03-24 17:23:51.908 10243 10243 V frida-lief: Have Fun!

总结

在这篇教程中我们看到了静态插桩和动态插桩的结合方法。

 

下面是这一技术的优/缺点

 

优点:

  • 不需要root
  • 不依赖frida-server
  • 可用于绕过anti-frida
  • 不需要修改AndroidManifest.xml和DEX文件

缺点:

  • 需要向APK里添加文件
  • 需要程序有至少一个native库
  • 注入进去的库的加载顺序不能控制

API

  • lief.ELF.Binary.add_library()

原文链接:https://lief.quarkslab.com/doc/latest/tutorials/09_frida_lief.html
编译:看雪翻译小组 梦野间
校对:看雪翻译小组 lumou

你好!Frida是一个强大的动态代码注入和调试工具,可以用于hook和修改应用程序的行为。如果你想要hook Native代码,可以使用Frida的JavaScript API来实现。 首先,你需要在设备上安装Frida,并确保设备已经越狱(iOS)或者已经root(Android)。 接下来,你需要编写一个JavaScript脚本来进行hook。在脚本中,你可以使用Frida提供的一些函数来定位和修改Native函数。 例如,下面的代码可以用来hook一个Native函数并修改它的行为: ```javascript // 导入Frida模块 const frida = require('frida'); // 目标进程的名称 const targetProcessName = 'your_target_process_name'; // 要hook的函数名称 const targetFunctionName = 'your_target_function_name'; // Frida attach到目标进程 frida.attach(targetProcessName) .then(session => { // 创建一个脚本对象 const script = session.createScript(` // 找到目标函数 const targetFunction = Module.findExportByName(null, "${targetFunctionName}"); // 替换目标函数的实现 Interceptor.replace(targetFunction, new NativeCallback(() => { // 修改函数的行为,这里可以写你想要的逻辑 console.log("Function ${targetFunctionName} hooked!"); // 调用原始函数 const originalFunction = new NativeFunction(targetFunction, 'void', []); originalFunction.call(); // 可以在这里添加你的自定义代码 }, 'void', [])); }); // 加载并运行脚本 script.load() .then(() => { console.log("Script loaded successfully!"); }) .catch(error => { console.log(`Script error: ${error}`); }); }) .catch(error => { console.log(`Attach error: ${error}`); }); ``` 在上面的代码中,我们首先导入了Frida模块,然后指定了目标进程的名称和要hook的函数名称。然后我们使用`frida.attach()`函数来连接到目标进程,并创建一个脚本对象。在脚本中,我们使用`Module.findExportByName()`函数来找到目标函数,然后使用`Interceptor.replace()`函数替换目标函数的实现。在替换的实现中,我们可以添加一些自定义的逻辑以修改函数的行为。 最后,我们使用`script.load()`函数加载并运行脚本。如果一切顺利,你应该能看到"Script loaded successfully!"的输出。 这只是一个简单的例子,你可以根据你的需求进行更复杂的hook和修改。希望对你有所帮助!如果有任何问题,请随时提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值