LivedForward 发表于 2019-11-7 10:09

还原Android被inline Hook的服务对象

本帖最后由 LivedForward 于 2020-2-7 13:23 编辑

本篇介绍如何还原Android被inlineHook的服务对象,以还原PMS为例。Android爆破签名校验原理是Hook了系统的PMS服务,那么我们如何还原被Hook的服务对象呢?下文介绍一种方案。

我们分析Android系统源码:
android.app.ActivityThread发现类里面有这样一个方法:



如果sPackageManager字段为空就会调用IPackageManager$Stub类的asInterface方法初始化IPackageManager对象,
那么Hook PMS就是将 sPackageManager 字段替换了,到这里我们就很容易得出一种还原PMS服务的方法:先将
sPackageManager 字段置空,然后反射调用getPackageManager方法,这样得到的IPM对象就是原始的服务对象,再

利用反射将PMS对象替换成原始的对象就好了

但是,我们这里不采用这种方式,因为跟进asInterface这个方法我们不难发现: 这里的queryLocalInterface方法也是一个Hook点。



那么new Stub.Proxy(binder);这条语句同样也是产生一个IPM对象,我们就从这里着手。
我们可以自己反射 IPackageManager$Stub$Proxy 这个类产生一个IPM对象,来替换
APP原始被Hook了的PMS服务对象。代码如下所示:

获取真实PMS服务对象之前: recoverPMS(this, getIPackageManager());

public static void recoverPMS(Context ct, Object ipm)    {      
    try{         
      Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");         
      Method currentActivityThreadMethod =               
      activityThreadClass.getDeclaredMethod("currentActivityThread");            
      Object currentActivityThread = currentActivityThreadMethod.invoke(null);            
      //1. 获取全局的ActivityThread对象            
      Field sPackageManagerField = activityThreadClass.getDeclaredField("sPackageManager");            
      sPackageManagerField.setAccessible(true);            
      /*通过置空系统IPM来迫使系统产生新的IPM对象,也是一种方法,但是这种方法也可以被HOOK.            
      IPackageManager$Stub.asInterface方法里存在Hook点,故不采用此方法.                           
      sPackageManagerField.set(currentActivityThread, null);            
      //2. 将sPackageManager置空,让后面反射获取IPackageManager时系统再产生            
      //新的IPackageManager            
      Method getPackageManagerMethod =            activityThreadClass.getDeclaredMethod("getPackageManager");            
      getPackageManagerMethod.setAccessible(true);            
      Object originalIPackageManager = getPackageManagerMethod.invoke(null);            
      //3. 获取到的新的IPackageManager,此IPackageManager没有被Hook   */            
      sPackageManagerField.set(currentActivityThread, ipm);            
      //4. 替换掉ActivityThread里面的被Hook过的 sPackageManager 字段            
      PackageManager pm = ct.getPackageManager();            
      Field mPmField = pm.getClass().getDeclaredField("mPM");            
      mPmField.setAccessible(true);            
      mPmField.set(pm, ipm);            
      //5. 替换ApplicationPackageManager里面的被Hook过的mPM对象      
       }catch (Exception e){            
         Log.d("ysh", "recovery pms error:" + Log.getStackTraceString(e));      
      }   
    }   
   
    public static Object getIPackageManager(){      
      try{            
            Class<?> serviceManagerClass = Class.forName("android.os.ServiceManager");            
            Method getServiceMethod =serviceManagerClass.getDeclaredMethod("getService", String.class);            
            getServiceMethod.setAccessible(true);            
            Object iBinder = getServiceMethod.invoke(null, new String[]{"package"});            
            Class<?> iPackageManager$Stub$ProxyClass = Class.forName("android.content.pm.IPackageManager$Stub$Proxy");            
            Constructor constructor = iPackageManager$Stub$ProxyClass.getDeclaredConstructor(IBinder.class);            
            constructor.setAccessible(true);            
            Object iPackageManager$Stub$Proxy = constructor.newInstance(new Object[]{iBinder});            
            return iPackageManager$Stub$Proxy;      
      }catch (Exception e){            
            Log.d("ysh", "get IPackageManager error:" + Log.getStackTraceString(e));      
      }      
      return null;   
    }
有同学问我说Class这个类也可以被Hook啊,是的,你说的很对.那我这样回答好了,
就是Hook的函数多了,App运行会变得不稳定,你Hook了Java层的实现,我可以放到JNI层嘛,
或者使用另一种方式来实现.

MT管理器Native层Hook使用的是爱奇艺开源的XHook框架,Hook了open函数,针对这种,我们
从 /proc/self/maps 中解析 SO 符号表得到dlopen函数的地址,然后使用dlopen
再获取系统libc库open函数的地址就可以解决了.
MT在Java层则是重写了各种IO函数,达到IO重定向.

测试APP:
可以使用爆破签名校验之类的工具进行测试
https://pan.baidu.com/s/19VqYzGn2Y0n4quXAclwYKw

测试效果:



总体来说攻防相对,互相在彼此的脚印中发展,没有绝对的安全。Hook 系统的API越多,APP运行稳定性就会有一定降低,当APP无法正常运行时,我们是否就可以说,防护方面取得一定的成就。

BlueTears_ 发表于 2019-11-7 10:40

感谢分享

panxiansheng 发表于 2019-11-7 11:14


感谢分享

boell 发表于 2019-11-7 15:03

好东西,感谢楼主分享。

qtfreet00 发表于 2019-11-7 16:08

排版处理下

onihot 发表于 2019-11-9 11:06

感谢楼主分享技术贴,收藏细看

wujinhuangye 发表于 2019-11-11 15:01

模拟器有什么好的方法检测么

wujinhuangye 发表于 2019-11-11 17:48

本帖最后由 wujinhuangye 于 2019-11-11 17:53 编辑

public static void recoverPMS(Context ct, Object ipm) {
      try {
            Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
            Method currentActivityThreadMethod = activityThreadClass.getDeclaredMethod("currentActivityThread");
            Object currentActivityThread = currentActivityThreadMethod.invoke(null);            //1. 获取全局的ActivityThread对象
            Field sPackageManagerField = activityThreadClass.getDeclaredField("sPackageManager");
            sPackageManagerField.setAccessible(true);       /*通过置空系统IPM来迫使系统产生新的IPM对象,也是一种方法,但是这种方法也可以被HOOK.
           IPackageManager$Stub.asInterface方法里存在Hook点,故不采用此方法.
           sPackageManagerField.set(currentActivityThread, null);             //2. 将sPackageManager置空,让后面反射获取IPackageManager时系统再产生
           //新的IPackageManager
           Method getPackageManagerMethod = activityThreadClass.getDeclaredMethod("getPackageManager");
           getPackageManagerMethod.setAccessible(true);
           Object originalIPackageManager = getPackageManagerMethod.invoke(null);             //3. 获取到的新的IPackageManager,此IPackageManager没有被Hook*/
            sPackageManagerField.set(currentActivityThread, ipm);   //4. 替换掉ActivityThread里面的被Hook过的 sPackageManager 字段
            PackageManager pm = ct.getPackageManager();
            Field mPmField = pm.getClass().getDeclaredField("mPM");
            mPmField.setAccessible(true);
            mPmField.set(pm, ipm); //5. 替换ApplicationPackageManager里面的被Hook过的mPM对象
      } catch (Exception e) {
            Log.d("ysh", "recovery pms error:" + Log.getStackTraceString(e));
      }
    }

    public static Object getIPackageManager() {
      try {
            Class<?> serviceManagerClass = Class.forName("android.os.ServiceManager");
            Method getServiceMethod = serviceManagerClass.getDeclaredMethod("getService", String.class);
            getServiceMethod.setAccessible(true);
            Object iBinder = getServiceMethod.invoke(null, new String[]{"package"});
            Class<?> iPackageManager$Stub$ProxyClass = Class.forName("android.content.pm.IPackageManager$Stub$Proxy");
            Constructor constructor = iPackageManager$Stub$ProxyClass.getDeclaredConstructor(IBinder.class);
            constructor.setAccessible(true);
            Object iPackageManager$Stub$Proxy = constructor.newInstance(new Object[]{iBinder});
            return iPackageManager$Stub$Proxy;
      } catch (Exception e) {
            Log.d("ysh", "get IPackageManager error:" + Log.getStackTraceString(e));
      }
      return null;
    }

wujinhuangye 发表于 2019-11-11 20:07

我能否以 修复前取得的 packageManager.getNameForUid(Binder.getCallingUid() 和修复后取得的名称做对比 作为判断 是否是分身软件的依据

mephisto 发表于 2019-11-11 22:12

谢谢分享
页: [1]
查看完整版本: 还原Android被inline Hook的服务对象