小弟也是刚接触Android开发不久的大三学生,对Android还是挺感兴趣的。文中肯定会有一些见识以及描述方面的错误,请各位大佬随意指正批评。
本篇介绍如何还原Android被Inline Hook的服务对象,以还原PMS为例。
Android kstools爆破签名校验原理是替换(Hook)了系统的PMS服务,那么我们如何还原被Hook的服务对象呢?下文介绍一种方案。
我们分析Android系统源码: android.app.ActivityThread发现类里面有这样一个方法:
如果sPackageManager字段为空就会调用IPackageManager类的内部Stub类的asInterface方法初始化IPackageManager对象, 那么Hook PMS就是将 sPackageManager 字段替换了,到这里我们就很容易得出一种还原PMS服务的方法:先将 sPackageManager 字段置空,然后反射调用getPackageManager方法,这样得到的IPM对象就是原始的服务对象,再 利用反射将PMS对象替换成原始的对象就好了。 但是,我们这里不采用这种方式,因为跟进asInterface这个方法我们不难发现: 这里的queryLocalInterface方法也是一个Hook点。
为什么说
queryLocalInterface方法也是一个Hook点呢?这里我以Hook queryLocalInterface方法的思路来阐述: 这个方法的意思就是:先查看本进程是否存在这个Binder对象,如果有那么直接就是本进程调用了; 如果不存在那么创建一个代理对象,让代理对象委托驱动完成跨进程调用。 观察这个方法,前面的那个if语句判空返回肯定动不了手脚;最后一句调用构造函数然后直接返回我们也是无从下手, 要修改asInterface方法的返回值,我们唯一能做的就是从这一句下手:
我们修改这个getService方法的返回值,让这个方法返回一个我们伪造过的IBinder对象;这样, 我们可以在自己伪造的IBinder对象的queryLocalInterface方法作处理,进而使得asInterface方法返回 在queryLocalInterface方法里面处理过的值,最终实现hook系统服务的目的。 在跟踪这个getService方法之前我们思考一下,由于系统服务是一系列的远程Service,它们的本体, 也就是Binder本地对象一般都存在于某个单独的进程,在这个进程之外的其他进程存在的都是这些Binder本地对象的代理。 因此在我们的进程里面,存在的也只是这个Binder代理对象,我们也只能对这些Binder代理对象下手。然后, 这个getService是一个静态方法,如果此方法什么都不做,拿到Binder代理对象之后直接返回; 那么我们就无能为力了:我们没有办法拦截一个静态方法,也没有办法获取到这个静态方法里面的局部变量(即我们希望修改的那个Binder代理对象)。 接下来就可以看这个getService的代码了: 不难发现,ServiceManager为了避免每次都进行跨进程通信,把这些Binder代理对象缓存在一张map里面。 我们可以替换这个map里面的内容为Hook过的IBinder对象,由于系统在getService的时候每次都会优先查找缓存, 因此返回给使用者的都是被我们修改过的对象。从而可以Hook掉前文中所说的queryLocalInterface方法,紧接着 又可以愉快的Hook IPM了。 那么new Stub.Proxy(binder);这条语句同样也是产生一个IPM对象,我们就从这里着手。 我们可以自己反射 IPackageManager$Stub$Proxy 这个内部类产生一个IPM对象,来替换 APP原始被Hook了的PMS服务对象。代码如下所示: 获取真实PMS服务对象之前: recoverPMS(this, getIPackageManager()); 测试APP: 可以使用爆破签名校验之类的工具进行测试 链接: https://pan.baidu.com/s/1A8qrL6nNqsuNKCZqgUDhwA 提取码: dftm 测试效果:android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); // Hook点
IBinder b = ServiceManager.getService("service_name");
public static IBinder getService(String name) {
try {
IBinder service = sCache.get(name);
if (service != null) {
return service;
} else {
return getIServiceManager().getService(name);
}
} catch (RemoteException e) {
Log.e(TAG, "error in getService", e);
}
return null;
}
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;
}
另外,如果大佬们有交流群,不妨拉我进去一下,我想偷偷学一点东西。哈哈哈。
总体来说攻防相对,互相在彼此的脚印中发展,没有绝对的安全。Hook 系统的API越多,APP运行稳定性就会有一定降低,当APP无法正常运行时,我们是否就可以说,防护方面取得一定的成就。
2020安全开发者峰会(2020 SDC)议题征集 中国.北京 7月!
最后于 1天前 被LivedForward编辑 ,原因: