Hook技术(二)对系统服务Hook

上一次,我们用Hook技术对startActivity进行了操作。我们这一届继续Hook一些比较深入的东西。更加深入的理解Hook。

通过上一次,我们小结,Hook就是用我们的代理去替换原本的服务,然后达到拓展的目的,甚至改变原有服务的目的。那我们今天就尝试改变系统剪贴板。要想改变系统剪贴板,我们就得了解剪贴板服务最基本的流程,从而我们好控制系统剪贴板的Hook点在哪里。

剪贴板服务的基本流程

我们知道系统服务有个大管家,就是ServiceManager这个类,它管理着我们所有的系统服务。并且我们通过如下方法进行系统服务的获取

eg:得到ActivityManager服务

ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);

这个服务的作用是和ActivityManagerService进行通信,从而达到系统控制当前App的进程,进程中包含四大组件也得到控制。

ContextImpl.java

@Override
public Object getSystemService(String name) {
    return SystemServiceRegistry.getSystemService(this, name);
}

SystemServiceRegistry.java

private static final HashMap<String, ServiceFetcher<?>> SYSTEM_SERVICE_FETCHERS =
        new HashMap<String, ServiceFetcher<?>>();

得到服务

public static Object getSystemService(ContextImpl ctx, String name) {
    ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
    return fetcher != null ? fetcher.getService(ctx) : null;
}

注册服务

private static <T> void registerService(String serviceName, Class<T> serviceClass,
        ServiceFetcher<T> serviceFetcher) {
    SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);
    SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);
}

这里可以知道,ContextImpl对象利用SystemServiceRegistry管理我们的服务。

我们裁剪服务的存储

registerService(Context.CLIPBOARD_SERVICE, ClipboardManager.class,
        new CachedServiceFetcher<ClipboardManager>() {
    @Override
    public ClipboardManager createService(ContextImpl ctx) throws ServiceNotFoundException {
        return new ClipboardManager(ctx.getOuterContext(),
                ctx.mMainThread.getHandler());
    }});

看到ClipboardManager就是这个服务的管理者

ClipboardManager.java

public ClipboardManager(Context context, Handler handler) throws ServiceNotFoundException {
    mContext = context;
    mService = IClipboard.Stub.asInterface(
            ServiceManager.getServiceOrThrow(Context.CLIPBOARD_SERVICE));
}

ServiceManager.java

private static HashMap<String, IBinder> sCache = new HashMap<String, IBinder>();
public static IBinder getServiceOrThrow(String name) throws ServiceNotFoundException {
    final IBinder binder = getService(name);
    if (binder != null) {
        return binder;
    } else {
        throw new ServiceNotFoundException(name);
    }
}
public static IBinder getService(String name) {
    try {
        IBinder service = sCache.get(name);
        if (service != null) {
            return service;
        } else {
            return Binder.allowBlocking(getIServiceManager().getService(name));
        }
    } catch (RemoteException e) {
        Log.e(TAG, "error in getService", e);
    }
    return null;
}
public static void initServiceCache(Map<String, IBinder> cache) {
    if (sCache.size() != 0) {
        throw new IllegalStateException("setServiceCache may only be called once");
    }
    sCache.putAll(cache);
}

这里我们可以转化成两句核心代码为:

IBinder service = sCache.get(name);
IClipboard.Stub.asInterface(service);

那我们的思路就可以写成j将sCache中的服务换成我们hook过的服务。然后hookasInterface方法使用我们自己的服务。

接下来我们就分析hook项目的代码

解析项目代码

public class Main2Activity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        try {
            setBinderHook();//方法主入口
        } catch (Exception e) {
            e.printStackTrace();
        }
        EditText editText = new EditText(this);
        setContentView(editText);
        this.getSystemService("");
    }

    private void setBinderHook() throws Exception {
        final String CLIPBOARD_SERVICE = "clipboard";
        //对通过ServiceManager得到的clipboard服务进行hook
        Class<?> serviceManager = Class.forName("android.os.ServiceManager");
        Method getService = serviceManager.getDeclaredMethod("getService", String.class);
        IBinder rawBinder = (IBinder) getService.invoke(null, CLIPBOARD_SERVICE);
        //用BinderProxyHookHandler进行处理原始裁剪服务的IBinder对象
        IBinder hookedBinder = (IBinder) Proxy.newProxyInstance(serviceManager.getClassLoader(),
                new Class<?>[]{IBinder.class},
                new BinderProxyHookHandler(rawBinder));

        Field cacheField = serviceManager.getDeclaredField("sCache");
        cacheField.setAccessible(true);
        Map<String, IBinder> cache = (Map) cacheField.get(null);
        //将替换过的IBinder对象存入ServiceManager的缓存中替换掉原来的对象
        cache.put(CLIPBOARD_SERVICE, hookedBinder);
    }
}

下面这个代码可以看出来,将IBinder中的queryLocalInterface方法进行hook,让此方法返回我们让他返回的对象,这个对象是我们要替换掉系统服务的对象。要使得我们hook的对象听话,也就是一些方法达到我们的目的,我们必须继续使用动态代理模式对其进行代理。也就有了BinderHookHandler

public class BinderProxyHookHandler implements InvocationHandler {
    private static final String TAG = "BinderProxyHookHandler";
    IBinder base;
    Class<?> stub;
    Class<?> iinterface;

    public BinderProxyHookHandler(IBinder base) {
        this.base = base;
        try {
            this.stub = Class.forName("android.content.IClipboard$Stub");
            this.iinterface = Class.forName("android.content.IClipboard");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if ("queryLocalInterface".equals(method.getName())) {
            Log.d(TAG, "hook queryLocalInterface");
            return Proxy.newProxyInstance(proxy.getClass().getClassLoader(),
                    new Class[] { IBinder.class, IInterface.class, this.iinterface },
                    new BinderHookHandler(base, stub));
        }
        Log.d(TAG, "method:" + method.getName());
        return method.invoke(base, args);
    }
}

此时我们已经进入到我们裁剪服务的代理对象中,其中base是我们原Binder,我们通过android.content.IClipboard$Stub对其进行执行asInterface()然后除了我们需要改变的其余方法我们都可以用这个对象处理。其余我们进行拦截

public class BinderHookHandler implements InvocationHandler {

    private static final String TAG = "BinderHookHandler";

    // 原始的Service对象 (IInterface)
    Object base;
    public BinderHookHandler(IBinder base, Class<?> stubClass) {
        try {
            Method asInterfaceMethod = stubClass.getDeclaredMethod("asInterface", IBinder.class);
            // IClipboard.Stub.asInterface(base);
            this.base = asInterfaceMethod.invoke(null, base);
        } catch (Exception e) {
            throw new RuntimeException("hooked failed!");
        }
    }

    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 把剪切版的内容替换为 "I am wang cai niao"
        if ("getPrimaryClip".equals(method.getName())) {
            Log.d(TAG, "hook getPrimaryClip");
            return ClipData.newPlainText(null, "you are hooked");
        }
        // 欺骗系统,使之认为剪切版上一直有内容
        if ("hasPrimaryClip".equals(method.getName())) {
            return true;
        }
        return method.invoke(base, args);
    }
}

猜你喜欢

转载自blog.csdn.net/wangwei708846696/article/details/79525462