2019. 11. 9. 12:26ㆍSecurity ★ Development/시스템
안드로이드 오브젝트(Object) 형 변수의 어드레스, c에서 말하는 포인터 주소를 얻는 방법을 검색해보면 보통 '얻을 수 없다' 혹은 hashcode에 대한 소개가 나타납니다.
일반적으로 안드로이드 어플을 만들 때는, jni 및 c를 써도 object 어드레스 자체에 접근하는 건 어렵습니다. 관련된 인터페이스, api를 제공하지 않고 있기 때문인데 그래서 c단에서 안드로이드 어플과 같이 상호작용하는 함수를 만들 때는 jni 함수들을 이용하게 됩니다.
jni함수 내부에서 안드로이드 object 형 변수의 메모리에 접근이 발생하게 됩니다.
안드로이드 프레임워크, native library, art 등의 수정이 가능하다면 memory addresss에 직접 접근하여 수정, 확인할 수 있습니다.
이러한 작업을 해주는 일이 발생한다면 그건 'jni 함수를 구현한다' 라고 볼 수 있습니다.
실제 수정, 동작 확인까지 해본 후 본 내용을 작성합니다.
address를 구하는 것과 관련된 자바 object 변수는 크게 3가지 타입으로 나뉩니다.
String, primitive type array, Object(reference type array)
하나만 파악하면 다른것도 쉽게 파악할 수 있습니다. 조금 특이하게 별도 mirror type이 있는 String에 대해서 중점으로 보겠습니다.
mirror type이란 자바 layer의 클래스에 해당하는 c단의 class이며 이는 c/c++ 코딩할 때 제공되어지는 클래스와는 다릅니다.
아래 내용은 AOSP를 받아 소스코드를 보면서 따라가시는 것을 추천드립니다.
1. Java layer
String.java 혹은 StringFactory.java를 보면 native 함수들이 있습니다. 실제 java layer에서 데이터에 접근하기 위에서 쓰이는 내부 jni 함수들입니다. 예를들면 charAt()같은 것들이요.
안드로이드 어플에서 address에 접근하기 위한 구현부의 시작 부분이 됩니다. 필요하다면 원하는 클래스에 추가해줍니다. 여기까지는 평범한 jni 함수 추가와 같습니다.
2. JNI
쉬운 charAt()기준으로 보겠습니다. 이 함수 내부부터가 핵심입니다. 이 구현부는 java_lang_string.cc에 있습니다.
정확한 위치는 AOSP를 받아서 찾아보시고 혹시 커스텀으로 구현해주신다면 비슷하게 custom.cc와 같이 추가하고 내부 함수를 구현해주시면 되겠습니다.
charAt을 보면 JNIEnv를 받아 soa의 Decode를 통해 변환을 하고 다시 ->CharAt()을 호출합니다.
soa는 native object accessor라고 보시면 되고 env를 통해 현재 실행중인 app에 해당하는 accessor를 생성한 다음 Decode를 통해 jobject를 원하는 mirro type으로 캐스팅합니다.
캐스팅이 끝나면 그 클래스에 해당하는 CharAt()을 호출합니다.
soa, mirror type 등이 일반 어플 개발시 public하게 제공되는 것이 아니기 때문에 이런 방식의 구현이 어렵습니다.
3. Mirror class
CharAt()은 string-inl.h에 구현되어 있습니다. String과 관련된 mirror 쪽 파일은 string-inl.h 하나가 아니라 string.cc, string.h도 있습니다. 이 파일들이 있는 위치에는 다른 object, array 관련한 구현도 있습니다. 여기까지 오면 '아 여기구나' 하실 겁니다.
CharAt() 구현을 보면 다른것보다 GetValueCompressed() 혹은 GetValue()가 있고 이를 메소드 호출시 전달받은 index로 [index] 접근하여 리턴해줍니다.
바로 GetValue() 등이 포인터를 갖다준다는 것이죠.
GetValue()는 string.h에 있습니다. value_[0]을 리턴해줍니다. value_는 클래스 맨 아래 zero size array로 선언되어있습니다.
이게 바로 실제 자바 layer의 String 클래스가 저장하는 문자열이 있는 포인터이며 memory address입니다.
이부분을 수정해주면 어플 단에서 봤을 때 String 변수 확인시 수정이 된다는 것을 확인할 수 있습니다.
정리하면 커스텀 구현시 jobject를 mirrot::String으로 변환 후 GetValue()를 해주면 address를 구해 수정이 가능해진다는 것입니다.
4. 기타
비슷한 방식으로 primitive type array object, reference type array object 및 object의 data memory address에도 접근할 수 있습니다.
어플에서 추가해준 커스텀 클래스의 Object의 경우 value관련해서 field를 가져오는 부분이 있습니다. 이를 활용해야합니다.
또한 이 부분은 garbage collection과도 연관이 있습니다. 만약 object가 고정되있지 않다면 위에서본 value_의 address는 garbage collection이 발생할 때마다 바뀔 것입니다. 근처의 Alloc 관련한 부분을 보면 확인할 수 있습니다.
그리고 String의 경우 immutable type이기 때문에 어플단에서 String 변수의 값을 변경하려는 경우(="xxx"; 를 통해) 위의 value_가 바뀌지 않는다는 점, constant pool의 존재 등에 대해서도 파악하면 이 부분의 이해에 좋을 것 같습니다.
위에 짧게 말씀드린 Decode, Alloc과 관련하여 heap, gc쪽 소스도 art 근처에 있으니까 보시면 jobject에서 어떻게 캐스팅을 해서 포인터 호출을 하는지, object 메모리 할당은 어떻게 되는건지 등의 이해에도 도움이됩니다.
'Security ★ Development > 시스템' 카테고리의 다른 글
리버스 텔넷(Reverse telnet) (0) | 2020.06.06 |
---|---|
Parcel::remove() not yet implemented! (0) | 2020.05.23 |
system_server broadcast hooking (0) | 2018.03.17 |
윈도우 공유폴더 및 널 세션 제거 (0) | 2017.10.29 |