Android hook原理及APP安全

  • Xposed原理
  • 如何保证APP最安全。

这篇文章不对逆向和hook进行分析,如有不知道的,请看我之前的文章。

介绍:

现在安装Xposed比较方便,因为Xposed作者开发了一个Xposed Installer App,下载后按照提示傻瓜式安装(前提是root手机)。其实它的安装过程是这个样子的:首先探测手机型号,然后按照手机版本下载不同的刷机包,最后把Xposed刷机包刷入手机重启就好。刷机包下载(https://dl-xda.xposed.info/framework/)里面有所有版本的刷机包。

刷机包解压打开里面的问件构成是这个样子的:

META-INF/    里面有文件配置脚本 flash-script.sh 配置各个文件安装位置。
system/bin/   替换zygote进程等文件
system/framework/XposedBridge.jar jar包位置
system/lib system/lib64 一些so文件所在位置
xposed.prop xposed版本说明文件

所以安装Xposed的过程就上把上面这些文件放到手机里相同文件路径下。
通过查看文件安装脚本发现:
system/bin/下面的文件替换了app_process等文件,app_process就是zygote进程文件。所以Xposed通过替换zygote进程实现了控制手机上所有app进程。因为所有app进程都是由Zygote fork出来的。

Xposed的基本原理是修改了ART/Davilk虚拟机,将需要hook的函数注册为Native层函数。当执行到这一函数是虚拟机会优先执行Native层函数,然后再去执行Java层函数,这样完成函数的hook。

通过读Xposed源码发现其启动过程:
1、手机启动时init进程会启动zygote这个进程。由于zygote进程文件app_process已被替换,所以启动的时Xposed版的zygote进程。
2、Xposed_zygote进程启动后会初始化一些so文件(system/lib system/lib64),然后进入XposedBridge.jar中的XposedBridge.main中初始化jar包完成对一些关键Android系统函数的hook。
3、Hook则是利用修改过的虚拟机将函数注册为native函数。
4、然后再返回zygote中完成原本zygote需要做的工作。

hook流程:
在这里插入图片描述

Xposed
Installer框架中真正起作用的是对方法的Hook和Replace。在Android系统启动的时候,Zygote进程加载XposedBridge.jar,将所有需要替换的Method通过JNI方法hookMethodNative指向Native方法xposedCallHandler,这个方法再通过调用handleHookedMethod这个Java方法来调用被劫持的方法转入Hook逻辑。

上面提到的hookMethodNative是XposedBridge.jar中的私有的本地方法,它将一个方法对象作为传入参数并修改Dalvik虚拟机中对于该方法的定义,把该方法的类型改变为Native并将其实现指向另外一个B方法。

换言之,当调用那个被Hook的A方法时,其实调用的是B方法,调用者是不知道的。在hookMethodNative的实现中,会调用XposedBridge.jar中的handleHookedMethod这个方法来传递参数。handleHookedMethod这个方法类似于一个统一调度的Dispatch例程,其对应的底层的C++函数是xposedCallHandler。而handleHookedMethod实现里面会根据一个全局结构hookedMethodCallbacks来选择相应的Hook函数并调用他们的before和after函数,当多模块同时Hook一个方法的时候Xposed会自动根据Module的优先级来排序。

调用顺序如下:A.before -> B.before -> original method -> B.after -> A.after

扫描二维码关注公众号,回复: 8658554 查看本文章
总结:

1、替换/system/bin/app_process程序控制zygote进程,使用xposed版zygote,然后加载刷机包的so文件,然后初始化xposed.jar包的main方法,先对Android系统函数进行hook。然后再返回zygote中完成原本zygote需要做的工作。 app中hook则是先修改虚拟机,然后将方法改为native方法,然后进行hook

2、当我们在hook一个方法的,首先是xposed.jar的hookMethodNative方法,通过传入的方法参数信息,修改虚拟机的定义改为Native方法。 从而进行hook处理


如何保证我们APP的安全性

一、检测Hook的方法:

① 通过PackageManager查看安装列表

最简单的检测,我们调用Android提供的PackageManager的API来遍历系统中App的安装情况来辨别是否有安装Xposed Installer相关的软件包

PackageManager packageManager = context.getPackageManager();
List applicationInfoList = packageManager.getInstalledApplications(PackageManager.GET_META_DATA);
for (ApplicationInfo applicationInfo: applicationInfoList) {
    if (applicationInfo.packageName.equals("de.robv.android.xposed.installer")) {
        // is Xposed TODO... }
    }

② 自造异常读取栈

try {
    throw new Exception("blah");
} catch(Exception e) {
    for (StackTraceElement stackTraceElement: e.getStackTrace()) {
        // stackTraceElement.getClassName() stackTraceElement.getMethodName() 是否存 在Xposed
    }
}
E/GEnvironment: no such table: preference (code 1): while compiling: SELECT keyguard_show_livewallpaper FROM preference
...
at com.meituan.test.extpackage.ExtPackageManager.checkUpdate(ExtPackageManager.java:127)
at com.meituan.test.MiFGService$1.run(MiFGService.java:41)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5072)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
...
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:609)
at de.robv.android.xposed.XposedBridge.main(XposedBridge.java:132) //发现Xposed模块
at dalvik.system.NativeStart.main(Native Method)

③ 检查关键Java方法被变为Native JNI方法
刚上面我说过了,xposed在hook的时候会把你这个方法改为Native JNI方法,可以利用这个检测:(觉得比较重要的方法)

通过反射调用Modifier.isNative(method.getModifiers())方法可以校验方法是不是Native JNI方法,Xposed同样可以篡改isNative这个方法的返回值。

④ 反射读取XposedHelper类字段,Xposed框架将Hook信息存储在字段fieldCache,methodCache,constructorCache 中, 利用java 反射机制获取这些信息,检测Hook信息中是否含有你们App中敏感的方法,字段,构造方法

boolean methodCache = CheckHook(clsXposedHelper, "methodCache", keyWord);
 
private static boolean CheckHook(Object cls, String filedName, String str) {
    boolean result = false;
    String interName;
    Set keySet;
    try {
        Field filed = cls.getClass().getDeclaredField(filedName);
        filed.setAccessible(true);
        keySet = filed.get(cls)).keySet();
        if (!keySet.isEmpty()) {
            for (Object aKeySet: keySet) {
                interName = aKeySet.toString().toLowerCase();
                if (interName.contains("自己APP信息") || interName.contains("比如 支付宝") ) {
                    result = true;
                    break;
                    } 
                }
            }
        ...
    return result;
}

Native层检测
由上文可知,无论在Java层做何种检测,Xposed都可以通过Hook相关的API并返回指定的结果来绕过检测,只要有方法就可以被Hook。如果仅在Java层检测就显得很徒劳,为了有效提搞检测准确率,就须做到Java和Native层同时检测。每个App在系统中都有对应的加载库列表,这些加载库列表在/proc/下对应的pid/maps文件中描述,在Native层读取/proc/self/maps文件不失为检测Xposed Installer的有效办法之一。由于Xposed Installer通常只能Hook Java层,因此在Native层使用C来解析/proc/self/maps文件,搜检App自身加载的库中是否存在XposedBridge.jar、相关的Dex、Jar和So库等文件。

bool is_xposed()
{
   bool rel = false;
   FILE *fp = NULL;
   char* filepath = "/proc/self/maps";
   ...
   string xp_name = "XposedBridge.jar";
   fp = fopen(filepath,"r")) 
   while (!feof(fp))                                 
   {
       fgets(strLine,BUFFER_SIZE,fp);                    
       origin_str = strLine;
       str = trim(origin_str);
       if (contain(str,xp_name))
       {
           rel = true; //检测到Xposed模块
           break;
       }
   }
    ...
}

二、混淆永远都不可或缺

这个就不用多解释了


三、防止应用被抓包

调用api:

System.getProperty(“http.proxyHost”);
System.getProperty(“http.proxyPort”);

如果发现不为空那么就表示当前设备被挂代理了,那么就有可能被抓包的风险了


四、检测是否被调式:

这个也是借助系统的一个api来进行判断:android.os.Debug.isDebuggerConnected();


五、签名校验防护策略

第一:直接在本地做防护,如果发现签名不一致直接退出应用
第二:将签名信息携带请求参数中参与加密,服务端进行签名校验,失败就返回错误数据即可


六、加固方案
发布了51 篇原创文章 · 获赞 78 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/weixin_39079048/article/details/99703303