xposed检测方法

xposedhook原理

Xposed框架是由rovo89开发的一款针对Android平台的动态劫持项目,是一款可以在不修改APK的情况下影响程序运行(修改系统)的框架服务,通过替换/system/bin/app_process 程序控制 zygote 进程,从而使 app_process 在启动过程中加载XposedBridge.jar 这个jar包,从而完成对Zygote进程及其创建的Dalvik虚拟机的劫持

Zygote进程在启动过程中,除了创建一个Dalvik虚拟机实例之外,还会将Java运行时库加载到进程中来,同时还会注册一些Android核心类的JNI方法到前面创建的Dalvik虚拟机实例中去。 一个应用程序被孵化出来的时候,其不仅会获得Zygote进程中的Dalvik虚拟机实例,还会与Zygote一起共享Java运行时库,这也是可以将XposedBridge.jar这个jar包加载到每一个Android应用中的原因。
XposedBridge有一个私有的Native方法:hookMethodNative,这个方法也在app_process中使用。该函数提供一个方法对象利用Java的反射机制来对内置的方法覆写。

一、检测xposed方法

1.尝试加载xposed的类,如果能加载则表示已经安装了。

XposedHelpers类中存在fieldCache methodCache constructorCache 这三个静态成员,都是hashmap类型,凡是需要被hook的且已经被找到的对象都会被缓存到这三个map里面。
我们通过便利这三个map来找到相关hook信息。
备注:方法a是检测xposed到底改了什么东西存放到a中。抖音似乎会收集相关信息并上报。

[Java] 纯文本查看 复制代码

    public void b()
	{
		try
		{
			Object localObject = ClassLoader.getSystemClassLoader()
                .loadClass("de.robv.android.xposed.XposedHelpers").newInstance();
			// 如果加载类失败 则表示当前环境没有xposed 
			if (localObject != null)
			{
				a(localObject, "fieldCache");
				a(localObject, "methodCache");
				a(localObject, "constructorCache");
			}
			return;
		}
		catch (Throwable localThrowable)
		{}
	}

	private void a(Object arg5, String arg6)
	{
		try
		{
			// 从XposedHelpers中读取相关的hook信息
			Field v0_1 = arg5.getClass().getDeclaredField(arg6);
			v0_1.setAccessible(true);
			Set v0_2 = v0_1.get(arg5).keySet();
			if (v0_2 == null)
			{
				return;
			}
			if (v0_2.isEmpty())
			{
				return;
			}
			Iterator v1 = v0_2.iterator();
			// 排除无关紧要的类
			while (v1.hasNext())
			{
				Object v0_3 = v1.next();
				if (v0_3 == null)
				{
					continue;
				}
				if (((String)v0_3).length() <= 0)
				{
					continue;
				}
				if (((String)v0_3).toLowerCase().startsWith("android.support"))
				{
					continue;
				}
				if (((String)v0_3).toLowerCase().startsWith("javax."))
				{
					continue;
				}
				if (((String)v0_3).toLowerCase().startsWith("android.webkit"))
				{
					continue;
				}
				if (((String)v0_3).toLowerCase().startsWith("java.util"))
				{
					continue;
				}
				if (((String)v0_3).toLowerCase().startsWith("android.widget"))
				{
					continue;
				}
				if (((String)v0_3).toLowerCase().startsWith("sun."))
				{
					continue;
				}
				this.a.add(v0_3);
			}
		}
		catch (Throwable v0)
		{
			v0.printStackTrace();
		}
	}

2.检测xposed相关文件

检测XposedBridge.jar这个文件和de.robv.android.xposed.XposedBridge
XposedBridge.jar存放在framework里面,de.robv.android.xposed.XposedBridge是开发xposed框架使用的主要接口。
在这个方法里读取了maps这个文件,在linux内核中,这个文件存储了进程映射了的内存区域和访问权限。在这里我们可以看到这个进程加载了那些文件。
如果进程加载了xposed相关的so库或者jar则表示xposed框架已注入。

[Java] 纯文本查看 复制代码

    public static boolean a(String paramString)
	{
		try
		{
			Object localObject = new HashSet();
			// 读取maps文件信息
			BufferedReader localBufferedReader = 
                new BufferedReader(new FileReader("/proc/" + Process.myPid() + "/maps"));
			// 遍历查询关键词 反编译出来的代码可能不太准确
			for (;;)
			{
				String str = localBufferedReader.readLine();
				if (str == null)
				{
					break;
				}
				if ((str.endsWith(".so")) || (str.endsWith(".jar")))
				{
					((Set)localObject).add(str.substring(str.lastIndexOf(" ") + 1));
				}
			}
			localBufferedReader.close();
			localObject = ((Set)localObject).iterator();
			while (((Iterator)localObject).hasNext())
			{
				boolean bool = ((String)((Iterator)localObject).next()).contains(paramString);
				if (bool)
				{
					return true;
				}
			}
		}
		catch (Exception paramString)
		{}
		return false;
	}

3.检测方法的调用栈

如果你的手机安装了xposed框架,那么你在查看app崩溃时候的堆栈信息,一定能在顶层找到xposed的类信息,
既然xposed想改你的信息,那么在调用栈里面肯定是有它的身影的。比如出现de.robv.android.xposed.XposedBridge这个类,方法被hook的时候调用栈里会出现de.robv.android.xposed.XposedBridge.handleHookedMethod 和de.robv.android.xposed.XposedBridge.invokeOriginalMethodNative这些xposed的方法,在dalvik.system.NativeStart.main方法后出现de.robv.android.xposed.XposedBridge.main调用

[Java] 纯文本查看 复制代码

    try {
        throw new Exception("");
    } catch (Exception localException) {
        StackTraceElement[] arrayOfStackTraceElement = localException.getStackTrace();
        int m = arrayOfStackTraceElement.length;
        int i = 0;
        int j;
        // 遍历整个堆栈查询xposed相关信息
        for (int k = 0; i < m; k = j)
		{
            StackTraceElement localStackTraceElement = arrayOfStackTraceElement[i];
            j = k;
            if (localStackTraceElement.getClassName()
				.equals("com.android.internal.os.ZygoteInit"))
			{
                k += 1;
                j = k;
                if (k == 2)
				{
                    return true;
                }
            }
            if (localStackTraceElement.getClassName().equals(e))
			{
                return true;
            }
            i += 1;
        }
    }
    return false;
	}

	try
	{
		throw new Exception("");
	}
	catch(
	Exception localException)
	{
		arrayOfStackTraceElement = localException.getStackTrace();
		j = arrayOfStackTraceElement.length;
		i = 0;
	}
	for(;;)
	{
		boolean bool1 = bool2;
		if (i < j)
		{
			if (arrayOfStackTraceElement[i].getClassName()
                .equals("de.robv.android.xposed.XposedBridge"))
			{
				bool1 = true;
			}
		}
		else
		{
			return bool1;
		}
		i += 1;
	}

4.修改hook方法的查询结果

数组c包含了抖音里比较重要的及各类, this.a指的是检测出的被修改的方法,字段。他执行了两者的匹配,将结果存放到了一个JSONObject里面,里面包含了是否使用模拟器,系统的详细信息,是否是双开xpp,是否适用了xp,hook了那些方法等信息上报到https://xlog.snssdk.com/do/y?ver=0.4&ts=

[ HTML] 纯文本查看 复制代码

    private String[] c = {"android.os.Build#SERIAL", 
        "android.os.Build#PRODUCT",
        "android.os.Build#DEVICE", 
        "android.os.Build#FINGERPRINT", 
        "android.os.Build#MODEL", 
        "android.os.Build#BOARD", 
        "android.os.Build#BRAND", 
        "android.os.Build.BOOTLOADER", 
        "android.os.Build#HARDWARE", 
        "android.os.SystemProperties#get(java.lang.String,java.lang.String)", 
        "android.os.SystemProperties#get(java.lang.String)", 
        "java.lang.System#getProperty(java.lang.String)", 
        "android.telephony.TelephonyManager#getDeviceId()", 
        "android.telephony.TelephonyManager#getSubscriberId()", 
        "android.net.wifi.WifiInfo#getMacAddress()", 
        "android.os.Debug#isDebuggerConnected()", 
        "android.app.activitymanager#isUserAMonkey()", 
        "com.ss."};

	public ArrayList<String> c()
	{
		ArrayList localArrayList = new ArrayList();
		Iterator localIterator = this.a.iterator();
		while (localIterator.hasNext())
		{
			String str1 = (String) localIterator.next();
			String[] arrayOfString = this.c;
			int j = arrayOfString.length;
			int i = 0;
			while (i < j)
			{
				if (true == str1.startsWith(arrayOfString[i]))
				{
					String str2 = str1.replace(")#exact", ")").replace(")#bestmatch", ")").replace("#", ".");
					if (str2.length() > 0)
					{
						localArrayList.add(str2);
					}
				}
				i += 1;
			}
		}
		return localArrayList;
	}

5.检测方法是否被篡改

Modifier.isNative方法判断是否是native修饰的方法,xposedhook方法的时候,会把方法转位native方法,我们检测native方法中不该为native的方法来达到检测的目的,比如getDeviceId,getMacAddress这些方法如果是native则表示已经被篡改了。

[Java] 纯文本查看 复制代码

    public static boolean a(String paramString1, String paramString2, Class... paramVarArgs)
	{
		try
		{
			// 判断方法是不是用native修饰的
			boolean bool = Modifier.isNative(Class.forName(paramString1)
											 .getDeclaredMethod(paramString2, paramVarArgs).getModifiers());
			if (bool)
			{
				return true;
			}
		}
		catch (Exception paramString1)
		{
			paramString1.printStackTrace();
		}
		return false;
	}

6.检测包名

这个是最简单也是最不靠谱的方法,因为只要禁止app读取应用列表就没办法了。

[Java] 纯文本查看 复制代码

    if (((ApplicationInfo)localObject).packageName.equals("de.robv.android.xposed.installer"))
	{
		Log.wtf("HookDetection", "Xposed found on the system.");
	}

二、xposed对抗及检测

0.通过反射重写xposed设置,直接禁用

这个方法是酷安里中抄来的。重写application,在onCreate中写入

    try {
		Field v0_1 = ClassLoader.getSystemClassLoader()
			.loadClass("de.robv.android.xposed.XposedBridge")
			.getDeclaredField("disableHooks");
		v0_1.setAccessible(true);
		v0_1.set(null, Boolean.valueOf(true));
	}
    catch(Throwable v0) {
	}

通过xposed的hook方式阻止xposed检测达到通用的目的。

1、通过 ClassLoader 的 loadClass 加载XposedHelper 来修改一些局部变量值,阻止hook.

处理方式,通过Hook 类加载修改 加载的类名

// 过防止调用loadClass加载 de.robv.android.xposed.
        XposedHelpers.findAndHookMethod(ClassLoader.class, "loadClass", String.class, new XC_MethodHook() {
            @Override
            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                if(param.args != null && param.args[0] != null && param.args[0].toString().startsWith("de.robv.android.xposed.")){
 
                    // 改成一个不存在的类
                    // param.args[0] = "de.robv.android.xposed.ThTest";
                    // 调整了一下,听说这样改更好,直接改部分手机有未知影响。
                                        param.setThrowable(new ClassNotFoundException("not found"));
                }
 
                super.beforeHookedMethod(param);
            }
        });

2、堆栈判断过滤xposed的内容

通过 代码抛出一个异常,在堆栈中,查找Xposed相关的内容,进行判定

处理方式,通过Hook堆栈获取类名替换的方式进行阻止

XposedHelpers.findAndHookMethod(StackTraceElement.class, "getClassName", new XC_MethodHook() {
            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                String result = (String) param.getResult();
                if (result != null){
                    if (result.contains("de.robv.android.xposed.")) {
                        param.setResult("");
                        // Log.i(tag, "替换了,字符串名称 " + result);
                    }else if(result.contains("com.android.internal.os.ZygoteInit")){
                        param.setResult("");
                    }
                }
 
                super.afterHookedMethod(param);
            }
        });
        

3、进程中过滤xposed模块

通过读取 shell 命令 /proc/pid(应用进程id)/maps 可以拿到当前上下文的so和jar列表,查找Xposed相关

处理方式,楼主看到大部分多试通过使用 BufferedReader进行读取命令的内容,过滤掉 XposedBridge.jar就好。

XposedHelpers.findAndHookMethod(BufferedReader.class, "readLine", new XC_MethodHook() {
            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                String result = (String) param.getResult();
                if(result != null) {
                    if (result.contains("/data/data/de.robv.android.xposed.installer/bin/XposedBridge.jar")) {
                        param.setResult("");new File("").lastModified();
                    }
                }
 
                super.afterHookedMethod(param);
            }
        });

4、检测java方法是否变成了native方法

其他,由于Xposed的hook,是通过so修改被hook的方法为native来实现的,所以检测方也可以通过检测方法是否变成了native来达到检测的目的

处理方式,对指定的方法,进行返回正常的值,来达到屏蔽的效果,这里用getDeviceId举例

// 定义全局变量 modify
XposedHelpers.findAndHookMethod(Method.class, "getModifiers", new XC_MethodHook() {
            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                Method method = (Method)param.thisObject;
                String[] array = new String[] { "getDeviceId" };
                String method_name = method.getName();
                if(Arrays.asList(array).contains(method_name)){
                    modify = 0;
                }else{
                    modify = (int)param.getResult();
                }
 
                super.afterHookedMethod(param);
            }
        });
 
        XposedHelpers.findAndHookMethod(Modifier.class, "isNative", int.class, new XC_MethodHook() {
            @Override
            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                param.args[0] = modify;
 
                super.beforeHookedMethod(param);
            }
        });

参考。。。

博客发布页

https://blog.coderstory.cn/about-xposed/

定制xp:
https://blog.csdn.net/qq_35834055/article/details/103256122

  • 1
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值