이번에는 Uncrackable의 4레벨중 2레벨을 해볼 것이다.
1레벨에서도 암호화된 문자열을 복호화 하여 해결하였는데 이번에는 어떤 문제가 있을까?
한번 시작해보도록 하자.
apk파일은 이전에 첨부하였던 깃헙 URL에서 다운 받아서 진행하였다.
똑같이 adb install UnCrackable-Level2.apk를 해주어 어플을 설치해주면

앱이 설치되어 있는 것을 확인하고, 실행시켜 보겠다.

역시나 이번에도 루팅 탐지 솔루션이 작동하고 있는듯 하다.
jadx로 디컴파일하여 코드 로직을 확인해보겠다.

1레벨에서와 같이 sg.vantagepoint.uncrackable2 패키지의 Mainactivity에 onCreate()메서드로 루팅 탐지 로직이 존재하는 것을 발견하였다.
b클래스의 a와b와c의 메서드를 확인하여 중에 하나에서 true가 발생하면 루팅이 되었다고 판단하고 Root detected!를 출력한다.

b클래스에서는 Uncrackable 1레벨에서 존재하는 루팅 탐지 솔루션과 똑같은 조건들이 있으므로 설명은 생략하겠다.

마찬가지로 탐지가 됐을때 MainActivity클래스의 a()메서드에서 메시지를 띄우고 OK버튼을 눌렀을때 System.exit(0) 즉, 시스템을 종료시키게 된다.
이번에는 조금 다른 접근 방법으로 System.exit(0)을 무력화 시키는것이 아닌 b클래스의 메서드들의 조건들을 무력화 시키는 방법을 사용해보도록 하겠다.
Java.perform(function(){
var byp = Java.use("sg.vantagepoint.a.b");
byp.a.implementation = function(){return false}
byp.b.implementation = function(){return false}
byp.c.implementation = function(){return false}
console.log("[+]루팅 탐지 우회 성공.")
})
각각 a패키지의 b클래스의 a,b,c메서드들의 반환값을 false로 바꾸어주었다.
이제 한번 프리다로 실행을 시켜보면?

오류 없이 실행이 되었다.
공기계 화면에서 확인을 해보겠다.

루팅 탐지 솔루션을 우회하고 들어왔다!
이제 다음 작업으로는 또 Secret String을 찾아야 한다.

아무거나 쳤을때 역시나 Nope...이 뜨게 된다.
다시 jadx로 가서 디컴파일된 코드를 보겠다.

역시나 MainActivity클래스에 verify메서드에 해당 로직이 존재한다.
그러나뭔가 조금은 다른데 1레벨에서는 a.a(obj)였는데 이번에는 this.m.a(obj)다!
바로 m을 클릭해서 넘어가보니

CodeCheck 클래스의 인스턴스임을 알수 있다.
CodeCheck 를래스가 어떤 동작을 하는지 확인해보겠다.

bar()라는 네이티브 메소드를 선언해 주고 a()메서드 내부의 bar()메서드에서 사용자의 입력값을 byte로 변환하여 집어넣고 있는것을 알수있다.
여기에서 중요한 개념이 나오게 되는데 바로 네이티브 메서드이다.
JNI란 네이티브 코드의 메서드를 자바에서 사용할수 있도록 해주거나 그 반대도 가능하게 해주는 프레임워크이다.

자바에서 네이티브 코드를 사용하기 위해서는 두가지 조건이 필요한데
1. Native 로 선언해줄 것.
2. System.load 또는 System.loadLibrary를 통해 외부 라이브러리를 불러올 것.
그렇다면 이 apk에서는 어디에 외부 라이브러리를 불러오고 있을까?
답은 바로 MainActivity에서 CodeCheck m을 찾았을때 그 아래에 있다.

System.loadLibrary메서드로 foo파일을 불러오고 있는것을 알 수 있다.

리소스에 라이브러리 파일들이 있기는 하지만 아키텍처에 따라 다르기 때문에 어떤 아키텍처의 so파일을 사용하는지 알아보아야 한다.

ps로 확인한 결과 20144 PID 로 동작하고 있는것을 확인했다.
프로세스 정보가 저장되는 /proc디렉터리로 이동하여 확인해 보겠다.

cat maps | grep uncrackable2 명령어를 실행해주니 다음과 같은 라이브러리 파일들이 불러와지고 있는것을 볼 수 있다.
나는 arm 아키텍처 라이브러리 파일인것을 알아냈다.
여기에서 두가지 선택지가 있는데 adb pull 명령어로 가져와도 되고
adb pull /data/app/owasp.mstg.uncrackable2-1/lib/[아키텍처]/libfoo.so C:\Users\[사용자]\Downloads ,
APK easy tool을 다운받아 디컴파일해서

\APK Easy Tool v1.60 Portable\1-Decompiled APKs\UnCrackable-Level2\lib에 있는 라이브러리 파일을 가져와도 된다.
나는 APK easy tool을 이용해보고 싶어서 후자의 방법을 선택했다.
그렇게 libfoo.so파일을 디스어샘블러 프로그램으로 돌려주면 되는데....
IDA freeware에서는 arm아키텍처의 라이브러리 파일을 지원하지 않는다...
그래서 다른 방법을 찾다가 생각난것은 바로 기드라!
기드라는 무료로 사용이 가능한 IDA pro에 대응이 가능한 디스어셈블러라고 한다.
JDK 11이상 있어야하니 유의하여 사용하자.
어쨌든 기드라에서 프로젝트를 생성해서 libfoo.so파일을 집어넣고 디스어셈블 해보았다.


그랬더니 해석된 어샘블리 문자들이 주르륵 나온다!
옆에 함수들을 클릭하면 자동으로 디컴파일도 해서 오른쪽 [Decompile]에서 보여준다.
이대로 하나하나 내리면서 찾기에는 눈아프고 힘들기 때문에 좌측 상단의 Search 탭의 Label History를 통해 찾아주겠다.
우리가 찾는건 bar 함수이다.


찾아서 도착해보았더니 CodeCheck가 있다!
여기 부분의 함수에 힌트가 있을거라 생각해서 바로 디컴파일된 코드를 보겠다.

보면 local_34에 24길이의 배열을 선언해주고, Thanks for all thr fish 를 복사해주었다.
그리고 __s1에서 연산을 한것을 local_34와 srtncmp를 이용해 비교해서 변수의 길이가 같다면 true로 반환하고 아니면 false를 반환한다.
일단은 Secret String이 하드코딩 되어있는것 같아 입력을 해보면

???? 이게 맞네 어쨌든 답은 저게 맞다는게 입증됐다.
이렇게 끝나면 좀 짜치니까 프리다로 Secret String을 추출해보겠다.
결과적으로는 공기계에서는 안됐다.
왜인지는 모르겠지만

다음과 같은 오류가 지속적으로 발생하였다.
arm 아키텍처 게다가 64비트는 괜찮은데 32비트에서만 발생하는 문제인데, 조사해보니 arm 32비트 자체의 인라인 정책으로 인해서 메모리에 접근이 안되는것 같다더라.
나와 같은 오류로 고통받는 글들을 읽어보았지만 특별한 해결책이 없어서 그냥 이번 문제는 코드와 알게된 개념만 공유하고 마치도록 하겠다..
function bypass(){
Java.perform(function(){
var byp = Java.use("sg.vantagepoint.a.b");
byp.a.implementation = function(){return false}
byp.b.implementation = function(){return false}
byp.c.implementation = function(){return false}
console.log("[+]루팅 탐지 우회 성공.")
})
}
function find_secret(){
console.log("AAAAAAAAAAAAAAAAAAAAAAA를 입력하면 비밀이 나올지도 모릅니다..")
Interceptor.attach(Module.findExportByName("libfoo.so","strncmp"),{
onEnter: function(args){
var input = Memory.readUtf8String(args[0]);
var output = Memory.readUtf8String(args[1]);
if (input.startsWith("AAAA")){
console.log(`[+]Secret String:${output}`)
}
}
})
}
bypass();
find_secret();
새로운 개념이 또 등장하는데 바로 Interceptor.attach("[.so]","[메서드]", {callback})다.
보통 자바에서 외부 모듈을 사용하고 있을때 그 외부모듈을 후킹해서 메서드를 가져오는데 사용한다.
C 모듈에서 사용되는 메서드를 가져오는데 사용되고 메서드의 이름까지 알고 가져오는것이 좋다.
ptr('0x[주소]')로 사용해도 되긴 하지만 다른곳에서 사용되고 있는 메모리 까지 모두 불러와져서 충돌이 발생하고 있어 웬만해서 추천하지 않는다고 한다.
{callback} 부분에는 onEnter과onLeave가 들어가게 되는데 각각
그리고 Module.findExportByName(".so","메서드")인데 Interceptor.attach랑 같이 쓴걸 봤을것이다.
이거는 libfoo.so가 기본 라이브러리가 아닌 커스텀 라이브러리이기 때문에 주소값과 메서드명이 일치하지않아 불러오기 위해서 사용하였다.
간단하게 말해서 Interceptor.attach(Module.findExportByName("libfoo.so","strncmp")의 의미는 커스텀 모듈 libfoo.so에서 기본 모듈인 libc.so의 strcmp 갖다 쓰는거 메모리에서 후킹할거야! 라는 의미이다.

참고로 마지막으로 에뮬레이터에서 시도해봤는데 정상적으로 작동하는 모습이다.
게다가 libfoo.so 라이브러리가 아닌 libc.so 라이브러리를 인터셉트 해서 strncmp에 후킹을 해야 에러가 안뜨고 잘된다....
libfoo.so를 불러오면 다음과 같은 오류가 나오는데

아마 libfoo.so에서 strncmp를 기본 라이브러리인 libc.so에서 외부 참조 하고있는데 libfoo.so에서 strncmp를 메모리에 로드하기 전이라서 그런것 같다.
어쨌든 에뮬레이터라도 작동이 되긴 해서 다행이다.
이번에도 새롭게 알게된 개념들이 아주 많다.
JNI부터 시작해서 APK easy tool도 써보고 항상 IDA만 써봤는데 이번 기회에 기드라 사용법도 익히게 되서 아주 좋았다.
개인적으로 기드라가 더 좋은거 같다 ㅎㅎ
마지막 문제점은 해결책이 나오게 되면 게시글을 수정하겠다.
다음 Uncrackable Level3로 돌아오겠다.
- 다음 포스트에 계속...