转载:http://www.myhack58.com/Article/html/3/92/2013/38033.htm
首先简单介绍一下进程注入的概念:
进程注入就是将一段代码拷贝到目标进程,然后让目标进程执行这段代码的技术。由于这样的代码构造起来比较复杂,所以实际情况下,只将很少的代码注入到目标进程,而将真正做事的代码放到一个共享库中,即.so文件。被注入的那段代码只负责加载这个.so,并执行里面的函数。
由于.so中的函数是在目标进程中执行的,所以在.so中的函数可以修改目标进程空间的任何内存,当然也可以加钩子,从而达到改变目标进程工作机制的目的。
当然不是任何进程都有权限执行注入操作的。Android平台上的进程注入是基于ptrace()的,要调用ptrace()需要有root权限。目前市面上的主流安全软件也都是基于进程注入来管理和控制其他应用进程的。这也就是为什么这些安全软件需要获得root权限的原因。
关于如何.so注入的实现,有兴趣的朋友可以参考看雪论坛的上的一个注入库LibInject
和洗大师的一个开源项目Android Injector Library。
.so注入以后已经可以干很多事情了,但毕竟是在native层。想要在native层直接修改Java层的变量和逻辑还是很不方便的。况且Android平台的绝大多数应用都是用Java代码写的。因此自然而然就会想到,有没有什么方式可以将dex文件注入目标进程,然后执行dex文件中的Java代码?
经过一段时间的研究,笔者找到了一个切实可行的方法。这里分享给大家:首先,所有的Java类都是由类加载器(ClassLoader)加载的,我们要从特定的路径下加载一个我们自己的dex文件,就必须要有一个自己类加载器才行。有了这个类加载器我们就可以加载我们自己的类,并用反射调用这个类里面的方法。其次,要构造生成这样一个类加载器必须要获得现有的类加载器,因为类加载器是双亲委派模式的。现有的类加载器可以通过反射获得。只是这些都需要用native代码实现。
下面简述一下dex注入的过程:
1. 将.so注入目标进程,执行.so文件中的某个函数。
2. 在这个函数里先获得一个JNIEnv指针,通过这个指针就可以调JNI函数了。
3. 反射得到当前应用进程的PathClassLoader,用这个ClassLoader来构造一个DexClassLoader对象。Dex文件路径作为一个参数传入DexClassLoader的构造函数,另一个重要的参数是,一个具有可写权限的文件夹路径。因为在做dex优化时,需要生成优化过的dex文件,这跟生成/data/dalvik-cache/下的dex文件是一个道理。
4. 通过这个DexClassLoader对象,来加载目标类,然后反射目标类中的目标函数。最终调用之。
参考代码:
//功能:调用dexPath文件中的className类的methodName方法。 //dexPath: dex/jar/apk 文件路径 //dexOptDir: 优化目录, 这个目录的owner必须是要被注入进程的user,否则dex优化会失败 //className: 目标类名,如“com.hook.Test” //methodName: 目标方法名,如"main", 在Java代码里必须定义为public static void main(String[] args); //argc,传给目标方法的参数个数 //argv,传给目标方法的参数 int invoke_dex_method(const char* dexPath, const char* dexOptDir, const char* className, const char* methodName, int argc, char *argv[]) { ALOGD("Invoke dex E"); JNIEnv* env = android::AndroidRuntime::getJNIEnv(); jclass stringClass, classLoaderClass, dexClassLoaderClass, targetClass; jmethodID getSystemClassLoaderMethod, dexClassLoaderContructor, loadClassMethod, targetMethod; jobject systemClassLoaderObject, dexClassLoaderObject; jstring dexPathString, dexOptDirString, classNameString, tmpString; jobjectArray stringArray; /* Get SystemClasLoader */ stringClass = env->FindClass("java/lang/String"); classLoaderClass = env->FindClass("java/lang/ClassLoader"); dexClassLoaderClass = env->FindClass("dalvik/system/DexClassLoader"); getSystemClassLoaderMethod = env->GetStaticMethodID(classLoaderClass, "getSystemClassLoader", "()Ljava/lang/ClassLoader;"); systemClassLoaderObject = env->CallStaticObjectMethod(classLoaderClass, getSystemClassLoaderMethod); /* Create DexClassLoader */ dexClassLoaderContructor = env->GetMethodID(dexClassLoaderClass, "<init>", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/ClassLoader;)V"); dexPathString = env->NewStringUTF(dexPath); dexOptDirString = env->NewStringUTF(dexOptDir); dexClassLoaderObject = env->NewObject(dexClassLoaderClass, dexClassLoaderContructor, dexPathString, dexOptDirString, NULL, systemClassLoaderObject); /* Use DexClassLoader to load target class */ loadClassMethod = env->GetMethodID(dexClassLoaderClass, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;"); classNameString = env->NewStringUTF(className); targetClass = (jclass)env->CallObjectMethod(dexClassLoaderObject, loadClassMethod, classNameString); if (!targetClass) { ALOGE("Failed to load target class %s", className); return -1; } /* Invoke target method */ targetMethod = env->GetStaticMethodID(targetClass, methodName, "([Ljava/lang/String;)V"); if (!targetMethod) { ALOGE("Failed to load target method %s", methodName); return -1; } stringArray = env->NewObjectArray(argc, stringClass, NULL); for (int i = 0; i < argc; i++) { tmpString = env->NewStringUTF(argv[i]); env->SetObjectArrayElement(stringArray, i, tmpString); } env->CallStaticVoidMethod(targetClass, targetMethod, stringArray); ALOGD("Invoke dex X"); return 0; }