leak_monitor.cpp
bool LeakMonitor::Install(std::vector<std::string> *selected_list,
std::vector<std::string> *ignore_list) {
KCHECK(!has_install_monitor_);
// Reinstall can't hook again
if (has_install_monitor_) {
return true;
}
memory_analyzer_ = std::make_unique<MemoryAnalyzer>();
if (!memory_analyzer_->IsValid()) {
ALOGE("memory_analyzer_ NOT Valid");
return false;
}
std::vector<const std::string> register_pattern = {"^/data/.*\\.so$"};
std::vector<const std::string> ignore_pattern = {".*/libkoom-native.so$",
".*/libxhook_lib.so$"};
if (ignore_list != nullptr) {
for (std::string &item : *ignore_list) {
ignore_pattern.push_back(".*/" + item + ".so$");
}
}
if (selected_list != nullptr && !selected_list->empty()) {
// only hook the so in selected list
register_pattern.clear();
for (std::string &item : *selected_list) {
register_pattern.push_back("^/data/.*/" + item + ".so$");
}
}
std::vector<std::pair<const std::string, void *const>> hook_entries = {
std::make_pair("malloc", reinterpret_cast<void *>(WRAP(malloc))),
std::make_pair("realloc", reinterpret_cast<void *>(WRAP(realloc))),
std::make_pair("calloc", reinterpret_cast<void *>(WRAP(calloc))),
std::make_pair("memalign", reinterpret_cast<void *>(WRAP(memalign))),
std::make_pair("posix_memalign",
reinterpret_cast<void *>(WRAP(posix_memalign))),
std::make_pair("free", reinterpret_cast<void *>(WRAP(free)))};
if (HookHelper::HookMethods(register_pattern, ignore_pattern, hook_entries)) {
has_install_monitor_ = true;
return true;
}
HookHelper::UnHookMethods();
live_alloc_records_.Clear();
memory_analyzer_.reset(nullptr);
ALOGE("%s Fail", __FUNCTION__);
return false;
}
这个函数前面通过正则表达式定义了需要hook和不需要hook的动态库。
后续就是把需要hook的系统内存管理函数put到容器里面,接着传给HookHelper去实现hook。
hook_helper.cpp
bool HookHelper::HookMethods(
std::vector<const std::string> ®ister_pattern,
std::vector<const std::string> &ignore_pattern,
std::vector<std::pair<const std::string, void *const>> &methods) {
if (register_pattern.empty() || methods.empty()) {
ALOGE("Hook nothing");
return false;
}
register_pattern_ = std::move(register_pattern);
ignore_pattern_ = std::move(ignore_pattern);
methods_ = std::move(methods);
DlopenCb::GetInstance().AddCallback(Callback);
return HookImpl();
}
void HookHelper::Callback(std::set<std::string> &, int, std::string &) {
HookImpl();
}
做了一些数据准备工作,顺便加了个回调,便于后续的hook操作。
来看下HookImpl
bool HookHelper::HookImpl() {
pthread_mutex_lock(&DlopenCb::hook_mutex);
xhook_clear();
for (auto &pattern : register_pattern_) {
for (auto &method : methods_) {
if (xhook_register(pattern.c_str(), method.first.c_str(), method.second,
nullptr) != EXIT_SUCCESS) {
ALOGE("xhook_register pattern %s method %s fail", pattern.c_str(),
method.first.c_str());
pthread_mutex_unlock(&DlopenCb::hook_mutex);
return false;
}
}
}
for (auto &pattern : ignore_pattern_) {
for (auto &method : methods_) {
if (xhook_ignore(pattern.c_str(), method.first.c_str()) != EXIT_SUCCESS) {
ALOGE("xhook_ignore pattern %s method %s fail", pattern.c_str(),
method.first.c_str());
pthread_mutex_unlock(&DlopenCb::hook_mutex);
return false;
}
}
}
int ret = xhook_refresh(0);
pthread_mutex_unlock(&DlopenCb::hook_mutex);
return ret == 0;
}
这里就是hook的调用了,使用了爱奇艺的开源框架xhook。
看下实现类:
dlopencb.cpp
int Callback(struct dl_phdr_info *info, size_t size, void *data) {
auto *pair = static_cast<std::pair<std::set<std::string> *, std::set<std::string> *> *>(data);
auto origin = pair->first;
auto add = pair->second;
auto name = info->dlpi_name;
if (name != nullptr && hookDlopen(name) && origin->insert(name).second) {
add->insert(name);
}
return 0;
}
Callback 函数是一个回调函数,它用于迭代动态链接器的程序头部信息。它的功能如下:
接受三个参数:struct dl_phdr_info* info,size_t size,void* data。
将 data 转换为 std::pair<std::setstd::string, std::setstd::string> 类型的指针。
从 pair 中获取 origin(原始共享库集合)和 add(新增共享库集合)。
判断动态链接库的名称是否非空,并且是否需要 hookDlopen。如果是,则将其添加到 origin 集合,并且添加到 add 集合中。
dlopencb.cpp
void DlopenCb::Refresh(int source, std::string &loadLibName) {
//一开始输出日志,表示刷新操作开始。
XH_LOG_INFO("Refresh start %d", source);
//接着创建一个空的 addLibs 集合,用于存储新增的共享库。
std::set<std::string> addLibs;
pthread_mutex_lock(&add_lib_mutex);
//获取 hooked_libs 和 addLibs 的指针对,并调用 dl_iterate_phdr 函数进行迭代,每次迭代调用 Callback 函数。
auto callbackData =
make_pair(&hooked_libs, &addLibs);
dl_iterate_phdr(Callback, &callbackData);
pthread_mutex_unlock(&add_lib_mutex);
//如果 addLibs 集合不为空,则对 hook_mutex 进行加锁,清除现有的 xhook 钩子,并根据新增的共享库重新注册钩子。
if (!addLibs.empty()) {
pthread_mutex_lock(&hook_mutex);
xhook_clear();
//根据调试模式进行设置。
if (is_debug) {
xhook_enable_sigsegv_protection(0);
xhook_enable_debug(1);
} else {
xhook_enable_sigsegv_protection(1);
}
for (const auto &lib : addLibs) {
auto lib_ctr = lib.c_str();
xhook_register(lib_ctr, "android_dlopen_ext", (void *) (HookDlopenExt), nullptr);
// xhook_register(lib_ctr, "dlopen", (void *) (HookDlopen), nullptr);
//输出日志,表示新增的共享库已添加。
XH_LOG_INFO("Refresh new lib added %s", lib_ctr);
}
//刷新 xhook 钩子。
xhook_refresh(0);
pthread_mutex_unlock(&hook_mutex);
// notify
XH_LOG_INFO("Refresh hooked");
pthread_mutex_lock(&callback_mutex);
//对回调函数进行通知,传递新增的共享库信息。
for (auto &callback:callbacks) {
callback(addLibs, source, loadLibName);
}
pthread_mutex_unlock(&callback_mutex);
} else {//如果 addLibs 集合为空,则输出日志,表示没有发现新增的共享库。
XH_LOG_INFO("Refresh no lib found");
}
}
前面该宏定义:
// 兼容编译失败,实际API 21以下不支持开启
#if __ANDROID_API__ < 21
void* android_dlopen_ext(const char* __filename, int __flags, const android_dlextinfo* __info) {
return 0;
}
int dl_iterate_phdr(int (*__callback)(struct dl_phdr_info*, size_t, void*), void* __data) {
return 0;
}
#endif
这段代码是一个条件编译,它检查了当前的 Android API 版本是否低于 21。如果当前的 Android API 版本低于 21,则定义了两个函数 android_dlopen_ext 和 dl_iterate_phdr,但这两个函数的实现只是简单地返回了 0,所以在 API 21 以下的版本中这两个函数不支持。
至于怎么理解这两个函数。
android_dlopen_ext和dl_iterate_phdr
void* android_dlopen_ext(const char* __filename, int __flags, const android_dlextinfo* __info) :
这是一个函数声明,它声明了一个名为 android_dlopen_ext
的函数,该函数用于以扩展方式动态加载共享库(动态链接库)。
参数说明如下:
-
__filename
:这是一个指向要加载的共享库文件名的 C 字符串。 -
__flags
:这是一个整数,用于指定加载共享库的选项标志。 -
__info
:这是一个指向android_dlextinfo
结构体的指针,该结构体用于传递扩展加载选项的详细信息。如果不需要传递额外信息,可以传入nullptr
。
函数返回一个 void*
类型的指针,该指针通常用于表示加载的共享库的句柄或者标识符。在这个声明中,函数总是返回 0,表示加载失败或出错。
android_dlopen_ext
函数的具体实现通常由 Android 系统提供,它是 Android 平台上 dlopen
函数的一个扩展版本,用于支持更多的加载选项和功能。
int dl_iterate_phdr(int (*__callback)(struct dl_phdr_info*, size_t, void*), void* __data)
这是一个函数声明,它声明了一个名为 dl_iterate_phdr
的函数,该函数是用来迭代动态链接器的程序头部信息的。这个函数通常在操作系统中用于获取运行时链接器(Runtime Linker)加载的动态链接库的信息。
参数说明如下:
-
__callback
: 这是一个函数指针,指向一个函数,该函数用于处理迭代过程中获取的动态链接库的信息。它接受三个参数:-
struct dl_phdr_info*
: 这是一个结构体指针,用于存储动态链接库的程序头部信息。 -
size_t
: 这是一个表示结构体的大小的参数。 -
void*
: 这是一个指向用户自定义数据的指针,可以在回调函数中使用。
-
-
__data
: 这是一个指向用户自定义数据的指针,会传递给回调函数__callback
。