(PRACTICE) Uncrackable-Level 2

Uncrackable Level2 는 앱 실행과 동시에 앱이 종료된다.   APK 디컴파일 해서 분석해보자.


정보수집

Uncrackable Level1 과 동일하게 자바 코드 단에서 루팅 탐지 로직이 존재한다.


이전에 썼던 코드(AOS) Uncrackable-Level1를 재활용하자.

send("[*] hook hook hook !");
 
Java.perform(function () {
    console.log();
 
    var File = Java.use('java.io.File');
    File.$init.overload('java.lang.String', 'java.lang.String').implementation = function (str, su) {
        var stackTrace = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new());
 
        if (stackTrace.includes("sg.vantagepoint.a.b.a")) {
            console.warn('File.$init(str, "su") in sg.vantagepoint.a.b.a has been called !')
            console.log(`\tFile.$init(str, "su") => File.$init(${str}, "su")`);
            return this.$init("dumptrashdumptrash", su);
        }
 
        return this.$init(str, su);
    }
})

그럼 이전과 동일하게 MainActivity 로의 접근이 가능한데,   임의의 값을 입력하고 VERIFY 버튼을 누르면 “Nope…” 알림창이 발생한다.   private native boolean bar(byte[] bArr); 코드를 기드라에서 확인해보자.


소스코드를 통해서 해당 내용을 분석해보자.   this.m.a(obj) 결과에 따라 성공/실패 화면이 출력되는 것을 확인할 수 있는데,
obj 값은 애플리케이션 화면에서 VERIFY 버튼을 누를 때 입력한 텍스트 문자열을 의미한다.
그리고 이 때 사용되는 CodeCheck 클래스의 a 함수에서 네이티브 함수 bar(...)가 실행된다.


NATIVE FUNCTION (Java_sg_vantagepoint_uncrackable2_CodeCheck_bar)

(1) builtin_strncpy : local_50 변수에 “Thanks for all the fish” 문자열을 복사   (2) 사용자에게 입력받은 문자열을 __s1 변수에 할당   (3) 조건문에서 “Thanks for all the fish” 문자열과 사용자가 입력한 값을 비교   (4) 같은 값이면 1 반환, 다르면 0 반환  


앱에서 “Thanks for all the fish” 를 입력하면 20라인의 strncmp 값이 참이 되므로,
함수의 리턴 값이 1이 되어 “Success!” 메시지가 출력된다.
( 프리다 스크립트로 strncmp 함수의 인자 값을 변조해서 우회하는 방법도 가능하다. )


REFERENCE

  1. https://mas.owasp.org/crackmes/
  2. https://linears.tistory.com/entry/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-%EC%95%88%ED%8B%B0%EB%94%94%EB%B2%84%EA%B9%85
  3. https://m.blog.naver.com/koo__oo/222002009185?recommendTrackingCode=2
  4. https://hagsig.tistory.com/284