这篇文章上次修改于 888 天前,可能其部分内容已经发生变化,如有疑问可询问作者。

尝试使用frida追踪APP执行的每一条smali,找到hook点之后需要调用libart.so中的方法进行转换

这样可以得到可阅读的字符串内容,起初是想frida通过调用获取,但是感觉麻烦,而且性能上有一些损失

用过frida_fart的人都知道寒冰老师是自己写了一个so,即libext(64).so

加上是在高版本系统上,然后就想自己写个native库,然后frida传指针进行调用

然后就进入了一些列踩坑之旅...最终当然是没成

先把相关结论写在前面

  • 安卓高版本(N之后?)上只有部分系统so库可以直接被调用,用frida载入的so如果要用libart.so中的导出函数是办不到的,除非修改public.libraries.txt

可以查看手机/系统镜像中的public.libraries.txt,Pixel 4 Android 11的信息如下

flame:/ # cat /system/etc/public.libraries.txt
# See https://android.googlesource.com/platform/ndk/+/master/docs/PlatformApis.md
libandroid.so
libaaudio.so
libamidi.so
libbinder_ndk.so
libc.so
libcamera2ndk.so
libdl.so
libEGL.so
libGLESv1_CM.so
libGLESv2.so
libGLESv3.so
libicui18n.so
libicuuc.so
libjnigraphics.so
liblog.so
libmediandk.so
libm.so
libnativewindow.so
libneuralnetworks.so nopreload
libOpenMAXAL.so
libOpenSLES.so
libRS.so
libstdc++.so
libsync.so
libvulkan.so
libwebviewchromium_plat_support.so
libz.so

或者查看ndk中的toolchains,例如android-ndk-r20b

$ ls -al ./toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/aarch64-linux-android/29
total 18584
drwxr-xr-x  2 kali kali     4096 Oct 17  2019 .
drwxr-xr-x 10 kali kali     4096 Oct 17  2019 ..
-rw-r--r--  1 kali kali     3416 Oct 17  2019 crtbegin_dynamic.o
-rw-r--r--  1 kali kali     3304 Oct 17  2019 crtbegin_so.o
-rw-r--r--  1 kali kali     3416 Oct 17  2019 crtbegin_static.o
-rw-r--r--  1 kali kali     1128 Oct 17  2019 crtend_android.o
-rw-r--r--  1 kali kali      664 Oct 17  2019 crtend_so.o
-rwxr-xr-x  1 kali kali    22120 Oct 17  2019 libaaudio.so
-rwxr-xr-x  1 kali kali    13624 Oct 17  2019 libamidi.so
-rwxr-xr-x  1 kali kali    80760 Oct 17  2019 libandroid.so
-rwxr-xr-x  1 kali kali    31584 Oct 17  2019 libbinder_ndk.so
-rw-r--r--  1 kali kali       28 Oct 17  2019 libc++.a
-rw-r--r--  1 kali kali 15017040 Oct 17  2019 libc.a
-rwxr-xr-x  1 kali kali    28384 Oct 17  2019 libcamera2ndk.so
-rw-r--r--  1 kali kali    27538 Oct 17  2019 libcompiler_rt-extras.a
-rw-r--r--  1 kali kali       19 Oct 17  2019 libc++.so
-rwxr-xr-x  1 kali kali   290520 Oct 17  2019 libc.so
-rw-r--r--  1 kali kali     5600 Oct 17  2019 libdl.a
-rwxr-xr-x  1 kali kali    13176 Oct 17  2019 libdl.so
-rwxr-xr-x  1 kali kali    28096 Oct 17  2019 libEGL.so
-rwxr-xr-x  1 kali kali    71352 Oct 17  2019 libGLESv1_CM.so
-rwxr-xr-x  1 kali kali    55192 Oct 17  2019 libGLESv2.so
-rwxr-xr-x  1 kali kali   106096 Oct 17  2019 libGLESv3.so
-rwxr-xr-x  1 kali kali    11600 Oct 17  2019 libjnigraphics.so
-rwxr-xr-x  1 kali kali    12456 Oct 17  2019 liblog.so
-rw-r--r--  1 kali kali  2158918 Oct 17  2019 libm.a
-rwxr-xr-x  1 kali kali    73792 Oct 17  2019 libmediandk.so
-rwxr-xr-x  1 kali kali    63200 Oct 17  2019 libm.so
-rwxr-xr-x  1 kali kali    15288 Oct 17  2019 libnativewindow.so
-rwxr-xr-x  1 kali kali    20304 Oct 17  2019 libneuralnetworks.so
-rwxr-xr-x  1 kali kali    16520 Oct 17  2019 libOpenMAXAL.so
-rwxr-xr-x  1 kali kali    17920 Oct 17  2019 libOpenSLES.so
-rw-r--r--  1 kali kali    25296 Oct 17  2019 libstdc++.a
-rwxr-xr-x  1 kali kali    13408 Oct 17  2019 libstdc++.so
-rwxr-xr-x  1 kali kali    11704 Oct 17  2019 libsync.so
-rwxr-xr-x  1 kali kali    53216 Oct 17  2019 libvulkan.so
-rw-r--r--  1 kali kali   621802 Oct 17  2019 libz.a
-rwxr-xr-x  1 kali kali    30120 Oct 17  2019 libz.so

也就是说自行编译so的时候,只能添加ndk中这些so,即系统so

如果通过指定预编译so来设置依赖不在列表中的系统so,到时候载入时会提示dlopen failed: cannot locate symbol

那就没有办法了吗?当然是有的,可以通过偏移的方式去调用,这样so就不用依赖系统库了

如果用偏移的方案当然frida也就可以完成了,但是类型转换可能还是有些麻烦

所以最佳方案还是编译so,然后frida传指针,这样兼顾灵活性和性能

相关参考

冰冰老师一开始就说用偏移,但是我还是头铁踩坑...

大佬的经验都是在踩坑过的基础上得出的,所以大家不要像我这样去踩坑,浪费时间...又被群友拉开差距...

偏移方案还在实践中,这里对踩坑做下记录

记录

因为要对关键函数位置定位,而关键函数又是inline函数,所以最好是编译下系统然后把libart.so拿出来和手机上的对比下

例如这样插入一些代码

当然也可以直接和源代码进行对比分析,只是这样比较痛苦而且有可能不准确

当然我已经这样尝试过,只是hook代码组合有些问题,所以最开始没成

用的是android-11.0.0_r21做的对比

然后我就开始编译android-11.0.0_r21

官方环境指南

apt-get install gnupg flex bison build-essential curl zlib1g-dev gcc-multilib g++-multilib libc6-dev-i386 lib32ncurses-dev x11proto-core-dev libx11-dev lib32z1-dev libgl1-mesa-dev libxml2-utils xsltproc fontconfig
  • 对于kali,需要把lib32ncurses5-dev修改为lib32ncurses-dev

参考AOSP(Android) 镜像使用帮助

配置好repo,然后同步代码

proxychains repo init -u git://mirrors.ustc.edu.cn/aosp/platform/manifest -b android-11.0.0_r21 --depth=1
repo sync -c -j8

直到出现

repo sync has finished successfully.

这里记录一点,就是kali 2021中,由于用户是普通用户,在使用sudo挂载分区后,普通用户是不能直接创建文件和文件夹的

所以这个时候得chmod -R 777 挂载点,然后才能正常同步源代码

再记录一点,起初我尝试把新加的分区合并到/dev/sda1,重新划分extend磁盘和swap分区,但是虚拟机下系统启动就会贼慢,即使通过修改/etc/fstabuuid的内容,依然是这样,所以最后还是普通挂载使用

然后编译系统

export LC_ALL=C && . build/envsetup.sh
lunch
m
  • lunch选的是aosp_flame_userdebug
  • 没错,不需要设置java环境,kali自带java11,高版本也能直接编译pass(至于编译时具体用的是不是系统的java就不知道了
  • 请记得准备驱动
  • python优先级似乎也不用管,可能是kali 2021.3的原因
  • 编译建议至少16G内存起

libart.so路径

  • aosp11/out/target/product/flame/apex/com.android.art.debug/lib64/libart.so
  • aosp11/out/target/product/flame/apex/com.android.art.debug/lib/libart.so

然后才是本文正题,自定义调用系统库,并编译

自定义代码主要内容

const char* PrettyMethod(const ShadowFrame& shadow_frame) {
    std::ostringstream oss;
    oss << shadow_frame.GetMethod()->PrettyMethod() << "\n";
    return oss.str().c_str();
}

一开始我尝试在原有Android.bp中修改,添加相关内容,最终均未成功,有以下几种情况

  • 编译没问题,但是没有libsfxhelp.so产物
  • 编译异常,显示为一些看不懂的配置错误

查阅了一些资料,发现aosp这里面的处理流程比较复杂,加上不了解相关知识,于是放弃随系统编译生成自定义库

改为冰冰老师的建议,将代码单独提取出来,使用Android Studio编译

不过在提取代码的过程中发现相关头文件层级关系复杂,改动很麻烦

折腾了半个多小时,放弃单独提取代码,改为在原系统代码文件结构上加上自己的代码,单独配置ndk设置进行编译

于是我的代码放在

  • aosp11/art/runtime/interpreter/sfxhelp.cc

内容如下

#include <iostream>
#include <sstream>

#include "art_method-inl.h"
#include "shadow_frame-inl.h"

extern "C" const char* SFXTraceExecution(const art::ShadowFrame& shadow_frame, const art::Instruction* inst) {
    std::ostringstream oss;
    oss << shadow_frame.GetMethod()->PrettyMethod()
        << inst->DumpString(shadow_frame.GetMethod()->GetDexFile()) << "\n";
    return oss.str().c_str();
}

extern "C" const char* PrettyMethod(const art::ShadowFrame& shadow_frame) {
    std::ostringstream oss;
    oss << shadow_frame.GetMethod()->PrettyMethod() << "\n";
    return oss.str().c_str();
}

extern "C" const char* DumpString(const art::ShadowFrame& shadow_frame, const art::Instruction* inst) {
    std::ostringstream oss;
    oss << inst->DumpString(shadow_frame.GetMethod()->GetDexFile()) << "\n";
    return oss.str().c_str();
}

然后编译命令是

cd /home/kali/aosp11
/home/kali/Desktop/android-ndk-r23b/ndk-build NDK_PROJECT_PATH=. NDK_APPLICATION_MK=Application.mk APP_BUILD_SCRIPT=Android.mk

Application.mk内容如下

APP_ABI := all
APP_STL := c++_static
APP_PLATFORM := android-21

Android.mk内容如下

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE := sfxhelp
#$(warning "the value of LOCAL_PATH is $(LOCAL_PATH)")


# LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
LOCAL_C_INCLUDES += $(LOCAL_PATH)/art/runtime
LOCAL_C_INCLUDES += $(LOCAL_PATH)/art/libartbase
LOCAL_C_INCLUDES += $(LOCAL_PATH)/art/libdexfile
LOCAL_C_INCLUDES += $(LOCAL_PATH)/bionic/libc/platform
LOCAL_C_INCLUDES += $(LOCAL_PATH)/system/core/base/include
LOCAL_SRC_FILES += $(LOCAL_PATH)/art/runtime/interpreter/sfxhelp.cc

#$(warning "lc $(LOCAL_SRC_FILES)")

LOCAL_CFLAGS += -DART_STACK_OVERFLOW_GAP_arm=8192
LOCAL_CFLAGS += -DART_STACK_OVERFLOW_GAP_arm64=16384
LOCAL_CFLAGS += -DART_STACK_OVERFLOW_GAP_x86=16384
LOCAL_CFLAGS += -DART_STACK_OVERFLOW_GAP_x86_64=20480
LOCAL_CFLAGS += -DART_DEFAULT_GC_TYPE_IS_CMS
LOCAL_CFLAGS += -DIMT_SIZE=43

LOCAL_CPPFLAGS += -std=c++17
LOCAL_LDLIBS += -ldl -Wl,--unresolved-symbols=ignore-all

include $(BUILD_SHARED_LIBRARY)

编译产物路径如下,即Application.mk所在目录下的libs文件夹内

  • /home/kali/aosp11/libs/arm64-v8a/libsfxhelp.so

上面是能正确编译出结果的配置,下面是一些踩坑记录

踩坑点

error: no template named 'enable_if_t' in namespace 'std'; did you mean 'enable_if'

网上说要用c++11以上就不会有这个问题

改成c++11后发现还是有,下面这样的报错

error: no template named 'is_base_of_v' in namespace 'std'; did you mean 'is_base_of'

最终测试了几遍,发现得c++17

然后遇到下面这种未定义的错误

error: use of undeclared identifier 'ART_STACK_OVERFLOW_GAP_arm'

最终找到这些主要是在

  • aosp11/art/build/art.go中配置的

然后这样LOCAL_CFLAGS += -DART_STACK_OVERFLOW_GAP_arm=8192设置即可

然后新的错误是

ld: error: undefined symbol: art::ArtMethod::PrettyMethod(bool)

这主要是最开始我没有引入头文件导致的(好像是,记不太清楚了

#include "art_method-inl.h"

然后编译出产物了

接着尝试使用frida载入so,提示找不到对应的符号...

这是为什么呢?原因是,虽然编译成功,但是调用的符号本身相关的代码没有被包含进来

即自定义so应当依赖libart.so,这样才能找到符号,我尝试了两种方案

一种是通过这样的命令让编译so依赖系统库

LOCAL_LDLIBS += -lart

但是最开始提到,ndk下

  • toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/aarch64-linux-android/29

这样的目录下是没有libart.so的,所以我把libart.so给每一个上面这样的目录都放一个libart.so

然后上面的LOCAL_LDLIBS += -lart就能正常工作了

这样编译出的so就会依赖libart.so

另一个方案是参考下面的文章

即添加这样一段配置,就可以向自定义so中添加要依赖的so,libart.so文件需要放在配置同一级目录

include $(CLEAR_VARS)
LOCAL_MODULE := art
LOCAL_SRC_FILES := libart.so
include $(PREBUILT_SHARED_LIBRARY)

...

LOCAL_SHARED_LIBRARIES := art

...

不过这样编译出来,实际上和前面的结果是一样的

而且即使这样,frida也是不能使用的,会提示打开失败,这就是文章开头说到的问题,高版本不支持直接用系统库

后来我又想,感觉直接把编译的libart.so也用frida载入好了,但是新的问题又有了,需要更多的依赖库...

果然我还是too young too simple

  • 不听老人言,吃亏在眼前

参考链接