[턴] 안드로이드 훅 기술 이해 및 간단한 실전

9030 단어
1. 훅이란 무엇인가
훅은 영어로 번역하면'갈고리'라는 뜻인데 우리는 언제 이'갈고리'를 사용합니까?Android 운영 체제에서 시스템은 자체 이벤트 배포 메커니즘을 유지 관리합니다.응용 프로그램은 트리거 이벤트와 백엔드 논리 처리를 포함하고 이벤트 절차에 따라 한 걸음 한 걸음 아래로 실행된다.한편,'갈고리'는 사건이 종점으로 전송되기 전에 사건의 전송을 캡처하고 감시하며 갈고리에 걸린 사건처럼 사건을 연결할 때 자신의 특정한 사건을 처리할 수 있다는 뜻이다.
훅의 이 기능은 자신의 코드를 훅에 걸린 프로그램의 프로세스에 융합시켜 목표 프로세스의 일부분이 될 수 있도록 한다.API Hook 기술은 시스템의 API 함수 실행을 리디렉션하는 API 실행 결과를 변경하는 데 사용되는 기술입니다.안드로이드 시스템에서 샌드박스 메커니즘을 사용했는데 일반 사용자 프로그램의 프로세스 공간은 모두 독립되어 프로그램의 운행이 서로 방해되지 않는다.이로써 우리는 하나의 프로그램을 통해 다른 프로그램의 어떤 행위를 바꾸기를 바라는 생각은 직접적으로 실현될 수 없지만 훅의 출현은 이런 문제를 해결하는 길을 개척해 주었다.물론 훅의 대상과 훅의 후처리 방식에 따라 훅은 메시지 훅, API 훅 등 다양한 종류로 나뉜다.
2. 자주 쓰는 훅 프레임
  • 안드로이드의 훅 메커니즘에 대해 대체적으로 두 가지 방식이 있다.
  • 루트 권한을 가지려면 훅 시스템을 직접 사용하면 모든 앱을 제거할 수 있다.
  • 루트 권한은 없지만 훅 자체만 가능하고 시스템의 다른 앱에 대해 아무것도 할 수 없다.
  • 몇 가지 훅 방안:
  • Xposed

  • 교체를 통해/system/bin/appprocess 프로그램이 Zygote 프로세스를 제어하여 app프로세스를 시작하는 동안 XposedBridge가 로드됩니다.jar 이 Jar 패키지는 Zygote 프로세스와 생성된 Dalvik 가상 머신에 대한 납치를 완성합니다.Xposed는 전원을 켤 때 모든 훅 펀치에 대한 납치를 완료하고 원래 펀션이 실행된 전후에 사용자 정의 코드를 추가합니다.
  • Cydia Substrate Cydia Substrate 프레임워크는 애플 사용자에게 탈옥 관련 서비스 프레임워크를 제공했고 당연히 안드로이드 버전도 내놓았다.Cydia Substrate는 모든 프로세스의 코드를 수정할 수 있는 코드 수정 플랫폼입니다.Java든 C/C++(native 코드)로 작성되었든 Xposed는 Hook app 만 지원합니다process의 Java 함수입니다.
  • Legend Legend는 안드로이드가 Root 환경에서 사용하지 않는 Apk Hook 프레임워크로 이 프레임워크의 코드는 디자인이 간결하고 유니버설성이 높으며 역방향 프로젝트를 할 때 일부 Hook 장면에 적합하다.대부분의 기능이 자바 층에 놓여 있어 호환성이 매우 좋다.원리는 이렇다. 신구 방법에 대응하는 가상 기기의 데이터 구조를 직접 구성한 다음에 정보를 교체하여 메모리에 쓰면 된다.

  • 3. Java 반사로 API Hook 구현
    안드로이드 플랫폼의 가상 기기에 자바와 반사하는 방식을 주입하여 안드로이드 가상 기기에서 함수를 호출하는 방식(ClassLoader)을 바꾸어 자바 함수의 방향을 바꾸는 목적을 달성한다. 여기서 우리는 이런 조작을 자바 API Hook이라고 부른다.
    다음은 훅 뷰의 OnClickListener를 통해 훅의 사용 방법을 설명합니다.
    먼저 View의 set On Click Listener 방법에 들어가서 우리는 On Click Listener 대상이 Listener Info라는 내부 클래스에 저장된 것을 보았다. 그 중에서 mListener Info는 View의 구성원 변수이다.Listene Info에는 View의 각종 감청 사건, 예를 들면 On Click Listener, On Long Click Listener, On Key Listener 등이 저장되어 있다.
        public void setOnClickListener(@Nullable OnClickListener l) {
            if (!isClickable()) {
                setClickable(true);
            }
            getListenerInfo().mOnClickListener = l;
        }
    
        ListenerInfo getListenerInfo() {
            if (mListenerInfo != null) {
                return mListenerInfo;
            }
            mListenerInfo = new ListenerInfo();
            return mListenerInfo;
        }
    
    

    우리의 목표는 Hook On Click Listener이기 때문에 View에 감청 이벤트를 설정한 후 On Click Listener 대상을 교체하고 사용자 정의 조작을 주입해야 합니다.
        private void hookOnClickListener(View view) {
            try {
                //    View   ListenerInfo   
                Method getListenerInfo = View.class.getDeclaredMethod("getListenerInfo");
                getListenerInfo.setAccessible(true);
                Object listenerInfo = getListenerInfo.invoke(view);
                //        OnClickListener   
                Class> listenerInfoClz = Class.forName("android.view.View$ListenerInfo");
                Field mOnClickListener = listenerInfoClz.getDeclaredField("mOnClickListener");
                mOnClickListener.setAccessible(true);
                View.OnClickListener originOnClickListener = (View.OnClickListener) mOnClickListener.get(listenerInfo);
                //       OnClickListener       OnClickListener
                View.OnClickListener hookedOnClickListener = new HookedOnClickListener(originOnClickListener);
                mOnClickListener.set(listenerInfo, hookedOnClickListener);
            } catch (Exception e) {
                log.warn("hook clickListener failed!", e);
            }
        }
    
        class HookedOnClickListener implements View.OnClickListener {
            private View.OnClickListener origin;
    
            HookedOnClickListener(View.OnClickListener origin) {
                this.origin = origin;
            }
    
            @Override
            public void onClick(View v) {
                Toast.makeText(MainActivity.this, "hook click", Toast.LENGTH_SHORT).show();
                log.info("Before click, do what you want to to.");
                if (origin != null) {
                    origin.onClick(v);
                }
                log.info("After click, do what you want to to.");
            }
        }
    
    

    여기까지, 우리는 OnClickListener에 성공했습니다. 클릭하기 전과 클릭한 후에 어떤 조작을 실행할 수 있고 우리의 목적을 달성할 수 있습니다.다음은 호출된 부분입니다. Button에 OnClickListener를 설정한 후 훅 동작을 실행합니다.버튼을 클릭하면 로그의 인쇄 결과는: Before click → onClick → After click입니다.
            Button btnSend = (Button) findViewById(R.id.btn_send);
            btnSend.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    log.info("onClick");
                }
            });
            hookOnClickListener(btnSend);
    
    

    4. 훅으로 앱 내 알림 차단
    응용 프로그램에 많은 SDK가 접속되면 SDK 내부에서 시스템 서비스인 Notification Manager를 사용하여 알림을 보내기 때문에 알림을 관리하고 제어하기 어렵다.현재 우리는 훅 기술로 일부 알림을 차단하고 응용 내의 알림 발송 조작을 제한한다.
    알림을 보내는 방법은 Notification Manager의 notify 방법입니다. API를 따라 들어가 보겠습니다.INotification Manager 유형의 객체를 사용하고 enqueueNotification WithTag 방법을 사용하여 알림 전송을 완료합니다.
        public void notify(String tag, int id, Notification notification)
        {
            INotificationManager service = getService();
            …… //       
            try {
                service.enqueueNotificationWithTag(pkg, mContext.getOpPackageName(), tag, id,
                        stripped, idOut, UserHandle.myUserId());
                if (id != idOut[0]) {
                    Log.w(TAG, "notify: id corrupted: sent " + id + ", got back " + idOut[0]);
                }
            } catch (RemoteException e) {
            }
        }
    
    
        private static INotificationManager sService;
    
        /** @hide */
        static public INotificationManager getService()
        {
            if (sService != null) {
                return sService;
            }
            IBinder b = ServiceManager.getService("notification");
            sService = INotificationManager.Stub.asInterface(b);
            return sService;
        }
    
    

    Inotification Manager는 크로스 프로세스 통신의 Binder 클래스이고 sService는 NMS(Notification Manager Service)가 클라이언트의 에이전트로 sService에 위탁하여 NMS에 전달하는 통지를 보낸다. 구체적인 원리는 여기서 더 이상 자세히 연구하지 않고 시스템 서비스와 응용의 통신 과정을 이해할 수 있다.
    SService는 정적 구성원 변수이며 한 번만 초기화됩니다.SService를 사용자 정의로 바꾸면 되잖아요. 그렇죠.다음은 대량의 자바 반사와 동적 에이전트를 사용하는데 특히 코드의 작성에 주의해야 한다.
        private void hookNotificationManager(Context context) {
            try {
                NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
                //       sService
                Method getService = NotificationManager.class.getDeclaredMethod("getService");
                getService.setAccessible(true);
                final Object sService = getService.invoke(notificationManager);
    
                Class iNotiMngClz = Class.forName("android.app.INotificationManager");
                //      INotificationManager
                Object proxyNotiMng = Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{iNotiMngClz}, new InvocationHandler() {
    
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        log.debug("invoke(). method:{}", method);
                        if (args != null && args.length > 0) {
                            for (Object arg : args) {
                                log.debug("type:{}, arg:{}", arg != null ? arg.getClass() : null, arg);
                            }
                        }
                        //      sService   ,     
                        // return method.invoke(sService, args);
                        //     ,     
                        return null;
                        //          Tag   ID     
                    }
                });
                //    sService
                Field sServiceField = NotificationManager.class.getDeclaredField("sService");
                sServiceField.setAccessible(true);
                sServiceField.set(notificationManager, proxyNotiMng);
            } catch (Exception e) {
                log.warn("Hook NotificationManager failed!", e);
            }
        }
    
    

    Hook의 시기는 가능한 한 빨라야 합니다. 우리는attachBaseContext에서 조작합니다.
        @Override
        protected void attachBaseContext(Context newBase) {
            super.attachBaseContext(newBase);
            hookNotificationManager(newBase);
        }
    
    

    이렇게 해서 우리는 알림에 대한 차단을 완성했다. 이를 통해 알 수 있듯이 훅 기술은 정말 강하다. 많은 플러그인화의 원리는 훅 위에 세워진 것이다.
    요약:
  • 훅의 선택점: 정적 변수와 단일 예. 대상을 만들면 쉽게 변하지 않고 위치를 정하기 쉽기 때문이다.
  • 훅 프로세스:
  • 훅 포인트를 찾는데 원칙은 정적 변수나 단일 대상이고 가능한 훅 퍼블릭의 대상과 방법이다.
  • 인터페이스라면 동적 에이전트를 사용할 수 있는 적합한 에이전트 방식을 선택합니다.
  • 대들보를 훔쳐서 기둥을 바꾸는 - 원시 대상을 대리 대상으로 교체한다.
  • 안드로이드의 API 버전이 비교적 많고 방법과 클래스가 다를 수 있으므로 API의 호환 작업을 잘 해야 한다.

  • 참고 자료:
  • 동적 주입 기술
  • 안드로이드 플러그인화 원리 해석 - 훅 메커니즘의 동적 에이전트
  • 안드로이드 훅 전면 침입 모니터
  • 링크:https://www.jianshu.com/p/4f6d20076922출처:

    좋은 웹페이지 즐겨찾기