아까 설명했듯이 엑티비티가 실행되면
처음에 onCreate 메소드가 호출됩니다.
onCreate 메소드가 길어서 일부만 가져왔습니다.
코드를 보면
저장된 인스턴스 상태를 가져오며
엑티비티 메인으로 값을 설정하는 등의 초기 설정을 합니다.
그다음으로는 onStart 메소드가 호출되어야 합니다.
하지만 onStart 메소드는 보이지 않습니다.
아마도 super 클래스의 메소드를 오버라이드 하지 않고
그냥 상속받아서 사용하는 것 같다고 추측하고 있지만,
정확히는 잘 모르겠습니다.
이런건 개발을 해보면서 배워야 하는데,
안 해봐서 잘 모르겠습니다.
이럴 경우에는 다시 onStart 메소드로 돌아가서,
onCreate 메소드에서 어떤 함수들을 호출하는지 확인해봐야 합니다.
onCreate 메소드의 마지막 부분을 확인해 보면,
com/kozhevin/rootchecks/util/CheckTask 엑티비티에서
init 메소드를 호출하고 execute 메소드를 호출합니다.
CheckTask 엑티비티에서 init 메소드부터 확인해 보면
중간에 asyncTask를 호출하는 부분이 있습니다.
네, 비동기로 뭔가를 하는 것 같습니다.
어플리케이션에서는 엑티비티간의 전환은 보이지 않았으므로
백그라운드로 작업을 하는 것 같습니다.
같은 엑티비티 내에서
백그라운드 작업을 하는
doInBackground 메소드입니다.
쭉 읽다가 보면
결과값이 비어있는 경우
MeatGrinder 엑티비티의 getInstance 메소드와
isLibraryLoaded 메소드를 호출합니다.
MeatGrinder의 isLibraryLoaded 메소드에서는
native-lib이라는 이름의 라이브러리를
로드하는 것을 확인할 수 있으며,
여러 메소드를 native 하게 로드하는 것을 확인할 수 있습니다.
해당 라이브러리를 ida로 열어보면,
어플리케이션에서 루팅을 확인하는
키워드들이 보이는 것을 확인할 수 있습니다.
자 이제부터가 드디어 본 주제였던
루팅을 탐지하는 방법에 대하여 이야기해보겠습니다.
안드로이드에서 루팅을 하게 된다면, 크게
- 루팅 하는 과정에서
- 루팅을 활용하는 과정에서
흔적이 발생합니다.
루팅을 탐지할 때는
기본적으로 이러한 흔적으로 루팅을 탐지합니다.
예를 들면 매번 루트의 권한을 획득하는 건 귀찮으니깐,
이 과정을 생략하기 위해 su 바이너리를 이용합니다.
하지만 이 바이너리의 존재로 루팅이 탐지될 수도 있습니다.
또한 루트 권한을 활용하는
다른 어플리케이션의 흔적으로
루팅이 되었는지 확인할 수 있습니다.
가장 처음으로는 build.prop 파일로
루팅이 되었는지를 확인할 수 있습니다.
이 build.prop 파일은 제조사에서 제품을 출시할 때
제품에 대한 정보나 build 정보 등을 포함하여 출시합니다.
그중에서 ro.build.tags 는
출시 당시에 'release-key'로 되어 있지만,
루팅 과정에서 test-keys 나 dev-keys 같은
다른 키로 변경되는 경우가 있으며,
이 키가 변경되었는지 확인을 통해
루팅을 탐지할 수 있습니다.
그 외에도
ro.debuggable, service.adb.root,
ro.secure, sys.initd, ro.build.selinux 등을
확인하여 루팅이 되어 있는지를
확인하기도 합니다.
아까 예를 들었던 su 바이너리를 통해서
루팅을 탐지할 수 있다는 것을 기억할 것입니다.
근데 혹시 이 su 바이너리가
정확히 뭐하는 바이너리인지 아시나요?
su는 switch user 즉 사용자를 변경하는 바이너리로,
인자 없이 단독으로 su가 오게 된다면
기본적으로 root로 인식하여
root로 사용자를 변경할 수 있습니다.
따라서 이 바이너리의 존재로 루팅을 탐지하기도 합니다.
그다음으로
또 su 바이너리를 찾는 것입니다.
오른쪽에 보이는 폴더들의 리스트에서 su 바이너리가 있는지 찾습니다.
아까 which su를 통해서도 su 바이너리를 찾았고,
방금 전에는 특정 폴더들에서 su 바이너리가 있는지 찾았습니다.
이 둘의 차이점은 무엇일까요?
차이점은 환경변수에서
PATH 변수에 등록된 주소에서 차이점이 있습니다.
which 명령어의 경우에는
이 PATH 변수에 등록된 환경변수에서
인자로 들어온 이름의 실행파일을 찾습니다.
즉 이 PATH 변수에 등록이 안된 경로에 대해서는,
which 명령어로 su 바이너리가 존재하는지 확인할 수 없습니다.
따라서 su 바이너리가 자주 있거나,
있을 가능성이 높은 폴더들에 대하여
추가로 확인을 하는 것입니다.
그다음으로는 Superuser.apk 어플리케이션의
유무를 확인합니다.
이 superuser.apk 는
다른 어플리케이션에서 su를 이용할 수 있는 권한을
관리해주는 어플리케이션으로,
이 어필리케이션에 접근할 수 있는지 확인하는 방식으로,
루팅을 탐지합니다.
그다음으로는 busybox의 존재로
루팅을 탐지할 수 있습니다.
안드로이드의 경우 리눅스를 기반으로 하여,
그 커널 위에서 동작하는 것은 다 들어보셨을 겁니다.
이렇게 리눅스 기반이기는 하지만 완전한 리눅스가 아니기에,
기존에 사용하던 여러 리눅스 명령어를 사용하지 못합니다.
따라서 이 busybox를 설치하여
여러 명령어를 사용할 수 있게 합니다.
busybox는 여러 스크립트 들을 포함하는 실행파일로,
이 busybox로 여러 명령어들을 대체하여
명령어들을 사용할 수 있는 것처럼 해줍니다.
다음은 xposed 관련 파일들로
루팅을 탐지할 수 있습니다.
xposed는 일종의 프레임워크로
여러 모듈을 적용하여
시스템이나 다른 응용프로그램의 동작을
변경할 수 있게 해 준다고 합니다.
저는 써본 적이 없어서 잘은 모르겠습니다.
앞에서 언급한 파일들 외에도
magisk 같은 루팅을 이용하는 파일들이
존재하는지 확인합니다.
또한 폴더의 권한으로
루팅 여부를 판단할 수 있습니다.
system 폴더를 예로 들어 설명하겠습니다.
system 폴더의 경우 기본적으로 읽기 전용 폴더로,
이는 root 권한으로도 수정할 수 없습니다.
이 안에 있는 파일들을 수정하기 위해서는
system 폴더를 remount 하여야 하는데,
이러한 폴더 권한의 변경으로
루팅을 탐지하는 경우도 있습니다.
마지막으로 후킹을 탐지하는 방법입니다.
후킹이란 프로그램에서 함수, 이벤트, 메시지 등이 호출될 때,
이를 중간에서 낚아채가는 것을 말합니다.
이 안드로이드에서 후킹 하는 것에 관련된 부분은
주성이가 더 잘 설명해 줄 것이라 믿고,
저는 후킹으로 뭘 할 수 있는지
정도만 보여드리겠습니다.
* 해당 슬라이드의 사진 부분은 공개할 수 없습니다.
이것은 어느 메신저에서
자체 프로토콜을 빌드하는 부분의
결과 값을 후킹 한 것입니다.
전에 상섭이형 발표에서는
치트엔진으로 하셨다고 들은 것 같은데,
이렇게 프리다로 후킹을 해서도
그 내용을 확인할 수도 있습니다.
과제라고는 하는데 그냥 선택사항입니다.
첫 번째 과제는 오늘 native root checker에서
루팅을 탐지하는 방법을 소개했는데,
이를 참고하여 루팅 탐지를
우회하는 스크립트를 작성하는 것입니다.
두 번째 과제는
오늘 소개한 루팅 탐지 방법 외에도
여러 창의적인 방법들이 존재하는데
그중 하나를 찾아 정리해 보고,
그것은 어떤 식으로 탐지를 하는 건지
공부해 보는 것입니다.
할 사람들은 기한 없이 하시면서 질문 주시면 되고,
제출은 onsoim@gmail.com으로 보내주시고 알려주시면 됩니다.
그리고 안드로이드 쪽 공부를 한다면 도움이 될 자료들입니다.
첫 번째는 아시는 분은 아는 그 발표자료입니다.
물론 내용은 좀 삭제되어 있지만
난독화 쪽으로 흥미로운 내용을
많이 포함하고 있습니다.
한번 쭉 보시고, 나중에 더 공부하고 다시 보셔도 좋을 것 같습니다.
두 번째는 아까 간략하게 설명한 엑티비티 라이프사이클에 관한 자료입니다.
정리가 아주 잘 되어 있습니다.
이상으로 끝내겠습니다. 감사합니다.
'0x10 Android' 카테고리의 다른 글
Android :: Android101 (2/3) - APK 분석방법 (0) | 2020.03.01 |
---|---|
Android :: Android101 (1/3) - APK 배경지식 (0) | 2020.03.01 |
Android :: KakaoTalk DB 복호화 (5) | 2019.09.09 |
Android :: 중고로운 평화나라 (0) | 2019.04.01 |