首页  编辑  

Hook系统任意服务和类

Tags: /Android/   Date Created:
http://www.zhongruitech.com/4448420426.html

Hook技术(五)如何Hook系统中任意服务

获取服务&注册

ServiceManager.getService()

public static IBinder getService(String name) {
    try {
        IBinder service = sCache.get(name); //先从缓存中查看
        if (service != null) {
            return service;
        } else {
            return getIServiceManager().getService(name);//[1.2]
        }
    } catch (RemoteException e) {
        Log.e(TAG, "error in getService", e);
    }
    return null;
}

1.2 ServiceManagerNative.java ::ServiceManagerProxy()

这里可以看到底层的ServiceManager进行交互,通过底层的ServiceManager返回对应服务的IBinder对象

class ServiceManagerProxy implements IServiceManager {
    public IBinder getService(String name) throws RemoteException {
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        data.writeInterfaceToken(IServiceManager.descriptor);
        data.writeString(name);
        //mRemote为BinderProxy
        mRemote.transact(GET_SERVICE_TRANSACTION, data, reply, 0);
        //从reply里面解析出获取的IBinder对象
        IBinder binder = reply.readStrongBinder();
        reply.recycle();
        data.recycle();
        return binder;
    }
}

关于底层ServiceManager

//有关代码目录

framework/native/cmds/servicemanager/
  - service_manager.c
  - binder.c

kernel/drivers/ (不同Linux分支路径略有不同)
  - staging/android/binder.c
  - android/binder.c 

关于ServiceManager的启动过程如下:
1. 打开binder驱动:binder_open;
2. 注册成为binder服务的大管家:binder_become_context_manager
3. 进入无限循环,处理client端发来的请求:binder_loop;

ServiceManager是由init进程通过解析init.rc文件而创建的,其所对应的可执行程序/system/bin/servicemanager,所对应的源文件是service_manager.c,进程名为/system/bin/servicemanager

可见ServiceManager相当于一个房屋中介,有人过来租房子,有人过来出租。那么系统服务在什么时候进行注册呢?

系统服务的注册

这一部分在SystemServer中,我们只是举一个例子的代码

//ActivityManagerService.java->setSystemProcess()
public void setSystemProcess() {
    try {
        ServiceManager.addService(Context.ACTIVITY_SERVICE, this, true);
        ServiceManager.addService(ProcessStats.SERVICE_NAME, mProcessStats);
        ServiceManager.addService("meminfo", new MemBinder(this));
        ServiceManager.addService("gfxinfo", new GraphicsBinder(this));
        ServiceManager.addService("dbinfo", new DbBinder(this));
        if (MONITOR_CPU_USAGE) {
            ServiceManager.addService("cpuinfo", new CpuBinder(this));
        }
        ServiceManager.addService("permission", new PermissionController(this));
        ServiceManager.addService("processinfo", new ProcessInfoService(this));
        ...
    } catch (PackageManager.NameNotFoundException e) {
        ...
    }
}

可见在SystemServer中都是通过ServiceManager.addService()方法将服务添加到大管家中。

SystemServer的启动时机是在Zygote进程启动时候通过抛出异常的办法执行SystemServer.main()方法进入java世界。

启动的一些系统服务如下:

EntropyService:熵(shang)服务,用于产生随机数
PowerManagerService:电源管理服务
ActivityManage搜索rService:最核心服务之一,Activity管理服务
TelephonyRegistry:电话服务,电话底层通知服务
PackageManagerService:程序包管理服务
AccountManagerService:联系人帐户管理服务
ContentService:内容提供器的服务,提供跨进程数据交换
LightsService:光感应传感器服务
BatteryService:电池服务,当电量不足时发广播
VibratorService:震动器服务
AlarmManagerService:闹钟服务
WindowManagerService:窗口管理服务
BluetoothService:蓝牙服务
InputMethodManagerService:输入法服务,打开关闭输入法
AccessibilityManagerService:辅助管理程序截获所有的用户输入,并根据这些输入给用户一些额外的反馈,起到辅助的效果,View的点击、焦点等事件分发管理服务
DevicePolicyManagerService:提供一些系统级别的设置及属性
StatusBarManagerService:状态栏管理服务
ClipboardService:粘贴板服务
NetworkManagementService:手机网络管理服务
TextServicesManagerService:
NetworkStatsService:手机网络状态服务
NetworkPolicyManagerService:
WifiP2pService:Wifi点对点直联服务
WifiService:WIFI服务
ConnectivityService:网络连接状态服务
ThrottleService:modem节流阀控制服务
MountService:磁盘加载服务,通常也mountd和vold服务结合
NotificationManagerService:通知管理服务,通常和StatusBarManagerService
DeviceStorageMonitorService:存储设备容量监听服务
LocationManagerService:位置管理服务
CountryDetectorService:检查当前用户所在的国家
SearchManagerService:搜索管理服务
DropBoxManagerService:系统日志文件管理服务(大部分程序错误信息)
WallpaperManagerService:壁纸管理服务
AudioService:AudioFlinger上层的封装的音量控制管理服务
UsbService:USB Host和device管理服务
UiModeManagerService:UI模式管理服务,监听车载、座机等场合下UI的变化
BackupManagerService:备份服务
AppWidgetService:应用桌面部件服务
RecognitionManagerService:身份识别服务
DiskStatsService:磁盘统计服务
SamplingProfilerService:性能统计服务
NetworkTimeUpdateService:网络时间更新服务

研究系统服务的Hook点

我们在应用中获取系统服务使用如下代码获取AMS的代理服务:

ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
public Object getSystemService(String name) {
    ServiceFetcher fetcher = SYSTEM_SERVICE_MAP.get(name);
    return fetcher == null ? null : fetcher.getService(this);
}

可以看到从一个Map结构中根据名字得到service,那么一个服务在java层怎么注册的呢?

registerService(ACCOUNT_SERVICE, new ServiceFetcher() {
                public Object createService(ContextImpl ctx) {
                    IBinder b = ServiceManager.getService(ACCOUNT_SERVICE);
                    IAccountManager service = IAccountManager.Stub.asInterface(b);
                    return new AccountManager(ctx, service);
                }});
registerService(ACTIVITY_SERVICE, new ServiceFetcher() {
                public Object createService(ContextImpl ctx) {
                    return new ActivityManager(ctx.getOuterContext(), ctx.mMainThread.getHandler());
                }});

可以看出,核心不变,只是简单包装成了一个ServiceFetcher对象而已。

但是我们对于ActivityManager的使用,查看startActivity/startService就会知道我们并没有像上面一样获取AM服务,而是通过ActivityManagerNative.getDefault()完成

private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
        protected IActivityManager create() {
            IBinder b = ServiceManager.getService("activity");
            IActivityManager am = asInterface(b);//进行如下调用
            return am;
        }
    };

static public IActivityManager asInterface(IBinder obj) {
    if (obj == null) {
        return null;
    }
    IActivityManager in = (IActivityManager)obj.queryLocalInterface(descriptor);
    if (in != null) {
        return in;
    }
    return new ActivityManagerProxy(obj);
}

queryLocalInterface()方法的目的是如果是服务端则直接返回服务端Binder对象,如果是客户端,则返回null,进而通过asInterface()返回代理对象。

所以综上所述服务的获取套路是:

IBinder b = ServiceManager.getService("service_name"); // 获取原始的IBinder对象
IXXInterface in = IXXInterface.Stub.asInterface(b); // 转换为Service接口

Hook点总结:

我们看到最终返回什么还是由于queryLocalInterface()方法决定,我们只要拦截该方法,让其一直返回我们的代理对象从而asInterface()就会一直返回我们代理的对象达到Hook目的。

举个栗子

剪贴板使我们的系统服务,我们就对其进行改造

  1. 得到剪贴板服务
final String CLIPBOARD_SERVICE = "clipboard";
Class<?> serviceManager = Class.forName("android.os.ServiceManager");
Method getService = serviceManager.getDeclaredMethod("getService", String.class);
IBinder rawBinder = (IBinder) getService.invoke(null, CLIPBOARD_SERVICE);
  1. 对ServiceManager进行hook
IBinder hookedBinder = (IBinder) Proxy.newProxyInstance(serviceManager.getClassLoader(),
        new Class<?>[] { IBinder.class },
        new BinderProxyHookHandler(rawBinder));
  1. 用我们自己的剪贴板服务代替系统剪贴板服务
Field cacheField = serviceManager.getDeclaredField("sCache");
cacheField.setAccessible(true);
Map<String, IBinder> cache = (Map) cacheField.get(null);
cache.put(CLIPBOARD_SERVICE, hookedBinder);
  1. 我们hook处理的逻辑
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())) {
            return Proxy.newProxyInstance(proxy.getClass().getClassLoader(),
                    new Class[] { IBinder.class, IInterface.class, this.iinterface },
                    new BinderHookHandler(base, stub));
        }
        return method.invoke(base, args);
    }
}

写到这块,我突然又一次不理解动态代理了,我先说下不理解的原因步骤:

  • 由于我们最初代理的是ServiceManager
  • 然后我们在对应BinderProxyHookHandler的invoke方法中是可以拦截queryLocalInterface方法,这点没错。
  • 如果我们在invoke中不做任何处理返回的是IBinder对应接口方法中的那些原有实现。
  • 但是此时我们对queryLocalInterface做了特殊处理,意味着执行到该方法的时候返回我们特殊处理的结果。
  • 此时特殊处理的结果又是一个代理。使用的参数是我们ServiceManager的ClassLoader,我们不是要返回我们自己的剪贴板服务吗。怎么返回一个代理,这个代理传入的接口是IClipboard。

怎么理解这里!!!

通过查阅资料,原因是我没有理解动态代理,这里的代理意思。

本质上,这里通过Proxy.newProxyInstance(classLoader,interface,handle),其中
- classLoader只需要和接口的interface保持一致,因为系统会自动生成一个类继承这些接口
- interface提供的是这些接口内部需要系统生成类的方法,也就是那个生成的类中会实现这些方法
- handle是专门处理这些方法的实现。

所以在这里当调用到IBinder中的queryLocalInterface()方法时候,我们生成了一个对IClipboard的代理,当执行IClipboard里面的每个方法的时候都回去BinderHookHandler拦截。所以代理返回的对象其实是系统自动为我们生成的实现那些接口的一个对象。

点这里看原因

  1. 最终进行处理
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 {
        // 把剪切版的内容替换为 "you are hooked"
        if ("getPrimaryClip".equals(method.getName())) {
            return ClipData.newPlainText(null, "you are hooked");
        }
        // 欺骗系统,使之认为剪切版上一直有内容
        if ("hasPrimaryClip".equals(method.getName())) {
            return true;
        }
        return method.invoke(base, args);
    }
}

此时我们已经把系统对于一个服务的hook点搞清楚了,那下来我们就多研究几个hook系统服务的点。

Hook其他系统服务

这个时候我们就不适用上面那么low的代码进行hook了,我把大神们写的DroidPlugin框架进行拆解,我们只进行一个服务的hook,其他服务按照框架任意添加,这样我们就能够处理任何服务的任何方法了。是不是想想很爽。

Hook NotificationManager

public class APP extends Application{
    @Override
    public void onCreate() {
        super.onCreate();
        try {
            PluginProcessManager.installHook(this);//入口进行装载hook
            PluginProcessManager.setHookEnable(true);//设置hook可用
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
    }
}

如何使用

就按照我们正常的使用逻辑就好,出现的效果就是当我们点击和取消toast的时候在后台有对应信息打印

public class MainActivity extends AppCompatActivity {
    private Button start;
    private Button end;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        start = (Button) findViewById(R.id.start);
        end = (Button) findViewById(R.id.end);
        final Toast toast = Toast.makeText(MainActivity.this, "toast", Toast.LENGTH_LONG);
        start.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                toast.show();
            }
        });
        end.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                toast.cancel();
            }
        });
    }
}

Hook类

这个类是Hook的基类,应该由控制能否hook的变量,和context,还有hook处理控制

public abstract class Hook {
    private boolean mEnable = false;//是否能进行Hook
    protected Context mHostContext;
    protected BaseHookHandle mHookHandles;//掌控Hook

    public void setEnable(boolean enable, boolean reInstallHook) {
        this.mEnable = enable;
    }
    public final void setEnable(boolean enable) {
        setEnable(enable, false);
    }
    public boolean isEnable() {
        return mEnable;
    }
    protected Hook(Context hostContext) {
        mHostContext = hostContext;
        mHookHandles = createHookHandle();
    }
    protected abstract BaseHookHandle createHookHandle();
    protected abstract void onInstall(ClassLoader classLoader) throws Throwable;
    protected void onUnInstall(ClassLoader classLoader) throws Throwable {
    }
}

对Binder的hook

只要是关于binder的hook,都需要继承该基类

abstract class BinderHook extends Hook implements InvocationHandler {

    private Object mOldObj;//原IBinder服务代理对象
    public BinderHook(Context hostContext) {
        super(hostContext);
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
            if (!isEnable()) {
                return method.invoke(mOldObj, args);
            }
            //通过Hook基类得到的方法处理类
            HookedMethodHandler hookedMethodHandler = mHookHandles.getHookedMethodHandler(method);
            if (hookedMethodHandler != null) {
                //传入原系统服务对象
                return hookedMethodHandler.doHookInner(mOldObj, method, args);
            } else {
                return method.invoke(mOldObj, args);
            }
        } catch (Exception e) {}
    }

    abstract Object getOldObj() throws Exception;

    void setOldObj(Object mOldObj) {
        this.mOldObj = mOldObj;
    }

    public abstract String getServiceName();

    //方法的入口
    @Override
    protected void onInstall(ClassLoader classLoader) throws Throwable {
        new ServiceManagerCacheBinderHook(mHostContext, getServiceName()).onInstall(classLoader);//先进行服务代理IBinder的替换
        mOldObj = getOldObj();//得到原始IBinder服务对象
        Class<?> clazz = mOldObj.getClass();
        List<Class<?>> interfaces = Utils.getAllInterfaces(clazz);
        Class[] ifs = interfaces != null && interfaces.size() > 0 ? interfaces.toArray(new Class[interfaces.size()]) : new Class[0];
        Object proxiedObj = MyProxy.newProxyInstance(clazz.getClassLoader(), ifs, this);
        MyServiceManager.addProxiedObj(getServiceName(), proxiedObj);//添加处理过的IBinder代理对象
    }
}

当执行过该类的onInstall方法之后,产生的结果就是将app进程中serviceManager对应的缓存中IBinder替换成我们自己的代理对象,并且当使用的时候进行我们mHookHandles的处理。

子类只是进行一些所需类进行实现的。

public class INotificationManagerBinderHook extends BinderHook {
    public static final String SERVICE_NAME = "notification";
    public INotificationManagerBinderHook(Context hostContext) {
        super(hostContext);
    }
    @Override
    protected BaseHookHandle createHookHandle() {
        return new INotificationManagerHookHandle(mHostContext);
    }
    public Object getOldObj() throws Exception {
        IBinder iBinder = MyServiceManager.getOriginService(SERVICE_NAME);
        return INotificationManagerCompat.asInterface(iBinder);
    }
    public String getServiceName() {
        return SERVICE_NAME;
    }
}

我们在BinderHook.onInstall()首先要进行服务的替换,那么服务的替换就是下面这个核心类:


public class ServiceManagerCacheBinderHook extends Hook implements InvocationHandler {

    private String TAG = ServiceManagerCacheBinderHook.class.getSimpleName();
    private String mServiceName;
    public ServiceManagerCacheBinderHook(Context hostContext, String servicename) {
        super(hostContext);
        mServiceName = servicename;
        setEnable(true);
    }
    @Override
    protected void onInstall(ClassLoader classLoader) throws Throwable {
        //拿到ServiceManager中的服务IBinder缓存列表
        Object sCacheObj = FieldUtils.readStaticField(ServiceManagerCompat.Class(), "sCache");
        if (sCacheObj instanceof Map) {
            Map sCache = (Map) sCacheObj;
            Object Obj = sCache.get(mServiceName);//得到原始服务对象
            if (Obj != null && false) {
                throw new RuntimeException("Can not install binder hook for " + mServiceName);
            } else {
                sCache.remove(mServiceName);//根据名称移除掉原有服务
                IBinder mServiceIBinder = ServiceManagerCompat.getService(mServiceName);//看移除了能否拿到原有服务
                if (mServiceIBinder == null) {
                    if (Obj != null && Obj instanceof IBinder && !Proxy.isProxyClass(Obj.getClass())) {
                        mServiceIBinder = ((IBinder) Obj);
                    }
                }
                if (mServiceIBinder != null) {
                    MyServiceManager.addOriginService(mServiceName, mServiceIBinder);//将原始服务IBinder存入缓存
                    Class clazz = mServiceIBinder.getClass();
                    List<Class<?>> interfaces = Utils.getAllInterfaces(clazz);
                    Class[] ifs = interfaces != null && interfaces.size() > 0 ? interfaces.toArray(new Class[interfaces.size()]) : new Class[0];
                    IBinder mProxyServiceIBinder = (IBinder) MyProxy.newProxyInstance(clazz.getClassLoader(), ifs, this);//服务代理类
                    sCache.put(mServiceName, mProxyServiceIBinder);//代替系统的服务
                    MyServiceManager.addProxiedServiceCache(mServiceName, mProxyServiceIBinder);//加入代理服务中
                }
            }
        }
    }
    /**
     * 这个方法代理的是原IBinder代理的服务类所有方法
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
            IBinder originService = MyServiceManager.getOriginService(mServiceName);
            if (!isEnable()) {
                return method.invoke(originService, args);
            }
            //对其进行拦截
            HookedMethodHandler hookedMethodHandler = mHookHandles.getHookedMethodHandler(method);
            if (hookedMethodHandler != null) {
                return hookedMethodHandler.doHookInner(originService, method, args);
            } else {
                return method.invoke(originService, args);
            }
        } catch (Exception e) {
        }
    }

    private class ServiceManagerHookHandle extends BaseHookHandle {
        private ServiceManagerHookHandle(Context context) {
            super(context);
        }
        @Override
        protected void init() {
            //此方法是:查看本进程是不是存在这个Binder对象
            //android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            sHookedMethodHandlers.put("queryLocalInterface", new queryLocalInterface(mHostContext));
        }
        class queryLocalInterface extends HookedMethodHandler {
            public queryLocalInterface(Context context) {
                super(context);
            }
            @Override
            protected void afterInvoke(Object receiver, Method method, Object[] args, Object invokeResult) throws Throwable {
                Object localInterface = invokeResult;//由于已经在缓存中将代理服务移除,所以此处为null
                //模仿的是asInterface()这个方法
                Object proxiedObj = MyServiceManager.getProxiedObj(mServiceName);
                if (localInterface == null && proxiedObj != null) {
                    setFakedResult(proxiedObj);
                }
            }
        }
    }
    @Override
    protected BaseHookHandle createHookHandle() {
        return new ServiceManagerHookHandle(mHostContext);
    }
}

目的就是替换

通过这种结构,我们就将处理进行独立,每次hook一个服务只需要继承BinderHook即可。详细代码请看DroidPlugin框架。如需我独立出来的框架进行学习微信关注:码老板