在这篇教程中我们可以学会怎么在未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库。
这种注入需要:
Frida和LIEF
有一种不那么出名但非常古老的注入技术是通过修改ELF格式。Mayhem 在Phrack详细解释了这一技术的原理,而LIEF(译者注:LIEF,本文作者实现的一个库)提供了一种用户友好的API来实现。
简而言之,可执行文件格式包含了链接在可执行文件上的库。我们可以使用ldd
或readelf
(Unix)列出这些库,或者是使用elf_reader.py(Linux, Windows, OSX):
$ python ./elf_reader.py -d /bin/ls
== Dynamic entries ==
|Tag | Value | Info |
|NEEDED | 0x1 | libcap.so.2 |
|NEEDED | 0x80 | libc.so.6 |
在这里/bin/ls
有两个依赖:
当可执行文件载入的时候,载入器会遍历这些库,并把它们映射到进程到的内存空间中去,并在加载之后调用它的构造方法。
这一想法的原理是添加frida-agent.so
作为APK的native库的依赖。
添加这个依赖的代码非常简单,像下面这样:
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
与下列库相链接:
$ 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")
这条语句后,在第一个位置上,我们有了一条新的依赖。
$ 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配置文件的配置如下:
{
"interaction": {
"type": "script",
"path": "/data/local/tmp/myscript.js",
"on_change": "reload"
}
}
使用配置配置文件必须遵循两个条件:
- 文件必须和gadget库同名(例如:
libgadget.so
和 libgadget.conf
)
- 配置文件必须和gadget库位于同一目录中
第二个要求也就意味着在设备中安装之后,gadget库会会在/data/app/org.telegram.messenger-1/lib
目录中寻找配置文件。
在安装app之后,当满足下述条件时,Android包管理器会从APK的lib/
目录中复制文件:
- 名字具有
lib
前缀
- 名字具有
.so
后缀
- 名字是
gdbserver
Frida 实现这些要求的源码如下。因此我们只需要给libgadget.conf
添加.so
后缀就行了。
#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的.apk
的lib
目录结构如下:
$ tree lib
.
└── arm64-v8a
├── libgadget.config.so
├── libgadget.so
└── libtmessages.28.so
libtmessages.28.so
链接到了libgadget.so
运行
在满足下述要求后:
- 对
libtmessages.28.so
的注入完成
- gadget库及其配置文件放在
/lib/ABI
目录下
- 应用程序重新签名
安装new.apk
这个重新打包的APK,并把myscript.js
放在/data/local/tmp
目录下:
$ 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函数调用的功能:
'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日志了。
$ adb logcat -s "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
[培训]二进制漏洞攻防(第3期);满10人开班;模糊测试与工具使用二次开发;网络协议漏洞挖掘;Linux内核漏洞挖掘与利用;AOSP漏洞挖掘与利用;代码审计。
最后于 2018-7-18 00:04
被梦野间编辑
,原因: