修补已安装Android应用的.so文件
如果我们安装了Android APK并拥有root访问权限,我们可以修改该应用的原生(.so)文件而不会改变签名。即使AndroidManifest.xml中的extractNativeLibs设置为false,这也是可行的。我们还可以修补AOT编译文件(ODEX/VDEX)而不改变包签名,但那是另一个话题,本文将专注于原生代码。
注意:这不是漏洞,需要root权限。此方法在Mystique漏洞利用演示(2022)中讨论过。我只想展示这对渗透测试很有用,并额外分享如何使用C语言编写二进制补丁的技巧。
背景
我正在对一个具有复杂RASP的Android应用进行渗透测试。面临许多挑战:
- 如果我解压APK文件并重新打包,它可以检测到签名更改
- 如果我使用Frida,它可以在内存中检测到Frida,即使我使用fridare更改名称
- 它可以检测Zygisk,因此所有使用Zygisk的注入方法都会被检测到
- 它可以检测任何函数的钩子,不仅仅是PLT。似乎通过扫描函数序言来检查是否跳转到二进制外部位置;应用开发者需要手动调用此检查(这是相当昂贵的操作),通常在执行某些关键场景之前进行
- RASP使用原生库,该库已被混淆
如果有足够时间,我相信可以追踪并修补所有内容,但我们时间有限,而且只要求检查特定功能。
Android原生库安装
在Android 6.0之前,所有原生库在安装期间都会被提取。因此,当应用安装时,原始APK文件和提取的库都存储在设备上,这对用户来说占用相当多的额外空间。
从Android 6开始,AndroidManifest.xml中有一个名为extractNativeLibs的设置。如果设置为true,则行为与先前版本相同。如果设置为false,库不会被提取,但库必须在APK内未压缩存储并按页边界对齐(使用zipalign)。此设置设为false时,APK会更大,但安装时不会为提取的库占用额外空间。
因为库未压缩且处于页对齐位置,Android可以直接将库mmap到内存中。在Android Gradle Plugin 3.6.0(2020年2月)之后,extractNativeLibs默认为false,这是我们处理近期应用时的设置。
修补方法
如果extractNativeLibs设置为true,我们可以直接用新文件覆盖提取的.so文件。如果extractNativeLibs设置为false,我们仍然可以将库放在如果extractNativeLibs设置为true时会被使用的目录中。
例如,如果APK路径是:
/data/app/~~xa3ANgaSg-DH4SuFIlqKLg==/com.tinyhack.testnative-FFtQq51Ol3Dmg2qvpJAYRg==/base.apk
假设我们使用64位Android,如果我们将修补的库放在(我们移除base.apk并用lib/arm64替换):
/data/app/~~xa3ANgaSg-DH4SuFIlqKLg==/com.tinyhack.testnative-FFtQq51Ol3Dmg2qvpJAYRg==/lib/arm64
那么,此库将被加载而不是APK内同名的库。
使用C语言编写补丁
知道我们可以改变代码并且它能正常运行后,是时候编写补丁了。我见过的大多数教程使用汇编语言进行修补,但我们可以使用C编写二进制补丁。
我创建C文件,使用section注解注释函数和字符串:
|
|
naked属性用于创建零大小函数,noinline确保此函数不被内联,我们希望它被调用。
然后编写替换函数:
|
|
我们需要知道要调用的所有函数的地址,并使用链接器脚本指定函数的确切地址。可以使用Ghidra或其他反汇编器/反编译器获取这些地址。
链接器脚本示例(linker_script.ld):
|
|
编译命令:
clang -O1 -T linker_script.ld -o output.elf code.c
然后提取要修补的代码:
objcopy --dump-section .my_section=dump.bin output.elf
限制
- 代码大小有限,但必要时可以使用dlopen加载另一个库,或使用LIEF添加新section
- 使用未导入的函数不容易(可以直接使用系统调用,直接在内存中解析ELF,或使用硬编码偏移量)
结论
RASP能否更好地检测此方法?是的,但可能需要更多资源:
- 它可以检测每个加载的库是否来自.apk(假设extractNativeLibs始终为false)
- 它可以哈希所有库并在运行时检查
当然,RASP的任何检查都可能被绕过,因为它们无法在操作系统级别工作。如果可能,请在应用中使用Google Play Integrity API(或手机供应商提供的其他认证框架)。
测试设备:Android 13 有效性:此方法在Android 13上仍然有效