还原Android被Inline Hook的服务对象
2020-01-22 16:30:25 Author: bbs.pediy.com(查看原文) 阅读量:221 收藏

[原创]还原Android被Inline Hook的服务对象

1天前 406

[原创]还原Android被Inline Hook的服务对象

    小弟也是刚接触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方法的返回值,我们唯一能做的就是从这一句下手:

android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); // Hook点

IBinder b = ServiceManager.getService("service_name");

   我们修改这个getService方法的返回值,让这个方法返回一个我们伪造过的IBinder对象;这样,

我们可以在自己伪造的IBinder对象的queryLocalInterface方法作处理,进而使得asInterface方法返回

在queryLocalInterface方法里面处理过的值,最终实现hook系统服务的目的。

   在跟踪这个getService方法之前我们思考一下,由于系统服务是一系列的远程Service,它们的本体,

也就是Binder本地对象一般都存在于某个单独的进程,在这个进程之外的其他进程存在的都是这些Binder本地对象的代理。

因此在我们的进程里面,存在的也只是这个Binder代理对象,我们也只能对这些Binder代理对象下手。然后,

这个getService是一个静态方法,如果此方法什么都不做,拿到Binder代理对象之后直接返回;

那么我们就无能为力了:我们没有办法拦截一个静态方法,也没有办法获取到这个静态方法里面的局部变量(即我们希望修改的那个Binder代理对象)。

   接下来就可以看这个getService的代码了:

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;
}

   不难发现,ServiceManager为了避免每次都进行跨进程通信,把这些Binder代理对象缓存在一张map里面。

我们可以替换这个map里面的内容为Hook过的IBinder对象,由于系统在getService的时候每次都会优先查找缓存,

因此返回给使用者的都是被我们修改过的对象。从而可以Hook掉前文中所说的queryLocalInterface方法,紧接着

又可以愉快的Hook IPM了。

   所以我们不采用置空字段的方式,即坚决不走queryLocalInterface方法来获取IPM对象。我们要自己new一个IPM。

   那么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;
	}

测试APP:

可以使用爆破签名校验之类的工具进行测试

链接: https://pan.baidu.com/s/1A8qrL6nNqsuNKCZqgUDhwA 提取码: dftm

测试效果:


    另外,如果大佬们有交流群,不妨拉我进去一下,我想偷偷学一点东西。哈哈哈。

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

2020安全开发者峰会(2020 SDC)议题征集 中国.北京 7月!

最后于 1天前 被LivedForward编辑 ,原因:


文章来源: https://bbs.pediy.com/thread-257339.htm
如有侵权请联系:admin#unsafe.sh