2010. 8. 4. 22:30

[번역] 안드로이드에서 숨겨진 시스템 서비스에 접근하기



원문: http://blog.codetastrophe.com/2008/12/accessing-hidden-system-service-apis-in.html

소스: http://code.google.com/p/codetastrophe/source/checkout

번역: SSKK (http://codemuri.tistory.com)

 

안드로이드 SDK 는 개발자들이 이를 이용하여 많은 것을 할 수 있게 해준다. 하지만 Public API 를 이용하여 접근할 수 없는 조금 흥미로운 시스템 기능이 있다. 약간의 작업과 http://source.android.com 에서 이용가능한 안드로이드 소스를 이용하여 여전히 이러한 기능에 접근하는 것이 가능하다. 작성한 코드는 여기에 있다.

 

노출하려고 했던 기능은 GPS 위성 정보를 수집하는 것이다 위성 자신의 데이터 (SV – Space Vehicles). 이러한 데이터를 수집하기 위한 콜백을 설정하기 위한 LocationManager 서비스 API 의 숨겨진 메소드들이 있다. 하지만 이 메소드들은 public API 를 통해서는 접근할 수 없다. 이 데이터에 접근할 수 있는 다양한 방법을 찾아보았고 그 중에서 가장 쉬운 방법은 IPC 인터페이스를 이용하여 LocationManager 서비스 자신에게 직접 메시지를 보내는 것이다.

 

LocationManager LocationManagerService

 

android.location 네임스페이스의 LocationManager API com.android.server 에 있는 LocationManagerService 에 대한 public 인터페이스이다. 이 서비스는 분리된 프로세스로 실행되고, API 는 안드로이드의 IPC 매커니즘 (Binder) 을 이용하여 그 서비스와 통신한다. 안드로이드 애플리케이션이 LocationManagerService 와 통신하기를 원할 때, API 객체에 대한 참조를 얻기 위해 이 API 호출을 사용한다.

 

LocationManager mLocationManager = (LocationManager)getSystemService(Context.LOCATION_SERVICE);

 

이후에는 일반적으로, 애플리케이션이 (requestLocationUpdate() 를 통해서) 로케이션 이벤트를 등록하기 위해 LocationManager API 를 사용할 것이다. 그런 다음 CellFinder 와 같은 아주 재미있고 흥미로운 것들을 하기 위해 이 데이터를 사용할 것이다.

LocationManager 소스 코드를 파고들면, registerGpsStatusListener() 라는 메소드가 있는 것을 볼 수 있다. 이 메소드는 GPS 위성 정보에 대한 업데이트를 얻을 수 있는 리스너를 설정한다 – GPS 뷰의 RPN, elevation, azimuth, 기타 등등. 원하는게 바로 이거다. 불행히도, 이 메소드 뿐만 아니라 GpsStatusListenerTransport 인자 타입 또한 SDK 에 포함된 android.jar 에는 존재하지 않는다. LocationManagerService 자신과 직접 통신함으로써 이러한 정보에 여전히 접근할 수 있다.

 

안드로이드 SDK 에서 사용가능한 API IPC 를 이용하여 시스템 서비스와 통신한다. 특히, 그 서비스와 통신하기 위해 AIDL 로 정의된 인터페이스를 사용한다. 그러한 서비스 IPC AIP 에 대한 AIDL 명세는 SDK 에서는 이용 가능하지 않지만 안드로이드 소스 코드로부터 그 명세를 얻을 수 있다. AIDL Java 클래스가 서비스에 메시지를 전송하거나 서비스로부터 메시지를 받기 위해 사용할 수 있는 클라이언트 stub 을 생성하는 데 사용된다.

 

LocationManagerService 에 직접 접근하기

 

LocationManagerService 와 통신하는 LocationManager 내의 클래스는 GpsStatusListenerTransport 이다. 이 클래스는 IGpsStatusListenerStub 클래스의 확장이고 AIDL 명세로부터 생성되었다 – IgpsStatusListener.aidl, ILocationListener.aidl 그리고 Address.aidl Ehgks 원하는 것을 얻기 위해 필요하다. 이 클래스들이 프로젝트에서 이용가능하기만 하면 (코드 변경을 피하기 위해서 android.location 네임스페이스를 선언했다 하지만 그것들이 속한 네임스페이스가 무엇인지는 중요하지 않다) 원하는 것을 얻는 것은 정말이지 무척 쉽다.

 

AIDL 인터페이스를 이용하여 LocationManagerService 를 위한 새로운 클라이언트를 작성하는 것이 가능하지만, 현존하는 클라이언트를 재사용하는 것이 실제로 더 쉽다. 새로운 연결을 설정하는 것은 많은 코드를 필요로 하고 이건 무척 짜증나는 일이다. 쉬운 방법은 시스템 서비스에 대한 핸들을 얻기 위해 Java Reflection API 를 이용하는 것이다.

 

Class c = Class.forName(mLocationManager.getClass().getName());

Field f = c.getDeclaredField(“mService”);

f.setAccessible(true);

ILocationManager mILM = (ILocationManager)f.get(mLocationManager);

 

자바에서 이처럼 private 필드에 접근하는 것은 일반적으로 제품 코드에서는 눈살을 찌푸리게 만들지만, 우리는 해커이고 데이터를 원하며 그 데이터를 얻기 위해 무엇이든 할 수 있기 때문에 이것을 멈출 수는 없다.

 

자 이제 ILocationManager API 에 접근하게 되었으므로, GPS 위성 상태 업데이트를 가져오기 위한 리스너를 등록하기 위해 호출할 수 있는 addGpsStatusListener() 메소드가 있다는 것을 알고 있다. 이 메소드는 IGpsStatusListener 객체를 취하고, IGpsStatusListener.stub 을 확장하는 클래스의 인스턴스를 생성할 수 있다. addGpsStatusListener() 에 이 객체를 전달함으로써 위성 상태에 대한 onSvStatusChanged() 콜백을 얻게 될 것이다. 멋지다!

 

이 환상적인 위성 정보를 얻기 전에, GPS 가 먼저 활성화 되어야 한다. 샘플 app 에서는, LocationManager 를 추가하면서 GPS 를 활성화하기 위해 정규 LocationManager API 를 사용하였다. 이 모든 노력의 결과는 어떤 위성에 관한 약간의 정보를 보여주는 쓸모 없고 지루한 애플리케이션이다.

 

 

저런

 

만약 당신이 서비스 코드를 파헤친다면, GPS 상태 업데이트를 위한 리스너를 추가하기 위해서는 어떠한 권한도 요구되지 않는다는 것을 알게 된다. 이것은 사소하고 위험도가 낮은 정보 누설 침범에 해당한다. 애플리케이션이 만약 올바른 권한을 가지고 있지 않다면 GPS 로케이션 데이터에 대해 접근할 수 있어서는 안된다. 만일 애플리케이션이 GPS 상태 업데이트를 주시하는 서비스를 생성한다면 ( 그러나 GPS location 업데이트가 아니다, GPS location 업데이트는 ACCESS_FINE_LOCATION 권한을 필요로 한다 ), 다른 모든 애플리케이션이 GPS 를 켜게 되었을 때 이러한 업데이트를 얻게 될 것이다. 이 데이터를 이용하여 휴대전화의 위치를 찾는 것은 약간의 수학을 이용하면 가능하다. 정확한 시간, GPS 위성의 사용자에 대한 상대 위치, 그리고 그 당시 GPS 위성의 절대 위치를 알고 있어야 한다. 시간과 상대 위치는 휴대전화로부터 얻을 수 있고, 위성의 정확한 위치는 http://www.navcen.uscg.gov/GPS/almanacs.htm 에서 찾을 있다. 연습삼아 이렇게 하는 방법에 관해서는 독자에게 남겨두겠다.

 

.

관련글:
http://codemuri.tistory.com/entry/시스템-서비스에-접근하기