Why should I know this?

Android에서 모든 Activity를 후킹하는 가장 깔끔한 방법. 본문

Knowledge/Android

Android에서 모든 Activity를 후킹하는 가장 깔끔한 방법.

die4taoam 2019. 2. 18. 23:14

 

Android에서 모든 Activity는 ActivityThread에 mInstrumentation이란 녀석이 관장한다.

Android에서 제공하는 Test Framework도 이녀석을 사용하는데...

이를 활용하면 재미있는 작업을 할 수 있다.

Application 보호용 솔루션을 자동화 테스트하는데 이만한 방법이 없다는 생각을 했었다.

 

왜 그런 생각을 했는지 소스코드와 함께 살펴보자.

아래는 reflection 패턴을 활용하여 mInstrumentation을 교체하는 소스코드이다.

 private void changeInstrumetation() { 
 	try { 
    // replace instrumentation. Application -> LoadedApk -> ActivityThread  
    Object mLoadedApk = new reflector<Object>(this.getApplication(), "mLoadedApk").get(); 
    Object ActivityThread = new reflector<Object>(mLoadedApk, "mActivityThread").get(); 
    reflector<Instrumentation> mInstrumentation = new reflector<Instrumentation>(ActivityThread, "mInstrumentation"); 
    mInstrumentation.set(new Instrumentation()); 
    } 
    catch (NoSuchFieldException e) { e.printStackTrace(); } 
    catch (IllegalAccessException e) { e.printStackTrace(); } 
}

 

그리고 아래의 코드는 교체된 Instrumentation에서 하는 작업이다.

 

 public void callActivityOnCreate(Activity activity, Bundle icicle, PersistableBundle persistentState) 
 { 
	super.callActivityOnCreate(activity, icicle); 
	ViewGroup viewGroup = (ViewGroup) activity.getWindow().getDecorView(); 
	findAllViews(viewGroup); 
} 

public void findAllViews(ViewGroup viewGroup) 
{ 
	for (int i = 0, n = viewGroup.getChildCount(); i < n; i++) { 
    	View child = viewGroup.getChildAt(i); 
        if (child instanceof ViewGroup) { 
        	findAllViews((ViewGroup) child); 
        } else { 
        	String viewClassName= child.getClass().getName(); 
            Log.d( TAG, "==============================================================="); 
            Log.d( TAG, "=[Find View] : " + viewClassName); 
            Log.d( TAG, "==============================================================="); 
        } 
    } 
}

 

Android에서 Activity교체가 이뤄지는 작업은 mInstrumention이 callActivityOnCreate()를 호출하여 이뤄진다. 해당 메소드의 호출이 이뤄지면 ActivityThread에 존재하는 전역 변수인 activity가 현재 activity로 변경된다. 그러므로 super.callActivityOnCreate를 호출한뒤 activity에 존재하는 view의 정보를 모두 뽑아와 로그로 뿌려줄 수 있다.

 

 

여기서 추가 설명.

Android Test-Framework는 위의 방식으로 Activity을 뽑아와 개발자가 기재한 방식대로 테스트를 진행한다. 만약 이를 활용할 수 없는 상황이면? 직접 mInstrumentation에 정의해서 넣으면 된다. 이런 방식으로 Test-Framework를 활용할 수 없는 환경에서도 자동화된 테스트를 할 수 있다.

Comments