这篇文章上次修改于 520 天前,可能其部分内容已经发生变化,如有疑问可询问作者。
这是去年尝试用ebpf实现jnitrace的记录,在此分享
前言
由于eBPF本身的限制,在命中hook点的时候是不能调用用户态空间的函数的
但是提供了读取和修改用户态数据的能力
思路
大部分jni函数到底层经过的地方只能获取到一些零碎的消息
比如可以拿到mid,但是不能直接获取到对应的名字
经过测试,比如CallObjectMethod和CallObjectMethodV必定会调用(大部分情况)
InvokeVirtualOrInterfaceWithVarArgs
=> soa obj mid args- 这里是
jmethodID
- 这里是
InvokeVirtualOrInterfaceWithVarArgs
=> soa obj interface_method args- 这里是
ArtMethod
- 这里是
参考ecapture
的代码,可以通过读取结构体的方式获取到一些信息
比如读取ArtMethod可以获取到这些信息
GcRoot<mirror::Class> declaring_class_;
std::atomic<std::uint32_t> access_flags_;
uint32_t dex_code_item_offset_;
uint32_t dex_method_index_;
uint16_t method_index_;
而要获取到具体的方法名,类名等等,还需要调用更多的函数才行,因为函数名并没有直接在ArtMethod
中
比如以PrettyMethod为例,可以看到需要经过复杂的组合才能获取到完整的信息
std::string ArtMethod::PrettyMethod(bool with_signature) {
if (UNLIKELY(IsRuntimeMethod())) {
std::string result = GetDeclaringClassDescriptor();
result += '.';
result += GetName();
// Do not add "<no signature>" even if `with_signature` is true.
return result;
}
ArtMethod* m = GetInterfaceMethodIfProxy(Runtime::Current()->GetClassLinker()->GetImagePointerSize());
std::string res(m->GetDexFile()->PrettyMethod(m->GetDexMethodIndex(), with_signature));
if (with_signature && m->IsObsolete()) {
return "<OBSOLETE> " + res;
} else {
return res;
}
}
对于eBPF来说,虽然此路不通,但是我们可以只记录这些信息就够了,有了这些信息之后
可以手动解析dex去拿到具体的内容信息,FART中就是这样的思路,自己去解析dex;但是eBPF不具有主动执行代码的可能,所以这里只能放到bcc接收完数据之后
另外的方案是我们在APP启动阶段在相应的函数观测对应关系,这样就不用自己去解析了,但这样的弊端是需要记录大量的数据,并且APP必须经过启动阶段,不够灵活
在jni函数会经过的地方进行观测,收集足够的信息,再辅助其他方法,可以实现大部分信息的获取
对于调用的方法,简单参数,在上述基础的支持已经可以了
但是对于参数列表来说,如果是jobject,应该如何获取更详细的信息呢?
对于参数列表,可以在BuildArgArrayFromVarArgs进行观测,常规参数比较容易获取到信息
对于jobject可能得具体分析soa.Decode看看有没有什么可以拿到的信息
这个过程实在是太麻烦了
关键函数FindVirtualMethod
ObjPtr<mirror::Object> receiver = soa.Decode<mirror::Object>(obj);
ArtMethod* method = FindVirtualMethod(receiver, interface_method);
参考jnitrace
只记录经过了调用的那些信息,这样倒是可以平衡(不过这种情况也是需要在一开始就追踪,这样才尽可能完整
总结
简单来说就是基于uprobe断点,在断点处通过不断【读取结构体】获取到想要的信息,采集信息,最终组合输出
下面是基于eBPF进行的jnitrace实验性测试,不提供任何保证,仅供参考:
- Android 13
- kernel 5.10
- eadb && bcc 环境
- https://gist.github.com/SeeFlowerX/148336c839b183e70e287c66dac482d4
注意:脚本中的偏移不一定通用
效果:
没有评论