android 进程注入,[原创]一种简单的注入zygote进程的方案

很早就注册了看雪号,结果没分享过什么东西emm,发现现在大家用的注入方案基本上都是替换系统库,分享个不需要替换系统库的方案吧

先说限制:1.只能在Android 7.0及更高版本中使用;2.部分设备会报UnsatisfiedLinkError,还没找到原因,各位大神可以分析下~

1.源码分析

注:本文分析的是Android 7.0的源码

zygote对应的可执行文件其实就是app_process,它的main方法如下:

int main(int argc, char* const argv[])

{

// 省略无关代码...

AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));

// 省略无关代码...

if (zygote) {

runtime.start("com.android.internal.os.ZygoteInit", args, zygote);

} else if (className) {

runtime.start("com.android.internal.os.RuntimeInit", args, zygote);

} else {

fprintf(stderr, "Error: no class name or --zygote supplied.\n");

app_usage();

LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");

return 10;

}

}

跟踪下去,最终会调用JNI_CreateJavaVM创建虚拟机,这个方法是这样实现的:

// JNI Invocation interface.

extern "C" jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {

ScopedTrace trace(__FUNCTION__);

const JavaVMInitArgs* args = static_cast(vm_args);

if (IsBadJniVersion(args->version)) {

LOG(ERROR) << "Bad JNI version passed to CreateJavaVM: " << args->version;

return JNI_EVERSION;

}

RuntimeOptions options;

for (int i = 0; i < args->nOptions; ++i) {

JavaVMOption* option = &args->options[i];

options.push_back(std::make_pair(std::string(option->optionString), option->extraInfo));

}

bool ignore_unrecognized = args->ignoreUnrecognized;

if (!Runtime::Create(options, ignore_unrecognized)) {

return JNI_ERR;

}

// Initialize native loader. This step makes sure we have

// everything set up before we start using JNI.

android::InitializeNativeLoader();

Runtime* runtime = Runtime::Current();

bool started = runtime->Start();

if (!started) {

delete Thread::Current()->GetJniEnv();

delete runtime->GetJavaVM();

LOG(WARNING) << "CreateJavaVM failed";

return JNI_ERR;

}

*p_env = Thread::Current()->GetJniEnv();

*p_vm = runtime->GetJavaVM();

return JNI_OK;

}

注意看android::InitializeNativeLoader(),这个函数直接调用了g_namespaces->Initialize(),而g_namespaces是一个LibraryNamespaces指针,继续看下去,我们发现了宝藏:

void Initialize() {

std::vector<:string> sonames;

const char* android_root_env = getenv("ANDROID_ROOT");

std::string root_dir = android_root_env != nullptr ? android_root_env : "/system";

std::string public_native_libraries_system_config =

root_dir + kPublicNativeLibrariesSystemConfigPathFromRoot;

LOG_ALWAYS_FATAL_IF(!ReadConfig(public_native_libraries_system_config, &sonames),

"Error reading public native library list from \"%s\": %s",

public_native_libraries_system_config.c_str(), strerror(errno));

// 省略无关代码

// This file is optional, quietly ignore if the file does not exist.

ReadConfig(kPublicNativeLibrariesVendorConfig, &sonames);

// android_init_namespaces() expects all the public libraries

// to be loaded so that they can be found by soname alone.

//

// TODO(dimitry): this is a bit misleading since we do not know

// if the vendor public library is going to be opened from /vendor/lib

// we might as well end up loading them from /system/lib

// For now we rely on CTS test to catch things like this but

// it should probably be addressed in the future.

for (const auto& soname : sonames) {

dlopen(soname.c_str(), RTLD_NOW | RTLD_NODELETE);

}

public_libraries_ = base::Join(sonames, ':');

}

public_native_libraries_system_config=/system/etc/public.libraries.txt,而ReadConfig方法很简单,读取传进来的文件路径,按行分割,忽略空行和以#开头的行,然后把这行push_back到传进来的vector里。

所以这个函数做了这几件事:

读取/system/etc/public.libraries.txt和/vendor/etc/public.libraries.txt

挨个dlopen这两个txt文件里提到的所有so库

2.注入实现

有了上面的分析基础,我们这样做就可以让我们自己的so库被zygote进程dlopen了:

把自己写的so库扔到/system/lib/下面(64位是/system/lib64/)

把这个so库的文件名追加到/system/etc/public.libraries.txt里

到这,已经可以在zygote里加载自己的库了,但还有一个问题:zygote只打开了这个库,并没有调用任何函数,而我们常用的JNI_OnLoad函数在这是不会被调用的,怎么才能让zygote执行自己的代码呢?各位估计已经知道了,写一个用__attribute__((constructor))修饰的函数,这个函数会被登记在.init.array里,会在so被加载时调用,我们就完成了注入逻辑;接下来你就可以在zygote里做自己想要做的事了,玩的开心 :)

最后夹带点私货,此方案最先发表在我的博客上,顺便结合SandHook写了个简单的Xposed实现,感兴趣的可以去围观下~

最后于 2020-6-5 09:14

被残页编辑

,原因: 尝试修复代码块高亮

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值