Andrdoid中相应用程序的行为拦截实现方式之----从Java层进行拦截

致谢:html

感谢 简行之旅的这篇blog:http://blog.csdn.net/l173864930/article/details/38455951,这篇文章是參考这篇blog的进行一步一步操做的,假设没有这篇好文章的话,貌似我这篇文章的诞生可能就有点难度了。java


今天周日,昨天花了一天的时间总算是搞定了。问题仍是想相应用程序的行为进行拦截的操做,就是像小米手机同样,哪些应用在获取你什么权限的信息。在以前写过相应用程序的行为进行拦截的方式(C层)实现的博客,在写完这篇以后。原本想是尽快的把Java层拦截的文章结束的,但是因为各类缘由吧。因此一直没有时间去弄这些了。今天算是有空。就总结一下吧。如下进入正题:android


1、摘要

咱们知道现在一些安全软件都会有一个功能就是可以拦截应用的行为(比方地理位置信息。通信录等),因此这里就来实现如下这种功能,固然实现这种功能有两种方式,一种是从底层进行拦截。这个我在以前的博客中已经解说过了。ios

没看过的同窗可以转战:shell

http://blog.csdn.net/jiangwei0910410003/article/details/39346151
编程

另外一种方式就是从上层进行拦截,也就是咱们今天所要说的内容,这种方式都是可以的,固然很是多人不少其它的偏向上层。因为底层拦截需要熟知Binder协议和Binder的数据格式的。上层拦截就简单点了。安全


2、知识点概要

首先咱们需要了解一点知识就是不管是底层拦截仍是上层拦截。都需要一个技术支持:进程注入,关于这个知识点,这里就不做解释了,不了解的同窗可以转战:http://blog.csdn.net/jiangwei0910410003/article/details/39293635app

了解了进程注入以后,这篇文章主要解说三点知识:jvm

一、怎样动态载入so。并且运行当中的函数ide

二、怎样在C层运行Java方法(NDK一般是指Java中调用C层函数)

三、怎样改动系统服务(Context.getSystemService(String...)事实上返回来的就是Binder对象)对象

固然咱们还需要一些预备知识:知道怎样使用NDK进行编译项目。不了解的同窗可以转战:

http://blog.csdn.net/jiangwei0910410003/article/details/17710243

这篇文章编译环境是Window下的,我的感受仍是不方便,仍是在Ubuntu环境下操做比較方便

另外一点需要声明:就是拦截行为是需要root权限的


3、样例

第一个样例:简单的进程注入功能

目的:但愿将咱们本身的功能模块(so文件)注入到目标进程中。而后改动目标进程中的某个函数的运行过程

文件:注入功能可运行文件poison、目标进程可运行文件demo一、需要注入的模块libmyso.so


注入功能的可运行文件核心代码poison.c,这个功能模块在后面讲到的样例中也会用到,因此他是公用的

#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <dlfcn.h>
#include <sys/mman.h>
#include <sys/ptrace.h>
#include <sys/wait.h>

#include "ptrace_utils.h"
#include "elf_utils.h"
#include "log.h"
#include "tools.h"

struct process_hook {
	pid_t 		pid;
	char 		*dso;
} process_hook = {0, ""};

int main(int argc, char* argv[]) {
	LOGI("argv len:"+argc);
	if(argc < 2)
	   exit(0);

	struct pt_regs regs;

	process_hook.dso = strdup(argv[1]);
	process_hook.pid = atoi(argv[2]);

	if (access(process_hook.dso, R_OK|X_OK) < 0) {
		LOGE("[-] so file must chmod rx\n");
		return 1;
	}

	const char* process_name = get_process_name(process_hook.pid);
	ptrace_attach(process_hook.pid, strstr(process_name,"zygote"));
	LOGI("[+] ptrace attach to [%d] %s\n", process_hook.pid, get_process_name(process_hook.pid));

	if (ptrace_getregs(process_hook.pid, &regs) < 0) {
		LOGE("[-] Can't get regs %d\n", errno);
		goto DETACH;
	}

	LOGI("[+] pc: %x, r7: %d", regs.ARM_pc, regs.ARM_r7);

	void* remote_dlsym_addr = get_remote_address(process_hook.pid, (void *)dlsym);
	void* remote_dlopen_addr =  get_remote_address(process_hook.pid, (void *)dlopen);

	LOGI("[+] remote_dlopen address %p\n", remote_dlopen_addr);
	LOGI("[+] remote_dlsym  address %p\n", remote_dlsym_addr);

	if(ptrace_dlopen(process_hook.pid, remote_dlopen_addr, process_hook.dso) == NULL){
		LOGE("[-] Ptrace dlopen fail. %s\n", dlerror());
	}

	if (regs.ARM_pc & 1 ) {
		regs.ARM_pc &= (~1u);
		regs.ARM_cpsr |= CPSR_T_MASK;
	} else {
		regs.ARM_cpsr &= ~CPSR_T_MASK;
	}

	if (ptrace_setregs(process_hook.pid, &regs) == -1) {
		LOGE("[-] Set regs fail. %s\n", strerror(errno));
		goto DETACH;
	}

	LOGI("[+] Inject success!\n");

DETACH:
	ptrace_detach(process_hook.pid);
	LOGI("[+] Inject done!\n");
	return 0;
}
咱们看到,这个注入功能的代码和咱们以前说的从底层进行拦截的那篇文章中的注入代码(inject.c)不太同样呀?这个是有人在网上重新改写了一下,事实上功能上没什么差异的。咱们从main函数可以看到,有两个入口參数:

第一个是:需要注入so文件的全路径

第二个是:需要注入进程的pid

也就是说,咱们在运行poison程序的时候需要传递这两个值。在以前说道的注入代码(inject.c)中,事实上这两个參数是在代码中写死的,假设忘记的同窗可以回去看一下。就是前面提到的从底层进行拦截的那篇文章。

那么这样改动以后,貌似灵活性更高了。

固然注入功能的代码不止这一个,事实上是一个project。这里因为篇幅的缘由就不作介绍了,project的下载地址:

http://download.csdn.net/detail/jiangwei0910410003/8138061

使用NDK编译一下。生成可运行文件就OK了。


第一部分:代码实现

1)目标进程依赖的so文件inso.h和inso.c

__attribute__ ((visibility ("default"))) void setA(int i);

__attribute__ ((visibility ("default"))) int getA();

inso.c代码

#include <stdio.h>
#include "inso.h"

static int gA = 1;

void setA(int i){
   gA = i;
}

int getA(){
   return gA;
}

编译成so文件就能够,项目下载:http://download.csdn.net/detail/jiangwei0910410003/8138107


2)目标进程的可运行文件demo1.c

这个就简单了,就是很是easy的代码,起一个循环每个一段时间打印数值,这个项目需要引用上面编译的inso.so文件

头文件inso.h(和上面的头文件是同样的)

__attribute__ ((visibility ("default"))) void setA(int i);

__attribute__ ((visibility ("default"))) int getA();

demo1.c文件

#include <stdio.h>
#include <unistd.h>

#include "inso.h"
#include "log.h"

int main(){

	LOGI("DEMO1 start.");

	while(1){
		LOGI("%d", getA());
		setA(getA() + 1);
		sleep(2);
	}

	return 0;
}
代码简单吧。就是运行循环打印数值,这里使用的是底层的log方法,在log.h文件里定义了,篇幅缘由,这里就不列举了,项目下载地址:http://download.csdn.net/detail/jiangwei0910410003/8138071


3)注入的模块功能源文件myso.c

#include <stdio.h>
#include <stddef.h>
#include <dlfcn.h>
#include <pthread.h>
#include <stddef.h>

#include  "log.h"

__attribute__ ((__constructor__))
void Main() {
	LOGI(">>>>>>>>>>>>>Inject Success!!!!<<<<<<<<<<<<<<");

	void (*setA_func)(int);

	void* handle = dlopen("libinso.so", RTLD_NOW);
	LOGI("Handle:%p",handle);
	//void (*setA_func)(int) = (void (*)(int))dlsym(handle, "setA");
	setA_func = (void (*)(int))dlsym(handle,"setA");
	LOGI("Func:%p",setA_func);
	if (setA_func) {
	   LOGI("setA is Executing!!!");
	   (*setA_func)(999);
	}
	dlclose(handle);
}

说明:

这段代码需要解释一下,首先来看一下:

__attribute__ ((__constructor__))
gcc为函数提供了几种类型的属性。当中包括:构造函数(constructors)和析构函数(destructors)。
程序猿应当使用类似如下的方式来指定这些属性:

static void start(void) __attribute__ ((constructor));
static void stop(void) __attribute__ ((destructor));
带有"构造函数"属性的函数将在main()函数以前被运行,而声明为"析构函数"属性的函数则将在main()退出时运行。
使用方法举例:
#include <iostream>
void breforemain() __attribute__((constructor));
void aftermain() __attribute__((destructor));
class AAA{
public:
      AAA(){std::cout << "before main function AAA" << std::endl;}
      ~AAA(){std::cout << "after main function AAA" << std::endl;}
};
AAA aaa;
void breforemain()
{
    std::cout << "before main function" << std::endl;
}

void aftermain()
{
    std::cout << "after main function" << std::endl;
}

int main(int argc,char** argv)
{
    std::cout << "in main function" << std::endl;
    return 0;
}
输出结果:
before main function AAA
before main function
in main function
after main function AAA
after main function

有点类似于Spring的AOP编程~~


另外一个就是咱们開始说的,怎样载入so文件,并且运行当中的函数,原理很是easy,就是打开so文件。而后返回一个函数指针。

需要的头文件:#include <dlfcn.h>

核心代码:

打开so文件,返回一个句柄handle:

void* handle = dlopen("libinso.so", RTLD_NOW);

获得指定的函数指针:

setA_func = (void (*)(int))dlsym(handle,"setA");

函数指针的定义:

void (*setA_func)(int);
就是这么简单,有点类似Java中动态载入jar包,而后运行当中的方法。

项目下载地址:

http://download.csdn.net/detail/jiangwei0910410003/8138109

第二部分:复制文件

好了。到这里离成功不远了,咱们保证上面的project编译都能经过,获得如下文件:

demo一、poison、libmyso.so、libinso.so

而后咱们就可以实践了

首先我将这些文件复制到手机中的/data/data/文件夹中

adb push demo1 /data/data/

adb push poison /data/data/

adb push libmyso.so /data/data/

拷贝完以后,还需要进入adb shell。改动他们的权限

chmod 777 demo1

chmod 777 poison

chmod 777 libmyso.so

这里要注意的是,libinso.so文件要单独复制到/system/lib中。否则在运行demo1的时候,会报错(找不到libinso.so),固然在拷贝的时候会遇到一点问题

报错:"Failed to push selection: Read-only file system"

这时候仅仅要改变system文件夹的挂载读写属性就行了

mount -o remount rw /system/

而后进入adb shell在改动一下/system的属性

chmod 777 /system

而后就可以拷贝了:

adb push libinso.so /system/lib/

而后进入到system/lib中,改动libinso.so的属性

chmod 777 libinso.so


第三部分:開始运行

而后进入到data/data文件夹中。開始运行文件,这时候咱们需要开三个终端:一个是监听log信息,一个是运行demo1,一个是运行poison,例如如下图所看到的:

一、监听log信息

adb logcat -s TTT



二、运行demo1

./demo1



三、运行poison

./poison /data/data/libmyso.so 1440

这里咱们看到,运行poison有两个入口參数:一个是so文件的路径,一个是目标进程(demo1)的pid就是log信息中的显示的pid

到这里咱们就实现了咱们的第一个样例了。


第二个样例:将目标进程改为Android应用

如下继续:将目标进程改变成一个Android应用

这里和上边的惟一差异就是咱们需要将demo1变成一个Android应用

那么来看一下这个Android应用的代码:

package com.demo.host;

import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;

public final class MainActivity extends Activity {
	private static int sA = 1;
	public static void setA(int a) {
		sA = a;
	}
	public static int getA() {
		return sA;
	}
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		new Thread() {
			public void run() {
				while (true) {
					Log.i("TTT", "" + getA());
					setA(getA() + 1);
					try {
						Thread.sleep(1000);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			};

		}.start();
	}
}
代码和demo1的功能同样的,写个循环,打印数值,project下载地址:http://download.csdn.net/detail/jiangwei0910410003/8138227

这个应用就变成了咱们注入的目标进程,但是有一个问题,咱们怎么才干改动setA()方法的行为呢?在第一个案例中。咱们是动态载入libinso.so,而后获取到setA()函数。改动器返回值。

这里咱们需要作的就是怎么动态的去改动上面的MainActivity中的setA()方法的返回值,事实上这里就到了我開始说的第二个知识点:怎样在底层C中调用Java方法?

咱们知道在使用NDK的时候,Java层调用底层C的时候链接的纽带就是那个JNIEnv变量,这个变量是做为函数參数传递过来的。那么假设咱们在底层C中获取到这个变量的话,就可以调用Java方法了,但是这里咱们又未定义本地方法,怎么获得JNIEnv变量呢?

答案就是#include <android_runtime/AndroidRuntime.h>这个头文件,获得JVM变量以后,而后获得当前线程的JNIEnv变量:

JavaVM* jvm = AndroidRuntime::getJavaVM();
LOGI("jvm is %p",jvm);
jvm->AttachCurrentThread(&jni_env, NULL);
//TODO 使用JNIEnv
jvm->DetachCurrentThread();

经过AndroidRuntime中的getJavaVM方法获取jvm变量,而后在获取当前线程的JNIEnv变量就能够

关于AndroidRuntime这个类的定义和实现是在  AndroidRuntime源代码文件夹/jni/AndroidRuntime.cpp中

好了。当咱们拿到JNIEnv变量以后。咱们就可以干很是多事了,因为咱们知道在弄NDK的时候。假设使用JNIEnv变量的时候都清楚,他比如Java中的反射机制,可以动态的载入Java中的类。而后获取其方法。字段等信息,进行操做。

但是现在另外一个问题。就是咱们怎么去动态载入MainActivity这个类呢?

但是当咱们尝试使用PathClassLoader去载入MainActivity时,会抛ClassNotFoundException

惟一可行的方案是找到host(目标应用)的PathClassLoader,而后经过这个ClassLoader寻找MainActivity

因为咱们是注入到MainActivity这个应用的进程中。那么咱们的注入代码和MainActivity是在一个进程中的。又因为Android中一个进程相应一个全局Context对象,因此咱们仅仅要获得这个进程Context对象的类载入器就可以了

(事实上Android中多个应用是可以跑在一个进程中的。他们会拥有一共同的全局Context变量,固然这个Context不是特定的Activity的。而是Application对象持有的Context)
要想获得一个进程中的Context对象。

经过阅读源代码。发现可以经过如下的方式读取到Context对象:

假设是System_Process,可以经过例如如下方式获取

Context context = ActivityThread.mSystemContext 
假设是非System_Process(即普通的Android进程),可以经过例如如下方式获取
Context context = ((ApplicationThread)RuntimeInit.getApplicationObject()).app_obj.this$0 
到这里,咱们都知道该怎么办了,没错就是用反射机制,获取到全局的Context变量


上面的思路是有了,如下在来整理一下吧:

首先咱们需要注入到MainActivity所在的进程,而后改动他的setA()方法。

但是咱们注入的时候是把so文件注入到一个进程中。因此需要在底层改动setA()方法的运行

假设底层想改动/运行Java层方法的话。必需要获得JNIEnv变量

而后可以经过AndroidRuntime类先获得jvm变量,而后在经过jvm变量获得JNIEnv变量

获得JNIEnv变量以后,用JNIEnv的一些方法去动态载入MainActivity类。而后改动他的方法,但是会出现异常找不到MainActivity类

找不到这个类的缘由是类载入器找的不正确。咱们需要找到全局的Context对象的类载入器。因为咱们是注入到了MainActivity这个应用的进程中,一个进程有一个全局的Context对象。因此仅仅要获得它的类载入器就可以了。

而后经过查看源代码,咱们可以在Java层经过反射获取到这个对象。

最后:经过上面的分析,咱们还需要一些项目的支持:

一、底层获取JNIEnv对象的项目。也就是咱们需要注入的so。

二、在上层还需要一个模块。去获取到全局的Context对象,而后动态的载入MainActivity类,改动他的方法。


第一部分:代码实现

这样分析以后,咱们可能制定的方案是:

1)上层模块DemoInject2:

主要是获取进程的Context变量,而后经过这个变量去载入MainActivity类,改动他的setA()方法的逻辑,核心代码:

ContextHunter.java

package com.demo.inject2;

import android.app.Application;
import android.content.Context;

public final class ContexHunter {
	private static Context sContext = null;

	public static Context getContext() {

		if (sContext == null) {

			synchronized (ContexHunter.class) {
				if (sContext == null) {
					sContext = searchContextForSystemServer();
					if (sContext == null) {
						sContext = searchContextForZygoteProcess();
					}
				}
			}
		}

		return sContext;
	}

	private static Context searchContextForSystemServer() {
		Context result = null;
		try {
			result = (Context) ActivityThread.getSystemContext();
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}

		return result;

	}

	private static Context searchContextForZygoteProcess() {
		Context result = null;
		try {
			Object obj = RuntimeInit.getApplicationObject();
			if (obj != null) {
				obj = ApplicationThread.getActivityThreadObj(obj);
				if (obj != null) {
					result = (Application) ActivityThread.getInitialApplication(obj);
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}

		return result;
	}
}
动态载入MainActivity类。改动其setA()方法的逻辑功能的

EntryClass.java

package com.demo.inject2;

import java.lang.reflect.Method;

import android.content.Context;
import android.util.Log;

public final class EntryClass {

	public static Object[] invoke(int i) {

		try {
			Log.i("TTT", ">>>>>>>>>>>>>I am in, I am a bad boy 2!!!!<<<<<<<<<<<<<<");
			Context context = ContexHunter.getContext();
			Class<?> MainActivity_class = context.getClassLoader().loadClass("com.demo.host.MainActivity");
			Method setA_method = MainActivity_class.getDeclaredMethod("setA", int.class);
			setA_method.invoke(null, 1);
		} catch (Exception e) {
			e.printStackTrace();
		}

		return null;
	}
}
整个项目的下载地址:http://download.csdn.net/detail/jiangwei0910410003/8138735

注:这个项目没有入口的Activity的,因此运行是没有效果的。他的主要功能是一个功能模块apk,如下会说道他的用途。

2)注入的so文件源代码:importdex.cpp

他的功能是获取到当前进程的JVM变量,而后获取到JNIEnv变量,经过JNIEnv变量,载入上面获得的DemoInject2.apk,而后运行EntryClass类中的invoke方法

#include <stdio.h>
#include <stddef.h>
#include <jni.h>
#include <android_runtime/AndroidRuntime.h>

#include "log.h"
#include "importdex.h"

using namespace android;

static const char JSTRING[] = "Ljava/lang/String;";
static const char JCLASS_LOADER[] = "Ljava/lang/ClassLoader;";
static const char JCLASS[] = "Ljava/lang/Class;";

static JNIEnv* jni_env;
static char sig_buffer[512];

//EntryClass entryClass;

//ClassLoader.getSystemClassLoader()
static jobject getSystemClassLoader(){
	
	LOGI("getSystemClassLoader is Executing!!");	
	
	jclass class_loader_claxx = jni_env->FindClass("java/lang/ClassLoader");
	snprintf(sig_buffer, 512, "()%s", JCLASS_LOADER);

	LOGI("sig_buffer is %s",sig_buffer);

	jmethodID getSystemClassLoader_method = jni_env->GetStaticMethodID(class_loader_claxx, "getSystemClassLoader", sig_buffer);

	LOGI("getSystemClassLoader is finished!!");

	return jni_env->CallStaticObjectMethod(class_loader_claxx, getSystemClassLoader_method);

}

__attribute__ ((__constructor__))
void callback() {
	LOGI("Main is Executing!!");
	JavaVM* jvm = AndroidRuntime::getJavaVM();
	LOGI("jvm is %p",jvm);
	jvm->AttachCurrentThread(&jni_env, NULL);
	//TODO 使用JNIEnv

	jvm->DetachCurrentThread();

	LOGI("jni_env is %p",jni_env);

	jstring apk_path = jni_env->NewStringUTF("/data/local/tmp/DemoInject2.apk");
	jstring dex_out_path = jni_env->NewStringUTF("/data/data/com.demo.host/");
	jclass dexloader_claxx = jni_env->FindClass("dalvik/system/DexClassLoader");

	//LOGI("apk_path:%s",apk_path);
	//LOGI("dex_out_path:%s",dex_out_path);

	snprintf(sig_buffer, 512, "(%s%s%s%s)V", JSTRING, JSTRING, JSTRING, JCLASS_LOADER);

	LOGI("sig_buffer is %s",sig_buffer);

	jmethodID dexloader_init_method = jni_env->GetMethodID(dexloader_claxx, "<init>", sig_buffer);

	snprintf(sig_buffer, 512, "(%s)%s", JSTRING, JCLASS);

	LOGI("sig_buffer is %s",sig_buffer);

	jmethodID loadClass_method = jni_env->GetMethodID(dexloader_claxx, "loadClass", sig_buffer);

	jobject class_loader = getSystemClassLoader();
	//check_value(class_loader);
	LOGI("GetClassLoader");

	jobject dex_loader_obj = jni_env->NewObject(dexloader_claxx, dexloader_init_method, apk_path, dex_out_path, NULL, class_loader);
	
	LOGI("step---1");
	LOGI("dex_loader_obj:%s",dex_loader_obj);
	jstring class_name = jni_env->NewStringUTF("com.demo.inject2.EntryClass");
	jclass entry_class = static_cast<jclass>(jni_env->CallObjectMethod(dex_loader_obj, loadClass_method, class_name));
	LOGI("step---2");
	LOGI("jni_env:%p",jni_env);
	LOGI("step---2-1");
	LOGI("entry_class:%s",entry_class);
	jmethodID invoke_method = jni_env->GetStaticMethodID(entry_class, "invoke", "(I)[Ljava/lang/Object;");
	//check_value(invoke_method);
	LOGI("step---3");
	jobjectArray objectarray = (jobjectArray) jni_env->CallStaticObjectMethod(entry_class, invoke_method, 0);
	LOGI("step---4");
	jvm->DetachCurrentThread();

	LOGI("Main is finished");

}
这里。咱们可以看到,一旦咱们拿到了JNIEnv变量以后,真的是什么都可以干,想调用Java世界中的不论什么类中的方法,就连动态载入apk都是可以的。因此这一点真的很是重要,要切记。

这个就是底层中调用Java层的方法的最好实现方案了。超有用的。

这个项目的下载地址:http://download.csdn.net/detail/jiangwei0910410003/8138253


第二部分:复制文件

好的,上面的介绍工做算是结束了,编译顺利的话。会获得三个文件:

DemoHost.apk、DemoInject2.apk、libimportdex.so

首先需要将DemoHost.apk安装到手机上

而后将DemoInject2.apk复制到手机的/data/local/tmp/文件夹中:adb push DemoInject2.apk  /data/local/tmp/

因为在importdex.cpp代码中的路劲就是这个:

jstring apk_path = jni_env->NewStringUTF("/data/local/tmp/DemoInject2.apk");
固然你可以改动的。

最后仍是需要将libimportdex.so文件复制到/data/data/文件夹中

adb push libimportdex.so /data/data/

改动权限:

chmod 777 libimportdex.so


第三部分:開始运行

文件拷贝好了。如下開始运行了,仍是需要开启三个终端:监听log信息。运行注入程序,启动MainActivity(固然这个可以不需要。因为你可以手动的打开)

一、监听log信息

adb logcat -s TTT



二、运行注入程序

./poison /data/data/libimportdex.so 15427

 

三、开启MainActivity

am start com.demo.host/.MainActivity


经过log信息咱们可以看到注入成功了。

注:在这个样例中,编译的过程当中会遇到一个问题:就是很是多头文件,函数定义都找不到的。比方

#include <android_runtime/AndroidRuntime.h>这个头文件,这个就需要咱们去Android的系统源代码中查找了。关于Android系统源代码下载和编译的知识,可以转战:http://blog.csdn.net/jiangwei0910410003/article/details/37988637

当咱们加入这个头文件的时候,在编译仍是出错,因为找不到指定的函数定义,这时候咱们有两种选择:

第一种:将这个头文件的实现源文件也拷贝过来,进行编译(源代码中有)

另一种:使用so文件进行编译,关于这个so文件可以到手机设备的/system/lib/文件夹下找到:libandroid_runtime.so


这里我就是採用这种方式进行编译的,咱们将libandroid_runtime.so文件拷贝出来而后将其放到咱们以前的NDK配置文件夹中的:详细文件夹例如如下:


最后咱们在Android.mk文件进行引用:

LOCAL_LDLIBS := -llog -lbinder -lutils -landroid_runtime

固然,这里咱们会看到-lXXX是通用的格式,相同的,咱们假设要用到JVM中的函数的话,会用到libandroid_runtime.so文件,头文件:android_runtime.h,也是可以在源代码中找到的(后面会说起到)

(注:这里就介绍了咱们怎样在使用Android系统中底层的一些函数。同一时候编译的时候引用到了动态连接库文件)


第三个样例:替换系统服务,拦截程序行为

在看一个样例,第一个样例是怎样将目标模块注入到目标进程中,而后运行它的函数,只是在这个样例中咱们的目标进程是一个简单的C程序编译的可运行文件运行的,第二个样例和第一个样例的差异是将目标进程换成Android中的一个应用。在这个过程当中需要解决很是多问题的。并且这个样例是很是重要的。因为它介绍了怎样在底层调用Java方法:可以将Java模块打成jar/dex/apk,而后在底层使用类载入器进行动态载入,而后运行其指定的方法。

那么到这里算是结束了吗?答案是否认的,因为咱们的目的是拦截。因此最后一个样例咱们就需要实现咱们的目的了

相应用行为的拦截

需要的项目:注入的so文件proxybinder.cpp。上层模块DemoInject3.apk

首先来看一下实现原理吧:

在平常开发过程当中。 咱们经常会使用到的ActivityManager、PackageManager就是一个client的调用。仅仅是自己封装得比較好,让你感受不到。而Service部分的逻辑,主要集中在system_process和com.android.phone这两个进程里头。
broadcastIntent是ActivityManagerService(AMS)的一个方法,AMS的宿主进程是system_process,毫无疑问咱们需要先注入到system_process进程。至于接下来怎么作呢,正是本章的内容。
所有NativeService都继承到IBinder接口。BinderProxy原理很是easy。就是先到找到要代理的NativeService引用,再经过本身编写的ProxyBinder对象代理NativeService。从而达到截获IPC通信的目的。

如下咱们以AMS为例,作一个说明:


AMS跟binder进行通信,是经过JNI实现的。

AMS继承Binder(IBinder的子类。封装了IPC通信公共部分的逻辑),Binder里保存着一个类型为int的mObject的字段。这个字段正是其C++对象JavaBBinder对象的地址,这个JavaBBinder才是AMS终于跟内核通信的对象。

代码例如如下: 

public class Binder implements IBinder {  
    //...  
  
    /* mObject is used by native code, do not remove or rename */  
    private int mObject; //这个对象保存的就是JavaBBinder的指针  
    private IInterface mOwner;  
    private String mDescriptor;  
  
    //...  
} 
相同的。在JavaBBinder中,也保存着一个类型jobject的mObject,指向上层Java对象。看看JavaBBinder的代码:
class JavaBBinder : public BBinder  
{  
  
    //...  
  
    jobject object() const  
    {  
        return mObject;  
    }  
  
    //...  
  
    private:  
        JavaVM* const   mVM;  
        jobject const   mObject; //这个保存的是AMS的引用  
    };  
}  
Java和C++就是经过这两个字段相互连结在一块儿的。
当中JavaBBinder中的mObject是整个IPC关键的一节。所有的client请求。都是先到达JavaBBinder,而后JavaBBinder再经过JNI调用mObject的execTransact的方法,终于把请求发送到AMS。

所以,咱们仅仅要想办法找到AMS的对象的JavaBBinder,再把mObject替换为代理对象(记做ProxyBinder,一个Java对象的引用),就能够实现BinderService代理,如下是示意图:

在实现这个代理,咱们需要获取AMS和及相应用的JavaBBinder两个对象。


获取AMS引用

要获取AMS引用,经过ServiceManager就能够,只是这类是隐藏类,经过反射才干够调用。经过ServiceManager.getService("activity")即可以拿到AMS。
获取JavaBBinder对象
经过前面的介绍,拿到AMS以后,就可以获取其mObject字段,这个对象正好就是JavaBBinder的地址。

另外,也有一种比較简单的方式,那就是经过defaultServiceManager的getService方法获取到。

替换mObject对象
JavaBBinder的mObject对象并不能直接替换,因为mObject是const的,我写了一个DummyJavaBBinder的类。可以很是容易地处理好这个问题,DummyJavaBBinder的实现例如如下:
class DummyJavaBBinder : public BBinder{  
public:  
    virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0) {  
        return NO_ERROR;  
    }  
  
    jobject object() const {  
        return mObject;  
    }  
  
    JavaVM* javaVM() const {  
        return mVM;  
    }  
  
    void changeObj(jobject newobj){  
        const jobject* p_old_obj = &mObject;  
        jobject* p_old_obj_noconst = const_cast<jobject *>(p_old_obj);  
        *p_old_obj_noconst = newobj;  
    }  
  
private:  
    JavaVM* const   mVM;  
    jobject const   mObject;  
};  
这个类的做用主要加入了changeObj方法,主要功能是把mObject去掉const限制。并改动为的newobj。
事实上底层的注入模块的功能很是easy,就是动态载入DemoInject3.apk,而后运行它的invoke方法,获得上层的Binder对象。而后替换底层的Binder对象。这样咱们上层定义的Binder对象中的onTransact方法中就可以拦截信息了。

proxybinder.so的项目下载地址:http://download.csdn.net/detail/jiangwei0910410003/8138929


第一部分:代码实现

1)DemoInject3.apk的核心代码

package com.demo.inject3;

import android.os.Binder;
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException;
import android.util.Log;

public final class EntryClass {

	private static final class ProxyActivityManagerServcie extends Binder {

		private IBinder mBinder;

		public ProxyActivityManagerServcie(IBinder binder) {
			mBinder = binder;
		}

		@Override
		protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
			//打印值。因为data是请求者带来的数据。reply是接受者返回的数据。这里看data数据
			Log.i("TTT", "code="+code);
            		String callingPackage = data.readString();
            		Log.i("TTT", "callingPackage:"+callingPackage);
            		String resolvedType = data.readString();
            		Log.i("TTT", "resolvedType:"+resolvedType);
            		String resultWho = data.readString();
            		Log.i("TTT", "resultWho:"+resultWho);
            		int requestCode = data.readInt();
            		Log.i("TTT", "requestCode:"+requestCode);
            		int startFlags = data.readInt();
            		Log.i("TTT", "startFlags:"+startFlags);
            		String profileFile = data.readString();
            		Log.i("TTT", "profileFile:"+profileFile);
            		int v1 = data.readInt();
            		Log.i("TTT","v1:"+v1);
            		int v2 = data.readInt();
            		Log.i("TTT","v2:"+v2);
            		int userId = data.readInt();
           	 	Log.i("TTT","userId:"+userId);
			return mBinder.transact(code, data, reply, flags);
		}
	}

	public static Object[] invoke(int i) {
		IBinder activity_proxy = null;
		try {
			activity_proxy = new ProxyActivityManagerServcie(ServiceManager.getService("activity"));
			Log.i("TTT", ">>>>>>>>>>>>>I am in, I am a bad boy 3!!!!<<<<<<<<<<<<<<");
		} catch (Exception e) {
			e.printStackTrace();
		}
		//将activity_proxy传递给底层,进行Binder对象的改动
		return new Object[] { "activity", activity_proxy };
	}
}
ProxyActivityManagerService继承了Binder对象,在onTransact中參数data是请求者携带的数据,因此咱们可以打印这个数据的值来看一下,里面都是什么样的值?固然咱们确定不知道这些数据中包括什么信息。因此要到源代码中找到一个有这种方法的类,可以定位到ActivityManagerNative.java这个类。在 源代码文件夹/java/android/app/ActivityManagerNative.java

如下就是他的onTransact方法的一部分:

 @Override
    public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
            throws RemoteException {
        switch (code) {
        case START_ACTIVITY_TRANSACTION:
        {
            data.enforceInterface(IActivityManager.descriptor);
            IBinder b = data.readStrongBinder();
            IApplicationThread app = ApplicationThreadNative.asInterface(b);
            String callingPackage = data.readString();
            Intent intent = Intent.CREATOR.createFromParcel(data);
            String resolvedType = data.readString();
            IBinder resultTo = data.readStrongBinder();
            String resultWho = data.readString();
            int requestCode = data.readInt();
            int startFlags = data.readInt();
            String profileFile = data.readString();
            ParcelFileDescriptor profileFd = data.readInt() != 0
                    ? data.readFileDescriptor() : null;
            Bundle options = data.readInt() != 0
                    ? Bundle.CREATOR.createFromParcel(data) : null;
            int result = startActivity(app, callingPackage, intent, resolvedType,
                    resultTo, resultWho, requestCode, startFlags,
                    profileFile, profileFd, options);
            reply.writeNoException();
            reply.writeInt(result);
            return true;
        }
.........
这个直截取了一小部分。因为这种方法的内容太多了。主要是那个code的值有很是多值,可以看一下code的取值:
   // Please keep these transaction codes the same -- they are also
    // sent by C++ code.
    int START_RUNNING_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION;
    int HANDLE_APPLICATION_CRASH_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+1;
    int START_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+2;
    int UNHANDLED_BACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+3;
    int OPEN_CONTENT_URI_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+4;

    // Remaining non-native transaction codes.
    int FINISH_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+10;
    int REGISTER_RECEIVER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+11;
    int UNREGISTER_RECEIVER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+12;
    int BROADCAST_INTENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+13;
    int UNBROADCAST_INTENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+14;
    int FINISH_RECEIVER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+15;
    int ATTACH_APPLICATION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+16;
    int ACTIVITY_IDLE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+17;
    int ACTIVITY_PAUSED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+18;
    int ACTIVITY_STOPPED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+19;
    int GET_CALLING_PACKAGE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+20;
    int GET_CALLING_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+21;
    int GET_TASKS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+22;
    int MOVE_TASK_TO_FRONT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+23;
    int MOVE_TASK_TO_BACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+24;
    int MOVE_TASK_BACKWARDS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+25;
    int GET_TASK_FOR_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+26;
    int REPORT_THUMBNAIL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+27;
    int GET_CONTENT_PROVIDER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+28;
    int PUBLISH_CONTENT_PROVIDERS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+29;
    int REF_CONTENT_PROVIDER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+30;
    int FINISH_SUB_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+31;
    int GET_RUNNING_SERVICE_CONTROL_PANEL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+32;
    int START_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+33;
    int STOP_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+34;
    int BIND_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+35;
    int UNBIND_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+36;
    int PUBLISH_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+37;
    int ACTIVITY_RESUMED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+38;
    int GOING_TO_SLEEP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+39;
    int WAKING_UP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+40;
    int SET_DEBUG_APP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+41;
    int SET_ALWAYS_FINISH_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+42;
    int START_INSTRUMENTATION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+43;
    int FINISH_INSTRUMENTATION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+44;
    int GET_CONFIGURATION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+45;
    int UPDATE_CONFIGURATION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+46;
    int STOP_SERVICE_TOKEN_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+47;
    int GET_ACTIVITY_CLASS_FOR_TOKEN_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+48;
    int GET_PACKAGE_FOR_TOKEN_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+49;
    int SET_PROCESS_LIMIT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+50;
    int GET_PROCESS_LIMIT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+51;
    int CHECK_PERMISSION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+52;
    int CHECK_URI_PERMISSION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+53;
    int GRANT_URI_PERMISSION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+54;
    int REVOKE_URI_PERMISSION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+55;
    int SET_ACTIVITY_CONTROLLER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+56;
    int SHOW_WAITING_FOR_DEBUGGER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+57;
    int SIGNAL_PERSISTENT_PROCESSES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+58;
    int GET_RECENT_TASKS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+59;
    int SERVICE_DONE_EXECUTING_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+60;
    int ACTIVITY_DESTROYED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+61;
    int GET_INTENT_SENDER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+62;
    int CANCEL_INTENT_SENDER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+63;
    int GET_PACKAGE_FOR_INTENT_SENDER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+64;
    int ENTER_SAFE_MODE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+65;
    int START_NEXT_MATCHING_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+66;
    int NOTE_WAKEUP_ALARM_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+67;
    int REMOVE_CONTENT_PROVIDER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+68;
    int SET_REQUESTED_ORIENTATION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+69;
    int GET_REQUESTED_ORIENTATION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+70;
    int UNBIND_FINISHED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+71;
    int SET_PROCESS_FOREGROUND_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+72;
    int SET_SERVICE_FOREGROUND_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+73;
    int MOVE_ACTIVITY_TASK_TO_BACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+74;
    int GET_MEMORY_INFO_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+75;
    int GET_PROCESSES_IN_ERROR_STATE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+76;
    int CLEAR_APP_DATA_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+77;
    int FORCE_STOP_PACKAGE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+78;
    int KILL_PIDS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+79;
    int GET_SERVICES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+80;
    int GET_TASK_THUMBNAILS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+81;
    int GET_RUNNING_APP_PROCESSES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+82;
    int GET_DEVICE_CONFIGURATION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+83;
    int PEEK_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+84;
    int PROFILE_CONTROL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+85;
    int SHUTDOWN_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+86;
    int STOP_APP_SWITCHES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+87;
    int RESUME_APP_SWITCHES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+88;
    int START_BACKUP_AGENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+89;
    int BACKUP_AGENT_CREATED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+90;
    int UNBIND_BACKUP_AGENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+91;
    int GET_UID_FOR_INTENT_SENDER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+92;
    int HANDLE_INCOMING_USER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+93;
    int GET_TASK_TOP_THUMBNAIL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+94;
    int KILL_APPLICATION_WITH_APPID_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+95;
    int CLOSE_SYSTEM_DIALOGS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+96;
    int GET_PROCESS_MEMORY_INFO_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+97;
    int KILL_APPLICATION_PROCESS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+98;
    ...........
这些值咱们相同可以在 源代码文件夹/java/android/app/IActivityManager.java源代码中看到,很是多值,上面仅仅是一部分。因此上面的onTransact方法中的switch分支结构是有很是多代码的,这里,咱们就从那个分支中选择一种可能将他的代码复制到咱们的onTransact方法中进行分析:
@Override
protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
	//打印值。因为data是请求者带来的数据。reply是接受者返回的数据,这里看data数据
	Log.i("TTT", "code="+code);
	String callingPackage = data.readString();
	Log.i("TTT", "callingPackage:"+callingPackage);
	String resolvedType = data.readString();
	Log.i("TTT", "resolvedType:"+resolvedType);
	String resultWho = data.readString();
	Log.i("TTT", "resultWho:"+resultWho);
	int requestCode = data.readInt();
	Log.i("TTT", "requestCode:"+requestCode);
	int startFlags = data.readInt();
	Log.i("TTT", "startFlags:"+startFlags);
	String profileFile = data.readString();
	Log.i("TTT", "profileFile:"+profileFile);
	int v1 = data.readInt();
	Log.i("TTT","v1:"+v1);
	int v2 = data.readInt();
	Log.i("TTT","v2:"+v2);
	int userId = data.readInt();
	Log.i("TTT","userId:"+userId);
	return mBinder.transact(code, data, reply, flags);
}
经过打印的值,来分析这些字段的含义。

DemoInject3.apk的项目下载地址:http://download.csdn.net/detail/jiangwei0910410003/8138233
好了。假设上面的项目都编译成功的话,会获得两个文件:

libproxybinder.so、DemoInject3.apk


第二部分:复制文件

将libproxybinder.so文件复制到/data/data/文件夹下

adb push libproxybinder.so /data/data/

改动权限

chmod 777 libproxybinder.so

将DemoInject3.apk复制到/data/local/tmp/文件夹下

adb push DemoInject3.apk /data/local/tmp/


第三部分:開始运行

那么如下開始运行吧。

但是想想,咱们应该注入到哪一个进程中呢?这个在开头的时候也说过了,是system_process。但是当咱们使用ps命令去查看进程信息的时候,会发现找不到这个进程名。事实上google一下以后。会发现,事实上咱们真正要注入的进程名是:system_server,而system_process仅仅是这个进程的应用名称。至关于咱们普通应用的名字和咱们的应用的包名(通常包名是进程的名称)。因此咱们再次查找一下system_server进程的时候就找到了:


进程的pid是599。


好的如下就開始运行了,咱们需要开启两个终端就能够:一个监听log信息,一个运行注入程序

一、运行注入程序

./poison /data/data/libproxybinder.so 599


为了能看到哪些应用在使用权限,这里我打开了百度地图app.


二、监听log信息

adb logcat -s TTT


经过打印的结果。咱们看到两个重要的信息,一个resultWho字段,他的值就是应用使用的权限,百度地图使用了位置信息权限。

还有就是requestCode字段。他的值就是应用的进程pid,不信的话,咱们在使用ps查看一下进程列表:


看到了,进程pid为5611,看到进程名为:com.baidu.BaiduMap.pad:remote

因此咱们可以获得两个重要的字段:

resultWho:应用使用的权限

requestCode:应用的进程id

有了这两个值,咱们就可以获得哪一个应用正在使用什么权限(可以经过pid获得应用的名称,这个很是easy。百度一下就可以了)


那么到此咱们就实现了在Java层堆应用程序的行为拦截,在第三个样例中主要就是在上次咱们本身定义一个Binder,而后将其传给底层,而后底层在替换JavaBBinder对象就能够。在这个样例中咱们可以看到我以前讲到的第三个知识点:怎样替换系统服务(Binder).


4、总结

这篇文章总算是结束了。篇幅有点长,主要是经过三个样例。徐徐渐进的解说一下怎样拦截应用的行为:

第一个样例:怎样将咱们的功能模块libmyso.so注入到简单的应用程序中demo1

技术点:动态载入so文件。而后运行当中的指定函数

第二个样例:怎样将咱们的功能模块libimportdex.so注入到咱们的Android应用中

技术点:获取JNIEnv对象,获取进程中的Context对象

第三个样例:怎样将咱们的功能模块注入到系统进程system_process(system_server)中

技术点:上层本身定义一个Binder,在onTransact方法中分析数据。底层替换JavaBBinder对象


固然这篇文章讲的知识点可能比較多。但是主要就是我开头讲到的三个知识点:

一、怎样动态载入so,并且运行当中的函数

二、怎样在C层运行Java方法(NDK一般是指Java中调用C层函数)

三、怎样改动系统服务(Context.getSystemService(String...)事实上返回来的就是Binder对象)对象

注:上面讲到的三个样例中的项目代码都有对应的下载地址,假设download下来以后。运行有问题的话。可以给我留言,我会尽量的帮助解决一下。


最后:事实上这篇写完这篇文章以后,发现了我还有两个地方没有理解清楚:

一、同一个进程中的Application对象以及Context对象的含义

二、Android中的Binder机制

这个我会在兴许的文章中再次进行具体的总结。

(PS:总算是写完了,真心写了一下午还没写完。一大早上来接着写的。这个文章的篇幅有点长,可能需要很是有耐心的去看。固然假设看到有错误的地方,还请各位大哥能指出来,谢谢~~)

相关文章
相关标签/搜索