UnCrackable Level3(후킹을 활용한 실습 문제 분석)

2026. 1. 15. 16:08·cs/sec
728x90

1. 0단계 준비

1.1. 환경 구성

  • CMD창에 adb install 명령어 기입 후, 파일을 드래그하여 실행
  • 휴대폰에 unCrackable-Level3가 설치 됨
 adb install C:\Users\A3SC\Downloads\UnCrackable-Level3.apk

 

1.2. 사용 도구(Tool)

  • 후킹 시 사용되는 도구(Tool)는 다음과 같음
구분 내용
Jadx Dex를 Java로 변환하는 디컴파일러
Ghidra NSA에서 제작한 Java 기반의 리버싱 프레임워크
JDK 자바 개발 키트
Apktool APK 파일을 smali 코드로 변환해주는 툴
  • 참고) JDK와 Apktool은 ApkEasy Tool로 대체 가능

 

2. 1단계 루팅 또는 변조 탐지

2.1. 1단계 분석

  • 목적: 루팅 또는 변조 탐지로 인해 발생하는 애플리케이션 종료 로직을 후킹하여 정상 실행 상태로 변경 
Tip1. AndroidManifest.xml > MainActivity > OnCreate() > 알림창을 타고 들어감
• AndroidManifest.xml 파일에 안드로이드 4대 컴포넌트(Activity, Service, Broadcast Receiver, Content Provider)에 대한 정보가 명시
• Application이 실행될 때 가장 먼저 실행되는 Activity를 MainActivity라고 칭함
• Activity 생명주기에서 onCreate()는 Activity가 실행되면 가장 먼저 호출

 

1) 파일을 실행하면 "Rooting or tampering detected. This is unacceptable. The app is now going to exit."이라는 알림창이 뜸

  • 루팅된 기기에서 unCrakable Level3에 접근하면 알림창 발생

그림 1. 루팅 또는 탐지 알림창

 

2)  AndroidManifest.xml에서 MainActivity를 더블 클릭하면 해당 코드로 이동

그림 2. AndroidMainifest.xml에서 MainActivity로 이동

 

3)  MainActivity에서 OnCreate()를 보면 “Rooting or tampering detected.” 의 showDialog 알림창 메세지를  발생하는 코드가 존재

그림 3. MainActivity에서 onCreate()함수로 이동

 

4) 알림창이 발생하는 조건을 보기 위해 RootDetection을 더블 클릭하여 이동

  • 코드를 해석해보면 RootDetection클래스의 checkRoot1, checkRoot2, checkRoot3 메서드 중 하나라도 true가 나오면 루팅 탐지 알림 창이 나옴

그림 4. 루팅 탐지 조건 코드

 

5)  RootDetection의 반환값을 모두 false로 변조하면 루팅 탐지 조건이 충족되지 않아 알림창이 표시되지 않음

그림 5. 루팅 탐지 코드

 

  • RootDetection.checkRoot1() 메서드

자바에서 OS의 환경 변수의 값이 필요할 경우 System.getenv( )메서드를 사용

즉, 환경변수 값을 읽어 su명령어가 존재하는지 판단하여 루팅 탐지를 수행

public static boolean checkRoot1() {
    for (String str : System.getenv("PATH").split(":")) {
        if (new File(str, "su").exists()) {
            return true;
        }
    }
    return false;
}
  • RootDetection.checkRoot2()메서

App이 “test-keys”로 sign 되어있으면 루팅으로 탐지  일반적으로 정식 버전에는 빌드 태그에 release-keys 가 포함되며, test-keys가 포함되어있으면 개발용 또는 비공식적으로 빌드된 기기로 판단

public static boolean checkRoot2() {
    String str = Build.TAGS;
    return str != null && str.contains("test-keys");
}
  • RootDetection.checkRoot3() 메서드

String[]에 포함되는 경로의 파일이 하나라도 존재하면 루팅된 환경으로 판단

public static boolean checkRoot3() {
    for (String str : new String[] {
            "/system/app/Superuser.apk",
            "/system/xbin/daemonsu",
            "/system/etc/init.d/99SuperSUDaemon",
            "/system/bin/.ext/.su",
            "/system/etc/.has_su_daemon",
            "/system/etc/.installed_su_daemon",
            "/dev/com.koushikdutta.superuser.daemon/"
    }) {
        if (new File(str).exists()) {
            return true;
        }
    }
    return false;
}

2.1. 1단계 우회 코드

//1단계
Java.perform(function(){
   var root = Java.use("sg.vantagepoint.util.RootDetection");
   root.checkRoot1.implementation = function(){
      var retval = this.checkRoot1();
      console.log(retval)
      return false;
   }  
});


Java.perform(function(){
   var root = Java.use("sg.vantagepoint.util.RootDetection");
   root.checkRoot2.implementation = function(str){
      return false;
   }  
});


Java.perform(function(){
   var root = Java.use("sg.vantagepoint.util.RootDetection");
   root.checkRoot3.implementation = function(){
      var retval = this.checkRoot3();
      console.log(retval)
      return false;
   }  
});

 

3. 2단계 Frida hooking 을 통한 so 파일 내 탐지 로직 우회

3.1. 2단계 분석

  • 목적: 애플리케이션 종료 원인을 파악하기 위해 APK의 so 파일 분석
  • CMD창에서 Frida를 사용하여 -U옵션으로 USB로 연결된 기기를 지정하고, -f 옵션으로 애플리케이션을 새로 실행한 뒤, JS 스크립트를 통해 후킹을 수행
frida -U -f owasp.mstg.uncrackable3 -l level3.js

 

1) 방법1. 1단계 코드로 진행하면 오류가 발생하며 애플리케이션이 종료

분석: BackTrace를 확인한 결과, libfoo.so의 goodbye를 호출하며 종료

*BackTrace는 프로그램 실행 중 특정 시점, 특히 오류가 발생했을 때 현재 활성화된 함수 호출들의 목록(호출 스택)을 의미

false
Process crashed: Trace/BPT trap

***
*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
Build fingerprint: 'samsung/beyond0lteks/beyond0:12/SP1A.210812.016/G970NKSU7HWD3:user/release-keys'
Revision: '26'
ABI: 'arm64'
Processor: '2'
Timestamp: 2026-01-15 14:05:19.560207028+0900
Process uptime: 2s
Cmdline: owasp.mstg.uncrackable3
pid: 21316, tid: 21368, name: tg.uncrackable3  >>> owasp.mstg.uncrackable3 <<<
uid: 10309
signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
    x0  0000000000000000  x1  0000000000005378  x2  0000000000000006  x3  0000000000000000
    x4  00000072504d8000  x5  00000072504d8000  x6  00000072504d8000  x7  00000000011b64fc
    x8  0000000000000083  x9  e3172e2f6de7ce6d  x10 0000000000000001  x11 0000000000000000
    x12 0000006f19448270  x13 0000000000000023  x14 0000000000000000  x15 000068f570eb0c56
    x16 0000007242a1a598  x17 00000072429f7060  x18 0000006ea7e68000  x19 0000006f194514c8
    x20 0000006f194514d8  x21 0000006f194514da  x22 0000006f194514e0  x23 0000007077d384b8
    x24 0000006f19449cb0  x25 0000006f19449cb0  x26 0000006f19449ff8  x27 00000000000fe000
    x28 00000000000fc000  x29 0000006f19449a00
    lr  0000006f19451090  sp  0000006f19449a00  pc  00000072429f7068  pst 0000000000000000
backtrace:
      #00 pc 00000000000a0068  /apex/com.android.runtime/lib64/bionic/libc.so!libc.so (tgkill+8) (BuildId: 88b933fc529619f5e58bc6d5510017ee)
      #01 pc 000000000000308c  /data/app/~~WoqSrnOx_1UfioKiBXzoNA==/owasp.mstg.uncrackable3-EleZC6oF0_wivYTSkcIY5A==/lib/arm64/libfoo.so (goodbye()+12) (BuildId: 7f891562d834beba2a395a2a6c5ab8d4e55cb3d8)
      #02 pc 00000000000031ac  /data/app/~~WoqSrnOx_1UfioKiBXzoNA==/owasp.mstg.uncrackable3-EleZC6oF0_wivYTSkcIY5A==/lib/arm64/libfoo.so (BuildId: 7f891562d834beba2a395a2a6c5ab8d4e55cb3d8)
      #03 pc 00000000000b4b38  /apex/com.android.runtime/lib64/bionic/libc.so!libc.so (__pthread_start(void*)+264) (BuildId: 88b933fc529619f5e58bc6d5510017ee)
      #04 pc 0000000000052c60  /apex/com.android.runtime/lib64/bionic/libc.so!libc.so (__start_thread+64) (BuildId: 88b933fc529619f5e58bc6d5510017ee)
***
[SM G970N::owasp.mstg.uncrackable3 ]->

Thank you for using Frida!
PS C:\Users\A3SC\Desktop\code>

 

방법2. MainActivity에서 System.loadLibrary("foo")로 libfoo.so가 메모리에 올라감을 확인

  • MainActivity 상단에 선언된 init 함수에 native키워드가 존재하는 것을 보아, 해당 함수는 네이티브 함수임을 확인

그림 6. MainActivity init

 

    • 외부라이브러리 파일 MainActivity 클래스 최하단에 static 코드 존재
    • 자바 static블록은 클래스가 메모리로 로딩될 대 자동으로 실행되며, 클래스가 로딩 시 한 번만 호출

그림 7. MainActivity static 블록

 

 

2) libfoo.so(APK의 so 파일)은 ELF 포멧의 네이티브 공유 라이브러리이기 때문에 정적 분석 대상임

 

그림 8. libfoo.so 파일(APK의 so 파일)

 

3) Ghidra를 이용해 분석

  • Ghidra, JDK, APKtool을 설치(JDK, APKtool은 APK Easy Tool로 대체 가능)
  • 설치 후, CMD창에 APKTool을 사용해 APK파일을 디컴파일하는 명령어 실행
java -jar C:\Windows\apktool.jar d C:\Users\A3SC\Desktop\code\UnCrackable-Level3.apk

https://lakedata.tistory.com/187에서 2단계: Ghidra 분석에 설치 방법 자세히 있음

 

4) libfoo.so파일 Ghidra에서 분석

    • 안드로이드 기기 아키텍처에 해당하는 arm64용 libfoo.so 파일을 Ghidra로 가져와 분석
    • libfoo.so파일에 goodbye함수가 존재하며, 해당 함수는 exit(0)를 호출하여 애플리케이션을 종료시키는 역할임을 확인

 

그림 9. goodbye함수 Show Call Tree 이동

 

 

  • goodbye 상위함수 호출을 확인해야 함
Tip2. Ghidra에서 Function 확인 추천
  • Listing에서 함수에 커서를 놓고, 마우스 왼쪽 키를 누르고 References > Show Call Trees

그림10 . goodbye함수 Show Call Tree 이동

그림 11. goodbye함수 Function Call Tree

 

  • 상단에 Graph > Block Flow로 Graph(Bock Flow)에서 함수 호출 흐름을 따라감

그림 12. goodbye함수 Graph

 

  • FUN_001030d0을 더블 클릭하거나 or 키보드에서 G 누르고 입력창에 해당하는 코드 기입

그림13. Go to 이동

 

  • Decomipe에서 해당 코드를 볼 수 있음
  • Decompile FUN_001030d0 보면 /proc/self/maps 파일을 반복적으로 읽으며 'frida', 'xposed' 문자열 존재 여부를 검사함
  • 문자열이 발견될 경우 탐지 메시지를 출력한 뒤 goodbye() 함수를 호출하여 프로세스를 종료

그림14. FUN_001030d0 코드

 

step1) 탐지 조건 비활성화

  • do-while(조건) 구조에서 Xposed탐지를 제거하여 조건을 true로 유지함으로써, 루프가 반복되도록 하고 goodbye()로 가지 않게 함
  • 해당 탐지가 strstr()기반 문자열 비교로 이루어져 있어, strstr()를 후킹하여 Frida, Xposed 문자열이 포함되더라도 항상 0(NULL)을 반환하도록 조작
  • do-while문은 조건과 무관하게 최소 1회는 반드시 실행되므로 Frida 탐지도 무력화 함

step2) libc.so에서 strstr() 후킹

  • strstr에 후킹을 걸 때 libo.so에서 걸어 줌
    • strstr()가 libc.so의 기본 라이브러리 함수이므로 후킹을 libo.so 기준으로 수행, 이를 통해 상위 네이티브 라이브러리 로드 이전부터 모든 strstr() 호출을 제어 가능

step3) Interceptor.attach() 사용

  • 종료 함수를 무력화 Intercepter.replace() 대신 Interceptor.attach()를 사용하여 strstr()의 반환값 제어

Interceptor.replace()를 사용하지 않은 이유

* Interceptor.attach()는 함수 실행은 유지한 채 입력과 반환값만 조작함. 

함수 자체를 무력화하려면 Interceptor.replace()를 사용함

goodbye()와 같은 종료 함수는 abort(), raise(SIGABRT), _exit(), kill() 등 여러 종료 루트를 통해 프로세스를 종료할 수 있음
따라서 Interceptor.replace()를 이용해 goodbye() 하나만 무력화하더라도 다른 종료 경로로 인해 앱이 종료될 수 있음

Interceptor.attach

Interceptor.attach(target, callbacks[, data])는 지정한 함수 호출을 가로채는 Frida API

  • target
    • 가로챌 함수의 주소 또는 심볼
  • onEnter(args)
    • 함수 호출 시 실행되며, args를 통해 인자를 읽거나 수정
  • onLeave(retval)
    • 함수 반환 시 실행되며, retval.replace()를 사용해 반환값을 변경
    • (포인터 반환 함수는 ptr(), 정수 반환 함수는 숫자 사용)
(참고)
Tip3. 검색을 잘해야 함(검색에 root, check, frida, detect, exit, kill을 쳐봄)

그림15. Ghidra 검색창

3.2. 2단계 우회 코드

//1단계
Java.perform(function(){
   var root = Java.use("sg.vantagepoint.util.RootDetection");
   root.checkRoot1.implementation = function(){
      var retval = this.checkRoot1();
      console.log(retval)
      return false;
   }  
});


Java.perform(function(){
   var root = Java.use("sg.vantagepoint.util.RootDetection");
   root.checkRoot2.implementation = function(str){
      return false;
   }  
});


Java.perform(function(){
   var root = Java.use("sg.vantagepoint.util.RootDetection");
   root.checkRoot3.implementation = function(){
      var retval = this.checkRoot3();
      console.log(retval)
      return false;
   }  
});

//2단계
Interceptor.attach(Module.getExportByName("libc.so", "strstr"), {
    onEnter: function (args) {
        this.block =
            Memory.readUtf8String(args[0]).includes("frida") ||
            Memory.readUtf8String(args[0]).includes("xposed");
    },
    onLeave: function (retval) {
        if (this.block) retval.replace(0);
    }
});

 

4. 3단계 Secret String(Key) 값 찾기

4.1. 3단계 분석

  • 목적: Nope.. That’s not it. Try again. 알림창에 인해 발생하는 애플리케이션 종료를 Secret String(Key)를 분석하여 입력 검증 통과

그림 16. Nope 알림창 확인

 

1)      Nope… 알림창은 check_code메서드의 반환값 조건을 확인

 

  • check_code메서드는 사용자가 입력한 문자열을 바이트 배열로 바꾼 다음, bar를 호출해 검사하고 결과를 true/false로 반환
  • bar는 native키워드가 있으므로, 해당 메서드는 네이티브 구현된 메서드라고 판단

그림18. check_code메서드의 bar 호출

 

2) Ghidra를 통해 libfoo.so 라이브러리의 bar의 내용을 살펴봄

  • ava_sg_vantagepoint_uncrackable3_CodeCheck_bar 디컴파일 코드를 확인
  • do-while문 반복문에서 if문으로 XOR(^)연산을 통한 데이터 검증로직이 존재

그림19. bar함수 조건문 확인

 

  • Java_sg_vantagepoint_uncrackable3_CodeCheck_bar 디컴파일 코드를 자세히 살펴봄
void Java_sg_vantagepoint_uncrackable3_CodeCheck_bar
               (long *param_1, undefined8 param_2, undefined8 param_3)
{
    long lVar1;
    int iVar2;
    undefined8 uVar3;
    long lVar4;
    ulong uVar5;
    byte local_68[32];  // 지역 byte 배열(크기 32)
    long local_48;

    // 값 저장
    lVar1 = tpidr_el0;
    local_48 = *(long *)(lVar1 + 0x28);

    uVar3 = 0;  // 초기값
    memset(local_68, 0, sizeof(local_68));  // local_68 버퍼를 0으로 초기화

    // 전역 변수 DAT_00115054 값이 2인지 검사
    if (DAT_00115054 == 2) {
        // local_68을 인자로 FUN_001010e0 호출
        FUN_001010e0(local_68);

        // param 관련된 배열 포인터 값 가져오기
        lVar4 = (**(code **)(*param_1 + 0x5c0))(param_1, param_3, 0);

        // param의 데이터 길이 확인
        iVar2 = (**(code **)(*param_1 + 0x558))(param_1, param_3);

        // 데이터 길이가 24(0x18)인지 검사
        if (iVar2 == 0x18) {
            uVar5 = 0;

            // 각 바이트마다 XOR 후 비교
            do {
                // local_68[i]와 DAT_00115038[i]를 XOR한 값과 입력값 lVar4[i] 비교
                if (*(byte *)(lVar4 + uVar5) !=
                    (*(byte *)((long)&DAT_00115038 + uVar5) ^ local_68[uVar5]))
                    goto LAB_00103474;  // 틀리면 실패 처리

                uVar5++;
            } while (uVar5 < 0x18);

            // uVar5가 24에 도달했는지 검사
            if ((int)uVar5 == 0x18) {
                uVar3 = 1;  // 성공 플래그
                goto LAB_00103478;
            }
        }

LAB_00103474:
        uVar3 = 0;  // 실패
    }

LAB_00103478:
    // 값 비교 후 정상 종료
    if (*(long *)(lVar1 + 0x28) == local_48) {
        return;
    }

    __stack_chk_fail(uVar3);  // 값이 다를 경우 __stack_chk_fail 호출
}
  • 코드 분석 결과, 해당 검증 로직은 XOR연산을 이용하여 사용자 입력을 검증하는 구조로 구현되어 있음을 확인
if (*(byte *)(lVar4 + uVar5) != (*(byte *)((long)&DAT_00115038 + uVar5) ^ local_68[uVar5]))
  • 위의 코드를 쉽게 풀어쓰면 아래와 같음
if (A != (B ^ C))
  • 조건문은 입력 문자열의 각 바이트가 DAT_00115038에 저장된 바이트 값과 local_68의 바이트 값을 XOR 연산한 결과와 일치하는지를 검증
  • Secret String을 복원하기 위해, local_68과 DAT_00115038 간의 XOR 연산 결과를 분석
  • local_68: 함수 내부에서 생성된 바이트 배열§  uVar5는 인덱스 이므로 실제 데이터인 local_68, DAT_00115038만 파악
  • DAT_00115038: 바이너리에 포함된 고정 바이트 배열
  • uVar5는 인덱스 이므로 실제 데이터인 local_68, DAT_00115038만 파악
  • *(byte *)(lVar4 + uVar5): lVar4와 uVar5는 결과를 검증하는 기준값이므로,  XOR 연산 결과에 영향을 주지 못함

3) DAT_00115038 분석

  • XOR연산에 필요한 값인 DAT_00115038 커서를 대고 References > Find References to DAT_00115038

그림20. DAT_00115038의 References확인

 

  • References 이동 후 아래의 창이 뜨면 다 확인

그림21. DAT_00115038의 References 구체화

 

  • Location이 001033c인 곳을 누르면 MainActivitiy init로 이동
  • DAT_00115038를 통해 MainActivitiy init에서도 호출되는 것을 확인
  • 문자열 복사 strncpy함수를 이용해 입력한 값으로부터 0x18(24글자)를 DAT_00115038로 저장하는 것을 알 수 있음
  • 0x는 16진수(hex), 16진수 0x18을 10진수로 바꾸면 24

그림22. MainActivity_init 디컴파일

 

  • MainActivity클래스를 보니 사전에 입력된 'pizzapizzapizzapizzapizz' 문자열이 init함수 호출 시 입력

그림23. MainActivity 24글자 문자열 선언

그림24. MainActivity init호출 확인

4)  local_68 분석 

  • 검증로직에 필요한 local_68값을 알아내기 위해 FUN_00101e0을 더블클릭하여 분석

그림25. local_68 확인

 

  • FUN_001010e0 함수의 맨 아래로 가 param_1 포인터 배열에 숫자 3개를 차례대로 저장하는 것을 확인

그림26. FUN_001010e0 함수 parm 포인터 배열 확인

 

  • parm 포인터 배열
*param_1     = 0x1549170f1311081d; //param_1[0]과 동일
param_1[1]   = 0x15131d5a1903000d;
param_1[2]   = 0x14130817005a0e08;

 

  • 메모리에 저장될 때 리틀엔디안 방식으로 배치되므로, 각 숫자의 바이트가 뒤집혀 배열로 이어짐
  • 검증 로직에서 쓰이는 local_68 값은 아래의 배열
  • [0x1D,0x08,0x11,0x13,0x0F,0x17,0x49,0x15,0x0D,0x00,0x03,0x19,0x5A,0x1D,0x13,0x15,0x08,0x0E,0x5A,0x00,0x17,0x08,0x13,0x14]

*리틀엔디안: 낮은 주소에 데이터의 낮은 바이트(LSB : Least Significant Byte)를 저장하는 방식

 

  • 정답은 making owasp great again 이다!

그림 27. Secret String 문자열 확인

 

 

그림 28. Secret String 값 성공

 

4.2. 3단계 우회 코드

//1단계
Java.perform(function(){
   var root = Java.use("sg.vantagepoint.util.RootDetection");
   root.checkRoot1.implementation = function(){
      var retval = this.checkRoot1();
      console.log(retval)
      return false;
   }  
});


Java.perform(function(){
   var root = Java.use("sg.vantagepoint.util.RootDetection");
   root.checkRoot2.implementation = function(str){
      return false;
   }  
});


Java.perform(function(){
   var root = Java.use("sg.vantagepoint.util.RootDetection");
   root.checkRoot3.implementation = function(){
      var retval = this.checkRoot3();
      console.log(retval)
      return false;
   }  
});

//2단계
Interceptor.attach(Module.getExportByName("libc.so", "strstr"), {
    onEnter: function (args) {
        this.block =
            Memory.readUtf8String(args[0]).includes("frida") ||
            Memory.readUtf8String(args[0]).includes("xposed");
    },
    onLeave: function (retval) {
        if (this.block) retval.replace(0);
    }
});

//3단계
function solve() {
	var key = "pizzapizzapizzapizzapizz";
	var a1 = [0x1D,0x08,0x11,0x13,0x0F,0x17,0x49,0x15,0x0D,0x00,0x03,0x19,0x5A,
    		 0x1D,0x13,0x15,0x08,0x0E,0x5A,0x00,0x17,0x08,0x13,0x14];
	var str = "";
	for(var i=0; i < 24; i++) {
		str += String.fromCharCode(key.charCodeAt(i%24) ^ a1[i]);
	}
	
	console.warn('solve: ' + str);
}
setImmediate(solve)

 

5. 4단계 Secret String(Key) 우회

5.1. 4단계 분석

  • 목적: Nope.. That’s not it. Try again. 알림창에 인해 발생하는 애플리케이션 종료 로직을 후킹하여 정상 실행 상태로 변경

1)    파일을 실행하면 “Node… That’s not it. Try again”이라는 알림창이 뜸

  • 알림창 Nope… 부분을 실행하는 조건문을 true로 나오게 하면 Success!
  • 조건문인 if(this.check.check_code(string))를 더블클릭해서 이동
  • check_code 메서드의 반환값을 조건문에서 확인해야 함

그림29. Nope 알림창 확인

 

 

그림30. check_code메서드 코드

2) 후킹 후 아무 문자열을 입력해도 성공

그림 31. Secret String 후킹 성공

 

5.2. 4단계 우회 코드

//1단계
Java.perform(function(){
   var root = Java.use("sg.vantagepoint.util.RootDetection");
   root.checkRoot1.implementation = function(){
      var retval = this.checkRoot1();
      console.log(retval)
      return false;
   }  
});


Java.perform(function(){
   var root = Java.use("sg.vantagepoint.util.RootDetection");
   root.checkRoot2.implementation = function(str){
      return false;
   }  
});


Java.perform(function(){
   var root = Java.use("sg.vantagepoint.util.RootDetection");
   root.checkRoot3.implementation = function(){
      var retval = this.checkRoot3();
      console.log(retval)
      return false;
   }  
});

//2단계
Interceptor.attach(Module.getExportByName("libc.so", "strstr"), {
    onEnter: function (args) {
        this.block =
            Memory.readUtf8String(args[0]).includes("frida") ||
            Memory.readUtf8String(args[0]).includes("xposed");
    },
    onLeave: function (retval) {
        if (this.block) retval.replace(0);
    }
});

//3단계
function solve() {
	var key = "pizzapizzapizzapizzapizz";
	var a1 = [0x1D,0x08,0x11,0x13,0x0F,0x17,0x49,0x15,0x0D,0x00,0x03,0x19,0x5A,
    		 0x1D,0x13,0x15,0x08,0x0E,0x5A,0x00,0x17,0x08,0x13,0x14];
	var str = "";
	for(var i=0; i < 24; i++) {
		str += String.fromCharCode(key.charCodeAt(i%24) ^ a1[i]);
	}
	
	console.warn('solve: ' + str);
}
setImmediate(solve);

//4단계
Java.perform(function(){
	var class_a = Java.use("sg.vantagepoint.uncrackable3.CodeCheck");
	class_a.check_code.implementation = function (arg){ 
		return true;
	}
})

 

 

Tip4. 코드 참고 자료

https://frida.re/docs/javascript-api/

 

JavaScript API

Observe and reprogram running programs on Windows, macOS, GNU/Linux, iOS, watchOS, tvOS, Android, FreeBSD, and QNX

frida.re

https://pygments.org/demo/ 보고서 코드 넣을때 추천

728x90
반응형

'cs > sec' 카테고리의 다른 글

아이폰XR 16.4 IPA 추출하기  (0) 2026.01.22
[모의해킹] Mobile 교육  (0) 2026.01.16
UnCreakable Level2 실습(후킹을 활용한 실습 문제 분석)  (0) 2026.01.15
Frida 환경 설정 가이드 및 UnCreakable Level1 실습(후킹을 활용한 실습 문제 분석)  (0) 2026.01.15
[모의해킹]도파민을 이용한 iOS 15.8 탈옥 후 Burp  (0) 2026.01.11
'cs/sec' 카테고리의 다른 글
  • 아이폰XR 16.4 IPA 추출하기
  • [모의해킹] Mobile 교육
  • UnCreakable Level2 실습(후킹을 활용한 실습 문제 분석)
  • Frida 환경 설정 가이드 및 UnCreakable Level1 실습(후킹을 활용한 실습 문제 분석)
lakedata
lakedata
lakedata 님의 블로그 입니다.
  • lakedata
    lakedata 님의 블로그
    lakedata
  • 전체
    오늘
    어제
    • 분류 전체보기 (188)
      • cs (82)
        • dev (28)
        • sec (29)
        • ops (25)
      • 자격증 (32)
        • 정보처리기사 (20)
        • 정보보안기사 (1)
        • aws dva (6)
        • aws dop (2)
      • IT서적 (27)
        • 클린아키텍처 (10)
        • 객체지향의사실과오해 (7)
        • 오브젝트 (10)
      • 코테 (42)
        • 알고리즘 (20)
        • 백준 (13)
        • 프로그래머스 (7)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

    • github
  • 공지사항

  • 인기 글

  • 태그

    AWS
    CS
    알고리즘
    Spring
    SQL
    docker
    Java
    Security
  • 최근 댓글

  • 최근 글

  • 반응형
    250x250
  • hELLO· Designed By정상우.v4.10.3
lakedata
UnCrackable Level3(후킹을 활용한 실습 문제 분석)
상단으로

티스토리툴바