안드로이드 빈더훅의 실현

10542 단어
1. 약술
Binder Hook은 현재 App에서 사용하는 시스템 서비스 서비스를 Hook에서 제거할 수 있습니다.Location Manager의 경우 Location Manager를 가져올 때 2단계입니다.첫째, Ibinder 객체 가져오기;둘째, Ibinder 객체는 asInterface()를 통해 Location Manager Service 객체로 전환됩니다.마지막으로 Location Manager를 초기화합니다. 응용 프로그램 층에서 사용하는 것은 모두 Location Manager입니다.Hook의 대략적인 원리는 서비스 관리자가 어떤 Binder를 가져올 때 로컬에 캐시된 Binder가 있으면 프로세스를 뛰어넘어 Binder를 요청하지 않는다는 것이다.Service Manager가 로컬 캐시를 질의할 때 사용자 정의된 Custom Binder 객체를 캐시에 추가하여 프로세스 간에 시스템에 요청하지 않도록 할 수 있습니다.ILocationManagerStub.asInterface 메서드는 사용자 정의된 서비스 객체를 반환합니다.이 안에는 사용자 정의 대상을 사용해야 하는 두 곳이 있다.Hook의 일부 기능만 있고 다른 기능은 보존해야 하기 때문에 동적 에이전트로 사용자 정의 Binder와 사용자 정의 서비스를 만듭니다.
다음 내용을 이해하기 전에 다음과 같은 지식을 알아야 한다.
  • 약간의 Binder 지식으로 Ibinder가 IInterface로 전환하는 대체적인 절차를 알 수 있다.
  • Java의 동적 에이전트

  • 2. Context에서 시스템 서비스 가져오기 프로세스
    Activity 같은 클래스는 시스템 서비스를 가져올 때 getSystem 서비스 (서비스Name) 방법을 호출해서 가져옵니다.
    Title: Context     Service   
    Context->SystemServiceRegistry: getSystemService(name)
    SystemServiceRegistry->ServiceFetcher: getSystemService(name)
    ServiceFetcher->ServiceManager: getService(contetx)
    ServiceManager->return: getService(name)
    

    Context의 getSystemService () 방법은 SystemServiceRegistry의 getSystemService () 방법을 호출합니다.SystemServiceRegistry 의 SYSTEM 상수SERVICE_FETCHERS, 이것은 Map입니다.ServiceName과 해당 ServiceFetcher가 저장되어 있습니다.ServiceFetcher는 구체적인 서비스를 만드는 데 사용되는 클래스입니다.ServiceFetcher의 주요 방법은createService() 방법입니다.ServiceFetcher의createService() 방법에서 ServiceManager.getService(name) 방법이 호출되었습니다.Location Manager에 해당하는 ServiceFetcher의 경우 create Service() 메소드 소스는 다음과 같습니다.
    @Override
    public LocationManager createService(ContextImpl ctx) {
        IBinder b = ServiceManager.getService("location");
        return new LocationManager(ctx, ILocationManager.Stub.asInterface(b));
    }
    

    만약 우리가 Location Manager의 getLastKnownLocation () 방법을 수정하려고 한다면 (다음은 모두).우리가 해야 할 일은 서비스 관리자에게getService("location")는 사용자 정의 Binder를 반환합니다.먼저 이 방법의 간소화 후의 원본 코드를 살펴보자.
    public static IBinder getService(String name) {
        IBinder service = sCache.get(name);
        if (service != null) {
            return service;
        } else {
            return getIServiceManager().getService(name);
        }
    }
    

    sCache는 시스템에 요청한 Binder를 캐시하는 Map입니다.만약 우리가 이 방법을 우리 자신의 binder로 되돌려야 한다면, sCache에 있는 사용자 정의 Binder를 미리 입력하면 된다.put 전에 사용자 정의 Binder를 만들어야 합니다.이 Binder는 ILocationManager에 의해Stub.asInterface 처리 후 사용자 정의 LocationManager Service를 반환할 수 있습니다.먼저 Binder의 asInterface() 구현을 살펴보겠습니다.
    public static android.location.ILocationManager asInterface(android.os.IBinder obj) {
        if ((obj == null)) {
            return null;
        }
        android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
        if (((iin != null) && (iin instanceof android.location.ILocationManager))) {
            return ((android.location.ILocationManager) iin);
        }
        return new android.location.ILocationManager.Stub.Proxy(obj);
    }
    

    query Local Interface () 방법을 사용자 정의 서비스로 되돌려서if 문장 내부를 걷고else를 걷지 않으면 Hook이 성공한 셈입니다.
    오류: asInterface (binder) 는 binder를 형식 변환을 한 번 한 것입니다. 실제 XXX service = XXX.Stub.asInterface(binder) 의 반환값은 binder의 출처에 따라 두 가지 상황이 있습니다.
  • 크로스 프로세스에서 서비스의 유형은Test입니다.Stub.Proxy
  • 같은 프로세스에서 서비스의 유형은Test입니다.Stub X.Stub.asInterface(binder);얻은 반환값은 반드시 binder 자신이 아니라 시스템 서비스를 호출할 때 binder 자신이 아닐 것이다.

  • 3 사용자 지정 서비스 및 Binder 작성
    3.1 사용자 지정 서비스 객체
    만약 우리가 시스템의 LocationManager가 되돌아오게 하려는 위치 정보가 모두 천안문(116.23, 39.54)에 있다고 가정하자.Location Manager 서비스의 getLastLocation () 방법을 모두 (116.23, 39.54) 로 되돌려야 합니다.시스템의 이 서비스 대상을 직접 가져올 수 없기 때문에, 먼저 시스템의 Location Manager 서비스를 반사적으로 받을 수 있습니다.그런 다음 getLastLocation() 방법을 차단합니다.
    package com.zhp.binderhook;
    
    import android.location.Location;
    import android.location.LocationManager;
    import android.os.IBinder;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    
    /**
     *         Handler
     * @author ZHP
     * @since 16/12/25 17:36
     */
    
    public class ServiceHookHandler implements InvocationHandler {
    
        private Object mOriginService;
    
        /**
         * @param binder      Binder  
         */
        @SuppressWarnings("unchecked")
        public ServiceHookHandler(IBinder binder) {
            try {
                //       Binder  ,     Service   ,         
                //  : this.mOriginService = ILocationManager.Stub.asInterface(binder);
                Class ILocationManager$Stub = Class.forName("android.location.ILocationManager$Stub");
                Method asInterface = ILocationManager$Stub.getDeclaredMethod("asInterface", IBinder.class);
                this.mOriginService = asInterface.invoke(null, binder);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
    
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            switch(method.getName()) {
                case "getLastLocation":
                    //           
                    Location location = new Location(LocationManager.GPS_PROVIDER);
                    location.setLongitude(116.23);
                    location.setLatitude(39.54);
                    return location;
    
                default:
                    return method.invoke(this.mOriginService, args);
            }
        }
    }
    

    3.2 사용자 정의 Binder 객체
    기본 Binder 객체는query LocalInterface() 메서드를 호출할 때 기본 서비스 객체를 반환합니다.3.1의 사용자 지정 서비스로 돌아가기를 원합니다.그래서 여기서 query Local Interface () 방법을 차단합니다.
    package com.zhp.binderhook;
    
    import android.os.IBinder;
    import android.os.IInterface;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    /**
     *         Handler,       Binder
     * @author ZHP
     * @since 16/12/25 17:36
     */
    
    public class BinderHookHandler implements InvocationHandler {
    
        private IBinder mOriginBinder;
    
        private Class ILocationManager;
    
        @SuppressWarnings("unchecked")
        public BinderHookHandler(IBinder binder) {
            this.mOriginBinder = binder;
            try {
                this.ILocationManager = Class.forName("android.location.ILocationManager");
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            switch (method.getName()) {
                //         Service
                case "queryLocalInterface":
                    ClassLoader classLoader = mOriginBinder.getClass().getClassLoader();
                    Class[] interfaces = new Class[] {IInterface.class, IBinder.class, ILocationManager};
                    ServiceHookHandler handler = new ServiceHookHandler(this.mOriginBinder);
                    return Proxy.newProxyInstance(classLoader, interfaces, handler);
    
                default:
                    return method.invoke(mOriginBinder, args);
    
            }
        }
    }
    

    4. 사용자 정의 Binder를 Service Manager에 배치
    사용자 정의 Binder가 생기면 서비스 관리자의 sCache 변수에 주입하면 훅이 완성됩니다~
    package com.zhp.binderhook;
    
    import android.content.Context;
    import android.os.IBinder;
    
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.util.Map;
    
    /**
     * @author ZHP
     * @since 16/12/25 17:56
     */
    
    public class HookManager {
    
        @SuppressWarnings("unchecked")
        public static boolean hookLocationManager() {
            try {
                // 1.        Binder
                Class ServiceManager = Class.forName("android.os.ServiceManager");
                Method getService = ServiceManager.getDeclaredMethod("getService", String.class);
                IBinder binder = (IBinder) getService.invoke(null, Context.LOCATION_SERVICE);
    
                // 2.        Binder
                ClassLoader classLoader = binder.getClass().getClassLoader();
                Class[] interfaces = {IBinder.class};
                BinderHookHandler handler = new BinderHookHandler(binder);
                IBinder customBinder = (IBinder) Proxy.newProxyInstance(classLoader, interfaces, handler);
    
                // 3.   ServiceManager  sCache
                Field sCache = ServiceManager.getDeclaredField("sCache");
                sCache.setAccessible(true);
                Map cache = (Map) sCache.get(null);
    
                // 4.      Binder       Binder
                cache.put(Context.LOCATION_SERVICE, customBinder);
                sCache.setAccessible(false);
                return true;
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
        }
    }
    

    5. 테스트
    package com.zhp.binderhook;
    
    import android.Manifest;
    import android.content.Context;
    import android.content.pm.PackageManager;
    import android.location.Location;
    import android.location.LocationManager;
    import android.os.Bundle;
    import android.support.v4.app.ActivityCompat;
    import android.support.v7.app.AppCompatActivity;
    import android.util.Log;
    import android.view.View;
    import android.widget.Toast;
    
    public class MainActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            // Hook
            HookManager.hookLocationManager();
        }
    
        /**
         *       
         */
        private void requestLocation() {
            //       
            if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
                Log.i("   ", "      ");
                Toast.makeText(this, "      ", Toast.LENGTH_SHORT).show();
                return;
            }
    
            //        
            LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
            Location location = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
            String message = "(" + location.getLongitude() + ", " + location.getLatitude() + ")";
            Toast.makeText(this, message, Toast.LENGTH_LONG).show();
            Log.i("   ", message);
        }
    
        public void onClick(View view) {
            this.requestLocation();
        }
    }
    

    온클릭이 호출될 때 토스트와 로그는 천안문의 좌표(116.23, 39.54)를 표시한다.훅 성공 증명!
    Binder Hook으로 Activity Manager를 제거할 수도 있습니다.

    좋은 웹페이지 즐겨찾기