Android 劫持並模擬運行Service範例

場景:

假設某SDK的aar有個服務,但是此服務必須通過Service啓動,並且改Service可能是多進程的,如果不想啓動service或避免使用多進程,以此避免和AMS通信,該如何實現呢

方案:

答案是:通過Context去Hook啓動行爲

如:DataService extend Service{....}

實現方法如下


public class DataContextImplHook extends ContextThemeWrapper {

    private Service hookTargetService = null;
    private AtomicInteger hookStartupCounter = null;
    private final Map<KeyRecord, IBinder> connectionPool = new HashMap<>();
    private boolean isAliveService = false;
    private Handler mainThreadHandler  =  null;

    public DataContextImplHook(Context base) {
        super(base, 0);
        mainThreadHandler = new Handler(Looper.getMainLooper());
    }

    boolean isWorkThread(){
       return mainThreadHandler.getLooper()==Looper.myLooper();
    }

    @Override
    public ComponentName startService(Intent service) {
        ComponentName component = service.getComponent();
        if (component != null && component.getClassName().equals(DataService.class.getName())) {
            if(isWorkThread()) {
                if (hookStartupCounter == null) {
                    hookStartupCounter = new AtomicInteger(1);
                }
                if (hookTargetService == null) {
                    isAliveService = true;
                    hookTargetService = new DataService();
                    hookTargetService.onCreate();
                   
                }
                hookTargetService.onStartCommand(service, 0, hookStartupCounter.getAndIncrement());
                return component;
            }
            ComponentName componentName = submit(new SyncTask<ComponentName>() {
                @Override
                public ComponentName doTask() {
                    return startService(service);
                }
            });
            return componentName;
        }
        return super.startService(service);
    }


    @Override
    public boolean bindService(Intent service, ServiceConnection conn, int flags) {
        ComponentName component = service.getComponent();
        if (conn != null && component != null && component.getClassName().equals(DataService.class.getName())) {
            if(isWorkThread()) {
                if (hookStartupCounter == null) {
                    hookStartupCounter = new AtomicInteger(1);
                }
                if (hookTargetService == null) {
                    isAliveService = true;
                    hookTargetService = new DataService();
                    hookTargetService.onCreate();
                 
                }
                IBinder binder = queryConnectionPool(service);
                if (binder != null) {
                    hookTargetService.onRebind(service);
                    connectionPool.put(new KeyRecord(conn, service), binder);
                    conn.onServiceConnected(component, binder);
                    return true;
                }
                binder = hookTargetService.onBind(service);
                if (binder != null && binder.isBinderAlive()) {
                    connectionPool.put(new KeyRecord(conn, service), binder);
                    conn.onServiceConnected(component, binder);
                }
                return binder != null;
            }
            Boolean submit = submit(new SyncTask<Boolean>() {
                @Override
                public Boolean doTask() {
                    return bindService(service,conn,flags);
                }
            });
            return submit!=null?submit.booleanValue():false;
        }
        return super.bindService(service, conn, flags);
    }

    private IBinder queryConnectionPool(Intent service) {
        if (service == null) {
            return null;
        }
        Set<KeyRecord> keySet = connectionPool.keySet();
        KeyRecord key = null;
        for (KeyRecord record : keySet) {
            if (service.filterEquals(record.intent)) {
                key = record;
                break;
            }
        }
        if (key == null) {
            return null;
        }
        IBinder binder = connectionPool.get(key);
        if (binder == null || !binder.isBinderAlive()) {
            connectionPool.remove(key);
            return null;
        }
        return binder;
    }

    @Override
    public void unbindService(ServiceConnection conn) {

        execute(new Runnable() {
            @Override
            public void run() {
                doUnBind(conn);
            }
        });
        super.unbindService(conn);
    }

    private boolean doUnBind(ServiceConnection conn) {
        List<KeyRecord> recordList = new LinkedList<>();
        for (Map.Entry<KeyRecord, IBinder> entry : connectionPool.entrySet()) {
            KeyRecord key = entry.getKey();
            if (key == null) continue;
            if (key.connection == conn) {
                recordList.add(key);
            }
        }
        for (KeyRecord record : recordList) {
            connectionPool.remove(record);
            record.connection.onServiceDisconnected(record.intent.getComponent());
        }
        if (!isAliveService && connectionPool.isEmpty()) {
            if (hookTargetService != null) {
                hookTargetService.onDestroy();
                hookTargetService = null;
            }
        }
        if(!recordList.isEmpty()){
            return true;
        }
        return false;
    }

    @Override
    public boolean stopService(Intent service) {
        ComponentName component = service.getComponent();
        if (component != null && component.getClassName().equals(DataService.class.getName())) {
           if(isWorkThread()){
               isAliveService = false;
               if (hookTargetService != null) {
                   if(connectionPool.isEmpty()) {
                       hookTargetService.onDestroy();
                       hookTargetService = null;
                   }
               }
               return hookTargetService !=null;
           }
            Boolean submit = submit(new SyncTask<Boolean>() {
                @Override
                public Boolean doTask() {
                    return stopService(service);
                }
            });
           return submit!=null?submit.booleanValue():false;
        }
        return super.stopService(service);
    }

    static class KeyRecord {
        Intent intent;
        ServiceConnection connection;

        public KeyRecord(ServiceConnection conn, Intent intent) {
            this.intent = intent;
            this.connection = conn;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (!(o instanceof KeyRecord)) return false;
            KeyRecord record = (KeyRecord) o;
            return (Objects.equals(intent, record.intent) || intent.filterEquals(record.intent)) &&
                    Objects.equals(connection, record.connection);
        }

        @Override
        public int hashCode() {
            return Objects.hash(intent, connection);
        }
    }

    static abstract class SyncTask<V> {
        V result;
        final CountDownLatch countDownLatch = new CountDownLatch(1);

        public void execute() {
            try {
                result = doTask();
            }catch (Throwable throwable){
                throw throwable;
            }finally {
                countDownLatch.countDown();
            }

        }

        public abstract V doTask();

        public V getResult() {
            try {
                countDownLatch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return result;
        }

        public void forceCountDown() {
            countDownLatch.countDown();
        }
    }

    public <V> V submit(final SyncTask<V> task) {
        if (task == null) {
            return null;
        }
        try {
            this.mainThreadHandler.post(new Runnable() {
                @Override
                public void run() {
                    task.execute();
                }
            });
        }catch (Exception e){
            e.printStackTrace();
            task.forceCountDown();
        }
        return task.getResult();
    }

    public void execute(final Runnable runnable) {
        if (isWorkThread()) {
            runnable.run();
            return;
        }
        this.mainThreadHandler.post(runnable);
    }

}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章